patch-2.2.11 linux/drivers/isdn/avmb1/kcapi.c
Next file: linux/drivers/isdn/avmb1/t1isa.c
Previous file: linux/drivers/isdn/avmb1/compat.h
Back to the patch index
Back to the overall index
- Lines: 1523
- Date:
Mon Aug 9 12:04:39 1999
- Orig file:
v2.2.10/linux/drivers/isdn/avmb1/kcapi.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.2.10/linux/drivers/isdn/avmb1/kcapi.c linux/drivers/isdn/avmb1/kcapi.c
@@ -0,0 +1,1522 @@
+/*
+ * $Id: kcapi.c,v 1.5 1999/07/09 15:05:48 keil Exp $
+ *
+ * Kernel CAPI 2.0 Module
+ *
+ * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: kcapi.c,v $
+ * Revision 1.5 1999/07/09 15:05:48 keil
+ * compat.h is now isdn_compat.h
+ *
+ * Revision 1.4 1999/07/08 14:15:17 calle
+ * Forgot to count down ncards in drivercb_detach_ctr.
+ *
+ * Revision 1.3 1999/07/06 07:42:02 calle
+ * - changes in /proc interface
+ * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb.
+ *
+ * Revision 1.2 1999/07/05 15:09:52 calle
+ * - renamed "appl_release" to "appl_released".
+ * - version und profile data now cleared on controller reset
+ * - extended /proc interface, to allow driver and controller specific
+ * informations to include by driver hackers.
+ *
+ * Revision 1.1 1999/07/01 15:26:42 calle
+ * complete new version (I love it):
+ * + new hardware independed "capi_driver" interface that will make it easy to:
+ * - support other controllers with CAPI-2.0 (i.e. USB Controller)
+ * - write a CAPI-2.0 for the passive cards
+ * - support serial link CAPI-2.0 boxes.
+ * + wrote "capi_driver" for all supported cards.
+ * + "capi_driver" (supported cards) now have to be configured with
+ * make menuconfig, in the past all supported cards where included
+ * at once.
+ * + new and better informations in /proc/capi/
+ * + new ioctl to switch trace of capi messages per controller
+ * using "avmcapictrl trace [contr] on|off|...."
+ * + complete testcircle with all supported cards and also the
+ * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done.
+ *
+ */
+#define CONFIG_AVMB1_COMPAT
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <asm/segment.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/tqueue.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/isdn_compat.h>
+#include "capicmd.h"
+#include "capiutil.h"
+#include "capilli.h"
+#ifdef CONFIG_AVMB1_COMPAT
+#include <linux/b1lli.h>
+#endif
+
+static char *revision = "$Revision: 1.5 $";
+
+/* ------------------------------------------------------------- */
+
+#define CARD_FREE 0
+#define CARD_DETECTED 1
+#define CARD_LOADING 2
+#define CARD_RUNNING 3
+
+/* ------------------------------------------------------------- */
+
+int showcapimsgs = 0;
+
+MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
+MODULE_PARM(showcapimsgs, "0-4i");
+
+/* ------------------------------------------------------------- */
+
+struct msgidqueue {
+ struct msgidqueue *next;
+ __u16 msgid;
+};
+
+struct capi_ncci {
+ struct capi_ncci *next;
+ __u16 applid;
+ __u32 ncci;
+ __u32 winsize;
+ int nmsg;
+ struct msgidqueue *msgidqueue;
+ struct msgidqueue *msgidlast;
+ struct msgidqueue *msgidfree;
+ struct msgidqueue msgidpool[CAPI_MAXDATAWINDOW];
+};
+
+struct capi_appl {
+ __u16 applid;
+ capi_register_params rparam;
+ int releasing;
+ __u32 param;
+ void (*signal) (__u16 applid, __u32 param);
+ struct sk_buff_head recv_queue;
+ int nncci;
+ struct capi_ncci *nccilist;
+
+ unsigned long nrecvctlpkt;
+ unsigned long nrecvdatapkt;
+ unsigned long nsentctlpkt;
+ unsigned long nsentdatapkt;
+};
+
+/* ------------------------------------------------------------- */
+
+static struct capi_version driver_version = {2, 0, 1, 1<<4};
+static char driver_serial[CAPI_SERIAL_LEN] = "4711";
+static char capi_manufakturer[64] = "AVM Berlin";
+
+#define APPL(a) (&applications[(a)-1])
+#define VALID_APPLID(a) ((a) && (a) <= CAPI_MAXAPPL && APPL(a)->applid == a)
+#define APPL_IS_FREE(a) (APPL(a)->applid == 0)
+#define APPL_MARK_FREE(a) do{ APPL(a)->applid=0; MOD_DEC_USE_COUNT; }while(0);
+#define APPL_MARK_USED(a) do{ APPL(a)->applid=(a); MOD_INC_USE_COUNT; }while(0);
+
+#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f)
+
+#define VALID_CARD(c) ((c) > 0 && (c) <= CAPI_MAXCONTR)
+#define CARD(c) (&cards[(c)-1])
+#define CARDNR(cp) (((cp)-cards)+1)
+
+static struct capi_appl applications[CAPI_MAXAPPL];
+static struct capi_ctr cards[CAPI_MAXCONTR];
+static int ncards = 0;
+static struct sk_buff_head recv_queue;
+static struct capi_interface_user *capi_users = 0;
+static struct capi_driver *drivers;
+#ifdef CONFIG_AVMB1_COMPAT
+static struct capi_driver *b1isa_driver;
+static struct capi_driver *t1isa_driver;
+#endif
+static long notify_up_set = 0;
+static long notify_down_set = 0;
+
+static struct tq_struct tq_state_notify;
+static struct tq_struct tq_recv_notify;
+
+/* -------- util functions ------------------------------------ */
+
+static char *cardstate2str(unsigned short cardstate)
+{
+ switch (cardstate) {
+ default:
+ case CARD_FREE: return "free";
+ case CARD_DETECTED: return "detected";
+ case CARD_LOADING: return "loading";
+ case CARD_RUNNING: return "running";
+ }
+}
+
+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;
+}
+
+/* -------- /proc functions ----------------------------------- */
+/*
+ * /proc/capi/applications:
+ * applid l3cnt dblkcnt dblklen #ncci recvqueuelen
+ */
+static int proc_applications_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capi_appl *ap;
+ int i;
+ int len = 0;
+ off_t begin = 0;
+
+ for (i=0; i < CAPI_MAXAPPL; i++) {
+ ap = &applications[i];
+ if (ap->applid == 0) continue;
+ len += sprintf(page+len, "%u %d %d %d %d %d\n",
+ ap->applid,
+ ap->rparam.level3cnt,
+ ap->rparam.datablkcnt,
+ ap->rparam.datablklen,
+ ap->nncci,
+ skb_queue_len(&ap->recv_queue));
+ if (len+begin > off+count)
+ goto endloop;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+endloop:
+ if (i >= CAPI_MAXAPPL)
+ *eof = 1;
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * /proc/capi/ncci:
+ * applid ncci winsize nblk
+ */
+static int proc_ncci_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capi_appl *ap;
+ struct capi_ncci *np;
+ int i;
+ int len = 0;
+ off_t begin = 0;
+
+ for (i=0; i < CAPI_MAXAPPL; i++) {
+ ap = &applications[i];
+ if (ap->applid == 0) continue;
+ for (np = ap->nccilist; np; np = np->next) {
+ len += sprintf(page+len, "%d 0x%x %d %d\n",
+ np->applid,
+ np->ncci,
+ np->winsize,
+ np->nmsg);
+ if (len+begin > off+count)
+ goto endloop;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ }
+endloop:
+ if (i >= CAPI_MAXAPPL)
+ *eof = 1;
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * /proc/capi/driver:
+ * driver ncontroller
+ */
+static int proc_driver_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capi_driver *driver;
+ int len = 0;
+ off_t begin = 0;
+
+ for (driver = drivers; driver; driver = driver->next) {
+ len += sprintf(page+len, "%-32s %d %s\n",
+ driver->name,
+ driver->ncontroller,
+ driver->revision);
+ if (len+begin > off+count)
+ goto endloop;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+endloop:
+ if (!driver)
+ *eof = 1;
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * /proc/capi/users:
+ * name
+ */
+static int proc_users_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capi_interface_user *cp;
+ int len = 0;
+ off_t begin = 0;
+
+ for (cp = capi_users; cp ; cp = cp->next) {
+ len += sprintf(page+len, "%s\n", cp->name);
+ if (len+begin > off+count)
+ goto endloop;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+endloop:
+ if (cp == 0)
+ *eof = 1;
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * /proc/capi/controller:
+ * cnr driver cardstate name driverinfo
+ */
+static int proc_controller_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capi_ctr *cp;
+ int i;
+ int len = 0;
+ off_t begin = 0;
+
+ for (i=0; i < CAPI_MAXCONTR; i++) {
+ cp = &cards[i];
+ if (cp->cardstate == CARD_FREE) continue;
+ len += sprintf(page+len, "%d %-10s %-8s %-16s %s\n",
+ cp->cnr, cp->driver->name,
+ cardstate2str(cp->cardstate),
+ cp->name,
+ cp->driver->procinfo ? cp->driver->procinfo(cp) : ""
+ );
+ if (len+begin > off+count)
+ goto endloop;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+endloop:
+ if (i >= CAPI_MAXCONTR)
+ *eof = 1;
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * /proc/capi/applstats:
+ * applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt
+ */
+static int proc_applstats_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capi_appl *ap;
+ int i;
+ int len = 0;
+ off_t begin = 0;
+
+ for (i=0; i < CAPI_MAXAPPL; i++) {
+ ap = &applications[i];
+ if (ap->applid == 0) continue;
+ len += sprintf(page+len, "%u %lu %lu %lu %lu\n",
+ ap->applid,
+ ap->nrecvctlpkt,
+ ap->nrecvdatapkt,
+ ap->nsentctlpkt,
+ ap->nsentdatapkt);
+ if (len+begin > off+count)
+ goto endloop;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+endloop:
+ if (i >= CAPI_MAXAPPL)
+ *eof = 1;
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * /proc/capi/contrstats:
+ * cnr nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt
+ */
+static int proc_contrstats_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capi_ctr *cp;
+ int i;
+ int len = 0;
+ off_t begin = 0;
+
+ for (i=0; i < CAPI_MAXCONTR; i++) {
+ cp = &cards[i];
+ if (cp->cardstate == CARD_FREE) continue;
+ len += sprintf(page+len, "%d %lu %lu %lu %lu\n",
+ cp->cnr,
+ cp->nrecvctlpkt,
+ cp->nrecvdatapkt,
+ cp->nsentctlpkt,
+ cp->nsentdatapkt);
+ if (len+begin > off+count)
+ goto endloop;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+endloop:
+ if (i >= CAPI_MAXCONTR)
+ *eof = 1;
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+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/applications", 0 , proc_applications_read_proc },
+ { "capi/ncci", 0 , proc_ncci_read_proc },
+ { "capi/driver", 0 , proc_driver_read_proc },
+ { "capi/users", 0 , proc_users_read_proc },
+ { "capi/controller", 0 , proc_controller_read_proc },
+ { "capi/applstats", 0 , proc_applstats_read_proc },
+ { "capi/contrstats", 0 , proc_contrstats_read_proc },
+ { "capi/drivers", S_IFDIR, 0 },
+ { "capi/controllers", S_IFDIR, 0 },
+};
+
+static void proc_capi_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, 0);
+ if (p->procent) p->procent->read_proc = p->read_proc;
+ }
+}
+
+static void proc_capi_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, 0);
+ p->procent = 0;
+ }
+ }
+}
+
+/* -------- NCCI Handling ------------------------------------- */
+
+static inline void mq_init(struct capi_ncci * np)
+{
+ int i;
+ np->msgidqueue = 0;
+ np->msgidlast = 0;
+ 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 capi_ncci * np, __u16 msgid)
+{
+ struct msgidqueue *mq;
+ if ((mq = np->msgidfree) == 0)
+ return 0;
+ np->msgidfree = mq->next;
+ mq->msgid = msgid;
+ mq->next = 0;
+ 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 capi_ncci * np, __u16 msgid)
+{
+ struct msgidqueue **pp;
+ for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) {
+ if ((*pp)->msgid == msgid) {
+ struct msgidqueue *mq = *pp;
+ *pp = mq->next;
+ if (mq == np->msgidlast)
+ np->msgidlast = 0;
+ mq->next = np->msgidfree;
+ np->msgidfree = mq;
+ np->nmsg--;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void controllercb_appl_registered(struct capi_ctr * card, __u16 appl)
+{
+}
+
+static void controllercb_appl_released(struct capi_ctr * card, __u16 appl)
+{
+ struct capi_ncci **pp, **nextpp;
+ for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) {
+ if (NCCI2CTRL((*pp)->ncci) == card->cnr) {
+ struct capi_ncci *np = *pp;
+ *pp = np->next;
+ printk(KERN_INFO "kcapi: appl %d ncci 0x%x down!\n", appl, np->ncci);
+ kfree(np);
+ APPL(appl)->nncci--;
+ nextpp = pp;
+ } else {
+ nextpp = &(*pp)->next;
+ }
+ }
+ APPL(appl)->releasing--;
+ if (APPL(appl)->releasing <= 0) {
+ APPL(appl)->signal = 0;
+ APPL_MARK_FREE(appl);
+ printk(KERN_INFO "kcapi: appl %d down\n", appl);
+ }
+}
+/*
+ * ncci managment
+ */
+
+static void controllercb_new_ncci(struct capi_ctr * card,
+ __u16 appl, __u32 ncci, __u32 winsize)
+{
+ struct capi_ncci *np;
+ if (!VALID_APPLID(appl)) {
+ printk(KERN_ERR "avmb1_handle_new_ncci: illegal appl %d\n", appl);
+ return;
+ }
+ if ((np = (struct capi_ncci *) kmalloc(sizeof(struct capi_ncci), GFP_ATOMIC)) == 0) {
+ printk(KERN_ERR "capi_new_ncci: alloc failed ncci 0x%x\n", ncci);
+ return;
+ }
+ if (winsize > CAPI_MAXDATAWINDOW) {
+ printk(KERN_ERR "capi_new_ncci: winsize %d too big, set to %d\n",
+ winsize, CAPI_MAXDATAWINDOW);
+ winsize = CAPI_MAXDATAWINDOW;
+ }
+ np->applid = appl;
+ np->ncci = ncci;
+ np->winsize = winsize;
+ mq_init(np);
+ np->next = APPL(appl)->nccilist;
+ APPL(appl)->nccilist = np;
+ APPL(appl)->nncci++;
+ printk(KERN_INFO "kcapi: appl %d ncci 0x%x up\n", appl, ncci);
+
+}
+
+static void controllercb_free_ncci(struct capi_ctr * card,
+ __u16 appl, __u32 ncci)
+{
+ struct capi_ncci **pp;
+ if (!VALID_APPLID(appl)) {
+ printk(KERN_ERR "free_ncci: illegal appl %d\n", appl);
+ return;
+ }
+ for (pp = &APPL(appl)->nccilist; *pp; pp = &(*pp)->next) {
+ if ((*pp)->ncci == ncci) {
+ struct capi_ncci *np = *pp;
+ *pp = np->next;
+ kfree(np);
+ APPL(appl)->nncci--;
+ printk(KERN_INFO "kcapi: appl %d ncci 0x%x down\n", appl, ncci);
+ return;
+ }
+ }
+ printk(KERN_ERR "free_ncci: ncci 0x%x not found\n", ncci);
+}
+
+
+static struct capi_ncci *find_ncci(struct capi_appl * app, __u32 ncci)
+{
+ struct capi_ncci *np;
+ for (np = app->nccilist; np; np = np->next) {
+ if (np->ncci == ncci)
+ return np;
+ }
+ return 0;
+}
+
+/* -------- Receiver ------------------------------------------ */
+
+static void recv_handler(void *dummy)
+{
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&recv_queue)) != 0) {
+ __u16 appl = CAPIMSG_APPID(skb->data);
+ struct capi_ncci *np;
+ if (!VALID_APPLID(appl)) {
+ printk(KERN_ERR "kcapi: recv_handler: applid %d ? (%s)\n",
+ appl, capi_message2str(skb->data));
+ kfree_skb(skb);
+ continue;
+ }
+ if (APPL(appl)->signal == 0) {
+ printk(KERN_ERR "kcapi: recv_handler: applid %d has no signal function\n",
+ appl);
+ kfree_skb(skb);
+ continue;
+ }
+ if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
+ && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF
+ && (np = find_ncci(APPL(appl), CAPIMSG_NCCI(skb->data))) != 0
+ && mq_dequeue(np, CAPIMSG_MSGID(skb->data)) == 0) {
+ printk(KERN_ERR "kcapi: msgid %hu ncci 0x%x not on queue\n",
+ CAPIMSG_MSGID(skb->data), np->ncci);
+ }
+ if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
+ && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) {
+ APPL(appl)->nrecvdatapkt++;
+ } else {
+ APPL(appl)->nrecvctlpkt++;
+ }
+ skb_queue_tail(&APPL(appl)->recv_queue, skb);
+ (APPL(appl)->signal) (APPL(appl)->applid, APPL(appl)->param);
+ }
+}
+
+static void controllercb_handle_capimsg(struct capi_ctr * card,
+ __u16 appl, struct sk_buff *skb)
+{
+ int showctl = 0;
+ __u8 cmd, subcmd;
+
+ 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;
+ if (card->traceflag) showctl |= 2;
+ } else {
+ card->nrecvctlpkt++;
+ }
+ 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));
+ }
+
+ }
+ skb_queue_tail(&recv_queue, skb);
+ queue_task(&tq_recv_notify, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ return;
+
+error:
+ kfree_skb(skb);
+}
+
+/* -------- Notifier ------------------------------------------ */
+
+static void notify_up(__u16 contr)
+{
+ struct capi_interface_user *p;
+
+ printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);
+ for (p = capi_users; p; p = p->next) {
+ if (!p->callback) continue;
+ (*p->callback) (KCI_CONTRUP, contr, &CARD(contr)->profile);
+ }
+}
+
+static void notify_down(__u16 contr)
+{
+ struct capi_interface_user *p;
+ printk(KERN_NOTICE "kcapi: notify down contr %d\n", contr);
+ for (p = capi_users; p; p = p->next) {
+ if (!p->callback) continue;
+ (*p->callback) (KCI_CONTRDOWN, contr, 0);
+ }
+}
+
+static void notify_handler(void *dummy)
+{
+ __u16 contr;
+
+ for (contr=1; VALID_CARD(contr); contr++)
+ if (test_and_clear_bit(contr, ¬ify_up_set))
+ notify_up(contr);
+ for (contr=1; VALID_CARD(contr); contr++)
+ if (test_and_clear_bit(contr, ¬ify_down_set))
+ notify_down(contr);
+}
+
+
+static void controllercb_ready(struct capi_ctr * card)
+{
+ __u16 appl;
+
+ card->cardstate = CARD_RUNNING;
+
+ for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+ if (!VALID_APPLID(appl)) continue;
+ if (APPL(appl)->releasing) continue;
+ card->driver->register_appl(card, appl, &APPL(appl)->rparam);
+ }
+
+ set_bit(CARDNR(card), ¬ify_up_set);
+ queue_task(&tq_state_notify, &tq_scheduler);
+
+ printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
+ CARDNR(card), card->name);
+}
+
+static void controllercb_reseted(struct capi_ctr * card)
+{
+ __u16 appl;
+
+ if (card->cardstate == CARD_FREE)
+ return;
+ 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 capi_ncci **pp, **nextpp;
+ for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) {
+ if (NCCI2CTRL((*pp)->ncci) == card->cnr) {
+ struct capi_ncci *np = *pp;
+ *pp = np->next;
+ printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down!\n", appl, np->ncci);
+ kfree(np);
+ nextpp = pp;
+ } else {
+ nextpp = &(*pp)->next;
+ }
+ }
+ }
+ set_bit(CARDNR(card), ¬ify_down_set);
+ queue_task(&tq_state_notify, &tq_scheduler);
+ printk(KERN_NOTICE "kcapi: card %d down.\n", CARDNR(card));
+}
+
+static void controllercb_suspend_output(struct capi_ctr *card)
+{
+ if (!card->blocked) {
+ printk(KERN_DEBUG "kcapi: card %d suspend\n", CARDNR(card));
+ card->blocked = 1;
+ }
+}
+
+static void controllercb_resume_output(struct capi_ctr *card)
+{
+ if (card->blocked) {
+ printk(KERN_DEBUG "kcapi: card %d resume\n", CARDNR(card));
+ card->blocked = 0;
+ }
+}
+
+/* ------------------------------------------------------------- */
+
+
+struct capi_ctr *
+drivercb_attach_ctr(struct capi_driver *driver, char *name, void *driverdata)
+{
+ struct capi_ctr *card, **pp;
+ int i;
+
+ for (i=0; i < CAPI_MAXCONTR && cards[i].cardstate != CARD_FREE; i++) ;
+
+ if (i == CAPI_MAXCONTR) {
+ printk(KERN_ERR "kcapi: out of controller slots\n");
+ return 0;
+ }
+ card = &cards[i];
+ memset(card, 0, sizeof(struct capi_ctr));
+ card->driver = driver;
+ card->cnr = CARDNR(card);
+ strncpy(card->name, name, sizeof(card->name));
+ card->cardstate = CARD_DETECTED;
+ card->blocked = 0;
+ card->driverdata = driverdata;
+ card->traceflag = showcapimsgs;
+
+ card->ready = controllercb_ready;
+ card->reseted = controllercb_reseted;
+ card->suspend_output = controllercb_suspend_output;
+ card->resume_output = controllercb_resume_output;
+ card->handle_capimsg = controllercb_handle_capimsg;
+ card->appl_registered = controllercb_appl_registered;
+ card->appl_released = controllercb_appl_released;
+ card->new_ncci = controllercb_new_ncci;
+ card->free_ncci = controllercb_free_ncci;
+
+ for (pp = &driver->controller; *pp; pp = &(*pp)->next) ;
+ card->next = 0;
+ *pp = card;
+ driver->ncontroller++;
+ sprintf(card->procfn, "capi/controllers/%d", card->cnr);
+ card->procent = create_proc_entry(card->procfn, 0, 0);
+ if (card->procent) {
+ card->procent->read_proc =
+ (int (*)(char *,char **,off_t,int,int *,void *))
+ driver->ctr_read_proc;
+ card->procent->data = card;
+ }
+
+ ncards++;
+ printk(KERN_NOTICE "kcapi: Controller %d: %s attached\n",
+ card->cnr, card->name);
+ return card;
+}
+
+static int drivercb_detach_ctr(struct capi_ctr *card)
+{
+ struct capi_driver *driver = card->driver;
+ struct capi_ctr **pp;
+
+ if (card->cardstate == CARD_FREE)
+ return 0;
+ if (card->cardstate != CARD_DETECTED)
+ controllercb_reseted(card);
+ for (pp = &driver->controller; *pp ; pp = &(*pp)->next) {
+ if (*pp == card) {
+ *pp = card->next;
+ driver->ncontroller--;
+ ncards--;
+ break;
+ }
+ }
+ if (card->procent) {
+ remove_proc_entry(card->procfn, 0);
+ card->procent = 0;
+ }
+ card->cardstate = CARD_FREE;
+ printk(KERN_NOTICE "kcapi: Controller %d: %s unregistered\n",
+ card->cnr, card->name);
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+/* fallback if no driver read_proc function defined by driver */
+
+static int driver_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capi_driver *driver = (struct capi_driver *)data;
+ int len = 0;
+
+ len += sprintf(page+len, "%-16s %s\n", "name", driver->name);
+ len += sprintf(page+len, "%-16s %s\n", "revision", driver->revision);
+
+ if (len < off)
+ return 0;
+ *eof = 1;
+ *start = page - off;
+ return ((count < len-off) ? count : len-off);
+}
+
+/* ------------------------------------------------------------- */
+
+static struct capi_driver_interface di = {
+ drivercb_attach_ctr,
+ drivercb_detach_ctr,
+};
+
+struct capi_driver_interface *attach_capi_driver(struct capi_driver *driver)
+{
+ struct capi_driver **pp;
+
+ for (pp = &drivers; *pp; pp = &(*pp)->next) ;
+ driver->next = 0;
+ *pp = driver;
+ printk(KERN_NOTICE "kcapi: driver %s attached\n", driver->name);
+#ifdef CONFIG_AVMB1_COMPAT
+ if (strcmp(driver->name, "b1isa") == 0 && driver->add_card)
+ b1isa_driver = driver;
+ if (strcmp(driver->name, "t1isa") == 0 && driver->add_card)
+ t1isa_driver = driver;
+#endif
+ sprintf(driver->procfn, "capi/drivers/%s", driver->name);
+ driver->procent = create_proc_entry(driver->procfn, 0, 0);
+ if (driver->procent) {
+ if (driver->driver_read_proc) {
+ driver->procent->read_proc =
+ (int (*)(char *,char **,off_t,int,int *,void *))
+ driver->driver_read_proc;
+ } else {
+ driver->procent->read_proc = driver_read_proc;
+ }
+ driver->procent->data = driver;
+ }
+ return &di;
+}
+
+void detach_capi_driver(struct capi_driver *driver)
+{
+ struct capi_driver **pp;
+ for (pp = &drivers; *pp && *pp != driver; pp = &(*pp)->next) ;
+ if (*pp) {
+ *pp = (*pp)->next;
+#ifdef CONFIG_AVMB1_COMPAT
+ if (driver == b1isa_driver) b1isa_driver = 0;
+ if (driver == t1isa_driver) t1isa_driver = 0;
+#endif
+ printk(KERN_NOTICE "kcapi: driver %s detached\n", driver->name);
+ } else {
+ printk(KERN_ERR "kcapi: driver %s double detach ?\n", driver->name);
+ }
+ if (driver->procent) {
+ remove_proc_entry(driver->procfn, 0);
+ driver->procent = 0;
+ }
+}
+
+/* ------------------------------------------------------------- */
+/* -------- CAPI2.0 Interface ---------------------------------- */
+/* ------------------------------------------------------------- */
+
+static int capi_installed(void)
+{
+ int i;
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (cards[i].cardstate == CARD_RUNNING)
+ return 1;
+ }
+ return 0;
+}
+
+static __u16 capi_register(capi_register_params * rparam, __u16 * applidp)
+{
+ int appl;
+ int i;
+
+ if (rparam->datablklen < 128)
+ return CAPI_LOGBLKSIZETOSMALL;
+
+ for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+ if (APPL_IS_FREE(appl))
+ break;
+ }
+ if (appl > CAPI_MAXAPPL)
+ return CAPI_TOOMANYAPPLS;
+
+ APPL_MARK_USED(appl);
+ skb_queue_head_init(&APPL(appl)->recv_queue);
+ APPL(appl)->nncci = 0;
+
+ memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params));
+
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (cards[i].cardstate != CARD_RUNNING)
+ continue;
+ cards[i].driver->register_appl(&cards[i], appl,
+ &APPL(appl)->rparam);
+ }
+ *applidp = appl;
+ printk(KERN_INFO "kcapi: appl %d up\n", appl);
+
+ return CAPI_NOERROR;
+}
+
+static __u16 capi_release(__u16 applid)
+{
+ struct sk_buff *skb;
+ int i;
+
+ if (!VALID_APPLID(applid) || APPL(applid)->releasing)
+ return CAPI_ILLAPPNR;
+ while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0)
+ kfree_skb(skb);
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (cards[i].cardstate != CARD_RUNNING)
+ continue;
+ APPL(applid)->releasing++;
+ cards[i].driver->release_appl(&cards[i], applid);
+ }
+ if (APPL(applid)->releasing <= 0) {
+ APPL(applid)->signal = 0;
+ APPL_MARK_FREE(applid);
+ printk(KERN_INFO "kcapi: appl %d down\n", applid);
+ }
+ return CAPI_NOERROR;
+}
+
+static __u16 capi_put_message(__u16 applid, struct sk_buff *skb)
+{
+ struct capi_ncci *np;
+ int contr;
+ int showctl = 0;
+ __u8 cmd, subcmd;
+
+ if (ncards == 0)
+ return CAPI_REGNOTINSTALLED;
+ if (!VALID_APPLID(applid))
+ return CAPI_ILLAPPNR;
+ if (skb->len < 12
+ || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data))
+ || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data)))
+ return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+ contr = CAPIMSG_CONTROLLER(skb->data);
+ if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) {
+ contr = 1;
+ if (CARD(contr)->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+ }
+ if (CARD(contr)->blocked)
+ return CAPI_SENDQUEUEFULL;
+
+ cmd = CAPIMSG_COMMAND(skb->data);
+ subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+ if (cmd == CAPI_DATA_B3 && subcmd== CAPI_REQ) {
+ if ((np = find_ncci(APPL(applid), CAPIMSG_NCCI(skb->data))) != 0
+ && mq_enqueue(np, CAPIMSG_MSGID(skb->data)) == 0)
+ return CAPI_SENDQUEUEFULL;
+ CARD(contr)->nsentdatapkt++;
+ APPL(applid)->nsentdatapkt++;
+ if (CARD(contr)->traceflag > 2) showctl |= 2;
+ } else {
+ CARD(contr)->nsentctlpkt++;
+ APPL(applid)->nsentctlpkt++;
+ if (CARD(contr)->traceflag) showctl |= 2;
+ }
+ showctl |= (CARD(contr)->traceflag & 1);
+ if (showctl & 2) {
+ if (showctl & 1) {
+ printk(KERN_DEBUG "kcapi: put [0x%lx] id#%d %s len=%u\n",
+ (unsigned long) contr,
+ CAPIMSG_APPID(skb->data),
+ capi_cmd2str(cmd, subcmd),
+ CAPIMSG_LEN(skb->data));
+ } else {
+ printk(KERN_DEBUG "kcapi: put [0x%lx] %s\n",
+ (unsigned long) contr,
+ capi_message2str(skb->data));
+ }
+
+ }
+ CARD(contr)->driver->send_message(CARD(contr), skb);
+ return CAPI_NOERROR;
+}
+
+static __u16 capi_get_message(__u16 applid, struct sk_buff **msgp)
+{
+ struct sk_buff *skb;
+
+ if (!VALID_APPLID(applid))
+ return CAPI_ILLAPPNR;
+ if ((skb = skb_dequeue(&APPL(applid)->recv_queue)) == 0)
+ return CAPI_RECEIVEQUEUEEMPTY;
+ *msgp = skb;
+ return CAPI_NOERROR;
+}
+
+static __u16 capi_set_signal(__u16 applid,
+ void (*signal) (__u16 applid, __u32 param),
+ __u32 param)
+{
+ if (!VALID_APPLID(applid))
+ return CAPI_ILLAPPNR;
+ APPL(applid)->signal = signal;
+ APPL(applid)->param = param;
+ return CAPI_NOERROR;
+}
+
+static __u16 capi_get_manufacturer(__u16 contr, __u8 buf[CAPI_MANUFACTURER_LEN])
+{
+ if (contr == 0) {
+ strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
+ return CAPI_NOERROR;
+ }
+ if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING)
+ return 0x2002;
+
+ strncpy(buf, CARD(contr)->manu, CAPI_MANUFACTURER_LEN);
+ return CAPI_NOERROR;
+}
+
+static __u16 capi_get_version(__u16 contr, struct capi_version *verp)
+{
+ if (contr == 0) {
+ *verp = driver_version;
+ return CAPI_NOERROR;
+ }
+ if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING)
+ return 0x2002;
+
+ memcpy((void *) verp, &CARD(contr)->version, sizeof(capi_version));
+ return CAPI_NOERROR;
+}
+
+static __u16 capi_get_serial(__u16 contr, __u8 serial[CAPI_SERIAL_LEN])
+{
+ if (contr == 0) {
+ strncpy(serial, driver_serial, CAPI_SERIAL_LEN);
+ return CAPI_NOERROR;
+ }
+ if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING)
+ return 0x2002;
+
+ strncpy((void *) serial, CARD(contr)->serial, CAPI_SERIAL_LEN);
+ return CAPI_NOERROR;
+}
+
+static __u16 capi_get_profile(__u16 contr, struct capi_profile *profp)
+{
+ if (contr == 0) {
+ profp->ncontroller = ncards;
+ return CAPI_NOERROR;
+ }
+ if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING)
+ return 0x2002;
+
+ memcpy((void *) profp, &CARD(contr)->profile,
+ sizeof(struct capi_profile));
+ return CAPI_NOERROR;
+}
+
+#ifdef CONFIG_AVMB1_COMPAT
+static int old_capi_manufacturer(unsigned int cmd, void *data)
+{
+ avmb1_loadandconfigdef ldef;
+ avmb1_extcarddef cdef;
+ avmb1_resetdef rdef;
+ avmb1_getdef gdef;
+ struct capi_driver *driver;
+ struct capi_ctr *card;
+ capicardparams cparams;
+ capiloaddata ldata;
+ int retval;
+
+ switch (cmd) {
+ case AVMB1_ADDCARD:
+ case AVMB1_ADDCARD_WITH_TYPE:
+ if (cmd == AVMB1_ADDCARD) {
+ if ((retval = copy_from_user((void *) &cdef, data,
+ sizeof(avmb1_carddef))))
+ return retval;
+ cdef.cardtype = AVM_CARDTYPE_B1;
+ } else {
+ if ((retval = copy_from_user((void *) &cdef, data,
+ sizeof(avmb1_extcarddef))))
+ return retval;
+ }
+ cparams.port = cdef.port;
+ cparams.irq = cdef.irq;
+ cparams.cardnr = cdef.cardnr;
+
+ switch (cdef.cardtype) {
+ case AVM_CARDTYPE_B1: driver = b1isa_driver; break;
+ case AVM_CARDTYPE_T1: driver = t1isa_driver; break;
+ default: driver = 0;
+ }
+ if (!driver || !driver->add_card) {
+ return -EIO;
+ }
+
+ return driver->add_card(driver, &cparams);
+
+ case AVMB1_LOAD:
+ case AVMB1_LOAD_AND_CONFIG:
+
+ if (cmd == AVMB1_LOAD) {
+ if ((retval = copy_from_user((void *) &ldef, data,
+ sizeof(avmb1_loaddef))))
+ return retval;
+ ldef.t4config.len = 0;
+ ldef.t4config.data = 0;
+ } else {
+ if ((retval = copy_from_user((void *) &ldef, data,
+ sizeof(avmb1_loadandconfigdef))))
+ return retval;
+ }
+ if (!VALID_CARD(ldef.contr))
+ return -ESRCH;
+
+ card = CARD(ldef.contr);
+ if (card->cardstate == CARD_FREE)
+ 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->driver->load_firmware(card, &ldata);
+
+ if (retval) {
+ card->cardstate = CARD_DETECTED;
+ return retval;
+ }
+
+ while (card->cardstate != CARD_RUNNING) {
+
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10); /* 0.1 sec */
+
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ return 0;
+
+ case AVMB1_RESETCARD:
+ if ((retval = copy_from_user((void *) &rdef, data,
+ sizeof(avmb1_resetdef))))
+ return retval;
+ if (!VALID_CARD(rdef.contr))
+ return -ESRCH;
+ card = CARD(rdef.contr);
+
+ if (card->cardstate == CARD_FREE)
+ return -ESRCH;
+ if (card->cardstate == CARD_DETECTED)
+ return 0;
+
+ card->driver->reset_ctr(card);
+
+ while (card->cardstate > CARD_DETECTED) {
+
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10); /* 0.1 sec */
+
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ return 0;
+
+ case AVMB1_GET_CARDINFO:
+ if ((retval = copy_from_user((void *) &gdef, data,
+ sizeof(avmb1_getdef))))
+ return retval;
+
+ if (!VALID_CARD(gdef.contr))
+ return -ESRCH;
+
+ card = CARD(gdef.contr);
+
+ if (card->cardstate == CARD_FREE)
+ return -ESRCH;
+
+ gdef.cardstate = card->cardstate;
+ if (card->driver == b1isa_driver)
+ gdef.cardtype = AVM_CARDTYPE_B1;
+ else if (card->driver == t1isa_driver)
+ gdef.cardtype = AVM_CARDTYPE_T1;
+ else gdef.cardtype = AVM_CARDTYPE_B1;
+
+ if ((retval = copy_to_user(data, (void *) &gdef,
+ sizeof(avmb1_getdef))))
+ return retval;
+
+ return 0;
+
+ case AVMB1_REMOVECARD:
+ if ((retval = copy_from_user((void *) &rdef, data,
+ sizeof(avmb1_resetdef))))
+ return retval;
+
+ if (!VALID_CARD(rdef.contr))
+ return -ESRCH;
+ card = CARD(rdef.contr);
+
+ if (card->cardstate == CARD_FREE)
+ return -ESRCH;
+
+ if (card->cardstate != CARD_DETECTED)
+ return -EBUSY;
+
+ card->driver->remove_ctr(card);
+
+ while (card->cardstate != CARD_FREE) {
+
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10); /* 0.1 sec */
+
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+#endif
+
+static int capi_manufacturer(unsigned int cmd, void *data)
+{
+ struct capi_ctr *card;
+ kcapi_flagdef fdef;
+ int retval;
+
+ switch (cmd) {
+#ifdef CONFIG_AVMB1_COMPAT
+ case AVMB1_ADDCARD:
+ case AVMB1_ADDCARD_WITH_TYPE:
+ 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:
+ if ((retval = copy_from_user((void *) &fdef, data,
+ sizeof(kcapi_flagdef))))
+ return retval;
+
+ if (!VALID_CARD(fdef.contr))
+ return -ESRCH;
+ card = CARD(fdef.contr);
+ if (card->cardstate == CARD_FREE)
+ return -ESRCH;
+ card->traceflag = fdef.flag;
+ printk(KERN_INFO "kcapi: contr %d set trace=%d\n",
+ card->cnr, card->traceflag);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+struct capi_interface avmb1_interface =
+{
+ capi_installed,
+ capi_register,
+ capi_release,
+ capi_put_message,
+ capi_get_message,
+ capi_set_signal,
+ capi_get_manufacturer,
+ capi_get_version,
+ capi_get_serial,
+ capi_get_profile,
+ capi_manufacturer
+};
+
+/* ------------------------------------------------------------- */
+/* -------- Exported Functions --------------------------------- */
+/* ------------------------------------------------------------- */
+
+struct capi_interface *attach_capi_interface(struct capi_interface_user *userp)
+{
+ struct capi_interface_user *p;
+
+ for (p = capi_users; p; p = p->next) {
+ if (p == userp) {
+ printk(KERN_ERR "kcapi: double attach from %s\n",
+ userp->name);
+ return 0;
+ }
+ }
+ userp->next = capi_users;
+ capi_users = userp;
+ MOD_INC_USE_COUNT;
+ printk(KERN_NOTICE "kcapi: %s attached\n", userp->name);
+
+ return &avmb1_interface;
+}
+
+int detach_capi_interface(struct capi_interface_user *userp)
+{
+ struct capi_interface_user **pp;
+
+ for (pp = &capi_users; *pp; pp = &(*pp)->next) {
+ if (*pp == userp) {
+ *pp = userp->next;
+ userp->next = 0;
+ MOD_DEC_USE_COUNT;
+ printk(KERN_NOTICE "kcapi: %s detached\n", userp->name);
+ return 0;
+ }
+ }
+ printk(KERN_ERR "kcapi: double detach from %s\n", userp->name);
+ return -1;
+}
+
+/* ------------------------------------------------------------- */
+/* -------- Init & Cleanup ------------------------------------- */
+/* ------------------------------------------------------------- */
+
+EXPORT_SYMBOL(attach_capi_interface);
+EXPORT_SYMBOL(detach_capi_interface);
+EXPORT_SYMBOL(attach_capi_driver);
+EXPORT_SYMBOL(detach_capi_driver);
+
+/*
+ * init / exit functions
+ */
+
+#ifdef MODULE
+#define kcapi_init init_module
+#endif
+
+int kcapi_init(void)
+{
+ char *p;
+ char rev[10];
+
+ skb_queue_head_init(&recv_queue);
+ /* init_bh(CAPI_BH, do_capi_bh); */
+
+ tq_state_notify.routine = notify_handler;
+ tq_state_notify.data = 0;
+
+ tq_recv_notify.routine = recv_handler;
+ tq_recv_notify.data = 0;
+
+ proc_capi_init();
+
+ if ((p = strchr(revision, ':'))) {
+ strcpy(rev, p + 1);
+ p = strchr(rev, '$');
+ *p = 0;
+ } else
+ strcpy(rev, "1.0");
+
+#ifdef MODULE
+ printk(KERN_NOTICE "CAPI-driver Rev%s: loaded\n", rev);
+#else
+ printk(KERN_NOTICE "CAPI-driver Rev%s: started\n", rev);
+#endif
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ char rev[10];
+ char *p;
+
+ if ((p = strchr(revision, ':'))) {
+ strcpy(rev, p + 1);
+ p = strchr(rev, '$');
+ *p = 0;
+ } else {
+ strcpy(rev, "1.0");
+ }
+
+ schedule(); /* execute queued tasks .... */
+ proc_capi_exit();
+ printk(KERN_NOTICE "CAPI-driver Rev%s: unloaded\n", rev);
+}
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)