summaryrefslogtreecommitdiff
path: root/drivers/isdn/capi
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/capi
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/capi')
-rw-r--r--drivers/isdn/capi/Kconfig53
-rw-r--r--drivers/isdn/capi/Makefile15
-rw-r--r--drivers/isdn/capi/capi.c1554
-rw-r--r--drivers/isdn/capi/capidrv.c2315
-rw-r--r--drivers/isdn/capi/capidrv.h140
-rw-r--r--drivers/isdn/capi/capifs.c212
-rw-r--r--drivers/isdn/capi/capifs.h11
-rw-r--r--drivers/isdn/capi/capilib.c200
-rw-r--r--drivers/isdn/capi/capiutil.c859
-rw-r--r--drivers/isdn/capi/kcapi.c991
-rw-r--r--drivers/isdn/capi/kcapi.h49
-rw-r--r--drivers/isdn/capi/kcapi_proc.c336
12 files changed, 6735 insertions, 0 deletions
diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig
new file mode 100644
index 000000000000..8b6c9a431ffa
--- /dev/null
+++ b/drivers/isdn/capi/Kconfig
@@ -0,0 +1,53 @@
+#
+# Config.in for the CAPI subsystem
+#
+config ISDN_DRV_AVMB1_VERBOSE_REASON
+ bool "Verbose reason code reporting (kernel size +=7K)"
+ depends on ISDN_CAPI
+ help
+ If you say Y here, the AVM B1 driver will give verbose reasons for
+ disconnecting. This will increase the size of the kernel by 7 KB. If
+ unsure, say Y.
+
+config ISDN_CAPI_MIDDLEWARE
+ bool "CAPI2.0 Middleware support (EXPERIMENTAL)"
+ depends on ISDN_CAPI && EXPERIMENTAL
+ help
+ This option will enhance the capabilities of the /dev/capi20
+ interface. It will provide a means of moving a data connection,
+ established via the usual /dev/capi20 interface to a special tty
+ device. If you want to use pppd with pppdcapiplugin to dial up to
+ your ISP, say Y here.
+
+config ISDN_CAPI_CAPI20
+ tristate "CAPI2.0 /dev/capi support"
+ depends on ISDN_CAPI
+ help
+ This option will provide the CAPI 2.0 interface to userspace
+ applications via /dev/capi20. Applications should use the
+ standardized libcapi20 to access this functionality. You should say
+ Y/M here.
+
+config ISDN_CAPI_CAPIFS_BOOL
+ bool "CAPI2.0 filesystem support"
+ depends on ISDN_CAPI_MIDDLEWARE && ISDN_CAPI_CAPI20
+
+config ISDN_CAPI_CAPIFS
+ tristate
+ depends on ISDN_CAPI_CAPIFS_BOOL
+ default ISDN_CAPI_CAPI20
+ help
+ This option provides a special file system, similar to /dev/pts with
+ device nodes for the special ttys established by using the
+ middleware extension above. If you want to use pppd with
+ pppdcapiplugin to dial up to your ISP, say Y here.
+
+config ISDN_CAPI_CAPIDRV
+ tristate "CAPI2.0 capidrv interface support"
+ depends on ISDN_CAPI && ISDN_I4L
+ help
+ This option provides the glue code to hook up CAPI driven cards to
+ the legacy isdn4linux link layer. If you have a card which is
+ supported by a CAPI driver, but still want to use old features like
+ ippp interfaces or ttyI emulation, say Y/M here.
+
diff --git a/drivers/isdn/capi/Makefile b/drivers/isdn/capi/Makefile
new file mode 100644
index 000000000000..57123e3e4978
--- /dev/null
+++ b/drivers/isdn/capi/Makefile
@@ -0,0 +1,15 @@
+# Makefile for the CAPI subsystem.
+
+# Ordering constraints: kernelcapi.o first
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_CAPI) += kernelcapi.o
+obj-$(CONFIG_ISDN_CAPI_CAPI20) += capi.o
+obj-$(CONFIG_ISDN_CAPI_CAPIDRV) += capidrv.o
+obj-$(CONFIG_ISDN_CAPI_CAPIFS) += capifs.o
+
+# Multipart objects.
+
+kernelcapi-y := kcapi.o capiutil.o capilib.o
+kernelcapi-$(CONFIG_PROC_FS) += kcapi_proc.o
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
new file mode 100644
index 000000000000..06163538bb20
--- /dev/null
+++ b/drivers/isdn/capi/capi.c
@@ -0,0 +1,1554 @@
+/* $Id: capi.c,v 1.1.2.7 2004/04/28 09:48:59 armin Exp $
+ *
+ * CAPI 2.0 Interface for Linux
+ *
+ * Copyright 1996 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/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#include <linux/tty.h>
+#ifdef CONFIG_PPP
+#include <linux/netdevice.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#endif /* CONFIG_PPP */
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/poll.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capicmd.h>
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+#include "capifs.h"
+#endif
+
+static char *revision = "$Revision: 1.1.2.7 $";
+
+MODULE_DESCRIPTION("CAPI4Linux: Userspace /dev/capi20 interface");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+#undef _DEBUG_REFCOUNT /* alloc/free and open/close debug */
+#undef _DEBUG_TTYFUNCS /* call to tty_driver */
+#undef _DEBUG_DATAFLOW /* data flow */
+
+/* -------- driver information -------------------------------------- */
+
+static struct class_simple *capi_class;
+
+int capi_major = 68; /* allocated */
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#define CAPINC_NR_PORTS 32
+#define CAPINC_MAX_PORTS 256
+int capi_ttymajor = 191;
+int capi_ttyminors = CAPINC_NR_PORTS;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+module_param_named(major, capi_major, uint, 0);
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+module_param_named(ttymajor, capi_ttymajor, uint, 0);
+module_param_named(ttyminors, capi_ttyminors, uint, 0);
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- defines ------------------------------------------------- */
+
+#define CAPINC_MAX_RECVQUEUE 10
+#define CAPINC_MAX_SENDQUEUE 10
+#define CAPI_MAX_BLKSIZE 2048
+
+/* -------- data structures ----------------------------------------- */
+
+struct capidev;
+struct capincci;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+struct capiminor;
+
+struct capiminor {
+ struct list_head list;
+ struct capincci *nccip;
+ unsigned int minor;
+
+ struct capi20_appl *ap;
+ u32 ncci;
+ u16 datahandle;
+ u16 msgid;
+
+ struct tty_struct *tty;
+ int ttyinstop;
+ int ttyoutstop;
+ struct sk_buff *ttyskb;
+ atomic_t ttyopencount;
+
+ struct sk_buff_head inqueue;
+ int inbytes;
+ struct sk_buff_head outqueue;
+ int outbytes;
+
+ /* transmit path */
+ struct datahandle_queue {
+ struct datahandle_queue *next;
+ u16 datahandle;
+ } *ackqueue;
+ int nack;
+
+};
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+struct capincci {
+ struct capincci *next;
+ u32 ncci;
+ struct capidev *cdev;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ struct capiminor *minorp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+};
+
+struct capidev {
+ struct list_head list;
+ struct capi20_appl ap;
+ u16 errcode;
+ unsigned userflags;
+
+ struct sk_buff_head recvqueue;
+ wait_queue_head_t recvwait;
+
+ struct capincci *nccis;
+
+ struct semaphore ncci_list_sem;
+};
+
+/* -------- global variables ---------------------------------------- */
+
+static DEFINE_RWLOCK(capidev_list_lock);
+static LIST_HEAD(capidev_list);
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+static DEFINE_RWLOCK(capiminor_list_lock);
+static LIST_HEAD(capiminor_list);
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- datahandles --------------------------------------------- */
+
+static int capincci_add_ack(struct capiminor *mp, u16 datahandle)
+{
+ struct datahandle_queue *n, **pp;
+
+ n = kmalloc(sizeof(*n), GFP_ATOMIC);
+ if (!n) {
+ printk(KERN_ERR "capi: alloc datahandle failed\n");
+ return -1;
+ }
+ n->next = NULL;
+ n->datahandle = datahandle;
+ for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) ;
+ *pp = n;
+ mp->nack++;
+ return 0;
+}
+
+static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
+{
+ struct datahandle_queue **pp, *p;
+
+ for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) {
+ if ((*pp)->datahandle == datahandle) {
+ p = *pp;
+ *pp = (*pp)->next;
+ kfree(p);
+ mp->nack--;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static void capiminor_del_all_ack(struct capiminor *mp)
+{
+ struct datahandle_queue **pp, *p;
+
+ pp = &mp->ackqueue;
+ while (*pp) {
+ p = *pp;
+ *pp = (*pp)->next;
+ kfree(p);
+ mp->nack--;
+ }
+}
+
+
+/* -------- struct capiminor ---------------------------------------- */
+
+static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci)
+{
+ struct capiminor *mp, *p;
+ unsigned int minor = 0;
+ unsigned long flags;
+
+ mp = kmalloc(sizeof(*mp), GFP_ATOMIC);
+ if (!mp) {
+ printk(KERN_ERR "capi: can't alloc capiminor\n");
+ return NULL;
+ }
+
+ memset(mp, 0, sizeof(struct capiminor));
+ mp->ap = ap;
+ mp->ncci = ncci;
+ mp->msgid = 0;
+ atomic_set(&mp->ttyopencount,0);
+
+ skb_queue_head_init(&mp->inqueue);
+ skb_queue_head_init(&mp->outqueue);
+
+ /* Allocate the least unused minor number.
+ */
+ write_lock_irqsave(&capiminor_list_lock, flags);
+ if (list_empty(&capiminor_list))
+ list_add(&mp->list, &capiminor_list);
+ else {
+ list_for_each_entry(p, &capiminor_list, list) {
+ if (p->minor > minor)
+ break;
+ minor++;
+ }
+
+ if (minor < capi_ttyminors) {
+ mp->minor = minor;
+ list_add(&mp->list, p->list.prev);
+ }
+ }
+ write_unlock_irqrestore(&capiminor_list_lock, flags);
+
+ if (!(minor < capi_ttyminors)) {
+ printk(KERN_NOTICE "capi: out of minors\n");
+ kfree(mp);
+ return NULL;
+ }
+
+ return mp;
+}
+
+static void capiminor_free(struct capiminor *mp)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&capiminor_list_lock, flags);
+ list_del(&mp->list);
+ write_unlock_irqrestore(&capiminor_list_lock, flags);
+
+ if (mp->ttyskb) kfree_skb(mp->ttyskb);
+ mp->ttyskb = NULL;
+ skb_queue_purge(&mp->inqueue);
+ skb_queue_purge(&mp->outqueue);
+ capiminor_del_all_ack(mp);
+ kfree(mp);
+}
+
+struct capiminor *capiminor_find(unsigned int minor)
+{
+ struct list_head *l;
+ struct capiminor *p = NULL;
+
+ read_lock(&capiminor_list_lock);
+ list_for_each(l, &capiminor_list) {
+ p = list_entry(l, struct capiminor, list);
+ if (p->minor == minor)
+ break;
+ }
+ read_unlock(&capiminor_list_lock);
+ if (l == &capiminor_list)
+ return NULL;
+
+ return p;
+}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- struct capincci ----------------------------------------- */
+
+static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci)
+{
+ struct capincci *np, **pp;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ struct capiminor *mp = NULL;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+ np = kmalloc(sizeof(*np), GFP_ATOMIC);
+ if (!np)
+ return NULL;
+ memset(np, 0, sizeof(struct capincci));
+ np->ncci = ncci;
+ np->cdev = cdev;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ mp = NULL;
+ if (cdev->userflags & CAPIFLAG_HIGHJACKING)
+ mp = np->minorp = capiminor_alloc(&cdev->ap, ncci);
+ if (mp) {
+ mp->nccip = np;
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "set mp->nccip\n");
+#endif
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+ capifs_new_ncci(mp->minor, MKDEV(capi_ttymajor, mp->minor));
+#endif
+ }
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ for (pp=&cdev->nccis; *pp; pp = &(*pp)->next)
+ ;
+ *pp = np;
+ return np;
+}
+
+static void capincci_free(struct capidev *cdev, u32 ncci)
+{
+ struct capincci *np, **pp;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ struct capiminor *mp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+ pp=&cdev->nccis;
+ while (*pp) {
+ np = *pp;
+ if (ncci == 0xffffffff || np->ncci == ncci) {
+ *pp = (*pp)->next;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ if ((mp = np->minorp) != 0) {
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+ capifs_free_ncci(mp->minor);
+#endif
+ if (mp->tty) {
+ mp->nccip = NULL;
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "reset mp->nccip\n");
+#endif
+ tty_hangup(mp->tty);
+ } else {
+ capiminor_free(mp);
+ }
+ }
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ kfree(np);
+ if (*pp == 0) return;
+ } else {
+ pp = &(*pp)->next;
+ }
+ }
+}
+
+static struct capincci *capincci_find(struct capidev *cdev, u32 ncci)
+{
+ struct capincci *p;
+
+ for (p=cdev->nccis; p ; p = p->next) {
+ if (p->ncci == ncci)
+ break;
+ }
+ return p;
+}
+
+/* -------- struct capidev ------------------------------------------ */
+
+static struct capidev *capidev_alloc(void)
+{
+ struct capidev *cdev;
+ unsigned long flags;
+
+ cdev = kmalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return NULL;
+ memset(cdev, 0, sizeof(struct capidev));
+
+ init_MUTEX(&cdev->ncci_list_sem);
+ skb_queue_head_init(&cdev->recvqueue);
+ init_waitqueue_head(&cdev->recvwait);
+ write_lock_irqsave(&capidev_list_lock, flags);
+ list_add_tail(&cdev->list, &capidev_list);
+ write_unlock_irqrestore(&capidev_list_lock, flags);
+ return cdev;
+}
+
+static void capidev_free(struct capidev *cdev)
+{
+ unsigned long flags;
+
+ if (cdev->ap.applid) {
+ capi20_release(&cdev->ap);
+ cdev->ap.applid = 0;
+ }
+ skb_queue_purge(&cdev->recvqueue);
+
+ down(&cdev->ncci_list_sem);
+ capincci_free(cdev, 0xffffffff);
+ up(&cdev->ncci_list_sem);
+
+ write_lock_irqsave(&capidev_list_lock, flags);
+ list_del(&cdev->list);
+ write_unlock_irqrestore(&capidev_list_lock, flags);
+ kfree(cdev);
+}
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- handle data queue --------------------------------------- */
+
+static struct sk_buff *
+gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
+{
+ struct sk_buff *nskb;
+ nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC);
+ if (nskb) {
+ u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
+ unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN);
+ capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN);
+ capimsg_setu16(s, 2, mp->ap->applid);
+ capimsg_setu8 (s, 4, CAPI_DATA_B3);
+ capimsg_setu8 (s, 5, CAPI_RESP);
+ capimsg_setu16(s, 6, mp->msgid++);
+ capimsg_setu32(s, 8, mp->ncci);
+ capimsg_setu16(s, 12, datahandle);
+ }
+ return nskb;
+}
+
+static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
+{
+ struct sk_buff *nskb;
+ int datalen;
+ u16 errcode, datahandle;
+ struct tty_ldisc *ld;
+
+ datalen = skb->len - CAPIMSG_LEN(skb->data);
+ if (mp->tty == NULL)
+ {
+#ifdef _DEBUG_DATAFLOW
+ printk(KERN_DEBUG "capi: currently no receiver\n");
+#endif
+ return -1;
+ }
+
+ ld = tty_ldisc_ref(mp->tty);
+ if (ld == NULL)
+ return -1;
+ if (ld->receive_buf == NULL) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+ printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n");
+#endif
+ goto bad;
+ }
+ if (mp->ttyinstop) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+ printk(KERN_DEBUG "capi: recv tty throttled\n");
+#endif
+ goto bad;
+ }
+ if (ld->receive_room &&
+ ld->receive_room(mp->tty) < datalen) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+ printk(KERN_DEBUG "capi: no room in tty\n");
+#endif
+ goto bad;
+ }
+ if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) {
+ printk(KERN_ERR "capi: gen_data_b3_resp failed\n");
+ goto bad;
+ }
+ datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4);
+ errcode = capi20_put_message(mp->ap, nskb);
+ if (errcode != CAPI_NOERROR) {
+ printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n",
+ errcode);
+ kfree_skb(nskb);
+ goto bad;
+ }
+ (void)skb_pull(skb, CAPIMSG_LEN(skb->data));
+#ifdef _DEBUG_DATAFLOW
+ printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n",
+ datahandle, skb->len);
+#endif
+ ld->receive_buf(mp->tty, skb->data, NULL, skb->len);
+ kfree_skb(skb);
+ tty_ldisc_deref(ld);
+ return 0;
+bad:
+ tty_ldisc_deref(ld);
+ return -1;
+}
+
+static void handle_minor_recv(struct capiminor *mp)
+{
+ struct sk_buff *skb;
+ while ((skb = skb_dequeue(&mp->inqueue)) != 0) {
+ unsigned int len = skb->len;
+ mp->inbytes -= len;
+ if (handle_recv_skb(mp, skb) < 0) {
+ skb_queue_head(&mp->inqueue, skb);
+ mp->inbytes += len;
+ return;
+ }
+ }
+}
+
+static int handle_minor_send(struct capiminor *mp)
+{
+ struct sk_buff *skb;
+ u16 len;
+ int count = 0;
+ u16 errcode;
+ u16 datahandle;
+
+ if (mp->tty && mp->ttyoutstop) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+ printk(KERN_DEBUG "capi: send: tty stopped\n");
+#endif
+ return 0;
+ }
+
+ while ((skb = skb_dequeue(&mp->outqueue)) != 0) {
+ datahandle = mp->datahandle;
+ len = (u16)skb->len;
+ skb_push(skb, CAPI_DATA_B3_REQ_LEN);
+ memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
+ capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
+ capimsg_setu16(skb->data, 2, mp->ap->applid);
+ capimsg_setu8 (skb->data, 4, CAPI_DATA_B3);
+ capimsg_setu8 (skb->data, 5, CAPI_REQ);
+ capimsg_setu16(skb->data, 6, mp->msgid++);
+ capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */
+ capimsg_setu32(skb->data, 12, (u32) skb->data); /* Data32 */
+ capimsg_setu16(skb->data, 16, len); /* Data length */
+ capimsg_setu16(skb->data, 18, datahandle);
+ capimsg_setu16(skb->data, 20, 0); /* Flags */
+
+ if (capincci_add_ack(mp, datahandle) < 0) {
+ skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
+ skb_queue_head(&mp->outqueue, skb);
+ return count;
+ }
+ errcode = capi20_put_message(mp->ap, skb);
+ if (errcode == CAPI_NOERROR) {
+ mp->datahandle++;
+ count++;
+ mp->outbytes -= len;
+#ifdef _DEBUG_DATAFLOW
+ printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n",
+ datahandle, len);
+#endif
+ continue;
+ }
+ capiminor_del_ack(mp, datahandle);
+
+ if (errcode == CAPI_SENDQUEUEFULL) {
+ skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
+ skb_queue_head(&mp->outqueue, skb);
+ break;
+ }
+
+ /* ups, drop packet */
+ printk(KERN_ERR "capi: put_message = %x\n", errcode);
+ mp->outbytes -= len;
+ kfree_skb(skb);
+ }
+ return count;
+}
+
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+/* -------- function called by lower level -------------------------- */
+
+static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
+{
+ struct capidev *cdev = ap->private;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ struct capiminor *mp;
+ u16 datahandle;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ struct capincci *np;
+ u32 ncci;
+
+ if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) {
+ u16 info = CAPIMSG_U16(skb->data, 12); // Info field
+ if (info == 0) {
+ down(&cdev->ncci_list_sem);
+ capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
+ up(&cdev->ncci_list_sem);
+ }
+ }
+ if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) {
+ down(&cdev->ncci_list_sem);
+ capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
+ up(&cdev->ncci_list_sem);
+ }
+ if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) {
+ skb_queue_tail(&cdev->recvqueue, skb);
+ wake_up_interruptible(&cdev->recvwait);
+ return;
+ }
+ ncci = CAPIMSG_CONTROL(skb->data);
+ for (np = cdev->nccis; np && np->ncci != ncci; np = np->next)
+ ;
+ if (!np) {
+ printk(KERN_ERR "BUG: capi_signal: ncci not found\n");
+ skb_queue_tail(&cdev->recvqueue, skb);
+ wake_up_interruptible(&cdev->recvwait);
+ return;
+ }
+#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE
+ skb_queue_tail(&cdev->recvqueue, skb);
+ wake_up_interruptible(&cdev->recvwait);
+#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ mp = np->minorp;
+ if (!mp) {
+ skb_queue_tail(&cdev->recvqueue, skb);
+ wake_up_interruptible(&cdev->recvwait);
+ return;
+ }
+
+
+ if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) {
+
+ datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2);
+#ifdef _DEBUG_DATAFLOW
+ printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n",
+ datahandle, skb->len-CAPIMSG_LEN(skb->data));
+#endif
+ skb_queue_tail(&mp->inqueue, skb);
+ mp->inbytes += skb->len;
+ handle_minor_recv(mp);
+
+ } else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) {
+
+ datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4);
+#ifdef _DEBUG_DATAFLOW
+ printk(KERN_DEBUG "capi_signal: DATA_B3_CONF %u 0x%x\n",
+ datahandle,
+ CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2));
+#endif
+ kfree_skb(skb);
+ (void)capiminor_del_ack(mp, datahandle);
+ if (mp->tty)
+ tty_wakeup(mp->tty);
+ (void)handle_minor_send(mp);
+
+ } else {
+ /* ups, let capi application handle it :-) */
+ skb_queue_tail(&cdev->recvqueue, skb);
+ wake_up_interruptible(&cdev->recvwait);
+ }
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+}
+
+/* -------- file_operations for capidev ----------------------------- */
+
+static ssize_t
+capi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct capidev *cdev = (struct capidev *)file->private_data;
+ struct sk_buff *skb;
+ size_t copied;
+
+ if (!cdev->ap.applid)
+ return -ENODEV;
+
+ if ((skb = skb_dequeue(&cdev->recvqueue)) == 0) {
+
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ for (;;) {
+ interruptible_sleep_on(&cdev->recvwait);
+ if ((skb = skb_dequeue(&cdev->recvqueue)) != 0)
+ break;
+ if (signal_pending(current))
+ break;
+ }
+ if (skb == 0)
+ return -ERESTARTNOHAND;
+ }
+ if (skb->len > count) {
+ skb_queue_head(&cdev->recvqueue, skb);
+ return -EMSGSIZE;
+ }
+ if (copy_to_user(buf, skb->data, skb->len)) {
+ skb_queue_head(&cdev->recvqueue, skb);
+ return -EFAULT;
+ }
+ copied = skb->len;
+
+ kfree_skb(skb);
+
+ return copied;
+}
+
+static ssize_t
+capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct capidev *cdev = (struct capidev *)file->private_data;
+ struct sk_buff *skb;
+ u16 mlen;
+
+ if (!cdev->ap.applid)
+ return -ENODEV;
+
+ skb = alloc_skb(count, GFP_USER);
+ if (!skb)
+ return -ENOMEM;
+
+ if (copy_from_user(skb_put(skb, count), buf, count)) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+ mlen = CAPIMSG_LEN(skb->data);
+ if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+ if ((size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ } else {
+ if (mlen != count) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ }
+ CAPIMSG_SETAPPID(skb->data, cdev->ap.applid);
+
+ if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) {
+ down(&cdev->ncci_list_sem);
+ capincci_free(cdev, CAPIMSG_NCCI(skb->data));
+ up(&cdev->ncci_list_sem);
+ }
+
+ cdev->errcode = capi20_put_message(&cdev->ap, skb);
+
+ if (cdev->errcode) {
+ kfree_skb(skb);
+ return -EIO;
+ }
+ return count;
+}
+
+static unsigned int
+capi_poll(struct file *file, poll_table * wait)
+{
+ struct capidev *cdev = (struct capidev *)file->private_data;
+ unsigned int mask = 0;
+
+ if (!cdev->ap.applid)
+ return POLLERR;
+
+ poll_wait(file, &(cdev->recvwait), wait);
+ mask = POLLOUT | POLLWRNORM;
+ if (!skb_queue_empty(&cdev->recvqueue))
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+}
+
+static int
+capi_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct capidev *cdev = file->private_data;
+ struct capi20_appl *ap = &cdev->ap;
+ capi_ioctl_struct data;
+ int retval = -EINVAL;
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case CAPI_REGISTER:
+ {
+ if (ap->applid)
+ return -EEXIST;
+
+ if (copy_from_user(&cdev->ap.rparam, argp,
+ sizeof(struct capi_register_params)))
+ return -EFAULT;
+
+ cdev->ap.private = cdev;
+ cdev->ap.recv_message = capi_recv_message;
+ cdev->errcode = capi20_register(ap);
+ if (cdev->errcode) {
+ ap->applid = 0;
+ return -EIO;
+ }
+ }
+ return (int)ap->applid;
+
+ case CAPI_GET_VERSION:
+ {
+ if (copy_from_user(&data.contr, argp,
+ sizeof(data.contr)))
+ return -EFAULT;
+ cdev->errcode = capi20_get_version(data.contr, &data.version);
+ if (cdev->errcode)
+ return -EIO;
+ if (copy_to_user(argp, &data.version,
+ sizeof(data.version)))
+ return -EFAULT;
+ }
+ return 0;
+
+ case CAPI_GET_SERIAL:
+ {
+ if (copy_from_user(&data.contr, argp,
+ sizeof(data.contr)))
+ return -EFAULT;
+ cdev->errcode = capi20_get_serial (data.contr, data.serial);
+ if (cdev->errcode)
+ return -EIO;
+ if (copy_to_user(argp, data.serial,
+ sizeof(data.serial)))
+ return -EFAULT;
+ }
+ return 0;
+ case CAPI_GET_PROFILE:
+ {
+ if (copy_from_user(&data.contr, argp,
+ sizeof(data.contr)))
+ return -EFAULT;
+
+ if (data.contr == 0) {
+ cdev->errcode = capi20_get_profile(data.contr, &data.profile);
+ if (cdev->errcode)
+ return -EIO;
+
+ retval = copy_to_user(argp,
+ &data.profile.ncontroller,
+ sizeof(data.profile.ncontroller));
+
+ } else {
+ cdev->errcode = capi20_get_profile(data.contr, &data.profile);
+ if (cdev->errcode)
+ return -EIO;
+
+ retval = copy_to_user(argp, &data.profile,
+ sizeof(data.profile));
+ }
+ if (retval)
+ return -EFAULT;
+ }
+ return 0;
+
+ case CAPI_GET_MANUFACTURER:
+ {
+ if (copy_from_user(&data.contr, argp,
+ sizeof(data.contr)))
+ return -EFAULT;
+ cdev->errcode = capi20_get_manufacturer(data.contr, data.manufacturer);
+ if (cdev->errcode)
+ return -EIO;
+
+ if (copy_to_user(argp, data.manufacturer,
+ sizeof(data.manufacturer)))
+ return -EFAULT;
+
+ }
+ return 0;
+ case CAPI_GET_ERRCODE:
+ data.errcode = cdev->errcode;
+ cdev->errcode = CAPI_NOERROR;
+ if (arg) {
+ if (copy_to_user(argp, &data.errcode,
+ sizeof(data.errcode)))
+ return -EFAULT;
+ }
+ return data.errcode;
+
+ case CAPI_INSTALLED:
+ if (capi20_isinstalled() == CAPI_NOERROR)
+ return 0;
+ return -ENXIO;
+
+ case CAPI_MANUFACTURER_CMD:
+ {
+ struct capi_manufacturer_cmd mcmd;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (copy_from_user(&mcmd, argp, sizeof(mcmd)))
+ return -EFAULT;
+ return capi20_manufacturer(mcmd.cmd, mcmd.data);
+ }
+ return 0;
+
+ case CAPI_SET_FLAGS:
+ case CAPI_CLR_FLAGS:
+ {
+ unsigned userflags;
+ if (copy_from_user(&userflags, argp,
+ sizeof(userflags)))
+ return -EFAULT;
+ if (cmd == CAPI_SET_FLAGS)
+ cdev->userflags |= userflags;
+ else
+ cdev->userflags &= ~userflags;
+ }
+ return 0;
+
+ case CAPI_GET_FLAGS:
+ if (copy_to_user(argp, &cdev->userflags,
+ sizeof(cdev->userflags)))
+ return -EFAULT;
+ return 0;
+
+ case CAPI_NCCI_OPENCOUNT:
+ {
+ struct capincci *nccip;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ struct capiminor *mp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ unsigned ncci;
+ int count = 0;
+ if (copy_from_user(&ncci, argp, sizeof(ncci)))
+ return -EFAULT;
+
+ down(&cdev->ncci_list_sem);
+ if ((nccip = capincci_find(cdev, (u32) ncci)) == 0) {
+ up(&cdev->ncci_list_sem);
+ return 0;
+ }
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ if ((mp = nccip->minorp) != 0) {
+ count += atomic_read(&mp->ttyopencount);
+ }
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ up(&cdev->ncci_list_sem);
+ return count;
+ }
+ return 0;
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ case CAPI_NCCI_GETUNIT:
+ {
+ struct capincci *nccip;
+ struct capiminor *mp;
+ unsigned ncci;
+ int unit = 0;
+ if (copy_from_user(&ncci, argp,
+ sizeof(ncci)))
+ return -EFAULT;
+ down(&cdev->ncci_list_sem);
+ nccip = capincci_find(cdev, (u32) ncci);
+ if (!nccip || (mp = nccip->minorp) == 0) {
+ up(&cdev->ncci_list_sem);
+ return -ESRCH;
+ }
+ unit = mp->minor;
+ up(&cdev->ncci_list_sem);
+ return unit;
+ }
+ return 0;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ }
+ return -EINVAL;
+}
+
+static int
+capi_open(struct inode *inode, struct file *file)
+{
+ if (file->private_data)
+ return -EEXIST;
+
+ if ((file->private_data = capidev_alloc()) == 0)
+ return -ENOMEM;
+
+ return nonseekable_open(inode, file);
+}
+
+static int
+capi_release(struct inode *inode, struct file *file)
+{
+ struct capidev *cdev = (struct capidev *)file->private_data;
+
+ capidev_free(cdev);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static struct file_operations capi_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = capi_read,
+ .write = capi_write,
+ .poll = capi_poll,
+ .ioctl = capi_ioctl,
+ .open = capi_open,
+ .release = capi_release,
+};
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- tty_operations for capincci ----------------------------- */
+
+static int capinc_tty_open(struct tty_struct * tty, struct file * file)
+{
+ struct capiminor *mp;
+
+ if ((mp = capiminor_find(iminor(file->f_dentry->d_inode))) == 0)
+ return -ENXIO;
+ if (mp->nccip == 0)
+ return -ENXIO;
+
+ tty->driver_data = (void *)mp;
+
+ if (atomic_read(&mp->ttyopencount) == 0)
+ mp->tty = tty;
+ atomic_inc(&mp->ttyopencount);
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount));
+#endif
+ handle_minor_recv(mp);
+ return 0;
+}
+
+static void capinc_tty_close(struct tty_struct * tty, struct file * file)
+{
+ struct capiminor *mp;
+
+ mp = (struct capiminor *)tty->driver_data;
+ if (mp) {
+ if (atomic_dec_and_test(&mp->ttyopencount)) {
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "capinc_tty_close lastclose\n");
+#endif
+ tty->driver_data = NULL;
+ mp->tty = NULL;
+ }
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "capinc_tty_close ocount=%d\n", atomic_read(&mp->ttyopencount));
+#endif
+ if (mp->nccip == 0)
+ capiminor_free(mp);
+ }
+
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "capinc_tty_close\n");
+#endif
+}
+
+static int capinc_tty_write(struct tty_struct * tty,
+ const unsigned char *buf, int count)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ struct sk_buff *skb;
+
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count);
+#endif
+
+ if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_write: mp or mp->ncci NULL\n");
+#endif
+ return 0;
+ }
+
+ skb = mp->ttyskb;
+ if (skb) {
+ mp->ttyskb = NULL;
+ skb_queue_tail(&mp->outqueue, skb);
+ mp->outbytes += skb->len;
+ }
+
+ skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n");
+ return -ENOMEM;
+ }
+
+ skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
+ memcpy(skb_put(skb, count), buf, count);
+
+ skb_queue_tail(&mp->outqueue, skb);
+ mp->outbytes += skb->len;
+ (void)handle_minor_send(mp);
+ (void)handle_minor_recv(mp);
+ return count;
+}
+
+static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ struct sk_buff *skb;
+
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_put_char(%u)\n", ch);
+#endif
+
+ if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_put_char: mp or mp->ncci NULL\n");
+#endif
+ return;
+ }
+
+ skb = mp->ttyskb;
+ if (skb) {
+ if (skb_tailroom(skb) > 0) {
+ *(skb_put(skb, 1)) = ch;
+ return;
+ }
+ mp->ttyskb = NULL;
+ skb_queue_tail(&mp->outqueue, skb);
+ mp->outbytes += skb->len;
+ (void)handle_minor_send(mp);
+ }
+ skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC);
+ if (skb) {
+ skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
+ *(skb_put(skb, 1)) = ch;
+ mp->ttyskb = skb;
+ } else {
+ printk(KERN_ERR "capinc_put_char: char %u lost\n", ch);
+ }
+}
+
+static void capinc_tty_flush_chars(struct tty_struct *tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ struct sk_buff *skb;
+
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_flush_chars\n");
+#endif
+
+ if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_flush_chars: mp or mp->ncci NULL\n");
+#endif
+ return;
+ }
+
+ skb = mp->ttyskb;
+ if (skb) {
+ mp->ttyskb = NULL;
+ skb_queue_tail(&mp->outqueue, skb);
+ mp->outbytes += skb->len;
+ (void)handle_minor_send(mp);
+ }
+ (void)handle_minor_recv(mp);
+}
+
+static int capinc_tty_write_room(struct tty_struct *tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ int room;
+ if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_write_room: mp or mp->ncci NULL\n");
+#endif
+ return 0;
+ }
+ room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue);
+ room *= CAPI_MAX_BLKSIZE;
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_write_room = %d\n", room);
+#endif
+ return room;
+}
+
+int capinc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_chars_in_buffer: mp or mp->ncci NULL\n");
+#endif
+ return 0;
+ }
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n",
+ mp->outbytes, mp->nack,
+ skb_queue_len(&mp->outqueue),
+ skb_queue_len(&mp->inqueue));
+#endif
+ return mp->outbytes;
+}
+
+static int capinc_tty_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int error = 0;
+ switch (cmd) {
+ default:
+ error = n_tty_ioctl (tty, file, cmd, arg);
+ break;
+ }
+ return error;
+}
+
+static void capinc_tty_set_termios(struct tty_struct *tty, struct termios * old)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_set_termios\n");
+#endif
+}
+
+static void capinc_tty_throttle(struct tty_struct * tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_throttle\n");
+#endif
+ if (mp)
+ mp->ttyinstop = 1;
+}
+
+static void capinc_tty_unthrottle(struct tty_struct * tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_unthrottle\n");
+#endif
+ if (mp) {
+ mp->ttyinstop = 0;
+ handle_minor_recv(mp);
+ }
+}
+
+static void capinc_tty_stop(struct tty_struct *tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_stop\n");
+#endif
+ if (mp) {
+ mp->ttyoutstop = 1;
+ }
+}
+
+static void capinc_tty_start(struct tty_struct *tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_start\n");
+#endif
+ if (mp) {
+ mp->ttyoutstop = 0;
+ (void)handle_minor_send(mp);
+ }
+}
+
+static void capinc_tty_hangup(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_hangup\n");
+#endif
+}
+
+static void capinc_tty_break_ctl(struct tty_struct *tty, int state)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state);
+#endif
+}
+
+static void capinc_tty_flush_buffer(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_flush_buffer\n");
+#endif
+}
+
+static void capinc_tty_set_ldisc(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_set_ldisc\n");
+#endif
+}
+
+static void capinc_tty_send_xchar(struct tty_struct *tty, char ch)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_send_xchar(%d)\n", ch);
+#endif
+}
+
+static int capinc_tty_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ return 0;
+}
+
+static struct tty_driver *capinc_tty_driver;
+
+static struct tty_operations capinc_ops = {
+ .open = capinc_tty_open,
+ .close = capinc_tty_close,
+ .write = capinc_tty_write,
+ .put_char = capinc_tty_put_char,
+ .flush_chars = capinc_tty_flush_chars,
+ .write_room = capinc_tty_write_room,
+ .chars_in_buffer = capinc_tty_chars_in_buffer,
+ .ioctl = capinc_tty_ioctl,
+ .set_termios = capinc_tty_set_termios,
+ .throttle = capinc_tty_throttle,
+ .unthrottle = capinc_tty_unthrottle,
+ .stop = capinc_tty_stop,
+ .start = capinc_tty_start,
+ .hangup = capinc_tty_hangup,
+ .break_ctl = capinc_tty_break_ctl,
+ .flush_buffer = capinc_tty_flush_buffer,
+ .set_ldisc = capinc_tty_set_ldisc,
+ .send_xchar = capinc_tty_send_xchar,
+ .read_proc = capinc_tty_read_proc,
+};
+
+static int capinc_tty_init(void)
+{
+ struct tty_driver *drv;
+
+ if (capi_ttyminors > CAPINC_MAX_PORTS)
+ capi_ttyminors = CAPINC_MAX_PORTS;
+ if (capi_ttyminors <= 0)
+ capi_ttyminors = CAPINC_NR_PORTS;
+
+ drv = alloc_tty_driver(capi_ttyminors);
+ if (!drv)
+ return -ENOMEM;
+
+ drv->owner = THIS_MODULE;
+ drv->driver_name = "capi_nc";
+ drv->devfs_name = "capi/";
+ drv->name = "capi";
+ drv->major = capi_ttymajor;
+ drv->minor_start = 0;
+ drv->type = TTY_DRIVER_TYPE_SERIAL;
+ drv->subtype = SERIAL_TYPE_NORMAL;
+ drv->init_termios = tty_std_termios;
+ drv->init_termios.c_iflag = ICRNL;
+ drv->init_termios.c_oflag = OPOST | ONLCR;
+ drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ drv->init_termios.c_lflag = 0;
+ drv->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_RESET_TERMIOS;
+ tty_set_operations(drv, &capinc_ops);
+ if (tty_register_driver(drv)) {
+ put_tty_driver(drv);
+ printk(KERN_ERR "Couldn't register capi_nc driver\n");
+ return -1;
+ }
+ capinc_tty_driver = drv;
+ return 0;
+}
+
+static void capinc_tty_exit(void)
+{
+ struct tty_driver *drv = capinc_tty_driver;
+ int retval;
+ if ((retval = tty_unregister_driver(drv)))
+ printk(KERN_ERR "capi: failed to unregister capi_nc driver (%d)\n", retval);
+ put_tty_driver(drv);
+}
+
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- /proc functions ----------------------------------------- */
+
+/*
+ * /proc/capi/capi20:
+ * minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
+ */
+static int proc_capidev_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capidev *cdev;
+ struct list_head *l;
+ int len = 0;
+
+ read_lock(&capidev_list_lock);
+ list_for_each(l, &capidev_list) {
+ cdev = list_entry(l, struct capidev, list);
+ len += sprintf(page+len, "0 %d %lu %lu %lu %lu\n",
+ cdev->ap.applid,
+ cdev->ap.nrecvctlpkt,
+ cdev->ap.nrecvdatapkt,
+ cdev->ap.nsentctlpkt,
+ cdev->ap.nsentdatapkt);
+ if (len <= off) {
+ off -= len;
+ len = 0;
+ } else {
+ if (len-off > count)
+ goto endloop;
+ }
+ }
+
+endloop:
+ read_unlock(&capidev_list_lock);
+ if (len < count)
+ *eof = 1;
+ if (len > count) len = count;
+ if (len < 0) len = 0;
+ return len;
+}
+
+/*
+ * /proc/capi/capi20ncci:
+ * applid ncci
+ */
+static int proc_capincci_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capidev *cdev;
+ struct capincci *np;
+ struct list_head *l;
+ int len = 0;
+
+ read_lock(&capidev_list_lock);
+ list_for_each(l, &capidev_list) {
+ cdev = list_entry(l, struct capidev, list);
+ for (np=cdev->nccis; np; np = np->next) {
+ len += sprintf(page+len, "%d 0x%x\n",
+ cdev->ap.applid,
+ np->ncci);
+ if (len <= off) {
+ off -= len;
+ len = 0;
+ } else {
+ if (len-off > count)
+ goto endloop;
+ }
+ }
+ }
+endloop:
+ read_unlock(&capidev_list_lock);
+ *start = page+off;
+ if (len < count)
+ *eof = 1;
+ if (len>count) len = count;
+ if (len<0) len = 0;
+ return len;
+}
+
+static struct procfsentries {
+ char *name;
+ mode_t mode;
+ int (*read_proc)(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+ struct proc_dir_entry *procent;
+} procfsentries[] = {
+ /* { "capi", S_IFDIR, 0 }, */
+ { "capi/capi20", 0 , proc_capidev_read_proc },
+ { "capi/capi20ncci", 0 , proc_capincci_read_proc },
+};
+
+static void __init proc_init(void)
+{
+ int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
+ int i;
+
+ for (i=0; i < nelem; i++) {
+ struct procfsentries *p = procfsentries + i;
+ p->procent = create_proc_entry(p->name, p->mode, NULL);
+ if (p->procent) p->procent->read_proc = p->read_proc;
+ }
+}
+
+static void __exit proc_exit(void)
+{
+ int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
+ int i;
+
+ for (i=nelem-1; i >= 0; i--) {
+ struct procfsentries *p = procfsentries + i;
+ if (p->procent) {
+ remove_proc_entry(p->name, NULL);
+ p->procent = NULL;
+ }
+ }
+}
+
+/* -------- init function and module interface ---------------------- */
+
+
+static char rev[32];
+
+static int __init capi_init(void)
+{
+ char *p;
+ char *compileinfo;
+
+ if ((p = strchr(revision, ':')) != 0 && p[1]) {
+ strlcpy(rev, p + 2, sizeof(rev));
+ if ((p = strchr(rev, '$')) != 0 && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ if (register_chrdev(capi_major, "capi20", &capi_fops)) {
+ printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
+ return -EIO;
+ }
+
+ capi_class = class_simple_create(THIS_MODULE, "capi");
+ if (IS_ERR(capi_class)) {
+ unregister_chrdev(capi_major, "capi20");
+ return PTR_ERR(capi_class);
+ }
+
+ class_simple_device_add(capi_class, MKDEV(capi_major, 0), NULL, "capi");
+ devfs_mk_cdev(MKDEV(capi_major, 0), S_IFCHR | S_IRUSR | S_IWUSR,
+ "isdn/capi20");
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ if (capinc_tty_init() < 0) {
+ class_simple_device_remove(MKDEV(capi_major, 0));
+ class_simple_destroy(capi_class);
+ unregister_chrdev(capi_major, "capi20");
+ return -ENOMEM;
+ }
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+ proc_init();
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+ compileinfo = " (middleware+capifs)";
+#else
+ compileinfo = " (no capifs)";
+#endif
+#else
+ compileinfo = " (no middleware)";
+#endif
+ printk(KERN_NOTICE "capi20: Rev %s: started up with major %d%s\n",
+ rev, capi_major, compileinfo);
+
+ return 0;
+}
+
+static void __exit capi_exit(void)
+{
+ proc_exit();
+
+ class_simple_device_remove(MKDEV(capi_major, 0));
+ class_simple_destroy(capi_class);
+ unregister_chrdev(capi_major, "capi20");
+ devfs_remove("isdn/capi20");
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ capinc_tty_exit();
+#endif
+ printk(KERN_NOTICE "capi: Rev %s: unloaded\n", rev);
+}
+
+module_init(capi_init);
+module_exit(capi_exit);
diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c
new file mode 100644
index 000000000000..d10c8b82e6aa
--- /dev/null
+++ b/drivers/isdn/capi/capidrv.c
@@ -0,0 +1,2315 @@
+/* $Id: capidrv.c,v 1.1.2.2 2004/01/12 23:17:24 keil Exp $
+ *
+ * ISDN4Linux Driver, using capi20 interface (kernelcapi)
+ *
+ * Copyright 1997 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#include <linux/isdn.h>
+#include <linux/isdnif.h>
+#include <linux/proc_fs.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capicmd.h>
+#include "capidrv.h"
+
+static char *revision = "$Revision: 1.1.2.2 $";
+static int debugmode = 0;
+
+MODULE_DESCRIPTION("CAPI4Linux: Interface to ISDN4Linux");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+module_param(debugmode, uint, 0);
+
+/* -------- type definitions ----------------------------------------- */
+
+
+struct capidrv_contr {
+
+ struct capidrv_contr *next;
+ struct module *owner;
+ u32 contrnr;
+ char name[20];
+
+ /*
+ * for isdn4linux
+ */
+ isdn_if interface;
+ int myid;
+
+ /*
+ * LISTEN state
+ */
+ int state;
+ u32 cipmask;
+ u32 cipmask2;
+ struct timer_list listentimer;
+
+ /*
+ * ID of capi message sent
+ */
+ u16 msgid;
+
+ /*
+ * B-Channels
+ */
+ int nbchan;
+ struct capidrv_bchan {
+ struct capidrv_contr *contr;
+ u8 msn[ISDN_MSNLEN];
+ int l2;
+ int l3;
+ u8 num[ISDN_MSNLEN];
+ u8 mynum[ISDN_MSNLEN];
+ int si1;
+ int si2;
+ int incoming;
+ int disconnecting;
+ struct capidrv_plci {
+ struct capidrv_plci *next;
+ u32 plci;
+ u32 ncci; /* ncci for CONNECT_ACTIVE_IND */
+ u16 msgid; /* to identfy CONNECT_CONF */
+ int chan;
+ int state;
+ int leasedline;
+ struct capidrv_ncci {
+ struct capidrv_ncci *next;
+ struct capidrv_plci *plcip;
+ u32 ncci;
+ u16 msgid; /* to identfy CONNECT_B3_CONF */
+ int chan;
+ int state;
+ int oldstate;
+ /* */
+ u16 datahandle;
+ struct ncci_datahandle_queue {
+ struct ncci_datahandle_queue *next;
+ u16 datahandle;
+ int len;
+ } *ackqueue;
+ } *ncci_list;
+ } *plcip;
+ struct capidrv_ncci *nccip;
+ } *bchans;
+
+ struct capidrv_plci *plci_list;
+
+ /* for q931 data */
+ u8 q931_buf[4096];
+ u8 *q931_read;
+ u8 *q931_write;
+ u8 *q931_end;
+};
+
+
+struct capidrv_data {
+ struct capi20_appl ap;
+ int ncontr;
+ struct capidrv_contr *contr_list;
+};
+
+typedef struct capidrv_plci capidrv_plci;
+typedef struct capidrv_ncci capidrv_ncci;
+typedef struct capidrv_contr capidrv_contr;
+typedef struct capidrv_data capidrv_data;
+typedef struct capidrv_bchan capidrv_bchan;
+
+/* -------- data definitions ----------------------------------------- */
+
+static capidrv_data global;
+static DEFINE_SPINLOCK(global_lock);
+
+static void handle_dtrace_data(capidrv_contr *card,
+ int send, int level2, u8 *data, u16 len);
+
+/* -------- convert functions ---------------------------------------- */
+
+static inline u32 b1prot(int l2, int l3)
+{
+ switch (l2) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ return 0;
+ case ISDN_PROTO_L2_HDLC:
+ default:
+ return 0;
+ case ISDN_PROTO_L2_TRANS:
+ return 1;
+ case ISDN_PROTO_L2_V11096:
+ case ISDN_PROTO_L2_V11019:
+ case ISDN_PROTO_L2_V11038:
+ return 2;
+ case ISDN_PROTO_L2_FAX:
+ return 4;
+ case ISDN_PROTO_L2_MODEM:
+ return 8;
+ }
+}
+
+static inline u32 b2prot(int l2, int l3)
+{
+ switch (l2) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ default:
+ return 0;
+ case ISDN_PROTO_L2_HDLC:
+ case ISDN_PROTO_L2_TRANS:
+ case ISDN_PROTO_L2_V11096:
+ case ISDN_PROTO_L2_V11019:
+ case ISDN_PROTO_L2_V11038:
+ case ISDN_PROTO_L2_MODEM:
+ return 1;
+ case ISDN_PROTO_L2_FAX:
+ return 4;
+ }
+}
+
+static inline u32 b3prot(int l2, int l3)
+{
+ switch (l2) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ case ISDN_PROTO_L2_HDLC:
+ case ISDN_PROTO_L2_TRANS:
+ case ISDN_PROTO_L2_V11096:
+ case ISDN_PROTO_L2_V11019:
+ case ISDN_PROTO_L2_V11038:
+ case ISDN_PROTO_L2_MODEM:
+ default:
+ return 0;
+ case ISDN_PROTO_L2_FAX:
+ return 4;
+ }
+}
+
+static _cstruct b1config_async_v110(u16 rate)
+{
+ /* CAPI-Spec "B1 Configuration" */
+ static unsigned char buf[9];
+ buf[0] = 8; /* len */
+ /* maximum bitrate */
+ buf[1] = rate & 0xff; buf[2] = (rate >> 8) & 0xff;
+ buf[3] = 8; buf[4] = 0; /* 8 bits per character */
+ buf[5] = 0; buf[6] = 0; /* parity none */
+ buf[7] = 0; buf[8] = 0; /* 1 stop bit */
+ return buf;
+}
+
+static _cstruct b1config(int l2, int l3)
+{
+ switch (l2) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ case ISDN_PROTO_L2_HDLC:
+ case ISDN_PROTO_L2_TRANS:
+ default:
+ return NULL;
+ case ISDN_PROTO_L2_V11096:
+ return b1config_async_v110(9600);
+ case ISDN_PROTO_L2_V11019:
+ return b1config_async_v110(19200);
+ case ISDN_PROTO_L2_V11038:
+ return b1config_async_v110(38400);
+ }
+}
+
+static inline u16 si2cip(u8 si1, u8 si2)
+{
+ static const u8 cip[17][5] =
+ {
+ /* 0 1 2 3 4 */
+ {0, 0, 0, 0, 0}, /*0 */
+ {16, 16, 4, 26, 16}, /*1 */
+ {17, 17, 17, 4, 4}, /*2 */
+ {2, 2, 2, 2, 2}, /*3 */
+ {18, 18, 18, 18, 18}, /*4 */
+ {2, 2, 2, 2, 2}, /*5 */
+ {0, 0, 0, 0, 0}, /*6 */
+ {2, 2, 2, 2, 2}, /*7 */
+ {2, 2, 2, 2, 2}, /*8 */
+ {21, 21, 21, 21, 21}, /*9 */
+ {19, 19, 19, 19, 19}, /*10 */
+ {0, 0, 0, 0, 0}, /*11 */
+ {0, 0, 0, 0, 0}, /*12 */
+ {0, 0, 0, 0, 0}, /*13 */
+ {0, 0, 0, 0, 0}, /*14 */
+ {22, 22, 22, 22, 22}, /*15 */
+ {27, 27, 27, 28, 27} /*16 */
+ };
+ if (si1 > 16)
+ si1 = 0;
+ if (si2 > 4)
+ si2 = 0;
+
+ return (u16) cip[si1][si2];
+}
+
+static inline u8 cip2si1(u16 cipval)
+{
+ static const u8 si[32] =
+ {7, 1, 7, 7, 1, 1, 7, 7, /*0-7 */
+ 7, 1, 0, 0, 0, 0, 0, 0, /*8-15 */
+ 1, 2, 4, 10, 9, 9, 15, 7, /*16-23 */
+ 7, 7, 1, 16, 16, 0, 0, 0}; /*24-31 */
+
+ if (cipval > 31)
+ cipval = 0; /* .... */
+ return si[cipval];
+}
+
+static inline u8 cip2si2(u16 cipval)
+{
+ static const u8 si[32] =
+ {0, 0, 0, 0, 2, 3, 0, 0, /*0-7 */
+ 0, 3, 0, 0, 0, 0, 0, 0, /*8-15 */
+ 1, 2, 0, 0, 9, 0, 0, 0, /*16-23 */
+ 0, 0, 3, 2, 3, 0, 0, 0}; /*24-31 */
+
+ if (cipval > 31)
+ cipval = 0; /* .... */
+ return si[cipval];
+}
+
+
+/* -------- controller management ------------------------------------- */
+
+static inline capidrv_contr *findcontrbydriverid(int driverid)
+{
+ unsigned long flags;
+ capidrv_contr *p;
+
+ spin_lock_irqsave(&global_lock, flags);
+ for (p = global.contr_list; p; p = p->next)
+ if (p->myid == driverid)
+ break;
+ spin_unlock_irqrestore(&global_lock, flags);
+ return p;
+}
+
+static capidrv_contr *findcontrbynumber(u32 contr)
+{
+ unsigned long flags;
+ capidrv_contr *p = global.contr_list;
+
+ spin_lock_irqsave(&global_lock, flags);
+ for (p = global.contr_list; p; p = p->next)
+ if (p->contrnr == contr)
+ break;
+ spin_unlock_irqrestore(&global_lock, flags);
+ return p;
+}
+
+
+/* -------- plci management ------------------------------------------ */
+
+static capidrv_plci *new_plci(capidrv_contr * card, int chan)
+{
+ capidrv_plci *plcip;
+
+ plcip = (capidrv_plci *) kmalloc(sizeof(capidrv_plci), GFP_ATOMIC);
+
+ if (plcip == 0)
+ return NULL;
+
+ memset(plcip, 0, sizeof(capidrv_plci));
+ plcip->state = ST_PLCI_NONE;
+ plcip->plci = 0;
+ plcip->msgid = 0;
+ plcip->chan = chan;
+ plcip->next = card->plci_list;
+ card->plci_list = plcip;
+ card->bchans[chan].plcip = plcip;
+
+ return plcip;
+}
+
+static capidrv_plci *find_plci_by_plci(capidrv_contr * card, u32 plci)
+{
+ capidrv_plci *p;
+ for (p = card->plci_list; p; p = p->next)
+ if (p->plci == plci)
+ return p;
+ return NULL;
+}
+
+static capidrv_plci *find_plci_by_msgid(capidrv_contr * card, u16 msgid)
+{
+ capidrv_plci *p;
+ for (p = card->plci_list; p; p = p->next)
+ if (p->msgid == msgid)
+ return p;
+ return NULL;
+}
+
+static capidrv_plci *find_plci_by_ncci(capidrv_contr * card, u32 ncci)
+{
+ capidrv_plci *p;
+ for (p = card->plci_list; p; p = p->next)
+ if (p->plci == (ncci & 0xffff))
+ return p;
+ return NULL;
+}
+
+static void free_plci(capidrv_contr * card, capidrv_plci * plcip)
+{
+ capidrv_plci **pp;
+
+ for (pp = &card->plci_list; *pp; pp = &(*pp)->next) {
+ if (*pp == plcip) {
+ *pp = (*pp)->next;
+ card->bchans[plcip->chan].plcip = NULL;
+ card->bchans[plcip->chan].disconnecting = 0;
+ card->bchans[plcip->chan].incoming = 0;
+ kfree(plcip);
+ return;
+ }
+ }
+ printk(KERN_ERR "capidrv-%d: free_plci %p (0x%x) not found, Huh?\n",
+ card->contrnr, plcip, plcip->plci);
+}
+
+/* -------- ncci management ------------------------------------------ */
+
+static inline capidrv_ncci *new_ncci(capidrv_contr * card,
+ capidrv_plci * plcip,
+ u32 ncci)
+{
+ capidrv_ncci *nccip;
+
+ nccip = (capidrv_ncci *) kmalloc(sizeof(capidrv_ncci), GFP_ATOMIC);
+
+ if (nccip == 0)
+ return NULL;
+
+ memset(nccip, 0, sizeof(capidrv_ncci));
+ nccip->ncci = ncci;
+ nccip->state = ST_NCCI_NONE;
+ nccip->plcip = plcip;
+ nccip->chan = plcip->chan;
+ nccip->datahandle = 0;
+
+ nccip->next = plcip->ncci_list;
+ plcip->ncci_list = nccip;
+
+ card->bchans[plcip->chan].nccip = nccip;
+
+ return nccip;
+}
+
+static inline capidrv_ncci *find_ncci(capidrv_contr * card, u32 ncci)
+{
+ capidrv_plci *plcip;
+ capidrv_ncci *p;
+
+ if ((plcip = find_plci_by_ncci(card, ncci)) == 0)
+ return NULL;
+
+ for (p = plcip->ncci_list; p; p = p->next)
+ if (p->ncci == ncci)
+ return p;
+ return NULL;
+}
+
+static inline capidrv_ncci *find_ncci_by_msgid(capidrv_contr * card,
+ u32 ncci, u16 msgid)
+{
+ capidrv_plci *plcip;
+ capidrv_ncci *p;
+
+ if ((plcip = find_plci_by_ncci(card, ncci)) == 0)
+ return NULL;
+
+ for (p = plcip->ncci_list; p; p = p->next)
+ if (p->msgid == msgid)
+ return p;
+ return NULL;
+}
+
+static void free_ncci(capidrv_contr * card, struct capidrv_ncci *nccip)
+{
+ struct capidrv_ncci **pp;
+
+ for (pp = &(nccip->plcip->ncci_list); *pp; pp = &(*pp)->next) {
+ if (*pp == nccip) {
+ *pp = (*pp)->next;
+ break;
+ }
+ }
+ card->bchans[nccip->chan].nccip = NULL;
+ kfree(nccip);
+}
+
+static int capidrv_add_ack(struct capidrv_ncci *nccip,
+ u16 datahandle, int len)
+{
+ struct ncci_datahandle_queue *n, **pp;
+
+ n = (struct ncci_datahandle_queue *)
+ kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC);
+ if (!n) {
+ printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n");
+ return -1;
+ }
+ n->next = NULL;
+ n->datahandle = datahandle;
+ n->len = len;
+ for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) ;
+ *pp = n;
+ return 0;
+}
+
+static int capidrv_del_ack(struct capidrv_ncci *nccip, u16 datahandle)
+{
+ struct ncci_datahandle_queue **pp, *p;
+ int len;
+
+ for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) {
+ if ((*pp)->datahandle == datahandle) {
+ p = *pp;
+ len = p->len;
+ *pp = (*pp)->next;
+ kfree(p);
+ return len;
+ }
+ }
+ return -1;
+}
+
+/* -------- convert and send capi message ---------------------------- */
+
+static void send_message(capidrv_contr * card, _cmsg * cmsg)
+{
+ struct sk_buff *skb;
+ size_t len;
+ capi_cmsg2message(cmsg, cmsg->buf);
+ len = CAPIMSG_LEN(cmsg->buf);
+ skb = alloc_skb(len, GFP_ATOMIC);
+ memcpy(skb_put(skb, len), cmsg->buf, len);
+ if (capi20_put_message(&global.ap, skb) != CAPI_NOERROR)
+ kfree_skb(skb);
+}
+
+/* -------- state machine -------------------------------------------- */
+
+struct listenstatechange {
+ int actstate;
+ int nextstate;
+ int event;
+};
+
+static struct listenstatechange listentable[] =
+{
+ {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ},
+ {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ},
+ {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR},
+ {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR},
+ {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+ {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+ {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+ {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+ {},
+};
+
+static void listen_change_state(capidrv_contr * card, int event)
+{
+ struct listenstatechange *p = listentable;
+ while (p->event) {
+ if (card->state == p->actstate && p->event == event) {
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: listen_change_state %d -> %d\n",
+ card->contrnr, card->state, p->nextstate);
+ card->state = p->nextstate;
+ return;
+ }
+ p++;
+ }
+ printk(KERN_ERR "capidrv-%d: listen_change_state state=%d event=%d ????\n",
+ card->contrnr, card->state, event);
+
+}
+
+/* ------------------------------------------------------------------ */
+
+static void p0(capidrv_contr * card, capidrv_plci * plci)
+{
+ isdn_ctrl cmd;
+
+ card->bchans[plci->chan].contr = NULL;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.driver = card->myid;
+ cmd.arg = plci->chan;
+ card->interface.statcallb(&cmd);
+ free_plci(card, plci);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct plcistatechange {
+ int actstate;
+ int nextstate;
+ int event;
+ void (*changefunc) (capidrv_contr * card, capidrv_plci * plci);
+};
+
+static struct plcistatechange plcitable[] =
+{
+ /* P-0 */
+ {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, NULL},
+ {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, NULL},
+ {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, NULL},
+ {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, NULL},
+ /* P-0.1 */
+ {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0},
+ {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, NULL},
+ /* P-1 */
+ {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, NULL},
+ {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+ {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+ {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ /* P-ACT */
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, NULL},
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_IND, NULL},
+ /* P-2 */
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_IND, NULL},
+ /* P-3 */
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, NULL},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, NULL},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ /* P-4 */
+ {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, NULL},
+ {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+ {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+ {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ /* P-5 */
+ {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ /* P-6 */
+ {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0},
+ /* P-0.Res */
+ {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0},
+ {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, NULL},
+ /* P-RES */
+ {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, NULL},
+ /* P-HELD */
+ {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, NULL},
+ {},
+};
+
+static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event)
+{
+ struct plcistatechange *p = plcitable;
+ while (p->event) {
+ if (plci->state == p->actstate && p->event == event) {
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: plci_change_state:0x%x %d -> %d\n",
+ card->contrnr, plci->plci, plci->state, p->nextstate);
+ plci->state = p->nextstate;
+ if (p->changefunc)
+ p->changefunc(card, plci);
+ return;
+ }
+ p++;
+ }
+ printk(KERN_ERR "capidrv-%d: plci_change_state:0x%x state=%d event=%d ????\n",
+ card->contrnr, plci->plci, plci->state, event);
+}
+
+/* ------------------------------------------------------------------ */
+
+static _cmsg cmsg;
+
+static void n0(capidrv_contr * card, capidrv_ncci * ncci)
+{
+ isdn_ctrl cmd;
+
+ capi_fill_DISCONNECT_REQ(&cmsg,
+ global.ap.applid,
+ card->msgid++,
+ ncci->plcip->plci,
+ NULL, /* BChannelinformation */
+ NULL, /* Keypadfacility */
+ NULL, /* Useruserdata */ /* $$$$ */
+ NULL /* Facilitydataarray */
+ );
+ send_message(card, &cmsg);
+ plci_change_state(card, ncci->plcip, EV_PLCI_DISCONNECT_REQ);
+
+ cmd.command = ISDN_STAT_BHUP;
+ cmd.driver = card->myid;
+ cmd.arg = ncci->chan;
+ card->interface.statcallb(&cmd);
+ free_ncci(card, ncci);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct nccistatechange {
+ int actstate;
+ int nextstate;
+ int event;
+ void (*changefunc) (capidrv_contr * card, capidrv_ncci * ncci);
+};
+
+static struct nccistatechange nccitable[] =
+{
+ /* N-0 */
+ {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, NULL},
+ {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, NULL},
+ /* N-0.1 */
+ {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, NULL},
+ {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, n0},
+ /* N-1 */
+ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, NULL},
+ {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, NULL},
+ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+ /* N-2 */
+ {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, NULL},
+ {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+ {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+ /* N-ACT */
+ {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, NULL},
+ {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, NULL},
+ {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+ {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+ /* N-3 */
+ {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, NULL},
+ {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+ {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+ /* N-4 */
+ {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+ {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR,NULL},
+ /* N-5 */
+ {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0},
+ {},
+};
+
+static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int event)
+{
+ struct nccistatechange *p = nccitable;
+ while (p->event) {
+ if (ncci->state == p->actstate && p->event == event) {
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ncci_change_state:0x%x %d -> %d\n",
+ card->contrnr, ncci->ncci, ncci->state, p->nextstate);
+ if (p->nextstate == ST_NCCI_PREVIOUS) {
+ ncci->state = ncci->oldstate;
+ ncci->oldstate = p->actstate;
+ } else {
+ ncci->oldstate = p->actstate;
+ ncci->state = p->nextstate;
+ }
+ if (p->changefunc)
+ p->changefunc(card, ncci);
+ return;
+ }
+ p++;
+ }
+ printk(KERN_ERR "capidrv-%d: ncci_change_state:0x%x state=%d event=%d ????\n",
+ card->contrnr, ncci->ncci, ncci->state, event);
+}
+
+/* ------------------------------------------------------------------- */
+
+static inline int new_bchan(capidrv_contr * card)
+{
+ int i;
+ for (i = 0; i < card->nbchan; i++) {
+ if (card->bchans[i].plcip == 0) {
+ card->bchans[i].disconnecting = 0;
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* ------------------------------------------------------------------- */
+
+static void handle_controller(_cmsg * cmsg)
+{
+ capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController & 0x7f);
+ return;
+ }
+ switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+ case CAPI_LISTEN_CONF: /* Controller */
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: listenconf Info=0x%4x (%s) cipmask=0x%x\n",
+ card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->cipmask);
+ if (cmsg->Info) {
+ listen_change_state(card, EV_LISTEN_CONF_ERROR);
+ } else if (card->cipmask == 0) {
+ listen_change_state(card, EV_LISTEN_CONF_EMPTY);
+ } else {
+ listen_change_state(card, EV_LISTEN_CONF_OK);
+ }
+ break;
+
+ case CAPI_MANUFACTURER_IND: /* Controller */
+ if ( cmsg->ManuID == 0x214D5641
+ && cmsg->Class == 0
+ && cmsg->Function == 1) {
+ u8 *data = cmsg->ManuData+3;
+ u16 len = cmsg->ManuData[0];
+ u16 layer;
+ int direction;
+ if (len == 255) {
+ len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8));
+ data += 2;
+ }
+ len -= 2;
+ layer = ((*(data-1)) << 8) | *(data-2);
+ if (layer & 0x300)
+ direction = (layer & 0x200) ? 0 : 1;
+ else direction = (layer & 0x800) ? 0 : 1;
+ if (layer & 0x0C00) {
+ if ((layer & 0xff) == 0x80) {
+ handle_dtrace_data(card, direction, 1, data, len);
+ break;
+ }
+ } else if ((layer & 0xff) < 0x80) {
+ handle_dtrace_data(card, direction, 0, data, len);
+ break;
+ }
+ printk(KERN_INFO "capidrv-%d: %s from controller 0x%x layer 0x%x, ignored\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController, layer);
+ break;
+ }
+ goto ignored;
+ case CAPI_MANUFACTURER_CONF: /* Controller */
+ if (cmsg->ManuID == 0x214D5641) {
+ char *s = NULL;
+ switch (cmsg->Class) {
+ case 0: break;
+ case 1: s = "unknown class"; break;
+ case 2: s = "unknown function"; break;
+ default: s = "unkown error"; break;
+ }
+ if (s)
+ printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController,
+ cmsg->Function, s);
+ break;
+ }
+ goto ignored;
+ case CAPI_FACILITY_IND: /* Controller/plci/ncci */
+ goto ignored;
+ case CAPI_FACILITY_CONF: /* Controller/plci/ncci */
+ goto ignored;
+ case CAPI_INFO_IND: /* Controller/plci */
+ goto ignored;
+ case CAPI_INFO_CONF: /* Controller/plci */
+ goto ignored;
+
+ default:
+ printk(KERN_ERR "capidrv-%d: got %s from controller 0x%x ???",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController);
+ }
+ return;
+
+ ignored:
+ printk(KERN_INFO "capidrv-%d: %s from controller 0x%x ignored\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController);
+}
+
+static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg)
+{
+ capidrv_plci *plcip;
+ capidrv_bchan *bchan;
+ isdn_ctrl cmd;
+ int chan;
+
+ if ((chan = new_bchan(card)) == -1) {
+ printk(KERN_ERR "capidrv-%d: incoming call on not existing bchan ?\n", card->contrnr);
+ return;
+ }
+ bchan = &card->bchans[chan];
+ if ((plcip = new_plci(card, chan)) == 0) {
+ printk(KERN_ERR "capidrv-%d: incoming call: no memory, sorry.\n", card->contrnr);
+ return;
+ }
+ bchan->incoming = 1;
+ plcip->plci = cmsg->adr.adrPLCI;
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_IND);
+
+ cmd.command = ISDN_STAT_ICALL;
+ cmd.driver = card->myid;
+ cmd.arg = chan;
+ memset(&cmd.parm.setup, 0, sizeof(cmd.parm.setup));
+ strncpy(cmd.parm.setup.phone,
+ cmsg->CallingPartyNumber + 3,
+ cmsg->CallingPartyNumber[0] - 2);
+ strncpy(cmd.parm.setup.eazmsn,
+ cmsg->CalledPartyNumber + 2,
+ cmsg->CalledPartyNumber[0] - 1);
+ cmd.parm.setup.si1 = cip2si1(cmsg->CIPValue);
+ cmd.parm.setup.si2 = cip2si2(cmsg->CIPValue);
+ cmd.parm.setup.plan = cmsg->CallingPartyNumber[1];
+ cmd.parm.setup.screen = cmsg->CallingPartyNumber[2];
+
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s\n",
+ card->contrnr,
+ cmd.parm.setup.phone,
+ cmd.parm.setup.si1,
+ cmd.parm.setup.si2,
+ cmd.parm.setup.eazmsn);
+
+ if (cmd.parm.setup.si1 == 1 && cmd.parm.setup.si2 != 0) {
+ printk(KERN_INFO "capidrv-%d: patching si2=%d to 0 for VBOX\n",
+ card->contrnr,
+ cmd.parm.setup.si2);
+ cmd.parm.setup.si2 = 0;
+ }
+
+ switch (card->interface.statcallb(&cmd)) {
+ case 0:
+ case 3:
+ /* No device matching this call.
+ * and isdn_common.c has send a HANGUP command
+ * which is ignored in state ST_PLCI_INCOMING,
+ * so we send RESP to ignore the call
+ */
+ capi_cmsg_answer(cmsg);
+ cmsg->Reject = 1; /* ignore */
+ send_message(card, cmsg);
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n",
+ card->contrnr,
+ cmd.parm.setup.phone,
+ cmd.parm.setup.si1,
+ cmd.parm.setup.si2,
+ cmd.parm.setup.eazmsn);
+ break;
+ case 1:
+ /* At least one device matching this call (RING on ttyI)
+ * HL-driver may send ALERTING on the D-channel in this
+ * case.
+ * really means: RING on ttyI or a net interface
+ * accepted this call already.
+ *
+ * If the call was accepted, state has already changed,
+ * and CONNECT_RESP already sent.
+ */
+ if (plcip->state == ST_PLCI_INCOMING) {
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s tty alerting\n",
+ card->contrnr,
+ cmd.parm.setup.phone,
+ cmd.parm.setup.si1,
+ cmd.parm.setup.si2,
+ cmd.parm.setup.eazmsn);
+ capi_fill_ALERT_REQ(cmsg,
+ global.ap.applid,
+ card->msgid++,
+ plcip->plci, /* adr */
+ NULL,/* BChannelinformation */
+ NULL,/* Keypadfacility */
+ NULL,/* Useruserdata */
+ NULL /* Facilitydataarray */
+ );
+ plcip->msgid = cmsg->Messagenumber;
+ send_message(card, cmsg);
+ } else {
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s on netdev\n",
+ card->contrnr,
+ cmd.parm.setup.phone,
+ cmd.parm.setup.si1,
+ cmd.parm.setup.si2,
+ cmd.parm.setup.eazmsn);
+ }
+ break;
+
+ case 2: /* Call will be rejected. */
+ capi_cmsg_answer(cmsg);
+ cmsg->Reject = 2; /* reject call, normal call clearing */
+ send_message(card, cmsg);
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+ break;
+
+ default:
+ /* An error happened. (Invalid parameters for example.) */
+ capi_cmsg_answer(cmsg);
+ cmsg->Reject = 8; /* reject call,
+ destination out of order */
+ send_message(card, cmsg);
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+ break;
+ }
+ return;
+}
+
+static void handle_plci(_cmsg * cmsg)
+{
+ capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+ capidrv_plci *plcip;
+ isdn_ctrl cmd;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController & 0x7f);
+ return;
+ }
+ switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+ case CAPI_DISCONNECT_IND: /* plci */
+ if (cmsg->Reason) {
+ printk(KERN_INFO "capidrv-%d: %s reason 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI);
+ }
+ if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) {
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ goto notfound;
+ }
+ card->bchans[plcip->chan].disconnecting = 1;
+ plci_change_state(card, plcip, EV_PLCI_DISCONNECT_IND);
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ plci_change_state(card, plcip, EV_PLCI_DISCONNECT_RESP);
+ break;
+
+ case CAPI_DISCONNECT_CONF: /* plci */
+ if (cmsg->Info) {
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Info, capi_info2str(cmsg->Info),
+ cmsg->adr.adrPLCI);
+ }
+ if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+ goto notfound;
+
+ card->bchans[plcip->chan].disconnecting = 1;
+ break;
+
+ case CAPI_ALERT_CONF: /* plci */
+ if (cmsg->Info) {
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Info, capi_info2str(cmsg->Info),
+ cmsg->adr.adrPLCI);
+ }
+ break;
+
+ case CAPI_CONNECT_IND: /* plci */
+ handle_incoming_call(card, cmsg);
+ break;
+
+ case CAPI_CONNECT_CONF: /* plci */
+ if (cmsg->Info) {
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Info, capi_info2str(cmsg->Info),
+ cmsg->adr.adrPLCI);
+ }
+ if (!(plcip = find_plci_by_msgid(card, cmsg->Messagenumber)))
+ goto notfound;
+
+ plcip->plci = cmsg->adr.adrPLCI;
+ if (cmsg->Info) {
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_ERROR);
+ } else {
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_OK);
+ }
+ break;
+
+ case CAPI_CONNECT_ACTIVE_IND: /* plci */
+
+ if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+ goto notfound;
+
+ if (card->bchans[plcip->chan].incoming) {
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND);
+ } else {
+ capidrv_ncci *nccip;
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+
+ nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI);
+
+ if (!nccip) {
+ printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr);
+ break; /* $$$$ */
+ }
+ capi_fill_CONNECT_B3_REQ(cmsg,
+ global.ap.applid,
+ card->msgid++,
+ plcip->plci, /* adr */
+ NULL /* NCPI */
+ );
+ nccip->msgid = cmsg->Messagenumber;
+ send_message(card, cmsg);
+ cmd.command = ISDN_STAT_DCONN;
+ cmd.driver = card->myid;
+ cmd.arg = plcip->chan;
+ card->interface.statcallb(&cmd);
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND);
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_REQ);
+ }
+ break;
+
+ case CAPI_INFO_IND: /* Controller/plci */
+
+ if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+ goto notfound;
+
+ if (cmsg->InfoNumber == 0x4000) {
+ if (cmsg->InfoElement[0] == 4) {
+ cmd.command = ISDN_STAT_CINF;
+ cmd.driver = card->myid;
+ cmd.arg = plcip->chan;
+ sprintf(cmd.parm.num, "%lu",
+ (unsigned long)
+ ((u32) cmsg->InfoElement[1]
+ | ((u32) (cmsg->InfoElement[2]) << 8)
+ | ((u32) (cmsg->InfoElement[3]) << 16)
+ | ((u32) (cmsg->InfoElement[4]) << 24)));
+ card->interface.statcallb(&cmd);
+ break;
+ }
+ }
+ printk(KERN_ERR "capidrv-%d: %s\n",
+ card->contrnr, capi_cmsg2str(cmsg));
+ break;
+
+ case CAPI_CONNECT_ACTIVE_CONF: /* plci */
+ goto ignored;
+ case CAPI_SELECT_B_PROTOCOL_CONF: /* plci */
+ goto ignored;
+ case CAPI_FACILITY_IND: /* Controller/plci/ncci */
+ goto ignored;
+ case CAPI_FACILITY_CONF: /* Controller/plci/ncci */
+ goto ignored;
+
+ case CAPI_INFO_CONF: /* Controller/plci */
+ goto ignored;
+
+ default:
+ printk(KERN_ERR "capidrv-%d: got %s for plci 0x%x ???",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrPLCI);
+ }
+ return;
+ ignored:
+ printk(KERN_INFO "capidrv-%d: %s for plci 0x%x ignored\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrPLCI);
+ return;
+ notfound:
+ printk(KERN_ERR "capidrv-%d: %s: plci 0x%x not found\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrPLCI);
+ return;
+}
+
+static void handle_ncci(_cmsg * cmsg)
+{
+ capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+ capidrv_plci *plcip;
+ capidrv_ncci *nccip;
+ isdn_ctrl cmd;
+ int len;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController & 0x7f);
+ return;
+ }
+ switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+ case CAPI_CONNECT_B3_ACTIVE_IND: /* ncci */
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_ACTIVE_IND);
+
+ cmd.command = ISDN_STAT_BCONN;
+ cmd.driver = card->myid;
+ cmd.arg = nccip->chan;
+ card->interface.statcallb(&cmd);
+
+ printk(KERN_INFO "capidrv-%d: chan %d up with ncci 0x%x\n",
+ card->contrnr, nccip->chan, nccip->ncci);
+ break;
+
+ case CAPI_CONNECT_B3_ACTIVE_CONF: /* ncci */
+ goto ignored;
+
+ case CAPI_CONNECT_B3_IND: /* ncci */
+
+ plcip = find_plci_by_ncci(card, cmsg->adr.adrNCCI);
+ if (plcip) {
+ nccip = new_ncci(card, plcip, cmsg->adr.adrNCCI);
+ if (nccip) {
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_IND);
+ capi_fill_CONNECT_B3_RESP(cmsg,
+ global.ap.applid,
+ card->msgid++,
+ nccip->ncci, /* adr */
+ 0, /* Reject */
+ NULL /* NCPI */
+ );
+ send_message(card, cmsg);
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP);
+ break;
+ }
+ printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr);
+ } else {
+ printk(KERN_ERR "capidrv-%d: %s: plci for ncci 0x%x not found\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrNCCI);
+ }
+ capi_fill_CONNECT_B3_RESP(cmsg,
+ global.ap.applid,
+ card->msgid++,
+ cmsg->adr.adrNCCI,
+ 2, /* Reject */
+ NULL /* NCPI */
+ );
+ send_message(card, cmsg);
+ break;
+
+ case CAPI_CONNECT_B3_CONF: /* ncci */
+
+ if (!(nccip = find_ncci_by_msgid(card,
+ cmsg->adr.adrNCCI,
+ cmsg->Messagenumber)))
+ goto notfound;
+
+ nccip->ncci = cmsg->adr.adrNCCI;
+ if (cmsg->Info) {
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Info, capi_info2str(cmsg->Info),
+ cmsg->adr.adrNCCI);
+ }
+
+ if (cmsg->Info)
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_ERROR);
+ else
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_OK);
+ break;
+
+ case CAPI_CONNECT_B3_T90_ACTIVE_IND: /* ncci */
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ break;
+
+ case CAPI_DATA_B3_IND: /* ncci */
+ /* handled in handle_data() */
+ goto ignored;
+
+ case CAPI_DATA_B3_CONF: /* ncci */
+ if (cmsg->Info) {
+ printk(KERN_WARNING "CAPI_DATA_B3_CONF: Info %x - %s\n",
+ cmsg->Info, capi_info2str(cmsg->Info));
+ }
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+
+ len = capidrv_del_ack(nccip, cmsg->DataHandle);
+ if (len < 0)
+ break;
+ cmd.command = ISDN_STAT_BSENT;
+ cmd.driver = card->myid;
+ cmd.arg = nccip->chan;
+ cmd.parm.length = len;
+ card->interface.statcallb(&cmd);
+ break;
+
+ case CAPI_DISCONNECT_B3_IND: /* ncci */
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+
+ card->bchans[nccip->chan].disconnecting = 1;
+ ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_IND);
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_RESP);
+ break;
+
+ case CAPI_DISCONNECT_B3_CONF: /* ncci */
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+ if (cmsg->Info) {
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Info, capi_info2str(cmsg->Info),
+ cmsg->adr.adrNCCI);
+ ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_CONF_ERROR);
+ }
+ break;
+
+ case CAPI_RESET_B3_IND: /* ncci */
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+ ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND);
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ break;
+
+ case CAPI_RESET_B3_CONF: /* ncci */
+ goto ignored; /* $$$$ */
+
+ case CAPI_FACILITY_IND: /* Controller/plci/ncci */
+ goto ignored;
+ case CAPI_FACILITY_CONF: /* Controller/plci/ncci */
+ goto ignored;
+
+ default:
+ printk(KERN_ERR "capidrv-%d: got %s for ncci 0x%x ???",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrNCCI);
+ }
+ return;
+ ignored:
+ printk(KERN_INFO "capidrv-%d: %s for ncci 0x%x ignored\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrNCCI);
+ return;
+ notfound:
+ printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrNCCI);
+}
+
+
+static void handle_data(_cmsg * cmsg, struct sk_buff *skb)
+{
+ capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+ capidrv_ncci *nccip;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController & 0x7f);
+ kfree_skb(skb);
+ return;
+ }
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) {
+ printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrNCCI);
+ kfree_skb(skb);
+ return;
+ }
+ (void) skb_pull(skb, CAPIMSG_LEN(skb->data));
+ card->interface.rcvcallb_skb(card->myid, nccip->chan, skb);
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+}
+
+static _cmsg s_cmsg;
+
+static void capidrv_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
+{
+ capi_message2cmsg(&s_cmsg, skb->data);
+ if (debugmode > 3)
+ printk(KERN_DEBUG "capidrv_signal: applid=%d %s\n",
+ ap->applid, capi_cmsg2str(&s_cmsg));
+
+ if (s_cmsg.Command == CAPI_DATA_B3
+ && s_cmsg.Subcommand == CAPI_IND) {
+ handle_data(&s_cmsg, skb);
+ return;
+ }
+ if ((s_cmsg.adr.adrController & 0xffffff00) == 0)
+ handle_controller(&s_cmsg);
+ else if ((s_cmsg.adr.adrPLCI & 0xffff0000) == 0)
+ handle_plci(&s_cmsg);
+ else
+ handle_ncci(&s_cmsg);
+ /*
+ * data of skb used in s_cmsg,
+ * free data when s_cmsg is not used again
+ * thanks to Lars Heete <hel@admin.de>
+ */
+ kfree_skb(skb);
+}
+
+/* ------------------------------------------------------------------- */
+
+#define PUTBYTE_TO_STATUS(card, byte) \
+ do { \
+ *(card)->q931_write++ = (byte); \
+ if ((card)->q931_write > (card)->q931_end) \
+ (card)->q931_write = (card)->q931_buf; \
+ } while (0)
+
+static void handle_dtrace_data(capidrv_contr *card,
+ int send, int level2, u8 *data, u16 len)
+{
+ u8 *p, *end;
+ isdn_ctrl cmd;
+
+ if (!len) {
+ printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n",
+ card->contrnr, len);
+ return;
+ }
+
+ if (level2) {
+ PUTBYTE_TO_STATUS(card, 'D');
+ PUTBYTE_TO_STATUS(card, '2');
+ PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+ PUTBYTE_TO_STATUS(card, ':');
+ } else {
+ PUTBYTE_TO_STATUS(card, 'D');
+ PUTBYTE_TO_STATUS(card, '3');
+ PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+ PUTBYTE_TO_STATUS(card, ':');
+ }
+
+ for (p = data, end = data+len; p < end; p++) {
+ u8 w;
+ PUTBYTE_TO_STATUS(card, ' ');
+ w = (*p >> 4) & 0xf;
+ PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+ w = *p & 0xf;
+ PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+ }
+ PUTBYTE_TO_STATUS(card, '\n');
+
+ cmd.command = ISDN_STAT_STAVAIL;
+ cmd.driver = card->myid;
+ cmd.arg = len*3+5;
+ card->interface.statcallb(&cmd);
+}
+
+/* ------------------------------------------------------------------- */
+
+static _cmsg cmdcmsg;
+
+static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card)
+{
+ switch (c->arg) {
+ case 1:
+ debugmode = (int)(*((unsigned int *)c->parm.num));
+ printk(KERN_DEBUG "capidrv-%d: debugmode=%d\n",
+ card->contrnr, debugmode);
+ return 0;
+ default:
+ printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n",
+ card->contrnr, c->arg);
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Handle leased lines (CAPI-Bundling)
+ */
+
+struct internal_bchannelinfo {
+ unsigned short channelalloc;
+ unsigned short operation;
+ unsigned char cmask[31];
+};
+
+static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep)
+{
+ unsigned long bmask = 0;
+ int active = !0;
+ char *s;
+ int i;
+
+ if (strncmp(teln, "FV:", 3) != 0)
+ return 1;
+ s = teln + 3;
+ while (*s && *s == ' ') s++;
+ if (!*s) return -2;
+ if (*s == 'p' || *s == 'P') {
+ active = 0;
+ s++;
+ }
+ if (*s == 'a' || *s == 'A') {
+ active = !0;
+ s++;
+ }
+ while (*s) {
+ int digit1 = 0;
+ int digit2 = 0;
+ if (!isdigit(*s)) return -3;
+ while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; }
+ if (digit1 <= 0 && digit1 > 30) return -4;
+ if (*s == 0 || *s == ',' || *s == ' ') {
+ bmask |= (1 << digit1);
+ digit1 = 0;
+ if (*s) s++;
+ continue;
+ }
+ if (*s != '-') return -5;
+ s++;
+ if (!isdigit(*s)) return -3;
+ while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; }
+ if (digit2 <= 0 && digit2 > 30) return -4;
+ if (*s == 0 || *s == ',' || *s == ' ') {
+ if (digit1 > digit2)
+ for (i = digit2; i <= digit1 ; i++)
+ bmask |= (1 << i);
+ else
+ for (i = digit1; i <= digit2 ; i++)
+ bmask |= (1 << i);
+ digit1 = digit2 = 0;
+ if (*s) s++;
+ continue;
+ }
+ return -6;
+ }
+ if (activep) *activep = active;
+ if (bmaskp) *bmaskp = bmask;
+ return 0;
+}
+
+static int FVteln2capi20(char *teln, u8 AdditionalInfo[1+2+2+31])
+{
+ unsigned long bmask;
+ int active;
+ int rc, i;
+
+ rc = decodeFVteln(teln, &bmask, &active);
+ if (rc) return rc;
+ /* Length */
+ AdditionalInfo[0] = 2+2+31;
+ /* Channel: 3 => use channel allocation */
+ AdditionalInfo[1] = 3; AdditionalInfo[2] = 0;
+ /* Operation: 0 => DTE mode, 1 => DCE mode */
+ if (active) {
+ AdditionalInfo[3] = 0; AdditionalInfo[4] = 0;
+ } else {
+ AdditionalInfo[3] = 1; AdditionalInfo[4] = 0;
+ }
+ /* Channel mask array */
+ AdditionalInfo[5] = 0; /* no D-Channel */
+ for (i=1; i <= 30; i++)
+ AdditionalInfo[5+i] = (bmask & (1 << i)) ? 0xff : 0;
+ return 0;
+}
+
+static int capidrv_command(isdn_ctrl * c, capidrv_contr * card)
+{
+ isdn_ctrl cmd;
+ struct capidrv_bchan *bchan;
+ struct capidrv_plci *plcip;
+ u8 AdditionalInfo[1+2+2+31];
+ int rc, isleasedline = 0;
+
+ if (c->command == ISDN_CMD_IOCTL)
+ return capidrv_ioctl(c, card);
+
+ switch (c->command) {
+ case ISDN_CMD_DIAL:{
+ u8 calling[ISDN_MSNLEN + 3];
+ u8 called[ISDN_MSNLEN + 2];
+
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n",
+ card->contrnr,
+ c->arg,
+ c->parm.setup.phone,
+ c->parm.setup.si1,
+ c->parm.setup.si2,
+ c->parm.setup.eazmsn);
+
+ bchan = &card->bchans[c->arg % card->nbchan];
+
+ if (bchan->plcip) {
+ printk(KERN_ERR "capidrv-%d: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n",
+ card->contrnr,
+ c->arg,
+ c->parm.setup.phone,
+ c->parm.setup.si1,
+ c->parm.setup.si2,
+ c->parm.setup.eazmsn,
+ bchan->plcip->plci);
+ return 0;
+ }
+ bchan->si1 = c->parm.setup.si1;
+ bchan->si2 = c->parm.setup.si2;
+
+ strncpy(bchan->num, c->parm.setup.phone, sizeof(bchan->num));
+ strncpy(bchan->mynum, c->parm.setup.eazmsn, sizeof(bchan->mynum));
+ rc = FVteln2capi20(bchan->num, AdditionalInfo);
+ isleasedline = (rc == 0);
+ if (rc < 0)
+ printk(KERN_ERR "capidrv-%d: WARNING: invalid leased linedefinition \"%s\"\n", card->contrnr, bchan->num);
+
+ if (isleasedline) {
+ calling[0] = 0;
+ called[0] = 0;
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: connecting leased line\n", card->contrnr);
+ } else {
+ calling[0] = strlen(bchan->mynum) + 2;
+ calling[1] = 0;
+ calling[2] = 0x80;
+ strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN);
+ called[0] = strlen(bchan->num) + 1;
+ called[1] = 0x80;
+ strncpy(called + 2, bchan->num, ISDN_MSNLEN);
+ }
+
+ capi_fill_CONNECT_REQ(&cmdcmsg,
+ global.ap.applid,
+ card->msgid++,
+ card->contrnr, /* adr */
+ si2cip(bchan->si1, bchan->si2), /* cipvalue */
+ called, /* CalledPartyNumber */
+ calling, /* CallingPartyNumber */
+ NULL, /* CalledPartySubaddress */
+ NULL, /* CallingPartySubaddress */
+ b1prot(bchan->l2, bchan->l3), /* B1protocol */
+ b2prot(bchan->l2, bchan->l3), /* B2protocol */
+ b3prot(bchan->l2, bchan->l3), /* B3protocol */
+ b1config(bchan->l2, bchan->l3), /* B1configuration */
+ NULL, /* B2configuration */
+ NULL, /* B3configuration */
+ NULL, /* BC */
+ NULL, /* LLC */
+ NULL, /* HLC */
+ /* BChannelinformation */
+ isleasedline ? AdditionalInfo : NULL,
+ NULL, /* Keypadfacility */
+ NULL, /* Useruserdata */
+ NULL /* Facilitydataarray */
+ );
+ if ((plcip = new_plci(card, (c->arg % card->nbchan))) == 0) {
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.driver = card->myid;
+ cmd.arg = (c->arg % card->nbchan);
+ card->interface.statcallb(&cmd);
+ return -1;
+ }
+ plcip->msgid = cmdcmsg.Messagenumber;
+ plcip->leasedline = isleasedline;
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ);
+ send_message(card, &cmdcmsg);
+ return 0;
+ }
+
+ case ISDN_CMD_ACCEPTD:
+
+ bchan = &card->bchans[c->arg % card->nbchan];
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTD(ch=%ld) l2=%d l3=%d\n",
+ card->contrnr,
+ c->arg, bchan->l2, bchan->l3);
+
+ capi_fill_CONNECT_RESP(&cmdcmsg,
+ global.ap.applid,
+ card->msgid++,
+ bchan->plcip->plci, /* adr */
+ 0, /* Reject */
+ b1prot(bchan->l2, bchan->l3), /* B1protocol */
+ b2prot(bchan->l2, bchan->l3), /* B2protocol */
+ b3prot(bchan->l2, bchan->l3), /* B3protocol */
+ b1config(bchan->l2, bchan->l3), /* B1configuration */
+ NULL, /* B2configuration */
+ NULL, /* B3configuration */
+ NULL, /* ConnectedNumber */
+ NULL, /* ConnectedSubaddress */
+ NULL, /* LLC */
+ NULL, /* BChannelinformation */
+ NULL, /* Keypadfacility */
+ NULL, /* Useruserdata */
+ NULL /* Facilitydataarray */
+ );
+ capi_cmsg2message(&cmdcmsg, cmdcmsg.buf);
+ plci_change_state(card, bchan->plcip, EV_PLCI_CONNECT_RESP);
+ send_message(card, &cmdcmsg);
+ return 0;
+
+ case ISDN_CMD_ACCEPTB:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTB(ch=%ld)\n",
+ card->contrnr,
+ c->arg);
+ return -ENOSYS;
+
+ case ISDN_CMD_HANGUP:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_HANGUP(ch=%ld)\n",
+ card->contrnr,
+ c->arg);
+ bchan = &card->bchans[c->arg % card->nbchan];
+
+ if (bchan->disconnecting) {
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: chan %ld already disconnecting ...\n",
+ card->contrnr,
+ c->arg);
+ return 0;
+ }
+ if (bchan->nccip) {
+ bchan->disconnecting = 1;
+ capi_fill_DISCONNECT_B3_REQ(&cmdcmsg,
+ global.ap.applid,
+ card->msgid++,
+ bchan->nccip->ncci,
+ NULL /* NCPI */
+ );
+ ncci_change_state(card, bchan->nccip, EV_NCCI_DISCONNECT_B3_REQ);
+ send_message(card, &cmdcmsg);
+ return 0;
+ } else if (bchan->plcip) {
+ if (bchan->plcip->state == ST_PLCI_INCOMING) {
+ /*
+ * just ignore, we a called from
+ * isdn_status_callback(),
+ * which will return 0 or 2, this is handled
+ * by the CONNECT_IND handler
+ */
+ bchan->disconnecting = 1;
+ return 0;
+ } else if (bchan->plcip->plci) {
+ bchan->disconnecting = 1;
+ capi_fill_DISCONNECT_REQ(&cmdcmsg,
+ global.ap.applid,
+ card->msgid++,
+ bchan->plcip->plci,
+ NULL, /* BChannelinformation */
+ NULL, /* Keypadfacility */
+ NULL, /* Useruserdata */
+ NULL /* Facilitydataarray */
+ );
+ plci_change_state(card, bchan->plcip, EV_PLCI_DISCONNECT_REQ);
+ send_message(card, &cmdcmsg);
+ return 0;
+ } else {
+ printk(KERN_ERR "capidrv-%d: chan %ld disconnect request while waiting for CONNECT_CONF\n",
+ card->contrnr,
+ c->arg);
+ return -EINVAL;
+ }
+ }
+ printk(KERN_ERR "capidrv-%d: chan %ld disconnect request on free channel\n",
+ card->contrnr,
+ c->arg);
+ return -EINVAL;
+/* ready */
+
+ case ISDN_CMD_SETL2:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: set L2 on chan %ld to %ld\n",
+ card->contrnr,
+ (c->arg & 0xff), (c->arg >> 8));
+ bchan = &card->bchans[(c->arg & 0xff) % card->nbchan];
+ bchan->l2 = (c->arg >> 8);
+ return 0;
+
+ case ISDN_CMD_SETL3:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: set L3 on chan %ld to %ld\n",
+ card->contrnr,
+ (c->arg & 0xff), (c->arg >> 8));
+ bchan = &card->bchans[(c->arg & 0xff) % card->nbchan];
+ bchan->l3 = (c->arg >> 8);
+ return 0;
+
+ case ISDN_CMD_SETEAZ:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: set EAZ \"%s\" on chan %ld\n",
+ card->contrnr,
+ c->parm.num, c->arg);
+ bchan = &card->bchans[c->arg % card->nbchan];
+ strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN);
+ return 0;
+
+ case ISDN_CMD_CLREAZ:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: clearing EAZ on chan %ld\n",
+ card->contrnr, c->arg);
+ bchan = &card->bchans[c->arg % card->nbchan];
+ bchan->msn[0] = 0;
+ return 0;
+
+ default:
+ printk(KERN_ERR "capidrv-%d: ISDN_CMD_%d, Huh?\n",
+ card->contrnr, c->command);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int if_command(isdn_ctrl * c)
+{
+ capidrv_contr *card = findcontrbydriverid(c->driver);
+
+ if (card)
+ return capidrv_command(c, card);
+
+ printk(KERN_ERR
+ "capidrv: if_command %d called with invalid driverId %d!\n",
+ c->command, c->driver);
+ return -ENODEV;
+}
+
+static _cmsg sendcmsg;
+
+static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb)
+{
+ capidrv_contr *card = findcontrbydriverid(id);
+ capidrv_bchan *bchan;
+ capidrv_ncci *nccip;
+ int len = skb->len;
+ int msglen;
+ u16 errcode;
+ u16 datahandle;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n",
+ id);
+ return 0;
+ }
+ if (debugmode > 4)
+ printk(KERN_DEBUG "capidrv-%d: sendbuf len=%d skb=%p doack=%d\n",
+ card->contrnr, len, skb, doack);
+ bchan = &card->bchans[channel % card->nbchan];
+ nccip = bchan->nccip;
+ if (!nccip || nccip->state != ST_NCCI_ACTIVE) {
+ printk(KERN_ERR "capidrv-%d: if_sendbuf: %s:%d: chan not up!\n",
+ card->contrnr, card->name, channel);
+ return 0;
+ }
+ datahandle = nccip->datahandle;
+ capi_fill_DATA_B3_REQ(&sendcmsg, global.ap.applid, card->msgid++,
+ nccip->ncci, /* adr */
+ (u32) skb->data, /* Data */
+ skb->len, /* DataLength */
+ datahandle, /* DataHandle */
+ 0 /* Flags */
+ );
+
+ if (capidrv_add_ack(nccip, datahandle, doack ? (int)skb->len : -1) < 0)
+ return 0;
+
+ capi_cmsg2message(&sendcmsg, sendcmsg.buf);
+ msglen = CAPIMSG_LEN(sendcmsg.buf);
+ if (skb_headroom(skb) < msglen) {
+ struct sk_buff *nskb = skb_realloc_headroom(skb, msglen);
+ if (!nskb) {
+ printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n",
+ card->contrnr);
+ (void)capidrv_del_ack(nccip, datahandle);
+ return 0;
+ }
+ printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom, need %d\n",
+ card->contrnr, skb_headroom(skb), msglen);
+ memcpy(skb_push(nskb, msglen), sendcmsg.buf, msglen);
+ errcode = capi20_put_message(&global.ap, nskb);
+ if (errcode == CAPI_NOERROR) {
+ dev_kfree_skb(skb);
+ nccip->datahandle++;
+ return len;
+ }
+ if (debugmode > 3)
+ printk(KERN_DEBUG "capidrv-%d: sendbuf putmsg ret(%x) - %s\n",
+ card->contrnr, errcode, capi_info2str(errcode));
+ (void)capidrv_del_ack(nccip, datahandle);
+ dev_kfree_skb(nskb);
+ return errcode == CAPI_SENDQUEUEFULL ? 0 : -1;
+ } else {
+ memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen);
+ errcode = capi20_put_message(&global.ap, skb);
+ if (errcode == CAPI_NOERROR) {
+ nccip->datahandle++;
+ return len;
+ }
+ if (debugmode > 3)
+ printk(KERN_DEBUG "capidrv-%d: sendbuf putmsg ret(%x) - %s\n",
+ card->contrnr, errcode, capi_info2str(errcode));
+ skb_pull(skb, msglen);
+ (void)capidrv_del_ack(nccip, datahandle);
+ return errcode == CAPI_SENDQUEUEFULL ? 0 : -1;
+ }
+}
+
+static int if_readstat(u8 __user *buf, int len, int id, int channel)
+{
+ capidrv_contr *card = findcontrbydriverid(id);
+ int count;
+ u8 __user *p;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n",
+ id);
+ return -ENODEV;
+ }
+
+ for (p=buf, count=0; count < len; p++, count++) {
+ put_user(*card->q931_read++, p);
+ if (card->q931_read > card->q931_end)
+ card->q931_read = card->q931_buf;
+ }
+ return count;
+
+}
+
+static void enable_dchannel_trace(capidrv_contr *card)
+{
+ u8 manufacturer[CAPI_MANUFACTURER_LEN];
+ capi_version version;
+ u16 contr = card->contrnr;
+ u16 errcode;
+ u16 avmversion[3];
+
+ errcode = capi20_get_manufacturer(contr, manufacturer);
+ if (errcode != CAPI_NOERROR) {
+ printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n",
+ card->name, errcode);
+ return;
+ }
+ if (strstr(manufacturer, "AVM") == 0) {
+ printk(KERN_ERR "%s: not from AVM, no d-channel trace possible (%s)\n",
+ card->name, manufacturer);
+ return;
+ }
+ errcode = capi20_get_version(contr, &version);
+ if (errcode != CAPI_NOERROR) {
+ printk(KERN_ERR "%s: can't get version (0x%x)\n",
+ card->name, errcode);
+ return;
+ }
+ avmversion[0] = (version.majormanuversion >> 4) & 0x0f;
+ avmversion[1] = (version.majormanuversion << 4) & 0xf0;
+ avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
+ avmversion[2] |= version.minormanuversion & 0x0f;
+
+ if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
+ printk(KERN_INFO "%s: D2 trace enabled\n", card->name);
+ capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.ap.applid,
+ card->msgid++,
+ contr,
+ 0x214D5641, /* ManuID */
+ 0, /* Class */
+ 1, /* Function */
+ (_cstruct)"\004\200\014\000\000");
+ } else {
+ printk(KERN_INFO "%s: D3 trace enabled\n", card->name);
+ capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.ap.applid,
+ card->msgid++,
+ contr,
+ 0x214D5641, /* ManuID */
+ 0, /* Class */
+ 1, /* Function */
+ (_cstruct)"\004\002\003\000\000");
+ }
+ send_message(card, &cmdcmsg);
+}
+
+
+static void send_listen(capidrv_contr *card)
+{
+ capi_fill_LISTEN_REQ(&cmdcmsg, global.ap.applid,
+ card->msgid++,
+ card->contrnr, /* controller */
+ 1 << 6, /* Infomask */
+ card->cipmask,
+ card->cipmask2,
+ NULL, NULL);
+ send_message(card, &cmdcmsg);
+ listen_change_state(card, EV_LISTEN_REQ);
+}
+
+static void listentimerfunc(unsigned long x)
+{
+ capidrv_contr *card = (capidrv_contr *)x;
+ if (card->state != ST_LISTEN_NONE && card->state != ST_LISTEN_ACTIVE)
+ printk(KERN_ERR "%s: controller dead ??\n", card->name);
+ send_listen(card);
+ mod_timer(&card->listentimer, jiffies + 60*HZ);
+}
+
+
+static int capidrv_addcontr(u16 contr, struct capi_profile *profp)
+{
+ capidrv_contr *card;
+ unsigned long flags;
+ isdn_ctrl cmd;
+ char id[20];
+ int i;
+
+ sprintf(id, "capidrv-%d", contr);
+ if (!try_module_get(THIS_MODULE)) {
+ printk(KERN_WARNING "capidrv: (%s) Could not reserve module\n", id);
+ return -1;
+ }
+ if (!(card = (capidrv_contr *) kmalloc(sizeof(capidrv_contr), GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "capidrv: (%s) Could not allocate contr-struct.\n", id);
+ return -1;
+ }
+ memset(card, 0, sizeof(capidrv_contr));
+ card->owner = THIS_MODULE;
+ init_timer(&card->listentimer);
+ strcpy(card->name, id);
+ card->contrnr = contr;
+ card->nbchan = profp->nbchannel;
+ card->bchans = (capidrv_bchan *) kmalloc(sizeof(capidrv_bchan) * card->nbchan, GFP_ATOMIC);
+ if (!card->bchans) {
+ printk(KERN_WARNING
+ "capidrv: (%s) Could not allocate bchan-structs.\n", id);
+ module_put(card->owner);
+ kfree(card);
+ return -1;
+ }
+ card->interface.channels = profp->nbchannel;
+ card->interface.maxbufsize = 2048;
+ card->interface.command = if_command;
+ card->interface.writebuf_skb = if_sendbuf;
+ card->interface.writecmd = NULL;
+ card->interface.readstat = if_readstat;
+ card->interface.features = ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L2_TRANS |
+ ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_P_UNKNOWN |
+ ISDN_FEATURE_L2_X75I |
+ ISDN_FEATURE_L2_X75UI |
+ ISDN_FEATURE_L2_X75BUI;
+ if (profp->support1 & (1<<2))
+ card->interface.features |= ISDN_FEATURE_L2_V11096 |
+ ISDN_FEATURE_L2_V11019 |
+ ISDN_FEATURE_L2_V11038;
+ if (profp->support1 & (1<<8))
+ card->interface.features |= ISDN_FEATURE_L2_MODEM;
+ card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */
+ strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+
+
+ card->q931_read = card->q931_buf;
+ card->q931_write = card->q931_buf;
+ card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1;
+
+ if (!register_isdn(&card->interface)) {
+ printk(KERN_ERR "capidrv: Unable to register contr %s\n", id);
+ kfree(card->bchans);
+ module_put(card->owner);
+ kfree(card);
+ return -1;
+ }
+ card->myid = card->interface.channels;
+ memset(card->bchans, 0, sizeof(capidrv_bchan) * card->nbchan);
+ for (i = 0; i < card->nbchan; i++) {
+ card->bchans[i].contr = card;
+ }
+
+ spin_lock_irqsave(&global_lock, flags);
+ card->next = global.contr_list;
+ global.contr_list = card;
+ global.ncontr++;
+ spin_unlock_irqrestore(&global_lock, flags);
+
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+
+ card->cipmask = 0x1FFF03FF; /* any */
+ card->cipmask2 = 0;
+
+ card->listentimer.data = (unsigned long)card;
+ card->listentimer.function = listentimerfunc;
+ send_listen(card);
+ mod_timer(&card->listentimer, jiffies + 60*HZ);
+
+ printk(KERN_INFO "%s: now up (%d B channels)\n",
+ card->name, card->nbchan);
+
+ enable_dchannel_trace(card);
+
+ return 0;
+}
+
+static int capidrv_delcontr(u16 contr)
+{
+ capidrv_contr **pp, *card;
+ unsigned long flags;
+ isdn_ctrl cmd;
+
+ spin_lock_irqsave(&global_lock, flags);
+ for (card = global.contr_list; card; card = card->next) {
+ if (card->contrnr == contr)
+ break;
+ }
+ if (!card) {
+ spin_unlock_irqrestore(&global_lock, flags);
+ printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr);
+ return -1;
+ }
+ #warning FIXME: maybe a race condition the card should be removed here from global list /kkeil
+ spin_unlock_irqrestore(&global_lock, flags);
+
+ del_timer(&card->listentimer);
+
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: id=%d unloading\n",
+ card->contrnr, card->myid);
+
+ cmd.command = ISDN_STAT_STOP;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+
+ while (card->nbchan) {
+
+ cmd.command = ISDN_STAT_DISCH;
+ cmd.driver = card->myid;
+ cmd.arg = card->nbchan-1;
+ cmd.parm.num[0] = 0;
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: id=%d disable chan=%ld\n",
+ card->contrnr, card->myid, cmd.arg);
+ card->interface.statcallb(&cmd);
+
+ if (card->bchans[card->nbchan-1].nccip)
+ free_ncci(card, card->bchans[card->nbchan-1].nccip);
+ if (card->bchans[card->nbchan-1].plcip)
+ free_plci(card, card->bchans[card->nbchan-1].plcip);
+ if (card->plci_list)
+ printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ card->nbchan--;
+ }
+ kfree(card->bchans);
+ card->bchans = NULL;
+
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: id=%d isdn unload\n",
+ card->contrnr, card->myid);
+
+ cmd.command = ISDN_STAT_UNLOAD;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: id=%d remove contr from list\n",
+ card->contrnr, card->myid);
+
+ spin_lock_irqsave(&global_lock, flags);
+ for (pp = &global.contr_list; *pp; pp = &(*pp)->next) {
+ if (*pp == card) {
+ *pp = (*pp)->next;
+ card->next = NULL;
+ global.ncontr--;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&global_lock, flags);
+
+ module_put(card->owner);
+ printk(KERN_INFO "%s: now down.\n", card->name);
+ kfree(card);
+ return 0;
+}
+
+
+static void lower_callback(unsigned int cmd, u32 contr, void *data)
+{
+
+ switch (cmd) {
+ case KCI_CONTRUP:
+ printk(KERN_INFO "capidrv: controller %hu up\n", contr);
+ (void) capidrv_addcontr(contr, (capi_profile *) data);
+ break;
+ case KCI_CONTRDOWN:
+ printk(KERN_INFO "capidrv: controller %hu down\n", contr);
+ (void) capidrv_delcontr(contr);
+ break;
+ }
+}
+
+/*
+ * /proc/capi/capidrv:
+ * nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
+ */
+static int proc_capidrv_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += sprintf(page+len, "%lu %lu %lu %lu\n",
+ global.ap.nrecvctlpkt,
+ global.ap.nrecvdatapkt,
+ global.ap.nsentctlpkt,
+ global.ap.nsentdatapkt);
+ if (off+count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len-off) ? count : len-off);
+}
+
+static struct procfsentries {
+ char *name;
+ mode_t mode;
+ int (*read_proc)(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+ struct proc_dir_entry *procent;
+} procfsentries[] = {
+ /* { "capi", S_IFDIR, 0 }, */
+ { "capi/capidrv", 0 , proc_capidrv_read_proc },
+};
+
+static void __init proc_init(void)
+{
+ int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
+ int i;
+
+ for (i=0; i < nelem; i++) {
+ struct procfsentries *p = procfsentries + i;
+ p->procent = create_proc_entry(p->name, p->mode, NULL);
+ if (p->procent) p->procent->read_proc = p->read_proc;
+ }
+}
+
+static void __exit proc_exit(void)
+{
+ int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
+ int i;
+
+ for (i=nelem-1; i >= 0; i--) {
+ struct procfsentries *p = procfsentries + i;
+ if (p->procent) {
+ remove_proc_entry(p->name, NULL);
+ p->procent = NULL;
+ }
+ }
+}
+
+static int __init capidrv_init(void)
+{
+ capi_profile profile;
+ char rev[32];
+ char *p;
+ u32 ncontr, contr;
+ u16 errcode;
+
+ if ((p = strchr(revision, ':')) != 0 && p[1]) {
+ strncpy(rev, p + 2, sizeof(rev));
+ rev[sizeof(rev)-1] = 0;
+ if ((p = strchr(rev, '$')) != 0 && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ global.ap.rparam.level3cnt = -2; /* number of bchannels twice */
+ global.ap.rparam.datablkcnt = 16;
+ global.ap.rparam.datablklen = 2048;
+
+ global.ap.recv_message = capidrv_recv_message;
+ errcode = capi20_register(&global.ap);
+ if (errcode) {
+ return -EIO;
+ }
+
+ capi20_set_callback(&global.ap, lower_callback);
+
+ errcode = capi20_get_profile(0, &profile);
+ if (errcode != CAPI_NOERROR) {
+ capi20_release(&global.ap);
+ return -EIO;
+ }
+
+ ncontr = profile.ncontroller;
+ for (contr = 1; contr <= ncontr; contr++) {
+ errcode = capi20_get_profile(contr, &profile);
+ if (errcode != CAPI_NOERROR)
+ continue;
+ (void) capidrv_addcontr(contr, &profile);
+ }
+ proc_init();
+
+ printk(KERN_NOTICE "capidrv: Rev %s: loaded\n", rev);
+ return 0;
+}
+
+static void __exit capidrv_exit(void)
+{
+ char rev[10];
+ char *p;
+
+ if ((p = strchr(revision, ':')) != 0) {
+ strcpy(rev, p + 1);
+ p = strchr(rev, '$');
+ *p = 0;
+ } else {
+ strcpy(rev, " ??? ");
+ }
+
+ capi20_release(&global.ap);
+
+ proc_exit();
+
+ printk(KERN_NOTICE "capidrv: Rev%s: unloaded\n", rev);
+}
+
+module_init(capidrv_init);
+module_exit(capidrv_exit);
diff --git a/drivers/isdn/capi/capidrv.h b/drivers/isdn/capi/capidrv.h
new file mode 100644
index 000000000000..1e698e1e269f
--- /dev/null
+++ b/drivers/isdn/capi/capidrv.h
@@ -0,0 +1,140 @@
+/* $Id: capidrv.h,v 1.2.8.2 2001/09/23 22:24:33 kai Exp $
+ *
+ * ISDN4Linux Driver, using capi20 interface (kernelcapi)
+ *
+ * Copyright 1997 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 __CAPIDRV_H__
+#define __CAPIDRV_H__
+
+/*
+ * LISTEN state machine
+ */
+#define ST_LISTEN_NONE 0 /* L-0 */
+#define ST_LISTEN_WAIT_CONF 1 /* L-0.1 */
+#define ST_LISTEN_ACTIVE 2 /* L-1 */
+#define ST_LISTEN_ACTIVE_WAIT_CONF 3 /* L-1.1 */
+
+
+#define EV_LISTEN_REQ 1 /* L-0 -> L-0.1
+ L-1 -> L-1.1 */
+#define EV_LISTEN_CONF_ERROR 2 /* L-0.1 -> L-0
+ L-1.1 -> L-1 */
+#define EV_LISTEN_CONF_EMPTY 3 /* L-0.1 -> L-0
+ L-1.1 -> L-0 */
+#define EV_LISTEN_CONF_OK 4 /* L-0.1 -> L-1
+ L-1.1 -> L.1 */
+
+/*
+ * per plci state machine
+ */
+#define ST_PLCI_NONE 0 /* P-0 */
+#define ST_PLCI_OUTGOING 1 /* P-0.1 */
+#define ST_PLCI_ALLOCATED 2 /* P-1 */
+#define ST_PLCI_ACTIVE 3 /* P-ACT */
+#define ST_PLCI_INCOMING 4 /* P-2 */
+#define ST_PLCI_FACILITY_IND 5 /* P-3 */
+#define ST_PLCI_ACCEPTING 6 /* P-4 */
+#define ST_PLCI_DISCONNECTING 7 /* P-5 */
+#define ST_PLCI_DISCONNECTED 8 /* P-6 */
+#define ST_PLCI_RESUMEING 9 /* P-0.Res */
+#define ST_PLCI_RESUME 10 /* P-Res */
+#define ST_PLCI_HELD 11 /* P-HELD */
+
+#define EV_PLCI_CONNECT_REQ 1 /* P-0 -> P-0.1
+ */
+#define EV_PLCI_CONNECT_CONF_ERROR 2 /* P-0.1 -> P-0
+ */
+#define EV_PLCI_CONNECT_CONF_OK 3 /* P-0.1 -> P-1
+ */
+#define EV_PLCI_FACILITY_IND_UP 4 /* P-0 -> P-1
+ */
+#define EV_PLCI_CONNECT_IND 5 /* P-0 -> P-2
+ */
+#define EV_PLCI_CONNECT_ACTIVE_IND 6 /* P-1 -> P-ACT
+ */
+#define EV_PLCI_CONNECT_REJECT 7 /* P-2 -> P-5
+ P-3 -> P-5
+ */
+#define EV_PLCI_DISCONNECT_REQ 8 /* P-1 -> P-5
+ P-2 -> P-5
+ P-3 -> P-5
+ P-4 -> P-5
+ P-ACT -> P-5
+ P-Res -> P-5 (*)
+ P-HELD -> P-5 (*)
+ */
+#define EV_PLCI_DISCONNECT_IND 9 /* P-1 -> P-6
+ P-2 -> P-6
+ P-3 -> P-6
+ P-4 -> P-6
+ P-5 -> P-6
+ P-ACT -> P-6
+ P-Res -> P-6 (*)
+ P-HELD -> P-6 (*)
+ */
+#define EV_PLCI_FACILITY_IND_DOWN 10 /* P-0.1 -> P-5
+ P-1 -> P-5
+ P-ACT -> P-5
+ P-2 -> P-5
+ P-3 -> P-5
+ P-4 -> P-5
+ */
+#define EV_PLCI_DISCONNECT_RESP 11 /* P-6 -> P-0
+ */
+#define EV_PLCI_CONNECT_RESP 12 /* P-6 -> P-0
+ */
+
+#define EV_PLCI_RESUME_REQ 13 /* P-0 -> P-0.Res
+ */
+#define EV_PLCI_RESUME_CONF_OK 14 /* P-0.Res -> P-Res
+ */
+#define EV_PLCI_RESUME_CONF_ERROR 15 /* P-0.Res -> P-0
+ */
+#define EV_PLCI_RESUME_IND 16 /* P-Res -> P-ACT
+ */
+#define EV_PLCI_HOLD_IND 17 /* P-ACT -> P-HELD
+ */
+#define EV_PLCI_RETRIEVE_IND 18 /* P-HELD -> P-ACT
+ */
+#define EV_PLCI_SUSPEND_IND 19 /* P-ACT -> P-5
+ */
+#define EV_PLCI_CD_IND 20 /* P-2 -> P-5
+ */
+
+/*
+ * per ncci state machine
+ */
+#define ST_NCCI_PREVIOUS -1
+#define ST_NCCI_NONE 0 /* N-0 */
+#define ST_NCCI_OUTGOING 1 /* N-0.1 */
+#define ST_NCCI_INCOMING 2 /* N-1 */
+#define ST_NCCI_ALLOCATED 3 /* N-2 */
+#define ST_NCCI_ACTIVE 4 /* N-ACT */
+#define ST_NCCI_RESETING 5 /* N-3 */
+#define ST_NCCI_DISCONNECTING 6 /* N-4 */
+#define ST_NCCI_DISCONNECTED 7 /* N-5 */
+
+#define EV_NCCI_CONNECT_B3_REQ 1 /* N-0 -> N-0.1 */
+#define EV_NCCI_CONNECT_B3_IND 2 /* N-0 -> N.1 */
+#define EV_NCCI_CONNECT_B3_CONF_OK 3 /* N-0.1 -> N.2 */
+#define EV_NCCI_CONNECT_B3_CONF_ERROR 4 /* N-0.1 -> N.0 */
+#define EV_NCCI_CONNECT_B3_REJECT 5 /* N-1 -> N-4 */
+#define EV_NCCI_CONNECT_B3_RESP 6 /* N-1 -> N-2 */
+#define EV_NCCI_CONNECT_B3_ACTIVE_IND 7 /* N-2 -> N-ACT */
+#define EV_NCCI_RESET_B3_REQ 8 /* N-ACT -> N-3 */
+#define EV_NCCI_RESET_B3_IND 9 /* N-3 -> N-ACT */
+#define EV_NCCI_DISCONNECT_B3_IND 10 /* N-4 -> N.5 */
+#define EV_NCCI_DISCONNECT_B3_CONF_ERROR 11 /* N-4 -> previous */
+#define EV_NCCI_DISCONNECT_B3_REQ 12 /* N-1 -> N-4
+ N-2 -> N-4
+ N-3 -> N-4
+ N-ACT -> N-4 */
+#define EV_NCCI_DISCONNECT_B3_RESP 13 /* N-5 -> N-0 */
+
+#endif /* __CAPIDRV_H__ */
diff --git a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c
new file mode 100644
index 000000000000..f8570fd9d2ab
--- /dev/null
+++ b/drivers/isdn/capi/capifs.c
@@ -0,0 +1,212 @@
+/* $Id: capifs.c,v 1.1.2.3 2004/01/16 21:09:26 keil Exp $
+ *
+ * Copyright 2000 by Carsten Paeth <calle@calle.de>
+ *
+ * Heavily based on devpts filesystem from H. Peter Anvin
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+
+MODULE_DESCRIPTION("CAPI4Linux: /dev/capi/ filesystem");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+/* ------------------------------------------------------------------ */
+
+#define CAPIFS_SUPER_MAGIC (('C'<<8)|'N')
+
+static struct vfsmount *capifs_mnt;
+static struct dentry *capifs_root;
+
+static struct {
+ int setuid;
+ int setgid;
+ uid_t uid;
+ gid_t gid;
+ umode_t mode;
+} config = {.mode = 0600};
+
+/* ------------------------------------------------------------------ */
+
+static int capifs_remount(struct super_block *s, int *flags, char *data)
+{
+ int setuid = 0;
+ int setgid = 0;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ umode_t mode = 0600;
+ char *this_char;
+
+ this_char = NULL;
+ while ((this_char = strsep(&data, ",")) != NULL) {
+ int n;
+ char dummy;
+ if (!*this_char)
+ continue;
+ if (sscanf(this_char, "uid=%i%c", &n, &dummy) == 1) {
+ setuid = 1;
+ uid = n;
+ } else if (sscanf(this_char, "gid=%i%c", &n, &dummy) == 1) {
+ setgid = 1;
+ gid = n;
+ } else if (sscanf(this_char, "mode=%o%c", &n, &dummy) == 1)
+ mode = n & ~S_IFMT;
+ else {
+ printk("capifs: called with bogus options\n");
+ return -EINVAL;
+ }
+ }
+ config.setuid = setuid;
+ config.setgid = setgid;
+ config.uid = uid;
+ config.gid = gid;
+ config.mode = mode;
+ return 0;
+}
+
+static struct super_operations capifs_sops =
+{
+ .statfs = simple_statfs,
+ .remount_fs = capifs_remount,
+};
+
+
+static int
+capifs_fill_super(struct super_block *s, void *data, int silent)
+{
+ struct inode * inode;
+
+ s->s_blocksize = 1024;
+ s->s_blocksize_bits = 10;
+ s->s_magic = CAPIFS_SUPER_MAGIC;
+ s->s_op = &capifs_sops;
+ s->s_time_gran = 1;
+
+ inode = new_inode(s);
+ if (!inode)
+ goto fail;
+ inode->i_ino = 1;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blocks = 0;
+ inode->i_blksize = 1024;
+ inode->i_uid = inode->i_gid = 0;
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ inode->i_nlink = 2;
+
+ capifs_root = s->s_root = d_alloc_root(inode);
+ if (s->s_root)
+ return 0;
+
+ printk("capifs: get root dentry failed\n");
+ iput(inode);
+fail:
+ return -ENOMEM;
+}
+
+static struct super_block *capifs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return get_sb_single(fs_type, flags, data, capifs_fill_super);
+}
+
+static struct file_system_type capifs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "capifs",
+ .get_sb = capifs_get_sb,
+ .kill_sb = kill_anon_super,
+};
+
+static struct dentry *get_node(int num)
+{
+ char s[10];
+ struct dentry *root = capifs_root;
+ down(&root->d_inode->i_sem);
+ return lookup_one_len(s, root, sprintf(s, "%d", num));
+}
+
+void capifs_new_ncci(unsigned int number, dev_t device)
+{
+ struct dentry *dentry;
+ struct inode *inode = new_inode(capifs_mnt->mnt_sb);
+ if (!inode)
+ return;
+ inode->i_ino = number+2;
+ inode->i_blksize = 1024;
+ inode->i_uid = config.setuid ? config.uid : current->fsuid;
+ inode->i_gid = config.setgid ? config.gid : current->fsgid;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ init_special_inode(inode, S_IFCHR|config.mode, device);
+ //inode->i_op = &capifs_file_inode_operations;
+
+ dentry = get_node(number);
+ if (!IS_ERR(dentry) && !dentry->d_inode)
+ d_instantiate(dentry, inode);
+ up(&capifs_root->d_inode->i_sem);
+}
+
+void capifs_free_ncci(unsigned int number)
+{
+ struct dentry *dentry = get_node(number);
+
+ if (!IS_ERR(dentry)) {
+ struct inode *inode = dentry->d_inode;
+ if (inode) {
+ inode->i_nlink--;
+ d_delete(dentry);
+ dput(dentry);
+ }
+ dput(dentry);
+ }
+ up(&capifs_root->d_inode->i_sem);
+}
+
+static int __init capifs_init(void)
+{
+ char rev[32];
+ char *p;
+ int err;
+
+ if ((p = strchr(revision, ':')) != 0 && p[1]) {
+ strlcpy(rev, p + 2, sizeof(rev));
+ if ((p = strchr(rev, '$')) != 0 && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ err = register_filesystem(&capifs_fs_type);
+ if (!err) {
+ capifs_mnt = kern_mount(&capifs_fs_type);
+ if (IS_ERR(capifs_mnt))
+ err = PTR_ERR(capifs_mnt);
+ }
+ if (!err)
+ printk(KERN_NOTICE "capifs: Rev %s\n", rev);
+ return err;
+}
+
+static void __exit capifs_exit(void)
+{
+ unregister_filesystem(&capifs_fs_type);
+ mntput(capifs_mnt);
+}
+
+EXPORT_SYMBOL(capifs_new_ncci);
+EXPORT_SYMBOL(capifs_free_ncci);
+
+module_init(capifs_init);
+module_exit(capifs_exit);
diff --git a/drivers/isdn/capi/capifs.h b/drivers/isdn/capi/capifs.h
new file mode 100644
index 000000000000..d0bd4c3c430a
--- /dev/null
+++ b/drivers/isdn/capi/capifs.h
@@ -0,0 +1,11 @@
+/* $Id: capifs.h,v 1.1.2.2 2004/01/16 21:09:26 keil Exp $
+ *
+ * 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.
+ *
+ */
+
+void capifs_new_ncci(unsigned int num, dev_t device);
+void capifs_free_ncci(unsigned int num);
diff --git a/drivers/isdn/capi/capilib.c b/drivers/isdn/capi/capilib.c
new file mode 100644
index 000000000000..68409d971e73
--- /dev/null
+++ b/drivers/isdn/capi/capilib.c
@@ -0,0 +1,200 @@
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/isdn/capilli.h>
+
+#define DBG(format, arg...) do { \
+printk(KERN_DEBUG "%s: " format "\n" , __FUNCTION__ , ## arg); \
+} while (0)
+
+struct capilib_msgidqueue {
+ struct capilib_msgidqueue *next;
+ u16 msgid;
+};
+
+struct capilib_ncci {
+ struct list_head list;
+ u16 applid;
+ u32 ncci;
+ u32 winsize;
+ int nmsg;
+ struct capilib_msgidqueue *msgidqueue;
+ struct capilib_msgidqueue *msgidlast;
+ struct capilib_msgidqueue *msgidfree;
+ struct capilib_msgidqueue msgidpool[CAPI_MAXDATAWINDOW];
+};
+
+// ---------------------------------------------------------------------------
+// NCCI Handling
+
+static inline void mq_init(struct capilib_ncci * np)
+{
+ u_int i;
+ np->msgidqueue = NULL;
+ np->msgidlast = NULL;
+ np->nmsg = 0;
+ memset(np->msgidpool, 0, sizeof(np->msgidpool));
+ np->msgidfree = &np->msgidpool[0];
+ for (i = 1; i < np->winsize; i++) {
+ np->msgidpool[i].next = np->msgidfree;
+ np->msgidfree = &np->msgidpool[i];
+ }
+}
+
+static inline int mq_enqueue(struct capilib_ncci * np, u16 msgid)
+{
+ struct capilib_msgidqueue *mq;
+ if ((mq = np->msgidfree) == 0)
+ return 0;
+ np->msgidfree = mq->next;
+ mq->msgid = msgid;
+ mq->next = NULL;
+ if (np->msgidlast)
+ np->msgidlast->next = mq;
+ np->msgidlast = mq;
+ if (!np->msgidqueue)
+ np->msgidqueue = mq;
+ np->nmsg++;
+ return 1;
+}
+
+static inline int mq_dequeue(struct capilib_ncci * np, u16 msgid)
+{
+ struct capilib_msgidqueue **pp;
+ for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) {
+ if ((*pp)->msgid == msgid) {
+ struct capilib_msgidqueue *mq = *pp;
+ *pp = mq->next;
+ if (mq == np->msgidlast)
+ np->msgidlast = NULL;
+ mq->next = np->msgidfree;
+ np->msgidfree = mq;
+ np->nmsg--;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void capilib_new_ncci(struct list_head *head, u16 applid, u32 ncci, u32 winsize)
+{
+ struct capilib_ncci *np;
+
+ np = kmalloc(sizeof(*np), GFP_ATOMIC);
+ if (!np) {
+ printk(KERN_WARNING "capilib_new_ncci: no memory.\n");
+ return;
+ }
+ if (winsize > CAPI_MAXDATAWINDOW) {
+ printk(KERN_ERR "capi_new_ncci: winsize %d too big\n",
+ winsize);
+ winsize = CAPI_MAXDATAWINDOW;
+ }
+ np->applid = applid;
+ np->ncci = ncci;
+ np->winsize = winsize;
+ mq_init(np);
+ list_add_tail(&np->list, head);
+ DBG("kcapi: appl %d ncci 0x%x up", applid, ncci);
+}
+
+EXPORT_SYMBOL(capilib_new_ncci);
+
+void capilib_free_ncci(struct list_head *head, u16 applid, u32 ncci)
+{
+ struct list_head *l;
+ struct capilib_ncci *np;
+
+ list_for_each(l, head) {
+ np = list_entry(l, struct capilib_ncci, list);
+ if (np->applid != applid)
+ continue;
+ if (np->ncci != ncci)
+ continue;
+ printk(KERN_INFO "kcapi: appl %d ncci 0x%x down\n", applid, ncci);
+ list_del(&np->list);
+ kfree(np);
+ return;
+ }
+ printk(KERN_ERR "capilib_free_ncci: ncci 0x%x not found\n", ncci);
+}
+
+EXPORT_SYMBOL(capilib_free_ncci);
+
+void capilib_release_appl(struct list_head *head, u16 applid)
+{
+ struct list_head *l, *n;
+ struct capilib_ncci *np;
+
+ list_for_each_safe(l, n, head) {
+ np = list_entry(l, struct capilib_ncci, list);
+ if (np->applid != applid)
+ continue;
+ printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down\n", applid, np->ncci);
+ list_del(&np->list);
+ kfree(np);
+ }
+}
+
+EXPORT_SYMBOL(capilib_release_appl);
+
+void capilib_release(struct list_head *head)
+{
+ struct list_head *l, *n;
+ struct capilib_ncci *np;
+
+ list_for_each_safe(l, n, head) {
+ np = list_entry(l, struct capilib_ncci, list);
+ printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down\n", np->applid, np->ncci);
+ list_del(&np->list);
+ kfree(np);
+ }
+}
+
+EXPORT_SYMBOL(capilib_release);
+
+u16 capilib_data_b3_req(struct list_head *head, u16 applid, u32 ncci, u16 msgid)
+{
+ struct list_head *l;
+ struct capilib_ncci *np;
+
+ list_for_each(l, head) {
+ np = list_entry(l, struct capilib_ncci, list);
+ if (np->applid != applid)
+ continue;
+ if (np->ncci != ncci)
+ continue;
+
+ if (mq_enqueue(np, msgid) == 0)
+ return CAPI_SENDQUEUEFULL;
+
+ return CAPI_NOERROR;
+ }
+ printk(KERN_ERR "capilib_data_b3_req: ncci 0x%x not found\n", ncci);
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capilib_data_b3_req);
+
+void capilib_data_b3_conf(struct list_head *head, u16 applid, u32 ncci, u16 msgid)
+{
+ struct list_head *l;
+ struct capilib_ncci *np;
+
+ list_for_each(l, head) {
+ np = list_entry(l, struct capilib_ncci, list);
+ if (np->applid != applid)
+ continue;
+ if (np->ncci != ncci)
+ continue;
+
+ if (mq_dequeue(np, msgid) == 0) {
+ printk(KERN_ERR "kcapi: msgid %hu ncci 0x%x not on queue\n",
+ msgid, ncci);
+ }
+ return;
+ }
+ printk(KERN_ERR "capilib_data_b3_conf: ncci 0x%x not found\n", ncci);
+}
+
+EXPORT_SYMBOL(capilib_data_b3_conf);
diff --git a/drivers/isdn/capi/capiutil.c b/drivers/isdn/capi/capiutil.c
new file mode 100644
index 000000000000..e7cf6bc286a6
--- /dev/null
+++ b/drivers/isdn/capi/capiutil.c
@@ -0,0 +1,859 @@
+/* $Id: capiutil.c,v 1.13.6.4 2001/09/23 22:24:33 kai Exp $
+ *
+ * CAPI 2.0 convert capi message to capi message struct
+ *
+ * From CAPI 2.0 Development Kit AVM 1995 (msg.c)
+ * Rewritten for Linux 1996 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/string.h>
+#include <linux/ctype.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/isdn/capiutil.h>
+
+/* from CAPI2.0 DDK AVM Berlin GmbH */
+
+#ifndef CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
+char *capi_info2str(u16 reason)
+{
+ return "..";
+}
+#else
+char *capi_info2str(u16 reason)
+{
+ switch (reason) {
+
+/*-- informative values (corresponding message was processed) -----*/
+ case 0x0001:
+ return "NCPI not supported by current protocol, NCPI ignored";
+ case 0x0002:
+ return "Flags not supported by current protocol, flags ignored";
+ case 0x0003:
+ return "Alert already sent by another application";
+
+/*-- error information concerning CAPI_REGISTER -----*/
+ case 0x1001:
+ return "Too many applications";
+ case 0x1002:
+ return "Logical block size too small, must be at least 128 Bytes";
+ case 0x1003:
+ return "Buffer exceeds 64 kByte";
+ case 0x1004:
+ return "Message buffer size too small, must be at least 1024 Bytes";
+ case 0x1005:
+ return "Max. number of logical connections not supported";
+ case 0x1006:
+ return "Reserved";
+ case 0x1007:
+ return "The message could not be accepted because of an internal busy condition";
+ case 0x1008:
+ return "OS resource error (no memory ?)";
+ case 0x1009:
+ return "CAPI not installed";
+ case 0x100A:
+ return "Controller does not support external equipment";
+ case 0x100B:
+ return "Controller does only support external equipment";
+
+/*-- error information concerning message exchange functions -----*/
+ case 0x1101:
+ return "Illegal application number";
+ case 0x1102:
+ return "Illegal command or subcommand or message length less than 12 bytes";
+ case 0x1103:
+ return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI";
+ case 0x1104:
+ return "Queue is empty";
+ case 0x1105:
+ return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE";
+ case 0x1106:
+ return "Unknown notification parameter";
+ case 0x1107:
+ return "The Message could not be accepted because of an internal busy condition";
+ case 0x1108:
+ return "OS Resource error (no memory ?)";
+ case 0x1109:
+ return "CAPI not installed";
+ case 0x110A:
+ return "Controller does not support external equipment";
+ case 0x110B:
+ return "Controller does only support external equipment";
+
+/*-- error information concerning resource / coding problems -----*/
+ case 0x2001:
+ return "Message not supported in current state";
+ case 0x2002:
+ return "Illegal Controller / PLCI / NCCI";
+ case 0x2003:
+ return "Out of PLCI";
+ case 0x2004:
+ return "Out of NCCI";
+ case 0x2005:
+ return "Out of LISTEN";
+ case 0x2006:
+ return "Out of FAX resources (protocol T.30)";
+ case 0x2007:
+ return "Illegal message parameter coding";
+
+/*-- error information concerning requested services -----*/
+ case 0x3001:
+ return "B1 protocol not supported";
+ case 0x3002:
+ return "B2 protocol not supported";
+ case 0x3003:
+ return "B3 protocol not supported";
+ case 0x3004:
+ return "B1 protocol parameter not supported";
+ case 0x3005:
+ return "B2 protocol parameter not supported";
+ case 0x3006:
+ return "B3 protocol parameter not supported";
+ case 0x3007:
+ return "B protocol combination not supported";
+ case 0x3008:
+ return "NCPI not supported";
+ case 0x3009:
+ return "CIP Value unknown";
+ case 0x300A:
+ return "Flags not supported (reserved bits)";
+ case 0x300B:
+ return "Facility not supported";
+ case 0x300C:
+ return "Data length not supported by current protocol";
+ case 0x300D:
+ return "Reset procedure not supported by current protocol";
+
+/*-- informations about the clearing of a physical connection -----*/
+ case 0x3301:
+ return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)";
+ case 0x3302:
+ return "Protocol error layer 2";
+ case 0x3303:
+ return "Protocol error layer 3";
+ case 0x3304:
+ return "Another application got that call";
+/*-- T.30 specific reasons -----*/
+ case 0x3311:
+ return "Connecting not successful (remote station is no FAX G3 machine)";
+ case 0x3312:
+ return "Connecting not successful (training error)";
+ case 0x3313:
+ return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)";
+ case 0x3314:
+ return "Disconnected during transfer (remote abort)";
+ case 0x3315:
+ return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)";
+ case 0x3316:
+ return "Disconnected during transfer (local tx data underrun)";
+ case 0x3317:
+ return "Disconnected during transfer (local rx data overflow)";
+ case 0x3318:
+ return "Disconnected during transfer (local abort)";
+ case 0x3319:
+ return "Illegal parameter coding (e.g. SFF coding error)";
+
+/*-- disconnect causes from the network according to ETS 300 102-1/Q.931 -----*/
+ case 0x3481: return "Unallocated (unassigned) number";
+ case 0x3482: return "No route to specified transit network";
+ case 0x3483: return "No route to destination";
+ case 0x3486: return "Channel unacceptable";
+ case 0x3487:
+ return "Call awarded and being delivered in an established channel";
+ case 0x3490: return "Normal call clearing";
+ case 0x3491: return "User busy";
+ case 0x3492: return "No user responding";
+ case 0x3493: return "No answer from user (user alerted)";
+ case 0x3495: return "Call rejected";
+ case 0x3496: return "Number changed";
+ case 0x349A: return "Non-selected user clearing";
+ case 0x349B: return "Destination out of order";
+ case 0x349C: return "Invalid number format";
+ case 0x349D: return "Facility rejected";
+ case 0x349E: return "Response to STATUS ENQUIRY";
+ case 0x349F: return "Normal, unspecified";
+ case 0x34A2: return "No circuit / channel available";
+ case 0x34A6: return "Network out of order";
+ case 0x34A9: return "Temporary failure";
+ case 0x34AA: return "Switching equipment congestion";
+ case 0x34AB: return "Access information discarded";
+ case 0x34AC: return "Requested circuit / channel not available";
+ case 0x34AF: return "Resources unavailable, unspecified";
+ case 0x34B1: return "Quality of service unavailable";
+ case 0x34B2: return "Requested facility not subscribed";
+ case 0x34B9: return "Bearer capability not authorized";
+ case 0x34BA: return "Bearer capability not presently available";
+ case 0x34BF: return "Service or option not available, unspecified";
+ case 0x34C1: return "Bearer capability not implemented";
+ case 0x34C2: return "Channel type not implemented";
+ case 0x34C5: return "Requested facility not implemented";
+ case 0x34C6: return "Only restricted digital information bearer capability is available";
+ case 0x34CF: return "Service or option not implemented, unspecified";
+ case 0x34D1: return "Invalid call reference value";
+ case 0x34D2: return "Identified channel does not exist";
+ case 0x34D3: return "A suspended call exists, but this call identity does not";
+ case 0x34D4: return "Call identity in use";
+ case 0x34D5: return "No call suspended";
+ case 0x34D6: return "Call having the requested call identity has been cleared";
+ case 0x34D8: return "Incompatible destination";
+ case 0x34DB: return "Invalid transit network selection";
+ case 0x34DF: return "Invalid message, unspecified";
+ case 0x34E0: return "Mandatory information element is missing";
+ case 0x34E1: return "Message type non-existent or not implemented";
+ case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented";
+ case 0x34E3: return "Information element non-existent or not implemented";
+ case 0x34E4: return "Invalid information element contents";
+ case 0x34E5: return "Message not compatible with call state";
+ case 0x34E6: return "Recovery on timer expiry";
+ case 0x34EF: return "Protocol error, unspecified";
+ case 0x34FF: return "Interworking, unspecified";
+
+ default: return "No additional information";
+ }
+}
+#endif
+
+typedef struct {
+ int typ;
+ size_t off;
+} _cdef;
+
+#define _CBYTE 1
+#define _CWORD 2
+#define _CDWORD 3
+#define _CSTRUCT 4
+#define _CMSTRUCT 5
+#define _CEND 6
+
+static _cdef cdef[] =
+{
+ /*00 */
+ {_CEND},
+ /*01 */
+ {_CEND},
+ /*02 */
+ {_CEND},
+ /*03 */
+ {_CDWORD, offsetof(_cmsg, adr.adrController)},
+ /*04 */
+ {_CMSTRUCT, offsetof(_cmsg, AdditionalInfo)},
+ /*05 */
+ {_CSTRUCT, offsetof(_cmsg, B1configuration)},
+ /*06 */
+ {_CWORD, offsetof(_cmsg, B1protocol)},
+ /*07 */
+ {_CSTRUCT, offsetof(_cmsg, B2configuration)},
+ /*08 */
+ {_CWORD, offsetof(_cmsg, B2protocol)},
+ /*09 */
+ {_CSTRUCT, offsetof(_cmsg, B3configuration)},
+ /*0a */
+ {_CWORD, offsetof(_cmsg, B3protocol)},
+ /*0b */
+ {_CSTRUCT, offsetof(_cmsg, BC)},
+ /*0c */
+ {_CSTRUCT, offsetof(_cmsg, BChannelinformation)},
+ /*0d */
+ {_CMSTRUCT, offsetof(_cmsg, BProtocol)},
+ /*0e */
+ {_CSTRUCT, offsetof(_cmsg, CalledPartyNumber)},
+ /*0f */
+ {_CSTRUCT, offsetof(_cmsg, CalledPartySubaddress)},
+ /*10 */
+ {_CSTRUCT, offsetof(_cmsg, CallingPartyNumber)},
+ /*11 */
+ {_CSTRUCT, offsetof(_cmsg, CallingPartySubaddress)},
+ /*12 */
+ {_CDWORD, offsetof(_cmsg, CIPmask)},
+ /*13 */
+ {_CDWORD, offsetof(_cmsg, CIPmask2)},
+ /*14 */
+ {_CWORD, offsetof(_cmsg, CIPValue)},
+ /*15 */
+ {_CDWORD, offsetof(_cmsg, Class)},
+ /*16 */
+ {_CSTRUCT, offsetof(_cmsg, ConnectedNumber)},
+ /*17 */
+ {_CSTRUCT, offsetof(_cmsg, ConnectedSubaddress)},
+ /*18 */
+ {_CDWORD, offsetof(_cmsg, Data)},
+ /*19 */
+ {_CWORD, offsetof(_cmsg, DataHandle)},
+ /*1a */
+ {_CWORD, offsetof(_cmsg, DataLength)},
+ /*1b */
+ {_CSTRUCT, offsetof(_cmsg, FacilityConfirmationParameter)},
+ /*1c */
+ {_CSTRUCT, offsetof(_cmsg, Facilitydataarray)},
+ /*1d */
+ {_CSTRUCT, offsetof(_cmsg, FacilityIndicationParameter)},
+ /*1e */
+ {_CSTRUCT, offsetof(_cmsg, FacilityRequestParameter)},
+ /*1f */
+ {_CWORD, offsetof(_cmsg, FacilitySelector)},
+ /*20 */
+ {_CWORD, offsetof(_cmsg, Flags)},
+ /*21 */
+ {_CDWORD, offsetof(_cmsg, Function)},
+ /*22 */
+ {_CSTRUCT, offsetof(_cmsg, HLC)},
+ /*23 */
+ {_CWORD, offsetof(_cmsg, Info)},
+ /*24 */
+ {_CSTRUCT, offsetof(_cmsg, InfoElement)},
+ /*25 */
+ {_CDWORD, offsetof(_cmsg, InfoMask)},
+ /*26 */
+ {_CWORD, offsetof(_cmsg, InfoNumber)},
+ /*27 */
+ {_CSTRUCT, offsetof(_cmsg, Keypadfacility)},
+ /*28 */
+ {_CSTRUCT, offsetof(_cmsg, LLC)},
+ /*29 */
+ {_CSTRUCT, offsetof(_cmsg, ManuData)},
+ /*2a */
+ {_CDWORD, offsetof(_cmsg, ManuID)},
+ /*2b */
+ {_CSTRUCT, offsetof(_cmsg, NCPI)},
+ /*2c */
+ {_CWORD, offsetof(_cmsg, Reason)},
+ /*2d */
+ {_CWORD, offsetof(_cmsg, Reason_B3)},
+ /*2e */
+ {_CWORD, offsetof(_cmsg, Reject)},
+ /*2f */
+ {_CSTRUCT, offsetof(_cmsg, Useruserdata)}
+};
+
+static unsigned char *cpars[] =
+{
+ /* ALERT_REQ */ [0x01] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* CONNECT_REQ */ [0x02] = "\x03\x14\x0e\x10\x0f\x11\x0d\x06\x08\x0a\x05\x07\x09\x01\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* DISCONNECT_REQ */ [0x04] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* LISTEN_REQ */ [0x05] = "\x03\x25\x12\x13\x10\x11\x01",
+ /* INFO_REQ */ [0x08] = "\x03\x0e\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* FACILITY_REQ */ [0x09] = "\x03\x1f\x1e\x01",
+ /* SELECT_B_PROTOCOL_REQ */ [0x0a] = "\x03\x0d\x06\x08\x0a\x05\x07\x09\x01\x01",
+ /* CONNECT_B3_REQ */ [0x0b] = "\x03\x2b\x01",
+ /* DISCONNECT_B3_REQ */ [0x0d] = "\x03\x2b\x01",
+ /* DATA_B3_REQ */ [0x0f] = "\x03\x18\x1a\x19\x20\x01",
+ /* RESET_B3_REQ */ [0x10] = "\x03\x2b\x01",
+ /* ALERT_CONF */ [0x13] = "\x03\x23\x01",
+ /* CONNECT_CONF */ [0x14] = "\x03\x23\x01",
+ /* DISCONNECT_CONF */ [0x16] = "\x03\x23\x01",
+ /* LISTEN_CONF */ [0x17] = "\x03\x23\x01",
+ /* MANUFACTURER_REQ */ [0x18] = "\x03\x2a\x15\x21\x29\x01",
+ /* INFO_CONF */ [0x1a] = "\x03\x23\x01",
+ /* FACILITY_CONF */ [0x1b] = "\x03\x23\x1f\x1b\x01",
+ /* SELECT_B_PROTOCOL_CONF */ [0x1c] = "\x03\x23\x01",
+ /* CONNECT_B3_CONF */ [0x1d] = "\x03\x23\x01",
+ /* DISCONNECT_B3_CONF */ [0x1f] = "\x03\x23\x01",
+ /* DATA_B3_CONF */ [0x21] = "\x03\x19\x23\x01",
+ /* RESET_B3_CONF */ [0x22] = "\x03\x23\x01",
+ /* CONNECT_IND */ [0x26] = "\x03\x14\x0e\x10\x0f\x11\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* CONNECT_ACTIVE_IND */ [0x27] = "\x03\x16\x17\x28\x01",
+ /* DISCONNECT_IND */ [0x28] = "\x03\x2c\x01",
+ /* MANUFACTURER_CONF */ [0x2a] = "\x03\x2a\x15\x21\x29\x01",
+ /* INFO_IND */ [0x2c] = "\x03\x26\x24\x01",
+ /* FACILITY_IND */ [0x2d] = "\x03\x1f\x1d\x01",
+ /* CONNECT_B3_IND */ [0x2f] = "\x03\x2b\x01",
+ /* CONNECT_B3_ACTIVE_IND */ [0x30] = "\x03\x2b\x01",
+ /* DISCONNECT_B3_IND */ [0x31] = "\x03\x2d\x2b\x01",
+ /* DATA_B3_IND */ [0x33] = "\x03\x18\x1a\x19\x20\x01",
+ /* RESET_B3_IND */ [0x34] = "\x03\x2b\x01",
+ /* CONNECT_B3_T90_ACTIVE_IND */ [0x35] = "\x03\x2b\x01",
+ /* CONNECT_RESP */ [0x38] = "\x03\x2e\x0d\x06\x08\x0a\x05\x07\x09\x01\x16\x17\x28\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* CONNECT_ACTIVE_RESP */ [0x39] = "\x03\x01",
+ /* DISCONNECT_RESP */ [0x3a] = "\x03\x01",
+ /* MANUFACTURER_IND */ [0x3c] = "\x03\x2a\x15\x21\x29\x01",
+ /* INFO_RESP */ [0x3e] = "\x03\x01",
+ /* FACILITY_RESP */ [0x3f] = "\x03\x1f\x01",
+ /* CONNECT_B3_RESP */ [0x41] = "\x03\x2e\x2b\x01",
+ /* CONNECT_B3_ACTIVE_RESP */ [0x42] = "\x03\x01",
+ /* DISCONNECT_B3_RESP */ [0x43] = "\x03\x01",
+ /* DATA_B3_RESP */ [0x45] = "\x03\x19\x01",
+ /* RESET_B3_RESP */ [0x46] = "\x03\x01",
+ /* CONNECT_B3_T90_ACTIVE_RESP */ [0x47] = "\x03\x01",
+ /* MANUFACTURER_RESP */ [0x4e] = "\x03\x2a\x15\x21\x29\x01",
+};
+
+/*-------------------------------------------------------*/
+
+#define byteTLcpy(x,y) *(u8 *)(x)=*(u8 *)(y);
+#define wordTLcpy(x,y) *(u16 *)(x)=*(u16 *)(y);
+#define dwordTLcpy(x,y) memcpy(x,y,4);
+#define structTLcpy(x,y,l) memcpy (x,y,l)
+#define structTLcpyovl(x,y,l) memmove (x,y,l)
+
+#define byteTRcpy(x,y) *(u8 *)(y)=*(u8 *)(x);
+#define wordTRcpy(x,y) *(u16 *)(y)=*(u16 *)(x);
+#define dwordTRcpy(x,y) memcpy(y,x,4);
+#define structTRcpy(x,y,l) memcpy (y,x,l)
+#define structTRcpyovl(x,y,l) memmove (y,x,l)
+
+/*-------------------------------------------------------*/
+static unsigned command_2_index(unsigned c, unsigned sc)
+{
+ if (c & 0x80)
+ c = 0x9 + (c & 0x0f);
+ else if (c <= 0x0f);
+ else if (c == 0x41)
+ c = 0x9 + 0x1;
+ else if (c == 0xff)
+ c = 0x00;
+ return (sc & 3) * (0x9 + 0x9) + c;
+}
+
+/*-------------------------------------------------------*/
+#define TYP (cdef[cmsg->par[cmsg->p]].typ)
+#define OFF (((u8 *)cmsg)+cdef[cmsg->par[cmsg->p]].off)
+
+static void jumpcstruct(_cmsg * cmsg)
+{
+ unsigned layer;
+ for (cmsg->p++, layer = 1; layer;) {
+ /* $$$$$ assert (cmsg->p); */
+ cmsg->p++;
+ switch (TYP) {
+ case _CMSTRUCT:
+ layer++;
+ break;
+ case _CEND:
+ layer--;
+ break;
+ }
+ }
+}
+/*-------------------------------------------------------*/
+static void pars_2_message(_cmsg * cmsg)
+{
+
+ for (; TYP != _CEND; cmsg->p++) {
+ switch (TYP) {
+ case _CBYTE:
+ byteTLcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l++;
+ break;
+ case _CWORD:
+ wordTLcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l += 2;
+ break;
+ case _CDWORD:
+ dwordTLcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l += 4;
+ break;
+ case _CSTRUCT:
+ if (*(u8 **) OFF == 0) {
+ *(cmsg->m + cmsg->l) = '\0';
+ cmsg->l++;
+ } else if (**(_cstruct *) OFF != 0xff) {
+ structTLcpy(cmsg->m + cmsg->l, *(_cstruct *) OFF, 1 + **(_cstruct *) OFF);
+ cmsg->l += 1 + **(_cstruct *) OFF;
+ } else {
+ _cstruct s = *(_cstruct *) OFF;
+ structTLcpy(cmsg->m + cmsg->l, s, 3 + *(u16 *) (s + 1));
+ cmsg->l += 3 + *(u16 *) (s + 1);
+ }
+ break;
+ case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+ if (*(_cmstruct *) OFF == CAPI_DEFAULT) {
+ *(cmsg->m + cmsg->l) = '\0';
+ cmsg->l++;
+ jumpcstruct(cmsg);
+ }
+/*----- Metastruktur wird composed -----*/
+ else {
+ unsigned _l = cmsg->l;
+ unsigned _ls;
+ cmsg->l++;
+ cmsg->p++;
+ pars_2_message(cmsg);
+ _ls = cmsg->l - _l - 1;
+ if (_ls < 255)
+ (cmsg->m + _l)[0] = (u8) _ls;
+ else {
+ structTLcpyovl(cmsg->m + _l + 3, cmsg->m + _l + 1, _ls);
+ (cmsg->m + _l)[0] = 0xff;
+ wordTLcpy(cmsg->m + _l + 1, &_ls);
+ }
+ }
+ break;
+ }
+ }
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_cmsg2message(_cmsg * cmsg, u8 * msg)
+{
+ cmsg->m = msg;
+ cmsg->l = 8;
+ cmsg->p = 0;
+ cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)];
+
+ pars_2_message(cmsg);
+
+ wordTLcpy(msg + 0, &cmsg->l);
+ byteTLcpy(cmsg->m + 4, &cmsg->Command);
+ byteTLcpy(cmsg->m + 5, &cmsg->Subcommand);
+ wordTLcpy(cmsg->m + 2, &cmsg->ApplId);
+ wordTLcpy(cmsg->m + 6, &cmsg->Messagenumber);
+
+ return 0;
+}
+
+/*-------------------------------------------------------*/
+static void message_2_pars(_cmsg * cmsg)
+{
+ for (; TYP != _CEND; cmsg->p++) {
+
+ switch (TYP) {
+ case _CBYTE:
+ byteTRcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l++;
+ break;
+ case _CWORD:
+ wordTRcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l += 2;
+ break;
+ case _CDWORD:
+ dwordTRcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l += 4;
+ break;
+ case _CSTRUCT:
+ *(u8 **) OFF = cmsg->m + cmsg->l;
+
+ if (cmsg->m[cmsg->l] != 0xff)
+ cmsg->l += 1 + cmsg->m[cmsg->l];
+ else
+ cmsg->l += 3 + *(u16 *) (cmsg->m + cmsg->l + 1);
+ break;
+ case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+ if (cmsg->m[cmsg->l] == '\0') {
+ *(_cmstruct *) OFF = CAPI_DEFAULT;
+ cmsg->l++;
+ jumpcstruct(cmsg);
+ } else {
+ unsigned _l = cmsg->l;
+ *(_cmstruct *) OFF = CAPI_COMPOSE;
+ cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1;
+ cmsg->p++;
+ message_2_pars(cmsg);
+ }
+ break;
+ }
+ }
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_message2cmsg(_cmsg * cmsg, u8 * msg)
+{
+ memset(cmsg, 0, sizeof(_cmsg));
+ cmsg->m = msg;
+ cmsg->l = 8;
+ cmsg->p = 0;
+ byteTRcpy(cmsg->m + 4, &cmsg->Command);
+ byteTRcpy(cmsg->m + 5, &cmsg->Subcommand);
+ cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)];
+
+ message_2_pars(cmsg);
+
+ wordTRcpy(msg + 0, &cmsg->l);
+ wordTRcpy(cmsg->m + 2, &cmsg->ApplId);
+ wordTRcpy(cmsg->m + 6, &cmsg->Messagenumber);
+
+ return 0;
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_cmsg_header(_cmsg * cmsg, u16 _ApplId,
+ u8 _Command, u8 _Subcommand,
+ u16 _Messagenumber, u32 _Controller)
+{
+ memset(cmsg, 0, sizeof(_cmsg));
+ cmsg->ApplId = _ApplId;
+ cmsg->Command = _Command;
+ cmsg->Subcommand = _Subcommand;
+ cmsg->Messagenumber = _Messagenumber;
+ cmsg->adr.adrController = _Controller;
+ return 0;
+}
+
+/*-------------------------------------------------------*/
+
+static char *mnames[] =
+{
+ [0x01] = "ALERT_REQ",
+ [0x02] = "CONNECT_REQ",
+ [0x04] = "DISCONNECT_REQ",
+ [0x05] = "LISTEN_REQ",
+ [0x08] = "INFO_REQ",
+ [0x09] = "FACILITY_REQ",
+ [0x0a] = "SELECT_B_PROTOCOL_REQ",
+ [0x0b] = "CONNECT_B3_REQ",
+ [0x0d] = "DISCONNECT_B3_REQ",
+ [0x0f] = "DATA_B3_REQ",
+ [0x10] = "RESET_B3_REQ",
+ [0x13] = "ALERT_CONF",
+ [0x14] = "CONNECT_CONF",
+ [0x16] = "DISCONNECT_CONF",
+ [0x17] = "LISTEN_CONF",
+ [0x18] = "MANUFACTURER_REQ",
+ [0x1a] = "INFO_CONF",
+ [0x1b] = "FACILITY_CONF",
+ [0x1c] = "SELECT_B_PROTOCOL_CONF",
+ [0x1d] = "CONNECT_B3_CONF",
+ [0x1f] = "DISCONNECT_B3_CONF",
+ [0x21] = "DATA_B3_CONF",
+ [0x22] = "RESET_B3_CONF",
+ [0x26] = "CONNECT_IND",
+ [0x27] = "CONNECT_ACTIVE_IND",
+ [0x28] = "DISCONNECT_IND",
+ [0x2a] = "MANUFACTURER_CONF",
+ [0x2c] = "INFO_IND",
+ [0x2d] = "FACILITY_IND",
+ [0x2f] = "CONNECT_B3_IND",
+ [0x30] = "CONNECT_B3_ACTIVE_IND",
+ [0x31] = "DISCONNECT_B3_IND",
+ [0x33] = "DATA_B3_IND",
+ [0x34] = "RESET_B3_IND",
+ [0x35] = "CONNECT_B3_T90_ACTIVE_IND",
+ [0x38] = "CONNECT_RESP",
+ [0x39] = "CONNECT_ACTIVE_RESP",
+ [0x3a] = "DISCONNECT_RESP",
+ [0x3c] = "MANUFACTURER_IND",
+ [0x3e] = "INFO_RESP",
+ [0x3f] = "FACILITY_RESP",
+ [0x41] = "CONNECT_B3_RESP",
+ [0x42] = "CONNECT_B3_ACTIVE_RESP",
+ [0x43] = "DISCONNECT_B3_RESP",
+ [0x45] = "DATA_B3_RESP",
+ [0x46] = "RESET_B3_RESP",
+ [0x47] = "CONNECT_B3_T90_ACTIVE_RESP",
+ [0x4e] = "MANUFACTURER_RESP"
+};
+
+char *capi_cmd2str(u8 cmd, u8 subcmd)
+{
+ return mnames[command_2_index(cmd, subcmd)];
+}
+
+
+/*-------------------------------------------------------*/
+/*-------------------------------------------------------*/
+
+static char *pnames[] =
+{
+ /*00 */ NULL,
+ /*01 */ NULL,
+ /*02 */ NULL,
+ /*03 */ "Controller/PLCI/NCCI",
+ /*04 */ "AdditionalInfo",
+ /*05 */ "B1configuration",
+ /*06 */ "B1protocol",
+ /*07 */ "B2configuration",
+ /*08 */ "B2protocol",
+ /*09 */ "B3configuration",
+ /*0a */ "B3protocol",
+ /*0b */ "BC",
+ /*0c */ "BChannelinformation",
+ /*0d */ "BProtocol",
+ /*0e */ "CalledPartyNumber",
+ /*0f */ "CalledPartySubaddress",
+ /*10 */ "CallingPartyNumber",
+ /*11 */ "CallingPartySubaddress",
+ /*12 */ "CIPmask",
+ /*13 */ "CIPmask2",
+ /*14 */ "CIPValue",
+ /*15 */ "Class",
+ /*16 */ "ConnectedNumber",
+ /*17 */ "ConnectedSubaddress",
+ /*18 */ "Data32",
+ /*19 */ "DataHandle",
+ /*1a */ "DataLength",
+ /*1b */ "FacilityConfirmationParameter",
+ /*1c */ "Facilitydataarray",
+ /*1d */ "FacilityIndicationParameter",
+ /*1e */ "FacilityRequestParameter",
+ /*1f */ "FacilitySelector",
+ /*20 */ "Flags",
+ /*21 */ "Function",
+ /*22 */ "HLC",
+ /*23 */ "Info",
+ /*24 */ "InfoElement",
+ /*25 */ "InfoMask",
+ /*26 */ "InfoNumber",
+ /*27 */ "Keypadfacility",
+ /*28 */ "LLC",
+ /*29 */ "ManuData",
+ /*2a */ "ManuID",
+ /*2b */ "NCPI",
+ /*2c */ "Reason",
+ /*2d */ "Reason_B3",
+ /*2e */ "Reject",
+ /*2f */ "Useruserdata"
+};
+
+
+static char buf[8192];
+static char *p = NULL;
+
+#include <stdarg.h>
+
+/*-------------------------------------------------------*/
+static void bufprint(char *fmt,...)
+{
+ va_list f;
+ va_start(f, fmt);
+ vsprintf(p, fmt, f);
+ va_end(f);
+ p += strlen(p);
+}
+
+static void printstructlen(u8 * m, unsigned len)
+{
+ unsigned hex = 0;
+ for (; len; len--, m++)
+ if (isalnum(*m) || *m == ' ') {
+ if (hex)
+ bufprint(">");
+ bufprint("%c", *m);
+ hex = 0;
+ } else {
+ if (!hex)
+ bufprint("<%02x", *m);
+ else
+ bufprint(" %02x", *m);
+ hex = 1;
+ }
+ if (hex)
+ bufprint(">");
+}
+
+static void printstruct(u8 * m)
+{
+ unsigned len;
+ if (m[0] != 0xff) {
+ len = m[0];
+ m += 1;
+ } else {
+ len = ((u16 *) (m + 1))[0];
+ m += 3;
+ }
+ printstructlen(m, len);
+}
+
+/*-------------------------------------------------------*/
+#define NAME (pnames[cmsg->par[cmsg->p]])
+
+static void protocol_message_2_pars(_cmsg * cmsg, int level)
+{
+ for (; TYP != _CEND; cmsg->p++) {
+ int slen = 29 + 3 - level;
+ int i;
+
+ bufprint(" ");
+ for (i = 0; i < level - 1; i++)
+ bufprint(" ");
+
+ switch (TYP) {
+ case _CBYTE:
+ bufprint("%-*s = 0x%x\n", slen, NAME, *(u8 *) (cmsg->m + cmsg->l));
+ cmsg->l++;
+ break;
+ case _CWORD:
+ bufprint("%-*s = 0x%x\n", slen, NAME, *(u16 *) (cmsg->m + cmsg->l));
+ cmsg->l += 2;
+ break;
+ case _CDWORD:
+ bufprint("%-*s = 0x%lx\n", slen, NAME, *(u32 *) (cmsg->m + cmsg->l));
+ cmsg->l += 4;
+ break;
+ case _CSTRUCT:
+ bufprint("%-*s = ", slen, NAME);
+ if (cmsg->m[cmsg->l] == '\0')
+ bufprint("default");
+ else
+ printstruct(cmsg->m + cmsg->l);
+ bufprint("\n");
+ if (cmsg->m[cmsg->l] != 0xff)
+ cmsg->l += 1 + cmsg->m[cmsg->l];
+ else
+ cmsg->l += 3 + *(u16 *) (cmsg->m + cmsg->l + 1);
+
+ break;
+
+ case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+ if (cmsg->m[cmsg->l] == '\0') {
+ bufprint("%-*s = default\n", slen, NAME);
+ cmsg->l++;
+ jumpcstruct(cmsg);
+ } else {
+ char *name = NAME;
+ unsigned _l = cmsg->l;
+ bufprint("%-*s\n", slen, name);
+ cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1;
+ cmsg->p++;
+ protocol_message_2_pars(cmsg, level + 1);
+ }
+ break;
+ }
+ }
+}
+/*-------------------------------------------------------*/
+char *capi_message2str(u8 * msg)
+{
+
+ _cmsg cmsg;
+ p = buf;
+ p[0] = 0;
+
+ cmsg.m = msg;
+ cmsg.l = 8;
+ cmsg.p = 0;
+ byteTRcpy(cmsg.m + 4, &cmsg.Command);
+ byteTRcpy(cmsg.m + 5, &cmsg.Subcommand);
+ cmsg.par = cpars[command_2_index(cmsg.Command, cmsg.Subcommand)];
+
+ bufprint("%-26s ID=%03d #0x%04x LEN=%04d\n",
+ mnames[command_2_index(cmsg.Command, cmsg.Subcommand)],
+ ((unsigned short *) msg)[1],
+ ((unsigned short *) msg)[3],
+ ((unsigned short *) msg)[0]);
+
+ protocol_message_2_pars(&cmsg, 1);
+ return buf;
+}
+
+char *capi_cmsg2str(_cmsg * cmsg)
+{
+ p = buf;
+ p[0] = 0;
+ cmsg->l = 8;
+ cmsg->p = 0;
+ bufprint("%s ID=%03d #0x%04x LEN=%04d\n",
+ mnames[command_2_index(cmsg->Command, cmsg->Subcommand)],
+ ((u16 *) cmsg->m)[1],
+ ((u16 *) cmsg->m)[3],
+ ((u16 *) cmsg->m)[0]);
+ protocol_message_2_pars(cmsg, 1);
+ return buf;
+}
+
+EXPORT_SYMBOL(capi_cmsg2message);
+EXPORT_SYMBOL(capi_message2cmsg);
+EXPORT_SYMBOL(capi_cmsg_header);
+EXPORT_SYMBOL(capi_cmd2str);
+EXPORT_SYMBOL(capi_cmsg2str);
+EXPORT_SYMBOL(capi_message2str);
+EXPORT_SYMBOL(capi_info2str);
diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c
new file mode 100644
index 000000000000..feec40cf5900
--- /dev/null
+++ b/drivers/isdn/capi/kcapi.c
@@ -0,0 +1,991 @@
+/* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $
+ *
+ * Kernel CAPI 2.0 Module
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define CONFIG_AVMB1_COMPAT
+
+#include "kcapi.h"
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#ifdef CONFIG_AVMB1_COMPAT
+#include <linux/b1lli.h>
+#endif
+
+static char *revision = "$Revision: 1.1.2.8 $";
+
+/* ------------------------------------------------------------- */
+
+static int showcapimsgs = 0;
+
+MODULE_DESCRIPTION("CAPI4Linux: kernel CAPI layer");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+module_param(showcapimsgs, uint, 0);
+
+/* ------------------------------------------------------------- */
+
+struct capi_notifier {
+ struct work_struct work;
+ unsigned int cmd;
+ u32 controller;
+ u16 applid;
+ u32 ncci;
+};
+
+/* ------------------------------------------------------------- */
+
+static struct capi_version driver_version = {2, 0, 1, 1<<4};
+static char driver_serial[CAPI_SERIAL_LEN] = "0004711";
+static char capi_manufakturer[64] = "AVM Berlin";
+
+#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f)
+
+LIST_HEAD(capi_drivers);
+DEFINE_RWLOCK(capi_drivers_list_lock);
+
+static DEFINE_RWLOCK(application_lock);
+static DECLARE_MUTEX(controller_sem);
+
+struct capi20_appl *capi_applications[CAPI_MAXAPPL];
+struct capi_ctr *capi_cards[CAPI_MAXCONTR];
+
+static int ncards;
+
+/* -------- controller ref counting -------------------------------------- */
+
+static inline struct capi_ctr *
+capi_ctr_get(struct capi_ctr *card)
+{
+ if (!try_module_get(card->owner))
+ return NULL;
+ return card;
+}
+
+static inline void
+capi_ctr_put(struct capi_ctr *card)
+{
+ module_put(card->owner);
+}
+
+/* ------------------------------------------------------------- */
+
+static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr)
+{
+ if (contr - 1 >= CAPI_MAXCONTR)
+ return NULL;
+
+ return capi_cards[contr - 1];
+}
+
+static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid)
+{
+ if (applid - 1 >= CAPI_MAXAPPL)
+ return NULL;
+
+ return capi_applications[applid - 1];
+}
+
+/* -------- util functions ------------------------------------ */
+
+static inline int capi_cmd_valid(u8 cmd)
+{
+ switch (cmd) {
+ case CAPI_ALERT:
+ case CAPI_CONNECT:
+ case CAPI_CONNECT_ACTIVE:
+ case CAPI_CONNECT_B3_ACTIVE:
+ case CAPI_CONNECT_B3:
+ case CAPI_CONNECT_B3_T90_ACTIVE:
+ case CAPI_DATA_B3:
+ case CAPI_DISCONNECT_B3:
+ case CAPI_DISCONNECT:
+ case CAPI_FACILITY:
+ case CAPI_INFO:
+ case CAPI_LISTEN:
+ case CAPI_MANUFACTURER:
+ case CAPI_RESET_B3:
+ case CAPI_SELECT_B_PROTOCOL:
+ return 1;
+ }
+ return 0;
+}
+
+static inline int capi_subcmd_valid(u8 subcmd)
+{
+ switch (subcmd) {
+ case CAPI_REQ:
+ case CAPI_CONF:
+ case CAPI_IND:
+ case CAPI_RESP:
+ return 1;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static void register_appl(struct capi_ctr *card, u16 applid, capi_register_params *rparam)
+{
+ card = capi_ctr_get(card);
+
+ if (card)
+ card->register_appl(card, applid, rparam);
+ else
+ printk(KERN_WARNING "%s: cannot get card resources\n", __FUNCTION__);
+}
+
+
+static void release_appl(struct capi_ctr *card, u16 applid)
+{
+ DBG("applid %#x", applid);
+
+ card->release_appl(card, applid);
+ capi_ctr_put(card);
+}
+
+/* -------- KCI_CONTRUP --------------------------------------- */
+
+static void notify_up(u32 contr)
+{
+ struct capi_ctr *card = get_capi_ctr_by_nr(contr);
+ struct capi20_appl *ap;
+ u16 applid;
+
+ if (showcapimsgs & 1) {
+ printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr);
+ }
+ if (!card) {
+ printk(KERN_WARNING "%s: invalid contr %d\n", __FUNCTION__, contr);
+ return;
+ }
+ for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
+ ap = get_capi_appl_by_nr(applid);
+ if (!ap || ap->release_in_progress) continue;
+ register_appl(card, applid, &ap->rparam);
+ if (ap->callback && !ap->release_in_progress)
+ ap->callback(KCI_CONTRUP, contr, &card->profile);
+ }
+}
+
+/* -------- KCI_CONTRDOWN ------------------------------------- */
+
+static void notify_down(u32 contr)
+{
+ struct capi20_appl *ap;
+ u16 applid;
+
+ if (showcapimsgs & 1) {
+ printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr);
+ }
+
+ for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
+ ap = get_capi_appl_by_nr(applid);
+ if (ap && ap->callback && !ap->release_in_progress)
+ ap->callback(KCI_CONTRDOWN, contr, NULL);
+ }
+}
+
+static void notify_handler(void *data)
+{
+ struct capi_notifier *np = data;
+
+ switch (np->cmd) {
+ case KCI_CONTRUP:
+ notify_up(np->controller);
+ break;
+ case KCI_CONTRDOWN:
+ notify_down(np->controller);
+ break;
+ }
+
+ kfree(np);
+}
+
+/*
+ * The notifier will result in adding/deleteing of devices. Devices can
+ * only removed in user process, not in bh.
+ */
+static int notify_push(unsigned int cmd, u32 controller, u16 applid, u32 ncci)
+{
+ struct capi_notifier *np = kmalloc(sizeof(*np), GFP_ATOMIC);
+
+ if (!np)
+ return -ENOMEM;
+
+ INIT_WORK(&np->work, notify_handler, np);
+ np->cmd = cmd;
+ np->controller = controller;
+ np->applid = applid;
+ np->ncci = ncci;
+
+ schedule_work(&np->work);
+ return 0;
+}
+
+
+/* -------- Receiver ------------------------------------------ */
+
+static void recv_handler(void *_ap)
+{
+ struct sk_buff *skb;
+ struct capi20_appl *ap = (struct capi20_appl *) _ap;
+
+ if ((!ap) || (ap->release_in_progress))
+ return;
+
+ down(&ap->recv_sem);
+ while ((skb = skb_dequeue(&ap->recv_queue))) {
+ if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND)
+ ap->nrecvdatapkt++;
+ else
+ ap->nrecvctlpkt++;
+
+ ap->recv_message(ap, skb);
+ }
+ up(&ap->recv_sem);
+}
+
+void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb)
+{
+ struct capi20_appl *ap;
+ int showctl = 0;
+ u8 cmd, subcmd;
+ unsigned long flags;
+
+ if (card->cardstate != CARD_RUNNING) {
+ printk(KERN_INFO "kcapi: controller %d not active, got: %s",
+ card->cnr, capi_message2str(skb->data));
+ goto error;
+ }
+
+ cmd = CAPIMSG_COMMAND(skb->data);
+ subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+ if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) {
+ card->nrecvdatapkt++;
+ if (card->traceflag > 2) showctl |= 2;
+ } else {
+ card->nrecvctlpkt++;
+ if (card->traceflag) showctl |= 2;
+ }
+ showctl |= (card->traceflag & 1);
+ if (showctl & 2) {
+ if (showctl & 1) {
+ printk(KERN_DEBUG "kcapi: got [0x%lx] id#%d %s len=%u\n",
+ (unsigned long) card->cnr,
+ CAPIMSG_APPID(skb->data),
+ capi_cmd2str(cmd, subcmd),
+ CAPIMSG_LEN(skb->data));
+ } else {
+ printk(KERN_DEBUG "kcapi: got [0x%lx] %s\n",
+ (unsigned long) card->cnr,
+ capi_message2str(skb->data));
+ }
+
+ }
+
+ read_lock_irqsave(&application_lock, flags);
+ ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data));
+ if ((!ap) || (ap->release_in_progress)) {
+ read_unlock_irqrestore(&application_lock, flags);
+ printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n",
+ CAPIMSG_APPID(skb->data), capi_message2str(skb->data));
+ goto error;
+ }
+ skb_queue_tail(&ap->recv_queue, skb);
+ schedule_work(&ap->recv_work);
+ read_unlock_irqrestore(&application_lock, flags);
+
+ return;
+
+error:
+ kfree_skb(skb);
+}
+
+EXPORT_SYMBOL(capi_ctr_handle_message);
+
+void capi_ctr_ready(struct capi_ctr * card)
+{
+ card->cardstate = CARD_RUNNING;
+
+ printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
+ card->cnr, card->name);
+
+ notify_push(KCI_CONTRUP, card->cnr, 0, 0);
+}
+
+EXPORT_SYMBOL(capi_ctr_ready);
+
+void capi_ctr_reseted(struct capi_ctr * card)
+{
+ u16 appl;
+
+ DBG("");
+
+ if (card->cardstate == CARD_DETECTED)
+ return;
+
+ card->cardstate = CARD_DETECTED;
+
+ memset(card->manu, 0, sizeof(card->manu));
+ memset(&card->version, 0, sizeof(card->version));
+ memset(&card->profile, 0, sizeof(card->profile));
+ memset(card->serial, 0, sizeof(card->serial));
+
+ for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+ struct capi20_appl *ap = get_capi_appl_by_nr(appl);
+ if (!ap || ap->release_in_progress)
+ continue;
+
+ capi_ctr_put(card);
+ }
+
+ printk(KERN_NOTICE "kcapi: card %d down.\n", card->cnr);
+
+ notify_push(KCI_CONTRDOWN, card->cnr, 0, 0);
+}
+
+EXPORT_SYMBOL(capi_ctr_reseted);
+
+void capi_ctr_suspend_output(struct capi_ctr *card)
+{
+ if (!card->blocked) {
+ printk(KERN_DEBUG "kcapi: card %d suspend\n", card->cnr);
+ card->blocked = 1;
+ }
+}
+
+EXPORT_SYMBOL(capi_ctr_suspend_output);
+
+void capi_ctr_resume_output(struct capi_ctr *card)
+{
+ if (card->blocked) {
+ printk(KERN_DEBUG "kcapi: card %d resume\n", card->cnr);
+ card->blocked = 0;
+ }
+}
+
+EXPORT_SYMBOL(capi_ctr_resume_output);
+
+/* ------------------------------------------------------------- */
+
+int
+attach_capi_ctr(struct capi_ctr *card)
+{
+ int i;
+
+ down(&controller_sem);
+
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (capi_cards[i] == NULL)
+ break;
+ }
+ if (i == CAPI_MAXCONTR) {
+ up(&controller_sem);
+ printk(KERN_ERR "kcapi: out of controller slots\n");
+ return -EBUSY;
+ }
+ capi_cards[i] = card;
+
+ up(&controller_sem);
+
+ card->nrecvctlpkt = 0;
+ card->nrecvdatapkt = 0;
+ card->nsentctlpkt = 0;
+ card->nsentdatapkt = 0;
+ card->cnr = i + 1;
+ card->cardstate = CARD_DETECTED;
+ card->blocked = 0;
+ card->traceflag = showcapimsgs;
+
+ sprintf(card->procfn, "capi/controllers/%d", card->cnr);
+ card->procent = create_proc_entry(card->procfn, 0, NULL);
+ if (card->procent) {
+ card->procent->read_proc =
+ (int (*)(char *,char **,off_t,int,int *,void *))
+ card->ctr_read_proc;
+ card->procent->data = card;
+ }
+
+ ncards++;
+ printk(KERN_NOTICE "kcapi: Controller %d: %s attached\n",
+ card->cnr, card->name);
+ return 0;
+}
+
+EXPORT_SYMBOL(attach_capi_ctr);
+
+int detach_capi_ctr(struct capi_ctr *card)
+{
+ if (card->cardstate != CARD_DETECTED)
+ capi_ctr_reseted(card);
+
+ ncards--;
+
+ if (card->procent) {
+ remove_proc_entry(card->procfn, NULL);
+ card->procent = NULL;
+ }
+ capi_cards[card->cnr - 1] = NULL;
+ printk(KERN_NOTICE "kcapi: Controller %d: %s unregistered\n",
+ card->cnr, card->name);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(detach_capi_ctr);
+
+void register_capi_driver(struct capi_driver *driver)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&capi_drivers_list_lock, flags);
+ list_add_tail(&driver->list, &capi_drivers);
+ write_unlock_irqrestore(&capi_drivers_list_lock, flags);
+}
+
+EXPORT_SYMBOL(register_capi_driver);
+
+void unregister_capi_driver(struct capi_driver *driver)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&capi_drivers_list_lock, flags);
+ list_del(&driver->list);
+ write_unlock_irqrestore(&capi_drivers_list_lock, flags);
+}
+
+EXPORT_SYMBOL(unregister_capi_driver);
+
+/* ------------------------------------------------------------- */
+/* -------- CAPI2.0 Interface ---------------------------------- */
+/* ------------------------------------------------------------- */
+
+u16 capi20_isinstalled(void)
+{
+ int i;
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (capi_cards[i] && capi_cards[i]->cardstate == CARD_RUNNING)
+ return CAPI_NOERROR;
+ }
+ return CAPI_REGNOTINSTALLED;
+}
+
+EXPORT_SYMBOL(capi20_isinstalled);
+
+u16 capi20_register(struct capi20_appl *ap)
+{
+ int i;
+ u16 applid;
+ unsigned long flags;
+
+ DBG("");
+
+ if (ap->rparam.datablklen < 128)
+ return CAPI_LOGBLKSIZETOSMALL;
+
+ write_lock_irqsave(&application_lock, flags);
+
+ for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
+ if (capi_applications[applid - 1] == NULL)
+ break;
+ }
+ if (applid > CAPI_MAXAPPL) {
+ write_unlock_irqrestore(&application_lock, flags);
+ return CAPI_TOOMANYAPPLS;
+ }
+
+ ap->applid = applid;
+ capi_applications[applid - 1] = ap;
+
+ ap->nrecvctlpkt = 0;
+ ap->nrecvdatapkt = 0;
+ ap->nsentctlpkt = 0;
+ ap->nsentdatapkt = 0;
+ ap->callback = NULL;
+ init_MUTEX(&ap->recv_sem);
+ skb_queue_head_init(&ap->recv_queue);
+ INIT_WORK(&ap->recv_work, recv_handler, (void *)ap);
+ ap->release_in_progress = 0;
+
+ write_unlock_irqrestore(&application_lock, flags);
+
+ down(&controller_sem);
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING)
+ continue;
+ register_appl(capi_cards[i], applid, &ap->rparam);
+ }
+ up(&controller_sem);
+
+ if (showcapimsgs & 1) {
+ printk(KERN_DEBUG "kcapi: appl %d up\n", applid);
+ }
+
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_register);
+
+u16 capi20_release(struct capi20_appl *ap)
+{
+ int i;
+ unsigned long flags;
+
+ DBG("applid %#x", ap->applid);
+
+ write_lock_irqsave(&application_lock, flags);
+ ap->release_in_progress = 1;
+ capi_applications[ap->applid - 1] = NULL;
+ write_unlock_irqrestore(&application_lock, flags);
+
+ down(&controller_sem);
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING)
+ continue;
+ release_appl(capi_cards[i], ap->applid);
+ }
+ up(&controller_sem);
+
+ flush_scheduled_work();
+ skb_queue_purge(&ap->recv_queue);
+
+ if (showcapimsgs & 1) {
+ printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid);
+ }
+
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_release);
+
+u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
+{
+ struct capi_ctr *card;
+ int showctl = 0;
+ u8 cmd, subcmd;
+
+ DBG("applid %#x", ap->applid);
+
+ if (ncards == 0)
+ return CAPI_REGNOTINSTALLED;
+ if ((ap->applid == 0) || ap->release_in_progress)
+ return CAPI_ILLAPPNR;
+ if (skb->len < 12
+ || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data))
+ || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data)))
+ return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+ card = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data));
+ if (!card || card->cardstate != CARD_RUNNING) {
+ card = get_capi_ctr_by_nr(1); // XXX why?
+ if (!card || card->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+ }
+ if (card->blocked)
+ return CAPI_SENDQUEUEFULL;
+
+ cmd = CAPIMSG_COMMAND(skb->data);
+ subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+ if (cmd == CAPI_DATA_B3 && subcmd== CAPI_REQ) {
+ card->nsentdatapkt++;
+ ap->nsentdatapkt++;
+ if (card->traceflag > 2) showctl |= 2;
+ } else {
+ card->nsentctlpkt++;
+ ap->nsentctlpkt++;
+ if (card->traceflag) showctl |= 2;
+ }
+ showctl |= (card->traceflag & 1);
+ if (showctl & 2) {
+ if (showctl & 1) {
+ printk(KERN_DEBUG "kcapi: put [%#x] id#%d %s len=%u\n",
+ CAPIMSG_CONTROLLER(skb->data),
+ CAPIMSG_APPID(skb->data),
+ capi_cmd2str(cmd, subcmd),
+ CAPIMSG_LEN(skb->data));
+ } else {
+ printk(KERN_DEBUG "kcapi: put [%#x] %s\n",
+ CAPIMSG_CONTROLLER(skb->data),
+ capi_message2str(skb->data));
+ }
+
+ }
+ return card->send_message(card, skb);
+}
+
+EXPORT_SYMBOL(capi20_put_message);
+
+u16 capi20_get_manufacturer(u32 contr, u8 *buf)
+{
+ struct capi_ctr *card;
+
+ if (contr == 0) {
+ strlcpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
+ return CAPI_NOERROR;
+ }
+ card = get_capi_ctr_by_nr(contr);
+ if (!card || card->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+ strlcpy(buf, card->manu, CAPI_MANUFACTURER_LEN);
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_manufacturer);
+
+u16 capi20_get_version(u32 contr, struct capi_version *verp)
+{
+ struct capi_ctr *card;
+
+ if (contr == 0) {
+ *verp = driver_version;
+ return CAPI_NOERROR;
+ }
+ card = get_capi_ctr_by_nr(contr);
+ if (!card || card->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+
+ memcpy((void *) verp, &card->version, sizeof(capi_version));
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_version);
+
+u16 capi20_get_serial(u32 contr, u8 *serial)
+{
+ struct capi_ctr *card;
+
+ if (contr == 0) {
+ strlcpy(serial, driver_serial, CAPI_SERIAL_LEN);
+ return CAPI_NOERROR;
+ }
+ card = get_capi_ctr_by_nr(contr);
+ if (!card || card->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+
+ strlcpy((void *) serial, card->serial, CAPI_SERIAL_LEN);
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_serial);
+
+u16 capi20_get_profile(u32 contr, struct capi_profile *profp)
+{
+ struct capi_ctr *card;
+
+ if (contr == 0) {
+ profp->ncontroller = ncards;
+ return CAPI_NOERROR;
+ }
+ card = get_capi_ctr_by_nr(contr);
+ if (!card || card->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+
+ memcpy((void *) profp, &card->profile,
+ sizeof(struct capi_profile));
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_profile);
+
+#ifdef CONFIG_AVMB1_COMPAT
+static int old_capi_manufacturer(unsigned int cmd, void __user *data)
+{
+ avmb1_loadandconfigdef ldef;
+ avmb1_extcarddef cdef;
+ avmb1_resetdef rdef;
+ capicardparams cparams;
+ struct capi_ctr *card;
+ struct capi_driver *driver = NULL;
+ capiloaddata ldata;
+ struct list_head *l;
+ unsigned long flags;
+ int retval;
+
+ switch (cmd) {
+ case AVMB1_ADDCARD:
+ case AVMB1_ADDCARD_WITH_TYPE:
+ if (cmd == AVMB1_ADDCARD) {
+ if ((retval = copy_from_user(&cdef, data,
+ sizeof(avmb1_carddef))))
+ return retval;
+ cdef.cardtype = AVM_CARDTYPE_B1;
+ } else {
+ if ((retval = copy_from_user(&cdef, data,
+ sizeof(avmb1_extcarddef))))
+ return retval;
+ }
+ cparams.port = cdef.port;
+ cparams.irq = cdef.irq;
+ cparams.cardnr = cdef.cardnr;
+
+ read_lock_irqsave(&capi_drivers_list_lock, flags);
+ switch (cdef.cardtype) {
+ case AVM_CARDTYPE_B1:
+ list_for_each(l, &capi_drivers) {
+ driver = list_entry(l, struct capi_driver, list);
+ if (strcmp(driver->name, "b1isa") == 0)
+ break;
+ }
+ break;
+ case AVM_CARDTYPE_T1:
+ list_for_each(l, &capi_drivers) {
+ driver = list_entry(l, struct capi_driver, list);
+ if (strcmp(driver->name, "t1isa") == 0)
+ break;
+ }
+ break;
+ default:
+ driver = NULL;
+ break;
+ }
+ if (!driver) {
+ read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+ printk(KERN_ERR "kcapi: driver not loaded.\n");
+ return -EIO;
+ }
+ if (!driver->add_card) {
+ read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+ printk(KERN_ERR "kcapi: driver has no add card function.\n");
+ return -EIO;
+ }
+
+ retval = driver->add_card(driver, &cparams);
+ read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+ return retval;
+
+ case AVMB1_LOAD:
+ case AVMB1_LOAD_AND_CONFIG:
+
+ if (cmd == AVMB1_LOAD) {
+ if (copy_from_user(&ldef, data,
+ sizeof(avmb1_loaddef)))
+ return -EFAULT;
+ ldef.t4config.len = 0;
+ ldef.t4config.data = NULL;
+ } else {
+ if (copy_from_user(&ldef, data,
+ sizeof(avmb1_loadandconfigdef)))
+ return -EFAULT;
+ }
+ card = get_capi_ctr_by_nr(ldef.contr);
+ card = capi_ctr_get(card);
+ if (!card)
+ return -ESRCH;
+ if (card->load_firmware == 0) {
+ printk(KERN_DEBUG "kcapi: load: no load function\n");
+ return -ESRCH;
+ }
+
+ if (ldef.t4file.len <= 0) {
+ printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len);
+ return -EINVAL;
+ }
+ if (ldef.t4file.data == 0) {
+ printk(KERN_DEBUG "kcapi: load: invalid parameter: dataptr is 0\n");
+ return -EINVAL;
+ }
+
+ ldata.firmware.user = 1;
+ ldata.firmware.data = ldef.t4file.data;
+ ldata.firmware.len = ldef.t4file.len;
+ ldata.configuration.user = 1;
+ ldata.configuration.data = ldef.t4config.data;
+ ldata.configuration.len = ldef.t4config.len;
+
+ if (card->cardstate != CARD_DETECTED) {
+ printk(KERN_INFO "kcapi: load: contr=%d not in detect state\n", ldef.contr);
+ return -EBUSY;
+ }
+ card->cardstate = CARD_LOADING;
+
+ retval = card->load_firmware(card, &ldata);
+
+ if (retval) {
+ card->cardstate = CARD_DETECTED;
+ capi_ctr_put(card);
+ return retval;
+ }
+
+ while (card->cardstate != CARD_RUNNING) {
+
+ msleep_interruptible(100); /* 0.1 sec */
+
+ if (signal_pending(current)) {
+ capi_ctr_put(card);
+ return -EINTR;
+ }
+ }
+ capi_ctr_put(card);
+ return 0;
+
+ case AVMB1_RESETCARD:
+ if (copy_from_user(&rdef, data, sizeof(avmb1_resetdef)))
+ return -EFAULT;
+ card = get_capi_ctr_by_nr(rdef.contr);
+ if (!card)
+ return -ESRCH;
+
+ if (card->cardstate == CARD_DETECTED)
+ return 0;
+
+ card->reset_ctr(card);
+
+ while (card->cardstate > CARD_DETECTED) {
+
+ msleep_interruptible(100); /* 0.1 sec */
+
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ return 0;
+
+ }
+ return -EINVAL;
+}
+#endif
+
+int capi20_manufacturer(unsigned int cmd, void __user *data)
+{
+ struct capi_ctr *card;
+
+ switch (cmd) {
+#ifdef CONFIG_AVMB1_COMPAT
+ case AVMB1_LOAD:
+ case AVMB1_LOAD_AND_CONFIG:
+ case AVMB1_RESETCARD:
+ case AVMB1_GET_CARDINFO:
+ case AVMB1_REMOVECARD:
+ return old_capi_manufacturer(cmd, data);
+#endif
+ case KCAPI_CMD_TRACE:
+ {
+ kcapi_flagdef fdef;
+
+ if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef)))
+ return -EFAULT;
+
+ card = get_capi_ctr_by_nr(fdef.contr);
+ if (!card)
+ return -ESRCH;
+
+ card->traceflag = fdef.flag;
+ printk(KERN_INFO "kcapi: contr %d set trace=%d\n",
+ card->cnr, card->traceflag);
+ return 0;
+ }
+ case KCAPI_CMD_ADDCARD:
+ {
+ struct list_head *l;
+ struct capi_driver *driver = NULL;
+ capicardparams cparams;
+ kcapi_carddef cdef;
+ int retval;
+
+ if ((retval = copy_from_user(&cdef, data, sizeof(cdef))))
+ return retval;
+
+ cparams.port = cdef.port;
+ cparams.irq = cdef.irq;
+ cparams.membase = cdef.membase;
+ cparams.cardnr = cdef.cardnr;
+ cparams.cardtype = 0;
+ cdef.driver[sizeof(cdef.driver)-1] = 0;
+
+ list_for_each(l, &capi_drivers) {
+ driver = list_entry(l, struct capi_driver, list);
+ if (strcmp(driver->name, cdef.driver) == 0)
+ break;
+ }
+ if (driver == 0) {
+ printk(KERN_ERR "kcapi: driver \"%s\" not loaded.\n",
+ cdef.driver);
+ return -ESRCH;
+ }
+
+ if (!driver->add_card) {
+ printk(KERN_ERR "kcapi: driver \"%s\" has no add card function.\n", cdef.driver);
+ return -EIO;
+ }
+
+ return driver->add_card(driver, &cparams);
+ }
+
+ default:
+ printk(KERN_ERR "kcapi: manufacturer command %d unknown.\n",
+ cmd);
+ break;
+
+ }
+ return -EINVAL;
+}
+
+EXPORT_SYMBOL(capi20_manufacturer);
+
+/* temporary hack */
+void capi20_set_callback(struct capi20_appl *ap,
+ void (*callback) (unsigned int cmd, __u32 contr, void *data))
+{
+ ap->callback = callback;
+}
+
+EXPORT_SYMBOL(capi20_set_callback);
+
+/* ------------------------------------------------------------- */
+/* -------- Init & Cleanup ------------------------------------- */
+/* ------------------------------------------------------------- */
+
+/*
+ * init / exit functions
+ */
+
+static int __init kcapi_init(void)
+{
+ char *p;
+ char rev[32];
+
+ kcapi_proc_init();
+
+ if ((p = strchr(revision, ':')) != 0 && p[1]) {
+ strlcpy(rev, p + 2, sizeof(rev));
+ if ((p = strchr(rev, '$')) != 0 && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ printk(KERN_NOTICE "CAPI Subsystem Rev %s\n", rev);
+
+ return 0;
+}
+
+static void __exit kcapi_exit(void)
+{
+ kcapi_proc_exit();
+
+ /* make sure all notifiers are finished */
+ flush_scheduled_work();
+}
+
+module_init(kcapi_init);
+module_exit(kcapi_exit);
diff --git a/drivers/isdn/capi/kcapi.h b/drivers/isdn/capi/kcapi.h
new file mode 100644
index 000000000000..1cb2c40f9921
--- /dev/null
+++ b/drivers/isdn/capi/kcapi.h
@@ -0,0 +1,49 @@
+/*
+ * Kernel CAPI 2.0 Module
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/isdn/capilli.h>
+
+#ifdef KCAPI_DEBUG
+#define DBG(format, arg...) do { \
+printk(KERN_DEBUG "%s: " format "\n" , __FUNCTION__ , ## arg); \
+} while (0)
+#else
+#define DBG(format, arg...) /* */
+#endif
+
+enum {
+ CARD_DETECTED = 1,
+ CARD_LOADING = 2,
+ CARD_RUNNING = 3,
+};
+
+extern struct list_head capi_drivers;
+extern rwlock_t capi_drivers_list_lock;
+
+extern struct capi20_appl *capi_applications[CAPI_MAXAPPL];
+extern struct capi_ctr *capi_cards[CAPI_MAXCONTR];
+
+#ifdef CONFIG_PROC_FS
+
+void kcapi_proc_init(void);
+void kcapi_proc_exit(void);
+
+#else
+
+static inline void kcapi_proc_init(void) { };
+static inline void kcapi_proc_exit(void) { };
+
+#endif
+
diff --git a/drivers/isdn/capi/kcapi_proc.c b/drivers/isdn/capi/kcapi_proc.c
new file mode 100644
index 000000000000..16dc5418ff41
--- /dev/null
+++ b/drivers/isdn/capi/kcapi_proc.c
@@ -0,0 +1,336 @@
+/*
+ * Kernel CAPI 2.0 Module - /proc/capi handling
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include "kcapi.h"
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+
+static char *
+cardstate2str(unsigned short cardstate)
+{
+ switch (cardstate) {
+ case CARD_DETECTED: return "detected";
+ case CARD_LOADING: return "loading";
+ case CARD_RUNNING: return "running";
+ default: return "???";
+ }
+}
+
+// /proc/capi
+// ===========================================================================
+
+// /proc/capi/controller:
+// cnr driver cardstate name driverinfo
+// /proc/capi/contrstats:
+// cnr nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt
+// ---------------------------------------------------------------------------
+
+static void *controller_start(struct seq_file *seq, loff_t *pos)
+{
+ if (*pos < CAPI_MAXCONTR)
+ return &capi_cards[*pos];
+
+ return NULL;
+}
+
+static void *controller_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ ++*pos;
+ if (*pos < CAPI_MAXCONTR)
+ return &capi_cards[*pos];
+
+ return NULL;
+}
+
+static void controller_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int controller_show(struct seq_file *seq, void *v)
+{
+ struct capi_ctr *ctr = *(struct capi_ctr **) v;
+
+ if (!ctr)
+ return 0;
+
+ seq_printf(seq, "%d %-10s %-8s %-16s %s\n",
+ ctr->cnr, ctr->driver_name,
+ cardstate2str(ctr->cardstate),
+ ctr->name,
+ ctr->procinfo ? ctr->procinfo(ctr) : "");
+
+ return 0;
+}
+
+static int contrstats_show(struct seq_file *seq, void *v)
+{
+ struct capi_ctr *ctr = *(struct capi_ctr **) v;
+
+ if (!ctr)
+ return 0;
+
+ seq_printf(seq, "%d %lu %lu %lu %lu\n",
+ ctr->cnr,
+ ctr->nrecvctlpkt,
+ ctr->nrecvdatapkt,
+ ctr->nsentctlpkt,
+ ctr->nsentdatapkt);
+
+ return 0;
+}
+
+struct seq_operations seq_controller_ops = {
+ .start = controller_start,
+ .next = controller_next,
+ .stop = controller_stop,
+ .show = controller_show,
+};
+
+struct seq_operations seq_contrstats_ops = {
+ .start = controller_start,
+ .next = controller_next,
+ .stop = controller_stop,
+ .show = contrstats_show,
+};
+
+static int seq_controller_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &seq_controller_ops);
+}
+
+static int seq_contrstats_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &seq_contrstats_ops);
+}
+
+static struct file_operations proc_controller_ops = {
+ .open = seq_controller_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static struct file_operations proc_contrstats_ops = {
+ .open = seq_contrstats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+// /proc/capi/applications:
+// applid l3cnt dblkcnt dblklen #ncci recvqueuelen
+// /proc/capi/applstats:
+// applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt
+// ---------------------------------------------------------------------------
+
+static void *
+applications_start(struct seq_file *seq, loff_t *pos)
+{
+ if (*pos < CAPI_MAXAPPL)
+ return &capi_applications[*pos];
+
+ return NULL;
+}
+
+static void *
+applications_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ ++*pos;
+ if (*pos < CAPI_MAXAPPL)
+ return &capi_applications[*pos];
+
+ return NULL;
+}
+
+static void
+applications_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int
+applications_show(struct seq_file *seq, void *v)
+{
+ struct capi20_appl *ap = *(struct capi20_appl **) v;
+
+ if (!ap)
+ return 0;
+
+ seq_printf(seq, "%u %d %d %d\n",
+ ap->applid,
+ ap->rparam.level3cnt,
+ ap->rparam.datablkcnt,
+ ap->rparam.datablklen);
+
+ return 0;
+}
+
+static int
+applstats_show(struct seq_file *seq, void *v)
+{
+ struct capi20_appl *ap = *(struct capi20_appl **) v;
+
+ if (!ap)
+ return 0;
+
+ seq_printf(seq, "%u %lu %lu %lu %lu\n",
+ ap->applid,
+ ap->nrecvctlpkt,
+ ap->nrecvdatapkt,
+ ap->nsentctlpkt,
+ ap->nsentdatapkt);
+
+ return 0;
+}
+
+struct seq_operations seq_applications_ops = {
+ .start = applications_start,
+ .next = applications_next,
+ .stop = applications_stop,
+ .show = applications_show,
+};
+
+struct seq_operations seq_applstats_ops = {
+ .start = applications_start,
+ .next = applications_next,
+ .stop = applications_stop,
+ .show = applstats_show,
+};
+
+static int
+seq_applications_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &seq_applications_ops);
+}
+
+static int
+seq_applstats_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &seq_applstats_ops);
+}
+
+static struct file_operations proc_applications_ops = {
+ .open = seq_applications_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static struct file_operations proc_applstats_ops = {
+ .open = seq_applstats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static void
+create_seq_entry(char *name, mode_t mode, struct file_operations *f)
+{
+ struct proc_dir_entry *entry;
+ entry = create_proc_entry(name, mode, NULL);
+ if (entry)
+ entry->proc_fops = f;
+}
+
+// ---------------------------------------------------------------------------
+
+
+static __inline__ struct capi_driver *capi_driver_get_idx(loff_t pos)
+{
+ struct capi_driver *drv = NULL;
+ struct list_head *l;
+ loff_t i;
+
+ i = 0;
+ list_for_each(l, &capi_drivers) {
+ drv = list_entry(l, struct capi_driver, list);
+ if (i++ == pos)
+ return drv;
+ }
+ return NULL;
+}
+
+static void *capi_driver_start(struct seq_file *seq, loff_t *pos)
+{
+ struct capi_driver *drv;
+ read_lock(&capi_drivers_list_lock);
+ drv = capi_driver_get_idx(*pos);
+ return drv;
+}
+
+static void *capi_driver_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ struct capi_driver *drv = (struct capi_driver *)v;
+ ++*pos;
+ if (drv->list.next == &capi_drivers) return NULL;
+ return list_entry(drv->list.next, struct capi_driver, list);
+}
+
+static void capi_driver_stop(struct seq_file *seq, void *v)
+{
+ read_unlock(&capi_drivers_list_lock);
+}
+
+static int capi_driver_show(struct seq_file *seq, void *v)
+{
+ struct capi_driver *drv = (struct capi_driver *)v;
+ seq_printf(seq, "%-32s %s\n", drv->name, drv->revision);
+ return 0;
+}
+
+struct seq_operations seq_capi_driver_ops = {
+ .start = capi_driver_start,
+ .next = capi_driver_next,
+ .stop = capi_driver_stop,
+ .show = capi_driver_show,
+};
+
+static int
+seq_capi_driver_open(struct inode *inode, struct file *file)
+{
+ int err;
+ err = seq_open(file, &seq_capi_driver_ops);
+ return err;
+}
+
+static struct file_operations proc_driver_ops = {
+ .open = seq_capi_driver_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+// ---------------------------------------------------------------------------
+
+void __init
+kcapi_proc_init(void)
+{
+ proc_mkdir("capi", NULL);
+ proc_mkdir("capi/controllers", NULL);
+ create_seq_entry("capi/controller", 0, &proc_controller_ops);
+ create_seq_entry("capi/contrstats", 0, &proc_contrstats_ops);
+ create_seq_entry("capi/applications", 0, &proc_applications_ops);
+ create_seq_entry("capi/applstats", 0, &proc_applstats_ops);
+ create_seq_entry("capi/driver", 0, &proc_driver_ops);
+}
+
+void __exit
+kcapi_proc_exit(void)
+{
+ remove_proc_entry("capi/driver", NULL);
+ remove_proc_entry("capi/controller", NULL);
+ remove_proc_entry("capi/contrstats", NULL);
+ remove_proc_entry("capi/applications", NULL);
+ remove_proc_entry("capi/applstats", NULL);
+ remove_proc_entry("capi/controllers", NULL);
+ remove_proc_entry("capi", NULL);
+}