summaryrefslogtreecommitdiffstats
path: root/drivers/isdn/avmb1/capidrv.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/isdn/avmb1/capidrv.c')
-rw-r--r--drivers/isdn/avmb1/capidrv.c331
1 files changed, 295 insertions, 36 deletions
diff --git a/drivers/isdn/avmb1/capidrv.c b/drivers/isdn/avmb1/capidrv.c
index c1dfcdafe..1d4a7c2e8 100644
--- a/drivers/isdn/avmb1/capidrv.c
+++ b/drivers/isdn/avmb1/capidrv.c
@@ -1,11 +1,42 @@
/*
- * $Id: capidrv.c,v 1.3 1997/05/18 09:24:15 calle Exp $
+ * $Id: capidrv.c,v 1.11 1998/02/13 07:09: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.11 1998/02/13 07:09:15 calle
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.10 1998/02/02 19:52:23 calle
+ * Fixed vbox (audio) acceptb.
+ *
+ * Revision 1.9 1998/01/31 11:14:45 calle
+ * merged changes to 2.0 tree, prepare 2.1.82 to work.
+ *
+ * Revision 1.8 1997/11/04 06:12:09 calle
+ * capi.c: new read/write in file_ops since 2.1.60
+ * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware.
+ * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON)
+ * compat.h: added #define LinuxVersionCode
+ *
+ * Revision 1.7 1997/10/11 10:36:34 calle
+ * Added isdnlog support. patch to isdnlog needed.
+ *
+ * Revision 1.6 1997/10/11 10:25:55 calle
+ * New interface for lowlevel drivers. BSENT with nr. of bytes sent,
+ * allow sending without ACK.
+ *
+ * Revision 1.5 1997/10/01 09:21:16 fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.4 1997/07/13 12:22:43 calle
+ * bug fix for more than one controller in connect_req.
+ * debugoutput now with contrnr.
+ *
* Revision 1.3 1997/05/18 09:24:15 calle
* added verbose disconnect reason reporting to avmb1.
* some fixes in capi20 interface.
@@ -48,13 +79,11 @@
#include "capicmd.h"
#include "capidrv.h"
-static char *revision = "$Revision: 1.3 $";
+static char *revision = "$Revision: 1.11 $";
int debugmode = 0;
-#ifdef HAS_NEW_SYMTAB
MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
MODULE_PARM(debugmode, "i");
-#endif
/* -------- type definitions ----------------------------------------- */
@@ -116,14 +145,26 @@ struct capidrv_contr {
int oldstate;
/* */
__u16 datahandle;
+ struct ncci_datahandle_queue {
+ struct ncci_datahandle_queue *next;
+ __u16 datahandle;
+ int len;
+ } *ackqueue;
} *ncci_list;
} *plcip;
struct capidrv_ncci *nccip;
} *bchans;
struct capidrv_plci *plci_list;
+
+ /* for q931 data */
+ __u8 q931_buf[4096];
+ __u8 *q931_read;
+ __u8 *q931_write;
+ __u8 *q931_end;
};
+
struct capidrv_data {
__u16 appid;
int ncontr;
@@ -141,6 +182,9 @@ typedef struct capidrv_bchan capidrv_bchan;
static capidrv_data global;
static struct capi_interface *capifuncs;
+static void handle_dtrace_data(capidrv_contr *card,
+ int send, int level2, __u8 *data, __u16 len);
+
/* -------- convert functions ---------------------------------------- */
static inline __u32 b1prot(int l2, int l3)
@@ -167,9 +211,8 @@ static inline __u32 b2prot(int l2, int l3)
default:
return 0;
case ISDN_PROTO_L2_HDLC:
- return 1;
case ISDN_PROTO_L2_TRANS:
- return 0;
+ return 1;
}
}
@@ -410,6 +453,42 @@ static void free_ncci(capidrv_contr * card, struct capidrv_ncci *nccip)
kfree(nccip);
}
+static int capidrv_add_ack(struct capidrv_ncci *nccip,
+ __u16 datahandle, int len)
+{
+ struct ncci_datahandle_queue *n, **pp;
+
+ n = (struct ncci_datahandle_queue *)
+ kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC);
+ if (!n) {
+ printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n");
+ return -1;
+ }
+ n->next = 0;
+ n->datahandle = datahandle;
+ n->len = len;
+ for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) ;
+ *pp = n;
+ return 0;
+}
+
+static int capidrv_del_ack(struct capidrv_ncci *nccip, __u16 datahandle)
+{
+ struct ncci_datahandle_queue **pp, *p;
+ int len;
+
+ for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) {
+ if ((*pp)->datahandle == datahandle) {
+ p = *pp;
+ len = p->len;
+ *pp = (*pp)->next;
+ kfree(p);
+ return len;
+ }
+ }
+ return -1;
+}
+
/* -------- convert and send capi message ---------------------------- */
static void send_message(capidrv_contr * card, _cmsg * cmsg)
@@ -419,7 +498,6 @@ static void send_message(capidrv_contr * card, _cmsg * cmsg)
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);
}
@@ -686,8 +764,53 @@ static void handle_controller(_cmsg * cmsg)
break;
case CAPI_MANUFACTURER_IND: /* Controller */
+ if ( cmsg->ManuID == 0x214D5641
+ && cmsg->Class == 0
+ && cmsg->Function == 1) {
+ __u8 *data = cmsg->ManuData+3;
+ __u16 len = cmsg->ManuData[0];
+ __u16 layer;
+ int direction;
+ if (len == 255) {
+ len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8));
+ data += 2;
+ }
+ len -= 2;
+ layer = ((*(data-1)) << 8) | *(data-2);
+ if (layer & 0x300)
+ direction = (layer & 0x200) ? 0 : 1;
+ else direction = (layer & 0x800) ? 0 : 1;
+ if (layer & 0x0C00) {
+ if ((layer & 0xff) == 0x80) {
+ handle_dtrace_data(card, direction, 1, data, len);
+ break;
+ }
+ } else if ((layer & 0xff) < 0x80) {
+ handle_dtrace_data(card, direction, 0, data, len);
+ break;
+ }
+ printk(KERN_INFO "capidrv: %s from controller 0x%x layer 0x%x, ignored\n",
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController, layer);
+ break;
+ }
goto ignored;
case CAPI_MANUFACTURER_CONF: /* Controller */
+ if (cmsg->ManuID == 0x214D5641) {
+ char *s = 0;
+ switch (cmsg->Class) {
+ case 0: break;
+ case 1: s = "unknown class"; break;
+ case 2: s = "unknown function"; break;
+ default: s = "unkown error"; break;
+ }
+ if (s)
+ printk(KERN_INFO "capidrv: %s from controller 0x%x function %d: %s\n",
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController,
+ cmsg->Function, s);
+ break;
+ }
goto ignored;
case CAPI_FACILITY_IND: /* Controller/plci/ncci */
goto ignored;
@@ -996,6 +1119,7 @@ static void handle_ncci(_cmsg * cmsg)
capidrv_plci *plcip;
capidrv_ncci *nccip;
isdn_ctrl cmd;
+ int len;
if (!card) {
printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
@@ -1093,11 +1217,14 @@ static void handle_ncci(_cmsg * cmsg)
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);
-
+ len = capidrv_del_ack(nccip, cmsg->DataHandle);
+ if (len < 0)
+ break;
+ cmd.command = ISDN_STAT_BSENT;
+ cmd.driver = card->myid;
+ cmd.arg = nccip->chan;
+ cmd.parm.length = len;
+ card->interface.statcallb(&cmd);
break;
case CAPI_DISCONNECT_B3_IND: /* ncci */
@@ -1206,6 +1333,60 @@ static void capidrv_signal(__u16 applid, __u32 dummy)
/* ------------------------------------------------------------------- */
+#define PUTBYTE_TO_STATUS(card, byte) \
+ do { \
+ *(card)->q931_write++ = (byte); \
+ if ((card)->q931_write > (card)->q931_end) \
+ (card)->q931_write = (card)->q931_buf; \
+ } while (0)
+
+static void handle_dtrace_data(capidrv_contr *card,
+ int send, int level2, __u8 *data, __u16 len)
+{
+ long flags;
+ __u8 *p, *end;
+ isdn_ctrl cmd;
+
+ if (!len) {
+ printk(KERN_DEBUG "avmb1_q931_data: len == %d\n", len);
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+
+ if (level2) {
+ PUTBYTE_TO_STATUS(card, 'D');
+ PUTBYTE_TO_STATUS(card, '2');
+ PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+ PUTBYTE_TO_STATUS(card, ':');
+ } else {
+ PUTBYTE_TO_STATUS(card, 'D');
+ PUTBYTE_TO_STATUS(card, '3');
+ PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+ PUTBYTE_TO_STATUS(card, ':');
+ }
+
+ for (p = data, end = data+len; p < end; p++) {
+ __u8 w;
+ PUTBYTE_TO_STATUS(card, ' ');
+ w = (*p >> 4) & 0xf;
+ PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+ w = *p & 0xf;
+ PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+ }
+ PUTBYTE_TO_STATUS(card, '\n');
+
+ restore_flags(flags);
+
+ cmd.command = ISDN_STAT_STAVAIL;
+ cmd.driver = card->myid;
+ cmd.arg = len*3+5;
+ card->interface.statcallb(&cmd);
+}
+
+/* ------------------------------------------------------------------- */
+
static _cmsg cmdcmsg;
static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card)
@@ -1270,7 +1451,7 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card)
capi_fill_CONNECT_REQ(&cmdcmsg,
global.appid,
card->msgid++,
- 1, /* adr */
+ card->contrnr, /* adr */
si2cip(bchan->si1, bchan->si2), /* cipvalue */
called, /* CalledPartyNumber */
calling, /* CallingPartyNumber */
@@ -1471,7 +1652,7 @@ static int if_command(isdn_ctrl * c)
static _cmsg sendcmsg;
-static int if_sendbuf(int id, int channel, struct sk_buff *skb)
+static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb)
{
capidrv_contr *card = findcontrbydriverid(id);
capidrv_bchan *bchan;
@@ -1479,6 +1660,7 @@ static int if_sendbuf(int id, int channel, struct sk_buff *skb)
int len = skb->len;
size_t msglen;
__u16 errcode;
+ __u16 datahandle;
if (!card) {
printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n",
@@ -1492,51 +1674,128 @@ static int if_sendbuf(int id, int channel, struct sk_buff *skb)
card->name, channel);
return 0;
}
+ datahandle = nccip->datahandle;
capi_fill_DATA_B3_REQ(&sendcmsg, global.appid, card->msgid++,
nccip->ncci, /* adr */
(__u32) skb->data, /* Data */
skb->len, /* DataLength */
- nccip->datahandle++, /* DataHandle */
+ datahandle, /* DataHandle */
0 /* Flags */
);
+
+ if (capidrv_add_ack(nccip, datahandle, doack ? skb->len : -1) < 0)
+ return 0;
+
capi_cmsg2message(&sendcmsg, sendcmsg.buf);
msglen = CAPIMSG_LEN(sendcmsg.buf);
if (skb_headroom(skb) < msglen) {
struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len);
if (!nskb) {
printk(KERN_ERR "capidrv: if_sendbuf: no memory\n");
+ (void)capidrv_del_ack(nccip, datahandle);
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:
+ if (errcode == CAPI_NOERROR) {
dev_kfree_skb(skb);
+ nccip->datahandle++;
return len;
- case CAPI_SENDQUEUEFULL:
- dev_kfree_skb(nskb);
- return 0;
- default:
- return -1;
}
+ (void)capidrv_del_ack(nccip, datahandle);
+ dev_kfree_skb(nskb);
+ return errcode == CAPI_SENDQUEUEFULL ? 0 : -1;
} else {
memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen);
errcode = (*capifuncs->capi_put_message) (global.appid, skb);
- switch (errcode) {
- case CAPI_NOERROR:
+ if (errcode == CAPI_NOERROR) {
+ nccip->datahandle++;
return len;
- case CAPI_SENDQUEUEFULL:
- return 0;
- default:
- return -1;
}
+ (void)capidrv_del_ack(nccip, datahandle);
+ return errcode == CAPI_SENDQUEUEFULL ? 0 : -1;
+ }
+}
+
+static int if_readstat(__u8 *buf, int len, int user, int id, int channel)
+{
+ capidrv_contr *card = findcontrbydriverid(id);
+ int count;
+ __u8 *p;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n",
+ id);
+ return -ENODEV;
+ }
+
+ for (p=buf, count=0; count < len; p++, count++) {
+ if (user)
+ put_user(*card->q931_read++, p);
+ else
+ *p = *card->q931_read++;
+ if (card->q931_read > card->q931_end)
+ card->q931_read = card->q931_buf;
+ }
+ return count;
+
+}
+
+static void enable_dchannel_trace(capidrv_contr *card)
+{
+ __u8 manufacturer[CAPI_MANUFACTURER_LEN];
+ capi_version version;
+ __u16 contr = card->contrnr;
+ __u16 errcode;
+ __u16 avmversion[3];
+
+ errcode = (*capifuncs->capi_get_manufacturer)(contr, manufacturer);
+ if (errcode != CAPI_NOERROR) {
+ printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n",
+ card->name, errcode);
+ return;
+ }
+ if (strstr(manufacturer, "AVM") == 0) {
+ printk(KERN_ERR "%s: not from AVM, no d-channel trace possible\n",
+ card->name);
+ return;
+ }
+ errcode = (*capifuncs->capi_get_version)(contr, &version);
+ if (errcode != CAPI_NOERROR) {
+ printk(KERN_ERR "%s: can't get version (0x%x)\n",
+ card->name, errcode);
+ return;
+ }
+ avmversion[0] = (version.majormanuversion >> 4) & 0x0f;
+ avmversion[1] = (version.majormanuversion << 4) & 0xf0;
+ avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
+ avmversion[2] |= version.minormanuversion & 0x0f;
+
+ if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
+ printk(KERN_INFO "%s: D2 trace enabled\n", card->name);
+ capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid,
+ card->msgid++,
+ contr,
+ 0x214D5641, /* ManuID */
+ 0, /* Class */
+ 1, /* Function */
+ (_cstruct)"\004\200\014\000\000");
+ } else {
+ printk(KERN_INFO "%s: D3 trace enabled\n", card->name);
+ capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid,
+ card->msgid++,
+ contr,
+ 0x214D5641, /* ManuID */
+ 0, /* Class */
+ 1, /* Function */
+ (_cstruct)"\004\002\003\000\000");
}
+ send_message(card, &cmdcmsg);
}
static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
@@ -1568,7 +1827,7 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
card->interface.command = if_command;
card->interface.writebuf_skb = if_sendbuf;
card->interface.writecmd = 0;
- card->interface.readstat = 0;
+ card->interface.readstat = if_readstat;
card->interface.features = ISDN_FEATURE_L2_X75I |
ISDN_FEATURE_L2_X75UI |
ISDN_FEATURE_L2_X75BUI |
@@ -1581,6 +1840,9 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
card->next = global.contr_list;
global.contr_list = card;
global.ncontr++;
+ card->q931_read = card->q931_buf;
+ card->q931_write = card->q931_buf;
+ card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1;
if (!register_isdn(&card->interface)) {
global.contr_list = global.contr_list->next;
@@ -1600,7 +1862,7 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
cmd.command = ISDN_STAT_RUN;
card->interface.statcallb(&cmd);
- card->cipmask = 1; /* any */
+ card->cipmask = 0x1FFF03FF; /* any */
card->cipmask2 = 0;
capi_fill_LISTEN_REQ(&cmdcmsg, global.appid,
@@ -1616,6 +1878,8 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
printk(KERN_INFO "%s: now up (%d B channels)\n",
card->name, card->nbchan);
+ enable_dchannel_trace(card);
+
return 0;
}
@@ -1694,11 +1958,6 @@ int capidrv_init(void)
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, '$');