diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/pcbit |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/isdn/pcbit')
-rw-r--r-- | drivers/isdn/pcbit/Kconfig | 14 | ||||
-rw-r--r-- | drivers/isdn/pcbit/Makefile | 9 | ||||
-rw-r--r-- | drivers/isdn/pcbit/callbacks.c | 367 | ||||
-rw-r--r-- | drivers/isdn/pcbit/callbacks.h | 49 | ||||
-rw-r--r-- | drivers/isdn/pcbit/capi.c | 663 | ||||
-rw-r--r-- | drivers/isdn/pcbit/capi.h | 88 | ||||
-rw-r--r-- | drivers/isdn/pcbit/drv.c | 1088 | ||||
-rw-r--r-- | drivers/isdn/pcbit/edss1.c | 325 | ||||
-rw-r--r-- | drivers/isdn/pcbit/edss1.h | 99 | ||||
-rw-r--r-- | drivers/isdn/pcbit/layer2.c | 732 | ||||
-rw-r--r-- | drivers/isdn/pcbit/layer2.h | 288 | ||||
-rw-r--r-- | drivers/isdn/pcbit/module.c | 130 | ||||
-rw-r--r-- | drivers/isdn/pcbit/pcbit.h | 169 |
13 files changed, 4021 insertions, 0 deletions
diff --git a/drivers/isdn/pcbit/Kconfig b/drivers/isdn/pcbit/Kconfig new file mode 100644 index 00000000000..f06997faef1 --- /dev/null +++ b/drivers/isdn/pcbit/Kconfig @@ -0,0 +1,14 @@ +# +# Config.in for PCBIT ISDN driver +# +config ISDN_DRV_PCBIT + tristate "PCBIT-D support" + depends on ISDN_I4L && ISA && (BROKEN || !PPC) + help + This enables support for the PCBIT ISDN-card. This card is + manufactured in Portugal by Octal. For running this card, + additional firmware is necessary, which has to be downloaded into + the card using a utility which is distributed separately. See + <file:Documentation/isdn/README> and + <file:Documentation/isdn/README.pcbit> for more information. + diff --git a/drivers/isdn/pcbit/Makefile b/drivers/isdn/pcbit/Makefile new file mode 100644 index 00000000000..2d026c3242e --- /dev/null +++ b/drivers/isdn/pcbit/Makefile @@ -0,0 +1,9 @@ +# Makefile for the pcbit ISDN device driver + +# Each configuration option enables a list of files. + +obj-$(CONFIG_ISDN_DRV_PCBIT) += pcbit.o + +# Multipart objects. + +pcbit-y := module.o edss1.o drv.o layer2.o capi.o callbacks.o diff --git a/drivers/isdn/pcbit/callbacks.c b/drivers/isdn/pcbit/callbacks.c new file mode 100644 index 00000000000..692ec72d1ee --- /dev/null +++ b/drivers/isdn/pcbit/callbacks.c @@ -0,0 +1,367 @@ +/* + * Callbacks for the FSM + * + * Copyright (C) 1996 Universidade de Lisboa + * + * Written by Pedro Roque Marques (roque@di.fc.ul.pt) + * + * This software may be used and distributed according to the terms of + * the GNU General Public License, incorporated herein by reference. + */ + +/* + * Fix: 19981230 - Carlos Morgado <chbm@techie.com> + * Port of Nelson Escravana's <nelson.escravana@usa.net> fix to CalledPN + * NULL pointer dereference in cb_in_1 (originally fixed in 2.0) + */ + +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/kernel.h> + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/skbuff.h> + +#include <asm/io.h> + +#include <linux/isdnif.h> + +#include "pcbit.h" +#include "layer2.h" +#include "edss1.h" +#include "callbacks.h" +#include "capi.h" + +ushort last_ref_num = 1; + +/* + * send_conn_req + * + */ + +void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *cbdata) +{ + struct sk_buff *skb; + int len; + ushort refnum; + + +#ifdef DEBUG + printk(KERN_DEBUG "Called Party Number: %s\n", + cbdata->data.setup.CalledPN); +#endif + /* + * hdr - kmalloc in capi_conn_req + * - kfree when msg has been sent + */ + + if ((len = capi_conn_req(cbdata->data.setup.CalledPN, &skb, + chan->proto)) < 0) + { + printk("capi_conn_req failed\n"); + return; + } + + + refnum = last_ref_num++ & 0x7fffU; + + chan->callref = 0; + chan->layer2link = 0; + chan->snum = 0; + chan->s_refnum = refnum; + + pcbit_l2_write(dev, MSG_CONN_REQ, refnum, skb, len); +} + +/* + * rcv CONNECT + * will go into ACTIVE state + * send CONN_ACTIVE_RESP + * send Select protocol request + */ + +void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data) +{ + isdn_ctrl ictl; + struct sk_buff *skb; + int len; + ushort refnum; + + if ((len=capi_conn_active_resp(chan, &skb)) < 0) + { + printk("capi_conn_active_req failed\n"); + return; + } + + refnum = last_ref_num++ & 0x7fffU; + chan->s_refnum = refnum; + + pcbit_l2_write(dev, MSG_CONN_ACTV_RESP, refnum, skb, len); + + + ictl.command = ISDN_STAT_DCONN; + ictl.driver=dev->id; + ictl.arg=chan->id; + dev->dev_if->statcallb(&ictl); + + /* ACTIVE D-channel */ + + /* Select protocol */ + + if ((len=capi_select_proto_req(chan, &skb, 1 /*outgoing*/)) < 0) { + printk("capi_select_proto_req failed\n"); + return; + } + + refnum = last_ref_num++ & 0x7fffU; + chan->s_refnum = refnum; + + pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len); +} + + +/* + * Disconnect received (actually RELEASE COMPLETE) + * This means we were not able to establish connection with remote + * Inform the big boss above + */ +void cb_out_3(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data) +{ + isdn_ctrl ictl; + + ictl.command = ISDN_STAT_DHUP; + ictl.driver=dev->id; + ictl.arg=chan->id; + dev->dev_if->statcallb(&ictl); +} + + +/* + * Incoming call received + * inform user + */ + +void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *cbdata) +{ + isdn_ctrl ictl; + unsigned short refnum; + struct sk_buff *skb; + int len; + + + ictl.command = ISDN_STAT_ICALL; + ictl.driver=dev->id; + ictl.arg=chan->id; + + /* + * ictl.num >= strlen() + strlen() + 5 + */ + + if (cbdata->data.setup.CallingPN == NULL) { + printk(KERN_DEBUG "NULL CallingPN to phone; using 0\n"); + strcpy(ictl.parm.setup.phone, "0"); + } + else { + strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN); + } + if (cbdata->data.setup.CalledPN == NULL) { + printk(KERN_DEBUG "NULL CalledPN to eazmsn; using 0\n"); + strcpy(ictl.parm.setup.eazmsn, "0"); + } + else { + strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN); + } + ictl.parm.setup.si1 = 7; + ictl.parm.setup.si2 = 0; + ictl.parm.setup.plan = 0; + ictl.parm.setup.screen = 0; + +#ifdef DEBUG + printk(KERN_DEBUG "statstr: %s\n", ictl.num); +#endif + + dev->dev_if->statcallb(&ictl); + + + if ((len=capi_conn_resp(chan, &skb)) < 0) { + printk(KERN_DEBUG "capi_conn_resp failed\n"); + return; + } + + refnum = last_ref_num++ & 0x7fffU; + chan->s_refnum = refnum; + + pcbit_l2_write(dev, MSG_CONN_RESP, refnum, skb, len); +} + +/* + * user has replied + * open the channel + * send CONNECT message CONNECT_ACTIVE_REQ in CAPI + */ + +void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data) +{ + unsigned short refnum; + struct sk_buff *skb; + int len; + + if ((len = capi_conn_active_req(chan, &skb)) < 0) { + printk(KERN_DEBUG "capi_conn_active_req failed\n"); + return; + } + + + refnum = last_ref_num++ & 0x7fffU; + chan->s_refnum = refnum; + + printk(KERN_DEBUG "sending MSG_CONN_ACTV_REQ\n"); + pcbit_l2_write(dev, MSG_CONN_ACTV_REQ, refnum, skb, len); +} + +/* + * CONN_ACK arrived + * start b-proto selection + * + */ + +void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data) +{ + unsigned short refnum; + struct sk_buff *skb; + int len; + + if ((len = capi_select_proto_req(chan, &skb, 0 /*incoming*/)) < 0) + { + printk("capi_select_proto_req failed\n"); + return; + } + + refnum = last_ref_num++ & 0x7fffU; + chan->s_refnum = refnum; + + pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len); + +} + + +/* + * Received disconnect ind on active state + * send disconnect resp + * send msg to user + */ +void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data) +{ + struct sk_buff *skb; + int len; + ushort refnum; + isdn_ctrl ictl; + + if ((len = capi_disc_resp(chan, &skb)) < 0) { + printk("capi_disc_resp failed\n"); + return; + } + + refnum = last_ref_num++ & 0x7fffU; + chan->s_refnum = refnum; + + pcbit_l2_write(dev, MSG_DISC_RESP, refnum, skb, len); + + ictl.command = ISDN_STAT_BHUP; + ictl.driver=dev->id; + ictl.arg=chan->id; + dev->dev_if->statcallb(&ictl); +} + + +/* + * User HANGUP on active/call proceeding state + * send disc.req + */ +void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data) +{ + struct sk_buff *skb; + int len; + ushort refnum; + + if ((len = capi_disc_req(chan->callref, &skb, CAUSE_NORMAL)) < 0) + { + printk("capi_disc_req failed\n"); + return; + } + + refnum = last_ref_num++ & 0x7fffU; + chan->s_refnum = refnum; + + pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb, len); +} + +/* + * Disc confirm received send BHUP + * Problem: when the HL driver sends the disc req itself + * LL receives BHUP + */ +void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data) +{ + isdn_ctrl ictl; + + ictl.command = ISDN_STAT_BHUP; + ictl.driver=dev->id; + ictl.arg=chan->id; + dev->dev_if->statcallb(&ictl); +} + +void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data) +{ +} + +/* + * send activate b-chan protocol + */ +void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data) +{ + struct sk_buff *skb; + int len; + ushort refnum; + + if ((len = capi_activate_transp_req(chan, &skb)) < 0) + { + printk("capi_conn_activate_transp_req failed\n"); + return; + } + + refnum = last_ref_num++ & 0x7fffU; + chan->s_refnum = refnum; + + pcbit_l2_write(dev, MSG_ACT_TRANSP_REQ, refnum, skb, len); +} + +/* + * Inform User that the B-channel is available + */ +void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data) +{ + isdn_ctrl ictl; + + ictl.command = ISDN_STAT_BCONN; + ictl.driver=dev->id; + ictl.arg=chan->id; + dev->dev_if->statcallb(&ictl); +} + + + diff --git a/drivers/isdn/pcbit/callbacks.h b/drivers/isdn/pcbit/callbacks.h new file mode 100644 index 00000000000..f510dc56b57 --- /dev/null +++ b/drivers/isdn/pcbit/callbacks.h @@ -0,0 +1,49 @@ +/* + * Callbacks prototypes for FSM + * + * Copyright (C) 1996 Universidade de Lisboa + * + * Written by Pedro Roque Marques (roque@di.fc.ul.pt) + * + * This software may be used and distributed according to the terms of + * the GNU General Public License, incorporated herein by reference. + */ + +#ifndef CALLBACKS_H +#define CALLBACKS_H + + +extern void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); + +extern void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); + +extern void cb_out_3(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); + +extern void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); +extern void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); +extern void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); + +extern void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); +extern void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); +extern void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); + +extern void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); + +extern void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); +extern void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan, + struct callb_data *data); + +#endif + + diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c new file mode 100644 index 00000000000..29eb03a8c29 --- /dev/null +++ b/drivers/isdn/pcbit/capi.c @@ -0,0 +1,663 @@ +/* + * CAPI encoder/decoder for + * Portugal Telecom CAPI 2.0 + * + * Copyright (C) 1996 Universidade de Lisboa + * + * Written by Pedro Roque Marques (roque@di.fc.ul.pt) + * + * This software may be used and distributed according to the terms of + * the GNU General Public License, incorporated herein by reference. + * + * Not compatible with the AVM Gmbh. CAPI 2.0 + * + */ + +/* + * Documentation: + * - "Common ISDN API - Perfil Português - Versão 2.1", + * Telecom Portugal, Fev 1992. + * - "Common ISDN API - Especificação de protocolos para + * acesso aos canais B", Inesc, Jan 1994. + */ + +/* + * TODO: better decoding of Information Elements + * for debug purposes mainly + * encode our number in CallerPN and ConnectedPN + */ + +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/kernel.h> + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/mm.h> + +#include <linux/skbuff.h> + +#include <asm/io.h> +#include <asm/string.h> + +#include <linux/isdnif.h> + +#include "pcbit.h" +#include "edss1.h" +#include "capi.h" + + +/* + * Encoding of CAPI messages + * + */ + +int capi_conn_req(const char * calledPN, struct sk_buff **skb, int proto) +{ + ushort len; + + /* + * length + * AppInfoMask - 2 + * BC0 - 3 + * BC1 - 1 + * Chan - 2 + * Keypad - 1 + * CPN - 1 + * CPSA - 1 + * CalledPN - 2 + strlen + * CalledPSA - 1 + * rest... - 4 + * ---------------- + * Total 18 + strlen + */ + + len = 18 + strlen(calledPN); + + if (proto == ISDN_PROTO_L2_TRANS) + len++; + + if ((*skb = dev_alloc_skb(len)) == NULL) { + + printk(KERN_WARNING "capi_conn_req: alloc_skb failed\n"); + return -1; + } + + /* InfoElmMask */ + *((ushort*) skb_put(*skb, 2)) = AppInfoMask; + + if (proto == ISDN_PROTO_L2_TRANS) + { + /* Bearer Capability - Mandatory*/ + *(skb_put(*skb, 1)) = 3; /* BC0.Length */ + *(skb_put(*skb, 1)) = 0x80; /* Speech */ + *(skb_put(*skb, 1)) = 0x10; /* Circuit Mode */ + *(skb_put(*skb, 1)) = 0x23; /* A-law */ + } + else + { + /* Bearer Capability - Mandatory*/ + *(skb_put(*skb, 1)) = 2; /* BC0.Length */ + *(skb_put(*skb, 1)) = 0x88; /* Digital Information */ + *(skb_put(*skb, 1)) = 0x90; /* BC0.Octect4 */ + } + + /* Bearer Capability - Optional*/ + *(skb_put(*skb, 1)) = 0; /* BC1.Length = 0 */ + + *(skb_put(*skb, 1)) = 1; /* ChannelID.Length = 1 */ + *(skb_put(*skb, 1)) = 0x83; /* Basic Interface - Any Channel */ + + *(skb_put(*skb, 1)) = 0; /* Keypad.Length = 0 */ + + + *(skb_put(*skb, 1)) = 0; /* CallingPN.Length = 0 */ + *(skb_put(*skb, 1)) = 0; /* CallingPSA.Length = 0 */ + + /* Called Party Number */ + *(skb_put(*skb, 1)) = strlen(calledPN) + 1; + *(skb_put(*skb, 1)) = 0x81; + memcpy(skb_put(*skb, strlen(calledPN)), calledPN, strlen(calledPN)); + + /* '#' */ + + *(skb_put(*skb, 1)) = 0; /* CalledPSA.Length = 0 */ + + /* LLC.Length = 0; */ + /* HLC0.Length = 0; */ + /* HLC1.Length = 0; */ + /* UTUS.Length = 0; */ + memset(skb_put(*skb, 4), 0, 4); + + return len; +} + +int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb) +{ + + if ((*skb = dev_alloc_skb(5)) == NULL) { + + printk(KERN_WARNING "capi_conn_resp: alloc_skb failed\n"); + return -1; + } + + *((ushort*) skb_put(*skb, 2) ) = chan->callref; + *(skb_put(*skb, 1)) = 0x01; /* ACCEPT_CALL */ + *(skb_put(*skb, 1)) = 0; + *(skb_put(*skb, 1)) = 0; + + return 5; +} + +int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb) +{ + /* + * 8 bytes + */ + + if ((*skb = dev_alloc_skb(8)) == NULL) { + + printk(KERN_WARNING "capi_conn_active_req: alloc_skb failed\n"); + return -1; + } + + *((ushort*) skb_put(*skb, 2) ) = chan->callref; + +#ifdef DEBUG + printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref); +#endif + + *(skb_put(*skb, 1)) = 0; /* BC.Length = 0; */ + *(skb_put(*skb, 1)) = 0; /* ConnectedPN.Length = 0 */ + *(skb_put(*skb, 1)) = 0; /* PSA.Length */ + *(skb_put(*skb, 1)) = 0; /* LLC.Length = 0; */ + *(skb_put(*skb, 1)) = 0; /* HLC.Length = 0; */ + *(skb_put(*skb, 1)) = 0; /* UTUS.Length = 0; */ + + return 8; +} + +int capi_conn_active_resp(struct pcbit_chan* chan, struct sk_buff **skb) +{ + /* + * 2 bytes + */ + + if ((*skb = dev_alloc_skb(2)) == NULL) { + + printk(KERN_WARNING "capi_conn_active_resp: alloc_skb failed\n"); + return -1; + } + + *((ushort*) skb_put(*skb, 2) ) = chan->callref; + + return 2; +} + + +int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb, + int outgoing) +{ + + /* + * 18 bytes + */ + + if ((*skb = dev_alloc_skb(18)) == NULL) { + + printk(KERN_WARNING "capi_select_proto_req: alloc_skb failed\n"); + return -1; + } + + *((ushort*) skb_put(*skb, 2) ) = chan->callref; + + /* Layer2 protocol */ + + switch (chan->proto) { + case ISDN_PROTO_L2_X75I: + *(skb_put(*skb, 1)) = 0x05; /* LAPB */ + break; + case ISDN_PROTO_L2_HDLC: + *(skb_put(*skb, 1)) = 0x02; + break; + case ISDN_PROTO_L2_TRANS: + /* + * Voice (a-law) + */ + *(skb_put(*skb, 1)) = 0x06; + break; + default: +#ifdef DEBUG + printk(KERN_DEBUG "Transparent\n"); +#endif + *(skb_put(*skb, 1)) = 0x03; + break; + } + + *(skb_put(*skb, 1)) = (outgoing ? 0x02 : 0x42); /* Don't ask */ + *(skb_put(*skb, 1)) = 0x00; + + *((ushort *) skb_put(*skb, 2)) = MRU; + + + *(skb_put(*skb, 1)) = 0x08; /* Modulo */ + *(skb_put(*skb, 1)) = 0x07; /* Max Window */ + + *(skb_put(*skb, 1)) = 0x01; /* No Layer3 Protocol */ + + /* + * 2 - layer3 MTU [10] + * - Modulo [12] + * - Window + * - layer1 proto [14] + * - bitrate + * - sub-channel [16] + * - layer1dataformat [17] + */ + + memset(skb_put(*skb, 8), 0, 8); + + return 18; +} + + +int capi_activate_transp_req(struct pcbit_chan *chan, struct sk_buff **skb) +{ + + if ((*skb = dev_alloc_skb(7)) == NULL) { + + printk(KERN_WARNING "capi_activate_transp_req: alloc_skb failed\n"); + return -1; + } + + *((ushort*) skb_put(*skb, 2) ) = chan->callref; + + + *(skb_put(*skb, 1)) = chan->layer2link; /* Layer2 id */ + *(skb_put(*skb, 1)) = 0x00; /* Transmit by default */ + + *((ushort *) skb_put(*skb, 2)) = MRU; + + *(skb_put(*skb, 1)) = 0x01; /* Enables reception*/ + + return 7; +} + +int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb) +{ + ushort data_len; + + + /* + * callref - 2 + * layer2link - 1 + * wBlockLength - 2 + * data - 4 + * sernum - 1 + */ + + data_len = skb->len; + + if(skb_headroom(skb) < 10) + { + printk(KERN_CRIT "No headspace (%u) on headroom %p for capi header\n", skb_headroom(skb), skb); + } + else + { + skb_push(skb, 10); + } + + *((u16 *) (skb->data)) = chan->callref; + skb->data[2] = chan->layer2link; + *((u16 *) (skb->data + 3)) = data_len; + + chan->s_refnum = (chan->s_refnum + 1) % 8; + *((u32 *) (skb->data + 5)) = chan->s_refnum; + + skb->data[9] = 0; /* HDLC frame number */ + + return 10; +} + +int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb) + +{ + if ((*skb = dev_alloc_skb(4)) == NULL) { + + printk(KERN_WARNING "capi_tdata_resp: alloc_skb failed\n"); + return -1; + } + + *((ushort*) skb_put(*skb, 2) ) = chan->callref; + + *(skb_put(*skb, 1)) = chan->layer2link; + *(skb_put(*skb, 1)) = chan->r_refnum; + + return (*skb)->len; +} + +int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause) +{ + + if ((*skb = dev_alloc_skb(6)) == NULL) { + + printk(KERN_WARNING "capi_disc_req: alloc_skb failed\n"); + return -1; + } + + *((ushort*) skb_put(*skb, 2) ) = callref; + + *(skb_put(*skb, 1)) = 2; /* Cause.Length = 2; */ + *(skb_put(*skb, 1)) = 0x80; + *(skb_put(*skb, 1)) = 0x80 | cause; + + /* + * Change it: we should send 'Sic transit gloria Mundi' here ;-) + */ + + *(skb_put(*skb, 1)) = 0; /* UTUS.Length = 0; */ + + return 6; +} + +int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb) +{ + if ((*skb = dev_alloc_skb(2)) == NULL) { + + printk(KERN_WARNING "capi_disc_resp: alloc_skb failed\n"); + return -1; + } + + *((ushort*) skb_put(*skb, 2)) = chan->callref; + + return 2; +} + + +/* + * Decoding of CAPI messages + * + */ + +int capi_decode_conn_ind(struct pcbit_chan * chan, + struct sk_buff *skb, + struct callb_data *info) +{ + int CIlen, len; + + /* Call Reference [CAPI] */ + chan->callref = *((ushort*) skb->data); + skb_pull(skb, 2); + +#ifdef DEBUG + printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref); +#endif + + /* Channel Identification */ + + /* Expect + Len = 1 + Octect 3 = 0100 10CC - [ 7 Basic, 4 , 2-1 chan ] + */ + + CIlen = skb->data[0]; +#ifdef DEBUG + if (CIlen == 1) { + + if ( ((skb->data[1]) & 0xFC) == 0x48 ) + printk(KERN_DEBUG "decode_conn_ind: chan ok\n"); + printk(KERN_DEBUG "phyChan = %d\n", skb->data[1] & 0x03); + } + else + printk(KERN_DEBUG "conn_ind: CIlen = %d\n", CIlen); +#endif + skb_pull(skb, CIlen + 1); + + /* Calling Party Number */ + /* An "additional service" as far as Portugal Telecom is concerned */ + + len = skb->data[0]; + + if (len > 0) { + int count = 1; + +#ifdef DEBUG + printk(KERN_DEBUG "CPN: Octect 3 %02x\n", skb->data[1]); +#endif + if ((skb->data[1] & 0x80) == 0) + count = 2; + + if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC))) + return -1; + + memcpy(info->data.setup.CallingPN, skb->data + count + 1, + len - count); + info->data.setup.CallingPN[len - count] = 0; + + } + else { + info->data.setup.CallingPN = NULL; + printk(KERN_DEBUG "NULL CallingPN\n"); + } + + skb_pull(skb, len + 1); + + /* Calling Party Subaddress */ + skb_pull(skb, skb->data[0] + 1); + + /* Called Party Number */ + + len = skb->data[0]; + + if (len > 0) { + int count = 1; + + if ((skb->data[1] & 0x80) == 0) + count = 2; + + if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC))) + return -1; + + memcpy(info->data.setup.CalledPN, skb->data + count + 1, + len - count); + info->data.setup.CalledPN[len - count] = 0; + + } + else { + info->data.setup.CalledPN = NULL; + printk(KERN_DEBUG "NULL CalledPN\n"); + } + + skb_pull(skb, len + 1); + + /* Called Party Subaddress */ + skb_pull(skb, skb->data[0] + 1); + + /* LLC */ + skb_pull(skb, skb->data[0] + 1); + + /* HLC */ + skb_pull(skb, skb->data[0] + 1); + + /* U2U */ + skb_pull(skb, skb->data[0] + 1); + + return 0; +} + +/* + * returns errcode + */ + +int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb, + int *complete) +{ + int errcode; + + chan->callref = *((ushort *) skb->data); /* Update CallReference */ + skb_pull(skb, 2); + + errcode = *((ushort *) skb->data); /* read errcode */ + skb_pull(skb, 2); + + *complete = *(skb->data); + skb_pull(skb, 1); + + /* FIX ME */ + /* This is actually a firmware bug */ + if (!*complete) + { + printk(KERN_DEBUG "complete=%02x\n", *complete); + *complete = 1; + } + + + /* Optional Bearer Capability */ + skb_pull(skb, *(skb->data) + 1); + + /* Channel Identification */ + skb_pull(skb, *(skb->data) + 1); + + /* High Layer Compatibility follows */ + skb_pull(skb, *(skb->data) + 1); + + return errcode; +} + +int capi_decode_conn_actv_ind(struct pcbit_chan * chan, struct sk_buff *skb) +{ + ushort len; +#ifdef DEBUG + char str[32]; +#endif + + /* Yet Another Bearer Capability */ + skb_pull(skb, *(skb->data) + 1); + + + /* Connected Party Number */ + len=*(skb->data); + +#ifdef DEBUG + if (len > 1 && len < 31) { + memcpy(str, skb->data + 2, len - 1); + str[len] = 0; + printk(KERN_DEBUG "Connected Party Number: %s\n", str); + } + else + printk(KERN_DEBUG "actv_ind CPN len = %d\n", len); +#endif + + skb_pull(skb, len + 1); + + /* Connected Subaddress */ + skb_pull(skb, *(skb->data) + 1); + + /* Low Layer Capability */ + skb_pull(skb, *(skb->data) + 1); + + /* High Layer Capability */ + skb_pull(skb, *(skb->data) + 1); + + return 0; +} + +int capi_decode_conn_actv_conf(struct pcbit_chan * chan, struct sk_buff *skb) +{ + ushort errcode; + + errcode = *((ushort*) skb->data); + skb_pull(skb, 2); + + /* Channel Identification + skb_pull(skb, skb->data[0] + 1); + */ + return errcode; +} + + +int capi_decode_sel_proto_conf(struct pcbit_chan *chan, struct sk_buff *skb) +{ + ushort errcode; + + chan->layer2link = *(skb->data); + skb_pull(skb, 1); + + errcode = *((ushort*) skb->data); + skb_pull(skb, 2); + + return errcode; +} + +int capi_decode_actv_trans_conf(struct pcbit_chan *chan, struct sk_buff *skb) +{ + ushort errcode; + + if (chan->layer2link != *(skb->data) ) + printk("capi_decode_actv_trans_conf: layer2link doesn't match\n"); + + skb_pull(skb, 1); + + errcode = *((ushort*) skb->data); + skb_pull(skb, 2); + + return errcode; +} + +int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb) +{ + ushort len; +#ifdef DEBUG + int i; +#endif + /* Cause */ + + len = *(skb->data); + skb_pull(skb, 1); + +#ifdef DEBUG + + for (i=0; i<len; i++) + printk(KERN_DEBUG "Cause Octect %d: %02x\n", i+3, + *(skb->data + i)); +#endif + + skb_pull(skb, len); + + return 0; +} + +int capi_decode_disc_conf(struct pcbit_chan *chan, struct sk_buff *skb) +{ + ushort errcode; + + errcode = *((ushort*) skb->data); + skb_pull(skb, 2); + + return errcode; +} + +#ifdef DEBUG +int capi_decode_debug_188(u_char *hdr, ushort hdrlen) +{ + char str[64]; + int len; + + len = hdr[0]; + + if (len < 64 && len == hdrlen - 1) { + memcpy(str, hdr + 1, hdrlen - 1); + str[hdrlen - 1] = 0; + printk("%s\n", str); + } + else + printk("debug message incorrect\n"); + + return 0; +} +#endif + + + |