diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 14:08:29 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 14:08:29 +0000 |
commit | 57d569635c05dc4ea9b9f1f8dcec69b9ddc989b2 (patch) | |
tree | 1f703abf7d95dcd50ee52da3b96eb1b4b2b4ea53 /drivers/isdn/eicon/eicon_idi.c | |
parent | 59223edaa18759982db0a8aced0e77457d10c68e (diff) |
The rest of 2.3.6.
Diffstat (limited to 'drivers/isdn/eicon/eicon_idi.c')
-rw-r--r-- | drivers/isdn/eicon/eicon_idi.c | 1479 |
1 files changed, 1479 insertions, 0 deletions
diff --git a/drivers/isdn/eicon/eicon_idi.c b/drivers/isdn/eicon/eicon_idi.c new file mode 100644 index 000000000..a28f316c1 --- /dev/null +++ b/drivers/isdn/eicon/eicon_idi.c @@ -0,0 +1,1479 @@ +/* $Id: eicon_idi.c,v 1.9 1999/03/29 11:19:42 armin Exp $ + * + * ISDN lowlevel-module for Eicon.Diehl active cards. + * IDI interface + * + * Copyright 1998,99 by Armin Schindler (mac@melware.de) + * Copyright 1999 Cytronics & Melware (info@melware.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: eicon_idi.c,v $ + * Revision 1.9 1999/03/29 11:19:42 armin + * I/O stuff now in seperate file (eicon_io.c) + * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. + * + * Revision 1.8 1999/03/02 12:37:43 armin + * Added some important checks. + * Analog Modem with DSP. + * Channels will be added to Link-Level after loading firmware. + * + * Revision 1.7 1999/02/03 18:34:35 armin + * Channel selection for outgoing calls w/o CHI. + * Added channel # in debug messages. + * L2 Transparent should work with 800 byte/packet now. + * + * Revision 1.6 1999/01/26 07:18:59 armin + * Bug with wrong added CPN fixed. + * + * Revision 1.5 1999/01/24 20:14:11 armin + * Changed and added debug stuff. + * Better data sending. (still problems with tty's flip buffer) + * + * Revision 1.4 1999/01/10 18:46:05 armin + * Bug with wrong values in HLC fixed. + * Bytes to send are counted and limited now. + * + * Revision 1.3 1999/01/05 14:49:34 armin + * Added experimental usage of full BC and HLC for + * speech, 3.1kHz audio, fax gr.2/3 + * + * Revision 1.2 1999/01/04 13:19:29 armin + * Channel status with listen-request wrong - fixed. + * + * Revision 1.1 1999/01/01 18:09:41 armin + * First checkin of new eicon driver. + * DIVA-Server BRI/PCI and PRI/PCI are supported. + * Old diehl code is obsolete. + * + * + */ + +#define __NO_VERSION__ +#include "eicon.h" +#include "eicon_idi.h" +#include "eicon_dsp.h" + +#undef EICON_FULL_SERVICE_OKTETT + +char *eicon_idi_revision = "$Revision: 1.9 $"; + +eicon_manifbuf *manbuf; + +static char BC_Speech[3] = { 0x80, 0x90, 0xa3 }; +static char BC_31khz[3] = { 0x90, 0x90, 0xa3 }; +static char BC_64k[2] = { 0x88, 0x90 }; +static char BC_video[3] = { 0x91, 0x90, 0xa5 }; + +#ifdef EICON_FULL_SERVICE_OKTETT +/* +static char HLC_telephony[2] = { 0x91, 0x81 }; +*/ +static char HLC_faxg3[2] = { 0x91, 0x84 }; +#endif + +int eicon_idi_manage_assign(eicon_card *card); +int eicon_idi_manage_remove(eicon_card *card); + +int +idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan) +{ + int l = 0; + if (!signet) { + /* Signal Layer */ + reqbuf->XBuffer.P[l++] = CAI; + reqbuf->XBuffer.P[l++] = 1; + reqbuf->XBuffer.P[l++] = 0; + reqbuf->XBuffer.P[l++] = KEY; + reqbuf->XBuffer.P[l++] = 3; + reqbuf->XBuffer.P[l++] = 'I'; + reqbuf->XBuffer.P[l++] = '4'; + reqbuf->XBuffer.P[l++] = 'L'; + reqbuf->XBuffer.P[l++] = SHIFT|6; + reqbuf->XBuffer.P[l++] = SIN; + reqbuf->XBuffer.P[l++] = 2; + reqbuf->XBuffer.P[l++] = 0; + reqbuf->XBuffer.P[l++] = 0; + reqbuf->XBuffer.P[l++] = 0; /* end */ + reqbuf->Req = ASSIGN; + reqbuf->ReqCh = 0; + reqbuf->ReqId = 0; + reqbuf->XBuffer.length = l; + reqbuf->Reference = 0; /* Sig Entity */ + } + else { + /* Network Layer */ + reqbuf->XBuffer.P[l++] = CAI; + reqbuf->XBuffer.P[l++] = 1; + reqbuf->XBuffer.P[l++] = chan->e.D3Id; + reqbuf->XBuffer.P[l++] = LLC; + reqbuf->XBuffer.P[l++] = 2; + switch(chan->l2prot) { + case ISDN_PROTO_L2_HDLC: + reqbuf->XBuffer.P[l++] = 2; + break; + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + reqbuf->XBuffer.P[l++] = 5; + break; + case ISDN_PROTO_L2_TRANS: + case ISDN_PROTO_L2_MODEM: + reqbuf->XBuffer.P[l++] = 2; + break; + default: + reqbuf->XBuffer.P[l++] = 1; + } + switch(chan->l3prot) { + case ISDN_PROTO_L3_TRANS: + default: + reqbuf->XBuffer.P[l++] = 4; + } + reqbuf->XBuffer.P[l++] = 0; /* end */ + reqbuf->Req = ASSIGN; + reqbuf->ReqCh = 0; + reqbuf->ReqId = 0x20; + reqbuf->XBuffer.length = l; + reqbuf->Reference = 1; /* Net Entity */ + } + return(0); +} + +int +idi_put_req(eicon_REQ *reqbuf, int rq, int signet) +{ + reqbuf->Req = rq; + reqbuf->ReqCh = 0; + reqbuf->ReqId = 1; + reqbuf->XBuffer.length = 1; + reqbuf->XBuffer.P[0] = 0; + reqbuf->Reference = signet; + return(0); +} + +int +idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan) +{ + int l = 9; + reqbuf->Req = CALL_RES; + reqbuf->ReqCh = 0; + reqbuf->ReqId = 1; + reqbuf->XBuffer.P[0] = CAI; + reqbuf->XBuffer.P[1] = 6; + reqbuf->XBuffer.P[2] = 9; + reqbuf->XBuffer.P[3] = 0; + reqbuf->XBuffer.P[4] = 0; + reqbuf->XBuffer.P[5] = 0; + reqbuf->XBuffer.P[6] = 32; + reqbuf->XBuffer.P[7] = 3; + switch(chan->l2prot) { + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + case ISDN_PROTO_L2_HDLC: + reqbuf->XBuffer.P[1] = 1; + reqbuf->XBuffer.P[2] = 0x05; + l = 4; + break; + case ISDN_PROTO_L2_V11096: + reqbuf->XBuffer.P[2] = 0x0d; + reqbuf->XBuffer.P[3] = 5; + reqbuf->XBuffer.P[4] = 0; + break; + case ISDN_PROTO_L2_V11019: + reqbuf->XBuffer.P[2] = 0x0d; + reqbuf->XBuffer.P[3] = 6; + reqbuf->XBuffer.P[4] = 0; + break; + case ISDN_PROTO_L2_V11038: + reqbuf->XBuffer.P[2] = 0x0d; + reqbuf->XBuffer.P[3] = 7; + reqbuf->XBuffer.P[4] = 0; + break; + case ISDN_PROTO_L2_MODEM: + reqbuf->XBuffer.P[2] = 0x11; + reqbuf->XBuffer.P[3] = 7; + reqbuf->XBuffer.P[4] = 0; + reqbuf->XBuffer.P[5] = 0; + reqbuf->XBuffer.P[6] = 128; + reqbuf->XBuffer.P[7] = 0; + break; + } + reqbuf->XBuffer.P[8] = 0; + reqbuf->XBuffer.length = l; + reqbuf->Reference = 0; /* Sig Entity */ + if (DebugVar & 8) + printk(KERN_DEBUG"idi_req: Ch%d: Call_Res\n", chan->No); + return(0); +} + +int +idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) +{ + struct sk_buff *skb; + struct sk_buff *skb2; + eicon_REQ *reqbuf; + eicon_chan_ptr *chan2; + + skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC); + skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); + + if ((!skb) || (!skb2)) { + if (DebugVar & 1) + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + return -ENOMEM; + } + + chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); + chan2->ptr = chan; + + reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ)); + if (DebugVar & 8) + printk(KERN_DEBUG "idi_req: Ch%d: 0x%02x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig"); + if (layer) cmd |= 0x700; + switch(cmd) { + case ASSIGN: + case ASSIGN|0x700: + idi_assign_req(reqbuf, layer, chan); + break; + case REMOVE: + case REMOVE|0x700: + idi_put_req(reqbuf, REMOVE, layer); + break; + case INDICATE_REQ: + idi_put_req(reqbuf, INDICATE_REQ, 0); + break; + case HANGUP: + idi_put_req(reqbuf, HANGUP, 0); + break; + case REJECT: + idi_put_req(reqbuf, REJECT, 0); + break; + case CALL_ALERT: + idi_put_req(reqbuf, CALL_ALERT, 0); + break; + case CALL_RES: + idi_call_res_req(reqbuf, chan); + break; + case IDI_N_CONNECT|0x700: + idi_put_req(reqbuf, IDI_N_CONNECT, 1); + break; + case IDI_N_CONNECT_ACK|0x700: + idi_put_req(reqbuf, IDI_N_CONNECT_ACK, 1); + break; + case IDI_N_DISC|0x700: + idi_put_req(reqbuf, IDI_N_DISC, 1); + break; + case IDI_N_DISC_ACK|0x700: + idi_put_req(reqbuf, IDI_N_DISC_ACK, 1); + break; + default: + if (DebugVar & 1) + printk(KERN_ERR "idi_req: Ch%d: Unknown request\n", chan->No); + return(-1); + } + + skb_queue_tail(&chan->e.X, skb); + skb_queue_tail(&card->sndq, skb2); + eicon_schedule_tx(card); + return(0); +} + +int +eicon_idi_listen_req(eicon_card *card, eicon_chan *chan) +{ + if (DebugVar & 16) + printk(KERN_DEBUG"idi_req: Ch%d: Listen_Req eazmask=0x%x\n",chan->No, chan->eazmask); + if (!chan->e.D3Id) { + idi_do_req(card, chan, ASSIGN, 0); + } + if (chan->fsm_state == EICON_STATE_NULL) { + idi_do_req(card, chan, INDICATE_REQ, 0); + chan->fsm_state = EICON_STATE_LISTEN; + } + return(0); +} + +unsigned char +idi_si2bc(int si1, int si2, char *bc, char *hlc) +{ + hlc[0] = 0; + switch(si1) { + case 1: + bc[0] = 0x90; /* 3,1 kHz audio */ + bc[1] = 0x90; /* 64 kbit/s */ + bc[2] = 0xa3; /* G.711 A-law */ +#ifdef EICON_FULL_SERVICE_OKTETT + if (si2 == 1) { + bc[0] = 0x80; /* Speech */ + hlc[0] = 0x02; /* hlc len */ + hlc[1] = 0x91; /* first hic */ + hlc[2] = 0x81; /* Telephony */ + } +#endif + return(3); + case 2: + bc[0] = 0x90; /* 3,1 kHz audio */ + bc[1] = 0x90; /* 64 kbit/s */ + bc[2] = 0xa3; /* G.711 A-law */ +#ifdef EICON_FULL_SERVICE_OKTETT + if (si2 == 2) { + hlc[0] = 0x02; /* hlc len */ + hlc[1] = 0x91; /* first hic */ + hlc[2] = 0x84; /* Fax Gr.2/3 */ + } +#endif + return(3); + case 5: + case 7: + default: + bc[0] = 0x88; + bc[1] = 0x90; + return(2); + } + return (0); +} + +int +idi_hangup(eicon_card *card, eicon_chan *chan) +{ + if ((chan->fsm_state == EICON_STATE_ACTIVE) || + (chan->fsm_state == EICON_STATE_WMCONN)) { + if (chan->e.B2Id) idi_do_req(card, chan, IDI_N_DISC, 1); + } + if (chan->e.B2Id) idi_do_req(card, chan, REMOVE, 1); + idi_do_req(card, chan, HANGUP, 0); + chan->fsm_state = EICON_STATE_NULL; + if (DebugVar & 8) + printk(KERN_DEBUG"idi_req: Ch%d: Hangup\n", chan->No); + return(0); +} + +int +idi_connect_res(eicon_card *card, eicon_chan *chan) +{ + chan->fsm_state = EICON_STATE_IWAIT; + idi_do_req(card, chan, CALL_RES, 0); + idi_do_req(card, chan, ASSIGN, 1); + return(0); +} + +int +idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, + char *eazmsn, int si1, int si2) +{ + int l = 0; + int i; + unsigned char tmp; + unsigned char bc[5]; + unsigned char hlc[5]; + struct sk_buff *skb; + struct sk_buff *skb2; + eicon_REQ *reqbuf; + eicon_chan_ptr *chan2; + + skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC); + skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); + + if ((!skb) || (!skb2)) { + if (DebugVar & 1) + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + return -ENOMEM; + } + + chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); + chan2->ptr = chan; + + reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ)); + reqbuf->Req = CALL_REQ; + reqbuf->ReqCh = 0; + reqbuf->ReqId = 1; + + reqbuf->XBuffer.P[l++] = CPN; + reqbuf->XBuffer.P[l++] = strlen(phone) + 1; + reqbuf->XBuffer.P[l++] = 0xc1; + for(i=0; i<strlen(phone);i++) + reqbuf->XBuffer.P[l++] = phone[i]; + + reqbuf->XBuffer.P[l++] = OAD; + reqbuf->XBuffer.P[l++] = strlen(eazmsn) + 2; + reqbuf->XBuffer.P[l++] = 0x01; + reqbuf->XBuffer.P[l++] = 0x81; + for(i=0; i<strlen(eazmsn);i++) + reqbuf->XBuffer.P[l++] = eazmsn[i]; + + if ((tmp = idi_si2bc(si1, si2, bc, hlc)) > 0) { + reqbuf->XBuffer.P[l++] = BC; + reqbuf->XBuffer.P[l++] = tmp; + for(i=0; i<tmp;i++) + reqbuf->XBuffer.P[l++] = bc[i]; + if ((tmp=hlc[0])) { + reqbuf->XBuffer.P[l++] = HLC; + reqbuf->XBuffer.P[l++] = tmp; + for(i=1; i<=tmp;i++) + reqbuf->XBuffer.P[l++] = hlc[i]; + } + } + reqbuf->XBuffer.P[l++] = CAI; + reqbuf->XBuffer.P[l++] = 6; + reqbuf->XBuffer.P[l++] = 0x09; + reqbuf->XBuffer.P[l++] = 0; + reqbuf->XBuffer.P[l++] = 0; + reqbuf->XBuffer.P[l++] = 0; + reqbuf->XBuffer.P[l++] = 32; + reqbuf->XBuffer.P[l++] = 3; + switch(chan->l2prot) { + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + case ISDN_PROTO_L2_HDLC: + reqbuf->XBuffer.P[l-6] = 5; + reqbuf->XBuffer.P[l-7] = 1; + l -= 5; + break; + case ISDN_PROTO_L2_V11096: + reqbuf->XBuffer.P[l-7] = 3; + reqbuf->XBuffer.P[l-6] = 0x0d; + reqbuf->XBuffer.P[l-5] = 5; + reqbuf->XBuffer.P[l-4] = 0; + l -= 3; + break; + case ISDN_PROTO_L2_V11019: + reqbuf->XBuffer.P[l-7] = 3; + reqbuf->XBuffer.P[l-6] = 0x0d; + reqbuf->XBuffer.P[l-5] = 6; + reqbuf->XBuffer.P[l-4] = 0; + l -= 3; + break; + case ISDN_PROTO_L2_V11038: + reqbuf->XBuffer.P[l-7] = 3; + reqbuf->XBuffer.P[l-6] = 0x0d; + reqbuf->XBuffer.P[l-5] = 7; + reqbuf->XBuffer.P[l-4] = 0; + l -= 3; + break; + case ISDN_PROTO_L2_MODEM: + reqbuf->XBuffer.P[l-6] = 0x11; + reqbuf->XBuffer.P[l-5] = 7; + reqbuf->XBuffer.P[l-4] = 0; + reqbuf->XBuffer.P[l-3] = 0; + reqbuf->XBuffer.P[l-2] = 128; + reqbuf->XBuffer.P[l-1] = 0; + break; + } + + reqbuf->XBuffer.P[l++] = 0; /* end */ + reqbuf->XBuffer.length = l; + reqbuf->Reference = 0; /* Sig Entity */ + + skb_queue_tail(&chan->e.X, skb); + skb_queue_tail(&card->sndq, skb2); + eicon_schedule_tx(card); + + if (DebugVar & 8) + printk(KERN_DEBUG"idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone); + return(0); +} + + +void +idi_IndParse(eicon_card *ccard, eicon_chan *chan, idi_ind_message *message, unsigned char *buffer, int len) +{ + int i,j; + int pos = 0; + int codeset = 0; + int wlen = 0; + int lock = 0; + __u8 w; + __u16 code; + isdn_ctrl cmd; + + memset(message, 0, sizeof(idi_ind_message)); + + if ((!len) || (!buffer[pos])) return; + while(pos <= len) { + w = buffer[pos++]; + if (!w) return; + if (w & 0x80) { + wlen = 0; + } + else { + wlen = buffer[pos++]; + } + + if (pos > len) return; + + if (lock & 0x80) lock &= 0x7f; + else codeset = lock; + + if((w&0xf0) == SHIFT) { + codeset = w; + if(!(codeset & 0x08)) lock = codeset & 7; + codeset &= 7; + lock |= 0x80; + } + else { + if (w==ESC && wlen >=2) { + code = buffer[pos++]|0x800; + wlen--; + } + else code = w; + code |= (codeset<<8); + + switch(code) { + case OAD: + j = 1; + if (wlen) { + message->plan = buffer[pos++]; + if (message->plan &0x80) + message->screen = 0; + else { + message->screen = buffer[pos++]; + j = 2; + } + } + for(i=0; i < wlen-j; i++) + message->oad[i] = buffer[pos++]; + if (DebugVar & 2) + printk(KERN_DEBUG"idi_inf: Ch%d: OAD=(0x%02x,0x%02x) %s\n", chan->No, + message->plan, message->screen, message->oad); + break; + case RDN: + j = 1; + if (wlen) { + if (!(buffer[pos++] & 0x80)) { + pos++; + j = 2; + } + } + for(i=0; i < wlen-j; i++) + message->rdn[i] = buffer[pos++]; + if (DebugVar & 2) + printk(KERN_DEBUG"idi_inf: Ch%d: RDN= %s\n", chan->No, + message->rdn); + break; + case CPN: + for(i=0; i < wlen; i++) + message->cpn[i] = buffer[pos++]; + if (DebugVar & 2) + printk(KERN_DEBUG"idi_inf: Ch%d: CPN=(0x%02x) %s\n", chan->No, + (__u8)message->cpn[0], message->cpn + 1); + break; + case DSA: + pos++; + for(i=0; i < wlen-1; i++) + message->dsa[i] = buffer[pos++]; + if (DebugVar & 2) + printk(KERN_DEBUG"idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa); + break; + case OSA: + pos++; + for(i=0; i < wlen-1; i++) + message->osa[i] = buffer[pos++]; + if (DebugVar & 2) + printk(KERN_DEBUG"idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa); + break; + case BC: + for(i=0; i < wlen; i++) + message->bc[i] = buffer[pos++]; + if (DebugVar & 4) + printk(KERN_DEBUG"idi_inf: Ch%d: BC = 0x%02x 0x%02x 0x%02x\n", chan->No, + message->bc[0],message->bc[1],message->bc[2]); + break; + case 0x800|BC: + for(i=0; i < wlen; i++) + message->e_bc[i] = buffer[pos++]; + if (DebugVar & 4) + printk(KERN_DEBUG"idi_inf: Ch%d: ESC/BC=%d\n", chan->No, message->bc[0]); + break; + case LLC: + for(i=0; i < wlen; i++) + message->llc[i] = buffer[pos++]; + if (DebugVar & 4) + printk(KERN_DEBUG"idi_inf: Ch%d: LLC=%d %d %d %d\n", chan->No, message->llc[0], + message->llc[1],message->llc[2],message->llc[3]); + break; + case HLC: + for(i=0; i < wlen; i++) + message->hlc[i] = buffer[pos++]; + if (DebugVar & 4) + printk(KERN_DEBUG"idi_inf: Ch%d: HLC=%x %x %x %x %x\n", chan->No, + message->hlc[0], message->hlc[1], + message->hlc[2], message->hlc[3], message->hlc[4]); + break; + case DSP: + case 0x600|DSP: + for(i=0; i < wlen; i++) + message->display[i] = buffer[pos++]; + if (DebugVar & 4) + printk(KERN_DEBUG"idi_inf: Ch%d: Display: %s\n", chan->No, + message->display); + break; + case 0x600|KEY: + for(i=0; i < wlen; i++) + message->keypad[i] = buffer[pos++]; + if (DebugVar & 4) + printk(KERN_DEBUG"idi_inf: Ch%d: Keypad: %s\n", chan->No, + message->keypad); + break; + case NI: + case 0x600|NI: + if (wlen) { + if (DebugVar & 4) { + switch(buffer[pos] & 127) { + case 0: + printk(KERN_DEBUG"idi_inf: Ch%d: User suspended.\n", chan->No); + break; + case 1: + printk(KERN_DEBUG"idi_inf: Ch%d: User resumed.\n", chan->No); + break; + case 2: + printk(KERN_DEBUG"idi_inf: Ch%d: Bearer service change.\n", chan->No); + break; + default: + printk(KERN_DEBUG"idi_inf: Ch%d: Unknown Notification %x.\n", + chan->No, buffer[pos] & 127); + } + } + pos += wlen; + } + break; + case PI: + case 0x600|PI: + if (wlen > 1) { + if (DebugVar & 4) { + switch(buffer[pos+1] & 127) { + case 1: + printk(KERN_DEBUG"idi_inf: Ch%d: Call is not end-to-end ISDN.\n", chan->No); + break; + case 2: + printk(KERN_DEBUG"idi_inf: Ch%d: Destination address is non ISDN.\n", chan->No); + break; + case 3: + printk(KERN_DEBUG"idi_inf: Ch%d: Origination address is non ISDN.\n", chan->No); + break; + case 4: + printk(KERN_DEBUG"idi_inf: Ch%d: Call has returned to the ISDN.\n", chan->No); + break; + case 5: + printk(KERN_DEBUG"idi_inf: Ch%d: Interworking has occurred.\n", chan->No); + break; + case 8: + printk(KERN_DEBUG"idi_inf: Ch%d: In-band information available.\n", chan->No); + break; + default: + printk(KERN_DEBUG"idi_inf: Ch%d: Unknown Progress %x.\n", + chan->No, buffer[pos+1] & 127); + } + } + } + pos += wlen; + break; + case CAU: + for(i=0; i < wlen; i++) + message->cau[i] = buffer[pos++]; + memcpy(&chan->cause, &message->cau, 2); + if (DebugVar & 4) + printk(KERN_DEBUG"idi_inf: Ch%d: CAU=%d %d\n", chan->No, + message->cau[0],message->cau[1]); + break; + case 0x800|CAU: + for(i=0; i < wlen; i++) + message->e_cau[i] = buffer[pos++]; + if (DebugVar & 4) + printk(KERN_DEBUG"idi_inf: Ch%d: ECAU=%d %d\n", chan->No, + message->e_cau[0],message->e_cau[1]); + break; + case 0x800|CHI: + for(i=0; i < wlen; i++) + message->e_chi[i] = buffer[pos++]; + if (DebugVar & 4) + printk(KERN_DEBUG"idi_inf: Ch%d: ESC/CHI=%d\n", chan->No, + message->e_cau[0]); + break; + case 0x800|0x7a: + pos ++; + message->e_mt=buffer[pos++]; + if (DebugVar & 2) + printk(KERN_DEBUG"idi_inf: Ch%d: EMT=0x%x\n", chan->No, message->e_mt); + break; + case DT: + for(i=0; i < wlen; i++) + message->dt[i] = buffer[pos++]; + if (DebugVar & 4) + printk(KERN_DEBUG"idi_inf: Ch%d: DT: %02d.%02d.%02d %02d:%02d:%02d\n", chan->No, + message->dt[2], message->dt[1], message->dt[0], + message->dt[3], message->dt[4], message->dt[5]); + break; + case 0x600|SIN: + for(i=0; i < wlen; i++) + message->sin[i] = buffer[pos++]; + if (DebugVar & 2) + printk(KERN_DEBUG"idi_inf: Ch%d: SIN=%d %d\n", chan->No, + message->sin[0],message->sin[1]); + break; + case 0x600|CPS: + if (DebugVar & 2) + printk(KERN_DEBUG"idi_inf: Ch%d: Called Party Status in ind\n", chan->No); + pos += wlen; + break; + case 0x600|CIF: + for (i = 0; i < wlen; i++) + if (buffer[pos + i] != '0') break; + memcpy(&cmd.parm.num, &buffer[pos + i], wlen - i); + cmd.parm.num[wlen - i] = 0; + if (DebugVar & 2) + printk(KERN_DEBUG"idi_inf: Ch%d: CIF=%s\n", chan->No, cmd.parm.num); + pos += wlen; + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_CINF; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + break; + case 0x600|DATE: + if (DebugVar & 2) + printk(KERN_DEBUG"idi_inf: Ch%d: Date in ind\n", chan->No); + pos += wlen; + break; + case 0xe08: + case 0xe7a: + case 0xe04: + case 0xe00: + /* *** TODO *** */ + case CHA: + /* Charge advice */ + case FTY: + case 0x600|FTY: + case CHI: + case 0x800: + /* Not yet interested in this */ + pos += wlen; + break; + case 0x880: + /* Managment Information Element */ + if (!manbuf) { + if (DebugVar & 1) + printk(KERN_WARNING"idi_err: manbuf not allocated\n"); + } + else { + memcpy(&manbuf->data[manbuf->pos], &buffer[pos], wlen); + manbuf->length[manbuf->count] = wlen; + manbuf->count++; + manbuf->pos += wlen; + } + pos += wlen; + break; + default: + pos += wlen; + if (DebugVar & 6) + printk(KERN_WARNING"idi_inf: Ch%d: unknown information element 0x%x in ind, len:%x\n", + chan->No, code, wlen); + } + } + } +} + +void +idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *si1, unsigned char *si2) +{ + si1[0] = 0; + si2[0] = 0; + if (memcmp(bc, BC_Speech, 3) == 0) { /* Speech */ + si1[0] = 1; +#ifdef EICON_FULL_SERVICE_OKTETT + si2[0] = 1; +#endif + } + if (memcmp(bc, BC_31khz, 3) == 0) { /* 3.1kHz audio */ + si1[0] = 1; +#ifdef EICON_FULL_SERVICE_OKTETT + si2[0] = 2; + if (memcmp(hlc, HLC_faxg3, 2) == 0) { /* Fax Gr.2/3 */ + si1[0] = 2; + } +#endif + } + if (memcmp(bc, BC_64k, 2) == 0) { /* unrestricted 64 kbits */ + si1[0] = 7; + } + if (memcmp(bc, BC_video, 3) == 0) { /* video */ + si1[0] = 4; + } +} + +void +idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len) +{ + isdn_ctrl cmd; + eicon_dsp_ind *p = (eicon_dsp_ind *) (&buffer[1]); + static char *connmsg[] = + {"", "V.21", "V.23", "V.22", "V.22bis", "V.32bis", "V.34", + "V.8", "Bell 212A", "Bell 103", "V.29 Leased", "V.33 Leased", "V.90", + "V.21 CH2", "V.27ter", "V.29", "V.33", "V.17"}; + + switch (buffer[0]) { + case DSP_UDATA_INDICATION_SYNC: + if (DebugVar & 16) + printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_SYNC time %d\n", chan->No, p->time); + break; + case DSP_UDATA_INDICATION_DCD_OFF: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DCD_OFF time %d\n", chan->No, p->time); + break; + case DSP_UDATA_INDICATION_DCD_ON: + if ((chan->l2prot == ISDN_PROTO_L2_MODEM) && + (chan->fsm_state == EICON_STATE_WMCONN)) { + chan->fsm_state = EICON_STATE_ACTIVE; + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_BCONN; + cmd.arg = chan->No; + sprintf(cmd.parm.num, "%d/%s", p->speed, connmsg[p->norm]); + ccard->interface.statcallb(&cmd); + } + if (DebugVar & 8) { + printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DCD_ON time %d\n", chan->No, p->time); + printk(KERN_DEBUG"idi_ind: Ch%d: %d %d %d %d\n", chan->No, + p->norm, p->options, p->speed, p->delay); + } + break; + case DSP_UDATA_INDICATION_CTS_OFF: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_CTS_OFF time %d\n", chan->No, p->time); + break; + case DSP_UDATA_INDICATION_CTS_ON: + if (DebugVar & 8) { + printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_CTS_ON time %d\n", chan->No, p->time); + printk(KERN_DEBUG"idi_ind: Ch%d: %d %d %d %d\n", chan->No, + p->norm, p->options, p->speed, p->delay); + } + break; + case DSP_UDATA_INDICATION_DISCONNECT: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]); + break; + default: + if (DebugVar & 8) + printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]); + } +} + +void +idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) +{ + int tmp; + int free_buff; + struct sk_buff *skb2; + eicon_IND *ind = (eicon_IND *)skb->data; + eicon_chan *chan; + idi_ind_message message; + isdn_ctrl cmd; + + if ((chan = ccard->IdTable[ind->IndId]) == NULL) { + dev_kfree_skb(skb); + return; + } + + if ((DebugVar & 128) || + ((DebugVar & 16) && (ind->Ind != 8))) { + printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No, + ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); + } + + free_buff = 1; + /* Signal Layer */ + if (chan->e.D3Id == ind->IndId) { + idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length); + switch(ind->Ind) { + case HANGUP: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: Hangup\n", chan->No); + while((skb2 = skb_dequeue(&chan->e.X))) { + dev_kfree_skb(skb2); + } + chan->e.busy = 0; + chan->queued = 0; + chan->waitq = 0; + chan->waitpq = 0; + chan->fsm_state = EICON_STATE_NULL; + if (message.e_cau[0] & 0x7f) { + cmd.driver = ccard->myid; + cmd.arg = chan->No; + sprintf(cmd.parm.num,"E%02x%02x", + chan->cause[0]&0x7f, message.e_cau[0]&0x7f); + cmd.command = ISDN_STAT_CAUSE; + ccard->interface.statcallb(&cmd); + } + chan->cause[0] = 0; + cmd.driver = ccard->myid; + cmd.arg = chan->No; + cmd.command = ISDN_STAT_DHUP; + ccard->interface.statcallb(&cmd); + eicon_idi_listen_req(ccard, chan); + break; + case INDICATE_IND: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: Indicate_Ind\n", chan->No); + chan->fsm_state = EICON_STATE_ICALL; + idi_bc2si(message.bc, message.hlc, &chan->si1, &chan->si2); + strcpy(chan->cpn, message.cpn + 1); + if (strlen(message.dsa)) { + strcat(chan->cpn, "."); + strcat(chan->cpn, message.dsa); + } + strcpy(chan->oad, message.oad); + try_stat_icall_again: + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_ICALL; + cmd.arg = chan->No; + cmd.parm.setup.si1 = chan->si1; + cmd.parm.setup.si2 = chan->si2; + strcpy(cmd.parm.setup.eazmsn, chan->cpn); + strcpy(cmd.parm.setup.phone, chan->oad); + cmd.parm.setup.plan = message.plan; + cmd.parm.setup.screen = message.screen; + tmp = ccard->interface.statcallb(&cmd); + switch(tmp) { + case 0: /* no user responding */ + idi_do_req(ccard, chan, HANGUP, 0); + break; + case 1: /* alert */ + if (DebugVar & 8) + printk(KERN_DEBUG"idi_req: Ch%d: Call Alert\n", chan->No); + if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_ICALLW)) { + chan->fsm_state = EICON_STATE_ICALL; + idi_do_req(ccard, chan, CALL_ALERT, 0); + } + break; + case 2: /* reject */ + if (DebugVar & 8) + printk(KERN_DEBUG"idi_req: Ch%d: Call Reject\n", chan->No); + idi_do_req(ccard, chan, REJECT, 0); + break; + case 3: /* incomplete number */ + if (DebugVar & 8) + printk(KERN_DEBUG"idi_req: Ch%d: Incomplete Number\n", chan->No); + switch(ccard->type) { + case EICON_CTYPE_MAESTRAP: + case EICON_CTYPE_S2M: + /* TODO (other protocols) */ + chan->fsm_state = EICON_STATE_ICALLW; + break; + default: + idi_do_req(ccard, chan, HANGUP, 0); + } + break; + } + break; + case INFO_IND: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: Info_Ind\n", chan->No); + if ((chan->fsm_state == EICON_STATE_ICALLW) && + (message.cpn[0])) { + strcat(chan->cpn, message.cpn + 1); + goto try_stat_icall_again; + } + break; + case CALL_IND: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: Call_Ind\n", chan->No); + if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_IWAIT)) { + chan->fsm_state = EICON_STATE_IBWAIT; + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_DCONN; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + } else + idi_hangup(ccard, chan); + break; + case CALL_CON: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: Call_Con\n", chan->No); + if (chan->fsm_state == EICON_STATE_OCALL) { + chan->fsm_state = EICON_STATE_OBWAIT; + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_DCONN; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + idi_do_req(ccard, chan, ASSIGN, 1); + idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + } else + idi_hangup(ccard, chan); + break; + case AOC_IND: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: Advice of Charge\n", chan->No); + break; + default: + if (DebugVar & 8) + printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED SigIndication 0x%02x\n", chan->No, ind->Ind); + } + } + /* Network Layer */ + else if (chan->e.B2Id == ind->IndId) { + + if (chan->No == ccard->nchannels) { + /* Management Indication */ + idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length); + chan->fsm_state = 1; + } + else + switch(ind->Ind) { + case IDI_N_CONNECT_ACK: + if (DebugVar & 16) + printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect_Ack\n", chan->No); + if (chan->l2prot == ISDN_PROTO_L2_MODEM) { + chan->fsm_state = EICON_STATE_WMCONN; + break; + } + chan->fsm_state = EICON_STATE_ACTIVE; + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_BCONN; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + break; + case IDI_N_CONNECT: + if (DebugVar & 16) + printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect\n", chan->No); + if (chan->e.B2Id) idi_do_req(ccard, chan, IDI_N_CONNECT_ACK, 1); + if (chan->l2prot == ISDN_PROTO_L2_MODEM) { + chan->fsm_state = EICON_STATE_WMCONN; + break; + } + chan->fsm_state = EICON_STATE_ACTIVE; + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_BCONN; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + break; + case IDI_N_DISC: + if (DebugVar & 16) + printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC\n", chan->No); + if (chan->e.B2Id) { + idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1); + idi_do_req(ccard, chan, REMOVE, 1); + } + chan->queued = 0; + chan->waitq = 0; + chan->waitpq = 0; + if (chan->fsm_state == EICON_STATE_ACTIVE) { + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_BHUP; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + } + break; + case IDI_N_DISC_ACK: + if (DebugVar & 16) + printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC_ACK\n", chan->No); + break; + case IDI_N_DATA_ACK: + if (DebugVar & 16) + printk(KERN_DEBUG"idi_ind: Ch%d: N_DATA_ACK\n", chan->No); + break; + case IDI_N_DATA: + skb_pull(skb, sizeof(eicon_IND) - 1); + if (DebugVar & 128) + printk(KERN_DEBUG"idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len); + ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb); + free_buff = 0; + break; + case IDI_N_UDATA: + idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + break; + default: + if (DebugVar & 8) + printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind); + } + } + else { + if (DebugVar & 1) + printk(KERN_ERR "idi_ind: Ch%d: Ind is neither SIG nor NET !\n", chan->No); + } + if (free_buff) dev_kfree_skb(skb); +} + +void +idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) +{ + int j; + eicon_RC *ack = (eicon_RC *)skb->data; + eicon_chan *chan; + isdn_ctrl cmd; + + if ((ack->Rc != ASSIGN_OK) && (ack->Rc != OK)) { + if ((chan = ccard->IdTable[ack->RcId]) != NULL) { + chan->e.busy = 0; + if (DebugVar & 24) + printk(KERN_ERR "eicon_ack: Ch%d: Not OK: Rc=%d Id=%d Ch=%d\n", chan->No, + ack->Rc, ack->RcId, ack->RcCh); + if (chan->No == ccard->nchannels) { /* Management */ + chan->fsm_state = 2; + } else { /* any other channel */ + /* card reports error: we hangup */ + idi_hangup(ccard, chan); + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + } + } + } + else { + if ((chan = ccard->IdTable[ack->RcId]) != NULL) { + if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) { + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No, + ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id); + } else { + if (chan->No == ccard->nchannels) { /* Management */ + if (chan->e.Req == 0x04) chan->fsm_state = 1; + } + if (chan->e.ReqCh) { + switch(chan->e.Req & 0x0f) { + case IDI_N_MDATA: + case IDI_N_DATA: + chan->queued -= chan->waitq; + if (chan->queued < 0) chan->queued = 0; + if ((chan->e.Req & 0x0f) == IDI_N_DATA) { + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_BSENT; + cmd.arg = chan->No; + cmd.parm.length = chan->waitpq; + chan->waitpq = 0; + ccard->interface.statcallb(&cmd); + } + break; + default: + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No, + ack->RcId, ack->RcCh, ack->Reference); + } + } + else { + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No, + ack->RcId, ack->RcCh, ack->Reference); + } + + if (chan->e.Req == REMOVE) { + if (ack->Reference == chan->e.ref) { + ccard->IdTable[ack->RcId] = NULL; + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No, + ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig"); + if (!chan->e.ReqCh) + chan->e.D3Id = 0; + else + chan->e.B2Id = 0; + } + else { + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No, + ack->Reference, chan->e.ref); + } + } + chan->e.busy = 0; + } + } + else { + for(j = 0; j < ccard->nchannels + 1; j++) { + if (ccard->bch[j].e.ref == ack->Reference) { + if (!ccard->bch[j].e.ReqCh) + ccard->bch[j].e.D3Id = ack->RcId; + else + ccard->bch[j].e.B2Id = ack->RcId; + ccard->IdTable[ack->RcId] = &ccard->bch[j]; + ccard->bch[j].e.busy = 0; + ccard->bch[j].e.ref = 0; + if (DebugVar & 16) + printk(KERN_DEBUG"idi_ack: Ch%d: Id %d assigned (%s)\n", j, + ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig"); + break; + } + } + if (j > ccard->nchannels) { + if (DebugVar & 24) + printk(KERN_DEBUG"idi_ack: Ch??: ref %d not found for Id %d\n", + ack->Reference, ack->RcId); + } + } + } + dev_kfree_skb(skb); + eicon_schedule_tx(ccard); +} + +int +idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) +{ + struct sk_buff *xmit_skb; + struct sk_buff *skb2; + eicon_REQ *reqbuf; + eicon_chan_ptr *chan2; + int len, plen = 0, offset = 0; + unsigned long flags; + + if (chan->fsm_state != EICON_STATE_ACTIVE) { + if (DebugVar & 1) + printk(KERN_DEBUG"idi_snd: Ch%d: send bytes on state %d !\n", chan->No, chan->fsm_state); + return -ENODEV; + } + + len = skb->len; + if (len > 2138) /* too much for the shared memory */ + return -1; + if (!len) + return 0; + if (chan->queued + len > ((chan->l2prot == ISDN_PROTO_L2_TRANS) ? 4000 : EICON_MAX_QUEUED)) + return 0; + if (DebugVar & 128) + printk(KERN_DEBUG"idi_snd: Ch%d: %d bytes\n", chan->No, len); + save_flags(flags); + cli(); + while(offset < len) { + + plen = ((len - offset) > 270) ? 270 : len - offset; + + xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC); + skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); + + if ((!skb) || (!skb2)) { + restore_flags(flags); + if (DebugVar & 1) + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + return -ENOMEM; + } + + chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); + chan2->ptr = chan; + + reqbuf = (eicon_REQ *)skb_put(xmit_skb, plen + sizeof(eicon_REQ)); + if (((len - offset) > 270) && + (chan->l2prot != ISDN_PROTO_L2_TRANS)) { + reqbuf->Req = IDI_N_MDATA; + } else { + reqbuf->Req = IDI_N_DATA; + if (ack) reqbuf->Req |= N_D_BIT; + } + reqbuf->ReqCh = 0; + reqbuf->ReqId = 1; + memcpy(&reqbuf->XBuffer.P, skb->data + offset, plen); + reqbuf->XBuffer.length = plen; + reqbuf->Reference = 1; /* Net Entity */ + + skb_queue_tail(&chan->e.X, xmit_skb); + skb_queue_tail(&card->sndq, skb2); + + offset += plen; + } + chan->queued += len; + restore_flags(flags); + eicon_schedule_tx(card); + dev_kfree_skb(skb); + return len; +} + + + +int +eicon_idi_manage_assign(eicon_card *card) +{ + struct sk_buff *skb; + struct sk_buff *skb2; + eicon_REQ *reqbuf; + eicon_chan *chan; + eicon_chan_ptr *chan2; + + chan = &(card->bch[card->nchannels]); + + skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC); + skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); + + if ((!skb) || (!skb2)) { + if (DebugVar & 1) + printk(KERN_WARNING "idi_err: alloc_skb failed\n"); + return -ENOMEM; + } + + chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); + chan2->ptr = chan; + + reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ)); + + reqbuf->XBuffer.P[0] = 0; + reqbuf->Req = ASSIGN; + reqbuf->ReqCh = 0; + reqbuf->ReqId = 0xe0; + reqbuf->XBuffer.length = 1; + reqbuf->Reference = 2; /* Man Entity */ + + skb_queue_tail(&chan->e.X, skb); + skb_queue_tail(&card->sndq, skb2); + eicon_schedule_tx(card); + return(0); +} + + +int +eicon_idi_manage_remove(eicon_card *card) +{ + struct sk_buff *skb; + struct sk_buff *skb2; + eicon_REQ *reqbuf; + eicon_chan *chan; + eicon_chan_ptr *chan2; + + chan = &(card->bch[card->nchannels]); + + skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC); + skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); + + if ((!skb) || (!skb2)) { + if (DebugVar & 1) + printk(KERN_WARNING "idi_err: alloc_skb failed\n"); + return -ENOMEM; + } + + chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); + chan2->ptr = chan; + + reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ)); + + reqbuf->Req = REMOVE; + reqbuf->ReqCh = 0; + reqbuf->ReqId = 1; + reqbuf->XBuffer.length = 0; + reqbuf->Reference = 2; /* Man Entity */ + + skb_queue_tail(&chan->e.X, skb); + skb_queue_tail(&card->sndq, skb2); + eicon_schedule_tx(card); + return(0); +} + + +int +eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) +{ + int l = 0; + int ret = 0; + int timeout; + int i; + struct sk_buff *skb; + struct sk_buff *skb2; + eicon_REQ *reqbuf; + eicon_chan *chan; + eicon_chan_ptr *chan2; + + chan = &(card->bch[card->nchannels]); + + if (chan->e.D3Id) return -EBUSY; + chan->e.D3Id = 1; + while((skb2 = skb_dequeue(&chan->e.X))) + dev_kfree_skb(skb2); + chan->e.busy = 0; + + if ((ret = eicon_idi_manage_assign(card))) { + chan->e.D3Id = 0; + return(ret); + } + + timeout = jiffies + 50; + while (timeout > jiffies) { + if (chan->e.B2Id) break; + SLEEP(10); + } + if (!chan->e.B2Id) { + chan->e.D3Id = 0; + return -EIO; + } + + chan->fsm_state = 0; + + if (!(manbuf = kmalloc(sizeof(eicon_manifbuf), GFP_KERNEL))) { + if (DebugVar & 1) + printk(KERN_WARNING "idi_err: alloc_manifbuf failed\n"); + chan->e.D3Id = 0; + return -ENOMEM; + } + if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) { + chan->e.D3Id = 0; + return -EFAULT; + } + + skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC); + skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); + + if ((!skb) || (!skb2)) { + if (DebugVar & 1) + printk(KERN_WARNING "idi_err_manif: alloc_skb failed\n"); + kfree(manbuf); + chan->e.D3Id = 0; + return -ENOMEM; + } + + chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); + chan2->ptr = chan; + + reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ)); + + reqbuf->XBuffer.P[l++] = ESC; + reqbuf->XBuffer.P[l++] = 6; + reqbuf->XBuffer.P[l++] = 0x80; + for (i = 0; i < manbuf->length[0]; i++) + reqbuf->XBuffer.P[l++] = manbuf->data[i]; + reqbuf->XBuffer.P[1] = manbuf->length[0] + 1; + + reqbuf->XBuffer.P[l++] = 0; + reqbuf->Req = (manbuf->count) ? manbuf->count : 0x02; /* Request */ + reqbuf->ReqCh = 0; + reqbuf->ReqId = 1; + reqbuf->XBuffer.length = l; + reqbuf->Reference = 2; /* Man Entity */ + + skb_queue_tail(&chan->e.X, skb); + skb_queue_tail(&card->sndq, skb2); + + manbuf->count = 0; + manbuf->pos = 0; + + eicon_schedule_tx(card); + + timeout = jiffies + 50; + while (timeout > jiffies) { + if (chan->fsm_state) break; + SLEEP(10); + } + if ((!chan->fsm_state) || (chan->fsm_state == 2)) { + eicon_idi_manage_remove(card); + kfree(manbuf); + chan->e.D3Id = 0; + return -EIO; + } + + if ((ret = eicon_idi_manage_remove(card))) { + chan->e.D3Id = 0; + return(ret); + } + + if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) { + chan->e.D3Id = 0; + return -EFAULT; + } + + kfree(manbuf); + chan->e.D3Id = 0; + return(0); +} |