/* $Id$ * * 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$ * Revision 1.20 1999/09/21 20:35:43 armin * added more error checking. * * Revision 1.19 1999/09/21 20:06:40 armin * Added pointer checks. * * Revision 1.18 1999/09/07 12:48:05 armin * Prepared for sub-address usage. * * Revision 1.17 1999/09/07 12:35:39 armin * Better checking and channel Id handling. * * Revision 1.16 1999/09/04 13:44:19 armin * Fix of V.42 analog Modem negotiation handling. * * Revision 1.15 1999/08/28 21:32:50 armin * Prepared for fax related functions. * Now compilable without errors/warnings. * * Revision 1.14 1999/08/28 20:24:40 armin * Corrected octet 3/3a in CPN/OAD information element. * Thanks to John Simpson * * Revision 1.13 1999/08/22 20:26:44 calle * backported changes from kernel 2.3.14: * - several #include "config.h" gone, others come. * - "struct device" changed to "struct net_device" in 2.3.14, added a * define in isdn_compat.h for older kernel versions. * * Revision 1.12 1999/08/18 20:16:59 armin * Added XLOG function for all cards. * Bugfix of alloc_skb NULL pointer. * * Revision 1.11 1999/07/25 15:12:03 armin * fix of some debug logs. * enabled ISA-cards option. * * Revision 1.10 1999/07/11 17:16:24 armin * Bugfixes in queue handling. * Added DSP-DTMF decoder functions. * Reorganized ack_handler. * * 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. * * */ #include #define __NO_VERSION__ #include "eicon.h" #include "eicon_idi.h" #include "eicon_dsp.h" #undef EICON_FULL_SERVICE_OKTETT char *eicon_idi_revision = "$Revision$"; 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_fill_in_T30(eicon_chan *chan, unsigned char *buffer); int idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan) { int l = 0; int tmp; tmp = 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: reqbuf->XBuffer.P[l++] = 2; break; case ISDN_PROTO_L2_MODEM: if (chan->fsm_state == EICON_STATE_IWAIT) reqbuf->XBuffer.P[l++] = 9; /* V.42 incoming */ else reqbuf->XBuffer.P[l++] = 10; /* V.42 */ break; case ISDN_PROTO_L2_FAX: if (chan->fsm_state == EICON_STATE_IWAIT) reqbuf->XBuffer.P[l++] = 3; /* autoconnect on incoming */ else reqbuf->XBuffer.P[l++] = 2; break; default: reqbuf->XBuffer.P[l++] = 1; } switch(chan->l3prot) { case ISDN_PROTO_L3_FAX: #ifdef CONFIG_ISDN_TTY_FAX reqbuf->XBuffer.P[l++] = 6; reqbuf->XBuffer.P[l++] = NLC; tmp = idi_fill_in_T30(chan, &reqbuf->XBuffer.P[l+1]); reqbuf->XBuffer.P[l++] = tmp; l += tmp; break; #endif 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; case ISDN_PROTO_L2_FAX: reqbuf->XBuffer.P[2] = 0x10; reqbuf->XBuffer.P[3] = 0; reqbuf->XBuffer.P[4] = 0; reqbuf->XBuffer.P[5] = 0; reqbuf->XBuffer.P[6] = 128; reqbuf->XBuffer.P[7] = 0; break; case ISDN_PROTO_L2_TRANS: switch(chan->l3prot) { case ISDN_PROTO_L3_TRANSDSP: reqbuf->XBuffer.P[2] = 22; /* DTMF, audio events on */ } break; } reqbuf->XBuffer.P[8] = 0; reqbuf->XBuffer.length = l; reqbuf->Reference = 0; /* Sig Entity */ eicon_log(NULL, 8, "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)) { eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in do_req()\n", chan->No); if (skb) dev_kfree_skb(skb); if (skb2) dev_kfree_skb(skb2); 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)); eicon_log(card, 8, "idi_req: Ch%d: req %x (%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: eicon_log(card, 1, "idi_req: Ch%d: Unknown request\n", chan->No); dev_kfree_skb(skb); dev_kfree_skb(skb2); 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 ((!card) || (!chan)) return 1; eicon_log(card, 16, "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 ((!card) || (!chan)) return 1; 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; eicon_log(card, 8, "idi_req: Ch%d: Hangup\n", chan->No); #ifdef CONFIG_ISDN_TTY_FAX chan->fax = 0; #endif return(0); } int idi_connect_res(eicon_card *card, eicon_chan *chan) { if ((!card) || (!chan)) return 1; chan->fsm_state = EICON_STATE_IWAIT; idi_do_req(card, chan, CALL_RES, 0); /* check if old NetID has been removed */ if (chan->e.B2Id) { eicon_log(card, 1, "idi_err: Ch%d: Old NetID %x was not removed.\n", chan->No, chan->e.B2Id); idi_do_req(card, chan, REMOVE, 1); } 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 *sub, *sp; unsigned char bc[5]; unsigned char hlc[5]; struct sk_buff *skb; struct sk_buff *skb2; eicon_REQ *reqbuf; eicon_chan_ptr *chan2; if ((!card) || (!chan)) return 1; skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC); skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); if ((!skb) || (!skb2)) { eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in connect_req()\n", chan->No); if (skb) dev_kfree_skb(skb); if (skb2) dev_kfree_skb(skb2); 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; sub = NULL; sp = phone; while (*sp) { if (*sp == '.') { sub = sp + 1; *sp = 0; } else sp++; } reqbuf->XBuffer.P[l++] = CPN; reqbuf->XBuffer.P[l++] = strlen(phone) + 1; reqbuf->XBuffer.P[l++] = 0x81; for(i=0; iXBuffer.P[l++] = phone[i] & 0x7f; if (sub) { reqbuf->XBuffer.P[l++] = DSA; reqbuf->XBuffer.P[l++] = strlen(sub) + 2; reqbuf->XBuffer.P[l++] = 0x80; /* NSAP coded */ reqbuf->XBuffer.P[l++] = 0x50; /* local IDI format */ while (*sub) reqbuf->XBuffer.P[l++] = *sub++ & 0x7f; } sub = NULL; sp = eazmsn; while (*sp) { if (*sp == '.') { sub = sp + 1; *sp = 0; } else sp++; } reqbuf->XBuffer.P[l++] = OAD; reqbuf->XBuffer.P[l++] = strlen(eazmsn) + 2; reqbuf->XBuffer.P[l++] = 0x01; reqbuf->XBuffer.P[l++] = 0x80; for(i=0; iXBuffer.P[l++] = eazmsn[i] & 0x7f; if (sub) { reqbuf->XBuffer.P[l++] = OSA; reqbuf->XBuffer.P[l++] = strlen(sub) + 2; reqbuf->XBuffer.P[l++] = 0x80; /* NSAP coded */ reqbuf->XBuffer.P[l++] = 0x50; /* local IDI format */ while (*sub) reqbuf->XBuffer.P[l++] = *sub++ & 0x7f; } if ((tmp = idi_si2bc(si1, si2, bc, hlc)) > 0) { reqbuf->XBuffer.P[l++] = BC; reqbuf->XBuffer.P[l++] = tmp; for(i=0; iXBuffer.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; case ISDN_PROTO_L2_FAX: reqbuf->XBuffer.P[l-6] = 0x10; reqbuf->XBuffer.P[l-5] = 0; 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; case ISDN_PROTO_L2_TRANS: switch(chan->l3prot) { case ISDN_PROTO_L3_TRANSDSP: reqbuf->XBuffer.P[l-6] = 22; /* DTMF, audio events on */ } 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); eicon_log(card, 8, "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); if (pos + wlen > len) { eicon_log(ccard, 1, "idi_err: Ch%d: IElen %d of %x exceeds Ind_Length (+%d)\n", chan->No, wlen, code, (pos + wlen) - len); return; } switch(code) { case OAD: if (wlen > sizeof(message->oad)) { pos += wlen; break; } 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++]; eicon_log(ccard, 2, "idi_inf: Ch%d: OAD=(0x%02x,0x%02x) %s\n", chan->No, message->plan, message->screen, message->oad); break; case RDN: if (wlen > sizeof(message->rdn)) { pos += wlen; break; } j = 1; if (wlen) { if (!(buffer[pos++] & 0x80)) { pos++; j = 2; } } for(i=0; i < wlen-j; i++) message->rdn[i] = buffer[pos++]; eicon_log(ccard, 2, "idi_inf: Ch%d: RDN= %s\n", chan->No, message->rdn); break; case CPN: if (wlen > sizeof(message->cpn)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->cpn[i] = buffer[pos++]; eicon_log(ccard, 2, "idi_inf: Ch%d: CPN=(0x%02x) %s\n", chan->No, (__u8)message->cpn[0], message->cpn + 1); break; case DSA: if (wlen > sizeof(message->dsa)) { pos += wlen; break; } pos += 2; for(i=0; i < wlen-2; i++) message->dsa[i] = buffer[pos++]; eicon_log(ccard, 2, "idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa); break; case OSA: if (wlen > sizeof(message->osa)) { pos += wlen; break; } pos += 2; for(i=0; i < wlen-2; i++) message->osa[i] = buffer[pos++]; eicon_log(ccard, 2, "idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa); break; case BC: if (wlen > sizeof(message->bc)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->bc[i] = buffer[pos++]; eicon_log(ccard, 4, "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: if (wlen > sizeof(message->e_bc)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->e_bc[i] = buffer[pos++]; eicon_log(ccard, 4, "idi_inf: Ch%d: ESC/BC=%d\n", chan->No, message->bc[0]); break; case LLC: if (wlen > sizeof(message->llc)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->llc[i] = buffer[pos++]; eicon_log(ccard, 4, "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: if (wlen > sizeof(message->hlc)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->hlc[i] = buffer[pos++]; eicon_log(ccard, 4, "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: if (wlen > sizeof(message->display)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->display[i] = buffer[pos++]; eicon_log(ccard, 4, "idi_inf: Ch%d: Display: %s\n", chan->No, message->display); break; case 0x600|KEY: if (wlen > sizeof(message->keypad)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->keypad[i] = buffer[pos++]; eicon_log(ccard, 4, "idi_inf: Ch%d: Keypad: %s\n", chan->No, message->keypad); break; case NI: case 0x600|NI: if (wlen) { switch(buffer[pos] & 127) { case 0: eicon_log(ccard, 4, "idi_inf: Ch%d: User suspended.\n", chan->No); break; case 1: eicon_log(ccard, 4, "idi_inf: Ch%d: User resumed.\n", chan->No); break; case 2: eicon_log(ccard, 4, "idi_inf: Ch%d: Bearer service change.\n", chan->No); break; default: eicon_log(ccard, 4, "idi_inf: Ch%d: Unknown Notification %x.\n", chan->No, buffer[pos] & 127); } pos += wlen; } break; case PI: case 0x600|PI: if (wlen > 1) { switch(buffer[pos+1] & 127) { case 1: eicon_log(ccard, 4, "idi_inf: Ch%d: Call is not end-to-end ISDN.\n", chan->No); break; case 2: eicon_log(ccard, 4, "idi_inf: Ch%d: Destination address is non ISDN.\n", chan->No); break; case 3: eicon_log(ccard, 4, "idi_inf: Ch%d: Origination address is non ISDN.\n", chan->No); break; case 4: eicon_log(ccard, 4, "idi_inf: Ch%d: Call has returned to the ISDN.\n", chan->No); break; case 5: eicon_log(ccard, 4, "idi_inf: Ch%d: Interworking has occurred.\n", chan->No); break; case 8: eicon_log(ccard, 4, "idi_inf: Ch%d: In-band information available.\n", chan->No); break; default: eicon_log(ccard, 4, "idi_inf: Ch%d: Unknown Progress %x.\n", chan->No, buffer[pos+1] & 127); } } pos += wlen; break; case CAU: if (wlen > sizeof(message->cau)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->cau[i] = buffer[pos++]; memcpy(&chan->cause, &message->cau, 2); eicon_log(ccard, 4, "idi_inf: Ch%d: CAU=%d %d\n", chan->No, message->cau[0],message->cau[1]); break; case 0x800|CAU: if (wlen > sizeof(message->e_cau)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->e_cau[i] = buffer[pos++]; eicon_log(ccard, 4, "idi_inf: Ch%d: ECAU=%d %d\n", chan->No, message->e_cau[0],message->e_cau[1]); break; case 0x800|CHI: if (wlen > sizeof(message->e_chi)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->e_chi[i] = buffer[pos++]; eicon_log(ccard, 4, "idi_inf: Ch%d: ESC/CHI=%d\n", chan->No, message->e_cau[0]); break; case 0x800|0x7a: pos ++; message->e_mt=buffer[pos++]; eicon_log(ccard, 4, "idi_inf: Ch%d: EMT=0x%x\n", chan->No, message->e_mt); break; case DT: if (wlen > sizeof(message->dt)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->dt[i] = buffer[pos++]; eicon_log(ccard, 4, "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: if (wlen > sizeof(message->sin)) { pos += wlen; break; } for(i=0; i < wlen; i++) message->sin[i] = buffer[pos++]; eicon_log(ccard, 2, "idi_inf: Ch%d: SIN=%d %d\n", chan->No, message->sin[0],message->sin[1]); break; case 0x600|CPS: eicon_log(ccard, 2, "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; eicon_log(ccard, 2, "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: eicon_log(ccard, 2, "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) { eicon_log(ccard, 1, "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; eicon_log(ccard, 6, "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; } } /********************* FAX stuff ***************************/ #ifdef CONFIG_ISDN_TTY_FAX int idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer) { /* TODO , code follows */ return(0); } /* send fax struct */ int idi_send_edata(eicon_card *card, eicon_chan *chan) { /* TODO , code follows */ return (0); } void idi_parse_edata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len) { /* TODO , code follows */ } void idi_fax_send_header(eicon_card *card, eicon_chan *chan, int header) { /* TODO , code follows */ } void idi_fax_cmd(eicon_card *card, eicon_chan *chan) { /* TODO , code follows */ } void idi_edata_rcveop(eicon_card *card, eicon_chan *chan) { /* TODO , code follows */ } void idi_reset_fax_stat(eicon_chan *chan) { /* TODO , code follows */ } void idi_edata_action(eicon_card *ccard, eicon_chan *chan, char *buffer, int len) { /* TODO , code follows */ } void fax_put_rcv(eicon_card *ccard, eicon_chan *chan, u_char *Data, int len) { /* TODO , code follows */ } void idi_faxdata_rcv(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb) { /* TODO , code follows */ } int idi_fax_send_outbuf(eicon_card *ccard, eicon_chan *chan, eicon_OBJBUFFER *OutBuf) { /* TODO , code follows */ return(0); } int idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb) { /* TODO , code follows */ return(0); } void idi_fax_hangup(eicon_card *ccard, eicon_chan *chan) { /* TODO , code follows */ } #endif /******** FAX ********/ int idi_send_udata(eicon_card *card, eicon_chan *chan, int UReq, u_char *buffer, int len) { struct sk_buff *skb; struct sk_buff *skb2; eicon_REQ *reqbuf; eicon_chan_ptr *chan2; if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) { eicon_log(card, 1, "idi_snd: Ch%d: send udata on state %d !\n", chan->No, chan->fsm_state); return -ENODEV; } eicon_log(card, 8, "idi_snd: Ch%d: udata 0x%x: %d %d %d %d\n", chan->No, UReq, buffer[0], buffer[1], buffer[2], buffer[3]); skb = alloc_skb(sizeof(eicon_REQ) + len + 1, GFP_ATOMIC); skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); if ((!skb) || (!skb2)) { eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_udata()\n", chan->No); if (skb) dev_kfree_skb(skb); if (skb2) dev_kfree_skb(skb2); return -ENOMEM; } chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); chan2->ptr = chan; reqbuf = (eicon_REQ *)skb_put(skb, 1 + len + sizeof(eicon_REQ)); reqbuf->Req = IDI_N_UDATA; reqbuf->ReqCh = 0; reqbuf->ReqId = 1; reqbuf->XBuffer.length = len + 1; reqbuf->XBuffer.P[0] = UReq; memcpy(&reqbuf->XBuffer.P[1], buffer, len); reqbuf->Reference = 1; /* Net Entity */ skb_queue_tail(&chan->e.X, skb); skb_queue_tail(&card->sndq, skb2); eicon_schedule_tx(card); return (0); } void idi_audio_cmd(eicon_card *ccard, eicon_chan *chan, int cmd, u_char *value) { u_char buf[6]; struct enable_dtmf_s *dtmf_buf = (struct enable_dtmf_s *)buf; if ((!ccard) || (!chan)) return; memset(buf, 0, 6); switch(cmd) { case ISDN_AUDIO_SETDD: if (value[0]) { dtmf_buf->tone = (__u16) (value[1] * 5); dtmf_buf->gap = (__u16) (value[1] * 5); idi_send_udata(ccard, chan, DSP_UDATA_REQUEST_ENABLE_DTMF_RECEIVER, buf, 4); } else { idi_send_udata(ccard, chan, DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER, buf, 0); } break; } } 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"}; static u_char dtmf_code[] = { '1','4','7','*','2','5','8','0','3','6','9','#','A','B','C','D' }; if ((!ccard) || (!chan)) return; switch (buffer[0]) { case DSP_UDATA_INDICATION_SYNC: eicon_log(ccard, 16, "idi_ind: Ch%d: UDATA_SYNC time %d\n", chan->No, p->time); break; case DSP_UDATA_INDICATION_DCD_OFF: eicon_log(ccard, 8, "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); } eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DCD_ON time %d\n", chan->No, p->time); eicon_log(ccard, 8, "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: eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_CTS_OFF time %d\n", chan->No, p->time); break; case DSP_UDATA_INDICATION_CTS_ON: eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_CTS_ON time %d\n", chan->No, p->time); eicon_log(ccard, 8, "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: eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]); break; case DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED: eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DTMF_REC '%c'\n", chan->No, dtmf_code[buffer[1]]); cmd.driver = ccard->myid; cmd.command = ISDN_STAT_AUDIO; cmd.parm.num[0] = ISDN_AUDIO_DTMF; cmd.parm.num[1] = dtmf_code[buffer[1]]; cmd.arg = chan->No; ccard->interface.statcallb(&cmd); break; default: eicon_log(ccard, 8, "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 dlev; int free_buff; ulong flags; struct sk_buff *skb2; eicon_IND *ind = (eicon_IND *)skb->data; eicon_chan *chan; idi_ind_message message; isdn_ctrl cmd; if (!ccard) return; if ((chan = ccard->IdTable[ind->IndId]) == NULL) { dev_kfree_skb(skb); return; } if (ind->Ind != 8) dlev = 144; else dlev = 128; eicon_log(ccard, dlev, "idi_hdl: Ch%d: Ind=%d Id=%x 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: eicon_log(ccard, 8, "idi_ind: Ch%d: Hangup\n", chan->No); while((skb2 = skb_dequeue(&chan->e.X))) { dev_kfree_skb(skb2); } save_flags(flags); cli(); chan->e.busy = 0; chan->queued = 0; chan->waitq = 0; chan->waitpq = 0; restore_flags(flags); 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; #ifdef CONFIG_ISDN_TTY_FAX if (!chan->e.B2Id) chan->fax = 0; #endif if ((chan->fsm_state == EICON_STATE_ACTIVE) || (chan->fsm_state == EICON_STATE_WMCONN)) { chan->fsm_state = EICON_STATE_NULL; } else { if (chan->e.B2Id) idi_do_req(ccard, chan, REMOVE, 1); chan->fsm_state = EICON_STATE_NULL; 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: eicon_log(ccard, 8, "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); if (strlen(message.osa)) { strcat(chan->oad, "."); strcat(chan->oad, message.osa); } 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); chan->fsm_state = EICON_STATE_NULL; break; case 1: /* alert */ eicon_log(ccard, 8, "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 */ eicon_log(ccard, 8, "idi_req: Ch%d: Call Reject\n", chan->No); idi_do_req(ccard, chan, REJECT, 0); break; case 3: /* incomplete number */ eicon_log(ccard, 8, "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: eicon_log(ccard, 8, "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: eicon_log(ccard, 8, "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); switch(chan->l2prot) { case ISDN_PROTO_L2_FAX: #ifdef CONFIG_ISDN_TTY_FAX if (chan->fax) chan->fax->phase = ISDN_FAX_PHASE_A; #endif break; case ISDN_PROTO_L2_MODEM: /* do nothing, wait for connect */ break; default: idi_do_req(ccard, chan, IDI_N_CONNECT, 1); } } else idi_hangup(ccard, chan); break; case CALL_CON: eicon_log(ccard, 8, "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); /* check if old NetID has been removed */ if (chan->e.B2Id) { eicon_log(ccard, 1, "idi_err: Ch%d: Old NetID %x was not removed.\n", chan->No, chan->e.B2Id); idi_do_req(ccard, chan, REMOVE, 1); } idi_do_req(ccard, chan, ASSIGN, 1); idi_do_req(ccard, chan, IDI_N_CONNECT, 1); #ifdef CONFIG_ISDN_TTY_FAX if (chan->l2prot == ISDN_PROTO_L2_FAX) { if (chan->fax) chan->fax->phase = ISDN_FAX_PHASE_A; } #endif } else idi_hangup(ccard, chan); break; case AOC_IND: eicon_log(ccard, 8, "idi_ind: Ch%d: Advice of Charge\n", chan->No); break; default: eicon_log(ccard, 8, "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: eicon_log(ccard, 16, "idi_ind: Ch%d: N_Connect_Ack\n", chan->No); if (chan->l2prot == ISDN_PROTO_L2_MODEM) { chan->fsm_state = EICON_STATE_WMCONN; break; } if (chan->l2prot == ISDN_PROTO_L2_FAX) { #ifdef CONFIG_ISDN_TTY_FAX chan->fsm_state = EICON_STATE_ACTIVE; idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); if (chan->fax) { if (chan->fax->phase == ISDN_FAX_PHASE_B) { idi_fax_send_header(ccard, chan, 2); cmd.driver = ccard->myid; cmd.command = ISDN_STAT_FAXIND; cmd.arg = chan->No; chan->fax->r_code = ISDN_TTY_FAX_DCS; ccard->interface.statcallb(&cmd); } } else { eicon_log(ccard, 1, "idi_ind: N_CONNECT_ACK with NULL fax struct, ERROR\n"); } #endif 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: eicon_log(ccard, 16,"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_FAX) { break; } 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: eicon_log(ccard, 16, "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); } #ifdef CONFIG_ISDN_TTY_FAX if (chan->l2prot == ISDN_PROTO_L2_FAX) { idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); idi_fax_hangup(ccard, chan); } #endif save_flags(flags); cli(); chan->queued = 0; chan->waitq = 0; chan->waitpq = 0; restore_flags(flags); idi_do_req(ccard, chan, HANGUP, 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); chan->fsm_state = EICON_STATE_NULL; } #ifdef CONFIG_ISDN_TTY_FAX chan->fax = 0; #endif break; case IDI_N_DISC_ACK: eicon_log(ccard, 16, "idi_ind: Ch%d: N_DISC_ACK\n", chan->No); #ifdef CONFIG_ISDN_TTY_FAX if (chan->l2prot == ISDN_PROTO_L2_FAX) { idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); idi_fax_hangup(ccard, chan); } #endif break; case IDI_N_DATA_ACK: eicon_log(ccard, 16, "idi_ind: Ch%d: N_DATA_ACK\n", chan->No); break; case IDI_N_DATA: skb_pull(skb, sizeof(eicon_IND) - 1); eicon_log(ccard, 128, "idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len); if (chan->l2prot == ISDN_PROTO_L2_FAX) { #ifdef CONFIG_ISDN_TTY_FAX idi_faxdata_rcv(ccard, chan, skb); #endif } else { 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; #ifdef CONFIG_ISDN_TTY_FAX case IDI_N_EDATA: idi_edata_action(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); break; #endif default: eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind); } } else { eicon_log(ccard, 1, "idi_ind: Ch%d: Ind is neither SIG nor NET !\n", chan->No); } if (free_buff) dev_kfree_skb(skb); } int idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack) { ulong flags; isdn_ctrl cmd; if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) { /* I dont know why this happens, just ignoring this RC */ eicon_log(ccard, 16, "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); return 1; } /* Management Interface */ if (chan->No == ccard->nchannels) { /* Managementinterface: changing state */ if (chan->e.Req == 0x04) chan->fsm_state = 1; } /* Remove an Id */ if (chan->e.Req == REMOVE) { if (ack->Reference != chan->e.ref) { eicon_log(ccard, 16, "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No, ack->Reference, chan->e.ref); return 0; } save_flags(flags); cli(); ccard->IdTable[ack->RcId] = NULL; eicon_log(ccard, 16, "idi_ack: Ch%d: Removed : Id=%x 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; restore_flags(flags); return 1; } /* Signal layer */ if (!chan->e.ReqCh) { eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No, ack->RcId, ack->RcCh, ack->Reference); } else { /* Network layer */ switch(chan->e.Req & 0x0f) { case IDI_N_MDATA: case IDI_N_DATA: if ((chan->e.Req & 0x0f) == IDI_N_DATA) { if (chan->queued) { cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BSENT; cmd.arg = chan->No; cmd.parm.length = chan->waitpq; ccard->interface.statcallb(&cmd); } save_flags(flags); cli(); chan->waitpq = 0; restore_flags(flags); #ifdef CONFIG_ISDN_TTY_FAX if (chan->l2prot == ISDN_PROTO_L2_FAX) { if (((chan->queued - chan->waitq) < 1) && (chan->fax2.Eop)) { chan->fax2.Eop = 0; if (chan->fax) { cmd.driver = ccard->myid; cmd.command = ISDN_STAT_FAXIND; cmd.arg = chan->No; chan->fax->r_code = ISDN_TTY_FAX_SENT; ccard->interface.statcallb(&cmd); } else { eicon_log(ccard, 1, "idi_ack: Sent with NULL fax struct, ERROR\n"); } } } #endif } save_flags(flags); cli(); chan->queued -= chan->waitq; if (chan->queued < 0) chan->queued = 0; restore_flags(flags); break; default: eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No, ack->RcId, ack->RcCh, ack->Reference); } } return 1; } void idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) { int j; ulong flags; eicon_RC *ack = (eicon_RC *)skb->data; eicon_chan *chan; isdn_ctrl cmd; int dCh = -1; if (!ccard) return; save_flags(flags); cli(); if ((chan = ccard->IdTable[ack->RcId]) != NULL) dCh = chan->No; restore_flags(flags); switch (ack->Rc) { case OK_FC: case N_FLOW_CONTROL: case ASSIGN_RC: eicon_log(ccard, 1, "idi_ack: Ch%d: unhandled RC 0x%x\n", dCh, ack->Rc); break; case READY_INT: case TIMER_INT: /* we do nothing here */ break; case OK: if (!chan) { eicon_log(ccard, 1, "idi_ack: Ch%d: OK on chan without Id\n", dCh); break; } if (!idi_handle_ack_ok(ccard, chan, ack)) chan = NULL; break; case ASSIGN_OK: if (chan) { eicon_log(ccard, 1, "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n", chan->No, chan->e.D3Id, chan->e.B2Id); } save_flags(flags); cli(); 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; eicon_log(ccard, 16, "idi_ack: Ch%d: Id %x assigned (%s)\n", j, ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig"); break; } } restore_flags(flags); if (j > ccard->nchannels) { eicon_log(ccard, 24, "idi_ack: Ch??: ref %d not found for Id %d\n", ack->Reference, ack->RcId); } break; case OUT_OF_RESOURCES: case UNKNOWN_COMMAND: case WRONG_COMMAND: case WRONG_ID: case WRONG_CH: case UNKNOWN_IE: case WRONG_IE: default: eicon_log(ccard, 1, "eicon_ack: Ch%d: Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n", dCh, ack->Rc, ack->RcId, ack->RcCh, (chan) ? chan->e.Req : -1); if (!chan) { eicon_log(ccard, 1, "idi_ack: Ch%d: Not OK !! on chan without Id\n", dCh); break; } if (dCh == ccard->nchannels) { /* Management */ chan->fsm_state = 2; } else if (dCh >= 0) { /* 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); } } save_flags(flags); cli(); if (chan) chan->e.busy = 0; restore_flags(flags); dev_kfree_skb(skb); eicon_schedule_tx(ccard); } int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que) { 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 ((!card) || (!chan)) return -1; if (chan->fsm_state != EICON_STATE_ACTIVE) { eicon_log(card, 1, "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; eicon_log(card, 128, "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 ((!xmit_skb) || (!skb2)) { restore_flags(flags); eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No); if (xmit_skb) dev_kfree_skb(skb); if (skb2) dev_kfree_skb(skb2); 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; } if (que) 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)) { eicon_log(card, 1, "idi_err: alloc_skb failed in manage_assign()\n"); if (skb) dev_kfree_skb(skb); if (skb2) dev_kfree_skb(skb2); 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)) { eicon_log(card, 1, "idi_err: alloc_skb failed in manage_remove()\n"); if (skb) dev_kfree_skb(skb); if (skb2) dev_kfree_skb(skb2); 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))) { eicon_log(card, 1, "idi_err: alloc_manifbuf failed\n"); chan->e.D3Id = 0; return -ENOMEM; } if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) { kfree(manbuf); 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)) { eicon_log(card, 1, "idi_err_manif: alloc_skb failed in manage()\n"); if (skb) dev_kfree_skb(skb); if (skb2) dev_kfree_skb(skb2); 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))) { kfree(manbuf); chan->e.D3Id = 0; return(ret); } if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) { kfree(manbuf); chan->e.D3Id = 0; return -EFAULT; } kfree(manbuf); chan->e.D3Id = 0; return(0); }