diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
commit | 76cb841cb886eef6b3bee341a2266c76578724ad (patch) | |
tree | f5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /drivers/isdn/hardware | |
parent | Initial commit. (diff) | |
download | linux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip |
Adding upstream version 4.19.249.upstream/4.19.249upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/isdn/hardware')
117 files changed, 69570 insertions, 0 deletions
diff --git a/drivers/isdn/hardware/Kconfig b/drivers/isdn/hardware/Kconfig new file mode 100644 index 000000000..30d028d24 --- /dev/null +++ b/drivers/isdn/hardware/Kconfig @@ -0,0 +1,9 @@ +# +# ISDN hardware drivers +# +comment "CAPI hardware drivers" + +source "drivers/isdn/hardware/avm/Kconfig" + +source "drivers/isdn/hardware/eicon/Kconfig" + diff --git a/drivers/isdn/hardware/Makefile b/drivers/isdn/hardware/Makefile new file mode 100644 index 000000000..a5d8fce4c --- /dev/null +++ b/drivers/isdn/hardware/Makefile @@ -0,0 +1,7 @@ +# Makefile for the CAPI hardware drivers + +# Object files in subdirectories + +obj-$(CONFIG_CAPI_AVM) += avm/ +obj-$(CONFIG_CAPI_EICON) += eicon/ +obj-$(CONFIG_MISDN) += mISDN/ diff --git a/drivers/isdn/hardware/avm/Kconfig b/drivers/isdn/hardware/avm/Kconfig new file mode 100644 index 000000000..b99b906ea --- /dev/null +++ b/drivers/isdn/hardware/avm/Kconfig @@ -0,0 +1,64 @@ +# +# ISDN AVM drivers +# + +menuconfig CAPI_AVM + bool "Active AVM cards" + help + Enable support for AVM active ISDN cards. + +if CAPI_AVM + +config ISDN_DRV_AVMB1_B1ISA + tristate "AVM B1 ISA support" + depends on ISA + help + Enable support for the ISA version of the AVM B1 card. + +config ISDN_DRV_AVMB1_B1PCI + tristate "AVM B1 PCI support" + depends on PCI + help + Enable support for the PCI version of the AVM B1 card. + +config ISDN_DRV_AVMB1_B1PCIV4 + bool "AVM B1 PCI V4 support" + depends on ISDN_DRV_AVMB1_B1PCI + help + Enable support for the V4 version of AVM B1 PCI card. + +config ISDN_DRV_AVMB1_T1ISA + tristate "AVM T1/T1-B ISA support" + depends on ISA + help + Enable support for the AVM T1 T1B card. + Note: This is a PRI card and handle 30 B-channels. + +config ISDN_DRV_AVMB1_B1PCMCIA + tristate "AVM B1/M1/M2 PCMCIA support" + depends on PCMCIA + help + Enable support for the PCMCIA version of the AVM B1 card. + +config ISDN_DRV_AVMB1_AVM_CS + tristate "AVM B1/M1/M2 PCMCIA cs module" + depends on ISDN_DRV_AVMB1_B1PCMCIA + help + Enable the PCMCIA client driver for the AVM B1/M1/M2 + PCMCIA cards. + +config ISDN_DRV_AVMB1_T1PCI + tristate "AVM T1/T1-B PCI support" + depends on PCI + help + Enable support for the AVM T1 T1B card. + Note: This is a PRI card and handle 30 B-channels. + +config ISDN_DRV_AVMB1_C4 + tristate "AVM C4/C2 support" + depends on PCI + help + Enable support for the AVM C4/C2 PCI cards. + These cards handle 4/2 BRI ISDN lines (8/4 channels). + +endif # CAPI_AVM diff --git a/drivers/isdn/hardware/avm/Makefile b/drivers/isdn/hardware/avm/Makefile new file mode 100644 index 000000000..3830a0573 --- /dev/null +++ b/drivers/isdn/hardware/avm/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for the AVM ISDN device drivers + +# Each configuration option enables a list of files. + +obj-$(CONFIG_ISDN_DRV_AVMB1_B1ISA) += b1isa.o b1.o +obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCI) += b1pci.o b1.o b1dma.o +obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCMCIA) += b1pcmcia.o b1.o +obj-$(CONFIG_ISDN_DRV_AVMB1_AVM_CS) += avm_cs.o +obj-$(CONFIG_ISDN_DRV_AVMB1_T1ISA) += t1isa.o b1.o +obj-$(CONFIG_ISDN_DRV_AVMB1_T1PCI) += t1pci.o b1.o b1dma.o +obj-$(CONFIG_ISDN_DRV_AVMB1_C4) += c4.o b1.o diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c new file mode 100644 index 000000000..62b8030ee --- /dev/null +++ b/drivers/isdn/hardware/avm/avm_cs.c @@ -0,0 +1,166 @@ +/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $ + * + * A PCMCIA client driver for AVM B1/M1/M2 + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/ptrace.h> +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/serial.h> +#include <linux/major.h> +#include <asm/io.h> + +#include <pcmcia/cistpl.h> +#include <pcmcia/ciscode.h> +#include <pcmcia/ds.h> +#include <pcmcia/cisreg.h> + +#include <linux/skbuff.h> +#include <linux/capi.h> +#include <linux/b1lli.h> +#include <linux/b1pcmcia.h> + +/*====================================================================*/ + +MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/*====================================================================*/ + +static int avmcs_config(struct pcmcia_device *link); +static void avmcs_release(struct pcmcia_device *link); +static void avmcs_detach(struct pcmcia_device *p_dev); + +static int avmcs_probe(struct pcmcia_device *p_dev) +{ + /* General socket configuration */ + p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + p_dev->config_index = 1; + p_dev->config_regs = PRESENT_OPTION; + + return avmcs_config(p_dev); +} /* avmcs_attach */ + + +static void avmcs_detach(struct pcmcia_device *link) +{ + avmcs_release(link); +} /* avmcs_detach */ + +static int avmcs_configcheck(struct pcmcia_device *p_dev, void *priv_data) +{ + p_dev->resource[0]->end = 16; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + return pcmcia_request_io(p_dev); +} + +static int avmcs_config(struct pcmcia_device *link) +{ + int i = -1; + char devname[128]; + int cardtype; + int (*addcard)(unsigned int port, unsigned irq); + + devname[0] = 0; + if (link->prod_id[1]) + strlcpy(devname, link->prod_id[1], sizeof(devname)); + + /* + * find IO port + */ + if (pcmcia_loop_config(link, avmcs_configcheck, NULL)) + return -ENODEV; + + do { + if (!link->irq) { + /* undo */ + pcmcia_disable_device(link); + break; + } + + /* + * configure the PCMCIA socket + */ + i = pcmcia_enable_device(link); + if (i != 0) { + pcmcia_disable_device(link); + break; + } + + } while (0); + + if (devname[0]) { + char *s = strrchr(devname, ' '); + if (!s) + s = devname; + else s++; + if (strcmp("M1", s) == 0) { + cardtype = AVM_CARDTYPE_M1; + } else if (strcmp("M2", s) == 0) { + cardtype = AVM_CARDTYPE_M2; + } else { + cardtype = AVM_CARDTYPE_B1; + } + } else + cardtype = AVM_CARDTYPE_B1; + + /* If any step failed, release any partially configured state */ + if (i != 0) { + avmcs_release(link); + return -ENODEV; + } + + + switch (cardtype) { + case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break; + case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break; + default: + case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break; + } + if ((i = (*addcard)(link->resource[0]->start, link->irq)) < 0) { + dev_err(&link->dev, + "avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n", + (unsigned int) link->resource[0]->start, link->irq); + avmcs_release(link); + return -ENODEV; + } + return 0; + +} /* avmcs_config */ + + +static void avmcs_release(struct pcmcia_device *link) +{ + b1pcmcia_delcard(link->resource[0]->start, link->irq); + pcmcia_disable_device(link); +} /* avmcs_release */ + + +static const struct pcmcia_device_id avmcs_ids[] = { + PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335), + PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430), + PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, avmcs_ids); + +static struct pcmcia_driver avmcs_driver = { + .owner = THIS_MODULE, + .name = "avm_cs", + .probe = avmcs_probe, + .remove = avmcs_detach, + .id_table = avmcs_ids, +}; +module_pcmcia_driver(avmcs_driver); diff --git a/drivers/isdn/hardware/avm/avmcard.h b/drivers/isdn/hardware/avm/avmcard.h new file mode 100644 index 000000000..cdfa89c71 --- /dev/null +++ b/drivers/isdn/hardware/avm/avmcard.h @@ -0,0 +1,581 @@ +/* $Id: avmcard.h,v 1.1.4.1.2.1 2001/12/21 15:00:17 kai Exp $ + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#ifndef _AVMCARD_H_ +#define _AVMCARD_H_ + +#include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/interrupt.h> + +#define AVMB1_PORTLEN 0x1f +#define AVM_MAXVERSION 8 +#define AVM_NCCI_PER_CHANNEL 4 + +/* + * Versions + */ + +#define VER_DRIVER 0 +#define VER_CARDTYPE 1 +#define VER_HWID 2 +#define VER_SERIAL 3 +#define VER_OPTION 4 +#define VER_PROTO 5 +#define VER_PROFILE 6 +#define VER_CAPI 7 + +enum avmcardtype { + avm_b1isa, + avm_b1pci, + avm_b1pcmcia, + avm_m1, + avm_m2, + avm_t1isa, + avm_t1pci, + avm_c4, + avm_c2 +}; + +typedef struct avmcard_dmabuf { + long size; + u8 *dmabuf; + dma_addr_t dmaaddr; +} avmcard_dmabuf; + +typedef struct avmcard_dmainfo { + u32 recvlen; + avmcard_dmabuf recvbuf; + + avmcard_dmabuf sendbuf; + struct sk_buff_head send_queue; + + struct pci_dev *pcidev; +} avmcard_dmainfo; + +typedef struct avmctrl_info { + char cardname[32]; + + int versionlen; + char versionbuf[1024]; + char *version[AVM_MAXVERSION]; + + char infobuf[128]; /* for function procinfo */ + + struct avmcard *card; + struct capi_ctr capi_ctrl; + + struct list_head ncci_head; +} avmctrl_info; + +typedef struct avmcard { + char name[32]; + + spinlock_t lock; + unsigned int port; + unsigned irq; + unsigned long membase; + enum avmcardtype cardtype; + unsigned char revision; + unsigned char class; + int cardnr; /* for t1isa */ + + char msgbuf[128]; /* capimsg msg part */ + char databuf[2048]; /* capimsg data part */ + + void __iomem *mbase; + volatile u32 csr; + avmcard_dmainfo *dma; + + struct avmctrl_info *ctrlinfo; + + u_int nr_controllers; + u_int nlogcontr; + struct list_head list; +} avmcard; + +extern int b1_irq_table[16]; + +/* + * LLI Messages to the ISDN-ControllerISDN Controller + */ + +#define SEND_POLL 0x72 /* + * after load <- RECEIVE_POLL + */ +#define SEND_INIT 0x11 /* + * first message <- RECEIVE_INIT + * int32 NumApplications int32 + * NumNCCIs int32 BoardNumber + */ +#define SEND_REGISTER 0x12 /* + * register an application int32 + * ApplIDId int32 NumMessages + * int32 NumB3Connections int32 + * NumB3Blocks int32 B3Size + * + * AnzB3Connection != 0 && + * AnzB3Blocks >= 1 && B3Size >= 1 + */ +#define SEND_RELEASE 0x14 /* + * deregister an application int32 + * ApplID + */ +#define SEND_MESSAGE 0x15 /* + * send capi-message int32 length + * capi-data ... + */ +#define SEND_DATA_B3_REQ 0x13 /* + * send capi-data-message int32 + * MsgLength capi-data ... int32 + * B3Length data .... + */ + +#define SEND_CONFIG 0x21 /* + */ + +#define SEND_POLLACK 0x73 /* T1 Watchdog */ + +/* + * LLI Messages from the ISDN-ControllerISDN Controller + */ + +#define RECEIVE_POLL 0x32 /* + * <- after SEND_POLL + */ +#define RECEIVE_INIT 0x27 /* + * <- after SEND_INIT int32 length + * byte total length b1struct board + * driver revision b1struct card + * type b1struct reserved b1struct + * serial number b1struct driver + * capability b1struct d-channel + * protocol b1struct CAPI-2.0 + * profile b1struct capi version + */ +#define RECEIVE_MESSAGE 0x21 /* + * <- after SEND_MESSAGE int32 + * AppllID int32 Length capi-data + * .... + */ +#define RECEIVE_DATA_B3_IND 0x22 /* + * received data int32 AppllID + * int32 Length capi-data ... + * int32 B3Length data ... + */ +#define RECEIVE_START 0x23 /* + * Handshake + */ +#define RECEIVE_STOP 0x24 /* + * Handshake + */ +#define RECEIVE_NEW_NCCI 0x25 /* + * int32 AppllID int32 NCCI int32 + * WindowSize + */ +#define RECEIVE_FREE_NCCI 0x26 /* + * int32 AppllID int32 NCCI + */ +#define RECEIVE_RELEASE 0x26 /* + * int32 AppllID int32 0xffffffff + */ +#define RECEIVE_TASK_READY 0x31 /* + * int32 tasknr + * int32 Length Taskname ... + */ +#define RECEIVE_DEBUGMSG 0x71 /* + * int32 Length message + * + */ +#define RECEIVE_POLLDWORD 0x75 /* t1pci in dword mode */ + +#define WRITE_REGISTER 0x00 +#define READ_REGISTER 0x01 + +/* + * port offsets + */ + +#define B1_READ 0x00 +#define B1_WRITE 0x01 +#define B1_INSTAT 0x02 +#define B1_OUTSTAT 0x03 +#define B1_ANALYSE 0x04 +#define B1_REVISION 0x05 +#define B1_RESET 0x10 + + +#define B1_STAT0(cardtype) ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l) +#define B1_STAT1(cardtype) (0x80E00000l) + +/* ---------------------------------------------------------------- */ + +static inline unsigned char b1outp(unsigned int base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); + return inb(base + B1_ANALYSE); +} + + +static inline int b1_rx_full(unsigned int base) +{ + return inb(base + B1_INSTAT) & 0x1; +} + +static inline unsigned char b1_get_byte(unsigned int base) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + while (!b1_rx_full(base) && time_before(jiffies, stop)); + if (b1_rx_full(base)) + return inb(base + B1_READ); + printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base); + return 0; +} + +static inline unsigned int b1_get_word(unsigned int base) +{ + unsigned int val = 0; + val |= b1_get_byte(base); + val |= (b1_get_byte(base) << 8); + val |= (b1_get_byte(base) << 16); + val |= (b1_get_byte(base) << 24); + return val; +} + +static inline int b1_tx_empty(unsigned int base) +{ + return inb(base + B1_OUTSTAT) & 0x1; +} + +static inline void b1_put_byte(unsigned int base, unsigned char val) +{ + while (!b1_tx_empty(base)); + b1outp(base, B1_WRITE, val); +} + +static inline int b1_save_put_byte(unsigned int base, unsigned char val) +{ + unsigned long stop = jiffies + 2 * HZ; + while (!b1_tx_empty(base) && time_before(jiffies, stop)); + if (!b1_tx_empty(base)) return -1; + b1outp(base, B1_WRITE, val); + return 0; +} + +static inline void b1_put_word(unsigned int base, unsigned int val) +{ + b1_put_byte(base, val & 0xff); + b1_put_byte(base, (val >> 8) & 0xff); + b1_put_byte(base, (val >> 16) & 0xff); + b1_put_byte(base, (val >> 24) & 0xff); +} + +static inline unsigned int b1_get_slice(unsigned int base, + unsigned char *dp) +{ + unsigned int len, i; + + len = i = b1_get_word(base); + while (i-- > 0) *dp++ = b1_get_byte(base); + return len; +} + +static inline void b1_put_slice(unsigned int base, + unsigned char *dp, unsigned int len) +{ + unsigned i = len; + b1_put_word(base, i); + while (i-- > 0) + b1_put_byte(base, *dp++); +} + +static void b1_wr_reg(unsigned int base, + unsigned int reg, + unsigned int value) +{ + b1_put_byte(base, WRITE_REGISTER); + b1_put_word(base, reg); + b1_put_word(base, value); +} + +static inline unsigned int b1_rd_reg(unsigned int base, + unsigned int reg) +{ + b1_put_byte(base, READ_REGISTER); + b1_put_word(base, reg); + return b1_get_word(base); + +} + +static inline void b1_reset(unsigned int base) +{ + b1outp(base, B1_RESET, 0); + mdelay(55 * 2); /* 2 TIC's */ + + b1outp(base, B1_RESET, 1); + mdelay(55 * 2); /* 2 TIC's */ + + b1outp(base, B1_RESET, 0); + mdelay(55 * 2); /* 2 TIC's */ +} + +static inline unsigned char b1_disable_irq(unsigned int base) +{ + return b1outp(base, B1_INSTAT, 0x00); +} + +/* ---------------------------------------------------------------- */ + +static inline void b1_set_test_bit(unsigned int base, + enum avmcardtype cardtype, + int onoff) +{ + b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20); +} + +static inline int b1_get_test_bit(unsigned int base, + enum avmcardtype cardtype) +{ + return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0; +} + +/* ---------------------------------------------------------------- */ + +#define T1_FASTLINK 0x00 +#define T1_SLOWLINK 0x08 + +#define T1_READ B1_READ +#define T1_WRITE B1_WRITE +#define T1_INSTAT B1_INSTAT +#define T1_OUTSTAT B1_OUTSTAT +#define T1_IRQENABLE 0x05 +#define T1_FIFOSTAT 0x06 +#define T1_RESETLINK 0x10 +#define T1_ANALYSE 0x11 +#define T1_IRQMASTER 0x12 +#define T1_IDENT 0x17 +#define T1_RESETBOARD 0x1f + +#define T1F_IREADY 0x01 +#define T1F_IHALF 0x02 +#define T1F_IFULL 0x04 +#define T1F_IEMPTY 0x08 +#define T1F_IFLAGS 0xF0 + +#define T1F_OREADY 0x10 +#define T1F_OHALF 0x20 +#define T1F_OEMPTY 0x40 +#define T1F_OFULL 0x80 +#define T1F_OFLAGS 0xF0 + +/* there are HEMA cards with 1k and 4k FIFO out */ +#define FIFO_OUTBSIZE 256 +#define FIFO_INPBSIZE 512 + +#define HEMA_VERSION_ID 0 +#define HEMA_PAL_ID 0 + +static inline void t1outp(unsigned int base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); +} + +static inline unsigned char t1inp(unsigned int base, + unsigned short offset) +{ + return inb(base + offset); +} + +static inline int t1_isfastlink(unsigned int base) +{ + return (inb(base + T1_IDENT) & ~0x82) == 1; +} + +static inline unsigned char t1_fifostatus(unsigned int base) +{ + return inb(base + T1_FIFOSTAT); +} + +static inline unsigned int t1_get_slice(unsigned int base, + unsigned char *dp) +{ + unsigned int len, i; +#ifdef FASTLINK_DEBUG + unsigned wcnt = 0, bcnt = 0; +#endif + + len = i = b1_get_word(base); + if (t1_isfastlink(base)) { + int status; + while (i > 0) { + status = t1_fifostatus(base) & (T1F_IREADY | T1F_IHALF); + if (i >= FIFO_INPBSIZE) status |= T1F_IFULL; + + switch (status) { + case T1F_IREADY | T1F_IHALF | T1F_IFULL: + insb(base + B1_READ, dp, FIFO_INPBSIZE); + dp += FIFO_INPBSIZE; + i -= FIFO_INPBSIZE; +#ifdef FASTLINK_DEBUG + wcnt += FIFO_INPBSIZE; +#endif + break; + case T1F_IREADY | T1F_IHALF: + insb(base + B1_READ, dp, i); +#ifdef FASTLINK_DEBUG + wcnt += i; +#endif + dp += i; + i = 0; + break; + default: + *dp++ = b1_get_byte(base); + i--; +#ifdef FASTLINK_DEBUG + bcnt++; +#endif + break; + } + } +#ifdef FASTLINK_DEBUG + if (wcnt) + printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n", + base, len, wcnt, bcnt); +#endif + } else { + while (i-- > 0) + *dp++ = b1_get_byte(base); + } + return len; +} + +static inline void t1_put_slice(unsigned int base, + unsigned char *dp, unsigned int len) +{ + unsigned i = len; + b1_put_word(base, i); + if (t1_isfastlink(base)) { + int status; + while (i > 0) { + status = t1_fifostatus(base) & (T1F_OREADY | T1F_OHALF); + if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY; + switch (status) { + case T1F_OREADY | T1F_OHALF | T1F_OEMPTY: + outsb(base + B1_WRITE, dp, FIFO_OUTBSIZE); + dp += FIFO_OUTBSIZE; + i -= FIFO_OUTBSIZE; + break; + case T1F_OREADY | T1F_OHALF: + outsb(base + B1_WRITE, dp, i); + dp += i; + i = 0; + break; + default: + b1_put_byte(base, *dp++); + i--; + break; + } + } + } else { + while (i-- > 0) + b1_put_byte(base, *dp++); + } +} + +static inline void t1_disable_irq(unsigned int base) +{ + t1outp(base, T1_IRQMASTER, 0x00); +} + +static inline void t1_reset(unsigned int base) +{ + /* reset T1 Controller */ + b1_reset(base); + /* disable irq on HEMA */ + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_OUTSTAT, 0x00); + t1outp(base, T1_IRQMASTER, 0x00); + /* reset HEMA board configuration */ + t1outp(base, T1_RESETBOARD, 0xf); +} + +static inline void b1_setinterrupt(unsigned int base, unsigned irq, + enum avmcardtype cardtype) +{ + switch (cardtype) { + case avm_t1isa: + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_INSTAT, 0x02); + t1outp(base, T1_IRQMASTER, 0x08); + break; + case avm_b1isa: + b1outp(base, B1_INSTAT, 0x00); + b1outp(base, B1_RESET, b1_irq_table[irq]); + b1outp(base, B1_INSTAT, 0x02); + break; + default: + case avm_m1: + case avm_m2: + case avm_b1pci: + b1outp(base, B1_INSTAT, 0x00); + b1outp(base, B1_RESET, 0xf0); + b1outp(base, B1_INSTAT, 0x02); + break; + case avm_c4: + case avm_t1pci: + b1outp(base, B1_RESET, 0xf0); + break; + } +} + +/* b1.c */ +avmcard *b1_alloc_card(int nr_controllers); +void b1_free_card(avmcard *card); +int b1_detect(unsigned int base, enum avmcardtype cardtype); +void b1_getrevision(avmcard *card); +int b1_load_t4file(avmcard *card, capiloaddatapart *t4file); +int b1_load_config(avmcard *card, capiloaddatapart *config); +int b1_loaded(avmcard *card); + +int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data); +void b1_reset_ctr(struct capi_ctr *ctrl); +void b1_register_appl(struct capi_ctr *ctrl, u16 appl, + capi_register_params *rp); +void b1_release_appl(struct capi_ctr *ctrl, u16 appl); +u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); +void b1_parse_version(avmctrl_info *card); +irqreturn_t b1_interrupt(int interrupt, void *devptr); + +int b1_proc_show(struct seq_file *m, void *v); + +avmcard_dmainfo *avmcard_dma_alloc(char *name, struct pci_dev *, + long rsize, long ssize); +void avmcard_dma_free(avmcard_dmainfo *); + +/* b1dma.c */ +int b1pciv4_detect(avmcard *card); +int t1pci_detect(avmcard *card); +void b1dma_reset(avmcard *card); +irqreturn_t b1dma_interrupt(int interrupt, void *devptr); + +int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data); +void b1dma_reset_ctr(struct capi_ctr *ctrl); +void b1dma_remove_ctr(struct capi_ctr *ctrl); +void b1dma_register_appl(struct capi_ctr *ctrl, + u16 appl, + capi_register_params *rp); +void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl); +u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); +int b1dma_proc_show(struct seq_file *m, void *v); + +#endif /* _AVMCARD_H_ */ diff --git a/drivers/isdn/hardware/avm/b1.c b/drivers/isdn/hardware/avm/b1.c new file mode 100644 index 000000000..40ca1e8fa --- /dev/null +++ b/drivers/isdn/hardware/avm/b1.c @@ -0,0 +1,804 @@ +/* $Id: b1.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ + * + * Common module for AVM B1 cards. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <linux/kernelcapi.h> +#include <linux/slab.h> +#include <asm/io.h> +#include <linux/init.h> +#include <linux/uaccess.h> +#include <linux/netdevice.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> + +static char *revision = "$Revision: 1.1.2.2 $"; + +/* ------------------------------------------------------------- */ + +MODULE_DESCRIPTION("CAPI4Linux: Common support for active AVM cards"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +int b1_irq_table[16] = +{0, + 0, + 0, + 192, /* irq 3 */ + 32, /* irq 4 */ + 160, /* irq 5 */ + 96, /* irq 6 */ + 224, /* irq 7 */ + 0, + 64, /* irq 9 */ + 80, /* irq 10 */ + 208, /* irq 11 */ + 48, /* irq 12 */ + 0, + 0, + 112, /* irq 15 */ +}; + +/* ------------------------------------------------------------- */ + +avmcard *b1_alloc_card(int nr_controllers) +{ + avmcard *card; + avmctrl_info *cinfo; + int i; + + card = kzalloc(sizeof(*card), GFP_KERNEL); + if (!card) + return NULL; + + cinfo = kcalloc(nr_controllers, sizeof(*cinfo), GFP_KERNEL); + if (!cinfo) { + kfree(card); + return NULL; + } + + card->ctrlinfo = cinfo; + for (i = 0; i < nr_controllers; i++) { + INIT_LIST_HEAD(&cinfo[i].ncci_head); + cinfo[i].card = card; + } + spin_lock_init(&card->lock); + card->nr_controllers = nr_controllers; + + return card; +} + +/* ------------------------------------------------------------- */ + +void b1_free_card(avmcard *card) +{ + kfree(card->ctrlinfo); + kfree(card); +} + +/* ------------------------------------------------------------- */ + +int b1_detect(unsigned int base, enum avmcardtype cardtype) +{ + int onoff, i; + + /* + * Statusregister 0000 00xx + */ + if ((inb(base + B1_INSTAT) & 0xfc) + || (inb(base + B1_OUTSTAT) & 0xfc)) + return 1; + /* + * Statusregister 0000 001x + */ + b1outp(base, B1_INSTAT, 0x2); /* enable irq */ + /* b1outp(base, B1_OUTSTAT, 0x2); */ + if ((inb(base + B1_INSTAT) & 0xfe) != 0x2 + /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */) + return 2; + /* + * Statusregister 0000 000x + */ + b1outp(base, B1_INSTAT, 0x0); /* disable irq */ + b1outp(base, B1_OUTSTAT, 0x0); + if ((inb(base + B1_INSTAT) & 0xfe) + || (inb(base + B1_OUTSTAT) & 0xfe)) + return 3; + + for (onoff = !0, i = 0; i < 10; i++) { + b1_set_test_bit(base, cardtype, onoff); + if (b1_get_test_bit(base, cardtype) != onoff) + return 4; + onoff = !onoff; + } + + if (cardtype == avm_m1) + return 0; + + if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01) + return 5; + + return 0; +} + +void b1_getrevision(avmcard *card) +{ + card->class = inb(card->port + B1_ANALYSE); + card->revision = inb(card->port + B1_REVISION); +} + +#define FWBUF_SIZE 256 +int b1_load_t4file(avmcard *card, capiloaddatapart *t4file) +{ + unsigned char buf[FWBUF_SIZE]; + unsigned char *dp; + int i, left; + unsigned int base = card->port; + + dp = t4file->data; + left = t4file->len; + while (left > FWBUF_SIZE) { + if (t4file->user) { + if (copy_from_user(buf, dp, FWBUF_SIZE)) + return -EFAULT; + } else { + memcpy(buf, dp, FWBUF_SIZE); + } + for (i = 0; i < FWBUF_SIZE; i++) + if (b1_save_put_byte(base, buf[i]) < 0) { + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); + return -EIO; + } + left -= FWBUF_SIZE; + dp += FWBUF_SIZE; + } + if (left) { + if (t4file->user) { + if (copy_from_user(buf, dp, left)) + return -EFAULT; + } else { + memcpy(buf, dp, left); + } + for (i = 0; i < left; i++) + if (b1_save_put_byte(base, buf[i]) < 0) { + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); + return -EIO; + } + } + return 0; +} + +int b1_load_config(avmcard *card, capiloaddatapart *config) +{ + unsigned char buf[FWBUF_SIZE]; + unsigned char *dp; + unsigned int base = card->port; + int i, j, left; + + dp = config->data; + left = config->len; + if (left) { + b1_put_byte(base, SEND_CONFIG); + b1_put_word(base, 1); + b1_put_byte(base, SEND_CONFIG); + b1_put_word(base, left); + } + while (left > FWBUF_SIZE) { + if (config->user) { + if (copy_from_user(buf, dp, FWBUF_SIZE)) + return -EFAULT; + } else { + memcpy(buf, dp, FWBUF_SIZE); + } + for (i = 0; i < FWBUF_SIZE; ) { + b1_put_byte(base, SEND_CONFIG); + for (j = 0; j < 4; j++) { + b1_put_byte(base, buf[i++]); + } + } + left -= FWBUF_SIZE; + dp += FWBUF_SIZE; + } + if (left) { + if (config->user) { + if (copy_from_user(buf, dp, left)) + return -EFAULT; + } else { + memcpy(buf, dp, left); + } + for (i = 0; i < left; ) { + b1_put_byte(base, SEND_CONFIG); + for (j = 0; j < 4; j++) { + if (i < left) + b1_put_byte(base, buf[i++]); + else + b1_put_byte(base, 0); + } + } + } + return 0; +} + +int b1_loaded(avmcard *card) +{ + unsigned int base = card->port; + unsigned long stop; + unsigned char ans; + unsigned long tout = 2; + + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_tx_empty(base)) + break; + } + if (!b1_tx_empty(base)) { + printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n", + card->name); + return 0; + } + b1_put_byte(base, SEND_POLL); + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_rx_full(base)) { + if ((ans = b1_get_byte(base)) == RECEIVE_POLL) { + return 1; + } + printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n", + card->name, ans); + return 0; + } + } + printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name); + return 0; +} + +/* ------------------------------------------------------------- */ + +int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + int retval; + + b1_reset(port); + + if ((retval = b1_load_t4file(card, &data->firmware))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + b1_disable_irq(port); + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(card, &data->configuration))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1_loaded(card)) { + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + spin_lock_irqsave(&card->lock, flags); + b1_setinterrupt(port, card->irq, card->cardtype); + b1_put_byte(port, SEND_INIT); + b1_put_word(port, CAPI_MAXAPPL); + b1_put_word(port, AVM_NCCI_PER_CHANNEL * 2); + b1_put_word(port, ctrl->cnr - 1); + spin_unlock_irqrestore(&card->lock, flags); + + return 0; +} + +void b1_reset_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + + b1_reset(port); + b1_reset(port); + + memset(cinfo->version, 0, sizeof(cinfo->version)); + spin_lock_irqsave(&card->lock, flags); + capilib_release(&cinfo->ncci_head); + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_down(ctrl); +} + +void b1_register_appl(struct capi_ctr *ctrl, + u16 appl, + capi_register_params *rp) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + int nconn, want = rp->level3cnt; + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel; + + spin_lock_irqsave(&card->lock, flags); + b1_put_byte(port, SEND_REGISTER); + b1_put_word(port, appl); + b1_put_word(port, 1024 * (nconn + 1)); + b1_put_word(port, nconn); + b1_put_word(port, rp->datablkcnt); + b1_put_word(port, rp->datablklen); + spin_unlock_irqrestore(&card->lock, flags); +} + +void b1_release_appl(struct capi_ctr *ctrl, u16 appl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + capilib_release_appl(&cinfo->ncci_head, appl); + b1_put_byte(port, SEND_RELEASE); + b1_put_word(port, appl); + spin_unlock_irqrestore(&card->lock, flags); +} + +u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + u16 len = CAPIMSG_LEN(skb->data); + u8 cmd = CAPIMSG_COMMAND(skb->data); + u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); + u16 dlen, retval; + + spin_lock_irqsave(&card->lock, flags); + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + retval = capilib_data_b3_req(&cinfo->ncci_head, + CAPIMSG_APPID(skb->data), + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + if (retval != CAPI_NOERROR) { + spin_unlock_irqrestore(&card->lock, flags); + return retval; + } + + dlen = CAPIMSG_DATALEN(skb->data); + + b1_put_byte(port, SEND_DATA_B3_REQ); + b1_put_slice(port, skb->data, len); + b1_put_slice(port, skb->data + len, dlen); + } else { + b1_put_byte(port, SEND_MESSAGE); + b1_put_slice(port, skb->data, len); + } + spin_unlock_irqrestore(&card->lock, flags); + + dev_kfree_skb_any(skb); + return CAPI_NOERROR; +} + +/* ------------------------------------------------------------- */ + +void b1_parse_version(avmctrl_info *cinfo) +{ + struct capi_ctr *ctrl = &cinfo->capi_ctrl; + avmcard *card = cinfo->card; + capi_profile *profp; + u8 *dversion; + u8 flag; + int i, j; + + for (j = 0; j < AVM_MAXVERSION; j++) + cinfo->version[j] = ""; + for (i = 0, j = 0; + j < AVM_MAXVERSION && i < cinfo->versionlen; + j++, i += cinfo->versionbuf[i] + 1) + cinfo->version[j] = &cinfo->versionbuf[i + 1]; + + strlcpy(ctrl->serial, cinfo->version[VER_SERIAL], sizeof(ctrl->serial)); + memcpy(&ctrl->profile, cinfo->version[VER_PROFILE], sizeof(capi_profile)); + strlcpy(ctrl->manu, "AVM GmbH", sizeof(ctrl->manu)); + dversion = cinfo->version[VER_DRIVER]; + ctrl->version.majorversion = 2; + ctrl->version.minorversion = 0; + ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4); + ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf); + ctrl->version.minormanuversion = (dversion[3] - '0') << 4; + ctrl->version.minormanuversion |= + (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf); + + profp = &ctrl->profile; + + flag = ((u8 *)(profp->manu))[1]; + switch (flag) { + case 0: if (cinfo->version[VER_CARDTYPE]) + strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]); + else strcpy(cinfo->cardname, "B1"); + break; + case 3: strcpy(cinfo->cardname, "PCMCIA B"); break; + case 4: strcpy(cinfo->cardname, "PCMCIA M1"); break; + case 5: strcpy(cinfo->cardname, "PCMCIA M2"); break; + case 6: strcpy(cinfo->cardname, "B1 V3.0"); break; + case 7: strcpy(cinfo->cardname, "B1 PCI"); break; + default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break; + } + printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n", + card->name, ctrl->cnr, cinfo->cardname); + + flag = ((u8 *)(profp->manu))[3]; + if (flag) + printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n", + card->name, + ctrl->cnr, + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + + flag = ((u8 *)(profp->manu))[5]; + if (flag) + printk(KERN_NOTICE "%s: card %d Linetype:%s%s%s%s\n", + card->name, + ctrl->cnr, + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); +} + +/* ------------------------------------------------------------- */ + +irqreturn_t b1_interrupt(int interrupt, void *devptr) +{ + avmcard *card = devptr; + avmctrl_info *cinfo = &card->ctrlinfo[0]; + struct capi_ctr *ctrl = &cinfo->capi_ctrl; + unsigned char b1cmd; + struct sk_buff *skb; + + unsigned ApplId; + unsigned MsgLen; + unsigned DataB3Len; + unsigned NCCI; + unsigned WindowSize; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + if (!b1_rx_full(card->port)) { + spin_unlock_irqrestore(&card->lock, flags); + return IRQ_NONE; + } + + b1cmd = b1_get_byte(card->port); + + switch (b1cmd) { + + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + DataB3Len = b1_get_slice(card->port, card->databuf); + spin_unlock_irqrestore(&card->lock, flags); + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf + MsgLen, 0, 30-MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + skb_put_data(skb, card->msgbuf, MsgLen); + skb_put_data(skb, card->databuf, DataB3Len); + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + spin_unlock_irqrestore(&card->lock, flags); + } else { + skb_put_data(skb, card->msgbuf, MsgLen); + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) + capilib_data_b3_conf(&cinfo->ncci_head, ApplId, + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + WindowSize = b1_get_word(card->port); + capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize); + spin_unlock_irqrestore(&card->lock, flags); + break; + + case RECEIVE_FREE_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + if (NCCI != 0xffffffff) + capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI); + spin_unlock_irqrestore(&card->lock, flags); + break; + + case RECEIVE_START: + /* b1_put_byte(card->port, SEND_POLLACK); */ + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_resume_output(ctrl); + break; + + case RECEIVE_STOP: + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf); + spin_unlock_irqrestore(&card->lock, flags); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + capi_ctr_ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + spin_unlock_irqrestore(&card->lock, flags); + card->msgbuf[MsgLen] = 0; + while (MsgLen > 0 + && (card->msgbuf[MsgLen - 1] == '\n' + || card->msgbuf[MsgLen - 1] == '\r')) { + card->msgbuf[MsgLen - 1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = b1_get_slice(card->port, card->msgbuf); + spin_unlock_irqrestore(&card->lock, flags); + card->msgbuf[MsgLen] = 0; + while (MsgLen > 0 + && (card->msgbuf[MsgLen - 1] == '\n' + || card->msgbuf[MsgLen - 1] == '\r')) { + card->msgbuf[MsgLen - 1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + case 0xff: + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "%s: card removed ?\n", card->name); + return IRQ_NONE; + default: + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", + card->name, b1cmd); + return IRQ_HANDLED; + } + return IRQ_HANDLED; +} + +/* ------------------------------------------------------------- */ +int b1_proc_show(struct seq_file *m, void *v) +{ + struct capi_ctr *ctrl = m->private; + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + u8 flag; + char *s; + + seq_printf(m, "%-16s %s\n", "name", card->name); + seq_printf(m, "%-16s 0x%x\n", "io", card->port); + seq_printf(m, "%-16s %d\n", "irq", card->irq); + switch (card->cardtype) { + case avm_b1isa: s = "B1 ISA"; break; + case avm_b1pci: s = "B1 PCI"; break; + case avm_b1pcmcia: s = "B1 PCMCIA"; break; + case avm_m1: s = "M1"; break; + case avm_m2: s = "M2"; break; + case avm_t1isa: s = "T1 ISA (HEMA)"; break; + case avm_t1pci: s = "T1 PCI"; break; + case avm_c4: s = "C4"; break; + case avm_c2: s = "C2"; break; + default: s = "???"; break; + } + seq_printf(m, "%-16s %s\n", "type", s); + if (card->cardtype == avm_t1isa) + seq_printf(m, "%-16s %d\n", "cardnr", card->cardnr); + if ((s = cinfo->version[VER_DRIVER]) != NULL) + seq_printf(m, "%-16s %s\n", "ver_driver", s); + if ((s = cinfo->version[VER_CARDTYPE]) != NULL) + seq_printf(m, "%-16s %s\n", "ver_cardtype", s); + if ((s = cinfo->version[VER_SERIAL]) != NULL) + seq_printf(m, "%-16s %s\n", "ver_serial", s); + + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[3]; + if (flag) + seq_printf(m, "%-16s%s%s%s%s%s%s%s\n", + "protocol", + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + } + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[5]; + if (flag) + seq_printf(m, "%-16s%s%s%s%s\n", + "linetype", + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); + } + seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname); + + return 0; +} +EXPORT_SYMBOL(b1_proc_show); + +/* ------------------------------------------------------------- */ + +#ifdef CONFIG_PCI + +avmcard_dmainfo * +avmcard_dma_alloc(char *name, struct pci_dev *pdev, long rsize, long ssize) +{ + avmcard_dmainfo *p; + void *buf; + + p = kzalloc(sizeof(avmcard_dmainfo), GFP_KERNEL); + if (!p) { + printk(KERN_WARNING "%s: no memory.\n", name); + goto err; + } + + p->recvbuf.size = rsize; + buf = pci_alloc_consistent(pdev, rsize, &p->recvbuf.dmaaddr); + if (!buf) { + printk(KERN_WARNING "%s: allocation of receive dma buffer failed.\n", name); + goto err_kfree; + } + p->recvbuf.dmabuf = buf; + + p->sendbuf.size = ssize; + buf = pci_alloc_consistent(pdev, ssize, &p->sendbuf.dmaaddr); + if (!buf) { + printk(KERN_WARNING "%s: allocation of send dma buffer failed.\n", name); + goto err_free_consistent; + } + + p->sendbuf.dmabuf = buf; + skb_queue_head_init(&p->send_queue); + + return p; + +err_free_consistent: + pci_free_consistent(p->pcidev, p->recvbuf.size, + p->recvbuf.dmabuf, p->recvbuf.dmaaddr); +err_kfree: + kfree(p); +err: + return NULL; +} + +void avmcard_dma_free(avmcard_dmainfo *p) +{ + pci_free_consistent(p->pcidev, p->recvbuf.size, + p->recvbuf.dmabuf, p->recvbuf.dmaaddr); + pci_free_consistent(p->pcidev, p->sendbuf.size, + p->sendbuf.dmabuf, p->sendbuf.dmaaddr); + skb_queue_purge(&p->send_queue); + kfree(p); +} + +EXPORT_SYMBOL(avmcard_dma_alloc); +EXPORT_SYMBOL(avmcard_dma_free); + +#endif + +EXPORT_SYMBOL(b1_irq_table); + +EXPORT_SYMBOL(b1_alloc_card); +EXPORT_SYMBOL(b1_free_card); +EXPORT_SYMBOL(b1_detect); +EXPORT_SYMBOL(b1_getrevision); +EXPORT_SYMBOL(b1_load_t4file); +EXPORT_SYMBOL(b1_load_config); +EXPORT_SYMBOL(b1_loaded); +EXPORT_SYMBOL(b1_load_firmware); +EXPORT_SYMBOL(b1_reset_ctr); +EXPORT_SYMBOL(b1_register_appl); +EXPORT_SYMBOL(b1_release_appl); +EXPORT_SYMBOL(b1_send_message); + +EXPORT_SYMBOL(b1_parse_version); +EXPORT_SYMBOL(b1_interrupt); + +static int __init b1_init(void) +{ + char *p; + char rev[32]; + + if ((p = strchr(revision, ':')) != NULL && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != NULL && p > rev) + *(p - 1) = 0; + } else + strcpy(rev, "1.0"); + + printk(KERN_INFO "b1: revision %s\n", rev); + + return 0; +} + +static void __exit b1_exit(void) +{ +} + +module_init(b1_init); +module_exit(b1_exit); diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c new file mode 100644 index 000000000..6a3dc9937 --- /dev/null +++ b/drivers/isdn/hardware/avm/b1dma.c @@ -0,0 +1,981 @@ +/* $Id: b1dma.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $ + * + * Common module for AVM B1 cards that support dma with AMCC + * + * Copyright 2000 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <linux/kernelcapi.h> +#include <linux/gfp.h> +#include <asm/io.h> +#include <linux/init.h> +#include <linux/uaccess.h> +#include <linux/netdevice.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> + +static char *revision = "$Revision: 1.1.2.3 $"; + +#undef AVM_B1DMA_DEBUG + +/* ------------------------------------------------------------- */ + +MODULE_DESCRIPTION("CAPI4Linux: DMA support for active AVM cards"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +static bool suppress_pollack = 0; +module_param(suppress_pollack, bool, 0); + +/* ------------------------------------------------------------- */ + +static void b1dma_dispatch_tx(avmcard *card); + +/* ------------------------------------------------------------- */ + +/* S5933 */ + +#define AMCC_RXPTR 0x24 +#define AMCC_RXLEN 0x28 +#define AMCC_TXPTR 0x2c +#define AMCC_TXLEN 0x30 + +#define AMCC_INTCSR 0x38 +# define EN_READ_TC_INT 0x00008000L +# define EN_WRITE_TC_INT 0x00004000L +# define EN_TX_TC_INT EN_READ_TC_INT +# define EN_RX_TC_INT EN_WRITE_TC_INT +# define AVM_FLAG 0x30000000L + +# define ANY_S5933_INT 0x00800000L +# define READ_TC_INT 0x00080000L +# define WRITE_TC_INT 0x00040000L +# define TX_TC_INT READ_TC_INT +# define RX_TC_INT WRITE_TC_INT +# define MASTER_ABORT_INT 0x00100000L +# define TARGET_ABORT_INT 0x00200000L +# define BUS_MASTER_INT 0x00200000L +# define ALL_INT 0x000C0000L + +#define AMCC_MCSR 0x3c +# define A2P_HI_PRIORITY 0x00000100L +# define EN_A2P_TRANSFERS 0x00000400L +# define P2A_HI_PRIORITY 0x00001000L +# define EN_P2A_TRANSFERS 0x00004000L +# define RESET_A2P_FLAGS 0x04000000L +# define RESET_P2A_FLAGS 0x02000000L + +/* ------------------------------------------------------------- */ + +static inline void b1dma_writel(avmcard *card, u32 value, int off) +{ + writel(value, card->mbase + off); +} + +static inline u32 b1dma_readl(avmcard *card, int off) +{ + return readl(card->mbase + off); +} + +/* ------------------------------------------------------------- */ + +static inline int b1dma_tx_empty(unsigned int port) +{ + return inb(port + 0x03) & 0x1; +} + +static inline int b1dma_rx_full(unsigned int port) +{ + return inb(port + 0x02) & 0x1; +} + +static int b1dma_tolink(avmcard *card, void *buf, unsigned int len) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + unsigned char *s = (unsigned char *)buf; + while (len--) { + while (!b1dma_tx_empty(card->port) + && time_before(jiffies, stop)); + if (!b1dma_tx_empty(card->port)) + return -1; + t1outp(card->port, 0x01, *s++); + } + return 0; +} + +static int b1dma_fromlink(avmcard *card, void *buf, unsigned int len) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + unsigned char *s = (unsigned char *)buf; + while (len--) { + while (!b1dma_rx_full(card->port) + && time_before(jiffies, stop)); + if (!b1dma_rx_full(card->port)) + return -1; + *s++ = t1inp(card->port, 0x00); + } + return 0; +} + +static int WriteReg(avmcard *card, u32 reg, u8 val) +{ + u8 cmd = 0x00; + if (b1dma_tolink(card, &cmd, 1) == 0 + && b1dma_tolink(card, ®, 4) == 0) { + u32 tmp = val; + return b1dma_tolink(card, &tmp, 4); + } + return -1; +} + +static u8 ReadReg(avmcard *card, u32 reg) +{ + u8 cmd = 0x01; + if (b1dma_tolink(card, &cmd, 1) == 0 + && b1dma_tolink(card, ®, 4) == 0) { + u32 tmp; + if (b1dma_fromlink(card, &tmp, 4) == 0) + return (u8)tmp; + } + return 0xff; +} + +/* ------------------------------------------------------------- */ + +static inline void _put_byte(void **pp, u8 val) +{ + u8 *s = *pp; + *s++ = val; + *pp = s; +} + +static inline void _put_word(void **pp, u32 val) +{ + u8 *s = *pp; + *s++ = val & 0xff; + *s++ = (val >> 8) & 0xff; + *s++ = (val >> 16) & 0xff; + *s++ = (val >> 24) & 0xff; + *pp = s; +} + +static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len) +{ + unsigned i = len; + _put_word(pp, i); + while (i-- > 0) + _put_byte(pp, *dp++); +} + +static inline u8 _get_byte(void **pp) +{ + u8 *s = *pp; + u8 val; + val = *s++; + *pp = s; + return val; +} + +static inline u32 _get_word(void **pp) +{ + u8 *s = *pp; + u32 val; + val = *s++; + val |= (*s++ << 8); + val |= (*s++ << 16); + val |= (*s++ << 24); + *pp = s; + return val; +} + +static inline u32 _get_slice(void **pp, unsigned char *dp) +{ + unsigned int len, i; + + len = i = _get_word(pp); + while (i-- > 0) *dp++ = _get_byte(pp); + return len; +} + +/* ------------------------------------------------------------- */ + +void b1dma_reset(avmcard *card) +{ + card->csr = 0x0; + b1dma_writel(card, card->csr, AMCC_INTCSR); + b1dma_writel(card, 0, AMCC_MCSR); + b1dma_writel(card, 0, AMCC_RXLEN); + b1dma_writel(card, 0, AMCC_TXLEN); + + t1outp(card->port, 0x10, 0x00); + t1outp(card->port, 0x07, 0x00); + + b1dma_writel(card, 0, AMCC_MCSR); + mdelay(10); + b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */ + mdelay(10); + b1dma_writel(card, 0, AMCC_MCSR); + if (card->cardtype == avm_t1pci) + mdelay(42); + else + mdelay(10); +} + +/* ------------------------------------------------------------- */ + +static int b1dma_detect(avmcard *card) +{ + b1dma_writel(card, 0, AMCC_MCSR); + mdelay(10); + b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */ + mdelay(10); + b1dma_writel(card, 0, AMCC_MCSR); + mdelay(42); + + b1dma_writel(card, 0, AMCC_RXLEN); + b1dma_writel(card, 0, AMCC_TXLEN); + card->csr = 0x0; + b1dma_writel(card, card->csr, AMCC_INTCSR); + + if (b1dma_readl(card, AMCC_MCSR) != 0x000000E6) + return 1; + + b1dma_writel(card, 0xffffffff, AMCC_RXPTR); + b1dma_writel(card, 0xffffffff, AMCC_TXPTR); + if (b1dma_readl(card, AMCC_RXPTR) != 0xfffffffc + || b1dma_readl(card, AMCC_TXPTR) != 0xfffffffc) + return 2; + + b1dma_writel(card, 0x0, AMCC_RXPTR); + b1dma_writel(card, 0x0, AMCC_TXPTR); + if (b1dma_readl(card, AMCC_RXPTR) != 0x0 + || b1dma_readl(card, AMCC_TXPTR) != 0x0) + return 3; + + t1outp(card->port, 0x10, 0x00); + t1outp(card->port, 0x07, 0x00); + + t1outp(card->port, 0x02, 0x02); + t1outp(card->port, 0x03, 0x02); + + if ((t1inp(card->port, 0x02) & 0xFE) != 0x02 + || t1inp(card->port, 0x3) != 0x03) + return 4; + + t1outp(card->port, 0x02, 0x00); + t1outp(card->port, 0x03, 0x00); + + if ((t1inp(card->port, 0x02) & 0xFE) != 0x00 + || t1inp(card->port, 0x3) != 0x01) + return 5; + + return 0; +} + +int t1pci_detect(avmcard *card) +{ + int ret; + + if ((ret = b1dma_detect(card)) != 0) + return ret; + + /* Transputer test */ + + if (WriteReg(card, 0x80001000, 0x11) != 0 + || WriteReg(card, 0x80101000, 0x22) != 0 + || WriteReg(card, 0x80201000, 0x33) != 0 + || WriteReg(card, 0x80301000, 0x44) != 0) + return 6; + + if (ReadReg(card, 0x80001000) != 0x11 + || ReadReg(card, 0x80101000) != 0x22 + || ReadReg(card, 0x80201000) != 0x33 + || ReadReg(card, 0x80301000) != 0x44) + return 7; + + if (WriteReg(card, 0x80001000, 0x55) != 0 + || WriteReg(card, 0x80101000, 0x66) != 0 + || WriteReg(card, 0x80201000, 0x77) != 0 + || WriteReg(card, 0x80301000, 0x88) != 0) + return 8; + + if (ReadReg(card, 0x80001000) != 0x55 + || ReadReg(card, 0x80101000) != 0x66 + || ReadReg(card, 0x80201000) != 0x77 + || ReadReg(card, 0x80301000) != 0x88) + return 9; + + return 0; +} + +int b1pciv4_detect(avmcard *card) +{ + int ret, i; + + if ((ret = b1dma_detect(card)) != 0) + return ret; + + for (i = 0; i < 5; i++) { + if (WriteReg(card, 0x80A00000, 0x21) != 0) + return 6; + if ((ReadReg(card, 0x80A00000) & 0x01) != 0x01) + return 7; + } + for (i = 0; i < 5; i++) { + if (WriteReg(card, 0x80A00000, 0x20) != 0) + return 8; + if ((ReadReg(card, 0x80A00000) & 0x01) != 0x00) + return 9; + } + + return 0; +} + +static void b1dma_queue_tx(avmcard *card, struct sk_buff *skb) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + skb_queue_tail(&card->dma->send_queue, skb); + + if (!(card->csr & EN_TX_TC_INT)) { + b1dma_dispatch_tx(card); + b1dma_writel(card, card->csr, AMCC_INTCSR); + } + + spin_unlock_irqrestore(&card->lock, flags); +} + +/* ------------------------------------------------------------- */ + +static void b1dma_dispatch_tx(avmcard *card) +{ + avmcard_dmainfo *dma = card->dma; + struct sk_buff *skb; + u8 cmd, subcmd; + u16 len; + u32 txlen; + void *p; + + skb = skb_dequeue(&dma->send_queue); + + len = CAPIMSG_LEN(skb->data); + + if (len) { + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + p = dma->sendbuf.dmabuf; + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + u16 dlen = CAPIMSG_DATALEN(skb->data); + _put_byte(&p, SEND_DATA_B3_REQ); + _put_slice(&p, skb->data, len); + _put_slice(&p, skb->data + len, dlen); + } else { + _put_byte(&p, SEND_MESSAGE); + _put_slice(&p, skb->data, len); + } + txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf; +#ifdef AVM_B1DMA_DEBUG + printk(KERN_DEBUG "tx: put msg len=%d\n", txlen); +#endif + } else { + txlen = skb->len - 2; +#ifdef AVM_B1DMA_POLLDEBUG + if (skb->data[2] == SEND_POLLACK) + printk(KERN_INFO "%s: send ack\n", card->name); +#endif +#ifdef AVM_B1DMA_DEBUG + printk(KERN_DEBUG "tx: put 0x%x len=%d\n", + skb->data[2], txlen); +#endif + skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf, + skb->len - 2); + } + txlen = (txlen + 3) & ~3; + + b1dma_writel(card, dma->sendbuf.dmaaddr, AMCC_TXPTR); + b1dma_writel(card, txlen, AMCC_TXLEN); + + card->csr |= EN_TX_TC_INT; + + dev_kfree_skb_any(skb); +} + +/* ------------------------------------------------------------- */ + +static void queue_pollack(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(3, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost poll ack\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_POLLACK); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + b1dma_queue_tx(card, skb); +} + +/* ------------------------------------------------------------- */ + +static void b1dma_handle_rx(avmcard *card) +{ + avmctrl_info *cinfo = &card->ctrlinfo[0]; + avmcard_dmainfo *dma = card->dma; + struct capi_ctr *ctrl = &cinfo->capi_ctrl; + struct sk_buff *skb; + void *p = dma->recvbuf.dmabuf + 4; + u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize; + u8 b1cmd = _get_byte(&p); + +#ifdef AVM_B1DMA_DEBUG + printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen); +#endif + + switch (b1cmd) { + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + DataB3Len = _get_slice(&p, card->databuf); + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf + MsgLen, 0, 30 - MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + skb_put_data(skb, card->msgbuf, MsgLen); + skb_put_data(skb, card->databuf, DataB3Len); + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + skb_put_data(skb, card->msgbuf, MsgLen); + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) { + spin_lock(&card->lock); + capilib_data_b3_conf(&cinfo->ncci_head, ApplId, + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + spin_unlock(&card->lock); + } + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + WindowSize = _get_word(&p); + spin_lock(&card->lock); + capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize); + spin_unlock(&card->lock); + break; + + case RECEIVE_FREE_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + + if (NCCI != 0xffffffff) { + spin_lock(&card->lock); + capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI); + spin_unlock(&card->lock); + } + break; + + case RECEIVE_START: +#ifdef AVM_B1DMA_POLLDEBUG + printk(KERN_INFO "%s: receive poll\n", card->name); +#endif + if (!suppress_pollack) + queue_pollack(card); + capi_ctr_resume_output(ctrl); + break; + + case RECEIVE_STOP: + capi_ctr_suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + cinfo->versionlen = _get_slice(&p, cinfo->versionbuf); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + capi_ctr_ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + card->msgbuf[MsgLen] = 0; + while (MsgLen > 0 + && (card->msgbuf[MsgLen - 1] == '\n' + || card->msgbuf[MsgLen - 1] == '\r')) { + card->msgbuf[MsgLen - 1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = _get_slice(&p, card->msgbuf); + card->msgbuf[MsgLen] = 0; + while (MsgLen > 0 + && (card->msgbuf[MsgLen - 1] == '\n' + || card->msgbuf[MsgLen - 1] == '\r')) { + card->msgbuf[MsgLen - 1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + default: + printk(KERN_ERR "%s: b1dma_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } +} + +/* ------------------------------------------------------------- */ + +static void b1dma_handle_interrupt(avmcard *card) +{ + u32 status; + u32 newcsr; + + spin_lock(&card->lock); + + status = b1dma_readl(card, AMCC_INTCSR); + if ((status & ANY_S5933_INT) == 0) { + spin_unlock(&card->lock); + return; + } + + newcsr = card->csr | (status & ALL_INT); + if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT; + if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT; + b1dma_writel(card, newcsr, AMCC_INTCSR); + + if ((status & RX_TC_INT) != 0) { + struct avmcard_dmainfo *dma = card->dma; + u32 rxlen; + if (card->dma->recvlen == 0) { + rxlen = b1dma_readl(card, AMCC_RXLEN); + if (rxlen == 0) { + dma->recvlen = *((u32 *)dma->recvbuf.dmabuf); + rxlen = (dma->recvlen + 3) & ~3; + b1dma_writel(card, dma->recvbuf.dmaaddr + 4, AMCC_RXPTR); + b1dma_writel(card, rxlen, AMCC_RXLEN); +#ifdef AVM_B1DMA_DEBUG + } else { + printk(KERN_ERR "%s: rx not complete (%d).\n", + card->name, rxlen); +#endif + } + } else { + spin_unlock(&card->lock); + b1dma_handle_rx(card); + dma->recvlen = 0; + spin_lock(&card->lock); + b1dma_writel(card, dma->recvbuf.dmaaddr, AMCC_RXPTR); + b1dma_writel(card, 4, AMCC_RXLEN); + } + } + + if ((status & TX_TC_INT) != 0) { + if (skb_queue_empty(&card->dma->send_queue)) + card->csr &= ~EN_TX_TC_INT; + else + b1dma_dispatch_tx(card); + } + b1dma_writel(card, card->csr, AMCC_INTCSR); + + spin_unlock(&card->lock); +} + +irqreturn_t b1dma_interrupt(int interrupt, void *devptr) +{ + avmcard *card = devptr; + + b1dma_handle_interrupt(card); + return IRQ_HANDLED; +} + +/* ------------------------------------------------------------- */ + +static int b1dma_loaded(avmcard *card) +{ + unsigned long stop; + unsigned char ans; + unsigned long tout = 2; + unsigned int base = card->port; + + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_tx_empty(base)) + break; + } + if (!b1_tx_empty(base)) { + printk(KERN_ERR "%s: b1dma_loaded: tx err, corrupted t4 file ?\n", + card->name); + return 0; + } + b1_put_byte(base, SEND_POLLACK); + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_rx_full(base)) { + if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) { + return 1; + } + printk(KERN_ERR "%s: b1dma_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans); + return 0; + } + } + printk(KERN_ERR "%s: b1dma_loaded: firmware not running\n", card->name); + return 0; +} + +/* ------------------------------------------------------------- */ + +static void b1dma_send_init(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(15, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_INIT); + _put_word(&p, CAPI_MAXAPPL); + _put_word(&p, AVM_NCCI_PER_CHANNEL * 30); + _put_word(&p, card->cardnr - 1); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + b1dma_queue_tx(card, skb); +} + +int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + int retval; + + b1dma_reset(card); + + if ((retval = b1_load_t4file(card, &data->firmware))) { + b1dma_reset(card); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(card, &data->configuration))) { + b1dma_reset(card); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1dma_loaded(card)) { + b1dma_reset(card); + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + card->csr = AVM_FLAG; + b1dma_writel(card, card->csr, AMCC_INTCSR); + b1dma_writel(card, EN_A2P_TRANSFERS | EN_P2A_TRANSFERS | A2P_HI_PRIORITY | + P2A_HI_PRIORITY | RESET_A2P_FLAGS | RESET_P2A_FLAGS, + AMCC_MCSR); + t1outp(card->port, 0x07, 0x30); + t1outp(card->port, 0x10, 0xF0); + + card->dma->recvlen = 0; + b1dma_writel(card, card->dma->recvbuf.dmaaddr, AMCC_RXPTR); + b1dma_writel(card, 4, AMCC_RXLEN); + card->csr |= EN_RX_TC_INT; + b1dma_writel(card, card->csr, AMCC_INTCSR); + + b1dma_send_init(card); + + return 0; +} + +void b1dma_reset_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + b1dma_reset(card); + + memset(cinfo->version, 0, sizeof(cinfo->version)); + capilib_release(&cinfo->ncci_head); + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_down(ctrl); +} + +/* ------------------------------------------------------------- */ + +void b1dma_register_appl(struct capi_ctr *ctrl, + u16 appl, + capi_register_params *rp) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + int want = rp->level3cnt; + int nconn; + void *p; + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel; + + skb = alloc_skb(23, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_REGISTER); + _put_word(&p, appl); + _put_word(&p, 1024 * (nconn + 1)); + _put_word(&p, nconn); + _put_word(&p, rp->datablkcnt); + _put_word(&p, rp->datablklen); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + b1dma_queue_tx(card, skb); +} + +/* ------------------------------------------------------------- */ + +void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + void *p; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + capilib_release_appl(&cinfo->ncci_head, appl); + spin_unlock_irqrestore(&card->lock, flags); + + skb = alloc_skb(7, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost release appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_RELEASE); + _put_word(&p, appl); + + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + b1dma_queue_tx(card, skb); +} + +/* ------------------------------------------------------------- */ + +u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + u16 retval = CAPI_NOERROR; + + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + retval = capilib_data_b3_req(&cinfo->ncci_head, + CAPIMSG_APPID(skb->data), + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + spin_unlock_irqrestore(&card->lock, flags); + } + if (retval == CAPI_NOERROR) + b1dma_queue_tx(card, skb); + + return retval; +} + +/* ------------------------------------------------------------- */ + +int b1dma_proc_show(struct seq_file *m, void *v) +{ + struct capi_ctr *ctrl = m->private; + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + u8 flag; + char *s; + u32 txoff, txlen, rxoff, rxlen, csr; + unsigned long flags; + + seq_printf(m, "%-16s %s\n", "name", card->name); + seq_printf(m, "%-16s 0x%x\n", "io", card->port); + seq_printf(m, "%-16s %d\n", "irq", card->irq); + seq_printf(m, "%-16s 0x%lx\n", "membase", card->membase); + switch (card->cardtype) { + case avm_b1isa: s = "B1 ISA"; break; + case avm_b1pci: s = "B1 PCI"; break; + case avm_b1pcmcia: s = "B1 PCMCIA"; break; + case avm_m1: s = "M1"; break; + case avm_m2: s = "M2"; break; + case avm_t1isa: s = "T1 ISA (HEMA)"; break; + case avm_t1pci: s = "T1 PCI"; break; + case avm_c4: s = "C4"; break; + case avm_c2: s = "C2"; break; + default: s = "???"; break; + } + seq_printf(m, "%-16s %s\n", "type", s); + if ((s = cinfo->version[VER_DRIVER]) != NULL) + seq_printf(m, "%-16s %s\n", "ver_driver", s); + if ((s = cinfo->version[VER_CARDTYPE]) != NULL) + seq_printf(m, "%-16s %s\n", "ver_cardtype", s); + if ((s = cinfo->version[VER_SERIAL]) != NULL) + seq_printf(m, "%-16s %s\n", "ver_serial", s); + + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[3]; + if (flag) + seq_printf(m, "%-16s%s%s%s%s%s%s%s\n", + "protocol", + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + } + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[5]; + if (flag) + seq_printf(m, "%-16s%s%s%s%s\n", + "linetype", + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); + } + seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname); + + + spin_lock_irqsave(&card->lock, flags); + + txoff = (dma_addr_t)b1dma_readl(card, AMCC_TXPTR)-card->dma->sendbuf.dmaaddr; + txlen = b1dma_readl(card, AMCC_TXLEN); + + rxoff = (dma_addr_t)b1dma_readl(card, AMCC_RXPTR)-card->dma->recvbuf.dmaaddr; + rxlen = b1dma_readl(card, AMCC_RXLEN); + + csr = b1dma_readl(card, AMCC_INTCSR); + + spin_unlock_irqrestore(&card->lock, flags); + + seq_printf(m, "%-16s 0x%lx\n", "csr (cached)", (unsigned long)card->csr); + seq_printf(m, "%-16s 0x%lx\n", "csr", (unsigned long)csr); + seq_printf(m, "%-16s %lu\n", "txoff", (unsigned long)txoff); + seq_printf(m, "%-16s %lu\n", "txlen", (unsigned long)txlen); + seq_printf(m, "%-16s %lu\n", "rxoff", (unsigned long)rxoff); + seq_printf(m, "%-16s %lu\n", "rxlen", (unsigned long)rxlen); + + return 0; +} +EXPORT_SYMBOL(b1dma_proc_show); + +/* ------------------------------------------------------------- */ + +EXPORT_SYMBOL(b1dma_reset); +EXPORT_SYMBOL(t1pci_detect); +EXPORT_SYMBOL(b1pciv4_detect); +EXPORT_SYMBOL(b1dma_interrupt); + +EXPORT_SYMBOL(b1dma_load_firmware); +EXPORT_SYMBOL(b1dma_reset_ctr); +EXPORT_SYMBOL(b1dma_register_appl); +EXPORT_SYMBOL(b1dma_release_appl); +EXPORT_SYMBOL(b1dma_send_message); + +static int __init b1dma_init(void) +{ + char *p; + char rev[32]; + + if ((p = strchr(revision, ':')) != NULL && p[1]) { + strlcpy(rev, p + 2, sizeof(rev)); + if ((p = strchr(rev, '$')) != NULL && p > rev) + *(p - 1) = 0; + } else + strcpy(rev, "1.0"); + + printk(KERN_INFO "b1dma: revision %s\n", rev); + + return 0; +} + +static void __exit b1dma_exit(void) +{ +} + +module_init(b1dma_init); +module_exit(b1dma_exit); diff --git a/drivers/isdn/hardware/avm/b1isa.c b/drivers/isdn/hardware/avm/b1isa.c new file mode 100644 index 000000000..cdfea72e0 --- /dev/null +++ b/drivers/isdn/hardware/avm/b1isa.c @@ -0,0 +1,243 @@ +/* $Id: b1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $ + * + * Module for AVM B1 ISA-card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +/* ------------------------------------------------------------- */ + +static char *revision = "$Revision: 1.1.2.3 $"; + +/* ------------------------------------------------------------- */ + +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 ISA card"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +static void b1isa_remove(struct pci_dev *pdev) +{ + avmctrl_info *cinfo = pci_get_drvdata(pdev); + avmcard *card; + + if (!cinfo) + return; + + card = cinfo->card; + + b1_reset(card->port); + b1_reset(card->port); + + detach_capi_ctr(&cinfo->capi_ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + b1_free_card(card); +} + +/* ------------------------------------------------------------- */ + +static char *b1isa_procinfo(struct capi_ctr *ctrl); + +static int b1isa_probe(struct pci_dev *pdev) +{ + avmctrl_info *cinfo; + avmcard *card; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "b1isa: no memory.\n"); + retval = -ENOMEM; + goto err; + } + + cinfo = card->ctrlinfo; + + card->port = pci_resource_start(pdev, 0); + card->irq = pdev->irq; + card->cardtype = avm_b1isa; + sprintf(card->name, "b1isa-%x", card->port); + + if (card->port != 0x150 && card->port != 0x250 + && card->port != 0x300 && card->port != 0x340) { + printk(KERN_WARNING "b1isa: invalid port 0x%x.\n", card->port); + retval = -EINVAL; + goto err_free; + } + if (b1_irq_table[card->irq & 0xf] == 0) { + printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq); + retval = -EINVAL; + goto err_free; + } + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_WARNING "b1isa: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free; + } + retval = request_irq(card->irq, b1_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq); + goto err_release_region; + } + b1_reset(card->port); + if ((retval = b1_detect(card->port, card->cardtype)) != 0) { + printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n", + card->port, retval); + retval = -ENODEV; + goto err_free_irq; + } + b1_reset(card->port); + b1_getrevision(card); + + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "b1isa"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1_register_appl; + cinfo->capi_ctrl.release_appl = b1_release_appl; + cinfo->capi_ctrl.send_message = b1_send_message; + cinfo->capi_ctrl.load_firmware = b1_load_firmware; + cinfo->capi_ctrl.reset_ctr = b1_reset_ctr; + cinfo->capi_ctrl.procinfo = b1isa_procinfo; + cinfo->capi_ctrl.proc_show = b1_proc_show; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "b1isa: attach controller failed.\n"); + goto err_free_irq; + } + + printk(KERN_INFO "b1isa: AVM B1 ISA at i/o %#x, irq %d, revision %d\n", + card->port, card->irq, card->revision); + + pci_set_drvdata(pdev, cinfo); + return 0; + +err_free_irq: + free_irq(card->irq, card); +err_release_region: + release_region(card->port, AVMB1_PORTLEN); +err_free: + b1_free_card(card); +err: + return retval; +} + +static char *b1isa_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->revision : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +#define MAX_CARDS 4 +static struct pci_dev isa_dev[MAX_CARDS]; +static int io[MAX_CARDS]; +static int irq[MAX_CARDS]; + +module_param_hw_array(io, int, ioport, NULL, 0); +module_param_hw_array(irq, int, irq, NULL, 0); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); + +static int b1isa_add_card(struct capi_driver *driver, capicardparams *data) +{ + int i; + + for (i = 0; i < MAX_CARDS; i++) { + if (isa_dev[i].resource[0].start) + continue; + + isa_dev[i].resource[0].start = data->port; + isa_dev[i].irq = data->irq; + + if (b1isa_probe(&isa_dev[i]) == 0) + return 0; + } + return -ENODEV; +} + +static struct capi_driver capi_driver_b1isa = { + .name = "b1isa", + .revision = "1.0", + .add_card = b1isa_add_card, +}; + +static int __init b1isa_init(void) +{ + char *p; + char rev[32]; + int i; + + if ((p = strchr(revision, ':')) != NULL && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != NULL && p > rev) + *(p - 1) = 0; + } else + strcpy(rev, "1.0"); + + for (i = 0; i < MAX_CARDS; i++) { + if (!io[i]) + break; + + isa_dev[i].resource[0].start = io[i]; + isa_dev[i].irq = irq[i]; + + if (b1isa_probe(&isa_dev[i]) != 0) + return -ENODEV; + } + + strlcpy(capi_driver_b1isa.revision, rev, 32); + register_capi_driver(&capi_driver_b1isa); + printk(KERN_INFO "b1isa: revision %s\n", rev); + + return 0; +} + +static void __exit b1isa_exit(void) +{ + int i; + + for (i = 0; i < MAX_CARDS; i++) { + if (isa_dev[i].resource[0].start) + b1isa_remove(&isa_dev[i]); + } + unregister_capi_driver(&capi_driver_b1isa); +} + +module_init(b1isa_init); +module_exit(b1isa_exit); diff --git a/drivers/isdn/hardware/avm/b1pci.c b/drivers/isdn/hardware/avm/b1pci.c new file mode 100644 index 000000000..b76b57a82 --- /dev/null +++ b/drivers/isdn/hardware/avm/b1pci.c @@ -0,0 +1,416 @@ +/* $Id: b1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ + * + * Module for AVM B1 PCI-card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/capi.h> +#include <asm/io.h> +#include <linux/init.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +/* ------------------------------------------------------------- */ + +static char *revision = "$Revision: 1.1.2.2 $"; + +/* ------------------------------------------------------------- */ + +static struct pci_device_id b1pci_pci_tbl[] = { + { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, PCI_ANY_ID, PCI_ANY_ID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(pci, b1pci_pci_tbl); +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 PCI card"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +static char *b1pci_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->revision : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +static int b1pci_probe(struct capicardparams *p, struct pci_dev *pdev) +{ + avmcard *card; + avmctrl_info *cinfo; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "b1pci: no memory.\n"); + retval = -ENOMEM; + goto err; + } + + cinfo = card->ctrlinfo; + sprintf(card->name, "b1pci-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->cardtype = avm_b1pci; + + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free; + } + b1_reset(card->port); + retval = b1_detect(card->port, card->cardtype); + if (retval) { + printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n", + card->port, retval); + retval = -ENODEV; + goto err_release_region; + } + b1_reset(card->port); + b1_getrevision(card); + + retval = request_irq(card->irq, b1_interrupt, IRQF_SHARED, card->name, card); + if (retval) { + printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq); + retval = -EBUSY; + goto err_release_region; + } + + cinfo->capi_ctrl.driver_name = "b1pci"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1_register_appl; + cinfo->capi_ctrl.release_appl = b1_release_appl; + cinfo->capi_ctrl.send_message = b1_send_message; + cinfo->capi_ctrl.load_firmware = b1_load_firmware; + cinfo->capi_ctrl.reset_ctr = b1_reset_ctr; + cinfo->capi_ctrl.procinfo = b1pci_procinfo; + cinfo->capi_ctrl.proc_show = b1_proc_show; + strcpy(cinfo->capi_ctrl.name, card->name); + cinfo->capi_ctrl.owner = THIS_MODULE; + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "b1pci: attach controller failed.\n"); + goto err_free_irq; + } + + if (card->revision >= 4) { + printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, revision %d (no dma)\n", + card->port, card->irq, card->revision); + } else { + printk(KERN_INFO "b1pci: AVM B1 PCI at i/o %#x, irq %d, revision %d\n", + card->port, card->irq, card->revision); + } + + pci_set_drvdata(pdev, card); + return 0; + +err_free_irq: + free_irq(card->irq, card); +err_release_region: + release_region(card->port, AVMB1_PORTLEN); +err_free: + b1_free_card(card); +err: + return retval; +} + +static void b1pci_remove(struct pci_dev *pdev) +{ + avmcard *card = pci_get_drvdata(pdev); + avmctrl_info *cinfo = card->ctrlinfo; + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + detach_capi_ctr(&cinfo->capi_ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + b1_free_card(card); +} + +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 +/* ------------------------------------------------------------- */ + +static char *b1pciv4_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx r%d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->membase : 0, + cinfo->card ? cinfo->card->revision : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +static int b1pciv4_probe(struct capicardparams *p, struct pci_dev *pdev) +{ + avmcard *card; + avmctrl_info *cinfo; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "b1pci: no memory.\n"); + retval = -ENOMEM; + goto err; + } + + card->dma = avmcard_dma_alloc("b1pci", pdev, 2048 + 128, 2048 + 128); + if (!card->dma) { + printk(KERN_WARNING "b1pci: dma alloc.\n"); + retval = -ENOMEM; + goto err_free; + } + + cinfo = card->ctrlinfo; + sprintf(card->name, "b1pciv4-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->membase = p->membase; + card->cardtype = avm_b1pci; + + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free_dma; + } + + card->mbase = ioremap(card->membase, 64); + if (!card->mbase) { + printk(KERN_NOTICE "b1pci: can't remap memory at 0x%lx\n", + card->membase); + retval = -ENOMEM; + goto err_release_region; + } + + b1dma_reset(card); + + retval = b1pciv4_detect(card); + if (retval) { + printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n", + card->port, retval); + retval = -ENODEV; + goto err_unmap; + } + b1dma_reset(card); + b1_getrevision(card); + + retval = request_irq(card->irq, b1dma_interrupt, IRQF_SHARED, card->name, card); + if (retval) { + printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", + card->irq); + retval = -EBUSY; + goto err_unmap; + } + + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "b1pciv4"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1dma_register_appl; + cinfo->capi_ctrl.release_appl = b1dma_release_appl; + cinfo->capi_ctrl.send_message = b1dma_send_message; + cinfo->capi_ctrl.load_firmware = b1dma_load_firmware; + cinfo->capi_ctrl.reset_ctr = b1dma_reset_ctr; + cinfo->capi_ctrl.procinfo = b1pciv4_procinfo; + cinfo->capi_ctrl.proc_show = b1dma_proc_show; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "b1pci: attach controller failed.\n"); + goto err_free_irq; + } + card->cardnr = cinfo->capi_ctrl.cnr; + + printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, mem %#lx, revision %d (dma)\n", + card->port, card->irq, card->membase, card->revision); + + pci_set_drvdata(pdev, card); + return 0; + +err_free_irq: + free_irq(card->irq, card); +err_unmap: + iounmap(card->mbase); +err_release_region: + release_region(card->port, AVMB1_PORTLEN); +err_free_dma: + avmcard_dma_free(card->dma); +err_free: + b1_free_card(card); +err: + return retval; + +} + +static void b1pciv4_remove(struct pci_dev *pdev) +{ + avmcard *card = pci_get_drvdata(pdev); + avmctrl_info *cinfo = card->ctrlinfo; + + b1dma_reset(card); + + detach_capi_ctr(&cinfo->capi_ctrl); + free_irq(card->irq, card); + iounmap(card->mbase); + release_region(card->port, AVMB1_PORTLEN); + avmcard_dma_free(card->dma); + b1_free_card(card); +} + +#endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */ + +static int b1pci_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct capicardparams param; + int retval; + + if (pci_enable_device(pdev) < 0) { + printk(KERN_ERR "b1pci: failed to enable AVM-B1\n"); + return -ENODEV; + } + param.irq = pdev->irq; + + if (pci_resource_start(pdev, 2)) { /* B1 PCI V4 */ +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + pci_set_master(pdev); +#endif + param.membase = pci_resource_start(pdev, 0); + param.port = pci_resource_start(pdev, 2); + + printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n", + param.port, param.irq, param.membase); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + retval = b1pciv4_probe(¶m, pdev); +#else + retval = b1pci_probe(¶m, pdev); +#endif + if (retval != 0) { + printk(KERN_ERR "b1pci: no AVM-B1 V4 at i/o %#x, irq %d, mem %#x detected\n", + param.port, param.irq, param.membase); + } + } else { + param.membase = 0; + param.port = pci_resource_start(pdev, 1); + + printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", + param.port, param.irq); + retval = b1pci_probe(¶m, pdev); + if (retval != 0) { + printk(KERN_ERR "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n", + param.port, param.irq); + } + } + return retval; +} + +static void b1pci_pci_remove(struct pci_dev *pdev) +{ +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + avmcard *card = pci_get_drvdata(pdev); + + if (card->dma) + b1pciv4_remove(pdev); + else + b1pci_remove(pdev); +#else + b1pci_remove(pdev); +#endif +} + +static struct pci_driver b1pci_pci_driver = { + .name = "b1pci", + .id_table = b1pci_pci_tbl, + .probe = b1pci_pci_probe, + .remove = b1pci_pci_remove, +}; + +static struct capi_driver capi_driver_b1pci = { + .name = "b1pci", + .revision = "1.0", +}; +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 +static struct capi_driver capi_driver_b1pciv4 = { + .name = "b1pciv4", + .revision = "1.0", +}; +#endif + +static int __init b1pci_init(void) +{ + char *p; + char rev[32]; + int err; + + if ((p = strchr(revision, ':')) != NULL && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != NULL && p > rev) + *(p - 1) = 0; + } else + strcpy(rev, "1.0"); + + + err = pci_register_driver(&b1pci_pci_driver); + if (!err) { + strlcpy(capi_driver_b1pci.revision, rev, 32); + register_capi_driver(&capi_driver_b1pci); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + strlcpy(capi_driver_b1pciv4.revision, rev, 32); + register_capi_driver(&capi_driver_b1pciv4); +#endif + printk(KERN_INFO "b1pci: revision %s\n", rev); + } + return err; +} + +static void __exit b1pci_exit(void) +{ + unregister_capi_driver(&capi_driver_b1pci); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + unregister_capi_driver(&capi_driver_b1pciv4); +#endif + pci_unregister_driver(&b1pci_pci_driver); +} + +module_init(b1pci_init); +module_exit(b1pci_exit); diff --git a/drivers/isdn/hardware/avm/b1pcmcia.c b/drivers/isdn/hardware/avm/b1pcmcia.c new file mode 100644 index 000000000..3aca16e62 --- /dev/null +++ b/drivers/isdn/hardware/avm/b1pcmcia.c @@ -0,0 +1,224 @@ +/* $Id: b1pcmcia.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ + * + * Module for AVM B1/M1/M2 PCMCIA-card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/capi.h> +#include <linux/b1pcmcia.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +/* ------------------------------------------------------------- */ + +static char *revision = "$Revision: 1.1.2.2 $"; + +/* ------------------------------------------------------------- */ + +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM PCMCIA cards"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + detach_capi_ctr(ctrl); + free_irq(card->irq, card); + b1_free_card(card); +} + +/* ------------------------------------------------------------- */ + +static LIST_HEAD(cards); + +static char *b1pcmcia_procinfo(struct capi_ctr *ctrl); + +static int b1pcmcia_add_card(unsigned int port, unsigned irq, + enum avmcardtype cardtype) +{ + avmctrl_info *cinfo; + avmcard *card; + char *cardname; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "b1pcmcia: no memory.\n"); + retval = -ENOMEM; + goto err; + } + cinfo = card->ctrlinfo; + + switch (cardtype) { + case avm_m1: sprintf(card->name, "m1-%x", port); break; + case avm_m2: sprintf(card->name, "m2-%x", port); break; + default: sprintf(card->name, "b1pcmcia-%x", port); break; + } + card->port = port; + card->irq = irq; + card->cardtype = cardtype; + + retval = request_irq(card->irq, b1_interrupt, IRQF_SHARED, card->name, card); + if (retval) { + printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n", + card->irq); + retval = -EBUSY; + goto err_free; + } + b1_reset(card->port); + if ((retval = b1_detect(card->port, card->cardtype)) != 0) { + printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n", + card->port, retval); + retval = -ENODEV; + goto err_free_irq; + } + b1_reset(card->port); + b1_getrevision(card); + + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "b1pcmcia"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1_register_appl; + cinfo->capi_ctrl.release_appl = b1_release_appl; + cinfo->capi_ctrl.send_message = b1_send_message; + cinfo->capi_ctrl.load_firmware = b1_load_firmware; + cinfo->capi_ctrl.reset_ctr = b1_reset_ctr; + cinfo->capi_ctrl.procinfo = b1pcmcia_procinfo; + cinfo->capi_ctrl.proc_show = b1_proc_show; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "b1pcmcia: attach controller failed.\n"); + goto err_free_irq; + } + switch (cardtype) { + case avm_m1: cardname = "M1"; break; + case avm_m2: cardname = "M2"; break; + default: cardname = "B1 PCMCIA"; break; + } + + printk(KERN_INFO "b1pcmcia: AVM %s at i/o %#x, irq %d, revision %d\n", + cardname, card->port, card->irq, card->revision); + + list_add(&card->list, &cards); + return cinfo->capi_ctrl.cnr; + +err_free_irq: + free_irq(card->irq, card); +err_free: + b1_free_card(card); +err: + return retval; +} + +/* ------------------------------------------------------------- */ + +static char *b1pcmcia_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->revision : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +int b1pcmcia_addcard_b1(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(port, irq, avm_b1pcmcia); +} + +int b1pcmcia_addcard_m1(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(port, irq, avm_m1); +} + +int b1pcmcia_addcard_m2(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(port, irq, avm_m2); +} + +int b1pcmcia_delcard(unsigned int port, unsigned irq) +{ + struct list_head *l; + avmcard *card; + + list_for_each(l, &cards) { + card = list_entry(l, avmcard, list); + if (card->port == port && card->irq == irq) { + b1pcmcia_remove_ctr(&card->ctrlinfo[0].capi_ctrl); + return 0; + } + } + return -ESRCH; +} + +EXPORT_SYMBOL(b1pcmcia_addcard_b1); +EXPORT_SYMBOL(b1pcmcia_addcard_m1); +EXPORT_SYMBOL(b1pcmcia_addcard_m2); +EXPORT_SYMBOL(b1pcmcia_delcard); + +static struct capi_driver capi_driver_b1pcmcia = { + .name = "b1pcmcia", + .revision = "1.0", +}; + +static int __init b1pcmcia_init(void) +{ + char *p; + char rev[32]; + + if ((p = strchr(revision, ':')) != NULL && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != NULL && p > rev) + *(p - 1) = 0; + } else + strcpy(rev, "1.0"); + + strlcpy(capi_driver_b1pcmcia.revision, rev, 32); + register_capi_driver(&capi_driver_b1pcmcia); + printk(KERN_INFO "b1pci: revision %s\n", rev); + + return 0; +} + +static void __exit b1pcmcia_exit(void) +{ + unregister_capi_driver(&capi_driver_b1pcmcia); +} + +module_init(b1pcmcia_init); +module_exit(b1pcmcia_exit); diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c new file mode 100644 index 000000000..ac72cd204 --- /dev/null +++ b/drivers/isdn/hardware/avm/c4.c @@ -0,0 +1,1317 @@ +/* $Id: c4.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ + * + * Module for AVM C4 & C2 card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/capi.h> +#include <linux/kernelcapi.h> +#include <linux/init.h> +#include <linux/gfp.h> +#include <asm/io.h> +#include <linux/uaccess.h> +#include <linux/netdevice.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +#undef AVM_C4_DEBUG +#undef AVM_C4_POLLDEBUG + +/* ------------------------------------------------------------- */ + +static char *revision = "$Revision: 1.1.2.2 $"; + +/* ------------------------------------------------------------- */ + +static bool suppress_pollack; + +static const struct pci_device_id c4_pci_tbl[] = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, 0, 0, (unsigned long)4 }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C2, 0, 0, (unsigned long)2 }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(pci, c4_pci_tbl); +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM C2/C4 cards"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); +module_param(suppress_pollack, bool, 0); + +/* ------------------------------------------------------------- */ + +static void c4_dispatch_tx(avmcard *card); + +/* ------------------------------------------------------------- */ + +#define DC21285_DRAM_A0MR 0x40000000 +#define DC21285_DRAM_A1MR 0x40004000 +#define DC21285_DRAM_A2MR 0x40008000 +#define DC21285_DRAM_A3MR 0x4000C000 + +#define CAS_OFFSET 0x88 + +#define DC21285_ARMCSR_BASE 0x42000000 + +#define PCI_OUT_INT_STATUS 0x30 +#define PCI_OUT_INT_MASK 0x34 +#define MAILBOX_0 0x50 +#define MAILBOX_1 0x54 +#define MAILBOX_2 0x58 +#define MAILBOX_3 0x5C +#define DOORBELL 0x60 +#define DOORBELL_SETUP 0x64 + +#define CHAN_1_CONTROL 0x90 +#define CHAN_2_CONTROL 0xB0 +#define DRAM_TIMING 0x10C +#define DRAM_ADDR_SIZE_0 0x110 +#define DRAM_ADDR_SIZE_1 0x114 +#define DRAM_ADDR_SIZE_2 0x118 +#define DRAM_ADDR_SIZE_3 0x11C +#define SA_CONTROL 0x13C +#define XBUS_CYCLE 0x148 +#define XBUS_STROBE 0x14C +#define DBELL_PCI_MASK 0x150 +#define DBELL_SA_MASK 0x154 + +#define SDRAM_SIZE 0x1000000 + +/* ------------------------------------------------------------- */ + +#define MBOX_PEEK_POKE MAILBOX_0 + +#define DBELL_ADDR 0x01 +#define DBELL_DATA 0x02 +#define DBELL_RNWR 0x40 +#define DBELL_INIT 0x80 + +/* ------------------------------------------------------------- */ + +#define MBOX_UP_ADDR MAILBOX_0 +#define MBOX_UP_LEN MAILBOX_1 +#define MBOX_DOWN_ADDR MAILBOX_2 +#define MBOX_DOWN_LEN MAILBOX_3 + +#define DBELL_UP_HOST 0x00000100 +#define DBELL_UP_ARM 0x00000200 +#define DBELL_DOWN_HOST 0x00000400 +#define DBELL_DOWN_ARM 0x00000800 +#define DBELL_RESET_HOST 0x40000000 +#define DBELL_RESET_ARM 0x80000000 + +/* ------------------------------------------------------------- */ + +#define DRAM_TIMING_DEF 0x001A01A5 +#define DRAM_AD_SZ_DEF0 0x00000045 +#define DRAM_AD_SZ_NULL 0x00000000 + +#define SA_CTL_ALLRIGHT 0x64AA0271 + +#define INIT_XBUS_CYCLE 0x100016DB +#define INIT_XBUS_STROBE 0xF1F1F1F1 + +/* ------------------------------------------------------------- */ + +#define RESET_TIMEOUT (15 * HZ) /* 15 sec */ +#define PEEK_POKE_TIMEOUT (HZ / 10) /* 0.1 sec */ + +/* ------------------------------------------------------------- */ + +#define c4outmeml(addr, value) writel(value, addr) +#define c4inmeml(addr) readl(addr) +#define c4outmemw(addr, value) writew(value, addr) +#define c4inmemw(addr) readw(addr) +#define c4outmemb(addr, value) writeb(value, addr) +#define c4inmemb(addr) readb(addr) + +/* ------------------------------------------------------------- */ + +static inline int wait_for_doorbell(avmcard *card, unsigned long t) +{ + unsigned long stop; + + stop = jiffies + t; + while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) { + if (!time_before(jiffies, stop)) + return -1; + mb(); + } + return 0; +} + +static int c4_poke(avmcard *card, unsigned long off, unsigned long value) +{ + + if (wait_for_doorbell(card, HZ / 10) < 0) + return -1; + + c4outmeml(card->mbase + MBOX_PEEK_POKE, off); + c4outmeml(card->mbase + DOORBELL, DBELL_ADDR); + + if (wait_for_doorbell(card, HZ / 10) < 0) + return -1; + + c4outmeml(card->mbase + MBOX_PEEK_POKE, value); + c4outmeml(card->mbase + DOORBELL, DBELL_DATA | DBELL_ADDR); + + return 0; +} + +static int c4_peek(avmcard *card, unsigned long off, unsigned long *valuep) +{ + if (wait_for_doorbell(card, HZ / 10) < 0) + return -1; + + c4outmeml(card->mbase + MBOX_PEEK_POKE, off); + c4outmeml(card->mbase + DOORBELL, DBELL_RNWR | DBELL_ADDR); + + if (wait_for_doorbell(card, HZ / 10) < 0) + return -1; + + *valuep = c4inmeml(card->mbase + MBOX_PEEK_POKE); + + return 0; +} + +/* ------------------------------------------------------------- */ + +static int c4_load_t4file(avmcard *card, capiloaddatapart *t4file) +{ + u32 val; + unsigned char *dp; + u_int left; + u32 loadoff = 0; + + dp = t4file->data; + left = t4file->len; + while (left >= sizeof(u32)) { + if (t4file->user) { + if (copy_from_user(&val, dp, sizeof(val))) + return -EFAULT; + } else { + memcpy(&val, dp, sizeof(val)); + } + if (c4_poke(card, loadoff, val)) { + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); + return -EIO; + } + left -= sizeof(u32); + dp += sizeof(u32); + loadoff += sizeof(u32); + } + if (left) { + val = 0; + if (t4file->user) { + if (copy_from_user(&val, dp, left)) + return -EFAULT; + } else { + memcpy(&val, dp, left); + } + if (c4_poke(card, loadoff, val)) { + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); + return -EIO; + } + } + return 0; +} + +/* ------------------------------------------------------------- */ + +static inline void _put_byte(void **pp, u8 val) +{ + u8 *s = *pp; + *s++ = val; + *pp = s; +} + +static inline void _put_word(void **pp, u32 val) +{ + u8 *s = *pp; + *s++ = val & 0xff; + *s++ = (val >> 8) & 0xff; + *s++ = (val >> 16) & 0xff; + *s++ = (val >> 24) & 0xff; + *pp = s; +} + +static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len) +{ + unsigned i = len; + _put_word(pp, i); + while (i-- > 0) + _put_byte(pp, *dp++); +} + +static inline u8 _get_byte(void **pp) +{ + u8 *s = *pp; + u8 val; + val = *s++; + *pp = s; + return val; +} + +static inline u32 _get_word(void **pp) +{ + u8 *s = *pp; + u32 val; + val = *s++; + val |= (*s++ << 8); + val |= (*s++ << 16); + val |= (*s++ << 24); + *pp = s; + return val; +} + +static inline u32 _get_slice(void **pp, unsigned char *dp) +{ + unsigned int len, i; + + len = i = _get_word(pp); + while (i-- > 0) *dp++ = _get_byte(pp); + return len; +} + +/* ------------------------------------------------------------- */ + +static void c4_reset(avmcard *card) +{ + unsigned long stop; + + c4outmeml(card->mbase + DOORBELL, DBELL_RESET_ARM); + + stop = jiffies + HZ * 10; + while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) { + if (!time_before(jiffies, stop)) + return; + c4outmeml(card->mbase + DOORBELL, DBELL_ADDR); + mb(); + } + + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0); + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0); +} + +/* ------------------------------------------------------------- */ + +static int c4_detect(avmcard *card) +{ + unsigned long stop, dummy; + + c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x0c); + if (c4inmeml(card->mbase + PCI_OUT_INT_MASK) != 0x0c) + return 1; + + c4outmeml(card->mbase + DOORBELL, DBELL_RESET_ARM); + + stop = jiffies + HZ * 10; + while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) { + if (!time_before(jiffies, stop)) + return 2; + c4outmeml(card->mbase + DOORBELL, DBELL_ADDR); + mb(); + } + + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0); + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0); + + c4outmeml(card->mbase + MAILBOX_0, 0x55aa55aa); + if (c4inmeml(card->mbase + MAILBOX_0) != 0x55aa55aa) return 3; + + c4outmeml(card->mbase + MAILBOX_0, 0xaa55aa55); + if (c4inmeml(card->mbase + MAILBOX_0) != 0xaa55aa55) return 4; + + if (c4_poke(card, DC21285_ARMCSR_BASE + DBELL_SA_MASK, 0)) return 5; + if (c4_poke(card, DC21285_ARMCSR_BASE + DBELL_PCI_MASK, 0)) return 6; + if (c4_poke(card, DC21285_ARMCSR_BASE + SA_CONTROL, SA_CTL_ALLRIGHT)) + return 7; + if (c4_poke(card, DC21285_ARMCSR_BASE + XBUS_CYCLE, INIT_XBUS_CYCLE)) + return 8; + if (c4_poke(card, DC21285_ARMCSR_BASE + XBUS_STROBE, INIT_XBUS_STROBE)) + return 8; + if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_TIMING, 0)) return 9; + + mdelay(1); + + if (c4_peek(card, DC21285_DRAM_A0MR, &dummy)) return 10; + if (c4_peek(card, DC21285_DRAM_A1MR, &dummy)) return 11; + if (c4_peek(card, DC21285_DRAM_A2MR, &dummy)) return 12; + if (c4_peek(card, DC21285_DRAM_A3MR, &dummy)) return 13; + + if (c4_poke(card, DC21285_DRAM_A0MR + CAS_OFFSET, 0)) return 14; + if (c4_poke(card, DC21285_DRAM_A1MR + CAS_OFFSET, 0)) return 15; + if (c4_poke(card, DC21285_DRAM_A2MR + CAS_OFFSET, 0)) return 16; + if (c4_poke(card, DC21285_DRAM_A3MR + CAS_OFFSET, 0)) return 17; + + mdelay(1); + + if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_TIMING, DRAM_TIMING_DEF)) + return 18; + + if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_0, DRAM_AD_SZ_DEF0)) + return 19; + if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_1, DRAM_AD_SZ_NULL)) + return 20; + if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_2, DRAM_AD_SZ_NULL)) + return 21; + if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_3, DRAM_AD_SZ_NULL)) + return 22; + + /* Transputer test */ + + if (c4_poke(card, 0x000000, 0x11111111) + || c4_poke(card, 0x400000, 0x22222222) + || c4_poke(card, 0x800000, 0x33333333) + || c4_poke(card, 0xC00000, 0x44444444)) + return 23; + + if (c4_peek(card, 0x000000, &dummy) || dummy != 0x11111111 + || c4_peek(card, 0x400000, &dummy) || dummy != 0x22222222 + || c4_peek(card, 0x800000, &dummy) || dummy != 0x33333333 + || c4_peek(card, 0xC00000, &dummy) || dummy != 0x44444444) + return 24; + + if (c4_poke(card, 0x000000, 0x55555555) + || c4_poke(card, 0x400000, 0x66666666) + || c4_poke(card, 0x800000, 0x77777777) + || c4_poke(card, 0xC00000, 0x88888888)) + return 25; + + if (c4_peek(card, 0x000000, &dummy) || dummy != 0x55555555 + || c4_peek(card, 0x400000, &dummy) || dummy != 0x66666666 + || c4_peek(card, 0x800000, &dummy) || dummy != 0x77777777 + || c4_peek(card, 0xC00000, &dummy) || dummy != 0x88888888) + return 26; + + return 0; +} + +/* ------------------------------------------------------------- */ + +static void c4_dispatch_tx(avmcard *card) +{ + avmcard_dmainfo *dma = card->dma; + struct sk_buff *skb; + u8 cmd, subcmd; + u16 len; + u32 txlen; + void *p; + + + if (card->csr & DBELL_DOWN_ARM) { /* tx busy */ + return; + } + + skb = skb_dequeue(&dma->send_queue); + if (!skb) { +#ifdef AVM_C4_DEBUG + printk(KERN_DEBUG "%s: tx underrun\n", card->name); +#endif + return; + } + + len = CAPIMSG_LEN(skb->data); + + if (len) { + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + p = dma->sendbuf.dmabuf; + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + u16 dlen = CAPIMSG_DATALEN(skb->data); + _put_byte(&p, SEND_DATA_B3_REQ); + _put_slice(&p, skb->data, len); + _put_slice(&p, skb->data + len, dlen); + } else { + _put_byte(&p, SEND_MESSAGE); + _put_slice(&p, skb->data, len); + } + txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf; +#ifdef AVM_C4_DEBUG + printk(KERN_DEBUG "%s: tx put msg len=%d\n", card->name, txlen); +#endif + } else { + txlen = skb->len - 2; +#ifdef AVM_C4_POLLDEBUG + if (skb->data[2] == SEND_POLLACK) + printk(KERN_INFO "%s: ack to c4\n", card->name); +#endif +#ifdef AVM_C4_DEBUG + printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n", + card->name, skb->data[2], txlen); +#endif + skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf, + skb->len - 2); + } + txlen = (txlen + 3) & ~3; + + c4outmeml(card->mbase + MBOX_DOWN_ADDR, dma->sendbuf.dmaaddr); + c4outmeml(card->mbase + MBOX_DOWN_LEN, txlen); + + card->csr |= DBELL_DOWN_ARM; + + c4outmeml(card->mbase + DOORBELL, DBELL_DOWN_ARM); + + dev_kfree_skb_any(skb); +} + +/* ------------------------------------------------------------- */ + +static void queue_pollack(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(3, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost poll ack\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_POLLACK); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); +} + +/* ------------------------------------------------------------- */ + +static void c4_handle_rx(avmcard *card) +{ + avmcard_dmainfo *dma = card->dma; + struct capi_ctr *ctrl; + avmctrl_info *cinfo; + struct sk_buff *skb; + void *p = dma->recvbuf.dmabuf; + u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize; + u8 b1cmd = _get_byte(&p); + u32 cidx; + + +#ifdef AVM_C4_DEBUG + printk(KERN_DEBUG "%s: rx 0x%x len=%lu\n", card->name, + b1cmd, (unsigned long)dma->recvlen); +#endif + + switch (b1cmd) { + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + DataB3Len = _get_slice(&p, card->databuf); + cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr; + if (cidx >= card->nlogcontr) cidx = 0; + ctrl = &card->ctrlinfo[cidx].capi_ctrl; + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf + MsgLen, 0, 30 - MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + skb_put_data(skb, card->msgbuf, MsgLen); + skb_put_data(skb, card->databuf, DataB3Len); + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr; + if (cidx >= card->nlogcontr) cidx = 0; + cinfo = &card->ctrlinfo[cidx]; + ctrl = &card->ctrlinfo[cidx].capi_ctrl; + + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + skb_put_data(skb, card->msgbuf, MsgLen); + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) + capilib_data_b3_conf(&cinfo->ncci_head, ApplId, + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + WindowSize = _get_word(&p); + cidx = (NCCI & 0x7f) - card->cardnr; + if (cidx >= card->nlogcontr) cidx = 0; + + capilib_new_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + + if (NCCI != 0xffffffff) { + cidx = (NCCI & 0x7f) - card->cardnr; + if (cidx >= card->nlogcontr) cidx = 0; + capilib_free_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI); + } + break; + + case RECEIVE_START: +#ifdef AVM_C4_POLLDEBUG + printk(KERN_INFO "%s: poll from c4\n", card->name); +#endif + if (!suppress_pollack) + queue_pollack(card); + for (cidx = 0; cidx < card->nr_controllers; cidx++) { + ctrl = &card->ctrlinfo[cidx].capi_ctrl; + capi_ctr_resume_output(ctrl); + } + break; + + case RECEIVE_STOP: + for (cidx = 0; cidx < card->nr_controllers; cidx++) { + ctrl = &card->ctrlinfo[cidx].capi_ctrl; + capi_ctr_suspend_output(ctrl); + } + break; + + case RECEIVE_INIT: + + cidx = card->nlogcontr; + if (cidx >= card->nr_controllers) { + printk(KERN_ERR "%s: card with %d controllers ??\n", + card->name, cidx + 1); + break; + } + card->nlogcontr++; + cinfo = &card->ctrlinfo[cidx]; + ctrl = &cinfo->capi_ctrl; + cinfo->versionlen = _get_slice(&p, cinfo->versionbuf); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + capi_ctr_ready(&cinfo->capi_ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + card->msgbuf[MsgLen] = 0; + while (MsgLen > 0 + && (card->msgbuf[MsgLen - 1] == '\n' + || card->msgbuf[MsgLen - 1] == '\r')) { + card->msgbuf[MsgLen - 1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = _get_slice(&p, card->msgbuf); + card->msgbuf[MsgLen] = 0; + while (MsgLen > 0 + && (card->msgbuf[MsgLen - 1] == '\n' + || card->msgbuf[MsgLen - 1] == '\r')) { + card->msgbuf[MsgLen - 1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + default: + printk(KERN_ERR "%s: c4_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } +} + +/* ------------------------------------------------------------- */ + +static irqreturn_t c4_handle_interrupt(avmcard *card) +{ + unsigned long flags; + u32 status; + + spin_lock_irqsave(&card->lock, flags); + status = c4inmeml(card->mbase + DOORBELL); + + if (status & DBELL_RESET_HOST) { + u_int i; + c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x0c); + spin_unlock_irqrestore(&card->lock, flags); + if (card->nlogcontr == 0) + return IRQ_HANDLED; + printk(KERN_ERR "%s: unexpected reset\n", card->name); + for (i = 0; i < card->nr_controllers; i++) { + avmctrl_info *cinfo = &card->ctrlinfo[i]; + memset(cinfo->version, 0, sizeof(cinfo->version)); + spin_lock_irqsave(&card->lock, flags); + capilib_release(&cinfo->ncci_head); + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_down(&cinfo->capi_ctrl); + } + card->nlogcontr = 0; + return IRQ_HANDLED; + } + + status &= (DBELL_UP_HOST | DBELL_DOWN_HOST); + if (!status) { + spin_unlock_irqrestore(&card->lock, flags); + return IRQ_HANDLED; + } + c4outmeml(card->mbase + DOORBELL, status); + + if ((status & DBELL_UP_HOST) != 0) { + card->dma->recvlen = c4inmeml(card->mbase + MBOX_UP_LEN); + c4outmeml(card->mbase + MBOX_UP_LEN, 0); + c4_handle_rx(card); + card->dma->recvlen = 0; + c4outmeml(card->mbase + MBOX_UP_LEN, card->dma->recvbuf.size); + c4outmeml(card->mbase + DOORBELL, DBELL_UP_ARM); + } + + if ((status & DBELL_DOWN_HOST) != 0) { + card->csr &= ~DBELL_DOWN_ARM; + c4_dispatch_tx(card); + } else if (card->csr & DBELL_DOWN_HOST) { + if (c4inmeml(card->mbase + MBOX_DOWN_LEN) == 0) { + card->csr &= ~DBELL_DOWN_ARM; + c4_dispatch_tx(card); + } + } + spin_unlock_irqrestore(&card->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t c4_interrupt(int interrupt, void *devptr) +{ + avmcard *card = devptr; + + return c4_handle_interrupt(card); +} + +/* ------------------------------------------------------------- */ + +static void c4_send_init(avmcard *card) +{ + struct sk_buff *skb; + void *p; + unsigned long flags; + + skb = alloc_skb(15, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_INIT); + _put_word(&p, CAPI_MAXAPPL); + _put_word(&p, AVM_NCCI_PER_CHANNEL * 30); + _put_word(&p, card->cardnr - 1); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + spin_lock_irqsave(&card->lock, flags); + c4_dispatch_tx(card); + spin_unlock_irqrestore(&card->lock, flags); +} + +static int queue_sendconfigword(avmcard *card, u32 val) +{ + struct sk_buff *skb; + unsigned long flags; + void *p; + + skb = alloc_skb(3 + 4, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, send config\n", + card->name); + return -ENOMEM; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_CONFIG); + _put_word(&p, val); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + spin_lock_irqsave(&card->lock, flags); + c4_dispatch_tx(card); + spin_unlock_irqrestore(&card->lock, flags); + return 0; +} + +static int queue_sendconfig(avmcard *card, char cval[4]) +{ + struct sk_buff *skb; + unsigned long flags; + void *p; + + skb = alloc_skb(3 + 4, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, send config\n", + card->name); + return -ENOMEM; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_CONFIG); + _put_byte(&p, cval[0]); + _put_byte(&p, cval[1]); + _put_byte(&p, cval[2]); + _put_byte(&p, cval[3]); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + + spin_lock_irqsave(&card->lock, flags); + c4_dispatch_tx(card); + spin_unlock_irqrestore(&card->lock, flags); + return 0; +} + +static int c4_send_config(avmcard *card, capiloaddatapart *config) +{ + u8 val[4]; + unsigned char *dp; + u_int left; + int retval; + + if ((retval = queue_sendconfigword(card, 1)) != 0) + return retval; + if ((retval = queue_sendconfigword(card, config->len)) != 0) + return retval; + + dp = config->data; + left = config->len; + while (left >= sizeof(u32)) { + if (config->user) { + if (copy_from_user(val, dp, sizeof(val))) + return -EFAULT; + } else { + memcpy(val, dp, sizeof(val)); + } + if ((retval = queue_sendconfig(card, val)) != 0) + return retval; + left -= sizeof(val); + dp += sizeof(val); + } + if (left) { + memset(val, 0, sizeof(val)); + if (config->user) { + if (copy_from_user(&val, dp, left)) + return -EFAULT; + } else { + memcpy(&val, dp, left); + } + if ((retval = queue_sendconfig(card, val)) != 0) + return retval; + } + + return 0; +} + +static int c4_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + int retval; + + if ((retval = c4_load_t4file(card, &data->firmware))) { + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + c4_reset(card); + return retval; + } + + card->csr = 0; + c4outmeml(card->mbase + MBOX_UP_LEN, 0); + c4outmeml(card->mbase + MBOX_DOWN_LEN, 0); + c4outmeml(card->mbase + DOORBELL, DBELL_INIT); + mdelay(1); + c4outmeml(card->mbase + DOORBELL, + DBELL_UP_HOST | DBELL_DOWN_HOST | DBELL_RESET_HOST); + + c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x08); + + card->dma->recvlen = 0; + c4outmeml(card->mbase + MBOX_UP_ADDR, card->dma->recvbuf.dmaaddr); + c4outmeml(card->mbase + MBOX_UP_LEN, card->dma->recvbuf.size); + c4outmeml(card->mbase + DOORBELL, DBELL_UP_ARM); + + if (data->configuration.len > 0 && data->configuration.data) { + retval = c4_send_config(card, &data->configuration); + if (retval) { + printk(KERN_ERR "%s: failed to set config!!\n", + card->name); + c4_reset(card); + return retval; + } + } + + c4_send_init(card); + + return 0; +} + + +static void c4_reset_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card; + avmctrl_info *cinfo; + u_int i; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + c4_reset(card); + + spin_unlock_irqrestore(&card->lock, flags); + + for (i = 0; i < card->nr_controllers; i++) { + cinfo = &card->ctrlinfo[i]; + memset(cinfo->version, 0, sizeof(cinfo->version)); + capi_ctr_down(&cinfo->capi_ctrl); + } + card->nlogcontr = 0; +} + +static void c4_remove(struct pci_dev *pdev) +{ + avmcard *card = pci_get_drvdata(pdev); + avmctrl_info *cinfo; + u_int i; + + if (!card) + return; + + c4_reset(card); + + for (i = 0; i < card->nr_controllers; i++) { + cinfo = &card->ctrlinfo[i]; + detach_capi_ctr(&cinfo->capi_ctrl); + } + + free_irq(card->irq, card); + iounmap(card->mbase); + release_region(card->port, AVMB1_PORTLEN); + avmcard_dma_free(card->dma); + pci_set_drvdata(pdev, NULL); + b1_free_card(card); +} + +/* ------------------------------------------------------------- */ + + +static void c4_register_appl(struct capi_ctr *ctrl, + u16 appl, + capi_register_params *rp) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + int want = rp->level3cnt; + unsigned long flags; + int nconn; + void *p; + + if (ctrl->cnr == card->cardnr) { + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * 4 * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel * 4; + + skb = alloc_skb(23, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_REGISTER); + _put_word(&p, appl); + _put_word(&p, 1024 * (nconn + 1)); + _put_word(&p, nconn); + _put_word(&p, rp->datablkcnt); + _put_word(&p, rp->datablklen); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + + spin_lock_irqsave(&card->lock, flags); + c4_dispatch_tx(card); + spin_unlock_irqrestore(&card->lock, flags); + } +} + +/* ------------------------------------------------------------- */ + +static void c4_release_appl(struct capi_ctr *ctrl, u16 appl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned long flags; + struct sk_buff *skb; + void *p; + + spin_lock_irqsave(&card->lock, flags); + capilib_release_appl(&cinfo->ncci_head, appl); + spin_unlock_irqrestore(&card->lock, flags); + + if (ctrl->cnr == card->cardnr) { + skb = alloc_skb(7, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost release appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_RELEASE); + _put_word(&p, appl); + + skb_put(skb, (u8 *)p - (u8 *)skb->data); + skb_queue_tail(&card->dma->send_queue, skb); + spin_lock_irqsave(&card->lock, flags); + c4_dispatch_tx(card); + spin_unlock_irqrestore(&card->lock, flags); + } +} + +/* ------------------------------------------------------------- */ + + +static u16 c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + u16 retval = CAPI_NOERROR; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { + retval = capilib_data_b3_req(&cinfo->ncci_head, + CAPIMSG_APPID(skb->data), + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + } + if (retval == CAPI_NOERROR) { + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); + } + spin_unlock_irqrestore(&card->lock, flags); + return retval; +} + +/* ------------------------------------------------------------- */ + +static char *c4_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->membase : 0 + ); + return cinfo->infobuf; +} + +static int c4_proc_show(struct seq_file *m, void *v) +{ + struct capi_ctr *ctrl = m->private; + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + u8 flag; + char *s; + + seq_printf(m, "%-16s %s\n", "name", card->name); + seq_printf(m, "%-16s 0x%x\n", "io", card->port); + seq_printf(m, "%-16s %d\n", "irq", card->irq); + seq_printf(m, "%-16s 0x%lx\n", "membase", card->membase); + switch (card->cardtype) { + case avm_b1isa: s = "B1 ISA"; break; + case avm_b1pci: s = "B1 PCI"; break; + case avm_b1pcmcia: s = "B1 PCMCIA"; break; + case avm_m1: s = "M1"; break; + case avm_m2: s = "M2"; break; + case avm_t1isa: s = "T1 ISA (HEMA)"; break; + case avm_t1pci: s = "T1 PCI"; break; + case avm_c4: s = "C4"; break; + case avm_c2: s = "C2"; break; + default: s = "???"; break; + } + seq_printf(m, "%-16s %s\n", "type", s); + if ((s = cinfo->version[VER_DRIVER]) != NULL) + seq_printf(m, "%-16s %s\n", "ver_driver", s); + if ((s = cinfo->version[VER_CARDTYPE]) != NULL) + seq_printf(m, "%-16s %s\n", "ver_cardtype", s); + if ((s = cinfo->version[VER_SERIAL]) != NULL) + seq_printf(m, "%-16s %s\n", "ver_serial", s); + + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[3]; + if (flag) + seq_printf(m, "%-16s%s%s%s%s%s%s%s\n", + "protocol", + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + } + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[5]; + if (flag) + seq_printf(m, "%-16s%s%s%s%s\n", + "linetype", + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); + } + seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname); + + return 0; +} + +/* ------------------------------------------------------------- */ + +static int c4_add_card(struct capicardparams *p, struct pci_dev *dev, + int nr_controllers) +{ + avmcard *card; + avmctrl_info *cinfo; + int retval; + int i; + + card = b1_alloc_card(nr_controllers); + if (!card) { + printk(KERN_WARNING "c4: no memory.\n"); + retval = -ENOMEM; + goto err; + } + card->dma = avmcard_dma_alloc("c4", dev, 2048 + 128, 2048 + 128); + if (!card->dma) { + printk(KERN_WARNING "c4: no memory.\n"); + retval = -ENOMEM; + goto err_free; + } + + sprintf(card->name, "c%d-%x", nr_controllers, p->port); + card->port = p->port; + card->irq = p->irq; + card->membase = p->membase; + card->cardtype = (nr_controllers == 4) ? avm_c4 : avm_c2; + + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_WARNING "c4: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free_dma; + } + + card->mbase = ioremap(card->membase, 128); + if (card->mbase == NULL) { + printk(KERN_NOTICE "c4: can't remap memory at 0x%lx\n", + card->membase); + retval = -EIO; + goto err_release_region; + } + + retval = c4_detect(card); + if (retval != 0) { + printk(KERN_NOTICE "c4: NO card at 0x%x error(%d)\n", + card->port, retval); + retval = -EIO; + goto err_unmap; + } + c4_reset(card); + + retval = request_irq(card->irq, c4_interrupt, IRQF_SHARED, card->name, card); + if (retval) { + printk(KERN_ERR "c4: unable to get IRQ %d.\n", card->irq); + retval = -EBUSY; + goto err_unmap; + } + + for (i = 0; i < nr_controllers; i++) { + cinfo = &card->ctrlinfo[i]; + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "c4"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = c4_register_appl; + cinfo->capi_ctrl.release_appl = c4_release_appl; + cinfo->capi_ctrl.send_message = c4_send_message; + cinfo->capi_ctrl.load_firmware = c4_load_firmware; + cinfo->capi_ctrl.reset_ctr = c4_reset_ctr; + cinfo->capi_ctrl.procinfo = c4_procinfo; + cinfo->capi_ctrl.proc_show = c4_proc_show; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "c4: attach controller failed (%d).\n", i); + for (i--; i >= 0; i--) { + cinfo = &card->ctrlinfo[i]; + detach_capi_ctr(&cinfo->capi_ctrl); + } + goto err_free_irq; + } + if (i == 0) + card->cardnr = cinfo->capi_ctrl.cnr; + } + + printk(KERN_INFO "c4: AVM C%d at i/o %#x, irq %d, mem %#lx\n", + nr_controllers, card->port, card->irq, + card->membase); + pci_set_drvdata(dev, card); + return 0; + +err_free_irq: + free_irq(card->irq, card); +err_unmap: + iounmap(card->mbase); +err_release_region: + release_region(card->port, AVMB1_PORTLEN); +err_free_dma: + avmcard_dma_free(card->dma); +err_free: + b1_free_card(card); +err: + return retval; +} + +/* ------------------------------------------------------------- */ + +static int c4_probe(struct pci_dev *dev, const struct pci_device_id *ent) +{ + int nr = ent->driver_data; + int retval = 0; + struct capicardparams param; + + if (pci_enable_device(dev) < 0) { + printk(KERN_ERR "c4: failed to enable AVM-C%d\n", nr); + return -ENODEV; + } + pci_set_master(dev); + + param.port = pci_resource_start(dev, 1); + param.irq = dev->irq; + param.membase = pci_resource_start(dev, 0); + + printk(KERN_INFO "c4: PCI BIOS reports AVM-C%d at i/o %#x, irq %d, mem %#x\n", + nr, param.port, param.irq, param.membase); + + retval = c4_add_card(¶m, dev, nr); + if (retval != 0) { + printk(KERN_ERR "c4: no AVM-C%d at i/o %#x, irq %d detected, mem %#x\n", + nr, param.port, param.irq, param.membase); + pci_disable_device(dev); + return -ENODEV; + } + return 0; +} + +static struct pci_driver c4_pci_driver = { + .name = "c4", + .id_table = c4_pci_tbl, + .probe = c4_probe, + .remove = c4_remove, +}; + +static struct capi_driver capi_driver_c2 = { + .name = "c2", + .revision = "1.0", +}; + +static struct capi_driver capi_driver_c4 = { + .name = "c4", + .revision = "1.0", +}; + +static int __init c4_init(void) +{ + char *p; + char rev[32]; + int err; + + if ((p = strchr(revision, ':')) != NULL && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != NULL && p > rev) + *(p - 1) = 0; + } else + strcpy(rev, "1.0"); + + err = pci_register_driver(&c4_pci_driver); + if (!err) { + strlcpy(capi_driver_c2.revision, rev, 32); + register_capi_driver(&capi_driver_c2); + strlcpy(capi_driver_c4.revision, rev, 32); + register_capi_driver(&capi_driver_c4); + printk(KERN_INFO "c4: revision %s\n", rev); + } + return err; +} + +static void __exit c4_exit(void) +{ + unregister_capi_driver(&capi_driver_c2); + unregister_capi_driver(&capi_driver_c4); + pci_unregister_driver(&c4_pci_driver); +} + +module_init(c4_init); +module_exit(c4_exit); diff --git a/drivers/isdn/hardware/avm/t1isa.c b/drivers/isdn/hardware/avm/t1isa.c new file mode 100644 index 000000000..2153619c5 --- /dev/null +++ b/drivers/isdn/hardware/avm/t1isa.c @@ -0,0 +1,594 @@ +/* $Id: t1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $ + * + * Module for AVM T1 HEMA-card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <linux/netdevice.h> +#include <linux/kernelcapi.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/gfp.h> +#include <asm/io.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +/* ------------------------------------------------------------- */ + +static char *revision = "$Revision: 1.1.2.3 $"; + +/* ------------------------------------------------------------- */ + +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 HEMA ISA card"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +static int hema_irq_table[16] = +{0, + 0, + 0, + 0x80, /* irq 3 */ + 0, + 0x90, /* irq 5 */ + 0, + 0xA0, /* irq 7 */ + 0, + 0xB0, /* irq 9 */ + 0xC0, /* irq 10 */ + 0xD0, /* irq 11 */ + 0xE0, /* irq 12 */ + 0, + 0, + 0xF0, /* irq 15 */ +}; + +static int t1_detectandinit(unsigned int base, unsigned irq, int cardnr) +{ + unsigned char cregs[8]; + unsigned char reverse_cardnr; + unsigned char dummy; + int i; + + reverse_cardnr = ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1) + | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3); + cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf); + cregs[1] = 0x00; /* fast & slow link connected to CON1 */ + cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */ + cregs[3] = 0; + cregs[4] = 0x11; /* zero wait state */ + cregs[5] = hema_irq_table[irq & 0xf]; + cregs[6] = 0; + cregs[7] = 0; + + /* + * no one else should use the ISA bus in this moment, + * but no function there to prevent this :-( + * save_flags(flags); cli(); + */ + + /* board reset */ + t1outp(base, T1_RESETBOARD, 0xf); + mdelay(100); + dummy = t1inp(base, T1_FASTLINK + T1_OUTSTAT); /* first read */ + + /* write config */ + dummy = (base >> 4) & 0xff; + for (i = 1; i <= 0xf; i++) t1outp(base, i, dummy); + t1outp(base, HEMA_PAL_ID & 0xf, dummy); + t1outp(base, HEMA_PAL_ID >> 4, cregs[0]); + for (i = 1; i < 7; i++) t1outp(base, 0, cregs[i]); + t1outp(base, ((base >> 4)) & 0x3, cregs[7]); + /* restore_flags(flags); */ + + mdelay(100); + t1outp(base, T1_FASTLINK + T1_RESETLINK, 0); + t1outp(base, T1_SLOWLINK + T1_RESETLINK, 0); + mdelay(10); + t1outp(base, T1_FASTLINK + T1_RESETLINK, 1); + t1outp(base, T1_SLOWLINK + T1_RESETLINK, 1); + mdelay(100); + t1outp(base, T1_FASTLINK + T1_RESETLINK, 0); + t1outp(base, T1_SLOWLINK + T1_RESETLINK, 0); + mdelay(10); + t1outp(base, T1_FASTLINK + T1_ANALYSE, 0); + mdelay(5); + t1outp(base, T1_SLOWLINK + T1_ANALYSE, 0); + + if (t1inp(base, T1_FASTLINK + T1_OUTSTAT) != 0x1) /* tx empty */ + return 1; + if (t1inp(base, T1_FASTLINK + T1_INSTAT) != 0x0) /* rx empty */ + return 2; + if (t1inp(base, T1_FASTLINK + T1_IRQENABLE) != 0x0) + return 3; + if ((t1inp(base, T1_FASTLINK + T1_FIFOSTAT) & 0xf0) != 0x70) + return 4; + if ((t1inp(base, T1_FASTLINK + T1_IRQMASTER) & 0x0e) != 0) + return 5; + if ((t1inp(base, T1_FASTLINK + T1_IDENT) & 0x7d) != 1) + return 6; + if (t1inp(base, T1_SLOWLINK + T1_OUTSTAT) != 0x1) /* tx empty */ + return 7; + if ((t1inp(base, T1_SLOWLINK + T1_IRQMASTER) & 0x0e) != 0) + return 8; + if ((t1inp(base, T1_SLOWLINK + T1_IDENT) & 0x7d) != 0) + return 9; + return 0; +} + +static irqreturn_t t1isa_interrupt(int interrupt, void *devptr) +{ + avmcard *card = devptr; + avmctrl_info *cinfo = &card->ctrlinfo[0]; + struct capi_ctr *ctrl = &cinfo->capi_ctrl; + unsigned char b1cmd; + struct sk_buff *skb; + + unsigned ApplId; + unsigned MsgLen; + unsigned DataB3Len; + unsigned NCCI; + unsigned WindowSize; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + while (b1_rx_full(card->port)) { + + b1cmd = b1_get_byte(card->port); + + switch (b1cmd) { + + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + DataB3Len = t1_get_slice(card->port, card->databuf); + spin_unlock_irqrestore(&card->lock, flags); + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf + MsgLen, 0, 30 - MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + skb_put_data(skb, card->msgbuf, MsgLen); + skb_put_data(skb, card->databuf, DataB3Len); + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + skb_put_data(skb, card->msgbuf, MsgLen); + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3) + capilib_data_b3_conf(&cinfo->ncci_head, ApplId, + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + WindowSize = b1_get_word(card->port); + capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize); + spin_unlock_irqrestore(&card->lock, flags); + break; + + case RECEIVE_FREE_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + if (NCCI != 0xffffffff) + capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI); + spin_unlock_irqrestore(&card->lock, flags); + break; + + case RECEIVE_START: + b1_put_byte(card->port, SEND_POLLACK); + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_resume_output(ctrl); + break; + + case RECEIVE_STOP: + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf); + spin_unlock_irqrestore(&card->lock, flags); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + capi_ctr_ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + spin_unlock_irqrestore(&card->lock, flags); + card->msgbuf[MsgLen] = 0; + while (MsgLen > 0 + && (card->msgbuf[MsgLen - 1] == '\n' + || card->msgbuf[MsgLen - 1] == '\r')) { + card->msgbuf[MsgLen - 1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = t1_get_slice(card->port, card->msgbuf); + spin_unlock_irqrestore(&card->lock, flags); + card->msgbuf[MsgLen] = 0; + while (MsgLen > 0 + && (card->msgbuf[MsgLen - 1] == '\n' + || card->msgbuf[MsgLen - 1] == '\r')) { + card->msgbuf[MsgLen - 1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + + case 0xff: + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "%s: card reseted ?\n", card->name); + return IRQ_HANDLED; + default: + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", + card->name, b1cmd); + return IRQ_NONE; + } + } + return IRQ_HANDLED; +} + +/* ------------------------------------------------------------- */ + +static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + int retval; + + t1_disable_irq(port); + b1_reset(port); + + if ((retval = b1_load_t4file(card, &data->firmware))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(card, &data->configuration))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1_loaded(card)) { + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + spin_lock_irqsave(&card->lock, flags); + b1_setinterrupt(port, card->irq, card->cardtype); + b1_put_byte(port, SEND_INIT); + b1_put_word(port, CAPI_MAXAPPL); + b1_put_word(port, AVM_NCCI_PER_CHANNEL * 30); + b1_put_word(port, ctrl->cnr - 1); + spin_unlock_irqrestore(&card->lock, flags); + + return 0; +} + +static void t1isa_reset_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + + t1_disable_irq(port); + b1_reset(port); + b1_reset(port); + + memset(cinfo->version, 0, sizeof(cinfo->version)); + spin_lock_irqsave(&card->lock, flags); + capilib_release(&cinfo->ncci_head); + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_down(ctrl); +} + +static void t1isa_remove(struct pci_dev *pdev) +{ + avmctrl_info *cinfo = pci_get_drvdata(pdev); + avmcard *card; + + if (!cinfo) + return; + + card = cinfo->card; + + t1_disable_irq(card->port); + b1_reset(card->port); + b1_reset(card->port); + t1_reset(card->port); + + detach_capi_ctr(&cinfo->capi_ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + b1_free_card(card); +} + +/* ------------------------------------------------------------- */ + +static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); +static char *t1isa_procinfo(struct capi_ctr *ctrl); + +static int t1isa_probe(struct pci_dev *pdev, int cardnr) +{ + avmctrl_info *cinfo; + avmcard *card; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "t1isa: no memory.\n"); + retval = -ENOMEM; + goto err; + } + + cinfo = card->ctrlinfo; + card->port = pci_resource_start(pdev, 0); + card->irq = pdev->irq; + card->cardtype = avm_t1isa; + card->cardnr = cardnr; + sprintf(card->name, "t1isa-%x", card->port); + + if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) { + printk(KERN_WARNING "t1isa: invalid port 0x%x.\n", card->port); + retval = -EINVAL; + goto err_free; + } + if (hema_irq_table[card->irq & 0xf] == 0) { + printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq); + retval = -EINVAL; + goto err_free; + } + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_INFO "t1isa: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free; + } + retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_INFO "t1isa: unable to get IRQ %d.\n", card->irq); + retval = -EBUSY; + goto err_release_region; + } + + if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) { + printk(KERN_INFO "t1isa: NO card at 0x%x (%d)\n", + card->port, retval); + retval = -ENODEV; + goto err_free_irq; + } + t1_disable_irq(card->port); + b1_reset(card->port); + + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "t1isa"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1_register_appl; + cinfo->capi_ctrl.release_appl = b1_release_appl; + cinfo->capi_ctrl.send_message = t1isa_send_message; + cinfo->capi_ctrl.load_firmware = t1isa_load_firmware; + cinfo->capi_ctrl.reset_ctr = t1isa_reset_ctr; + cinfo->capi_ctrl.procinfo = t1isa_procinfo; + cinfo->capi_ctrl.proc_show = b1_proc_show; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_INFO "t1isa: attach controller failed.\n"); + goto err_free_irq; + } + + printk(KERN_INFO "t1isa: AVM T1 ISA at i/o %#x, irq %d, card %d\n", + card->port, card->irq, card->cardnr); + + pci_set_drvdata(pdev, cinfo); + return 0; + +err_free_irq: + free_irq(card->irq, card); +err_release_region: + release_region(card->port, AVMB1_PORTLEN); +err_free: + b1_free_card(card); +err: + return retval; +} + +static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + u16 len = CAPIMSG_LEN(skb->data); + u8 cmd = CAPIMSG_COMMAND(skb->data); + u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); + u16 dlen, retval; + + spin_lock_irqsave(&card->lock, flags); + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + retval = capilib_data_b3_req(&cinfo->ncci_head, + CAPIMSG_APPID(skb->data), + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + if (retval != CAPI_NOERROR) { + spin_unlock_irqrestore(&card->lock, flags); + return retval; + } + dlen = CAPIMSG_DATALEN(skb->data); + + b1_put_byte(port, SEND_DATA_B3_REQ); + t1_put_slice(port, skb->data, len); + t1_put_slice(port, skb->data + len, dlen); + } else { + b1_put_byte(port, SEND_MESSAGE); + t1_put_slice(port, skb->data, len); + } + spin_unlock_irqrestore(&card->lock, flags); + dev_kfree_skb_any(skb); + return CAPI_NOERROR; +} +/* ------------------------------------------------------------- */ + +static char *t1isa_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d %d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->cardnr : 0 + ); + return cinfo->infobuf; +} + + +/* ------------------------------------------------------------- */ + +#define MAX_CARDS 4 +static struct pci_dev isa_dev[MAX_CARDS]; +static int io[MAX_CARDS]; +static int irq[MAX_CARDS]; +static int cardnr[MAX_CARDS]; + +module_param_hw_array(io, int, ioport, NULL, 0); +module_param_hw_array(irq, int, irq, NULL, 0); +module_param_array(cardnr, int, NULL, 0); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_PARM_DESC(cardnr, "Card number(s) (as jumpered)"); + +static int t1isa_add_card(struct capi_driver *driver, capicardparams *data) +{ + int i; + + for (i = 0; i < MAX_CARDS; i++) { + if (isa_dev[i].resource[0].start) + continue; + + isa_dev[i].resource[0].start = data->port; + isa_dev[i].irq = data->irq; + + if (t1isa_probe(&isa_dev[i], data->cardnr) == 0) + return 0; + } + return -ENODEV; +} + +static struct capi_driver capi_driver_t1isa = { + .name = "t1isa", + .revision = "1.0", + .add_card = t1isa_add_card, +}; + +static int __init t1isa_init(void) +{ + char rev[32]; + char *p; + int i; + + if ((p = strchr(revision, ':')) != NULL && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != NULL && p > rev) + *(p - 1) = 0; + } else + strcpy(rev, "1.0"); + + for (i = 0; i < MAX_CARDS; i++) { + if (!io[i]) + break; + + isa_dev[i].resource[0].start = io[i]; + isa_dev[i].irq = irq[i]; + + if (t1isa_probe(&isa_dev[i], cardnr[i]) != 0) + return -ENODEV; + } + + strlcpy(capi_driver_t1isa.revision, rev, 32); + register_capi_driver(&capi_driver_t1isa); + printk(KERN_INFO "t1isa: revision %s\n", rev); + + return 0; +} + +static void __exit t1isa_exit(void) +{ + int i; + + unregister_capi_driver(&capi_driver_t1isa); + for (i = 0; i < MAX_CARDS; i++) { + if (!io[i]) + break; + + t1isa_remove(&isa_dev[i]); + } +} + +module_init(t1isa_init); +module_exit(t1isa_exit); diff --git a/drivers/isdn/hardware/avm/t1pci.c b/drivers/isdn/hardware/avm/t1pci.c new file mode 100644 index 000000000..f5ed1d500 --- /dev/null +++ b/drivers/isdn/hardware/avm/t1pci.c @@ -0,0 +1,259 @@ +/* $Id: t1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ + * + * Module for AVM T1 PCI-card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/capi.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +#undef CONFIG_T1PCI_DEBUG +#undef CONFIG_T1PCI_POLLDEBUG + +/* ------------------------------------------------------------- */ +static char *revision = "$Revision: 1.1.2.2 $"; +/* ------------------------------------------------------------- */ + +static struct pci_device_id t1pci_pci_tbl[] = { + { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_T1, PCI_ANY_ID, PCI_ANY_ID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(pci, t1pci_pci_tbl); +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 PCI card"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +static char *t1pci_procinfo(struct capi_ctr *ctrl); + +static int t1pci_add_card(struct capicardparams *p, struct pci_dev *pdev) +{ + avmcard *card; + avmctrl_info *cinfo; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "t1pci: no memory.\n"); + retval = -ENOMEM; + goto err; + } + + card->dma = avmcard_dma_alloc("t1pci", pdev, 2048 + 128, 2048 + 128); + if (!card->dma) { + printk(KERN_WARNING "t1pci: no memory.\n"); + retval = -ENOMEM; + goto err_free; + } + + cinfo = card->ctrlinfo; + sprintf(card->name, "t1pci-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->membase = p->membase; + card->cardtype = avm_t1pci; + + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_WARNING "t1pci: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free_dma; + } + + card->mbase = ioremap(card->membase, 64); + if (!card->mbase) { + printk(KERN_NOTICE "t1pci: can't remap memory at 0x%lx\n", + card->membase); + retval = -EIO; + goto err_release_region; + } + + b1dma_reset(card); + + retval = t1pci_detect(card); + if (retval != 0) { + if (retval < 6) + printk(KERN_NOTICE "t1pci: NO card at 0x%x (%d)\n", + card->port, retval); + else + printk(KERN_NOTICE "t1pci: card at 0x%x, but cable not connected or T1 has no power (%d)\n", + card->port, retval); + retval = -EIO; + goto err_unmap; + } + b1dma_reset(card); + + retval = request_irq(card->irq, b1dma_interrupt, IRQF_SHARED, card->name, card); + if (retval) { + printk(KERN_ERR "t1pci: unable to get IRQ %d.\n", card->irq); + retval = -EBUSY; + goto err_unmap; + } + + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "t1pci"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1dma_register_appl; + cinfo->capi_ctrl.release_appl = b1dma_release_appl; + cinfo->capi_ctrl.send_message = b1dma_send_message; + cinfo->capi_ctrl.load_firmware = b1dma_load_firmware; + cinfo->capi_ctrl.reset_ctr = b1dma_reset_ctr; + cinfo->capi_ctrl.procinfo = t1pci_procinfo; + cinfo->capi_ctrl.proc_show = b1dma_proc_show; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "t1pci: attach controller failed.\n"); + retval = -EBUSY; + goto err_free_irq; + } + card->cardnr = cinfo->capi_ctrl.cnr; + + printk(KERN_INFO "t1pci: AVM T1 PCI at i/o %#x, irq %d, mem %#lx\n", + card->port, card->irq, card->membase); + + pci_set_drvdata(pdev, card); + return 0; + +err_free_irq: + free_irq(card->irq, card); +err_unmap: + iounmap(card->mbase); +err_release_region: + release_region(card->port, AVMB1_PORTLEN); +err_free_dma: + avmcard_dma_free(card->dma); +err_free: + b1_free_card(card); +err: + return retval; +} + +/* ------------------------------------------------------------- */ + +static void t1pci_remove(struct pci_dev *pdev) +{ + avmcard *card = pci_get_drvdata(pdev); + avmctrl_info *cinfo = card->ctrlinfo; + + b1dma_reset(card); + + detach_capi_ctr(&cinfo->capi_ctrl); + free_irq(card->irq, card); + iounmap(card->mbase); + release_region(card->port, AVMB1_PORTLEN); + avmcard_dma_free(card->dma); + b1_free_card(card); +} + +/* ------------------------------------------------------------- */ + +static char *t1pci_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->membase : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +static int t1pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct capicardparams param; + int retval; + + if (pci_enable_device(dev) < 0) { + printk(KERN_ERR "t1pci: failed to enable AVM-T1-PCI\n"); + return -ENODEV; + } + pci_set_master(dev); + + param.port = pci_resource_start(dev, 1); + param.irq = dev->irq; + param.membase = pci_resource_start(dev, 0); + + printk(KERN_INFO "t1pci: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n", + param.port, param.irq, param.membase); + + retval = t1pci_add_card(¶m, dev); + if (retval != 0) { + printk(KERN_ERR "t1pci: no AVM-T1-PCI at i/o %#x, irq %d detected, mem %#x\n", + param.port, param.irq, param.membase); + pci_disable_device(dev); + return -ENODEV; + } + return 0; +} + +static struct pci_driver t1pci_pci_driver = { + .name = "t1pci", + .id_table = t1pci_pci_tbl, + .probe = t1pci_probe, + .remove = t1pci_remove, +}; + +static struct capi_driver capi_driver_t1pci = { + .name = "t1pci", + .revision = "1.0", +}; + +static int __init t1pci_init(void) +{ + char *p; + char rev[32]; + int err; + + if ((p = strchr(revision, ':')) != NULL && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != NULL && p > rev) + *(p - 1) = 0; + } else + strcpy(rev, "1.0"); + + err = pci_register_driver(&t1pci_pci_driver); + if (!err) { + strlcpy(capi_driver_t1pci.revision, rev, 32); + register_capi_driver(&capi_driver_t1pci); + printk(KERN_INFO "t1pci: revision %s\n", rev); + } + return err; +} + +static void __exit t1pci_exit(void) +{ + unregister_capi_driver(&capi_driver_t1pci); + pci_unregister_driver(&t1pci_pci_driver); +} + +module_init(t1pci_init); +module_exit(t1pci_exit); diff --git a/drivers/isdn/hardware/eicon/Kconfig b/drivers/isdn/hardware/eicon/Kconfig new file mode 100644 index 000000000..6082b6a5c --- /dev/null +++ b/drivers/isdn/hardware/eicon/Kconfig @@ -0,0 +1,51 @@ +# +# ISDN DIVAS Eicon driver +# + +menuconfig CAPI_EICON + bool "Active Eicon DIVA Server cards" + help + Enable support for Eicon Networks active ISDN cards. + +if CAPI_EICON + +config ISDN_DIVAS + tristate "Support Eicon DIVA Server cards" + depends on PROC_FS && PCI + help + Say Y here if you have an Eicon Networks DIVA Server PCI ISDN card. + In order to use this card, additional firmware is necessary, which + has to be downloaded into the card using the divactrl utility. + +if ISDN_DIVAS + +config ISDN_DIVAS_BRIPCI + bool "DIVA Server BRI/PCI support" + help + Enable support for DIVA Server BRI-PCI. + +config ISDN_DIVAS_PRIPCI + bool "DIVA Server PRI/PCI support" + help + Enable support for DIVA Server PRI-PCI. + +config ISDN_DIVAS_DIVACAPI + tristate "DIVA CAPI2.0 interface support" + help + You need this to provide the CAPI interface + for DIVA Server cards. + +config ISDN_DIVAS_USERIDI + tristate "DIVA User-IDI interface support" + help + Enable support for user-mode IDI interface. + +config ISDN_DIVAS_MAINT + tristate "DIVA Maint driver support" + depends on m + help + Enable Divas Maintenance driver. + +endif # ISDN_DIVAS + +endif # CAPI_EICON diff --git a/drivers/isdn/hardware/eicon/Makefile b/drivers/isdn/hardware/eicon/Makefile new file mode 100644 index 000000000..a0ab2e2d7 --- /dev/null +++ b/drivers/isdn/hardware/eicon/Makefile @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for the Eicon DIVA ISDN drivers. + +# Each configuration option enables a list of files. + +obj-$(CONFIG_ISDN_DIVAS) += divadidd.o divas.o +obj-$(CONFIG_ISDN_DIVAS_MAINT) += diva_mnt.o +obj-$(CONFIG_ISDN_DIVAS_USERIDI) += diva_idi.o +obj-$(CONFIG_ISDN_DIVAS_DIVACAPI) += divacapi.o + +# Multipart objects. + +divas-y := divasmain.o divasfunc.o di.o io.o istream.o \ + diva.o divasproc.o diva_dma.o +divas-$(CONFIG_ISDN_DIVAS_BRIPCI) += os_bri.o s_bri.o os_4bri.o s_4bri.o +divas-$(CONFIG_ISDN_DIVAS_PRIPCI) += os_pri.o s_pri.o + +divacapi-y := capimain.o capifunc.o message.o capidtmf.o + +divadidd-y := diva_didd.o diddfunc.o dadapter.o + +diva_mnt-y := divamnt.o mntfunc.o debug.o maintidi.o + +diva_idi-y := divasi.o idifunc.o um_idi.o dqueue.o diff --git a/drivers/isdn/hardware/eicon/adapter.h b/drivers/isdn/hardware/eicon/adapter.h new file mode 100644 index 000000000..f9b24eb87 --- /dev/null +++ b/drivers/isdn/hardware/eicon/adapter.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: adapter.h,v 1.4 2004/03/21 17:26:01 armin Exp $ */ + +#ifndef __DIVA_USER_MODE_IDI_ADAPTER_H__ +#define __DIVA_USER_MODE_IDI_ADAPTER_H__ + +#define DIVA_UM_IDI_ADAPTER_REMOVED 0x00000001 + +typedef struct _diva_um_idi_adapter { + struct list_head link; + DESCRIPTOR d; + int adapter_nr; + struct list_head entity_q; /* entities linked to this adapter */ + dword status; +} diva_um_idi_adapter_t; + + +#endif diff --git a/drivers/isdn/hardware/eicon/capi20.h b/drivers/isdn/hardware/eicon/capi20.h new file mode 100644 index 000000000..391e4175b --- /dev/null +++ b/drivers/isdn/hardware/eicon/capi20.h @@ -0,0 +1,699 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef _INC_CAPI20 +#define _INC_CAPI20 +/* operations on message queues */ +/* the common device type for CAPI20 drivers */ +#define FILE_DEVICE_CAPI20 0x8001 +/* DEVICE_CONTROL codes for user and kernel mode applications */ +#define CAPI20_CTL_REGISTER 0x0801 +#define CAPI20_CTL_RELEASE 0x0802 +#define CAPI20_CTL_GET_MANUFACTURER 0x0805 +#define CAPI20_CTL_GET_VERSION 0x0806 +#define CAPI20_CTL_GET_SERIAL 0x0807 +#define CAPI20_CTL_GET_PROFILE 0x0808 +/* INTERNAL_DEVICE_CONTROL codes for kernel mode applicatios only */ +#define CAPI20_CTL_PUT_MESSAGE 0x0803 +#define CAPI20_CTL_GET_MESSAGE 0x0804 +/* the wrapped codes as required by the system */ +#define CAPI_CTL_CODE(f, m) CTL_CODE(FILE_DEVICE_CAPI20, f, m, FILE_ANY_ACCESS) +#define IOCTL_CAPI_REGISTER CAPI_CTL_CODE(CAPI20_CTL_REGISTER, METHOD_BUFFERED) +#define IOCTL_CAPI_RELEASE CAPI_CTL_CODE(CAPI20_CTL_RELEASE, METHOD_BUFFERED) +#define IOCTL_CAPI_GET_MANUFACTURER CAPI_CTL_CODE(CAPI20_CTL_GET_MANUFACTURER, METHOD_BUFFERED) +#define IOCTL_CAPI_GET_VERSION CAPI_CTL_CODE(CAPI20_CTL_GET_VERSION, METHOD_BUFFERED) +#define IOCTL_CAPI_GET_SERIAL CAPI_CTL_CODE(CAPI20_CTL_GET_SERIAL, METHOD_BUFFERED) +#define IOCTL_CAPI_GET_PROFILE CAPI_CTL_CODE(CAPI20_CTL_GET_PROFILE, METHOD_BUFFERED) +#define IOCTL_CAPI_PUT_MESSAGE CAPI_CTL_CODE(CAPI20_CTL_PUT_MESSAGE, METHOD_BUFFERED) +#define IOCTL_CAPI_GET_MESSAGE CAPI_CTL_CODE(CAPI20_CTL_GET_MESSAGE, METHOD_BUFFERED) +struct divas_capi_register_params { + word MessageBufferSize; + word maxLogicalConnection; + word maxBDataBlocks; + word maxBDataLen; +}; +struct divas_capi_version { + word CapiMajor; + word CapiMinor; + word ManuMajor; + word ManuMinor; +}; +typedef struct api_profile_s { + word Number; + word Channels; + dword Global_Options; + dword B1_Protocols; + dword B2_Protocols; + dword B3_Protocols; +} API_PROFILE; +/* ISDN Common API message types */ +#define _ALERT_R 0x8001 +#define _CONNECT_R 0x8002 +#define _CONNECT_I 0x8202 +#define _CONNECT_ACTIVE_I 0x8203 +#define _DISCONNECT_R 0x8004 +#define _DISCONNECT_I 0x8204 +#define _LISTEN_R 0x8005 +#define _INFO_R 0x8008 +#define _INFO_I 0x8208 +#define _SELECT_B_REQ 0x8041 +#define _FACILITY_R 0x8080 +#define _FACILITY_I 0x8280 +#define _CONNECT_B3_R 0x8082 +#define _CONNECT_B3_I 0x8282 +#define _CONNECT_B3_ACTIVE_I 0x8283 +#define _DISCONNECT_B3_R 0x8084 +#define _DISCONNECT_B3_I 0x8284 +#define _DATA_B3_R 0x8086 +#define _DATA_B3_I 0x8286 +#define _RESET_B3_R 0x8087 +#define _RESET_B3_I 0x8287 +#define _CONNECT_B3_T90_ACTIVE_I 0x8288 +#define _MANUFACTURER_R 0x80ff +#define _MANUFACTURER_I 0x82ff +/* OR this to convert a REQUEST to a CONFIRM */ +#define CONFIRM 0x0100 +/* OR this to convert a INDICATION to a RESPONSE */ +#define RESPONSE 0x0100 +/*------------------------------------------------------------------*/ +/* diehl isdn private MANUFACTURER codes */ +/*------------------------------------------------------------------*/ +#define _DI_MANU_ID 0x44444944 +#define _DI_ASSIGN_PLCI 0x0001 +#define _DI_ADV_CODEC 0x0002 +#define _DI_DSP_CTRL 0x0003 +#define _DI_SIG_CTRL 0x0004 +#define _DI_RXT_CTRL 0x0005 +#define _DI_IDI_CTRL 0x0006 +#define _DI_CFG_CTRL 0x0007 +#define _DI_REMOVE_CODEC 0x0008 +#define _DI_OPTIONS_REQUEST 0x0009 +#define _DI_SSEXT_CTRL 0x000a +#define _DI_NEGOTIATE_B3 0x000b +/*------------------------------------------------------------------*/ +/* parameter structures */ +/*------------------------------------------------------------------*/ +/* ALERT-REQUEST */ +typedef struct { + byte structs[0]; /* Additional Info */ +} _ALT_REQP; +/* ALERT-CONFIRM */ +typedef struct { + word Info; +} _ALT_CONP; +/* CONNECT-REQUEST */ +typedef struct { + word CIP_Value; + byte structs[0]; /* Called party number, + Called party subaddress, + Calling party number, + Calling party subaddress, + B_protocol, + BC, + LLC, + HLC, + Additional Info */ +} _CON_REQP; +/* CONNECT-CONFIRM */ +typedef struct { + word Info; +} _CON_CONP; +/* CONNECT-INDICATION */ +typedef struct { + word CIP_Value; + byte structs[0]; /* Called party number, + Called party subaddress, + Calling party number, + Calling party subaddress, + BC, + LLC, + HLC, + Additional Info */ +} _CON_INDP; +/* CONNECT-RESPONSE */ +typedef struct { + word Accept; + byte structs[0]; /* B_protocol, + Connected party number, + Connected party subaddress, + LLC */ +} _CON_RESP; +/* CONNECT-ACTIVE-INDICATION */ +typedef struct { + byte structs[0]; /* Connected party number, + Connected party subaddress, + LLC */ +} _CON_A_INDP; +/* CONNECT-ACTIVE-RESPONSE */ +typedef struct { + byte structs[0]; /* empty */ +} _CON_A_RESP; +/* DISCONNECT-REQUEST */ +typedef struct { + byte structs[0]; /* Additional Info */ +} _DIS_REQP; +/* DISCONNECT-CONFIRM */ +typedef struct { + word Info; +} _DIS_CONP; +/* DISCONNECT-INDICATION */ +typedef struct { + word Info; +} _DIS_INDP; +/* DISCONNECT-RESPONSE */ +typedef struct { + byte structs[0]; /* empty */ +} _DIS_RESP; +/* LISTEN-REQUEST */ +typedef struct { + dword Info_Mask; + dword CIP_Mask; + byte structs[0]; /* Calling party number, + Calling party subaddress */ +} _LIS_REQP; +/* LISTEN-CONFIRM */ +typedef struct { + word Info; +} _LIS_CONP; +/* INFO-REQUEST */ +typedef struct { + byte structs[0]; /* Called party number, + Additional Info */ +} _INF_REQP; +/* INFO-CONFIRM */ +typedef struct { + word Info; +} _INF_CONP; +/* INFO-INDICATION */ +typedef struct { + word Number; + byte structs[0]; /* Info element */ +} _INF_INDP; +/* INFO-RESPONSE */ +typedef struct { + byte structs[0]; /* empty */ +} _INF_RESP; +/* SELECT-B-REQUEST */ +typedef struct { + byte structs[0]; /* B-protocol */ +} _SEL_B_REQP; +/* SELECT-B-CONFIRM */ +typedef struct { + word Info; +} _SEL_B_CONP; +/* FACILITY-REQUEST */ +typedef struct { + word Selector; + byte structs[0]; /* Facility parameters */ +} _FAC_REQP; +/* FACILITY-CONFIRM STRUCT FOR SUPPLEMENT. SERVICES */ +typedef struct { + byte struct_length; + word function; + byte length; + word SupplementaryServiceInfo; + dword SupportedServices; +} _FAC_CON_STRUCTS; +/* FACILITY-CONFIRM */ +typedef struct { + word Info; + word Selector; + byte structs[0]; /* Facility parameters */ +} _FAC_CONP; +/* FACILITY-INDICATION */ +typedef struct { + word Selector; + byte structs[0]; /* Facility parameters */ +} _FAC_INDP; +/* FACILITY-RESPONSE */ +typedef struct { + word Selector; + byte structs[0]; /* Facility parameters */ +} _FAC_RESP; +/* CONNECT-B3-REQUEST */ +typedef struct { + byte structs[0]; /* NCPI */ +} _CON_B3_REQP; +/* CONNECT-B3-CONFIRM */ +typedef struct { + word Info; +} _CON_B3_CONP; +/* CONNECT-B3-INDICATION */ +typedef struct { + byte structs[0]; /* NCPI */ +} _CON_B3_INDP; +/* CONNECT-B3-RESPONSE */ +typedef struct { + word Accept; + byte structs[0]; /* NCPI */ +} _CON_B3_RESP; +/* CONNECT-B3-ACTIVE-INDICATION */ +typedef struct { + byte structs[0]; /* NCPI */ +} _CON_B3_A_INDP; +/* CONNECT-B3-ACTIVE-RESPONSE */ +typedef struct { + byte structs[0]; /* empty */ +} _CON_B3_A_RESP; +/* DISCONNECT-B3-REQUEST */ +typedef struct { + byte structs[0]; /* NCPI */ +} _DIS_B3_REQP; +/* DISCONNECT-B3-CONFIRM */ +typedef struct { + word Info; +} _DIS_B3_CONP; +/* DISCONNECT-B3-INDICATION */ +typedef struct { + word Info; + byte structs[0]; /* NCPI */ +} _DIS_B3_INDP; +/* DISCONNECT-B3-RESPONSE */ +typedef struct { + byte structs[0]; /* empty */ +} _DIS_B3_RESP; +/* DATA-B3-REQUEST */ +typedef struct { + dword Data; + word Data_Length; + word Number; + word Flags; +} _DAT_B3_REQP; +/* DATA-B3-REQUEST 64 BIT Systems */ +typedef struct { + dword Data; + word Data_Length; + word Number; + word Flags; + void *pData; +} _DAT_B3_REQ64P; +/* DATA-B3-CONFIRM */ +typedef struct { + word Number; + word Info; +} _DAT_B3_CONP; +/* DATA-B3-INDICATION */ +typedef struct { + dword Data; + word Data_Length; + word Number; + word Flags; +} _DAT_B3_INDP; +/* DATA-B3-INDICATION 64 BIT Systems */ +typedef struct { + dword Data; + word Data_Length; + word Number; + word Flags; + void *pData; +} _DAT_B3_IND64P; +/* DATA-B3-RESPONSE */ +typedef struct { + word Number; +} _DAT_B3_RESP; +/* RESET-B3-REQUEST */ +typedef struct { + byte structs[0]; /* NCPI */ +} _RES_B3_REQP; +/* RESET-B3-CONFIRM */ +typedef struct { + word Info; +} _RES_B3_CONP; +/* RESET-B3-INDICATION */ +typedef struct { + byte structs[0]; /* NCPI */ +} _RES_B3_INDP; +/* RESET-B3-RESPONSE */ +typedef struct { + byte structs[0]; /* empty */ +} _RES_B3_RESP; +/* CONNECT-B3-T90-ACTIVE-INDICATION */ +typedef struct { + byte structs[0]; /* NCPI */ +} _CON_B3_T90_A_INDP; +/* CONNECT-B3-T90-ACTIVE-RESPONSE */ +typedef struct { + word Reject; + byte structs[0]; /* NCPI */ +} _CON_B3_T90_A_RESP; +/*------------------------------------------------------------------*/ +/* message structure */ +/*------------------------------------------------------------------*/ +typedef struct _API_MSG CAPI_MSG; +typedef struct _MSG_HEADER CAPI_MSG_HEADER; +struct _API_MSG { + struct _MSG_HEADER { + word length; + word appl_id; + word command; + word number; + byte controller; + byte plci; + word ncci; + } header; + union { + _ALT_REQP alert_req; + _ALT_CONP alert_con; + _CON_REQP connect_req; + _CON_CONP connect_con; + _CON_INDP connect_ind; + _CON_RESP connect_res; + _CON_A_INDP connect_a_ind; + _CON_A_RESP connect_a_res; + _DIS_REQP disconnect_req; + _DIS_CONP disconnect_con; + _DIS_INDP disconnect_ind; + _DIS_RESP disconnect_res; + _LIS_REQP listen_req; + _LIS_CONP listen_con; + _INF_REQP info_req; + _INF_CONP info_con; + _INF_INDP info_ind; + _INF_RESP info_res; + _SEL_B_REQP select_b_req; + _SEL_B_CONP select_b_con; + _FAC_REQP facility_req; + _FAC_CONP facility_con; + _FAC_INDP facility_ind; + _FAC_RESP facility_res; + _CON_B3_REQP connect_b3_req; + _CON_B3_CONP connect_b3_con; + _CON_B3_INDP connect_b3_ind; + _CON_B3_RESP connect_b3_res; + _CON_B3_A_INDP connect_b3_a_ind; + _CON_B3_A_RESP connect_b3_a_res; + _DIS_B3_REQP disconnect_b3_req; + _DIS_B3_CONP disconnect_b3_con; + _DIS_B3_INDP disconnect_b3_ind; + _DIS_B3_RESP disconnect_b3_res; + _DAT_B3_REQP data_b3_req; + _DAT_B3_REQ64P data_b3_req64; + _DAT_B3_CONP data_b3_con; + _DAT_B3_INDP data_b3_ind; + _DAT_B3_IND64P data_b3_ind64; + _DAT_B3_RESP data_b3_res; + _RES_B3_REQP reset_b3_req; + _RES_B3_CONP reset_b3_con; + _RES_B3_INDP reset_b3_ind; + _RES_B3_RESP reset_b3_res; + _CON_B3_T90_A_INDP connect_b3_t90_a_ind; + _CON_B3_T90_A_RESP connect_b3_t90_a_res; + byte b[200]; + } info; +}; +/*------------------------------------------------------------------*/ +/* non-fatal errors */ +/*------------------------------------------------------------------*/ +#define _NCPI_IGNORED 0x0001 +#define _FLAGS_IGNORED 0x0002 +#define _ALERT_IGNORED 0x0003 +/*------------------------------------------------------------------*/ +/* API function error codes */ +/*------------------------------------------------------------------*/ +#define GOOD 0x0000 +#define _TOO_MANY_APPLICATIONS 0x1001 +#define _BLOCK_TOO_SMALL 0x1002 +#define _BUFFER_TOO_BIG 0x1003 +#define _MSG_BUFFER_TOO_SMALL 0x1004 +#define _TOO_MANY_CONNECTIONS 0x1005 +#define _REG_CAPI_BUSY 0x1007 +#define _REG_RESOURCE_ERROR 0x1008 +#define _REG_CAPI_NOT_INSTALLED 0x1009 +#define _WRONG_APPL_ID 0x1101 +#define _BAD_MSG 0x1102 +#define _QUEUE_FULL 0x1103 +#define _GET_NO_MSG 0x1104 +#define _MSG_LOST 0x1105 +#define _WRONG_NOTIFY 0x1106 +#define _CAPI_BUSY 0x1107 +#define _RESOURCE_ERROR 0x1108 +#define _CAPI_NOT_INSTALLED 0x1109 +#define _NO_EXTERNAL_EQUIPMENT 0x110a +#define _ONLY_EXTERNAL_EQUIPMENT 0x110b +/*------------------------------------------------------------------*/ +/* addressing/coding error codes */ +/*------------------------------------------------------------------*/ +#define _WRONG_STATE 0x2001 +#define _WRONG_IDENTIFIER 0x2002 +#define _OUT_OF_PLCI 0x2003 +#define _OUT_OF_NCCI 0x2004 +#define _OUT_OF_LISTEN 0x2005 +#define _OUT_OF_FAX 0x2006 +#define _WRONG_MESSAGE_FORMAT 0x2007 +#define _OUT_OF_INTERCONNECT_RESOURCES 0x2008 +/*------------------------------------------------------------------*/ +/* configuration error codes */ +/*------------------------------------------------------------------*/ +#define _B1_NOT_SUPPORTED 0x3001 +#define _B2_NOT_SUPPORTED 0x3002 +#define _B3_NOT_SUPPORTED 0x3003 +#define _B1_PARM_NOT_SUPPORTED 0x3004 +#define _B2_PARM_NOT_SUPPORTED 0x3005 +#define _B3_PARM_NOT_SUPPORTED 0x3006 +#define _B_STACK_NOT_SUPPORTED 0x3007 +#define _NCPI_NOT_SUPPORTED 0x3008 +#define _CIP_NOT_SUPPORTED 0x3009 +#define _FLAGS_NOT_SUPPORTED 0x300a +#define _FACILITY_NOT_SUPPORTED 0x300b +#define _DATA_LEN_NOT_SUPPORTED 0x300c +#define _RESET_NOT_SUPPORTED 0x300d +#define _SUPPLEMENTARY_SERVICE_NOT_SUPPORTED 0x300e +#define _REQUEST_NOT_ALLOWED_IN_THIS_STATE 0x3010 +#define _FACILITY_SPECIFIC_FUNCTION_NOT_SUPP 0x3011 +/*------------------------------------------------------------------*/ +/* reason codes */ +/*------------------------------------------------------------------*/ +#define _L1_ERROR 0x3301 +#define _L2_ERROR 0x3302 +#define _L3_ERROR 0x3303 +#define _OTHER_APPL_CONNECTED 0x3304 +#define _CAPI_GUARD_ERROR 0x3305 +#define _L3_CAUSE 0x3400 +/*------------------------------------------------------------------*/ +/* b3 reason codes */ +/*------------------------------------------------------------------*/ +#define _B_CHANNEL_LOST 0x3301 +#define _B2_ERROR 0x3302 +#define _B3_ERROR 0x3303 +/*------------------------------------------------------------------*/ +/* fax error codes */ +/*------------------------------------------------------------------*/ +#define _FAX_NO_CONNECTION 0x3311 +#define _FAX_TRAINING_ERROR 0x3312 +#define _FAX_REMOTE_REJECT 0x3313 +#define _FAX_REMOTE_ABORT 0x3314 +#define _FAX_PROTOCOL_ERROR 0x3315 +#define _FAX_TX_UNDERRUN 0x3316 +#define _FAX_RX_OVERFLOW 0x3317 +#define _FAX_LOCAL_ABORT 0x3318 +#define _FAX_PARAMETER_ERROR 0x3319 +/*------------------------------------------------------------------*/ +/* line interconnect error codes */ +/*------------------------------------------------------------------*/ +#define _LI_USER_INITIATED 0x0000 +#define _LI_LINE_NO_LONGER_AVAILABLE 0x3805 +#define _LI_INTERCONNECT_NOT_ESTABLISHED 0x3806 +#define _LI_LINES_NOT_COMPATIBLE 0x3807 +#define _LI2_USER_INITIATED 0x0000 +#define _LI2_PLCI_HAS_NO_BCHANNEL 0x3800 +#define _LI2_LINES_NOT_COMPATIBLE 0x3801 +#define _LI2_NOT_IN_SAME_INTERCONNECTION 0x3802 +/*------------------------------------------------------------------*/ +/* global options */ +/*------------------------------------------------------------------*/ +#define GL_INTERNAL_CONTROLLER_SUPPORTED 0x00000001L +#define GL_EXTERNAL_EQUIPMENT_SUPPORTED 0x00000002L +#define GL_HANDSET_SUPPORTED 0x00000004L +#define GL_DTMF_SUPPORTED 0x00000008L +#define GL_SUPPLEMENTARY_SERVICES_SUPPORTED 0x00000010L +#define GL_CHANNEL_ALLOCATION_SUPPORTED 0x00000020L +#define GL_BCHANNEL_OPERATION_SUPPORTED 0x00000040L +#define GL_LINE_INTERCONNECT_SUPPORTED 0x00000080L +#define GL_ECHO_CANCELLER_SUPPORTED 0x00000100L +/*------------------------------------------------------------------*/ +/* protocol selection */ +/*------------------------------------------------------------------*/ +#define B1_HDLC 0 +#define B1_TRANSPARENT 1 +#define B1_V110_ASYNC 2 +#define B1_V110_SYNC 3 +#define B1_T30 4 +#define B1_HDLC_INVERTED 5 +#define B1_TRANSPARENT_R 6 +#define B1_MODEM_ALL_NEGOTIATE 7 +#define B1_MODEM_ASYNC 8 +#define B1_MODEM_SYNC_HDLC 9 +#define B2_X75 0 +#define B2_TRANSPARENT 1 +#define B2_SDLC 2 +#define B2_LAPD 3 +#define B2_T30 4 +#define B2_PPP 5 +#define B2_TRANSPARENT_NO_CRC 6 +#define B2_MODEM_EC_COMPRESSION 7 +#define B2_X75_V42BIS 8 +#define B2_V120_ASYNC 9 +#define B2_V120_ASYNC_V42BIS 10 +#define B2_V120_BIT_TRANSPARENT 11 +#define B2_LAPD_FREE_SAPI_SEL 12 +#define B3_TRANSPARENT 0 +#define B3_T90NL 1 +#define B3_ISO8208 2 +#define B3_X25_DCE 3 +#define B3_T30 4 +#define B3_T30_WITH_EXTENSIONS 5 +#define B3_RESERVED 6 +#define B3_MODEM 7 +/*------------------------------------------------------------------*/ +/* facility definitions */ +/*------------------------------------------------------------------*/ +#define SELECTOR_HANDSET 0 +#define SELECTOR_DTMF 1 +#define SELECTOR_V42BIS 2 +#define SELECTOR_SU_SERV 3 +#define SELECTOR_POWER_MANAGEMENT 4 +#define SELECTOR_LINE_INTERCONNECT 5 +#define SELECTOR_ECHO_CANCELLER 6 +/*------------------------------------------------------------------*/ +/* supplementary services definitions */ +/*------------------------------------------------------------------*/ +#define S_GET_SUPPORTED_SERVICES 0x0000 +#define S_LISTEN 0x0001 +#define S_HOLD 0x0002 +#define S_RETRIEVE 0x0003 +#define S_SUSPEND 0x0004 +#define S_RESUME 0x0005 +#define S_ECT 0x0006 +#define S_3PTY_BEGIN 0x0007 +#define S_3PTY_END 0x0008 +#define S_CALL_DEFLECTION 0x000d +#define S_CALL_FORWARDING_START 0x0009 +#define S_CALL_FORWARDING_STOP 0x000a +#define S_INTERROGATE_DIVERSION 0x000b /* or interrogate parameters */ +#define S_INTERROGATE_NUMBERS 0x000c +#define S_CCBS_REQUEST 0x000f +#define S_CCBS_DEACTIVATE 0x0010 +#define S_CCBS_INTERROGATE 0x0011 +#define S_CCBS_CALL 0x0012 +#define S_MWI_ACTIVATE 0x0013 +#define S_MWI_DEACTIVATE 0x0014 +#define S_CONF_BEGIN 0x0017 +#define S_CONF_ADD 0x0018 +#define S_CONF_SPLIT 0x0019 +#define S_CONF_DROP 0x001a +#define S_CONF_ISOLATE 0x001b +#define S_CONF_REATTACH 0x001c +#define S_CCBS_ERASECALLLINKAGEID 0x800d +#define S_CCBS_STOP_ALERTING 0x8012 +#define S_CCBS_INFO_RETAIN 0x8013 +#define S_MWI_INDICATE 0x8014 +#define S_CONF_PARTYDISC 0x8016 +#define S_CONF_NOTIFICATION 0x8017 +/* Service Masks */ +#define MASK_HOLD_RETRIEVE 0x00000001 +#define MASK_TERMINAL_PORTABILITY 0x00000002 +#define MASK_ECT 0x00000004 +#define MASK_3PTY 0x00000008 +#define MASK_CALL_FORWARDING 0x00000010 +#define MASK_CALL_DEFLECTION 0x00000020 +#define MASK_MWI 0x00000100 +#define MASK_CCNR 0x00000200 +#define MASK_CONF 0x00000400 +/*------------------------------------------------------------------*/ +/* dtmf definitions */ +/*------------------------------------------------------------------*/ +#define DTMF_LISTEN_START 1 +#define DTMF_LISTEN_STOP 2 +#define DTMF_DIGITS_SEND 3 +#define DTMF_SUCCESS 0 +#define DTMF_INCORRECT_DIGIT 1 +#define DTMF_UNKNOWN_REQUEST 2 +/*------------------------------------------------------------------*/ +/* line interconnect definitions */ +/*------------------------------------------------------------------*/ +#define LI_GET_SUPPORTED_SERVICES 0 +#define LI_REQ_CONNECT 1 +#define LI_REQ_DISCONNECT 2 +#define LI_IND_CONNECT_ACTIVE 1 +#define LI_IND_DISCONNECT 2 +#define LI_FLAG_CONFERENCE_A_B ((dword) 0x00000001L) +#define LI_FLAG_CONFERENCE_B_A ((dword) 0x00000002L) +#define LI_FLAG_MONITOR_A ((dword) 0x00000004L) +#define LI_FLAG_MONITOR_B ((dword) 0x00000008L) +#define LI_FLAG_ANNOUNCEMENT_A ((dword) 0x00000010L) +#define LI_FLAG_ANNOUNCEMENT_B ((dword) 0x00000020L) +#define LI_FLAG_MIX_A ((dword) 0x00000040L) +#define LI_FLAG_MIX_B ((dword) 0x00000080L) +#define LI_CONFERENCING_SUPPORTED ((dword) 0x00000001L) +#define LI_MONITORING_SUPPORTED ((dword) 0x00000002L) +#define LI_ANNOUNCEMENTS_SUPPORTED ((dword) 0x00000004L) +#define LI_MIXING_SUPPORTED ((dword) 0x00000008L) +#define LI_CROSS_CONTROLLER_SUPPORTED ((dword) 0x00000010L) +#define LI2_GET_SUPPORTED_SERVICES 0 +#define LI2_REQ_CONNECT 1 +#define LI2_REQ_DISCONNECT 2 +#define LI2_IND_CONNECT_ACTIVE 1 +#define LI2_IND_DISCONNECT 2 +#define LI2_FLAG_INTERCONNECT_A_B ((dword) 0x00000001L) +#define LI2_FLAG_INTERCONNECT_B_A ((dword) 0x00000002L) +#define LI2_FLAG_MONITOR_B ((dword) 0x00000004L) +#define LI2_FLAG_MIX_B ((dword) 0x00000008L) +#define LI2_FLAG_MONITOR_X ((dword) 0x00000010L) +#define LI2_FLAG_MIX_X ((dword) 0x00000020L) +#define LI2_FLAG_LOOP_B ((dword) 0x00000040L) +#define LI2_FLAG_LOOP_PC ((dword) 0x00000080L) +#define LI2_FLAG_LOOP_X ((dword) 0x00000100L) +#define LI2_CROSS_CONTROLLER_SUPPORTED ((dword) 0x00000001L) +#define LI2_ASYMMETRIC_SUPPORTED ((dword) 0x00000002L) +#define LI2_MONITORING_SUPPORTED ((dword) 0x00000004L) +#define LI2_MIXING_SUPPORTED ((dword) 0x00000008L) +#define LI2_REMOTE_MONITORING_SUPPORTED ((dword) 0x00000010L) +#define LI2_REMOTE_MIXING_SUPPORTED ((dword) 0x00000020L) +#define LI2_B_LOOPING_SUPPORTED ((dword) 0x00000040L) +#define LI2_PC_LOOPING_SUPPORTED ((dword) 0x00000080L) +#define LI2_X_LOOPING_SUPPORTED ((dword) 0x00000100L) +/*------------------------------------------------------------------*/ +/* echo canceller definitions */ +/*------------------------------------------------------------------*/ +#define EC_GET_SUPPORTED_SERVICES 0 +#define EC_ENABLE_OPERATION 1 +#define EC_DISABLE_OPERATION 2 +#define EC_ENABLE_NON_LINEAR_PROCESSING 0x0001 +#define EC_DO_NOT_REQUIRE_REVERSALS 0x0002 +#define EC_DETECT_DISABLE_TONE 0x0004 +#define EC_ENABLE_ADAPTIVE_PREDELAY 0x0008 +#define EC_NON_LINEAR_PROCESSING_SUPPORTED 0x0001 +#define EC_BYPASS_ON_ANY_2100HZ_SUPPORTED 0x0002 +#define EC_BYPASS_ON_REV_2100HZ_SUPPORTED 0x0004 +#define EC_ADAPTIVE_PREDELAY_SUPPORTED 0x0008 +#define EC_BYPASS_INDICATION 1 +#define EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ 1 +#define EC_BYPASS_DUE_TO_REVERSED_2100HZ 2 +#define EC_BYPASS_RELEASED 3 +/*------------------------------------------------------------------*/ +/* function prototypes */ +/*------------------------------------------------------------------*/ +/*------------------------------------------------------------------*/ +#endif /* _INC_CAPI20 */ diff --git a/drivers/isdn/hardware/eicon/capidtmf.c b/drivers/isdn/hardware/eicon/capidtmf.c new file mode 100644 index 000000000..e3f778415 --- /dev/null +++ b/drivers/isdn/hardware/eicon/capidtmf.c @@ -0,0 +1,685 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "platform.h" + + + + + + + + + +#include "capidtmf.h" + +/* #define TRACE_ */ + +#define FILE_ "CAPIDTMF.C" + +/*---------------------------------------------------------------------------*/ + + +#define trace(a) + + + +/*---------------------------------------------------------------------------*/ + +static short capidtmf_expand_table_alaw[0x0100] = +{ + -5504, 5504, -344, 344, -22016, 22016, -1376, 1376, + -2752, 2752, -88, 88, -11008, 11008, -688, 688, + -7552, 7552, -472, 472, -30208, 30208, -1888, 1888, + -3776, 3776, -216, 216, -15104, 15104, -944, 944, + -4480, 4480, -280, 280, -17920, 17920, -1120, 1120, + -2240, 2240, -24, 24, -8960, 8960, -560, 560, + -6528, 6528, -408, 408, -26112, 26112, -1632, 1632, + -3264, 3264, -152, 152, -13056, 13056, -816, 816, + -6016, 6016, -376, 376, -24064, 24064, -1504, 1504, + -3008, 3008, -120, 120, -12032, 12032, -752, 752, + -8064, 8064, -504, 504, -32256, 32256, -2016, 2016, + -4032, 4032, -248, 248, -16128, 16128, -1008, 1008, + -4992, 4992, -312, 312, -19968, 19968, -1248, 1248, + -2496, 2496, -56, 56, -9984, 9984, -624, 624, + -7040, 7040, -440, 440, -28160, 28160, -1760, 1760, + -3520, 3520, -184, 184, -14080, 14080, -880, 880, + -5248, 5248, -328, 328, -20992, 20992, -1312, 1312, + -2624, 2624, -72, 72, -10496, 10496, -656, 656, + -7296, 7296, -456, 456, -29184, 29184, -1824, 1824, + -3648, 3648, -200, 200, -14592, 14592, -912, 912, + -4224, 4224, -264, 264, -16896, 16896, -1056, 1056, + -2112, 2112, -8, 8, -8448, 8448, -528, 528, + -6272, 6272, -392, 392, -25088, 25088, -1568, 1568, + -3136, 3136, -136, 136, -12544, 12544, -784, 784, + -5760, 5760, -360, 360, -23040, 23040, -1440, 1440, + -2880, 2880, -104, 104, -11520, 11520, -720, 720, + -7808, 7808, -488, 488, -31232, 31232, -1952, 1952, + -3904, 3904, -232, 232, -15616, 15616, -976, 976, + -4736, 4736, -296, 296, -18944, 18944, -1184, 1184, + -2368, 2368, -40, 40, -9472, 9472, -592, 592, + -6784, 6784, -424, 424, -27136, 27136, -1696, 1696, + -3392, 3392, -168, 168, -13568, 13568, -848, 848 +}; + +static short capidtmf_expand_table_ulaw[0x0100] = +{ + -32124, 32124, -1884, 1884, -7932, 7932, -372, 372, + -15996, 15996, -876, 876, -3900, 3900, -120, 120, + -23932, 23932, -1372, 1372, -5884, 5884, -244, 244, + -11900, 11900, -620, 620, -2876, 2876, -56, 56, + -28028, 28028, -1628, 1628, -6908, 6908, -308, 308, + -13948, 13948, -748, 748, -3388, 3388, -88, 88, + -19836, 19836, -1116, 1116, -4860, 4860, -180, 180, + -9852, 9852, -492, 492, -2364, 2364, -24, 24, + -30076, 30076, -1756, 1756, -7420, 7420, -340, 340, + -14972, 14972, -812, 812, -3644, 3644, -104, 104, + -21884, 21884, -1244, 1244, -5372, 5372, -212, 212, + -10876, 10876, -556, 556, -2620, 2620, -40, 40, + -25980, 25980, -1500, 1500, -6396, 6396, -276, 276, + -12924, 12924, -684, 684, -3132, 3132, -72, 72, + -17788, 17788, -988, 988, -4348, 4348, -148, 148, + -8828, 8828, -428, 428, -2108, 2108, -8, 8, + -31100, 31100, -1820, 1820, -7676, 7676, -356, 356, + -15484, 15484, -844, 844, -3772, 3772, -112, 112, + -22908, 22908, -1308, 1308, -5628, 5628, -228, 228, + -11388, 11388, -588, 588, -2748, 2748, -48, 48, + -27004, 27004, -1564, 1564, -6652, 6652, -292, 292, + -13436, 13436, -716, 716, -3260, 3260, -80, 80, + -18812, 18812, -1052, 1052, -4604, 4604, -164, 164, + -9340, 9340, -460, 460, -2236, 2236, -16, 16, + -29052, 29052, -1692, 1692, -7164, 7164, -324, 324, + -14460, 14460, -780, 780, -3516, 3516, -96, 96, + -20860, 20860, -1180, 1180, -5116, 5116, -196, 196, + -10364, 10364, -524, 524, -2492, 2492, -32, 32, + -24956, 24956, -1436, 1436, -6140, 6140, -260, 260, + -12412, 12412, -652, 652, -3004, 3004, -64, 64, + -16764, 16764, -924, 924, -4092, 4092, -132, 132, + -8316, 8316, -396, 396, -1980, 1980, 0, 0 +}; + + +/*---------------------------------------------------------------------------*/ + +static short capidtmf_recv_window_function[CAPIDTMF_RECV_ACCUMULATE_CYCLES] = +{ + -500L, -999L, -1499L, -1998L, -2496L, -2994L, -3491L, -3988L, + -4483L, -4978L, -5471L, -5963L, -6454L, -6943L, -7431L, -7917L, + -8401L, -8883L, -9363L, -9840L, -10316L, -10789L, -11259L, -11727L, + -12193L, -12655L, -13115L, -13571L, -14024L, -14474L, -14921L, -15364L, + -15804L, -16240L, -16672L, -17100L, -17524L, -17944L, -18360L, -18772L, + -19180L, -19583L, -19981L, -20375L, -20764L, -21148L, -21527L, -21901L, + -22270L, -22634L, -22993L, -23346L, -23694L, -24037L, -24374L, -24705L, + -25030L, -25350L, -25664L, -25971L, -26273L, -26568L, -26858L, -27141L, + -27418L, -27688L, -27952L, -28210L, -28461L, -28705L, -28943L, -29174L, + -29398L, -29615L, -29826L, -30029L, -30226L, -30415L, -30598L, -30773L, + -30941L, -31102L, -31256L, -31402L, -31541L, -31673L, -31797L, -31914L, + -32024L, -32126L, -32221L, -32308L, -32388L, -32460L, -32524L, -32581L, + -32631L, -32673L, -32707L, -32734L, -32753L, -32764L, -32768L, -32764L, + -32753L, -32734L, -32707L, -32673L, -32631L, -32581L, -32524L, -32460L, + -32388L, -32308L, -32221L, -32126L, -32024L, -31914L, -31797L, -31673L, + -31541L, -31402L, -31256L, -31102L, -30941L, -30773L, -30598L, -30415L, + -30226L, -30029L, -29826L, -29615L, -29398L, -29174L, -28943L, -28705L, + -28461L, -28210L, -27952L, -27688L, -27418L, -27141L, -26858L, -26568L, + -26273L, -25971L, -25664L, -25350L, -25030L, -24705L, -24374L, -24037L, + -23694L, -23346L, -22993L, -22634L, -22270L, -21901L, -21527L, -21148L, + -20764L, -20375L, -19981L, -19583L, -19180L, -18772L, -18360L, -17944L, + -17524L, -17100L, -16672L, -16240L, -15804L, -15364L, -14921L, -14474L, + -14024L, -13571L, -13115L, -12655L, -12193L, -11727L, -11259L, -10789L, + -10316L, -9840L, -9363L, -8883L, -8401L, -7917L, -7431L, -6943L, + -6454L, -5963L, -5471L, -4978L, -4483L, -3988L, -3491L, -2994L, + -2496L, -1998L, -1499L, -999L, -500L, +}; + +static byte capidtmf_leading_zeroes_table[0x100] = +{ + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#define capidtmf_byte_leading_zeroes(b) (capidtmf_leading_zeroes_table[(BYTE)(b)]) +#define capidtmf_word_leading_zeroes(w) (((w) & 0xff00) ? capidtmf_leading_zeroes_table[(w) >> 8] : 8 + capidtmf_leading_zeroes_table[(w)]) +#define capidtmf_dword_leading_zeroes(d) (((d) & 0xffff0000L) ? (((d) & 0xff000000L) ? capidtmf_leading_zeroes_table[(d) >> 24] : 8 + capidtmf_leading_zeroes_table[(d) >> 16]) : (((d) & 0xff00) ? 16 + capidtmf_leading_zeroes_table[(d) >> 8] : 24 + capidtmf_leading_zeroes_table[(d)])) + + +/*---------------------------------------------------------------------------*/ + + +static void capidtmf_goertzel_loop(long *buffer, long *coeffs, short *sample, long count) +{ + int i, j; + long c, d, q0, q1, q2; + + for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - 1; i++) + { + q1 = buffer[i]; + q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT]; + d = coeffs[i] >> 1; + c = d << 1; + if (c >= 0) + { + for (j = 0; j < count; j++) + { + q0 = sample[j] - q2 + (c * (q1 >> 16)) + (((dword)(((dword) d) * ((dword)(q1 & 0xffff)))) >> 15); + q2 = q1; + q1 = q0; + } + } + else + { + c = -c; + d = -d; + for (j = 0; j < count; j++) + { + q0 = sample[j] - q2 - ((c * (q1 >> 16)) + (((dword)(((dword) d) * ((dword)(q1 & 0xffff)))) >> 15)); + q2 = q1; + q1 = q0; + } + } + buffer[i] = q1; + buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = q2; + } + q1 = buffer[i]; + q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT]; + c = (coeffs[i] >> 1) << 1; + if (c >= 0) + { + for (j = 0; j < count; j++) + { + q0 = sample[j] - q2 + (c * (q1 >> 16)) + (((dword)(((dword)(c >> 1)) * ((dword)(q1 & 0xffff)))) >> 15); + q2 = q1; + q1 = q0; + c -= CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT; + } + } + else + { + c = -c; + for (j = 0; j < count; j++) + { + q0 = sample[j] - q2 - ((c * (q1 >> 16)) + (((dword)(((dword)(c >> 1)) * ((dword)(q1 & 0xffff)))) >> 15)); + q2 = q1; + q1 = q0; + c += CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT; + } + } + coeffs[i] = c; + buffer[i] = q1; + buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = q2; +} + + +static void capidtmf_goertzel_result(long *buffer, long *coeffs) +{ + int i; + long d, e, q1, q2, lo, mid, hi; + dword k; + + for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++) + { + q1 = buffer[i]; + q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT]; + d = coeffs[i] >> 1; + if (d >= 0) + d = ((d << 1) * (-q1 >> 16)) + (((dword)(((dword) d) * ((dword)(-q1 & 0xffff)))) >> 15); + else + d = ((-d << 1) * (-q1 >> 16)) + (((dword)(((dword) -d) * ((dword)(-q1 & 0xffff)))) >> 15); + e = (q2 >= 0) ? q2 : -q2; + if (d >= 0) + { + k = ((dword)(d & 0xffff)) * ((dword)(e & 0xffff)); + lo = k & 0xffff; + mid = k >> 16; + k = ((dword)(d >> 16)) * ((dword)(e & 0xffff)); + mid += k & 0xffff; + hi = k >> 16; + k = ((dword)(d & 0xffff)) * ((dword)(e >> 16)); + mid += k & 0xffff; + hi += k >> 16; + hi += ((dword)(d >> 16)) * ((dword)(e >> 16)); + } + else + { + d = -d; + k = ((dword)(d & 0xffff)) * ((dword)(e & 0xffff)); + lo = -((long)(k & 0xffff)); + mid = -((long)(k >> 16)); + k = ((dword)(d >> 16)) * ((dword)(e & 0xffff)); + mid -= k & 0xffff; + hi = -((long)(k >> 16)); + k = ((dword)(d & 0xffff)) * ((dword)(e >> 16)); + mid -= k & 0xffff; + hi -= k >> 16; + hi -= ((dword)(d >> 16)) * ((dword)(e >> 16)); + } + if (q2 < 0) + { + lo = -lo; + mid = -mid; + hi = -hi; + } + d = (q1 >= 0) ? q1 : -q1; + k = ((dword)(d & 0xffff)) * ((dword)(d & 0xffff)); + lo += k & 0xffff; + mid += k >> 16; + k = ((dword)(d >> 16)) * ((dword)(d & 0xffff)); + mid += (k & 0xffff) << 1; + hi += (k >> 16) << 1; + hi += ((dword)(d >> 16)) * ((dword)(d >> 16)); + d = (q2 >= 0) ? q2 : -q2; + k = ((dword)(d & 0xffff)) * ((dword)(d & 0xffff)); + lo += k & 0xffff; + mid += k >> 16; + k = ((dword)(d >> 16)) * ((dword)(d & 0xffff)); + mid += (k & 0xffff) << 1; + hi += (k >> 16) << 1; + hi += ((dword)(d >> 16)) * ((dword)(d >> 16)); + mid += lo >> 16; + hi += mid >> 16; + buffer[i] = (lo & 0xffff) | (mid << 16); + buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = hi; + } +} + + +/*---------------------------------------------------------------------------*/ + +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_697 0 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_770 1 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_852 2 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_941 3 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1209 4 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1336 5 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1477 6 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1633 7 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_635 8 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1010 9 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1140 10 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1272 11 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1405 12 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1555 13 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1715 14 +#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1875 15 + +#define CAPIDTMF_RECV_GUARD_SNR_DONTCARE 0xc000 +#define CAPIDTMF_RECV_NO_DIGIT 0xff +#define CAPIDTMF_RECV_TIME_GRANULARITY (CAPIDTMF_RECV_ACCUMULATE_CYCLES + 1) + +#define CAPIDTMF_RECV_INDICATION_DIGIT 0x0001 + +static long capidtmf_recv_goertzel_coef_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = +{ + 0xda97L * 2, /* 697 Hz (Low group 697 Hz) */ + 0xd299L * 2, /* 770 Hz (Low group 770 Hz) */ + 0xc8cbL * 2, /* 852 Hz (Low group 852 Hz) */ + 0xbd36L * 2, /* 941 Hz (Low group 941 Hz) */ + 0x9501L * 2, /* 1209 Hz (High group 1209 Hz) */ + 0x7f89L * 2, /* 1336 Hz (High group 1336 Hz) */ + 0x6639L * 2, /* 1477 Hz (High group 1477 Hz) */ + 0x48c6L * 2, /* 1633 Hz (High group 1633 Hz) */ + 0xe14cL * 2, /* 630 Hz (Lower guard of low group 631 Hz) */ + 0xb2e0L * 2, /* 1015 Hz (Upper guard of low group 1039 Hz) */ + 0xa1a0L * 2, /* 1130 Hz (Lower guard of high group 1140 Hz) */ + 0x8a87L * 2, /* 1272 Hz (Guard between 1209 Hz and 1336 Hz: 1271 Hz) */ + 0x7353L * 2, /* 1405 Hz (2nd harmonics of 697 Hz and guard between 1336 Hz and 1477 Hz: 1405 Hz) */ + 0x583bL * 2, /* 1552 Hz (2nd harmonics of 770 Hz and guard between 1477 Hz and 1633 Hz: 1553 Hz) */ + 0x37d8L * 2, /* 1720 Hz (2nd harmonics of 852 Hz and upper guard of high group: 1715 Hz) */ + 0x0000L * 2 /* 100-630 Hz (fundamentals) */ +}; + + +static word capidtmf_recv_guard_snr_low_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = +{ + 14, /* Low group peak versus 697 Hz */ + 14, /* Low group peak versus 770 Hz */ + 16, /* Low group peak versus 852 Hz */ + 16, /* Low group peak versus 941 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1209 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1336 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1477 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1633 Hz */ + 14, /* Low group peak versus 635 Hz */ + 16, /* Low group peak versus 1010 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1140 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1272 Hz */ + DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 8, /* Low group peak versus 1405 Hz */ + DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 4, /* Low group peak versus 1555 Hz */ + DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 4, /* Low group peak versus 1715 Hz */ + 12 /* Low group peak versus 100-630 Hz */ +}; + + +static word capidtmf_recv_guard_snr_high_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = +{ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 697 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 770 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 852 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 941 Hz */ + 20, /* High group peak versus 1209 Hz */ + 20, /* High group peak versus 1336 Hz */ + 20, /* High group peak versus 1477 Hz */ + 20, /* High group peak versus 1633 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 635 Hz */ + CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 1010 Hz */ + 16, /* High group peak versus 1140 Hz */ + 4, /* High group peak versus 1272 Hz */ + 6, /* High group peak versus 1405 Hz */ + 8, /* High group peak versus 1555 Hz */ + 16, /* High group peak versus 1715 Hz */ + 12 /* High group peak versus 100-630 Hz */ +}; + + +/*---------------------------------------------------------------------------*/ + +static void capidtmf_recv_init(t_capidtmf_state *p_state) +{ + p_state->recv.min_gap_duration = 1; + p_state->recv.min_digit_duration = 1; + + p_state->recv.cycle_counter = 0; + p_state->recv.current_digit_on_time = 0; + p_state->recv.current_digit_off_time = 0; + p_state->recv.current_digit_value = CAPIDTMF_RECV_NO_DIGIT; + + p_state->recv.digit_write_pos = 0; + p_state->recv.digit_read_pos = 0; + p_state->recv.indication_state = 0; + p_state->recv.indication_state_ack = 0; + p_state->recv.state = CAPIDTMF_RECV_STATE_IDLE; +} + + +void capidtmf_recv_enable(t_capidtmf_state *p_state, word min_digit_duration, word min_gap_duration) +{ + p_state->recv.indication_state_ack &= CAPIDTMF_RECV_INDICATION_DIGIT; + p_state->recv.min_digit_duration = (word)(((((dword) min_digit_duration) * 8) + + ((dword)(CAPIDTMF_RECV_TIME_GRANULARITY / 2))) / ((dword) CAPIDTMF_RECV_TIME_GRANULARITY)); + if (p_state->recv.min_digit_duration <= 1) + p_state->recv.min_digit_duration = 1; + else + (p_state->recv.min_digit_duration)--; + p_state->recv.min_gap_duration = + (word)((((dword) min_gap_duration) * 8) / ((dword) CAPIDTMF_RECV_TIME_GRANULARITY)); + if (p_state->recv.min_gap_duration <= 1) + p_state->recv.min_gap_duration = 1; + else + (p_state->recv.min_gap_duration)--; + p_state->recv.state |= CAPIDTMF_RECV_STATE_DTMF_ACTIVE; +} + + +void capidtmf_recv_disable(t_capidtmf_state *p_state) +{ + p_state->recv.state &= ~CAPIDTMF_RECV_STATE_DTMF_ACTIVE; + if (p_state->recv.state == CAPIDTMF_RECV_STATE_IDLE) + capidtmf_recv_init(p_state); + else + { + p_state->recv.cycle_counter = 0; + p_state->recv.current_digit_on_time = 0; + p_state->recv.current_digit_off_time = 0; + p_state->recv.current_digit_value = CAPIDTMF_RECV_NO_DIGIT; + } +} + + +word capidtmf_recv_indication(t_capidtmf_state *p_state, byte *buffer) +{ + word i, j, k, flags; + + flags = p_state->recv.indication_state ^ p_state->recv.indication_state_ack; + p_state->recv.indication_state_ack ^= flags & CAPIDTMF_RECV_INDICATION_DIGIT; + if (p_state->recv.digit_write_pos != p_state->recv.digit_read_pos) + { + i = 0; + k = p_state->recv.digit_write_pos; + j = p_state->recv.digit_read_pos; + do + { + buffer[i++] = p_state->recv.digit_buffer[j]; + j = (j == CAPIDTMF_RECV_DIGIT_BUFFER_SIZE - 1) ? 0 : j + 1; + } while (j != k); + p_state->recv.digit_read_pos = k; + return (i); + } + p_state->recv.indication_state_ack ^= flags; + return (0); +} + + +#define CAPIDTMF_RECV_WINDOWED_SAMPLES 32 + +void capidtmf_recv_block(t_capidtmf_state *p_state, byte *buffer, word length) +{ + byte result_digit; + word sample_number, cycle_counter, n, i; + word low_peak, high_peak; + dword lo, hi; + byte *p; + short *q; + byte goertzel_result_buffer[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT]; + short windowed_sample_buffer[CAPIDTMF_RECV_WINDOWED_SAMPLES]; + + + if (p_state->recv.state & CAPIDTMF_RECV_STATE_DTMF_ACTIVE) + { + cycle_counter = p_state->recv.cycle_counter; + sample_number = 0; + while (sample_number < length) + { + if (cycle_counter < CAPIDTMF_RECV_ACCUMULATE_CYCLES) + { + if (cycle_counter == 0) + { + for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++) + { + p_state->recv.goertzel_buffer[0][i] = 0; + p_state->recv.goertzel_buffer[1][i] = 0; + } + } + n = CAPIDTMF_RECV_ACCUMULATE_CYCLES - cycle_counter; + if (n > length - sample_number) + n = length - sample_number; + if (n > CAPIDTMF_RECV_WINDOWED_SAMPLES) + n = CAPIDTMF_RECV_WINDOWED_SAMPLES; + p = buffer + sample_number; + q = capidtmf_recv_window_function + cycle_counter; + if (p_state->ulaw) + { + for (i = 0; i < n; i++) + { + windowed_sample_buffer[i] = + (short)((capidtmf_expand_table_ulaw[p[i]] * ((long)(q[i]))) >> 15); + } + } + else + { + for (i = 0; i < n; i++) + { + windowed_sample_buffer[i] = + (short)((capidtmf_expand_table_alaw[p[i]] * ((long)(q[i]))) >> 15); + } + } + capidtmf_recv_goertzel_coef_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - 1] = CAPIDTMF_RECV_FUNDAMENTAL_OFFSET; + capidtmf_goertzel_loop(p_state->recv.goertzel_buffer[0], + capidtmf_recv_goertzel_coef_table, windowed_sample_buffer, n); + cycle_counter += n; + sample_number += n; + } + else + { + capidtmf_goertzel_result(p_state->recv.goertzel_buffer[0], + capidtmf_recv_goertzel_coef_table); + for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++) + { + lo = (dword)(p_state->recv.goertzel_buffer[0][i]); + hi = (dword)(p_state->recv.goertzel_buffer[1][i]); + if (hi != 0) + { + n = capidtmf_dword_leading_zeroes(hi); + hi = (hi << n) | (lo >> (32 - n)); + } + else + { + n = capidtmf_dword_leading_zeroes(lo); + hi = lo << n; + n += 32; + } + n = 195 - 3 * n; + if (hi >= 0xcb300000L) + n += 2; + else if (hi >= 0xa1450000L) + n++; + goertzel_result_buffer[i] = (byte) n; + } + low_peak = DSPDTMF_RX_SENSITIVITY_LOW_DEFAULT; + result_digit = CAPIDTMF_RECV_NO_DIGIT; + for (i = 0; i < CAPIDTMF_LOW_GROUP_FREQUENCIES; i++) + { + if (goertzel_result_buffer[i] > low_peak) + { + low_peak = goertzel_result_buffer[i]; + result_digit = (byte) i; + } + } + high_peak = DSPDTMF_RX_SENSITIVITY_HIGH_DEFAULT; + n = CAPIDTMF_RECV_NO_DIGIT; + for (i = CAPIDTMF_LOW_GROUP_FREQUENCIES; i < CAPIDTMF_RECV_BASE_FREQUENCY_COUNT; i++) + { + if (goertzel_result_buffer[i] > high_peak) + { + high_peak = goertzel_result_buffer[i]; + n = (i - CAPIDTMF_LOW_GROUP_FREQUENCIES) << 2; + } + } + result_digit |= (byte) n; + if (low_peak + DSPDTMF_RX_HIGH_EXCEEDING_LOW_DEFAULT < high_peak) + result_digit = CAPIDTMF_RECV_NO_DIGIT; + if (high_peak + DSPDTMF_RX_LOW_EXCEEDING_HIGH_DEFAULT < low_peak) + result_digit = CAPIDTMF_RECV_NO_DIGIT; + n = 0; + for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++) + { + if ((((short)(low_peak - goertzel_result_buffer[i] - capidtmf_recv_guard_snr_low_table[i])) < 0) + || (((short)(high_peak - goertzel_result_buffer[i] - capidtmf_recv_guard_snr_high_table[i])) < 0)) + { + n++; + } + } + if (n != 2) + result_digit = CAPIDTMF_RECV_NO_DIGIT; + + if (result_digit == CAPIDTMF_RECV_NO_DIGIT) + { + if (p_state->recv.current_digit_on_time != 0) + { + if (++(p_state->recv.current_digit_off_time) >= p_state->recv.min_gap_duration) + { + p_state->recv.current_digit_on_time = 0; + p_state->recv.current_digit_off_time = 0; + } + } + else + { + if (p_state->recv.current_digit_off_time != 0) + (p_state->recv.current_digit_off_time)--; + } + } + else + { + if ((p_state->recv.current_digit_on_time == 0) + && (p_state->recv.current_digit_off_time != 0)) + { + (p_state->recv.current_digit_off_time)--; + } + else + { + n = p_state->recv.current_digit_off_time; + if ((p_state->recv.current_digit_on_time != 0) + && (result_digit != p_state->recv.current_digit_value)) + { + p_state->recv.current_digit_on_time = 0; + n = 0; + } + p_state->recv.current_digit_value = result_digit; + p_state->recv.current_digit_off_time = 0; + if (p_state->recv.current_digit_on_time != 0xffff) + { + p_state->recv.current_digit_on_time += n + 1; + if (p_state->recv.current_digit_on_time >= p_state->recv.min_digit_duration) + { + p_state->recv.current_digit_on_time = 0xffff; + i = (p_state->recv.digit_write_pos == CAPIDTMF_RECV_DIGIT_BUFFER_SIZE - 1) ? + 0 : p_state->recv.digit_write_pos + 1; + if (i == p_state->recv.digit_read_pos) + { + trace(dprintf("%s,%d: Receive digit overrun", + (char *)(FILE_), __LINE__)); + } + else + { + p_state->recv.digit_buffer[p_state->recv.digit_write_pos] = result_digit; + p_state->recv.digit_write_pos = i; + p_state->recv.indication_state = + (p_state->recv.indication_state & ~CAPIDTMF_RECV_INDICATION_DIGIT) | + (~p_state->recv.indication_state_ack & CAPIDTMF_RECV_INDICATION_DIGIT); + } + } + } + } + } + cycle_counter = 0; + sample_number++; + } + } + p_state->recv.cycle_counter = cycle_counter; + } +} + + +void capidtmf_init(t_capidtmf_state *p_state, byte ulaw) +{ + p_state->ulaw = ulaw; + capidtmf_recv_init(p_state); +} + + +/*---------------------------------------------------------------------------*/ diff --git a/drivers/isdn/hardware/eicon/capidtmf.h b/drivers/isdn/hardware/eicon/capidtmf.h new file mode 100644 index 000000000..0a9cf59bb --- /dev/null +++ b/drivers/isdn/hardware/eicon/capidtmf.h @@ -0,0 +1,79 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef CAPIDTMF_H_ +#define CAPIDTMF_H_ +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +#define CAPIDTMF_TONE_GROUP_COUNT 2 +#define CAPIDTMF_LOW_GROUP_FREQUENCIES 4 +#define CAPIDTMF_HIGH_GROUP_FREQUENCIES 4 +#define DSPDTMF_RX_SENSITIVITY_LOW_DEFAULT 50 /* -52 dBm */ +#define DSPDTMF_RX_SENSITIVITY_HIGH_DEFAULT 50 /* -52 dBm */ +#define DSPDTMF_RX_HIGH_EXCEEDING_LOW_DEFAULT 10 /* dB */ +#define DSPDTMF_RX_LOW_EXCEEDING_HIGH_DEFAULT 10 /* dB */ +#define DSPDTMF_RX_HARMONICS_SEL_DEFAULT 12 /* dB */ +#define CAPIDTMF_RECV_BASE_FREQUENCY_COUNT (CAPIDTMF_LOW_GROUP_FREQUENCIES + CAPIDTMF_HIGH_GROUP_FREQUENCIES) +#define CAPIDTMF_RECV_GUARD_FREQUENCY_COUNT 8 +#define CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT (CAPIDTMF_RECV_BASE_FREQUENCY_COUNT + CAPIDTMF_RECV_GUARD_FREQUENCY_COUNT) +#define CAPIDTMF_RECV_POSITIVE_COEFF_COUNT 16 +#define CAPIDTMF_RECV_NEGATIVE_COEFF_COUNT (CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - CAPIDTMF_RECV_POSITIVE_COEFF_COUNT) +#define CAPIDTMF_RECV_ACCUMULATE_CYCLES 205 +#define CAPIDTMF_RECV_FUNDAMENTAL_OFFSET (0xff35L * 2) +#define CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT (0x0028L * 2) +#define CAPIDTMF_RECV_DIGIT_BUFFER_SIZE 32 +#define CAPIDTMF_RECV_STATE_IDLE 0x00 +#define CAPIDTMF_RECV_STATE_DTMF_ACTIVE 0x01 +typedef struct tag_capidtmf_recv_state +{ + byte digit_buffer[CAPIDTMF_RECV_DIGIT_BUFFER_SIZE]; + word digit_write_pos; + word digit_read_pos; + word indication_state; + word indication_state_ack; + long goertzel_buffer[2][CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT]; + word min_gap_duration; + word min_digit_duration; + word cycle_counter; + word current_digit_on_time; + word current_digit_off_time; + byte current_digit_value; + byte state; +} t_capidtmf_recv_state; +typedef struct tag_capidtmf_state +{ + byte ulaw; + t_capidtmf_recv_state recv; +} t_capidtmf_state; +word capidtmf_recv_indication(t_capidtmf_state *p_state, byte *buffer); +void capidtmf_recv_block(t_capidtmf_state *p_state, byte *buffer, word length); +void capidtmf_init(t_capidtmf_state *p_state, byte ulaw); +void capidtmf_recv_enable(t_capidtmf_state *p_state, word min_digit_duration, word min_gap_duration); +void capidtmf_recv_disable(t_capidtmf_state *p_state); +#define capidtmf_indication(p_state, buffer) (((p_state)->recv.indication_state != (p_state)->recv.indication_state_ack) ? capidtmf_recv_indication(p_state, buffer) : 0) +#define capidtmf_recv_process_block(p_state, buffer, length) { if ((p_state)->recv.state != CAPIDTMF_RECV_STATE_IDLE) capidtmf_recv_block(p_state, buffer, length); } +/*---------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +#endif diff --git a/drivers/isdn/hardware/eicon/capifunc.c b/drivers/isdn/hardware/eicon/capifunc.c new file mode 100644 index 000000000..7a0bdbdd8 --- /dev/null +++ b/drivers/isdn/hardware/eicon/capifunc.c @@ -0,0 +1,1219 @@ +/* $Id: capifunc.c,v 1.61.4.7 2005/02/11 19:40:25 armin Exp $ + * + * ISDN interface module for Eicon active cards DIVA. + * CAPI Interface common functions + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include "platform.h" +#include "os_capi.h" +#include "di_defs.h" +#include "capi20.h" +#include "divacapi.h" +#include "divasync.h" +#include "capifunc.h" + +#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) +#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) + +DIVA_CAPI_ADAPTER *adapter = (DIVA_CAPI_ADAPTER *) NULL; +APPL *application = (APPL *) NULL; +byte max_appl = MAX_APPL; +byte max_adapter = 0; +static CAPI_MSG *mapped_msg = (CAPI_MSG *) NULL; + +byte UnMapController(byte); +char DRIVERRELEASE_CAPI[32]; + +extern void AutomaticLaw(DIVA_CAPI_ADAPTER *); +extern void callback(ENTITY *); +extern word api_remove_start(void); +extern word CapiRelease(word); +extern word CapiRegister(word); +extern word api_put(APPL *, CAPI_MSG *); + +static diva_os_spin_lock_t api_lock; + +static LIST_HEAD(cards); + +static dword notify_handle; +static void DIRequest(ENTITY *e); +static DESCRIPTOR MAdapter; +static DESCRIPTOR DAdapter; +static byte ControllerMap[MAX_DESCRIPTORS + 1]; + + +static void diva_register_appl(struct capi_ctr *, __u16, + capi_register_params *); +static void diva_release_appl(struct capi_ctr *, __u16); +static char *diva_procinfo(struct capi_ctr *); +static u16 diva_send_message(struct capi_ctr *, + diva_os_message_buffer_s *); +extern void diva_os_set_controller_struct(struct capi_ctr *); + +extern void DIVA_DIDD_Read(DESCRIPTOR *, int); + +/* + * debug + */ +static void no_printf(unsigned char *, ...); +#include "debuglib.c" +static void xlog(char *x, ...) +{ +#ifndef DIVA_NO_DEBUGLIB + va_list ap; + if (myDriverDebugHandle.dbgMask & DL_XLOG) { + va_start(ap, x); + if (myDriverDebugHandle.dbg_irq) { + myDriverDebugHandle.dbg_irq(myDriverDebugHandle.id, + DLI_XLOG, x, ap); + } else if (myDriverDebugHandle.dbg_old) { + myDriverDebugHandle.dbg_old(myDriverDebugHandle.id, + x, ap); + } + va_end(ap); + } +#endif +} + +/* + * info for proc + */ +static char *diva_procinfo(struct capi_ctr *ctrl) +{ + return (ctrl->serial); +} + +/* + * stop debugging + */ +static void stop_dbg(void) +{ + DbgDeregister(); + memset(&MAdapter, 0, sizeof(MAdapter)); + dprintf = no_printf; +} + +/* + * dummy debug function + */ +static void no_printf(unsigned char *x, ...) +{ +} + +/* + * Controller mapping + */ +byte MapController(byte Controller) +{ + byte i; + byte MappedController = 0; + byte ctrl = Controller & 0x7f; /* mask external controller bit off */ + + for (i = 1; i < max_adapter + 1; i++) { + if (ctrl == ControllerMap[i]) { + MappedController = (byte) i; + break; + } + } + if (i > max_adapter) { + ControllerMap[0] = ctrl; + MappedController = 0; + } + return (MappedController | (Controller & 0x80)); /* put back external controller bit */ +} + +/* + * Controller unmapping + */ +byte UnMapController(byte MappedController) +{ + byte Controller; + byte ctrl = MappedController & 0x7f; /* mask external controller bit off */ + + if (ctrl <= max_adapter) { + Controller = ControllerMap[ctrl]; + } else { + Controller = 0; + } + + return (Controller | (MappedController & 0x80)); /* put back external controller bit */ +} + +/* + * find a new free id + */ +static int find_free_id(void) +{ + int num = 0; + DIVA_CAPI_ADAPTER *a; + + while (num < MAX_DESCRIPTORS) { + a = &adapter[num]; + if (!a->Id) + break; + num++; + } + return (num + 1); +} + +/* + * find a card structure by controller number + */ +static diva_card *find_card_by_ctrl(word controller) +{ + struct list_head *tmp; + diva_card *card; + + list_for_each(tmp, &cards) { + card = list_entry(tmp, diva_card, list); + if (ControllerMap[card->Id] == controller) { + if (card->remove_in_progress) + card = NULL; + return (card); + } + } + return (diva_card *) 0; +} + +/* + * Buffer RX/TX + */ +void *TransmitBufferSet(APPL *appl, dword ref) +{ + appl->xbuffer_used[ref] = true; + DBG_PRV1(("%d:xbuf_used(%d)", appl->Id, ref + 1)) + return (void *)(long)ref; +} + +void *TransmitBufferGet(APPL *appl, void *p) +{ + if (appl->xbuffer_internal[(dword)(long)p]) + return appl->xbuffer_internal[(dword)(long)p]; + + return appl->xbuffer_ptr[(dword)(long)p]; +} + +void TransmitBufferFree(APPL *appl, void *p) +{ + appl->xbuffer_used[(dword)(long)p] = false; + DBG_PRV1(("%d:xbuf_free(%d)", appl->Id, ((dword)(long)p) + 1)) + } + +void *ReceiveBufferGet(APPL *appl, int Num) +{ + return &appl->ReceiveBuffer[Num * appl->MaxDataLength]; +} + +/* + * api_remove_start/complete for cleanup + */ +void api_remove_complete(void) +{ + DBG_PRV1(("api_remove_complete")) + } + +/* + * main function called by message.c + */ +void sendf(APPL *appl, word command, dword Id, word Number, byte *format, ...) +{ + word i, j; + word length = 12, dlength = 0; + byte *write; + CAPI_MSG msg; + byte *string = NULL; + va_list ap; + diva_os_message_buffer_s *dmb; + diva_card *card = NULL; + dword tmp; + + if (!appl) + return; + + DBG_PRV1(("sendf(a=%d,cmd=%x,format=%s)", + appl->Id, command, (byte *) format)) + + PUT_WORD(&msg.header.appl_id, appl->Id); + PUT_WORD(&msg.header.command, command); + if ((byte) (command >> 8) == 0x82) + Number = appl->Number++; + PUT_WORD(&msg.header.number, Number); + + PUT_DWORD(&msg.header.controller, Id); + write = (byte *)&msg; + write += 12; + + va_start(ap, format); + for (i = 0; format[i]; i++) { + switch (format[i]) { + case 'b': + tmp = va_arg(ap, dword); + *(byte *) write = (byte) (tmp & 0xff); + write += 1; + length += 1; + break; + case 'w': + tmp = va_arg(ap, dword); + PUT_WORD(write, (tmp & 0xffff)); + write += 2; + length += 2; + break; + case 'd': + tmp = va_arg(ap, dword); + PUT_DWORD(write, tmp); + write += 4; + length += 4; + break; + case 's': + case 'S': + string = va_arg(ap, byte *); + length += string[0] + 1; + for (j = 0; j <= string[0]; j++) + *write++ = string[j]; + break; + } + } + va_end(ap); + + PUT_WORD(&msg.header.length, length); + msg.header.controller = UnMapController(msg.header.controller); + + if (command == _DATA_B3_I) + dlength = GET_WORD( + ((byte *)&msg.info.data_b3_ind.Data_Length)); + + if (!(dmb = diva_os_alloc_message_buffer(length + dlength, + (void **) &write))) { + DBG_ERR(("sendf: alloc_message_buffer failed, incoming msg dropped.")) + return; + } + + /* copy msg header to sk_buff */ + memcpy(write, (byte *)&msg, length); + + /* if DATA_B3_IND, copy data too */ + if (command == _DATA_B3_I) { + dword data = GET_DWORD(&msg.info.data_b3_ind.Data); + memcpy(write + length, (void *)(long)data, dlength); + } + +#ifndef DIVA_NO_DEBUGLIB + if (myDriverDebugHandle.dbgMask & DL_XLOG) { + switch (command) { + default: + xlog("\x00\x02", &msg, 0x81, length); + break; + case _DATA_B3_R | CONFIRM: + if (myDriverDebugHandle.dbgMask & DL_BLK) + xlog("\x00\x02", &msg, 0x81, length); + break; + case _DATA_B3_I: + if (myDriverDebugHandle.dbgMask & DL_BLK) { + xlog("\x00\x02", &msg, 0x81, length); + for (i = 0; i < dlength; i += 256) { + DBG_BLK((((char *)(long)GET_DWORD(&msg.info.data_b3_ind.Data)) + i, + ((dlength - i) < 256) ? (dlength - i) : 256)) + if (!(myDriverDebugHandle.dbgMask & DL_PRV0)) + break; /* not more if not explicitly requested */ + } + } + break; + } + } +#endif + + /* find the card structure for this controller */ + if (!(card = find_card_by_ctrl(write[8] & 0x7f))) { + DBG_ERR(("sendf - controller %d not found, incoming msg dropped", + write[8] & 0x7f)) + diva_os_free_message_buffer(dmb); + return; + } + /* send capi msg to capi layer */ + capi_ctr_handle_message(&card->capi_ctrl, appl->Id, dmb); +} + +/* + * cleanup adapter + */ +static void clean_adapter(int id, struct list_head *free_mem_q) +{ + DIVA_CAPI_ADAPTER *a; + int i, k; + + a = &adapter[id]; + k = li_total_channels - a->li_channels; + if (k == 0) { + if (li_config_table) { + list_add((struct list_head *)li_config_table, free_mem_q); + li_config_table = NULL; + } + } else { + if (a->li_base < k) { + memmove(&li_config_table[a->li_base], + &li_config_table[a->li_base + a->li_channels], + (k - a->li_base) * sizeof(LI_CONFIG)); + for (i = 0; i < k; i++) { + memmove(&li_config_table[i].flag_table[a->li_base], + &li_config_table[i].flag_table[a->li_base + a->li_channels], + k - a->li_base); + memmove(&li_config_table[i]. + coef_table[a->li_base], + &li_config_table[i].coef_table[a->li_base + a->li_channels], + k - a->li_base); + } + } + } + li_total_channels = k; + for (i = id; i < max_adapter; i++) { + if (adapter[i].request) + adapter[i].li_base -= a->li_channels; + } + if (a->plci) + list_add((struct list_head *)a->plci, free_mem_q); + + memset(a, 0x00, sizeof(DIVA_CAPI_ADAPTER)); + while ((max_adapter != 0) && !adapter[max_adapter - 1].request) + max_adapter--; +} + +/* + * remove a card, but ensures consistent state of LI tables + * in the time adapter is removed + */ +static void divacapi_remove_card(DESCRIPTOR *d) +{ + diva_card *card = NULL; + diva_os_spin_lock_magic_t old_irql; + LIST_HEAD(free_mem_q); + struct list_head *link; + struct list_head *tmp; + + /* + * Set "remove in progress flag". + * Ensures that there is no call from sendf to CAPI in + * the time CAPI controller is about to be removed. + */ + diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card"); + list_for_each(tmp, &cards) { + card = list_entry(tmp, diva_card, list); + if (card->d.request == d->request) { + card->remove_in_progress = 1; + list_del(tmp); + break; + } + } + diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card"); + + if (card) { + /* + * Detach CAPI. Sendf cannot call to CAPI any more. + * After detach no call to send_message() is done too. + */ + detach_capi_ctr(&card->capi_ctrl); + + /* + * Now get API lock (to ensure stable state of LI tables) + * and update the adapter map/LI table. + */ + diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card"); + + clean_adapter(card->Id - 1, &free_mem_q); + DBG_TRC(("DelAdapterMap (%d) -> (%d)", + ControllerMap[card->Id], card->Id)) + ControllerMap[card->Id] = 0; + DBG_TRC(("adapter remove, max_adapter=%d", + max_adapter)); + diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card"); + + /* After releasing the lock, we can free the memory */ + diva_os_free(0, card); + } + + /* free queued memory areas */ + list_for_each_safe(link, tmp, &free_mem_q) { + list_del(link); + diva_os_free(0, link); + } +} + +/* + * remove cards + */ +static void divacapi_remove_cards(void) +{ + DESCRIPTOR d; + struct list_head *tmp; + diva_card *card; + diva_os_spin_lock_magic_t old_irql; + +rescan: + diva_os_enter_spin_lock(&api_lock, &old_irql, "remove cards"); + list_for_each(tmp, &cards) { + card = list_entry(tmp, diva_card, list); + diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards"); + d.request = card->d.request; + divacapi_remove_card(&d); + goto rescan; + } + diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards"); +} + +/* + * sync_callback + */ +static void sync_callback(ENTITY *e) +{ + diva_os_spin_lock_magic_t old_irql; + + DBG_TRC(("cb:Id=%x,Rc=%x,Ind=%x", e->Id, e->Rc, e->Ind)) + + diva_os_enter_spin_lock(&api_lock, &old_irql, "sync_callback"); + callback(e); + diva_os_leave_spin_lock(&api_lock, &old_irql, "sync_callback"); +} + +/* + * add a new card + */ +static int diva_add_card(DESCRIPTOR *d) +{ + int k = 0, i = 0; + diva_os_spin_lock_magic_t old_irql; + diva_card *card = NULL; + struct capi_ctr *ctrl = NULL; + DIVA_CAPI_ADAPTER *a = NULL; + IDI_SYNC_REQ sync_req; + char serial[16]; + void *mem_to_free; + LI_CONFIG *new_li_config_table; + int j; + + if (!(card = (diva_card *) diva_os_malloc(0, sizeof(diva_card)))) { + DBG_ERR(("diva_add_card: failed to allocate card struct.")) + return (0); + } + memset((char *) card, 0x00, sizeof(diva_card)); + memcpy(&card->d, d, sizeof(DESCRIPTOR)); + sync_req.GetName.Req = 0; + sync_req.GetName.Rc = IDI_SYNC_REQ_GET_NAME; + card->d.request((ENTITY *)&sync_req); + strlcpy(card->name, sync_req.GetName.name, sizeof(card->name)); + ctrl = &card->capi_ctrl; + strcpy(ctrl->name, card->name); + ctrl->register_appl = diva_register_appl; + ctrl->release_appl = diva_release_appl; + ctrl->send_message = diva_send_message; + ctrl->procinfo = diva_procinfo; + ctrl->driverdata = card; + diva_os_set_controller_struct(ctrl); + + if (attach_capi_ctr(ctrl)) { + DBG_ERR(("diva_add_card: failed to attach controller.")) + diva_os_free(0, card); + return (0); + } + + diva_os_enter_spin_lock(&api_lock, &old_irql, "find id"); + card->Id = find_free_id(); + diva_os_leave_spin_lock(&api_lock, &old_irql, "find id"); + + strlcpy(ctrl->manu, M_COMPANY, sizeof(ctrl->manu)); + ctrl->version.majorversion = 2; + ctrl->version.minorversion = 0; + ctrl->version.majormanuversion = DRRELMAJOR; + ctrl->version.minormanuversion = DRRELMINOR; + sync_req.GetSerial.Req = 0; + sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL; + sync_req.GetSerial.serial = 0; + card->d.request((ENTITY *)&sync_req); + if ((i = ((sync_req.GetSerial.serial & 0xff000000) >> 24))) { + sprintf(serial, "%ld-%d", + sync_req.GetSerial.serial & 0x00ffffff, i + 1); + } else { + sprintf(serial, "%ld", sync_req.GetSerial.serial); + } + serial[CAPI_SERIAL_LEN - 1] = 0; + strlcpy(ctrl->serial, serial, sizeof(ctrl->serial)); + + a = &adapter[card->Id - 1]; + card->adapter = a; + a->os_card = card; + ControllerMap[card->Id] = (byte) (ctrl->cnr); + + DBG_TRC(("AddAdapterMap (%d) -> (%d)", ctrl->cnr, card->Id)) + + sync_req.xdi_capi_prms.Req = 0; + sync_req.xdi_capi_prms.Rc = IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS; + sync_req.xdi_capi_prms.info.structure_length = + sizeof(diva_xdi_get_capi_parameters_t); + card->d.request((ENTITY *)&sync_req); + a->flag_dynamic_l1_down = + sync_req.xdi_capi_prms.info.flag_dynamic_l1_down; + a->group_optimization_enabled = + sync_req.xdi_capi_prms.info.group_optimization_enabled; + a->request = DIRequest; /* card->d.request; */ + a->max_plci = card->d.channels + 30; + a->max_listen = (card->d.channels > 2) ? 8 : 2; + if (! + (a->plci = + (PLCI *) diva_os_malloc(0, sizeof(PLCI) * a->max_plci))) { + DBG_ERR(("diva_add_card: failed alloc plci struct.")) + memset(a, 0, sizeof(DIVA_CAPI_ADAPTER)); + return (0); + } + memset(a->plci, 0, sizeof(PLCI) * a->max_plci); + + for (k = 0; k < a->max_plci; k++) { + a->Id = (byte) card->Id; + a->plci[k].Sig.callback = sync_callback; + a->plci[k].Sig.XNum = 1; + a->plci[k].Sig.X = a->plci[k].XData; + a->plci[k].Sig.user[0] = (word) (card->Id - 1); + a->plci[k].Sig.user[1] = (word) k; + a->plci[k].NL.callback = sync_callback; + a->plci[k].NL.XNum = 1; + a->plci[k].NL.X = a->plci[k].XData; + a->plci[k].NL.user[0] = (word) ((card->Id - 1) | 0x8000); + a->plci[k].NL.user[1] = (word) k; + a->plci[k].adapter = a; + } + + a->profile.Number = card->Id; + a->profile.Channels = card->d.channels; + if (card->d.features & DI_FAX3) { + a->profile.Global_Options = 0x71; + if (card->d.features & DI_CODEC) + a->profile.Global_Options |= 0x6; +#if IMPLEMENT_DTMF + a->profile.Global_Options |= 0x8; +#endif /* IMPLEMENT_DTMF */ + a->profile.Global_Options |= 0x80; /* Line Interconnect */ +#if IMPLEMENT_ECHO_CANCELLER + a->profile.Global_Options |= 0x100; +#endif /* IMPLEMENT_ECHO_CANCELLER */ + a->profile.B1_Protocols = 0xdf; + a->profile.B2_Protocols = 0x1fdb; + a->profile.B3_Protocols = 0xb7; + a->manufacturer_features = MANUFACTURER_FEATURE_HARDDTMF; + } else { + a->profile.Global_Options = 0x71; + if (card->d.features & DI_CODEC) + a->profile.Global_Options |= 0x2; + a->profile.B1_Protocols = 0x43; + a->profile.B2_Protocols = 0x1f0f; + a->profile.B3_Protocols = 0x07; + a->manufacturer_features = 0; + } + + a->li_pri = (a->profile.Channels > 2); + a->li_channels = a->li_pri ? MIXER_CHANNELS_PRI : MIXER_CHANNELS_BRI; + a->li_base = 0; + for (i = 0; &adapter[i] != a; i++) { + if (adapter[i].request) + a->li_base = adapter[i].li_base + adapter[i].li_channels; + } + k = li_total_channels + a->li_channels; + new_li_config_table = + (LI_CONFIG *) diva_os_malloc(0, ((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * k) * ((k + 3) & ~3)); + if (new_li_config_table == NULL) { + DBG_ERR(("diva_add_card: failed alloc li_config table.")) + memset(a, 0, sizeof(DIVA_CAPI_ADAPTER)); + return (0); + } + + /* Prevent access to line interconnect table in process update */ + diva_os_enter_spin_lock(&api_lock, &old_irql, "add card"); + + j = 0; + for (i = 0; i < k; i++) { + if ((i >= a->li_base) && (i < a->li_base + a->li_channels)) + memset(&new_li_config_table[i], 0, sizeof(LI_CONFIG)); + else + memcpy(&new_li_config_table[i], &li_config_table[j], sizeof(LI_CONFIG)); + new_li_config_table[i].flag_table = + ((byte *) new_li_config_table) + (((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * i) * ((k + 3) & ~3)); + new_li_config_table[i].coef_table = + ((byte *) new_li_config_table) + (((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * i + 1) * ((k + 3) & ~3)); + if ((i >= a->li_base) && (i < a->li_base + a->li_channels)) { + new_li_config_table[i].adapter = a; + memset(&new_li_config_table[i].flag_table[0], 0, k); + memset(&new_li_config_table[i].coef_table[0], 0, k); + } else { + if (a->li_base != 0) { + memcpy(&new_li_config_table[i].flag_table[0], + &li_config_table[j].flag_table[0], + a->li_base); + memcpy(&new_li_config_table[i].coef_table[0], + &li_config_table[j].coef_table[0], + a->li_base); + } + memset(&new_li_config_table[i].flag_table[a->li_base], 0, a->li_channels); + memset(&new_li_config_table[i].coef_table[a->li_base], 0, a->li_channels); + if (a->li_base + a->li_channels < k) { + memcpy(&new_li_config_table[i].flag_table[a->li_base + + a->li_channels], + &li_config_table[j].flag_table[a->li_base], + k - (a->li_base + a->li_channels)); + memcpy(&new_li_config_table[i].coef_table[a->li_base + + a->li_channels], + &li_config_table[j].coef_table[a->li_base], + k - (a->li_base + a->li_channels)); + } + j++; + } + } + li_total_channels = k; + + mem_to_free = li_config_table; + + li_config_table = new_li_config_table; + for (i = card->Id; i < max_adapter; i++) { + if (adapter[i].request) + adapter[i].li_base += a->li_channels; + } + + if (a == &adapter[max_adapter]) + max_adapter++; + + list_add(&(card->list), &cards); + AutomaticLaw(a); + + diva_os_leave_spin_lock(&api_lock, &old_irql, "add card"); + + if (mem_to_free) { + diva_os_free(0, mem_to_free); + } + + i = 0; + while (i++ < 30) { + if (a->automatic_law > 3) + break; + diva_os_sleep(10); + } + + /* profile information */ + PUT_WORD(&ctrl->profile.nbchannel, card->d.channels); + ctrl->profile.goptions = a->profile.Global_Options; + ctrl->profile.support1 = a->profile.B1_Protocols; + ctrl->profile.support2 = a->profile.B2_Protocols; + ctrl->profile.support3 = a->profile.B3_Protocols; + /* manufacturer profile information */ + ctrl->profile.manu[0] = a->man_profile.private_options; + ctrl->profile.manu[1] = a->man_profile.rtp_primary_payloads; + ctrl->profile.manu[2] = a->man_profile.rtp_additional_payloads; + ctrl->profile.manu[3] = 0; + ctrl->profile.manu[4] = 0; + + capi_ctr_ready(ctrl); + + DBG_TRC(("adapter added, max_adapter=%d", max_adapter)); + return (1); +} + +/* + * register appl + */ +static void diva_register_appl(struct capi_ctr *ctrl, __u16 appl, + capi_register_params *rp) +{ + APPL *this; + word bnum, xnum; + int i = 0; + unsigned char *p; + void *DataNCCI, *DataFlags, *ReceiveBuffer, *xbuffer_used; + void **xbuffer_ptr, **xbuffer_internal; + diva_os_spin_lock_magic_t old_irql; + unsigned int mem_len; + int nconn = rp->level3cnt; + + + if (diva_os_in_irq()) { + DBG_ERR(("CAPI_REGISTER - in irq context !")) + return; + } + + DBG_TRC(("application register Id=%d", appl)) + + if (appl > MAX_APPL) { + DBG_ERR(("CAPI_REGISTER - appl.Id exceeds MAX_APPL")) + return; + } + + if (nconn <= 0) + nconn = ctrl->profile.nbchannel * -nconn; + + if (nconn == 0) + nconn = ctrl->profile.nbchannel; + + DBG_LOG(("CAPI_REGISTER - Id = %d", appl)) + DBG_LOG((" MaxLogicalConnections = %d(%d)", nconn, rp->level3cnt)) + DBG_LOG((" MaxBDataBuffers = %d", rp->datablkcnt)) + DBG_LOG((" MaxBDataLength = %d", rp->datablklen)) + + if (nconn < 1 || + nconn > 255 || + rp->datablklen < 80 || + rp->datablklen > 2150 || rp->datablkcnt > 255) { + DBG_ERR(("CAPI_REGISTER - invalid parameters")) + return; + } + + if (application[appl - 1].Id == appl) { + DBG_LOG(("CAPI_REGISTER - appl already registered")) + return; /* appl already registered */ + } + + /* alloc memory */ + + bnum = nconn * rp->datablkcnt; + xnum = nconn * MAX_DATA_B3; + + mem_len = bnum * sizeof(word); /* DataNCCI */ + mem_len += bnum * sizeof(word); /* DataFlags */ + mem_len += bnum * rp->datablklen; /* ReceiveBuffer */ + mem_len += xnum; /* xbuffer_used */ + mem_len += xnum * sizeof(void *); /* xbuffer_ptr */ + mem_len += xnum * sizeof(void *); /* xbuffer_internal */ + mem_len += xnum * rp->datablklen; /* xbuffer_ptr[xnum] */ + + DBG_LOG((" Allocated Memory = %d", mem_len)) + if (!(p = diva_os_malloc(0, mem_len))) { + DBG_ERR(("CAPI_REGISTER - memory allocation failed")) + return; + } + memset(p, 0, mem_len); + + DataNCCI = (void *)p; + p += bnum * sizeof(word); + DataFlags = (void *)p; + p += bnum * sizeof(word); + ReceiveBuffer = (void *)p; + p += bnum * rp->datablklen; + xbuffer_used = (void *)p; + p += xnum; + xbuffer_ptr = (void **)p; + p += xnum * sizeof(void *); + xbuffer_internal = (void **)p; + p += xnum * sizeof(void *); + for (i = 0; i < xnum; i++) { + xbuffer_ptr[i] = (void *)p; + p += rp->datablklen; + } + + /* initialize application data */ + diva_os_enter_spin_lock(&api_lock, &old_irql, "register_appl"); + + this = &application[appl - 1]; + memset(this, 0, sizeof(APPL)); + + this->Id = appl; + + for (i = 0; i < max_adapter; i++) { + adapter[i].CIP_Mask[appl - 1] = 0; + } + + this->queue_size = 1000; + + this->MaxNCCI = (byte) nconn; + this->MaxNCCIData = (byte) rp->datablkcnt; + this->MaxBuffer = bnum; + this->MaxDataLength = rp->datablklen; + + this->DataNCCI = DataNCCI; + this->DataFlags = DataFlags; + this->ReceiveBuffer = ReceiveBuffer; + this->xbuffer_used = xbuffer_used; + this->xbuffer_ptr = xbuffer_ptr; + this->xbuffer_internal = xbuffer_internal; + for (i = 0; i < xnum; i++) { + this->xbuffer_ptr[i] = xbuffer_ptr[i]; + } + + CapiRegister(this->Id); + diva_os_leave_spin_lock(&api_lock, &old_irql, "register_appl"); + +} + +/* + * release appl + */ +static void diva_release_appl(struct capi_ctr *ctrl, __u16 appl) +{ + diva_os_spin_lock_magic_t old_irql; + APPL *this = &application[appl - 1]; + void *mem_to_free = NULL; + + DBG_TRC(("application %d(%d) cleanup", this->Id, appl)) + + if (diva_os_in_irq()) { + DBG_ERR(("CAPI_RELEASE - in irq context !")) + return; + } + + diva_os_enter_spin_lock(&api_lock, &old_irql, "release_appl"); + if (this->Id) { + CapiRelease(this->Id); + mem_to_free = this->DataNCCI; + this->DataNCCI = NULL; + this->Id = 0; + } + diva_os_leave_spin_lock(&api_lock, &old_irql, "release_appl"); + + if (mem_to_free) + diva_os_free(0, mem_to_free); + +} + +/* + * send message + */ +static u16 diva_send_message(struct capi_ctr *ctrl, + diva_os_message_buffer_s *dmb) +{ + int i = 0; + word ret = 0; + diva_os_spin_lock_magic_t old_irql; + CAPI_MSG *msg = (CAPI_MSG *) DIVA_MESSAGE_BUFFER_DATA(dmb); + APPL *this = &application[GET_WORD(&msg->header.appl_id) - 1]; + diva_card *card = ctrl->driverdata; + __u32 length = DIVA_MESSAGE_BUFFER_LEN(dmb); + word clength = GET_WORD(&msg->header.length); + word command = GET_WORD(&msg->header.command); + u16 retval = CAPI_NOERROR; + + if (diva_os_in_irq()) { + DBG_ERR(("CAPI_SEND_MSG - in irq context !")) + return CAPI_REGOSRESOURCEERR; + } + DBG_PRV1(("Write - appl = %d, cmd = 0x%x", this->Id, command)) + + if (card->remove_in_progress) { + DBG_ERR(("CAPI_SEND_MSG - remove in progress!")) + return CAPI_REGOSRESOURCEERR; + } + + diva_os_enter_spin_lock(&api_lock, &old_irql, "send message"); + + if (!this->Id) { + diva_os_leave_spin_lock(&api_lock, &old_irql, "send message"); + return CAPI_ILLAPPNR; + } + + /* patch controller number */ + msg->header.controller = ControllerMap[card->Id] + | (msg->header.controller & 0x80); /* preserve external controller bit */ + + switch (command) { + default: + xlog("\x00\x02", msg, 0x80, clength); + break; + + case _DATA_B3_I | RESPONSE: +#ifndef DIVA_NO_DEBUGLIB + if (myDriverDebugHandle.dbgMask & DL_BLK) + xlog("\x00\x02", msg, 0x80, clength); +#endif + break; + + case _DATA_B3_R: +#ifndef DIVA_NO_DEBUGLIB + if (myDriverDebugHandle.dbgMask & DL_BLK) + xlog("\x00\x02", msg, 0x80, clength); +#endif + + if (clength == 24) + clength = 22; /* workaround for PPcom bug */ + /* header is always 22 */ + if (GET_WORD(&msg->info.data_b3_req.Data_Length) > + this->MaxDataLength + || GET_WORD(&msg->info.data_b3_req.Data_Length) > + (length - clength)) { + DBG_ERR(("Write - invalid message size")) + retval = CAPI_ILLCMDORSUBCMDORMSGTOSMALL; + goto write_end; + } + + for (i = 0; i < (MAX_DATA_B3 * this->MaxNCCI) + && this->xbuffer_used[i]; i++); + if (i == (MAX_DATA_B3 * this->MaxNCCI)) { + DBG_ERR(("Write - too many data pending")) + retval = CAPI_SENDQUEUEFULL; + goto write_end; + } + msg->info.data_b3_req.Data = i; + + this->xbuffer_internal[i] = NULL; + memcpy(this->xbuffer_ptr[i], &((__u8 *) msg)[clength], + GET_WORD(&msg->info.data_b3_req.Data_Length)); + +#ifndef DIVA_NO_DEBUGLIB + if ((myDriverDebugHandle.dbgMask & DL_BLK) + && (myDriverDebugHandle.dbgMask & DL_XLOG)) { + int j; + for (j = 0; j < + GET_WORD(&msg->info.data_b3_req.Data_Length); + j += 256) { + DBG_BLK((((char *) this->xbuffer_ptr[i]) + j, + ((GET_WORD(&msg->info.data_b3_req.Data_Length) - j) < + 256) ? (GET_WORD(&msg->info.data_b3_req.Data_Length) - j) : 256)) + if (!(myDriverDebugHandle.dbgMask & DL_PRV0)) + break; /* not more if not explicitly requested */ + } + } +#endif + break; + } + + memcpy(mapped_msg, msg, (__u32) clength); + mapped_msg->header.controller = MapController(mapped_msg->header.controller); + mapped_msg->header.length = clength; + mapped_msg->header.command = command; + mapped_msg->header.number = GET_WORD(&msg->header.number); + + ret = api_put(this, mapped_msg); + switch (ret) { + case 0: + break; + case _BAD_MSG: + DBG_ERR(("Write - bad message")) + retval = CAPI_ILLCMDORSUBCMDORMSGTOSMALL; + break; + case _QUEUE_FULL: + DBG_ERR(("Write - queue full")) + retval = CAPI_SENDQUEUEFULL; + break; + default: + DBG_ERR(("Write - api_put returned unknown error")) + retval = CAPI_UNKNOWNNOTPAR; + break; + } + +write_end: + diva_os_leave_spin_lock(&api_lock, &old_irql, "send message"); + if (retval == CAPI_NOERROR) + diva_os_free_message_buffer(dmb); + return retval; +} + + +/* + * cards request function + */ +static void DIRequest(ENTITY *e) +{ + DIVA_CAPI_ADAPTER *a = &(adapter[(byte) e->user[0]]); + diva_card *os_card = (diva_card *) a->os_card; + + if (e->Req && (a->FlowControlIdTable[e->ReqCh] == e->Id)) { + a->FlowControlSkipTable[e->ReqCh] = 1; + } + + (*(os_card->d.request)) (e); +} + +/* + * callback function from didd + */ +static void didd_callback(void *context, DESCRIPTOR *adapter, int removal) +{ + if (adapter->type == IDI_DADAPTER) { + DBG_ERR(("Notification about IDI_DADAPTER change ! Oops.")); + return; + } else if (adapter->type == IDI_DIMAINT) { + if (removal) { + stop_dbg(); + } else { + memcpy(&MAdapter, adapter, sizeof(MAdapter)); + dprintf = (DIVA_DI_PRINTF) MAdapter.request; + DbgRegister("CAPI20", DRIVERRELEASE_CAPI, DBG_DEFAULT); + } + } else if ((adapter->type > 0) && (adapter->type < 16)) { /* IDI Adapter */ + if (removal) { + divacapi_remove_card(adapter); + } else { + diva_add_card(adapter); + } + } + return; +} + +/* + * connect to didd + */ +static int divacapi_connect_didd(void) +{ + int x = 0; + int dadapter = 0; + IDI_SYNC_REQ req; + DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS]; + + DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table)); + + for (x = 0; x < MAX_DESCRIPTORS; x++) { + if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */ + memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter)); + dprintf = (DIVA_DI_PRINTF) MAdapter.request; + DbgRegister("CAPI20", DRIVERRELEASE_CAPI, DBG_DEFAULT); + break; + } + } + for (x = 0; x < MAX_DESCRIPTORS; x++) { + if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */ + dadapter = 1; + memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter)); + req.didd_notify.e.Req = 0; + req.didd_notify.e.Rc = + IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY; + req.didd_notify.info.callback = (void *)didd_callback; + req.didd_notify.info.context = NULL; + DAdapter.request((ENTITY *)&req); + if (req.didd_notify.e.Rc != 0xff) { + stop_dbg(); + return (0); + } + notify_handle = req.didd_notify.info.handle; + } + else if ((DIDD_Table[x].type > 0) && (DIDD_Table[x].type < 16)) { /* IDI Adapter found */ + diva_add_card(&DIDD_Table[x]); + } + } + + if (!dadapter) { + stop_dbg(); + } + + return (dadapter); +} + +/* + * diconnect from didd + */ +static void divacapi_disconnect_didd(void) +{ + IDI_SYNC_REQ req; + + stop_dbg(); + + req.didd_notify.e.Req = 0; + req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY; + req.didd_notify.info.handle = notify_handle; + DAdapter.request((ENTITY *)&req); +} + +/* + * we do not provide date/time here, + * the application should do this. + */ +int fax_head_line_time(char *buffer) +{ + return (0); +} + +/* + * init (alloc) main structures + */ +static int __init init_main_structs(void) +{ + if (!(mapped_msg = (CAPI_MSG *) diva_os_malloc(0, MAX_MSG_SIZE))) { + DBG_ERR(("init: failed alloc mapped_msg.")) + return 0; + } + + if (!(adapter = diva_os_malloc(0, sizeof(DIVA_CAPI_ADAPTER) * MAX_DESCRIPTORS))) { + DBG_ERR(("init: failed alloc adapter struct.")) + diva_os_free(0, mapped_msg); + return 0; + } + memset(adapter, 0, sizeof(DIVA_CAPI_ADAPTER) * MAX_DESCRIPTORS); + + if (!(application = diva_os_malloc(0, sizeof(APPL) * MAX_APPL))) { + DBG_ERR(("init: failed alloc application struct.")) + diva_os_free(0, mapped_msg); + diva_os_free(0, adapter); + return 0; + } + memset(application, 0, sizeof(APPL) * MAX_APPL); + + return (1); +} + +/* + * remove (free) main structures + */ +static void remove_main_structs(void) +{ + if (application) + diva_os_free(0, application); + if (adapter) + diva_os_free(0, adapter); + if (mapped_msg) + diva_os_free(0, mapped_msg); +} + +/* + * api_remove_start + */ +static void do_api_remove_start(void) +{ + diva_os_spin_lock_magic_t old_irql; + int ret = 1, count = 100; + + do { + diva_os_enter_spin_lock(&api_lock, &old_irql, "api remove start"); + ret = api_remove_start(); + diva_os_leave_spin_lock(&api_lock, &old_irql, "api remove start"); + + diva_os_sleep(10); + } while (ret && count--); + + if (ret) + DBG_ERR(("could not remove signaling ID's")) + } + +/* + * init + */ +int __init init_capifunc(void) +{ + diva_os_initialize_spin_lock(&api_lock, "capifunc"); + memset(ControllerMap, 0, MAX_DESCRIPTORS + 1); + max_adapter = 0; + + + if (!init_main_structs()) { + DBG_ERR(("init: failed to init main structs.")) + diva_os_destroy_spin_lock(&api_lock, "capifunc"); + return (0); + } + + if (!divacapi_connect_didd()) { + DBG_ERR(("init: failed to connect to DIDD.")) + do_api_remove_start(); + divacapi_remove_cards(); + remove_main_structs(); + diva_os_destroy_spin_lock(&api_lock, "capifunc"); + return (0); + } + + return (1); +} + +/* + * finit + */ +void __exit finit_capifunc(void) +{ + do_api_remove_start(); + divacapi_disconnect_didd(); + divacapi_remove_cards(); + remove_main_structs(); + diva_os_destroy_spin_lock(&api_lock, "capifunc"); +} diff --git a/drivers/isdn/hardware/eicon/capifunc.h b/drivers/isdn/hardware/eicon/capifunc.h new file mode 100644 index 000000000..e96c45bb5 --- /dev/null +++ b/drivers/isdn/hardware/eicon/capifunc.h @@ -0,0 +1,40 @@ +/* $Id: capifunc.h,v 1.11.4.1 2004/08/28 20:03:53 armin Exp $ + * + * ISDN interface module for Eicon active cards DIVA. + * CAPI Interface common functions + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#ifndef __CAPIFUNC_H__ +#define __CAPIFUNC_H__ + +#define DRRELMAJOR 2 +#define DRRELMINOR 0 +#define DRRELEXTRA "" + +#define M_COMPANY "Eicon Networks" + +extern char DRIVERRELEASE_CAPI[]; + +typedef struct _diva_card { + struct list_head list; + int remove_in_progress; + int Id; + struct capi_ctr capi_ctrl; + DIVA_CAPI_ADAPTER *adapter; + DESCRIPTOR d; + char name[32]; +} diva_card; + +/* + * prototypes + */ +int init_capifunc(void); +void finit_capifunc(void); + +#endif /* __CAPIFUNC_H__ */ diff --git a/drivers/isdn/hardware/eicon/capimain.c b/drivers/isdn/hardware/eicon/capimain.c new file mode 100644 index 000000000..f9244dc1c --- /dev/null +++ b/drivers/isdn/hardware/eicon/capimain.c @@ -0,0 +1,141 @@ +/* $Id: capimain.c,v 1.24 2003/09/09 06:51:05 schindler Exp $ + * + * ISDN interface module for Eicon active cards DIVA. + * CAPI Interface + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/uaccess.h> +#include <linux/seq_file.h> +#include <linux/skbuff.h> + +#include "os_capi.h" + +#include "platform.h" +#include "di_defs.h" +#include "capi20.h" +#include "divacapi.h" +#include "cp_vers.h" +#include "capifunc.h" + +static char *main_revision = "$Revision: 1.24 $"; +static char *DRIVERNAME = + "Eicon DIVA - CAPI Interface driver (http://www.melware.net)"; +static char *DRIVERLNAME = "divacapi"; + +MODULE_DESCRIPTION("CAPI driver for Eicon DIVA cards"); +MODULE_AUTHOR("Cytronics & Melware, Eicon Networks"); +MODULE_SUPPORTED_DEVICE("CAPI and DIVA card drivers"); +MODULE_LICENSE("GPL"); + +/* + * get revision number from revision string + */ +static char *getrev(const char *revision) +{ + char *rev; + char *p; + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + *--p = 0; + } else + rev = "1.0"; + return rev; + +} + +/* + * alloc a message buffer + */ +diva_os_message_buffer_s *diva_os_alloc_message_buffer(unsigned long size, + void **data_buf) +{ + diva_os_message_buffer_s *dmb = alloc_skb(size, GFP_ATOMIC); + if (dmb) { + *data_buf = skb_put(dmb, size); + } + return (dmb); +} + +/* + * free a message buffer + */ +void diva_os_free_message_buffer(diva_os_message_buffer_s *dmb) +{ + kfree_skb(dmb); +} + +/* + * proc function for controller info + */ +static int diva_ctl_proc_show(struct seq_file *m, void *v) +{ + struct capi_ctr *ctrl = m->private; + diva_card *card = (diva_card *) ctrl->driverdata; + + seq_printf(m, "%s\n", ctrl->name); + seq_printf(m, "Serial No. : %s\n", ctrl->serial); + seq_printf(m, "Id : %d\n", card->Id); + seq_printf(m, "Channels : %d\n", card->d.channels); + + return 0; +} + +/* + * set additional os settings in capi_ctr struct + */ +void diva_os_set_controller_struct(struct capi_ctr *ctrl) +{ + ctrl->driver_name = DRIVERLNAME; + ctrl->load_firmware = NULL; + ctrl->reset_ctr = NULL; + ctrl->proc_show = diva_ctl_proc_show; + ctrl->owner = THIS_MODULE; +} + +/* + * module init + */ +static int __init divacapi_init(void) +{ + char tmprev[32]; + int ret = 0; + + sprintf(DRIVERRELEASE_CAPI, "%d.%d%s", DRRELMAJOR, DRRELMINOR, + DRRELEXTRA); + + printk(KERN_INFO "%s\n", DRIVERNAME); + printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_CAPI); + strcpy(tmprev, main_revision); + printk("%s Build: %s(%s)\n", getrev(tmprev), + diva_capi_common_code_build, DIVA_BUILD); + + if (!(init_capifunc())) { + printk(KERN_ERR "%s: failed init capi_driver.\n", + DRIVERLNAME); + ret = -EIO; + } + + return ret; +} + +/* + * module exit + */ +static void __exit divacapi_exit(void) +{ + finit_capifunc(); + printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME); +} + +module_init(divacapi_init); +module_exit(divacapi_exit); diff --git a/drivers/isdn/hardware/eicon/cardtype.h b/drivers/isdn/hardware/eicon/cardtype.h new file mode 100644 index 000000000..8b20e22ca --- /dev/null +++ b/drivers/isdn/hardware/eicon/cardtype.h @@ -0,0 +1,1098 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef _CARDTYPE_H_ +#define _CARDTYPE_H_ +#ifndef CARDTYPE_H_WANT_DATA +#define CARDTYPE_H_WANT_DATA 0 +#endif +#ifndef CARDTYPE_H_WANT_IDI_DATA +#define CARDTYPE_H_WANT_IDI_DATA 0 +#endif +#ifndef CARDTYPE_H_WANT_RESOURCE_DATA +#define CARDTYPE_H_WANT_RESOURCE_DATA 1 +#endif +#ifndef CARDTYPE_H_WANT_FILE_DATA +#define CARDTYPE_H_WANT_FILE_DATA 1 +#endif +/* + * D-channel protocol identifiers + * + * Attention: Unfortunately the identifiers defined here differ from + * the identifiers used in Protocol/1/Common/prot/q931.h . + * The only reason for this is that q931.h has not a global + * scope and we did not know about the definitions there. + * But the definitions here cannot be changed easily because + * they are used in setup scripts and programs. + * Thus the definitions here have to be mapped if they are + * used in the protocol code context ! + * + * Now the identifiers are defined in the q931lib/constant.h file. + * Unfortunately this file has also not a global scope. + * But beginning with PROTTYPE_US any new identifier will get the same + * value as the corresponding PROT_* definition in q931lib/constant.h ! + */ +#define PROTTYPE_MINVAL 0 +#define PROTTYPE_ETSI 0 +#define PROTTYPE_1TR6 1 +#define PROTTYPE_BELG 2 +#define PROTTYPE_FRANC 3 +#define PROTTYPE_ATEL 4 +#define PROTTYPE_NI 5 /* DMS 100, Nortel, National ISDN */ +#define PROTTYPE_5ESS 6 /* 5ESS , AT&T, 5ESS Custom */ +#define PROTTYPE_JAPAN 7 +#define PROTTYPE_SWED 8 +#define PROTTYPE_US 9 /* US autodetect */ +#define PROTTYPE_ITALY 10 +#define PROTTYPE_TWAN 11 +#define PROTTYPE_AUSTRAL 12 +#define PROTTYPE_4ESDN 13 +#define PROTTYPE_4ESDS 14 +#define PROTTYPE_4ELDS 15 +#define PROTTYPE_4EMGC 16 +#define PROTTYPE_4EMGI 17 +#define PROTTYPE_HONGKONG 18 +#define PROTTYPE_RBSCAS 19 +#define PROTTYPE_CORNETN 20 +#define PROTTYPE_QSIG 21 +#define PROTTYPE_NI_EWSD 22 /* EWSD, Siemens, National ISDN */ +#define PROTTYPE_5ESS_NI 23 /* 5ESS, Lucent, National ISDN */ +#define PROTTYPE_T1CORNETN 24 +#define PROTTYPE_CORNETNQ 25 +#define PROTTYPE_T1CORNETNQ 26 +#define PROTTYPE_T1QSIG 27 +#define PROTTYPE_E1UNCH 28 +#define PROTTYPE_T1UNCH 29 +#define PROTTYPE_E1CHAN 30 +#define PROTTYPE_T1CHAN 31 +#define PROTTYPE_R2CAS 32 +#define PROTTYPE_MAXVAL 32 +/* + * Card type identifiers + */ +#define CARD_UNKNOWN 0 +#define CARD_NONE 0 +/* DIVA cards */ +#define CARDTYPE_DIVA_MCA 0 +#define CARDTYPE_DIVA_ISA 1 +#define CARDTYPE_DIVA_PCM 2 +#define CARDTYPE_DIVAPRO_ISA 3 +#define CARDTYPE_DIVAPRO_PCM 4 +#define CARDTYPE_DIVAPICO_ISA 5 +#define CARDTYPE_DIVAPICO_PCM 6 +/* DIVA 2.0 cards */ +#define CARDTYPE_DIVAPRO20_PCI 7 +#define CARDTYPE_DIVA20_PCI 8 +/* S cards */ +#define CARDTYPE_QUADRO_ISA 9 +#define CARDTYPE_S_ISA 10 +#define CARDTYPE_S_MCA 11 +#define CARDTYPE_SX_ISA 12 +#define CARDTYPE_SX_MCA 13 +#define CARDTYPE_SXN_ISA 14 +#define CARDTYPE_SXN_MCA 15 +#define CARDTYPE_SCOM_ISA 16 +#define CARDTYPE_SCOM_MCA 17 +#define CARDTYPE_PR_ISA 18 +#define CARDTYPE_PR_MCA 19 +/* Diva Server cards (formerly called Maestra, later Amadeo) */ +#define CARDTYPE_MAESTRA_ISA 20 +#define CARDTYPE_MAESTRA_PCI 21 +/* Diva Server cards to be developed (Quadro, Primary rate) */ +#define CARDTYPE_DIVASRV_Q_8M_PCI 22 +#define CARDTYPE_DIVASRV_P_30M_PCI 23 +#define CARDTYPE_DIVASRV_P_2M_PCI 24 +#define CARDTYPE_DIVASRV_P_9M_PCI 25 +/* DIVA 2.0 cards */ +#define CARDTYPE_DIVA20_ISA 26 +#define CARDTYPE_DIVA20U_ISA 27 +#define CARDTYPE_DIVA20U_PCI 28 +#define CARDTYPE_DIVAPRO20_ISA 29 +#define CARDTYPE_DIVAPRO20U_ISA 30 +#define CARDTYPE_DIVAPRO20U_PCI 31 +/* DIVA combi cards (piccola ISDN + rockwell V.34 modem) */ +#define CARDTYPE_DIVAMOBILE_PCM 32 +#define CARDTYPE_TDKGLOBALPRO_PCM 33 +/* DIVA Pro PC OEM card for 'New Media Corporation' */ +#define CARDTYPE_NMC_DIVAPRO_PCM 34 +/* DIVA Pro 2.0 OEM cards for 'British Telecom' */ +#define CARDTYPE_BT_EXLANE_PCI 35 +#define CARDTYPE_BT_EXLANE_ISA 36 +/* DIVA low cost cards, 1st name DIVA 3.0, 2nd DIVA 2.01, 3rd ??? */ +#define CARDTYPE_DIVALOW_ISA 37 +#define CARDTYPE_DIVALOWU_ISA 38 +#define CARDTYPE_DIVALOW_PCI 39 +#define CARDTYPE_DIVALOWU_PCI 40 +/* DIVA combi cards (piccola ISDN + rockwell V.90 modem) */ +#define CARDTYPE_DIVAMOBILE_V90_PCM 41 +#define CARDTYPE_TDKGLOBPRO_V90_PCM 42 +#define CARDTYPE_DIVASRV_P_23M_PCI 43 +#define CARDTYPE_DIVALOW_USB 44 +/* DIVA Audio (CT) family */ +#define CARDTYPE_DIVA_CT_ST 45 +#define CARDTYPE_DIVA_CT_U 46 +#define CARDTYPE_DIVA_CTLITE_ST 47 +#define CARDTYPE_DIVA_CTLITE_U 48 +/* DIVA ISDN plus V.90 series */ +#define CARDTYPE_DIVAISDN_V90_PCM 49 +#define CARDTYPE_DIVAISDN_V90_PCI 50 +#define CARDTYPE_DIVAISDN_TA 51 +/* DIVA Server Voice cards */ +#define CARDTYPE_DIVASRV_VOICE_Q_8M_PCI 52 +/* DIVA Server V2 cards */ +#define CARDTYPE_DIVASRV_Q_8M_V2_PCI 53 +#define CARDTYPE_DIVASRV_P_30M_V2_PCI 54 +/* DIVA Server Voice V2 cards */ +#define CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI 55 +#define CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI 56 +/* Diva LAN */ +#define CARDTYPE_DIVAISDN_LAN 57 +#define CARDTYPE_DIVA_202_PCI_ST 58 +#define CARDTYPE_DIVA_202_PCI_U 59 +#define CARDTYPE_DIVASRV_B_2M_V2_PCI 60 +#define CARDTYPE_DIVASRV_B_2F_PCI 61 +#define CARDTYPE_DIVALOW_USBV2 62 +#define CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI 63 +#define CARDTYPE_DIVA_PRO_30_PCI_ST 64 +#define CARDTYPE_DIVA_CT_ST_V20 65 +/* Diva Mobile V.90 PC Card and Diva ISDN PC Card */ +#define CARDTYPE_DIVAMOBILE_V2_PCM 66 +#define CARDTYPE_DIVA_V2_PCM 67 +/* Re-badged Diva Pro PC Card */ +#define CARDTYPE_DIVA_PC_CARD 68 +/* next free card type identifier */ +#define CARDTYPE_MAX 69 +/* + * The card families + */ +#define FAMILY_DIVA 1 +#define FAMILY_S 2 +#define FAMILY_MAESTRA 3 +#define FAMILY_MAX 4 +/* + * The basic card types + */ +#define CARD_DIVA 1 /* DSP based, old DSP */ +#define CARD_PRO 2 /* DSP based, new DSP */ +#define CARD_PICO 3 /* HSCX based */ +#define CARD_S 4 /* IDI on board based */ +#define CARD_SX 5 /* IDI on board based */ +#define CARD_SXN 6 /* IDI on board based */ +#define CARD_SCOM 7 /* IDI on board based */ +#define CARD_QUAD 8 /* IDI on board based */ +#define CARD_PR 9 /* IDI on board based */ +#define CARD_MAE 10 /* IDI on board based */ +#define CARD_MAEQ 11 /* IDI on board based */ +#define CARD_MAEP 12 /* IDI on board based */ +#define CARD_DIVALOW 13 /* IPAC based */ +#define CARD_CT 14 /* SCOUT based */ +#define CARD_DIVATA 15 /* DIVA TA */ +#define CARD_DIVALAN 16 /* DIVA LAN */ +#define CARD_MAE2 17 /* IDI on board based */ +#define CARD_MAX 18 +/* + * The internal card types of the S family + */ +#define CARD_I_NONE 0 +#define CARD_I_S 0 +#define CARD_I_SX 1 +#define CARD_I_SCOM 2 +#define CARD_I_QUAD 3 +#define CARD_I_PR 4 +/* + * The bus types we support + */ +#define BUS_ISA 1 +#define BUS_PCM 2 +#define BUS_PCI 3 +#define BUS_MCA 4 +#define BUS_USB 5 +#define BUS_COM 6 +#define BUS_LAN 7 +/* + * The chips we use for B-channel traffic + */ +#define CHIP_NONE 0 +#define CHIP_DSP 1 +#define CHIP_HSCX 2 +#define CHIP_IPAC 3 +#define CHIP_SCOUT 4 +#define CHIP_EXTERN 5 +#define CHIP_IPACX 6 +/* + * The structures where the card properties are aggregated by id + */ +typedef struct CARD_PROPERTIES +{ char *Name; /* official marketing name */ + unsigned short PnPId; /* plug and play ID (for non PCMIA cards) */ + unsigned short Version; /* major and minor version no of the card */ + unsigned char DescType; /* card type to set in the IDI descriptor */ + unsigned char Family; /* basic family of the card */ + unsigned short Features; /* features bits to set in the IDI desc. */ + unsigned char Card; /* basic card type */ + unsigned char IType; /* internal type of S cards (read from ram) */ + unsigned char Bus; /* bus type this card is designed for */ + unsigned char Chip; /* chipset used on card */ + unsigned char Adapters; /* number of adapters on card */ + unsigned char Channels; /* # of channels per adapter */ + unsigned short E_info; /* # of ram entity info structs per adapter */ + unsigned short SizeIo; /* size of IO window per adapter */ + unsigned short SizeMem; /* size of memory window per adapter */ +} CARD_PROPERTIES; +typedef struct CARD_RESOURCE +{ unsigned char Int[10]; + unsigned short IoFirst; + unsigned short IoStep; + unsigned short IoCnt; + unsigned long MemFirst; + unsigned long MemStep; + unsigned short MemCnt; +} CARD_RESOURCE; +/* test if the card of type 't' is a plug & play card */ +#define IS_PNP(t) \ + ( \ + ( \ + CardProperties[t].Bus != BUS_ISA \ + && \ + CardProperties[t].Bus != BUS_MCA \ + ) \ + || \ + ( \ + CardProperties[t].Family != FAMILY_S \ + && \ + CardProperties[t].Card != CARD_DIVA \ + ) \ + ) +/* extract IDI Descriptor info for card type 't' (p == DescType/Features) */ +#define IDI_PROP(t, p) (CardProperties[t].p) +#if CARDTYPE_H_WANT_DATA +#if CARDTYPE_H_WANT_IDI_DATA +/* include "di_defs.h" for IDI adapter type and feature flag definitions */ +#include "di_defs.h" +#else /*!CARDTYPE_H_WANT_IDI_DATA*/ +/* define IDI adapter types and feature flags here to prevent inclusion */ +#ifndef IDI_ADAPTER_S +#define IDI_ADAPTER_S 1 +#define IDI_ADAPTER_PR 2 +#define IDI_ADAPTER_DIVA 3 +#define IDI_ADAPTER_MAESTRA 4 +#endif +#ifndef DI_VOICE +#define DI_VOICE 0x0 /* obsolete define */ +#define DI_FAX3 0x1 +#define DI_MODEM 0x2 +#define DI_POST 0x4 +#define DI_V110 0x8 +#define DI_V120 0x10 +#define DI_POTS 0x20 +#define DI_CODEC 0x40 +#define DI_MANAGE 0x80 +#define DI_V_42 0x0100 +#define DI_EXTD_FAX 0x0200 /* Extended FAX (ECM, 2D, T.6, Polling) */ +#define DI_AT_PARSER 0x0400 /* Build-in AT Parser in the L2 */ +#define DI_VOICE_OVER_IP 0x0800 /* Voice over IP support */ +#endif +#endif /*CARDTYPE_H_WANT_IDI_DATA*/ +#define DI_V1x0 (DI_V110 | DI_V120) +#define DI_NULL 0x0000 +#if defined(SOFT_DSP_SUPPORT) +#define SOFT_DSP_ADD_FEATURES (DI_MODEM | DI_FAX3 | DI_AT_PARSER) +#else +#define SOFT_DSP_ADD_FEATURES 0 +#endif +#if defined(SOFT_V110_SUPPORT) +#define DI_SOFT_V110 DI_V110 +#else +#define DI_SOFT_V110 0 +#endif +/*--- CardProperties [Index=CARDTYPE_....] ---------------------------------*/ +CARD_PROPERTIES CardProperties[] = +{ + { /* 0 */ + "Diva MCA", 0x6336, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3, + CARD_DIVA, CARD_I_NONE, BUS_MCA, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 1 */ + "Diva ISA", 0x0000, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3, + CARD_DIVA, CARD_I_NONE, BUS_ISA, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 2 */ + "Diva/PCM", 0x0000, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3, + CARD_DIVA, CARD_I_NONE, BUS_PCM, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 3 */ + "Diva PRO ISA", 0x0031, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC, + CARD_PRO, CARD_I_NONE, BUS_ISA, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 4 */ + "Diva PRO PC-Card", 0x0000, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_PRO, CARD_I_NONE, BUS_PCM, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 5 */ + "Diva PICCOLA ISA", 0x0051, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_ISA, CHIP_HSCX, + 1, 2, 0, 8, 0 + }, + { /* 6 */ + "Diva PICCOLA PCM", 0x0000, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_HSCX, + 1, 2, 0, 8, 0 + }, + { /* 7 */ + "Diva PRO 2.0 S/T PCI", 0xe001, 0x0200, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS, + CARD_PRO, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 8 */ + "Diva 2.0 S/T PCI", 0xe002, 0x0200, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_PCI, CHIP_HSCX, + 1, 2, 0, 8, 0 + }, + { /* 9 */ + "QUADRO ISA", 0x0000, 0x0100, + IDI_ADAPTER_S, FAMILY_S, DI_NULL, + CARD_QUAD, CARD_I_QUAD, BUS_ISA, CHIP_NONE, + 4, 2, 16, 0, 0x800 + }, + { /* 10 */ + "S ISA", 0x0000, 0x0100, + IDI_ADAPTER_S, FAMILY_S, DI_CODEC, + CARD_S, CARD_I_S, BUS_ISA, CHIP_NONE, + 1, 1, 16, 0, 0x800 + }, + { /* 11 */ + "S MCA", 0x6a93, 0x0100, + IDI_ADAPTER_S, FAMILY_S, DI_CODEC, + CARD_S, CARD_I_S, BUS_MCA, CHIP_NONE, + 1, 1, 16, 16, 0x400 + }, + { /* 12 */ + "SX ISA", 0x0000, 0x0100, + IDI_ADAPTER_S, FAMILY_S, DI_NULL, + CARD_SX, CARD_I_SX, BUS_ISA, CHIP_NONE, + 1, 2, 16, 0, 0x800 + }, + { /* 13 */ + "SX MCA", 0x6a93, 0x0100, + IDI_ADAPTER_S, FAMILY_S, DI_NULL, + CARD_SX, CARD_I_SX, BUS_MCA, CHIP_NONE, + 1, 2, 16, 16, 0x400 + }, + { /* 14 */ + "SXN ISA", 0x0000, 0x0100, + IDI_ADAPTER_S, FAMILY_S, DI_NULL, + CARD_SXN, CARD_I_SCOM, BUS_ISA, CHIP_NONE, + 1, 2, 16, 0, 0x800 + }, + { /* 15 */ + "SXN MCA", 0x6a93, 0x0100, + IDI_ADAPTER_S, FAMILY_S, DI_NULL, + CARD_SXN, CARD_I_SCOM, BUS_MCA, CHIP_NONE, + 1, 2, 16, 16, 0x400 + }, + { /* 16 */ + "SCOM ISA", 0x0000, 0x0100, + IDI_ADAPTER_S, FAMILY_S, DI_CODEC, + CARD_SCOM, CARD_I_SCOM, BUS_ISA, CHIP_NONE, + 1, 2, 16, 0, 0x800 + }, + { /* 17 */ + "SCOM MCA", 0x6a93, 0x0100, + IDI_ADAPTER_S, FAMILY_S, DI_CODEC, + CARD_SCOM, CARD_I_SCOM, BUS_MCA, CHIP_NONE, + 1, 2, 16, 16, 0x400 + }, + { /* 18 */ + "S2M ISA", 0x0000, 0x0100, + IDI_ADAPTER_PR, FAMILY_S, DI_NULL, + CARD_PR, CARD_I_PR, BUS_ISA, CHIP_NONE, + 1, 30, 256, 0, 0x4000 + }, + { /* 19 */ + "S2M MCA", 0x6abb, 0x0100, + IDI_ADAPTER_PR, FAMILY_S, DI_NULL, + CARD_PR, CARD_I_PR, BUS_MCA, CHIP_NONE, + 1, 30, 256, 16, 0x4000 + }, + { /* 20 */ + "Diva Server BRI-2M ISA", 0x0041, 0x0100, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_MAE, CARD_I_NONE, BUS_ISA, CHIP_DSP, + 1, 2, 16, 8, 0 + }, + { /* 21 */ + "Diva Server BRI-2M PCI", 0xE010, 0x0100, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_MAE, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 16, 8, 0 + }, + { /* 22 */ + "Diva Server 4BRI-8M PCI", 0xE012, 0x0100, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_MAEQ, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 4, 2, 16, 8, 0 + }, + { /* 23 */ + "Diva Server PRI-30M PCI", 0xE014, 0x0100, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 30, 256, 8, 0 + }, + { /* 24 */ + "Diva Server PRI-2M PCI", 0xe014, 0x0100, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 30, 256, 8, 0 + }, + { /* 25 */ + "Diva Server PRI-9M PCI", 0x0000, 0x0100, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 30, 256, 8, 0 + }, + { /* 26 */ + "Diva 2.0 S/T ISA", 0x0071, 0x0200, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_ISA, CHIP_HSCX, + 1, 2, 0, 8, 0 + }, + { /* 27 */ + "Diva 2.0 U ISA", 0x0091, 0x0200, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_ISA, CHIP_HSCX, + 1, 2, 0, 8, 0 + }, + { /* 28 */ + "Diva 2.0 U PCI", 0xe004, 0x0200, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_PCI, CHIP_HSCX, + 1, 2, 0, 8, 0 + }, + { /* 29 */ + "Diva PRO 2.0 S/T ISA", 0x0061, 0x0200, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS, + CARD_PRO, CARD_I_NONE, BUS_ISA, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 30 */ + "Diva PRO 2.0 U ISA", 0x0081, 0x0200, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS, + CARD_PRO, CARD_I_NONE, BUS_ISA, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 31 */ + "Diva PRO 2.0 U PCI", 0xe003, 0x0200, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS, + CARD_PRO, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 32 */ + "Diva MOBILE", 0x0000, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_HSCX, + 1, 2, 0, 8, 0 + }, + { /* 33 */ + "TDK DFI3600", 0x0000, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_HSCX, + 1, 2, 0, 8, 0 + }, + { /* 34 (OEM version of 4 - "Diva PRO PC-Card") */ + "New Media ISDN", 0x0000, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_PRO, CARD_I_NONE, BUS_PCM, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 35 (OEM version of 7 - "Diva PRO 2.0 S/T PCI") */ + "BT ExLane PCI", 0xe101, 0x0200, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS, + CARD_PRO, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 36 (OEM version of 29 - "Diva PRO 2.0 S/T ISA") */ + "BT ExLane ISA", 0x1061, 0x0200, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS, + CARD_PRO, CARD_I_NONE, BUS_ISA, CHIP_DSP, + 1, 2, 0, 8, 0 + }, + { /* 37 */ + "Diva 2.01 S/T ISA", 0x00A1, 0x0300, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_DIVALOW, CARD_I_NONE, BUS_ISA, CHIP_IPAC, + 1, 2, 0, 8, 0 + }, + { /* 38 */ + "Diva 2.01 U ISA", 0x00B1, 0x0300, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_DIVALOW, CARD_I_NONE, BUS_ISA, CHIP_IPAC, + 1, 2, 0, 8, 0 + }, + { /* 39 */ + "Diva 2.01 S/T PCI", 0xe005, 0x0300, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_DIVALOW, CARD_I_NONE, BUS_PCI, CHIP_IPAC, + 1, 2, 0, 8, 0 + }, + { /* 40 no ID yet */ + "Diva 2.01 U PCI", 0x0000, 0x0300, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_DIVALOW, CARD_I_NONE, BUS_PCI, CHIP_IPAC, + 1, 2, 0, 8, 0 + }, + { /* 41 */ + "Diva MOBILE V.90", 0x0000, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_HSCX, + 1, 2, 0, 8, 0 + }, + { /* 42 */ + "TDK DFI3600 V.90", 0x0000, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_HSCX, + 1, 2, 0, 8, 0 + }, + { /* 43 */ + "Diva Server PRI-23M PCI", 0xe014, 0x0100, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 30, 256, 8, 0 + }, + { /* 44 */ + "Diva 2.01 S/T USB", 0x1000, 0x0300, + IDI_ADAPTER_DIVA , FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_DIVALOW, CARD_I_NONE, BUS_USB, CHIP_IPAC, + 1, 2, 0, 8, 0 + }, + { /* 45 */ + "Diva CT S/T PCI", 0xe006, 0x0300, + IDI_ADAPTER_DIVA , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC, + CARD_CT, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 0, 0, 0 + }, + { /* 46 */ + "Diva CT U PCI", 0xe007, 0x0300, + IDI_ADAPTER_DIVA , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC, + CARD_CT, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 0, 0, 0 + }, + { /* 47 */ + "Diva CT Lite S/T PCI", 0xe008, 0x0300, + IDI_ADAPTER_DIVA , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC, + CARD_CT, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 0, 0, 0 + }, + { /* 48 */ + "Diva CT Lite U PCI", 0xe009, 0x0300, + IDI_ADAPTER_DIVA , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC, + CARD_CT, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 0, 0, 0 + }, + { /* 49 */ + "Diva ISDN+V.90 PC Card", 0x8D8C, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC, + CARD_DIVALOW, CARD_I_NONE, BUS_PCM, CHIP_IPAC, + 1, 2, 0, 8, 0 + }, + { /* 50 */ + "Diva ISDN+V.90 PCI", 0xe00A, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_DIVALOW, CARD_I_NONE, BUS_PCI, CHIP_IPAC, + 1, 2, 0, 8, 0 + }, + { /* 51 (DivaTA) no ID */ + "Diva TA", 0x0000, 0x0300, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V110 | DI_FAX3 | SOFT_DSP_ADD_FEATURES, + CARD_DIVATA, CARD_I_NONE, BUS_COM, CHIP_EXTERN, + 1, 1, 0, 8, 0 + }, + { /* 52 (Diva Server 4BRI-8M PCI adapter enabled for Voice) */ + "Diva Server Voice 4BRI-8M PCI", 0xE016, 0x0100, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP, + CARD_MAEQ, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 4, 2, 16, 8, 0 + }, + { /* 53 (Diva Server 4BRI 2.0 adapter) */ + "Diva Server 4BRI-8M 2.0 PCI", 0xE013, 0x0200, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_MAEQ, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 4, 2, 16, 8, 0 + }, + { /* 54 (Diva Server PRI 2.0 adapter) */ + "Diva Server PRI 2.0 PCI", 0xE015, 0x0200, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 30, 256, 8, 0 + }, + { /* 55 (Diva Server 4BRI-8M 2.0 PCI adapter enabled for Voice) */ + "Diva Server Voice 4BRI-8M 2.0 PCI", 0xE017, 0x0200, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP, + CARD_MAEQ, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 4, 2, 16, 8, 0 + }, + { /* 56 (Diva Server PRI 2.0 PCI adapter enabled for Voice) */ + "Diva Server Voice PRI 2.0 PCI", 0xE019, 0x0200, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP, + CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 30, 256, 8, 0 + }, + { + /* 57 (DivaLan ) no ID */ + "Diva LAN", 0x0000, 0x0300, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V110 | DI_FAX3 | SOFT_DSP_ADD_FEATURES, + CARD_DIVALAN, CARD_I_NONE, BUS_LAN, CHIP_EXTERN, + 1, 1, 0, 8, 0 + }, + { /* 58 */ + "Diva 2.02 PCI S/T", 0xE00B, 0x0300, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES | DI_SOFT_V110, + CARD_DIVALOW, CARD_I_NONE, BUS_PCI, CHIP_IPACX, + 1, 2, 0, 8, 0 + }, + { /* 59 */ + "Diva 2.02 PCI U", 0xE00C, 0x0300, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_DIVALOW, CARD_I_NONE, BUS_PCI, CHIP_IPACX, + 1, 2, 0, 8, 0 + }, + { /* 60 */ + "Diva Server BRI-2M 2.0 PCI", 0xE018, 0x0200, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_MAE2, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 16, 8, 0 + }, + { /* 61 (the previous name was Diva Server BRI-2F 2.0 PCI) */ + "Diva Server 2FX", 0xE01A, 0x0200, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_SOFT_V110, + CARD_MAE2, CARD_I_NONE, BUS_PCI, CHIP_IPACX, + 1, 2, 16, 8, 0 + }, + { /* 62 */ + " Diva ISDN USB 2.0", 0x1003, 0x0300, + IDI_ADAPTER_DIVA , FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_DIVALOW, CARD_I_NONE, BUS_USB, CHIP_IPACX, + 1, 2, 0, 8, 0 + }, + { /* 63 (Diva Server BRI-2M 2.0 PCI adapter enabled for Voice) */ + "Diva Server Voice BRI-2M 2.0 PCI", 0xE01B, 0x0200, + IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP, + CARD_MAE2, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 16, 8, 0 + }, + { /* 64 */ + "Diva Pro 3.0 PCI", 0xe00d, 0x0300, + IDI_ADAPTER_DIVA , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM, + CARD_PRO, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 0, 0, 0 + }, + { /* 65 */ + "Diva ISDN + CT 2.0", 0xE00E, 0x0300, + IDI_ADAPTER_DIVA , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC, + CARD_CT, CARD_I_NONE, BUS_PCI, CHIP_DSP, + 1, 2, 0, 0, 0 + }, + { /* 66 */ + "Diva Mobile V.90 PC Card", 0x8331, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_IPACX, + 1, 2, 0, 8, 0 + }, + { /* 67 */ + "Diva ISDN PC Card", 0x8311, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_IPACX, + 1, 2, 0, 8, 0 + }, + { /* 68 */ + "Diva ISDN PC Card", 0x0000, 0x0100, + IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES, + CARD_PRO, CARD_I_NONE, BUS_PCM, CHIP_DSP, + 1, 2, 0, 8, 0 + }, +}; +#if CARDTYPE_H_WANT_RESOURCE_DATA +/*--- CardResource [Index=CARDTYPE_....] ---------------------------(GEI)-*/ +CARD_RESOURCE CardResource[] = { +/* Interrupts IO-Address Mem-Address */ + /* 0*/ { 3,4,9,0,0,0,0,0,0,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA MCA + /* 1*/ { 3,4,9,10,11,12,0,0,0,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA ISA + /* 2*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA PCMCIA + /* 3*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA PRO ISA + /* 4*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA PRO PCMCIA + /* 5*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA PICCOLA ISA + /* 6*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA PICCOLA PCMCIA + /* 7*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA PRO 2.0 PCI + /* 8*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.0 PCI + /* 9*/ { 3,4,5,7,9,10,11,12,0,0, 0x0,0x0,0, 0x80000,0x2000,64 }, // QUADRO ISA + /*10*/ { 3,4,9,10,11,12,0,0,0,0, 0x0,0x0,0, 0xc0000,0x2000,16 }, // S ISA + /*11*/ { 3,4,9,0,0,0,0,0,0,0, 0xc00,0x10,16, 0xc0000,0x2000,16 }, // S MCA + /*12*/ { 3,4,9,10,11,12,0,0,0,0, 0x0,0x0,0, 0xc0000,0x2000,16 }, // SX ISA + /*13*/ { 3,4,9,0,0,0,0,0,0,0, 0xc00,0x10,16, 0xc0000,0x2000,16 }, // SX MCA + /*14*/ { 3,4,5,7,9,10,11,12,0,0, 0x0,0x0,0, 0x80000,0x0800,256 }, // SXN ISA + /*15*/ { 3,4,9,0,0,0,0,0,0,0, 0xc00,0x10,16, 0xc0000,0x2000,16 }, // SXN MCA + /*16*/ { 3,4,5,7,9,10,11,12,0,0, 0x0,0x0,0, 0x80000,0x0800,256 }, // SCOM ISA + /*17*/ { 3,4,9,0,0,0,0,0,0,0, 0xc00,0x10,16, 0xc0000,0x2000,16 }, // SCOM MCA + /*18*/ { 3,4,5,7,9,10,11,12,0,0, 0x0,0x0,0, 0xc0000,0x4000,16 }, // S2M ISA + /*19*/ { 3,4,9,0,0,0,0,0,0,0, 0xc00,0x10,16, 0xc0000,0x4000,16 }, // S2M MCA + /*20*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // MAESTRA ISA + /*21*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // MAESTRA PCI + /*22*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // MAESTRA QUADRO ISA + /*23*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048, 0x0,0x0,0 }, // MAESTRA QUADRO PCI + /*24*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // MAESTRA PRIMARY ISA + /*25*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // MAESTRA PRIMARY PCI + /*26*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA 2.0 ISA + /*27*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA 2.0 /U ISA + /*28*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.0 /U PCI + /*29*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA PRO 2.0 ISA + /*30*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA PRO 2.0 /U ISA + /*31*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA PRO 2.0 /U PCI + /*32*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA MOBILE + /*33*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // TDK DFI3600 (same as DIVA MOBILE [32]) + /*34*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // New Media ISDN (same as DIVA PRO PCMCIA [4]) + /*35*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // BT ExLane PCI (same as DIVA PRO 2.0 PCI [7]) + /*36*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // BT ExLane ISA (same as DIVA PRO 2.0 ISA [29]) + /*37*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA 2.01 S/T ISA + /*38*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA 2.01 U ISA + /*39*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.01 S/T PCI + /*40*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.01 U PCI + /*41*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA MOBILE V.90 + /*42*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // TDK DFI3600 V.90 (same as DIVA MOBILE V.90 [39]) + /*43*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048, 0x0,0x0,0 }, // DIVA Server PRI-23M PCI + /*44*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA 2.01 S/T USB + /*45*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA CT S/T PCI + /*46*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA CT U PCI + /*47*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA CT Lite S/T PCI + /*48*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA CT Lite U PCI + /*49*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA ISDN+V.90 PC Card + /*50*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA ISDN+V.90 PCI + /*51*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA TA + /*52*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048, 0x0,0x0,0 }, // MAESTRA VOICE QUADRO PCI + /*53*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048, 0x0,0x0,0 }, // MAESTRA VOICE QUADRO PCI + /*54*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // MAESTRA VOICE PRIMARY PCI + /*55*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048, 0x0,0x0,0 }, // MAESTRA VOICE QUADRO PCI + /*56*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // MAESTRA VOICE PRIMARY PCI + /*57*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA LAN + /*58*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.02 S/T PCI + /*59*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.02 U PCI + /*60*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // Diva Server BRI-2M 2.0 PCI + /*61*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // Diva Server BRI-2F PCI + /*62*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA 2.01 S/T USB + /*63*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // Diva Server Voice BRI-2M 2.0 PCI + /*64*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 3.0 PCI + /*65*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA CT S/T PCI V2.0 + /*66*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA Mobile V.90 PC Card + /*67*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA ISDN PC Card + /*68*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA ISDN PC Card +}; +#endif /*CARDTYPE_H_WANT_RESOURCE_DATA*/ +#else /*!CARDTYPE_H_WANT_DATA*/ +extern CARD_PROPERTIES CardProperties[]; +extern CARD_RESOURCE CardResource[]; +#endif /*CARDTYPE_H_WANT_DATA*/ +/* + * all existing download files + */ +#define CARD_DSP_CNT 5 +#define CARD_PROT_CNT 9 +#define CARD_FT_UNKNOWN 0 +#define CARD_FT_B 1 +#define CARD_FT_D 2 +#define CARD_FT_S 3 +#define CARD_FT_M 4 +#define CARD_FT_NEW_DSP_COMBIFILE 5 /* File format of new DSP code (the DSP code powered by Telindus) */ +#define CARD_FILE_NONE 0 +#define CARD_B_S 1 +#define CARD_B_P 2 +#define CARD_D_K1 3 +#define CARD_D_K2 4 +#define CARD_D_H 5 +#define CARD_D_V 6 +#define CARD_D_M 7 +#define CARD_D_F 8 +#define CARD_P_S_E 9 +#define CARD_P_S_1 10 +#define CARD_P_S_B 11 +#define CARD_P_S_F 12 +#define CARD_P_S_A 13 +#define CARD_P_S_N 14 +#define CARD_P_S_5 15 +#define CARD_P_S_J 16 +#define CARD_P_SX_E 17 +#define CARD_P_SX_1 18 +#define CARD_P_SX_B 19 +#define CARD_P_SX_F 20 +#define CARD_P_SX_A 21 +#define CARD_P_SX_N 22 +#define CARD_P_SX_5 23 +#define CARD_P_SX_J 24 +#define CARD_P_SY_E 25 +#define CARD_P_SY_1 26 +#define CARD_P_SY_B 27 +#define CARD_P_SY_F 28 +#define CARD_P_SY_A 29 +#define CARD_P_SY_N 30 +#define CARD_P_SY_5 31 +#define CARD_P_SY_J 32 +#define CARD_P_SQ_E 33 +#define CARD_P_SQ_1 34 +#define CARD_P_SQ_B 35 +#define CARD_P_SQ_F 36 +#define CARD_P_SQ_A 37 +#define CARD_P_SQ_N 38 +#define CARD_P_SQ_5 39 +#define CARD_P_SQ_J 40 +#define CARD_P_P_E 41 +#define CARD_P_P_1 42 +#define CARD_P_P_B 43 +#define CARD_P_P_F 44 +#define CARD_P_P_A 45 +#define CARD_P_P_N 46 +#define CARD_P_P_5 47 +#define CARD_P_P_J 48 +#define CARD_P_M_E 49 +#define CARD_P_M_1 50 +#define CARD_P_M_B 51 +#define CARD_P_M_F 52 +#define CARD_P_M_A 53 +#define CARD_P_M_N 54 +#define CARD_P_M_5 55 +#define CARD_P_M_J 56 +#define CARD_P_S_S 57 +#define CARD_P_SX_S 58 +#define CARD_P_SY_S 59 +#define CARD_P_SQ_S 60 +#define CARD_P_P_S 61 +#define CARD_P_M_S 62 +#define CARD_D_NEW_DSP_COMBIFILE 63 +typedef struct CARD_FILES_DATA +{ + char *Name; + unsigned char Type; +} + CARD_FILES_DATA; +typedef struct CARD_FILES +{ + unsigned char Boot; + unsigned char Dsp[CARD_DSP_CNT]; + unsigned char DspTelindus; + unsigned char Prot[CARD_PROT_CNT]; +} + CARD_FILES; +#if CARDTYPE_H_WANT_DATA +#if CARDTYPE_H_WANT_FILE_DATA +CARD_FILES_DATA CardFData[] = { +// Filename Filetype + 0, CARD_FT_UNKNOWN, + "didnload.bin", CARD_FT_B, + "diprload.bin", CARD_FT_B, + "didiva.bin", CARD_FT_D, + "didivapp.bin", CARD_FT_D, + "dihscx.bin", CARD_FT_D, + "div110.bin", CARD_FT_D, + "dimodem.bin", CARD_FT_D, + "difax.bin", CARD_FT_D, + "di_etsi.bin", CARD_FT_S, + "di_1tr6.bin", CARD_FT_S, + "di_belg.bin", CARD_FT_S, + "di_franc.bin", CARD_FT_S, + "di_atel.bin", CARD_FT_S, + "di_ni.bin", CARD_FT_S, + "di_5ess.bin", CARD_FT_S, + "di_japan.bin", CARD_FT_S, + "di_etsi.sx", CARD_FT_S, + "di_1tr6.sx", CARD_FT_S, + "di_belg.sx", CARD_FT_S, + "di_franc.sx", CARD_FT_S, + "di_atel.sx", CARD_FT_S, + "di_ni.sx", CARD_FT_S, + "di_5ess.sx", CARD_FT_S, + "di_japan.sx", CARD_FT_S, + "di_etsi.sy", CARD_FT_S, + "di_1tr6.sy", CARD_FT_S, + "di_belg.sy", CARD_FT_S, + "di_franc.sy", CARD_FT_S, + "di_atel.sy", CARD_FT_S, + "di_ni.sy", CARD_FT_S, + "di_5ess.sy", CARD_FT_S, + "di_japan.sy", CARD_FT_S, + "di_etsi.sq", CARD_FT_S, + "di_1tr6.sq", CARD_FT_S, + "di_belg.sq", CARD_FT_S, + "di_franc.sq", CARD_FT_S, + "di_atel.sq", CARD_FT_S, + "di_ni.sq", CARD_FT_S, + "di_5ess.sq", CARD_FT_S, + "di_japan.sq", CARD_FT_S, + "di_etsi.p", CARD_FT_S, + "di_1tr6.p", CARD_FT_S, + "di_belg.p", CARD_FT_S, + "di_franc.p", CARD_FT_S, + "di_atel.p", CARD_FT_S, + "di_ni.p", CARD_FT_S, + "di_5ess.p", CARD_FT_S, + "di_japan.p", CARD_FT_S, + "di_etsi.sm", CARD_FT_M, + "di_1tr6.sm", CARD_FT_M, + "di_belg.sm", CARD_FT_M, + "di_franc.sm", CARD_FT_M, + "di_atel.sm", CARD_FT_M, + "di_ni.sm", CARD_FT_M, + "di_5ess.sm", CARD_FT_M, + "di_japan.sm", CARD_FT_M, + "di_swed.bin", CARD_FT_S, + "di_swed.sx", CARD_FT_S, + "di_swed.sy", CARD_FT_S, + "di_swed.sq", CARD_FT_S, + "di_swed.p", CARD_FT_S, + "di_swed.sm", CARD_FT_M, + "didspdld.bin", CARD_FT_NEW_DSP_COMBIFILE +}; +CARD_FILES CardFiles[] = +{ + { /* CARD_UNKNOWN */ + CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE + }, + { /* CARD_DIVA */ + CARD_FILE_NONE, + CARD_D_K1, CARD_D_H, CARD_D_V, CARD_FILE_NONE, CARD_D_F, + CARD_D_NEW_DSP_COMBIFILE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE + }, + { /* CARD_PRO */ + CARD_FILE_NONE, + CARD_D_K2, CARD_D_H, CARD_D_V, CARD_D_M, CARD_D_F, + CARD_D_NEW_DSP_COMBIFILE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE + }, + { /* CARD_PICO */ + CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE + }, + { /* CARD_S */ + CARD_B_S, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, + CARD_P_S_E, CARD_P_S_1, CARD_P_S_B, CARD_P_S_F, + CARD_P_S_A, CARD_P_S_N, CARD_P_S_5, CARD_P_S_J, + CARD_P_S_S + }, + { /* CARD_SX */ + CARD_B_S, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, + CARD_P_SX_E, CARD_P_SX_1, CARD_P_SX_B, CARD_P_SX_F, + CARD_P_SX_A, CARD_P_SX_N, CARD_P_SX_5, CARD_P_SX_J, + CARD_P_SX_S + }, + { /* CARD_SXN */ + CARD_B_S, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, + CARD_P_SY_E, CARD_P_SY_1, CARD_P_SY_B, CARD_P_SY_F, + CARD_P_SY_A, CARD_P_SY_N, CARD_P_SY_5, CARD_P_SY_J, + CARD_P_SY_S + }, + { /* CARD_SCOM */ + CARD_B_S, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, + CARD_P_SY_E, CARD_P_SY_1, CARD_P_SY_B, CARD_P_SY_F, + CARD_P_SY_A, CARD_P_SY_N, CARD_P_SY_5, CARD_P_SY_J, + CARD_P_SY_S + }, + { /* CARD_QUAD */ + CARD_B_S, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, + CARD_P_SQ_E, CARD_P_SQ_1, CARD_P_SQ_B, CARD_P_SQ_F, + CARD_P_SQ_A, CARD_P_SQ_N, CARD_P_SQ_5, CARD_P_SQ_J, + CARD_P_SQ_S + }, + { /* CARD_PR */ + CARD_B_P, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, + CARD_P_P_E, CARD_P_P_1, CARD_P_P_B, CARD_P_P_F, + CARD_P_P_A, CARD_P_P_N, CARD_P_P_5, CARD_P_P_J, + CARD_P_P_S + }, + { /* CARD_MAE */ + CARD_FILE_NONE, + CARD_D_K2, CARD_D_H, CARD_D_V, CARD_D_M, CARD_D_F, + CARD_D_NEW_DSP_COMBIFILE, + CARD_P_M_E, CARD_P_M_1, CARD_P_M_B, CARD_P_M_F, + CARD_P_M_A, CARD_P_M_N, CARD_P_M_5, CARD_P_M_J, + CARD_P_M_S + }, + { /* CARD_MAEQ */ /* currently not supported */ + CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE + }, + { /* CARD_MAEP */ /* currently not supported */ + CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, + CARD_FILE_NONE + } +}; +#endif /*CARDTYPE_H_WANT_FILE_DATA*/ +#else /*!CARDTYPE_H_WANT_DATA*/ +extern CARD_FILES_DATA CardFData[]; +extern CARD_FILES CardFiles[]; +#endif /*CARDTYPE_H_WANT_DATA*/ +#endif /* _CARDTYPE_H_ */ diff --git a/drivers/isdn/hardware/eicon/cp_vers.h b/drivers/isdn/hardware/eicon/cp_vers.h new file mode 100644 index 000000000..c97230c60 --- /dev/null +++ b/drivers/isdn/hardware/eicon/cp_vers.h @@ -0,0 +1,26 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +static char diva_capi_common_code_build[] = "102-28"; diff --git a/drivers/isdn/hardware/eicon/dadapter.c b/drivers/isdn/hardware/eicon/dadapter.c new file mode 100644 index 000000000..514209994 --- /dev/null +++ b/drivers/isdn/hardware/eicon/dadapter.c @@ -0,0 +1,364 @@ +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "platform.h" +#include "pc.h" +#include "debuglib.h" +#include "di_defs.h" +#include "divasync.h" +#include "dadapter.h" +/* -------------------------------------------------------------------------- + Adapter array change notification framework + -------------------------------------------------------------------------- */ +typedef struct _didd_adapter_change_notification { + didd_adapter_change_callback_t callback; + void IDI_CALL_ENTITY_T *context; +} didd_adapter_change_notification_t, \ + * IDI_CALL_ENTITY_T pdidd_adapter_change_notification_t; +#define DIVA_DIDD_MAX_NOTIFICATIONS 256 +static didd_adapter_change_notification_t \ +NotificationTable[DIVA_DIDD_MAX_NOTIFICATIONS]; +/* -------------------------------------------------------------------------- + Array to held adapter information + -------------------------------------------------------------------------- */ +static DESCRIPTOR HandleTable[NEW_MAX_DESCRIPTORS]; +static dword Adapters = 0; /* Number of adapters */ +/* -------------------------------------------------------------------------- + Shadow IDI_DIMAINT + and 'shadow' debug stuff + -------------------------------------------------------------------------- */ +static void no_printf(unsigned char *format, ...) +{ +#ifdef EBUG + va_list ap; + va_start(ap, format); + debug((format, ap)); + va_end(ap); +#endif +} + +/* ------------------------------------------------------------------------- + Portable debug Library + ------------------------------------------------------------------------- */ +#include "debuglib.c" + +static DESCRIPTOR MAdapter = {IDI_DIMAINT, /* Adapter Type */ + 0x00, /* Channels */ + 0x0000, /* Features */ + (IDI_CALL)no_printf}; +/* -------------------------------------------------------------------------- + DAdapter. Only IDI clients with buffer, that is huge enough to + get all descriptors will receive information about DAdapter + { byte type, byte channels, word features, IDI_CALL request } + -------------------------------------------------------------------------- */ +static void IDI_CALL_LINK_T diva_dadapter_request(ENTITY IDI_CALL_ENTITY_T *); +static DESCRIPTOR DAdapter = {IDI_DADAPTER, /* Adapter Type */ + 0x00, /* Channels */ + 0x0000, /* Features */ + diva_dadapter_request }; +/* -------------------------------------------------------------------------- + LOCALS + -------------------------------------------------------------------------- */ +static dword diva_register_adapter_callback(\ + didd_adapter_change_callback_t callback, + void IDI_CALL_ENTITY_T *context); +static void diva_remove_adapter_callback(dword handle); +static void diva_notify_adapter_change(DESCRIPTOR *d, int removal); +static diva_os_spin_lock_t didd_spin; +/* -------------------------------------------------------------------------- + Should be called as first step, after driver init + -------------------------------------------------------------------------- */ +void diva_didd_load_time_init(void) { + memset(&HandleTable[0], 0x00, sizeof(HandleTable)); + memset(&NotificationTable[0], 0x00, sizeof(NotificationTable)); + diva_os_initialize_spin_lock(&didd_spin, "didd"); +} +/* -------------------------------------------------------------------------- + Should be called as last step, if driver does unload + -------------------------------------------------------------------------- */ +void diva_didd_load_time_finit(void) { + diva_os_destroy_spin_lock(&didd_spin, "didd"); +} +/* -------------------------------------------------------------------------- + Called in order to register new adapter in adapter array + return adapter handle (> 0) on success + return -1 adapter array overflow + -------------------------------------------------------------------------- */ +static int diva_didd_add_descriptor(DESCRIPTOR *d) { + diva_os_spin_lock_magic_t irql; + int i; + if (d->type == IDI_DIMAINT) { + if (d->request) { + MAdapter.request = d->request; + dprintf = (DIVA_DI_PRINTF)d->request; + diva_notify_adapter_change(&MAdapter, 0); /* Inserted */ + DBG_TRC(("DIMAINT registered, dprintf=%08x", d->request)) + } else { + DBG_TRC(("DIMAINT removed")) + diva_notify_adapter_change(&MAdapter, 1); /* About to remove */ + MAdapter.request = (IDI_CALL)no_printf; + dprintf = no_printf; + } + return (NEW_MAX_DESCRIPTORS); + } + for (i = 0; i < NEW_MAX_DESCRIPTORS; i++) { + diva_os_enter_spin_lock(&didd_spin, &irql, "didd_add"); + if (HandleTable[i].type == 0) { + memcpy(&HandleTable[i], d, sizeof(*d)); + Adapters++; + diva_os_leave_spin_lock(&didd_spin, &irql, "didd_add"); + diva_notify_adapter_change(d, 0); /* we have new adapter */ + DBG_TRC(("Add adapter[%d], request=%08x", (i + 1), d->request)) + return (i + 1); + } + diva_os_leave_spin_lock(&didd_spin, &irql, "didd_add"); + } + DBG_ERR(("Can't add adapter, out of resources")) + return (-1); +} +/* -------------------------------------------------------------------------- + Called in order to remove one registered adapter from array + return adapter handle (> 0) on success + return 0 on success + -------------------------------------------------------------------------- */ +static int diva_didd_remove_descriptor(IDI_CALL request) { + diva_os_spin_lock_magic_t irql; + int i; + if (request == MAdapter.request) { + DBG_TRC(("DIMAINT removed")) + dprintf = no_printf; + diva_notify_adapter_change(&MAdapter, 1); /* About to remove */ + MAdapter.request = (IDI_CALL)no_printf; + return (0); + } + for (i = 0; (Adapters && (i < NEW_MAX_DESCRIPTORS)); i++) { + if (HandleTable[i].request == request) { + diva_notify_adapter_change(&HandleTable[i], 1); /* About to remove */ + diva_os_enter_spin_lock(&didd_spin, &irql, "didd_rm"); + memset(&HandleTable[i], 0x00, sizeof(HandleTable[0])); + Adapters--; + diva_os_leave_spin_lock(&didd_spin, &irql, "didd_rm"); + DBG_TRC(("Remove adapter[%d], request=%08x", (i + 1), request)) + return (0); + } + } + DBG_ERR(("Invalid request=%08x, can't remove adapter", request)) + return (-1); +} +/* -------------------------------------------------------------------------- + Read adapter array + return 1 if not enough space to save all available adapters + -------------------------------------------------------------------------- */ +static int diva_didd_read_adapter_array(DESCRIPTOR *buffer, int length) { + diva_os_spin_lock_magic_t irql; + int src, dst; + memset(buffer, 0x00, length); + length /= sizeof(DESCRIPTOR); + DBG_TRC(("DIDD_Read, space = %d, Adapters = %d", length, Adapters + 2)) + + diva_os_enter_spin_lock(&didd_spin, &irql, "didd_read"); + for (src = 0, dst = 0; + (Adapters && (src < NEW_MAX_DESCRIPTORS) && (dst < length)); + src++) { + if (HandleTable[src].type) { + memcpy(&buffer[dst], &HandleTable[src], sizeof(DESCRIPTOR)); + dst++; + } + } + diva_os_leave_spin_lock(&didd_spin, &irql, "didd_read"); + if (dst < length) { + memcpy(&buffer[dst], &MAdapter, sizeof(DESCRIPTOR)); + dst++; + } else { + DBG_ERR(("Can't write DIMAINT. Array too small")) + } + if (dst < length) { + memcpy(&buffer[dst], &DAdapter, sizeof(DESCRIPTOR)); + dst++; + } else { + DBG_ERR(("Can't write DADAPTER. Array too small")) + } + DBG_TRC(("Read %d adapters", dst)) + return (dst == length); +} +/* -------------------------------------------------------------------------- + DAdapter request function. + This function does process only synchronous requests, and is used + for reception/registration of new interfaces + -------------------------------------------------------------------------- */ +static void IDI_CALL_LINK_T diva_dadapter_request( \ + ENTITY IDI_CALL_ENTITY_T *e) { + IDI_SYNC_REQ *syncReq = (IDI_SYNC_REQ *)e; + if (e->Req) { /* We do not process it, also return error */ + e->Rc = OUT_OF_RESOURCES; + DBG_ERR(("Can't process async request, Req=%02x", e->Req)) + return; + } + /* + So, we process sync request + */ + switch (e->Rc) { + case IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY: { + diva_didd_adapter_notify_t *pinfo = &syncReq->didd_notify.info; + pinfo->handle = diva_register_adapter_callback( \ + (didd_adapter_change_callback_t)pinfo->callback, + (void IDI_CALL_ENTITY_T *)pinfo->context); + e->Rc = 0xff; + } break; + case IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY: { + diva_didd_adapter_notify_t *pinfo = &syncReq->didd_notify.info; + diva_remove_adapter_callback(pinfo->handle); + e->Rc = 0xff; + } break; + case IDI_SYNC_REQ_DIDD_ADD_ADAPTER: { + diva_didd_add_adapter_t *pinfo = &syncReq->didd_add_adapter.info; + if (diva_didd_add_descriptor((DESCRIPTOR *)pinfo->descriptor) < 0) { + e->Rc = OUT_OF_RESOURCES; + } else { + e->Rc = 0xff; + } + } break; + case IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER: { + diva_didd_remove_adapter_t *pinfo = &syncReq->didd_remove_adapter.info; + if (diva_didd_remove_descriptor((IDI_CALL)pinfo->p_request) < 0) { + e->Rc = OUT_OF_RESOURCES; + } else { + e->Rc = 0xff; + } + } break; + case IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY: { + diva_didd_read_adapter_array_t *pinfo =\ + &syncReq->didd_read_adapter_array.info; + if (diva_didd_read_adapter_array((DESCRIPTOR *)pinfo->buffer, + (int)pinfo->length)) { + e->Rc = OUT_OF_RESOURCES; + } else { + e->Rc = 0xff; + } + } break; + default: + DBG_ERR(("Can't process sync request, Req=%02x", e->Rc)) + e->Rc = OUT_OF_RESOURCES; + } +} +/* -------------------------------------------------------------------------- + IDI client does register his notification function + -------------------------------------------------------------------------- */ +static dword diva_register_adapter_callback( \ + didd_adapter_change_callback_t callback, + void IDI_CALL_ENTITY_T *context) { + diva_os_spin_lock_magic_t irql; + dword i; + + for (i = 0; i < DIVA_DIDD_MAX_NOTIFICATIONS; i++) { + diva_os_enter_spin_lock(&didd_spin, &irql, "didd_nfy_add"); + if (!NotificationTable[i].callback) { + NotificationTable[i].callback = callback; + NotificationTable[i].context = context; + diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy_add"); + DBG_TRC(("Register adapter notification[%d]=%08x", i + 1, callback)) + return (i + 1); + } + diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy_add"); + } + DBG_ERR(("Can't register adapter notification, overflow")) + return (0); +} +/* -------------------------------------------------------------------------- + IDI client does register his notification function + -------------------------------------------------------------------------- */ +static void diva_remove_adapter_callback(dword handle) { + diva_os_spin_lock_magic_t irql; + if (handle && ((--handle) < DIVA_DIDD_MAX_NOTIFICATIONS)) { + diva_os_enter_spin_lock(&didd_spin, &irql, "didd_nfy_rm"); + NotificationTable[handle].callback = NULL; + NotificationTable[handle].context = NULL; + diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy_rm"); + DBG_TRC(("Remove adapter notification[%d]", (int)(handle + 1))) + return; + } + DBG_ERR(("Can't remove adapter notification, handle=%d", handle)) + } +/* -------------------------------------------------------------------------- + Notify all client about adapter array change + Does suppose following behavior in the client side: + Step 1: Redister Notification + Step 2: Read Adapter Array + -------------------------------------------------------------------------- */ +static void diva_notify_adapter_change(DESCRIPTOR *d, int removal) { + int i, do_notify; + didd_adapter_change_notification_t nfy; + diva_os_spin_lock_magic_t irql; + for (i = 0; i < DIVA_DIDD_MAX_NOTIFICATIONS; i++) { + do_notify = 0; + diva_os_enter_spin_lock(&didd_spin, &irql, "didd_nfy"); + if (NotificationTable[i].callback) { + memcpy(&nfy, &NotificationTable[i], sizeof(nfy)); + do_notify = 1; + } + diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy"); + if (do_notify) { + (*(nfy.callback))(nfy.context, d, removal); + } + } +} +/* -------------------------------------------------------------------------- + For all systems, that are linked by Kernel Mode Linker this is ONLY one + function thet should be exported by this device driver + IDI clients should look for IDI_DADAPTER, and use request function + of this adapter (sync request) in order to receive appropriate services: + - add new adapter + - remove existing adapter + - add adapter array notification + - remove adapter array notification + (read adapter is redundant in this case) + INPUT: + buffer - pointer to buffer that will receive adapter array + length - length (in bytes) of space in buffer + OUTPUT: + Adapter array will be written to memory described by 'buffer' + If the last adapter seen in the returned adapter array is + IDI_DADAPTER or if last adapter in array does have type '0', then + it was enougth space in buffer to accommodate all available + adapter descriptors + *NOTE 1 (debug interface): + The IDI adapter of type 'IDI_DIMAINT' does register as 'request' + famous 'dprintf' function (of type DI_PRINTF, please look + include/debuglib.c and include/debuglib.h) for details. + So dprintf is not exported from module debug module directly, + instead of this IDI_DIMAINT is registered. + Module load order will receive in this case: + 1. DIDD (this file) + 2. DIMAINT does load and register 'IDI_DIMAINT', at this step + DIDD should be able to get 'dprintf', save it, and + register with DIDD by means of 'dprintf' function. + 3. any other driver is loaded and is able to access adapter array + and debug interface + This approach does allow to load/unload debug interface on demand, + and save memory, it it is necessary. + -------------------------------------------------------------------------- */ +void IDI_CALL_LINK_T DIVA_DIDD_Read(void IDI_CALL_ENTITY_T *buffer, + int length) { + diva_didd_read_adapter_array(buffer, length); +} diff --git a/drivers/isdn/hardware/eicon/dadapter.h b/drivers/isdn/hardware/eicon/dadapter.h new file mode 100644 index 000000000..5540f46a5 --- /dev/null +++ b/drivers/isdn/hardware/eicon/dadapter.h @@ -0,0 +1,34 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_DIDD_DADAPTER_INC__ +#define __DIVA_DIDD_DADAPTER_INC__ + +void diva_didd_load_time_init(void); +void diva_didd_load_time_finit(void); + +#define NEW_MAX_DESCRIPTORS 64 + +#endif diff --git a/drivers/isdn/hardware/eicon/debug.c b/drivers/isdn/hardware/eicon/debug.c new file mode 100644 index 000000000..301788115 --- /dev/null +++ b/drivers/isdn/hardware/eicon/debug.c @@ -0,0 +1,2128 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "platform.h" +#include "pc.h" +#include "di_defs.h" +#include "debug_if.h" +#include "divasync.h" +#include "kst_ifc.h" +#include "maintidi.h" +#include "man_defs.h" + +/* + LOCALS +*/ +#define DBG_MAGIC (0x47114711L) + +static void DI_register(void *arg); +static void DI_deregister(pDbgHandle hDbg); +static void DI_format(int do_lock, word id, int type, char *format, va_list argument_list); +static void DI_format_locked(word id, int type, char *format, va_list argument_list); +static void DI_format_old(word id, char *format, va_list ap) { } +static void DiProcessEventLog(unsigned short id, unsigned long msgID, va_list ap) { } +static void single_p(byte *P, word *PLength, byte Id); +static void diva_maint_xdi_cb(ENTITY *e); +static word SuperTraceCreateReadReq(byte *P, const char *path); +static int diva_mnt_cmp_nmbr(const char *nmbr); +static void diva_free_dma_descriptor(IDI_CALL request, int nr); +static int diva_get_dma_descriptor(IDI_CALL request, dword *dma_magic); +void diva_mnt_internal_dprintf(dword drv_id, dword type, char *p, ...); + +static dword MaxDumpSize = 256; +static dword MaxXlogSize = 2 + 128; +static char TraceFilter[DIVA_MAX_SELECTIVE_FILTER_LENGTH + 1]; +static int TraceFilterIdent = -1; +static int TraceFilterChannel = -1; + +typedef struct _diva_maint_client { + dword sec; + dword usec; + pDbgHandle hDbg; + char drvName[128]; + dword dbgMask; + dword last_dbgMask; + IDI_CALL request; + _DbgHandle_ Dbg; + int logical; + int channels; + diva_strace_library_interface_t *pIdiLib; + BUFFERS XData; + char xbuffer[2048 + 512]; + byte *pmem; + int request_pending; + int dma_handle; +} diva_maint_client_t; +static diva_maint_client_t clients[MAX_DESCRIPTORS]; + +static void diva_change_management_debug_mask(diva_maint_client_t *pC, dword old_mask); + +static void diva_maint_error(void *user_context, + diva_strace_library_interface_t *hLib, + int Adapter, + int error, + const char *file, + int line); +static void diva_maint_state_change_notify(void *user_context, + diva_strace_library_interface_t *hLib, + int Adapter, + diva_trace_line_state_t *channel, + int notify_subject); +static void diva_maint_trace_notify(void *user_context, + diva_strace_library_interface_t *hLib, + int Adapter, + void *xlog_buffer, + int length); + + + +typedef struct MSG_QUEUE { + dword Size; /* total size of queue (constant) */ + byte *Base; /* lowest address (constant) */ + byte *High; /* Base + Size (constant) */ + byte *Head; /* first message in queue (if any) */ + byte *Tail; /* first free position */ + byte *Wrap; /* current wraparound position */ + dword Count; /* current no of bytes in queue */ +} MSG_QUEUE; + +typedef struct MSG_HEAD { + volatile dword Size; /* size of data following MSG_HEAD */ +#define MSG_INCOMPLETE 0x8000 /* ored to Size until queueCompleteMsg */ +} MSG_HEAD; + +#define queueCompleteMsg(p) do { ((MSG_HEAD *)p - 1)->Size &= ~MSG_INCOMPLETE; } while (0) +#define queueCount(q) ((q)->Count) +#define MSG_NEED(size) \ + ((sizeof(MSG_HEAD) + size + sizeof(dword) - 1) & ~(sizeof(dword) - 1)) + +static void queueInit(MSG_QUEUE *Q, byte *Buffer, dword sizeBuffer) { + Q->Size = sizeBuffer; + Q->Base = Q->Head = Q->Tail = Buffer; + Q->High = Buffer + sizeBuffer; + Q->Wrap = NULL; + Q->Count = 0; +} + +static byte *queueAllocMsg(MSG_QUEUE *Q, word size) { + /* Allocate 'size' bytes at tail of queue which will be filled later + * directly with callers own message header info and/or message. + * An 'alloced' message is marked incomplete by oring the 'Size' field + * with MSG_INCOMPLETE. + * This must be reset via queueCompleteMsg() after the message is filled. + * As long as a message is marked incomplete queuePeekMsg() will return + * a 'queue empty' condition when it reaches such a message. */ + + MSG_HEAD *Msg; + word need = MSG_NEED(size); + + if (Q->Tail == Q->Head) { + if (Q->Wrap || need > Q->Size) { + return NULL; /* full */ + } + goto alloc; /* empty */ + } + + if (Q->Tail > Q->Head) { + if (Q->Tail + need <= Q->High) goto alloc; /* append */ + if (Q->Base + need > Q->Head) { + return NULL; /* too much */ + } + /* wraparound the queue (but not the message) */ + Q->Wrap = Q->Tail; + Q->Tail = Q->Base; + goto alloc; + } + + if (Q->Tail + need > Q->Head) { + return NULL; /* too much */ + } + +alloc: + Msg = (MSG_HEAD *)Q->Tail; + + Msg->Size = size | MSG_INCOMPLETE; + + Q->Tail += need; + Q->Count += size; + + + + return ((byte *)(Msg + 1)); +} + +static void queueFreeMsg(MSG_QUEUE *Q) { +/* Free the message at head of queue */ + + word size = ((MSG_HEAD *)Q->Head)->Size & ~MSG_INCOMPLETE; + + Q->Head += MSG_NEED(size); + Q->Count -= size; + + if (Q->Wrap) { + if (Q->Head >= Q->Wrap) { + Q->Head = Q->Base; + Q->Wrap = NULL; + } + } else if (Q->Head >= Q->Tail) { + Q->Head = Q->Tail = Q->Base; + } +} + +static byte *queuePeekMsg(MSG_QUEUE *Q, word *size) { + /* Show the first valid message in queue BUT DON'T free the message. + * After looking on the message contents it can be freed queueFreeMsg() + * or simply remain in message queue. */ + + MSG_HEAD *Msg = (MSG_HEAD *)Q->Head; + + if (((byte *)Msg == Q->Tail && !Q->Wrap) || + (Msg->Size & MSG_INCOMPLETE)) { + return NULL; + } else { + *size = Msg->Size; + return ((byte *)(Msg + 1)); + } +} + +/* + Message queue header +*/ +static MSG_QUEUE *dbg_queue; +static byte *dbg_base; +static int external_dbg_queue; +static diva_os_spin_lock_t dbg_q_lock; +static diva_os_spin_lock_t dbg_adapter_lock; +static int dbg_q_busy; +static volatile dword dbg_sequence; + +/* + INTERFACE: + Initialize run time queue structures. + base: base of the message queue + length: length of the message queue + do_init: perfor queue reset + + return: zero on success, -1 on error +*/ +int diva_maint_init(byte *base, unsigned long length, int do_init) { + if (dbg_queue || (!base) || (length < (4096 * 4))) { + return (-1); + } + + TraceFilter[0] = 0; + TraceFilterIdent = -1; + TraceFilterChannel = -1; + + dbg_base = base; + + *(dword *)base = (dword)DBG_MAGIC; /* Store Magic */ + base += sizeof(dword); + length -= sizeof(dword); + + *(dword *)base = 2048; /* Extension Field Length */ + base += sizeof(dword); + length -= sizeof(dword); + + strcpy(base, "KERNEL MODE BUFFER\n"); + base += 2048; + length -= 2048; + + *(dword *)base = 0; /* Terminate extension */ + base += sizeof(dword); + length -= sizeof(dword); + + *(void **)base = (void *)(base + sizeof(void *)); /* Store Base */ + base += sizeof(void *); + length -= sizeof(void *); + + dbg_queue = (MSG_QUEUE *)base; + queueInit(dbg_queue, base + sizeof(MSG_QUEUE), length - sizeof(MSG_QUEUE) - 512); + external_dbg_queue = 0; + + if (!do_init) { + external_dbg_queue = 1; /* memory was located on the external device */ + } + + + if (diva_os_initialize_spin_lock(&dbg_q_lock, "dbg_init")) { + dbg_queue = NULL; + dbg_base = NULL; + external_dbg_queue = 0; + return (-1); + } + + if (diva_os_initialize_spin_lock(&dbg_adapter_lock, "dbg_init")) { + diva_os_destroy_spin_lock(&dbg_q_lock, "dbg_init"); + dbg_queue = NULL; + dbg_base = NULL; + external_dbg_queue = 0; + return (-1); + } + + return (0); +} + +/* + INTERFACE: + Finit at unload time + return address of internal queue or zero if queue + was external +*/ +void *diva_maint_finit(void) { + void *ret = (void *)dbg_base; + int i; + + dbg_queue = NULL; + dbg_base = NULL; + + if (ret) { + diva_os_destroy_spin_lock(&dbg_q_lock, "dbg_finit"); + diva_os_destroy_spin_lock(&dbg_adapter_lock, "dbg_finit"); + } + + if (external_dbg_queue) { + ret = NULL; + } + external_dbg_queue = 0; + + for (i = 1; i < ARRAY_SIZE(clients); i++) { + if (clients[i].pmem) { + diva_os_free(0, clients[i].pmem); + } + } + + return (ret); +} + +/* + INTERFACE: + Return amount of messages in debug queue +*/ +dword diva_dbg_q_length(void) { + return (dbg_queue ? queueCount(dbg_queue) : 0); +} + +/* + INTERFACE: + Lock message queue and return the pointer to the first + entry. +*/ +diva_dbg_entry_head_t *diva_maint_get_message(word *size, + diva_os_spin_lock_magic_t *old_irql) { + diva_dbg_entry_head_t *pmsg = NULL; + + diva_os_enter_spin_lock(&dbg_q_lock, old_irql, "read"); + if (dbg_q_busy) { + diva_os_leave_spin_lock(&dbg_q_lock, old_irql, "read_busy"); + return NULL; + } + dbg_q_busy = 1; + + if (!(pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, size))) { + dbg_q_busy = 0; + diva_os_leave_spin_lock(&dbg_q_lock, old_irql, "read_empty"); + } + + return (pmsg); +} + +/* + INTERFACE: + acknowledge last message and unlock queue +*/ +void diva_maint_ack_message(int do_release, + diva_os_spin_lock_magic_t *old_irql) { + if (!dbg_q_busy) { + return; + } + if (do_release) { + queueFreeMsg(dbg_queue); + } + dbg_q_busy = 0; + diva_os_leave_spin_lock(&dbg_q_lock, old_irql, "read_ack"); +} + + +/* + INTERFACE: + PRT COMP function used to register + with MAINT adapter or log in compatibility + mode in case older driver version is connected too +*/ +void diva_maint_prtComp(char *format, ...) { + void *hDbg; + va_list ap; + + if (!format) + return; + + va_start(ap, format); + + /* + register to new log driver functions + */ + if ((format[0] == 0) && ((unsigned char)format[1] == 255)) { + hDbg = va_arg(ap, void *); /* ptr to DbgHandle */ + DI_register(hDbg); + } + + va_end(ap); +} + +static void DI_register(void *arg) { + diva_os_spin_lock_magic_t old_irql; + dword sec, usec; + pDbgHandle hDbg; + int id, free_id = -1, best_id = 0; + + diva_os_get_time(&sec, &usec); + + hDbg = (pDbgHandle)arg; + /* + Check for bad args, specially for the old obsolete debug handle + */ + if ((hDbg == NULL) || + ((hDbg->id == 0) && (((_OldDbgHandle_ *)hDbg)->id == -1)) || + (hDbg->Registered != 0)) { + return; + } + + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "register"); + + for (id = 1; id < ARRAY_SIZE(clients); id++) { + if (clients[id].hDbg == hDbg) { + /* + driver already registered + */ + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register"); + return; + } + if (clients[id].hDbg) { /* slot is busy */ + continue; + } + free_id = id; + if (!strcmp(clients[id].drvName, hDbg->drvName)) { + /* + This driver was already registered with this name + and slot is still free - reuse it + */ + best_id = 1; + break; + } + if (!clients[id].hDbg) { /* slot is busy */ + break; + } + } + + if (free_id != -1) { + diva_dbg_entry_head_t *pmsg = NULL; + int len; + char tmp[256]; + word size; + + /* + Register new driver with id == free_id + */ + clients[free_id].hDbg = hDbg; + clients[free_id].sec = sec; + clients[free_id].usec = usec; + strcpy(clients[free_id].drvName, hDbg->drvName); + + clients[free_id].dbgMask = hDbg->dbgMask; + if (best_id) { + hDbg->dbgMask |= clients[free_id].last_dbgMask; + } else { + clients[free_id].last_dbgMask = 0; + } + + hDbg->Registered = DBG_HANDLE_REG_NEW; + hDbg->id = (byte)free_id; + hDbg->dbg_end = DI_deregister; + hDbg->dbg_prt = DI_format_locked; + hDbg->dbg_ev = DiProcessEventLog; + hDbg->dbg_irq = DI_format_locked; + if (hDbg->Version > 0) { + hDbg->dbg_old = DI_format_old; + } + hDbg->next = (pDbgHandle)DBG_MAGIC; + + /* + Log driver register, MAINT driver ID is '0' + */ + len = sprintf(tmp, "DIMAINT - drv # %d = '%s' registered", + free_id, hDbg->drvName); + + while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue, + (word)(len + 1 + sizeof(*pmsg))))) { + if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) { + queueFreeMsg(dbg_queue); + } else { + break; + } + } + + if (pmsg) { + pmsg->sequence = dbg_sequence++; + pmsg->time_sec = sec; + pmsg->time_usec = usec; + pmsg->facility = MSG_TYPE_STRING; + pmsg->dli = DLI_REG; + pmsg->drv_id = 0; /* id 0 - DIMAINT */ + pmsg->di_cpu = 0; + pmsg->data_length = len + 1; + + memcpy(&pmsg[1], tmp, len + 1); + queueCompleteMsg(pmsg); + diva_maint_wakeup_read(); + } + } + + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register"); +} + +static void DI_deregister(pDbgHandle hDbg) { + diva_os_spin_lock_magic_t old_irql, old_irql1; + dword sec, usec; + int i; + word size; + byte *pmem = NULL; + + diva_os_get_time(&sec, &usec); + + diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "read"); + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "read"); + + for (i = 1; i < ARRAY_SIZE(clients); i++) { + if (clients[i].hDbg == hDbg) { + diva_dbg_entry_head_t *pmsg; + char tmp[256]; + int len; + + clients[i].hDbg = NULL; + + hDbg->id = -1; + hDbg->dbgMask = 0; + hDbg->dbg_end = NULL; + hDbg->dbg_prt = NULL; + hDbg->dbg_irq = NULL; + if (hDbg->Version > 0) + hDbg->dbg_old = NULL; + hDbg->Registered = 0; + hDbg->next = NULL; + + if (clients[i].pIdiLib) { + (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib); + clients[i].pIdiLib = NULL; + + pmem = clients[i].pmem; + clients[i].pmem = NULL; + } + + /* + Log driver register, MAINT driver ID is '0' + */ + len = sprintf(tmp, "DIMAINT - drv # %d = '%s' de-registered", + i, hDbg->drvName); + + while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue, + (word)(len + 1 + sizeof(*pmsg))))) { + if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) { + queueFreeMsg(dbg_queue); + } else { + break; + } + } + + if (pmsg) { + pmsg->sequence = dbg_sequence++; + pmsg->time_sec = sec; + pmsg->time_usec = usec; + pmsg->facility = MSG_TYPE_STRING; + pmsg->dli = DLI_REG; + pmsg->drv_id = 0; /* id 0 - DIMAINT */ + pmsg->di_cpu = 0; + pmsg->data_length = len + 1; + + memcpy(&pmsg[1], tmp, len + 1); + queueCompleteMsg(pmsg); + diva_maint_wakeup_read(); + } + + break; + } + } + + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "read_ack"); + diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "read_ack"); + + if (pmem) { + diva_os_free(0, pmem); + } +} + +static void DI_format_locked(unsigned short id, + int type, + char *format, + va_list argument_list) { + DI_format(1, id, type, format, argument_list); +} + +static void DI_format(int do_lock, + unsigned short id, + int type, + char *format, + va_list ap) { + diva_os_spin_lock_magic_t old_irql; + dword sec, usec; + diva_dbg_entry_head_t *pmsg = NULL; + dword length; + word size; + static char fmtBuf[MSG_FRAME_MAX_SIZE + sizeof(*pmsg) + 1]; + char *data; + unsigned short code; + + if (diva_os_in_irq()) { + dbg_sequence++; + return; + } + + if ((!format) || + ((TraceFilter[0] != 0) && ((TraceFilterIdent < 0) || (TraceFilterChannel < 0)))) { + return; + } + + + + diva_os_get_time(&sec, &usec); + + if (do_lock) { + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "format"); + } + + switch (type) { + case DLI_MXLOG: + case DLI_BLK: + case DLI_SEND: + case DLI_RECV: + if (!(length = va_arg(ap, unsigned long))) { + break; + } + if (length > MaxDumpSize) { + length = MaxDumpSize; + } + while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue, + (word)length + sizeof(*pmsg)))) { + if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) { + queueFreeMsg(dbg_queue); + } else { + break; + } + } + if (pmsg) { + memcpy(&pmsg[1], format, length); + pmsg->sequence = dbg_sequence++; + pmsg->time_sec = sec; + pmsg->time_usec = usec; + pmsg->facility = MSG_TYPE_BINARY; + pmsg->dli = type; /* DLI_XXX */ + pmsg->drv_id = id; /* driver MAINT id */ + pmsg->di_cpu = 0; + pmsg->data_length = length; + queueCompleteMsg(pmsg); + } + break; + + case DLI_XLOG: { + byte *p; + data = va_arg(ap, char *); + code = (unsigned short)va_arg(ap, unsigned int); + length = (unsigned long)va_arg(ap, unsigned int); + + if (length > MaxXlogSize) + length = MaxXlogSize; + + while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue, + (word)length + sizeof(*pmsg) + 2))) { + if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) { + queueFreeMsg(dbg_queue); + } else { + break; + } + } + if (pmsg) { + p = (byte *)&pmsg[1]; + p[0] = (char)(code); + p[1] = (char)(code >> 8); + if (data && length) { + memcpy(&p[2], &data[0], length); + } + length += 2; + + pmsg->sequence = dbg_sequence++; + pmsg->time_sec = sec; + pmsg->time_usec = usec; + pmsg->facility = MSG_TYPE_BINARY; + pmsg->dli = type; /* DLI_XXX */ + pmsg->drv_id = id; /* driver MAINT id */ + pmsg->di_cpu = 0; + pmsg->data_length = length; + queueCompleteMsg(pmsg); + } + } break; + + case DLI_LOG: + case DLI_FTL: + case DLI_ERR: + case DLI_TRC: + case DLI_REG: + case DLI_MEM: + case DLI_SPL: + case DLI_IRP: + case DLI_TIM: + case DLI_TAPI: + case DLI_NDIS: + case DLI_CONN: + case DLI_STAT: + case DLI_PRV0: + case DLI_PRV1: + case DLI_PRV2: + case DLI_PRV3: + if ((length = (unsigned long)vsprintf(&fmtBuf[0], format, ap)) > 0) { + length += (sizeof(*pmsg) + 1); + + while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue, + (word)length))) { + if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) { + queueFreeMsg(dbg_queue); + } else { + break; + } + } + + pmsg->sequence = dbg_sequence++; + pmsg->time_sec = sec; + pmsg->time_usec = usec; + pmsg->facility = MSG_TYPE_STRING; + pmsg->dli = type; /* DLI_XXX */ + pmsg->drv_id = id; /* driver MAINT id */ + pmsg->di_cpu = 0; + pmsg->data_length = length - sizeof(*pmsg); + + memcpy(&pmsg[1], fmtBuf, pmsg->data_length); + queueCompleteMsg(pmsg); + } + break; + + } /* switch type */ + + + if (queueCount(dbg_queue)) { + diva_maint_wakeup_read(); + } + + if (do_lock) { + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "format"); + } +} + +/* + Write driver ID and driver revision to callers buffer +*/ +int diva_get_driver_info(dword id, byte *data, int data_length) { + diva_os_spin_lock_magic_t old_irql; + byte *p = data; + int to_copy; + + if (!data || !id || (data_length < 17) || + (id >= ARRAY_SIZE(clients))) { + return (-1); + } + + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "driver info"); + + if (clients[id].hDbg) { + *p++ = 1; + *p++ = (byte)clients[id].sec; /* save seconds */ + *p++ = (byte)(clients[id].sec >> 8); + *p++ = (byte)(clients[id].sec >> 16); + *p++ = (byte)(clients[id].sec >> 24); + + *p++ = (byte)(clients[id].usec / 1000); /* save mseconds */ + *p++ = (byte)((clients[id].usec / 1000) >> 8); + *p++ = (byte)((clients[id].usec / 1000) >> 16); + *p++ = (byte)((clients[id].usec / 1000) >> 24); + + data_length -= 9; + + if ((to_copy = min(strlen(clients[id].drvName), (size_t)(data_length - 1)))) { + memcpy(p, clients[id].drvName, to_copy); + p += to_copy; + data_length -= to_copy; + if ((data_length >= 4) && clients[id].hDbg->drvTag[0]) { + *p++ = '('; + data_length -= 1; + if ((to_copy = min(strlen(clients[id].hDbg->drvTag), (size_t)(data_length - 2)))) { + memcpy(p, clients[id].hDbg->drvTag, to_copy); + p += to_copy; + data_length -= to_copy; + if (data_length >= 2) { + *p++ = ')'; + data_length--; + } + } + } + } + } + *p++ = 0; + + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "driver info"); + + return (p - data); +} + +int diva_get_driver_dbg_mask(dword id, byte *data) { + diva_os_spin_lock_magic_t old_irql; + int ret = -1; + + if (!data || !id || (id >= ARRAY_SIZE(clients))) { + return (-1); + } + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "driver info"); + + if (clients[id].hDbg) { + ret = 4; + *data++ = (byte)(clients[id].hDbg->dbgMask); + *data++ = (byte)(clients[id].hDbg->dbgMask >> 8); + *data++ = (byte)(clients[id].hDbg->dbgMask >> 16); + *data++ = (byte)(clients[id].hDbg->dbgMask >> 24); + } + + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "driver info"); + + return (ret); +} + +int diva_set_driver_dbg_mask(dword id, dword mask) { + diva_os_spin_lock_magic_t old_irql, old_irql1; + int ret = -1; + + + if (!id || (id >= ARRAY_SIZE(clients))) { + return (-1); + } + + diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "dbg mask"); + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "dbg mask"); + + if (clients[id].hDbg) { + dword old_mask = clients[id].hDbg->dbgMask; + mask &= 0x7fffffff; + clients[id].hDbg->dbgMask = mask; + clients[id].last_dbgMask = (clients[id].hDbg->dbgMask | clients[id].dbgMask); + ret = 4; + diva_change_management_debug_mask(&clients[id], old_mask); + } + + + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "dbg mask"); + + if (clients[id].request_pending) { + clients[id].request_pending = 0; + (*(clients[id].request))((ENTITY *)(*(clients[id].pIdiLib->DivaSTraceGetHandle))(clients[id].pIdiLib->hLib)); + } + + diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "dbg mask"); + + return (ret); +} + +static int diva_get_idi_adapter_info(IDI_CALL request, dword *serial, dword *logical) { + IDI_SYNC_REQ sync_req; + + sync_req.xdi_logical_adapter_number.Req = 0; + sync_req.xdi_logical_adapter_number.Rc = IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER; + (*request)((ENTITY *)&sync_req); + *logical = sync_req.xdi_logical_adapter_number.info.logical_adapter_number; + + sync_req.GetSerial.Req = 0; + sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL; + sync_req.GetSerial.serial = 0; + (*request)((ENTITY *)&sync_req); + *serial = sync_req.GetSerial.serial; + + return (0); +} + +/* + Register XDI adapter as MAINT compatible driver +*/ +void diva_mnt_add_xdi_adapter(const DESCRIPTOR *d) { + diva_os_spin_lock_magic_t old_irql, old_irql1; + dword sec, usec, logical, serial, org_mask; + int id, free_id = -1; + char tmp[128]; + diva_dbg_entry_head_t *pmsg = NULL; + int len; + word size; + byte *pmem; + + diva_os_get_time(&sec, &usec); + diva_get_idi_adapter_info(d->request, &serial, &logical); + if (serial & 0xff000000) { + sprintf(tmp, "ADAPTER:%d SN:%u-%d", + (int)logical, + serial & 0x00ffffff, + (byte)(((serial & 0xff000000) >> 24) + 1)); + } else { + sprintf(tmp, "ADAPTER:%d SN:%u", (int)logical, serial); + } + + if (!(pmem = diva_os_malloc(0, DivaSTraceGetMemotyRequirement(d->channels)))) { + return; + } + memset(pmem, 0x00, DivaSTraceGetMemotyRequirement(d->channels)); + + diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "register"); + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "register"); + + for (id = 1; id < ARRAY_SIZE(clients); id++) { + if (clients[id].hDbg && (clients[id].request == d->request)) { + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register"); + diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "register"); + diva_os_free(0, pmem); + return; + } + if (clients[id].hDbg) { /* slot is busy */ + continue; + } + if (free_id < 0) { + free_id = id; + } + if (!strcmp(clients[id].drvName, tmp)) { + /* + This driver was already registered with this name + and slot is still free - reuse it + */ + free_id = id; + break; + } + } + + if (free_id < 0) { + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register"); + diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "register"); + diva_os_free(0, pmem); + return; + } + + id = free_id; + clients[id].request = d->request; + clients[id].request_pending = 0; + clients[id].hDbg = &clients[id].Dbg; + clients[id].sec = sec; + clients[id].usec = usec; + strcpy(clients[id].drvName, tmp); + strcpy(clients[id].Dbg.drvName, tmp); + clients[id].Dbg.drvTag[0] = 0; + clients[id].logical = (int)logical; + clients[id].channels = (int)d->channels; + clients[id].dma_handle = -1; + + clients[id].Dbg.dbgMask = 0; + clients[id].dbgMask = clients[id].Dbg.dbgMask; + if (id) { + clients[id].Dbg.dbgMask |= clients[free_id].last_dbgMask; + } else { + clients[id].last_dbgMask = 0; + } + clients[id].Dbg.Registered = DBG_HANDLE_REG_NEW; + clients[id].Dbg.id = (byte)id; + clients[id].Dbg.dbg_end = DI_deregister; + clients[id].Dbg.dbg_prt = DI_format_locked; + clients[id].Dbg.dbg_ev = DiProcessEventLog; + clients[id].Dbg.dbg_irq = DI_format_locked; + clients[id].Dbg.next = (pDbgHandle)DBG_MAGIC; + + { + diva_trace_library_user_interface_t diva_maint_user_ifc = { &clients[id], + diva_maint_state_change_notify, + diva_maint_trace_notify, + diva_maint_error }; + + /* + Attach to adapter management interface + */ + if ((clients[id].pIdiLib = + DivaSTraceLibraryCreateInstance((int)logical, &diva_maint_user_ifc, pmem))) { + if (((*(clients[id].pIdiLib->DivaSTraceLibraryStart))(clients[id].pIdiLib->hLib))) { + diva_mnt_internal_dprintf(0, DLI_ERR, "Adapter(%d) Start failed", (int)logical); + (*(clients[id].pIdiLib->DivaSTraceLibraryFinit))(clients[id].pIdiLib->hLib); + clients[id].pIdiLib = NULL; + } + } else { + diva_mnt_internal_dprintf(0, DLI_ERR, "A(%d) management init failed", (int)logical); + } + } + + if (!clients[id].pIdiLib) { + clients[id].request = NULL; + clients[id].request_pending = 0; + clients[id].hDbg = NULL; + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register"); + diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "register"); + diva_os_free(0, pmem); + return; + } + + /* + Log driver register, MAINT driver ID is '0' + */ + len = sprintf(tmp, "DIMAINT - drv # %d = '%s' registered", + id, clients[id].Dbg.drvName); + + while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue, + (word)(len + 1 + sizeof(*pmsg))))) { + if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) { + queueFreeMsg(dbg_queue); + } else { + break; + } + } + + if (pmsg) { + pmsg->sequence = dbg_sequence++; + pmsg->time_sec = sec; + pmsg->time_usec = usec; + pmsg->facility = MSG_TYPE_STRING; + pmsg->dli = DLI_REG; + pmsg->drv_id = 0; /* id 0 - DIMAINT */ + pmsg->di_cpu = 0; + pmsg->data_length = len + 1; + + memcpy(&pmsg[1], tmp, len + 1); + queueCompleteMsg(pmsg); + diva_maint_wakeup_read(); + } + + org_mask = clients[id].Dbg.dbgMask; + clients[id].Dbg.dbgMask = 0; + + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register"); + + if (clients[id].request_pending) { + clients[id].request_pending = 0; + (*(clients[id].request))((ENTITY *)(*(clients[id].pIdiLib->DivaSTraceGetHandle))(clients[id].pIdiLib->hLib)); + } + + diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "register"); + + diva_set_driver_dbg_mask(id, org_mask); +} + +/* + De-Register XDI adapter +*/ +void diva_mnt_remove_xdi_adapter(const DESCRIPTOR *d) { + diva_os_spin_lock_magic_t old_irql, old_irql1; + dword sec, usec; + int i; + word size; + byte *pmem = NULL; + + diva_os_get_time(&sec, &usec); + + diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "read"); + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "read"); + + for (i = 1; i < ARRAY_SIZE(clients); i++) { + if (clients[i].hDbg && (clients[i].request == d->request)) { + diva_dbg_entry_head_t *pmsg; + char tmp[256]; + int len; + + if (clients[i].pIdiLib) { + (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib); + clients[i].pIdiLib = NULL; + + pmem = clients[i].pmem; + clients[i].pmem = NULL; + } + + clients[i].hDbg = NULL; + clients[i].request_pending = 0; + if (clients[i].dma_handle >= 0) { + /* + Free DMA handle + */ + diva_free_dma_descriptor(clients[i].request, clients[i].dma_handle); + clients[i].dma_handle = -1; + } + clients[i].request = NULL; + + /* + Log driver register, MAINT driver ID is '0' + */ + len = sprintf(tmp, "DIMAINT - drv # %d = '%s' de-registered", + i, clients[i].Dbg.drvName); + + memset(&clients[i].Dbg, 0x00, sizeof(clients[i].Dbg)); + + while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue, + (word)(len + 1 + sizeof(*pmsg))))) { + if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) { + queueFreeMsg(dbg_queue); + } else { + break; + } + } + + if (pmsg) { + pmsg->sequence = dbg_sequence++; + pmsg->time_sec = sec; + pmsg->time_usec = usec; + pmsg->facility = MSG_TYPE_STRING; + pmsg->dli = DLI_REG; + pmsg->drv_id = 0; /* id 0 - DIMAINT */ + pmsg->di_cpu = 0; + pmsg->data_length = len + 1; + + memcpy(&pmsg[1], tmp, len + 1); + queueCompleteMsg(pmsg); + diva_maint_wakeup_read(); + } + + break; + } + } + + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "read_ack"); + diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "read_ack"); + + if (pmem) { + diva_os_free(0, pmem); + } +} + +/* ---------------------------------------------------------------- + Low level interface for management interface client + ---------------------------------------------------------------- */ +/* + Return handle to client structure +*/ +void *SuperTraceOpenAdapter(int AdapterNumber) { + int i; + + for (i = 1; i < ARRAY_SIZE(clients); i++) { + if (clients[i].hDbg && clients[i].request && (clients[i].logical == AdapterNumber)) { + return (&clients[i]); + } + } + + return NULL; +} + +int SuperTraceCloseAdapter(void *AdapterHandle) { + return (0); +} + +int SuperTraceReadRequest(void *AdapterHandle, const char *name, byte *data) { + diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle; + + if (pC && pC->pIdiLib && pC->request) { + ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib); + byte *xdata = (byte *)&pC->xbuffer[0]; + char tmp = 0; + word length; + + if (!strcmp(name, "\\")) { /* Read ROOT */ + name = &tmp; + } + length = SuperTraceCreateReadReq(xdata, name); + single_p(xdata, &length, 0); /* End Of Message */ + + e->Req = MAN_READ; + e->ReqCh = 0; + e->X->PLength = length; + e->X->P = (byte *)xdata; + + pC->request_pending = 1; + + return (0); + } + + return (-1); +} + +int SuperTraceGetNumberOfChannels(void *AdapterHandle) { + if (AdapterHandle) { + diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle; + + return (pC->channels); + } + + return (0); +} + +int SuperTraceASSIGN(void *AdapterHandle, byte *data) { + diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle; + + if (pC && pC->pIdiLib && pC->request) { + ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib); + IDI_SYNC_REQ *preq; + char buffer[((sizeof(preq->xdi_extended_features) + 4) > sizeof(ENTITY)) ? (sizeof(preq->xdi_extended_features) + 4) : sizeof(ENTITY)]; + char features[4]; + word assign_data_length = 1; + + features[0] = 0; + pC->xbuffer[0] = 0; + preq = (IDI_SYNC_REQ *)&buffer[0]; + preq->xdi_extended_features.Req = 0; + preq->xdi_extended_features.Rc = IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES; + preq->xdi_extended_features.info.buffer_length_in_bytes = sizeof(features); + preq->xdi_extended_features.info.features = &features[0]; + + (*(pC->request))((ENTITY *)preq); + + if ((features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) && + (features[0] & DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA)) { + dword uninitialized_var(rx_dma_magic); + if ((pC->dma_handle = diva_get_dma_descriptor(pC->request, &rx_dma_magic)) >= 0) { + pC->xbuffer[0] = LLI; + pC->xbuffer[1] = 8; + pC->xbuffer[2] = 0x40; + pC->xbuffer[3] = (byte)pC->dma_handle; + pC->xbuffer[4] = (byte)rx_dma_magic; + pC->xbuffer[5] = (byte)(rx_dma_magic >> 8); + pC->xbuffer[6] = (byte)(rx_dma_magic >> 16); + pC->xbuffer[7] = (byte)(rx_dma_magic >> 24); + pC->xbuffer[8] = (byte)(DIVA_MAX_MANAGEMENT_TRANSFER_SIZE & 0xFF); + pC->xbuffer[9] = (byte)(DIVA_MAX_MANAGEMENT_TRANSFER_SIZE >> 8); + pC->xbuffer[10] = 0; + + assign_data_length = 11; + } + } else { + pC->dma_handle = -1; + } + + e->Id = MAN_ID; + e->callback = diva_maint_xdi_cb; + e->XNum = 1; + e->X = &pC->XData; + e->Req = ASSIGN; + e->ReqCh = 0; + e->X->PLength = assign_data_length; + e->X->P = (byte *)&pC->xbuffer[0]; + + pC->request_pending = 1; + + return (0); + } + + return (-1); +} + +int SuperTraceREMOVE(void *AdapterHandle) { + diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle; + + if (pC && pC->pIdiLib && pC->request) { + ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib); + + e->XNum = 1; + e->X = &pC->XData; + e->Req = REMOVE; + e->ReqCh = 0; + e->X->PLength = 1; + e->X->P = (byte *)&pC->xbuffer[0]; + pC->xbuffer[0] = 0; + + pC->request_pending = 1; + + return (0); + } + + return (-1); +} + +int SuperTraceTraceOnRequest(void *hAdapter, const char *name, byte *data) { + diva_maint_client_t *pC = (diva_maint_client_t *)hAdapter; + + if (pC && pC->pIdiLib && pC->request) { + ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib); + byte *xdata = (byte *)&pC->xbuffer[0]; + char tmp = 0; + word length; + + if (!strcmp(name, "\\")) { /* Read ROOT */ + name = &tmp; + } + length = SuperTraceCreateReadReq(xdata, name); + single_p(xdata, &length, 0); /* End Of Message */ + e->Req = MAN_EVENT_ON; + e->ReqCh = 0; + e->X->PLength = length; + e->X->P = (byte *)xdata; + + pC->request_pending = 1; + + return (0); + } + + return (-1); +} + +int SuperTraceWriteVar(void *AdapterHandle, + byte *data, + const char *name, + void *var, + byte type, + byte var_length) { + diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle; + + if (pC && pC->pIdiLib && pC->request) { + ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib); + diva_man_var_header_t *pVar = (diva_man_var_header_t *)&pC->xbuffer[0]; + word length = SuperTraceCreateReadReq((byte *)pVar, name); + + memcpy(&pC->xbuffer[length], var, var_length); + length += var_length; + pVar->length += var_length; + pVar->value_length = var_length; + pVar->type = type; + single_p((byte *)pVar, &length, 0); /* End Of Message */ + + e->Req = MAN_WRITE; + e->ReqCh = 0; + e->X->PLength = length; + e->X->P = (byte *)pVar; + + pC->request_pending = 1; + + return (0); + } + + return (-1); +} + +int SuperTraceExecuteRequest(void *AdapterHandle, + const char *name, + byte *data) { + diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle; + + if (pC && pC->pIdiLib && pC->request) { + ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib); + byte *xdata = (byte *)&pC->xbuffer[0]; + word length; + + length = SuperTraceCreateReadReq(xdata, name); + single_p(xdata, &length, 0); /* End Of Message */ + + e->Req = MAN_EXECUTE; + e->ReqCh = 0; + e->X->PLength = length; + e->X->P = (byte *)xdata; + + pC->request_pending = 1; + + return (0); + } + + return (-1); +} + +static word SuperTraceCreateReadReq(byte *P, const char *path) { + byte var_length; + byte *plen; + + var_length = (byte)strlen(path); + + *P++ = ESC; + plen = P++; + *P++ = 0x80; /* MAN_IE */ + *P++ = 0x00; /* Type */ + *P++ = 0x00; /* Attribute */ + *P++ = 0x00; /* Status */ + *P++ = 0x00; /* Variable Length */ + *P++ = var_length; + memcpy(P, path, var_length); + P += var_length; + *plen = var_length + 0x06; + + return ((word)(var_length + 0x08)); +} + +static void single_p(byte *P, word *PLength, byte Id) { + P[(*PLength)++] = Id; +} + +static void diva_maint_xdi_cb(ENTITY *e) { + diva_strace_context_t *pLib = DIVAS_CONTAINING_RECORD(e, diva_strace_context_t, e); + diva_maint_client_t *pC; + diva_os_spin_lock_magic_t old_irql, old_irql1; + + + diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "xdi_cb"); + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "xdi_cb"); + + pC = (diva_maint_client_t *)pLib->hAdapter; + + if ((e->complete == 255) || (pC->dma_handle < 0)) { + if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) { + diva_mnt_internal_dprintf(0, DLI_ERR, "Trace internal library error"); + } + } else { + /* + Process combined management interface indication + */ + if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) { + diva_mnt_internal_dprintf(0, DLI_ERR, "Trace internal library error (DMA mode)"); + } + } + + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "xdi_cb"); + + + if (pC->request_pending) { + pC->request_pending = 0; + (*(pC->request))(e); + } + + diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "xdi_cb"); +} + + +static void diva_maint_error(void *user_context, + diva_strace_library_interface_t *hLib, + int Adapter, + int error, + const char *file, + int line) { + diva_mnt_internal_dprintf(0, DLI_ERR, + "Trace library error(%d) A(%d) %s %d", error, Adapter, file, line); +} + +static void print_ie(diva_trace_ie_t *ie, char *buffer, int length) { + int i; + + buffer[0] = 0; + + if (length > 32) { + for (i = 0; ((i < ie->length) && (length > 3)); i++) { + sprintf(buffer, "%02x", ie->data[i]); + buffer += 2; + length -= 2; + if (i < (ie->length - 1)) { + strcpy(buffer, " "); + buffer++; + length--; + } + } + } +} + +static void diva_maint_state_change_notify(void *user_context, + diva_strace_library_interface_t *hLib, + int Adapter, + diva_trace_line_state_t *channel, + int notify_subject) { + diva_maint_client_t *pC = (diva_maint_client_t *)user_context; + diva_trace_fax_state_t *fax = &channel->fax; + diva_trace_modem_state_t *modem = &channel->modem; + char tmp[256]; + + if (!pC->hDbg) { + return; + } + + switch (notify_subject) { + case DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE: { + int view = (TraceFilter[0] == 0); + /* + Process selective Trace + */ + if (channel->Line[0] == 'I' && channel->Line[1] == 'd' && + channel->Line[2] == 'l' && channel->Line[3] == 'e') { + if ((TraceFilterIdent == pC->hDbg->id) && (TraceFilterChannel == (int)channel->ChannelNumber)) { + (*(hLib->DivaSTraceSetBChannel))(hLib, (int)channel->ChannelNumber, 0); + (*(hLib->DivaSTraceSetAudioTap))(hLib, (int)channel->ChannelNumber, 0); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, "Selective Trace OFF for Ch=%d", + (int)channel->ChannelNumber); + TraceFilterIdent = -1; + TraceFilterChannel = -1; + view = 1; + } + } else if (TraceFilter[0] && (TraceFilterIdent < 0) && !(diva_mnt_cmp_nmbr(&channel->RemoteAddress[0]) && + diva_mnt_cmp_nmbr(&channel->LocalAddress[0]))) { + + if ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0) { /* Activate B-channel trace */ + (*(hLib->DivaSTraceSetBChannel))(hLib, (int)channel->ChannelNumber, 1); + } + if ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0) { /* Activate AudioTap Trace */ + (*(hLib->DivaSTraceSetAudioTap))(hLib, (int)channel->ChannelNumber, 1); + } + + TraceFilterIdent = pC->hDbg->id; + TraceFilterChannel = (int)channel->ChannelNumber; + + if (TraceFilterIdent >= 0) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, "Selective Trace ON for Ch=%d", + (int)channel->ChannelNumber); + view = 1; + } + } + if (view && (pC->hDbg->dbgMask & DIVA_MGT_DBG_LINE_EVENTS)) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Ch = %d", + (int)channel->ChannelNumber); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Status = <%s>", &channel->Line[0]); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Layer1 = <%s>", &channel->Framing[0]); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Layer2 = <%s>", &channel->Layer2[0]); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Layer3 = <%s>", &channel->Layer3[0]); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L RAddr = <%s>", + &channel->RemoteAddress[0]); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L RSAddr = <%s>", + &channel->RemoteSubAddress[0]); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L LAddr = <%s>", + &channel->LocalAddress[0]); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L LSAddr = <%s>", + &channel->LocalSubAddress[0]); + print_ie(&channel->call_BC, tmp, sizeof(tmp)); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L BC = <%s>", tmp); + print_ie(&channel->call_HLC, tmp, sizeof(tmp)); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L HLC = <%s>", tmp); + print_ie(&channel->call_LLC, tmp, sizeof(tmp)); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L LLC = <%s>", tmp); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L CR = 0x%x", channel->CallReference); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Disc = 0x%x", + channel->LastDisconnecCause); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Owner = <%s>", &channel->UserID[0]); + } + + } break; + + case DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE: + if (pC->hDbg->dbgMask & DIVA_MGT_DBG_MDM_PROGRESS) { + { + int ch = TraceFilterChannel; + int id = TraceFilterIdent; + + if ((id >= 0) && (ch >= 0) && (id < ARRAY_SIZE(clients)) && + (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) { + if (ch != (int)modem->ChannelNumber) { + break; + } + } else if (TraceFilter[0] != 0) { + break; + } + } + + + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Ch = %lu", + (int)modem->ChannelNumber); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Event = %lu", modem->Event); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Norm = %lu", modem->Norm); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Opts. = 0x%08x", modem->Options); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Tx = %lu Bps", modem->TxSpeed); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Rx = %lu Bps", modem->RxSpeed); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RT = %lu mSec", + modem->RoundtripMsec); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Sr = %lu", modem->SymbolRate); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Rxl = %d dBm", modem->RxLeveldBm); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM El = %d dBm", modem->EchoLeveldBm); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM SNR = %lu dB", modem->SNRdb); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM MAE = %lu", modem->MAE); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM LRet = %lu", + modem->LocalRetrains); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RRet = %lu", + modem->RemoteRetrains); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM LRes = %lu", modem->LocalResyncs); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RRes = %lu", + modem->RemoteResyncs); + if (modem->Event == 3) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Disc = %lu", modem->DiscReason); + } + } + if ((modem->Event == 3) && (pC->hDbg->dbgMask & DIVA_MGT_DBG_MDM_STATISTICS)) { + (*(pC->pIdiLib->DivaSTraceGetModemStatistics))(pC->pIdiLib); + } + break; + + case DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE: + if (pC->hDbg->dbgMask & DIVA_MGT_DBG_FAX_PROGRESS) { + { + int ch = TraceFilterChannel; + int id = TraceFilterIdent; + + if ((id >= 0) && (ch >= 0) && (id < ARRAY_SIZE(clients)) && + (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) { + if (ch != (int)fax->ChannelNumber) { + break; + } + } else if (TraceFilter[0] != 0) { + break; + } + } + + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Ch = %lu", (int)fax->ChannelNumber); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Event = %lu", fax->Event); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Pages = %lu", fax->Page_Counter); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Feat. = 0x%08x", fax->Features); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX ID = <%s>", &fax->Station_ID[0]); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Saddr = <%s>", &fax->Subaddress[0]); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Pwd = <%s>", &fax->Password[0]); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Speed = %lu", fax->Speed); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Res. = 0x%08x", fax->Resolution); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Width = %lu", fax->Paper_Width); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Length= %lu", fax->Paper_Length); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX SLT = %lu", fax->Scanline_Time); + if (fax->Event == 3) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Disc = %lu", fax->Disc_Reason); + } + } + if ((fax->Event == 3) && (pC->hDbg->dbgMask & DIVA_MGT_DBG_FAX_STATISTICS)) { + (*(pC->pIdiLib->DivaSTraceGetFaxStatistics))(pC->pIdiLib); + } + break; + + case DIVA_SUPER_TRACE_INTERFACE_CHANGE: + if (pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_EVENTS) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, + "Layer 1 -> [%s]", channel->pInterface->Layer1); + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, + "Layer 2 -> [%s]", channel->pInterface->Layer2); + } + break; + + case DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE: + if (pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_STATISTICS) { + /* + Incoming Statistics + */ + if (channel->pInterfaceStat->inc.Calls) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Inc Calls =%lu", channel->pInterfaceStat->inc.Calls); + } + if (channel->pInterfaceStat->inc.Connected) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Inc Connected =%lu", channel->pInterfaceStat->inc.Connected); + } + if (channel->pInterfaceStat->inc.User_Busy) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Inc Busy =%lu", channel->pInterfaceStat->inc.User_Busy); + } + if (channel->pInterfaceStat->inc.Call_Rejected) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Inc Rejected =%lu", channel->pInterfaceStat->inc.Call_Rejected); + } + if (channel->pInterfaceStat->inc.Wrong_Number) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Inc Wrong Nr =%lu", channel->pInterfaceStat->inc.Wrong_Number); + } + if (channel->pInterfaceStat->inc.Incompatible_Dst) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Inc Incomp. Dest =%lu", channel->pInterfaceStat->inc.Incompatible_Dst); + } + if (channel->pInterfaceStat->inc.Out_of_Order) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Inc Out of Order =%lu", channel->pInterfaceStat->inc.Out_of_Order); + } + if (channel->pInterfaceStat->inc.Ignored) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Inc Ignored =%lu", channel->pInterfaceStat->inc.Ignored); + } + + /* + Outgoing Statistics + */ + if (channel->pInterfaceStat->outg.Calls) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Outg Calls =%lu", channel->pInterfaceStat->outg.Calls); + } + if (channel->pInterfaceStat->outg.Connected) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Outg Connected =%lu", channel->pInterfaceStat->outg.Connected); + } + if (channel->pInterfaceStat->outg.User_Busy) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Outg Busy =%lu", channel->pInterfaceStat->outg.User_Busy); + } + if (channel->pInterfaceStat->outg.No_Answer) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Outg No Answer =%lu", channel->pInterfaceStat->outg.No_Answer); + } + if (channel->pInterfaceStat->outg.Wrong_Number) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Outg Wrong Nr =%lu", channel->pInterfaceStat->outg.Wrong_Number); + } + if (channel->pInterfaceStat->outg.Call_Rejected) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Outg Rejected =%lu", channel->pInterfaceStat->outg.Call_Rejected); + } + if (channel->pInterfaceStat->outg.Other_Failures) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "Outg Other Failures =%lu", channel->pInterfaceStat->outg.Other_Failures); + } + } + break; + + case DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE: + if (channel->pInterfaceStat->mdm.Disc_Normal) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "MDM Disc Normal = %lu", channel->pInterfaceStat->mdm.Disc_Normal); + } + if (channel->pInterfaceStat->mdm.Disc_Unspecified) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "MDM Disc Unsp. = %lu", channel->pInterfaceStat->mdm.Disc_Unspecified); + } + if (channel->pInterfaceStat->mdm.Disc_Busy_Tone) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "MDM Disc Busy Tone = %lu", channel->pInterfaceStat->mdm.Disc_Busy_Tone); + } + if (channel->pInterfaceStat->mdm.Disc_Congestion) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "MDM Disc Congestion = %lu", channel->pInterfaceStat->mdm.Disc_Congestion); + } + if (channel->pInterfaceStat->mdm.Disc_Carr_Wait) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "MDM Disc Carrier Wait = %lu", channel->pInterfaceStat->mdm.Disc_Carr_Wait); + } + if (channel->pInterfaceStat->mdm.Disc_Trn_Timeout) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "MDM Disc Trn. T.o. = %lu", channel->pInterfaceStat->mdm.Disc_Trn_Timeout); + } + if (channel->pInterfaceStat->mdm.Disc_Incompat) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "MDM Disc Incompatible = %lu", channel->pInterfaceStat->mdm.Disc_Incompat); + } + if (channel->pInterfaceStat->mdm.Disc_Frame_Rej) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "MDM Disc Frame Reject = %lu", channel->pInterfaceStat->mdm.Disc_Frame_Rej); + } + if (channel->pInterfaceStat->mdm.Disc_V42bis) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "MDM Disc V.42bis = %lu", channel->pInterfaceStat->mdm.Disc_V42bis); + } + break; + + case DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE: + if (channel->pInterfaceStat->fax.Disc_Normal) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Normal = %lu", channel->pInterfaceStat->fax.Disc_Normal); + } + if (channel->pInterfaceStat->fax.Disc_Not_Ident) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Not Ident. = %lu", channel->pInterfaceStat->fax.Disc_Not_Ident); + } + if (channel->pInterfaceStat->fax.Disc_No_Response) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc No Response = %lu", channel->pInterfaceStat->fax.Disc_No_Response); + } + if (channel->pInterfaceStat->fax.Disc_Retries) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Max Retries = %lu", channel->pInterfaceStat->fax.Disc_Retries); + } + if (channel->pInterfaceStat->fax.Disc_Unexp_Msg) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Unexp. Msg. = %lu", channel->pInterfaceStat->fax.Disc_Unexp_Msg); + } + if (channel->pInterfaceStat->fax.Disc_No_Polling) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc No Polling = %lu", channel->pInterfaceStat->fax.Disc_No_Polling); + } + if (channel->pInterfaceStat->fax.Disc_Training) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Training = %lu", channel->pInterfaceStat->fax.Disc_Training); + } + if (channel->pInterfaceStat->fax.Disc_Unexpected) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Unexpected = %lu", channel->pInterfaceStat->fax.Disc_Unexpected); + } + if (channel->pInterfaceStat->fax.Disc_Application) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Application = %lu", channel->pInterfaceStat->fax.Disc_Application); + } + if (channel->pInterfaceStat->fax.Disc_Incompat) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Incompatible = %lu", channel->pInterfaceStat->fax.Disc_Incompat); + } + if (channel->pInterfaceStat->fax.Disc_No_Command) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc No Command = %lu", channel->pInterfaceStat->fax.Disc_No_Command); + } + if (channel->pInterfaceStat->fax.Disc_Long_Msg) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Long Msg. = %lu", channel->pInterfaceStat->fax.Disc_Long_Msg); + } + if (channel->pInterfaceStat->fax.Disc_Supervisor) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Supervisor = %lu", channel->pInterfaceStat->fax.Disc_Supervisor); + } + if (channel->pInterfaceStat->fax.Disc_SUB_SEP_PWD) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc SUP SEP PWD = %lu", channel->pInterfaceStat->fax.Disc_SUB_SEP_PWD); + } + if (channel->pInterfaceStat->fax.Disc_Invalid_Msg) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Invalid Msg. = %lu", channel->pInterfaceStat->fax.Disc_Invalid_Msg); + } + if (channel->pInterfaceStat->fax.Disc_Page_Coding) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Page Coding = %lu", channel->pInterfaceStat->fax.Disc_Page_Coding); + } + if (channel->pInterfaceStat->fax.Disc_App_Timeout) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Appl. T.o. = %lu", channel->pInterfaceStat->fax.Disc_App_Timeout); + } + if (channel->pInterfaceStat->fax.Disc_Unspecified) { + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, + "FAX Disc Unspec. = %lu", channel->pInterfaceStat->fax.Disc_Unspecified); + } + break; + } +} + +/* + Receive trace information from the Management Interface and store it in the + internal trace buffer with MSG_TYPE_MLOG as is, without any filtering. + Event Filtering and formatting is done in Management Interface self. +*/ +static void diva_maint_trace_notify(void *user_context, + diva_strace_library_interface_t *hLib, + int Adapter, + void *xlog_buffer, + int length) { + diva_maint_client_t *pC = (diva_maint_client_t *)user_context; + diva_dbg_entry_head_t *pmsg; + word size; + dword sec, usec; + int ch = TraceFilterChannel; + int id = TraceFilterIdent; + + /* + Selective trace + */ + if ((id >= 0) && (ch >= 0) && (id < ARRAY_SIZE(clients)) && + (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) { + const char *p = NULL; + int ch_value = -1; + MI_XLOG_HDR *TrcData = (MI_XLOG_HDR *)xlog_buffer; + + if (Adapter != clients[id].logical) { + return; /* Ignore all trace messages from other adapters */ + } + + if (TrcData->code == 24) { + p = (char *)&TrcData->code; + p += 2; + } + + /* + All L1 messages start as [dsp,ch], so we can filter this information + and filter out all messages that use different channel + */ + if (p && p[0] == '[') { + if (p[2] == ',') { + p += 3; + ch_value = *p - '0'; + } else if (p[3] == ',') { + p += 4; + ch_value = *p - '0'; + } + if (ch_value >= 0) { + if (p[2] == ']') { + ch_value = ch_value * 10 + p[1] - '0'; + } + if (ch_value != ch) { + return; /* Ignore other channels */ + } + } + } + + } else if (TraceFilter[0] != 0) { + return; /* Ignore trace if trace filter is activated, but idle */ + } + + diva_os_get_time(&sec, &usec); + + while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue, + (word)length + sizeof(*pmsg)))) { + if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) { + queueFreeMsg(dbg_queue); + } else { + break; + } + } + if (pmsg) { + memcpy(&pmsg[1], xlog_buffer, length); + pmsg->sequence = dbg_sequence++; + pmsg->time_sec = sec; + pmsg->time_usec = usec; + pmsg->facility = MSG_TYPE_MLOG; + pmsg->dli = pC->logical; + pmsg->drv_id = pC->hDbg->id; + pmsg->di_cpu = 0; + pmsg->data_length = length; + queueCompleteMsg(pmsg); + if (queueCount(dbg_queue)) { + diva_maint_wakeup_read(); + } + } +} + + +/* + Convert MAINT trace mask to management interface trace mask/work/facility and + issue command to management interface +*/ +static void diva_change_management_debug_mask(diva_maint_client_t *pC, dword old_mask) { + if (pC->request && pC->hDbg && pC->pIdiLib) { + dword changed = pC->hDbg->dbgMask ^ old_mask; + + if (changed & DIVA_MGT_DBG_TRACE) { + (*(pC->pIdiLib->DivaSTraceSetInfo))(pC->pIdiLib, + (pC->hDbg->dbgMask & DIVA_MGT_DBG_TRACE) != 0); + } + if (changed & DIVA_MGT_DBG_DCHAN) { + (*(pC->pIdiLib->DivaSTraceSetDChannel))(pC->pIdiLib, + (pC->hDbg->dbgMask & DIVA_MGT_DBG_DCHAN) != 0); + } + if (!TraceFilter[0]) { + if (changed & DIVA_MGT_DBG_IFC_BCHANNEL) { + int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0); + + for (i = 0; i < pC->channels; i++) { + (*(pC->pIdiLib->DivaSTraceSetBChannel))(pC->pIdiLib, i + 1, state); + } + } + if (changed & DIVA_MGT_DBG_IFC_AUDIO) { + int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0); + + for (i = 0; i < pC->channels; i++) { + (*(pC->pIdiLib->DivaSTraceSetAudioTap))(pC->pIdiLib, i + 1, state); + } + } + } + } +} + + +void diva_mnt_internal_dprintf(dword drv_id, dword type, char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + DI_format(0, (word)drv_id, (int)type, fmt, ap); + va_end(ap); +} + +/* + Shutdown all adapters before driver removal +*/ +int diva_mnt_shutdown_xdi_adapters(void) { + diva_os_spin_lock_magic_t old_irql, old_irql1; + int i, fret = 0; + byte *pmem; + + + for (i = 1; i < ARRAY_SIZE(clients); i++) { + pmem = NULL; + + diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "unload"); + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "unload"); + + if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request) { + if ((*(clients[i].pIdiLib->DivaSTraceLibraryStop))(clients[i].pIdiLib) == 1) { + /* + Adapter removal complete + */ + if (clients[i].pIdiLib) { + (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib); + clients[i].pIdiLib = NULL; + + pmem = clients[i].pmem; + clients[i].pmem = NULL; + } + clients[i].hDbg = NULL; + clients[i].request_pending = 0; + + if (clients[i].dma_handle >= 0) { + /* + Free DMA handle + */ + diva_free_dma_descriptor(clients[i].request, clients[i].dma_handle); + clients[i].dma_handle = -1; + } + clients[i].request = NULL; + } else { + fret = -1; + } + } + + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "unload"); + if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request && clients[i].request_pending) { + clients[i].request_pending = 0; + (*(clients[i].request))((ENTITY *)(*(clients[i].pIdiLib->DivaSTraceGetHandle))(clients[i].pIdiLib->hLib)); + if (clients[i].dma_handle >= 0) { + diva_free_dma_descriptor(clients[i].request, clients[i].dma_handle); + clients[i].dma_handle = -1; + } + } + diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "unload"); + + if (pmem) { + diva_os_free(0, pmem); + } + } + + return (fret); +} + +/* + Set/Read the trace filter used for selective tracing. + Affects B- and Audio Tap trace mask at run time +*/ +int diva_set_trace_filter(int filter_length, const char *filter) { + diva_os_spin_lock_magic_t old_irql, old_irql1; + int i, ch, on, client_b_on, client_atap_on; + + diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "dbg mask"); + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "write_filter"); + + if (filter_length <= DIVA_MAX_SELECTIVE_FILTER_LENGTH) { + memcpy(&TraceFilter[0], filter, filter_length); + if (TraceFilter[filter_length]) { + TraceFilter[filter_length] = 0; + } + if (TraceFilter[0] == '*') { + TraceFilter[0] = 0; + } + } else { + filter_length = -1; + } + + TraceFilterIdent = -1; + TraceFilterChannel = -1; + + on = (TraceFilter[0] == 0); + + for (i = 1; i < ARRAY_SIZE(clients); i++) { + if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request) { + client_b_on = on && ((clients[i].hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0); + client_atap_on = on && ((clients[i].hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0); + for (ch = 0; ch < clients[i].channels; ch++) { + (*(clients[i].pIdiLib->DivaSTraceSetBChannel))(clients[i].pIdiLib->hLib, ch + 1, client_b_on); + (*(clients[i].pIdiLib->DivaSTraceSetAudioTap))(clients[i].pIdiLib->hLib, ch + 1, client_atap_on); + } + } + } + + for (i = 1; i < ARRAY_SIZE(clients); i++) { + if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request && clients[i].request_pending) { + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "write_filter"); + clients[i].request_pending = 0; + (*(clients[i].request))((ENTITY *)(*(clients[i].pIdiLib->DivaSTraceGetHandle))(clients[i].pIdiLib->hLib)); + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "write_filter"); + } + } + + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "write_filter"); + diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "dbg mask"); + + return (filter_length); +} + +int diva_get_trace_filter(int max_length, char *filter) { + diva_os_spin_lock_magic_t old_irql; + int len; + + diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "read_filter"); + len = strlen(&TraceFilter[0]) + 1; + if (max_length >= len) { + memcpy(filter, &TraceFilter[0], len); + } + diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "read_filter"); + + return (len); +} + +static int diva_dbg_cmp_key(const char *ref, const char *key) { + while (*key && (*ref++ == *key++)); + return (!*key && !*ref); +} + +/* + In case trace filter starts with "C" character then + all following characters are interpreted as command. + Following commands are available: + - single, trace single call at time, independent from CPN/CiPN +*/ +static int diva_mnt_cmp_nmbr(const char *nmbr) { + const char *ref = &TraceFilter[0]; + int ref_len = strlen(&TraceFilter[0]), nmbr_len = strlen(nmbr); + + if (ref[0] == 'C') { + if (diva_dbg_cmp_key(&ref[1], "single")) { + return (0); + } + return (-1); + } + + if (!ref_len || (ref_len > nmbr_len)) { + return (-1); + } + + nmbr = nmbr + nmbr_len - 1; + ref = ref + ref_len - 1; + + while (ref_len--) { + if (*nmbr-- != *ref--) { + return (-1); + } + } + + return (0); +} + +static int diva_get_dma_descriptor(IDI_CALL request, dword *dma_magic) { + ENTITY e; + IDI_SYNC_REQ *pReq = (IDI_SYNC_REQ *)&e; + + if (!request) { + return (-1); + } + + pReq->xdi_dma_descriptor_operation.Req = 0; + pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION; + + pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC; + pReq->xdi_dma_descriptor_operation.info.descriptor_number = -1; + pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL; + pReq->xdi_dma_descriptor_operation.info.descriptor_magic = 0; + + (*request)((ENTITY *)pReq); + + if (!pReq->xdi_dma_descriptor_operation.info.operation && + (pReq->xdi_dma_descriptor_operation.info.descriptor_number >= 0) && + pReq->xdi_dma_descriptor_operation.info.descriptor_magic) { + *dma_magic = pReq->xdi_dma_descriptor_operation.info.descriptor_magic; + return (pReq->xdi_dma_descriptor_operation.info.descriptor_number); + } else { + return (-1); + } +} + +static void diva_free_dma_descriptor(IDI_CALL request, int nr) { + ENTITY e; + IDI_SYNC_REQ *pReq = (IDI_SYNC_REQ *)&e; + + if (!request || (nr < 0)) { + return; + } + + pReq->xdi_dma_descriptor_operation.Req = 0; + pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION; + + pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE; + pReq->xdi_dma_descriptor_operation.info.descriptor_number = nr; + pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL; + pReq->xdi_dma_descriptor_operation.info.descriptor_magic = 0; + + (*request)((ENTITY *)pReq); +} diff --git a/drivers/isdn/hardware/eicon/debug_if.h b/drivers/isdn/hardware/eicon/debug_if.h new file mode 100644 index 000000000..fc5953a35 --- /dev/null +++ b/drivers/isdn/hardware/eicon/debug_if.h @@ -0,0 +1,88 @@ +/* + * + Copyright (c) Eicon Technology Corporation, 2000. + * + This source file is supplied for the use with Eicon + Technology Corporation's range of DIVA Server Adapters. + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_DEBUG_IF_H__ +#define __DIVA_DEBUG_IF_H__ +#define MSG_TYPE_DRV_ID 0x0001 +#define MSG_TYPE_FLAGS 0x0002 +#define MSG_TYPE_STRING 0x0003 +#define MSG_TYPE_BINARY 0x0004 +#define MSG_TYPE_MLOG 0x0005 + +#define MSG_FRAME_MAX_SIZE 2150 + +typedef struct _diva_dbg_entry_head { + dword sequence; + dword time_sec; + dword time_usec; + dword facility; + dword dli; + dword drv_id; + dword di_cpu; + dword data_length; +} diva_dbg_entry_head_t; + +int diva_maint_init(byte *base, unsigned long length, int do_init); +void *diva_maint_finit(void); +dword diva_dbg_q_length(void); +diva_dbg_entry_head_t *diva_maint_get_message(word *size, + diva_os_spin_lock_magic_t *old_irql); +void diva_maint_ack_message(int do_release, + diva_os_spin_lock_magic_t *old_irql); +void diva_maint_prtComp(char *format, ...); +void diva_maint_wakeup_read(void); +int diva_get_driver_info(dword id, byte *data, int data_length); +int diva_get_driver_dbg_mask(dword id, byte *data); +int diva_set_driver_dbg_mask(dword id, dword mask); +void diva_mnt_remove_xdi_adapter(const DESCRIPTOR *d); +void diva_mnt_add_xdi_adapter(const DESCRIPTOR *d); +int diva_mnt_shutdown_xdi_adapters(void); + +#define DIVA_MAX_SELECTIVE_FILTER_LENGTH 127 +int diva_set_trace_filter(int filter_length, const char *filter); +int diva_get_trace_filter(int max_length, char *filter); + + +#define DITRACE_CMD_GET_DRIVER_INFO 1 +#define DITRACE_READ_DRIVER_DBG_MASK 2 +#define DITRACE_WRITE_DRIVER_DBG_MASK 3 +#define DITRACE_READ_TRACE_ENTRY 4 +#define DITRACE_READ_TRACE_ENTRYS 5 +#define DITRACE_WRITE_SELECTIVE_TRACE_FILTER 6 +#define DITRACE_READ_SELECTIVE_TRACE_FILTER 7 + +/* + Trace lavels for debug via management interface +*/ +#define DIVA_MGT_DBG_TRACE 0x00000001 /* All trace messages from the card */ +#define DIVA_MGT_DBG_DCHAN 0x00000002 /* All D-channel relater trace messages */ +#define DIVA_MGT_DBG_MDM_PROGRESS 0x00000004 /* Modem progress events */ +#define DIVA_MGT_DBG_FAX_PROGRESS 0x00000008 /* Fax progress events */ +#define DIVA_MGT_DBG_IFC_STATISTICS 0x00000010 /* Interface call statistics */ +#define DIVA_MGT_DBG_MDM_STATISTICS 0x00000020 /* Global modem statistics */ +#define DIVA_MGT_DBG_FAX_STATISTICS 0x00000040 /* Global call statistics */ +#define DIVA_MGT_DBG_LINE_EVENTS 0x00000080 /* Line state events */ +#define DIVA_MGT_DBG_IFC_EVENTS 0x00000100 /* Interface/L1/L2 state events */ +#define DIVA_MGT_DBG_IFC_BCHANNEL 0x00000200 /* B-Channel trace for all channels */ +#define DIVA_MGT_DBG_IFC_AUDIO 0x00000400 /* Audio Tap trace for all channels */ + +# endif /* DEBUG_IF___H */ diff --git a/drivers/isdn/hardware/eicon/debuglib.c b/drivers/isdn/hardware/eicon/debuglib.c new file mode 100644 index 000000000..d5b1092a5 --- /dev/null +++ b/drivers/isdn/hardware/eicon/debuglib.c @@ -0,0 +1,156 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "debuglib.h" + +#ifdef DIVA_NO_DEBUGLIB +static DIVA_DI_PRINTF dprintf; +#else /* DIVA_NO_DEBUGLIB */ + +_DbgHandle_ myDriverDebugHandle = { 0 /*!Registered*/, DBG_HANDLE_VERSION }; +DIVA_DI_PRINTF dprintf = no_printf; +/*****************************************************************************/ +#define DBG_FUNC(name) \ + void \ + myDbgPrint_##name(char *format, ...) \ + { va_list ap; \ + if (myDriverDebugHandle.dbg_prt) \ + { va_start(ap, format); \ + (myDriverDebugHandle.dbg_prt) \ + (myDriverDebugHandle.id, DLI_##name, format, ap); \ + va_end(ap); \ + } } +DBG_FUNC(LOG) +DBG_FUNC(FTL) +DBG_FUNC(ERR) +DBG_FUNC(TRC) +DBG_FUNC(MXLOG) +DBG_FUNC(FTL_MXLOG) +void +myDbgPrint_EVL(long msgID, ...) +{ va_list ap; + if (myDriverDebugHandle.dbg_ev) + { va_start(ap, msgID); + (myDriverDebugHandle.dbg_ev) + (myDriverDebugHandle.id, (unsigned long)msgID, ap); + va_end(ap); + } } +DBG_FUNC(REG) +DBG_FUNC(MEM) +DBG_FUNC(SPL) +DBG_FUNC(IRP) +DBG_FUNC(TIM) +DBG_FUNC(BLK) +DBG_FUNC(TAPI) +DBG_FUNC(NDIS) +DBG_FUNC(CONN) +DBG_FUNC(STAT) +DBG_FUNC(SEND) +DBG_FUNC(RECV) +DBG_FUNC(PRV0) +DBG_FUNC(PRV1) +DBG_FUNC(PRV2) +DBG_FUNC(PRV3) +/*****************************************************************************/ +int +DbgRegister(char *drvName, char *drvTag, unsigned long dbgMask) +{ + int len; +/* + * deregister (if already registered) and zero out myDriverDebugHandle + */ + DbgDeregister(); +/* + * initialize the debug handle + */ + myDriverDebugHandle.Version = DBG_HANDLE_VERSION; + myDriverDebugHandle.id = -1; + myDriverDebugHandle.dbgMask = dbgMask | (DL_EVL | DL_FTL | DL_LOG); + len = strlen(drvName); + memcpy(myDriverDebugHandle.drvName, drvName, + (len < sizeof(myDriverDebugHandle.drvName)) ? + len : sizeof(myDriverDebugHandle.drvName) - 1); + len = strlen(drvTag); + memcpy(myDriverDebugHandle.drvTag, drvTag, + (len < sizeof(myDriverDebugHandle.drvTag)) ? + len : sizeof(myDriverDebugHandle.drvTag) - 1); +/* + * Try to register debugging via old (and only) interface + */ + dprintf("\000\377", &myDriverDebugHandle); + if (myDriverDebugHandle.dbg_prt) + { + return (1); + } +/* + * Check if we registered with an old maint driver (see debuglib.h) + */ + if (myDriverDebugHandle.dbg_end != NULL + /* location of 'dbg_prt' in _OldDbgHandle_ struct */ + && (myDriverDebugHandle.regTime.LowPart || + myDriverDebugHandle.regTime.HighPart)) + /* same location as in _OldDbgHandle_ struct */ + { + dprintf("%s: Cannot log to old maint driver !", drvName); + myDriverDebugHandle.dbg_end = + ((_OldDbgHandle_ *)&myDriverDebugHandle)->dbg_end; + DbgDeregister(); + } + return (0); +} +/*****************************************************************************/ +void +DbgSetLevel(unsigned long dbgMask) +{ + myDriverDebugHandle.dbgMask = dbgMask | (DL_EVL | DL_FTL | DL_LOG); +} +/*****************************************************************************/ +void +DbgDeregister(void) +{ + if (myDriverDebugHandle.dbg_end) + { + (myDriverDebugHandle.dbg_end)(&myDriverDebugHandle); + } + memset(&myDriverDebugHandle, 0, sizeof(myDriverDebugHandle)); +} +void xdi_dbg_xlog(char *x, ...) { + va_list ap; + va_start(ap, x); + if (myDriverDebugHandle.dbg_end && + (myDriverDebugHandle.dbg_irq || myDriverDebugHandle.dbg_old) && + (myDriverDebugHandle.dbgMask & DL_STAT)) { + if (myDriverDebugHandle.dbg_irq) { + (*(myDriverDebugHandle.dbg_irq))(myDriverDebugHandle.id, + (x[0] != 0) ? DLI_TRC : DLI_XLOG, x, ap); + } else { + (*(myDriverDebugHandle.dbg_old))(myDriverDebugHandle.id, x, ap); + } + } + va_end(ap); +} +/*****************************************************************************/ +#endif /* DIVA_NO_DEBUGLIB */ diff --git a/drivers/isdn/hardware/eicon/debuglib.h b/drivers/isdn/hardware/eicon/debuglib.h new file mode 100644 index 000000000..6dcbf6afb --- /dev/null +++ b/drivers/isdn/hardware/eicon/debuglib.h @@ -0,0 +1,322 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#if !defined(__DEBUGLIB_H__) +#define __DEBUGLIB_H__ +#include <stdarg.h> +/* + * define global debug priorities + */ +#define DL_LOG 0x00000001 /* always worth mentioning */ +#define DL_FTL 0x00000002 /* always sampled error */ +#define DL_ERR 0x00000004 /* any kind of error */ +#define DL_TRC 0x00000008 /* verbose information */ +#define DL_XLOG 0x00000010 /* old xlog info */ +#define DL_MXLOG 0x00000020 /* maestra xlog info */ +#define DL_FTL_MXLOG 0x00000021 /* fatal maestra xlog info */ +#define DL_EVL 0x00000080 /* special NT eventlog msg */ +#define DL_COMPAT (DL_MXLOG | DL_XLOG) +#define DL_PRIOR_MASK (DL_EVL | DL_COMPAT | DL_TRC | DL_ERR | DL_FTL | DL_LOG) +#define DLI_LOG 0x0100 +#define DLI_FTL 0x0200 +#define DLI_ERR 0x0300 +#define DLI_TRC 0x0400 +#define DLI_XLOG 0x0500 +#define DLI_MXLOG 0x0600 +#define DLI_FTL_MXLOG 0x0600 +#define DLI_EVL 0x0800 +/* + * define OS (operating system interface) debuglevel + */ +#define DL_REG 0x00000100 /* init/query registry */ +#define DL_MEM 0x00000200 /* memory management */ +#define DL_SPL 0x00000400 /* event/spinlock handling */ +#define DL_IRP 0x00000800 /* I/O request handling */ +#define DL_TIM 0x00001000 /* timer/watchdog handling */ +#define DL_BLK 0x00002000 /* raw data block contents */ +#define DL_OS_MASK (DL_BLK | DL_TIM | DL_IRP | DL_SPL | DL_MEM | DL_REG) +#define DLI_REG 0x0900 +#define DLI_MEM 0x0A00 +#define DLI_SPL 0x0B00 +#define DLI_IRP 0x0C00 +#define DLI_TIM 0x0D00 +#define DLI_BLK 0x0E00 +/* + * define ISDN (connection interface) debuglevel + */ +#define DL_TAPI 0x00010000 /* debug TAPI interface */ +#define DL_NDIS 0x00020000 /* debug NDIS interface */ +#define DL_CONN 0x00040000 /* connection handling */ +#define DL_STAT 0x00080000 /* trace state machines */ +#define DL_SEND 0x00100000 /* trace raw xmitted data */ +#define DL_RECV 0x00200000 /* trace raw received data */ +#define DL_DATA (DL_SEND | DL_RECV) +#define DL_ISDN_MASK (DL_DATA | DL_STAT | DL_CONN | DL_NDIS | DL_TAPI) +#define DLI_TAPI 0x1100 +#define DLI_NDIS 0x1200 +#define DLI_CONN 0x1300 +#define DLI_STAT 0x1400 +#define DLI_SEND 0x1500 +#define DLI_RECV 0x1600 +/* + * define some private (unspecified) debuglevel + */ +#define DL_PRV0 0x01000000 +#define DL_PRV1 0x02000000 +#define DL_PRV2 0x04000000 +#define DL_PRV3 0x08000000 +#define DL_PRIV_MASK (DL_PRV0 | DL_PRV1 | DL_PRV2 | DL_PRV3) +#define DLI_PRV0 0x1900 +#define DLI_PRV1 0x1A00 +#define DLI_PRV2 0x1B00 +#define DLI_PRV3 0x1C00 +#define DT_INDEX(x) ((x) & 0x000F) +#define DL_INDEX(x) ((((x) >> 8) & 0x00FF) - 1) +#define DLI_NAME(x) ((x) & 0xFF00) +/* + * Debug mask for kernel mode tracing, if set the output is also sent to + * the system debug function. Requires that the project is compiled + * with _KERNEL_DBG_PRINT_ + */ +#define DL_TO_KERNEL 0x40000000 + +#ifdef DIVA_NO_DEBUGLIB +#define myDbgPrint_LOG(x...) do { } while (0); +#define myDbgPrint_FTL(x...) do { } while (0); +#define myDbgPrint_ERR(x...) do { } while (0); +#define myDbgPrint_TRC(x...) do { } while (0); +#define myDbgPrint_MXLOG(x...) do { } while (0); +#define myDbgPrint_EVL(x...) do { } while (0); +#define myDbgPrint_REG(x...) do { } while (0); +#define myDbgPrint_MEM(x...) do { } while (0); +#define myDbgPrint_SPL(x...) do { } while (0); +#define myDbgPrint_IRP(x...) do { } while (0); +#define myDbgPrint_TIM(x...) do { } while (0); +#define myDbgPrint_BLK(x...) do { } while (0); +#define myDbgPrint_TAPI(x...) do { } while (0); +#define myDbgPrint_NDIS(x...) do { } while (0); +#define myDbgPrint_CONN(x...) do { } while (0); +#define myDbgPrint_STAT(x...) do { } while (0); +#define myDbgPrint_SEND(x...) do { } while (0); +#define myDbgPrint_RECV(x...) do { } while (0); +#define myDbgPrint_PRV0(x...) do { } while (0); +#define myDbgPrint_PRV1(x...) do { } while (0); +#define myDbgPrint_PRV2(x...) do { } while (0); +#define myDbgPrint_PRV3(x...) do { } while (0); +#define DBG_TEST(func, args) do { } while (0); +#define DBG_EVL_ID(args) do { } while (0); + +#else /* DIVA_NO_DEBUGLIB */ +/* + * define low level macros for formatted & raw debugging + */ +#define DBG_DECL(func) extern void myDbgPrint_##func(char *, ...); +DBG_DECL(LOG) +DBG_DECL(FTL) +DBG_DECL(ERR) +DBG_DECL(TRC) +DBG_DECL(MXLOG) +DBG_DECL(FTL_MXLOG) +extern void myDbgPrint_EVL(long, ...); +DBG_DECL(REG) +DBG_DECL(MEM) +DBG_DECL(SPL) +DBG_DECL(IRP) +DBG_DECL(TIM) +DBG_DECL(BLK) +DBG_DECL(TAPI) +DBG_DECL(NDIS) +DBG_DECL(CONN) +DBG_DECL(STAT) +DBG_DECL(SEND) +DBG_DECL(RECV) +DBG_DECL(PRV0) +DBG_DECL(PRV1) +DBG_DECL(PRV2) +DBG_DECL(PRV3) +#ifdef _KERNEL_DBG_PRINT_ +/* + * tracing to maint and kernel if selected in the trace mask. + */ +#define DBG_TEST(func, args) \ + { if ((myDriverDebugHandle.dbgMask) & (unsigned long)DL_##func) \ + { \ + if ((myDriverDebugHandle.dbgMask) & DL_TO_KERNEL) \ + { DbgPrint args; DbgPrint("\r\n"); } \ + myDbgPrint_##func args; \ + } } +#else +/* + * Standard tracing to maint driver. + */ +#define DBG_TEST(func, args) \ + { if ((myDriverDebugHandle.dbgMask) & (unsigned long)DL_##func) \ + { myDbgPrint_##func args; \ + } } +#endif +/* + * For event level debug use a separate define, the parameter are + * different and cause compiler errors on some systems. + */ +#define DBG_EVL_ID(args) \ + { if ((myDriverDebugHandle.dbgMask) & (unsigned long)DL_EVL) \ + { myDbgPrint_EVL args; \ + } } + +#endif /* DIVA_NO_DEBUGLIB */ + +#define DBG_LOG(args) DBG_TEST(LOG, args) +#define DBG_FTL(args) DBG_TEST(FTL, args) +#define DBG_ERR(args) DBG_TEST(ERR, args) +#define DBG_TRC(args) DBG_TEST(TRC, args) +#define DBG_MXLOG(args) DBG_TEST(MXLOG, args) +#define DBG_FTL_MXLOG(args) DBG_TEST(FTL_MXLOG, args) +#define DBG_EVL(args) DBG_EVL_ID(args) +#define DBG_REG(args) DBG_TEST(REG, args) +#define DBG_MEM(args) DBG_TEST(MEM, args) +#define DBG_SPL(args) DBG_TEST(SPL, args) +#define DBG_IRP(args) DBG_TEST(IRP, args) +#define DBG_TIM(args) DBG_TEST(TIM, args) +#define DBG_BLK(args) DBG_TEST(BLK, args) +#define DBG_TAPI(args) DBG_TEST(TAPI, args) +#define DBG_NDIS(args) DBG_TEST(NDIS, args) +#define DBG_CONN(args) DBG_TEST(CONN, args) +#define DBG_STAT(args) DBG_TEST(STAT, args) +#define DBG_SEND(args) DBG_TEST(SEND, args) +#define DBG_RECV(args) DBG_TEST(RECV, args) +#define DBG_PRV0(args) DBG_TEST(PRV0, args) +#define DBG_PRV1(args) DBG_TEST(PRV1, args) +#define DBG_PRV2(args) DBG_TEST(PRV2, args) +#define DBG_PRV3(args) DBG_TEST(PRV3, args) +/* + * prototypes for debug register/deregister functions in "debuglib.c" + */ +#ifdef DIVA_NO_DEBUGLIB +#define DbgRegister(name, tag, mask) do { } while (0) +#define DbgDeregister() do { } while (0) +#define DbgSetLevel(mask) do { } while (0) +#else +extern DIVA_DI_PRINTF dprintf; +extern int DbgRegister(char *drvName, char *drvTag, unsigned long dbgMask); +extern void DbgDeregister(void); +extern void DbgSetLevel(unsigned long dbgMask); +#endif +/* + * driver internal structure for debug handling; + * in client drivers this structure is maintained in "debuglib.c", + * in the debug driver "debug.c" maintains a chain of such structs. + */ +typedef struct _DbgHandle_ *pDbgHandle; +typedef void (*DbgEnd)(pDbgHandle); +typedef void (*DbgLog)(unsigned short, int, char *, va_list); +typedef void (*DbgOld)(unsigned short, char *, va_list); +typedef void (*DbgEv)(unsigned short, unsigned long, va_list); +typedef void (*DbgIrq)(unsigned short, int, char *, va_list); +typedef struct _DbgHandle_ +{ char Registered; /* driver successfully registered */ +#define DBG_HANDLE_REG_NEW 0x01 /* this (new) structure */ +#define DBG_HANDLE_REG_OLD 0x7f /* old structure (see below) */ + char Version; /* version of this structure */ +#define DBG_HANDLE_VERSION 1 /* contains dbg_old function now */ +#define DBG_HANDLE_VER_EXT 2 /* pReserved points to extended info*/ + short id; /* internal id of registered driver */ + struct _DbgHandle_ *next; /* ptr to next registered driver */ + struct /*LARGE_INTEGER*/ { + unsigned long LowPart; + long HighPart; + } regTime; /* timestamp for registration */ + void *pIrp; /* ptr to pending i/o request */ + unsigned long dbgMask; /* current debug mask */ + char drvName[128]; /* ASCII name of registered driver */ + char drvTag[64]; /* revision string */ + DbgEnd dbg_end; /* function for debug closing */ + DbgLog dbg_prt; /* function for debug appending */ + DbgOld dbg_old; /* function for old debug appending */ + DbgEv dbg_ev; /* function for Windows NT Eventlog */ + DbgIrq dbg_irq; /* function for irql checked debug */ + void *pReserved3; +} _DbgHandle_; +extern _DbgHandle_ myDriverDebugHandle; +typedef struct _OldDbgHandle_ +{ struct _OldDbgHandle_ *next; + void *pIrp; + long regTime[2]; + unsigned long dbgMask; + short id; + char drvName[78]; + DbgEnd dbg_end; + DbgLog dbg_prt; +} _OldDbgHandle_; +/* the differences in DbgHandles + old: tmp: new: + 0 long next char Registered char Registered + char filler char Version + short id short id + 4 long pIrp long regTime.lo long next + 8 long regTime.lo long regTime.hi long regTime.lo + 12 long regTime.hi long next long regTime.hi + 16 long dbgMask long pIrp long pIrp + 20 short id long dbgMask long dbgMask + 22 char drvName[78] .. + 24 .. char drvName[16] char drvName[16] + 40 .. char drvTag[64] char drvTag[64] + 100 void *dbg_end .. .. + 104 void *dbg_prt void *dbg_end void *dbg_end + 108 .. void *dbg_prt void *dbg_prt + 112 .. .. void *dbg_old + 116 .. .. void *dbg_ev + 120 .. .. void *dbg_irq + 124 .. .. void *pReserved3 + ( new->id == 0 && *((short *)&new->dbgMask) == -1 ) identifies "old", + new->Registered and new->Version overlay old->next, + new->next overlays old->pIrp, new->regTime matches old->regTime and + thus these fields can be maintained in new struct whithout trouble; + id, dbgMask, drvName, dbg_end and dbg_prt need special handling ! +*/ +#define DBG_EXT_TYPE_CARD_TRACE 0x00000001 +typedef struct +{ + unsigned long ExtendedType; + union + { + /* DBG_EXT_TYPE_CARD_TRACE */ + struct + { + void (*MaskChangedNotify)(void *pContext); + unsigned long ModuleTxtMask; + unsigned long DebugLevel; + unsigned long B_ChannelMask; + unsigned long LogBufferSize; + } CardTrace; + } Data; +} _DbgExtendedInfo_; +#ifndef DIVA_NO_DEBUGLIB +/* ------------------------------------------------------------- + Function used for xlog-style debug + ------------------------------------------------------------- */ +#define XDI_USE_XLOG 1 +void xdi_dbg_xlog(char *x, ...); +#endif /* DIVA_NO_DEBUGLIB */ +#endif /* __DEBUGLIB_H__ */ diff --git a/drivers/isdn/hardware/eicon/dfifo.h b/drivers/isdn/hardware/eicon/dfifo.h new file mode 100644 index 000000000..6a1d3337f --- /dev/null +++ b/drivers/isdn/hardware/eicon/dfifo.h @@ -0,0 +1,54 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_IDI_DFIFO_INC__ +#define __DIVA_IDI_DFIFO_INC__ +#define DIVA_DFIFO_CACHE_SZ 64 /* Used to isolate pipe from + rest of the world + should be divisible by 4 + */ +#define DIVA_DFIFO_RAW_SZ (2512 * 8) +#define DIVA_DFIFO_DATA_SZ 68 +#define DIVA_DFIFO_HDR_SZ 4 +#define DIVA_DFIFO_SEGMENT_SZ (DIVA_DFIFO_DATA_SZ + DIVA_DFIFO_HDR_SZ) +#define DIVA_DFIFO_SEGMENTS ((DIVA_DFIFO_RAW_SZ) / (DIVA_DFIFO_SEGMENT_SZ) + 1) +#define DIVA_DFIFO_MEM_SZ ( \ + (DIVA_DFIFO_SEGMENT_SZ) * (DIVA_DFIFO_SEGMENTS) + \ + (DIVA_DFIFO_CACHE_SZ) * 2 \ + ) +#define DIVA_DFIFO_STEP DIVA_DFIFO_SEGMENT_SZ +/* ------------------------------------------------------------------------- + Block header layout is: + byte[0] -> flags + byte[1] -> length of data in block + byte[2] -> reserved + byte[4] -> reserved + ------------------------------------------------------------------------- */ +#define DIVA_DFIFO_WRAP 0x80 /* This is the last block in fifo */ +#define DIVA_DFIFO_READY 0x40 /* This block is ready for processing */ +#define DIVA_DFIFO_LAST 0x20 /* This block is last in message */ +#define DIVA_DFIFO_AUTO 0x10 /* Don't look for 'ready', don't ack */ +int diva_dfifo_create(void *start, int length); +#endif diff --git a/drivers/isdn/hardware/eicon/di.c b/drivers/isdn/hardware/eicon/di.c new file mode 100644 index 000000000..cd3fba1ad --- /dev/null +++ b/drivers/isdn/hardware/eicon/di.c @@ -0,0 +1,835 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "platform.h" +#include "pc.h" +#include "pr_pc.h" +#include "di_defs.h" +#include "di.h" +#if !defined USE_EXTENDED_DEBUGS +#include "dimaint.h" +#else +#define dprintf +#endif +#include "io.h" +#include "dfifo.h" +#define PR_RAM ((struct pr_ram *)0) +#define RAM ((struct dual *)0) +/*------------------------------------------------------------------*/ +/* local function prototypes */ +/*------------------------------------------------------------------*/ +void pr_out(ADAPTER *a); +byte pr_dpc(ADAPTER *a); +static byte pr_ready(ADAPTER *a); +static byte isdn_rc(ADAPTER *, byte, byte, byte, word, dword, dword); +static byte isdn_ind(ADAPTER *, byte, byte, byte, PBUFFER *, byte, word); +/* ----------------------------------------------------------------- + Functions used for the extended XDI Debug + macros + global convergence counter (used by all adapters) + Look by the implementation part of the functions + about the parameters. + If you change the dubugging parameters, then you should update + the aididbg.doc in the IDI doc's. + ----------------------------------------------------------------- */ +#if defined(XDI_USE_XLOG) +#define XDI_A_NR(_x_) ((byte)(((ISDN_ADAPTER *)(_x_->io))->ANum)) +static void xdi_xlog(byte *msg, word code, int length); +static byte xdi_xlog_sec = 0; +#else +#define XDI_A_NR(_x_) ((byte)0) +#endif +static void xdi_xlog_rc_event(byte Adapter, + byte Id, byte Ch, byte Rc, byte cb, byte type); +static void xdi_xlog_request(byte Adapter, byte Id, + byte Ch, byte Req, byte type); +static void xdi_xlog_ind(byte Adapter, + byte Id, + byte Ch, + byte Ind, + byte rnr_valid, + byte rnr, + byte type); +/*------------------------------------------------------------------*/ +/* output function */ +/*------------------------------------------------------------------*/ +void pr_out(ADAPTER *a) +{ + byte e_no; + ENTITY *this = NULL; + BUFFERS *X; + word length; + word i; + word clength; + REQ *ReqOut; + byte more; + byte ReadyCount; + byte ReqCount; + byte Id; + dtrc(dprintf("pr_out")); + /* while a request is pending ... */ + e_no = look_req(a); + if (!e_no) + { + dtrc(dprintf("no_req")); + return; + } + ReadyCount = pr_ready(a); + if (!ReadyCount) + { + dtrc(dprintf("not_ready")); + return; + } + ReqCount = 0; + while (e_no && ReadyCount) { + next_req(a); + this = entity_ptr(a, e_no); +#ifdef USE_EXTENDED_DEBUGS + if (!this) + { + DBG_FTL(("XDI: [%02x] !A%d ==> NULL entity ptr - try to ignore", + xdi_xlog_sec++, (int)((ISDN_ADAPTER *)a->io)->ANum)) + e_no = look_req(a); + ReadyCount--; + continue; + } + { + DBG_TRC((">A%d Id=0x%x Req=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, this->Id, this->Req)) + } +#else + dbug(dprintf("out:Req=%x,Id=%x,Ch=%x", this->Req, this->Id, this->ReqCh)); +#endif + /* get address of next available request buffer */ + ReqOut = (REQ *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextReq)]; +#if defined(DIVA_ISTREAM) + if (!(a->tx_stream[this->Id] && + this->Req == N_DATA)) { +#endif + /* now copy the data from the current data buffer into the */ + /* adapters request buffer */ + length = 0; + i = this->XCurrent; + X = PTR_X(a, this); + while (i < this->XNum && length < 270) { + clength = min((word)(270 - length), (word)(X[i].PLength-this->XOffset)); + a->ram_out_buffer(a, + &ReqOut->XBuffer.P[length], + PTR_P(a, this, &X[i].P[this->XOffset]), + clength); + length += clength; + this->XOffset += clength; + if (this->XOffset == X[i].PLength) { + this->XCurrent = (byte)++i; + this->XOffset = 0; + } + } +#if defined(DIVA_ISTREAM) + } else { /* Use CMA extension in order to transfer data to the card */ + i = this->XCurrent; + X = PTR_X(a, this); + while (i < this->XNum) { + diva_istream_write(a, + this->Id, + PTR_P(a, this, &X[i].P[0]), + X[i].PLength, + ((i + 1) == this->XNum), + 0, 0); + this->XCurrent = (byte)++i; + } + length = 0; + } +#endif + a->ram_outw(a, &ReqOut->XBuffer.length, length); + a->ram_out(a, &ReqOut->ReqId, this->Id); + a->ram_out(a, &ReqOut->ReqCh, this->ReqCh); + /* if it's a specific request (no ASSIGN) ... */ + if (this->Id & 0x1f) { + /* if buffers are left in the list of data buffers do */ + /* do chaining (LL_MDATA, N_MDATA) */ + this->More++; + if (i < this->XNum && this->MInd) { + xdi_xlog_request(XDI_A_NR(a), this->Id, this->ReqCh, this->MInd, + a->IdTypeTable[this->No]); + a->ram_out(a, &ReqOut->Req, this->MInd); + more = true; + } + else { + xdi_xlog_request(XDI_A_NR(a), this->Id, this->ReqCh, this->Req, + a->IdTypeTable[this->No]); + this->More |= XMOREF; + a->ram_out(a, &ReqOut->Req, this->Req); + more = false; + if (a->FlowControlIdTable[this->ReqCh] == this->Id) + a->FlowControlSkipTable[this->ReqCh] = true; + /* + Note that remove request was sent to the card + */ + if (this->Req == REMOVE) { + a->misc_flags_table[e_no] |= DIVA_MISC_FLAGS_REMOVE_PENDING; + } + } + /* if we did chaining, this entity is put back into the */ + /* request queue */ + if (more) { + req_queue(a, this->No); + } + } + /* else it's a ASSIGN */ + else { + /* save the request code used for buffer chaining */ + this->MInd = 0; + if (this->Id == BLLC_ID) this->MInd = LL_MDATA; + if (this->Id == NL_ID || + this->Id == TASK_ID || + this->Id == MAN_ID + ) this->MInd = N_MDATA; + /* send the ASSIGN */ + a->IdTypeTable[this->No] = this->Id; + xdi_xlog_request(XDI_A_NR(a), this->Id, this->ReqCh, this->Req, this->Id); + this->More |= XMOREF; + a->ram_out(a, &ReqOut->Req, this->Req); + /* save the reference of the ASSIGN */ + assign_queue(a, this->No, a->ram_inw(a, &ReqOut->Reference)); + } + a->ram_outw(a, &PR_RAM->NextReq, a->ram_inw(a, &ReqOut->next)); + ReadyCount--; + ReqCount++; + e_no = look_req(a); + } + /* send the filled request buffers to the ISDN adapter */ + a->ram_out(a, &PR_RAM->ReqInput, + (byte)(a->ram_in(a, &PR_RAM->ReqInput) + ReqCount)); + /* if it is a 'unreturncoded' UREMOVE request, remove the */ + /* Id from our table after sending the request */ + if (this && (this->Req == UREMOVE) && this->Id) { + Id = this->Id; + e_no = a->IdTable[Id]; + free_entity(a, e_no); + for (i = 0; i < 256; i++) + { + if (a->FlowControlIdTable[i] == Id) + a->FlowControlIdTable[i] = 0; + } + a->IdTable[Id] = 0; + this->Id = 0; + } +} +static byte pr_ready(ADAPTER *a) +{ + byte ReadyCount; + ReadyCount = (byte)(a->ram_in(a, &PR_RAM->ReqOutput) - + a->ram_in(a, &PR_RAM->ReqInput)); + if (!ReadyCount) { + if (!a->ReadyInt) { + a->ram_inc(a, &PR_RAM->ReadyInt); + a->ReadyInt++; + } + } + return ReadyCount; +} +/*------------------------------------------------------------------*/ +/* isdn interrupt handler */ +/*------------------------------------------------------------------*/ +byte pr_dpc(ADAPTER *a) +{ + byte Count; + RC *RcIn; + IND *IndIn; + byte c; + byte RNRId; + byte Rc; + byte Ind; + /* if return codes are available ... */ + if ((Count = a->ram_in(a, &PR_RAM->RcOutput)) != 0) { + dtrc(dprintf("#Rc=%x", Count)); + /* get the buffer address of the first return code */ + RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextRc)]; + /* for all return codes do ... */ + while (Count--) { + if ((Rc = a->ram_in(a, &RcIn->Rc)) != 0) { + dword tmp[2]; + /* + Get extended information, associated with return code + */ + a->ram_in_buffer(a, + &RcIn->Reserved2[0], + (byte *)&tmp[0], + 8); + /* call return code handler, if it is not our return code */ + /* the handler returns 2 */ + /* for all return codes we process, we clear the Rc field */ + isdn_rc(a, + Rc, + a->ram_in(a, &RcIn->RcId), + a->ram_in(a, &RcIn->RcCh), + a->ram_inw(a, &RcIn->Reference), + tmp[0], /* type of extended information */ + tmp[1]); /* extended information */ + a->ram_out(a, &RcIn->Rc, 0); + } + /* get buffer address of next return code */ + RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &RcIn->next)]; + } + /* clear all return codes (no chaining!) */ + a->ram_out(a, &PR_RAM->RcOutput, 0); + /* call output function */ + pr_out(a); + } + /* clear RNR flag */ + RNRId = 0; + /* if indications are available ... */ + if ((Count = a->ram_in(a, &PR_RAM->IndOutput)) != 0) { + dtrc(dprintf("#Ind=%x", Count)); + /* get the buffer address of the first indication */ + IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextInd)]; + /* for all indications do ... */ + while (Count--) { + /* if the application marks an indication as RNR, all */ + /* indications from the same Id delivered in this interrupt */ + /* are marked RNR */ + if (RNRId && RNRId == a->ram_in(a, &IndIn->IndId)) { + a->ram_out(a, &IndIn->Ind, 0); + a->ram_out(a, &IndIn->RNR, true); + } + else { + Ind = a->ram_in(a, &IndIn->Ind); + if (Ind) { + RNRId = 0; + /* call indication handler, a return value of 2 means chain */ + /* a return value of 1 means RNR */ + /* for all indications we process, we clear the Ind field */ + c = isdn_ind(a, + Ind, + a->ram_in(a, &IndIn->IndId), + a->ram_in(a, &IndIn->IndCh), + &IndIn->RBuffer, + a->ram_in(a, &IndIn->MInd), + a->ram_inw(a, &IndIn->MLength)); + if (c == 1) { + dtrc(dprintf("RNR")); + a->ram_out(a, &IndIn->Ind, 0); + RNRId = a->ram_in(a, &IndIn->IndId); + a->ram_out(a, &IndIn->RNR, true); + } + } + } + /* get buffer address of next indication */ + IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &IndIn->next)]; + } + a->ram_out(a, &PR_RAM->IndOutput, 0); + } + return false; +} +byte scom_test_int(ADAPTER *a) +{ + return a->ram_in(a, (void *)0x3fe); +} +void scom_clear_int(ADAPTER *a) +{ + a->ram_out(a, (void *)0x3fe, 0); +} +/*------------------------------------------------------------------*/ +/* return code handler */ +/*------------------------------------------------------------------*/ +static byte isdn_rc(ADAPTER *a, + byte Rc, + byte Id, + byte Ch, + word Ref, + dword extended_info_type, + dword extended_info) +{ + ENTITY *this; + byte e_no; + word i; + int cancel_rc; +#ifdef USE_EXTENDED_DEBUGS + { + DBG_TRC(("<A%d Id=0x%x Rc=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, Id, Rc)) + } +#else + dbug(dprintf("isdn_rc(Rc=%x,Id=%x,Ch=%x)", Rc, Id, Ch)); +#endif + /* check for ready interrupt */ + if (Rc == READY_INT) { + xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 0, 0); + if (a->ReadyInt) { + a->ReadyInt--; + return 0; + } + return 2; + } + /* if we know this Id ... */ + e_no = a->IdTable[Id]; + if (e_no) { + this = entity_ptr(a, e_no); + xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 0, a->IdTypeTable[this->No]); + this->RcCh = Ch; + /* if it is a return code to a REMOVE request, remove the */ + /* Id from our table */ + if ((a->misc_flags_table[e_no] & DIVA_MISC_FLAGS_REMOVE_PENDING) && + (Rc == OK)) { + if (a->IdTypeTable[e_no] == NL_ID) { + if (a->RcExtensionSupported && + (extended_info_type != DIVA_RC_TYPE_REMOVE_COMPLETE)) { + dtrc(dprintf("XDI: N-REMOVE, A(%02x) Id:%02x, ignore RC=OK", + XDI_A_NR(a), Id)); + return (0); + } + if (extended_info_type == DIVA_RC_TYPE_REMOVE_COMPLETE) + a->RcExtensionSupported = true; + } + a->misc_flags_table[e_no] &= ~DIVA_MISC_FLAGS_REMOVE_PENDING; + a->misc_flags_table[e_no] &= ~DIVA_MISC_FLAGS_NO_RC_CANCELLING; + free_entity(a, e_no); + for (i = 0; i < 256; i++) + { + if (a->FlowControlIdTable[i] == Id) + a->FlowControlIdTable[i] = 0; + } + a->IdTable[Id] = 0; + this->Id = 0; + /* --------------------------------------------------------------- + If we send N_DISC or N_DISK_ACK after we have received OK_FC + then the card will respond with OK_FC and later with RC==OK. + If we send N_REMOVE in this state we will receive only RC==OK + This will create the state in that the XDI is waiting for the + additional RC and does not delivery the RC to the client. This + code corrects the counter of outstanding RC's in this case. + --------------------------------------------------------------- */ + if ((this->More & XMOREC) > 1) { + this->More &= ~XMOREC; + this->More |= 1; + dtrc(dprintf("XDI: correct MORE on REMOVE A(%02x) Id:%02x", + XDI_A_NR(a), Id)); + } + } + if (Rc == OK_FC) { + a->FlowControlIdTable[Ch] = Id; + a->FlowControlSkipTable[Ch] = false; + this->Rc = Rc; + this->More &= ~(XBUSY | XMOREC); + this->complete = 0xff; + xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]); + CALLBACK(a, this); + return 0; + } + /* + New protocol code sends return codes that comes from release + of flow control condition marked with DIVA_RC_TYPE_OK_FC extended + information element type. + If like return code arrives then application is able to process + all return codes self and XDI should not cances return codes. + This return code does not decrement XMOREC partial return code + counter due to fact that it was no request for this return code, + also XMOREC was not incremented. + */ + if (extended_info_type == DIVA_RC_TYPE_OK_FC) { + a->misc_flags_table[e_no] |= DIVA_MISC_FLAGS_NO_RC_CANCELLING; + this->Rc = Rc; + this->complete = 0xff; + xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]); + DBG_TRC(("XDI OK_FC A(%02x) Id:%02x Ch:%02x Rc:%02x", + XDI_A_NR(a), Id, Ch, Rc)) + CALLBACK(a, this); + return 0; + } + cancel_rc = !(a->misc_flags_table[e_no] & DIVA_MISC_FLAGS_NO_RC_CANCELLING); + if (cancel_rc && (a->FlowControlIdTable[Ch] == Id)) + { + a->FlowControlIdTable[Ch] = 0; + if ((Rc != OK) || !a->FlowControlSkipTable[Ch]) + { + this->Rc = Rc; + if (Ch == this->ReqCh) + { + this->More &= ~(XBUSY | XMOREC); + this->complete = 0xff; + } + xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]); + CALLBACK(a, this); + } + return 0; + } + if (this->More & XMOREC) + this->More--; + /* call the application callback function */ + if (((!cancel_rc) || (this->More & XMOREF)) && !(this->More & XMOREC)) { + this->Rc = Rc; + this->More &= ~XBUSY; + this->complete = 0xff; + xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]); + CALLBACK(a, this); + } + return 0; + } + /* if it's an ASSIGN return code check if it's a return */ + /* code to an ASSIGN request from us */ + if ((Rc & 0xf0) == ASSIGN_RC) { + e_no = get_assign(a, Ref); + if (e_no) { + this = entity_ptr(a, e_no); + this->Id = Id; + xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 2, a->IdTypeTable[this->No]); + /* call the application callback function */ + this->Rc = Rc; + this->More &= ~XBUSY; + this->complete = 0xff; +#if defined(DIVA_ISTREAM) /* { */ + if ((Rc == ASSIGN_OK) && a->ram_offset && + (a->IdTypeTable[this->No] == NL_ID) && + ((extended_info_type == DIVA_RC_TYPE_RX_DMA) || + (extended_info_type == DIVA_RC_TYPE_CMA_PTR)) && + extended_info) { + dword offset = (*(a->ram_offset)) (a); + dword tmp[2]; + extended_info -= offset; +#ifdef PLATFORM_GT_32BIT + a->ram_in_dw(a, (void *)ULongToPtr(extended_info), (dword *)&tmp[0], 2); +#else + a->ram_in_dw(a, (void *)extended_info, (dword *)&tmp[0], 2); +#endif + a->tx_stream[Id] = tmp[0]; + a->rx_stream[Id] = tmp[1]; + if (extended_info_type == DIVA_RC_TYPE_RX_DMA) { + DBG_TRC(("Id=0x%x RxDMA=%08x:%08x", + Id, a->tx_stream[Id], a->rx_stream[Id])) + a->misc_flags_table[this->No] |= DIVA_MISC_FLAGS_RX_DMA; + } else { + DBG_TRC(("Id=0x%x CMA=%08x:%08x", + Id, a->tx_stream[Id], a->rx_stream[Id])) + a->misc_flags_table[this->No] &= ~DIVA_MISC_FLAGS_RX_DMA; + a->rx_pos[Id] = 0; + a->rx_stream[Id] -= offset; + } + a->tx_pos[Id] = 0; + a->tx_stream[Id] -= offset; + } else { + a->tx_stream[Id] = 0; + a->rx_stream[Id] = 0; + a->misc_flags_table[this->No] &= ~DIVA_MISC_FLAGS_RX_DMA; + } +#endif /* } */ + CALLBACK(a, this); + if (Rc == ASSIGN_OK) { + a->IdTable[Id] = e_no; + } + else + { + free_entity(a, e_no); + for (i = 0; i < 256; i++) + { + if (a->FlowControlIdTable[i] == Id) + a->FlowControlIdTable[i] = 0; + } + a->IdTable[Id] = 0; + this->Id = 0; + } + return 1; + } + } + return 2; +} +/*------------------------------------------------------------------*/ +/* indication handler */ +/*------------------------------------------------------------------*/ +static byte isdn_ind(ADAPTER *a, + byte Ind, + byte Id, + byte Ch, + PBUFFER *RBuffer, + byte MInd, + word MLength) +{ + ENTITY *this; + word clength; + word offset; + BUFFERS *R; + byte *cma = NULL; +#ifdef USE_EXTENDED_DEBUGS + { + DBG_TRC(("<A%d Id=0x%x Ind=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, Id, Ind)) + } +#else + dbug(dprintf("isdn_ind(Ind=%x,Id=%x,Ch=%x)", Ind, Id, Ch)); +#endif + if (a->IdTable[Id]) { + this = entity_ptr(a, a->IdTable[Id]); + this->IndCh = Ch; + xdi_xlog_ind(XDI_A_NR(a), Id, Ch, Ind, + 0/* rnr_valid */, 0 /* rnr */, a->IdTypeTable[this->No]); + /* if the Receive More flag is not yet set, this is the */ + /* first buffer of the packet */ + if (this->RCurrent == 0xff) { + /* check for receive buffer chaining */ + if (Ind == this->MInd) { + this->complete = 0; + this->Ind = MInd; + } + else { + this->complete = 1; + this->Ind = Ind; + } + /* call the application callback function for the receive */ + /* look ahead */ + this->RLength = MLength; +#if defined(DIVA_ISTREAM) + if ((a->rx_stream[this->Id] || + (a->misc_flags_table[this->No] & DIVA_MISC_FLAGS_RX_DMA)) && + ((Ind == N_DATA) || + (a->protocol_capabilities & PROTCAP_CMA_ALLPR))) { + PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)a->io; + if (a->misc_flags_table[this->No] & DIVA_MISC_FLAGS_RX_DMA) { +#if defined(DIVA_IDI_RX_DMA) + dword d; + diva_get_dma_map_entry(\ + (struct _diva_dma_map_entry *)IoAdapter->dma_map, + (int)a->rx_stream[this->Id], (void **)&cma, &d); +#else + cma = &a->stream_buffer[0]; + cma[0] = cma[1] = cma[2] = cma[3] = 0; +#endif + this->RLength = MLength = (word)*(dword *)cma; + cma += 4; + } else { + int final = 0; + cma = &a->stream_buffer[0]; + this->RLength = MLength = (word)diva_istream_read(a, + Id, + cma, + sizeof(a->stream_buffer), + &final, NULL, NULL); + } + IoAdapter->RBuffer.length = min(MLength, (word)270); + if (IoAdapter->RBuffer.length != MLength) { + this->complete = 0; + } else { + this->complete = 1; + } + memcpy(IoAdapter->RBuffer.P, cma, IoAdapter->RBuffer.length); + this->RBuffer = (DBUFFER *)&IoAdapter->RBuffer; + } +#endif + if (!cma) { + a->ram_look_ahead(a, RBuffer, this); + } + this->RNum = 0; + CALLBACK(a, this); + /* map entity ptr, selector could be re-mapped by call to */ + /* IDI from within callback */ + this = entity_ptr(a, a->IdTable[Id]); + xdi_xlog_ind(XDI_A_NR(a), Id, Ch, Ind, + 1/* rnr_valid */, this->RNR/* rnr */, a->IdTypeTable[this->No]); + /* check for RNR */ + if (this->RNR == 1) { + this->RNR = 0; + return 1; + } + /* if no buffers are provided by the application, the */ + /* application want to copy the data itself including */ + /* N_MDATA/LL_MDATA chaining */ + if (!this->RNR && !this->RNum) { + xdi_xlog_ind(XDI_A_NR(a), Id, Ch, Ind, + 2/* rnr_valid */, 0/* rnr */, a->IdTypeTable[this->No]); + return 0; + } + /* if there is no RNR, set the More flag */ + this->RCurrent = 0; + this->ROffset = 0; + } + if (this->RNR == 2) { + if (Ind != this->MInd) { + this->RCurrent = 0xff; + this->RNR = 0; + } + return 0; + } + /* if we have received buffers from the application, copy */ + /* the data into these buffers */ + offset = 0; + R = PTR_R(a, this); + do { + if (this->ROffset == R[this->RCurrent].PLength) { + this->ROffset = 0; + this->RCurrent++; + } + if (cma) { + clength = min(MLength, (word)(R[this->RCurrent].PLength-this->ROffset)); + } else { + clength = min(a->ram_inw(a, &RBuffer->length)-offset, + R[this->RCurrent].PLength-this->ROffset); + } + if (R[this->RCurrent].P) { + if (cma) { + memcpy(PTR_P(a, this, &R[this->RCurrent].P[this->ROffset]), + &cma[offset], + clength); + } else { + a->ram_in_buffer(a, + &RBuffer->P[offset], + PTR_P(a, this, &R[this->RCurrent].P[this->ROffset]), + clength); + } + } + offset += clength; + this->ROffset += clength; + if (cma) { + if (offset >= MLength) { + break; + } + continue; + } + } while (offset < (a->ram_inw(a, &RBuffer->length))); + /* if it's the last buffer of the packet, call the */ + /* application callback function for the receive complete */ + /* call */ + if (Ind != this->MInd) { + R[this->RCurrent].PLength = this->ROffset; + if (this->ROffset) this->RCurrent++; + this->RNum = this->RCurrent; + this->RCurrent = 0xff; + this->Ind = Ind; + this->complete = 2; + xdi_xlog_ind(XDI_A_NR(a), Id, Ch, Ind, + 3/* rnr_valid */, 0/* rnr */, a->IdTypeTable[this->No]); + CALLBACK(a, this); + } + return 0; + } + return 2; +} +#if defined(XDI_USE_XLOG) +/* ----------------------------------------------------------- + This function works in the same way as xlog on the + active board + ----------------------------------------------------------- */ +static void xdi_xlog(byte *msg, word code, int length) { + xdi_dbg_xlog("\x00\x02", msg, code, length); +} +#endif +/* ----------------------------------------------------------- + This function writes the information about the Return Code + processing in the trace buffer. Trace ID is 221. + INPUT: + Adapter - system unicue adapter number (0 ... 255) + Id - Id of the entity that had sent this return code + Ch - Channel of the entity that had sent this return code + Rc - return code value + cb: (0...2) + switch (cb) { + case 0: printf ("DELIVERY"); break; + case 1: printf ("CALLBACK"); break; + case 2: printf ("ASSIGN"); break; + } + DELIVERY - have entered isdn_rc with this RC + CALLBACK - about to make callback to the application + for this RC + ASSIGN - about to make callback for RC that is result + of ASSIGN request. It is no DELIVERY message + before of this message + type - the Id that was sent by the ASSIGN of this entity. + This should be global Id like NL_ID, DSIG_ID, MAN_ID. + An unknown Id will cause "?-" in the front of the request. + In this case the log.c is to be extended. + ----------------------------------------------------------- */ +static void xdi_xlog_rc_event(byte Adapter, + byte Id, byte Ch, byte Rc, byte cb, byte type) { +#if defined(XDI_USE_XLOG) + word LogInfo[4]; + PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8))); + PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8))); + PUT_WORD(&LogInfo[2], ((word)Rc | (word)(type << 8))); + PUT_WORD(&LogInfo[3], cb); + xdi_xlog((byte *)&LogInfo[0], 221, sizeof(LogInfo)); +#endif +} +/* ------------------------------------------------------------------------ + This function writes the information about the request processing + in the trace buffer. Trace ID is 220. + INPUT: + Adapter - system unicue adapter number (0 ... 255) + Id - Id of the entity that had sent this request + Ch - Channel of the entity that had sent this request + Req - Code of the request + type - the Id that was sent by the ASSIGN of this entity. + This should be global Id like NL_ID, DSIG_ID, MAN_ID. + An unknown Id will cause "?-" in the front of the request. + In this case the log.c is to be extended. + ------------------------------------------------------------------------ */ +static void xdi_xlog_request(byte Adapter, byte Id, + byte Ch, byte Req, byte type) { +#if defined(XDI_USE_XLOG) + word LogInfo[3]; + PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8))); + PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8))); + PUT_WORD(&LogInfo[2], ((word)Req | (word)(type << 8))); + xdi_xlog((byte *)&LogInfo[0], 220, sizeof(LogInfo)); +#endif +} +/* ------------------------------------------------------------------------ + This function writes the information about the indication processing + in the trace buffer. Trace ID is 222. + INPUT: + Adapter - system unicue adapter number (0 ... 255) + Id - Id of the entity that had sent this indication + Ch - Channel of the entity that had sent this indication + Ind - Code of the indication + rnr_valid: (0 .. 3) supported + switch (rnr_valid) { + case 0: printf ("DELIVERY"); break; + case 1: printf ("RNR=%d", rnr); + case 2: printf ("RNum=0"); + case 3: printf ("COMPLETE"); + } + DELIVERY - indication entered isdn_rc function + RNR=... - application had returned RNR=... after the + look ahead callback + RNum=0 - application had not returned any buffer to copy + this indication and will copy it self + COMPLETE - XDI had copied the data to the buffers provided + bu the application and is about to issue the + final callback + rnr: Look case 1 of the rnr_valid + type: the Id that was sent by the ASSIGN of this entity. This should + be global Id like NL_ID, DSIG_ID, MAN_ID. An unknown Id will + cause "?-" in the front of the request. In this case the + log.c is to be extended. + ------------------------------------------------------------------------ */ +static void xdi_xlog_ind(byte Adapter, + byte Id, + byte Ch, + byte Ind, + byte rnr_valid, + byte rnr, + byte type) { +#if defined(XDI_USE_XLOG) + word LogInfo[4]; + PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8))); + PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8))); + PUT_WORD(&LogInfo[2], ((word)Ind | (word)(type << 8))); + PUT_WORD(&LogInfo[3], ((word)rnr | (word)(rnr_valid << 8))); + xdi_xlog((byte *)&LogInfo[0], 222, sizeof(LogInfo)); +#endif +} diff --git a/drivers/isdn/hardware/eicon/di.h b/drivers/isdn/hardware/eicon/di.h new file mode 100644 index 000000000..ff26c6563 --- /dev/null +++ b/drivers/isdn/hardware/eicon/di.h @@ -0,0 +1,118 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +/* + * some macros for detailed trace management + */ +#include "di_dbg.h" +/*****************************************************************************/ +#define XMOREC 0x1f +#define XMOREF 0x20 +#define XBUSY 0x40 +#define RMORE 0x80 +#define DIVA_MISC_FLAGS_REMOVE_PENDING 0x01 +#define DIVA_MISC_FLAGS_NO_RC_CANCELLING 0x02 +#define DIVA_MISC_FLAGS_RX_DMA 0x04 +/* structure for all information we have to keep on a per */ +/* adapater basis */ +typedef struct adapter_s ADAPTER; +struct adapter_s { + void *io; + byte IdTable[256]; + byte IdTypeTable[256]; + byte FlowControlIdTable[256]; + byte FlowControlSkipTable[256]; + byte ReadyInt; + byte RcExtensionSupported; + byte misc_flags_table[256]; + dword protocol_capabilities; + byte (*ram_in)(ADAPTER *a, void *adr); + word (*ram_inw)(ADAPTER *a, void *adr); + void (*ram_in_buffer)(ADAPTER *a, void *adr, void *P, word length); + void (*ram_look_ahead)(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e); + void (*ram_out)(ADAPTER *a, void *adr, byte data); + void (*ram_outw)(ADAPTER *a, void *adr, word data); + void (*ram_out_buffer)(ADAPTER *a, void *adr, void *P, word length); + void (*ram_inc)(ADAPTER *a, void *adr); +#if defined(DIVA_ISTREAM) + dword rx_stream[256]; + dword tx_stream[256]; + word tx_pos[256]; + word rx_pos[256]; + byte stream_buffer[2512]; + dword (*ram_offset)(ADAPTER *a); + void (*ram_out_dw)(ADAPTER *a, + void *addr, + const dword *data, + int dwords); + void (*ram_in_dw)(ADAPTER *a, + void *addr, + dword *data, + int dwords); + void (*istream_wakeup)(ADAPTER *a); +#else + byte stream_buffer[4]; +#endif +}; +/*------------------------------------------------------------------*/ +/* public functions of IDI common code */ +/*------------------------------------------------------------------*/ +void pr_out(ADAPTER *a); +byte pr_dpc(ADAPTER *a); +byte scom_test_int(ADAPTER *a); +void scom_clear_int(ADAPTER *a); +/*------------------------------------------------------------------*/ +/* OS specific functions used by IDI common code */ +/*------------------------------------------------------------------*/ +void free_entity(ADAPTER *a, byte e_no); +void assign_queue(ADAPTER *a, byte e_no, word ref); +byte get_assign(ADAPTER *a, word ref); +void req_queue(ADAPTER *a, byte e_no); +byte look_req(ADAPTER *a); +void next_req(ADAPTER *a); +ENTITY *entity_ptr(ADAPTER *a, byte e_no); +#if defined(DIVA_ISTREAM) +struct _diva_xdi_stream_interface; +void diva_xdi_provide_istream_info(ADAPTER *a, + struct _diva_xdi_stream_interface *pI); +void pr_stream(ADAPTER *a); +int diva_istream_write(void *context, + int Id, + void *data, + int length, + int final, + byte usr1, + byte usr2); +int diva_istream_read(void *context, + int Id, + void *data, + int max_length, + int *final, + byte *usr1, + byte *usr2); +#if defined(DIVA_IDI_RX_DMA) +#include "diva_dma.h" +#endif +#endif diff --git a/drivers/isdn/hardware/eicon/di_dbg.h b/drivers/isdn/hardware/eicon/di_dbg.h new file mode 100644 index 000000000..1380b60e5 --- /dev/null +++ b/drivers/isdn/hardware/eicon/di_dbg.h @@ -0,0 +1,37 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_DI_DBG_INC__ +#define __DIVA_DI_DBG_INC__ +#if !defined(dtrc) +#define dtrc(a) +#endif +#if !defined(dbug) +#define dbug(a) +#endif +#if !defined USE_EXTENDED_DEBUGS +extern void (*dprintf)(char*, ...); +#endif +#endif diff --git a/drivers/isdn/hardware/eicon/di_defs.h b/drivers/isdn/hardware/eicon/di_defs.h new file mode 100644 index 000000000..a5094d221 --- /dev/null +++ b/drivers/isdn/hardware/eicon/di_defs.h @@ -0,0 +1,181 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef _DI_DEFS_ +#define _DI_DEFS_ +/* typedefs for our data structures */ +typedef struct get_name_s GET_NAME; +/* The entity_s structure is used to pass all + parameters between application and IDI */ +typedef struct entity_s ENTITY; +typedef struct buffers_s BUFFERS; +typedef struct postcall_s POSTCALL; +typedef struct get_para_s GET_PARA; +#define BOARD_NAME_LENGTH 9 +#define IDI_CALL_LINK_T +#define IDI_CALL_ENTITY_T +/* typedef void ( * IDI_CALL)(ENTITY *); */ +/* -------------------------------------------------------- + IDI_CALL + -------------------------------------------------------- */ +typedef void (IDI_CALL_LINK_T *IDI_CALL)(ENTITY IDI_CALL_ENTITY_T *); +typedef struct { + word length; /* length of data/parameter field */ + byte P[270]; /* data/parameter field */ +} DBUFFER; +struct get_name_s { + word command; /* command = 0x0100 */ + byte name[BOARD_NAME_LENGTH]; +}; +struct postcall_s { + word command; /* command = 0x0300 */ + word dummy; /* not used */ + void (*callback)(void *); /* call back */ + void *context; /* context pointer */ +}; +#define REQ_PARA 0x0600 /* request command line parameters */ +#define REQ_PARA_LEN 1 /* number of data bytes */ +#define L1_STARTUP_DOWN_POS 0 /* '-y' command line parameter in......*/ +#define L1_STARTUP_DOWN_MSK 0x01 /* first byte position (index 0) with value 0x01 */ +struct get_para_s { + word command; /* command = 0x0600 */ + byte len; /* max length of para field in bytes */ + byte para[REQ_PARA_LEN]; /* parameter field */ +}; +struct buffers_s { + word PLength; + byte *P; +}; +struct entity_s { + byte Req; /* pending request */ + byte Rc; /* return code received */ + byte Ind; /* indication received */ + byte ReqCh; /* channel of current Req */ + byte RcCh; /* channel of current Rc */ + byte IndCh; /* channel of current Ind */ + byte Id; /* ID used by this entity */ + byte GlobalId; /* reserved field */ + byte XNum; /* number of X-buffers */ + byte RNum; /* number of R-buffers */ + BUFFERS *X; /* pointer to X-buffer list */ + BUFFERS *R; /* pointer to R-buffer list */ + word RLength; /* length of current R-data */ + DBUFFER *RBuffer; /* buffer of current R-data */ + byte RNR; /* receive not ready flag */ + byte complete; /* receive complete status */ + IDI_CALL callback; + word user[2]; + /* fields used by the driver internally */ + byte No; /* entity number */ + byte reserved2; /* reserved field */ + byte More; /* R/X More flags */ + byte MInd; /* MDATA coding for this ID */ + byte XCurrent; /* current transmit buffer */ + byte RCurrent; /* current receive buffer */ + word XOffset; /* offset in x-buffer */ + word ROffset; /* offset in r-buffer */ +}; +typedef struct { + byte type; + byte channels; + word features; + IDI_CALL request; +} DESCRIPTOR; +/* descriptor type field coding */ +#define IDI_ADAPTER_S 1 +#define IDI_ADAPTER_PR 2 +#define IDI_ADAPTER_DIVA 3 +#define IDI_ADAPTER_MAESTRA 4 +#define IDI_VADAPTER 0x40 +#define IDI_DRIVER 0x80 +#define IDI_DADAPTER 0xfd +#define IDI_DIDDPNP 0xfe +#define IDI_DIMAINT 0xff +/* Hardware IDs ISA PNP */ +#define HW_ID_DIVA_PRO 3 /* same as IDI_ADAPTER_DIVA */ +#define HW_ID_MAESTRA 4 /* same as IDI_ADAPTER_MAESTRA */ +#define HW_ID_PICCOLA 5 +#define HW_ID_DIVA_PRO20 6 +#define HW_ID_DIVA20 7 +#define HW_ID_DIVA_PRO20_U 8 +#define HW_ID_DIVA20_U 9 +#define HW_ID_DIVA30 10 +#define HW_ID_DIVA30_U 11 +/* Hardware IDs PCI */ +#define HW_ID_EICON_PCI 0x1133 +#define HW_ID_SIEMENS_PCI 0x8001 /* unused SubVendor ID for Siemens Cornet-N cards */ +#define HW_ID_PROTTYPE_CORNETN 0x0014 /* SubDevice ID for Siemens Cornet-N cards */ +#define HW_ID_FUJITSU_SIEMENS_PCI 0x110A /* SubVendor ID for Fujitsu Siemens */ +#define HW_ID_GS03_PCI 0x0021 /* SubDevice ID for Fujitsu Siemens ISDN S0 card */ +#define HW_ID_DIVA_PRO20_PCI 0xe001 +#define HW_ID_DIVA20_PCI 0xe002 +#define HW_ID_DIVA_PRO20_PCI_U 0xe003 +#define HW_ID_DIVA20_PCI_U 0xe004 +#define HW_ID_DIVA201_PCI 0xe005 +#define HW_ID_DIVA_CT_ST 0xe006 +#define HW_ID_DIVA_CT_U 0xe007 +#define HW_ID_DIVA_CTL_ST 0xe008 +#define HW_ID_DIVA_CTL_U 0xe009 +#define HW_ID_DIVA_ISDN_V90_PCI 0xe00a +#define HW_ID_DIVA202_PCI_ST 0xe00b +#define HW_ID_DIVA202_PCI_U 0xe00c +#define HW_ID_DIVA_PRO30_PCI 0xe00d +#define HW_ID_MAESTRA_PCI 0xe010 +#define HW_ID_MAESTRAQ_PCI 0xe012 +#define HW_ID_DSRV_Q8M_V2_PCI 0xe013 +#define HW_ID_MAESTRAP_PCI 0xe014 +#define HW_ID_DSRV_P30M_V2_PCI 0xe015 +#define HW_ID_DSRV_VOICE_Q8M_PCI 0xe016 +#define HW_ID_DSRV_VOICE_Q8M_V2_PCI 0xe017 +#define HW_ID_DSRV_B2M_V2_PCI 0xe018 +#define HW_ID_DSRV_VOICE_P30M_V2_PCI 0xe019 +#define HW_ID_DSRV_B2F_PCI 0xe01a +#define HW_ID_DSRV_VOICE_B2M_V2_PCI 0xe01b +/* Hardware IDs USB */ +#define EICON_USB_VENDOR_ID 0x071D +#define HW_ID_DIVA_USB_REV1 0x1000 +#define HW_ID_DIVA_USB_REV2 0x1003 +#define HW_ID_TELEDAT_SURF_USB_REV2 0x1004 +#define HW_ID_TELEDAT_SURF_USB_REV1 0x2000 +/* -------------------------------------------------------------------------- + Adapter array change notification framework + -------------------------------------------------------------------------- */ +typedef void (IDI_CALL_LINK_T *didd_adapter_change_callback_t)(void IDI_CALL_ENTITY_T *context, DESCRIPTOR *adapter, int removal); +/* -------------------------------------------------------------------------- */ +#define DI_VOICE 0x0 /* obsolete define */ +#define DI_FAX3 0x1 +#define DI_MODEM 0x2 +#define DI_POST 0x4 +#define DI_V110 0x8 +#define DI_V120 0x10 +#define DI_POTS 0x20 +#define DI_CODEC 0x40 +#define DI_MANAGE 0x80 +#define DI_V_42 0x0100 +#define DI_EXTD_FAX 0x0200 /* Extended FAX (ECM, 2D, T.6, Polling) */ +#define DI_AT_PARSER 0x0400 /* Build-in AT Parser in the L2 */ +#define DI_VOICE_OVER_IP 0x0800 /* Voice over IP support */ +typedef void (IDI_CALL_LINK_T *_IDI_CALL)(void *, ENTITY *); +#endif diff --git a/drivers/isdn/hardware/eicon/did_vers.h b/drivers/isdn/hardware/eicon/did_vers.h new file mode 100644 index 000000000..fa8db8249 --- /dev/null +++ b/drivers/isdn/hardware/eicon/did_vers.h @@ -0,0 +1,26 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +static char diva_didd_common_code_build[] = "102-51"; diff --git a/drivers/isdn/hardware/eicon/diddfunc.c b/drivers/isdn/hardware/eicon/diddfunc.c new file mode 100644 index 000000000..b0b23ed8b --- /dev/null +++ b/drivers/isdn/hardware/eicon/diddfunc.c @@ -0,0 +1,115 @@ +/* $Id: diddfunc.c,v 1.14.6.2 2004/08/28 20:03:53 armin Exp $ + * + * DIDD Interface module for Eicon active cards. + * + * Functions are in dadapter.c + * + * Copyright 2002-2003 by Armin Schindler (mac@melware.de) + * Copyright 2002-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include "platform.h" +#include "di_defs.h" +#include "dadapter.h" +#include "divasync.h" + +#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) +#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) + + +extern void DIVA_DIDD_Read(void *, int); +extern char *DRIVERRELEASE_DIDD; +static dword notify_handle; +static DESCRIPTOR _DAdapter; + +/* + * didd callback function + */ +static void *didd_callback(void *context, DESCRIPTOR *adapter, + int removal) +{ + if (adapter->type == IDI_DADAPTER) { + DBG_ERR(("Notification about IDI_DADAPTER change ! Oops.")) + return (NULL); + } else if (adapter->type == IDI_DIMAINT) { + if (removal) { + DbgDeregister(); + } else { + DbgRegister("DIDD", DRIVERRELEASE_DIDD, DBG_DEFAULT); + } + } + return (NULL); +} + +/* + * connect to didd + */ +static int __init connect_didd(void) +{ + int x = 0; + int dadapter = 0; + IDI_SYNC_REQ req; + DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS]; + + DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table)); + + for (x = 0; x < MAX_DESCRIPTORS; x++) { + if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */ + dadapter = 1; + memcpy(&_DAdapter, &DIDD_Table[x], sizeof(_DAdapter)); + req.didd_notify.e.Req = 0; + req.didd_notify.e.Rc = + IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY; + req.didd_notify.info.callback = (void *)didd_callback; + req.didd_notify.info.context = NULL; + _DAdapter.request((ENTITY *)&req); + if (req.didd_notify.e.Rc != 0xff) + return (0); + notify_handle = req.didd_notify.info.handle; + } else if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */ + DbgRegister("DIDD", DRIVERRELEASE_DIDD, DBG_DEFAULT); + } + } + return (dadapter); +} + +/* + * disconnect from didd + */ +static void __exit disconnect_didd(void) +{ + IDI_SYNC_REQ req; + + req.didd_notify.e.Req = 0; + req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY; + req.didd_notify.info.handle = notify_handle; + _DAdapter.request((ENTITY *)&req); +} + +/* + * init + */ +int __init diddfunc_init(void) +{ + diva_didd_load_time_init(); + + if (!connect_didd()) { + DBG_ERR(("init: failed to connect to DIDD.")) + diva_didd_load_time_finit(); + return (0); + } + return (1); +} + +/* + * finit + */ +void __exit diddfunc_finit(void) +{ + DbgDeregister(); + disconnect_didd(); + diva_didd_load_time_finit(); +} diff --git a/drivers/isdn/hardware/eicon/diva.c b/drivers/isdn/hardware/eicon/diva.c new file mode 100644 index 000000000..1b25d8bc1 --- /dev/null +++ b/drivers/isdn/hardware/eicon/diva.c @@ -0,0 +1,666 @@ +// SPDX-License-Identifier: GPL-2.0 +/* $Id: diva.c,v 1.21.4.1 2004/05/08 14:33:43 armin Exp $ */ + +#define CARDTYPE_H_WANT_DATA 1 +#define CARDTYPE_H_WANT_IDI_DATA 0 +#define CARDTYPE_H_WANT_RESOURCE_DATA 0 +#define CARDTYPE_H_WANT_FILE_DATA 0 + +#include "platform.h" +#include "debuglib.h" +#include "cardtype.h" +#include "pc.h" +#include "di_defs.h" +#include "di.h" +#include "io.h" +#include "pc_maint.h" +#include "xdi_msg.h" +#include "xdi_adapter.h" +#include "diva_pci.h" +#include "diva.h" + +#ifdef CONFIG_ISDN_DIVAS_PRIPCI +#include "os_pri.h" +#endif +#ifdef CONFIG_ISDN_DIVAS_BRIPCI +#include "os_bri.h" +#include "os_4bri.h" +#endif + +PISDN_ADAPTER IoAdapters[MAX_ADAPTER]; +extern IDI_CALL Requests[MAX_ADAPTER]; +extern int create_adapter_proc(diva_os_xdi_adapter_t *a); +extern void remove_adapter_proc(diva_os_xdi_adapter_t *a); + +#define DivaIdiReqFunc(N) \ + static void DivaIdiRequest##N(ENTITY *e) \ + { if (IoAdapters[N]) (*IoAdapters[N]->DIRequest)(IoAdapters[N], e); } + +/* +** Create own 32 Adapters +*/ +DivaIdiReqFunc(0) +DivaIdiReqFunc(1) +DivaIdiReqFunc(2) +DivaIdiReqFunc(3) +DivaIdiReqFunc(4) +DivaIdiReqFunc(5) +DivaIdiReqFunc(6) +DivaIdiReqFunc(7) +DivaIdiReqFunc(8) +DivaIdiReqFunc(9) +DivaIdiReqFunc(10) +DivaIdiReqFunc(11) +DivaIdiReqFunc(12) +DivaIdiReqFunc(13) +DivaIdiReqFunc(14) +DivaIdiReqFunc(15) +DivaIdiReqFunc(16) +DivaIdiReqFunc(17) +DivaIdiReqFunc(18) +DivaIdiReqFunc(19) +DivaIdiReqFunc(20) +DivaIdiReqFunc(21) +DivaIdiReqFunc(22) +DivaIdiReqFunc(23) +DivaIdiReqFunc(24) +DivaIdiReqFunc(25) +DivaIdiReqFunc(26) +DivaIdiReqFunc(27) +DivaIdiReqFunc(28) +DivaIdiReqFunc(29) +DivaIdiReqFunc(30) +DivaIdiReqFunc(31) + +/* +** LOCALS +*/ +static LIST_HEAD(adapter_queue); + +typedef struct _diva_get_xlog { + word command; + byte req; + byte rc; + byte data[sizeof(struct mi_pc_maint)]; +} diva_get_xlog_t; + +typedef struct _diva_supported_cards_info { + int CardOrdinal; + diva_init_card_proc_t init_card; +} diva_supported_cards_info_t; + +static diva_supported_cards_info_t divas_supported_cards[] = { +#ifdef CONFIG_ISDN_DIVAS_PRIPCI + /* + PRI Cards + */ + {CARDTYPE_DIVASRV_P_30M_PCI, diva_pri_init_card}, + /* + PRI Rev.2 Cards + */ + {CARDTYPE_DIVASRV_P_30M_V2_PCI, diva_pri_init_card}, + /* + PRI Rev.2 VoIP Cards + */ + {CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI, diva_pri_init_card}, +#endif +#ifdef CONFIG_ISDN_DIVAS_BRIPCI + /* + 4BRI Rev 1 Cards + */ + {CARDTYPE_DIVASRV_Q_8M_PCI, diva_4bri_init_card}, + {CARDTYPE_DIVASRV_VOICE_Q_8M_PCI, diva_4bri_init_card}, + /* + 4BRI Rev 2 Cards + */ + {CARDTYPE_DIVASRV_Q_8M_V2_PCI, diva_4bri_init_card}, + {CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI, diva_4bri_init_card}, + /* + 4BRI Based BRI Rev 2 Cards + */ + {CARDTYPE_DIVASRV_B_2M_V2_PCI, diva_4bri_init_card}, + {CARDTYPE_DIVASRV_B_2F_PCI, diva_4bri_init_card}, + {CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI, diva_4bri_init_card}, + /* + BRI + */ + {CARDTYPE_MAESTRA_PCI, diva_bri_init_card}, +#endif + + /* + EOL + */ + {-1} +}; + +static void diva_init_request_array(void); +static void *divas_create_pci_card(int handle, void *pci_dev_handle); + +static diva_os_spin_lock_t adapter_lock; + +static int diva_find_free_adapters(int base, int nr) +{ + int i; + + for (i = 0; i < nr; i++) { + if (IoAdapters[base + i]) { + return (-1); + } + } + + return (0); +} + +static diva_os_xdi_adapter_t *diva_q_get_next(struct list_head *what) +{ + diva_os_xdi_adapter_t *a = NULL; + + if (what && (what->next != &adapter_queue)) + a = list_entry(what->next, diva_os_xdi_adapter_t, link); + + return (a); +} + +/* -------------------------------------------------------------------------- + Add card to the card list + -------------------------------------------------------------------------- */ +void *diva_driver_add_card(void *pdev, unsigned long CardOrdinal) +{ + diva_os_spin_lock_magic_t old_irql; + diva_os_xdi_adapter_t *pdiva, *pa; + int i, j, max, nr; + + for (i = 0; divas_supported_cards[i].CardOrdinal != -1; i++) { + if (divas_supported_cards[i].CardOrdinal == CardOrdinal) { + if (!(pdiva = divas_create_pci_card(i, pdev))) { + return NULL; + } + switch (CardOrdinal) { + case CARDTYPE_DIVASRV_Q_8M_PCI: + case CARDTYPE_DIVASRV_VOICE_Q_8M_PCI: + case CARDTYPE_DIVASRV_Q_8M_V2_PCI: + case CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI: + max = MAX_ADAPTER - 4; + nr = 4; + break; + + default: + max = MAX_ADAPTER; + nr = 1; + } + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card"); + + for (i = 0; i < max; i++) { + if (!diva_find_free_adapters(i, nr)) { + pdiva->controller = i + 1; + pdiva->xdi_adapter.ANum = pdiva->controller; + IoAdapters[i] = &pdiva->xdi_adapter; + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card"); + create_adapter_proc(pdiva); /* add adapter to proc file system */ + + DBG_LOG(("add %s:%d", + CardProperties + [CardOrdinal].Name, + pdiva->controller)) + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card"); + pa = pdiva; + for (j = 1; j < nr; j++) { /* slave adapters, if any */ + pa = diva_q_get_next(&pa->link); + if (pa && !pa->interface.cleanup_adapter_proc) { + pa->controller = i + 1 + j; + pa->xdi_adapter.ANum = pa->controller; + IoAdapters[i + j] = &pa->xdi_adapter; + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card"); + DBG_LOG(("add slave adapter (%d)", + pa->controller)) + create_adapter_proc(pa); /* add adapter to proc file system */ + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card"); + } else { + DBG_ERR(("slave adapter problem")) + break; + } + } + + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card"); + return (pdiva); + } + } + + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card"); + + /* + Not able to add adapter - remove it and return error + */ + DBG_ERR(("can not alloc request array")) + diva_driver_remove_card(pdiva); + + return NULL; + } + } + + return NULL; +} + +/* -------------------------------------------------------------------------- + Called on driver load, MAIN, main, DriverEntry + -------------------------------------------------------------------------- */ +int divasa_xdi_driver_entry(void) +{ + diva_os_initialize_spin_lock(&adapter_lock, "adapter"); + memset(&IoAdapters[0], 0x00, sizeof(IoAdapters)); + diva_init_request_array(); + + return (0); +} + +/* -------------------------------------------------------------------------- + Remove adapter from list + -------------------------------------------------------------------------- */ +static diva_os_xdi_adapter_t *get_and_remove_from_queue(void) +{ + diva_os_spin_lock_magic_t old_irql; + diva_os_xdi_adapter_t *a = NULL; + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "driver_unload"); + + if (!list_empty(&adapter_queue)) { + a = list_entry(adapter_queue.next, diva_os_xdi_adapter_t, link); + list_del(adapter_queue.next); + } + + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "driver_unload"); + return (a); +} + +/* -------------------------------------------------------------------------- + Remove card from the card list + -------------------------------------------------------------------------- */ +void diva_driver_remove_card(void *pdiva) +{ + diva_os_spin_lock_magic_t old_irql; + diva_os_xdi_adapter_t *a[4]; + diva_os_xdi_adapter_t *pa; + int i; + + pa = a[0] = (diva_os_xdi_adapter_t *) pdiva; + a[1] = a[2] = a[3] = NULL; + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "remode adapter"); + + for (i = 1; i < 4; i++) { + if ((pa = diva_q_get_next(&pa->link)) + && !pa->interface.cleanup_adapter_proc) { + a[i] = pa; + } else { + break; + } + } + + for (i = 0; ((i < 4) && a[i]); i++) { + list_del(&a[i]->link); + } + + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "driver_unload"); + + (*(a[0]->interface.cleanup_adapter_proc)) (a[0]); + + for (i = 0; i < 4; i++) { + if (a[i]) { + if (a[i]->controller) { + DBG_LOG(("remove adapter (%d)", + a[i]->controller)) IoAdapters[a[i]->controller - 1] = NULL; + remove_adapter_proc(a[i]); + } + diva_os_free(0, a[i]); + } + } +} + +/* -------------------------------------------------------------------------- + Create diva PCI adapter and init internal adapter structures + -------------------------------------------------------------------------- */ +static void *divas_create_pci_card(int handle, void *pci_dev_handle) +{ + diva_supported_cards_info_t *pI = &divas_supported_cards[handle]; + diva_os_spin_lock_magic_t old_irql; + diva_os_xdi_adapter_t *a; + + DBG_LOG(("found %d-%s", pI->CardOrdinal, CardProperties[pI->CardOrdinal].Name)) + + if (!(a = (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a)))) { + DBG_ERR(("A: can't alloc adapter")); + return NULL; + } + + memset(a, 0x00, sizeof(*a)); + + a->CardIndex = handle; + a->CardOrdinal = pI->CardOrdinal; + a->Bus = DIVAS_XDI_ADAPTER_BUS_PCI; + a->xdi_adapter.cardType = a->CardOrdinal; + a->resources.pci.bus = diva_os_get_pci_bus(pci_dev_handle); + a->resources.pci.func = diva_os_get_pci_func(pci_dev_handle); + a->resources.pci.hdev = pci_dev_handle; + + /* + Add master adapter first, so slave adapters will receive higher + numbers as master adapter + */ + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "found_pci_card"); + list_add_tail(&a->link, &adapter_queue); + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "found_pci_card"); + + if ((*(pI->init_card)) (a)) { + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "found_pci_card"); + list_del(&a->link); + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "found_pci_card"); + diva_os_free(0, a); + DBG_ERR(("A: can't get adapter resources")); + return NULL; + } + + return (a); +} + +/* -------------------------------------------------------------------------- + Called on driver unload FINIT, finit, Unload + -------------------------------------------------------------------------- */ +void divasa_xdi_driver_unload(void) +{ + diva_os_xdi_adapter_t *a; + + while ((a = get_and_remove_from_queue())) { + if (a->interface.cleanup_adapter_proc) { + (*(a->interface.cleanup_adapter_proc)) (a); + } + if (a->controller) { + IoAdapters[a->controller - 1] = NULL; + remove_adapter_proc(a); + } + diva_os_free(0, a); + } + diva_os_destroy_spin_lock(&adapter_lock, "adapter"); +} + +/* +** Receive and process command from user mode utility +*/ +void *diva_xdi_open_adapter(void *os_handle, const void __user *src, + int length, void *mptr, + divas_xdi_copy_from_user_fn_t cp_fn) +{ + diva_xdi_um_cfg_cmd_t *msg = (diva_xdi_um_cfg_cmd_t *)mptr; + diva_os_xdi_adapter_t *a = NULL; + diva_os_spin_lock_magic_t old_irql; + struct list_head *tmp; + + if (length < sizeof(diva_xdi_um_cfg_cmd_t)) { + DBG_ERR(("A: A(?) open, msg too small (%d < %d)", + length, sizeof(diva_xdi_um_cfg_cmd_t))) + return NULL; + } + if ((*cp_fn) (os_handle, msg, src, sizeof(*msg)) <= 0) { + DBG_ERR(("A: A(?) open, write error")) + return NULL; + } + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "open_adapter"); + list_for_each(tmp, &adapter_queue) { + a = list_entry(tmp, diva_os_xdi_adapter_t, link); + if (a->controller == (int)msg->adapter) + break; + a = NULL; + } + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "open_adapter"); + + if (!a) { + DBG_ERR(("A: A(%d) open, adapter not found", msg->adapter)) + } + + return (a); +} + +/* +** Easy cleanup mailbox status +*/ +void diva_xdi_close_adapter(void *adapter, void *os_handle) +{ + diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter; + + a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY; + if (a->xdi_mbox.data) { + diva_os_free(0, a->xdi_mbox.data); + a->xdi_mbox.data = NULL; + } +} + +int +diva_xdi_write(void *adapter, void *os_handle, const void __user *src, + int length, void *mptr, + divas_xdi_copy_from_user_fn_t cp_fn) +{ + diva_xdi_um_cfg_cmd_t *msg = (diva_xdi_um_cfg_cmd_t *)mptr; + diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter; + void *data; + + if (a->xdi_mbox.status & DIVA_XDI_MBOX_BUSY) { + DBG_ERR(("A: A(%d) write, mbox busy", a->controller)) + return (-1); + } + + if (length < sizeof(diva_xdi_um_cfg_cmd_t)) { + DBG_ERR(("A: A(%d) write, message too small (%d < %d)", + a->controller, length, + sizeof(diva_xdi_um_cfg_cmd_t))) + return (-3); + } + + if (!(data = diva_os_malloc(0, length))) { + DBG_ERR(("A: A(%d) write, ENOMEM", a->controller)) + return (-2); + } + + if (msg) { + *(diva_xdi_um_cfg_cmd_t *)data = *msg; + length = (*cp_fn) (os_handle, (char *)data + sizeof(*msg), + src + sizeof(*msg), length - sizeof(*msg)); + } else { + length = (*cp_fn) (os_handle, data, src, length); + } + if (length > 0) { + if ((*(a->interface.cmd_proc)) + (a, (diva_xdi_um_cfg_cmd_t *) data, length)) { + length = -3; + } + } else { + DBG_ERR(("A: A(%d) write error (%d)", a->controller, + length)) + } + + diva_os_free(0, data); + + return (length); +} + +/* +** Write answers to user mode utility, if any +*/ +int +diva_xdi_read(void *adapter, void *os_handle, void __user *dst, + int max_length, divas_xdi_copy_to_user_fn_t cp_fn) +{ + diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter; + int ret; + + if (!(a->xdi_mbox.status & DIVA_XDI_MBOX_BUSY)) { + DBG_ERR(("A: A(%d) rx mbox empty", a->controller)) + return (-1); + } + if (!a->xdi_mbox.data) { + a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY; + DBG_ERR(("A: A(%d) rx ENOMEM", a->controller)) + return (-2); + } + + if (max_length < a->xdi_mbox.data_length) { + DBG_ERR(("A: A(%d) rx buffer too short(%d < %d)", + a->controller, max_length, + a->xdi_mbox.data_length)) + return (-3); + } + + ret = (*cp_fn) (os_handle, dst, a->xdi_mbox.data, + a->xdi_mbox.data_length); + if (ret > 0) { + diva_os_free(0, a->xdi_mbox.data); + a->xdi_mbox.data = NULL; + a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY; + } + + return (ret); +} + + +irqreturn_t diva_os_irq_wrapper(int irq, void *context) +{ + diva_os_xdi_adapter_t *a = context; + diva_xdi_clear_interrupts_proc_t clear_int_proc; + + if (!a || !a->xdi_adapter.diva_isr_handler) + return IRQ_NONE; + + if ((clear_int_proc = a->clear_interrupts_proc)) { + (*clear_int_proc) (a); + a->clear_interrupts_proc = NULL; + return IRQ_HANDLED; + } + + (*(a->xdi_adapter.diva_isr_handler)) (&a->xdi_adapter); + return IRQ_HANDLED; +} + +static void diva_init_request_array(void) +{ + Requests[0] = DivaIdiRequest0; + Requests[1] = DivaIdiRequest1; + Requests[2] = DivaIdiRequest2; + Requests[3] = DivaIdiRequest3; + Requests[4] = DivaIdiRequest4; + Requests[5] = DivaIdiRequest5; + Requests[6] = DivaIdiRequest6; + Requests[7] = DivaIdiRequest7; + Requests[8] = DivaIdiRequest8; + Requests[9] = DivaIdiRequest9; + Requests[10] = DivaIdiRequest10; + Requests[11] = DivaIdiRequest11; + Requests[12] = DivaIdiRequest12; + Requests[13] = DivaIdiRequest13; + Requests[14] = DivaIdiRequest14; + Requests[15] = DivaIdiRequest15; + Requests[16] = DivaIdiRequest16; + Requests[17] = DivaIdiRequest17; + Requests[18] = DivaIdiRequest18; + Requests[19] = DivaIdiRequest19; + Requests[20] = DivaIdiRequest20; + Requests[21] = DivaIdiRequest21; + Requests[22] = DivaIdiRequest22; + Requests[23] = DivaIdiRequest23; + Requests[24] = DivaIdiRequest24; + Requests[25] = DivaIdiRequest25; + Requests[26] = DivaIdiRequest26; + Requests[27] = DivaIdiRequest27; + Requests[28] = DivaIdiRequest28; + Requests[29] = DivaIdiRequest29; + Requests[30] = DivaIdiRequest30; + Requests[31] = DivaIdiRequest31; +} + +void diva_xdi_display_adapter_features(int card) +{ + dword features; + if (!card || ((card - 1) >= MAX_ADAPTER) || !IoAdapters[card - 1]) { + return; + } + card--; + features = IoAdapters[card]->Properties.Features; + + DBG_LOG(("FEATURES FOR ADAPTER: %d", card + 1)) + DBG_LOG((" DI_FAX3 : %s", + (features & DI_FAX3) ? "Y" : "N")) + DBG_LOG((" DI_MODEM : %s", + (features & DI_MODEM) ? "Y" : "N")) + DBG_LOG((" DI_POST : %s", + (features & DI_POST) ? "Y" : "N")) + DBG_LOG((" DI_V110 : %s", + (features & DI_V110) ? "Y" : "N")) + DBG_LOG((" DI_V120 : %s", + (features & DI_V120) ? "Y" : "N")) + DBG_LOG((" DI_POTS : %s", + (features & DI_POTS) ? "Y" : "N")) + DBG_LOG((" DI_CODEC : %s", + (features & DI_CODEC) ? "Y" : "N")) + DBG_LOG((" DI_MANAGE : %s", + (features & DI_MANAGE) ? "Y" : "N")) + DBG_LOG((" DI_V_42 : %s", + (features & DI_V_42) ? "Y" : "N")) + DBG_LOG((" DI_EXTD_FAX : %s", + (features & DI_EXTD_FAX) ? "Y" : "N")) + DBG_LOG((" DI_AT_PARSER : %s", + (features & DI_AT_PARSER) ? "Y" : "N")) + DBG_LOG((" DI_VOICE_OVER_IP : %s", + (features & DI_VOICE_OVER_IP) ? "Y" : "N")) + } + +void diva_add_slave_adapter(diva_os_xdi_adapter_t *a) +{ + diva_os_spin_lock_magic_t old_irql; + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add_slave"); + list_add_tail(&a->link, &adapter_queue); + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add_slave"); +} + +int diva_card_read_xlog(diva_os_xdi_adapter_t *a) +{ + diva_get_xlog_t *req; + byte *data; + + if (!a->xdi_adapter.Initialized || !a->xdi_adapter.DIRequest) { + return (-1); + } + if (!(data = diva_os_malloc(0, sizeof(struct mi_pc_maint)))) { + return (-1); + } + memset(data, 0x00, sizeof(struct mi_pc_maint)); + + if (!(req = diva_os_malloc(0, sizeof(*req)))) { + diva_os_free(0, data); + return (-1); + } + req->command = 0x0400; + req->req = LOG; + req->rc = 0x00; + + (*(a->xdi_adapter.DIRequest)) (&a->xdi_adapter, (ENTITY *) req); + + if (!req->rc || req->req) { + diva_os_free(0, data); + diva_os_free(0, req); + return (-1); + } + + memcpy(data, &req->req, sizeof(struct mi_pc_maint)); + + diva_os_free(0, req); + + a->xdi_mbox.data_length = sizeof(struct mi_pc_maint); + a->xdi_mbox.data = data; + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + + return (0); +} + +void xdiFreeFile(void *handle) +{ +} diff --git a/drivers/isdn/hardware/eicon/diva.h b/drivers/isdn/hardware/eicon/diva.h new file mode 100644 index 000000000..1ad76650f --- /dev/null +++ b/drivers/isdn/hardware/eicon/diva.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: diva.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */ + +#ifndef __DIVA_XDI_OS_PART_H__ +#define __DIVA_XDI_OS_PART_H__ + + +int divasa_xdi_driver_entry(void); +void divasa_xdi_driver_unload(void); +void *diva_driver_add_card(void *pdev, unsigned long CardOrdinal); +void diva_driver_remove_card(void *pdiva); + +typedef int (*divas_xdi_copy_to_user_fn_t) (void *os_handle, void __user *dst, + const void *src, int length); + +typedef int (*divas_xdi_copy_from_user_fn_t) (void *os_handle, void *dst, + const void __user *src, int length); + +int diva_xdi_read(void *adapter, void *os_handle, void __user *dst, + int max_length, divas_xdi_copy_to_user_fn_t cp_fn); + +int diva_xdi_write(void *adapter, void *os_handle, const void __user *src, + int length, void *msg, + divas_xdi_copy_from_user_fn_t cp_fn); + +void *diva_xdi_open_adapter(void *os_handle, const void __user *src, + int length, void *msg, + divas_xdi_copy_from_user_fn_t cp_fn); + +void diva_xdi_close_adapter(void *adapter, void *os_handle); + + +#endif diff --git a/drivers/isdn/hardware/eicon/diva_didd.c b/drivers/isdn/hardware/eicon/diva_didd.c new file mode 100644 index 000000000..60e79257d --- /dev/null +++ b/drivers/isdn/hardware/eicon/diva_didd.c @@ -0,0 +1,139 @@ +/* $Id: diva_didd.c,v 1.13.6.4 2005/02/11 19:40:25 armin Exp $ + * + * DIDD Interface module for Eicon active cards. + * + * Functions are in dadapter.c + * + * Copyright 2002-2003 by Armin Schindler (mac@melware.de) + * Copyright 2002-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <net/net_namespace.h> + +#include "platform.h" +#include "di_defs.h" +#include "dadapter.h" +#include "divasync.h" +#include "did_vers.h" + +static char *main_revision = "$Revision: 1.13.6.4 $"; + +static char *DRIVERNAME = + "Eicon DIVA - DIDD table (http://www.melware.net)"; +static char *DRIVERLNAME = "divadidd"; +char *DRIVERRELEASE_DIDD = "2.0"; + +MODULE_DESCRIPTION("DIDD table driver for diva drivers"); +MODULE_AUTHOR("Cytronics & Melware, Eicon Networks"); +MODULE_SUPPORTED_DEVICE("Eicon diva drivers"); +MODULE_LICENSE("GPL"); + +#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) +#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) + +extern int diddfunc_init(void); +extern void diddfunc_finit(void); + +extern void DIVA_DIDD_Read(void *, int); + +static struct proc_dir_entry *proc_didd; +struct proc_dir_entry *proc_net_eicon = NULL; + +EXPORT_SYMBOL(DIVA_DIDD_Read); +EXPORT_SYMBOL(proc_net_eicon); + +static char *getrev(const char *revision) +{ + char *rev; + char *p; + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + *--p = 0; + } else + rev = "1.0"; + return rev; +} + +static int divadidd_proc_show(struct seq_file *m, void *v) +{ + char tmprev[32]; + + strcpy(tmprev, main_revision); + seq_printf(m, "%s\n", DRIVERNAME); + seq_printf(m, "name : %s\n", DRIVERLNAME); + seq_printf(m, "release : %s\n", DRIVERRELEASE_DIDD); + seq_printf(m, "build : %s(%s)\n", + diva_didd_common_code_build, DIVA_BUILD); + seq_printf(m, "revision : %s\n", getrev(tmprev)); + + return 0; +} + +static int __init create_proc(void) +{ + proc_net_eicon = proc_mkdir("eicon", init_net.proc_net); + + if (proc_net_eicon) { + proc_didd = proc_create_single(DRIVERLNAME, S_IRUGO, + proc_net_eicon, divadidd_proc_show); + return (1); + } + return (0); +} + +static void remove_proc(void) +{ + remove_proc_entry(DRIVERLNAME, proc_net_eicon); + remove_proc_entry("eicon", init_net.proc_net); +} + +static int __init divadidd_init(void) +{ + char tmprev[32]; + int ret = 0; + + printk(KERN_INFO "%s\n", DRIVERNAME); + printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_DIDD); + strcpy(tmprev, main_revision); + printk("%s Build:%s(%s)\n", getrev(tmprev), + diva_didd_common_code_build, DIVA_BUILD); + + if (!create_proc()) { + printk(KERN_ERR "%s: could not create proc entry\n", + DRIVERLNAME); + ret = -EIO; + goto out; + } + + if (!diddfunc_init()) { + printk(KERN_ERR "%s: failed to connect to DIDD.\n", + DRIVERLNAME); +#ifdef MODULE + remove_proc(); +#endif + ret = -EIO; + goto out; + } + +out: + return (ret); +} + +static void __exit divadidd_exit(void) +{ + diddfunc_finit(); + remove_proc(); + printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME); +} + +module_init(divadidd_init); +module_exit(divadidd_exit); diff --git a/drivers/isdn/hardware/eicon/diva_dma.c b/drivers/isdn/hardware/eicon/diva_dma.c new file mode 100644 index 000000000..217b6aa9f --- /dev/null +++ b/drivers/isdn/hardware/eicon/diva_dma.c @@ -0,0 +1,94 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "platform.h" +#include "diva_dma.h" +/* + Every entry has length of PAGE_SIZE + and represents one single physical page +*/ +struct _diva_dma_map_entry { + int busy; + dword phys_bus_addr; /* 32bit address as seen by the card */ + void *local_ram_addr; /* local address as seen by the host */ + void *addr_handle; /* handle uset to free allocated memory */ +}; +/* + Create local mapping structure and init it to default state +*/ +struct _diva_dma_map_entry *diva_alloc_dma_map(void *os_context, int nentries) { + diva_dma_map_entry_t *pmap = diva_os_malloc(0, sizeof(*pmap) * (nentries + 1)); + if (pmap) + memset(pmap, 0, sizeof(*pmap) * (nentries + 1)); + return pmap; +} +/* + Free local map (context should be freed before) if any +*/ +void diva_free_dma_mapping(struct _diva_dma_map_entry *pmap) { + if (pmap) { + diva_os_free(0, pmap); + } +} +/* + Set information saved on the map entry +*/ +void diva_init_dma_map_entry(struct _diva_dma_map_entry *pmap, + int nr, void *virt, dword phys, + void *addr_handle) { + pmap[nr].phys_bus_addr = phys; + pmap[nr].local_ram_addr = virt; + pmap[nr].addr_handle = addr_handle; +} +/* + Allocate one single entry in the map +*/ +int diva_alloc_dma_map_entry(struct _diva_dma_map_entry *pmap) { + int i; + for (i = 0; (pmap && pmap[i].local_ram_addr); i++) { + if (!pmap[i].busy) { + pmap[i].busy = 1; + return (i); + } + } + return (-1); +} +/* + Free one single entry in the map +*/ +void diva_free_dma_map_entry(struct _diva_dma_map_entry *pmap, int nr) { + pmap[nr].busy = 0; +} +/* + Get information saved on the map entry +*/ +void diva_get_dma_map_entry(struct _diva_dma_map_entry *pmap, int nr, + void **pvirt, dword *pphys) { + *pphys = pmap[nr].phys_bus_addr; + *pvirt = pmap[nr].local_ram_addr; +} +void *diva_get_entry_handle(struct _diva_dma_map_entry *pmap, int nr) { + return (pmap[nr].addr_handle); +} diff --git a/drivers/isdn/hardware/eicon/diva_dma.h b/drivers/isdn/hardware/eicon/diva_dma.h new file mode 100644 index 000000000..d32c91be5 --- /dev/null +++ b/drivers/isdn/hardware/eicon/diva_dma.h @@ -0,0 +1,48 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_DMA_MAPPING_IFC_H__ +#define __DIVA_DMA_MAPPING_IFC_H__ +typedef struct _diva_dma_map_entry diva_dma_map_entry_t; +struct _diva_dma_map_entry *diva_alloc_dma_map(void *os_context, int nentries); +void diva_init_dma_map_entry(struct _diva_dma_map_entry *pmap, + int nr, void *virt, dword phys, + void *addr_handle); +int diva_alloc_dma_map_entry(struct _diva_dma_map_entry *pmap); +void diva_free_dma_map_entry(struct _diva_dma_map_entry *pmap, int entry); +void diva_get_dma_map_entry(struct _diva_dma_map_entry *pmap, int nr, + void **pvirt, dword *pphys); +void diva_free_dma_mapping(struct _diva_dma_map_entry *pmap); +/* + Functionality to be implemented by OS wrapper + and running in process context +*/ +void diva_init_dma_map(void *hdev, + struct _diva_dma_map_entry **ppmap, + int nentries); +void diva_free_dma_map(void *hdev, + struct _diva_dma_map_entry *pmap); +void *diva_get_entry_handle(struct _diva_dma_map_entry *pmap, int nr); +#endif diff --git a/drivers/isdn/hardware/eicon/diva_pci.h b/drivers/isdn/hardware/eicon/diva_pci.h new file mode 100644 index 000000000..7ef5db98a --- /dev/null +++ b/drivers/isdn/hardware/eicon/diva_pci.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: diva_pci.h,v 1.6 2003/01/04 15:29:45 schindler Exp $ */ + +#ifndef __DIVA_PCI_INTERFACE_H__ +#define __DIVA_PCI_INTERFACE_H__ + +void __iomem *divasa_remap_pci_bar(diva_os_xdi_adapter_t *a, + int id, + unsigned long bar, + unsigned long area_length); +void divasa_unmap_pci_bar(void __iomem *bar); +unsigned long divasa_get_pci_irq(unsigned char bus, + unsigned char func, void *pci_dev_handle); +unsigned long divasa_get_pci_bar(unsigned char bus, + unsigned char func, + int bar, void *pci_dev_handle); +byte diva_os_get_pci_bus(void *pci_dev_handle); +byte diva_os_get_pci_func(void *pci_dev_handle); + +#endif diff --git a/drivers/isdn/hardware/eicon/divacapi.h b/drivers/isdn/hardware/eicon/divacapi.h new file mode 100644 index 000000000..c4868a0d8 --- /dev/null +++ b/drivers/isdn/hardware/eicon/divacapi.h @@ -0,0 +1,1350 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/*#define DEBUG */ + +#include <linux/types.h> + +#define IMPLEMENT_DTMF 1 +#define IMPLEMENT_LINE_INTERCONNECT2 1 +#define IMPLEMENT_ECHO_CANCELLER 1 +#define IMPLEMENT_RTP 1 +#define IMPLEMENT_T38 1 +#define IMPLEMENT_FAX_SUB_SEP_PWD 1 +#define IMPLEMENT_V18 1 +#define IMPLEMENT_DTMF_TONE 1 +#define IMPLEMENT_PIAFS 1 +#define IMPLEMENT_FAX_PAPER_FORMATS 1 +#define IMPLEMENT_VOWN 1 +#define IMPLEMENT_CAPIDTMF 1 +#define IMPLEMENT_FAX_NONSTANDARD 1 +#define VSWITCH_SUPPORT 1 + + +#define IMPLEMENT_LINE_INTERCONNECT 0 +#define IMPLEMENT_MARKED_OK_AFTER_FC 1 + +#include "capidtmf.h" + +/*------------------------------------------------------------------*/ +/* Common API internal definitions */ +/*------------------------------------------------------------------*/ + +#define MAX_APPL 240 +#define MAX_NCCI 127 + +#define MSG_IN_QUEUE_SIZE ((4096 + 3) & 0xfffc) /* must be multiple of 4 */ + + +#define MSG_IN_OVERHEAD sizeof(APPL *) + +#define MAX_NL_CHANNEL 255 +#define MAX_DATA_B3 8 +#define MAX_DATA_ACK MAX_DATA_B3 +#define MAX_MULTI_IE 6 +#define MAX_MSG_SIZE 256 +#define MAX_MSG_PARMS 10 +#define MAX_CPN_MASK_SIZE 16 +#define MAX_MSN_CONFIG 10 +#define EXT_CONTROLLER 0x80 +#define CODEC 0x01 +#define CODEC_PERMANENT 0x02 +#define ADV_VOICE 0x03 +#define MAX_CIP_TYPES 5 /* kind of CIP types for group optimization */ + +#define FAX_CONNECT_INFO_BUFFER_SIZE 256 +#define NCPI_BUFFER_SIZE 256 + +#define MAX_CHANNELS_PER_PLCI 8 +#define MAX_INTERNAL_COMMAND_LEVELS 4 +#define INTERNAL_REQ_BUFFER_SIZE 272 + +#define INTERNAL_IND_BUFFER_SIZE 768 + +#define DTMF_PARAMETER_BUFFER_SIZE 12 +#define ADV_VOICE_COEF_BUFFER_SIZE 50 + +#define LI_PLCI_B_QUEUE_ENTRIES 256 + + + +typedef struct _APPL APPL; +typedef struct _PLCI PLCI; +typedef struct _NCCI NCCI; +typedef struct _DIVA_CAPI_ADAPTER DIVA_CAPI_ADAPTER; +typedef struct _DATA_B3_DESC DATA_B3_DESC; +typedef struct _DATA_ACK_DESC DATA_ACK_DESC; +typedef struct manufacturer_profile_s MANUFACTURER_PROFILE; +typedef struct fax_ncpi_s FAX_NCPI; +typedef struct api_parse_s API_PARSE; +typedef struct api_save_s API_SAVE; +typedef struct msn_config_s MSN_CONFIG; +typedef struct msn_config_max_s MSN_CONFIG_MAX; +typedef struct msn_ld_s MSN_LD; + +struct manufacturer_profile_s { + dword private_options; + dword rtp_primary_payloads; + dword rtp_additional_payloads; +}; + +struct fax_ncpi_s { + word options; + word format; +}; + +struct msn_config_s { + byte msn[MAX_CPN_MASK_SIZE]; +}; + +struct msn_config_max_s { + MSN_CONFIG msn_conf[MAX_MSN_CONFIG]; +}; + +struct msn_ld_s { + dword low; + dword high; +}; + +struct api_parse_s { + word length; + byte *info; +}; + +struct api_save_s { + API_PARSE parms[MAX_MSG_PARMS + 1]; + byte info[MAX_MSG_SIZE]; +}; + +struct _DATA_B3_DESC { + word Handle; + word Number; + word Flags; + word Length; + void *P; +}; + +struct _DATA_ACK_DESC { + word Handle; + word Number; +}; + +typedef void (*t_std_internal_command)(dword Id, PLCI *plci, byte Rc); + +/************************************************************************/ +/* Don't forget to adapt dos.asm after changing the _APPL structure!!!! */ +struct _APPL { + word Id; + word NullCREnable; + word CDEnable; + dword S_Handle; + + + + + + + LIST_ENTRY s_function; + dword s_context; + word s_count; + APPL *s_next; + byte *xbuffer_used; + void **xbuffer_internal; + void **xbuffer_ptr; + + + + + + + byte *queue; + word queue_size; + word queue_free; + word queue_read; + word queue_write; + word queue_signal; + byte msg_lost; + byte appl_flags; + word Number; + + word MaxBuffer; + byte MaxNCCI; + byte MaxNCCIData; + word MaxDataLength; + word NCCIDataFlowCtrlTimer; + byte *ReceiveBuffer; + word *DataNCCI; + word *DataFlags; +}; + + +struct _PLCI { + ENTITY Sig; + ENTITY NL; + word RNum; + word RFlags; + BUFFERS RData[2]; + BUFFERS XData[1]; + BUFFERS NData[2]; + + DIVA_CAPI_ADAPTER *adapter; + APPL *appl; + PLCI *relatedPTYPLCI; + byte Id; + byte State; + byte sig_req; + byte nl_req; + byte SuppState; + byte channels; + byte tel; + byte B1_resource; + byte B2_prot; + byte B3_prot; + + word command; + word m_command; + word internal_command; + word number; + word req_in_start; + word req_in; + word req_out; + word msg_in_write_pos; + word msg_in_read_pos; + word msg_in_wrap_pos; + + void *data_sent_ptr; + byte data_sent; + byte send_disc; + byte sig_global_req; + byte sig_remove_id; + byte nl_global_req; + byte nl_remove_id; + byte b_channel; + byte adv_nl; + byte manufacturer; + byte call_dir; + byte hook_state; + byte spoofed_msg; + byte ptyState; + byte cr_enquiry; + word hangup_flow_ctrl_timer; + + word ncci_ring_list; + byte inc_dis_ncci_table[MAX_CHANNELS_PER_PLCI]; + t_std_internal_command internal_command_queue[MAX_INTERNAL_COMMAND_LEVELS]; + DECLARE_BITMAP(c_ind_mask_table, MAX_APPL); + DECLARE_BITMAP(group_optimization_mask_table, MAX_APPL); + byte RBuffer[200]; + dword msg_in_queue[MSG_IN_QUEUE_SIZE/sizeof(dword)]; + API_SAVE saved_msg; + API_SAVE B_protocol; + byte fax_connect_info_length; + byte fax_connect_info_buffer[FAX_CONNECT_INFO_BUFFER_SIZE]; + byte fax_edata_ack_length; + word nsf_control_bits; + byte ncpi_state; + byte ncpi_buffer[NCPI_BUFFER_SIZE]; + + byte internal_req_buffer[INTERNAL_REQ_BUFFER_SIZE]; + byte internal_ind_buffer[INTERNAL_IND_BUFFER_SIZE + 3]; + dword requested_options_conn; + dword requested_options; + word B1_facilities; + API_SAVE *adjust_b_parms_msg; + word adjust_b_facilities; + word adjust_b_command; + word adjust_b_ncci; + word adjust_b_mode; + word adjust_b_state; + byte adjust_b_restore; + + byte dtmf_rec_active; + word dtmf_rec_pulse_ms; + word dtmf_rec_pause_ms; + byte dtmf_send_requests; + word dtmf_send_pulse_ms; + word dtmf_send_pause_ms; + word dtmf_cmd; + word dtmf_msg_number_queue[8]; + byte dtmf_parameter_length; + byte dtmf_parameter_buffer[DTMF_PARAMETER_BUFFER_SIZE]; + + + t_capidtmf_state capidtmf_state; + + + byte li_bchannel_id; /* BRI: 1..2, PRI: 1..32 */ + byte li_channel_bits; + byte li_notify_update; + word li_cmd; + word li_write_command; + word li_write_channel; + word li_plci_b_write_pos; + word li_plci_b_read_pos; + word li_plci_b_req_pos; + dword li_plci_b_queue[LI_PLCI_B_QUEUE_ENTRIES]; + + + word ec_cmd; + word ec_idi_options; + word ec_tail_length; + + + byte tone_last_indication_code; + + byte vswitchstate; + byte vsprot; + byte vsprotdialect; + byte notifiedcall; /* Flag if it is a spoofed call */ + + int rx_dma_descriptor; + dword rx_dma_magic; +}; + + +struct _NCCI { + byte data_out; + byte data_pending; + byte data_ack_out; + byte data_ack_pending; + DATA_B3_DESC DBuffer[MAX_DATA_B3]; + DATA_ACK_DESC DataAck[MAX_DATA_ACK]; +}; + + +struct _DIVA_CAPI_ADAPTER { + IDI_CALL request; + byte Id; + byte max_plci; + byte max_listen; + byte listen_active; + PLCI *plci; + byte ch_ncci[MAX_NL_CHANNEL + 1]; + byte ncci_ch[MAX_NCCI + 1]; + byte ncci_plci[MAX_NCCI + 1]; + byte ncci_state[MAX_NCCI + 1]; + byte ncci_next[MAX_NCCI + 1]; + NCCI ncci[MAX_NCCI + 1]; + + byte ch_flow_control[MAX_NL_CHANNEL + 1]; /* Used by XON protocol */ + byte ch_flow_control_pending; + byte ch_flow_plci[MAX_NL_CHANNEL + 1]; + int last_flow_control_ch; + + dword Info_Mask[MAX_APPL]; + dword CIP_Mask[MAX_APPL]; + + dword Notification_Mask[MAX_APPL]; + PLCI *codec_listen[MAX_APPL]; + dword requested_options_table[MAX_APPL]; + API_PROFILE profile; + MANUFACTURER_PROFILE man_profile; + dword manufacturer_features; + + byte AdvCodecFLAG; + PLCI *AdvCodecPLCI; + PLCI *AdvSignalPLCI; + APPL *AdvSignalAppl; + byte TelOAD[23]; + byte TelOSA[23]; + byte scom_appl_disable; + PLCI *automatic_lawPLCI; + byte automatic_law; + byte u_law; + + byte adv_voice_coef_length; + byte adv_voice_coef_buffer[ADV_VOICE_COEF_BUFFER_SIZE]; + + byte li_pri; + byte li_channels; + word li_base; + + byte adapter_disabled; + byte group_optimization_enabled; /* use application groups if enabled */ + dword sdram_bar; + byte flag_dynamic_l1_down; /* for hunt groups:down layer 1 if no appl present*/ + byte FlowControlIdTable[256]; + byte FlowControlSkipTable[256]; + void *os_card; /* pointer to associated OS dependent adapter structure */ +}; + + +/*------------------------------------------------------------------*/ +/* Application flags */ +/*------------------------------------------------------------------*/ + +#define APPL_FLAG_OLD_LI_SPEC 0x01 +#define APPL_FLAG_PRIV_EC_SPEC 0x02 + + +/*------------------------------------------------------------------*/ +/* API parameter definitions */ +/*------------------------------------------------------------------*/ + +#define X75_TTX 1 /* x.75 for ttx */ +#define TRF 2 /* transparent with hdlc framing */ +#define TRF_IN 3 /* transparent with hdlc fr. inc. */ +#define SDLC 4 /* sdlc, sna layer-2 */ +#define X75_BTX 5 /* x.75 for btx */ +#define LAPD 6 /* lapd (Q.921) */ +#define X25_L2 7 /* x.25 layer-2 */ +#define V120_L2 8 /* V.120 layer-2 protocol */ +#define V42_IN 9 /* V.42 layer-2 protocol, incoming */ +#define V42 10 /* V.42 layer-2 protocol */ +#define MDM_ATP 11 /* AT Parser built in the L2 */ +#define X75_V42BIS 12 /* ISO7776 (X.75 SLP) modified to support V.42 bis compression */ +#define RTPL2_IN 13 /* RTP layer-2 protocol, incoming */ +#define RTPL2 14 /* RTP layer-2 protocol */ +#define V120_V42BIS 15 /* V.120 layer-2 protocol supporting V.42 bis compression */ + +#define T70NL 1 +#define X25PLP 2 +#define T70NLX 3 +#define TRANSPARENT_NL 4 +#define ISO8208 5 +#define T30 6 + + +/*------------------------------------------------------------------*/ +/* FAX interface to IDI */ +/*------------------------------------------------------------------*/ + +#define CAPI_MAX_HEAD_LINE_SPACE 89 +#define CAPI_MAX_DATE_TIME_LENGTH 18 + +#define T30_MAX_STATION_ID_LENGTH 20 +#define T30_MAX_SUBADDRESS_LENGTH 20 +#define T30_MAX_PASSWORD_LENGTH 20 + +typedef struct t30_info_s T30_INFO; +struct t30_info_s { + byte code; + byte rate_div_2400; + byte resolution; + byte data_format; + byte pages_low; + byte pages_high; + byte operating_mode; + byte control_bits_low; + byte control_bits_high; + byte feature_bits_low; + byte feature_bits_high; + byte recording_properties; + byte universal_6; + byte universal_7; + byte station_id_len; + byte head_line_len; + byte station_id[T30_MAX_STATION_ID_LENGTH]; +/* byte head_line[]; */ +/* byte sub_sep_length; */ +/* byte sub_sep_field[]; */ +/* byte pwd_length; */ +/* byte pwd_field[]; */ +/* byte nsf_info_length; */ +/* byte nsf_info_field[]; */ +}; + + +#define T30_RESOLUTION_R8_0385 0x00 +#define T30_RESOLUTION_R8_0770_OR_200 0x01 +#define T30_RESOLUTION_R8_1540 0x02 +#define T30_RESOLUTION_R16_1540_OR_400 0x04 +#define T30_RESOLUTION_R4_0385_OR_100 0x08 +#define T30_RESOLUTION_300_300 0x10 +#define T30_RESOLUTION_INCH_BASED 0x40 +#define T30_RESOLUTION_METRIC_BASED 0x80 + +#define T30_RECORDING_WIDTH_ISO_A4 0 +#define T30_RECORDING_WIDTH_ISO_B4 1 +#define T30_RECORDING_WIDTH_ISO_A3 2 +#define T30_RECORDING_WIDTH_COUNT 3 + +#define T30_RECORDING_LENGTH_ISO_A4 0 +#define T30_RECORDING_LENGTH_ISO_B4 1 +#define T30_RECORDING_LENGTH_UNLIMITED 2 +#define T30_RECORDING_LENGTH_COUNT 3 + +#define T30_MIN_SCANLINE_TIME_00_00_00 0 +#define T30_MIN_SCANLINE_TIME_05_05_05 1 +#define T30_MIN_SCANLINE_TIME_10_05_05 2 +#define T30_MIN_SCANLINE_TIME_10_10_10 3 +#define T30_MIN_SCANLINE_TIME_20_10_10 4 +#define T30_MIN_SCANLINE_TIME_20_20_20 5 +#define T30_MIN_SCANLINE_TIME_40_20_20 6 +#define T30_MIN_SCANLINE_TIME_40_40_40 7 +#define T30_MIN_SCANLINE_TIME_RES_8 8 +#define T30_MIN_SCANLINE_TIME_RES_9 9 +#define T30_MIN_SCANLINE_TIME_RES_10 10 +#define T30_MIN_SCANLINE_TIME_10_10_05 11 +#define T30_MIN_SCANLINE_TIME_20_10_05 12 +#define T30_MIN_SCANLINE_TIME_20_20_10 13 +#define T30_MIN_SCANLINE_TIME_40_20_10 14 +#define T30_MIN_SCANLINE_TIME_40_40_20 15 +#define T30_MIN_SCANLINE_TIME_COUNT 16 + +#define T30_DATA_FORMAT_SFF 0 +#define T30_DATA_FORMAT_ASCII 1 +#define T30_DATA_FORMAT_NATIVE 2 +#define T30_DATA_FORMAT_COUNT 3 + + +#define T30_OPERATING_MODE_STANDARD 0 +#define T30_OPERATING_MODE_CLASS2 1 +#define T30_OPERATING_MODE_CLASS1 2 +#define T30_OPERATING_MODE_CAPI 3 +#define T30_OPERATING_MODE_CAPI_NEG 4 +#define T30_OPERATING_MODE_COUNT 5 + +/* EDATA transmit messages */ +#define EDATA_T30_DIS 0x01 +#define EDATA_T30_FTT 0x02 +#define EDATA_T30_MCF 0x03 +#define EDATA_T30_PARAMETERS 0x04 + +/* EDATA receive messages */ +#define EDATA_T30_DCS 0x81 +#define EDATA_T30_TRAIN_OK 0x82 +#define EDATA_T30_EOP 0x83 +#define EDATA_T30_MPS 0x84 +#define EDATA_T30_EOM 0x85 +#define EDATA_T30_DTC 0x86 +#define EDATA_T30_PAGE_END 0x87 /* Indicates end of page data. Reserved, but not implemented ! */ +#define EDATA_T30_EOP_CAPI 0x88 + + +#define T30_SUCCESS 0 +#define T30_ERR_NO_DIS_RECEIVED 1 +#define T30_ERR_TIMEOUT_NO_RESPONSE 2 +#define T30_ERR_RETRY_NO_RESPONSE 3 +#define T30_ERR_TOO_MANY_REPEATS 4 +#define T30_ERR_UNEXPECTED_MESSAGE 5 +#define T30_ERR_UNEXPECTED_DCN 6 +#define T30_ERR_DTC_UNSUPPORTED 7 +#define T30_ERR_ALL_RATES_FAILED 8 +#define T30_ERR_TOO_MANY_TRAINS 9 +#define T30_ERR_RECEIVE_CORRUPTED 10 +#define T30_ERR_UNEXPECTED_DISC 11 +#define T30_ERR_APPLICATION_DISC 12 +#define T30_ERR_INCOMPATIBLE_DIS 13 +#define T30_ERR_INCOMPATIBLE_DCS 14 +#define T30_ERR_TIMEOUT_NO_COMMAND 15 +#define T30_ERR_RETRY_NO_COMMAND 16 +#define T30_ERR_TIMEOUT_COMMAND_TOO_LONG 17 +#define T30_ERR_TIMEOUT_RESPONSE_TOO_LONG 18 +#define T30_ERR_NOT_IDENTIFIED 19 +#define T30_ERR_SUPERVISORY_TIMEOUT 20 +#define T30_ERR_TOO_LONG_SCAN_LINE 21 +/* #define T30_ERR_RETRY_NO_PAGE_AFTER_MPS 22 */ +#define T30_ERR_RETRY_NO_PAGE_RECEIVED 23 +#define T30_ERR_RETRY_NO_DCS_AFTER_FTT 24 +#define T30_ERR_RETRY_NO_DCS_AFTER_EOM 25 +#define T30_ERR_RETRY_NO_DCS_AFTER_MPS 26 +#define T30_ERR_RETRY_NO_DCN_AFTER_MCF 27 +#define T30_ERR_RETRY_NO_DCN_AFTER_RTN 28 +#define T30_ERR_RETRY_NO_CFR 29 +#define T30_ERR_RETRY_NO_MCF_AFTER_EOP 30 +#define T30_ERR_RETRY_NO_MCF_AFTER_EOM 31 +#define T30_ERR_RETRY_NO_MCF_AFTER_MPS 32 +#define T30_ERR_SUB_SEP_UNSUPPORTED 33 +#define T30_ERR_PWD_UNSUPPORTED 34 +#define T30_ERR_SUB_SEP_PWD_UNSUPPORTED 35 +#define T30_ERR_INVALID_COMMAND_FRAME 36 +#define T30_ERR_UNSUPPORTED_PAGE_CODING 37 +#define T30_ERR_INVALID_PAGE_CODING 38 +#define T30_ERR_INCOMPATIBLE_PAGE_CONFIG 39 +#define T30_ERR_TIMEOUT_FROM_APPLICATION 40 +#define T30_ERR_V34FAX_NO_REACTION_ON_MARK 41 +#define T30_ERR_V34FAX_TRAINING_TIMEOUT 42 +#define T30_ERR_V34FAX_UNEXPECTED_V21 43 +#define T30_ERR_V34FAX_PRIMARY_CTS_ON 44 +#define T30_ERR_V34FAX_TURNAROUND_POLLING 45 +#define T30_ERR_V34FAX_V8_INCOMPATIBILITY 46 + + +#define T30_CONTROL_BIT_DISABLE_FINE 0x0001 +#define T30_CONTROL_BIT_ENABLE_ECM 0x0002 +#define T30_CONTROL_BIT_ECM_64_BYTES 0x0004 +#define T30_CONTROL_BIT_ENABLE_2D_CODING 0x0008 +#define T30_CONTROL_BIT_ENABLE_T6_CODING 0x0010 +#define T30_CONTROL_BIT_ENABLE_UNCOMPR 0x0020 +#define T30_CONTROL_BIT_ACCEPT_POLLING 0x0040 +#define T30_CONTROL_BIT_REQUEST_POLLING 0x0080 +#define T30_CONTROL_BIT_MORE_DOCUMENTS 0x0100 +#define T30_CONTROL_BIT_ACCEPT_SUBADDRESS 0x0200 +#define T30_CONTROL_BIT_ACCEPT_SEL_POLLING 0x0400 +#define T30_CONTROL_BIT_ACCEPT_PASSWORD 0x0800 +#define T30_CONTROL_BIT_ENABLE_V34FAX 0x1000 +#define T30_CONTROL_BIT_EARLY_CONNECT 0x2000 + +#define T30_CONTROL_BIT_ALL_FEATURES (T30_CONTROL_BIT_ENABLE_ECM | T30_CONTROL_BIT_ENABLE_2D_CODING | T30_CONTROL_BIT_ENABLE_T6_CODING | T30_CONTROL_BIT_ENABLE_UNCOMPR | T30_CONTROL_BIT_ENABLE_V34FAX) + +#define T30_FEATURE_BIT_FINE 0x0001 +#define T30_FEATURE_BIT_ECM 0x0002 +#define T30_FEATURE_BIT_ECM_64_BYTES 0x0004 +#define T30_FEATURE_BIT_2D_CODING 0x0008 +#define T30_FEATURE_BIT_T6_CODING 0x0010 +#define T30_FEATURE_BIT_UNCOMPR_ENABLED 0x0020 +#define T30_FEATURE_BIT_POLLING 0x0040 +#define T30_FEATURE_BIT_MORE_DOCUMENTS 0x0100 +#define T30_FEATURE_BIT_V34FAX 0x1000 + + +#define T30_NSF_CONTROL_BIT_ENABLE_NSF 0x0001 +#define T30_NSF_CONTROL_BIT_RAW_INFO 0x0002 +#define T30_NSF_CONTROL_BIT_NEGOTIATE_IND 0x0004 +#define T30_NSF_CONTROL_BIT_NEGOTIATE_RESP 0x0008 + +#define T30_NSF_ELEMENT_NSF_FIF 0x00 +#define T30_NSF_ELEMENT_NSC_FIF 0x01 +#define T30_NSF_ELEMENT_NSS_FIF 0x02 +#define T30_NSF_ELEMENT_COMPANY_NAME 0x03 + + +/*------------------------------------------------------------------*/ +/* Analog modem definitions */ +/*------------------------------------------------------------------*/ + +typedef struct async_s ASYNC_FORMAT; +struct async_s { + unsigned pe:1; + unsigned parity:2; + unsigned spare:2; + unsigned stp:1; + unsigned ch_len:2; /* 3th octett in CAI */ +}; + + +/*------------------------------------------------------------------*/ +/* PLCI/NCCI states */ +/*------------------------------------------------------------------*/ + +#define IDLE 0 +#define OUTG_CON_PENDING 1 +#define INC_CON_PENDING 2 +#define INC_CON_ALERT 3 +#define INC_CON_ACCEPT 4 +#define INC_ACT_PENDING 5 +#define LISTENING 6 +#define CONNECTED 7 +#define OUTG_DIS_PENDING 8 +#define INC_DIS_PENDING 9 +#define LOCAL_CONNECT 10 +#define INC_RES_PENDING 11 +#define OUTG_RES_PENDING 12 +#define SUSPENDING 13 +#define ADVANCED_VOICE_SIG 14 +#define ADVANCED_VOICE_NOSIG 15 +#define RESUMING 16 +#define INC_CON_CONNECTED_ALERT 17 +#define OUTG_REJ_PENDING 18 + + +/*------------------------------------------------------------------*/ +/* auxiliary states for supplementary services */ +/*------------------------------------------------------------------*/ + +#define IDLE 0 +#define HOLD_REQUEST 1 +#define HOLD_INDICATE 2 +#define CALL_HELD 3 +#define RETRIEVE_REQUEST 4 +#define RETRIEVE_INDICATION 5 + +/*------------------------------------------------------------------*/ +/* Capi IE + Msg types */ +/*------------------------------------------------------------------*/ +#define ESC_CAUSE 0x800 | CAU /* Escape cause element */ +#define ESC_MSGTYPE 0x800 | MSGTYPEIE /* Escape message type */ +#define ESC_CHI 0x800 | CHI /* Escape channel id */ +#define ESC_LAW 0x800 | BC /* Escape law info */ +#define ESC_CR 0x800 | CRIE /* Escape CallReference */ +#define ESC_PROFILE 0x800 | PROFILEIE /* Escape profile */ +#define ESC_SSEXT 0x800 | SSEXTIE /* Escape Supplem. Serv.*/ +#define ESC_VSWITCH 0x800 | VSWITCHIE /* Escape VSwitch */ +#define CST 0x14 /* Call State i.e. */ +#define PI 0x1E /* Progress Indicator */ +#define NI 0x27 /* Notification Ind */ +#define CONN_NR 0x4C /* Connected Number */ +#define CONG_RNR 0xBF /* Congestion RNR */ +#define CONG_RR 0xB0 /* Congestion RR */ +#define RESERVED 0xFF /* Res. for future use */ +#define ON_BOARD_CODEC 0x02 /* external controller */ +#define HANDSET 0x04 /* Codec+Handset(Pro11) */ +#define HOOK_SUPPORT 0x01 /* activate Hook signal */ +#define SCR 0x7a /* unscreened number */ + +#define HOOK_OFF_REQ 0x9001 /* internal conn req */ +#define HOOK_ON_REQ 0x9002 /* internal disc req */ +#define SUSPEND_REQ 0x9003 /* internal susp req */ +#define RESUME_REQ 0x9004 /* internal resume req */ +#define USELAW_REQ 0x9005 /* internal law req */ +#define LISTEN_SIG_ASSIGN_PEND 0x9006 +#define PERM_LIST_REQ 0x900a /* permanent conn DCE */ +#define C_HOLD_REQ 0x9011 +#define C_RETRIEVE_REQ 0x9012 +#define C_NCR_FAC_REQ 0x9013 +#define PERM_COD_ASSIGN 0x9014 +#define PERM_COD_CALL 0x9015 +#define PERM_COD_HOOK 0x9016 +#define PERM_COD_CONN_PEND 0x9017 /* wait for connect_con */ +#define PTY_REQ_PEND 0x9018 +#define CD_REQ_PEND 0x9019 +#define CF_START_PEND 0x901a +#define CF_STOP_PEND 0x901b +#define ECT_REQ_PEND 0x901c +#define GETSERV_REQ_PEND 0x901d +#define BLOCK_PLCI 0x901e +#define INTERR_NUMBERS_REQ_PEND 0x901f +#define INTERR_DIVERSION_REQ_PEND 0x9020 +#define MWI_ACTIVATE_REQ_PEND 0x9021 +#define MWI_DEACTIVATE_REQ_PEND 0x9022 +#define SSEXT_REQ_COMMAND 0x9023 +#define SSEXT_NC_REQ_COMMAND 0x9024 +#define START_L1_SIG_ASSIGN_PEND 0x9025 +#define REM_L1_SIG_ASSIGN_PEND 0x9026 +#define CONF_BEGIN_REQ_PEND 0x9027 +#define CONF_ADD_REQ_PEND 0x9028 +#define CONF_SPLIT_REQ_PEND 0x9029 +#define CONF_DROP_REQ_PEND 0x902a +#define CONF_ISOLATE_REQ_PEND 0x902b +#define CONF_REATTACH_REQ_PEND 0x902c +#define VSWITCH_REQ_PEND 0x902d +#define GET_MWI_STATE 0x902e +#define CCBS_REQUEST_REQ_PEND 0x902f +#define CCBS_DEACTIVATE_REQ_PEND 0x9030 +#define CCBS_INTERROGATE_REQ_PEND 0x9031 + +#define NO_INTERNAL_COMMAND 0 +#define DTMF_COMMAND_1 1 +#define DTMF_COMMAND_2 2 +#define DTMF_COMMAND_3 3 +#define MIXER_COMMAND_1 4 +#define MIXER_COMMAND_2 5 +#define MIXER_COMMAND_3 6 +#define ADV_VOICE_COMMAND_CONNECT_1 7 +#define ADV_VOICE_COMMAND_CONNECT_2 8 +#define ADV_VOICE_COMMAND_CONNECT_3 9 +#define ADV_VOICE_COMMAND_DISCONNECT_1 10 +#define ADV_VOICE_COMMAND_DISCONNECT_2 11 +#define ADV_VOICE_COMMAND_DISCONNECT_3 12 +#define ADJUST_B_RESTORE_1 13 +#define ADJUST_B_RESTORE_2 14 +#define RESET_B3_COMMAND_1 15 +#define SELECT_B_COMMAND_1 16 +#define FAX_CONNECT_INFO_COMMAND_1 17 +#define FAX_CONNECT_INFO_COMMAND_2 18 +#define FAX_ADJUST_B23_COMMAND_1 19 +#define FAX_ADJUST_B23_COMMAND_2 20 +#define EC_COMMAND_1 21 +#define EC_COMMAND_2 22 +#define EC_COMMAND_3 23 +#define RTP_CONNECT_B3_REQ_COMMAND_1 24 +#define RTP_CONNECT_B3_REQ_COMMAND_2 25 +#define RTP_CONNECT_B3_REQ_COMMAND_3 26 +#define RTP_CONNECT_B3_RES_COMMAND_1 27 +#define RTP_CONNECT_B3_RES_COMMAND_2 28 +#define RTP_CONNECT_B3_RES_COMMAND_3 29 +#define HOLD_SAVE_COMMAND_1 30 +#define RETRIEVE_RESTORE_COMMAND_1 31 +#define FAX_DISCONNECT_COMMAND_1 32 +#define FAX_DISCONNECT_COMMAND_2 33 +#define FAX_DISCONNECT_COMMAND_3 34 +#define FAX_EDATA_ACK_COMMAND_1 35 +#define FAX_EDATA_ACK_COMMAND_2 36 +#define FAX_CONNECT_ACK_COMMAND_1 37 +#define FAX_CONNECT_ACK_COMMAND_2 38 +#define STD_INTERNAL_COMMAND_COUNT 39 + +#define UID 0x2d /* User Id for Mgmt */ + +#define CALL_DIR_OUT 0x01 /* call direction of initial call */ +#define CALL_DIR_IN 0x02 +#define CALL_DIR_ORIGINATE 0x04 /* DTE/DCE direction according to */ +#define CALL_DIR_ANSWER 0x08 /* state of B-Channel Operation */ +#define CALL_DIR_FORCE_OUTG_NL 0x10 /* for RESET_B3 reconnect, after DISC_B3... */ + +#define AWAITING_MANUF_CON 0x80 /* command spoofing flags */ +#define SPOOFING_REQUIRED 0xff +#define AWAITING_SELECT_B 0xef + +/*------------------------------------------------------------------*/ +/* B_CTRL / DSP_CTRL */ +/*------------------------------------------------------------------*/ + +#define DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS 0x01 +#define DSP_CTRL_SET_BCHANNEL_PASSIVATION_BRI 0x02 +#define DSP_CTRL_SET_DTMF_PARAMETERS 0x03 + +#define MANUFACTURER_FEATURE_SLAVE_CODEC 0x00000001L +#define MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS 0x00000002L +#define MANUFACTURER_FEATURE_HARDDTMF 0x00000004L +#define MANUFACTURER_FEATURE_SOFTDTMF_SEND 0x00000008L +#define MANUFACTURER_FEATURE_DTMF_PARAMETERS 0x00000010L +#define MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE 0x00000020L +#define MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD 0x00000040L +#define MANUFACTURER_FEATURE_V18 0x00000080L +#define MANUFACTURER_FEATURE_MIXER_CH_CH 0x00000100L +#define MANUFACTURER_FEATURE_MIXER_CH_PC 0x00000200L +#define MANUFACTURER_FEATURE_MIXER_PC_CH 0x00000400L +#define MANUFACTURER_FEATURE_MIXER_PC_PC 0x00000800L +#define MANUFACTURER_FEATURE_ECHO_CANCELLER 0x00001000L +#define MANUFACTURER_FEATURE_RTP 0x00002000L +#define MANUFACTURER_FEATURE_T38 0x00004000L +#define MANUFACTURER_FEATURE_TRANSP_DELIVERY_CONF 0x00008000L +#define MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL 0x00010000L +#define MANUFACTURER_FEATURE_OOB_CHANNEL 0x00020000L +#define MANUFACTURER_FEATURE_IN_BAND_CHANNEL 0x00040000L +#define MANUFACTURER_FEATURE_IN_BAND_FEATURE 0x00080000L +#define MANUFACTURER_FEATURE_PIAFS 0x00100000L +#define MANUFACTURER_FEATURE_DTMF_TONE 0x00200000L +#define MANUFACTURER_FEATURE_FAX_PAPER_FORMATS 0x00400000L +#define MANUFACTURER_FEATURE_OK_FC_LABEL 0x00800000L +#define MANUFACTURER_FEATURE_VOWN 0x01000000L +#define MANUFACTURER_FEATURE_XCONNECT 0x02000000L +#define MANUFACTURER_FEATURE_DMACONNECT 0x04000000L +#define MANUFACTURER_FEATURE_AUDIO_TAP 0x08000000L +#define MANUFACTURER_FEATURE_FAX_NONSTANDARD 0x10000000L + +/*------------------------------------------------------------------*/ +/* DTMF interface to IDI */ +/*------------------------------------------------------------------*/ + + +#define DTMF_DIGIT_TONE_LOW_GROUP_697_HZ 0x00 +#define DTMF_DIGIT_TONE_LOW_GROUP_770_HZ 0x01 +#define DTMF_DIGIT_TONE_LOW_GROUP_852_HZ 0x02 +#define DTMF_DIGIT_TONE_LOW_GROUP_941_HZ 0x03 +#define DTMF_DIGIT_TONE_LOW_GROUP_MASK 0x03 +#define DTMF_DIGIT_TONE_HIGH_GROUP_1209_HZ 0x00 +#define DTMF_DIGIT_TONE_HIGH_GROUP_1336_HZ 0x04 +#define DTMF_DIGIT_TONE_HIGH_GROUP_1477_HZ 0x08 +#define DTMF_DIGIT_TONE_HIGH_GROUP_1633_HZ 0x0c +#define DTMF_DIGIT_TONE_HIGH_GROUP_MASK 0x0c +#define DTMF_DIGIT_TONE_CODE_0 0x07 +#define DTMF_DIGIT_TONE_CODE_1 0x00 +#define DTMF_DIGIT_TONE_CODE_2 0x04 +#define DTMF_DIGIT_TONE_CODE_3 0x08 +#define DTMF_DIGIT_TONE_CODE_4 0x01 +#define DTMF_DIGIT_TONE_CODE_5 0x05 +#define DTMF_DIGIT_TONE_CODE_6 0x09 +#define DTMF_DIGIT_TONE_CODE_7 0x02 +#define DTMF_DIGIT_TONE_CODE_8 0x06 +#define DTMF_DIGIT_TONE_CODE_9 0x0a +#define DTMF_DIGIT_TONE_CODE_STAR 0x03 +#define DTMF_DIGIT_TONE_CODE_HASHMARK 0x0b +#define DTMF_DIGIT_TONE_CODE_A 0x0c +#define DTMF_DIGIT_TONE_CODE_B 0x0d +#define DTMF_DIGIT_TONE_CODE_C 0x0e +#define DTMF_DIGIT_TONE_CODE_D 0x0f + +#define DTMF_UDATA_REQUEST_SEND_DIGITS 16 +#define DTMF_UDATA_REQUEST_ENABLE_RECEIVER 17 +#define DTMF_UDATA_REQUEST_DISABLE_RECEIVER 18 +#define DTMF_UDATA_INDICATION_DIGITS_SENT 16 +#define DTMF_UDATA_INDICATION_DIGITS_RECEIVED 17 +#define DTMF_UDATA_INDICATION_MODEM_CALLING_TONE 18 +#define DTMF_UDATA_INDICATION_FAX_CALLING_TONE 19 +#define DTMF_UDATA_INDICATION_ANSWER_TONE 20 + +#define UDATA_REQUEST_MIXER_TAP_DATA 27 +#define UDATA_INDICATION_MIXER_TAP_DATA 27 + +#define DTMF_LISTEN_ACTIVE_FLAG 0x01 +#define DTMF_SEND_DIGIT_FLAG 0x01 + + +/*------------------------------------------------------------------*/ +/* Mixer interface to IDI */ +/*------------------------------------------------------------------*/ + + +#define LI2_FLAG_PCCONNECT_A_B 0x40000000 +#define LI2_FLAG_PCCONNECT_B_A 0x80000000 + +#define MIXER_BCHANNELS_BRI 2 +#define MIXER_IC_CHANNELS_BRI MIXER_BCHANNELS_BRI +#define MIXER_IC_CHANNEL_BASE MIXER_BCHANNELS_BRI +#define MIXER_CHANNELS_BRI (MIXER_BCHANNELS_BRI + MIXER_IC_CHANNELS_BRI) +#define MIXER_CHANNELS_PRI 32 + +typedef struct li_config_s LI_CONFIG; + +struct xconnect_card_address_s { + dword low; + dword high; +}; + +struct xconnect_transfer_address_s { + struct xconnect_card_address_s card_address; + dword offset; +}; + +struct li_config_s { + DIVA_CAPI_ADAPTER *adapter; + PLCI *plci; + struct xconnect_transfer_address_s send_b; + struct xconnect_transfer_address_s send_pc; + byte *flag_table; /* dword aligned and sized */ + byte *coef_table; /* dword aligned and sized */ + byte channel; + byte curchnl; + byte chflags; +}; + +extern LI_CONFIG *li_config_table; +extern word li_total_channels; + +#define LI_CHANNEL_INVOLVED 0x01 +#define LI_CHANNEL_ACTIVE 0x02 +#define LI_CHANNEL_TX_DATA 0x04 +#define LI_CHANNEL_RX_DATA 0x08 +#define LI_CHANNEL_CONFERENCE 0x10 +#define LI_CHANNEL_ADDRESSES_SET 0x80 + +#define LI_CHFLAG_MONITOR 0x01 +#define LI_CHFLAG_MIX 0x02 +#define LI_CHFLAG_LOOP 0x04 + +#define LI_FLAG_INTERCONNECT 0x01 +#define LI_FLAG_MONITOR 0x02 +#define LI_FLAG_MIX 0x04 +#define LI_FLAG_PCCONNECT 0x08 +#define LI_FLAG_CONFERENCE 0x10 +#define LI_FLAG_ANNOUNCEMENT 0x20 + +#define LI_COEF_CH_CH 0x01 +#define LI_COEF_CH_PC 0x02 +#define LI_COEF_PC_CH 0x04 +#define LI_COEF_PC_PC 0x08 +#define LI_COEF_CH_CH_SET 0x10 +#define LI_COEF_CH_PC_SET 0x20 +#define LI_COEF_PC_CH_SET 0x40 +#define LI_COEF_PC_PC_SET 0x80 + +#define LI_REQ_SILENT_UPDATE 0xffff + +#define LI_PLCI_B_LAST_FLAG ((dword) 0x80000000L) +#define LI_PLCI_B_DISC_FLAG ((dword) 0x40000000L) +#define LI_PLCI_B_SKIP_FLAG ((dword) 0x20000000L) +#define LI_PLCI_B_FLAG_MASK ((dword) 0xe0000000L) + +#define UDATA_REQUEST_SET_MIXER_COEFS_BRI 24 +#define UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC 25 +#define UDATA_REQUEST_SET_MIXER_COEFS_PRI_ASYN 26 +#define UDATA_INDICATION_MIXER_COEFS_SET 24 + +#define MIXER_FEATURE_ENABLE_TX_DATA 0x0001 +#define MIXER_FEATURE_ENABLE_RX_DATA 0x0002 + +#define MIXER_COEF_LINE_CHANNEL_MASK 0x1f +#define MIXER_COEF_LINE_FROM_PC_FLAG 0x20 +#define MIXER_COEF_LINE_TO_PC_FLAG 0x40 +#define MIXER_COEF_LINE_ROW_FLAG 0x80 + +#define UDATA_REQUEST_XCONNECT_FROM 28 +#define UDATA_INDICATION_XCONNECT_FROM 28 +#define UDATA_REQUEST_XCONNECT_TO 29 +#define UDATA_INDICATION_XCONNECT_TO 29 + +#define XCONNECT_CHANNEL_PORT_B 0x0000 +#define XCONNECT_CHANNEL_PORT_PC 0x8000 +#define XCONNECT_CHANNEL_PORT_MASK 0x8000 +#define XCONNECT_CHANNEL_NUMBER_MASK 0x7fff +#define XCONNECT_CHANNEL_PORT_COUNT 2 + +#define XCONNECT_SUCCESS 0x0000 +#define XCONNECT_ERROR 0x0001 + + +/*------------------------------------------------------------------*/ +/* Echo canceller interface to IDI */ +/*------------------------------------------------------------------*/ + + +#define PRIVATE_ECHO_CANCELLER 0 + +#define PRIV_SELECTOR_ECHO_CANCELLER 255 + +#define EC_ENABLE_OPERATION 1 +#define EC_DISABLE_OPERATION 2 +#define EC_FREEZE_COEFFICIENTS 3 +#define EC_RESUME_COEFFICIENT_UPDATE 4 +#define EC_RESET_COEFFICIENTS 5 + +#define EC_DISABLE_NON_LINEAR_PROCESSING 0x0001 +#define EC_DO_NOT_REQUIRE_REVERSALS 0x0002 +#define EC_DETECT_DISABLE_TONE 0x0004 + +#define EC_SUCCESS 0 +#define EC_UNSUPPORTED_OPERATION 1 + +#define EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ 1 +#define EC_BYPASS_DUE_TO_REVERSED_2100HZ 2 +#define EC_BYPASS_RELEASED 3 + +#define DSP_CTRL_SET_LEC_PARAMETERS 0x05 + +#define LEC_ENABLE_ECHO_CANCELLER 0x0001 +#define LEC_ENABLE_2100HZ_DETECTOR 0x0002 +#define LEC_REQUIRE_2100HZ_REVERSALS 0x0004 +#define LEC_MANUAL_DISABLE 0x0008 +#define LEC_ENABLE_NONLINEAR_PROCESSING 0x0010 +#define LEC_FREEZE_COEFFICIENTS 0x0020 +#define LEC_RESET_COEFFICIENTS 0x8000 + +#define LEC_MAX_SUPPORTED_TAIL_LENGTH 32 + +#define LEC_UDATA_INDICATION_DISABLE_DETECT 9 + +#define LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ 0x00 +#define LEC_DISABLE_TYPE_REVERSED_2100HZ 0x01 +#define LEC_DISABLE_RELEASED 0x02 + + +/*------------------------------------------------------------------*/ +/* RTP interface to IDI */ +/*------------------------------------------------------------------*/ + + +#define B1_RTP 31 +#define B2_RTP 31 +#define B3_RTP 31 + +#define PRIVATE_RTP 1 + +#define RTP_PRIM_PAYLOAD_PCMU_8000 0 +#define RTP_PRIM_PAYLOAD_1016_8000 1 +#define RTP_PRIM_PAYLOAD_G726_32_8000 2 +#define RTP_PRIM_PAYLOAD_GSM_8000 3 +#define RTP_PRIM_PAYLOAD_G723_8000 4 +#define RTP_PRIM_PAYLOAD_DVI4_8000 5 +#define RTP_PRIM_PAYLOAD_DVI4_16000 6 +#define RTP_PRIM_PAYLOAD_LPC_8000 7 +#define RTP_PRIM_PAYLOAD_PCMA_8000 8 +#define RTP_PRIM_PAYLOAD_G722_16000 9 +#define RTP_PRIM_PAYLOAD_QCELP_8000 12 +#define RTP_PRIM_PAYLOAD_G728_8000 14 +#define RTP_PRIM_PAYLOAD_G729_8000 18 +#define RTP_PRIM_PAYLOAD_GSM_HR_8000 30 +#define RTP_PRIM_PAYLOAD_GSM_EFR_8000 31 + +#define RTP_ADD_PAYLOAD_BASE 32 +#define RTP_ADD_PAYLOAD_RED 32 +#define RTP_ADD_PAYLOAD_CN_8000 33 +#define RTP_ADD_PAYLOAD_DTMF 34 + +#define RTP_SUCCESS 0 +#define RTP_ERR_SSRC_OR_PAYLOAD_CHANGE 1 + +#define UDATA_REQUEST_RTP_RECONFIGURE 64 +#define UDATA_INDICATION_RTP_CHANGE 65 +#define BUDATA_REQUEST_QUERY_RTCP_REPORT 1 +#define BUDATA_INDICATION_RTCP_REPORT 1 + +#define RTP_CONNECT_OPTION_DISC_ON_SSRC_CHANGE 0x00000001L +#define RTP_CONNECT_OPTION_DISC_ON_PT_CHANGE 0x00000002L +#define RTP_CONNECT_OPTION_DISC_ON_UNKNOWN_PT 0x00000004L +#define RTP_CONNECT_OPTION_NO_SILENCE_TRANSMIT 0x00010000L + +#define RTP_PAYLOAD_OPTION_VOICE_ACTIVITY_DETECT 0x0001 +#define RTP_PAYLOAD_OPTION_DISABLE_POST_FILTER 0x0002 +#define RTP_PAYLOAD_OPTION_G723_LOW_CODING_RATE 0x0100 + +#define RTP_PACKET_FILTER_IGNORE_UNKNOWN_SSRC 0x00000001L + +#define RTP_CHANGE_FLAG_SSRC_CHANGE 0x00000001L +#define RTP_CHANGE_FLAG_PAYLOAD_TYPE_CHANGE 0x00000002L +#define RTP_CHANGE_FLAG_UNKNOWN_PAYLOAD_TYPE 0x00000004L + + +/*------------------------------------------------------------------*/ +/* T.38 interface to IDI */ +/*------------------------------------------------------------------*/ + + +#define B1_T38 30 +#define B2_T38 30 +#define B3_T38 30 + +#define PRIVATE_T38 2 + + +/*------------------------------------------------------------------*/ +/* PIAFS interface to IDI */ +/*------------------------------------------------------------------*/ + + +#define B1_PIAFS 29 +#define B2_PIAFS 29 + +#define PRIVATE_PIAFS 29 + +/* + B2 configuration for PIAFS: + +---------------------+------+-----------------------------------------+ + | PIAFS Protocol | byte | Bit 1 - Protocol Speed | + | Speed configuration | | 0 - 32K | + | | | 1 - 64K (default) | + | | | Bit 2 - Variable Protocol Speed | + | | | 0 - Speed is fix | + | | | 1 - Speed is variable (default) | + +---------------------+------+-----------------------------------------+ + | Direction | word | Enable compression/decompression for | + | | | 0: All direction | + | | | 1: disable outgoing data | + | | | 2: disable incoming data | + | | | 3: disable both direction (default) | + +---------------------+------+-----------------------------------------+ + | Number of code | word | Parameter P1 of V.42bis in accordance | + | words | | with V.42bis | + +---------------------+------+-----------------------------------------+ + | Maximum String | word | Parameter P2 of V.42bis in accordance | + | Length | | with V.42bis | + +---------------------+------+-----------------------------------------+ + | control (UDATA) | byte | enable PIAFS control communication | + | abilities | | | + +---------------------+------+-----------------------------------------+ +*/ +#define PIAFS_UDATA_ABILITIES 0x80 + +/*------------------------------------------------------------------*/ +/* FAX SUB/SEP/PWD extension */ +/*------------------------------------------------------------------*/ + + +#define PRIVATE_FAX_SUB_SEP_PWD 3 + + + +/*------------------------------------------------------------------*/ +/* V.18 extension */ +/*------------------------------------------------------------------*/ + + +#define PRIVATE_V18 4 + + + +/*------------------------------------------------------------------*/ +/* DTMF TONE extension */ +/*------------------------------------------------------------------*/ + + +#define DTMF_GET_SUPPORTED_DETECT_CODES 0xf8 +#define DTMF_GET_SUPPORTED_SEND_CODES 0xf9 +#define DTMF_LISTEN_TONE_START 0xfa +#define DTMF_LISTEN_TONE_STOP 0xfb +#define DTMF_SEND_TONE 0xfc +#define DTMF_LISTEN_MF_START 0xfd +#define DTMF_LISTEN_MF_STOP 0xfe +#define DTMF_SEND_MF 0xff + +#define DTMF_MF_DIGIT_TONE_CODE_1 0x10 +#define DTMF_MF_DIGIT_TONE_CODE_2 0x11 +#define DTMF_MF_DIGIT_TONE_CODE_3 0x12 +#define DTMF_MF_DIGIT_TONE_CODE_4 0x13 +#define DTMF_MF_DIGIT_TONE_CODE_5 0x14 +#define DTMF_MF_DIGIT_TONE_CODE_6 0x15 +#define DTMF_MF_DIGIT_TONE_CODE_7 0x16 +#define DTMF_MF_DIGIT_TONE_CODE_8 0x17 +#define DTMF_MF_DIGIT_TONE_CODE_9 0x18 +#define DTMF_MF_DIGIT_TONE_CODE_0 0x19 +#define DTMF_MF_DIGIT_TONE_CODE_K1 0x1a +#define DTMF_MF_DIGIT_TONE_CODE_K2 0x1b +#define DTMF_MF_DIGIT_TONE_CODE_KP 0x1c +#define DTMF_MF_DIGIT_TONE_CODE_S1 0x1d +#define DTMF_MF_DIGIT_TONE_CODE_ST 0x1e + +#define DTMF_DIGIT_CODE_COUNT 16 +#define DTMF_MF_DIGIT_CODE_BASE DSP_DTMF_DIGIT_CODE_COUNT +#define DTMF_MF_DIGIT_CODE_COUNT 15 +#define DTMF_TOTAL_DIGIT_CODE_COUNT (DSP_MF_DIGIT_CODE_BASE + DSP_MF_DIGIT_CODE_COUNT) + +#define DTMF_TONE_DIGIT_BASE 0x80 + +#define DTMF_SIGNAL_NO_TONE (DTMF_TONE_DIGIT_BASE + 0) +#define DTMF_SIGNAL_UNIDENTIFIED_TONE (DTMF_TONE_DIGIT_BASE + 1) + +#define DTMF_SIGNAL_DIAL_TONE (DTMF_TONE_DIGIT_BASE + 2) +#define DTMF_SIGNAL_PABX_INTERNAL_DIAL_TONE (DTMF_TONE_DIGIT_BASE + 3) +#define DTMF_SIGNAL_SPECIAL_DIAL_TONE (DTMF_TONE_DIGIT_BASE + 4) /* stutter dial tone */ +#define DTMF_SIGNAL_SECOND_DIAL_TONE (DTMF_TONE_DIGIT_BASE + 5) +#define DTMF_SIGNAL_RINGING_TONE (DTMF_TONE_DIGIT_BASE + 6) +#define DTMF_SIGNAL_SPECIAL_RINGING_TONE (DTMF_TONE_DIGIT_BASE + 7) +#define DTMF_SIGNAL_BUSY_TONE (DTMF_TONE_DIGIT_BASE + 8) +#define DTMF_SIGNAL_CONGESTION_TONE (DTMF_TONE_DIGIT_BASE + 9) /* reorder tone */ +#define DTMF_SIGNAL_SPECIAL_INFORMATION_TONE (DTMF_TONE_DIGIT_BASE + 10) +#define DTMF_SIGNAL_COMFORT_TONE (DTMF_TONE_DIGIT_BASE + 11) +#define DTMF_SIGNAL_HOLD_TONE (DTMF_TONE_DIGIT_BASE + 12) +#define DTMF_SIGNAL_RECORD_TONE (DTMF_TONE_DIGIT_BASE + 13) +#define DTMF_SIGNAL_CALLER_WAITING_TONE (DTMF_TONE_DIGIT_BASE + 14) +#define DTMF_SIGNAL_CALL_WAITING_TONE (DTMF_TONE_DIGIT_BASE + 15) +#define DTMF_SIGNAL_PAY_TONE (DTMF_TONE_DIGIT_BASE + 16) +#define DTMF_SIGNAL_POSITIVE_INDICATION_TONE (DTMF_TONE_DIGIT_BASE + 17) +#define DTMF_SIGNAL_NEGATIVE_INDICATION_TONE (DTMF_TONE_DIGIT_BASE + 18) +#define DTMF_SIGNAL_WARNING_TONE (DTMF_TONE_DIGIT_BASE + 19) +#define DTMF_SIGNAL_INTRUSION_TONE (DTMF_TONE_DIGIT_BASE + 20) +#define DTMF_SIGNAL_CALLING_CARD_SERVICE_TONE (DTMF_TONE_DIGIT_BASE + 21) +#define DTMF_SIGNAL_PAYPHONE_RECOGNITION_TONE (DTMF_TONE_DIGIT_BASE + 22) +#define DTMF_SIGNAL_CPE_ALERTING_SIGNAL (DTMF_TONE_DIGIT_BASE + 23) +#define DTMF_SIGNAL_OFF_HOOK_WARNING_TONE (DTMF_TONE_DIGIT_BASE + 24) + +#define DTMF_SIGNAL_INTERCEPT_TONE (DTMF_TONE_DIGIT_BASE + 63) + +#define DTMF_SIGNAL_MODEM_CALLING_TONE (DTMF_TONE_DIGIT_BASE + 64) +#define DTMF_SIGNAL_FAX_CALLING_TONE (DTMF_TONE_DIGIT_BASE + 65) +#define DTMF_SIGNAL_ANSWER_TONE (DTMF_TONE_DIGIT_BASE + 66) +#define DTMF_SIGNAL_REVERSED_ANSWER_TONE (DTMF_TONE_DIGIT_BASE + 67) +#define DTMF_SIGNAL_ANSAM_TONE (DTMF_TONE_DIGIT_BASE + 68) +#define DTMF_SIGNAL_REVERSED_ANSAM_TONE (DTMF_TONE_DIGIT_BASE + 69) +#define DTMF_SIGNAL_BELL103_ANSWER_TONE (DTMF_TONE_DIGIT_BASE + 70) +#define DTMF_SIGNAL_FAX_FLAGS (DTMF_TONE_DIGIT_BASE + 71) +#define DTMF_SIGNAL_G2_FAX_GROUP_ID (DTMF_TONE_DIGIT_BASE + 72) +#define DTMF_SIGNAL_HUMAN_SPEECH (DTMF_TONE_DIGIT_BASE + 73) +#define DTMF_SIGNAL_ANSWERING_MACHINE_390 (DTMF_TONE_DIGIT_BASE + 74) + +#define DTMF_MF_LISTEN_ACTIVE_FLAG 0x02 +#define DTMF_SEND_MF_FLAG 0x02 +#define DTMF_TONE_LISTEN_ACTIVE_FLAG 0x04 +#define DTMF_SEND_TONE_FLAG 0x04 + +#define PRIVATE_DTMF_TONE 5 + + +/*------------------------------------------------------------------*/ +/* FAX paper format extension */ +/*------------------------------------------------------------------*/ + + +#define PRIVATE_FAX_PAPER_FORMATS 6 + + + +/*------------------------------------------------------------------*/ +/* V.OWN extension */ +/*------------------------------------------------------------------*/ + + +#define PRIVATE_VOWN 7 + + + +/*------------------------------------------------------------------*/ +/* FAX non-standard facilities extension */ +/*------------------------------------------------------------------*/ + + +#define PRIVATE_FAX_NONSTANDARD 8 + + + +/*------------------------------------------------------------------*/ +/* Advanced voice */ +/*------------------------------------------------------------------*/ + +#define ADV_VOICE_WRITE_ACTIVATION 0 +#define ADV_VOICE_WRITE_DEACTIVATION 1 +#define ADV_VOICE_WRITE_UPDATE 2 + +#define ADV_VOICE_OLD_COEF_COUNT 6 +#define ADV_VOICE_NEW_COEF_BASE (ADV_VOICE_OLD_COEF_COUNT * sizeof(word)) + +/*------------------------------------------------------------------*/ +/* B1 resource switching */ +/*------------------------------------------------------------------*/ + +#define B1_FACILITY_LOCAL 0x01 +#define B1_FACILITY_MIXER 0x02 +#define B1_FACILITY_DTMFX 0x04 +#define B1_FACILITY_DTMFR 0x08 +#define B1_FACILITY_VOICE 0x10 +#define B1_FACILITY_EC 0x20 + +#define ADJUST_B_MODE_SAVE 0x0001 +#define ADJUST_B_MODE_REMOVE_L23 0x0002 +#define ADJUST_B_MODE_SWITCH_L1 0x0004 +#define ADJUST_B_MODE_NO_RESOURCE 0x0008 +#define ADJUST_B_MODE_ASSIGN_L23 0x0010 +#define ADJUST_B_MODE_USER_CONNECT 0x0020 +#define ADJUST_B_MODE_CONNECT 0x0040 +#define ADJUST_B_MODE_RESTORE 0x0080 + +#define ADJUST_B_START 0 +#define ADJUST_B_SAVE_MIXER_1 1 +#define ADJUST_B_SAVE_DTMF_1 2 +#define ADJUST_B_REMOVE_L23_1 3 +#define ADJUST_B_REMOVE_L23_2 4 +#define ADJUST_B_SAVE_EC_1 5 +#define ADJUST_B_SAVE_DTMF_PARAMETER_1 6 +#define ADJUST_B_SAVE_VOICE_1 7 +#define ADJUST_B_SWITCH_L1_1 8 +#define ADJUST_B_SWITCH_L1_2 9 +#define ADJUST_B_RESTORE_VOICE_1 10 +#define ADJUST_B_RESTORE_VOICE_2 11 +#define ADJUST_B_RESTORE_DTMF_PARAMETER_1 12 +#define ADJUST_B_RESTORE_DTMF_PARAMETER_2 13 +#define ADJUST_B_RESTORE_EC_1 14 +#define ADJUST_B_RESTORE_EC_2 15 +#define ADJUST_B_ASSIGN_L23_1 16 +#define ADJUST_B_ASSIGN_L23_2 17 +#define ADJUST_B_CONNECT_1 18 +#define ADJUST_B_CONNECT_2 19 +#define ADJUST_B_CONNECT_3 20 +#define ADJUST_B_CONNECT_4 21 +#define ADJUST_B_RESTORE_DTMF_1 22 +#define ADJUST_B_RESTORE_DTMF_2 23 +#define ADJUST_B_RESTORE_MIXER_1 24 +#define ADJUST_B_RESTORE_MIXER_2 25 +#define ADJUST_B_RESTORE_MIXER_3 26 +#define ADJUST_B_RESTORE_MIXER_4 27 +#define ADJUST_B_RESTORE_MIXER_5 28 +#define ADJUST_B_RESTORE_MIXER_6 29 +#define ADJUST_B_RESTORE_MIXER_7 30 +#define ADJUST_B_END 31 + +/*------------------------------------------------------------------*/ +/* XON Protocol def's */ +/*------------------------------------------------------------------*/ +#define N_CH_XOFF 0x01 +#define N_XON_SENT 0x02 +#define N_XON_REQ 0x04 +#define N_XON_CONNECT_IND 0x08 +#define N_RX_FLOW_CONTROL_MASK 0x3f +#define N_OK_FC_PENDING 0x80 +#define N_TX_FLOW_CONTROL_MASK 0xc0 + +/*------------------------------------------------------------------*/ +/* NCPI state */ +/*------------------------------------------------------------------*/ +#define NCPI_VALID_CONNECT_B3_IND 0x01 +#define NCPI_VALID_CONNECT_B3_ACT 0x02 +#define NCPI_VALID_DISC_B3_IND 0x04 +#define NCPI_CONNECT_B3_ACT_SENT 0x08 +#define NCPI_NEGOTIATE_B3_SENT 0x10 +#define NCPI_MDM_CTS_ON_RECEIVED 0x40 +#define NCPI_MDM_DCD_ON_RECEIVED 0x80 + +/*------------------------------------------------------------------*/ diff --git a/drivers/isdn/hardware/eicon/divamnt.c b/drivers/isdn/hardware/eicon/divamnt.c new file mode 100644 index 000000000..5a95587b3 --- /dev/null +++ b/drivers/isdn/hardware/eicon/divamnt.c @@ -0,0 +1,239 @@ +/* $Id: divamnt.c,v 1.32.6.10 2005/02/11 19:40:25 armin Exp $ + * + * Driver for Eicon DIVA Server ISDN cards. + * Maint module + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/poll.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> + +#include "platform.h" +#include "di_defs.h" +#include "divasync.h" +#include "debug_if.h" + +static DEFINE_MUTEX(maint_mutex); +static char *main_revision = "$Revision: 1.32.6.10 $"; + +static int major; + +MODULE_DESCRIPTION("Maint driver for Eicon DIVA Server cards"); +MODULE_AUTHOR("Cytronics & Melware, Eicon Networks"); +MODULE_SUPPORTED_DEVICE("DIVA card driver"); +MODULE_LICENSE("GPL"); + +static int buffer_length = 128; +module_param(buffer_length, int, 0); +static unsigned long diva_dbg_mem = 0; +module_param(diva_dbg_mem, ulong, 0); + +static char *DRIVERNAME = + "Eicon DIVA - MAINT module (http://www.melware.net)"; +static char *DRIVERLNAME = "diva_mnt"; +static char *DEVNAME = "DivasMAINT"; +char *DRIVERRELEASE_MNT = "2.0"; + +static wait_queue_head_t msgwaitq; +static unsigned long opened; + +extern int mntfunc_init(int *, void **, unsigned long); +extern void mntfunc_finit(void); +extern int maint_read_write(void __user *buf, int count); + +/* + * helper functions + */ +static char *getrev(const char *revision) +{ + char *rev; + char *p; + + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + *--p = 0; + } else + rev = "1.0"; + + return rev; +} + +/* + * kernel/user space copy functions + */ +int diva_os_copy_to_user(void *os_handle, void __user *dst, const void *src, + int length) +{ + return (copy_to_user(dst, src, length)); +} +int diva_os_copy_from_user(void *os_handle, void *dst, const void __user *src, + int length) +{ + return (copy_from_user(dst, src, length)); +} + +/* + * get time + */ +void diva_os_get_time(dword *sec, dword *usec) +{ + struct timespec64 time; + + ktime_get_ts64(&time); + + *sec = (dword) time.tv_sec; + *usec = (dword) (time.tv_nsec / NSEC_PER_USEC); +} + +/* + * device node operations + */ +static __poll_t maint_poll(struct file *file, poll_table *wait) +{ + __poll_t mask = 0; + + poll_wait(file, &msgwaitq, wait); + mask = EPOLLOUT | EPOLLWRNORM; + if (file->private_data || diva_dbg_q_length()) { + mask |= EPOLLIN | EPOLLRDNORM; + } + return (mask); +} + +static int maint_open(struct inode *ino, struct file *filep) +{ + int ret; + + mutex_lock(&maint_mutex); + /* only one open is allowed, so we test + it atomically */ + if (test_and_set_bit(0, &opened)) + ret = -EBUSY; + else { + filep->private_data = NULL; + ret = nonseekable_open(ino, filep); + } + mutex_unlock(&maint_mutex); + return ret; +} + +static int maint_close(struct inode *ino, struct file *filep) +{ + if (filep->private_data) { + diva_os_free(0, filep->private_data); + filep->private_data = NULL; + } + + /* clear 'used' flag */ + clear_bit(0, &opened); + + return (0); +} + +static ssize_t divas_maint_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return (maint_read_write((char __user *) buf, (int) count)); +} + +static ssize_t divas_maint_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return (maint_read_write(buf, (int) count)); +} + +static const struct file_operations divas_maint_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = divas_maint_read, + .write = divas_maint_write, + .poll = maint_poll, + .open = maint_open, + .release = maint_close +}; + +static void divas_maint_unregister_chrdev(void) +{ + unregister_chrdev(major, DEVNAME); +} + +static int __init divas_maint_register_chrdev(void) +{ + if ((major = register_chrdev(0, DEVNAME, &divas_maint_fops)) < 0) + { + printk(KERN_ERR "%s: failed to create /dev entry.\n", + DRIVERLNAME); + return (0); + } + + return (1); +} + +/* + * wake up reader + */ +void diva_maint_wakeup_read(void) +{ + wake_up_interruptible(&msgwaitq); +} + +/* + * Driver Load + */ +static int __init maint_init(void) +{ + char tmprev[50]; + int ret = 0; + void *buffer = NULL; + + init_waitqueue_head(&msgwaitq); + + printk(KERN_INFO "%s\n", DRIVERNAME); + printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_MNT); + strcpy(tmprev, main_revision); + printk("%s Build: %s \n", getrev(tmprev), DIVA_BUILD); + + if (!divas_maint_register_chrdev()) { + ret = -EIO; + goto out; + } + + if (!(mntfunc_init(&buffer_length, &buffer, diva_dbg_mem))) { + printk(KERN_ERR "%s: failed to connect to DIDD.\n", + DRIVERLNAME); + divas_maint_unregister_chrdev(); + ret = -EIO; + goto out; + } + + printk(KERN_INFO "%s: trace buffer = %p - %d kBytes, %s (Major: %d)\n", + DRIVERLNAME, buffer, (buffer_length / 1024), + (diva_dbg_mem == 0) ? "internal" : "external", major); + +out: + return (ret); +} + +/* +** Driver Unload +*/ +static void __exit maint_exit(void) +{ + divas_maint_unregister_chrdev(); + mntfunc_finit(); + + printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME); +} + +module_init(maint_init); +module_exit(maint_exit); diff --git a/drivers/isdn/hardware/eicon/divasfunc.c b/drivers/isdn/hardware/eicon/divasfunc.c new file mode 100644 index 000000000..4be5f8814 --- /dev/null +++ b/drivers/isdn/hardware/eicon/divasfunc.c @@ -0,0 +1,237 @@ +/* $Id: divasfunc.c,v 1.23.4.2 2004/08/28 20:03:53 armin Exp $ + * + * Low level driver for Eicon DIVA Server ISDN cards. + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include "platform.h" +#include "di_defs.h" +#include "pc.h" +#include "di.h" +#include "io.h" +#include "divasync.h" +#include "diva.h" +#include "xdi_vers.h" + +#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) +#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) + +static int debugmask; + +extern void DIVA_DIDD_Read(void *, int); + +extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER]; + +extern char *DRIVERRELEASE_DIVAS; + +static dword notify_handle; +static DESCRIPTOR DAdapter; +static DESCRIPTOR MAdapter; + +/* -------------------------------------------------------------------------- + MAINT driver connector section + -------------------------------------------------------------------------- */ +static void no_printf(unsigned char *x, ...) +{ + /* dummy debug function */ +} + +#include "debuglib.c" + +/* + * get the adapters serial number + */ +void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf) +{ + int contr = 0; + + if ((contr = ((IoAdapter->serialNo & 0xff000000) >> 24))) { + sprintf(buf, "%d-%d", + IoAdapter->serialNo & 0x00ffffff, contr + 1); + } else { + sprintf(buf, "%d", IoAdapter->serialNo); + } +} + +/* + * register a new adapter + */ +void diva_xdi_didd_register_adapter(int card) +{ + DESCRIPTOR d; + IDI_SYNC_REQ req; + + if (card && ((card - 1) < MAX_ADAPTER) && + IoAdapters[card - 1] && Requests[card - 1]) { + d.type = IoAdapters[card - 1]->Properties.DescType; + d.request = Requests[card - 1]; + d.channels = IoAdapters[card - 1]->Properties.Channels; + d.features = IoAdapters[card - 1]->Properties.Features; + DBG_TRC(("DIDD register A(%d) channels=%d", card, + d.channels)) + /* workaround for different Name in structure */ + strlcpy(IoAdapters[card - 1]->Name, + IoAdapters[card - 1]->Properties.Name, + sizeof(IoAdapters[card - 1]->Name)); + req.didd_remove_adapter.e.Req = 0; + req.didd_add_adapter.e.Rc = IDI_SYNC_REQ_DIDD_ADD_ADAPTER; + req.didd_add_adapter.info.descriptor = (void *) &d; + DAdapter.request((ENTITY *)&req); + if (req.didd_add_adapter.e.Rc != 0xff) { + DBG_ERR(("DIDD register A(%d) failed !", card)) + } + IoAdapters[card - 1]->os_trap_nfy_Fnc = NULL; + } +} + +/* + * remove an adapter + */ +void diva_xdi_didd_remove_adapter(int card) +{ + IDI_SYNC_REQ req; + ADAPTER *a = &IoAdapters[card - 1]->a; + + IoAdapters[card - 1]->os_trap_nfy_Fnc = NULL; + DBG_TRC(("DIDD de-register A(%d)", card)) + req.didd_remove_adapter.e.Req = 0; + req.didd_remove_adapter.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER; + req.didd_remove_adapter.info.p_request = + (IDI_CALL) Requests[card - 1]; + DAdapter.request((ENTITY *)&req); + memset(&(a->IdTable), 0x00, 256); +} + +/* + * start debug + */ +static void start_dbg(void) +{ + DbgRegister("DIVAS", DRIVERRELEASE_DIVAS, (debugmask) ? debugmask : DBG_DEFAULT); + DBG_LOG(("DIVA ISDNXDI BUILD (%s[%s])", + DIVA_BUILD, diva_xdi_common_code_build)) + } + +/* + * stop debug + */ +static void stop_dbg(void) +{ + DbgDeregister(); + memset(&MAdapter, 0, sizeof(MAdapter)); + dprintf = no_printf; +} + +/* + * didd callback function + */ +static void *didd_callback(void *context, DESCRIPTOR *adapter, + int removal) +{ + if (adapter->type == IDI_DADAPTER) { + DBG_ERR(("Notification about IDI_DADAPTER change ! Oops.")); + return (NULL); + } + + if (adapter->type == IDI_DIMAINT) { + if (removal) { + stop_dbg(); + } else { + memcpy(&MAdapter, adapter, sizeof(MAdapter)); + dprintf = (DIVA_DI_PRINTF) MAdapter.request; + start_dbg(); + } + } + return (NULL); +} + +/* + * connect to didd + */ +static int __init connect_didd(void) +{ + int x = 0; + int dadapter = 0; + IDI_SYNC_REQ req; + DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS]; + + DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table)); + + for (x = 0; x < MAX_DESCRIPTORS; x++) { + if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */ + dadapter = 1; + memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter)); + req.didd_notify.e.Req = 0; + req.didd_notify.e.Rc = + IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY; + req.didd_notify.info.callback = (void *)didd_callback; + req.didd_notify.info.context = NULL; + DAdapter.request((ENTITY *)&req); + if (req.didd_notify.e.Rc != 0xff) { + stop_dbg(); + return (0); + } + notify_handle = req.didd_notify.info.handle; + } else if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */ + memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter)); + dprintf = (DIVA_DI_PRINTF) MAdapter.request; + start_dbg(); + } + } + + if (!dadapter) { + stop_dbg(); + } + + return (dadapter); +} + +/* + * disconnect from didd + */ +static void disconnect_didd(void) +{ + IDI_SYNC_REQ req; + + stop_dbg(); + + req.didd_notify.e.Req = 0; + req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY; + req.didd_notify.info.handle = notify_handle; + DAdapter.request((ENTITY *)&req); +} + +/* + * init + */ +int __init divasfunc_init(int dbgmask) +{ + char *version; + + debugmask = dbgmask; + + if (!connect_didd()) { + DBG_ERR(("divasfunc: failed to connect to DIDD.")) + return (0); + } + + version = diva_xdi_common_code_build; + + divasa_xdi_driver_entry(); + + return (1); +} + +/* + * exit + */ +void divasfunc_exit(void) +{ + divasa_xdi_driver_unload(); + disconnect_didd(); +} diff --git a/drivers/isdn/hardware/eicon/divasi.c b/drivers/isdn/hardware/eicon/divasi.c new file mode 100644 index 000000000..e7081e0c0 --- /dev/null +++ b/drivers/isdn/hardware/eicon/divasi.c @@ -0,0 +1,562 @@ +/* $Id: divasi.c,v 1.25.6.2 2005/01/31 12:22:20 armin Exp $ + * + * Driver for Eicon DIVA Server ISDN cards. + * User Mode IDI Interface + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/skbuff.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> + +#include "platform.h" +#include "di_defs.h" +#include "divasync.h" +#include "um_xdi.h" +#include "um_idi.h" + +static char *main_revision = "$Revision: 1.25.6.2 $"; + +static int major; + +MODULE_DESCRIPTION("User IDI Interface for Eicon ISDN cards"); +MODULE_AUTHOR("Cytronics & Melware, Eicon Networks"); +MODULE_SUPPORTED_DEVICE("DIVA card driver"); +MODULE_LICENSE("GPL"); + +typedef struct _diva_um_idi_os_context { + wait_queue_head_t read_wait; + wait_queue_head_t close_wait; + struct timer_list diva_timer_id; + int aborted; + int adapter_nr; +} diva_um_idi_os_context_t; + +static char *DRIVERNAME = "Eicon DIVA - User IDI (http://www.melware.net)"; +static char *DRIVERLNAME = "diva_idi"; +static char *DEVNAME = "DivasIDI"; +char *DRIVERRELEASE_IDI = "2.0"; + +extern int idifunc_init(void); +extern void idifunc_finit(void); + +/* + * helper functions + */ +static char *getrev(const char *revision) +{ + char *rev; + char *p; + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + *--p = 0; + } else + rev = "1.0"; + return rev; +} + +/* + * LOCALS + */ +static ssize_t um_idi_read(struct file *file, char __user *buf, size_t count, + loff_t *offset); +static ssize_t um_idi_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset); +static __poll_t um_idi_poll(struct file *file, poll_table *wait); +static int um_idi_open(struct inode *inode, struct file *file); +static int um_idi_release(struct inode *inode, struct file *file); +static int remove_entity(void *entity); +static void diva_um_timer_function(struct timer_list *t); + +/* + * proc entry + */ +extern struct proc_dir_entry *proc_net_eicon; +static struct proc_dir_entry *um_idi_proc_entry = NULL; + +static int um_idi_proc_show(struct seq_file *m, void *v) +{ + char tmprev[32]; + + seq_printf(m, "%s\n", DRIVERNAME); + seq_printf(m, "name : %s\n", DRIVERLNAME); + seq_printf(m, "release : %s\n", DRIVERRELEASE_IDI); + strcpy(tmprev, main_revision); + seq_printf(m, "revision : %s\n", getrev(tmprev)); + seq_printf(m, "build : %s\n", DIVA_BUILD); + seq_printf(m, "major : %d\n", major); + + return 0; +} + +static int __init create_um_idi_proc(void) +{ + um_idi_proc_entry = proc_create_single(DRIVERLNAME, S_IRUGO, + proc_net_eicon, um_idi_proc_show); + if (!um_idi_proc_entry) + return (0); + return (1); +} + +static void remove_um_idi_proc(void) +{ + if (um_idi_proc_entry) { + remove_proc_entry(DRIVERLNAME, proc_net_eicon); + um_idi_proc_entry = NULL; + } +} + +static const struct file_operations divas_idi_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = um_idi_read, + .write = um_idi_write, + .poll = um_idi_poll, + .open = um_idi_open, + .release = um_idi_release +}; + +static void divas_idi_unregister_chrdev(void) +{ + unregister_chrdev(major, DEVNAME); +} + +static int __init divas_idi_register_chrdev(void) +{ + if ((major = register_chrdev(0, DEVNAME, &divas_idi_fops)) < 0) + { + printk(KERN_ERR "%s: failed to create /dev entry.\n", + DRIVERLNAME); + return (0); + } + + return (1); +} + +/* +** Driver Load +*/ +static int __init divasi_init(void) +{ + char tmprev[50]; + int ret = 0; + + printk(KERN_INFO "%s\n", DRIVERNAME); + printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_IDI); + strcpy(tmprev, main_revision); + printk("%s Build: %s\n", getrev(tmprev), DIVA_BUILD); + + if (!divas_idi_register_chrdev()) { + ret = -EIO; + goto out; + } + + if (!create_um_idi_proc()) { + divas_idi_unregister_chrdev(); + printk(KERN_ERR "%s: failed to create proc entry.\n", + DRIVERLNAME); + ret = -EIO; + goto out; + } + + if (!(idifunc_init())) { + remove_um_idi_proc(); + divas_idi_unregister_chrdev(); + printk(KERN_ERR "%s: failed to connect to DIDD.\n", + DRIVERLNAME); + ret = -EIO; + goto out; + } + printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major); + +out: + return (ret); +} + + +/* +** Driver Unload +*/ +static void __exit divasi_exit(void) +{ + idifunc_finit(); + remove_um_idi_proc(); + divas_idi_unregister_chrdev(); + + printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME); +} + +module_init(divasi_init); +module_exit(divasi_exit); + + +/* + * FILE OPERATIONS + */ + +static int +divas_um_idi_copy_to_user(void *os_handle, void *dst, const void *src, + int length) +{ + memcpy(dst, src, length); + return (length); +} + +static ssize_t +um_idi_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + diva_um_idi_os_context_t *p_os; + int ret = -EINVAL; + void *data; + + if (!file->private_data) { + return (-ENODEV); + } + + if (! + (p_os = + (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file-> + private_data))) + { + return (-ENODEV); + } + if (p_os->aborted) { + return (-ENODEV); + } + + if (!(data = diva_os_malloc(0, count))) { + return (-ENOMEM); + } + + ret = diva_um_idi_read(file->private_data, + file, data, count, + divas_um_idi_copy_to_user); + switch (ret) { + case 0: /* no message available */ + ret = (-EAGAIN); + break; + case (-1): /* adapter was removed */ + ret = (-ENODEV); + break; + case (-2): /* message_length > length of user buffer */ + ret = (-EFAULT); + break; + } + + if (ret > 0) { + if (copy_to_user(buf, data, ret)) { + ret = (-EFAULT); + } + } + + diva_os_free(0, data); + DBG_TRC(("read: ret %d", ret)); + return (ret); +} + + +static int +divas_um_idi_copy_from_user(void *os_handle, void *dst, const void *src, + int length) +{ + memcpy(dst, src, length); + return (length); +} + +static int um_idi_open_adapter(struct file *file, int adapter_nr) +{ + diva_um_idi_os_context_t *p_os; + void *e = + divas_um_idi_create_entity((dword) adapter_nr, (void *) file); + + if (!(file->private_data = e)) { + return (0); + } + p_os = (diva_um_idi_os_context_t *) diva_um_id_get_os_context(e); + init_waitqueue_head(&p_os->read_wait); + init_waitqueue_head(&p_os->close_wait); + timer_setup(&p_os->diva_timer_id, diva_um_timer_function, 0); + p_os->aborted = 0; + p_os->adapter_nr = adapter_nr; + return (1); +} + +static ssize_t +um_idi_write(struct file *file, const char __user *buf, size_t count, + loff_t *offset) +{ + diva_um_idi_os_context_t *p_os; + int ret = -EINVAL; + void *data; + int adapter_nr = 0; + + if (!file->private_data) { + /* the first write() selects the adapter_nr */ + if (count == sizeof(int)) { + if (copy_from_user + ((void *) &adapter_nr, buf, + count)) return (-EFAULT); + if (!(um_idi_open_adapter(file, adapter_nr))) + return (-ENODEV); + return (count); + } else + return (-ENODEV); + } + + if (!(p_os = + (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file-> + private_data))) + { + return (-ENODEV); + } + if (p_os->aborted) { + return (-ENODEV); + } + + if (!(data = diva_os_malloc(0, count))) { + return (-ENOMEM); + } + + if (copy_from_user(data, buf, count)) { + ret = -EFAULT; + } else { + ret = diva_um_idi_write(file->private_data, + file, data, count, + divas_um_idi_copy_from_user); + switch (ret) { + case 0: /* no space available */ + ret = (-EAGAIN); + break; + case (-1): /* adapter was removed */ + ret = (-ENODEV); + break; + case (-2): /* length of user buffer > max message_length */ + ret = (-EFAULT); + break; + } + } + diva_os_free(0, data); + DBG_TRC(("write: ret %d", ret)); + return (ret); +} + +static __poll_t um_idi_poll(struct file *file, poll_table *wait) +{ + diva_um_idi_os_context_t *p_os; + + if (!file->private_data) { + return (EPOLLERR); + } + + if ((!(p_os = + (diva_um_idi_os_context_t *) + diva_um_id_get_os_context(file->private_data))) + || p_os->aborted) { + return (EPOLLERR); + } + + poll_wait(file, &p_os->read_wait, wait); + + if (p_os->aborted) { + return (EPOLLERR); + } + + switch (diva_user_mode_idi_ind_ready(file->private_data, file)) { + case (-1): + return (EPOLLERR); + + case 0: + return (0); + } + + return (EPOLLIN | EPOLLRDNORM); +} + +static int um_idi_open(struct inode *inode, struct file *file) +{ + return (0); +} + + +static int um_idi_release(struct inode *inode, struct file *file) +{ + diva_um_idi_os_context_t *p_os; + unsigned int adapter_nr; + int ret = 0; + + if (!(file->private_data)) { + ret = -ENODEV; + goto out; + } + + if (!(p_os = + (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->private_data))) { + ret = -ENODEV; + goto out; + } + + adapter_nr = p_os->adapter_nr; + + if ((ret = remove_entity(file->private_data))) { + goto out; + } + + if (divas_um_idi_delete_entity + ((int) adapter_nr, file->private_data)) { + ret = -ENODEV; + goto out; + } + +out: + return (ret); +} + +int diva_os_get_context_size(void) +{ + return (sizeof(diva_um_idi_os_context_t)); +} + +void diva_os_wakeup_read(void *os_context) +{ + diva_um_idi_os_context_t *p_os = + (diva_um_idi_os_context_t *) os_context; + wake_up_interruptible(&p_os->read_wait); +} + +void diva_os_wakeup_close(void *os_context) +{ + diva_um_idi_os_context_t *p_os = + (diva_um_idi_os_context_t *) os_context; + wake_up_interruptible(&p_os->close_wait); +} + +static +void diva_um_timer_function(struct timer_list *t) +{ + diva_um_idi_os_context_t *p_os = from_timer(p_os, t, diva_timer_id); + + p_os->aborted = 1; + wake_up_interruptible(&p_os->read_wait); + wake_up_interruptible(&p_os->close_wait); + DBG_ERR(("entity removal watchdog")) + } + +/* +** If application exits without entity removal this function will remove +** entity and block until removal is complete +*/ +static int remove_entity(void *entity) +{ + struct task_struct *curtask = current; + diva_um_idi_os_context_t *p_os; + + diva_um_idi_stop_wdog(entity); + + if (!entity) { + DBG_FTL(("Zero entity on remove")) + return (0); + } + + if (!(p_os = + (diva_um_idi_os_context_t *) + diva_um_id_get_os_context(entity))) { + DBG_FTL(("Zero entity os context on remove")) + return (0); + } + + if (!divas_um_idi_entity_assigned(entity) || p_os->aborted) { + /* + Entity is not assigned, also can be removed + */ + return (0); + } + + DBG_TRC(("E(%08x) check remove", entity)) + + /* + If adapter not answers on remove request inside of + 10 Sec, then adapter is dead + */ + diva_um_idi_start_wdog(entity); + + { + DECLARE_WAITQUEUE(wait, curtask); + + add_wait_queue(&p_os->close_wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (!divas_um_idi_entity_start_remove(entity) + || p_os->aborted) { + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&p_os->close_wait, &wait); + } + + DBG_TRC(("E(%08x) start remove", entity)) + { + DECLARE_WAITQUEUE(wait, curtask); + + add_wait_queue(&p_os->close_wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (!divas_um_idi_entity_assigned(entity) + || p_os->aborted) { + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&p_os->close_wait, &wait); + } + + DBG_TRC(("E(%08x) remove complete, aborted:%d", entity, + p_os->aborted)) + + diva_um_idi_stop_wdog(entity); + + p_os->aborted = 0; + + return (0); +} + +/* + * timer watchdog + */ +void diva_um_idi_start_wdog(void *entity) +{ + diva_um_idi_os_context_t *p_os; + + if (entity && + ((p_os = + (diva_um_idi_os_context_t *) + diva_um_id_get_os_context(entity)))) { + mod_timer(&p_os->diva_timer_id, jiffies + 10 * HZ); + } +} + +void diva_um_idi_stop_wdog(void *entity) +{ + diva_um_idi_os_context_t *p_os; + + if (entity && + ((p_os = + (diva_um_idi_os_context_t *) + diva_um_id_get_os_context(entity)))) { + del_timer(&p_os->diva_timer_id); + } +} diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c new file mode 100644 index 000000000..b6a3950b2 --- /dev/null +++ b/drivers/isdn/hardware/eicon/divasmain.c @@ -0,0 +1,848 @@ +/* $Id: divasmain.c,v 1.55.4.6 2005/02/09 19:28:20 armin Exp $ + * + * Low level driver for Eicon DIVA Server ISDN cards. + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/uaccess.h> +#include <asm/io.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/poll.h> +#include <linux/kmod.h> + +#include "platform.h" +#undef ID_MASK +#undef N_DATA +#include "pc.h" +#include "di_defs.h" +#include "divasync.h" +#include "diva.h" +#include "di.h" +#include "io.h" +#include "xdi_msg.h" +#include "xdi_adapter.h" +#include "xdi_vers.h" +#include "diva_dma.h" +#include "diva_pci.h" + +static char *main_revision = "$Revision: 1.55.4.6 $"; + +static int major; + +static int dbgmask; + +MODULE_DESCRIPTION("Kernel driver for Eicon DIVA Server cards"); +MODULE_AUTHOR("Cytronics & Melware, Eicon Networks"); +MODULE_LICENSE("GPL"); + +module_param(dbgmask, int, 0); +MODULE_PARM_DESC(dbgmask, "initial debug mask"); + +static char *DRIVERNAME = + "Eicon DIVA Server driver (http://www.melware.net)"; +static char *DRIVERLNAME = "divas"; +static char *DEVNAME = "Divas"; +char *DRIVERRELEASE_DIVAS = "2.0"; + +extern irqreturn_t diva_os_irq_wrapper(int irq, void *context); +extern int create_divas_proc(void); +extern void remove_divas_proc(void); +extern void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf); +extern int divasfunc_init(int dbgmask); +extern void divasfunc_exit(void); + +typedef struct _diva_os_thread_dpc { + struct tasklet_struct divas_task; + diva_os_soft_isr_t *psoft_isr; +} diva_os_thread_dpc_t; + +/* -------------------------------------------------------------------------- + PCI driver interface section + -------------------------------------------------------------------------- */ +/* + vendor, device Vendor and device ID to match (or PCI_ANY_ID) + subvendor, Subsystem vendor and device ID to match (or PCI_ANY_ID) + subdevice + class, Device class to match. The class_mask tells which bits + class_mask of the class are honored during the comparison. + driver_data Data private to the driver. +*/ + +#if !defined(PCI_DEVICE_ID_EICON_MAESTRAP_2) +#define PCI_DEVICE_ID_EICON_MAESTRAP_2 0xE015 +#endif + +#if !defined(PCI_DEVICE_ID_EICON_4BRI_VOIP) +#define PCI_DEVICE_ID_EICON_4BRI_VOIP 0xE016 +#endif + +#if !defined(PCI_DEVICE_ID_EICON_4BRI_2_VOIP) +#define PCI_DEVICE_ID_EICON_4BRI_2_VOIP 0xE017 +#endif + +#if !defined(PCI_DEVICE_ID_EICON_BRI2M_2) +#define PCI_DEVICE_ID_EICON_BRI2M_2 0xE018 +#endif + +#if !defined(PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP) +#define PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP 0xE019 +#endif + +#if !defined(PCI_DEVICE_ID_EICON_2F) +#define PCI_DEVICE_ID_EICON_2F 0xE01A +#endif + +#if !defined(PCI_DEVICE_ID_EICON_BRI2M_2_VOIP) +#define PCI_DEVICE_ID_EICON_BRI2M_2_VOIP 0xE01B +#endif + +/* + This table should be sorted by PCI device ID +*/ +static const struct pci_device_id divas_pci_tbl[] = { + /* Diva Server BRI-2M PCI 0xE010 */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRA), + CARDTYPE_MAESTRA_PCI }, + /* Diva Server 4BRI-8M PCI 0xE012 */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRAQ), + CARDTYPE_DIVASRV_Q_8M_PCI }, + /* Diva Server 4BRI-8M 2.0 PCI 0xE013 */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRAQ_U), + CARDTYPE_DIVASRV_Q_8M_V2_PCI }, + /* Diva Server PRI-30M PCI 0xE014 */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRAP), + CARDTYPE_DIVASRV_P_30M_PCI }, + /* Diva Server PRI 2.0 adapter 0xE015 */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRAP_2), + CARDTYPE_DIVASRV_P_30M_V2_PCI }, + /* Diva Server Voice 4BRI-8M PCI 0xE016 */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_4BRI_VOIP), + CARDTYPE_DIVASRV_VOICE_Q_8M_PCI }, + /* Diva Server Voice 4BRI-8M 2.0 PCI 0xE017 */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_4BRI_2_VOIP), + CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI }, + /* Diva Server BRI-2M 2.0 PCI 0xE018 */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_BRI2M_2), + CARDTYPE_DIVASRV_B_2M_V2_PCI }, + /* Diva Server Voice PRI 2.0 PCI 0xE019 */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP), + CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI }, + /* Diva Server 2FX 0xE01A */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_2F), + CARDTYPE_DIVASRV_B_2F_PCI }, + /* Diva Server Voice BRI-2M 2.0 PCI 0xE01B */ + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_BRI2M_2_VOIP), + CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI }, + { 0, } /* 0 terminated list. */ +}; +MODULE_DEVICE_TABLE(pci, divas_pci_tbl); + +static int divas_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent); +static void divas_remove_one(struct pci_dev *pdev); + +static struct pci_driver diva_pci_driver = { + .name = "divas", + .probe = divas_init_one, + .remove = divas_remove_one, + .id_table = divas_pci_tbl, +}; + +/********************************************************* + ** little helper functions + *********************************************************/ +static char *getrev(const char *revision) +{ + char *rev; + char *p; + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + *--p = 0; + } else + rev = "1.0"; + return rev; +} + +void diva_log_info(unsigned char *format, ...) +{ + va_list args; + unsigned char line[160]; + + va_start(args, format); + vsnprintf(line, sizeof(line), format, args); + va_end(args); + + printk(KERN_INFO "%s: %s\n", DRIVERLNAME, line); +} + +void divas_get_version(char *p) +{ + char tmprev[32]; + + strcpy(tmprev, main_revision); + sprintf(p, "%s: %s(%s) %s(%s) major=%d\n", DRIVERLNAME, DRIVERRELEASE_DIVAS, + getrev(tmprev), diva_xdi_common_code_build, DIVA_BUILD, major); +} + +/* -------------------------------------------------------------------------- + PCI Bus services + -------------------------------------------------------------------------- */ +byte diva_os_get_pci_bus(void *pci_dev_handle) +{ + struct pci_dev *pdev = (struct pci_dev *) pci_dev_handle; + return ((byte) pdev->bus->number); +} + +byte diva_os_get_pci_func(void *pci_dev_handle) +{ + struct pci_dev *pdev = (struct pci_dev *) pci_dev_handle; + return ((byte) pdev->devfn); +} + +unsigned long divasa_get_pci_irq(unsigned char bus, unsigned char func, + void *pci_dev_handle) +{ + unsigned char irq = 0; + struct pci_dev *dev = (struct pci_dev *) pci_dev_handle; + + irq = dev->irq; + + return ((unsigned long) irq); +} + +unsigned long divasa_get_pci_bar(unsigned char bus, unsigned char func, + int bar, void *pci_dev_handle) +{ + unsigned long ret = 0; + struct pci_dev *dev = (struct pci_dev *) pci_dev_handle; + + if (bar < 6) { + ret = dev->resource[bar].start; + } + + DBG_TRC(("GOT BAR[%d]=%08x", bar, ret)); + + { + unsigned long type = (ret & 0x00000001); + if (type & PCI_BASE_ADDRESS_SPACE_IO) { + DBG_TRC((" I/O")); + ret &= PCI_BASE_ADDRESS_IO_MASK; + } else { + DBG_TRC((" memory")); + ret &= PCI_BASE_ADDRESS_MEM_MASK; + } + DBG_TRC((" final=%08x", ret)); + } + + return (ret); +} + +void PCIwrite(byte bus, byte func, int offset, void *data, int length, + void *pci_dev_handle) +{ + struct pci_dev *dev = (struct pci_dev *) pci_dev_handle; + + switch (length) { + case 1: /* byte */ + pci_write_config_byte(dev, offset, + *(unsigned char *) data); + break; + case 2: /* word */ + pci_write_config_word(dev, offset, + *(unsigned short *) data); + break; + case 4: /* dword */ + pci_write_config_dword(dev, offset, + *(unsigned int *) data); + break; + + default: /* buffer */ + if (!(length % 4) && !(length & 0x03)) { /* Copy as dword */ + dword *p = (dword *) data; + length /= 4; + + while (length--) { + pci_write_config_dword(dev, offset, + *(unsigned int *) + p++); + } + } else { /* copy as byte stream */ + byte *p = (byte *) data; + + while (length--) { + pci_write_config_byte(dev, offset, + *(unsigned char *) + p++); + } + } + } +} + +void PCIread(byte bus, byte func, int offset, void *data, int length, + void *pci_dev_handle) +{ + struct pci_dev *dev = (struct pci_dev *) pci_dev_handle; + + switch (length) { + case 1: /* byte */ + pci_read_config_byte(dev, offset, (unsigned char *) data); + break; + case 2: /* word */ + pci_read_config_word(dev, offset, (unsigned short *) data); + break; + case 4: /* dword */ + pci_read_config_dword(dev, offset, (unsigned int *) data); + break; + + default: /* buffer */ + if (!(length % 4) && !(length & 0x03)) { /* Copy as dword */ + dword *p = (dword *) data; + length /= 4; + + while (length--) { + pci_read_config_dword(dev, offset, + (unsigned int *) + p++); + } + } else { /* copy as byte stream */ + byte *p = (byte *) data; + + while (length--) { + pci_read_config_byte(dev, offset, + (unsigned char *) + p++); + } + } + } +} + +/* + Init map with DMA pages. It is not problem if some allocations fail - + the channels that will not get one DMA page will use standard PIO + interface +*/ +static void *diva_pci_alloc_consistent(struct pci_dev *hwdev, + size_t size, + dma_addr_t *dma_handle, + void **addr_handle) +{ + void *addr = pci_alloc_consistent(hwdev, size, dma_handle); + + *addr_handle = addr; + + return (addr); +} + +void diva_init_dma_map(void *hdev, + struct _diva_dma_map_entry **ppmap, int nentries) +{ + struct pci_dev *pdev = (struct pci_dev *) hdev; + struct _diva_dma_map_entry *pmap = + diva_alloc_dma_map(hdev, nentries); + + if (pmap) { + int i; + dma_addr_t dma_handle; + void *cpu_addr; + void *addr_handle; + + for (i = 0; i < nentries; i++) { + if (!(cpu_addr = diva_pci_alloc_consistent(pdev, + PAGE_SIZE, + &dma_handle, + &addr_handle))) + { + break; + } + diva_init_dma_map_entry(pmap, i, cpu_addr, + (dword) dma_handle, + addr_handle); + DBG_TRC(("dma map alloc [%d]=(%08lx:%08x:%08lx)", + i, (unsigned long) cpu_addr, + (dword) dma_handle, + (unsigned long) addr_handle))} + } + + *ppmap = pmap; +} + +/* + Free all contained in the map entries and memory used by the map + Should be always called after adapter removal from DIDD array +*/ +void diva_free_dma_map(void *hdev, struct _diva_dma_map_entry *pmap) +{ + struct pci_dev *pdev = (struct pci_dev *) hdev; + int i; + dword phys_addr; + void *cpu_addr; + dma_addr_t dma_handle; + void *addr_handle; + + for (i = 0; (pmap != NULL); i++) { + diva_get_dma_map_entry(pmap, i, &cpu_addr, &phys_addr); + if (!cpu_addr) { + break; + } + addr_handle = diva_get_entry_handle(pmap, i); + dma_handle = (dma_addr_t) phys_addr; + pci_free_consistent(pdev, PAGE_SIZE, addr_handle, + dma_handle); + DBG_TRC(("dma map free [%d]=(%08lx:%08x:%08lx)", i, + (unsigned long) cpu_addr, (dword) dma_handle, + (unsigned long) addr_handle)) + } + + diva_free_dma_mapping(pmap); +} + + +/********************************************************* + ** I/O port utilities + *********************************************************/ + +int +diva_os_register_io_port(void *adapter, int on, unsigned long port, + unsigned long length, const char *name, int id) +{ + if (on) { + if (!request_region(port, length, name)) { + DBG_ERR(("A: I/O: can't register port=%08x", port)) + return (-1); + } + } else { + release_region(port, length); + } + return (0); +} + +void __iomem *divasa_remap_pci_bar(diva_os_xdi_adapter_t *a, int id, unsigned long bar, unsigned long area_length) +{ + void __iomem *ret = ioremap(bar, area_length); + DBG_TRC(("remap(%08x)->%p", bar, ret)); + return (ret); +} + +void divasa_unmap_pci_bar(void __iomem *bar) +{ + if (bar) { + iounmap(bar); + } +} + +/********************************************************* + ** I/O port access + *********************************************************/ +inline byte inpp(void __iomem *addr) +{ + return (inb((unsigned long) addr)); +} + +inline word inppw(void __iomem *addr) +{ + return (inw((unsigned long) addr)); +} + +inline void inppw_buffer(void __iomem *addr, void *P, int length) +{ + insw((unsigned long) addr, (word *) P, length >> 1); +} + +inline void outppw_buffer(void __iomem *addr, void *P, int length) +{ + outsw((unsigned long) addr, (word *) P, length >> 1); +} + +inline void outppw(void __iomem *addr, word w) +{ + outw(w, (unsigned long) addr); +} + +inline void outpp(void __iomem *addr, word p) +{ + outb(p, (unsigned long) addr); +} + +/* -------------------------------------------------------------------------- + IRQ request / remove + -------------------------------------------------------------------------- */ +int diva_os_register_irq(void *context, byte irq, const char *name) +{ + int result = request_irq(irq, diva_os_irq_wrapper, + IRQF_SHARED, name, context); + return (result); +} + +void diva_os_remove_irq(void *context, byte irq) +{ + free_irq(irq, context); +} + +/* -------------------------------------------------------------------------- + DPC framework implementation + -------------------------------------------------------------------------- */ +static void diva_os_dpc_proc(unsigned long context) +{ + diva_os_thread_dpc_t *psoft_isr = (diva_os_thread_dpc_t *) context; + diva_os_soft_isr_t *pisr = psoft_isr->psoft_isr; + + (*(pisr->callback)) (pisr, pisr->callback_context); +} + +int diva_os_initialize_soft_isr(diva_os_soft_isr_t *psoft_isr, + diva_os_soft_isr_callback_t callback, + void *callback_context) +{ + diva_os_thread_dpc_t *pdpc; + + pdpc = (diva_os_thread_dpc_t *) diva_os_malloc(0, sizeof(*pdpc)); + if (!(psoft_isr->object = pdpc)) { + return (-1); + } + memset(pdpc, 0x00, sizeof(*pdpc)); + psoft_isr->callback = callback; + psoft_isr->callback_context = callback_context; + pdpc->psoft_isr = psoft_isr; + tasklet_init(&pdpc->divas_task, diva_os_dpc_proc, (unsigned long)pdpc); + + return (0); +} + +int diva_os_schedule_soft_isr(diva_os_soft_isr_t *psoft_isr) +{ + if (psoft_isr && psoft_isr->object) { + diva_os_thread_dpc_t *pdpc = + (diva_os_thread_dpc_t *) psoft_isr->object; + + tasklet_schedule(&pdpc->divas_task); + } + + return (1); +} + +int diva_os_cancel_soft_isr(diva_os_soft_isr_t *psoft_isr) +{ + return (0); +} + +void diva_os_remove_soft_isr(diva_os_soft_isr_t *psoft_isr) +{ + if (psoft_isr && psoft_isr->object) { + diva_os_thread_dpc_t *pdpc = + (diva_os_thread_dpc_t *) psoft_isr->object; + void *mem; + + tasklet_kill(&pdpc->divas_task); + mem = psoft_isr->object; + psoft_isr->object = NULL; + diva_os_free(0, mem); + } +} + +/* + * kernel/user space copy functions + */ +static int +xdi_copy_to_user(void *os_handle, void __user *dst, const void *src, int length) +{ + if (copy_to_user(dst, src, length)) { + return (-EFAULT); + } + return (length); +} + +static int +xdi_copy_from_user(void *os_handle, void *dst, const void __user *src, int length) +{ + if (copy_from_user(dst, src, length)) { + return (-EFAULT); + } + return (length); +} + +/* + * device node operations + */ +static int divas_open(struct inode *inode, struct file *file) +{ + return (0); +} + +static int divas_release(struct inode *inode, struct file *file) +{ + if (file->private_data) { + diva_xdi_close_adapter(file->private_data, file); + } + return (0); +} + +static ssize_t divas_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + diva_xdi_um_cfg_cmd_t msg; + int ret = -EINVAL; + + if (!file->private_data) { + file->private_data = diva_xdi_open_adapter(file, buf, + count, &msg, + xdi_copy_from_user); + if (!file->private_data) + return (-ENODEV); + ret = diva_xdi_write(file->private_data, file, + buf, count, &msg, xdi_copy_from_user); + } else { + ret = diva_xdi_write(file->private_data, file, + buf, count, NULL, xdi_copy_from_user); + } + + switch (ret) { + case -1: /* Message should be removed from rx mailbox first */ + ret = -EBUSY; + break; + case -2: /* invalid adapter was specified in this call */ + ret = -ENOMEM; + break; + case -3: + ret = -ENXIO; + break; + } + DBG_TRC(("write: ret %d", ret)); + return (ret); +} + +static ssize_t divas_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + diva_xdi_um_cfg_cmd_t msg; + int ret = -EINVAL; + + if (!file->private_data) { + file->private_data = diva_xdi_open_adapter(file, buf, + count, &msg, + xdi_copy_from_user); + } + if (!file->private_data) { + return (-ENODEV); + } + + ret = diva_xdi_read(file->private_data, file, + buf, count, xdi_copy_to_user); + switch (ret) { + case -1: /* RX mailbox is empty */ + ret = -EAGAIN; + break; + case -2: /* no memory, mailbox was cleared, last command is failed */ + ret = -ENOMEM; + break; + case -3: /* can't copy to user, retry */ + ret = -EFAULT; + break; + } + DBG_TRC(("read: ret %d", ret)); + return (ret); +} + +static __poll_t divas_poll(struct file *file, poll_table *wait) +{ + if (!file->private_data) { + return (EPOLLERR); + } + return (EPOLLIN | EPOLLRDNORM); +} + +static const struct file_operations divas_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = divas_read, + .write = divas_write, + .poll = divas_poll, + .open = divas_open, + .release = divas_release +}; + +static void divas_unregister_chrdev(void) +{ + unregister_chrdev(major, DEVNAME); +} + +static int __init divas_register_chrdev(void) +{ + if ((major = register_chrdev(0, DEVNAME, &divas_fops)) < 0) + { + printk(KERN_ERR "%s: failed to create /dev entry.\n", + DRIVERLNAME); + return (0); + } + + return (1); +} + +/* -------------------------------------------------------------------------- + PCI driver section + -------------------------------------------------------------------------- */ +static int divas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + void *pdiva = NULL; + u8 pci_latency; + u8 new_latency = 32; + + DBG_TRC(("%s bus: %08x fn: %08x insertion.\n", + CardProperties[ent->driver_data].Name, + pdev->bus->number, pdev->devfn)) + printk(KERN_INFO "%s: %s bus: %08x fn: %08x insertion.\n", + DRIVERLNAME, CardProperties[ent->driver_data].Name, + pdev->bus->number, pdev->devfn); + + if (pci_enable_device(pdev)) { + DBG_TRC(("%s: %s bus: %08x fn: %08x device init failed.\n", + DRIVERLNAME, + CardProperties[ent->driver_data].Name, + pdev->bus->number, + pdev->devfn)) + printk(KERN_ERR + "%s: %s bus: %08x fn: %08x device init failed.\n", + DRIVERLNAME, + CardProperties[ent->driver_data]. + Name, pdev->bus->number, + pdev->devfn); + return (-EIO); + } + + pci_set_master(pdev); + + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); + if (!pci_latency) { + DBG_TRC(("%s: bus: %08x fn: %08x fix latency.\n", + DRIVERLNAME, pdev->bus->number, pdev->devfn)) + printk(KERN_INFO + "%s: bus: %08x fn: %08x fix latency.\n", + DRIVERLNAME, pdev->bus->number, pdev->devfn); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, new_latency); + } + + if (!(pdiva = diva_driver_add_card(pdev, ent->driver_data))) { + DBG_TRC(("%s: %s bus: %08x fn: %08x card init failed.\n", + DRIVERLNAME, + CardProperties[ent->driver_data].Name, + pdev->bus->number, + pdev->devfn)) + printk(KERN_ERR + "%s: %s bus: %08x fn: %08x card init failed.\n", + DRIVERLNAME, + CardProperties[ent->driver_data]. + Name, pdev->bus->number, + pdev->devfn); + return (-EIO); + } + + pci_set_drvdata(pdev, pdiva); + + return (0); +} + +static void divas_remove_one(struct pci_dev *pdev) +{ + void *pdiva = pci_get_drvdata(pdev); + + DBG_TRC(("bus: %08x fn: %08x removal.\n", + pdev->bus->number, pdev->devfn)) + printk(KERN_INFO "%s: bus: %08x fn: %08x removal.\n", + DRIVERLNAME, pdev->bus->number, pdev->devfn); + + if (pdiva) { + diva_driver_remove_card(pdiva); + } + +} + +/* -------------------------------------------------------------------------- + Driver Load / Startup + -------------------------------------------------------------------------- */ +static int __init divas_init(void) +{ + char tmprev[50]; + int ret = 0; + + printk(KERN_INFO "%s\n", DRIVERNAME); + printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_DIVAS); + strcpy(tmprev, main_revision); + printk("%s Build: %s(%s)\n", getrev(tmprev), + diva_xdi_common_code_build, DIVA_BUILD); + printk(KERN_INFO "%s: support for: ", DRIVERLNAME); +#ifdef CONFIG_ISDN_DIVAS_BRIPCI + printk("BRI/PCI "); +#endif +#ifdef CONFIG_ISDN_DIVAS_PRIPCI + printk("PRI/PCI "); +#endif + printk("adapters\n"); + + if (!divasfunc_init(dbgmask)) { + printk(KERN_ERR "%s: failed to connect to DIDD.\n", + DRIVERLNAME); + ret = -EIO; + goto out; + } + + if (!divas_register_chrdev()) { +#ifdef MODULE + divasfunc_exit(); +#endif + ret = -EIO; + goto out; + } + + if (!create_divas_proc()) { +#ifdef MODULE + divas_unregister_chrdev(); + divasfunc_exit(); +#endif + printk(KERN_ERR "%s: failed to create proc entry.\n", + DRIVERLNAME); + ret = -EIO; + goto out; + } + + if ((ret = pci_register_driver(&diva_pci_driver))) { +#ifdef MODULE + remove_divas_proc(); + divas_unregister_chrdev(); + divasfunc_exit(); +#endif + printk(KERN_ERR "%s: failed to init pci driver.\n", + DRIVERLNAME); + goto out; + } + printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major); + +out: + return (ret); +} + +/* -------------------------------------------------------------------------- + Driver Unload + -------------------------------------------------------------------------- */ +static void __exit divas_exit(void) +{ + pci_unregister_driver(&diva_pci_driver); + remove_divas_proc(); + divas_unregister_chrdev(); + divasfunc_exit(); + + printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME); +} + +module_init(divas_init); +module_exit(divas_exit); diff --git a/drivers/isdn/hardware/eicon/divasproc.c b/drivers/isdn/hardware/eicon/divasproc.c new file mode 100644 index 000000000..f52f4622b --- /dev/null +++ b/drivers/isdn/hardware/eicon/divasproc.c @@ -0,0 +1,412 @@ +/* $Id: divasproc.c,v 1.19.4.3 2005/01/31 12:22:20 armin Exp $ + * + * Low level driver for Eicon DIVA Server ISDN cards. + * /proc functions + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/list.h> +#include <linux/uaccess.h> + +#include "platform.h" +#include "debuglib.h" +#undef ID_MASK +#undef N_DATA +#include "pc.h" +#include "di_defs.h" +#include "divasync.h" +#include "di.h" +#include "io.h" +#include "xdi_msg.h" +#include "xdi_adapter.h" +#include "diva.h" +#include "diva_pci.h" + + +extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER]; +extern void divas_get_version(char *); +extern void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf); + +/********************************************************* + ** Functions for /proc interface / File operations + *********************************************************/ + +static char *divas_proc_name = "divas"; +static char *adapter_dir_name = "adapter"; +static char *info_proc_name = "info"; +static char *grp_opt_proc_name = "group_optimization"; +static char *d_l1_down_proc_name = "dynamic_l1_down"; + +/* +** "divas" entry +*/ + +extern struct proc_dir_entry *proc_net_eicon; +static struct proc_dir_entry *divas_proc_entry = NULL; + +static ssize_t +divas_read(struct file *file, char __user *buf, size_t count, loff_t *off) +{ + int len = 0; + int cadapter; + char tmpbuf[80]; + char tmpser[16]; + + if (*off) + return 0; + + divas_get_version(tmpbuf); + if (copy_to_user(buf + len, &tmpbuf, strlen(tmpbuf))) + return -EFAULT; + len += strlen(tmpbuf); + + for (cadapter = 0; cadapter < MAX_ADAPTER; cadapter++) { + if (IoAdapters[cadapter]) { + diva_get_vserial_number(IoAdapters[cadapter], + tmpser); + sprintf(tmpbuf, + "%2d: %-30s Serial:%-10s IRQ:%2d\n", + cadapter + 1, + IoAdapters[cadapter]->Properties.Name, + tmpser, + IoAdapters[cadapter]->irq_info.irq_nr); + if ((strlen(tmpbuf) + len) > count) + break; + if (copy_to_user + (buf + len, &tmpbuf, + strlen(tmpbuf))) return -EFAULT; + len += strlen(tmpbuf); + } + } + + *off += len; + return (len); +} + +static ssize_t +divas_write(struct file *file, const char __user *buf, size_t count, loff_t *off) +{ + return (-ENODEV); +} + +static __poll_t divas_poll(struct file *file, poll_table *wait) +{ + return (EPOLLERR); +} + +static int divas_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +static int divas_close(struct inode *inode, struct file *file) +{ + return (0); +} + +static const struct file_operations divas_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = divas_read, + .write = divas_write, + .poll = divas_poll, + .open = divas_open, + .release = divas_close +}; + +int create_divas_proc(void) +{ + divas_proc_entry = proc_create(divas_proc_name, S_IFREG | S_IRUGO, + proc_net_eicon, &divas_fops); + if (!divas_proc_entry) + return (0); + + return (1); +} + +void remove_divas_proc(void) +{ + if (divas_proc_entry) { + remove_proc_entry(divas_proc_name, proc_net_eicon); + divas_proc_entry = NULL; + } +} + +static ssize_t grp_opt_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + diva_os_xdi_adapter_t *a = PDE_DATA(file_inode(file)); + PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1]; + + if ((count == 1) || (count == 2)) { + char c; + if (get_user(c, buffer)) + return -EFAULT; + switch (c) { + case '0': + IoAdapter->capi_cfg.cfg_1 &= + ~DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON; + break; + case '1': + IoAdapter->capi_cfg.cfg_1 |= + DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON; + break; + default: + return (-EINVAL); + } + return (count); + } + return (-EINVAL); +} + +static ssize_t d_l1_down_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + diva_os_xdi_adapter_t *a = PDE_DATA(file_inode(file)); + PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1]; + + if ((count == 1) || (count == 2)) { + char c; + if (get_user(c, buffer)) + return -EFAULT; + switch (c) { + case '0': + IoAdapter->capi_cfg.cfg_1 &= + ~DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON; + break; + case '1': + IoAdapter->capi_cfg.cfg_1 |= + DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON; + break; + default: + return (-EINVAL); + } + return (count); + } + return (-EINVAL); +} + +static int d_l1_down_proc_show(struct seq_file *m, void *v) +{ + diva_os_xdi_adapter_t *a = m->private; + PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1]; + + seq_printf(m, "%s\n", + (IoAdapter->capi_cfg. + cfg_1 & DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON) ? "1" : + "0"); + return 0; +} + +static int d_l1_down_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, d_l1_down_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations d_l1_down_proc_fops = { + .owner = THIS_MODULE, + .open = d_l1_down_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = d_l1_down_proc_write, +}; + +static int grp_opt_proc_show(struct seq_file *m, void *v) +{ + diva_os_xdi_adapter_t *a = m->private; + PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1]; + + seq_printf(m, "%s\n", + (IoAdapter->capi_cfg. + cfg_1 & DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON) + ? "1" : "0"); + return 0; +} + +static int grp_opt_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, grp_opt_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations grp_opt_proc_fops = { + .owner = THIS_MODULE, + .open = grp_opt_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = grp_opt_proc_write, +}; + +static ssize_t info_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + diva_os_xdi_adapter_t *a = PDE_DATA(file_inode(file)); + PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1]; + char c[4]; + + if (count <= 4) + return -EINVAL; + + if (copy_from_user(c, buffer, 4)) + return -EFAULT; + + /* this is for test purposes only */ + if (!memcmp(c, "trap", 4)) { + (*(IoAdapter->os_trap_nfy_Fnc)) (IoAdapter, IoAdapter->ANum); + return (count); + } + return (-EINVAL); +} + +static int info_proc_show(struct seq_file *m, void *v) +{ + int i = 0; + char *p; + char tmpser[16]; + diva_os_xdi_adapter_t *a = m->private; + PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1]; + + seq_printf(m, "Name : %s\n", IoAdapter->Properties.Name); + seq_printf(m, "DSP state : %08x\n", a->dsp_mask); + seq_printf(m, "Channels : %02d\n", IoAdapter->Properties.Channels); + seq_printf(m, "E. max/used : %03d/%03d\n", + IoAdapter->e_max, IoAdapter->e_count); + diva_get_vserial_number(IoAdapter, tmpser); + seq_printf(m, "Serial : %s\n", tmpser); + seq_printf(m, "IRQ : %d\n", IoAdapter->irq_info.irq_nr); + seq_printf(m, "CardIndex : %d\n", a->CardIndex); + seq_printf(m, "CardOrdinal : %d\n", a->CardOrdinal); + seq_printf(m, "Controller : %d\n", a->controller); + seq_printf(m, "Bus-Type : %s\n", + (a->Bus == + DIVAS_XDI_ADAPTER_BUS_ISA) ? "ISA" : "PCI"); + seq_printf(m, "Port-Name : %s\n", a->port_name); + if (a->Bus == DIVAS_XDI_ADAPTER_BUS_PCI) { + seq_printf(m, "PCI-bus : %d\n", a->resources.pci.bus); + seq_printf(m, "PCI-func : %d\n", a->resources.pci.func); + for (i = 0; i < 8; i++) { + if (a->resources.pci.bar[i]) { + seq_printf(m, + "Mem / I/O %d : 0x%x / mapped : 0x%lx", + i, a->resources.pci.bar[i], + (unsigned long) a->resources. + pci.addr[i]); + if (a->resources.pci.length[i]) { + seq_printf(m, + " / length : %d", + a->resources.pci. + length[i]); + } + seq_putc(m, '\n'); + } + } + } + if ((!a->xdi_adapter.port) && + ((!a->xdi_adapter.ram) || + (!a->xdi_adapter.reset) + || (!a->xdi_adapter.cfg))) { + if (!IoAdapter->irq_info.irq_nr) { + p = "slave"; + } else { + p = "out of service"; + } + } else if (a->xdi_adapter.trapped) { + p = "trapped"; + } else if (a->xdi_adapter.Initialized) { + p = "active"; + } else { + p = "ready"; + } + seq_printf(m, "State : %s\n", p); + + return 0; +} + +static int info_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, info_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations info_proc_fops = { + .owner = THIS_MODULE, + .open = info_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = info_proc_write, +}; + +/* +** adapter proc init/de-init +*/ + +/* -------------------------------------------------------------------------- + Create adapter directory and files in proc file system + -------------------------------------------------------------------------- */ +int create_adapter_proc(diva_os_xdi_adapter_t *a) +{ + struct proc_dir_entry *de, *pe; + char tmp[16]; + + sprintf(tmp, "%s%d", adapter_dir_name, a->controller); + if (!(de = proc_mkdir(tmp, proc_net_eicon))) + return (0); + a->proc_adapter_dir = (void *) de; + + pe = proc_create_data(info_proc_name, S_IRUGO | S_IWUSR, de, + &info_proc_fops, a); + if (!pe) + return (0); + a->proc_info = (void *) pe; + + pe = proc_create_data(grp_opt_proc_name, S_IRUGO | S_IWUSR, de, + &grp_opt_proc_fops, a); + if (pe) + a->proc_grp_opt = (void *) pe; + pe = proc_create_data(d_l1_down_proc_name, S_IRUGO | S_IWUSR, de, + &d_l1_down_proc_fops, a); + if (pe) + a->proc_d_l1_down = (void *) pe; + + DBG_TRC(("proc entry %s created", tmp)); + + return (1); +} + +/* -------------------------------------------------------------------------- + Remove adapter directory and files in proc file system + -------------------------------------------------------------------------- */ +void remove_adapter_proc(diva_os_xdi_adapter_t *a) +{ + char tmp[16]; + + if (a->proc_adapter_dir) { + if (a->proc_d_l1_down) { + remove_proc_entry(d_l1_down_proc_name, + (struct proc_dir_entry *) a->proc_adapter_dir); + } + if (a->proc_grp_opt) { + remove_proc_entry(grp_opt_proc_name, + (struct proc_dir_entry *) a->proc_adapter_dir); + } + if (a->proc_info) { + remove_proc_entry(info_proc_name, + (struct proc_dir_entry *) a->proc_adapter_dir); + } + sprintf(tmp, "%s%d", adapter_dir_name, a->controller); + remove_proc_entry(tmp, proc_net_eicon); + DBG_TRC(("proc entry %s%d removed", adapter_dir_name, + a->controller)); + } +} diff --git a/drivers/isdn/hardware/eicon/divasync.h b/drivers/isdn/hardware/eicon/divasync.h new file mode 100644 index 000000000..dd6b53a2c --- /dev/null +++ b/drivers/isdn/hardware/eicon/divasync.h @@ -0,0 +1,489 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_SYNC__H +#define __DIVA_SYNC__H +#define IDI_SYNC_REQ_REMOVE 0x00 +#define IDI_SYNC_REQ_GET_NAME 0x01 +#define IDI_SYNC_REQ_GET_SERIAL 0x02 +#define IDI_SYNC_REQ_SET_POSTCALL 0x03 +#define IDI_SYNC_REQ_GET_XLOG 0x04 +#define IDI_SYNC_REQ_GET_FEATURES 0x05 +#define IDI_SYNC_REQ_USB_REGISTER 0x06 +#define IDI_SYNC_REQ_USB_RELEASE 0x07 +#define IDI_SYNC_REQ_USB_ADD_DEVICE 0x08 +#define IDI_SYNC_REQ_USB_START_DEVICE 0x09 +#define IDI_SYNC_REQ_USB_STOP_DEVICE 0x0A +#define IDI_SYNC_REQ_USB_REMOVE_DEVICE 0x0B +#define IDI_SYNC_REQ_GET_CARDTYPE 0x0C +#define IDI_SYNC_REQ_GET_DBG_XLOG 0x0D +#define DIVA_USB +#define DIVA_USB_REQ 0xAC +#define DIVA_USB_TEST 0xAB +#define DIVA_USB_ADD_ADAPTER 0xAC +#define DIVA_USB_REMOVE_ADAPTER 0xAD +#define IDI_SYNC_REQ_SERIAL_HOOK 0x80 +#define IDI_SYNC_REQ_XCHANGE_STATUS 0x81 +#define IDI_SYNC_REQ_USB_HOOK 0x82 +#define IDI_SYNC_REQ_PORTDRV_HOOK 0x83 +#define IDI_SYNC_REQ_SLI 0x84 /* SLI request from 3signal modem drivers */ +#define IDI_SYNC_REQ_RECONFIGURE 0x85 +#define IDI_SYNC_REQ_RESET 0x86 +#define IDI_SYNC_REQ_GET_85X_DEVICE_DATA 0x87 +#define IDI_SYNC_REQ_LOCK_85X 0x88 +#define IDI_SYNC_REQ_DIVA_85X_USB_DATA_EXCHANGE 0x99 +#define IDI_SYNC_REQ_DIPORT_EXCHANGE_REQ 0x98 +#define IDI_SYNC_REQ_GET_85X_EXT_PORT_TYPE 0xA0 +/******************************************************************************/ +#define IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES 0x92 +/* + To receive XDI features: + 1. set 'buffer_length_in_bytes' to length of you buffer + 2. set 'features' to pointer to your buffer + 3. issue synchronous request to XDI + 4. Check that feature 'DIVA_XDI_EXTENDED_FEATURES_VALID' is present + after call. This feature does indicate that your request + was processed and XDI does support this synchronous request + 5. if on return bit 31 (0x80000000) in 'buffer_length_in_bytes' is + set then provided buffer was too small, and bits 30-0 does + contain necessary length of buffer. + in this case only features that do find place in the buffer + are indicated to caller +*/ +typedef struct _diva_xdi_get_extended_xdi_features { + dword buffer_length_in_bytes; + byte *features; +} diva_xdi_get_extended_xdi_features_t; +/* + features[0] +*/ +#define DIVA_XDI_EXTENDED_FEATURES_VALID 0x01 +#define DIVA_XDI_EXTENDED_FEATURE_CMA 0x02 +#define DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR 0x04 +#define DIVA_XDI_EXTENDED_FEATURE_CAPI_PRMS 0x08 +#define DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC 0x10 +#define DIVA_XDI_EXTENDED_FEATURE_RX_DMA 0x20 +#define DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA 0x40 +#define DIVA_XDI_EXTENDED_FEATURE_WIDE_ID 0x80 +#define DIVA_XDI_EXTENDED_FEATURES_MAX_SZ 1 +/******************************************************************************/ +#define IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR 0x93 +typedef struct _diva_xdi_get_adapter_sdram_bar { + dword bar; +} diva_xdi_get_adapter_sdram_bar_t; +/******************************************************************************/ +#define IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS 0x94 +/* + CAPI Parameters will be written in the caller's buffer +*/ +typedef struct _diva_xdi_get_capi_parameters { + dword structure_length; + byte flag_dynamic_l1_down; + byte group_optimization_enabled; +} diva_xdi_get_capi_parameters_t; +/******************************************************************************/ +#define IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER 0x95 +/* + Get logical adapter number, as assigned by XDI + 'controller' is starting with zero 'sub' controller number + in case of one adapter that supports multiple interfaces + 'controller' is zero for Master adapter (and adapter that supports + only one interface) +*/ +typedef struct _diva_xdi_get_logical_adapter_number { + dword logical_adapter_number; + dword controller; + dword total_controllers; +} diva_xdi_get_logical_adapter_number_s_t; +/******************************************************************************/ +#define IDI_SYNC_REQ_UP1DM_OPERATION 0x96 +/******************************************************************************/ +#define IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION 0x97 +#define IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC 0x01 +#define IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE 0x02 +typedef struct _diva_xdi_dma_descriptor_operation { + int operation; + int descriptor_number; + void *descriptor_address; + dword descriptor_magic; +} diva_xdi_dma_descriptor_operation_t; +/******************************************************************************/ +#define IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY 0x01 +#define IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY 0x02 +#define IDI_SYNC_REQ_DIDD_ADD_ADAPTER 0x03 +#define IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER 0x04 +#define IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY 0x05 +#define IDI_SYNC_REQ_DIDD_GET_CFG_LIB_IFC 0x10 +typedef struct _diva_didd_adapter_notify { + dword handle; /* Notification handle */ + void *callback; + void *context; +} diva_didd_adapter_notify_t; +typedef struct _diva_didd_add_adapter { + void *descriptor; +} diva_didd_add_adapter_t; +typedef struct _diva_didd_remove_adapter { + IDI_CALL p_request; +} diva_didd_remove_adapter_t; +typedef struct _diva_didd_read_adapter_array { + void *buffer; + dword length; +} diva_didd_read_adapter_array_t; +typedef struct _diva_didd_get_cfg_lib_ifc { + void *ifc; +} diva_didd_get_cfg_lib_ifc_t; +/******************************************************************************/ +#define IDI_SYNC_REQ_XDI_GET_STREAM 0x91 +#define DIVA_XDI_SYNCHRONOUS_SERVICE 0x01 +#define DIVA_XDI_DMA_SERVICE 0x02 +#define DIVA_XDI_AUTO_SERVICE 0x03 +#define DIVA_ISTREAM_COMPLETE_NOTIFY 0 +#define DIVA_ISTREAM_COMPLETE_READ 1 +#define DIVA_ISTREAM_COMPLETE_WRITE 2 +typedef struct _diva_xdi_stream_interface { + unsigned char Id; /* filled by XDI client */ + unsigned char provided_service; /* filled by XDI */ + unsigned char requested_service; /* filled by XDI Client */ + void *xdi_context; /* filled by XDI */ + void *client_context; /* filled by XDI client */ + int (*write)(void *context, + int Id, + void *data, + int length, + int final, + byte usr1, + byte usr2); + int (*read)(void *context, + int Id, + void *data, + int max_length, + int *final, + byte *usr1, + byte *usr2); + int (*complete)(void *client_context, + int Id, + int what, + void *data, + int length, + int *final); +} diva_xdi_stream_interface_t; +/******************************************************************************/ +/* + * IDI_SYNC_REQ_SERIAL_HOOK - special interface for the DIVA Mobile card + */ +typedef struct +{ unsigned char LineState; /* Modem line state (STATUS_R) */ +#define SERIAL_GSM_CELL 0x01 /* GSM or CELL cable attached */ + unsigned char CardState; /* PCMCIA card state (0 = down) */ + unsigned char IsdnState; /* ISDN layer 1 state (0 = down)*/ + unsigned char HookState; /* current logical hook state */ +#define SERIAL_ON_HOOK 0x02 /* set in DIVA CTRL_R register */ +} SERIAL_STATE; +typedef int (*SERIAL_INT_CB)(void *Context); +typedef int (*SERIAL_DPC_CB)(void *Context); +typedef unsigned char (*SERIAL_I_SYNC)(void *Context); +typedef struct +{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */ + unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (is the request) */ + unsigned char Function; /* private function code */ +#define SERIAL_HOOK_ATTACH 0x81 +#define SERIAL_HOOK_STATUS 0x82 +#define SERIAL_HOOK_I_SYNC 0x83 +#define SERIAL_HOOK_NOECHO 0x84 +#define SERIAL_HOOK_RING 0x85 +#define SERIAL_HOOK_DETACH 0x8f + unsigned char Flags; /* function refinements */ + /* parameters passed by the ATTACH request */ + SERIAL_INT_CB InterruptHandler; /* called on each interrupt */ + SERIAL_DPC_CB DeferredHandler; /* called on hook state changes */ + void *HandlerContext; /* context for both handlers */ + /* return values for both the ATTACH and the STATUS request */ + unsigned long IoBase; /* IO port assigned to UART */ + SERIAL_STATE State; + /* parameters and return values for the I_SYNC function */ + SERIAL_I_SYNC SyncFunction; /* to be called synchronized */ + void *SyncContext; /* context for this function */ + unsigned char SyncResult; /* return value of function */ +} SERIAL_HOOK; +/* + * IDI_SYNC_REQ_XCHANGE_STATUS - exchange the status between IDI and WMP + * IDI_SYNC_REQ_RECONFIGURE - reconfiguration of IDI from WMP + */ +typedef struct +{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */ + unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (is the request) */ +#define DRIVER_STATUS_BOOT 0xA1 +#define DRIVER_STATUS_INIT_DEV 0xA2 +#define DRIVER_STATUS_RUNNING 0xA3 +#define DRIVER_STATUS_SHUTDOWN 0xAF +#define DRIVER_STATUS_TRAPPED 0xAE + unsigned char wmpStatus; /* exported by WMP */ + unsigned char idiStatus; /* exported by IDI */ + unsigned long wizProto; /* from WMP registry to IDI */ + /* the cardtype value is defined by cardtype.h */ + unsigned long cardType; /* from IDI registry to WMP */ + unsigned long nt2; /* from IDI registry to WMP */ + unsigned long permanent; /* from IDI registry to WMP */ + unsigned long stableL2; /* from IDI registry to WMP */ + unsigned long tei; /* from IDI registry to WMP */ +#define CRC4_MASK 0x00000003 +#define L1_TRISTATE_MASK 0x00000004 +#define WATCHDOG_MASK 0x00000008 +#define NO_ORDER_CHECK_MASK 0x00000010 +#define LOW_CHANNEL_MASK 0x00000020 +#define NO_HSCX30_MASK 0x00000040 +#define SET_BOARD 0x00001000 +#define SET_CRC4 0x00030000 +#define SET_L1_TRISTATE 0x00040000 +#define SET_WATCHDOG 0x00080000 +#define SET_NO_ORDER_CHECK 0x00100000 +#define SET_LOW_CHANNEL 0x00200000 +#define SET_NO_HSCX30 0x00400000 +#define SET_MODE 0x00800000 +#define SET_PROTO 0x02000000 +#define SET_CARDTYPE 0x04000000 +#define SET_NT2 0x08000000 +#define SET_PERMANENT 0x10000000 +#define SET_STABLEL2 0x20000000 +#define SET_TEI 0x40000000 +#define SET_NUMBERLEN 0x80000000 + unsigned long Flag; /* |31-Type-16|15-Mask-0| */ + unsigned long NumberLen; /* reconfiguration: union is empty */ + union { + struct { /* possible reconfiguration, but ... ; SET_BOARD */ + unsigned long SerialNumber; + char *pCardname; /* di_defs.h: BOARD_NAME_LENGTH */ + } board; + struct { /* reset: need resources */ + void *pRawResources; + void *pXlatResources; + } res; + struct { /* reconfiguration: wizProto == PROTTYPE_RBSCAS */ +#define GLARE_RESOLVE_MASK 0x00000001 +#define DID_MASK 0x00000002 +#define BEARER_CAP_MASK 0x0000000c +#define SET_GLARE_RESOLVE 0x00010000 +#define SET_DID 0x00020000 +#define SET_BEARER_CAP 0x000c0000 + unsigned long Flag; /* |31-Type-16|15-VALUE-0| */ + unsigned short DigitTimeout; + unsigned short AnswerDelay; + } rbs; + struct { /* reconfiguration: wizProto == PROTTYPE_QSIG */ +#define CALL_REF_LENGTH1_MASK 0x00000001 +#define BRI_CHANNEL_ID_MASK 0x00000002 +#define SET_CALL_REF_LENGTH 0x00010000 +#define SET_BRI_CHANNEL_ID 0x00020000 + unsigned long Flag; /* |31-Type-16|15-VALUE-0| */ + } qsig; + struct { /* reconfiguration: NumberLen != 0 */ +#define SET_SPID1 0x00010000 +#define SET_NUMBER1 0x00020000 +#define SET_SUBADDRESS1 0x00040000 +#define SET_SPID2 0x00100000 +#define SET_NUMBER2 0x00200000 +#define SET_SUBADDRESS2 0x00400000 +#define MASK_SET 0xffff0000 + unsigned long Flag; /* |31-Type-16|15-Channel-0| */ + unsigned char *pBuffer; /* number value */ + } isdnNo; + } + parms + ; +} isdnProps; +/* + * IDI_SYNC_REQ_PORTDRV_HOOK - signal plug/unplug (Award Cardware only) + */ +typedef void (*PORTDRV_HOOK_CB)(void *Context, int Plug); +typedef struct +{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */ + unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (is the request) */ + unsigned char Function; /* private function code */ + unsigned char Flags; /* function refinements */ + PORTDRV_HOOK_CB Callback; /* to be called on plug/unplug */ + void *Context; /* context for callback */ + unsigned long Info; /* more info if needed */ +} PORTDRV_HOOK; +/* Codes for the 'Rc' element in structure below. */ +#define SLI_INSTALL (0xA1) +#define SLI_UNINSTALL (0xA2) +typedef int (*SLIENTRYPOINT)(void *p3SignalAPI, void *pContext); +typedef struct +{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */ + unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (is the request) */ + unsigned char Function; /* private function code */ + unsigned char Flags; /* function refinements */ + SLIENTRYPOINT Callback; /* to be called on plug/unplug */ + void *Context; /* context for callback */ + unsigned long Info; /* more info if needed */ +} SLIENTRYPOINT_REQ; +/******************************************************************************/ +/* + * Definitions for DIVA USB + */ +typedef int (*USB_SEND_REQ)(unsigned char PipeIndex, unsigned char Type, void *Data, int sizeData); +typedef int (*USB_START_DEV)(void *Adapter, void *Ipac); +/* called from WDM */ +typedef void (*USB_RECV_NOTIFY)(void *Ipac, void *msg); +typedef void (*USB_XMIT_NOTIFY)(void *Ipac, unsigned char PipeIndex); +/******************************************************************************/ +/* + * Parameter description for synchronous requests. + * + * Sorry, must repeat some parts of di_defs.h here because + * they are not defined for all operating environments + */ +typedef union +{ ENTITY Entity; + struct + { /* 'Req' and 'Rc' are at the same place as in the ENTITY struct */ + unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (is the request) */ + } Request; + struct + { unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (0x01) */ + unsigned char name[BOARD_NAME_LENGTH]; + } GetName; + struct + { unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (0x02) */ + unsigned long serial; /* serial number */ + } GetSerial; + struct + { unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (0x02) */ + unsigned long lineIdx;/* line, 0 if card has only one */ + } GetLineIdx; + struct + { unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (0x02) */ + unsigned long cardtype;/* card type */ + } GetCardType; + struct + { unsigned short command;/* command = 0x0300 */ + unsigned short dummy; /* not used */ + IDI_CALL callback;/* routine to call back */ + ENTITY *contxt; /* ptr to entity to use */ + } PostCall; + struct + { unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (0x04) */ + unsigned char pcm[1]; /* buffer (a pc_maint struct) */ + } GetXlog; + struct + { unsigned char Req; /* request (must be always 0) */ + unsigned char Rc; /* return code (0x05) */ + unsigned short features;/* feature defines see below */ + } GetFeatures; + SERIAL_HOOK SerialHook; +/* Added for DIVA USB */ + struct + { unsigned char Req; + unsigned char Rc; + USB_SEND_REQ UsbSendRequest; /* function in Diva Usb WDM driver in usb_os.c, */ + /* called from usb_drv.c to send a message to our device */ + /* eg UsbSendRequest (USB_PIPE_SIGNAL, USB_IPAC_START, 0, 0); */ + USB_RECV_NOTIFY usb_recv; /* called from usb_os.c to pass a received message and ptr to IPAC */ + /* on to usb_drv.c by a call to usb_recv(). */ + USB_XMIT_NOTIFY usb_xmit; /* called from usb_os.c in DivaUSB.sys WDM to indicate a completed transmit */ + /* to usb_drv.c by a call to usb_xmit(). */ + USB_START_DEV UsbStartDevice; /* Start the USB Device, in usb_os.c */ + IDI_CALL callback; /* routine to call back */ + ENTITY *contxt; /* ptr to entity to use */ + void **ipac_ptr; /* pointer to struct IPAC in VxD */ + } Usb_Msg_old; +/* message used by WDM and VXD to pass pointers of function and IPAC* */ + struct + { unsigned char Req; + unsigned char Rc; + USB_SEND_REQ pUsbSendRequest;/* function in Diva Usb WDM driver in usb_os.c, */ + /* called from usb_drv.c to send a message to our device */ + /* eg UsbSendRequest (USB_PIPE_SIGNAL, USB_IPAC_START, 0, 0); */ + USB_RECV_NOTIFY p_usb_recv; /* called from usb_os.c to pass a received message and ptr to IPAC */ + /* on to usb_drv.c by a call to usb_recv(). */ + USB_XMIT_NOTIFY p_usb_xmit; /* called from usb_os.c in DivaUSB.sys WDM to indicate a completed transmit */ + /* to usb_drv.c by a call to usb_xmit().*/ + void *ipac_ptr; /* &Diva.ipac pointer to struct IPAC in VxD */ + } Usb_Msg; + PORTDRV_HOOK PortdrvHook; + SLIENTRYPOINT_REQ sliEntryPointReq; + struct { + unsigned char Req; + unsigned char Rc; + diva_xdi_stream_interface_t info; + } xdi_stream_info; + struct { + unsigned char Req; + unsigned char Rc; + diva_xdi_get_extended_xdi_features_t info; + } xdi_extended_features; + struct { + unsigned char Req; + unsigned char Rc; + diva_xdi_get_adapter_sdram_bar_t info; + } xdi_sdram_bar; + struct { + unsigned char Req; + unsigned char Rc; + diva_xdi_get_capi_parameters_t info; + } xdi_capi_prms; + struct { + ENTITY e; + diva_didd_adapter_notify_t info; + } didd_notify; + struct { + ENTITY e; + diva_didd_add_adapter_t info; + } didd_add_adapter; + struct { + ENTITY e; + diva_didd_remove_adapter_t info; + } didd_remove_adapter; + struct { + ENTITY e; + diva_didd_read_adapter_array_t info; + } didd_read_adapter_array; + struct { + ENTITY e; + diva_didd_get_cfg_lib_ifc_t info; + } didd_get_cfg_lib_ifc; + struct { + unsigned char Req; + unsigned char Rc; + diva_xdi_get_logical_adapter_number_s_t info; + } xdi_logical_adapter_number; + struct { + unsigned char Req; + unsigned char Rc; + diva_xdi_dma_descriptor_operation_t info; + } xdi_dma_descriptor_operation; +} IDI_SYNC_REQ; +/******************************************************************************/ +#endif /* __DIVA_SYNC__H */ diff --git a/drivers/isdn/hardware/eicon/dqueue.c b/drivers/isdn/hardware/eicon/dqueue.c new file mode 100644 index 000000000..7958a2536 --- /dev/null +++ b/drivers/isdn/hardware/eicon/dqueue.c @@ -0,0 +1,110 @@ +/* $Id: dqueue.c,v 1.5 2003/04/12 21:40:49 schindler Exp $ + * + * Driver for Eicon DIVA Server ISDN cards. + * User Mode IDI Interface + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include "platform.h" +#include "dqueue.h" + +int +diva_data_q_init(diva_um_idi_data_queue_t *q, + int max_length, int max_segments) +{ + int i; + + q->max_length = max_length; + q->segments = max_segments; + + for (i = 0; i < q->segments; i++) { + q->data[i] = NULL; + q->length[i] = 0; + } + q->read = q->write = q->count = q->segment_pending = 0; + + for (i = 0; i < q->segments; i++) { + if (!(q->data[i] = diva_os_malloc(0, q->max_length))) { + diva_data_q_finit(q); + return (-1); + } + } + + return (0); +} + +int diva_data_q_finit(diva_um_idi_data_queue_t *q) +{ + int i; + + for (i = 0; i < q->segments; i++) { + if (q->data[i]) { + diva_os_free(0, q->data[i]); + } + q->data[i] = NULL; + q->length[i] = 0; + } + q->read = q->write = q->count = q->segment_pending = 0; + + return (0); +} + +int diva_data_q_get_max_length(const diva_um_idi_data_queue_t *q) +{ + return (q->max_length); +} + +void *diva_data_q_get_segment4write(diva_um_idi_data_queue_t *q) +{ + if ((!q->segment_pending) && (q->count < q->segments)) { + q->segment_pending = 1; + return (q->data[q->write]); + } + + return NULL; +} + +void +diva_data_q_ack_segment4write(diva_um_idi_data_queue_t *q, int length) +{ + if (q->segment_pending) { + q->length[q->write] = length; + q->count++; + q->write++; + if (q->write >= q->segments) { + q->write = 0; + } + q->segment_pending = 0; + } +} + +const void *diva_data_q_get_segment4read(const diva_um_idi_data_queue_t * + q) +{ + if (q->count) { + return (q->data[q->read]); + } + return NULL; +} + +int diva_data_q_get_segment_length(const diva_um_idi_data_queue_t *q) +{ + return (q->length[q->read]); +} + +void diva_data_q_ack_segment4read(diva_um_idi_data_queue_t *q) +{ + if (q->count) { + q->length[q->read] = 0; + q->count--; + q->read++; + if (q->read >= q->segments) { + q->read = 0; + } + } +} diff --git a/drivers/isdn/hardware/eicon/dqueue.h b/drivers/isdn/hardware/eicon/dqueue.h new file mode 100644 index 000000000..2da979968 --- /dev/null +++ b/drivers/isdn/hardware/eicon/dqueue.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: dqueue.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */ + +#ifndef _DIVA_USER_MODE_IDI_DATA_QUEUE_H__ +#define _DIVA_USER_MODE_IDI_DATA_QUEUE_H__ + +#define DIVA_UM_IDI_MAX_MSGS 64 + +typedef struct _diva_um_idi_data_queue { + int segments; + int max_length; + int read; + int write; + int count; + int segment_pending; + void *data[DIVA_UM_IDI_MAX_MSGS]; + int length[DIVA_UM_IDI_MAX_MSGS]; +} diva_um_idi_data_queue_t; + +int diva_data_q_init(diva_um_idi_data_queue_t *q, + int max_length, int max_segments); +int diva_data_q_finit(diva_um_idi_data_queue_t *q); +int diva_data_q_get_max_length(const diva_um_idi_data_queue_t *q); +void *diva_data_q_get_segment4write(diva_um_idi_data_queue_t *q); +void diva_data_q_ack_segment4write(diva_um_idi_data_queue_t *q, + int length); +const void *diva_data_q_get_segment4read(const diva_um_idi_data_queue_t * + q); +int diva_data_q_get_segment_length(const diva_um_idi_data_queue_t *q); +void diva_data_q_ack_segment4read(diva_um_idi_data_queue_t *q); + +#endif diff --git a/drivers/isdn/hardware/eicon/dsp_defs.h b/drivers/isdn/hardware/eicon/dsp_defs.h new file mode 100644 index 000000000..94828c87e --- /dev/null +++ b/drivers/isdn/hardware/eicon/dsp_defs.h @@ -0,0 +1,301 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef DSP_DEFS_H_ +#define DSP_DEFS_H_ +#include "dspdids.h" +/*---------------------------------------------------------------------------*/ +#define dsp_download_reserve_space(fp, length) +/*****************************************************************************/ +/* + * OS file access abstraction layer + * + * I/O functions returns -1 on error, 0 on EOF + */ +struct _OsFileHandle_; +typedef long (*OsFileIo)(struct _OsFileHandle_ *handle, + void *buffer, + long size); +typedef long (*OsFileSeek)(struct _OsFileHandle_ *handle, + long position, + int mode); +typedef long (*OsCardLoad)(struct _OsFileHandle_ *handle, + long length, + void **addr); +typedef struct _OsFileHandle_ +{ void *sysFileDesc; + unsigned long sysFileSize; + OsFileIo sysFileRead; + OsFileSeek sysFileSeek; + void *sysLoadDesc; + OsCardLoad sysCardLoad; +} OsFileHandle; +extern OsFileHandle *OsOpenFile(char *path_name); +extern void OsCloseFile(OsFileHandle *fp); +/*****************************************************************************/ +#define DSP_TELINDUS_FILE "dspdload.bin" +/* special DSP file for BRI cards for Qsig and CornetN because of missing memory */ +#define DSP_QSIG_TELINDUS_FILE "dspdqsig.bin" +#define DSP_MDM_TELINDUS_FILE "dspdvmdm.bin" +#define DSP_FAX_TELINDUS_FILE "dspdvfax.bin" +#define DSP_DIRECTORY_ENTRIES 64 +#define DSP_MEMORY_TYPE_EXTERNAL_DM 0 +#define DSP_MEMORY_TYPE_EXTERNAL_PM 1 +#define DSP_MEMORY_TYPE_INTERNAL_DM 2 +#define DSP_MEMORY_TYPE_INTERNAL_PM 3 +#define DSP_DOWNLOAD_FLAG_BOOTABLE 0x0001 +#define DSP_DOWNLOAD_FLAG_2181 0x0002 +#define DSP_DOWNLOAD_FLAG_TIMECRITICAL 0x0004 +#define DSP_DOWNLOAD_FLAG_COMPAND 0x0008 +#define DSP_MEMORY_BLOCK_COUNT 16 +#define DSP_SEGMENT_PM_FLAG 0x0001 +#define DSP_SEGMENT_SHARED_FLAG 0x0002 +#define DSP_SEGMENT_EXTERNAL_DM DSP_MEMORY_TYPE_EXTERNAL_DM +#define DSP_SEGMENT_EXTERNAL_PM DSP_MEMORY_TYPE_EXTERNAL_PM +#define DSP_SEGMENT_INTERNAL_DM DSP_MEMORY_TYPE_INTERNAL_DM +#define DSP_SEGMENT_INTERNAL_PM DSP_MEMORY_TYPE_INTERNAL_PM +#define DSP_SEGMENT_FIRST_RELOCATABLE 4 +#define DSP_DATA_BLOCK_PM_FLAG 0x0001 +#define DSP_DATA_BLOCK_DWORD_FLAG 0x0002 +#define DSP_DATA_BLOCK_RESOLVE_FLAG 0x0004 +#define DSP_RELOC_NONE 0x00 +#define DSP_RELOC_SEGMENT_MASK 0x3f +#define DSP_RELOC_TYPE_MASK 0xc0 +#define DSP_RELOC_TYPE_0 0x00 /* relocation of address in DM word / high part of PM word */ +#define DSP_RELOC_TYPE_1 0x40 /* relocation of address in low part of PM data word */ +#define DSP_RELOC_TYPE_2 0x80 /* relocation of address in standard command */ +#define DSP_RELOC_TYPE_3 0xc0 /* relocation of address in call/jump on flag in */ +#define DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE 48 +#define DSP_COMBIFILE_FORMAT_VERSION_BCD 0x0100 +#define DSP_FILE_FORMAT_IDENTIFICATION_SIZE 48 +#define DSP_FILE_FORMAT_VERSION_BCD 0x0100 +typedef struct tag_dsp_combifile_header +{ + char format_identification[DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE]; + word format_version_bcd; + word header_size; + word combifile_description_size; + word directory_entries; + word directory_size; + word download_count; + word usage_mask_size; +} t_dsp_combifile_header; +typedef struct tag_dsp_combifile_directory_entry +{ + word card_type_number; + word file_set_number; +} t_dsp_combifile_directory_entry; +typedef struct tag_dsp_file_header +{ + char format_identification[DSP_FILE_FORMAT_IDENTIFICATION_SIZE]; + word format_version_bcd; + word download_id; + word download_flags; + word required_processing_power; + word interface_channel_count; + word header_size; + word download_description_size; + word memory_block_table_size; + word memory_block_count; + word segment_table_size; + word segment_count; + word symbol_table_size; + word symbol_count; + word total_data_size_dm; + word data_block_count_dm; + word total_data_size_pm; + word data_block_count_pm; +} t_dsp_file_header; +typedef struct tag_dsp_memory_block_desc +{ + word alias_memory_block; + word memory_type; + word address; + word size; /* DSP words */ +} t_dsp_memory_block_desc; +typedef struct tag_dsp_segment_desc +{ + word memory_block; + word attributes; + word base; + word size; + word alignment; /* ==0 -> no other legal start address than base */ +} t_dsp_segment_desc; +typedef struct tag_dsp_symbol_desc +{ + word symbol_id; + word segment; + word offset; + word size; /* DSP words */ +} t_dsp_symbol_desc; +typedef struct tag_dsp_data_block_header +{ + word attributes; + word segment; + word offset; + word size; /* DSP words */ +} t_dsp_data_block_header; +typedef struct tag_dsp_download_desc +{ + word download_id; + word download_flags; + word required_processing_power; + word interface_channel_count; + word excess_header_size; + word memory_block_count; + word segment_count; + word symbol_count; + word data_block_count_dm; + word data_block_count_pm; + byte *p_excess_header_data; + char *p_download_description; + t_dsp_memory_block_desc *p_memory_block_table; + t_dsp_segment_desc *p_segment_table; + t_dsp_symbol_desc *p_symbol_table; + word *p_data_blocks_dm; + word *p_data_blocks_pm; +} t_dsp_desc; +typedef struct tag_dsp_portable_download_desc /* be sure to keep native alignment for MAESTRA's */ +{ + word download_id; + word download_flags; + word required_processing_power; + word interface_channel_count; + word excess_header_size; + word memory_block_count; + word segment_count; + word symbol_count; + word data_block_count_dm; + word data_block_count_pm; + dword p_excess_header_data; + dword p_download_description; + dword p_memory_block_table; + dword p_segment_table; + dword p_symbol_table; + dword p_data_blocks_dm; + dword p_data_blocks_pm; +} t_dsp_portable_desc; +#define DSP_DOWNLOAD_INDEX_KERNEL 0 +#define DSP30TX_DOWNLOAD_INDEX_KERNEL 1 +#define DSP30RX_DOWNLOAD_INDEX_KERNEL 2 +#define DSP_MAX_DOWNLOAD_COUNT 64 +#define DSP_DOWNLOAD_MAX_SEGMENTS 16 +#define DSP_UDATA_REQUEST_RECONFIGURE 0 +/* + parameters: + <word> reconfigure delay (in 8kHz samples) + <word> reconfigure code + <byte> reconfigure hdlc preamble flags +*/ +#define DSP_RECONFIGURE_TX_FLAG 0x8000 +#define DSP_RECONFIGURE_SHORT_TRAIN_FLAG 0x4000 +#define DSP_RECONFIGURE_ECHO_PROTECT_FLAG 0x2000 +#define DSP_RECONFIGURE_HDLC_FLAG 0x1000 +#define DSP_RECONFIGURE_SYNC_FLAG 0x0800 +#define DSP_RECONFIGURE_PROTOCOL_MASK 0x00ff +#define DSP_RECONFIGURE_IDLE 0 +#define DSP_RECONFIGURE_V25 1 +#define DSP_RECONFIGURE_V21_CH2 2 +#define DSP_RECONFIGURE_V27_2400 3 +#define DSP_RECONFIGURE_V27_4800 4 +#define DSP_RECONFIGURE_V29_7200 5 +#define DSP_RECONFIGURE_V29_9600 6 +#define DSP_RECONFIGURE_V33_12000 7 +#define DSP_RECONFIGURE_V33_14400 8 +#define DSP_RECONFIGURE_V17_7200 9 +#define DSP_RECONFIGURE_V17_9600 10 +#define DSP_RECONFIGURE_V17_12000 11 +#define DSP_RECONFIGURE_V17_14400 12 +/* + data indications if transparent framer + <byte> data 0 + <byte> data 1 + ... + data indications if HDLC framer + <byte> data 0 + <byte> data 1 + ... + <byte> CRC 0 + <byte> CRC 1 + <byte> preamble flags +*/ +#define DSP_UDATA_INDICATION_SYNC 0 +/* + returns: + <word> time of sync (sampled from counter at 8kHz) +*/ +#define DSP_UDATA_INDICATION_DCD_OFF 1 +/* + returns: + <word> time of DCD off (sampled from counter at 8kHz) +*/ +#define DSP_UDATA_INDICATION_DCD_ON 2 +/* + returns: + <word> time of DCD on (sampled from counter at 8kHz) + <byte> connected norm + <word> connected options + <dword> connected speed (bit/s) +*/ +#define DSP_UDATA_INDICATION_CTS_OFF 3 +/* + returns: + <word> time of CTS off (sampled from counter at 8kHz) +*/ +#define DSP_UDATA_INDICATION_CTS_ON 4 +/* + returns: + <word> time of CTS on (sampled from counter at 8kHz) + <byte> connected norm + <word> connected options + <dword> connected speed (bit/s) +*/ +#define DSP_CONNECTED_NORM_UNSPECIFIED 0 +#define DSP_CONNECTED_NORM_V21 1 +#define DSP_CONNECTED_NORM_V23 2 +#define DSP_CONNECTED_NORM_V22 3 +#define DSP_CONNECTED_NORM_V22_BIS 4 +#define DSP_CONNECTED_NORM_V32_BIS 5 +#define DSP_CONNECTED_NORM_V34 6 +#define DSP_CONNECTED_NORM_V8 7 +#define DSP_CONNECTED_NORM_BELL_212A 8 +#define DSP_CONNECTED_NORM_BELL_103 9 +#define DSP_CONNECTED_NORM_V29_LEASED_LINE 10 +#define DSP_CONNECTED_NORM_V33_LEASED_LINE 11 +#define DSP_CONNECTED_NORM_TFAST 12 +#define DSP_CONNECTED_NORM_V21_CH2 13 +#define DSP_CONNECTED_NORM_V27_TER 14 +#define DSP_CONNECTED_NORM_V29 15 +#define DSP_CONNECTED_NORM_V33 16 +#define DSP_CONNECTED_NORM_V17 17 +#define DSP_CONNECTED_OPTION_TRELLIS 0x0001 +/*---------------------------------------------------------------------------*/ +extern char *dsp_read_file(OsFileHandle *fp, + word card_type_number, + word *p_dsp_download_count, + t_dsp_desc *p_dsp_download_table, + t_dsp_portable_desc *p_dsp_portable_download_table); +/*---------------------------------------------------------------------------*/ +#endif /* DSP_DEFS_H_ */ diff --git a/drivers/isdn/hardware/eicon/dsp_tst.h b/drivers/isdn/hardware/eicon/dsp_tst.h new file mode 100644 index 000000000..85edd3ea5 --- /dev/null +++ b/drivers/isdn/hardware/eicon/dsp_tst.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: dsp_tst.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */ + +#ifndef __DIVA_PRI_HOST_TEST_DSPS_H__ +#define __DIVA_PRI_HOST_TEST_DSPS_H__ + +/* + DSP registers on maestra pri +*/ +#define DSP1_PORT (0x00) +#define DSP2_PORT (0x8) +#define DSP3_PORT (0x800) +#define DSP4_PORT (0x808) +#define DSP5_PORT (0x810) +#define DSP6_PORT (0x818) +#define DSP7_PORT (0x820) +#define DSP8_PORT (0x828) +#define DSP9_PORT (0x830) +#define DSP10_PORT (0x840) +#define DSP11_PORT (0x848) +#define DSP12_PORT (0x850) +#define DSP13_PORT (0x858) +#define DSP14_PORT (0x860) +#define DSP15_PORT (0x868) +#define DSP16_PORT (0x870) +#define DSP17_PORT (0x1000) +#define DSP18_PORT (0x1008) +#define DSP19_PORT (0x1010) +#define DSP20_PORT (0x1018) +#define DSP21_PORT (0x1020) +#define DSP22_PORT (0x1028) +#define DSP23_PORT (0x1030) +#define DSP24_PORT (0x1040) +#define DSP25_PORT (0x1048) +#define DSP26_PORT (0x1050) +#define DSP27_PORT (0x1058) +#define DSP28_PORT (0x1060) +#define DSP29_PORT (0x1068) +#define DSP30_PORT (0x1070) +#define DSP_ADR_OFFS 0x80 + +/*------------------------------------------------------------------ + Dsp related definitions + ------------------------------------------------------------------ */ +#define DSP_SIGNATURE_PROBE_WORD 0x5a5a +#define dsp_make_address_ex(pm, address) ((word)((pm) ? (address) : (address) + 0x4000)) + +#endif diff --git a/drivers/isdn/hardware/eicon/dspdids.h b/drivers/isdn/hardware/eicon/dspdids.h new file mode 100644 index 000000000..957b33cc0 --- /dev/null +++ b/drivers/isdn/hardware/eicon/dspdids.h @@ -0,0 +1,75 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef DSPDIDS_H_ +#define DSPDIDS_H_ +/*---------------------------------------------------------------------------*/ +#define DSP_DID_INVALID 0 +#define DSP_DID_DIVA 1 +#define DSP_DID_DIVA_PRO 2 +#define DSP_DID_DIVA_PRO_20 3 +#define DSP_DID_DIVA_PRO_PCCARD 4 +#define DSP_DID_DIVA_SERVER_BRI_1M 5 +#define DSP_DID_DIVA_SERVER_BRI_2M 6 +#define DSP_DID_DIVA_SERVER_PRI_2M_TX 7 +#define DSP_DID_DIVA_SERVER_PRI_2M_RX 8 +#define DSP_DID_DIVA_SERVER_PRI_30M 9 +#define DSP_DID_TASK_HSCX 100 +#define DSP_DID_TASK_HSCX_PRI_2M_TX 101 +#define DSP_DID_TASK_HSCX_PRI_2M_RX 102 +#define DSP_DID_TASK_V110KRNL 200 +#define DSP_DID_OVERLAY_V1100 201 +#define DSP_DID_OVERLAY_V1101 202 +#define DSP_DID_OVERLAY_V1102 203 +#define DSP_DID_OVERLAY_V1103 204 +#define DSP_DID_OVERLAY_V1104 205 +#define DSP_DID_OVERLAY_V1105 206 +#define DSP_DID_OVERLAY_V1106 207 +#define DSP_DID_OVERLAY_V1107 208 +#define DSP_DID_OVERLAY_V1108 209 +#define DSP_DID_OVERLAY_V1109 210 +#define DSP_DID_TASK_V110_PRI_2M_TX 220 +#define DSP_DID_TASK_V110_PRI_2M_RX 221 +#define DSP_DID_TASK_MODEM 300 +#define DSP_DID_TASK_FAX05 400 +#define DSP_DID_TASK_VOICE 500 +#define DSP_DID_TASK_TIKRNL81 600 +#define DSP_DID_OVERLAY_DIAL 601 +#define DSP_DID_OVERLAY_V22 602 +#define DSP_DID_OVERLAY_V32 603 +#define DSP_DID_OVERLAY_FSK 604 +#define DSP_DID_OVERLAY_FAX 605 +#define DSP_DID_OVERLAY_VXX 606 +#define DSP_DID_OVERLAY_V8 607 +#define DSP_DID_OVERLAY_INFO 608 +#define DSP_DID_OVERLAY_V34 609 +#define DSP_DID_OVERLAY_DFX 610 +#define DSP_DID_PARTIAL_OVERLAY_DIAL 611 +#define DSP_DID_PARTIAL_OVERLAY_FSK 612 +#define DSP_DID_PARTIAL_OVERLAY_FAX 613 +#define DSP_DID_TASK_TIKRNL05 700 +/*---------------------------------------------------------------------------*/ +#endif +/*---------------------------------------------------------------------------*/ diff --git a/drivers/isdn/hardware/eicon/dsrv4bri.h b/drivers/isdn/hardware/eicon/dsrv4bri.h new file mode 100644 index 000000000..f353fb6b8 --- /dev/null +++ b/drivers/isdn/hardware/eicon/dsrv4bri.h @@ -0,0 +1,40 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_XDI_DSRV_4_BRI_INC__ +#define __DIVA_XDI_DSRV_4_BRI_INC__ +/* + * Some special registers in the PLX 9054 + */ +#define PLX9054_P2LDBELL 0x60 +#define PLX9054_L2PDBELL 0x64 +#define PLX9054_INTCSR 0x69 +#define PLX9054_INT_ENABLE 0x09 +#define PLX9054_SOFT_RESET 0x4000 +#define PLX9054_RELOAD_EEPROM 0x2000 +#define DIVA_4BRI_REVISION(__x__) (((__x__)->cardType == CARDTYPE_DIVASRV_Q_8M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_B_2M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_B_2F_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI)) +void diva_os_set_qBri_functions(PISDN_ADAPTER IoAdapter); +void diva_os_set_qBri2_functions(PISDN_ADAPTER IoAdapter); +#endif diff --git a/drivers/isdn/hardware/eicon/dsrv_bri.h b/drivers/isdn/hardware/eicon/dsrv_bri.h new file mode 100644 index 000000000..8a67dbc65 --- /dev/null +++ b/drivers/isdn/hardware/eicon/dsrv_bri.h @@ -0,0 +1,37 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_XDI_DSRV_BRI_INC__ +#define __DIVA_XDI_DSRV_BRI_INC__ +/* + Functions exported from os dependent part of + BRI card configuration and used in + OS independed part +*/ +/* + Prepare OS dependent part of BRI functions +*/ +void diva_os_prepare_maestra_functions(PISDN_ADAPTER IoAdapter); +#endif diff --git a/drivers/isdn/hardware/eicon/dsrv_pri.h b/drivers/isdn/hardware/eicon/dsrv_pri.h new file mode 100644 index 000000000..fd1a9ff9f --- /dev/null +++ b/drivers/isdn/hardware/eicon/dsrv_pri.h @@ -0,0 +1,38 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_XDI_DSRV_PRI_INC__ +#define __DIVA_XDI_DSRV_PRI_INC__ +/* + Functions exported from os dependent part of + PRI card configuration and used in + OS independed part +*/ +/* + Prepare OS dependent part of PRI/PRI Rev.2 functions +*/ +void diva_os_prepare_pri_functions(PISDN_ADAPTER IoAdapter); +void diva_os_prepare_pri2_functions(PISDN_ADAPTER IoAdapter); +#endif diff --git a/drivers/isdn/hardware/eicon/entity.h b/drivers/isdn/hardware/eicon/entity.h new file mode 100644 index 000000000..f9767d321 --- /dev/null +++ b/drivers/isdn/hardware/eicon/entity.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: entity.h,v 1.4 2004/03/21 17:26:01 armin Exp $ */ + +#ifndef __DIVAS_USER_MODE_IDI_ENTITY__ +#define __DIVAS_USER_MODE_IDI_ENTITY__ + +#define DIVA_UM_IDI_RC_PENDING 0x00000001 +#define DIVA_UM_IDI_REMOVE_PENDING 0x00000002 +#define DIVA_UM_IDI_TX_FLOW_CONTROL 0x00000004 +#define DIVA_UM_IDI_REMOVED 0x00000008 +#define DIVA_UM_IDI_ASSIGN_PENDING 0x00000010 + +typedef struct _divas_um_idi_entity { + struct list_head link; + diva_um_idi_adapter_t *adapter; /* Back to adapter */ + ENTITY e; + void *os_ref; + dword status; + void *os_context; + int rc_count; + diva_um_idi_data_queue_t data; /* definad by user 1 ... MAX */ + diva_um_idi_data_queue_t rc; /* two entries */ + BUFFERS XData; + BUFFERS RData; + byte buffer[2048 + 512]; +} divas_um_idi_entity_t; + + +#endif diff --git a/drivers/isdn/hardware/eicon/helpers.h b/drivers/isdn/hardware/eicon/helpers.h new file mode 100644 index 000000000..c9156b0ac --- /dev/null +++ b/drivers/isdn/hardware/eicon/helpers.h @@ -0,0 +1,51 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_XDI_CARD_CONFIG_HELPERS_INC__ +#define __DIVA_XDI_CARD_CONFIG_HELPERS_INC__ +dword diva_get_protocol_file_features(byte *File, + int offset, + char *IdStringBuffer, + dword IdBufferSize); +void diva_configure_protocol(PISDN_ADAPTER IoAdapter); +/* + Low level file access system abstraction +*/ +/* ------------------------------------------------------------------------- + Access to single file + Return pointer to the image of the requested file, + write image length to 'FileLength' + ------------------------------------------------------------------------- */ +void *xdiLoadFile(char *FileName, dword *FileLength, unsigned long MaxLoadSize); +/* ------------------------------------------------------------------------- + Dependent on the protocol settings does read return pointer + to the image of appropriate protocol file + ------------------------------------------------------------------------- */ +void *xdiLoadArchive(PISDN_ADAPTER IoAdapter, dword *FileLength, unsigned long MaxLoadSize); +/* -------------------------------------------------------------------------- + Free all system resources accessed by xdiLoadFile and xdiLoadArchive + -------------------------------------------------------------------------- */ +void xdiFreeFile(void *handle); +#endif diff --git a/drivers/isdn/hardware/eicon/idifunc.c b/drivers/isdn/hardware/eicon/idifunc.c new file mode 100644 index 000000000..fef6586fe --- /dev/null +++ b/drivers/isdn/hardware/eicon/idifunc.c @@ -0,0 +1,268 @@ +/* $Id: idifunc.c,v 1.14.4.4 2004/08/28 20:03:53 armin Exp $ + * + * Driver for Eicon DIVA Server ISDN cards. + * User Mode IDI Interface + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include "platform.h" +#include "di_defs.h" +#include "divasync.h" +#include "um_xdi.h" +#include "um_idi.h" + +#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) +#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) + +extern char *DRIVERRELEASE_IDI; + +extern void DIVA_DIDD_Read(void *, int); +extern int diva_user_mode_idi_create_adapter(const DESCRIPTOR *, int); +extern void diva_user_mode_idi_remove_adapter(int); + +static dword notify_handle; +static DESCRIPTOR DAdapter; +static DESCRIPTOR MAdapter; + +static void no_printf(unsigned char *x, ...) +{ + /* dummy debug function */ +} + +#include "debuglib.c" + +/* + * stop debug + */ +static void stop_dbg(void) +{ + DbgDeregister(); + memset(&MAdapter, 0, sizeof(MAdapter)); + dprintf = no_printf; +} + +typedef struct _udiva_card { + struct list_head list; + int Id; + DESCRIPTOR d; +} udiva_card; + +static LIST_HEAD(cards); +static diva_os_spin_lock_t ll_lock; + +/* + * find card in list + */ +static udiva_card *find_card_in_list(DESCRIPTOR *d) +{ + udiva_card *card; + struct list_head *tmp; + diva_os_spin_lock_magic_t old_irql; + + diva_os_enter_spin_lock(&ll_lock, &old_irql, "find card"); + list_for_each(tmp, &cards) { + card = list_entry(tmp, udiva_card, list); + if (card->d.request == d->request) { + diva_os_leave_spin_lock(&ll_lock, &old_irql, + "find card"); + return (card); + } + } + diva_os_leave_spin_lock(&ll_lock, &old_irql, "find card"); + return ((udiva_card *) NULL); +} + +/* + * new card + */ +static void um_new_card(DESCRIPTOR *d) +{ + int adapter_nr = 0; + udiva_card *card = NULL; + IDI_SYNC_REQ sync_req; + diva_os_spin_lock_magic_t old_irql; + + if (!(card = diva_os_malloc(0, sizeof(udiva_card)))) { + DBG_ERR(("cannot get buffer for card")); + return; + } + memcpy(&card->d, d, sizeof(DESCRIPTOR)); + sync_req.xdi_logical_adapter_number.Req = 0; + sync_req.xdi_logical_adapter_number.Rc = + IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER; + card->d.request((ENTITY *)&sync_req); + adapter_nr = + sync_req.xdi_logical_adapter_number.info.logical_adapter_number; + card->Id = adapter_nr; + if (!(diva_user_mode_idi_create_adapter(d, adapter_nr))) { + diva_os_enter_spin_lock(&ll_lock, &old_irql, "add card"); + list_add_tail(&card->list, &cards); + diva_os_leave_spin_lock(&ll_lock, &old_irql, "add card"); + } else { + DBG_ERR(("could not create user mode idi card %d", + adapter_nr)); + diva_os_free(0, card); + } +} + +/* + * remove card + */ +static void um_remove_card(DESCRIPTOR *d) +{ + diva_os_spin_lock_magic_t old_irql; + udiva_card *card = NULL; + + if (!(card = find_card_in_list(d))) { + DBG_ERR(("cannot find card to remove")); + return; + } + diva_user_mode_idi_remove_adapter(card->Id); + diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove card"); + list_del(&card->list); + diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove card"); + DBG_LOG(("idi proc entry removed for card %d", card->Id)); + diva_os_free(0, card); +} + +/* + * remove all adapter + */ +static void __exit remove_all_idi_proc(void) +{ + udiva_card *card; + diva_os_spin_lock_magic_t old_irql; + +rescan: + diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove all"); + if (!list_empty(&cards)) { + card = list_entry(cards.next, udiva_card, list); + list_del(&card->list); + diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove all"); + diva_user_mode_idi_remove_adapter(card->Id); + diva_os_free(0, card); + goto rescan; + } + diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove all"); +} + +/* + * DIDD notify callback + */ +static void *didd_callback(void *context, DESCRIPTOR *adapter, + int removal) +{ + if (adapter->type == IDI_DADAPTER) { + DBG_ERR(("Notification about IDI_DADAPTER change ! Oops.")); + return (NULL); + } else if (adapter->type == IDI_DIMAINT) { + if (removal) { + stop_dbg(); + } else { + memcpy(&MAdapter, adapter, sizeof(MAdapter)); + dprintf = (DIVA_DI_PRINTF) MAdapter.request; + DbgRegister("User IDI", DRIVERRELEASE_IDI, DBG_DEFAULT); + } + } else if ((adapter->type > 0) && (adapter->type < 16)) { /* IDI Adapter */ + if (removal) { + um_remove_card(adapter); + } else { + um_new_card(adapter); + } + } + return (NULL); +} + +/* + * connect DIDD + */ +static int __init connect_didd(void) +{ + int x = 0; + int dadapter = 0; + IDI_SYNC_REQ req; + DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS]; + + DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table)); + + for (x = 0; x < MAX_DESCRIPTORS; x++) { + if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */ + dadapter = 1; + memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter)); + req.didd_notify.e.Req = 0; + req.didd_notify.e.Rc = + IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY; + req.didd_notify.info.callback = (void *)didd_callback; + req.didd_notify.info.context = NULL; + DAdapter.request((ENTITY *)&req); + if (req.didd_notify.e.Rc != 0xff) { + stop_dbg(); + return (0); + } + notify_handle = req.didd_notify.info.handle; + } else if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */ + memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter)); + dprintf = (DIVA_DI_PRINTF) MAdapter.request; + DbgRegister("User IDI", DRIVERRELEASE_IDI, DBG_DEFAULT); + } else if ((DIDD_Table[x].type > 0) + && (DIDD_Table[x].type < 16)) { /* IDI Adapter found */ + um_new_card(&DIDD_Table[x]); + } + } + + if (!dadapter) { + stop_dbg(); + } + + return (dadapter); +} + +/* + * Disconnect from DIDD + */ +static void __exit disconnect_didd(void) +{ + IDI_SYNC_REQ req; + + stop_dbg(); + + req.didd_notify.e.Req = 0; + req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY; + req.didd_notify.info.handle = notify_handle; + DAdapter.request((ENTITY *)&req); +} + +/* + * init + */ +int __init idifunc_init(void) +{ + diva_os_initialize_spin_lock(&ll_lock, "idifunc"); + + if (diva_user_mode_idi_init()) { + DBG_ERR(("init: init failed.")); + return (0); + } + + if (!connect_didd()) { + diva_user_mode_idi_finit(); + DBG_ERR(("init: failed to connect to DIDD.")); + return (0); + } + return (1); +} + +/* + * finit + */ +void __exit idifunc_finit(void) +{ + diva_user_mode_idi_finit(); + disconnect_didd(); + remove_all_idi_proc(); +} diff --git a/drivers/isdn/hardware/eicon/io.c b/drivers/isdn/hardware/eicon/io.c new file mode 100644 index 000000000..8851ce580 --- /dev/null +++ b/drivers/isdn/hardware/eicon/io.c @@ -0,0 +1,852 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "platform.h" +#include "di_defs.h" +#include "pc.h" +#include "pr_pc.h" +#include "divasync.h" +#define MIPS_SCOM +#include "pkmaint.h" /* pc_main.h, packed in os-dependent fashion */ +#include "di.h" +#include "mi_pc.h" +#include "io.h" +extern ADAPTER *adapter[MAX_ADAPTER]; +extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER]; +void request(PISDN_ADAPTER, ENTITY *); +static void pcm_req(PISDN_ADAPTER, ENTITY *); +/* -------------------------------------------------------------------------- + local functions + -------------------------------------------------------------------------- */ +#define ReqFunc(N) \ + static void Request##N(ENTITY *e) \ + { if (IoAdapters[N]) (*IoAdapters[N]->DIRequest)(IoAdapters[N], e); } +ReqFunc(0) +ReqFunc(1) +ReqFunc(2) +ReqFunc(3) +ReqFunc(4) +ReqFunc(5) +ReqFunc(6) +ReqFunc(7) +ReqFunc(8) +ReqFunc(9) +ReqFunc(10) +ReqFunc(11) +ReqFunc(12) +ReqFunc(13) +ReqFunc(14) +ReqFunc(15) +IDI_CALL Requests[MAX_ADAPTER] = +{ &Request0, &Request1, &Request2, &Request3, + &Request4, &Request5, &Request6, &Request7, + &Request8, &Request9, &Request10, &Request11, + &Request12, &Request13, &Request14, &Request15 +}; +/*****************************************************************************/ +/* + This array should indicate all new services, that this version of XDI + is able to provide to his clients +*/ +static byte extended_xdi_features[DIVA_XDI_EXTENDED_FEATURES_MAX_SZ + 1] = { + (DIVA_XDI_EXTENDED_FEATURES_VALID | + DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR | + DIVA_XDI_EXTENDED_FEATURE_CAPI_PRMS | +#if defined(DIVA_IDI_RX_DMA) + DIVA_XDI_EXTENDED_FEATURE_CMA | + DIVA_XDI_EXTENDED_FEATURE_RX_DMA | + DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA | +#endif + DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC), + 0 +}; +/*****************************************************************************/ +void +dump_xlog_buffer(PISDN_ADAPTER IoAdapter, Xdesc *xlogDesc) +{ + dword logLen; + word *Xlog = xlogDesc->buf; + word logCnt = xlogDesc->cnt; + word logOut = xlogDesc->out / sizeof(*Xlog); + DBG_FTL(("%s: ************* XLOG recovery (%d) *************", + &IoAdapter->Name[0], (int)logCnt)) + DBG_FTL(("Microcode: %s", &IoAdapter->ProtocolIdString[0])) + for (; logCnt > 0; --logCnt) + { + if (!GET_WORD(&Xlog[logOut])) + { + if (--logCnt == 0) + break; + logOut = 0; + } + if (GET_WORD(&Xlog[logOut]) <= (logOut * sizeof(*Xlog))) + { + if (logCnt > 2) + { + DBG_FTL(("Possibly corrupted XLOG: %d entries left", + (int)logCnt)) + } + break; + } + logLen = (dword)(GET_WORD(&Xlog[logOut]) - (logOut * sizeof(*Xlog))); + DBG_FTL_MXLOG(((char *)&Xlog[logOut + 1], (dword)(logLen - 2))) + logOut = (GET_WORD(&Xlog[logOut]) + 1) / sizeof(*Xlog); + } + DBG_FTL(("%s: ***************** end of XLOG *****************", + &IoAdapter->Name[0])) + } +/*****************************************************************************/ +#if defined(XDI_USE_XLOG) +static char *(ExceptionCauseTable[]) = +{ + "Interrupt", + "TLB mod /IBOUND", + "TLB load /DBOUND", + "TLB store", + "Address error load", + "Address error store", + "Instruction load bus error", + "Data load/store bus error", + "Syscall", + "Breakpoint", + "Reverd instruction", + "Coprocessor unusable", + "Overflow", + "TRAP", + "VCEI", + "Floating Point Exception", + "CP2", + "Reserved 17", + "Reserved 18", + "Reserved 19", + "Reserved 20", + "Reserved 21", + "Reserved 22", + "WATCH", + "Reserved 24", + "Reserved 25", + "Reserved 26", + "Reserved 27", + "Reserved 28", + "Reserved 29", + "Reserved 30", + "VCED" +}; +#endif +void +dump_trap_frame(PISDN_ADAPTER IoAdapter, byte __iomem *exceptionFrame) +{ + MP_XCPTC __iomem *xcept = (MP_XCPTC __iomem *)exceptionFrame; + dword __iomem *regs; + regs = &xcept->regs[0]; + DBG_FTL(("%s: ***************** CPU TRAPPED *****************", + &IoAdapter->Name[0])) + DBG_FTL(("Microcode: %s", &IoAdapter->ProtocolIdString[0])) + DBG_FTL(("Cause: %s", + ExceptionCauseTable[(READ_DWORD(&xcept->cr) & 0x0000007c) >> 2])) + DBG_FTL(("sr 0x%08x cr 0x%08x epc 0x%08x vaddr 0x%08x", + READ_DWORD(&xcept->sr), READ_DWORD(&xcept->cr), + READ_DWORD(&xcept->epc), READ_DWORD(&xcept->vaddr))) + DBG_FTL(("zero 0x%08x at 0x%08x v0 0x%08x v1 0x%08x", + READ_DWORD(®s[0]), READ_DWORD(®s[1]), + READ_DWORD(®s[2]), READ_DWORD(®s[3]))) + DBG_FTL(("a0 0x%08x a1 0x%08x a2 0x%08x a3 0x%08x", + READ_DWORD(®s[4]), READ_DWORD(®s[5]), + READ_DWORD(®s[6]), READ_DWORD(®s[7]))) + DBG_FTL(("t0 0x%08x t1 0x%08x t2 0x%08x t3 0x%08x", + READ_DWORD(®s[8]), READ_DWORD(®s[9]), + READ_DWORD(®s[10]), READ_DWORD(®s[11]))) + DBG_FTL(("t4 0x%08x t5 0x%08x t6 0x%08x t7 0x%08x", + READ_DWORD(®s[12]), READ_DWORD(®s[13]), + READ_DWORD(®s[14]), READ_DWORD(®s[15]))) + DBG_FTL(("s0 0x%08x s1 0x%08x s2 0x%08x s3 0x%08x", + READ_DWORD(®s[16]), READ_DWORD(®s[17]), + READ_DWORD(®s[18]), READ_DWORD(®s[19]))) + DBG_FTL(("s4 0x%08x s5 0x%08x s6 0x%08x s7 0x%08x", + READ_DWORD(®s[20]), READ_DWORD(®s[21]), + READ_DWORD(®s[22]), READ_DWORD(®s[23]))) + DBG_FTL(("t8 0x%08x t9 0x%08x k0 0x%08x k1 0x%08x", + READ_DWORD(®s[24]), READ_DWORD(®s[25]), + READ_DWORD(®s[26]), READ_DWORD(®s[27]))) + DBG_FTL(("gp 0x%08x sp 0x%08x s8 0x%08x ra 0x%08x", + READ_DWORD(®s[28]), READ_DWORD(®s[29]), + READ_DWORD(®s[30]), READ_DWORD(®s[31]))) + DBG_FTL(("md 0x%08x|%08x resvd 0x%08x class 0x%08x", + READ_DWORD(&xcept->mdhi), READ_DWORD(&xcept->mdlo), + READ_DWORD(&xcept->reseverd), READ_DWORD(&xcept->xclass))) + } +/* -------------------------------------------------------------------------- + Real XDI Request function + -------------------------------------------------------------------------- */ +void request(PISDN_ADAPTER IoAdapter, ENTITY *e) +{ + byte i; + diva_os_spin_lock_magic_t irql; +/* + * if the Req field in the entity structure is 0, + * we treat this request as a special function call + */ + if (!e->Req) + { + IDI_SYNC_REQ *syncReq = (IDI_SYNC_REQ *)e; + switch (e->Rc) + { +#if defined(DIVA_IDI_RX_DMA) + case IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION: { + diva_xdi_dma_descriptor_operation_t *pI = \ + &syncReq->xdi_dma_descriptor_operation.info; + if (!IoAdapter->dma_map) { + pI->operation = -1; + pI->descriptor_number = -1; + return; + } + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "dma_op"); + if (pI->operation == IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC) { + pI->descriptor_number = diva_alloc_dma_map_entry(\ + (struct _diva_dma_map_entry *)IoAdapter->dma_map); + if (pI->descriptor_number >= 0) { + dword dma_magic; + void *local_addr; + diva_get_dma_map_entry(\ + (struct _diva_dma_map_entry *)IoAdapter->dma_map, + pI->descriptor_number, + &local_addr, &dma_magic); + pI->descriptor_address = local_addr; + pI->descriptor_magic = dma_magic; + pI->operation = 0; + } else { + pI->operation = -1; + } + } else if ((pI->operation == IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE) && + (pI->descriptor_number >= 0)) { + diva_free_dma_map_entry((struct _diva_dma_map_entry *)IoAdapter->dma_map, + pI->descriptor_number); + pI->descriptor_number = -1; + pI->operation = 0; + } else { + pI->descriptor_number = -1; + pI->operation = -1; + } + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "dma_op"); + } return; +#endif + case IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER: { + diva_xdi_get_logical_adapter_number_s_t *pI = \ + &syncReq->xdi_logical_adapter_number.info; + pI->logical_adapter_number = IoAdapter->ANum; + pI->controller = IoAdapter->ControllerNumber; + pI->total_controllers = IoAdapter->Properties.Adapters; + } return; + case IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS: { + diva_xdi_get_capi_parameters_t prms, *pI = &syncReq->xdi_capi_prms.info; + memset(&prms, 0x00, sizeof(prms)); + prms.structure_length = min_t(size_t, sizeof(prms), pI->structure_length); + memset(pI, 0x00, pI->structure_length); + prms.flag_dynamic_l1_down = (IoAdapter->capi_cfg.cfg_1 & \ + DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON) ? 1 : 0; + prms.group_optimization_enabled = (IoAdapter->capi_cfg.cfg_1 & \ + DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON) ? 1 : 0; + memcpy(pI, &prms, prms.structure_length); + } return; + case IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR: + syncReq->xdi_sdram_bar.info.bar = IoAdapter->sdram_bar; + return; + case IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES: { + dword i; + diva_xdi_get_extended_xdi_features_t *pI =\ + &syncReq->xdi_extended_features.info; + pI->buffer_length_in_bytes &= ~0x80000000; + if (pI->buffer_length_in_bytes && pI->features) { + memset(pI->features, 0x00, pI->buffer_length_in_bytes); + } + for (i = 0; ((pI->features) && (i < pI->buffer_length_in_bytes) && + (i < DIVA_XDI_EXTENDED_FEATURES_MAX_SZ)); i++) { + pI->features[i] = extended_xdi_features[i]; + } + if ((pI->buffer_length_in_bytes < DIVA_XDI_EXTENDED_FEATURES_MAX_SZ) || + (!pI->features)) { + pI->buffer_length_in_bytes =\ + (0x80000000 | DIVA_XDI_EXTENDED_FEATURES_MAX_SZ); + } + } return; + case IDI_SYNC_REQ_XDI_GET_STREAM: + if (IoAdapter) { + diva_xdi_provide_istream_info(&IoAdapter->a, + &syncReq->xdi_stream_info.info); + } else { + syncReq->xdi_stream_info.info.provided_service = 0; + } + return; + case IDI_SYNC_REQ_GET_NAME: + if (IoAdapter) + { + strcpy(&syncReq->GetName.name[0], IoAdapter->Name); + DBG_TRC(("xdi: Adapter %d / Name '%s'", + IoAdapter->ANum, IoAdapter->Name)) + return; + } + syncReq->GetName.name[0] = '\0'; + break; + case IDI_SYNC_REQ_GET_SERIAL: + if (IoAdapter) + { + syncReq->GetSerial.serial = IoAdapter->serialNo; + DBG_TRC(("xdi: Adapter %d / SerialNo %ld", + IoAdapter->ANum, IoAdapter->serialNo)) + return; + } + syncReq->GetSerial.serial = 0; + break; + case IDI_SYNC_REQ_GET_CARDTYPE: + if (IoAdapter) + { + syncReq->GetCardType.cardtype = IoAdapter->cardType; + DBG_TRC(("xdi: Adapter %d / CardType %ld", + IoAdapter->ANum, IoAdapter->cardType)) + return; + } + syncReq->GetCardType.cardtype = 0; + break; + case IDI_SYNC_REQ_GET_XLOG: + if (IoAdapter) + { + pcm_req(IoAdapter, e); + return; + } + e->Ind = 0; + break; + case IDI_SYNC_REQ_GET_DBG_XLOG: + if (IoAdapter) + { + pcm_req(IoAdapter, e); + return; + } + e->Ind = 0; + break; + case IDI_SYNC_REQ_GET_FEATURES: + if (IoAdapter) + { + syncReq->GetFeatures.features = + (unsigned short)IoAdapter->features; + return; + } + syncReq->GetFeatures.features = 0; + break; + case IDI_SYNC_REQ_PORTDRV_HOOK: + if (IoAdapter) + { + DBG_TRC(("Xdi:IDI_SYNC_REQ_PORTDRV_HOOK - ignored")) + return; + } + break; + } + if (IoAdapter) + { + return; + } + } + DBG_TRC(("xdi: Id 0x%x / Req 0x%x / Rc 0x%x", e->Id, e->Req, e->Rc)) + if (!IoAdapter) + { + DBG_FTL(("xdi: uninitialized Adapter used - ignore request")) + return; + } + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req"); +/* + * assign an entity + */ + if (!(e->Id & 0x1f)) + { + if (IoAdapter->e_count >= IoAdapter->e_max) + { + DBG_FTL(("xdi: all Ids in use (max=%d) --> Req ignored", + IoAdapter->e_max)) + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req"); + return; + } +/* + * find a new free id + */ + for (i = 1; IoAdapter->e_tbl[i].e; ++i); + IoAdapter->e_tbl[i].e = e; + IoAdapter->e_count++; + e->No = (byte)i; + e->More = 0; + e->RCurrent = 0xff; + } + else + { + i = e->No; + } +/* + * if the entity is still busy, ignore the request call + */ + if (e->More & XBUSY) + { + DBG_FTL(("xdi: Id 0x%x busy --> Req 0x%x ignored", e->Id, e->Req)) + if (!IoAdapter->trapped && IoAdapter->trapFnc) + { + IoAdapter->trapFnc(IoAdapter); + /* + Firs trap, also notify user if supported + */ + if (IoAdapter->trapped && IoAdapter->os_trap_nfy_Fnc) { + (*(IoAdapter->os_trap_nfy_Fnc))(IoAdapter, IoAdapter->ANum); + } + } + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req"); + return; + } +/* + * initialize transmit status variables + */ + e->More |= XBUSY; + e->More &= ~XMOREF; + e->XCurrent = 0; + e->XOffset = 0; +/* + * queue this entity in the adapter request queue + */ + IoAdapter->e_tbl[i].next = 0; + if (IoAdapter->head) + { + IoAdapter->e_tbl[IoAdapter->tail].next = i; + IoAdapter->tail = i; + } + else + { + IoAdapter->head = i; + IoAdapter->tail = i; + } +/* + * queue the DPC to process the request + */ + diva_os_schedule_soft_isr(&IoAdapter->req_soft_isr); + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req"); +} +/* --------------------------------------------------------------------- + Main DPC routine + --------------------------------------------------------------------- */ +void DIDpcRoutine(struct _diva_os_soft_isr *psoft_isr, void *Context) { + PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)Context; + ADAPTER *a = &IoAdapter->a; + diva_os_atomic_t *pin_dpc = &IoAdapter->in_dpc; + if (diva_os_atomic_increment(pin_dpc) == 1) { + do { + if (IoAdapter->tst_irq(a)) + { + if (!IoAdapter->Unavailable) + IoAdapter->dpc(a); + IoAdapter->clr_irq(a); + } + IoAdapter->out(a); + } while (diva_os_atomic_decrement(pin_dpc) > 0); + /* ---------------------------------------------------------------- + Look for XLOG request (cards with indirect addressing) + ---------------------------------------------------------------- */ + if (IoAdapter->pcm_pending) { + struct pc_maint *pcm; + diva_os_spin_lock_magic_t OldIrql; + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, + &OldIrql, + "data_dpc"); + pcm = (struct pc_maint *)IoAdapter->pcm_data; + switch (IoAdapter->pcm_pending) { + case 1: /* ask card for XLOG */ + a->ram_out(a, &IoAdapter->pcm->rc, 0); + a->ram_out(a, &IoAdapter->pcm->req, pcm->req); + IoAdapter->pcm_pending = 2; + break; + case 2: /* Try to get XLOG from the card */ + if ((int)(a->ram_in(a, &IoAdapter->pcm->rc))) { + a->ram_in_buffer(a, IoAdapter->pcm, pcm, sizeof(*pcm)); + IoAdapter->pcm_pending = 3; + } + break; + case 3: /* let XDI recovery XLOG */ + break; + } + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, + &OldIrql, + "data_dpc"); + } + /* ---------------------------------------------------------------- */ + } +} +/* -------------------------------------------------------------------------- + XLOG interface + -------------------------------------------------------------------------- */ +static void +pcm_req(PISDN_ADAPTER IoAdapter, ENTITY *e) +{ + diva_os_spin_lock_magic_t OldIrql; + int i, rc; + ADAPTER *a = &IoAdapter->a; + struct pc_maint *pcm = (struct pc_maint *)&e->Ind; +/* + * special handling of I/O based card interface + * the memory access isn't an atomic operation ! + */ + if (IoAdapter->Properties.Card == CARD_MAE) + { + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, + &OldIrql, + "data_pcm_1"); + IoAdapter->pcm_data = (void *)pcm; + IoAdapter->pcm_pending = 1; + diva_os_schedule_soft_isr(&IoAdapter->req_soft_isr); + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, + &OldIrql, + "data_pcm_1"); + for (rc = 0, i = (IoAdapter->trapped ? 3000 : 250); !rc && (i > 0); --i) + { + diva_os_sleep(1); + if (IoAdapter->pcm_pending == 3) { + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, + &OldIrql, + "data_pcm_3"); + IoAdapter->pcm_pending = 0; + IoAdapter->pcm_data = NULL; + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, + &OldIrql, + "data_pcm_3"); + return; + } + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, + &OldIrql, + "data_pcm_2"); + diva_os_schedule_soft_isr(&IoAdapter->req_soft_isr); + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, + &OldIrql, + "data_pcm_2"); + } + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, + &OldIrql, + "data_pcm_4"); + IoAdapter->pcm_pending = 0; + IoAdapter->pcm_data = NULL; + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, + &OldIrql, + "data_pcm_4"); + goto Trapped; + } +/* + * memory based shared ram is accessible from different + * processors without disturbing concurrent processes. + */ + a->ram_out(a, &IoAdapter->pcm->rc, 0); + a->ram_out(a, &IoAdapter->pcm->req, pcm->req); + for (i = (IoAdapter->trapped ? 3000 : 250); --i > 0;) + { + diva_os_sleep(1); + rc = (int)(a->ram_in(a, &IoAdapter->pcm->rc)); + if (rc) + { + a->ram_in_buffer(a, IoAdapter->pcm, pcm, sizeof(*pcm)); + return; + } + } +Trapped: + if (IoAdapter->trapFnc) + { + int trapped = IoAdapter->trapped; + IoAdapter->trapFnc(IoAdapter); + /* + Firs trap, also notify user if supported + */ + if (!trapped && IoAdapter->trapped && IoAdapter->os_trap_nfy_Fnc) { + (*(IoAdapter->os_trap_nfy_Fnc))(IoAdapter, IoAdapter->ANum); + } + } +} +/*------------------------------------------------------------------*/ +/* ram access functions for memory mapped cards */ +/*------------------------------------------------------------------*/ +byte mem_in(ADAPTER *a, void *addr) +{ + byte val; + volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io); + val = READ_BYTE(Base + (unsigned long)addr); + DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base); + return (val); +} +word mem_inw(ADAPTER *a, void *addr) +{ + word val; + volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io); + val = READ_WORD((Base + (unsigned long)addr)); + DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base); + return (val); +} +void mem_in_dw(ADAPTER *a, void *addr, dword *data, int dwords) +{ + volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io); + while (dwords--) { + *data++ = READ_DWORD((Base + (unsigned long)addr)); + addr += 4; + } + DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base); +} +void mem_in_buffer(ADAPTER *a, void *addr, void *buffer, word length) +{ + volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io); + memcpy_fromio(buffer, (Base + (unsigned long)addr), length); + DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base); +} +void mem_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e) +{ + PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)a->io; + IoAdapter->RBuffer.length = mem_inw(a, &RBuffer->length); + mem_in_buffer(a, RBuffer->P, IoAdapter->RBuffer.P, + IoAdapter->RBuffer.length); + e->RBuffer = (DBUFFER *)&IoAdapter->RBuffer; +} +void mem_out(ADAPTER *a, void *addr, byte data) +{ + volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io); + WRITE_BYTE(Base + (unsigned long)addr, data); + DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base); +} +void mem_outw(ADAPTER *a, void *addr, word data) +{ + volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io); + WRITE_WORD((Base + (unsigned long)addr), data); + DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base); +} +void mem_out_dw(ADAPTER *a, void *addr, const dword *data, int dwords) +{ + volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io); + while (dwords--) { + WRITE_DWORD((Base + (unsigned long)addr), *data); + addr += 4; + data++; + } + DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base); +} +void mem_out_buffer(ADAPTER *a, void *addr, void *buffer, word length) +{ + volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io); + memcpy_toio((Base + (unsigned long)addr), buffer, length); + DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base); +} +void mem_inc(ADAPTER *a, void *addr) +{ + volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io); + byte x = READ_BYTE(Base + (unsigned long)addr); + WRITE_BYTE(Base + (unsigned long)addr, x + 1); + DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base); +} +/*------------------------------------------------------------------*/ +/* ram access functions for io-mapped cards */ +/*------------------------------------------------------------------*/ +byte io_in(ADAPTER *a, void *adr) +{ + byte val; + byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); + outppw(Port + 4, (word)(unsigned long)adr); + val = inpp(Port); + DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); + return (val); +} +word io_inw(ADAPTER *a, void *adr) +{ + word val; + byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); + outppw(Port + 4, (word)(unsigned long)adr); + val = inppw(Port); + DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); + return (val); +} +void io_in_buffer(ADAPTER *a, void *adr, void *buffer, word len) +{ + byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); + byte *P = (byte *)buffer; + if ((long)adr & 1) { + outppw(Port + 4, (word)(unsigned long)adr); + *P = inpp(Port); + P++; + adr = ((byte *) adr) + 1; + len--; + if (!len) { + DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); + return; + } + } + outppw(Port + 4, (word)(unsigned long)adr); + inppw_buffer(Port, P, len + 1); + DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); +} +void io_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e) +{ + byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); + outppw(Port + 4, (word)(unsigned long)RBuffer); + ((PISDN_ADAPTER)a->io)->RBuffer.length = inppw(Port); + inppw_buffer(Port, ((PISDN_ADAPTER)a->io)->RBuffer.P, ((PISDN_ADAPTER)a->io)->RBuffer.length + 1); + e->RBuffer = (DBUFFER *) &(((PISDN_ADAPTER)a->io)->RBuffer); + DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); +} +void io_out(ADAPTER *a, void *adr, byte data) +{ + byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); + outppw(Port + 4, (word)(unsigned long)adr); + outpp(Port, data); + DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); +} +void io_outw(ADAPTER *a, void *adr, word data) +{ + byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); + outppw(Port + 4, (word)(unsigned long)adr); + outppw(Port, data); + DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); +} +void io_out_buffer(ADAPTER *a, void *adr, void *buffer, word len) +{ + byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); + byte *P = (byte *)buffer; + if ((long)adr & 1) { + outppw(Port + 4, (word)(unsigned long)adr); + outpp(Port, *P); + P++; + adr = ((byte *) adr) + 1; + len--; + if (!len) { + DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); + return; + } + } + outppw(Port + 4, (word)(unsigned long)adr); + outppw_buffer(Port, P, len + 1); + DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); +} +void io_inc(ADAPTER *a, void *adr) +{ + byte x; + byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); + outppw(Port + 4, (word)(unsigned long)adr); + x = inpp(Port); + outppw(Port + 4, (word)(unsigned long)adr); + outpp(Port, x + 1); + DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); +} +/*------------------------------------------------------------------*/ +/* OS specific functions related to queuing of entities */ +/*------------------------------------------------------------------*/ +void free_entity(ADAPTER *a, byte e_no) +{ + PISDN_ADAPTER IoAdapter; + diva_os_spin_lock_magic_t irql; + IoAdapter = (PISDN_ADAPTER) a->io; + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_free"); + IoAdapter->e_tbl[e_no].e = NULL; + IoAdapter->e_count--; + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_free"); +} +void assign_queue(ADAPTER *a, byte e_no, word ref) +{ + PISDN_ADAPTER IoAdapter; + diva_os_spin_lock_magic_t irql; + IoAdapter = (PISDN_ADAPTER) a->io; + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_assign"); + IoAdapter->e_tbl[e_no].assign_ref = ref; + IoAdapter->e_tbl[e_no].next = (byte)IoAdapter->assign; + IoAdapter->assign = e_no; + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_assign"); +} +byte get_assign(ADAPTER *a, word ref) +{ + PISDN_ADAPTER IoAdapter; + diva_os_spin_lock_magic_t irql; + byte e_no; + IoAdapter = (PISDN_ADAPTER) a->io; + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, + &irql, + "data_assign_get"); + for (e_no = (byte)IoAdapter->assign; + e_no && IoAdapter->e_tbl[e_no].assign_ref != ref; + e_no = IoAdapter->e_tbl[e_no].next); + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, + &irql, + "data_assign_get"); + return e_no; +} +void req_queue(ADAPTER *a, byte e_no) +{ + PISDN_ADAPTER IoAdapter; + diva_os_spin_lock_magic_t irql; + IoAdapter = (PISDN_ADAPTER) a->io; + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req_q"); + IoAdapter->e_tbl[e_no].next = 0; + if (IoAdapter->head) { + IoAdapter->e_tbl[IoAdapter->tail].next = e_no; + IoAdapter->tail = e_no; + } + else { + IoAdapter->head = e_no; + IoAdapter->tail = e_no; + } + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req_q"); +} +byte look_req(ADAPTER *a) +{ + PISDN_ADAPTER IoAdapter; + IoAdapter = (PISDN_ADAPTER) a->io; + return ((byte)IoAdapter->head); +} +void next_req(ADAPTER *a) +{ + PISDN_ADAPTER IoAdapter; + diva_os_spin_lock_magic_t irql; + IoAdapter = (PISDN_ADAPTER) a->io; + diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req_next"); + IoAdapter->head = IoAdapter->e_tbl[IoAdapter->head].next; + if (!IoAdapter->head) IoAdapter->tail = 0; + diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req_next"); +} +/*------------------------------------------------------------------*/ +/* memory map functions */ +/*------------------------------------------------------------------*/ +ENTITY *entity_ptr(ADAPTER *a, byte e_no) +{ + PISDN_ADAPTER IoAdapter; + IoAdapter = (PISDN_ADAPTER)a->io; + return (IoAdapter->e_tbl[e_no].e); +} +void *PTR_X(ADAPTER *a, ENTITY *e) +{ + return ((void *) e->X); +} +void *PTR_R(ADAPTER *a, ENTITY *e) +{ + return ((void *) e->R); +} +void *PTR_P(ADAPTER *a, ENTITY *e, void *P) +{ + return P; +} +void CALLBACK(ADAPTER *a, ENTITY *e) +{ + if (e && e->callback) + e->callback(e); +} diff --git a/drivers/isdn/hardware/eicon/io.h b/drivers/isdn/hardware/eicon/io.h new file mode 100644 index 000000000..01deced18 --- /dev/null +++ b/drivers/isdn/hardware/eicon/io.h @@ -0,0 +1,308 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_XDI_COMMON_IO_H_INC__ /* { */ +#define __DIVA_XDI_COMMON_IO_H_INC__ +/* + maximum = 16 adapters +*/ +#define DI_MAX_LINKS MAX_ADAPTER +#define ISDN_MAX_NUM_LEN 60 +/* -------------------------------------------------------------------------- + structure for quadro card management (obsolete for + systems that do provide per card load event) + -------------------------------------------------------------------------- */ +typedef struct { + dword Num; + DEVICE_NAME DeviceName[4]; + PISDN_ADAPTER QuadroAdapter[4]; +} ADAPTER_LIST_ENTRY, *PADAPTER_LIST_ENTRY; +/* -------------------------------------------------------------------------- + Special OS memory support structures + -------------------------------------------------------------------------- */ +#define MAX_MAPPED_ENTRIES 8 +typedef struct { + void *Address; + dword Length; +} ADAPTER_MEMORY; +/* -------------------------------------------------------------------------- + Configuration of XDI clients carried by XDI + -------------------------------------------------------------------------- */ +#define DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON 0x01 +#define DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON 0x02 +typedef struct _diva_xdi_capi_cfg { + byte cfg_1; +} diva_xdi_capi_cfg_t; +/* -------------------------------------------------------------------------- + Main data structure kept per adapter + -------------------------------------------------------------------------- */ +struct _ISDN_ADAPTER { + void (*DIRequest)(PISDN_ADAPTER, ENTITY *); + int State; /* from NT4 1.srv, a good idea, but a poor achievement */ + int Initialized; + int RegisteredWithDidd; + int Unavailable; /* callback function possible? */ + int ResourcesClaimed; + int PnpBiosConfigUsed; + dword Logging; + dword features; + char ProtocolIdString[80]; + /* + remember mapped memory areas + */ + ADAPTER_MEMORY MappedMemory[MAX_MAPPED_ENTRIES]; + CARD_PROPERTIES Properties; + dword cardType; + dword protocol_id; /* configured protocol identifier */ + char protocol_name[8]; /* readable name of protocol */ + dword BusType; + dword BusNumber; + dword slotNumber; + dword slotId; + dword ControllerNumber; /* for QUADRO cards only */ + PISDN_ADAPTER MultiMaster; /* for 4-BRI card only - use MultiMaster or QuadroList */ + PADAPTER_LIST_ENTRY QuadroList; /* for QUADRO card only */ + PDEVICE_OBJECT DeviceObject; + dword DeviceId; + diva_os_adapter_irq_info_t irq_info; + dword volatile IrqCount; + int trapped; + dword DspCodeBaseAddr; + dword MaxDspCodeSize; + dword downloadAddr; + dword DspCodeBaseAddrTable[4]; /* add. for MultiMaster */ + dword MaxDspCodeSizeTable[4]; /* add. for MultiMaster */ + dword downloadAddrTable[4]; /* add. for MultiMaster */ + dword MemoryBase; + dword MemorySize; + byte __iomem *Address; + byte __iomem *Config; + byte __iomem *Control; + byte __iomem *reset; + byte __iomem *port; + byte __iomem *ram; + byte __iomem *cfg; + byte __iomem *prom; + byte __iomem *ctlReg; + struct pc_maint *pcm; + diva_os_dependent_devica_name_t os_name; + byte Name[32]; + dword serialNo; + dword ANum; + dword ArchiveType; /* ARCHIVE_TYPE_NONE ..._SINGLE ..._USGEN ..._MULTI */ + char *ProtocolSuffix; /* internal protocolfile table */ + char Archive[32]; + char Protocol[32]; + char AddDownload[32]; /* Dsp- or other additional download files */ + char Oad1[ISDN_MAX_NUM_LEN]; + char Osa1[ISDN_MAX_NUM_LEN]; + char Oad2[ISDN_MAX_NUM_LEN]; + char Osa2[ISDN_MAX_NUM_LEN]; + char Spid1[ISDN_MAX_NUM_LEN]; + char Spid2[ISDN_MAX_NUM_LEN]; + byte nosig; + byte BriLayer2LinkCount; /* amount of TEI's that adapter will support in P2MP mode */ + dword Channels; + dword tei; + dword nt2; + dword TerminalCount; + dword WatchDog; + dword Permanent; + dword BChMask; /* B channel mask for unchannelized modes */ + dword StableL2; + dword DidLen; + dword NoOrderCheck; + dword ForceLaw; /* VoiceCoding - default:0, a-law: 1, my-law: 2 */ + dword SigFlags; + dword LowChannel; + dword NoHscx30; + dword ProtVersion; + dword crc4; + dword L1TristateOrQsig; /* enable Layer 1 Tristate (bit 2)Or Qsig params (bit 0,1)*/ + dword InitialDspInfo; + dword ModemGuardTone; + dword ModemMinSpeed; + dword ModemMaxSpeed; + dword ModemOptions; + dword ModemOptions2; + dword ModemNegotiationMode; + dword ModemModulationsMask; + dword ModemTransmitLevel; + dword FaxOptions; + dword FaxMaxSpeed; + dword Part68LevelLimiter; + dword UsEktsNumCallApp; + byte UsEktsFeatAddConf; + byte UsEktsFeatRemoveConf; + byte UsEktsFeatCallTransfer; + byte UsEktsFeatMsgWaiting; + byte QsigDialect; + byte ForceVoiceMailAlert; + byte DisableAutoSpid; + byte ModemCarrierWaitTimeSec; + byte ModemCarrierLossWaitTimeTenthSec; + byte PiafsLinkTurnaroundInFrames; + byte DiscAfterProgress; + byte AniDniLimiter[3]; + byte TxAttenuation; /* PRI/E1 only: attenuate TX signal */ + word QsigFeatures; + dword GenerateRingtone; + dword SupplementaryServicesFeatures; + dword R2Dialect; + dword R2CasOptions; + dword FaxV34Options; + dword DisabledDspMask; + dword AdapterTestMask; + dword DspImageLength; + word AlertToIn20mSecTicks; + word ModemEyeSetup; + byte R2CtryLength; + byte CCBSRelTimer; + byte *PcCfgBufferFile;/* flexible parameter via file */ + byte *PcCfgBuffer; /* flexible parameter via multistring */ + diva_os_dump_file_t dump_file; /* dump memory to file at lowest irq level */ + diva_os_board_trace_t board_trace; /* traces from the board */ + diva_os_spin_lock_t isr_spin_lock; + diva_os_spin_lock_t data_spin_lock; + diva_os_soft_isr_t req_soft_isr; + diva_os_soft_isr_t isr_soft_isr; + diva_os_atomic_t in_dpc; + PBUFFER RBuffer; /* Copy of receive lookahead buffer */ + word e_max; + word e_count; + E_INFO *e_tbl; + word assign; /* list of pending ASSIGNs */ + word head; /* head of request queue */ + word tail; /* tail of request queue */ + ADAPTER a; /* not a separate structure */ + void (*out)(ADAPTER *a); + byte (*dpc)(ADAPTER *a); + byte (*tst_irq)(ADAPTER *a); + void (*clr_irq)(ADAPTER *a); + int (*load)(PISDN_ADAPTER); + int (*mapmem)(PISDN_ADAPTER); + int (*chkIrq)(PISDN_ADAPTER); + void (*disIrq)(PISDN_ADAPTER); + void (*start)(PISDN_ADAPTER); + void (*stop)(PISDN_ADAPTER); + void (*rstFnc)(PISDN_ADAPTER); + void (*trapFnc)(PISDN_ADAPTER); + dword (*DetectDsps)(PISDN_ADAPTER); + void (*os_trap_nfy_Fnc)(PISDN_ADAPTER, dword); + diva_os_isr_callback_t diva_isr_handler; + dword sdram_bar; /* must be 32 bit */ + dword fpga_features; + volatile int pcm_pending; + volatile void *pcm_data; + diva_xdi_capi_cfg_t capi_cfg; + dword tasks; + void *dma_map; + int (*DivaAdapterTestProc)(PISDN_ADAPTER); + void *AdapterTestMemoryStart; + dword AdapterTestMemoryLength; + const byte *cfg_lib_memory_init; + dword cfg_lib_memory_init_length; +}; +/* --------------------------------------------------------------------- + Entity table + --------------------------------------------------------------------- */ +struct e_info_s { + ENTITY *e; + byte next; /* chaining index */ + word assign_ref; /* assign reference */ +}; +/* --------------------------------------------------------------------- + S-cards shared ram structure for loading + --------------------------------------------------------------------- */ +struct s_load { + byte ctrl; + byte card; + byte msize; + byte fill0; + word ebit; + word elocl; + word eloch; + byte reserved[20]; + word signature; + byte fill[224]; + byte b[256]; +}; +#define PR_RAM ((struct pr_ram *)0) +#define RAM ((struct dual *)0) +/* --------------------------------------------------------------------- + platform specific conversions + --------------------------------------------------------------------- */ +extern void *PTR_P(ADAPTER *a, ENTITY *e, void *P); +extern void *PTR_X(ADAPTER *a, ENTITY *e); +extern void *PTR_R(ADAPTER *a, ENTITY *e); +extern void CALLBACK(ADAPTER *a, ENTITY *e); +extern void set_ram(void **adr_ptr); +/* --------------------------------------------------------------------- + ram access functions for io mapped cards + --------------------------------------------------------------------- */ +byte io_in(ADAPTER *a, void *adr); +word io_inw(ADAPTER *a, void *adr); +void io_in_buffer(ADAPTER *a, void *adr, void *P, word length); +void io_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e); +void io_out(ADAPTER *a, void *adr, byte data); +void io_outw(ADAPTER *a, void *adr, word data); +void io_out_buffer(ADAPTER *a, void *adr, void *P, word length); +void io_inc(ADAPTER *a, void *adr); +void bri_in_buffer(PISDN_ADAPTER IoAdapter, dword Pos, + void *Buf, dword Len); +int bri_out_buffer(PISDN_ADAPTER IoAdapter, dword Pos, + void *Buf, dword Len, int Verify); +/* --------------------------------------------------------------------- + ram access functions for memory mapped cards + --------------------------------------------------------------------- */ +byte mem_in(ADAPTER *a, void *adr); +word mem_inw(ADAPTER *a, void *adr); +void mem_in_buffer(ADAPTER *a, void *adr, void *P, word length); +void mem_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e); +void mem_out(ADAPTER *a, void *adr, byte data); +void mem_outw(ADAPTER *a, void *adr, word data); +void mem_out_buffer(ADAPTER *a, void *adr, void *P, word length); +void mem_inc(ADAPTER *a, void *adr); +void mem_in_dw(ADAPTER *a, void *addr, dword *data, int dwords); +void mem_out_dw(ADAPTER *a, void *addr, const dword *data, int dwords); +/* --------------------------------------------------------------------- + functions exported by io.c + --------------------------------------------------------------------- */ +extern IDI_CALL Requests[MAX_ADAPTER]; +extern void DIDpcRoutine(struct _diva_os_soft_isr *psoft_isr, + void *context); +extern void request(PISDN_ADAPTER, ENTITY *); +/* --------------------------------------------------------------------- + trapFn helpers, used to recover debug trace from dead card + --------------------------------------------------------------------- */ +typedef struct { + word *buf; + word cnt; + word out; +} Xdesc; +extern void dump_trap_frame(PISDN_ADAPTER IoAdapter, byte __iomem *exception); +extern void dump_xlog_buffer(PISDN_ADAPTER IoAdapter, Xdesc *xlogDesc); +/* --------------------------------------------------------------------- */ +#endif /* } __DIVA_XDI_COMMON_IO_H_INC__ */ diff --git a/drivers/isdn/hardware/eicon/istream.c b/drivers/isdn/hardware/eicon/istream.c new file mode 100644 index 000000000..045bda5c8 --- /dev/null +++ b/drivers/isdn/hardware/eicon/istream.c @@ -0,0 +1,226 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "platform.h" +#if defined(DIVA_ISTREAM) /* { */ +#include "pc.h" +#include "pr_pc.h" +#include "di_defs.h" +#include "divasync.h" +#include "di.h" +#if !defined USE_EXTENDED_DEBUGS +#include "dimaint.h" +#else +#define dprintf +#endif +#include "dfifo.h" +int diva_istream_write(void *context, + int Id, + void *data, + int length, + int final, + byte usr1, + byte usr2); +int diva_istream_read(void *context, + int Id, + void *data, + int max_length, + int *final, + byte *usr1, + byte *usr2); +/* ------------------------------------------------------------------- + Does provide iStream interface to the client + ------------------------------------------------------------------- */ +void diva_xdi_provide_istream_info(ADAPTER *a, + diva_xdi_stream_interface_t *pi) { + pi->provided_service = 0; +} +/* ------------------------------------------------------------------ + Does write the data from caller's buffer to the card's + stream interface. + If synchronous service was requested, then function + does return amount of data written to stream. + 'final' does indicate that piece of data to be written is + final part of frame (necessary only by structured datatransfer) + return 0 if zero lengh packet was written + return -1 if stream is full + ------------------------------------------------------------------ */ +int diva_istream_write(void *context, + int Id, + void *data, + int length, + int final, + byte usr1, + byte usr2) { + ADAPTER *a = (ADAPTER *)context; + int written = 0, to_write = -1; + char tmp[4]; + byte *data_ptr = (byte *)data; + for (;;) { + a->ram_in_dw(a, +#ifdef PLATFORM_GT_32BIT + ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id]), +#else + (void *)(a->tx_stream[Id] + a->tx_pos[Id]), +#endif + (dword *)&tmp[0], + 1); + if (tmp[0] & DIVA_DFIFO_READY) { /* No free blocks more */ + if (to_write < 0) + return (-1); /* was not able to write */ + break; /* only part of message was written */ + } + to_write = min(length, DIVA_DFIFO_DATA_SZ); + if (to_write) { + a->ram_out_buffer(a, +#ifdef PLATFORM_GT_32BIT + ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id] + 4), +#else + (void *)(a->tx_stream[Id] + a->tx_pos[Id] + 4), +#endif + data_ptr, + (word)to_write); + length -= to_write; + written += to_write; + data_ptr += to_write; + } + tmp[1] = (char)to_write; + tmp[0] = (tmp[0] & DIVA_DFIFO_WRAP) | + DIVA_DFIFO_READY | + ((!length && final) ? DIVA_DFIFO_LAST : 0); + if (tmp[0] & DIVA_DFIFO_LAST) { + tmp[2] = usr1; + tmp[3] = usr2; + } + a->ram_out_dw(a, +#ifdef PLATFORM_GT_32BIT + ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id]), +#else + (void *)(a->tx_stream[Id] + a->tx_pos[Id]), +#endif + (dword *)&tmp[0], + 1); + if (tmp[0] & DIVA_DFIFO_WRAP) { + a->tx_pos[Id] = 0; + } else { + a->tx_pos[Id] += DIVA_DFIFO_STEP; + } + if (!length) { + break; + } + } + return (written); +} +/* ------------------------------------------------------------------- + In case of SYNCRONOUS service: + Does write data from stream in caller's buffer. + Does return amount of data written to buffer + Final flag is set on return if last part of structured frame + was received + return 0 if zero packet was received + return -1 if stream is empty + return -2 if read buffer does not profide sufficient space + to accommodate entire segment + max_length should be at least 68 bytes + ------------------------------------------------------------------- */ +int diva_istream_read(void *context, + int Id, + void *data, + int max_length, + int *final, + byte *usr1, + byte *usr2) { + ADAPTER *a = (ADAPTER *)context; + int read = 0, to_read = -1; + char tmp[4]; + byte *data_ptr = (byte *)data; + *final = 0; + for (;;) { + a->ram_in_dw(a, +#ifdef PLATFORM_GT_32BIT + ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id]), +#else + (void *)(a->rx_stream[Id] + a->rx_pos[Id]), +#endif + (dword *)&tmp[0], + 1); + if (tmp[1] > max_length) { + if (to_read < 0) + return (-2); /* was not able to read */ + break; + } + if (!(tmp[0] & DIVA_DFIFO_READY)) { + if (to_read < 0) + return (-1); /* was not able to read */ + break; + } + to_read = min(max_length, (int)tmp[1]); + if (to_read) { + a->ram_in_buffer(a, +#ifdef PLATFORM_GT_32BIT + ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id] + 4), +#else + (void *)(a->rx_stream[Id] + a->rx_pos[Id] + 4), +#endif + data_ptr, + (word)to_read); + max_length -= to_read; + read += to_read; + data_ptr += to_read; + } + if (tmp[0] & DIVA_DFIFO_LAST) { + *final = 1; + } + tmp[0] &= DIVA_DFIFO_WRAP; + a->ram_out_dw(a, +#ifdef PLATFORM_GT_32BIT + ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id]), +#else + (void *)(a->rx_stream[Id] + a->rx_pos[Id]), +#endif + (dword *)&tmp[0], + 1); + if (tmp[0] & DIVA_DFIFO_WRAP) { + a->rx_pos[Id] = 0; + } else { + a->rx_pos[Id] += DIVA_DFIFO_STEP; + } + if (*final) { + if (usr1) + *usr1 = tmp[2]; + if (usr2) + *usr2 = tmp[3]; + break; + } + } + return (read); +} +/* --------------------------------------------------------------------- + Does check if one of streams had caused interrupt and does + wake up corresponding application + --------------------------------------------------------------------- */ +void pr_stream(ADAPTER *a) { +} +#endif /* } */ diff --git a/drivers/isdn/hardware/eicon/kst_ifc.h b/drivers/isdn/hardware/eicon/kst_ifc.h new file mode 100644 index 000000000..894fdfda1 --- /dev/null +++ b/drivers/isdn/hardware/eicon/kst_ifc.h @@ -0,0 +1,335 @@ +/* + * + Copyright (c) Eicon Networks, 2000. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 1.9 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_EICON_TRACE_API__ +#define __DIVA_EICON_TRACE_API__ + +#define DIVA_TRACE_LINE_TYPE_LEN 64 +#define DIVA_TRACE_IE_LEN 64 +#define DIVA_TRACE_FAX_PRMS_LEN 128 + +typedef struct _diva_trace_ie { + byte length; + byte data[DIVA_TRACE_IE_LEN]; +} diva_trace_ie_t; + +/* + Structure used to represent "State\\BX\\Modem" directory + to user. +*/ +typedef struct _diva_trace_modem_state { + dword ChannelNumber; + + dword Event; + + dword Norm; + + dword Options; /* Options received from Application */ + + dword TxSpeed; + dword RxSpeed; + + dword RoundtripMsec; + + dword SymbolRate; + + int RxLeveldBm; + int EchoLeveldBm; + + dword SNRdb; + dword MAE; + + dword LocalRetrains; + dword RemoteRetrains; + dword LocalResyncs; + dword RemoteResyncs; + + dword DiscReason; + +} diva_trace_modem_state_t; + +/* + Representation of "State\\BX\\FAX" directory +*/ +typedef struct _diva_trace_fax_state { + dword ChannelNumber; + dword Event; + dword Page_Counter; + dword Features; + char Station_ID[DIVA_TRACE_FAX_PRMS_LEN]; + char Subaddress[DIVA_TRACE_FAX_PRMS_LEN]; + char Password[DIVA_TRACE_FAX_PRMS_LEN]; + dword Speed; + dword Resolution; + dword Paper_Width; + dword Paper_Length; + dword Scanline_Time; + dword Disc_Reason; + dword dummy; +} diva_trace_fax_state_t; + +/* + Structure used to represent Interface State in the abstract + and interface/D-channel protocol independent form. +*/ +typedef struct _diva_trace_interface_state { + char Layer1[DIVA_TRACE_LINE_TYPE_LEN]; + char Layer2[DIVA_TRACE_LINE_TYPE_LEN]; +} diva_trace_interface_state_t; + +typedef struct _diva_incoming_call_statistics { + dword Calls; + dword Connected; + dword User_Busy; + dword Call_Rejected; + dword Wrong_Number; + dword Incompatible_Dst; + dword Out_of_Order; + dword Ignored; +} diva_incoming_call_statistics_t; + +typedef struct _diva_outgoing_call_statistics { + dword Calls; + dword Connected; + dword User_Busy; + dword No_Answer; + dword Wrong_Number; + dword Call_Rejected; + dword Other_Failures; +} diva_outgoing_call_statistics_t; + +typedef struct _diva_modem_call_statistics { + dword Disc_Normal; + dword Disc_Unspecified; + dword Disc_Busy_Tone; + dword Disc_Congestion; + dword Disc_Carr_Wait; + dword Disc_Trn_Timeout; + dword Disc_Incompat; + dword Disc_Frame_Rej; + dword Disc_V42bis; +} diva_modem_call_statistics_t; + +typedef struct _diva_fax_call_statistics { + dword Disc_Normal; + dword Disc_Not_Ident; + dword Disc_No_Response; + dword Disc_Retries; + dword Disc_Unexp_Msg; + dword Disc_No_Polling; + dword Disc_Training; + dword Disc_Unexpected; + dword Disc_Application; + dword Disc_Incompat; + dword Disc_No_Command; + dword Disc_Long_Msg; + dword Disc_Supervisor; + dword Disc_SUB_SEP_PWD; + dword Disc_Invalid_Msg; + dword Disc_Page_Coding; + dword Disc_App_Timeout; + dword Disc_Unspecified; +} diva_fax_call_statistics_t; + +typedef struct _diva_prot_statistics { + dword X_Frames; + dword X_Bytes; + dword X_Errors; + dword R_Frames; + dword R_Bytes; + dword R_Errors; +} diva_prot_statistics_t; + +typedef struct _diva_ifc_statistics { + diva_incoming_call_statistics_t inc; + diva_outgoing_call_statistics_t outg; + diva_modem_call_statistics_t mdm; + diva_fax_call_statistics_t fax; + diva_prot_statistics_t b1; + diva_prot_statistics_t b2; + diva_prot_statistics_t d1; + diva_prot_statistics_t d2; +} diva_ifc_statistics_t; + +/* + Structure used to represent "State\\BX" directory + to user. +*/ +typedef struct _diva_trace_line_state { + dword ChannelNumber; + + char Line[DIVA_TRACE_LINE_TYPE_LEN]; + + char Framing[DIVA_TRACE_LINE_TYPE_LEN]; + + char Layer2[DIVA_TRACE_LINE_TYPE_LEN]; + char Layer3[DIVA_TRACE_LINE_TYPE_LEN]; + + char RemoteAddress[DIVA_TRACE_LINE_TYPE_LEN]; + char RemoteSubAddress[DIVA_TRACE_LINE_TYPE_LEN]; + + char LocalAddress[DIVA_TRACE_LINE_TYPE_LEN]; + char LocalSubAddress[DIVA_TRACE_LINE_TYPE_LEN]; + + diva_trace_ie_t call_BC; + diva_trace_ie_t call_HLC; + diva_trace_ie_t call_LLC; + + dword Charges; + + dword CallReference; + + dword LastDisconnecCause; + + char UserID[DIVA_TRACE_LINE_TYPE_LEN]; + + diva_trace_modem_state_t modem; + diva_trace_fax_state_t fax; + + diva_trace_interface_state_t *pInterface; + + diva_ifc_statistics_t *pInterfaceStat; + +} diva_trace_line_state_t; + +#define DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE ('l') +#define DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE ('m') +#define DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE ('f') +#define DIVA_SUPER_TRACE_INTERFACE_CHANGE ('i') +#define DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE ('s') +#define DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE ('M') +#define DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE ('F') + +struct _diva_strace_library_interface; +typedef void (*diva_trace_channel_state_change_proc_t)(void *user_context, + struct _diva_strace_library_interface *hLib, + int Adapter, + diva_trace_line_state_t *channel, int notify_subject); +typedef void (*diva_trace_channel_trace_proc_t)(void *user_context, + struct _diva_strace_library_interface *hLib, + int Adapter, void *xlog_buffer, int length); +typedef void (*diva_trace_error_proc_t)(void *user_context, + struct _diva_strace_library_interface *hLib, + int Adapter, + int error, const char *file, int line); + +/* + This structure creates interface from user to library +*/ +typedef struct _diva_trace_library_user_interface { + void *user_context; + diva_trace_channel_state_change_proc_t notify_proc; + diva_trace_channel_trace_proc_t trace_proc; + diva_trace_error_proc_t error_notify_proc; +} diva_trace_library_user_interface_t; + +/* + Interface from Library to User +*/ +typedef int (*DivaSTraceLibraryStart_proc_t)(void *hLib); +typedef int (*DivaSTraceLibraryFinit_proc_t)(void *hLib); +typedef int (*DivaSTraceMessageInput_proc_t)(void *hLib); +typedef void* (*DivaSTraceGetHandle_proc_t)(void *hLib); + +/* + Turn Audio Tap trace on/off + Channel should be in the range 1 ... Number of Channels +*/ +typedef int (*DivaSTraceSetAudioTap_proc_t)(void *hLib, int Channel, int on); + +/* + Turn B-channel trace on/off + Channel should be in the range 1 ... Number of Channels +*/ +typedef int (*DivaSTraceSetBChannel_proc_t)(void *hLib, int Channel, int on); + +/* + Turn D-channel (Layer1/Layer2/Layer3) trace on/off + Layer1 - All D-channel frames received/sent over the interface + inclusive Layer 2 headers, Layer 2 frames and TEI management frames + Layer2 - Events from LAPD protocol instance with SAPI of signalling protocol + Layer3 - All D-channel frames addressed to assigned to the card TEI and + SAPI of signalling protocol, and signalling protocol events. +*/ +typedef int (*DivaSTraceSetDChannel_proc_t)(void *hLib, int on); + +/* + Get overall card statistics +*/ +typedef int (*DivaSTraceGetOutgoingCallStatistics_proc_t)(void *hLib); +typedef int (*DivaSTraceGetIncomingCallStatistics_proc_t)(void *hLib); +typedef int (*DivaSTraceGetModemStatistics_proc_t)(void *hLib); +typedef int (*DivaSTraceGetFaxStatistics_proc_t)(void *hLib); +typedef int (*DivaSTraceGetBLayer1Statistics_proc_t)(void *hLib); +typedef int (*DivaSTraceGetBLayer2Statistics_proc_t)(void *hLib); +typedef int (*DivaSTraceGetDLayer1Statistics_proc_t)(void *hLib); +typedef int (*DivaSTraceGetDLayer2Statistics_proc_t)(void *hLib); + +/* + Call control +*/ +typedef int (*DivaSTraceClearCall_proc_t)(void *hLib, int Channel); + +typedef struct _diva_strace_library_interface { + void *hLib; + DivaSTraceLibraryStart_proc_t DivaSTraceLibraryStart; + DivaSTraceLibraryStart_proc_t DivaSTraceLibraryStop; + DivaSTraceLibraryFinit_proc_t DivaSTraceLibraryFinit; + DivaSTraceMessageInput_proc_t DivaSTraceMessageInput; + DivaSTraceGetHandle_proc_t DivaSTraceGetHandle; + DivaSTraceSetAudioTap_proc_t DivaSTraceSetAudioTap; + DivaSTraceSetBChannel_proc_t DivaSTraceSetBChannel; + DivaSTraceSetDChannel_proc_t DivaSTraceSetDChannel; + DivaSTraceSetDChannel_proc_t DivaSTraceSetInfo; + DivaSTraceGetOutgoingCallStatistics_proc_t \ + DivaSTraceGetOutgoingCallStatistics; + DivaSTraceGetIncomingCallStatistics_proc_t \ + DivaSTraceGetIncomingCallStatistics; + DivaSTraceGetModemStatistics_proc_t \ + DivaSTraceGetModemStatistics; + DivaSTraceGetFaxStatistics_proc_t \ + DivaSTraceGetFaxStatistics; + DivaSTraceGetBLayer1Statistics_proc_t \ + DivaSTraceGetBLayer1Statistics; + DivaSTraceGetBLayer2Statistics_proc_t \ + DivaSTraceGetBLayer2Statistics; + DivaSTraceGetDLayer1Statistics_proc_t \ + DivaSTraceGetDLayer1Statistics; + DivaSTraceGetDLayer2Statistics_proc_t \ + DivaSTraceGetDLayer2Statistics; + DivaSTraceClearCall_proc_t DivaSTraceClearCall; +} diva_strace_library_interface_t; + +/* + Create and return Library interface +*/ +diva_strace_library_interface_t *DivaSTraceLibraryCreateInstance(int Adapter, + const diva_trace_library_user_interface_t *user_proc, + byte *pmem); +dword DivaSTraceGetMemotyRequirement(int channels); + +#define DIVA_MAX_ADAPTERS 64 +#define DIVA_MAX_LINES 32 + +#endif diff --git a/drivers/isdn/hardware/eicon/maintidi.c b/drivers/isdn/hardware/eicon/maintidi.c new file mode 100644 index 000000000..2ee789f95 --- /dev/null +++ b/drivers/isdn/hardware/eicon/maintidi.c @@ -0,0 +1,2194 @@ +/* + * + Copyright (c) Eicon Networks, 2000. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 1.9 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "platform.h" +#include "kst_ifc.h" +#include "di_defs.h" +#include "maintidi.h" +#include "pc.h" +#include "man_defs.h" + + +extern void diva_mnt_internal_dprintf(dword drv_id, dword type, char *p, ...); + +#define MODEM_PARSE_ENTRIES 16 /* amount of variables of interest */ +#define FAX_PARSE_ENTRIES 12 /* amount of variables of interest */ +#define LINE_PARSE_ENTRIES 15 /* amount of variables of interest */ +#define STAT_PARSE_ENTRIES 70 /* amount of variables of interest */ + +/* + LOCAL FUNCTIONS +*/ +static int DivaSTraceLibraryStart(void *hLib); +static int DivaSTraceLibraryStop(void *hLib); +static int SuperTraceLibraryFinit(void *hLib); +static void *SuperTraceGetHandle(void *hLib); +static int SuperTraceMessageInput(void *hLib); +static int SuperTraceSetAudioTap(void *hLib, int Channel, int on); +static int SuperTraceSetBChannel(void *hLib, int Channel, int on); +static int SuperTraceSetDChannel(void *hLib, int on); +static int SuperTraceSetInfo(void *hLib, int on); +static int SuperTraceClearCall(void *hLib, int Channel); +static int SuperTraceGetOutgoingCallStatistics(void *hLib); +static int SuperTraceGetIncomingCallStatistics(void *hLib); +static int SuperTraceGetModemStatistics(void *hLib); +static int SuperTraceGetFaxStatistics(void *hLib); +static int SuperTraceGetBLayer1Statistics(void *hLib); +static int SuperTraceGetBLayer2Statistics(void *hLib); +static int SuperTraceGetDLayer1Statistics(void *hLib); +static int SuperTraceGetDLayer2Statistics(void *hLib); + +/* + LOCAL FUNCTIONS +*/ +static int ScheduleNextTraceRequest(diva_strace_context_t *pLib); +static int process_idi_event(diva_strace_context_t *pLib, + diva_man_var_header_t *pVar); +static int process_idi_info(diva_strace_context_t *pLib, + diva_man_var_header_t *pVar); +static int diva_modem_event(diva_strace_context_t *pLib, int Channel); +static int diva_fax_event(diva_strace_context_t *pLib, int Channel); +static int diva_line_event(diva_strace_context_t *pLib, int Channel); +static int diva_modem_info(diva_strace_context_t *pLib, + int Channel, + diva_man_var_header_t *pVar); +static int diva_fax_info(diva_strace_context_t *pLib, + int Channel, + diva_man_var_header_t *pVar); +static int diva_line_info(diva_strace_context_t *pLib, + int Channel, + diva_man_var_header_t *pVar); +static int diva_ifc_statistics(diva_strace_context_t *pLib, + diva_man_var_header_t *pVar); +static diva_man_var_header_t *get_next_var(diva_man_var_header_t *pVar); +static diva_man_var_header_t *find_var(diva_man_var_header_t *pVar, + const char *name); +static int diva_strace_read_int(diva_man_var_header_t *pVar, int *var); +static int diva_strace_read_uint(diva_man_var_header_t *pVar, dword *var); +static int diva_strace_read_asz(diva_man_var_header_t *pVar, char *var); +static int diva_strace_read_asc(diva_man_var_header_t *pVar, char *var); +static int diva_strace_read_ie(diva_man_var_header_t *pVar, + diva_trace_ie_t *var); +static void diva_create_parse_table(diva_strace_context_t *pLib); +static void diva_trace_error(diva_strace_context_t *pLib, + int error, const char *file, int line); +static void diva_trace_notify_user(diva_strace_context_t *pLib, + int Channel, + int notify_subject); +static int diva_trace_read_variable(diva_man_var_header_t *pVar, + void *variable); + +/* + Initialize the library and return context + of the created trace object that will represent + the IDI adapter. + Return 0 on error. +*/ +diva_strace_library_interface_t *DivaSTraceLibraryCreateInstance(int Adapter, + const diva_trace_library_user_interface_t *user_proc, + byte *pmem) { + diva_strace_context_t *pLib = (diva_strace_context_t *)pmem; + int i; + + if (!pLib) { + return NULL; + } + + pmem += sizeof(*pLib); + memset(pLib, 0x00, sizeof(*pLib)); + + pLib->Adapter = Adapter; + + /* + Set up Library Interface + */ + pLib->instance.hLib = pLib; + pLib->instance.DivaSTraceLibraryStart = DivaSTraceLibraryStart; + pLib->instance.DivaSTraceLibraryStop = DivaSTraceLibraryStop; + pLib->instance.DivaSTraceLibraryFinit = SuperTraceLibraryFinit; + pLib->instance.DivaSTraceMessageInput = SuperTraceMessageInput; + pLib->instance.DivaSTraceGetHandle = SuperTraceGetHandle; + pLib->instance.DivaSTraceSetAudioTap = SuperTraceSetAudioTap; + pLib->instance.DivaSTraceSetBChannel = SuperTraceSetBChannel; + pLib->instance.DivaSTraceSetDChannel = SuperTraceSetDChannel; + pLib->instance.DivaSTraceSetInfo = SuperTraceSetInfo; + pLib->instance.DivaSTraceGetOutgoingCallStatistics = \ + SuperTraceGetOutgoingCallStatistics; + pLib->instance.DivaSTraceGetIncomingCallStatistics = \ + SuperTraceGetIncomingCallStatistics; + pLib->instance.DivaSTraceGetModemStatistics = \ + SuperTraceGetModemStatistics; + pLib->instance.DivaSTraceGetFaxStatistics = \ + SuperTraceGetFaxStatistics; + pLib->instance.DivaSTraceGetBLayer1Statistics = \ + SuperTraceGetBLayer1Statistics; + pLib->instance.DivaSTraceGetBLayer2Statistics = \ + SuperTraceGetBLayer2Statistics; + pLib->instance.DivaSTraceGetDLayer1Statistics = \ + SuperTraceGetDLayer1Statistics; + pLib->instance.DivaSTraceGetDLayer2Statistics = \ + SuperTraceGetDLayer2Statistics; + pLib->instance.DivaSTraceClearCall = SuperTraceClearCall; + + + if (user_proc) { + pLib->user_proc_table.user_context = user_proc->user_context; + pLib->user_proc_table.notify_proc = user_proc->notify_proc; + pLib->user_proc_table.trace_proc = user_proc->trace_proc; + pLib->user_proc_table.error_notify_proc = user_proc->error_notify_proc; + } + + if (!(pLib->hAdapter = SuperTraceOpenAdapter(Adapter))) { + diva_mnt_internal_dprintf(0, DLI_ERR, "Can not open XDI adapter"); + return NULL; + } + pLib->Channels = SuperTraceGetNumberOfChannels(pLib->hAdapter); + + /* + Calculate amount of parte table entites necessary to translate + information from all events of onterest + */ + pLib->parse_entries = (MODEM_PARSE_ENTRIES + FAX_PARSE_ENTRIES + \ + STAT_PARSE_ENTRIES + \ + LINE_PARSE_ENTRIES + 1) * pLib->Channels; + pLib->parse_table = (diva_strace_path2action_t *)pmem; + + for (i = 0; i < 30; i++) { + pLib->lines[i].pInterface = &pLib->Interface; + pLib->lines[i].pInterfaceStat = &pLib->InterfaceStat; + } + + pLib->e.R = &pLib->RData; + + pLib->req_busy = 1; + pLib->rc_ok = ASSIGN_OK; + + diva_create_parse_table(pLib); + + return ((diva_strace_library_interface_t *)pLib); +} + +static int DivaSTraceLibraryStart(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + + return (SuperTraceASSIGN(pLib->hAdapter, pLib->buffer)); +} + +/* + Return (-1) on error + Return (0) if was initiated or pending + Return (1) if removal is complete +*/ +static int DivaSTraceLibraryStop(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + + if (!pLib->e.Id) { /* Was never started/assigned */ + return (1); + } + + switch (pLib->removal_state) { + case 0: + pLib->removal_state = 1; + ScheduleNextTraceRequest(pLib); + break; + + case 3: + return (1); + } + + return (0); +} + +static int SuperTraceLibraryFinit(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + if (pLib) { + if (pLib->hAdapter) { + SuperTraceCloseAdapter(pLib->hAdapter); + } + return (0); + } + return (-1); +} + +static void *SuperTraceGetHandle(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + + return (&pLib->e); +} + +/* + After library handle object is gone in signaled state + this function should be called and will pick up incoming + IDI messages (return codes and indications). +*/ +static int SuperTraceMessageInput(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + int ret = 0; + byte Rc, Ind; + + if (pLib->e.complete == 255) { + /* + Process return code + */ + pLib->req_busy = 0; + Rc = pLib->e.Rc; + pLib->e.Rc = 0; + + if (pLib->removal_state == 2) { + pLib->removal_state = 3; + return (0); + } + + if (Rc != pLib->rc_ok) { + int ignore = 0; + /* + Auto-detect amount of events/channels and features + */ + if (pLib->general_b_ch_event == 1) { + pLib->general_b_ch_event = 2; + ignore = 1; + } else if (pLib->general_fax_event == 1) { + pLib->general_fax_event = 2; + ignore = 1; + } else if (pLib->general_mdm_event == 1) { + pLib->general_mdm_event = 2; + ignore = 1; + } else if ((pLib->ChannelsTraceActive < pLib->Channels) && pLib->ChannelsTraceActive) { + pLib->ChannelsTraceActive = pLib->Channels; + ignore = 1; + } else if (pLib->ModemTraceActive < pLib->Channels) { + pLib->ModemTraceActive = pLib->Channels; + ignore = 1; + } else if (pLib->FaxTraceActive < pLib->Channels) { + pLib->FaxTraceActive = pLib->Channels; + ignore = 1; + } else if (pLib->audio_trace_init == 2) { + ignore = 1; + pLib->audio_trace_init = 1; + } else if (pLib->eye_pattern_pending) { + pLib->eye_pattern_pending = 0; + ignore = 1; + } else if (pLib->audio_tap_pending) { + pLib->audio_tap_pending = 0; + ignore = 1; + } + + if (!ignore) { + return (-1); /* request failed */ + } + } else { + if (pLib->general_b_ch_event == 1) { + pLib->ChannelsTraceActive = pLib->Channels; + pLib->general_b_ch_event = 2; + } else if (pLib->general_fax_event == 1) { + pLib->general_fax_event = 2; + pLib->FaxTraceActive = pLib->Channels; + } else if (pLib->general_mdm_event == 1) { + pLib->general_mdm_event = 2; + pLib->ModemTraceActive = pLib->Channels; + } + } + if (pLib->audio_trace_init == 2) { + pLib->audio_trace_init = 1; + } + pLib->rc_ok = 0xff; /* default OK after assign was done */ + if ((ret = ScheduleNextTraceRequest(pLib))) { + return (-1); + } + } else { + /* + Process indication + Always 'RNR' indication if return code is pending + */ + Ind = pLib->e.Ind; + pLib->e.Ind = 0; + if (pLib->removal_state) { + pLib->e.RNum = 0; + pLib->e.RNR = 2; + } else if (pLib->req_busy) { + pLib->e.RNum = 0; + pLib->e.RNR = 1; + } else { + if (pLib->e.complete != 0x02) { + /* + Look-ahead call, set up buffers + */ + pLib->e.RNum = 1; + pLib->e.R->P = (byte *)&pLib->buffer[0]; + pLib->e.R->PLength = (word)(sizeof(pLib->buffer) - 1); + + } else { + /* + Indication reception complete, process it now + */ + byte *p = (byte *)&pLib->buffer[0]; + pLib->buffer[pLib->e.R->PLength] = 0; /* terminate I.E. with zero */ + + switch (Ind) { + case MAN_COMBI_IND: { + int total_length = pLib->e.R->PLength; + word this_ind_length; + + while (total_length > 3 && *p) { + Ind = *p++; + this_ind_length = (word)p[0] | ((word)p[1] << 8); + p += 2; + + switch (Ind) { + case MAN_INFO_IND: + if (process_idi_info(pLib, (diva_man_var_header_t *)p)) { + return (-1); + } + break; + case MAN_EVENT_IND: + if (process_idi_event(pLib, (diva_man_var_header_t *)p)) { + return (-1); + } + break; + case MAN_TRACE_IND: + if (pLib->trace_on == 1) { + /* + Ignore first trace event that is result of + EVENT_ON operation + */ + pLib->trace_on++; + } else { + /* + Delivery XLOG buffer to application + */ + if (pLib->user_proc_table.trace_proc) { + (*(pLib->user_proc_table.trace_proc))(pLib->user_proc_table.user_context, + &pLib->instance, pLib->Adapter, + p, this_ind_length); + } + } + break; + default: + diva_mnt_internal_dprintf(0, DLI_ERR, "Unknown IDI Ind (DMA mode): %02x", Ind); + } + p += (this_ind_length + 1); + total_length -= (4 + this_ind_length); + } + } break; + case MAN_INFO_IND: + if (process_idi_info(pLib, (diva_man_var_header_t *)p)) { + return (-1); + } + break; + case MAN_EVENT_IND: + if (process_idi_event(pLib, (diva_man_var_header_t *)p)) { + return (-1); + } + break; + case MAN_TRACE_IND: + if (pLib->trace_on == 1) { + /* + Ignore first trace event that is result of + EVENT_ON operation + */ + pLib->trace_on++; + } else { + /* + Delivery XLOG buffer to application + */ + if (pLib->user_proc_table.trace_proc) { + (*(pLib->user_proc_table.trace_proc))(pLib->user_proc_table.user_context, + &pLib->instance, pLib->Adapter, + p, pLib->e.R->PLength); + } + } + break; + default: + diva_mnt_internal_dprintf(0, DLI_ERR, "Unknown IDI Ind: %02x", Ind); + } + } + } + } + + if ((ret = ScheduleNextTraceRequest(pLib))) { + return (-1); + } + + return (ret); +} + +/* + Internal state machine responsible for scheduling of requests +*/ +static int ScheduleNextTraceRequest(diva_strace_context_t *pLib) { + char name[64]; + int ret = 0; + int i; + + if (pLib->req_busy) { + return (0); + } + + if (pLib->removal_state == 1) { + if (SuperTraceREMOVE(pLib->hAdapter)) { + pLib->removal_state = 3; + } else { + pLib->req_busy = 1; + pLib->removal_state = 2; + } + return (0); + } + + if (pLib->removal_state) { + return (0); + } + + if (!pLib->general_b_ch_event) { + if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\B Event", pLib->buffer))) { + return (-1); + } + pLib->general_b_ch_event = 1; + pLib->req_busy = 1; + return (0); + } + + if (!pLib->general_fax_event) { + if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\FAX Event", pLib->buffer))) { + return (-1); + } + pLib->general_fax_event = 1; + pLib->req_busy = 1; + return (0); + } + + if (!pLib->general_mdm_event) { + if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\Modem Event", pLib->buffer))) { + return (-1); + } + pLib->general_mdm_event = 1; + pLib->req_busy = 1; + return (0); + } + + if (pLib->ChannelsTraceActive < pLib->Channels) { + pLib->ChannelsTraceActive++; + sprintf(name, "State\\B%d\\Line", pLib->ChannelsTraceActive); + if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) { + pLib->ChannelsTraceActive--; + return (-1); + } + pLib->req_busy = 1; + return (0); + } + + if (pLib->ModemTraceActive < pLib->Channels) { + pLib->ModemTraceActive++; + sprintf(name, "State\\B%d\\Modem\\Event", pLib->ModemTraceActive); + if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) { + pLib->ModemTraceActive--; + return (-1); + } + pLib->req_busy = 1; + return (0); + } + + if (pLib->FaxTraceActive < pLib->Channels) { + pLib->FaxTraceActive++; + sprintf(name, "State\\B%d\\FAX\\Event", pLib->FaxTraceActive); + if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) { + pLib->FaxTraceActive--; + return (-1); + } + pLib->req_busy = 1; + return (0); + } + + if (!pLib->trace_mask_init) { + word tmp = 0x0000; + if (SuperTraceWriteVar(pLib->hAdapter, + pLib->buffer, + "Trace\\Event Enable", + &tmp, + 0x87, /* MI_BITFLD */ + sizeof(tmp))) { + return (-1); + } + pLib->trace_mask_init = 1; + pLib->req_busy = 1; + return (0); + } + + if (!pLib->audio_trace_init) { + dword tmp = 0x00000000; + if (SuperTraceWriteVar(pLib->hAdapter, + pLib->buffer, + "Trace\\AudioCh# Enable", + &tmp, + 0x87, /* MI_BITFLD */ + sizeof(tmp))) { + return (-1); + } + pLib->audio_trace_init = 2; + pLib->req_busy = 1; + return (0); + } + + if (!pLib->bchannel_init) { + dword tmp = 0x00000000; + if (SuperTraceWriteVar(pLib->hAdapter, + pLib->buffer, + "Trace\\B-Ch# Enable", + &tmp, + 0x87, /* MI_BITFLD */ + sizeof(tmp))) { + return (-1); + } + pLib->bchannel_init = 1; + pLib->req_busy = 1; + return (0); + } + + if (!pLib->trace_length_init) { + word tmp = 30; + if (SuperTraceWriteVar(pLib->hAdapter, + pLib->buffer, + "Trace\\Max Log Length", + &tmp, + 0x82, /* MI_UINT */ + sizeof(tmp))) { + return (-1); + } + pLib->trace_length_init = 1; + pLib->req_busy = 1; + return (0); + } + + if (!pLib->trace_on) { + if (SuperTraceTraceOnRequest(pLib->hAdapter, + "Trace\\Log Buffer", + pLib->buffer)) { + return (-1); + } + pLib->trace_on = 1; + pLib->req_busy = 1; + return (0); + } + + if (pLib->trace_event_mask != pLib->current_trace_event_mask) { + if (SuperTraceWriteVar(pLib->hAdapter, + pLib->buffer, + "Trace\\Event Enable", + &pLib->trace_event_mask, + 0x87, /* MI_BITFLD */ + sizeof(pLib->trace_event_mask))) { + return (-1); + } + pLib->current_trace_event_mask = pLib->trace_event_mask; + pLib->req_busy = 1; + return (0); + } + + if ((pLib->audio_tap_pending >= 0) && (pLib->audio_tap_mask != pLib->current_audio_tap_mask)) { + if (SuperTraceWriteVar(pLib->hAdapter, + pLib->buffer, + "Trace\\AudioCh# Enable", + &pLib->audio_tap_mask, + 0x87, /* MI_BITFLD */ + sizeof(pLib->audio_tap_mask))) { + return (-1); + } + pLib->current_audio_tap_mask = pLib->audio_tap_mask; + pLib->audio_tap_pending = 1; + pLib->req_busy = 1; + return (0); + } + + if ((pLib->eye_pattern_pending >= 0) && (pLib->audio_tap_mask != pLib->current_eye_pattern_mask)) { + if (SuperTraceWriteVar(pLib->hAdapter, + pLib->buffer, + "Trace\\EyeCh# Enable", + &pLib->audio_tap_mask, + 0x87, /* MI_BITFLD */ + sizeof(pLib->audio_tap_mask))) { + return (-1); + } + pLib->current_eye_pattern_mask = pLib->audio_tap_mask; + pLib->eye_pattern_pending = 1; + pLib->req_busy = 1; + return (0); + } + + if (pLib->bchannel_trace_mask != pLib->current_bchannel_trace_mask) { + if (SuperTraceWriteVar(pLib->hAdapter, + pLib->buffer, + "Trace\\B-Ch# Enable", + &pLib->bchannel_trace_mask, + 0x87, /* MI_BITFLD */ + sizeof(pLib->bchannel_trace_mask))) { + return (-1); + } + pLib->current_bchannel_trace_mask = pLib->bchannel_trace_mask; + pLib->req_busy = 1; + return (0); + } + + if (!pLib->trace_events_down) { + if (SuperTraceTraceOnRequest(pLib->hAdapter, + "Events Down", + pLib->buffer)) { + return (-1); + } + pLib->trace_events_down = 1; + pLib->req_busy = 1; + return (0); + } + + if (!pLib->l1_trace) { + if (SuperTraceTraceOnRequest(pLib->hAdapter, + "State\\Layer1", + pLib->buffer)) { + return (-1); + } + pLib->l1_trace = 1; + pLib->req_busy = 1; + return (0); + } + + if (!pLib->l2_trace) { + if (SuperTraceTraceOnRequest(pLib->hAdapter, + "State\\Layer2 No1", + pLib->buffer)) { + return (-1); + } + pLib->l2_trace = 1; + pLib->req_busy = 1; + return (0); + } + + for (i = 0; i < 30; i++) { + if (pLib->pending_line_status & (1L << i)) { + sprintf(name, "State\\B%d", i + 1); + if (SuperTraceReadRequest(pLib->hAdapter, name, pLib->buffer)) { + return (-1); + } + pLib->pending_line_status &= ~(1L << i); + pLib->req_busy = 1; + return (0); + } + if (pLib->pending_modem_status & (1L << i)) { + sprintf(name, "State\\B%d\\Modem", i + 1); + if (SuperTraceReadRequest(pLib->hAdapter, name, pLib->buffer)) { + return (-1); + } + pLib->pending_modem_status &= ~(1L << i); + pLib->req_busy = 1; + return (0); + } + if (pLib->pending_fax_status & (1L << i)) { + sprintf(name, "State\\B%d\\FAX", i + 1); + if (SuperTraceReadRequest(pLib->hAdapter, name, pLib->buffer)) { + return (-1); + } + pLib->pending_fax_status &= ~(1L << i); + pLib->req_busy = 1; + return (0); + } + if (pLib->clear_call_command & (1L << i)) { + sprintf(name, "State\\B%d\\Clear Call", i + 1); + if (SuperTraceExecuteRequest(pLib->hAdapter, name, pLib->buffer)) { + return (-1); + } + pLib->clear_call_command &= ~(1L << i); + pLib->req_busy = 1; + return (0); + } + } + + if (pLib->outgoing_ifc_stats) { + if (SuperTraceReadRequest(pLib->hAdapter, + "Statistics\\Outgoing Calls", + pLib->buffer)) { + return (-1); + } + pLib->outgoing_ifc_stats = 0; + pLib->req_busy = 1; + return (0); + } + + if (pLib->incoming_ifc_stats) { + if (SuperTraceReadRequest(pLib->hAdapter, + "Statistics\\Incoming Calls", + pLib->buffer)) { + return (-1); + } + pLib->incoming_ifc_stats = 0; + pLib->req_busy = 1; + return (0); + } + + if (pLib->modem_ifc_stats) { + if (SuperTraceReadRequest(pLib->hAdapter, + "Statistics\\Modem", + pLib->buffer)) { + return (-1); + } + pLib->modem_ifc_stats = 0; + pLib->req_busy = 1; + return (0); + } + + if (pLib->fax_ifc_stats) { + if (SuperTraceReadRequest(pLib->hAdapter, + "Statistics\\FAX", + pLib->buffer)) { + return (-1); + } + pLib->fax_ifc_stats = 0; + pLib->req_busy = 1; + return (0); + } + + if (pLib->b1_ifc_stats) { + if (SuperTraceReadRequest(pLib->hAdapter, + "Statistics\\B-Layer1", + pLib->buffer)) { + return (-1); + } + pLib->b1_ifc_stats = 0; + pLib->req_busy = 1; + return (0); + } + + if (pLib->b2_ifc_stats) { + if (SuperTraceReadRequest(pLib->hAdapter, + "Statistics\\B-Layer2", + pLib->buffer)) { + return (-1); + } + pLib->b2_ifc_stats = 0; + pLib->req_busy = 1; + return (0); + } + + if (pLib->d1_ifc_stats) { + if (SuperTraceReadRequest(pLib->hAdapter, + "Statistics\\D-Layer1", + pLib->buffer)) { + return (-1); + } + pLib->d1_ifc_stats = 0; + pLib->req_busy = 1; + return (0); + } + + if (pLib->d2_ifc_stats) { + if (SuperTraceReadRequest(pLib->hAdapter, + "Statistics\\D-Layer2", + pLib->buffer)) { + return (-1); + } + pLib->d2_ifc_stats = 0; + pLib->req_busy = 1; + return (0); + } + + if (!pLib->IncomingCallsCallsActive) { + pLib->IncomingCallsCallsActive = 1; + sprintf(name, "%s", "Statistics\\Incoming Calls\\Calls"); + if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) { + pLib->IncomingCallsCallsActive = 0; + return (-1); + } + pLib->req_busy = 1; + return (0); + } + if (!pLib->IncomingCallsConnectedActive) { + pLib->IncomingCallsConnectedActive = 1; + sprintf(name, "%s", "Statistics\\Incoming Calls\\Connected"); + if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) { + pLib->IncomingCallsConnectedActive = 0; + return (-1); + } + pLib->req_busy = 1; + return (0); + } + if (!pLib->OutgoingCallsCallsActive) { + pLib->OutgoingCallsCallsActive = 1; + sprintf(name, "%s", "Statistics\\Outgoing Calls\\Calls"); + if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) { + pLib->OutgoingCallsCallsActive = 0; + return (-1); + } + pLib->req_busy = 1; + return (0); + } + if (!pLib->OutgoingCallsConnectedActive) { + pLib->OutgoingCallsConnectedActive = 1; + sprintf(name, "%s", "Statistics\\Outgoing Calls\\Connected"); + if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) { + pLib->OutgoingCallsConnectedActive = 0; + return (-1); + } + pLib->req_busy = 1; + return (0); + } + + return (0); +} + +static int process_idi_event(diva_strace_context_t *pLib, + diva_man_var_header_t *pVar) { + const char *path = (char *)&pVar->path_length + 1; + char name[64]; + int i; + + if (!strncmp("State\\B Event", path, pVar->path_length)) { + dword ch_id; + if (!diva_trace_read_variable(pVar, &ch_id)) { + if (!pLib->line_init_event && !pLib->pending_line_status) { + for (i = 1; i <= pLib->Channels; i++) { + diva_line_event(pLib, i); + } + return (0); + } else if (ch_id && ch_id <= pLib->Channels) { + return (diva_line_event(pLib, (int)ch_id)); + } + return (0); + } + return (-1); + } + + if (!strncmp("State\\FAX Event", path, pVar->path_length)) { + dword ch_id; + if (!diva_trace_read_variable(pVar, &ch_id)) { + if (!pLib->pending_fax_status && !pLib->fax_init_event) { + for (i = 1; i <= pLib->Channels; i++) { + diva_fax_event(pLib, i); + } + return (0); + } else if (ch_id && ch_id <= pLib->Channels) { + return (diva_fax_event(pLib, (int)ch_id)); + } + return (0); + } + return (-1); + } + + if (!strncmp("State\\Modem Event", path, pVar->path_length)) { + dword ch_id; + if (!diva_trace_read_variable(pVar, &ch_id)) { + if (!pLib->pending_modem_status && !pLib->modem_init_event) { + for (i = 1; i <= pLib->Channels; i++) { + diva_modem_event(pLib, i); + } + return (0); + } else if (ch_id && ch_id <= pLib->Channels) { + return (diva_modem_event(pLib, (int)ch_id)); + } + return (0); + } + return (-1); + } + + /* + First look for Line Event + */ + for (i = 1; i <= pLib->Channels; i++) { + sprintf(name, "State\\B%d\\Line", i); + if (find_var(pVar, name)) { + return (diva_line_event(pLib, i)); + } + } + + /* + Look for Moden Progress Event + */ + for (i = 1; i <= pLib->Channels; i++) { + sprintf(name, "State\\B%d\\Modem\\Event", i); + if (find_var(pVar, name)) { + return (diva_modem_event(pLib, i)); + } + } + + /* + Look for Fax Event + */ + for (i = 1; i <= pLib->Channels; i++) { + sprintf(name, "State\\B%d\\FAX\\Event", i); + if (find_var(pVar, name)) { + return (diva_fax_event(pLib, i)); + } + } + + /* + Notification about loss of events + */ + if (!strncmp("Events Down", path, pVar->path_length)) { + if (pLib->trace_events_down == 1) { + pLib->trace_events_down = 2; + } else { + diva_trace_error(pLib, 1, "Events Down", 0); + } + return (0); + } + + if (!strncmp("State\\Layer1", path, pVar->path_length)) { + diva_strace_read_asz(pVar, &pLib->lines[0].pInterface->Layer1[0]); + if (pLib->l1_trace == 1) { + pLib->l1_trace = 2; + } else { + diva_trace_notify_user(pLib, 0, DIVA_SUPER_TRACE_INTERFACE_CHANGE); + } + return (0); + } + if (!strncmp("State\\Layer2 No1", path, pVar->path_length)) { + char *tmp = &pLib->lines[0].pInterface->Layer2[0]; + dword l2_state; + if (diva_strace_read_uint(pVar, &l2_state)) + return -1; + + switch (l2_state) { + case 0: + strcpy(tmp, "Idle"); + break; + case 1: + strcpy(tmp, "Layer2 UP"); + break; + case 2: + strcpy(tmp, "Layer2 Disconnecting"); + break; + case 3: + strcpy(tmp, "Layer2 Connecting"); + break; + case 4: + strcpy(tmp, "SPID Initializing"); + break; + case 5: + strcpy(tmp, "SPID Initialised"); + break; + case 6: + strcpy(tmp, "Layer2 Connecting"); + break; + + case 7: + strcpy(tmp, "Auto SPID Stopped"); + break; + + case 8: + strcpy(tmp, "Auto SPID Idle"); + break; + + case 9: + strcpy(tmp, "Auto SPID Requested"); + break; + + case 10: + strcpy(tmp, "Auto SPID Delivery"); + break; + + case 11: + strcpy(tmp, "Auto SPID Complete"); + break; + + default: + sprintf(tmp, "U:%d", (int)l2_state); + } + if (pLib->l2_trace == 1) { + pLib->l2_trace = 2; + } else { + diva_trace_notify_user(pLib, 0, DIVA_SUPER_TRACE_INTERFACE_CHANGE); + } + return (0); + } + + if (!strncmp("Statistics\\Incoming Calls\\Calls", path, pVar->path_length) || + !strncmp("Statistics\\Incoming Calls\\Connected", path, pVar->path_length)) { + return (SuperTraceGetIncomingCallStatistics(pLib)); + } + + if (!strncmp("Statistics\\Outgoing Calls\\Calls", path, pVar->path_length) || + !strncmp("Statistics\\Outgoing Calls\\Connected", path, pVar->path_length)) { + return (SuperTraceGetOutgoingCallStatistics(pLib)); + } + + return (-1); +} + +static int diva_line_event(diva_strace_context_t *pLib, int Channel) { + pLib->pending_line_status |= (1L << (Channel - 1)); + return (0); +} + +static int diva_modem_event(diva_strace_context_t *pLib, int Channel) { + pLib->pending_modem_status |= (1L << (Channel - 1)); + return (0); +} + +static int diva_fax_event(diva_strace_context_t *pLib, int Channel) { + pLib->pending_fax_status |= (1L << (Channel - 1)); + return (0); +} + +/* + Process INFO indications that arrive from the card + Uses path of first I.E. to detect the source of the + infication +*/ +static int process_idi_info(diva_strace_context_t *pLib, + diva_man_var_header_t *pVar) { + const char *path = (char *)&pVar->path_length + 1; + char name[64]; + int i, len; + + /* + First look for Modem Status Info + */ + for (i = pLib->Channels; i > 0; i--) { + len = sprintf(name, "State\\B%d\\Modem", i); + if (!strncmp(name, path, len)) { + return (diva_modem_info(pLib, i, pVar)); + } + } + + /* + Look for Fax Status Info + */ + for (i = pLib->Channels; i > 0; i--) { + len = sprintf(name, "State\\B%d\\FAX", i); + if (!strncmp(name, path, len)) { + return (diva_fax_info(pLib, i, pVar)); + } + } + + /* + Look for Line Status Info + */ + for (i = pLib->Channels; i > 0; i--) { + len = sprintf(name, "State\\B%d", i); + if (!strncmp(name, path, len)) { + return (diva_line_info(pLib, i, pVar)); + } + } + + if (!diva_ifc_statistics(pLib, pVar)) { + return (0); + } + + return (-1); +} + +/* + MODEM INSTANCE STATE UPDATE + + Update Modem Status Information and issue notification to user, + that will inform about change in the state of modem instance, that is + associuated with this channel +*/ +static int diva_modem_info(diva_strace_context_t *pLib, + int Channel, + diva_man_var_header_t *pVar) { + diva_man_var_header_t *cur; + int i, nr = Channel - 1; + + for (i = pLib->modem_parse_entry_first[nr]; + i <= pLib->modem_parse_entry_last[nr]; i++) { + if ((cur = find_var(pVar, pLib->parse_table[i].path))) { + if (diva_trace_read_variable(cur, pLib->parse_table[i].variable)) { + diva_trace_error(pLib, -3, __FILE__, __LINE__); + return (-1); + } + } else { + diva_trace_error(pLib, -2, __FILE__, __LINE__); + return (-1); + } + } + + /* + We do not use first event to notify user - this is the event that is + generated as result of EVENT ON operation and is used only to initialize + internal variables of application + */ + if (pLib->modem_init_event & (1L << nr)) { + diva_trace_notify_user(pLib, nr, DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE); + } else { + pLib->modem_init_event |= (1L << nr); + } + + return (0); +} + +static int diva_fax_info(diva_strace_context_t *pLib, + int Channel, + diva_man_var_header_t *pVar) { + diva_man_var_header_t *cur; + int i, nr = Channel - 1; + + for (i = pLib->fax_parse_entry_first[nr]; + i <= pLib->fax_parse_entry_last[nr]; i++) { + if ((cur = find_var(pVar, pLib->parse_table[i].path))) { + if (diva_trace_read_variable(cur, pLib->parse_table[i].variable)) { + diva_trace_error(pLib, -3, __FILE__, __LINE__); + return (-1); + } + } else { + diva_trace_error(pLib, -2, __FILE__, __LINE__); + return (-1); + } + } + + /* + We do not use first event to notify user - this is the event that is + generated as result of EVENT ON operation and is used only to initialize + internal variables of application + */ + if (pLib->fax_init_event & (1L << nr)) { + diva_trace_notify_user(pLib, nr, DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE); + } else { + pLib->fax_init_event |= (1L << nr); + } + + return (0); +} + +/* + LINE STATE UPDATE + Update Line Status Information and issue notification to user, + that will inform about change in the line state. +*/ +static int diva_line_info(diva_strace_context_t *pLib, + int Channel, + diva_man_var_header_t *pVar) { + diva_man_var_header_t *cur; + int i, nr = Channel - 1; + + for (i = pLib->line_parse_entry_first[nr]; + i <= pLib->line_parse_entry_last[nr]; i++) { + if ((cur = find_var(pVar, pLib->parse_table[i].path))) { + if (diva_trace_read_variable(cur, pLib->parse_table[i].variable)) { + diva_trace_error(pLib, -3, __FILE__, __LINE__); + return (-1); + } + } else { + diva_trace_error(pLib, -2 , __FILE__, __LINE__); + return (-1); + } + } + + /* + We do not use first event to notify user - this is the event that is + generated as result of EVENT ON operation and is used only to initialize + internal variables of application + + Exception is is if the line is "online". In this case we have to notify + user about this confition. + */ + if (pLib->line_init_event & (1L << nr)) { + diva_trace_notify_user(pLib, nr, DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE); + } else { + pLib->line_init_event |= (1L << nr); + if (strcmp(&pLib->lines[nr].Line[0], "Idle")) { + diva_trace_notify_user(pLib, nr, DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE); + } + } + + return (0); +} + +/* + Move position to next vatianle in the chain +*/ +static diva_man_var_header_t *get_next_var(diva_man_var_header_t *pVar) { + byte *msg = (byte *)pVar; + byte *start; + int msg_length; + + if (*msg != ESC) return NULL; + + start = msg + 2; + msg_length = *(msg + 1); + msg = (start + msg_length); + + if (*msg != ESC) return NULL; + + return ((diva_man_var_header_t *)msg); +} + +/* + Move position to variable with given name +*/ +static diva_man_var_header_t *find_var(diva_man_var_header_t *pVar, + const char *name) { + const char *path; + + do { + path = (char *)&pVar->path_length + 1; + + if (!strncmp(name, path, pVar->path_length)) { + break; + } + } while ((pVar = get_next_var(pVar))); + + return (pVar); +} + +static void diva_create_line_parse_table(diva_strace_context_t *pLib, + int Channel) { + diva_trace_line_state_t *pLine = &pLib->lines[Channel]; + int nr = Channel + 1; + + if ((pLib->cur_parse_entry + LINE_PARSE_ENTRIES) >= pLib->parse_entries) { + diva_trace_error(pLib, -1, __FILE__, __LINE__); + return; + } + + pLine->ChannelNumber = nr; + + pLib->line_parse_entry_first[Channel] = pLib->cur_parse_entry; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Framing", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Framing[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Line", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Line[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Layer2", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Layer2[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Layer3", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Layer3[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Remote Address", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLine->RemoteAddress[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Remote SubAddr", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLine->RemoteSubAddress[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Local Address", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLine->LocalAddress[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Local SubAddr", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLine->LocalSubAddress[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\BC", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_BC; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\HLC", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_HLC; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\LLC", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_LLC; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Charges", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Charges; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Call Reference", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->CallReference; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Last Disc Cause", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLine->LastDisconnecCause; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\User ID", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->UserID[0]; + + pLib->line_parse_entry_last[Channel] = pLib->cur_parse_entry - 1; +} + +static void diva_create_fax_parse_table(diva_strace_context_t *pLib, + int Channel) { + diva_trace_fax_state_t *pFax = &pLib->lines[Channel].fax; + int nr = Channel + 1; + + if ((pLib->cur_parse_entry + FAX_PARSE_ENTRIES) >= pLib->parse_entries) { + diva_trace_error(pLib, -1, __FILE__, __LINE__); + return; + } + pFax->ChannelNumber = nr; + + pLib->fax_parse_entry_first[Channel] = pLib->cur_parse_entry; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Event", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Event; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Page Counter", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Page_Counter; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Features", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Features; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Station ID", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Station_ID[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Subaddress", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Subaddress[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Password", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Password[0]; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Speed", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Speed; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Resolution", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Resolution; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Paper Width", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Paper_Width; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Paper Length", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Paper_Length; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Scanline Time", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Scanline_Time; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\FAX\\Disc Reason", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Disc_Reason; + + pLib->fax_parse_entry_last[Channel] = pLib->cur_parse_entry - 1; +} + +static void diva_create_modem_parse_table(diva_strace_context_t *pLib, + int Channel) { + diva_trace_modem_state_t *pModem = &pLib->lines[Channel].modem; + int nr = Channel + 1; + + if ((pLib->cur_parse_entry + MODEM_PARSE_ENTRIES) >= pLib->parse_entries) { + diva_trace_error(pLib, -1, __FILE__, __LINE__); + return; + } + pModem->ChannelNumber = nr; + + pLib->modem_parse_entry_first[Channel] = pLib->cur_parse_entry; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Event", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Event; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Norm", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Norm; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Options", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Options; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\TX Speed", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->TxSpeed; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\RX Speed", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RxSpeed; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Roundtrip ms", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RoundtripMsec; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Symbol Rate", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->SymbolRate; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\RX Level dBm", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RxLeveldBm; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Echo Level dBm", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->EchoLeveldBm; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\SNR dB", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->SNRdb; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\MAE", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->MAE; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Local Retrains", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->LocalRetrains; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Remote Retrains", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RemoteRetrains; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Local Resyncs", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->LocalResyncs; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Remote Resyncs", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RemoteResyncs; + + sprintf(pLib->parse_table[pLib->cur_parse_entry].path, + "State\\B%d\\Modem\\Disc Reason", nr); + pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->DiscReason; + + pLib->modem_parse_entry_last[Channel] = pLib->cur_parse_entry - 1; +} + +static void diva_create_parse_table(diva_strace_context_t *pLib) { + int i; + + for (i = 0; i < pLib->Channels; i++) { + diva_create_line_parse_table(pLib, i); + diva_create_modem_parse_table(pLib, i); + diva_create_fax_parse_table(pLib, i); + } + + pLib->statistic_parse_first = pLib->cur_parse_entry; + + /* + Outgoing Calls + */ + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Outgoing Calls\\Calls"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.outg.Calls; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Outgoing Calls\\Connected"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.outg.Connected; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Outgoing Calls\\User Busy"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.outg.User_Busy; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Outgoing Calls\\No Answer"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.outg.No_Answer; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Outgoing Calls\\Wrong Number"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.outg.Wrong_Number; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Outgoing Calls\\Call Rejected"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.outg.Call_Rejected; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Outgoing Calls\\Other Failures"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.outg.Other_Failures; + + /* + Incoming Calls + */ + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Incoming Calls\\Calls"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.inc.Calls; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Incoming Calls\\Connected"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.inc.Connected; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Incoming Calls\\User Busy"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.inc.User_Busy; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Incoming Calls\\Call Rejected"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.inc.Call_Rejected; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Incoming Calls\\Wrong Number"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.inc.Wrong_Number; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Incoming Calls\\Incompatible Dst"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.inc.Incompatible_Dst; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Incoming Calls\\Out of Order"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.inc.Out_of_Order; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Incoming Calls\\Ignored"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.inc.Ignored; + + /* + Modem Statistics + */ + pLib->mdm_statistic_parse_first = pLib->cur_parse_entry; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Modem\\Disc Normal"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.mdm.Disc_Normal; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Modem\\Disc Unspecified"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.mdm.Disc_Unspecified; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Modem\\Disc Busy Tone"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.mdm.Disc_Busy_Tone; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Modem\\Disc Congestion"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.mdm.Disc_Congestion; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Modem\\Disc Carr. Wait"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.mdm.Disc_Carr_Wait; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Modem\\Disc Trn Timeout"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.mdm.Disc_Trn_Timeout; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Modem\\Disc Incompat."); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.mdm.Disc_Incompat; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Modem\\Disc Frame Rej."); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.mdm.Disc_Frame_Rej; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\Modem\\Disc V42bis"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.mdm.Disc_V42bis; + + pLib->mdm_statistic_parse_last = pLib->cur_parse_entry - 1; + + /* + Fax Statistics + */ + pLib->fax_statistic_parse_first = pLib->cur_parse_entry; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Normal"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Normal; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Not Ident."); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Not_Ident; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc No Response"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_No_Response; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Retries"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Retries; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Unexp. Msg."); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Unexp_Msg; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc No Polling."); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_No_Polling; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Training"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Training; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Unexpected"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Unexpected; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Application"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Application; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Incompat."); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Incompat; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc No Command"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_No_Command; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Long Msg"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Long_Msg; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Supervisor"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Supervisor; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc SUB SEP PWD"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_SUB_SEP_PWD; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Invalid Msg"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Invalid_Msg; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Page Coding"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Page_Coding; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc App Timeout"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_App_Timeout; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\FAX\\Disc Unspecified"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.fax.Disc_Unspecified; + + pLib->fax_statistic_parse_last = pLib->cur_parse_entry - 1; + + /* + B-Layer1" + */ + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer1\\X-Frames"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b1.X_Frames; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer1\\X-Bytes"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b1.X_Bytes; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer1\\X-Errors"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b1.X_Errors; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer1\\R-Frames"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b1.R_Frames; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer1\\R-Bytes"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b1.R_Bytes; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer1\\R-Errors"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b1.R_Errors; + + /* + B-Layer2 + */ + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer2\\X-Frames"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b2.X_Frames; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer2\\X-Bytes"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b2.X_Bytes; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer2\\X-Errors"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b2.X_Errors; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer2\\R-Frames"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b2.R_Frames; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer2\\R-Bytes"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b2.R_Bytes; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\B-Layer2\\R-Errors"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.b2.R_Errors; + + /* + D-Layer1 + */ + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer1\\X-Frames"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d1.X_Frames; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer1\\X-Bytes"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d1.X_Bytes; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer1\\X-Errors"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d1.X_Errors; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer1\\R-Frames"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d1.R_Frames; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer1\\R-Bytes"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d1.R_Bytes; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer1\\R-Errors"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d1.R_Errors; + + /* + D-Layer2 + */ + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer2\\X-Frames"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d2.X_Frames; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer2\\X-Bytes"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d2.X_Bytes; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer2\\X-Errors"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d2.X_Errors; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer2\\R-Frames"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d2.R_Frames; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer2\\R-Bytes"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d2.R_Bytes; + + strcpy(pLib->parse_table[pLib->cur_parse_entry].path, + "Statistics\\D-Layer2\\R-Errors"); + pLib->parse_table[pLib->cur_parse_entry++].variable = \ + &pLib->InterfaceStat.d2.R_Errors; + + + pLib->statistic_parse_last = pLib->cur_parse_entry - 1; +} + +static void diva_trace_error(diva_strace_context_t *pLib, + int error, const char *file, int line) { + if (pLib->user_proc_table.error_notify_proc) { + (*(pLib->user_proc_table.error_notify_proc))(\ + pLib->user_proc_table.user_context, + &pLib->instance, pLib->Adapter, + error, file, line); + } +} + +/* + Delivery notification to user +*/ +static void diva_trace_notify_user(diva_strace_context_t *pLib, + int Channel, + int notify_subject) { + if (pLib->user_proc_table.notify_proc) { + (*(pLib->user_proc_table.notify_proc))(pLib->user_proc_table.user_context, + &pLib->instance, + pLib->Adapter, + &pLib->lines[Channel], + notify_subject); + } +} + +/* + Read variable value to they destination based on the variable type +*/ +static int diva_trace_read_variable(diva_man_var_header_t *pVar, + void *variable) { + switch (pVar->type) { + case 0x03: /* MI_ASCIIZ - syting */ + return (diva_strace_read_asz(pVar, (char *)variable)); + case 0x04: /* MI_ASCII - string */ + return (diva_strace_read_asc(pVar, (char *)variable)); + case 0x05: /* MI_NUMBER - counted sequence of bytes */ + return (diva_strace_read_ie(pVar, (diva_trace_ie_t *)variable)); + case 0x81: /* MI_INT - signed integer */ + return (diva_strace_read_int(pVar, (int *)variable)); + case 0x82: /* MI_UINT - unsigned integer */ + return (diva_strace_read_uint(pVar, (dword *)variable)); + case 0x83: /* MI_HINT - unsigned integer, hex representetion */ + return (diva_strace_read_uint(pVar, (dword *)variable)); + case 0x87: /* MI_BITFLD - unsigned integer, bit representation */ + return (diva_strace_read_uint(pVar, (dword *)variable)); + } + + /* + This type of variable is not handled, indicate error + Or one problem in management interface, or in application recodeing + table, or this application should handle it. + */ + return (-1); +} + +/* + Read signed integer to destination +*/ +static int diva_strace_read_int(diva_man_var_header_t *pVar, int *var) { + byte *ptr = (char *)&pVar->path_length; + int value; + + ptr += (pVar->path_length + 1); + + switch (pVar->value_length) { + case 1: + value = *(char *)ptr; + break; + + case 2: + value = (short)GET_WORD(ptr); + break; + + case 4: + value = (int)GET_DWORD(ptr); + break; + + default: + return (-1); + } + + *var = value; + + return (0); +} + +static int diva_strace_read_uint(diva_man_var_header_t *pVar, dword *var) { + byte *ptr = (char *)&pVar->path_length; + dword value; + + ptr += (pVar->path_length + 1); + + switch (pVar->value_length) { + case 1: + value = (byte)(*ptr); + break; + + case 2: + value = (word)GET_WORD(ptr); + break; + + case 3: + value = (dword)GET_DWORD(ptr); + value &= 0x00ffffff; + break; + + case 4: + value = (dword)GET_DWORD(ptr); + break; + + default: + return (-1); + } + + *var = value; + + return (0); +} + +/* + Read zero terminated ASCII string +*/ +static int diva_strace_read_asz(diva_man_var_header_t *pVar, char *var) { + char *ptr = (char *)&pVar->path_length; + int length; + + ptr += (pVar->path_length + 1); + + if (!(length = pVar->value_length)) { + length = strlen(ptr); + } + memcpy(var, ptr, length); + var[length] = 0; + + return (0); +} + +/* + Read counted (with leading length byte) ASCII string +*/ +static int diva_strace_read_asc(diva_man_var_header_t *pVar, char *var) { + char *ptr = (char *)&pVar->path_length; + + ptr += (pVar->path_length + 1); + memcpy(var, ptr + 1, *ptr); + var[(int)*ptr] = 0; + + return (0); +} + +/* + Read one information element - i.e. one string of byte values with + one length byte in front +*/ +static int diva_strace_read_ie(diva_man_var_header_t *pVar, + diva_trace_ie_t *var) { + char *ptr = (char *)&pVar->path_length; + + ptr += (pVar->path_length + 1); + + var->length = *ptr; + memcpy(&var->data[0], ptr + 1, *ptr); + + return (0); +} + +static int SuperTraceSetAudioTap(void *hLib, int Channel, int on) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + + if ((Channel < 1) || (Channel > pLib->Channels)) { + return (-1); + } + Channel--; + + if (on) { + pLib->audio_tap_mask |= (1L << Channel); + } else { + pLib->audio_tap_mask &= ~(1L << Channel); + } + + /* + EYE patterns have TM_M_DATA set as additional + condition + */ + if (pLib->audio_tap_mask) { + pLib->trace_event_mask |= TM_M_DATA; + } else { + pLib->trace_event_mask &= ~TM_M_DATA; + } + + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceSetBChannel(void *hLib, int Channel, int on) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + + if ((Channel < 1) || (Channel > pLib->Channels)) { + return (-1); + } + Channel--; + + if (on) { + pLib->bchannel_trace_mask |= (1L << Channel); + } else { + pLib->bchannel_trace_mask &= ~(1L << Channel); + } + + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceSetDChannel(void *hLib, int on) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + + if (on) { + pLib->trace_event_mask |= (TM_D_CHAN | TM_C_COMM | TM_DL_ERR | TM_LAYER1); + } else { + pLib->trace_event_mask &= ~(TM_D_CHAN | TM_C_COMM | TM_DL_ERR | TM_LAYER1); + } + + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceSetInfo(void *hLib, int on) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + + if (on) { + pLib->trace_event_mask |= TM_STRING; + } else { + pLib->trace_event_mask &= ~TM_STRING; + } + + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceClearCall(void *hLib, int Channel) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + + if ((Channel < 1) || (Channel > pLib->Channels)) { + return (-1); + } + Channel--; + + pLib->clear_call_command |= (1L << Channel); + + return (ScheduleNextTraceRequest(pLib)); +} + +/* + Parse and update cumulative statistice +*/ +static int diva_ifc_statistics(diva_strace_context_t *pLib, + diva_man_var_header_t *pVar) { + diva_man_var_header_t *cur; + int i, one_updated = 0, mdm_updated = 0, fax_updated = 0; + + for (i = pLib->statistic_parse_first; i <= pLib->statistic_parse_last; i++) { + if ((cur = find_var(pVar, pLib->parse_table[i].path))) { + if (diva_trace_read_variable(cur, pLib->parse_table[i].variable)) { + diva_trace_error(pLib, -3 , __FILE__, __LINE__); + return (-1); + } + one_updated = 1; + if ((i >= pLib->mdm_statistic_parse_first) && (i <= pLib->mdm_statistic_parse_last)) { + mdm_updated = 1; + } + if ((i >= pLib->fax_statistic_parse_first) && (i <= pLib->fax_statistic_parse_last)) { + fax_updated = 1; + } + } + } + + /* + We do not use first event to notify user - this is the event that is + generated as result of EVENT ON operation and is used only to initialize + internal variables of application + */ + if (mdm_updated) { + diva_trace_notify_user(pLib, 0, DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE); + } else if (fax_updated) { + diva_trace_notify_user(pLib, 0, DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE); + } else if (one_updated) { + diva_trace_notify_user(pLib, 0, DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE); + } + + return (one_updated ? 0 : -1); +} + +static int SuperTraceGetOutgoingCallStatistics(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + pLib->outgoing_ifc_stats = 1; + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceGetIncomingCallStatistics(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + pLib->incoming_ifc_stats = 1; + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceGetModemStatistics(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + pLib->modem_ifc_stats = 1; + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceGetFaxStatistics(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + pLib->fax_ifc_stats = 1; + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceGetBLayer1Statistics(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + pLib->b1_ifc_stats = 1; + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceGetBLayer2Statistics(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + pLib->b2_ifc_stats = 1; + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceGetDLayer1Statistics(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + pLib->d1_ifc_stats = 1; + return (ScheduleNextTraceRequest(pLib)); +} + +static int SuperTraceGetDLayer2Statistics(void *hLib) { + diva_strace_context_t *pLib = (diva_strace_context_t *)hLib; + pLib->d2_ifc_stats = 1; + return (ScheduleNextTraceRequest(pLib)); +} + +dword DivaSTraceGetMemotyRequirement(int channels) { + dword parse_entries = (MODEM_PARSE_ENTRIES + FAX_PARSE_ENTRIES + \ + STAT_PARSE_ENTRIES + \ + LINE_PARSE_ENTRIES + 1) * channels; + return (sizeof(diva_strace_context_t) + \ + (parse_entries * sizeof(diva_strace_path2action_t))); +} diff --git a/drivers/isdn/hardware/eicon/maintidi.h b/drivers/isdn/hardware/eicon/maintidi.h new file mode 100644 index 000000000..2b46147c5 --- /dev/null +++ b/drivers/isdn/hardware/eicon/maintidi.h @@ -0,0 +1,171 @@ +/* + * + Copyright (c) Eicon Networks, 2000. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 1.9 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_EICON_TRACE_IDI_IFC_H__ +#define __DIVA_EICON_TRACE_IDI_IFC_H__ + +void *SuperTraceOpenAdapter(int AdapterNumber); +int SuperTraceCloseAdapter(void *AdapterHandle); +int SuperTraceWrite(void *AdapterHandle, + const void *data, int length); +int SuperTraceReadRequest(void *AdapterHandle, const char *name, byte *data); +int SuperTraceGetNumberOfChannels(void *AdapterHandle); +int SuperTraceASSIGN(void *AdapterHandle, byte *data); +int SuperTraceREMOVE(void *AdapterHandle); +int SuperTraceTraceOnRequest(void *hAdapter, const char *name, byte *data); +int SuperTraceWriteVar(void *AdapterHandle, + byte *data, + const char *name, + void *var, + byte type, + byte var_length); +int SuperTraceExecuteRequest(void *AdapterHandle, + const char *name, + byte *data); + +typedef struct _diva_strace_path2action { + char path[64]; /* Full path to variable */ + void *variable; /* Variable that will receive value */ +} diva_strace_path2action_t; + +#define DIVA_MAX_MANAGEMENT_TRANSFER_SIZE 4096 + +typedef struct _diva_strace_context { + diva_strace_library_interface_t instance; + + int Adapter; + void *hAdapter; + + int Channels; + int req_busy; + + ENTITY e; + IDI_CALL request; + BUFFERS XData; + BUFFERS RData; + byte buffer[DIVA_MAX_MANAGEMENT_TRANSFER_SIZE + 1]; + int removal_state; + int general_b_ch_event; + int general_fax_event; + int general_mdm_event; + + byte rc_ok; + + /* + Initialization request state machine + */ + int ChannelsTraceActive; + int ModemTraceActive; + int FaxTraceActive; + int IncomingCallsCallsActive; + int IncomingCallsConnectedActive; + int OutgoingCallsCallsActive; + int OutgoingCallsConnectedActive; + + int trace_mask_init; + int audio_trace_init; + int bchannel_init; + int trace_length_init; + int trace_on; + int trace_events_down; + int l1_trace; + int l2_trace; + + /* + Trace\Event Enable + */ + word trace_event_mask; + word current_trace_event_mask; + + dword audio_tap_mask; + dword current_audio_tap_mask; + dword current_eye_pattern_mask; + int audio_tap_pending; + int eye_pattern_pending; + + dword bchannel_trace_mask; + dword current_bchannel_trace_mask; + + + diva_trace_line_state_t lines[30]; + + int parse_entries; + int cur_parse_entry; + diva_strace_path2action_t *parse_table; + + diva_trace_library_user_interface_t user_proc_table; + + int line_parse_entry_first[30]; + int line_parse_entry_last[30]; + + int modem_parse_entry_first[30]; + int modem_parse_entry_last[30]; + + int fax_parse_entry_first[30]; + int fax_parse_entry_last[30]; + + int statistic_parse_first; + int statistic_parse_last; + + int mdm_statistic_parse_first; + int mdm_statistic_parse_last; + + int fax_statistic_parse_first; + int fax_statistic_parse_last; + + dword line_init_event; + dword modem_init_event; + dword fax_init_event; + + dword pending_line_status; + dword pending_modem_status; + dword pending_fax_status; + + dword clear_call_command; + + int outgoing_ifc_stats; + int incoming_ifc_stats; + int modem_ifc_stats; + int fax_ifc_stats; + int b1_ifc_stats; + int b2_ifc_stats; + int d1_ifc_stats; + int d2_ifc_stats; + + diva_trace_interface_state_t Interface; + diva_ifc_statistics_t InterfaceStat; +} diva_strace_context_t; + +typedef struct _diva_man_var_header { + byte escape; + byte length; + byte management_id; + byte type; + byte attribute; + byte status; + byte value_length; + byte path_length; +} diva_man_var_header_t; + +#endif diff --git a/drivers/isdn/hardware/eicon/man_defs.h b/drivers/isdn/hardware/eicon/man_defs.h new file mode 100644 index 000000000..249c47170 --- /dev/null +++ b/drivers/isdn/hardware/eicon/man_defs.h @@ -0,0 +1,133 @@ +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 1.9 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +/* Definitions for use with the Management Information Element */ + +/*------------------------------------------------------------------*/ +/* Management information element */ +/* ---------------------------------------------------------- */ +/* Byte Coding Comment */ +/* ---------------------------------------------------------- */ +/* 0 | 0 1 1 1 1 1 1 1 | ESC */ +/* 1 | 0 x x x x x x x | Length of information element (m-1) */ +/* 2 | 1 0 0 0 0 0 0 0 | Management Information Id */ +/* 3 | x x x x x x x x | Type */ +/* 4 | x x x x x x x x | Attribute */ +/* 5 | x x x x x x x x | Status */ +/* 6 | x x x x x x x x | Variable Value Length (m-n) */ +/* 7 | x x x x x x x x | Path / Variable Name String Length (n-8)*/ +/* 8..n | x x x x x x x x | Path/Node Name String separated by '\' */ +/* n..m | x x x x x x x x | Variable content */ +/*------------------------------------------------------------------*/ + +/*------------------------------------------------------------------*/ +/* Type Field */ +/* */ +/* MAN_READ: not used */ +/* MAN_WRITE: not used */ +/* MAN_EVENT_ON: not used */ +/* MAN_EVENT_OFF: not used */ +/* MAN_INFO_IND: type of variable */ +/* MAN_EVENT_IND: type of variable */ +/* MAN_TRACE_IND not used */ +/*------------------------------------------------------------------*/ +#define MI_DIR 0x01 /* Directory string (zero terminated) */ +#define MI_EXECUTE 0x02 /* Executable function (has no value) */ +#define MI_ASCIIZ 0x03 /* Zero terminated string */ +#define MI_ASCII 0x04 /* String, first byte is length */ +#define MI_NUMBER 0x05 /* Number string, first byte is length*/ +#define MI_TRACE 0x06 /* Trace information, format see below*/ + +#define MI_FIXED_LENGTH 0x80 /* get length from MAN_INFO max_len */ +#define MI_INT 0x81 /* number to display as signed int */ +#define MI_UINT 0x82 /* number to display as unsigned int */ +#define MI_HINT 0x83 /* number to display in hex format */ +#define MI_HSTR 0x84 /* number to display as a hex string */ +#define MI_BOOLEAN 0x85 /* number to display as boolean */ +#define MI_IP_ADDRESS 0x86 /* number to display as IP address */ +#define MI_BITFLD 0x87 /* number to display as bit field */ +#define MI_SPID_STATE 0x88 /* state# of SPID initialisation */ + +/*------------------------------------------------------------------*/ +/* Attribute Field */ +/* */ +/* MAN_READ: not used */ +/* MAN_WRITE: not used */ +/* MAN_EVENT_ON: not used */ +/* MAN_EVENT_OFF: not used */ +/* MAN_INFO_IND: set according to capabilities of that variable */ +/* MAN_EVENT_IND: not used */ +/* MAN_TRACE_IND not used */ +/*------------------------------------------------------------------*/ +#define MI_WRITE 0x01 /* Variable is writeable */ +#define MI_EVENT 0x02 /* Variable can indicate changes */ + +/*------------------------------------------------------------------*/ +/* Status Field */ +/* */ +/* MAN_READ: not used */ +/* MAN_WRITE: not used */ +/* MAN_EVENT_ON: not used */ +/* MAN_EVENT_OFF: not used */ +/* MAN_INFO_IND: set according to the actual status */ +/* MAN_EVENT_IND: set according to the actual statu */ +/* MAN_TRACE_IND not used */ +/*------------------------------------------------------------------*/ +#define MI_LOCKED 0x01 /* write protected by another instance*/ +#define MI_EVENT_ON 0x02 /* Event logging switched on */ +#define MI_PROTECTED 0x04 /* write protected by this instance */ + +/*------------------------------------------------------------------*/ +/* Data Format used for MAN_TRACE_IND (no MI-element used) */ +/*------------------------------------------------------------------*/ +typedef struct mi_xlog_hdr_s MI_XLOG_HDR; +struct mi_xlog_hdr_s +{ + unsigned long time; /* Timestamp in msec units */ + unsigned short size; /* Size of data that follows */ + unsigned short code; /* code of trace event */ +}; /* unspecified data follows this header */ + +/*------------------------------------------------------------------*/ +/* Trace mask definitions for trace events except B channel and */ +/* debug trace events */ +/*------------------------------------------------------------------*/ +#define TM_D_CHAN 0x0001 /* D-Channel (D-.) Code 3,4 */ +#define TM_L_LAYER 0x0002 /* Low Layer (LL) Code 6,7 */ +#define TM_N_LAYER 0x0004 /* Network Layer (N) Code 14,15 */ +#define TM_DL_ERR 0x0008 /* Data Link Error (MDL) Code 9 */ +#define TM_LAYER1 0x0010 /* Layer 1 Code 20 */ +#define TM_C_COMM 0x0020 /* Call Comment (SIG) Code 5,21,22 */ +#define TM_M_DATA 0x0040 /* Modulation Data (EYE) Code 23 */ +#define TM_STRING 0x0080 /* Sting data Code 24 */ +#define TM_N_USED2 0x0100 /* not used */ +#define TM_N_USED3 0x0200 /* not used */ +#define TM_N_USED4 0x0400 /* not used */ +#define TM_N_USED5 0x0800 /* not used */ +#define TM_N_USED6 0x1000 /* not used */ +#define TM_N_USED7 0x2000 /* not used */ +#define TM_N_USED8 0x4000 /* not used */ +#define TM_REST 0x8000 /* Codes 10,11,12,13,16,18,19,128,129 */ + +/*------ End of file -----------------------------------------------*/ diff --git a/drivers/isdn/hardware/eicon/mdm_msg.h b/drivers/isdn/hardware/eicon/mdm_msg.h new file mode 100644 index 000000000..0e6b2e009 --- /dev/null +++ b/drivers/isdn/hardware/eicon/mdm_msg.h @@ -0,0 +1,346 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __EICON_MDM_MSG_H__ +#define __EICON_MDM_MSG_H__ +#define DSP_UDATA_INDICATION_DCD_OFF 0x01 +#define DSP_UDATA_INDICATION_DCD_ON 0x02 +#define DSP_UDATA_INDICATION_CTS_OFF 0x03 +#define DSP_UDATA_INDICATION_CTS_ON 0x04 +/* ===================================================================== + DCD_OFF Message: + <word> time of DCD off (sampled from counter at 8kHz) + DCD_ON Message: + <word> time of DCD on (sampled from counter at 8kHz) + <byte> connected norm + <word> connected options + <dword> connected speed (bit/s, max of tx and rx speed) + <word> roundtrip delay (ms) + <dword> connected speed tx (bit/s) + <dword> connected speed rx (bit/s) + Size of this message == 19 bytes, but we will receive only 11 + ===================================================================== */ +#define DSP_CONNECTED_NORM_UNSPECIFIED 0 +#define DSP_CONNECTED_NORM_V21 1 +#define DSP_CONNECTED_NORM_V23 2 +#define DSP_CONNECTED_NORM_V22 3 +#define DSP_CONNECTED_NORM_V22_BIS 4 +#define DSP_CONNECTED_NORM_V32_BIS 5 +#define DSP_CONNECTED_NORM_V34 6 +#define DSP_CONNECTED_NORM_V8 7 +#define DSP_CONNECTED_NORM_BELL_212A 8 +#define DSP_CONNECTED_NORM_BELL_103 9 +#define DSP_CONNECTED_NORM_V29_LEASED_LINE 10 +#define DSP_CONNECTED_NORM_V33_LEASED_LINE 11 +#define DSP_CONNECTED_NORM_V90 12 +#define DSP_CONNECTED_NORM_V21_CH2 13 +#define DSP_CONNECTED_NORM_V27_TER 14 +#define DSP_CONNECTED_NORM_V29 15 +#define DSP_CONNECTED_NORM_V33 16 +#define DSP_CONNECTED_NORM_V17 17 +#define DSP_CONNECTED_NORM_V32 18 +#define DSP_CONNECTED_NORM_K56_FLEX 19 +#define DSP_CONNECTED_NORM_X2 20 +#define DSP_CONNECTED_NORM_V18 21 +#define DSP_CONNECTED_NORM_V18_LOW_HIGH 22 +#define DSP_CONNECTED_NORM_V18_HIGH_LOW 23 +#define DSP_CONNECTED_NORM_V21_LOW_HIGH 24 +#define DSP_CONNECTED_NORM_V21_HIGH_LOW 25 +#define DSP_CONNECTED_NORM_BELL103_LOW_HIGH 26 +#define DSP_CONNECTED_NORM_BELL103_HIGH_LOW 27 +#define DSP_CONNECTED_NORM_V23_75_1200 28 +#define DSP_CONNECTED_NORM_V23_1200_75 29 +#define DSP_CONNECTED_NORM_EDT_110 30 +#define DSP_CONNECTED_NORM_BAUDOT_45 31 +#define DSP_CONNECTED_NORM_BAUDOT_47 32 +#define DSP_CONNECTED_NORM_BAUDOT_50 33 +#define DSP_CONNECTED_NORM_DTMF 34 +#define DSP_CONNECTED_NORM_V18_RESERVED_13 35 +#define DSP_CONNECTED_NORM_V18_RESERVED_14 36 +#define DSP_CONNECTED_NORM_V18_RESERVED_15 37 +#define DSP_CONNECTED_NORM_VOWN 38 +#define DSP_CONNECTED_NORM_V23_OFF_HOOK 39 +#define DSP_CONNECTED_NORM_V23_ON_HOOK 40 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_3 41 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_4 42 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_5 43 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_6 44 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_7 45 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_8 46 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_9 47 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_10 48 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_11 49 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_12 50 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_13 51 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_14 52 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_15 53 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_16 54 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_17 55 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_18 56 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_19 57 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_20 58 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_21 59 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_22 60 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_23 61 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_24 62 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_25 63 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_26 64 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_27 65 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_28 66 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_29 67 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_30 68 +#define DSP_CONNECTED_NORM_VOWN_RESERVED_31 69 +#define DSP_CONNECTED_OPTION_TRELLIS 0x0001 +#define DSP_CONNECTED_OPTION_V42_TRANS 0x0002 +#define DSP_CONNECTED_OPTION_V42_LAPM 0x0004 +#define DSP_CONNECTED_OPTION_SHORT_TRAIN 0x0008 +#define DSP_CONNECTED_OPTION_TALKER_ECHO_PROTECT 0x0010 +#define DSP_CONNECTED_OPTION_V42BIS 0x0020 +#define DSP_CONNECTED_OPTION_MNP2 0x0040 +#define DSP_CONNECTED_OPTION_MNP3 0x0080 +#define DSP_CONNECTED_OPTION_MNP4 0x00c0 +#define DSP_CONNECTED_OPTION_MNP5 0x0100 +#define DSP_CONNECTED_OPTION_MNP10 0x0200 +#define DSP_CONNECTED_OPTION_MASK_V42 0x0024 +#define DSP_CONNECTED_OPTION_MASK_MNP 0x03c0 +#define DSP_CONNECTED_OPTION_MASK_ERROR_CORRECT 0x03e4 +#define DSP_CONNECTED_OPTION_MASK_COMPRESSION 0x0320 +#define DSP_UDATA_INDICATION_DISCONNECT 5 +/* + returns: + <byte> cause +*/ +/* ========================================================== + DLC: B2 modem configuration + ========================================================== */ +/* + Fields in assign DLC information element for modem protocol V.42/MNP: + <byte> length of information element + <word> information field length + <byte> address A (not used, default 3) + <byte> address B (not used, default 1) + <byte> modulo mode (not used, default 7) + <byte> window size (not used, default 7) + <word> XID length (not used, default 0) + ... XID information (not used, default empty) + <byte> modem protocol negotiation options + <byte> modem protocol options + <byte> modem protocol break configuration + <byte> modem protocol application options +*/ +#define DLC_MODEMPROT_DISABLE_V42_V42BIS 0x01 +#define DLC_MODEMPROT_DISABLE_MNP_MNP5 0x02 +#define DLC_MODEMPROT_REQUIRE_PROTOCOL 0x04 +#define DLC_MODEMPROT_DISABLE_V42_DETECT 0x08 +#define DLC_MODEMPROT_DISABLE_COMPRESSION 0x10 +#define DLC_MODEMPROT_REQUIRE_PROTOCOL_V34UP 0x20 +#define DLC_MODEMPROT_NO_PROTOCOL_IF_1200 0x01 +#define DLC_MODEMPROT_BUFFER_IN_V42_DETECT 0x02 +#define DLC_MODEMPROT_DISABLE_V42_SREJ 0x04 +#define DLC_MODEMPROT_DISABLE_MNP3 0x08 +#define DLC_MODEMPROT_DISABLE_MNP4 0x10 +#define DLC_MODEMPROT_DISABLE_MNP10 0x20 +#define DLC_MODEMPROT_NO_PROTOCOL_IF_V22BIS 0x40 +#define DLC_MODEMPROT_NO_PROTOCOL_IF_V32BIS 0x80 +#define DLC_MODEMPROT_BREAK_DISABLED 0x00 +#define DLC_MODEMPROT_BREAK_NORMAL 0x01 +#define DLC_MODEMPROT_BREAK_EXPEDITED 0x02 +#define DLC_MODEMPROT_BREAK_DESTRUCTIVE 0x03 +#define DLC_MODEMPROT_BREAK_CONFIG_MASK 0x03 +#define DLC_MODEMPROT_APPL_EARLY_CONNECT 0x01 +#define DLC_MODEMPROT_APPL_PASS_INDICATIONS 0x02 +/* ========================================================== + CAI parameters used for the modem L1 configuration + ========================================================== */ +/* + Fields in assign CAI information element: + <byte> length of information element + <byte> info field and B-channel hardware + <byte> rate adaptation bit rate + <byte> async framing parameters + <byte> reserved + <word> packet length + <byte> modem line taking options + <byte> modem modulation negotiation parameters + <byte> modem modulation options + <byte> modem disabled modulations mask low + <byte> modem disabled modulations mask high + <byte> modem enabled modulations mask + <word> modem min TX speed + <word> modem max TX speed + <word> modem min RX speed + <word> modem max RX speed + <byte> modem disabled symbol rates mask + <byte> modem info options mask + <byte> modem transmit level adjust + <byte> modem speaker parameters + <word> modem private debug config + <struct> modem reserved + <struct> v18 config parameters + <struct> v18 probing sequence + <struct> v18 probing message +*/ +#define DSP_CAI_HARDWARE_HDLC_64K 0x05 +#define DSP_CAI_HARDWARE_HDLC_56K 0x08 +#define DSP_CAI_HARDWARE_TRANSP 0x09 +#define DSP_CAI_HARDWARE_V110_SYNC 0x0c +#define DSP_CAI_HARDWARE_V110_ASYNC 0x0d +#define DSP_CAI_HARDWARE_HDLC_128K 0x0f +#define DSP_CAI_HARDWARE_FAX 0x10 +#define DSP_CAI_HARDWARE_MODEM_ASYNC 0x11 +#define DSP_CAI_HARDWARE_MODEM_SYNC 0x12 +#define DSP_CAI_HARDWARE_V110_HDLCA 0x13 +#define DSP_CAI_HARDWARE_ADVANCED_VOICE 0x14 +#define DSP_CAI_HARDWARE_TRANSP_DTMF 0x16 +#define DSP_CAI_HARDWARE_DTMF_VOICE_ISDN 0x17 +#define DSP_CAI_HARDWARE_DTMF_VOICE_LOCAL 0x18 +#define DSP_CAI_HARDWARE_MASK 0x3f +#define DSP_CAI_ENABLE_INFO_INDICATIONS 0x80 +#define DSP_CAI_RATE_ADAPTATION_300 0x00 +#define DSP_CAI_RATE_ADAPTATION_600 0x01 +#define DSP_CAI_RATE_ADAPTATION_1200 0x02 +#define DSP_CAI_RATE_ADAPTATION_2400 0x03 +#define DSP_CAI_RATE_ADAPTATION_4800 0x04 +#define DSP_CAI_RATE_ADAPTATION_9600 0x05 +#define DSP_CAI_RATE_ADAPTATION_19200 0x06 +#define DSP_CAI_RATE_ADAPTATION_38400 0x07 +#define DSP_CAI_RATE_ADAPTATION_48000 0x08 +#define DSP_CAI_RATE_ADAPTATION_56000 0x09 +#define DSP_CAI_RATE_ADAPTATION_7200 0x0a +#define DSP_CAI_RATE_ADAPTATION_14400 0x0b +#define DSP_CAI_RATE_ADAPTATION_28800 0x0c +#define DSP_CAI_RATE_ADAPTATION_12000 0x0d +#define DSP_CAI_RATE_ADAPTATION_1200_75 0x0e +#define DSP_CAI_RATE_ADAPTATION_75_1200 0x0f +#define DSP_CAI_RATE_ADAPTATION_MASK 0x0f +#define DSP_CAI_ASYNC_PARITY_ENABLE 0x01 +#define DSP_CAI_ASYNC_PARITY_SPACE 0x00 +#define DSP_CAI_ASYNC_PARITY_ODD 0x02 +#define DSP_CAI_ASYNC_PARITY_EVEN 0x04 +#define DSP_CAI_ASYNC_PARITY_MARK 0x06 +#define DSP_CAI_ASYNC_PARITY_MASK 0x06 +#define DSP_CAI_ASYNC_ONE_STOP_BIT 0x00 +#define DSP_CAI_ASYNC_TWO_STOP_BITS 0x20 +#define DSP_CAI_ASYNC_CHAR_LENGTH_8 0x00 +#define DSP_CAI_ASYNC_CHAR_LENGTH_7 0x40 +#define DSP_CAI_ASYNC_CHAR_LENGTH_6 0x80 +#define DSP_CAI_ASYNC_CHAR_LENGTH_5 0xc0 +#define DSP_CAI_ASYNC_CHAR_LENGTH_MASK 0xc0 +#define DSP_CAI_MODEM_LEASED_LINE_MODE 0x01 +#define DSP_CAI_MODEM_4_WIRE_OPERATION 0x02 +#define DSP_CAI_MODEM_DISABLE_BUSY_DETECT 0x04 +#define DSP_CAI_MODEM_DISABLE_CALLING_TONE 0x08 +#define DSP_CAI_MODEM_DISABLE_ANSWER_TONE 0x10 +#define DSP_CAI_MODEM_ENABLE_DIAL_TONE_DET 0x20 +#define DSP_CAI_MODEM_USE_POTS_INTERFACE 0x40 +#define DSP_CAI_MODEM_FORCE_RAY_TAYLOR_FAX 0x80 +#define DSP_CAI_MODEM_NEGOTIATE_HIGHEST 0x00 +#define DSP_CAI_MODEM_NEGOTIATE_DISABLED 0x01 +#define DSP_CAI_MODEM_NEGOTIATE_IN_CLASS 0x02 +#define DSP_CAI_MODEM_NEGOTIATE_V100 0x03 +#define DSP_CAI_MODEM_NEGOTIATE_V8 0x04 +#define DSP_CAI_MODEM_NEGOTIATE_V8BIS 0x05 +#define DSP_CAI_MODEM_NEGOTIATE_MASK 0x07 +#define DSP_CAI_MODEM_GUARD_TONE_NONE 0x00 +#define DSP_CAI_MODEM_GUARD_TONE_550HZ 0x40 +#define DSP_CAI_MODEM_GUARD_TONE_1800HZ 0x80 +#define DSP_CAI_MODEM_GUARD_TONE_MASK 0xc0 +#define DSP_CAI_MODEM_DISABLE_RETRAIN 0x01 +#define DSP_CAI_MODEM_DISABLE_STEPUPDOWN 0x02 +#define DSP_CAI_MODEM_DISABLE_SPLIT_SPEED 0x04 +#define DSP_CAI_MODEM_DISABLE_TRELLIS 0x08 +#define DSP_CAI_MODEM_ALLOW_RDL_TEST_LOOP 0x10 +#define DSP_CAI_MODEM_DISABLE_FLUSH_TIMER 0x40 +#define DSP_CAI_MODEM_REVERSE_DIRECTION 0x80 +#define DSP_CAI_MODEM_DISABLE_V21 0x01 +#define DSP_CAI_MODEM_DISABLE_V23 0x02 +#define DSP_CAI_MODEM_DISABLE_V22 0x04 +#define DSP_CAI_MODEM_DISABLE_V22BIS 0x08 +#define DSP_CAI_MODEM_DISABLE_V32 0x10 +#define DSP_CAI_MODEM_DISABLE_V32BIS 0x20 +#define DSP_CAI_MODEM_DISABLE_V34 0x40 +#define DSP_CAI_MODEM_DISABLE_V90 0x80 +#define DSP_CAI_MODEM_DISABLE_BELL103 0x01 +#define DSP_CAI_MODEM_DISABLE_BELL212A 0x02 +#define DSP_CAI_MODEM_DISABLE_VFC 0x04 +#define DSP_CAI_MODEM_DISABLE_K56FLEX 0x08 +#define DSP_CAI_MODEM_DISABLE_X2 0x10 +#define DSP_CAI_MODEM_ENABLE_V29FDX 0x01 +#define DSP_CAI_MODEM_ENABLE_V33 0x02 +#define DSP_CAI_MODEM_DISABLE_2400_SYMBOLS 0x01 +#define DSP_CAI_MODEM_DISABLE_2743_SYMBOLS 0x02 +#define DSP_CAI_MODEM_DISABLE_2800_SYMBOLS 0x04 +#define DSP_CAI_MODEM_DISABLE_3000_SYMBOLS 0x08 +#define DSP_CAI_MODEM_DISABLE_3200_SYMBOLS 0x10 +#define DSP_CAI_MODEM_DISABLE_3429_SYMBOLS 0x20 +#define DSP_CAI_MODEM_DISABLE_TX_REDUCTION 0x01 +#define DSP_CAI_MODEM_DISABLE_PRECODING 0x02 +#define DSP_CAI_MODEM_DISABLE_PREEMPHASIS 0x04 +#define DSP_CAI_MODEM_DISABLE_SHAPING 0x08 +#define DSP_CAI_MODEM_DISABLE_NONLINEAR_EN 0x10 +#define DSP_CAI_MODEM_SPEAKER_OFF 0x00 +#define DSP_CAI_MODEM_SPEAKER_DURING_TRAIN 0x01 +#define DSP_CAI_MODEM_SPEAKER_TIL_CONNECT 0x02 +#define DSP_CAI_MODEM_SPEAKER_ALWAYS_ON 0x03 +#define DSP_CAI_MODEM_SPEAKER_CONTROL_MASK 0x03 +#define DSP_CAI_MODEM_SPEAKER_VOLUME_MIN 0x00 +#define DSP_CAI_MODEM_SPEAKER_VOLUME_LOW 0x04 +#define DSP_CAI_MODEM_SPEAKER_VOLUME_HIGH 0x08 +#define DSP_CAI_MODEM_SPEAKER_VOLUME_MAX 0x0c +#define DSP_CAI_MODEM_SPEAKER_VOLUME_MASK 0x0c +/* ========================================================== + DCD/CTS State + ========================================================== */ +#define MDM_WANT_CONNECT_B3_ACTIVE_I 0x01 +#define MDM_NCPI_VALID 0x02 +#define MDM_NCPI_CTS_ON_RECEIVED 0x04 +#define MDM_NCPI_DCD_ON_RECEIVED 0x08 +/* ========================================================== + CAPI NCPI Constants + ========================================================== */ +#define MDM_NCPI_ECM_V42 0x0001 +#define MDM_NCPI_ECM_MNP 0x0002 +#define MDM_NCPI_TRANSPARENT 0x0004 +#define MDM_NCPI_COMPRESSED 0x0010 +/* ========================================================== + CAPI B2 Config Constants + ========================================================== */ +#define MDM_B2_DISABLE_V42bis 0x0001 +#define MDM_B2_DISABLE_MNP 0x0002 +#define MDM_B2_DISABLE_TRANS 0x0004 +#define MDM_B2_DISABLE_V42 0x0008 +#define MDM_B2_DISABLE_COMP 0x0010 +/* ========================================================== + CAPI B1 Config Constants + ========================================================== */ +#define MDM_CAPI_DISABLE_RETRAIN 0x0001 +#define MDM_CAPI_DISABLE_RING_TONE 0x0002 +#define MDM_CAPI_GUARD_1800 0x0004 +#define MDM_CAPI_GUARD_550 0x0008 +#define MDM_CAPI_NEG_V8 0x0003 +#define MDM_CAPI_NEG_V100 0x0002 +#define MDM_CAPI_NEG_MOD_CLASS 0x0001 +#define MDM_CAPI_NEG_DISABLED 0x0000 +#endif diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c new file mode 100644 index 000000000..def7992a3 --- /dev/null +++ b/drivers/isdn/hardware/eicon/message.c @@ -0,0 +1,14954 @@ +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/bitmap.h> + +#include "platform.h" +#include "di_defs.h" +#include "pc.h" +#include "capi20.h" +#include "divacapi.h" +#include "mdm_msg.h" +#include "divasync.h" + +#define FILE_ "MESSAGE.C" +#define dprintf + +/*------------------------------------------------------------------*/ +/* This is options supported for all adapters that are server by */ +/* XDI driver. Allo it is not necessary to ask it from every adapter*/ +/* and it is not necessary to save it separate for every adapter */ +/* Macrose defined here have only local meaning */ +/*------------------------------------------------------------------*/ +static dword diva_xdi_extended_features = 0; + +#define DIVA_CAPI_USE_CMA 0x00000001 +#define DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR 0x00000002 +#define DIVA_CAPI_XDI_PROVIDES_NO_CANCEL 0x00000004 +#define DIVA_CAPI_XDI_PROVIDES_RX_DMA 0x00000008 + +/* + CAPI can request to process all return codes self only if: + protocol code supports this && xdi supports this +*/ +#define DIVA_CAPI_SUPPORTS_NO_CANCEL(__a__) (((__a__)->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL) && ((__a__)->manufacturer_features & MANUFACTURER_FEATURE_OK_FC_LABEL) && (diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_NO_CANCEL)) + +/*------------------------------------------------------------------*/ +/* local function prototypes */ +/*------------------------------------------------------------------*/ + +static void group_optimization(DIVA_CAPI_ADAPTER *a, PLCI *plci); +void AutomaticLaw(DIVA_CAPI_ADAPTER *); +word CapiRelease(word); +word CapiRegister(word); +word api_put(APPL *, CAPI_MSG *); +static word api_parse(byte *, word, byte *, API_PARSE *); +static void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out); +static void api_load_msg(API_SAVE *in, API_PARSE *out); + +word api_remove_start(void); +void api_remove_complete(void); + +static void plci_remove(PLCI *); +static void diva_get_extended_adapter_features(DIVA_CAPI_ADAPTER *a); +static void diva_ask_for_xdi_sdram_bar(DIVA_CAPI_ADAPTER *, IDI_SYNC_REQ *); + +void callback(ENTITY *); + +static void control_rc(PLCI *, byte, byte, byte, byte, byte); +static void data_rc(PLCI *, byte); +static void data_ack(PLCI *, byte); +static void sig_ind(PLCI *); +static void SendInfo(PLCI *, dword, byte **, byte); +static void SendSetupInfo(APPL *, PLCI *, dword, byte **, byte); +static void SendSSExtInd(APPL *, PLCI *plci, dword Id, byte **parms); + +static void VSwitchReqInd(PLCI *plci, dword Id, byte **parms); + +static void nl_ind(PLCI *); + +static byte connect_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte connect_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte connect_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte disconnect_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte disconnect_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte listen_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte info_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte info_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte alert_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte facility_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte facility_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte connect_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte connect_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte connect_b3_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte disconnect_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte disconnect_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte data_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte data_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte reset_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte reset_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte connect_b3_t90_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte select_b_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte manufacturer_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +static byte manufacturer_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); + +static word get_plci(DIVA_CAPI_ADAPTER *); +static void add_p(PLCI *, byte, byte *); +static void add_s(PLCI *plci, byte code, API_PARSE *p); +static void add_ss(PLCI *plci, byte code, API_PARSE *p); +static void add_ie(PLCI *plci, byte code, byte *p, word p_length); +static void add_d(PLCI *, word, byte *); +static void add_ai(PLCI *, API_PARSE *); +static word add_b1(PLCI *, API_PARSE *, word, word); +static word add_b23(PLCI *, API_PARSE *); +static word add_modem_b23(PLCI *plci, API_PARSE *bp_parms); +static void sig_req(PLCI *, byte, byte); +static void nl_req_ncci(PLCI *, byte, byte); +static void send_req(PLCI *); +static void send_data(PLCI *); +static word plci_remove_check(PLCI *); +static void listen_check(DIVA_CAPI_ADAPTER *); +static byte AddInfo(byte **, byte **, byte *, byte *); +static byte getChannel(API_PARSE *); +static void IndParse(PLCI *, const word *, byte **, byte); +static byte ie_compare(byte *, byte *); +static word find_cip(DIVA_CAPI_ADAPTER *, byte *, byte *); +static word CPN_filter_ok(byte *cpn, DIVA_CAPI_ADAPTER *, word); + +/* + XON protocol helpers +*/ +static void channel_flow_control_remove(PLCI *plci); +static void channel_x_off(PLCI *plci, byte ch, byte flag); +static void channel_x_on(PLCI *plci, byte ch); +static void channel_request_xon(PLCI *plci, byte ch); +static void channel_xmit_xon(PLCI *plci); +static int channel_can_xon(PLCI *plci, byte ch); +static void channel_xmit_extended_xon(PLCI *plci); + +static byte SendMultiIE(PLCI *plci, dword Id, byte **parms, byte ie_type, dword info_mask, byte setupParse); +static word AdvCodecSupport(DIVA_CAPI_ADAPTER *, PLCI *, APPL *, byte); +static void CodecIdCheck(DIVA_CAPI_ADAPTER *, PLCI *); +static void SetVoiceChannel(PLCI *, byte *, DIVA_CAPI_ADAPTER *); +static void VoiceChannelOff(PLCI *plci); +static void adv_voice_write_coefs(PLCI *plci, word write_command); +static void adv_voice_clear_config(PLCI *plci); + +static word get_b1_facilities(PLCI *plci, byte b1_resource); +static byte add_b1_facilities(PLCI *plci, byte b1_resource, word b1_facilities); +static void adjust_b1_facilities(PLCI *plci, byte new_b1_resource, word new_b1_facilities); +static word adjust_b_process(dword Id, PLCI *plci, byte Rc); +static void adjust_b1_resource(dword Id, PLCI *plci, API_SAVE *bp_msg, word b1_facilities, word internal_command); +static void adjust_b_restore(dword Id, PLCI *plci, byte Rc); +static void reset_b3_command(dword Id, PLCI *plci, byte Rc); +static void select_b_command(dword Id, PLCI *plci, byte Rc); +static void fax_connect_ack_command(dword Id, PLCI *plci, byte Rc); +static void fax_edata_ack_command(dword Id, PLCI *plci, byte Rc); +static void fax_connect_info_command(dword Id, PLCI *plci, byte Rc); +static void fax_adjust_b23_command(dword Id, PLCI *plci, byte Rc); +static void fax_disconnect_command(dword Id, PLCI *plci, byte Rc); +static void hold_save_command(dword Id, PLCI *plci, byte Rc); +static void retrieve_restore_command(dword Id, PLCI *plci, byte Rc); +static void init_b1_config(PLCI *plci); +static void clear_b1_config(PLCI *plci); + +static void dtmf_command(dword Id, PLCI *plci, byte Rc); +static byte dtmf_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg); +static void dtmf_confirmation(dword Id, PLCI *plci); +static void dtmf_indication(dword Id, PLCI *plci, byte *msg, word length); +static void dtmf_parameter_write(PLCI *plci); + + +static void mixer_set_bchannel_id_esc(PLCI *plci, byte bchannel_id); +static void mixer_set_bchannel_id(PLCI *plci, byte *chi); +static void mixer_clear_config(PLCI *plci); +static void mixer_notify_update(PLCI *plci, byte others); +static void mixer_command(dword Id, PLCI *plci, byte Rc); +static byte mixer_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg); +static void mixer_indication_coefs_set(dword Id, PLCI *plci); +static void mixer_indication_xconnect_from(dword Id, PLCI *plci, byte *msg, word length); +static void mixer_indication_xconnect_to(dword Id, PLCI *plci, byte *msg, word length); +static void mixer_remove(PLCI *plci); + + +static void ec_command(dword Id, PLCI *plci, byte Rc); +static byte ec_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg); +static void ec_indication(dword Id, PLCI *plci, byte *msg, word length); + + +static void rtp_connect_b3_req_command(dword Id, PLCI *plci, byte Rc); +static void rtp_connect_b3_res_command(dword Id, PLCI *plci, byte Rc); + + +static int diva_get_dma_descriptor(PLCI *plci, dword *dma_magic); +static void diva_free_dma_descriptor(PLCI *plci, int nr); + +/*------------------------------------------------------------------*/ +/* external function prototypes */ +/*------------------------------------------------------------------*/ + +extern byte MapController(byte); +extern byte UnMapController(byte); +#define MapId(Id)(((Id) & 0xffffff00L) | MapController((byte)(Id))) +#define UnMapId(Id)(((Id) & 0xffffff00L) | UnMapController((byte)(Id))) + +void sendf(APPL *, word, dword, word, byte *, ...); +void *TransmitBufferSet(APPL *appl, dword ref); +void *TransmitBufferGet(APPL *appl, void *p); +void TransmitBufferFree(APPL *appl, void *p); +void *ReceiveBufferGet(APPL *appl, int Num); + +int fax_head_line_time(char *buffer); + + +/*------------------------------------------------------------------*/ +/* Global data definitions */ +/*------------------------------------------------------------------*/ +extern byte max_adapter; +extern byte max_appl; +extern DIVA_CAPI_ADAPTER *adapter; +extern APPL *application; + + + + + + + +static byte remove_started = false; +static PLCI dummy_plci; + + +static struct _ftable { + word command; + byte *format; + byte (*function)(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *); +} ftable[] = { + {_DATA_B3_R, "dwww", data_b3_req}, + {_DATA_B3_I | RESPONSE, "w", data_b3_res}, + {_INFO_R, "ss", info_req}, + {_INFO_I | RESPONSE, "", info_res}, + {_CONNECT_R, "wsssssssss", connect_req}, + {_CONNECT_I | RESPONSE, "wsssss", connect_res}, + {_CONNECT_ACTIVE_I | RESPONSE, "", connect_a_res}, + {_DISCONNECT_R, "s", disconnect_req}, + {_DISCONNECT_I | RESPONSE, "", disconnect_res}, + {_LISTEN_R, "dddss", listen_req}, + {_ALERT_R, "s", alert_req}, + {_FACILITY_R, "ws", facility_req}, + {_FACILITY_I | RESPONSE, "ws", facility_res}, + {_CONNECT_B3_R, "s", connect_b3_req}, + {_CONNECT_B3_I | RESPONSE, "ws", connect_b3_res}, + {_CONNECT_B3_ACTIVE_I | RESPONSE, "", connect_b3_a_res}, + {_DISCONNECT_B3_R, "s", disconnect_b3_req}, + {_DISCONNECT_B3_I | RESPONSE, "", disconnect_b3_res}, + {_RESET_B3_R, "s", reset_b3_req}, + {_RESET_B3_I | RESPONSE, "", reset_b3_res}, + {_CONNECT_B3_T90_ACTIVE_I | RESPONSE, "ws", connect_b3_t90_a_res}, + {_CONNECT_B3_T90_ACTIVE_I | RESPONSE, "", connect_b3_t90_a_res}, + {_SELECT_B_REQ, "s", select_b_req}, + {_MANUFACTURER_R, "dws", manufacturer_req}, + {_MANUFACTURER_I | RESPONSE, "dws", manufacturer_res}, + {_MANUFACTURER_I | RESPONSE, "", manufacturer_res} +}; + +static byte *cip_bc[29][2] = { + { "", "" }, /* 0 */ + { "\x03\x80\x90\xa3", "\x03\x80\x90\xa2" }, /* 1 */ + { "\x02\x88\x90", "\x02\x88\x90" }, /* 2 */ + { "\x02\x89\x90", "\x02\x89\x90" }, /* 3 */ + { "\x03\x90\x90\xa3", "\x03\x90\x90\xa2" }, /* 4 */ + { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 5 */ + { "\x02\x98\x90", "\x02\x98\x90" }, /* 6 */ + { "\x04\x88\xc0\xc6\xe6", "\x04\x88\xc0\xc6\xe6" }, /* 7 */ + { "\x04\x88\x90\x21\x8f", "\x04\x88\x90\x21\x8f" }, /* 8 */ + { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 9 */ + { "", "" }, /* 10 */ + { "", "" }, /* 11 */ + { "", "" }, /* 12 */ + { "", "" }, /* 13 */ + { "", "" }, /* 14 */ + { "", "" }, /* 15 */ + + { "\x03\x80\x90\xa3", "\x03\x80\x90\xa2" }, /* 16 */ + { "\x03\x90\x90\xa3", "\x03\x90\x90\xa2" }, /* 17 */ + { "\x02\x88\x90", "\x02\x88\x90" }, /* 18 */ + { "\x02\x88\x90", "\x02\x88\x90" }, /* 19 */ + { "\x02\x88\x90", "\x02\x88\x90" }, /* 20 */ + { "\x02\x88\x90", "\x02\x88\x90" }, /* 21 */ + { "\x02\x88\x90", "\x02\x88\x90" }, /* 22 */ + { "\x02\x88\x90", "\x02\x88\x90" }, /* 23 */ + { "\x02\x88\x90", "\x02\x88\x90" }, /* 24 */ + { "\x02\x88\x90", "\x02\x88\x90" }, /* 25 */ + { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 26 */ + { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 27 */ + { "\x02\x88\x90", "\x02\x88\x90" } /* 28 */ +}; + +static byte *cip_hlc[29] = { + "", /* 0 */ + "", /* 1 */ + "", /* 2 */ + "", /* 3 */ + "", /* 4 */ + "", /* 5 */ + "", /* 6 */ + "", /* 7 */ + "", /* 8 */ + "", /* 9 */ + "", /* 10 */ + "", /* 11 */ + "", /* 12 */ + "", /* 13 */ + "", /* 14 */ + "", /* 15 */ + + "\x02\x91\x81", /* 16 */ + "\x02\x91\x84", /* 17 */ + "\x02\x91\xa1", /* 18 */ + "\x02\x91\xa4", /* 19 */ + "\x02\x91\xa8", /* 20 */ + "\x02\x91\xb1", /* 21 */ + "\x02\x91\xb2", /* 22 */ + "\x02\x91\xb5", /* 23 */ + "\x02\x91\xb8", /* 24 */ + "\x02\x91\xc1", /* 25 */ + "\x02\x91\x81", /* 26 */ + "\x03\x91\xe0\x01", /* 27 */ + "\x03\x91\xe0\x02" /* 28 */ +}; + +/*------------------------------------------------------------------*/ + +#define V120_HEADER_LENGTH 1 +#define V120_HEADER_EXTEND_BIT 0x80 +#define V120_HEADER_BREAK_BIT 0x40 +#define V120_HEADER_C1_BIT 0x04 +#define V120_HEADER_C2_BIT 0x08 +#define V120_HEADER_FLUSH_COND (V120_HEADER_BREAK_BIT | V120_HEADER_C1_BIT | V120_HEADER_C2_BIT) + +static byte v120_default_header[] = +{ + + 0x83 /* Ext, BR , res, res, C2 , C1 , B , F */ + +}; + +static byte v120_break_header[] = +{ + + 0xc3 | V120_HEADER_BREAK_BIT /* Ext, BR , res, res, C2 , C1 , B , F */ + +}; + + +/*------------------------------------------------------------------*/ +/* API_PUT function */ +/*------------------------------------------------------------------*/ + +word api_put(APPL *appl, CAPI_MSG *msg) +{ + word i, j, k, l, n; + word ret; + byte c; + byte controller; + DIVA_CAPI_ADAPTER *a; + PLCI *plci; + NCCI *ncci_ptr; + word ncci; + CAPI_MSG *m; + API_PARSE msg_parms[MAX_MSG_PARMS + 1]; + + if (msg->header.length < sizeof(msg->header) || + msg->header.length > MAX_MSG_SIZE) { + dbug(1, dprintf("bad len")); + return _BAD_MSG; + } + + controller = (byte)((msg->header.controller & 0x7f) - 1); + + /* controller starts with 0 up to (max_adapter - 1) */ + if (controller >= max_adapter) + { + dbug(1, dprintf("invalid ctrl")); + return _BAD_MSG; + } + + a = &adapter[controller]; + plci = NULL; + if ((msg->header.plci != 0) && (msg->header.plci <= a->max_plci) && !a->adapter_disabled) + { + dbug(1, dprintf("plci=%x", msg->header.plci)); + plci = &a->plci[msg->header.plci - 1]; + ncci = GET_WORD(&msg->header.ncci); + if (plci->Id + && (plci->appl + || (plci->State == INC_CON_PENDING) + || (plci->State == INC_CON_ALERT) + || (msg->header.command == (_DISCONNECT_I | RESPONSE))) + && ((ncci == 0) + || (msg->header.command == (_DISCONNECT_B3_I | RESPONSE)) + || ((ncci < MAX_NCCI + 1) && (a->ncci_plci[ncci] == plci->Id)))) + { + i = plci->msg_in_read_pos; + j = plci->msg_in_write_pos; + if (j >= i) + { + if (j + msg->header.length + MSG_IN_OVERHEAD <= MSG_IN_QUEUE_SIZE) + i += MSG_IN_QUEUE_SIZE - j; + else + j = 0; + } + else + { + + n = (((CAPI_MSG *)(plci->msg_in_queue))->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc; + + if (i > MSG_IN_QUEUE_SIZE - n) + i = MSG_IN_QUEUE_SIZE - n + 1; + i -= j; + } + + if (i <= ((msg->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc)) + + { + dbug(0, dprintf("Q-FULL1(msg) - len=%d write=%d read=%d wrap=%d free=%d", + msg->header.length, plci->msg_in_write_pos, + plci->msg_in_read_pos, plci->msg_in_wrap_pos, i)); + + return _QUEUE_FULL; + } + c = false; + if ((((byte *) msg) < ((byte *)(plci->msg_in_queue))) + || (((byte *) msg) >= ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue))) + { + if (plci->msg_in_write_pos != plci->msg_in_read_pos) + c = true; + } + if (msg->header.command == _DATA_B3_R) + { + if (msg->header.length < 20) + { + dbug(1, dprintf("DATA_B3 REQ wrong length %d", msg->header.length)); + return _BAD_MSG; + } + ncci_ptr = &(a->ncci[ncci]); + n = ncci_ptr->data_pending; + l = ncci_ptr->data_ack_pending; + k = plci->msg_in_read_pos; + while (k != plci->msg_in_write_pos) + { + if (k == plci->msg_in_wrap_pos) + k = 0; + if ((((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.command == _DATA_B3_R) + && (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.ncci == ncci)) + { + n++; + if (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->info.data_b3_req.Flags & 0x0004) + l++; + } + + k += (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.length + + MSG_IN_OVERHEAD + 3) & 0xfffc; + + } + if ((n >= MAX_DATA_B3) || (l >= MAX_DATA_ACK)) + { + dbug(0, dprintf("Q-FULL2(data) - pending=%d/%d ack_pending=%d/%d", + ncci_ptr->data_pending, n, ncci_ptr->data_ack_pending, l)); + + return _QUEUE_FULL; + } + if (plci->req_in || plci->internal_command) + { + if ((((byte *) msg) >= ((byte *)(plci->msg_in_queue))) + && (((byte *) msg) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue))) + { + dbug(0, dprintf("Q-FULL3(requeue)")); + + return _QUEUE_FULL; + } + c = true; + } + } + else + { + if (plci->req_in || plci->internal_command) + c = true; + else + { + plci->command = msg->header.command; + plci->number = msg->header.number; + } + } + if (c) + { + dbug(1, dprintf("enqueue msg(0x%04x,0x%x,0x%x) - len=%d write=%d read=%d wrap=%d free=%d", + msg->header.command, plci->req_in, plci->internal_command, + msg->header.length, plci->msg_in_write_pos, + plci->msg_in_read_pos, plci->msg_in_wrap_pos, i)); + if (j == 0) + plci->msg_in_wrap_pos = plci->msg_in_write_pos; + m = (CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]); + for (i = 0; i < msg->header.length; i++) + ((byte *)(plci->msg_in_queue))[j++] = ((byte *) msg)[i]; + if (m->header.command == _DATA_B3_R) + { + + m->info.data_b3_req.Data = (dword)(long)(TransmitBufferSet(appl, m->info.data_b3_req.Data)); + + } + + j = (j + 3) & 0xfffc; + + *((APPL **)(&((byte *)(plci->msg_in_queue))[j])) = appl; + plci->msg_in_write_pos = j + MSG_IN_OVERHEAD; + return 0; + } + } + else + { + plci = NULL; + } + } + dbug(1, dprintf("com=%x", msg->header.command)); + + for (j = 0; j < MAX_MSG_PARMS + 1; j++) msg_parms[j].length = 0; + for (i = 0, ret = _BAD_MSG; i < ARRAY_SIZE(ftable); i++) { + + if (ftable[i].command == msg->header.command) { + /* break loop if the message is correct, otherwise continue scan */ + /* (for example: CONNECT_B3_T90_ACT_RES has two specifications) */ + if (!api_parse(msg->info.b, (word)(msg->header.length - 12), ftable[i].format, msg_parms)) { + ret = 0; + break; + } + for (j = 0; j < MAX_MSG_PARMS + 1; j++) msg_parms[j].length = 0; + } + } + if (ret) { + dbug(1, dprintf("BAD_MSG")); + if (plci) plci->command = 0; + return ret; + } + + + c = ftable[i].function(GET_DWORD(&msg->header.controller), + msg->header.number, + a, + plci, + appl, + msg_parms); + + channel_xmit_extended_xon(plci); + + if (c == 1) send_req(plci); + if (c == 2 && plci) plci->req_in = plci->req_in_start = plci->req_out = 0; + if (plci && !plci->req_in) plci->command = 0; + return 0; +} + + +/*------------------------------------------------------------------*/ +/* api_parse function, check the format of api messages */ +/*------------------------------------------------------------------*/ + +static word api_parse(byte *msg, word length, byte *format, API_PARSE *parms) +{ + word i; + word p; + + for (i = 0, p = 0; format[i]; i++) { + if (parms) + { + parms[i].info = &msg[p]; + } + switch (format[i]) { + case 'b': + p += 1; + break; + case 'w': + p += 2; + break; + case 'd': + p += 4; + break; + case 's': + if (msg[p] == 0xff) { + parms[i].info += 2; + parms[i].length = msg[p + 1] + (msg[p + 2] << 8); + p += (parms[i].length + 3); + } + else { + parms[i].length = msg[p]; + p += (parms[i].length + 1); + } + break; + } + + if (p > length) return true; + } + if (parms) parms[i].info = NULL; + return false; +} + +static void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out) +{ + word i, j, n = 0; + byte *p; + + p = out->info; + for (i = 0; format[i] != '\0'; i++) + { + out->parms[i].info = p; + out->parms[i].length = in[i].length; + switch (format[i]) + { + case 'b': + n = 1; + break; + case 'w': + n = 2; + break; + case 'd': + n = 4; + break; + case 's': + n = in[i].length + 1; + break; + } + for (j = 0; j < n; j++) + *(p++) = in[i].info[j]; + } + out->parms[i].info = NULL; + out->parms[i].length = 0; +} + +static void api_load_msg(API_SAVE *in, API_PARSE *out) +{ + word i; + + i = 0; + do + { + out[i].info = in->parms[i].info; + out[i].length = in->parms[i].length; + } while (in->parms[i++].info); +} + + +/*------------------------------------------------------------------*/ +/* CAPI remove function */ +/*------------------------------------------------------------------*/ + +word api_remove_start(void) +{ + word i; + word j; + + if (!remove_started) { + remove_started = true; + for (i = 0; i < max_adapter; i++) { + if (adapter[i].request) { + for (j = 0; j < adapter[i].max_plci; j++) { + if (adapter[i].plci[j].Sig.Id) plci_remove(&adapter[i].plci[j]); + } + } + } + return 1; + } + else { + for (i = 0; i < max_adapter; i++) { + if (adapter[i].request) { + for (j = 0; j < adapter[i].max_plci; j++) { + if (adapter[i].plci[j].Sig.Id) return 1; + } + } + } + } + api_remove_complete(); + return 0; +} + + +/*------------------------------------------------------------------*/ +/* internal command queue */ +/*------------------------------------------------------------------*/ + +static void init_internal_command_queue(PLCI *plci) +{ + word i; + + dbug(1, dprintf("%s,%d: init_internal_command_queue", + (char *)(FILE_), __LINE__)); + + plci->internal_command = 0; + for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS; i++) + plci->internal_command_queue[i] = NULL; +} + + +static void start_internal_command(dword Id, PLCI *plci, t_std_internal_command command_function) +{ + word i; + + dbug(1, dprintf("[%06lx] %s,%d: start_internal_command", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + if (plci->internal_command == 0) + { + plci->internal_command_queue[0] = command_function; + (*command_function)(Id, plci, OK); + } + else + { + i = 1; + while (plci->internal_command_queue[i] != NULL) + i++; + plci->internal_command_queue[i] = command_function; + } +} + + +static void next_internal_command(dword Id, PLCI *plci) +{ + word i; + + dbug(1, dprintf("[%06lx] %s,%d: next_internal_command", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + plci->internal_command = 0; + plci->internal_command_queue[0] = NULL; + while (plci->internal_command_queue[1] != NULL) + { + for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS - 1; i++) + plci->internal_command_queue[i] = plci->internal_command_queue[i + 1]; + plci->internal_command_queue[MAX_INTERNAL_COMMAND_LEVELS - 1] = NULL; + (*(plci->internal_command_queue[0]))(Id, plci, OK); + if (plci->internal_command != 0) + return; + plci->internal_command_queue[0] = NULL; + } +} + + +/*------------------------------------------------------------------*/ +/* NCCI allocate/remove function */ +/*------------------------------------------------------------------*/ + +static dword ncci_mapping_bug = 0; + +static word get_ncci(PLCI *plci, byte ch, word force_ncci) +{ + DIVA_CAPI_ADAPTER *a; + word ncci, i, j, k; + + a = plci->adapter; + if (!ch || a->ch_ncci[ch]) + { + ncci_mapping_bug++; + dbug(1, dprintf("NCCI mapping exists %ld %02x %02x %02x-%02x", + ncci_mapping_bug, ch, force_ncci, a->ncci_ch[a->ch_ncci[ch]], a->ch_ncci[ch])); + ncci = ch; + } + else + { + if (force_ncci) + ncci = force_ncci; + else + { + if ((ch < MAX_NCCI + 1) && !a->ncci_ch[ch]) + ncci = ch; + else + { + ncci = 1; + while ((ncci < MAX_NCCI + 1) && a->ncci_ch[ncci]) + ncci++; + if (ncci == MAX_NCCI + 1) + { + ncci_mapping_bug++; + i = 1; + do + { + j = 1; + while ((j < MAX_NCCI + 1) && (a->ncci_ch[j] != i)) + j++; + k = j; + if (j < MAX_NCCI + 1) + { + do + { + j++; + } while ((j < MAX_NCCI + 1) && (a->ncci_ch[j] != i)); + } + } while ((i < MAX_NL_CHANNEL + 1) && (j < MAX_NCCI + 1)); + if (i < MAX_NL_CHANNEL + 1) + { + dbug(1, dprintf("NCCI mapping overflow %ld %02x %02x %02x-%02x-%02x", + ncci_mapping_bug, ch, force_ncci, i, k, j)); + } + else + { + dbug(1, dprintf("NCCI mapping overflow %ld %02x %02x", + ncci_mapping_bug, ch, force_ncci)); + } + ncci = ch; + } + } + a->ncci_plci[ncci] = plci->Id; + a->ncci_state[ncci] = IDLE; + if (!plci->ncci_ring_list) + plci->ncci_ring_list = ncci; + else + a->ncci_next[ncci] = a->ncci_next[plci->ncci_ring_list]; + a->ncci_next[plci->ncci_ring_list] = (byte) ncci; + } + a->ncci_ch[ncci] = ch; + a->ch_ncci[ch] = (byte) ncci; + dbug(1, dprintf("NCCI mapping established %ld %02x %02x %02x-%02x", + ncci_mapping_bug, ch, force_ncci, ch, ncci)); + } + return (ncci); +} + + +static void ncci_free_receive_buffers(PLCI *plci, word ncci) +{ + DIVA_CAPI_ADAPTER *a; + APPL *appl; + word i, ncci_code; + dword Id; + + a = plci->adapter; + Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id; + if (ncci) + { + if (a->ncci_plci[ncci] == plci->Id) + { + if (!plci->appl) + { + ncci_mapping_bug++; + dbug(1, dprintf("NCCI mapping appl expected %ld %08lx", + ncci_mapping_bug, Id)); + } + else + { + appl = plci->appl; + ncci_code = ncci | (((word) a->Id) << 8); + for (i = 0; i < appl->MaxBuffer; i++) + { + if ((appl->DataNCCI[i] == ncci_code) + && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id)) + { + appl->DataNCCI[i] = 0; + } + } + } + } + } + else + { + for (ncci = 1; ncci < MAX_NCCI + 1; ncci++) + { + if (a->ncci_plci[ncci] == plci->Id) + { + if (!plci->appl) + { + ncci_mapping_bug++; + dbug(1, dprintf("NCCI mapping no appl %ld %08lx", + ncci_mapping_bug, Id)); + } + else + { + appl = plci->appl; + ncci_code = ncci | (((word) a->Id) << 8); + for (i = 0; i < appl->MaxBuffer; i++) + { + if ((appl->DataNCCI[i] == ncci_code) + && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id)) + { + appl->DataNCCI[i] = 0; + } + } + } + } + } + } +} + + +static void cleanup_ncci_data(PLCI *plci, word ncci) +{ + NCCI *ncci_ptr; + + if (ncci && (plci->adapter->ncci_plci[ncci] == plci->Id)) + { + ncci_ptr = &(plci->adapter->ncci[ncci]); + if (plci->appl) + { + while (ncci_ptr->data_pending != 0) + { + if (!plci->data_sent || (ncci_ptr->DBuffer[ncci_ptr->data_out].P != plci->data_sent_ptr)) + TransmitBufferFree(plci->appl, ncci_ptr->DBuffer[ncci_ptr->data_out].P); + (ncci_ptr->data_out)++; + if (ncci_ptr->data_out == MAX_DATA_B3) + ncci_ptr->data_out = 0; + (ncci_ptr->data_pending)--; + } + } + ncci_ptr->data_out = 0; + ncci_ptr->data_pending = 0; + ncci_ptr->data_ack_out = 0; + ncci_ptr->data_ack_pending = 0; + } +} + + +static void ncci_remove(PLCI *plci, word ncci, byte preserve_ncci) +{ + DIVA_CAPI_ADAPTER *a; + dword Id; + word i; + + a = plci->adapter; + Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id; + if (!preserve_ncci) + ncci_free_receive_buffers(plci, ncci); + if (ncci) + { + if (a->ncci_plci[ncci] != plci->Id) + { + ncci_mapping_bug++; + dbug(1, dprintf("NCCI mapping doesn't exist %ld %08lx %02x", + ncci_mapping_bug, Id, preserve_ncci)); + } + else + { + cleanup_ncci_data(plci, ncci); + dbug(1, dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x", + ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci)); + a->ch_ncci[a->ncci_ch[ncci]] = 0; + if (!preserve_ncci) + { + a->ncci_ch[ncci] = 0; + a->ncci_plci[ncci] = 0; + a->ncci_state[ncci] = IDLE; + i = plci->ncci_ring_list; + while ((i != 0) && (a->ncci_next[i] != plci->ncci_ring_list) && (a->ncci_next[i] != ncci)) + i = a->ncci_next[i]; + if ((i != 0) && (a->ncci_next[i] == ncci)) + { + if (i == ncci) + plci->ncci_ring_list = 0; + else if (plci->ncci_ring_list == ncci) + plci->ncci_ring_list = i; + a->ncci_next[i] = a->ncci_next[ncci]; + } + a->ncci_next[ncci] = 0; + } + } + } + else + { + for (ncci = 1; ncci < MAX_NCCI + 1; ncci++) + { + if (a->ncci_plci[ncci] == plci->Id) + { + cleanup_ncci_data(plci, ncci); + dbug(1, dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x", + ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci)); + a->ch_ncci[a->ncci_ch[ncci]] = 0; + if (!preserve_ncci) + { + a->ncci_ch[ncci] = 0; + a->ncci_plci[ncci] = 0; + a->ncci_state[ncci] = IDLE; + a->ncci_next[ncci] = 0; + } + } + } + if (!preserve_ncci) + plci->ncci_ring_list = 0; + } +} + + +/*------------------------------------------------------------------*/ +/* PLCI remove function */ +/*------------------------------------------------------------------*/ + +static void plci_free_msg_in_queue(PLCI *plci) +{ + word i; + + if (plci->appl) + { + i = plci->msg_in_read_pos; + while (i != plci->msg_in_write_pos) + { + if (i == plci->msg_in_wrap_pos) + i = 0; + if (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->header.command == _DATA_B3_R) + { + + TransmitBufferFree(plci->appl, + (byte *)(long)(((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->info.data_b3_req.Data)); + + } + + i += (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->header.length + + MSG_IN_OVERHEAD + 3) & 0xfffc; + + } + } + plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE; + plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE; + plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE; +} + + +static void plci_remove(PLCI *plci) +{ + + if (!plci) { + dbug(1, dprintf("plci_remove(no plci)")); + return; + } + init_internal_command_queue(plci); + dbug(1, dprintf("plci_remove(%x,tel=%x)", plci->Id, plci->tel)); + if (plci_remove_check(plci)) + { + return; + } + if (plci->Sig.Id == 0xff) + { + dbug(1, dprintf("D-channel X.25 plci->NL.Id:%0x", plci->NL.Id)); + if (plci->NL.Id && !plci->nl_remove_id) + { + nl_req_ncci(plci, REMOVE, 0); + send_req(plci); + } + } + else + { + if (!plci->sig_remove_id + && (plci->Sig.Id + || (plci->req_in != plci->req_out) + || (plci->nl_req || plci->sig_req))) + { + sig_req(plci, HANGUP, 0); + send_req(plci); + } + } + ncci_remove(plci, 0, false); + plci_free_msg_in_queue(plci); + + plci->channels = 0; + plci->appl = NULL; + if ((plci->State == INC_CON_PENDING) || (plci->State == INC_CON_ALERT)) + plci->State = OUTG_DIS_PENDING; +} + +/*------------------------------------------------------------------*/ +/* translation function for each message */ +/*------------------------------------------------------------------*/ + +static byte connect_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word ch; + word i; + word Info; + byte LinkLayer; + API_PARSE *ai; + API_PARSE *bp; + API_PARSE ai_parms[5]; + word channel = 0; + dword ch_mask; + byte m; + static byte esc_chi[35] = {0x02, 0x18, 0x01}; + static byte lli[2] = {0x01, 0x00}; + byte noCh = 0; + word dir = 0; + byte *p_chi = ""; + + for (i = 0; i < 5; i++) ai_parms[i].length = 0; + + dbug(1, dprintf("connect_req(%d)", parms->length)); + Info = _WRONG_IDENTIFIER; + if (a) + { + if (a->adapter_disabled) + { + dbug(1, dprintf("adapter disabled")); + Id = ((word)1 << 8) | a->Id; + sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", 0); + sendf(appl, _DISCONNECT_I, Id, 0, "w", _L1_ERROR); + return false; + } + Info = _OUT_OF_PLCI; + if ((i = get_plci(a))) + { + Info = 0; + plci = &a->plci[i - 1]; + plci->appl = appl; + plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE; + /* check 'external controller' bit for codec support */ + if (Id & EXT_CONTROLLER) + { + if (AdvCodecSupport(a, plci, appl, 0)) + { + plci->Id = 0; + sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", _WRONG_IDENTIFIER); + return 2; + } + } + ai = &parms[9]; + bp = &parms[5]; + ch = 0; + if (bp->length)LinkLayer = bp->info[3]; + else LinkLayer = 0; + if (ai->length) + { + ch = 0xffff; + if (!api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms)) + { + ch = 0; + if (ai_parms[0].length) + { + ch = GET_WORD(ai_parms[0].info + 1); + if (ch > 4) ch = 0; /* safety -> ignore ChannelID */ + if (ch == 4) /* explizit CHI in message */ + { + /* check length of B-CH struct */ + if ((ai_parms[0].info)[3] >= 1) + { + if ((ai_parms[0].info)[4] == CHI) + { + p_chi = &((ai_parms[0].info)[5]); + } + else + { + p_chi = &((ai_parms[0].info)[3]); + } + if (p_chi[0] > 35) /* check length of channel ID */ + { + Info = _WRONG_MESSAGE_FORMAT; + } + } + else Info = _WRONG_MESSAGE_FORMAT; + } + + if (ch == 3 && ai_parms[0].length >= 7 && ai_parms[0].length <= 36) + { + dir = GET_WORD(ai_parms[0].info + 3); + ch_mask = 0; + m = 0x3f; + for (i = 0; i + 5 <= ai_parms[0].length; i++) + { + if (ai_parms[0].info[i + 5] != 0) + { + if ((ai_parms[0].info[i + 5] | m) != 0xff) + Info = _WRONG_MESSAGE_FORMAT; + else + { + if (ch_mask == 0) + channel = i; + ch_mask |= 1L << i; + } + } + m = 0; + } + if (ch_mask == 0) + Info = _WRONG_MESSAGE_FORMAT; + if (!Info) + { + if ((ai_parms[0].length == 36) || (ch_mask != ((dword)(1L << channel)))) + { + esc_chi[0] = (byte)(ai_parms[0].length - 2); + for (i = 0; i + 5 <= ai_parms[0].length; i++) + esc_chi[i + 3] = ai_parms[0].info[i + 5]; + } + else + esc_chi[0] = 2; + esc_chi[2] = (byte)channel; + plci->b_channel = (byte)channel; /* not correct for ETSI ch 17..31 */ + add_p(plci, LLI, lli); + add_p(plci, ESC, esc_chi); + plci->State = LOCAL_CONNECT; + if (!dir) plci->call_dir |= CALL_DIR_FORCE_OUTG_NL; /* dir 0=DTE, 1=DCE */ + } + } + } + } + else Info = _WRONG_MESSAGE_FORMAT; + } + + dbug(1, dprintf("ch=%x,dir=%x,p_ch=%d", ch, dir, channel)); + plci->command = _CONNECT_R; + plci->number = Number; + /* x.31 or D-ch free SAPI in LinkLayer? */ + if (ch == 1 && LinkLayer != 3 && LinkLayer != 12) noCh = true; + if ((ch == 0 || ch == 2 || noCh || ch == 3 || ch == 4) && !Info) + { + /* B-channel used for B3 connections (ch==0), or no B channel */ + /* is used (ch==2) or perm. connection (3) is used do a CALL */ + if (noCh) Info = add_b1(plci, &parms[5], 2, 0); /* no resource */ + else Info = add_b1(plci, &parms[5], ch, 0); + add_s(plci, OAD, &parms[2]); + add_s(plci, OSA, &parms[4]); + add_s(plci, BC, &parms[6]); + add_s(plci, LLC, &parms[7]); + add_s(plci, HLC, &parms[8]); + if (a->Info_Mask[appl->Id - 1] & 0x200) + { + /* early B3 connect (CIP mask bit 9) no release after a disc */ + add_p(plci, LLI, "\x01\x01"); + } + if (GET_WORD(parms[0].info) < 29) { + add_p(plci, BC, cip_bc[GET_WORD(parms[0].info)][a->u_law]); + add_p(plci, HLC, cip_hlc[GET_WORD(parms[0].info)]); + } + add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(plci, ASSIGN, DSIG_ID); + } + else if (ch == 1) { + + /* D-Channel used for B3 connections */ + plci->Sig.Id = 0xff; + Info = 0; + } + + if (!Info && ch != 2 && !noCh) { + Info = add_b23(plci, &parms[5]); + if (!Info) { + if (!(plci->tel && !plci->adv_nl))nl_req_ncci(plci, ASSIGN, 0); + } + } + + if (!Info) + { + if (ch == 0 || ch == 2 || ch == 3 || noCh || ch == 4) + { + if (plci->spoofed_msg == SPOOFING_REQUIRED) + { + api_save_msg(parms, "wsssssssss", &plci->saved_msg); + plci->spoofed_msg = CALL_REQ; + plci->internal_command = BLOCK_PLCI; + plci->command = 0; + dbug(1, dprintf("Spoof")); + send_req(plci); + return false; + } + if (ch == 4)add_p(plci, CHI, p_chi); + add_s(plci, CPN, &parms[1]); + add_s(plci, DSA, &parms[3]); + if (noCh) add_p(plci, ESC, "\x02\x18\xfd"); /* D-channel, no B-L3 */ + add_ai(plci, &parms[9]); + if (!dir)sig_req(plci, CALL_REQ, 0); + else + { + plci->command = PERM_LIST_REQ; + plci->appl = appl; + sig_req(plci, LISTEN_REQ, 0); + send_req(plci); + return false; + } + } + send_req(plci); + return false; + } + plci->Id = 0; + } + } + sendf(appl, + _CONNECT_R | CONFIRM, + Id, + Number, + "w", Info); + return 2; +} + +static byte connect_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word i, Info; + word Reject; + static byte cau_t[] = {0, 0, 0x90, 0x91, 0xac, 0x9d, 0x86, 0xd8, 0x9b}; + static byte esc_t[] = {0x03, 0x08, 0x00, 0x00}; + API_PARSE *ai; + API_PARSE ai_parms[5]; + word ch = 0; + + if (!plci) { + dbug(1, dprintf("connect_res(no plci)")); + return 0; /* no plci, no send */ + } + + dbug(1, dprintf("connect_res(State=0x%x)", plci->State)); + for (i = 0; i < 5; i++) ai_parms[i].length = 0; + ai = &parms[5]; + dbug(1, dprintf("ai->length=%d", ai->length)); + + if (ai->length) + { + if (!api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms)) + { + dbug(1, dprintf("ai_parms[0].length=%d/0x%x", ai_parms[0].length, GET_WORD(ai_parms[0].info + 1))); + ch = 0; + if (ai_parms[0].length) + { + ch = GET_WORD(ai_parms[0].info + 1); + dbug(1, dprintf("BCH-I=0x%x", ch)); + } + } + } + + if (plci->State == INC_CON_CONNECTED_ALERT) + { + dbug(1, dprintf("Connected Alert Call_Res")); + if (a->Info_Mask[appl->Id - 1] & 0x200) + { + /* early B3 connect (CIP mask bit 9) no release after a disc */ + add_p(plci, LLI, "\x01\x01"); + } + add_s(plci, CONN_NR, &parms[2]); + add_s(plci, LLC, &parms[4]); + add_ai(plci, &parms[5]); + plci->State = INC_CON_ACCEPT; + sig_req(plci, CALL_RES, 0); + return 1; + } + else if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT) { + __clear_bit(appl->Id - 1, plci->c_ind_mask_table); + dbug(1, dprintf("c_ind_mask =%*pb", MAX_APPL, plci->c_ind_mask_table)); + Reject = GET_WORD(parms[0].info); + dbug(1, dprintf("Reject=0x%x", Reject)); + if (Reject) + { + if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL)) + { + if ((Reject & 0xff00) == 0x3400) + { + esc_t[2] = ((byte)(Reject & 0x00ff)) | 0x80; + add_p(plci, ESC, esc_t); + add_ai(plci, &parms[5]); + sig_req(plci, REJECT, 0); + } + else if (Reject == 1 || Reject >= 9) + { + add_ai(plci, &parms[5]); + sig_req(plci, HANGUP, 0); + } + else + { + esc_t[2] = cau_t[(Reject&0x000f)]; + add_p(plci, ESC, esc_t); + add_ai(plci, &parms[5]); + sig_req(plci, REJECT, 0); + } + plci->appl = appl; + } + else + { + sendf(appl, _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED); + } + } + else { + plci->appl = appl; + if (Id & EXT_CONTROLLER) { + if (AdvCodecSupport(a, plci, appl, 0)) { + dbug(1, dprintf("connect_res(error from AdvCodecSupport)")); + sig_req(plci, HANGUP, 0); + return 1; + } + if (plci->tel == ADV_VOICE && a->AdvCodecPLCI) + { + Info = add_b23(plci, &parms[1]); + if (Info) + { + dbug(1, dprintf("connect_res(error from add_b23)")); + sig_req(plci, HANGUP, 0); + return 1; + } + if (plci->adv_nl) + { + nl_req_ncci(plci, ASSIGN, 0); + } + } + } + else + { + plci->tel = 0; + if (ch != 2) + { + Info = add_b23(plci, &parms[1]); + if (Info) + { + dbug(1, dprintf("connect_res(error from add_b23 2)")); + sig_req(plci, HANGUP, 0); + return 1; + } + } + nl_req_ncci(plci, ASSIGN, 0); + } + + if (plci->spoofed_msg == SPOOFING_REQUIRED) + { + api_save_msg(parms, "wsssss", &plci->saved_msg); + plci->spoofed_msg = CALL_RES; + plci->internal_command = BLOCK_PLCI; + plci->command = 0; + dbug(1, dprintf("Spoof")); + } + else + { + add_b1(plci, &parms[1], ch, plci->B1_facilities); + if (a->Info_Mask[appl->Id - 1] & 0x200) + { + /* early B3 connect (CIP mask bit 9) no release after a disc */ + add_p(plci, LLI, "\x01\x01"); + } + add_s(plci, CONN_NR, &parms[2]); + add_s(plci, LLC, &parms[4]); + add_ai(plci, &parms[5]); + plci->State = INC_CON_ACCEPT; + sig_req(plci, CALL_RES, 0); + } + + for_each_set_bit(i, plci->c_ind_mask_table, max_appl) + sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED); + } + } + return 1; +} + +static byte connect_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *msg) +{ + dbug(1, dprintf("connect_a_res")); + return false; +} + +static byte disconnect_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *msg) +{ + word Info; + word i; + + dbug(1, dprintf("disconnect_req")); + + Info = _WRONG_IDENTIFIER; + + if (plci) + { + if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT) + { + __clear_bit(appl->Id - 1, plci->c_ind_mask_table); + plci->appl = appl; + for_each_set_bit(i, plci->c_ind_mask_table, max_appl) + sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0); + plci->State = OUTG_DIS_PENDING; + } + if (plci->Sig.Id && plci->appl) + { + Info = 0; + if (plci->Sig.Id != 0xff) + { + if (plci->State != INC_DIS_PENDING) + { + add_ai(plci, &msg[0]); + sig_req(plci, HANGUP, 0); + plci->State = OUTG_DIS_PENDING; + return 1; + } + } + else + { + if (plci->NL.Id && !plci->nl_remove_id) + { + mixer_remove(plci); + nl_req_ncci(plci, REMOVE, 0); + sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", 0); + sendf(appl, _DISCONNECT_I, Id, 0, "w", 0); + plci->State = INC_DIS_PENDING; + } + return 1; + } + } + } + + if (!appl) return false; + sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", Info); + return false; +} + +static byte disconnect_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *msg) +{ + dbug(1, dprintf("disconnect_res")); + if (plci) + { + /* clear ind mask bit, just in case of collsion of */ + /* DISCONNECT_IND and CONNECT_RES */ + __clear_bit(appl->Id - 1, plci->c_ind_mask_table); + ncci_free_receive_buffers(plci, 0); + if (plci_remove_check(plci)) + { + return 0; + } + if (plci->State == INC_DIS_PENDING + || plci->State == SUSPENDING) { + if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL)) { + if (plci->State != SUSPENDING) plci->State = IDLE; + dbug(1, dprintf("chs=%d", plci->channels)); + if (!plci->channels) { + plci_remove(plci); + } + } + } + } + return 0; +} + +static byte listen_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word Info; + byte i; + + dbug(1, dprintf("listen_req(Appl=0x%x)", appl->Id)); + + Info = _WRONG_IDENTIFIER; + if (a) { + Info = 0; + a->Info_Mask[appl->Id - 1] = GET_DWORD(parms[0].info); + a->CIP_Mask[appl->Id - 1] = GET_DWORD(parms[1].info); + dbug(1, dprintf("CIP_MASK=0x%lx", GET_DWORD(parms[1].info))); + if (a->Info_Mask[appl->Id - 1] & 0x200) { /* early B3 connect provides */ + a->Info_Mask[appl->Id - 1] |= 0x10; /* call progression infos */ + } + + /* check if external controller listen and switch listen on or off*/ + if (Id&EXT_CONTROLLER && GET_DWORD(parms[1].info)) { + if (a->profile.Global_Options & ON_BOARD_CODEC) { + dummy_plci.State = IDLE; + a->codec_listen[appl->Id - 1] = &dummy_plci; + a->TelOAD[0] = (byte)(parms[3].length); + for (i = 1; parms[3].length >= i && i < 22; i++) { + a->TelOAD[i] = parms[3].info[i]; + } + a->TelOAD[i] = 0; + a->TelOSA[0] = (byte)(parms[4].length); + for (i = 1; parms[4].length >= i && i < 22; i++) { + a->TelOSA[i] = parms[4].info[i]; + } + a->TelOSA[i] = 0; + } + else Info = 0x2002; /* wrong controller, codec not supported */ + } + else{ /* clear listen */ + a->codec_listen[appl->Id - 1] = (PLCI *)0; + } + } + sendf(appl, + _LISTEN_R | CONFIRM, + Id, + Number, + "w", Info); + + if (a) listen_check(a); + return false; +} + +static byte info_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *msg) +{ + word i; + API_PARSE *ai; + PLCI *rc_plci = NULL; + API_PARSE ai_parms[5]; + word Info = 0; + + dbug(1, dprintf("info_req")); + for (i = 0; i < 5; i++) ai_parms[i].length = 0; + + ai = &msg[1]; + + if (ai->length) + { + if (api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms)) + { + dbug(1, dprintf("AddInfo wrong")); + Info = _WRONG_MESSAGE_FORMAT; + } + } + if (!a) Info = _WRONG_STATE; + + if (!Info && plci) + { /* no fac, with CPN, or KEY */ + rc_plci = plci; + if (!ai_parms[3].length && plci->State && (msg[0].length || ai_parms[1].length)) + { + /* overlap sending option */ + dbug(1, dprintf("OvlSnd")); + add_s(plci, CPN, &msg[0]); + add_s(plci, KEY, &ai_parms[1]); + sig_req(plci, INFO_REQ, 0); + send_req(plci); + return false; + } + + if (plci->State && ai_parms[2].length) + { + /* User_Info option */ + dbug(1, dprintf("UUI")); + add_s(plci, UUI, &ai_parms[2]); + sig_req(plci, USER_DATA, 0); + } + else if (plci->State && ai_parms[3].length) + { + /* Facility option */ + dbug(1, dprintf("FAC")); + add_s(plci, CPN, &msg[0]); + add_ai(plci, &msg[1]); + sig_req(plci, FACILITY_REQ, 0); + } + else + { + Info = _WRONG_STATE; + } + } + else if ((ai_parms[1].length || ai_parms[2].length || ai_parms[3].length) && !Info) + { + /* NCR_Facility option -> send UUI and Keypad too */ + dbug(1, dprintf("NCR_FAC")); + if ((i = get_plci(a))) + { + rc_plci = &a->plci[i - 1]; + appl->NullCREnable = true; + rc_plci->internal_command = C_NCR_FAC_REQ; + rc_plci->appl = appl; + add_p(rc_plci, CAI, "\x01\x80"); + add_p(rc_plci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(rc_plci, ASSIGN, DSIG_ID); + send_req(rc_plci); + } + else + { + Info = _OUT_OF_PLCI; + } + + if (!Info) + { + add_s(rc_plci, CPN, &msg[0]); + add_ai(rc_plci, &msg[1]); + sig_req(rc_plci, NCR_FACILITY, 0); + send_req(rc_plci); + return false; + /* for application controlled supplementary services */ + } + } + + if (!rc_plci) + { + Info = _WRONG_MESSAGE_FORMAT; + } + + if (!Info) + { + send_req(rc_plci); + } + else + { /* appl is not assigned to a PLCI or error condition */ + dbug(1, dprintf("localInfoCon")); + sendf(appl, + _INFO_R | CONFIRM, + Id, + Number, + "w", Info); + } + return false; +} + +static byte info_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *msg) +{ + dbug(1, dprintf("info_res")); + return false; +} + +static byte alert_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *msg) +{ + word Info; + byte ret; + + dbug(1, dprintf("alert_req")); + + Info = _WRONG_IDENTIFIER; + ret = false; + if (plci) { + Info = _ALERT_IGNORED; + if (plci->State != INC_CON_ALERT) { + Info = _WRONG_STATE; + if (plci->State == INC_CON_PENDING) { + Info = 0; + plci->State = INC_CON_ALERT; + add_ai(plci, &msg[0]); + sig_req(plci, CALL_ALERT, 0); + ret = 1; + } + } + } + sendf(appl, + _ALERT_R | CONFIRM, + Id, + Number, + "w", Info); + return ret; +} + +static byte facility_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *msg) +{ + word Info = 0; + word i = 0; + + word selector; + word SSreq; + long relatedPLCIvalue; + DIVA_CAPI_ADAPTER *relatedadapter; + byte *SSparms = ""; + byte RCparms[] = "\x05\x00\x00\x02\x00\x00"; + byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00"; + API_PARSE *parms; + API_PARSE ss_parms[11]; + PLCI *rplci; + byte cai[15]; + dword d; + API_PARSE dummy; + + dbug(1, dprintf("facility_req")); + for (i = 0; i < 9; i++) ss_parms[i].length = 0; + + parms = &msg[1]; + + if (!a) + { + dbug(1, dprintf("wrong Ctrl")); + Info = _WRONG_IDENTIFIER; + } + + selector = GET_WORD(msg[0].info); + + if (!Info) + { + switch (selector) + { + case SELECTOR_HANDSET: + Info = AdvCodecSupport(a, plci, appl, HOOK_SUPPORT); + break; + + case SELECTOR_SU_SERV: + if (!msg[1].length) + { + Info = _WRONG_MESSAGE_FORMAT; + break; + } + SSreq = GET_WORD(&(msg[1].info[1])); + PUT_WORD(&RCparms[1], SSreq); + SSparms = RCparms; + switch (SSreq) + { + case S_GET_SUPPORTED_SERVICES: + if ((i = get_plci(a))) + { + rplci = &a->plci[i - 1]; + rplci->appl = appl; + add_p(rplci, CAI, "\x01\x80"); + add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(rplci, ASSIGN, DSIG_ID); + send_req(rplci); + } + else + { + PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY); + SSparms = (byte *)SSstruct; + break; + } + rplci->internal_command = GETSERV_REQ_PEND; + rplci->number = Number; + rplci->appl = appl; + sig_req(rplci, S_SUPPORTED, 0); + send_req(rplci); + return false; + break; + + case S_LISTEN: + if (parms->length == 7) + { + if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms)) + { + dbug(1, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + } + else + { + Info = _WRONG_MESSAGE_FORMAT; + break; + } + a->Notification_Mask[appl->Id - 1] = GET_DWORD(ss_parms[2].info); + if (a->Notification_Mask[appl->Id - 1] & SMASK_MWI) /* MWI active? */ + { + if ((i = get_plci(a))) + { + rplci = &a->plci[i - 1]; + rplci->appl = appl; + add_p(rplci, CAI, "\x01\x80"); + add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(rplci, ASSIGN, DSIG_ID); + send_req(rplci); + } + else + { + break; + } + rplci->internal_command = GET_MWI_STATE; + rplci->number = Number; + sig_req(rplci, MWI_POLL, 0); + send_req(rplci); + } + break; + + case S_HOLD: + api_parse(&parms->info[1], (word)parms->length, "ws", ss_parms); + if (plci && plci->State && plci->SuppState == IDLE) + { + plci->SuppState = HOLD_REQUEST; + plci->command = C_HOLD_REQ; + add_s(plci, CAI, &ss_parms[1]); + sig_req(plci, CALL_HOLD, 0); + send_req(plci); + return false; + } + else Info = 0x3010; /* wrong state */ + break; + case S_RETRIEVE: + if (plci && plci->State && plci->SuppState == CALL_HELD) + { + if (Id & EXT_CONTROLLER) + { + if (AdvCodecSupport(a, plci, appl, 0)) + { + Info = 0x3010; /* wrong state */ + break; + } + } + else plci->tel = 0; + + plci->SuppState = RETRIEVE_REQUEST; + plci->command = C_RETRIEVE_REQ; + if (plci->spoofed_msg == SPOOFING_REQUIRED) + { + plci->spoofed_msg = CALL_RETRIEVE; + plci->internal_command = BLOCK_PLCI; + plci->command = 0; + dbug(1, dprintf("Spoof")); + return false; + } + else + { + sig_req(plci, CALL_RETRIEVE, 0); + send_req(plci); + return false; + } + } + else Info = 0x3010; /* wrong state */ + break; + case S_SUSPEND: + if (parms->length) + { + if (api_parse(&parms->info[1], (word)parms->length, "wbs", ss_parms)) + { + dbug(1, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + } + if (plci && plci->State) + { + add_s(plci, CAI, &ss_parms[2]); + plci->command = SUSPEND_REQ; + sig_req(plci, SUSPEND, 0); + plci->State = SUSPENDING; + send_req(plci); + } + else Info = 0x3010; /* wrong state */ + break; + + case S_RESUME: + if (!(i = get_plci(a))) + { + Info = _OUT_OF_PLCI; + break; + } + rplci = &a->plci[i - 1]; + rplci->appl = appl; + rplci->number = Number; + rplci->tel = 0; + rplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE; + /* check 'external controller' bit for codec support */ + if (Id & EXT_CONTROLLER) + { + if (AdvCodecSupport(a, rplci, appl, 0)) + { + rplci->Id = 0; + Info = 0x300A; + break; + } + } + if (parms->length) + { + if (api_parse(&parms->info[1], (word)parms->length, "wbs", ss_parms)) + { + dbug(1, dprintf("format wrong")); + rplci->Id = 0; + Info = _WRONG_MESSAGE_FORMAT; + break; + } + } + dummy.length = 0; + dummy.info = "\x00"; + add_b1(rplci, &dummy, 0, 0); + if (a->Info_Mask[appl->Id - 1] & 0x200) + { + /* early B3 connect (CIP mask bit 9) no release after a disc */ + add_p(rplci, LLI, "\x01\x01"); + } + add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(rplci, ASSIGN, DSIG_ID); + send_req(rplci); + add_s(rplci, CAI, &ss_parms[2]); + rplci->command = RESUME_REQ; + sig_req(rplci, RESUME, 0); + rplci->State = RESUMING; + send_req(rplci); + break; + + case S_CONF_BEGIN: /* Request */ + case S_CONF_DROP: + case S_CONF_ISOLATE: + case S_CONF_REATTACH: + if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms)) + { + dbug(1, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + if (plci && plci->State && ((plci->SuppState == IDLE) || (plci->SuppState == CALL_HELD))) + { + d = GET_DWORD(ss_parms[2].info); + if (d >= 0x80) + { + dbug(1, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + plci->ptyState = (byte)SSreq; + plci->command = 0; + cai[0] = 2; + switch (SSreq) + { + case S_CONF_BEGIN: + cai[1] = CONF_BEGIN; + plci->internal_command = CONF_BEGIN_REQ_PEND; + break; + case S_CONF_DROP: + cai[1] = CONF_DROP; + plci->internal_command = CONF_DROP_REQ_PEND; + break; + case S_CONF_ISOLATE: + cai[1] = CONF_ISOLATE; + plci->internal_command = CONF_ISOLATE_REQ_PEND; + break; + case S_CONF_REATTACH: + cai[1] = CONF_REATTACH; + plci->internal_command = CONF_REATTACH_REQ_PEND; + break; + } + cai[2] = (byte)d; /* Conference Size resp. PartyId */ + add_p(plci, CAI, cai); + sig_req(plci, S_SERVICE, 0); + send_req(plci); + return false; + } + else Info = 0x3010; /* wrong state */ + break; + + case S_ECT: + case S_3PTY_BEGIN: + case S_3PTY_END: + case S_CONF_ADD: + if (parms->length == 7) + { + if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms)) + { + dbug(1, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + } + else if (parms->length == 8) /* workaround for the T-View-S */ + { + if (api_parse(&parms->info[1], (word)parms->length, "wbdb", ss_parms)) + { + dbug(1, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + } + else + { + Info = _WRONG_MESSAGE_FORMAT; + break; + } + if (!msg[1].length) + { + Info = _WRONG_MESSAGE_FORMAT; + break; + } + if (!plci) + { + Info = _WRONG_IDENTIFIER; + break; + } + relatedPLCIvalue = GET_DWORD(ss_parms[2].info); + relatedPLCIvalue &= 0x0000FFFF; + dbug(1, dprintf("PTY/ECT/addCONF,relPLCI=%lx", relatedPLCIvalue)); + /* controller starts with 0 up to (max_adapter - 1) */ + if (((relatedPLCIvalue & 0x7f) == 0) + || (MapController((byte)(relatedPLCIvalue & 0x7f)) == 0) + || (MapController((byte)(relatedPLCIvalue & 0x7f)) > max_adapter)) + { + if (SSreq == S_3PTY_END) + { + dbug(1, dprintf("wrong Controller use 2nd PLCI=PLCI")); + rplci = plci; + } + else + { + Info = 0x3010; /* wrong state */ + break; + } + } + else + { + relatedadapter = &adapter[MapController((byte)(relatedPLCIvalue & 0x7f)) - 1]; + relatedPLCIvalue >>= 8; + /* find PLCI PTR*/ + for (i = 0, rplci = NULL; i < relatedadapter->max_plci; i++) + { + if (relatedadapter->plci[i].Id == (byte)relatedPLCIvalue) + { + rplci = &relatedadapter->plci[i]; + } + } + if (!rplci || !relatedPLCIvalue) + { + if (SSreq == S_3PTY_END) + { + dbug(1, dprintf("use 2nd PLCI=PLCI")); + rplci = plci; + } + else + { + Info = 0x3010; /* wrong state */ + break; + } + } + } +/* + dbug(1, dprintf("rplci:%x", rplci)); + dbug(1, dprintf("plci:%x", plci)); + dbug(1, dprintf("rplci->ptyState:%x", rplci->ptyState)); + dbug(1, dprintf("plci->ptyState:%x", plci->ptyState)); + dbug(1, dprintf("SSreq:%x", SSreq)); + dbug(1, dprintf("rplci->internal_command:%x", rplci->internal_command)); + dbug(1, dprintf("rplci->appl:%x", rplci->appl)); + dbug(1, dprintf("rplci->Id:%x", rplci->Id)); +*/ + /* send PTY/ECT req, cannot check all states because of US stuff */ + if (!rplci->internal_command && rplci->appl) + { + plci->command = 0; + rplci->relatedPTYPLCI = plci; + plci->relatedPTYPLCI = rplci; + rplci->ptyState = (byte)SSreq; + if (SSreq == S_ECT) + { + rplci->internal_command = ECT_REQ_PEND; + cai[1] = ECT_EXECUTE; + + rplci->vswitchstate = 0; + rplci->vsprot = 0; + rplci->vsprotdialect = 0; + plci->vswitchstate = 0; + plci->vsprot = 0; + plci->vsprotdialect = 0; + + } + else if (SSreq == S_CONF_ADD) + { + rplci->internal_command = CONF_ADD_REQ_PEND; + cai[1] = CONF_ADD; + } + else + { + rplci->internal_command = PTY_REQ_PEND; + cai[1] = (byte)(SSreq - 3); + } + rplci->number = Number; + if (plci != rplci) /* explicit invocation */ + { + cai[0] = 2; + cai[2] = plci->Sig.Id; + dbug(1, dprintf("explicit invocation")); + } + else + { + dbug(1, dprintf("implicit invocation")); + cai[0] = 1; + } + add_p(rplci, CAI, cai); + sig_req(rplci, S_SERVICE, 0); + send_req(rplci); + return false; + } + else + { + dbug(0, dprintf("Wrong line")); + Info = 0x3010; /* wrong state */ + break; + } + break; + + case S_CALL_DEFLECTION: + if (api_parse(&parms->info[1], (word)parms->length, "wbwss", ss_parms)) + { + dbug(1, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + if (!plci) + { + Info = _WRONG_IDENTIFIER; + break; + } + /* reuse unused screening indicator */ + ss_parms[3].info[3] = (byte)GET_WORD(&(ss_parms[2].info[0])); + plci->command = 0; + plci->internal_command = CD_REQ_PEND; + appl->CDEnable = true; + cai[0] = 1; + cai[1] = CALL_DEFLECTION; + add_p(plci, CAI, cai); + add_p(plci, CPN, ss_parms[3].info); + sig_req(plci, S_SERVICE, 0); + send_req(plci); + return false; + break; + + case S_CALL_FORWARDING_START: + if (api_parse(&parms->info[1], (word)parms->length, "wbdwwsss", ss_parms)) + { + dbug(1, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + + if ((i = get_plci(a))) + { + rplci = &a->plci[i - 1]; + rplci->appl = appl; + add_p(rplci, CAI, "\x01\x80"); + add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(rplci, ASSIGN, DSIG_ID); + send_req(rplci); + } + else + { + Info = _OUT_OF_PLCI; + break; + } + + /* reuse unused screening indicator */ + rplci->internal_command = CF_START_PEND; + rplci->appl = appl; + rplci->number = Number; + appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0])); + cai[0] = 2; + cai[1] = 0x70 | (byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */ + cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */ + add_p(rplci, CAI, cai); + add_p(rplci, OAD, ss_parms[5].info); + add_p(rplci, CPN, ss_parms[6].info); + sig_req(rplci, S_SERVICE, 0); + send_req(rplci); + return false; + break; + + case S_INTERROGATE_DIVERSION: + case S_INTERROGATE_NUMBERS: + case S_CALL_FORWARDING_STOP: + case S_CCBS_REQUEST: + case S_CCBS_DEACTIVATE: + case S_CCBS_INTERROGATE: + switch (SSreq) + { + case S_INTERROGATE_NUMBERS: + if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms)) + { + dbug(0, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + } + break; + case S_CCBS_REQUEST: + case S_CCBS_DEACTIVATE: + if (api_parse(&parms->info[1], (word)parms->length, "wbdw", ss_parms)) + { + dbug(0, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + } + break; + case S_CCBS_INTERROGATE: + if (api_parse(&parms->info[1], (word)parms->length, "wbdws", ss_parms)) + { + dbug(0, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + } + break; + default: + if (api_parse(&parms->info[1], (word)parms->length, "wbdwws", ss_parms)) + { + dbug(0, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + break; + } + + if (Info) break; + if ((i = get_plci(a))) + { + rplci = &a->plci[i - 1]; + switch (SSreq) + { + case S_INTERROGATE_DIVERSION: /* use cai with S_SERVICE below */ + cai[1] = 0x60 | (byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */ + rplci->internal_command = INTERR_DIVERSION_REQ_PEND; /* move to rplci if assigned */ + break; + case S_INTERROGATE_NUMBERS: /* use cai with S_SERVICE below */ + cai[1] = DIVERSION_INTERROGATE_NUM; /* Function */ + rplci->internal_command = INTERR_NUMBERS_REQ_PEND; /* move to rplci if assigned */ + break; + case S_CALL_FORWARDING_STOP: + rplci->internal_command = CF_STOP_PEND; + cai[1] = 0x80 | (byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */ + break; + case S_CCBS_REQUEST: + cai[1] = CCBS_REQUEST; + rplci->internal_command = CCBS_REQUEST_REQ_PEND; + break; + case S_CCBS_DEACTIVATE: + cai[1] = CCBS_DEACTIVATE; + rplci->internal_command = CCBS_DEACTIVATE_REQ_PEND; + break; + case S_CCBS_INTERROGATE: + cai[1] = CCBS_INTERROGATE; + rplci->internal_command = CCBS_INTERROGATE_REQ_PEND; + break; + default: + cai[1] = 0; + break; + } + rplci->appl = appl; + rplci->number = Number; + add_p(rplci, CAI, "\x01\x80"); + add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(rplci, ASSIGN, DSIG_ID); + send_req(rplci); + } + else + { + Info = _OUT_OF_PLCI; + break; + } + + appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0])); + switch (SSreq) + { + case S_INTERROGATE_NUMBERS: + cai[0] = 1; + add_p(rplci, CAI, cai); + break; + case S_CCBS_REQUEST: + case S_CCBS_DEACTIVATE: + cai[0] = 3; + PUT_WORD(&cai[2], GET_WORD(&(ss_parms[3].info[0]))); + add_p(rplci, CAI, cai); + break; + case S_CCBS_INTERROGATE: + cai[0] = 3; + PUT_WORD(&cai[2], GET_WORD(&(ss_parms[3].info[0]))); + add_p(rplci, CAI, cai); + add_p(rplci, OAD, ss_parms[4].info); + break; + default: + cai[0] = 2; + cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */ + add_p(rplci, CAI, cai); + add_p(rplci, OAD, ss_parms[5].info); + break; + } + + sig_req(rplci, S_SERVICE, 0); + send_req(rplci); + return false; + break; + + case S_MWI_ACTIVATE: + if (api_parse(&parms->info[1], (word)parms->length, "wbwdwwwssss", ss_parms)) + { + dbug(1, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + if (!plci) + { + if ((i = get_plci(a))) + { + rplci = &a->plci[i - 1]; + rplci->appl = appl; + rplci->cr_enquiry = true; + add_p(rplci, CAI, "\x01\x80"); + add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(rplci, ASSIGN, DSIG_ID); + send_req(rplci); + } + else + { + Info = _OUT_OF_PLCI; + break; + } + } + else + { + rplci = plci; + rplci->cr_enquiry = false; + } + + rplci->command = 0; + rplci->internal_command = MWI_ACTIVATE_REQ_PEND; + rplci->appl = appl; + rplci->number = Number; + + cai[0] = 13; + cai[1] = ACTIVATION_MWI; /* Function */ + PUT_WORD(&cai[2], GET_WORD(&(ss_parms[2].info[0]))); /* Basic Service */ + PUT_DWORD(&cai[4], GET_DWORD(&(ss_parms[3].info[0]))); /* Number of Messages */ + PUT_WORD(&cai[8], GET_WORD(&(ss_parms[4].info[0]))); /* Message Status */ + PUT_WORD(&cai[10], GET_WORD(&(ss_parms[5].info[0]))); /* Message Reference */ + PUT_WORD(&cai[12], GET_WORD(&(ss_parms[6].info[0]))); /* Invocation Mode */ + add_p(rplci, CAI, cai); + add_p(rplci, CPN, ss_parms[7].info); /* Receiving User Number */ + add_p(rplci, OAD, ss_parms[8].info); /* Controlling User Number */ + add_p(rplci, OSA, ss_parms[9].info); /* Controlling User Provided Number */ + add_p(rplci, UID, ss_parms[10].info); /* Time */ + sig_req(rplci, S_SERVICE, 0); + send_req(rplci); + return false; + + case S_MWI_DEACTIVATE: + if (api_parse(&parms->info[1], (word)parms->length, "wbwwss", ss_parms)) + { + dbug(1, dprintf("format wrong")); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + if (!plci) + { + if ((i = get_plci(a))) + { + rplci = &a->plci[i - 1]; + rplci->appl = appl; + rplci->cr_enquiry = true; + add_p(rplci, CAI, "\x01\x80"); + add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(rplci, ASSIGN, DSIG_ID); + send_req(rplci); + } + else + { + Info = _OUT_OF_PLCI; + break; + } + } + else + { + rplci = plci; + rplci->cr_enquiry = false; + } + + rplci->command = 0; + rplci->internal_command = MWI_DEACTIVATE_REQ_PEND; + rplci->appl = appl; + rplci->number = Number; + + cai[0] = 5; + cai[1] = DEACTIVATION_MWI; /* Function */ + PUT_WORD(&cai[2], GET_WORD(&(ss_parms[2].info[0]))); /* Basic Service */ + PUT_WORD(&cai[4], GET_WORD(&(ss_parms[3].info[0]))); /* Invocation Mode */ + add_p(rplci, CAI, cai); + add_p(rplci, CPN, ss_parms[4].info); /* Receiving User Number */ + add_p(rplci, OAD, ss_parms[5].info); /* Controlling User Number */ + sig_req(rplci, S_SERVICE, 0); + send_req(rplci); + return false; + + default: + Info = 0x300E; /* not supported */ + break; + } + break; /* case SELECTOR_SU_SERV: end */ + + + case SELECTOR_DTMF: + return (dtmf_request(Id, Number, a, plci, appl, msg)); + + + + case SELECTOR_LINE_INTERCONNECT: + return (mixer_request(Id, Number, a, plci, appl, msg)); + + + + case PRIV_SELECTOR_ECHO_CANCELLER: + appl->appl_flags |= APPL_FLAG_PRIV_EC_SPEC; + return (ec_request(Id, Number, a, plci, appl, msg)); + + case SELECTOR_ECHO_CANCELLER: + appl->appl_flags &= ~APPL_FLAG_PRIV_EC_SPEC; + return (ec_request(Id, Number, a, plci, appl, msg)); + + + case SELECTOR_V42BIS: + default: + Info = _FACILITY_NOT_SUPPORTED; + break; + } /* end of switch (selector) */ + } + + dbug(1, dprintf("SendFacRc")); + sendf(appl, + _FACILITY_R | CONFIRM, + Id, + Number, + "wws", Info, selector, SSparms); + return false; +} + +static byte facility_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *msg) +{ + dbug(1, dprintf("facility_res")); + return false; +} + +static byte connect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word Info = 0; + byte req; + byte len; + word w; + word fax_control_bits, fax_feature_bits, fax_info_change; + API_PARSE *ncpi; + byte pvc[2]; + + API_PARSE fax_parms[9]; + word i; + + + dbug(1, dprintf("connect_b3_req")); + if (plci) + { + if ((plci->State == IDLE) || (plci->State == OUTG_DIS_PENDING) + || (plci->State == INC_DIS_PENDING) || (plci->SuppState != IDLE)) + { + Info = _WRONG_STATE; + } + else + { + /* local reply if assign unsuccessful + or B3 protocol allows only one layer 3 connection + and already connected + or B2 protocol not any LAPD + and connect_b3_req contradicts originate/answer direction */ + if (!plci->NL.Id + || (((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE)) + && ((plci->channels != 0) + || (((plci->B2_prot != B2_SDLC) && (plci->B2_prot != B2_LAPD) && (plci->B2_prot != B2_LAPD_FREE_SAPI_SEL)) + && ((plci->call_dir & CALL_DIR_ANSWER) && !(plci->call_dir & CALL_DIR_FORCE_OUTG_NL)))))) + { + dbug(1, dprintf("B3 already connected=%d or no NL.Id=0x%x, dir=%d sstate=0x%x", + plci->channels, plci->NL.Id, plci->call_dir, plci->SuppState)); + Info = _WRONG_STATE; + sendf(appl, + _CONNECT_B3_R | CONFIRM, + Id, + Number, + "w", Info); + return false; + } + plci->requested_options_conn = 0; + + req = N_CONNECT; + ncpi = &parms[0]; + if (plci->B3_prot == 2 || plci->B3_prot == 3) + { + if (ncpi->length > 2) + { + /* check for PVC */ + if (ncpi->info[2] || ncpi->info[3]) + { + pvc[0] = ncpi->info[3]; + pvc[1] = ncpi->info[2]; + add_d(plci, 2, pvc); + req = N_RESET; + } + else + { + if (ncpi->info[1] & 1) req = N_CONNECT | N_D_BIT; + add_d(plci, (word)(ncpi->length - 3), &ncpi->info[4]); + } + } + } + else if (plci->B3_prot == 5) + { + if (plci->NL.Id && !plci->nl_remove_id) + { + fax_control_bits = GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low); + fax_feature_bits = GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->feature_bits_low); + if (!(fax_control_bits & T30_CONTROL_BIT_MORE_DOCUMENTS) + || (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS)) + { + len = offsetof(T30_INFO, universal_6); + fax_info_change = false; + if (ncpi->length >= 4) + { + w = GET_WORD(&ncpi->info[3]); + if ((w & 0x0001) != ((word)(((T30_INFO *)(plci->fax_connect_info_buffer))->resolution & 0x0001))) + { + ((T30_INFO *)(plci->fax_connect_info_buffer))->resolution = + (byte)((((T30_INFO *)(plci->fax_connect_info_buffer))->resolution & ~T30_RESOLUTION_R8_0770_OR_200) | + ((w & 0x0001) ? T30_RESOLUTION_R8_0770_OR_200 : 0)); + fax_info_change = true; + } + fax_control_bits &= ~(T30_CONTROL_BIT_REQUEST_POLLING | T30_CONTROL_BIT_MORE_DOCUMENTS); + if (w & 0x0002) /* Fax-polling request */ + fax_control_bits |= T30_CONTROL_BIT_REQUEST_POLLING; + if ((w & 0x0004) /* Request to send / poll another document */ + && (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS)) + { + fax_control_bits |= T30_CONTROL_BIT_MORE_DOCUMENTS; + } + if (ncpi->length >= 6) + { + w = GET_WORD(&ncpi->info[5]); + if (((byte) w) != ((T30_INFO *)(plci->fax_connect_info_buffer))->data_format) + { + ((T30_INFO *)(plci->fax_connect_info_buffer))->data_format = (byte) w; + fax_info_change = true; + } + + if ((a->man_profile.private_options & (1L << PRIVATE_FAX_SUB_SEP_PWD)) + && (GET_WORD(&ncpi->info[5]) & 0x8000)) /* Private SEP/SUB/PWD enable */ + { + plci->requested_options_conn |= (1L << PRIVATE_FAX_SUB_SEP_PWD); + } + if ((a->man_profile.private_options & (1L << PRIVATE_FAX_NONSTANDARD)) + && (GET_WORD(&ncpi->info[5]) & 0x4000)) /* Private non-standard facilities enable */ + { + plci->requested_options_conn |= (1L << PRIVATE_FAX_NONSTANDARD); + } + fax_control_bits &= ~(T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_SEL_POLLING | + T30_CONTROL_BIT_ACCEPT_PASSWORD); + if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id - 1]) + & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD))) + { + if (api_parse(&ncpi->info[1], ncpi->length, "wwwwsss", fax_parms)) + Info = _WRONG_MESSAGE_FORMAT; + else + { + if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id - 1]) + & (1L << PRIVATE_FAX_SUB_SEP_PWD)) + { + fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_PASSWORD; + if (fax_control_bits & T30_CONTROL_BIT_ACCEPT_POLLING) + fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING; + } + w = fax_parms[4].length; + if (w > 20) + w = 20; + ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = (byte) w; + for (i = 0; i < w; i++) + ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id[i] = fax_parms[4].info[1 + i]; + ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0; + len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH; + w = fax_parms[5].length; + if (w > 20) + w = 20; + plci->fax_connect_info_buffer[len++] = (byte) w; + for (i = 0; i < w; i++) + plci->fax_connect_info_buffer[len++] = fax_parms[5].info[1 + i]; + w = fax_parms[6].length; + if (w > 20) + w = 20; + plci->fax_connect_info_buffer[len++] = (byte) w; + for (i = 0; i < w; i++) + plci->fax_connect_info_buffer[len++] = fax_parms[6].info[1 + i]; + if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id - 1]) + & (1L << PRIVATE_FAX_NONSTANDARD)) + { + if (api_parse(&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms)) + { + dbug(1, dprintf("non-standard facilities info missing or wrong format")); + plci->fax_connect_info_buffer[len++] = 0; + } + else + { + if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2)) + plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]); + plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length); + for (i = 0; i < fax_parms[7].length; i++) + plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1 + i]; + } + } + } + } + else + { + len = offsetof(T30_INFO, universal_6); + } + fax_info_change = true; + + } + if (fax_control_bits != GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low)) + { + PUT_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low, fax_control_bits); + fax_info_change = true; + } + } + if (Info == GOOD) + { + plci->fax_connect_info_length = len; + if (fax_info_change) + { + if (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS) + { + start_internal_command(Id, plci, fax_connect_info_command); + return false; + } + else + { + start_internal_command(Id, plci, fax_adjust_b23_command); + return false; + } + } + } + } + else Info = _WRONG_STATE; + } + else Info = _WRONG_STATE; + } + + else if (plci->B3_prot == B3_RTP) + { + plci->internal_req_buffer[0] = ncpi->length + 1; + plci->internal_req_buffer[1] = UDATA_REQUEST_RTP_RECONFIGURE; + for (w = 0; w < ncpi->length; w++) + plci->internal_req_buffer[2 + w] = ncpi->info[1 + w]; + start_internal_command(Id, plci, rtp_connect_b3_req_command); + return false; + } + + if (!Info) + { + nl_req_ncci(plci, req, 0); + return 1; + } + } + } + else Info = _WRONG_IDENTIFIER; + + sendf(appl, + _CONNECT_B3_R | CONFIRM, + Id, + Number, + "w", Info); + return false; +} + +static byte connect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word ncci; + API_PARSE *ncpi; + byte req; + + word w; + + + API_PARSE fax_parms[9]; + word i; + byte len; + + + dbug(1, dprintf("connect_b3_res")); + + ncci = (word)(Id >> 16); + if (plci && ncci) { + if (a->ncci_state[ncci] == INC_CON_PENDING) { + if (GET_WORD(&parms[0].info[0]) != 0) + { + a->ncci_state[ncci] = OUTG_REJ_PENDING; + channel_request_xon(plci, a->ncci_ch[ncci]); + channel_xmit_xon(plci); + cleanup_ncci_data(plci, ncci); + nl_req_ncci(plci, N_DISC, (byte)ncci); + return 1; + } + a->ncci_state[ncci] = INC_ACT_PENDING; + + req = N_CONNECT_ACK; + ncpi = &parms[1]; + if ((plci->B3_prot == 4) || (plci->B3_prot == 5) || (plci->B3_prot == 7)) + { + + if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id - 1]) + & (1L << PRIVATE_FAX_NONSTANDARD)) + { + if (((plci->B3_prot == 4) || (plci->B3_prot == 5)) + && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF) + && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP)) + { + len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH; + if (plci->fax_connect_info_length < len) + { + ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0; + ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0; + } + if (api_parse(&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms)) + { + dbug(1, dprintf("non-standard facilities info missing or wrong format")); + } + else + { + if (plci->fax_connect_info_length <= len) + plci->fax_connect_info_buffer[len] = 0; + len += 1 + plci->fax_connect_info_buffer[len]; + if (plci->fax_connect_info_length <= len) + plci->fax_connect_info_buffer[len] = 0; + len += 1 + plci->fax_connect_info_buffer[len]; + if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2)) + plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]); + plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length); + for (i = 0; i < fax_parms[7].length; i++) + plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1 + i]; + } + plci->fax_connect_info_length = len; + ((T30_INFO *)(plci->fax_connect_info_buffer))->code = 0; + start_internal_command(Id, plci, fax_connect_ack_command); + return false; + } + } + + nl_req_ncci(plci, req, (byte)ncci); + if ((plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT) + && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT)) + { + if (plci->B3_prot == 4) + sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", ""); + else + sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer); + plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT; + } + } + + else if (plci->B3_prot == B3_RTP) + { + plci->internal_req_buffer[0] = ncpi->length + 1; + plci->internal_req_buffer[1] = UDATA_REQUEST_RTP_RECONFIGURE; + for (w = 0; w < ncpi->length; w++) + plci->internal_req_buffer[2 + w] = ncpi->info[1+w]; + start_internal_command(Id, plci, rtp_connect_b3_res_command); + return false; + } + + else + { + if (ncpi->length > 2) { + if (ncpi->info[1] & 1) req = N_CONNECT_ACK | N_D_BIT; + add_d(plci, (word)(ncpi->length - 3), &ncpi->info[4]); + } + nl_req_ncci(plci, req, (byte)ncci); + sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", ""); + if (plci->adjust_b_restore) + { + plci->adjust_b_restore = false; + start_internal_command(Id, plci, adjust_b_restore); + } + } + return 1; + } + } + return false; +} + +static byte connect_b3_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word ncci; + + ncci = (word)(Id >> 16); + dbug(1, dprintf("connect_b3_a_res(ncci=0x%x)", ncci)); + + if (plci && ncci && (plci->State != IDLE) && (plci->State != INC_DIS_PENDING) + && (plci->State != OUTG_DIS_PENDING)) + { + if (a->ncci_state[ncci] == INC_ACT_PENDING) { + a->ncci_state[ncci] = CONNECTED; + if (plci->State != INC_CON_CONNECTED_ALERT) plci->State = CONNECTED; + channel_request_xon(plci, a->ncci_ch[ncci]); + channel_xmit_xon(plci); + } + } + return false; +} + +static byte disconnect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word Info; + word ncci; + API_PARSE *ncpi; + + dbug(1, dprintf("disconnect_b3_req")); + + Info = _WRONG_IDENTIFIER; + ncci = (word)(Id >> 16); + if (plci && ncci) + { + Info = _WRONG_STATE; + if ((a->ncci_state[ncci] == CONNECTED) + || (a->ncci_state[ncci] == OUTG_CON_PENDING) + || (a->ncci_state[ncci] == INC_CON_PENDING) + || (a->ncci_state[ncci] == INC_ACT_PENDING)) + { + a->ncci_state[ncci] = OUTG_DIS_PENDING; + channel_request_xon(plci, a->ncci_ch[ncci]); + channel_xmit_xon(plci); + + if (a->ncci[ncci].data_pending + && ((plci->B3_prot == B3_TRANSPARENT) + || (plci->B3_prot == B3_T30) + || (plci->B3_prot == B3_T30_WITH_EXTENSIONS))) + { + plci->send_disc = (byte)ncci; + plci->command = 0; + return false; + } + else + { + cleanup_ncci_data(plci, ncci); + + if (plci->B3_prot == 2 || plci->B3_prot == 3) + { + ncpi = &parms[0]; + if (ncpi->length > 3) + { + add_d(plci, (word)(ncpi->length - 3), (byte *)&(ncpi->info[4])); + } + } + nl_req_ncci(plci, N_DISC, (byte)ncci); + } + return 1; + } + } + sendf(appl, + _DISCONNECT_B3_R | CONFIRM, + Id, + Number, + "w", Info); + return false; +} + +static byte disconnect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word ncci; + word i; + + ncci = (word)(Id >> 16); + dbug(1, dprintf("disconnect_b3_res(ncci=0x%x", ncci)); + if (plci && ncci) { + plci->requested_options_conn = 0; + plci->fax_connect_info_length = 0; + plci->ncpi_state = 0x00; + if (((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE)) + && ((plci->B2_prot != B2_LAPD) && (plci->B2_prot != B2_LAPD_FREE_SAPI_SEL))) + { + plci->call_dir |= CALL_DIR_FORCE_OUTG_NL; + } + for (i = 0; i < MAX_CHANNELS_PER_PLCI && plci->inc_dis_ncci_table[i] != (byte)ncci; i++); + if (i < MAX_CHANNELS_PER_PLCI) { + if (plci->channels)plci->channels--; + for (; i < MAX_CHANNELS_PER_PLCI - 1; i++) plci->inc_dis_ncci_table[i] = plci->inc_dis_ncci_table[i + 1]; + plci->inc_dis_ncci_table[MAX_CHANNELS_PER_PLCI - 1] = 0; + + ncci_free_receive_buffers(plci, ncci); + + if ((plci->State == IDLE || plci->State == SUSPENDING) && !plci->channels) { + if (plci->State == SUSPENDING) { + sendf(plci->appl, + _FACILITY_I, + Id & 0xffffL, + 0, + "ws", (word)3, "\x03\x04\x00\x00"); + sendf(plci->appl, _DISCONNECT_I, Id & 0xffffL, 0, "w", 0); + } + plci_remove(plci); + plci->State = IDLE; + } + } + else + { + if ((a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS) + && ((plci->B3_prot == 4) || (plci->B3_prot == 5)) + && (a->ncci_state[ncci] == INC_DIS_PENDING)) + { + ncci_free_receive_buffers(plci, ncci); + + nl_req_ncci(plci, N_EDATA, (byte)ncci); + + plci->adapter->ncci_state[ncci] = IDLE; + start_internal_command(Id, plci, fax_disconnect_command); + return 1; + } + } + } + return false; +} + +static byte data_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + NCCI *ncci_ptr; + DATA_B3_DESC *data; + word Info; + word ncci; + word i; + + dbug(1, dprintf("data_b3_req")); + + Info = _WRONG_IDENTIFIER; + ncci = (word)(Id >> 16); + dbug(1, dprintf("ncci=0x%x, plci=0x%x", ncci, plci)); + + if (plci && ncci) + { + Info = _WRONG_STATE; + if ((a->ncci_state[ncci] == CONNECTED) + || (a->ncci_state[ncci] == INC_ACT_PENDING)) + { + /* queue data */ + ncci_ptr = &(a->ncci[ncci]); + i = ncci_ptr->data_out + ncci_ptr->data_pending; + if (i >= MAX_DATA_B3) + i -= MAX_DATA_B3; + data = &(ncci_ptr->DBuffer[i]); + data->Number = Number; + if ((((byte *)(parms[0].info)) >= ((byte *)(plci->msg_in_queue))) + && (((byte *)(parms[0].info)) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue))) + { + + data->P = (byte *)(long)(*((dword *)(parms[0].info))); + + } + else + data->P = TransmitBufferSet(appl, *(dword *)parms[0].info); + data->Length = GET_WORD(parms[1].info); + data->Handle = GET_WORD(parms[2].info); + data->Flags = GET_WORD(parms[3].info); + (ncci_ptr->data_pending)++; + + /* check for delivery confirmation */ + if (data->Flags & 0x0004) + { + i = ncci_ptr->data_ack_out + ncci_ptr->data_ack_pending; + if (i >= MAX_DATA_ACK) + i -= MAX_DATA_ACK; + ncci_ptr->DataAck[i].Number = data->Number; + ncci_ptr->DataAck[i].Handle = data->Handle; + (ncci_ptr->data_ack_pending)++; + } + + send_data(plci); + return false; + } + } + if (appl) + { + if (plci) + { + if ((((byte *)(parms[0].info)) >= ((byte *)(plci->msg_in_queue))) + && (((byte *)(parms[0].info)) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue))) + { + + TransmitBufferFree(appl, (byte *)(long)(*((dword *)(parms[0].info)))); + + } + } + sendf(appl, + _DATA_B3_R | CONFIRM, + Id, + Number, + "ww", GET_WORD(parms[2].info), Info); + } + return false; +} + +static byte data_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word n; + word ncci; + word NCCIcode; + + dbug(1, dprintf("data_b3_res")); + + ncci = (word)(Id >> 16); + if (plci && ncci) { + n = GET_WORD(parms[0].info); + dbug(1, dprintf("free(%d)", n)); + NCCIcode = ncci | (((word) a->Id) << 8); + if (n < appl->MaxBuffer && + appl->DataNCCI[n] == NCCIcode && + (byte)(appl->DataFlags[n] >> 8) == plci->Id) { + dbug(1, dprintf("found")); + appl->DataNCCI[n] = 0; + + if (channel_can_xon(plci, a->ncci_ch[ncci])) { + channel_request_xon(plci, a->ncci_ch[ncci]); + } + channel_xmit_xon(plci); + + if (appl->DataFlags[n] & 4) { + nl_req_ncci(plci, N_DATA_ACK, (byte)ncci); + return 1; + } + } + } + return false; +} + +static byte reset_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word Info; + word ncci; + + dbug(1, dprintf("reset_b3_req")); + + Info = _WRONG_IDENTIFIER; + ncci = (word)(Id >> 16); + if (plci && ncci) + { + Info = _WRONG_STATE; + switch (plci->B3_prot) + { + case B3_ISO8208: + case B3_X25_DCE: + if (a->ncci_state[ncci] == CONNECTED) + { + nl_req_ncci(plci, N_RESET, (byte)ncci); + send_req(plci); + Info = GOOD; + } + break; + case B3_TRANSPARENT: + if (a->ncci_state[ncci] == CONNECTED) + { + start_internal_command(Id, plci, reset_b3_command); + Info = GOOD; + } + break; + } + } + /* reset_b3 must result in a reset_b3_con & reset_b3_Ind */ + sendf(appl, + _RESET_B3_R | CONFIRM, + Id, + Number, + "w", Info); + return false; +} + +static byte reset_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word ncci; + + dbug(1, dprintf("reset_b3_res")); + + ncci = (word)(Id >> 16); + if (plci && ncci) { + switch (plci->B3_prot) + { + case B3_ISO8208: + case B3_X25_DCE: + if (a->ncci_state[ncci] == INC_RES_PENDING) + { + a->ncci_state[ncci] = CONNECTED; + nl_req_ncci(plci, N_RESET_ACK, (byte)ncci); + return true; + } + break; + } + } + return false; +} + +static byte connect_b3_t90_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word ncci; + API_PARSE *ncpi; + byte req; + + dbug(1, dprintf("connect_b3_t90_a_res")); + + ncci = (word)(Id >> 16); + if (plci && ncci) { + if (a->ncci_state[ncci] == INC_ACT_PENDING) { + a->ncci_state[ncci] = CONNECTED; + } + else if (a->ncci_state[ncci] == INC_CON_PENDING) { + a->ncci_state[ncci] = CONNECTED; + + req = N_CONNECT_ACK; + + /* parms[0]==0 for CAPI original message definition! */ + if (parms[0].info) { + ncpi = &parms[1]; + if (ncpi->length > 2) { + if (ncpi->info[1] & 1) req = N_CONNECT_ACK | N_D_BIT; + add_d(plci, (word)(ncpi->length - 3), &ncpi->info[4]); + } + } + nl_req_ncci(plci, req, (byte)ncci); + return 1; + } + } + return false; +} + + +static byte select_b_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *msg) +{ + word Info = 0; + word i; + byte tel; + API_PARSE bp_parms[7]; + + if (!plci || !msg) + { + Info = _WRONG_IDENTIFIER; + } + else + { + dbug(1, dprintf("select_b_req[%d],PLCI=0x%x,Tel=0x%x,NL=0x%x,appl=0x%x,sstate=0x%x", + msg->length, plci->Id, plci->tel, plci->NL.Id, plci->appl, plci->SuppState)); + dbug(1, dprintf("PlciState=0x%x", plci->State)); + for (i = 0; i < 7; i++) bp_parms[i].length = 0; + + /* check if no channel is open, no B3 connected only */ + if ((plci->State == IDLE) || (plci->State == OUTG_DIS_PENDING) || (plci->State == INC_DIS_PENDING) + || (plci->SuppState != IDLE) || plci->channels || plci->nl_remove_id) + { + Info = _WRONG_STATE; + } + /* check message format and fill bp_parms pointer */ + else if (msg->length && api_parse(&msg->info[1], (word)msg->length, "wwwsss", bp_parms)) + { + Info = _WRONG_MESSAGE_FORMAT; + } + else + { + if ((plci->State == INC_CON_PENDING) || (plci->State == INC_CON_ALERT)) /* send alert tone inband to the network, */ + { /* e.g. Qsig or RBS or Cornet-N or xess PRI */ + if (Id & EXT_CONTROLLER) + { + sendf(appl, _SELECT_B_REQ | CONFIRM, Id, Number, "w", 0x2002); /* wrong controller */ + return 0; + } + plci->State = INC_CON_CONNECTED_ALERT; + plci->appl = appl; + __clear_bit(appl->Id - 1, plci->c_ind_mask_table); + dbug(1, dprintf("c_ind_mask =%*pb", MAX_APPL, plci->c_ind_mask_table)); + /* disconnect the other appls its quasi a connect */ + for_each_set_bit(i, plci->c_ind_mask_table, max_appl) + sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED); + } + + api_save_msg(msg, "s", &plci->saved_msg); + tel = plci->tel; + if (Id & EXT_CONTROLLER) + { + if (tel) /* external controller in use by this PLCI */ + { + if (a->AdvSignalAppl && a->AdvSignalAppl != appl) + { + dbug(1, dprintf("Ext_Ctrl in use 1")); + Info = _WRONG_STATE; + } + } + else /* external controller NOT in use by this PLCI ? */ + { + if (a->AdvSignalPLCI) + { + dbug(1, dprintf("Ext_Ctrl in use 2")); + Info = _WRONG_STATE; + } + else /* activate the codec */ + { + dbug(1, dprintf("Ext_Ctrl start")); + if (AdvCodecSupport(a, plci, appl, 0)) + { + dbug(1, dprintf("Error in codec procedures")); + Info = _WRONG_STATE; + } + else if (plci->spoofed_msg == SPOOFING_REQUIRED) /* wait until codec is active */ + { + plci->spoofed_msg = AWAITING_SELECT_B; + plci->internal_command = BLOCK_PLCI; /* lock other commands */ + plci->command = 0; + dbug(1, dprintf("continue if codec loaded")); + return false; + } + } + } + } + else /* external controller bit is OFF */ + { + if (tel) /* external controller in use, need to switch off */ + { + if (a->AdvSignalAppl == appl) + { + CodecIdCheck(a, plci); + plci->tel = 0; + plci->adv_nl = 0; + dbug(1, dprintf("Ext_Ctrl disable")); + } + else + { + dbug(1, dprintf("Ext_Ctrl not requested")); + } + } + } + if (!Info) + { + if (plci->call_dir & CALL_DIR_OUT) + plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE; + else if (plci->call_dir & CALL_DIR_IN) + plci->call_dir = CALL_DIR_IN | CALL_DIR_ANSWER; + start_internal_command(Id, plci, select_b_command); + return false; + } + } + } + sendf(appl, _SELECT_B_REQ | CONFIRM, Id, Number, "w", Info); + return false; +} + +static byte manufacturer_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *parms) +{ + word command; + word i; + word ncci; + API_PARSE *m; + API_PARSE m_parms[5]; + word codec; + byte req; + byte ch; + byte dir; + static byte chi[2] = {0x01, 0x00}; + static byte lli[2] = {0x01, 0x00}; + static byte codec_cai[2] = {0x01, 0x01}; + static byte null_msg = {0}; + static API_PARSE null_parms = { 0, &null_msg }; + PLCI *v_plci; + word Info = 0; + + dbug(1, dprintf("manufacturer_req")); + for (i = 0; i < 5; i++) m_parms[i].length = 0; + + if (GET_DWORD(parms[0].info) != _DI_MANU_ID) { + Info = _WRONG_MESSAGE_FORMAT; + } + command = GET_WORD(parms[1].info); + m = &parms[2]; + if (!Info) + { + switch (command) { + case _DI_ASSIGN_PLCI: + if (api_parse(&m->info[1], (word)m->length, "wbbs", m_parms)) { + Info = _WRONG_MESSAGE_FORMAT; + break; + } + codec = GET_WORD(m_parms[0].info); + ch = m_parms[1].info[0]; + dir = m_parms[2].info[0]; + if ((i = get_plci(a))) { + plci = &a->plci[i - 1]; + plci->appl = appl; + plci->command = _MANUFACTURER_R; + plci->m_command = command; + plci->number = Number; + plci->State = LOCAL_CONNECT; + Id = (((word)plci->Id << 8) | plci->adapter->Id | 0x80); + dbug(1, dprintf("ManCMD,plci=0x%x", Id)); + + if ((ch == 1 || ch == 2) && (dir <= 2)) { + chi[1] = (byte)(0x80 | ch); + lli[1] = 0; + plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE; + switch (codec) + { + case 0: + Info = add_b1(plci, &m_parms[3], 0, 0); + break; + case 1: + add_p(plci, CAI, codec_cai); + break; + /* manual 'swich on' to the codec support without signalling */ + /* first 'assign plci' with this function, then use */ + case 2: + if (AdvCodecSupport(a, plci, appl, 0)) { + Info = _RESOURCE_ERROR; + } + else { + Info = add_b1(plci, &null_parms, 0, B1_FACILITY_LOCAL); + lli[1] = 0x10; /* local call codec stream */ + } + break; + } + + plci->State = LOCAL_CONNECT; + plci->manufacturer = true; + plci->command = _MANUFACTURER_R; + plci->m_command = command; + plci->number = Number; + + if (!Info) + { + add_p(plci, LLI, lli); + add_p(plci, CHI, chi); + add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(plci, ASSIGN, DSIG_ID); + + if (!codec) + { + Info = add_b23(plci, &m_parms[3]); + if (!Info) + { + nl_req_ncci(plci, ASSIGN, 0); + send_req(plci); + } + } + if (!Info) + { + dbug(1, dprintf("dir=0x%x,spoof=0x%x", dir, plci->spoofed_msg)); + if (plci->spoofed_msg == SPOOFING_REQUIRED) + { + api_save_msg(m_parms, "wbbs", &plci->saved_msg); + plci->spoofed_msg = AWAITING_MANUF_CON; + plci->internal_command = BLOCK_PLCI; /* reject other req meanwhile */ + plci->command = 0; + send_req(plci); + return false; + } + if (dir == 1) { + sig_req(plci, CALL_REQ, 0); + } + else if (!dir) { + sig_req(plci, LISTEN_REQ, 0); + } + send_req(plci); + } + else + { + sendf(appl, + _MANUFACTURER_R | CONFIRM, + Id, + Number, + "dww", _DI_MANU_ID, command, Info); + return 2; + } + } + } + } + else Info = _OUT_OF_PLCI; + break; + + case _DI_IDI_CTRL: + if (!plci) + { + Info = _WRONG_IDENTIFIER; + break; + } + if (api_parse(&m->info[1], (word)m->length, "bs", m_parms)) { + Info = _WRONG_MESSAGE_FORMAT; + break; + } + req = m_parms[0].info[0]; + plci->command = _MANUFACTURER_R; + plci->m_command = command; + plci->number = Number; + if (req == CALL_REQ) + { + plci->b_channel = getChannel(&m_parms[1]); + mixer_set_bchannel_id_esc(plci, plci->b_channel); + if (plci->spoofed_msg == SPOOFING_REQUIRED) + { + plci->spoofed_msg = CALL_REQ | AWAITING_MANUF_CON; + plci->internal_command = BLOCK_PLCI; /* reject other req meanwhile */ + plci->command = 0; + break; + } + } + else if (req == LAW_REQ) + { + plci->cr_enquiry = true; + } + add_ss(plci, FTY, &m_parms[1]); + sig_req(plci, req, 0); + send_req(plci); + if (req == HANGUP) + { + if (plci->NL.Id && !plci->nl_remove_id) + { + if (plci->channels) + { + for (ncci = 1; ncci < MAX_NCCI + 1; ncci++) + { + if ((a->ncci_plci[ncci] == plci->Id) && (a->ncci_state[ncci] == CONNECTED)) + { + a->ncci_state[ncci] = OUTG_DIS_PENDING; + cleanup_ncci_data(plci, ncci); + nl_req_ncci(plci, N_DISC, (byte)ncci); + } + } + } + mixer_remove(plci); + nl_req_ncci(plci, REMOVE, 0); + send_req(plci); + } + } + break; + + case _DI_SIG_CTRL: + /* signalling control for loop activation B-channel */ + if (!plci) + { + Info = _WRONG_IDENTIFIER; + break; + } + if (m->length) { + plci->command = _MANUFACTURER_R; + plci->number = Number; + add_ss(plci, FTY, m); + sig_req(plci, SIG_CTRL, 0); + send_req(plci); + } + else Info = _WRONG_MESSAGE_FORMAT; + break; + + case _DI_RXT_CTRL: + /* activation control for receiver/transmitter B-channel */ + if (!plci) + { + Info = _WRONG_IDENTIFIER; + break; + } + if (m->length) { + plci->command = _MANUFACTURER_R; + plci->number = Number; + add_ss(plci, FTY, m); + sig_req(plci, DSP_CTRL, 0); + send_req(plci); + } + else Info = _WRONG_MESSAGE_FORMAT; + break; + + case _DI_ADV_CODEC: + case _DI_DSP_CTRL: + /* TEL_CTRL commands to support non standard adjustments: */ + /* Ring on/off, Handset micro volume, external micro vol. */ + /* handset+external speaker volume, receiver+transm. gain,*/ + /* handsfree on (hookinfo off), set mixer command */ + + if (command == _DI_ADV_CODEC) + { + if (!a->AdvCodecPLCI) { + Info = _WRONG_STATE; + break; + } + v_plci = a->AdvCodecPLCI; + } + else + { + if (plci + && (m->length >= 3) + && (m->info[1] == 0x1c) + && (m->info[2] >= 1)) + { + if (m->info[3] == DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS) + { + if ((plci->tel != ADV_VOICE) || (plci != a->AdvSignalPLCI)) + { + Info = _WRONG_STATE; + break; + } + a->adv_voice_coef_length = m->info[2] - 1; + if (a->adv_voice_coef_length > m->length - 3) + a->adv_voice_coef_length = (byte)(m->length - 3); + if (a->adv_voice_coef_length > ADV_VOICE_COEF_BUFFER_SIZE) + a->adv_voice_coef_length = ADV_VOICE_COEF_BUFFER_SIZE; + for (i = 0; i < a->adv_voice_coef_length; i++) + a->adv_voice_coef_buffer[i] = m->info[4 + i]; + if (plci->B1_facilities & B1_FACILITY_VOICE) + adv_voice_write_coefs(plci, ADV_VOICE_WRITE_UPDATE); + break; + } + else if (m->info[3] == DSP_CTRL_SET_DTMF_PARAMETERS) + { + if (!(a->manufacturer_features & MANUFACTURER_FEATURE_DTMF_PARAMETERS)) + { + Info = _FACILITY_NOT_SUPPORTED; + break; + } + + plci->dtmf_parameter_length = m->info[2] - 1; + if (plci->dtmf_parameter_length > m->length - 3) + plci->dtmf_parameter_length = (byte)(m->length - 3); + if (plci->dtmf_parameter_length > DTMF_PARAMETER_BUFFER_SIZE) + plci->dtmf_parameter_length = DTMF_PARAMETER_BUFFER_SIZE; + for (i = 0; i < plci->dtmf_parameter_length; i++) + plci->dtmf_parameter_buffer[i] = m->info[4 + i]; + if (plci->B1_facilities & B1_FACILITY_DTMFR) + dtmf_parameter_write(plci); + break; + + } + } + v_plci = plci; + } + + if (!v_plci) + { + Info = _WRONG_IDENTIFIER; + break; + } + if (m->length) { + add_ss(v_plci, FTY, m); + sig_req(v_plci, TEL_CTRL, 0); + send_req(v_plci); + } + else Info = _WRONG_MESSAGE_FORMAT; + + break; + + case _DI_OPTIONS_REQUEST: + if (api_parse(&m->info[1], (word)m->length, "d", m_parms)) { + Info = _WRONG_MESSAGE_FORMAT; + break; + } + if (GET_DWORD(m_parms[0].info) & ~a->man_profile.private_options) + { + Info = _FACILITY_NOT_SUPPORTED; + break; + } + a->requested_options_table[appl->Id - 1] = GET_DWORD(m_parms[0].info); + break; + + + + default: + Info = _WRONG_MESSAGE_FORMAT; + break; + } + } + + sendf(appl, + _MANUFACTURER_R | CONFIRM, + Id, + Number, + "dww", _DI_MANU_ID, command, Info); + return false; +} + + +static byte manufacturer_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, + PLCI *plci, APPL *appl, API_PARSE *msg) +{ + word indication; + + API_PARSE m_parms[3]; + API_PARSE *ncpi; + API_PARSE fax_parms[9]; + word i; + byte len; + + + dbug(1, dprintf("manufacturer_res")); + + if ((msg[0].length == 0) + || (msg[1].length == 0) + || (GET_DWORD(msg[0].info) != _DI_MANU_ID)) + { + return false; + } + indication = GET_WORD(msg[1].info); + switch (indication) + { + + case _DI_NEGOTIATE_B3: + if (!plci) + break; + if (((plci->B3_prot != 4) && (plci->B3_prot != 5)) + || !(plci->ncpi_state & NCPI_NEGOTIATE_B3_SENT)) + { + dbug(1, dprintf("wrong state for NEGOTIATE_B3 parameters")); + break; + } + if (api_parse(&msg[2].info[1], msg[2].length, "ws", m_parms)) + { + dbug(1, dprintf("wrong format in NEGOTIATE_B3 parameters")); + break; + } + ncpi = &m_parms[1]; + len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH; + if (plci->fax_connect_info_length < len) + { + ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0; + ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0; + } + if (api_parse(&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms)) + { + dbug(1, dprintf("non-standard facilities info missing or wrong format")); + } + else + { + if (plci->fax_connect_info_length <= len) + plci->fax_connect_info_buffer[len] = 0; + len += 1 + plci->fax_connect_info_buffer[len]; + if (plci->fax_connect_info_length <= len) + plci->fax_connect_info_buffer[len] = 0; + len += 1 + plci->fax_connect_info_buffer[len]; + if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2)) + plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]); + plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length); + for (i = 0; i < fax_parms[7].length; i++) + plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1 + i]; + } + plci->fax_connect_info_length = len; + plci->fax_edata_ack_length = plci->fax_connect_info_length; + start_internal_command(Id, plci, fax_edata_ack_command); + break; + + } + return false; +} + +/*------------------------------------------------------------------*/ +/* IDI callback function */ +/*------------------------------------------------------------------*/ + +void callback(ENTITY *e) +{ + DIVA_CAPI_ADAPTER *a; + APPL *appl; + PLCI *plci; + CAPI_MSG *m; + word i, j; + byte rc; + byte ch; + byte req; + byte global_req; + int no_cancel_rc; + + dbug(1, dprintf("%x:CB(%x:Req=%x,Rc=%x,Ind=%x)", + (e->user[0] + 1) & 0x7fff, e->Id, e->Req, e->Rc, e->Ind)); + + a = &(adapter[(byte)e->user[0]]); + plci = &(a->plci[e->user[1]]); + no_cancel_rc = DIVA_CAPI_SUPPORTS_NO_CANCEL(a); + + /* + If new protocol code and new XDI is used then CAPI should work + fully in accordance with IDI cpec an look on callback field instead + of Rc field for return codes. + */ + if (((e->complete == 0xff) && no_cancel_rc) || + (e->Rc && !no_cancel_rc)) { + rc = e->Rc; + ch = e->RcCh; + req = e->Req; + e->Rc = 0; + + if (e->user[0] & 0x8000) + { + /* + If REMOVE request was sent then we have to wait until + return code with Id set to zero arrives. + All other return codes should be ignored. + */ + if (req == REMOVE) + { + if (e->Id) + { + dbug(1, dprintf("cancel RC in REMOVE state")); + return; + } + channel_flow_control_remove(plci); + for (i = 0; i < 256; i++) + { + if (a->FlowControlIdTable[i] == plci->nl_remove_id) + a->FlowControlIdTable[i] = 0; + } + plci->nl_remove_id = 0; + if (plci->rx_dma_descriptor > 0) { + diva_free_dma_descriptor(plci, plci->rx_dma_descriptor - 1); + plci->rx_dma_descriptor = 0; + } + } + if (rc == OK_FC) + { + a->FlowControlIdTable[ch] = e->Id; + a->FlowControlSkipTable[ch] = 0; + + a->ch_flow_control[ch] |= N_OK_FC_PENDING; + a->ch_flow_plci[ch] = plci->Id; + plci->nl_req = 0; + } + else + { + /* + Cancel return codes self, if feature was requested + */ + if (no_cancel_rc && (a->FlowControlIdTable[ch] == e->Id) && e->Id) { + a->FlowControlIdTable[ch] = 0; + if ((rc == OK) && a->FlowControlSkipTable[ch]) { + dbug(3, dprintf("XDI CAPI: RC cancelled Id:0x02, Ch:%02x", e->Id, ch)); + return; + } + } + + if (a->ch_flow_control[ch] & N_OK_FC_PENDING) + { + a->ch_flow_control[ch] &= ~N_OK_FC_PENDING; + if (ch == e->ReqCh) + plci->nl_req = 0; + } + else + plci->nl_req = 0; + } + if (plci->nl_req) + control_rc(plci, 0, rc, ch, 0, true); + else + { + if (req == N_XON) + { + channel_x_on(plci, ch); + if (plci->internal_command) + control_rc(plci, req, rc, ch, 0, true); + } + else + { + if (plci->nl_global_req) + { + global_req = plci->nl_global_req; + plci->nl_global_req = 0; + if (rc != ASSIGN_OK) { + e->Id = 0; + if (plci->rx_dma_descriptor > 0) { + diva_free_dma_descriptor(plci, plci->rx_dma_descriptor - 1); + plci->rx_dma_descriptor = 0; + } + } + channel_xmit_xon(plci); + control_rc(plci, 0, rc, ch, global_req, true); + } + else if (plci->data_sent) + { + channel_xmit_xon(plci); + plci->data_sent = false; + plci->NL.XNum = 1; + data_rc(plci, ch); + if (plci->internal_command) + control_rc(plci, req, rc, ch, 0, true); + } + else + { + channel_xmit_xon(plci); + control_rc(plci, req, rc, ch, 0, true); + } + } + } + } + else + { + /* + If REMOVE request was sent then we have to wait until + return code with Id set to zero arrives. + All other return codes should be ignored. + */ + if (req == REMOVE) + { + if (e->Id) + { + dbug(1, dprintf("cancel RC in REMOVE state")); + return; + } + plci->sig_remove_id = 0; + } + plci->sig_req = 0; + if (plci->sig_global_req) + { + global_req = plci->sig_global_req; + plci->sig_global_req = 0; + if (rc != ASSIGN_OK) + e->Id = 0; + channel_xmit_xon(plci); + control_rc(plci, 0, rc, ch, global_req, false); + } + else + { + channel_xmit_xon(plci); + control_rc(plci, req, rc, ch, 0, false); + } + } + /* + Again: in accordance with IDI spec Rc and Ind can't be delivered in the + same callback. Also if new XDI and protocol code used then jump + direct to finish. + */ + if (no_cancel_rc) { + channel_xmit_xon(plci); + goto capi_callback_suffix; + } + } + + channel_xmit_xon(plci); + + if (e->Ind) { + if (e->user[0] & 0x8000) { + byte Ind = e->Ind & 0x0f; + byte Ch = e->IndCh; + if (((Ind == N_DISC) || (Ind == N_DISC_ACK)) && + (a->ch_flow_plci[Ch] == plci->Id)) { + if (a->ch_flow_control[Ch] & N_RX_FLOW_CONTROL_MASK) { + dbug(3, dprintf("XDI CAPI: I: pending N-XON Ch:%02x", Ch)); + } + a->ch_flow_control[Ch] &= ~N_RX_FLOW_CONTROL_MASK; + } + nl_ind(plci); + if ((e->RNR != 1) && + (a->ch_flow_plci[Ch] == plci->Id) && + (a->ch_flow_control[Ch] & N_RX_FLOW_CONTROL_MASK)) { + a->ch_flow_control[Ch] &= ~N_RX_FLOW_CONTROL_MASK; + dbug(3, dprintf("XDI CAPI: I: remove faked N-XON Ch:%02x", Ch)); + } + } else { + sig_ind(plci); + } + e->Ind = 0; + } + +capi_callback_suffix: + + while (!plci->req_in + && !plci->internal_command + && (plci->msg_in_write_pos != plci->msg_in_read_pos)) + { + j = (plci->msg_in_read_pos == plci->msg_in_wrap_pos) ? 0 : plci->msg_in_read_pos; + + i = (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]))->header.length + 3) & 0xfffc; + + m = (CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]); + appl = *((APPL **)(&((byte *)(plci->msg_in_queue))[j + i])); + dbug(1, dprintf("dequeue msg(0x%04x) - write=%d read=%d wrap=%d", + m->header.command, plci->msg_in_write_pos, plci->msg_in_read_pos, plci->msg_in_wrap_pos)); + if (plci->msg_in_read_pos == plci->msg_in_wrap_pos) + { + plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE; + plci->msg_in_read_pos = i + MSG_IN_OVERHEAD; + } + else + { + plci->msg_in_read_pos = j + i + MSG_IN_OVERHEAD; + } + if (plci->msg_in_read_pos == plci->msg_in_write_pos) + { + plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE; + plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE; + } + else if (plci->msg_in_read_pos == plci->msg_in_wrap_pos) + { + plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE; + plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE; + } + i = api_put(appl, m); + if (i != 0) + { + if (m->header.command == _DATA_B3_R) + + TransmitBufferFree(appl, (byte *)(long)(m->info.data_b3_req.Data)); + + dbug(1, dprintf("Error 0x%04x from msg(0x%04x)", i, m->header.command)); + break; + } + + if (plci->li_notify_update) + { + plci->li_notify_update = false; + mixer_notify_update(plci, false); + } + + } + send_data(plci); + send_req(plci); +} + + +static void control_rc(PLCI *plci, byte req, byte rc, byte ch, byte global_req, + byte nl_rc) +{ + dword Id; + dword rId; + word Number; + word Info = 0; + word i; + word ncci; + DIVA_CAPI_ADAPTER *a; + APPL *appl; + PLCI *rplci; + byte SSparms[] = "\x05\x00\x00\x02\x00\x00"; + byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00"; + + if (!plci) { + dbug(0, dprintf("A: control_rc, no plci %02x:%02x:%02x:%02x:%02x", req, rc, ch, global_req, nl_rc)); + return; + } + dbug(1, dprintf("req0_in/out=%d/%d", plci->req_in, plci->req_out)); + if (plci->req_in != plci->req_out) + { + if (nl_rc || (global_req != ASSIGN) || (rc == ASSIGN_OK)) + { + dbug(1, dprintf("req_1return")); + return; + } + /* cancel outstanding request on the PLCI after SIG ASSIGN failure */ + } + plci->req_in = plci->req_in_start = plci->req_out = 0; + dbug(1, dprintf("control_rc")); + + appl = plci->appl; + a = plci->adapter; + ncci = a->ch_ncci[ch]; + if (appl) + { + Id = (((dword)(ncci ? ncci : ch)) << 16) | ((word)plci->Id << 8) | a->Id; + if (plci->tel && plci->SuppState != CALL_HELD) Id |= EXT_CONTROLLER; + Number = plci->number; + dbug(1, dprintf("Contr_RC-Id=%08lx,plci=%x,tel=%x, entity=0x%x, command=0x%x, int_command=0x%x", Id, plci->Id, plci->tel, plci->Sig.Id, plci->command, plci->internal_command)); + dbug(1, dprintf("channels=0x%x", plci->channels)); + if (plci_remove_check(plci)) + return; + if (req == REMOVE && rc == ASSIGN_OK) + { + sig_req(plci, HANGUP, 0); + sig_req(plci, REMOVE, 0); + send_req(plci); + } + if (plci->command) + { + switch (plci->command) + { + case C_HOLD_REQ: + dbug(1, dprintf("HoldRC=0x%x", rc)); + SSparms[1] = (byte)S_HOLD; + if (rc != OK) + { + plci->SuppState = IDLE; + Info = 0x2001; + } + sendf(appl, _FACILITY_R | CONFIRM, Id, Number, "wws", Info, 3, SSparms); + break; + + case C_RETRIEVE_REQ: + dbug(1, dprintf("RetrieveRC=0x%x", rc)); + SSparms[1] = (byte)S_RETRIEVE; + if (rc != OK) + { + plci->SuppState = CALL_HELD; + Info = 0x2001; + } + sendf(appl, _FACILITY_R | CONFIRM, Id, Number, "wws", Info, 3, SSparms); + break; + + case _INFO_R: + dbug(1, dprintf("InfoRC=0x%x", rc)); + if (rc != OK) Info = _WRONG_STATE; + sendf(appl, _INFO_R | CONFIRM, Id, Number, "w", Info); + break; + + case _CONNECT_R: + dbug(1, dprintf("Connect_R=0x%x/0x%x/0x%x/0x%x", req, rc, global_req, nl_rc)); + if (plci->State == INC_DIS_PENDING) + break; + if (plci->Sig.Id != 0xff) + { + if (((global_req == ASSIGN) && (rc != ASSIGN_OK)) + || (!nl_rc && (req == CALL_REQ) && (rc != OK))) + { + dbug(1, dprintf("No more IDs/Call_Req failed")); + sendf(appl, _CONNECT_R | CONFIRM, Id & 0xffL, Number, "w", _OUT_OF_PLCI); + plci_remove(plci); + plci->State = IDLE; + break; + } + if (plci->State != LOCAL_CONNECT) plci->State = OUTG_CON_PENDING; + sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", 0); + } + else /* D-ch activation */ + { + if (rc != ASSIGN_OK) + { + dbug(1, dprintf("No more IDs/X.25 Call_Req failed")); + sendf(appl, _CONNECT_R | CONFIRM, Id & 0xffL, Number, "w", _OUT_OF_PLCI); + plci_remove(plci); + plci->State = IDLE; + break; + } + sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", 0); + sendf(plci->appl, _CONNECT_ACTIVE_I, Id, 0, "sss", "", "", ""); + plci->State = INC_ACT_PENDING; + } + break; + + case _CONNECT_I | RESPONSE: + if (plci->State != INC_DIS_PENDING) + plci->State = INC_CON_ACCEPT; + break; + + case _DISCONNECT_R: + if (plci->State == INC_DIS_PENDING) + break; + if (plci->Sig.Id != 0xff) + { + plci->State = OUTG_DIS_PENDING; + sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", 0); + } + break; + + case SUSPEND_REQ: + break; + + case RESUME_REQ: + break; + + case _CONNECT_B3_R: + if (rc != OK) + { + sendf(appl, _CONNECT_B3_R | CONFIRM, Id, Number, "w", _WRONG_IDENTIFIER); + break; + } + ncci = get_ncci(plci, ch, 0); + Id = (Id & 0xffff) | (((dword) ncci) << 16); + plci->channels++; + if (req == N_RESET) + { + a->ncci_state[ncci] = INC_ACT_PENDING; + sendf(appl, _CONNECT_B3_R | CONFIRM, Id, Number, "w", 0); + sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", ""); + } + else + { + a->ncci_state[ncci] = OUTG_CON_PENDING; + sendf(appl, _CONNECT_B3_R | CONFIRM, Id, Number, "w", 0); + } + break; + + case _CONNECT_B3_I | RESPONSE: + break; + + case _RESET_B3_R: +/* sendf(appl, _RESET_B3_R | CONFIRM, Id, Number, "w", 0);*/ + break; + + case _DISCONNECT_B3_R: + sendf(appl, _DISCONNECT_B3_R | CONFIRM, Id, Number, "w", 0); + break; + + case _MANUFACTURER_R: + break; + + case PERM_LIST_REQ: + if (rc != OK) + { + Info = _WRONG_IDENTIFIER; + sendf(plci->appl, _CONNECT_R | CONFIRM, Id, Number, "w", Info); + plci_remove(plci); + } + else + sendf(plci->appl, _CONNECT_R | CONFIRM, Id, Number, "w", Info); + break; + + default: + break; + } + plci->command = 0; + } + else if (plci->internal_command) + { + switch (plci->internal_command) + { + case BLOCK_PLCI: + return; + + case GET_MWI_STATE: + if (rc == OK) /* command supported, wait for indication */ + { + return; + } + plci_remove(plci); + break; + + /* Get Supported Services */ + case GETSERV_REQ_PEND: + if (rc == OK) /* command supported, wait for indication */ + { + break; + } + PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY); + sendf(appl, _FACILITY_R | CONFIRM, Id, Number, "wws", 0, 3, SSstruct); + plci_remove(plci); + break; + + case INTERR_DIVERSION_REQ_PEND: /* Interrogate Parameters */ + case INTERR_NUMBERS_REQ_PEND: + case CF_START_PEND: /* Call Forwarding Start pending */ + case CF_STOP_PEND: /* Call Forwarding Stop pending */ + case CCBS_REQUEST_REQ_PEND: + case CCBS_DEACTIVATE_REQ_PEND: + case CCBS_INTERROGATE_REQ_PEND: + switch (plci->internal_command) + { + case INTERR_DIVERSION_REQ_PEND: + SSparms[1] = S_INTERROGATE_DIVERSION; + break; + case INTERR_NUMBERS_REQ_PEND: + SSparms[1] = S_INTERROGATE_NUMBERS; + break; + case CF_START_PEND: + SSparms[1] = S_CALL_FORWARDING_START; + break; + case CF_STOP_PEND: + SSparms[1] = S_CALL_FORWARDING_STOP; + break; + case CCBS_REQUEST_REQ_PEND: + SSparms[1] = S_CCBS_REQUEST; + break; + case CCBS_DEACTIVATE_REQ_PEND: + SSparms[1] = S_CCBS_DEACTIVATE; + break; + case CCBS_INTERROGATE_REQ_PEND: + SSparms[1] = S_CCBS_INTERROGATE; + break; + } + if (global_req == ASSIGN) + { + dbug(1, dprintf("AssignDiversion_RC=0x%x/0x%x", req, rc)); + return; + } + if (!plci->appl) break; + if (rc == ISDN_GUARD_REJ) + { + Info = _CAPI_GUARD_ERROR; + } + else if (rc != OK) + { + Info = _SUPPLEMENTARY_SERVICE_NOT_SUPPORTED; + } + sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0x7, + plci->number, "wws", Info, (word)3, SSparms); + if (Info) plci_remove(plci); + break; + + /* 3pty conference pending */ + case PTY_REQ_PEND: + if (!plci->relatedPTYPLCI) break; + rplci = plci->relatedPTYPLCI; + SSparms[1] = plci->ptyState; + rId = ((word)rplci->Id << 8) | rplci->adapter->Id; + if (rplci->tel) rId |= EXT_CONTROLLER; + if (rc != OK) + { + Info = 0x300E; /* not supported */ + plci->relatedPTYPLCI = NULL; + plci->ptyState = 0; + } + sendf(rplci->appl, + _FACILITY_R | CONFIRM, + rId, + plci->number, + "wws", Info, (word)3, SSparms); + break; + + /* Explicit Call Transfer pending */ + case ECT_REQ_PEND: + dbug(1, dprintf("ECT_RC=0x%x/0x%x", req, rc)); + if (!plci->relatedPTYPLCI) break; + rplci = plci->relatedPTYPLCI; + SSparms[1] = S_ECT; + rId = ((word)rplci->Id << 8) | rplci->adapter->Id; + if (rplci->tel) rId |= EXT_CONTROLLER; + if (rc != OK) + { + Info = 0x300E; /* not supported */ + plci->relatedPTYPLCI = NULL; + plci->ptyState = 0; + } + sendf(rplci->appl, + _FACILITY_R | CONFIRM, + rId, + plci->number, + "wws", Info, (word)3, SSparms); + break; + + case _MANUFACTURER_R: + dbug(1, dprintf("_Manufacturer_R=0x%x/0x%x", req, rc)); + if ((global_req == ASSIGN) && (rc != ASSIGN_OK)) + { + dbug(1, dprintf("No more IDs")); + sendf(appl, _MANUFACTURER_R | CONFIRM, Id, Number, "dww", _DI_MANU_ID, _MANUFACTURER_R, _OUT_OF_PLCI); + plci_remove(plci); /* after codec init, internal codec commands pending */ + } + break; + + case _CONNECT_R: + dbug(1, dprintf("_Connect_R=0x%x/0x%x", req, rc)); + if ((global_req == ASSIGN) && (rc != ASSIGN_OK)) + { + dbug(1, dprintf("No more IDs")); + sendf(appl, _CONNECT_R | CONFIRM, Id & 0xffL, Number, "w", _OUT_OF_PLCI); + plci_remove(plci); /* after codec init, internal codec commands pending */ + } + break; + + case PERM_COD_HOOK: /* finished with Hook_Ind */ + return; + + case PERM_COD_CALL: + dbug(1, dprintf("***Codec Connect_Pending A, Rc = 0x%x", rc)); + plci->internal_command = PERM_COD_CONN_PEND; + return; + + case PERM_COD_ASSIGN: + dbug(1, dprintf("***Codec Assign A, Rc = 0x%x", rc)); + if (rc != ASSIGN_OK) break; + sig_req(plci, CALL_REQ, 0); + send_req(plci); + plci->internal_command = PERM_COD_CALL; + return; + + /* Null Call Reference Request pending */ + case C_NCR_FAC_REQ: + dbug(1, dprintf("NCR_FAC=0x%x/0x%x", req, rc)); + if (global_req == ASSIGN) + { + if (rc == ASSIGN_OK) + { + return; + } + else + { + sendf(appl, _INFO_R | CONFIRM, Id & 0xf, Number, "w", _WRONG_STATE); + appl->NullCREnable = false; + plci_remove(plci); + } + } + else if (req == NCR_FACILITY) + { + if (rc == OK) + { + sendf(appl, _INFO_R | CONFIRM, Id & 0xf, Number, "w", 0); + } + else + { + sendf(appl, _INFO_R | CONFIRM, Id & 0xf, Number, "w", _WRONG_STATE); + appl->NullCREnable = false; + } + plci_remove(plci); + } + break; + + case HOOK_ON_REQ: + if (plci->channels) + { + if (a->ncci_state[ncci] == CONNECTED) + { + a->ncci_state[ncci] = OUTG_DIS_PENDING; + cleanup_ncci_data(plci, ncci); + nl_req_ncci(plci, N_DISC, (byte)ncci); + } + break; + } + break; + + case HOOK_OFF_REQ: + if (plci->State == INC_DIS_PENDING) + break; + sig_req(plci, CALL_REQ, 0); + send_req(plci); + plci->State = OUTG_CON_PENDING; + break; + + + case MWI_ACTIVATE_REQ_PEND: + case MWI_DEACTIVATE_REQ_PEND: + if (global_req == ASSIGN && rc == ASSIGN_OK) + { + dbug(1, dprintf("MWI_REQ assigned")); + return; + } + else if (rc != OK) + { + if (rc == WRONG_IE) + { + Info = 0x2007; /* Illegal message parameter coding */ + dbug(1, dprintf("MWI_REQ invalid parameter")); + } + else + { + Info = 0x300B; /* not supported */ + dbug(1, dprintf("MWI_REQ not supported")); + } + /* 0x3010: Request not allowed in this state */ + PUT_WORD(&SSparms[4], 0x300E); /* SS not supported */ + + } + if (plci->internal_command == MWI_ACTIVATE_REQ_PEND) + { + PUT_WORD(&SSparms[1], S_MWI_ACTIVATE); + } + else PUT_WORD(&SSparms[1], S_MWI_DEACTIVATE); + + if (plci->cr_enquiry) + { + sendf(plci->appl, + _FACILITY_R | CONFIRM, + Id & 0xf, + plci->number, + "wws", Info, (word)3, SSparms); + if (rc != OK) plci_remove(plci); + } + else + { + sendf(plci->appl, + _FACILITY_R | CONFIRM, + Id, + plci->number, + "wws", Info, (word)3, SSparms); + } + break; + + case CONF_BEGIN_REQ_PEND: + case CONF_ADD_REQ_PEND: + case CONF_SPLIT_REQ_PEND: + case CONF_DROP_REQ_PEND: + case CONF_ISOLATE_REQ_PEND: + case CONF_REATTACH_REQ_PEND: + dbug(1, dprintf("CONF_RC=0x%x/0x%x", req, rc)); + if ((plci->internal_command == CONF_ADD_REQ_PEND) && (!plci->relatedPTYPLCI)) break; + rplci = plci; + rId = Id; + switch (plci->internal_command) + { + case CONF_BEGIN_REQ_PEND: + SSparms[1] = S_CONF_BEGIN; + break; + case CONF_ADD_REQ_PEND: + SSparms[1] = S_CONF_ADD; + rplci = plci->relatedPTYPLCI; + rId = ((word)rplci->Id << 8) | rplci->adapter->Id; + break; + case CONF_SPLIT_REQ_PEND: + SSparms[1] = S_CONF_SPLIT; + break; + case CONF_DROP_REQ_PEND: + SSparms[1] = S_CONF_DROP; + break; + case CONF_ISOLATE_REQ_PEND: + SSparms[1] = S_CONF_ISOLATE; + break; + case CONF_REATTACH_REQ_PEND: + SSparms[1] = S_CONF_REATTACH; + break; + } + + if (rc != OK) + { + Info = 0x300E; /* not supported */ + plci->relatedPTYPLCI = NULL; + plci->ptyState = 0; + } + sendf(rplci->appl, + _FACILITY_R | CONFIRM, + rId, + plci->number, + "wws", Info, (word)3, SSparms); + break; + + case VSWITCH_REQ_PEND: + if (rc != OK) + { + if (plci->relatedPTYPLCI) + { + plci->relatedPTYPLCI->vswitchstate = 0; + plci->relatedPTYPLCI->vsprot = 0; + plci->relatedPTYPLCI->vsprotdialect = 0; + } + plci->vswitchstate = 0; + plci->vsprot = 0; + plci->vsprotdialect = 0; + } + else + { + if (plci->relatedPTYPLCI && + plci->vswitchstate == 1 && + plci->relatedPTYPLCI->vswitchstate == 3) /* join complete */ + plci->vswitchstate = 3; + } + break; + + /* Call Deflection Request pending (SSCT) */ + case CD_REQ_PEND: + SSparms[1] = S_CALL_DEFLECTION; + if (rc != OK) + { + Info = 0x300E; /* not supported */ + plci->appl->CDEnable = 0; + } + sendf(plci->appl, _FACILITY_R | CONFIRM, Id, + plci->number, "wws", Info, (word)3, SSparms); + break; + + case RTP_CONNECT_B3_REQ_COMMAND_2: + if (rc == OK) + { + ncci = get_ncci(plci, ch, 0); + Id = (Id & 0xffff) | (((dword) ncci) << 16); + plci->channels++; + a->ncci_state[ncci] = OUTG_CON_PENDING; + } + /* fall through */ + + default: + if (plci->internal_command_queue[0]) + { + (*(plci->internal_command_queue[0]))(Id, plci, rc); + if (plci->internal_command) + return; + } + break; + } + next_internal_command(Id, plci); + } + } + else /* appl==0 */ + { + Id = ((word)plci->Id << 8) | plci->adapter->Id; + if (plci->tel) Id |= EXT_CONTROLLER; + + switch (plci->internal_command) + { + case BLOCK_PLCI: + return; + + case START_L1_SIG_ASSIGN_PEND: + case REM_L1_SIG_ASSIGN_PEND: + if (global_req == ASSIGN) + { + break; + } + else + { + dbug(1, dprintf("***L1 Req rem PLCI")); + plci->internal_command = 0; + sig_req(plci, REMOVE, 0); + send_req(plci); + } + break; + + /* Call Deflection Request pending, just no appl ptr assigned */ + case CD_REQ_PEND: + SSparms[1] = S_CALL_DEFLECTION; + if (rc != OK) + { + Info = 0x300E; /* not supported */ + } + for (i = 0; i < max_appl; i++) + { + if (application[i].CDEnable) + { + if (!application[i].Id) application[i].CDEnable = 0; + else + { + sendf(&application[i], _FACILITY_R | CONFIRM, Id, + plci->number, "wws", Info, (word)3, SSparms); + if (Info) application[i].CDEnable = 0; + } + } + } + plci->internal_command = 0; + break; + + case PERM_COD_HOOK: /* finished with Hook_Ind */ + return; + + case PERM_COD_CALL: + plci->internal_command = PERM_COD_CONN_PEND; + dbug(1, dprintf("***Codec Connect_Pending, Rc = 0x%x", rc)); + return; + + case PERM_COD_ASSIGN: + dbug(1, dprintf("***Codec Assign, Rc = 0x%x", rc)); + plci->internal_command = 0; + if (rc != ASSIGN_OK) break; + plci->internal_command = PERM_COD_CALL; + sig_req(plci, CALL_REQ, 0); + send_req(plci); + return; + + case LISTEN_SIG_ASSIGN_PEND: + if (rc == ASSIGN_OK) + { + plci->internal_command = 0; + dbug(1, dprintf("ListenCheck, new SIG_ID = 0x%x", plci->Sig.Id)); + add_p(plci, ESC, "\x02\x18\x00"); /* support call waiting */ + sig_req(plci, INDICATE_REQ, 0); + send_req(plci); + } + else + { + dbug(1, dprintf("ListenCheck failed (assignRc=0x%x)", rc)); + a->listen_active--; + plci_remove(plci); + plci->State = IDLE; + } + break; + + case USELAW_REQ: + if (global_req == ASSIGN) + { + if (rc == ASSIGN_OK) + { + sig_req(plci, LAW_REQ, 0); + send_req(plci); + dbug(1, dprintf("Auto-Law assigned")); + } + else + { + dbug(1, dprintf("Auto-Law assign failed")); + a->automatic_law = 3; + plci->internal_command = 0; + a->automatic_lawPLCI = NULL; + } + break; + } + else if (req == LAW_REQ && rc == OK) + { + dbug(1, dprintf("Auto-Law initiated")); + a->automatic_law = 2; + plci->internal_command = 0; + } + else + { + dbug(1, dprintf("Auto-Law not supported")); + a->automatic_law = 3; + plci->internal_command = 0; + sig_req(plci, REMOVE, 0); + send_req(plci); + a->automatic_lawPLCI = NULL; + } + break; + } + plci_remove_check(plci); + } +} + +static void data_rc(PLCI *plci, byte ch) +{ + dword Id; + DIVA_CAPI_ADAPTER *a; + NCCI *ncci_ptr; + DATA_B3_DESC *data; + word ncci; + + if (plci->appl) + { + TransmitBufferFree(plci->appl, plci->data_sent_ptr); + a = plci->adapter; + ncci = a->ch_ncci[ch]; + if (ncci && (a->ncci_plci[ncci] == plci->Id)) + { + ncci_ptr = &(a->ncci[ncci]); + dbug(1, dprintf("data_out=%d, data_pending=%d", ncci_ptr->data_out, ncci_ptr->data_pending)); + if (ncci_ptr->data_pending) + { + data = &(ncci_ptr->DBuffer[ncci_ptr->data_out]); + if (!(data->Flags & 4) && a->ncci_state[ncci]) + { + Id = (((dword)ncci) << 16) | ((word)plci->Id << 8) | a->Id; + if (plci->tel) Id |= EXT_CONTROLLER; + sendf(plci->appl, _DATA_B3_R | CONFIRM, Id, data->Number, + "ww", data->Handle, 0); + } + (ncci_ptr->data_out)++; + if (ncci_ptr->data_out == MAX_DATA_B3) + ncci_ptr->data_out = 0; + (ncci_ptr->data_pending)--; + } + } + } +} + +static void data_ack(PLCI *plci, byte ch) +{ + dword Id; + DIVA_CAPI_ADAPTER *a; + NCCI *ncci_ptr; + word ncci; + + a = plci->adapter; + ncci = a->ch_ncci[ch]; + ncci_ptr = &(a->ncci[ncci]); + if (ncci_ptr->data_ack_pending) + { + if (a->ncci_state[ncci] && (a->ncci_plci[ncci] == plci->Id)) + { + Id = (((dword)ncci) << 16) | ((word)plci->Id << 8) | a->Id; + if (plci->tel) Id |= EXT_CONTROLLER; + sendf(plci->appl, _DATA_B3_R | CONFIRM, Id, ncci_ptr->DataAck[ncci_ptr->data_ack_out].Number, + "ww", ncci_ptr->DataAck[ncci_ptr->data_ack_out].Handle, 0); + } + (ncci_ptr->data_ack_out)++; + if (ncci_ptr->data_ack_out == MAX_DATA_ACK) + ncci_ptr->data_ack_out = 0; + (ncci_ptr->data_ack_pending)--; + } +} + +static void sig_ind(PLCI *plci) +{ + dword x_Id; + dword Id; + dword rId; + word i; + word cip; + dword cip_mask; + byte *ie; + DIVA_CAPI_ADAPTER *a; + API_PARSE saved_parms[MAX_MSG_PARMS + 1]; +#define MAXPARMSIDS 31 + byte *parms[MAXPARMSIDS]; + byte *add_i[4]; + byte *multi_fac_parms[MAX_MULTI_IE]; + byte *multi_pi_parms[MAX_MULTI_IE]; + byte *multi_ssext_parms[MAX_MULTI_IE]; + byte *multi_CiPN_parms[MAX_MULTI_IE]; + + byte *multi_vswitch_parms[MAX_MULTI_IE]; + + byte ai_len; + byte *esc_chi = ""; + byte *esc_law = ""; + byte *pty_cai = ""; + byte *esc_cr = ""; + byte *esc_profile = ""; + + byte facility[256]; + PLCI *tplci = NULL; + byte chi[] = "\x02\x18\x01"; + byte voice_cai[] = "\x06\x14\x00\x00\x00\x00\x08"; + byte resume_cau[] = "\x05\x05\x00\x02\x00\x00"; + /* ESC_MSGTYPE must be the last but one message, a new IE has to be */ + /* included before the ESC_MSGTYPE and MAXPARMSIDS has to be incremented */ + /* SMSG is situated at the end because its 0 (for compatibility reasons */ + /* (see Info_Mask Bit 4, first IE. then the message type) */ + static const word parms_id[] = + {MAXPARMSIDS, CPN, 0xff, DSA, OSA, BC, LLC, HLC, ESC_CAUSE, DSP, DT, CHA, + UUI, CONG_RR, CONG_RNR, ESC_CHI, KEY, CHI, CAU, ESC_LAW, + RDN, RDX, CONN_NR, RIN, NI, CAI, ESC_CR, + CST, ESC_PROFILE, 0xff, ESC_MSGTYPE, SMSG}; + /* 14 FTY repl by ESC_CHI */ + /* 18 PI repl by ESC_LAW */ + /* removed OAD changed to 0xff for future use, OAD is multiIE now */ + static const word multi_fac_id[] = {1, FTY}; + static const word multi_pi_id[] = {1, PI}; + static const word multi_CiPN_id[] = {1, OAD}; + static const word multi_ssext_id[] = {1, ESC_SSEXT}; + + static const word multi_vswitch_id[] = {1, ESC_VSWITCH}; + + byte *cau; + word ncci; + byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/ + byte CF_Ind[] = "\x09\x02\x00\x06\x00\x00\x00\x00\x00\x00"; + byte Interr_Err_Ind[] = "\x0a\x02\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + byte CONF_Ind[] = "\x09\x16\x00\x06\x00\x00\x00\x00\x00\x00"; + byte force_mt_info = false; + byte dir; + dword d; + word w; + + a = plci->adapter; + Id = ((word)plci->Id << 8) | a->Id; + PUT_WORD(&SS_Ind[4], 0x0000); + + if (plci->sig_remove_id) + { + plci->Sig.RNR = 2; /* discard */ + dbug(1, dprintf("SIG discard while remove pending")); + return; + } + if (plci->tel && plci->SuppState != CALL_HELD) Id |= EXT_CONTROLLER; + dbug(1, dprintf("SigInd-Id=%08lx,plci=%x,tel=%x,state=0x%x,channels=%d,Discflowcl=%d", + Id, plci->Id, plci->tel, plci->State, plci->channels, plci->hangup_flow_ctrl_timer)); + if (plci->Sig.Ind == CALL_HOLD_ACK && plci->channels) + { + plci->Sig.RNR = 1; + return; + } + if (plci->Sig.Ind == HANGUP && plci->channels) + { + plci->Sig.RNR = 1; + plci->hangup_flow_ctrl_timer++; + /* recover the network layer after timeout */ + if (plci->hangup_flow_ctrl_timer == 100) + { + dbug(1, dprintf("Exceptional disc")); + plci->Sig.RNR = 0; + plci->hangup_flow_ctrl_timer = 0; + for (ncci = 1; ncci < MAX_NCCI + 1; ncci++) + { + if (a->ncci_plci[ncci] == plci->Id) + { + cleanup_ncci_data(plci, ncci); + if (plci->channels)plci->channels--; + if (plci->appl) + sendf(plci->appl, _DISCONNECT_B3_I, (((dword) ncci) << 16) | Id, 0, "ws", 0, ""); + } + } + if (plci->appl) + sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", 0); + plci_remove(plci); + plci->State = IDLE; + } + return; + } + + /* do first parse the info with no OAD in, because OAD will be converted */ + /* first the multiple facility IE, then mult. progress ind. */ + /* then the parameters for the info_ind + conn_ind */ + IndParse(plci, multi_fac_id, multi_fac_parms, MAX_MULTI_IE); + IndParse(plci, multi_pi_id, multi_pi_parms, MAX_MULTI_IE); + IndParse(plci, multi_ssext_id, multi_ssext_parms, MAX_MULTI_IE); + + IndParse(plci, multi_vswitch_id, multi_vswitch_parms, MAX_MULTI_IE); + + IndParse(plci, parms_id, parms, 0); + IndParse(plci, multi_CiPN_id, multi_CiPN_parms, MAX_MULTI_IE); + esc_chi = parms[14]; + esc_law = parms[18]; + pty_cai = parms[24]; + esc_cr = parms[25]; + esc_profile = parms[27]; + if (esc_cr[0] && plci) + { + if (plci->cr_enquiry && plci->appl) + { + plci->cr_enquiry = false; + /* d = MANU_ID */ + /* w = m_command */ + /* b = total length */ + /* b = indication type */ + /* b = length of all IEs */ + /* b = IE1 */ + /* S = IE1 length + cont. */ + /* b = IE2 */ + /* S = IE2 length + cont. */ + sendf(plci->appl, + _MANUFACTURER_I, + Id, + 0, + "dwbbbbSbS", _DI_MANU_ID, plci->m_command, + 2 + 1 + 1 + esc_cr[0] + 1 + 1 + esc_law[0], plci->Sig.Ind, 1 + 1 + esc_cr[0] + 1 + 1 + esc_law[0], ESC, esc_cr, ESC, esc_law); + } + } + /* create the additional info structure */ + add_i[1] = parms[15]; /* KEY of additional info */ + add_i[2] = parms[11]; /* UUI of additional info */ + ai_len = AddInfo(add_i, multi_fac_parms, esc_chi, facility); + + /* the ESC_LAW indicates if u-Law or a-Law is actually used by the card */ + /* indication returns by the card if requested by the function */ + /* AutomaticLaw() after driver init */ + if (a->automatic_law < 4) + { + if (esc_law[0]) { + if (esc_law[2]) { + dbug(0, dprintf("u-Law selected")); + a->u_law = 1; + } + else { + dbug(0, dprintf("a-Law selected")); + a->u_law = 0; + } + a->automatic_law = 4; + if (plci == a->automatic_lawPLCI) { + plci->internal_command = 0; + sig_req(plci, REMOVE, 0); + send_req(plci); + a->automatic_lawPLCI = NULL; + } + } + if (esc_profile[0]) + { + dbug(1, dprintf("[%06x] CardProfile: %lx %lx %lx %lx %lx", + UnMapController(a->Id), GET_DWORD(&esc_profile[6]), + GET_DWORD(&esc_profile[10]), GET_DWORD(&esc_profile[14]), + GET_DWORD(&esc_profile[18]), GET_DWORD(&esc_profile[46]))); + + a->profile.Global_Options &= 0x000000ffL; + a->profile.B1_Protocols &= 0x000003ffL; + a->profile.B2_Protocols &= 0x00001fdfL; + a->profile.B3_Protocols &= 0x000000b7L; + + a->profile.Global_Options &= GET_DWORD(&esc_profile[6]) | + GL_BCHANNEL_OPERATION_SUPPORTED; + a->profile.B1_Protocols &= GET_DWORD(&esc_profile[10]); + a->profile.B2_Protocols &= GET_DWORD(&esc_profile[14]); + a->profile.B3_Protocols &= GET_DWORD(&esc_profile[18]); + a->manufacturer_features = GET_DWORD(&esc_profile[46]); + a->man_profile.private_options = 0; + + if (a->manufacturer_features & MANUFACTURER_FEATURE_ECHO_CANCELLER) + { + a->man_profile.private_options |= 1L << PRIVATE_ECHO_CANCELLER; + a->profile.Global_Options |= GL_ECHO_CANCELLER_SUPPORTED; + } + + + if (a->manufacturer_features & MANUFACTURER_FEATURE_RTP) + a->man_profile.private_options |= 1L << PRIVATE_RTP; + a->man_profile.rtp_primary_payloads = GET_DWORD(&esc_profile[50]); + a->man_profile.rtp_additional_payloads = GET_DWORD(&esc_profile[54]); + + + if (a->manufacturer_features & MANUFACTURER_FEATURE_T38) + a->man_profile.private_options |= 1L << PRIVATE_T38; + + + if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD) + a->man_profile.private_options |= 1L << PRIVATE_FAX_SUB_SEP_PWD; + + + if (a->manufacturer_features & MANUFACTURER_FEATURE_V18) + a->man_profile.private_options |= 1L << PRIVATE_V18; + + + if (a->manufacturer_features & MANUFACTURER_FEATURE_DTMF_TONE) + a->man_profile.private_options |= 1L << PRIVATE_DTMF_TONE; + + + if (a->manufacturer_features & MANUFACTURER_FEATURE_PIAFS) + a->man_profile.private_options |= 1L << PRIVATE_PIAFS; + + + if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS) + a->man_profile.private_options |= 1L << PRIVATE_FAX_PAPER_FORMATS; + + + if (a->manufacturer_features & MANUFACTURER_FEATURE_VOWN) + a->man_profile.private_options |= 1L << PRIVATE_VOWN; + + + if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_NONSTANDARD) + a->man_profile.private_options |= 1L << PRIVATE_FAX_NONSTANDARD; + + } + else + { + a->profile.Global_Options &= 0x0000007fL; + a->profile.B1_Protocols &= 0x000003dfL; + a->profile.B2_Protocols &= 0x00001adfL; + a->profile.B3_Protocols &= 0x000000b7L; + a->manufacturer_features &= MANUFACTURER_FEATURE_HARDDTMF; + } + if (a->manufacturer_features & (MANUFACTURER_FEATURE_HARDDTMF | + MANUFACTURER_FEATURE_SOFTDTMF_SEND | MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)) + { + a->profile.Global_Options |= GL_DTMF_SUPPORTED; + } + a->manufacturer_features &= ~MANUFACTURER_FEATURE_OOB_CHANNEL; + dbug(1, dprintf("[%06x] Profile: %lx %lx %lx %lx %lx", + UnMapController(a->Id), a->profile.Global_Options, + a->profile.B1_Protocols, a->profile.B2_Protocols, + a->profile.B3_Protocols, a->manufacturer_features)); + } + /* codec plci for the handset/hook state support is just an internal id */ + if (plci != a->AdvCodecPLCI) + { + force_mt_info = SendMultiIE(plci, Id, multi_fac_parms, FTY, 0x20, 0); + force_mt_info |= SendMultiIE(plci, Id, multi_pi_parms, PI, 0x210, 0); + SendSSExtInd(NULL, plci, Id, multi_ssext_parms); + SendInfo(plci, Id, parms, force_mt_info); + + VSwitchReqInd(plci, Id, multi_vswitch_parms); + + } + + /* switch the codec to the b-channel */ + if (esc_chi[0] && plci && !plci->SuppState) { + plci->b_channel = esc_chi[esc_chi[0]]&0x1f; + mixer_set_bchannel_id_esc(plci, plci->b_channel); + dbug(1, dprintf("storeChannel=0x%x", plci->b_channel)); + if (plci->tel == ADV_VOICE && plci->appl) { + SetVoiceChannel(a->AdvCodecPLCI, esc_chi, a); + } + } + + if (plci->appl) plci->appl->Number++; + + switch (plci->Sig.Ind) { + /* Response to Get_Supported_Services request */ + case S_SUPPORTED: + dbug(1, dprintf("S_Supported")); + if (!plci->appl) break; + if (pty_cai[0] == 4) + { + PUT_DWORD(&CF_Ind[6], GET_DWORD(&pty_cai[1])); + } + else + { + PUT_DWORD(&CF_Ind[6], MASK_TERMINAL_PORTABILITY | MASK_HOLD_RETRIEVE); + } + PUT_WORD(&CF_Ind[1], 0); + PUT_WORD(&CF_Ind[4], 0); + sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0x7, plci->number, "wws", 0, 3, CF_Ind); + plci_remove(plci); + break; + + /* Supplementary Service rejected */ + case S_SERVICE_REJ: + dbug(1, dprintf("S_Reject=0x%x", pty_cai[5])); + if (!pty_cai[0]) break; + switch (pty_cai[5]) + { + case ECT_EXECUTE: + case THREE_PTY_END: + case THREE_PTY_BEGIN: + if (!plci->relatedPTYPLCI) break; + tplci = plci->relatedPTYPLCI; + rId = ((word)tplci->Id << 8) | tplci->adapter->Id; + if (tplci->tel) rId |= EXT_CONTROLLER; + if (pty_cai[5] == ECT_EXECUTE) + { + PUT_WORD(&SS_Ind[1], S_ECT); + + plci->vswitchstate = 0; + plci->relatedPTYPLCI->vswitchstate = 0; + + } + else + { + PUT_WORD(&SS_Ind[1], pty_cai[5] + 3); + } + if (pty_cai[2] != 0xff) + { + PUT_WORD(&SS_Ind[4], 0x3600 | (word)pty_cai[2]); + } + else + { + PUT_WORD(&SS_Ind[4], 0x300E); + } + plci->relatedPTYPLCI = NULL; + plci->ptyState = 0; + sendf(tplci->appl, _FACILITY_I, rId, 0, "ws", 3, SS_Ind); + break; + + case CALL_DEFLECTION: + if (pty_cai[2] != 0xff) + { + PUT_WORD(&SS_Ind[4], 0x3600 | (word)pty_cai[2]); + } + else + { + PUT_WORD(&SS_Ind[4], 0x300E); + } + PUT_WORD(&SS_Ind[1], pty_cai[5]); + for (i = 0; i < max_appl; i++) + { + if (application[i].CDEnable) + { + if (application[i].Id) sendf(&application[i], _FACILITY_I, Id, 0, "ws", 3, SS_Ind); + application[i].CDEnable = false; + } + } + break; + + case DEACTIVATION_DIVERSION: + case ACTIVATION_DIVERSION: + case DIVERSION_INTERROGATE_CFU: + case DIVERSION_INTERROGATE_CFB: + case DIVERSION_INTERROGATE_CFNR: + case DIVERSION_INTERROGATE_NUM: + case CCBS_REQUEST: + case CCBS_DEACTIVATE: + case CCBS_INTERROGATE: + if (!plci->appl) break; + if (pty_cai[2] != 0xff) + { + PUT_WORD(&Interr_Err_Ind[4], 0x3600 | (word)pty_cai[2]); + } + else + { + PUT_WORD(&Interr_Err_Ind[4], 0x300E); + } + switch (pty_cai[5]) + { + case DEACTIVATION_DIVERSION: + dbug(1, dprintf("Deact_Div")); + Interr_Err_Ind[0] = 0x9; + Interr_Err_Ind[3] = 0x6; + PUT_WORD(&Interr_Err_Ind[1], S_CALL_FORWARDING_STOP); + break; + case ACTIVATION_DIVERSION: + dbug(1, dprintf("Act_Div")); + Interr_Err_Ind[0] = 0x9; + Interr_Err_Ind[3] = 0x6; + PUT_WORD(&Interr_Err_Ind[1], S_CALL_FORWARDING_START); + break; + case DIVERSION_INTERROGATE_CFU: + case DIVERSION_INTERROGATE_CFB: + case DIVERSION_INTERROGATE_CFNR: + dbug(1, dprintf("Interr_Div")); + Interr_Err_Ind[0] = 0xa; + Interr_Err_Ind[3] = 0x7; + PUT_WORD(&Interr_Err_Ind[1], S_INTERROGATE_DIVERSION); + break; + case DIVERSION_INTERROGATE_NUM: + dbug(1, dprintf("Interr_Num")); + Interr_Err_Ind[0] = 0xa; + Interr_Err_Ind[3] = 0x7; + PUT_WORD(&Interr_Err_Ind[1], S_INTERROGATE_NUMBERS); + break; + case CCBS_REQUEST: + dbug(1, dprintf("CCBS Request")); + Interr_Err_Ind[0] = 0xd; + Interr_Err_Ind[3] = 0xa; + PUT_WORD(&Interr_Err_Ind[1], S_CCBS_REQUEST); + break; + case CCBS_DEACTIVATE: + dbug(1, dprintf("CCBS Deactivate")); + Interr_Err_Ind[0] = 0x9; + Interr_Err_Ind[3] = 0x6; + PUT_WORD(&Interr_Err_Ind[1], S_CCBS_DEACTIVATE); + break; + case CCBS_INTERROGATE: + dbug(1, dprintf("CCBS Interrogate")); + Interr_Err_Ind[0] = 0xb; + Interr_Err_Ind[3] = 0x8; + PUT_WORD(&Interr_Err_Ind[1], S_CCBS_INTERROGATE); + break; + } + PUT_DWORD(&Interr_Err_Ind[6], plci->appl->S_Handle); + sendf(plci->appl, _FACILITY_I, Id & 0x7, 0, "ws", 3, Interr_Err_Ind); + plci_remove(plci); + break; + case ACTIVATION_MWI: + case DEACTIVATION_MWI: + if (pty_cai[5] == ACTIVATION_MWI) + { + PUT_WORD(&SS_Ind[1], S_MWI_ACTIVATE); + } + else PUT_WORD(&SS_Ind[1], S_MWI_DEACTIVATE); + + if (pty_cai[2] != 0xff) + { + PUT_WORD(&SS_Ind[4], 0x3600 | (word)pty_cai[2]); + } + else + { + PUT_WORD(&SS_Ind[4], 0x300E); + } + + if (plci->cr_enquiry) + { + sendf(plci->appl, _FACILITY_I, Id & 0xf, 0, "ws", 3, SS_Ind); + plci_remove(plci); + } + else + { + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind); + } + break; + case CONF_ADD: /* ERROR */ + case CONF_BEGIN: + case CONF_DROP: + case CONF_ISOLATE: + case CONF_REATTACH: + CONF_Ind[0] = 9; + CONF_Ind[3] = 6; + switch (pty_cai[5]) + { + case CONF_BEGIN: + PUT_WORD(&CONF_Ind[1], S_CONF_BEGIN); + plci->ptyState = 0; + break; + case CONF_DROP: + CONF_Ind[0] = 5; + CONF_Ind[3] = 2; + PUT_WORD(&CONF_Ind[1], S_CONF_DROP); + plci->ptyState = CONNECTED; + break; + case CONF_ISOLATE: + CONF_Ind[0] = 5; + CONF_Ind[3] = 2; + PUT_WORD(&CONF_Ind[1], S_CONF_ISOLATE); + plci->ptyState = CONNECTED; + break; + case CONF_REATTACH: + CONF_Ind[0] = 5; + CONF_Ind[3] = 2; + PUT_WORD(&CONF_Ind[1], S_CONF_REATTACH); + plci->ptyState = CONNECTED; + break; + case CONF_ADD: + PUT_WORD(&CONF_Ind[1], S_CONF_ADD); + plci->relatedPTYPLCI = NULL; + tplci = plci->relatedPTYPLCI; + if (tplci) tplci->ptyState = CONNECTED; + plci->ptyState = CONNECTED; + break; + } + + if (pty_cai[2] != 0xff) + { + PUT_WORD(&CONF_Ind[4], 0x3600 | (word)pty_cai[2]); + } + else + { + PUT_WORD(&CONF_Ind[4], 0x3303); /* Time-out: network did not respond + within the required time */ + } + + PUT_DWORD(&CONF_Ind[6], 0x0); + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, CONF_Ind); + break; + } + break; + + /* Supplementary Service indicates success */ + case S_SERVICE: + dbug(1, dprintf("Service_Ind")); + PUT_WORD(&CF_Ind[4], 0); + switch (pty_cai[5]) + { + case THREE_PTY_END: + case THREE_PTY_BEGIN: + case ECT_EXECUTE: + if (!plci->relatedPTYPLCI) break; + tplci = plci->relatedPTYPLCI; + rId = ((word)tplci->Id << 8) | tplci->adapter->Id; + if (tplci->tel) rId |= EXT_CONTROLLER; + if (pty_cai[5] == ECT_EXECUTE) + { + PUT_WORD(&SS_Ind[1], S_ECT); + + if (plci->vswitchstate != 3) + { + + plci->ptyState = IDLE; + plci->relatedPTYPLCI = NULL; + plci->ptyState = 0; + + } + + dbug(1, dprintf("ECT OK")); + sendf(tplci->appl, _FACILITY_I, rId, 0, "ws", 3, SS_Ind); + + + + } + else + { + switch (plci->ptyState) + { + case S_3PTY_BEGIN: + plci->ptyState = CONNECTED; + dbug(1, dprintf("3PTY ON")); + break; + + case S_3PTY_END: + plci->ptyState = IDLE; + plci->relatedPTYPLCI = NULL; + plci->ptyState = 0; + dbug(1, dprintf("3PTY OFF")); + break; + } + PUT_WORD(&SS_Ind[1], pty_cai[5] + 3); + sendf(tplci->appl, _FACILITY_I, rId, 0, "ws", 3, SS_Ind); + } + break; + + case CALL_DEFLECTION: + PUT_WORD(&SS_Ind[1], pty_cai[5]); + for (i = 0; i < max_appl; i++) + { + if (application[i].CDEnable) + { + if (application[i].Id) sendf(&application[i], _FACILITY_I, Id, 0, "ws", 3, SS_Ind); + application[i].CDEnable = false; + } + } + break; + + case DEACTIVATION_DIVERSION: + case ACTIVATION_DIVERSION: + if (!plci->appl) break; + PUT_WORD(&CF_Ind[1], pty_cai[5] + 2); + PUT_DWORD(&CF_Ind[6], plci->appl->S_Handle); + sendf(plci->appl, _FACILITY_I, Id & 0x7, 0, "ws", 3, CF_Ind); + plci_remove(plci); + break; + + case DIVERSION_INTERROGATE_CFU: + case DIVERSION_INTERROGATE_CFB: + case DIVERSION_INTERROGATE_CFNR: + case DIVERSION_INTERROGATE_NUM: + case CCBS_REQUEST: + case CCBS_DEACTIVATE: + case CCBS_INTERROGATE: + if (!plci->appl) break; + switch (pty_cai[5]) + { + case DIVERSION_INTERROGATE_CFU: + case DIVERSION_INTERROGATE_CFB: + case DIVERSION_INTERROGATE_CFNR: + dbug(1, dprintf("Interr_Div")); + PUT_WORD(&pty_cai[1], S_INTERROGATE_DIVERSION); + pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */ + break; + case DIVERSION_INTERROGATE_NUM: + dbug(1, dprintf("Interr_Num")); + PUT_WORD(&pty_cai[1], S_INTERROGATE_NUMBERS); + pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */ + break; + case CCBS_REQUEST: + dbug(1, dprintf("CCBS Request")); + PUT_WORD(&pty_cai[1], S_CCBS_REQUEST); + pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */ + break; + case CCBS_DEACTIVATE: + dbug(1, dprintf("CCBS Deactivate")); + PUT_WORD(&pty_cai[1], S_CCBS_DEACTIVATE); + pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */ + break; + case CCBS_INTERROGATE: + dbug(1, dprintf("CCBS Interrogate")); + PUT_WORD(&pty_cai[1], S_CCBS_INTERROGATE); + pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */ + break; + } + PUT_WORD(&pty_cai[4], 0); /* Supplementary Service Reason */ + PUT_DWORD(&pty_cai[6], plci->appl->S_Handle); + sendf(plci->appl, _FACILITY_I, Id & 0x7, 0, "wS", 3, pty_cai); + plci_remove(plci); + break; + + case ACTIVATION_MWI: + case DEACTIVATION_MWI: + if (pty_cai[5] == ACTIVATION_MWI) + { + PUT_WORD(&SS_Ind[1], S_MWI_ACTIVATE); + } + else PUT_WORD(&SS_Ind[1], S_MWI_DEACTIVATE); + if (plci->cr_enquiry) + { + sendf(plci->appl, _FACILITY_I, Id & 0xf, 0, "ws", 3, SS_Ind); + plci_remove(plci); + } + else + { + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind); + } + break; + case MWI_INDICATION: + if (pty_cai[0] >= 0x12) + { + PUT_WORD(&pty_cai[3], S_MWI_INDICATE); + pty_cai[2] = pty_cai[0] - 2; /* len Parameter */ + pty_cai[5] = pty_cai[0] - 5; /* Supplementary Service-specific parameter len */ + if (plci->appl && (a->Notification_Mask[plci->appl->Id - 1] & SMASK_MWI)) + { + if (plci->internal_command == GET_MWI_STATE) /* result on Message Waiting Listen */ + { + sendf(plci->appl, _FACILITY_I, Id & 0xf, 0, "wS", 3, &pty_cai[2]); + plci_remove(plci); + return; + } + else sendf(plci->appl, _FACILITY_I, Id, 0, "wS", 3, &pty_cai[2]); + pty_cai[0] = 0; + } + else + { + for (i = 0; i < max_appl; i++) + { + if (a->Notification_Mask[i]&SMASK_MWI) + { + sendf(&application[i], _FACILITY_I, Id & 0x7, 0, "wS", 3, &pty_cai[2]); + pty_cai[0] = 0; + } + } + } + + if (!pty_cai[0]) + { /* acknowledge */ + facility[2] = 0; /* returncode */ + } + else facility[2] = 0xff; + } + else + { + /* reject */ + facility[2] = 0xff; /* returncode */ + } + facility[0] = 2; + facility[1] = MWI_RESPONSE; /* Function */ + add_p(plci, CAI, facility); + add_p(plci, ESC, multi_ssext_parms[0]); /* remembered parameter -> only one possible */ + sig_req(plci, S_SERVICE, 0); + send_req(plci); + plci->command = 0; + next_internal_command(Id, plci); + break; + case CONF_ADD: /* OK */ + case CONF_BEGIN: + case CONF_DROP: + case CONF_ISOLATE: + case CONF_REATTACH: + case CONF_PARTYDISC: + CONF_Ind[0] = 9; + CONF_Ind[3] = 6; + switch (pty_cai[5]) + { + case CONF_BEGIN: + PUT_WORD(&CONF_Ind[1], S_CONF_BEGIN); + if (pty_cai[0] == 6) + { + d = pty_cai[6]; + PUT_DWORD(&CONF_Ind[6], d); /* PartyID */ + } + else + { + PUT_DWORD(&CONF_Ind[6], 0x0); + } + break; + case CONF_ISOLATE: + PUT_WORD(&CONF_Ind[1], S_CONF_ISOLATE); + CONF_Ind[0] = 5; + CONF_Ind[3] = 2; + break; + case CONF_REATTACH: + PUT_WORD(&CONF_Ind[1], S_CONF_REATTACH); + CONF_Ind[0] = 5; + CONF_Ind[3] = 2; + break; + case CONF_DROP: + PUT_WORD(&CONF_Ind[1], S_CONF_DROP); + CONF_Ind[0] = 5; + CONF_Ind[3] = 2; + break; + case CONF_ADD: + PUT_WORD(&CONF_Ind[1], S_CONF_ADD); + d = pty_cai[6]; + PUT_DWORD(&CONF_Ind[6], d); /* PartyID */ + tplci = plci->relatedPTYPLCI; + if (tplci) tplci->ptyState = CONNECTED; + break; + case CONF_PARTYDISC: + CONF_Ind[0] = 7; + CONF_Ind[3] = 4; + PUT_WORD(&CONF_Ind[1], S_CONF_PARTYDISC); + d = pty_cai[6]; + PUT_DWORD(&CONF_Ind[4], d); /* PartyID */ + break; + } + plci->ptyState = CONNECTED; + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, CONF_Ind); + break; + case CCBS_INFO_RETAIN: + case CCBS_ERASECALLLINKAGEID: + case CCBS_STOP_ALERTING: + CONF_Ind[0] = 5; + CONF_Ind[3] = 2; + switch (pty_cai[5]) + { + case CCBS_INFO_RETAIN: + PUT_WORD(&CONF_Ind[1], S_CCBS_INFO_RETAIN); + break; + case CCBS_STOP_ALERTING: + PUT_WORD(&CONF_Ind[1], S_CCBS_STOP_ALERTING); + break; + case CCBS_ERASECALLLINKAGEID: + PUT_WORD(&CONF_Ind[1], S_CCBS_ERASECALLLINKAGEID); + CONF_Ind[0] = 7; + CONF_Ind[3] = 4; + CONF_Ind[6] = 0; + CONF_Ind[7] = 0; + break; + } + w = pty_cai[6]; + PUT_WORD(&CONF_Ind[4], w); /* PartyID */ + + if (plci->appl && (a->Notification_Mask[plci->appl->Id - 1] & SMASK_CCBS)) + { + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, CONF_Ind); + } + else + { + for (i = 0; i < max_appl; i++) + if (a->Notification_Mask[i] & SMASK_CCBS) + sendf(&application[i], _FACILITY_I, Id & 0x7, 0, "ws", 3, CONF_Ind); + } + break; + } + break; + case CALL_HOLD_REJ: + cau = parms[7]; + if (cau) + { + i = _L3_CAUSE | cau[2]; + if (cau[2] == 0) i = 0x3603; + } + else + { + i = 0x3603; + } + PUT_WORD(&SS_Ind[1], S_HOLD); + PUT_WORD(&SS_Ind[4], i); + if (plci->SuppState == HOLD_REQUEST) + { + plci->SuppState = IDLE; + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind); + } + break; + + case CALL_HOLD_ACK: + if (plci->SuppState == HOLD_REQUEST) + { + plci->SuppState = CALL_HELD; + CodecIdCheck(a, plci); + start_internal_command(Id, plci, hold_save_command); + } + break; + + case CALL_RETRIEVE_REJ: + cau = parms[7]; + if (cau) + { + i = _L3_CAUSE | cau[2]; + if (cau[2] == 0) i = 0x3603; + } + else + { + i = 0x3603; + } + PUT_WORD(&SS_Ind[1], S_RETRIEVE); + PUT_WORD(&SS_Ind[4], i); + if (plci->SuppState == RETRIEVE_REQUEST) + { + plci->SuppState = CALL_HELD; + CodecIdCheck(a, plci); + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind); + } + break; + + case CALL_RETRIEVE_ACK: + PUT_WORD(&SS_Ind[1], S_RETRIEVE); + if (plci->SuppState == RETRIEVE_REQUEST) + { + plci->SuppState = IDLE; + plci->call_dir |= CALL_DIR_FORCE_OUTG_NL; + plci->b_channel = esc_chi[esc_chi[0]]&0x1f; + if (plci->tel) + { + mixer_set_bchannel_id_esc(plci, plci->b_channel); + dbug(1, dprintf("RetrChannel=0x%x", plci->b_channel)); + SetVoiceChannel(a->AdvCodecPLCI, esc_chi, a); + if (plci->B2_prot == B2_TRANSPARENT && plci->B3_prot == B3_TRANSPARENT) + { + dbug(1, dprintf("Get B-ch")); + start_internal_command(Id, plci, retrieve_restore_command); + } + else + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind); + } + else + start_internal_command(Id, plci, retrieve_restore_command); + } + break; + + case INDICATE_IND: + if (plci->State != LISTENING) { + sig_req(plci, HANGUP, 0); + send_req(plci); + break; + } + cip = find_cip(a, parms[4], parms[6]); + cip_mask = 1L << cip; + dbug(1, dprintf("cip=%d,cip_mask=%lx", cip, cip_mask)); + bitmap_zero(plci->c_ind_mask_table, MAX_APPL); + if (!remove_started && !a->adapter_disabled) + { + group_optimization(a, plci); + for_each_set_bit(i, plci->group_optimization_mask_table, max_appl) { + if (application[i].Id + && (a->CIP_Mask[i] & 1 || a->CIP_Mask[i] & cip_mask) + && CPN_filter_ok(parms[0], a, i)) { + dbug(1, dprintf("storedcip_mask[%d]=0x%lx", i, a->CIP_Mask[i])); + __set_bit(i, plci->c_ind_mask_table); + dbug(1, dprintf("c_ind_mask =%*pb", MAX_APPL, plci->c_ind_mask_table)); + plci->State = INC_CON_PENDING; + plci->call_dir = (plci->call_dir & ~(CALL_DIR_OUT | CALL_DIR_ORIGINATE)) | + CALL_DIR_IN | CALL_DIR_ANSWER; + if (esc_chi[0]) { + plci->b_channel = esc_chi[esc_chi[0]] & 0x1f; + mixer_set_bchannel_id_esc(plci, plci->b_channel); + } + /* if a listen on the ext controller is done, check if hook states */ + /* are supported or if just a on board codec must be activated */ + if (a->codec_listen[i] && !a->AdvSignalPLCI) { + if (a->profile.Global_Options & HANDSET) + plci->tel = ADV_VOICE; + else if (a->profile.Global_Options & ON_BOARD_CODEC) + plci->tel = CODEC; + if (plci->tel) Id |= EXT_CONTROLLER; + a->codec_listen[i] = plci; + } + + sendf(&application[i], _CONNECT_I, Id, 0, + "wSSSSSSSbSSSSS", cip, /* CIP */ + parms[0], /* CalledPartyNumber */ + multi_CiPN_parms[0], /* CallingPartyNumber */ + parms[2], /* CalledPartySubad */ + parms[3], /* CallingPartySubad */ + parms[4], /* BearerCapability */ + parms[5], /* LowLC */ + parms[6], /* HighLC */ + ai_len, /* nested struct add_i */ + add_i[0], /* B channel info */ + add_i[1], /* keypad facility */ + add_i[2], /* user user data */ + add_i[3], /* nested facility */ + multi_CiPN_parms[1] /* second CiPN(SCR) */ + ); + SendSSExtInd(&application[i], + plci, + Id, + multi_ssext_parms); + SendSetupInfo(&application[i], + plci, + Id, + parms, + SendMultiIE(plci, Id, multi_pi_parms, PI, 0x210, true)); + } + } + dbug(1, dprintf("c_ind_mask =%*pb", MAX_APPL, plci->c_ind_mask_table)); + } + if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL)) { + sig_req(plci, HANGUP, 0); + send_req(plci); + plci->State = IDLE; + } + plci->notifiedcall = 0; + a->listen_active--; + listen_check(a); + break; + + case CALL_PEND_NOTIFY: + plci->notifiedcall = 1; + listen_check(a); + break; + + case CALL_IND: + case CALL_CON: + if (plci->State == ADVANCED_VOICE_SIG || plci->State == ADVANCED_VOICE_NOSIG) + { + if (plci->internal_command == PERM_COD_CONN_PEND) + { + if (plci->State == ADVANCED_VOICE_NOSIG) + { + dbug(1, dprintf("***Codec OK")); + if (a->AdvSignalPLCI) + { + tplci = a->AdvSignalPLCI; + if (tplci->spoofed_msg) + { + dbug(1, dprintf("***Spoofed Msg(0x%x)", tplci->spoofed_msg)); + tplci->command = 0; + tplci->internal_command = 0; + x_Id = ((word)tplci->Id << 8) | tplci->adapter->Id | 0x80; + switch (tplci->spoofed_msg) + { + case CALL_RES: + tplci->command = _CONNECT_I | RESPONSE; + api_load_msg(&tplci->saved_msg, saved_parms); + add_b1(tplci, &saved_parms[1], 0, tplci->B1_facilities); + if (tplci->adapter->Info_Mask[tplci->appl->Id - 1] & 0x200) + { + /* early B3 connect (CIP mask bit 9) no release after a disc */ + add_p(tplci, LLI, "\x01\x01"); + } + add_s(tplci, CONN_NR, &saved_parms[2]); + add_s(tplci, LLC, &saved_parms[4]); + add_ai(tplci, &saved_parms[5]); + tplci->State = INC_CON_ACCEPT; + sig_req(tplci, CALL_RES, 0); + send_req(tplci); + break; + + case AWAITING_SELECT_B: + dbug(1, dprintf("Select_B continue")); + start_internal_command(x_Id, tplci, select_b_command); + break; + + case AWAITING_MANUF_CON: /* Get_Plci per Manufacturer_Req to ext controller */ + if (!tplci->Sig.Id) + { + dbug(1, dprintf("No SigID!")); + sendf(tplci->appl, _MANUFACTURER_R | CONFIRM, x_Id, tplci->number, "dww", _DI_MANU_ID, _MANUFACTURER_R, _OUT_OF_PLCI); + plci_remove(tplci); + break; + } + tplci->command = _MANUFACTURER_R; + api_load_msg(&tplci->saved_msg, saved_parms); + dir = saved_parms[2].info[0]; + if (dir == 1) { + sig_req(tplci, CALL_REQ, 0); + } + else if (!dir) { + sig_req(tplci, LISTEN_REQ, 0); + } + send_req(tplci); + sendf(tplci->appl, _MANUFACTURER_R | CONFIRM, x_Id, tplci->number, "dww", _DI_MANU_ID, _MANUFACTURER_R, 0); + break; + + case (CALL_REQ | AWAITING_MANUF_CON): + sig_req(tplci, CALL_REQ, 0); + send_req(tplci); + break; + + case CALL_REQ: + if (!tplci->Sig.Id) + { + dbug(1, dprintf("No SigID!")); + sendf(tplci->appl, _CONNECT_R | CONFIRM, tplci->adapter->Id, 0, "w", _OUT_OF_PLCI); + plci_remove(tplci); + break; + } + tplci->command = _CONNECT_R; + api_load_msg(&tplci->saved_msg, saved_parms); + add_s(tplci, CPN, &saved_parms[1]); + add_s(tplci, DSA, &saved_parms[3]); + add_ai(tplci, &saved_parms[9]); + sig_req(tplci, CALL_REQ, 0); + send_req(tplci); + break; + + case CALL_RETRIEVE: + tplci->command = C_RETRIEVE_REQ; + sig_req(tplci, CALL_RETRIEVE, 0); + send_req(tplci); + break; + } + tplci->spoofed_msg = 0; + if (tplci->internal_command == 0) + next_internal_command(x_Id, tplci); + } + } + next_internal_command(Id, plci); + break; + } + dbug(1, dprintf("***Codec Hook Init Req")); + plci->internal_command = PERM_COD_HOOK; + add_p(plci, FTY, "\x01\x09"); /* Get Hook State*/ + sig_req(plci, TEL_CTRL, 0); + send_req(plci); + } + } + else if (plci->command != _MANUFACTURER_R /* old style permanent connect */ + && plci->State != INC_ACT_PENDING) + { + mixer_set_bchannel_id_esc(plci, plci->b_channel); + if (plci->tel == ADV_VOICE && plci->SuppState == IDLE) /* with permanent codec switch on immediately */ + { + chi[2] = plci->b_channel; + SetVoiceChannel(a->AdvCodecPLCI, chi, a); + } + sendf(plci->appl, _CONNECT_ACTIVE_I, Id, 0, "Sss", parms[21], "", ""); + plci->State = INC_ACT_PENDING; + } + break; + + case TEL_CTRL: + ie = multi_fac_parms[0]; /* inspect the facility hook indications */ + if (plci->State == ADVANCED_VOICE_SIG && ie[0]) { + switch (ie[1] & 0x91) { + case 0x80: /* hook off */ + case 0x81: + if (plci->internal_command == PERM_COD_HOOK) + { + dbug(1, dprintf("init:hook_off")); + plci->hook_state = ie[1]; + next_internal_command(Id, plci); + break; + } + else /* ignore doubled hook indications */ + { + if (((plci->hook_state) & 0xf0) == 0x80) + { + dbug(1, dprintf("ignore hook")); + break; + } + plci->hook_state = ie[1]&0x91; + } + /* check for incoming call pending */ + /* and signal '+'.Appl must decide */ + /* with connect_res if call must */ + /* accepted or not */ + for (i = 0, tplci = NULL; i < max_appl; i++) { + if (a->codec_listen[i] + && (a->codec_listen[i]->State == INC_CON_PENDING + || a->codec_listen[i]->State == INC_CON_ALERT)) { + tplci = a->codec_listen[i]; + tplci->appl = &application[i]; + } + } + /* no incoming call, do outgoing call */ + /* and signal '+' if outg. setup */ + if (!a->AdvSignalPLCI && !tplci) { + if ((i = get_plci(a))) { + a->AdvSignalPLCI = &a->plci[i - 1]; + tplci = a->AdvSignalPLCI; + tplci->tel = ADV_VOICE; + PUT_WORD(&voice_cai[5], a->AdvSignalAppl->MaxDataLength); + if (a->Info_Mask[a->AdvSignalAppl->Id - 1] & 0x200) { + /* early B3 connect (CIP mask bit 9) no release after a disc */ + add_p(tplci, LLI, "\x01\x01"); + } + add_p(tplci, CAI, voice_cai); + add_p(tplci, OAD, a->TelOAD); + add_p(tplci, OSA, a->TelOSA); + add_p(tplci, SHIFT | 6, NULL); + add_p(tplci, SIN, "\x02\x01\x00"); + add_p(tplci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(tplci, ASSIGN, DSIG_ID); + a->AdvSignalPLCI->internal_command = HOOK_OFF_REQ; + a->AdvSignalPLCI->command = 0; + tplci->appl = a->AdvSignalAppl; + tplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE; + send_req(tplci); + } + + } + + if (!tplci) break; + Id = ((word)tplci->Id << 8) | a->Id; + Id |= EXT_CONTROLLER; + sendf(tplci->appl, + _FACILITY_I, + Id, + 0, + "ws", (word)0, "\x01+"); + break; + + case 0x90: /* hook on */ + case 0x91: + if (plci->internal_command == PERM_COD_HOOK) + { + dbug(1, dprintf("init:hook_on")); + plci->hook_state = ie[1] & 0x91; + next_internal_command(Id, plci); + break; + } + else /* ignore doubled hook indications */ + { + if (((plci->hook_state) & 0xf0) == 0x90) break; + plci->hook_state = ie[1] & 0x91; + } + /* hangup the adv. voice call and signal '-' to the appl */ + if (a->AdvSignalPLCI) { + Id = ((word)a->AdvSignalPLCI->Id << 8) | a->Id; + if (plci->tel) Id |= EXT_CONTROLLER; + sendf(a->AdvSignalAppl, + _FACILITY_I, + Id, + 0, + "ws", (word)0, "\x01-"); + a->AdvSignalPLCI->internal_command = HOOK_ON_REQ; + a->AdvSignalPLCI->command = 0; + sig_req(a->AdvSignalPLCI, HANGUP, 0); + send_req(a->AdvSignalPLCI); + } + break; + } + } + break; + + case RESUME: + __clear_bit(plci->appl->Id - 1, plci->c_ind_mask_table); + PUT_WORD(&resume_cau[4], GOOD); + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", (word)3, resume_cau); + break; + + case SUSPEND: + bitmap_zero(plci->c_ind_mask_table, MAX_APPL); + + if (plci->NL.Id && !plci->nl_remove_id) { + mixer_remove(plci); + nl_req_ncci(plci, REMOVE, 0); + } + if (!plci->sig_remove_id) { + plci->internal_command = 0; + sig_req(plci, REMOVE, 0); + } + send_req(plci); + if (!plci->channels) { + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", (word)3, "\x05\x04\x00\x02\x00\x00"); + sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", 0); + } + break; + + case SUSPEND_REJ: + break; + + case HANGUP: + plci->hangup_flow_ctrl_timer = 0; + if (plci->manufacturer && plci->State == LOCAL_CONNECT) break; + cau = parms[7]; + if (cau) { + i = _L3_CAUSE | cau[2]; + if (cau[2] == 0) i = 0; + else if (cau[2] == 8) i = _L1_ERROR; + else if (cau[2] == 9 || cau[2] == 10) i = _L2_ERROR; + else if (cau[2] == 5) i = _CAPI_GUARD_ERROR; + } + else { + i = _L3_ERROR; + } + + if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT) + { + for_each_set_bit(i, plci->c_ind_mask_table, max_appl) + sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0); + } + else + { + bitmap_zero(plci->c_ind_mask_table, MAX_APPL); + } + if (!plci->appl) + { + if (plci->State == LISTENING) + { + plci->notifiedcall = 0; + a->listen_active--; + } + plci->State = INC_DIS_PENDING; + if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL)) + { + plci->State = IDLE; + if (plci->NL.Id && !plci->nl_remove_id) + { + mixer_remove(plci); + nl_req_ncci(plci, REMOVE, 0); + } + if (!plci->sig_remove_id) + { + plci->internal_command = 0; + sig_req(plci, REMOVE, 0); + } + send_req(plci); + } + } + else + { + /* collision of DISCONNECT or CONNECT_RES with HANGUP can */ + /* result in a second HANGUP! Don't generate another */ + /* DISCONNECT */ + if (plci->State != IDLE && plci->State != INC_DIS_PENDING) + { + if (plci->State == RESUMING) + { + PUT_WORD(&resume_cau[4], i); + sendf(plci->appl, _FACILITY_I, Id, 0, "ws", (word)3, resume_cau); + } + plci->State = INC_DIS_PENDING; + sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", i); + } + } + break; + + case SSEXT_IND: + SendSSExtInd(NULL, plci, Id, multi_ssext_parms); + break; + + case VSWITCH_REQ: + VSwitchReqInd(plci, Id, multi_vswitch_parms); + break; + case VSWITCH_IND: + if (plci->relatedPTYPLCI && + plci->vswitchstate == 3 && + plci->relatedPTYPLCI->vswitchstate == 3 && + parms[MAXPARMSIDS - 1][0]) + { + add_p(plci->relatedPTYPLCI, SMSG, parms[MAXPARMSIDS - 1]); + sig_req(plci->relatedPTYPLCI, VSWITCH_REQ, 0); + send_req(plci->relatedPTYPLCI); + } + else VSwitchReqInd(plci, Id, multi_vswitch_parms); + break; + + } +} + + +static void SendSetupInfo(APPL *appl, PLCI *plci, dword Id, byte **parms, byte Info_Sent_Flag) +{ + word i; + byte *ie; + word Info_Number; + byte *Info_Element; + word Info_Mask = 0; + + dbug(1, dprintf("SetupInfo")); + + for (i = 0; i < MAXPARMSIDS; i++) { + ie = parms[i]; + Info_Number = 0; + Info_Element = ie; + if (ie[0]) { + switch (i) { + case 0: + dbug(1, dprintf("CPN ")); + Info_Number = 0x0070; + Info_Mask = 0x80; + Info_Sent_Flag = true; + break; + case 8: /* display */ + dbug(1, dprintf("display(%d)", i)); + Info_Number = 0x0028; + Info_Mask = 0x04; + Info_Sent_Flag = true; + break; + case 16: /* Channel Id */ + dbug(1, dprintf("CHI")); + Info_Number = 0x0018; + Info_Mask = 0x100; + Info_Sent_Flag = true; + mixer_set_bchannel_id(plci, Info_Element); + break; + case 19: /* Redirected Number */ + dbug(1, dprintf("RDN")); + Info_Number = 0x0074; + Info_Mask = 0x400; + Info_Sent_Flag = true; + break; + case 20: /* Redirected Number extended */ + dbug(1, dprintf("RDX")); + Info_Number = 0x0073; + Info_Mask = 0x400; + Info_Sent_Flag = true; + break; + case 22: /* Redirecing Number */ + dbug(1, dprintf("RIN")); + Info_Number = 0x0076; + Info_Mask = 0x400; + Info_Sent_Flag = true; + break; + default: + Info_Number = 0; + break; + } + } + + if (i == MAXPARMSIDS - 2) { /* to indicate the message type "Setup" */ + Info_Number = 0x8000 | 5; + Info_Mask = 0x10; + Info_Element = ""; + } + + if (Info_Sent_Flag && Info_Number) { + if (plci->adapter->Info_Mask[appl->Id - 1] & Info_Mask) { + sendf(appl, _INFO_I, Id, 0, "wS", Info_Number, Info_Element); + } + } + } +} + + +static void SendInfo(PLCI *plci, dword Id, byte **parms, byte iesent) +{ + word i; + word j; + word k; + byte *ie; + word Info_Number; + byte *Info_Element; + word Info_Mask = 0; + static byte charges[5] = {4, 0, 0, 0, 0}; + static byte cause[] = {0x02, 0x80, 0x00}; + APPL *appl; + + dbug(1, dprintf("InfoParse ")); + + if ( + !plci->appl + && !plci->State + && plci->Sig.Ind != NCR_FACILITY + ) + { + dbug(1, dprintf("NoParse ")); + return; + } + cause[2] = 0; + for (i = 0; i < MAXPARMSIDS; i++) { + ie = parms[i]; + Info_Number = 0; + Info_Element = ie; + if (ie[0]) { + switch (i) { + case 0: + dbug(1, dprintf("CPN ")); + Info_Number = 0x0070; + Info_Mask = 0x80; + break; + case 7: /* ESC_CAU */ + dbug(1, dprintf("cau(0x%x)", ie[2])); + Info_Number = 0x0008; + Info_Mask = 0x00; + cause[2] = ie[2]; + Info_Element = NULL; + break; + case 8: /* display */ + dbug(1, dprintf("display(%d)", i)); + Info_Number = 0x0028; + Info_Mask = 0x04; + break; + case 9: /* Date display */ + dbug(1, dprintf("date(%d)", i)); + Info_Number = 0x0029; + Info_Mask = 0x02; + break; + case 10: /* charges */ + for (j = 0; j < 4; j++) charges[1 + j] = 0; + for (j = 0; j < ie[0] && !(ie[1 + j] & 0x80); j++); + for (k = 1, j++; j < ie[0] && k <= 4; j++, k++) charges[k] = ie[1 + j]; + Info_Number = 0x4000; + Info_Mask = 0x40; + Info_Element = charges; + break; + case 11: /* user user info */ + dbug(1, dprintf("uui")); + Info_Number = 0x007E; + Info_Mask = 0x08; + break; + case 12: /* congestion receiver ready */ + dbug(1, dprintf("clRDY")); + Info_Number = 0x00B0; + Info_Mask = 0x08; + Info_Element = ""; + break; + case 13: /* congestion receiver not ready */ + dbug(1, dprintf("clNRDY")); + Info_Number = 0x00BF; + Info_Mask = 0x08; + Info_Element = ""; + break; + case 15: /* Keypad Facility */ + dbug(1, dprintf("KEY")); + Info_Number = 0x002C; + Info_Mask = 0x20; + break; + case 16: /* Channel Id */ + dbug(1, dprintf("CHI")); + Info_Number = 0x0018; + Info_Mask = 0x100; + mixer_set_bchannel_id(plci, Info_Element); + break; + case 17: /* if no 1tr6 cause, send full cause, else esc_cause */ + dbug(1, dprintf("q9cau(0x%x)", ie[2])); + if (!cause[2] || cause[2] < 0x80) break; /* eg. layer 1 error */ + Info_Number = 0x0008; + Info_Mask = 0x01; + if (cause[2] != ie[2]) Info_Element = cause; + break; + case 19: /* Redirected Number */ + dbug(1, dprintf("RDN")); + Info_Number = 0x0074; + Info_Mask = 0x400; + break; + case 22: /* Redirecing Number */ + dbug(1, dprintf("RIN")); + Info_Number = 0x0076; + Info_Mask = 0x400; + break; + case 23: /* Notification Indicator */ + dbug(1, dprintf("NI")); + Info_Number = (word)NI; + Info_Mask = 0x210; + break; + case 26: /* Call State */ + dbug(1, dprintf("CST")); + Info_Number = (word)CST; + Info_Mask = 0x01; /* do with cause i.e. for now */ + break; + case MAXPARMSIDS - 2: /* Escape Message Type, must be the last indication */ + dbug(1, dprintf("ESC/MT[0x%x]", ie[3])); + Info_Number = 0x8000 | ie[3]; + if (iesent) Info_Mask = 0xffff; + else Info_Mask = 0x10; + Info_Element = ""; + break; + default: + Info_Number = 0; + Info_Mask = 0; + Info_Element = ""; + break; + } + } + + if (plci->Sig.Ind == NCR_FACILITY) /* check controller broadcast */ + { + for (j = 0; j < max_appl; j++) + { + appl = &application[j]; + if (Info_Number + && appl->Id + && plci->adapter->Info_Mask[appl->Id - 1] & Info_Mask) + { + dbug(1, dprintf("NCR_Ind")); + iesent = true; + sendf(&application[j], _INFO_I, Id & 0x0f, 0, "wS", Info_Number, Info_Element); + } + } + } + else if (!plci->appl) + { /* overlap receiving broadcast */ + if (Info_Number == CPN + || Info_Number == KEY + || Info_Number == NI + || Info_Number == DSP + || Info_Number == UUI) + { + for_each_set_bit(j, plci->c_ind_mask_table, max_appl) { + dbug(1, dprintf("Ovl_Ind")); + iesent = true; + sendf(&application[j], _INFO_I, Id, 0, "wS", Info_Number, Info_Element); + } + } + } /* all other signalling states */ + else if (Info_Number + && plci->adapter->Info_Mask[plci->appl->Id - 1] & Info_Mask) + { + dbug(1, dprintf("Std_Ind")); + iesent = true; + sendf(plci->appl, _INFO_I, Id, 0, "wS", Info_Number, Info_Element); + } + } +} + + +static byte SendMultiIE(PLCI *plci, dword Id, byte **parms, byte ie_type, + dword info_mask, byte setupParse) +{ + word i; + word j; + byte *ie; + word Info_Number; + byte *Info_Element; + APPL *appl; + word Info_Mask = 0; + byte iesent = 0; + + if ( + !plci->appl + && !plci->State + && plci->Sig.Ind != NCR_FACILITY + && !setupParse + ) + { + dbug(1, dprintf("NoM-IEParse ")); + return 0; + } + dbug(1, dprintf("M-IEParse ")); + + for (i = 0; i < MAX_MULTI_IE; i++) + { + ie = parms[i]; + Info_Number = 0; + Info_Element = ie; + if (ie[0]) + { + dbug(1, dprintf("[Ind0x%x]:IE=0x%x", plci->Sig.Ind, ie_type)); + Info_Number = (word)ie_type; + Info_Mask = (word)info_mask; + } + + if (plci->Sig.Ind == NCR_FACILITY) /* check controller broadcast */ + { + for (j = 0; j < max_appl; j++) + { + appl = &application[j]; + if (Info_Number + && appl->Id + && plci->adapter->Info_Mask[appl->Id - 1] & Info_Mask) + { + iesent = true; + dbug(1, dprintf("Mlt_NCR_Ind")); + sendf(&application[j], _INFO_I, Id & 0x0f, 0, "wS", Info_Number, Info_Element); + } + } + } + else if (!plci->appl && Info_Number) + { /* overlap receiving broadcast */ + for_each_set_bit(j, plci->c_ind_mask_table, max_appl) { + iesent = true; + dbug(1, dprintf("Mlt_Ovl_Ind")); + sendf(&application[j] , _INFO_I, Id, 0, "wS", Info_Number, Info_Element); + } + } /* all other signalling states */ + else if (Info_Number + && plci->adapter->Info_Mask[plci->appl->Id - 1] & Info_Mask) + { + iesent = true; + dbug(1, dprintf("Mlt_Std_Ind")); + sendf(plci->appl, _INFO_I, Id, 0, "wS", Info_Number, Info_Element); + } + } + return iesent; +} + +static void SendSSExtInd(APPL *appl, PLCI *plci, dword Id, byte **parms) +{ + word i; + /* Format of multi_ssext_parms[i][]: + 0 byte length + 1 byte SSEXTIE + 2 byte SSEXT_REQ/SSEXT_IND + 3 byte length + 4 word SSExtCommand + 6... Params + */ + if ( + plci + && plci->State + && plci->Sig.Ind != NCR_FACILITY + ) + for (i = 0; i < MAX_MULTI_IE; i++) + { + if (parms[i][0] < 6) continue; + if (parms[i][2] == SSEXT_REQ) continue; + + if (appl) + { + parms[i][0] = 0; /* kill it */ + sendf(appl, _MANUFACTURER_I, + Id, + 0, + "dwS", + _DI_MANU_ID, + _DI_SSEXT_CTRL, + &parms[i][3]); + } + else if (plci->appl) + { + parms[i][0] = 0; /* kill it */ + sendf(plci->appl, _MANUFACTURER_I, + Id, + 0, + "dwS", + _DI_MANU_ID, + _DI_SSEXT_CTRL, + &parms[i][3]); + } + } +}; + +static void nl_ind(PLCI *plci) +{ + byte ch; + word ncci; + dword Id; + DIVA_CAPI_ADAPTER *a; + word NCCIcode; + APPL *APPLptr; + word count; + word Num; + word i, ncpi_state; + byte len, ncci_state; + word msg; + word info = 0; + word fax_feature_bits; + byte fax_send_edata_ack; + static byte v120_header_buffer[2 + 3]; + static word fax_info[] = { + 0, /* T30_SUCCESS */ + _FAX_NO_CONNECTION, /* T30_ERR_NO_DIS_RECEIVED */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_TIMEOUT_NO_RESPONSE */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_RESPONSE */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_TOO_MANY_REPEATS */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_UNEXPECTED_MESSAGE */ + _FAX_REMOTE_ABORT, /* T30_ERR_UNEXPECTED_DCN */ + _FAX_LOCAL_ABORT, /* T30_ERR_DTC_UNSUPPORTED */ + _FAX_TRAINING_ERROR, /* T30_ERR_ALL_RATES_FAILED */ + _FAX_TRAINING_ERROR, /* T30_ERR_TOO_MANY_TRAINS */ + _FAX_PARAMETER_ERROR, /* T30_ERR_RECEIVE_CORRUPTED */ + _FAX_REMOTE_ABORT, /* T30_ERR_UNEXPECTED_DISC */ + _FAX_LOCAL_ABORT, /* T30_ERR_APPLICATION_DISC */ + _FAX_REMOTE_REJECT, /* T30_ERR_INCOMPATIBLE_DIS */ + _FAX_LOCAL_ABORT, /* T30_ERR_INCOMPATIBLE_DCS */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_TIMEOUT_NO_COMMAND */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_COMMAND */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_TIMEOUT_COMMAND_TOO_LONG */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_TIMEOUT_RESPONSE_TOO_LONG */ + _FAX_NO_CONNECTION, /* T30_ERR_NOT_IDENTIFIED */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_SUPERVISORY_TIMEOUT */ + _FAX_PARAMETER_ERROR, /* T30_ERR_TOO_LONG_SCAN_LINE */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_PAGE_AFTER_MPS */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_PAGE_AFTER_CFR */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_DCS_AFTER_FTT */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_DCS_AFTER_EOM */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_DCS_AFTER_MPS */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_DCN_AFTER_MCF */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_DCN_AFTER_RTN */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_CFR */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_MCF_AFTER_EOP */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_MCF_AFTER_EOM */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_MCF_AFTER_MPS */ + 0x331d, /* T30_ERR_SUB_SEP_UNSUPPORTED */ + 0x331e, /* T30_ERR_PWD_UNSUPPORTED */ + 0x331f, /* T30_ERR_SUB_SEP_PWD_UNSUPPORTED */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_INVALID_COMMAND_FRAME */ + _FAX_PARAMETER_ERROR, /* T30_ERR_UNSUPPORTED_PAGE_CODING */ + _FAX_PARAMETER_ERROR, /* T30_ERR_INVALID_PAGE_CODING */ + _FAX_REMOTE_REJECT, /* T30_ERR_INCOMPATIBLE_PAGE_CONFIG */ + _FAX_LOCAL_ABORT, /* T30_ERR_TIMEOUT_FROM_APPLICATION */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_V34FAX_NO_REACTION_ON_MARK */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_V34FAX_TRAINING_TIMEOUT */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_V34FAX_UNEXPECTED_V21 */ + _FAX_PROTOCOL_ERROR, /* T30_ERR_V34FAX_PRIMARY_CTS_ON */ + _FAX_LOCAL_ABORT, /* T30_ERR_V34FAX_TURNAROUND_POLLING */ + _FAX_LOCAL_ABORT /* T30_ERR_V34FAX_V8_INCOMPATIBILITY */ + }; + + byte dtmf_code_buffer[CAPIDTMF_RECV_DIGIT_BUFFER_SIZE + 1]; + + + static word rtp_info[] = { + GOOD, /* RTP_SUCCESS */ + 0x3600 /* RTP_ERR_SSRC_OR_PAYLOAD_CHANGE */ + }; + + static dword udata_forwarding_table[0x100 / sizeof(dword)] = + { + 0x0020301e, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 + }; + + ch = plci->NL.IndCh; + a = plci->adapter; + ncci = a->ch_ncci[ch]; + Id = (((dword)(ncci ? ncci : ch)) << 16) | (((word) plci->Id) << 8) | a->Id; + if (plci->tel) Id |= EXT_CONTROLLER; + APPLptr = plci->appl; + dbug(1, dprintf("NL_IND-Id(NL:0x%x)=0x%08lx,plci=%x,tel=%x,state=0x%x,ch=0x%x,chs=%d,Ind=%x", + plci->NL.Id, Id, plci->Id, plci->tel, plci->State, ch, plci->channels, plci->NL.Ind & 0x0f)); + + /* in the case if no connect_active_Ind was sent to the appl we wait for */ + + if (plci->nl_remove_id) + { + plci->NL.RNR = 2; /* discard */ + dbug(1, dprintf("NL discard while remove pending")); + return; + } + if ((plci->NL.Ind & 0x0f) == N_CONNECT) + { + if (plci->State == INC_DIS_PENDING + || plci->State == OUTG_DIS_PENDING + || plci->State == IDLE) + { + plci->NL.RNR = 2; /* discard */ + dbug(1, dprintf("discard n_connect")); + return; + } + if (plci->State < INC_ACT_PENDING) + { + plci->NL.RNR = 1; /* flow control */ + channel_x_off(plci, ch, N_XON_CONNECT_IND); + return; + } + } + + if (!APPLptr) /* no application or invalid data */ + { /* while reloading the DSP */ + dbug(1, dprintf("discard1")); + plci->NL.RNR = 2; + return; + } + + if (((plci->NL.Ind & 0x0f) == N_UDATA) + && (((plci->B2_prot != B2_SDLC) && ((plci->B1_resource == 17) || (plci->B1_resource == 18))) + || (plci->B2_prot == 7) + || (plci->B3_prot == 7))) + { + plci->ncpi_buffer[0] = 0; + + ncpi_state = plci->ncpi_state; + if (plci->NL.complete == 1) + { + byte *data = &plci->NL.RBuffer->P[0]; + + if ((plci->NL.RBuffer->length >= 12) + && ((*data == DSP_UDATA_INDICATION_DCD_ON) + || (*data == DSP_UDATA_INDICATION_CTS_ON))) + { + word conn_opt, ncpi_opt = 0x00; +/* HexDump ("MDM N_UDATA:", plci->NL.RBuffer->length, data); */ + + if (*data == DSP_UDATA_INDICATION_DCD_ON) + plci->ncpi_state |= NCPI_MDM_DCD_ON_RECEIVED; + if (*data == DSP_UDATA_INDICATION_CTS_ON) + plci->ncpi_state |= NCPI_MDM_CTS_ON_RECEIVED; + + data++; /* indication code */ + data += 2; /* timestamp */ + if ((*data == DSP_CONNECTED_NORM_V18) || (*data == DSP_CONNECTED_NORM_VOWN)) + ncpi_state &= ~(NCPI_MDM_DCD_ON_RECEIVED | NCPI_MDM_CTS_ON_RECEIVED); + data++; /* connected norm */ + conn_opt = GET_WORD(data); + data += 2; /* connected options */ + + PUT_WORD(&(plci->ncpi_buffer[1]), (word)(GET_DWORD(data) & 0x0000FFFF)); + + if (conn_opt & DSP_CONNECTED_OPTION_MASK_V42) + { + ncpi_opt |= MDM_NCPI_ECM_V42; + } + else if (conn_opt & DSP_CONNECTED_OPTION_MASK_MNP) + { + ncpi_opt |= MDM_NCPI_ECM_MNP; + } + else + { + ncpi_opt |= MDM_NCPI_TRANSPARENT; + } + if (conn_opt & DSP_CONNECTED_OPTION_MASK_COMPRESSION) + { + ncpi_opt |= MDM_NCPI_COMPRESSED; + } + PUT_WORD(&(plci->ncpi_buffer[3]), ncpi_opt); + plci->ncpi_buffer[0] = 4; + + plci->ncpi_state |= NCPI_VALID_CONNECT_B3_IND | NCPI_VALID_CONNECT_B3_ACT | NCPI_VALID_DISC_B3_IND; + } + } + if (plci->B3_prot == 7) + { + if (((a->ncci_state[ncci] == INC_ACT_PENDING) || (a->ncci_state[ncci] == OUTG_CON_PENDING)) + && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT) + && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT)) + { + a->ncci_state[ncci] = INC_ACT_PENDING; + sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer); + plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT; + } + } + + if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1]) + & ((1L << PRIVATE_V18) | (1L << PRIVATE_VOWN))) + || !(ncpi_state & NCPI_MDM_DCD_ON_RECEIVED) + || !(ncpi_state & NCPI_MDM_CTS_ON_RECEIVED)) + + { + plci->NL.RNR = 2; + return; + } + } + + if (plci->NL.complete == 2) + { + if (((plci->NL.Ind & 0x0f) == N_UDATA) + && !(udata_forwarding_table[plci->RData[0].P[0] >> 5] & (1L << (plci->RData[0].P[0] & 0x1f)))) + { + switch (plci->RData[0].P[0]) + { + + case DTMF_UDATA_INDICATION_FAX_CALLING_TONE: + if (plci->dtmf_rec_active & DTMF_LISTEN_ACTIVE_FLAG) + sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", SELECTOR_DTMF, "\x01X"); + break; + case DTMF_UDATA_INDICATION_ANSWER_TONE: + if (plci->dtmf_rec_active & DTMF_LISTEN_ACTIVE_FLAG) + sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", SELECTOR_DTMF, "\x01Y"); + break; + case DTMF_UDATA_INDICATION_DIGITS_RECEIVED: + dtmf_indication(Id, plci, plci->RData[0].P, plci->RData[0].PLength); + break; + case DTMF_UDATA_INDICATION_DIGITS_SENT: + dtmf_confirmation(Id, plci); + break; + + + case UDATA_INDICATION_MIXER_TAP_DATA: + capidtmf_recv_process_block(&(plci->capidtmf_state), plci->RData[0].P + 1, (word)(plci->RData[0].PLength - 1)); + i = capidtmf_indication(&(plci->capidtmf_state), dtmf_code_buffer + 1); + if (i != 0) + { + dtmf_code_buffer[0] = DTMF_UDATA_INDICATION_DIGITS_RECEIVED; + dtmf_indication(Id, plci, dtmf_code_buffer, (word)(i + 1)); + } + break; + + + case UDATA_INDICATION_MIXER_COEFS_SET: + mixer_indication_coefs_set(Id, plci); + break; + case UDATA_INDICATION_XCONNECT_FROM: + mixer_indication_xconnect_from(Id, plci, plci->RData[0].P, plci->RData[0].PLength); + break; + case UDATA_INDICATION_XCONNECT_TO: + mixer_indication_xconnect_to(Id, plci, plci->RData[0].P, plci->RData[0].PLength); + break; + + + case LEC_UDATA_INDICATION_DISABLE_DETECT: + ec_indication(Id, plci, plci->RData[0].P, plci->RData[0].PLength); + break; + + + + default: + break; + } + } + else + { + if ((plci->RData[0].PLength != 0) + && ((plci->B2_prot == B2_V120_ASYNC) + || (plci->B2_prot == B2_V120_ASYNC_V42BIS) + || (plci->B2_prot == B2_V120_BIT_TRANSPARENT))) + { + + sendf(plci->appl, _DATA_B3_I, Id, 0, + "dwww", + plci->RData[1].P, + (plci->NL.RNum < 2) ? 0 : plci->RData[1].PLength, + plci->RNum, + plci->RFlags); + + } + else + { + + sendf(plci->appl, _DATA_B3_I, Id, 0, + "dwww", + plci->RData[0].P, + plci->RData[0].PLength, + plci->RNum, + plci->RFlags); + + } + } + return; + } + + fax_feature_bits = 0; + if ((plci->NL.Ind & 0x0f) == N_CONNECT || + (plci->NL.Ind & 0x0f) == N_CONNECT_ACK || + (plci->NL.Ind & 0x0f) == N_DISC || + (plci->NL.Ind & 0x0f) == N_EDATA || + (plci->NL.Ind & 0x0f) == N_DISC_ACK) + { + info = 0; + plci->ncpi_buffer[0] = 0; + switch (plci->B3_prot) { + case 0: /*XPARENT*/ + case 1: /*T.90 NL*/ + break; /* no network control protocol info - jfr */ + case 2: /*ISO8202*/ + case 3: /*X25 DCE*/ + for (i = 0; i < plci->NL.RLength; i++) plci->ncpi_buffer[4 + i] = plci->NL.RBuffer->P[i]; + plci->ncpi_buffer[0] = (byte)(i + 3); + plci->ncpi_buffer[1] = (byte)(plci->NL.Ind & N_D_BIT ? 1 : 0); + plci->ncpi_buffer[2] = 0; + plci->ncpi_buffer[3] = 0; + break; + case 4: /*T.30 - FAX*/ + case 5: /*T.30 - FAX*/ + if (plci->NL.RLength >= sizeof(T30_INFO)) + { + dbug(1, dprintf("FaxStatus %04x", ((T30_INFO *)plci->NL.RBuffer->P)->code)); + len = 9; + PUT_WORD(&(plci->ncpi_buffer[1]), ((T30_INFO *)plci->NL.RBuffer->P)->rate_div_2400 * 2400); + fax_feature_bits = GET_WORD(&((T30_INFO *)plci->NL.RBuffer->P)->feature_bits_low); + i = (((T30_INFO *)plci->NL.RBuffer->P)->resolution & T30_RESOLUTION_R8_0770_OR_200) ? 0x0001 : 0x0000; + if (plci->B3_prot == 5) + { + if (!(fax_feature_bits & T30_FEATURE_BIT_ECM)) + i |= 0x8000; /* This is not an ECM connection */ + if (fax_feature_bits & T30_FEATURE_BIT_T6_CODING) + i |= 0x4000; /* This is a connection with MMR compression */ + if (fax_feature_bits & T30_FEATURE_BIT_2D_CODING) + i |= 0x2000; /* This is a connection with MR compression */ + if (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS) + i |= 0x0004; /* More documents */ + if (fax_feature_bits & T30_FEATURE_BIT_POLLING) + i |= 0x0002; /* Fax-polling indication */ + } + dbug(1, dprintf("FAX Options %04x %04x", fax_feature_bits, i)); + PUT_WORD(&(plci->ncpi_buffer[3]), i); + PUT_WORD(&(plci->ncpi_buffer[5]), ((T30_INFO *)plci->NL.RBuffer->P)->data_format); + plci->ncpi_buffer[7] = ((T30_INFO *)plci->NL.RBuffer->P)->pages_low; + plci->ncpi_buffer[8] = ((T30_INFO *)plci->NL.RBuffer->P)->pages_high; + plci->ncpi_buffer[len] = 0; + if (((T30_INFO *)plci->NL.RBuffer->P)->station_id_len) + { + plci->ncpi_buffer[len] = 20; + for (i = 0; i < T30_MAX_STATION_ID_LENGTH; i++) + plci->ncpi_buffer[++len] = ((T30_INFO *)plci->NL.RBuffer->P)->station_id[i]; + } + if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK)) + { + if (((T30_INFO *)plci->NL.RBuffer->P)->code < ARRAY_SIZE(fax_info)) + info = fax_info[((T30_INFO *)plci->NL.RBuffer->P)->code]; + else + info = _FAX_PROTOCOL_ERROR; + } + + if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id - 1]) + & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD))) + { + i = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + ((T30_INFO *)plci->NL.RBuffer->P)->head_line_len; + while (i < plci->NL.RBuffer->length) + plci->ncpi_buffer[++len] = plci->NL.RBuffer->P[i++]; + } + + plci->ncpi_buffer[0] = len; + fax_feature_bits = GET_WORD(&((T30_INFO *)plci->NL.RBuffer->P)->feature_bits_low); + PUT_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->feature_bits_low, fax_feature_bits); + + plci->ncpi_state |= NCPI_VALID_CONNECT_B3_IND; + if (((plci->NL.Ind & 0x0f) == N_CONNECT_ACK) + || (((plci->NL.Ind & 0x0f) == N_CONNECT) + && (fax_feature_bits & T30_FEATURE_BIT_POLLING)) + || (((plci->NL.Ind & 0x0f) == N_EDATA) + && ((((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_TRAIN_OK) + || (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DIS) + || (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DTC)))) + { + plci->ncpi_state |= NCPI_VALID_CONNECT_B3_ACT; + } + if (((plci->NL.Ind & 0x0f) == N_DISC) + || ((plci->NL.Ind & 0x0f) == N_DISC_ACK) + || (((plci->NL.Ind & 0x0f) == N_EDATA) + && (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_EOP_CAPI))) + { + plci->ncpi_state |= NCPI_VALID_CONNECT_B3_ACT | NCPI_VALID_DISC_B3_IND; + } + } + break; + + case B3_RTP: + if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK)) + { + if (plci->NL.RLength != 0) + { + info = rtp_info[plci->NL.RBuffer->P[0]]; + plci->ncpi_buffer[0] = plci->NL.RLength - 1; + for (i = 1; i < plci->NL.RLength; i++) + plci->ncpi_buffer[i] = plci->NL.RBuffer->P[i]; + } + } + break; + + } + plci->NL.RNR = 2; + } + switch (plci->NL.Ind & 0x0f) { + case N_EDATA: + if ((plci->B3_prot == 4) || (plci->B3_prot == 5)) + { + dbug(1, dprintf("EDATA ncci=0x%x state=%d code=%02x", ncci, a->ncci_state[ncci], + ((T30_INFO *)plci->NL.RBuffer->P)->code)); + fax_send_edata_ack = (((T30_INFO *)(plci->fax_connect_info_buffer))->operating_mode == T30_OPERATING_MODE_CAPI_NEG); + + if ((plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF) + && (plci->nsf_control_bits & (T30_NSF_CONTROL_BIT_NEGOTIATE_IND | T30_NSF_CONTROL_BIT_NEGOTIATE_RESP)) + && (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DIS) + && (a->ncci_state[ncci] == OUTG_CON_PENDING) + && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT) + && !(plci->ncpi_state & NCPI_NEGOTIATE_B3_SENT)) + { + ((T30_INFO *)(plci->fax_connect_info_buffer))->code = ((T30_INFO *)plci->NL.RBuffer->P)->code; + sendf(plci->appl, _MANUFACTURER_I, Id, 0, "dwbS", _DI_MANU_ID, _DI_NEGOTIATE_B3, + (byte)(plci->ncpi_buffer[0] + 1), plci->ncpi_buffer); + plci->ncpi_state |= NCPI_NEGOTIATE_B3_SENT; + if (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP) + fax_send_edata_ack = false; + } + + if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS) + { + switch (((T30_INFO *)plci->NL.RBuffer->P)->code) + { + case EDATA_T30_DIS: + if ((a->ncci_state[ncci] == OUTG_CON_PENDING) + && !(GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low) & T30_CONTROL_BIT_REQUEST_POLLING) + && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT) + && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT)) + { + a->ncci_state[ncci] = INC_ACT_PENDING; + if (plci->B3_prot == 4) + sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", ""); + else + sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer); + plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT; + } + break; + + case EDATA_T30_TRAIN_OK: + if ((a->ncci_state[ncci] == INC_ACT_PENDING) + && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT) + && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT)) + { + if (plci->B3_prot == 4) + sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", ""); + else + sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer); + plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT; + } + break; + + case EDATA_T30_EOP_CAPI: + if (a->ncci_state[ncci] == CONNECTED) + { + sendf(plci->appl, _DISCONNECT_B3_I, Id, 0, "wS", GOOD, plci->ncpi_buffer); + a->ncci_state[ncci] = INC_DIS_PENDING; + plci->ncpi_state = 0; + fax_send_edata_ack = false; + } + break; + } + } + else + { + switch (((T30_INFO *)plci->NL.RBuffer->P)->code) + { + case EDATA_T30_TRAIN_OK: + if ((a->ncci_state[ncci] == INC_ACT_PENDING) + && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT) + && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT)) + { + if (plci->B3_prot == 4) + sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", ""); + else + sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer); + plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT; + } + break; + } + } + if (fax_send_edata_ack) + { + ((T30_INFO *)(plci->fax_connect_info_buffer))->code = ((T30_INFO *)plci->NL.RBuffer->P)->code; + plci->fax_edata_ack_length = 1; + start_internal_command(Id, plci, fax_edata_ack_command); + } + } + else + { + dbug(1, dprintf("EDATA ncci=0x%x state=%d", ncci, a->ncci_state[ncci])); + } + break; + case N_CONNECT: + if (!a->ch_ncci[ch]) + { + ncci = get_ncci(plci, ch, 0); + Id = (Id & 0xffff) | (((dword) ncci) << 16); + } + dbug(1, dprintf("N_CONNECT: ch=%d state=%d plci=%lx plci_Id=%lx plci_State=%d", + ch, a->ncci_state[ncci], a->ncci_plci[ncci], plci->Id, plci->State)); + + msg = _CONNECT_B3_I; + if (a->ncci_state[ncci] == IDLE) + plci->channels++; + else if (plci->B3_prot == 1) + msg = _CONNECT_B3_T90_ACTIVE_I; + + a->ncci_state[ncci] = INC_CON_PENDING; + if (plci->B3_prot == 4) + sendf(plci->appl, msg, Id, 0, "s", ""); + else + sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer); + break; + case N_CONNECT_ACK: + dbug(1, dprintf("N_connect_Ack")); + if (plci->internal_command_queue[0] + && ((plci->adjust_b_state == ADJUST_B_CONNECT_2) + || (plci->adjust_b_state == ADJUST_B_CONNECT_3) + || (plci->adjust_b_state == ADJUST_B_CONNECT_4))) + { + (*(plci->internal_command_queue[0]))(Id, plci, 0); + if (!plci->internal_command) + next_internal_command(Id, plci); + break; + } + msg = _CONNECT_B3_ACTIVE_I; + if (plci->B3_prot == 1) + { + if (a->ncci_state[ncci] != OUTG_CON_PENDING) + msg = _CONNECT_B3_T90_ACTIVE_I; + a->ncci_state[ncci] = INC_ACT_PENDING; + sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer); + } + else if ((plci->B3_prot == 4) || (plci->B3_prot == 5) || (plci->B3_prot == 7)) + { + if ((a->ncci_state[ncci] == OUTG_CON_PENDING) + && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT) + && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT)) + { + a->ncci_state[ncci] = INC_ACT_PENDING; + if (plci->B3_prot == 4) + sendf(plci->appl, msg, Id, 0, "s", ""); + else + sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer); + plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT; + } + } + else + { + a->ncci_state[ncci] = INC_ACT_PENDING; + sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer); + } + if (plci->adjust_b_restore) + { + plci->adjust_b_restore = false; + start_internal_command(Id, plci, adjust_b_restore); + } + break; + case N_DISC: + case N_DISC_ACK: + if (plci->internal_command_queue[0] + && ((plci->internal_command == FAX_DISCONNECT_COMMAND_1) + || (plci->internal_command == FAX_DISCONNECT_COMMAND_2) + || (plci->internal_command == FAX_DISCONNECT_COMMAND_3))) + { + (*(plci->internal_command_queue[0]))(Id, plci, 0); + if (!plci->internal_command) + next_internal_command(Id, plci); + } + ncci_state = a->ncci_state[ncci]; + ncci_remove(plci, ncci, false); + + /* with N_DISC or N_DISC_ACK the IDI frees the respective */ + /* channel, so we cannot store the state in ncci_state! The */ + /* information which channel we received a N_DISC is thus */ + /* stored in the inc_dis_ncci_table buffer. */ + for (i = 0; plci->inc_dis_ncci_table[i]; i++); + plci->inc_dis_ncci_table[i] = (byte) ncci; + + /* need a connect_b3_ind before a disconnect_b3_ind with FAX */ + if (!plci->channels + && (plci->B1_resource == 16) + && (plci->State <= CONNECTED)) + { + len = 9; + i = ((T30_INFO *)plci->fax_connect_info_buffer)->rate_div_2400 * 2400; + PUT_WORD(&plci->ncpi_buffer[1], i); + PUT_WORD(&plci->ncpi_buffer[3], 0); + i = ((T30_INFO *)plci->fax_connect_info_buffer)->data_format; + PUT_WORD(&plci->ncpi_buffer[5], i); + PUT_WORD(&plci->ncpi_buffer[7], 0); + plci->ncpi_buffer[len] = 0; + plci->ncpi_buffer[0] = len; + if (plci->B3_prot == 4) + sendf(plci->appl, _CONNECT_B3_I, Id, 0, "s", ""); + else + { + + if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id - 1]) + & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD))) + { + plci->ncpi_buffer[++len] = 0; + plci->ncpi_buffer[++len] = 0; + plci->ncpi_buffer[++len] = 0; + plci->ncpi_buffer[0] = len; + } + + sendf(plci->appl, _CONNECT_B3_I, Id, 0, "S", plci->ncpi_buffer); + } + sendf(plci->appl, _DISCONNECT_B3_I, Id, 0, "wS", info, plci->ncpi_buffer); + plci->ncpi_state = 0; + sig_req(plci, HANGUP, 0); + send_req(plci); + plci->State = OUTG_DIS_PENDING; + /* disc here */ + } + else if ((a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS) + && ((plci->B3_prot == 4) || (plci->B3_prot == 5)) + && ((ncci_state == INC_DIS_PENDING) || (ncci_state == IDLE))) + { + if (ncci_state == IDLE) + { + if (plci->channels) + plci->channels--; + if ((plci->State == IDLE || plci->State == SUSPENDING) && !plci->channels) { + if (plci->State == SUSPENDING) { + sendf(plci->appl, + _FACILITY_I, + Id & 0xffffL, + 0, + "ws", (word)3, "\x03\x04\x00\x00"); + sendf(plci->appl, _DISCONNECT_I, Id & 0xffffL, 0, "w", 0); + } + plci_remove(plci); + plci->State = IDLE; + } + } + } + else if (plci->channels) + { + sendf(plci->appl, _DISCONNECT_B3_I, Id, 0, "wS", info, plci->ncpi_buffer); + plci->ncpi_state = 0; + if ((ncci_state == OUTG_REJ_PENDING) + && ((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE))) + { + sig_req(plci, HANGUP, 0); + send_req(plci); + plci->State = OUTG_DIS_PENDING; + } + } + break; + case N_RESET: + a->ncci_state[ncci] = INC_RES_PENDING; + sendf(plci->appl, _RESET_B3_I, Id, 0, "S", plci->ncpi_buffer); + break; + case N_RESET_ACK: + a->ncci_state[ncci] = CONNECTED; + sendf(plci->appl, _RESET_B3_I, Id, 0, "S", plci->ncpi_buffer); + break; + + case N_UDATA: + if (!(udata_forwarding_table[plci->NL.RBuffer->P[0] >> 5] & (1L << (plci->NL.RBuffer->P[0] & 0x1f)))) + { + plci->RData[0].P = plci->internal_ind_buffer + (-((int)(long)(plci->internal_ind_buffer)) & 3); + plci->RData[0].PLength = INTERNAL_IND_BUFFER_SIZE; + plci->NL.R = plci->RData; + plci->NL.RNum = 1; + return; + } + /* fall through */ + case N_BDATA: + case N_DATA: + if (((a->ncci_state[ncci] != CONNECTED) && (plci->B2_prot == 1)) /* transparent */ + || (a->ncci_state[ncci] == IDLE) + || (a->ncci_state[ncci] == INC_DIS_PENDING)) + { + plci->NL.RNR = 2; + break; + } + if ((a->ncci_state[ncci] != CONNECTED) + && (a->ncci_state[ncci] != OUTG_DIS_PENDING) + && (a->ncci_state[ncci] != OUTG_REJ_PENDING)) + { + dbug(1, dprintf("flow control")); + plci->NL.RNR = 1; /* flow control */ + channel_x_off(plci, ch, 0); + break; + } + + NCCIcode = ncci | (((word)a->Id) << 8); + + /* count all buffers within the Application pool */ + /* belonging to the same NCCI. If this is below the */ + /* number of buffers available per NCCI we accept */ + /* this packet, otherwise we reject it */ + count = 0; + Num = 0xffff; + for (i = 0; i < APPLptr->MaxBuffer; i++) { + if (NCCIcode == APPLptr->DataNCCI[i]) count++; + if (!APPLptr->DataNCCI[i] && Num == 0xffff) Num = i; + } + + if (count >= APPLptr->MaxNCCIData || Num == 0xffff) + { + dbug(3, dprintf("Flow-Control")); + plci->NL.RNR = 1; + if (++(APPLptr->NCCIDataFlowCtrlTimer) >= + (word)((a->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL) ? 40 : 2000)) + { + plci->NL.RNR = 2; + dbug(3, dprintf("DiscardData")); + } else { + channel_x_off(plci, ch, 0); + } + break; + } + else + { + APPLptr->NCCIDataFlowCtrlTimer = 0; + } + + plci->RData[0].P = ReceiveBufferGet(APPLptr, Num); + if (!plci->RData[0].P) { + plci->NL.RNR = 1; + channel_x_off(plci, ch, 0); + break; + } + + APPLptr->DataNCCI[Num] = NCCIcode; + APPLptr->DataFlags[Num] = (plci->Id << 8) | (plci->NL.Ind >> 4); + dbug(3, dprintf("Buffer(%d), Max = %d", Num, APPLptr->MaxBuffer)); + + plci->RNum = Num; + plci->RFlags = plci->NL.Ind >> 4; + plci->RData[0].PLength = APPLptr->MaxDataLength; + plci->NL.R = plci->RData; + if ((plci->NL.RLength != 0) + && ((plci->B2_prot == B2_V120_ASYNC) + || (plci->B2_prot == B2_V120_ASYNC_V42BIS) + || (plci->B2_prot == B2_V120_BIT_TRANSPARENT))) + { + plci->RData[1].P = plci->RData[0].P; + plci->RData[1].PLength = plci->RData[0].PLength; + plci->RData[0].P = v120_header_buffer + (-((unsigned long)v120_header_buffer) & 3); + if ((plci->NL.RBuffer->P[0] & V120_HEADER_EXTEND_BIT) || (plci->NL.RLength == 1)) + plci->RData[0].PLength = 1; + else + plci->RData[0].PLength = 2; + if (plci->NL.RBuffer->P[0] & V120_HEADER_BREAK_BIT) + plci->RFlags |= 0x0010; + if (plci->NL.RBuffer->P[0] & (V120_HEADER_C1_BIT | V120_HEADER_C2_BIT)) + plci->RFlags |= 0x8000; + plci->NL.RNum = 2; + } + else + { + if ((plci->NL.Ind & 0x0f) == N_UDATA) + plci->RFlags |= 0x0010; + + else if ((plci->B3_prot == B3_RTP) && ((plci->NL.Ind & 0x0f) == N_BDATA)) + plci->RFlags |= 0x0001; + + plci->NL.RNum = 1; + } + break; + case N_DATA_ACK: + data_ack(plci, ch); + break; + default: + plci->NL.RNR = 2; + break; + } +} + +/*------------------------------------------------------------------*/ +/* find a free PLCI */ +/*------------------------------------------------------------------*/ + +static word get_plci(DIVA_CAPI_ADAPTER *a) +{ + word i, j; + PLCI *plci; + + for (i = 0; i < a->max_plci && a->plci[i].Id; i++); + if (i == a->max_plci) { + dbug(1, dprintf("get_plci: out of PLCIs")); + return 0; + } + plci = &a->plci[i]; + plci->Id = (byte)(i + 1); + + plci->Sig.Id = 0; + plci->NL.Id = 0; + plci->sig_req = 0; + plci->nl_req = 0; + + plci->appl = NULL; + plci->relatedPTYPLCI = NULL; + plci->State = IDLE; + plci->SuppState = IDLE; + plci->channels = 0; + plci->tel = 0; + plci->B1_resource = 0; + plci->B2_prot = 0; + plci->B3_prot = 0; + + plci->command = 0; + plci->m_command = 0; + init_internal_command_queue(plci); + plci->number = 0; + plci->req_in_start = 0; + plci->req_in = 0; + plci->req_out = 0; + plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE; + plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE; + plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE; + + plci->data_sent = false; + plci->send_disc = 0; + plci->sig_global_req = 0; + plci->sig_remove_id = 0; + plci->nl_global_req = 0; + plci->nl_remove_id = 0; + plci->adv_nl = 0; + plci->manufacturer = false; + plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE; + plci->spoofed_msg = 0; + plci->ptyState = 0; + plci->cr_enquiry = false; + plci->hangup_flow_ctrl_timer = 0; + + plci->ncci_ring_list = 0; + for (j = 0; j < MAX_CHANNELS_PER_PLCI; j++) plci->inc_dis_ncci_table[j] = 0; + bitmap_zero(plci->c_ind_mask_table, MAX_APPL); + bitmap_fill(plci->group_optimization_mask_table, MAX_APPL); + plci->fax_connect_info_length = 0; + plci->nsf_control_bits = 0; + plci->ncpi_state = 0x00; + plci->ncpi_buffer[0] = 0; + + plci->requested_options_conn = 0; + plci->requested_options = 0; + plci->notifiedcall = 0; + plci->vswitchstate = 0; + plci->vsprot = 0; + plci->vsprotdialect = 0; + init_b1_config(plci); + dbug(1, dprintf("get_plci(%x)", plci->Id)); + return i + 1; +} + +/*------------------------------------------------------------------*/ +/* put a parameter in the parameter buffer */ +/*------------------------------------------------------------------*/ + +static void add_p(PLCI *plci, byte code, byte *p) +{ + word p_length; + + p_length = 0; + if (p) p_length = p[0]; + add_ie(plci, code, p, p_length); +} + +/*------------------------------------------------------------------*/ +/* put a structure in the parameter buffer */ +/*------------------------------------------------------------------*/ +static void add_s(PLCI *plci, byte code, API_PARSE *p) +{ + if (p) add_ie(plci, code, p->info, (word)p->length); +} + +/*------------------------------------------------------------------*/ +/* put multiple structures in the parameter buffer */ +/*------------------------------------------------------------------*/ +static void add_ss(PLCI *plci, byte code, API_PARSE *p) +{ + byte i; + + if (p) { + dbug(1, dprintf("add_ss(%x,len=%d)", code, p->length)); + for (i = 2; i < (byte)p->length; i += p->info[i] + 2) { + dbug(1, dprintf("add_ss_ie(%x,len=%d)", p->info[i - 1], p->info[i])); + add_ie(plci, p->info[i - 1], (byte *)&(p->info[i]), (word)p->info[i]); + } + } +} + +/*------------------------------------------------------------------*/ +/* return the channel number sent by the application in a esc_chi */ +/*------------------------------------------------------------------*/ +static byte getChannel(API_PARSE *p) +{ + byte i; + + if (p) { + for (i = 2; i < (byte)p->length; i += p->info[i] + 2) { + if (p->info[i] == 2) { + if (p->info[i - 1] == ESC && p->info[i + 1] == CHI) return (p->info[i + 2]); + } + } + } + return 0; +} + + +/*------------------------------------------------------------------*/ +/* put an information element in the parameter buffer */ +/*------------------------------------------------------------------*/ + +static void add_ie(PLCI *plci, byte code, byte *p, word p_length) +{ + word i; + + if (!(code & 0x80) && !p_length) return; + + if (plci->req_in == plci->req_in_start) { + plci->req_in += 2; + } + else { + plci->req_in--; + } + plci->RBuffer[plci->req_in++] = code; + + if (p) { + plci->RBuffer[plci->req_in++] = (byte)p_length; + for (i = 0; i < p_length; i++) plci->RBuffer[plci->req_in++] = p[1 + i]; + } + + plci->RBuffer[plci->req_in++] = 0; +} + +/*------------------------------------------------------------------*/ +/* put a unstructured data into the buffer */ +/*------------------------------------------------------------------*/ + +static void add_d(PLCI *plci, word length, byte *p) +{ + word i; + + if (plci->req_in == plci->req_in_start) { + plci->req_in += 2; + } + else { + plci->req_in--; + } + for (i = 0; i < length; i++) plci->RBuffer[plci->req_in++] = p[i]; +} + +/*------------------------------------------------------------------*/ +/* put parameters from the Additional Info parameter in the */ +/* parameter buffer */ +/*------------------------------------------------------------------*/ + +static void add_ai(PLCI *plci, API_PARSE *ai) +{ + word i; + API_PARSE ai_parms[5]; + + for (i = 0; i < 5; i++) ai_parms[i].length = 0; + + if (!ai->length) + return; + if (api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms)) + return; + + add_s(plci, KEY, &ai_parms[1]); + add_s(plci, UUI, &ai_parms[2]); + add_ss(plci, FTY, &ai_parms[3]); +} + +/*------------------------------------------------------------------*/ +/* put parameter for b1 protocol in the parameter buffer */ +/*------------------------------------------------------------------*/ + +static word add_b1(PLCI *plci, API_PARSE *bp, word b_channel_info, + word b1_facilities) +{ + API_PARSE bp_parms[8]; + API_PARSE mdm_cfg[9]; + API_PARSE global_config[2]; + byte cai[256]; + byte resource[] = {5, 9, 13, 12, 16, 39, 9, 17, 17, 18}; + byte voice_cai[] = "\x06\x14\x00\x00\x00\x00\x08"; + word i; + + API_PARSE mdm_cfg_v18[4]; + word j, n, w; + dword d; + + + for (i = 0; i < 8; i++) bp_parms[i].length = 0; + for (i = 0; i < 2; i++) global_config[i].length = 0; + + dbug(1, dprintf("add_b1")); + api_save_msg(bp, "s", &plci->B_protocol); + + if (b_channel_info == 2) { + plci->B1_resource = 0; + adjust_b1_facilities(plci, plci->B1_resource, b1_facilities); + add_p(plci, CAI, "\x01\x00"); + dbug(1, dprintf("Cai=1,0 (no resource)")); + return 0; + } + + if (plci->tel == CODEC_PERMANENT) return 0; + else if (plci->tel == CODEC) { + plci->B1_resource = 1; + adjust_b1_facilities(plci, plci->B1_resource, b1_facilities); + add_p(plci, CAI, "\x01\x01"); + dbug(1, dprintf("Cai=1,1 (Codec)")); + return 0; + } + else if (plci->tel == ADV_VOICE) { + plci->B1_resource = add_b1_facilities(plci, 9, (word)(b1_facilities | B1_FACILITY_VOICE)); + adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities | B1_FACILITY_VOICE)); + voice_cai[1] = plci->B1_resource; + PUT_WORD(&voice_cai[5], plci->appl->MaxDataLength); + add_p(plci, CAI, voice_cai); + dbug(1, dprintf("Cai=1,0x%x (AdvVoice)", voice_cai[1])); + return 0; + } + plci->call_dir &= ~(CALL_DIR_ORIGINATE | CALL_DIR_ANSWER); + if (plci->call_dir & CALL_DIR_OUT) + plci->call_dir |= CALL_DIR_ORIGINATE; + else if (plci->call_dir & CALL_DIR_IN) + plci->call_dir |= CALL_DIR_ANSWER; + + if (!bp->length) { + plci->B1_resource = 0x5; + adjust_b1_facilities(plci, plci->B1_resource, b1_facilities); + add_p(plci, CAI, "\x01\x05"); + return 0; + } + + dbug(1, dprintf("b_prot_len=%d", (word)bp->length)); + if (bp->length > 256) return _WRONG_MESSAGE_FORMAT; + if (api_parse(&bp->info[1], (word)bp->length, "wwwsssb", bp_parms)) + { + bp_parms[6].length = 0; + if (api_parse(&bp->info[1], (word)bp->length, "wwwsss", bp_parms)) + { + dbug(1, dprintf("b-form.!")); + return _WRONG_MESSAGE_FORMAT; + } + } + else if (api_parse(&bp->info[1], (word)bp->length, "wwwssss", bp_parms)) + { + dbug(1, dprintf("b-form.!")); + return _WRONG_MESSAGE_FORMAT; + } + + if (bp_parms[6].length) + { + if (api_parse(&bp_parms[6].info[1], (word)bp_parms[6].length, "w", global_config)) + { + return _WRONG_MESSAGE_FORMAT; + } + switch (GET_WORD(global_config[0].info)) + { + case 1: + plci->call_dir = (plci->call_dir & ~CALL_DIR_ANSWER) | CALL_DIR_ORIGINATE; + break; + case 2: + plci->call_dir = (plci->call_dir & ~CALL_DIR_ORIGINATE) | CALL_DIR_ANSWER; + break; + } + } + dbug(1, dprintf("call_dir=%04x", plci->call_dir)); + + + if ((GET_WORD(bp_parms[0].info) == B1_RTP) + && (plci->adapter->man_profile.private_options & (1L << PRIVATE_RTP))) + { + plci->B1_resource = add_b1_facilities(plci, 31, (word)(b1_facilities & ~B1_FACILITY_VOICE)); + adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE)); + cai[1] = plci->B1_resource; + cai[2] = 0; + cai[3] = 0; + cai[4] = 0; + PUT_WORD(&cai[5], plci->appl->MaxDataLength); + for (i = 0; i < bp_parms[3].length; i++) + cai[7 + i] = bp_parms[3].info[1 + i]; + cai[0] = 6 + bp_parms[3].length; + add_p(plci, CAI, cai); + return 0; + } + + + if ((GET_WORD(bp_parms[0].info) == B1_PIAFS) + && (plci->adapter->man_profile.private_options & (1L << PRIVATE_PIAFS))) + { + plci->B1_resource = add_b1_facilities(plci, 35/* PIAFS HARDWARE FACILITY */, (word)(b1_facilities & ~B1_FACILITY_VOICE)); + adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE)); + cai[1] = plci->B1_resource; + cai[2] = 0; + cai[3] = 0; + cai[4] = 0; + PUT_WORD(&cai[5], plci->appl->MaxDataLength); + cai[0] = 6; + add_p(plci, CAI, cai); + return 0; + } + + + if ((GET_WORD(bp_parms[0].info) >= 32) + || (!((1L << GET_WORD(bp_parms[0].info)) & plci->adapter->profile.B1_Protocols) + && ((GET_WORD(bp_parms[0].info) != 3) + || !((1L << B1_HDLC) & plci->adapter->profile.B1_Protocols) + || ((bp_parms[3].length != 0) && (GET_WORD(&bp_parms[3].info[1]) != 0) && (GET_WORD(&bp_parms[3].info[1]) != 56000))))) + { + return _B1_NOT_SUPPORTED; + } + plci->B1_resource = add_b1_facilities(plci, resource[GET_WORD(bp_parms[0].info)], + (word)(b1_facilities & ~B1_FACILITY_VOICE)); + adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE)); + cai[0] = 6; + cai[1] = plci->B1_resource; + for (i = 2; i < sizeof(cai); i++) cai[i] = 0; + + if ((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE) + || (GET_WORD(bp_parms[0].info) == B1_MODEM_ASYNC) + || (GET_WORD(bp_parms[0].info) == B1_MODEM_SYNC_HDLC)) + { /* B1 - modem */ + for (i = 0; i < 7; i++) mdm_cfg[i].length = 0; + + if (bp_parms[3].length) + { + if (api_parse(&bp_parms[3].info[1], (word)bp_parms[3].length, "wwwwww", mdm_cfg)) + { + return (_WRONG_MESSAGE_FORMAT); + } + + cai[2] = 0; /* Bit rate for adaptation */ + + dbug(1, dprintf("MDM Max Bit Rate:<%d>", GET_WORD(mdm_cfg[0].info))); + + PUT_WORD(&cai[13], 0); /* Min Tx speed */ + PUT_WORD(&cai[15], GET_WORD(mdm_cfg[0].info)); /* Max Tx speed */ + PUT_WORD(&cai[17], 0); /* Min Rx speed */ + PUT_WORD(&cai[19], GET_WORD(mdm_cfg[0].info)); /* Max Rx speed */ + + cai[3] = 0; /* Async framing parameters */ + switch (GET_WORD(mdm_cfg[2].info)) + { /* Parity */ + case 1: /* odd parity */ + cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_ODD); + dbug(1, dprintf("MDM: odd parity")); + break; + + case 2: /* even parity */ + cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_EVEN); + dbug(1, dprintf("MDM: even parity")); + break; + + default: + dbug(1, dprintf("MDM: no parity")); + break; + } + + switch (GET_WORD(mdm_cfg[3].info)) + { /* stop bits */ + case 1: /* 2 stop bits */ + cai[3] |= DSP_CAI_ASYNC_TWO_STOP_BITS; + dbug(1, dprintf("MDM: 2 stop bits")); + break; + + default: + dbug(1, dprintf("MDM: 1 stop bit")); + break; + } + + switch (GET_WORD(mdm_cfg[1].info)) + { /* char length */ + case 5: + cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_5; + dbug(1, dprintf("MDM: 5 bits")); + break; + + case 6: + cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_6; + dbug(1, dprintf("MDM: 6 bits")); + break; + + case 7: + cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_7; + dbug(1, dprintf("MDM: 7 bits")); + break; + + default: + dbug(1, dprintf("MDM: 8 bits")); + break; + } + + cai[7] = 0; /* Line taking options */ + cai[8] = 0; /* Modulation negotiation options */ + cai[9] = 0; /* Modulation options */ + + if (((plci->call_dir & CALL_DIR_ORIGINATE) != 0) ^ ((plci->call_dir & CALL_DIR_OUT) != 0)) + { + cai[9] |= DSP_CAI_MODEM_REVERSE_DIRECTION; + dbug(1, dprintf("MDM: Reverse direction")); + } + + if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_DISABLE_RETRAIN) + { + cai[9] |= DSP_CAI_MODEM_DISABLE_RETRAIN; + dbug(1, dprintf("MDM: Disable retrain")); + } + + if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_DISABLE_RING_TONE) + { + cai[7] |= DSP_CAI_MODEM_DISABLE_CALLING_TONE | DSP_CAI_MODEM_DISABLE_ANSWER_TONE; + dbug(1, dprintf("MDM: Disable ring tone")); + } + + if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_GUARD_1800) + { + cai[8] |= DSP_CAI_MODEM_GUARD_TONE_1800HZ; + dbug(1, dprintf("MDM: 1800 guard tone")); + } + else if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_GUARD_550) + { + cai[8] |= DSP_CAI_MODEM_GUARD_TONE_550HZ; + dbug(1, dprintf("MDM: 550 guard tone")); + } + + if ((GET_WORD(mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_V100) + { + cai[8] |= DSP_CAI_MODEM_NEGOTIATE_V100; + dbug(1, dprintf("MDM: V100")); + } + else if ((GET_WORD(mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_MOD_CLASS) + { + cai[8] |= DSP_CAI_MODEM_NEGOTIATE_IN_CLASS; + dbug(1, dprintf("MDM: IN CLASS")); + } + else if ((GET_WORD(mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_DISABLED) + { + cai[8] |= DSP_CAI_MODEM_NEGOTIATE_DISABLED; + dbug(1, dprintf("MDM: DISABLED")); + } + cai[0] = 20; + + if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_V18)) + && (GET_WORD(mdm_cfg[5].info) & 0x8000)) /* Private V.18 enable */ + { + plci->requested_options |= 1L << PRIVATE_V18; + } + if (GET_WORD(mdm_cfg[5].info) & 0x4000) /* Private VOWN enable */ + plci->requested_options |= 1L << PRIVATE_VOWN; + + if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1]) + & ((1L << PRIVATE_V18) | (1L << PRIVATE_VOWN))) + { + if (!api_parse(&bp_parms[3].info[1], (word)bp_parms[3].length, "wwwwwws", mdm_cfg)) + { + i = 27; + if (mdm_cfg[6].length >= 4) + { + d = GET_DWORD(&mdm_cfg[6].info[1]); + cai[7] |= (byte) d; /* line taking options */ + cai[9] |= (byte)(d >> 8); /* modulation options */ + cai[++i] = (byte)(d >> 16); /* vown modulation options */ + cai[++i] = (byte)(d >> 24); + if (mdm_cfg[6].length >= 8) + { + d = GET_DWORD(&mdm_cfg[6].info[5]); + cai[10] |= (byte) d; /* disabled modulations mask */ + cai[11] |= (byte)(d >> 8); + if (mdm_cfg[6].length >= 12) + { + d = GET_DWORD(&mdm_cfg[6].info[9]); + cai[12] = (byte) d; /* enabled modulations mask */ + cai[++i] = (byte)(d >> 8); /* vown enabled modulations */ + cai[++i] = (byte)(d >> 16); + cai[++i] = (byte)(d >> 24); + cai[++i] = 0; + if (mdm_cfg[6].length >= 14) + { + w = GET_WORD(&mdm_cfg[6].info[13]); + if (w != 0) + PUT_WORD(&cai[13], w); /* min tx speed */ + if (mdm_cfg[6].length >= 16) + { + w = GET_WORD(&mdm_cfg[6].info[15]); + if (w != 0) + PUT_WORD(&cai[15], w); /* max tx speed */ + if (mdm_cfg[6].length >= 18) + { + w = GET_WORD(&mdm_cfg[6].info[17]); + if (w != 0) + PUT_WORD(&cai[17], w); /* min rx speed */ + if (mdm_cfg[6].length >= 20) + { + w = GET_WORD(&mdm_cfg[6].info[19]); + if (w != 0) + PUT_WORD(&cai[19], w); /* max rx speed */ + if (mdm_cfg[6].length >= 22) + { + w = GET_WORD(&mdm_cfg[6].info[21]); + cai[23] = (byte)(-((short) w)); /* transmit level */ + if (mdm_cfg[6].length >= 24) + { + w = GET_WORD(&mdm_cfg[6].info[23]); + cai[22] |= (byte) w; /* info options mask */ + cai[21] |= (byte)(w >> 8); /* disabled symbol rates */ + } + } + } + } + } + } + } + } + } + cai[27] = i - 27; + i++; + if (!api_parse(&bp_parms[3].info[1], (word)bp_parms[3].length, "wwwwwwss", mdm_cfg)) + { + if (!api_parse(&mdm_cfg[7].info[1], (word)mdm_cfg[7].length, "sss", mdm_cfg_v18)) + { + for (n = 0; n < 3; n++) + { + cai[i] = (byte)(mdm_cfg_v18[n].length); + for (j = 1; j < ((word)(cai[i] + 1)); j++) + cai[i + j] = mdm_cfg_v18[n].info[j]; + i += cai[i] + 1; + } + } + } + cai[0] = (byte)(i - 1); + } + } + + } + } + if (GET_WORD(bp_parms[0].info) == 2 || /* V.110 async */ + GET_WORD(bp_parms[0].info) == 3) /* V.110 sync */ + { + if (bp_parms[3].length) { + dbug(1, dprintf("V.110,%d", GET_WORD(&bp_parms[3].info[1]))); + switch (GET_WORD(&bp_parms[3].info[1])) { /* Rate */ + case 0: + case 56000: + if (GET_WORD(bp_parms[0].info) == 3) { /* V.110 sync 56k */ + dbug(1, dprintf("56k sync HSCX")); + cai[1] = 8; + cai[2] = 0; + cai[3] = 0; + } + else if (GET_WORD(bp_parms[0].info) == 2) { + dbug(1, dprintf("56k async DSP")); + cai[2] = 9; + } + break; + case 50: cai[2] = 1; break; + case 75: cai[2] = 1; break; + case 110: cai[2] = 1; break; + case 150: cai[2] = 1; break; + case 200: cai[2] = 1; break; + case 300: cai[2] = 1; break; + case 600: cai[2] = 1; break; + case 1200: cai[2] = 2; break; + case 2400: cai[2] = 3; break; + case 4800: cai[2] = 4; break; + case 7200: cai[2] = 10; break; + case 9600: cai[2] = 5; break; + case 12000: cai[2] = 13; break; + case 24000: cai[2] = 0; break; + case 14400: cai[2] = 11; break; + case 19200: cai[2] = 6; break; + case 28800: cai[2] = 12; break; + case 38400: cai[2] = 7; break; + case 48000: cai[2] = 8; break; + case 76: cai[2] = 15; break; /* 75/1200 */ + case 1201: cai[2] = 14; break; /* 1200/75 */ + case 56001: cai[2] = 9; break; /* V.110 56000 */ + + default: + return _B1_PARM_NOT_SUPPORTED; + } + cai[3] = 0; + if (cai[1] == 13) /* v.110 async */ + { + if (bp_parms[3].length >= 8) + { + switch (GET_WORD(&bp_parms[3].info[3])) + { /* char length */ + case 5: + cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_5; + break; + case 6: + cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_6; + break; + case 7: + cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_7; + break; + } + switch (GET_WORD(&bp_parms[3].info[5])) + { /* Parity */ + case 1: /* odd parity */ + cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_ODD); + break; + case 2: /* even parity */ + cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_EVEN); + break; + } + switch (GET_WORD(&bp_parms[3].info[7])) + { /* stop bits */ + case 1: /* 2 stop bits */ + cai[3] |= DSP_CAI_ASYNC_TWO_STOP_BITS; + break; + } + } + } + } + else if (cai[1] == 8 || GET_WORD(bp_parms[0].info) == 3) { + dbug(1, dprintf("V.110 default 56k sync")); + cai[1] = 8; + cai[2] = 0; + cai[3] = 0; + } + else { + dbug(1, dprintf("V.110 default 9600 async")); + cai[2] = 5; + } + } + PUT_WORD(&cai[5], plci->appl->MaxDataLength); + dbug(1, dprintf("CAI[%d]=%x,%x,%x,%x,%x,%x", cai[0], cai[1], cai[2], cai[3], cai[4], cai[5], cai[6])); +/* HexDump ("CAI", sizeof(cai), &cai[0]); */ + + add_p(plci, CAI, cai); + return 0; +} + +/*------------------------------------------------------------------*/ +/* put parameter for b2 and B3 protocol in the parameter buffer */ +/*------------------------------------------------------------------*/ + +static word add_b23(PLCI *plci, API_PARSE *bp) +{ + word i, fax_control_bits; + byte pos, len; + byte SAPI = 0x40; /* default SAPI 16 for x.31 */ + API_PARSE bp_parms[8]; + API_PARSE *b1_config; + API_PARSE *b2_config; + API_PARSE b2_config_parms[8]; + API_PARSE *b3_config; + API_PARSE b3_config_parms[6]; + API_PARSE global_config[2]; + + static byte llc[3] = {2,0,0}; + static byte dlc[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + static byte nlc[256]; + static byte lli[12] = {1,1}; + + const byte llc2_out[] = {1,2,4,6,2,0,0,0, X75_V42BIS,V120_L2,V120_V42BIS,V120_L2,6}; + const byte llc2_in[] = {1,3,4,6,3,0,0,0, X75_V42BIS,V120_L2,V120_V42BIS,V120_L2,6}; + + const byte llc3[] = {4,3,2,2,6,6,0}; + const byte header[] = {0,2,3,3,0,0,0}; + + for (i = 0; i < 8; i++) bp_parms[i].length = 0; + for (i = 0; i < 6; i++) b2_config_parms[i].length = 0; + for (i = 0; i < 5; i++) b3_config_parms[i].length = 0; + + lli[0] = 1; + lli[1] = 1; + if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL) + lli[1] |= 2; + if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL) + lli[1] |= 4; + + if ((lli[1] & 0x02) && (diva_xdi_extended_features & DIVA_CAPI_USE_CMA)) { + lli[1] |= 0x10; + if (plci->rx_dma_descriptor <= 0) { + plci->rx_dma_descriptor = diva_get_dma_descriptor(plci, &plci->rx_dma_magic); + if (plci->rx_dma_descriptor >= 0) + plci->rx_dma_descriptor++; + } + if (plci->rx_dma_descriptor > 0) { + lli[0] = 6; + lli[1] |= 0x40; + lli[2] = (byte)(plci->rx_dma_descriptor - 1); + lli[3] = (byte)plci->rx_dma_magic; + lli[4] = (byte)(plci->rx_dma_magic >> 8); + lli[5] = (byte)(plci->rx_dma_magic >> 16); + lli[6] = (byte)(plci->rx_dma_magic >> 24); + } + } + + if (DIVA_CAPI_SUPPORTS_NO_CANCEL(plci->adapter)) { + lli[1] |= 0x20; + } + + dbug(1, dprintf("add_b23")); + api_save_msg(bp, "s", &plci->B_protocol); + + if (!bp->length && plci->tel) + { + plci->adv_nl = true; + dbug(1, dprintf("Default adv.Nl")); + add_p(plci, LLI, lli); + plci->B2_prot = 1 /*XPARENT*/; + plci->B3_prot = 0 /*XPARENT*/; + llc[1] = 2; + llc[2] = 4; + add_p(plci, LLC, llc); + dlc[0] = 2; + PUT_WORD(&dlc[1], plci->appl->MaxDataLength); + add_p(plci, DLC, dlc); + return 0; + } + + if (!bp->length) /*default*/ + { + dbug(1, dprintf("ret default")); + add_p(plci, LLI, lli); + plci->B2_prot = 0 /*X.75 */; + plci->B3_prot = 0 /*XPARENT*/; + llc[1] = 1; + llc[2] = 4; + add_p(plci, LLC, llc); + dlc[0] = 2; + PUT_WORD(&dlc[1], plci->appl->MaxDataLength); + add_p(plci, DLC, dlc); + return 0; + } + dbug(1, dprintf("b_prot_len=%d", (word)bp->length)); + if ((word)bp->length > 256) return _WRONG_MESSAGE_FORMAT; + + if (api_parse(&bp->info[1], (word)bp->length, "wwwsssb", bp_parms)) + { + bp_parms[6].length = 0; + if (api_parse(&bp->info[1], (word)bp->length, "wwwsss", bp_parms)) + { + dbug(1, dprintf("b-form.!")); + return _WRONG_MESSAGE_FORMAT; + } + } + else if (api_parse(&bp->info[1], (word)bp->length, "wwwssss", bp_parms)) + { + dbug(1, dprintf("b-form.!")); + return _WRONG_MESSAGE_FORMAT; + } + + if (plci->tel == ADV_VOICE) /* transparent B on advanced voice */ + { + if (GET_WORD(bp_parms[1].info) != 1 + || GET_WORD(bp_parms[2].info) != 0) return _B2_NOT_SUPPORTED; + plci->adv_nl = true; + } + else if (plci->tel) return _B2_NOT_SUPPORTED; + + + if ((GET_WORD(bp_parms[1].info) == B2_RTP) + && (GET_WORD(bp_parms[2].info) == B3_RTP) + && (plci->adapter->man_profile.private_options & (1L << PRIVATE_RTP))) + { + add_p(plci, LLI, lli); + plci->B2_prot = (byte) GET_WORD(bp_parms[1].info); + plci->B3_prot = (byte) GET_WORD(bp_parms[2].info); + llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ? 14 : 13; + llc[2] = 4; + add_p(plci, LLC, llc); + dlc[0] = 2; + PUT_WORD(&dlc[1], plci->appl->MaxDataLength); + dlc[3] = 3; /* Addr A */ + dlc[4] = 1; /* Addr B */ + dlc[5] = 7; /* modulo mode */ + dlc[6] = 7; /* window size */ + dlc[7] = 0; /* XID len Lo */ + dlc[8] = 0; /* XID len Hi */ + for (i = 0; i < bp_parms[4].length; i++) + dlc[9 + i] = bp_parms[4].info[1 + i]; + dlc[0] = (byte)(8 + bp_parms[4].length); + add_p(plci, DLC, dlc); + for (i = 0; i < bp_parms[5].length; i++) + nlc[1 + i] = bp_parms[5].info[1 + i]; + nlc[0] = (byte)(bp_parms[5].length); + add_p(plci, NLC, nlc); + return 0; + } + + + + if ((GET_WORD(bp_parms[1].info) >= 32) + || (!((1L << GET_WORD(bp_parms[1].info)) & plci->adapter->profile.B2_Protocols) + && ((GET_WORD(bp_parms[1].info) != B2_PIAFS) + || !(plci->adapter->man_profile.private_options & (1L << PRIVATE_PIAFS))))) + + { + return _B2_NOT_SUPPORTED; + } + if ((GET_WORD(bp_parms[2].info) >= 32) + || !((1L << GET_WORD(bp_parms[2].info)) & plci->adapter->profile.B3_Protocols)) + { + return _B3_NOT_SUPPORTED; + } + if ((GET_WORD(bp_parms[1].info) != B2_SDLC) + && ((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE) + || (GET_WORD(bp_parms[0].info) == B1_MODEM_ASYNC) + || (GET_WORD(bp_parms[0].info) == B1_MODEM_SYNC_HDLC))) + { + return (add_modem_b23(plci, bp_parms)); + } + + add_p(plci, LLI, lli); + + plci->B2_prot = (byte)GET_WORD(bp_parms[1].info); + plci->B3_prot = (byte)GET_WORD(bp_parms[2].info); + if (plci->B2_prot == 12) SAPI = 0; /* default SAPI D-channel */ + + if (bp_parms[6].length) + { + if (api_parse(&bp_parms[6].info[1], (word)bp_parms[6].length, "w", global_config)) + { + return _WRONG_MESSAGE_FORMAT; + } + switch (GET_WORD(global_config[0].info)) + { + case 1: + plci->call_dir = (plci->call_dir & ~CALL_DIR_ANSWER) | CALL_DIR_ORIGINATE; + break; + case 2: + plci->call_dir = (plci->call_dir & ~CALL_DIR_ORIGINATE) | CALL_DIR_ANSWER; + break; + } + } + dbug(1, dprintf("call_dir=%04x", plci->call_dir)); + + + if (plci->B2_prot == B2_PIAFS) + llc[1] = PIAFS_CRC; + else +/* IMPLEMENT_PIAFS */ + { + llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ? + llc2_out[GET_WORD(bp_parms[1].info)] : llc2_in[GET_WORD(bp_parms[1].info)]; + } + llc[2] = llc3[GET_WORD(bp_parms[2].info)]; + + add_p(plci, LLC, llc); + + dlc[0] = 2; + PUT_WORD(&dlc[1], plci->appl->MaxDataLength + + header[GET_WORD(bp_parms[2].info)]); + + b1_config = &bp_parms[3]; + nlc[0] = 0; + if (plci->B3_prot == 4 + || plci->B3_prot == 5) + { + for (i = 0; i < sizeof(T30_INFO); i++) nlc[i] = 0; + nlc[0] = sizeof(T30_INFO); + if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS) + ((T30_INFO *)&nlc[1])->operating_mode = T30_OPERATING_MODE_CAPI; + ((T30_INFO *)&nlc[1])->rate_div_2400 = 0xff; + if (b1_config->length >= 2) + { + ((T30_INFO *)&nlc[1])->rate_div_2400 = (byte)(GET_WORD(&b1_config->info[1]) / 2400); + } + } + b2_config = &bp_parms[4]; + + + if (llc[1] == PIAFS_CRC) + { + if (plci->B3_prot != B3_TRANSPARENT) + { + return _B_STACK_NOT_SUPPORTED; + } + if (b2_config->length && api_parse(&b2_config->info[1], (word)b2_config->length, "bwww", b2_config_parms)) { + return _WRONG_MESSAGE_FORMAT; + } + PUT_WORD(&dlc[1], plci->appl->MaxDataLength); + dlc[3] = 0; /* Addr A */ + dlc[4] = 0; /* Addr B */ + dlc[5] = 0; /* modulo mode */ + dlc[6] = 0; /* window size */ + if (b2_config->length >= 7) { + dlc[7] = 7; + dlc[8] = 0; + dlc[9] = b2_config_parms[0].info[0]; /* PIAFS protocol Speed configuration */ + dlc[10] = b2_config_parms[1].info[0]; /* V.42bis P0 */ + dlc[11] = b2_config_parms[1].info[1]; /* V.42bis P0 */ + dlc[12] = b2_config_parms[2].info[0]; /* V.42bis P1 */ + dlc[13] = b2_config_parms[2].info[1]; /* V.42bis P1 */ + dlc[14] = b2_config_parms[3].info[0]; /* V.42bis P2 */ + dlc[15] = b2_config_parms[3].info[1]; /* V.42bis P2 */ + dlc[0] = 15; + if (b2_config->length >= 8) { /* PIAFS control abilities */ + dlc[7] = 10; + dlc[16] = 2; /* Length of PIAFS extension */ + dlc[17] = PIAFS_UDATA_ABILITIES; /* control (UDATA) ability */ + dlc[18] = b2_config_parms[4].info[0]; /* value */ + dlc[0] = 18; + } + } + else /* default values, 64K, variable, no compression */ + { + dlc[7] = 7; + dlc[8] = 0; + dlc[9] = 0x03; /* PIAFS protocol Speed configuration */ + dlc[10] = 0x03; /* V.42bis P0 */ + dlc[11] = 0; /* V.42bis P0 */ + dlc[12] = 0; /* V.42bis P1 */ + dlc[13] = 0; /* V.42bis P1 */ + dlc[14] = 0; /* V.42bis P2 */ + dlc[15] = 0; /* V.42bis P2 */ + dlc[0] = 15; + } + add_p(plci, DLC, dlc); + } + else + + if ((llc[1] == V120_L2) || (llc[1] == V120_V42BIS)) + { + if (plci->B3_prot != B3_TRANSPARENT) + return _B_STACK_NOT_SUPPORTED; + + dlc[0] = 6; + PUT_WORD(&dlc[1], GET_WORD(&dlc[1]) + 2); + dlc[3] = 0x08; + dlc[4] = 0x01; + dlc[5] = 127; + dlc[6] = 7; + if (b2_config->length != 0) + { + if ((llc[1] == V120_V42BIS) && api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbwww", b2_config_parms)) { + return _WRONG_MESSAGE_FORMAT; + } + dlc[3] = (byte)((b2_config->info[2] << 3) | ((b2_config->info[1] >> 5) & 0x04)); + dlc[4] = (byte)((b2_config->info[1] << 1) | 0x01); + if (b2_config->info[3] != 128) + { + dbug(1, dprintf("1D-dlc= %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4])); + return _B2_PARM_NOT_SUPPORTED; + } + dlc[5] = (byte)(b2_config->info[3] - 1); + dlc[6] = b2_config->info[4]; + if (llc[1] == V120_V42BIS) { + if (b2_config->length >= 10) { + dlc[7] = 6; + dlc[8] = 0; + dlc[9] = b2_config_parms[4].info[0]; + dlc[10] = b2_config_parms[4].info[1]; + dlc[11] = b2_config_parms[5].info[0]; + dlc[12] = b2_config_parms[5].info[1]; + dlc[13] = b2_config_parms[6].info[0]; + dlc[14] = b2_config_parms[6].info[1]; + dlc[0] = 14; + dbug(1, dprintf("b2_config_parms[4].info[0] [1]: %x %x", b2_config_parms[4].info[0], b2_config_parms[4].info[1])); + dbug(1, dprintf("b2_config_parms[5].info[0] [1]: %x %x", b2_config_parms[5].info[0], b2_config_parms[5].info[1])); + dbug(1, dprintf("b2_config_parms[6].info[0] [1]: %x %x", b2_config_parms[6].info[0], b2_config_parms[6].info[1])); + } + else { + dlc[6] = 14; + } + } + } + } + else + { + if (b2_config->length) + { + dbug(1, dprintf("B2-Config")); + if (llc[1] == X75_V42BIS) { + if (api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbwww", b2_config_parms)) + { + return _WRONG_MESSAGE_FORMAT; + } + } + else { + if (api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbs", b2_config_parms)) + { + return _WRONG_MESSAGE_FORMAT; + } + } + /* if B2 Protocol is LAPD, b2_config structure is different */ + if (llc[1] == 6) + { + dlc[0] = 4; + if (b2_config->length >= 1) dlc[2] = b2_config->info[1]; /* TEI */ + else dlc[2] = 0x01; + if ((b2_config->length >= 2) && (plci->B2_prot == 12)) + { + SAPI = b2_config->info[2]; /* SAPI */ + } + dlc[1] = SAPI; + if ((b2_config->length >= 3) && (b2_config->info[3] == 128)) + { + dlc[3] = 127; /* Mode */ + } + else + { + dlc[3] = 7; /* Mode */ + } + + if (b2_config->length >= 4) dlc[4] = b2_config->info[4]; /* Window */ + else dlc[4] = 1; + dbug(1, dprintf("D-dlc[%d]=%x,%x,%x,%x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4])); + if (b2_config->length > 5) return _B2_PARM_NOT_SUPPORTED; + } + else + { + dlc[0] = (byte)(b2_config_parms[4].length + 6); + dlc[3] = b2_config->info[1]; + dlc[4] = b2_config->info[2]; + if (b2_config->info[3] != 8 && b2_config->info[3] != 128) { + dbug(1, dprintf("1D-dlc= %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4])); + return _B2_PARM_NOT_SUPPORTED; + } + + dlc[5] = (byte)(b2_config->info[3] - 1); + dlc[6] = b2_config->info[4]; + if (dlc[6] > dlc[5]) { + dbug(1, dprintf("2D-dlc= %x %x %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4], dlc[5], dlc[6])); + return _B2_PARM_NOT_SUPPORTED; + } + + if (llc[1] == X75_V42BIS) { + if (b2_config->length >= 10) { + dlc[7] = 6; + dlc[8] = 0; + dlc[9] = b2_config_parms[4].info[0]; + dlc[10] = b2_config_parms[4].info[1]; + dlc[11] = b2_config_parms[5].info[0]; + dlc[12] = b2_config_parms[5].info[1]; + dlc[13] = b2_config_parms[6].info[0]; + dlc[14] = b2_config_parms[6].info[1]; + dlc[0] = 14; + dbug(1, dprintf("b2_config_parms[4].info[0] [1]: %x %x", b2_config_parms[4].info[0], b2_config_parms[4].info[1])); + dbug(1, dprintf("b2_config_parms[5].info[0] [1]: %x %x", b2_config_parms[5].info[0], b2_config_parms[5].info[1])); + dbug(1, dprintf("b2_config_parms[6].info[0] [1]: %x %x", b2_config_parms[6].info[0], b2_config_parms[6].info[1])); + } + else { + dlc[6] = 14; + } + + } + else { + PUT_WORD(&dlc[7], (word)b2_config_parms[4].length); + for (i = 0; i < b2_config_parms[4].length; i++) + dlc[11 + i] = b2_config_parms[4].info[1 + i]; + } + } + } + } + add_p(plci, DLC, dlc); + + b3_config = &bp_parms[5]; + if (b3_config->length) + { + if (plci->B3_prot == 4 + || plci->B3_prot == 5) + { + if (api_parse(&b3_config->info[1], (word)b3_config->length, "wwss", b3_config_parms)) + { + return _WRONG_MESSAGE_FORMAT; + } + i = GET_WORD((byte *)(b3_config_parms[0].info)); + ((T30_INFO *)&nlc[1])->resolution = (byte)(((i & 0x0001) || + ((plci->B3_prot == 4) && (((byte)(GET_WORD((byte *)b3_config_parms[1].info))) != 5))) ? T30_RESOLUTION_R8_0770_OR_200 : 0); + ((T30_INFO *)&nlc[1])->data_format = (byte)(GET_WORD((byte *)b3_config_parms[1].info)); + fax_control_bits = T30_CONTROL_BIT_ALL_FEATURES; + if ((((T30_INFO *)&nlc[1])->rate_div_2400 != 0) && (((T30_INFO *)&nlc[1])->rate_div_2400 <= 6)) + fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_V34FAX; + if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS) + { + + if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1]) + & (1L << PRIVATE_FAX_PAPER_FORMATS)) + { + ((T30_INFO *)&nlc[1])->resolution |= T30_RESOLUTION_R8_1540 | + T30_RESOLUTION_R16_1540_OR_400 | T30_RESOLUTION_300_300 | + T30_RESOLUTION_INCH_BASED | T30_RESOLUTION_METRIC_BASED; + } + + ((T30_INFO *)&nlc[1])->recording_properties = + T30_RECORDING_WIDTH_ISO_A3 | + (T30_RECORDING_LENGTH_UNLIMITED << 2) | + (T30_MIN_SCANLINE_TIME_00_00_00 << 4); + } + if (plci->B3_prot == 5) + { + if (i & 0x0002) /* Accept incoming fax-polling requests */ + fax_control_bits |= T30_CONTROL_BIT_ACCEPT_POLLING; + if (i & 0x2000) /* Do not use MR compression */ + fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_2D_CODING; + if (i & 0x4000) /* Do not use MMR compression */ + fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_T6_CODING; + if (i & 0x8000) /* Do not use ECM */ + fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_ECM; + if (plci->fax_connect_info_length != 0) + { + ((T30_INFO *)&nlc[1])->resolution = ((T30_INFO *)plci->fax_connect_info_buffer)->resolution; + ((T30_INFO *)&nlc[1])->data_format = ((T30_INFO *)plci->fax_connect_info_buffer)->data_format; + ((T30_INFO *)&nlc[1])->recording_properties = ((T30_INFO *)plci->fax_connect_info_buffer)->recording_properties; + fax_control_bits |= GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low) & + (T30_CONTROL_BIT_REQUEST_POLLING | T30_CONTROL_BIT_MORE_DOCUMENTS); + } + } + /* copy station id to NLC */ + for (i = 0; i < T30_MAX_STATION_ID_LENGTH; i++) + { + if (i < b3_config_parms[2].length) + { + ((T30_INFO *)&nlc[1])->station_id[i] = ((byte *)b3_config_parms[2].info)[1 + i]; + } + else + { + ((T30_INFO *)&nlc[1])->station_id[i] = ' '; + } + } + ((T30_INFO *)&nlc[1])->station_id_len = T30_MAX_STATION_ID_LENGTH; + /* copy head line to NLC */ + if (b3_config_parms[3].length) + { + + pos = (byte)(fax_head_line_time(&(((T30_INFO *)&nlc[1])->station_id[T30_MAX_STATION_ID_LENGTH]))); + if (pos != 0) + { + if (CAPI_MAX_DATE_TIME_LENGTH + 2 + b3_config_parms[3].length > CAPI_MAX_HEAD_LINE_SPACE) + pos = 0; + else + { + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' '; + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' '; + len = (byte)b3_config_parms[2].length; + if (len > 20) + len = 20; + if (CAPI_MAX_DATE_TIME_LENGTH + 2 + len + 2 + b3_config_parms[3].length <= CAPI_MAX_HEAD_LINE_SPACE) + { + for (i = 0; i < len; i++) + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ((byte *)b3_config_parms[2].info)[1 + i]; + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' '; + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' '; + } + } + } + + len = (byte)b3_config_parms[3].length; + if (len > CAPI_MAX_HEAD_LINE_SPACE - pos) + len = (byte)(CAPI_MAX_HEAD_LINE_SPACE - pos); + ((T30_INFO *)&nlc[1])->head_line_len = (byte)(pos + len); + nlc[0] += (byte)(pos + len); + for (i = 0; i < len; i++) + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ((byte *)b3_config_parms[3].info)[1 + i]; + } else + ((T30_INFO *)&nlc[1])->head_line_len = 0; + + plci->nsf_control_bits = 0; + if (plci->B3_prot == 5) + { + if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_FAX_SUB_SEP_PWD)) + && (GET_WORD((byte *)b3_config_parms[1].info) & 0x8000)) /* Private SUB/SEP/PWD enable */ + { + plci->requested_options |= 1L << PRIVATE_FAX_SUB_SEP_PWD; + } + if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_FAX_NONSTANDARD)) + && (GET_WORD((byte *)b3_config_parms[1].info) & 0x4000)) /* Private non-standard facilities enable */ + { + plci->requested_options |= 1L << PRIVATE_FAX_NONSTANDARD; + } + if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1]) + & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD))) + { + if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1]) + & (1L << PRIVATE_FAX_SUB_SEP_PWD)) + { + fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_PASSWORD; + if (fax_control_bits & T30_CONTROL_BIT_ACCEPT_POLLING) + fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING; + } + len = nlc[0]; + pos = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH; + if (pos < plci->fax_connect_info_length) + { + for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--) + nlc[++len] = plci->fax_connect_info_buffer[pos++]; + } + else + nlc[++len] = 0; + if (pos < plci->fax_connect_info_length) + { + for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--) + nlc[++len] = plci->fax_connect_info_buffer[pos++]; + } + else + nlc[++len] = 0; + if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1]) + & (1L << PRIVATE_FAX_NONSTANDARD)) + { + if ((pos < plci->fax_connect_info_length) && (plci->fax_connect_info_buffer[pos] != 0)) + { + if ((plci->fax_connect_info_buffer[pos] >= 3) && (plci->fax_connect_info_buffer[pos + 1] >= 2)) + plci->nsf_control_bits = GET_WORD(&plci->fax_connect_info_buffer[pos + 2]); + for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--) + nlc[++len] = plci->fax_connect_info_buffer[pos++]; + } + else + { + if (api_parse(&b3_config->info[1], (word)b3_config->length, "wwsss", b3_config_parms)) + { + dbug(1, dprintf("non-standard facilities info missing or wrong format")); + nlc[++len] = 0; + } + else + { + if ((b3_config_parms[4].length >= 3) && (b3_config_parms[4].info[1] >= 2)) + plci->nsf_control_bits = GET_WORD(&b3_config_parms[4].info[2]); + nlc[++len] = (byte)(b3_config_parms[4].length); + for (i = 0; i < b3_config_parms[4].length; i++) + nlc[++len] = b3_config_parms[4].info[1 + i]; + } + } + } + nlc[0] = len; + if ((plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF) + && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP)) + { + ((T30_INFO *)&nlc[1])->operating_mode = T30_OPERATING_MODE_CAPI_NEG; + } + } + } + + PUT_WORD(&(((T30_INFO *)&nlc[1])->control_bits_low), fax_control_bits); + len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH; + for (i = 0; i < len; i++) + plci->fax_connect_info_buffer[i] = nlc[1 + i]; + ((T30_INFO *) plci->fax_connect_info_buffer)->head_line_len = 0; + i += ((T30_INFO *)&nlc[1])->head_line_len; + while (i < nlc[0]) + plci->fax_connect_info_buffer[len++] = nlc[++i]; + plci->fax_connect_info_length = len; + } + else + { + nlc[0] = 14; + if (b3_config->length != 16) + return _B3_PARM_NOT_SUPPORTED; + for (i = 0; i < 12; i++) nlc[1 + i] = b3_config->info[1 + i]; + if (GET_WORD(&b3_config->info[13]) != 8 && GET_WORD(&b3_config->info[13]) != 128) + return _B3_PARM_NOT_SUPPORTED; + nlc[13] = b3_config->info[13]; + if (GET_WORD(&b3_config->info[15]) >= nlc[13]) + return _B3_PARM_NOT_SUPPORTED; + nlc[14] = b3_config->info[15]; + } + } + else + { + if (plci->B3_prot == 4 + || plci->B3_prot == 5 /*T.30 - FAX*/) return _B3_PARM_NOT_SUPPORTED; + } + add_p(plci, NLC, nlc); + return 0; +} + +/*----------------------------------------------------------------*/ +/* make the same as add_b23, but only for the modem related */ +/* L2 and L3 B-Chan protocol. */ +/* */ +/* Enabled L2 and L3 Configurations: */ +/* If L1 == Modem all negotiation */ +/* only L2 == Modem with full negotiation is allowed */ +/* If L1 == Modem async or sync */ +/* only L2 == Transparent is allowed */ +/* L3 == Modem or L3 == Transparent are allowed */ +/* B2 Configuration for modem: */ +/* word : enable/disable compression, bitoptions */ +/* B3 Configuration for modem: */ +/* empty */ +/*----------------------------------------------------------------*/ +static word add_modem_b23(PLCI *plci, API_PARSE *bp_parms) +{ + static byte lli[12] = {1,1}; + static byte llc[3] = {2,0,0}; + static byte dlc[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + API_PARSE mdm_config[2]; + word i; + word b2_config = 0; + + for (i = 0; i < 2; i++) mdm_config[i].length = 0; + for (i = 0; i < sizeof(dlc); i++) dlc[i] = 0; + + if (((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE) + && (GET_WORD(bp_parms[1].info) != B2_MODEM_EC_COMPRESSION)) + || ((GET_WORD(bp_parms[0].info) != B1_MODEM_ALL_NEGOTIATE) + && (GET_WORD(bp_parms[1].info) != B2_TRANSPARENT))) + { + return (_B_STACK_NOT_SUPPORTED); + } + if ((GET_WORD(bp_parms[2].info) != B3_MODEM) + && (GET_WORD(bp_parms[2].info) != B3_TRANSPARENT)) + { + return (_B_STACK_NOT_SUPPORTED); + } + + plci->B2_prot = (byte) GET_WORD(bp_parms[1].info); + plci->B3_prot = (byte) GET_WORD(bp_parms[2].info); + + if ((GET_WORD(bp_parms[1].info) == B2_MODEM_EC_COMPRESSION) && bp_parms[4].length) + { + if (api_parse(&bp_parms[4].info[1], + (word)bp_parms[4].length, "w", + mdm_config)) + { + return (_WRONG_MESSAGE_FORMAT); + } + b2_config = GET_WORD(mdm_config[0].info); + } + + /* OK, L2 is modem */ + + lli[0] = 1; + lli[1] = 1; + if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL) + lli[1] |= 2; + if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL) + lli[1] |= 4; + + if ((lli[1] & 0x02) && (diva_xdi_extended_features & DIVA_CAPI_USE_CMA)) { + lli[1] |= 0x10; + if (plci->rx_dma_descriptor <= 0) { + plci->rx_dma_descriptor = diva_get_dma_descriptor(plci, &plci->rx_dma_magic); + if (plci->rx_dma_descriptor >= 0) + plci->rx_dma_descriptor++; + } + if (plci->rx_dma_descriptor > 0) { + lli[1] |= 0x40; + lli[0] = 6; + lli[2] = (byte)(plci->rx_dma_descriptor - 1); + lli[3] = (byte)plci->rx_dma_magic; + lli[4] = (byte)(plci->rx_dma_magic >> 8); + lli[5] = (byte)(plci->rx_dma_magic >> 16); + lli[6] = (byte)(plci->rx_dma_magic >> 24); + } + } + + if (DIVA_CAPI_SUPPORTS_NO_CANCEL(plci->adapter)) { + lli[1] |= 0x20; + } + + llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ? + /*V42*/ 10 : /*V42_IN*/ 9; + llc[2] = 4; /* pass L3 always transparent */ + add_p(plci, LLI, lli); + add_p(plci, LLC, llc); + i = 1; + PUT_WORD(&dlc[i], plci->appl->MaxDataLength); + i += 2; + if (GET_WORD(bp_parms[1].info) == B2_MODEM_EC_COMPRESSION) + { + if (bp_parms[4].length) + { + dbug(1, dprintf("MDM b2_config=%02x", b2_config)); + dlc[i++] = 3; /* Addr A */ + dlc[i++] = 1; /* Addr B */ + dlc[i++] = 7; /* modulo mode */ + dlc[i++] = 7; /* window size */ + dlc[i++] = 0; /* XID len Lo */ + dlc[i++] = 0; /* XID len Hi */ + + if (b2_config & MDM_B2_DISABLE_V42bis) + { + dlc[i] |= DLC_MODEMPROT_DISABLE_V42_V42BIS; + } + if (b2_config & MDM_B2_DISABLE_MNP) + { + dlc[i] |= DLC_MODEMPROT_DISABLE_MNP_MNP5; + } + if (b2_config & MDM_B2_DISABLE_TRANS) + { + dlc[i] |= DLC_MODEMPROT_REQUIRE_PROTOCOL; + } + if (b2_config & MDM_B2_DISABLE_V42) + { + dlc[i] |= DLC_MODEMPROT_DISABLE_V42_DETECT; + } + if (b2_config & MDM_B2_DISABLE_COMP) + { + dlc[i] |= DLC_MODEMPROT_DISABLE_COMPRESSION; + } + i++; + } + } + else + { + dlc[i++] = 3; /* Addr A */ + dlc[i++] = 1; /* Addr B */ + dlc[i++] = 7; /* modulo mode */ + dlc[i++] = 7; /* window size */ + dlc[i++] = 0; /* XID len Lo */ + dlc[i++] = 0; /* XID len Hi */ + dlc[i++] = DLC_MODEMPROT_DISABLE_V42_V42BIS | + DLC_MODEMPROT_DISABLE_MNP_MNP5 | + DLC_MODEMPROT_DISABLE_V42_DETECT | + DLC_MODEMPROT_DISABLE_COMPRESSION; + } + dlc[0] = (byte)(i - 1); +/* HexDump ("DLC", sizeof(dlc), &dlc[0]); */ + add_p(plci, DLC, dlc); + return (0); +} + + +/*------------------------------------------------------------------*/ +/* send a request for the signaling entity */ +/*------------------------------------------------------------------*/ + +static void sig_req(PLCI *plci, byte req, byte Id) +{ + if (!plci) return; + if (plci->adapter->adapter_disabled) return; + dbug(1, dprintf("sig_req(%x)", req)); + if (req == REMOVE) + plci->sig_remove_id = plci->Sig.Id; + if (plci->req_in == plci->req_in_start) { + plci->req_in += 2; + plci->RBuffer[plci->req_in++] = 0; + } + PUT_WORD(&plci->RBuffer[plci->req_in_start], plci->req_in-plci->req_in_start - 2); + plci->RBuffer[plci->req_in++] = Id; /* sig/nl flag */ + plci->RBuffer[plci->req_in++] = req; /* request */ + plci->RBuffer[plci->req_in++] = 0; /* channel */ + plci->req_in_start = plci->req_in; +} + +/*------------------------------------------------------------------*/ +/* send a request for the network layer entity */ +/*------------------------------------------------------------------*/ + +static void nl_req_ncci(PLCI *plci, byte req, byte ncci) +{ + if (!plci) return; + if (plci->adapter->adapter_disabled) return; + dbug(1, dprintf("nl_req %02x %02x %02x", plci->Id, req, ncci)); + if (req == REMOVE) + { + plci->nl_remove_id = plci->NL.Id; + ncci_remove(plci, 0, (byte)(ncci != 0)); + ncci = 0; + } + if (plci->req_in == plci->req_in_start) { + plci->req_in += 2; + plci->RBuffer[plci->req_in++] = 0; + } + PUT_WORD(&plci->RBuffer[plci->req_in_start], plci->req_in-plci->req_in_start - 2); + plci->RBuffer[plci->req_in++] = 1; /* sig/nl flag */ + plci->RBuffer[plci->req_in++] = req; /* request */ + plci->RBuffer[plci->req_in++] = plci->adapter->ncci_ch[ncci]; /* channel */ + plci->req_in_start = plci->req_in; +} + +static void send_req(PLCI *plci) +{ + ENTITY *e; + word l; +/* word i; */ + + if (!plci) return; + if (plci->adapter->adapter_disabled) return; + channel_xmit_xon(plci); + + /* if nothing to do, return */ + if (plci->req_in == plci->req_out) return; + dbug(1, dprintf("send_req(in=%d,out=%d)", plci->req_in, plci->req_out)); + + if (plci->nl_req || plci->sig_req) return; + + l = GET_WORD(&plci->RBuffer[plci->req_out]); + plci->req_out += 2; + plci->XData[0].P = &plci->RBuffer[plci->req_out]; + plci->req_out += l; + if (plci->RBuffer[plci->req_out] == 1) + { + e = &plci->NL; + plci->req_out++; + e->Req = plci->nl_req = plci->RBuffer[plci->req_out++]; + e->ReqCh = plci->RBuffer[plci->req_out++]; + if (!(e->Id & 0x1f)) + { + e->Id = NL_ID; + plci->RBuffer[plci->req_out - 4] = CAI; + plci->RBuffer[plci->req_out - 3] = 1; + plci->RBuffer[plci->req_out - 2] = (plci->Sig.Id == 0xff) ? 0 : plci->Sig.Id; + plci->RBuffer[plci->req_out - 1] = 0; + l += 3; + plci->nl_global_req = plci->nl_req; + } + dbug(1, dprintf("%x:NLREQ(%x:%x:%x)", plci->adapter->Id, e->Id, e->Req, e->ReqCh)); + } + else + { + e = &plci->Sig; + if (plci->RBuffer[plci->req_out]) + e->Id = plci->RBuffer[plci->req_out]; + plci->req_out++; + e->Req = plci->sig_req = plci->RBuffer[plci->req_out++]; + e->ReqCh = plci->RBuffer[plci->req_out++]; + if (!(e->Id & 0x1f)) + plci->sig_global_req = plci->sig_req; + dbug(1, dprintf("%x:SIGREQ(%x:%x:%x)", plci->adapter->Id, e->Id, e->Req, e->ReqCh)); + } + plci->XData[0].PLength = l; + e->X = plci->XData; + plci->adapter->request(e); + dbug(1, dprintf("send_ok")); +} + +static void send_data(PLCI *plci) +{ + DIVA_CAPI_ADAPTER *a; + DATA_B3_DESC *data; + NCCI *ncci_ptr; + word ncci; + + if (!plci->nl_req && plci->ncci_ring_list) + { + a = plci->adapter; + ncci = plci->ncci_ring_list; + do + { + ncci = a->ncci_next[ncci]; + ncci_ptr = &(a->ncci[ncci]); + if (!(a->ncci_ch[ncci] + && (a->ch_flow_control[a->ncci_ch[ncci]] & N_OK_FC_PENDING))) + { + if (ncci_ptr->data_pending) + { + if ((a->ncci_state[ncci] == CONNECTED) + || (a->ncci_state[ncci] == INC_ACT_PENDING) + || (plci->send_disc == ncci)) + { + data = &(ncci_ptr->DBuffer[ncci_ptr->data_out]); + if ((plci->B2_prot == B2_V120_ASYNC) + || (plci->B2_prot == B2_V120_ASYNC_V42BIS) + || (plci->B2_prot == B2_V120_BIT_TRANSPARENT)) + { + plci->NData[1].P = TransmitBufferGet(plci->appl, data->P); + plci->NData[1].PLength = data->Length; + if (data->Flags & 0x10) + plci->NData[0].P = v120_break_header; + else + plci->NData[0].P = v120_default_header; + plci->NData[0].PLength = 1; + plci->NL.XNum = 2; + plci->NL.Req = plci->nl_req = (byte)((data->Flags & 0x07) << 4 | N_DATA); + } + else + { + plci->NData[0].P = TransmitBufferGet(plci->appl, data->P); + plci->NData[0].PLength = data->Length; + if (data->Flags & 0x10) + plci->NL.Req = plci->nl_req = (byte)N_UDATA; + + else if ((plci->B3_prot == B3_RTP) && (data->Flags & 0x01)) + plci->NL.Req = plci->nl_req = (byte)N_BDATA; + + else + plci->NL.Req = plci->nl_req = (byte)((data->Flags & 0x07) << 4 | N_DATA); + } + plci->NL.X = plci->NData; + plci->NL.ReqCh = a->ncci_ch[ncci]; + dbug(1, dprintf("%x:DREQ(%x:%x)", a->Id, plci->NL.Id, plci->NL.Req)); + plci->data_sent = true; + plci->data_sent_ptr = data->P; + a->request(&plci->NL); + } + else { + cleanup_ncci_data(plci, ncci); + } + } + else if (plci->send_disc == ncci) + { + /* dprintf("N_DISC"); */ + plci->NData[0].PLength = 0; + plci->NL.ReqCh = a->ncci_ch[ncci]; + plci->NL.Req = plci->nl_req = N_DISC; + a->request(&plci->NL); + plci->command = _DISCONNECT_B3_R; + plci->send_disc = 0; + } + } + } while (!plci->nl_req && (ncci != plci->ncci_ring_list)); + plci->ncci_ring_list = ncci; + } +} + +static void listen_check(DIVA_CAPI_ADAPTER *a) +{ + word i, j; + PLCI *plci; + byte activnotifiedcalls = 0; + + dbug(1, dprintf("listen_check(%d,%d)", a->listen_active, a->max_listen)); + if (!remove_started && !a->adapter_disabled) + { + for (i = 0; i < a->max_plci; i++) + { + plci = &(a->plci[i]); + if (plci->notifiedcall) activnotifiedcalls++; + } + dbug(1, dprintf("listen_check(%d)", activnotifiedcalls)); + + for (i = a->listen_active; i < ((word)(a->max_listen + activnotifiedcalls)); i++) { + if ((j = get_plci(a))) { + a->listen_active++; + plci = &a->plci[j - 1]; + plci->State = LISTENING; + + add_p(plci, OAD, "\x01\xfd"); + + add_p(plci, KEY, "\x04\x43\x41\x32\x30"); + + add_p(plci, CAI, "\x01\xc0"); + add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + add_p(plci, LLI, "\x01\xc4"); /* support Dummy CR FAC + MWI + SpoofNotify */ + add_p(plci, SHIFT | 6, NULL); + add_p(plci, SIN, "\x02\x00\x00"); + plci->internal_command = LISTEN_SIG_ASSIGN_PEND; /* do indicate_req if OK */ + sig_req(plci, ASSIGN, DSIG_ID); + send_req(plci); + } + } + } +} + +/*------------------------------------------------------------------*/ +/* functions for all parameters sent in INDs */ +/*------------------------------------------------------------------*/ + +static void IndParse(PLCI *plci, const word *parms_id, byte **parms, byte multiIEsize) +{ + word ploc; /* points to current location within packet */ + byte w; + byte wlen; + byte codeset, lock; + byte *in; + word i; + word code; + word mIEindex = 0; + ploc = 0; + codeset = 0; + lock = 0; + + in = plci->Sig.RBuffer->P; + for (i = 0; i < parms_id[0]; i++) /* multiIE parms_id contains just the 1st */ + { /* element but parms array is larger */ + parms[i] = (byte *)""; + } + for (i = 0; i < multiIEsize; i++) + { + parms[i] = (byte *)""; + } + + while (ploc < plci->Sig.RBuffer->length - 1) { + + /* read information element id and length */ + w = in[ploc]; + + if (w & 0x80) { +/* w &=0xf0; removed, cannot detect congestion levels */ +/* upper 4 bit masked with w==SHIFT now */ + wlen = 0; + } + else { + wlen = (byte)(in[ploc + 1] + 1); + } + /* check if length valid (not exceeding end of packet) */ + if ((ploc + wlen) > 270) return; + if (lock & 0x80) lock &= 0x7f; + else codeset = lock; + + if ((w & 0xf0) == SHIFT) { + codeset = in[ploc]; + if (!(codeset & 0x08)) lock = (byte)(codeset & 7); + codeset &= 7; + lock |= 0x80; + } + else { + if (w == ESC && wlen >= 3) code = in[ploc + 2] | 0x800; + else code = w; + code |= (codeset << 8); + + for (i = 1; i < parms_id[0] + 1 && parms_id[i] != code; i++); + + if (i < parms_id[0] + 1) { + if (!multiIEsize) { /* with multiIEs use next field index, */ + mIEindex = i - 1; /* with normal IEs use same index like parms_id */ + } + + parms[mIEindex] = &in[ploc + 1]; + dbug(1, dprintf("mIE[%d]=0x%x", *parms[mIEindex], in[ploc])); + if (parms_id[i] == OAD + || parms_id[i] == CONN_NR + || parms_id[i] == CAD) { + if (in[ploc + 2] & 0x80) { + in[ploc + 0] = (byte)(in[ploc + 1] + 1); + in[ploc + 1] = (byte)(in[ploc + 2] & 0x7f); + in[ploc + 2] = 0x80; + parms[mIEindex] = &in[ploc]; + } + } + mIEindex++; /* effects multiIEs only */ + } + } + + ploc += (wlen + 1); + } + return; +} + +/*------------------------------------------------------------------*/ +/* try to match a cip from received BC and HLC */ +/*------------------------------------------------------------------*/ + +static byte ie_compare(byte *ie1, byte *ie2) +{ + word i; + if (!ie1 || !ie2) return false; + if (!ie1[0]) return false; + for (i = 0; i < (word)(ie1[0] + 1); i++) if (ie1[i] != ie2[i]) return false; + return true; +} + +static word find_cip(DIVA_CAPI_ADAPTER *a, byte *bc, byte *hlc) +{ + word i; + word j; + + for (i = 9; i && !ie_compare(bc, cip_bc[i][a->u_law]); i--); + + for (j = 16; j < 29 && + (!ie_compare(bc, cip_bc[j][a->u_law]) || !ie_compare(hlc, cip_hlc[j])); j++); + if (j == 29) return i; + return j; +} + + +static byte AddInfo(byte **add_i, + byte **fty_i, + byte *esc_chi, + byte *facility) +{ + byte i; + byte j; + byte k; + byte flen; + byte len = 0; + /* facility is a nested structure */ + /* FTY can be more than once */ + + if (esc_chi[0] && !(esc_chi[esc_chi[0]] & 0x7f)) + { + add_i[0] = (byte *)"\x02\x02\x00"; /* use neither b nor d channel */ + } + + else + { + add_i[0] = (byte *)""; + } + if (!fty_i[0][0]) + { + add_i[3] = (byte *)""; + } + else + { /* facility array found */ + for (i = 0, j = 1; i < MAX_MULTI_IE && fty_i[i][0]; i++) + { + dbug(1, dprintf("AddIFac[%d]", fty_i[i][0])); + len += fty_i[i][0]; + len += 2; + flen = fty_i[i][0]; + facility[j++] = 0x1c; /* copy fac IE */ + for (k = 0; k <= flen; k++, j++) + { + facility[j] = fty_i[i][k]; +/* dbug(1, dprintf("%x ",facility[j])); */ + } + } + facility[0] = len; + add_i[3] = facility; + } +/* dbug(1, dprintf("FacArrLen=%d ",len)); */ + len = add_i[0][0] + add_i[1][0] + add_i[2][0] + add_i[3][0]; + len += 4; /* calculate length of all */ + return (len); +} + +/*------------------------------------------------------------------*/ +/* voice and codec features */ +/*------------------------------------------------------------------*/ + +static void SetVoiceChannel(PLCI *plci, byte *chi, DIVA_CAPI_ADAPTER *a) +{ + byte voice_chi[] = "\x02\x18\x01"; + byte channel; + + channel = chi[chi[0]] & 0x3; + dbug(1, dprintf("ExtDevON(Ch=0x%x)", channel)); + voice_chi[2] = (channel) ? channel : 1; + add_p(plci, FTY, "\x02\x01\x07"); /* B On, default on 1 */ + add_p(plci, ESC, voice_chi); /* Channel */ + sig_req(plci, TEL_CTRL, 0); + send_req(plci); + if (a->AdvSignalPLCI) + { + adv_voice_write_coefs(a->AdvSignalPLCI, ADV_VOICE_WRITE_ACTIVATION); + } +} + +static void VoiceChannelOff(PLCI *plci) +{ + dbug(1, dprintf("ExtDevOFF")); + add_p(plci, FTY, "\x02\x01\x08"); /* B Off */ + sig_req(plci, TEL_CTRL, 0); + send_req(plci); + if (plci->adapter->AdvSignalPLCI) + { + adv_voice_clear_config(plci->adapter->AdvSignalPLCI); + } +} + + +static word AdvCodecSupport(DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, + byte hook_listen) +{ + word j; + PLCI *splci; + + /* check if hardware supports handset with hook states (adv.codec) */ + /* or if just a on board codec is supported */ + /* the advanced codec plci is just for internal use */ + + /* diva Pro with on-board codec: */ + if (a->profile.Global_Options & HANDSET) + { + /* new call, but hook states are already signalled */ + if (a->AdvCodecFLAG) + { + if (a->AdvSignalAppl != appl || a->AdvSignalPLCI) + { + dbug(1, dprintf("AdvSigPlci=0x%x", a->AdvSignalPLCI)); + return 0x2001; /* codec in use by another application */ + } + if (plci != NULL) + { + a->AdvSignalPLCI = plci; + plci->tel = ADV_VOICE; + } + return 0; /* adv codec still used */ + } + if ((j = get_plci(a))) + { + splci = &a->plci[j - 1]; + splci->tel = CODEC_PERMANENT; + /* hook_listen indicates if a facility_req with handset/hook support */ + /* was sent. Otherwise if just a call on an external device was made */ + /* the codec will be used but the hook info will be discarded (just */ + /* the external controller is in use */ + if (hook_listen) splci->State = ADVANCED_VOICE_SIG; + else + { + splci->State = ADVANCED_VOICE_NOSIG; + if (plci) + { + plci->spoofed_msg = SPOOFING_REQUIRED; + } + /* indicate D-ch connect if */ + } /* codec is connected OK */ + if (plci != NULL) + { + a->AdvSignalPLCI = plci; + plci->tel = ADV_VOICE; + } + a->AdvSignalAppl = appl; + a->AdvCodecFLAG = true; + a->AdvCodecPLCI = splci; + add_p(splci, CAI, "\x01\x15"); + add_p(splci, LLI, "\x01\x00"); + add_p(splci, ESC, "\x02\x18\x00"); + add_p(splci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + splci->internal_command = PERM_COD_ASSIGN; + dbug(1, dprintf("Codec Assign")); + sig_req(splci, ASSIGN, DSIG_ID); + send_req(splci); + } + else + { + return 0x2001; /* wrong state, no more plcis */ + } + } + else if (a->profile.Global_Options & ON_BOARD_CODEC) + { + if (hook_listen) return 0x300B; /* Facility not supported */ + /* no hook with SCOM */ + if (plci != NULL) plci->tel = CODEC; + dbug(1, dprintf("S/SCOM codec")); + /* first time we use the scom-s codec we must shut down the internal */ + /* handset application of the card. This can be done by an assign with */ + /* a cai with the 0x80 bit set. Assign return code is 'out of resource'*/ + if (!a->scom_appl_disable) { + if ((j = get_plci(a))) { + splci = &a->plci[j - 1]; + add_p(splci, CAI, "\x01\x80"); + add_p(splci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + sig_req(splci, ASSIGN, 0xC0); /* 0xc0 is the TEL_ID */ + send_req(splci); + a->scom_appl_disable = true; + } + else{ + return 0x2001; /* wrong state, no more plcis */ + } + } + } + else return 0x300B; /* Facility not supported */ + + return 0; +} + + +static void CodecIdCheck(DIVA_CAPI_ADAPTER *a, PLCI *plci) +{ + + dbug(1, dprintf("CodecIdCheck")); + + if (a->AdvSignalPLCI == plci) + { + dbug(1, dprintf("PLCI owns codec")); + VoiceChannelOff(a->AdvCodecPLCI); + if (a->AdvCodecPLCI->State == ADVANCED_VOICE_NOSIG) + { + dbug(1, dprintf("remove temp codec PLCI")); + plci_remove(a->AdvCodecPLCI); + a->AdvCodecFLAG = 0; + a->AdvCodecPLCI = NULL; + a->AdvSignalAppl = NULL; + } + a->AdvSignalPLCI = NULL; + } +} + +/* ------------------------------------------------------------------- + Ask for physical address of card on PCI bus + ------------------------------------------------------------------- */ +static void diva_ask_for_xdi_sdram_bar(DIVA_CAPI_ADAPTER *a, + IDI_SYNC_REQ *preq) { + a->sdram_bar = 0; + if (diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR) { + ENTITY *e = (ENTITY *)preq; + + e->user[0] = a->Id - 1; + preq->xdi_sdram_bar.info.bar = 0; + preq->xdi_sdram_bar.Req = 0; + preq->xdi_sdram_bar.Rc = IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR; + + (*(a->request))(e); + + a->sdram_bar = preq->xdi_sdram_bar.info.bar; + dbug(3, dprintf("A(%d) SDRAM BAR = %08x", a->Id, a->sdram_bar)); + } +} + +/* ------------------------------------------------------------------- + Ask XDI about extended features + ------------------------------------------------------------------- */ +static void diva_get_extended_adapter_features(DIVA_CAPI_ADAPTER *a) { + IDI_SYNC_REQ *preq; + char buffer[((sizeof(preq->xdi_extended_features) + 4) > sizeof(ENTITY)) ? (sizeof(preq->xdi_extended_features) + 4) : sizeof(ENTITY)]; + + char features[4]; + preq = (IDI_SYNC_REQ *)&buffer[0]; + + if (!diva_xdi_extended_features) { + ENTITY *e = (ENTITY *)preq; + diva_xdi_extended_features |= 0x80000000; + + e->user[0] = a->Id - 1; + preq->xdi_extended_features.Req = 0; + preq->xdi_extended_features.Rc = IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES; + preq->xdi_extended_features.info.buffer_length_in_bytes = sizeof(features); + preq->xdi_extended_features.info.features = &features[0]; + + (*(a->request))(e); + + if (features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) { + /* + Check features located in the byte '0' + */ + if (features[0] & DIVA_XDI_EXTENDED_FEATURE_CMA) { + diva_xdi_extended_features |= DIVA_CAPI_USE_CMA; + } + if (features[0] & DIVA_XDI_EXTENDED_FEATURE_RX_DMA) { + diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_RX_DMA; + dbug(1, dprintf("XDI provides RxDMA")); + } + if (features[0] & DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR) { + diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR; + } + if (features[0] & DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC) { + diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_NO_CANCEL; + dbug(3, dprintf("XDI provides NO_CANCEL_RC feature")); + } + + } + } + + diva_ask_for_xdi_sdram_bar(a, preq); +} + +/*------------------------------------------------------------------*/ +/* automatic law */ +/*------------------------------------------------------------------*/ +/* called from OS specific part after init time to get the Law */ +/* a-law (Euro) and u-law (us,japan) use different BCs in the Setup message */ +void AutomaticLaw(DIVA_CAPI_ADAPTER *a) +{ + word j; + PLCI *splci; + + if (a->automatic_law) { + return; + } + if ((j = get_plci(a))) { + diva_get_extended_adapter_features(a); + splci = &a->plci[j - 1]; + a->automatic_lawPLCI = splci; + a->automatic_law = 1; + add_p(splci, CAI, "\x01\x80"); + add_p(splci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + splci->internal_command = USELAW_REQ; + splci->command = 0; + splci->number = 0; + sig_req(splci, ASSIGN, DSIG_ID); + send_req(splci); + } +} + +/* called from OS specific part if an application sends an Capi20Release */ +word CapiRelease(word Id) +{ + word i, j, appls_found; + PLCI *plci; + APPL *this; + DIVA_CAPI_ADAPTER *a; + + if (!Id) + { + dbug(0, dprintf("A: CapiRelease(Id==0)")); + return (_WRONG_APPL_ID); + } + + this = &application[Id - 1]; /* get application pointer */ + + for (i = 0, appls_found = 0; i < max_appl; i++) + { + if (application[i].Id) /* an application has been found */ + { + appls_found++; + } + } + + for (i = 0; i < max_adapter; i++) /* scan all adapters... */ + { + a = &adapter[i]; + if (a->request) + { + a->Info_Mask[Id - 1] = 0; + a->CIP_Mask[Id - 1] = 0; + a->Notification_Mask[Id - 1] = 0; + a->codec_listen[Id - 1] = NULL; + a->requested_options_table[Id - 1] = 0; + for (j = 0; j < a->max_plci; j++) /* and all PLCIs connected */ + { /* with this application */ + plci = &a->plci[j]; + if (plci->Id) /* if plci owns no application */ + { /* it may be not jet connected */ + if (plci->State == INC_CON_PENDING + || plci->State == INC_CON_ALERT) + { + if (test_bit(Id - 1, plci->c_ind_mask_table)) + { + __clear_bit(Id - 1, plci->c_ind_mask_table); + if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL)) + { + sig_req(plci, HANGUP, 0); + send_req(plci); + plci->State = OUTG_DIS_PENDING; + } + } + } + if (test_bit(Id - 1, plci->c_ind_mask_table)) + { + __clear_bit(Id - 1, plci->c_ind_mask_table); + if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL)) + { + if (!plci->appl) + { + plci_remove(plci); + plci->State = IDLE; + } + } + } + if (plci->appl == this) + { + plci->appl = NULL; + plci_remove(plci); + plci->State = IDLE; + } + } + } + listen_check(a); + + if (a->flag_dynamic_l1_down) + { + if (appls_found == 1) /* last application does a capi release */ + { + if ((j = get_plci(a))) + { + plci = &a->plci[j - 1]; + plci->command = 0; + add_p(plci, OAD, "\x01\xfd"); + add_p(plci, CAI, "\x01\x80"); + add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + add_p(plci, SHIFT | 6, NULL); + add_p(plci, SIN, "\x02\x00\x00"); + plci->internal_command = REM_L1_SIG_ASSIGN_PEND; + sig_req(plci, ASSIGN, DSIG_ID); + add_p(plci, FTY, "\x02\xff\x06"); /* l1 down */ + sig_req(plci, SIG_CTRL, 0); + send_req(plci); + } + } + } + if (a->AdvSignalAppl == this) + { + this->NullCREnable = false; + if (a->AdvCodecPLCI) + { + plci_remove(a->AdvCodecPLCI); + a->AdvCodecPLCI->tel = 0; + a->AdvCodecPLCI->adv_nl = 0; + } + a->AdvSignalAppl = NULL; + a->AdvSignalPLCI = NULL; + a->AdvCodecFLAG = 0; + a->AdvCodecPLCI = NULL; + } + } + } + + this->Id = 0; + + return GOOD; +} + +static word plci_remove_check(PLCI *plci) +{ + if (!plci) return true; + if (!plci->NL.Id && bitmap_empty(plci->c_ind_mask_table, MAX_APPL)) + { + if (plci->Sig.Id == 0xff) + plci->Sig.Id = 0; + if (!plci->Sig.Id) + { + dbug(1, dprintf("plci_remove_complete(%x)", plci->Id)); + dbug(1, dprintf("tel=0x%x,Sig=0x%x", plci->tel, plci->Sig.Id)); + if (plci->Id) + { + CodecIdCheck(plci->adapter, plci); + clear_b1_config(plci); + ncci_remove(plci, 0, false); + plci_free_msg_in_queue(plci); + channel_flow_control_remove(plci); + plci->Id = 0; + plci->State = IDLE; + plci->channels = 0; + plci->appl = NULL; + plci->notifiedcall = 0; + } + listen_check(plci->adapter); + return true; + } + } + return false; +} + + +/*------------------------------------------------------------------*/ + +static byte plci_nl_busy(PLCI *plci) +{ + /* only applicable for non-multiplexed protocols */ + return (plci->nl_req + || (plci->ncci_ring_list + && plci->adapter->ncci_ch[plci->ncci_ring_list] + && (plci->adapter->ch_flow_control[plci->adapter->ncci_ch[plci->ncci_ring_list]] & N_OK_FC_PENDING))); +} + + +/*------------------------------------------------------------------*/ +/* DTMF facilities */ +/*------------------------------------------------------------------*/ + + +static struct +{ + byte send_mask; + byte listen_mask; + byte character; + byte code; +} dtmf_digit_map[] = +{ + { 0x01, 0x01, 0x23, DTMF_DIGIT_TONE_CODE_HASHMARK }, + { 0x01, 0x01, 0x2a, DTMF_DIGIT_TONE_CODE_STAR }, + { 0x01, 0x01, 0x30, DTMF_DIGIT_TONE_CODE_0 }, + { 0x01, 0x01, 0x31, DTMF_DIGIT_TONE_CODE_1 }, + { 0x01, 0x01, 0x32, DTMF_DIGIT_TONE_CODE_2 }, + { 0x01, 0x01, 0x33, DTMF_DIGIT_TONE_CODE_3 }, + { 0x01, 0x01, 0x34, DTMF_DIGIT_TONE_CODE_4 }, + { 0x01, 0x01, 0x35, DTMF_DIGIT_TONE_CODE_5 }, + { 0x01, 0x01, 0x36, DTMF_DIGIT_TONE_CODE_6 }, + { 0x01, 0x01, 0x37, DTMF_DIGIT_TONE_CODE_7 }, + { 0x01, 0x01, 0x38, DTMF_DIGIT_TONE_CODE_8 }, + { 0x01, 0x01, 0x39, DTMF_DIGIT_TONE_CODE_9 }, + { 0x01, 0x01, 0x41, DTMF_DIGIT_TONE_CODE_A }, + { 0x01, 0x01, 0x42, DTMF_DIGIT_TONE_CODE_B }, + { 0x01, 0x01, 0x43, DTMF_DIGIT_TONE_CODE_C }, + { 0x01, 0x01, 0x44, DTMF_DIGIT_TONE_CODE_D }, + { 0x01, 0x00, 0x61, DTMF_DIGIT_TONE_CODE_A }, + { 0x01, 0x00, 0x62, DTMF_DIGIT_TONE_CODE_B }, + { 0x01, 0x00, 0x63, DTMF_DIGIT_TONE_CODE_C }, + { 0x01, 0x00, 0x64, DTMF_DIGIT_TONE_CODE_D }, + + { 0x04, 0x04, 0x80, DTMF_SIGNAL_NO_TONE }, + { 0x00, 0x04, 0x81, DTMF_SIGNAL_UNIDENTIFIED_TONE }, + { 0x04, 0x04, 0x82, DTMF_SIGNAL_DIAL_TONE }, + { 0x04, 0x04, 0x83, DTMF_SIGNAL_PABX_INTERNAL_DIAL_TONE }, + { 0x04, 0x04, 0x84, DTMF_SIGNAL_SPECIAL_DIAL_TONE }, + { 0x04, 0x04, 0x85, DTMF_SIGNAL_SECOND_DIAL_TONE }, + { 0x04, 0x04, 0x86, DTMF_SIGNAL_RINGING_TONE }, + { 0x04, 0x04, 0x87, DTMF_SIGNAL_SPECIAL_RINGING_TONE }, + { 0x04, 0x04, 0x88, DTMF_SIGNAL_BUSY_TONE }, + { 0x04, 0x04, 0x89, DTMF_SIGNAL_CONGESTION_TONE }, + { 0x04, 0x04, 0x8a, DTMF_SIGNAL_SPECIAL_INFORMATION_TONE }, + { 0x04, 0x04, 0x8b, DTMF_SIGNAL_COMFORT_TONE }, + { 0x04, 0x04, 0x8c, DTMF_SIGNAL_HOLD_TONE }, + { 0x04, 0x04, 0x8d, DTMF_SIGNAL_RECORD_TONE }, + { 0x04, 0x04, 0x8e, DTMF_SIGNAL_CALLER_WAITING_TONE }, + { 0x04, 0x04, 0x8f, DTMF_SIGNAL_CALL_WAITING_TONE }, + { 0x04, 0x04, 0x90, DTMF_SIGNAL_PAY_TONE }, + { 0x04, 0x04, 0x91, DTMF_SIGNAL_POSITIVE_INDICATION_TONE }, + { 0x04, 0x04, 0x92, DTMF_SIGNAL_NEGATIVE_INDICATION_TONE }, + { 0x04, 0x04, 0x93, DTMF_SIGNAL_WARNING_TONE }, + { 0x04, 0x04, 0x94, DTMF_SIGNAL_INTRUSION_TONE }, + { 0x04, 0x04, 0x95, DTMF_SIGNAL_CALLING_CARD_SERVICE_TONE }, + { 0x04, 0x04, 0x96, DTMF_SIGNAL_PAYPHONE_RECOGNITION_TONE }, + { 0x04, 0x04, 0x97, DTMF_SIGNAL_CPE_ALERTING_SIGNAL }, + { 0x04, 0x04, 0x98, DTMF_SIGNAL_OFF_HOOK_WARNING_TONE }, + { 0x04, 0x04, 0xbf, DTMF_SIGNAL_INTERCEPT_TONE }, + { 0x04, 0x04, 0xc0, DTMF_SIGNAL_MODEM_CALLING_TONE }, + { 0x04, 0x04, 0xc1, DTMF_SIGNAL_FAX_CALLING_TONE }, + { 0x04, 0x04, 0xc2, DTMF_SIGNAL_ANSWER_TONE }, + { 0x04, 0x04, 0xc3, DTMF_SIGNAL_REVERSED_ANSWER_TONE }, + { 0x04, 0x04, 0xc4, DTMF_SIGNAL_ANSAM_TONE }, + { 0x04, 0x04, 0xc5, DTMF_SIGNAL_REVERSED_ANSAM_TONE }, + { 0x04, 0x04, 0xc6, DTMF_SIGNAL_BELL103_ANSWER_TONE }, + { 0x04, 0x04, 0xc7, DTMF_SIGNAL_FAX_FLAGS }, + { 0x04, 0x04, 0xc8, DTMF_SIGNAL_G2_FAX_GROUP_ID }, + { 0x00, 0x04, 0xc9, DTMF_SIGNAL_HUMAN_SPEECH }, + { 0x04, 0x04, 0xca, DTMF_SIGNAL_ANSWERING_MACHINE_390 }, + { 0x02, 0x02, 0xf1, DTMF_MF_DIGIT_TONE_CODE_1 }, + { 0x02, 0x02, 0xf2, DTMF_MF_DIGIT_TONE_CODE_2 }, + { 0x02, 0x02, 0xf3, DTMF_MF_DIGIT_TONE_CODE_3 }, + { 0x02, 0x02, 0xf4, DTMF_MF_DIGIT_TONE_CODE_4 }, + { 0x02, 0x02, 0xf5, DTMF_MF_DIGIT_TONE_CODE_5 }, + { 0x02, 0x02, 0xf6, DTMF_MF_DIGIT_TONE_CODE_6 }, + { 0x02, 0x02, 0xf7, DTMF_MF_DIGIT_TONE_CODE_7 }, + { 0x02, 0x02, 0xf8, DTMF_MF_DIGIT_TONE_CODE_8 }, + { 0x02, 0x02, 0xf9, DTMF_MF_DIGIT_TONE_CODE_9 }, + { 0x02, 0x02, 0xfa, DTMF_MF_DIGIT_TONE_CODE_0 }, + { 0x02, 0x02, 0xfb, DTMF_MF_DIGIT_TONE_CODE_K1 }, + { 0x02, 0x02, 0xfc, DTMF_MF_DIGIT_TONE_CODE_K2 }, + { 0x02, 0x02, 0xfd, DTMF_MF_DIGIT_TONE_CODE_KP }, + { 0x02, 0x02, 0xfe, DTMF_MF_DIGIT_TONE_CODE_S1 }, + { 0x02, 0x02, 0xff, DTMF_MF_DIGIT_TONE_CODE_ST }, + +}; + +#define DTMF_DIGIT_MAP_ENTRIES ARRAY_SIZE(dtmf_digit_map) + + +static void dtmf_enable_receiver(PLCI *plci, byte enable_mask) +{ + word min_digit_duration, min_gap_duration; + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_enable_receiver %02x", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, enable_mask)); + + if (enable_mask != 0) + { + min_digit_duration = (plci->dtmf_rec_pulse_ms == 0) ? 40 : plci->dtmf_rec_pulse_ms; + min_gap_duration = (plci->dtmf_rec_pause_ms == 0) ? 40 : plci->dtmf_rec_pause_ms; + plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_ENABLE_RECEIVER; + PUT_WORD(&plci->internal_req_buffer[1], min_digit_duration); + PUT_WORD(&plci->internal_req_buffer[3], min_gap_duration); + plci->NData[0].PLength = 5; + + PUT_WORD(&plci->internal_req_buffer[5], INTERNAL_IND_BUFFER_SIZE); + plci->NData[0].PLength += 2; + capidtmf_recv_enable(&(plci->capidtmf_state), min_digit_duration, min_gap_duration); + + } + else + { + plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_DISABLE_RECEIVER; + plci->NData[0].PLength = 1; + + capidtmf_recv_disable(&(plci->capidtmf_state)); + + } + plci->NData[0].P = plci->internal_req_buffer; + plci->NL.X = plci->NData; + plci->NL.ReqCh = 0; + plci->NL.Req = plci->nl_req = (byte) N_UDATA; + plci->adapter->request(&plci->NL); +} + + +static void dtmf_send_digits(PLCI *plci, byte *digit_buffer, word digit_count) +{ + word w, i; + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_send_digits %d", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, digit_count)); + + plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_SEND_DIGITS; + w = (plci->dtmf_send_pulse_ms == 0) ? 40 : plci->dtmf_send_pulse_ms; + PUT_WORD(&plci->internal_req_buffer[1], w); + w = (plci->dtmf_send_pause_ms == 0) ? 40 : plci->dtmf_send_pause_ms; + PUT_WORD(&plci->internal_req_buffer[3], w); + for (i = 0; i < digit_count; i++) + { + w = 0; + while ((w < DTMF_DIGIT_MAP_ENTRIES) + && (digit_buffer[i] != dtmf_digit_map[w].character)) + { + w++; + } + plci->internal_req_buffer[5 + i] = (w < DTMF_DIGIT_MAP_ENTRIES) ? + dtmf_digit_map[w].code : DTMF_DIGIT_TONE_CODE_STAR; + } + plci->NData[0].PLength = 5 + digit_count; + plci->NData[0].P = plci->internal_req_buffer; + plci->NL.X = plci->NData; + plci->NL.ReqCh = 0; + plci->NL.Req = plci->nl_req = (byte) N_UDATA; + plci->adapter->request(&plci->NL); +} + + +static void dtmf_rec_clear_config(PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_rec_clear_config", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + plci->dtmf_rec_active = 0; + plci->dtmf_rec_pulse_ms = 0; + plci->dtmf_rec_pause_ms = 0; + + capidtmf_init(&(plci->capidtmf_state), plci->adapter->u_law); + +} + + +static void dtmf_send_clear_config(PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_send_clear_config", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + plci->dtmf_send_requests = 0; + plci->dtmf_send_pulse_ms = 0; + plci->dtmf_send_pause_ms = 0; +} + + +static void dtmf_prepare_switch(dword Id, PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_prepare_switch", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + while (plci->dtmf_send_requests != 0) + dtmf_confirmation(Id, plci); +} + + +static word dtmf_save_config(dword Id, PLCI *plci, byte Rc) +{ + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_save_config %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + return (GOOD); +} + + +static word dtmf_restore_config(dword Id, PLCI *plci, byte Rc) +{ + word Info; + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_restore_config %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + Info = GOOD; + if (plci->B1_facilities & B1_FACILITY_DTMFR) + { + switch (plci->adjust_b_state) + { + case ADJUST_B_RESTORE_DTMF_1: + plci->internal_command = plci->adjust_b_command; + if (plci_nl_busy(plci)) + { + plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1; + break; + } + dtmf_enable_receiver(plci, plci->dtmf_rec_active); + plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_2; + break; + case ADJUST_B_RESTORE_DTMF_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Reenable DTMF receiver failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _WRONG_STATE; + break; + } + break; + } + } + return (Info); +} + + +static void dtmf_command(dword Id, PLCI *plci, byte Rc) +{ + word internal_command, Info; + byte mask; + byte result[4]; + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_command %02x %04x %04x %d %d %d %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command, + plci->dtmf_cmd, plci->dtmf_rec_pulse_ms, plci->dtmf_rec_pause_ms, + plci->dtmf_send_pulse_ms, plci->dtmf_send_pause_ms)); + + Info = GOOD; + result[0] = 2; + PUT_WORD(&result[1], DTMF_SUCCESS); + internal_command = plci->internal_command; + plci->internal_command = 0; + mask = 0x01; + switch (plci->dtmf_cmd) + { + + case DTMF_LISTEN_TONE_START: + mask <<= 1; /* fall through */ + case DTMF_LISTEN_MF_START: + mask <<= 1; /* fall through */ + + case DTMF_LISTEN_START: + switch (internal_command) + { + default: + adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities | + B1_FACILITY_DTMFR), DTMF_COMMAND_1); + /* fall through */ + case DTMF_COMMAND_1: + if (adjust_b_process(Id, plci, Rc) != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Load DTMF failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } + if (plci->internal_command) + return; + /* fall through */ + case DTMF_COMMAND_2: + if (plci_nl_busy(plci)) + { + plci->internal_command = DTMF_COMMAND_2; + return; + } + plci->internal_command = DTMF_COMMAND_3; + dtmf_enable_receiver(plci, (byte)(plci->dtmf_rec_active | mask)); + return; + case DTMF_COMMAND_3: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Enable DTMF receiver failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } + + plci->tone_last_indication_code = DTMF_SIGNAL_NO_TONE; + + plci->dtmf_rec_active |= mask; + break; + } + break; + + + case DTMF_LISTEN_TONE_STOP: + mask <<= 1; /* fall through */ + case DTMF_LISTEN_MF_STOP: + mask <<= 1; /* fall through */ + + case DTMF_LISTEN_STOP: + switch (internal_command) + { + default: + plci->dtmf_rec_active &= ~mask; + if (plci->dtmf_rec_active) + break; +/* + case DTMF_COMMAND_1: + if (plci->dtmf_rec_active) + { + if (plci_nl_busy (plci)) + { + plci->internal_command = DTMF_COMMAND_1; + return; + } + plci->dtmf_rec_active &= ~mask; + plci->internal_command = DTMF_COMMAND_2; + dtmf_enable_receiver (plci, false); + return; + } + Rc = OK; + case DTMF_COMMAND_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug (1, dprintf("[%06lx] %s,%d: Disable DTMF receiver failed %02x", + UnMapId (Id), (char far *)(FILE_), __LINE__, Rc)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } +*/ + adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities & + ~(B1_FACILITY_DTMFX | B1_FACILITY_DTMFR)), DTMF_COMMAND_3); + /* fall through */ + case DTMF_COMMAND_3: + if (adjust_b_process(Id, plci, Rc) != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Unload DTMF failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } + if (plci->internal_command) + return; + break; + } + break; + + + case DTMF_SEND_TONE: + mask <<= 1; /* fall through */ + case DTMF_SEND_MF: + mask <<= 1; /* fall through */ + + case DTMF_DIGITS_SEND: + switch (internal_command) + { + default: + adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities | + ((plci->dtmf_parameter_length != 0) ? B1_FACILITY_DTMFX | B1_FACILITY_DTMFR : B1_FACILITY_DTMFX)), + DTMF_COMMAND_1); + /* fall through */ + case DTMF_COMMAND_1: + if (adjust_b_process(Id, plci, Rc) != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Load DTMF failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } + if (plci->internal_command) + return; + /* fall through */ + case DTMF_COMMAND_2: + if (plci_nl_busy(plci)) + { + plci->internal_command = DTMF_COMMAND_2; + return; + } + plci->dtmf_msg_number_queue[(plci->dtmf_send_requests)++] = plci->number; + plci->internal_command = DTMF_COMMAND_3; + dtmf_send_digits(plci, &plci->saved_msg.parms[3].info[1], plci->saved_msg.parms[3].length); + return; + case DTMF_COMMAND_3: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Send DTMF digits failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + if (plci->dtmf_send_requests != 0) + (plci->dtmf_send_requests)--; + Info = _FACILITY_NOT_SUPPORTED; + break; + } + return; + } + break; + } + sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->number, + "wws", Info, SELECTOR_DTMF, result); +} + + +static byte dtmf_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg) +{ + word Info; + word i, j; + byte mask; + API_PARSE dtmf_parms[5]; + byte result[40]; + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_request", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + Info = GOOD; + result[0] = 2; + PUT_WORD(&result[1], DTMF_SUCCESS); + if (!(a->profile.Global_Options & GL_DTMF_SUPPORTED)) + { + dbug(1, dprintf("[%06lx] %s,%d: Facility not supported", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _FACILITY_NOT_SUPPORTED; + } + else if (api_parse(&msg[1].info[1], msg[1].length, "w", dtmf_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_MESSAGE_FORMAT; + } + + else if ((GET_WORD(dtmf_parms[0].info) == DTMF_GET_SUPPORTED_DETECT_CODES) + || (GET_WORD(dtmf_parms[0].info) == DTMF_GET_SUPPORTED_SEND_CODES)) + { + if (!((a->requested_options_table[appl->Id - 1]) + & (1L << PRIVATE_DTMF_TONE))) + { + dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(dtmf_parms[0].info))); + PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST); + } + else + { + for (i = 0; i < 32; i++) + result[4 + i] = 0; + if (GET_WORD(dtmf_parms[0].info) == DTMF_GET_SUPPORTED_DETECT_CODES) + { + for (i = 0; i < DTMF_DIGIT_MAP_ENTRIES; i++) + { + if (dtmf_digit_map[i].listen_mask != 0) + result[4 + (dtmf_digit_map[i].character >> 3)] |= (1 << (dtmf_digit_map[i].character & 0x7)); + } + } + else + { + for (i = 0; i < DTMF_DIGIT_MAP_ENTRIES; i++) + { + if (dtmf_digit_map[i].send_mask != 0) + result[4 + (dtmf_digit_map[i].character >> 3)] |= (1 << (dtmf_digit_map[i].character & 0x7)); + } + } + result[0] = 3 + 32; + result[3] = 32; + } + } + + else if (plci == NULL) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_IDENTIFIER; + } + else + { + if (!plci->State + || !plci->NL.Id || plci->nl_remove_id) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong state", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_STATE; + } + else + { + plci->command = 0; + plci->dtmf_cmd = GET_WORD(dtmf_parms[0].info); + mask = 0x01; + switch (plci->dtmf_cmd) + { + + case DTMF_LISTEN_TONE_START: + case DTMF_LISTEN_TONE_STOP: + mask <<= 1; /* fall through */ + case DTMF_LISTEN_MF_START: + case DTMF_LISTEN_MF_STOP: + mask <<= 1; + if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[appl->Id - 1]) + & (1L << PRIVATE_DTMF_TONE))) + { + dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(dtmf_parms[0].info))); + PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST); + break; + } + /* fall through */ + + case DTMF_LISTEN_START: + case DTMF_LISTEN_STOP: + if (!(a->manufacturer_features & MANUFACTURER_FEATURE_HARDDTMF) + && !(a->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)) + { + dbug(1, dprintf("[%06lx] %s,%d: Facility not supported", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } + if (mask & DTMF_LISTEN_ACTIVE_FLAG) + { + if (api_parse(&msg[1].info[1], msg[1].length, "wwws", dtmf_parms)) + { + plci->dtmf_rec_pulse_ms = 0; + plci->dtmf_rec_pause_ms = 0; + } + else + { + plci->dtmf_rec_pulse_ms = GET_WORD(dtmf_parms[1].info); + plci->dtmf_rec_pause_ms = GET_WORD(dtmf_parms[2].info); + } + } + start_internal_command(Id, plci, dtmf_command); + return (false); + + + case DTMF_SEND_TONE: + mask <<= 1; /* fall through */ + case DTMF_SEND_MF: + mask <<= 1; + if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[appl->Id - 1]) + & (1L << PRIVATE_DTMF_TONE))) + { + dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(dtmf_parms[0].info))); + PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST); + break; + } + /* fall through */ + + case DTMF_DIGITS_SEND: + if (api_parse(&msg[1].info[1], msg[1].length, "wwws", dtmf_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + if (mask & DTMF_LISTEN_ACTIVE_FLAG) + { + plci->dtmf_send_pulse_ms = GET_WORD(dtmf_parms[1].info); + plci->dtmf_send_pause_ms = GET_WORD(dtmf_parms[2].info); + } + i = 0; + j = 0; + while ((i < dtmf_parms[3].length) && (j < DTMF_DIGIT_MAP_ENTRIES)) + { + j = 0; + while ((j < DTMF_DIGIT_MAP_ENTRIES) + && ((dtmf_parms[3].info[i + 1] != dtmf_digit_map[j].character) + || ((dtmf_digit_map[j].send_mask & mask) == 0))) + { + j++; + } + i++; + } + if (j == DTMF_DIGIT_MAP_ENTRIES) + { + dbug(1, dprintf("[%06lx] %s,%d: Incorrect DTMF digit %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, dtmf_parms[3].info[i])); + PUT_WORD(&result[1], DTMF_INCORRECT_DIGIT); + break; + } + if (plci->dtmf_send_requests >= ARRAY_SIZE(plci->dtmf_msg_number_queue)) + { + dbug(1, dprintf("[%06lx] %s,%d: DTMF request overrun", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_STATE; + break; + } + api_save_msg(dtmf_parms, "wwws", &plci->saved_msg); + start_internal_command(Id, plci, dtmf_command); + return (false); + + default: + dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, plci->dtmf_cmd)); + PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST); + } + } + } + sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number, + "wws", Info, SELECTOR_DTMF, result); + return (false); +} + + +static void dtmf_confirmation(dword Id, PLCI *plci) +{ + word i; + byte result[4]; + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_confirmation", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + result[0] = 2; + PUT_WORD(&result[1], DTMF_SUCCESS); + if (plci->dtmf_send_requests != 0) + { + sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->dtmf_msg_number_queue[0], + "wws", GOOD, SELECTOR_DTMF, result); + (plci->dtmf_send_requests)--; + for (i = 0; i < plci->dtmf_send_requests; i++) + plci->dtmf_msg_number_queue[i] = plci->dtmf_msg_number_queue[i + 1]; + } +} + + +static void dtmf_indication(dword Id, PLCI *plci, byte *msg, word length) +{ + word i, j, n; + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_indication", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + n = 0; + for (i = 1; i < length; i++) + { + j = 0; + while ((j < DTMF_DIGIT_MAP_ENTRIES) + && ((msg[i] != dtmf_digit_map[j].code) + || ((dtmf_digit_map[j].listen_mask & plci->dtmf_rec_active) == 0))) + { + j++; + } + if (j < DTMF_DIGIT_MAP_ENTRIES) + { + + if ((dtmf_digit_map[j].listen_mask & DTMF_TONE_LISTEN_ACTIVE_FLAG) + && (plci->tone_last_indication_code == DTMF_SIGNAL_NO_TONE) + && (dtmf_digit_map[j].character != DTMF_SIGNAL_UNIDENTIFIED_TONE)) + { + if (n + 1 == i) + { + for (i = length; i > n + 1; i--) + msg[i] = msg[i - 1]; + length++; + i++; + } + msg[++n] = DTMF_SIGNAL_UNIDENTIFIED_TONE; + } + plci->tone_last_indication_code = dtmf_digit_map[j].character; + + msg[++n] = dtmf_digit_map[j].character; + } + } + if (n != 0) + { + msg[0] = (byte) n; + sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "wS", SELECTOR_DTMF, msg); + } +} + + +/*------------------------------------------------------------------*/ +/* DTMF parameters */ +/*------------------------------------------------------------------*/ + +static void dtmf_parameter_write(PLCI *plci) +{ + word i; + byte parameter_buffer[DTMF_PARAMETER_BUFFER_SIZE + 2]; + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_write", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + parameter_buffer[0] = plci->dtmf_parameter_length + 1; + parameter_buffer[1] = DSP_CTRL_SET_DTMF_PARAMETERS; + for (i = 0; i < plci->dtmf_parameter_length; i++) + parameter_buffer[2 + i] = plci->dtmf_parameter_buffer[i]; + add_p(plci, FTY, parameter_buffer); + sig_req(plci, TEL_CTRL, 0); + send_req(plci); +} + + +static void dtmf_parameter_clear_config(PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_clear_config", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + plci->dtmf_parameter_length = 0; +} + + +static void dtmf_parameter_prepare_switch(dword Id, PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_prepare_switch", + UnMapId(Id), (char *)(FILE_), __LINE__)); + +} + + +static word dtmf_parameter_save_config(dword Id, PLCI *plci, byte Rc) +{ + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_save_config %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + return (GOOD); +} + + +static word dtmf_parameter_restore_config(dword Id, PLCI *plci, byte Rc) +{ + word Info; + + dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_restore_config %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + Info = GOOD; + if ((plci->B1_facilities & B1_FACILITY_DTMFR) + && (plci->dtmf_parameter_length != 0)) + { + switch (plci->adjust_b_state) + { + case ADJUST_B_RESTORE_DTMF_PARAMETER_1: + plci->internal_command = plci->adjust_b_command; + if (plci->sig_req) + { + plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_1; + break; + } + dtmf_parameter_write(plci); + plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_2; + break; + case ADJUST_B_RESTORE_DTMF_PARAMETER_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Restore DTMF parameters failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _WRONG_STATE; + break; + } + break; + } + } + return (Info); +} + + +/*------------------------------------------------------------------*/ +/* Line interconnect facilities */ +/*------------------------------------------------------------------*/ + + +LI_CONFIG *li_config_table; +word li_total_channels; + + +/*------------------------------------------------------------------*/ +/* translate a CHI information element to a channel number */ +/* returns 0xff - any channel */ +/* 0xfe - chi wrong coding */ +/* 0xfd - D-channel */ +/* 0x00 - no channel */ +/* else channel number / PRI: timeslot */ +/* if channels is provided we accept more than one channel. */ +/*------------------------------------------------------------------*/ + +static byte chi_to_channel(byte *chi, dword *pchannelmap) +{ + int p; + int i; + dword map; + byte excl; + byte ofs; + byte ch; + + if (pchannelmap) *pchannelmap = 0; + if (!chi[0]) return 0xff; + excl = 0; + + if (chi[1] & 0x20) { + if (chi[0] == 1 && chi[1] == 0xac) return 0xfd; /* exclusive d-channel */ + for (i = 1; i < chi[0] && !(chi[i] & 0x80); i++); + if (i == chi[0] || !(chi[i] & 0x80)) return 0xfe; + if ((chi[1] | 0xc8) != 0xe9) return 0xfe; + if (chi[1] & 0x08) excl = 0x40; + + /* int. id present */ + if (chi[1] & 0x40) { + p = i + 1; + for (i = p; i < chi[0] && !(chi[i] & 0x80); i++); + if (i == chi[0] || !(chi[i] & 0x80)) return 0xfe; + } + + /* coding standard, Number/Map, Channel Type */ + p = i + 1; + for (i = p; i < chi[0] && !(chi[i] & 0x80); i++); + if (i == chi[0] || !(chi[i] & 0x80)) return 0xfe; + if ((chi[p] | 0xd0) != 0xd3) return 0xfe; + + /* Number/Map */ + if (chi[p] & 0x10) { + + /* map */ + if ((chi[0] - p) == 4) ofs = 0; + else if ((chi[0] - p) == 3) ofs = 1; + else return 0xfe; + ch = 0; + map = 0; + for (i = 0; i < 4 && p < chi[0]; i++) { + p++; + ch += 8; + map <<= 8; + if (chi[p]) { + for (ch = 0; !(chi[p] & (1 << ch)); ch++); + map |= chi[p]; + } + } + ch += ofs; + map <<= ofs; + } + else { + + /* number */ + p = i + 1; + ch = chi[p] & 0x3f; + if (pchannelmap) { + if ((byte)(chi[0] - p) > 30) return 0xfe; + map = 0; + for (i = p; i <= chi[0]; i++) { + if ((chi[i] & 0x7f) > 31) return 0xfe; + map |= (1L << (chi[i] & 0x7f)); + } + } + else { + if (p != chi[0]) return 0xfe; + if (ch > 31) return 0xfe; + map = (1L << ch); + } + if (chi[p] & 0x40) return 0xfe; + } + if (pchannelmap) *pchannelmap = map; + else if (map != ((dword)(1L << ch))) return 0xfe; + return (byte)(excl | ch); + } + else { /* not PRI */ + for (i = 1; i < chi[0] && !(chi[i] & 0x80); i++); + if (i != chi[0] || !(chi[i] & 0x80)) return 0xfe; + if (chi[1] & 0x08) excl = 0x40; + + switch (chi[1] | 0x98) { + case 0x98: return 0; + case 0x99: + if (pchannelmap) *pchannelmap = 2; + return excl | 1; + case 0x9a: + if (pchannelmap) *pchannelmap = 4; + return excl | 2; + case 0x9b: return 0xff; + case 0x9c: return 0xfd; /* d-ch */ + default: return 0xfe; + } + } +} + + +static void mixer_set_bchannel_id_esc(PLCI *plci, byte bchannel_id) +{ + DIVA_CAPI_ADAPTER *a; + PLCI *splci; + byte old_id; + + a = plci->adapter; + old_id = plci->li_bchannel_id; + if (a->li_pri) + { + if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci)) + li_config_table[a->li_base + (old_id - 1)].plci = NULL; + plci->li_bchannel_id = (bchannel_id & 0x1f) + 1; + if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL) + li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci; + } + else + { + if (((bchannel_id & 0x03) == 1) || ((bchannel_id & 0x03) == 2)) + { + if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci)) + li_config_table[a->li_base + (old_id - 1)].plci = NULL; + plci->li_bchannel_id = bchannel_id & 0x03; + if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI != plci) && (a->AdvSignalPLCI->tel == ADV_VOICE)) + { + splci = a->AdvSignalPLCI; + if (li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci == NULL) + { + if ((splci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci == splci)) + { + li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci = NULL; + } + splci->li_bchannel_id = 3 - plci->li_bchannel_id; + li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci = splci; + dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id_esc %d", + (dword)((splci->Id << 8) | UnMapController(splci->adapter->Id)), + (char *)(FILE_), __LINE__, splci->li_bchannel_id)); + } + } + if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL) + li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci; + } + } + if ((old_id == 0) && (plci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci)) + { + mixer_clear_config(plci); + } + dbug(1, dprintf("[%06lx] %s,%d: mixer_set_bchannel_id_esc %d %d", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, bchannel_id, plci->li_bchannel_id)); +} + + +static void mixer_set_bchannel_id(PLCI *plci, byte *chi) +{ + DIVA_CAPI_ADAPTER *a; + PLCI *splci; + byte ch, old_id; + + a = plci->adapter; + old_id = plci->li_bchannel_id; + ch = chi_to_channel(chi, NULL); + if (!(ch & 0x80)) + { + if (a->li_pri) + { + if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci)) + li_config_table[a->li_base + (old_id - 1)].plci = NULL; + plci->li_bchannel_id = (ch & 0x1f) + 1; + if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL) + li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci; + } + else + { + if (((ch & 0x1f) == 1) || ((ch & 0x1f) == 2)) + { + if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci)) + li_config_table[a->li_base + (old_id - 1)].plci = NULL; + plci->li_bchannel_id = ch & 0x1f; + if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI != plci) && (a->AdvSignalPLCI->tel == ADV_VOICE)) + { + splci = a->AdvSignalPLCI; + if (li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci == NULL) + { + if ((splci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci == splci)) + { + li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci = NULL; + } + splci->li_bchannel_id = 3 - plci->li_bchannel_id; + li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci = splci; + dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id %d", + (dword)((splci->Id << 8) | UnMapController(splci->adapter->Id)), + (char *)(FILE_), __LINE__, splci->li_bchannel_id)); + } + } + if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL) + li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci; + } + } + } + if ((old_id == 0) && (plci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci)) + { + mixer_clear_config(plci); + } + dbug(1, dprintf("[%06lx] %s,%d: mixer_set_bchannel_id %02x %d", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, ch, plci->li_bchannel_id)); +} + + +#define MIXER_MAX_DUMP_CHANNELS 34 + +static void mixer_calculate_coefs(DIVA_CAPI_ADAPTER *a) +{ + word n, i, j; + char *p; + char hex_line[2 * MIXER_MAX_DUMP_CHANNELS + MIXER_MAX_DUMP_CHANNELS / 8 + 4]; + + dbug(1, dprintf("[%06lx] %s,%d: mixer_calculate_coefs", + (dword)(UnMapController(a->Id)), (char *)(FILE_), __LINE__)); + + for (i = 0; i < li_total_channels; i++) + { + li_config_table[i].channel &= LI_CHANNEL_ADDRESSES_SET; + if (li_config_table[i].chflags != 0) + li_config_table[i].channel |= LI_CHANNEL_INVOLVED; + else + { + for (j = 0; j < li_total_channels; j++) + { + if (((li_config_table[i].flag_table[j]) != 0) + || ((li_config_table[j].flag_table[i]) != 0)) + { + li_config_table[i].channel |= LI_CHANNEL_INVOLVED; + } + if (((li_config_table[i].flag_table[j] & LI_FLAG_CONFERENCE) != 0) + || ((li_config_table[j].flag_table[i] & LI_FLAG_CONFERENCE) != 0)) + { + li_config_table[i].channel |= LI_CHANNEL_CONFERENCE; + } + } + } + } + for (i = 0; i < li_total_channels; i++) + { + for (j = 0; j < li_total_channels; j++) + { + li_config_table[i].coef_table[j] &= ~(LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC); + if (li_config_table[i].flag_table[j] & LI_FLAG_CONFERENCE) + li_config_table[i].coef_table[j] |= LI_COEF_CH_CH; + } + } + for (n = 0; n < li_total_channels; n++) + { + if (li_config_table[n].channel & LI_CHANNEL_CONFERENCE) + { + for (i = 0; i < li_total_channels; i++) + { + if (li_config_table[i].channel & LI_CHANNEL_CONFERENCE) + { + for (j = 0; j < li_total_channels; j++) + { + li_config_table[i].coef_table[j] |= + li_config_table[i].coef_table[n] & li_config_table[n].coef_table[j]; + } + } + } + } + } + for (i = 0; i < li_total_channels; i++) + { + if (li_config_table[i].channel & LI_CHANNEL_INVOLVED) + { + li_config_table[i].coef_table[i] &= ~LI_COEF_CH_CH; + for (j = 0; j < li_total_channels; j++) + { + if (li_config_table[i].coef_table[j] & LI_COEF_CH_CH) + li_config_table[i].flag_table[j] |= LI_FLAG_CONFERENCE; + } + if (li_config_table[i].flag_table[i] & LI_FLAG_CONFERENCE) + li_config_table[i].coef_table[i] |= LI_COEF_CH_CH; + } + } + for (i = 0; i < li_total_channels; i++) + { + if (li_config_table[i].channel & LI_CHANNEL_INVOLVED) + { + for (j = 0; j < li_total_channels; j++) + { + if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT) + li_config_table[i].coef_table[j] |= LI_COEF_CH_CH; + if (li_config_table[i].flag_table[j] & LI_FLAG_MONITOR) + li_config_table[i].coef_table[j] |= LI_COEF_CH_PC; + if (li_config_table[i].flag_table[j] & LI_FLAG_MIX) + li_config_table[i].coef_table[j] |= LI_COEF_PC_CH; + if (li_config_table[i].flag_table[j] & LI_FLAG_PCCONNECT) + li_config_table[i].coef_table[j] |= LI_COEF_PC_PC; + } + if (li_config_table[i].chflags & LI_CHFLAG_MONITOR) + { + for (j = 0; j < li_total_channels; j++) + { + if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT) + { + li_config_table[i].coef_table[j] |= LI_COEF_CH_PC; + if (li_config_table[j].chflags & LI_CHFLAG_MIX) + li_config_table[i].coef_table[j] |= LI_COEF_PC_CH | LI_COEF_PC_PC; + } + } + } + if (li_config_table[i].chflags & LI_CHFLAG_MIX) + { + for (j = 0; j < li_total_channels; j++) + { + if (li_config_table[j].flag_table[i] & LI_FLAG_INTERCONNECT) + li_config_table[j].coef_table[i] |= LI_COEF_PC_CH; + } + } + if (li_config_table[i].chflags & LI_CHFLAG_LOOP) + { + for (j = 0; j < li_total_channels; j++) + { + if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT) + { + for (n = 0; n < li_total_channels; n++) + { + if (li_config_table[n].flag_table[i] & LI_FLAG_INTERCONNECT) + { + li_config_table[n].coef_table[j] |= LI_COEF_CH_CH; + if (li_config_table[j].chflags & LI_CHFLAG_MIX) + { + li_config_table[n].coef_table[j] |= LI_COEF_PC_CH; + if (li_config_table[n].chflags & LI_CHFLAG_MONITOR) + li_config_table[n].coef_table[j] |= LI_COEF_CH_PC | LI_COEF_PC_PC; + } + else if (li_config_table[n].chflags & LI_CHFLAG_MONITOR) + li_config_table[n].coef_table[j] |= LI_COEF_CH_PC; + } + } + } + } + } + } + } + for (i = 0; i < li_total_channels; i++) + { + if (li_config_table[i].channel & LI_CHANNEL_INVOLVED) + { + if (li_config_table[i].chflags & (LI_CHFLAG_MONITOR | LI_CHFLAG_MIX | LI_CHFLAG_LOOP)) + li_config_table[i].channel |= LI_CHANNEL_ACTIVE; + if (li_config_table[i].chflags & LI_CHFLAG_MONITOR) + li_config_table[i].channel |= LI_CHANNEL_RX_DATA; + if (li_config_table[i].chflags & LI_CHFLAG_MIX) + li_config_table[i].channel |= LI_CHANNEL_TX_DATA; + for (j = 0; j < li_total_channels; j++) + { + if ((li_config_table[i].flag_table[j] & + (LI_FLAG_INTERCONNECT | LI_FLAG_PCCONNECT | LI_FLAG_CONFERENCE | LI_FLAG_MONITOR)) + || (li_config_table[j].flag_table[i] & + (LI_FLAG_INTERCONNECT | LI_FLAG_PCCONNECT | LI_FLAG_CONFERENCE | LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX))) + { + li_config_table[i].channel |= LI_CHANNEL_ACTIVE; + } + if (li_config_table[i].flag_table[j] & (LI_FLAG_PCCONNECT | LI_FLAG_MONITOR)) + li_config_table[i].channel |= LI_CHANNEL_RX_DATA; + if (li_config_table[j].flag_table[i] & (LI_FLAG_PCCONNECT | LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX)) + li_config_table[i].channel |= LI_CHANNEL_TX_DATA; + } + if (!(li_config_table[i].channel & LI_CHANNEL_ACTIVE)) + { + li_config_table[i].coef_table[i] |= LI_COEF_PC_CH | LI_COEF_CH_PC; + li_config_table[i].channel |= LI_CHANNEL_TX_DATA | LI_CHANNEL_RX_DATA; + } + } + } + for (i = 0; i < li_total_channels; i++) + { + if (li_config_table[i].channel & LI_CHANNEL_INVOLVED) + { + j = 0; + while ((j < li_total_channels) && !(li_config_table[i].flag_table[j] & LI_FLAG_ANNOUNCEMENT)) + j++; + if (j < li_total_channels) + { + for (j = 0; j < li_total_channels; j++) + { + li_config_table[i].coef_table[j] &= ~(LI_COEF_CH_CH | LI_COEF_PC_CH); + if (li_config_table[i].flag_table[j] & LI_FLAG_ANNOUNCEMENT) + li_config_table[i].coef_table[j] |= LI_COEF_PC_CH; + } + } + } + } + n = li_total_channels; + if (n > MIXER_MAX_DUMP_CHANNELS) + n = MIXER_MAX_DUMP_CHANNELS; + + p = hex_line; + for (j = 0; j < n; j++) + { + if ((j & 0x7) == 0) + *(p++) = ' '; + p = hex_byte_pack(p, li_config_table[j].curchnl); + } + *p = '\0'; + dbug(1, dprintf("[%06lx] CURRENT %s", + (dword)(UnMapController(a->Id)), (char *)hex_line)); + p = hex_line; + for (j = 0; j < n; j++) + { + if ((j & 0x7) == 0) + *(p++) = ' '; + p = hex_byte_pack(p, li_config_table[j].channel); + } + *p = '\0'; + dbug(1, dprintf("[%06lx] CHANNEL %s", + (dword)(UnMapController(a->Id)), (char *)hex_line)); + p = hex_line; + for (j = 0; j < n; j++) + { + if ((j & 0x7) == 0) + *(p++) = ' '; + p = hex_byte_pack(p, li_config_table[j].chflags); + } + *p = '\0'; + dbug(1, dprintf("[%06lx] CHFLAG %s", + (dword)(UnMapController(a->Id)), (char *)hex_line)); + for (i = 0; i < n; i++) + { + p = hex_line; + for (j = 0; j < n; j++) + { + if ((j & 0x7) == 0) + *(p++) = ' '; + p = hex_byte_pack(p, li_config_table[i].flag_table[j]); + } + *p = '\0'; + dbug(1, dprintf("[%06lx] FLAG[%02x]%s", + (dword)(UnMapController(a->Id)), i, (char *)hex_line)); + } + for (i = 0; i < n; i++) + { + p = hex_line; + for (j = 0; j < n; j++) + { + if ((j & 0x7) == 0) + *(p++) = ' '; + p = hex_byte_pack(p, li_config_table[i].coef_table[j]); + } + *p = '\0'; + dbug(1, dprintf("[%06lx] COEF[%02x]%s", + (dword)(UnMapController(a->Id)), i, (char *)hex_line)); + } +} + + +static struct +{ + byte mask; + byte line_flags; +} mixer_write_prog_pri[] = +{ + { LI_COEF_CH_CH, 0 }, + { LI_COEF_CH_PC, MIXER_COEF_LINE_TO_PC_FLAG }, + { LI_COEF_PC_CH, MIXER_COEF_LINE_FROM_PC_FLAG }, + { LI_COEF_PC_PC, MIXER_COEF_LINE_TO_PC_FLAG | MIXER_COEF_LINE_FROM_PC_FLAG } +}; + +static struct +{ + byte from_ch; + byte to_ch; + byte mask; + byte xconnect_override; +} mixer_write_prog_bri[] = +{ + { 0, 0, LI_COEF_CH_CH, 0x01 }, /* B to B */ + { 1, 0, LI_COEF_CH_CH, 0x01 }, /* Alt B to B */ + { 0, 0, LI_COEF_PC_CH, 0x80 }, /* PC to B */ + { 1, 0, LI_COEF_PC_CH, 0x01 }, /* Alt PC to B */ + { 2, 0, LI_COEF_CH_CH, 0x00 }, /* IC to B */ + { 3, 0, LI_COEF_CH_CH, 0x00 }, /* Alt IC to B */ + { 0, 0, LI_COEF_CH_PC, 0x80 }, /* B to PC */ + { 1, 0, LI_COEF_CH_PC, 0x01 }, /* Alt B to PC */ + { 0, 0, LI_COEF_PC_PC, 0x01 }, /* PC to PC */ + { 1, 0, LI_COEF_PC_PC, 0x01 }, /* Alt PC to PC */ + { 2, 0, LI_COEF_CH_PC, 0x00 }, /* IC to PC */ + { 3, 0, LI_COEF_CH_PC, 0x00 }, /* Alt IC to PC */ + { 0, 2, LI_COEF_CH_CH, 0x00 }, /* B to IC */ + { 1, 2, LI_COEF_CH_CH, 0x00 }, /* Alt B to IC */ + { 0, 2, LI_COEF_PC_CH, 0x00 }, /* PC to IC */ + { 1, 2, LI_COEF_PC_CH, 0x00 }, /* Alt PC to IC */ + { 2, 2, LI_COEF_CH_CH, 0x00 }, /* IC to IC */ + { 3, 2, LI_COEF_CH_CH, 0x00 }, /* Alt IC to IC */ + { 1, 1, LI_COEF_CH_CH, 0x01 }, /* Alt B to Alt B */ + { 0, 1, LI_COEF_CH_CH, 0x01 }, /* B to Alt B */ + { 1, 1, LI_COEF_PC_CH, 0x80 }, /* Alt PC to Alt B */ + { 0, 1, LI_COEF_PC_CH, 0x01 }, /* PC to Alt B */ + { 3, 1, LI_COEF_CH_CH, 0x00 }, /* Alt IC to Alt B */ + { 2, 1, LI_COEF_CH_CH, 0x00 }, /* IC to Alt B */ + { 1, 1, LI_COEF_CH_PC, 0x80 }, /* Alt B to Alt PC */ + { 0, 1, LI_COEF_CH_PC, 0x01 }, /* B to Alt PC */ + { 1, 1, LI_COEF_PC_PC, 0x01 }, /* Alt PC to Alt PC */ + { 0, 1, LI_COEF_PC_PC, 0x01 }, /* PC to Alt PC */ + { 3, 1, LI_COEF_CH_PC, 0x00 }, /* Alt IC to Alt PC */ + { 2, 1, LI_COEF_CH_PC, 0x00 }, /* IC to Alt PC */ + { 1, 3, LI_COEF_CH_CH, 0x00 }, /* Alt B to Alt IC */ + { 0, 3, LI_COEF_CH_CH, 0x00 }, /* B to Alt IC */ + { 1, 3, LI_COEF_PC_CH, 0x00 }, /* Alt PC to Alt IC */ + { 0, 3, LI_COEF_PC_CH, 0x00 }, /* PC to Alt IC */ + { 3, 3, LI_COEF_CH_CH, 0x00 }, /* Alt IC to Alt IC */ + { 2, 3, LI_COEF_CH_CH, 0x00 } /* IC to Alt IC */ +}; + +static byte mixer_swapped_index_bri[] = +{ + 18, /* B to B */ + 19, /* Alt B to B */ + 20, /* PC to B */ + 21, /* Alt PC to B */ + 22, /* IC to B */ + 23, /* Alt IC to B */ + 24, /* B to PC */ + 25, /* Alt B to PC */ + 26, /* PC to PC */ + 27, /* Alt PC to PC */ + 28, /* IC to PC */ + 29, /* Alt IC to PC */ + 30, /* B to IC */ + 31, /* Alt B to IC */ + 32, /* PC to IC */ + 33, /* Alt PC to IC */ + 34, /* IC to IC */ + 35, /* Alt IC to IC */ + 0, /* Alt B to Alt B */ + 1, /* B to Alt B */ + 2, /* Alt PC to Alt B */ + 3, /* PC to Alt B */ + 4, /* Alt IC to Alt B */ + 5, /* IC to Alt B */ + 6, /* Alt B to Alt PC */ + 7, /* B to Alt PC */ + 8, /* Alt PC to Alt PC */ + 9, /* PC to Alt PC */ + 10, /* Alt IC to Alt PC */ + 11, /* IC to Alt PC */ + 12, /* Alt B to Alt IC */ + 13, /* B to Alt IC */ + 14, /* Alt PC to Alt IC */ + 15, /* PC to Alt IC */ + 16, /* Alt IC to Alt IC */ + 17 /* IC to Alt IC */ +}; + +static struct +{ + byte mask; + byte from_pc; + byte to_pc; +} xconnect_write_prog[] = +{ + { LI_COEF_CH_CH, false, false }, + { LI_COEF_CH_PC, false, true }, + { LI_COEF_PC_CH, true, false }, + { LI_COEF_PC_PC, true, true } +}; + + +static void xconnect_query_addresses(PLCI *plci) +{ + DIVA_CAPI_ADAPTER *a; + word w, ch; + byte *p; + + dbug(1, dprintf("[%06lx] %s,%d: xconnect_query_addresses", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + a = plci->adapter; + if (a->li_pri && ((plci->li_bchannel_id == 0) + || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci))) + { + dbug(1, dprintf("[%06x] %s,%d: Channel id wiped out", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + return; + } + p = plci->internal_req_buffer; + ch = (a->li_pri) ? plci->li_bchannel_id - 1 : 0; + *(p++) = UDATA_REQUEST_XCONNECT_FROM; + w = ch; + *(p++) = (byte) w; + *(p++) = (byte)(w >> 8); + w = ch | XCONNECT_CHANNEL_PORT_PC; + *(p++) = (byte) w; + *(p++) = (byte)(w >> 8); + plci->NData[0].P = plci->internal_req_buffer; + plci->NData[0].PLength = p - plci->internal_req_buffer; + plci->NL.X = plci->NData; + plci->NL.ReqCh = 0; + plci->NL.Req = plci->nl_req = (byte) N_UDATA; + plci->adapter->request(&plci->NL); +} + + +static void xconnect_write_coefs(PLCI *plci, word internal_command) +{ + + dbug(1, dprintf("[%06lx] %s,%d: xconnect_write_coefs %04x", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, internal_command)); + + plci->li_write_command = internal_command; + plci->li_write_channel = 0; +} + + +static byte xconnect_write_coefs_process(dword Id, PLCI *plci, byte Rc) +{ + DIVA_CAPI_ADAPTER *a; + word w, n, i, j, r, s, to_ch; + dword d; + byte *p; + struct xconnect_transfer_address_s *transfer_address; + byte ch_map[MIXER_CHANNELS_BRI]; + + dbug(1, dprintf("[%06x] %s,%d: xconnect_write_coefs_process %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->li_write_channel)); + + a = plci->adapter; + if ((plci->li_bchannel_id == 0) + || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci)) + { + dbug(1, dprintf("[%06x] %s,%d: Channel id wiped out", + UnMapId(Id), (char *)(FILE_), __LINE__)); + return (true); + } + i = a->li_base + (plci->li_bchannel_id - 1); + j = plci->li_write_channel; + p = plci->internal_req_buffer; + if (j != 0) + { + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: LI write coefs failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + return (false); + } + } + if (li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT) + { + r = 0; + s = 0; + if (j < li_total_channels) + { + if (li_config_table[i].channel & LI_CHANNEL_ADDRESSES_SET) + { + s = ((li_config_table[i].send_b.card_address.low | li_config_table[i].send_b.card_address.high) ? + (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_PC | LI_COEF_PC_PC)) & + ((li_config_table[i].send_pc.card_address.low | li_config_table[i].send_pc.card_address.high) ? + (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_PC_CH)); + } + r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4)); + while ((j < li_total_channels) + && ((r == 0) + || (!(li_config_table[j].channel & LI_CHANNEL_ADDRESSES_SET)) + || (!li_config_table[j].adapter->li_pri + && (j >= li_config_table[j].adapter->li_base + MIXER_BCHANNELS_BRI)) + || (((li_config_table[j].send_b.card_address.low != li_config_table[i].send_b.card_address.low) + || (li_config_table[j].send_b.card_address.high != li_config_table[i].send_b.card_address.high)) + && (!(a->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT) + || !(li_config_table[j].adapter->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT))) + || ((li_config_table[j].adapter->li_base != a->li_base) + && !(r & s & + ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ? + (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) & + ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ? + (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC)))))) + { + j++; + if (j < li_total_channels) + r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4)); + } + } + if (j < li_total_channels) + { + plci->internal_command = plci->li_write_command; + if (plci_nl_busy(plci)) + return (true); + to_ch = (a->li_pri) ? plci->li_bchannel_id - 1 : 0; + *(p++) = UDATA_REQUEST_XCONNECT_TO; + do + { + if (li_config_table[j].adapter->li_base != a->li_base) + { + r &= s & + ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ? + (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) & + ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ? + (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC)); + } + n = 0; + do + { + if (r & xconnect_write_prog[n].mask) + { + if (xconnect_write_prog[n].from_pc) + transfer_address = &(li_config_table[j].send_pc); + else + transfer_address = &(li_config_table[j].send_b); + d = transfer_address->card_address.low; + *(p++) = (byte) d; + *(p++) = (byte)(d >> 8); + *(p++) = (byte)(d >> 16); + *(p++) = (byte)(d >> 24); + d = transfer_address->card_address.high; + *(p++) = (byte) d; + *(p++) = (byte)(d >> 8); + *(p++) = (byte)(d >> 16); + *(p++) = (byte)(d >> 24); + d = transfer_address->offset; + *(p++) = (byte) d; + *(p++) = (byte)(d >> 8); + *(p++) = (byte)(d >> 16); + *(p++) = (byte)(d >> 24); + w = xconnect_write_prog[n].to_pc ? to_ch | XCONNECT_CHANNEL_PORT_PC : to_ch; + *(p++) = (byte) w; + *(p++) = (byte)(w >> 8); + w = ((li_config_table[i].coef_table[j] & xconnect_write_prog[n].mask) == 0) ? 0x01 : + (li_config_table[i].adapter->u_law ? + (li_config_table[j].adapter->u_law ? 0x80 : 0x86) : + (li_config_table[j].adapter->u_law ? 0x7a : 0x80)); + *(p++) = (byte) w; + *(p++) = (byte) 0; + li_config_table[i].coef_table[j] ^= xconnect_write_prog[n].mask << 4; + } + n++; + } while ((n < ARRAY_SIZE(xconnect_write_prog)) + && ((p - plci->internal_req_buffer) + 16 < INTERNAL_REQ_BUFFER_SIZE)); + if (n == ARRAY_SIZE(xconnect_write_prog)) + { + do + { + j++; + if (j < li_total_channels) + r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4)); + } while ((j < li_total_channels) + && ((r == 0) + || (!(li_config_table[j].channel & LI_CHANNEL_ADDRESSES_SET)) + || (!li_config_table[j].adapter->li_pri + && (j >= li_config_table[j].adapter->li_base + MIXER_BCHANNELS_BRI)) + || (((li_config_table[j].send_b.card_address.low != li_config_table[i].send_b.card_address.low) + || (li_config_table[j].send_b.card_address.high != li_config_table[i].send_b.card_address.high)) + && (!(a->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT) + || !(li_config_table[j].adapter->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT))) + || ((li_config_table[j].adapter->li_base != a->li_base) + && !(r & s & + ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ? + (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) & + ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ? + (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC)))))); + } + } while ((j < li_total_channels) + && ((p - plci->internal_req_buffer) + 16 < INTERNAL_REQ_BUFFER_SIZE)); + } + else if (j == li_total_channels) + { + plci->internal_command = plci->li_write_command; + if (plci_nl_busy(plci)) + return (true); + if (a->li_pri) + { + *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC; + w = 0; + if (li_config_table[i].channel & LI_CHANNEL_TX_DATA) + w |= MIXER_FEATURE_ENABLE_TX_DATA; + if (li_config_table[i].channel & LI_CHANNEL_RX_DATA) + w |= MIXER_FEATURE_ENABLE_RX_DATA; + *(p++) = (byte) w; + *(p++) = (byte)(w >> 8); + } + else + { + *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_BRI; + w = 0; + if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI) + && (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length)) + { + w = GET_WORD(a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE); + } + if (li_config_table[i].channel & LI_CHANNEL_TX_DATA) + w |= MIXER_FEATURE_ENABLE_TX_DATA; + if (li_config_table[i].channel & LI_CHANNEL_RX_DATA) + w |= MIXER_FEATURE_ENABLE_RX_DATA; + *(p++) = (byte) w; + *(p++) = (byte)(w >> 8); + for (j = 0; j < sizeof(ch_map); j += 2) + { + if (plci->li_bchannel_id == 2) + { + ch_map[j] = (byte)(j + 1); + ch_map[j + 1] = (byte) j; + } + else + { + ch_map[j] = (byte) j; + ch_map[j + 1] = (byte)(j + 1); + } + } + for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++) + { + i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch]; + j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch]; + if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED) + { + *p = (mixer_write_prog_bri[n].xconnect_override != 0) ? + mixer_write_prog_bri[n].xconnect_override : + ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01); + if ((i >= a->li_base + MIXER_BCHANNELS_BRI) || (j >= a->li_base + MIXER_BCHANNELS_BRI)) + { + w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4)); + li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4; + } + } + else + { + *p = 0x00; + if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI->tel == ADV_VOICE)) + { + w = (plci == a->AdvSignalPLCI) ? n : mixer_swapped_index_bri[n]; + if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w < a->adv_voice_coef_length) + *p = a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w]; + } + } + p++; + } + } + j = li_total_channels + 1; + } + } + else + { + if (j <= li_total_channels) + { + plci->internal_command = plci->li_write_command; + if (plci_nl_busy(plci)) + return (true); + if (j < a->li_base) + j = a->li_base; + if (a->li_pri) + { + *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC; + w = 0; + if (li_config_table[i].channel & LI_CHANNEL_TX_DATA) + w |= MIXER_FEATURE_ENABLE_TX_DATA; + if (li_config_table[i].channel & LI_CHANNEL_RX_DATA) + w |= MIXER_FEATURE_ENABLE_RX_DATA; + *(p++) = (byte) w; + *(p++) = (byte)(w >> 8); + for (n = 0; n < ARRAY_SIZE(mixer_write_prog_pri); n++) + { + *(p++) = (byte)((plci->li_bchannel_id - 1) | mixer_write_prog_pri[n].line_flags); + for (j = a->li_base; j < a->li_base + MIXER_CHANNELS_PRI; j++) + { + w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4)); + if (w & mixer_write_prog_pri[n].mask) + { + *(p++) = (li_config_table[i].coef_table[j] & mixer_write_prog_pri[n].mask) ? 0x80 : 0x01; + li_config_table[i].coef_table[j] ^= mixer_write_prog_pri[n].mask << 4; + } + else + *(p++) = 0x00; + } + *(p++) = (byte)((plci->li_bchannel_id - 1) | MIXER_COEF_LINE_ROW_FLAG | mixer_write_prog_pri[n].line_flags); + for (j = a->li_base; j < a->li_base + MIXER_CHANNELS_PRI; j++) + { + w = ((li_config_table[j].coef_table[i] & 0xf) ^ (li_config_table[j].coef_table[i] >> 4)); + if (w & mixer_write_prog_pri[n].mask) + { + *(p++) = (li_config_table[j].coef_table[i] & mixer_write_prog_pri[n].mask) ? 0x80 : 0x01; + li_config_table[j].coef_table[i] ^= mixer_write_prog_pri[n].mask << 4; + } + else + *(p++) = 0x00; + } + } + } + else + { + *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_BRI; + w = 0; + if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI) + && (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length)) + { + w = GET_WORD(a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE); + } + if (li_config_table[i].channel & LI_CHANNEL_TX_DATA) + w |= MIXER_FEATURE_ENABLE_TX_DATA; + if (li_config_table[i].channel & LI_CHANNEL_RX_DATA) + w |= MIXER_FEATURE_ENABLE_RX_DATA; + *(p++) = (byte) w; + *(p++) = (byte)(w >> 8); + for (j = 0; j < sizeof(ch_map); j += 2) + { + if (plci->li_bchannel_id == 2) + { + ch_map[j] = (byte)(j + 1); + ch_map[j + 1] = (byte) j; + } + else + { + ch_map[j] = (byte) j; + ch_map[j + 1] = (byte)(j + 1); + } + } + for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++) + { + i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch]; + j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch]; + if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED) + { + *p = ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01); + w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4)); + li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4; + } + else + { + *p = 0x00; + if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI->tel == ADV_VOICE)) + { + w = (plci == a->AdvSignalPLCI) ? n : mixer_swapped_index_bri[n]; + if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w < a->adv_voice_coef_length) + *p = a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w]; + } + } + p++; + } + } + j = li_total_channels + 1; + } + } + plci->li_write_channel = j; + if (p != plci->internal_req_buffer) + { + plci->NData[0].P = plci->internal_req_buffer; + plci->NData[0].PLength = p - plci->internal_req_buffer; + plci->NL.X = plci->NData; + plci->NL.ReqCh = 0; + plci->NL.Req = plci->nl_req = (byte) N_UDATA; + plci->adapter->request(&plci->NL); + } + return (true); +} + + +static void mixer_notify_update(PLCI *plci, byte others) +{ + DIVA_CAPI_ADAPTER *a; + word i, w; + PLCI *notify_plci; + byte msg[sizeof(CAPI_MSG_HEADER) + 6]; + + dbug(1, dprintf("[%06lx] %s,%d: mixer_notify_update %d", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, others)); + + a = plci->adapter; + if (a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED) + { + if (others) + plci->li_notify_update = true; + i = 0; + do + { + notify_plci = NULL; + if (others) + { + while ((i < li_total_channels) && (li_config_table[i].plci == NULL)) + i++; + if (i < li_total_channels) + notify_plci = li_config_table[i++].plci; + } + else + { + if ((plci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci)) + { + notify_plci = plci; + } + } + if ((notify_plci != NULL) + && !notify_plci->li_notify_update + && (notify_plci->appl != NULL) + && (notify_plci->State) + && notify_plci->NL.Id && !notify_plci->nl_remove_id) + { + notify_plci->li_notify_update = true; + ((CAPI_MSG *) msg)->header.length = 18; + ((CAPI_MSG *) msg)->header.appl_id = notify_plci->appl->Id; + ((CAPI_MSG *) msg)->header.command = _FACILITY_R; + ((CAPI_MSG *) msg)->header.number = 0; + ((CAPI_MSG *) msg)->header.controller = notify_plci->adapter->Id; + ((CAPI_MSG *) msg)->header.plci = notify_plci->Id; + ((CAPI_MSG *) msg)->header.ncci = 0; + ((CAPI_MSG *) msg)->info.facility_req.Selector = SELECTOR_LINE_INTERCONNECT; + ((CAPI_MSG *) msg)->info.facility_req.structs[0] = 3; + ((CAPI_MSG *) msg)->info.facility_req.structs[1] = LI_REQ_SILENT_UPDATE & 0xff; + ((CAPI_MSG *) msg)->info.facility_req.structs[2] = LI_REQ_SILENT_UPDATE >> 8; + ((CAPI_MSG *) msg)->info.facility_req.structs[3] = 0; + w = api_put(notify_plci->appl, (CAPI_MSG *) msg); + if (w != _QUEUE_FULL) + { + if (w != 0) + { + dbug(1, dprintf("[%06lx] %s,%d: Interconnect notify failed %06x %d", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, + (dword)((notify_plci->Id << 8) | UnMapController(notify_plci->adapter->Id)), w)); + } + notify_plci->li_notify_update = false; + } + } + } while (others && (notify_plci != NULL)); + if (others) + plci->li_notify_update = false; + } +} + + +static void mixer_clear_config(PLCI *plci) +{ + DIVA_CAPI_ADAPTER *a; + word i, j; + + dbug(1, dprintf("[%06lx] %s,%d: mixer_clear_config", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + plci->li_notify_update = false; + plci->li_plci_b_write_pos = 0; + plci->li_plci_b_read_pos = 0; + plci->li_plci_b_req_pos = 0; + a = plci->adapter; + if ((plci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci)) + { + i = a->li_base + (plci->li_bchannel_id - 1); + li_config_table[i].curchnl = 0; + li_config_table[i].channel = 0; + li_config_table[i].chflags = 0; + for (j = 0; j < li_total_channels; j++) + { + li_config_table[j].flag_table[i] = 0; + li_config_table[i].flag_table[j] = 0; + li_config_table[i].coef_table[j] = 0; + li_config_table[j].coef_table[i] = 0; + } + if (!a->li_pri) + { + li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET; + if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)) + { + i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1); + li_config_table[i].curchnl = 0; + li_config_table[i].channel = 0; + li_config_table[i].chflags = 0; + for (j = 0; j < li_total_channels; j++) + { + li_config_table[i].flag_table[j] = 0; + li_config_table[j].flag_table[i] = 0; + li_config_table[i].coef_table[j] = 0; + li_config_table[j].coef_table[i] = 0; + } + if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) + { + i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id); + li_config_table[i].curchnl = 0; + li_config_table[i].channel = 0; + li_config_table[i].chflags = 0; + for (j = 0; j < li_total_channels; j++) + { + li_config_table[i].flag_table[j] = 0; + li_config_table[j].flag_table[i] = 0; + li_config_table[i].coef_table[j] = 0; + li_config_table[j].coef_table[i] = 0; + } + } + } + } + } +} + + +static void mixer_prepare_switch(dword Id, PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: mixer_prepare_switch", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + do + { + mixer_indication_coefs_set(Id, plci); + } while (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos); +} + + +static word mixer_save_config(dword Id, PLCI *plci, byte Rc) +{ + DIVA_CAPI_ADAPTER *a; + word i, j; + + dbug(1, dprintf("[%06lx] %s,%d: mixer_save_config %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + a = plci->adapter; + if ((plci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci)) + { + i = a->li_base + (plci->li_bchannel_id - 1); + for (j = 0; j < li_total_channels; j++) + { + li_config_table[i].coef_table[j] &= 0xf; + li_config_table[j].coef_table[i] &= 0xf; + } + if (!a->li_pri) + li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET; + } + return (GOOD); +} + + +static word mixer_restore_config(dword Id, PLCI *plci, byte Rc) +{ + DIVA_CAPI_ADAPTER *a; + word Info; + + dbug(1, dprintf("[%06lx] %s,%d: mixer_restore_config %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + Info = GOOD; + a = plci->adapter; + if ((plci->B1_facilities & B1_FACILITY_MIXER) + && (plci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci)) + { + switch (plci->adjust_b_state) + { + case ADJUST_B_RESTORE_MIXER_1: + if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT) + { + plci->internal_command = plci->adjust_b_command; + if (plci_nl_busy(plci)) + { + plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_1; + break; + } + xconnect_query_addresses(plci); + plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_2; + break; + } + plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5; + Rc = OK; + /* fall through */ + case ADJUST_B_RESTORE_MIXER_2: + case ADJUST_B_RESTORE_MIXER_3: + case ADJUST_B_RESTORE_MIXER_4: + if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0)) + { + dbug(1, dprintf("[%06lx] %s,%d: Adjust B query addresses failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _WRONG_STATE; + break; + } + if (Rc == OK) + { + if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2) + plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_3; + else if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_4) + plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5; + } + else if (Rc == 0) + { + if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2) + plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_4; + else if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_3) + plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5; + } + if (plci->adjust_b_state != ADJUST_B_RESTORE_MIXER_5) + { + plci->internal_command = plci->adjust_b_command; + break; + } + /* fall through */ + case ADJUST_B_RESTORE_MIXER_5: + xconnect_write_coefs(plci, plci->adjust_b_command); + plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_6; + Rc = OK; + /* fall through */ + case ADJUST_B_RESTORE_MIXER_6: + if (!xconnect_write_coefs_process(Id, plci, Rc)) + { + dbug(1, dprintf("[%06lx] %s,%d: Write mixer coefs failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } + if (plci->internal_command) + break; + plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_7; + case ADJUST_B_RESTORE_MIXER_7: + break; + } + } + return (Info); +} + + +static void mixer_command(dword Id, PLCI *plci, byte Rc) +{ + DIVA_CAPI_ADAPTER *a; + word i, internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: mixer_command %02x %04x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command, + plci->li_cmd)); + + a = plci->adapter; + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (plci->li_cmd) + { + case LI_REQ_CONNECT: + case LI_REQ_DISCONNECT: + case LI_REQ_SILENT_UPDATE: + switch (internal_command) + { + default: + if (plci->li_channel_bits & LI_CHANNEL_INVOLVED) + { + adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities | + B1_FACILITY_MIXER), MIXER_COMMAND_1); + } + /* fall through */ + case MIXER_COMMAND_1: + if (plci->li_channel_bits & LI_CHANNEL_INVOLVED) + { + if (adjust_b_process(Id, plci, Rc) != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Load mixer failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + break; + } + if (plci->internal_command) + return; + } + plci->li_plci_b_req_pos = plci->li_plci_b_write_pos; + if ((plci->li_channel_bits & LI_CHANNEL_INVOLVED) + || ((get_b1_facilities(plci, plci->B1_resource) & B1_FACILITY_MIXER) + && (add_b1_facilities(plci, plci->B1_resource, (word)(plci->B1_facilities & + ~B1_FACILITY_MIXER)) == plci->B1_resource))) + { + xconnect_write_coefs(plci, MIXER_COMMAND_2); + } + else + { + do + { + mixer_indication_coefs_set(Id, plci); + } while (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos); + } + /* fall through */ + case MIXER_COMMAND_2: + if ((plci->li_channel_bits & LI_CHANNEL_INVOLVED) + || ((get_b1_facilities(plci, plci->B1_resource) & B1_FACILITY_MIXER) + && (add_b1_facilities(plci, plci->B1_resource, (word)(plci->B1_facilities & + ~B1_FACILITY_MIXER)) == plci->B1_resource))) + { + if (!xconnect_write_coefs_process(Id, plci, Rc)) + { + dbug(1, dprintf("[%06lx] %s,%d: Write mixer coefs failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + if (plci->li_plci_b_write_pos != plci->li_plci_b_req_pos) + { + do + { + plci->li_plci_b_write_pos = (plci->li_plci_b_write_pos == 0) ? + LI_PLCI_B_QUEUE_ENTRIES - 1 : plci->li_plci_b_write_pos - 1; + i = (plci->li_plci_b_write_pos == 0) ? + LI_PLCI_B_QUEUE_ENTRIES - 1 : plci->li_plci_b_write_pos - 1; + } while ((plci->li_plci_b_write_pos != plci->li_plci_b_req_pos) + && !(plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG)); + } + break; + } + if (plci->internal_command) + return; + } + if (!(plci->li_channel_bits & LI_CHANNEL_INVOLVED)) + { + adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities & + ~B1_FACILITY_MIXER), MIXER_COMMAND_3); + } + /* fall through */ + case MIXER_COMMAND_3: + if (!(plci->li_channel_bits & LI_CHANNEL_INVOLVED)) + { + if (adjust_b_process(Id, plci, Rc) != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Unload mixer failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + break; + } + if (plci->internal_command) + return; + } + break; + } + break; + } + if ((plci->li_bchannel_id == 0) + || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci)) + { + dbug(1, dprintf("[%06x] %s,%d: Channel id wiped out %d", + UnMapId(Id), (char *)(FILE_), __LINE__, (int)(plci->li_bchannel_id))); + } + else + { + i = a->li_base + (plci->li_bchannel_id - 1); + li_config_table[i].curchnl = plci->li_channel_bits; + if (!a->li_pri && (plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)) + { + i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1); + li_config_table[i].curchnl = plci->li_channel_bits; + if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) + { + i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id); + li_config_table[i].curchnl = plci->li_channel_bits; + } + } + } +} + + +static void li_update_connect(dword Id, DIVA_CAPI_ADAPTER *a, PLCI *plci, + dword plci_b_id, byte connect, dword li_flags) +{ + word i, ch_a, ch_a_v, ch_a_s, ch_b, ch_b_v, ch_b_s; + PLCI *plci_b; + DIVA_CAPI_ADAPTER *a_b; + + a_b = &(adapter[MapController((byte)(plci_b_id & 0x7f)) - 1]); + plci_b = &(a_b->plci[((plci_b_id >> 8) & 0xff) - 1]); + ch_a = a->li_base + (plci->li_bchannel_id - 1); + if (!a->li_pri && (plci->tel == ADV_VOICE) + && (plci == a->AdvSignalPLCI) && (Id & EXT_CONTROLLER)) + { + ch_a_v = ch_a + MIXER_IC_CHANNEL_BASE; + ch_a_s = (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ? + a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id) : ch_a_v; + } + else + { + ch_a_v = ch_a; + ch_a_s = ch_a; + } + ch_b = a_b->li_base + (plci_b->li_bchannel_id - 1); + if (!a_b->li_pri && (plci_b->tel == ADV_VOICE) + && (plci_b == a_b->AdvSignalPLCI) && (plci_b_id & EXT_CONTROLLER)) + { + ch_b_v = ch_b + MIXER_IC_CHANNEL_BASE; + ch_b_s = (a_b->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ? + a_b->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci_b->li_bchannel_id) : ch_b_v; + } + else + { + ch_b_v = ch_b; + ch_b_s = ch_b; + } + if (connect) + { + li_config_table[ch_a].flag_table[ch_a_v] &= ~LI_FLAG_MONITOR; + li_config_table[ch_a].flag_table[ch_a_s] &= ~LI_FLAG_MONITOR; + li_config_table[ch_a_v].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX); + li_config_table[ch_a_s].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX); + } + li_config_table[ch_a].flag_table[ch_b_v] &= ~LI_FLAG_MONITOR; + li_config_table[ch_a].flag_table[ch_b_s] &= ~LI_FLAG_MONITOR; + li_config_table[ch_b_v].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX); + li_config_table[ch_b_s].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX); + if (ch_a_v == ch_b_v) + { + li_config_table[ch_a_v].flag_table[ch_b_v] &= ~LI_FLAG_CONFERENCE; + li_config_table[ch_a_s].flag_table[ch_b_s] &= ~LI_FLAG_CONFERENCE; + } + else + { + if (li_config_table[ch_a_v].flag_table[ch_b_v] & LI_FLAG_CONFERENCE) + { + for (i = 0; i < li_total_channels; i++) + { + if (i != ch_a_v) + li_config_table[ch_a_v].flag_table[i] &= ~LI_FLAG_CONFERENCE; + } + } + if (li_config_table[ch_a_s].flag_table[ch_b_v] & LI_FLAG_CONFERENCE) + { + for (i = 0; i < li_total_channels; i++) + { + if (i != ch_a_s) + li_config_table[ch_a_s].flag_table[i] &= ~LI_FLAG_CONFERENCE; + } + } + if (li_config_table[ch_b_v].flag_table[ch_a_v] & LI_FLAG_CONFERENCE) + { + for (i = 0; i < li_total_channels; i++) + { + if (i != ch_a_v) + li_config_table[i].flag_table[ch_a_v] &= ~LI_FLAG_CONFERENCE; + } + } + if (li_config_table[ch_b_v].flag_table[ch_a_s] & LI_FLAG_CONFERENCE) + { + for (i = 0; i < li_total_channels; i++) + { + if (i != ch_a_s) + li_config_table[i].flag_table[ch_a_s] &= ~LI_FLAG_CONFERENCE; + } + } + } + if (li_flags & LI_FLAG_CONFERENCE_A_B) + { + li_config_table[ch_b_v].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE; + li_config_table[ch_b_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE; + li_config_table[ch_b_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE; + li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE; + } + if (li_flags & LI_FLAG_CONFERENCE_B_A) + { + li_config_table[ch_a_v].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE; + li_config_table[ch_a_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE; + li_config_table[ch_a_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE; + li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE; + } + if (li_flags & LI_FLAG_MONITOR_A) + { + li_config_table[ch_a].flag_table[ch_a_v] |= LI_FLAG_MONITOR; + li_config_table[ch_a].flag_table[ch_a_s] |= LI_FLAG_MONITOR; + } + if (li_flags & LI_FLAG_MONITOR_B) + { + li_config_table[ch_a].flag_table[ch_b_v] |= LI_FLAG_MONITOR; + li_config_table[ch_a].flag_table[ch_b_s] |= LI_FLAG_MONITOR; + } + if (li_flags & LI_FLAG_ANNOUNCEMENT_A) + { + li_config_table[ch_a_v].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT; + li_config_table[ch_a_s].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT; + } + if (li_flags & LI_FLAG_ANNOUNCEMENT_B) + { + li_config_table[ch_b_v].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT; + li_config_table[ch_b_s].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT; + } + if (li_flags & LI_FLAG_MIX_A) + { + li_config_table[ch_a_v].flag_table[ch_a] |= LI_FLAG_MIX; + li_config_table[ch_a_s].flag_table[ch_a] |= LI_FLAG_MIX; + } + if (li_flags & LI_FLAG_MIX_B) + { + li_config_table[ch_b_v].flag_table[ch_a] |= LI_FLAG_MIX; + li_config_table[ch_b_s].flag_table[ch_a] |= LI_FLAG_MIX; + } + if (ch_a_v != ch_a_s) + { + li_config_table[ch_a_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE; + li_config_table[ch_a_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE; + } + if (ch_b_v != ch_b_s) + { + li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE; + li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE; + } +} + + +static void li2_update_connect(dword Id, DIVA_CAPI_ADAPTER *a, PLCI *plci, + dword plci_b_id, byte connect, dword li_flags) +{ + word ch_a, ch_a_v, ch_a_s, ch_b, ch_b_v, ch_b_s; + PLCI *plci_b; + DIVA_CAPI_ADAPTER *a_b; + + a_b = &(adapter[MapController((byte)(plci_b_id & 0x7f)) - 1]); + plci_b = &(a_b->plci[((plci_b_id >> 8) & 0xff) - 1]); + ch_a = a->li_base + (plci->li_bchannel_id - 1); + if (!a->li_pri && (plci->tel == ADV_VOICE) + && (plci == a->AdvSignalPLCI) && (Id & EXT_CONTROLLER)) + { + ch_a_v = ch_a + MIXER_IC_CHANNEL_BASE; + ch_a_s = (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ? + a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id) : ch_a_v; + } + else + { + ch_a_v = ch_a; + ch_a_s = ch_a; + } + ch_b = a_b->li_base + (plci_b->li_bchannel_id - 1); + if (!a_b->li_pri && (plci_b->tel == ADV_VOICE) + && (plci_b == a_b->AdvSignalPLCI) && (plci_b_id & EXT_CONTROLLER)) + { + ch_b_v = ch_b + MIXER_IC_CHANNEL_BASE; + ch_b_s = (a_b->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ? + a_b->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci_b->li_bchannel_id) : ch_b_v; + } + else + { + ch_b_v = ch_b; + ch_b_s = ch_b; + } + if (connect) + { + li_config_table[ch_b].flag_table[ch_b_v] &= ~LI_FLAG_MONITOR; + li_config_table[ch_b].flag_table[ch_b_s] &= ~LI_FLAG_MONITOR; + li_config_table[ch_b_v].flag_table[ch_b] &= ~LI_FLAG_MIX; + li_config_table[ch_b_s].flag_table[ch_b] &= ~LI_FLAG_MIX; + li_config_table[ch_b].flag_table[ch_b] &= ~LI_FLAG_PCCONNECT; + li_config_table[ch_b].chflags &= ~(LI_CHFLAG_MONITOR | LI_CHFLAG_MIX | LI_CHFLAG_LOOP); + } + li_config_table[ch_b_v].flag_table[ch_a_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE); + li_config_table[ch_b_s].flag_table[ch_a_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE); + li_config_table[ch_b_v].flag_table[ch_a_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE); + li_config_table[ch_b_s].flag_table[ch_a_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE); + li_config_table[ch_a_v].flag_table[ch_b_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE); + li_config_table[ch_a_v].flag_table[ch_b_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE); + li_config_table[ch_a_s].flag_table[ch_b_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE); + li_config_table[ch_a_s].flag_table[ch_b_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE); + if (li_flags & LI2_FLAG_INTERCONNECT_A_B) + { + li_config_table[ch_b_v].flag_table[ch_a_v] |= LI_FLAG_INTERCONNECT; + li_config_table[ch_b_s].flag_table[ch_a_v] |= LI_FLAG_INTERCONNECT; + li_config_table[ch_b_v].flag_table[ch_a_s] |= LI_FLAG_INTERCONNECT; + li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_INTERCONNECT; + } + if (li_flags & LI2_FLAG_INTERCONNECT_B_A) + { + li_config_table[ch_a_v].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT; + li_config_table[ch_a_v].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT; + li_config_table[ch_a_s].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT; + li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT; + } + if (li_flags & LI2_FLAG_MONITOR_B) + { + li_config_table[ch_b].flag_table[ch_b_v] |= LI_FLAG_MONITOR; + li_config_table[ch_b].flag_table[ch_b_s] |= LI_FLAG_MONITOR; + } + if (li_flags & LI2_FLAG_MIX_B) + { + li_config_table[ch_b_v].flag_table[ch_b] |= LI_FLAG_MIX; + li_config_table[ch_b_s].flag_table[ch_b] |= LI_FLAG_MIX; + } + if (li_flags & LI2_FLAG_MONITOR_X) + li_config_table[ch_b].chflags |= LI_CHFLAG_MONITOR; + if (li_flags & LI2_FLAG_MIX_X) + li_config_table[ch_b].chflags |= LI_CHFLAG_MIX; + if (li_flags & LI2_FLAG_LOOP_B) + { + li_config_table[ch_b_v].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT; + li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT; + li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT; + li_config_table[ch_b_s].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT; + } + if (li_flags & LI2_FLAG_LOOP_PC) + li_config_table[ch_b].flag_table[ch_b] |= LI_FLAG_PCCONNECT; + if (li_flags & LI2_FLAG_LOOP_X) + li_config_table[ch_b].chflags |= LI_CHFLAG_LOOP; + if (li_flags & LI2_FLAG_PCCONNECT_A_B) + li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_PCCONNECT; + if (li_flags & LI2_FLAG_PCCONNECT_B_A) + li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_PCCONNECT; + if (ch_a_v != ch_a_s) + { + li_config_table[ch_a_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE; + li_config_table[ch_a_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE; + } + if (ch_b_v != ch_b_s) + { + li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE; + li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE; + } +} + + +static word li_check_main_plci(dword Id, PLCI *plci) +{ + if (plci == NULL) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI", + UnMapId(Id), (char *)(FILE_), __LINE__)); + return (_WRONG_IDENTIFIER); + } + if (!plci->State + || !plci->NL.Id || plci->nl_remove_id + || (plci->li_bchannel_id == 0)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong state", + UnMapId(Id), (char *)(FILE_), __LINE__)); + return (_WRONG_STATE); + } + li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci = plci; + return (GOOD); +} + + +static PLCI *li_check_plci_b(dword Id, PLCI *plci, + dword plci_b_id, word plci_b_write_pos, byte *p_result) +{ + byte ctlr_b; + PLCI *plci_b; + + if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos : + LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2) + { + dbug(1, dprintf("[%06lx] %s,%d: LI request overrun", + UnMapId(Id), (char *)(FILE_), __LINE__)); + PUT_WORD(p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE); + return (NULL); + } + ctlr_b = 0; + if ((plci_b_id & 0x7f) != 0) + { + ctlr_b = MapController((byte)(plci_b_id & 0x7f)); + if ((ctlr_b > max_adapter) || ((ctlr_b != 0) && (adapter[ctlr_b - 1].request == NULL))) + ctlr_b = 0; + } + if ((ctlr_b == 0) + || (((plci_b_id >> 8) & 0xff) == 0) + || (((plci_b_id >> 8) & 0xff) > adapter[ctlr_b - 1].max_plci)) + { + dbug(1, dprintf("[%06lx] %s,%d: LI invalid second PLCI %08lx", + UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id)); + PUT_WORD(p_result, _WRONG_IDENTIFIER); + return (NULL); + } + plci_b = &(adapter[ctlr_b - 1].plci[((plci_b_id >> 8) & 0xff) - 1]); + if (!plci_b->State + || !plci_b->NL.Id || plci_b->nl_remove_id + || (plci_b->li_bchannel_id == 0)) + { + dbug(1, dprintf("[%06lx] %s,%d: LI peer in wrong state %08lx", + UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id)); + PUT_WORD(p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE); + return (NULL); + } + li_config_table[plci_b->adapter->li_base + (plci_b->li_bchannel_id - 1)].plci = plci_b; + if (((byte)(plci_b_id & ~EXT_CONTROLLER)) != + ((byte)(UnMapController(plci->adapter->Id) & ~EXT_CONTROLLER)) + && (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT) + || !(plci_b->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT))) + { + dbug(1, dprintf("[%06lx] %s,%d: LI not on same ctrl %08lx", + UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id)); + PUT_WORD(p_result, _WRONG_IDENTIFIER); + return (NULL); + } + if (!(get_b1_facilities(plci_b, add_b1_facilities(plci_b, plci_b->B1_resource, + (word)(plci_b->B1_facilities | B1_FACILITY_MIXER))) & B1_FACILITY_MIXER)) + { + dbug(1, dprintf("[%06lx] %s,%d: Interconnect peer cannot mix %d", + UnMapId(Id), (char *)(FILE_), __LINE__, plci_b->B1_resource)); + PUT_WORD(p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE); + return (NULL); + } + return (plci_b); +} + + +static PLCI *li2_check_plci_b(dword Id, PLCI *plci, + dword plci_b_id, word plci_b_write_pos, byte *p_result) +{ + byte ctlr_b; + PLCI *plci_b; + + if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos : + LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2) + { + dbug(1, dprintf("[%06lx] %s,%d: LI request overrun", + UnMapId(Id), (char *)(FILE_), __LINE__)); + PUT_WORD(p_result, _WRONG_STATE); + return (NULL); + } + ctlr_b = 0; + if ((plci_b_id & 0x7f) != 0) + { + ctlr_b = MapController((byte)(plci_b_id & 0x7f)); + if ((ctlr_b > max_adapter) || ((ctlr_b != 0) && (adapter[ctlr_b - 1].request == NULL))) + ctlr_b = 0; + } + if ((ctlr_b == 0) + || (((plci_b_id >> 8) & 0xff) == 0) + || (((plci_b_id >> 8) & 0xff) > adapter[ctlr_b - 1].max_plci)) + { + dbug(1, dprintf("[%06lx] %s,%d: LI invalid second PLCI %08lx", + UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id)); + PUT_WORD(p_result, _WRONG_IDENTIFIER); + return (NULL); + } + plci_b = &(adapter[ctlr_b - 1].plci[((plci_b_id >> 8) & 0xff) - 1]); + if (!plci_b->State + || !plci_b->NL.Id || plci_b->nl_remove_id + || (plci_b->li_bchannel_id == 0) + || (li_config_table[plci_b->adapter->li_base + (plci_b->li_bchannel_id - 1)].plci != plci_b)) + { + dbug(1, dprintf("[%06lx] %s,%d: LI peer in wrong state %08lx", + UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id)); + PUT_WORD(p_result, _WRONG_STATE); + return (NULL); + } + if (((byte)(plci_b_id & ~EXT_CONTROLLER)) != + ((byte)(UnMapController(plci->adapter->Id) & ~EXT_CONTROLLER)) + && (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT) + || !(plci_b->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT))) + { + dbug(1, dprintf("[%06lx] %s,%d: LI not on same ctrl %08lx", + UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id)); + PUT_WORD(p_result, _WRONG_IDENTIFIER); + return (NULL); + } + if (!(get_b1_facilities(plci_b, add_b1_facilities(plci_b, plci_b->B1_resource, + (word)(plci_b->B1_facilities | B1_FACILITY_MIXER))) & B1_FACILITY_MIXER)) + { + dbug(1, dprintf("[%06lx] %s,%d: Interconnect peer cannot mix %d", + UnMapId(Id), (char *)(FILE_), __LINE__, plci_b->B1_resource)); + PUT_WORD(p_result, _WRONG_STATE); + return (NULL); + } + return (plci_b); +} + + +static byte mixer_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg) +{ + word Info; + word i; + dword d, li_flags, plci_b_id; + PLCI *plci_b; + API_PARSE li_parms[3]; + API_PARSE li_req_parms[3]; + API_PARSE li_participant_struct[2]; + API_PARSE li_participant_parms[3]; + word participant_parms_pos; + byte result_buffer[32]; + byte *result; + word result_pos; + word plci_b_write_pos; + + dbug(1, dprintf("[%06lx] %s,%d: mixer_request", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + Info = GOOD; + result = result_buffer; + result_buffer[0] = 0; + if (!(a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED)) + { + dbug(1, dprintf("[%06lx] %s,%d: Facility not supported", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _FACILITY_NOT_SUPPORTED; + } + else if (api_parse(&msg[1].info[1], msg[1].length, "ws", li_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_MESSAGE_FORMAT; + } + else + { + result_buffer[0] = 3; + PUT_WORD(&result_buffer[1], GET_WORD(li_parms[0].info)); + result_buffer[3] = 0; + switch (GET_WORD(li_parms[0].info)) + { + case LI_GET_SUPPORTED_SERVICES: + if (appl->appl_flags & APPL_FLAG_OLD_LI_SPEC) + { + result_buffer[0] = 17; + result_buffer[3] = 14; + PUT_WORD(&result_buffer[4], GOOD); + d = 0; + if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_CH) + d |= LI_CONFERENCING_SUPPORTED; + if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_PC) + d |= LI_MONITORING_SUPPORTED; + if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_CH) + d |= LI_ANNOUNCEMENTS_SUPPORTED | LI_MIXING_SUPPORTED; + if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT) + d |= LI_CROSS_CONTROLLER_SUPPORTED; + PUT_DWORD(&result_buffer[6], d); + if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT) + { + d = 0; + for (i = 0; i < li_total_channels; i++) + { + if ((li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT) + && (li_config_table[i].adapter->li_pri + || (i < li_config_table[i].adapter->li_base + MIXER_BCHANNELS_BRI))) + { + d++; + } + } + } + else + { + d = a->li_pri ? a->li_channels : MIXER_BCHANNELS_BRI; + } + PUT_DWORD(&result_buffer[10], d / 2); + PUT_DWORD(&result_buffer[14], d); + } + else + { + result_buffer[0] = 25; + result_buffer[3] = 22; + PUT_WORD(&result_buffer[4], GOOD); + d = LI2_ASYMMETRIC_SUPPORTED | LI2_B_LOOPING_SUPPORTED | LI2_X_LOOPING_SUPPORTED; + if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_PC) + d |= LI2_MONITORING_SUPPORTED | LI2_REMOTE_MONITORING_SUPPORTED; + if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_CH) + d |= LI2_MIXING_SUPPORTED | LI2_REMOTE_MIXING_SUPPORTED; + if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_PC) + d |= LI2_PC_LOOPING_SUPPORTED; + if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT) + d |= LI2_CROSS_CONTROLLER_SUPPORTED; + PUT_DWORD(&result_buffer[6], d); + d = a->li_pri ? a->li_channels : MIXER_BCHANNELS_BRI; + PUT_DWORD(&result_buffer[10], d / 2); + PUT_DWORD(&result_buffer[14], d - 1); + if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT) + { + d = 0; + for (i = 0; i < li_total_channels; i++) + { + if ((li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT) + && (li_config_table[i].adapter->li_pri + || (i < li_config_table[i].adapter->li_base + MIXER_BCHANNELS_BRI))) + { + d++; + } + } + } + PUT_DWORD(&result_buffer[18], d / 2); + PUT_DWORD(&result_buffer[22], d - 1); + } + break; + + case LI_REQ_CONNECT: + if (li_parms[1].length == 8) + { + appl->appl_flags |= APPL_FLAG_OLD_LI_SPEC; + if (api_parse(&li_parms[1].info[1], li_parms[1].length, "dd", li_req_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + plci_b_id = GET_DWORD(li_req_parms[0].info) & 0xffff; + li_flags = GET_DWORD(li_req_parms[1].info); + Info = li_check_main_plci(Id, plci); + result_buffer[0] = 9; + result_buffer[3] = 6; + PUT_DWORD(&result_buffer[4], plci_b_id); + PUT_WORD(&result_buffer[8], GOOD); + if (Info != GOOD) + break; + result = plci->saved_msg.info; + for (i = 0; i <= result_buffer[0]; i++) + result[i] = result_buffer[i]; + plci_b_write_pos = plci->li_plci_b_write_pos; + plci_b = li_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[8]); + if (plci_b == NULL) + break; + li_update_connect(Id, a, plci, plci_b_id, true, li_flags); + plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_LAST_FLAG; + plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1; + plci->li_plci_b_write_pos = plci_b_write_pos; + } + else + { + appl->appl_flags &= ~APPL_FLAG_OLD_LI_SPEC; + if (api_parse(&li_parms[1].info[1], li_parms[1].length, "ds", li_req_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + li_flags = GET_DWORD(li_req_parms[0].info) & ~(LI2_FLAG_INTERCONNECT_A_B | LI2_FLAG_INTERCONNECT_B_A); + Info = li_check_main_plci(Id, plci); + result_buffer[0] = 7; + result_buffer[3] = 4; + PUT_WORD(&result_buffer[4], Info); + result_buffer[6] = 0; + if (Info != GOOD) + break; + result = plci->saved_msg.info; + for (i = 0; i <= result_buffer[0]; i++) + result[i] = result_buffer[i]; + plci_b_write_pos = plci->li_plci_b_write_pos; + participant_parms_pos = 0; + result_pos = 7; + li2_update_connect(Id, a, plci, UnMapId(Id), true, li_flags); + while (participant_parms_pos < li_req_parms[1].length) + { + result[result_pos] = 6; + result_pos += 7; + PUT_DWORD(&result[result_pos - 6], 0); + PUT_WORD(&result[result_pos - 2], GOOD); + if (api_parse(&li_req_parms[1].info[1 + participant_parms_pos], + (word)(li_parms[1].length - participant_parms_pos), "s", li_participant_struct)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT); + break; + } + if (api_parse(&li_participant_struct[0].info[1], + li_participant_struct[0].length, "dd", li_participant_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT); + break; + } + plci_b_id = GET_DWORD(li_participant_parms[0].info) & 0xffff; + li_flags = GET_DWORD(li_participant_parms[1].info); + PUT_DWORD(&result[result_pos - 6], plci_b_id); + if (sizeof(result) - result_pos < 7) + { + dbug(1, dprintf("[%06lx] %s,%d: LI result overrun", + UnMapId(Id), (char *)(FILE_), __LINE__)); + PUT_WORD(&result[result_pos - 2], _WRONG_STATE); + break; + } + plci_b = li2_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[result_pos - 2]); + if (plci_b != NULL) + { + li2_update_connect(Id, a, plci, plci_b_id, true, li_flags); + plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | + ((li_flags & (LI2_FLAG_INTERCONNECT_A_B | LI2_FLAG_INTERCONNECT_B_A | + LI2_FLAG_PCCONNECT_A_B | LI2_FLAG_PCCONNECT_B_A)) ? 0 : LI_PLCI_B_DISC_FLAG); + plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1; + } + participant_parms_pos = (word)((&li_participant_struct[0].info[1 + li_participant_struct[0].length]) - + (&li_req_parms[1].info[1])); + } + result[0] = (byte)(result_pos - 1); + result[3] = (byte)(result_pos - 4); + result[6] = (byte)(result_pos - 7); + i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES - 1 : plci_b_write_pos - 1; + if ((plci_b_write_pos == plci->li_plci_b_read_pos) + || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG)) + { + plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG; + plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1; + } + else + plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG; + plci->li_plci_b_write_pos = plci_b_write_pos; + } + mixer_calculate_coefs(a); + plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel; + mixer_notify_update(plci, true); + sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number, + "wwS", Info, SELECTOR_LINE_INTERCONNECT, result); + plci->command = 0; + plci->li_cmd = GET_WORD(li_parms[0].info); + start_internal_command(Id, plci, mixer_command); + return (false); + + case LI_REQ_DISCONNECT: + if (li_parms[1].length == 4) + { + appl->appl_flags |= APPL_FLAG_OLD_LI_SPEC; + if (api_parse(&li_parms[1].info[1], li_parms[1].length, "d", li_req_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + plci_b_id = GET_DWORD(li_req_parms[0].info) & 0xffff; + Info = li_check_main_plci(Id, plci); + result_buffer[0] = 9; + result_buffer[3] = 6; + PUT_DWORD(&result_buffer[4], GET_DWORD(li_req_parms[0].info)); + PUT_WORD(&result_buffer[8], GOOD); + if (Info != GOOD) + break; + result = plci->saved_msg.info; + for (i = 0; i <= result_buffer[0]; i++) + result[i] = result_buffer[i]; + plci_b_write_pos = plci->li_plci_b_write_pos; + plci_b = li_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[8]); + if (plci_b == NULL) + break; + li_update_connect(Id, a, plci, plci_b_id, false, 0); + plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG | LI_PLCI_B_LAST_FLAG; + plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1; + plci->li_plci_b_write_pos = plci_b_write_pos; + } + else + { + appl->appl_flags &= ~APPL_FLAG_OLD_LI_SPEC; + if (api_parse(&li_parms[1].info[1], li_parms[1].length, "s", li_req_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_MESSAGE_FORMAT; + break; + } + Info = li_check_main_plci(Id, plci); + result_buffer[0] = 7; + result_buffer[3] = 4; + PUT_WORD(&result_buffer[4], Info); + result_buffer[6] = 0; + if (Info != GOOD) + break; + result = plci->saved_msg.info; + for (i = 0; i <= result_buffer[0]; i++) + result[i] = result_buffer[i]; + plci_b_write_pos = plci->li_plci_b_write_pos; + participant_parms_pos = 0; + result_pos = 7; + while (participant_parms_pos < li_req_parms[0].length) + { + result[result_pos] = 6; + result_pos += 7; + PUT_DWORD(&result[result_pos - 6], 0); + PUT_WORD(&result[result_pos - 2], GOOD); + if (api_parse(&li_req_parms[0].info[1 + participant_parms_pos], + (word)(li_parms[1].length - participant_parms_pos), "s", li_participant_struct)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT); + break; + } + if (api_parse(&li_participant_struct[0].info[1], + li_participant_struct[0].length, "d", li_participant_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT); + break; + } + plci_b_id = GET_DWORD(li_participant_parms[0].info) & 0xffff; + PUT_DWORD(&result[result_pos - 6], plci_b_id); + if (sizeof(result) - result_pos < 7) + { + dbug(1, dprintf("[%06lx] %s,%d: LI result overrun", + UnMapId(Id), (char *)(FILE_), __LINE__)); + PUT_WORD(&result[result_pos - 2], _WRONG_STATE); + break; + } + plci_b = li2_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[result_pos - 2]); + if (plci_b != NULL) + { + li2_update_connect(Id, a, plci, plci_b_id, false, 0); + plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG; + plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1; + } + participant_parms_pos = (word)((&li_participant_struct[0].info[1 + li_participant_struct[0].length]) - + (&li_req_parms[0].info[1])); + } + result[0] = (byte)(result_pos - 1); + result[3] = (byte)(result_pos - 4); + result[6] = (byte)(result_pos - 7); + i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES - 1 : plci_b_write_pos - 1; + if ((plci_b_write_pos == plci->li_plci_b_read_pos) + || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG)) + { + plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG; + plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1; + } + else + plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG; + plci->li_plci_b_write_pos = plci_b_write_pos; + } + mixer_calculate_coefs(a); + plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel; + mixer_notify_update(plci, true); + sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number, + "wwS", Info, SELECTOR_LINE_INTERCONNECT, result); + plci->command = 0; + plci->li_cmd = GET_WORD(li_parms[0].info); + start_internal_command(Id, plci, mixer_command); + return (false); + + case LI_REQ_SILENT_UPDATE: + if (!plci || !plci->State + || !plci->NL.Id || plci->nl_remove_id + || (plci->li_bchannel_id == 0) + || (li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci != plci)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong state", + UnMapId(Id), (char *)(FILE_), __LINE__)); + return (false); + } + plci_b_write_pos = plci->li_plci_b_write_pos; + if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos : + LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2) + { + dbug(1, dprintf("[%06lx] %s,%d: LI request overrun", + UnMapId(Id), (char *)(FILE_), __LINE__)); + return (false); + } + i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES - 1 : plci_b_write_pos - 1; + if ((plci_b_write_pos == plci->li_plci_b_read_pos) + || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG)) + { + plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG; + plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1; + } + else + plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG; + plci->li_plci_b_write_pos = plci_b_write_pos; + plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel; + plci->command = 0; + plci->li_cmd = GET_WORD(li_parms[0].info); + start_internal_command(Id, plci, mixer_command); + return (false); + + default: + dbug(1, dprintf("[%06lx] %s,%d: LI unknown request %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(li_parms[0].info))); + Info = _FACILITY_NOT_SUPPORTED; + } + } + sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number, + "wwS", Info, SELECTOR_LINE_INTERCONNECT, result); + return (false); +} + + +static void mixer_indication_coefs_set(dword Id, PLCI *plci) +{ + dword d; + byte result[12]; + + dbug(1, dprintf("[%06lx] %s,%d: mixer_indication_coefs_set", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + if (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos) + { + do + { + d = plci->li_plci_b_queue[plci->li_plci_b_read_pos]; + if (!(d & LI_PLCI_B_SKIP_FLAG)) + { + if (plci->appl->appl_flags & APPL_FLAG_OLD_LI_SPEC) + { + if (d & LI_PLCI_B_DISC_FLAG) + { + result[0] = 5; + PUT_WORD(&result[1], LI_IND_DISCONNECT); + result[3] = 2; + PUT_WORD(&result[4], _LI_USER_INITIATED); + } + else + { + result[0] = 7; + PUT_WORD(&result[1], LI_IND_CONNECT_ACTIVE); + result[3] = 4; + PUT_DWORD(&result[4], d & ~LI_PLCI_B_FLAG_MASK); + } + } + else + { + if (d & LI_PLCI_B_DISC_FLAG) + { + result[0] = 9; + PUT_WORD(&result[1], LI_IND_DISCONNECT); + result[3] = 6; + PUT_DWORD(&result[4], d & ~LI_PLCI_B_FLAG_MASK); + PUT_WORD(&result[8], _LI_USER_INITIATED); + } + else + { + result[0] = 7; + PUT_WORD(&result[1], LI_IND_CONNECT_ACTIVE); + result[3] = 4; + PUT_DWORD(&result[4], d & ~LI_PLCI_B_FLAG_MASK); + } + } + sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, + "ws", SELECTOR_LINE_INTERCONNECT, result); + } + plci->li_plci_b_read_pos = (plci->li_plci_b_read_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? + 0 : plci->li_plci_b_read_pos + 1; + } while (!(d & LI_PLCI_B_LAST_FLAG) && (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos)); + } +} + + +static void mixer_indication_xconnect_from(dword Id, PLCI *plci, byte *msg, word length) +{ + word i, j, ch; + struct xconnect_transfer_address_s s, *p; + DIVA_CAPI_ADAPTER *a; + + dbug(1, dprintf("[%06lx] %s,%d: mixer_indication_xconnect_from %d", + UnMapId(Id), (char *)(FILE_), __LINE__, (int)length)); + + a = plci->adapter; + i = 1; + for (i = 1; i < length; i += 16) + { + s.card_address.low = msg[i] | (msg[i + 1] << 8) | (((dword)(msg[i + 2])) << 16) | (((dword)(msg[i + 3])) << 24); + s.card_address.high = msg[i + 4] | (msg[i + 5] << 8) | (((dword)(msg[i + 6])) << 16) | (((dword)(msg[i + 7])) << 24); + s.offset = msg[i + 8] | (msg[i + 9] << 8) | (((dword)(msg[i + 10])) << 16) | (((dword)(msg[i + 11])) << 24); + ch = msg[i + 12] | (msg[i + 13] << 8); + j = ch & XCONNECT_CHANNEL_NUMBER_MASK; + if (!a->li_pri && (plci->li_bchannel_id == 2)) + j = 1 - j; + j += a->li_base; + if (ch & XCONNECT_CHANNEL_PORT_PC) + p = &(li_config_table[j].send_pc); + else + p = &(li_config_table[j].send_b); + p->card_address.low = s.card_address.low; + p->card_address.high = s.card_address.high; + p->offset = s.offset; + li_config_table[j].channel |= LI_CHANNEL_ADDRESSES_SET; + } + if (plci->internal_command_queue[0] + && ((plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2) + || (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_3) + || (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_4))) + { + (*(plci->internal_command_queue[0]))(Id, plci, 0); + if (!plci->internal_command) + next_internal_command(Id, plci); + } + mixer_notify_update(plci, true); +} + + +static void mixer_indication_xconnect_to(dword Id, PLCI *plci, byte *msg, word length) +{ + + dbug(1, dprintf("[%06lx] %s,%d: mixer_indication_xconnect_to %d", + UnMapId(Id), (char *)(FILE_), __LINE__, (int) length)); + +} + + +static byte mixer_notify_source_removed(PLCI *plci, dword plci_b_id) +{ + word plci_b_write_pos; + + plci_b_write_pos = plci->li_plci_b_write_pos; + if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos : + LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 1) + { + dbug(1, dprintf("[%06lx] %s,%d: LI request overrun", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + return (false); + } + plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG; + plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1; + plci->li_plci_b_write_pos = plci_b_write_pos; + return (true); +} + + +static void mixer_remove(PLCI *plci) +{ + DIVA_CAPI_ADAPTER *a; + PLCI *notify_plci; + dword plci_b_id; + word i, j; + + dbug(1, dprintf("[%06lx] %s,%d: mixer_remove", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + a = plci->adapter; + plci_b_id = (plci->Id << 8) | UnMapController(plci->adapter->Id); + if (a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED) + { + if ((plci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci)) + { + i = a->li_base + (plci->li_bchannel_id - 1); + if ((li_config_table[i].curchnl | li_config_table[i].channel) & LI_CHANNEL_INVOLVED) + { + for (j = 0; j < li_total_channels; j++) + { + if ((li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT) + || (li_config_table[j].flag_table[i] & LI_FLAG_INTERCONNECT)) + { + notify_plci = li_config_table[j].plci; + if ((notify_plci != NULL) + && (notify_plci != plci) + && (notify_plci->appl != NULL) + && !(notify_plci->appl->appl_flags & APPL_FLAG_OLD_LI_SPEC) + && (notify_plci->State) + && notify_plci->NL.Id && !notify_plci->nl_remove_id) + { + mixer_notify_source_removed(notify_plci, plci_b_id); + } + } + } + mixer_clear_config(plci); + mixer_calculate_coefs(a); + mixer_notify_update(plci, true); + } + li_config_table[i].plci = NULL; + plci->li_bchannel_id = 0; + } + } +} + + +/*------------------------------------------------------------------*/ +/* Echo canceller facilities */ +/*------------------------------------------------------------------*/ + + +static void ec_write_parameters(PLCI *plci) +{ + word w; + byte parameter_buffer[6]; + + dbug(1, dprintf("[%06lx] %s,%d: ec_write_parameters", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + parameter_buffer[0] = 5; + parameter_buffer[1] = DSP_CTRL_SET_LEC_PARAMETERS; + PUT_WORD(¶meter_buffer[2], plci->ec_idi_options); + plci->ec_idi_options &= ~LEC_RESET_COEFFICIENTS; + w = (plci->ec_tail_length == 0) ? 128 : plci->ec_tail_length; + PUT_WORD(¶meter_buffer[4], w); + add_p(plci, FTY, parameter_buffer); + sig_req(plci, TEL_CTRL, 0); + send_req(plci); +} + + +static void ec_clear_config(PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: ec_clear_config", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER | + LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING; + plci->ec_tail_length = 0; +} + + +static void ec_prepare_switch(dword Id, PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: ec_prepare_switch", + UnMapId(Id), (char *)(FILE_), __LINE__)); + +} + + +static word ec_save_config(dword Id, PLCI *plci, byte Rc) +{ + + dbug(1, dprintf("[%06lx] %s,%d: ec_save_config %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + return (GOOD); +} + + +static word ec_restore_config(dword Id, PLCI *plci, byte Rc) +{ + word Info; + + dbug(1, dprintf("[%06lx] %s,%d: ec_restore_config %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + Info = GOOD; + if (plci->B1_facilities & B1_FACILITY_EC) + { + switch (plci->adjust_b_state) + { + case ADJUST_B_RESTORE_EC_1: + plci->internal_command = plci->adjust_b_command; + if (plci->sig_req) + { + plci->adjust_b_state = ADJUST_B_RESTORE_EC_1; + break; + } + ec_write_parameters(plci); + plci->adjust_b_state = ADJUST_B_RESTORE_EC_2; + break; + case ADJUST_B_RESTORE_EC_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Restore EC failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _WRONG_STATE; + break; + } + break; + } + } + return (Info); +} + + +static void ec_command(dword Id, PLCI *plci, byte Rc) +{ + word internal_command, Info; + byte result[8]; + + dbug(1, dprintf("[%06lx] %s,%d: ec_command %02x %04x %04x %04x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command, + plci->ec_cmd, plci->ec_idi_options, plci->ec_tail_length)); + + Info = GOOD; + if (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) + { + result[0] = 2; + PUT_WORD(&result[1], EC_SUCCESS); + } + else + { + result[0] = 5; + PUT_WORD(&result[1], plci->ec_cmd); + result[3] = 2; + PUT_WORD(&result[4], GOOD); + } + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (plci->ec_cmd) + { + case EC_ENABLE_OPERATION: + case EC_FREEZE_COEFFICIENTS: + case EC_RESUME_COEFFICIENT_UPDATE: + case EC_RESET_COEFFICIENTS: + switch (internal_command) + { + default: + adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities | + B1_FACILITY_EC), EC_COMMAND_1); + /* fall through */ + case EC_COMMAND_1: + if (adjust_b_process(Id, plci, Rc) != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Load EC failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } + if (plci->internal_command) + return; + /* fall through */ + case EC_COMMAND_2: + if (plci->sig_req) + { + plci->internal_command = EC_COMMAND_2; + return; + } + plci->internal_command = EC_COMMAND_3; + ec_write_parameters(plci); + return; + case EC_COMMAND_3: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Enable EC failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } + break; + } + break; + + case EC_DISABLE_OPERATION: + switch (internal_command) + { + default: + case EC_COMMAND_1: + if (plci->B1_facilities & B1_FACILITY_EC) + { + if (plci->sig_req) + { + plci->internal_command = EC_COMMAND_1; + return; + } + plci->internal_command = EC_COMMAND_2; + ec_write_parameters(plci); + return; + } + Rc = OK; + /* fall through */ + case EC_COMMAND_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Disable EC failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } + adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities & + ~B1_FACILITY_EC), EC_COMMAND_3); + /* fall through */ + case EC_COMMAND_3: + if (adjust_b_process(Id, plci, Rc) != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Unload EC failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _FACILITY_NOT_SUPPORTED; + break; + } + if (plci->internal_command) + return; + break; + } + break; + } + sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->number, + "wws", Info, (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ? + PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result); +} + + +static byte ec_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg) +{ + word Info; + word opt; + API_PARSE ec_parms[3]; + byte result[16]; + + dbug(1, dprintf("[%06lx] %s,%d: ec_request", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + Info = GOOD; + result[0] = 0; + if (!(a->man_profile.private_options & (1L << PRIVATE_ECHO_CANCELLER))) + { + dbug(1, dprintf("[%06lx] %s,%d: Facility not supported", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _FACILITY_NOT_SUPPORTED; + } + else + { + if (appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) + { + if (api_parse(&msg[1].info[1], msg[1].length, "w", ec_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_MESSAGE_FORMAT; + } + else + { + if (plci == NULL) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_IDENTIFIER; + } + else if (!plci->State || !plci->NL.Id || plci->nl_remove_id) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong state", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_STATE; + } + else + { + plci->command = 0; + plci->ec_cmd = GET_WORD(ec_parms[0].info); + plci->ec_idi_options &= ~(LEC_MANUAL_DISABLE | LEC_RESET_COEFFICIENTS); + result[0] = 2; + PUT_WORD(&result[1], EC_SUCCESS); + if (msg[1].length >= 4) + { + opt = GET_WORD(&ec_parms[0].info[2]); + plci->ec_idi_options &= ~(LEC_ENABLE_NONLINEAR_PROCESSING | + LEC_ENABLE_2100HZ_DETECTOR | LEC_REQUIRE_2100HZ_REVERSALS); + if (!(opt & EC_DISABLE_NON_LINEAR_PROCESSING)) + plci->ec_idi_options |= LEC_ENABLE_NONLINEAR_PROCESSING; + if (opt & EC_DETECT_DISABLE_TONE) + plci->ec_idi_options |= LEC_ENABLE_2100HZ_DETECTOR; + if (!(opt & EC_DO_NOT_REQUIRE_REVERSALS)) + plci->ec_idi_options |= LEC_REQUIRE_2100HZ_REVERSALS; + if (msg[1].length >= 6) + { + plci->ec_tail_length = GET_WORD(&ec_parms[0].info[4]); + } + } + switch (plci->ec_cmd) + { + case EC_ENABLE_OPERATION: + plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS; + start_internal_command(Id, plci, ec_command); + return (false); + + case EC_DISABLE_OPERATION: + plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER | + LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING | + LEC_RESET_COEFFICIENTS; + start_internal_command(Id, plci, ec_command); + return (false); + + case EC_FREEZE_COEFFICIENTS: + plci->ec_idi_options |= LEC_FREEZE_COEFFICIENTS; + start_internal_command(Id, plci, ec_command); + return (false); + + case EC_RESUME_COEFFICIENT_UPDATE: + plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS; + start_internal_command(Id, plci, ec_command); + return (false); + + case EC_RESET_COEFFICIENTS: + plci->ec_idi_options |= LEC_RESET_COEFFICIENTS; + start_internal_command(Id, plci, ec_command); + return (false); + + default: + dbug(1, dprintf("[%06lx] %s,%d: EC unknown request %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, plci->ec_cmd)); + PUT_WORD(&result[1], EC_UNSUPPORTED_OPERATION); + } + } + } + } + else + { + if (api_parse(&msg[1].info[1], msg[1].length, "ws", ec_parms)) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong message format", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_MESSAGE_FORMAT; + } + else + { + if (GET_WORD(ec_parms[0].info) == EC_GET_SUPPORTED_SERVICES) + { + result[0] = 11; + PUT_WORD(&result[1], EC_GET_SUPPORTED_SERVICES); + result[3] = 8; + PUT_WORD(&result[4], GOOD); + PUT_WORD(&result[6], 0x0007); + PUT_WORD(&result[8], LEC_MAX_SUPPORTED_TAIL_LENGTH); + PUT_WORD(&result[10], 0); + } + else if (plci == NULL) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_IDENTIFIER; + } + else if (!plci->State || !plci->NL.Id || plci->nl_remove_id) + { + dbug(1, dprintf("[%06lx] %s,%d: Wrong state", + UnMapId(Id), (char *)(FILE_), __LINE__)); + Info = _WRONG_STATE; + } + else + { + plci->command = 0; + plci->ec_cmd = GET_WORD(ec_parms[0].info); + plci->ec_idi_options &= ~(LEC_MANUAL_DISABLE | LEC_RESET_COEFFICIENTS); + result[0] = 5; + PUT_WORD(&result[1], plci->ec_cmd); + result[3] = 2; + PUT_WORD(&result[4], GOOD); + plci->ec_idi_options &= ~(LEC_ENABLE_NONLINEAR_PROCESSING | + LEC_ENABLE_2100HZ_DETECTOR | LEC_REQUIRE_2100HZ_REVERSALS); + plci->ec_tail_length = 0; + if (ec_parms[1].length >= 2) + { + opt = GET_WORD(&ec_parms[1].info[1]); + if (opt & EC_ENABLE_NON_LINEAR_PROCESSING) + plci->ec_idi_options |= LEC_ENABLE_NONLINEAR_PROCESSING; + if (opt & EC_DETECT_DISABLE_TONE) + plci->ec_idi_options |= LEC_ENABLE_2100HZ_DETECTOR; + if (!(opt & EC_DO_NOT_REQUIRE_REVERSALS)) + plci->ec_idi_options |= LEC_REQUIRE_2100HZ_REVERSALS; + if (ec_parms[1].length >= 4) + { + plci->ec_tail_length = GET_WORD(&ec_parms[1].info[3]); + } + } + switch (plci->ec_cmd) + { + case EC_ENABLE_OPERATION: + plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS; + start_internal_command(Id, plci, ec_command); + return (false); + + case EC_DISABLE_OPERATION: + plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER | + LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING | + LEC_RESET_COEFFICIENTS; + start_internal_command(Id, plci, ec_command); + return (false); + + default: + dbug(1, dprintf("[%06lx] %s,%d: EC unknown request %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, plci->ec_cmd)); + PUT_WORD(&result[4], _FACILITY_SPECIFIC_FUNCTION_NOT_SUPP); + } + } + } + } + } + sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number, + "wws", Info, (appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ? + PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result); + return (false); +} + + +static void ec_indication(dword Id, PLCI *plci, byte *msg, word length) +{ + byte result[8]; + + dbug(1, dprintf("[%06lx] %s,%d: ec_indication", + UnMapId(Id), (char *)(FILE_), __LINE__)); + + if (!(plci->ec_idi_options & LEC_MANUAL_DISABLE)) + { + if (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) + { + result[0] = 2; + PUT_WORD(&result[1], 0); + switch (msg[1]) + { + case LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ: + PUT_WORD(&result[1], EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ); + break; + case LEC_DISABLE_TYPE_REVERSED_2100HZ: + PUT_WORD(&result[1], EC_BYPASS_DUE_TO_REVERSED_2100HZ); + break; + case LEC_DISABLE_RELEASED: + PUT_WORD(&result[1], EC_BYPASS_RELEASED); + break; + } + } + else + { + result[0] = 5; + PUT_WORD(&result[1], EC_BYPASS_INDICATION); + result[3] = 2; + PUT_WORD(&result[4], 0); + switch (msg[1]) + { + case LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ: + PUT_WORD(&result[4], EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ); + break; + case LEC_DISABLE_TYPE_REVERSED_2100HZ: + PUT_WORD(&result[4], EC_BYPASS_DUE_TO_REVERSED_2100HZ); + break; + case LEC_DISABLE_RELEASED: + PUT_WORD(&result[4], EC_BYPASS_RELEASED); + break; + } + } + sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ? + PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result); + } +} + + + +/*------------------------------------------------------------------*/ +/* Advanced voice */ +/*------------------------------------------------------------------*/ + +static void adv_voice_write_coefs(PLCI *plci, word write_command) +{ + DIVA_CAPI_ADAPTER *a; + word i; + byte *p; + + word w, n, j, k; + byte ch_map[MIXER_CHANNELS_BRI]; + + byte coef_buffer[ADV_VOICE_COEF_BUFFER_SIZE + 2]; + + dbug(1, dprintf("[%06lx] %s,%d: adv_voice_write_coefs %d", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, write_command)); + + a = plci->adapter; + p = coef_buffer + 1; + *(p++) = DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS; + i = 0; + while (i + sizeof(word) <= a->adv_voice_coef_length) + { + PUT_WORD(p, GET_WORD(a->adv_voice_coef_buffer + i)); + p += 2; + i += 2; + } + while (i < ADV_VOICE_OLD_COEF_COUNT * sizeof(word)) + { + PUT_WORD(p, 0x8000); + p += 2; + i += 2; + } + + if (!a->li_pri && (plci->li_bchannel_id == 0)) + { + if ((li_config_table[a->li_base].plci == NULL) && (li_config_table[a->li_base + 1].plci != NULL)) + { + plci->li_bchannel_id = 1; + li_config_table[a->li_base].plci = plci; + dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id %d", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, plci->li_bchannel_id)); + } + else if ((li_config_table[a->li_base].plci != NULL) && (li_config_table[a->li_base + 1].plci == NULL)) + { + plci->li_bchannel_id = 2; + li_config_table[a->li_base + 1].plci = plci; + dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id %d", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, plci->li_bchannel_id)); + } + } + if (!a->li_pri && (plci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci)) + { + i = a->li_base + (plci->li_bchannel_id - 1); + switch (write_command) + { + case ADV_VOICE_WRITE_ACTIVATION: + j = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1); + k = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id); + if (!(plci->B1_facilities & B1_FACILITY_MIXER)) + { + li_config_table[j].flag_table[i] |= LI_FLAG_CONFERENCE | LI_FLAG_MIX; + li_config_table[i].flag_table[j] |= LI_FLAG_CONFERENCE | LI_FLAG_MONITOR; + } + if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) + { + li_config_table[k].flag_table[i] |= LI_FLAG_CONFERENCE | LI_FLAG_MIX; + li_config_table[i].flag_table[k] |= LI_FLAG_CONFERENCE | LI_FLAG_MONITOR; + li_config_table[k].flag_table[j] |= LI_FLAG_CONFERENCE; + li_config_table[j].flag_table[k] |= LI_FLAG_CONFERENCE; + } + mixer_calculate_coefs(a); + li_config_table[i].curchnl = li_config_table[i].channel; + li_config_table[j].curchnl = li_config_table[j].channel; + if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) + li_config_table[k].curchnl = li_config_table[k].channel; + break; + + case ADV_VOICE_WRITE_DEACTIVATION: + for (j = 0; j < li_total_channels; j++) + { + li_config_table[i].flag_table[j] = 0; + li_config_table[j].flag_table[i] = 0; + } + k = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1); + for (j = 0; j < li_total_channels; j++) + { + li_config_table[k].flag_table[j] = 0; + li_config_table[j].flag_table[k] = 0; + } + if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) + { + k = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id); + for (j = 0; j < li_total_channels; j++) + { + li_config_table[k].flag_table[j] = 0; + li_config_table[j].flag_table[k] = 0; + } + } + mixer_calculate_coefs(a); + break; + } + if (plci->B1_facilities & B1_FACILITY_MIXER) + { + w = 0; + if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length) + w = GET_WORD(a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE); + if (li_config_table[i].channel & LI_CHANNEL_TX_DATA) + w |= MIXER_FEATURE_ENABLE_TX_DATA; + if (li_config_table[i].channel & LI_CHANNEL_RX_DATA) + w |= MIXER_FEATURE_ENABLE_RX_DATA; + *(p++) = (byte) w; + *(p++) = (byte)(w >> 8); + for (j = 0; j < sizeof(ch_map); j += 2) + { + ch_map[j] = (byte)(j + (plci->li_bchannel_id - 1)); + ch_map[j + 1] = (byte)(j + (2 - plci->li_bchannel_id)); + } + for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++) + { + i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch]; + j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch]; + if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED) + { + *(p++) = ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01); + w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4)); + li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4; + } + else + { + *(p++) = (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + n < a->adv_voice_coef_length) ? + a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + n] : 0x00; + } + } + } + else + { + for (i = ADV_VOICE_NEW_COEF_BASE; i < a->adv_voice_coef_length; i++) + *(p++) = a->adv_voice_coef_buffer[i]; + } + } + else + + { + for (i = ADV_VOICE_NEW_COEF_BASE; i < a->adv_voice_coef_length; i++) + *(p++) = a->adv_voice_coef_buffer[i]; + } + coef_buffer[0] = (p - coef_buffer) - 1; + add_p(plci, FTY, coef_buffer); + sig_req(plci, TEL_CTRL, 0); + send_req(plci); +} + + +static void adv_voice_clear_config(PLCI *plci) +{ + DIVA_CAPI_ADAPTER *a; + + word i, j; + + + dbug(1, dprintf("[%06lx] %s,%d: adv_voice_clear_config", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + a = plci->adapter; + if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)) + { + a->adv_voice_coef_length = 0; + + if (!a->li_pri && (plci->li_bchannel_id != 0) + && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci)) + { + i = a->li_base + (plci->li_bchannel_id - 1); + li_config_table[i].curchnl = 0; + li_config_table[i].channel = 0; + li_config_table[i].chflags = 0; + for (j = 0; j < li_total_channels; j++) + { + li_config_table[i].flag_table[j] = 0; + li_config_table[j].flag_table[i] = 0; + li_config_table[i].coef_table[j] = 0; + li_config_table[j].coef_table[i] = 0; + } + li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET; + i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1); + li_config_table[i].curchnl = 0; + li_config_table[i].channel = 0; + li_config_table[i].chflags = 0; + for (j = 0; j < li_total_channels; j++) + { + li_config_table[i].flag_table[j] = 0; + li_config_table[j].flag_table[i] = 0; + li_config_table[i].coef_table[j] = 0; + li_config_table[j].coef_table[i] = 0; + } + if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) + { + i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id); + li_config_table[i].curchnl = 0; + li_config_table[i].channel = 0; + li_config_table[i].chflags = 0; + for (j = 0; j < li_total_channels; j++) + { + li_config_table[i].flag_table[j] = 0; + li_config_table[j].flag_table[i] = 0; + li_config_table[i].coef_table[j] = 0; + li_config_table[j].coef_table[i] = 0; + } + } + } + + } +} + + +static void adv_voice_prepare_switch(dword Id, PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: adv_voice_prepare_switch", + UnMapId(Id), (char *)(FILE_), __LINE__)); + +} + + +static word adv_voice_save_config(dword Id, PLCI *plci, byte Rc) +{ + + dbug(1, dprintf("[%06lx] %s,%d: adv_voice_save_config %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + return (GOOD); +} + + +static word adv_voice_restore_config(dword Id, PLCI *plci, byte Rc) +{ + DIVA_CAPI_ADAPTER *a; + word Info; + + dbug(1, dprintf("[%06lx] %s,%d: adv_voice_restore_config %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + Info = GOOD; + a = plci->adapter; + if ((plci->B1_facilities & B1_FACILITY_VOICE) + && (plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)) + { + switch (plci->adjust_b_state) + { + case ADJUST_B_RESTORE_VOICE_1: + plci->internal_command = plci->adjust_b_command; + if (plci->sig_req) + { + plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_1; + break; + } + adv_voice_write_coefs(plci, ADV_VOICE_WRITE_UPDATE); + plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_2; + break; + case ADJUST_B_RESTORE_VOICE_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Restore voice config failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _WRONG_STATE; + break; + } + break; + } + } + return (Info); +} + + + + +/*------------------------------------------------------------------*/ +/* B1 resource switching */ +/*------------------------------------------------------------------*/ + +static byte b1_facilities_table[] = +{ + 0x00, /* 0 No bchannel resources */ + 0x00, /* 1 Codec (automatic law) */ + 0x00, /* 2 Codec (A-law) */ + 0x00, /* 3 Codec (y-law) */ + 0x00, /* 4 HDLC for X.21 */ + 0x00, /* 5 HDLC */ + 0x00, /* 6 External Device 0 */ + 0x00, /* 7 External Device 1 */ + 0x00, /* 8 HDLC 56k */ + 0x00, /* 9 Transparent */ + 0x00, /* 10 Loopback to network */ + 0x00, /* 11 Test pattern to net */ + 0x00, /* 12 Rate adaptation sync */ + 0x00, /* 13 Rate adaptation async */ + 0x00, /* 14 R-Interface */ + 0x00, /* 15 HDLC 128k leased line */ + 0x00, /* 16 FAX */ + 0x00, /* 17 Modem async */ + 0x00, /* 18 Modem sync HDLC */ + 0x00, /* 19 V.110 async HDLC */ + 0x12, /* 20 Adv voice (Trans,mixer) */ + 0x00, /* 21 Codec connected to IC */ + 0x0c, /* 22 Trans,DTMF */ + 0x1e, /* 23 Trans,DTMF+mixer */ + 0x1f, /* 24 Trans,DTMF+mixer+local */ + 0x13, /* 25 Trans,mixer+local */ + 0x12, /* 26 HDLC,mixer */ + 0x12, /* 27 HDLC 56k,mixer */ + 0x2c, /* 28 Trans,LEC+DTMF */ + 0x3e, /* 29 Trans,LEC+DTMF+mixer */ + 0x3f, /* 30 Trans,LEC+DTMF+mixer+local */ + 0x2c, /* 31 RTP,LEC+DTMF */ + 0x3e, /* 32 RTP,LEC+DTMF+mixer */ + 0x3f, /* 33 RTP,LEC+DTMF+mixer+local */ + 0x00, /* 34 Signaling task */ + 0x00, /* 35 PIAFS */ + 0x0c, /* 36 Trans,DTMF+TONE */ + 0x1e, /* 37 Trans,DTMF+TONE+mixer */ + 0x1f /* 38 Trans,DTMF+TONE+mixer+local*/ +}; + + +static word get_b1_facilities(PLCI *plci, byte b1_resource) +{ + word b1_facilities; + + b1_facilities = b1_facilities_table[b1_resource]; + if ((b1_resource == 9) || (b1_resource == 20) || (b1_resource == 25)) + { + + if (!(((plci->requested_options_conn | plci->requested_options) & (1L << PRIVATE_DTMF_TONE)) + || (plci->appl && (plci->adapter->requested_options_table[plci->appl->Id - 1] & (1L << PRIVATE_DTMF_TONE))))) + + { + if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_SEND) + b1_facilities |= B1_FACILITY_DTMFX; + if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE) + b1_facilities |= B1_FACILITY_DTMFR; + } + } + if ((b1_resource == 17) || (b1_resource == 18)) + { + if (plci->adapter->manufacturer_features & (MANUFACTURER_FEATURE_V18 | MANUFACTURER_FEATURE_VOWN)) + b1_facilities |= B1_FACILITY_DTMFX | B1_FACILITY_DTMFR; + } +/* + dbug (1, dprintf("[%06lx] %s,%d: get_b1_facilities %d %04x", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char far *)(FILE_), __LINE__, b1_resource, b1_facilites)); +*/ + return (b1_facilities); +} + + +static byte add_b1_facilities(PLCI *plci, byte b1_resource, word b1_facilities) +{ + byte b; + + switch (b1_resource) + { + case 5: + case 26: + if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE)) + b = 26; + else + b = 5; + break; + + case 8: + case 27: + if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE)) + b = 27; + else + b = 8; + break; + + case 9: + case 20: + case 22: + case 23: + case 24: + case 25: + case 28: + case 29: + case 30: + case 36: + case 37: + case 38: + if (b1_facilities & B1_FACILITY_EC) + { + if (b1_facilities & B1_FACILITY_LOCAL) + b = 30; + else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE)) + b = 29; + else + b = 28; + } + + else if ((b1_facilities & (B1_FACILITY_DTMFX | B1_FACILITY_DTMFR | B1_FACILITY_MIXER)) + && (((plci->requested_options_conn | plci->requested_options) & (1L << PRIVATE_DTMF_TONE)) + || (plci->appl && (plci->adapter->requested_options_table[plci->appl->Id - 1] & (1L << PRIVATE_DTMF_TONE))))) + { + if (b1_facilities & B1_FACILITY_LOCAL) + b = 38; + else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE)) + b = 37; + else + b = 36; + } + + else if (((plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_HARDDTMF) + && !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)) + || ((b1_facilities & B1_FACILITY_DTMFR) + && ((b1_facilities & B1_FACILITY_MIXER) + || !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))) + || ((b1_facilities & B1_FACILITY_DTMFX) + && ((b1_facilities & B1_FACILITY_MIXER) + || !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_SEND)))) + { + if (b1_facilities & B1_FACILITY_LOCAL) + b = 24; + else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE)) + b = 23; + else + b = 22; + } + else + { + if (b1_facilities & B1_FACILITY_LOCAL) + b = 25; + else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE)) + b = 20; + else + b = 9; + } + break; + + case 31: + case 32: + case 33: + if (b1_facilities & B1_FACILITY_LOCAL) + b = 33; + else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE)) + b = 32; + else + b = 31; + break; + + default: + b = b1_resource; + } + dbug(1, dprintf("[%06lx] %s,%d: add_b1_facilities %d %04x %d %04x", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, + b1_resource, b1_facilities, b, get_b1_facilities(plci, b))); + return (b); +} + + +static void adjust_b1_facilities(PLCI *plci, byte new_b1_resource, word new_b1_facilities) +{ + word removed_facilities; + + dbug(1, dprintf("[%06lx] %s,%d: adjust_b1_facilities %d %04x %04x", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__, new_b1_resource, new_b1_facilities, + new_b1_facilities & get_b1_facilities(plci, new_b1_resource))); + + new_b1_facilities &= get_b1_facilities(plci, new_b1_resource); + removed_facilities = plci->B1_facilities & ~new_b1_facilities; + + if (removed_facilities & B1_FACILITY_EC) + ec_clear_config(plci); + + + if (removed_facilities & B1_FACILITY_DTMFR) + { + dtmf_rec_clear_config(plci); + dtmf_parameter_clear_config(plci); + } + if (removed_facilities & B1_FACILITY_DTMFX) + dtmf_send_clear_config(plci); + + + if (removed_facilities & B1_FACILITY_MIXER) + mixer_clear_config(plci); + + if (removed_facilities & B1_FACILITY_VOICE) + adv_voice_clear_config(plci); + plci->B1_facilities = new_b1_facilities; +} + + +static void adjust_b_clear(PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: adjust_b_clear", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + plci->adjust_b_restore = false; +} + + +static word adjust_b_process(dword Id, PLCI *plci, byte Rc) +{ + word Info; + byte b1_resource; + NCCI *ncci_ptr; + API_PARSE bp[2]; + + dbug(1, dprintf("[%06lx] %s,%d: adjust_b_process %02x %d", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state)); + + Info = GOOD; + switch (plci->adjust_b_state) + { + case ADJUST_B_START: + if ((plci->adjust_b_parms_msg == NULL) + && (plci->adjust_b_mode & ADJUST_B_MODE_SWITCH_L1) + && ((plci->adjust_b_mode & ~(ADJUST_B_MODE_SAVE | ADJUST_B_MODE_SWITCH_L1 | + ADJUST_B_MODE_NO_RESOURCE | ADJUST_B_MODE_RESTORE)) == 0)) + { + b1_resource = (plci->adjust_b_mode == ADJUST_B_MODE_NO_RESOURCE) ? + 0 : add_b1_facilities(plci, plci->B1_resource, plci->adjust_b_facilities); + if (b1_resource == plci->B1_resource) + { + adjust_b1_facilities(plci, b1_resource, plci->adjust_b_facilities); + break; + } + if (plci->adjust_b_facilities & ~get_b1_facilities(plci, b1_resource)) + { + dbug(1, dprintf("[%06lx] %s,%d: Adjust B nonsupported facilities %d %d %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, + plci->B1_resource, b1_resource, plci->adjust_b_facilities)); + Info = _WRONG_STATE; + break; + } + } + if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE) + { + + mixer_prepare_switch(Id, plci); + + + dtmf_prepare_switch(Id, plci); + dtmf_parameter_prepare_switch(Id, plci); + + + ec_prepare_switch(Id, plci); + + adv_voice_prepare_switch(Id, plci); + } + plci->adjust_b_state = ADJUST_B_SAVE_MIXER_1; + Rc = OK; + /* fall through */ + case ADJUST_B_SAVE_MIXER_1: + if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE) + { + + Info = mixer_save_config(Id, plci, Rc); + if ((Info != GOOD) || plci->internal_command) + break; + + } + plci->adjust_b_state = ADJUST_B_SAVE_DTMF_1; + Rc = OK; + /* fall through */ + case ADJUST_B_SAVE_DTMF_1: + if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE) + { + + Info = dtmf_save_config(Id, plci, Rc); + if ((Info != GOOD) || plci->internal_command) + break; + + } + plci->adjust_b_state = ADJUST_B_REMOVE_L23_1; + /* fall through */ + case ADJUST_B_REMOVE_L23_1: + if ((plci->adjust_b_mode & ADJUST_B_MODE_REMOVE_L23) + && plci->NL.Id && !plci->nl_remove_id) + { + plci->internal_command = plci->adjust_b_command; + if (plci->adjust_b_ncci != 0) + { + ncci_ptr = &(plci->adapter->ncci[plci->adjust_b_ncci]); + while (ncci_ptr->data_pending) + { + plci->data_sent_ptr = ncci_ptr->DBuffer[ncci_ptr->data_out].P; + data_rc(plci, plci->adapter->ncci_ch[plci->adjust_b_ncci]); + } + while (ncci_ptr->data_ack_pending) + data_ack(plci, plci->adapter->ncci_ch[plci->adjust_b_ncci]); + } + nl_req_ncci(plci, REMOVE, + (byte)((plci->adjust_b_mode & ADJUST_B_MODE_CONNECT) ? plci->adjust_b_ncci : 0)); + send_req(plci); + plci->adjust_b_state = ADJUST_B_REMOVE_L23_2; + break; + } + plci->adjust_b_state = ADJUST_B_REMOVE_L23_2; + Rc = OK; + /* fall through */ + case ADJUST_B_REMOVE_L23_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Adjust B remove failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _WRONG_STATE; + break; + } + if (plci->adjust_b_mode & ADJUST_B_MODE_REMOVE_L23) + { + if (plci_nl_busy(plci)) + { + plci->internal_command = plci->adjust_b_command; + break; + } + } + plci->adjust_b_state = ADJUST_B_SAVE_EC_1; + Rc = OK; + /* fall through */ + case ADJUST_B_SAVE_EC_1: + if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE) + { + + Info = ec_save_config(Id, plci, Rc); + if ((Info != GOOD) || plci->internal_command) + break; + + } + plci->adjust_b_state = ADJUST_B_SAVE_DTMF_PARAMETER_1; + Rc = OK; + /* fall through */ + case ADJUST_B_SAVE_DTMF_PARAMETER_1: + if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE) + { + + Info = dtmf_parameter_save_config(Id, plci, Rc); + if ((Info != GOOD) || plci->internal_command) + break; + + } + plci->adjust_b_state = ADJUST_B_SAVE_VOICE_1; + Rc = OK; + /* fall through */ + case ADJUST_B_SAVE_VOICE_1: + if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE) + { + Info = adv_voice_save_config(Id, plci, Rc); + if ((Info != GOOD) || plci->internal_command) + break; + } + plci->adjust_b_state = ADJUST_B_SWITCH_L1_1; + /* fall through */ + case ADJUST_B_SWITCH_L1_1: + if (plci->adjust_b_mode & ADJUST_B_MODE_SWITCH_L1) + { + if (plci->sig_req) + { + plci->internal_command = plci->adjust_b_command; + break; + } + if (plci->adjust_b_parms_msg != NULL) + api_load_msg(plci->adjust_b_parms_msg, bp); + else + api_load_msg(&plci->B_protocol, bp); + Info = add_b1(plci, bp, + (word)((plci->adjust_b_mode & ADJUST_B_MODE_NO_RESOURCE) ? 2 : 0), + plci->adjust_b_facilities); + if (Info != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Adjust B invalid L1 parameters %d %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, + plci->B1_resource, plci->adjust_b_facilities)); + break; + } + plci->internal_command = plci->adjust_b_command; + sig_req(plci, RESOURCES, 0); + send_req(plci); + plci->adjust_b_state = ADJUST_B_SWITCH_L1_2; + break; + } + plci->adjust_b_state = ADJUST_B_SWITCH_L1_2; + Rc = OK; + /* fall through */ + case ADJUST_B_SWITCH_L1_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Adjust B switch failed %02x %d %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, + Rc, plci->B1_resource, plci->adjust_b_facilities)); + Info = _WRONG_STATE; + break; + } + plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_1; + Rc = OK; + /* fall through */ + case ADJUST_B_RESTORE_VOICE_1: + case ADJUST_B_RESTORE_VOICE_2: + if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE) + { + Info = adv_voice_restore_config(Id, plci, Rc); + if ((Info != GOOD) || plci->internal_command) + break; + } + plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_1; + Rc = OK; + /* fall through */ + case ADJUST_B_RESTORE_DTMF_PARAMETER_1: + case ADJUST_B_RESTORE_DTMF_PARAMETER_2: + if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE) + { + + Info = dtmf_parameter_restore_config(Id, plci, Rc); + if ((Info != GOOD) || plci->internal_command) + break; + + } + plci->adjust_b_state = ADJUST_B_RESTORE_EC_1; + Rc = OK; + /* fall through */ + case ADJUST_B_RESTORE_EC_1: + case ADJUST_B_RESTORE_EC_2: + if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE) + { + + Info = ec_restore_config(Id, plci, Rc); + if ((Info != GOOD) || plci->internal_command) + break; + + } + plci->adjust_b_state = ADJUST_B_ASSIGN_L23_1; + /* fall through */ + case ADJUST_B_ASSIGN_L23_1: + if (plci->adjust_b_mode & ADJUST_B_MODE_ASSIGN_L23) + { + if (plci_nl_busy(plci)) + { + plci->internal_command = plci->adjust_b_command; + break; + } + if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT) + plci->call_dir |= CALL_DIR_FORCE_OUTG_NL; + if (plci->adjust_b_parms_msg != NULL) + api_load_msg(plci->adjust_b_parms_msg, bp); + else + api_load_msg(&plci->B_protocol, bp); + Info = add_b23(plci, bp); + if (Info != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Adjust B invalid L23 parameters %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Info)); + break; + } + plci->internal_command = plci->adjust_b_command; + nl_req_ncci(plci, ASSIGN, 0); + send_req(plci); + plci->adjust_b_state = ADJUST_B_ASSIGN_L23_2; + break; + } + plci->adjust_b_state = ADJUST_B_ASSIGN_L23_2; + Rc = ASSIGN_OK; + /* fall through */ + case ADJUST_B_ASSIGN_L23_2: + if ((Rc != OK) && (Rc != OK_FC) && (Rc != ASSIGN_OK)) + { + dbug(1, dprintf("[%06lx] %s,%d: Adjust B assign failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _WRONG_STATE; + break; + } + if (plci->adjust_b_mode & ADJUST_B_MODE_ASSIGN_L23) + { + if (Rc != ASSIGN_OK) + { + plci->internal_command = plci->adjust_b_command; + break; + } + } + if (plci->adjust_b_mode & ADJUST_B_MODE_USER_CONNECT) + { + plci->adjust_b_restore = true; + break; + } + plci->adjust_b_state = ADJUST_B_CONNECT_1; + /* fall through */ + case ADJUST_B_CONNECT_1: + if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT) + { + plci->internal_command = plci->adjust_b_command; + if (plci_nl_busy(plci)) + break; + nl_req_ncci(plci, N_CONNECT, 0); + send_req(plci); + plci->adjust_b_state = ADJUST_B_CONNECT_2; + break; + } + plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1; + Rc = OK; + /* fall through */ + case ADJUST_B_CONNECT_2: + case ADJUST_B_CONNECT_3: + case ADJUST_B_CONNECT_4: + if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0)) + { + dbug(1, dprintf("[%06lx] %s,%d: Adjust B connect failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _WRONG_STATE; + break; + } + if (Rc == OK) + { + if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT) + { + get_ncci(plci, (byte)(Id >> 16), plci->adjust_b_ncci); + Id = (Id & 0xffff) | (((dword)(plci->adjust_b_ncci)) << 16); + } + if (plci->adjust_b_state == ADJUST_B_CONNECT_2) + plci->adjust_b_state = ADJUST_B_CONNECT_3; + else if (plci->adjust_b_state == ADJUST_B_CONNECT_4) + plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1; + } + else if (Rc == 0) + { + if (plci->adjust_b_state == ADJUST_B_CONNECT_2) + plci->adjust_b_state = ADJUST_B_CONNECT_4; + else if (plci->adjust_b_state == ADJUST_B_CONNECT_3) + plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1; + } + if (plci->adjust_b_state != ADJUST_B_RESTORE_DTMF_1) + { + plci->internal_command = plci->adjust_b_command; + break; + } + Rc = OK; + /* fall through */ + case ADJUST_B_RESTORE_DTMF_1: + case ADJUST_B_RESTORE_DTMF_2: + if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE) + { + + Info = dtmf_restore_config(Id, plci, Rc); + if ((Info != GOOD) || plci->internal_command) + break; + + } + plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_1; + Rc = OK; + /* fall through */ + case ADJUST_B_RESTORE_MIXER_1: + case ADJUST_B_RESTORE_MIXER_2: + case ADJUST_B_RESTORE_MIXER_3: + case ADJUST_B_RESTORE_MIXER_4: + case ADJUST_B_RESTORE_MIXER_5: + case ADJUST_B_RESTORE_MIXER_6: + case ADJUST_B_RESTORE_MIXER_7: + if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE) + { + + Info = mixer_restore_config(Id, plci, Rc); + if ((Info != GOOD) || plci->internal_command) + break; + + } + plci->adjust_b_state = ADJUST_B_END; + case ADJUST_B_END: + break; + } + return (Info); +} + + +static void adjust_b1_resource(dword Id, PLCI *plci, API_SAVE *bp_msg, word b1_facilities, word internal_command) +{ + + dbug(1, dprintf("[%06lx] %s,%d: adjust_b1_resource %d %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, + plci->B1_resource, b1_facilities)); + + plci->adjust_b_parms_msg = bp_msg; + plci->adjust_b_facilities = b1_facilities; + plci->adjust_b_command = internal_command; + plci->adjust_b_ncci = (word)(Id >> 16); + if ((bp_msg == NULL) && (plci->B1_resource == 0)) + plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_NO_RESOURCE | ADJUST_B_MODE_SWITCH_L1; + else + plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_SWITCH_L1 | ADJUST_B_MODE_RESTORE; + plci->adjust_b_state = ADJUST_B_START; + dbug(1, dprintf("[%06lx] %s,%d: Adjust B1 resource %d %04x...", + UnMapId(Id), (char *)(FILE_), __LINE__, + plci->B1_resource, b1_facilities)); +} + + +static void adjust_b_restore(dword Id, PLCI *plci, byte Rc) +{ + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: adjust_b_restore %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; + if (plci->req_in != 0) + { + plci->internal_command = ADJUST_B_RESTORE_1; + break; + } + Rc = OK; + /* fall through */ + case ADJUST_B_RESTORE_1: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: Adjust B enqueued failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + } + plci->adjust_b_parms_msg = NULL; + plci->adjust_b_facilities = plci->B1_facilities; + plci->adjust_b_command = ADJUST_B_RESTORE_2; + plci->adjust_b_ncci = (word)(Id >> 16); + plci->adjust_b_mode = ADJUST_B_MODE_RESTORE; + plci->adjust_b_state = ADJUST_B_START; + dbug(1, dprintf("[%06lx] %s,%d: Adjust B restore...", + UnMapId(Id), (char *)(FILE_), __LINE__)); + /* fall through */ + case ADJUST_B_RESTORE_2: + if (adjust_b_process(Id, plci, Rc) != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Adjust B restore failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + } + if (plci->internal_command) + break; + break; + } +} + + +static void reset_b3_command(dword Id, PLCI *plci, byte Rc) +{ + word Info; + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: reset_b3_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + Info = GOOD; + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; + plci->adjust_b_parms_msg = NULL; + plci->adjust_b_facilities = plci->B1_facilities; + plci->adjust_b_command = RESET_B3_COMMAND_1; + plci->adjust_b_ncci = (word)(Id >> 16); + plci->adjust_b_mode = ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_CONNECT; + plci->adjust_b_state = ADJUST_B_START; + dbug(1, dprintf("[%06lx] %s,%d: Reset B3...", + UnMapId(Id), (char *)(FILE_), __LINE__)); + /* fall through */ + case RESET_B3_COMMAND_1: + Info = adjust_b_process(Id, plci, Rc); + if (Info != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Reset failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + break; + } + if (plci->internal_command) + return; + break; + } +/* sendf (plci->appl, _RESET_B3_R | CONFIRM, Id, plci->number, "w", Info);*/ + sendf(plci->appl, _RESET_B3_I, Id, 0, "s", ""); +} + + +static void select_b_command(dword Id, PLCI *plci, byte Rc) +{ + word Info; + word internal_command; + byte esc_chi[3]; + + dbug(1, dprintf("[%06lx] %s,%d: select_b_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + Info = GOOD; + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; + plci->adjust_b_parms_msg = &plci->saved_msg; + if ((plci->tel == ADV_VOICE) && (plci == plci->adapter->AdvSignalPLCI)) + plci->adjust_b_facilities = plci->B1_facilities | B1_FACILITY_VOICE; + else + plci->adjust_b_facilities = plci->B1_facilities & ~B1_FACILITY_VOICE; + plci->adjust_b_command = SELECT_B_COMMAND_1; + plci->adjust_b_ncci = (word)(Id >> 16); + if (plci->saved_msg.parms[0].length == 0) + { + plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_SWITCH_L1 | + ADJUST_B_MODE_NO_RESOURCE; + } + else + { + plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_SWITCH_L1 | + ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_USER_CONNECT | ADJUST_B_MODE_RESTORE; + } + plci->adjust_b_state = ADJUST_B_START; + dbug(1, dprintf("[%06lx] %s,%d: Select B protocol...", + UnMapId(Id), (char *)(FILE_), __LINE__)); + /* fall through */ + case SELECT_B_COMMAND_1: + Info = adjust_b_process(Id, plci, Rc); + if (Info != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: Select B protocol failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + break; + } + if (plci->internal_command) + return; + if (plci->tel == ADV_VOICE) + { + esc_chi[0] = 0x02; + esc_chi[1] = 0x18; + esc_chi[2] = plci->b_channel; + SetVoiceChannel(plci->adapter->AdvCodecPLCI, esc_chi, plci->adapter); + } + break; + } + sendf(plci->appl, _SELECT_B_REQ | CONFIRM, Id, plci->number, "w", Info); +} + + +static void fax_connect_ack_command(dword Id, PLCI *plci, byte Rc) +{ + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: fax_connect_ack_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; /* fall through */ + case FAX_CONNECT_ACK_COMMAND_1: + if (plci_nl_busy(plci)) + { + plci->internal_command = FAX_CONNECT_ACK_COMMAND_1; + return; + } + plci->internal_command = FAX_CONNECT_ACK_COMMAND_2; + plci->NData[0].P = plci->fax_connect_info_buffer; + plci->NData[0].PLength = plci->fax_connect_info_length; + plci->NL.X = plci->NData; + plci->NL.ReqCh = 0; + plci->NL.Req = plci->nl_req = (byte) N_CONNECT_ACK; + plci->adapter->request(&plci->NL); + return; + case FAX_CONNECT_ACK_COMMAND_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: FAX issue CONNECT ACK failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + break; + } + } + if ((plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT) + && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT)) + { + if (plci->B3_prot == 4) + sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", ""); + else + sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer); + plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT; + } +} + + +static void fax_edata_ack_command(dword Id, PLCI *plci, byte Rc) +{ + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: fax_edata_ack_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; + /* fall through */ + case FAX_EDATA_ACK_COMMAND_1: + if (plci_nl_busy(plci)) + { + plci->internal_command = FAX_EDATA_ACK_COMMAND_1; + return; + } + plci->internal_command = FAX_EDATA_ACK_COMMAND_2; + plci->NData[0].P = plci->fax_connect_info_buffer; + plci->NData[0].PLength = plci->fax_edata_ack_length; + plci->NL.X = plci->NData; + plci->NL.ReqCh = 0; + plci->NL.Req = plci->nl_req = (byte) N_EDATA; + plci->adapter->request(&plci->NL); + return; + case FAX_EDATA_ACK_COMMAND_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: FAX issue EDATA ACK failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + break; + } + } +} + + +static void fax_connect_info_command(dword Id, PLCI *plci, byte Rc) +{ + word Info; + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: fax_connect_info_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + Info = GOOD; + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; /* fall through */ + case FAX_CONNECT_INFO_COMMAND_1: + if (plci_nl_busy(plci)) + { + plci->internal_command = FAX_CONNECT_INFO_COMMAND_1; + return; + } + plci->internal_command = FAX_CONNECT_INFO_COMMAND_2; + plci->NData[0].P = plci->fax_connect_info_buffer; + plci->NData[0].PLength = plci->fax_connect_info_length; + plci->NL.X = plci->NData; + plci->NL.ReqCh = 0; + plci->NL.Req = plci->nl_req = (byte) N_EDATA; + plci->adapter->request(&plci->NL); + return; + case FAX_CONNECT_INFO_COMMAND_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: FAX setting connect info failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _WRONG_STATE; + break; + } + if (plci_nl_busy(plci)) + { + plci->internal_command = FAX_CONNECT_INFO_COMMAND_2; + return; + } + plci->command = _CONNECT_B3_R; + nl_req_ncci(plci, N_CONNECT, 0); + send_req(plci); + return; + } + sendf(plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info); +} + + +static void fax_adjust_b23_command(dword Id, PLCI *plci, byte Rc) +{ + word Info; + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: fax_adjust_b23_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + Info = GOOD; + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; + plci->adjust_b_parms_msg = NULL; + plci->adjust_b_facilities = plci->B1_facilities; + plci->adjust_b_command = FAX_ADJUST_B23_COMMAND_1; + plci->adjust_b_ncci = (word)(Id >> 16); + plci->adjust_b_mode = ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_ASSIGN_L23; + plci->adjust_b_state = ADJUST_B_START; + dbug(1, dprintf("[%06lx] %s,%d: FAX adjust B23...", + UnMapId(Id), (char *)(FILE_), __LINE__)); + /* fall through */ + case FAX_ADJUST_B23_COMMAND_1: + Info = adjust_b_process(Id, plci, Rc); + if (Info != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: FAX adjust failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + break; + } + if (plci->internal_command) + return; + /* fall through */ + case FAX_ADJUST_B23_COMMAND_2: + if (plci_nl_busy(plci)) + { + plci->internal_command = FAX_ADJUST_B23_COMMAND_2; + return; + } + plci->command = _CONNECT_B3_R; + nl_req_ncci(plci, N_CONNECT, 0); + send_req(plci); + return; + } + sendf(plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info); +} + + +static void fax_disconnect_command(dword Id, PLCI *plci, byte Rc) +{ + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: fax_disconnect_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; + plci->internal_command = FAX_DISCONNECT_COMMAND_1; + return; + case FAX_DISCONNECT_COMMAND_1: + case FAX_DISCONNECT_COMMAND_2: + case FAX_DISCONNECT_COMMAND_3: + if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0)) + { + dbug(1, dprintf("[%06lx] %s,%d: FAX disconnect EDATA failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + break; + } + if (Rc == OK) + { + if ((internal_command == FAX_DISCONNECT_COMMAND_1) + || (internal_command == FAX_DISCONNECT_COMMAND_2)) + { + plci->internal_command = FAX_DISCONNECT_COMMAND_2; + } + } + else if (Rc == 0) + { + if (internal_command == FAX_DISCONNECT_COMMAND_1) + plci->internal_command = FAX_DISCONNECT_COMMAND_3; + } + return; + } +} + + + +static void rtp_connect_b3_req_command(dword Id, PLCI *plci, byte Rc) +{ + word Info; + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: rtp_connect_b3_req_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + Info = GOOD; + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; /* fall through */ + case RTP_CONNECT_B3_REQ_COMMAND_1: + if (plci_nl_busy(plci)) + { + plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_1; + return; + } + plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_2; + nl_req_ncci(plci, N_CONNECT, 0); + send_req(plci); + return; + case RTP_CONNECT_B3_REQ_COMMAND_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: RTP setting connect info failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + Info = _WRONG_STATE; + break; + } + if (plci_nl_busy(plci)) + { + plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_2; + return; + } + plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_3; + plci->NData[0].PLength = plci->internal_req_buffer[0]; + plci->NData[0].P = plci->internal_req_buffer + 1; + plci->NL.X = plci->NData; + plci->NL.ReqCh = 0; + plci->NL.Req = plci->nl_req = (byte) N_UDATA; + plci->adapter->request(&plci->NL); + break; + case RTP_CONNECT_B3_REQ_COMMAND_3: + return; + } + sendf(plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info); +} + + +static void rtp_connect_b3_res_command(dword Id, PLCI *plci, byte Rc) +{ + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: rtp_connect_b3_res_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; /* fall through */ + case RTP_CONNECT_B3_RES_COMMAND_1: + if (plci_nl_busy(plci)) + { + plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_1; + return; + } + plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_2; + nl_req_ncci(plci, N_CONNECT_ACK, (byte)(Id >> 16)); + send_req(plci); + return; + case RTP_CONNECT_B3_RES_COMMAND_2: + if ((Rc != OK) && (Rc != OK_FC)) + { + dbug(1, dprintf("[%06lx] %s,%d: RTP setting connect resp info failed %02x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc)); + break; + } + if (plci_nl_busy(plci)) + { + plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_2; + return; + } + sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", ""); + plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_3; + plci->NData[0].PLength = plci->internal_req_buffer[0]; + plci->NData[0].P = plci->internal_req_buffer + 1; + plci->NL.X = plci->NData; + plci->NL.ReqCh = 0; + plci->NL.Req = plci->nl_req = (byte) N_UDATA; + plci->adapter->request(&plci->NL); + return; + case RTP_CONNECT_B3_RES_COMMAND_3: + return; + } +} + + + +static void hold_save_command(dword Id, PLCI *plci, byte Rc) +{ + byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/ + word Info; + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: hold_save_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + Info = GOOD; + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + if (!plci->NL.Id) + break; + plci->command = 0; + plci->adjust_b_parms_msg = NULL; + plci->adjust_b_facilities = plci->B1_facilities; + plci->adjust_b_command = HOLD_SAVE_COMMAND_1; + plci->adjust_b_ncci = (word)(Id >> 16); + plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23; + plci->adjust_b_state = ADJUST_B_START; + dbug(1, dprintf("[%06lx] %s,%d: HOLD save...", + UnMapId(Id), (char *)(FILE_), __LINE__)); + /* fall through */ + case HOLD_SAVE_COMMAND_1: + Info = adjust_b_process(Id, plci, Rc); + if (Info != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: HOLD save failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + break; + } + if (plci->internal_command) + return; + } + sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", 3, SS_Ind); +} + + +static void retrieve_restore_command(dword Id, PLCI *plci, byte Rc) +{ + byte SS_Ind[] = "\x05\x03\x00\x02\x00\x00"; /* Retrieve_Ind struct*/ + word Info; + word internal_command; + + dbug(1, dprintf("[%06lx] %s,%d: retrieve_restore_command %02x %04x", + UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command)); + + Info = GOOD; + internal_command = plci->internal_command; + plci->internal_command = 0; + switch (internal_command) + { + default: + plci->command = 0; + plci->adjust_b_parms_msg = NULL; + plci->adjust_b_facilities = plci->B1_facilities; + plci->adjust_b_command = RETRIEVE_RESTORE_COMMAND_1; + plci->adjust_b_ncci = (word)(Id >> 16); + plci->adjust_b_mode = ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_USER_CONNECT | ADJUST_B_MODE_RESTORE; + plci->adjust_b_state = ADJUST_B_START; + dbug(1, dprintf("[%06lx] %s,%d: RETRIEVE restore...", + UnMapId(Id), (char *)(FILE_), __LINE__)); + /* fall through */ + case RETRIEVE_RESTORE_COMMAND_1: + Info = adjust_b_process(Id, plci, Rc); + if (Info != GOOD) + { + dbug(1, dprintf("[%06lx] %s,%d: RETRIEVE restore failed", + UnMapId(Id), (char *)(FILE_), __LINE__)); + break; + } + if (plci->internal_command) + return; + } + sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", 3, SS_Ind); +} + + +static void init_b1_config(PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: init_b1_config", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + plci->B1_resource = 0; + plci->B1_facilities = 0; + + plci->li_bchannel_id = 0; + mixer_clear_config(plci); + + + ec_clear_config(plci); + + + dtmf_rec_clear_config(plci); + dtmf_send_clear_config(plci); + dtmf_parameter_clear_config(plci); + + adv_voice_clear_config(plci); + adjust_b_clear(plci); +} + + +static void clear_b1_config(PLCI *plci) +{ + + dbug(1, dprintf("[%06lx] %s,%d: clear_b1_config", + (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)), + (char *)(FILE_), __LINE__)); + + adv_voice_clear_config(plci); + adjust_b_clear(plci); + + ec_clear_config(plci); + + + dtmf_rec_clear_config(plci); + dtmf_send_clear_config(plci); + dtmf_parameter_clear_config(plci); + + + if ((plci->li_bchannel_id != 0) + && (li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci == plci)) + { + mixer_clear_config(plci); + li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci = NULL; + plci->li_bchannel_id = 0; + } + + plci->B1_resource = 0; + plci->B1_facilities = 0; +} + + +/* ----------------------------------------------------------------- + XON protocol local helpers + ----------------------------------------------------------------- */ +static void channel_flow_control_remove(PLCI *plci) { + DIVA_CAPI_ADAPTER *a = plci->adapter; + word i; + for (i = 1; i < MAX_NL_CHANNEL + 1; i++) { + if (a->ch_flow_plci[i] == plci->Id) { + a->ch_flow_plci[i] = 0; + a->ch_flow_control[i] = 0; + } + } +} + +static void channel_x_on(PLCI *plci, byte ch) { + DIVA_CAPI_ADAPTER *a = plci->adapter; + if (a->ch_flow_control[ch] & N_XON_SENT) { + a->ch_flow_control[ch] &= ~N_XON_SENT; + } +} + +static void channel_x_off(PLCI *plci, byte ch, byte flag) { + DIVA_CAPI_ADAPTER *a = plci->adapter; + if ((a->ch_flow_control[ch] & N_RX_FLOW_CONTROL_MASK) == 0) { + a->ch_flow_control[ch] |= (N_CH_XOFF | flag); + a->ch_flow_plci[ch] = plci->Id; + a->ch_flow_control_pending++; + } +} + +static void channel_request_xon(PLCI *plci, byte ch) { + DIVA_CAPI_ADAPTER *a = plci->adapter; + + if (a->ch_flow_control[ch] & N_CH_XOFF) { + a->ch_flow_control[ch] |= N_XON_REQ; + a->ch_flow_control[ch] &= ~N_CH_XOFF; + a->ch_flow_control[ch] &= ~N_XON_CONNECT_IND; + } +} + +static void channel_xmit_extended_xon(PLCI *plci) { + DIVA_CAPI_ADAPTER *a; + int max_ch = ARRAY_SIZE(a->ch_flow_control); + int i, one_requested = 0; + + if ((!plci) || (!plci->Id) || ((a = plci->adapter) == NULL)) { + return; + } + + for (i = 0; i < max_ch; i++) { + if ((a->ch_flow_control[i] & N_CH_XOFF) && + (a->ch_flow_control[i] & N_XON_CONNECT_IND) && + (plci->Id == a->ch_flow_plci[i])) { + channel_request_xon(plci, (byte)i); + one_requested = 1; + } + } + + if (one_requested) { + channel_xmit_xon(plci); + } +} + +/* + Try to xmit next X_ON +*/ +static int find_channel_with_pending_x_on(DIVA_CAPI_ADAPTER *a, PLCI *plci) { + int max_ch = ARRAY_SIZE(a->ch_flow_control); + int i; + + if (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)) { + return (0); + } + + if (a->last_flow_control_ch >= max_ch) { + a->last_flow_control_ch = 1; + } + for (i = a->last_flow_control_ch; i < max_ch; i++) { + if ((a->ch_flow_control[i] & N_XON_REQ) && + (plci->Id == a->ch_flow_plci[i])) { + a->last_flow_control_ch = i + 1; + return (i); + } + } + + for (i = 1; i < a->last_flow_control_ch; i++) { + if ((a->ch_flow_control[i] & N_XON_REQ) && + (plci->Id == a->ch_flow_plci[i])) { + a->last_flow_control_ch = i + 1; + return (i); + } + } + + return (0); +} + +static void channel_xmit_xon(PLCI *plci) { + DIVA_CAPI_ADAPTER *a = plci->adapter; + byte ch; + + if (plci->nl_req || !plci->NL.Id || plci->nl_remove_id) { + return; + } + if ((ch = (byte)find_channel_with_pending_x_on(a, plci)) == 0) { + return; + } + a->ch_flow_control[ch] &= ~N_XON_REQ; + a->ch_flow_control[ch] |= N_XON_SENT; + + plci->NL.Req = plci->nl_req = (byte)N_XON; + plci->NL.ReqCh = ch; + plci->NL.X = plci->NData; + plci->NL.XNum = 1; + plci->NData[0].P = &plci->RBuffer[0]; + plci->NData[0].PLength = 0; + + plci->adapter->request(&plci->NL); +} + +static int channel_can_xon(PLCI *plci, byte ch) { + APPL *APPLptr; + DIVA_CAPI_ADAPTER *a; + word NCCIcode; + dword count; + word Num; + word i; + + APPLptr = plci->appl; + a = plci->adapter; + + if (!APPLptr) + return (0); + + NCCIcode = a->ch_ncci[ch] | (((word) a->Id) << 8); + + /* count all buffers within the Application pool */ + /* belonging to the same NCCI. XON if a first is */ + /* used. */ + count = 0; + Num = 0xffff; + for (i = 0; i < APPLptr->MaxBuffer; i++) { + if (NCCIcode == APPLptr->DataNCCI[i]) count++; + if (!APPLptr->DataNCCI[i] && Num == 0xffff) Num = i; + } + if ((count > 2) || (Num == 0xffff)) { + return (0); + } + return (1); +} + + +/*------------------------------------------------------------------*/ + +static word CPN_filter_ok(byte *cpn, DIVA_CAPI_ADAPTER *a, word offset) +{ + return 1; +} + + + +/**********************************************************************************/ +/* function groups the listening applications according to the CIP mask and the */ +/* Info_Mask. Each group gets just one Connect_Ind. Some application manufacturer */ +/* are not multi-instance capable, so they start e.g. 30 applications what causes */ +/* big problems on application level (one call, 30 Connect_Ind, ect). The */ +/* function must be enabled by setting "a->group_optimization_enabled" from the */ +/* OS specific part (per adapter). */ +/**********************************************************************************/ +static void group_optimization(DIVA_CAPI_ADAPTER *a, PLCI *plci) +{ + word i, j, k, busy, group_found; + dword info_mask_group[MAX_CIP_TYPES]; + dword cip_mask_group[MAX_CIP_TYPES]; + word appl_number_group_type[MAX_APPL]; + PLCI *auxplci; + + /* all APPLs within this inc. call are allowed to dial in */ + bitmap_fill(plci->group_optimization_mask_table, MAX_APPL); + + if (!a->group_optimization_enabled) + { + dbug(1, dprintf("No group optimization")); + return; + } + + dbug(1, dprintf("Group optimization = 0x%x...", a->group_optimization_enabled)); + + for (i = 0; i < MAX_CIP_TYPES; i++) + { + info_mask_group[i] = 0; + cip_mask_group[i] = 0; + } + for (i = 0; i < MAX_APPL; i++) + { + appl_number_group_type[i] = 0; + } + for (i = 0; i < max_appl; i++) /* check if any multi instance capable application is present */ + { /* group_optimization set to 1 means not to optimize multi-instance capable applications (default) */ + if (application[i].Id && (application[i].MaxNCCI) > 1 && (a->CIP_Mask[i]) && (a->group_optimization_enabled == 1)) + { + dbug(1, dprintf("Multi-Instance capable, no optimization required")); + return; /* allow good application unfiltered access */ + } + } + for (i = 0; i < max_appl; i++) /* Build CIP Groups */ + { + if (application[i].Id && a->CIP_Mask[i]) + { + for (k = 0, busy = false; k < a->max_plci; k++) + { + if (a->plci[k].Id) + { + auxplci = &a->plci[k]; + if (auxplci->appl == &application[i]) { + /* application has a busy PLCI */ + busy = true; + dbug(1, dprintf("Appl 0x%x is busy", i + 1)); + } else if (test_bit(i, plci->c_ind_mask_table)) { + /* application has an incoming call pending */ + busy = true; + dbug(1, dprintf("Appl 0x%x has inc. call pending", i + 1)); + } + } + } + + for (j = 0, group_found = 0; j <= (MAX_CIP_TYPES) && !busy && !group_found; j++) /* build groups with free applications only */ + { + if (j == MAX_CIP_TYPES) /* all groups are in use but group still not found */ + { /* the MAX_CIP_TYPES group enables all calls because of field overflow */ + appl_number_group_type[i] = MAX_CIP_TYPES; + group_found = true; + dbug(1, dprintf("Field overflow appl 0x%x", i + 1)); + } + else if ((info_mask_group[j] == a->CIP_Mask[i]) && (cip_mask_group[j] == a->Info_Mask[i])) + { /* is group already present ? */ + appl_number_group_type[i] = j | 0x80; /* store the group number for each application */ + group_found = true; + dbug(1, dprintf("Group 0x%x found with appl 0x%x, CIP=0x%lx", appl_number_group_type[i], i + 1, info_mask_group[j])); + } + else if (!info_mask_group[j]) + { /* establish a new group */ + appl_number_group_type[i] = j | 0x80; /* store the group number for each application */ + info_mask_group[j] = a->CIP_Mask[i]; /* store the new CIP mask for the new group */ + cip_mask_group[j] = a->Info_Mask[i]; /* store the new Info_Mask for this new group */ + group_found = true; + dbug(1, dprintf("New Group 0x%x established with appl 0x%x, CIP=0x%lx", appl_number_group_type[i], i + 1, info_mask_group[j])); + } + } + } + } + + for (i = 0; i < max_appl; i++) /* Build group_optimization_mask_table */ + { + if (appl_number_group_type[i]) /* application is free, has listens and is member of a group */ + { + if (appl_number_group_type[i] == MAX_CIP_TYPES) + { + dbug(1, dprintf("OverflowGroup 0x%x, valid appl = 0x%x, call enabled", appl_number_group_type[i], i + 1)); + } + else + { + dbug(1, dprintf("Group 0x%x, valid appl = 0x%x", appl_number_group_type[i], i + 1)); + for (j = i + 1; j < max_appl; j++) /* search other group members and mark them as busy */ + { + if (appl_number_group_type[i] == appl_number_group_type[j]) + { + dbug(1, dprintf("Appl 0x%x is member of group 0x%x, no call", j + 1, appl_number_group_type[j])); + /* disable call on other group members */ + __clear_bit(j, plci->group_optimization_mask_table); + appl_number_group_type[j] = 0; /* remove disabled group member from group list */ + } + } + } + } + else /* application should not get a call */ + { + __clear_bit(i, plci->group_optimization_mask_table); + } + } + +} + + + +/* OS notifies the driver about a application Capi_Register */ +word CapiRegister(word id) +{ + word i, j, appls_found; + + PLCI *plci; + DIVA_CAPI_ADAPTER *a; + + for (i = 0, appls_found = 0; i < max_appl; i++) + { + if (application[i].Id && (application[i].Id != id)) + { + appls_found++; /* an application has been found */ + } + } + + if (appls_found) return true; + for (i = 0; i < max_adapter; i++) /* scan all adapters... */ + { + a = &adapter[i]; + if (a->request) + { + if (a->flag_dynamic_l1_down) /* remove adapter from L1 tristate (Huntgroup) */ + { + if (!appls_found) /* first application does a capi register */ + { + if ((j = get_plci(a))) /* activate L1 of all adapters */ + { + plci = &a->plci[j - 1]; + plci->command = 0; + add_p(plci, OAD, "\x01\xfd"); + add_p(plci, CAI, "\x01\x80"); + add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30"); + add_p(plci, SHIFT | 6, NULL); + add_p(plci, SIN, "\x02\x00\x00"); + plci->internal_command = START_L1_SIG_ASSIGN_PEND; + sig_req(plci, ASSIGN, DSIG_ID); + add_p(plci, FTY, "\x02\xff\x07"); /* l1 start */ + sig_req(plci, SIG_CTRL, 0); + send_req(plci); + } + } + } + } + } + return false; +} + +/*------------------------------------------------------------------*/ + +/* Functions for virtual Switching e.g. Transfer by join, Conference */ + +static void VSwitchReqInd(PLCI *plci, dword Id, byte **parms) +{ + word i; + /* Format of vswitch_t: + 0 byte length + 1 byte VSWITCHIE + 2 byte VSWITCH_REQ/VSWITCH_IND + 3 byte reserved + 4 word VSwitchcommand + 6 word returnerror + 8... Params + */ + if (!plci || + !plci->appl || + !plci->State || + plci->Sig.Ind == NCR_FACILITY + ) + return; + + for (i = 0; i < MAX_MULTI_IE; i++) + { + if (!parms[i][0]) continue; + if (parms[i][0] < 7) + { + parms[i][0] = 0; /* kill it */ + continue; + } + dbug(1, dprintf("VSwitchReqInd(%d)", parms[i][4])); + switch (parms[i][4]) + { + case VSJOIN: + if (!plci->relatedPTYPLCI || + (plci->ptyState != S_ECT && plci->relatedPTYPLCI->ptyState != S_ECT)) + { /* Error */ + break; + } + /* remember all necessary informations */ + if (parms[i][0] != 11 || parms[i][8] != 3) /* Length Test */ + { + break; + } + if (parms[i][2] == VSWITCH_IND && parms[i][9] == 1) + { /* first indication after ECT-Request on Consultation Call */ + plci->vswitchstate = parms[i][9]; + parms[i][9] = 2; /* State */ + /* now ask first Call to join */ + } + else if (parms[i][2] == VSWITCH_REQ && parms[i][9] == 3) + { /* Answer of VSWITCH_REQ from first Call */ + plci->vswitchstate = parms[i][9]; + /* tell consultation call to join + and the protocol capabilities of the first call */ + } + else + { /* Error */ + break; + } + plci->vsprot = parms[i][10]; /* protocol */ + plci->vsprotdialect = parms[i][11]; /* protocoldialect */ + /* send join request to related PLCI */ + parms[i][1] = VSWITCHIE; + parms[i][2] = VSWITCH_REQ; + + plci->relatedPTYPLCI->command = 0; + plci->relatedPTYPLCI->internal_command = VSWITCH_REQ_PEND; + add_p(plci->relatedPTYPLCI, ESC, &parms[i][0]); + sig_req(plci->relatedPTYPLCI, VSWITCH_REQ, 0); + send_req(plci->relatedPTYPLCI); + break; + case VSTRANSPORT: + default: + if (plci->relatedPTYPLCI && + plci->vswitchstate == 3 && + plci->relatedPTYPLCI->vswitchstate == 3) + { + add_p(plci->relatedPTYPLCI, ESC, &parms[i][0]); + sig_req(plci->relatedPTYPLCI, VSWITCH_REQ, 0); + send_req(plci->relatedPTYPLCI); + } + break; + } + parms[i][0] = 0; /* kill it */ + } +} + + +/*------------------------------------------------------------------*/ + +static int diva_get_dma_descriptor(PLCI *plci, dword *dma_magic) { + ENTITY e; + IDI_SYNC_REQ *pReq = (IDI_SYNC_REQ *)&e; + + if (!(diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_RX_DMA)) { + return (-1); + } + + pReq->xdi_dma_descriptor_operation.Req = 0; + pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION; + + pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC; + pReq->xdi_dma_descriptor_operation.info.descriptor_number = -1; + pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL; + pReq->xdi_dma_descriptor_operation.info.descriptor_magic = 0; + + e.user[0] = plci->adapter->Id - 1; + plci->adapter->request((ENTITY *)pReq); + + if (!pReq->xdi_dma_descriptor_operation.info.operation && + (pReq->xdi_dma_descriptor_operation.info.descriptor_number >= 0) && + pReq->xdi_dma_descriptor_operation.info.descriptor_magic) { + *dma_magic = pReq->xdi_dma_descriptor_operation.info.descriptor_magic; + dbug(3, dprintf("dma_alloc, a:%d (%d-%08x)", + plci->adapter->Id, + pReq->xdi_dma_descriptor_operation.info.descriptor_number, + *dma_magic)); + return (pReq->xdi_dma_descriptor_operation.info.descriptor_number); + } else { + dbug(1, dprintf("dma_alloc failed")); + return (-1); + } +} + +static void diva_free_dma_descriptor(PLCI *plci, int nr) { + ENTITY e; + IDI_SYNC_REQ *pReq = (IDI_SYNC_REQ *)&e; + + if (nr < 0) { + return; + } + + pReq->xdi_dma_descriptor_operation.Req = 0; + pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION; + + pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE; + pReq->xdi_dma_descriptor_operation.info.descriptor_number = nr; + pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL; + pReq->xdi_dma_descriptor_operation.info.descriptor_magic = 0; + + e.user[0] = plci->adapter->Id - 1; + plci->adapter->request((ENTITY *)pReq); + + if (!pReq->xdi_dma_descriptor_operation.info.operation) { + dbug(1, dprintf("dma_free(%d)", nr)); + } else { + dbug(1, dprintf("dma_free failed (%d)", nr)); + } +} + +/*------------------------------------------------------------------*/ diff --git a/drivers/isdn/hardware/eicon/mi_pc.h b/drivers/isdn/hardware/eicon/mi_pc.h new file mode 100644 index 000000000..83e9ed8c1 --- /dev/null +++ b/drivers/isdn/hardware/eicon/mi_pc.h @@ -0,0 +1,204 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +/*---------------------------------------------------------------------------- +// MAESTRA ISA PnP */ +#define BRI_MEMORY_BASE 0x1f700000 +#define BRI_MEMORY_SIZE 0x00100000 /* 1MB on the BRI */ +#define BRI_SHARED_RAM_SIZE 0x00010000 /* 64k shared RAM */ +#define BRI_RAY_TAYLOR_DSP_CODE_SIZE 0x00020000 /* max 128k DSP-Code (Ray Taylor's code) */ +#define BRI_ORG_MAX_DSP_CODE_SIZE 0x00050000 /* max 320k DSP-Code (Telindus) */ +#define BRI_V90D_MAX_DSP_CODE_SIZE 0x00060000 /* max 384k DSP-Code if V.90D included */ +#define BRI_CACHED_ADDR(x) (((x) & 0x1fffffffL) | 0x80000000L) +#define BRI_UNCACHED_ADDR(x) (((x) & 0x1fffffffL) | 0xa0000000L) +#define ADDR 4 +#define ADDRH 6 +#define DATA 0 +#define RESET 7 +#define DEFAULT_ADDRESS 0x240 +#define DEFAULT_IRQ 3 +#define M_PCI_ADDR 0x04 /* MAESTRA BRI PCI */ +#define M_PCI_ADDRH 0x0c /* MAESTRA BRI PCI */ +#define M_PCI_DATA 0x00 /* MAESTRA BRI PCI */ +#define M_PCI_RESET 0x10 /* MAESTRA BRI PCI */ +/*---------------------------------------------------------------------------- +// MAESTRA PRI PCI */ +#define MP_IRQ_RESET 0xc18 /* offset of isr in the CONFIG memory bar */ +#define MP_IRQ_RESET_VAL 0xfe /* value to clear an interrupt */ +#define MP_MEMORY_SIZE 0x00400000 /* 4MB on standard PRI */ +#define MP2_MEMORY_SIZE 0x00800000 /* 8MB on PRI Rev. 2 */ +#define MP_SHARED_RAM_OFFSET 0x00001000 /* offset of shared RAM base in the DRAM memory bar */ +#define MP_SHARED_RAM_SIZE 0x00010000 /* 64k shared RAM */ +#define MP_PROTOCOL_OFFSET (MP_SHARED_RAM_OFFSET + MP_SHARED_RAM_SIZE) +#define MP_RAY_TAYLOR_DSP_CODE_SIZE 0x00040000 /* max 256k DSP-Code (Ray Taylor's code) */ +#define MP_ORG_MAX_DSP_CODE_SIZE 0x00060000 /* max 384k DSP-Code (Telindus) */ +#define MP_V90D_MAX_DSP_CODE_SIZE 0x00070000 /* max 448k DSP-Code if V.90D included) */ +#define MP_VOIP_MAX_DSP_CODE_SIZE 0x00090000 /* max 576k DSP-Code if voice over IP included */ +#define MP_CACHED_ADDR(x) (((x) & 0x1fffffffL) | 0x80000000L) +#define MP_UNCACHED_ADDR(x) (((x) & 0x1fffffffL) | 0xa0000000L) +#define MP_RESET 0x20 /* offset of RESET register in the DEVICES memory bar */ +/* RESET register bits */ +#define _MP_S2M_RESET 0x10 /* active lo */ +#define _MP_LED2 0x08 /* 1 = on */ +#define _MP_LED1 0x04 /* 1 = on */ +#define _MP_DSP_RESET 0x02 /* active lo */ +#define _MP_RISC_RESET 0x81 /* active hi, bit 7 for compatibility with old boards */ +/* CPU exception context structure in MP shared ram after trap */ +typedef struct mp_xcptcontext_s MP_XCPTC; +struct mp_xcptcontext_s { + dword sr; + dword cr; + dword epc; + dword vaddr; + dword regs[32]; + dword mdlo; + dword mdhi; + dword reseverd; + dword xclass; +}; +/* boot interface structure for PRI */ +struct mp_load { + dword volatile cmd; + dword volatile addr; + dword volatile len; + dword volatile err; + dword volatile live; + dword volatile res1[0x1b]; + dword volatile TrapId; /* has value 0x999999XX on a CPU trap */ + dword volatile res2[0x03]; + MP_XCPTC volatile xcpt; /* contains register dump */ + dword volatile rest[((0x1020 >> 2) - 6) - 0x1b - 1 - 0x03 - (sizeof(MP_XCPTC) >> 2)]; + dword volatile signature; + dword data[60000]; /* real interface description */ +}; +/*----------------------------------------------------------------------------*/ +/* SERVER 4BRI (Quattro PCI) */ +#define MQ_BOARD_REG_OFFSET 0x800000 /* PC relative On board registers offset */ +#define MQ_BREG_RISC 0x1200 /* RISC Reset ect */ +#define MQ_RISC_COLD_RESET_MASK 0x0001 /* RISC Cold reset */ +#define MQ_RISC_WARM_RESET_MASK 0x0002 /* RISC Warm reset */ +#define MQ_BREG_IRQ_TEST 0x0608 /* Interrupt request, no CPU interaction */ +#define MQ_IRQ_REQ_ON 0x1 +#define MQ_IRQ_REQ_OFF 0x0 +#define MQ_BOARD_DSP_OFFSET 0xa00000 /* PC relative On board DSP regs offset */ +#define MQ_DSP1_ADDR_OFFSET 0x0008 /* Addr register offset DSP 1 subboard 1 */ +#define MQ_DSP2_ADDR_OFFSET 0x0208 /* Addr register offset DSP 2 subboard 1 */ +#define MQ_DSP1_DATA_OFFSET 0x0000 /* Data register offset DSP 1 subboard 1 */ +#define MQ_DSP2_DATA_OFFSET 0x0200 /* Data register offset DSP 2 subboard 1 */ +#define MQ_DSP_JUNK_OFFSET 0x0400 /* DSP Data/Addr regs subboard offset */ +#define MQ_ISAC_DSP_RESET 0x0028 /* ISAC and DSP reset address offset */ +#define MQ_BOARD_ISAC_DSP_RESET 0x800028 /* ISAC and DSP reset address offset */ +#define MQ_INSTANCE_COUNT 4 /* 4BRI consists of four instances */ +#define MQ_MEMORY_SIZE 0x00400000 /* 4MB on standard 4BRI */ +#define MQ_CTRL_SIZE 0x00002000 /* 8K memory mapped registers */ +#define MQ_SHARED_RAM_SIZE 0x00010000 /* 64k shared RAM */ +#define MQ_ORG_MAX_DSP_CODE_SIZE 0x00050000 /* max 320k DSP-Code (Telindus) */ +#define MQ_V90D_MAX_DSP_CODE_SIZE 0x00060000 /* max 384K DSP-Code if V.90D included */ +#define MQ_VOIP_MAX_DSP_CODE_SIZE 0x00028000 /* max 4*160k = 640K DSP-Code if voice over IP included */ +#define MQ_CACHED_ADDR(x) (((x) & 0x1fffffffL) | 0x80000000L) +#define MQ_UNCACHED_ADDR(x) (((x) & 0x1fffffffL) | 0xa0000000L) +/*--------------------------------------------------------------------------------------------*/ +/* Additional definitions reflecting the different address map of the SERVER 4BRI V2 */ +#define MQ2_BREG_RISC 0x0200 /* RISC Reset ect */ +#define MQ2_BREG_IRQ_TEST 0x0400 /* Interrupt request, no CPU interaction */ +#define MQ2_BOARD_DSP_OFFSET 0x800000 /* PC relative On board DSP regs offset */ +#define MQ2_DSP1_DATA_OFFSET 0x1800 /* Data register offset DSP 1 subboard 1 */ +#define MQ2_DSP1_ADDR_OFFSET 0x1808 /* Addr register offset DSP 1 subboard 1 */ +#define MQ2_DSP2_DATA_OFFSET 0x1810 /* Data register offset DSP 2 subboard 1 */ +#define MQ2_DSP2_ADDR_OFFSET 0x1818 /* Addr register offset DSP 2 subboard 1 */ +#define MQ2_DSP_JUNK_OFFSET 0x1000 /* DSP Data/Addr regs subboard offset */ +#define MQ2_ISAC_DSP_RESET 0x0000 /* ISAC and DSP reset address offset */ +#define MQ2_BOARD_ISAC_DSP_RESET 0x800000 /* ISAC and DSP reset address offset */ +#define MQ2_IPACX_CONFIG 0x0300 /* IPACX Configuration TE(0)/NT(1) */ +#define MQ2_BOARD_IPACX_CONFIG 0x800300 /* "" */ +#define MQ2_MEMORY_SIZE 0x01000000 /* 16MB code/data memory */ +#define MQ2_CTRL_SIZE 0x00008000 /* 32K memory mapped registers */ +/*----------------------------------------------------------------------------*/ +/* SERVER BRI 2M/2F as derived from 4BRI V2 */ +#define BRI2_MEMORY_SIZE 0x00800000 /* 8MB code/data memory */ +#define BRI2_PROTOCOL_MEMORY_SIZE (MQ2_MEMORY_SIZE >> 2) /* same as one 4BRI Rev.2 task */ +#define BRI2_CTRL_SIZE 0x00008000 /* 32K memory mapped registers */ +#define M_INSTANCE_COUNT 1 /* BRI consists of one instance */ +/* + * Some useful constants for proper initialization of the GT6401x + */ +#define ID_REG 0x0000 /*Pci reg-contain the Dev&Ven ID of the card*/ +#define RAS0_BASEREG 0x0010 /*Ras0 register - contain the base addr Ras0*/ +#define RAS2_BASEREG 0x0014 +#define CS_BASEREG 0x0018 +#define BOOT_BASEREG 0x001c +#define GTREGS_BASEREG 0x0024 /*GTRegsBase reg-contain the base addr where*/ + /*the GT64010 internal regs where mapped */ +/* + * GT64010 internal registers + */ +/* DRAM device coding */ +#define LOW_RAS0_DREG 0x0400 /*Ras0 low decode address*/ +#define HI_RAS0_DREG 0x0404 /*Ras0 high decode address*/ +#define LOW_RAS1_DREG 0x0408 /*Ras1 low decode address*/ +#define HI_RAS1_DREG 0x040c /*Ras1 high decode address*/ +#define LOW_RAS2_DREG 0x0410 /*Ras2 low decode address*/ +#define HI_RAS2_DREG 0x0414 /*Ras2 high decode address*/ +#define LOW_RAS3_DREG 0x0418 /*Ras3 low decode address*/ +#define HI_RAS3_DREG 0x041c /*Ras3 high decode address*/ +/* I/O CS device coding */ +#define LOW_CS0_DREG 0x0420 /* CS0* low decode register */ +#define HI_CS0_DREG 0x0424 /* CS0* high decode register */ +#define LOW_CS1_DREG 0x0428 /* CS1* low decode register */ +#define HI_CS1_DREG 0x042c /* CS1* high decode register */ +#define LOW_CS2_DREG 0x0430 /* CS2* low decode register */ +#define HI_CS2_DREG 0x0434 /* CS2* high decode register */ +#define LOW_CS3_DREG 0x0438 /* CS3* low decode register */ +#define HI_CS3_DREG 0x043c /* CS3* high decode register */ +/* Boot PROM device coding */ +#define LOW_BOOTCS_DREG 0x0440 /* Boot CS low decode register */ +#define HI_BOOTCS_DREG 0x0444 /* Boot CS High decode register */ +/* DRAM group coding (for CPU) */ +#define LO_RAS10_GREG 0x0008 /*Ras1..0 group low decode address*/ +#define HI_RAS10_GREG 0x0010 /*Ras1..0 group high decode address*/ +#define LO_RAS32_GREG 0x0018 /*Ras3..2 group low decode address */ +#define HI_RAS32_GREG 0x0020 /*Ras3..2 group high decode address */ +/* I/O CS group coding for (CPU) */ +#define LO_CS20_GREG 0x0028 /* CS2..0 group low decode register */ +#define HI_CS20_GREG 0x0030 /* CS2..0 group high decode register */ +#define LO_CS3B_GREG 0x0038 /* CS3 & PROM group low decode register */ +#define HI_CS3B_GREG 0x0040 /* CS3 & PROM group high decode register */ +/* Galileo specific PCI config. */ +#define PCI_TIMEOUT_RET 0x0c04 /* Time Out and retry register */ +#define RAS10_BANKSIZE 0x0c08 /* RAS 1..0 group PCI bank size */ +#define RAS32_BANKSIZE 0x0c0c /* RAS 3..2 group PCI bank size */ +#define CS20_BANKSIZE 0x0c10 /* CS 2..0 group PCI bank size */ +#define CS3B_BANKSIZE 0x0c14 /* CS 3 & Boot group PCI bank size */ +#define DRAM_SIZE 0x0001 /*Dram size in mega bytes*/ +#define PROM_SIZE 0x08000 /*Prom size in bytes*/ +/*--------------------------------------------------------------------------*/ +#define OFFS_DIVA_INIT_TASK_COUNT 0x68 +#define OFFS_DSP_CODE_BASE_ADDR 0x6c +#define OFFS_XLOG_BUF_ADDR 0x70 +#define OFFS_XLOG_COUNT_ADDR 0x74 +#define OFFS_XLOG_OUT_ADDR 0x78 +#define OFFS_PROTOCOL_END_ADDR 0x7c +#define OFFS_PROTOCOL_ID_STRING 0x80 +/*--------------------------------------------------------------------------*/ diff --git a/drivers/isdn/hardware/eicon/mntfunc.c b/drivers/isdn/hardware/eicon/mntfunc.c new file mode 100644 index 000000000..1cd9affb6 --- /dev/null +++ b/drivers/isdn/hardware/eicon/mntfunc.c @@ -0,0 +1,370 @@ +/* $Id: mntfunc.c,v 1.19.6.4 2005/01/31 12:22:20 armin Exp $ + * + * Driver for Eicon DIVA Server ISDN cards. + * Maint module + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + + +#include "platform.h" +#include "di_defs.h" +#include "divasync.h" +#include "debug_if.h" + +extern char *DRIVERRELEASE_MNT; + +#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) +#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) + +extern void DIVA_DIDD_Read(void *, int); + +static dword notify_handle; +static DESCRIPTOR DAdapter; +static DESCRIPTOR MAdapter; +static DESCRIPTOR MaintDescriptor = +{ IDI_DIMAINT, 0, 0, (IDI_CALL) diva_maint_prtComp }; + +extern int diva_os_copy_to_user(void *os_handle, void __user *dst, + const void *src, int length); +extern int diva_os_copy_from_user(void *os_handle, void *dst, + const void __user *src, int length); + +static void no_printf(unsigned char *x, ...) +{ + /* dummy debug function */ +} + +#include "debuglib.c" + +/* + * DIDD callback function + */ +static void *didd_callback(void *context, DESCRIPTOR *adapter, + int removal) +{ + if (adapter->type == IDI_DADAPTER) { + DBG_ERR(("cb: Change in DAdapter ? Oops ?.")); + } else if (adapter->type == IDI_DIMAINT) { + if (removal) { + DbgDeregister(); + memset(&MAdapter, 0, sizeof(MAdapter)); + dprintf = no_printf; + } else { + memcpy(&MAdapter, adapter, sizeof(MAdapter)); + dprintf = (DIVA_DI_PRINTF) MAdapter.request; + DbgRegister("MAINT", DRIVERRELEASE_MNT, DBG_DEFAULT); + } + } else if ((adapter->type > 0) && (adapter->type < 16)) { + if (removal) { + diva_mnt_remove_xdi_adapter(adapter); + } else { + diva_mnt_add_xdi_adapter(adapter); + } + } + return (NULL); +} + +/* + * connect to didd + */ +static int __init connect_didd(void) +{ + int x = 0; + int dadapter = 0; + IDI_SYNC_REQ req; + DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS]; + + DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table)); + + for (x = 0; x < MAX_DESCRIPTORS; x++) { + if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */ + dadapter = 1; + memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter)); + req.didd_notify.e.Req = 0; + req.didd_notify.e.Rc = + IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY; + req.didd_notify.info.callback = (void *)didd_callback; + req.didd_notify.info.context = NULL; + DAdapter.request((ENTITY *)&req); + if (req.didd_notify.e.Rc != 0xff) + return (0); + notify_handle = req.didd_notify.info.handle; + /* Register MAINT (me) */ + req.didd_add_adapter.e.Req = 0; + req.didd_add_adapter.e.Rc = + IDI_SYNC_REQ_DIDD_ADD_ADAPTER; + req.didd_add_adapter.info.descriptor = + (void *) &MaintDescriptor; + DAdapter.request((ENTITY *)&req); + if (req.didd_add_adapter.e.Rc != 0xff) + return (0); + } else if ((DIDD_Table[x].type > 0) + && (DIDD_Table[x].type < 16)) { + diva_mnt_add_xdi_adapter(&DIDD_Table[x]); + } + } + return (dadapter); +} + +/* + * disconnect from didd + */ +static void __exit disconnect_didd(void) +{ + IDI_SYNC_REQ req; + + req.didd_notify.e.Req = 0; + req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY; + req.didd_notify.info.handle = notify_handle; + DAdapter.request((ENTITY *)&req); + + req.didd_remove_adapter.e.Req = 0; + req.didd_remove_adapter.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER; + req.didd_remove_adapter.info.p_request = + (IDI_CALL) MaintDescriptor.request; + DAdapter.request((ENTITY *)&req); +} + +/* + * read/write maint + */ +int maint_read_write(void __user *buf, int count) +{ + byte data[128]; + dword cmd, id, mask; + int ret = 0; + + if (count < (3 * sizeof(dword))) + return (-EFAULT); + + if (diva_os_copy_from_user(NULL, (void *) &data[0], + buf, 3 * sizeof(dword))) { + return (-EFAULT); + } + + cmd = *(dword *)&data[0]; /* command */ + id = *(dword *)&data[4]; /* driver id */ + mask = *(dword *)&data[8]; /* mask or size */ + + switch (cmd) { + case DITRACE_CMD_GET_DRIVER_INFO: + if ((ret = diva_get_driver_info(id, data, sizeof(data))) > 0) { + if ((count < ret) || diva_os_copy_to_user + (NULL, buf, (void *) &data[0], ret)) + ret = -EFAULT; + } else { + ret = -EINVAL; + } + break; + + case DITRACE_READ_DRIVER_DBG_MASK: + if ((ret = diva_get_driver_dbg_mask(id, (byte *) data)) > 0) { + if ((count < ret) || diva_os_copy_to_user + (NULL, buf, (void *) &data[0], ret)) + ret = -EFAULT; + } else { + ret = -ENODEV; + } + break; + + case DITRACE_WRITE_DRIVER_DBG_MASK: + if ((ret = diva_set_driver_dbg_mask(id, mask)) <= 0) { + ret = -ENODEV; + } + break; + + /* + Filter commands will ignore the ID due to fact that filtering affects + the B- channel and Audio Tap trace levels only. Also MAINT driver will + select the right trace ID by itself + */ + case DITRACE_WRITE_SELECTIVE_TRACE_FILTER: + if (!mask) { + ret = diva_set_trace_filter(1, "*"); + } else if (mask < sizeof(data)) { + if (diva_os_copy_from_user(NULL, data, (char __user *)buf + 12, mask)) { + ret = -EFAULT; + } else { + ret = diva_set_trace_filter((int)mask, data); + } + } else { + ret = -EINVAL; + } + break; + + case DITRACE_READ_SELECTIVE_TRACE_FILTER: + if ((ret = diva_get_trace_filter(sizeof(data), data)) > 0) { + if (diva_os_copy_to_user(NULL, buf, data, ret)) + ret = -EFAULT; + } else { + ret = -ENODEV; + } + break; + + case DITRACE_READ_TRACE_ENTRY:{ + diva_os_spin_lock_magic_t old_irql; + word size; + diva_dbg_entry_head_t *pmsg; + byte *pbuf; + + if (!(pbuf = diva_os_malloc(0, mask))) { + return (-ENOMEM); + } + + for (;;) { + if (!(pmsg = + diva_maint_get_message(&size, &old_irql))) { + break; + } + if (size > mask) { + diva_maint_ack_message(0, &old_irql); + ret = -EINVAL; + break; + } + ret = size; + memcpy(pbuf, pmsg, size); + diva_maint_ack_message(1, &old_irql); + if ((count < size) || + diva_os_copy_to_user(NULL, buf, (void *) pbuf, size)) + ret = -EFAULT; + break; + } + diva_os_free(0, pbuf); + } + break; + + case DITRACE_READ_TRACE_ENTRYS:{ + diva_os_spin_lock_magic_t old_irql; + word size; + diva_dbg_entry_head_t *pmsg; + byte *pbuf = NULL; + int written = 0; + + if (mask < 4096) { + ret = -EINVAL; + break; + } + if (!(pbuf = diva_os_malloc(0, mask))) { + return (-ENOMEM); + } + + for (;;) { + if (!(pmsg = + diva_maint_get_message(&size, &old_irql))) { + break; + } + if ((size + 8) > mask) { + diva_maint_ack_message(0, &old_irql); + break; + } + /* + Write entry length + */ + pbuf[written++] = (byte) size; + pbuf[written++] = (byte) (size >> 8); + pbuf[written++] = 0; + pbuf[written++] = 0; + /* + Write message + */ + memcpy(&pbuf[written], pmsg, size); + diva_maint_ack_message(1, &old_irql); + written += size; + mask -= (size + 4); + } + pbuf[written++] = 0; + pbuf[written++] = 0; + pbuf[written++] = 0; + pbuf[written++] = 0; + + if ((count < written) || diva_os_copy_to_user(NULL, buf, (void *) pbuf, written)) { + ret = -EFAULT; + } else { + ret = written; + } + diva_os_free(0, pbuf); + } + break; + + default: + ret = -EINVAL; + } + return (ret); +} + +/* + * init + */ +int __init mntfunc_init(int *buffer_length, void **buffer, + unsigned long diva_dbg_mem) +{ + if (*buffer_length < 64) { + *buffer_length = 64; + } + if (*buffer_length > 512) { + *buffer_length = 512; + } + *buffer_length *= 1024; + + if (diva_dbg_mem) { + *buffer = (void *) diva_dbg_mem; + } else { + while ((*buffer_length >= (64 * 1024)) + && + (!(*buffer = diva_os_malloc(0, *buffer_length)))) { + *buffer_length -= 1024; + } + + if (!*buffer) { + DBG_ERR(("init: Can not alloc trace buffer")); + return (0); + } + } + + if (diva_maint_init(*buffer, *buffer_length, (diva_dbg_mem == 0))) { + if (!diva_dbg_mem) { + diva_os_free(0, *buffer); + } + DBG_ERR(("init: maint init failed")); + return (0); + } + + if (!connect_didd()) { + DBG_ERR(("init: failed to connect to DIDD.")); + diva_maint_finit(); + if (!diva_dbg_mem) { + diva_os_free(0, *buffer); + } + return (0); + } + return (1); +} + +/* + * exit + */ +void __exit mntfunc_finit(void) +{ + void *buffer; + int i = 100; + + DbgDeregister(); + + while (diva_mnt_shutdown_xdi_adapters() && i--) { + diva_os_sleep(10); + } + + disconnect_didd(); + + if ((buffer = diva_maint_finit())) { + diva_os_free(0, buffer); + } + + memset(&MAdapter, 0, sizeof(MAdapter)); + dprintf = no_printf; +} diff --git a/drivers/isdn/hardware/eicon/os_4bri.c b/drivers/isdn/hardware/eicon/os_4bri.c new file mode 100644 index 000000000..87db5f4df --- /dev/null +++ b/drivers/isdn/hardware/eicon/os_4bri.c @@ -0,0 +1,1132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* $Id: os_4bri.c,v 1.28.4.4 2005/02/11 19:40:25 armin Exp $ */ + +#include "platform.h" +#include "debuglib.h" +#include "cardtype.h" +#include "pc.h" +#include "pr_pc.h" +#include "di_defs.h" +#include "dsp_defs.h" +#include "di.h" +#include "io.h" + +#include "xdi_msg.h" +#include "xdi_adapter.h" +#include "os_4bri.h" +#include "diva_pci.h" +#include "mi_pc.h" +#include "dsrv4bri.h" +#include "helpers.h" + +static void *diva_xdiLoadFileFile = NULL; +static dword diva_xdiLoadFileLength = 0; + +/* +** IMPORTS +*/ +extern void prepare_qBri_functions(PISDN_ADAPTER IoAdapter); +extern void prepare_qBri2_functions(PISDN_ADAPTER IoAdapter); +extern void diva_xdi_display_adapter_features(int card); +extern void diva_add_slave_adapter(diva_os_xdi_adapter_t *a); + +extern int qBri_FPGA_download(PISDN_ADAPTER IoAdapter); +extern void start_qBri_hardware(PISDN_ADAPTER IoAdapter); + +extern int diva_card_read_xlog(diva_os_xdi_adapter_t *a); + +/* +** LOCALS +*/ +static unsigned long _4bri_bar_length[4] = { + 0x100, + 0x100, /* I/O */ + MQ_MEMORY_SIZE, + 0x2000 +}; +static unsigned long _4bri_v2_bar_length[4] = { + 0x100, + 0x100, /* I/O */ + MQ2_MEMORY_SIZE, + 0x10000 +}; +static unsigned long _4bri_v2_bri_bar_length[4] = { + 0x100, + 0x100, /* I/O */ + BRI2_MEMORY_SIZE, + 0x10000 +}; + + +static int diva_4bri_cleanup_adapter(diva_os_xdi_adapter_t *a); +static int _4bri_get_serial_number(diva_os_xdi_adapter_t *a); +static int diva_4bri_cmd_card_proc(struct _diva_os_xdi_adapter *a, + diva_xdi_um_cfg_cmd_t *cmd, + int length); +static int diva_4bri_cleanup_slave_adapters(diva_os_xdi_adapter_t *a); +static int diva_4bri_write_fpga_image(diva_os_xdi_adapter_t *a, + byte *data, dword length); +static int diva_4bri_reset_adapter(PISDN_ADAPTER IoAdapter); +static int diva_4bri_write_sdram_block(PISDN_ADAPTER IoAdapter, + dword address, + const byte *data, + dword length, dword limit); +static int diva_4bri_start_adapter(PISDN_ADAPTER IoAdapter, + dword start_address, dword features); +static int check_qBri_interrupt(PISDN_ADAPTER IoAdapter); +static int diva_4bri_stop_adapter(diva_os_xdi_adapter_t *a); + +static int _4bri_is_rev_2_card(int card_ordinal) +{ + switch (card_ordinal) { + case CARDTYPE_DIVASRV_Q_8M_V2_PCI: + case CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI: + case CARDTYPE_DIVASRV_B_2M_V2_PCI: + case CARDTYPE_DIVASRV_B_2F_PCI: + case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI: + return (1); + } + return (0); +} + +static int _4bri_is_rev_2_bri_card(int card_ordinal) +{ + switch (card_ordinal) { + case CARDTYPE_DIVASRV_B_2M_V2_PCI: + case CARDTYPE_DIVASRV_B_2F_PCI: + case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI: + return (1); + } + return (0); +} + +static void diva_4bri_set_addresses(diva_os_xdi_adapter_t *a) +{ + dword offset = a->resources.pci.qoffset; + dword c_offset = offset * a->xdi_adapter.ControllerNumber; + + a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 2; + a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 2; + a->resources.pci.mem_type_id[MEM_TYPE_CONTROL] = 2; + a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 0; + a->resources.pci.mem_type_id[MEM_TYPE_CTLREG] = 3; + a->resources.pci.mem_type_id[MEM_TYPE_PROM] = 0; + + /* + Set up hardware related pointers + */ + a->xdi_adapter.Address = a->resources.pci.addr[2]; /* BAR2 SDRAM */ + a->xdi_adapter.Address += c_offset; + + a->xdi_adapter.Control = a->resources.pci.addr[2]; /* BAR2 SDRAM */ + + a->xdi_adapter.ram = a->resources.pci.addr[2]; /* BAR2 SDRAM */ + a->xdi_adapter.ram += c_offset + (offset - MQ_SHARED_RAM_SIZE); + + a->xdi_adapter.reset = a->resources.pci.addr[0]; /* BAR0 CONFIG */ + /* + ctlReg contains the register address for the MIPS CPU reset control + */ + a->xdi_adapter.ctlReg = a->resources.pci.addr[3]; /* BAR3 CNTRL */ + /* + prom contains the register address for FPGA and EEPROM programming + */ + a->xdi_adapter.prom = &a->xdi_adapter.reset[0x6E]; +} + +/* +** BAR0 - MEM - 0x100 - CONFIG MEM +** BAR1 - I/O - 0x100 - UNUSED +** BAR2 - MEM - MQ_MEMORY_SIZE (MQ2_MEMORY_SIZE on Rev.2) - SDRAM +** BAR3 - MEM - 0x2000 (0x10000 on Rev.2) - CNTRL +** +** Called by master adapter, that will initialize and add slave adapters +*/ +int diva_4bri_init_card(diva_os_xdi_adapter_t *a) +{ + int bar, i; + byte __iomem *p; + PADAPTER_LIST_ENTRY quadro_list; + diva_os_xdi_adapter_t *diva_current; + diva_os_xdi_adapter_t *adapter_list[4]; + PISDN_ADAPTER Slave; + unsigned long bar_length[ARRAY_SIZE(_4bri_bar_length)]; + int v2 = _4bri_is_rev_2_card(a->CardOrdinal); + int tasks = _4bri_is_rev_2_bri_card(a->CardOrdinal) ? 1 : MQ_INSTANCE_COUNT; + int factor = (tasks == 1) ? 1 : 2; + + if (v2) { + if (_4bri_is_rev_2_bri_card(a->CardOrdinal)) { + memcpy(bar_length, _4bri_v2_bri_bar_length, + sizeof(bar_length)); + } else { + memcpy(bar_length, _4bri_v2_bar_length, + sizeof(bar_length)); + } + } else { + memcpy(bar_length, _4bri_bar_length, sizeof(bar_length)); + } + DBG_TRC(("SDRAM_LENGTH=%08x, tasks=%d, factor=%d", + bar_length[2], tasks, factor)) + + /* + Get Serial Number + The serial number of 4BRI is accessible in accordance with PCI spec + via command register located in configuration space, also we do not + have to map any BAR before we can access it + */ + if (!_4bri_get_serial_number(a)) { + DBG_ERR(("A: 4BRI can't get Serial Number")) + diva_4bri_cleanup_adapter(a); + return (-1); + } + + /* + Set properties + */ + a->xdi_adapter.Properties = CardProperties[a->CardOrdinal]; + DBG_LOG(("Load %s, SN:%ld, bus:%02x, func:%02x", + a->xdi_adapter.Properties.Name, + a->xdi_adapter.serialNo, + a->resources.pci.bus, a->resources.pci.func)) + + /* + First initialization step: get and check hardware resoures. + Do not map resources and do not access card at this step + */ + for (bar = 0; bar < 4; bar++) { + a->resources.pci.bar[bar] = + divasa_get_pci_bar(a->resources.pci.bus, + a->resources.pci.func, bar, + a->resources.pci.hdev); + if (!a->resources.pci.bar[bar] + || (a->resources.pci.bar[bar] == 0xFFFFFFF0)) { + DBG_ERR( + ("A: invalid bar[%d]=%08x", bar, + a->resources.pci.bar[bar])) + return (-1); + } + } + a->resources.pci.irq = + (byte) divasa_get_pci_irq(a->resources.pci.bus, + a->resources.pci.func, + a->resources.pci.hdev); + if (!a->resources.pci.irq) { + DBG_ERR(("A: invalid irq")); + return (-1); + } + + a->xdi_adapter.sdram_bar = a->resources.pci.bar[2]; + + /* + Map all MEMORY BAR's + */ + for (bar = 0; bar < 4; bar++) { + if (bar != 1) { /* ignore I/O */ + a->resources.pci.addr[bar] = + divasa_remap_pci_bar(a, bar, a->resources.pci.bar[bar], + bar_length[bar]); + if (!a->resources.pci.addr[bar]) { + DBG_ERR(("A: 4BRI: can't map bar[%d]", bar)) + diva_4bri_cleanup_adapter(a); + return (-1); + } + } + } + + /* + Register I/O port + */ + sprintf(&a->port_name[0], "DIVA 4BRI %ld", (long) a->xdi_adapter.serialNo); + + if (diva_os_register_io_port(a, 1, a->resources.pci.bar[1], + bar_length[1], &a->port_name[0], 1)) { + DBG_ERR(("A: 4BRI: can't register bar[1]")) + diva_4bri_cleanup_adapter(a); + return (-1); + } + + a->resources.pci.addr[1] = + (void *) (unsigned long) a->resources.pci.bar[1]; + + /* + Set cleanup pointer for base adapter only, so slave adapter + will be unable to get cleanup + */ + a->interface.cleanup_adapter_proc = diva_4bri_cleanup_adapter; + + /* + Create slave adapters + */ + if (tasks > 1) { + if (!(a->slave_adapters[0] = + (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a)))) + { + diva_4bri_cleanup_adapter(a); + return (-1); + } + if (!(a->slave_adapters[1] = + (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a)))) + { + diva_os_free(0, a->slave_adapters[0]); + a->slave_adapters[0] = NULL; + diva_4bri_cleanup_adapter(a); + return (-1); + } + if (!(a->slave_adapters[2] = + (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a)))) + { + diva_os_free(0, a->slave_adapters[0]); + diva_os_free(0, a->slave_adapters[1]); + a->slave_adapters[0] = NULL; + a->slave_adapters[1] = NULL; + diva_4bri_cleanup_adapter(a); + return (-1); + } + memset(a->slave_adapters[0], 0x00, sizeof(*a)); + memset(a->slave_adapters[1], 0x00, sizeof(*a)); + memset(a->slave_adapters[2], 0x00, sizeof(*a)); + } + + adapter_list[0] = a; + adapter_list[1] = a->slave_adapters[0]; + adapter_list[2] = a->slave_adapters[1]; + adapter_list[3] = a->slave_adapters[2]; + + /* + Allocate slave list + */ + quadro_list = + (PADAPTER_LIST_ENTRY) diva_os_malloc(0, sizeof(*quadro_list)); + if (!(a->slave_list = quadro_list)) { + for (i = 0; i < (tasks - 1); i++) { + diva_os_free(0, a->slave_adapters[i]); + a->slave_adapters[i] = NULL; + } + diva_4bri_cleanup_adapter(a); + return (-1); + } + memset(quadro_list, 0x00, sizeof(*quadro_list)); + + /* + Set interfaces + */ + a->xdi_adapter.QuadroList = quadro_list; + for (i = 0; i < tasks; i++) { + adapter_list[i]->xdi_adapter.ControllerNumber = i; + adapter_list[i]->xdi_adapter.tasks = tasks; + quadro_list->QuadroAdapter[i] = + &adapter_list[i]->xdi_adapter; + } + + for (i = 0; i < tasks; i++) { + diva_current = adapter_list[i]; + + diva_current->dsp_mask = 0x00000003; + + diva_current->xdi_adapter.a.io = + &diva_current->xdi_adapter; + diva_current->xdi_adapter.DIRequest = request; + diva_current->interface.cmd_proc = diva_4bri_cmd_card_proc; + diva_current->xdi_adapter.Properties = + CardProperties[a->CardOrdinal]; + diva_current->CardOrdinal = a->CardOrdinal; + + diva_current->xdi_adapter.Channels = + CardProperties[a->CardOrdinal].Channels; + diva_current->xdi_adapter.e_max = + CardProperties[a->CardOrdinal].E_info; + diva_current->xdi_adapter.e_tbl = + diva_os_malloc(0, + diva_current->xdi_adapter.e_max * + sizeof(E_INFO)); + + if (!diva_current->xdi_adapter.e_tbl) { + diva_4bri_cleanup_slave_adapters(a); + diva_4bri_cleanup_adapter(a); + for (i = 1; i < (tasks - 1); i++) { + diva_os_free(0, adapter_list[i]); + } + return (-1); + } + memset(diva_current->xdi_adapter.e_tbl, 0x00, + diva_current->xdi_adapter.e_max * sizeof(E_INFO)); + + if (diva_os_initialize_spin_lock(&diva_current->xdi_adapter.isr_spin_lock, "isr")) { + diva_4bri_cleanup_slave_adapters(a); + diva_4bri_cleanup_adapter(a); + for (i = 1; i < (tasks - 1); i++) { + diva_os_free(0, adapter_list[i]); + } + return (-1); + } + if (diva_os_initialize_spin_lock(&diva_current->xdi_adapter.data_spin_lock, "data")) { + diva_4bri_cleanup_slave_adapters(a); + diva_4bri_cleanup_adapter(a); + for (i = 1; i < (tasks - 1); i++) { + diva_os_free(0, adapter_list[i]); + } + return (-1); + } + + strcpy(diva_current->xdi_adapter.req_soft_isr. dpc_thread_name, "kdivas4brid"); + + if (diva_os_initialize_soft_isr(&diva_current->xdi_adapter.req_soft_isr, DIDpcRoutine, + &diva_current->xdi_adapter)) { + diva_4bri_cleanup_slave_adapters(a); + diva_4bri_cleanup_adapter(a); + for (i = 1; i < (tasks - 1); i++) { + diva_os_free(0, adapter_list[i]); + } + return (-1); + } + + /* + Do not initialize second DPC - only one thread will be created + */ + diva_current->xdi_adapter.isr_soft_isr.object = + diva_current->xdi_adapter.req_soft_isr.object; + } + + if (v2) { + prepare_qBri2_functions(&a->xdi_adapter); + } else { + prepare_qBri_functions(&a->xdi_adapter); + } + + for (i = 0; i < tasks; i++) { + diva_current = adapter_list[i]; + if (i) + memcpy(&diva_current->resources, &a->resources, sizeof(divas_card_resources_t)); + diva_current->resources.pci.qoffset = (a->xdi_adapter.MemorySize >> factor); + } + + /* + Set up hardware related pointers + */ + a->xdi_adapter.cfg = (void *) (unsigned long) a->resources.pci.bar[0]; /* BAR0 CONFIG */ + a->xdi_adapter.port = (void *) (unsigned long) a->resources.pci.bar[1]; /* BAR1 */ + a->xdi_adapter.ctlReg = (void *) (unsigned long) a->resources.pci.bar[3]; /* BAR3 CNTRL */ + + for (i = 0; i < tasks; i++) { + diva_current = adapter_list[i]; + diva_4bri_set_addresses(diva_current); + Slave = a->xdi_adapter.QuadroList->QuadroAdapter[i]; + Slave->MultiMaster = &a->xdi_adapter; + Slave->sdram_bar = a->xdi_adapter.sdram_bar; + if (i) { + Slave->serialNo = ((dword) (Slave->ControllerNumber << 24)) | + a->xdi_adapter.serialNo; + Slave->cardType = a->xdi_adapter.cardType; + } + } + + /* + reset contains the base address for the PLX 9054 register set + */ + p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter); + WRITE_BYTE(&p[PLX9054_INTCSR], 0x00); /* disable PCI interrupts */ + DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p); + + /* + Set IRQ handler + */ + a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq; + sprintf(a->xdi_adapter.irq_info.irq_name, "DIVA 4BRI %ld", + (long) a->xdi_adapter.serialNo); + + if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr, + a->xdi_adapter.irq_info.irq_name)) { + diva_4bri_cleanup_slave_adapters(a); + diva_4bri_cleanup_adapter(a); + for (i = 1; i < (tasks - 1); i++) { + diva_os_free(0, adapter_list[i]); + } + return (-1); + } + + a->xdi_adapter.irq_info.registered = 1; + + /* + Add three slave adapters + */ + if (tasks > 1) { + diva_add_slave_adapter(adapter_list[1]); + diva_add_slave_adapter(adapter_list[2]); + diva_add_slave_adapter(adapter_list[3]); + } + + diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name, + a->resources.pci.irq, a->xdi_adapter.serialNo); + + return (0); +} + +/* +** Cleanup function will be called for master adapter only +** this is guaranteed by design: cleanup callback is set +** by master adapter only +*/ +static int diva_4bri_cleanup_adapter(diva_os_xdi_adapter_t *a) +{ + int bar; + + /* + Stop adapter if running + */ + if (a->xdi_adapter.Initialized) { + diva_4bri_stop_adapter(a); + } + + /* + Remove IRQ handler + */ + if (a->xdi_adapter.irq_info.registered) { + diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr); + } + a->xdi_adapter.irq_info.registered = 0; + + /* + Free DPC's and spin locks on all adapters + */ + diva_4bri_cleanup_slave_adapters(a); + + /* + Unmap all BARS + */ + for (bar = 0; bar < 4; bar++) { + if (bar != 1) { + if (a->resources.pci.bar[bar] + && a->resources.pci.addr[bar]) { + divasa_unmap_pci_bar(a->resources.pci.addr[bar]); + a->resources.pci.bar[bar] = 0; + a->resources.pci.addr[bar] = NULL; + } + } + } + + /* + Unregister I/O + */ + if (a->resources.pci.bar[1] && a->resources.pci.addr[1]) { + diva_os_register_io_port(a, 0, a->resources.pci.bar[1], + _4bri_is_rev_2_card(a-> + CardOrdinal) ? + _4bri_v2_bar_length[1] : + _4bri_bar_length[1], + &a->port_name[0], 1); + a->resources.pci.bar[1] = 0; + a->resources.pci.addr[1] = NULL; + } + + if (a->slave_list) { + diva_os_free(0, a->slave_list); + a->slave_list = NULL; + } + + return (0); +} + +static int _4bri_get_serial_number(diva_os_xdi_adapter_t *a) +{ + dword data[64]; + dword serNo; + word addr, status, i, j; + byte Bus, Slot; + void *hdev; + + Bus = a->resources.pci.bus; + Slot = a->resources.pci.func; + hdev = a->resources.pci.hdev; + + for (i = 0; i < 64; ++i) { + addr = i * 4; + for (j = 0; j < 5; ++j) { + PCIwrite(Bus, Slot, 0x4E, &addr, sizeof(addr), + hdev); + diva_os_wait(1); + PCIread(Bus, Slot, 0x4E, &status, sizeof(status), + hdev); + if (status & 0x8000) + break; + } + if (j >= 5) { + DBG_ERR(("EEPROM[%d] read failed (0x%x)", i * 4, addr)) + return (0); + } + PCIread(Bus, Slot, 0x50, &data[i], sizeof(data[i]), hdev); + } + DBG_BLK(((char *) &data[0], sizeof(data))) + + serNo = data[32]; + if (serNo == 0 || serNo == 0xffffffff) + serNo = data[63]; + + if (!serNo) { + DBG_LOG(("W: Serial Number == 0, create one serial number")); + serNo = a->resources.pci.bar[1] & 0xffff0000; + serNo |= a->resources.pci.bus << 8; + serNo |= a->resources.pci.func; + } + + a->xdi_adapter.serialNo = serNo; + + DBG_REG(("Serial No. : %ld", a->xdi_adapter.serialNo)) + + return (serNo); +} + +/* +** Release resources of slave adapters +*/ +static int diva_4bri_cleanup_slave_adapters(diva_os_xdi_adapter_t *a) +{ + diva_os_xdi_adapter_t *adapter_list[4]; + diva_os_xdi_adapter_t *diva_current; + int i; + + adapter_list[0] = a; + adapter_list[1] = a->slave_adapters[0]; + adapter_list[2] = a->slave_adapters[1]; + adapter_list[3] = a->slave_adapters[2]; + + for (i = 0; i < a->xdi_adapter.tasks; i++) { + diva_current = adapter_list[i]; + if (diva_current) { + diva_os_destroy_spin_lock(&diva_current-> + xdi_adapter. + isr_spin_lock, "unload"); + diva_os_destroy_spin_lock(&diva_current-> + xdi_adapter. + data_spin_lock, + "unload"); + + diva_os_cancel_soft_isr(&diva_current->xdi_adapter. + req_soft_isr); + diva_os_cancel_soft_isr(&diva_current->xdi_adapter. + isr_soft_isr); + + diva_os_remove_soft_isr(&diva_current->xdi_adapter. + req_soft_isr); + diva_current->xdi_adapter.isr_soft_isr.object = NULL; + + if (diva_current->xdi_adapter.e_tbl) { + diva_os_free(0, + diva_current->xdi_adapter. + e_tbl); + } + diva_current->xdi_adapter.e_tbl = NULL; + diva_current->xdi_adapter.e_max = 0; + diva_current->xdi_adapter.e_count = 0; + } + } + + return (0); +} + +static int +diva_4bri_cmd_card_proc(struct _diva_os_xdi_adapter *a, + diva_xdi_um_cfg_cmd_t *cmd, int length) +{ + int ret = -1; + + if (cmd->adapter != a->controller) { + DBG_ERR(("A: 4bri_cmd, invalid controller=%d != %d", + cmd->adapter, a->controller)) + return (-1); + } + + switch (cmd->command) { + case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL: + a->xdi_mbox.data_length = sizeof(dword); + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + *(dword *) a->xdi_mbox.data = + (dword) a->CardOrdinal; + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_GET_SERIAL_NR: + a->xdi_mbox.data_length = sizeof(dword); + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + *(dword *) a->xdi_mbox.data = + (dword) a->xdi_adapter.serialNo; + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG: + if (!a->xdi_adapter.ControllerNumber) { + /* + Only master adapter can access hardware config + */ + a->xdi_mbox.data_length = sizeof(dword) * 9; + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + int i; + dword *data = (dword *) a->xdi_mbox.data; + + for (i = 0; i < 8; i++) { + *data++ = a->resources.pci.bar[i]; + } + *data++ = (dword) a->resources.pci.irq; + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + } + break; + + case DIVA_XDI_UM_CMD_GET_CARD_STATE: + if (!a->xdi_adapter.ControllerNumber) { + a->xdi_mbox.data_length = sizeof(dword); + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + dword *data = (dword *) a->xdi_mbox.data; + if (!a->xdi_adapter.ram + || !a->xdi_adapter.reset + || !a->xdi_adapter.cfg) { + *data = 3; + } else if (a->xdi_adapter.trapped) { + *data = 2; + } else if (a->xdi_adapter.Initialized) { + *data = 1; + } else { + *data = 0; + } + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + } + break; + + case DIVA_XDI_UM_CMD_WRITE_FPGA: + if (!a->xdi_adapter.ControllerNumber) { + ret = + diva_4bri_write_fpga_image(a, + (byte *)&cmd[1], + cmd->command_data. + write_fpga. + image_length); + } + break; + + case DIVA_XDI_UM_CMD_RESET_ADAPTER: + if (!a->xdi_adapter.ControllerNumber) { + ret = diva_4bri_reset_adapter(&a->xdi_adapter); + } + break; + + case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK: + if (!a->xdi_adapter.ControllerNumber) { + ret = diva_4bri_write_sdram_block(&a->xdi_adapter, + cmd-> + command_data. + write_sdram. + offset, + (byte *) & + cmd[1], + cmd-> + command_data. + write_sdram. + length, + a->xdi_adapter. + MemorySize); + } + break; + + case DIVA_XDI_UM_CMD_START_ADAPTER: + if (!a->xdi_adapter.ControllerNumber) { + ret = diva_4bri_start_adapter(&a->xdi_adapter, + cmd->command_data. + start.offset, + cmd->command_data. + start.features); + } + break; + + case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES: + if (!a->xdi_adapter.ControllerNumber) { + a->xdi_adapter.features = + cmd->command_data.features.features; + a->xdi_adapter.a.protocol_capabilities = + a->xdi_adapter.features; + DBG_TRC(("Set raw protocol features (%08x)", + a->xdi_adapter.features)) + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_STOP_ADAPTER: + if (!a->xdi_adapter.ControllerNumber) { + ret = diva_4bri_stop_adapter(a); + } + break; + + case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY: + ret = diva_card_read_xlog(a); + break; + + case DIVA_XDI_UM_CMD_READ_SDRAM: + if (!a->xdi_adapter.ControllerNumber + && a->xdi_adapter.Address) { + if ( + (a->xdi_mbox.data_length = + cmd->command_data.read_sdram.length)) { + if ( + (a->xdi_mbox.data_length + + cmd->command_data.read_sdram.offset) < + a->xdi_adapter.MemorySize) { + a->xdi_mbox.data = + diva_os_malloc(0, + a->xdi_mbox. + data_length); + if (a->xdi_mbox.data) { + byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(&a->xdi_adapter); + byte __iomem *src = p; + byte *dst = a->xdi_mbox.data; + dword len = a->xdi_mbox.data_length; + + src += cmd->command_data.read_sdram.offset; + + while (len--) { + *dst++ = READ_BYTE(src++); + } + DIVA_OS_MEM_DETACH_ADDRESS(&a->xdi_adapter, p); + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + } + } + } + break; + + default: + DBG_ERR(("A: A(%d) invalid cmd=%d", a->controller, + cmd->command)) + } + + return (ret); +} + +void *xdiLoadFile(char *FileName, dword *FileLength, + unsigned long lim) +{ + void *ret = diva_xdiLoadFileFile; + + if (FileLength) { + *FileLength = diva_xdiLoadFileLength; + } + diva_xdiLoadFileFile = NULL; + diva_xdiLoadFileLength = 0; + + return (ret); +} + +void diva_os_set_qBri_functions(PISDN_ADAPTER IoAdapter) +{ +} + +void diva_os_set_qBri2_functions(PISDN_ADAPTER IoAdapter) +{ +} + +static int +diva_4bri_write_fpga_image(diva_os_xdi_adapter_t *a, byte *data, + dword length) +{ + int ret; + + diva_xdiLoadFileFile = data; + diva_xdiLoadFileLength = length; + + ret = qBri_FPGA_download(&a->xdi_adapter); + + diva_xdiLoadFileFile = NULL; + diva_xdiLoadFileLength = 0; + + return (ret ? 0 : -1); +} + +static int diva_4bri_reset_adapter(PISDN_ADAPTER IoAdapter) +{ + PISDN_ADAPTER Slave; + int i; + + if (!IoAdapter->Address || !IoAdapter->reset) { + return (-1); + } + if (IoAdapter->Initialized) { + DBG_ERR(("A: A(%d) can't reset 4BRI adapter - please stop first", + IoAdapter->ANum)) + return (-1); + } + + /* + Forget all entities on all adapters + */ + for (i = 0; ((i < IoAdapter->tasks) && IoAdapter->QuadroList); i++) { + Slave = IoAdapter->QuadroList->QuadroAdapter[i]; + Slave->e_count = 0; + if (Slave->e_tbl) { + memset(Slave->e_tbl, 0x00, + Slave->e_max * sizeof(E_INFO)); + } + Slave->head = 0; + Slave->tail = 0; + Slave->assign = 0; + Slave->trapped = 0; + + memset(&Slave->a.IdTable[0], 0x00, + sizeof(Slave->a.IdTable)); + memset(&Slave->a.IdTypeTable[0], 0x00, + sizeof(Slave->a.IdTypeTable)); + memset(&Slave->a.FlowControlIdTable[0], 0x00, + sizeof(Slave->a.FlowControlIdTable)); + memset(&Slave->a.FlowControlSkipTable[0], 0x00, + sizeof(Slave->a.FlowControlSkipTable)); + memset(&Slave->a.misc_flags_table[0], 0x00, + sizeof(Slave->a.misc_flags_table)); + memset(&Slave->a.rx_stream[0], 0x00, + sizeof(Slave->a.rx_stream)); + memset(&Slave->a.tx_stream[0], 0x00, + sizeof(Slave->a.tx_stream)); + memset(&Slave->a.tx_pos[0], 0x00, sizeof(Slave->a.tx_pos)); + memset(&Slave->a.rx_pos[0], 0x00, sizeof(Slave->a.rx_pos)); + } + + return (0); +} + + +static int +diva_4bri_write_sdram_block(PISDN_ADAPTER IoAdapter, + dword address, + const byte *data, dword length, dword limit) +{ + byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter); + byte __iomem *mem = p; + + if (((address + length) >= limit) || !mem) { + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p); + DBG_ERR(("A: A(%d) write 4BRI address=0x%08lx", + IoAdapter->ANum, address + length)) + return (-1); + } + mem += address; + + while (length--) { + WRITE_BYTE(mem++, *data++); + } + + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p); + return (0); +} + +static int +diva_4bri_start_adapter(PISDN_ADAPTER IoAdapter, + dword start_address, dword features) +{ + volatile word __iomem *signature; + int started = 0; + int i; + byte __iomem *p; + + /* + start adapter + */ + start_qBri_hardware(IoAdapter); + + p = DIVA_OS_MEM_ATTACH_RAM(IoAdapter); + /* + wait for signature in shared memory (max. 3 seconds) + */ + signature = (volatile word __iomem *) (&p[0x1E]); + + for (i = 0; i < 300; ++i) { + diva_os_wait(10); + if (READ_WORD(&signature[0]) == 0x4447) { + DBG_TRC(("Protocol startup time %d.%02d seconds", + (i / 100), (i % 100))) + started = 1; + break; + } + } + + for (i = 1; i < IoAdapter->tasks; i++) { + IoAdapter->QuadroList->QuadroAdapter[i]->features = + IoAdapter->features; + IoAdapter->QuadroList->QuadroAdapter[i]->a. + protocol_capabilities = IoAdapter->features; + } + + if (!started) { + DBG_FTL(("%s: Adapter selftest failed, signature=%04x", + IoAdapter->Properties.Name, + READ_WORD(&signature[0]))) + DIVA_OS_MEM_DETACH_RAM(IoAdapter, p); + (*(IoAdapter->trapFnc)) (IoAdapter); + IoAdapter->stop(IoAdapter); + return (-1); + } + DIVA_OS_MEM_DETACH_RAM(IoAdapter, p); + + for (i = 0; i < IoAdapter->tasks; i++) { + IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 1; + IoAdapter->QuadroList->QuadroAdapter[i]->IrqCount = 0; + } + + if (check_qBri_interrupt(IoAdapter)) { + DBG_ERR(("A: A(%d) interrupt test failed", + IoAdapter->ANum)) + for (i = 0; i < IoAdapter->tasks; i++) { + IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 0; + } + IoAdapter->stop(IoAdapter); + return (-1); + } + + IoAdapter->Properties.Features = (word) features; + diva_xdi_display_adapter_features(IoAdapter->ANum); + + for (i = 0; i < IoAdapter->tasks; i++) { + DBG_LOG(("A(%d) %s adapter successfully started", + IoAdapter->QuadroList->QuadroAdapter[i]->ANum, + (IoAdapter->tasks == 1) ? "BRI 2.0" : "4BRI")) + diva_xdi_didd_register_adapter(IoAdapter->QuadroList->QuadroAdapter[i]->ANum); + IoAdapter->QuadroList->QuadroAdapter[i]->Properties.Features = (word) features; + } + + return (0); +} + +static int check_qBri_interrupt(PISDN_ADAPTER IoAdapter) +{ +#ifdef SUPPORT_INTERRUPT_TEST_ON_4BRI + int i; + ADAPTER *a = &IoAdapter->a; + byte __iomem *p; + + IoAdapter->IrqCount = 0; + + if (IoAdapter->ControllerNumber > 0) + return (-1); + + p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter); + WRITE_BYTE(&p[PLX9054_INTCSR], PLX9054_INT_ENABLE); + DIVA_OS_MEM_DETACH_RESET(IoAdapter, p); + /* + interrupt test + */ + a->ReadyInt = 1; + a->ram_out(a, &PR_RAM->ReadyInt, 1); + + for (i = 100; !IoAdapter->IrqCount && (i-- > 0); diva_os_wait(10)); + + return ((IoAdapter->IrqCount > 0) ? 0 : -1); +#else + dword volatile __iomem *qBriIrq; + byte __iomem *p; + /* + Reset on-board interrupt register + */ + IoAdapter->IrqCount = 0; + p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + qBriIrq = (dword volatile __iomem *) (&p[_4bri_is_rev_2_card + (IoAdapter-> + cardType) ? (MQ2_BREG_IRQ_TEST) + : (MQ_BREG_IRQ_TEST)]); + + WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF); + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); + + p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter); + WRITE_BYTE(&p[PLX9054_INTCSR], PLX9054_INT_ENABLE); + DIVA_OS_MEM_DETACH_RESET(IoAdapter, p); + + diva_os_wait(100); + + return (0); +#endif /* SUPPORT_INTERRUPT_TEST_ON_4BRI */ +} + +static void diva_4bri_clear_interrupts(diva_os_xdi_adapter_t *a) +{ + PISDN_ADAPTER IoAdapter = &a->xdi_adapter; + + /* + clear any pending interrupt + */ + IoAdapter->disIrq(IoAdapter); + + IoAdapter->tst_irq(&IoAdapter->a); + IoAdapter->clr_irq(&IoAdapter->a); + IoAdapter->tst_irq(&IoAdapter->a); + + /* + kill pending dpcs + */ + diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr); + diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr); +} + +static int diva_4bri_stop_adapter(diva_os_xdi_adapter_t *a) +{ + PISDN_ADAPTER IoAdapter = &a->xdi_adapter; + int i; + + if (!IoAdapter->ram) { + return (-1); + } + + if (!IoAdapter->Initialized) { + DBG_ERR(("A: A(%d) can't stop PRI adapter - not running", + IoAdapter->ANum)) + return (-1); /* nothing to stop */ + } + + for (i = 0; i < IoAdapter->tasks; i++) { + IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 0; + } + + /* + Disconnect Adapters from DIDD + */ + for (i = 0; i < IoAdapter->tasks; i++) { + diva_xdi_didd_remove_adapter(IoAdapter->QuadroList->QuadroAdapter[i]->ANum); + } + + i = 100; + + /* + Stop interrupts + */ + a->clear_interrupts_proc = diva_4bri_clear_interrupts; + IoAdapter->a.ReadyInt = 1; + IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt); + do { + diva_os_sleep(10); + } while (i-- && a->clear_interrupts_proc); + + if (a->clear_interrupts_proc) { + diva_4bri_clear_interrupts(a); + a->clear_interrupts_proc = NULL; + DBG_ERR(("A: A(%d) no final interrupt from 4BRI adapter", + IoAdapter->ANum)) + } + IoAdapter->a.ReadyInt = 0; + + /* + Stop and reset adapter + */ + IoAdapter->stop(IoAdapter); + + return (0); +} diff --git a/drivers/isdn/hardware/eicon/os_4bri.h b/drivers/isdn/hardware/eicon/os_4bri.h new file mode 100644 index 000000000..94b270953 --- /dev/null +++ b/drivers/isdn/hardware/eicon/os_4bri.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: os_4bri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */ + +#ifndef __DIVA_OS_4_BRI_H__ +#define __DIVA_OS_4_BRI_H__ + +int diva_4bri_init_card(diva_os_xdi_adapter_t *a); + +#endif diff --git a/drivers/isdn/hardware/eicon/os_bri.c b/drivers/isdn/hardware/eicon/os_bri.c new file mode 100644 index 000000000..de93090bc --- /dev/null +++ b/drivers/isdn/hardware/eicon/os_bri.c @@ -0,0 +1,815 @@ +// SPDX-License-Identifier: GPL-2.0 +/* $Id: os_bri.c,v 1.21 2004/03/21 17:26:01 armin Exp $ */ + +#include "platform.h" +#include "debuglib.h" +#include "cardtype.h" +#include "pc.h" +#include "pr_pc.h" +#include "di_defs.h" +#include "dsp_defs.h" +#include "di.h" +#include "io.h" + +#include "xdi_msg.h" +#include "xdi_adapter.h" +#include "os_bri.h" +#include "diva_pci.h" +#include "mi_pc.h" +#include "pc_maint.h" +#include "dsrv_bri.h" + +/* +** IMPORTS +*/ +extern void prepare_maestra_functions(PISDN_ADAPTER IoAdapter); +extern void diva_xdi_display_adapter_features(int card); +extern int diva_card_read_xlog(diva_os_xdi_adapter_t *a); + +/* +** LOCALS +*/ +static int bri_bar_length[3] = { + 0x80, + 0x80, + 0x20 +}; +static int diva_bri_cleanup_adapter(diva_os_xdi_adapter_t *a); +static dword diva_bri_get_serial_number(diva_os_xdi_adapter_t *a); +static int diva_bri_cmd_card_proc(struct _diva_os_xdi_adapter *a, + diva_xdi_um_cfg_cmd_t *cmd, int length); +static int diva_bri_reregister_io(diva_os_xdi_adapter_t *a); +static int diva_bri_reset_adapter(PISDN_ADAPTER IoAdapter); +static int diva_bri_write_sdram_block(PISDN_ADAPTER IoAdapter, + dword address, + const byte *data, dword length); +static int diva_bri_start_adapter(PISDN_ADAPTER IoAdapter, + dword start_address, dword features); +static int diva_bri_stop_adapter(diva_os_xdi_adapter_t *a); + +static void diva_bri_set_addresses(diva_os_xdi_adapter_t *a) +{ + a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 0; + a->resources.pci.mem_type_id[MEM_TYPE_CFG] = 1; + a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 2; + a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 1; + a->resources.pci.mem_type_id[MEM_TYPE_PORT] = 2; + a->resources.pci.mem_type_id[MEM_TYPE_CTLREG] = 2; + + a->xdi_adapter.ram = a->resources.pci.addr[0]; + a->xdi_adapter.cfg = a->resources.pci.addr[1]; + a->xdi_adapter.Address = a->resources.pci.addr[2]; + + a->xdi_adapter.reset = a->xdi_adapter.cfg; + a->xdi_adapter.port = a->xdi_adapter.Address; + + a->xdi_adapter.ctlReg = a->xdi_adapter.port + M_PCI_RESET; + + a->xdi_adapter.reset += 0x4C; /* PLX 9050 !! */ +} + +/* +** BAR0 - MEM Addr - 0x80 - NOT USED +** BAR1 - I/O Addr - 0x80 +** BAR2 - I/O Addr - 0x20 +*/ +int diva_bri_init_card(diva_os_xdi_adapter_t *a) +{ + int bar; + dword bar2 = 0, bar2_length = 0xffffffff; + word cmd = 0, cmd_org; + byte Bus, Slot; + void *hdev; + byte __iomem *p; + + /* + Set properties + */ + a->xdi_adapter.Properties = CardProperties[a->CardOrdinal]; + DBG_LOG(("Load %s", a->xdi_adapter.Properties.Name)) + + /* + Get resources + */ + for (bar = 0; bar < 3; bar++) { + a->resources.pci.bar[bar] = + divasa_get_pci_bar(a->resources.pci.bus, + a->resources.pci.func, bar, + a->resources.pci.hdev); + if (!a->resources.pci.bar[bar]) { + DBG_ERR(("A: can't get BAR[%d]", bar)) + return (-1); + } + } + + a->resources.pci.irq = + (byte) divasa_get_pci_irq(a->resources.pci.bus, + a->resources.pci.func, + a->resources.pci.hdev); + if (!a->resources.pci.irq) { + DBG_ERR(("A: invalid irq")); + return (-1); + } + + /* + Get length of I/O bar 2 - it is different by older + EEPROM version + */ + Bus = a->resources.pci.bus; + Slot = a->resources.pci.func; + hdev = a->resources.pci.hdev; + + /* + Get plain original values of the BAR2 CDM registers + */ + PCIread(Bus, Slot, 0x18, &bar2, sizeof(bar2), hdev); + PCIread(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev); + /* + Disable device and get BAR2 length + */ + PCIwrite(Bus, Slot, 0x04, &cmd, sizeof(cmd), hdev); + PCIwrite(Bus, Slot, 0x18, &bar2_length, sizeof(bar2_length), hdev); + PCIread(Bus, Slot, 0x18, &bar2_length, sizeof(bar2_length), hdev); + /* + Restore BAR2 and CMD registers + */ + PCIwrite(Bus, Slot, 0x18, &bar2, sizeof(bar2), hdev); + PCIwrite(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev); + + /* + Calculate BAR2 length + */ + bar2_length = (~(bar2_length & ~7)) + 1; + DBG_LOG(("BAR[2] length=%lx", bar2_length)) + + /* + Map and register resources + */ + if (!(a->resources.pci.addr[0] = + divasa_remap_pci_bar(a, 0, a->resources.pci.bar[0], + bri_bar_length[0]))) { + DBG_ERR(("A: BRI, can't map BAR[0]")) + diva_bri_cleanup_adapter(a); + return (-1); + } + + sprintf(&a->port_name[0], "BRI %02x:%02x", + a->resources.pci.bus, a->resources.pci.func); + + if (diva_os_register_io_port(a, 1, a->resources.pci.bar[1], + bri_bar_length[1], &a->port_name[0], 1)) { + DBG_ERR(("A: BRI, can't register BAR[1]")) + diva_bri_cleanup_adapter(a); + return (-1); + } + a->resources.pci.addr[1] = (void *) (unsigned long) a->resources.pci.bar[1]; + a->resources.pci.length[1] = bri_bar_length[1]; + + if (diva_os_register_io_port(a, 1, a->resources.pci.bar[2], + bar2_length, &a->port_name[0], 2)) { + DBG_ERR(("A: BRI, can't register BAR[2]")) + diva_bri_cleanup_adapter(a); + return (-1); + } + a->resources.pci.addr[2] = (void *) (unsigned long) a->resources.pci.bar[2]; + a->resources.pci.length[2] = bar2_length; + + /* + Set all memory areas + */ + diva_bri_set_addresses(a); + + /* + Get Serial Number + */ + a->xdi_adapter.serialNo = diva_bri_get_serial_number(a); + + /* + Register I/O ports with correct name now + */ + if (diva_bri_reregister_io(a)) { + diva_bri_cleanup_adapter(a); + return (-1); + } + + /* + Initialize OS dependent objects + */ + if (diva_os_initialize_spin_lock + (&a->xdi_adapter.isr_spin_lock, "isr")) { + diva_bri_cleanup_adapter(a); + return (-1); + } + if (diva_os_initialize_spin_lock + (&a->xdi_adapter.data_spin_lock, "data")) { + diva_bri_cleanup_adapter(a); + return (-1); + } + + strcpy(a->xdi_adapter.req_soft_isr.dpc_thread_name, "kdivasbrid"); + + if (diva_os_initialize_soft_isr(&a->xdi_adapter.req_soft_isr, + DIDpcRoutine, &a->xdi_adapter)) { + diva_bri_cleanup_adapter(a); + return (-1); + } + /* + Do not initialize second DPC - only one thread will be created + */ + a->xdi_adapter.isr_soft_isr.object = a->xdi_adapter.req_soft_isr.object; + + /* + Create entity table + */ + a->xdi_adapter.Channels = CardProperties[a->CardOrdinal].Channels; + a->xdi_adapter.e_max = CardProperties[a->CardOrdinal].E_info; + a->xdi_adapter.e_tbl = diva_os_malloc(0, a->xdi_adapter.e_max * sizeof(E_INFO)); + if (!a->xdi_adapter.e_tbl) { + diva_bri_cleanup_adapter(a); + return (-1); + } + memset(a->xdi_adapter.e_tbl, 0x00, a->xdi_adapter.e_max * sizeof(E_INFO)); + + /* + Set up interface + */ + a->xdi_adapter.a.io = &a->xdi_adapter; + a->xdi_adapter.DIRequest = request; + a->interface.cleanup_adapter_proc = diva_bri_cleanup_adapter; + a->interface.cmd_proc = diva_bri_cmd_card_proc; + + p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter); + outpp(p, 0x41); + DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p); + + prepare_maestra_functions(&a->xdi_adapter); + + a->dsp_mask = 0x00000003; + + /* + Set IRQ handler + */ + a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq; + sprintf(a->xdi_adapter.irq_info.irq_name, "DIVA BRI %ld", + (long) a->xdi_adapter.serialNo); + if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr, + a->xdi_adapter.irq_info.irq_name)) { + diva_bri_cleanup_adapter(a); + return (-1); + } + a->xdi_adapter.irq_info.registered = 1; + + diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name, + a->resources.pci.irq, a->xdi_adapter.serialNo); + + return (0); +} + + +static int diva_bri_cleanup_adapter(diva_os_xdi_adapter_t *a) +{ + int i; + + if (a->xdi_adapter.Initialized) { + diva_bri_stop_adapter(a); + } + + /* + Remove ISR Handler + */ + if (a->xdi_adapter.irq_info.registered) { + diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr); + } + a->xdi_adapter.irq_info.registered = 0; + + if (a->resources.pci.addr[0] && a->resources.pci.bar[0]) { + divasa_unmap_pci_bar(a->resources.pci.addr[0]); + a->resources.pci.addr[0] = NULL; + a->resources.pci.bar[0] = 0; + } + + for (i = 1; i < 3; i++) { + if (a->resources.pci.addr[i] && a->resources.pci.bar[i]) { + diva_os_register_io_port(a, 0, + a->resources.pci.bar[i], + a->resources.pci. + length[i], + &a->port_name[0], i); + a->resources.pci.addr[i] = NULL; + a->resources.pci.bar[i] = 0; + } + } + + /* + Free OS objects + */ + diva_os_cancel_soft_isr(&a->xdi_adapter.req_soft_isr); + diva_os_cancel_soft_isr(&a->xdi_adapter.isr_soft_isr); + + diva_os_remove_soft_isr(&a->xdi_adapter.req_soft_isr); + a->xdi_adapter.isr_soft_isr.object = NULL; + + diva_os_destroy_spin_lock(&a->xdi_adapter.isr_spin_lock, "rm"); + diva_os_destroy_spin_lock(&a->xdi_adapter.data_spin_lock, "rm"); + + /* + Free memory + */ + if (a->xdi_adapter.e_tbl) { + diva_os_free(0, a->xdi_adapter.e_tbl); + a->xdi_adapter.e_tbl = NULL; + } + + return (0); +} + +void diva_os_prepare_maestra_functions(PISDN_ADAPTER IoAdapter) +{ +} + +/* +** Get serial number +*/ +static dword diva_bri_get_serial_number(diva_os_xdi_adapter_t *a) +{ + dword serNo = 0; + byte __iomem *confIO; + word serHi, serLo; + word __iomem *confMem; + + confIO = DIVA_OS_MEM_ATTACH_CFG(&a->xdi_adapter); + serHi = (word) (inppw(&confIO[0x22]) & 0x0FFF); + serLo = (word) (inppw(&confIO[0x26]) & 0x0FFF); + serNo = ((dword) serHi << 16) | (dword) serLo; + DIVA_OS_MEM_DETACH_CFG(&a->xdi_adapter, confIO); + + if ((serNo == 0) || (serNo == 0xFFFFFFFF)) { + DBG_FTL(("W: BRI use BAR[0] to get card serial number")) + + confMem = (word __iomem *)DIVA_OS_MEM_ATTACH_RAM(&a->xdi_adapter); + serHi = (word) (READ_WORD(&confMem[0x11]) & 0x0FFF); + serLo = (word) (READ_WORD(&confMem[0x13]) & 0x0FFF); + serNo = (((dword) serHi) << 16) | ((dword) serLo); + DIVA_OS_MEM_DETACH_RAM(&a->xdi_adapter, confMem); + } + + DBG_LOG(("Serial Number=%ld", serNo)) + + return (serNo); +} + +/* +** Unregister I/O and register it with new name, +** based on Serial Number +*/ +static int diva_bri_reregister_io(diva_os_xdi_adapter_t *a) +{ + int i; + + for (i = 1; i < 3; i++) { + diva_os_register_io_port(a, 0, a->resources.pci.bar[i], + a->resources.pci.length[i], + &a->port_name[0], i); + a->resources.pci.addr[i] = NULL; + } + + sprintf(a->port_name, "DIVA BRI %ld", + (long) a->xdi_adapter.serialNo); + + for (i = 1; i < 3; i++) { + if (diva_os_register_io_port(a, 1, a->resources.pci.bar[i], + a->resources.pci.length[i], + &a->port_name[0], i)) { + DBG_ERR(("A: failed to reregister BAR[%d]", i)) + return (-1); + } + a->resources.pci.addr[i] = + (void *) (unsigned long) a->resources.pci.bar[i]; + } + + return (0); +} + +/* +** Process command from user mode +*/ +static int +diva_bri_cmd_card_proc(struct _diva_os_xdi_adapter *a, + diva_xdi_um_cfg_cmd_t *cmd, int length) +{ + int ret = -1; + + if (cmd->adapter != a->controller) { + DBG_ERR(("A: pri_cmd, invalid controller=%d != %d", + cmd->adapter, a->controller)) + return (-1); + } + + switch (cmd->command) { + case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL: + a->xdi_mbox.data_length = sizeof(dword); + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + *(dword *) a->xdi_mbox.data = + (dword) a->CardOrdinal; + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_GET_SERIAL_NR: + a->xdi_mbox.data_length = sizeof(dword); + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + *(dword *) a->xdi_mbox.data = + (dword) a->xdi_adapter.serialNo; + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG: + a->xdi_mbox.data_length = sizeof(dword) * 9; + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + int i; + dword *data = (dword *) a->xdi_mbox.data; + + for (i = 0; i < 8; i++) { + *data++ = a->resources.pci.bar[i]; + } + *data++ = (dword) a->resources.pci.irq; + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_GET_CARD_STATE: + a->xdi_mbox.data_length = sizeof(dword); + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + dword *data = (dword *) a->xdi_mbox.data; + if (!a->xdi_adapter.port) { + *data = 3; + } else if (a->xdi_adapter.trapped) { + *data = 2; + } else if (a->xdi_adapter.Initialized) { + *data = 1; + } else { + *data = 0; + } + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_RESET_ADAPTER: + ret = diva_bri_reset_adapter(&a->xdi_adapter); + break; + + case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK: + ret = diva_bri_write_sdram_block(&a->xdi_adapter, + cmd->command_data. + write_sdram.offset, + (byte *)&cmd[1], + cmd->command_data. + write_sdram.length); + break; + + case DIVA_XDI_UM_CMD_START_ADAPTER: + ret = diva_bri_start_adapter(&a->xdi_adapter, + cmd->command_data.start. + offset, + cmd->command_data.start. + features); + break; + + case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES: + a->xdi_adapter.features = + cmd->command_data.features.features; + a->xdi_adapter.a.protocol_capabilities = + a->xdi_adapter.features; + DBG_TRC( + ("Set raw protocol features (%08x)", + a->xdi_adapter.features)) ret = 0; + break; + + case DIVA_XDI_UM_CMD_STOP_ADAPTER: + ret = diva_bri_stop_adapter(a); + break; + + case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY: + ret = diva_card_read_xlog(a); + break; + + default: + DBG_ERR( + ("A: A(%d) invalid cmd=%d", a->controller, + cmd->command))} + + return (ret); +} + +static int diva_bri_reset_adapter(PISDN_ADAPTER IoAdapter) +{ + byte __iomem *addrHi, *addrLo, *ioaddr; + dword i; + byte __iomem *Port; + + if (!IoAdapter->port) { + return (-1); + } + if (IoAdapter->Initialized) { + DBG_ERR(("A: A(%d) can't reset BRI adapter - please stop first", + IoAdapter->ANum)) return (-1); + } + (*(IoAdapter->rstFnc)) (IoAdapter); + diva_os_wait(100); + Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter); + addrHi = Port + + ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH); + addrLo = Port + ADDR; + ioaddr = Port + DATA; + /* + recover + */ + outpp(addrHi, (byte) 0); + outppw(addrLo, (word) 0); + outppw(ioaddr, (word) 0); + /* + clear shared memory + */ + outpp(addrHi, + (byte) ( + (IoAdapter->MemoryBase + IoAdapter->MemorySize - + BRI_SHARED_RAM_SIZE) >> 16)); + outppw(addrLo, 0); + for (i = 0; i < 0x8000; outppw(ioaddr, 0), ++i); + diva_os_wait(100); + + /* + clear signature + */ + outpp(addrHi, + (byte) ( + (IoAdapter->MemoryBase + IoAdapter->MemorySize - + BRI_SHARED_RAM_SIZE) >> 16)); + outppw(addrLo, 0x1e); + outpp(ioaddr, 0); + outpp(ioaddr, 0); + + outpp(addrHi, (byte) 0); + outppw(addrLo, (word) 0); + outppw(ioaddr, (word) 0); + + DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port); + + /* + Forget all outstanding entities + */ + IoAdapter->e_count = 0; + if (IoAdapter->e_tbl) { + memset(IoAdapter->e_tbl, 0x00, + IoAdapter->e_max * sizeof(E_INFO)); + } + IoAdapter->head = 0; + IoAdapter->tail = 0; + IoAdapter->assign = 0; + IoAdapter->trapped = 0; + + memset(&IoAdapter->a.IdTable[0], 0x00, + sizeof(IoAdapter->a.IdTable)); + memset(&IoAdapter->a.IdTypeTable[0], 0x00, + sizeof(IoAdapter->a.IdTypeTable)); + memset(&IoAdapter->a.FlowControlIdTable[0], 0x00, + sizeof(IoAdapter->a.FlowControlIdTable)); + memset(&IoAdapter->a.FlowControlSkipTable[0], 0x00, + sizeof(IoAdapter->a.FlowControlSkipTable)); + memset(&IoAdapter->a.misc_flags_table[0], 0x00, + sizeof(IoAdapter->a.misc_flags_table)); + memset(&IoAdapter->a.rx_stream[0], 0x00, + sizeof(IoAdapter->a.rx_stream)); + memset(&IoAdapter->a.tx_stream[0], 0x00, + sizeof(IoAdapter->a.tx_stream)); + memset(&IoAdapter->a.tx_pos[0], 0x00, sizeof(IoAdapter->a.tx_pos)); + memset(&IoAdapter->a.rx_pos[0], 0x00, sizeof(IoAdapter->a.rx_pos)); + + return (0); +} + +static int +diva_bri_write_sdram_block(PISDN_ADAPTER IoAdapter, + dword address, const byte *data, dword length) +{ + byte __iomem *addrHi, *addrLo, *ioaddr; + byte __iomem *Port; + + if (!IoAdapter->port) { + return (-1); + } + + Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter); + addrHi = Port + + ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH); + addrLo = Port + ADDR; + ioaddr = Port + DATA; + + while (length--) { + outpp(addrHi, (word) (address >> 16)); + outppw(addrLo, (word) (address & 0x0000ffff)); + outpp(ioaddr, *data++); + address++; + } + + DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port); + return (0); +} + +static int +diva_bri_start_adapter(PISDN_ADAPTER IoAdapter, + dword start_address, dword features) +{ + byte __iomem *Port; + dword i, test; + byte __iomem *addrHi, *addrLo, *ioaddr; + int started = 0; + ADAPTER *a = &IoAdapter->a; + + if (IoAdapter->Initialized) { + DBG_ERR( + ("A: A(%d) bri_start_adapter, adapter already running", + IoAdapter->ANum)) return (-1); + } + if (!IoAdapter->port) { + DBG_ERR(("A: A(%d) bri_start_adapter, adapter not mapped", + IoAdapter->ANum)) return (-1); + } + + sprintf(IoAdapter->Name, "A(%d)", (int) IoAdapter->ANum); + DBG_LOG(("A(%d) start BRI", IoAdapter->ANum)) + + Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter); + addrHi = Port + + ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH); + addrLo = Port + ADDR; + ioaddr = Port + DATA; + + outpp(addrHi, + (byte) ( + (IoAdapter->MemoryBase + IoAdapter->MemorySize - + BRI_SHARED_RAM_SIZE) >> 16)); + outppw(addrLo, 0x1e); + outppw(ioaddr, 0x00); + DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port); + + /* + start the protocol code + */ + Port = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + outpp(Port, 0x08); + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, Port); + + Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter); + addrHi = Port + + ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH); + addrLo = Port + ADDR; + ioaddr = Port + DATA; + /* + wait for signature (max. 3 seconds) + */ + for (i = 0; i < 300; ++i) { + diva_os_wait(10); + outpp(addrHi, + (byte) ( + (IoAdapter->MemoryBase + + IoAdapter->MemorySize - + BRI_SHARED_RAM_SIZE) >> 16)); + outppw(addrLo, 0x1e); + test = (dword) inppw(ioaddr); + if (test == 0x4447) { + DBG_LOG( + ("Protocol startup time %d.%02d seconds", + (i / 100), (i % 100))) + started = 1; + break; + } + } + DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port); + + if (!started) { + DBG_FTL(("A: A(%d) %s: Adapter selftest failed 0x%04X", + IoAdapter->ANum, IoAdapter->Properties.Name, + test)) + (*(IoAdapter->trapFnc)) (IoAdapter); + return (-1); + } + + IoAdapter->Initialized = 1; + + /* + Check Interrupt + */ + IoAdapter->IrqCount = 0; + a->ReadyInt = 1; + + if (IoAdapter->reset) { + Port = DIVA_OS_MEM_ATTACH_RESET(IoAdapter); + outpp(Port, 0x41); + DIVA_OS_MEM_DETACH_RESET(IoAdapter, Port); + } + + a->ram_out(a, &PR_RAM->ReadyInt, 1); + for (i = 0; ((!IoAdapter->IrqCount) && (i < 100)); i++) { + diva_os_wait(10); + } + if (!IoAdapter->IrqCount) { + DBG_ERR( + ("A: A(%d) interrupt test failed", + IoAdapter->ANum)) + IoAdapter->Initialized = 0; + IoAdapter->stop(IoAdapter); + return (-1); + } + + IoAdapter->Properties.Features = (word) features; + diva_xdi_display_adapter_features(IoAdapter->ANum); + DBG_LOG(("A(%d) BRI adapter successfully started", IoAdapter->ANum)) + /* + Register with DIDD + */ + diva_xdi_didd_register_adapter(IoAdapter->ANum); + + return (0); +} + +static void diva_bri_clear_interrupts(diva_os_xdi_adapter_t *a) +{ + PISDN_ADAPTER IoAdapter = &a->xdi_adapter; + + /* + clear any pending interrupt + */ + IoAdapter->disIrq(IoAdapter); + + IoAdapter->tst_irq(&IoAdapter->a); + IoAdapter->clr_irq(&IoAdapter->a); + IoAdapter->tst_irq(&IoAdapter->a); + + /* + kill pending dpcs + */ + diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr); + diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr); +} + +/* +** Stop card +*/ +static int diva_bri_stop_adapter(diva_os_xdi_adapter_t *a) +{ + PISDN_ADAPTER IoAdapter = &a->xdi_adapter; + int i = 100; + + if (!IoAdapter->port) { + return (-1); + } + if (!IoAdapter->Initialized) { + DBG_ERR(("A: A(%d) can't stop BRI adapter - not running", + IoAdapter->ANum)) + return (-1); /* nothing to stop */ + } + IoAdapter->Initialized = 0; + + /* + Disconnect Adapter from DIDD + */ + diva_xdi_didd_remove_adapter(IoAdapter->ANum); + + /* + Stop interrupts + */ + a->clear_interrupts_proc = diva_bri_clear_interrupts; + IoAdapter->a.ReadyInt = 1; + IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt); + do { + diva_os_sleep(10); + } while (i-- && a->clear_interrupts_proc); + if (a->clear_interrupts_proc) { + diva_bri_clear_interrupts(a); + a->clear_interrupts_proc = NULL; + DBG_ERR(("A: A(%d) no final interrupt from BRI adapter", + IoAdapter->ANum)) + } + IoAdapter->a.ReadyInt = 0; + + /* + Stop and reset adapter + */ + IoAdapter->stop(IoAdapter); + + return (0); +} diff --git a/drivers/isdn/hardware/eicon/os_bri.h b/drivers/isdn/hardware/eicon/os_bri.h new file mode 100644 index 000000000..37c92cc53 --- /dev/null +++ b/drivers/isdn/hardware/eicon/os_bri.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: os_bri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */ + +#ifndef __DIVA_OS_BRI_REV_1_H__ +#define __DIVA_OS_BRI_REV_1_H__ + +int diva_bri_init_card(diva_os_xdi_adapter_t *a); + +#endif diff --git a/drivers/isdn/hardware/eicon/os_capi.h b/drivers/isdn/hardware/eicon/os_capi.h new file mode 100644 index 000000000..e72394b95 --- /dev/null +++ b/drivers/isdn/hardware/eicon/os_capi.h @@ -0,0 +1,21 @@ +/* $Id: os_capi.h,v 1.7 2003/04/12 21:40:49 schindler Exp $ + * + * ISDN interface module for Eicon active cards DIVA. + * CAPI Interface OS include files + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000-2003 Cytronics & Melware (info@melware.de) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#ifndef __OS_CAPI_H__ +#define __OS_CAPI_H__ + +#include <linux/capi.h> +#include <linux/kernelcapi.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> + +#endif /* __OS_CAPI_H__ */ diff --git a/drivers/isdn/hardware/eicon/os_pri.c b/drivers/isdn/hardware/eicon/os_pri.c new file mode 100644 index 000000000..b20f1fb89 --- /dev/null +++ b/drivers/isdn/hardware/eicon/os_pri.c @@ -0,0 +1,1053 @@ +// SPDX-License-Identifier: GPL-2.0 +/* $Id: os_pri.c,v 1.32 2004/03/21 17:26:01 armin Exp $ */ + +#include "platform.h" +#include "debuglib.h" +#include "cardtype.h" +#include "pc.h" +#include "pr_pc.h" +#include "di_defs.h" +#include "dsp_defs.h" +#include "di.h" +#include "io.h" + +#include "xdi_msg.h" +#include "xdi_adapter.h" +#include "os_pri.h" +#include "diva_pci.h" +#include "mi_pc.h" +#include "pc_maint.h" +#include "dsp_tst.h" +#include "diva_dma.h" +#include "dsrv_pri.h" + +/* -------------------------------------------------------------------------- + OS Dependent part of XDI driver for DIVA PRI Adapter + + DSP detection/validation by Anthony Booth (Eicon Networks, www.eicon.com) + -------------------------------------------------------------------------- */ + +#define DIVA_PRI_NO_PCI_BIOS_WORKAROUND 1 + +extern int diva_card_read_xlog(diva_os_xdi_adapter_t *a); + +/* +** IMPORTS +*/ +extern void prepare_pri_functions(PISDN_ADAPTER IoAdapter); +extern void prepare_pri2_functions(PISDN_ADAPTER IoAdapter); +extern void diva_xdi_display_adapter_features(int card); + +static int diva_pri_cleanup_adapter(diva_os_xdi_adapter_t *a); +static int diva_pri_cmd_card_proc(struct _diva_os_xdi_adapter *a, + diva_xdi_um_cfg_cmd_t *cmd, int length); +static int pri_get_serial_number(diva_os_xdi_adapter_t *a); +static int diva_pri_stop_adapter(diva_os_xdi_adapter_t *a); +static dword diva_pri_detect_dsps(diva_os_xdi_adapter_t *a); + +/* +** Check card revision +*/ +static int pri_is_rev_2_card(int card_ordinal) +{ + switch (card_ordinal) { + case CARDTYPE_DIVASRV_P_30M_V2_PCI: + case CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI: + return (1); + } + return (0); +} + +static void diva_pri_set_addresses(diva_os_xdi_adapter_t *a) +{ + a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 0; + a->resources.pci.mem_type_id[MEM_TYPE_CONTROL] = 2; + a->resources.pci.mem_type_id[MEM_TYPE_CONFIG] = 4; + a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 0; + a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 2; + a->resources.pci.mem_type_id[MEM_TYPE_CFG] = 4; + a->resources.pci.mem_type_id[MEM_TYPE_PROM] = 3; + + a->xdi_adapter.Address = a->resources.pci.addr[0]; + a->xdi_adapter.Control = a->resources.pci.addr[2]; + a->xdi_adapter.Config = a->resources.pci.addr[4]; + + a->xdi_adapter.ram = a->resources.pci.addr[0]; + a->xdi_adapter.ram += MP_SHARED_RAM_OFFSET; + + a->xdi_adapter.reset = a->resources.pci.addr[2]; + a->xdi_adapter.reset += MP_RESET; + + a->xdi_adapter.cfg = a->resources.pci.addr[4]; + a->xdi_adapter.cfg += MP_IRQ_RESET; + + a->xdi_adapter.sdram_bar = a->resources.pci.bar[0]; + + a->xdi_adapter.prom = a->resources.pci.addr[3]; +} + +/* +** BAR0 - SDRAM, MP_MEMORY_SIZE, MP2_MEMORY_SIZE by Rev.2 +** BAR1 - DEVICES, 0x1000 +** BAR2 - CONTROL (REG), 0x2000 +** BAR3 - FLASH (REG), 0x8000 +** BAR4 - CONFIG (CFG), 0x1000 +*/ +int diva_pri_init_card(diva_os_xdi_adapter_t *a) +{ + int bar = 0; + int pri_rev_2; + unsigned long bar_length[5] = { + MP_MEMORY_SIZE, + 0x1000, + 0x2000, + 0x8000, + 0x1000 + }; + + pri_rev_2 = pri_is_rev_2_card(a->CardOrdinal); + + if (pri_rev_2) { + bar_length[0] = MP2_MEMORY_SIZE; + } + /* + Set properties + */ + a->xdi_adapter.Properties = CardProperties[a->CardOrdinal]; + DBG_LOG(("Load %s", a->xdi_adapter.Properties.Name)) + + /* + First initialization step: get and check hardware resoures. + Do not map resources and do not acecess card at this step + */ + for (bar = 0; bar < 5; bar++) { + a->resources.pci.bar[bar] = + divasa_get_pci_bar(a->resources.pci.bus, + a->resources.pci.func, bar, + a->resources.pci.hdev); + if (!a->resources.pci.bar[bar] + || (a->resources.pci.bar[bar] == 0xFFFFFFF0)) { + DBG_ERR(("A: invalid bar[%d]=%08x", bar, + a->resources.pci.bar[bar])) + return (-1); + } + } + a->resources.pci.irq = + (byte) divasa_get_pci_irq(a->resources.pci.bus, + a->resources.pci.func, + a->resources.pci.hdev); + if (!a->resources.pci.irq) { + DBG_ERR(("A: invalid irq")); + return (-1); + } + + /* + Map all BAR's + */ + for (bar = 0; bar < 5; bar++) { + a->resources.pci.addr[bar] = + divasa_remap_pci_bar(a, bar, a->resources.pci.bar[bar], + bar_length[bar]); + if (!a->resources.pci.addr[bar]) { + DBG_ERR(("A: A(%d), can't map bar[%d]", + a->controller, bar)) + diva_pri_cleanup_adapter(a); + return (-1); + } + } + + /* + Set all memory areas + */ + diva_pri_set_addresses(a); + + /* + Get Serial Number of this adapter + */ + if (pri_get_serial_number(a)) { + dword serNo; + serNo = a->resources.pci.bar[1] & 0xffff0000; + serNo |= ((dword) a->resources.pci.bus) << 8; + serNo += (a->resources.pci.func + a->controller + 1); + a->xdi_adapter.serialNo = serNo & ~0xFF000000; + DBG_ERR(("A: A(%d) can't get Serial Number, generated serNo=%ld", + a->controller, a->xdi_adapter.serialNo)) + } + + + /* + Initialize os objects + */ + if (diva_os_initialize_spin_lock(&a->xdi_adapter.isr_spin_lock, "isr")) { + diva_pri_cleanup_adapter(a); + return (-1); + } + if (diva_os_initialize_spin_lock + (&a->xdi_adapter.data_spin_lock, "data")) { + diva_pri_cleanup_adapter(a); + return (-1); + } + + strcpy(a->xdi_adapter.req_soft_isr.dpc_thread_name, "kdivasprid"); + + if (diva_os_initialize_soft_isr(&a->xdi_adapter.req_soft_isr, + DIDpcRoutine, &a->xdi_adapter)) { + diva_pri_cleanup_adapter(a); + return (-1); + } + + /* + Do not initialize second DPC - only one thread will be created + */ + a->xdi_adapter.isr_soft_isr.object = + a->xdi_adapter.req_soft_isr.object; + + /* + Next step of card initialization: + set up all interface pointers + */ + a->xdi_adapter.Channels = CardProperties[a->CardOrdinal].Channels; + a->xdi_adapter.e_max = CardProperties[a->CardOrdinal].E_info; + + a->xdi_adapter.e_tbl = + diva_os_malloc(0, a->xdi_adapter.e_max * sizeof(E_INFO)); + if (!a->xdi_adapter.e_tbl) { + diva_pri_cleanup_adapter(a); + return (-1); + } + memset(a->xdi_adapter.e_tbl, 0x00, a->xdi_adapter.e_max * sizeof(E_INFO)); + + a->xdi_adapter.a.io = &a->xdi_adapter; + a->xdi_adapter.DIRequest = request; + a->interface.cleanup_adapter_proc = diva_pri_cleanup_adapter; + a->interface.cmd_proc = diva_pri_cmd_card_proc; + + if (pri_rev_2) { + prepare_pri2_functions(&a->xdi_adapter); + } else { + prepare_pri_functions(&a->xdi_adapter); + } + + a->dsp_mask = diva_pri_detect_dsps(a); + + /* + Allocate DMA map + */ + if (pri_rev_2) { + diva_init_dma_map(a->resources.pci.hdev, + (struct _diva_dma_map_entry **) &a->xdi_adapter.dma_map, 32); + } + + /* + Set IRQ handler + */ + a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq; + sprintf(a->xdi_adapter.irq_info.irq_name, + "DIVA PRI %ld", (long) a->xdi_adapter.serialNo); + + if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr, + a->xdi_adapter.irq_info.irq_name)) { + diva_pri_cleanup_adapter(a); + return (-1); + } + a->xdi_adapter.irq_info.registered = 1; + + diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name, + a->resources.pci.irq, a->xdi_adapter.serialNo); + + return (0); +} + +static int diva_pri_cleanup_adapter(diva_os_xdi_adapter_t *a) +{ + int bar = 0; + + /* + Stop Adapter if adapter is running + */ + if (a->xdi_adapter.Initialized) { + diva_pri_stop_adapter(a); + } + + /* + Remove ISR Handler + */ + if (a->xdi_adapter.irq_info.registered) { + diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr); + } + a->xdi_adapter.irq_info.registered = 0; + + /* + Step 1: unmap all BAR's, if any was mapped + */ + for (bar = 0; bar < 5; bar++) { + if (a->resources.pci.bar[bar] + && a->resources.pci.addr[bar]) { + divasa_unmap_pci_bar(a->resources.pci.addr[bar]); + a->resources.pci.bar[bar] = 0; + a->resources.pci.addr[bar] = NULL; + } + } + + /* + Free OS objects + */ + diva_os_cancel_soft_isr(&a->xdi_adapter.isr_soft_isr); + diva_os_cancel_soft_isr(&a->xdi_adapter.req_soft_isr); + + diva_os_remove_soft_isr(&a->xdi_adapter.req_soft_isr); + a->xdi_adapter.isr_soft_isr.object = NULL; + + diva_os_destroy_spin_lock(&a->xdi_adapter.isr_spin_lock, "rm"); + diva_os_destroy_spin_lock(&a->xdi_adapter.data_spin_lock, "rm"); + + /* + Free memory accupied by XDI adapter + */ + if (a->xdi_adapter.e_tbl) { + diva_os_free(0, a->xdi_adapter.e_tbl); + a->xdi_adapter.e_tbl = NULL; + } + a->xdi_adapter.Channels = 0; + a->xdi_adapter.e_max = 0; + + + /* + Free adapter DMA map + */ + diva_free_dma_map(a->resources.pci.hdev, + (struct _diva_dma_map_entry *) a->xdi_adapter. + dma_map); + a->xdi_adapter.dma_map = NULL; + + + /* + Detach this adapter from debug driver + */ + + return (0); +} + +/* +** Activate On Board Boot Loader +*/ +static int diva_pri_reset_adapter(PISDN_ADAPTER IoAdapter) +{ + dword i; + struct mp_load __iomem *boot; + + if (!IoAdapter->Address || !IoAdapter->reset) { + return (-1); + } + if (IoAdapter->Initialized) { + DBG_ERR(("A: A(%d) can't reset PRI adapter - please stop first", + IoAdapter->ANum)) + return (-1); + } + + boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter); + WRITE_DWORD(&boot->err, 0); + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot); + + IoAdapter->rstFnc(IoAdapter); + + diva_os_wait(10); + + boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter); + i = READ_DWORD(&boot->live); + + diva_os_wait(10); + if (i == READ_DWORD(&boot->live)) { + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot); + DBG_ERR(("A: A(%d) CPU on PRI %ld is not alive!", + IoAdapter->ANum, IoAdapter->serialNo)) + return (-1); + } + if (READ_DWORD(&boot->err)) { + DBG_ERR(("A: A(%d) PRI %ld Board Selftest failed, error=%08lx", + IoAdapter->ANum, IoAdapter->serialNo, + READ_DWORD(&boot->err))) + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot); + return (-1); + } + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot); + + /* + Forget all outstanding entities + */ + IoAdapter->e_count = 0; + if (IoAdapter->e_tbl) { + memset(IoAdapter->e_tbl, 0x00, + IoAdapter->e_max * sizeof(E_INFO)); + } + IoAdapter->head = 0; + IoAdapter->tail = 0; + IoAdapter->assign = 0; + IoAdapter->trapped = 0; + + memset(&IoAdapter->a.IdTable[0], 0x00, + sizeof(IoAdapter->a.IdTable)); + memset(&IoAdapter->a.IdTypeTable[0], 0x00, + sizeof(IoAdapter->a.IdTypeTable)); + memset(&IoAdapter->a.FlowControlIdTable[0], 0x00, + sizeof(IoAdapter->a.FlowControlIdTable)); + memset(&IoAdapter->a.FlowControlSkipTable[0], 0x00, + sizeof(IoAdapter->a.FlowControlSkipTable)); + memset(&IoAdapter->a.misc_flags_table[0], 0x00, + sizeof(IoAdapter->a.misc_flags_table)); + memset(&IoAdapter->a.rx_stream[0], 0x00, + sizeof(IoAdapter->a.rx_stream)); + memset(&IoAdapter->a.tx_stream[0], 0x00, + sizeof(IoAdapter->a.tx_stream)); + memset(&IoAdapter->a.tx_pos[0], 0x00, sizeof(IoAdapter->a.tx_pos)); + memset(&IoAdapter->a.rx_pos[0], 0x00, sizeof(IoAdapter->a.rx_pos)); + + return (0); +} + +static int +diva_pri_write_sdram_block(PISDN_ADAPTER IoAdapter, + dword address, + const byte *data, dword length, dword limit) +{ + byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter); + byte __iomem *mem = p; + + if (((address + length) >= limit) || !mem) { + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p); + DBG_ERR(("A: A(%d) write PRI address=0x%08lx", + IoAdapter->ANum, address + length)) + return (-1); + } + mem += address; + + /* memcpy_toio(), maybe? */ + while (length--) { + WRITE_BYTE(mem++, *data++); + } + + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p); + return (0); +} + +static int +diva_pri_start_adapter(PISDN_ADAPTER IoAdapter, + dword start_address, dword features) +{ + dword i; + int started = 0; + byte __iomem *p; + struct mp_load __iomem *boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter); + ADAPTER *a = &IoAdapter->a; + + if (IoAdapter->Initialized) { + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot); + DBG_ERR(("A: A(%d) pri_start_adapter, adapter already running", + IoAdapter->ANum)) + return (-1); + } + if (!boot) { + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot); + DBG_ERR(("A: PRI %ld can't start, adapter not mapped", + IoAdapter->serialNo)) + return (-1); + } + + sprintf(IoAdapter->Name, "A(%d)", (int) IoAdapter->ANum); + DBG_LOG(("A(%d) start PRI at 0x%08lx", IoAdapter->ANum, + start_address)) + + WRITE_DWORD(&boot->addr, start_address); + WRITE_DWORD(&boot->cmd, 3); + + for (i = 0; i < 300; ++i) { + diva_os_wait(10); + if ((READ_DWORD(&boot->signature) >> 16) == 0x4447) { + DBG_LOG(("A(%d) Protocol startup time %d.%02d seconds", + IoAdapter->ANum, (i / 100), (i % 100))) + started = 1; + break; + } + } + + if (!started) { + byte __iomem *p = (byte __iomem *)boot; + dword TrapId; + dword debug; + TrapId = READ_DWORD(&p[0x80]); + debug = READ_DWORD(&p[0x1c]); + DBG_ERR(("A(%d) Adapter start failed 0x%08lx, TrapId=%08lx, debug=%08lx", + IoAdapter->ANum, READ_DWORD(&boot->signature), + TrapId, debug)) + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot); + if (IoAdapter->trapFnc) { + (*(IoAdapter->trapFnc)) (IoAdapter); + } + IoAdapter->stop(IoAdapter); + return (-1); + } + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot); + + IoAdapter->Initialized = true; + + /* + Check Interrupt + */ + IoAdapter->IrqCount = 0; + p = DIVA_OS_MEM_ATTACH_CFG(IoAdapter); + WRITE_DWORD(p, (dword)~0x03E00000); + DIVA_OS_MEM_DETACH_CFG(IoAdapter, p); + a->ReadyInt = 1; + a->ram_out(a, &PR_RAM->ReadyInt, 1); + + for (i = 100; !IoAdapter->IrqCount && (i-- > 0); diva_os_wait(10)); + + if (!IoAdapter->IrqCount) { + DBG_ERR(("A: A(%d) interrupt test failed", + IoAdapter->ANum)) + IoAdapter->Initialized = false; + IoAdapter->stop(IoAdapter); + return (-1); + } + + IoAdapter->Properties.Features = (word) features; + + diva_xdi_display_adapter_features(IoAdapter->ANum); + + DBG_LOG(("A(%d) PRI adapter successfully started", IoAdapter->ANum)) + /* + Register with DIDD + */ + diva_xdi_didd_register_adapter(IoAdapter->ANum); + + return (0); +} + +static void diva_pri_clear_interrupts(diva_os_xdi_adapter_t *a) +{ + PISDN_ADAPTER IoAdapter = &a->xdi_adapter; + + /* + clear any pending interrupt + */ + IoAdapter->disIrq(IoAdapter); + + IoAdapter->tst_irq(&IoAdapter->a); + IoAdapter->clr_irq(&IoAdapter->a); + IoAdapter->tst_irq(&IoAdapter->a); + + /* + kill pending dpcs + */ + diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr); + diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr); +} + +/* +** Stop Adapter, but do not unmap/unregister - adapter +** will be restarted later +*/ +static int diva_pri_stop_adapter(diva_os_xdi_adapter_t *a) +{ + PISDN_ADAPTER IoAdapter = &a->xdi_adapter; + int i = 100; + + if (!IoAdapter->ram) { + return (-1); + } + if (!IoAdapter->Initialized) { + DBG_ERR(("A: A(%d) can't stop PRI adapter - not running", + IoAdapter->ANum)) + return (-1); /* nothing to stop */ + } + IoAdapter->Initialized = 0; + + /* + Disconnect Adapter from DIDD + */ + diva_xdi_didd_remove_adapter(IoAdapter->ANum); + + /* + Stop interrupts + */ + a->clear_interrupts_proc = diva_pri_clear_interrupts; + IoAdapter->a.ReadyInt = 1; + IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt); + do { + diva_os_sleep(10); + } while (i-- && a->clear_interrupts_proc); + + if (a->clear_interrupts_proc) { + diva_pri_clear_interrupts(a); + a->clear_interrupts_proc = NULL; + DBG_ERR(("A: A(%d) no final interrupt from PRI adapter", + IoAdapter->ANum)) + } + IoAdapter->a.ReadyInt = 0; + + /* + Stop and reset adapter + */ + IoAdapter->stop(IoAdapter); + + return (0); +} + +/* +** Process commands form configuration/download framework and from +** user mode +** +** return 0 on success +*/ +static int +diva_pri_cmd_card_proc(struct _diva_os_xdi_adapter *a, + diva_xdi_um_cfg_cmd_t *cmd, int length) +{ + int ret = -1; + + if (cmd->adapter != a->controller) { + DBG_ERR(("A: pri_cmd, invalid controller=%d != %d", + cmd->adapter, a->controller)) + return (-1); + } + + switch (cmd->command) { + case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL: + a->xdi_mbox.data_length = sizeof(dword); + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + *(dword *) a->xdi_mbox.data = + (dword) a->CardOrdinal; + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_GET_SERIAL_NR: + a->xdi_mbox.data_length = sizeof(dword); + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + *(dword *) a->xdi_mbox.data = + (dword) a->xdi_adapter.serialNo; + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG: + a->xdi_mbox.data_length = sizeof(dword) * 9; + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + int i; + dword *data = (dword *) a->xdi_mbox.data; + + for (i = 0; i < 8; i++) { + *data++ = a->resources.pci.bar[i]; + } + *data++ = (dword) a->resources.pci.irq; + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_RESET_ADAPTER: + ret = diva_pri_reset_adapter(&a->xdi_adapter); + break; + + case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK: + ret = diva_pri_write_sdram_block(&a->xdi_adapter, + cmd->command_data. + write_sdram.offset, + (byte *)&cmd[1], + cmd->command_data. + write_sdram.length, + pri_is_rev_2_card(a-> + CardOrdinal) + ? MP2_MEMORY_SIZE : + MP_MEMORY_SIZE); + break; + + case DIVA_XDI_UM_CMD_STOP_ADAPTER: + ret = diva_pri_stop_adapter(a); + break; + + case DIVA_XDI_UM_CMD_START_ADAPTER: + ret = diva_pri_start_adapter(&a->xdi_adapter, + cmd->command_data.start. + offset, + cmd->command_data.start. + features); + break; + + case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES: + a->xdi_adapter.features = + cmd->command_data.features.features; + a->xdi_adapter.a.protocol_capabilities = + a->xdi_adapter.features; + DBG_TRC(("Set raw protocol features (%08x)", + a->xdi_adapter.features)) + ret = 0; + break; + + case DIVA_XDI_UM_CMD_GET_CARD_STATE: + a->xdi_mbox.data_length = sizeof(dword); + a->xdi_mbox.data = + diva_os_malloc(0, a->xdi_mbox.data_length); + if (a->xdi_mbox.data) { + dword *data = (dword *) a->xdi_mbox.data; + if (!a->xdi_adapter.ram || + !a->xdi_adapter.reset || + !a->xdi_adapter.cfg) { + *data = 3; + } else if (a->xdi_adapter.trapped) { + *data = 2; + } else if (a->xdi_adapter.Initialized) { + *data = 1; + } else { + *data = 0; + } + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + ret = 0; + } + break; + + case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY: + ret = diva_card_read_xlog(a); + break; + + case DIVA_XDI_UM_CMD_READ_SDRAM: + if (a->xdi_adapter.Address) { + if ( + (a->xdi_mbox.data_length = + cmd->command_data.read_sdram.length)) { + if ( + (a->xdi_mbox.data_length + + cmd->command_data.read_sdram.offset) < + a->xdi_adapter.MemorySize) { + a->xdi_mbox.data = + diva_os_malloc(0, + a->xdi_mbox. + data_length); + if (a->xdi_mbox.data) { + byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(&a->xdi_adapter); + byte __iomem *src = p; + byte *dst = a->xdi_mbox.data; + dword len = a->xdi_mbox.data_length; + + src += cmd->command_data.read_sdram.offset; + + while (len--) { + *dst++ = READ_BYTE(src++); + } + a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY; + DIVA_OS_MEM_DETACH_ADDRESS(&a->xdi_adapter, p); + ret = 0; + } + } + } + } + break; + + default: + DBG_ERR(("A: A(%d) invalid cmd=%d", a->controller, + cmd->command)) + } + + return (ret); +} + +/* +** Get Serial Number +*/ +static int pri_get_serial_number(diva_os_xdi_adapter_t *a) +{ + byte data[64]; + int i; + dword len = sizeof(data); + volatile byte __iomem *config; + volatile byte __iomem *flash; + byte c; + +/* + * First set some GT6401x config registers before accessing the BOOT-ROM + */ + config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter); + c = READ_BYTE(&config[0xc3c]); + if (!(c & 0x08)) { + WRITE_BYTE(&config[0xc3c], c); /* Base Address enable register */ + } + WRITE_BYTE(&config[LOW_BOOTCS_DREG], 0x00); + WRITE_BYTE(&config[HI_BOOTCS_DREG], 0xFF); + DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config); +/* + * Read only the last 64 bytes of manufacturing data + */ + memset(data, '\0', len); + flash = DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter); + for (i = 0; i < len; i++) { + data[i] = READ_BYTE(&flash[0x8000 - len + i]); + } + DIVA_OS_MEM_DETACH_PROM(&a->xdi_adapter, flash); + + config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter); + WRITE_BYTE(&config[LOW_BOOTCS_DREG], 0xFC); /* Disable FLASH EPROM access */ + WRITE_BYTE(&config[HI_BOOTCS_DREG], 0xFF); + DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config); + + if (memcmp(&data[48], "DIVAserverPR", 12)) { +#if !defined(DIVA_PRI_NO_PCI_BIOS_WORKAROUND) /* { */ + word cmd = 0, cmd_org; + void *addr; + dword addr1, addr3, addr4; + byte Bus, Slot; + void *hdev; + addr4 = a->resources.pci.bar[4]; + addr3 = a->resources.pci.bar[3]; /* flash */ + addr1 = a->resources.pci.bar[1]; /* unused */ + + DBG_ERR(("A: apply Compaq BIOS workaround")) + DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7])) + + Bus = a->resources.pci.bus; + Slot = a->resources.pci.func; + hdev = a->resources.pci.hdev; + PCIread(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev); + PCIwrite(Bus, Slot, 0x04, &cmd, sizeof(cmd), hdev); + + PCIwrite(Bus, Slot, 0x14, &addr4, sizeof(addr4), hdev); + PCIwrite(Bus, Slot, 0x20, &addr1, sizeof(addr1), hdev); + + PCIwrite(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev); + + addr = a->resources.pci.addr[1]; + a->resources.pci.addr[1] = a->resources.pci.addr[4]; + a->resources.pci.addr[4] = addr; + + addr1 = a->resources.pci.bar[1]; + a->resources.pci.bar[1] = a->resources.pci.bar[4]; + a->resources.pci.bar[4] = addr1; + + /* + Try to read Flash again + */ + len = sizeof(data); + + config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter); + if (!(config[0xc3c] & 0x08)) { + config[0xc3c] |= 0x08; /* Base Address enable register */ + } + config[LOW_BOOTCS_DREG] = 0x00; + config[HI_BOOTCS_DREG] = 0xFF; + DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config); + + memset(data, '\0', len); + flash = DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter); + for (i = 0; i < len; i++) { + data[i] = flash[0x8000 - len + i]; + } + DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter, flash); + config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter); + config[LOW_BOOTCS_DREG] = 0xFC; + config[HI_BOOTCS_DREG] = 0xFF; + DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config); + + if (memcmp(&data[48], "DIVAserverPR", 12)) { + DBG_ERR(("A: failed to read serial number")) + DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7])) + return (-1); + } +#else /* } { */ + DBG_ERR(("A: failed to read DIVA signature word")) + DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7])) + DBG_LOG(("%02x:%02x:%02x:%02x", data[47], data[46], + data[45], data[44])) +#endif /* } */ + } + + a->xdi_adapter.serialNo = + (data[47] << 24) | (data[46] << 16) | (data[45] << 8) | + data[44]; + if (!a->xdi_adapter.serialNo + || (a->xdi_adapter.serialNo == 0xffffffff)) { + a->xdi_adapter.serialNo = 0; + DBG_ERR(("A: failed to read serial number")) + return (-1); + } + + DBG_LOG(("Serial No. : %ld", a->xdi_adapter.serialNo)) + DBG_TRC(("Board Revision : %d.%02d", (int) data[41], + (int) data[40])) + DBG_TRC(("PLD revision : %d.%02d", (int) data[33], + (int) data[32])) + DBG_TRC(("Boot loader version : %d.%02d", (int) data[37], + (int) data[36])) + + DBG_TRC(("Manufacturing Date : %d/%02d/%02d (yyyy/mm/dd)", + (int) ((data[28] > 90) ? 1900 : 2000) + + (int) data[28], (int) data[29], (int) data[30])) + + return (0); +} + +void diva_os_prepare_pri2_functions(PISDN_ADAPTER IoAdapter) +{ +} + +void diva_os_prepare_pri_functions(PISDN_ADAPTER IoAdapter) +{ +} + +/* +** Checks presence of DSP on board +*/ +static int +dsp_check_presence(volatile byte __iomem *addr, volatile byte __iomem *data, int dsp) +{ + word pattern; + + WRITE_WORD(addr, 0x4000); + WRITE_WORD(data, DSP_SIGNATURE_PROBE_WORD); + + WRITE_WORD(addr, 0x4000); + pattern = READ_WORD(data); + + if (pattern != DSP_SIGNATURE_PROBE_WORD) { + DBG_TRC(("W: DSP[%d] %04x(is) != %04x(should)", + dsp, pattern, DSP_SIGNATURE_PROBE_WORD)) + return (-1); + } + + WRITE_WORD(addr, 0x4000); + WRITE_WORD(data, ~DSP_SIGNATURE_PROBE_WORD); + + WRITE_WORD(addr, 0x4000); + pattern = READ_WORD(data); + + if (pattern != (word)~DSP_SIGNATURE_PROBE_WORD) { + DBG_ERR(("A: DSP[%d] %04x(is) != %04x(should)", + dsp, pattern, (word)~DSP_SIGNATURE_PROBE_WORD)) + return (-2); + } + + DBG_TRC(("DSP[%d] present", dsp)) + + return (0); +} + + +/* +** Check if DSP's are present and operating +** Information about detected DSP's is returned as bit mask +** Bit 0 - DSP1 +** ... +** ... +** ... +** Bit 29 - DSP30 +*/ +static dword diva_pri_detect_dsps(diva_os_xdi_adapter_t *a) +{ + byte __iomem *base; + byte __iomem *p; + dword ret = 0; + dword row_offset[7] = { + 0x00000000, + 0x00000800, /* 1 - ROW 1 */ + 0x00000840, /* 2 - ROW 2 */ + 0x00001000, /* 3 - ROW 3 */ + 0x00001040, /* 4 - ROW 4 */ + 0x00000000 /* 5 - ROW 0 */ + }; + + byte __iomem *dsp_addr_port; + byte __iomem *dsp_data_port; + byte row_state; + int dsp_row = 0, dsp_index, dsp_num; + + if (!a->xdi_adapter.Control || !a->xdi_adapter.reset) { + return (0); + } + + p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter); + WRITE_BYTE(p, _MP_RISC_RESET | _MP_DSP_RESET); + DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p); + diva_os_wait(5); + + base = DIVA_OS_MEM_ATTACH_CONTROL(&a->xdi_adapter); + + for (dsp_num = 0; dsp_num < 30; dsp_num++) { + dsp_row = dsp_num / 7 + 1; + dsp_index = dsp_num % 7; + + dsp_data_port = base; + dsp_addr_port = base; + + dsp_data_port += row_offset[dsp_row]; + dsp_addr_port += row_offset[dsp_row]; + + dsp_data_port += (dsp_index * 8); + dsp_addr_port += (dsp_index * 8) + 0x80; + + if (!dsp_check_presence + (dsp_addr_port, dsp_data_port, dsp_num + 1)) { + ret |= (1 << dsp_num); + } + } + DIVA_OS_MEM_DETACH_CONTROL(&a->xdi_adapter, base); + + p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter); + WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2); + DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p); + diva_os_wait(5); + + /* + Verify modules + */ + for (dsp_row = 0; dsp_row < 4; dsp_row++) { + row_state = ((ret >> (dsp_row * 7)) & 0x7F); + if (row_state && (row_state != 0x7F)) { + for (dsp_index = 0; dsp_index < 7; dsp_index++) { + if (!(row_state & (1 << dsp_index))) { + DBG_ERR(("A: MODULE[%d]-DSP[%d] failed", + dsp_row + 1, + dsp_index + 1)) + } + } + } + } + + if (!(ret & 0x10000000)) { + DBG_ERR(("A: ON BOARD-DSP[1] failed")) + } + if (!(ret & 0x20000000)) { + DBG_ERR(("A: ON BOARD-DSP[2] failed")) + } + + /* + Print module population now + */ + DBG_LOG(("+-----------------------+")) + DBG_LOG(("| DSP MODULE POPULATION |")) + DBG_LOG(("+-----------------------+")) + DBG_LOG(("| 1 | 2 | 3 | 4 |")) + DBG_LOG(("+-----------------------+")) + DBG_LOG(("| %s | %s | %s | %s |", + ((ret >> (0 * 7)) & 0x7F) ? "Y" : "N", + ((ret >> (1 * 7)) & 0x7F) ? "Y" : "N", + ((ret >> (2 * 7)) & 0x7F) ? "Y" : "N", + ((ret >> (3 * 7)) & 0x7F) ? "Y" : "N")) + DBG_LOG(("+-----------------------+")) + + DBG_LOG(("DSP's(present-absent):%08x-%08x", ret, + ~ret & 0x3fffffff)) + + return (ret); +} diff --git a/drivers/isdn/hardware/eicon/os_pri.h b/drivers/isdn/hardware/eicon/os_pri.h new file mode 100644 index 000000000..0e91855b1 --- /dev/null +++ b/drivers/isdn/hardware/eicon/os_pri.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: os_pri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */ + +#ifndef __DIVA_OS_PRI_REV_1_H__ +#define __DIVA_OS_PRI_REV_1_H__ + +int diva_pri_init_card(diva_os_xdi_adapter_t *a); + +#endif diff --git a/drivers/isdn/hardware/eicon/pc.h b/drivers/isdn/hardware/eicon/pc.h new file mode 100644 index 000000000..329c0c26a --- /dev/null +++ b/drivers/isdn/hardware/eicon/pc.h @@ -0,0 +1,738 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef PC_H_INCLUDED /* { */ +#define PC_H_INCLUDED +/*------------------------------------------------------------------*/ +/* buffer definition */ +/*------------------------------------------------------------------*/ +typedef struct { + word length; /* length of data/parameter field */ + byte P[270]; /* data/parameter field */ +} PBUFFER; +/*------------------------------------------------------------------*/ +/* dual port ram structure */ +/*------------------------------------------------------------------*/ +struct dual +{ + byte Req; /* request register */ + byte ReqId; /* request task/entity identification */ + byte Rc; /* return code register */ + byte RcId; /* return code task/entity identification */ + byte Ind; /* Indication register */ + byte IndId; /* Indication task/entity identification */ + byte IMask; /* Interrupt Mask Flag */ + byte RNR; /* Receiver Not Ready (set by PC) */ + byte XLock; /* XBuffer locked Flag */ + byte Int; /* ISDN-S interrupt */ + byte ReqCh; /* Channel field for layer-3 Requests */ + byte RcCh; /* Channel field for layer-3 Returncodes */ + byte IndCh; /* Channel field for layer-3 Indications */ + byte MInd; /* more data indication field */ + word MLength; /* more data total packet length */ + byte ReadyInt; /* request field for ready interrupt */ + byte SWReg; /* Software register for special purposes */ + byte Reserved[11]; /* reserved space */ + byte InterfaceType; /* interface type 1=16K interface */ + word Signature; /* ISDN-S adapter Signature (GD) */ + PBUFFER XBuffer; /* Transmit Buffer */ + PBUFFER RBuffer; /* Receive Buffer */ +}; +/*------------------------------------------------------------------*/ +/* SWReg Values (0 means no command) */ +/*------------------------------------------------------------------*/ +#define SWREG_DIE_WITH_LEDON 0x01 +#define SWREG_HALT_CPU 0x02 /* Push CPU into a while (1) loop */ +/*------------------------------------------------------------------*/ +/* Id Fields Coding */ +/*------------------------------------------------------------------*/ +#define ID_MASK 0xe0 /* Mask for the ID field */ +#define GL_ERR_ID 0x1f /* ID for error reporting on global requests*/ +#define DSIG_ID 0x00 /* ID for D-channel signaling */ +#define NL_ID 0x20 /* ID for network-layer access (B or D) */ +#define BLLC_ID 0x60 /* ID for B-channel link level access */ +#define TASK_ID 0x80 /* ID for dynamic user tasks */ +#define TIMER_ID 0xa0 /* ID for timer task */ +#define TEL_ID 0xc0 /* ID for telephone support */ +#define MAN_ID 0xe0 /* ID for management */ +/*------------------------------------------------------------------*/ +/* ASSIGN and REMOVE requests are the same for all entities */ +/*------------------------------------------------------------------*/ +#define ASSIGN 0x01 +#define UREMOVE 0xfe /* without return code */ +#define REMOVE 0xff +/*------------------------------------------------------------------*/ +/* Timer Interrupt Task Interface */ +/*------------------------------------------------------------------*/ +#define ASSIGN_TIM 0x01 +#define REMOVE_TIM 0xff +/*------------------------------------------------------------------*/ +/* dynamic user task interface */ +/*------------------------------------------------------------------*/ +#define ASSIGN_TSK 0x01 +#define REMOVE_TSK 0xff +#define LOAD 0xf0 +#define RELOCATE 0xf1 +#define START 0xf2 +#define LOAD2 0xf3 +#define RELOCATE2 0xf4 +/*------------------------------------------------------------------*/ +/* dynamic user task messages */ +/*------------------------------------------------------------------*/ +#define TSK_B2 0x0000 +#define TSK_WAKEUP 0x2000 +#define TSK_TIMER 0x4000 +#define TSK_TSK 0x6000 +#define TSK_PC 0xe000 +/*------------------------------------------------------------------*/ +/* LL management primitives */ +/*------------------------------------------------------------------*/ +#define ASSIGN_LL 1 /* assign logical link */ +#define REMOVE_LL 0xff /* remove logical link */ +/*------------------------------------------------------------------*/ +/* LL service primitives */ +/*------------------------------------------------------------------*/ +#define LL_UDATA 1 /* link unit data request/indication */ +#define LL_ESTABLISH 2 /* link establish request/indication */ +#define LL_RELEASE 3 /* link release request/indication */ +#define LL_DATA 4 /* data request/indication */ +#define LL_LOCAL 5 /* switch to local operation (COM only) */ +#define LL_DATA_PEND 5 /* data pending indication (SDLC SHM only) */ +#define LL_REMOTE 6 /* switch to remote operation (COM only) */ +#define LL_TEST 8 /* link test request */ +#define LL_MDATA 9 /* more data request/indication */ +#define LL_BUDATA 10 /* broadcast unit data request/indication */ +#define LL_XID 12 /* XID command request/indication */ +#define LL_XID_R 13 /* XID response request/indication */ +/*------------------------------------------------------------------*/ +/* NL service primitives */ +/*------------------------------------------------------------------*/ +#define N_MDATA 1 /* more data to come REQ/IND */ +#define N_CONNECT 2 /* OSI N-CONNECT REQ/IND */ +#define N_CONNECT_ACK 3 /* OSI N-CONNECT CON/RES */ +#define N_DISC 4 /* OSI N-DISC REQ/IND */ +#define N_DISC_ACK 5 /* OSI N-DISC CON/RES */ +#define N_RESET 6 /* OSI N-RESET REQ/IND */ +#define N_RESET_ACK 7 /* OSI N-RESET CON/RES */ +#define N_DATA 8 /* OSI N-DATA REQ/IND */ +#define N_EDATA 9 /* OSI N-EXPEDITED DATA REQ/IND */ +#define N_UDATA 10 /* OSI D-UNIT-DATA REQ/IND */ +#define N_BDATA 11 /* BROADCAST-DATA REQ/IND */ +#define N_DATA_ACK 12 /* data ack ind for D-bit procedure */ +#define N_EDATA_ACK 13 /* data ack ind for INTERRUPT */ +#define N_XON 15 /* clear RNR state */ +#define N_COMBI_IND N_XON /* combined indication */ +#define N_Q_BIT 0x10 /* Q-bit for req/ind */ +#define N_M_BIT 0x20 /* M-bit for req/ind */ +#define N_D_BIT 0x40 /* D-bit for req/ind */ +/*------------------------------------------------------------------*/ +/* Signaling management primitives */ +/*------------------------------------------------------------------*/ +#define ASSIGN_SIG 1 /* assign signaling task */ +#define UREMOVE_SIG 0xfe /* remove signaling task without return code*/ +#define REMOVE_SIG 0xff /* remove signaling task */ +/*------------------------------------------------------------------*/ +/* Signaling service primitives */ +/*------------------------------------------------------------------*/ +#define CALL_REQ 1 /* call request */ +#define CALL_CON 1 /* call confirmation */ +#define CALL_IND 2 /* incoming call connected */ +#define LISTEN_REQ 2 /* listen request */ +#define HANGUP 3 /* hangup request/indication */ +#define SUSPEND 4 /* call suspend request/confirm */ +#define RESUME 5 /* call resume request/confirm */ +#define SUSPEND_REJ 6 /* suspend rejected indication */ +#define USER_DATA 8 /* user data for user to user signaling */ +#define CONGESTION 9 /* network congestion indication */ +#define INDICATE_REQ 10 /* request to indicate an incoming call */ +#define INDICATE_IND 10 /* indicates that there is an incoming call */ +#define CALL_RES 11 /* accept an incoming call */ +#define CALL_ALERT 12 /* send ALERT for incoming call */ +#define INFO_REQ 13 /* INFO request */ +#define INFO_IND 13 /* INFO indication */ +#define REJECT 14 /* reject an incoming call */ +#define RESOURCES 15 /* reserve B-Channel hardware resources */ +#define HW_CTRL 16 /* B-Channel hardware IOCTL req/ind */ +#define TEL_CTRL 16 /* Telephone control request/indication */ +#define STATUS_REQ 17 /* Request D-State (returned in INFO_IND) */ +#define FAC_REG_REQ 18 /* 1TR6 connection independent fac reg */ +#define FAC_REG_ACK 19 /* 1TR6 fac registration acknowledge */ +#define FAC_REG_REJ 20 /* 1TR6 fac registration reject */ +#define CALL_COMPLETE 21/* send a CALL_PROC for incoming call */ +#define SW_CTRL 22 /* extended software features */ +#define REGISTER_REQ 23 /* Q.931 connection independent reg req */ +#define REGISTER_IND 24 /* Q.931 connection independent reg ind */ +#define FACILITY_REQ 25 /* Q.931 connection independent fac req */ +#define FACILITY_IND 26 /* Q.931 connection independent fac ind */ +#define NCR_INFO_REQ 27 /* INFO_REQ with NULL CR */ +#define GCR_MIM_REQ 28 /* MANAGEMENT_INFO_REQ with global CR */ +#define SIG_CTRL 29 /* Control for Signalling Hardware */ +#define DSP_CTRL 30 /* Control for DSPs */ +#define LAW_REQ 31 /* Law config request for (returns info_i) */ +#define SPID_CTRL 32 /* Request/indication SPID related */ +#define NCR_FACILITY 33 /* Request/indication with NULL/DUMMY CR */ +#define CALL_HOLD 34 /* Request/indication to hold a CALL */ +#define CALL_RETRIEVE 35 /* Request/indication to retrieve a CALL */ +#define CALL_HOLD_ACK 36 /* OK of hold a CALL */ +#define CALL_RETRIEVE_ACK 37 /* OK of retrieve a CALL */ +#define CALL_HOLD_REJ 38 /* Reject of hold a CALL */ +#define CALL_RETRIEVE_REJ 39 /* Reject of retrieve a call */ +#define GCR_RESTART 40 /* Send/Receive Restart message */ +#define S_SERVICE 41 /* Send/Receive Supplementary Service */ +#define S_SERVICE_REJ 42 /* Reject Supplementary Service indication */ +#define S_SUPPORTED 43 /* Req/Ind to get Supported Services */ +#define STATUS_ENQ 44 /* Req to send the D-ch request if !state0 */ +#define CALL_GUARD 45 /* Req/Ind to use the FLAGS_CALL_OUTCHECK */ +#define CALL_GUARD_HP 46 /* Call Guard function to reject a call */ +#define CALL_GUARD_IF 47 /* Call Guard function, inform the appl */ +#define SSEXT_REQ 48 /* Supplem.Serv./QSIG specific request */ +#define SSEXT_IND 49 /* Supplem.Serv./QSIG specific indication */ +/* reserved commands for the US protocols */ +#define INT_3PTY_NIND 50 /* US specific indication */ +#define INT_CF_NIND 51 /* US specific indication */ +#define INT_3PTY_DROP 52 /* US specific indication */ +#define INT_MOVE_CONF 53 /* US specific indication */ +#define INT_MOVE_RC 54 /* US specific indication */ +#define INT_MOVE_FLIPPED_CONF 55 /* US specific indication */ +#define INT_X5NI_OK 56 /* internal transfer OK indication */ +#define INT_XDMS_START 57 /* internal transfer OK indication */ +#define INT_XDMS_STOP 58 /* internal transfer finish indication */ +#define INT_XDMS_STOP2 59 /* internal transfer send FA */ +#define INT_CUSTCONF_REJ 60 /* internal conference reject */ +#define INT_CUSTXFER 61 /* internal transfer request */ +#define INT_CUSTX_NIND 62 /* internal transfer ack */ +#define INT_CUSTXREJ_NIND 63 /* internal transfer rej */ +#define INT_X5NI_CF_XFER 64 /* internal transfer OK indication */ +#define VSWITCH_REQ 65 /* communication between protocol and */ +#define VSWITCH_IND 66 /* capifunctions for D-CH-switching */ +#define MWI_POLL 67 /* Message Waiting Status Request fkt */ +#define CALL_PEND_NOTIFY 68 /* notify capi to set new listen */ +#define DO_NOTHING 69 /* dont do somethin if you get this */ +#define INT_CT_REJ 70 /* ECT rejected internal command */ +#define CALL_HOLD_COMPLETE 71 /* In NT Mode indicate hold complete */ +#define CALL_RETRIEVE_COMPLETE 72 /* In NT Mode indicate retrieve complete */ +/*------------------------------------------------------------------*/ +/* management service primitives */ +/*------------------------------------------------------------------*/ +#define MAN_READ 2 +#define MAN_WRITE 3 +#define MAN_EXECUTE 4 +#define MAN_EVENT_ON 5 +#define MAN_EVENT_OFF 6 +#define MAN_LOCK 7 +#define MAN_UNLOCK 8 +#define MAN_INFO_IND 2 +#define MAN_EVENT_IND 3 +#define MAN_TRACE_IND 4 +#define MAN_COMBI_IND 9 +#define MAN_ESC 0x80 +/*------------------------------------------------------------------*/ +/* return code coding */ +/*------------------------------------------------------------------*/ +#define UNKNOWN_COMMAND 0x01 /* unknown command */ +#define WRONG_COMMAND 0x02 /* wrong command */ +#define WRONG_ID 0x03 /* unknown task/entity id */ +#define WRONG_CH 0x04 /* wrong task/entity id */ +#define UNKNOWN_IE 0x05 /* unknown information el. */ +#define WRONG_IE 0x06 /* wrong information el. */ +#define OUT_OF_RESOURCES 0x07 /* ISDN-S card out of res. */ +#define ISDN_GUARD_REJ 0x09 /* ISDN-Guard SuppServ rej */ +#define N_FLOW_CONTROL 0x10 /* Flow-Control, retry */ +#define ASSIGN_RC 0xe0 /* ASSIGN acknowledgement */ +#define ASSIGN_OK 0xef /* ASSIGN OK */ +#define OK_FC 0xfc /* Flow-Control RC */ +#define READY_INT 0xfd /* Ready interrupt */ +#define TIMER_INT 0xfe /* timer interrupt */ +#define OK 0xff /* command accepted */ +/*------------------------------------------------------------------*/ +/* information elements */ +/*------------------------------------------------------------------*/ +#define SHIFT 0x90 /* codeset shift */ +#define MORE 0xa0 /* more data */ +#define SDNCMPL 0xa1 /* sending complete */ +#define CL 0xb0 /* congestion level */ +/* codeset 0 */ +#define SMSG 0x00 /* segmented message */ +#define BC 0x04 /* Bearer Capability */ +#define CAU 0x08 /* cause */ +#define CAD 0x0c /* Connected address */ +#define CAI 0x10 /* call identity */ +#define CHI 0x18 /* channel identification */ +#define LLI 0x19 /* logical link id */ +#define CHA 0x1a /* charge advice */ +#define FTY 0x1c /* Facility */ +#define DT 0x29 /* ETSI date/time */ +#define KEY 0x2c /* keypad information element */ +#define UID 0x2d /* User id information element */ +#define DSP 0x28 /* display */ +#define SIG 0x34 /* signalling hardware control */ +#define OAD 0x6c /* origination address */ +#define OSA 0x6d /* origination sub-address */ +#define CPN 0x70 /* called party number */ +#define DSA 0x71 /* destination sub-address */ +#define RDX 0x73 /* redirecting number extended */ +#define RDN 0x74 /* redirecting number */ +#define RIN 0x76 /* redirection number */ +#define IUP 0x76 /* VN6 rerouter->PCS (codeset 6) */ +#define IPU 0x77 /* VN6 PCS->rerouter (codeset 6) */ +#define RI 0x79 /* restart indicator */ +#define MIE 0x7a /* management info element */ +#define LLC 0x7c /* low layer compatibility */ +#define HLC 0x7d /* high layer compatibility */ +#define UUI 0x7e /* user user information */ +#define ESC 0x7f /* escape extension */ +#define DLC 0x20 /* data link layer configuration */ +#define NLC 0x21 /* network layer configuration */ +#define REDIRECT_IE 0x22 /* redirection request/indication data */ +#define REDIRECT_NET_IE 0x23 /* redirection network override data */ +/* codeset 6 */ +#define SIN 0x01 /* service indicator */ +#define CIF 0x02 /* charging information */ +#define DATE 0x03 /* date */ +#define CPS 0x07 /* called party status */ +/*------------------------------------------------------------------*/ +/* ESC information elements */ +/*------------------------------------------------------------------*/ +#define MSGTYPEIE 0x7a /* Messagetype info element */ +#define CRIE 0x7b /* INFO info element */ +#define CODESET6IE 0xec /* Tunnel for Codeset 6 IEs */ +#define VSWITCHIE 0xed /* VSwitch info element */ +#define SSEXTIE 0xee /* Supplem. Service info element */ +#define PROFILEIE 0xef /* Profile info element */ +/*------------------------------------------------------------------*/ +/* TEL_CTRL contents */ +/*------------------------------------------------------------------*/ +#define RING_ON 0x01 +#define RING_OFF 0x02 +#define HANDS_FREE_ON 0x03 +#define HANDS_FREE_OFF 0x04 +#define ON_HOOK 0x80 +#define OFF_HOOK 0x90 +/* operation values used by ETSI supplementary services */ +#define THREE_PTY_BEGIN 0x04 +#define THREE_PTY_END 0x05 +#define ECT_EXECUTE 0x06 +#define ACTIVATION_DIVERSION 0x07 +#define DEACTIVATION_DIVERSION 0x08 +#define CALL_DEFLECTION 0x0D +#define INTERROGATION_DIVERSION 0x0B +#define INTERROGATION_SERV_USR_NR 0x11 +#define ACTIVATION_MWI 0x20 +#define DEACTIVATION_MWI 0x21 +#define MWI_INDICATION 0x22 +#define MWI_RESPONSE 0x23 +#define CONF_BEGIN 0x28 +#define CONF_ADD 0x29 +#define CONF_SPLIT 0x2a +#define CONF_DROP 0x2b +#define CONF_ISOLATE 0x2c +#define CONF_REATTACH 0x2d +#define CONF_PARTYDISC 0x2e +#define CCBS_INFO_RETAIN 0x2f +#define CCBS_ERASECALLLINKAGEID 0x30 +#define CCBS_STOP_ALERTING 0x31 +#define CCBS_REQUEST 0x32 +#define CCBS_DEACTIVATE 0x33 +#define CCBS_INTERROGATE 0x34 +#define CCBS_STATUS 0x35 +#define CCBS_ERASE 0x36 +#define CCBS_B_FREE 0x37 +#define CCNR_INFO_RETAIN 0x38 +#define CCBS_REMOTE_USER_FREE 0x39 +#define CCNR_REQUEST 0x3a +#define CCNR_INTERROGATE 0x3b +#define GET_SUPPORTED_SERVICES 0xff +#define DIVERSION_PROCEDURE_CFU 0x70 +#define DIVERSION_PROCEDURE_CFB 0x71 +#define DIVERSION_PROCEDURE_CFNR 0x72 +#define DIVERSION_DEACTIVATION_CFU 0x80 +#define DIVERSION_DEACTIVATION_CFB 0x81 +#define DIVERSION_DEACTIVATION_CFNR 0x82 +#define DIVERSION_INTERROGATE_NUM 0x11 +#define DIVERSION_INTERROGATE_CFU 0x60 +#define DIVERSION_INTERROGATE_CFB 0x61 +#define DIVERSION_INTERROGATE_CFNR 0x62 +/* Service Masks */ +#define SMASK_HOLD_RETRIEVE 0x00000001 +#define SMASK_TERMINAL_PORTABILITY 0x00000002 +#define SMASK_ECT 0x00000004 +#define SMASK_3PTY 0x00000008 +#define SMASK_CALL_FORWARDING 0x00000010 +#define SMASK_CALL_DEFLECTION 0x00000020 +#define SMASK_MCID 0x00000040 +#define SMASK_CCBS 0x00000080 +#define SMASK_MWI 0x00000100 +#define SMASK_CCNR 0x00000200 +#define SMASK_CONF 0x00000400 +/* ---------------------------------------------- + Types of transfers used to transfer the + information in the 'struct RC->Reserved2[8]' + The information is transferred as 2 dwords + (2 4Byte unsigned values) + First of them is the transfer type. + 2^32-1 possible messages are possible in this way. + The context of the second one had no meaning + ---------------------------------------------- */ +#define DIVA_RC_TYPE_NONE 0x00000000 +#define DIVA_RC_TYPE_REMOVE_COMPLETE 0x00000008 +#define DIVA_RC_TYPE_STREAM_PTR 0x00000009 +#define DIVA_RC_TYPE_CMA_PTR 0x0000000a +#define DIVA_RC_TYPE_OK_FC 0x0000000b +#define DIVA_RC_TYPE_RX_DMA 0x0000000c +/* ------------------------------------------------------ + IO Control codes for IN BAND SIGNALING + ------------------------------------------------------ */ +#define CTRL_L1_SET_SIG_ID 5 +#define CTRL_L1_SET_DAD 6 +#define CTRL_L1_RESOURCES 7 +/* ------------------------------------------------------ */ +/* ------------------------------------------------------ + Layer 2 types + ------------------------------------------------------ */ +#define X75T 1 /* x.75 for ttx */ +#define TRF 2 /* transparent with hdlc framing */ +#define TRF_IN 3 /* transparent with hdlc fr. inc. */ +#define SDLC 4 /* sdlc, sna layer-2 */ +#define X75 5 /* x.75 for btx */ +#define LAPD 6 /* lapd (Q.921) */ +#define X25_L2 7 /* x.25 layer-2 */ +#define V120_L2 8 /* V.120 layer-2 protocol */ +#define V42_IN 9 /* V.42 layer-2 protocol, incoming */ +#define V42 10 /* V.42 layer-2 protocol */ +#define MDM_ATP 11 /* AT Parser built in the L2 */ +#define X75_V42BIS 12 /* x.75 with V.42bis */ +#define RTPL2_IN 13 /* RTP layer-2 protocol, incoming */ +#define RTPL2 14 /* RTP layer-2 protocol */ +#define V120_V42BIS 15 /* V.120 asynchronous mode supporting V.42bis compression */ +#define LISTENER 27 /* Layer 2 to listen line */ +#define MTP2 28 /* MTP2 Layer 2 */ +#define PIAFS_CRC 29 /* PIAFS Layer 2 with CRC calculation at L2 */ +/* ------------------------------------------------------ + PIAFS DLC DEFINITIONS + ------------------------------------------------------ */ +#define PIAFS_64K 0x01 +#define PIAFS_VARIABLE_SPEED 0x02 +#define PIAFS_CHINESE_SPEED 0x04 +#define PIAFS_UDATA_ABILITY_ID 0x80 +#define PIAFS_UDATA_ABILITY_DCDON 0x01 +#define PIAFS_UDATA_ABILITY_DDI 0x80 +/* + DLC of PIAFS : + Byte | 8 7 6 5 4 3 2 1 + -----+-------------------------------------------------------- + 0 | 0 0 1 0 0 0 0 0 Data Link Configuration + 1 | X X X X X X X X Length of IE (at least 15 Bytes) + 2 | 0 0 0 0 0 0 0 0 max. information field, LOW byte (not used, fix 73 Bytes) + 3 | 0 0 0 0 0 0 0 0 max. information field, HIGH byte (not used, fix 73 Bytes) + 4 | 0 0 0 0 0 0 0 0 address A (not used) + 5 | 0 0 0 0 0 0 0 0 address B (not used) + 6 | 0 0 0 0 0 0 0 0 Mode (not used, fix 128) + 7 | 0 0 0 0 0 0 0 0 Window Size (not used, fix 127) + 8 | X X X X X X X X XID Length, Low Byte (at least 7 Bytes) + 9 | X X X X X X X X XID Length, High Byte + 10 | 0 0 0 0 0 C V S PIAFS Protocol Speed configuration -> Note(1) + | S = 0 -> Protocol Speed is 32K + | S = 1 -> Protocol Speed is 64K + | V = 0 -> Protocol Speed is fixed + | V = 1 -> Protocol Speed is variable + | C = 0 -> speed setting according to standard + | C = 1 -> speed setting for chinese implementation + 11 | 0 0 0 0 0 0 R T P0 - V42bis Compression enable/disable, Low Byte + | T = 0 -> Transmit Direction enable + | T = 1 -> Transmit Direction disable + | R = 0 -> Receive Direction enable + | R = 1 -> Receive Direction disable + 13 | 0 0 0 0 0 0 0 0 P0 - V42bis Compression enable/disable, High Byte + 14 | X X X X X X X X P1 - V42bis Dictionary Size, Low Byte + 15 | X X X X X X X X P1 - V42bis Dictionary Size, High Byte + 16 | X X X X X X X X P2 - V42bis String Length, Low Byte + 17 | X X X X X X X X P2 - V42bis String Length, High Byte + 18 | X X X X X X X X PIAFS extension length + 19 | 1 0 0 0 0 0 0 0 PIAFS extension Id (0x80) - UDATA abilities + 20 | U 0 0 0 0 0 0 D UDATA abilities -> Note (2) + | up to now the following Bits are defined: + | D - signal DCD ON + | U - use extensive UDATA control communication + | for DDI test application + + Note (1): ----------+------+-----------------------------------------+ + | PIAFS Protocol | Bit | | + | Speed configuration | S | Bit 1 - Protocol Speed | + | | | 0 - 32K | + | | | 1 - 64K (default) | + | | V | Bit 2 - Variable Protocol Speed | + | | | 0 - Speed is fix | + | | | 1 - Speed is variable (default) | + | | | OVERWRITES 32k Bit 1 | + | | C | Bit 3 0 - Speed Settings according to | + | | | PIAFS specification | + | | | 1 - Speed setting for chinese | + | | | PIAFS implementation | + | | | Explanation for chinese speed settings: | + | | | if Bit 3 is set the following | + | | | rules apply: | + | | | Bit1=0 Bit2=0: 32k fix | + | | | Bit1=1 Bit2=0: 64k fix | + | | | Bit1=0 Bit2=1: PIAFS is trying | + | | | to negotiate 32k is that is | + | | | not possible it tries to | + | | | negotiate 64k | + | | | Bit1=1 Bit2=1: PIAFS is trying | + | | | to negotiate 64k is that is | + | | | not possible it tries to | + | | | negotiate 32k | + + Note (2): ----------+------+-----------------------------------------+ + | PIAFS | Bit | this byte defines the usage of UDATA | + | Implementation | | control communication | + | UDATA usage | D | Bit 1 - DCD-ON signalling | + | | | 0 - no DCD-ON is signalled | + | | | (default) | + | | | 1 - DCD-ON will be signalled | + | | U | Bit 8 - DDI test application UDATA | + | | | control communication | + | | | 0 - no UDATA control | + | | | communication (default) | + | | | sets as well the DCD-ON | + | | | signalling | + | | | 1 - UDATA control communication | + | | | ATTENTION: Do not use these | + | | | setting if you | + | | | are not really | + | | | that you need it | + | | | and you know | + | | | exactly what you | + | | | are doing. | + | | | You can easily | + | | | disable any | + | | | data transfer. | + +---------------------+------+-----------------------------------------+ +*/ +/* ------------------------------------------------------ + LISTENER DLC DEFINITIONS + ------------------------------------------------------ */ +#define LISTENER_FEATURE_MASK_CUMMULATIVE 0x0001 +/* ------------------------------------------------------ + LISTENER META-FRAME CODE/PRIMITIVE DEFINITIONS + ------------------------------------------------------ */ +#define META_CODE_LL_UDATA_RX 0x01 +#define META_CODE_LL_UDATA_TX 0x02 +#define META_CODE_LL_DATA_RX 0x03 +#define META_CODE_LL_DATA_TX 0x04 +#define META_CODE_LL_MDATA_RX 0x05 +#define META_CODE_LL_MDATA_TX 0x06 +#define META_CODE_EMPTY 0x10 +#define META_CODE_LOST_FRAMES 0x11 +#define META_FLAG_TRUNCATED 0x0001 +/*------------------------------------------------------------------*/ +/* CAPI-like profile to indicate features on LAW_REQ */ +/*------------------------------------------------------------------*/ +#define GL_INTERNAL_CONTROLLER_SUPPORTED 0x00000001L +#define GL_EXTERNAL_EQUIPMENT_SUPPORTED 0x00000002L +#define GL_HANDSET_SUPPORTED 0x00000004L +#define GL_DTMF_SUPPORTED 0x00000008L +#define GL_SUPPLEMENTARY_SERVICES_SUPPORTED 0x00000010L +#define GL_CHANNEL_ALLOCATION_SUPPORTED 0x00000020L +#define GL_BCHANNEL_OPERATION_SUPPORTED 0x00000040L +#define GL_LINE_INTERCONNECT_SUPPORTED 0x00000080L +#define B1_HDLC_SUPPORTED 0x00000001L +#define B1_TRANSPARENT_SUPPORTED 0x00000002L +#define B1_V110_ASYNC_SUPPORTED 0x00000004L +#define B1_V110_SYNC_SUPPORTED 0x00000008L +#define B1_T30_SUPPORTED 0x00000010L +#define B1_HDLC_INVERTED_SUPPORTED 0x00000020L +#define B1_TRANSPARENT_R_SUPPORTED 0x00000040L +#define B1_MODEM_ALL_NEGOTIATE_SUPPORTED 0x00000080L +#define B1_MODEM_ASYNC_SUPPORTED 0x00000100L +#define B1_MODEM_SYNC_HDLC_SUPPORTED 0x00000200L +#define B2_X75_SUPPORTED 0x00000001L +#define B2_TRANSPARENT_SUPPORTED 0x00000002L +#define B2_SDLC_SUPPORTED 0x00000004L +#define B2_LAPD_SUPPORTED 0x00000008L +#define B2_T30_SUPPORTED 0x00000010L +#define B2_PPP_SUPPORTED 0x00000020L +#define B2_TRANSPARENT_NO_CRC_SUPPORTED 0x00000040L +#define B2_MODEM_EC_COMPRESSION_SUPPORTED 0x00000080L +#define B2_X75_V42BIS_SUPPORTED 0x00000100L +#define B2_V120_ASYNC_SUPPORTED 0x00000200L +#define B2_V120_ASYNC_V42BIS_SUPPORTED 0x00000400L +#define B2_V120_BIT_TRANSPARENT_SUPPORTED 0x00000800L +#define B2_LAPD_FREE_SAPI_SEL_SUPPORTED 0x00001000L +#define B3_TRANSPARENT_SUPPORTED 0x00000001L +#define B3_T90NL_SUPPORTED 0x00000002L +#define B3_ISO8208_SUPPORTED 0x00000004L +#define B3_X25_DCE_SUPPORTED 0x00000008L +#define B3_T30_SUPPORTED 0x00000010L +#define B3_T30_WITH_EXTENSIONS_SUPPORTED 0x00000020L +#define B3_RESERVED_SUPPORTED 0x00000040L +#define B3_MODEM_SUPPORTED 0x00000080L +#define MANUFACTURER_FEATURE_SLAVE_CODEC 0x00000001L +#define MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS 0x00000002L +#define MANUFACTURER_FEATURE_HARDDTMF 0x00000004L +#define MANUFACTURER_FEATURE_SOFTDTMF_SEND 0x00000008L +#define MANUFACTURER_FEATURE_DTMF_PARAMETERS 0x00000010L +#define MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE 0x00000020L +#define MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD 0x00000040L +#define MANUFACTURER_FEATURE_V18 0x00000080L +#define MANUFACTURER_FEATURE_MIXER_CH_CH 0x00000100L +#define MANUFACTURER_FEATURE_MIXER_CH_PC 0x00000200L +#define MANUFACTURER_FEATURE_MIXER_PC_CH 0x00000400L +#define MANUFACTURER_FEATURE_MIXER_PC_PC 0x00000800L +#define MANUFACTURER_FEATURE_ECHO_CANCELLER 0x00001000L +#define MANUFACTURER_FEATURE_RTP 0x00002000L +#define MANUFACTURER_FEATURE_T38 0x00004000L +#define MANUFACTURER_FEATURE_TRANSP_DELIVERY_CONF 0x00008000L +#define MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL 0x00010000L +#define MANUFACTURER_FEATURE_OOB_CHANNEL 0x00020000L +#define MANUFACTURER_FEATURE_IN_BAND_CHANNEL 0x00040000L +#define MANUFACTURER_FEATURE_IN_BAND_FEATURE 0x00080000L +#define MANUFACTURER_FEATURE_PIAFS 0x00100000L +#define MANUFACTURER_FEATURE_DTMF_TONE 0x00200000L +#define MANUFACTURER_FEATURE_FAX_PAPER_FORMATS 0x00400000L +#define MANUFACTURER_FEATURE_OK_FC_LABEL 0x00800000L +#define MANUFACTURER_FEATURE_VOWN 0x01000000L +#define MANUFACTURER_FEATURE_XCONNECT 0x02000000L +#define MANUFACTURER_FEATURE_DMACONNECT 0x04000000L +#define MANUFACTURER_FEATURE_AUDIO_TAP 0x08000000L +#define MANUFACTURER_FEATURE_FAX_NONSTANDARD 0x10000000L +#define MANUFACTURER_FEATURE_SS7 0x20000000L +#define MANUFACTURER_FEATURE_MADAPTER 0x40000000L +#define MANUFACTURER_FEATURE_MEASURE 0x80000000L +#define MANUFACTURER_FEATURE2_LISTENING 0x00000001L +#define MANUFACTURER_FEATURE2_SS_DIFFCONTPOSSIBLE 0x00000002L +#define MANUFACTURER_FEATURE2_GENERIC_TONE 0x00000004L +#define MANUFACTURER_FEATURE2_COLOR_FAX 0x00000008L +#define MANUFACTURER_FEATURE2_SS_ECT_DIFFCONTPOSSIBLE 0x00000010L +#define RTP_PRIM_PAYLOAD_PCMU_8000 0 +#define RTP_PRIM_PAYLOAD_1016_8000 1 +#define RTP_PRIM_PAYLOAD_G726_32_8000 2 +#define RTP_PRIM_PAYLOAD_GSM_8000 3 +#define RTP_PRIM_PAYLOAD_G723_8000 4 +#define RTP_PRIM_PAYLOAD_DVI4_8000 5 +#define RTP_PRIM_PAYLOAD_DVI4_16000 6 +#define RTP_PRIM_PAYLOAD_LPC_8000 7 +#define RTP_PRIM_PAYLOAD_PCMA_8000 8 +#define RTP_PRIM_PAYLOAD_G722_16000 9 +#define RTP_PRIM_PAYLOAD_QCELP_8000 12 +#define RTP_PRIM_PAYLOAD_G728_8000 14 +#define RTP_PRIM_PAYLOAD_G729_8000 18 +#define RTP_PRIM_PAYLOAD_GSM_HR_8000 30 +#define RTP_PRIM_PAYLOAD_GSM_EFR_8000 31 +#define RTP_ADD_PAYLOAD_BASE 32 +#define RTP_ADD_PAYLOAD_RED 32 +#define RTP_ADD_PAYLOAD_CN_8000 33 +#define RTP_ADD_PAYLOAD_DTMF 34 +#define RTP_PRIM_PAYLOAD_PCMU_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_PCMU_8000) +#define RTP_PRIM_PAYLOAD_1016_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_1016_8000) +#define RTP_PRIM_PAYLOAD_G726_32_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_G726_32_8000) +#define RTP_PRIM_PAYLOAD_GSM_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_GSM_8000) +#define RTP_PRIM_PAYLOAD_G723_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_G723_8000) +#define RTP_PRIM_PAYLOAD_DVI4_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_DVI4_8000) +#define RTP_PRIM_PAYLOAD_DVI4_16000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_DVI4_16000) +#define RTP_PRIM_PAYLOAD_LPC_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_LPC_8000) +#define RTP_PRIM_PAYLOAD_PCMA_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_PCMA_8000) +#define RTP_PRIM_PAYLOAD_G722_16000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_G722_16000) +#define RTP_PRIM_PAYLOAD_QCELP_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_QCELP_8000) +#define RTP_PRIM_PAYLOAD_G728_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_G728_8000) +#define RTP_PRIM_PAYLOAD_G729_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_G729_8000) +#define RTP_PRIM_PAYLOAD_GSM_HR_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_GSM_HR_8000) +#define RTP_PRIM_PAYLOAD_GSM_EFR_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_GSM_EFR_8000) +#define RTP_ADD_PAYLOAD_RED_SUPPORTED (1L << (RTP_ADD_PAYLOAD_RED - RTP_ADD_PAYLOAD_BASE)) +#define RTP_ADD_PAYLOAD_CN_8000_SUPPORTED (1L << (RTP_ADD_PAYLOAD_CN_8000 - RTP_ADD_PAYLOAD_BASE)) +#define RTP_ADD_PAYLOAD_DTMF_SUPPORTED (1L << (RTP_ADD_PAYLOAD_DTMF - RTP_ADD_PAYLOAD_BASE)) +/* virtual switching definitions */ +#define VSJOIN 1 +#define VSTRANSPORT 2 +#define VSGETPARAMS 3 +#define VSCAD 1 +#define VSRXCPNAME 2 +#define VSCALLSTAT 3 +#define VSINVOKEID 4 +#define VSCLMRKS 5 +#define VSTBCTIDENT 6 +#define VSETSILINKID 7 +#define VSSAMECONTROLLER 8 +/* Errorcodes for VSETSILINKID begin */ +#define VSETSILINKIDRRWC 1 +#define VSETSILINKIDREJECT 2 +#define VSETSILINKIDTIMEOUT 3 +#define VSETSILINKIDFAILCOUNT 4 +#define VSETSILINKIDERROR 5 +/* Errorcodes for VSETSILINKID end */ +/* -----------------------------------------------------------** +** The PROTOCOL_FEATURE_STRING in feature.h (included ** +** in prstart.sx and astart.sx) defines capabilities and ** +** features of the actual protocol code. It's used as a bit ** +** mask. ** +** The following Bits are defined: ** +** -----------------------------------------------------------*/ +#define PROTCAP_TELINDUS 0x0001 /* Telindus Variant of protocol code */ +#define PROTCAP_MAN_IF 0x0002 /* Management interface implemented */ +#define PROTCAP_V_42 0x0004 /* V42 implemented */ +#define PROTCAP_V90D 0x0008 /* V.90D (implies up to 384k DSP code) */ +#define PROTCAP_EXTD_FAX 0x0010 /* Extended FAX (ECM, 2D, T6, Polling) */ +#define PROTCAP_EXTD_RXFC 0x0020 /* RxFC (Extd Flow Control), OOB Chnl */ +#define PROTCAP_VOIP 0x0040 /* VoIP (implies up to 512k DSP code) */ +#define PROTCAP_CMA_ALLPR 0x0080 /* CMA support for all NL primitives */ +#define PROTCAP_FREE8 0x0100 /* not used */ +#define PROTCAP_FREE9 0x0200 /* not used */ +#define PROTCAP_FREE10 0x0400 /* not used */ +#define PROTCAP_FREE11 0x0800 /* not used */ +#define PROTCAP_FREE12 0x1000 /* not used */ +#define PROTCAP_FREE13 0x2000 /* not used */ +#define PROTCAP_FREE14 0x4000 /* not used */ +#define PROTCAP_EXTENSION 0x8000 /* used for future extensions */ +/* -----------------------------------------------------------* */ +/* Onhook data transmission ETS30065901 */ +/* Message Type */ +/*#define RESERVED4 0x4*/ +#define CALL_SETUP 0x80 +#define MESSAGE_WAITING_INDICATOR 0x82 +/*#define RESERVED84 0x84*/ +/*#define RESERVED85 0x85*/ +#define ADVICE_OF_CHARGE 0x86 +/*1111 0001 + to + 1111 1111 + F1H - Reserved for network operator use + to + FFH*/ +/* Parameter Types */ +#define DATE_AND_TIME 1 +#define CLI_PARAMETER_TYPE 2 +#define CALLED_DIRECTORY_NUMBER_PARAMETER_TYPE 3 +#define REASON_FOR_ABSENCE_OF_CLI_PARAMETER_TYPE 4 +#define NAME_PARAMETER_TYPE 7 +#define REASON_FOR_ABSENCE_OF_CALLING_PARTY_NAME_PARAMETER_TYPE 8 +#define VISUAL_INDICATOR_PARAMETER_TYPE 0xb +#define COMPLEMENTARY_CLI_PARAMETER_TYPE 0x10 +#define CALL_TYPE_PARAMETER_TYPE 0x11 +#define FIRST_CALLED_LINE_DIRECTORY_NUMBER_PARAMETER_TYPE 0x12 +#define NETWORK_MESSAGE_SYSTEM_STATUS_PARAMETER_TYPE 0x13 +#define FORWARDED_CALL_TYPE_PARAMETER_TYPE 0x15 +#define TYPE_OF_CALLING_USER_PARAMETER_TYPE 0x16 +#define REDIRECTING_NUMBER_PARAMETER_TYPE 0x1a +#define EXTENSION_FOR_NETWORK_OPERATOR_USE_PARAMETER_TYPE 0xe0 +/* -----------------------------------------------------------* */ +#else +#endif /* PC_H_INCLUDED } */ diff --git a/drivers/isdn/hardware/eicon/pc_init.h b/drivers/isdn/hardware/eicon/pc_init.h new file mode 100644 index 000000000..d1d00866e --- /dev/null +++ b/drivers/isdn/hardware/eicon/pc_init.h @@ -0,0 +1,267 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef PC_INIT_H_ +#define PC_INIT_H_ +/*------------------------------------------------------------------*/ +/* + Initialisation parameters for the card + 0x0008 <byte> TEI + 0x0009 <byte> NT2 flag + 0x000a <byte> Default DID length + 0x000b <byte> Disable watchdog flag + 0x000c <byte> Permanent connection flag + 0x000d <byte> Bit 3-8: L1 Hunt Group/Tristate + 0x000d <byte> Bit 1: QSig small CR length if set to 1 + 0x000d <byte> Bit 2: QSig small CHI length if set to 1 + 0x000e <byte> Bit 1-3: Stable L2, 0=OnDemand,1=NoDisc,2=permanent + 0x000e <byte> Bit 4: NT mode + 0x000e <byte> Bit 5: QSig Channel ID format + 0x000e <byte> Bit 6: QSig Call Forwarding Allowed Flag + 0x000e <byte> Bit 7: Disable AutoSPID Flag + 0x000f <byte> No order check flag + 0x0010 <byte> Force companding type:0=default,1=a-law,2=u-law + 0x0012 <byte> Low channel flag + 0x0013 <byte> Protocol version + 0x0014 <byte> CRC4 option:0=default,1=double_frm,2=multi_frm,3=auto + 0x0015 <byte> Bit 0: NoHscx30, Bit 1: Loopback flag, Bit 2: ForceHscx30 + 0x0016 <byte> DSP info + 0x0017-0x0019 Serial number + 0x001a <byte> Card type + 0x0020 <string> OAD 0 + 0x0040 <string> OSA 0 + 0x0060 <string> SPID 0 (if not T.1) + 0x0060 <struct> if T.1: Robbed Bit Configuration + 0x0060 length (8) + 0x0061 RBS Answer Delay + 0x0062 RBS Config Bit 3, 4: + 0 0 -> Wink Start + 1 0 -> Loop Start + 0 1 -> Ground Start + 1 1 -> reserved + Bit 5, 6: + 0 0 -> Pulse Dial -> Rotary + 1 0 -> DTMF + 0 1 -> MF + 1 1 -> reserved + 0x0063 RBS RX Digit Timeout + 0x0064 RBS Bearer Capability + 0x0065-0x0069 RBS Debug Mask + 0x0080 <string> OAD 1 + 0x00a0 <string> OSA 1 + 0x00c0 <string> SPID 1 + 0x00e0 <w-element list> Additional configuration +*/ +#define PCINIT_END_OF_LIST 0x00 +#define PCINIT_MODEM_GUARD_TONE 0x01 +#define PCINIT_MODEM_MIN_SPEED 0x02 +#define PCINIT_MODEM_MAX_SPEED 0x03 +#define PCINIT_MODEM_PROTOCOL_OPTIONS 0x04 +#define PCINIT_FAX_OPTIONS 0x05 +#define PCINIT_FAX_MAX_SPEED 0x06 +#define PCINIT_MODEM_OPTIONS 0x07 +#define PCINIT_MODEM_NEGOTIATION_MODE 0x08 +#define PCINIT_MODEM_MODULATIONS_MASK 0x09 +#define PCINIT_MODEM_TRANSMIT_LEVEL 0x0a +#define PCINIT_FAX_DISABLED_RESOLUTIONS 0x0b +#define PCINIT_FAX_MAX_RECORDING_WIDTH 0x0c +#define PCINIT_FAX_MAX_RECORDING_LENGTH 0x0d +#define PCINIT_FAX_MIN_SCANLINE_TIME 0x0e +#define PCINIT_US_EKTS_CACH_HANDLES 0x0f +#define PCINIT_US_EKTS_BEGIN_CONF 0x10 +#define PCINIT_US_EKTS_DROP_CONF 0x11 +#define PCINIT_US_EKTS_CALL_TRANSFER 0x12 +#define PCINIT_RINGERTONE_OPTION 0x13 +#define PCINIT_CARD_ADDRESS 0x14 +#define PCINIT_FPGA_FEATURES 0x15 +#define PCINIT_US_EKTS_MWI 0x16 +#define PCINIT_MODEM_SPEAKER_CONTROL 0x17 +#define PCINIT_MODEM_SPEAKER_VOLUME 0x18 +#define PCINIT_MODEM_CARRIER_WAIT_TIME 0x19 +#define PCINIT_MODEM_CARRIER_LOSS_TIME 0x1a +#define PCINIT_UNCHAN_B_MASK 0x1b +#define PCINIT_PART68_LIMITER 0x1c +#define PCINIT_XDI_FEATURES 0x1d +#define PCINIT_QSIG_DIALECT 0x1e +#define PCINIT_DISABLE_AUTOSPID_FLAG 0x1f +#define PCINIT_FORCE_VOICE_MAIL_ALERT 0x20 +#define PCINIT_PIAFS_TURNAROUND_FRAMES 0x21 +#define PCINIT_L2_COUNT 0x22 +#define PCINIT_QSIG_FEATURES 0x23 +#define PCINIT_NO_SIGNALLING 0x24 +#define PCINIT_CARD_SN 0x25 +#define PCINIT_CARD_PORT 0x26 +#define PCINIT_ALERTTO 0x27 +#define PCINIT_MODEM_EYE_SETUP 0x28 +#define PCINIT_FAX_V34_OPTIONS 0x29 +/*------------------------------------------------------------------*/ +#define PCINIT_MODEM_GUARD_TONE_NONE 0x00 +#define PCINIT_MODEM_GUARD_TONE_550HZ 0x01 +#define PCINIT_MODEM_GUARD_TONE_1800HZ 0x02 +#define PCINIT_MODEM_GUARD_TONE_CHOICES 0x03 +#define PCINIT_MODEMPROT_DISABLE_V42_V42BIS 0x0001 +#define PCINIT_MODEMPROT_DISABLE_MNP_MNP5 0x0002 +#define PCINIT_MODEMPROT_REQUIRE_PROTOCOL 0x0004 +#define PCINIT_MODEMPROT_DISABLE_V42_DETECT 0x0008 +#define PCINIT_MODEMPROT_DISABLE_COMPRESSION 0x0010 +#define PCINIT_MODEMPROT_REQUIRE_PROTOCOL_V34UP 0x0020 +#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_1200 0x0100 +#define PCINIT_MODEMPROT_BUFFER_IN_V42_DETECT 0x0200 +#define PCINIT_MODEMPROT_DISABLE_V42_SREJ 0x0400 +#define PCINIT_MODEMPROT_DISABLE_MNP3 0x0800 +#define PCINIT_MODEMPROT_DISABLE_MNP4 0x1000 +#define PCINIT_MODEMPROT_DISABLE_MNP10 0x2000 +#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_V22BIS 0x4000 +#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_V32BIS 0x8000 +#define PCINIT_MODEMCONFIG_LEASED_LINE_MODE 0x00000001L +#define PCINIT_MODEMCONFIG_4_WIRE_OPERATION 0x00000002L +#define PCINIT_MODEMCONFIG_DISABLE_BUSY_DETECT 0x00000004L +#define PCINIT_MODEMCONFIG_DISABLE_CALLING_TONE 0x00000008L +#define PCINIT_MODEMCONFIG_DISABLE_ANSWER_TONE 0x00000010L +#define PCINIT_MODEMCONFIG_ENABLE_DIAL_TONE_DET 0x00000020L +#define PCINIT_MODEMCONFIG_USE_POTS_INTERFACE 0x00000040L +#define PCINIT_MODEMCONFIG_FORCE_RAY_TAYLOR_FAX 0x00000080L +#define PCINIT_MODEMCONFIG_DISABLE_RETRAIN 0x00000100L +#define PCINIT_MODEMCONFIG_DISABLE_STEPDOWN 0x00000200L +#define PCINIT_MODEMCONFIG_DISABLE_SPLIT_SPEED 0x00000400L +#define PCINIT_MODEMCONFIG_DISABLE_TRELLIS 0x00000800L +#define PCINIT_MODEMCONFIG_ALLOW_RDL_TEST_LOOP 0x00001000L +#define PCINIT_MODEMCONFIG_DISABLE_STEPUP 0x00002000L +#define PCINIT_MODEMCONFIG_DISABLE_FLUSH_TIMER 0x00004000L +#define PCINIT_MODEMCONFIG_REVERSE_DIRECTION 0x00008000L +#define PCINIT_MODEMCONFIG_DISABLE_TX_REDUCTION 0x00010000L +#define PCINIT_MODEMCONFIG_DISABLE_PRECODING 0x00020000L +#define PCINIT_MODEMCONFIG_DISABLE_PREEMPHASIS 0x00040000L +#define PCINIT_MODEMCONFIG_DISABLE_SHAPING 0x00080000L +#define PCINIT_MODEMCONFIG_DISABLE_NONLINEAR_EN 0x00100000L +#define PCINIT_MODEMCONFIG_DISABLE_MANUALREDUCT 0x00200000L +#define PCINIT_MODEMCONFIG_DISABLE_16_POINT_TRN 0x00400000L +#define PCINIT_MODEMCONFIG_DISABLE_2400_SYMBOLS 0x01000000L +#define PCINIT_MODEMCONFIG_DISABLE_2743_SYMBOLS 0x02000000L +#define PCINIT_MODEMCONFIG_DISABLE_2800_SYMBOLS 0x04000000L +#define PCINIT_MODEMCONFIG_DISABLE_3000_SYMBOLS 0x08000000L +#define PCINIT_MODEMCONFIG_DISABLE_3200_SYMBOLS 0x10000000L +#define PCINIT_MODEMCONFIG_DISABLE_3429_SYMBOLS 0x20000000L +#define PCINIT_MODEM_NEGOTIATE_HIGHEST 0x00 +#define PCINIT_MODEM_NEGOTIATE_DISABLED 0x01 +#define PCINIT_MODEM_NEGOTIATE_IN_CLASS 0x02 +#define PCINIT_MODEM_NEGOTIATE_V100 0x03 +#define PCINIT_MODEM_NEGOTIATE_V8 0x04 +#define PCINIT_MODEM_NEGOTIATE_V8BIS 0x05 +#define PCINIT_MODEM_NEGOTIATE_CHOICES 0x06 +#define PCINIT_MODEMMODULATION_DISABLE_V21 0x00000001L +#define PCINIT_MODEMMODULATION_DISABLE_V23 0x00000002L +#define PCINIT_MODEMMODULATION_DISABLE_V22 0x00000004L +#define PCINIT_MODEMMODULATION_DISABLE_V22BIS 0x00000008L +#define PCINIT_MODEMMODULATION_DISABLE_V32 0x00000010L +#define PCINIT_MODEMMODULATION_DISABLE_V32BIS 0x00000020L +#define PCINIT_MODEMMODULATION_DISABLE_V34 0x00000040L +#define PCINIT_MODEMMODULATION_DISABLE_V90 0x00000080L +#define PCINIT_MODEMMODULATION_DISABLE_BELL103 0x00000100L +#define PCINIT_MODEMMODULATION_DISABLE_BELL212A 0x00000200L +#define PCINIT_MODEMMODULATION_DISABLE_VFC 0x00000400L +#define PCINIT_MODEMMODULATION_DISABLE_K56FLEX 0x00000800L +#define PCINIT_MODEMMODULATION_DISABLE_X2 0x00001000L +#define PCINIT_MODEMMODULATION_ENABLE_V29FDX 0x00010000L +#define PCINIT_MODEMMODULATION_ENABLE_V33 0x00020000L +#define PCINIT_MODEMMODULATION_ENABLE_V90A 0x00040000L +#define PCINIT_MODEM_TRANSMIT_LEVEL_CHOICES 0x10 +#define PCINIT_MODEM_SPEAKER_OFF 0x00 +#define PCINIT_MODEM_SPEAKER_DURING_TRAIN 0x01 +#define PCINIT_MODEM_SPEAKER_TIL_CONNECT 0x02 +#define PCINIT_MODEM_SPEAKER_ALWAYS_ON 0x03 +#define PCINIT_MODEM_SPEAKER_CHOICES 0x04 +#define PCINIT_MODEM_SPEAKER_VOLUME_MIN 0x00 +#define PCINIT_MODEM_SPEAKER_VOLUME_LOW 0x01 +#define PCINIT_MODEM_SPEAKER_VOLUME_HIGH 0x02 +#define PCINIT_MODEM_SPEAKER_VOLUME_MAX 0x03 +#define PCINIT_MODEM_SPEAKER_VOLUME_CHOICES 0x04 +/*------------------------------------------------------------------*/ +#define PCINIT_FAXCONFIG_DISABLE_FINE 0x0001 +#define PCINIT_FAXCONFIG_DISABLE_ECM 0x0002 +#define PCINIT_FAXCONFIG_ECM_64_BYTES 0x0004 +#define PCINIT_FAXCONFIG_DISABLE_2D_CODING 0x0008 +#define PCINIT_FAXCONFIG_DISABLE_T6_CODING 0x0010 +#define PCINIT_FAXCONFIG_DISABLE_UNCOMPR 0x0020 +#define PCINIT_FAXCONFIG_REFUSE_POLLING 0x0040 +#define PCINIT_FAXCONFIG_HIDE_TOTAL_PAGES 0x0080 +#define PCINIT_FAXCONFIG_HIDE_ALL_HEADLINE 0x0100 +#define PCINIT_FAXCONFIG_HIDE_PAGE_INFO 0x0180 +#define PCINIT_FAXCONFIG_HEADLINE_OPTIONS_MASK 0x0180 +#define PCINIT_FAXCONFIG_DISABLE_FEATURE_FALLBACK 0x0200 +#define PCINIT_FAXCONFIG_V34FAX_CONTROL_RATE_1200 0x0800 +#define PCINIT_FAXCONFIG_DISABLE_V34FAX 0x1000 +#define PCINIT_FAXCONFIG_DISABLE_R8_0770_OR_200 0x01 +#define PCINIT_FAXCONFIG_DISABLE_R8_1540 0x02 +#define PCINIT_FAXCONFIG_DISABLE_R16_1540_OR_400 0x04 +#define PCINIT_FAXCONFIG_DISABLE_R4_0385_OR_100 0x08 +#define PCINIT_FAXCONFIG_DISABLE_300_300 0x10 +#define PCINIT_FAXCONFIG_DISABLE_INCH_BASED 0x40 +#define PCINIT_FAXCONFIG_DISABLE_METRIC_BASED 0x80 +#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_A3 0 +#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_B4 1 +#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_A4 2 +#define PCINIT_FAXCONFIG_REC_WIDTH_COUNT 3 +#define PCINIT_FAXCONFIG_REC_LENGTH_UNLIMITED 0 +#define PCINIT_FAXCONFIG_REC_LENGTH_ISO_B4 1 +#define PCINIT_FAXCONFIG_REC_LENGTH_ISO_A4 2 +#define PCINIT_FAXCONFIG_REC_LENGTH_COUNT 3 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_00_00_00 0 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_05_05_05 1 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_05_05 2 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_10_10 3 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_10_10 4 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_20_20 5 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_20_20 6 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_40_40 7 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_8 8 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_9 9 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_10 10 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_10_05 11 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_10_05 12 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_20_10 13 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_20_10 14 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_40_20 15 +#define PCINIT_FAXCONFIG_SCANLINE_TIME_COUNT 16 +#define PCINIT_FAXCONFIG_DISABLE_TX_REDUCTION 0x00010000L +#define PCINIT_FAXCONFIG_DISABLE_PRECODING 0x00020000L +#define PCINIT_FAXCONFIG_DISABLE_PREEMPHASIS 0x00040000L +#define PCINIT_FAXCONFIG_DISABLE_SHAPING 0x00080000L +#define PCINIT_FAXCONFIG_DISABLE_NONLINEAR_EN 0x00100000L +#define PCINIT_FAXCONFIG_DISABLE_MANUALREDUCT 0x00200000L +#define PCINIT_FAXCONFIG_DISABLE_16_POINT_TRN 0x00400000L +#define PCINIT_FAXCONFIG_DISABLE_2400_SYMBOLS 0x01000000L +#define PCINIT_FAXCONFIG_DISABLE_2743_SYMBOLS 0x02000000L +#define PCINIT_FAXCONFIG_DISABLE_2800_SYMBOLS 0x04000000L +#define PCINIT_FAXCONFIG_DISABLE_3000_SYMBOLS 0x08000000L +#define PCINIT_FAXCONFIG_DISABLE_3200_SYMBOLS 0x10000000L +#define PCINIT_FAXCONFIG_DISABLE_3429_SYMBOLS 0x20000000L +/*--------------------------------------------------------------------------*/ +#define PCINIT_XDI_CMA_FOR_ALL_NL_PRIMITIVES 0x01 +/*--------------------------------------------------------------------------*/ +#define PCINIT_FPGA_PLX_ACCESS_SUPPORTED 0x01 +/*--------------------------------------------------------------------------*/ +#endif +/*--------------------------------------------------------------------------*/ diff --git a/drivers/isdn/hardware/eicon/pc_maint.h b/drivers/isdn/hardware/eicon/pc_maint.h new file mode 100644 index 000000000..496f018fb --- /dev/null +++ b/drivers/isdn/hardware/eicon/pc_maint.h @@ -0,0 +1,160 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifdef PLATFORM_GT_32BIT +/* #define POINTER_32BIT byte * __ptr32 */ +#define POINTER_32BIT dword +#else +#define POINTER_32BIT byte * +#endif +#if !defined(MIPS_SCOM) +#define BUFFER_SZ 48 +#define MAINT_OFFS 0x380 +#else +#define BUFFER_SZ 128 +#if defined(PRI) +#define MAINT_OFFS 0xef00 +#else +#define MAINT_OFFS 0xff00 +#endif +#endif +#define MIPS_BUFFER_SZ 128 +#if defined(PRI) +#define MIPS_MAINT_OFFS 0xef00 +#else +#define MIPS_MAINT_OFFS 0xff00 +#endif +#define LOG 1 +#define MEMR 2 +#define MEMW 3 +#define IOR 4 +#define IOW 5 +#define B1TEST 6 +#define B2TEST 7 +#define BTESTOFF 8 +#define DSIG_STATS 9 +#define B_CH_STATS 10 +#define D_CH_STATS 11 +#define BL1_STATS 12 +#define BL1_STATS_C 13 +#define GET_VERSION 14 +#define OS_STATS 15 +#define XLOG_SET_MASK 16 +#define XLOG_GET_MASK 17 +#define DSP_READ 20 +#define DSP_WRITE 21 +#define OK 0xff +#define MORE_EVENTS 0xfe +#define NO_EVENT 1 +struct DSigStruc +{ + byte Id; + byte u; + byte listen; + byte active; + byte sin[3]; + byte bc[6]; + byte llc[6]; + byte hlc[6]; + byte oad[20]; +}; +struct BL1Struc { + dword cx_b1; + dword cx_b2; + dword cr_b1; + dword cr_b2; + dword px_b1; + dword px_b2; + dword pr_b1; + dword pr_b2; + word er_b1; + word er_b2; +}; +struct L2Struc { + dword XTotal; + dword RTotal; + word XError; + word RError; +}; +struct OSStruc { + dword free_n; +}; +typedef union +{ + struct DSigStruc DSigStats; + struct BL1Struc BL1Stats; + struct L2Struc L2Stats; + struct OSStruc OSStats; + byte b[BUFFER_SZ]; + word w[BUFFER_SZ >> 1]; + word l[BUFFER_SZ >> 2]; /* word is wrong, do not use! Use 'd' instead. */ + dword d[BUFFER_SZ >> 2]; +} BUFFER; +typedef union +{ + struct DSigStruc DSigStats; + struct BL1Struc BL1Stats; + struct L2Struc L2Stats; + struct OSStruc OSStats; + byte b[MIPS_BUFFER_SZ]; + word w[MIPS_BUFFER_SZ >> 1]; + word l[BUFFER_SZ >> 2]; /* word is wrong, do not use! Use 'd' instead. */ + dword d[MIPS_BUFFER_SZ >> 2]; +} MIPS_BUFFER; +#if !defined(MIPS_SCOM) +struct pc_maint +{ + byte req; + byte rc; + POINTER_32BIT mem; + short length; + word port; + byte fill[6]; + BUFFER data; +}; +#else +struct pc_maint +{ + byte req; + byte rc; + byte reserved[2]; /* R3000 alignment ... */ + POINTER_32BIT mem; + short length; + word port; + byte fill[4]; /* data at offset 16 */ + BUFFER data; +}; +#endif +struct mi_pc_maint +{ + byte req; + byte rc; + byte reserved[2]; /* R3000 alignment ... */ + POINTER_32BIT mem; + short length; + word port; + byte fill[4]; /* data at offset 16 */ + MIPS_BUFFER data; +}; diff --git a/drivers/isdn/hardware/eicon/pkmaint.h b/drivers/isdn/hardware/eicon/pkmaint.h new file mode 100644 index 000000000..cf3fb14a8 --- /dev/null +++ b/drivers/isdn/hardware/eicon/pkmaint.h @@ -0,0 +1,43 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_XDI_OS_DEPENDENT_PACK_MAIN_ON_BYTE_INC__ +#define __DIVA_XDI_OS_DEPENDENT_PACK_MAIN_ON_BYTE_INC__ + + +/* + Only one purpose of this compiler dependent file to pack + structures, described in pc_maint.h so that no padding + will be included. + + With microsoft compile it is done by "pshpack1.h" and + after is restored by "poppack.h" +*/ + + +#include "pc_maint.h" + + +#endif diff --git a/drivers/isdn/hardware/eicon/platform.h b/drivers/isdn/hardware/eicon/platform.h new file mode 100644 index 000000000..62e2073c3 --- /dev/null +++ b/drivers/isdn/hardware/eicon/platform.h @@ -0,0 +1,369 @@ +/* $Id: platform.h,v 1.37.4.6 2005/01/31 12:22:20 armin Exp $ + * + * platform.h + * + * + * Copyright 2000-2003 by Armin Schindler (mac@melware.de) + * Copyright 2000 Eicon Networks + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + + +#ifndef __PLATFORM_H__ +#define __PLATFORM_H__ + +#if !defined(DIVA_BUILD) +#define DIVA_BUILD "local" +#endif + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/skbuff.h> +#include <linux/vmalloc.h> +#include <linux/proc_fs.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <asm/types.h> +#include <asm/io.h> + +#include "cardtype.h" + +/* activate debuglib for modules only */ +#ifndef MODULE +#define DIVA_NO_DEBUGLIB +#endif + +#define DIVA_USER_MODE_CARD_CONFIG 1 +#define USE_EXTENDED_DEBUGS 1 + +#define MAX_ADAPTER 32 + +#define DIVA_ISTREAM 1 + +#define MEMORY_SPACE_TYPE 0 +#define PORT_SPACE_TYPE 1 + + +#include <linux/string.h> + +#ifndef byte +#define byte u8 +#endif + +#ifndef word +#define word u16 +#endif + +#ifndef dword +#define dword u32 +#endif + +#ifndef qword +#define qword u64 +#endif + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#ifndef far +#define far +#endif + +#ifndef _pascal +#define _pascal +#endif + +#ifndef _loadds +#define _loadds +#endif + +#ifndef _cdecl +#define _cdecl +#endif + +#define MEM_TYPE_RAM 0 +#define MEM_TYPE_PORT 1 +#define MEM_TYPE_PROM 2 +#define MEM_TYPE_CTLREG 3 +#define MEM_TYPE_RESET 4 +#define MEM_TYPE_CFG 5 +#define MEM_TYPE_ADDRESS 6 +#define MEM_TYPE_CONFIG 7 +#define MEM_TYPE_CONTROL 8 + +#define MAX_MEM_TYPE 10 + +#define DIVA_OS_MEM_ATTACH_RAM(a) ((a)->ram) +#define DIVA_OS_MEM_ATTACH_PORT(a) ((a)->port) +#define DIVA_OS_MEM_ATTACH_PROM(a) ((a)->prom) +#define DIVA_OS_MEM_ATTACH_CTLREG(a) ((a)->ctlReg) +#define DIVA_OS_MEM_ATTACH_RESET(a) ((a)->reset) +#define DIVA_OS_MEM_ATTACH_CFG(a) ((a)->cfg) +#define DIVA_OS_MEM_ATTACH_ADDRESS(a) ((a)->Address) +#define DIVA_OS_MEM_ATTACH_CONFIG(a) ((a)->Config) +#define DIVA_OS_MEM_ATTACH_CONTROL(a) ((a)->Control) + +#define DIVA_OS_MEM_DETACH_RAM(a, x) do { } while (0) +#define DIVA_OS_MEM_DETACH_PORT(a, x) do { } while (0) +#define DIVA_OS_MEM_DETACH_PROM(a, x) do { } while (0) +#define DIVA_OS_MEM_DETACH_CTLREG(a, x) do { } while (0) +#define DIVA_OS_MEM_DETACH_RESET(a, x) do { } while (0) +#define DIVA_OS_MEM_DETACH_CFG(a, x) do { } while (0) +#define DIVA_OS_MEM_DETACH_ADDRESS(a, x) do { } while (0) +#define DIVA_OS_MEM_DETACH_CONFIG(a, x) do { } while (0) +#define DIVA_OS_MEM_DETACH_CONTROL(a, x) do { } while (0) + +#define DIVA_INVALID_FILE_HANDLE ((dword)(-1)) + +#define DIVAS_CONTAINING_RECORD(address, type, field) \ + ((type *)((char *)(address) - (char *)(&((type *)0)->field))) + +extern int sprintf(char *, const char *, ...); + +typedef void *LIST_ENTRY; + +typedef char DEVICE_NAME[64]; +typedef struct _ISDN_ADAPTER ISDN_ADAPTER; +typedef struct _ISDN_ADAPTER *PISDN_ADAPTER; + +typedef void (*DIVA_DI_PRINTF)(unsigned char *, ...); +#include "debuglib.h" + +#define dtrc(p) DBG_PRV0(p) +#define dbug(a, p) DBG_PRV1(p) + + +typedef struct e_info_s E_INFO; + +typedef char diva_os_dependent_devica_name_t[64]; +typedef void *PDEVICE_OBJECT; + +struct _diva_os_soft_isr; +struct _diva_os_timer; +struct _ISDN_ADAPTER; + +void diva_log_info(unsigned char *, ...); + +/* +** XDI DIDD Interface +*/ +void diva_xdi_didd_register_adapter(int card); +void diva_xdi_didd_remove_adapter(int card); + +/* +** memory allocation +*/ +static __inline__ void *diva_os_malloc(unsigned long flags, unsigned long size) +{ + void *ret = NULL; + + if (size) { + ret = (void *) vmalloc((unsigned int) size); + } + return (ret); +} +static __inline__ void diva_os_free(unsigned long flags, void *ptr) +{ + vfree(ptr); +} + +/* +** use skbuffs for message buffer +*/ +typedef struct sk_buff diva_os_message_buffer_s; +diva_os_message_buffer_s *diva_os_alloc_message_buffer(unsigned long size, void **data_buf); +void diva_os_free_message_buffer(diva_os_message_buffer_s *dmb); +#define DIVA_MESSAGE_BUFFER_LEN(x) x->len +#define DIVA_MESSAGE_BUFFER_DATA(x) x->data + +/* +** mSeconds waiting +*/ +static __inline__ void diva_os_sleep(dword mSec) +{ + msleep(mSec); +} +static __inline__ void diva_os_wait(dword mSec) +{ + mdelay(mSec); +} + +/* +** PCI Configuration space access +*/ +void PCIwrite(byte bus, byte func, int offset, void *data, int length, void *pci_dev_handle); +void PCIread(byte bus, byte func, int offset, void *data, int length, void *pci_dev_handle); + +/* +** I/O Port utilities +*/ +int diva_os_register_io_port(void *adapter, int reg, unsigned long port, + unsigned long length, const char *name, int id); +/* +** I/O port access abstraction +*/ +byte inpp(void __iomem *); +word inppw(void __iomem *); +void inppw_buffer(void __iomem *, void *, int); +void outppw(void __iomem *, word); +void outppw_buffer(void __iomem * , void*, int); +void outpp(void __iomem *, word); + +/* +** IRQ +*/ +typedef struct _diva_os_adapter_irq_info { + byte irq_nr; + int registered; + char irq_name[24]; +} diva_os_adapter_irq_info_t; +int diva_os_register_irq(void *context, byte irq, const char *name); +void diva_os_remove_irq(void *context, byte irq); + +#define diva_os_in_irq() in_irq() + +/* +** Spin Lock framework +*/ +typedef long diva_os_spin_lock_magic_t; +typedef spinlock_t diva_os_spin_lock_t; +static __inline__ int diva_os_initialize_spin_lock(spinlock_t *lock, void *unused) { \ + spin_lock_init(lock); return (0); } +static __inline__ void diva_os_enter_spin_lock(diva_os_spin_lock_t *a, \ + diva_os_spin_lock_magic_t *old_irql, \ + void *dbg) { spin_lock_bh(a); } +static __inline__ void diva_os_leave_spin_lock(diva_os_spin_lock_t *a, \ + diva_os_spin_lock_magic_t *old_irql, \ + void *dbg) { spin_unlock_bh(a); } + +#define diva_os_destroy_spin_lock(a, b) do { } while (0) + +/* +** Deffered processing framework +*/ +typedef int (*diva_os_isr_callback_t)(struct _ISDN_ADAPTER *); +typedef void (*diva_os_soft_isr_callback_t)(struct _diva_os_soft_isr *psoft_isr, void *context); + +typedef struct _diva_os_soft_isr { + void *object; + diva_os_soft_isr_callback_t callback; + void *callback_context; + char dpc_thread_name[24]; +} diva_os_soft_isr_t; + +int diva_os_initialize_soft_isr(diva_os_soft_isr_t *psoft_isr, diva_os_soft_isr_callback_t callback, void *callback_context); +int diva_os_schedule_soft_isr(diva_os_soft_isr_t *psoft_isr); +int diva_os_cancel_soft_isr(diva_os_soft_isr_t *psoft_isr); +void diva_os_remove_soft_isr(diva_os_soft_isr_t *psoft_isr); + +/* + Get time service +*/ +void diva_os_get_time(dword *sec, dword *usec); + +/* +** atomic operation, fake because we use threads +*/ +typedef int diva_os_atomic_t; +static inline diva_os_atomic_t +diva_os_atomic_increment(diva_os_atomic_t *pv) +{ + *pv += 1; + return (*pv); +} +static inline diva_os_atomic_t +diva_os_atomic_decrement(diva_os_atomic_t *pv) +{ + *pv -= 1; + return (*pv); +} + +/* +** CAPI SECTION +*/ +#define NO_CORNETN +#define IMPLEMENT_DTMF 1 +#define IMPLEMENT_ECHO_CANCELLER 1 +#define IMPLEMENT_RTP 1 +#define IMPLEMENT_T38 1 +#define IMPLEMENT_FAX_SUB_SEP_PWD 1 +#define IMPLEMENT_V18 1 +#define IMPLEMENT_DTMF_TONE 1 +#define IMPLEMENT_PIAFS 1 +#define IMPLEMENT_FAX_PAPER_FORMATS 1 +#define IMPLEMENT_VOWN 1 +#define IMPLEMENT_CAPIDTMF 1 +#define IMPLEMENT_FAX_NONSTANDARD 1 +#define VSWITCH_SUPPORT 1 + +#define IMPLEMENT_MARKED_OK_AFTER_FC 1 + +#define DIVA_IDI_RX_DMA 1 + +/* +** endian macros +** +** If only... In some cases we did use them for endianness conversion; +** unfortunately, other uses were real iomem accesses. +*/ +#define READ_BYTE(addr) readb(addr) +#define READ_WORD(addr) readw(addr) +#define READ_DWORD(addr) readl(addr) + +#define WRITE_BYTE(addr, v) writeb(v, addr) +#define WRITE_WORD(addr, v) writew(v, addr) +#define WRITE_DWORD(addr, v) writel(v, addr) + +static inline __u16 GET_WORD(void *addr) +{ + return le16_to_cpu(*(__le16 *)addr); +} +static inline __u32 GET_DWORD(void *addr) +{ + return le32_to_cpu(*(__le32 *)addr); +} +static inline void PUT_WORD(void *addr, __u16 v) +{ + *(__le16 *)addr = cpu_to_le16(v); +} +static inline void PUT_DWORD(void *addr, __u32 v) +{ + *(__le32 *)addr = cpu_to_le32(v); +} + +/* +** 32/64 bit macors +*/ +#ifdef BITS_PER_LONG +#if BITS_PER_LONG > 32 +#define PLATFORM_GT_32BIT +#define ULongToPtr(x) (void *)(unsigned long)(x) +#endif +#endif + +/* +** undef os definitions of macros we use +*/ +#undef ID_MASK +#undef N_DATA +#undef ADDR + +/* +** dump file +*/ +#define diva_os_dump_file_t char +#define diva_os_board_trace_t char +#define diva_os_dump_file(__x__) do { } while (0) + +/* +** size of internal arrays +*/ +#define MAX_DESCRIPTORS 64 + +#endif /* __PLATFORM_H__ */ diff --git a/drivers/isdn/hardware/eicon/pr_pc.h b/drivers/isdn/hardware/eicon/pr_pc.h new file mode 100644 index 000000000..a08d6d57a --- /dev/null +++ b/drivers/isdn/hardware/eicon/pr_pc.h @@ -0,0 +1,76 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +struct pr_ram { + word NextReq; /* pointer to next Req Buffer */ + word NextRc; /* pointer to next Rc Buffer */ + word NextInd; /* pointer to next Ind Buffer */ + byte ReqInput; /* number of Req Buffers sent */ + byte ReqOutput; /* number of Req Buffers returned */ + byte ReqReserved; /* number of Req Buffers reserved */ + byte Int; /* ISDN-P interrupt */ + byte XLock; /* Lock field for arbitration */ + byte RcOutput; /* number of Rc buffers received */ + byte IndOutput; /* number of Ind buffers received */ + byte IMask; /* Interrupt Mask Flag */ + byte Reserved1[2]; /* reserved field, do not use */ + byte ReadyInt; /* request field for ready interrupt */ + byte Reserved2[12]; /* reserved field, do not use */ + byte InterfaceType; /* interface type 1=16K interface */ + word Signature; /* ISDN-P initialized indication */ + byte B[1]; /* buffer space for Req,Ind and Rc */ +}; +typedef struct { + word next; + byte Req; + byte ReqId; + byte ReqCh; + byte Reserved1; + word Reference; + byte Reserved[8]; + PBUFFER XBuffer; +} REQ; +typedef struct { + word next; + byte Rc; + byte RcId; + byte RcCh; + byte Reserved1; + word Reference; + byte Reserved2[8]; +} RC; +typedef struct { + word next; + byte Ind; + byte IndId; + byte IndCh; + byte MInd; + word MLength; + word Reference; + byte RNR; + byte Reserved; + dword Ack; + PBUFFER RBuffer; +} IND; diff --git a/drivers/isdn/hardware/eicon/s_4bri.c b/drivers/isdn/hardware/eicon/s_4bri.c new file mode 100644 index 000000000..ec12165fb --- /dev/null +++ b/drivers/isdn/hardware/eicon/s_4bri.c @@ -0,0 +1,510 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "platform.h" +#include "di_defs.h" +#include "pc.h" +#include "pr_pc.h" +#include "di.h" +#include "mi_pc.h" +#include "pc_maint.h" +#include "divasync.h" +#include "pc_init.h" +#include "io.h" +#include "helpers.h" +#include "dsrv4bri.h" +#include "dsp_defs.h" +#include "sdp_hdr.h" + +/*****************************************************************************/ +#define MAX_XLOG_SIZE (64 * 1024) + +/* -------------------------------------------------------------------------- + Recovery XLOG from QBRI Card + -------------------------------------------------------------------------- */ +static void qBri_cpu_trapped(PISDN_ADAPTER IoAdapter) { + byte __iomem *base; + word *Xlog; + dword regs[4], TrapID, offset, size; + Xdesc xlogDesc; + int factor = (IoAdapter->tasks == 1) ? 1 : 2; + +/* + * check for trapped MIPS 46xx CPU, dump exception frame + */ + + base = DIVA_OS_MEM_ATTACH_CONTROL(IoAdapter); + offset = IoAdapter->ControllerNumber * (IoAdapter->MemorySize >> factor); + + TrapID = READ_DWORD(&base[0x80]); + + if ((TrapID == 0x99999999) || (TrapID == 0x99999901)) + { + dump_trap_frame(IoAdapter, &base[0x90]); + IoAdapter->trapped = 1; + } + + regs[0] = READ_DWORD((base + offset) + 0x70); + regs[1] = READ_DWORD((base + offset) + 0x74); + regs[2] = READ_DWORD((base + offset) + 0x78); + regs[3] = READ_DWORD((base + offset) + 0x7c); + regs[0] &= IoAdapter->MemorySize - 1; + + if ((regs[0] >= offset) + && (regs[0] < offset + (IoAdapter->MemorySize >> factor) - 1)) + { + if (!(Xlog = (word *)diva_os_malloc(0, MAX_XLOG_SIZE))) { + DIVA_OS_MEM_DETACH_CONTROL(IoAdapter, base); + return; + } + + size = offset + (IoAdapter->MemorySize >> factor) - regs[0]; + if (size > MAX_XLOG_SIZE) + size = MAX_XLOG_SIZE; + memcpy_fromio(Xlog, &base[regs[0]], size); + xlogDesc.buf = Xlog; + xlogDesc.cnt = READ_WORD(&base[regs[1] & (IoAdapter->MemorySize - 1)]); + xlogDesc.out = READ_WORD(&base[regs[2] & (IoAdapter->MemorySize - 1)]); + dump_xlog_buffer(IoAdapter, &xlogDesc); + diva_os_free(0, Xlog); + IoAdapter->trapped = 2; + } + DIVA_OS_MEM_DETACH_CONTROL(IoAdapter, base); +} + +/* -------------------------------------------------------------------------- + Reset QBRI Hardware + -------------------------------------------------------------------------- */ +static void reset_qBri_hardware(PISDN_ADAPTER IoAdapter) { + word volatile __iomem *qBriReset; + byte volatile __iomem *qBriCntrl; + byte volatile __iomem *p; + + qBriReset = (word volatile __iomem *)DIVA_OS_MEM_ATTACH_PROM(IoAdapter); + WRITE_WORD(qBriReset, READ_WORD(qBriReset) | PLX9054_SOFT_RESET); + diva_os_wait(1); + WRITE_WORD(qBriReset, READ_WORD(qBriReset) & ~PLX9054_SOFT_RESET); + diva_os_wait(1); + WRITE_WORD(qBriReset, READ_WORD(qBriReset) | PLX9054_RELOAD_EEPROM); + diva_os_wait(1); + WRITE_WORD(qBriReset, READ_WORD(qBriReset) & ~PLX9054_RELOAD_EEPROM); + diva_os_wait(1); + DIVA_OS_MEM_DETACH_PROM(IoAdapter, qBriReset); + + qBriCntrl = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + p = &qBriCntrl[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_RISC) : (MQ_BREG_RISC)]; + WRITE_DWORD(p, 0); + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, qBriCntrl); + + DBG_TRC(("resetted board @ reset addr 0x%08lx", qBriReset)) + DBG_TRC(("resetted board @ cntrl addr 0x%08lx", p)) + } + +/* -------------------------------------------------------------------------- + Start Card CPU + -------------------------------------------------------------------------- */ +void start_qBri_hardware(PISDN_ADAPTER IoAdapter) { + byte volatile __iomem *qBriReset; + byte volatile __iomem *p; + + p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + qBriReset = &p[(DIVA_4BRI_REVISION(IoAdapter)) ? (MQ2_BREG_RISC) : (MQ_BREG_RISC)]; + WRITE_DWORD(qBriReset, MQ_RISC_COLD_RESET_MASK); + diva_os_wait(2); + WRITE_DWORD(qBriReset, MQ_RISC_WARM_RESET_MASK | MQ_RISC_COLD_RESET_MASK); + diva_os_wait(10); + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); + + DBG_TRC(("started processor @ addr 0x%08lx", qBriReset)) + } + +/* -------------------------------------------------------------------------- + Stop Card CPU + -------------------------------------------------------------------------- */ +static void stop_qBri_hardware(PISDN_ADAPTER IoAdapter) { + byte volatile __iomem *p; + dword volatile __iomem *qBriReset; + dword volatile __iomem *qBriIrq; + dword volatile __iomem *qBriIsacDspReset; + int rev2 = DIVA_4BRI_REVISION(IoAdapter); + int reset_offset = rev2 ? (MQ2_BREG_RISC) : (MQ_BREG_RISC); + int irq_offset = rev2 ? (MQ2_BREG_IRQ_TEST) : (MQ_BREG_IRQ_TEST); + int hw_offset = rev2 ? (MQ2_ISAC_DSP_RESET) : (MQ_ISAC_DSP_RESET); + + if (IoAdapter->ControllerNumber > 0) + return; + p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + qBriReset = (dword volatile __iomem *)&p[reset_offset]; + qBriIsacDspReset = (dword volatile __iomem *)&p[hw_offset]; +/* + * clear interrupt line (reset Local Interrupt Test Register) + */ + WRITE_DWORD(qBriReset, 0); + WRITE_DWORD(qBriIsacDspReset, 0); + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); + + p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter); + WRITE_BYTE(&p[PLX9054_INTCSR], 0x00); /* disable PCI interrupts */ + DIVA_OS_MEM_DETACH_RESET(IoAdapter, p); + + p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + qBriIrq = (dword volatile __iomem *)&p[irq_offset]; + WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF); + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); + + DBG_TRC(("stopped processor @ addr 0x%08lx", qBriReset)) + + } + +/* -------------------------------------------------------------------------- + FPGA download + -------------------------------------------------------------------------- */ +#define FPGA_NAME_OFFSET 0x10 + +static byte *qBri_check_FPGAsrc(PISDN_ADAPTER IoAdapter, char *FileName, + dword *Length, dword *code) { + byte *File; + char *fpgaFile, *fpgaType, *fpgaDate, *fpgaTime; + dword fpgaFlen, fpgaTlen, fpgaDlen, cnt, year, i; + + if (!(File = (byte *)xdiLoadFile(FileName, Length, 0))) { + return (NULL); + } +/* + * scan file until FF and put id string into buffer + */ + for (i = 0; File[i] != 0xff;) + { + if (++i >= *Length) + { + DBG_FTL(("FPGA download: start of data header not found")) + xdiFreeFile(File); + return (NULL); + } + } + *code = i++; + + if ((File[i] & 0xF0) != 0x20) + { + DBG_FTL(("FPGA download: data header corrupted")) + xdiFreeFile(File); + return (NULL); + } + fpgaFlen = (dword)File[FPGA_NAME_OFFSET - 1]; + if (fpgaFlen == 0) + fpgaFlen = 12; + fpgaFile = (char *)&File[FPGA_NAME_OFFSET]; + fpgaTlen = (dword)fpgaFile[fpgaFlen + 2]; + if (fpgaTlen == 0) + fpgaTlen = 10; + fpgaType = (char *)&fpgaFile[fpgaFlen + 3]; + fpgaDlen = (dword) fpgaType[fpgaTlen + 2]; + if (fpgaDlen == 0) + fpgaDlen = 11; + fpgaDate = (char *)&fpgaType[fpgaTlen + 3]; + fpgaTime = (char *)&fpgaDate[fpgaDlen + 3]; + cnt = (dword)(((File[i] & 0x0F) << 20) + (File[i + 1] << 12) + + (File[i + 2] << 4) + (File[i + 3] >> 4)); + + if ((dword)(i + (cnt / 8)) > *Length) + { + DBG_FTL(("FPGA download: '%s' file too small (%ld < %ld)", + FileName, *Length, code + ((cnt + 7) / 8))) + xdiFreeFile(File); + return (NULL); + } + i = 0; + do + { + while ((fpgaDate[i] != '\0') + && ((fpgaDate[i] < '0') || (fpgaDate[i] > '9'))) + { + i++; + } + year = 0; + while ((fpgaDate[i] >= '0') && (fpgaDate[i] <= '9')) + year = year * 10 + (fpgaDate[i++] - '0'); + } while ((year < 2000) && (fpgaDate[i] != '\0')); + + switch (IoAdapter->cardType) { + case CARDTYPE_DIVASRV_B_2F_PCI: + break; + + default: + if (year >= 2001) { + IoAdapter->fpga_features |= PCINIT_FPGA_PLX_ACCESS_SUPPORTED; + } + } + + DBG_LOG(("FPGA[%s] file %s (%s %s) len %d", + fpgaType, fpgaFile, fpgaDate, fpgaTime, cnt)) + return (File); +} + +/******************************************************************************/ + +#define FPGA_PROG 0x0001 /* PROG enable low */ +#define FPGA_BUSY 0x0002 /* BUSY high, DONE low */ +#define FPGA_CS 0x000C /* Enable I/O pins */ +#define FPGA_CCLK 0x0100 +#define FPGA_DOUT 0x0400 +#define FPGA_DIN FPGA_DOUT /* bidirectional I/O */ + +int qBri_FPGA_download(PISDN_ADAPTER IoAdapter) { + int bit; + byte *File; + dword code, FileLength; + word volatile __iomem *addr = (word volatile __iomem *)DIVA_OS_MEM_ATTACH_PROM(IoAdapter); + word val, baseval = FPGA_CS | FPGA_PROG; + + + + if (DIVA_4BRI_REVISION(IoAdapter)) + { + char *name; + + switch (IoAdapter->cardType) { + case CARDTYPE_DIVASRV_B_2F_PCI: + name = "dsbri2f.bit"; + break; + + case CARDTYPE_DIVASRV_B_2M_V2_PCI: + case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI: + name = "dsbri2m.bit"; + break; + + default: + name = "ds4bri2.bit"; + } + + File = qBri_check_FPGAsrc(IoAdapter, name, + &FileLength, &code); + } + else + { + File = qBri_check_FPGAsrc(IoAdapter, "ds4bri.bit", + &FileLength, &code); + } + if (!File) { + DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr); + return (0); + } +/* + * prepare download, pulse PROGRAM pin down. + */ + WRITE_WORD(addr, baseval & ~FPGA_PROG); /* PROGRAM low pulse */ + WRITE_WORD(addr, baseval); /* release */ + diva_os_wait(50); /* wait until FPGA finished internal memory clear */ +/* + * check done pin, must be low + */ + if (READ_WORD(addr) & FPGA_BUSY) + { + DBG_FTL(("FPGA download: acknowledge for FPGA memory clear missing")) + xdiFreeFile(File); + DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr); + return (0); + } +/* + * put data onto the FPGA + */ + while (code < FileLength) + { + val = ((word)File[code++]) << 3; + + for (bit = 8; bit-- > 0; val <<= 1) /* put byte onto FPGA */ + { + baseval &= ~FPGA_DOUT; /* clr data bit */ + baseval |= (val & FPGA_DOUT); /* copy data bit */ + WRITE_WORD(addr, baseval); + WRITE_WORD(addr, baseval | FPGA_CCLK); /* set CCLK hi */ + WRITE_WORD(addr, baseval | FPGA_CCLK); /* set CCLK hi */ + WRITE_WORD(addr, baseval); /* set CCLK lo */ + } + } + xdiFreeFile(File); + diva_os_wait(100); + val = READ_WORD(addr); + + DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr); + + if (!(val & FPGA_BUSY)) + { + DBG_FTL(("FPGA download: chip remains in busy state (0x%04x)", val)) + return (0); + } + + return (1); +} + +static int load_qBri_hardware(PISDN_ADAPTER IoAdapter) { + return (0); +} + +/* -------------------------------------------------------------------------- + Card ISR + -------------------------------------------------------------------------- */ +static int qBri_ISR(struct _ISDN_ADAPTER *IoAdapter) { + dword volatile __iomem *qBriIrq; + + PADAPTER_LIST_ENTRY QuadroList = IoAdapter->QuadroList; + + word i; + int serviced = 0; + byte __iomem *p; + + p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter); + + if (!(READ_BYTE(&p[PLX9054_INTCSR]) & 0x80)) { + DIVA_OS_MEM_DETACH_RESET(IoAdapter, p); + return (0); + } + DIVA_OS_MEM_DETACH_RESET(IoAdapter, p); + +/* + * clear interrupt line (reset Local Interrupt Test Register) + */ + p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + qBriIrq = (dword volatile __iomem *)(&p[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_IRQ_TEST) : (MQ_BREG_IRQ_TEST)]); + WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF); + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); + + for (i = 0; i < IoAdapter->tasks; ++i) + { + IoAdapter = QuadroList->QuadroAdapter[i]; + + if (IoAdapter && IoAdapter->Initialized + && IoAdapter->tst_irq(&IoAdapter->a)) + { + IoAdapter->IrqCount++; + serviced = 1; + diva_os_schedule_soft_isr(&IoAdapter->isr_soft_isr); + } + } + + return (serviced); +} + +/* -------------------------------------------------------------------------- + Does disable the interrupt on the card + -------------------------------------------------------------------------- */ +static void disable_qBri_interrupt(PISDN_ADAPTER IoAdapter) { + dword volatile __iomem *qBriIrq; + byte __iomem *p; + + if (IoAdapter->ControllerNumber > 0) + return; +/* + * clear interrupt line (reset Local Interrupt Test Register) + */ + p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter); + WRITE_BYTE(&p[PLX9054_INTCSR], 0x00); /* disable PCI interrupts */ + DIVA_OS_MEM_DETACH_RESET(IoAdapter, p); + + p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + qBriIrq = (dword volatile __iomem *)(&p[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_IRQ_TEST) : (MQ_BREG_IRQ_TEST)]); + WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF); + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); +} + +/* -------------------------------------------------------------------------- + Install Adapter Entry Points + -------------------------------------------------------------------------- */ +static void set_common_qBri_functions(PISDN_ADAPTER IoAdapter) { + ADAPTER *a; + + a = &IoAdapter->a; + + a->ram_in = mem_in; + a->ram_inw = mem_inw; + a->ram_in_buffer = mem_in_buffer; + a->ram_look_ahead = mem_look_ahead; + a->ram_out = mem_out; + a->ram_outw = mem_outw; + a->ram_out_buffer = mem_out_buffer; + a->ram_inc = mem_inc; + + IoAdapter->out = pr_out; + IoAdapter->dpc = pr_dpc; + IoAdapter->tst_irq = scom_test_int; + IoAdapter->clr_irq = scom_clear_int; + IoAdapter->pcm = (struct pc_maint *)MIPS_MAINT_OFFS; + + IoAdapter->load = load_qBri_hardware; + + IoAdapter->disIrq = disable_qBri_interrupt; + IoAdapter->rstFnc = reset_qBri_hardware; + IoAdapter->stop = stop_qBri_hardware; + IoAdapter->trapFnc = qBri_cpu_trapped; + + IoAdapter->diva_isr_handler = qBri_ISR; + + IoAdapter->a.io = (void *)IoAdapter; +} + +static void set_qBri_functions(PISDN_ADAPTER IoAdapter) { + if (!IoAdapter->tasks) { + IoAdapter->tasks = MQ_INSTANCE_COUNT; + } + IoAdapter->MemorySize = MQ_MEMORY_SIZE; + set_common_qBri_functions(IoAdapter); + diva_os_set_qBri_functions(IoAdapter); +} + +static void set_qBri2_functions(PISDN_ADAPTER IoAdapter) { + if (!IoAdapter->tasks) { + IoAdapter->tasks = MQ_INSTANCE_COUNT; + } + IoAdapter->MemorySize = (IoAdapter->tasks == 1) ? BRI2_MEMORY_SIZE : MQ2_MEMORY_SIZE; + set_common_qBri_functions(IoAdapter); + diva_os_set_qBri2_functions(IoAdapter); +} + +/******************************************************************************/ + +void prepare_qBri_functions(PISDN_ADAPTER IoAdapter) { + + set_qBri_functions(IoAdapter->QuadroList->QuadroAdapter[0]); + set_qBri_functions(IoAdapter->QuadroList->QuadroAdapter[1]); + set_qBri_functions(IoAdapter->QuadroList->QuadroAdapter[2]); + set_qBri_functions(IoAdapter->QuadroList->QuadroAdapter[3]); + +} + +void prepare_qBri2_functions(PISDN_ADAPTER IoAdapter) { + if (!IoAdapter->tasks) { + IoAdapter->tasks = MQ_INSTANCE_COUNT; + } + + set_qBri2_functions(IoAdapter->QuadroList->QuadroAdapter[0]); + if (IoAdapter->tasks > 1) { + set_qBri2_functions(IoAdapter->QuadroList->QuadroAdapter[1]); + set_qBri2_functions(IoAdapter->QuadroList->QuadroAdapter[2]); + set_qBri2_functions(IoAdapter->QuadroList->QuadroAdapter[3]); + } + +} + +/* -------------------------------------------------------------------------- */ diff --git a/drivers/isdn/hardware/eicon/s_bri.c b/drivers/isdn/hardware/eicon/s_bri.c new file mode 100644 index 000000000..6a5bb7462 --- /dev/null +++ b/drivers/isdn/hardware/eicon/s_bri.c @@ -0,0 +1,191 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "platform.h" +#include "di_defs.h" +#include "pc.h" +#include "pr_pc.h" +#include "di.h" +#include "mi_pc.h" +#include "pc_maint.h" +#include "divasync.h" +#include "io.h" +#include "helpers.h" +#include "dsrv_bri.h" +#include "dsp_defs.h" +/*****************************************************************************/ +#define MAX_XLOG_SIZE (64 * 1024) +/* -------------------------------------------------------------------------- + Investigate card state, recovery trace buffer + -------------------------------------------------------------------------- */ +static void bri_cpu_trapped(PISDN_ADAPTER IoAdapter) { + byte __iomem *addrHi, *addrLo, *ioaddr; + word *Xlog; + dword regs[4], i, size; + Xdesc xlogDesc; + byte __iomem *Port; +/* + * first read pointers and trap frame + */ + if (!(Xlog = (word *)diva_os_malloc(0, MAX_XLOG_SIZE))) + return; + Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter); + addrHi = Port + ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH); + addrLo = Port + ADDR; + ioaddr = Port + DATA; + outpp(addrHi, 0); + outppw(addrLo, 0); + for (i = 0; i < 0x100; Xlog[i++] = inppw(ioaddr)); +/* + * check for trapped MIPS 3xxx CPU, dump only exception frame + */ + if (GET_DWORD(&Xlog[0x80 / sizeof(Xlog[0])]) == 0x99999999) + { + dump_trap_frame(IoAdapter, &((byte *)Xlog)[0x90]); + IoAdapter->trapped = 1; + } + regs[0] = GET_DWORD(&((byte *)Xlog)[0x70]); + regs[1] = GET_DWORD(&((byte *)Xlog)[0x74]); + regs[2] = GET_DWORD(&((byte *)Xlog)[0x78]); + regs[3] = GET_DWORD(&((byte *)Xlog)[0x7c]); + outpp(addrHi, (regs[1] >> 16) & 0x7F); + outppw(addrLo, regs[1] & 0xFFFF); + xlogDesc.cnt = inppw(ioaddr); + outpp(addrHi, (regs[2] >> 16) & 0x7F); + outppw(addrLo, regs[2] & 0xFFFF); + xlogDesc.out = inppw(ioaddr); + xlogDesc.buf = Xlog; + regs[0] &= IoAdapter->MemorySize - 1; + if ((regs[0] < IoAdapter->MemorySize - 1)) + { + size = IoAdapter->MemorySize - regs[0]; + if (size > MAX_XLOG_SIZE) + size = MAX_XLOG_SIZE; + for (i = 0; i < (size / sizeof(*Xlog)); regs[0] += 2) + { + outpp(addrHi, (regs[0] >> 16) & 0x7F); + outppw(addrLo, regs[0] & 0xFFFF); + Xlog[i++] = inppw(ioaddr); + } + dump_xlog_buffer(IoAdapter, &xlogDesc); + diva_os_free(0, Xlog); + IoAdapter->trapped = 2; + } + outpp(addrHi, (byte)((BRI_UNCACHED_ADDR(IoAdapter->MemoryBase + IoAdapter->MemorySize - + BRI_SHARED_RAM_SIZE)) >> 16)); + outppw(addrLo, 0x00); + DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port); +} +/* --------------------------------------------------------------------- + Reset hardware + --------------------------------------------------------------------- */ +static void reset_bri_hardware(PISDN_ADAPTER IoAdapter) { + byte __iomem *p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + outpp(p, 0x00); + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); +} +/* --------------------------------------------------------------------- + Halt system + --------------------------------------------------------------------- */ +static void stop_bri_hardware(PISDN_ADAPTER IoAdapter) { + byte __iomem *p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter); + if (p) { + outpp(p, 0x00); /* disable interrupts ! */ + } + DIVA_OS_MEM_DETACH_RESET(IoAdapter, p); + p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + outpp(p, 0x00); /* clear int, halt cpu */ + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); +} +static int load_bri_hardware(PISDN_ADAPTER IoAdapter) { + return (0); +} +/******************************************************************************/ +static int bri_ISR(struct _ISDN_ADAPTER *IoAdapter) { + byte __iomem *p; + + p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + if (!(inpp(p) & 0x01)) { + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); + return (0); + } + /* + clear interrupt line + */ + outpp(p, 0x08); + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); + IoAdapter->IrqCount++; + if (IoAdapter->Initialized) { + diva_os_schedule_soft_isr(&IoAdapter->isr_soft_isr); + } + return (1); +} +/* -------------------------------------------------------------------------- + Disable IRQ in the card hardware + -------------------------------------------------------------------------- */ +static void disable_bri_interrupt(PISDN_ADAPTER IoAdapter) { + byte __iomem *p; + p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter); + if (p) + { + outpp(p, 0x00); /* disable interrupts ! */ + } + DIVA_OS_MEM_DETACH_RESET(IoAdapter, p); + p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter); + outpp(p, 0x00); /* clear int, halt cpu */ + DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p); +} +/* ------------------------------------------------------------------------- + Fill card entry points + ------------------------------------------------------------------------- */ +void prepare_maestra_functions(PISDN_ADAPTER IoAdapter) { + ADAPTER *a = &IoAdapter->a; + a->ram_in = io_in; + a->ram_inw = io_inw; + a->ram_in_buffer = io_in_buffer; + a->ram_look_ahead = io_look_ahead; + a->ram_out = io_out; + a->ram_outw = io_outw; + a->ram_out_buffer = io_out_buffer; + a->ram_inc = io_inc; + IoAdapter->MemoryBase = BRI_MEMORY_BASE; + IoAdapter->MemorySize = BRI_MEMORY_SIZE; + IoAdapter->out = pr_out; + IoAdapter->dpc = pr_dpc; + IoAdapter->tst_irq = scom_test_int; + IoAdapter->clr_irq = scom_clear_int; + IoAdapter->pcm = (struct pc_maint *)MIPS_MAINT_OFFS; + IoAdapter->load = load_bri_hardware; + IoAdapter->disIrq = disable_bri_interrupt; + IoAdapter->rstFnc = reset_bri_hardware; + IoAdapter->stop = stop_bri_hardware; + IoAdapter->trapFnc = bri_cpu_trapped; + IoAdapter->diva_isr_handler = bri_ISR; + /* + Prepare OS dependent functions + */ + diva_os_prepare_maestra_functions(IoAdapter); +} +/* -------------------------------------------------------------------------- */ diff --git a/drivers/isdn/hardware/eicon/s_pri.c b/drivers/isdn/hardware/eicon/s_pri.c new file mode 100644 index 000000000..ddd0e0ef8 --- /dev/null +++ b/drivers/isdn/hardware/eicon/s_pri.c @@ -0,0 +1,205 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "platform.h" +#include "di_defs.h" +#include "pc.h" +#include "pr_pc.h" +#include "di.h" +#include "mi_pc.h" +#include "pc_maint.h" +#include "divasync.h" +#include "io.h" +#include "helpers.h" +#include "dsrv_pri.h" +#include "dsp_defs.h" +/*****************************************************************************/ +#define MAX_XLOG_SIZE (64 * 1024) +/* ------------------------------------------------------------------------- + Does return offset between ADAPTER->ram and real begin of memory + ------------------------------------------------------------------------- */ +static dword pri_ram_offset(ADAPTER *a) { + return ((dword)MP_SHARED_RAM_OFFSET); +} +/* ------------------------------------------------------------------------- + Recovery XLOG buffer from the card + ------------------------------------------------------------------------- */ +static void pri_cpu_trapped(PISDN_ADAPTER IoAdapter) { + byte __iomem *base; + word *Xlog; + dword regs[4], TrapID, size; + Xdesc xlogDesc; +/* + * check for trapped MIPS 46xx CPU, dump exception frame + */ + base = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter); + TrapID = READ_DWORD(&base[0x80]); + if ((TrapID == 0x99999999) || (TrapID == 0x99999901)) + { + dump_trap_frame(IoAdapter, &base[0x90]); + IoAdapter->trapped = 1; + } + regs[0] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x70]); + regs[1] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x74]); + regs[2] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x78]); + regs[3] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x7c]); + regs[0] &= IoAdapter->MemorySize - 1; + if ((regs[0] < IoAdapter->MemorySize - 1)) + { + if (!(Xlog = (word *)diva_os_malloc(0, MAX_XLOG_SIZE))) { + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, base); + return; + } + size = IoAdapter->MemorySize - regs[0]; + if (size > MAX_XLOG_SIZE) + size = MAX_XLOG_SIZE; + memcpy_fromio(Xlog, &base[regs[0]], size); + xlogDesc.buf = Xlog; + xlogDesc.cnt = READ_WORD(&base[regs[1] & (IoAdapter->MemorySize - 1)]); + xlogDesc.out = READ_WORD(&base[regs[2] & (IoAdapter->MemorySize - 1)]); + dump_xlog_buffer(IoAdapter, &xlogDesc); + diva_os_free(0, Xlog); + IoAdapter->trapped = 2; + } + DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, base); +} +/* ------------------------------------------------------------------------- + Hardware reset of PRI card + ------------------------------------------------------------------------- */ +static void reset_pri_hardware(PISDN_ADAPTER IoAdapter) { + byte __iomem *p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter); + WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2); + diva_os_wait(50); + WRITE_BYTE(p, 0x00); + diva_os_wait(50); + DIVA_OS_MEM_DETACH_RESET(IoAdapter, p); +} +/* ------------------------------------------------------------------------- + Stop Card Hardware + ------------------------------------------------------------------------- */ +static void stop_pri_hardware(PISDN_ADAPTER IoAdapter) { + dword i; + byte __iomem *p; + dword volatile __iomem *cfgReg = (void __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter); + WRITE_DWORD(&cfgReg[3], 0); + WRITE_DWORD(&cfgReg[1], 0); + DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg); + IoAdapter->a.ram_out(&IoAdapter->a, &RAM->SWReg, SWREG_HALT_CPU); + i = 0; + while ((i < 100) && (IoAdapter->a.ram_in(&IoAdapter->a, &RAM->SWReg) != 0)) + { + diva_os_wait(1); + i++; + } + DBG_TRC(("%s: PRI stopped (%d)", IoAdapter->Name, i)) + cfgReg = (void __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter); + WRITE_DWORD(&cfgReg[0], ((dword)(~0x03E00000))); + DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg); + diva_os_wait(1); + p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter); + WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2); + DIVA_OS_MEM_DETACH_RESET(IoAdapter, p); +} +static int load_pri_hardware(PISDN_ADAPTER IoAdapter) { + return (0); +} +/* -------------------------------------------------------------------------- + PRI Adapter interrupt Service Routine + -------------------------------------------------------------------------- */ +static int pri_ISR(struct _ISDN_ADAPTER *IoAdapter) { + byte __iomem *cfg = DIVA_OS_MEM_ATTACH_CFG(IoAdapter); + if (!(READ_DWORD(cfg) & 0x80000000)) { + DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfg); + return (0); + } + /* + clear interrupt line + */ + WRITE_DWORD(cfg, (dword)~0x03E00000); + DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfg); + IoAdapter->IrqCount++; + if (IoAdapter->Initialized) + { + diva_os_schedule_soft_isr(&IoAdapter->isr_soft_isr); + } + return (1); +} +/* ------------------------------------------------------------------------- + Disable interrupt in the card hardware + ------------------------------------------------------------------------- */ +static void disable_pri_interrupt(PISDN_ADAPTER IoAdapter) { + dword volatile __iomem *cfgReg = (dword volatile __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter); + WRITE_DWORD(&cfgReg[3], 0); + WRITE_DWORD(&cfgReg[1], 0); + WRITE_DWORD(&cfgReg[0], (dword)(~0x03E00000)); + DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg); +} +/* ------------------------------------------------------------------------- + Install entry points for PRI Adapter + ------------------------------------------------------------------------- */ +static void prepare_common_pri_functions(PISDN_ADAPTER IoAdapter) { + ADAPTER *a = &IoAdapter->a; + a->ram_in = mem_in; + a->ram_inw = mem_inw; + a->ram_in_buffer = mem_in_buffer; + a->ram_look_ahead = mem_look_ahead; + a->ram_out = mem_out; + a->ram_outw = mem_outw; + a->ram_out_buffer = mem_out_buffer; + a->ram_inc = mem_inc; + a->ram_offset = pri_ram_offset; + a->ram_out_dw = mem_out_dw; + a->ram_in_dw = mem_in_dw; + a->istream_wakeup = pr_stream; + IoAdapter->out = pr_out; + IoAdapter->dpc = pr_dpc; + IoAdapter->tst_irq = scom_test_int; + IoAdapter->clr_irq = scom_clear_int; + IoAdapter->pcm = (struct pc_maint *)(MIPS_MAINT_OFFS + - MP_SHARED_RAM_OFFSET); + IoAdapter->load = load_pri_hardware; + IoAdapter->disIrq = disable_pri_interrupt; + IoAdapter->rstFnc = reset_pri_hardware; + IoAdapter->stop = stop_pri_hardware; + IoAdapter->trapFnc = pri_cpu_trapped; + IoAdapter->diva_isr_handler = pri_ISR; +} +/* ------------------------------------------------------------------------- + Install entry points for PRI Adapter + ------------------------------------------------------------------------- */ +void prepare_pri_functions(PISDN_ADAPTER IoAdapter) { + IoAdapter->MemorySize = MP_MEMORY_SIZE; + prepare_common_pri_functions(IoAdapter); + diva_os_prepare_pri_functions(IoAdapter); +} +/* ------------------------------------------------------------------------- + Install entry points for PRI Rev.2 Adapter + ------------------------------------------------------------------------- */ +void prepare_pri2_functions(PISDN_ADAPTER IoAdapter) { + IoAdapter->MemorySize = MP2_MEMORY_SIZE; + prepare_common_pri_functions(IoAdapter); + diva_os_prepare_pri2_functions(IoAdapter); +} +/* ------------------------------------------------------------------------- */ diff --git a/drivers/isdn/hardware/eicon/sdp_hdr.h b/drivers/isdn/hardware/eicon/sdp_hdr.h new file mode 100644 index 000000000..5e20f8d68 --- /dev/null +++ b/drivers/isdn/hardware/eicon/sdp_hdr.h @@ -0,0 +1,117 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef __DIVA_SOFT_DSP_TASK_ENTRY_H__ +#define __DIVA_SOFT_DSP_TASK_ENTRY_H__ +/* + The soft DSP image is described by binary header contained on begin of this + image: + OFFSET FROM IMAGE START | VARIABLE + ------------------------------------------------------------------------ + DIVA_MIPS_TASK_IMAGE_LINK_OFFS | link to the next image + ---------------------------------------------------------------------- + DIVA_MIPS_TASK_IMAGE_GP_OFFS | image gp register value, void* + ---------------------------------------------------------------------- + DIVA_MIPS_TASK_IMAGE_ENTRY_OFFS | diva_mips_sdp_task_entry_t* + ---------------------------------------------------------------------- + DIVA_MIPS_TASK_IMAGE_LOAD_ADDR_OFFS | image image start address (void*) + ---------------------------------------------------------------------- + DIVA_MIPS_TASK_IMAGE_END_ADDR_OFFS | image image end address (void*) + ---------------------------------------------------------------------- + DIVA_MIPS_TASK_IMAGE_ID_STRING_OFFS | image id string char[...]; + ---------------------------------------------------------------------- +*/ +#define DIVA_MIPS_TASK_IMAGE_LINK_OFFS 0x6C +#define DIVA_MIPS_TASK_IMAGE_GP_OFFS 0x70 +#define DIVA_MIPS_TASK_IMAGE_ENTRY_OFFS 0x74 +#define DIVA_MIPS_TASK_IMAGE_LOAD_ADDR_OFFS 0x78 +#define DIVA_MIPS_TASK_IMAGE_END_ADDR_OFFS 0x7c +#define DIVA_MIPS_TASK_IMAGE_ID_STRING_OFFS 0x80 +/* + This function is called in order to set GP register of this task + This function should be always called before any function of the + task is called +*/ +typedef void (*diva_task_set_prog_gp_proc_t)(void *new_gp); +/* + This function is called to clear .bss at task initialization step +*/ +typedef void (*diva_task_sys_reset_proc_t)(void); +/* + This function is called in order to provide GP of master call to + task, that will be used by calls from the task to the master +*/ +typedef void (*diva_task_set_main_gp_proc_t)(void *main_gp); +/* + This function is called to provide address of 'dprintf' function + to the task +*/ +typedef word (*diva_prt_proc_t)(char *, ...); +typedef void (*diva_task_set_prt_proc_t)(diva_prt_proc_t fn); +/* + This function is called to set task PID +*/ +typedef void (*diva_task_set_pid_proc_t)(dword id); +/* + This function is called for run-time task init +*/ +typedef int (*diva_task_run_time_init_proc_t)(void*, dword); +/* + This function is called from system scheduler or from timer +*/ +typedef void (*diva_task_callback_proc_t)(void); +/* + This callback is used by task to get current time im mS +*/ +typedef dword (*diva_task_get_tick_count_proc_t)(void); +typedef void (*diva_task_set_get_time_proc_t)(\ + diva_task_get_tick_count_proc_t fn); +typedef struct _diva_mips_sdp_task_entry { + diva_task_set_prog_gp_proc_t set_gp_proc; + diva_task_sys_reset_proc_t sys_reset_proc; + diva_task_set_main_gp_proc_t set_main_gp_proc; + diva_task_set_prt_proc_t set_dprintf_proc; + diva_task_set_pid_proc_t set_pid_proc; + diva_task_run_time_init_proc_t run_time_init_proc; + diva_task_callback_proc_t task_callback_proc; + diva_task_callback_proc_t timer_callback_proc; + diva_task_set_get_time_proc_t set_get_time_proc; + void *last_entry_proc; +} diva_mips_sdp_task_entry_t; +/* + 'last_entry_proc' should be set to zero and is used for future extensuios +*/ +typedef struct _diva_mips_sw_task { + diva_mips_sdp_task_entry_t sdp_entry; + void *sdp_gp_reg; + void *own_gp_reg; +} diva_mips_sw_task_t; +#if !defined(DIVA_BRI2F_SDP_1_NAME) +#define DIVA_BRI2F_SDP_1_NAME "sdp0.2q0" +#endif +#if !defined(DIVA_BRI2F_SDP_2_NAME) +#define DIVA_BRI2F_SDP_2_NAME "sdp1.2q0" +#endif +#endif diff --git a/drivers/isdn/hardware/eicon/um_idi.c b/drivers/isdn/hardware/eicon/um_idi.c new file mode 100644 index 000000000..db4dd4ff3 --- /dev/null +++ b/drivers/isdn/hardware/eicon/um_idi.c @@ -0,0 +1,886 @@ +// SPDX-License-Identifier: GPL-2.0 +/* $Id: um_idi.c,v 1.14 2004/03/21 17:54:37 armin Exp $ */ + +#include "platform.h" +#include "di_defs.h" +#include "pc.h" +#include "dqueue.h" +#include "adapter.h" +#include "entity.h" +#include "um_xdi.h" +#include "um_idi.h" +#include "debuglib.h" +#include "divasync.h" + +#define DIVAS_MAX_XDI_ADAPTERS 64 + +/* -------------------------------------------------------------------------- + IMPORTS + -------------------------------------------------------------------------- */ +extern void diva_os_wakeup_read(void *os_context); +extern void diva_os_wakeup_close(void *os_context); +/* -------------------------------------------------------------------------- + LOCALS + -------------------------------------------------------------------------- */ +static LIST_HEAD(adapter_q); +static diva_os_spin_lock_t adapter_lock; + +static diva_um_idi_adapter_t *diva_um_idi_find_adapter(dword nr); +static void cleanup_adapter(diva_um_idi_adapter_t *a); +static void cleanup_entity(divas_um_idi_entity_t *e); +static int diva_user_mode_idi_adapter_features(diva_um_idi_adapter_t *a, + diva_um_idi_adapter_features_t + *features); +static int process_idi_request(divas_um_idi_entity_t *e, + const diva_um_idi_req_hdr_t *req); +static int process_idi_rc(divas_um_idi_entity_t *e, byte rc); +static int process_idi_ind(divas_um_idi_entity_t *e, byte ind); +static int write_return_code(divas_um_idi_entity_t *e, byte rc); + +/* -------------------------------------------------------------------------- + MAIN + -------------------------------------------------------------------------- */ +int diva_user_mode_idi_init(void) +{ + diva_os_initialize_spin_lock(&adapter_lock, "adapter"); + return (0); +} + +/* -------------------------------------------------------------------------- + Copy adapter features to user supplied buffer + -------------------------------------------------------------------------- */ +static int +diva_user_mode_idi_adapter_features(diva_um_idi_adapter_t *a, + diva_um_idi_adapter_features_t * + features) +{ + IDI_SYNC_REQ sync_req; + + if ((a) && (a->d.request)) { + features->type = a->d.type; + features->features = a->d.features; + features->channels = a->d.channels; + memset(features->name, 0, sizeof(features->name)); + + sync_req.GetName.Req = 0; + sync_req.GetName.Rc = IDI_SYNC_REQ_GET_NAME; + (*(a->d.request)) ((ENTITY *)&sync_req); + strlcpy(features->name, sync_req.GetName.name, + sizeof(features->name)); + + sync_req.GetSerial.Req = 0; + sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL; + sync_req.GetSerial.serial = 0; + (*(a->d.request))((ENTITY *)&sync_req); + features->serial_number = sync_req.GetSerial.serial; + } + + return ((a) ? 0 : -1); +} + +/* -------------------------------------------------------------------------- + REMOVE ADAPTER + -------------------------------------------------------------------------- */ +void diva_user_mode_idi_remove_adapter(int adapter_nr) +{ + struct list_head *tmp; + diva_um_idi_adapter_t *a; + + list_for_each(tmp, &adapter_q) { + a = list_entry(tmp, diva_um_idi_adapter_t, link); + if (a->adapter_nr == adapter_nr) { + list_del(tmp); + cleanup_adapter(a); + DBG_LOG(("DIDD: del adapter(%d)", a->adapter_nr)); + diva_os_free(0, a); + break; + } + } +} + +/* -------------------------------------------------------------------------- + CALLED ON DRIVER EXIT (UNLOAD) + -------------------------------------------------------------------------- */ +void diva_user_mode_idi_finit(void) +{ + struct list_head *tmp, *safe; + diva_um_idi_adapter_t *a; + + list_for_each_safe(tmp, safe, &adapter_q) { + a = list_entry(tmp, diva_um_idi_adapter_t, link); + list_del(tmp); + cleanup_adapter(a); + DBG_LOG(("DIDD: del adapter(%d)", a->adapter_nr)); + diva_os_free(0, a); + } + diva_os_destroy_spin_lock(&adapter_lock, "adapter"); +} + +/* ------------------------------------------------------------------------- + CREATE AND INIT IDI ADAPTER + ------------------------------------------------------------------------- */ +int diva_user_mode_idi_create_adapter(const DESCRIPTOR *d, int adapter_nr) +{ + diva_os_spin_lock_magic_t old_irql; + diva_um_idi_adapter_t *a = + (diva_um_idi_adapter_t *) diva_os_malloc(0, + sizeof + (diva_um_idi_adapter_t)); + + if (!a) { + return (-1); + } + memset(a, 0x00, sizeof(*a)); + INIT_LIST_HEAD(&a->entity_q); + + a->d = *d; + a->adapter_nr = adapter_nr; + + DBG_LOG(("DIDD_ADD A(%d), type:%02x, features:%04x, channels:%d", + adapter_nr, a->d.type, a->d.features, a->d.channels)); + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "create_adapter"); + list_add_tail(&a->link, &adapter_q); + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_adapter"); + return (0); +} + +/* ------------------------------------------------------------------------ + Find adapter by Adapter number + ------------------------------------------------------------------------ */ +static diva_um_idi_adapter_t *diva_um_idi_find_adapter(dword nr) +{ + diva_um_idi_adapter_t *a = NULL; + struct list_head *tmp; + + list_for_each(tmp, &adapter_q) { + a = list_entry(tmp, diva_um_idi_adapter_t, link); + DBG_TRC(("find_adapter: (%d)-(%d)", nr, a->adapter_nr)); + if (a->adapter_nr == (int)nr) + break; + a = NULL; + } + return (a); +} + +/* ------------------------------------------------------------------------ + Cleanup this adapter and cleanup/delete all entities assigned + to this adapter + ------------------------------------------------------------------------ */ +static void cleanup_adapter(diva_um_idi_adapter_t *a) +{ + struct list_head *tmp, *safe; + divas_um_idi_entity_t *e; + + list_for_each_safe(tmp, safe, &a->entity_q) { + e = list_entry(tmp, divas_um_idi_entity_t, link); + list_del(tmp); + cleanup_entity(e); + if (e->os_context) { + diva_os_wakeup_read(e->os_context); + diva_os_wakeup_close(e->os_context); + } + } + memset(&a->d, 0x00, sizeof(DESCRIPTOR)); +} + +/* ------------------------------------------------------------------------ + Cleanup, but NOT delete this entity + ------------------------------------------------------------------------ */ +static void cleanup_entity(divas_um_idi_entity_t *e) +{ + e->os_ref = NULL; + e->status = 0; + e->adapter = NULL; + e->e.Id = 0; + e->rc_count = 0; + + e->status |= DIVA_UM_IDI_REMOVED; + e->status |= DIVA_UM_IDI_REMOVE_PENDING; + + diva_data_q_finit(&e->data); + diva_data_q_finit(&e->rc); +} + + +/* ------------------------------------------------------------------------ + Create ENTITY, link it to the adapter and remove pointer to entity + ------------------------------------------------------------------------ */ +void *divas_um_idi_create_entity(dword adapter_nr, void *file) +{ + divas_um_idi_entity_t *e; + diva_um_idi_adapter_t *a; + diva_os_spin_lock_magic_t old_irql; + + if ((e = (divas_um_idi_entity_t *) diva_os_malloc(0, sizeof(*e)))) { + memset(e, 0x00, sizeof(*e)); + if (! + (e->os_context = + diva_os_malloc(0, diva_os_get_context_size()))) { + DBG_LOG(("E(%08x) no memory for os context", e)); + diva_os_free(0, e); + return NULL; + } + memset(e->os_context, 0x00, diva_os_get_context_size()); + + if ((diva_data_q_init(&e->data, 2048 + 512, 16))) { + diva_os_free(0, e->os_context); + diva_os_free(0, e); + return NULL; + } + if ((diva_data_q_init(&e->rc, sizeof(diva_um_idi_ind_hdr_t), 2))) { + diva_data_q_finit(&e->data); + diva_os_free(0, e->os_context); + diva_os_free(0, e); + return NULL; + } + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "create_entity"); + /* + Look for Adapter requested + */ + if (!(a = diva_um_idi_find_adapter(adapter_nr))) { + /* + No adapter was found, or this adapter was removed + */ + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_entity"); + + DBG_LOG(("A: no adapter(%ld)", adapter_nr)); + + cleanup_entity(e); + diva_os_free(0, e->os_context); + diva_os_free(0, e); + + return NULL; + } + + e->os_ref = file; /* link to os handle */ + e->adapter = a; /* link to adapter */ + + list_add_tail(&e->link, &a->entity_q); /* link from adapter */ + + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_entity"); + + DBG_LOG(("A(%ld), create E(%08x)", adapter_nr, e)); + } + + return (e); +} + +/* ------------------------------------------------------------------------ + Unlink entity and free memory + ------------------------------------------------------------------------ */ +int divas_um_idi_delete_entity(int adapter_nr, void *entity) +{ + divas_um_idi_entity_t *e; + diva_um_idi_adapter_t *a; + diva_os_spin_lock_magic_t old_irql; + + if (!(e = (divas_um_idi_entity_t *) entity)) + return (-1); + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "delete_entity"); + if ((a = e->adapter)) { + list_del(&e->link); + } + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "delete_entity"); + + diva_um_idi_stop_wdog(entity); + cleanup_entity(e); + diva_os_free(0, e->os_context); + memset(e, 0x00, sizeof(*e)); + + DBG_LOG(("A(%d) remove E:%08x", adapter_nr, e)); + diva_os_free(0, e); + + return (0); +} + +/* -------------------------------------------------------------------------- + Called by application to read data from IDI + -------------------------------------------------------------------------- */ +int diva_um_idi_read(void *entity, + void *os_handle, + void *dst, + int max_length, divas_um_idi_copy_to_user_fn_t cp_fn) +{ + divas_um_idi_entity_t *e; + diva_um_idi_adapter_t *a; + const void *data; + int length, ret = 0; + diva_um_idi_data_queue_t *q; + diva_os_spin_lock_magic_t old_irql; + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "read"); + + e = (divas_um_idi_entity_t *) entity; + if (!e || (!(a = e->adapter)) || + (e->status & DIVA_UM_IDI_REMOVE_PENDING) || + (e->status & DIVA_UM_IDI_REMOVED) || + (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) { + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "read"); + DBG_ERR(("E(%08x) read failed - adapter removed", e)) + return (-1); + } + + DBG_TRC(("A(%d) E(%08x) read(%d)", a->adapter_nr, e, max_length)); + + /* + Try to read return code first + */ + data = diva_data_q_get_segment4read(&e->rc); + q = &e->rc; + + /* + No return codes available, read indications now + */ + if (!data) { + if (!(e->status & DIVA_UM_IDI_RC_PENDING)) { + DBG_TRC(("A(%d) E(%08x) read data", a->adapter_nr, e)); + data = diva_data_q_get_segment4read(&e->data); + q = &e->data; + } + } else { + e->status &= ~DIVA_UM_IDI_RC_PENDING; + DBG_TRC(("A(%d) E(%08x) read rc", a->adapter_nr, e)); + } + + if (data) { + if ((length = diva_data_q_get_segment_length(q)) > + max_length) { + /* + Not enough space to read message + */ + DBG_ERR(("A: A(%d) E(%08x) read small buffer", + a->adapter_nr, e, ret)); + diva_os_leave_spin_lock(&adapter_lock, &old_irql, + "read"); + return (-2); + } + /* + Copy it to user, this function does access ONLY locked an verified + memory, also we can access it witch spin lock held + */ + + if ((ret = (*cp_fn) (os_handle, dst, data, length)) >= 0) { + /* + Acknowledge only if read was successful + */ + diva_data_q_ack_segment4read(q); + } + } + + + DBG_TRC(("A(%d) E(%08x) read=%d", a->adapter_nr, e, ret)); + + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "read"); + + return (ret); +} + + +int diva_um_idi_write(void *entity, + void *os_handle, + const void *src, + int length, divas_um_idi_copy_from_user_fn_t cp_fn) +{ + divas_um_idi_entity_t *e; + diva_um_idi_adapter_t *a; + diva_um_idi_req_hdr_t *req; + void *data; + int ret = 0; + diva_os_spin_lock_magic_t old_irql; + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "write"); + + e = (divas_um_idi_entity_t *) entity; + if (!e || (!(a = e->adapter)) || + (e->status & DIVA_UM_IDI_REMOVE_PENDING) || + (e->status & DIVA_UM_IDI_REMOVED) || + (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) { + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); + DBG_ERR(("E(%08x) write failed - adapter removed", e)) + return (-1); + } + + DBG_TRC(("A(%d) E(%08x) write(%d)", a->adapter_nr, e, length)); + + if ((length < sizeof(*req)) || (length > sizeof(e->buffer))) { + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); + return (-2); + } + + if (e->status & DIVA_UM_IDI_RC_PENDING) { + DBG_ERR(("A: A(%d) E(%08x) rc pending", a->adapter_nr, e)); + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); + return (-1); /* should wait for RC code first */ + } + + /* + Copy function does access only locked verified memory, + also it can be called with spin lock held + */ + if ((ret = (*cp_fn) (os_handle, e->buffer, src, length)) < 0) { + DBG_TRC(("A: A(%d) E(%08x) write error=%d", a->adapter_nr, + e, ret)); + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); + return (ret); + } + + req = (diva_um_idi_req_hdr_t *)&e->buffer[0]; + + switch (req->type) { + case DIVA_UM_IDI_GET_FEATURES:{ + DBG_LOG(("A(%d) get_features", a->adapter_nr)); + if (!(data = + diva_data_q_get_segment4write(&e->data))) { + DBG_ERR(("A(%d) get_features, no free buffer", + a->adapter_nr)); + diva_os_leave_spin_lock(&adapter_lock, + &old_irql, + "write"); + return (0); + } + diva_user_mode_idi_adapter_features(a, &(((diva_um_idi_ind_hdr_t + *) data)->hdr.features)); + ((diva_um_idi_ind_hdr_t *) data)->type = + DIVA_UM_IDI_IND_FEATURES; + ((diva_um_idi_ind_hdr_t *) data)->data_length = 0; + diva_data_q_ack_segment4write(&e->data, + sizeof(diva_um_idi_ind_hdr_t)); + + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); + + diva_os_wakeup_read(e->os_context); + } + break; + + case DIVA_UM_IDI_REQ: + case DIVA_UM_IDI_REQ_MAN: + case DIVA_UM_IDI_REQ_SIG: + case DIVA_UM_IDI_REQ_NET: + DBG_TRC(("A(%d) REQ(%02d)-(%02d)-(%08x)", a->adapter_nr, + req->Req, req->ReqCh, + req->type & DIVA_UM_IDI_REQ_TYPE_MASK)); + switch (process_idi_request(e, req)) { + case -1: + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); + return (-1); + case -2: + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); + diva_os_wakeup_read(e->os_context); + break; + default: + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); + break; + } + break; + + default: + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write"); + return (-1); + } + + DBG_TRC(("A(%d) E(%08x) write=%d", a->adapter_nr, e, ret)); + + return (ret); +} + +/* -------------------------------------------------------------------------- + CALLBACK FROM XDI + -------------------------------------------------------------------------- */ +static void diva_um_idi_xdi_callback(ENTITY *entity) +{ + divas_um_idi_entity_t *e = DIVAS_CONTAINING_RECORD(entity, + divas_um_idi_entity_t, + e); + diva_os_spin_lock_magic_t old_irql; + int call_wakeup = 0; + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "xdi_callback"); + + if (e->e.complete == 255) { + if (!(e->status & DIVA_UM_IDI_REMOVE_PENDING)) { + diva_um_idi_stop_wdog(e); + } + if ((call_wakeup = process_idi_rc(e, e->e.Rc))) { + if (e->rc_count) { + e->rc_count--; + } + } + e->e.Rc = 0; + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "xdi_callback"); + + if (call_wakeup) { + diva_os_wakeup_read(e->os_context); + diva_os_wakeup_close(e->os_context); + } + } else { + if (e->status & DIVA_UM_IDI_REMOVE_PENDING) { + e->e.RNum = 0; + e->e.RNR = 2; + } else { + call_wakeup = process_idi_ind(e, e->e.Ind); + } + e->e.Ind = 0; + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "xdi_callback"); + if (call_wakeup) { + diva_os_wakeup_read(e->os_context); + } + } +} + +static int process_idi_request(divas_um_idi_entity_t *e, + const diva_um_idi_req_hdr_t *req) +{ + int assign = 0; + byte Req = (byte) req->Req; + dword type = req->type & DIVA_UM_IDI_REQ_TYPE_MASK; + + if (!e->e.Id || !e->e.callback) { /* not assigned */ + if (Req != ASSIGN) { + DBG_ERR(("A: A(%d) E(%08x) not assigned", + e->adapter->adapter_nr, e)); + return (-1); /* NOT ASSIGNED */ + } else { + switch (type) { + case DIVA_UM_IDI_REQ_TYPE_MAN: + e->e.Id = MAN_ID; + DBG_TRC(("A(%d) E(%08x) assign MAN", + e->adapter->adapter_nr, e)); + break; + + case DIVA_UM_IDI_REQ_TYPE_SIG: + e->e.Id = DSIG_ID; + DBG_TRC(("A(%d) E(%08x) assign SIG", + e->adapter->adapter_nr, e)); + break; + + case DIVA_UM_IDI_REQ_TYPE_NET: + e->e.Id = NL_ID; + DBG_TRC(("A(%d) E(%08x) assign NET", + e->adapter->adapter_nr, e)); + break; + + default: + DBG_ERR(("A: A(%d) E(%08x) unknown type=%08x", + e->adapter->adapter_nr, e, + type)); + return (-1); + } + } + e->e.XNum = 1; + e->e.RNum = 1; + e->e.callback = diva_um_idi_xdi_callback; + e->e.X = &e->XData; + e->e.R = &e->RData; + assign = 1; + } + e->status |= DIVA_UM_IDI_RC_PENDING; + e->e.Req = Req; + e->e.ReqCh = (byte) req->ReqCh; + e->e.X->PLength = (word) req->data_length; + e->e.X->P = (byte *)&req[1]; /* Our buffer is safe */ + + DBG_TRC(("A(%d) E(%08x) request(%02x-%02x-%02x (%d))", + e->adapter->adapter_nr, e, e->e.Id, e->e.Req, + e->e.ReqCh, e->e.X->PLength)); + + e->rc_count++; + + if (e->adapter && e->adapter->d.request) { + diva_um_idi_start_wdog(e); + (*(e->adapter->d.request)) (&e->e); + } + + if (assign) { + if (e->e.Rc == OUT_OF_RESOURCES) { + /* + XDI has no entities more, call was not forwarded to the card, + no callback will be scheduled + */ + DBG_ERR(("A: A(%d) E(%08x) XDI out of entities", + e->adapter->adapter_nr, e)); + + e->e.Id = 0; + e->e.ReqCh = 0; + e->e.RcCh = 0; + e->e.Ind = 0; + e->e.IndCh = 0; + e->e.XNum = 0; + e->e.RNum = 0; + e->e.callback = NULL; + e->e.X = NULL; + e->e.R = NULL; + write_return_code(e, ASSIGN_RC | OUT_OF_RESOURCES); + return (-2); + } else { + e->status |= DIVA_UM_IDI_ASSIGN_PENDING; + } + } + + return (0); +} + +static int process_idi_rc(divas_um_idi_entity_t *e, byte rc) +{ + DBG_TRC(("A(%d) E(%08x) rc(%02x-%02x-%02x)", + e->adapter->adapter_nr, e, e->e.Id, rc, e->e.RcCh)); + + if (e->status & DIVA_UM_IDI_ASSIGN_PENDING) { + e->status &= ~DIVA_UM_IDI_ASSIGN_PENDING; + if (rc != ASSIGN_OK) { + DBG_ERR(("A: A(%d) E(%08x) ASSIGN failed", + e->adapter->adapter_nr, e)); + e->e.callback = NULL; + e->e.Id = 0; + e->e.Req = 0; + e->e.ReqCh = 0; + e->e.Rc = 0; + e->e.RcCh = 0; + e->e.Ind = 0; + e->e.IndCh = 0; + e->e.X = NULL; + e->e.R = NULL; + e->e.XNum = 0; + e->e.RNum = 0; + } + } + if ((e->e.Req == REMOVE) && e->e.Id && (rc == 0xff)) { + DBG_ERR(("A: A(%d) E(%08x) discard OK in REMOVE", + e->adapter->adapter_nr, e)); + return (0); /* let us do it in the driver */ + } + if ((e->e.Req == REMOVE) && (!e->e.Id)) { /* REMOVE COMPLETE */ + e->e.callback = NULL; + e->e.Id = 0; + e->e.Req = 0; + e->e.ReqCh = 0; + e->e.Rc = 0; + e->e.RcCh = 0; + e->e.Ind = 0; + e->e.IndCh = 0; + e->e.X = NULL; + e->e.R = NULL; + e->e.XNum = 0; + e->e.RNum = 0; + e->rc_count = 0; + } + if ((e->e.Req == REMOVE) && (rc != 0xff)) { /* REMOVE FAILED */ + DBG_ERR(("A: A(%d) E(%08x) REMOVE FAILED", + e->adapter->adapter_nr, e)); + } + write_return_code(e, rc); + + return (1); +} + +static int process_idi_ind(divas_um_idi_entity_t *e, byte ind) +{ + int do_wakeup = 0; + + if (e->e.complete != 0x02) { + diva_um_idi_ind_hdr_t *pind = + (diva_um_idi_ind_hdr_t *) + diva_data_q_get_segment4write(&e->data); + if (pind) { + e->e.RNum = 1; + e->e.R->P = (byte *)&pind[1]; + e->e.R->PLength = + (word) (diva_data_q_get_max_length(&e->data) - + sizeof(*pind)); + DBG_TRC(("A(%d) E(%08x) ind_1(%02x-%02x-%02x)-[%d-%d]", + e->adapter->adapter_nr, e, e->e.Id, ind, + e->e.IndCh, e->e.RLength, + e->e.R->PLength)); + + } else { + DBG_TRC(("A(%d) E(%08x) ind(%02x-%02x-%02x)-RNR", + e->adapter->adapter_nr, e, e->e.Id, ind, + e->e.IndCh)); + e->e.RNum = 0; + e->e.RNR = 1; + do_wakeup = 1; + } + } else { + diva_um_idi_ind_hdr_t *pind = + (diva_um_idi_ind_hdr_t *) (e->e.R->P); + + DBG_TRC(("A(%d) E(%08x) ind(%02x-%02x-%02x)-[%d]", + e->adapter->adapter_nr, e, e->e.Id, ind, + e->e.IndCh, e->e.R->PLength)); + + pind--; + pind->type = DIVA_UM_IDI_IND; + pind->hdr.ind.Ind = ind; + pind->hdr.ind.IndCh = e->e.IndCh; + pind->data_length = e->e.R->PLength; + diva_data_q_ack_segment4write(&e->data, + (int) (sizeof(*pind) + + e->e.R->PLength)); + do_wakeup = 1; + } + + if ((e->status & DIVA_UM_IDI_RC_PENDING) && !e->rc.count) { + do_wakeup = 0; + } + + return (do_wakeup); +} + +/* -------------------------------------------------------------------------- + Write return code to the return code queue of entity + -------------------------------------------------------------------------- */ +static int write_return_code(divas_um_idi_entity_t *e, byte rc) +{ + diva_um_idi_ind_hdr_t *prc; + + if (!(prc = + (diva_um_idi_ind_hdr_t *) diva_data_q_get_segment4write(&e->rc))) + { + DBG_ERR(("A: A(%d) E(%08x) rc(%02x) lost", + e->adapter->adapter_nr, e, rc)); + e->status &= ~DIVA_UM_IDI_RC_PENDING; + return (-1); + } + + prc->type = DIVA_UM_IDI_IND_RC; + prc->hdr.rc.Rc = rc; + prc->hdr.rc.RcCh = e->e.RcCh; + prc->data_length = 0; + diva_data_q_ack_segment4write(&e->rc, sizeof(*prc)); + + return (0); +} + +/* -------------------------------------------------------------------------- + Return amount of entries that can be bead from this entity or + -1 if adapter was removed + -------------------------------------------------------------------------- */ +int diva_user_mode_idi_ind_ready(void *entity, void *os_handle) +{ + divas_um_idi_entity_t *e; + diva_um_idi_adapter_t *a; + diva_os_spin_lock_magic_t old_irql; + int ret; + + if (!entity) + return (-1); + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "ind_ready"); + e = (divas_um_idi_entity_t *) entity; + a = e->adapter; + + if ((!a) || (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) { + /* + Adapter was unloaded + */ + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready"); + return (-1); /* adapter was removed */ + } + if (e->status & DIVA_UM_IDI_REMOVED) { + /* + entity was removed as result of adapter removal + user should assign this entity again + */ + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready"); + return (-1); + } + + ret = e->rc.count + e->data.count; + + if ((e->status & DIVA_UM_IDI_RC_PENDING) && !e->rc.count) { + ret = 0; + } + + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready"); + + return (ret); +} + +void *diva_um_id_get_os_context(void *entity) +{ + return (((divas_um_idi_entity_t *) entity)->os_context); +} + +int divas_um_idi_entity_assigned(void *entity) +{ + divas_um_idi_entity_t *e; + diva_um_idi_adapter_t *a; + int ret; + diva_os_spin_lock_magic_t old_irql; + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "assigned?"); + + + e = (divas_um_idi_entity_t *) entity; + if (!e || (!(a = e->adapter)) || + (e->status & DIVA_UM_IDI_REMOVED) || + (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) { + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "assigned?"); + return (0); + } + + e->status |= DIVA_UM_IDI_REMOVE_PENDING; + + ret = (e->e.Id || e->rc_count + || (e->status & DIVA_UM_IDI_ASSIGN_PENDING)); + + DBG_TRC(("Id:%02x, rc_count:%d, status:%08x", e->e.Id, e->rc_count, + e->status)) + + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "assigned?"); + + return (ret); +} + +int divas_um_idi_entity_start_remove(void *entity) +{ + divas_um_idi_entity_t *e; + diva_um_idi_adapter_t *a; + diva_os_spin_lock_magic_t old_irql; + + diva_os_enter_spin_lock(&adapter_lock, &old_irql, "start_remove"); + + e = (divas_um_idi_entity_t *) entity; + if (!e || (!(a = e->adapter)) || + (e->status & DIVA_UM_IDI_REMOVED) || + (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) { + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove"); + return (0); + } + + if (e->rc_count) { + /* + Entity BUSY + */ + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove"); + return (1); + } + + if (!e->e.Id) { + /* + Remove request was already pending, and arrived now + */ + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove"); + return (0); /* REMOVE was pending */ + } + + /* + Now send remove request + */ + e->e.Req = REMOVE; + e->e.ReqCh = 0; + + e->rc_count++; + + DBG_TRC(("A(%d) E(%08x) request(%02x-%02x-%02x (%d))", + e->adapter->adapter_nr, e, e->e.Id, e->e.Req, + e->e.ReqCh, e->e.X->PLength)); + + if (a->d.request) + (*(a->d.request)) (&e->e); + + diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove"); + + return (0); +} diff --git a/drivers/isdn/hardware/eicon/um_idi.h b/drivers/isdn/hardware/eicon/um_idi.h new file mode 100644 index 000000000..9aedd9e35 --- /dev/null +++ b/drivers/isdn/hardware/eicon/um_idi.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: um_idi.h,v 1.6 2004/03/21 17:26:01 armin Exp $ */ + +#ifndef __DIVA_USER_MODE_IDI_CORE_H__ +#define __DIVA_USER_MODE_IDI_CORE_H__ + + +/* + interface between UM IDI core and OS dependent part +*/ +int diva_user_mode_idi_init(void); +void diva_user_mode_idi_finit(void); +void *divas_um_idi_create_entity(dword adapter_nr, void *file); +int divas_um_idi_delete_entity(int adapter_nr, void *entity); + +typedef int (*divas_um_idi_copy_to_user_fn_t) (void *os_handle, + void *dst, + const void *src, + int length); +typedef int (*divas_um_idi_copy_from_user_fn_t) (void *os_handle, + void *dst, + const void *src, + int length); + +int diva_um_idi_read(void *entity, + void *os_handle, + void *dst, + int max_length, divas_um_idi_copy_to_user_fn_t cp_fn); + +int diva_um_idi_write(void *entity, + void *os_handle, + const void *src, + int length, divas_um_idi_copy_from_user_fn_t cp_fn); + +int diva_user_mode_idi_ind_ready(void *entity, void *os_handle); +void *diva_um_id_get_os_context(void *entity); +int diva_os_get_context_size(void); +int divas_um_idi_entity_assigned(void *entity); +int divas_um_idi_entity_start_remove(void *entity); + +void diva_um_idi_start_wdog(void *entity); +void diva_um_idi_stop_wdog(void *entity); + +#endif diff --git a/drivers/isdn/hardware/eicon/um_xdi.h b/drivers/isdn/hardware/eicon/um_xdi.h new file mode 100644 index 000000000..1f37aa4ef --- /dev/null +++ b/drivers/isdn/hardware/eicon/um_xdi.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: um_xdi.h,v 1.1.2.2 2002/10/02 14:38:38 armin Exp $ */ + +#ifndef __DIVA_USER_MODE_XDI_H__ +#define __DIVA_USER_MODE_XDI_H__ + +/* + Contains declaratiom of structures shared between application + and user mode idi driver +*/ + +typedef struct _diva_um_idi_adapter_features { + dword type; + dword features; + dword channels; + dword serial_number; + char name[128]; +} diva_um_idi_adapter_features_t; + +#define DIVA_UM_IDI_REQ_MASK 0x0000FFFF +#define DIVA_UM_IDI_REQ_TYPE_MASK (~(DIVA_UM_IDI_REQ_MASK)) +#define DIVA_UM_IDI_GET_FEATURES 1 /* trigger features indication */ +#define DIVA_UM_IDI_REQ 2 +#define DIVA_UM_IDI_REQ_TYPE_MAN 0x10000000 +#define DIVA_UM_IDI_REQ_TYPE_SIG 0x20000000 +#define DIVA_UM_IDI_REQ_TYPE_NET 0x30000000 +#define DIVA_UM_IDI_REQ_MAN (DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_MAN) +#define DIVA_UM_IDI_REQ_SIG (DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_SIG) +#define DIVA_UM_IDI_REQ_NET (DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_NET) +/* + data_length bytes will follow this structure +*/ +typedef struct _diva_um_idi_req_hdr { + dword type; + dword Req; + dword ReqCh; + dword data_length; +} diva_um_idi_req_hdr_t; + +typedef struct _diva_um_idi_ind_parameters { + dword Ind; + dword IndCh; +} diva_um_idi_ind_parameters_t; + +typedef struct _diva_um_idi_rc_parameters { + dword Rc; + dword RcCh; +} diva_um_idi_rc_parameters_t; + +typedef union _diva_um_idi_ind { + diva_um_idi_adapter_features_t features; + diva_um_idi_ind_parameters_t ind; + diva_um_idi_rc_parameters_t rc; +} diva_um_idi_ind_t; + +#define DIVA_UM_IDI_IND_FEATURES 1 /* features indication */ +#define DIVA_UM_IDI_IND 2 +#define DIVA_UM_IDI_IND_RC 3 +/* + data_length bytes of data follow + this structure +*/ +typedef struct _diva_um_idi_ind_hdr { + dword type; + diva_um_idi_ind_t hdr; + dword data_length; +} diva_um_idi_ind_hdr_t; + +#endif diff --git a/drivers/isdn/hardware/eicon/xdi_adapter.h b/drivers/isdn/hardware/eicon/xdi_adapter.h new file mode 100644 index 000000000..b036e217c --- /dev/null +++ b/drivers/isdn/hardware/eicon/xdi_adapter.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: xdi_adapter.h,v 1.7 2004/03/21 17:26:01 armin Exp $ */ + +#ifndef __DIVA_OS_XDI_ADAPTER_H__ +#define __DIVA_OS_XDI_ADAPTER_H__ + +#define DIVAS_XDI_ADAPTER_BUS_PCI 0 +#define DIVAS_XDI_ADAPTER_BUS_ISA 1 + +typedef struct _divas_pci_card_resources { + byte bus; + byte func; + void *hdev; + + dword bar[8]; /* contains context of appropriate BAR Register */ + void __iomem *addr[8]; /* same bar, but mapped into memory */ + dword length[8]; /* bar length */ + int mem_type_id[MAX_MEM_TYPE]; + unsigned int qoffset; + byte irq; +} divas_pci_card_resources_t; + +typedef union _divas_card_resources { + divas_pci_card_resources_t pci; +} divas_card_resources_t; + +struct _diva_os_xdi_adapter; +typedef int (*diva_init_card_proc_t)(struct _diva_os_xdi_adapter *a); +typedef int (*diva_cmd_card_proc_t)(struct _diva_os_xdi_adapter *a, + diva_xdi_um_cfg_cmd_t *data, + int length); +typedef void (*diva_xdi_clear_interrupts_proc_t)(struct + _diva_os_xdi_adapter *); + +#define DIVA_XDI_MBOX_BUSY 1 +#define DIVA_XDI_MBOX_WAIT_XLOG 2 + +typedef struct _xdi_mbox_t { + dword status; + diva_xdi_um_cfg_cmd_data_t cmd_data; + dword data_length; + void *data; +} xdi_mbox_t; + +typedef struct _diva_os_idi_adapter_interface { + diva_init_card_proc_t cleanup_adapter_proc; + diva_cmd_card_proc_t cmd_proc; +} diva_os_idi_adapter_interface_t; + +typedef struct _diva_os_xdi_adapter { + struct list_head link; + int CardIndex; + int CardOrdinal; + int controller; /* number of this controller */ + int Bus; /* PCI, ISA, ... */ + divas_card_resources_t resources; + char port_name[24]; + ISDN_ADAPTER xdi_adapter; + xdi_mbox_t xdi_mbox; + diva_os_idi_adapter_interface_t interface; + struct _diva_os_xdi_adapter *slave_adapters[3]; + void *slave_list; + void *proc_adapter_dir; /* adapterX proc entry */ + void *proc_info; /* info proc entry */ + void *proc_grp_opt; /* group_optimization */ + void *proc_d_l1_down; /* dynamic_l1_down */ + volatile diva_xdi_clear_interrupts_proc_t clear_interrupts_proc; + dword dsp_mask; +} diva_os_xdi_adapter_t; + +#endif diff --git a/drivers/isdn/hardware/eicon/xdi_msg.h b/drivers/isdn/hardware/eicon/xdi_msg.h new file mode 100644 index 000000000..0646079bf --- /dev/null +++ b/drivers/isdn/hardware/eicon/xdi_msg.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* $Id: xdi_msg.h,v 1.1.2.2 2001/02/16 08:40:36 armin Exp $ */ + +#ifndef __DIVA_XDI_UM_CFG_MESSAGE_H__ +#define __DIVA_XDI_UM_CFG_MESSAGE_H__ + +/* + Definition of messages used to communicate between + XDI device driver and user mode configuration utility +*/ + +/* + As acknowledge one DWORD - card ordinal will be read from the card +*/ +#define DIVA_XDI_UM_CMD_GET_CARD_ORDINAL 0 + +/* + no acknowledge will be generated, memory block will be written in the + memory at given offset +*/ +#define DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK 1 + +/* + no acknowledge will be genatated, FPGA will be programmed +*/ +#define DIVA_XDI_UM_CMD_WRITE_FPGA 2 + +/* + As acknowledge block of SDRAM will be read in the user buffer +*/ +#define DIVA_XDI_UM_CMD_READ_SDRAM 3 + +/* + As acknowledge dword with serial number will be read in the user buffer +*/ +#define DIVA_XDI_UM_CMD_GET_SERIAL_NR 4 + +/* + As acknowledge struct consisting from 9 dwords with PCI info. + dword[0...7] = 8 PCI BARS + dword[9] = IRQ +*/ +#define DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG 5 + +/* + Reset of the board + activation of primary + boot loader +*/ +#define DIVA_XDI_UM_CMD_RESET_ADAPTER 6 + +/* + Called after code download to start adapter + at specified address + Start does set new set of features due to fact that we not know + if protocol features have changed +*/ +#define DIVA_XDI_UM_CMD_START_ADAPTER 7 + +/* + Stop adapter, called if user + wishes to stop adapter without unload + of the driver, to reload adapter with + different protocol +*/ +#define DIVA_XDI_UM_CMD_STOP_ADAPTER 8 + +/* + Get state of current adapter + Acknowledge is one dword with following values: + 0 - adapter ready for download + 1 - adapter running + 2 - adapter dead + 3 - out of service, driver should be restarted or hardware problem +*/ +#define DIVA_XDI_UM_CMD_GET_CARD_STATE 9 + +/* + Reads XLOG entry from the card +*/ +#define DIVA_XDI_UM_CMD_READ_XLOG_ENTRY 10 + +/* + Set untranslated protocol code features +*/ +#define DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES 11 + +typedef struct _diva_xdi_um_cfg_cmd_data_set_features { + dword features; +} diva_xdi_um_cfg_cmd_data_set_features_t; + +typedef struct _diva_xdi_um_cfg_cmd_data_start { + dword offset; + dword features; +} diva_xdi_um_cfg_cmd_data_start_t; + +typedef struct _diva_xdi_um_cfg_cmd_data_write_sdram { + dword ram_number; + dword offset; + dword length; +} diva_xdi_um_cfg_cmd_data_write_sdram_t; + +typedef struct _diva_xdi_um_cfg_cmd_data_write_fpga { + dword fpga_number; + dword image_length; +} diva_xdi_um_cfg_cmd_data_write_fpga_t; + +typedef struct _diva_xdi_um_cfg_cmd_data_read_sdram { + dword ram_number; + dword offset; + dword length; +} diva_xdi_um_cfg_cmd_data_read_sdram_t; + +typedef union _diva_xdi_um_cfg_cmd_data { + diva_xdi_um_cfg_cmd_data_write_sdram_t write_sdram; + diva_xdi_um_cfg_cmd_data_write_fpga_t write_fpga; + diva_xdi_um_cfg_cmd_data_read_sdram_t read_sdram; + diva_xdi_um_cfg_cmd_data_start_t start; + diva_xdi_um_cfg_cmd_data_set_features_t features; +} diva_xdi_um_cfg_cmd_data_t; + +typedef struct _diva_xdi_um_cfg_cmd { + dword adapter; /* Adapter number 1...N */ + dword command; + diva_xdi_um_cfg_cmd_data_t command_data; + dword data_length; /* Plain binary data will follow */ +} diva_xdi_um_cfg_cmd_t; + +#endif diff --git a/drivers/isdn/hardware/eicon/xdi_vers.h b/drivers/isdn/hardware/eicon/xdi_vers.h new file mode 100644 index 000000000..b3479e59c --- /dev/null +++ b/drivers/isdn/hardware/eicon/xdi_vers.h @@ -0,0 +1,26 @@ + +/* + * + Copyright (c) Eicon Networks, 2002. + * + This source file is supplied for the use with + Eicon Networks range of DIVA Server Adapters. + * + Eicon File Revision : 2.1 + * + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + * + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + * + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +static char diva_xdi_common_code_build[] = "102-52"; diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig new file mode 100644 index 000000000..fda912b08 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/Kconfig @@ -0,0 +1,94 @@ +# +# Hardware for mISDN +# +comment "mISDN hardware drivers" + +config MISDN_HFCPCI + tristate "Support for HFC PCI cards" + depends on MISDN + depends on PCI + help + Enable support for cards with Cologne Chip AG's + HFC PCI chip. + +config MISDN_HFCMULTI + tristate "Support for HFC multiport cards (HFC-4S/8S/E1)" + depends on PCI || CPM1 + depends on MISDN + help + Enable support for cards with Cologne Chip AG's HFC multiport + chip. There are three types of chips that are quite similar, + but the interface is different: + * HFC-4S (4 S/T interfaces on one chip) + * HFC-8S (8 S/T interfaces on one chip) + * HFC-E1 (E1 interface for 2Mbit ISDN) + +config MISDN_HFCMULTI_8xx + bool "Support for XHFC embedded board in HFC multiport driver" + depends on MISDN + depends on MISDN_HFCMULTI + depends on CPM1 + default CPM1 + help + Enable support for the XHFC embedded solution from Speech Design. + +config MISDN_HFCUSB + tristate "Support for HFC-S USB based TAs" + depends on USB + help + Enable support for USB ISDN TAs with Cologne Chip AG's + HFC-S USB ISDN Controller + +config MISDN_AVMFRITZ + tristate "Support for AVM FRITZ!CARD PCI" + depends on MISDN + depends on PCI + select MISDN_IPAC + help + Enable support for AVMs FRITZ!CARD PCI cards + +config MISDN_SPEEDFAX + tristate "Support for Sedlbauer Speedfax+" + depends on MISDN + depends on PCI + select MISDN_IPAC + select MISDN_ISAR + help + Enable support for Sedlbauer Speedfax+. + +config MISDN_INFINEON + tristate "Support for cards with Infineon chipset" + depends on MISDN + depends on PCI + select MISDN_IPAC + help + Enable support for cards with ISAC + HSCX, IPAC or IPAC-SX + chip from Infineon (former manufacturer Siemens). + +config MISDN_W6692 + tristate "Support for cards with Winbond 6692" + depends on MISDN + depends on PCI + help + Enable support for Winbond 6692 PCI chip based cards. + +config MISDN_NETJET + tristate "Support for NETJet cards" + depends on MISDN + depends on PCI + depends on TTY + select MISDN_IPAC + select ISDN_HDLC + select ISDN_I4L + help + Enable support for Traverse Technologies NETJet PCI cards. + + +config MISDN_IPAC + tristate + depends on MISDN + +config MISDN_ISAR + tristate + depends on MISDN + diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile new file mode 100644 index 000000000..422f9fd8a --- /dev/null +++ b/drivers/isdn/hardware/mISDN/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the modular ISDN hardware drivers +# +# + +obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o +obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o +obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o +obj-$(CONFIG_MISDN_AVMFRITZ) += avmfritz.o +obj-$(CONFIG_MISDN_SPEEDFAX) += speedfax.o +obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o +obj-$(CONFIG_MISDN_W6692) += w6692.o +obj-$(CONFIG_MISDN_NETJET) += netjet.o +# chip modules +obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o +obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c new file mode 100644 index 000000000..8eb28a838 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/avmfritz.c @@ -0,0 +1,1177 @@ +/* + * avm_fritz.c low level stuff for AVM FRITZ!CARD PCI ISDN cards + * Thanks to AVM, Berlin for informations + * + * Author Karsten Keil <keil@isdn4linux.de> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> +#include <asm/unaligned.h> +#include "ipac.h" + + +#define AVMFRITZ_REV "2.3" + +static int AVM_cnt; +static int debug; + +enum { + AVM_FRITZ_PCI, + AVM_FRITZ_PCIV2, +}; + +#define HDLC_FIFO 0x0 +#define HDLC_STATUS 0x4 +#define CHIP_WINDOW 0x10 + +#define CHIP_INDEX 0x4 +#define AVM_HDLC_1 0x00 +#define AVM_HDLC_2 0x01 +#define AVM_ISAC_FIFO 0x02 +#define AVM_ISAC_REG_LOW 0x04 +#define AVM_ISAC_REG_HIGH 0x06 + +#define AVM_STATUS0_IRQ_ISAC 0x01 +#define AVM_STATUS0_IRQ_HDLC 0x02 +#define AVM_STATUS0_IRQ_TIMER 0x04 +#define AVM_STATUS0_IRQ_MASK 0x07 + +#define AVM_STATUS0_RESET 0x01 +#define AVM_STATUS0_DIS_TIMER 0x02 +#define AVM_STATUS0_RES_TIMER 0x04 +#define AVM_STATUS0_ENA_IRQ 0x08 +#define AVM_STATUS0_TESTBIT 0x10 + +#define AVM_STATUS1_INT_SEL 0x0f +#define AVM_STATUS1_ENA_IOM 0x80 + +#define HDLC_MODE_ITF_FLG 0x01 +#define HDLC_MODE_TRANS 0x02 +#define HDLC_MODE_CCR_7 0x04 +#define HDLC_MODE_CCR_16 0x08 +#define HDLC_FIFO_SIZE_128 0x20 +#define HDLC_MODE_TESTLOOP 0x80 + +#define HDLC_INT_XPR 0x80 +#define HDLC_INT_XDU 0x40 +#define HDLC_INT_RPR 0x20 +#define HDLC_INT_MASK 0xE0 + +#define HDLC_STAT_RME 0x01 +#define HDLC_STAT_RDO 0x10 +#define HDLC_STAT_CRCVFRRAB 0x0E +#define HDLC_STAT_CRCVFR 0x06 +#define HDLC_STAT_RML_MASK_V1 0x3f00 +#define HDLC_STAT_RML_MASK_V2 0x7f00 + +#define HDLC_CMD_XRS 0x80 +#define HDLC_CMD_XME 0x01 +#define HDLC_CMD_RRS 0x20 +#define HDLC_CMD_XML_MASK 0x3f00 + +#define HDLC_FIFO_SIZE_V1 32 +#define HDLC_FIFO_SIZE_V2 128 + +/* Fritz PCI v2.0 */ + +#define AVM_HDLC_FIFO_1 0x10 +#define AVM_HDLC_FIFO_2 0x18 + +#define AVM_HDLC_STATUS_1 0x14 +#define AVM_HDLC_STATUS_2 0x1c + +#define AVM_ISACX_INDEX 0x04 +#define AVM_ISACX_DATA 0x08 + +/* data struct */ +#define LOG_SIZE 63 + +struct hdlc_stat_reg { +#ifdef __BIG_ENDIAN + u8 fill; + u8 mode; + u8 xml; + u8 cmd; +#else + u8 cmd; + u8 xml; + u8 mode; + u8 fill; +#endif +} __attribute__((packed)); + +struct hdlc_hw { + union { + u32 ctrl; + struct hdlc_stat_reg sr; + } ctrl; + u32 stat; +}; + +struct fritzcard { + struct list_head list; + struct pci_dev *pdev; + char name[MISDN_MAX_IDLEN]; + u8 type; + u8 ctrlreg; + u16 irq; + u32 irqcnt; + u32 addr; + spinlock_t lock; /* hw lock */ + struct isac_hw isac; + struct hdlc_hw hdlc[2]; + struct bchannel bch[2]; + char log[LOG_SIZE + 1]; +}; + +static LIST_HEAD(Cards); +static DEFINE_RWLOCK(card_lock); /* protect Cards */ + +static void +_set_debug(struct fritzcard *card) +{ + card->isac.dch.debug = debug; + card->bch[0].debug = debug; + card->bch[1].debug = debug; +} + +static int +set_debug(const char *val, const struct kernel_param *kp) +{ + int ret; + struct fritzcard *card; + + ret = param_set_uint(val, kp); + if (!ret) { + read_lock(&card_lock); + list_for_each_entry(card, &Cards, list) + _set_debug(card); + read_unlock(&card_lock); + } + return ret; +} + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(AVMFRITZ_REV); +module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "avmfritz debug mask"); + +/* Interface functions */ + +static u8 +ReadISAC_V1(void *p, u8 offset) +{ + struct fritzcard *fc = p; + u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; + + outb(idx, fc->addr + CHIP_INDEX); + return inb(fc->addr + CHIP_WINDOW + (offset & 0xf)); +} + +static void +WriteISAC_V1(void *p, u8 offset, u8 value) +{ + struct fritzcard *fc = p; + u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; + + outb(idx, fc->addr + CHIP_INDEX); + outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf)); +} + +static void +ReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size) +{ + struct fritzcard *fc = p; + + outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); + insb(fc->addr + CHIP_WINDOW, data, size); +} + +static void +WriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size) +{ + struct fritzcard *fc = p; + + outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); + outsb(fc->addr + CHIP_WINDOW, data, size); +} + +static u8 +ReadISAC_V2(void *p, u8 offset) +{ + struct fritzcard *fc = p; + + outl(offset, fc->addr + AVM_ISACX_INDEX); + return 0xff & inl(fc->addr + AVM_ISACX_DATA); +} + +static void +WriteISAC_V2(void *p, u8 offset, u8 value) +{ + struct fritzcard *fc = p; + + outl(offset, fc->addr + AVM_ISACX_INDEX); + outl(value, fc->addr + AVM_ISACX_DATA); +} + +static void +ReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size) +{ + struct fritzcard *fc = p; + int i; + + outl(off, fc->addr + AVM_ISACX_INDEX); + for (i = 0; i < size; i++) + data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA); +} + +static void +WriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size) +{ + struct fritzcard *fc = p; + int i; + + outl(off, fc->addr + AVM_ISACX_INDEX); + for (i = 0; i < size; i++) + outl(data[i], fc->addr + AVM_ISACX_DATA); +} + +static struct bchannel * +Sel_BCS(struct fritzcard *fc, u32 channel) +{ + if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) && + (fc->bch[0].nr & channel)) + return &fc->bch[0]; + else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) && + (fc->bch[1].nr & channel)) + return &fc->bch[1]; + else + return NULL; +} + +static inline void +__write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { + u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1; + + outl(idx, fc->addr + CHIP_INDEX); + outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS); +} + +static inline void +__write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { + outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 : + AVM_HDLC_STATUS_1)); +} + +static void +write_ctrl(struct bchannel *bch, int which) { + struct fritzcard *fc = bch->hw; + struct hdlc_hw *hdlc; + + hdlc = &fc->hdlc[(bch->nr - 1) & 1]; + pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr, + which, hdlc->ctrl.ctrl); + switch (fc->type) { + case AVM_FRITZ_PCIV2: + __write_ctrl_pciv2(fc, hdlc, bch->nr); + break; + case AVM_FRITZ_PCI: + __write_ctrl_pci(fc, hdlc, bch->nr); + break; + } +} + + +static inline u32 +__read_status_pci(u_long addr, u32 channel) +{ + outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); + return inl(addr + CHIP_WINDOW + HDLC_STATUS); +} + +static inline u32 +__read_status_pciv2(u_long addr, u32 channel) +{ + return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 : + AVM_HDLC_STATUS_1)); +} + + +static u32 +read_status(struct fritzcard *fc, u32 channel) +{ + switch (fc->type) { + case AVM_FRITZ_PCIV2: + return __read_status_pciv2(fc->addr, channel); + case AVM_FRITZ_PCI: + return __read_status_pci(fc->addr, channel); + } + /* dummy */ + return 0; +} + +static void +enable_hwirq(struct fritzcard *fc) +{ + fc->ctrlreg |= AVM_STATUS0_ENA_IRQ; + outb(fc->ctrlreg, fc->addr + 2); +} + +static void +disable_hwirq(struct fritzcard *fc) +{ + fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ; + outb(fc->ctrlreg, fc->addr + 2); +} + +static int +modehdlc(struct bchannel *bch, int protocol) +{ + struct fritzcard *fc = bch->hw; + struct hdlc_hw *hdlc; + u8 mode; + + hdlc = &fc->hdlc[(bch->nr - 1) & 1]; + pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name, + '@' + bch->nr, bch->state, protocol, bch->nr); + hdlc->ctrl.ctrl = 0; + mode = (fc->type == AVM_FRITZ_PCIV2) ? HDLC_FIFO_SIZE_128 : 0; + + switch (protocol) { + case -1: /* used for init */ + bch->state = -1; + /* fall through */ + case ISDN_P_NONE: + if (bch->state == ISDN_P_NONE) + break; + hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; + hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; + write_ctrl(bch, 5); + bch->state = ISDN_P_NONE; + test_and_clear_bit(FLG_HDLC, &bch->Flags); + test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case ISDN_P_B_RAW: + bch->state = protocol; + hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; + hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; + write_ctrl(bch, 5); + hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; + write_ctrl(bch, 1); + hdlc->ctrl.sr.cmd = 0; + test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case ISDN_P_B_HDLC: + bch->state = protocol; + hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; + hdlc->ctrl.sr.mode = mode | HDLC_MODE_ITF_FLG; + write_ctrl(bch, 5); + hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; + write_ctrl(bch, 1); + hdlc->ctrl.sr.cmd = 0; + test_and_set_bit(FLG_HDLC, &bch->Flags); + break; + default: + pr_info("%s: protocol not known %x\n", fc->name, protocol); + return -ENOPROTOOPT; + } + return 0; +} + +static void +hdlc_empty_fifo(struct bchannel *bch, int count) +{ + u32 *ptr; + u8 *p; + u32 val, addr; + int cnt; + struct fritzcard *fc = bch->hw; + + pr_debug("%s: %s %d\n", fc->name, __func__, count); + if (test_bit(FLG_RX_OFF, &bch->Flags)) { + p = NULL; + bch->dropcnt += count; + } else { + cnt = bchannel_get_rxbuf(bch, count); + if (cnt < 0) { + pr_warning("%s.B%d: No bufferspace for %d bytes\n", + fc->name, bch->nr, count); + return; + } + p = skb_put(bch->rx_skb, count); + } + ptr = (u32 *)p; + if (fc->type == AVM_FRITZ_PCIV2) + addr = fc->addr + (bch->nr == 2 ? + AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); + else { + addr = fc->addr + CHIP_WINDOW; + outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr); + } + cnt = 0; + while (cnt < count) { + val = le32_to_cpu(inl(addr)); + if (p) { + put_unaligned(val, ptr); + ptr++; + } + cnt += 4; + } + if (p && (debug & DEBUG_HW_BFIFO)) { + snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ", + bch->nr, fc->name, count); + print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); + } +} + +static void +hdlc_fill_fifo(struct bchannel *bch) +{ + struct fritzcard *fc = bch->hw; + struct hdlc_hw *hdlc; + int count, fs, cnt = 0, idx; + bool fillempty = false; + u8 *p; + u32 *ptr, val, addr; + + idx = (bch->nr - 1) & 1; + hdlc = &fc->hdlc[idx]; + fs = (fc->type == AVM_FRITZ_PCIV2) ? + HDLC_FIFO_SIZE_V2 : HDLC_FIFO_SIZE_V1; + if (!bch->tx_skb) { + if (!test_bit(FLG_TX_EMPTY, &bch->Flags)) + return; + count = fs; + p = bch->fill; + fillempty = true; + } else { + count = bch->tx_skb->len - bch->tx_idx; + if (count <= 0) + return; + p = bch->tx_skb->data + bch->tx_idx; + } + hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME; + if (count > fs) { + count = fs; + } else { + if (test_bit(FLG_HDLC, &bch->Flags)) + hdlc->ctrl.sr.cmd |= HDLC_CMD_XME; + } + ptr = (u32 *)p; + if (!fillempty) { + pr_debug("%s.B%d: %d/%d/%d", fc->name, bch->nr, count, + bch->tx_idx, bch->tx_skb->len); + bch->tx_idx += count; + } else { + pr_debug("%s.B%d: fillempty %d\n", fc->name, bch->nr, count); + } + hdlc->ctrl.sr.xml = ((count == fs) ? 0 : count); + if (fc->type == AVM_FRITZ_PCIV2) { + __write_ctrl_pciv2(fc, hdlc, bch->nr); + addr = fc->addr + (bch->nr == 2 ? + AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); + } else { + __write_ctrl_pci(fc, hdlc, bch->nr); + addr = fc->addr + CHIP_WINDOW; + } + if (fillempty) { + while (cnt < count) { + /* all bytes the same - no worry about endian */ + outl(*ptr, addr); + cnt += 4; + } + } else { + while (cnt < count) { + val = get_unaligned(ptr); + outl(cpu_to_le32(val), addr); + ptr++; + cnt += 4; + } + } + if ((debug & DEBUG_HW_BFIFO) && !fillempty) { + snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ", + bch->nr, fc->name, count); + print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); + } +} + +static void +HDLC_irq_xpr(struct bchannel *bch) +{ + if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) { + hdlc_fill_fifo(bch); + } else { + if (bch->tx_skb) + dev_kfree_skb(bch->tx_skb); + if (get_next_bframe(bch)) { + hdlc_fill_fifo(bch); + test_and_clear_bit(FLG_TX_EMPTY, &bch->Flags); + } else if (test_bit(FLG_TX_EMPTY, &bch->Flags)) { + hdlc_fill_fifo(bch); + } + } +} + +static void +HDLC_irq(struct bchannel *bch, u32 stat) +{ + struct fritzcard *fc = bch->hw; + int len, fs; + u32 rmlMask; + struct hdlc_hw *hdlc; + + hdlc = &fc->hdlc[(bch->nr - 1) & 1]; + pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat); + if (fc->type == AVM_FRITZ_PCIV2) { + rmlMask = HDLC_STAT_RML_MASK_V2; + fs = HDLC_FIFO_SIZE_V2; + } else { + rmlMask = HDLC_STAT_RML_MASK_V1; + fs = HDLC_FIFO_SIZE_V1; + } + if (stat & HDLC_INT_RPR) { + if (stat & HDLC_STAT_RDO) { + pr_warning("%s: ch%d stat %x RDO\n", + fc->name, bch->nr, stat); + hdlc->ctrl.sr.xml = 0; + hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS; + write_ctrl(bch, 1); + hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS; + write_ctrl(bch, 1); + if (bch->rx_skb) + skb_trim(bch->rx_skb, 0); + } else { + len = (stat & rmlMask) >> 8; + if (!len) + len = fs; + hdlc_empty_fifo(bch, len); + if (!bch->rx_skb) + goto handle_tx; + if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { + recv_Bchannel(bch, 0, false); + } else if (stat & HDLC_STAT_RME) { + if ((stat & HDLC_STAT_CRCVFRRAB) == + HDLC_STAT_CRCVFR) { + recv_Bchannel(bch, 0, false); + } else { + pr_warning("%s: got invalid frame\n", + fc->name); + skb_trim(bch->rx_skb, 0); + } + } + } + } +handle_tx: + if (stat & HDLC_INT_XDU) { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame on HDLC + * in transparent mode we send the next data + */ + pr_warning("%s: ch%d stat %x XDU %s\n", fc->name, bch->nr, + stat, bch->tx_skb ? "tx_skb" : "no tx_skb"); + if (bch->tx_skb && bch->tx_skb->len) { + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + bch->tx_idx = 0; + } else if (test_bit(FLG_FILLEMPTY, &bch->Flags)) { + test_and_set_bit(FLG_TX_EMPTY, &bch->Flags); + } + hdlc->ctrl.sr.xml = 0; + hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS; + write_ctrl(bch, 1); + hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS; + HDLC_irq_xpr(bch); + return; + } else if (stat & HDLC_INT_XPR) + HDLC_irq_xpr(bch); +} + +static inline void +HDLC_irq_main(struct fritzcard *fc) +{ + u32 stat; + struct bchannel *bch; + + stat = read_status(fc, 1); + if (stat & HDLC_INT_MASK) { + bch = Sel_BCS(fc, 1); + if (bch) + HDLC_irq(bch, stat); + else + pr_debug("%s: spurious ch1 IRQ\n", fc->name); + } + stat = read_status(fc, 2); + if (stat & HDLC_INT_MASK) { + bch = Sel_BCS(fc, 2); + if (bch) + HDLC_irq(bch, stat); + else + pr_debug("%s: spurious ch2 IRQ\n", fc->name); + } +} + +static irqreturn_t +avm_fritz_interrupt(int intno, void *dev_id) +{ + struct fritzcard *fc = dev_id; + u8 val; + u8 sval; + + spin_lock(&fc->lock); + sval = inb(fc->addr + 2); + pr_debug("%s: irq stat0 %x\n", fc->name, sval); + if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) { + /* shared IRQ from other HW */ + spin_unlock(&fc->lock); + return IRQ_NONE; + } + fc->irqcnt++; + + if (!(sval & AVM_STATUS0_IRQ_ISAC)) { + val = ReadISAC_V1(fc, ISAC_ISTA); + mISDNisac_irq(&fc->isac, val); + } + if (!(sval & AVM_STATUS0_IRQ_HDLC)) + HDLC_irq_main(fc); + spin_unlock(&fc->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +avm_fritzv2_interrupt(int intno, void *dev_id) +{ + struct fritzcard *fc = dev_id; + u8 val; + u8 sval; + + spin_lock(&fc->lock); + sval = inb(fc->addr + 2); + pr_debug("%s: irq stat0 %x\n", fc->name, sval); + if (!(sval & AVM_STATUS0_IRQ_MASK)) { + /* shared IRQ from other HW */ + spin_unlock(&fc->lock); + return IRQ_NONE; + } + fc->irqcnt++; + + if (sval & AVM_STATUS0_IRQ_HDLC) + HDLC_irq_main(fc); + if (sval & AVM_STATUS0_IRQ_ISAC) { + val = ReadISAC_V2(fc, ISACX_ISTA); + mISDNisac_irq(&fc->isac, val); + } + if (sval & AVM_STATUS0_IRQ_TIMER) { + pr_debug("%s: timer irq\n", fc->name); + outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2); + udelay(1); + outb(fc->ctrlreg, fc->addr + 2); + } + spin_unlock(&fc->lock); + return IRQ_HANDLED; +} + +static int +avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct fritzcard *fc = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&fc->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + hdlc_fill_fifo(bch); + ret = 0; + } + spin_unlock_irqrestore(&fc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(&fc->lock, flags); + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) + ret = modehdlc(bch, ch->protocol); + else + ret = 0; + spin_unlock_irqrestore(&fc->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + spin_lock_irqsave(&fc->lock, flags); + mISDN_clear_bchannel(bch); + modehdlc(bch, ISDN_P_NONE); + spin_unlock_irqrestore(&fc->lock, flags); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static void +inithdlc(struct fritzcard *fc) +{ + modehdlc(&fc->bch[0], -1); + modehdlc(&fc->bch[1], -1); +} + +static void +clear_pending_hdlc_ints(struct fritzcard *fc) +{ + u32 val; + + val = read_status(fc, 1); + pr_debug("%s: HDLC 1 STA %x\n", fc->name, val); + val = read_status(fc, 2); + pr_debug("%s: HDLC 2 STA %x\n", fc->name, val); +} + +static void +reset_avm(struct fritzcard *fc) +{ + switch (fc->type) { + case AVM_FRITZ_PCI: + fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER; + break; + case AVM_FRITZ_PCIV2: + fc->ctrlreg = AVM_STATUS0_RESET; + break; + } + if (debug & DEBUG_HW) + pr_notice("%s: reset\n", fc->name); + disable_hwirq(fc); + mdelay(5); + switch (fc->type) { + case AVM_FRITZ_PCI: + fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER; + disable_hwirq(fc); + outb(AVM_STATUS1_ENA_IOM, fc->addr + 3); + break; + case AVM_FRITZ_PCIV2: + fc->ctrlreg = 0; + disable_hwirq(fc); + break; + } + mdelay(1); + if (debug & DEBUG_HW) + pr_notice("%s: S0/S1 %x/%x\n", fc->name, + inb(fc->addr + 2), inb(fc->addr + 3)); +} + +static int +init_card(struct fritzcard *fc) +{ + int ret, cnt = 3; + u_long flags; + + reset_avm(fc); /* disable IRQ */ + if (fc->type == AVM_FRITZ_PCIV2) + ret = request_irq(fc->irq, avm_fritzv2_interrupt, + IRQF_SHARED, fc->name, fc); + else + ret = request_irq(fc->irq, avm_fritz_interrupt, + IRQF_SHARED, fc->name, fc); + if (ret) { + pr_info("%s: couldn't get interrupt %d\n", + fc->name, fc->irq); + return ret; + } + while (cnt--) { + spin_lock_irqsave(&fc->lock, flags); + ret = fc->isac.init(&fc->isac); + if (ret) { + spin_unlock_irqrestore(&fc->lock, flags); + pr_info("%s: ISAC init failed with %d\n", + fc->name, ret); + break; + } + clear_pending_hdlc_ints(fc); + inithdlc(fc); + enable_hwirq(fc); + /* RESET Receiver and Transmitter */ + if (fc->type == AVM_FRITZ_PCIV2) { + WriteISAC_V2(fc, ISACX_MASK, 0); + WriteISAC_V2(fc, ISACX_CMDRD, 0x41); + } else { + WriteISAC_V1(fc, ISAC_MASK, 0); + WriteISAC_V1(fc, ISAC_CMDR, 0x41); + } + spin_unlock_irqrestore(&fc->lock, flags); + /* Timeout 10ms */ + msleep_interruptible(10); + if (debug & DEBUG_HW) + pr_notice("%s: IRQ %d count %d\n", fc->name, + fc->irq, fc->irqcnt); + if (!fc->irqcnt) { + pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", + fc->name, fc->irq, 3 - cnt); + reset_avm(fc); + } else + return 0; + } + free_irq(fc->irq, fc); + return -EIO; +} + +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + return mISDN_ctrl_bchannel(bch, cq); +} + +static int +avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct fritzcard *fc = bch->hw; + int ret = -EINVAL; + u_long flags; + + pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + cancel_work_sync(&bch->workq); + spin_lock_irqsave(&fc->lock, flags); + mISDN_clear_bchannel(bch); + modehdlc(bch, ISDN_P_NONE); + spin_unlock_irqrestore(&fc->lock, flags); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + ret = 0; + break; + case CONTROL_CHANNEL: + ret = channel_bctrl(bch, arg); + break; + default: + pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd); + } + return ret; +} + +static int +channel_ctrl(struct fritzcard *fc, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; + break; + case MISDN_CTRL_LOOP: + /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ + if (cq->channel < 0 || cq->channel > 3) { + ret = -EINVAL; + break; + } + ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel); + break; + case MISDN_CTRL_L1_TIMER3: + ret = fc->isac.ctrl(&fc->isac, HW_TIMER3_VALUE, cq->p1); + break; + default: + pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +open_bchannel(struct fritzcard *fc, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel == 0 || rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + bch = &fc->bch[rq->adr.channel - 1]; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; + return 0; +} + +/* + * device control function + */ +static int +avm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct fritzcard *fc = dch->hw; + struct channel_req *rq; + int err = 0; + + pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if (rq->protocol == ISDN_P_TE_S0) + err = fc->isac.open(&fc->isac, rq); + else + err = open_bchannel(fc, rq); + if (err) + break; + if (!try_module_get(THIS_MODULE)) + pr_info("%s: cannot get module\n", fc->name); + break; + case CLOSE_CHANNEL: + pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(fc, arg); + break; + default: + pr_debug("%s: %s unknown command %x\n", + fc->name, __func__, cmd); + return -EINVAL; + } + return err; +} + +static int +setup_fritz(struct fritzcard *fc) +{ + u32 val, ver; + + if (!request_region(fc->addr, 32, fc->name)) { + pr_info("%s: AVM config port %x-%x already in use\n", + fc->name, fc->addr, fc->addr + 31); + return -EIO; + } + switch (fc->type) { + case AVM_FRITZ_PCI: + val = inl(fc->addr); + outl(AVM_HDLC_1, fc->addr + CHIP_INDEX); + ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24; + if (debug & DEBUG_HW) { + pr_notice("%s: PCI stat %#x\n", fc->name, val); + pr_notice("%s: PCI Class %X Rev %d\n", fc->name, + val & 0xff, (val >> 8) & 0xff); + pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); + } + ASSIGN_FUNC(V1, ISAC, fc->isac); + fc->isac.type = IPAC_TYPE_ISAC; + break; + case AVM_FRITZ_PCIV2: + val = inl(fc->addr); + ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24; + if (debug & DEBUG_HW) { + pr_notice("%s: PCI V2 stat %#x\n", fc->name, val); + pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name, + val & 0xff, (val >> 8) & 0xff); + pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); + } + ASSIGN_FUNC(V2, ISAC, fc->isac); + fc->isac.type = IPAC_TYPE_ISACX; + break; + default: + release_region(fc->addr, 32); + pr_info("%s: AVM unknown type %d\n", fc->name, fc->type); + return -ENODEV; + } + pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name, + (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" : + "AVM Fritz!CARD PCIv2", fc->irq, fc->addr); + return 0; +} + +static void +release_card(struct fritzcard *card) +{ + u_long flags; + + disable_hwirq(card); + spin_lock_irqsave(&card->lock, flags); + modehdlc(&card->bch[0], ISDN_P_NONE); + modehdlc(&card->bch[1], ISDN_P_NONE); + spin_unlock_irqrestore(&card->lock, flags); + card->isac.release(&card->isac); + free_irq(card->irq, card); + mISDN_freebchannel(&card->bch[1]); + mISDN_freebchannel(&card->bch[0]); + mISDN_unregister_device(&card->isac.dch.dev); + release_region(card->addr, 32); + pci_disable_device(card->pdev); + pci_set_drvdata(card->pdev, NULL); + write_lock_irqsave(&card_lock, flags); + list_del(&card->list); + write_unlock_irqrestore(&card_lock, flags); + kfree(card); + AVM_cnt--; +} + +static int +setup_instance(struct fritzcard *card) +{ + int i, err; + unsigned short minsize; + u_long flags; + + snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1); + write_lock_irqsave(&card_lock, flags); + list_add_tail(&card->list, &Cards); + write_unlock_irqrestore(&card_lock, flags); + + _set_debug(card); + card->isac.name = card->name; + spin_lock_init(&card->lock); + card->isac.hwlock = &card->lock; + mISDNisac_init(&card->isac, card); + + card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + card->isac.dch.dev.D.ctrl = avm_dctrl; + for (i = 0; i < 2; i++) { + card->bch[i].nr = i + 1; + set_channelmap(i + 1, card->isac.dch.dev.channelmap); + if (AVM_FRITZ_PCIV2 == card->type) + minsize = HDLC_FIFO_SIZE_V2; + else + minsize = HDLC_FIFO_SIZE_V1; + mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, minsize); + card->bch[i].hw = card; + card->bch[i].ch.send = avm_l2l1B; + card->bch[i].ch.ctrl = avm_bctrl; + card->bch[i].ch.nr = i + 1; + list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels); + } + err = setup_fritz(card); + if (err) + goto error; + err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev, + card->name); + if (err) + goto error_reg; + err = init_card(card); + if (!err) { + AVM_cnt++; + pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt); + return 0; + } + mISDN_unregister_device(&card->isac.dch.dev); +error_reg: + release_region(card->addr, 32); +error: + card->isac.release(&card->isac); + mISDN_freebchannel(&card->bch[1]); + mISDN_freebchannel(&card->bch[0]); + write_lock_irqsave(&card_lock, flags); + list_del(&card->list); + write_unlock_irqrestore(&card_lock, flags); + kfree(card); + return err; +} + +static int +fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + struct fritzcard *card; + + card = kzalloc(sizeof(struct fritzcard), GFP_KERNEL); + if (!card) { + pr_info("No kmem for fritzcard\n"); + return err; + } + if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) + card->type = AVM_FRITZ_PCIV2; + else + card->type = AVM_FRITZ_PCI; + card->pdev = pdev; + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return err; + } + + pr_notice("mISDN: found adapter %s at %s\n", + (char *) ent->driver_data, pci_name(pdev)); + + card->addr = pci_resource_start(pdev, 1); + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) + pci_set_drvdata(pdev, NULL); + return err; +} + +static void +fritz_remove_pci(struct pci_dev *pdev) +{ + struct fritzcard *card = pci_get_drvdata(pdev); + + if (card) + release_card(card); + else + if (debug) + pr_info("%s: drvdata already removed\n", __func__); +} + +static const struct pci_device_id fcpci_ids[] = { + { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, (unsigned long) "Fritz!Card PCI"}, + { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, (unsigned long) "Fritz!Card PCI v2" }, + { } +}; +MODULE_DEVICE_TABLE(pci, fcpci_ids); + +static struct pci_driver fcpci_driver = { + .name = "fcpci", + .probe = fritzpci_probe, + .remove = fritz_remove_pci, + .id_table = fcpci_ids, +}; + +static int __init AVM_init(void) +{ + int err; + + pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV); + err = pci_register_driver(&fcpci_driver); + return err; +} + +static void __exit AVM_cleanup(void) +{ + pci_unregister_driver(&fcpci_driver); +} + +module_init(AVM_init); +module_exit(AVM_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h new file mode 100644 index 000000000..5acf826d9 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -0,0 +1,1236 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * see notice in hfc_multi.c + */ + +#define DEBUG_HFCMULTI_FIFO 0x00010000 +#define DEBUG_HFCMULTI_CRC 0x00020000 +#define DEBUG_HFCMULTI_INIT 0x00040000 +#define DEBUG_HFCMULTI_PLXSD 0x00080000 +#define DEBUG_HFCMULTI_MODE 0x00100000 +#define DEBUG_HFCMULTI_MSG 0x00200000 +#define DEBUG_HFCMULTI_STATE 0x00400000 +#define DEBUG_HFCMULTI_FILL 0x00800000 +#define DEBUG_HFCMULTI_SYNC 0x01000000 +#define DEBUG_HFCMULTI_DTMF 0x02000000 +#define DEBUG_HFCMULTI_LOCK 0x80000000 + +#define PCI_ENA_REGIO 0x01 +#define PCI_ENA_MEMIO 0x02 + +#define XHFC_IRQ 4 /* SIU_IRQ2 */ +#define XHFC_MEMBASE 0xFE000000 +#define XHFC_MEMSIZE 0x00001000 +#define XHFC_OFFSET 0x00001000 +#define PA_XHFC_A0 0x0020 /* PA10 */ +#define PB_XHFC_IRQ1 0x00000100 /* PB23 */ +#define PB_XHFC_IRQ2 0x00000200 /* PB22 */ +#define PB_XHFC_IRQ3 0x00000400 /* PB21 */ +#define PB_XHFC_IRQ4 0x00000800 /* PB20 */ + +/* + * NOTE: some registers are assigned multiple times due to different modes + * also registers are assigned differen for HFC-4s/8s and HFC-E1 + */ + +/* + #define MAX_FRAME_SIZE 2048 +*/ + +struct hfc_chan { + struct dchannel *dch; /* link if channel is a D-channel */ + struct bchannel *bch; /* link if channel is a B-channel */ + int port; /* the interface port this */ + /* channel is associated with */ + int nt_timer; /* -1 if off, 0 if elapsed, >0 if running */ + int los, ais, slip_tx, slip_rx, rdi; /* current alarms */ + int jitter; + u_long cfg; /* port configuration */ + int sync; /* sync state (used by E1) */ + u_int protocol; /* current protocol */ + int slot_tx; /* current pcm slot */ + int bank_tx; /* current pcm bank */ + int slot_rx; + int bank_rx; + int conf; /* conference setting of TX slot */ + int txpending; /* if there is currently data in */ + /* the FIFO 0=no, 1=yes, 2=splloop */ + int Zfill; /* rx-fifo level on last hfcmulti_tx */ + int rx_off; /* set to turn fifo receive off */ + int coeff_count; /* curren coeff block */ + s32 *coeff; /* memory pointer to 8 coeff blocks */ +}; + + +struct hfcm_hw { + u_char r_ctrl; + u_char r_irq_ctrl; + u_char r_cirm; + u_char r_ram_sz; + u_char r_pcm_md0; + u_char r_irqmsk_misc; + u_char r_dtmf; + u_char r_st_sync; + u_char r_sci_msk; + u_char r_tx0, r_tx1; + u_char a_st_ctrl0[8]; + u_char r_bert_wd_md; + timer_t timer; +}; + + +/* for each stack these flags are used (cfg) */ +#define HFC_CFG_NONCAP_TX 1 /* S/T TX interface has less capacity */ +#define HFC_CFG_DIS_ECHANNEL 2 /* disable E-channel processing */ +#define HFC_CFG_REG_ECHANNEL 3 /* register E-channel */ +#define HFC_CFG_OPTICAL 4 /* the E1 interface is optical */ +#define HFC_CFG_REPORT_LOS 5 /* the card should report loss of signal */ +#define HFC_CFG_REPORT_AIS 6 /* the card should report alarm ind. sign. */ +#define HFC_CFG_REPORT_SLIP 7 /* the card should report bit slips */ +#define HFC_CFG_REPORT_RDI 8 /* the card should report remote alarm */ +#define HFC_CFG_DTMF 9 /* enable DTMF-detection */ +#define HFC_CFG_CRC4 10 /* disable CRC-4 Multiframe mode, */ +/* use double frame instead. */ + +#define HFC_TYPE_E1 1 /* controller is HFC-E1 */ +#define HFC_TYPE_4S 4 /* controller is HFC-4S */ +#define HFC_TYPE_8S 8 /* controller is HFC-8S */ +#define HFC_TYPE_XHFC 5 /* controller is XHFC */ + +#define HFC_CHIP_EXRAM_128 0 /* external ram 128k */ +#define HFC_CHIP_EXRAM_512 1 /* external ram 256k */ +#define HFC_CHIP_REVISION0 2 /* old fifo handling */ +#define HFC_CHIP_PCM_SLAVE 3 /* PCM is slave */ +#define HFC_CHIP_PCM_MASTER 4 /* PCM is master */ +#define HFC_CHIP_RX_SYNC 5 /* disable pll sync for pcm */ +#define HFC_CHIP_DTMF 6 /* DTMF decoding is enabled */ +#define HFC_CHIP_CONF 7 /* conference handling is enabled */ +#define HFC_CHIP_ULAW 8 /* ULAW mode */ +#define HFC_CHIP_CLOCK2 9 /* double clock mode */ +#define HFC_CHIP_E1CLOCK_GET 10 /* always get clock from E1 interface */ +#define HFC_CHIP_E1CLOCK_PUT 11 /* always put clock from E1 interface */ +#define HFC_CHIP_WATCHDOG 12 /* whether we should send signals */ +/* to the watchdog */ +#define HFC_CHIP_B410P 13 /* whether we have a b410p with echocan in */ +/* hw */ +#define HFC_CHIP_PLXSD 14 /* whether we have a Speech-Design PLX */ +#define HFC_CHIP_EMBSD 15 /* whether we have a SD Embedded board */ + +#define HFC_IO_MODE_PCIMEM 0x00 /* normal memory mapped IO */ +#define HFC_IO_MODE_REGIO 0x01 /* PCI io access */ +#define HFC_IO_MODE_PLXSD 0x02 /* access HFC via PLX9030 */ +#define HFC_IO_MODE_EMBSD 0x03 /* direct access */ + +/* table entry in the PCI devices list */ +struct hm_map { + char *vendor_name; + char *card_name; + int type; + int ports; + int clock2; + int leds; + int opticalsupport; + int dip_type; + int io_mode; + int irq; +}; + +struct hfc_multi { + struct list_head list; + struct hm_map *mtyp; + int id; + int pcm; /* id of pcm bus */ + int ctype; /* controller type */ + int ports; + + u_int irq; /* irq used by card */ + u_int irqcnt; + struct pci_dev *pci_dev; + int io_mode; /* selects mode */ +#ifdef HFC_REGISTER_DEBUG + void (*HFC_outb)(struct hfc_multi *hc, u_char reg, + u_char val, const char *function, int line); + void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, + u_char val, const char *function, int line); + u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + void (*HFC_wait)(struct hfc_multi *hc, + const char *function, int line); + void (*HFC_wait_nodebug)(struct hfc_multi *hc, + const char *function, int line); +#else + void (*HFC_outb)(struct hfc_multi *hc, u_char reg, + u_char val); + void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, + u_char val); + u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg); + u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg); + u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg); + u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg); + void (*HFC_wait)(struct hfc_multi *hc); + void (*HFC_wait_nodebug)(struct hfc_multi *hc); +#endif + void (*read_fifo)(struct hfc_multi *hc, u_char *data, + int len); + void (*write_fifo)(struct hfc_multi *hc, u_char *data, + int len); + u_long pci_origmembase, plx_origmembase; + void __iomem *pci_membase; /* PCI memory */ + void __iomem *plx_membase; /* PLX memory */ + u_long xhfc_origmembase; + u_char *xhfc_membase; + u_long *xhfc_memaddr, *xhfc_memdata; +#ifdef CONFIG_MISDN_HFCMULTI_8xx + struct immap *immap; +#endif + u_long pb_irqmsk; /* Portbit mask to check the IRQ line */ + u_long pci_iobase; /* PCI IO */ + struct hfcm_hw hw; /* remember data of write-only-registers */ + + u_long chip; /* chip configuration */ + int masterclk; /* port that provides master clock -1=off */ + unsigned char silence;/* silence byte */ + unsigned char silence_data[128];/* silence block */ + int dtmf; /* flag that dtmf is currently in process */ + int Flen; /* F-buffer size */ + int Zlen; /* Z-buffer size (must be int for calculation)*/ + int max_trans; /* maximum transparent fifo fill */ + int Zmin; /* Z-buffer offset */ + int DTMFbase; /* base address of DTMF coefficients */ + + u_int slots; /* number of PCM slots */ + u_int leds; /* type of leds */ + u_long ledstate; /* save last state of leds */ + int opticalsupport; /* has the e1 board */ + /* an optical Interface */ + + u_int bmask[32]; /* bitmask of bchannels for port */ + u_char dnum[32]; /* array of used dchannel numbers for port */ + u_char created[32]; /* what port is created */ + u_int activity_tx; /* if there is data TX / RX */ + u_int activity_rx; /* bitmask according to port number */ + /* (will be cleared after */ + /* showing led-states) */ + u_int flash[8]; /* counter for flashing 8 leds on activity */ + + u_long wdcount; /* every 500 ms we need to */ + /* send the watchdog a signal */ + u_char wdbyte; /* watchdog toggle byte */ + int e1_state; /* keep track of last state */ + int e1_getclock; /* if sync is retrieved from interface */ + int syncronized; /* keep track of existing sync interface */ + int e1_resync; /* resync jobs */ + + spinlock_t lock; /* the lock */ + + struct mISDNclock *iclock; /* isdn clock support */ + int iclock_on; + + /* + * the channel index is counted from 0, regardless where the channel + * is located on the hfc-channel. + * the bch->channel is equvalent to the hfc-channel + */ + struct hfc_chan chan[32]; + signed char slot_owner[256]; /* owner channel of slot */ +}; + +/* PLX GPIOs */ +#define PLX_GPIO4_DIR_BIT 13 +#define PLX_GPIO4_BIT 14 +#define PLX_GPIO5_DIR_BIT 16 +#define PLX_GPIO5_BIT 17 +#define PLX_GPIO6_DIR_BIT 19 +#define PLX_GPIO6_BIT 20 +#define PLX_GPIO7_DIR_BIT 22 +#define PLX_GPIO7_BIT 23 +#define PLX_GPIO8_DIR_BIT 25 +#define PLX_GPIO8_BIT 26 + +#define PLX_GPIO4 (1 << PLX_GPIO4_BIT) +#define PLX_GPIO5 (1 << PLX_GPIO5_BIT) +#define PLX_GPIO6 (1 << PLX_GPIO6_BIT) +#define PLX_GPIO7 (1 << PLX_GPIO7_BIT) +#define PLX_GPIO8 (1 << PLX_GPIO8_BIT) + +#define PLX_GPIO4_DIR (1 << PLX_GPIO4_DIR_BIT) +#define PLX_GPIO5_DIR (1 << PLX_GPIO5_DIR_BIT) +#define PLX_GPIO6_DIR (1 << PLX_GPIO6_DIR_BIT) +#define PLX_GPIO7_DIR (1 << PLX_GPIO7_DIR_BIT) +#define PLX_GPIO8_DIR (1 << PLX_GPIO8_DIR_BIT) + +#define PLX_TERM_ON PLX_GPIO7 +#define PLX_SLAVE_EN_N PLX_GPIO5 +#define PLX_MASTER_EN PLX_GPIO6 +#define PLX_SYNC_O_EN PLX_GPIO4 +#define PLX_DSP_RES_N PLX_GPIO8 +/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */ +#define PLX_GPIOC_INIT (PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \ + | PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N) + +/* PLX Interrupt Control/STATUS */ +#define PLX_INTCSR_LINTI1_ENABLE 0x01 +#define PLX_INTCSR_LINTI1_STATUS 0x04 +#define PLX_INTCSR_LINTI2_ENABLE 0x08 +#define PLX_INTCSR_LINTI2_STATUS 0x20 +#define PLX_INTCSR_PCIINT_ENABLE 0x40 + +/* PLX Registers */ +#define PLX_INTCSR 0x4c +#define PLX_CNTRL 0x50 +#define PLX_GPIOC 0x54 + + +/* + * REGISTER SETTING FOR HFC-4S/8S AND HFC-E1 + */ + +/* write only registers */ +#define R_CIRM 0x00 +#define R_CTRL 0x01 +#define R_BRG_PCM_CFG 0x02 +#define R_RAM_ADDR0 0x08 +#define R_RAM_ADDR1 0x09 +#define R_RAM_ADDR2 0x0A +#define R_FIRST_FIFO 0x0B +#define R_RAM_SZ 0x0C +#define R_FIFO_MD 0x0D +#define R_INC_RES_FIFO 0x0E +#define R_FSM_IDX 0x0F +#define R_FIFO 0x0F +#define R_SLOT 0x10 +#define R_IRQMSK_MISC 0x11 +#define R_SCI_MSK 0x12 +#define R_IRQ_CTRL 0x13 +#define R_PCM_MD0 0x14 +#define R_PCM_MD1 0x15 +#define R_PCM_MD2 0x15 +#define R_SH0H 0x15 +#define R_SH1H 0x15 +#define R_SH0L 0x15 +#define R_SH1L 0x15 +#define R_SL_SEL0 0x15 +#define R_SL_SEL1 0x15 +#define R_SL_SEL2 0x15 +#define R_SL_SEL3 0x15 +#define R_SL_SEL4 0x15 +#define R_SL_SEL5 0x15 +#define R_SL_SEL6 0x15 +#define R_SL_SEL7 0x15 +#define R_ST_SEL 0x16 +#define R_ST_SYNC 0x17 +#define R_CONF_EN 0x18 +#define R_TI_WD 0x1A +#define R_BERT_WD_MD 0x1B +#define R_DTMF 0x1C +#define R_DTMF_N 0x1D +#define R_E1_WR_STA 0x20 +#define R_E1_RD_STA 0x20 +#define R_LOS0 0x22 +#define R_LOS1 0x23 +#define R_RX0 0x24 +#define R_RX_FR0 0x25 +#define R_RX_FR1 0x26 +#define R_TX0 0x28 +#define R_TX1 0x29 +#define R_TX_FR0 0x2C + +#define R_TX_FR1 0x2D +#define R_TX_FR2 0x2E +#define R_JATT_ATT 0x2F /* undocumented */ +#define A_ST_RD_STATE 0x30 +#define A_ST_WR_STATE 0x30 +#define R_RX_OFF 0x30 +#define A_ST_CTRL0 0x31 +#define R_SYNC_OUT 0x31 +#define A_ST_CTRL1 0x32 +#define A_ST_CTRL2 0x33 +#define A_ST_SQ_WR 0x34 +#define R_TX_OFF 0x34 +#define R_SYNC_CTRL 0x35 +#define A_ST_CLK_DLY 0x37 +#define R_PWM0 0x38 +#define R_PWM1 0x39 +#define A_ST_B1_TX 0x3C +#define A_ST_B2_TX 0x3D +#define A_ST_D_TX 0x3E +#define R_GPIO_OUT0 0x40 +#define R_GPIO_OUT1 0x41 +#define R_GPIO_EN0 0x42 +#define R_GPIO_EN1 0x43 +#define R_GPIO_SEL 0x44 +#define R_BRG_CTRL 0x45 +#define R_PWM_MD 0x46 +#define R_BRG_MD 0x47 +#define R_BRG_TIM0 0x48 +#define R_BRG_TIM1 0x49 +#define R_BRG_TIM2 0x4A +#define R_BRG_TIM3 0x4B +#define R_BRG_TIM_SEL01 0x4C +#define R_BRG_TIM_SEL23 0x4D +#define R_BRG_TIM_SEL45 0x4E +#define R_BRG_TIM_SEL67 0x4F +#define A_SL_CFG 0xD0 +#define A_CONF 0xD1 +#define A_CH_MSK 0xF4 +#define A_CON_HDLC 0xFA +#define A_SUBCH_CFG 0xFB +#define A_CHANNEL 0xFC +#define A_FIFO_SEQ 0xFD +#define A_IRQ_MSK 0xFF + +/* read only registers */ +#define A_Z12 0x04 +#define A_Z1L 0x04 +#define A_Z1 0x04 +#define A_Z1H 0x05 +#define A_Z2L 0x06 +#define A_Z2 0x06 +#define A_Z2H 0x07 +#define A_F1 0x0C +#define A_F12 0x0C +#define A_F2 0x0D +#define R_IRQ_OVIEW 0x10 +#define R_IRQ_MISC 0x11 +#define R_IRQ_STATECH 0x12 +#define R_CONF_OFLOW 0x14 +#define R_RAM_USE 0x15 +#define R_CHIP_ID 0x16 +#define R_BERT_STA 0x17 +#define R_F0_CNTL 0x18 +#define R_F0_CNTH 0x19 +#define R_BERT_EC 0x1A +#define R_BERT_ECL 0x1A +#define R_BERT_ECH 0x1B +#define R_STATUS 0x1C +#define R_CHIP_RV 0x1F +#define R_STATE 0x20 +#define R_SYNC_STA 0x24 +#define R_RX_SL0_0 0x25 +#define R_RX_SL0_1 0x26 +#define R_RX_SL0_2 0x27 +#define R_JATT_DIR 0x2b /* undocumented */ +#define R_SLIP 0x2c +#define A_ST_RD_STA 0x30 +#define R_FAS_EC 0x30 +#define R_FAS_ECL 0x30 +#define R_FAS_ECH 0x31 +#define R_VIO_EC 0x32 +#define R_VIO_ECL 0x32 +#define R_VIO_ECH 0x33 +#define A_ST_SQ_RD 0x34 +#define R_CRC_EC 0x34 +#define R_CRC_ECL 0x34 +#define R_CRC_ECH 0x35 +#define R_E_EC 0x36 +#define R_E_ECL 0x36 +#define R_E_ECH 0x37 +#define R_SA6_SA13_EC 0x38 +#define R_SA6_SA13_ECL 0x38 +#define R_SA6_SA13_ECH 0x39 +#define R_SA6_SA23_EC 0x3A +#define R_SA6_SA23_ECL 0x3A +#define R_SA6_SA23_ECH 0x3B +#define A_ST_B1_RX 0x3C +#define A_ST_B2_RX 0x3D +#define A_ST_D_RX 0x3E +#define A_ST_E_RX 0x3F +#define R_GPIO_IN0 0x40 +#define R_GPIO_IN1 0x41 +#define R_GPI_IN0 0x44 +#define R_GPI_IN1 0x45 +#define R_GPI_IN2 0x46 +#define R_GPI_IN3 0x47 +#define R_INT_DATA 0x88 +#define R_IRQ_FIFO_BL0 0xC8 +#define R_IRQ_FIFO_BL1 0xC9 +#define R_IRQ_FIFO_BL2 0xCA +#define R_IRQ_FIFO_BL3 0xCB +#define R_IRQ_FIFO_BL4 0xCC +#define R_IRQ_FIFO_BL5 0xCD +#define R_IRQ_FIFO_BL6 0xCE +#define R_IRQ_FIFO_BL7 0xCF + +/* read and write registers */ +#define A_FIFO_DATA0 0x80 +#define A_FIFO_DATA1 0x80 +#define A_FIFO_DATA2 0x80 +#define A_FIFO_DATA0_NOINC 0x84 +#define A_FIFO_DATA1_NOINC 0x84 +#define A_FIFO_DATA2_NOINC 0x84 +#define R_RAM_DATA 0xC0 + + +/* + * BIT SETTING FOR HFC-4S/8S AND HFC-E1 + */ + +/* chapter 2: universal bus interface */ +/* R_CIRM */ +#define V_IRQ_SEL 0x01 +#define V_SRES 0x08 +#define V_HFCRES 0x10 +#define V_PCMRES 0x20 +#define V_STRES 0x40 +#define V_ETRES 0x40 +#define V_RLD_EPR 0x80 +/* R_CTRL */ +#define V_FIFO_LPRIO 0x02 +#define V_SLOW_RD 0x04 +#define V_EXT_RAM 0x08 +#define V_CLK_OFF 0x20 +#define V_ST_CLK 0x40 +/* R_RAM_ADDR0 */ +#define V_RAM_ADDR2 0x01 +#define V_ADDR_RES 0x40 +#define V_ADDR_INC 0x80 +/* R_RAM_SZ */ +#define V_RAM_SZ 0x01 +#define V_PWM0_16KHZ 0x10 +#define V_PWM1_16KHZ 0x20 +#define V_FZ_MD 0x80 +/* R_CHIP_ID */ +#define V_PNP_IRQ 0x01 +#define V_CHIP_ID 0x10 + +/* chapter 3: data flow */ +/* R_FIRST_FIFO */ +#define V_FIRST_FIRO_DIR 0x01 +#define V_FIRST_FIFO_NUM 0x02 +/* R_FIFO_MD */ +#define V_FIFO_MD 0x01 +#define V_CSM_MD 0x04 +#define V_FSM_MD 0x08 +#define V_FIFO_SZ 0x10 +/* R_FIFO */ +#define V_FIFO_DIR 0x01 +#define V_FIFO_NUM 0x02 +#define V_REV 0x80 +/* R_SLOT */ +#define V_SL_DIR 0x01 +#define V_SL_NUM 0x02 +/* A_SL_CFG */ +#define V_CH_DIR 0x01 +#define V_CH_SEL 0x02 +#define V_ROUTING 0x40 +/* A_CON_HDLC */ +#define V_IFF 0x01 +#define V_HDLC_TRP 0x02 +#define V_TRP_IRQ 0x04 +#define V_DATA_FLOW 0x20 +/* A_SUBCH_CFG */ +#define V_BIT_CNT 0x01 +#define V_START_BIT 0x08 +#define V_LOOP_FIFO 0x40 +#define V_INV_DATA 0x80 +/* A_CHANNEL */ +#define V_CH_DIR0 0x01 +#define V_CH_NUM0 0x02 +/* A_FIFO_SEQ */ +#define V_NEXT_FIFO_DIR 0x01 +#define V_NEXT_FIFO_NUM 0x02 +#define V_SEQ_END 0x40 + +/* chapter 4: FIFO handling and HDLC controller */ +/* R_INC_RES_FIFO */ +#define V_INC_F 0x01 +#define V_RES_F 0x02 +#define V_RES_LOST 0x04 + +/* chapter 5: S/T interface */ +/* R_SCI_MSK */ +#define V_SCI_MSK_ST0 0x01 +#define V_SCI_MSK_ST1 0x02 +#define V_SCI_MSK_ST2 0x04 +#define V_SCI_MSK_ST3 0x08 +#define V_SCI_MSK_ST4 0x10 +#define V_SCI_MSK_ST5 0x20 +#define V_SCI_MSK_ST6 0x40 +#define V_SCI_MSK_ST7 0x80 +/* R_ST_SEL */ +#define V_ST_SEL 0x01 +#define V_MULT_ST 0x08 +/* R_ST_SYNC */ +#define V_SYNC_SEL 0x01 +#define V_AUTO_SYNC 0x08 +/* A_ST_WR_STA */ +#define V_ST_SET_STA 0x01 +#define V_ST_LD_STA 0x10 +#define V_ST_ACT 0x20 +#define V_SET_G2_G3 0x80 +/* A_ST_CTRL0 */ +#define V_B1_EN 0x01 +#define V_B2_EN 0x02 +#define V_ST_MD 0x04 +#define V_D_PRIO 0x08 +#define V_SQ_EN 0x10 +#define V_96KHZ 0x20 +#define V_TX_LI 0x40 +#define V_ST_STOP 0x80 +/* A_ST_CTRL1 */ +#define V_G2_G3_EN 0x01 +#define V_D_HI 0x04 +#define V_E_IGNO 0x08 +#define V_E_LO 0x10 +#define V_B12_SWAP 0x80 +/* A_ST_CTRL2 */ +#define V_B1_RX_EN 0x01 +#define V_B2_RX_EN 0x02 +#define V_ST_TRIS 0x40 +/* A_ST_CLK_DLY */ +#define V_ST_CK_DLY 0x01 +#define V_ST_SMPL 0x10 +/* A_ST_D_TX */ +#define V_ST_D_TX 0x40 +/* R_IRQ_STATECH */ +#define V_SCI_ST0 0x01 +#define V_SCI_ST1 0x02 +#define V_SCI_ST2 0x04 +#define V_SCI_ST3 0x08 +#define V_SCI_ST4 0x10 +#define V_SCI_ST5 0x20 +#define V_SCI_ST6 0x40 +#define V_SCI_ST7 0x80 +/* A_ST_RD_STA */ +#define V_ST_STA 0x01 +#define V_FR_SYNC_ST 0x10 +#define V_TI2_EXP 0x20 +#define V_INFO0 0x40 +#define V_G2_G3 0x80 +/* A_ST_SQ_RD */ +#define V_ST_SQ 0x01 +#define V_MF_RX_RDY 0x10 +#define V_MF_TX_RDY 0x80 +/* A_ST_D_RX */ +#define V_ST_D_RX 0x40 +/* A_ST_E_RX */ +#define V_ST_E_RX 0x40 + +/* chapter 5: E1 interface */ +/* R_E1_WR_STA */ +/* R_E1_RD_STA */ +#define V_E1_SET_STA 0x01 +#define V_E1_LD_STA 0x10 +/* R_RX0 */ +#define V_RX_CODE 0x01 +#define V_RX_FBAUD 0x04 +#define V_RX_CMI 0x08 +#define V_RX_INV_CMI 0x10 +#define V_RX_INV_CLK 0x20 +#define V_RX_INV_DATA 0x40 +#define V_AIS_ITU 0x80 +/* R_RX_FR0 */ +#define V_NO_INSYNC 0x01 +#define V_AUTO_RESYNC 0x02 +#define V_AUTO_RECO 0x04 +#define V_SWORD_COND 0x08 +#define V_SYNC_LOSS 0x10 +#define V_XCRC_SYNC 0x20 +#define V_MF_RESYNC 0x40 +#define V_RESYNC 0x80 +/* R_RX_FR1 */ +#define V_RX_MF 0x01 +#define V_RX_MF_SYNC 0x02 +#define V_RX_SL0_RAM 0x04 +#define V_ERR_SIM 0x20 +#define V_RES_NMF 0x40 +/* R_TX0 */ +#define V_TX_CODE 0x01 +#define V_TX_FBAUD 0x04 +#define V_TX_CMI_CODE 0x08 +#define V_TX_INV_CMI_CODE 0x10 +#define V_TX_INV_CLK 0x20 +#define V_TX_INV_DATA 0x40 +#define V_OUT_EN 0x80 +/* R_TX1 */ +#define V_INV_CLK 0x01 +#define V_EXCHG_DATA_LI 0x02 +#define V_AIS_OUT 0x04 +#define V_ATX 0x20 +#define V_NTRI 0x40 +#define V_AUTO_ERR_RES 0x80 +/* R_TX_FR0 */ +#define V_TRP_FAS 0x01 +#define V_TRP_NFAS 0x02 +#define V_TRP_RAL 0x04 +#define V_TRP_SA 0x08 +/* R_TX_FR1 */ +#define V_TX_FAS 0x01 +#define V_TX_NFAS 0x02 +#define V_TX_RAL 0x04 +#define V_TX_SA 0x08 +/* R_TX_FR2 */ +#define V_TX_MF 0x01 +#define V_TRP_SL0 0x02 +#define V_TX_SL0_RAM 0x04 +#define V_TX_E 0x10 +#define V_NEG_E 0x20 +#define V_XS12_ON 0x40 +#define V_XS15_ON 0x80 +/* R_RX_OFF */ +#define V_RX_SZ 0x01 +#define V_RX_INIT 0x04 +/* R_SYNC_OUT */ +#define V_SYNC_E1_RX 0x01 +#define V_IPATS0 0x20 +#define V_IPATS1 0x40 +#define V_IPATS2 0x80 +/* R_TX_OFF */ +#define V_TX_SZ 0x01 +#define V_TX_INIT 0x04 +/* R_SYNC_CTRL */ +#define V_EXT_CLK_SYNC 0x01 +#define V_SYNC_OFFS 0x02 +#define V_PCM_SYNC 0x04 +#define V_NEG_CLK 0x08 +#define V_HCLK 0x10 +/* + #define V_JATT_AUTO_DEL 0x20 + #define V_JATT_AUTO 0x40 +*/ +#define V_JATT_OFF 0x80 +/* R_STATE */ +#define V_E1_STA 0x01 +#define V_ALT_FR_RX 0x40 +#define V_ALT_FR_TX 0x80 +/* R_SYNC_STA */ +#define V_RX_STA 0x01 +#define V_FR_SYNC_E1 0x04 +#define V_SIG_LOS 0x08 +#define V_MFA_STA 0x10 +#define V_AIS 0x40 +#define V_NO_MF_SYNC 0x80 +/* R_RX_SL0_0 */ +#define V_SI_FAS 0x01 +#define V_SI_NFAS 0x02 +#define V_A 0x04 +#define V_CRC_OK 0x08 +#define V_TX_E1 0x10 +#define V_TX_E2 0x20 +#define V_RX_E1 0x40 +#define V_RX_E2 0x80 +/* R_SLIP */ +#define V_SLIP_RX 0x01 +#define V_FOSLIP_RX 0x08 +#define V_SLIP_TX 0x10 +#define V_FOSLIP_TX 0x80 + +/* chapter 6: PCM interface */ +/* R_PCM_MD0 */ +#define V_PCM_MD 0x01 +#define V_C4_POL 0x02 +#define V_F0_NEG 0x04 +#define V_F0_LEN 0x08 +#define V_PCM_ADDR 0x10 +/* R_SL_SEL0 */ +#define V_SL_SEL0 0x01 +#define V_SH_SEL0 0x80 +/* R_SL_SEL1 */ +#define V_SL_SEL1 0x01 +#define V_SH_SEL1 0x80 +/* R_SL_SEL2 */ +#define V_SL_SEL2 0x01 +#define V_SH_SEL2 0x80 +/* R_SL_SEL3 */ +#define V_SL_SEL3 0x01 +#define V_SH_SEL3 0x80 +/* R_SL_SEL4 */ +#define V_SL_SEL4 0x01 +#define V_SH_SEL4 0x80 +/* R_SL_SEL5 */ +#define V_SL_SEL5 0x01 +#define V_SH_SEL5 0x80 +/* R_SL_SEL6 */ +#define V_SL_SEL6 0x01 +#define V_SH_SEL6 0x80 +/* R_SL_SEL7 */ +#define V_SL_SEL7 0x01 +#define V_SH_SEL7 0x80 +/* R_PCM_MD1 */ +#define V_ODEC_CON 0x01 +#define V_PLL_ADJ 0x04 +#define V_PCM_DR 0x10 +#define V_PCM_LOOP 0x40 +/* R_PCM_MD2 */ +#define V_SYNC_PLL 0x02 +#define V_SYNC_SRC 0x04 +#define V_SYNC_OUT 0x08 +#define V_ICR_FR_TIME 0x40 +#define V_EN_PLL 0x80 + +/* chapter 7: pulse width modulation */ +/* R_PWM_MD */ +#define V_EXT_IRQ_EN 0x08 +#define V_PWM0_MD 0x10 +#define V_PWM1_MD 0x40 + +/* chapter 8: multiparty audio conferences */ +/* R_CONF_EN */ +#define V_CONF_EN 0x01 +#define V_ULAW 0x80 +/* A_CONF */ +#define V_CONF_NUM 0x01 +#define V_NOISE_SUPPR 0x08 +#define V_ATT_LEV 0x20 +#define V_CONF_SL 0x80 +/* R_CONF_OFLOW */ +#define V_CONF_OFLOW0 0x01 +#define V_CONF_OFLOW1 0x02 +#define V_CONF_OFLOW2 0x04 +#define V_CONF_OFLOW3 0x08 +#define V_CONF_OFLOW4 0x10 +#define V_CONF_OFLOW5 0x20 +#define V_CONF_OFLOW6 0x40 +#define V_CONF_OFLOW7 0x80 + +/* chapter 9: DTMF contoller */ +/* R_DTMF0 */ +#define V_DTMF_EN 0x01 +#define V_HARM_SEL 0x02 +#define V_DTMF_RX_CH 0x04 +#define V_DTMF_STOP 0x08 +#define V_CHBL_SEL 0x10 +#define V_RST_DTMF 0x40 +#define V_ULAW_SEL 0x80 + +/* chapter 10: BERT */ +/* R_BERT_WD_MD */ +#define V_PAT_SEQ 0x01 +#define V_BERT_ERR 0x08 +#define V_AUTO_WD_RES 0x20 +#define V_WD_RES 0x80 +/* R_BERT_STA */ +#define V_BERT_SYNC_SRC 0x01 +#define V_BERT_SYNC 0x10 +#define V_BERT_INV_DATA 0x20 + +/* chapter 11: auxiliary interface */ +/* R_BRG_PCM_CFG */ +#define V_BRG_EN 0x01 +#define V_BRG_MD 0x02 +#define V_PCM_CLK 0x20 +#define V_ADDR_WRDLY 0x40 +/* R_BRG_CTRL */ +#define V_BRG_CS 0x01 +#define V_BRG_ADDR 0x08 +#define V_BRG_CS_SRC 0x80 +/* R_BRG_MD */ +#define V_BRG_MD0 0x01 +#define V_BRG_MD1 0x02 +#define V_BRG_MD2 0x04 +#define V_BRG_MD3 0x08 +#define V_BRG_MD4 0x10 +#define V_BRG_MD5 0x20 +#define V_BRG_MD6 0x40 +#define V_BRG_MD7 0x80 +/* R_BRG_TIM0 */ +#define V_BRG_TIM0_IDLE 0x01 +#define V_BRG_TIM0_CLK 0x10 +/* R_BRG_TIM1 */ +#define V_BRG_TIM1_IDLE 0x01 +#define V_BRG_TIM1_CLK 0x10 +/* R_BRG_TIM2 */ +#define V_BRG_TIM2_IDLE 0x01 +#define V_BRG_TIM2_CLK 0x10 +/* R_BRG_TIM3 */ +#define V_BRG_TIM3_IDLE 0x01 +#define V_BRG_TIM3_CLK 0x10 +/* R_BRG_TIM_SEL01 */ +#define V_BRG_WR_SEL0 0x01 +#define V_BRG_RD_SEL0 0x04 +#define V_BRG_WR_SEL1 0x10 +#define V_BRG_RD_SEL1 0x40 +/* R_BRG_TIM_SEL23 */ +#define V_BRG_WR_SEL2 0x01 +#define V_BRG_RD_SEL2 0x04 +#define V_BRG_WR_SEL3 0x10 +#define V_BRG_RD_SEL3 0x40 +/* R_BRG_TIM_SEL45 */ +#define V_BRG_WR_SEL4 0x01 +#define V_BRG_RD_SEL4 0x04 +#define V_BRG_WR_SEL5 0x10 +#define V_BRG_RD_SEL5 0x40 +/* R_BRG_TIM_SEL67 */ +#define V_BRG_WR_SEL6 0x01 +#define V_BRG_RD_SEL6 0x04 +#define V_BRG_WR_SEL7 0x10 +#define V_BRG_RD_SEL7 0x40 + +/* chapter 12: clock, reset, interrupt, timer and watchdog */ +/* R_IRQMSK_MISC */ +#define V_STA_IRQMSK 0x01 +#define V_TI_IRQMSK 0x02 +#define V_PROC_IRQMSK 0x04 +#define V_DTMF_IRQMSK 0x08 +#define V_IRQ1S_MSK 0x10 +#define V_SA6_IRQMSK 0x20 +#define V_RX_EOMF_MSK 0x40 +#define V_TX_EOMF_MSK 0x80 +/* R_IRQ_CTRL */ +#define V_FIFO_IRQ 0x01 +#define V_GLOB_IRQ_EN 0x08 +#define V_IRQ_POL 0x10 +/* R_TI_WD */ +#define V_EV_TS 0x01 +#define V_WD_TS 0x10 +/* A_IRQ_MSK */ +#define V_IRQ 0x01 +#define V_BERT_EN 0x02 +#define V_MIX_IRQ 0x04 +/* R_IRQ_OVIEW */ +#define V_IRQ_FIFO_BL0 0x01 +#define V_IRQ_FIFO_BL1 0x02 +#define V_IRQ_FIFO_BL2 0x04 +#define V_IRQ_FIFO_BL3 0x08 +#define V_IRQ_FIFO_BL4 0x10 +#define V_IRQ_FIFO_BL5 0x20 +#define V_IRQ_FIFO_BL6 0x40 +#define V_IRQ_FIFO_BL7 0x80 +/* R_IRQ_MISC */ +#define V_STA_IRQ 0x01 +#define V_TI_IRQ 0x02 +#define V_IRQ_PROC 0x04 +#define V_DTMF_IRQ 0x08 +#define V_IRQ1S 0x10 +#define V_SA6_IRQ 0x20 +#define V_RX_EOMF 0x40 +#define V_TX_EOMF 0x80 +/* R_STATUS */ +#define V_BUSY 0x01 +#define V_PROC 0x02 +#define V_DTMF_STA 0x04 +#define V_LOST_STA 0x08 +#define V_SYNC_IN 0x10 +#define V_EXT_IRQSTA 0x20 +#define V_MISC_IRQSTA 0x40 +#define V_FR_IRQSTA 0x80 +/* R_IRQ_FIFO_BL0 */ +#define V_IRQ_FIFO0_TX 0x01 +#define V_IRQ_FIFO0_RX 0x02 +#define V_IRQ_FIFO1_TX 0x04 +#define V_IRQ_FIFO1_RX 0x08 +#define V_IRQ_FIFO2_TX 0x10 +#define V_IRQ_FIFO2_RX 0x20 +#define V_IRQ_FIFO3_TX 0x40 +#define V_IRQ_FIFO3_RX 0x80 +/* R_IRQ_FIFO_BL1 */ +#define V_IRQ_FIFO4_TX 0x01 +#define V_IRQ_FIFO4_RX 0x02 +#define V_IRQ_FIFO5_TX 0x04 +#define V_IRQ_FIFO5_RX 0x08 +#define V_IRQ_FIFO6_TX 0x10 +#define V_IRQ_FIFO6_RX 0x20 +#define V_IRQ_FIFO7_TX 0x40 +#define V_IRQ_FIFO7_RX 0x80 +/* R_IRQ_FIFO_BL2 */ +#define V_IRQ_FIFO8_TX 0x01 +#define V_IRQ_FIFO8_RX 0x02 +#define V_IRQ_FIFO9_TX 0x04 +#define V_IRQ_FIFO9_RX 0x08 +#define V_IRQ_FIFO10_TX 0x10 +#define V_IRQ_FIFO10_RX 0x20 +#define V_IRQ_FIFO11_TX 0x40 +#define V_IRQ_FIFO11_RX 0x80 +/* R_IRQ_FIFO_BL3 */ +#define V_IRQ_FIFO12_TX 0x01 +#define V_IRQ_FIFO12_RX 0x02 +#define V_IRQ_FIFO13_TX 0x04 +#define V_IRQ_FIFO13_RX 0x08 +#define V_IRQ_FIFO14_TX 0x10 +#define V_IRQ_FIFO14_RX 0x20 +#define V_IRQ_FIFO15_TX 0x40 +#define V_IRQ_FIFO15_RX 0x80 +/* R_IRQ_FIFO_BL4 */ +#define V_IRQ_FIFO16_TX 0x01 +#define V_IRQ_FIFO16_RX 0x02 +#define V_IRQ_FIFO17_TX 0x04 +#define V_IRQ_FIFO17_RX 0x08 +#define V_IRQ_FIFO18_TX 0x10 +#define V_IRQ_FIFO18_RX 0x20 +#define V_IRQ_FIFO19_TX 0x40 +#define V_IRQ_FIFO19_RX 0x80 +/* R_IRQ_FIFO_BL5 */ +#define V_IRQ_FIFO20_TX 0x01 +#define V_IRQ_FIFO20_RX 0x02 +#define V_IRQ_FIFO21_TX 0x04 +#define V_IRQ_FIFO21_RX 0x08 +#define V_IRQ_FIFO22_TX 0x10 +#define V_IRQ_FIFO22_RX 0x20 +#define V_IRQ_FIFO23_TX 0x40 +#define V_IRQ_FIFO23_RX 0x80 +/* R_IRQ_FIFO_BL6 */ +#define V_IRQ_FIFO24_TX 0x01 +#define V_IRQ_FIFO24_RX 0x02 +#define V_IRQ_FIFO25_TX 0x04 +#define V_IRQ_FIFO25_RX 0x08 +#define V_IRQ_FIFO26_TX 0x10 +#define V_IRQ_FIFO26_RX 0x20 +#define V_IRQ_FIFO27_TX 0x40 +#define V_IRQ_FIFO27_RX 0x80 +/* R_IRQ_FIFO_BL7 */ +#define V_IRQ_FIFO28_TX 0x01 +#define V_IRQ_FIFO28_RX 0x02 +#define V_IRQ_FIFO29_TX 0x04 +#define V_IRQ_FIFO29_RX 0x08 +#define V_IRQ_FIFO30_TX 0x10 +#define V_IRQ_FIFO30_RX 0x20 +#define V_IRQ_FIFO31_TX 0x40 +#define V_IRQ_FIFO31_RX 0x80 + +/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */ +/* R_GPIO_OUT0 */ +#define V_GPIO_OUT0 0x01 +#define V_GPIO_OUT1 0x02 +#define V_GPIO_OUT2 0x04 +#define V_GPIO_OUT3 0x08 +#define V_GPIO_OUT4 0x10 +#define V_GPIO_OUT5 0x20 +#define V_GPIO_OUT6 0x40 +#define V_GPIO_OUT7 0x80 +/* R_GPIO_OUT1 */ +#define V_GPIO_OUT8 0x01 +#define V_GPIO_OUT9 0x02 +#define V_GPIO_OUT10 0x04 +#define V_GPIO_OUT11 0x08 +#define V_GPIO_OUT12 0x10 +#define V_GPIO_OUT13 0x20 +#define V_GPIO_OUT14 0x40 +#define V_GPIO_OUT15 0x80 +/* R_GPIO_EN0 */ +#define V_GPIO_EN0 0x01 +#define V_GPIO_EN1 0x02 +#define V_GPIO_EN2 0x04 +#define V_GPIO_EN3 0x08 +#define V_GPIO_EN4 0x10 +#define V_GPIO_EN5 0x20 +#define V_GPIO_EN6 0x40 +#define V_GPIO_EN7 0x80 +/* R_GPIO_EN1 */ +#define V_GPIO_EN8 0x01 +#define V_GPIO_EN9 0x02 +#define V_GPIO_EN10 0x04 +#define V_GPIO_EN11 0x08 +#define V_GPIO_EN12 0x10 +#define V_GPIO_EN13 0x20 +#define V_GPIO_EN14 0x40 +#define V_GPIO_EN15 0x80 +/* R_GPIO_SEL */ +#define V_GPIO_SEL0 0x01 +#define V_GPIO_SEL1 0x02 +#define V_GPIO_SEL2 0x04 +#define V_GPIO_SEL3 0x08 +#define V_GPIO_SEL4 0x10 +#define V_GPIO_SEL5 0x20 +#define V_GPIO_SEL6 0x40 +#define V_GPIO_SEL7 0x80 +/* R_GPIO_IN0 */ +#define V_GPIO_IN0 0x01 +#define V_GPIO_IN1 0x02 +#define V_GPIO_IN2 0x04 +#define V_GPIO_IN3 0x08 +#define V_GPIO_IN4 0x10 +#define V_GPIO_IN5 0x20 +#define V_GPIO_IN6 0x40 +#define V_GPIO_IN7 0x80 +/* R_GPIO_IN1 */ +#define V_GPIO_IN8 0x01 +#define V_GPIO_IN9 0x02 +#define V_GPIO_IN10 0x04 +#define V_GPIO_IN11 0x08 +#define V_GPIO_IN12 0x10 +#define V_GPIO_IN13 0x20 +#define V_GPIO_IN14 0x40 +#define V_GPIO_IN15 0x80 +/* R_GPI_IN0 */ +#define V_GPI_IN0 0x01 +#define V_GPI_IN1 0x02 +#define V_GPI_IN2 0x04 +#define V_GPI_IN3 0x08 +#define V_GPI_IN4 0x10 +#define V_GPI_IN5 0x20 +#define V_GPI_IN6 0x40 +#define V_GPI_IN7 0x80 +/* R_GPI_IN1 */ +#define V_GPI_IN8 0x01 +#define V_GPI_IN9 0x02 +#define V_GPI_IN10 0x04 +#define V_GPI_IN11 0x08 +#define V_GPI_IN12 0x10 +#define V_GPI_IN13 0x20 +#define V_GPI_IN14 0x40 +#define V_GPI_IN15 0x80 +/* R_GPI_IN2 */ +#define V_GPI_IN16 0x01 +#define V_GPI_IN17 0x02 +#define V_GPI_IN18 0x04 +#define V_GPI_IN19 0x08 +#define V_GPI_IN20 0x10 +#define V_GPI_IN21 0x20 +#define V_GPI_IN22 0x40 +#define V_GPI_IN23 0x80 +/* R_GPI_IN3 */ +#define V_GPI_IN24 0x01 +#define V_GPI_IN25 0x02 +#define V_GPI_IN26 0x04 +#define V_GPI_IN27 0x08 +#define V_GPI_IN28 0x10 +#define V_GPI_IN29 0x20 +#define V_GPI_IN30 0x40 +#define V_GPI_IN31 0x80 + +/* map of all registers, used for debugging */ + +#ifdef HFC_REGISTER_DEBUG +struct hfc_register_names { + char *name; + u_char reg; +} hfc_register_names[] = { + /* write registers */ + {"R_CIRM", 0x00}, + {"R_CTRL", 0x01}, + {"R_BRG_PCM_CFG ", 0x02}, + {"R_RAM_ADDR0", 0x08}, + {"R_RAM_ADDR1", 0x09}, + {"R_RAM_ADDR2", 0x0A}, + {"R_FIRST_FIFO", 0x0B}, + {"R_RAM_SZ", 0x0C}, + {"R_FIFO_MD", 0x0D}, + {"R_INC_RES_FIFO", 0x0E}, + {"R_FIFO / R_FSM_IDX", 0x0F}, + {"R_SLOT", 0x10}, + {"R_IRQMSK_MISC", 0x11}, + {"R_SCI_MSK", 0x12}, + {"R_IRQ_CTRL", 0x13}, + {"R_PCM_MD0", 0x14}, + {"R_0x15", 0x15}, + {"R_ST_SEL", 0x16}, + {"R_ST_SYNC", 0x17}, + {"R_CONF_EN", 0x18}, + {"R_TI_WD", 0x1A}, + {"R_BERT_WD_MD", 0x1B}, + {"R_DTMF", 0x1C}, + {"R_DTMF_N", 0x1D}, + {"R_E1_XX_STA", 0x20}, + {"R_LOS0", 0x22}, + {"R_LOS1", 0x23}, + {"R_RX0", 0x24}, + {"R_RX_FR0", 0x25}, + {"R_RX_FR1", 0x26}, + {"R_TX0", 0x28}, + {"R_TX1", 0x29}, + {"R_TX_FR0", 0x2C}, + {"R_TX_FR1", 0x2D}, + {"R_TX_FR2", 0x2E}, + {"R_JATT_ATT", 0x2F}, + {"A_ST_xx_STA/R_RX_OFF", 0x30}, + {"A_ST_CTRL0/R_SYNC_OUT", 0x31}, + {"A_ST_CTRL1", 0x32}, + {"A_ST_CTRL2", 0x33}, + {"A_ST_SQ_WR", 0x34}, + {"R_TX_OFF", 0x34}, + {"R_SYNC_CTRL", 0x35}, + {"A_ST_CLK_DLY", 0x37}, + {"R_PWM0", 0x38}, + {"R_PWM1", 0x39}, + {"A_ST_B1_TX", 0x3C}, + {"A_ST_B2_TX", 0x3D}, + {"A_ST_D_TX", 0x3E}, + {"R_GPIO_OUT0", 0x40}, + {"R_GPIO_OUT1", 0x41}, + {"R_GPIO_EN0", 0x42}, + {"R_GPIO_EN1", 0x43}, + {"R_GPIO_SEL", 0x44}, + {"R_BRG_CTRL", 0x45}, + {"R_PWM_MD", 0x46}, + {"R_BRG_MD", 0x47}, + {"R_BRG_TIM0", 0x48}, + {"R_BRG_TIM1", 0x49}, + {"R_BRG_TIM2", 0x4A}, + {"R_BRG_TIM3", 0x4B}, + {"R_BRG_TIM_SEL01", 0x4C}, + {"R_BRG_TIM_SEL23", 0x4D}, + {"R_BRG_TIM_SEL45", 0x4E}, + {"R_BRG_TIM_SEL67", 0x4F}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC", 0x84}, + {"R_RAM_DATA", 0xC0}, + {"A_SL_CFG", 0xD0}, + {"A_CONF", 0xD1}, + {"A_CH_MSK", 0xF4}, + {"A_CON_HDLC", 0xFA}, + {"A_SUBCH_CFG", 0xFB}, + {"A_CHANNEL", 0xFC}, + {"A_FIFO_SEQ", 0xFD}, + {"A_IRQ_MSK", 0xFF}, + {NULL, 0}, + + /* read registers */ + {"A_Z1", 0x04}, + {"A_Z1H", 0x05}, + {"A_Z2", 0x06}, + {"A_Z2H", 0x07}, + {"A_F1", 0x0C}, + {"A_F2", 0x0D}, + {"R_IRQ_OVIEW", 0x10}, + {"R_IRQ_MISC", 0x11}, + {"R_IRQ_STATECH", 0x12}, + {"R_CONF_OFLOW", 0x14}, + {"R_RAM_USE", 0x15}, + {"R_CHIP_ID", 0x16}, + {"R_BERT_STA", 0x17}, + {"R_F0_CNTL", 0x18}, + {"R_F0_CNTH", 0x19}, + {"R_BERT_ECL", 0x1A}, + {"R_BERT_ECH", 0x1B}, + {"R_STATUS", 0x1C}, + {"R_CHIP_RV", 0x1F}, + {"R_STATE", 0x20}, + {"R_SYNC_STA", 0x24}, + {"R_RX_SL0_0", 0x25}, + {"R_RX_SL0_1", 0x26}, + {"R_RX_SL0_2", 0x27}, + {"R_JATT_DIR", 0x2b}, + {"R_SLIP", 0x2c}, + {"A_ST_RD_STA", 0x30}, + {"R_FAS_ECL", 0x30}, + {"R_FAS_ECH", 0x31}, + {"R_VIO_ECL", 0x32}, + {"R_VIO_ECH", 0x33}, + {"R_CRC_ECL / A_ST_SQ_RD", 0x34}, + {"R_CRC_ECH", 0x35}, + {"R_E_ECL", 0x36}, + {"R_E_ECH", 0x37}, + {"R_SA6_SA13_ECL", 0x38}, + {"R_SA6_SA13_ECH", 0x39}, + {"R_SA6_SA23_ECL", 0x3A}, + {"R_SA6_SA23_ECH", 0x3B}, + {"A_ST_B1_RX", 0x3C}, + {"A_ST_B2_RX", 0x3D}, + {"A_ST_D_RX", 0x3E}, + {"A_ST_E_RX", 0x3F}, + {"R_GPIO_IN0", 0x40}, + {"R_GPIO_IN1", 0x41}, + {"R_GPI_IN0", 0x44}, + {"R_GPI_IN1", 0x45}, + {"R_GPI_IN2", 0x46}, + {"R_GPI_IN3", 0x47}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC", 0x84}, + {"R_INT_DATA", 0x88}, + {"R_RAM_DATA", 0xC0}, + {"R_IRQ_FIFO_BL0", 0xC8}, + {"R_IRQ_FIFO_BL1", 0xC9}, + {"R_IRQ_FIFO_BL2", 0xCA}, + {"R_IRQ_FIFO_BL3", 0xCB}, + {"R_IRQ_FIFO_BL4", 0xCC}, + {"R_IRQ_FIFO_BL5", 0xCD}, + {"R_IRQ_FIFO_BL6", 0xCE}, + {"R_IRQ_FIFO_BL7", 0xCF}, +}; +#endif /* HFC_REGISTER_DEBUG */ diff --git a/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h b/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h new file mode 100644 index 000000000..b0d772340 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * For License see notice in hfc_multi.c + * + * special IO and init functions for the embedded XHFC board + * from Speech Design + * + */ + +#include <asm/cpm1.h> + +/* Change this to the value used by your board */ +#ifndef IMAP_ADDR +#define IMAP_ADDR 0xFFF00000 +#endif + +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_embsd(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else + HFC_outb_embsd(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + writeb(reg, hc->xhfc_memaddr); + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + writeb(val, hc->xhfc_memdata); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_embsd(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inb_embsd(struct hfc_multi *hc, u_char reg) +#endif +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + writeb(reg, hc->xhfc_memaddr); + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + return readb(hc->xhfc_memdata); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_embsd(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inw_embsd(struct hfc_multi *hc, u_char reg) +#endif +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + writeb(reg, hc->xhfc_memaddr); + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + return readb(hc->xhfc_memdata); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_embsd(struct hfc_multi *hc, const char *function, int line) +#else + HFC_wait_embsd(struct hfc_multi *hc) +#endif +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + writeb(R_STATUS, hc->xhfc_memaddr); + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + while (readb(hc->xhfc_memdata) & V_BUSY) + cpu_relax(); +} + +/* write fifo data (EMBSD) */ +void +write_fifo_embsd(struct hfc_multi *hc, u_char *data, int len) +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + *hc->xhfc_memaddr = A_FIFO_DATA0; + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + while (len) { + *hc->xhfc_memdata = *data; + data++; + len--; + } +} + +/* read fifo data (EMBSD) */ +void +read_fifo_embsd(struct hfc_multi *hc, u_char *data, int len) +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + *hc->xhfc_memaddr = A_FIFO_DATA0; + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + while (len) { + *data = (u_char)(*hc->xhfc_memdata); + data++; + len--; + } +} + +static int +setup_embedded(struct hfc_multi *hc, struct hm_map *m) +{ + printk(KERN_INFO + "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", + m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); + + hc->pci_dev = NULL; + if (m->clock2) + test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); + + hc->leds = m->leds; + hc->ledstate = 0xAFFEAFFE; + hc->opticalsupport = m->opticalsupport; + + hc->pci_iobase = 0; + hc->pci_membase = 0; + hc->xhfc_membase = NULL; + hc->xhfc_memaddr = NULL; + hc->xhfc_memdata = NULL; + + /* set memory access methods */ + if (m->io_mode) /* use mode from card config */ + hc->io_mode = m->io_mode; + switch (hc->io_mode) { + case HFC_IO_MODE_EMBSD: + test_and_set_bit(HFC_CHIP_EMBSD, &hc->chip); + hc->slots = 128; /* required */ + /* fall through */ + hc->HFC_outb = HFC_outb_embsd; + hc->HFC_inb = HFC_inb_embsd; + hc->HFC_inw = HFC_inw_embsd; + hc->HFC_wait = HFC_wait_embsd; + hc->read_fifo = read_fifo_embsd; + hc->write_fifo = write_fifo_embsd; + hc->xhfc_origmembase = XHFC_MEMBASE + XHFC_OFFSET * hc->id; + hc->xhfc_membase = (u_char *)ioremap(hc->xhfc_origmembase, + XHFC_MEMSIZE); + if (!hc->xhfc_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap xhfc address space. " + "(internal error)\n"); + return -EIO; + } + hc->xhfc_memaddr = (u_long *)(hc->xhfc_membase + 4); + hc->xhfc_memdata = (u_long *)(hc->xhfc_membase); + printk(KERN_INFO + "HFC-multi: xhfc_membase:%#lx xhfc_origmembase:%#lx " + "xhfc_memaddr:%#lx xhfc_memdata:%#lx\n", + (u_long)hc->xhfc_membase, hc->xhfc_origmembase, + (u_long)hc->xhfc_memaddr, (u_long)hc->xhfc_memdata); + break; + default: + printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); + return -EIO; + } + + /* Prepare the MPC8XX PortA 10 as output (address/data selector) */ + hc->immap = (struct immap *)(IMAP_ADDR); + hc->immap->im_ioport.iop_papar &= ~(PA_XHFC_A0); + hc->immap->im_ioport.iop_paodr &= ~(PA_XHFC_A0); + hc->immap->im_ioport.iop_padir |= PA_XHFC_A0; + + /* Prepare the MPC8xx PortB __X__ as input (ISDN__X__IRQ) */ + hc->pb_irqmsk = (PB_XHFC_IRQ1 << hc->id); + hc->immap->im_cpm.cp_pbpar &= ~(hc->pb_irqmsk); + hc->immap->im_cpm.cp_pbodr &= ~(hc->pb_irqmsk); + hc->immap->im_cpm.cp_pbdir &= ~(hc->pb_irqmsk); + + /* At this point the needed config is done */ + /* fifos are still not enabled */ + return 0; +} diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h new file mode 100644 index 000000000..411cd1077 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfc_pci.h @@ -0,0 +1,228 @@ +/* + * specific defines for CCD's HFC 2BDS0 PCI chips + * + * Author Werner Cornelius (werner@isdn4linux.de) + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * thresholds for transparent B-channel mode + * change mask and threshold simultaneously + */ +#define HFCPCI_BTRANS_THRESHOLD 128 +#define HFCPCI_FILLEMPTY 64 +#define HFCPCI_BTRANS_THRESMASK 0x00 + +/* defines for PCI config */ +#define PCI_ENA_MEMIO 0x02 +#define PCI_ENA_MASTER 0x04 + +/* GCI/IOM bus monitor registers */ +#define HCFPCI_C_I 0x08 +#define HFCPCI_TRxR 0x0C +#define HFCPCI_MON1_D 0x28 +#define HFCPCI_MON2_D 0x2C + +/* GCI/IOM bus timeslot registers */ +#define HFCPCI_B1_SSL 0x80 +#define HFCPCI_B2_SSL 0x84 +#define HFCPCI_AUX1_SSL 0x88 +#define HFCPCI_AUX2_SSL 0x8C +#define HFCPCI_B1_RSL 0x90 +#define HFCPCI_B2_RSL 0x94 +#define HFCPCI_AUX1_RSL 0x98 +#define HFCPCI_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ +#define HFCPCI_B1_D 0xA0 +#define HFCPCI_B2_D 0xA4 +#define HFCPCI_AUX1_D 0xA8 +#define HFCPCI_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ +#define HFCPCI_MST_EMOD 0xB4 +#define HFCPCI_MST_MODE 0xB8 +#define HFCPCI_CONNECT 0xBC + + +/* Interrupt and status registers */ +#define HFCPCI_FIFO_EN 0x44 +#define HFCPCI_TRM 0x48 +#define HFCPCI_B_MODE 0x4C +#define HFCPCI_CHIP_ID 0x58 +#define HFCPCI_CIRM 0x60 +#define HFCPCI_CTMT 0x64 +#define HFCPCI_INT_M1 0x68 +#define HFCPCI_INT_M2 0x6C +#define HFCPCI_INT_S1 0x78 +#define HFCPCI_INT_S2 0x7C +#define HFCPCI_STATUS 0x70 + +/* S/T section registers */ +#define HFCPCI_STATES 0xC0 +#define HFCPCI_SCTRL 0xC4 +#define HFCPCI_SCTRL_E 0xC8 +#define HFCPCI_SCTRL_R 0xCC +#define HFCPCI_SQ 0xD0 +#define HFCPCI_CLKDEL 0xDC +#define HFCPCI_B1_REC 0xF0 +#define HFCPCI_B1_SEND 0xF0 +#define HFCPCI_B2_REC 0xF4 +#define HFCPCI_B2_SEND 0xF4 +#define HFCPCI_D_REC 0xF8 +#define HFCPCI_D_SEND 0xF8 +#define HFCPCI_E_REC 0xFC + + +/* bits in status register (READ) */ +#define HFCPCI_PCI_PROC 0x02 +#define HFCPCI_NBUSY 0x04 +#define HFCPCI_TIMER_ELAP 0x10 +#define HFCPCI_STATINT 0x20 +#define HFCPCI_FRAMEINT 0x40 +#define HFCPCI_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define HFCPCI_CLTIMER 0x80 +#define HFCPCI_TIM3_125 0x04 +#define HFCPCI_TIM25 0x10 +#define HFCPCI_TIM50 0x14 +#define HFCPCI_TIM400 0x18 +#define HFCPCI_TIM800 0x1C +#define HFCPCI_AUTO_TIMER 0x20 +#define HFCPCI_TRANSB2 0x02 +#define HFCPCI_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define HFCPCI_AUX_MSK 0x07 +#define HFCPCI_RESET 0x08 +#define HFCPCI_B1_REV 0x40 +#define HFCPCI_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define HFCPCI_INTS_B1TRANS 0x01 +#define HFCPCI_INTS_B2TRANS 0x02 +#define HFCPCI_INTS_DTRANS 0x04 +#define HFCPCI_INTS_B1REC 0x08 +#define HFCPCI_INTS_B2REC 0x10 +#define HFCPCI_INTS_DREC 0x20 +#define HFCPCI_INTS_L1STATE 0x40 +#define HFCPCI_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define HFCPCI_PROC_TRANS 0x01 +#define HFCPCI_GCI_I_CHG 0x02 +#define HFCPCI_GCI_MON_REC 0x04 +#define HFCPCI_IRQ_ENABLE 0x08 +#define HFCPCI_PMESEL 0x80 + +/* bits in STATES */ +#define HFCPCI_STATE_MSK 0x0F +#define HFCPCI_LOAD_STATE 0x10 +#define HFCPCI_ACTIVATE 0x20 +#define HFCPCI_DO_ACTION 0x40 +#define HFCPCI_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define HFCPCI_MASTER 0x01 +#define HFCPCI_SLAVE 0x00 +#define HFCPCI_F0IO_POSITIV 0x02 +#define HFCPCI_F0_NEGATIV 0x04 +#define HFCPCI_F0_2C4 0x08 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_MODE_TE 0x00 +#define SCTRL_MODE_NT 0x04 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define HFCPCI_AUTO_AWAKE 0x01 +#define HFCPCI_DBIT_1 0x04 +#define HFCPCI_IGNORE_COL 0x08 +#define HFCPCI_CHG_B1_B2 0x80 + +/* bits in FIFO_EN register */ +#define HFCPCI_FIFOEN_B1 0x03 +#define HFCPCI_FIFOEN_B2 0x0C +#define HFCPCI_FIFOEN_DTX 0x10 +#define HFCPCI_FIFOEN_B1TX 0x01 +#define HFCPCI_FIFOEN_B1RX 0x02 +#define HFCPCI_FIFOEN_B2TX 0x04 +#define HFCPCI_FIFOEN_B2RX 0x08 + + +/* definitions of fifo memory area */ +#define MAX_D_FRAMES 15 +#define MAX_B_FRAMES 31 +#define B_SUB_VAL 0x200 +#define B_FIFO_SIZE (0x2000 - B_SUB_VAL) +#define D_FIFO_SIZE 512 +#define D_FREG_MASK 0xF + +struct zt { + __le16 z1; /* Z1 pointer 16 Bit */ + __le16 z2; /* Z2 pointer 16 Bit */ +}; + +struct dfifo { + u_char data[D_FIFO_SIZE]; /* FIFO data space */ + u_char fill1[0x20A0 - D_FIFO_SIZE]; /* reserved, do not use */ + u_char f1, f2; /* f pointers */ + u_char fill2[0x20C0 - 0x20A2]; /* reserved, do not use */ + /* mask index with D_FREG_MASK for access */ + struct zt za[MAX_D_FRAMES + 1]; + u_char fill3[0x4000 - 0x2100]; /* align 16K */ +}; + +struct bzfifo { + struct zt za[MAX_B_FRAMES + 1]; /* only range 0x0..0x1F allowed */ + u_char f1, f2; /* f pointers */ + u_char fill[0x2100 - 0x2082]; /* alignment */ +}; + + +union fifo_area { + struct { + struct dfifo d_tx; /* D-send channel */ + struct dfifo d_rx; /* D-receive channel */ + } d_chan; + struct { + u_char fill1[0x200]; + u_char txdat_b1[B_FIFO_SIZE]; + struct bzfifo txbz_b1; + struct bzfifo txbz_b2; + u_char txdat_b2[B_FIFO_SIZE]; + u_char fill2[D_FIFO_SIZE]; + u_char rxdat_b1[B_FIFO_SIZE]; + struct bzfifo rxbz_b1; + struct bzfifo rxbz_b2; + u_char rxdat_b2[B_FIFO_SIZE]; + } b_chans; + u_char fill[32768]; +}; + +#define Write_hfc(a, b, c) (writeb(c, (a->hw.pci_io) + b)) +#define Read_hfc(a, b) (readb((a->hw.pci_io) + b)) diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c new file mode 100644 index 000000000..0928fd1f0 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -0,0 +1,5581 @@ +/* + * hfcmulti.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards + * + * Author Andreas Eversberg (jolly@eversberg.eu) + * ported to mqueue mechanism: + * Peter Sprenger (sprengermoving-bytes.de) + * + * inspired by existing hfc-pci driver: + * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) + * Copyright 2008 by Karsten Keil (kkeil@suse.de) + * Copyright 2008 by Andreas Eversberg (jolly@eversberg.eu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Thanks to Cologne Chip AG for this great controller! + */ + +/* + * module parameters: + * type: + * By default (0), the card is automatically detected. + * Or use the following combinations: + * Bit 0-7 = 0x00001 = HFC-E1 (1 port) + * or Bit 0-7 = 0x00004 = HFC-4S (4 ports) + * or Bit 0-7 = 0x00008 = HFC-8S (8 ports) + * Bit 8 = 0x00100 = uLaw (instead of aLaw) + * Bit 9 = 0x00200 = Disable DTMF detect on all B-channels via hardware + * Bit 10 = spare + * Bit 11 = 0x00800 = Force PCM bus into slave mode. (otherwhise auto) + * or Bit 12 = 0x01000 = Force PCM bus into master mode. (otherwhise auto) + * Bit 13 = spare + * Bit 14 = 0x04000 = Use external ram (128K) + * Bit 15 = 0x08000 = Use external ram (512K) + * Bit 16 = 0x10000 = Use 64 timeslots instead of 32 + * or Bit 17 = 0x20000 = Use 128 timeslots instead of anything else + * Bit 18 = spare + * Bit 19 = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog) + * (all other bits are reserved and shall be 0) + * example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM + * bus (PCM master) + * + * port: (optional or required for all ports on all installed cards) + * HFC-4S/HFC-8S only bits: + * Bit 0 = 0x001 = Use master clock for this S/T interface + * (ony once per chip). + * Bit 1 = 0x002 = transmitter line setup (non capacitive mode) + * Don't use this unless you know what you are doing! + * Bit 2 = 0x004 = Disable E-channel. (No E-channel processing) + * example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock + * received from port 1 + * + * HFC-E1 only bits: + * Bit 0 = 0x0001 = interface: 0=copper, 1=optical + * Bit 1 = 0x0002 = reserved (later for 32 B-channels transparent mode) + * Bit 2 = 0x0004 = Report LOS + * Bit 3 = 0x0008 = Report AIS + * Bit 4 = 0x0010 = Report SLIP + * Bit 5 = 0x0020 = Report RDI + * Bit 8 = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame + * mode instead. + * Bit 9 = 0x0200 = Force get clock from interface, even in NT mode. + * or Bit 10 = 0x0400 = Force put clock to interface, even in TE mode. + * Bit 11 = 0x0800 = Use direct RX clock for PCM sync rather than PLL. + * (E1 only) + * Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0 + * for default. + * (all other bits are reserved and shall be 0) + * + * debug: + * NOTE: only one debug value must be given for all cards + * enable debugging (see hfc_multi.h for debug options) + * + * poll: + * NOTE: only one poll value must be given for all cards + * Give the number of samples for each fifo process. + * By default 128 is used. Decrease to reduce delay, increase to + * reduce cpu load. If unsure, don't mess with it! + * Valid is 8, 16, 32, 64, 128, 256. + * + * pcm: + * NOTE: only one pcm value must be given for every card. + * The PCM bus id tells the mISDNdsp module about the connected PCM bus. + * By default (0), the PCM bus id is 100 for the card that is PCM master. + * If multiple cards are PCM master (because they are not interconnected), + * each card with PCM master will have increasing PCM id. + * All PCM busses with the same ID are expected to be connected and have + * common time slots slots. + * Only one chip of the PCM bus must be master, the others slave. + * -1 means no support of PCM bus not even. + * Omit this value, if all cards are interconnected or none is connected. + * If unsure, don't give this parameter. + * + * dmask and bmask: + * NOTE: One dmask value must be given for every HFC-E1 card. + * If omitted, the E1 card has D-channel on time slot 16, which is default. + * dmask is a 32 bit mask. The bit must be set for an alternate time slot. + * If multiple bits are set, multiple virtual card fragments are created. + * For each bit set, a bmask value must be given. Each bit on the bmask + * value stands for a B-channel. The bmask may not overlap with dmask or + * with other bmask values for that card. + * Example: dmask=0x00020002 bmask=0x0000fffc,0xfffc0000 + * This will create one fragment with D-channel on slot 1 with + * B-channels on slots 2..15, and a second fragment with D-channel + * on slot 17 with B-channels on slot 18..31. Slot 16 is unused. + * If bit 0 is set (dmask=0x00000001) the D-channel is on slot 0 and will + * not function. + * Example: dmask=0x00000001 bmask=0xfffffffe + * This will create a port with all 31 usable timeslots as + * B-channels. + * If no bits are set on bmask, no B-channel is created for that fragment. + * Example: dmask=0xfffffffe bmask=0,0,0,0.... (31 0-values for bmask) + * This will create 31 ports with one D-channel only. + * If you don't know how to use it, you don't need it! + * + * iomode: + * NOTE: only one mode value must be given for every card. + * -> See hfc_multi.h for HFC_IO_MODE_* values + * By default, the IO mode is pci memory IO (MEMIO). + * Some cards require specific IO mode, so it cannot be changed. + * It may be useful to set IO mode to register io (REGIO) to solve + * PCI bridge problems. + * If unsure, don't give this parameter. + * + * clockdelay_nt: + * NOTE: only one clockdelay_nt value must be given once for all cards. + * Give the value of the clock control register (A_ST_CLK_DLY) + * of the S/T interfaces in NT mode. + * This register is needed for the TBR3 certification, so don't change it. + * + * clockdelay_te: + * NOTE: only one clockdelay_te value must be given once + * Give the value of the clock control register (A_ST_CLK_DLY) + * of the S/T interfaces in TE mode. + * This register is needed for the TBR3 certification, so don't change it. + * + * clock: + * NOTE: only one clock value must be given once + * Selects interface with clock source for mISDN and applications. + * Set to card number starting with 1. Set to -1 to disable. + * By default, the first card is used as clock source. + * + * hwid: + * NOTE: only one hwid value must be given once + * Enable special embedded devices with XHFC controllers. + */ + +/* + * debug register access (never use this, it will flood your system log) + * #define HFC_REGISTER_DEBUG + */ + +#define HFC_MULTI_VERSION "2.03" + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/mISDNdsp.h> + +/* + #define IRQCOUNT_DEBUG + #define IRQ_DEBUG +*/ + +#include "hfc_multi.h" +#ifdef ECHOPREP +#include "gaintab.h" +#endif + +#define MAX_CARDS 8 +#define MAX_PORTS (8 * MAX_CARDS) +#define MAX_FRAGS (32 * MAX_CARDS) + +static LIST_HEAD(HFClist); +static spinlock_t HFClock; /* global hfc list lock */ + +static void ph_state_change(struct dchannel *); + +static struct hfc_multi *syncmaster; +static int plxsd_master; /* if we have a master card (yet) */ +static spinlock_t plx_lock; /* may not acquire other lock inside */ + +#define TYP_E1 1 +#define TYP_4S 4 +#define TYP_8S 8 + +static int poll_timer = 6; /* default = 128 samples = 16ms */ +/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */ +static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode + (0x60 MUST be included!) */ + +#define DIP_4S 0x1 /* DIP Switches for Beronet 1S/2S/4S cards */ +#define DIP_8S 0x2 /* DIP Switches for Beronet 8S+ cards */ +#define DIP_E1 0x3 /* DIP Switches for Beronet E1 cards */ + +/* + * module stuff + */ + +static uint type[MAX_CARDS]; +static int pcm[MAX_CARDS]; +static uint dmask[MAX_CARDS]; +static uint bmask[MAX_FRAGS]; +static uint iomode[MAX_CARDS]; +static uint port[MAX_PORTS]; +static uint debug; +static uint poll; +static int clock; +static uint timer; +static uint clockdelay_te = CLKDEL_TE; +static uint clockdelay_nt = CLKDEL_NT; +#define HWID_NONE 0 +#define HWID_MINIP4 1 +#define HWID_MINIP8 2 +#define HWID_MINIP16 3 +static uint hwid = HWID_NONE; + +static int HFC_cnt, E1_cnt, bmask_cnt, Port_cnt, PCM_cnt = 99; + +MODULE_AUTHOR("Andreas Eversberg"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(HFC_MULTI_VERSION); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(clock, int, S_IRUGO | S_IWUSR); +module_param(timer, uint, S_IRUGO | S_IWUSR); +module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR); +module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR); +module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(pcm, int, NULL, S_IRUGO | S_IWUSR); +module_param_array(dmask, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(bmask, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); +module_param(hwid, uint, S_IRUGO | S_IWUSR); /* The hardware ID */ + +#ifdef HFC_REGISTER_DEBUG +#define HFC_outb(hc, reg, val) \ + (hc->HFC_outb(hc, reg, val, __func__, __LINE__)) +#define HFC_outb_nodebug(hc, reg, val) \ + (hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__)) +#define HFC_inb(hc, reg) \ + (hc->HFC_inb(hc, reg, __func__, __LINE__)) +#define HFC_inb_nodebug(hc, reg) \ + (hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__)) +#define HFC_inw(hc, reg) \ + (hc->HFC_inw(hc, reg, __func__, __LINE__)) +#define HFC_inw_nodebug(hc, reg) \ + (hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__)) +#define HFC_wait(hc) \ + (hc->HFC_wait(hc, __func__, __LINE__)) +#define HFC_wait_nodebug(hc) \ + (hc->HFC_wait_nodebug(hc, __func__, __LINE__)) +#else +#define HFC_outb(hc, reg, val) (hc->HFC_outb(hc, reg, val)) +#define HFC_outb_nodebug(hc, reg, val) (hc->HFC_outb_nodebug(hc, reg, val)) +#define HFC_inb(hc, reg) (hc->HFC_inb(hc, reg)) +#define HFC_inb_nodebug(hc, reg) (hc->HFC_inb_nodebug(hc, reg)) +#define HFC_inw(hc, reg) (hc->HFC_inw(hc, reg)) +#define HFC_inw_nodebug(hc, reg) (hc->HFC_inw_nodebug(hc, reg)) +#define HFC_wait(hc) (hc->HFC_wait(hc)) +#define HFC_wait_nodebug(hc) (hc->HFC_wait_nodebug(hc)) +#endif + +#ifdef CONFIG_MISDN_HFCMULTI_8xx +#include "hfc_multi_8xx.h" +#endif + +/* HFC_IO_MODE_PCIMEM */ +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else + HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + writeb(val, hc->pci_membase + reg); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inb_pcimem(struct hfc_multi *hc, u_char reg) +#endif +{ + return readb(hc->pci_membase + reg); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inw_pcimem(struct hfc_multi *hc, u_char reg) +#endif +{ + return readw(hc->pci_membase + reg); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line) +#else + HFC_wait_pcimem(struct hfc_multi *hc) +#endif +{ + while (readb(hc->pci_membase + R_STATUS) & V_BUSY) + cpu_relax(); +} + +/* HFC_IO_MODE_REGIO */ +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else + HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + outb(reg, hc->pci_iobase + 4); + outb(val, hc->pci_iobase); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inb_regio(struct hfc_multi *hc, u_char reg) +#endif +{ + outb(reg, hc->pci_iobase + 4); + return inb(hc->pci_iobase); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inw_regio(struct hfc_multi *hc, u_char reg) +#endif +{ + outb(reg, hc->pci_iobase + 4); + return inw(hc->pci_iobase); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_regio(struct hfc_multi *hc, const char *function, int line) +#else + HFC_wait_regio(struct hfc_multi *hc) +#endif +{ + outb(R_STATUS, hc->pci_iobase + 4); + while (inb(hc->pci_iobase) & V_BUSY) + cpu_relax(); +} + +#ifdef HFC_REGISTER_DEBUG +static void +HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +{ + char regname[256] = "", bits[9] = "xxxxxxxx"; + int i; + + i = -1; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0' + (!!(val & 1)); + bits[6] = '0' + (!!(val & 2)); + bits[5] = '0' + (!!(val & 4)); + bits[4] = '0' + (!!(val & 8)); + bits[3] = '0' + (!!(val & 16)); + bits[2] = '0' + (!!(val & 32)); + bits[1] = '0' + (!!(val & 64)); + bits[0] = '0' + (!!(val & 128)); + printk(KERN_DEBUG + "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n", + hc->id, reg, regname, val, bits, function, line); + HFC_outb_nodebug(hc, reg, val); +} +static u_char +HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) +{ + char regname[256] = "", bits[9] = "xxxxxxxx"; + u_char val = HFC_inb_nodebug(hc, reg); + int i; + + i = 0; + while (hfc_register_names[i++].name) + ; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0' + (!!(val & 1)); + bits[6] = '0' + (!!(val & 2)); + bits[5] = '0' + (!!(val & 4)); + bits[4] = '0' + (!!(val & 8)); + bits[3] = '0' + (!!(val & 16)); + bits[2] = '0' + (!!(val & 32)); + bits[1] = '0' + (!!(val & 64)); + bits[0] = '0' + (!!(val & 128)); + printk(KERN_DEBUG + "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n", + hc->id, reg, regname, val, bits, function, line); + return val; +} +static u_short +HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) +{ + char regname[256] = ""; + u_short val = HFC_inw_nodebug(hc, reg); + int i; + + i = 0; + while (hfc_register_names[i++].name) + ; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + printk(KERN_DEBUG + "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n", + hc->id, reg, regname, val, function, line); + return val; +} +static void +HFC_wait_debug(struct hfc_multi *hc, const char *function, int line) +{ + printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n", + hc->id, function, line); + HFC_wait_nodebug(hc); +} +#endif + +/* write fifo data (REGIO) */ +static void +write_fifo_regio(struct hfc_multi *hc, u_char *data, int len) +{ + outb(A_FIFO_DATA0, (hc->pci_iobase) + 4); + while (len >> 2) { + outl(cpu_to_le32(*(u32 *)data), hc->pci_iobase); + data += 4; + len -= 4; + } + while (len >> 1) { + outw(cpu_to_le16(*(u16 *)data), hc->pci_iobase); + data += 2; + len -= 2; + } + while (len) { + outb(*data, hc->pci_iobase); + data++; + len--; + } +} +/* write fifo data (PCIMEM) */ +static void +write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) +{ + while (len >> 2) { + writel(cpu_to_le32(*(u32 *)data), + hc->pci_membase + A_FIFO_DATA0); + data += 4; + len -= 4; + } + while (len >> 1) { + writew(cpu_to_le16(*(u16 *)data), + hc->pci_membase + A_FIFO_DATA0); + data += 2; + len -= 2; + } + while (len) { + writeb(*data, hc->pci_membase + A_FIFO_DATA0); + data++; + len--; + } +} + +/* read fifo data (REGIO) */ +static void +read_fifo_regio(struct hfc_multi *hc, u_char *data, int len) +{ + outb(A_FIFO_DATA0, (hc->pci_iobase) + 4); + while (len >> 2) { + *(u32 *)data = le32_to_cpu(inl(hc->pci_iobase)); + data += 4; + len -= 4; + } + while (len >> 1) { + *(u16 *)data = le16_to_cpu(inw(hc->pci_iobase)); + data += 2; + len -= 2; + } + while (len) { + *data = inb(hc->pci_iobase); + data++; + len--; + } +} + +/* read fifo data (PCIMEM) */ +static void +read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) +{ + while (len >> 2) { + *(u32 *)data = + le32_to_cpu(readl(hc->pci_membase + A_FIFO_DATA0)); + data += 4; + len -= 4; + } + while (len >> 1) { + *(u16 *)data = + le16_to_cpu(readw(hc->pci_membase + A_FIFO_DATA0)); + data += 2; + len -= 2; + } + while (len) { + *data = readb(hc->pci_membase + A_FIFO_DATA0); + data++; + len--; + } +} + +static void +enable_hwirq(struct hfc_multi *hc) +{ + hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN; + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); +} + +static void +disable_hwirq(struct hfc_multi *hc) +{ + hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN); + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); +} + +#define NUM_EC 2 +#define MAX_TDM_CHAN 32 + + +static inline void +enablepcibridge(struct hfc_multi *c) +{ + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */ +} + +static inline void +disablepcibridge(struct hfc_multi *c) +{ + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */ +} + +static inline unsigned char +readpcibridge(struct hfc_multi *hc, unsigned char address) +{ + unsigned short cipv; + unsigned char data; + + if (!hc->pci_iobase) + return 0; + + /* slow down a PCI read access by 1 PCI clock cycle */ + HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/ + + if (address == 0) + cipv = 0x4000; + else + cipv = 0x5800; + + /* select local bridge port address by writing to CIP port */ + /* data = HFC_inb(c, cipv); * was _io before */ + outw(cipv, hc->pci_iobase + 4); + data = inb(hc->pci_iobase); + + /* restore R_CTRL for normal PCI read cycle speed */ + HFC_outb(hc, R_CTRL, 0x0); /* was _io before */ + + return data; +} + +static inline void +writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data) +{ + unsigned short cipv; + unsigned int datav; + + if (!hc->pci_iobase) + return; + + if (address == 0) + cipv = 0x4000; + else + cipv = 0x5800; + + /* select local bridge port address by writing to CIP port */ + outw(cipv, hc->pci_iobase + 4); + /* define a 32 bit dword with 4 identical bytes for write sequence */ + datav = data | ((__u32) data << 8) | ((__u32) data << 16) | + ((__u32) data << 24); + + /* + * write this 32 bit dword to the bridge data port + * this will initiate a write sequence of up to 4 writes to the same + * address on the local bus interface the number of write accesses + * is undefined but >=1 and depends on the next PCI transaction + * during write sequence on the local bus + */ + outl(datav, hc->pci_iobase); +} + +static inline void +cpld_set_reg(struct hfc_multi *hc, unsigned char reg) +{ + /* Do data pin read low byte */ + HFC_outb(hc, R_GPIO_OUT1, reg); +} + +static inline void +cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val) +{ + cpld_set_reg(hc, reg); + + enablepcibridge(hc); + writepcibridge(hc, 1, val); + disablepcibridge(hc); + + return; +} + +static inline unsigned char +cpld_read_reg(struct hfc_multi *hc, unsigned char reg) +{ + unsigned char bytein; + + cpld_set_reg(hc, reg); + + /* Do data pin read low byte */ + HFC_outb(hc, R_GPIO_OUT1, reg); + + enablepcibridge(hc); + bytein = readpcibridge(hc, 1); + disablepcibridge(hc); + + return bytein; +} + +static inline void +vpm_write_address(struct hfc_multi *hc, unsigned short addr) +{ + cpld_write_reg(hc, 0, 0xff & addr); + cpld_write_reg(hc, 1, 0x01 & (addr >> 8)); +} + +static inline unsigned short +vpm_read_address(struct hfc_multi *c) +{ + unsigned short addr; + unsigned short highbit; + + addr = cpld_read_reg(c, 0); + highbit = cpld_read_reg(c, 1); + + addr = addr | (highbit << 8); + + return addr & 0x1ff; +} + +static inline unsigned char +vpm_in(struct hfc_multi *c, int which, unsigned short addr) +{ + unsigned char res; + + vpm_write_address(c, addr); + + if (!which) + cpld_set_reg(c, 2); + else + cpld_set_reg(c, 3); + + enablepcibridge(c); + res = readpcibridge(c, 1); + disablepcibridge(c); + + cpld_set_reg(c, 0); + + return res; +} + +static inline void +vpm_out(struct hfc_multi *c, int which, unsigned short addr, + unsigned char data) +{ + vpm_write_address(c, addr); + + enablepcibridge(c); + + if (!which) + cpld_set_reg(c, 2); + else + cpld_set_reg(c, 3); + + writepcibridge(c, 1, data); + + cpld_set_reg(c, 0); + + disablepcibridge(c); + + { + unsigned char regin; + regin = vpm_in(c, which, addr); + if (regin != data) + printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back " + "0x%x\n", data, addr, regin); + } + +} + + +static void +vpm_init(struct hfc_multi *wc) +{ + unsigned char reg; + unsigned int mask; + unsigned int i, x, y; + unsigned int ver; + + for (x = 0; x < NUM_EC; x++) { + /* Setup GPIO's */ + if (!x) { + ver = vpm_in(wc, x, 0x1a0); + printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver); + } + + for (y = 0; y < 4; y++) { + vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ + vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ + vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ + } + + /* Setup TDM path - sets fsync and tdm_clk as inputs */ + reg = vpm_in(wc, x, 0x1a3); /* misc_con */ + vpm_out(wc, x, 0x1a3, reg & ~2); + + /* Setup Echo length (256 taps) */ + vpm_out(wc, x, 0x022, 1); + vpm_out(wc, x, 0x023, 0xff); + + /* Setup timeslots */ + vpm_out(wc, x, 0x02f, 0x00); + mask = 0x02020202 << (x * 4); + + /* Setup the tdm channel masks for all chips */ + for (i = 0; i < 4; i++) + vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff); + + /* Setup convergence rate */ + printk(KERN_DEBUG "VPM: A-law mode\n"); + reg = 0x00 | 0x10 | 0x01; + vpm_out(wc, x, 0x20, reg); + printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg); + /*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */ + + vpm_out(wc, x, 0x24, 0x02); + reg = vpm_in(wc, x, 0x24); + printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg); + + /* Initialize echo cans */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, i, 0x00); + } + + /* + * ARM arch at least disallows a udelay of + * more than 2ms... it gives a fake "__bad_udelay" + * reference at link-time. + * long delays in kernel code are pretty sucky anyway + * for now work around it using 5 x 2ms instead of 1 x 10ms + */ + + udelay(2000); + udelay(2000); + udelay(2000); + udelay(2000); + udelay(2000); + + /* Put in bypass mode */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, i, 0x01); + } + + /* Enable bypass */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, 0x78 + i, 0x01); + } + + } +} + +#ifdef UNUSED +static void +vpm_check(struct hfc_multi *hctmp) +{ + unsigned char gpi2; + + gpi2 = HFC_inb(hctmp, R_GPI_IN2); + + if ((gpi2 & 0x3) != 0x3) + printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2); +} +#endif /* UNUSED */ + + +/* + * Interface to enable/disable the HW Echocan + * + * these functions are called within a spin_lock_irqsave on + * the channel instance lock, so we are not disturbed by irqs + * + * we can later easily change the interface to make other + * things configurable, for now we configure the taps + * + */ + +static void +vpm_echocan_on(struct hfc_multi *hc, int ch, int taps) +{ + unsigned int timeslot; + unsigned int unit; + struct bchannel *bch = hc->chan[ch].bch; +#ifdef TXADJ + int txadj = -4; + struct sk_buff *skb; +#endif + if (hc->chan[ch].protocol != ISDN_P_B_RAW) + return; + + if (!bch) + return; + +#ifdef TXADJ + skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, + sizeof(int), &txadj, GFP_ATOMIC); + if (skb) + recv_Bchannel_skb(bch, skb); +#endif + + timeslot = ((ch / 4) * 8) + ((ch % 4) * 4) + 1; + unit = ch % 4; + + printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n", + taps, timeslot); + + vpm_out(hc, unit, timeslot, 0x7e); +} + +static void +vpm_echocan_off(struct hfc_multi *hc, int ch) +{ + unsigned int timeslot; + unsigned int unit; + struct bchannel *bch = hc->chan[ch].bch; +#ifdef TXADJ + int txadj = 0; + struct sk_buff *skb; +#endif + + if (hc->chan[ch].protocol != ISDN_P_B_RAW) + return; + + if (!bch) + return; + +#ifdef TXADJ + skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, + sizeof(int), &txadj, GFP_ATOMIC); + if (skb) + recv_Bchannel_skb(bch, skb); +#endif + + timeslot = ((ch / 4) * 8) + ((ch % 4) * 4) + 1; + unit = ch % 4; + + printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n", + timeslot); + /* FILLME */ + vpm_out(hc, unit, timeslot, 0x01); +} + + +/* + * Speech Design resync feature + * NOTE: This is called sometimes outside interrupt handler. + * We must lock irqsave, so no other interrupt (other card) will occur! + * Also multiple interrupts may nest, so must lock each access (lists, card)! + */ +static inline void +hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm) +{ + struct hfc_multi *hc, *next, *pcmmaster = NULL; + void __iomem *plx_acc_32; + u_int pv; + u_long flags; + + spin_lock_irqsave(&HFClock, flags); + spin_lock(&plx_lock); /* must be locked inside other locks */ + + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n", + __func__, syncmaster); + + /* select new master */ + if (newmaster) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "using provided controller\n"); + } else { + list_for_each_entry_safe(hc, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (hc->syncronized) { + newmaster = hc; + break; + } + } + } + } + + /* Disable sync of all cards */ + list_for_each_entry_safe(hc, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv &= ~PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { + pcmmaster = hc; + if (hc->ctype == HFC_TYPE_E1) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Schedule SYNC_I\n"); + hc->e1_resync |= 1; /* get SYNC_I */ + } + } + } + } + + if (newmaster) { + hc = newmaster; + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "id=%d (0x%p) = syncronized with " + "interface.\n", hc->id, hc); + /* Enable new sync master */ + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + /* switch to jatt PLL, if not disabled by RX_SYNC */ + if (hc->ctype == HFC_TYPE_E1 + && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Schedule jatt PLL\n"); + hc->e1_resync |= 2; /* switch to jatt */ + } + } else { + if (pcmmaster) { + hc = pcmmaster; + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "id=%d (0x%p) = PCM master syncronized " + "with QUARTZ\n", hc->id, hc); + if (hc->ctype == HFC_TYPE_E1) { + /* Use the crystal clock for the PCM + master card */ + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Schedule QUARTZ for HFC-E1\n"); + hc->e1_resync |= 4; /* switch quartz */ + } else { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "QUARTZ is automatically " + "enabled by HFC-%dS\n", hc->ctype); + } + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + } else + if (!rm) + printk(KERN_ERR "%s no pcm master, this MUST " + "not happen!\n", __func__); + } + syncmaster = newmaster; + + spin_unlock(&plx_lock); + spin_unlock_irqrestore(&HFClock, flags); +} + +/* This must be called AND hc must be locked irqsave!!! */ +static inline void +plxsd_checksync(struct hfc_multi *hc, int rm) +{ + if (hc->syncronized) { + if (syncmaster == NULL) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: GOT sync on card %d" + " (id=%d)\n", __func__, hc->id + 1, + hc->id); + hfcmulti_resync(hc, hc, rm); + } + } else { + if (syncmaster == hc) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: LOST sync on card %d" + " (id=%d)\n", __func__, hc->id + 1, + hc->id); + hfcmulti_resync(hc, NULL, rm); + } + } +} + + +/* + * free hardware resources used by driver + */ +static void +release_io_hfcmulti(struct hfc_multi *hc) +{ + void __iomem *plx_acc_32; + u_int pv; + u_long plx_flags; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + /* soft reset also masks all interrupts */ + hc->hw.r_cirm |= V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(1000); + hc->hw.r_cirm &= ~V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(1000); /* instead of 'wait' that may cause locking */ + + /* release Speech Design card, if PLX was initialized */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: release PLXSD card %d\n", + __func__, hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + writel(PLX_GPIOC_INIT, plx_acc_32); + pv = readl(plx_acc_32); + /* Termination off */ + pv &= ~PLX_TERM_ON; + /* Disconnect the PCM */ + pv |= PLX_SLAVE_EN_N; + pv &= ~PLX_MASTER_EN; + pv &= ~PLX_SYNC_O_EN; + /* Put the DSP in Reset */ + pv &= ~PLX_DSP_RES_N; + writel(pv, plx_acc_32); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PCM off: PLX_GPIO=%x\n", + __func__, pv); + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + /* disable memory mapped ports / io ports */ + test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */ + if (hc->pci_dev) + pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0); + if (hc->pci_membase) + iounmap(hc->pci_membase); + if (hc->plx_membase) + iounmap(hc->plx_membase); + if (hc->pci_iobase) + release_region(hc->pci_iobase, 8); + if (hc->xhfc_membase) + iounmap((void *)hc->xhfc_membase); + + if (hc->pci_dev) { + pci_disable_device(hc->pci_dev); + pci_set_drvdata(hc->pci_dev, NULL); + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); +} + +/* + * function called to reset the HFC chip. A complete software reset of chip + * and fifos is done. All configuration of the chip is done. + */ + +static int +init_chip(struct hfc_multi *hc) +{ + u_long flags, val, val2 = 0, rev; + int i, err = 0; + u_char r_conf_en, rval; + void __iomem *plx_acc_32; + u_int pv; + u_long plx_flags, hfc_flags; + int plx_count; + struct hfc_multi *pos, *next, *plx_last_hc; + + spin_lock_irqsave(&hc->lock, flags); + /* reset all registers */ + memset(&hc->hw, 0, sizeof(struct hfcm_hw)); + + /* revision check */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + val = HFC_inb(hc, R_CHIP_ID); + if ((val >> 4) != 0x8 && (val >> 4) != 0xc && (val >> 4) != 0xe && + (val >> 1) != 0x31) { + printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val); + err = -EIO; + goto out; + } + rev = HFC_inb(hc, R_CHIP_RV); + printk(KERN_INFO + "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n", + val, rev, (rev == 0 && (hc->ctype != HFC_TYPE_XHFC)) ? + " (old FIFO handling)" : ""); + if (hc->ctype != HFC_TYPE_XHFC && rev == 0) { + test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip); + printk(KERN_WARNING + "HFC_multi: NOTE: Your chip is revision 0, " + "ask Cologne Chip for update. Newer chips " + "have a better FIFO handling. Old chips " + "still work but may have slightly lower " + "HDLC transmit performance.\n"); + } + if (rev > 1) { + printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't " + "consider chip revision = %ld. The chip / " + "bridge may not work.\n", rev); + } + + /* set s-ram size */ + hc->Flen = 0x10; + hc->Zmin = 0x80; + hc->Zlen = 384; + hc->DTMFbase = 0x1000; + if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 128K external RAM\n", + __func__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 1; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 1856; + hc->DTMFbase = 0x2000; + } + if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 512K external RAM\n", + __func__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 2; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 8000; + hc->DTMFbase = 0x2000; + } + if (hc->ctype == HFC_TYPE_XHFC) { + hc->Flen = 0x8; + hc->Zmin = 0x0; + hc->Zlen = 64; + hc->DTMFbase = 0x0; + } + hc->max_trans = poll << 1; + if (hc->max_trans > hc->Zlen) + hc->max_trans = hc->Zlen; + + /* Speech Design PLX bridge */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: initializing PLXSD card %d\n", + __func__, hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + writel(PLX_GPIOC_INIT, plx_acc_32); + pv = readl(plx_acc_32); + /* The first and the last cards are terminating the PCM bus */ + pv |= PLX_TERM_ON; /* hc is currently the last */ + /* Disconnect the PCM */ + pv |= PLX_SLAVE_EN_N; + pv &= ~PLX_MASTER_EN; + pv &= ~PLX_SYNC_O_EN; + /* Put the DSP in Reset */ + pv &= ~PLX_DSP_RES_N; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: slave/term: PLX_GPIO=%x\n", + __func__, pv); + /* + * If we are the 3rd PLXSD card or higher, we must turn + * termination of last PLXSD card off. + */ + spin_lock_irqsave(&HFClock, hfc_flags); + plx_count = 0; + plx_last_hc = NULL; + list_for_each_entry_safe(pos, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) { + plx_count++; + if (pos != hc) + plx_last_hc = pos; + } + } + if (plx_count >= 3) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: card %d is between, so " + "we disable termination\n", + __func__, plx_last_hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = plx_last_hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv &= ~PLX_TERM_ON; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: term off: PLX_GPIO=%x\n", + __func__, pv); + } + spin_unlock_irqrestore(&HFClock, hfc_flags); + hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ + } + + if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) + hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ + + /* we only want the real Z2 read-pointer for revision > 0 */ + if (!test_bit(HFC_CHIP_REVISION0, &hc->chip)) + hc->hw.r_ram_sz |= V_FZ_MD; + + /* select pcm mode */ + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into slave mode\n", + __func__); + } else + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into master mode\n", + __func__); + hc->hw.r_pcm_md0 |= V_PCM_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: performing PCM auto detect\n", + __func__); + } + + /* soft reset */ + HFC_outb(hc, R_CTRL, hc->hw.r_ctrl); + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, 0x0C /* R_FIFO_THRES */, + 0x11 /* 16 Bytes TX/RX */); + else + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + HFC_outb(hc, R_FIFO_MD, 0); + if (hc->ctype == HFC_TYPE_XHFC) + hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES; + else + hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES + | V_RLD_EPR; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(100); + hc->hw.r_cirm = 0; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(100); + if (hc->ctype != HFC_TYPE_XHFC) + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + + /* Speech Design PLX bridge pcm and sync mode */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + /* Connect PCM */ + if (hc->hw.r_pcm_md0 & V_PCM_MD) { + pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; + pv |= PLX_SYNC_O_EN; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: master: PLX_GPIO=%x\n", + __func__, pv); + } else { + pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N); + pv &= ~PLX_SYNC_O_EN; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: slave: PLX_GPIO=%x\n", + __func__, pv); + } + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + /* PCM setup */ + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90); + if (hc->slots == 32) + HFC_outb(hc, R_PCM_MD1, 0x00); + if (hc->slots == 64) + HFC_outb(hc, R_PCM_MD1, 0x10); + if (hc->slots == 128) + HFC_outb(hc, R_PCM_MD1, 0x20); + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */ + else if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) + HFC_outb(hc, R_PCM_MD2, 0x10); /* V_C2O_EN */ + else + HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */ + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_SLOT, i); + HFC_outb_nodebug(hc, A_SL_CFG, 0); + if (hc->ctype != HFC_TYPE_XHFC) + HFC_outb_nodebug(hc, A_CONF, 0); + hc->slot_owner[i] = -1; + } + + /* set clock speed */ + if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: setting double clock\n", __func__); + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + } + + if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) + HFC_outb(hc, 0x02 /* R_CLK_CFG */, 0x40 /* V_CLKO_OFF */); + + /* B410P GPIO */ + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + printk(KERN_NOTICE "Setting GPIOs\n"); + HFC_outb(hc, R_GPIO_SEL, 0x30); + HFC_outb(hc, R_GPIO_EN1, 0x3); + udelay(1000); + printk(KERN_NOTICE "calling vpm_init\n"); + vpm_init(hc); + } + + /* check if R_F0_CNT counts (8 kHz frame count) */ + val = HFC_inb(hc, R_F0_CNTL); + val += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "HFC_multi F0_CNT %ld after reset\n", val); + spin_unlock_irqrestore(&hc->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ / 100) ? : 1); /* Timeout minimum 10ms */ + spin_lock_irqsave(&hc->lock, flags); + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "HFC_multi F0_CNT %ld after 10 ms (1st try)\n", + val2); + if (val2 >= val + 8) { /* 1 ms */ + /* it counts, so we keep the pcm mode */ + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) + printk(KERN_INFO "controller is PCM bus MASTER\n"); + else + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) + printk(KERN_INFO "controller is PCM bus SLAVE\n"); + else { + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + printk(KERN_INFO "controller is PCM bus SLAVE " + "(auto detected)\n"); + } + } else { + /* does not count */ + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { + controller_fail: + printk(KERN_ERR "HFC_multi ERROR, getting no 125us " + "pulse. Seems that controller fails.\n"); + err = -EIO; + goto out; + } + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_INFO "controller is PCM bus SLAVE " + "(ignoring missing PCM clock)\n"); + } else { + /* only one pcm master */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && plxsd_master) { + printk(KERN_ERR "HFC_multi ERROR, no clock " + "on another Speech Design card found. " + "Please be sure to connect PCM cable.\n"); + err = -EIO; + goto out; + } + /* retry with master clock */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: master: " + "PLX_GPIO=%x\n", __func__, pv); + } + hc->hw.r_pcm_md0 |= V_PCM_MD; + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); + spin_unlock_irqrestore(&hc->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ / 100) ?: 1); /* Timeout min. 10ms */ + spin_lock_irqsave(&hc->lock, flags); + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "HFC_multi F0_CNT %ld after " + "10 ms (2nd try)\n", val2); + if (val2 >= val + 8) { /* 1 ms */ + test_and_set_bit(HFC_CHIP_PCM_MASTER, + &hc->chip); + printk(KERN_INFO "controller is PCM bus MASTER " + "(auto detected)\n"); + } else + goto controller_fail; + } + } + + /* Release the DSP Reset */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) + plxsd_master = 1; + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv |= PLX_DSP_RES_N; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: reset off: PLX_GPIO=%x\n", + __func__, pv); + } + + /* pcm id */ + if (hc->pcm) + printk(KERN_INFO "controller has given PCM BUS ID %d\n", + hc->pcm); + else { + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) + || test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + PCM_cnt++; /* SD has proprietary bridging */ + } + hc->pcm = PCM_cnt; + printk(KERN_INFO "controller has PCM BUS ID %d " + "(auto selected)\n", hc->pcm); + } + + /* set up timer */ + HFC_outb(hc, R_TI_WD, poll_timer); + hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; + + /* set E1 state machine IRQ */ + if (hc->ctype == HFC_TYPE_E1) + hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; + + /* set DTMF detection */ + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: enabling DTMF detection " + "for all B-channel\n", __func__); + hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP; + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + hc->hw.r_dtmf |= V_ULAW_SEL; + HFC_outb(hc, R_DTMF_N, 102 - 1); + hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK; + } + + /* conference engine */ + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + r_conf_en = V_CONF_EN | V_ULAW; + else + r_conf_en = V_CONF_EN; + if (hc->ctype != HFC_TYPE_XHFC) + HFC_outb(hc, R_CONF_EN, r_conf_en); + + /* setting leds */ + switch (hc->leds) { + case 1: /* HFC-E1 OEM */ + if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) + HFC_outb(hc, R_GPIO_SEL, 0x32); + else + HFC_outb(hc, R_GPIO_SEL, 0x30); + + HFC_outb(hc, R_GPIO_EN1, 0x0f); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + + HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); + break; + + case 2: /* HFC-4S OEM */ + case 3: + HFC_outb(hc, R_GPIO_SEL, 0xf0); + HFC_outb(hc, R_GPIO_EN1, 0xff); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + break; + } + + if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) { + hc->hw.r_st_sync = 0x10; /* V_AUTO_SYNCI */ + HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); + } + + /* set master clock */ + if (hc->masterclk >= 0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting ST master clock " + "to port %d (0..%d)\n", + __func__, hc->masterclk, hc->ports - 1); + hc->hw.r_st_sync |= (hc->masterclk | V_AUTO_SYNC); + HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); + } + + + + /* setting misc irq */ + HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n", + hc->hw.r_irqmsk_misc); + + /* RAM access test */ + HFC_outb(hc, R_RAM_ADDR0, 0); + HFC_outb(hc, R_RAM_ADDR1, 0); + HFC_outb(hc, R_RAM_ADDR2, 0); + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_RAM_ADDR0, i); + HFC_outb_nodebug(hc, R_RAM_DATA, ((i * 3) & 0xff)); + } + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_RAM_ADDR0, i); + HFC_inb_nodebug(hc, R_RAM_DATA); + rval = HFC_inb_nodebug(hc, R_INT_DATA); + if (rval != ((i * 3) & 0xff)) { + printk(KERN_DEBUG + "addr:%x val:%x should:%x\n", i, rval, + (i * 3) & 0xff); + err++; + } + } + if (err) { + printk(KERN_DEBUG "aborting - %d RAM access errors\n", err); + err = -EIO; + goto out; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); +out: + spin_unlock_irqrestore(&hc->lock, flags); + return err; +} + + +/* + * control the watchdog + */ +static void +hfcmulti_watchdog(struct hfc_multi *hc) +{ + hc->wdcount++; + + if (hc->wdcount > 10) { + hc->wdcount = 0; + hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ? + V_GPIO_OUT3 : V_GPIO_OUT2; + + /* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */ + HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); + HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte); + } +} + + + +/* + * output leds + */ +static void +hfcmulti_leds(struct hfc_multi *hc) +{ + unsigned long lled; + unsigned long leddw; + int i, state, active, leds; + struct dchannel *dch; + int led[4]; + + switch (hc->leds) { + case 1: /* HFC-E1 OEM */ + /* 2 red steady: LOS + * 1 red steady: L1 not active + * 2 green steady: L1 active + * 1st green flashing: activity on TX + * 2nd green flashing: activity on RX + */ + led[0] = 0; + led[1] = 0; + led[2] = 0; + led[3] = 0; + dch = hc->chan[hc->dnum[0]].dch; + if (dch) { + if (hc->chan[hc->dnum[0]].los) + led[1] = 1; + if (hc->e1_state != 1) { + led[0] = 1; + hc->flash[2] = 0; + hc->flash[3] = 0; + } else { + led[2] = 1; + led[3] = 1; + if (!hc->flash[2] && hc->activity_tx) + hc->flash[2] = poll; + if (!hc->flash[3] && hc->activity_rx) + hc->flash[3] = poll; + if (hc->flash[2] && hc->flash[2] < 1024) + led[2] = 0; + if (hc->flash[3] && hc->flash[3] < 1024) + led[3] = 0; + if (hc->flash[2] >= 2048) + hc->flash[2] = 0; + if (hc->flash[3] >= 2048) + hc->flash[3] = 0; + if (hc->flash[2]) + hc->flash[2] += poll; + if (hc->flash[3]) + hc->flash[3] += poll; + } + } + leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF; + /* leds are inverted */ + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_OUT1, leds); + hc->ledstate = leds; + } + break; + + case 2: /* HFC-4S OEM */ + /* red steady: PH_DEACTIVATE + * green steady: PH_ACTIVATE + * green flashing: activity on TX + */ + for (i = 0; i < 4; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + led[i] = 1; /* led green */ + hc->activity_tx |= hc->activity_rx; + if (!hc->flash[i] && + (hc->activity_tx & (1 << i))) + hc->flash[i] = poll; + if (hc->flash[i] && hc->flash[i] < 1024) + led[i] = 0; /* led off */ + if (hc->flash[i] >= 2048) + hc->flash[i] = 0; + if (hc->flash[i]) + hc->flash[i] += poll; + } else { + led[i] = 2; /* led red */ + hc->flash[i] = 0; + } + } else + led[i] = 0; /* led off */ + } + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + leds = 0; + for (i = 0; i < 4; i++) { + if (led[i] == 1) { + /*green*/ + leds |= (0x2 << (i * 2)); + } else if (led[i] == 2) { + /*red*/ + leds |= (0x1 << (i * 2)); + } + } + if (leds != (int)hc->ledstate) { + vpm_out(hc, 0, 0x1a8 + 3, leds); + hc->ledstate = leds; + } + } else { + leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) | + ((led[0] > 0) << 2) | ((led[2] > 0) << 3) | + ((led[3] & 1) << 4) | ((led[1] & 1) << 5) | + ((led[0] & 1) << 6) | ((led[2] & 1) << 7); + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F); + HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4); + hc->ledstate = leds; + } + } + break; + + case 3: /* HFC 1S/2S Beronet */ + /* red steady: PH_DEACTIVATE + * green steady: PH_ACTIVATE + * green flashing: activity on TX + */ + for (i = 0; i < 2; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + led[i] = 1; /* led green */ + hc->activity_tx |= hc->activity_rx; + if (!hc->flash[i] && + (hc->activity_tx & (1 << i))) + hc->flash[i] = poll; + if (hc->flash[i] < 1024) + led[i] = 0; /* led off */ + if (hc->flash[i] >= 2048) + hc->flash[i] = 0; + if (hc->flash[i]) + hc->flash[i] += poll; + } else { + led[i] = 2; /* led red */ + hc->flash[i] = 0; + } + } else + led[i] = 0; /* led off */ + } + leds = (led[0] > 0) | ((led[1] > 0) << 1) | ((led[0]&1) << 2) + | ((led[1]&1) << 3); + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_EN1, + ((led[0] > 0) << 2) | ((led[1] > 0) << 3)); + HFC_outb_nodebug(hc, R_GPIO_OUT1, + ((led[0] & 1) << 2) | ((led[1] & 1) << 3)); + hc->ledstate = leds; + } + break; + case 8: /* HFC 8S+ Beronet */ + /* off: PH_DEACTIVATE + * steady: PH_ACTIVATE + * flashing: activity on TX + */ + lled = 0xff; /* leds off */ + for (i = 0; i < 8; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + lled &= ~(1 << i); /* led on */ + hc->activity_tx |= hc->activity_rx; + if (!hc->flash[i] && + (hc->activity_tx & (1 << i))) + hc->flash[i] = poll; + if (hc->flash[i] < 1024) + lled |= 1 << i; /* led off */ + if (hc->flash[i] >= 2048) + hc->flash[i] = 0; + if (hc->flash[i]) + hc->flash[i] += poll; + } else + hc->flash[i] = 0; + } + } + leddw = lled << 24 | lled << 16 | lled << 8 | lled; + if (leddw != hc->ledstate) { + /* HFC_outb(hc, R_BRG_PCM_CFG, 1); + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */ + /* was _io before */ + HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); + outw(0x4000, hc->pci_iobase + 4); + outl(leddw, hc->pci_iobase); + HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK); + hc->ledstate = leddw; + } + break; + } + hc->activity_tx = 0; + hc->activity_rx = 0; +} +/* + * read dtmf coefficients + */ + +static void +hfcmulti_dtmf(struct hfc_multi *hc) +{ + s32 *coeff; + u_int mantissa; + int co, ch; + struct bchannel *bch = NULL; + u8 exponent; + int dtmf = 0; + int addr; + u16 w_float; + struct sk_buff *skb; + struct mISDNhead *hh; + + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__); + for (ch = 0; ch <= 31; ch++) { + /* only process enabled B-channels */ + bch = hc->chan[ch].bch; + if (!bch) + continue; + if (!hc->created[hc->chan[ch].port]) + continue; + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + continue; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf channel %d:", + __func__, ch); + coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]); + dtmf = 1; + for (co = 0; co < 8; co++) { + /* read W(n-1) coefficient */ + addr = hc->DTMFbase + ((co << 7) | (ch << 2)); + HFC_outb_nodebug(hc, R_RAM_ADDR0, addr); + HFC_outb_nodebug(hc, R_RAM_ADDR1, addr >> 8); + HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr >> 16) + | V_ADDR_INC); + w_float = HFC_inb_nodebug(hc, R_RAM_DATA); + w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float >> 12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent - 1); + } + + /* store coefficient */ + coeff[co << 1] = mantissa; + + /* read W(n) coefficient */ + w_float = HFC_inb_nodebug(hc, R_RAM_DATA); + w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float >> 12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent - 1); + } + + /* store coefficient */ + coeff[(co << 1) | 1] = mantissa; + } + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" DTMF ready %08x %08x %08x %08x " + "%08x %08x %08x %08x\n", + coeff[0], coeff[1], coeff[2], coeff[3], + coeff[4], coeff[5], coeff[6], coeff[7]); + hc->chan[ch].coeff_count++; + if (hc->chan[ch].coeff_count == 8) { + hc->chan[ch].coeff_count = 0; + skb = mI_alloc_skb(512, GFP_ATOMIC); + if (!skb) { + printk(KERN_DEBUG "%s: No memory for skb\n", + __func__); + continue; + } + hh = mISDN_HEAD_P(skb); + hh->prim = PH_CONTROL_IND; + hh->id = DTMF_HFC_COEF; + skb_put_data(skb, hc->chan[ch].coeff, 512); + recv_Bchannel_skb(bch, skb); + } + } + + /* restart DTMF processing */ + hc->dtmf = dtmf; + if (dtmf) + HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); +} + + +/* + * fill fifo as much as possible + */ + +static void +hfcmulti_tx(struct hfc_multi *hc, int ch) +{ + int i, ii, temp, len = 0; + int Zspace, z1, z2; /* must be int for calculation */ + int Fspace, f1, f2; + u_char *d; + int *txpending, slot_tx; + struct bchannel *bch; + struct dchannel *dch; + struct sk_buff **sp = NULL; + int *idxp; + + bch = hc->chan[ch].bch; + dch = hc->chan[ch].dch; + if ((!dch) && (!bch)) + return; + + txpending = &hc->chan[ch].txpending; + slot_tx = hc->chan[ch].slot_tx; + if (dch) { + if (!test_bit(FLG_ACTIVE, &dch->Flags)) + return; + sp = &dch->tx_skb; + idxp = &dch->tx_idx; + } else { + if (!test_bit(FLG_ACTIVE, &bch->Flags)) + return; + sp = &bch->tx_skb; + idxp = &bch->tx_idx; + } + if (*sp) + len = (*sp)->len; + + if ((!len) && *txpending != 1) + return; /* no data */ + + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].protocol == ISDN_P_B_RAW) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) + HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1)); + else + HFC_outb_nodebug(hc, R_FIFO, ch << 1); + HFC_wait_nodebug(hc); + + if (*txpending == 2) { + /* reset fifo */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_SUBCH_CFG, 0); + *txpending = 1; + } +next_frame: + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + f1 = HFC_inb_nodebug(hc, A_F1); + f2 = HFC_inb_nodebug(hc, A_F2); + while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): reread f2 because %d!=%d\n", + __func__, hc->id + 1, temp, f2); + f2 = temp; /* repeat until F2 is equal */ + } + Fspace = f2 - f1 - 1; + if (Fspace < 0) + Fspace += hc->Flen; + /* + * Old FIFO handling doesn't give us the current Z2 read + * pointer, so we cannot send the next frame before the fifo + * is empty. It makes no difference except for a slightly + * lower performance. + */ + if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) { + if (f1 != f2) + Fspace = 0; + else + Fspace = 1; + } + /* one frame only for ST D-channels, to allow resending */ + if (hc->ctype != HFC_TYPE_E1 && dch) { + if (f1 != f2) + Fspace = 0; + } + /* F-counter full condition */ + if (Fspace == 0) + return; + } + z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; + z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; + while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): reread z2 because " + "%d!=%d\n", __func__, hc->id + 1, temp, z2); + z2 = temp; /* repeat unti Z2 is equal */ + } + hc->chan[ch].Zfill = z1 - z2; + if (hc->chan[ch].Zfill < 0) + hc->chan[ch].Zfill += hc->Zlen; + Zspace = z2 - z1; + if (Zspace <= 0) + Zspace += hc->Zlen; + Zspace -= 4; /* keep not too full, so pointers will not overrun */ + /* fill transparent data only to maxinum transparent load (minus 4) */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + Zspace = Zspace - hc->Zlen + hc->max_trans; + if (Zspace <= 0) /* no space of 4 bytes */ + return; + + /* if no data */ + if (!len) { + if (z1 == z2) { /* empty */ + /* if done with FIFO audio data during PCM connection */ + if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && + *txpending && slot_tx >= 0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: reconnecting PCM due to no " + "more FIFO data: channel %d " + "slot_tx %d\n", + __func__, ch, slot_tx); + /* connect slot */ + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, 0xc0 + | 0x07 << 2 | V_HDLC_TRP | V_IFF); + /* Enable FIFO, no interrupt */ + else + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch << 1 | 1); + HFC_wait_nodebug(hc); + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, 0xc0 + | 0x07 << 2 | V_HDLC_TRP | V_IFF); + /* Enable FIFO, no interrupt */ + else + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch << 1); + HFC_wait_nodebug(hc); + } + *txpending = 0; + } + return; /* no data */ + } + + /* "fill fifo if empty" feature */ + if (bch && test_bit(FLG_FILLEMPTY, &bch->Flags) + && !test_bit(FLG_HDLC, &bch->Flags) && z2 == z1) { + if (debug & DEBUG_HFCMULTI_FILL) + printk(KERN_DEBUG "%s: buffer empty, so we have " + "underrun\n", __func__); + /* fill buffer, to prevent future underrun */ + hc->write_fifo(hc, hc->silence_data, poll >> 1); + Zspace -= (poll >> 1); + } + + /* if audio data and connected slot */ + if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending) + && slot_tx >= 0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: disconnecting PCM due to " + "FIFO data: channel %d slot_tx %d\n", + __func__, ch, slot_tx); + /* disconnect slot */ + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, 0x80 + | 0x07 << 2 | V_HDLC_TRP | V_IFF); + /* Enable FIFO, no interrupt */ + else + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch << 1 | 1); + HFC_wait_nodebug(hc); + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, 0x80 + | 0x07 << 2 | V_HDLC_TRP | V_IFF); + /* Enable FIFO, no interrupt */ + else + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch << 1); + HFC_wait_nodebug(hc); + } + *txpending = 1; + + /* show activity */ + if (dch) + hc->activity_tx |= 1 << hc->chan[ch].port; + + /* fill fifo to what we have left */ + ii = len; + if (dch || test_bit(FLG_HDLC, &bch->Flags)) + temp = 1; + else + temp = 0; + i = *idxp; + d = (*sp)->data + i; + if (ii - i > Zspace) + ii = Zspace + i; + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space " + "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n", + __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i, + temp ? "HDLC" : "TRANS"); + + /* Have to prep the audio data */ + hc->write_fifo(hc, d, ii - i); + hc->chan[ch].Zfill += ii - i; + *idxp = ii; + + /* if not all data has been written */ + if (ii != len) { + /* NOTE: fifo is started by the calling function */ + return; + } + + /* if all data has been written, terminate frame */ + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + /* increment f-counter */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_nodebug(hc); + } + + dev_kfree_skb(*sp); + /* check for next frame */ + if (bch && get_next_bframe(bch)) { + len = (*sp)->len; + goto next_frame; + } + if (dch && get_next_dframe(dch)) { + len = (*sp)->len; + goto next_frame; + } + + /* + * now we have no more data, so in case of transparent, + * we set the last byte in fifo to 'silence' in case we will get + * no more data at all. this prevents sending an undefined value. + */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); +} + + +/* NOTE: only called if E1 card is in active state */ +static void +hfcmulti_rx(struct hfc_multi *hc, int ch) +{ + int temp; + int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */ + int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ + int again = 0; + struct bchannel *bch; + struct dchannel *dch = NULL; + struct sk_buff *skb, **sp = NULL; + int maxlen; + + bch = hc->chan[ch].bch; + if (bch) { + if (!test_bit(FLG_ACTIVE, &bch->Flags)) + return; + } else if (hc->chan[ch].dch) { + dch = hc->chan[ch].dch; + if (!test_bit(FLG_ACTIVE, &dch->Flags)) + return; + } else { + return; + } +next_frame: + /* on first AND before getting next valid frame, R_FIFO must be written + to. */ + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].protocol == ISDN_P_B_RAW) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) + HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1) | 1); + else + HFC_outb_nodebug(hc, R_FIFO, (ch << 1) | 1); + HFC_wait_nodebug(hc); + + /* ignore if rx is off BUT change fifo (above) to start pending TX */ + if (hc->chan[ch].rx_off) { + if (bch) + bch->dropcnt += poll; /* not exact but fair enough */ + return; + } + + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + f1 = HFC_inb_nodebug(hc, A_F1); + while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): reread f1 because %d!=%d\n", + __func__, hc->id + 1, temp, f1); + f1 = temp; /* repeat until F1 is equal */ + } + f2 = HFC_inb_nodebug(hc, A_F2); + } + z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; + while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): reread z2 because " + "%d!=%d\n", __func__, hc->id + 1, temp, z2); + z1 = temp; /* repeat until Z1 is equal */ + } + z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; + Zsize = z1 - z2; + if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2) + /* complete hdlc frame */ + Zsize++; + if (Zsize < 0) + Zsize += hc->Zlen; + /* if buffer is empty */ + if (Zsize <= 0) + return; + + if (bch) { + maxlen = bchannel_get_rxbuf(bch, Zsize); + if (maxlen < 0) { + pr_warning("card%d.B%d: No bufferspace for %d bytes\n", + hc->id + 1, bch->nr, Zsize); + return; + } + sp = &bch->rx_skb; + maxlen = bch->maxlen; + } else { /* Dchannel */ + sp = &dch->rx_skb; + maxlen = dch->maxlen + 3; + if (*sp == NULL) { + *sp = mI_alloc_skb(maxlen, GFP_ATOMIC); + if (*sp == NULL) { + pr_warning("card%d: No mem for dch rx_skb\n", + hc->id + 1); + return; + } + } + } + /* show activity */ + if (dch) + hc->activity_rx |= 1 << hc->chan[ch].port; + + /* empty fifo with what we have */ + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d " + "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) " + "got=%d (again %d)\n", __func__, hc->id + 1, ch, + Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE", + f1, f2, Zsize + (*sp)->len, again); + /* HDLC */ + if ((Zsize + (*sp)->len) > maxlen) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): hdlc-frame too large.\n", + __func__, hc->id + 1); + skb_trim(*sp, 0); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + return; + } + + hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); + + if (f1 != f2) { + /* increment Z2,F2-counter */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_nodebug(hc); + /* check size */ + if ((*sp)->len < 4) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): Frame below minimum " + "size\n", __func__, hc->id + 1); + skb_trim(*sp, 0); + goto next_frame; + } + /* there is at least one complete frame, check crc */ + if ((*sp)->data[(*sp)->len - 1]) { + if (debug & DEBUG_HFCMULTI_CRC) + printk(KERN_DEBUG + "%s: CRC-error\n", __func__); + skb_trim(*sp, 0); + goto next_frame; + } + skb_trim(*sp, (*sp)->len - 3); + if ((*sp)->len < MISDN_COPY_SIZE) { + skb = *sp; + *sp = mI_alloc_skb(skb->len, GFP_ATOMIC); + if (*sp) { + skb_put_data(*sp, skb->data, skb->len); + skb_trim(skb, 0); + } else { + printk(KERN_DEBUG "%s: No mem\n", + __func__); + *sp = skb; + skb = NULL; + } + } else { + skb = NULL; + } + if (debug & DEBUG_HFCMULTI_FIFO) { + printk(KERN_DEBUG "%s(card %d):", + __func__, hc->id + 1); + temp = 0; + while (temp < (*sp)->len) + printk(" %02x", (*sp)->data[temp++]); + printk("\n"); + } + if (dch) + recv_Dchannel(dch); + else + recv_Bchannel(bch, MISDN_ID_ANY, false); + *sp = skb; + again++; + goto next_frame; + } + /* there is an incomplete frame */ + } else { + /* transparent */ + hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): fifo(%d) reading %d bytes " + "(z1=%04x, z2=%04x) TRANS\n", + __func__, hc->id + 1, ch, Zsize, z1, z2); + /* only bch is transparent */ + recv_Bchannel(bch, hc->chan[ch].Zfill, false); + } +} + + +/* + * Interrupt handler + */ +static void +signal_state_up(struct dchannel *dch, int info, char *msg) +{ + struct sk_buff *skb; + int id, data = info; + + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: %s\n", __func__, msg); + + id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */ + + skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data, + GFP_ATOMIC); + if (!skb) + return; + recv_Dchannel_skb(dch, skb); +} + +static inline void +handle_timer_irq(struct hfc_multi *hc) +{ + int ch, temp; + struct dchannel *dch; + u_long flags; + + /* process queued resync jobs */ + if (hc->e1_resync) { + /* lock, so e1_resync gets not changed */ + spin_lock_irqsave(&HFClock, flags); + if (hc->e1_resync & 1) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Enable SYNC_I\n"); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC); + /* disable JATT, if RX_SYNC is set */ + if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) + HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); + } + if (hc->e1_resync & 2) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Enable jatt PLL\n"); + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } + if (hc->e1_resync & 4) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Enable QUARTZ for HFC-E1\n"); + /* set jatt to quartz */ + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC + | V_JATT_OFF); + /* switch to JATT, in case it is not already */ + HFC_outb(hc, R_SYNC_OUT, 0); + } + hc->e1_resync = 0; + spin_unlock_irqrestore(&HFClock, flags); + } + + if (hc->ctype != HFC_TYPE_E1 || hc->e1_state == 1) + for (ch = 0; ch <= 31; ch++) { + if (hc->created[hc->chan[ch].port]) { + hfcmulti_tx(hc, ch); + /* fifo is started when switching to rx-fifo */ + hfcmulti_rx(hc, ch); + if (hc->chan[ch].dch && + hc->chan[ch].nt_timer > -1) { + dch = hc->chan[ch].dch; + if (!(--hc->chan[ch].nt_timer)) { + schedule_event(dch, + FLG_PHCHANGE); + if (debug & + DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: nt_timer at " + "state %x\n", + __func__, + dch->state); + } + } + } + } + if (hc->ctype == HFC_TYPE_E1 && hc->created[0]) { + dch = hc->chan[hc->dnum[0]].dch; + /* LOS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS; + hc->chan[hc->dnum[0]].los = temp; + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[0]].cfg)) { + if (!temp && hc->chan[hc->dnum[0]].los) + signal_state_up(dch, L1_SIGNAL_LOS_ON, + "LOS detected"); + if (temp && !hc->chan[hc->dnum[0]].los) + signal_state_up(dch, L1_SIGNAL_LOS_OFF, + "LOS gone"); + } + if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dnum[0]].cfg)) { + /* AIS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS; + if (!temp && hc->chan[hc->dnum[0]].ais) + signal_state_up(dch, L1_SIGNAL_AIS_ON, + "AIS detected"); + if (temp && !hc->chan[hc->dnum[0]].ais) + signal_state_up(dch, L1_SIGNAL_AIS_OFF, + "AIS gone"); + hc->chan[hc->dnum[0]].ais = temp; + } + if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dnum[0]].cfg)) { + /* SLIP */ + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX; + if (!temp && hc->chan[hc->dnum[0]].slip_rx) + signal_state_up(dch, L1_SIGNAL_SLIP_RX, + " bit SLIP detected RX"); + hc->chan[hc->dnum[0]].slip_rx = temp; + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX; + if (!temp && hc->chan[hc->dnum[0]].slip_tx) + signal_state_up(dch, L1_SIGNAL_SLIP_TX, + " bit SLIP detected TX"); + hc->chan[hc->dnum[0]].slip_tx = temp; + } + if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dnum[0]].cfg)) { + /* RDI */ + temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A; + if (!temp && hc->chan[hc->dnum[0]].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_ON, + "RDI detected"); + if (temp && !hc->chan[hc->dnum[0]].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_OFF, + "RDI gone"); + hc->chan[hc->dnum[0]].rdi = temp; + } + temp = HFC_inb_nodebug(hc, R_JATT_DIR); + switch (hc->chan[hc->dnum[0]].sync) { + case 0: + if ((temp & 0x60) == 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 now " + "in clock sync\n", + __func__, hc->id); + HFC_outb(hc, R_RX_OFF, + hc->chan[hc->dnum[0]].jitter | V_RX_INIT); + HFC_outb(hc, R_TX_OFF, + hc->chan[hc->dnum[0]].jitter | V_RX_INIT); + hc->chan[hc->dnum[0]].sync = 1; + goto check_framesync; + } + break; + case 1: + if ((temp & 0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "lost clock sync\n", + __func__, hc->id); + hc->chan[hc->dnum[0]].sync = 0; + break; + } + check_framesync: + temp = HFC_inb_nodebug(hc, R_SYNC_STA); + if (temp == 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "now in frame sync\n", + __func__, hc->id); + hc->chan[hc->dnum[0]].sync = 2; + } + break; + case 2: + if ((temp & 0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 lost " + "clock & frame sync\n", + __func__, hc->id); + hc->chan[hc->dnum[0]].sync = 0; + break; + } + temp = HFC_inb_nodebug(hc, R_SYNC_STA); + if (temp != 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "lost frame sync\n", + __func__, hc->id); + hc->chan[hc->dnum[0]].sync = 1; + } + break; + } + } + + if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) + hfcmulti_watchdog(hc); + + if (hc->leds) + hfcmulti_leds(hc); +} + +static void +ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech) +{ + struct dchannel *dch; + int ch; + int active; + u_char st_status, temp; + + /* state machine */ + for (ch = 0; ch <= 31; ch++) { + if (hc->chan[ch].dch) { + dch = hc->chan[ch].dch; + if (r_irq_statech & 1) { + HFC_outb_nodebug(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + /* undocumented: status changes during read */ + st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE); + while (st_status != (temp = + HFC_inb_nodebug(hc, A_ST_RD_STATE))) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: reread " + "STATE because %d!=%d\n", + __func__, temp, + st_status); + st_status = temp; /* repeat */ + } + + /* Speech Design TE-sync indication */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && + dch->dev.D.protocol == ISDN_P_TE_S0) { + if (st_status & V_FR_SYNC_ST) + hc->syncronized |= + (1 << hc->chan[ch].port); + else + hc->syncronized &= + ~(1 << hc->chan[ch].port); + } + dch->state = st_status & 0x0f; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + if (dch->state == active) { + HFC_outb_nodebug(hc, R_FIFO, + (ch << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, + R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + dch->tx_idx = 0; + } + schedule_event(dch, FLG_PHCHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: S/T newstate %x port %d\n", + __func__, dch->state, + hc->chan[ch].port); + } + r_irq_statech >>= 1; + } + } + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + plxsd_checksync(hc, 0); +} + +static void +fifo_irq(struct hfc_multi *hc, int block) +{ + int ch, j; + struct dchannel *dch; + struct bchannel *bch; + u_char r_irq_fifo_bl; + + r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block); + j = 0; + while (j < 8) { + ch = (block << 2) + (j >> 1); + dch = hc->chan[ch].dch; + bch = hc->chan[ch].bch; + if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) { + j += 2; + continue; + } + if (dch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &dch->Flags)) { + hfcmulti_tx(hc, ch); + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + if (bch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &bch->Flags)) { + hfcmulti_tx(hc, ch); + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + j++; + if (dch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &dch->Flags)) { + hfcmulti_rx(hc, ch); + } + if (bch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &bch->Flags)) { + hfcmulti_rx(hc, ch); + } + j++; + } +} + +#ifdef IRQ_DEBUG +int irqsem; +#endif +static irqreturn_t +hfcmulti_interrupt(int intno, void *dev_id) +{ +#ifdef IRQCOUNT_DEBUG + static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0, + iq5 = 0, iq6 = 0, iqcnt = 0; +#endif + struct hfc_multi *hc = dev_id; + struct dchannel *dch; + u_char r_irq_statech, status, r_irq_misc, r_irq_oview; + int i; + void __iomem *plx_acc; + u_short wval; + u_char e1_syncsta, temp, temp2; + u_long flags; + + if (!hc) { + printk(KERN_ERR "HFC-multi: Spurious interrupt!\n"); + return IRQ_NONE; + } + + spin_lock(&hc->lock); + +#ifdef IRQ_DEBUG + if (irqsem) + printk(KERN_ERR "irq for card %d during irq from " + "card %d, this is no bug.\n", hc->id + 1, irqsem); + irqsem = hc->id + 1; +#endif +#ifdef CONFIG_MISDN_HFCMULTI_8xx + if (hc->immap->im_cpm.cp_pbdat & hc->pb_irqmsk) + goto irq_notforus; +#endif + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, flags); + plx_acc = hc->plx_membase + PLX_INTCSR; + wval = readw(plx_acc); + spin_unlock_irqrestore(&plx_lock, flags); + if (!(wval & PLX_INTCSR_LINTI1_STATUS)) + goto irq_notforus; + } + + status = HFC_inb_nodebug(hc, R_STATUS); + r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH); +#ifdef IRQCOUNT_DEBUG + if (r_irq_statech) + iq1++; + if (status & V_DTMF_STA) + iq2++; + if (status & V_LOST_STA) + iq3++; + if (status & V_EXT_IRQSTA) + iq4++; + if (status & V_MISC_IRQSTA) + iq5++; + if (status & V_FR_IRQSTA) + iq6++; + if (iqcnt++ > 5000) { + printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n", + iq1, iq2, iq3, iq4, iq5, iq6); + iqcnt = 0; + } +#endif + + if (!r_irq_statech && + !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | + V_MISC_IRQSTA | V_FR_IRQSTA))) { + /* irq is not for us */ + goto irq_notforus; + } + hc->irqcnt++; + if (r_irq_statech) { + if (hc->ctype != HFC_TYPE_E1) + ph_state_irq(hc, r_irq_statech); + } + if (status & V_EXT_IRQSTA) + ; /* external IRQ */ + if (status & V_LOST_STA) { + /* LOST IRQ */ + HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */ + } + if (status & V_MISC_IRQSTA) { + /* misc IRQ */ + r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC); + r_irq_misc &= hc->hw.r_irqmsk_misc; /* ignore disabled irqs */ + if (r_irq_misc & V_STA_IRQ) { + if (hc->ctype == HFC_TYPE_E1) { + /* state machine */ + dch = hc->chan[hc->dnum[0]].dch; + e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && hc->e1_getclock) { + if (e1_syncsta & V_FR_SYNC_E1) + hc->syncronized = 1; + else + hc->syncronized = 0; + } + /* undocumented: status changes during read */ + temp = HFC_inb_nodebug(hc, R_E1_RD_STA); + while (temp != (temp2 = + HFC_inb_nodebug(hc, R_E1_RD_STA))) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: reread " + "STATE because %d!=%d\n", + __func__, temp, temp2); + temp = temp2; /* repeat */ + } + /* broadcast state change to all fragments */ + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 (id=%d) newstate %x\n", + __func__, hc->id, temp & 0x7); + for (i = 0; i < hc->ports; i++) { + dch = hc->chan[hc->dnum[i]].dch; + dch->state = temp & 0x7; + schedule_event(dch, FLG_PHCHANGE); + } + + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + plxsd_checksync(hc, 0); + } + } + if (r_irq_misc & V_TI_IRQ) { + if (hc->iclock_on) + mISDN_clock_update(hc->iclock, poll, NULL); + handle_timer_irq(hc); + } + + if (r_irq_misc & V_DTMF_IRQ) + hfcmulti_dtmf(hc); + + if (r_irq_misc & V_IRQ_PROC) { + static int irq_proc_cnt; + if (!irq_proc_cnt++) + printk(KERN_DEBUG "%s: got V_IRQ_PROC -" + " this should not happen\n", __func__); + } + + } + if (status & V_FR_IRQSTA) { + /* FIFO IRQ */ + r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW); + for (i = 0; i < 8; i++) { + if (r_irq_oview & (1 << i)) + fifo_irq(hc, i); + } + } + +#ifdef IRQ_DEBUG + irqsem = 0; +#endif + spin_unlock(&hc->lock); + return IRQ_HANDLED; + +irq_notforus: +#ifdef IRQ_DEBUG + irqsem = 0; +#endif + spin_unlock(&hc->lock); + return IRQ_NONE; +} + + +/* + * timer callback for D-chan busy resolution. Currently no function + */ + +static void +hfcmulti_dbusy_timer(struct timer_list *t) +{ +} + + +/* + * activate/deactivate hardware for selected channels and mode + * + * configure B-channel with the given protocol + * ch eqals to the HFC-channel (0-31) + * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31 + * for S/T, 1-31 for E1) + * the hdlc interrupts will be set/unset + */ +static int +mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, + int bank_tx, int slot_rx, int bank_rx) +{ + int flow_tx = 0, flow_rx = 0, routing = 0; + int oslot_tx, oslot_rx; + int conf; + + if (ch < 0 || ch > 31) + return -EINVAL; + oslot_tx = hc->chan[ch].slot_tx; + oslot_rx = hc->chan[ch].slot_rx; + conf = hc->chan[ch].conf; + + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: card %d channel %d protocol %x slot old=%d new=%d " + "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n", + __func__, hc->id, ch, protocol, oslot_tx, slot_tx, + bank_tx, oslot_rx, slot_rx, bank_rx); + + if (oslot_tx >= 0 && slot_tx != oslot_tx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: remove from slot %d (TX)\n", + __func__, oslot_tx); + if (hc->slot_owner[oslot_tx << 1] == ch) { + HFC_outb(hc, R_SLOT, oslot_tx << 1); + HFC_outb(hc, A_SL_CFG, 0); + if (hc->ctype != HFC_TYPE_XHFC) + HFC_outb(hc, A_CONF, 0); + hc->slot_owner[oslot_tx << 1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: we are not owner of this tx slot " + "anymore, channel %d is.\n", + __func__, hc->slot_owner[oslot_tx << 1]); + } + } + + if (oslot_rx >= 0 && slot_rx != oslot_rx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: remove from slot %d (RX)\n", + __func__, oslot_rx); + if (hc->slot_owner[(oslot_rx << 1) | 1] == ch) { + HFC_outb(hc, R_SLOT, (oslot_rx << 1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, 0); + hc->slot_owner[(oslot_rx << 1) | 1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: we are not owner of this rx slot " + "anymore, channel %d is.\n", + __func__, + hc->slot_owner[(oslot_rx << 1) | 1]); + } + } + + if (slot_tx < 0) { + flow_tx = 0x80; /* FIFO->ST */ + /* disable pcm slot */ + hc->chan[ch].slot_tx = -1; + hc->chan[ch].bank_tx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_tx = 0x80; /* FIFO->ST */ + else + flow_tx = 0xc0; /* PCM->ST */ + /* put on slot */ + routing = bank_tx ? 0xc0 : 0x80; + if (conf >= 0 || bank_tx > 1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put channel %d to slot %d bank" + " %d flow %02x routing %02x conf %d (TX)\n", + __func__, ch, slot_tx, bank_tx, + flow_tx, routing, conf); + HFC_outb(hc, R_SLOT, slot_tx << 1); + HFC_outb(hc, A_SL_CFG, (ch << 1) | routing); + if (hc->ctype != HFC_TYPE_XHFC) + HFC_outb(hc, A_CONF, + (conf < 0) ? 0 : (conf | V_CONF_SL)); + hc->slot_owner[slot_tx << 1] = ch; + hc->chan[ch].slot_tx = slot_tx; + hc->chan[ch].bank_tx = bank_tx; + } + if (slot_rx < 0) { + /* disable pcm slot */ + flow_rx = 0x80; /* ST->FIFO */ + hc->chan[ch].slot_rx = -1; + hc->chan[ch].bank_rx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_rx = 0x80; /* ST->FIFO */ + else + flow_rx = 0xc0; /* ST->(FIFO,PCM) */ + /* put on slot */ + routing = bank_rx ? 0x80 : 0xc0; /* reversed */ + if (conf >= 0 || bank_rx > 1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put channel %d to slot %d bank" + " %d flow %02x routing %02x conf %d (RX)\n", + __func__, ch, slot_rx, bank_rx, + flow_rx, routing, conf); + HFC_outb(hc, R_SLOT, (slot_rx << 1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, (ch << 1) | V_CH_DIR | routing); + hc->slot_owner[(slot_rx << 1) | 1] = ch; + hc->chan[ch].slot_rx = slot_rx; + hc->chan[ch].bank_rx = bank_rx; + } + + switch (protocol) { + case (ISDN_P_NONE): + /* disable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* disable RX fifo */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->chan[ch].bch && hc->ctype != HFC_TYPE_E1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] &= + ((ch & 0x3) == 0) ? ~V_B1_EN : ~V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + if (hc->chan[ch].bch) { + test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); + test_and_clear_bit(FLG_TRANSPARENT, + &hc->chan[ch].bch->Flags); + } + break; + case (ISDN_P_B_RAW): /* B-channel */ + + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) { + + printk(KERN_DEBUG + "Setting B-channel %d to echo cancelable " + "state on PCM slot %d\n", ch, + ((ch / 4) * 8) + ((ch % 4) * 4) + 1); + printk(KERN_DEBUG + "Enabling pass through for channel\n"); + vpm_out(hc, ch, ((ch / 4) * 8) + + ((ch % 4) * 4) + 1, 0x01); + /* rx path */ + /* S/T -> PCM */ + HFC_outb(hc, R_FIFO, (ch << 1)); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + + ((ch % 4) * 4) + 1) << 1); + HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1)); + + /* PCM -> FIFO */ + HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + if (hc->chan[ch].protocol != protocol) { + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + + ((ch % 4) * 4) + 1) << 1) | 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1); + + /* tx path */ + /* PCM -> S/T */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + + ((ch % 4) * 4)) << 1) | 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1); + + /* FIFO -> PCM */ + HFC_outb(hc, R_FIFO, 0x20 | (ch << 1)); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + if (hc->chan[ch].protocol != protocol) { + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + /* tx silence */ + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); + HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + + ((ch % 4) * 4)) << 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1)); + } else { + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x07 << 2 | + V_HDLC_TRP | V_IFF); + /* Enable FIFO, no interrupt */ + else + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + if (hc->chan[ch].protocol != protocol) { + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + /* tx silence */ + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x07 << 2 | + V_HDLC_TRP); + /* Enable FIFO, no interrupt*/ + else + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | + V_HDLC_TRP); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + if (hc->chan[ch].protocol != protocol) { + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + } + if (hc->ctype != HFC_TYPE_E1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= + ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + if (hc->chan[ch].bch) + test_and_set_bit(FLG_TRANSPARENT, + &hc->chan[ch].bch->Flags); + break; + case (ISDN_P_B_HDLC): /* B-channel */ + case (ISDN_P_TE_S0): /* D-channel */ + case (ISDN_P_NT_S0): + case (ISDN_P_TE_E1): + case (ISDN_P_NT_E1): + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + if (hc->ctype == HFC_TYPE_E1 || hc->chan[ch].bch) { + /* E1 or B-channel */ + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04); + HFC_outb(hc, A_SUBCH_CFG, 0); + } else { + /* D-Channel without HDLC fill flags */ + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 2); + } + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); + if (hc->ctype == HFC_TYPE_E1 || hc->chan[ch].bch) + HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */ + else + HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */ + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->chan[ch].bch) { + test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); + if (hc->ctype != HFC_TYPE_E1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= + ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + } + break; + default: + printk(KERN_DEBUG "%s: protocol not known %x\n", + __func__, protocol); + hc->chan[ch].protocol = ISDN_P_NONE; + return -ENOPROTOOPT; + } + hc->chan[ch].protocol = protocol; + return 0; +} + + +/* + * connect/disconnect PCM + */ + +static void +hfcmulti_pcm(struct hfc_multi *hc, int ch, int slot_tx, int bank_tx, + int slot_rx, int bank_rx) +{ + if (slot_tx < 0 || slot_rx < 0 || bank_tx < 0 || bank_rx < 0) { + /* disable PCM */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0); + return; + } + + /* enable pcm */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx, + slot_rx, bank_rx); +} + +/* + * set/disable conference + */ + +static void +hfcmulti_conf(struct hfc_multi *hc, int ch, int num) +{ + if (num >= 0 && num <= 7) + hc->chan[ch].conf = num; + else + hc->chan[ch].conf = -1; + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx, + hc->chan[ch].bank_tx, hc->chan[ch].slot_rx, + hc->chan[ch].bank_rx); +} + + +/* + * set/disable sample loop + */ + +/* NOTE: this function is experimental and therefore disabled */ + +/* + * Layer 1 callback function + */ +static int +hfcm_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfc_multi *hc = dch->hw; + u_long flags; + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + break; + case HW_RESET_REQ: + /* start activation */ + spin_lock_irqsave(&hc->lock, flags); + if (hc->ctype == HFC_TYPE_E1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_RESET_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); + HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT * 3)); + /* activate */ + } + spin_unlock_irqrestore(&hc->lock, flags); + l1_event(dch->l1, HW_POWERUP_IND); + break; + case HW_DEACT_REQ: + /* start deactivation */ + spin_lock_irqsave(&hc->lock, flags); + if (hc->ctype == HFC_TYPE_E1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_DEACT_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); + /* deactivate */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[dch->slot].port); + plxsd_checksync(hc, 0); + } + } + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case HW_POWERUP_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (hc->ctype == HFC_TYPE_E1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_POWERUP_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */ + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -1; + } + return 0; +} + +/* + * Layer2 -> Layer 1 Transfer + */ + +static int +handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_multi *hc = dch->hw; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (skb->len < 1) + break; + spin_lock_irqsave(&hc->lock, flags); + ret = dchannel_senddata(dch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcmulti_tx(hc, dch->slot); + ret = 0; + /* start fifo */ + HFC_outb(hc, R_FIFO, 0); + HFC_wait(hc); + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + spin_lock_irqsave(&hc->lock, flags); + ret = 0; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_ACTIVATE port %d (0..%d)\n", + __func__, hc->chan[dch->slot].port, + hc->ports - 1); + /* start activation */ + if (hc->ctype == HFC_TYPE_E1) { + ph_state_change(dch); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 report state %x \n", + __func__, dch->state); + } else { + HFC_outb(hc, R_ST_SEL, + hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); + /* G1 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 1); + HFC_outb(hc, A_ST_WR_STATE, 1 | + (V_ST_ACT * 3)); /* activate */ + dch->state = 1; + } + spin_unlock_irqrestore(&hc->lock, flags); + } else + ret = l1_event(dch->l1, hh->prim); + break; + case PH_DEACTIVATE_REQ: + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + spin_lock_irqsave(&hc->lock, flags); + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_DEACTIVATE port %d (0..%d)\n", + __func__, hc->chan[dch->slot].port, + hc->ports - 1); + /* start deactivation */ + if (hc->ctype == HFC_TYPE_E1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_DEACTIVATE no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, + hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); + /* deactivate */ + dch->state = 1; + } + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + } else + ret = l1_event(dch->l1, hh->prim); + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfc_multi *hc = bch->hw; + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + mISDN_clear_bchannel(bch); + hc->chan[bch->slot].coeff_count = 0; + hc->chan[bch->slot].rx_off = 0; + hc->chan[bch->slot].conf = -1; + mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0); + spin_unlock_irqrestore(&hc->lock, flags); +} + +static int +handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_multi *hc = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (!skb->len) + break; + spin_lock_irqsave(&hc->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + hfcmulti_tx(hc, bch->slot); + ret = 0; + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", + __func__, bch->slot); + spin_lock_irqsave(&hc->lock, flags); + /* activate B-channel if not already activated */ + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + hc->chan[bch->slot].txpending = 0; + ret = mode_hfcmulti(hc, bch->slot, + ch->protocol, + hc->chan[bch->slot].slot_tx, + hc->chan[bch->slot].bank_tx, + hc->chan[bch->slot].slot_rx, + hc->chan[bch->slot].bank_rx); + if (!ret) { + if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf + && test_bit(HFC_CHIP_DTMF, &hc->chip)) { + /* start decoder */ + hc->dtmf = 1; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG + "%s: start dtmf decoder\n", + __func__); + HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | + V_RST_DTMF); + } + } + } else + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL, + GFP_KERNEL); + break; + case PH_CONTROL_REQ: + spin_lock_irqsave(&hc->lock, flags); + switch (hh->id) { + case HFC_SPL_LOOP_ON: /* set sample loop */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HFC_SPL_LOOP_ON (len = %d)\n", + __func__, skb->len); + ret = 0; + break; + case HFC_SPL_LOOP_OFF: /* set silence */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n", + __func__); + ret = 0; + break; + default: + printk(KERN_ERR + "%s: unknown PH_CONTROL_REQ info %x\n", + __func__, hh->id); + ret = -EINVAL; + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); /* locked there */ + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL, + GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * bchannel control function + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + struct dsp_features *features = + (struct dsp_features *)(*((u_long *)&cq->p1)); + struct hfc_multi *hc = bch->hw; + int slot_tx; + int bank_tx; + int slot_rx; + int bank_rx; + int num; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + ret = mISDN_ctrl_bchannel(bch, cq); + cq->op |= MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP; + break; + case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */ + ret = mISDN_ctrl_bchannel(bch, cq); + hc->chan[bch->slot].rx_off = !!cq->p1; + if (!hc->chan[bch->slot].rx_off) { + /* reset fifo on rx on */ + HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + } + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n", + __func__, bch->nr, hc->chan[bch->slot].rx_off); + break; + case MISDN_CTRL_FILL_EMPTY: + ret = mISDN_ctrl_bchannel(bch, cq); + hc->silence = bch->fill[0]; + memset(hc->silence_data, hc->silence, sizeof(hc->silence_data)); + break; + case MISDN_CTRL_HW_FEATURES: /* fill features structure */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_FEATURE request\n", + __func__); + /* create confirm */ + features->hfc_id = hc->id; + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) + features->hfc_dtmf = 1; + if (test_bit(HFC_CHIP_CONF, &hc->chip)) + features->hfc_conf = 1; + features->hfc_loops = 0; + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + features->hfc_echocanhw = 1; + } else { + features->pcm_id = hc->pcm; + features->pcm_slots = hc->slots; + features->pcm_banks = 2; + } + break; + case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */ + slot_tx = cq->p1 & 0xff; + bank_tx = cq->p1 >> 8; + slot_rx = cq->p2 & 0xff; + bank_rx = cq->p2 >> 8; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HFC_PCM_CONN slot %d bank %d (TX) " + "slot %d bank %d (RX)\n", + __func__, slot_tx, bank_tx, + slot_rx, bank_rx); + if (slot_tx < hc->slots && bank_tx <= 2 && + slot_rx < hc->slots && bank_rx <= 2) + hfcmulti_pcm(hc, bch->slot, + slot_tx, bank_tx, slot_rx, bank_rx); + else { + printk(KERN_WARNING + "%s: HFC_PCM_CONN slot %d bank %d (TX) " + "slot %d bank %d (RX) out of range\n", + __func__, slot_tx, bank_tx, + slot_rx, bank_rx); + ret = -EINVAL; + } + break; + case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_PCM_DISC\n", + __func__); + hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0); + break; + case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */ + num = cq->p1 & 0xff; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n", + __func__, num); + if (num <= 7) + hfcmulti_conf(hc, bch->slot, num); + else { + printk(KERN_WARNING + "%s: HW_CONF_JOIN conf %d out of range\n", + __func__, num); + ret = -EINVAL; + } + break; + case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__); + hfcmulti_conf(hc, bch->slot, -1); + break; + case MISDN_CTRL_HFC_ECHOCAN_ON: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__); + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + vpm_echocan_on(hc, bch->slot, cq->p1); + else + ret = -EINVAL; + break; + + case MISDN_CTRL_HFC_ECHOCAN_OFF: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n", + __func__); + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + vpm_echocan_off(hc, bch->slot); + else + ret = -EINVAL; + break; + default: + ret = mISDN_ctrl_bchannel(bch, cq); + break; + } + return ret; +} + +static int +hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_multi *hc = bch->hw; + int err = -EINVAL; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + deactivate_bchannel(bch); /* locked there */ + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + err = 0; + break; + case CONTROL_CHANNEL: + spin_lock_irqsave(&hc->lock, flags); + err = channel_bctrl(bch, arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return err; +} + +/* + * handle D-channel events + * + * handle state change event + */ +static void +ph_state_change(struct dchannel *dch) +{ + struct hfc_multi *hc; + int ch, i; + + if (!dch) { + printk(KERN_WARNING "%s: ERROR given dch is NULL\n", __func__); + return; + } + hc = dch->hw; + ch = dch->slot; + + if (hc->ctype == HFC_TYPE_E1) { + if (dch->dev.D.protocol == ISDN_P_TE_E1) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 TE (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 NT (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + } + switch (dch->state) { + case (1): + if (hc->e1_state != 1) { + for (i = 1; i <= 31; i++) { + /* reset fifos on e1 activation */ + HFC_outb_nodebug(hc, R_FIFO, + (i << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, + V_RES_F); + HFC_wait_nodebug(hc); + } + } + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + + default: + if (hc->e1_state != 1) + return; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + } + hc->e1_state = dch->state; + } else { + if (dch->dev.D.protocol == ISDN_P_TE_S0) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: S/T TE newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case (0): + l1_event(dch->l1, HW_RESET_IND); + break; + case (3): + l1_event(dch->l1, HW_DEACT_IND); + break; + case (5): + case (8): + l1_event(dch->l1, ANYSIGNAL); + break; + case (6): + l1_event(dch->l1, INFO2); + break; + case (7): + l1_event(dch->l1, INFO4_P8); + break; + } + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: S/T NT newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case (2): + if (hc->chan[ch].nt_timer == 0) { + hc->chan[ch].nt_timer = -1; + HFC_outb(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, 4 | + V_ST_LD_STA); /* G4 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 4); + dch->state = 4; + } else { + /* one extra count for the next event */ + hc->chan[ch].nt_timer = + nt_t1_count[poll_timer] + 1; + HFC_outb(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + /* allow G2 -> G3 transition */ + HFC_outb(hc, A_ST_WR_STATE, 2 | + V_SET_G2_G3); + } + break; + case (1): + hc->chan[ch].nt_timer = -1; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + case (4): + hc->chan[ch].nt_timer = -1; + break; + case (3): + hc->chan[ch].nt_timer = -1; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + } + } + } +} + +/* + * called for card mode init message + */ + +static void +hfcmulti_initmode(struct dchannel *dch) +{ + struct hfc_multi *hc = dch->hw; + u_char a_st_wr_state, r_e1_wr_sta; + int i, pt; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + i = dch->slot; + pt = hc->chan[i].port; + if (hc->ctype == HFC_TYPE_E1) { + /* E1 */ + hc->chan[hc->dnum[pt]].slot_tx = -1; + hc->chan[hc->dnum[pt]].slot_rx = -1; + hc->chan[hc->dnum[pt]].conf = -1; + if (hc->dnum[pt]) { + mode_hfcmulti(hc, dch->slot, dch->dev.D.protocol, + -1, 0, -1, 0); + timer_setup(&dch->timer, hfcmulti_dbusy_timer, 0); + } + for (i = 1; i <= 31; i++) { + if (!((1 << i) & hc->bmask[pt])) /* skip unused chan */ + continue; + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0); + } + } + if (hc->ctype == HFC_TYPE_E1 && pt == 0) { + /* E1, port 0 */ + dch = hc->chan[hc->dnum[0]].dch; + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[0]].cfg)) { + HFC_outb(hc, R_LOS0, 255); /* 2 ms */ + HFC_outb(hc, R_LOS1, 255); /* 512 ms */ + } + if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dnum[0]].cfg)) { + HFC_outb(hc, R_RX0, 0); + hc->hw.r_tx0 = 0 | V_OUT_EN; + } else { + HFC_outb(hc, R_RX0, 1); + hc->hw.r_tx0 = 1 | V_OUT_EN; + } + hc->hw.r_tx1 = V_ATX | V_NTRI; + HFC_outb(hc, R_TX0, hc->hw.r_tx0); + HFC_outb(hc, R_TX1, hc->hw.r_tx1); + HFC_outb(hc, R_TX_FR0, 0x00); + HFC_outb(hc, R_TX_FR1, 0xf8); + + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[0]].cfg)) + HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); + + HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); + + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[0]].cfg)) + HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); + + if (dch->dev.D.protocol == ISDN_P_NT_E1) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is NT-mode\n", + __func__); + r_e1_wr_sta = 0; /* G0 */ + hc->e1_getclock = 0; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is TE-mode\n", + __func__); + r_e1_wr_sta = 0; /* F0 */ + hc->e1_getclock = 1; + } + if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) + HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); + else + HFC_outb(hc, R_SYNC_OUT, 0); + if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip)) + hc->e1_getclock = 1; + if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip)) + hc->e1_getclock = 0; + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + /* SLAVE (clock master) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: E1 port is clock master " + "(clock from PCM)\n", __func__); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC); + } else { + if (hc->e1_getclock) { + /* MASTER (clock slave) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: E1 port is clock slave " + "(clock to PCM)\n", __func__); + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } else { + /* MASTER (clock master) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is " + "clock master " + "(clock from QUARTZ)\n", + __func__); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | + V_PCM_SYNC | V_JATT_OFF); + HFC_outb(hc, R_SYNC_OUT, 0); + } + } + HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */ + HFC_outb(hc, R_PWM_MD, V_PWM0_MD); + HFC_outb(hc, R_PWM0, 0x50); + HFC_outb(hc, R_PWM1, 0xff); + /* state machine setup */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized = 0; + plxsd_checksync(hc, 0); + } + } + if (hc->ctype != HFC_TYPE_E1) { + /* ST */ + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0); + timer_setup(&dch->timer, hfcmulti_dbusy_timer, 0); + hc->chan[i - 2].slot_tx = -1; + hc->chan[i - 2].slot_rx = -1; + hc->chan[i - 2].conf = -1; + mode_hfcmulti(hc, i - 2, ISDN_P_NONE, -1, 0, -1, 0); + hc->chan[i - 1].slot_tx = -1; + hc->chan[i - 1].slot_rx = -1; + hc->chan[i - 1].conf = -1; + mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0); + /* select interface */ + HFC_outb(hc, R_ST_SEL, pt); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + if (dch->dev.D.protocol == ISDN_P_NT_S0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: ST port %d is NT-mode\n", + __func__, pt); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt); + a_st_wr_state = 1; /* G1 */ + hc->hw.a_st_ctrl0[pt] = V_ST_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: ST port %d is TE-mode\n", + __func__, pt); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te); + a_st_wr_state = 2; /* F2 */ + hc->hw.a_st_ctrl0[pt] = 0; + } + if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) + hc->hw.a_st_ctrl0[pt] |= V_TX_LI; + if (hc->ctype == HFC_TYPE_XHFC) { + hc->hw.a_st_ctrl0[pt] |= 0x40 /* V_ST_PU_CTRL */; + HFC_outb(hc, 0x35 /* A_ST_CTRL3 */, + 0x7c << 1 /* V_ST_PULSE */); + } + /* line setup */ + HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[pt]); + /* disable E-channel */ + if ((dch->dev.D.protocol == ISDN_P_NT_S0) || + test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg)) + HFC_outb(hc, A_ST_CTRL1, V_E_IGNO); + else + HFC_outb(hc, A_ST_CTRL1, 0); + /* enable B-channel receive */ + HFC_outb(hc, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN); + /* state machine setup */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state); + hc->hw.r_sci_msk |= 1 << pt; + /* state machine interrupts */ + HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk); + /* unset sync on port */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[dch->slot].port); + plxsd_checksync(hc, 0); + } + } + if (debug & DEBUG_HFCMULTI_INIT) + printk("%s: done\n", __func__); +} + + +static int +open_dchannel(struct hfc_multi *hc, struct dchannel *dch, + struct channel_req *rq) +{ + int err = 0; + u_long flags; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, + dch->dev.id, __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if ((dch->dev.D.protocol != ISDN_P_NONE) && + (dch->dev.D.protocol != rq->protocol)) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: change protocol %x to %x\n", + __func__, dch->dev.D.protocol, rq->protocol); + } + if ((dch->dev.D.protocol == ISDN_P_TE_S0) && + (rq->protocol != ISDN_P_TE_S0)) + l1_event(dch->l1, CLOSE_CHANNEL); + if (dch->dev.D.protocol != rq->protocol) { + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(dch, hfcm_l1callback); + if (err) + return err; + } + dch->dev.D.protocol = rq->protocol; + spin_lock_irqsave(&hc->lock, flags); + hfcmulti_initmode(dch); + spin_unlock_irqrestore(&hc->lock, flags); + } + if (test_bit(FLG_ACTIVE, &dch->Flags)) + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + rq->ch = &dch->dev.D; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +static int +open_bchannel(struct hfc_multi *hc, struct dchannel *dch, + struct channel_req *rq) +{ + struct bchannel *bch; + int ch; + + if (!test_channelmap(rq->adr.channel, dch->dev.channelmap)) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if (hc->ctype == HFC_TYPE_E1) + ch = rq->adr.channel; + else + ch = (rq->adr.channel - 1) + (dch->slot - 2); + bch = hc->chan[ch].bch; + if (!bch) { + printk(KERN_ERR "%s:internal error ch %d has no bch\n", + __func__, ch); + return -EINVAL; + } + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + hc->chan[ch].rx_off = 0; + rq->ch = &bch->ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +/* + * device control function + */ +static int +channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) +{ + struct hfc_multi *hc = dch->hw; + int ret = 0; + int wd_mode, wd_cnt; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_L1_TIMER3; + break; + case MISDN_CTRL_HFC_WD_INIT: /* init the watchdog */ + wd_cnt = cq->p1 & 0xf; + wd_mode = !!(cq->p1 >> 4); + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: MISDN_CTRL_HFC_WD_INIT mode %s" + ", counter 0x%x\n", __func__, + wd_mode ? "AUTO" : "MANUAL", wd_cnt); + /* set the watchdog timer */ + HFC_outb(hc, R_TI_WD, poll_timer | (wd_cnt << 4)); + hc->hw.r_bert_wd_md = (wd_mode ? V_AUTO_WD_RES : 0); + if (hc->ctype == HFC_TYPE_XHFC) + hc->hw.r_bert_wd_md |= 0x40 /* V_WD_EN */; + /* init the watchdog register and reset the counter */ + HFC_outb(hc, R_BERT_WD_MD, hc->hw.r_bert_wd_md | V_WD_RES); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + /* enable the watchdog output for Speech-Design */ + HFC_outb(hc, R_GPIO_SEL, V_GPIO_SEL7); + HFC_outb(hc, R_GPIO_EN1, V_GPIO_EN15); + HFC_outb(hc, R_GPIO_OUT1, 0); + HFC_outb(hc, R_GPIO_OUT1, V_GPIO_OUT15); + } + break; + case MISDN_CTRL_HFC_WD_RESET: /* reset the watchdog counter */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: MISDN_CTRL_HFC_WD_RESET\n", + __func__); + HFC_outb(hc, R_BERT_WD_MD, hc->hw.r_bert_wd_md | V_WD_RES); + break; + case MISDN_CTRL_L1_TIMER3: + ret = l1_event(dch->l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_multi *hc = dch->hw; + struct channel_req *rq; + int err = 0; + u_long flags; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + switch (rq->protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + if (hc->ctype == HFC_TYPE_E1) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); /* locked there */ + break; + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + if (hc->ctype != HFC_TYPE_E1) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); /* locked there */ + break; + default: + spin_lock_irqsave(&hc->lock, flags); + err = open_bchannel(hc, dch, rq); + spin_unlock_irqrestore(&hc->lock, flags); + } + break; + case CLOSE_CHANNEL: + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, dch->dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + spin_lock_irqsave(&hc->lock, flags); + err = channel_dctrl(dch, arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + err = -EINVAL; + } + return err; +} + +static int +clockctl(void *priv, int enable) +{ + struct hfc_multi *hc = priv; + + hc->iclock_on = enable; + return 0; +} + +/* + * initialize the card + */ + +/* + * start timer irq, wait some time and check if we have interrupts. + * if not, reset chip and try again. + */ +static int +init_card(struct hfc_multi *hc) +{ + int err = -EIO; + u_long flags; + void __iomem *plx_acc; + u_long plx_flags; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + spin_lock_irqsave(&hc->lock, flags); + /* set interrupts but leave global interrupt disabled */ + hc->hw.r_irq_ctrl = V_FIFO_IRQ; + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + + if (request_irq(hc->irq, hfcmulti_interrupt, IRQF_SHARED, + "HFC-multi", hc)) { + printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", + hc->irq); + hc->irq = 0; + return -EIO; + } + + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc = hc->plx_membase + PLX_INTCSR; + writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE), + plx_acc); /* enable PCI & LINT1 irq */ + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", + __func__, hc->irq, hc->irqcnt); + err = init_chip(hc); + if (err) + goto error; + /* + * Finally enable IRQ output + * this is only allowed, if an IRQ routine is already + * established for this HFC, so don't do that earlier + */ + spin_lock_irqsave(&hc->lock, flags); + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + /* printk(KERN_DEBUG "no master irq set!!!\n"); */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ + /* turn IRQ off until chip is completely initialized */ + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", + __func__, hc->irq, hc->irqcnt); + if (hc->irqcnt) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); + + return 0; + } + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_INFO "ignoring missing interrupts\n"); + return 0; + } + + printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n", + hc->irq); + + err = -EIO; + +error: + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc = hc->plx_membase + PLX_INTCSR; + writew(0x00, plx_acc); /*disable IRQs*/ + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free irq %d\n", __func__, hc->irq); + if (hc->irq) { + free_irq(hc->irq, hc); + hc->irq = 0; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err); + return err; +} + +/* + * find pci device and set it up + */ + +static int +setup_pci(struct hfc_multi *hc, struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + + printk(KERN_INFO + "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", + m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); + + hc->pci_dev = pdev; + if (m->clock2) + test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); + + if (ent->vendor == PCI_VENDOR_ID_DIGIUM && + ent->device == PCI_DEVICE_ID_DIGIUM_HFC4S) { + test_and_set_bit(HFC_CHIP_B410P, &hc->chip); + test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); + test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + hc->slots = 32; + } + + if (hc->pci_dev->irq <= 0) { + printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n"); + return -EIO; + } + if (pci_enable_device(hc->pci_dev)) { + printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n"); + return -EIO; + } + hc->leds = m->leds; + hc->ledstate = 0xAFFEAFFE; + hc->opticalsupport = m->opticalsupport; + + hc->pci_iobase = 0; + hc->pci_membase = NULL; + hc->plx_membase = NULL; + + /* set memory access methods */ + if (m->io_mode) /* use mode from card config */ + hc->io_mode = m->io_mode; + switch (hc->io_mode) { + case HFC_IO_MODE_PLXSD: + test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip); + hc->slots = 128; /* required */ + hc->HFC_outb = HFC_outb_pcimem; + hc->HFC_inb = HFC_inb_pcimem; + hc->HFC_inw = HFC_inw_pcimem; + hc->HFC_wait = HFC_wait_pcimem; + hc->read_fifo = read_fifo_pcimem; + hc->write_fifo = write_fifo_pcimem; + hc->plx_origmembase = hc->pci_dev->resource[0].start; + /* MEMBASE 1 is PLX PCI Bridge */ + + if (!hc->plx_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI PLX bridge found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->plx_membase = ioremap(hc->plx_origmembase, 0x80); + if (!hc->plx_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap plx address space. " + "(internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + printk(KERN_INFO + "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n", + (u_long)hc->plx_membase, hc->plx_origmembase); + + hc->pci_origmembase = hc->pci_dev->resource[2].start; + /* MEMBASE 1 is PLX PCI Bridge */ + if (!hc->pci_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->pci_membase = ioremap(hc->pci_origmembase, 0x400); + if (!hc->pci_membase) { + printk(KERN_WARNING "HFC-multi: failed to remap io " + "address space. (internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + printk(KERN_INFO + "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d " + "leds-type %d\n", + hc->id, (u_long)hc->pci_membase, hc->pci_origmembase, + hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); + break; + case HFC_IO_MODE_PCIMEM: + hc->HFC_outb = HFC_outb_pcimem; + hc->HFC_inb = HFC_inb_pcimem; + hc->HFC_inw = HFC_inw_pcimem; + hc->HFC_wait = HFC_wait_pcimem; + hc->read_fifo = read_fifo_pcimem; + hc->write_fifo = write_fifo_pcimem; + hc->pci_origmembase = hc->pci_dev->resource[1].start; + if (!hc->pci_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->pci_membase = ioremap(hc->pci_origmembase, 256); + if (!hc->pci_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap io address space. " + "(internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ " + "%d HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase, + hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); + break; + case HFC_IO_MODE_REGIO: + hc->HFC_outb = HFC_outb_regio; + hc->HFC_inb = HFC_inb_regio; + hc->HFC_inw = HFC_inw_regio; + hc->HFC_wait = HFC_wait_regio; + hc->read_fifo = read_fifo_regio; + hc->write_fifo = write_fifo_regio; + hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start; + if (!hc->pci_iobase) { + printk(KERN_WARNING + "HFC-multi: No IO for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + if (!request_region(hc->pci_iobase, 8, "hfcmulti")) { + printk(KERN_WARNING "HFC-multi: failed to request " + "address space at 0x%08lx (internal error)\n", + hc->pci_iobase); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + printk(KERN_INFO + "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n", + m->vendor_name, m->card_name, (u_int) hc->pci_iobase, + hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO); + break; + default: + printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + pci_set_drvdata(hc->pci_dev, hc); + + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + return 0; +} + + +/* + * remove port + */ + +static void +release_port(struct hfc_multi *hc, struct dchannel *dch) +{ + int pt, ci, i = 0; + u_long flags; + struct bchannel *pb; + + ci = dch->slot; + pt = hc->chan[ci].port; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered for port %d\n", + __func__, pt + 1); + + if (pt >= hc->ports) { + printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", + __func__, pt + 1); + return; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: releasing port=%d\n", + __func__, pt + 1); + + if (dch->dev.D.protocol == ISDN_P_TE_S0) + l1_event(dch->l1, CLOSE_CHANNEL); + + hc->chan[ci].dch = NULL; + + if (hc->created[pt]) { + hc->created[pt] = 0; + mISDN_unregister_device(&dch->dev); + } + + spin_lock_irqsave(&hc->lock, flags); + + if (dch->timer.function) { + del_timer(&dch->timer); + dch->timer.function = NULL; + } + + if (hc->ctype == HFC_TYPE_E1) { /* E1 */ + /* remove sync */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized = 0; + plxsd_checksync(hc, 1); + } + /* free channels */ + for (i = 0; i <= 31; i++) { + if (!((1 << i) & hc->bmask[pt])) /* skip unused chan */ + continue; + if (hc->chan[i].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[i].port + 1, i); + pb = hc->chan[i].bch; + hc->chan[i].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[i].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + } + } else { + /* remove sync */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[ci].port); + plxsd_checksync(hc, 1); + } + /* free channels */ + if (hc->chan[ci - 2].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[ci - 2].port + 1, + ci - 2); + pb = hc->chan[ci - 2].bch; + hc->chan[ci - 2].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[ci - 2].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + if (hc->chan[ci - 1].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[ci - 1].port + 1, + ci - 1); + pb = hc->chan[ci - 1].bch; + hc->chan[ci - 1].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[ci - 1].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + } + + spin_unlock_irqrestore(&hc->lock, flags); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free port %d channel D(%d)\n", __func__, + pt+1, ci); + mISDN_freedchannel(dch); + kfree(dch); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done!\n", __func__); +} + +static void +release_card(struct hfc_multi *hc) +{ + u_long flags; + int ch; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: release card (%d) entered\n", + __func__, hc->id); + + /* unregister clock source */ + if (hc->iclock) + mISDN_unregister_clock(hc->iclock); + + /* disable and free irq */ + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + udelay(1000); + if (hc->irq) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free irq %d (hc=%p)\n", + __func__, hc->irq, hc); + free_irq(hc->irq, hc); + hc->irq = 0; + + } + + /* disable D-channels & B-channels */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: disable all channels (d and b)\n", + __func__); + for (ch = 0; ch <= 31; ch++) { + if (hc->chan[ch].dch) + release_port(hc, hc->chan[ch].dch); + } + + /* dimm leds */ + if (hc->leds) + hfcmulti_leds(hc); + + /* release hardware */ + release_io_hfcmulti(hc); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: remove instance from list\n", + __func__); + list_del(&hc->list); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: delete instance\n", __func__); + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: card successfully removed\n", + __func__); +} + +static void +init_e1_port_hw(struct hfc_multi *hc, struct hm_map *m) +{ + /* set optical line type */ + if (port[Port_cnt] & 0x001) { + if (!m->opticalsupport) { + printk(KERN_INFO + "This board has no optical " + "support\n"); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set optical " + "interfacs: card(%d) " + "port(%d)\n", + __func__, + HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_OPTICAL, + &hc->chan[hc->dnum[0]].cfg); + } + } + /* set LOS report */ + if (port[Port_cnt] & 0x004) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT set " + "LOS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_LOS, + &hc->chan[hc->dnum[0]].cfg); + } + /* set AIS report */ + if (port[Port_cnt] & 0x008) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT set " + "AIS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_AIS, + &hc->chan[hc->dnum[0]].cfg); + } + /* set SLIP report */ + if (port[Port_cnt] & 0x010) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set SLIP report: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_SLIP, + &hc->chan[hc->dnum[0]].cfg); + } + /* set RDI report */ + if (port[Port_cnt] & 0x020) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set RDI report: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_RDI, + &hc->chan[hc->dnum[0]].cfg); + } + /* set CRC-4 Mode */ + if (!(port[Port_cnt] & 0x100)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn on CRC4 report:" + " card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_CRC4, + &hc->chan[hc->dnum[0]].cfg); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn off CRC4" + " report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + } + /* set forced clock */ + if (port[Port_cnt] & 0x0200) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT force getting clock from " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip); + } else + if (port[Port_cnt] & 0x0400) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT force putting clock to " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip); + } + /* set JATT PLL */ + if (port[Port_cnt] & 0x0800) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT disable JATT PLL on " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip); + } + /* set elastic jitter buffer */ + if (port[Port_cnt] & 0x3000) { + hc->chan[hc->dnum[0]].jitter = (port[Port_cnt]>>12) & 0x3; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set elastic " + "buffer to %d: card(%d) port(%d)\n", + __func__, hc->chan[hc->dnum[0]].jitter, + HFC_cnt + 1, 1); + } else + hc->chan[hc->dnum[0]].jitter = 2; /* default */ +} + +static int +init_e1_port(struct hfc_multi *hc, struct hm_map *m, int pt) +{ + struct dchannel *dch; + struct bchannel *bch; + int ch, ret = 0; + char name[MISDN_MAX_IDLEN]; + int bcount = 0; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); + dch->hw = hc; + dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; + dch->slot = hc->dnum[pt]; + hc->chan[hc->dnum[pt]].dch = dch; + hc->chan[hc->dnum[pt]].port = pt; + hc->chan[hc->dnum[pt]].nt_timer = -1; + for (ch = 1; ch <= 31; ch++) { + if (!((1 << ch) & hc->bmask[pt])) /* skip unused channel */ + continue; + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL); + if (!hc->chan[ch].coeff) { + printk(KERN_ERR "%s: no memory for coeffs\n", + __func__); + ret = -ENOMEM; + kfree(bch); + goto free_chan; + } + bch->nr = ch; + bch->slot = ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM, poll >> 1); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = hfcm_bctrl; + bch->ch.nr = ch; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[ch].bch = bch; + hc->chan[ch].port = pt; + set_channelmap(bch->nr, dch->dev.channelmap); + bcount++; + } + dch->dev.nrbchan = bcount; + if (pt == 0) + init_e1_port_hw(hc, m); + if (hc->ports > 1) + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d-%d", + HFC_cnt + 1, pt+1); + else + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); + if (ret) + goto free_chan; + hc->created[pt] = 1; + return ret; +free_chan: + release_port(hc, dch); + return ret; +} + +static int +init_multi_port(struct hfc_multi *hc, int pt) +{ + struct dchannel *dch; + struct bchannel *bch; + int ch, i, ret = 0; + char name[MISDN_MAX_IDLEN]; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); + dch->hw = hc; + dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; + dch->dev.nrbchan = 2; + i = pt << 2; + dch->slot = i + 2; + hc->chan[i + 2].dch = dch; + hc->chan[i + 2].port = pt; + hc->chan[i + 2].nt_timer = -1; + for (ch = 0; ch < dch->dev.nrbchan; ch++) { + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL); + if (!hc->chan[i + ch].coeff) { + printk(KERN_ERR "%s: no memory for coeffs\n", + __func__); + ret = -ENOMEM; + kfree(bch); + goto free_chan; + } + bch->nr = ch + 1; + bch->slot = i + ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM, poll >> 1); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = hfcm_bctrl; + bch->ch.nr = ch + 1; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[i + ch].bch = bch; + hc->chan[i + ch].port = pt; + set_channelmap(bch->nr, dch->dev.channelmap); + } + /* set master clock */ + if (port[Port_cnt] & 0x001) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL set master clock: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + printk(KERN_ERR "Error: Master clock " + "for port(%d) of card(%d) is only" + " possible with TE-mode\n", + pt + 1, HFC_cnt + 1); + ret = -EINVAL; + goto free_chan; + } + if (hc->masterclk >= 0) { + printk(KERN_ERR "Error: Master clock " + "for port(%d) of card(%d) already " + "defined for port(%d)\n", + pt + 1, HFC_cnt + 1, hc->masterclk + 1); + ret = -EINVAL; + goto free_chan; + } + hc->masterclk = pt; + } + /* set transmitter line to non capacitive */ + if (port[Port_cnt] & 0x002) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL set non capacitive " + "transmitter: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + test_and_set_bit(HFC_CFG_NONCAP_TX, + &hc->chan[i + 2].cfg); + } + /* disable E-channel */ + if (port[Port_cnt] & 0x004) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL disable E-channel: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + test_and_set_bit(HFC_CFG_DIS_ECHANNEL, + &hc->chan[i + 2].cfg); + } + if (hc->ctype == HFC_TYPE_XHFC) { + snprintf(name, MISDN_MAX_IDLEN - 1, "xhfc.%d-%d", + HFC_cnt + 1, pt + 1); + ret = mISDN_register_device(&dch->dev, NULL, name); + } else { + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d-%d", + hc->ctype, HFC_cnt + 1, pt + 1); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); + } + if (ret) + goto free_chan; + hc->created[pt] = 1; + return ret; +free_chan: + release_port(hc, dch); + return ret; +} + +static int +hfcmulti_init(struct hm_map *m, struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret_err = 0; + int pt; + struct hfc_multi *hc; + u_long flags; + u_char dips = 0, pmj = 0; /* dip settings, port mode Jumpers */ + int i, ch; + u_int maskcheck; + + if (HFC_cnt >= MAX_CARDS) { + printk(KERN_ERR "too many cards (max=%d).\n", + MAX_CARDS); + return -EINVAL; + } + if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) { + printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but " + "type[%d] %d was supplied as module parameter\n", + m->vendor_name, m->card_name, m->type, HFC_cnt, + type[HFC_cnt] & 0xff); + printk(KERN_WARNING "HFC-MULTI: Load module without parameters " + "first, to see cards and their types."); + return -EINVAL; + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n", + __func__, m->vendor_name, m->card_name, m->type, + type[HFC_cnt]); + + /* allocate card+fifo structure */ + hc = kzalloc(sizeof(struct hfc_multi), GFP_KERNEL); + if (!hc) { + printk(KERN_ERR "No kmem for HFC-Multi card\n"); + return -ENOMEM; + } + spin_lock_init(&hc->lock); + hc->mtyp = m; + hc->ctype = m->type; + hc->ports = m->ports; + hc->id = HFC_cnt; + hc->pcm = pcm[HFC_cnt]; + hc->io_mode = iomode[HFC_cnt]; + if (hc->ctype == HFC_TYPE_E1 && dmask[E1_cnt]) { + /* fragment card */ + pt = 0; + maskcheck = 0; + for (ch = 0; ch <= 31; ch++) { + if (!((1 << ch) & dmask[E1_cnt])) + continue; + hc->dnum[pt] = ch; + hc->bmask[pt] = bmask[bmask_cnt++]; + if ((maskcheck & hc->bmask[pt]) + || (dmask[E1_cnt] & hc->bmask[pt])) { + printk(KERN_INFO + "HFC-E1 #%d has overlapping B-channels on fragment #%d\n", + E1_cnt + 1, pt); + kfree(hc); + return -EINVAL; + } + maskcheck |= hc->bmask[pt]; + printk(KERN_INFO + "HFC-E1 #%d uses D-channel on slot %d and a B-channel map of 0x%08x\n", + E1_cnt + 1, ch, hc->bmask[pt]); + pt++; + } + hc->ports = pt; + } + if (hc->ctype == HFC_TYPE_E1 && !dmask[E1_cnt]) { + /* default card layout */ + hc->dnum[0] = 16; + hc->bmask[0] = 0xfffefffe; + hc->ports = 1; + } + + /* set chip specific features */ + hc->masterclk = -1; + if (type[HFC_cnt] & 0x100) { + test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); + hc->silence = 0xff; /* ulaw silence */ + } else + hc->silence = 0x2a; /* alaw silence */ + if ((poll >> 1) > sizeof(hc->silence_data)) { + printk(KERN_ERR "HFCMULTI error: silence_data too small, " + "please fix\n"); + kfree(hc); + return -EINVAL; + } + for (i = 0; i < (poll >> 1); i++) + hc->silence_data[i] = hc->silence; + + if (hc->ctype != HFC_TYPE_XHFC) { + if (!(type[HFC_cnt] & 0x200)) + test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); + test_and_set_bit(HFC_CHIP_CONF, &hc->chip); + } + + if (type[HFC_cnt] & 0x800) + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + if (type[HFC_cnt] & 0x1000) { + test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); + test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + } + if (type[HFC_cnt] & 0x4000) + test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip); + if (type[HFC_cnt] & 0x8000) + test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip); + hc->slots = 32; + if (type[HFC_cnt] & 0x10000) + hc->slots = 64; + if (type[HFC_cnt] & 0x20000) + hc->slots = 128; + if (type[HFC_cnt] & 0x80000) { + test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip); + hc->wdcount = 0; + hc->wdbyte = V_GPIO_OUT2; + printk(KERN_NOTICE "Watchdog enabled\n"); + } + + if (pdev && ent) + /* setup pci, hc->slots may change due to PLXSD */ + ret_err = setup_pci(hc, pdev, ent); + else +#ifdef CONFIG_MISDN_HFCMULTI_8xx + ret_err = setup_embedded(hc, m); +#else + { + printk(KERN_WARNING "Embedded IO Mode not selected\n"); + ret_err = -EIO; + } +#endif + if (ret_err) { + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + return ret_err; + } + + hc->HFC_outb_nodebug = hc->HFC_outb; + hc->HFC_inb_nodebug = hc->HFC_inb; + hc->HFC_inw_nodebug = hc->HFC_inw; + hc->HFC_wait_nodebug = hc->HFC_wait; +#ifdef HFC_REGISTER_DEBUG + hc->HFC_outb = HFC_outb_debug; + hc->HFC_inb = HFC_inb_debug; + hc->HFC_inw = HFC_inw_debug; + hc->HFC_wait = HFC_wait_debug; +#endif + /* create channels */ + for (pt = 0; pt < hc->ports; pt++) { + if (Port_cnt >= MAX_PORTS) { + printk(KERN_ERR "too many ports (max=%d).\n", + MAX_PORTS); + ret_err = -EINVAL; + goto free_card; + } + if (hc->ctype == HFC_TYPE_E1) + ret_err = init_e1_port(hc, m, pt); + else + ret_err = init_multi_port(hc, pt); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: Registering D-channel, card(%d) port(%d) " + "result %d\n", + __func__, HFC_cnt + 1, pt + 1, ret_err); + + if (ret_err) { + while (pt) { /* release already registered ports */ + pt--; + if (hc->ctype == HFC_TYPE_E1) + release_port(hc, + hc->chan[hc->dnum[pt]].dch); + else + release_port(hc, + hc->chan[(pt << 2) + 2].dch); + } + goto free_card; + } + if (hc->ctype != HFC_TYPE_E1) + Port_cnt++; /* for each S0 port */ + } + if (hc->ctype == HFC_TYPE_E1) { + Port_cnt++; /* for each E1 port */ + E1_cnt++; + } + + /* disp switches */ + switch (m->dip_type) { + case DIP_4S: + /* + * Get DIP setting for beroNet 1S/2S/4S cards + * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) + + * GPI 19/23 (R_GPI_IN2)) + */ + dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) | + ((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) | + (~HFC_inb(hc, R_GPI_IN2) & 0x08); + + /* Port mode (TE/NT) jumpers */ + pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4) & 0xf); + + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + pmj = ~pmj & 0xf; + + printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n", + m->vendor_name, m->card_name, dips, pmj); + break; + case DIP_8S: + /* + * Get DIP Setting for beroNet 8S0+ cards + * Enable PCI auxbridge function + */ + HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); + /* prepare access to auxport */ + outw(0x4000, hc->pci_iobase + 4); + /* + * some dummy reads are required to + * read valid DIP switch data + */ + dips = inb(hc->pci_iobase); + dips = inb(hc->pci_iobase); + dips = inb(hc->pci_iobase); + dips = ~inb(hc->pci_iobase) & 0x3F; + outw(0x0, hc->pci_iobase + 4); + /* disable PCI auxbridge function */ + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + printk(KERN_INFO "%s: %s DIPs(0x%x)\n", + m->vendor_name, m->card_name, dips); + break; + case DIP_E1: + /* + * get DIP Setting for beroNet E1 cards + * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0) + */ + dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0) >> 4; + printk(KERN_INFO "%s: %s DIPs(0x%x)\n", + m->vendor_name, m->card_name, dips); + break; + } + + /* add to list */ + spin_lock_irqsave(&HFClock, flags); + list_add_tail(&hc->list, &HFClist); + spin_unlock_irqrestore(&HFClock, flags); + + /* use as clock source */ + if (clock == HFC_cnt + 1) + hc->iclock = mISDN_register_clock("HFCMulti", 0, clockctl, hc); + + /* initialize hardware */ + hc->irq = (m->irq) ? : hc->pci_dev->irq; + ret_err = init_card(hc); + if (ret_err) { + printk(KERN_ERR "init card returns %d\n", ret_err); + release_card(hc); + return ret_err; + } + + /* start IRQ and return */ + spin_lock_irqsave(&hc->lock, flags); + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + return 0; + +free_card: + release_io_hfcmulti(hc); + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + return ret_err; +} + +static void hfc_remove_pci(struct pci_dev *pdev) +{ + struct hfc_multi *card = pci_get_drvdata(pdev); + u_long flags; + + if (debug) + printk(KERN_INFO "removing hfc_multi card vendor:%x " + "device:%x subvendor:%x subdevice:%x\n", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); + + if (card) { + spin_lock_irqsave(&HFClock, flags); + release_card(card); + spin_unlock_irqrestore(&HFClock, flags); + } else { + if (debug) + printk(KERN_DEBUG "%s: drvdata already removed\n", + __func__); + } +} + +#define VENDOR_CCD "Cologne Chip AG" +#define VENDOR_BN "beroNet GmbH" +#define VENDOR_DIG "Digium Inc." +#define VENDOR_JH "Junghanns.NET GmbH" +#define VENDOR_PRIM "PrimuX" + +static const struct hm_map hfcm_map[] = { + /*0*/ {VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0, 0}, + /*1*/ {VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, + /*2*/ {VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, + /*3*/ {VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, + /*4*/ {VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0, 0}, + /*5*/ {VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0, 0}, + /*6*/ {VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, + /*7*/ {VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0, 0}, + /*8*/ {VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO, 0}, + /*9*/ {VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0, 0}, + /*10*/ {VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0, 0}, + /*11*/ {VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0, 0}, + + /*12*/ {VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0, 0}, + /*13*/ {VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S, + HFC_IO_MODE_REGIO, 0}, + /*14*/ {VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0, 0}, + /*15*/ {VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0, 0}, + + /*16*/ {VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0, 0}, + /*17*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0, 0}, + /*18*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0, 0}, + + /*19*/ {VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, + /*20*/ {VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0, 0}, + /*21*/ {VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, + /*22*/ {VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, + + /*23*/ {VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0, 0}, + /*24*/ {VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0, 0}, + /*25*/ {VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0, 0}, + + /*26*/ {VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0, + HFC_IO_MODE_PLXSD, 0}, + /*27*/ {VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0, + HFC_IO_MODE_PLXSD, 0}, + /*28*/ {VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0, 0}, + /*29*/ {VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0, 0}, + /*30*/ {VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0, 0}, + /*31*/ {VENDOR_CCD, "XHFC-4S Speech Design", 5, 4, 0, 0, 0, 0, + HFC_IO_MODE_EMBSD, XHFC_IRQ}, + /*32*/ {VENDOR_JH, "HFC-8S (junghanns)", 8, 8, 1, 0, 0, 0, 0, 0}, + /*33*/ {VENDOR_BN, "HFC-2S Beronet Card PCIe", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, + /*34*/ {VENDOR_BN, "HFC-4S Beronet Card PCIe", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, +}; + +#undef H +#define H(x) ((unsigned long)&hfcm_map[x]) +static const struct pci_device_id hfmultipci_ids[] = { + + /* Cards with HFC-4S Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */ + { PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, + PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + 0xb761, 0, 0, H(33)}, /* BN2S PCIe */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + 0xb762, 0, 0, H(34)}, /* BN4S PCIe */ + + /* Cards with HFC-8S Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, /* IOB8ST Recording */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_JH8S, 0, 0, H(32)}, /* Junganns 8S */ + + + /* Cards with HFC-E1 Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */ + + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */ + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */ + + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_JHSE1, 0, 0, H(25)}, /* Junghanns E1 */ + + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFC4S), 0 }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFC8S), 0 }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFCE1), 0 }, + {0, } +}; +#undef H + +MODULE_DEVICE_TABLE(pci, hfmultipci_ids); + +static int +hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + int ret; + + if (m == NULL && ent->vendor == PCI_VENDOR_ID_CCD && ( + ent->device == PCI_DEVICE_ID_CCD_HFC4S || + ent->device == PCI_DEVICE_ID_CCD_HFC8S || + ent->device == PCI_DEVICE_ID_CCD_HFCE1)) { + printk(KERN_ERR + "Unknown HFC multiport controller (vendor:%04x device:%04x " + "subvendor:%04x subdevice:%04x)\n", pdev->vendor, + pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device); + printk(KERN_ERR + "Please contact the driver maintainer for support.\n"); + return -ENODEV; + } + ret = hfcmulti_init(m, pdev, ent); + if (ret) + return ret; + HFC_cnt++; + printk(KERN_INFO "%d devices registered\n", HFC_cnt); + return 0; +} + +static struct pci_driver hfcmultipci_driver = { + .name = "hfc_multi", + .probe = hfcmulti_probe, + .remove = hfc_remove_pci, + .id_table = hfmultipci_ids, +}; + +static void __exit +HFCmulti_cleanup(void) +{ + struct hfc_multi *card, *next; + + /* get rid of all devices of this driver */ + list_for_each_entry_safe(card, next, &HFClist, list) + release_card(card); + pci_unregister_driver(&hfcmultipci_driver); +} + +static int __init +HFCmulti_init(void) +{ + int err; + int i, xhfc = 0; + struct hm_map m; + + printk(KERN_INFO "mISDN: HFC-multi driver %s\n", HFC_MULTI_VERSION); + +#ifdef IRQ_DEBUG + printk(KERN_DEBUG "%s: IRQ_DEBUG IS ENABLED!\n", __func__); +#endif + + spin_lock_init(&HFClock); + spin_lock_init(&plx_lock); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: init entered\n", __func__); + + switch (poll) { + case 0: + poll_timer = 6; + poll = 128; + break; + case 8: + poll_timer = 2; + break; + case 16: + poll_timer = 3; + break; + case 32: + poll_timer = 4; + break; + case 64: + poll_timer = 5; + break; + case 128: + poll_timer = 6; + break; + case 256: + poll_timer = 7; + break; + default: + printk(KERN_ERR + "%s: Wrong poll value (%d).\n", __func__, poll); + err = -EINVAL; + return err; + + } + + if (!clock) + clock = 1; + + /* Register the embedded devices. + * This should be done before the PCI cards registration */ + switch (hwid) { + case HWID_MINIP4: + xhfc = 1; + m = hfcm_map[31]; + break; + case HWID_MINIP8: + xhfc = 2; + m = hfcm_map[31]; + break; + case HWID_MINIP16: + xhfc = 4; + m = hfcm_map[31]; + break; + default: + xhfc = 0; + } + + for (i = 0; i < xhfc; ++i) { + err = hfcmulti_init(&m, NULL, NULL); + if (err) { + printk(KERN_ERR "error registering embedded driver: " + "%x\n", err); + return err; + } + HFC_cnt++; + printk(KERN_INFO "%d devices registered\n", HFC_cnt); + } + + /* Register the PCI cards */ + err = pci_register_driver(&hfcmultipci_driver); + if (err < 0) { + printk(KERN_ERR "error registering pci driver: %x\n", err); + return err; + } + + return 0; +} + + +module_init(HFCmulti_init); +module_exit(HFCmulti_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c new file mode 100644 index 000000000..53349850f --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -0,0 +1,2359 @@ +/* + * + * hfcpci.c low level driver for CCD's hfc-pci based cards + * + * Author Werner Cornelius (werner@isdn4linux.de) + * based on existing driver for CCD hfc ISA cards + * type approval valid for HFC-S PCI A based card + * + * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Module options: + * + * debug: + * NOTE: only one poll value must be given for all cards + * See hfc_pci.h for debug flags. + * + * poll: + * NOTE: only one poll value must be given for all cards + * Give the number of samples for each fifo process. + * By default 128 is used. Decrease to reduce delay, increase to + * reduce cpu load. If unsure, don't mess with it! + * A value of 128 will use controller's interrupt. Other values will + * use kernel timer, because the controller will not allow lower values + * than 128. + * Also note that the value depends on the kernel timer frequency. + * If kernel uses a frequency of 1000 Hz, steps of 8 samples are possible. + * If the kernel uses 100 Hz, steps of 80 samples are possible. + * If the kernel uses 300 Hz, steps of about 26 samples are possible. + * + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> + +#include "hfc_pci.h" + +static const char *hfcpci_revision = "2.0"; + +static int HFC_cnt; +static uint debug; +static uint poll, tics; +static struct timer_list hfc_tl; +static unsigned long hfc_jiffies; + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); + +enum { + HFC_CCD_2BD0, + HFC_CCD_B000, + HFC_CCD_B006, + HFC_CCD_B007, + HFC_CCD_B008, + HFC_CCD_B009, + HFC_CCD_B00A, + HFC_CCD_B00B, + HFC_CCD_B00C, + HFC_CCD_B100, + HFC_CCD_B700, + HFC_CCD_B701, + HFC_ASUS_0675, + HFC_BERKOM_A1T, + HFC_BERKOM_TCONCEPT, + HFC_ANIGMA_MC145575, + HFC_ZOLTRIX_2BD0, + HFC_DIGI_DF_M_IOM2_E, + HFC_DIGI_DF_M_E, + HFC_DIGI_DF_M_IOM2_A, + HFC_DIGI_DF_M_A, + HFC_ABOCOM_2BD1, + HFC_SITECOM_DC105V2, +}; + +struct hfcPCI_hw { + unsigned char cirm; + unsigned char ctmt; + unsigned char clkdel; + unsigned char states; + unsigned char conn; + unsigned char mst_m; + unsigned char int_m1; + unsigned char int_m2; + unsigned char sctrl; + unsigned char sctrl_r; + unsigned char sctrl_e; + unsigned char trm; + unsigned char fifo_en; + unsigned char bswapped; + unsigned char protocol; + int nt_timer; + unsigned char __iomem *pci_io; /* start of PCI IO memory */ + dma_addr_t dmahandle; + void *fifos; /* FIFO memory */ + int last_bfifo_cnt[2]; + /* marker saving last b-fifo frame count */ + struct timer_list timer; +}; + +#define HFC_CFG_MASTER 1 +#define HFC_CFG_SLAVE 2 +#define HFC_CFG_PCM 3 +#define HFC_CFG_2HFC 4 +#define HFC_CFG_SLAVEHFC 5 +#define HFC_CFG_NEG_F0 6 +#define HFC_CFG_SW_DD_DU 7 + +#define FLG_HFC_TIMER_T1 16 +#define FLG_HFC_TIMER_T3 17 + +#define NT_T1_COUNT 1120 /* number of 3.125ms interrupts (3.5s) */ +#define NT_T3_COUNT 31 /* number of 3.125ms interrupts (97 ms) */ +#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + + +struct hfc_pci { + u_char subtype; + u_char chanlimit; + u_char initdone; + u_long cfg; + u_int irq; + u_int irqcnt; + struct pci_dev *pdev; + struct hfcPCI_hw hw; + spinlock_t lock; /* card lock */ + struct dchannel dch; + struct bchannel bch[2]; +}; + +/* Interface functions */ +static void +enable_hwirq(struct hfc_pci *hc) +{ + hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE; + Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); +} + +static void +disable_hwirq(struct hfc_pci *hc) +{ + hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE); + Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); +} + +/* + * free hardware resources used by driver + */ +static void +release_io_hfcpci(struct hfc_pci *hc) +{ + /* disable memory mapped ports + busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, 0); + del_timer(&hc->hw.timer); + pci_free_consistent(hc->pdev, 0x8000, hc->hw.fifos, hc->hw.dmahandle); + iounmap(hc->hw.pci_io); +} + +/* + * set mode (NT or TE) + */ +static void +hfcpci_setmode(struct hfc_pci *hc) +{ + if (hc->hw.protocol == ISDN_P_NT_S0) { + hc->hw.clkdel = CLKDEL_NT; /* ST-Bit delay for NT-Mode */ + hc->hw.sctrl |= SCTRL_MODE_NT; /* NT-MODE */ + hc->hw.states = 1; /* G1 */ + } else { + hc->hw.clkdel = CLKDEL_TE; /* ST-Bit delay for TE-Mode */ + hc->hw.sctrl &= ~SCTRL_MODE_NT; /* TE-MODE */ + hc->hw.states = 2; /* F2 */ + } + Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states); + udelay(10); + Write_hfc(hc, HFCPCI_STATES, hc->hw.states | 0x40); /* Deactivate */ + Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); +} + +/* + * function called to reset the HFC PCI chip. A complete software reset of chip + * and fifos is done. + */ +static void +reset_hfcpci(struct hfc_pci *hc) +{ + u_char val; + int cnt = 0; + + printk(KERN_DEBUG "reset_hfcpci: entered\n"); + val = Read_hfc(hc, HFCPCI_CHIP_ID); + printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val); + /* enable memory mapped ports, disable busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); + disable_hwirq(hc); + /* enable memory ports + busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, + PCI_ENA_MEMIO + PCI_ENA_MASTER); + val = Read_hfc(hc, HFCPCI_STATUS); + printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val); + hc->hw.cirm = HFCPCI_RESET; /* Reset On */ + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); + set_current_state(TASK_UNINTERRUPTIBLE); + mdelay(10); /* Timeout 10ms */ + hc->hw.cirm = 0; /* Reset Off */ + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); + val = Read_hfc(hc, HFCPCI_STATUS); + printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val); + while (cnt < 50000) { /* max 50000 us */ + udelay(5); + cnt += 5; + val = Read_hfc(hc, HFCPCI_STATUS); + if (!(val & 2)) + break; + } + printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt); + + hc->hw.fifo_en = 0x30; /* only D fifos enabled */ + + hc->hw.bswapped = 0; /* no exchange */ + hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; + hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */ + hc->hw.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ + hc->hw.sctrl_r = 0; + hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE; /* S/T Auto awake */ + hc->hw.mst_m = 0; + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; /* HFC Master Mode */ + if (test_bit(HFC_CFG_NEG_F0, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_F0_NEGATIV; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + + hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | + HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + + /* Clear already pending ints */ + val = Read_hfc(hc, HFCPCI_INT_S1); + + /* set NT/TE mode */ + hfcpci_setmode(hc); + + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + + /* + * Init GCI/IOM2 in master mode + * Slots 0 and 1 are set for B-chan 1 and 2 + * D- and monitor/CI channel are not enabled + * STIO1 is used as output for data, B1+B2 from ST->IOM+HFC + * STIO2 is used as data input, B1+B2 from IOM->ST + * ST B-channel send disabled -> continuous 1s + * The IOM slots are always enabled + */ + if (test_bit(HFC_CFG_PCM, &hc->cfg)) { + /* set data flow directions: connect B1,B2: HFC to/from PCM */ + hc->hw.conn = 0x09; + } else { + hc->hw.conn = 0x36; /* set data flow directions */ + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { + Write_hfc(hc, HFCPCI_B1_SSL, 0xC0); + Write_hfc(hc, HFCPCI_B2_SSL, 0xC1); + Write_hfc(hc, HFCPCI_B1_RSL, 0xC0); + Write_hfc(hc, HFCPCI_B2_RSL, 0xC1); + } else { + Write_hfc(hc, HFCPCI_B1_SSL, 0x80); + Write_hfc(hc, HFCPCI_B2_SSL, 0x81); + Write_hfc(hc, HFCPCI_B1_RSL, 0x80); + Write_hfc(hc, HFCPCI_B2_RSL, 0x81); + } + } + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + val = Read_hfc(hc, HFCPCI_INT_S2); +} + +/* + * Timer function called when kernel timer expires + */ +static void +hfcpci_Timer(struct timer_list *t) +{ + struct hfc_pci *hc = from_timer(hc, t, hw.timer); + hc->hw.timer.expires = jiffies + 75; + /* WD RESET */ +/* + * WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80); + * add_timer(&hc->hw.timer); + */ +} + + +/* + * select a b-channel entry matching and active + */ +static struct bchannel * +Sel_BCS(struct hfc_pci *hc, int channel) +{ + if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) && + (hc->bch[0].nr & channel)) + return &hc->bch[0]; + else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) && + (hc->bch[1].nr & channel)) + return &hc->bch[1]; + else + return NULL; +} + +/* + * clear the desired B-channel rx fifo + */ +static void +hfcpci_clear_fifo_rx(struct hfc_pci *hc, int fifo) +{ + u_char fifo_state; + struct bzfifo *bzr; + + if (fifo) { + bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX; + } else { + bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX; + } + if (fifo_state) + hc->hw.fifo_en ^= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + hc->hw.last_bfifo_cnt[fifo] = 0; + bzr->f1 = MAX_B_FRAMES; + bzr->f2 = bzr->f1; /* init F pointers to remain constant */ + bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); + bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16( + le16_to_cpu(bzr->za[MAX_B_FRAMES].z1)); + if (fifo_state) + hc->hw.fifo_en |= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); +} + +/* + * clear the desired B-channel tx fifo + */ +static void hfcpci_clear_fifo_tx(struct hfc_pci *hc, int fifo) +{ + u_char fifo_state; + struct bzfifo *bzt; + + if (fifo) { + bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX; + } else { + bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX; + } + if (fifo_state) + hc->hw.fifo_en ^= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) " + "z1(%x) z2(%x) state(%x)\n", + fifo, bzt->f1, bzt->f2, + le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), + le16_to_cpu(bzt->za[MAX_B_FRAMES].z2), + fifo_state); + bzt->f2 = MAX_B_FRAMES; + bzt->f1 = bzt->f2; /* init F pointers to remain constant */ + bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); + bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 2); + if (fifo_state) + hc->hw.fifo_en |= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)\n", + fifo, bzt->f1, bzt->f2, + le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), + le16_to_cpu(bzt->za[MAX_B_FRAMES].z2)); +} + +/* + * read a complete B-frame out of the buffer + */ +static void +hfcpci_empty_bfifo(struct bchannel *bch, struct bzfifo *bz, + u_char *bdata, int count) +{ + u_char *ptr, *ptr1, new_f2; + int maxlen, new_z2; + struct zt *zp; + + if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO)) + printk(KERN_DEBUG "hfcpci_empty_fifo\n"); + zp = &bz->za[bz->f2]; /* point to Z-Regs */ + new_z2 = le16_to_cpu(zp->z2) + count; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; + if ((count > MAX_DATA_SIZE + 3) || (count < 4) || + (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) { + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "hfcpci_empty_fifo: incoming packet " + "invalid length %d or crc\n", count); +#ifdef ERROR_STATISTIC + bch->err_inv++; +#endif + bz->za[new_f2].z2 = cpu_to_le16(new_z2); + bz->f2 = new_f2; /* next buffer */ + } else { + bch->rx_skb = mI_alloc_skb(count - 3, GFP_ATOMIC); + if (!bch->rx_skb) { + printk(KERN_WARNING "HFCPCI: receive out of memory\n"); + return; + } + count -= 3; + ptr = skb_put(bch->rx_skb, count); + + if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = count; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - + le16_to_cpu(zp->z2); /* maximum */ + + ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL); + /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + count -= maxlen; + + if (count) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, count); /* rest */ + } + bz->za[new_f2].z2 = cpu_to_le16(new_z2); + bz->f2 = new_f2; /* next buffer */ + recv_Bchannel(bch, MISDN_ID_ANY, false); + } +} + +/* + * D-channel receive procedure + */ +static int +receive_dmsg(struct hfc_pci *hc) +{ + struct dchannel *dch = &hc->dch; + int maxlen; + int rcnt, total; + int count = 5; + u_char *ptr, *ptr1; + struct dfifo *df; + struct zt *zp; + + df = &((union fifo_area *)(hc->hw.fifos))->d_chan.d_rx; + while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { + zp = &df->za[df->f2 & D_FREG_MASK]; + rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); + if (rcnt < 0) + rcnt += D_FIFO_SIZE; + rcnt++; + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG + "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)\n", + df->f1, df->f2, + le16_to_cpu(zp->z1), + le16_to_cpu(zp->z2), + rcnt); + + if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || + (df->data[le16_to_cpu(zp->z1)])) { + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG + "empty_fifo hfcpci packet inv. len " + "%d or crc %d\n", + rcnt, + df->data[le16_to_cpu(zp->z1)]); +#ifdef ERROR_STATISTIC + cs->err_rx++; +#endif + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | + (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = + cpu_to_le16((le16_to_cpu(zp->z2) + rcnt) & + (D_FIFO_SIZE - 1)); + } else { + dch->rx_skb = mI_alloc_skb(rcnt - 3, GFP_ATOMIC); + if (!dch->rx_skb) { + printk(KERN_WARNING + "HFC-PCI: D receive out of memory\n"); + break; + } + total = rcnt; + rcnt -= 3; + ptr = skb_put(dch->rx_skb, rcnt); + + if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE) + maxlen = rcnt; /* complete transfer */ + else + maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2); + /* maximum */ + + ptr1 = df->data + le16_to_cpu(zp->z2); + /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; + + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = df->data; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | + (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16(( + le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1)); + recv_Dchannel(dch); + } + } + return 1; +} + +/* + * check for transparent receive data and read max one 'poll' size if avail + */ +static void +hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *rxbz, + struct bzfifo *txbz, u_char *bdata) +{ + __le16 *z1r, *z2r, *z1t, *z2t; + int new_z2, fcnt_rx, fcnt_tx, maxlen; + u_char *ptr, *ptr1; + + z1r = &rxbz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ + z2r = z1r + 1; + z1t = &txbz->za[MAX_B_FRAMES].z1; + z2t = z1t + 1; + + fcnt_rx = le16_to_cpu(*z1r) - le16_to_cpu(*z2r); + if (!fcnt_rx) + return; /* no data avail */ + + if (fcnt_rx <= 0) + fcnt_rx += B_FIFO_SIZE; /* bytes actually buffered */ + new_z2 = le16_to_cpu(*z2r) + fcnt_rx; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + + fcnt_tx = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); + if (fcnt_tx <= 0) + fcnt_tx += B_FIFO_SIZE; + /* fcnt_tx contains available bytes in tx-fifo */ + fcnt_tx = B_FIFO_SIZE - fcnt_tx; + /* remaining bytes to send (bytes in tx-fifo) */ + + if (test_bit(FLG_RX_OFF, &bch->Flags)) { + bch->dropcnt += fcnt_rx; + *z2r = cpu_to_le16(new_z2); + return; + } + maxlen = bchannel_get_rxbuf(bch, fcnt_rx); + if (maxlen < 0) { + pr_warning("B%d: No bufferspace for %d bytes\n", + bch->nr, fcnt_rx); + } else { + ptr = skb_put(bch->rx_skb, fcnt_rx); + if (le16_to_cpu(*z2r) + fcnt_rx <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = fcnt_rx; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r); + /* maximum */ + + ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL); + /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + fcnt_rx -= maxlen; + + if (fcnt_rx) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, fcnt_rx); /* rest */ + } + recv_Bchannel(bch, fcnt_tx, false); /* bch, id, !force */ + } + *z2r = cpu_to_le16(new_z2); /* new position */ +} + +/* + * B-channel main receive routine + */ +static void +main_rec_hfcpci(struct bchannel *bch) +{ + struct hfc_pci *hc = bch->hw; + int rcnt, real_fifo; + int receive = 0, count = 5; + struct bzfifo *txbz, *rxbz; + u_char *bdata; + struct zt *zp; + + if ((bch->nr & 2) && (!hc->hw.bswapped)) { + rxbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; + txbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2; + real_fifo = 1; + } else { + rxbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1; + txbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b1; + real_fifo = 0; + } +Begin: + count--; + if (rxbz->f1 != rxbz->f2) { + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci rec ch(%x) f1(%d) f2(%d)\n", + bch->nr, rxbz->f1, rxbz->f2); + zp = &rxbz->za[rxbz->f2]; + + rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); + if (rcnt < 0) + rcnt += B_FIFO_SIZE; + rcnt++; + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)\n", + bch->nr, le16_to_cpu(zp->z1), + le16_to_cpu(zp->z2), rcnt); + hfcpci_empty_bfifo(bch, rxbz, bdata, rcnt); + rcnt = rxbz->f1 - rxbz->f2; + if (rcnt < 0) + rcnt += MAX_B_FRAMES + 1; + if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) { + rcnt = 0; + hfcpci_clear_fifo_rx(hc, real_fifo); + } + hc->hw.last_bfifo_cnt[real_fifo] = rcnt; + if (rcnt > 1) + receive = 1; + else + receive = 0; + } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { + hfcpci_empty_fifo_trans(bch, rxbz, txbz, bdata); + return; + } else + receive = 0; + if (count && receive) + goto Begin; + +} + +/* + * D-channel send routine + */ +static void +hfcpci_fill_dfifo(struct hfc_pci *hc) +{ + struct dchannel *dch = &hc->dch; + int fcnt; + int count, new_z1, maxlen; + struct dfifo *df; + u_char *src, *dst, new_f1; + + if ((dch->debug & DEBUG_HW_DCHANNEL) && !(dch->debug & DEBUG_HW_DFIFO)) + printk(KERN_DEBUG "%s\n", __func__); + + if (!dch->tx_skb) + return; + count = dch->tx_skb->len - dch->tx_idx; + if (count <= 0) + return; + df = &((union fifo_area *) (hc->hw.fifos))->d_chan.d_tx; + + if (dch->debug & DEBUG_HW_DFIFO) + printk(KERN_DEBUG "%s:f1(%d) f2(%d) z1(f1)(%x)\n", __func__, + df->f1, df->f2, + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1)); + fcnt = df->f1 - df->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_D_FRAMES - 1)) { + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG + "hfcpci_fill_Dfifo more as 14 frames\n"); +#ifdef ERROR_STATISTIC + cs->err_tx++; +#endif + return; + } + /* now determine free bytes in FIFO buffer */ + maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) - + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1; + if (maxlen <= 0) + maxlen += D_FIFO_SIZE; /* count now contains available bytes */ + + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_Dfifo count(%d/%d)\n", + count, maxlen); + if (count > maxlen) { + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_Dfifo no fifo mem\n"); + return; + } + new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) & + (D_FIFO_SIZE - 1); + new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); + src = dch->tx_skb->data + dch->tx_idx; /* source pointer */ + dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); + maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); + /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = df->data; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); + /* for next buffer */ + df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); + /* new pos actual buffer */ + df->f1 = new_f1; /* next frame */ + dch->tx_idx = dch->tx_skb->len; +} + +/* + * B-channel send routine + */ +static void +hfcpci_fill_fifo(struct bchannel *bch) +{ + struct hfc_pci *hc = bch->hw; + int maxlen, fcnt; + int count, new_z1; + struct bzfifo *bz; + u_char *bdata; + u_char new_f1, *src, *dst; + __le16 *z1t, *z2t; + + if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO)) + printk(KERN_DEBUG "%s\n", __func__); + if ((!bch->tx_skb) || bch->tx_skb->len == 0) { + if (!test_bit(FLG_FILLEMPTY, &bch->Flags) && + !test_bit(FLG_TRANSPARENT, &bch->Flags)) + return; + count = HFCPCI_FILLEMPTY; + } else { + count = bch->tx_skb->len - bch->tx_idx; + } + if ((bch->nr & 2) && (!hc->hw.bswapped)) { + bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b2; + } else { + bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b1; + } + + if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { + z1t = &bz->za[MAX_B_FRAMES].z1; + z2t = z1t + 1; + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_fifo_trans ch(%x) " + "cnt(%d) z1(%x) z2(%x)\n", bch->nr, count, + le16_to_cpu(*z1t), le16_to_cpu(*z2t)); + fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); + if (fcnt <= 0) + fcnt += B_FIFO_SIZE; + if (test_bit(FLG_FILLEMPTY, &bch->Flags)) { + /* fcnt contains available bytes in fifo */ + if (count > fcnt) + count = fcnt; + new_z1 = le16_to_cpu(*z1t) + count; + /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); + /* end of fifo */ + if (bch->debug & DEBUG_HW_BFIFO) + printk(KERN_DEBUG "hfcpci_FFt fillempty " + "fcnt(%d) maxl(%d) nz1(%x) dst(%p)\n", + fcnt, maxlen, new_z1, dst); + if (maxlen > count) + maxlen = count; /* limit size */ + memset(dst, bch->fill[0], maxlen); /* first copy */ + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + memset(dst, bch->fill[0], count); + } + *z1t = cpu_to_le16(new_z1); /* now send data */ + return; + } + /* fcnt contains available bytes in fifo */ + fcnt = B_FIFO_SIZE - fcnt; + /* remaining bytes to send (bytes in fifo) */ + + next_t_frame: + count = bch->tx_skb->len - bch->tx_idx; + /* maximum fill shall be poll*2 */ + if (count > (poll << 1) - fcnt) + count = (poll << 1) - fcnt; + if (count <= 0) + return; + /* data is suitable for fifo */ + new_z1 = le16_to_cpu(*z1t) + count; + /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + src = bch->tx_skb->data + bch->tx_idx; + /* source pointer */ + dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); + /* end of fifo */ + if (bch->debug & DEBUG_HW_BFIFO) + printk(KERN_DEBUG "hfcpci_FFt fcnt(%d) " + "maxl(%d) nz1(%x) dst(%p)\n", + fcnt, maxlen, new_z1, dst); + fcnt += count; + bch->tx_idx += count; + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + *z1t = cpu_to_le16(new_z1); /* now send data */ + if (bch->tx_idx < bch->tx_skb->len) + return; + dev_kfree_skb(bch->tx_skb); + if (get_next_bframe(bch)) + goto next_t_frame; + return; + } + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)\n", + __func__, bch->nr, bz->f1, bz->f2, + bz->za[bz->f1].z1); + fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_B_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_B_FRAMES - 1)) { + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "hfcpci_fill_Bfifo more as 14 frames\n"); + return; + } + /* now determine free bytes in FIFO buffer */ + maxlen = le16_to_cpu(bz->za[bz->f2].z2) - + le16_to_cpu(bz->za[bz->f1].z1) - 1; + if (maxlen <= 0) + maxlen += B_FIFO_SIZE; /* count now contains available bytes */ + + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_fifo ch(%x) count(%d/%d)\n", + bch->nr, count, maxlen); + + if (maxlen < count) { + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_fifo no fifo mem\n"); + return; + } + new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count; + /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + + new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); + src = bch->tx_skb->data + bch->tx_idx; /* source pointer */ + dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1); + /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + bz->za[new_f1].z1 = cpu_to_le16(new_z1); /* for next buffer */ + bz->f1 = new_f1; /* next frame */ + dev_kfree_skb(bch->tx_skb); + get_next_bframe(bch); +} + + + +/* + * handle L1 state changes TE + */ + +static void +ph_state_te(struct dchannel *dch) +{ + if (dch->debug) + printk(KERN_DEBUG "%s: TE newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case 0: + l1_event(dch->l1, HW_RESET_IND); + break; + case 3: + l1_event(dch->l1, HW_DEACT_IND); + break; + case 5: + case 8: + l1_event(dch->l1, ANYSIGNAL); + break; + case 6: + l1_event(dch->l1, INFO2); + break; + case 7: + l1_event(dch->l1, INFO4_P8); + break; + } +} + +/* + * handle L1 state changes NT + */ + +static void +handle_nt_timer3(struct dchannel *dch) { + struct hfc_pci *hc = dch->hw; + + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + hc->hw.nt_timer = 0; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); +} + +static void +ph_state_nt(struct dchannel *dch) +{ + struct hfc_pci *hc = dch->hw; + + if (dch->debug) + printk(KERN_DEBUG "%s: NT newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case 2: + if (hc->hw.nt_timer < 0) { + hc->hw.nt_timer = 0; + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* Clear already pending ints */ + (void) Read_hfc(hc, HFCPCI_INT_S1); + Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE); + udelay(10); + Write_hfc(hc, HFCPCI_STATES, 4); + dch->state = 4; + } else if (hc->hw.nt_timer == 0) { + hc->hw.int_m1 |= HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + hc->hw.nt_timer = NT_T1_COUNT; + hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; + hc->hw.ctmt |= HFCPCI_TIM3_125; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | + HFCPCI_CLTIMER); + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_set_bit(FLG_HFC_TIMER_T1, &dch->Flags); + /* allow G2 -> G3 transition */ + Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); + } else { + Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); + } + break; + case 1: + hc->hw.nt_timer = 0; + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + case 4: + hc->hw.nt_timer = 0; + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + break; + case 3: + if (!test_and_set_bit(FLG_HFC_TIMER_T3, &dch->Flags)) { + if (!test_and_clear_bit(FLG_L2_ACTIVATED, + &dch->Flags)) { + handle_nt_timer3(dch); + break; + } + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 |= HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + hc->hw.nt_timer = NT_T3_COUNT; + hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; + hc->hw.ctmt |= HFCPCI_TIM3_125; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | + HFCPCI_CLTIMER); + } + break; + } +} + +static void +ph_state(struct dchannel *dch) +{ + struct hfc_pci *hc = dch->hw; + + if (hc->hw.protocol == ISDN_P_NT_S0) { + if (test_bit(FLG_HFC_TIMER_T3, &dch->Flags) && + hc->hw.nt_timer < 0) + handle_nt_timer3(dch); + else + ph_state_nt(dch); + } else + ph_state_te(dch); +} + +/* + * Layer 1 callback function + */ +static int +hfc_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfc_pci *hc = dch->hw; + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + break; + case HW_RESET_REQ: + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); + /* HFC ST 3 */ + udelay(6); + Write_hfc(hc, HFCPCI_STATES, 3); /* HFC ST 2 */ + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | + HFCPCI_DO_ACTION); + l1_event(dch->l1, HW_POWERUP_IND); + break; + case HW_DEACT_REQ: + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); + break; + case HW_POWERUP_REQ: + Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -1; + } + return 0; +} + +/* + * Interrupt handler + */ +static inline void +tx_birq(struct bchannel *bch) +{ + if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) + hfcpci_fill_fifo(bch); + else { + if (bch->tx_skb) + dev_kfree_skb(bch->tx_skb); + if (get_next_bframe(bch)) + hfcpci_fill_fifo(bch); + } +} + +static inline void +tx_dirq(struct dchannel *dch) +{ + if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len) + hfcpci_fill_dfifo(dch->hw); + else { + if (dch->tx_skb) + dev_kfree_skb(dch->tx_skb); + if (get_next_dframe(dch)) + hfcpci_fill_dfifo(dch->hw); + } +} + +static irqreturn_t +hfcpci_int(int intno, void *dev_id) +{ + struct hfc_pci *hc = dev_id; + u_char exval; + struct bchannel *bch; + u_char val, stat; + + spin_lock(&hc->lock); + if (!(hc->hw.int_m2 & 0x08)) { + spin_unlock(&hc->lock); + return IRQ_NONE; /* not initialised */ + } + stat = Read_hfc(hc, HFCPCI_STATUS); + if (HFCPCI_ANYINT & stat) { + val = Read_hfc(hc, HFCPCI_INT_S1); + if (hc->dch.debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG + "HFC-PCI: stat(%02x) s1(%02x)\n", stat, val); + } else { + /* shared */ + spin_unlock(&hc->lock); + return IRQ_NONE; + } + hc->irqcnt++; + + if (hc->dch.debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "HFC-PCI irq %x\n", val); + val &= hc->hw.int_m1; + if (val & 0x40) { /* state machine irq */ + exval = Read_hfc(hc, HFCPCI_STATES) & 0xf; + if (hc->dch.debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "ph_state chg %d->%d\n", + hc->dch.state, exval); + hc->dch.state = exval; + schedule_event(&hc->dch, FLG_PHCHANGE); + val &= ~0x40; + } + if (val & 0x80) { /* timer irq */ + if (hc->hw.protocol == ISDN_P_NT_S0) { + if ((--hc->hw.nt_timer) < 0) + schedule_event(&hc->dch, FLG_PHCHANGE); + } + val &= ~0x80; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); + } + if (val & 0x08) { /* B1 rx */ + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch) + main_rec_hfcpci(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n"); + } + if (val & 0x10) { /* B2 rx */ + bch = Sel_BCS(hc, 2); + if (bch) + main_rec_hfcpci(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n"); + } + if (val & 0x01) { /* B1 tx */ + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch) + tx_birq(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n"); + } + if (val & 0x02) { /* B2 tx */ + bch = Sel_BCS(hc, 2); + if (bch) + tx_birq(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n"); + } + if (val & 0x20) /* D rx */ + receive_dmsg(hc); + if (val & 0x04) { /* D tx */ + if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags)) + del_timer(&hc->dch.timer); + tx_dirq(&hc->dch); + } + spin_unlock(&hc->lock); + return IRQ_HANDLED; +} + +/* + * timer callback for D-chan busy resolution. Currently no function + */ +static void +hfcpci_dbusy_timer(struct timer_list *t) +{ +} + +/* + * activate/deactivate hardware for selected channels and mode + */ +static int +mode_hfcpci(struct bchannel *bch, int bc, int protocol) +{ + struct hfc_pci *hc = bch->hw; + int fifo2; + u_char rx_slot = 0, tx_slot = 0, pcm_mode; + + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "HFCPCI bchannel protocol %x-->%x ch %x-->%x\n", + bch->state, protocol, bch->nr, bc); + + fifo2 = bc; + pcm_mode = (bc >> 24) & 0xff; + if (pcm_mode) { /* PCM SLOT USE */ + if (!test_bit(HFC_CFG_PCM, &hc->cfg)) + printk(KERN_WARNING + "%s: pcm channel id without HFC_CFG_PCM\n", + __func__); + rx_slot = (bc >> 8) & 0xff; + tx_slot = (bc >> 16) & 0xff; + bc = bc & 0xff; + } else if (test_bit(HFC_CFG_PCM, &hc->cfg) && (protocol > ISDN_P_NONE)) + printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n", + __func__); + if (hc->chanlimit > 1) { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } else { + if (bc & 2) { + if (protocol != ISDN_P_NONE) { + hc->hw.bswapped = 1; /* B1 and B2 exchanged */ + hc->hw.sctrl_e |= 0x80; + } else { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } + fifo2 = 1; + } else { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } + } + switch (protocol) { + case (-1): /* used for init */ + bch->state = -1; + bch->nr = bc; + /* fall through */ + case (ISDN_P_NONE): + if (bch->state == ISDN_P_NONE) + return 0; + if (bc & 2) { + hc->hw.sctrl &= ~SCTRL_B2_ENA; + hc->hw.sctrl_r &= ~SCTRL_B2_ENA; + } else { + hc->hw.sctrl &= ~SCTRL_B1_ENA; + hc->hw.sctrl_r &= ~SCTRL_B1_ENA; + } + if (fifo2 & 2) { + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2; + hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS | + HFCPCI_INTS_B2REC); + } else { + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1; + hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS | + HFCPCI_INTS_B1REC); + } +#ifdef REVERSE_BITORDER + if (bch->nr & 2) + hc->hw.cirm &= 0x7f; + else + hc->hw.cirm &= 0xbf; +#endif + bch->state = ISDN_P_NONE; + bch->nr = bc; + test_and_clear_bit(FLG_HDLC, &bch->Flags); + test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_RAW): + bch->state = protocol; + bch->nr = bc; + hfcpci_clear_fifo_rx(hc, (fifo2 & 2) ? 1 : 0); + hfcpci_clear_fifo_tx(hc, (fifo2 & 2) ? 1 : 0); + if (bc & 2) { + hc->hw.sctrl |= SCTRL_B2_ENA; + hc->hw.sctrl_r |= SCTRL_B2_ENA; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x80; +#endif + } else { + hc->hw.sctrl |= SCTRL_B1_ENA; + hc->hw.sctrl_r |= SCTRL_B1_ENA; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x40; +#endif + } + if (fifo2 & 2) { + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; + if (!tics) + hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS | + HFCPCI_INTS_B2REC); + hc->hw.ctmt |= 2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; + if (!tics) + hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS | + HFCPCI_INTS_B1REC); + hc->hw.ctmt |= 1; + hc->hw.conn &= ~0x03; + } + test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_HDLC): + bch->state = protocol; + bch->nr = bc; + hfcpci_clear_fifo_rx(hc, (fifo2 & 2) ? 1 : 0); + hfcpci_clear_fifo_tx(hc, (fifo2 & 2) ? 1 : 0); + if (bc & 2) { + hc->hw.sctrl |= SCTRL_B2_ENA; + hc->hw.sctrl_r |= SCTRL_B2_ENA; + } else { + hc->hw.sctrl |= SCTRL_B1_ENA; + hc->hw.sctrl_r |= SCTRL_B1_ENA; + } + if (fifo2 & 2) { + hc->hw.last_bfifo_cnt[1] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; + hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS | + HFCPCI_INTS_B2REC); + hc->hw.ctmt &= ~2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.last_bfifo_cnt[0] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; + hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS | + HFCPCI_INTS_B1REC); + hc->hw.ctmt &= ~1; + hc->hw.conn &= ~0x03; + } + test_and_set_bit(FLG_HDLC, &bch->Flags); + break; + default: + printk(KERN_DEBUG "prot not known %x\n", protocol); + return -ENOPROTOOPT; + } + if (test_bit(HFC_CFG_PCM, &hc->cfg)) { + if ((protocol == ISDN_P_NONE) || + (protocol == -1)) { /* init case */ + rx_slot = 0; + tx_slot = 0; + } else { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { + rx_slot |= 0xC0; + tx_slot |= 0xC0; + } else { + rx_slot |= 0x80; + tx_slot |= 0x80; + } + } + if (bc & 2) { + hc->hw.conn &= 0xc7; + hc->hw.conn |= 0x08; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n", + __func__, tx_slot); + printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n", + __func__, rx_slot); + Write_hfc(hc, HFCPCI_B2_SSL, tx_slot); + Write_hfc(hc, HFCPCI_B2_RSL, rx_slot); + } else { + hc->hw.conn &= 0xf8; + hc->hw.conn |= 0x01; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n", + __func__, tx_slot); + printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n", + __func__, rx_slot); + Write_hfc(hc, HFCPCI_B1_SSL, tx_slot); + Write_hfc(hc, HFCPCI_B1_RSL, rx_slot); + } + } + Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); +#ifdef REVERSE_BITORDER + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); +#endif + return 0; +} + +static int +set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan) +{ + struct hfc_pci *hc = bch->hw; + + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x\n", + bch->state, protocol, bch->nr, chan); + if (bch->nr != chan) { + printk(KERN_DEBUG + "HFCPCI rxtest wrong channel parameter %x/%x\n", + bch->nr, chan); + return -EINVAL; + } + switch (protocol) { + case (ISDN_P_B_RAW): + bch->state = protocol; + hfcpci_clear_fifo_rx(hc, (chan & 2) ? 1 : 0); + if (chan & 2) { + hc->hw.sctrl_r |= SCTRL_B2_ENA; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; + if (!tics) + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + hc->hw.ctmt |= 2; + hc->hw.conn &= ~0x18; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x80; +#endif + } else { + hc->hw.sctrl_r |= SCTRL_B1_ENA; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; + if (!tics) + hc->hw.int_m1 |= HFCPCI_INTS_B1REC; + hc->hw.ctmt |= 1; + hc->hw.conn &= ~0x03; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x40; +#endif + } + break; + case (ISDN_P_B_HDLC): + bch->state = protocol; + hfcpci_clear_fifo_rx(hc, (chan & 2) ? 1 : 0); + if (chan & 2) { + hc->hw.sctrl_r |= SCTRL_B2_ENA; + hc->hw.last_bfifo_cnt[1] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + hc->hw.ctmt &= ~2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.sctrl_r |= SCTRL_B1_ENA; + hc->hw.last_bfifo_cnt[0] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; + hc->hw.int_m1 |= HFCPCI_INTS_B1REC; + hc->hw.ctmt &= ~1; + hc->hw.conn &= ~0x03; + } + break; + default: + printk(KERN_DEBUG "prot not known %x\n", protocol); + return -ENOPROTOOPT; + } + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); +#ifdef REVERSE_BITORDER + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); +#endif + return 0; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfc_pci *hc = bch->hw; + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + mISDN_clear_bchannel(bch); + mode_hfcpci(bch, bch->nr, ISDN_P_NONE); + spin_unlock_irqrestore(&hc->lock, flags); +} + +/* + * Layer 1 B-channel hardware access + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + return mISDN_ctrl_bchannel(bch, cq); +} +static int +hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_pci *hc = bch->hw; + int ret = -EINVAL; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); + switch (cmd) { + case HW_TESTRX_RAW: + spin_lock_irqsave(&hc->lock, flags); + ret = set_hfcpci_rxtest(bch, ISDN_P_B_RAW, (int)(long)arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case HW_TESTRX_HDLC: + spin_lock_irqsave(&hc->lock, flags); + ret = set_hfcpci_rxtest(bch, ISDN_P_B_HDLC, (int)(long)arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case HW_TESTRX_OFF: + spin_lock_irqsave(&hc->lock, flags); + mode_hfcpci(bch, bch->nr, ISDN_P_NONE); + spin_unlock_irqrestore(&hc->lock, flags); + ret = 0; + break; + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + deactivate_bchannel(bch); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + ret = 0; + break; + case CONTROL_CHANNEL: + ret = channel_bctrl(bch, arg); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return ret; +} + +/* + * Layer2 -> Layer 1 Dchannel data + */ +static int +hfcpci_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_pci *hc = dch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&hc->lock, flags); + ret = dchannel_senddata(dch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcpci_fill_dfifo(dch->hw); + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (hc->hw.protocol == ISDN_P_NT_S0) { + ret = 0; + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + spin_unlock_irqrestore(&hc->lock, flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + } + test_and_set_bit(FLG_L2_ACTIVATED, &dch->Flags); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | + HFCPCI_DO_ACTION | 1); + } else + ret = l1_event(dch->l1, hh->prim); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_DEACTIVATE_REQ: + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + spin_lock_irqsave(&hc->lock, flags); + if (hc->hw.protocol == ISDN_P_NT_S0) { + /* prepare deactivation */ + Write_hfc(hc, HFCPCI_STATES, 0x40); + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + ret = 0; + } else { + ret = l1_event(dch->l1, hh->prim); + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * Layer2 -> Layer 1 Bchannel data + */ +static int +hfcpci_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_pci *hc = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&hc->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + hfcpci_fill_fifo(bch); + ret = 0; + } + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) + ret = mode_hfcpci(bch, bch->nr, ch->protocol); + else + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * called for card init message + */ + +static void +inithfcpci(struct hfc_pci *hc) +{ + printk(KERN_DEBUG "inithfcpci: entered\n"); + timer_setup(&hc->dch.timer, hfcpci_dbusy_timer, 0); + hc->chanlimit = 2; + mode_hfcpci(&hc->bch[0], 1, -1); + mode_hfcpci(&hc->bch[1], 2, -1); +} + + +static int +init_card(struct hfc_pci *hc) +{ + int cnt = 3; + u_long flags; + + printk(KERN_DEBUG "init_card: entered\n"); + + + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + if (request_irq(hc->irq, hfcpci_int, IRQF_SHARED, "HFC PCI", hc)) { + printk(KERN_WARNING + "mISDN: couldn't get interrupt %d\n", hc->irq); + return -EIO; + } + spin_lock_irqsave(&hc->lock, flags); + reset_hfcpci(hc); + while (cnt) { + inithfcpci(hc); + /* + * Finally enable IRQ output + * this is only allowed, if an IRQ routine is already + * established for this HFC, so don't do that earlier + */ + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + /* Timeout 80ms */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((80 * HZ) / 1000); + printk(KERN_INFO "HFC PCI: IRQ %d count %d\n", + hc->irq, hc->irqcnt); + /* now switch timer interrupt off */ + spin_lock_irqsave(&hc->lock, flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* reinit mode reg */ + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + if (!hc->irqcnt) { + printk(KERN_WARNING + "HFC PCI: IRQ(%d) getting no interrupts " + "during init %d\n", hc->irq, 4 - cnt); + if (cnt == 1) + break; + else { + reset_hfcpci(hc); + cnt--; + } + } else { + spin_unlock_irqrestore(&hc->lock, flags); + hc->initdone = 1; + return 0; + } + } + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + free_irq(hc->irq, hc); + return -EIO; +} + +static int +channel_ctrl(struct hfc_pci *hc, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + u_char slot; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | + MISDN_CTRL_DISCONNECT | MISDN_CTRL_L1_TIMER3; + break; + case MISDN_CTRL_LOOP: + /* channel 0 disabled loop */ + if (cq->channel < 0 || cq->channel > 2) { + ret = -EINVAL; + break; + } + if (cq->channel & 1) { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC0; + else + slot = 0x80; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B1_SSL, slot); + Write_hfc(hc, HFCPCI_B1_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~7) | 6; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + } + if (cq->channel & 2) { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC1; + else + slot = 0x81; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B2_SSL, slot); + Write_hfc(hc, HFCPCI_B2_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~0x38) | 0x30; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + } + if (cq->channel & 3) + hc->hw.trm |= 0x80; /* enable IOM-loop */ + else { + hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + hc->hw.trm &= 0x7f; /* disable IOM-loop */ + } + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + break; + case MISDN_CTRL_CONNECT: + if (cq->channel == cq->p1) { + ret = -EINVAL; + break; + } + if (cq->channel < 1 || cq->channel > 2 || + cq->p1 < 1 || cq->p1 > 2) { + ret = -EINVAL; + break; + } + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC0; + else + slot = 0x80; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B1_SSL, slot); + Write_hfc(hc, HFCPCI_B2_RSL, slot); + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC1; + else + slot = 0x81; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B2_SSL, slot); + Write_hfc(hc, HFCPCI_B1_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x36; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + hc->hw.trm |= 0x80; + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + break; + case MISDN_CTRL_DISCONNECT: + hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + hc->hw.trm &= 0x7f; /* disable IOM-loop */ + break; + case MISDN_CTRL_L1_TIMER3: + ret = l1_event(hc->dch.l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch, + struct channel_req *rq) +{ + int err = 0; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, + hc->dch.dev.id, __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if (rq->adr.channel == 1) { + /* TODO: E-Channel */ + return -EINVAL; + } + if (!hc->initdone) { + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hc->dch, hfc_l1callback); + if (err) + return err; + } + hc->hw.protocol = rq->protocol; + ch->protocol = rq->protocol; + err = init_card(hc); + if (err) + return err; + } else { + if (rq->protocol != ch->protocol) { + if (hc->hw.protocol == ISDN_P_TE_S0) + l1_event(hc->dch.l1, CLOSE_CHANNEL); + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hc->dch, hfc_l1callback); + if (err) + return err; + } + hc->hw.protocol = rq->protocol; + ch->protocol = rq->protocol; + hfcpci_setmode(hc); + } + } + + if (((ch->protocol == ISDN_P_NT_S0) && (hc->dch.state == 3)) || + ((ch->protocol == ISDN_P_TE_S0) && (hc->dch.state == 7))) { + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + } + rq->ch = ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +static int +open_bchannel(struct hfc_pci *hc, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel == 0 || rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + bch = &hc->bch[rq->adr.channel - 1]; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; /* TODO: E-channel */ + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +/* + * device control function + */ +static int +hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_pci *hc = dch->hw; + struct channel_req *rq; + int err = 0; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if ((rq->protocol == ISDN_P_TE_S0) || + (rq->protocol == ISDN_P_NT_S0)) + err = open_dchannel(hc, ch, rq); + else + err = open_bchannel(hc, rq); + break; + case CLOSE_CHANNEL: + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, hc->dch.dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(hc, arg); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -EINVAL; + } + return err; +} + +static int +setup_hw(struct hfc_pci *hc) +{ + void *buffer; + + printk(KERN_INFO "mISDN: HFC-PCI driver %s\n", hfcpci_revision); + hc->hw.cirm = 0; + hc->dch.state = 0; + pci_set_master(hc->pdev); + if (!hc->irq) { + printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); + return 1; + } + hc->hw.pci_io = + (char __iomem *)(unsigned long)hc->pdev->resource[1].start; + + if (!hc->hw.pci_io) { + printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); + return 1; + } + /* Allocate memory for FIFOS */ + /* the memory needs to be on a 32k boundary within the first 4G */ + pci_set_dma_mask(hc->pdev, 0xFFFF8000); + buffer = pci_alloc_consistent(hc->pdev, 0x8000, &hc->hw.dmahandle); + /* We silently assume the address is okay if nonzero */ + if (!buffer) { + printk(KERN_WARNING + "HFC-PCI: Error allocating memory for FIFO!\n"); + return 1; + } + hc->hw.fifos = buffer; + pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle); + hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256); + printk(KERN_INFO + "HFC-PCI: defined at mem %#lx fifo %#lx(%#lx) IRQ %d HZ %d\n", + (u_long) hc->hw.pci_io, (u_long) hc->hw.fifos, + (u_long) hc->hw.dmahandle, hc->irq, HZ); + /* enable memory mapped ports, disable busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); + hc->hw.int_m2 = 0; + disable_hwirq(hc); + hc->hw.int_m1 = 0; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + timer_setup(&hc->hw.timer, hfcpci_Timer, 0); + /* default PCM master */ + test_and_set_bit(HFC_CFG_MASTER, &hc->cfg); + return 0; +} + +static void +release_card(struct hfc_pci *hc) { + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + hc->hw.int_m2 = 0; /* interrupt output off ! */ + disable_hwirq(hc); + mode_hfcpci(&hc->bch[0], 1, ISDN_P_NONE); + mode_hfcpci(&hc->bch[1], 2, ISDN_P_NONE); + if (hc->dch.timer.function != NULL) { + del_timer(&hc->dch.timer); + hc->dch.timer.function = NULL; + } + spin_unlock_irqrestore(&hc->lock, flags); + if (hc->hw.protocol == ISDN_P_TE_S0) + l1_event(hc->dch.l1, CLOSE_CHANNEL); + if (hc->initdone) + free_irq(hc->irq, hc); + release_io_hfcpci(hc); /* must release after free_irq! */ + mISDN_unregister_device(&hc->dch.dev); + mISDN_freebchannel(&hc->bch[1]); + mISDN_freebchannel(&hc->bch[0]); + mISDN_freedchannel(&hc->dch); + pci_set_drvdata(hc->pdev, NULL); + kfree(hc); +} + +static int +setup_card(struct hfc_pci *card) +{ + int err = -EINVAL; + u_int i; + char name[MISDN_MAX_IDLEN]; + + card->dch.debug = debug; + spin_lock_init(&card->lock); + mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state); + card->dch.hw = card; + card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + card->dch.dev.D.send = hfcpci_l2l1D; + card->dch.dev.D.ctrl = hfc_dctrl; + card->dch.dev.nrbchan = 2; + for (i = 0; i < 2; i++) { + card->bch[i].nr = i + 1; + set_channelmap(i + 1, card->dch.dev.channelmap); + card->bch[i].debug = debug; + mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, poll >> 1); + card->bch[i].hw = card; + card->bch[i].ch.send = hfcpci_l2l1B; + card->bch[i].ch.ctrl = hfc_bctrl; + card->bch[i].ch.nr = i + 1; + list_add(&card->bch[i].ch.list, &card->dch.dev.bchannels); + } + err = setup_hw(card); + if (err) + goto error; + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1); + err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, name); + if (err) + goto error; + HFC_cnt++; + printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); + return 0; +error: + mISDN_freebchannel(&card->bch[1]); + mISDN_freebchannel(&card->bch[0]); + mISDN_freedchannel(&card->dch); + kfree(card); + return err; +} + +/* private data in the PCI devices list */ +struct _hfc_map { + u_int subtype; + u_int flag; + char *name; +}; + +static const struct _hfc_map hfc_map[] = +{ + {HFC_CCD_2BD0, 0, "CCD/Billion/Asuscom 2BD0"}, + {HFC_CCD_B000, 0, "Billion B000"}, + {HFC_CCD_B006, 0, "Billion B006"}, + {HFC_CCD_B007, 0, "Billion B007"}, + {HFC_CCD_B008, 0, "Billion B008"}, + {HFC_CCD_B009, 0, "Billion B009"}, + {HFC_CCD_B00A, 0, "Billion B00A"}, + {HFC_CCD_B00B, 0, "Billion B00B"}, + {HFC_CCD_B00C, 0, "Billion B00C"}, + {HFC_CCD_B100, 0, "Seyeon B100"}, + {HFC_CCD_B700, 0, "Primux II S0 B700"}, + {HFC_CCD_B701, 0, "Primux II S0 NT B701"}, + {HFC_ABOCOM_2BD1, 0, "Abocom/Magitek 2BD1"}, + {HFC_ASUS_0675, 0, "Asuscom/Askey 675"}, + {HFC_BERKOM_TCONCEPT, 0, "German telekom T-Concept"}, + {HFC_BERKOM_A1T, 0, "German telekom A1T"}, + {HFC_ANIGMA_MC145575, 0, "Motorola MC145575"}, + {HFC_ZOLTRIX_2BD0, 0, "Zoltrix 2BD0"}, + {HFC_DIGI_DF_M_IOM2_E, 0, + "Digi International DataFire Micro V IOM2 (Europe)"}, + {HFC_DIGI_DF_M_E, 0, + "Digi International DataFire Micro V (Europe)"}, + {HFC_DIGI_DF_M_IOM2_A, 0, + "Digi International DataFire Micro V IOM2 (North America)"}, + {HFC_DIGI_DF_M_A, 0, + "Digi International DataFire Micro V (North America)"}, + {HFC_SITECOM_DC105V2, 0, "Sitecom Connectivity DC-105 ISDN TA"}, + {}, +}; + +static const struct pci_device_id hfc_ids[] = +{ + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_2BD0), + (unsigned long) &hfc_map[0] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B000), + (unsigned long) &hfc_map[1] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B006), + (unsigned long) &hfc_map[2] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B007), + (unsigned long) &hfc_map[3] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B008), + (unsigned long) &hfc_map[4] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B009), + (unsigned long) &hfc_map[5] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00A), + (unsigned long) &hfc_map[6] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00B), + (unsigned long) &hfc_map[7] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00C), + (unsigned long) &hfc_map[8] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B100), + (unsigned long) &hfc_map[9] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B700), + (unsigned long) &hfc_map[10] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B701), + (unsigned long) &hfc_map[11] }, + { PCI_VDEVICE(ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1), + (unsigned long) &hfc_map[12] }, + { PCI_VDEVICE(ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675), + (unsigned long) &hfc_map[13] }, + { PCI_VDEVICE(BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT), + (unsigned long) &hfc_map[14] }, + { PCI_VDEVICE(BERKOM, PCI_DEVICE_ID_BERKOM_A1T), + (unsigned long) &hfc_map[15] }, + { PCI_VDEVICE(ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575), + (unsigned long) &hfc_map[16] }, + { PCI_VDEVICE(ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0), + (unsigned long) &hfc_map[17] }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E), + (unsigned long) &hfc_map[18] }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_E), + (unsigned long) &hfc_map[19] }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A), + (unsigned long) &hfc_map[20] }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_A), + (unsigned long) &hfc_map[21] }, + { PCI_VDEVICE(SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2), + (unsigned long) &hfc_map[22] }, + {}, +}; + +static int +hfc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + struct hfc_pci *card; + struct _hfc_map *m = (struct _hfc_map *)ent->driver_data; + + card = kzalloc(sizeof(struct hfc_pci), GFP_KERNEL); + if (!card) { + printk(KERN_ERR "No kmem for HFC card\n"); + return err; + } + card->pdev = pdev; + card->subtype = m->subtype; + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return err; + } + + printk(KERN_INFO "mISDN_hfcpci: found adapter %s at %s\n", + m->name, pci_name(pdev)); + + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_card(card); + if (err) + pci_set_drvdata(pdev, NULL); + return err; +} + +static void +hfc_remove_pci(struct pci_dev *pdev) +{ + struct hfc_pci *card = pci_get_drvdata(pdev); + + if (card) + release_card(card); + else + if (debug) + printk(KERN_DEBUG "%s: drvdata already removed\n", + __func__); +} + + +static struct pci_driver hfc_driver = { + .name = "hfcpci", + .probe = hfc_probe, + .remove = hfc_remove_pci, + .id_table = hfc_ids, +}; + +static int +_hfcpci_softirq(struct device *dev, void *unused) +{ + struct hfc_pci *hc = dev_get_drvdata(dev); + struct bchannel *bch; + if (hc == NULL) + return 0; + + if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) { + spin_lock(&hc->lock); + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */ + main_rec_hfcpci(bch); + tx_birq(bch); + } + bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2); + if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */ + main_rec_hfcpci(bch); + tx_birq(bch); + } + spin_unlock(&hc->lock); + } + return 0; +} + +static void +hfcpci_softirq(struct timer_list *unused) +{ + WARN_ON_ONCE(driver_for_each_device(&hfc_driver.driver, NULL, NULL, + _hfcpci_softirq) != 0); + + /* if next event would be in the past ... */ + if ((s32)(hfc_jiffies + tics - jiffies) <= 0) + hfc_jiffies = jiffies + 1; + else + hfc_jiffies += tics; + hfc_tl.expires = hfc_jiffies; + add_timer(&hfc_tl); +} + +static int __init +HFC_init(void) +{ + int err; + + if (!poll) + poll = HFCPCI_BTRANS_THRESHOLD; + + if (poll != HFCPCI_BTRANS_THRESHOLD) { + tics = (poll * HZ) / 8000; + if (tics < 1) + tics = 1; + poll = (tics * 8000) / HZ; + if (poll > 256 || poll < 8) { + printk(KERN_ERR "%s: Wrong poll value %d not in range " + "of 8..256.\n", __func__, poll); + err = -EINVAL; + return err; + } + } + if (poll != HFCPCI_BTRANS_THRESHOLD) { + printk(KERN_INFO "%s: Using alternative poll value of %d\n", + __func__, poll); + timer_setup(&hfc_tl, hfcpci_softirq, 0); + hfc_tl.expires = jiffies + tics; + hfc_jiffies = hfc_tl.expires; + add_timer(&hfc_tl); + } else + tics = 0; /* indicate the use of controller's timer */ + + err = pci_register_driver(&hfc_driver); + if (err) { + if (timer_pending(&hfc_tl)) + del_timer(&hfc_tl); + } + + return err; +} + +static void __exit +HFC_cleanup(void) +{ + if (timer_pending(&hfc_tl)) + del_timer_sync(&hfc_tl); + + pci_unregister_driver(&hfc_driver); +} + +module_init(HFC_init); +module_exit(HFC_cleanup); + +MODULE_DEVICE_TABLE(pci, hfc_ids); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c new file mode 100644 index 000000000..c952002c6 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcsusb.c @@ -0,0 +1,2158 @@ +/* hfcsusb.c + * mISDN driver for Colognechip HFC-S USB chip + * + * Copyright 2001 by Peter Sprenger (sprenger@moving-bytes.de) + * Copyright 2008 by Martin Bachem (info@bachem-it.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * module params + * debug=<n>, default=0, with n=0xHHHHGGGG + * H - l1 driver flags described in hfcsusb.h + * G - common mISDN debug flags described at mISDNhw.h + * + * poll=<n>, default 128 + * n : burst size of PH_DATA_IND at transparent rx data + * + * Revision: 0.3.3 (socket), 2008-11-05 + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/usb.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> +#include "hfcsusb.h" + +static unsigned int debug; +static int poll = DEFAULT_TRANSP_BURST_SZ; + +static LIST_HEAD(HFClist); +static DEFINE_RWLOCK(HFClock); + + +MODULE_AUTHOR("Martin Bachem"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, int, 0); + +static int hfcsusb_cnt; + +/* some function prototypes */ +static void hfcsusb_ph_command(struct hfcsusb *hw, u_char command); +static void release_hw(struct hfcsusb *hw); +static void reset_hfcsusb(struct hfcsusb *hw); +static void setPortMode(struct hfcsusb *hw); +static void hfcsusb_start_endpoint(struct hfcsusb *hw, int channel); +static void hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel); +static int hfcsusb_setup_bch(struct bchannel *bch, int protocol); +static void deactivate_bchannel(struct bchannel *bch); +static void hfcsusb_ph_info(struct hfcsusb *hw); + +/* start next background transfer for control channel */ +static void +ctrl_start_transfer(struct hfcsusb *hw) +{ + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + if (hw->ctrl_cnt) { + hw->ctrl_urb->pipe = hw->ctrl_out_pipe; + hw->ctrl_urb->setup_packet = (u_char *)&hw->ctrl_write; + hw->ctrl_urb->transfer_buffer = NULL; + hw->ctrl_urb->transfer_buffer_length = 0; + hw->ctrl_write.wIndex = + cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].hfcs_reg); + hw->ctrl_write.wValue = + cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].reg_val); + + usb_submit_urb(hw->ctrl_urb, GFP_ATOMIC); + } +} + +/* + * queue a control transfer request to write HFC-S USB + * chip register using CTRL resuest queue + */ +static int write_reg(struct hfcsusb *hw, __u8 reg, __u8 val) +{ + struct ctrl_buf *buf; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s reg(0x%02x) val(0x%02x)\n", + hw->name, __func__, reg, val); + + spin_lock(&hw->ctrl_lock); + if (hw->ctrl_cnt >= HFC_CTRL_BUFSIZE) { + spin_unlock(&hw->ctrl_lock); + return 1; + } + buf = &hw->ctrl_buff[hw->ctrl_in_idx]; + buf->hfcs_reg = reg; + buf->reg_val = val; + if (++hw->ctrl_in_idx >= HFC_CTRL_BUFSIZE) + hw->ctrl_in_idx = 0; + if (++hw->ctrl_cnt == 1) + ctrl_start_transfer(hw); + spin_unlock(&hw->ctrl_lock); + + return 0; +} + +/* control completion routine handling background control cmds */ +static void +ctrl_complete(struct urb *urb) +{ + struct hfcsusb *hw = (struct hfcsusb *) urb->context; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + urb->dev = hw->dev; + if (hw->ctrl_cnt) { + hw->ctrl_cnt--; /* decrement actual count */ + if (++hw->ctrl_out_idx >= HFC_CTRL_BUFSIZE) + hw->ctrl_out_idx = 0; /* pointer wrap */ + + ctrl_start_transfer(hw); /* start next transfer */ + } +} + +/* handle LED bits */ +static void +set_led_bit(struct hfcsusb *hw, signed short led_bits, int set_on) +{ + if (set_on) { + if (led_bits < 0) + hw->led_state &= ~abs(led_bits); + else + hw->led_state |= led_bits; + } else { + if (led_bits < 0) + hw->led_state |= abs(led_bits); + else + hw->led_state &= ~led_bits; + } +} + +/* handle LED requests */ +static void +handle_led(struct hfcsusb *hw, int event) +{ + struct hfcsusb_vdata *driver_info = (struct hfcsusb_vdata *) + hfcsusb_idtab[hw->vend_idx].driver_info; + __u8 tmpled; + + if (driver_info->led_scheme == LED_OFF) + return; + tmpled = hw->led_state; + + switch (event) { + case LED_POWER_ON: + set_led_bit(hw, driver_info->led_bits[0], 1); + set_led_bit(hw, driver_info->led_bits[1], 0); + set_led_bit(hw, driver_info->led_bits[2], 0); + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + case LED_POWER_OFF: + set_led_bit(hw, driver_info->led_bits[0], 0); + set_led_bit(hw, driver_info->led_bits[1], 0); + set_led_bit(hw, driver_info->led_bits[2], 0); + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + case LED_S0_ON: + set_led_bit(hw, driver_info->led_bits[1], 1); + break; + case LED_S0_OFF: + set_led_bit(hw, driver_info->led_bits[1], 0); + break; + case LED_B1_ON: + set_led_bit(hw, driver_info->led_bits[2], 1); + break; + case LED_B1_OFF: + set_led_bit(hw, driver_info->led_bits[2], 0); + break; + case LED_B2_ON: + set_led_bit(hw, driver_info->led_bits[3], 1); + break; + case LED_B2_OFF: + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + } + + if (hw->led_state != tmpled) { + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s reg(0x%02x) val(x%02x)\n", + hw->name, __func__, + HFCUSB_P_DATA, hw->led_state); + + write_reg(hw, HFCUSB_P_DATA, hw->led_state); + } +} + +/* + * Layer2 -> Layer 1 Bchannel data + */ +static int +hfcusb_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfcsusb *hw = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + u_long flags; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&hw->lock, flags); + ret = bchannel_senddata(bch, skb); + spin_unlock_irqrestore(&hw->lock, flags); + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s PH_DATA_REQ ret(%i)\n", + hw->name, __func__, ret); + if (ret > 0) + ret = 0; + return ret; + case PH_ACTIVATE_REQ: + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + hfcsusb_start_endpoint(hw, bch->nr - 1); + ret = hfcsusb_setup_bch(bch, ch->protocol); + } else + ret = 0; + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * send full D/B channel status information + * as MPH_INFORMATION_IND + */ +static void +hfcsusb_ph_info(struct hfcsusb *hw) +{ + struct ph_info *phi; + struct dchannel *dch = &hw->dch; + int i; + + phi = kzalloc(sizeof(struct ph_info) + + dch->dev.nrbchan * sizeof(struct ph_info_ch), GFP_ATOMIC); + phi->dch.ch.protocol = hw->protocol; + phi->dch.ch.Flags = dch->Flags; + phi->dch.state = dch->state; + phi->dch.num_bch = dch->dev.nrbchan; + for (i = 0; i < dch->dev.nrbchan; i++) { + phi->bch[i].protocol = hw->bch[i].ch.protocol; + phi->bch[i].Flags = hw->bch[i].Flags; + } + _queue_data(&dch->dev.D, MPH_INFORMATION_IND, MISDN_ID_ANY, + sizeof(struct ph_info_dch) + dch->dev.nrbchan * + sizeof(struct ph_info_ch), phi, GFP_ATOMIC); + kfree(phi); +} + +/* + * Layer2 -> Layer 1 Dchannel data + */ +static int +hfcusb_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct hfcsusb *hw = dch->hw; + int ret = -EINVAL; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_DATA_REQ\n", + hw->name, __func__); + + spin_lock_irqsave(&hw->lock, flags); + ret = dchannel_senddata(dch, skb); + spin_unlock_irqrestore(&hw->lock, flags); + if (ret > 0) { + ret = 0; + queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL); + } + break; + + case PH_ACTIVATE_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_ACTIVATE_REQ %s\n", + hw->name, __func__, + (hw->protocol == ISDN_P_NT_S0) ? "NT" : "TE"); + + if (hw->protocol == ISDN_P_NT_S0) { + ret = 0; + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + _queue_data(&dch->dev.D, + PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_ATOMIC); + } else { + hfcsusb_ph_command(hw, + HFC_L1_ACTIVATE_NT); + test_and_set_bit(FLG_L2_ACTIVATED, + &dch->Flags); + } + } else { + hfcsusb_ph_command(hw, HFC_L1_ACTIVATE_TE); + ret = l1_event(dch->l1, hh->prim); + } + break; + + case PH_DEACTIVATE_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_DEACTIVATE_REQ\n", + hw->name, __func__); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + + if (hw->protocol == ISDN_P_NT_S0) { + hfcsusb_ph_command(hw, HFC_L1_DEACTIVATE_NT); + spin_lock_irqsave(&hw->lock, flags); + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + spin_unlock_irqrestore(&hw->lock, flags); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + ret = 0; + } else + ret = l1_event(dch->l1, hh->prim); + break; + case MPH_INFORMATION_REQ: + hfcsusb_ph_info(hw); + ret = 0; + break; + } + + return ret; +} + +/* + * Layer 1 callback function + */ +static int +hfc_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s cmd 0x%x\n", + hw->name, __func__, cmd); + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + case HW_RESET_REQ: + case HW_POWERUP_REQ: + break; + + case HW_DEACT_REQ: + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: unknown cmd %x\n", + hw->name, __func__, cmd); + return -1; + } + hfcsusb_ph_info(hw); + return 0; +} + +static int +open_dchannel(struct hfcsusb *hw, struct mISDNchannel *ch, + struct channel_req *rq) +{ + int err = 0; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: %s: dev(%d) open addr(%i) from %p\n", + hw->name, __func__, hw->dch.dev.id, rq->adr.channel, + __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + + test_and_clear_bit(FLG_ACTIVE, &hw->dch.Flags); + test_and_clear_bit(FLG_ACTIVE, &hw->ech.Flags); + hfcsusb_start_endpoint(hw, HFC_CHAN_D); + + /* E-Channel logging */ + if (rq->adr.channel == 1) { + if (hw->fifos[HFCUSB_PCM_RX].pipe) { + hfcsusb_start_endpoint(hw, HFC_CHAN_E); + set_bit(FLG_ACTIVE, &hw->ech.Flags); + _queue_data(&hw->ech.dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + } else + return -EINVAL; + } + + if (!hw->initdone) { + hw->protocol = rq->protocol; + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hw->dch, hfc_l1callback); + if (err) + return err; + } + setPortMode(hw); + ch->protocol = rq->protocol; + hw->initdone = 1; + } else { + if (rq->protocol != ch->protocol) + return -EPROTONOSUPPORT; + } + + if (((ch->protocol == ISDN_P_NT_S0) && (hw->dch.state == 3)) || + ((ch->protocol == ISDN_P_TE_S0) && (hw->dch.state == 7))) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + rq->ch = ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s: %s: cannot get module\n", + hw->name, __func__); + return 0; +} + +static int +open_bchannel(struct hfcsusb *hw, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel == 0 || rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s B%i\n", + hw->name, __func__, rq->adr.channel); + + bch = &hw->bch[rq->adr.channel - 1]; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; + + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s: %s:cannot get module\n", + hw->name, __func__); + return 0; +} + +static int +channel_ctrl(struct hfcsusb *hw, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s op(0x%x) channel(0x%x)\n", + hw->name, __func__, (cq->op), (cq->channel)); + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | + MISDN_CTRL_DISCONNECT; + break; + default: + printk(KERN_WARNING "%s: %s: unknown Op %x\n", + hw->name, __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +/* + * device control function + */ +static int +hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfcsusb *hw = dch->hw; + struct channel_req *rq; + int err = 0; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: cmd:%x %p\n", + hw->name, __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if ((rq->protocol == ISDN_P_TE_S0) || + (rq->protocol == ISDN_P_NT_S0)) + err = open_dchannel(hw, ch, rq); + else + err = open_bchannel(hw, rq); + if (!err) + hw->open++; + break; + case CLOSE_CHANNEL: + hw->open--; + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG + "%s: %s: dev(%d) close from %p (open %d)\n", + hw->name, __func__, hw->dch.dev.id, + __builtin_return_address(0), hw->open); + if (!hw->open) { + hfcsusb_stop_endpoint(hw, HFC_CHAN_D); + if (hw->fifos[HFCUSB_PCM_RX].pipe) + hfcsusb_stop_endpoint(hw, HFC_CHAN_E); + handle_led(hw, LED_POWER_ON); + } + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(hw, arg); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: unknown command %x\n", + hw->name, __func__, cmd); + return -EINVAL; + } + return err; +} + +/* + * S0 TE state change event handler + */ +static void +ph_state_te(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DEBUG_HW) { + if (dch->state <= HFC_MAX_TE_LAYER1_STATE) + printk(KERN_DEBUG "%s: %s: %s\n", hw->name, __func__, + HFC_TE_LAYER1_STATES[dch->state]); + else + printk(KERN_DEBUG "%s: %s: TE F%d\n", + hw->name, __func__, dch->state); + } + + switch (dch->state) { + case 0: + l1_event(dch->l1, HW_RESET_IND); + break; + case 3: + l1_event(dch->l1, HW_DEACT_IND); + break; + case 5: + case 8: + l1_event(dch->l1, ANYSIGNAL); + break; + case 6: + l1_event(dch->l1, INFO2); + break; + case 7: + l1_event(dch->l1, INFO4_P8); + break; + } + if (dch->state == 7) + handle_led(hw, LED_S0_ON); + else + handle_led(hw, LED_S0_OFF); +} + +/* + * S0 NT state change event handler + */ +static void +ph_state_nt(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DEBUG_HW) { + if (dch->state <= HFC_MAX_NT_LAYER1_STATE) + printk(KERN_DEBUG "%s: %s: %s\n", + hw->name, __func__, + HFC_NT_LAYER1_STATES[dch->state]); + + else + printk(KERN_INFO DRIVER_NAME "%s: %s: NT G%d\n", + hw->name, __func__, dch->state); + } + + switch (dch->state) { + case (1): + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + handle_led(hw, LED_S0_OFF); + break; + + case (2): + if (hw->nt_timer < 0) { + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + hfcsusb_ph_command(dch->hw, HFC_L1_DEACTIVATE_NT); + } else { + hw->timers |= NT_ACTIVATION_TIMER; + hw->nt_timer = NT_T1_COUNT; + /* allow G2 -> G3 transition */ + write_reg(hw, HFCUSB_STATES, 2 | HFCUSB_NT_G2_G3); + } + break; + case (3): + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + handle_led(hw, LED_S0_ON); + break; + case (4): + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + break; + default: + break; + } + hfcsusb_ph_info(hw); +} + +static void +ph_state(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (hw->protocol == ISDN_P_NT_S0) + ph_state_nt(dch); + else if (hw->protocol == ISDN_P_TE_S0) + ph_state_te(dch); +} + +/* + * disable/enable BChannel for desired protocoll + */ +static int +hfcsusb_setup_bch(struct bchannel *bch, int protocol) +{ + struct hfcsusb *hw = bch->hw; + __u8 conhdlc, sctrl, sctrl_r; + + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: protocol %x-->%x B%d\n", + hw->name, __func__, bch->state, protocol, + bch->nr); + + /* setup val for CON_HDLC */ + conhdlc = 0; + if (protocol > ISDN_P_NONE) + conhdlc = 8; /* enable FIFO */ + + switch (protocol) { + case (-1): /* used for init */ + bch->state = -1; + /* fall through */ + case (ISDN_P_NONE): + if (bch->state == ISDN_P_NONE) + return 0; /* already in idle state */ + bch->state = ISDN_P_NONE; + clear_bit(FLG_HDLC, &bch->Flags); + clear_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_RAW): + conhdlc |= 2; + bch->state = protocol; + set_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_HDLC): + bch->state = protocol; + set_bit(FLG_HDLC, &bch->Flags); + break; + default: + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: prot not known %x\n", + hw->name, __func__, protocol); + return -ENOPROTOOPT; + } + + if (protocol >= ISDN_P_NONE) { + write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 0 : 2); + write_reg(hw, HFCUSB_CON_HDLC, conhdlc); + write_reg(hw, HFCUSB_INC_RES_F, 2); + write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 1 : 3); + write_reg(hw, HFCUSB_CON_HDLC, conhdlc); + write_reg(hw, HFCUSB_INC_RES_F, 2); + + sctrl = 0x40 + ((hw->protocol == ISDN_P_TE_S0) ? 0x00 : 0x04); + sctrl_r = 0x0; + if (test_bit(FLG_ACTIVE, &hw->bch[0].Flags)) { + sctrl |= 1; + sctrl_r |= 1; + } + if (test_bit(FLG_ACTIVE, &hw->bch[1].Flags)) { + sctrl |= 2; + sctrl_r |= 2; + } + write_reg(hw, HFCUSB_SCTRL, sctrl); + write_reg(hw, HFCUSB_SCTRL_R, sctrl_r); + + if (protocol > ISDN_P_NONE) + handle_led(hw, (bch->nr == 1) ? LED_B1_ON : LED_B2_ON); + else + handle_led(hw, (bch->nr == 1) ? LED_B1_OFF : + LED_B2_OFF); + } + hfcsusb_ph_info(hw); + return 0; +} + +static void +hfcsusb_ph_command(struct hfcsusb *hw, u_char command) +{ + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: %x\n", + hw->name, __func__, command); + + switch (command) { + case HFC_L1_ACTIVATE_TE: + /* force sending sending INFO1 */ + write_reg(hw, HFCUSB_STATES, 0x14); + /* start l1 activation */ + write_reg(hw, HFCUSB_STATES, 0x04); + break; + + case HFC_L1_FORCE_DEACTIVATE_TE: + write_reg(hw, HFCUSB_STATES, 0x10); + write_reg(hw, HFCUSB_STATES, 0x03); + break; + + case HFC_L1_ACTIVATE_NT: + if (hw->dch.state == 3) + _queue_data(&hw->dch.dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + else + write_reg(hw, HFCUSB_STATES, HFCUSB_ACTIVATE | + HFCUSB_DO_ACTION | HFCUSB_NT_G2_G3); + break; + + case HFC_L1_DEACTIVATE_NT: + write_reg(hw, HFCUSB_STATES, + HFCUSB_DO_ACTION); + break; + } +} + +/* + * Layer 1 B-channel hardware access + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + return mISDN_ctrl_bchannel(bch, cq); +} + +/* collect data from incoming interrupt or isochron USB data */ +static void +hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len, + int finish) +{ + struct hfcsusb *hw = fifo->hw; + struct sk_buff *rx_skb = NULL; + int maxlen = 0; + int fifon = fifo->fifonum; + int i; + int hdlc = 0; + unsigned long flags; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: fifo(%i) len(%i) " + "dch(%p) bch(%p) ech(%p)\n", + hw->name, __func__, fifon, len, + fifo->dch, fifo->bch, fifo->ech); + + if (!len) + return; + + if ((!!fifo->dch + !!fifo->bch + !!fifo->ech) != 1) { + printk(KERN_DEBUG "%s: %s: undefined channel\n", + hw->name, __func__); + return; + } + + spin_lock_irqsave(&hw->lock, flags); + if (fifo->dch) { + rx_skb = fifo->dch->rx_skb; + maxlen = fifo->dch->maxlen; + hdlc = 1; + } + if (fifo->bch) { + if (test_bit(FLG_RX_OFF, &fifo->bch->Flags)) { + fifo->bch->dropcnt += len; + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + maxlen = bchannel_get_rxbuf(fifo->bch, len); + rx_skb = fifo->bch->rx_skb; + if (maxlen < 0) { + if (rx_skb) + skb_trim(rx_skb, 0); + pr_warning("%s.B%d: No bufferspace for %d bytes\n", + hw->name, fifo->bch->nr, len); + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + maxlen = fifo->bch->maxlen; + hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); + } + if (fifo->ech) { + rx_skb = fifo->ech->rx_skb; + maxlen = fifo->ech->maxlen; + hdlc = 1; + } + + if (fifo->dch || fifo->ech) { + if (!rx_skb) { + rx_skb = mI_alloc_skb(maxlen, GFP_ATOMIC); + if (rx_skb) { + if (fifo->dch) + fifo->dch->rx_skb = rx_skb; + if (fifo->ech) + fifo->ech->rx_skb = rx_skb; + skb_trim(rx_skb, 0); + } else { + printk(KERN_DEBUG "%s: %s: No mem for rx_skb\n", + hw->name, __func__); + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + } + /* D/E-Channel SKB range check */ + if ((rx_skb->len + len) >= MAX_DFRAME_LEN_L1) { + printk(KERN_DEBUG "%s: %s: sbk mem exceeded " + "for fifo(%d) HFCUSB_D_RX\n", + hw->name, __func__, fifon); + skb_trim(rx_skb, 0); + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + } + + skb_put_data(rx_skb, data, len); + + if (hdlc) { + /* we have a complete hdlc packet */ + if (finish) { + if ((rx_skb->len > 3) && + (!(rx_skb->data[rx_skb->len - 1]))) { + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG "%s: %s: fifon(%i)" + " new RX len(%i): ", + hw->name, __func__, fifon, + rx_skb->len); + i = 0; + while (i < rx_skb->len) + printk("%02x ", + rx_skb->data[i++]); + printk("\n"); + } + + /* remove CRC & status */ + skb_trim(rx_skb, rx_skb->len - 3); + + if (fifo->dch) + recv_Dchannel(fifo->dch); + if (fifo->bch) + recv_Bchannel(fifo->bch, MISDN_ID_ANY, + 0); + if (fifo->ech) + recv_Echannel(fifo->ech, + &hw->dch); + } else { + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG + "%s: CRC or minlen ERROR fifon(%i) " + "RX len(%i): ", + hw->name, fifon, rx_skb->len); + i = 0; + while (i < rx_skb->len) + printk("%02x ", + rx_skb->data[i++]); + printk("\n"); + } + skb_trim(rx_skb, 0); + } + } + } else { + /* deliver transparent data to layer2 */ + recv_Bchannel(fifo->bch, MISDN_ID_ANY, false); + } + spin_unlock_irqrestore(&hw->lock, flags); +} + +static void +fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, + void *buf, int num_packets, int packet_size, int interval, + usb_complete_t complete, void *context) +{ + int k; + + usb_fill_bulk_urb(urb, dev, pipe, buf, packet_size * num_packets, + complete, context); + + urb->number_of_packets = num_packets; + urb->transfer_flags = URB_ISO_ASAP; + urb->actual_length = 0; + urb->interval = interval; + + for (k = 0; k < num_packets; k++) { + urb->iso_frame_desc[k].offset = packet_size * k; + urb->iso_frame_desc[k].length = packet_size; + urb->iso_frame_desc[k].actual_length = 0; + } +} + +/* receive completion routine for all ISO tx fifos */ +static void +rx_iso_complete(struct urb *urb) +{ + struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; + struct usb_fifo *fifo = context_iso_urb->owner_fifo; + struct hfcsusb *hw = fifo->hw; + int k, len, errcode, offset, num_isoc_packets, fifon, maxlen, + status, iso_status, i; + __u8 *buf; + static __u8 eof[8]; + __u8 s0_state; + unsigned long flags; + + fifon = fifo->fifonum; + status = urb->status; + + spin_lock_irqsave(&hw->lock, flags); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + spin_unlock_irqrestore(&hw->lock, flags); + + /* + * ISO transfer only partially completed, + * look at individual frame status for details + */ + if (status == -EXDEV) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: with -EXDEV " + "urb->status %d, fifonum %d\n", + hw->name, __func__, status, fifon); + + /* clear status, so go on with ISO transfers */ + status = 0; + } + + s0_state = 0; + if (fifo->active && !status) { + num_isoc_packets = iso_packets[fifon]; + maxlen = fifo->usb_packet_maxlen; + + for (k = 0; k < num_isoc_packets; ++k) { + len = urb->iso_frame_desc[k].actual_length; + offset = urb->iso_frame_desc[k].offset; + buf = context_iso_urb->buffer + offset; + iso_status = urb->iso_frame_desc[k].status; + + if (iso_status && (debug & DBG_HFC_FIFO_VERBOSE)) { + printk(KERN_DEBUG "%s: %s: " + "ISO packet %i, status: %i\n", + hw->name, __func__, k, iso_status); + } + + /* USB data log for every D ISO in */ + if ((fifon == HFCUSB_D_RX) && + (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG + "%s: %s: %d (%d/%d) len(%d) ", + hw->name, __func__, urb->start_frame, + k, num_isoc_packets - 1, + len); + for (i = 0; i < len; i++) + printk("%x ", buf[i]); + printk("\n"); + } + + if (!iso_status) { + if (fifo->last_urblen != maxlen) { + /* + * save fifo fill-level threshold bits + * to use them later in TX ISO URB + * completions + */ + hw->threshold_mask = buf[1]; + + if (fifon == HFCUSB_D_RX) + s0_state = (buf[0] >> 4); + + eof[fifon] = buf[0] & 1; + if (len > 2) + hfcsusb_rx_frame(fifo, buf + 2, + len - 2, (len < maxlen) + ? eof[fifon] : 0); + } else + hfcsusb_rx_frame(fifo, buf, len, + (len < maxlen) ? + eof[fifon] : 0); + fifo->last_urblen = len; + } + } + + /* signal S0 layer1 state change */ + if ((s0_state) && (hw->initdone) && + (s0_state != hw->dch.state)) { + hw->dch.state = s0_state; + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, + context_iso_urb->buffer, num_isoc_packets, + fifo->usb_packet_maxlen, fifo->intervall, + (usb_complete_t)rx_iso_complete, urb->context); + errcode = usb_submit_urb(urb, GFP_ATOMIC); + if (errcode < 0) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: error submitting " + "ISO URB: %d\n", + hw->name, __func__, errcode); + } + } else { + if (status && (debug & DBG_HFC_URB_INFO)) + printk(KERN_DEBUG "%s: %s: rx_iso_complete : " + "urb->status %d, fifonum %d\n", + hw->name, __func__, status, fifon); + } +} + +/* receive completion routine for all interrupt rx fifos */ +static void +rx_int_complete(struct urb *urb) +{ + int len, status, i; + __u8 *buf, maxlen, fifon; + struct usb_fifo *fifo = (struct usb_fifo *) urb->context; + struct hfcsusb *hw = fifo->hw; + static __u8 eof[8]; + unsigned long flags; + + spin_lock_irqsave(&hw->lock, flags); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + spin_unlock_irqrestore(&hw->lock, flags); + + fifon = fifo->fifonum; + if ((!fifo->active) || (urb->status)) { + if (debug & DBG_HFC_URB_ERROR) + printk(KERN_DEBUG + "%s: %s: RX-Fifo %i is going down (%i)\n", + hw->name, __func__, fifon, urb->status); + + fifo->urb->interval = 0; /* cancel automatic rescheduling */ + return; + } + len = urb->actual_length; + buf = fifo->buffer; + maxlen = fifo->usb_packet_maxlen; + + /* USB data log for every D INT in */ + if ((fifon == HFCUSB_D_RX) && (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG "%s: %s: D RX INT len(%d) ", + hw->name, __func__, len); + for (i = 0; i < len; i++) + printk("%02x ", buf[i]); + printk("\n"); + } + + if (fifo->last_urblen != fifo->usb_packet_maxlen) { + /* the threshold mask is in the 2nd status byte */ + hw->threshold_mask = buf[1]; + + /* signal S0 layer1 state change */ + if (hw->initdone && ((buf[0] >> 4) != hw->dch.state)) { + hw->dch.state = (buf[0] >> 4); + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + eof[fifon] = buf[0] & 1; + /* if we have more than the 2 status bytes -> collect data */ + if (len > 2) + hfcsusb_rx_frame(fifo, buf + 2, + urb->actual_length - 2, + (len < maxlen) ? eof[fifon] : 0); + } else { + hfcsusb_rx_frame(fifo, buf, urb->actual_length, + (len < maxlen) ? eof[fifon] : 0); + } + fifo->last_urblen = urb->actual_length; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: error resubmitting USB\n", + hw->name, __func__); + } +} + +/* transmit completion routine for all ISO tx fifos */ +static void +tx_iso_complete(struct urb *urb) +{ + struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; + struct usb_fifo *fifo = context_iso_urb->owner_fifo; + struct hfcsusb *hw = fifo->hw; + struct sk_buff *tx_skb; + int k, tx_offset, num_isoc_packets, sink, remain, current_len, + errcode, hdlc, i; + int *tx_idx; + int frame_complete, fifon, status, fillempty = 0; + __u8 threshbit, *p; + unsigned long flags; + + spin_lock_irqsave(&hw->lock, flags); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + + if (fifo->dch) { + tx_skb = fifo->dch->tx_skb; + tx_idx = &fifo->dch->tx_idx; + hdlc = 1; + } else if (fifo->bch) { + tx_skb = fifo->bch->tx_skb; + tx_idx = &fifo->bch->tx_idx; + hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); + if (!tx_skb && !hdlc && + test_bit(FLG_FILLEMPTY, &fifo->bch->Flags)) + fillempty = 1; + } else { + printk(KERN_DEBUG "%s: %s: neither BCH nor DCH\n", + hw->name, __func__); + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + + fifon = fifo->fifonum; + status = urb->status; + + tx_offset = 0; + + /* + * ISO transfer only partially completed, + * look at individual frame status for details + */ + if (status == -EXDEV) { + if (debug & DBG_HFC_URB_ERROR) + printk(KERN_DEBUG "%s: %s: " + "-EXDEV (%i) fifon (%d)\n", + hw->name, __func__, status, fifon); + + /* clear status, so go on with ISO transfers */ + status = 0; + } + + if (fifo->active && !status) { + /* is FifoFull-threshold set for our channel? */ + threshbit = (hw->threshold_mask & (1 << fifon)); + num_isoc_packets = iso_packets[fifon]; + + /* predict dataflow to avoid fifo overflow */ + if (fifon >= HFCUSB_D_TX) + sink = (threshbit) ? SINK_DMIN : SINK_DMAX; + else + sink = (threshbit) ? SINK_MIN : SINK_MAX; + fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, + context_iso_urb->buffer, num_isoc_packets, + fifo->usb_packet_maxlen, fifo->intervall, + (usb_complete_t)tx_iso_complete, urb->context); + memset(context_iso_urb->buffer, 0, + sizeof(context_iso_urb->buffer)); + frame_complete = 0; + + for (k = 0; k < num_isoc_packets; ++k) { + /* analyze tx success of previous ISO packets */ + if (debug & DBG_HFC_URB_ERROR) { + errcode = urb->iso_frame_desc[k].status; + if (errcode) { + printk(KERN_DEBUG "%s: %s: " + "ISO packet %i, status: %i\n", + hw->name, __func__, k, errcode); + } + } + + /* Generate next ISO Packets */ + if (tx_skb) + remain = tx_skb->len - *tx_idx; + else if (fillempty) + remain = 15; /* > not complete */ + else + remain = 0; + + if (remain > 0) { + fifo->bit_line -= sink; + current_len = (0 - fifo->bit_line) / 8; + if (current_len > 14) + current_len = 14; + if (current_len < 0) + current_len = 0; + if (remain < current_len) + current_len = remain; + + /* how much bit do we put on the line? */ + fifo->bit_line += current_len * 8; + + context_iso_urb->buffer[tx_offset] = 0; + if (current_len == remain) { + if (hdlc) { + /* signal frame completion */ + context_iso_urb-> + buffer[tx_offset] = 1; + /* add 2 byte flags and 16bit + * CRC at end of ISDN frame */ + fifo->bit_line += 32; + } + frame_complete = 1; + } + + /* copy tx data to iso-urb buffer */ + p = context_iso_urb->buffer + tx_offset + 1; + if (fillempty) { + memset(p, fifo->bch->fill[0], + current_len); + } else { + memcpy(p, (tx_skb->data + *tx_idx), + current_len); + *tx_idx += current_len; + } + urb->iso_frame_desc[k].offset = tx_offset; + urb->iso_frame_desc[k].length = current_len + 1; + + /* USB data log for every D ISO out */ + if ((fifon == HFCUSB_D_RX) && !fillempty && + (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG + "%s: %s (%d/%d) offs(%d) len(%d) ", + hw->name, __func__, + k, num_isoc_packets - 1, + urb->iso_frame_desc[k].offset, + urb->iso_frame_desc[k].length); + + for (i = urb->iso_frame_desc[k].offset; + i < (urb->iso_frame_desc[k].offset + + urb->iso_frame_desc[k].length); + i++) + printk("%x ", + context_iso_urb->buffer[i]); + + printk(" skb->len(%i) tx-idx(%d)\n", + tx_skb->len, *tx_idx); + } + + tx_offset += (current_len + 1); + } else { + urb->iso_frame_desc[k].offset = tx_offset++; + urb->iso_frame_desc[k].length = 1; + /* we lower data margin every msec */ + fifo->bit_line -= sink; + if (fifo->bit_line < BITLINE_INF) + fifo->bit_line = BITLINE_INF; + } + + if (frame_complete) { + frame_complete = 0; + + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG "%s: %s: " + "fifon(%i) new TX len(%i): ", + hw->name, __func__, + fifon, tx_skb->len); + i = 0; + while (i < tx_skb->len) + printk("%02x ", + tx_skb->data[i++]); + printk("\n"); + } + + dev_kfree_skb(tx_skb); + tx_skb = NULL; + if (fifo->dch && get_next_dframe(fifo->dch)) + tx_skb = fifo->dch->tx_skb; + else if (fifo->bch && + get_next_bframe(fifo->bch)) + tx_skb = fifo->bch->tx_skb; + } + } + errcode = usb_submit_urb(urb, GFP_ATOMIC); + if (errcode < 0) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG + "%s: %s: error submitting ISO URB: %d \n", + hw->name, __func__, errcode); + } + + /* + * abuse DChannel tx iso completion to trigger NT mode state + * changes tx_iso_complete is assumed to be called every + * fifo->intervall (ms) + */ + if ((fifon == HFCUSB_D_TX) && (hw->protocol == ISDN_P_NT_S0) + && (hw->timers & NT_ACTIVATION_TIMER)) { + if ((--hw->nt_timer) < 0) + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + } else { + if (status && (debug & DBG_HFC_URB_ERROR)) + printk(KERN_DEBUG "%s: %s: urb->status %s (%i)" + "fifonum=%d\n", + hw->name, __func__, + symbolic(urb_errlist, status), status, fifon); + } + spin_unlock_irqrestore(&hw->lock, flags); +} + +/* + * allocs urbs and start isoc transfer with two pending urbs to avoid + * gaps in the transfer chain + */ +static int +start_isoc_chain(struct usb_fifo *fifo, int num_packets_per_urb, + usb_complete_t complete, int packet_size) +{ + struct hfcsusb *hw = fifo->hw; + int i, k, errcode; + + if (debug) + printk(KERN_DEBUG "%s: %s: fifo %i\n", + hw->name, __func__, fifo->fifonum); + + /* allocate Memory for Iso out Urbs */ + for (i = 0; i < 2; i++) { + if (!(fifo->iso[i].urb)) { + fifo->iso[i].urb = + usb_alloc_urb(num_packets_per_urb, GFP_KERNEL); + if (!(fifo->iso[i].urb)) { + printk(KERN_DEBUG + "%s: %s: alloc urb for fifo %i failed", + hw->name, __func__, fifo->fifonum); + continue; + } + fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo; + fifo->iso[i].indx = i; + + /* Init the first iso */ + if (ISO_BUFFER_SIZE >= + (fifo->usb_packet_maxlen * + num_packets_per_urb)) { + fill_isoc_urb(fifo->iso[i].urb, + fifo->hw->dev, fifo->pipe, + fifo->iso[i].buffer, + num_packets_per_urb, + fifo->usb_packet_maxlen, + fifo->intervall, complete, + &fifo->iso[i]); + memset(fifo->iso[i].buffer, 0, + sizeof(fifo->iso[i].buffer)); + + for (k = 0; k < num_packets_per_urb; k++) { + fifo->iso[i].urb-> + iso_frame_desc[k].offset = + k * packet_size; + fifo->iso[i].urb-> + iso_frame_desc[k].length = + packet_size; + } + } else { + printk(KERN_DEBUG + "%s: %s: ISO Buffer size to small!\n", + hw->name, __func__); + } + } + fifo->bit_line = BITLINE_INF; + + errcode = usb_submit_urb(fifo->iso[i].urb, GFP_KERNEL); + fifo->active = (errcode >= 0) ? 1 : 0; + fifo->stop_gracefull = 0; + if (errcode < 0) { + printk(KERN_DEBUG "%s: %s: %s URB nr:%d\n", + hw->name, __func__, + symbolic(urb_errlist, errcode), i); + } + } + return fifo->active; +} + +static void +stop_iso_gracefull(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int i, timeout; + u_long flags; + + for (i = 0; i < 2; i++) { + spin_lock_irqsave(&hw->lock, flags); + if (debug) + printk(KERN_DEBUG "%s: %s for fifo %i.%i\n", + hw->name, __func__, fifo->fifonum, i); + fifo->stop_gracefull = 1; + spin_unlock_irqrestore(&hw->lock, flags); + } + + for (i = 0; i < 2; i++) { + timeout = 3; + while (fifo->stop_gracefull && timeout--) + schedule_timeout_interruptible((HZ / 1000) * 16); + if (debug && fifo->stop_gracefull) + printk(KERN_DEBUG "%s: ERROR %s for fifo %i.%i\n", + hw->name, __func__, fifo->fifonum, i); + } +} + +static void +stop_int_gracefull(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int timeout; + u_long flags; + + spin_lock_irqsave(&hw->lock, flags); + if (debug) + printk(KERN_DEBUG "%s: %s for fifo %i\n", + hw->name, __func__, fifo->fifonum); + fifo->stop_gracefull = 1; + spin_unlock_irqrestore(&hw->lock, flags); + + timeout = 3; + while (fifo->stop_gracefull && timeout--) + schedule_timeout_interruptible((HZ / 1000) * 3); + if (debug && fifo->stop_gracefull) + printk(KERN_DEBUG "%s: ERROR %s for fifo %i\n", + hw->name, __func__, fifo->fifonum); +} + +/* start the interrupt transfer for the given fifo */ +static void +start_int_fifo(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int errcode; + + if (debug) + printk(KERN_DEBUG "%s: %s: INT IN fifo:%d\n", + hw->name, __func__, fifo->fifonum); + + if (!fifo->urb) { + fifo->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!fifo->urb) + return; + } + usb_fill_int_urb(fifo->urb, fifo->hw->dev, fifo->pipe, + fifo->buffer, fifo->usb_packet_maxlen, + (usb_complete_t)rx_int_complete, fifo, fifo->intervall); + fifo->active = 1; + fifo->stop_gracefull = 0; + errcode = usb_submit_urb(fifo->urb, GFP_KERNEL); + if (errcode) { + printk(KERN_DEBUG "%s: %s: submit URB: status:%i\n", + hw->name, __func__, errcode); + fifo->active = 0; + } +} + +static void +setPortMode(struct hfcsusb *hw) +{ + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s %s\n", hw->name, __func__, + (hw->protocol == ISDN_P_TE_S0) ? "TE" : "NT"); + + if (hw->protocol == ISDN_P_TE_S0) { + write_reg(hw, HFCUSB_SCTRL, 0x40); + write_reg(hw, HFCUSB_SCTRL_E, 0x00); + write_reg(hw, HFCUSB_CLKDEL, CLKDEL_TE); + write_reg(hw, HFCUSB_STATES, 3 | 0x10); + write_reg(hw, HFCUSB_STATES, 3); + } else { + write_reg(hw, HFCUSB_SCTRL, 0x44); + write_reg(hw, HFCUSB_SCTRL_E, 0x09); + write_reg(hw, HFCUSB_CLKDEL, CLKDEL_NT); + write_reg(hw, HFCUSB_STATES, 1 | 0x10); + write_reg(hw, HFCUSB_STATES, 1); + } +} + +static void +reset_hfcsusb(struct hfcsusb *hw) +{ + struct usb_fifo *fifo; + int i; + + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + /* do Chip reset */ + write_reg(hw, HFCUSB_CIRM, 8); + + /* aux = output, reset off */ + write_reg(hw, HFCUSB_CIRM, 0x10); + + /* set USB_SIZE to match the wMaxPacketSize for INT or BULK transfers */ + write_reg(hw, HFCUSB_USB_SIZE, (hw->packet_size / 8) | + ((hw->packet_size / 8) << 4)); + + /* set USB_SIZE_I to match the the wMaxPacketSize for ISO transfers */ + write_reg(hw, HFCUSB_USB_SIZE_I, hw->iso_packet_size); + + /* enable PCM/GCI master mode */ + write_reg(hw, HFCUSB_MST_MODE1, 0); /* set default values */ + write_reg(hw, HFCUSB_MST_MODE0, 1); /* enable master mode */ + + /* init the fifos */ + write_reg(hw, HFCUSB_F_THRES, + (HFCUSB_TX_THRESHOLD / 8) | ((HFCUSB_RX_THRESHOLD / 8) << 4)); + + fifo = hw->fifos; + for (i = 0; i < HFCUSB_NUM_FIFOS; i++) { + write_reg(hw, HFCUSB_FIFO, i); /* select the desired fifo */ + fifo[i].max_size = + (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN; + fifo[i].last_urblen = 0; + + /* set 2 bit for D- & E-channel */ + write_reg(hw, HFCUSB_HDLC_PAR, ((i <= HFCUSB_B2_RX) ? 0 : 2)); + + /* enable all fifos */ + if (i == HFCUSB_D_TX) + write_reg(hw, HFCUSB_CON_HDLC, + (hw->protocol == ISDN_P_NT_S0) ? 0x08 : 0x09); + else + write_reg(hw, HFCUSB_CON_HDLC, 0x08); + write_reg(hw, HFCUSB_INC_RES_F, 2); /* reset the fifo */ + } + + write_reg(hw, HFCUSB_SCTRL_R, 0); /* disable both B receivers */ + handle_led(hw, LED_POWER_ON); +} + +/* start USB data pipes dependand on device's endpoint configuration */ +static void +hfcsusb_start_endpoint(struct hfcsusb *hw, int channel) +{ + /* quick check if endpoint already running */ + if ((channel == HFC_CHAN_D) && (hw->fifos[HFCUSB_D_RX].active)) + return; + if ((channel == HFC_CHAN_B1) && (hw->fifos[HFCUSB_B1_RX].active)) + return; + if ((channel == HFC_CHAN_B2) && (hw->fifos[HFCUSB_B2_RX].active)) + return; + if ((channel == HFC_CHAN_E) && (hw->fifos[HFCUSB_PCM_RX].active)) + return; + + /* start rx endpoints using USB INT IN method */ + if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) + start_int_fifo(hw->fifos + channel * 2 + 1); + + /* start rx endpoints using USB ISO IN method */ + if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) { + switch (channel) { + case HFC_CHAN_D: + start_isoc_chain(hw->fifos + HFCUSB_D_RX, + ISOC_PACKETS_D, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_E: + start_isoc_chain(hw->fifos + HFCUSB_PCM_RX, + ISOC_PACKETS_D, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_B1: + start_isoc_chain(hw->fifos + HFCUSB_B1_RX, + ISOC_PACKETS_B, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_B2: + start_isoc_chain(hw->fifos + HFCUSB_B2_RX, + ISOC_PACKETS_B, + (usb_complete_t)rx_iso_complete, + 16); + break; + } + } + + /* start tx endpoints using USB ISO OUT method */ + switch (channel) { + case HFC_CHAN_D: + start_isoc_chain(hw->fifos + HFCUSB_D_TX, + ISOC_PACKETS_B, + (usb_complete_t)tx_iso_complete, 1); + break; + case HFC_CHAN_B1: + start_isoc_chain(hw->fifos + HFCUSB_B1_TX, + ISOC_PACKETS_D, + (usb_complete_t)tx_iso_complete, 1); + break; + case HFC_CHAN_B2: + start_isoc_chain(hw->fifos + HFCUSB_B2_TX, + ISOC_PACKETS_B, + (usb_complete_t)tx_iso_complete, 1); + break; + } +} + +/* stop USB data pipes dependand on device's endpoint configuration */ +static void +hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel) +{ + /* quick check if endpoint currently running */ + if ((channel == HFC_CHAN_D) && (!hw->fifos[HFCUSB_D_RX].active)) + return; + if ((channel == HFC_CHAN_B1) && (!hw->fifos[HFCUSB_B1_RX].active)) + return; + if ((channel == HFC_CHAN_B2) && (!hw->fifos[HFCUSB_B2_RX].active)) + return; + if ((channel == HFC_CHAN_E) && (!hw->fifos[HFCUSB_PCM_RX].active)) + return; + + /* rx endpoints using USB INT IN method */ + if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) + stop_int_gracefull(hw->fifos + channel * 2 + 1); + + /* rx endpoints using USB ISO IN method */ + if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) + stop_iso_gracefull(hw->fifos + channel * 2 + 1); + + /* tx endpoints using USB ISO OUT method */ + if (channel != HFC_CHAN_E) + stop_iso_gracefull(hw->fifos + channel * 2); +} + + +/* Hardware Initialization */ +static int +setup_hfcsusb(struct hfcsusb *hw) +{ + void *dmabuf = kmalloc(sizeof(u_char), GFP_KERNEL); + u_char b; + int ret; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + if (!dmabuf) + return -ENOMEM; + + ret = read_reg_atomic(hw, HFCUSB_CHIP_ID, dmabuf); + + memcpy(&b, dmabuf, sizeof(u_char)); + kfree(dmabuf); + + /* check the chip id */ + if (ret != 1) { + printk(KERN_DEBUG "%s: %s: cannot read chip id\n", + hw->name, __func__); + return 1; + } + if (b != HFCUSB_CHIPID) { + printk(KERN_DEBUG "%s: %s: Invalid chip id 0x%02x\n", + hw->name, __func__, b); + return 1; + } + + /* first set the needed config, interface and alternate */ + (void) usb_set_interface(hw->dev, hw->if_used, hw->alt_used); + + hw->led_state = 0; + + /* init the background machinery for control requests */ + hw->ctrl_read.bRequestType = 0xc0; + hw->ctrl_read.bRequest = 1; + hw->ctrl_read.wLength = cpu_to_le16(1); + hw->ctrl_write.bRequestType = 0x40; + hw->ctrl_write.bRequest = 0; + hw->ctrl_write.wLength = 0; + usb_fill_control_urb(hw->ctrl_urb, hw->dev, hw->ctrl_out_pipe, + (u_char *)&hw->ctrl_write, NULL, 0, + (usb_complete_t)ctrl_complete, hw); + + reset_hfcsusb(hw); + return 0; +} + +static void +release_hw(struct hfcsusb *hw) +{ + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + /* + * stop all endpoints gracefully + * TODO: mISDN_core should generate CLOSE_CHANNEL + * signals after calling mISDN_unregister_device() + */ + hfcsusb_stop_endpoint(hw, HFC_CHAN_D); + hfcsusb_stop_endpoint(hw, HFC_CHAN_B1); + hfcsusb_stop_endpoint(hw, HFC_CHAN_B2); + if (hw->fifos[HFCUSB_PCM_RX].pipe) + hfcsusb_stop_endpoint(hw, HFC_CHAN_E); + if (hw->protocol == ISDN_P_TE_S0) + l1_event(hw->dch.l1, CLOSE_CHANNEL); + + mISDN_unregister_device(&hw->dch.dev); + mISDN_freebchannel(&hw->bch[1]); + mISDN_freebchannel(&hw->bch[0]); + mISDN_freedchannel(&hw->dch); + + if (hw->ctrl_urb) { + usb_kill_urb(hw->ctrl_urb); + usb_free_urb(hw->ctrl_urb); + hw->ctrl_urb = NULL; + } + + if (hw->intf) + usb_set_intfdata(hw->intf, NULL); + list_del(&hw->list); + kfree(hw); + hw = NULL; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfcsusb *hw = bch->hw; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: bch->nr(%i)\n", + hw->name, __func__, bch->nr); + + spin_lock_irqsave(&hw->lock, flags); + mISDN_clear_bchannel(bch); + spin_unlock_irqrestore(&hw->lock, flags); + hfcsusb_setup_bch(bch, ISDN_P_NONE); + hfcsusb_stop_endpoint(hw, bch->nr - 1); +} + +/* + * Layer 1 B-channel hardware access + */ +static int +hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + int ret = -EINVAL; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); + + switch (cmd) { + case HW_TESTRX_RAW: + case HW_TESTRX_HDLC: + case HW_TESTRX_OFF: + ret = -EINVAL; + break; + + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + deactivate_bchannel(bch); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + ret = 0; + break; + case CONTROL_CHANNEL: + ret = channel_bctrl(bch, arg); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return ret; +} + +static int +setup_instance(struct hfcsusb *hw, struct device *parent) +{ + u_long flags; + int err, i; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + spin_lock_init(&hw->ctrl_lock); + spin_lock_init(&hw->lock); + + mISDN_initdchannel(&hw->dch, MAX_DFRAME_LEN_L1, ph_state); + hw->dch.debug = debug & 0xFFFF; + hw->dch.hw = hw; + hw->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + hw->dch.dev.D.send = hfcusb_l2l1D; + hw->dch.dev.D.ctrl = hfc_dctrl; + + /* enable E-Channel logging */ + if (hw->fifos[HFCUSB_PCM_RX].pipe) + mISDN_initdchannel(&hw->ech, MAX_DFRAME_LEN_L1, NULL); + + hw->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + hw->dch.dev.nrbchan = 2; + for (i = 0; i < 2; i++) { + hw->bch[i].nr = i + 1; + set_channelmap(i + 1, hw->dch.dev.channelmap); + hw->bch[i].debug = debug; + mISDN_initbchannel(&hw->bch[i], MAX_DATA_MEM, poll >> 1); + hw->bch[i].hw = hw; + hw->bch[i].ch.send = hfcusb_l2l1B; + hw->bch[i].ch.ctrl = hfc_bctrl; + hw->bch[i].ch.nr = i + 1; + list_add(&hw->bch[i].ch.list, &hw->dch.dev.bchannels); + } + + hw->fifos[HFCUSB_B1_TX].bch = &hw->bch[0]; + hw->fifos[HFCUSB_B1_RX].bch = &hw->bch[0]; + hw->fifos[HFCUSB_B2_TX].bch = &hw->bch[1]; + hw->fifos[HFCUSB_B2_RX].bch = &hw->bch[1]; + hw->fifos[HFCUSB_D_TX].dch = &hw->dch; + hw->fifos[HFCUSB_D_RX].dch = &hw->dch; + hw->fifos[HFCUSB_PCM_RX].ech = &hw->ech; + hw->fifos[HFCUSB_PCM_TX].ech = &hw->ech; + + err = setup_hfcsusb(hw); + if (err) + goto out; + + snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s.%d", DRIVER_NAME, + hfcsusb_cnt + 1); + printk(KERN_INFO "%s: registered as '%s'\n", + DRIVER_NAME, hw->name); + + err = mISDN_register_device(&hw->dch.dev, parent, hw->name); + if (err) + goto out; + + hfcsusb_cnt++; + write_lock_irqsave(&HFClock, flags); + list_add_tail(&hw->list, &HFClist); + write_unlock_irqrestore(&HFClock, flags); + return 0; + +out: + mISDN_freebchannel(&hw->bch[1]); + mISDN_freebchannel(&hw->bch[0]); + mISDN_freedchannel(&hw->dch); + kfree(hw); + return err; +} + +static int +hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct hfcsusb *hw; + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *iface = intf->cur_altsetting; + struct usb_host_interface *iface_used = NULL; + struct usb_host_endpoint *ep; + struct hfcsusb_vdata *driver_info; + int ifnum = iface->desc.bInterfaceNumber, i, idx, alt_idx, + probe_alt_setting, vend_idx, cfg_used, *vcf, attr, cfg_found, + ep_addr, cmptbl[16], small_match, iso_packet_size, packet_size, + alt_used = 0; + + vend_idx = 0xffff; + for (i = 0; hfcsusb_idtab[i].idVendor; i++) { + if ((le16_to_cpu(dev->descriptor.idVendor) + == hfcsusb_idtab[i].idVendor) && + (le16_to_cpu(dev->descriptor.idProduct) + == hfcsusb_idtab[i].idProduct)) { + vend_idx = i; + continue; + } + } + + printk(KERN_DEBUG + "%s: interface(%d) actalt(%d) minor(%d) vend_idx(%d)\n", + __func__, ifnum, iface->desc.bAlternateSetting, + intf->minor, vend_idx); + + if (vend_idx == 0xffff) { + printk(KERN_WARNING + "%s: no valid vendor found in USB descriptor\n", + __func__); + return -EIO; + } + /* if vendor and product ID is OK, start probing alternate settings */ + alt_idx = 0; + small_match = -1; + + /* default settings */ + iso_packet_size = 16; + packet_size = 64; + + while (alt_idx < intf->num_altsetting) { + iface = intf->altsetting + alt_idx; + probe_alt_setting = iface->desc.bAlternateSetting; + cfg_used = 0; + + while (validconf[cfg_used][0]) { + cfg_found = 1; + vcf = validconf[cfg_used]; + ep = iface->endpoint; + memcpy(cmptbl, vcf, 16 * sizeof(int)); + + /* check for all endpoints in this alternate setting */ + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + ep_addr = ep->desc.bEndpointAddress; + + /* get endpoint base */ + idx = ((ep_addr & 0x7f) - 1) * 2; + if (idx > 15) + return -EIO; + + if (ep_addr & 0x80) + idx++; + attr = ep->desc.bmAttributes; + + if (cmptbl[idx] != EP_NOP) { + if (cmptbl[idx] == EP_NUL) + cfg_found = 0; + if (attr == USB_ENDPOINT_XFER_INT + && cmptbl[idx] == EP_INT) + cmptbl[idx] = EP_NUL; + if (attr == USB_ENDPOINT_XFER_BULK + && cmptbl[idx] == EP_BLK) + cmptbl[idx] = EP_NUL; + if (attr == USB_ENDPOINT_XFER_ISOC + && cmptbl[idx] == EP_ISO) + cmptbl[idx] = EP_NUL; + + if (attr == USB_ENDPOINT_XFER_INT && + ep->desc.bInterval < vcf[17]) { + cfg_found = 0; + } + } + ep++; + } + + for (i = 0; i < 16; i++) + if (cmptbl[i] != EP_NOP && cmptbl[i] != EP_NUL) + cfg_found = 0; + + if (cfg_found) { + if (small_match < cfg_used) { + small_match = cfg_used; + alt_used = probe_alt_setting; + iface_used = iface; + } + } + cfg_used++; + } + alt_idx++; + } /* (alt_idx < intf->num_altsetting) */ + + /* not found a valid USB Ta Endpoint config */ + if (small_match == -1) + return -EIO; + + iface = iface_used; + hw = kzalloc(sizeof(struct hfcsusb), GFP_KERNEL); + if (!hw) + return -ENOMEM; /* got no mem */ + snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s", DRIVER_NAME); + + ep = iface->endpoint; + vcf = validconf[small_match]; + + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + struct usb_fifo *f; + + ep_addr = ep->desc.bEndpointAddress; + /* get endpoint base */ + idx = ((ep_addr & 0x7f) - 1) * 2; + if (ep_addr & 0x80) + idx++; + f = &hw->fifos[idx & 7]; + + /* init Endpoints */ + if (vcf[idx] == EP_NOP || vcf[idx] == EP_NUL) { + ep++; + continue; + } + switch (ep->desc.bmAttributes) { + case USB_ENDPOINT_XFER_INT: + f->pipe = usb_rcvintpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_INT; + packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + case USB_ENDPOINT_XFER_BULK: + if (ep_addr & 0x80) + f->pipe = usb_rcvbulkpipe(dev, + ep->desc.bEndpointAddress); + else + f->pipe = usb_sndbulkpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_BULK; + packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + case USB_ENDPOINT_XFER_ISOC: + if (ep_addr & 0x80) + f->pipe = usb_rcvisocpipe(dev, + ep->desc.bEndpointAddress); + else + f->pipe = usb_sndisocpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_ISOC; + iso_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + default: + f->pipe = 0; + } + + if (f->pipe) { + f->fifonum = idx & 7; + f->hw = hw; + f->usb_packet_maxlen = + le16_to_cpu(ep->desc.wMaxPacketSize); + f->intervall = ep->desc.bInterval; + } + ep++; + } + hw->dev = dev; /* save device */ + hw->if_used = ifnum; /* save used interface */ + hw->alt_used = alt_used; /* and alternate config */ + hw->ctrl_paksize = dev->descriptor.bMaxPacketSize0; /* control size */ + hw->cfg_used = vcf[16]; /* store used config */ + hw->vend_idx = vend_idx; /* store found vendor */ + hw->packet_size = packet_size; + hw->iso_packet_size = iso_packet_size; + + /* create the control pipes needed for register access */ + hw->ctrl_in_pipe = usb_rcvctrlpipe(hw->dev, 0); + hw->ctrl_out_pipe = usb_sndctrlpipe(hw->dev, 0); + + driver_info = (struct hfcsusb_vdata *) + hfcsusb_idtab[vend_idx].driver_info; + + hw->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!hw->ctrl_urb) { + pr_warn("%s: No memory for control urb\n", + driver_info->vend_name); + kfree(hw); + return -ENOMEM; + } + + pr_info("%s: %s: detected \"%s\" (%s, if=%d alt=%d)\n", + hw->name, __func__, driver_info->vend_name, + conf_str[small_match], ifnum, alt_used); + + if (setup_instance(hw, dev->dev.parent)) + return -EIO; + + hw->intf = intf; + usb_set_intfdata(hw->intf, hw); + return 0; +} + +/* function called when an active device is removed */ +static void +hfcsusb_disconnect(struct usb_interface *intf) +{ + struct hfcsusb *hw = usb_get_intfdata(intf); + struct hfcsusb *next; + int cnt = 0; + + printk(KERN_INFO "%s: device disconnected\n", hw->name); + + handle_led(hw, LED_POWER_OFF); + release_hw(hw); + + list_for_each_entry_safe(hw, next, &HFClist, list) + cnt++; + if (!cnt) + hfcsusb_cnt = 0; + + usb_set_intfdata(intf, NULL); +} + +static struct usb_driver hfcsusb_drv = { + .name = DRIVER_NAME, + .id_table = hfcsusb_idtab, + .probe = hfcsusb_probe, + .disconnect = hfcsusb_disconnect, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(hfcsusb_drv); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.h b/drivers/isdn/hardware/mISDN/hfcsusb.h new file mode 100644 index 000000000..e4fa2a282 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcsusb.h @@ -0,0 +1,425 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * hfcsusb.h, HFC-S USB mISDN driver + */ + +#ifndef __HFCSUSB_H__ +#define __HFCSUSB_H__ + + +#define DRIVER_NAME "HFC-S_USB" + +#define DBG_HFC_CALL_TRACE 0x00010000 +#define DBG_HFC_FIFO_VERBOSE 0x00020000 +#define DBG_HFC_USB_VERBOSE 0x00100000 +#define DBG_HFC_URB_INFO 0x00200000 +#define DBG_HFC_URB_ERROR 0x00400000 + +#define DEFAULT_TRANSP_BURST_SZ 128 + +#define HFC_CTRL_TIMEOUT 20 /* 5ms timeout writing/reading regs */ +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +/* hfcsusb Layer1 commands */ +#define HFC_L1_ACTIVATE_TE 1 +#define HFC_L1_ACTIVATE_NT 2 +#define HFC_L1_DEACTIVATE_NT 3 +#define HFC_L1_FORCE_DEACTIVATE_TE 4 + +/* cmd FLAGS in HFCUSB_STATES register */ +#define HFCUSB_LOAD_STATE 0x10 +#define HFCUSB_ACTIVATE 0x20 +#define HFCUSB_DO_ACTION 0x40 +#define HFCUSB_NT_G2_G3 0x80 + +/* timers */ +#define NT_ACTIVATION_TIMER 0x01 /* enables NT mode activation Timer */ +#define NT_T1_COUNT 10 + +#define MAX_BCH_SIZE 2048 /* allowed B-channel packet size */ + +#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */ +#define HFCUSB_TX_THRESHOLD 96 /* threshold for fifo report bit tx */ + +#define HFCUSB_CHIP_ID 0x16 /* Chip ID register index */ +#define HFCUSB_CIRM 0x00 /* cirm register index */ +#define HFCUSB_USB_SIZE 0x07 /* int length register */ +#define HFCUSB_USB_SIZE_I 0x06 /* iso length register */ +#define HFCUSB_F_CROSS 0x0b /* bit order register */ +#define HFCUSB_CLKDEL 0x37 /* bit delay register */ +#define HFCUSB_CON_HDLC 0xfa /* channel connect register */ +#define HFCUSB_HDLC_PAR 0xfb +#define HFCUSB_SCTRL 0x31 /* S-bus control register (tx) */ +#define HFCUSB_SCTRL_E 0x32 /* same for E and special funcs */ +#define HFCUSB_SCTRL_R 0x33 /* S-bus control register (rx) */ +#define HFCUSB_F_THRES 0x0c /* threshold register */ +#define HFCUSB_FIFO 0x0f /* fifo select register */ +#define HFCUSB_F_USAGE 0x1a /* fifo usage register */ +#define HFCUSB_MST_MODE0 0x14 +#define HFCUSB_MST_MODE1 0x15 +#define HFCUSB_P_DATA 0x1f +#define HFCUSB_INC_RES_F 0x0e +#define HFCUSB_B1_SSL 0x20 +#define HFCUSB_B2_SSL 0x21 +#define HFCUSB_B1_RSL 0x24 +#define HFCUSB_B2_RSL 0x25 +#define HFCUSB_STATES 0x30 + + +#define HFCUSB_CHIPID 0x40 /* ID value of HFC-S USB */ + +/* fifo registers */ +#define HFCUSB_NUM_FIFOS 8 /* maximum number of fifos */ +#define HFCUSB_B1_TX 0 /* index for B1 transmit bulk/int */ +#define HFCUSB_B1_RX 1 /* index for B1 receive bulk/int */ +#define HFCUSB_B2_TX 2 +#define HFCUSB_B2_RX 3 +#define HFCUSB_D_TX 4 +#define HFCUSB_D_RX 5 +#define HFCUSB_PCM_TX 6 +#define HFCUSB_PCM_RX 7 + + +#define USB_INT 0 +#define USB_BULK 1 +#define USB_ISOC 2 + +#define ISOC_PACKETS_D 8 +#define ISOC_PACKETS_B 8 +#define ISO_BUFFER_SIZE 128 + +/* defines how much ISO packets are handled in one URB */ +static int iso_packets[8] = +{ ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, + ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D +}; + + +/* Fifo flow Control for TX ISO */ +#define SINK_MAX 68 +#define SINK_MIN 48 +#define SINK_DMIN 12 +#define SINK_DMAX 18 +#define BITLINE_INF (-96 * 8) + +/* HFC-S USB register access by Control-URSs */ +#define write_reg_atomic(a, b, c) \ + usb_control_msg((a)->dev, (a)->ctrl_out_pipe, 0, 0x40, (c), (b), \ + 0, 0, HFC_CTRL_TIMEOUT) +#define read_reg_atomic(a, b, c) \ + usb_control_msg((a)->dev, (a)->ctrl_in_pipe, 1, 0xC0, 0, (b), (c), \ + 1, HFC_CTRL_TIMEOUT) +#define HFC_CTRL_BUFSIZE 64 + +struct ctrl_buf { + __u8 hfcs_reg; /* register number */ + __u8 reg_val; /* value to be written (or read) */ +}; + +/* + * URB error codes + * Used to represent a list of values and their respective symbolic names + */ +struct hfcusb_symbolic_list { + const int num; + const char *name; +}; + +static struct hfcusb_symbolic_list urb_errlist[] = { + {-ENOMEM, "No memory for allocation of internal structures"}, + {-ENOSPC, "The host controller's bandwidth is already consumed"}, + {-ENOENT, "URB was canceled by unlink_urb"}, + {-EXDEV, "ISO transfer only partially completed"}, + {-EAGAIN, "Too match scheduled for the future"}, + {-ENXIO, "URB already queued"}, + {-EFBIG, "Too much ISO frames requested"}, + {-ENOSR, "Buffer error (overrun)"}, + {-EPIPE, "Specified endpoint is stalled (device not responding)"}, + {-EOVERFLOW, "Babble (bad cable?)"}, + {-EPROTO, "Bit-stuff error (bad cable?)"}, + {-EILSEQ, "CRC/Timeout"}, + {-ETIMEDOUT, "NAK (device does not respond)"}, + {-ESHUTDOWN, "Device unplugged"}, + {-1, NULL} +}; + +static inline const char * +symbolic(struct hfcusb_symbolic_list list[], const int num) +{ + int i; + for (i = 0; list[i].name != NULL; i++) + if (list[i].num == num) + return list[i].name; + return "<unknown USB Error>"; +} + +/* USB descriptor need to contain one of the following EndPoint combination: */ +#define CNF_4INT3ISO 1 /* 4 INT IN, 3 ISO OUT */ +#define CNF_3INT3ISO 2 /* 3 INT IN, 3 ISO OUT */ +#define CNF_4ISO3ISO 3 /* 4 ISO IN, 3 ISO OUT */ +#define CNF_3ISO3ISO 4 /* 3 ISO IN, 3 ISO OUT */ + +#define EP_NUL 1 /* Endpoint at this position not allowed */ +#define EP_NOP 2 /* all type of endpoints allowed at this position */ +#define EP_ISO 3 /* Isochron endpoint mandatory at this position */ +#define EP_BLK 4 /* Bulk endpoint mandatory at this position */ +#define EP_INT 5 /* Interrupt endpoint mandatory at this position */ + +#define HFC_CHAN_B1 0 +#define HFC_CHAN_B2 1 +#define HFC_CHAN_D 2 +#define HFC_CHAN_E 3 + + +/* + * List of all supported enpoints configiration sets, used to find the + * best matching endpoint configuration within a devices' USB descriptor. + * We need at least 3 RX endpoints, and 3 TX endpoints, either + * INT-in and ISO-out, or ISO-in and ISO-out) + * with 4 RX endpoints even E-Channel logging is possible + */ +static int +validconf[][19] = { + /* INT in, ISO out config */ + {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT, + EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, + CNF_4INT3ISO, 2, 1}, + {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL, + EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, + CNF_3INT3ISO, 2, 0}, + /* ISO in, ISO out config */ + {EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, + EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO, + CNF_4ISO3ISO, 2, 1}, + {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, + EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL, + CNF_3ISO3ISO, 2, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* EOL element */ +}; + +/* string description of chosen config */ +static char *conf_str[] = { + "4 Interrupt IN + 3 Isochron OUT", + "3 Interrupt IN + 3 Isochron OUT", + "4 Isochron IN + 3 Isochron OUT", + "3 Isochron IN + 3 Isochron OUT" +}; + + +#define LED_OFF 0 /* no LED support */ +#define LED_SCHEME1 1 /* LED standard scheme */ +#define LED_SCHEME2 2 /* not used yet... */ + +#define LED_POWER_ON 1 +#define LED_POWER_OFF 2 +#define LED_S0_ON 3 +#define LED_S0_OFF 4 +#define LED_B1_ON 5 +#define LED_B1_OFF 6 +#define LED_B1_DATA 7 +#define LED_B2_ON 8 +#define LED_B2_OFF 9 +#define LED_B2_DATA 10 + +#define LED_NORMAL 0 /* LEDs are normal */ +#define LED_INVERTED 1 /* LEDs are inverted */ + +/* time in ms to perform a Flashing LED when B-Channel has traffic */ +#define LED_TIME 250 + + + +struct hfcsusb; +struct usb_fifo; + +/* structure defining input+output fifos (interrupt/bulk mode) */ +struct iso_urb { + struct urb *urb; + __u8 buffer[ISO_BUFFER_SIZE]; /* buffer rx/tx USB URB data */ + struct usb_fifo *owner_fifo; /* pointer to owner fifo */ + __u8 indx; /* Fifos's ISO double buffer 0 or 1 ? */ +#ifdef ISO_FRAME_START_DEBUG + int start_frames[ISO_FRAME_START_RING_COUNT]; + __u8 iso_frm_strt_pos; /* index in start_frame[] */ +#endif +}; + +struct usb_fifo { + int fifonum; /* fifo index attached to this structure */ + int active; /* fifo is currently active */ + struct hfcsusb *hw; /* pointer to main structure */ + int pipe; /* address of endpoint */ + __u8 usb_packet_maxlen; /* maximum length for usb transfer */ + unsigned int max_size; /* maximum size of receive/send packet */ + __u8 intervall; /* interrupt interval */ + struct urb *urb; /* transfer structure for usb routines */ + __u8 buffer[128]; /* buffer USB INT OUT URB data */ + int bit_line; /* how much bits are in the fifo? */ + + __u8 usb_transfer_mode; /* switched between ISO and INT */ + struct iso_urb iso[2]; /* two urbs to have one always + one pending */ + + struct dchannel *dch; /* link to hfcsusb_t->dch */ + struct bchannel *bch; /* link to hfcsusb_t->bch */ + struct dchannel *ech; /* link to hfcsusb_t->ech, TODO: E-CHANNEL */ + int last_urblen; /* remember length of last packet */ + __u8 stop_gracefull; /* stops URB retransmission */ +}; + +struct hfcsusb { + struct list_head list; + struct dchannel dch; + struct bchannel bch[2]; + struct dchannel ech; /* TODO : wait for struct echannel ;) */ + + struct usb_device *dev; /* our device */ + struct usb_interface *intf; /* used interface */ + int if_used; /* used interface number */ + int alt_used; /* used alternate config */ + int cfg_used; /* configuration index used */ + int vend_idx; /* index in hfcsusb_idtab */ + int packet_size; + int iso_packet_size; + struct usb_fifo fifos[HFCUSB_NUM_FIFOS]; + + /* control pipe background handling */ + struct ctrl_buf ctrl_buff[HFC_CTRL_BUFSIZE]; + int ctrl_in_idx, ctrl_out_idx, ctrl_cnt; + struct urb *ctrl_urb; + struct usb_ctrlrequest ctrl_write; + struct usb_ctrlrequest ctrl_read; + int ctrl_paksize; + int ctrl_in_pipe, ctrl_out_pipe; + spinlock_t ctrl_lock; /* lock for ctrl */ + spinlock_t lock; + + __u8 threshold_mask; + __u8 led_state; + + __u8 protocol; + int nt_timer; + int open; + __u8 timers; + __u8 initdone; + char name[MISDN_MAX_IDLEN]; +}; + +/* private vendor specific data */ +struct hfcsusb_vdata { + __u8 led_scheme; /* led display scheme */ + signed short led_bits[8]; /* array of 8 possible LED bitmask */ + char *vend_name; /* device name */ +}; + + +#define HFC_MAX_TE_LAYER1_STATE 8 +#define HFC_MAX_NT_LAYER1_STATE 4 + +static const char *HFC_TE_LAYER1_STATES[HFC_MAX_TE_LAYER1_STATE + 1] = { + "TE F0 - Reset", + "TE F1 - Reset", + "TE F2 - Sensing", + "TE F3 - Deactivated", + "TE F4 - Awaiting signal", + "TE F5 - Identifying input", + "TE F6 - Synchronized", + "TE F7 - Activated", + "TE F8 - Lost framing", +}; + +static const char *HFC_NT_LAYER1_STATES[HFC_MAX_NT_LAYER1_STATE + 1] = { + "NT G0 - Reset", + "NT G1 - Deactive", + "NT G2 - Pending activation", + "NT G3 - Active", + "NT G4 - Pending deactivation", +}; + +/* supported devices */ +static const struct usb_device_id hfcsusb_idtab[] = { + { + USB_DEVICE(0x0959, 0x2bd0), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_OFF, {4, 0, 2, 1}, + "ISDN USB TA (Cologne Chip HFC-S USB based)"}), + }, + { + USB_DEVICE(0x0675, 0x1688), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {1, 2, 0, 0}, + "DrayTek miniVigor 128 USB ISDN TA"}), + }, + { + USB_DEVICE(0x07b0, 0x0007), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Billion tiny USB ISDN TA 128"}), + }, + { + USB_DEVICE(0x0742, 0x2008), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "Stollmann USB TA"}), + }, + { + USB_DEVICE(0x0742, 0x2009), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "Aceex USB ISDN TA"}), + }, + { + USB_DEVICE(0x0742, 0x200A), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "OEM USB ISDN TA"}), + }, + { + USB_DEVICE(0x08e3, 0x0301), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {2, 0, 1, 4}, + "Olitec USB RNIS"}), + }, + { + USB_DEVICE(0x07fa, 0x0846), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Bewan Modem RNIS USB"}), + }, + { + USB_DEVICE(0x07fa, 0x0847), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Djinn Numeris USB"}), + }, + { + USB_DEVICE(0x07b0, 0x0006), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Twister ISDN TA"}), + }, + { + USB_DEVICE(0x071d, 0x1005), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x02, 0, 0x01, 0x04}, + "Eicon DIVA USB 4.0"}), + }, + { + USB_DEVICE(0x0586, 0x0102), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x88, -64, -32, -16}, + "ZyXEL OMNI.NET USB II"}), + }, + { + USB_DEVICE(0x1ae7, 0x0525), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x88, -64, -32, -16}, + "X-Tensions USB ISDN TA XC-525"}), + }, + { } +}; + +MODULE_DEVICE_TABLE(usb, hfcsusb_idtab); + +#endif /* __HFCSUSB_H__ */ diff --git a/drivers/isdn/hardware/mISDN/iohelper.h b/drivers/isdn/hardware/mISDN/iohelper.h new file mode 100644 index 000000000..c3e7bb1da --- /dev/null +++ b/drivers/isdn/hardware/mISDN/iohelper.h @@ -0,0 +1,109 @@ +/* + * iohelper.h + * helper for define functions to access ISDN hardware + * supported are memory mapped IO + * indirect port IO (one port for address, one for data) + * + * Author Karsten Keil <keil@isdn4linux.de> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _IOHELPER_H +#define _IOHELPER_H + +typedef u8 (read_reg_func)(void *hwp, u8 offset); + typedef void (write_reg_func)(void *hwp, u8 offset, u8 value); + typedef void (fifo_func)(void *hwp, u8 offset, u8 *datap, int size); + + struct _ioport { + u32 port; + u32 ale; + }; + +#define IOFUNC_IO(name, hws, ap) \ + static u8 Read##name##_IO(void *p, u8 off) { \ + struct hws *hw = p; \ + return inb(hw->ap.port + off); \ + } \ + static void Write##name##_IO(void *p, u8 off, u8 val) { \ + struct hws *hw = p; \ + outb(val, hw->ap.port + off); \ + } \ + static void ReadFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) { \ + struct hws *hw = p; \ + insb(hw->ap.port + off, dp, size); \ + } \ + static void WriteFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) { \ + struct hws *hw = p; \ + outsb(hw->ap.port + off, dp, size); \ + } + +#define IOFUNC_IND(name, hws, ap) \ + static u8 Read##name##_IND(void *p, u8 off) { \ + struct hws *hw = p; \ + outb(off, hw->ap.ale); \ + return inb(hw->ap.port); \ + } \ + static void Write##name##_IND(void *p, u8 off, u8 val) { \ + struct hws *hw = p; \ + outb(off, hw->ap.ale); \ + outb(val, hw->ap.port); \ + } \ + static void ReadFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) { \ + struct hws *hw = p; \ + outb(off, hw->ap.ale); \ + insb(hw->ap.port, dp, size); \ + } \ + static void WriteFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) { \ + struct hws *hw = p; \ + outb(off, hw->ap.ale); \ + outsb(hw->ap.port, dp, size); \ + } + +#define IOFUNC_MEMIO(name, hws, typ, adr) \ + static u8 Read##name##_MIO(void *p, u8 off) { \ + struct hws *hw = p; \ + return readb(((typ *)hw->adr) + off); \ + } \ + static void Write##name##_MIO(void *p, u8 off, u8 val) { \ + struct hws *hw = p; \ + writeb(val, ((typ *)hw->adr) + off); \ + } \ + static void ReadFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) { \ + struct hws *hw = p; \ + while (size--) \ + *dp++ = readb(((typ *)hw->adr) + off); \ + } \ + static void WriteFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) { \ + struct hws *hw = p; \ + while (size--) \ + writeb(*dp++, ((typ *)hw->adr) + off); \ + } + +#define ASSIGN_FUNC(typ, name, dest) do { \ + dest.read_reg = &Read##name##_##typ; \ + dest.write_reg = &Write##name##_##typ; \ + dest.read_fifo = &ReadFiFo##name##_##typ; \ + dest.write_fifo = &WriteFiFo##name##_##typ; \ + } while (0) +#define ASSIGN_FUNC_IPAC(typ, target) do { \ + ASSIGN_FUNC(typ, ISAC, target.isac); \ + ASSIGN_FUNC(typ, IPAC, target); \ + } while (0) + +#endif diff --git a/drivers/isdn/hardware/mISDN/ipac.h b/drivers/isdn/hardware/mISDN/ipac.h new file mode 100644 index 000000000..720ee72aa --- /dev/null +++ b/drivers/isdn/hardware/mISDN/ipac.h @@ -0,0 +1,406 @@ +/* + * + * ipac.h Defines for the Infineon (former Siemens) ISDN + * chip series + * + * Author Karsten Keil <keil@isdn4linux.de> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "iohelper.h" + +struct isac_hw { + struct dchannel dch; + u32 type; + u32 off; /* offset to isac regs */ + char *name; + spinlock_t *hwlock; /* lock HW access */ + read_reg_func *read_reg; + write_reg_func *write_reg; + fifo_func *read_fifo; + fifo_func *write_fifo; + int (*monitor)(void *, u32, u8 *, int); + void (*release)(struct isac_hw *); + int (*init)(struct isac_hw *); + int (*ctrl)(struct isac_hw *, u32, u_long); + int (*open)(struct isac_hw *, struct channel_req *); + u8 *mon_tx; + u8 *mon_rx; + int mon_txp; + int mon_txc; + int mon_rxp; + struct arcofi_msg *arcofi_list; + struct timer_list arcofitimer; + wait_queue_head_t arcofi_wait; + u8 arcofi_bc; + u8 arcofi_state; + u8 mocr; + u8 adf2; + u8 state; +}; + +struct ipac_hw; + +struct hscx_hw { + struct bchannel bch; + struct ipac_hw *ip; + u8 fifo_size; + u8 off; /* offset to ICA or ICB */ + u8 slot; + char log[64]; +}; + +struct ipac_hw { + struct isac_hw isac; + struct hscx_hw hscx[2]; + char *name; + void *hw; + spinlock_t *hwlock; /* lock HW access */ + struct module *owner; + u32 type; + read_reg_func *read_reg; + write_reg_func *write_reg; + fifo_func *read_fifo; + fifo_func *write_fifo; + void (*release)(struct ipac_hw *); + int (*init)(struct ipac_hw *); + int (*ctrl)(struct ipac_hw *, u32, u_long); + u8 conf; +}; + +#define IPAC_TYPE_ISAC 0x0010 +#define IPAC_TYPE_IPAC 0x0020 +#define IPAC_TYPE_ISACX 0x0040 +#define IPAC_TYPE_IPACX 0x0080 +#define IPAC_TYPE_HSCX 0x0100 + +#define ISAC_USE_ARCOFI 0x1000 + +/* Monitor functions */ +#define MONITOR_RX_0 0x1000 +#define MONITOR_RX_1 0x1001 +#define MONITOR_TX_0 0x2000 +#define MONITOR_TX_1 0x2001 + +/* All registers original Siemens Spec */ +/* IPAC/ISAC registers */ +#define ISAC_ISTA 0x20 +#define ISAC_MASK 0x20 +#define ISAC_CMDR 0x21 +#define ISAC_STAR 0x21 +#define ISAC_MODE 0x22 +#define ISAC_TIMR 0x23 +#define ISAC_EXIR 0x24 +#define ISAC_RBCL 0x25 +#define ISAC_RSTA 0x27 +#define ISAC_RBCH 0x2A +#define ISAC_SPCR 0x30 +#define ISAC_CIR0 0x31 +#define ISAC_CIX0 0x31 +#define ISAC_MOR0 0x32 +#define ISAC_MOX0 0x32 +#define ISAC_CIR1 0x33 +#define ISAC_CIX1 0x33 +#define ISAC_MOR1 0x34 +#define ISAC_MOX1 0x34 +#define ISAC_STCR 0x37 +#define ISAC_ADF1 0x38 +#define ISAC_ADF2 0x39 +#define ISAC_MOCR 0x3a +#define ISAC_MOSR 0x3a +#define ISAC_SQRR 0x3b +#define ISAC_SQXR 0x3b + +#define ISAC_RBCH_XAC 0x80 + +#define IPAC_D_TIN2 0x01 + +/* IPAC/HSCX */ +#define IPAC_ISTAB 0x20 /* RD */ +#define IPAC_MASKB 0x20 /* WR */ +#define IPAC_STARB 0x21 /* RD */ +#define IPAC_CMDRB 0x21 /* WR */ +#define IPAC_MODEB 0x22 /* R/W */ +#define IPAC_EXIRB 0x24 /* RD */ +#define IPAC_RBCLB 0x25 /* RD */ +#define IPAC_RAH1 0x26 /* WR */ +#define IPAC_RAH2 0x27 /* WR */ +#define IPAC_RSTAB 0x27 /* RD */ +#define IPAC_RAL1 0x28 /* R/W */ +#define IPAC_RAL2 0x29 /* WR */ +#define IPAC_RHCRB 0x29 /* RD */ +#define IPAC_XBCL 0x2A /* WR */ +#define IPAC_CCR2 0x2C /* R/W */ +#define IPAC_RBCHB 0x2D /* RD */ +#define IPAC_XBCH 0x2D /* WR */ +#define HSCX_VSTR 0x2E /* RD */ +#define IPAC_RLCR 0x2E /* WR */ +#define IPAC_CCR1 0x2F /* R/W */ +#define IPAC_TSAX 0x30 /* WR */ +#define IPAC_TSAR 0x31 /* WR */ +#define IPAC_XCCR 0x32 /* WR */ +#define IPAC_RCCR 0x33 /* WR */ + +/* IPAC_ISTAB/IPAC_MASKB bits */ +#define IPAC_B_XPR 0x10 +#define IPAC_B_RPF 0x40 +#define IPAC_B_RME 0x80 +#define IPAC_B_ON 0x2F + +/* IPAC_EXIRB bits */ +#define IPAC_B_RFS 0x04 +#define IPAC_B_RFO 0x10 +#define IPAC_B_XDU 0x40 +#define IPAC_B_XMR 0x80 + +/* IPAC special registers */ +#define IPAC_CONF 0xC0 /* R/W */ +#define IPAC_ISTA 0xC1 /* RD */ +#define IPAC_MASK 0xC1 /* WR */ +#define IPAC_ID 0xC2 /* RD */ +#define IPAC_ACFG 0xC3 /* R/W */ +#define IPAC_AOE 0xC4 /* R/W */ +#define IPAC_ARX 0xC5 /* RD */ +#define IPAC_ATX 0xC5 /* WR */ +#define IPAC_PITA1 0xC6 /* R/W */ +#define IPAC_PITA2 0xC7 /* R/W */ +#define IPAC_POTA1 0xC8 /* R/W */ +#define IPAC_POTA2 0xC9 /* R/W */ +#define IPAC_PCFG 0xCA /* R/W */ +#define IPAC_SCFG 0xCB /* R/W */ +#define IPAC_TIMR2 0xCC /* R/W */ + +/* IPAC_ISTA/_MASK bits */ +#define IPAC__EXB 0x01 +#define IPAC__ICB 0x02 +#define IPAC__EXA 0x04 +#define IPAC__ICA 0x08 +#define IPAC__EXD 0x10 +#define IPAC__ICD 0x20 +#define IPAC__INT0 0x40 +#define IPAC__INT1 0x80 +#define IPAC__ON 0xC0 + +/* HSCX ISTA/MASK bits */ +#define HSCX__EXB 0x01 +#define HSCX__EXA 0x02 +#define HSCX__ICA 0x04 + +/* ISAC/ISACX/IPAC/IPACX L1 commands */ +#define ISAC_CMD_TIM 0x0 +#define ISAC_CMD_RS 0x1 +#define ISAC_CMD_SCZ 0x4 +#define ISAC_CMD_SSZ 0x2 +#define ISAC_CMD_AR8 0x8 +#define ISAC_CMD_AR10 0x9 +#define ISAC_CMD_ARL 0xA +#define ISAC_CMD_DUI 0xF + +/* ISAC/ISACX/IPAC/IPACX L1 indications */ +#define ISAC_IND_DR 0x0 +#define ISAC_IND_RS 0x1 +#define ISAC_IND_SD 0x2 +#define ISAC_IND_DIS 0x3 +#define ISAC_IND_RSY 0x4 +#define ISAC_IND_DR6 0x5 +#define ISAC_IND_EI 0x6 +#define ISAC_IND_PU 0x7 +#define ISAC_IND_ARD 0x8 +#define ISAC_IND_TI 0xA +#define ISAC_IND_ATI 0xB +#define ISAC_IND_AI8 0xC +#define ISAC_IND_AI10 0xD +#define ISAC_IND_DID 0xF + +/* the new ISACX / IPACX */ +/* D-channel registers */ +#define ISACX_RFIFOD 0x00 /* RD */ +#define ISACX_XFIFOD 0x00 /* WR */ +#define ISACX_ISTAD 0x20 /* RD */ +#define ISACX_MASKD 0x20 /* WR */ +#define ISACX_STARD 0x21 /* RD */ +#define ISACX_CMDRD 0x21 /* WR */ +#define ISACX_MODED 0x22 /* R/W */ +#define ISACX_EXMD1 0x23 /* R/W */ +#define ISACX_TIMR1 0x24 /* R/W */ +#define ISACX_SAP1 0x25 /* WR */ +#define ISACX_SAP2 0x26 /* WR */ +#define ISACX_RBCLD 0x26 /* RD */ +#define ISACX_RBCHD 0x27 /* RD */ +#define ISACX_TEI1 0x27 /* WR */ +#define ISACX_TEI2 0x28 /* WR */ +#define ISACX_RSTAD 0x28 /* RD */ +#define ISACX_TMD 0x29 /* R/W */ +#define ISACX_CIR0 0x2E /* RD */ +#define ISACX_CIX0 0x2E /* WR */ +#define ISACX_CIR1 0x2F /* RD */ +#define ISACX_CIX1 0x2F /* WR */ + +/* Transceiver registers */ +#define ISACX_TR_CONF0 0x30 /* R/W */ +#define ISACX_TR_CONF1 0x31 /* R/W */ +#define ISACX_TR_CONF2 0x32 /* R/W */ +#define ISACX_TR_STA 0x33 /* RD */ +#define ISACX_TR_CMD 0x34 /* R/W */ +#define ISACX_SQRR1 0x35 /* RD */ +#define ISACX_SQXR1 0x35 /* WR */ +#define ISACX_SQRR2 0x36 /* RD */ +#define ISACX_SQXR2 0x36 /* WR */ +#define ISACX_SQRR3 0x37 /* RD */ +#define ISACX_SQXR3 0x37 /* WR */ +#define ISACX_ISTATR 0x38 /* RD */ +#define ISACX_MASKTR 0x39 /* R/W */ +#define ISACX_TR_MODE 0x3A /* R/W */ +#define ISACX_ACFG1 0x3C /* R/W */ +#define ISACX_ACFG2 0x3D /* R/W */ +#define ISACX_AOE 0x3E /* R/W */ +#define ISACX_ARX 0x3F /* RD */ +#define ISACX_ATX 0x3F /* WR */ + +/* IOM: Timeslot, DPS, CDA */ +#define ISACX_CDA10 0x40 /* R/W */ +#define ISACX_CDA11 0x41 /* R/W */ +#define ISACX_CDA20 0x42 /* R/W */ +#define ISACX_CDA21 0x43 /* R/W */ +#define ISACX_CDA_TSDP10 0x44 /* R/W */ +#define ISACX_CDA_TSDP11 0x45 /* R/W */ +#define ISACX_CDA_TSDP20 0x46 /* R/W */ +#define ISACX_CDA_TSDP21 0x47 /* R/W */ +#define ISACX_BCHA_TSDP_BC1 0x48 /* R/W */ +#define ISACX_BCHA_TSDP_BC2 0x49 /* R/W */ +#define ISACX_BCHB_TSDP_BC1 0x4A /* R/W */ +#define ISACX_BCHB_TSDP_BC2 0x4B /* R/W */ +#define ISACX_TR_TSDP_BC1 0x4C /* R/W */ +#define ISACX_TR_TSDP_BC2 0x4D /* R/W */ +#define ISACX_CDA1_CR 0x4E /* R/W */ +#define ISACX_CDA2_CR 0x4F /* R/W */ + +/* IOM: Contol, Sync transfer, Monitor */ +#define ISACX_TR_CR 0x50 /* R/W */ +#define ISACX_TRC_CR 0x50 /* R/W */ +#define ISACX_BCHA_CR 0x51 /* R/W */ +#define ISACX_BCHB_CR 0x52 /* R/W */ +#define ISACX_DCI_CR 0x53 /* R/W */ +#define ISACX_DCIC_CR 0x53 /* R/W */ +#define ISACX_MON_CR 0x54 /* R/W */ +#define ISACX_SDS1_CR 0x55 /* R/W */ +#define ISACX_SDS2_CR 0x56 /* R/W */ +#define ISACX_IOM_CR 0x57 /* R/W */ +#define ISACX_STI 0x58 /* RD */ +#define ISACX_ASTI 0x58 /* WR */ +#define ISACX_MSTI 0x59 /* R/W */ +#define ISACX_SDS_CONF 0x5A /* R/W */ +#define ISACX_MCDA 0x5B /* RD */ +#define ISACX_MOR 0x5C /* RD */ +#define ISACX_MOX 0x5C /* WR */ +#define ISACX_MOSR 0x5D /* RD */ +#define ISACX_MOCR 0x5E /* R/W */ +#define ISACX_MSTA 0x5F /* RD */ +#define ISACX_MCONF 0x5F /* WR */ + +/* Interrupt and general registers */ +#define ISACX_ISTA 0x60 /* RD */ +#define ISACX_MASK 0x60 /* WR */ +#define ISACX_AUXI 0x61 /* RD */ +#define ISACX_AUXM 0x61 /* WR */ +#define ISACX_MODE1 0x62 /* R/W */ +#define ISACX_MODE2 0x63 /* R/W */ +#define ISACX_ID 0x64 /* RD */ +#define ISACX_SRES 0x64 /* WR */ +#define ISACX_TIMR2 0x65 /* R/W */ + +/* Register Bits */ +/* ISACX/IPACX _ISTAD (R) and _MASKD (W) */ +#define ISACX_D_XDU 0x04 +#define ISACX_D_XMR 0x08 +#define ISACX_D_XPR 0x10 +#define ISACX_D_RFO 0x20 +#define ISACX_D_RPF 0x40 +#define ISACX_D_RME 0x80 + +/* ISACX/IPACX _ISTA (R) and _MASK (W) */ +#define ISACX__ICD 0x01 +#define ISACX__MOS 0x02 +#define ISACX__TRAN 0x04 +#define ISACX__AUX 0x08 +#define ISACX__CIC 0x10 +#define ISACX__ST 0x20 +#define IPACX__ON 0x2C +#define IPACX__ICB 0x40 +#define IPACX__ICA 0x80 + +/* ISACX/IPACX _CMDRD (W) */ +#define ISACX_CMDRD_XRES 0x01 +#define ISACX_CMDRD_XME 0x02 +#define ISACX_CMDRD_XTF 0x08 +#define ISACX_CMDRD_STI 0x10 +#define ISACX_CMDRD_RRES 0x40 +#define ISACX_CMDRD_RMC 0x80 + +/* ISACX/IPACX _RSTAD (R) */ +#define ISACX_RSTAD_TA 0x01 +#define ISACX_RSTAD_CR 0x02 +#define ISACX_RSTAD_SA0 0x04 +#define ISACX_RSTAD_SA1 0x08 +#define ISACX_RSTAD_RAB 0x10 +#define ISACX_RSTAD_CRC 0x20 +#define ISACX_RSTAD_RDO 0x40 +#define ISACX_RSTAD_VFR 0x80 + +/* ISACX/IPACX _CIR0 (R) */ +#define ISACX_CIR0_BAS 0x01 +#define ISACX_CIR0_SG 0x08 +#define ISACX_CIR0_CIC1 0x08 +#define ISACX_CIR0_CIC0 0x08 + +/* B-channel registers */ +#define IPACX_OFF_ICA 0x70 +#define IPACX_OFF_ICB 0x80 + +/* ICA: IPACX_OFF_ICA + Reg ICB: IPACX_OFF_ICB + Reg */ + +#define IPACX_ISTAB 0x00 /* RD */ +#define IPACX_MASKB 0x00 /* WR */ +#define IPACX_STARB 0x01 /* RD */ +#define IPACX_CMDRB 0x01 /* WR */ +#define IPACX_MODEB 0x02 /* R/W */ +#define IPACX_EXMB 0x03 /* R/W */ +#define IPACX_RAH1 0x05 /* WR */ +#define IPACX_RAH2 0x06 /* WR */ +#define IPACX_RBCLB 0x06 /* RD */ +#define IPACX_RBCHB 0x07 /* RD */ +#define IPACX_RAL1 0x07 /* WR */ +#define IPACX_RAL2 0x08 /* WR */ +#define IPACX_RSTAB 0x08 /* RD */ +#define IPACX_TMB 0x09 /* R/W */ +#define IPACX_RFIFOB 0x0A /* RD */ +#define IPACX_XFIFOB 0x0A /* WR */ + +/* IPACX_ISTAB / IPACX_MASKB bits */ +#define IPACX_B_XDU 0x04 +#define IPACX_B_XPR 0x10 +#define IPACX_B_RFO 0x20 +#define IPACX_B_RPF 0x40 +#define IPACX_B_RME 0x80 + +#define IPACX_B_ON 0x0B + +extern int mISDNisac_init(struct isac_hw *, void *); +extern irqreturn_t mISDNisac_irq(struct isac_hw *, u8); +extern u32 mISDNipac_init(struct ipac_hw *, void *); +extern irqreturn_t mISDNipac_irq(struct ipac_hw *, int); diff --git a/drivers/isdn/hardware/mISDN/isar.h b/drivers/isdn/hardware/mISDN/isar.h new file mode 100644 index 000000000..cadfc49c9 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/isar.h @@ -0,0 +1,269 @@ +/* + * + * isar.h ISAR (Siemens PSB 7110) specific defines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "iohelper.h" + +struct isar_hw; + +struct isar_ch { + struct bchannel bch; + struct isar_hw *is; + struct timer_list ftimer; + u8 nr; + u8 dpath; + u8 mml; + u8 state; + u8 cmd; + u8 mod; + u8 newcmd; + u8 newmod; + u8 try_mod; + u8 conmsg[16]; +}; + +struct isar_hw { + struct isar_ch ch[2]; + void *hw; + spinlock_t *hwlock; /* lock HW access */ + char *name; + struct module *owner; + read_reg_func *read_reg; + write_reg_func *write_reg; + fifo_func *read_fifo; + fifo_func *write_fifo; + int (*ctrl)(void *, u32, u_long); + void (*release)(struct isar_hw *); + int (*init)(struct isar_hw *); + int (*open)(struct isar_hw *, struct channel_req *); + int (*firmware)(struct isar_hw *, const u8 *, int); + unsigned long Flags; + int version; + u8 bstat; + u8 iis; + u8 cmsb; + u8 clsb; + u8 buf[256]; + u8 log[256]; +}; + +#define ISAR_IRQMSK 0x04 +#define ISAR_IRQSTA 0x04 +#define ISAR_IRQBIT 0x75 +#define ISAR_CTRL_H 0x61 +#define ISAR_CTRL_L 0x60 +#define ISAR_IIS 0x58 +#define ISAR_IIA 0x58 +#define ISAR_HIS 0x50 +#define ISAR_HIA 0x50 +#define ISAR_MBOX 0x4c +#define ISAR_WADR 0x4a +#define ISAR_RADR 0x48 + +#define ISAR_HIS_VNR 0x14 +#define ISAR_HIS_DKEY 0x02 +#define ISAR_HIS_FIRM 0x1e +#define ISAR_HIS_STDSP 0x08 +#define ISAR_HIS_DIAG 0x05 +#define ISAR_HIS_P0CFG 0x3c +#define ISAR_HIS_P12CFG 0x24 +#define ISAR_HIS_SARTCFG 0x25 +#define ISAR_HIS_PUMPCFG 0x26 +#define ISAR_HIS_PUMPCTRL 0x2a +#define ISAR_HIS_IOM2CFG 0x27 +#define ISAR_HIS_IOM2REQ 0x07 +#define ISAR_HIS_IOM2CTRL 0x2b +#define ISAR_HIS_BSTREQ 0x0c +#define ISAR_HIS_PSTREQ 0x0e +#define ISAR_HIS_SDATA 0x20 +#define ISAR_HIS_DPS1 0x40 +#define ISAR_HIS_DPS2 0x80 +#define SET_DPS(x) ((x << 6) & 0xc0) + +#define ISAR_IIS_MSCMSD 0x3f +#define ISAR_IIS_VNR 0x15 +#define ISAR_IIS_DKEY 0x03 +#define ISAR_IIS_FIRM 0x1f +#define ISAR_IIS_STDSP 0x09 +#define ISAR_IIS_DIAG 0x25 +#define ISAR_IIS_GSTEV 0x00 +#define ISAR_IIS_BSTEV 0x28 +#define ISAR_IIS_BSTRSP 0x2c +#define ISAR_IIS_PSTRSP 0x2e +#define ISAR_IIS_PSTEV 0x2a +#define ISAR_IIS_IOM2RSP 0x27 +#define ISAR_IIS_RDATA 0x20 +#define ISAR_IIS_INVMSG 0x3f + +#define ISAR_CTRL_SWVER 0x10 +#define ISAR_CTRL_STST 0x40 + +#define ISAR_MSG_HWVER 0x20 + +#define ISAR_DP1_USE 1 +#define ISAR_DP2_USE 2 +#define ISAR_RATE_REQ 3 + +#define PMOD_DISABLE 0 +#define PMOD_FAX 1 +#define PMOD_DATAMODEM 2 +#define PMOD_HALFDUPLEX 3 +#define PMOD_V110 4 +#define PMOD_DTMF 5 +#define PMOD_DTMF_TRANS 6 +#define PMOD_BYPASS 7 + +#define PCTRL_ORIG 0x80 +#define PV32P2_V23R 0x40 +#define PV32P2_V22A 0x20 +#define PV32P2_V22B 0x10 +#define PV32P2_V22C 0x08 +#define PV32P2_V21 0x02 +#define PV32P2_BEL 0x01 + +/* LSB MSB in ISAR doc wrong !!! Arghhh */ +#define PV32P3_AMOD 0x80 +#define PV32P3_V32B 0x02 +#define PV32P3_V23B 0x01 +#define PV32P4_48 0x11 +#define PV32P5_48 0x05 +#define PV32P4_UT48 0x11 +#define PV32P5_UT48 0x0d +#define PV32P4_96 0x11 +#define PV32P5_96 0x03 +#define PV32P4_UT96 0x11 +#define PV32P5_UT96 0x0f +#define PV32P4_B96 0x91 +#define PV32P5_B96 0x0b +#define PV32P4_UTB96 0xd1 +#define PV32P5_UTB96 0x0f +#define PV32P4_120 0xb1 +#define PV32P5_120 0x09 +#define PV32P4_UT120 0xf1 +#define PV32P5_UT120 0x0f +#define PV32P4_144 0x99 +#define PV32P5_144 0x09 +#define PV32P4_UT144 0xf9 +#define PV32P5_UT144 0x0f +#define PV32P6_CTN 0x01 +#define PV32P6_ATN 0x02 + +#define PFAXP2_CTN 0x01 +#define PFAXP2_ATN 0x04 + +#define PSEV_10MS_TIMER 0x02 +#define PSEV_CON_ON 0x18 +#define PSEV_CON_OFF 0x19 +#define PSEV_V24_OFF 0x20 +#define PSEV_CTS_ON 0x21 +#define PSEV_CTS_OFF 0x22 +#define PSEV_DCD_ON 0x23 +#define PSEV_DCD_OFF 0x24 +#define PSEV_DSR_ON 0x25 +#define PSEV_DSR_OFF 0x26 +#define PSEV_REM_RET 0xcc +#define PSEV_REM_REN 0xcd +#define PSEV_GSTN_CLR 0xd4 + +#define PSEV_RSP_READY 0xbc +#define PSEV_LINE_TX_H 0xb3 +#define PSEV_LINE_TX_B 0xb2 +#define PSEV_LINE_RX_H 0xb1 +#define PSEV_LINE_RX_B 0xb0 +#define PSEV_RSP_CONN 0xb5 +#define PSEV_RSP_DISC 0xb7 +#define PSEV_RSP_FCERR 0xb9 +#define PSEV_RSP_SILDET 0xbe +#define PSEV_RSP_SILOFF 0xab +#define PSEV_FLAGS_DET 0xba + +#define PCTRL_CMD_TDTMF 0x5a + +#define PCTRL_CMD_FTH 0xa7 +#define PCTRL_CMD_FRH 0xa5 +#define PCTRL_CMD_FTM 0xa8 +#define PCTRL_CMD_FRM 0xa6 +#define PCTRL_CMD_SILON 0xac +#define PCTRL_CMD_CONT 0xa2 +#define PCTRL_CMD_ESC 0xa4 +#define PCTRL_CMD_SILOFF 0xab +#define PCTRL_CMD_HALT 0xa9 + +#define PCTRL_LOC_RET 0xcf +#define PCTRL_LOC_REN 0xce + +#define SMODE_DISABLE 0 +#define SMODE_V14 2 +#define SMODE_HDLC 3 +#define SMODE_BINARY 4 +#define SMODE_FSK_V14 5 + +#define SCTRL_HDMC_BOTH 0x00 +#define SCTRL_HDMC_DTX 0x80 +#define SCTRL_HDMC_DRX 0x40 +#define S_P1_OVSP 0x40 +#define S_P1_SNP 0x20 +#define S_P1_EOP 0x10 +#define S_P1_EDP 0x08 +#define S_P1_NSB 0x04 +#define S_P1_CHS_8 0x03 +#define S_P1_CHS_7 0x02 +#define S_P1_CHS_6 0x01 +#define S_P1_CHS_5 0x00 + +#define S_P2_BFT_DEF 0x10 + +#define IOM_CTRL_ENA 0x80 +#define IOM_CTRL_NOPCM 0x00 +#define IOM_CTRL_ALAW 0x02 +#define IOM_CTRL_ULAW 0x04 +#define IOM_CTRL_RCV 0x01 + +#define IOM_P1_TXD 0x10 + +#define HDLC_FED 0x40 +#define HDLC_FSD 0x20 +#define HDLC_FST 0x20 +#define HDLC_ERROR 0x1c +#define HDLC_ERR_FAD 0x10 +#define HDLC_ERR_RER 0x08 +#define HDLC_ERR_CER 0x04 +#define SART_NMD 0x01 + +#define BSTAT_RDM0 0x1 +#define BSTAT_RDM1 0x2 +#define BSTAT_RDM2 0x4 +#define BSTAT_RDM3 0x8 +#define BSTEV_TBO 0x1f +#define BSTEV_RBO 0x2f + +/* FAX State Machine */ +#define STFAX_NULL 0 +#define STFAX_READY 1 +#define STFAX_LINE 2 +#define STFAX_CONT 3 +#define STFAX_ACTIV 4 +#define STFAX_ESCAPE 5 +#define STFAX_SILDET 6 + +extern u32 mISDNisar_init(struct isar_hw *, void *); +extern void mISDNisar_irq(struct isar_hw *); diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c new file mode 100644 index 000000000..95a0d728e --- /dev/null +++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c @@ -0,0 +1,1182 @@ +/* + * mISDNinfineon.c + * Support for cards based on following Infineon ISDN chipsets + * - ISAC + HSCX + * - IPAC and IPAC-X + * - ISAC-SX + HSCX + * + * Supported cards: + * - Dialogic Diva 2.0 + * - Dialogic Diva 2.0U + * - Dialogic Diva 2.01 + * - Dialogic Diva 2.02 + * - Sedlbauer Speedwin + * - HST Saphir3 + * - Develo (former ELSA) Microlink PCI (Quickstep 1000) + * - Develo (former ELSA) Quickstep 3000 + * - Berkom Scitel BRIX Quadro + * - Dr.Neuhaus (Sagem) Niccy + * + * + * + * Author Karsten Keil <keil@isdn4linux.de> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> +#include "ipac.h" + +#define INFINEON_REV "1.0" + +static int inf_cnt; +static u32 debug; +static u32 irqloops = 4; + +enum inf_types { + INF_NONE, + INF_DIVA20, + INF_DIVA20U, + INF_DIVA201, + INF_DIVA202, + INF_SPEEDWIN, + INF_SAPHIR3, + INF_QS1000, + INF_QS3000, + INF_NICCY, + INF_SCT_1, + INF_SCT_2, + INF_SCT_3, + INF_SCT_4, + INF_GAZEL_R685, + INF_GAZEL_R753 +}; + +enum addr_mode { + AM_NONE = 0, + AM_IO, + AM_MEMIO, + AM_IND_IO, +}; + +struct inf_cinfo { + enum inf_types typ; + const char *full; + const char *name; + enum addr_mode cfg_mode; + enum addr_mode addr_mode; + u8 cfg_bar; + u8 addr_bar; + void *irqfunc; +}; + +struct _ioaddr { + enum addr_mode mode; + union { + void __iomem *p; + struct _ioport io; + } a; +}; + +struct _iohandle { + enum addr_mode mode; + resource_size_t size; + resource_size_t start; + void __iomem *p; +}; + +struct inf_hw { + struct list_head list; + struct pci_dev *pdev; + const struct inf_cinfo *ci; + char name[MISDN_MAX_IDLEN]; + u32 irq; + u32 irqcnt; + struct _iohandle cfg; + struct _iohandle addr; + struct _ioaddr isac; + struct _ioaddr hscx; + spinlock_t lock; /* HW access lock */ + struct ipac_hw ipac; + struct inf_hw *sc[3]; /* slave cards */ +}; + + +#define PCI_SUBVENDOR_HST_SAPHIR3 0x52 +#define PCI_SUBVENDOR_SEDLBAUER_PCI 0x53 +#define PCI_SUB_ID_SEDLBAUER 0x01 + +static struct pci_device_id infineon_ids[] = { + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20), INF_DIVA20 }, + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20_U), INF_DIVA20U }, + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA201), INF_DIVA201 }, + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA202), INF_DIVA202 }, + { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, + PCI_SUBVENDOR_SEDLBAUER_PCI, PCI_SUB_ID_SEDLBAUER, 0, 0, + INF_SPEEDWIN }, + { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, + PCI_SUBVENDOR_HST_SAPHIR3, PCI_SUB_ID_SEDLBAUER, 0, 0, INF_SAPHIR3 }, + { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_MICROLINK), INF_QS1000 }, + { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_QS3000), INF_QS3000 }, + { PCI_VDEVICE(SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY), INF_NICCY }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO, 0, 0, + INF_SCT_1 }, + { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R685), INF_GAZEL_R685 }, + { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R753), INF_GAZEL_R753 }, + { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO), INF_GAZEL_R753 }, + { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_OLITEC), INF_GAZEL_R753 }, + { } +}; +MODULE_DEVICE_TABLE(pci, infineon_ids); + +/* PCI interface specific defines */ +/* Diva 2.0/2.0U */ +#define DIVA_HSCX_PORT 0x00 +#define DIVA_HSCX_ALE 0x04 +#define DIVA_ISAC_PORT 0x08 +#define DIVA_ISAC_ALE 0x0C +#define DIVA_PCI_CTRL 0x10 + +/* DIVA_PCI_CTRL bits */ +#define DIVA_IRQ_BIT 0x01 +#define DIVA_RESET_BIT 0x08 +#define DIVA_EEPROM_CLK 0x40 +#define DIVA_LED_A 0x10 +#define DIVA_LED_B 0x20 +#define DIVA_IRQ_CLR 0x80 + +/* Diva 2.01/2.02 */ +/* Siemens PITA */ +#define PITA_ICR_REG 0x00 +#define PITA_INT0_STATUS 0x02 + +#define PITA_MISC_REG 0x1c +#define PITA_PARA_SOFTRESET 0x01000000 +#define PITA_SER_SOFTRESET 0x02000000 +#define PITA_PARA_MPX_MODE 0x04000000 +#define PITA_INT0_ENABLE 0x00020000 + +/* TIGER 100 Registers */ +#define TIGER_RESET_ADDR 0x00 +#define TIGER_EXTERN_RESET 0x01 +#define TIGER_AUX_CTRL 0x02 +#define TIGER_AUX_DATA 0x03 +#define TIGER_AUX_IRQMASK 0x05 +#define TIGER_AUX_STATUS 0x07 + +/* Tiger AUX BITs */ +#define TIGER_IOMASK 0xdd /* 1 and 5 are inputs */ +#define TIGER_IRQ_BIT 0x02 + +#define TIGER_IPAC_ALE 0xC0 +#define TIGER_IPAC_PORT 0xC8 + +/* ELSA (now Develo) PCI cards */ +#define ELSA_IRQ_ADDR 0x4c +#define ELSA_IRQ_MASK 0x04 +#define QS1000_IRQ_OFF 0x01 +#define QS3000_IRQ_OFF 0x03 +#define QS1000_IRQ_ON 0x41 +#define QS3000_IRQ_ON 0x43 + +/* Dr Neuhaus/Sagem Niccy */ +#define NICCY_ISAC_PORT 0x00 +#define NICCY_HSCX_PORT 0x01 +#define NICCY_ISAC_ALE 0x02 +#define NICCY_HSCX_ALE 0x03 + +#define NICCY_IRQ_CTRL_REG 0x38 +#define NICCY_IRQ_ENABLE 0x001f00 +#define NICCY_IRQ_DISABLE 0xff0000 +#define NICCY_IRQ_BIT 0x800000 + + +/* Scitel PLX */ +#define SCT_PLX_IRQ_ADDR 0x4c +#define SCT_PLX_RESET_ADDR 0x50 +#define SCT_PLX_IRQ_ENABLE 0x41 +#define SCT_PLX_RESET_BIT 0x04 + +/* Gazel */ +#define GAZEL_IPAC_DATA_PORT 0x04 +/* Gazel PLX */ +#define GAZEL_CNTRL 0x50 +#define GAZEL_RESET 0x04 +#define GAZEL_RESET_9050 0x40000000 +#define GAZEL_INCSR 0x4C +#define GAZEL_ISAC_EN 0x08 +#define GAZEL_INT_ISAC 0x20 +#define GAZEL_HSCX_EN 0x01 +#define GAZEL_INT_HSCX 0x04 +#define GAZEL_PCI_EN 0x40 +#define GAZEL_IPAC_EN 0x03 + + +static LIST_HEAD(Cards); +static DEFINE_RWLOCK(card_lock); /* protect Cards */ + +static void +_set_debug(struct inf_hw *card) +{ + card->ipac.isac.dch.debug = debug; + card->ipac.hscx[0].bch.debug = debug; + card->ipac.hscx[1].bch.debug = debug; +} + +static int +set_debug(const char *val, const struct kernel_param *kp) +{ + int ret; + struct inf_hw *card; + + ret = param_set_uint(val, kp); + if (!ret) { + read_lock(&card_lock); + list_for_each_entry(card, &Cards, list) + _set_debug(card); + read_unlock(&card_lock); + } + return ret; +} + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(INFINEON_REV); +module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "infineon debug mask"); +module_param(irqloops, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(irqloops, "infineon maximal irqloops (default 4)"); + +/* Interface functions */ + +IOFUNC_IO(ISAC, inf_hw, isac.a.io) +IOFUNC_IO(IPAC, inf_hw, hscx.a.io) +IOFUNC_IND(ISAC, inf_hw, isac.a.io) +IOFUNC_IND(IPAC, inf_hw, hscx.a.io) +IOFUNC_MEMIO(ISAC, inf_hw, u32, isac.a.p) +IOFUNC_MEMIO(IPAC, inf_hw, u32, hscx.a.p) + +static irqreturn_t +diva_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u8 val; + + spin_lock(&hw->lock); + val = inb((u32)hw->cfg.start + DIVA_PCI_CTRL); + if (!(val & DIVA_IRQ_BIT)) { /* for us or shared ? */ + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +diva20x_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u8 val; + + spin_lock(&hw->lock); + val = readb(hw->cfg.p); + if (!(val & PITA_INT0_STATUS)) { /* for us or shared ? */ + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + writeb(PITA_INT0_STATUS, hw->cfg.p); /* ACK PITA INT0 */ + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +tiger_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u8 val; + + spin_lock(&hw->lock); + val = inb((u32)hw->cfg.start + TIGER_AUX_STATUS); + if (val & TIGER_IRQ_BIT) { /* for us or shared ? */ + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +elsa_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u8 val; + + spin_lock(&hw->lock); + val = inb((u32)hw->cfg.start + ELSA_IRQ_ADDR); + if (!(val & ELSA_IRQ_MASK)) { + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +niccy_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u32 val; + + spin_lock(&hw->lock); + val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + if (!(val & NICCY_IRQ_BIT)) { /* for us or shared ? */ + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +gazel_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + irqreturn_t ret; + + spin_lock(&hw->lock); + ret = mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return ret; +} + +static irqreturn_t +ipac_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u8 val; + + spin_lock(&hw->lock); + val = hw->ipac.read_reg(hw, IPAC_ISTA); + if (!(val & 0x3f)) { + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static void +enable_hwirq(struct inf_hw *hw) +{ + u16 w; + u32 val; + + switch (hw->ci->typ) { + case INF_DIVA201: + case INF_DIVA202: + writel(PITA_INT0_ENABLE, hw->cfg.p); + break; + case INF_SPEEDWIN: + case INF_SAPHIR3: + outb(TIGER_IRQ_BIT, (u32)hw->cfg.start + TIGER_AUX_IRQMASK); + break; + case INF_QS1000: + outb(QS1000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR); + break; + case INF_QS3000: + outb(QS3000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR); + break; + case INF_NICCY: + val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + val |= NICCY_IRQ_ENABLE; + outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + break; + case INF_SCT_1: + w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); + w |= SCT_PLX_IRQ_ENABLE; + outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); + break; + case INF_GAZEL_R685: + outb(GAZEL_ISAC_EN + GAZEL_HSCX_EN + GAZEL_PCI_EN, + (u32)hw->cfg.start + GAZEL_INCSR); + break; + case INF_GAZEL_R753: + outb(GAZEL_IPAC_EN + GAZEL_PCI_EN, + (u32)hw->cfg.start + GAZEL_INCSR); + break; + default: + break; + } +} + +static void +disable_hwirq(struct inf_hw *hw) +{ + u16 w; + u32 val; + + switch (hw->ci->typ) { + case INF_DIVA201: + case INF_DIVA202: + writel(0, hw->cfg.p); + break; + case INF_SPEEDWIN: + case INF_SAPHIR3: + outb(0, (u32)hw->cfg.start + TIGER_AUX_IRQMASK); + break; + case INF_QS1000: + outb(QS1000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR); + break; + case INF_QS3000: + outb(QS3000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR); + break; + case INF_NICCY: + val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + val &= NICCY_IRQ_DISABLE; + outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + break; + case INF_SCT_1: + w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); + w &= (~SCT_PLX_IRQ_ENABLE); + outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); + break; + case INF_GAZEL_R685: + case INF_GAZEL_R753: + outb(0, (u32)hw->cfg.start + GAZEL_INCSR); + break; + default: + break; + } +} + +static void +ipac_chip_reset(struct inf_hw *hw) +{ + hw->ipac.write_reg(hw, IPAC_POTA2, 0x20); + mdelay(5); + hw->ipac.write_reg(hw, IPAC_POTA2, 0x00); + mdelay(5); + hw->ipac.write_reg(hw, IPAC_CONF, hw->ipac.conf); + hw->ipac.write_reg(hw, IPAC_MASK, 0xc0); +} + +static void +reset_inf(struct inf_hw *hw) +{ + u16 w; + u32 val; + + if (debug & DEBUG_HW) + pr_notice("%s: resetting card\n", hw->name); + switch (hw->ci->typ) { + case INF_DIVA20: + case INF_DIVA20U: + outb(0, (u32)hw->cfg.start + DIVA_PCI_CTRL); + mdelay(10); + outb(DIVA_RESET_BIT, (u32)hw->cfg.start + DIVA_PCI_CTRL); + mdelay(10); + /* Workaround PCI9060 */ + outb(9, (u32)hw->cfg.start + 0x69); + outb(DIVA_RESET_BIT | DIVA_LED_A, + (u32)hw->cfg.start + DIVA_PCI_CTRL); + break; + case INF_DIVA201: + writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE, + hw->cfg.p + PITA_MISC_REG); + mdelay(1); + writel(PITA_PARA_MPX_MODE, hw->cfg.p + PITA_MISC_REG); + mdelay(10); + break; + case INF_DIVA202: + writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE, + hw->cfg.p + PITA_MISC_REG); + mdelay(1); + writel(PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET, + hw->cfg.p + PITA_MISC_REG); + mdelay(10); + break; + case INF_SPEEDWIN: + case INF_SAPHIR3: + ipac_chip_reset(hw); + hw->ipac.write_reg(hw, IPAC_ACFG, 0xff); + hw->ipac.write_reg(hw, IPAC_AOE, 0x00); + hw->ipac.write_reg(hw, IPAC_PCFG, 0x12); + break; + case INF_QS1000: + case INF_QS3000: + ipac_chip_reset(hw); + hw->ipac.write_reg(hw, IPAC_ACFG, 0x00); + hw->ipac.write_reg(hw, IPAC_AOE, 0x3c); + hw->ipac.write_reg(hw, IPAC_ATX, 0xff); + break; + case INF_NICCY: + break; + case INF_SCT_1: + w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR); + w &= (~SCT_PLX_RESET_BIT); + outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); + mdelay(10); + w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR); + w |= SCT_PLX_RESET_BIT; + outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); + mdelay(10); + break; + case INF_GAZEL_R685: + val = inl((u32)hw->cfg.start + GAZEL_CNTRL); + val |= (GAZEL_RESET_9050 + GAZEL_RESET); + outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); + val &= ~(GAZEL_RESET_9050 + GAZEL_RESET); + mdelay(4); + outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); + mdelay(10); + hw->ipac.isac.adf2 = 0x87; + hw->ipac.hscx[0].slot = 0x1f; + hw->ipac.hscx[1].slot = 0x23; + break; + case INF_GAZEL_R753: + val = inl((u32)hw->cfg.start + GAZEL_CNTRL); + val |= (GAZEL_RESET_9050 + GAZEL_RESET); + outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); + val &= ~(GAZEL_RESET_9050 + GAZEL_RESET); + mdelay(4); + outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); + mdelay(10); + ipac_chip_reset(hw); + hw->ipac.write_reg(hw, IPAC_ACFG, 0xff); + hw->ipac.write_reg(hw, IPAC_AOE, 0x00); + hw->ipac.conf = 0x01; /* IOM off */ + break; + default: + return; + } + enable_hwirq(hw); +} + +static int +inf_ctrl(struct inf_hw *hw, u32 cmd, u_long arg) +{ + int ret = 0; + + switch (cmd) { + case HW_RESET_REQ: + reset_inf(hw); + break; + default: + pr_info("%s: %s unknown command %x %lx\n", + hw->name, __func__, cmd, arg); + ret = -EINVAL; + break; + } + return ret; +} + +static int +init_irq(struct inf_hw *hw) +{ + int ret, cnt = 3; + u_long flags; + + if (!hw->ci->irqfunc) + return -EINVAL; + ret = request_irq(hw->irq, hw->ci->irqfunc, IRQF_SHARED, hw->name, hw); + if (ret) { + pr_info("%s: couldn't get interrupt %d\n", hw->name, hw->irq); + return ret; + } + while (cnt--) { + spin_lock_irqsave(&hw->lock, flags); + reset_inf(hw); + ret = hw->ipac.init(&hw->ipac); + if (ret) { + spin_unlock_irqrestore(&hw->lock, flags); + pr_info("%s: ISAC init failed with %d\n", + hw->name, ret); + break; + } + spin_unlock_irqrestore(&hw->lock, flags); + msleep_interruptible(10); + if (debug & DEBUG_HW) + pr_notice("%s: IRQ %d count %d\n", hw->name, + hw->irq, hw->irqcnt); + if (!hw->irqcnt) { + pr_info("%s: IRQ(%d) got no requests during init %d\n", + hw->name, hw->irq, 3 - cnt); + } else + return 0; + } + free_irq(hw->irq, hw); + return -EIO; +} + +static void +release_io(struct inf_hw *hw) +{ + if (hw->cfg.mode) { + if (hw->cfg.mode == AM_MEMIO) { + release_mem_region(hw->cfg.start, hw->cfg.size); + if (hw->cfg.p) + iounmap(hw->cfg.p); + } else + release_region(hw->cfg.start, hw->cfg.size); + hw->cfg.mode = AM_NONE; + } + if (hw->addr.mode) { + if (hw->addr.mode == AM_MEMIO) { + release_mem_region(hw->addr.start, hw->addr.size); + if (hw->addr.p) + iounmap(hw->addr.p); + } else + release_region(hw->addr.start, hw->addr.size); + hw->addr.mode = AM_NONE; + } +} + +static int +setup_io(struct inf_hw *hw) +{ + int err = 0; + + if (hw->ci->cfg_mode) { + hw->cfg.start = pci_resource_start(hw->pdev, hw->ci->cfg_bar); + hw->cfg.size = pci_resource_len(hw->pdev, hw->ci->cfg_bar); + if (hw->ci->cfg_mode == AM_MEMIO) { + if (!request_mem_region(hw->cfg.start, hw->cfg.size, + hw->name)) + err = -EBUSY; + } else { + if (!request_region(hw->cfg.start, hw->cfg.size, + hw->name)) + err = -EBUSY; + } + if (err) { + pr_info("mISDN: %s config port %lx (%lu bytes)" + "already in use\n", hw->name, + (ulong)hw->cfg.start, (ulong)hw->cfg.size); + return err; + } + hw->cfg.mode = hw->ci->cfg_mode; + if (hw->ci->cfg_mode == AM_MEMIO) { + hw->cfg.p = ioremap(hw->cfg.start, hw->cfg.size); + if (!hw->cfg.p) + return -ENOMEM; + } + if (debug & DEBUG_HW) + pr_notice("%s: IO cfg %lx (%lu bytes) mode%d\n", + hw->name, (ulong)hw->cfg.start, + (ulong)hw->cfg.size, hw->ci->cfg_mode); + + } + if (hw->ci->addr_mode) { + hw->addr.start = pci_resource_start(hw->pdev, hw->ci->addr_bar); + hw->addr.size = pci_resource_len(hw->pdev, hw->ci->addr_bar); + if (hw->ci->addr_mode == AM_MEMIO) { + if (!request_mem_region(hw->addr.start, hw->addr.size, + hw->name)) + err = -EBUSY; + } else { + if (!request_region(hw->addr.start, hw->addr.size, + hw->name)) + err = -EBUSY; + } + if (err) { + pr_info("mISDN: %s address port %lx (%lu bytes)" + "already in use\n", hw->name, + (ulong)hw->addr.start, (ulong)hw->addr.size); + return err; + } + hw->addr.mode = hw->ci->addr_mode; + if (hw->ci->addr_mode == AM_MEMIO) { + hw->addr.p = ioremap(hw->addr.start, hw->addr.size); + if (!hw->addr.p) + return -ENOMEM; + } + if (debug & DEBUG_HW) + pr_notice("%s: IO addr %lx (%lu bytes) mode%d\n", + hw->name, (ulong)hw->addr.start, + (ulong)hw->addr.size, hw->ci->addr_mode); + + } + + switch (hw->ci->typ) { + case INF_DIVA20: + case INF_DIVA20U: + hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; + hw->isac.mode = hw->cfg.mode; + hw->isac.a.io.ale = (u32)hw->cfg.start + DIVA_ISAC_ALE; + hw->isac.a.io.port = (u32)hw->cfg.start + DIVA_ISAC_PORT; + hw->hscx.mode = hw->cfg.mode; + hw->hscx.a.io.ale = (u32)hw->cfg.start + DIVA_HSCX_ALE; + hw->hscx.a.io.port = (u32)hw->cfg.start + DIVA_HSCX_PORT; + break; + case INF_DIVA201: + hw->ipac.type = IPAC_TYPE_IPAC; + hw->ipac.isac.off = 0x80; + hw->isac.mode = hw->addr.mode; + hw->isac.a.p = hw->addr.p; + hw->hscx.mode = hw->addr.mode; + hw->hscx.a.p = hw->addr.p; + break; + case INF_DIVA202: + hw->ipac.type = IPAC_TYPE_IPACX; + hw->isac.mode = hw->addr.mode; + hw->isac.a.p = hw->addr.p; + hw->hscx.mode = hw->addr.mode; + hw->hscx.a.p = hw->addr.p; + break; + case INF_SPEEDWIN: + case INF_SAPHIR3: + hw->ipac.type = IPAC_TYPE_IPAC; + hw->ipac.isac.off = 0x80; + hw->isac.mode = hw->cfg.mode; + hw->isac.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE; + hw->isac.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT; + hw->hscx.mode = hw->cfg.mode; + hw->hscx.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE; + hw->hscx.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT; + outb(0xff, (ulong)hw->cfg.start); + mdelay(1); + outb(0x00, (ulong)hw->cfg.start); + mdelay(1); + outb(TIGER_IOMASK, (ulong)hw->cfg.start + TIGER_AUX_CTRL); + break; + case INF_QS1000: + case INF_QS3000: + hw->ipac.type = IPAC_TYPE_IPAC; + hw->ipac.isac.off = 0x80; + hw->isac.a.io.ale = (u32)hw->addr.start; + hw->isac.a.io.port = (u32)hw->addr.start + 1; + hw->isac.mode = hw->addr.mode; + hw->hscx.a.io.ale = (u32)hw->addr.start; + hw->hscx.a.io.port = (u32)hw->addr.start + 1; + hw->hscx.mode = hw->addr.mode; + break; + case INF_NICCY: + hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; + hw->isac.mode = hw->addr.mode; + hw->isac.a.io.ale = (u32)hw->addr.start + NICCY_ISAC_ALE; + hw->isac.a.io.port = (u32)hw->addr.start + NICCY_ISAC_PORT; + hw->hscx.mode = hw->addr.mode; + hw->hscx.a.io.ale = (u32)hw->addr.start + NICCY_HSCX_ALE; + hw->hscx.a.io.port = (u32)hw->addr.start + NICCY_HSCX_PORT; + break; + case INF_SCT_1: + hw->ipac.type = IPAC_TYPE_IPAC; + hw->ipac.isac.off = 0x80; + hw->isac.a.io.ale = (u32)hw->addr.start; + hw->isac.a.io.port = hw->isac.a.io.ale + 4; + hw->isac.mode = hw->addr.mode; + hw->hscx.a.io.ale = hw->isac.a.io.ale; + hw->hscx.a.io.port = hw->isac.a.io.port; + hw->hscx.mode = hw->addr.mode; + break; + case INF_SCT_2: + hw->ipac.type = IPAC_TYPE_IPAC; + hw->ipac.isac.off = 0x80; + hw->isac.a.io.ale = (u32)hw->addr.start + 0x08; + hw->isac.a.io.port = hw->isac.a.io.ale + 4; + hw->isac.mode = hw->addr.mode; + hw->hscx.a.io.ale = hw->isac.a.io.ale; + hw->hscx.a.io.port = hw->isac.a.io.port; + hw->hscx.mode = hw->addr.mode; + break; + case INF_SCT_3: + hw->ipac.type = IPAC_TYPE_IPAC; + hw->ipac.isac.off = 0x80; + hw->isac.a.io.ale = (u32)hw->addr.start + 0x10; + hw->isac.a.io.port = hw->isac.a.io.ale + 4; + hw->isac.mode = hw->addr.mode; + hw->hscx.a.io.ale = hw->isac.a.io.ale; + hw->hscx.a.io.port = hw->isac.a.io.port; + hw->hscx.mode = hw->addr.mode; + break; + case INF_SCT_4: + hw->ipac.type = IPAC_TYPE_IPAC; + hw->ipac.isac.off = 0x80; + hw->isac.a.io.ale = (u32)hw->addr.start + 0x20; + hw->isac.a.io.port = hw->isac.a.io.ale + 4; + hw->isac.mode = hw->addr.mode; + hw->hscx.a.io.ale = hw->isac.a.io.ale; + hw->hscx.a.io.port = hw->isac.a.io.port; + hw->hscx.mode = hw->addr.mode; + break; + case INF_GAZEL_R685: + hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; + hw->ipac.isac.off = 0x80; + hw->isac.mode = hw->addr.mode; + hw->isac.a.io.port = (u32)hw->addr.start; + hw->hscx.mode = hw->addr.mode; + hw->hscx.a.io.port = hw->isac.a.io.port; + break; + case INF_GAZEL_R753: + hw->ipac.type = IPAC_TYPE_IPAC; + hw->ipac.isac.off = 0x80; + hw->isac.mode = hw->addr.mode; + hw->isac.a.io.ale = (u32)hw->addr.start; + hw->isac.a.io.port = (u32)hw->addr.start + GAZEL_IPAC_DATA_PORT; + hw->hscx.mode = hw->addr.mode; + hw->hscx.a.io.ale = hw->isac.a.io.ale; + hw->hscx.a.io.port = hw->isac.a.io.port; + break; + default: + return -EINVAL; + } + switch (hw->isac.mode) { + case AM_MEMIO: + ASSIGN_FUNC_IPAC(MIO, hw->ipac); + break; + case AM_IND_IO: + ASSIGN_FUNC_IPAC(IND, hw->ipac); + break; + case AM_IO: + ASSIGN_FUNC_IPAC(IO, hw->ipac); + break; + default: + return -EINVAL; + } + return 0; +} + +static void +release_card(struct inf_hw *card) { + ulong flags; + int i; + + spin_lock_irqsave(&card->lock, flags); + disable_hwirq(card); + spin_unlock_irqrestore(&card->lock, flags); + card->ipac.isac.release(&card->ipac.isac); + free_irq(card->irq, card); + mISDN_unregister_device(&card->ipac.isac.dch.dev); + release_io(card); + write_lock_irqsave(&card_lock, flags); + list_del(&card->list); + write_unlock_irqrestore(&card_lock, flags); + switch (card->ci->typ) { + case INF_SCT_2: + case INF_SCT_3: + case INF_SCT_4: + break; + case INF_SCT_1: + for (i = 0; i < 3; i++) { + if (card->sc[i]) + release_card(card->sc[i]); + card->sc[i] = NULL; + } + /* fall through */ + default: + pci_disable_device(card->pdev); + pci_set_drvdata(card->pdev, NULL); + break; + } + kfree(card); + inf_cnt--; +} + +static int +setup_instance(struct inf_hw *card) +{ + int err; + ulong flags; + + snprintf(card->name, MISDN_MAX_IDLEN - 1, "%s.%d", card->ci->name, + inf_cnt + 1); + write_lock_irqsave(&card_lock, flags); + list_add_tail(&card->list, &Cards); + write_unlock_irqrestore(&card_lock, flags); + + _set_debug(card); + card->ipac.isac.name = card->name; + card->ipac.name = card->name; + card->ipac.owner = THIS_MODULE; + spin_lock_init(&card->lock); + card->ipac.isac.hwlock = &card->lock; + card->ipac.hwlock = &card->lock; + card->ipac.ctrl = (void *)&inf_ctrl; + + err = setup_io(card); + if (err) + goto error_setup; + + card->ipac.isac.dch.dev.Bprotocols = + mISDNipac_init(&card->ipac, card); + + if (card->ipac.isac.dch.dev.Bprotocols == 0) + goto error_setup; + + err = mISDN_register_device(&card->ipac.isac.dch.dev, + &card->pdev->dev, card->name); + if (err) + goto error; + + err = init_irq(card); + if (!err) { + inf_cnt++; + pr_notice("Infineon %d cards installed\n", inf_cnt); + return 0; + } + mISDN_unregister_device(&card->ipac.isac.dch.dev); +error: + card->ipac.release(&card->ipac); +error_setup: + release_io(card); + write_lock_irqsave(&card_lock, flags); + list_del(&card->list); + write_unlock_irqrestore(&card_lock, flags); + return err; +} + +static const struct inf_cinfo inf_card_info[] = { + { + INF_DIVA20, + "Dialogic Diva 2.0", + "diva20", + AM_IND_IO, AM_NONE, 2, 0, + &diva_irq + }, + { + INF_DIVA20U, + "Dialogic Diva 2.0U", + "diva20U", + AM_IND_IO, AM_NONE, 2, 0, + &diva_irq + }, + { + INF_DIVA201, + "Dialogic Diva 2.01", + "diva201", + AM_MEMIO, AM_MEMIO, 0, 1, + &diva20x_irq + }, + { + INF_DIVA202, + "Dialogic Diva 2.02", + "diva202", + AM_MEMIO, AM_MEMIO, 0, 1, + &diva20x_irq + }, + { + INF_SPEEDWIN, + "Sedlbauer SpeedWin PCI", + "speedwin", + AM_IND_IO, AM_NONE, 0, 0, + &tiger_irq + }, + { + INF_SAPHIR3, + "HST Saphir 3", + "saphir", + AM_IND_IO, AM_NONE, 0, 0, + &tiger_irq + }, + { + INF_QS1000, + "Develo Microlink PCI", + "qs1000", + AM_IO, AM_IND_IO, 1, 3, + &elsa_irq + }, + { + INF_QS3000, + "Develo QuickStep 3000", + "qs3000", + AM_IO, AM_IND_IO, 1, 3, + &elsa_irq + }, + { + INF_NICCY, + "Sagem NICCY", + "niccy", + AM_IO, AM_IND_IO, 0, 1, + &niccy_irq + }, + { + INF_SCT_1, + "SciTel Quadro", + "p1_scitel", + AM_IO, AM_IND_IO, 1, 5, + &ipac_irq + }, + { + INF_SCT_2, + "SciTel Quadro", + "p2_scitel", + AM_NONE, AM_IND_IO, 0, 4, + &ipac_irq + }, + { + INF_SCT_3, + "SciTel Quadro", + "p3_scitel", + AM_NONE, AM_IND_IO, 0, 3, + &ipac_irq + }, + { + INF_SCT_4, + "SciTel Quadro", + "p4_scitel", + AM_NONE, AM_IND_IO, 0, 2, + &ipac_irq + }, + { + INF_GAZEL_R685, + "Gazel R685", + "gazel685", + AM_IO, AM_IO, 1, 2, + &gazel_irq + }, + { + INF_GAZEL_R753, + "Gazel R753", + "gazel753", + AM_IO, AM_IND_IO, 1, 2, + &ipac_irq + }, + { + INF_NONE, + } +}; + +static const struct inf_cinfo * +get_card_info(enum inf_types typ) +{ + const struct inf_cinfo *ci = inf_card_info; + + while (ci->typ != INF_NONE) { + if (ci->typ == typ) + return ci; + ci++; + } + return NULL; +} + +static int +inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + struct inf_hw *card; + + card = kzalloc(sizeof(struct inf_hw), GFP_KERNEL); + if (!card) { + pr_info("No memory for Infineon ISDN card\n"); + return err; + } + card->pdev = pdev; + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return err; + } + card->ci = get_card_info(ent->driver_data); + if (!card->ci) { + pr_info("mISDN: do not have information about adapter at %s\n", + pci_name(pdev)); + kfree(card); + pci_disable_device(pdev); + return -EINVAL; + } else + pr_notice("mISDN: found adapter %s at %s\n", + card->ci->full, pci_name(pdev)); + + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) { + pci_disable_device(pdev); + kfree(card); + pci_set_drvdata(pdev, NULL); + } else if (ent->driver_data == INF_SCT_1) { + int i; + struct inf_hw *sc; + + for (i = 1; i < 4; i++) { + sc = kzalloc(sizeof(struct inf_hw), GFP_KERNEL); + if (!sc) { + release_card(card); + pci_disable_device(pdev); + return -ENOMEM; + } + sc->irq = card->irq; + sc->pdev = card->pdev; + sc->ci = card->ci + i; + err = setup_instance(sc); + if (err) { + pci_disable_device(pdev); + kfree(sc); + release_card(card); + break; + } else + card->sc[i - 1] = sc; + } + } + return err; +} + +static void +inf_remove(struct pci_dev *pdev) +{ + struct inf_hw *card = pci_get_drvdata(pdev); + + if (card) + release_card(card); + else + pr_debug("%s: drvdata already removed\n", __func__); +} + +static struct pci_driver infineon_driver = { + .name = "ISDN Infineon pci", + .probe = inf_probe, + .remove = inf_remove, + .id_table = infineon_ids, +}; + +static int __init +infineon_init(void) +{ + int err; + + pr_notice("Infineon ISDN Driver Rev. %s\n", INFINEON_REV); + err = pci_register_driver(&infineon_driver); + return err; +} + +static void __exit +infineon_cleanup(void) +{ + pci_unregister_driver(&infineon_driver); +} + +module_init(infineon_init); +module_exit(infineon_cleanup); diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c new file mode 100644 index 000000000..71e635d6c --- /dev/null +++ b/drivers/isdn/hardware/mISDN/mISDNipac.c @@ -0,0 +1,1652 @@ +/* + * isac.c ISAC specific routines + * + * Author Karsten Keil <keil@isdn4linux.de> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/irqreturn.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/mISDNhw.h> +#include "ipac.h" + + +#define DBUSY_TIMER_VALUE 80 +#define ARCOFI_USE 1 + +#define ISAC_REV "2.0" + +MODULE_AUTHOR("Karsten Keil"); +MODULE_VERSION(ISAC_REV); +MODULE_LICENSE("GPL v2"); + +#define ReadISAC(is, o) (is->read_reg(is->dch.hw, o + is->off)) +#define WriteISAC(is, o, v) (is->write_reg(is->dch.hw, o + is->off, v)) +#define ReadHSCX(h, o) (h->ip->read_reg(h->ip->hw, h->off + o)) +#define WriteHSCX(h, o, v) (h->ip->write_reg(h->ip->hw, h->off + o, v)) +#define ReadIPAC(ip, o) (ip->read_reg(ip->hw, o)) +#define WriteIPAC(ip, o, v) (ip->write_reg(ip->hw, o, v)) + +static inline void +ph_command(struct isac_hw *isac, u8 command) +{ + pr_debug("%s: ph_command %x\n", isac->name, command); + if (isac->type & IPAC_TYPE_ISACX) + WriteISAC(isac, ISACX_CIX0, (command << 4) | 0xE); + else + WriteISAC(isac, ISAC_CIX0, (command << 2) | 3); +} + +static void +isac_ph_state_change(struct isac_hw *isac) +{ + switch (isac->state) { + case (ISAC_IND_RS): + case (ISAC_IND_EI): + ph_command(isac, ISAC_CMD_DUI); + } + schedule_event(&isac->dch, FLG_PHCHANGE); +} + +static void +isac_ph_state_bh(struct dchannel *dch) +{ + struct isac_hw *isac = container_of(dch, struct isac_hw, dch); + + switch (isac->state) { + case ISAC_IND_RS: + case ISAC_IND_EI: + dch->state = 0; + l1_event(dch->l1, HW_RESET_IND); + break; + case ISAC_IND_DID: + dch->state = 3; + l1_event(dch->l1, HW_DEACT_CNF); + break; + case ISAC_IND_DR: + case ISAC_IND_DR6: + dch->state = 3; + l1_event(dch->l1, HW_DEACT_IND); + break; + case ISAC_IND_PU: + dch->state = 4; + l1_event(dch->l1, HW_POWERUP_IND); + break; + case ISAC_IND_RSY: + if (dch->state <= 5) { + dch->state = 5; + l1_event(dch->l1, ANYSIGNAL); + } else { + dch->state = 8; + l1_event(dch->l1, LOSTFRAMING); + } + break; + case ISAC_IND_ARD: + dch->state = 6; + l1_event(dch->l1, INFO2); + break; + case ISAC_IND_AI8: + dch->state = 7; + l1_event(dch->l1, INFO4_P8); + break; + case ISAC_IND_AI10: + dch->state = 7; + l1_event(dch->l1, INFO4_P10); + break; + } + pr_debug("%s: TE newstate %x\n", isac->name, dch->state); +} + +static void +isac_empty_fifo(struct isac_hw *isac, int count) +{ + u8 *ptr; + + pr_debug("%s: %s %d\n", isac->name, __func__, count); + + if (!isac->dch.rx_skb) { + isac->dch.rx_skb = mI_alloc_skb(isac->dch.maxlen, GFP_ATOMIC); + if (!isac->dch.rx_skb) { + pr_info("%s: D receive out of memory\n", isac->name); + WriteISAC(isac, ISAC_CMDR, 0x80); + return; + } + } + if ((isac->dch.rx_skb->len + count) >= isac->dch.maxlen) { + pr_debug("%s: %s overrun %d\n", isac->name, __func__, + isac->dch.rx_skb->len + count); + WriteISAC(isac, ISAC_CMDR, 0x80); + return; + } + ptr = skb_put(isac->dch.rx_skb, count); + isac->read_fifo(isac->dch.hw, isac->off, ptr, count); + WriteISAC(isac, ISAC_CMDR, 0x80); + if (isac->dch.debug & DEBUG_HW_DFIFO) { + char pfx[MISDN_MAX_IDLEN + 16]; + + snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-recv %s %d ", + isac->name, count); + print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count); + } +} + +static void +isac_fill_fifo(struct isac_hw *isac) +{ + int count, more; + u8 *ptr; + + if (!isac->dch.tx_skb) + return; + count = isac->dch.tx_skb->len - isac->dch.tx_idx; + if (count <= 0) + return; + + more = 0; + if (count > 32) { + more = !0; + count = 32; + } + pr_debug("%s: %s %d\n", isac->name, __func__, count); + ptr = isac->dch.tx_skb->data + isac->dch.tx_idx; + isac->dch.tx_idx += count; + isac->write_fifo(isac->dch.hw, isac->off, ptr, count); + WriteISAC(isac, ISAC_CMDR, more ? 0x8 : 0xa); + if (test_and_set_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) { + pr_debug("%s: %s dbusytimer running\n", isac->name, __func__); + del_timer(&isac->dch.timer); + } + isac->dch.timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); + add_timer(&isac->dch.timer); + if (isac->dch.debug & DEBUG_HW_DFIFO) { + char pfx[MISDN_MAX_IDLEN + 16]; + + snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-send %s %d ", + isac->name, count); + print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count); + } +} + +static void +isac_rme_irq(struct isac_hw *isac) +{ + u8 val, count; + + val = ReadISAC(isac, ISAC_RSTA); + if ((val & 0x70) != 0x20) { + if (val & 0x40) { + pr_debug("%s: ISAC RDO\n", isac->name); +#ifdef ERROR_STATISTIC + isac->dch.err_rx++; +#endif + } + if (!(val & 0x20)) { + pr_debug("%s: ISAC CRC error\n", isac->name); +#ifdef ERROR_STATISTIC + isac->dch.err_crc++; +#endif + } + WriteISAC(isac, ISAC_CMDR, 0x80); + if (isac->dch.rx_skb) + dev_kfree_skb(isac->dch.rx_skb); + isac->dch.rx_skb = NULL; + } else { + count = ReadISAC(isac, ISAC_RBCL) & 0x1f; + if (count == 0) + count = 32; + isac_empty_fifo(isac, count); + recv_Dchannel(&isac->dch); + } +} + +static void +isac_xpr_irq(struct isac_hw *isac) +{ + if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) + del_timer(&isac->dch.timer); + if (isac->dch.tx_skb && isac->dch.tx_idx < isac->dch.tx_skb->len) { + isac_fill_fifo(isac); + } else { + if (isac->dch.tx_skb) + dev_kfree_skb(isac->dch.tx_skb); + if (get_next_dframe(&isac->dch)) + isac_fill_fifo(isac); + } +} + +static void +isac_retransmit(struct isac_hw *isac) +{ + if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) + del_timer(&isac->dch.timer); + if (test_bit(FLG_TX_BUSY, &isac->dch.Flags)) { + /* Restart frame */ + isac->dch.tx_idx = 0; + isac_fill_fifo(isac); + } else if (isac->dch.tx_skb) { /* should not happen */ + pr_info("%s: tx_skb exist but not busy\n", isac->name); + test_and_set_bit(FLG_TX_BUSY, &isac->dch.Flags); + isac->dch.tx_idx = 0; + isac_fill_fifo(isac); + } else { + pr_info("%s: ISAC XDU no TX_BUSY\n", isac->name); + if (get_next_dframe(&isac->dch)) + isac_fill_fifo(isac); + } +} + +static void +isac_mos_irq(struct isac_hw *isac) +{ + u8 val; + int ret; + + val = ReadISAC(isac, ISAC_MOSR); + pr_debug("%s: ISAC MOSR %02x\n", isac->name, val); +#if ARCOFI_USE + if (val & 0x08) { + if (!isac->mon_rx) { + isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC); + if (!isac->mon_rx) { + pr_info("%s: ISAC MON RX out of memory!\n", + isac->name); + isac->mocr &= 0xf0; + isac->mocr |= 0x0a; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + goto afterMONR0; + } else + isac->mon_rxp = 0; + } + if (isac->mon_rxp >= MAX_MON_FRAME) { + isac->mocr &= 0xf0; + isac->mocr |= 0x0a; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + isac->mon_rxp = 0; + pr_debug("%s: ISAC MON RX overflow!\n", isac->name); + goto afterMONR0; + } + isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR0); + pr_debug("%s: ISAC MOR0 %02x\n", isac->name, + isac->mon_rx[isac->mon_rxp - 1]); + if (isac->mon_rxp == 1) { + isac->mocr |= 0x04; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + } + } +afterMONR0: + if (val & 0x80) { + if (!isac->mon_rx) { + isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC); + if (!isac->mon_rx) { + pr_info("%s: ISAC MON RX out of memory!\n", + isac->name); + isac->mocr &= 0x0f; + isac->mocr |= 0xa0; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + goto afterMONR1; + } else + isac->mon_rxp = 0; + } + if (isac->mon_rxp >= MAX_MON_FRAME) { + isac->mocr &= 0x0f; + isac->mocr |= 0xa0; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + isac->mon_rxp = 0; + pr_debug("%s: ISAC MON RX overflow!\n", isac->name); + goto afterMONR1; + } + isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR1); + pr_debug("%s: ISAC MOR1 %02x\n", isac->name, + isac->mon_rx[isac->mon_rxp - 1]); + isac->mocr |= 0x40; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + } +afterMONR1: + if (val & 0x04) { + isac->mocr &= 0xf0; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + isac->mocr |= 0x0a; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + if (isac->monitor) { + ret = isac->monitor(isac->dch.hw, MONITOR_RX_0, + isac->mon_rx, isac->mon_rxp); + if (ret) + kfree(isac->mon_rx); + } else { + pr_info("%s: MONITOR 0 received %d but no user\n", + isac->name, isac->mon_rxp); + kfree(isac->mon_rx); + } + isac->mon_rx = NULL; + isac->mon_rxp = 0; + } + if (val & 0x40) { + isac->mocr &= 0x0f; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + isac->mocr |= 0xa0; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + if (isac->monitor) { + ret = isac->monitor(isac->dch.hw, MONITOR_RX_1, + isac->mon_rx, isac->mon_rxp); + if (ret) + kfree(isac->mon_rx); + } else { + pr_info("%s: MONITOR 1 received %d but no user\n", + isac->name, isac->mon_rxp); + kfree(isac->mon_rx); + } + isac->mon_rx = NULL; + isac->mon_rxp = 0; + } + if (val & 0x02) { + if ((!isac->mon_tx) || (isac->mon_txc && + (isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) { + isac->mocr &= 0xf0; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + isac->mocr |= 0x0a; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { + if (isac->monitor) + isac->monitor(isac->dch.hw, + MONITOR_TX_0, NULL, 0); + } + kfree(isac->mon_tx); + isac->mon_tx = NULL; + isac->mon_txc = 0; + isac->mon_txp = 0; + goto AfterMOX0; + } + if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { + if (isac->monitor) + isac->monitor(isac->dch.hw, + MONITOR_TX_0, NULL, 0); + kfree(isac->mon_tx); + isac->mon_tx = NULL; + isac->mon_txc = 0; + isac->mon_txp = 0; + goto AfterMOX0; + } + WriteISAC(isac, ISAC_MOX0, isac->mon_tx[isac->mon_txp++]); + pr_debug("%s: ISAC %02x -> MOX0\n", isac->name, + isac->mon_tx[isac->mon_txp - 1]); + } +AfterMOX0: + if (val & 0x20) { + if ((!isac->mon_tx) || (isac->mon_txc && + (isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) { + isac->mocr &= 0x0f; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + isac->mocr |= 0xa0; + WriteISAC(isac, ISAC_MOCR, isac->mocr); + if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { + if (isac->monitor) + isac->monitor(isac->dch.hw, + MONITOR_TX_1, NULL, 0); + } + kfree(isac->mon_tx); + isac->mon_tx = NULL; + isac->mon_txc = 0; + isac->mon_txp = 0; + goto AfterMOX1; + } + if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { + if (isac->monitor) + isac->monitor(isac->dch.hw, + MONITOR_TX_1, NULL, 0); + kfree(isac->mon_tx); + isac->mon_tx = NULL; + isac->mon_txc = 0; + isac->mon_txp = 0; + goto AfterMOX1; + } + WriteISAC(isac, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]); + pr_debug("%s: ISAC %02x -> MOX1\n", isac->name, + isac->mon_tx[isac->mon_txp - 1]); + } +AfterMOX1: + val = 0; /* dummy to avoid warning */ +#endif +} + +static void +isac_cisq_irq(struct isac_hw *isac) { + u8 val; + + val = ReadISAC(isac, ISAC_CIR0); + pr_debug("%s: ISAC CIR0 %02X\n", isac->name, val); + if (val & 2) { + pr_debug("%s: ph_state change %x->%x\n", isac->name, + isac->state, (val >> 2) & 0xf); + isac->state = (val >> 2) & 0xf; + isac_ph_state_change(isac); + } + if (val & 1) { + val = ReadISAC(isac, ISAC_CIR1); + pr_debug("%s: ISAC CIR1 %02X\n", isac->name, val); + } +} + +static void +isacsx_cic_irq(struct isac_hw *isac) +{ + u8 val; + + val = ReadISAC(isac, ISACX_CIR0); + pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val); + if (val & ISACX_CIR0_CIC0) { + pr_debug("%s: ph_state change %x->%x\n", isac->name, + isac->state, val >> 4); + isac->state = val >> 4; + isac_ph_state_change(isac); + } +} + +static void +isacsx_rme_irq(struct isac_hw *isac) +{ + int count; + u8 val; + + val = ReadISAC(isac, ISACX_RSTAD); + if ((val & (ISACX_RSTAD_VFR | + ISACX_RSTAD_RDO | + ISACX_RSTAD_CRC | + ISACX_RSTAD_RAB)) + != (ISACX_RSTAD_VFR | ISACX_RSTAD_CRC)) { + pr_debug("%s: RSTAD %#x, dropped\n", isac->name, val); +#ifdef ERROR_STATISTIC + if (val & ISACX_RSTAD_CRC) + isac->dch.err_rx++; + else + isac->dch.err_crc++; +#endif + WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC); + if (isac->dch.rx_skb) + dev_kfree_skb(isac->dch.rx_skb); + isac->dch.rx_skb = NULL; + } else { + count = ReadISAC(isac, ISACX_RBCLD) & 0x1f; + if (count == 0) + count = 32; + isac_empty_fifo(isac, count); + if (isac->dch.rx_skb) { + skb_trim(isac->dch.rx_skb, isac->dch.rx_skb->len - 1); + pr_debug("%s: dchannel received %d\n", isac->name, + isac->dch.rx_skb->len); + recv_Dchannel(&isac->dch); + } + } +} + +irqreturn_t +mISDNisac_irq(struct isac_hw *isac, u8 val) +{ + if (unlikely(!val)) + return IRQ_NONE; + pr_debug("%s: ISAC interrupt %02x\n", isac->name, val); + if (isac->type & IPAC_TYPE_ISACX) { + if (val & ISACX__CIC) + isacsx_cic_irq(isac); + if (val & ISACX__ICD) { + val = ReadISAC(isac, ISACX_ISTAD); + pr_debug("%s: ISTAD %02x\n", isac->name, val); + if (val & ISACX_D_XDU) { + pr_debug("%s: ISAC XDU\n", isac->name); +#ifdef ERROR_STATISTIC + isac->dch.err_tx++; +#endif + isac_retransmit(isac); + } + if (val & ISACX_D_XMR) { + pr_debug("%s: ISAC XMR\n", isac->name); +#ifdef ERROR_STATISTIC + isac->dch.err_tx++; +#endif + isac_retransmit(isac); + } + if (val & ISACX_D_XPR) + isac_xpr_irq(isac); + if (val & ISACX_D_RFO) { + pr_debug("%s: ISAC RFO\n", isac->name); + WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC); + } + if (val & ISACX_D_RME) + isacsx_rme_irq(isac); + if (val & ISACX_D_RPF) + isac_empty_fifo(isac, 0x20); + } + } else { + if (val & 0x80) /* RME */ + isac_rme_irq(isac); + if (val & 0x40) /* RPF */ + isac_empty_fifo(isac, 32); + if (val & 0x10) /* XPR */ + isac_xpr_irq(isac); + if (val & 0x04) /* CISQ */ + isac_cisq_irq(isac); + if (val & 0x20) /* RSC - never */ + pr_debug("%s: ISAC RSC interrupt\n", isac->name); + if (val & 0x02) /* SIN - never */ + pr_debug("%s: ISAC SIN interrupt\n", isac->name); + if (val & 0x01) { /* EXI */ + val = ReadISAC(isac, ISAC_EXIR); + pr_debug("%s: ISAC EXIR %02x\n", isac->name, val); + if (val & 0x80) /* XMR */ + pr_debug("%s: ISAC XMR\n", isac->name); + if (val & 0x40) { /* XDU */ + pr_debug("%s: ISAC XDU\n", isac->name); +#ifdef ERROR_STATISTIC + isac->dch.err_tx++; +#endif + isac_retransmit(isac); + } + if (val & 0x04) /* MOS */ + isac_mos_irq(isac); + } + } + return IRQ_HANDLED; +} +EXPORT_SYMBOL(mISDNisac_irq); + +static int +isac_l1hw(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct isac_hw *isac = container_of(dch, struct isac_hw, dch); + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + u32 id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(isac->hwlock, flags); + ret = dchannel_senddata(dch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + isac_fill_fifo(isac); + ret = 0; + spin_unlock_irqrestore(isac->hwlock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(isac->hwlock, flags); + return ret; + case PH_ACTIVATE_REQ: + ret = l1_event(dch->l1, hh->prim); + break; + case PH_DEACTIVATE_REQ: + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + ret = l1_event(dch->l1, hh->prim); + break; + } + + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +isac_ctrl(struct isac_hw *isac, u32 cmd, unsigned long para) +{ + u8 tl = 0; + unsigned long flags; + int ret = 0; + + switch (cmd) { + case HW_TESTLOOP: + spin_lock_irqsave(isac->hwlock, flags); + if (!(isac->type & IPAC_TYPE_ISACX)) { + /* TODO: implement for IPAC_TYPE_ISACX */ + if (para & 1) /* B1 */ + tl |= 0x0c; + else if (para & 2) /* B2 */ + tl |= 0x3; + /* we only support IOM2 mode */ + WriteISAC(isac, ISAC_SPCR, tl); + if (tl) + WriteISAC(isac, ISAC_ADF1, 0x8); + else + WriteISAC(isac, ISAC_ADF1, 0x0); + } + spin_unlock_irqrestore(isac->hwlock, flags); + break; + case HW_TIMER3_VALUE: + ret = l1_event(isac->dch.l1, HW_TIMER3_VALUE | (para & 0xff)); + break; + default: + pr_debug("%s: %s unknown command %x %lx\n", isac->name, + __func__, cmd, para); + ret = -1; + } + return ret; +} + +static int +isac_l1cmd(struct dchannel *dch, u32 cmd) +{ + struct isac_hw *isac = container_of(dch, struct isac_hw, dch); + u_long flags; + + pr_debug("%s: cmd(%x) state(%02x)\n", isac->name, cmd, isac->state); + switch (cmd) { + case INFO3_P8: + spin_lock_irqsave(isac->hwlock, flags); + ph_command(isac, ISAC_CMD_AR8); + spin_unlock_irqrestore(isac->hwlock, flags); + break; + case INFO3_P10: + spin_lock_irqsave(isac->hwlock, flags); + ph_command(isac, ISAC_CMD_AR10); + spin_unlock_irqrestore(isac->hwlock, flags); + break; + case HW_RESET_REQ: + spin_lock_irqsave(isac->hwlock, flags); + if ((isac->state == ISAC_IND_EI) || + (isac->state == ISAC_IND_DR) || + (isac->state == ISAC_IND_DR6) || + (isac->state == ISAC_IND_RS)) + ph_command(isac, ISAC_CMD_TIM); + else + ph_command(isac, ISAC_CMD_RS); + spin_unlock_irqrestore(isac->hwlock, flags); + break; + case HW_DEACT_REQ: + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); + break; + case HW_POWERUP_REQ: + spin_lock_irqsave(isac->hwlock, flags); + ph_command(isac, ISAC_CMD_TIM); + spin_unlock_irqrestore(isac->hwlock, flags); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + pr_debug("%s: %s unknown command %x\n", isac->name, + __func__, cmd); + return -1; + } + return 0; +} + +static void +isac_release(struct isac_hw *isac) +{ + if (isac->type & IPAC_TYPE_ISACX) + WriteISAC(isac, ISACX_MASK, 0xff); + else if (isac->type != 0) + WriteISAC(isac, ISAC_MASK, 0xff); + if (isac->dch.timer.function != NULL) { + del_timer(&isac->dch.timer); + isac->dch.timer.function = NULL; + } + kfree(isac->mon_rx); + isac->mon_rx = NULL; + kfree(isac->mon_tx); + isac->mon_tx = NULL; + if (isac->dch.l1) + l1_event(isac->dch.l1, CLOSE_CHANNEL); + mISDN_freedchannel(&isac->dch); +} + +static void +dbusy_timer_handler(struct timer_list *t) +{ + struct isac_hw *isac = from_timer(isac, t, dch.timer); + int rbch, star; + u_long flags; + + if (test_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) { + spin_lock_irqsave(isac->hwlock, flags); + rbch = ReadISAC(isac, ISAC_RBCH); + star = ReadISAC(isac, ISAC_STAR); + pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n", + isac->name, rbch, star); + if (rbch & ISAC_RBCH_XAC) /* D-Channel Busy */ + test_and_set_bit(FLG_L1_BUSY, &isac->dch.Flags); + else { + /* discard frame; reset transceiver */ + test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags); + if (isac->dch.tx_idx) + isac->dch.tx_idx = 0; + else + pr_info("%s: ISAC D-Channel Busy no tx_idx\n", + isac->name); + /* Transmitter reset */ + WriteISAC(isac, ISAC_CMDR, 0x01); + } + spin_unlock_irqrestore(isac->hwlock, flags); + } +} + +static int +open_dchannel_caller(struct isac_hw *isac, struct channel_req *rq, void *caller) +{ + pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__, + isac->dch.dev.id, caller); + if (rq->protocol != ISDN_P_TE_S0) + return -EINVAL; + if (rq->adr.channel == 1) + /* E-Channel not supported */ + return -EINVAL; + rq->ch = &isac->dch.dev.D; + rq->ch->protocol = rq->protocol; + if (isac->dch.state == 7) + _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + return 0; +} + +static int +open_dchannel(struct isac_hw *isac, struct channel_req *rq) +{ + return open_dchannel_caller(isac, rq, __builtin_return_address(0)); +} + +static const char *ISACVer[] = +{"2086/2186 V1.1", "2085 B1", "2085 B2", + "2085 V2.3"}; + +static int +isac_init(struct isac_hw *isac) +{ + u8 val; + int err = 0; + + if (!isac->dch.l1) { + err = create_l1(&isac->dch, isac_l1cmd); + if (err) + return err; + } + isac->mon_tx = NULL; + isac->mon_rx = NULL; + timer_setup(&isac->dch.timer, dbusy_timer_handler, 0); + isac->mocr = 0xaa; + if (isac->type & IPAC_TYPE_ISACX) { + /* Disable all IRQ */ + WriteISAC(isac, ISACX_MASK, 0xff); + val = ReadISAC(isac, ISACX_STARD); + pr_debug("%s: ISACX STARD %x\n", isac->name, val); + val = ReadISAC(isac, ISACX_ISTAD); + pr_debug("%s: ISACX ISTAD %x\n", isac->name, val); + val = ReadISAC(isac, ISACX_ISTA); + pr_debug("%s: ISACX ISTA %x\n", isac->name, val); + /* clear LDD */ + WriteISAC(isac, ISACX_TR_CONF0, 0x00); + /* enable transmitter */ + WriteISAC(isac, ISACX_TR_CONF2, 0x00); + /* transparent mode 0, RAC, stop/go */ + WriteISAC(isac, ISACX_MODED, 0xc9); + /* all HDLC IRQ unmasked */ + val = ReadISAC(isac, ISACX_ID); + if (isac->dch.debug & DEBUG_HW) + pr_notice("%s: ISACX Design ID %x\n", + isac->name, val & 0x3f); + val = ReadISAC(isac, ISACX_CIR0); + pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val); + isac->state = val >> 4; + isac_ph_state_change(isac); + ph_command(isac, ISAC_CMD_RS); + WriteISAC(isac, ISACX_MASK, IPACX__ON); + WriteISAC(isac, ISACX_MASKD, 0x00); + } else { /* old isac */ + WriteISAC(isac, ISAC_MASK, 0xff); + val = ReadISAC(isac, ISAC_STAR); + pr_debug("%s: ISAC STAR %x\n", isac->name, val); + val = ReadISAC(isac, ISAC_MODE); + pr_debug("%s: ISAC MODE %x\n", isac->name, val); + val = ReadISAC(isac, ISAC_ADF2); + pr_debug("%s: ISAC ADF2 %x\n", isac->name, val); + val = ReadISAC(isac, ISAC_ISTA); + pr_debug("%s: ISAC ISTA %x\n", isac->name, val); + if (val & 0x01) { + val = ReadISAC(isac, ISAC_EXIR); + pr_debug("%s: ISAC EXIR %x\n", isac->name, val); + } + val = ReadISAC(isac, ISAC_RBCH); + if (isac->dch.debug & DEBUG_HW) + pr_notice("%s: ISAC version (%x): %s\n", isac->name, + val, ISACVer[(val >> 5) & 3]); + isac->type |= ((val >> 5) & 3); + if (!isac->adf2) + isac->adf2 = 0x80; + if (!(isac->adf2 & 0x80)) { /* only IOM 2 Mode */ + pr_info("%s: only support IOM2 mode but adf2=%02x\n", + isac->name, isac->adf2); + isac_release(isac); + return -EINVAL; + } + WriteISAC(isac, ISAC_ADF2, isac->adf2); + WriteISAC(isac, ISAC_SQXR, 0x2f); + WriteISAC(isac, ISAC_SPCR, 0x00); + WriteISAC(isac, ISAC_STCR, 0x70); + WriteISAC(isac, ISAC_MODE, 0xc9); + WriteISAC(isac, ISAC_TIMR, 0x00); + WriteISAC(isac, ISAC_ADF1, 0x00); + val = ReadISAC(isac, ISAC_CIR0); + pr_debug("%s: ISAC CIR0 %x\n", isac->name, val); + isac->state = (val >> 2) & 0xf; + isac_ph_state_change(isac); + ph_command(isac, ISAC_CMD_RS); + WriteISAC(isac, ISAC_MASK, 0); + } + return err; +} + +int +mISDNisac_init(struct isac_hw *isac, void *hw) +{ + mISDN_initdchannel(&isac->dch, MAX_DFRAME_LEN_L1, isac_ph_state_bh); + isac->dch.hw = hw; + isac->dch.dev.D.send = isac_l1hw; + isac->init = isac_init; + isac->release = isac_release; + isac->ctrl = isac_ctrl; + isac->open = open_dchannel; + isac->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0); + isac->dch.dev.nrbchan = 2; + return 0; +} +EXPORT_SYMBOL(mISDNisac_init); + +static void +waitforCEC(struct hscx_hw *hx) +{ + u8 starb, to = 50; + + while (to) { + starb = ReadHSCX(hx, IPAC_STARB); + if (!(starb & 0x04)) + break; + udelay(1); + to--; + } + if (to < 50) + pr_debug("%s: B%1d CEC %d us\n", hx->ip->name, hx->bch.nr, + 50 - to); + if (!to) + pr_info("%s: B%1d CEC timeout\n", hx->ip->name, hx->bch.nr); +} + + +static void +waitforXFW(struct hscx_hw *hx) +{ + u8 starb, to = 50; + + while (to) { + starb = ReadHSCX(hx, IPAC_STARB); + if ((starb & 0x44) == 0x40) + break; + udelay(1); + to--; + } + if (to < 50) + pr_debug("%s: B%1d XFW %d us\n", hx->ip->name, hx->bch.nr, + 50 - to); + if (!to) + pr_info("%s: B%1d XFW timeout\n", hx->ip->name, hx->bch.nr); +} + +static void +hscx_cmdr(struct hscx_hw *hx, u8 cmd) +{ + if (hx->ip->type & IPAC_TYPE_IPACX) + WriteHSCX(hx, IPACX_CMDRB, cmd); + else { + waitforCEC(hx); + WriteHSCX(hx, IPAC_CMDRB, cmd); + } +} + +static void +hscx_empty_fifo(struct hscx_hw *hscx, u8 count) +{ + u8 *p; + int maxlen; + + pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count); + if (test_bit(FLG_RX_OFF, &hscx->bch.Flags)) { + hscx->bch.dropcnt += count; + hscx_cmdr(hscx, 0x80); /* RMC */ + return; + } + maxlen = bchannel_get_rxbuf(&hscx->bch, count); + if (maxlen < 0) { + hscx_cmdr(hscx, 0x80); /* RMC */ + if (hscx->bch.rx_skb) + skb_trim(hscx->bch.rx_skb, 0); + pr_warning("%s.B%d: No bufferspace for %d bytes\n", + hscx->ip->name, hscx->bch.nr, count); + return; + } + p = skb_put(hscx->bch.rx_skb, count); + + if (hscx->ip->type & IPAC_TYPE_IPACX) + hscx->ip->read_fifo(hscx->ip->hw, + hscx->off + IPACX_RFIFOB, p, count); + else + hscx->ip->read_fifo(hscx->ip->hw, + hscx->off, p, count); + + hscx_cmdr(hscx, 0x80); /* RMC */ + + if (hscx->bch.debug & DEBUG_HW_BFIFO) { + snprintf(hscx->log, 64, "B%1d-recv %s %d ", + hscx->bch.nr, hscx->ip->name, count); + print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count); + } +} + +static void +hscx_fill_fifo(struct hscx_hw *hscx) +{ + int count, more; + u8 *p; + + if (!hscx->bch.tx_skb) { + if (!test_bit(FLG_TX_EMPTY, &hscx->bch.Flags)) + return; + count = hscx->fifo_size; + more = 1; + p = hscx->log; + memset(p, hscx->bch.fill[0], count); + } else { + count = hscx->bch.tx_skb->len - hscx->bch.tx_idx; + if (count <= 0) + return; + p = hscx->bch.tx_skb->data + hscx->bch.tx_idx; + + more = test_bit(FLG_TRANSPARENT, &hscx->bch.Flags) ? 1 : 0; + if (count > hscx->fifo_size) { + count = hscx->fifo_size; + more = 1; + } + pr_debug("%s: B%1d %d/%d/%d\n", hscx->ip->name, hscx->bch.nr, + count, hscx->bch.tx_idx, hscx->bch.tx_skb->len); + hscx->bch.tx_idx += count; + } + if (hscx->ip->type & IPAC_TYPE_IPACX) + hscx->ip->write_fifo(hscx->ip->hw, + hscx->off + IPACX_XFIFOB, p, count); + else { + waitforXFW(hscx); + hscx->ip->write_fifo(hscx->ip->hw, + hscx->off, p, count); + } + hscx_cmdr(hscx, more ? 0x08 : 0x0a); + + if (hscx->bch.tx_skb && (hscx->bch.debug & DEBUG_HW_BFIFO)) { + snprintf(hscx->log, 64, "B%1d-send %s %d ", + hscx->bch.nr, hscx->ip->name, count); + print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count); + } +} + +static void +hscx_xpr(struct hscx_hw *hx) +{ + if (hx->bch.tx_skb && hx->bch.tx_idx < hx->bch.tx_skb->len) { + hscx_fill_fifo(hx); + } else { + if (hx->bch.tx_skb) + dev_kfree_skb(hx->bch.tx_skb); + if (get_next_bframe(&hx->bch)) { + hscx_fill_fifo(hx); + test_and_clear_bit(FLG_TX_EMPTY, &hx->bch.Flags); + } else if (test_bit(FLG_TX_EMPTY, &hx->bch.Flags)) { + hscx_fill_fifo(hx); + } + } +} + +static void +ipac_rme(struct hscx_hw *hx) +{ + int count; + u8 rstab; + + if (hx->ip->type & IPAC_TYPE_IPACX) + rstab = ReadHSCX(hx, IPACX_RSTAB); + else + rstab = ReadHSCX(hx, IPAC_RSTAB); + pr_debug("%s: B%1d RSTAB %02x\n", hx->ip->name, hx->bch.nr, rstab); + if ((rstab & 0xf0) != 0xa0) { + /* !(VFR && !RDO && CRC && !RAB) */ + if (!(rstab & 0x80)) { + if (hx->bch.debug & DEBUG_HW_BCHANNEL) + pr_notice("%s: B%1d invalid frame\n", + hx->ip->name, hx->bch.nr); + } + if (rstab & 0x40) { + if (hx->bch.debug & DEBUG_HW_BCHANNEL) + pr_notice("%s: B%1d RDO proto=%x\n", + hx->ip->name, hx->bch.nr, + hx->bch.state); + } + if (!(rstab & 0x20)) { + if (hx->bch.debug & DEBUG_HW_BCHANNEL) + pr_notice("%s: B%1d CRC error\n", + hx->ip->name, hx->bch.nr); + } + hscx_cmdr(hx, 0x80); /* Do RMC */ + return; + } + if (hx->ip->type & IPAC_TYPE_IPACX) + count = ReadHSCX(hx, IPACX_RBCLB); + else + count = ReadHSCX(hx, IPAC_RBCLB); + count &= (hx->fifo_size - 1); + if (count == 0) + count = hx->fifo_size; + hscx_empty_fifo(hx, count); + if (!hx->bch.rx_skb) + return; + if (hx->bch.rx_skb->len < 2) { + pr_debug("%s: B%1d frame to short %d\n", + hx->ip->name, hx->bch.nr, hx->bch.rx_skb->len); + skb_trim(hx->bch.rx_skb, 0); + } else { + skb_trim(hx->bch.rx_skb, hx->bch.rx_skb->len - 1); + recv_Bchannel(&hx->bch, 0, false); + } +} + +static void +ipac_irq(struct hscx_hw *hx, u8 ista) +{ + u8 istab, m, exirb = 0; + + if (hx->ip->type & IPAC_TYPE_IPACX) + istab = ReadHSCX(hx, IPACX_ISTAB); + else if (hx->ip->type & IPAC_TYPE_IPAC) { + istab = ReadHSCX(hx, IPAC_ISTAB); + m = (hx->bch.nr & 1) ? IPAC__EXA : IPAC__EXB; + if (m & ista) { + exirb = ReadHSCX(hx, IPAC_EXIRB); + pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name, + hx->bch.nr, exirb); + } + } else if (hx->bch.nr & 2) { /* HSCX B */ + if (ista & (HSCX__EXA | HSCX__ICA)) + ipac_irq(&hx->ip->hscx[0], ista); + if (ista & HSCX__EXB) { + exirb = ReadHSCX(hx, IPAC_EXIRB); + pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name, + hx->bch.nr, exirb); + } + istab = ista & 0xF8; + } else { /* HSCX A */ + istab = ReadHSCX(hx, IPAC_ISTAB); + if (ista & HSCX__EXA) { + exirb = ReadHSCX(hx, IPAC_EXIRB); + pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name, + hx->bch.nr, exirb); + } + istab = istab & 0xF8; + } + if (exirb & IPAC_B_XDU) + istab |= IPACX_B_XDU; + if (exirb & IPAC_B_RFO) + istab |= IPACX_B_RFO; + pr_debug("%s: B%1d ISTAB %02x\n", hx->ip->name, hx->bch.nr, istab); + + if (!test_bit(FLG_ACTIVE, &hx->bch.Flags)) + return; + + if (istab & IPACX_B_RME) + ipac_rme(hx); + + if (istab & IPACX_B_RPF) { + hscx_empty_fifo(hx, hx->fifo_size); + if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) + recv_Bchannel(&hx->bch, 0, false); + } + + if (istab & IPACX_B_RFO) { + pr_debug("%s: B%1d RFO error\n", hx->ip->name, hx->bch.nr); + hscx_cmdr(hx, 0x40); /* RRES */ + } + + if (istab & IPACX_B_XPR) + hscx_xpr(hx); + + if (istab & IPACX_B_XDU) { + if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) { + if (test_bit(FLG_FILLEMPTY, &hx->bch.Flags)) + test_and_set_bit(FLG_TX_EMPTY, &hx->bch.Flags); + hscx_xpr(hx); + return; + } + pr_debug("%s: B%1d XDU error at len %d\n", hx->ip->name, + hx->bch.nr, hx->bch.tx_idx); + hx->bch.tx_idx = 0; + hscx_cmdr(hx, 0x01); /* XRES */ + } +} + +irqreturn_t +mISDNipac_irq(struct ipac_hw *ipac, int maxloop) +{ + int cnt = maxloop + 1; + u8 ista, istad; + struct isac_hw *isac = &ipac->isac; + + if (ipac->type & IPAC_TYPE_IPACX) { + ista = ReadIPAC(ipac, ISACX_ISTA); + while (ista && --cnt) { + pr_debug("%s: ISTA %02x\n", ipac->name, ista); + if (ista & IPACX__ICA) + ipac_irq(&ipac->hscx[0], ista); + if (ista & IPACX__ICB) + ipac_irq(&ipac->hscx[1], ista); + if (ista & (ISACX__ICD | ISACX__CIC)) + mISDNisac_irq(&ipac->isac, ista); + ista = ReadIPAC(ipac, ISACX_ISTA); + } + } else if (ipac->type & IPAC_TYPE_IPAC) { + ista = ReadIPAC(ipac, IPAC_ISTA); + while (ista && --cnt) { + pr_debug("%s: ISTA %02x\n", ipac->name, ista); + if (ista & (IPAC__ICD | IPAC__EXD)) { + istad = ReadISAC(isac, ISAC_ISTA); + pr_debug("%s: ISTAD %02x\n", ipac->name, istad); + if (istad & IPAC_D_TIN2) + pr_debug("%s TIN2 irq\n", ipac->name); + if (ista & IPAC__EXD) + istad |= 1; /* ISAC EXI */ + mISDNisac_irq(isac, istad); + } + if (ista & (IPAC__ICA | IPAC__EXA)) + ipac_irq(&ipac->hscx[0], ista); + if (ista & (IPAC__ICB | IPAC__EXB)) + ipac_irq(&ipac->hscx[1], ista); + ista = ReadIPAC(ipac, IPAC_ISTA); + } + } else if (ipac->type & IPAC_TYPE_HSCX) { + while (--cnt) { + ista = ReadIPAC(ipac, IPAC_ISTAB + ipac->hscx[1].off); + pr_debug("%s: B2 ISTA %02x\n", ipac->name, ista); + if (ista) + ipac_irq(&ipac->hscx[1], ista); + istad = ReadISAC(isac, ISAC_ISTA); + pr_debug("%s: ISTAD %02x\n", ipac->name, istad); + if (istad) + mISDNisac_irq(isac, istad); + if (0 == (ista | istad)) + break; + } + } + if (cnt > maxloop) /* only for ISAC/HSCX without PCI IRQ test */ + return IRQ_NONE; + if (cnt < maxloop) + pr_debug("%s: %d irqloops cpu%d\n", ipac->name, + maxloop - cnt, smp_processor_id()); + if (maxloop && !cnt) + pr_notice("%s: %d IRQ LOOP cpu%d\n", ipac->name, + maxloop, smp_processor_id()); + return IRQ_HANDLED; +} +EXPORT_SYMBOL(mISDNipac_irq); + +static int +hscx_mode(struct hscx_hw *hscx, u32 bprotocol) +{ + pr_debug("%s: HSCX %c protocol %x-->%x ch %d\n", hscx->ip->name, + '@' + hscx->bch.nr, hscx->bch.state, bprotocol, hscx->bch.nr); + if (hscx->ip->type & IPAC_TYPE_IPACX) { + if (hscx->bch.nr & 1) { /* B1 and ICA */ + WriteIPAC(hscx->ip, ISACX_BCHA_TSDP_BC1, 0x80); + WriteIPAC(hscx->ip, ISACX_BCHA_CR, 0x88); + } else { /* B2 and ICB */ + WriteIPAC(hscx->ip, ISACX_BCHB_TSDP_BC1, 0x81); + WriteIPAC(hscx->ip, ISACX_BCHB_CR, 0x88); + } + switch (bprotocol) { + case ISDN_P_NONE: /* init */ + WriteHSCX(hscx, IPACX_MODEB, 0xC0); /* rec off */ + WriteHSCX(hscx, IPACX_EXMB, 0x30); /* std adj. */ + WriteHSCX(hscx, IPACX_MASKB, 0xFF); /* ints off */ + hscx_cmdr(hscx, 0x41); + test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags); + test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags); + break; + case ISDN_P_B_RAW: + WriteHSCX(hscx, IPACX_MODEB, 0x88); /* ex trans */ + WriteHSCX(hscx, IPACX_EXMB, 0x00); /* trans */ + hscx_cmdr(hscx, 0x41); + WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON); + test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags); + break; + case ISDN_P_B_HDLC: + WriteHSCX(hscx, IPACX_MODEB, 0xC0); /* trans */ + WriteHSCX(hscx, IPACX_EXMB, 0x00); /* hdlc,crc */ + hscx_cmdr(hscx, 0x41); + WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON); + test_and_set_bit(FLG_HDLC, &hscx->bch.Flags); + break; + default: + pr_info("%s: protocol not known %x\n", hscx->ip->name, + bprotocol); + return -ENOPROTOOPT; + } + } else if (hscx->ip->type & IPAC_TYPE_IPAC) { /* IPAC */ + WriteHSCX(hscx, IPAC_CCR1, 0x82); + WriteHSCX(hscx, IPAC_CCR2, 0x30); + WriteHSCX(hscx, IPAC_XCCR, 0x07); + WriteHSCX(hscx, IPAC_RCCR, 0x07); + WriteHSCX(hscx, IPAC_TSAX, hscx->slot); + WriteHSCX(hscx, IPAC_TSAR, hscx->slot); + switch (bprotocol) { + case ISDN_P_NONE: + WriteHSCX(hscx, IPAC_TSAX, 0x1F); + WriteHSCX(hscx, IPAC_TSAR, 0x1F); + WriteHSCX(hscx, IPAC_MODEB, 0x84); + WriteHSCX(hscx, IPAC_CCR1, 0x82); + WriteHSCX(hscx, IPAC_MASKB, 0xFF); /* ints off */ + test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags); + test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags); + break; + case ISDN_P_B_RAW: + WriteHSCX(hscx, IPAC_MODEB, 0xe4); /* ex trans */ + WriteHSCX(hscx, IPAC_CCR1, 0x82); + hscx_cmdr(hscx, 0x41); + WriteHSCX(hscx, IPAC_MASKB, 0); + test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags); + break; + case ISDN_P_B_HDLC: + WriteHSCX(hscx, IPAC_MODEB, 0x8c); + WriteHSCX(hscx, IPAC_CCR1, 0x8a); + hscx_cmdr(hscx, 0x41); + WriteHSCX(hscx, IPAC_MASKB, 0); + test_and_set_bit(FLG_HDLC, &hscx->bch.Flags); + break; + default: + pr_info("%s: protocol not known %x\n", hscx->ip->name, + bprotocol); + return -ENOPROTOOPT; + } + } else if (hscx->ip->type & IPAC_TYPE_HSCX) { /* HSCX */ + WriteHSCX(hscx, IPAC_CCR1, 0x85); + WriteHSCX(hscx, IPAC_CCR2, 0x30); + WriteHSCX(hscx, IPAC_XCCR, 0x07); + WriteHSCX(hscx, IPAC_RCCR, 0x07); + WriteHSCX(hscx, IPAC_TSAX, hscx->slot); + WriteHSCX(hscx, IPAC_TSAR, hscx->slot); + switch (bprotocol) { + case ISDN_P_NONE: + WriteHSCX(hscx, IPAC_TSAX, 0x1F); + WriteHSCX(hscx, IPAC_TSAR, 0x1F); + WriteHSCX(hscx, IPAC_MODEB, 0x84); + WriteHSCX(hscx, IPAC_CCR1, 0x85); + WriteHSCX(hscx, IPAC_MASKB, 0xFF); /* ints off */ + test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags); + test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags); + break; + case ISDN_P_B_RAW: + WriteHSCX(hscx, IPAC_MODEB, 0xe4); /* ex trans */ + WriteHSCX(hscx, IPAC_CCR1, 0x85); + hscx_cmdr(hscx, 0x41); + WriteHSCX(hscx, IPAC_MASKB, 0); + test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags); + break; + case ISDN_P_B_HDLC: + WriteHSCX(hscx, IPAC_MODEB, 0x8c); + WriteHSCX(hscx, IPAC_CCR1, 0x8d); + hscx_cmdr(hscx, 0x41); + WriteHSCX(hscx, IPAC_MASKB, 0); + test_and_set_bit(FLG_HDLC, &hscx->bch.Flags); + break; + default: + pr_info("%s: protocol not known %x\n", hscx->ip->name, + bprotocol); + return -ENOPROTOOPT; + } + } else + return -EINVAL; + hscx->bch.state = bprotocol; + return 0; +} + +static int +hscx_l2l1(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hscx_hw *hx = container_of(bch, struct hscx_hw, bch); + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(hx->ip->hwlock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + ret = 0; + hscx_fill_fifo(hx); + } + spin_unlock_irqrestore(hx->ip->hwlock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(hx->ip->hwlock, flags); + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) + ret = hscx_mode(hx, ch->protocol); + else + ret = 0; + spin_unlock_irqrestore(hx->ip->hwlock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + spin_lock_irqsave(hx->ip->hwlock, flags); + mISDN_clear_bchannel(bch); + hscx_mode(hx, ISDN_P_NONE); + spin_unlock_irqrestore(hx->ip->hwlock, flags); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + ret = 0; + break; + default: + pr_info("%s: %s unknown prim(%x,%x)\n", + hx->ip->name, __func__, hh->prim, hh->id); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + return mISDN_ctrl_bchannel(bch, cq); +} + +static int +hscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hscx_hw *hx = container_of(bch, struct hscx_hw, bch); + int ret = -EINVAL; + u_long flags; + + pr_debug("%s: %s cmd:%x %p\n", hx->ip->name, __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + cancel_work_sync(&bch->workq); + spin_lock_irqsave(hx->ip->hwlock, flags); + mISDN_clear_bchannel(bch); + hscx_mode(hx, ISDN_P_NONE); + spin_unlock_irqrestore(hx->ip->hwlock, flags); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(hx->ip->owner); + ret = 0; + break; + case CONTROL_CHANNEL: + ret = channel_bctrl(bch, arg); + break; + default: + pr_info("%s: %s unknown prim(%x)\n", + hx->ip->name, __func__, cmd); + } + return ret; +} + +static void +free_ipac(struct ipac_hw *ipac) +{ + isac_release(&ipac->isac); +} + +static const char *HSCXVer[] = +{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", + "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; + + + +static void +hscx_init(struct hscx_hw *hx) +{ + u8 val; + + WriteHSCX(hx, IPAC_RAH2, 0xFF); + WriteHSCX(hx, IPAC_XBCH, 0x00); + WriteHSCX(hx, IPAC_RLCR, 0x00); + + if (hx->ip->type & IPAC_TYPE_HSCX) { + WriteHSCX(hx, IPAC_CCR1, 0x85); + val = ReadHSCX(hx, HSCX_VSTR); + pr_debug("%s: HSCX VSTR %02x\n", hx->ip->name, val); + if (hx->bch.debug & DEBUG_HW) + pr_notice("%s: HSCX version %s\n", hx->ip->name, + HSCXVer[val & 0x0f]); + } else + WriteHSCX(hx, IPAC_CCR1, 0x82); + WriteHSCX(hx, IPAC_CCR2, 0x30); + WriteHSCX(hx, IPAC_XCCR, 0x07); + WriteHSCX(hx, IPAC_RCCR, 0x07); +} + +static int +ipac_init(struct ipac_hw *ipac) +{ + u8 val; + + if (ipac->type & IPAC_TYPE_HSCX) { + hscx_init(&ipac->hscx[0]); + hscx_init(&ipac->hscx[1]); + val = ReadIPAC(ipac, IPAC_ID); + } else if (ipac->type & IPAC_TYPE_IPAC) { + hscx_init(&ipac->hscx[0]); + hscx_init(&ipac->hscx[1]); + WriteIPAC(ipac, IPAC_MASK, IPAC__ON); + val = ReadIPAC(ipac, IPAC_CONF); + /* conf is default 0, but can be overwritten by card setup */ + pr_debug("%s: IPAC CONF %02x/%02x\n", ipac->name, + val, ipac->conf); + WriteIPAC(ipac, IPAC_CONF, ipac->conf); + val = ReadIPAC(ipac, IPAC_ID); + if (ipac->hscx[0].bch.debug & DEBUG_HW) + pr_notice("%s: IPAC Design ID %02x\n", ipac->name, val); + } + /* nothing special for IPACX to do here */ + return isac_init(&ipac->isac); +} + +static int +open_bchannel(struct ipac_hw *ipac, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel == 0 || rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + bch = &ipac->hscx[rq->adr.channel - 1].bch; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; + return 0; +} + +static int +channel_ctrl(struct ipac_hw *ipac, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; + break; + case MISDN_CTRL_LOOP: + /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ + if (cq->channel < 0 || cq->channel > 3) { + ret = -EINVAL; + break; + } + ret = ipac->ctrl(ipac, HW_TESTLOOP, cq->channel); + break; + case MISDN_CTRL_L1_TIMER3: + ret = ipac->isac.ctrl(&ipac->isac, HW_TIMER3_VALUE, cq->p1); + break; + default: + pr_info("%s: unknown CTRL OP %x\n", ipac->name, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +ipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct isac_hw *isac = container_of(dch, struct isac_hw, dch); + struct ipac_hw *ipac = container_of(isac, struct ipac_hw, isac); + struct channel_req *rq; + int err = 0; + + pr_debug("%s: DCTRL: %x %p\n", ipac->name, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if (rq->protocol == ISDN_P_TE_S0) + err = open_dchannel_caller(isac, rq, __builtin_return_address(0)); + else + err = open_bchannel(ipac, rq); + if (err) + break; + if (!try_module_get(ipac->owner)) + pr_info("%s: cannot get module\n", ipac->name); + break; + case CLOSE_CHANNEL: + pr_debug("%s: dev(%d) close from %p\n", ipac->name, + dch->dev.id, __builtin_return_address(0)); + module_put(ipac->owner); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(ipac, arg); + break; + default: + pr_debug("%s: unknown DCTRL command %x\n", ipac->name, cmd); + return -EINVAL; + } + return err; +} + +u32 +mISDNipac_init(struct ipac_hw *ipac, void *hw) +{ + u32 ret; + u8 i; + + ipac->hw = hw; + if (ipac->isac.dch.debug & DEBUG_HW) + pr_notice("%s: ipac type %x\n", ipac->name, ipac->type); + if (ipac->type & IPAC_TYPE_HSCX) { + ipac->isac.type = IPAC_TYPE_ISAC; + ipac->hscx[0].off = 0; + ipac->hscx[1].off = 0x40; + ipac->hscx[0].fifo_size = 32; + ipac->hscx[1].fifo_size = 32; + } else if (ipac->type & IPAC_TYPE_IPAC) { + ipac->isac.type = IPAC_TYPE_IPAC | IPAC_TYPE_ISAC; + ipac->hscx[0].off = 0; + ipac->hscx[1].off = 0x40; + ipac->hscx[0].fifo_size = 64; + ipac->hscx[1].fifo_size = 64; + } else if (ipac->type & IPAC_TYPE_IPACX) { + ipac->isac.type = IPAC_TYPE_IPACX | IPAC_TYPE_ISACX; + ipac->hscx[0].off = IPACX_OFF_ICA; + ipac->hscx[1].off = IPACX_OFF_ICB; + ipac->hscx[0].fifo_size = 64; + ipac->hscx[1].fifo_size = 64; + } else + return 0; + + mISDNisac_init(&ipac->isac, hw); + + ipac->isac.dch.dev.D.ctrl = ipac_dctrl; + + for (i = 0; i < 2; i++) { + ipac->hscx[i].bch.nr = i + 1; + set_channelmap(i + 1, ipac->isac.dch.dev.channelmap); + list_add(&ipac->hscx[i].bch.ch.list, + &ipac->isac.dch.dev.bchannels); + mISDN_initbchannel(&ipac->hscx[i].bch, MAX_DATA_MEM, + ipac->hscx[i].fifo_size); + ipac->hscx[i].bch.ch.nr = i + 1; + ipac->hscx[i].bch.ch.send = &hscx_l2l1; + ipac->hscx[i].bch.ch.ctrl = hscx_bctrl; + ipac->hscx[i].bch.hw = hw; + ipac->hscx[i].ip = ipac; + /* default values for IOM time slots + * can be overwritten by card */ + ipac->hscx[i].slot = (i == 0) ? 0x2f : 0x03; + } + + ipac->init = ipac_init; + ipac->release = free_ipac; + + ret = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + return ret; +} +EXPORT_SYMBOL(mISDNipac_init); + +static int __init +isac_mod_init(void) +{ + pr_notice("mISDNipac module version %s\n", ISAC_REV); + return 0; +} + +static void __exit +isac_mod_cleanup(void) +{ + pr_notice("mISDNipac module unloaded\n"); +} +module_init(isac_mod_init); +module_exit(isac_mod_cleanup); diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c new file mode 100644 index 000000000..386731ec2 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/mISDNisar.c @@ -0,0 +1,1708 @@ +/* + * mISDNisar.c ISAR (Siemens PSB 7110) specific functions + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* define this to enable static debug messages, if you kernel supports + * dynamic debugging, you should use debugfs for this + */ +/* #define DEBUG */ + +#include <linux/gfp.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#include <linux/mISDNhw.h> +#include <linux/module.h> +#include "isar.h" + +#define ISAR_REV "2.1" + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(ISAR_REV); + +#define DEBUG_HW_FIRMWARE_FIFO 0x10000 + +static const u8 faxmodulation_s[] = "3,24,48,72,73,74,96,97,98,121,122,145,146"; +static const u8 faxmodulation[] = {3, 24, 48, 72, 73, 74, 96, 97, 98, 121, + 122, 145, 146}; +#define FAXMODCNT 13 + +static void isar_setup(struct isar_hw *); + +static inline int +waitforHIA(struct isar_hw *isar, int timeout) +{ + int t = timeout; + u8 val = isar->read_reg(isar->hw, ISAR_HIA); + + while ((val & 1) && t) { + udelay(1); + t--; + val = isar->read_reg(isar->hw, ISAR_HIA); + } + pr_debug("%s: HIA after %dus\n", isar->name, timeout - t); + return timeout; +} + +/* + * send msg to ISAR mailbox + * if msg is NULL use isar->buf + */ +static int +send_mbox(struct isar_hw *isar, u8 his, u8 creg, u8 len, u8 *msg) +{ + if (!waitforHIA(isar, 1000)) + return 0; + pr_debug("send_mbox(%02x,%02x,%d)\n", his, creg, len); + isar->write_reg(isar->hw, ISAR_CTRL_H, creg); + isar->write_reg(isar->hw, ISAR_CTRL_L, len); + isar->write_reg(isar->hw, ISAR_WADR, 0); + if (!msg) + msg = isar->buf; + if (msg && len) { + isar->write_fifo(isar->hw, ISAR_MBOX, msg, len); + if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) { + int l = 0; + + while (l < (int)len) { + hex_dump_to_buffer(msg + l, len - l, 32, 1, + isar->log, 256, 1); + pr_debug("%s: %s %02x: %s\n", isar->name, + __func__, l, isar->log); + l += 32; + } + } + } + isar->write_reg(isar->hw, ISAR_HIS, his); + waitforHIA(isar, 1000); + return 1; +} + +/* + * receive message from ISAR mailbox + * if msg is NULL use isar->buf + */ +static void +rcv_mbox(struct isar_hw *isar, u8 *msg) +{ + if (!msg) + msg = isar->buf; + isar->write_reg(isar->hw, ISAR_RADR, 0); + if (msg && isar->clsb) { + isar->read_fifo(isar->hw, ISAR_MBOX, msg, isar->clsb); + if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) { + int l = 0; + + while (l < (int)isar->clsb) { + hex_dump_to_buffer(msg + l, isar->clsb - l, 32, + 1, isar->log, 256, 1); + pr_debug("%s: %s %02x: %s\n", isar->name, + __func__, l, isar->log); + l += 32; + } + } + } + isar->write_reg(isar->hw, ISAR_IIA, 0); +} + +static inline void +get_irq_infos(struct isar_hw *isar) +{ + isar->iis = isar->read_reg(isar->hw, ISAR_IIS); + isar->cmsb = isar->read_reg(isar->hw, ISAR_CTRL_H); + isar->clsb = isar->read_reg(isar->hw, ISAR_CTRL_L); + pr_debug("%s: rcv_mbox(%02x,%02x,%d)\n", isar->name, + isar->iis, isar->cmsb, isar->clsb); +} + +/* + * poll answer message from ISAR mailbox + * should be used only with ISAR IRQs disabled before DSP was started + * + */ +static int +poll_mbox(struct isar_hw *isar, int maxdelay) +{ + int t = maxdelay; + u8 irq; + + irq = isar->read_reg(isar->hw, ISAR_IRQBIT); + while (t && !(irq & ISAR_IRQSTA)) { + udelay(1); + t--; + } + if (t) { + get_irq_infos(isar); + rcv_mbox(isar, NULL); + } + pr_debug("%s: pulled %d bytes after %d us\n", + isar->name, isar->clsb, maxdelay - t); + return t; +} + +static int +ISARVersion(struct isar_hw *isar) +{ + int ver; + + /* disable ISAR IRQ */ + isar->write_reg(isar->hw, ISAR_IRQBIT, 0); + isar->buf[0] = ISAR_MSG_HWVER; + isar->buf[1] = 0; + isar->buf[2] = 1; + if (!send_mbox(isar, ISAR_HIS_VNR, 0, 3, NULL)) + return -1; + if (!poll_mbox(isar, 1000)) + return -2; + if (isar->iis == ISAR_IIS_VNR) { + if (isar->clsb == 1) { + ver = isar->buf[0] & 0xf; + return ver; + } + return -3; + } + return -4; +} + +static int +load_firmware(struct isar_hw *isar, const u8 *buf, int size) +{ + u32 saved_debug = isar->ch[0].bch.debug; + int ret, cnt; + u8 nom, noc; + u16 left, val, *sp = (u16 *)buf; + u8 *mp; + u_long flags; + + struct { + u16 sadr; + u16 len; + u16 d_key; + } blk_head; + + if (1 != isar->version) { + pr_err("%s: ISAR wrong version %d firmware download aborted\n", + isar->name, isar->version); + return -EINVAL; + } + if (!(saved_debug & DEBUG_HW_FIRMWARE_FIFO)) + isar->ch[0].bch.debug &= ~DEBUG_HW_BFIFO; + pr_debug("%s: load firmware %d words (%d bytes)\n", + isar->name, size / 2, size); + cnt = 0; + size /= 2; + /* disable ISAR IRQ */ + spin_lock_irqsave(isar->hwlock, flags); + isar->write_reg(isar->hw, ISAR_IRQBIT, 0); + spin_unlock_irqrestore(isar->hwlock, flags); + while (cnt < size) { + blk_head.sadr = le16_to_cpu(*sp++); + blk_head.len = le16_to_cpu(*sp++); + blk_head.d_key = le16_to_cpu(*sp++); + cnt += 3; + pr_debug("ISAR firmware block (%#x,%d,%#x)\n", + blk_head.sadr, blk_head.len, blk_head.d_key & 0xff); + left = blk_head.len; + if (cnt + left > size) { + pr_info("%s: firmware error have %d need %d words\n", + isar->name, size, cnt + left); + ret = -EINVAL; + goto reterrflg; + } + spin_lock_irqsave(isar->hwlock, flags); + if (!send_mbox(isar, ISAR_HIS_DKEY, blk_head.d_key & 0xff, + 0, NULL)) { + pr_info("ISAR send_mbox dkey failed\n"); + ret = -ETIME; + goto reterror; + } + if (!poll_mbox(isar, 1000)) { + pr_warning("ISAR poll_mbox dkey failed\n"); + ret = -ETIME; + goto reterror; + } + spin_unlock_irqrestore(isar->hwlock, flags); + if ((isar->iis != ISAR_IIS_DKEY) || isar->cmsb || isar->clsb) { + pr_info("ISAR wrong dkey response (%x,%x,%x)\n", + isar->iis, isar->cmsb, isar->clsb); + ret = 1; + goto reterrflg; + } + while (left > 0) { + if (left > 126) + noc = 126; + else + noc = left; + nom = (2 * noc) + 3; + mp = isar->buf; + /* the ISAR is big endian */ + *mp++ = blk_head.sadr >> 8; + *mp++ = blk_head.sadr & 0xFF; + left -= noc; + cnt += noc; + *mp++ = noc; + pr_debug("%s: load %3d words at %04x\n", isar->name, + noc, blk_head.sadr); + blk_head.sadr += noc; + while (noc) { + val = le16_to_cpu(*sp++); + *mp++ = val >> 8; + *mp++ = val & 0xFF; + noc--; + } + spin_lock_irqsave(isar->hwlock, flags); + if (!send_mbox(isar, ISAR_HIS_FIRM, 0, nom, NULL)) { + pr_info("ISAR send_mbox prog failed\n"); + ret = -ETIME; + goto reterror; + } + if (!poll_mbox(isar, 1000)) { + pr_info("ISAR poll_mbox prog failed\n"); + ret = -ETIME; + goto reterror; + } + spin_unlock_irqrestore(isar->hwlock, flags); + if ((isar->iis != ISAR_IIS_FIRM) || + isar->cmsb || isar->clsb) { + pr_info("ISAR wrong prog response (%x,%x,%x)\n", + isar->iis, isar->cmsb, isar->clsb); + ret = -EIO; + goto reterrflg; + } + } + pr_debug("%s: ISAR firmware block %d words loaded\n", + isar->name, blk_head.len); + } + isar->ch[0].bch.debug = saved_debug; + /* 10ms delay */ + cnt = 10; + while (cnt--) + mdelay(1); + isar->buf[0] = 0xff; + isar->buf[1] = 0xfe; + isar->bstat = 0; + spin_lock_irqsave(isar->hwlock, flags); + if (!send_mbox(isar, ISAR_HIS_STDSP, 0, 2, NULL)) { + pr_info("ISAR send_mbox start dsp failed\n"); + ret = -ETIME; + goto reterror; + } + if (!poll_mbox(isar, 1000)) { + pr_info("ISAR poll_mbox start dsp failed\n"); + ret = -ETIME; + goto reterror; + } + if ((isar->iis != ISAR_IIS_STDSP) || isar->cmsb || isar->clsb) { + pr_info("ISAR wrong start dsp response (%x,%x,%x)\n", + isar->iis, isar->cmsb, isar->clsb); + ret = -EIO; + goto reterror; + } else + pr_debug("%s: ISAR start dsp success\n", isar->name); + + /* NORMAL mode entered */ + /* Enable IRQs of ISAR */ + isar->write_reg(isar->hw, ISAR_IRQBIT, ISAR_IRQSTA); + spin_unlock_irqrestore(isar->hwlock, flags); + cnt = 1000; /* max 1s */ + while ((!isar->bstat) && cnt) { + mdelay(1); + cnt--; + } + if (!cnt) { + pr_info("ISAR no general status event received\n"); + ret = -ETIME; + goto reterrflg; + } else + pr_debug("%s: ISAR general status event %x\n", + isar->name, isar->bstat); + /* 10ms delay */ + cnt = 10; + while (cnt--) + mdelay(1); + isar->iis = 0; + spin_lock_irqsave(isar->hwlock, flags); + if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) { + pr_info("ISAR send_mbox self tst failed\n"); + ret = -ETIME; + goto reterror; + } + spin_unlock_irqrestore(isar->hwlock, flags); + cnt = 10000; /* max 100 ms */ + while ((isar->iis != ISAR_IIS_DIAG) && cnt) { + udelay(10); + cnt--; + } + mdelay(1); + if (!cnt) { + pr_info("ISAR no self tst response\n"); + ret = -ETIME; + goto reterrflg; + } + if ((isar->cmsb == ISAR_CTRL_STST) && (isar->clsb == 1) + && (isar->buf[0] == 0)) + pr_debug("%s: ISAR selftest OK\n", isar->name); + else { + pr_info("ISAR selftest not OK %x/%x/%x\n", + isar->cmsb, isar->clsb, isar->buf[0]); + ret = -EIO; + goto reterrflg; + } + spin_lock_irqsave(isar->hwlock, flags); + isar->iis = 0; + if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) { + pr_info("ISAR RQST SVN failed\n"); + ret = -ETIME; + goto reterror; + } + spin_unlock_irqrestore(isar->hwlock, flags); + cnt = 30000; /* max 300 ms */ + while ((isar->iis != ISAR_IIS_DIAG) && cnt) { + udelay(10); + cnt--; + } + mdelay(1); + if (!cnt) { + pr_info("ISAR no SVN response\n"); + ret = -ETIME; + goto reterrflg; + } else { + if ((isar->cmsb == ISAR_CTRL_SWVER) && (isar->clsb == 1)) { + pr_notice("%s: ISAR software version %#x\n", + isar->name, isar->buf[0]); + } else { + pr_info("%s: ISAR wrong swver response (%x,%x)" + " cnt(%d)\n", isar->name, isar->cmsb, + isar->clsb, cnt); + ret = -EIO; + goto reterrflg; + } + } + spin_lock_irqsave(isar->hwlock, flags); + isar_setup(isar); + spin_unlock_irqrestore(isar->hwlock, flags); + ret = 0; +reterrflg: + spin_lock_irqsave(isar->hwlock, flags); +reterror: + isar->ch[0].bch.debug = saved_debug; + if (ret) + /* disable ISAR IRQ */ + isar->write_reg(isar->hw, ISAR_IRQBIT, 0); + spin_unlock_irqrestore(isar->hwlock, flags); + return ret; +} + +static inline void +deliver_status(struct isar_ch *ch, int status) +{ + pr_debug("%s: HL->LL FAXIND %x\n", ch->is->name, status); + _queue_data(&ch->bch.ch, PH_CONTROL_IND, status, 0, NULL, GFP_ATOMIC); +} + +static inline void +isar_rcv_frame(struct isar_ch *ch) +{ + u8 *ptr; + int maxlen; + + if (!ch->is->clsb) { + pr_debug("%s; ISAR zero len frame\n", ch->is->name); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + return; + } + if (test_bit(FLG_RX_OFF, &ch->bch.Flags)) { + ch->bch.dropcnt += ch->is->clsb; + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + return; + } + switch (ch->bch.state) { + case ISDN_P_NONE: + pr_debug("%s: ISAR protocol 0 spurious IIS_RDATA %x/%x/%x\n", + ch->is->name, ch->is->iis, ch->is->cmsb, ch->is->clsb); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + break; + case ISDN_P_B_RAW: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_MODEM_ASYNC: + maxlen = bchannel_get_rxbuf(&ch->bch, ch->is->clsb); + if (maxlen < 0) { + pr_warning("%s.B%d: No bufferspace for %d bytes\n", + ch->is->name, ch->bch.nr, ch->is->clsb); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + break; + } + rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb)); + recv_Bchannel(&ch->bch, 0, false); + break; + case ISDN_P_B_HDLC: + maxlen = bchannel_get_rxbuf(&ch->bch, ch->is->clsb); + if (maxlen < 0) { + pr_warning("%s.B%d: No bufferspace for %d bytes\n", + ch->is->name, ch->bch.nr, ch->is->clsb); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + break; + } + if (ch->is->cmsb & HDLC_ERROR) { + pr_debug("%s: ISAR frame error %x len %d\n", + ch->is->name, ch->is->cmsb, ch->is->clsb); +#ifdef ERROR_STATISTIC + if (ch->is->cmsb & HDLC_ERR_RER) + ch->bch.err_inv++; + if (ch->is->cmsb & HDLC_ERR_CER) + ch->bch.err_crc++; +#endif + skb_trim(ch->bch.rx_skb, 0); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + break; + } + if (ch->is->cmsb & HDLC_FSD) + skb_trim(ch->bch.rx_skb, 0); + ptr = skb_put(ch->bch.rx_skb, ch->is->clsb); + rcv_mbox(ch->is, ptr); + if (ch->is->cmsb & HDLC_FED) { + if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */ + pr_debug("%s: ISAR frame to short %d\n", + ch->is->name, ch->bch.rx_skb->len); + skb_trim(ch->bch.rx_skb, 0); + break; + } + skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2); + recv_Bchannel(&ch->bch, 0, false); + } + break; + case ISDN_P_B_T30_FAX: + if (ch->state != STFAX_ACTIV) { + pr_debug("%s: isar_rcv_frame: not ACTIV\n", + ch->is->name); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + if (ch->bch.rx_skb) + skb_trim(ch->bch.rx_skb, 0); + break; + } + if (!ch->bch.rx_skb) { + ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen, + GFP_ATOMIC); + if (unlikely(!ch->bch.rx_skb)) { + pr_info("%s: B receive out of memory\n", + __func__); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + break; + } + } + if (ch->cmd == PCTRL_CMD_FRM) { + rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb)); + pr_debug("%s: isar_rcv_frame: %d\n", + ch->is->name, ch->bch.rx_skb->len); + if (ch->is->cmsb & SART_NMD) { /* ABORT */ + pr_debug("%s: isar_rcv_frame: no more data\n", + ch->is->name); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + send_mbox(ch->is, SET_DPS(ch->dpath) | + ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, + 0, NULL); + ch->state = STFAX_ESCAPE; + /* set_skb_flag(skb, DF_NOMOREDATA); */ + } + recv_Bchannel(&ch->bch, 0, false); + if (ch->is->cmsb & SART_NMD) + deliver_status(ch, HW_MOD_NOCARR); + break; + } + if (ch->cmd != PCTRL_CMD_FRH) { + pr_debug("%s: isar_rcv_frame: unknown fax mode %x\n", + ch->is->name, ch->cmd); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + if (ch->bch.rx_skb) + skb_trim(ch->bch.rx_skb, 0); + break; + } + /* PCTRL_CMD_FRH */ + if ((ch->bch.rx_skb->len + ch->is->clsb) > + (ch->bch.maxlen + 2)) { + pr_info("%s: %s incoming packet too large\n", + ch->is->name, __func__); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + skb_trim(ch->bch.rx_skb, 0); + break; + } else if (ch->is->cmsb & HDLC_ERROR) { + pr_info("%s: ISAR frame error %x len %d\n", + ch->is->name, ch->is->cmsb, ch->is->clsb); + skb_trim(ch->bch.rx_skb, 0); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + break; + } + if (ch->is->cmsb & HDLC_FSD) + skb_trim(ch->bch.rx_skb, 0); + ptr = skb_put(ch->bch.rx_skb, ch->is->clsb); + rcv_mbox(ch->is, ptr); + if (ch->is->cmsb & HDLC_FED) { + if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */ + pr_info("%s: ISAR frame to short %d\n", + ch->is->name, ch->bch.rx_skb->len); + skb_trim(ch->bch.rx_skb, 0); + break; + } + skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2); + recv_Bchannel(&ch->bch, 0, false); + } + if (ch->is->cmsb & SART_NMD) { /* ABORT */ + pr_debug("%s: isar_rcv_frame: no more data\n", + ch->is->name); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + if (ch->bch.rx_skb) + skb_trim(ch->bch.rx_skb, 0); + send_mbox(ch->is, SET_DPS(ch->dpath) | + ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); + ch->state = STFAX_ESCAPE; + deliver_status(ch, HW_MOD_NOCARR); + } + break; + default: + pr_info("isar_rcv_frame protocol (%x)error\n", ch->bch.state); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); + break; + } +} + +static void +isar_fill_fifo(struct isar_ch *ch) +{ + int count; + u8 msb; + u8 *ptr; + + pr_debug("%s: ch%d tx_skb %d tx_idx %d\n", ch->is->name, ch->bch.nr, + ch->bch.tx_skb ? ch->bch.tx_skb->len : -1, ch->bch.tx_idx); + if (!(ch->is->bstat & + (ch->dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2))) + return; + if (!ch->bch.tx_skb) { + if (!test_bit(FLG_TX_EMPTY, &ch->bch.Flags) || + (ch->bch.state != ISDN_P_B_RAW)) + return; + count = ch->mml; + /* use the card buffer */ + memset(ch->is->buf, ch->bch.fill[0], count); + send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, + 0, count, ch->is->buf); + return; + } + count = ch->bch.tx_skb->len - ch->bch.tx_idx; + if (count <= 0) + return; + if (count > ch->mml) { + msb = 0; + count = ch->mml; + } else { + msb = HDLC_FED; + } + ptr = ch->bch.tx_skb->data + ch->bch.tx_idx; + if (!ch->bch.tx_idx) { + pr_debug("%s: frame start\n", ch->is->name); + if ((ch->bch.state == ISDN_P_B_T30_FAX) && + (ch->cmd == PCTRL_CMD_FTH)) { + if (count > 1) { + if ((ptr[0] == 0xff) && (ptr[1] == 0x13)) { + /* last frame */ + test_and_set_bit(FLG_LASTDATA, + &ch->bch.Flags); + pr_debug("%s: set LASTDATA\n", + ch->is->name); + if (msb == HDLC_FED) + test_and_set_bit(FLG_DLEETX, + &ch->bch.Flags); + } + } + } + msb |= HDLC_FST; + } + ch->bch.tx_idx += count; + switch (ch->bch.state) { + case ISDN_P_NONE: + pr_info("%s: wrong protocol 0\n", __func__); + break; + case ISDN_P_B_RAW: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_MODEM_ASYNC: + send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, + 0, count, ptr); + break; + case ISDN_P_B_HDLC: + send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, + msb, count, ptr); + break; + case ISDN_P_B_T30_FAX: + if (ch->state != STFAX_ACTIV) + pr_debug("%s: not ACTIV\n", ch->is->name); + else if (ch->cmd == PCTRL_CMD_FTH) + send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, + msb, count, ptr); + else if (ch->cmd == PCTRL_CMD_FTM) + send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, + 0, count, ptr); + else + pr_debug("%s: not FTH/FTM\n", ch->is->name); + break; + default: + pr_info("%s: protocol(%x) error\n", + __func__, ch->bch.state); + break; + } +} + +static inline struct isar_ch * +sel_bch_isar(struct isar_hw *isar, u8 dpath) +{ + struct isar_ch *base = &isar->ch[0]; + + if ((!dpath) || (dpath > 2)) + return NULL; + if (base->dpath == dpath) + return base; + base++; + if (base->dpath == dpath) + return base; + return NULL; +} + +static void +send_next(struct isar_ch *ch) +{ + pr_debug("%s: %s ch%d tx_skb %d tx_idx %d\n", ch->is->name, __func__, + ch->bch.nr, ch->bch.tx_skb ? ch->bch.tx_skb->len : -1, + ch->bch.tx_idx); + if (ch->bch.state == ISDN_P_B_T30_FAX) { + if (ch->cmd == PCTRL_CMD_FTH) { + if (test_bit(FLG_LASTDATA, &ch->bch.Flags)) { + pr_debug("set NMD_DATA\n"); + test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags); + } + } else if (ch->cmd == PCTRL_CMD_FTM) { + if (test_bit(FLG_DLEETX, &ch->bch.Flags)) { + test_and_set_bit(FLG_LASTDATA, &ch->bch.Flags); + test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags); + } + } + } + if (ch->bch.tx_skb) + dev_kfree_skb(ch->bch.tx_skb); + if (get_next_bframe(&ch->bch)) { + isar_fill_fifo(ch); + test_and_clear_bit(FLG_TX_EMPTY, &ch->bch.Flags); + } else if (test_bit(FLG_TX_EMPTY, &ch->bch.Flags)) { + isar_fill_fifo(ch); + } else { + if (test_and_clear_bit(FLG_DLEETX, &ch->bch.Flags)) { + if (test_and_clear_bit(FLG_LASTDATA, + &ch->bch.Flags)) { + if (test_and_clear_bit(FLG_NMD_DATA, + &ch->bch.Flags)) { + u8 zd = 0; + send_mbox(ch->is, SET_DPS(ch->dpath) | + ISAR_HIS_SDATA, 0x01, 1, &zd); + } + test_and_set_bit(FLG_LL_OK, &ch->bch.Flags); + } else { + deliver_status(ch, HW_MOD_CONNECT); + } + } else if (test_bit(FLG_FILLEMPTY, &ch->bch.Flags)) { + test_and_set_bit(FLG_TX_EMPTY, &ch->bch.Flags); + } + } +} + +static void +check_send(struct isar_hw *isar, u8 rdm) +{ + struct isar_ch *ch; + + pr_debug("%s: rdm %x\n", isar->name, rdm); + if (rdm & BSTAT_RDM1) { + ch = sel_bch_isar(isar, 1); + if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) { + if (ch->bch.tx_skb && (ch->bch.tx_skb->len > + ch->bch.tx_idx)) + isar_fill_fifo(ch); + else + send_next(ch); + } + } + if (rdm & BSTAT_RDM2) { + ch = sel_bch_isar(isar, 2); + if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) { + if (ch->bch.tx_skb && (ch->bch.tx_skb->len > + ch->bch.tx_idx)) + isar_fill_fifo(ch); + else + send_next(ch); + } + } +} + +const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4", + "300", "600", "1200", "2400", "4800", "7200", + "9600nt", "9600t", "12000", "14400", "WRONG"}; +const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21", + "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"}; + +static void +isar_pump_status_rsp(struct isar_ch *ch) { + u8 ril = ch->is->buf[0]; + u8 rim; + + if (!test_and_clear_bit(ISAR_RATE_REQ, &ch->is->Flags)) + return; + if (ril > 14) { + pr_info("%s: wrong pstrsp ril=%d\n", ch->is->name, ril); + ril = 15; + } + switch (ch->is->buf[1]) { + case 0: + rim = 0; + break; + case 0x20: + rim = 2; + break; + case 0x40: + rim = 3; + break; + case 0x41: + rim = 4; + break; + case 0x51: + rim = 5; + break; + case 0x61: + rim = 6; + break; + case 0x71: + rim = 7; + break; + case 0x82: + rim = 8; + break; + case 0x92: + rim = 9; + break; + case 0xa2: + rim = 10; + break; + default: + rim = 1; + break; + } + sprintf(ch->conmsg, "%s %s", dmril[ril], dmrim[rim]); + pr_debug("%s: pump strsp %s\n", ch->is->name, ch->conmsg); +} + +static void +isar_pump_statev_modem(struct isar_ch *ch, u8 devt) { + u8 dps = SET_DPS(ch->dpath); + + switch (devt) { + case PSEV_10MS_TIMER: + pr_debug("%s: pump stev TIMER\n", ch->is->name); + break; + case PSEV_CON_ON: + pr_debug("%s: pump stev CONNECT\n", ch->is->name); + deliver_status(ch, HW_MOD_CONNECT); + break; + case PSEV_CON_OFF: + pr_debug("%s: pump stev NO CONNECT\n", ch->is->name); + send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + deliver_status(ch, HW_MOD_NOCARR); + break; + case PSEV_V24_OFF: + pr_debug("%s: pump stev V24 OFF\n", ch->is->name); + break; + case PSEV_CTS_ON: + pr_debug("%s: pump stev CTS ON\n", ch->is->name); + break; + case PSEV_CTS_OFF: + pr_debug("%s pump stev CTS OFF\n", ch->is->name); + break; + case PSEV_DCD_ON: + pr_debug("%s: pump stev CARRIER ON\n", ch->is->name); + test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags); + send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + break; + case PSEV_DCD_OFF: + pr_debug("%s: pump stev CARRIER OFF\n", ch->is->name); + break; + case PSEV_DSR_ON: + pr_debug("%s: pump stev DSR ON\n", ch->is->name); + break; + case PSEV_DSR_OFF: + pr_debug("%s: pump stev DSR_OFF\n", ch->is->name); + break; + case PSEV_REM_RET: + pr_debug("%s: pump stev REMOTE RETRAIN\n", ch->is->name); + break; + case PSEV_REM_REN: + pr_debug("%s: pump stev REMOTE RENEGOTIATE\n", ch->is->name); + break; + case PSEV_GSTN_CLR: + pr_debug("%s: pump stev GSTN CLEAR\n", ch->is->name); + break; + default: + pr_info("u%s: unknown pump stev %x\n", ch->is->name, devt); + break; + } +} + +static void +isar_pump_statev_fax(struct isar_ch *ch, u8 devt) { + u8 dps = SET_DPS(ch->dpath); + u8 p1; + + switch (devt) { + case PSEV_10MS_TIMER: + pr_debug("%s: pump stev TIMER\n", ch->is->name); + break; + case PSEV_RSP_READY: + pr_debug("%s: pump stev RSP_READY\n", ch->is->name); + ch->state = STFAX_READY; + deliver_status(ch, HW_MOD_READY); +#ifdef AUTOCON + if (test_bit(BC_FLG_ORIG, &ch->bch.Flags)) + isar_pump_cmd(bch, HW_MOD_FRH, 3); + else + isar_pump_cmd(bch, HW_MOD_FTH, 3); +#endif + break; + case PSEV_LINE_TX_H: + if (ch->state == STFAX_LINE) { + pr_debug("%s: pump stev LINE_TX_H\n", ch->is->name); + ch->state = STFAX_CONT; + send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, + PCTRL_CMD_CONT, 0, NULL); + } else { + pr_debug("%s: pump stev LINE_TX_H wrong st %x\n", + ch->is->name, ch->state); + } + break; + case PSEV_LINE_RX_H: + if (ch->state == STFAX_LINE) { + pr_debug("%s: pump stev LINE_RX_H\n", ch->is->name); + ch->state = STFAX_CONT; + send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, + PCTRL_CMD_CONT, 0, NULL); + } else { + pr_debug("%s: pump stev LINE_RX_H wrong st %x\n", + ch->is->name, ch->state); + } + break; + case PSEV_LINE_TX_B: + if (ch->state == STFAX_LINE) { + pr_debug("%s: pump stev LINE_TX_B\n", ch->is->name); + ch->state = STFAX_CONT; + send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, + PCTRL_CMD_CONT, 0, NULL); + } else { + pr_debug("%s: pump stev LINE_TX_B wrong st %x\n", + ch->is->name, ch->state); + } + break; + case PSEV_LINE_RX_B: + if (ch->state == STFAX_LINE) { + pr_debug("%s: pump stev LINE_RX_B\n", ch->is->name); + ch->state = STFAX_CONT; + send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, + PCTRL_CMD_CONT, 0, NULL); + } else { + pr_debug("%s: pump stev LINE_RX_B wrong st %x\n", + ch->is->name, ch->state); + } + break; + case PSEV_RSP_CONN: + if (ch->state == STFAX_CONT) { + pr_debug("%s: pump stev RSP_CONN\n", ch->is->name); + ch->state = STFAX_ACTIV; + test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags); + send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + if (ch->cmd == PCTRL_CMD_FTH) { + int delay = (ch->mod == 3) ? 1000 : 200; + /* 1s (200 ms) Flags before data */ + if (test_and_set_bit(FLG_FTI_RUN, + &ch->bch.Flags)) + del_timer(&ch->ftimer); + ch->ftimer.expires = + jiffies + ((delay * HZ) / 1000); + test_and_set_bit(FLG_LL_CONN, + &ch->bch.Flags); + add_timer(&ch->ftimer); + } else { + deliver_status(ch, HW_MOD_CONNECT); + } + } else { + pr_debug("%s: pump stev RSP_CONN wrong st %x\n", + ch->is->name, ch->state); + } + break; + case PSEV_FLAGS_DET: + pr_debug("%s: pump stev FLAGS_DET\n", ch->is->name); + break; + case PSEV_RSP_DISC: + pr_debug("%s: pump stev RSP_DISC state(%d)\n", + ch->is->name, ch->state); + if (ch->state == STFAX_ESCAPE) { + p1 = 5; + switch (ch->newcmd) { + case 0: + ch->state = STFAX_READY; + break; + case PCTRL_CMD_FTM: + p1 = 2; + /* fall through */ + case PCTRL_CMD_FTH: + send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, + PCTRL_CMD_SILON, 1, &p1); + ch->state = STFAX_SILDET; + break; + case PCTRL_CMD_FRH: + case PCTRL_CMD_FRM: + ch->mod = ch->newmod; + p1 = ch->newmod; + ch->newmod = 0; + ch->cmd = ch->newcmd; + ch->newcmd = 0; + send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, + ch->cmd, 1, &p1); + ch->state = STFAX_LINE; + ch->try_mod = 3; + break; + default: + pr_debug("%s: RSP_DISC unknown newcmd %x\n", + ch->is->name, ch->newcmd); + break; + } + } else if (ch->state == STFAX_ACTIV) { + if (test_and_clear_bit(FLG_LL_OK, &ch->bch.Flags)) + deliver_status(ch, HW_MOD_OK); + else if (ch->cmd == PCTRL_CMD_FRM) + deliver_status(ch, HW_MOD_NOCARR); + else + deliver_status(ch, HW_MOD_FCERROR); + ch->state = STFAX_READY; + } else if (ch->state != STFAX_SILDET) { + /* ignore in STFAX_SILDET */ + ch->state = STFAX_READY; + deliver_status(ch, HW_MOD_FCERROR); + } + break; + case PSEV_RSP_SILDET: + pr_debug("%s: pump stev RSP_SILDET\n", ch->is->name); + if (ch->state == STFAX_SILDET) { + ch->mod = ch->newmod; + p1 = ch->newmod; + ch->newmod = 0; + ch->cmd = ch->newcmd; + ch->newcmd = 0; + send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, + ch->cmd, 1, &p1); + ch->state = STFAX_LINE; + ch->try_mod = 3; + } + break; + case PSEV_RSP_SILOFF: + pr_debug("%s: pump stev RSP_SILOFF\n", ch->is->name); + break; + case PSEV_RSP_FCERR: + if (ch->state == STFAX_LINE) { + pr_debug("%s: pump stev RSP_FCERR try %d\n", + ch->is->name, ch->try_mod); + if (ch->try_mod--) { + send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, + ch->cmd, 1, &ch->mod); + break; + } + } + pr_debug("%s: pump stev RSP_FCERR\n", ch->is->name); + ch->state = STFAX_ESCAPE; + send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, + 0, NULL); + deliver_status(ch, HW_MOD_FCERROR); + break; + default: + break; + } +} + +void +mISDNisar_irq(struct isar_hw *isar) +{ + struct isar_ch *ch; + + get_irq_infos(isar); + switch (isar->iis & ISAR_IIS_MSCMSD) { + case ISAR_IIS_RDATA: + ch = sel_bch_isar(isar, isar->iis >> 6); + if (ch) + isar_rcv_frame(ch); + else { + pr_debug("%s: ISAR spurious IIS_RDATA %x/%x/%x\n", + isar->name, isar->iis, isar->cmsb, + isar->clsb); + isar->write_reg(isar->hw, ISAR_IIA, 0); + } + break; + case ISAR_IIS_GSTEV: + isar->write_reg(isar->hw, ISAR_IIA, 0); + isar->bstat |= isar->cmsb; + check_send(isar, isar->cmsb); + break; + case ISAR_IIS_BSTEV: +#ifdef ERROR_STATISTIC + ch = sel_bch_isar(isar, isar->iis >> 6); + if (ch) { + if (isar->cmsb == BSTEV_TBO) + ch->bch.err_tx++; + if (isar->cmsb == BSTEV_RBO) + ch->bch.err_rdo++; + } +#endif + pr_debug("%s: Buffer STEV dpath%d msb(%x)\n", + isar->name, isar->iis >> 6, isar->cmsb); + isar->write_reg(isar->hw, ISAR_IIA, 0); + break; + case ISAR_IIS_PSTEV: + ch = sel_bch_isar(isar, isar->iis >> 6); + if (ch) { + rcv_mbox(isar, NULL); + if (ch->bch.state == ISDN_P_B_MODEM_ASYNC) + isar_pump_statev_modem(ch, isar->cmsb); + else if (ch->bch.state == ISDN_P_B_T30_FAX) + isar_pump_statev_fax(ch, isar->cmsb); + else if (ch->bch.state == ISDN_P_B_RAW) { + int tt; + tt = isar->cmsb | 0x30; + if (tt == 0x3e) + tt = '*'; + else if (tt == 0x3f) + tt = '#'; + else if (tt > '9') + tt += 7; + tt |= DTMF_TONE_VAL; + _queue_data(&ch->bch.ch, PH_CONTROL_IND, + MISDN_ID_ANY, sizeof(tt), &tt, + GFP_ATOMIC); + } else + pr_debug("%s: ISAR IIS_PSTEV pm %d sta %x\n", + isar->name, ch->bch.state, + isar->cmsb); + } else { + pr_debug("%s: ISAR spurious IIS_PSTEV %x/%x/%x\n", + isar->name, isar->iis, isar->cmsb, + isar->clsb); + isar->write_reg(isar->hw, ISAR_IIA, 0); + } + break; + case ISAR_IIS_PSTRSP: + ch = sel_bch_isar(isar, isar->iis >> 6); + if (ch) { + rcv_mbox(isar, NULL); + isar_pump_status_rsp(ch); + } else { + pr_debug("%s: ISAR spurious IIS_PSTRSP %x/%x/%x\n", + isar->name, isar->iis, isar->cmsb, + isar->clsb); + isar->write_reg(isar->hw, ISAR_IIA, 0); + } + break; + case ISAR_IIS_DIAG: + case ISAR_IIS_BSTRSP: + case ISAR_IIS_IOM2RSP: + rcv_mbox(isar, NULL); + break; + case ISAR_IIS_INVMSG: + rcv_mbox(isar, NULL); + pr_debug("%s: invalid msg his:%x\n", isar->name, isar->cmsb); + break; + default: + rcv_mbox(isar, NULL); + pr_debug("%s: unhandled msg iis(%x) ctrl(%x/%x)\n", + isar->name, isar->iis, isar->cmsb, isar->clsb); + break; + } +} +EXPORT_SYMBOL(mISDNisar_irq); + +static void +ftimer_handler(struct timer_list *t) +{ + struct isar_ch *ch = from_timer(ch, t, ftimer); + + pr_debug("%s: ftimer flags %lx\n", ch->is->name, ch->bch.Flags); + test_and_clear_bit(FLG_FTI_RUN, &ch->bch.Flags); + if (test_and_clear_bit(FLG_LL_CONN, &ch->bch.Flags)) + deliver_status(ch, HW_MOD_CONNECT); +} + +static void +setup_pump(struct isar_ch *ch) { + u8 dps = SET_DPS(ch->dpath); + u8 ctrl, param[6]; + + switch (ch->bch.state) { + case ISDN_P_NONE: + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL); + break; + case ISDN_P_B_L2DTMF: + if (test_bit(FLG_DTMFSEND, &ch->bch.Flags)) { + param[0] = 5; /* TOA 5 db */ + send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, + PMOD_DTMF_TRANS, 1, param); + } else { + param[0] = 40; /* REL -46 dbm */ + send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, + PMOD_DTMF, 1, param); + } + /* fall through */ + case ISDN_P_B_MODEM_ASYNC: + ctrl = PMOD_DATAMODEM; + if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) { + ctrl |= PCTRL_ORIG; + param[5] = PV32P6_CTN; + } else { + param[5] = PV32P6_ATN; + } + param[0] = 6; /* 6 db */ + param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B | + PV32P2_V22C | PV32P2_V21 | PV32P2_BEL; + param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B; + param[3] = PV32P4_UT144; + param[4] = PV32P5_UT144; + send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param); + break; + case ISDN_P_B_T30_FAX: + ctrl = PMOD_FAX; + if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) { + ctrl |= PCTRL_ORIG; + param[1] = PFAXP2_CTN; + } else { + param[1] = PFAXP2_ATN; + } + param[0] = 6; /* 6 db */ + send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param); + ch->state = STFAX_NULL; + ch->newcmd = 0; + ch->newmod = 0; + test_and_set_bit(FLG_FTI_RUN, &ch->bch.Flags); + break; + } + udelay(1000); + send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + udelay(1000); +} + +static void +setup_sart(struct isar_ch *ch) { + u8 dps = SET_DPS(ch->dpath); + u8 ctrl, param[2] = {0, 0}; + + switch (ch->bch.state) { + case ISDN_P_NONE: + send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, + 0, NULL); + break; + case ISDN_P_B_RAW: + case ISDN_P_B_L2DTMF: + send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, + 2, param); + break; + case ISDN_P_B_HDLC: + case ISDN_P_B_T30_FAX: + send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, + 1, param); + break; + case ISDN_P_B_MODEM_ASYNC: + ctrl = SMODE_V14 | SCTRL_HDMC_BOTH; + param[0] = S_P1_CHS_8; + param[1] = S_P2_BFT_DEF; + send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, ctrl, 2, param); + break; + } + udelay(1000); + send_mbox(ch->is, dps | ISAR_HIS_BSTREQ, 0, 0, NULL); + udelay(1000); +} + +static void +setup_iom2(struct isar_ch *ch) { + u8 dps = SET_DPS(ch->dpath); + u8 cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD, 0, 0, 0, 0}; + + if (ch->bch.nr == 2) { + msg[1] = 1; + msg[3] = 1; + } + switch (ch->bch.state) { + case ISDN_P_NONE: + cmsb = 0; + /* dummy slot */ + msg[1] = ch->dpath + 2; + msg[3] = ch->dpath + 2; + break; + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + break; + case ISDN_P_B_MODEM_ASYNC: + case ISDN_P_B_T30_FAX: + cmsb |= IOM_CTRL_RCV; + /* fall through */ + case ISDN_P_B_L2DTMF: + if (test_bit(FLG_DTMFSEND, &ch->bch.Flags)) + cmsb |= IOM_CTRL_RCV; + cmsb |= IOM_CTRL_ALAW; + break; + } + send_mbox(ch->is, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg); + udelay(1000); + send_mbox(ch->is, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL); + udelay(1000); +} + +static int +modeisar(struct isar_ch *ch, u32 bprotocol) +{ + /* Here we are selecting the best datapath for requested protocol */ + if (ch->bch.state == ISDN_P_NONE) { /* New Setup */ + switch (bprotocol) { + case ISDN_P_NONE: /* init */ + if (!ch->dpath) + /* no init for dpath 0 */ + return 0; + test_and_clear_bit(FLG_HDLC, &ch->bch.Flags); + test_and_clear_bit(FLG_TRANSPARENT, &ch->bch.Flags); + break; + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + /* best is datapath 2 */ + if (!test_and_set_bit(ISAR_DP2_USE, &ch->is->Flags)) + ch->dpath = 2; + else if (!test_and_set_bit(ISAR_DP1_USE, + &ch->is->Flags)) + ch->dpath = 1; + else { + pr_info("modeisar both paths in use\n"); + return -EBUSY; + } + if (bprotocol == ISDN_P_B_HDLC) + test_and_set_bit(FLG_HDLC, &ch->bch.Flags); + else + test_and_set_bit(FLG_TRANSPARENT, + &ch->bch.Flags); + break; + case ISDN_P_B_MODEM_ASYNC: + case ISDN_P_B_T30_FAX: + case ISDN_P_B_L2DTMF: + /* only datapath 1 */ + if (!test_and_set_bit(ISAR_DP1_USE, &ch->is->Flags)) + ch->dpath = 1; + else { + pr_info("%s: ISAR modeisar analog functions" + "only with DP1\n", ch->is->name); + return -EBUSY; + } + break; + default: + pr_info("%s: protocol not known %x\n", ch->is->name, + bprotocol); + return -ENOPROTOOPT; + } + } + pr_debug("%s: ISAR ch%d dp%d protocol %x->%x\n", ch->is->name, + ch->bch.nr, ch->dpath, ch->bch.state, bprotocol); + ch->bch.state = bprotocol; + setup_pump(ch); + setup_iom2(ch); + setup_sart(ch); + if (ch->bch.state == ISDN_P_NONE) { + /* Clear resources */ + if (ch->dpath == 1) + test_and_clear_bit(ISAR_DP1_USE, &ch->is->Flags); + else if (ch->dpath == 2) + test_and_clear_bit(ISAR_DP2_USE, &ch->is->Flags); + ch->dpath = 0; + ch->is->ctrl(ch->is->hw, HW_DEACT_IND, ch->bch.nr); + } else + ch->is->ctrl(ch->is->hw, HW_ACTIVATE_IND, ch->bch.nr); + return 0; +} + +static void +isar_pump_cmd(struct isar_ch *ch, u32 cmd, u8 para) +{ + u8 dps = SET_DPS(ch->dpath); + u8 ctrl = 0, nom = 0, p1 = 0; + + pr_debug("%s: isar_pump_cmd %x/%x state(%x)\n", + ch->is->name, cmd, para, ch->bch.state); + switch (cmd) { + case HW_MOD_FTM: + if (ch->state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FTM; + nom = 1; + ch->state = STFAX_LINE; + ch->cmd = ctrl; + ch->mod = para; + ch->newmod = 0; + ch->newcmd = 0; + ch->try_mod = 3; + } else if ((ch->state == STFAX_ACTIV) && + (ch->cmd == PCTRL_CMD_FTM) && (ch->mod == para)) + deliver_status(ch, HW_MOD_CONNECT); + else { + ch->newmod = para; + ch->newcmd = PCTRL_CMD_FTM; + nom = 0; + ctrl = PCTRL_CMD_ESC; + ch->state = STFAX_ESCAPE; + } + break; + case HW_MOD_FTH: + if (ch->state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FTH; + nom = 1; + ch->state = STFAX_LINE; + ch->cmd = ctrl; + ch->mod = para; + ch->newmod = 0; + ch->newcmd = 0; + ch->try_mod = 3; + } else if ((ch->state == STFAX_ACTIV) && + (ch->cmd == PCTRL_CMD_FTH) && (ch->mod == para)) + deliver_status(ch, HW_MOD_CONNECT); + else { + ch->newmod = para; + ch->newcmd = PCTRL_CMD_FTH; + nom = 0; + ctrl = PCTRL_CMD_ESC; + ch->state = STFAX_ESCAPE; + } + break; + case HW_MOD_FRM: + if (ch->state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FRM; + nom = 1; + ch->state = STFAX_LINE; + ch->cmd = ctrl; + ch->mod = para; + ch->newmod = 0; + ch->newcmd = 0; + ch->try_mod = 3; + } else if ((ch->state == STFAX_ACTIV) && + (ch->cmd == PCTRL_CMD_FRM) && (ch->mod == para)) + deliver_status(ch, HW_MOD_CONNECT); + else { + ch->newmod = para; + ch->newcmd = PCTRL_CMD_FRM; + nom = 0; + ctrl = PCTRL_CMD_ESC; + ch->state = STFAX_ESCAPE; + } + break; + case HW_MOD_FRH: + if (ch->state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FRH; + nom = 1; + ch->state = STFAX_LINE; + ch->cmd = ctrl; + ch->mod = para; + ch->newmod = 0; + ch->newcmd = 0; + ch->try_mod = 3; + } else if ((ch->state == STFAX_ACTIV) && + (ch->cmd == PCTRL_CMD_FRH) && (ch->mod == para)) + deliver_status(ch, HW_MOD_CONNECT); + else { + ch->newmod = para; + ch->newcmd = PCTRL_CMD_FRH; + nom = 0; + ctrl = PCTRL_CMD_ESC; + ch->state = STFAX_ESCAPE; + } + break; + case PCTRL_CMD_TDTMF: + p1 = para; + nom = 1; + ctrl = PCTRL_CMD_TDTMF; + break; + } + if (ctrl) + send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1); +} + +static void +isar_setup(struct isar_hw *isar) +{ + u8 msg; + int i; + + /* Dpath 1, 2 */ + msg = 61; + for (i = 0; i < 2; i++) { + /* Buffer Config */ + send_mbox(isar, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) | + ISAR_HIS_P12CFG, 4, 1, &msg); + isar->ch[i].mml = msg; + isar->ch[i].bch.state = 0; + isar->ch[i].dpath = i + 1; + modeisar(&isar->ch[i], ISDN_P_NONE); + } +} + +static int +isar_l2l1(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct isar_ch *ich = container_of(bch, struct isar_ch, bch); + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + u32 id, *val; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(ich->is->hwlock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + ret = 0; + isar_fill_fifo(ich); + } + spin_unlock_irqrestore(ich->is->hwlock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(ich->is->hwlock, flags); + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) + ret = modeisar(ich, ch->protocol); + else + ret = 0; + spin_unlock_irqrestore(ich->is->hwlock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + spin_lock_irqsave(ich->is->hwlock, flags); + mISDN_clear_bchannel(bch); + modeisar(ich, ISDN_P_NONE); + spin_unlock_irqrestore(ich->is->hwlock, flags); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + ret = 0; + break; + case PH_CONTROL_REQ: + val = (u32 *)skb->data; + pr_debug("%s: PH_CONTROL | REQUEST %x/%x\n", ich->is->name, + hh->id, *val); + if ((hh->id == 0) && ((*val & ~DTMF_TONE_MASK) == + DTMF_TONE_VAL)) { + if (bch->state == ISDN_P_B_L2DTMF) { + char tt = *val & DTMF_TONE_MASK; + + if (tt == '*') + tt = 0x1e; + else if (tt == '#') + tt = 0x1f; + else if (tt > '9') + tt -= 7; + tt &= 0x1f; + spin_lock_irqsave(ich->is->hwlock, flags); + isar_pump_cmd(ich, PCTRL_CMD_TDTMF, tt); + spin_unlock_irqrestore(ich->is->hwlock, flags); + } else { + pr_info("%s: DTMF send wrong protocol %x\n", + __func__, bch->state); + return -EINVAL; + } + } else if ((hh->id == HW_MOD_FRM) || (hh->id == HW_MOD_FRH) || + (hh->id == HW_MOD_FTM) || (hh->id == HW_MOD_FTH)) { + for (id = 0; id < FAXMODCNT; id++) + if (faxmodulation[id] == *val) + break; + if ((FAXMODCNT > id) && + test_bit(FLG_INITIALIZED, &bch->Flags)) { + pr_debug("%s: isar: new mod\n", ich->is->name); + isar_pump_cmd(ich, hh->id, *val); + ret = 0; + } else { + pr_info("%s: wrong modulation\n", + ich->is->name); + ret = -EINVAL; + } + } else if (hh->id == HW_MOD_LASTDATA) + test_and_set_bit(FLG_DLEETX, &bch->Flags); + else { + pr_info("%s: unknown PH_CONTROL_REQ %x\n", + ich->is->name, hh->id); + ret = -EINVAL; + } + /* fall through */ + default: + pr_info("%s: %s unknown prim(%x,%x)\n", + ich->is->name, __func__, hh->prim, hh->id); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + return mISDN_ctrl_bchannel(bch, cq); +} + +static int +isar_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct isar_ch *ich = container_of(bch, struct isar_ch, bch); + int ret = -EINVAL; + u_long flags; + + pr_debug("%s: %s cmd:%x %p\n", ich->is->name, __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + cancel_work_sync(&bch->workq); + spin_lock_irqsave(ich->is->hwlock, flags); + mISDN_clear_bchannel(bch); + modeisar(ich, ISDN_P_NONE); + spin_unlock_irqrestore(ich->is->hwlock, flags); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(ich->is->owner); + ret = 0; + break; + case CONTROL_CHANNEL: + ret = channel_bctrl(bch, arg); + break; + default: + pr_info("%s: %s unknown prim(%x)\n", + ich->is->name, __func__, cmd); + } + return ret; +} + +static void +free_isar(struct isar_hw *isar) +{ + modeisar(&isar->ch[0], ISDN_P_NONE); + modeisar(&isar->ch[1], ISDN_P_NONE); + del_timer(&isar->ch[0].ftimer); + del_timer(&isar->ch[1].ftimer); + test_and_clear_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags); + test_and_clear_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags); +} + +static int +init_isar(struct isar_hw *isar) +{ + int cnt = 3; + + while (cnt--) { + isar->version = ISARVersion(isar); + if (isar->ch[0].bch.debug & DEBUG_HW) + pr_notice("%s: Testing version %d (%d time)\n", + isar->name, isar->version, 3 - cnt); + if (isar->version == 1) + break; + isar->ctrl(isar->hw, HW_RESET_REQ, 0); + } + if (isar->version != 1) + return -EINVAL; + timer_setup(&isar->ch[0].ftimer, ftimer_handler, 0); + test_and_set_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags); + timer_setup(&isar->ch[1].ftimer, ftimer_handler, 0); + test_and_set_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags); + return 0; +} + +static int +isar_open(struct isar_hw *isar, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel == 0 || rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + bch = &isar->ch[rq->adr.channel - 1].bch; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; + return 0; +} + +u32 +mISDNisar_init(struct isar_hw *isar, void *hw) +{ + u32 ret, i; + + isar->hw = hw; + for (i = 0; i < 2; i++) { + isar->ch[i].bch.nr = i + 1; + mISDN_initbchannel(&isar->ch[i].bch, MAX_DATA_MEM, 32); + isar->ch[i].bch.ch.nr = i + 1; + isar->ch[i].bch.ch.send = &isar_l2l1; + isar->ch[i].bch.ch.ctrl = isar_bctrl; + isar->ch[i].bch.hw = hw; + isar->ch[i].is = isar; + } + + isar->init = &init_isar; + isar->release = &free_isar; + isar->firmware = &load_firmware; + isar->open = &isar_open; + + ret = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_L2DTMF & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_MODEM_ASYNC & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_T30_FAX & ISDN_P_B_MASK)); + + return ret; +} +EXPORT_SYMBOL(mISDNisar_init); + +static int __init isar_mod_init(void) +{ + pr_notice("mISDN: ISAR driver Rev. %s\n", ISAR_REV); + return 0; +} + +static void __exit isar_mod_cleanup(void) +{ + pr_notice("mISDN: ISAR module unloaded\n"); +} +module_init(isar_mod_init); +module_exit(isar_mod_cleanup); diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c new file mode 100644 index 000000000..4a342daac --- /dev/null +++ b/drivers/isdn/hardware/mISDN/netjet.c @@ -0,0 +1,1168 @@ +/* + * NETJet mISDN driver + * + * Author Karsten Keil <keil@isdn4linux.de> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> +#include "ipac.h" +#include "iohelper.h" +#include "netjet.h" +#include <linux/isdn/hdlc.h> + +#define NETJET_REV "2.0" + +enum nj_types { + NETJET_S_TJ300, + NETJET_S_TJ320, + ENTERNOW__TJ320, +}; + +struct tiger_dma { + size_t size; + u32 *start; + int idx; + u32 dmastart; + u32 dmairq; + u32 dmaend; + u32 dmacur; +}; + +struct tiger_hw; + +struct tiger_ch { + struct bchannel bch; + struct tiger_hw *nj; + int idx; + int free; + int lastrx; + u16 rxstate; + u16 txstate; + struct isdnhdlc_vars hsend; + struct isdnhdlc_vars hrecv; + u8 *hsbuf; + u8 *hrbuf; +}; + +#define TX_INIT 0x0001 +#define TX_IDLE 0x0002 +#define TX_RUN 0x0004 +#define TX_UNDERRUN 0x0100 +#define RX_OVERRUN 0x0100 + +#define LOG_SIZE 64 + +struct tiger_hw { + struct list_head list; + struct pci_dev *pdev; + char name[MISDN_MAX_IDLEN]; + enum nj_types typ; + int irq; + u32 irqcnt; + u32 base; + size_t base_s; + dma_addr_t dma; + void *dma_p; + spinlock_t lock; /* lock HW */ + struct isac_hw isac; + struct tiger_dma send; + struct tiger_dma recv; + struct tiger_ch bc[2]; + u8 ctrlreg; + u8 dmactrl; + u8 auxd; + u8 last_is0; + u8 irqmask0; + char log[LOG_SIZE]; +}; + +static LIST_HEAD(Cards); +static DEFINE_RWLOCK(card_lock); /* protect Cards */ +static u32 debug; +static int nj_cnt; + +static void +_set_debug(struct tiger_hw *card) +{ + card->isac.dch.debug = debug; + card->bc[0].bch.debug = debug; + card->bc[1].bch.debug = debug; +} + +static int +set_debug(const char *val, const struct kernel_param *kp) +{ + int ret; + struct tiger_hw *card; + + ret = param_set_uint(val, kp); + if (!ret) { + read_lock(&card_lock); + list_for_each_entry(card, &Cards, list) + _set_debug(card); + read_unlock(&card_lock); + } + return ret; +} + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(NETJET_REV); +module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Netjet debug mask"); + +static void +nj_disable_hwirq(struct tiger_hw *card) +{ + outb(0, card->base + NJ_IRQMASK0); + outb(0, card->base + NJ_IRQMASK1); +} + + +static u8 +ReadISAC_nj(void *p, u8 offset) +{ + struct tiger_hw *card = p; + u8 ret; + + card->auxd &= 0xfc; + card->auxd |= (offset >> 4) & 3; + outb(card->auxd, card->base + NJ_AUXDATA); + ret = inb(card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2)); + return ret; +} + +static void +WriteISAC_nj(void *p, u8 offset, u8 value) +{ + struct tiger_hw *card = p; + + card->auxd &= 0xfc; + card->auxd |= (offset >> 4) & 3; + outb(card->auxd, card->base + NJ_AUXDATA); + outb(value, card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2)); +} + +static void +ReadFiFoISAC_nj(void *p, u8 offset, u8 *data, int size) +{ + struct tiger_hw *card = p; + + card->auxd &= 0xfc; + outb(card->auxd, card->base + NJ_AUXDATA); + insb(card->base + NJ_ISAC_OFF, data, size); +} + +static void +WriteFiFoISAC_nj(void *p, u8 offset, u8 *data, int size) +{ + struct tiger_hw *card = p; + + card->auxd &= 0xfc; + outb(card->auxd, card->base + NJ_AUXDATA); + outsb(card->base + NJ_ISAC_OFF, data, size); +} + +static void +fill_mem(struct tiger_ch *bc, u32 idx, u32 cnt, u32 fill) +{ + struct tiger_hw *card = bc->bch.hw; + u32 mask = 0xff, val; + + pr_debug("%s: B%1d fill %02x len %d idx %d/%d\n", card->name, + bc->bch.nr, fill, cnt, idx, card->send.idx); + if (bc->bch.nr & 2) { + fill <<= 8; + mask <<= 8; + } + mask ^= 0xffffffff; + while (cnt--) { + val = card->send.start[idx]; + val &= mask; + val |= fill; + card->send.start[idx++] = val; + if (idx >= card->send.size) + idx = 0; + } +} + +static int +mode_tiger(struct tiger_ch *bc, u32 protocol) +{ + struct tiger_hw *card = bc->bch.hw; + + pr_debug("%s: B%1d protocol %x-->%x\n", card->name, + bc->bch.nr, bc->bch.state, protocol); + switch (protocol) { + case ISDN_P_NONE: + if (bc->bch.state == ISDN_P_NONE) + break; + fill_mem(bc, 0, card->send.size, 0xff); + bc->bch.state = protocol; + /* only stop dma and interrupts if both channels NULL */ + if ((card->bc[0].bch.state == ISDN_P_NONE) && + (card->bc[1].bch.state == ISDN_P_NONE)) { + card->dmactrl = 0; + outb(card->dmactrl, card->base + NJ_DMACTRL); + outb(0, card->base + NJ_IRQMASK0); + } + test_and_clear_bit(FLG_HDLC, &bc->bch.Flags); + test_and_clear_bit(FLG_TRANSPARENT, &bc->bch.Flags); + bc->txstate = 0; + bc->rxstate = 0; + bc->lastrx = -1; + break; + case ISDN_P_B_RAW: + test_and_set_bit(FLG_TRANSPARENT, &bc->bch.Flags); + bc->bch.state = protocol; + bc->idx = 0; + bc->free = card->send.size / 2; + bc->rxstate = 0; + bc->txstate = TX_INIT | TX_IDLE; + bc->lastrx = -1; + if (!card->dmactrl) { + card->dmactrl = 1; + outb(card->dmactrl, card->base + NJ_DMACTRL); + outb(0x0f, card->base + NJ_IRQMASK0); + } + break; + case ISDN_P_B_HDLC: + test_and_set_bit(FLG_HDLC, &bc->bch.Flags); + bc->bch.state = protocol; + bc->idx = 0; + bc->free = card->send.size / 2; + bc->rxstate = 0; + bc->txstate = TX_INIT | TX_IDLE; + isdnhdlc_rcv_init(&bc->hrecv, 0); + isdnhdlc_out_init(&bc->hsend, 0); + bc->lastrx = -1; + if (!card->dmactrl) { + card->dmactrl = 1; + outb(card->dmactrl, card->base + NJ_DMACTRL); + outb(0x0f, card->base + NJ_IRQMASK0); + } + break; + default: + pr_info("%s: %s protocol %x not handled\n", card->name, + __func__, protocol); + return -ENOPROTOOPT; + } + card->send.dmacur = inl(card->base + NJ_DMA_READ_ADR); + card->recv.dmacur = inl(card->base + NJ_DMA_WRITE_ADR); + card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2; + card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2; + pr_debug("%s: %s ctrl %x irq %02x/%02x idx %d/%d\n", + card->name, __func__, + inb(card->base + NJ_DMACTRL), + inb(card->base + NJ_IRQMASK0), + inb(card->base + NJ_IRQSTAT0), + card->send.idx, + card->recv.idx); + return 0; +} + +static void +nj_reset(struct tiger_hw *card) +{ + outb(0xff, card->base + NJ_CTRL); /* Reset On */ + mdelay(1); + + /* now edge triggered for TJ320 GE 13/07/00 */ + /* see comment in IRQ function */ + if (card->typ == NETJET_S_TJ320) /* TJ320 */ + card->ctrlreg = 0x40; /* Reset Off and status read clear */ + else + card->ctrlreg = 0x00; /* Reset Off and status read clear */ + outb(card->ctrlreg, card->base + NJ_CTRL); + mdelay(10); + + /* configure AUX pins (all output except ISAC IRQ pin) */ + card->auxd = 0; + card->dmactrl = 0; + outb(~NJ_ISACIRQ, card->base + NJ_AUXCTRL); + outb(NJ_ISACIRQ, card->base + NJ_IRQMASK1); + outb(card->auxd, card->base + NJ_AUXDATA); +} + +static int +inittiger(struct tiger_hw *card) +{ + int i; + + card->dma_p = pci_alloc_consistent(card->pdev, NJ_DMA_SIZE, + &card->dma); + if (!card->dma_p) { + pr_info("%s: No DMA memory\n", card->name); + return -ENOMEM; + } + if ((u64)card->dma > 0xffffffff) { + pr_info("%s: DMA outside 32 bit\n", card->name); + return -ENOMEM; + } + for (i = 0; i < 2; i++) { + card->bc[i].hsbuf = kmalloc(NJ_DMA_TXSIZE, GFP_ATOMIC); + if (!card->bc[i].hsbuf) { + pr_info("%s: no B%d send buffer\n", card->name, i + 1); + return -ENOMEM; + } + card->bc[i].hrbuf = kmalloc(NJ_DMA_RXSIZE, GFP_ATOMIC); + if (!card->bc[i].hrbuf) { + pr_info("%s: no B%d recv buffer\n", card->name, i + 1); + return -ENOMEM; + } + } + memset(card->dma_p, 0xff, NJ_DMA_SIZE); + + card->send.start = card->dma_p; + card->send.dmastart = (u32)card->dma; + card->send.dmaend = card->send.dmastart + + (4 * (NJ_DMA_TXSIZE - 1)); + card->send.dmairq = card->send.dmastart + + (4 * ((NJ_DMA_TXSIZE / 2) - 1)); + card->send.size = NJ_DMA_TXSIZE; + + if (debug & DEBUG_HW) + pr_notice("%s: send buffer phy %#x - %#x - %#x virt %p" + " size %zu u32\n", card->name, + card->send.dmastart, card->send.dmairq, + card->send.dmaend, card->send.start, card->send.size); + + outl(card->send.dmastart, card->base + NJ_DMA_READ_START); + outl(card->send.dmairq, card->base + NJ_DMA_READ_IRQ); + outl(card->send.dmaend, card->base + NJ_DMA_READ_END); + + card->recv.start = card->dma_p + (NJ_DMA_SIZE / 2); + card->recv.dmastart = (u32)card->dma + (NJ_DMA_SIZE / 2); + card->recv.dmaend = card->recv.dmastart + + (4 * (NJ_DMA_RXSIZE - 1)); + card->recv.dmairq = card->recv.dmastart + + (4 * ((NJ_DMA_RXSIZE / 2) - 1)); + card->recv.size = NJ_DMA_RXSIZE; + + if (debug & DEBUG_HW) + pr_notice("%s: recv buffer phy %#x - %#x - %#x virt %p" + " size %zu u32\n", card->name, + card->recv.dmastart, card->recv.dmairq, + card->recv.dmaend, card->recv.start, card->recv.size); + + outl(card->recv.dmastart, card->base + NJ_DMA_WRITE_START); + outl(card->recv.dmairq, card->base + NJ_DMA_WRITE_IRQ); + outl(card->recv.dmaend, card->base + NJ_DMA_WRITE_END); + return 0; +} + +static void +read_dma(struct tiger_ch *bc, u32 idx, int cnt) +{ + struct tiger_hw *card = bc->bch.hw; + int i, stat; + u32 val; + u8 *p, *pn; + + if (bc->lastrx == idx) { + bc->rxstate |= RX_OVERRUN; + pr_info("%s: B%1d overrun at idx %d\n", card->name, + bc->bch.nr, idx); + } + bc->lastrx = idx; + if (test_bit(FLG_RX_OFF, &bc->bch.Flags)) { + bc->bch.dropcnt += cnt; + return; + } + stat = bchannel_get_rxbuf(&bc->bch, cnt); + /* only transparent use the count here, HDLC overun is detected later */ + if (stat == -ENOMEM) { + pr_warning("%s.B%d: No memory for %d bytes\n", + card->name, bc->bch.nr, cnt); + return; + } + if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) + p = skb_put(bc->bch.rx_skb, cnt); + else + p = bc->hrbuf; + + for (i = 0; i < cnt; i++) { + val = card->recv.start[idx++]; + if (bc->bch.nr & 2) + val >>= 8; + if (idx >= card->recv.size) + idx = 0; + p[i] = val & 0xff; + } + + if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) { + recv_Bchannel(&bc->bch, 0, false); + return; + } + + pn = bc->hrbuf; + while (cnt > 0) { + stat = isdnhdlc_decode(&bc->hrecv, pn, cnt, &i, + bc->bch.rx_skb->data, bc->bch.maxlen); + if (stat > 0) { /* valid frame received */ + p = skb_put(bc->bch.rx_skb, stat); + if (debug & DEBUG_HW_BFIFO) { + snprintf(card->log, LOG_SIZE, + "B%1d-recv %s %d ", bc->bch.nr, + card->name, stat); + print_hex_dump_bytes(card->log, + DUMP_PREFIX_OFFSET, p, + stat); + } + recv_Bchannel(&bc->bch, 0, false); + stat = bchannel_get_rxbuf(&bc->bch, bc->bch.maxlen); + if (stat < 0) { + pr_warning("%s.B%d: No memory for %d bytes\n", + card->name, bc->bch.nr, cnt); + return; + } + } else if (stat == -HDLC_CRC_ERROR) { + pr_info("%s: B%1d receive frame CRC error\n", + card->name, bc->bch.nr); + } else if (stat == -HDLC_FRAMING_ERROR) { + pr_info("%s: B%1d receive framing error\n", + card->name, bc->bch.nr); + } else if (stat == -HDLC_LENGTH_ERROR) { + pr_info("%s: B%1d receive frame too long (> %d)\n", + card->name, bc->bch.nr, bc->bch.maxlen); + } + pn += i; + cnt -= i; + } +} + +static void +recv_tiger(struct tiger_hw *card, u8 irq_stat) +{ + u32 idx; + int cnt = card->recv.size / 2; + + /* Note receive is via the WRITE DMA channel */ + card->last_is0 &= ~NJ_IRQM0_WR_MASK; + card->last_is0 |= (irq_stat & NJ_IRQM0_WR_MASK); + + if (irq_stat & NJ_IRQM0_WR_END) + idx = cnt - 1; + else + idx = card->recv.size - 1; + + if (test_bit(FLG_ACTIVE, &card->bc[0].bch.Flags)) + read_dma(&card->bc[0], idx, cnt); + if (test_bit(FLG_ACTIVE, &card->bc[1].bch.Flags)) + read_dma(&card->bc[1], idx, cnt); +} + +/* sync with current DMA address at start or after exception */ +static void +resync(struct tiger_ch *bc, struct tiger_hw *card) +{ + card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR); + card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2; + if (bc->free > card->send.size / 2) + bc->free = card->send.size / 2; + /* currently we simple sync to the next complete free area + * this hast the advantage that we have always maximum time to + * handle TX irq + */ + if (card->send.idx < ((card->send.size / 2) - 1)) + bc->idx = (card->recv.size / 2) - 1; + else + bc->idx = card->recv.size - 1; + bc->txstate = TX_RUN; + pr_debug("%s: %s B%1d free %d idx %d/%d\n", card->name, + __func__, bc->bch.nr, bc->free, bc->idx, card->send.idx); +} + +static int bc_next_frame(struct tiger_ch *); + +static void +fill_hdlc_flag(struct tiger_ch *bc) +{ + struct tiger_hw *card = bc->bch.hw; + int count, i; + u32 m, v; + u8 *p; + + if (bc->free == 0) + return; + pr_debug("%s: %s B%1d %d state %x idx %d/%d\n", card->name, + __func__, bc->bch.nr, bc->free, bc->txstate, + bc->idx, card->send.idx); + if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN)) + resync(bc, card); + count = isdnhdlc_encode(&bc->hsend, NULL, 0, &i, + bc->hsbuf, bc->free); + pr_debug("%s: B%1d hdlc encoded %d flags\n", card->name, + bc->bch.nr, count); + bc->free -= count; + p = bc->hsbuf; + m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff; + for (i = 0; i < count; i++) { + if (bc->idx >= card->send.size) + bc->idx = 0; + v = card->send.start[bc->idx]; + v &= m; + v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8; + card->send.start[bc->idx++] = v; + } + if (debug & DEBUG_HW_BFIFO) { + snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ", + bc->bch.nr, card->name, count); + print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count); + } +} + +static void +fill_dma(struct tiger_ch *bc) +{ + struct tiger_hw *card = bc->bch.hw; + int count, i, fillempty = 0; + u32 m, v, n = 0; + u8 *p; + + if (bc->free == 0) + return; + if (!bc->bch.tx_skb) { + if (!test_bit(FLG_TX_EMPTY, &bc->bch.Flags)) + return; + fillempty = 1; + count = card->send.size >> 1; + p = bc->bch.fill; + } else { + count = bc->bch.tx_skb->len - bc->bch.tx_idx; + if (count <= 0) + return; + pr_debug("%s: %s B%1d %d/%d/%d/%d state %x idx %d/%d\n", + card->name, __func__, bc->bch.nr, count, bc->free, + bc->bch.tx_idx, bc->bch.tx_skb->len, bc->txstate, + bc->idx, card->send.idx); + p = bc->bch.tx_skb->data + bc->bch.tx_idx; + } + if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN)) + resync(bc, card); + if (test_bit(FLG_HDLC, &bc->bch.Flags) && !fillempty) { + count = isdnhdlc_encode(&bc->hsend, p, count, &i, + bc->hsbuf, bc->free); + pr_debug("%s: B%1d hdlc encoded %d in %d\n", card->name, + bc->bch.nr, i, count); + bc->bch.tx_idx += i; + bc->free -= count; + p = bc->hsbuf; + } else { + if (count > bc->free) + count = bc->free; + if (!fillempty) + bc->bch.tx_idx += count; + bc->free -= count; + } + m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff; + if (fillempty) { + n = p[0]; + if (!(bc->bch.nr & 1)) + n <<= 8; + for (i = 0; i < count; i++) { + if (bc->idx >= card->send.size) + bc->idx = 0; + v = card->send.start[bc->idx]; + v &= m; + v |= n; + card->send.start[bc->idx++] = v; + } + } else { + for (i = 0; i < count; i++) { + if (bc->idx >= card->send.size) + bc->idx = 0; + v = card->send.start[bc->idx]; + v &= m; + n = p[i]; + v |= (bc->bch.nr & 1) ? n : n << 8; + card->send.start[bc->idx++] = v; + } + } + if (debug & DEBUG_HW_BFIFO) { + snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ", + bc->bch.nr, card->name, count); + print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count); + } + if (bc->free) + bc_next_frame(bc); +} + + +static int +bc_next_frame(struct tiger_ch *bc) +{ + int ret = 1; + + if (bc->bch.tx_skb && bc->bch.tx_idx < bc->bch.tx_skb->len) { + fill_dma(bc); + } else { + if (bc->bch.tx_skb) + dev_kfree_skb(bc->bch.tx_skb); + if (get_next_bframe(&bc->bch)) { + fill_dma(bc); + test_and_clear_bit(FLG_TX_EMPTY, &bc->bch.Flags); + } else if (test_bit(FLG_TX_EMPTY, &bc->bch.Flags)) { + fill_dma(bc); + } else if (test_bit(FLG_FILLEMPTY, &bc->bch.Flags)) { + test_and_set_bit(FLG_TX_EMPTY, &bc->bch.Flags); + ret = 0; + } else { + ret = 0; + } + } + return ret; +} + +static void +send_tiger_bc(struct tiger_hw *card, struct tiger_ch *bc) +{ + int ret; + + bc->free += card->send.size / 2; + if (bc->free >= card->send.size) { + if (!(bc->txstate & (TX_UNDERRUN | TX_INIT))) { + pr_info("%s: B%1d TX underrun state %x\n", card->name, + bc->bch.nr, bc->txstate); + bc->txstate |= TX_UNDERRUN; + } + bc->free = card->send.size; + } + ret = bc_next_frame(bc); + if (!ret) { + if (test_bit(FLG_HDLC, &bc->bch.Flags)) { + fill_hdlc_flag(bc); + return; + } + pr_debug("%s: B%1d TX no data free %d idx %d/%d\n", card->name, + bc->bch.nr, bc->free, bc->idx, card->send.idx); + if (!(bc->txstate & (TX_IDLE | TX_INIT))) { + fill_mem(bc, bc->idx, bc->free, 0xff); + if (bc->free == card->send.size) + bc->txstate |= TX_IDLE; + } + } +} + +static void +send_tiger(struct tiger_hw *card, u8 irq_stat) +{ + int i; + + /* Note send is via the READ DMA channel */ + if ((irq_stat & card->last_is0) & NJ_IRQM0_RD_MASK) { + pr_info("%s: tiger warn write double dma %x/%x\n", + card->name, irq_stat, card->last_is0); + return; + } else { + card->last_is0 &= ~NJ_IRQM0_RD_MASK; + card->last_is0 |= (irq_stat & NJ_IRQM0_RD_MASK); + } + for (i = 0; i < 2; i++) { + if (test_bit(FLG_ACTIVE, &card->bc[i].bch.Flags)) + send_tiger_bc(card, &card->bc[i]); + } +} + +static irqreturn_t +nj_irq(int intno, void *dev_id) +{ + struct tiger_hw *card = dev_id; + u8 val, s1val, s0val; + + spin_lock(&card->lock); + s0val = inb(card->base | NJ_IRQSTAT0); + s1val = inb(card->base | NJ_IRQSTAT1); + if ((s1val & NJ_ISACIRQ) && (s0val == 0)) { + /* shared IRQ */ + spin_unlock(&card->lock); + return IRQ_NONE; + } + pr_debug("%s: IRQSTAT0 %02x IRQSTAT1 %02x\n", card->name, s0val, s1val); + card->irqcnt++; + if (!(s1val & NJ_ISACIRQ)) { + val = ReadISAC_nj(card, ISAC_ISTA); + if (val) + mISDNisac_irq(&card->isac, val); + } + + if (s0val) + /* write to clear */ + outb(s0val, card->base | NJ_IRQSTAT0); + else + goto end; + s1val = s0val; + /* set bits in sval to indicate which page is free */ + card->recv.dmacur = inl(card->base | NJ_DMA_WRITE_ADR); + card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2; + if (card->recv.dmacur < card->recv.dmairq) + s0val = 0x08; /* the 2nd write area is free */ + else + s0val = 0x04; /* the 1st write area is free */ + + card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR); + card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2; + if (card->send.dmacur < card->send.dmairq) + s0val |= 0x02; /* the 2nd read area is free */ + else + s0val |= 0x01; /* the 1st read area is free */ + + pr_debug("%s: DMA Status %02x/%02x/%02x %d/%d\n", card->name, + s1val, s0val, card->last_is0, + card->recv.idx, card->send.idx); + /* test if we have a DMA interrupt */ + if (s0val != card->last_is0) { + if ((s0val & NJ_IRQM0_RD_MASK) != + (card->last_is0 & NJ_IRQM0_RD_MASK)) + /* got a write dma int */ + send_tiger(card, s0val); + if ((s0val & NJ_IRQM0_WR_MASK) != + (card->last_is0 & NJ_IRQM0_WR_MASK)) + /* got a read dma int */ + recv_tiger(card, s0val); + } +end: + spin_unlock(&card->lock); + return IRQ_HANDLED; +} + +static int +nj_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) +{ + int ret = -EINVAL; + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch); + struct tiger_hw *card = bch->hw; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&card->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + fill_dma(bc); + ret = 0; + } + spin_unlock_irqrestore(&card->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(&card->lock, flags); + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) + ret = mode_tiger(bc, ch->protocol); + else + ret = 0; + spin_unlock_irqrestore(&card->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + spin_lock_irqsave(&card->lock, flags); + mISDN_clear_bchannel(bch); + mode_tiger(bc, ISDN_P_NONE); + spin_unlock_irqrestore(&card->lock, flags); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +channel_bctrl(struct tiger_ch *bc, struct mISDN_ctrl_req *cq) +{ + return mISDN_ctrl_bchannel(&bc->bch, cq); +} + +static int +nj_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch); + struct tiger_hw *card = bch->hw; + int ret = -EINVAL; + u_long flags; + + pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + cancel_work_sync(&bch->workq); + spin_lock_irqsave(&card->lock, flags); + mISDN_clear_bchannel(bch); + mode_tiger(bc, ISDN_P_NONE); + spin_unlock_irqrestore(&card->lock, flags); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + ret = 0; + break; + case CONTROL_CHANNEL: + ret = channel_bctrl(bc, arg); + break; + default: + pr_info("%s: %s unknown prim(%x)\n", card->name, __func__, cmd); + } + return ret; +} + +static int +channel_ctrl(struct tiger_hw *card, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; + break; + case MISDN_CTRL_LOOP: + /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ + if (cq->channel < 0 || cq->channel > 3) { + ret = -EINVAL; + break; + } + ret = card->isac.ctrl(&card->isac, HW_TESTLOOP, cq->channel); + break; + case MISDN_CTRL_L1_TIMER3: + ret = card->isac.ctrl(&card->isac, HW_TIMER3_VALUE, cq->p1); + break; + default: + pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +open_bchannel(struct tiger_hw *card, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel == 0 || rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + bch = &card->bc[rq->adr.channel - 1].bch; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; + return 0; +} + +/* + * device control function + */ +static int +nj_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct tiger_hw *card = dch->hw; + struct channel_req *rq; + int err = 0; + + pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if (rq->protocol == ISDN_P_TE_S0) + err = card->isac.open(&card->isac, rq); + else + err = open_bchannel(card, rq); + if (err) + break; + if (!try_module_get(THIS_MODULE)) + pr_info("%s: cannot get module\n", card->name); + break; + case CLOSE_CHANNEL: + pr_debug("%s: dev(%d) close from %p\n", card->name, dch->dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(card, arg); + break; + default: + pr_debug("%s: %s unknown command %x\n", + card->name, __func__, cmd); + return -EINVAL; + } + return err; +} + +static int +nj_init_card(struct tiger_hw *card) +{ + u_long flags; + int ret; + + spin_lock_irqsave(&card->lock, flags); + nj_disable_hwirq(card); + spin_unlock_irqrestore(&card->lock, flags); + + card->irq = card->pdev->irq; + if (request_irq(card->irq, nj_irq, IRQF_SHARED, card->name, card)) { + pr_info("%s: couldn't get interrupt %d\n", + card->name, card->irq); + card->irq = -1; + return -EIO; + } + + spin_lock_irqsave(&card->lock, flags); + nj_reset(card); + ret = card->isac.init(&card->isac); + if (ret) + goto error; + ret = inittiger(card); + if (ret) + goto error; + mode_tiger(&card->bc[0], ISDN_P_NONE); + mode_tiger(&card->bc[1], ISDN_P_NONE); +error: + spin_unlock_irqrestore(&card->lock, flags); + return ret; +} + + +static void +nj_release(struct tiger_hw *card) +{ + u_long flags; + int i; + + if (card->base_s) { + spin_lock_irqsave(&card->lock, flags); + nj_disable_hwirq(card); + mode_tiger(&card->bc[0], ISDN_P_NONE); + mode_tiger(&card->bc[1], ISDN_P_NONE); + spin_unlock_irqrestore(&card->lock, flags); + card->isac.release(&card->isac); + release_region(card->base, card->base_s); + card->base_s = 0; + } + if (card->irq > 0) + free_irq(card->irq, card); + if (card->isac.dch.dev.dev.class) + mISDN_unregister_device(&card->isac.dch.dev); + + for (i = 0; i < 2; i++) { + mISDN_freebchannel(&card->bc[i].bch); + kfree(card->bc[i].hsbuf); + kfree(card->bc[i].hrbuf); + } + if (card->dma_p) + pci_free_consistent(card->pdev, NJ_DMA_SIZE, + card->dma_p, card->dma); + write_lock_irqsave(&card_lock, flags); + list_del(&card->list); + write_unlock_irqrestore(&card_lock, flags); + pci_clear_master(card->pdev); + pci_disable_device(card->pdev); + pci_set_drvdata(card->pdev, NULL); + kfree(card); +} + + +static int +nj_setup(struct tiger_hw *card) +{ + card->base = pci_resource_start(card->pdev, 0); + card->base_s = pci_resource_len(card->pdev, 0); + if (!request_region(card->base, card->base_s, card->name)) { + pr_info("%s: NETjet config port %#x-%#x already in use\n", + card->name, card->base, + (u32)(card->base + card->base_s - 1)); + card->base_s = 0; + return -EIO; + } + ASSIGN_FUNC(nj, ISAC, card->isac); + return 0; +} + + +static int +setup_instance(struct tiger_hw *card) +{ + int i, err; + u_long flags; + + snprintf(card->name, MISDN_MAX_IDLEN - 1, "netjet.%d", nj_cnt + 1); + write_lock_irqsave(&card_lock, flags); + list_add_tail(&card->list, &Cards); + write_unlock_irqrestore(&card_lock, flags); + + _set_debug(card); + card->isac.name = card->name; + spin_lock_init(&card->lock); + card->isac.hwlock = &card->lock; + mISDNisac_init(&card->isac, card); + + card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + card->isac.dch.dev.D.ctrl = nj_dctrl; + for (i = 0; i < 2; i++) { + card->bc[i].bch.nr = i + 1; + set_channelmap(i + 1, card->isac.dch.dev.channelmap); + mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM, + NJ_DMA_RXSIZE >> 1); + card->bc[i].bch.hw = card; + card->bc[i].bch.ch.send = nj_l2l1B; + card->bc[i].bch.ch.ctrl = nj_bctrl; + card->bc[i].bch.ch.nr = i + 1; + list_add(&card->bc[i].bch.ch.list, + &card->isac.dch.dev.bchannels); + card->bc[i].bch.hw = card; + } + err = nj_setup(card); + if (err) + goto error; + err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev, + card->name); + if (err) + goto error; + err = nj_init_card(card); + if (!err) { + nj_cnt++; + pr_notice("Netjet %d cards installed\n", nj_cnt); + return 0; + } +error: + nj_release(card); + return err; +} + +static int +nj_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + int cfg; + struct tiger_hw *card; + + if (pdev->subsystem_vendor == 0x8086 && + pdev->subsystem_device == 0x0003) { + pr_notice("Netjet: Digium X100P/X101P not handled\n"); + return -ENODEV; + } + + if (pdev->subsystem_vendor == 0x55 && + pdev->subsystem_device == 0x02) { + pr_notice("Netjet: Enter!Now not handled yet\n"); + return -ENODEV; + } + + if (pdev->subsystem_vendor == 0xb100 && + pdev->subsystem_device == 0x0003) { + pr_notice("Netjet: Digium TDM400P not handled yet\n"); + return -ENODEV; + } + + card = kzalloc(sizeof(struct tiger_hw), GFP_KERNEL); + if (!card) { + pr_info("No kmem for Netjet\n"); + return err; + } + + card->pdev = pdev; + + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return err; + } + + printk(KERN_INFO "nj_probe(mISDN): found adapter at %s\n", + pci_name(pdev)); + + pci_set_master(pdev); + + /* the TJ300 and TJ320 must be detected, the IRQ handling is different + * unfortunately the chips use the same device ID, but the TJ320 has + * the bit20 in status PCI cfg register set + */ + pci_read_config_dword(pdev, 0x04, &cfg); + if (cfg & 0x00100000) + card->typ = NETJET_S_TJ320; + else + card->typ = NETJET_S_TJ300; + + card->base = pci_resource_start(pdev, 0); + pci_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) + pci_set_drvdata(pdev, NULL); + + return err; +} + + +static void nj_remove(struct pci_dev *pdev) +{ + struct tiger_hw *card = pci_get_drvdata(pdev); + + if (card) + nj_release(card); + else + pr_info("%s drvdata already removed\n", __func__); +} + +/* We cannot select cards with PCI_SUB... IDs, since here are cards with + * SUB IDs set to PCI_ANY_ID, so we need to match all and reject + * known other cards which not work with this driver - see probe function */ +static const struct pci_device_id nj_pci_ids[] = { + { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { } +}; +MODULE_DEVICE_TABLE(pci, nj_pci_ids); + +static struct pci_driver nj_driver = { + .name = "netjet", + .probe = nj_probe, + .remove = nj_remove, + .id_table = nj_pci_ids, +}; + +static int __init nj_init(void) +{ + int err; + + pr_notice("Netjet PCI driver Rev. %s\n", NETJET_REV); + err = pci_register_driver(&nj_driver); + return err; +} + +static void __exit nj_cleanup(void) +{ + pci_unregister_driver(&nj_driver); +} + +module_init(nj_init); +module_exit(nj_cleanup); diff --git a/drivers/isdn/hardware/mISDN/netjet.h b/drivers/isdn/hardware/mISDN/netjet.h new file mode 100644 index 000000000..ddd41ef1a --- /dev/null +++ b/drivers/isdn/hardware/mISDN/netjet.h @@ -0,0 +1,57 @@ +/* + * NETjet common header file + * + * Author Karsten Keil + * based on work of Matt Henderson and Daniel Potts, + * Traverse Technologies P/L www.traverse.com.au + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#define NJ_CTRL 0x00 +#define NJ_DMACTRL 0x01 +#define NJ_AUXCTRL 0x02 +#define NJ_AUXDATA 0x03 +#define NJ_IRQMASK0 0x04 +#define NJ_IRQMASK1 0x05 +#define NJ_IRQSTAT0 0x06 +#define NJ_IRQSTAT1 0x07 +#define NJ_DMA_READ_START 0x08 +#define NJ_DMA_READ_IRQ 0x0c +#define NJ_DMA_READ_END 0x10 +#define NJ_DMA_READ_ADR 0x14 +#define NJ_DMA_WRITE_START 0x18 +#define NJ_DMA_WRITE_IRQ 0x1c +#define NJ_DMA_WRITE_END 0x20 +#define NJ_DMA_WRITE_ADR 0x24 +#define NJ_PULSE_CNT 0x28 + +#define NJ_ISAC_OFF 0xc0 +#define NJ_ISACIRQ 0x10 + +#define NJ_IRQM0_RD_MASK 0x03 +#define NJ_IRQM0_RD_IRQ 0x01 +#define NJ_IRQM0_RD_END 0x02 +#define NJ_IRQM0_WR_MASK 0x0c +#define NJ_IRQM0_WR_IRQ 0x04 +#define NJ_IRQM0_WR_END 0x08 + +/* one page here is no need to be smaller */ +#define NJ_DMA_SIZE 4096 +/* 2 * 64 byte is a compromise between IRQ count and latency */ +#define NJ_DMA_RXSIZE 128 /* 2 * 64 */ +#define NJ_DMA_TXSIZE 128 /* 2 * 64 */ diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c new file mode 100644 index 000000000..1f1446ed8 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/speedfax.c @@ -0,0 +1,532 @@ +/* + * speedfax.c low level stuff for Sedlbauer Speedfax+ cards + * based on the ISAR DSP + * Thanks to Sedlbauer AG for informations and HW + * + * Author Karsten Keil <keil@isdn4linux.de> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/firmware.h> +#include "ipac.h" +#include "isar.h" + +#define SPEEDFAX_REV "2.0" + +#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51 +#define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54 +#define PCI_SUB_ID_SEDLBAUER 0x01 + +#define SFAX_PCI_ADDR 0xc8 +#define SFAX_PCI_ISAC 0xd0 +#define SFAX_PCI_ISAR 0xe0 + +/* TIGER 100 Registers */ + +#define TIGER_RESET_ADDR 0x00 +#define TIGER_EXTERN_RESET_ON 0x01 +#define TIGER_EXTERN_RESET_OFF 0x00 +#define TIGER_AUX_CTRL 0x02 +#define TIGER_AUX_DATA 0x03 +#define TIGER_AUX_IRQMASK 0x05 +#define TIGER_AUX_STATUS 0x07 + +/* Tiger AUX BITs */ +#define SFAX_AUX_IOMASK 0xdd /* 1 and 5 are inputs */ +#define SFAX_ISAR_RESET_BIT_OFF 0x00 +#define SFAX_ISAR_RESET_BIT_ON 0x01 +#define SFAX_TIGER_IRQ_BIT 0x02 +#define SFAX_LED1_BIT 0x08 +#define SFAX_LED2_BIT 0x10 + +#define SFAX_PCI_RESET_ON (SFAX_ISAR_RESET_BIT_ON) +#define SFAX_PCI_RESET_OFF (SFAX_LED1_BIT | SFAX_LED2_BIT) + +static int sfax_cnt; +static u32 debug; +static u32 irqloops = 4; + +struct sfax_hw { + struct list_head list; + struct pci_dev *pdev; + char name[MISDN_MAX_IDLEN]; + u32 irq; + u32 irqcnt; + u32 cfg; + struct _ioport p_isac; + struct _ioport p_isar; + u8 aux_data; + spinlock_t lock; /* HW access lock */ + struct isac_hw isac; + struct isar_hw isar; +}; + +static LIST_HEAD(Cards); +static DEFINE_RWLOCK(card_lock); /* protect Cards */ + +static void +_set_debug(struct sfax_hw *card) +{ + card->isac.dch.debug = debug; + card->isar.ch[0].bch.debug = debug; + card->isar.ch[1].bch.debug = debug; +} + +static int +set_debug(const char *val, const struct kernel_param *kp) +{ + int ret; + struct sfax_hw *card; + + ret = param_set_uint(val, kp); + if (!ret) { + read_lock(&card_lock); + list_for_each_entry(card, &Cards, list) + _set_debug(card); + read_unlock(&card_lock); + } + return ret; +} + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SPEEDFAX_REV); +MODULE_FIRMWARE("isdn/ISAR.BIN"); +module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Speedfax debug mask"); +module_param(irqloops, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(irqloops, "Speedfax maximal irqloops (default 4)"); + +IOFUNC_IND(ISAC, sfax_hw, p_isac) +IOFUNC_IND(ISAR, sfax_hw, p_isar) + +static irqreturn_t +speedfax_irq(int intno, void *dev_id) +{ + struct sfax_hw *sf = dev_id; + u8 val; + int cnt = irqloops; + + spin_lock(&sf->lock); + val = inb(sf->cfg + TIGER_AUX_STATUS); + if (val & SFAX_TIGER_IRQ_BIT) { /* for us or shared ? */ + spin_unlock(&sf->lock); + return IRQ_NONE; /* shared */ + } + sf->irqcnt++; + val = ReadISAR_IND(sf, ISAR_IRQBIT); +Start_ISAR: + if (val & ISAR_IRQSTA) + mISDNisar_irq(&sf->isar); + val = ReadISAC_IND(sf, ISAC_ISTA); + if (val) + mISDNisac_irq(&sf->isac, val); + val = ReadISAR_IND(sf, ISAR_IRQBIT); + if ((val & ISAR_IRQSTA) && cnt--) + goto Start_ISAR; + if (cnt < irqloops) + pr_debug("%s: %d irqloops cpu%d\n", sf->name, + irqloops - cnt, smp_processor_id()); + if (irqloops && !cnt) + pr_notice("%s: %d IRQ LOOP cpu%d\n", sf->name, + irqloops, smp_processor_id()); + spin_unlock(&sf->lock); + return IRQ_HANDLED; +} + +static void +enable_hwirq(struct sfax_hw *sf) +{ + WriteISAC_IND(sf, ISAC_MASK, 0); + WriteISAR_IND(sf, ISAR_IRQBIT, ISAR_IRQMSK); + outb(SFAX_TIGER_IRQ_BIT, sf->cfg + TIGER_AUX_IRQMASK); +} + +static void +disable_hwirq(struct sfax_hw *sf) +{ + WriteISAC_IND(sf, ISAC_MASK, 0xFF); + WriteISAR_IND(sf, ISAR_IRQBIT, 0); + outb(0, sf->cfg + TIGER_AUX_IRQMASK); +} + +static void +reset_speedfax(struct sfax_hw *sf) +{ + + pr_debug("%s: resetting card\n", sf->name); + outb(TIGER_EXTERN_RESET_ON, sf->cfg + TIGER_RESET_ADDR); + outb(SFAX_PCI_RESET_ON, sf->cfg + TIGER_AUX_DATA); + mdelay(1); + outb(TIGER_EXTERN_RESET_OFF, sf->cfg + TIGER_RESET_ADDR); + sf->aux_data = SFAX_PCI_RESET_OFF; + outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); + mdelay(1); +} + +static int +sfax_ctrl(struct sfax_hw *sf, u32 cmd, u_long arg) +{ + int ret = 0; + + switch (cmd) { + case HW_RESET_REQ: + reset_speedfax(sf); + break; + case HW_ACTIVATE_IND: + if (arg & 1) + sf->aux_data &= ~SFAX_LED1_BIT; + if (arg & 2) + sf->aux_data &= ~SFAX_LED2_BIT; + outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); + break; + case HW_DEACT_IND: + if (arg & 1) + sf->aux_data |= SFAX_LED1_BIT; + if (arg & 2) + sf->aux_data |= SFAX_LED2_BIT; + outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); + break; + default: + pr_info("%s: %s unknown command %x %lx\n", + sf->name, __func__, cmd, arg); + ret = -EINVAL; + break; + } + return ret; +} + +static int +channel_ctrl(struct sfax_hw *sf, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; + break; + case MISDN_CTRL_LOOP: + /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ + if (cq->channel < 0 || cq->channel > 3) { + ret = -EINVAL; + break; + } + ret = sf->isac.ctrl(&sf->isac, HW_TESTLOOP, cq->channel); + break; + case MISDN_CTRL_L1_TIMER3: + ret = sf->isac.ctrl(&sf->isac, HW_TIMER3_VALUE, cq->p1); + break; + default: + pr_info("%s: unknown Op %x\n", sf->name, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +sfax_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct sfax_hw *sf = dch->hw; + struct channel_req *rq; + int err = 0; + + pr_debug("%s: cmd:%x %p\n", sf->name, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if (rq->protocol == ISDN_P_TE_S0) + err = sf->isac.open(&sf->isac, rq); + else + err = sf->isar.open(&sf->isar, rq); + if (err) + break; + if (!try_module_get(THIS_MODULE)) + pr_info("%s: cannot get module\n", sf->name); + break; + case CLOSE_CHANNEL: + pr_debug("%s: dev(%d) close from %p\n", sf->name, + dch->dev.id, __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(sf, arg); + break; + default: + pr_debug("%s: unknown command %x\n", sf->name, cmd); + return -EINVAL; + } + return err; +} + +static int +init_card(struct sfax_hw *sf) +{ + int ret, cnt = 3; + u_long flags; + + ret = request_irq(sf->irq, speedfax_irq, IRQF_SHARED, sf->name, sf); + if (ret) { + pr_info("%s: couldn't get interrupt %d\n", sf->name, sf->irq); + return ret; + } + while (cnt--) { + spin_lock_irqsave(&sf->lock, flags); + ret = sf->isac.init(&sf->isac); + if (ret) { + spin_unlock_irqrestore(&sf->lock, flags); + pr_info("%s: ISAC init failed with %d\n", + sf->name, ret); + break; + } + enable_hwirq(sf); + /* RESET Receiver and Transmitter */ + WriteISAC_IND(sf, ISAC_CMDR, 0x41); + spin_unlock_irqrestore(&sf->lock, flags); + msleep_interruptible(10); + if (debug & DEBUG_HW) + pr_notice("%s: IRQ %d count %d\n", sf->name, + sf->irq, sf->irqcnt); + if (!sf->irqcnt) { + pr_info("%s: IRQ(%d) got no requests during init %d\n", + sf->name, sf->irq, 3 - cnt); + } else + return 0; + } + free_irq(sf->irq, sf); + return -EIO; +} + + +static int +setup_speedfax(struct sfax_hw *sf) +{ + u_long flags; + + if (!request_region(sf->cfg, 256, sf->name)) { + pr_info("mISDN: %s config port %x-%x already in use\n", + sf->name, sf->cfg, sf->cfg + 255); + return -EIO; + } + outb(0xff, sf->cfg); + outb(0, sf->cfg); + outb(0xdd, sf->cfg + TIGER_AUX_CTRL); + outb(0, sf->cfg + TIGER_AUX_IRQMASK); + + sf->isac.type = IPAC_TYPE_ISAC; + sf->p_isac.ale = sf->cfg + SFAX_PCI_ADDR; + sf->p_isac.port = sf->cfg + SFAX_PCI_ISAC; + sf->p_isar.ale = sf->cfg + SFAX_PCI_ADDR; + sf->p_isar.port = sf->cfg + SFAX_PCI_ISAR; + ASSIGN_FUNC(IND, ISAC, sf->isac); + ASSIGN_FUNC(IND, ISAR, sf->isar); + spin_lock_irqsave(&sf->lock, flags); + reset_speedfax(sf); + disable_hwirq(sf); + spin_unlock_irqrestore(&sf->lock, flags); + return 0; +} + +static void +release_card(struct sfax_hw *card) { + u_long flags; + + spin_lock_irqsave(&card->lock, flags); + disable_hwirq(card); + spin_unlock_irqrestore(&card->lock, flags); + card->isac.release(&card->isac); + free_irq(card->irq, card); + card->isar.release(&card->isar); + mISDN_unregister_device(&card->isac.dch.dev); + release_region(card->cfg, 256); + pci_disable_device(card->pdev); + pci_set_drvdata(card->pdev, NULL); + write_lock_irqsave(&card_lock, flags); + list_del(&card->list); + write_unlock_irqrestore(&card_lock, flags); + kfree(card); + sfax_cnt--; +} + +static int +setup_instance(struct sfax_hw *card) +{ + const struct firmware *firmware; + int i, err; + u_long flags; + + snprintf(card->name, MISDN_MAX_IDLEN - 1, "Speedfax.%d", sfax_cnt + 1); + write_lock_irqsave(&card_lock, flags); + list_add_tail(&card->list, &Cards); + write_unlock_irqrestore(&card_lock, flags); + _set_debug(card); + spin_lock_init(&card->lock); + card->isac.hwlock = &card->lock; + card->isar.hwlock = &card->lock; + card->isar.ctrl = (void *)&sfax_ctrl; + card->isac.name = card->name; + card->isar.name = card->name; + card->isar.owner = THIS_MODULE; + + err = request_firmware(&firmware, "isdn/ISAR.BIN", &card->pdev->dev); + if (err < 0) { + pr_info("%s: firmware request failed %d\n", + card->name, err); + goto error_fw; + } + if (debug & DEBUG_HW) + pr_notice("%s: got firmware %zu bytes\n", + card->name, firmware->size); + + mISDNisac_init(&card->isac, card); + + card->isac.dch.dev.D.ctrl = sfax_dctrl; + card->isac.dch.dev.Bprotocols = + mISDNisar_init(&card->isar, card); + for (i = 0; i < 2; i++) { + set_channelmap(i + 1, card->isac.dch.dev.channelmap); + list_add(&card->isar.ch[i].bch.ch.list, + &card->isac.dch.dev.bchannels); + } + + err = setup_speedfax(card); + if (err) + goto error_setup; + err = card->isar.init(&card->isar); + if (err) + goto error; + err = mISDN_register_device(&card->isac.dch.dev, + &card->pdev->dev, card->name); + if (err) + goto error; + err = init_card(card); + if (err) + goto error_init; + err = card->isar.firmware(&card->isar, firmware->data, firmware->size); + if (!err) { + release_firmware(firmware); + sfax_cnt++; + pr_notice("SpeedFax %d cards installed\n", sfax_cnt); + return 0; + } + disable_hwirq(card); + free_irq(card->irq, card); +error_init: + mISDN_unregister_device(&card->isac.dch.dev); +error: + release_region(card->cfg, 256); +error_setup: + card->isac.release(&card->isac); + card->isar.release(&card->isar); + release_firmware(firmware); +error_fw: + pci_disable_device(card->pdev); + write_lock_irqsave(&card_lock, flags); + list_del(&card->list); + write_unlock_irqrestore(&card_lock, flags); + kfree(card); + return err; +} + +static int +sfaxpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + struct sfax_hw *card = kzalloc(sizeof(struct sfax_hw), GFP_KERNEL); + + if (!card) { + pr_info("No memory for Speedfax+ PCI\n"); + return err; + } + card->pdev = pdev; + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return err; + } + + pr_notice("mISDN: Speedfax found adapter %s at %s\n", + (char *)ent->driver_data, pci_name(pdev)); + + card->cfg = pci_resource_start(pdev, 0); + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) + pci_set_drvdata(pdev, NULL); + return err; +} + +static void +sfax_remove_pci(struct pci_dev *pdev) +{ + struct sfax_hw *card = pci_get_drvdata(pdev); + + if (card) + release_card(card); + else + pr_debug("%s: drvdata already removed\n", __func__); +} + +static struct pci_device_id sfaxpci_ids[] = { + { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, + PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER, + 0, 0, (unsigned long) "Pyramid Speedfax + PCI" + }, + { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, + PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER, + 0, 0, (unsigned long) "Sedlbauer Speedfax + PCI" + }, + { } +}; +MODULE_DEVICE_TABLE(pci, sfaxpci_ids); + +static struct pci_driver sfaxpci_driver = { + .name = "speedfax+ pci", + .probe = sfaxpci_probe, + .remove = sfax_remove_pci, + .id_table = sfaxpci_ids, +}; + +static int __init +Speedfax_init(void) +{ + int err; + + pr_notice("Sedlbauer Speedfax+ Driver Rev. %s\n", + SPEEDFAX_REV); + err = pci_register_driver(&sfaxpci_driver); + return err; +} + +static void __exit +Speedfax_cleanup(void) +{ + pci_unregister_driver(&sfaxpci_driver); +} + +module_init(Speedfax_init); +module_exit(Speedfax_cleanup); diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c new file mode 100644 index 000000000..6f60aced1 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/w6692.c @@ -0,0 +1,1432 @@ +/* + * w6692.c mISDN driver for Winbond w6692 based cards + * + * Author Karsten Keil <kkeil@suse.de> + * based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> +#include "w6692.h" + +#define W6692_REV "2.0" + +#define DBUSY_TIMER_VALUE 80 + +enum { + W6692_ASUS, + W6692_WINBOND, + W6692_USR +}; + +/* private data in the PCI devices list */ +struct w6692map { + u_int subtype; + char *name; +}; + +static const struct w6692map w6692_map[] = +{ + {W6692_ASUS, "Dynalink/AsusCom IS64PH"}, + {W6692_WINBOND, "Winbond W6692"}, + {W6692_USR, "USR W6692"} +}; + +#define PCI_DEVICE_ID_USR_6692 0x3409 + +struct w6692_ch { + struct bchannel bch; + u32 addr; + struct timer_list timer; + u8 b_mode; +}; + +struct w6692_hw { + struct list_head list; + struct pci_dev *pdev; + char name[MISDN_MAX_IDLEN]; + u32 irq; + u32 irqcnt; + u32 addr; + u32 fmask; /* feature mask - bit set per card nr */ + int subtype; + spinlock_t lock; /* hw lock */ + u8 imask; + u8 pctl; + u8 xaddr; + u8 xdata; + u8 state; + struct w6692_ch bc[2]; + struct dchannel dch; + char log[64]; +}; + +static LIST_HEAD(Cards); +static DEFINE_RWLOCK(card_lock); /* protect Cards */ + +static int w6692_cnt; +static int debug; +static u32 led; +static u32 pots; + +static void +_set_debug(struct w6692_hw *card) +{ + card->dch.debug = debug; + card->bc[0].bch.debug = debug; + card->bc[1].bch.debug = debug; +} + +static int +set_debug(const char *val, const struct kernel_param *kp) +{ + int ret; + struct w6692_hw *card; + + ret = param_set_uint(val, kp); + if (!ret) { + read_lock(&card_lock); + list_for_each_entry(card, &Cards, list) + _set_debug(card); + read_unlock(&card_lock); + } + return ret; +} + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(W6692_REV); +module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "W6692 debug mask"); +module_param(led, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)"); +module_param(pots, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)"); + +static inline u8 +ReadW6692(struct w6692_hw *card, u8 offset) +{ + return inb(card->addr + offset); +} + +static inline void +WriteW6692(struct w6692_hw *card, u8 offset, u8 value) +{ + outb(value, card->addr + offset); +} + +static inline u8 +ReadW6692B(struct w6692_ch *bc, u8 offset) +{ + return inb(bc->addr + offset); +} + +static inline void +WriteW6692B(struct w6692_ch *bc, u8 offset, u8 value) +{ + outb(value, bc->addr + offset); +} + +static void +enable_hwirq(struct w6692_hw *card) +{ + WriteW6692(card, W_IMASK, card->imask); +} + +static void +disable_hwirq(struct w6692_hw *card) +{ + WriteW6692(card, W_IMASK, 0xff); +} + +static const char *W6692Ver[] = {"V00", "V01", "V10", "V11"}; + +static void +W6692Version(struct w6692_hw *card) +{ + int val; + + val = ReadW6692(card, W_D_RBCH); + pr_notice("%s: Winbond W6692 version: %s\n", card->name, + W6692Ver[(val >> 6) & 3]); +} + +static void +w6692_led_handler(struct w6692_hw *card, int on) +{ + if ((!(card->fmask & led)) || card->subtype == W6692_USR) + return; + if (on) { + card->xdata &= 0xfb; /* LED ON */ + WriteW6692(card, W_XDATA, card->xdata); + } else { + card->xdata |= 0x04; /* LED OFF */ + WriteW6692(card, W_XDATA, card->xdata); + } +} + +static void +ph_command(struct w6692_hw *card, u8 cmd) +{ + pr_debug("%s: ph_command %x\n", card->name, cmd); + WriteW6692(card, W_CIX, cmd); +} + +static void +W6692_new_ph(struct w6692_hw *card) +{ + if (card->state == W_L1CMD_RST) + ph_command(card, W_L1CMD_DRC); + schedule_event(&card->dch, FLG_PHCHANGE); +} + +static void +W6692_ph_bh(struct dchannel *dch) +{ + struct w6692_hw *card = dch->hw; + + switch (card->state) { + case W_L1CMD_RST: + dch->state = 0; + l1_event(dch->l1, HW_RESET_IND); + break; + case W_L1IND_CD: + dch->state = 3; + l1_event(dch->l1, HW_DEACT_CNF); + break; + case W_L1IND_DRD: + dch->state = 3; + l1_event(dch->l1, HW_DEACT_IND); + break; + case W_L1IND_CE: + dch->state = 4; + l1_event(dch->l1, HW_POWERUP_IND); + break; + case W_L1IND_LD: + if (dch->state <= 5) { + dch->state = 5; + l1_event(dch->l1, ANYSIGNAL); + } else { + dch->state = 8; + l1_event(dch->l1, LOSTFRAMING); + } + break; + case W_L1IND_ARD: + dch->state = 6; + l1_event(dch->l1, INFO2); + break; + case W_L1IND_AI8: + dch->state = 7; + l1_event(dch->l1, INFO4_P8); + break; + case W_L1IND_AI10: + dch->state = 7; + l1_event(dch->l1, INFO4_P10); + break; + default: + pr_debug("%s: TE unknown state %02x dch state %02x\n", + card->name, card->state, dch->state); + break; + } + pr_debug("%s: TE newstate %02x\n", card->name, dch->state); +} + +static void +W6692_empty_Dfifo(struct w6692_hw *card, int count) +{ + struct dchannel *dch = &card->dch; + u8 *ptr; + + pr_debug("%s: empty_Dfifo %d\n", card->name, count); + if (!dch->rx_skb) { + dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC); + if (!dch->rx_skb) { + pr_info("%s: D receive out of memory\n", card->name); + WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); + return; + } + } + if ((dch->rx_skb->len + count) >= dch->maxlen) { + pr_debug("%s: empty_Dfifo overrun %d\n", card->name, + dch->rx_skb->len + count); + WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); + return; + } + ptr = skb_put(dch->rx_skb, count); + insb(card->addr + W_D_RFIFO, ptr, count); + WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); + if (debug & DEBUG_HW_DFIFO) { + snprintf(card->log, 63, "D-recv %s %d ", + card->name, count); + print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); + } +} + +static void +W6692_fill_Dfifo(struct w6692_hw *card) +{ + struct dchannel *dch = &card->dch; + int count; + u8 *ptr; + u8 cmd = W_D_CMDR_XMS; + + pr_debug("%s: fill_Dfifo\n", card->name); + if (!dch->tx_skb) + return; + count = dch->tx_skb->len - dch->tx_idx; + if (count <= 0) + return; + if (count > W_D_FIFO_THRESH) + count = W_D_FIFO_THRESH; + else + cmd |= W_D_CMDR_XME; + ptr = dch->tx_skb->data + dch->tx_idx; + dch->tx_idx += count; + outsb(card->addr + W_D_XFIFO, ptr, count); + WriteW6692(card, W_D_CMDR, cmd); + if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) { + pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name); + del_timer(&dch->timer); + } + dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000); + add_timer(&dch->timer); + if (debug & DEBUG_HW_DFIFO) { + snprintf(card->log, 63, "D-send %s %d ", + card->name, count); + print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); + } +} + +static void +d_retransmit(struct w6692_hw *card) +{ + struct dchannel *dch = &card->dch; + + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(dch, D_CLEARBUSY); +#endif + if (test_bit(FLG_TX_BUSY, &dch->Flags)) { + /* Restart frame */ + dch->tx_idx = 0; + W6692_fill_Dfifo(card); + } else if (dch->tx_skb) { /* should not happen */ + pr_info("%s: %s without TX_BUSY\n", card->name, __func__); + test_and_set_bit(FLG_TX_BUSY, &dch->Flags); + dch->tx_idx = 0; + W6692_fill_Dfifo(card); + } else { + pr_info("%s: XDU no TX_BUSY\n", card->name); + if (get_next_dframe(dch)) + W6692_fill_Dfifo(card); + } +} + +static void +handle_rxD(struct w6692_hw *card) { + u8 stat; + int count; + + stat = ReadW6692(card, W_D_RSTA); + if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) { + if (stat & W_D_RSTA_RDOV) { + pr_debug("%s: D-channel RDOV\n", card->name); +#ifdef ERROR_STATISTIC + card->dch.err_rx++; +#endif + } + if (stat & W_D_RSTA_CRCE) { + pr_debug("%s: D-channel CRC error\n", card->name); +#ifdef ERROR_STATISTIC + card->dch.err_crc++; +#endif + } + if (stat & W_D_RSTA_RMB) { + pr_debug("%s: D-channel ABORT\n", card->name); +#ifdef ERROR_STATISTIC + card->dch.err_rx++; +#endif + } + if (card->dch.rx_skb) + dev_kfree_skb(card->dch.rx_skb); + card->dch.rx_skb = NULL; + WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST); + } else { + count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1); + if (count == 0) + count = W_D_FIFO_THRESH; + W6692_empty_Dfifo(card, count); + recv_Dchannel(&card->dch); + } +} + +static void +handle_txD(struct w6692_hw *card) { + if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags)) + del_timer(&card->dch.timer); + if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) { + W6692_fill_Dfifo(card); + } else { + if (card->dch.tx_skb) + dev_kfree_skb(card->dch.tx_skb); + if (get_next_dframe(&card->dch)) + W6692_fill_Dfifo(card); + } +} + +static void +handle_statusD(struct w6692_hw *card) +{ + struct dchannel *dch = &card->dch; + u8 exval, v1, cir; + + exval = ReadW6692(card, W_D_EXIR); + + pr_debug("%s: D_EXIR %02x\n", card->name, exval); + if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) { + /* Transmit underrun/collision */ + pr_debug("%s: D-channel underrun/collision\n", card->name); +#ifdef ERROR_STATISTIC + dch->err_tx++; +#endif + d_retransmit(card); + } + if (exval & W_D_EXI_RDOV) { /* RDOV */ + pr_debug("%s: D-channel RDOV\n", card->name); + WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST); + } + if (exval & W_D_EXI_TIN2) /* TIN2 - never */ + pr_debug("%s: spurious TIN2 interrupt\n", card->name); + if (exval & W_D_EXI_MOC) { /* MOC - not supported */ + v1 = ReadW6692(card, W_MOSR); + pr_debug("%s: spurious MOC interrupt MOSR %02x\n", + card->name, v1); + } + if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */ + cir = ReadW6692(card, W_CIR); + pr_debug("%s: ISC CIR %02X\n", card->name, cir); + if (cir & W_CIR_ICC) { + v1 = cir & W_CIR_COD_MASK; + pr_debug("%s: ph_state_change %x -> %x\n", card->name, + dch->state, v1); + card->state = v1; + if (card->fmask & led) { + switch (v1) { + case W_L1IND_AI8: + case W_L1IND_AI10: + w6692_led_handler(card, 1); + break; + default: + w6692_led_handler(card, 0); + break; + } + } + W6692_new_ph(card); + } + if (cir & W_CIR_SCC) { + v1 = ReadW6692(card, W_SQR); + pr_debug("%s: SCC SQR %02X\n", card->name, v1); + } + } + if (exval & W_D_EXI_WEXP) + pr_debug("%s: spurious WEXP interrupt!\n", card->name); + if (exval & W_D_EXI_TEXP) + pr_debug("%s: spurious TEXP interrupt!\n", card->name); +} + +static void +W6692_empty_Bfifo(struct w6692_ch *wch, int count) +{ + struct w6692_hw *card = wch->bch.hw; + u8 *ptr; + int maxlen; + + pr_debug("%s: empty_Bfifo %d\n", card->name, count); + if (unlikely(wch->bch.state == ISDN_P_NONE)) { + pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name); + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); + if (wch->bch.rx_skb) + skb_trim(wch->bch.rx_skb, 0); + return; + } + if (test_bit(FLG_RX_OFF, &wch->bch.Flags)) { + wch->bch.dropcnt += count; + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); + return; + } + maxlen = bchannel_get_rxbuf(&wch->bch, count); + if (maxlen < 0) { + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); + if (wch->bch.rx_skb) + skb_trim(wch->bch.rx_skb, 0); + pr_warning("%s.B%d: No bufferspace for %d bytes\n", + card->name, wch->bch.nr, count); + return; + } + ptr = skb_put(wch->bch.rx_skb, count); + insb(wch->addr + W_B_RFIFO, ptr, count); + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); + if (debug & DEBUG_HW_DFIFO) { + snprintf(card->log, 63, "B%1d-recv %s %d ", + wch->bch.nr, card->name, count); + print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); + } +} + +static void +W6692_fill_Bfifo(struct w6692_ch *wch) +{ + struct w6692_hw *card = wch->bch.hw; + int count, fillempty = 0; + u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS; + + pr_debug("%s: fill Bfifo\n", card->name); + if (!wch->bch.tx_skb) { + if (!test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) + return; + ptr = wch->bch.fill; + count = W_B_FIFO_THRESH; + fillempty = 1; + } else { + count = wch->bch.tx_skb->len - wch->bch.tx_idx; + if (count <= 0) + return; + ptr = wch->bch.tx_skb->data + wch->bch.tx_idx; + } + if (count > W_B_FIFO_THRESH) + count = W_B_FIFO_THRESH; + else if (test_bit(FLG_HDLC, &wch->bch.Flags)) + cmd |= W_B_CMDR_XME; + + pr_debug("%s: fill Bfifo%d/%d\n", card->name, + count, wch->bch.tx_idx); + wch->bch.tx_idx += count; + if (fillempty) { + while (count > 0) { + outsb(wch->addr + W_B_XFIFO, ptr, MISDN_BCH_FILL_SIZE); + count -= MISDN_BCH_FILL_SIZE; + } + } else { + outsb(wch->addr + W_B_XFIFO, ptr, count); + } + WriteW6692B(wch, W_B_CMDR, cmd); + if ((debug & DEBUG_HW_BFIFO) && !fillempty) { + snprintf(card->log, 63, "B%1d-send %s %d ", + wch->bch.nr, card->name, count); + print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); + } +} + +#if 0 +static int +setvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb) +{ + struct w6692_hw *card = wch->bch.hw; + u16 *vol = (u16 *)skb->data; + u8 val; + + if ((!(card->fmask & pots)) || + !test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) + return -ENODEV; + if (skb->len < 2) + return -EINVAL; + if (*vol > 7) + return -EINVAL; + val = *vol & 7; + val = 7 - val; + if (mic) { + val <<= 3; + card->xaddr &= 0xc7; + } else { + card->xaddr &= 0xf8; + } + card->xaddr |= val; + WriteW6692(card, W_XADDR, card->xaddr); + return 0; +} + +static int +enable_pots(struct w6692_ch *wch) +{ + struct w6692_hw *card = wch->bch.hw; + + if ((!(card->fmask & pots)) || + !test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) + return -ENODEV; + wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0; + WriteW6692B(wch, W_B_MODE, wch->b_mode); + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); + card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0); + WriteW6692(card, W_PCTL, card->pctl); + return 0; +} +#endif + +static int +disable_pots(struct w6692_ch *wch) +{ + struct w6692_hw *card = wch->bch.hw; + + if (!(card->fmask & pots)) + return -ENODEV; + wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0); + WriteW6692B(wch, W_B_MODE, wch->b_mode); + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | + W_B_CMDR_XRST); + return 0; +} + +static int +w6692_mode(struct w6692_ch *wch, u32 pr) +{ + struct w6692_hw *card; + + card = wch->bch.hw; + pr_debug("%s: B%d protocol %x-->%x\n", card->name, + wch->bch.nr, wch->bch.state, pr); + switch (pr) { + case ISDN_P_NONE: + if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM)) + disable_pots(wch); + wch->b_mode = 0; + mISDN_clear_bchannel(&wch->bch); + WriteW6692B(wch, W_B_MODE, wch->b_mode); + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); + test_and_clear_bit(FLG_HDLC, &wch->bch.Flags); + test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags); + break; + case ISDN_P_B_RAW: + wch->b_mode = W_B_MODE_MMS; + WriteW6692B(wch, W_B_MODE, wch->b_mode); + WriteW6692B(wch, W_B_EXIM, 0); + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | + W_B_CMDR_XRST); + test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags); + break; + case ISDN_P_B_HDLC: + wch->b_mode = W_B_MODE_ITF; + WriteW6692B(wch, W_B_MODE, wch->b_mode); + WriteW6692B(wch, W_B_ADM1, 0xff); + WriteW6692B(wch, W_B_ADM2, 0xff); + WriteW6692B(wch, W_B_EXIM, 0); + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | + W_B_CMDR_XRST); + test_and_set_bit(FLG_HDLC, &wch->bch.Flags); + break; + default: + pr_info("%s: protocol %x not known\n", card->name, pr); + return -ENOPROTOOPT; + } + wch->bch.state = pr; + return 0; +} + +static void +send_next(struct w6692_ch *wch) +{ + if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len) { + W6692_fill_Bfifo(wch); + } else { + if (wch->bch.tx_skb) + dev_kfree_skb(wch->bch.tx_skb); + if (get_next_bframe(&wch->bch)) { + W6692_fill_Bfifo(wch); + test_and_clear_bit(FLG_TX_EMPTY, &wch->bch.Flags); + } else if (test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) { + W6692_fill_Bfifo(wch); + } + } +} + +static void +W6692B_interrupt(struct w6692_hw *card, int ch) +{ + struct w6692_ch *wch = &card->bc[ch]; + int count; + u8 stat, star = 0; + + stat = ReadW6692B(wch, W_B_EXIR); + pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat); + if (stat & W_B_EXI_RME) { + star = ReadW6692B(wch, W_B_STAR); + if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) { + if ((star & W_B_STAR_RDOV) && + test_bit(FLG_ACTIVE, &wch->bch.Flags)) { + pr_debug("%s: B%d RDOV proto=%x\n", card->name, + wch->bch.nr, wch->bch.state); +#ifdef ERROR_STATISTIC + wch->bch.err_rdo++; +#endif + } + if (test_bit(FLG_HDLC, &wch->bch.Flags)) { + if (star & W_B_STAR_CRCE) { + pr_debug("%s: B%d CRC error\n", + card->name, wch->bch.nr); +#ifdef ERROR_STATISTIC + wch->bch.err_crc++; +#endif + } + if (star & W_B_STAR_RMB) { + pr_debug("%s: B%d message abort\n", + card->name, wch->bch.nr); +#ifdef ERROR_STATISTIC + wch->bch.err_inv++; +#endif + } + } + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | + W_B_CMDR_RRST | W_B_CMDR_RACT); + if (wch->bch.rx_skb) + skb_trim(wch->bch.rx_skb, 0); + } else { + count = ReadW6692B(wch, W_B_RBCL) & + (W_B_FIFO_THRESH - 1); + if (count == 0) + count = W_B_FIFO_THRESH; + W6692_empty_Bfifo(wch, count); + recv_Bchannel(&wch->bch, 0, false); + } + } + if (stat & W_B_EXI_RMR) { + if (!(stat & W_B_EXI_RME)) + star = ReadW6692B(wch, W_B_STAR); + if (star & W_B_STAR_RDOV) { + pr_debug("%s: B%d RDOV proto=%x\n", card->name, + wch->bch.nr, wch->bch.state); +#ifdef ERROR_STATISTIC + wch->bch.err_rdo++; +#endif + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | + W_B_CMDR_RRST | W_B_CMDR_RACT); + } else { + W6692_empty_Bfifo(wch, W_B_FIFO_THRESH); + if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) + recv_Bchannel(&wch->bch, 0, false); + } + } + if (stat & W_B_EXI_RDOV) { + /* only if it is not handled yet */ + if (!(star & W_B_STAR_RDOV)) { + pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name, + wch->bch.nr, wch->bch.state); +#ifdef ERROR_STATISTIC + wch->bch.err_rdo++; +#endif + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | + W_B_CMDR_RRST | W_B_CMDR_RACT); + } + } + if (stat & W_B_EXI_XFR) { + if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) { + star = ReadW6692B(wch, W_B_STAR); + pr_debug("%s: B%d star %02x\n", card->name, + wch->bch.nr, star); + } + if (star & W_B_STAR_XDOW) { + pr_warning("%s: B%d XDOW proto=%x\n", card->name, + wch->bch.nr, wch->bch.state); +#ifdef ERROR_STATISTIC + wch->bch.err_xdu++; +#endif + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | + W_B_CMDR_RACT); + /* resend */ + if (wch->bch.tx_skb) { + if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) + wch->bch.tx_idx = 0; + } + } + send_next(wch); + if (star & W_B_STAR_XDOW) + return; /* handle XDOW only once */ + } + if (stat & W_B_EXI_XDUN) { + pr_warning("%s: B%d XDUN proto=%x\n", card->name, + wch->bch.nr, wch->bch.state); +#ifdef ERROR_STATISTIC + wch->bch.err_xdu++; +#endif + /* resend - no XRST needed */ + if (wch->bch.tx_skb) { + if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) + wch->bch.tx_idx = 0; + } else if (test_bit(FLG_FILLEMPTY, &wch->bch.Flags)) { + test_and_set_bit(FLG_TX_EMPTY, &wch->bch.Flags); + } + send_next(wch); + } +} + +static irqreturn_t +w6692_irq(int intno, void *dev_id) +{ + struct w6692_hw *card = dev_id; + u8 ista; + + spin_lock(&card->lock); + ista = ReadW6692(card, W_ISTA); + if ((ista | card->imask) == card->imask) { + /* possible a shared IRQ reqest */ + spin_unlock(&card->lock); + return IRQ_NONE; + } + card->irqcnt++; + pr_debug("%s: ista %02x\n", card->name, ista); + ista &= ~card->imask; + if (ista & W_INT_B1_EXI) + W6692B_interrupt(card, 0); + if (ista & W_INT_B2_EXI) + W6692B_interrupt(card, 1); + if (ista & W_INT_D_RME) + handle_rxD(card); + if (ista & W_INT_D_RMR) + W6692_empty_Dfifo(card, W_D_FIFO_THRESH); + if (ista & W_INT_D_XFR) + handle_txD(card); + if (ista & W_INT_D_EXI) + handle_statusD(card); + if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */ + pr_debug("%s: W6692 spurious XINT!\n", card->name); +/* End IRQ Handler */ + spin_unlock(&card->lock); + return IRQ_HANDLED; +} + +static void +dbusy_timer_handler(struct timer_list *t) +{ + struct dchannel *dch = from_timer(dch, t, timer); + struct w6692_hw *card = dch->hw; + int rbch, star; + u_long flags; + + if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) { + spin_lock_irqsave(&card->lock, flags); + rbch = ReadW6692(card, W_D_RBCH); + star = ReadW6692(card, W_D_STAR); + pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n", + card->name, rbch, star); + if (star & W_D_STAR_XBZ) /* D-Channel Busy */ + test_and_set_bit(FLG_L1_BUSY, &dch->Flags); + else { + /* discard frame; reset transceiver */ + test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags); + if (dch->tx_idx) + dch->tx_idx = 0; + else + pr_info("%s: W6692 D-Channel Busy no tx_idx\n", + card->name); + /* Transmitter reset */ + WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST); + } + spin_unlock_irqrestore(&card->lock, flags); + } +} + +static void initW6692(struct w6692_hw *card) +{ + u8 val; + + timer_setup(&card->dch.timer, dbusy_timer_handler, 0); + w6692_mode(&card->bc[0], ISDN_P_NONE); + w6692_mode(&card->bc[1], ISDN_P_NONE); + WriteW6692(card, W_D_CTL, 0x00); + disable_hwirq(card); + WriteW6692(card, W_D_SAM, 0xff); + WriteW6692(card, W_D_TAM, 0xff); + WriteW6692(card, W_D_MODE, W_D_MODE_RACT); + card->state = W_L1CMD_RST; + ph_command(card, W_L1CMD_RST); + ph_command(card, W_L1CMD_ECK); + /* enable all IRQ but extern */ + card->imask = 0x18; + WriteW6692(card, W_D_EXIM, 0x00); + WriteW6692B(&card->bc[0], W_B_EXIM, 0); + WriteW6692B(&card->bc[1], W_B_EXIM, 0); + /* Reset D-chan receiver and transmitter */ + WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST); + /* Reset B-chan receiver and transmitter */ + WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); + WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); + /* enable peripheral */ + if (card->subtype == W6692_USR) { + /* seems that USR implemented some power control features + * Pin 79 is connected to the oscilator circuit so we + * have to handle it here + */ + card->pctl = 0x80; + card->xdata = 0; + WriteW6692(card, W_PCTL, card->pctl); + WriteW6692(card, W_XDATA, card->xdata); + } else { + card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 | + W_PCTL_OE1 | W_PCTL_OE0; + card->xaddr = 0x00;/* all sw off */ + if (card->fmask & pots) + card->xdata |= 0x06; /* POWER UP/ LED OFF / ALAW */ + if (card->fmask & led) + card->xdata |= 0x04; /* LED OFF */ + if ((card->fmask & pots) || (card->fmask & led)) { + WriteW6692(card, W_PCTL, card->pctl); + WriteW6692(card, W_XADDR, card->xaddr); + WriteW6692(card, W_XDATA, card->xdata); + val = ReadW6692(card, W_XADDR); + if (debug & DEBUG_HW) + pr_notice("%s: W_XADDR=%02x\n", + card->name, val); + } + } +} + +static void +reset_w6692(struct w6692_hw *card) +{ + WriteW6692(card, W_D_CTL, W_D_CTL_SRST); + mdelay(10); + WriteW6692(card, W_D_CTL, 0); +} + +static int +init_card(struct w6692_hw *card) +{ + int cnt = 3; + u_long flags; + + spin_lock_irqsave(&card->lock, flags); + disable_hwirq(card); + spin_unlock_irqrestore(&card->lock, flags); + if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) { + pr_info("%s: couldn't get interrupt %d\n", card->name, + card->irq); + return -EIO; + } + while (cnt--) { + spin_lock_irqsave(&card->lock, flags); + initW6692(card); + enable_hwirq(card); + spin_unlock_irqrestore(&card->lock, flags); + /* Timeout 10ms */ + msleep_interruptible(10); + if (debug & DEBUG_HW) + pr_notice("%s: IRQ %d count %d\n", card->name, + card->irq, card->irqcnt); + if (!card->irqcnt) { + pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", + card->name, card->irq, 3 - cnt); + reset_w6692(card); + } else + return 0; + } + free_irq(card->irq, card); + return -EIO; +} + +static int +w6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch); + struct w6692_hw *card = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&card->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + ret = 0; + W6692_fill_Bfifo(bc); + } + spin_unlock_irqrestore(&card->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(&card->lock, flags); + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) + ret = w6692_mode(bc, ch->protocol); + else + ret = 0; + spin_unlock_irqrestore(&card->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + spin_lock_irqsave(&card->lock, flags); + mISDN_clear_bchannel(bch); + w6692_mode(bc, ISDN_P_NONE); + spin_unlock_irqrestore(&card->lock, flags); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + ret = 0; + break; + default: + pr_info("%s: %s unknown prim(%x,%x)\n", + card->name, __func__, hh->prim, hh->id); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + return mISDN_ctrl_bchannel(bch, cq); +} + +static int +open_bchannel(struct w6692_hw *card, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel == 0 || rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + bch = &card->bc[rq->adr.channel - 1].bch; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; + return 0; +} + +static int +channel_ctrl(struct w6692_hw *card, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_L1_TIMER3; + break; + case MISDN_CTRL_L1_TIMER3: + ret = l1_event(card->dch.l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); + break; + default: + pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch); + struct w6692_hw *card = bch->hw; + int ret = -EINVAL; + u_long flags; + + pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + cancel_work_sync(&bch->workq); + spin_lock_irqsave(&card->lock, flags); + mISDN_clear_bchannel(bch); + w6692_mode(bc, ISDN_P_NONE); + spin_unlock_irqrestore(&card->lock, flags); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + ret = 0; + break; + case CONTROL_CHANNEL: + ret = channel_bctrl(bch, arg); + break; + default: + pr_info("%s: %s unknown prim(%x)\n", + card->name, __func__, cmd); + } + return ret; +} + +static int +w6692_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + u32 id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&card->lock, flags); + ret = dchannel_senddata(dch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + W6692_fill_Dfifo(card); + ret = 0; + spin_unlock_irqrestore(&card->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&card->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + ret = l1_event(dch->l1, hh->prim); + break; + case PH_DEACTIVATE_REQ: + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + ret = l1_event(dch->l1, hh->prim); + break; + } + + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +w6692_l1callback(struct dchannel *dch, u32 cmd) +{ + struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); + u_long flags; + + pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state); + switch (cmd) { + case INFO3_P8: + spin_lock_irqsave(&card->lock, flags); + ph_command(card, W_L1CMD_AR8); + spin_unlock_irqrestore(&card->lock, flags); + break; + case INFO3_P10: + spin_lock_irqsave(&card->lock, flags); + ph_command(card, W_L1CMD_AR10); + spin_unlock_irqrestore(&card->lock, flags); + break; + case HW_RESET_REQ: + spin_lock_irqsave(&card->lock, flags); + if (card->state != W_L1IND_DRD) + ph_command(card, W_L1CMD_RST); + ph_command(card, W_L1CMD_ECK); + spin_unlock_irqrestore(&card->lock, flags); + break; + case HW_DEACT_REQ: + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); + break; + case HW_POWERUP_REQ: + spin_lock_irqsave(&card->lock, flags); + ph_command(card, W_L1CMD_ECK); + spin_unlock_irqrestore(&card->lock, flags); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + pr_debug("%s: %s unknown command %x\n", card->name, + __func__, cmd); + return -1; + } + return 0; +} + +static int +open_dchannel(struct w6692_hw *card, struct channel_req *rq, void *caller) +{ + pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__, + card->dch.dev.id, caller); + if (rq->protocol != ISDN_P_TE_S0) + return -EINVAL; + if (rq->adr.channel == 1) + /* E-Channel not supported */ + return -EINVAL; + rq->ch = &card->dch.dev.D; + rq->ch->protocol = rq->protocol; + if (card->dch.state == 7) + _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + return 0; +} + +static int +w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); + struct channel_req *rq; + int err = 0; + + pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if (rq->protocol == ISDN_P_TE_S0) + err = open_dchannel(card, rq, __builtin_return_address(0)); + else + err = open_bchannel(card, rq); + if (err) + break; + if (!try_module_get(THIS_MODULE)) + pr_info("%s: cannot get module\n", card->name); + break; + case CLOSE_CHANNEL: + pr_debug("%s: dev(%d) close from %p\n", card->name, + dch->dev.id, __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(card, arg); + break; + default: + pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd); + return -EINVAL; + } + return err; +} + +static int +setup_w6692(struct w6692_hw *card) +{ + u32 val; + + if (!request_region(card->addr, 256, card->name)) { + pr_info("%s: config port %x-%x already in use\n", card->name, + card->addr, card->addr + 255); + return -EIO; + } + W6692Version(card); + card->bc[0].addr = card->addr; + card->bc[1].addr = card->addr + 0x40; + val = ReadW6692(card, W_ISTA); + if (debug & DEBUG_HW) + pr_notice("%s ISTA=%02x\n", card->name, val); + val = ReadW6692(card, W_IMASK); + if (debug & DEBUG_HW) + pr_notice("%s IMASK=%02x\n", card->name, val); + val = ReadW6692(card, W_D_EXIR); + if (debug & DEBUG_HW) + pr_notice("%s D_EXIR=%02x\n", card->name, val); + val = ReadW6692(card, W_D_EXIM); + if (debug & DEBUG_HW) + pr_notice("%s D_EXIM=%02x\n", card->name, val); + val = ReadW6692(card, W_D_RSTA); + if (debug & DEBUG_HW) + pr_notice("%s D_RSTA=%02x\n", card->name, val); + return 0; +} + +static void +release_card(struct w6692_hw *card) +{ + u_long flags; + + spin_lock_irqsave(&card->lock, flags); + disable_hwirq(card); + w6692_mode(&card->bc[0], ISDN_P_NONE); + w6692_mode(&card->bc[1], ISDN_P_NONE); + if ((card->fmask & led) || card->subtype == W6692_USR) { + card->xdata |= 0x04; /* LED OFF */ + WriteW6692(card, W_XDATA, card->xdata); + } + spin_unlock_irqrestore(&card->lock, flags); + free_irq(card->irq, card); + l1_event(card->dch.l1, CLOSE_CHANNEL); + mISDN_unregister_device(&card->dch.dev); + release_region(card->addr, 256); + mISDN_freebchannel(&card->bc[1].bch); + mISDN_freebchannel(&card->bc[0].bch); + mISDN_freedchannel(&card->dch); + write_lock_irqsave(&card_lock, flags); + list_del(&card->list); + write_unlock_irqrestore(&card_lock, flags); + pci_disable_device(card->pdev); + pci_set_drvdata(card->pdev, NULL); + kfree(card); +} + +static int +setup_instance(struct w6692_hw *card) +{ + int i, err; + u_long flags; + + snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1); + write_lock_irqsave(&card_lock, flags); + list_add_tail(&card->list, &Cards); + write_unlock_irqrestore(&card_lock, flags); + card->fmask = (1 << w6692_cnt); + _set_debug(card); + spin_lock_init(&card->lock); + mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh); + card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0); + card->dch.dev.D.send = w6692_l2l1D; + card->dch.dev.D.ctrl = w6692_dctrl; + card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + card->dch.hw = card; + card->dch.dev.nrbchan = 2; + for (i = 0; i < 2; i++) { + mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM, + W_B_FIFO_THRESH); + card->bc[i].bch.hw = card; + card->bc[i].bch.nr = i + 1; + card->bc[i].bch.ch.nr = i + 1; + card->bc[i].bch.ch.send = w6692_l2l1B; + card->bc[i].bch.ch.ctrl = w6692_bctrl; + set_channelmap(i + 1, card->dch.dev.channelmap); + list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels); + } + err = setup_w6692(card); + if (err) + goto error_setup; + err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, + card->name); + if (err) + goto error_reg; + err = init_card(card); + if (err) + goto error_init; + err = create_l1(&card->dch, w6692_l1callback); + if (!err) { + w6692_cnt++; + pr_notice("W6692 %d cards installed\n", w6692_cnt); + return 0; + } + + free_irq(card->irq, card); +error_init: + mISDN_unregister_device(&card->dch.dev); +error_reg: + release_region(card->addr, 256); +error_setup: + mISDN_freebchannel(&card->bc[1].bch); + mISDN_freebchannel(&card->bc[0].bch); + mISDN_freedchannel(&card->dch); + write_lock_irqsave(&card_lock, flags); + list_del(&card->list); + write_unlock_irqrestore(&card_lock, flags); + kfree(card); + return err; +} + +static int +w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + struct w6692_hw *card; + struct w6692map *m = (struct w6692map *)ent->driver_data; + + card = kzalloc(sizeof(struct w6692_hw), GFP_KERNEL); + if (!card) { + pr_info("No kmem for w6692 card\n"); + return err; + } + card->pdev = pdev; + card->subtype = m->subtype; + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return err; + } + + printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n", + m->name, pci_name(pdev)); + + card->addr = pci_resource_start(pdev, 1); + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) + pci_set_drvdata(pdev, NULL); + return err; +} + +static void +w6692_remove_pci(struct pci_dev *pdev) +{ + struct w6692_hw *card = pci_get_drvdata(pdev); + + if (card) + release_card(card); + else + if (debug) + pr_notice("%s: drvdata already removed\n", __func__); +} + +static const struct pci_device_id w6692_ids[] = { + { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]}, + { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, + PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0, + (ulong)&w6692_map[2]}, + { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]}, + { } +}; +MODULE_DEVICE_TABLE(pci, w6692_ids); + +static struct pci_driver w6692_driver = { + .name = "w6692", + .probe = w6692_probe, + .remove = w6692_remove_pci, + .id_table = w6692_ids, +}; + +static int __init w6692_init(void) +{ + int err; + + pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV); + + err = pci_register_driver(&w6692_driver); + return err; +} + +static void __exit w6692_cleanup(void) +{ + pci_unregister_driver(&w6692_driver); +} + +module_init(w6692_init); +module_exit(w6692_cleanup); diff --git a/drivers/isdn/hardware/mISDN/w6692.h b/drivers/isdn/hardware/mISDN/w6692.h new file mode 100644 index 000000000..f95697757 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/w6692.h @@ -0,0 +1,190 @@ +/* + * Winbond W6692 specific defines + * + * Author Karsten Keil <keil@isdn4linux.de> + * based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* Specifications of W6692 registers */ + +#define W_D_RFIFO 0x00 /* R */ +#define W_D_XFIFO 0x04 /* W */ +#define W_D_CMDR 0x08 /* W */ +#define W_D_MODE 0x0c /* R/W */ +#define W_D_TIMR 0x10 /* R/W */ +#define W_ISTA 0x14 /* R_clr */ +#define W_IMASK 0x18 /* R/W */ +#define W_D_EXIR 0x1c /* R_clr */ +#define W_D_EXIM 0x20 /* R/W */ +#define W_D_STAR 0x24 /* R */ +#define W_D_RSTA 0x28 /* R */ +#define W_D_SAM 0x2c /* R/W */ +#define W_D_SAP1 0x30 /* R/W */ +#define W_D_SAP2 0x34 /* R/W */ +#define W_D_TAM 0x38 /* R/W */ +#define W_D_TEI1 0x3c /* R/W */ +#define W_D_TEI2 0x40 /* R/W */ +#define W_D_RBCH 0x44 /* R */ +#define W_D_RBCL 0x48 /* R */ +#define W_TIMR2 0x4c /* W */ +#define W_L1_RC 0x50 /* R/W */ +#define W_D_CTL 0x54 /* R/W */ +#define W_CIR 0x58 /* R */ +#define W_CIX 0x5c /* W */ +#define W_SQR 0x60 /* R */ +#define W_SQX 0x64 /* W */ +#define W_PCTL 0x68 /* R/W */ +#define W_MOR 0x6c /* R */ +#define W_MOX 0x70 /* R/W */ +#define W_MOSR 0x74 /* R_clr */ +#define W_MOCR 0x78 /* R/W */ +#define W_GCR 0x7c /* R/W */ + +#define W_B_RFIFO 0x80 /* R */ +#define W_B_XFIFO 0x84 /* W */ +#define W_B_CMDR 0x88 /* W */ +#define W_B_MODE 0x8c /* R/W */ +#define W_B_EXIR 0x90 /* R_clr */ +#define W_B_EXIM 0x94 /* R/W */ +#define W_B_STAR 0x98 /* R */ +#define W_B_ADM1 0x9c /* R/W */ +#define W_B_ADM2 0xa0 /* R/W */ +#define W_B_ADR1 0xa4 /* R/W */ +#define W_B_ADR2 0xa8 /* R/W */ +#define W_B_RBCL 0xac /* R */ +#define W_B_RBCH 0xb0 /* R */ + +#define W_XADDR 0xf4 /* R/W */ +#define W_XDATA 0xf8 /* R/W */ +#define W_EPCTL 0xfc /* W */ + +/* W6692 register bits */ + +#define W_D_CMDR_XRST 0x01 +#define W_D_CMDR_XME 0x02 +#define W_D_CMDR_XMS 0x08 +#define W_D_CMDR_STT 0x10 +#define W_D_CMDR_RRST 0x40 +#define W_D_CMDR_RACK 0x80 + +#define W_D_MODE_RLP 0x01 +#define W_D_MODE_DLP 0x02 +#define W_D_MODE_MFD 0x04 +#define W_D_MODE_TEE 0x08 +#define W_D_MODE_TMS 0x10 +#define W_D_MODE_RACT 0x40 +#define W_D_MODE_MMS 0x80 + +#define W_INT_B2_EXI 0x01 +#define W_INT_B1_EXI 0x02 +#define W_INT_D_EXI 0x04 +#define W_INT_XINT0 0x08 +#define W_INT_XINT1 0x10 +#define W_INT_D_XFR 0x20 +#define W_INT_D_RME 0x40 +#define W_INT_D_RMR 0x80 + +#define W_D_EXI_WEXP 0x01 +#define W_D_EXI_TEXP 0x02 +#define W_D_EXI_ISC 0x04 +#define W_D_EXI_MOC 0x08 +#define W_D_EXI_TIN2 0x10 +#define W_D_EXI_XCOL 0x20 +#define W_D_EXI_XDUN 0x40 +#define W_D_EXI_RDOV 0x80 + +#define W_D_STAR_DRDY 0x10 +#define W_D_STAR_XBZ 0x20 +#define W_D_STAR_XDOW 0x80 + +#define W_D_RSTA_RMB 0x10 +#define W_D_RSTA_CRCE 0x20 +#define W_D_RSTA_RDOV 0x40 + +#define W_D_CTL_SRST 0x20 + +#define W_CIR_SCC 0x80 +#define W_CIR_ICC 0x40 +#define W_CIR_COD_MASK 0x0f + +#define W_PCTL_PCX 0x01 +#define W_PCTL_XMODE 0x02 +#define W_PCTL_OE0 0x04 +#define W_PCTL_OE1 0x08 +#define W_PCTL_OE2 0x10 +#define W_PCTL_OE3 0x20 +#define W_PCTL_OE4 0x40 +#define W_PCTL_OE5 0x80 + +#define W_B_CMDR_XRST 0x01 +#define W_B_CMDR_XME 0x02 +#define W_B_CMDR_XMS 0x04 +#define W_B_CMDR_RACT 0x20 +#define W_B_CMDR_RRST 0x40 +#define W_B_CMDR_RACK 0x80 + +#define W_B_MODE_FTS0 0x01 +#define W_B_MODE_FTS1 0x02 +#define W_B_MODE_SW56 0x04 +#define W_B_MODE_BSW0 0x08 +#define W_B_MODE_BSW1 0x10 +#define W_B_MODE_EPCM 0x20 +#define W_B_MODE_ITF 0x40 +#define W_B_MODE_MMS 0x80 + +#define W_B_EXI_XDUN 0x01 +#define W_B_EXI_XFR 0x02 +#define W_B_EXI_RDOV 0x10 +#define W_B_EXI_RME 0x20 +#define W_B_EXI_RMR 0x40 + +#define W_B_STAR_XBZ 0x01 +#define W_B_STAR_XDOW 0x04 +#define W_B_STAR_RMB 0x10 +#define W_B_STAR_CRCE 0x20 +#define W_B_STAR_RDOV 0x40 + +#define W_B_RBCH_LOV 0x20 + +/* W6692 Layer1 commands */ + +#define W_L1CMD_ECK 0x00 +#define W_L1CMD_RST 0x01 +#define W_L1CMD_SCP 0x04 +#define W_L1CMD_SSP 0x02 +#define W_L1CMD_AR8 0x08 +#define W_L1CMD_AR10 0x09 +#define W_L1CMD_EAL 0x0a +#define W_L1CMD_DRC 0x0f + +/* W6692 Layer1 indications */ + +#define W_L1IND_CE 0x07 +#define W_L1IND_DRD 0x00 +#define W_L1IND_LD 0x04 +#define W_L1IND_ARD 0x08 +#define W_L1IND_TI 0x0a +#define W_L1IND_ATI 0x0b +#define W_L1IND_AI8 0x0c +#define W_L1IND_AI10 0x0d +#define W_L1IND_CD 0x0f + +/* FIFO thresholds */ +#define W_D_FIFO_THRESH 64 +#define W_B_FIFO_THRESH 64 |