diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/act2000 |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/isdn/act2000')
-rw-r--r-- | drivers/isdn/act2000/Kconfig | 13 | ||||
-rw-r--r-- | drivers/isdn/act2000/Makefile | 9 | ||||
-rw-r--r-- | drivers/isdn/act2000/act2000.h | 202 | ||||
-rw-r--r-- | drivers/isdn/act2000/act2000_isa.c | 449 | ||||
-rw-r--r-- | drivers/isdn/act2000/act2000_isa.h | 136 | ||||
-rw-r--r-- | drivers/isdn/act2000/capi.c | 1177 | ||||
-rw-r--r-- | drivers/isdn/act2000/capi.h | 366 | ||||
-rw-r--r-- | drivers/isdn/act2000/module.c | 808 |
8 files changed, 3160 insertions, 0 deletions
diff --git a/drivers/isdn/act2000/Kconfig b/drivers/isdn/act2000/Kconfig new file mode 100644 index 00000000000..78e6ad8d57c --- /dev/null +++ b/drivers/isdn/act2000/Kconfig @@ -0,0 +1,13 @@ +# +# Config.in for IBM Active 2000 ISDN driver +# +config ISDN_DRV_ACT2000 + tristate "IBM Active 2000 support" + depends on ISDN_I4L && ISA + help + Say Y here if you have an IBM Active 2000 ISDN card. In order to use + this card, additional firmware is necessary, which has to be loaded + into the card using a utility which is part of the latest + isdn4k-utils package. Please read the file + <file:Documentation/isdn/README.act2000> for more information. + diff --git a/drivers/isdn/act2000/Makefile b/drivers/isdn/act2000/Makefile new file mode 100644 index 00000000000..05e582fb5c0 --- /dev/null +++ b/drivers/isdn/act2000/Makefile @@ -0,0 +1,9 @@ +# Makefile for the act2000 ISDN device driver + +# Each configuration option enables a list of files. + +obj-$(CONFIG_ISDN_DRV_ACT2000) += act2000.o + +# Multipart objects. + +act2000-y := module.o capi.o act2000_isa.o diff --git a/drivers/isdn/act2000/act2000.h b/drivers/isdn/act2000/act2000.h new file mode 100644 index 00000000000..b091d1a5412 --- /dev/null +++ b/drivers/isdn/act2000/act2000.h @@ -0,0 +1,202 @@ +/* $Id: act2000.h,v 1.8.6.3 2001/09/23 22:24:32 kai Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. + * + * Author Fritz Elfert + * Copyright by Fritz Elfert <fritz@isdn4linux.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Thanks to Friedemann Baitinger and IBM Germany + * + */ + +#ifndef act2000_h +#define act2000_h + +#include <linux/compiler.h> + +#define ACT2000_IOCTL_SETPORT 1 +#define ACT2000_IOCTL_GETPORT 2 +#define ACT2000_IOCTL_SETIRQ 3 +#define ACT2000_IOCTL_GETIRQ 4 +#define ACT2000_IOCTL_SETBUS 5 +#define ACT2000_IOCTL_GETBUS 6 +#define ACT2000_IOCTL_SETPROTO 7 +#define ACT2000_IOCTL_GETPROTO 8 +#define ACT2000_IOCTL_SETMSN 9 +#define ACT2000_IOCTL_GETMSN 10 +#define ACT2000_IOCTL_LOADBOOT 11 +#define ACT2000_IOCTL_ADDCARD 12 + +#define ACT2000_IOCTL_TEST 98 +#define ACT2000_IOCTL_DEBUGVAR 99 + +#define ACT2000_BUS_ISA 1 +#define ACT2000_BUS_MCA 2 +#define ACT2000_BUS_PCMCIA 3 + +/* Struct for adding new cards */ +typedef struct act2000_cdef { + int bus; + int port; + int irq; + char id[10]; +} act2000_cdef; + +/* Struct for downloading firmware */ +typedef struct act2000_ddef { + int length; /* Length of code */ + char __user *buffer; /* Ptr. to code */ +} act2000_ddef; + +typedef struct act2000_fwid { + char isdn[4]; + char revlen[2]; + char revision[504]; +} act2000_fwid; + +#if defined(__KERNEL__) || defined(__DEBUGVAR__) + +#ifdef __KERNEL__ +/* Kernel includes */ + +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> +#include <linux/skbuff.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/major.h> +#include <asm/io.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/ioport.h> +#include <linux/timer.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/isdnif.h> + +#endif /* __KERNEL__ */ + +#define ACT2000_PORTLEN 8 + +#define ACT2000_FLAGS_RUNNING 1 /* Cards driver activated */ +#define ACT2000_FLAGS_PVALID 2 /* Cards port is valid */ +#define ACT2000_FLAGS_IVALID 4 /* Cards irq is valid */ +#define ACT2000_FLAGS_LOADED 8 /* Firmware loaded */ + +#define ACT2000_BCH 2 /* # of channels per card */ + +/* D-Channel states */ +#define ACT2000_STATE_NULL 0 +#define ACT2000_STATE_ICALL 1 +#define ACT2000_STATE_OCALL 2 +#define ACT2000_STATE_IWAIT 3 +#define ACT2000_STATE_OWAIT 4 +#define ACT2000_STATE_IBWAIT 5 +#define ACT2000_STATE_OBWAIT 6 +#define ACT2000_STATE_BWAIT 7 +#define ACT2000_STATE_BHWAIT 8 +#define ACT2000_STATE_BHWAIT2 9 +#define ACT2000_STATE_DHWAIT 10 +#define ACT2000_STATE_DHWAIT2 11 +#define ACT2000_STATE_BSETUP 12 +#define ACT2000_STATE_ACTIVE 13 + +#define ACT2000_MAX_QUEUED 8000 /* 2 * maxbuff */ + +#define ACT2000_LOCK_TX 0 +#define ACT2000_LOCK_RX 1 + +typedef struct act2000_chan { + unsigned short callref; /* Call Reference */ + unsigned short fsm_state; /* Current D-Channel state */ + unsigned short eazmask; /* EAZ-Mask for this Channel */ + short queued; /* User-Data Bytes in TX queue */ + unsigned short plci; + unsigned short ncci; + unsigned char l2prot; /* Layer 2 protocol */ + unsigned char l3prot; /* Layer 3 protocol */ +} act2000_chan; + +typedef struct msn_entry { + char eaz; + char msn[16]; + struct msn_entry * next; +} msn_entry; + +typedef struct irq_data_isa { + __u8 *rcvptr; + __u16 rcvidx; + __u16 rcvlen; + struct sk_buff *rcvskb; + __u8 rcvignore; + __u8 rcvhdr[8]; +} irq_data_isa; + +typedef union irq_data { + irq_data_isa isa; +} irq_data; + +/* + * Per card driver data + */ +typedef struct act2000_card { + unsigned short port; /* Base-port-address */ + unsigned short irq; /* Interrupt */ + u_char ptype; /* Protocol type (1TR6 or Euro) */ + u_char bus; /* Cardtype (ISA, MCA, PCMCIA) */ + struct act2000_card *next; /* Pointer to next device struct */ + spinlock_t lock; /* protect critical operations */ + int myid; /* Driver-Nr. assigned by linklevel */ + unsigned long flags; /* Statusflags */ + unsigned long ilock; /* Semaphores for IRQ-Routines */ + struct sk_buff_head rcvq; /* Receive-Message queue */ + struct sk_buff_head sndq; /* Send-Message queue */ + struct sk_buff_head ackq; /* Data-Ack-Message queue */ + u_char *ack_msg; /* Ptr to User Data in User skb */ + __u16 need_b3ack; /* Flag: Need ACK for current skb */ + struct sk_buff *sbuf; /* skb which is currently sent */ + struct timer_list ptimer; /* Poll timer */ + struct work_struct snd_tq; /* Task struct for xmit bh */ + struct work_struct rcv_tq; /* Task struct for rcv bh */ + struct work_struct poll_tq; /* Task struct for polled rcv bh */ + msn_entry *msn_list; + unsigned short msgnum; /* Message number for sending */ + spinlock_t mnlock; /* lock for msgnum */ + act2000_chan bch[ACT2000_BCH]; /* B-Channel status/control */ + char status_buf[256]; /* Buffer for status messages */ + char *status_buf_read; + char *status_buf_write; + char *status_buf_end; + irq_data idat; /* Data used for IRQ handler */ + isdn_if interface; /* Interface to upper layer */ + char regname[35]; /* Name used for request_region */ +} act2000_card; + +extern __inline__ void act2000_schedule_tx(act2000_card *card) +{ + schedule_work(&card->snd_tq); +} + +extern __inline__ void act2000_schedule_rx(act2000_card *card) +{ + schedule_work(&card->rcv_tq); +} + +extern __inline__ void act2000_schedule_poll(act2000_card *card) +{ + schedule_work(&card->poll_tq); +} + +extern char *act2000_find_eaz(act2000_card *, char); + +#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ +#endif /* act2000_h */ diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c new file mode 100644 index 00000000000..bc98d77c5ec --- /dev/null +++ b/drivers/isdn/act2000/act2000_isa.c @@ -0,0 +1,449 @@ +/* $Id: act2000_isa.c,v 1.11.6.3 2001/09/23 22:24:32 kai Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). + * + * Author Fritz Elfert + * Copyright by Fritz Elfert <fritz@isdn4linux.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Thanks to Friedemann Baitinger and IBM Germany + * + */ + +#include "act2000.h" +#include "act2000_isa.h" +#include "capi.h" + +static act2000_card *irq2card_map[16]; + +/* + * Reset Controller, then try to read the Card's signature. + + Return: + * 1 = Signature found. + * 0 = Signature not found. + */ +static int +act2000_isa_reset(unsigned short portbase) +{ + unsigned char reg; + int i; + int found; + int serial = 0; + + found = 0; + if ((reg = inb(portbase + ISA_COR)) != 0xff) { + outb(reg | ISA_COR_RESET, portbase + ISA_COR); + mdelay(10); + outb(reg, portbase + ISA_COR); + mdelay(10); + + for (i = 0; i < 16; i++) { + if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL) + serial |= 0x10000; + serial >>= 1; + } + if (serial == ISA_SER_ID) + found++; + } + return found; +} + +int +act2000_isa_detect(unsigned short portbase) +{ + int ret = 0; + + if (request_region(portbase, ACT2000_PORTLEN, "act2000isa")) { + ret = act2000_isa_reset(portbase); + release_region(portbase, ISA_REGION); + } + return ret; +} + +static irqreturn_t +act2000_isa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + act2000_card *card = irq2card_map[irq]; + u_char istatus; + + if (!card) { + printk(KERN_WARNING + "act2000: Spurious interrupt!\n"); + return IRQ_NONE; + } + istatus = (inb(ISA_PORT_ISR) & 0x07); + if (istatus & ISA_ISR_OUT) { + /* RX fifo has data */ + istatus &= ISA_ISR_OUT_MASK; + outb(0, ISA_PORT_SIS); + act2000_isa_receive(card); + outb(ISA_SIS_INT, ISA_PORT_SIS); + } + if (istatus & ISA_ISR_ERR) { + /* Error Interrupt */ + istatus &= ISA_ISR_ERR_MASK; + printk(KERN_WARNING "act2000: errIRQ\n"); + } + if (istatus) + printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", irq, istatus); + return IRQ_HANDLED; +} + +static void +act2000_isa_select_irq(act2000_card * card) +{ + unsigned char reg; + + reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR; + switch (card->irq) { + case 3: + reg = ISA_COR_IRQ03; + break; + case 5: + reg = ISA_COR_IRQ05; + break; + case 7: + reg = ISA_COR_IRQ07; + break; + case 10: + reg = ISA_COR_IRQ10; + break; + case 11: + reg = ISA_COR_IRQ11; + break; + case 12: + reg = ISA_COR_IRQ12; + break; + case 15: + reg = ISA_COR_IRQ15; + break; + } + outb(reg, ISA_PORT_COR); +} + +static void +act2000_isa_enable_irq(act2000_card * card) +{ + act2000_isa_select_irq(card); + /* Enable READ irq */ + outb(ISA_SIS_INT, ISA_PORT_SIS); +} + +/* + * Install interrupt handler, enable irq on card. + * If irq is -1, choose next free irq, else irq is given explicitely. + */ +int +act2000_isa_config_irq(act2000_card * card, short irq) +{ + if (card->flags & ACT2000_FLAGS_IVALID) { + free_irq(card->irq, NULL); + irq2card_map[card->irq] = NULL; + } + card->flags &= ~ACT2000_FLAGS_IVALID; + outb(ISA_COR_IRQOFF, ISA_PORT_COR); + if (!irq) + return 0; + + if (!request_irq(irq, &act2000_isa_interrupt, 0, card->regname, NULL)) { + card->irq = irq; + irq2card_map[card->irq] = card; + card->flags |= ACT2000_FLAGS_IVALID; + printk(KERN_WARNING + "act2000: Could not request irq %d\n",irq); + return -EBUSY; + } else { + act2000_isa_select_irq(card); + /* Disable READ and WRITE irq */ + outb(0, ISA_PORT_SIS); + outb(0, ISA_PORT_SOS); + } + return 0; +} + +int +act2000_isa_config_port(act2000_card * card, unsigned short portbase) +{ + if (card->flags & ACT2000_FLAGS_PVALID) { + release_region(card->port, ISA_REGION); + card->flags &= ~ACT2000_FLAGS_PVALID; + } + if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL) + return -EBUSY; + else { + card->port = portbase; + card->flags |= ACT2000_FLAGS_PVALID; + return 0; + } +} + +/* + * Release ressources, used by an adaptor. + */ +void +act2000_isa_release(act2000_card * card) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + if (card->flags & ACT2000_FLAGS_IVALID) { + free_irq(card->irq, NULL); + irq2card_map[card->irq] = NULL; + } + card->flags &= ~ACT2000_FLAGS_IVALID; + if (card->flags & ACT2000_FLAGS_PVALID) + release_region(card->port, ISA_REGION); + card->flags &= ~ACT2000_FLAGS_PVALID; + spin_unlock_irqrestore(&card->lock, flags); +} + +static int +act2000_isa_writeb(act2000_card * card, u_char data) +{ + u_char timeout = 40; + + while (timeout) { + if (inb(ISA_PORT_SOS) & ISA_SOS_READY) { + outb(data, ISA_PORT_SDO); + return 0; + } else { + timeout--; + udelay(10); + } + } + return 1; +} + +static int +act2000_isa_readb(act2000_card * card, u_char * data) +{ + u_char timeout = 40; + + while (timeout) { + if (inb(ISA_PORT_SIS) & ISA_SIS_READY) { + *data = inb(ISA_PORT_SDI); + return 0; + } else { + timeout--; + udelay(10); + } + } + return 1; +} + +void +act2000_isa_receive(act2000_card *card) +{ + u_char c; + + if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0) + return; + while (!act2000_isa_readb(card, &c)) { + if (card->idat.isa.rcvidx < 8) { + card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c; + if (card->idat.isa.rcvidx == 8) { + int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr); + + if (valid) { + card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len; + card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen); + if (card->idat.isa.rcvskb == NULL) { + card->idat.isa.rcvignore = 1; + printk(KERN_WARNING + "act2000_isa_receive: no memory\n"); + test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); + return; + } + memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8); + card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8); + } else { + card->idat.isa.rcvidx = 0; + printk(KERN_WARNING + "act2000_isa_receive: Invalid CAPI msg\n"); + { + int i; __u8 *p; __u8 *c; __u8 tmp[30]; + for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++) + c += sprintf(c, "%02x ", *(p++)); + printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp); + } + } + } + } else { + if (!card->idat.isa.rcvignore) + *card->idat.isa.rcvptr++ = c; + if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) { + if (!card->idat.isa.rcvignore) { + skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb); + act2000_schedule_rx(card); + } + card->idat.isa.rcvidx = 0; + card->idat.isa.rcvlen = 8; + card->idat.isa.rcvignore = 0; + card->idat.isa.rcvskb = NULL; + card->idat.isa.rcvptr = card->idat.isa.rcvhdr; + } + } + } + if (!(card->flags & ACT2000_FLAGS_IVALID)) { + /* In polling mode, schedule myself */ + if ((card->idat.isa.rcvidx) && + (card->idat.isa.rcvignore || + (card->idat.isa.rcvidx < card->idat.isa.rcvlen))) + act2000_schedule_poll(card); + } + test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); +} + +void +act2000_isa_send(act2000_card * card) +{ + unsigned long flags; + struct sk_buff *skb; + actcapi_msg *msg; + int l; + + if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0) + return; + while (1) { + spin_lock_irqsave(&card->lock, flags); + if (!(card->sbuf)) { + if ((card->sbuf = skb_dequeue(&card->sndq))) { + card->ack_msg = card->sbuf->data; + msg = (actcapi_msg *)card->sbuf->data; + if ((msg->hdr.cmd.cmd == 0x86) && + (msg->hdr.cmd.subcmd == 0) ) { + /* Save flags in message */ + card->need_b3ack = msg->msg.data_b3_req.flags; + msg->msg.data_b3_req.flags = 0; + } + } + } + spin_unlock_irqrestore(&card->lock, flags); + if (!(card->sbuf)) { + /* No more data to send */ + test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); + return; + } + skb = card->sbuf; + l = 0; + while (skb->len) { + if (act2000_isa_writeb(card, *(skb->data))) { + /* Fifo is full, but more data to send */ + test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); + /* Schedule myself */ + act2000_schedule_tx(card); + return; + } + skb_pull(skb, 1); + l++; + } + msg = (actcapi_msg *)card->ack_msg; + if ((msg->hdr.cmd.cmd == 0x86) && + (msg->hdr.cmd.subcmd == 0) ) { + /* + * If it's user data, reset data-ptr + * and put skb into ackq. + */ + skb->data = card->ack_msg; + /* Restore flags in message */ + msg->msg.data_b3_req.flags = card->need_b3ack; + skb_queue_tail(&card->ackq, skb); + } else + dev_kfree_skb(skb); + card->sbuf = NULL; + } +} + +/* + * Get firmware ID, check for 'ISDN' signature. + */ +static int +act2000_isa_getid(act2000_card * card) +{ + + act2000_fwid fid; + u_char *p = (u_char *) & fid; + int count = 0; + + while (1) { + if (count > 510) + return -EPROTO; + if (act2000_isa_readb(card, p++)) + break; + count++; + } + if (count <= 20) { + printk(KERN_WARNING "act2000: No Firmware-ID!\n"); + return -ETIME; + } + *p = '\0'; + fid.revlen[0] = '\0'; + if (strcmp(fid.isdn, "ISDN")) { + printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n"); + return -EPROTO; + } + if ((p = strchr(fid.revision, '\n'))) + *p = '\0'; + printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision); + if (card->flags & ACT2000_FLAGS_IVALID) { + printk(KERN_DEBUG "Enabling Interrupts ...\n"); + act2000_isa_enable_irq(card); + } + return 0; +} + +/* + * Download microcode into card, check Firmware signature. + */ +int +act2000_isa_download(act2000_card * card, act2000_ddef __user * cb) +{ + unsigned int length; + int l; + int c; + long timeout; + u_char *b; + u_char __user *p; + u_char *buf; + act2000_ddef cblock; + + if (!act2000_isa_reset(card->port)) + return -ENXIO; + msleep_interruptible(500); + if (copy_from_user(&cblock, cb, sizeof(cblock))) + return -EFAULT; + length = cblock.length; + p = cblock.buffer; + if (!access_ok(VERIFY_READ, p, length)) + return -EFAULT; + buf = (u_char *) kmalloc(1024, GFP_KERNEL); + if (!buf) + return -ENOMEM; + timeout = 0; + while (length) { + l = (length > 1024) ? 1024 : length; + c = 0; + b = buf; + if (copy_from_user(buf, p, l)) { + kfree(buf); + return -EFAULT; + } + while (c < l) { + if (act2000_isa_writeb(card, *b++)) { + printk(KERN_WARNING + "act2000: loader timed out" + " len=%d c=%d\n", length, c); + kfree(buf); + return -ETIME; + } + c++; + } + length -= l; + p += l; + } + kfree(buf); + msleep_interruptible(500); + return (act2000_isa_getid(card)); +} diff --git a/drivers/isdn/act2000/act2000_isa.h b/drivers/isdn/act2000/act2000_isa.h new file mode 100644 index 00000000000..ad86c5ed9aa --- /dev/null +++ b/drivers/isdn/act2000/act2000_isa.h @@ -0,0 +1,136 @@ +/* $Id: act2000_isa.h,v 1.4.6.1 2001/09/23 22:24:32 kai Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). + * + * Author Fritz Elfert + * Copyright by Fritz Elfert <fritz@isdn4linux.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Thanks to Friedemann Baitinger and IBM Germany + * + */ + +#ifndef act2000_isa_h +#define act2000_isa_h + +#define ISA_POLL_LOOP 40 /* Try to read-write before give up */ + +typedef enum { + INT_NO_CHANGE = 0, /* Do not change the Mask */ + INT_ON = 1, /* Set to Enable */ + INT_OFF = 2, /* Set to Disable */ +} ISA_INT_T; + +/**************************************************************************/ +/* Configuration Register COR (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* Soft Res| IRQM | IRQ Select | N/A | WAIT |Proc err */ +/**************************************************************************/ +#define ISA_COR 0 /* Offset for ISA config register */ +#define ISA_COR_PERR 0x01 /* Processor Error Enabled */ +#define ISA_COR_WS 0x02 /* Insert Wait State if 1 */ +#define ISA_COR_IRQOFF 0x38 /* No Interrupt */ +#define ISA_COR_IRQ07 0x30 /* IRQ 7 Enable */ +#define ISA_COR_IRQ05 0x28 /* IRQ 5 Enable */ +#define ISA_COR_IRQ03 0x20 /* IRQ 3 Enable */ +#define ISA_COR_IRQ10 0x18 /* IRQ 10 Enable */ +#define ISA_COR_IRQ11 0x10 /* IRQ 11 Enable */ +#define ISA_COR_IRQ12 0x08 /* IRQ 12 Enable */ +#define ISA_COR_IRQ15 0x00 /* IRQ 15 Enable */ +#define ISA_COR_IRQPULSE 0x40 /* 0 = Level 1 = Pulse Interrupt */ +#define ISA_COR_RESET 0x80 /* Soft Reset for Transputer */ + +/**************************************************************************/ +/* Interrupt Source Register ISR (RO) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A |Err sig |Ser ID |IN Intr |Out Intr| Error */ +/**************************************************************************/ +#define ISA_ISR 1 /* Offset for Interrupt Register */ +#define ISA_ISR_ERR 0x01 /* Error Interrupt */ +#define ISA_ISR_OUT 0x02 /* Output Interrupt */ +#define ISA_ISR_INP 0x04 /* Input Interrupt */ +#define ISA_ISR_SERIAL 0x08 /* Read out Serial ID after Reset */ +#define ISA_ISR_ERRSIG 0x10 /* Error Signal Input */ +#define ISA_ISR_ERR_MASK 0xfe /* Mask Error Interrupt */ +#define ISA_ISR_OUT_MASK 0xfd /* Mask Output Interrupt */ +#define ISA_ISR_INP_MASK 0xfb /* Mask Input Interrupt */ + +/* Signature delivered after Reset at ISA_ISR_SERIAL (LSB first) */ +#define ISA_SER_ID 0x0201 /* ID for ISA Card */ + +/**************************************************************************/ +/* EEPROM Register EPR (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A |ROM Hold| ROM CS |ROM CLK | ROM IN |ROM Out */ +/**************************************************************************/ +#define ISA_EPR 2 /* Offset for this Register */ +#define ISA_EPR_OUT 0x01 /* Rome Register Out (RO) */ +#define ISA_EPR_IN 0x02 /* Rom Register In (WR) */ +#define ISA_EPR_CLK 0x04 /* Rom Clock (WR) */ +#define ISA_EPR_CS 0x08 /* Rom Cip Select (WR) */ +#define ISA_EPR_HOLD 0x10 /* Rom Hold Signal (WR) */ + +/**************************************************************************/ +/* EEPROM enable Register EER (unused) */ +/**************************************************************************/ +#define ISA_EER 3 /* Offset for this Register */ + +/**************************************************************************/ +/* SLC Data Input SDI (RO) */ +/**************************************************************************/ +#define ISA_SDI 4 /* Offset for this Register */ + +/**************************************************************************/ +/* SLC Data Output SDO (WO) */ +/**************************************************************************/ +#define ISA_SDO 5 /* Offset for this Register */ + +/**************************************************************************/ +/* IMS C011 Mode 2 Input Status Register for INMOS CPU SIS (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Data Pre */ +/**************************************************************************/ +#define ISA_SIS 6 /* Offset for this Register */ +#define ISA_SIS_READY 0x01 /* If 1 : data is available */ +#define ISA_SIS_INT 0x02 /* Enable Interrupt for READ */ + +/**************************************************************************/ +/* IMS C011 Mode 2 Output Status Register from INMOS CPU SOS (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Out Rdy */ +/**************************************************************************/ +#define ISA_SOS 7 /* Offset for this Register */ +#define ISA_SOS_READY 0x01 /* If 1 : we can write Data */ +#define ISA_SOS_INT 0x02 /* Enable Interrupt for WRITE */ + +#define ISA_REGION 8 /* Number of Registers */ + + +/* Macros for accessing ports */ +#define ISA_PORT_COR (card->port+ISA_COR) +#define ISA_PORT_ISR (card->port+ISA_ISR) +#define ISA_PORT_EPR (card->port+ISA_EPR) +#define ISA_PORT_EER (card->port+ISA_EER) +#define ISA_PORT_SDI (card->port+ISA_SDI) +#define ISA_PORT_SDO (card->port+ISA_SDO) +#define ISA_PORT_SIS (card->port+ISA_SIS) +#define ISA_PORT_SOS (card->port+ISA_SOS) + +/* Prototypes */ + +extern int act2000_isa_detect(unsigned short portbase); +extern int act2000_isa_config_irq(act2000_card * card, short irq); +extern int act2000_isa_config_port(act2000_card * card, unsigned short portbase); +extern int act2000_isa_download(act2000_card * card, act2000_ddef __user * cb); +extern void act2000_isa_release(act2000_card * card); +extern void act2000_isa_receive(act2000_card *card); +extern void act2000_isa_send(act2000_card *card); + +#endif /* act2000_isa_h */ diff --git a/drivers/isdn/act2000/capi.c b/drivers/isdn/act2000/capi.c new file mode 100644 index 00000000000..40395f56723 --- /dev/null +++ b/drivers/isdn/act2000/capi.c @@ -0,0 +1,1177 @@ +/* $Id: capi.c,v 1.9.6.2 2001/09/23 22:24:32 kai Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. + * CAPI encoder/decoder + * + * Author Fritz Elfert + * Copyright by Fritz Elfert <fritz@isdn4linux.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Thanks to Friedemann Baitinger and IBM Germany + * + */ + +#include "act2000.h" +#include "capi.h" + +static actcapi_msgdsc valid_msg[] = { + {{ 0x86, 0x02}, "DATA_B3_IND"}, /* DATA_B3_IND/CONF must be first because of speed!!! */ + {{ 0x86, 0x01}, "DATA_B3_CONF"}, + {{ 0x02, 0x01}, "CONNECT_CONF"}, + {{ 0x02, 0x02}, "CONNECT_IND"}, + {{ 0x09, 0x01}, "CONNECT_INFO_CONF"}, + {{ 0x03, 0x02}, "CONNECT_ACTIVE_IND"}, + {{ 0x04, 0x01}, "DISCONNECT_CONF"}, + {{ 0x04, 0x02}, "DISCONNECT_IND"}, + {{ 0x05, 0x01}, "LISTEN_CONF"}, + {{ 0x06, 0x01}, "GET_PARAMS_CONF"}, + {{ 0x07, 0x01}, "INFO_CONF"}, + {{ 0x07, 0x02}, "INFO_IND"}, + {{ 0x08, 0x01}, "DATA_CONF"}, + {{ 0x08, 0x02}, "DATA_IND"}, + {{ 0x40, 0x01}, "SELECT_B2_PROTOCOL_CONF"}, + {{ 0x80, 0x01}, "SELECT_B3_PROTOCOL_CONF"}, + {{ 0x81, 0x01}, "LISTEN_B3_CONF"}, + {{ 0x82, 0x01}, "CONNECT_B3_CONF"}, + {{ 0x82, 0x02}, "CONNECT_B3_IND"}, + {{ 0x83, 0x02}, "CONNECT_B3_ACTIVE_IND"}, + {{ 0x84, 0x01}, "DISCONNECT_B3_CONF"}, + {{ 0x84, 0x02}, "DISCONNECT_B3_IND"}, + {{ 0x85, 0x01}, "GET_B3_PARAMS_CONF"}, + {{ 0x01, 0x01}, "RESET_B3_CONF"}, + {{ 0x01, 0x02}, "RESET_B3_IND"}, + /* {{ 0x87, 0x02, "HANDSET_IND"}, not implemented */ + {{ 0xff, 0x01}, "MANUFACTURER_CONF"}, + {{ 0xff, 0x02}, "MANUFACTURER_IND"}, +#ifdef DEBUG_MSG + /* Requests */ + {{ 0x01, 0x00}, "RESET_B3_REQ"}, + {{ 0x02, 0x00}, "CONNECT_REQ"}, + {{ 0x04, 0x00}, "DISCONNECT_REQ"}, + {{ 0x05, 0x00}, "LISTEN_REQ"}, + {{ 0x06, 0x00}, "GET_PARAMS_REQ"}, + {{ 0x07, 0x00}, "INFO_REQ"}, + {{ 0x08, 0x00}, "DATA_REQ"}, + {{ 0x09, 0x00}, "CONNECT_INFO_REQ"}, + {{ 0x40, 0x00}, "SELECT_B2_PROTOCOL_REQ"}, + {{ 0x80, 0x00}, "SELECT_B3_PROTOCOL_REQ"}, + {{ 0x81, 0x00}, "LISTEN_B3_REQ"}, + {{ 0x82, 0x00}, "CONNECT_B3_REQ"}, + {{ 0x84, 0x00}, "DISCONNECT_B3_REQ"}, + {{ 0x85, 0x00}, "GET_B3_PARAMS_REQ"}, + {{ 0x86, 0x00}, "DATA_B3_REQ"}, + {{ 0xff, 0x00}, "MANUFACTURER_REQ"}, + /* Responses */ + {{ 0x01, 0x03}, "RESET_B3_RESP"}, + {{ 0x02, 0x03}, "CONNECT_RESP"}, + {{ 0x03, 0x03}, "CONNECT_ACTIVE_RESP"}, + {{ 0x04, 0x03}, "DISCONNECT_RESP"}, + {{ 0x07, 0x03}, "INFO_RESP"}, + {{ 0x08, 0x03}, "DATA_RESP"}, + {{ 0x82, 0x03}, "CONNECT_B3_RESP"}, + {{ 0x83, 0x03}, "CONNECT_B3_ACTIVE_RESP"}, + {{ 0x84, 0x03}, "DISCONNECT_B3_RESP"}, + {{ 0x86, 0x03}, "DATA_B3_RESP"}, + {{ 0xff, 0x03}, "MANUFACTURER_RESP"}, +#endif + {{ 0x00, 0x00}, NULL}, +}; +#define num_valid_msg (sizeof(valid_msg)/sizeof(actcapi_msgdsc)) +#define num_valid_imsg 27 /* MANUFACTURER_IND */ + +/* + * Check for a valid incoming CAPI message. + * Return: + * 0 = Invalid message + * 1 = Valid message, no B-Channel-data + * 2 = Valid message, B-Channel-data + */ +int +actcapi_chkhdr(act2000_card * card, actcapi_msghdr *hdr) +{ + int i; + + if (hdr->applicationID != 1) + return 0; + if (hdr->len < 9) + return 0; + for (i = 0; i < num_valid_imsg; i++) + if ((hdr->cmd.cmd == valid_msg[i].cmd.cmd) && + (hdr->cmd.subcmd == valid_msg[i].cmd.subcmd)) { + return (i?1:2); + } + return 0; +} + +#define ACTCAPI_MKHDR(l, c, s) { \ + skb = alloc_skb(l + 8, GFP_ATOMIC); \ + if (skb) { \ + m = (actcapi_msg *)skb_put(skb, l + 8); \ + m->hdr.len = l + 8; \ + m->hdr.applicationID = 1; \ + m->hdr.cmd.cmd = c; \ + m->hdr.cmd.subcmd = s; \ + m->hdr.msgnum = actcapi_nextsmsg(card); \ + } else m = NULL;\ +} + +#define ACTCAPI_CHKSKB if (!skb) { \ + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); \ + return; \ +} + +#define ACTCAPI_QUEUE_TX { \ + actcapi_debug_msg(skb, 1); \ + skb_queue_tail(&card->sndq, skb); \ + act2000_schedule_tx(card); \ +} + +int +actcapi_listen_req(act2000_card *card) +{ + __u16 eazmask = 0; + int i; + actcapi_msg *m; + struct sk_buff *skb; + + for (i = 0; i < ACT2000_BCH; i++) + eazmask |= card->bch[i].eazmask; + ACTCAPI_MKHDR(9, 0x05, 0x00); + if (!skb) { + |