diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-06-08 14:58:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-06-08 14:58:25 +0000 |
commit | f4ba6274ad71d670822593e031049b748691ec98 (patch) | |
tree | 463767d02e03d7a06c1e912c3c7d397d4ed9a2c2 /drivers | |
parent | 06cc037a64e3bad7b6125e82958b02b45a8f01d0 (diff) |
These files were missing in the 2.1.42 merge.
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/isdn/avmb1/.cvsignore | 1 | ||||
-rw-r--r-- | drivers/isdn/avmb1/Makefile | 77 | ||||
-rw-r--r-- | drivers/isdn/avmb1/b1capi.c | 946 | ||||
-rw-r--r-- | drivers/isdn/avmb1/b1lli.c | 594 | ||||
-rw-r--r-- | drivers/isdn/avmb1/b1pci.c | 121 | ||||
-rw-r--r-- | drivers/isdn/avmb1/capi.c | 540 | ||||
-rw-r--r-- | drivers/isdn/avmb1/capicmd.h | 119 | ||||
-rw-r--r-- | drivers/isdn/avmb1/capidev.h | 29 | ||||
-rw-r--r-- | drivers/isdn/avmb1/capidrv.c | 1764 | ||||
-rw-r--r-- | drivers/isdn/avmb1/capidrv.h | 111 | ||||
-rw-r--r-- | drivers/isdn/avmb1/capiutil.c | 974 | ||||
-rw-r--r-- | drivers/isdn/avmb1/capiutil.h | 501 | ||||
-rw-r--r-- | drivers/isdn/avmb1/compat.h | 30 |
13 files changed, 5807 insertions, 0 deletions
diff --git a/drivers/isdn/avmb1/.cvsignore b/drivers/isdn/avmb1/.cvsignore new file mode 100644 index 000000000..4671378ae --- /dev/null +++ b/drivers/isdn/avmb1/.cvsignore @@ -0,0 +1 @@ +.depend diff --git a/drivers/isdn/avmb1/Makefile b/drivers/isdn/avmb1/Makefile new file mode 100644 index 000000000..cce4af131 --- /dev/null +++ b/drivers/isdn/avmb1/Makefile @@ -0,0 +1,77 @@ +# +# $Id: Makefile,v 1.4 1997/03/30 17:10:40 calle Exp $ +# +# Makefile for the CAPI and AVM-B1 device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# +# $Log: Makefile,v $ +# Revision 1.4 1997/03/30 17:10:40 calle +# added support for AVM-B1-PCI card. +# +# Revision 1.3 1997/03/22 02:00:57 fritz +# -Reworked toplevel Makefile. From now on, no different Makefiles +# for standalone- and in-kernel-compilation are needed any more. +# -Added local Rules.make for above reason. +# -Experimental changes in teles3.c for enhanced IRQ-checking with +# 2.1.X and SMP kernels. +# -Removed diffstd-script, same functionality is in stddiff -r. +# -Enhanced scripts std2kern and stddiff. +# +# Revision 1.1 1997/03/05 21:26:14 fritz +# Renamed, according naming conventions in CVS tree. +# +# Revision 1.1 1997/03/04 21:50:26 calle +# Frirst version in isdn4linux +# +# Revision 2.2 1997/02/12 09:31:39 calle +# +# Revision 1.1 1997/01/31 10:32:20 calle +# Initial revision +# +# + +# +# Objects that don't export a symtab +# +L_OBJS := # used as component of an L_TARGET +O_OBJS := # used as component of an O_TARGET +M_OBJS := # used as module +# +# Objects that do export a symtab +# +LX_OBJS := # used as component of an L_TARGET +OX_OBJS := # used as component of an O_TARGET +MX_OBJS := # used as module +# +# Targets, created by linking others +# +O_TARGET := # used for .o targets (from O and OX objects) +L_TARGET := # used for .a targets (from L and LX objects) + +ifeq ($(CONFIG_ISDN_DRV_AVMB1),y) + O_TARGET += avmb1.o + O_OBJS += capi.o b1lli.o + OX_OBJS += capiutil.o b1capi.o capidrv.o + ifdef CONFIG_PCI + OX_OBJS += b1pci.o + endif +else + ifeq ($(CONFIG_ISDN_DRV_AVMB1),m) + O_TARGET += kernelcapi.o + O_OBJS += b1lli.o + OX_OBJS += b1capi.o + M_OBJS += capi.o kernelcapi.o + MX_OBJS += capiutil.o capidrv.o + ifdef CONFIG_PCI + MX_OBJS += b1pci.o + endif + endif +endif + +include $(TOPDIR)/Rules.make diff --git a/drivers/isdn/avmb1/b1capi.c b/drivers/isdn/avmb1/b1capi.c new file mode 100644 index 000000000..b379d8f7d --- /dev/null +++ b/drivers/isdn/avmb1/b1capi.c @@ -0,0 +1,946 @@ +/* + * $Id: b1capi.c,v 1.4 1997/05/27 15:17:45 fritz Exp $ + * + * CAPI 2.0 Module for AVM B1-card. + * + * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1capi.c,v $ + * Revision 1.4 1997/05/27 15:17:45 fritz + * Added changes for recent 2.1.x kernels: + * changed return type of isdn_close + * queue_task_* -> queue_task + * clear/set_bit -> test_and_... where apropriate. + * changed type of hard_header_cache parameter. + * + * Revision 1.3 1997/05/18 09:24:09 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.2 1997/03/05 21:20:41 fritz + * Removed include of config.h (mkdep stated this is unneded). + * + * Revision 1.1 1997/03/04 21:50:27 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + * + */ + +#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/skbuff.h> +#include <linux/tqueue.h> +#include <linux/capi.h> +#include <linux/b1lli.h> +#include <linux/kernelcapi.h> +#include "compat.h" +#include "capicmd.h" +#include "capiutil.h" + +static char *revision = "$Revision: 1.4 $"; + +/* ------------------------------------------------------------- */ + +int portbase = 0x150; +int irq = 15; +int showcapimsgs = 0; /* used in lli.c */ +int loaddebug = 0; + +#ifdef HAS_NEW_SYMTAB +MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); +MODULE_PARM(portbase, "i"); +MODULE_PARM(irq, "2-15i"); +MODULE_PARM(showcapimsgs, "0-3i"); +MODULE_PARM(loaddebug, "0-1i"); +#endif + +/* ------------------------------------------------------------- */ + +struct msgidqueue { + struct msgidqueue *next; + __u16 msgid; +}; + +typedef struct avmb1_ncci { + struct avmb1_ncci *next; + __u16 applid; + __u32 ncci; + __u32 winsize; + struct msgidqueue *msgidqueue; + struct msgidqueue *msgidlast; + struct msgidqueue *msgidfree; + struct msgidqueue msgidpool[CAPI_MAXDATAWINDOW]; +} avmb1_ncci; + +typedef struct avmb1_appl { + __u16 applid; + capi_register_params rparam; + int releasing; + __u32 param; + void (*signal) (__u16 applid, __u32 param); + struct sk_buff_head recv_queue; + struct avmb1_ncci *nccilist; +} avmb1_appl; + +/* ------------------------------------------------------------- */ + +static struct capi_version driver_version = {2, 0, 0, 9}; +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) <= ncards) +#define CARD(c) (&cards[(c)-1]) +#define CARDNR(cp) ((cards-(cp))+1) + +static avmb1_appl applications[CAPI_MAXAPPL]; +static avmb1_card cards[CAPI_MAXCONTR]; +static int ncards = 0; +static struct sk_buff_head recv_queue; +static struct capi_interface_user *capi_users = 0; +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 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; +} + +/* -------- NCCI Handling ------------------------------------- */ + +static inline void mq_init(avmb1_ncci * np) +{ + int i; + np->msgidqueue = 0; + np->msgidlast = 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(avmb1_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; + return 1; +} + +static inline int mq_dequeue(avmb1_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; + return 1; + } + } + return 0; +} + +void avmb1_handle_new_ncci(avmb1_card * card, + __u16 appl, __u32 ncci, __u32 winsize) +{ + avmb1_ncci *np; + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "avmb1_handle_new_ncci: illegal appl %d\n", appl); + return; + } + if ((np = (avmb1_ncci *) kmalloc(sizeof(avmb1_ncci), GFP_ATOMIC)) == 0) { + printk(KERN_ERR "avmb1_handle_new_ncci: alloc failed ncci 0x%x\n", ncci); + return; + } + if (winsize > CAPI_MAXDATAWINDOW) { + printk(KERN_ERR "avmb1_handle_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; + printk(KERN_INFO "b1capi: appl %d ncci 0x%x up\n", appl, ncci); + +} + +void avmb1_handle_free_ncci(avmb1_card * card, + __u16 appl, __u32 ncci) +{ + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "avmb1_handle_free_ncci: illegal appl %d\n", appl); + return; + } + if (ncci != 0xffffffff) { + avmb1_ncci **pp; + for (pp = &APPL(appl)->nccilist; *pp; pp = &(*pp)->next) { + if ((*pp)->ncci == ncci) { + avmb1_ncci *np = *pp; + *pp = np->next; + kfree(np); + printk(KERN_INFO "b1capi: appl %d ncci 0x%x down\n", appl, ncci); + return; + } + } + printk(KERN_ERR "avmb1_handle_free_ncci: ncci 0x%x not found\n", ncci); + } else { + avmb1_ncci **pp, **nextpp; + for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) { + if (NCCI2CTRL((*pp)->ncci) == card->cnr) { + avmb1_ncci *np = *pp; + *pp = np->next; + printk(KERN_INFO "b1capi: appl %d ncci 0x%x down!\n", appl, np->ncci); + kfree(np); + 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 "b1capi: appl %d down\n", appl); + } + } +} + +static avmb1_ncci *find_ncci(avmb1_appl * app, __u32 ncci) +{ + avmb1_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 avmb1_ncci *np; + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "b1capi: recv_handler: applid %d ? (%s)\n", + appl, capi_message2str(skb->data)); + kfree_skb(skb, FREE_READ); + continue; + } + if (APPL(appl)->signal == 0) { + printk(KERN_ERR "b1capi: recv_handler: applid %d has no signal function\n", + appl); + kfree_skb(skb, FREE_READ); + 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 "b1capi: msgid %hu ncci 0x%x not on queue\n", + CAPIMSG_MSGID(skb->data), np->ncci); + } + skb_queue_tail(&APPL(appl)->recv_queue, skb); + (APPL(appl)->signal) (APPL(appl)->applid, APPL(appl)->param); + } +} + + +void avmb1_handle_capimsg(avmb1_card * card, __u16 appl, struct sk_buff *skb) +{ + if (card->cardstate != CARD_RUNNING) { + printk(KERN_INFO "b1capi: controller %d not active, got: %s", + card->cnr, capi_message2str(skb->data)); + goto error; + return; + } + skb_queue_tail(&recv_queue, skb); + queue_task(&tq_recv_notify, &tq_immediate); + mark_bh(IMMEDIATE_BH); + return; + + error: + kfree_skb(skb, FREE_READ); +} + +void avmb1_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmb1_card *card; + + card = (avmb1_card *) devptr; + + if (!card) { + printk(KERN_WARNING "avmb1_interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "avmb1_interrupt: reentering interrupt hander\n"); + return; + } + + card->interrupt = 1; + + B1_handle_interrupt(card); + + card->interrupt = 0; +} + +/* -------- Notifier ------------------------------------------ */ + +static void notify_up(__u16 contr) +{ + struct capi_interface_user *p; + + for (p = capi_users; p; p = p->next) { + if (p->callback) + (*p->callback) (KCI_CONTRUP, contr, + (capi_profile *) + CARD(contr)->version[VER_PROFILE]); + } +} + +static void notify_down(__u16 contr) +{ + struct capi_interface_user *p; + for (p = capi_users; p; p = p->next) { + if (p->callback) + (*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); +} + +/* -------- card ready callback ------------------------------- */ + +void avmb1_card_ready(avmb1_card * card) +{ + __u16 appl; + + card->cversion.majorversion = 2; + card->cversion.minorversion = 0; + card->cversion.majormanuversion = (card->version[VER_DRIVER][0] - '0') << 4; + card->cversion.majormanuversion |= (card->version[VER_DRIVER][2] - '0'); + card->cversion.minormanuversion = (card->version[VER_DRIVER][3] - '0') << 4; + card->cversion.minormanuversion |= (card->version[VER_DRIVER][5] - '0') * 10; + card->cversion.minormanuversion |= (card->version[VER_DRIVER][6] - '0'); + card->cardstate = CARD_RUNNING; + + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + if (VALID_APPLID(appl) && !APPL(appl)->releasing) { + B1_send_register(card->port, appl, + 1024 * (APPL(appl)->rparam.level3cnt+1), + APPL(appl)->rparam.level3cnt, + APPL(appl)->rparam.datablkcnt, + APPL(appl)->rparam.datablklen); + } + } + + set_bit(CARDNR(card), ¬ify_up_set); + queue_task(&tq_state_notify, &tq_scheduler); +} + +/* ------------------------------------------------------------- */ + +int avmb1_addcard(int port, int irq) +{ + struct avmb1_card *card; + int irqval; + + + card = &cards[ncards]; + memset(card, 0, sizeof(avmb1_card)); + sprintf(card->name, "avmb1-%d", ncards + 1); + + request_region(port, AVMB1_PORTLEN, card->name); + + if ((irqval = request_irq(irq, avmb1_interrupt, SA_SHIRQ, card->name, card)) != 0) { + printk(KERN_ERR "b1capi: unable to get IRQ %d (irqval=%d).\n", + irq, irqval); + release_region((unsigned short) port, AVMB1_PORTLEN); + return -EIO; + } + ncards++; + card->cnr = ncards; + card->port = port; + card->irq = irq; + card->cardstate = CARD_DETECTED; + return 0; +} + +int avmb1_probecard(int port, int irq) +{ + int rc; + + if (check_region((unsigned short) port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "b1capi: ports 0x%03x-0x%03x in use.\n", + portbase, portbase + AVMB1_PORTLEN); + return -EIO; + } + if (!B1_valid_irq(irq)) { + printk(KERN_WARNING "b1capi: irq %d not valid.\n", irq); + return -EIO; + } + if ((rc = B1_detect(port)) != 0) { + printk(KERN_NOTICE "b1capi: NO card at 0x%x (%d)\n", port, rc); + return -EIO; + } + B1_reset(port); + printk(KERN_NOTICE "b1capi: AVM-B1-Controller detected at 0x%x\n", port); + + return 0; +} + +/* ------------------------------------------------------------- */ +/* -------- CAPI2.0 Interface ---------------------------------- */ +/* ------------------------------------------------------------- */ + +static int capi_installed(void) +{ + int i; + for (i = 0; i < ncards; i++) { + if (cards[i].cardstate == CARD_RUNNING) + return 1; + } + return 0; +} + +static __u16 capi_register(capi_register_params * rparam, __u16 * applidp) +{ + int i; + int appl; + + 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); + + memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params)); + + for (i = 0; i < ncards; i++) { + if (cards[i].cardstate != CARD_RUNNING) + continue; + B1_send_register(cards[i].port, appl, + 1024 * (APPL(appl)->rparam.level3cnt + 1), + APPL(appl)->rparam.level3cnt, + APPL(appl)->rparam.datablkcnt, + APPL(appl)->rparam.datablklen); + } + *applidp = appl; + printk(KERN_INFO "b1capi: appl %d up\n", appl); + + return CAPI_NOERROR; +} + +static __u16 capi_release(__u16 applid) +{ + struct sk_buff *skb; + int i; + + if (ncards == 0) + return CAPI_REGNOTINSTALLED; + if (!VALID_APPLID(applid) || APPL(applid)->releasing) + return CAPI_ILLAPPNR; + while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0) + kfree_skb(skb, FREE_READ); + for (i = 0; i < ncards; i++) { + if (cards[i].cardstate != CARD_RUNNING) + continue; + APPL(applid)->releasing++; + B1_send_release(cards[i].port, applid); + } + if (APPL(applid)->releasing == 0) { + APPL(applid)->signal = 0; + APPL_MARK_FREE(applid); + printk(KERN_INFO "b1capi: appl %d down\n", applid); + } + return CAPI_NOERROR; +} + +static __u16 capi_put_message(__u16 applid, struct sk_buff *skb) +{ + avmb1_ncci *np; + int contr; + 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; + + if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_REQ + && (np = find_ncci(APPL(applid), CAPIMSG_NCCI(skb->data))) != 0 + && mq_enqueue(np, CAPIMSG_MSGID(skb->data)) == 0) + return CAPI_SENDQUEUEFULL; + + B1_send_message(CARD(contr)->port, 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, capi_manufakturer, 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[VER_SERIAL], + 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, 8); + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + memcpy((void *) serial, CARD(contr)->version[VER_SERIAL], + CAPI_SERIAL_LEN); + serial[CAPI_SERIAL_LEN - 1] = 0; + 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)->version[VER_PROFILE], + sizeof(struct capi_profile)); + return CAPI_NOERROR; +} + +static int capi_manufacturer(unsigned int cmd, void *data) +{ + unsigned long flags; + avmb1_loaddef ldef; + avmb1_carddef cdef; + avmb1_resetdef rdef; + avmb1_card *card; + int rc; + + switch (cmd) { + case AVMB1_ADDCARD: + if ((rc = copy_from_user((void *) &cdef, data, + sizeof(avmb1_carddef)))) + return rc; + if (!B1_valid_irq(cdef.irq)) + return -EINVAL; + + if ((rc = avmb1_probecard(cdef.port, cdef.irq)) != 0) + return rc; + + return avmb1_addcard(cdef.port, cdef.irq); + + case AVMB1_LOAD: + + if ((rc = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loaddef)))) + return rc; + if (!VALID_CARD(ldef.contr) || ldef.t4file.len <= 0) { + if (loaddebug) + printk(KERN_DEBUG "b1capi: load: invalid parameter contr=%d len=%d\n", ldef.contr, ldef.t4file.len); + return -EINVAL; + } + + card = CARD(ldef.contr); + save_flags(flags); + cli(); + if (card->cardstate != CARD_DETECTED) { + restore_flags(flags); + if (loaddebug) + printk(KERN_DEBUG "b1capi: load: contr=%d not in detect state\n", ldef.contr); + return -EBUSY; + } + card->cardstate = CARD_LOADING; + restore_flags(flags); + + if (loaddebug) { + printk(KERN_DEBUG "b1capi: load: reseting contr %d\n", + ldef.contr); + } + + B1_reset(card->port); + if ((rc = B1_load_t4file(card->port, &ldef.t4file))) { + B1_reset(card->port); + printk(KERN_ERR "b1capi: failed to load t4file!!\n"); + card->cardstate = CARD_DETECTED; + return rc; + } + B1_disable_irq(card->port); + if (loaddebug) { + printk(KERN_DEBUG "b1capi: load: ready contr %d: checking\n", + ldef.contr); + } + + if (!B1_loaded(card->port)) { + card->cardstate = CARD_DETECTED; + printk(KERN_ERR "b1capi: failed to load t4file.\n"); + return -EIO; + } + /* + * enable interrupt + */ + + card->cardstate = CARD_INITSTATE; + save_flags(flags); + cli(); + B1_assign_irq(card->port, card->irq); + B1_enable_irq(card->port); + restore_flags(flags); + + if (loaddebug) { + printk(KERN_DEBUG "b1capi: load: irq enabled contr %d\n", + ldef.contr); + } + + /* + * init card + */ + B1_send_init(card->port, AVM_NAPPS, AVM_NNCCI, card->cnr - 1); + + if (loaddebug) { + printk(KERN_DEBUG "b1capi: load: waiting for init reply contr %d\n", + ldef.contr); + } + + while (card->cardstate != CARD_RUNNING) { + + current->timeout = jiffies + HZ / 10; /* 0.1 sec */ + current->state = TASK_INTERRUPTIBLE; + schedule(); + + if (current->signal & ~current->blocked) + return -EINTR; + } + return 0; + case AVMB1_RESETCARD: + if ((rc = copy_from_user((void *) &rdef, data, + sizeof(avmb1_resetdef)))) + return rc; + + if (!VALID_CARD(rdef.contr)) + return -EINVAL; + + card = CARD(rdef.contr); + + if (card->cardstate == CARD_RUNNING) + return -EBUSY; + + B1_reset(card->port); + B1_reset(card->port); + + card->cardstate = CARD_DETECTED; + 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 "b1capi: double attach from %s\n", + userp->name); + return 0; + } + } + userp->next = capi_users; + capi_users = userp; + MOD_INC_USE_COUNT; + + 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; + return 0; + } + } + printk(KERN_ERR "b1capi: double detach from %s\n", userp->name); + return -1; +} + +/* ------------------------------------------------------------- */ +/* -------- Init & Cleanup ------------------------------------- */ +/* ------------------------------------------------------------- */ + +#ifdef HAS_NEW_SYMTAB +EXPORT_SYMBOL(attach_capi_interface); +EXPORT_SYMBOL(detach_capi_interface); +EXPORT_SYMBOL(avmb1_addcard); +EXPORT_SYMBOL(avmb1_probecard); +#else +static struct symbol_table capidev_syms = +{ +#include <linux/symtab_begin.h> + X(attach_capi_interface), + X(detach_capi_interface), + X(avmb1_addcard), + X(avmb1_probecard), +#include <linux/symtab_end.h> +}; +#endif + + +/* + * init / exit functions + */ + +#ifdef MODULE +#define avmb1_init init_module +#endif + +int avmb1_init(void) +{ + char *p; + char rev[10]; + + +#ifndef HAS_NEW_SYMTAB + /* No symbols to export, hide all symbols */ + register_symtab(&capidev_syms); +#endif + 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; + + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, " ??? "); + +#ifdef MODULE + if (portbase) { + int rc; + if ((rc = avmb1_probecard(portbase, irq)) != 0) + return rc; + if ((rc = avmb1_addcard(portbase, irq)) != 0) + return rc; + } else { + printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev); + } +#else + printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: started\n", rev); +#endif + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + char rev[10]; + char *p; + int i; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else { + strcpy(rev, " ??? "); + } + + for (i = 0; i < ncards; i++) { + /* + * disable card + */ + B1_disable_irq(cards[i].port); + B1_reset(cards[i].port); + B1_reset(cards[i].port); + /* + * free kernel resources + */ + free_irq(cards[i].irq, &cards[i]); + release_region(cards[i].port, AVMB1_PORTLEN); + + } + printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: unloaded\n", rev); +} +#endif diff --git a/drivers/isdn/avmb1/b1lli.c b/drivers/isdn/avmb1/b1lli.c new file mode 100644 index 000000000..bc1cb1cd3 --- /dev/null +++ b/drivers/isdn/avmb1/b1lli.c @@ -0,0 +1,594 @@ +/* + * $Id: b1lli.c,v 1.1 1997/03/04 21:50:28 calle Exp $ + * + * ISDN lowlevel-module for AVM B1-card. + * + * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1lli.c,v $ + * Revision 1.1 1997/03/04 21:50:28 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + * + */ + +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <asm/segment.h> +#include <asm/io.h> +#include <linux/capi.h> +#include <linux/b1lli.h> + +#include "compat.h" +#include "capicmd.h" +#include "capiutil.h" + +/* + * LLI Messages to the ISDN-ControllerISDN Controller + */ + +#define SEND_POLL 0x72 /* + * after load <- RECEIVE_POLL + */ +#define SEND_INIT 0x11 /* + * first message <- RECEIVE_INIT + * int32 NumApplications int32 + * NumNCCIs int32 BoardNumber + */ +#define SEND_REGISTER 0x12 /* + * register an application int32 + * ApplIDId int32 NumMessages + * int32 NumB3Connections int32 + * NumB3Blocks int32 B3Size + * + * AnzB3Connection != 0 && + * AnzB3Blocks >= 1 && B3Size >= 1 + */ +#define SEND_RELEASE 0x14 /* + * deregister an application int32 + * ApplID + */ +#define SEND_MESSAGE 0x15 /* + * send capi-message int32 length + * capi-data ... + */ +#define SEND_DATA_B3_REQ 0x13 /* + * send capi-data-message int32 + * MsgLength capi-data ... int32 + * B3Length data .... + */ + +/* + * LLI Messages from the ISDN-ControllerISDN Controller + */ + +#define RECEIVE_POLL 0x32 /* + * <- after SEND_POLL + */ +#define RECEIVE_INIT 0x27 /* + * <- after SEND_INIT int32 length + * byte total length b1struct board + * driver revision b1struct card + * type b1struct reserved b1struct + * serial number b1struct driver + * capability b1struct d-channel + * protocol b1struct CAPI-2.0 + * profile b1struct capi version + */ +#define RECEIVE_MESSAGE 0x21 /* + * <- after SEND_MESSAGE int32 + * AppllID int32 Length capi-data + * .... + */ +#define RECEIVE_DATA_B3_IND 0x22 /* + * received data int32 AppllID + * int32 Length capi-data ... + * int32 B3Length data ... + */ +#define RECEIVE_START 0x23 /* + * Handshake + */ +#define RECEIVE_STOP 0x24 /* + * Handshake + */ +#define RECEIVE_NEW_NCCI 0x25 /* + * int32 AppllID int32 NCCI int32 + * WindowSize + */ +#define RECEIVE_FREE_NCCI 0x26 /* + * int32 AppllID int32 NCCI + */ +#define RECEIVE_RELEASE 0x26 /* + * int32 AppllID int32 0xffffffff + */ + +/* + * port offsets + */ + +#define B1_READ 0x00 +#define B1_WRITE 0x01 +#define B1_INSTAT 0x02 +#define B1_OUTSTAT 0x03 +#define B1_RESET 0x10 +#define B1_ANALYSE 0x04 + + + +static inline unsigned char b1outp(unsigned short base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); + return inb(base + B1_ANALYSE); +} + +static int irq_table[16] = +{0, + 0, + 0, + 192, /* irq 3 */ + 32, /* irq 4 */ + 160, /* irq 5 */ + 96, /* irq 6 */ + 224, /* irq 7 */ + 0, + 64, /* irq 9 */ + 80, /* irq 10 */ + 208, /* irq 11 */ + 48, /* irq 12 */ + 0, + 0, + 112, /* irq 15 */ +}; + +int B1_valid_irq(unsigned irq) +{ + return irq_table[irq] != 0; +} + +unsigned char B1_assign_irq(unsigned short base, unsigned irq) +{ + return b1outp(base, B1_RESET, irq_table[irq]); +} + +unsigned char B1_enable_irq(unsigned short base) +{ + return b1outp(base, B1_INSTAT, 0x02); +} + +unsigned char B1_disable_irq(unsigned short base) +{ + return b1outp(base, B1_INSTAT, 0x00); +} + +void B1_reset(unsigned short base) +{ + b1outp(base, B1_RESET, 0); + udelay(55 * 2 * 1000); /* 2 TIC's */ + + b1outp(base, B1_RESET, 1); + udelay(55 * 2 * 1000); /* 2 TIC's */ + + b1outp(base, B1_RESET, 0); + udelay(55 * 2 * 1000); /* 2 TIC's */ +} + +int B1_detect(unsigned short base) +{ + /* + * Statusregister 0000 00xx + */ + if ((inb(base + B1_INSTAT) & 0xfc) + || (inb(base + B1_OUTSTAT) & 0xfc)) + return 1; + + /* + * Statusregister 0000 001x + */ + b1outp(base, B1_INSTAT, 0x2); /* enable irq */ + b1outp(base, B1_OUTSTAT, 0x2); + if ((inb(base + B1_INSTAT) & 0xfe) != 0x2 + || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2) + return 2; + + /* + * Statusregister 0000 000x + */ + b1outp(base, B1_INSTAT, 0x0); /* disable irq */ + b1outp(base, B1_OUTSTAT, 0x0); + if ((inb(base + B1_INSTAT) & 0xfe) + || (inb(base + B1_OUTSTAT) & 0xfe)) + return 3; + + return 0; +} + +static inline int B1_rx_full(unsigned short base) +{ + return inb(base + B1_INSTAT) & 0x1; +} + +static inline unsigned char B1_get_byte(unsigned short base) +{ + unsigned long i = jiffies + 5 * HZ; /* maximum wait time 5 sec */ + while (!B1_rx_full(base) && i > jiffies); + if (B1_rx_full(base)) + return inb(base + B1_READ); + printk(KERN_CRIT "b1lli: rx not full after 5 second\n"); + return 0; +} + +static inline unsigned int B1_get_word(unsigned short base) +{ + unsigned int val = 0; + val |= B1_get_byte(base); + val |= (B1_get_byte(base) << 8); + val |= (B1_get_byte(base) << 16); + val |= (B1_get_byte(base) << 24); + return val; +} + +static inline int B1_tx_empty(unsigned short base) +{ + return inb(base + B1_OUTSTAT) & 0x1; +} + +static inline void B1_put_byte(unsigned short base, unsigned char val) +{ + while (!B1_tx_empty(base)); + b1outp(base, B1_WRITE, val); +} + +static inline void B1_put_word(unsigned short base, unsigned int val) +{ + B1_put_byte(base, val & 0xff); + B1_put_byte(base, (val >> 8) & 0xff); + B1_put_byte(base, (val >> 16) & 0xff); + B1_put_byte(base, (val >> 24) & 0xff); +} + +static inline unsigned int B1_get_slice(unsigned short base, + unsigned char *dp) +{ + unsigned int len, i; + + len = i = B1_get_word(base); + while (i-- > 0) + *dp++ = B1_get_byte(base); + return len; +} + +static inline void B1_put_slice(unsigned short base, + unsigned char *dp, unsigned int len) +{ + B1_put_word(base, len); + while (len-- > 0) + B1_put_byte(base, *dp++); +} + +extern int loaddebug; + +int B1_load_t4file(unsigned short base, avmb1_t4file * t4file) +{ + /* + * Data is in user space !!! + */ + unsigned char buf[256]; + unsigned char *dp; + int i, left, retval; + + + dp = t4file->data; + left = t4file->len; + while (left > sizeof(buf)) { + retval = copy_from_user(buf, dp, sizeof(buf)); + if (retval) + return -EFAULT; + if (loaddebug) + printk(KERN_DEBUG "b1capi: loading: %d bytes ..", sizeof(buf)); + for (i = 0; i < sizeof(buf); i++) + B1_put_byte(base, buf[i]); + if (loaddebug) + printk("ok\n"); + left -= sizeof(buf); + dp += sizeof(buf); + } + if (left) { + retval = copy_from_user(buf, dp, left); + if (retval) + return -EFAULT; + if (loaddebug) + printk(KERN_DEBUG "b1capi: loading: %d bytes ..", left); + for (i = 0; i < left; i++) + B1_put_byte(base, buf[i]); + if (loaddebug) + printk("ok\n"); + } + return 0; +} + +int B1_loaded(unsigned short base) +{ + int i; + unsigned char ans; + + if (loaddebug) + printk(KERN_DEBUG "b1capi: loaded: wait 1 ..\n"); + for (i = jiffies + 10 * HZ; i > jiffies;) { + if (B1_tx_empty(base)) + break; + } + if (!B1_tx_empty(base)) { + printk(KERN_ERR "b1lli: B1_loaded: timeout tx\n"); + return 0; + } + B1_put_byte(base, SEND_POLL); + printk(KERN_DEBUG "b1capi: loaded: wait 2 ..\n"); + for (i = jiffies + 10 * HZ; i > jiffies;) { + if (B1_rx_full(base)) { + if ((ans = B1_get_byte(base)) == RECEIVE_POLL) { + if (loaddebug) + printk(KERN_DEBUG "b1capi: loaded: ok\n"); + return 1; + } + printk(KERN_ERR "b1lli: B1_loaded: got 0x%x ???\n", ans); + return 0; + } + } + printk(KERN_ERR "b1lli: B1_loaded: timeout rx\n"); + return 0; +} + +/* + * ------------------------------------------------------------------- + */ +static inline void parse_version(avmb1_card * card) +{ + int i, j; + for (j = 0; j < AVM_MAXVERSION; j++) + card->version[j] = "\0\0" + 1; + for (i = 0, j = 0; + j < AVM_MAXVERSION && i < card->versionlen; + j++, i += card->versionbuf[i] + 1) + card->version[j] = &card->versionbuf[i + 1]; +} +/* + * ------------------------------------------------------------------- + */ + +void B1_send_init(unsigned short port, + unsigned int napps, unsigned int nncci, unsigned int cardnr) +{ + unsigned long flags; + + save_flags(flags); + cli(); + B1_put_byte(port, SEND_INIT); + B1_put_word(port, napps); + B1_put_word(port, nncci); + B1_put_word(port, cardnr); + restore_flags(flags); +} + +void B1_send_register(unsigned short port, + __u16 appid, __u32 nmsg, + __u32 nb3conn, __u32 nb3blocks, __u32 b3bsize) +{ + unsigned long flags; + + save_flags(flags); + cli(); + B1_put_byte(port, SEND_REGISTER); + B1_put_word(port, appid); + B1_put_word(port, nmsg); + B1_put_word(port, nb3conn); + B1_put_word(port, nb3blocks); + B1_put_word(port, b3bsize); + restore_flags(flags); +} + +void B1_send_release(unsigned short port, + __u16 appid) +{ + unsigned long flags; + + save_flags(flags); + cli(); + B1_put_byte(port, SEND_RELEASE); + B1_put_word(port, appid); + restore_flags(flags); +} + +extern int showcapimsgs; + +void B1_send_message(unsigned short port, struct sk_buff *skb) +{ + unsigned long flags; + __u16 len = CAPIMSG_LEN(skb->data); + __u8 cmd = CAPIMSG_COMMAND(skb->data); + __u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); + __u32 contr = CAPIMSG_CONTROL(skb->data); + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + + if (showcapimsgs > 2) { + if (showcapimsgs & 1) { + printk(KERN_DEBUG "b1lli: Put [0x%lx] id#%d %s len=%u\n", + (unsigned long) contr, + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), len); + } else { + printk(KERN_DEBUG "b1lli: Put %s\n", capi_message2str(skb->data)); + } + + } + save_flags(flags); + cli(); + B1_put_byte(port, SEND_DATA_B3_REQ); + B1_put_slice(port, skb->data, len); + B1_put_slice(port, skb->data + len, dlen); + restore_flags(flags); + } else { + if (showcapimsgs) { + + if (showcapimsgs & 1) { + printk(KERN_DEBUG "b1lli: Put [0x%lx] id#%d %s len=%u\n", + (unsigned long) contr, + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), len); + } else { + printk(KERN_DEBUG "b1lli: Put %s\n", capi_message2str(skb->data)); + } + } + save_flags(flags); + cli(); + B1_put_byte(port, SEND_MESSAGE); + B1_put_slice(port, skb->data, len); + restore_flags(flags); + } + dev_kfree_skb(skb, FREE_WRITE); +} + +/* + * ------------------------------------------------------------------- + */ + +void B1_handle_interrupt(avmb1_card * card) +{ + unsigned char b1cmd; + struct sk_buff *skb; + + unsigned ApplId; + unsigned MsgLen; + unsigned DataB3Len; + unsigned NCCI; + unsigned WindowSize; + + if (!B1_rx_full(card->port)) + return; + + b1cmd = B1_get_byte(card->port); + + switch (b1cmd) { + + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) B1_get_word(card->port); + MsgLen = B1_get_slice(card->port, card->msgbuf); + DataB3Len = B1_get_slice(card->port, card->databuf); + + if (showcapimsgs > 2) { + __u8 cmd = CAPIMSG_COMMAND(card->msgbuf); + __u8 subcmd = CAPIMSG_SUBCOMMAND(card->msgbuf); + __u32 contr = CAPIMSG_CONTROL(card->msgbuf); + CAPIMSG_SETDATA(card->msgbuf, card->databuf); + if (showcapimsgs & 1) { + printk(KERN_DEBUG "b1lli: Got [0x%lx] id#%d %s len=%u/%u\n", + (unsigned long) contr, + CAPIMSG_APPID(card->msgbuf), + capi_cmd2str(cmd, subcmd), + MsgLen, DataB3Len); + } else { + printk(KERN_DEBUG "b1lli: Got %s\n", capi_message2str(card->msgbuf)); + } + } + if (!(skb = dev_alloc_skb(DataB3Len + MsgLen))) { + printk(KERN_ERR "b1lli: incoming packet dropped\n"); + } else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + CAPIMSG_SETDATA(skb->data, skb->data + MsgLen); + avmb1_handle_capimsg(card, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) B1_get_word(card->port); + MsgLen = B1_get_slice(card->port, card->msgbuf); + if (showcapimsgs) { + __u8 cmd = CAPIMSG_COMMAND(card->msgbuf); + __u8 subcmd = CAPIMSG_SUBCOMMAND(card->msgbuf); + __u32 contr = CAPIMSG_CONTROL(card->msgbuf); + if (showcapimsgs & 1) { + printk(KERN_DEBUG "b1lli: Got [0x%lx] id#%d %s len=%u\n", + (unsigned long) contr, + CAPIMSG_APPID(card->msgbuf), + capi_cmd2str(cmd, subcmd), + MsgLen); + } else { + printk(KERN_DEBUG "b1lli: Got %s\n", capi_message2str(card->msgbuf)); + } + + } + if (!(skb = dev_alloc_skb(MsgLen))) { + printk(KERN_ERR "b1lli: incoming packet dropped\n"); + } else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + avmb1_handle_capimsg(card, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = B1_get_word(card->port); + NCCI = B1_get_word(card->port); + WindowSize = B1_get_word(card->port); + + if (showcapimsgs) + printk(KERN_DEBUG "b1lli: NEW_NCCI app %u ncci 0x%x\n", ApplId, NCCI); + + avmb1_handle_new_ncci(card, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = B1_get_word(card->port); + NCCI = B1_get_word(card->port); + + if (showcapimsgs) + printk(KERN_DEBUG "b1lli: FREE_NCCI app %u ncci 0x%x\n", ApplId, NCCI); + + avmb1_handle_free_ncci(card, ApplId, NCCI); + break; + + case RECEIVE_START: + if (card->blocked) + printk(KERN_DEBUG "b1lli: RESTART\n"); + card->blocked = 0; + break; + + case RECEIVE_STOP: + printk(KERN_DEBUG "b1lli: STOP\n"); + card->blocked = 1; + break; + + case RECEIVE_INIT: + + card->versionlen = B1_get_slice(card->port, card->versionbuf); + card->cardstate = CARD_ACTIVE; + parse_version(card); + printk(KERN_INFO "b1lli: %s-card (%s) with %s now active\n", + card->version[VER_CARDTYPE], + card->version[VER_DRIVER], + card->version[VER_PROTO]); + avmb1_card_ready(card); + break; + default: + printk(KERN_ERR "b1lli: B1_handle_interrupt: 0x%x ???\n", b1cmd); + break; + } +} diff --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c new file mode 100644 index 000000000..af7b58407 --- /dev/null +++ b/drivers/isdn/avmb1/b1pci.c @@ -0,0 +1,121 @@ +/* + * $Id: b1pci.c,v 1.2 1997/05/18 09:24:13 calle Exp $ + * + * Module for AVM B1 PCI-card. + * + * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1pci.c,v $ + * Revision 1.2 1997/05/18 09:24:13 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.1 1997/03/30 17:10:42 calle + * added support for AVM-B1-PCI card. + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/bios32.h> +#include <linux/pci.h> +#include <linux/skbuff.h> +#include "compat.h" +#include <linux/capi.h> +#include <linux/b1lli.h> + +#ifndef PCI_VENDOR_ID_AVM +#define PCI_VENDOR_ID_AVM 0x1244 +#endif + +#ifndef PCI_DEVICE_ID_AVM_B1 +#define PCI_DEVICE_ID_AVM_B1 0x700 +#endif + +static char *revision = "$Revision: 1.2 $"; + +/* ------------------------------------------------------------- */ + +#ifdef HAS_NEW_SYMTAB +MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); +#endif + +/* ------------------------------------------------------------- */ + +/* ------------------------------------------------------------- */ +/* -------- Init & Cleanup ------------------------------------- */ +/* ------------------------------------------------------------- */ + +/* + * init / exit functions + */ + +#ifdef MODULE +#define b1pci_init init_module +#endif + +int b1pci_init(void) +{ + char *p; + char rev[10]; + int rc; + int pci_index; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, " ??? "); + + +#ifdef CONFIG_PCI + if (!pcibios_present()) { + printk(KERN_ERR "b1pci: no PCI-BIOS present\n"); + return -EIO; + } + + printk(KERN_INFO "b1pci: revision %s\n", rev); + + for (pci_index = 0; pci_index < 8; pci_index++) { + unsigned char pci_bus, pci_device_fn; + unsigned int ioaddr; + unsigned char irq; + + if (pcibios_find_device (PCI_VENDOR_ID_AVM, + PCI_DEVICE_ID_AVM_B1, pci_index, + &pci_bus, &pci_device_fn) != 0) { + continue; + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &irq); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &ioaddr); + /* Strip the I/O address out of the returned value */ + ioaddr &= PCI_BASE_ADDRESS_IO_MASK; + printk(KERN_INFO + "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", + ioaddr, irq); + if ((rc = avmb1_probecard(ioaddr, irq)) != 0) { + printk(KERN_ERR + "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n", + ioaddr, irq); + return rc; + } + if ((rc = avmb1_addcard(ioaddr, irq)) != 0) + return rc; + } + return 0; +#else + printk(KERN_ERR "b1pci: kernel not compiled with PCI.\n"); + return -EIO; +#endif +} + +#ifdef MODULE +void cleanup_module(void) +{ +} +#endif diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c new file mode 100644 index 000000000..34fde7bd9 --- /dev/null +++ b/drivers/isdn/avmb1/capi.c @@ -0,0 +1,540 @@ +/* + * $Id: capi.c,v 1.4 1997/05/27 15:17:50 fritz Exp $ + * + * CAPI 2.0 Interface for Linux + * + * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capi.c,v $ + * Revision 1.4 1997/05/27 15:17:50 fritz + * Added changes for recent 2.1.x kernels: + * changed return type of isdn_close + * queue_task_* -> queue_task + * clear/set_bit -> test_and_... where apropriate. + * changed type of hard_header_cache parameter. + * + * Revision 1.3 1997/05/18 09:24:14 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.2 1997/03/05 21:17:59 fritz + * Added capi_poll for compiling under 2.1.27 + * + * Revision 1.1 1997/03/04 21:50:29 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/sched.h> +#include <linux/malloc.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> +#if (LINUX_VERSION_CODE >= 0x020117) +#include <asm/poll.h> +#endif +#include <linux/capi.h> +#include <linux/kernelcapi.h> + +#include "compat.h" +#include "capiutil.h" +#include "capicmd.h" +#include "capidev.h" + +#ifdef HAS_NEW_SYMTAB +MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)"); +#endif + +/* -------- driver information -------------------------------------- */ + +int capi_major = 68; /* allocated */ + +#ifdef HAS_NEW_SYMTAB +MODULE_PARM(capi_major, "i"); +#endif + +/* -------- global variables ---------------------------------------- */ + +static struct capidev capidevs[CAPI_MAXMINOR + 1]; +struct capi_interface *capifuncs; + +/* -------- function called by lower level -------------------------- */ + +static void capi_signal(__u16 applid, __u32 minor) +{ + struct capidev *cdev; + struct sk_buff *skb = 0; + + if (minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) { + printk(KERN_ERR "BUG: capi_signal: illegal minor %d\n", minor); + return; + } + cdev = &capidevs[minor]; + (void) (*capifuncs->capi_get_message) (applid, &skb); + if (skb) { + skb_queue_tail(&cdev->recv_queue, skb); + wake_up_interruptible(&cdev->recv_wait); + } else { + printk(KERN_ERR "BUG: capi_signal: no skb\n"); + } +} + +/* -------- file_operations ----------------------------------------- */ + +#if LINUX_VERSION_CODE < 0x020100 +static int capi_lseek(struct inode *inode, struct file *file, + off_t offset, int origin) +{ + return -ESPIPE; +} +#else +static long long capi_llseek(struct inode *inode, struct file *file, + long long offset, int origin) +{ + return -ESPIPE; +} +#endif + +#if LINUX_VERSION_CODE < 0x020100 +static int capi_read(struct inode *inode, struct file *file, + char *buf, int count) +#else +static long capi_read(struct inode *inode, struct file *file, + char *buf, unsigned long count) +#endif +{ + unsigned int minor = MINOR(inode->i_rdev); + struct capidev *cdev; + struct sk_buff *skb; + int retval; + size_t copied; + + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + return -ENODEV; + + cdev = &capidevs[minor]; + + if ((skb = skb_dequeue(&cdev->recv_queue)) == 0) { + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + for (;;) { + interruptible_sleep_on(&cdev->recv_wait); + if ((skb = skb_dequeue(&cdev->recv_queue)) != 0) + break; + if (current->signal & ~current->blocked) + break; + } + if (skb == 0) + return -ERESTARTNOHAND; + } + if (skb->len > count) { + skb_queue_head(&cdev->recv_queue, skb); + return -EMSGSIZE; + } + if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) + CAPIMSG_SETDATA(skb->data, buf + CAPIMSG_LEN(skb->data)); + retval = copy_to_user(buf, skb->data, skb->len); + if (retval) { + skb_queue_head(&cdev->recv_queue, skb); + return retval; + } + copied = skb->len; + + + kfree_skb(skb, FREE_READ); + + return copied; +} + +#if LINUX_VERSION_CODE < 0x020100 +static int capi_write(struct inode *inode, struct file *file, + const char *buf, int count) +#else +static long capi_write(struct inode *inode, struct file *file, + const char *buf, unsigned long count) +#endif +{ + unsigned int minor = MINOR(inode->i_rdev); + struct capidev *cdev; + struct sk_buff *skb; + int retval; + __u8 cmd; + __u8 subcmd; + __u16 mlen; + + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + return -ENODEV; + + cdev = &capidevs[minor]; + + skb = alloc_skb(count, GFP_USER); + + if ((retval = copy_from_user(skb_put(skb, count), buf, count))) { + dev_kfree_skb(skb, FREE_WRITE); + return retval; + } + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + mlen = CAPIMSG_LEN(skb->data); + if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + if (mlen + dlen != count) { + dev_kfree_skb(skb, FREE_WRITE); + return -EINVAL; + } + } else if (mlen != count) { + dev_kfree_skb(skb, FREE_WRITE); + return -EINVAL; + } + CAPIMSG_SETAPPID(skb->data, cdev->applid); + + cdev->errcode = (*capifuncs->capi_put_message) (cdev->applid, skb); + + if (cdev->errcode) { + dev_kfree_skb(skb, FREE_WRITE); + return -EIO; + } + return count; +} + +#if (LINUX_VERSION_CODE < 0x020117) +static int capi_select(struct inode *inode, struct file *file, + int sel_type, select_table * wait) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct capidev *cdev; + + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + return -ENODEV; + + cdev = &capidevs[minor]; + + switch (sel_type) { + case SEL_IN: + if (!skb_queue_empty(&cdev->recv_queue)) + return 1; + /* fall througth */ + case SEL_EX: + /* error conditions ? */ + + select_wait(&cdev->recv_wait, wait); + return 0; + case SEL_OUT: + /* + if (!queue_full()) + return 1; + select_wait(&cdev->send_wait, wait); + return 0; + */ + return 1; + } + return 1; +} +#else +static unsigned int +capi_poll(struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + unsigned int minor = MINOR(file->f_inode->i_rdev); + struct capidev *cdev; + + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + return POLLERR; + + cdev = &capidevs[minor]; + poll_wait(&(cdev->recv_wait), wait); + mask = POLLOUT | POLLWRNORM; + if (!skb_queue_empty(&cdev->recv_queue)) + mask |= POLLIN | POLLRDNORM; + return mask; +} +#endif + +static int capi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct capidev *cdev; + capi_ioctl_struct data; + int retval; + + + if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) + return -ENODEV; + + cdev = &capidevs[minor]; + + switch (cmd) { + case CAPI_REGISTER: + { + if (!minor) + return -EINVAL; + retval = copy_from_user((void *) &data.rparams, + (void *) arg, sizeof(struct capi_register_params)); + if (retval) + return -EFAULT; + if (cdev->is_registered) + return -EEXIST; + cdev->errcode = (*capifuncs->capi_register) (&data.rparams, + &cdev->applid); + if (cdev->errcode) + return -EIO; + (void) (*capifuncs->capi_set_signal) (cdev->applid, capi_signal, minor); + cdev->is_registered = 1; + } + return 0; + + case CAPI_GET_VERSION: + { + retval = copy_from_user((void *) &data.contr, + (void *) arg, + sizeof(data.contr)); + if (retval) + return -EFAULT; + cdev->errcode = (*capifuncs->capi_get_version) (data.contr, &data.version); + if (cdev->errcode) + return -EIO; + retval = copy_to_user((void *) arg, + (void *) &data.version, + sizeof(data.version)); + if (retval) + return -EFAULT; + } + return 0; + + case CAPI_GET_SERIAL: + { + retval = copy_from_user((void *) &data.contr, + (void *) arg, + sizeof(data.contr)); + if (retval) + return -EFAULT; + cdev->errcode = (*capifuncs->capi_get_serial) (data.contr, data.serial); + if (cdev->errcode) + return -EIO; + retval = copy_to_user((void *) arg, + (void *) data.serial, + sizeof(data.serial)); + if (retval) + return -EFAULT; + } + return 0; + case CAPI_GET_PROFILE: + { + retval = copy_from_user((void *) &data.contr, + (void *) arg, + sizeof(data.contr)); + if (retval) + return -EFAULT; + + if (data.contr == 0) { + cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile); + if (cdev->errcode) + return -EIO; + + retval = copy_to_user((void *) arg, + (void *) &data.profile.ncontroller, + sizeof(data.profile.ncontroller)); + + } else { + cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile); + if (cdev->errcode) + return -EIO; + + retval = copy_to_user((void *) arg, + (void *) &data.profile, + sizeof(data.profile)); + } + if (retval) + return -EFAULT; + } + return 0; + + case CAPI_GET_MANUFACTURER: + { + retval = copy_from_user((void *) &data.contr, + (void *) arg, + sizeof(data.contr)); + if (retval) + return -EFAULT; + cdev->errcode = (*capifuncs->capi_get_manufacturer) (data.contr, data.manufacturer); + if (cdev->errcode) + return -EIO; + + retval = copy_to_user((void *) arg, (void *) data.manufacturer, + sizeof(data.manufacturer)); + if (retval) + return -EFAULT; + + } + return 0; + case CAPI_GET_ERRCODE: + data.errcode = cdev->errcode; + cdev->errcode = CAPI_NOERROR; + if (arg) { + retval = copy_to_user((void *) arg, + (void *) &data.errcode, + sizeof(data.errcode)); + if (retval) + return -EFAULT; + } + return data.errcode; + + case CAPI_INSTALLED: + if ((*capifuncs->capi_installed) ()) + return 0; + return -ENXIO; + + case CAPI_MANUFACTURER_CMD: + { + struct capi_manufacturer_cmd mcmd; + if (minor) + return -EINVAL; + if (!suser()) + return -EPERM; + retval = copy_from_user((void *) &mcmd, (void *) arg, + sizeof(mcmd)); + if (retval) + return -EFAULT; + return (*capifuncs->capi_manufacturer) (mcmd.cmd, mcmd.data); + } + return 0; + } + return -EINVAL; +} + +static int capi_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + + if (minor >= CAPI_MAXMINOR) + return -ENXIO; + + if (minor) { + if (capidevs[minor].is_open) + return -EEXIST; + + capidevs[minor].is_open = 1; + skb_queue_head_init(&capidevs[minor].recv_queue); + MOD_INC_USE_COUNT; + + } else { + + if (!capidevs[minor].is_open) { + capidevs[minor].is_open = 1; + MOD_INC_USE_COUNT; + } + } + + + return 0; +} + +static CLOSETYPE +capi_release(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct capidev *cdev; + struct sk_buff *skb; + + if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) { + printk(KERN_ERR "capi20: release minor %d ???\n", minor); + return CLOSEVAL; + } + cdev = &capidevs[minor]; + + if (minor) { + + if (cdev->is_registered) + (*capifuncs->capi_release) (cdev->applid); + + cdev->is_registered = 0; + cdev->applid = 0; + + while ((skb = skb_dequeue(&cdev->recv_queue)) != 0) + kfree_skb(skb, FREE_READ); + } + cdev->is_open = 0; + + MOD_DEC_USE_COUNT; + return CLOSEVAL; +} + +static struct file_operations capi_fops = +{ +#if LINUX_VERSION_CODE < 0x020100 + capi_lseek, +#else + capi_llseek, +#endif + capi_read, + capi_write, + NULL, /* capi_readdir */ +#if (LINUX_VERSION_CODE < 0x020117) + capi_select, +#else + capi_poll, +#endif + capi_ioctl, + NULL, /* capi_mmap */ + capi_open, + capi_release, + NULL, /* capi_fsync */ + NULL, /* capi_fasync */ +}; + + +/* -------- init function and module interface ---------------------- */ + +#ifdef MODULE +#define capi_init init_module +#endif + +static struct capi_interface_user cuser = { + "capi20", + 0, +}; + +int capi_init(void) +{ + memset(capidevs, 0, sizeof(capidevs)); + + if (register_chrdev(capi_major, "capi20", &capi_fops)) { + printk(KERN_ERR "capi20: unable to get major %d\n", capi_major); + return -EIO; + } + printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major); + + if ((capifuncs = attach_capi_interface(&cuser)) == 0) { + unregister_chrdev(capi_major, "capi20"); + return -EIO; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + unregister_chrdev(capi_major, "capi20"); + (void) detach_capi_interface(&cuser); +} + +#endif diff --git a/drivers/isdn/avmb1/capicmd.h b/drivers/isdn/avmb1/capicmd.h new file mode 100644 index 000000000..104ef824f --- /dev/null +++ b/drivers/isdn/avmb1/capicmd.h @@ -0,0 +1,119 @@ +/* + * $Id: capicmd.h,v 1.1 1997/03/04 21:50:30 calle Exp $ + * + * CAPI 2.0 Interface for Linux + * + * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capicmd.h,v $ + * Revision 1.1 1997/03/04 21:50:30 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + * + */ +#ifndef __CAPICMD_H__ +#define __CAPICMD_H__ + +/*----- CAPI commands -----*/ +#define CAPI_ALERT 0x01 +#define CAPI_CONNECT 0x02 +#define CAPI_CONNECT_ACTIVE 0x03 +#define CAPI_CONNECT_B3_ACTIVE 0x83 +#define CAPI_CONNECT_B3 0x82 +#define CAPI_CONNECT_B3_T90_ACTIVE 0x88 +#define CAPI_DATA_B3 0x86 +#define CAPI_DISCONNECT_B3 0x84 +#define CAPI_DISCONNECT 0x04 +#define CAPI_FACILITY 0x80 +#define CAPI_INFO 0x08 +#define CAPI_LISTEN 0x05 +#define CAPI_MANUFACTURER 0xff +#define CAPI_RESET_B3 0x87 +#define CAPI_SELECT_B_PROTOCOL 0x41 + +/*----- CAPI subcommands -----*/ + +#define CAPI_REQ 0x80 +#define CAPI_CONF 0x81 +#define CAPI_IND 0x82 +#define CAPI_RESP 0x83 + +/*----- CAPI combined commands -----*/ + +#define CAPICMD(cmd,subcmd) (((cmd)<<8)|(subcmd)) + +#define CAPI_DISCONNECT_REQ CAPICMD(CAPI_DISCONNECT,CAPI_REQ) +#define CAPI_DISCONNECT_CONF CAPICMD(CAPI_DISCONNECT,CAPI_CONF) +#define CAPI_DISCONNECT_IND CAPICMD(CAPI_DISCONNECT,CAPI_IND) +#define CAPI_DISCONNECT_RESP CAPICMD(CAPI_DISCONNECT,CAPI_RESP) + +#define CAPI_ALERT_REQ CAPICMD(CAPI_ALERT,CAPI_REQ) +#define CAPI_ALERT_CONF CAPICMD(CAPI_ALERT,CAPI_CONF) + +#define CAPI_CONNECT_REQ CAPICMD(CAPI_CONNECT,CAPI_REQ) +#define CAPI_CONNECT_CONF CAPICMD(CAPI_CONNECT,CAPI_CONF) +#define CAPI_CONNECT_IND CAPICMD(CAPI_CONNECT,CAPI_IND) +#define CAPI_CONNECT_RESP CAPICMD(CAPI_CONNECT,CAPI_RESP) + +#define CAPI_CONNECT_ACTIVE_REQ CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_REQ) +#define CAPI_CONNECT_ACTIVE_CONF CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_CONF) +#define CAPI_CONNECT_ACTIVE_IND CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_IND) +#define CAPI_CONNECT_ACTIVE_RESP CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_RESP) + +#define CAPI_SELECT_B_PROTOCOL_REQ CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_REQ) +#define CAPI_SELECT_B_PROTOCOL_CONF CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_CONF) + +#define CAPI_CONNECT_B3_ACTIVE_REQ CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_REQ) +#define CAPI_CONNECT_B3_ACTIVE_CONF CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_CONF) +#define CAPI_CONNECT_B3_ACTIVE_IND CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_IND) +#define CAPI_CONNECT_B3_ACTIVE_RESP CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_RESP) + +#define CAPI_CONNECT_B3_REQ CAPICMD(CAPI_CONNECT_B3,CAPI_REQ) +#define CAPI_CONNECT_B3_CONF CAPICMD(CAPI_CONNECT_B3,CAPI_CONF) +#define CAPI_CONNECT_B3_IND CAPICMD(CAPI_CONNECT_B3,CAPI_IND) +#define CAPI_CONNECT_B3_RESP CAPICMD(CAPI_CONNECT_B3,CAPI_RESP) + + +#define CAPI_CONNECT_B3_T90_ACTIVE_IND CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_IND) +#define CAPI_CONNECT_B3_T90_ACTIVE_RESP CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_RESP) + +#define CAPI_DATA_B3_REQ CAPICMD(CAPI_DATA_B3,CAPI_REQ) +#define CAPI_DATA_B3_CONF CAPICMD(CAPI_DATA_B3,CAPI_CONF) +#define CAPI_DATA_B3_IND CAPICMD(CAPI_DATA_B3,CAPI_IND) +#define CAPI_DATA_B3_RESP CAPICMD(CAPI_DATA_B3,CAPI_RESP) + +#define CAPI_DISCONNECT_B3_REQ CAPICMD(CAPI_DISCONNECT_B3,CAPI_REQ) +#define CAPI_DISCONNECT_B3_CONF CAPICMD(CAPI_DISCONNECT_B3,CAPI_CONF) +#define CAPI_DISCONNECT_B3_IND CAPICMD(CAPI_DISCONNECT_B3,CAPI_IND) +#define CAPI_DISCONNECT_B3_RESP CAPICMD(CAPI_DISCONNECT_B3,CAPI_RESP) + +#define CAPI_RESET_B3_REQ CAPICMD(CAPI_RESET_B3,CAPI_REQ) +#define CAPI_RESET_B3_CONF CAPICMD(CAPI_RESET_B3,CAPI_CONF) +#define CAPI_RESET_B3_IND CAPICMD(CAPI_RESET_B3,CAPI_IND) +#define CAPI_RESET_B3_RESP CAPICMD(CAPI_RESET_B3,CAPI_RESP) + +#define CAPI_LISTEN_REQ CAPICMD(CAPI_LISTEN,CAPI_REQ) +#define CAPI_LISTEN_CONF CAPICMD(CAPI_LISTEN,CAPI_CONF) + +#define CAPI_MANUFACTURER_REQ CAPICMD(CAPI_MANUFACTURER,CAPI_REQ) +#define CAPI_MANUFACTURER_CONF CAPICMD(CAPI_MANUFACTURER,CAPI_CONF) +#define CAPI_MANUFACTURER_IND CAPICMD(CAPI_MANUFACTURER,CAPI_IND) +#define CAPI_MANUFACTURER_RESP CAPICMD(CAPI_MANUFACTURER,CAPI_RESP) + +#define CAPI_FACILITY_REQ CAPICMD(CAPI_FACILITY,CAPI_REQ) +#define CAPI_FACILITY_CONF CAPICMD(CAPI_FACILITY,CAPI_CONF) +#define CAPI_FACILITY_IND CAPICMD(CAPI_FACILITY,CAPI_IND) +#define CAPI_FACILITY_RESP CAPICMD(CAPI_FACILITY,CAPI_RESP) + +#define CAPI_INFO_REQ CAPICMD(CAPI_INFO,CAPI_REQ) +#define CAPI_INFO_CONF CAPICMD(CAPI_INFO,CAPI_CONF) +#define CAPI_INFO_IND CAPICMD(CAPI_INFO,CAPI_IND) +#define CAPI_INFO_RESP CAPICMD(CAPI_INFO,CAPI_RESP) + +#endif /* __CAPICMD_H__ */ diff --git a/drivers/isdn/avmb1/capidev.h b/drivers/isdn/avmb1/capidev.h new file mode 100644 index 000000000..f2e0d6d2d --- /dev/null +++ b/drivers/isdn/avmb1/capidev.h @@ -0,0 +1,29 @@ +/* + * $Id: capidev.h,v 1.1 1997/03/04 21:50:30 calle Exp $ + * + * CAPI 2.0 Interface for Linux + * + * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capidev.h,v $ + * Revision 1.1 1997/03/04 21:50:30 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + */ + +struct capidev { + int is_open; + int is_registered; + __u16 applid; + struct sk_buff_head recv_queue; + struct wait_queue *recv_wait; + __u16 errcode; +}; + +#define CAPI_MAXMINOR CAPI_MAXAPPL diff --git a/drivers/isdn/avmb1/capidrv.c b/drivers/isdn/avmb1/capidrv.c new file mode 100644 index 000000000..f6bb51f13 --- /dev/null +++ b/drivers/isdn/avmb1/capidrv.c @@ -0,0 +1,1764 @@ +/* + * $Id: capidrv.c,v 1.3 1997/05/18 09:24:15 calle Exp $ + * + * ISDN4Linux Driver, using capi20 interface (kernelcapi) + * + * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capidrv.c,v $ + * Revision 1.3 1997/05/18 09:24:15 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.2 1997/03/05 21:19:59 fritz + * Removed include of config.h (mkdep stated this is unneded). + * + * Revision 1.1 1997/03/04 21:50:31 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/sched.h> +#include <linux/malloc.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/capi.h> +#include <linux/kernelcapi.h> + +#include "compat.h" +#include "capiutil.h" +#include "capicmd.h" +#include "capidrv.h" + +static char *revision = "$Revision: 1.3 $"; +int debugmode = 0; + +#ifdef HAS_NEW_SYMTAB +MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); +MODULE_PARM(debugmode, "i"); +#endif + +/* -------- type definitions ----------------------------------------- */ + + +struct capidrv_contr { + + struct capidrv_contr *next; + + __u32 contrnr; + char name[20]; + + /* + * for isdn4linux + */ + isdn_if interface; + int myid; + + /* + * LISTEN state + */ + int state; + __u32 cipmask; + __u32 cipmask2; + + /* + * 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; + 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; + } *ncci_list; + } *plcip; + struct capidrv_ncci *nccip; + } *bchans; + + struct capidrv_plci *plci_list; +}; + +struct capidrv_data { + __u16 appid; + 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 struct capi_interface *capifuncs; + +/* -------- 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; + } +} + +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: + return 1; + case ISDN_PROTO_L2_TRANS: + return 0; + } +} + +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: + default: + return 0; + } +} + +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 managment ------------------------------------- */ + +static inline capidrv_contr *findcontrbydriverid(int driverid) +{ + capidrv_contr *p = global.contr_list; + + while (p) { + if (p->myid == driverid) + return p; + p = p->next; + } + return (capidrv_contr *) 0; +} + +static capidrv_contr *findcontrbynumber(__u32 contr) +{ + capidrv_contr *p = global.contr_list; + + while (p) { + if (p->contrnr == contr) + return p; + p = p->next; + } + return (capidrv_contr *) 0; +} + + +/* -------- 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 0; + + 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 0; +} + +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 0; +} + +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 0; +} + +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 = 0; + card->bchans[plcip->chan].disconnecting = 0; + card->bchans[plcip->chan].incoming = 0; + kfree(plcip); + return; + } + } + printk(KERN_ERR "capidrv: free_plci %p (0x%x) not found, Huh?\n", + 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 0; + + 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 0; + + for (p = plcip->ncci_list; p; p = p->next) + if (p->ncci == ncci) + return p; + return 0; +} + +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 0; + + for (p = plcip->ncci_list; p; p = p->next) + if (p->msgid == msgid) + return p; + return 0; +} + +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 = 0; + kfree(nccip); +} + +/* -------- 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 = dev_alloc_skb(len); + SET_SKB_FREE(skb); + memcpy(skb_put(skb, len), cmsg->buf, len); + (*capifuncs->capi_put_message) (global.appid, 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: listen_change_state %d -> %d\n", + card->state, p->nextstate); + card->state = p->nextstate; + return; + } + p++; + } + printk(KERN_ERR "capidrv: listen_change_state state=%d event=%d ????\n", + card->state, event); + +} + +/* ------------------------------------------------------------------ */ + +static void p0(capidrv_contr * card, capidrv_plci * plci) +{ + isdn_ctrl cmd; + + card->bchans[plci->chan].contr = 0; + 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, 0}, + {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0}, + {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0}, + /* 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, 0}, + {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + /* P-1 */ + {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, + {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, +{ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + /* P-ACT */ + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + /* P-2 */ + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, + {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0}, + {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + /* P-3 */ +{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, +{ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0}, +{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + /* P-4 */ + {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, + {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, +{ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + /* P-5 */ +{ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + /* P-6 */ + {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0}, + {}, +}; + +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: plci_change_state:0x%x %d -> %d\n", + plci->plci, plci->state, p->nextstate); + plci->state = p->nextstate; + if (p->changefunc) + p->changefunc(card, plci); + return; + } + p++; + } + printk(KERN_ERR "capidrv: plci_change_state:0x%x state=%d event=%d ????\n", + 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.appid, + card->msgid++, + ncci->plcip->plci, + 0, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* 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, 0}, + {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0}, + /* N-0.1 */ + {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0}, + {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, 0}, + /* N-1 */ + {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0}, + {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0}, + {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, + {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + /* N-2 */ + {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0}, + {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, +{ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + /* N-ACT */ + {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0}, + {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, + {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + /* N-3 */ + {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0}, + {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, + {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + /* N-4 */ + {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, + {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR, 0}, + /* 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: ncci_change_state:0x%x %d -> %d\n", + 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: ncci_change_state:0x%x state=%d event=%d ????\n", + 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: listenconf Info=0x%4x (%s) cipmask=0x%x\n", + 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 */ + goto ignored; + case CAPI_MANUFACTURER_CONF: /* Controller */ + 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: got %s from controller 0x%x ???", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController); + } + return; + + ignored: + printk(KERN_INFO "capidrv: %s from controller 0x%x ignored\n", + 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: incoming call on not existing bchan ?\n"); + return; + } + bchan = &card->bchans[chan]; + if ((plcip = new_plci(card, chan)) == 0) { + printk(KERN_ERR "capidrv: incoming call: no memory, sorry.\n"); + 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: incoming call %s,%d,%d,%s\n", + cmd.parm.setup.phone, + cmd.parm.setup.si1, + cmd.parm.setup.si2, + cmd.parm.setup.eazmsn); + + switch (card->interface.statcallb(&cmd)) { + case 0: + /* 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: incoming call %s,%d,%d,%s ignored\n", + 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: incoming call %s,%d,%d,%s tty alerting\n", + cmd.parm.setup.phone, + cmd.parm.setup.si1, + cmd.parm.setup.si2, + cmd.parm.setup.eazmsn); + capi_fill_ALERT_REQ(cmsg, + global.appid, + card->msgid++, + plcip->plci, /* adr */ + 0, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* Facilitydataarray */ + ); + plcip->msgid = cmsg->Messagenumber; + send_message(card, cmsg); + } else { + printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s on netdev\n", + 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: %s reason 0x%x (%s) for plci 0x%x\n", + 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: %s info 0x%x (%s) for plci 0x%x\n", + 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: %s info 0x%x (%s) for plci 0x%x\n", + 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: %s info 0x%x (%s) for plci 0x%x\n", + 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: no mem for ncci, sorry\n"); + break; /* $$$$ */ + } + capi_fill_CONNECT_B3_REQ(cmsg, + global.appid, + card->msgid++, + plcip->plci, /* adr */ + 0 /* 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: %s\n", 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: got %s for plci 0x%x ???", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrPLCI); + } + return; + ignored: + printk(KERN_INFO "capidrv: %s for plci 0x%x ignored\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrPLCI); + return; + notfound: + printk(KERN_ERR "capidrv: %s: plci 0x%x not found\n", + 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; + + 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: chan %d up with ncci 0x%x\n", + 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.appid, + card->msgid++, + nccip->ncci, /* adr */ + 0, /* Reject */ + 0 /* NCPI */ + ); + send_message(card, cmsg); + ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP); + break; + } + printk(KERN_ERR "capidrv: no mem for ncci, sorry\n"); + } else { + printk(KERN_ERR "capidrv: %s: plci for ncci 0x%x not found\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrNCCI); + } + capi_fill_CONNECT_B3_RESP(cmsg, + global.appid, + card->msgid++, + cmsg->adr.adrNCCI, + 2, /* Reject */ + 0 /* 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: %s info 0x%x (%s) for ncci 0x%x\n", + 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 (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) + goto notfound; + + cmd.command = ISDN_STAT_BSENT; + cmd.driver = card->myid; + cmd.arg = nccip->chan; + 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: %s info 0x%x (%s) for ncci 0x%x\n", + 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 */ + 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: got %s for ncci 0x%x ???", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrNCCI); + } + return; + ignored: + printk(KERN_INFO "capidrv: %s for ncci 0x%x ignored\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrNCCI); + return; + notfound: + printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n", + 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); + return; + } + if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) { + printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrNCCI); + kfree_skb(skb, FREE_READ); + 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_signal(__u16 applid, __u32 dummy) +{ + struct sk_buff *skb = 0; + + while ((*capifuncs->capi_get_message) (global.appid, &skb) == CAPI_NOERROR) { + capi_message2cmsg(&s_cmsg, skb->data); + if (debugmode > 1) + printk(KERN_DEBUG "capidrv_signal: %s\n", capi_cmsg2str(&s_cmsg)); + + if (s_cmsg.Command == CAPI_DATA_B3 + && s_cmsg.Subcommand == CAPI_IND) { + handle_data(&s_cmsg, skb); + continue; + } + kfree_skb(skb, FREE_READ); + 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); + } +} + +/* ------------------------------------------------------------------- */ + +static _cmsg cmdcmsg; + +static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card) +{ + switch (c->arg) { + default: + printk(KERN_DEBUG "capidrv: capidrv_ioctl(%ld) called ??\n", c->arg); + return -EINVAL; + } + return -EINVAL; +} + +static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) +{ + isdn_ctrl cmd; + struct capidrv_bchan *bchan; + struct capidrv_plci *plcip; + + 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: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n", + 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: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n", + 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)); + + 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.appid, + card->msgid++, + 1, /* adr */ + si2cip(bchan->si1, bchan->si2), /* cipvalue */ + called, /* CalledPartyNumber */ + calling, /* CallingPartyNumber */ + 0, /* CalledPartySubaddress */ + 0, /* CallingPartySubaddress */ + b1prot(bchan->l2, bchan->l3), /* B1protocol */ + b2prot(bchan->l2, bchan->l3), /* B2protocol */ + b3prot(bchan->l2, bchan->l3), /* B3protocol */ + 0, /* B1configuration */ + 0, /* B2configuration */ + 0, /* B3configuration */ + 0, /* BC */ + 0, /* LLC */ + 0, /* HLC */ + 0, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* 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; + plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ); + send_message(card, &cmdcmsg); + return 0; + } + + case ISDN_CMD_ACCEPTD: + + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTD(ch=%ld)\n", + c->arg); + bchan = &card->bchans[c->arg % card->nbchan]; + + capi_fill_CONNECT_RESP(&cmdcmsg, + global.appid, + 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 */ + 0, /* B1configuration */ + 0, /* B2configuration */ + 0, /* B3configuration */ + 0, /* ConnectedNumber */ + 0, /* ConnectedSubaddress */ + 0, /* LLC */ + 0, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* 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: ISDN_CMD_ACCEPTB(ch=%ld)\n", + c->arg); + return -ENOSYS; + + case ISDN_CMD_HANGUP: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_HANGUP(ch=%ld)\n", + c->arg); + bchan = &card->bchans[c->arg % card->nbchan]; + + if (bchan->disconnecting) { + if (debugmode) + printk(KERN_DEBUG "capidrv: chan %ld already disconnecting ...\n", + c->arg); + return 0; + } + if (bchan->nccip) { + bchan->disconnecting = 1; + capi_fill_DISCONNECT_B3_REQ(&cmdcmsg, + global.appid, + card->msgid++, + bchan->nccip->ncci, + 0 /* NCPI */ + ); + ncci_change_state(card, bchan->nccip, EV_NCCI_DISCONNECT_B3_REQ); + send_message(card, &cmdcmsg); + } else if (bchan->plcip) { + bchan->disconnecting = 1; + 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 + */ + } else { + capi_fill_DISCONNECT_REQ(&cmdcmsg, + global.appid, + card->msgid++, + bchan->plcip->plci, + 0, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* Facilitydataarray */ + ); + plci_change_state(card, bchan->plcip, EV_PLCI_DISCONNECT_REQ); + send_message(card, &cmdcmsg); + } + } +/* ready */ + + case ISDN_CMD_SETL2: + if (debugmode) + printk(KERN_DEBUG "capidrv: set L2 on chan %ld to %ld\n", + (c->arg & 0xff), (c->arg >> 8)); + bchan = &card->bchans[c->arg % card->nbchan]; + bchan->l2 = (c->arg >> 8); + return 0; + + case ISDN_CMD_SETL3: + if (debugmode) + printk(KERN_DEBUG "capidrv: set L3 on chan %ld to %ld\n", + (c->arg & 0xff), (c->arg >> 8)); + bchan = &card->bchans[c->arg % card->nbchan]; + bchan->l3 = (c->arg >> 8); + return 0; + + case ISDN_CMD_SETEAZ: + if (debugmode) + printk(KERN_DEBUG "capidrv: set EAZ \"%s\" on chan %ld\n", + 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: clearing EAZ on chan %ld\n", c->arg); + bchan = &card->bchans[c->arg % card->nbchan]; + bchan->msn[0] = 0; + return 0; + + case ISDN_CMD_LOCK: + if (debugmode > 1) + printk(KERN_DEBUG "capidrv: ISDN_CMD_LOCK (%ld)\n", c->arg); + MOD_INC_USE_COUNT; + break; + + case ISDN_CMD_UNLOCK: + if (debugmode > 1) + printk(KERN_DEBUG "capidrv: ISDN_CMD_UNLOCK (%ld)\n", c->arg); + MOD_DEC_USE_COUNT; + break; + +/* never called */ + case ISDN_CMD_GETL2: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL2\n"); + return -ENODEV; + case ISDN_CMD_GETL3: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL3\n"); + return -ENODEV; + case ISDN_CMD_GETEAZ: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_GETEAZ\n"); + return -ENODEV; + case ISDN_CMD_SETSIL: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_SETSIL\n"); + return -ENODEV; + case ISDN_CMD_GETSIL: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_GETSIL\n"); + return -ENODEV; + default: + printk(KERN_ERR "capidrv: ISDN_CMD_%d, Huh?\n", 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, struct sk_buff *skb) +{ + capidrv_contr *card = findcontrbydriverid(id); + capidrv_bchan *bchan; + capidrv_ncci *nccip; + int len = skb->len; + size_t msglen; + __u16 errcode; + + if (!card) { + printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n", + id); + return 0; + } + bchan = &card->bchans[channel % card->nbchan]; + nccip = bchan->nccip; + if (!nccip || nccip->state != ST_NCCI_ACTIVE) { + printk(KERN_ERR "capidrv: if_sendbuf: %s:%d: chan not up!\n", + card->name, channel); + return 0; + } + capi_fill_DATA_B3_REQ(&sendcmsg, global.appid, card->msgid++, + nccip->ncci, /* adr */ + (__u32) skb->data, /* Data */ + skb->len, /* DataLength */ + nccip->datahandle++, /* DataHandle */ + 0 /* Flags */ + ); + capi_cmsg2message(&sendcmsg, sendcmsg.buf); + msglen = CAPIMSG_LEN(sendcmsg.buf); + if (skb_headroom(skb) < msglen) { + struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len); + if (!nskb) { + printk(KERN_ERR "capidrv: if_sendbuf: no memory\n"); + return 0; + } +#if 0 + printk(KERN_DEBUG "capidrv: only %d bytes headroom\n", + skb_headroom(skb)); +#endif + SET_SKB_FREE(nskb); + memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen); + memcpy(skb_put(nskb, skb->len), skb->data, skb->len); + errcode = (*capifuncs->capi_put_message) (global.appid, nskb); + switch (errcode) { + case CAPI_NOERROR: + dev_kfree_skb(skb, FREE_WRITE); + return len; + case CAPI_SENDQUEUEFULL: + dev_kfree_skb(nskb, FREE_WRITE); + return 0; + default: + return -1; + } + } else { + memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen); + errcode = (*capifuncs->capi_put_message) (global.appid, skb); + switch (errcode) { + case CAPI_NOERROR: + return len; + case CAPI_SENDQUEUEFULL: + return 0; + default: + return -1; + } + } +} + +static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) +{ + capidrv_contr *card; + isdn_ctrl cmd; + char id[20]; + int i; + + sprintf(id, "capidrv-%d", contr); + 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)); + 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); + 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 = 0; + card->interface.readstat = 0; + card->interface.features = ISDN_FEATURE_L2_X75I | + ISDN_FEATURE_L2_X75UI | + ISDN_FEATURE_L2_X75BUI | + ISDN_FEATURE_L2_HDLC | + ISDN_FEATURE_L2_TRANS | + ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_P_UNKNOWN; + card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */ + strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); + card->next = global.contr_list; + global.contr_list = card; + global.ncontr++; + + if (!register_isdn(&card->interface)) { + global.contr_list = global.contr_list->next; + printk(KERN_ERR "capidrv: Unable to register contr %s\n", id); + kfree(card->bchans); + 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; + } + + cmd.driver = card->myid; + cmd.command = ISDN_STAT_RUN; + card->interface.statcallb(&cmd); + + card->cipmask = 1; /* any */ + card->cipmask2 = 0; + + capi_fill_LISTEN_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, /* controller */ + 1 << 6, /* Infomask */ + card->cipmask, + card->cipmask2, + 0, 0); + send_message(card, &cmdcmsg); + listen_change_state(card, EV_LISTEN_REQ); + + printk(KERN_INFO "%s: now up (%d B channels)\n", + card->name, card->nbchan); + + return 0; +} + +static int capidrv_delcontr(__u16 contr) +{ + capidrv_contr **pp, *card; + isdn_ctrl cmd; + int i; + + for (pp = &global.contr_list; *pp; pp = &(*pp)->next) { + if ((*pp)->contrnr == contr) + break; + } + if (!*pp) { + printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr); + return -1; + } + card = *pp; + *pp = (*pp)->next; + global.ncontr--; + + for (i = 0; i < card->nbchan; i++) { + if (card->bchans[i].nccip) + free_ncci(card, card->bchans[i].nccip); + if (card->bchans[i].plcip) + free_plci(card, card->bchans[i].plcip); + if (card->plci_list) + printk(KERN_ERR "capidrv: bug in free_plci()\n"); + } + kfree(card->bchans); + + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + + printk(KERN_INFO "%s: now down.\n", card->name); + + kfree(card); + + return 0; +} + + +static void lower_callback(unsigned int cmd, __u16 contr, void *data) +{ + switch (cmd) { + case KCI_CONTRUP: + (void) capidrv_addcontr(contr, (capi_profile *) data); + break; + case KCI_CONTRDOWN: + (void) capidrv_delcontr(contr); + break; + } +} + +static struct capi_interface_user cuser = { + "capidrv", + lower_callback +}; + +#ifdef MODULE +#define capidrv_init init_module +#endif + +int capidrv_init(void) +{ + struct capi_register_params rparam; + capi_profile profile; + char rev[10]; + char *p; + __u32 ncontr, contr; + __u16 errcode; + + capifuncs = attach_capi_interface(&cuser); + + if (!capifuncs) + return -EIO; + +#ifndef HAS_NEW_SYMTAB + /* No symbols to export, hide all symbols */ + register_symtab(NULL); +#endif + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, " ??? "); + + rparam.level3cnt = 2; + rparam.datablkcnt = 8; + rparam.datablklen = 2048; + errcode = (*capifuncs->capi_register) (&rparam, &global.appid); + if (errcode) { + detach_capi_interface(&cuser); + return -EIO; + } + + errcode = (*capifuncs->capi_get_profile) (0, &profile); + if (errcode != CAPI_NOERROR) { + (void) (*capifuncs->capi_release) (global.appid); + detach_capi_interface(&cuser); + return -EIO; + } + + (void) (*capifuncs->capi_set_signal) (global.appid, capidrv_signal, 0); + + ncontr = profile.ncontroller; + for (contr = 1; contr <= ncontr; contr++) { + errcode = (*capifuncs->capi_get_profile) (contr, &profile); + if (errcode != CAPI_NOERROR) + continue; + (void) capidrv_addcontr(contr, &profile); + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + capidrv_contr *card, *next; + char rev[10]; + char *p; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else { + strcpy(rev, " ??? "); + } + + for (card = global.contr_list; card; card = next) { + next = card->next; + capidrv_delcontr(card->contrnr); + } + + (void) (*capifuncs->capi_release) (global.appid); + detach_capi_interface(&cuser); + + printk(KERN_NOTICE "capidrv: Rev%s: unloaded\n", rev); +} + +#endif diff --git a/drivers/isdn/avmb1/capidrv.h b/drivers/isdn/avmb1/capidrv.h new file mode 100644 index 000000000..f30c3f4dd --- /dev/null +++ b/drivers/isdn/avmb1/capidrv.h @@ -0,0 +1,111 @@ +/* + * $Id: capidrv.h,v 1.1 1997/03/04 21:50:33 calle Exp $ + * + * ISDN4Linux Driver, using capi20 interface (kernelcapi) + * + * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capidrv.h,v $ + * Revision 1.1 1997/03/04 21:50:33 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + */ +#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 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 */ +#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 */ +#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 */ + +/* + * 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/avmb1/capiutil.c b/drivers/isdn/avmb1/capiutil.c new file mode 100644 index 000000000..b3c25cd2a --- /dev/null +++ b/drivers/isdn/avmb1/capiutil.c @@ -0,0 +1,974 @@ +/* + * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle 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.in-berlin.de) + * + * $Log: capiutil.c,v $ + * Revision 1.3 1997/05/18 09:24:18 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.2 1997/03/05 21:22:13 fritz + * Fix: Symbols have to be exported unconditionally. + * + * Revision 1.1 1997/03/04 21:50:34 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + */ +#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 <asm/segment.h> + +#include "compat.h" +#include "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 to 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[] = +{ + /*00 */ 0, + /*01 ALERT_REQ */ (unsigned char *) "\x03\x04\x0c\x27\x2f\x1c\x01\x01", + /*02 CONNECT_REQ */ (unsigned char *) "\x03\x14\x0e\x10\x0f\x11\x0d\x06\x08\x0a\x05\x07\x09\x01\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01", + /*03 */ 0, + /*04 DISCONNECT_REQ */ (unsigned char *) "\x03\x04\x0c\x27\x2f\x1c\x01\x01", + /*05 LISTEN_REQ */ (unsigned char *) "\x03\x25\x12\x13\x10\x11\x01", + /*06 */ 0, + /*07 */ 0, + /*08 INFO_REQ */ (unsigned char *) "\x03\x0e\x04\x0c\x27\x2f\x1c\x01\x01", + /*09 FACILITY_REQ */ (unsigned char *) "\x03\x1f\x1e\x01", + /*0a SELECT_B_PROTOCOL_REQ */ (unsigned char *) "\x03\x0d\x06\x08\x0a\x05\x07\x09\x01\x01", + /*0b CONNECT_B3_REQ */ (unsigned char *) "\x03\x2b\x01", + /*0c */ 0, + /*0d DISCONNECT_B3_REQ */ (unsigned char *) "\x03\x2b\x01", + /*0e */ 0, + /*0f DATA_B3_REQ */ (unsigned char *) "\x03\x18\x1a\x19\x20\x01", + /*10 RESET_B3_REQ */ (unsigned char *) "\x03\x2b\x01", + /*11 */ 0, + /*12 */ 0, + /*13 ALERT_CONF */ (unsigned char *) "\x03\x23\x01", + /*14 CONNECT_CONF */ (unsigned char *) "\x03\x23\x01", + /*15 */ 0, + /*16 DISCONNECT_CONF */ (unsigned char *) "\x03\x23\x01", + /*17 LISTEN_CONF */ (unsigned char *) "\x03\x23\x01", + /*18 MANUFACTURER_REQ */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01", + /*19 */ 0, + /*1a INFO_CONF */ (unsigned char *) "\x03\x23\x01", + /*1b FACILITY_CONF */ (unsigned char *) "\x03\x23\x1f\x1b\x01", + /*1c SELECT_B_PROTOCOL_CONF */ (unsigned char *) "\x03\x23\x01", + /*1d CONNECT_B3_CONF */ (unsigned char *) "\x03\x23\x01", + /*1e */ 0, + /*1f DISCONNECT_B3_CONF */ (unsigned char *) "\x03\x23\x01", + /*20 */ 0, + /*21 DATA_B3_CONF */ (unsigned char *) "\x03\x19\x23\x01", + /*22 RESET_B3_CONF */ (unsigned char *) "\x03\x23\x01", + /*23 */ 0, + /*24 */ 0, + /*25 */ 0, + /*26 CONNECT_IND */ (unsigned char *) "\x03\x14\x0e\x10\x0f\x11\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01", + /*27 CONNECT_ACTIVE_IND */ (unsigned char *) "\x03\x16\x17\x28\x01", + /*28 DISCONNECT_IND */ (unsigned char *) "\x03\x2c\x01", + /*29 */ 0, + /*2a MANUFACTURER_CONF */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01", + /*2b */ 0, + /*2c INFO_IND */ (unsigned char *) "\x03\x26\x24\x01", + /*2d FACILITY_IND */ (unsigned char *) "\x03\x1f\x1d\x01", + /*2e */ 0, + /*2f CONNECT_B3_IND */ (unsigned char *) "\x03\x2b\x01", + /*30 CONNECT_B3_ACTIVE_IND */ (unsigned char *) "\x03\x2b\x01", + /*31 DISCONNECT_B3_IND */ (unsigned char *) "\x03\x2d\x2b\x01", + /*32 */ 0, + /*33 DATA_B3_IND */ (unsigned char *) "\x03\x18\x1a\x19\x20\x01", + /*34 RESET_B3_IND */ (unsigned char *) "\x03\x2b\x01", + /*35 CONNECT_B3_T90_ACTIVE_IND */ (unsigned char *) "\x03\x2b\x01", + /*36 */ 0, + /*37 */ 0, + /*38 CONNECT_RESP */ (unsigned char *) "\x03\x2e\x0d\x06\x08\x0a\x05\x07\x09\x01\x16\x17\x28\x04\x0c\x27\x2f\x1c\x01\x01", + /*39 CONNECT_ACTIVE_RESP */ (unsigned char *) "\x03\x01", + /*3a DISCONNECT_RESP */ (unsigned char *) "\x03\x01", + /*3b */ 0, + /*3c MANUFACTURER_IND */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01", + /*3d */ 0, + /*3e INFO_RESP */ (unsigned char *) "\x03\x01", + /*3f FACILITY_RESP */ (unsigned char *) "\x03\x1f\x01", + /*40 */ 0, + /*41 CONNECT_B3_RESP */ (unsigned char *) "\x03\x2e\x2b\x01", + /*42 CONNECT_B3_ACTIVE_RESP */ (unsigned char *) "\x03\x01", + /*43 DISCONNECT_B3_RESP */ (unsigned char *) "\x03\x01", + /*44 */ 0, + /*45 DATA_B3_RESP */ (unsigned char *) "\x03\x19\x01", + /*46 RESET_B3_RESP */ (unsigned char *) "\x03\x01", + /*47 CONNECT_B3_T90_ACTIVE_RESP */ (unsigned char *) "\x03\x01", + /*48 */ 0, + /*49 */ 0, + /*4a */ 0, + /*4b */ 0, + /*4c */ 0, + /*4d */ 0, + /*4e MANUFACTURER_RESP */ (unsigned char *) "\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[] = +{ + 0, + "ALERT_REQ", + "CONNECT_REQ", + 0, + "DISCONNECT_REQ", + "LISTEN_REQ", + 0, + 0, + "INFO_REQ", + "FACILITY_REQ", + "SELECT_B_PROTOCOL_REQ", + "CONNECT_B3_REQ", + 0, + "DISCONNECT_B3_REQ", + 0, + "DATA_B3_REQ", + "RESET_B3_REQ", + 0, + 0, + "ALERT_CONF", + "CONNECT_CONF", + 0, + "DISCONNECT_CONF", + "LISTEN_CONF", + "MANUFACTURER_REQ", + 0, + "INFO_CONF", + "FACILITY_CONF", + "SELECT_B_PROTOCOL_CONF", + "CONNECT_B3_CONF", + 0, + "DISCONNECT_B3_CONF", + 0, + "DATA_B3_CONF", + "RESET_B3_CONF", + 0, + 0, + 0, + "CONNECT_IND", + "CONNECT_ACTIVE_IND", + "DISCONNECT_IND", + 0, + "MANUFACTURER_CONF", + 0, + "INFO_IND", + "FACILITY_IND", + 0, + "CONNECT_B3_IND", + "CONNECT_B3_ACTIVE_IND", + "DISCONNECT_B3_IND", + 0, + "DATA_B3_IND", + "RESET_B3_IND", + "CONNECT_B3_T90_ACTIVE_IND", + 0, + 0, + "CONNECT_RESP", + "CONNECT_ACTIVE_RESP", + "DISCONNECT_RESP", + 0, + "MANUFACTURER_IND", + 0, + "INFO_RESP", + "FACILITY_RESP", + 0, + "CONNECT_B3_RESP", + "CONNECT_B3_ACTIVE_RESP", + "DISCONNECT_B3_RESP", + 0, + "DATA_B3_RESP", + "RESET_B3_RESP", + "CONNECT_B3_T90_ACTIVE_RESP", + 0, + 0, + 0, + 0, + 0, + 0, + "MANUFACTURER_RESP" +}; + +char *capi_cmd2str(__u8 cmd, __u8 subcmd) +{ + return mnames[command_2_index(cmd, subcmd)]; +} + + +/*-------------------------------------------------------*/ +/*-------------------------------------------------------*/ + +static char *pnames[] = +{ + /*00 */ 0, + /*01 */ 0, + /*02 */ 0, + /*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 */ "Data", + /*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 = 0; + +#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: + if (strcmp(NAME, "Data") == 0) { + bufprint("%-*s = ", slen, NAME); + printstructlen((__u8 *) * (__u32 *) (cmsg->m + cmsg->l), + *(__u16 *) (cmsg->m + cmsg->l + sizeof(__u32))); + bufprint("\n"); + } else + 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; +} + + +#ifdef HAS_NEW_SYMTAB +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); +#else +static struct symbol_table capifunc_syms = +{ +#include <linux/symtab_begin.h> + X(capi_cmsg2message), + X(capi_message2cmsg), + X(capi_cmsg_header), + X(capi_cmd2str), + X(capi_cmsg2str), + X(capi_message2str), + X(capi_info2str), +#include <linux/symtab_end.h> +}; +#endif + +#ifdef MODULE + +int init_module(void) +{ +#ifndef HAS_NEW_SYMTAB + register_symtab(&capifunc_syms); +#endif + return 0; +} + +void cleanup_module(void) +{ +} + +#endif diff --git a/drivers/isdn/avmb1/capiutil.h b/drivers/isdn/avmb1/capiutil.h new file mode 100644 index 000000000..38209212d --- /dev/null +++ b/drivers/isdn/avmb1/capiutil.h @@ -0,0 +1,501 @@ +/* + * $Id: capiutil.h,v 1.2 1997/05/18 09:24:19 calle Exp $ + * + * CAPI 2.0 defines & types + * + * From CAPI 2.0 Development Kit AVM 1995 (capi20.h) + * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capiutil.h,v $ + * Revision 1.2 1997/05/18 09:24:19 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.1 1997/03/04 21:50:35 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + * + */ +#ifndef __CAPIUTIL_H__ +#define __CAPIUTIL_H__ + +#include <asm/types.h> + +#define CAPIMSG_LEN(m) (m[0] | (m[1] << 8)) +#define CAPIMSG_APPID(m) (m[2] | (m[3] << 8)) +#define CAPIMSG_COMMAND(m) (m[4]) +#define CAPIMSG_SUBCOMMAND(m) (m[5]) +#define CAPIMSG_MSGID(m) (m[6] | (m[7] << 8)) +#define CAPIMSG_CONTROLLER(m) (m[8] & 0x7f) +#define CAPIMSG_CONTROL(m) (m[8]|(m[9]<<8)|(m[10]<<16)|(m[11]<<24)) +#define CAPIMSG_NCCI(m) CAPIMSG_CONTROL(m) +#define CAPIMSG_DATA(m) (m[12]|(m[13]<<8)|(m[14]<<16)|(m[15]<<24)) +#define CAPIMSG_DATALEN(m) (m[16] | (m[17]<<8)) + +#define CAPIMSG_SETAPPID(m, applid) \ + do { \ + ((__u8 *)m)[2] = (__u16)(applid) & 0xff; \ + ((__u8 *)m)[3] = ((__u16)(applid) >> 8) & 0xff; \ + } while (0) + +#define CAPIMSG_SETDATA(m, data) \ + do { \ + ((__u8 *)m)[12] = (__u32)(data) & 0xff; \ + ((__u8 *)m)[13] = ((__u32)(data) >> 8) & 0xff; \ + ((__u8 *)m)[14] = ((__u32)(data) >> 16) & 0xff; \ + ((__u8 *)m)[15] = ((__u32)(data) >> 24) & 0xff; \ + } while (0) + +/*----- basic-type definitions -----*/ + +typedef __u8 *_cstruct; + +typedef enum { + CAPI_COMPOSE, + CAPI_DEFAULT +} _cmstruct; + +/* + The _cmsg structure contains all possible CAPI 2.0 parameter. + All parameters are stored here first. The function CAPI_CMSG_2_MESSAGE + assembles the parameter and builds CAPI2.0 conform messages. + CAPI_MESSAGE_2_CMSG disassembles CAPI 2.0 messages and stores the + parameter in the _cmsg structure + */ + +typedef struct { + /* Header */ + __u16 ApplId; + __u8 Command; + __u8 Subcommand; + __u16 Messagenumber; + + /* Parameter */ + union { + __u32 adrController; + __u32 adrPLCI; + __u32 adrNCCI; + } adr; + + _cmstruct AdditionalInfo; + _cstruct B1configuration; + __u16 B1protocol; + _cstruct B2configuration; + __u16 B2protocol; + _cstruct B3configuration; + __u16 B3protocol; + _cstruct BC; + _cstruct BChannelinformation; + _cmstruct BProtocol; + _cstruct CalledPartyNumber; + _cstruct CalledPartySubaddress; + _cstruct CallingPartyNumber; + _cstruct CallingPartySubaddress; + __u32 CIPmask; + __u32 CIPmask2; + __u16 CIPValue; + __u32 Class; + _cstruct ConnectedNumber; + _cstruct ConnectedSubaddress; + __u32 Data; + __u16 DataHandle; + __u16 DataLength; + _cstruct FacilityConfirmationParameter; + _cstruct Facilitydataarray; + _cstruct FacilityIndicationParameter; + _cstruct FacilityRequestParameter; + __u16 FacilitySelector; + __u16 Flags; + __u32 Function; + _cstruct HLC; + __u16 Info; + _cstruct InfoElement; + __u32 InfoMask; + __u16 InfoNumber; + _cstruct Keypadfacility; + _cstruct LLC; + _cstruct ManuData; + __u32 ManuID; + _cstruct NCPI; + __u16 Reason; + __u16 Reason_B3; + __u16 Reject; + _cstruct Useruserdata; + + /* intern */ + unsigned l, p; + unsigned char *par; + __u8 *m; + + /* buffer to construct message */ + __u8 buf[180]; + +} _cmsg; + +/* + * capi_cmsg2message() assembles the parameter from _cmsg to a CAPI 2.0 + * conform message + */ +unsigned capi_cmsg2message(_cmsg * cmsg, __u8 * msg); + +/* + * capi_message2cmsg disassembles a CAPI message an writes the parameter + * into _cmsg for easy access + */ +unsigned capi_message2cmsg(_cmsg * cmsg, __u8 * msg); + +/* + * capi_cmsg_header() fills the _cmsg structure with default values, so only + * parameter with non default values must be changed before sending the + * message. + */ +unsigned capi_cmsg_header(_cmsg * cmsg, __u16 _ApplId, + __u8 _Command, __u8 _Subcommand, + __u16 _Messagenumber, __u32 _Controller); + +/* + * capi_info2str generated a readable string for Capi2.0 reasons. + */ +char *capi_info2str(__u16 reason); + +/*-----------------------------------------------------------------------*/ + +/* + * Debugging / Tracing functions + */ +char *capi_cmd2str(__u8 cmd, __u8 subcmd); +char *capi_cmsg2str(_cmsg * cmsg); +char *capi_message2str(__u8 * msg); + +/*-----------------------------------------------------------------------*/ + +static inline void capi_cmsg_answer(_cmsg * cmsg) +{ + cmsg->Subcommand |= 0x01; +} + +/*-----------------------------------------------------------------------*/ + +static inline void capi_fill_CONNECT_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct NCPI) +{ + capi_cmsg_header(cmsg, ApplId, 0x82, 0x80, Messagenumber, adr); + cmsg->NCPI = NCPI; +} + +static inline void capi_fill_FACILITY_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 FacilitySelector, + _cstruct FacilityRequestParameter) +{ + capi_cmsg_header(cmsg, ApplId, 0x80, 0x80, Messagenumber, adr); + cmsg->FacilitySelector = FacilitySelector; + cmsg->FacilityRequestParameter = FacilityRequestParameter; +} + +static inline void capi_fill_INFO_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct CalledPartyNumber, + _cstruct BChannelinformation, + _cstruct Keypadfacility, + _cstruct Useruserdata, + _cstruct Facilitydataarray) +{ + capi_cmsg_header(cmsg, ApplId, 0x08, 0x80, Messagenumber, adr); + cmsg->CalledPartyNumber = CalledPartyNumber; + cmsg->BChannelinformation = BChannelinformation; + cmsg->Keypadfacility = Keypadfacility; + cmsg->Useruserdata = Useruserdata; + cmsg->Facilitydataarray = Facilitydataarray; +} + +static inline void capi_fill_LISTEN_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u32 InfoMask, + __u32 CIPmask, + __u32 CIPmask2, + _cstruct CallingPartyNumber, + _cstruct CallingPartySubaddress) +{ + capi_cmsg_header(cmsg, ApplId, 0x05, 0x80, Messagenumber, adr); + cmsg->InfoMask = InfoMask; + cmsg->CIPmask = CIPmask; + cmsg->CIPmask2 = CIPmask2; + cmsg->CallingPartyNumber = CallingPartyNumber; + cmsg->CallingPartySubaddress = CallingPartySubaddress; +} + +static inline void capi_fill_ALERT_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct BChannelinformation, + _cstruct Keypadfacility, + _cstruct Useruserdata, + _cstruct Facilitydataarray) +{ + capi_cmsg_header(cmsg, ApplId, 0x01, 0x80, Messagenumber, adr); + cmsg->BChannelinformation = BChannelinformation; + cmsg->Keypadfacility = Keypadfacility; + cmsg->Useruserdata = Useruserdata; + cmsg->Facilitydataarray = Facilitydataarray; +} + +static inline void capi_fill_CONNECT_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 CIPValue, + _cstruct CalledPartyNumber, + _cstruct CallingPartyNumber, + _cstruct CalledPartySubaddress, + _cstruct CallingPartySubaddress, + __u16 B1protocol, + __u16 B2protocol, + __u16 B3protocol, + _cstruct B1configuration, + _cstruct B2configuration, + _cstruct B3configuration, + _cstruct BC, + _cstruct LLC, + _cstruct HLC, + _cstruct BChannelinformation, + _cstruct Keypadfacility, + _cstruct Useruserdata, + _cstruct Facilitydataarray) +{ + + capi_cmsg_header(cmsg, ApplId, 0x02, 0x80, Messagenumber, adr); + cmsg->CIPValue = CIPValue; + cmsg->CalledPartyNumber = CalledPartyNumber; + cmsg->CallingPartyNumber = CallingPartyNumber; + cmsg->CalledPartySubaddress = CalledPartySubaddress; + cmsg->CallingPartySubaddress = CallingPartySubaddress; + cmsg->B1protocol = B1protocol; + cmsg->B2protocol = B2protocol; + cmsg->B3protocol = B3protocol; + cmsg->B1configuration = B1configuration; + cmsg->B2configuration = B2configuration; + cmsg->B3configuration = B3configuration; + cmsg->BC = BC; + cmsg->LLC = LLC; + cmsg->HLC = HLC; + cmsg->BChannelinformation = BChannelinformation; + cmsg->Keypadfacility = Keypadfacility; + cmsg->Useruserdata = Useruserdata; + cmsg->Facilitydataarray = Facilitydataarray; +} + +static inline void capi_fill_DATA_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u32 Data, + __u16 DataLength, + __u16 DataHandle, + __u16 Flags) +{ + + capi_cmsg_header(cmsg, ApplId, 0x86, 0x80, Messagenumber, adr); + cmsg->Data = Data; + cmsg->DataLength = DataLength; + cmsg->DataHandle = DataHandle; + cmsg->Flags = Flags; +} + +static inline void capi_fill_DISCONNECT_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct BChannelinformation, + _cstruct Keypadfacility, + _cstruct Useruserdata, + _cstruct Facilitydataarray) +{ + + capi_cmsg_header(cmsg, ApplId, 0x04, 0x80, Messagenumber, adr); + cmsg->BChannelinformation = BChannelinformation; + cmsg->Keypadfacility = Keypadfacility; + cmsg->Useruserdata = Useruserdata; + cmsg->Facilitydataarray = Facilitydataarray; +} + +static inline void capi_fill_DISCONNECT_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct NCPI) +{ + + capi_cmsg_header(cmsg, ApplId, 0x84, 0x80, Messagenumber, adr); + cmsg->NCPI = NCPI; +} + +static inline void capi_fill_MANUFACTURER_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u32 ManuID, + __u32 Class, + __u32 Function, + _cstruct ManuData) +{ + + capi_cmsg_header(cmsg, ApplId, 0xff, 0x80, Messagenumber, adr); + cmsg->ManuID = ManuID; + cmsg->Class = Class; + cmsg->Function = Function; + cmsg->ManuData = ManuData; +} + +static inline void capi_fill_RESET_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct NCPI) +{ + + capi_cmsg_header(cmsg, ApplId, 0x87, 0x80, Messagenumber, adr); + cmsg->NCPI = NCPI; +} + +static inline void capi_fill_SELECT_B_PROTOCOL_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 B1protocol, + __u16 B2protocol, + __u16 B3protocol, + _cstruct B1configuration, + _cstruct B2configuration, + _cstruct B3configuration) +{ + + capi_cmsg_header(cmsg, ApplId, 0x41, 0x80, Messagenumber, adr); + cmsg->B1protocol = B1protocol; + cmsg->B2protocol = B2protocol; + cmsg->B3protocol = B3protocol; + cmsg->B1configuration = B1configuration; + cmsg->B2configuration = B2configuration; + cmsg->B3configuration = B3configuration; +} + +static inline void capi_fill_CONNECT_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 Reject, + __u16 B1protocol, + __u16 B2protocol, + __u16 B3protocol, + _cstruct B1configuration, + _cstruct B2configuration, + _cstruct B3configuration, + _cstruct ConnectedNumber, + _cstruct ConnectedSubaddress, + _cstruct LLC, + _cstruct BChannelinformation, + _cstruct Keypadfacility, + _cstruct Useruserdata, + _cstruct Facilitydataarray) +{ + capi_cmsg_header(cmsg, ApplId, 0x02, 0x83, Messagenumber, adr); + cmsg->Reject = Reject; + cmsg->B1protocol = B1protocol; + cmsg->B2protocol = B2protocol; + cmsg->B3protocol = B3protocol; + cmsg->B1configuration = B1configuration; + cmsg->B2configuration = B2configuration; + cmsg->B3configuration = B3configuration; + cmsg->ConnectedNumber = ConnectedNumber; + cmsg->ConnectedSubaddress = ConnectedSubaddress; + cmsg->LLC = LLC; + cmsg->BChannelinformation = BChannelinformation; + cmsg->Keypadfacility = Keypadfacility; + cmsg->Useruserdata = Useruserdata; + cmsg->Facilitydataarray = Facilitydataarray; +} + +static inline void capi_fill_CONNECT_ACTIVE_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x03, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_CONNECT_B3_ACTIVE_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x83, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_CONNECT_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 Reject, + _cstruct NCPI) +{ + capi_cmsg_header(cmsg, ApplId, 0x82, 0x83, Messagenumber, adr); + cmsg->Reject = Reject; + cmsg->NCPI = NCPI; +} + +static inline void capi_fill_CONNECT_B3_T90_ACTIVE_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x88, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_DATA_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 DataHandle) +{ + + capi_cmsg_header(cmsg, ApplId, 0x86, 0x83, Messagenumber, adr); + cmsg->DataHandle = DataHandle; +} + +static inline void capi_fill_DISCONNECT_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x84, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_DISCONNECT_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x04, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_FACILITY_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 FacilitySelector) +{ + + capi_cmsg_header(cmsg, ApplId, 0x80, 0x83, Messagenumber, adr); + cmsg->FacilitySelector = FacilitySelector; +} + +static inline void capi_fill_INFO_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x08, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_MANUFACTURER_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u32 ManuID, + __u32 Class, + __u32 Function, + _cstruct ManuData) +{ + + capi_cmsg_header(cmsg, ApplId, 0xff, 0x83, Messagenumber, adr); + cmsg->ManuID = ManuID; + cmsg->Class = Class; + cmsg->Function = Function; + cmsg->ManuData = ManuData; +} + +static inline void capi_fill_RESET_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x87, 0x83, Messagenumber, adr); +} + +#endif /* __CAPIUTIL_H__ */ diff --git a/drivers/isdn/avmb1/compat.h b/drivers/isdn/avmb1/compat.h new file mode 100644 index 000000000..551b20d60 --- /dev/null +++ b/drivers/isdn/avmb1/compat.h @@ -0,0 +1,30 @@ +/* + * $Id: compat.h,v 1.1 1997/03/04 21:50:36 calle Exp $ + * + * Headerfile for Compartibility between different kernel versions + * + * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: compat.h,v $ + * Revision 1.1 1997/03/04 21:50:36 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + * + */ +#ifndef __COMPAT_H__ +#define __COMPAT_H__ + +#include <linux/version.h> +#include <linux/isdnif.h> + +#if LINUX_VERSION_CODE >= 0x020112 /* 2.1.18 */ +#define HAS_NEW_SYMTAB +#endif + +#endif /* __COMPAT_H__ */ |