diff options
Diffstat (limited to 'drivers/isdn/eicon/eicon_idi.c')
-rw-r--r-- | drivers/isdn/eicon/eicon_idi.c | 648 |
1 files changed, 551 insertions, 97 deletions
diff --git a/drivers/isdn/eicon/eicon_idi.c b/drivers/isdn/eicon/eicon_idi.c index a28f316c1..af9c30483 100644 --- a/drivers/isdn/eicon/eicon_idi.c +++ b/drivers/isdn/eicon/eicon_idi.c @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.c,v 1.9 1999/03/29 11:19:42 armin Exp $ +/* $Id: eicon_idi.c,v 1.15 1999/08/28 21:32:50 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * IDI interface @@ -21,6 +21,33 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.c,v $ + * 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 <xfl23@dial.pipex.com> + * + * 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. @@ -61,6 +88,7 @@ * */ +#include <linux/config.h> #define __NO_VERSION__ #include "eicon.h" #include "eicon_idi.h" @@ -68,7 +96,7 @@ #undef EICON_FULL_SERVICE_OKTETT -char *eicon_idi_revision = "$Revision: 1.9 $"; +char *eicon_idi_revision = "$Revision: 1.15 $"; eicon_manifbuf *manbuf; @@ -86,11 +114,15 @@ static char HLC_faxg3[2] = { 0x91, 0x84 }; 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; @@ -133,10 +165,25 @@ idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan) case ISDN_PROTO_L2_MODEM: reqbuf->XBuffer.P[l++] = 2; 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; @@ -210,6 +257,20 @@ idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan) 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; @@ -232,7 +293,11 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "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; } @@ -241,7 +306,7 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) 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"); + printk(KERN_DEBUG "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig"); if (layer) cmd |= 0x700; switch(cmd) { case ASSIGN: @@ -282,6 +347,8 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) default: if (DebugVar & 1) printk(KERN_ERR "idi_req: Ch%d: Unknown request\n", chan->No); + dev_kfree_skb(skb); + dev_kfree_skb(skb2); return(-1); } @@ -358,6 +425,9 @@ idi_hangup(eicon_card *card, eicon_chan *chan) chan->fsm_state = EICON_STATE_NULL; if (DebugVar & 8) printk(KERN_DEBUG"idi_req: Ch%d: Hangup\n", chan->No); +#ifdef CONFIG_ISDN_TTY_FAX + chan->fax = 0; +#endif return(0); } @@ -389,7 +459,11 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "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; } @@ -403,14 +477,14 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, reqbuf->XBuffer.P[l++] = CPN; reqbuf->XBuffer.P[l++] = strlen(phone) + 1; - reqbuf->XBuffer.P[l++] = 0xc1; + reqbuf->XBuffer.P[l++] = 0x81; 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; + reqbuf->XBuffer.P[l++] = 0x80; for(i=0; i<strlen(eazmsn);i++) reqbuf->XBuffer.P[l++] = eazmsn[i]; @@ -472,6 +546,20 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, 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 */ @@ -813,6 +901,195 @@ idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *si1, unsigned ch } } +/********************* 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)) { + if (DebugVar & 1) + printk(KERN_DEBUG"idi_snd: Ch%d: send udata on state %d !\n", chan->No, chan->fsm_state); + return -ENODEV; + } + if (DebugVar & 8) + printk(KERN_DEBUG"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)) { + if (DebugVar & 1) + printk(KERN_WARNING "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; + + 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) { @@ -822,6 +1099,9 @@ idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int {"", "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' + }; switch (buffer[0]) { case DSP_UDATA_INDICATION_SYNC: @@ -863,6 +1143,17 @@ idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int if (DebugVar & 8) printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]); break; + case DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED: + if (DebugVar & 8) + printk(KERN_DEBUG"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: if (DebugVar & 8) printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]); @@ -887,7 +1178,7 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) 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, + printk(KERN_DEBUG "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); } @@ -921,6 +1212,10 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) cmd.command = ISDN_STAT_DHUP; ccard->interface.statcallb(&cmd); eicon_idi_listen_req(ccard, chan); +#ifdef CONFIG_ISDN_TTY_FAX + if (!chan->e.B2Id) + chan->fax = 0; +#endif break; case INDICATE_IND: if (DebugVar & 8) @@ -994,9 +1289,17 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) cmd.command = ISDN_STAT_DCONN; cmd.arg = chan->No; ccard->interface.statcallb(&cmd); - idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + if (chan->l2prot != ISDN_PROTO_L2_FAX) { + idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + } +#ifdef CONFIG_ISDN_TTY_FAX + else { + if (chan->fax) + chan->fax->phase = ISDN_FAX_PHASE_A; + } +#endif } else - idi_hangup(ccard, chan); + idi_hangup(ccard, chan); break; case CALL_CON: if (DebugVar & 8) @@ -1009,6 +1312,12 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) ccard->interface.statcallb(&cmd); 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; @@ -1038,6 +1347,27 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) 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 { + if (DebugVar & 1) + printk(KERN_DEBUG "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; @@ -1048,6 +1378,9 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) 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_FAX) { + break; + } if (chan->l2prot == ISDN_PROTO_L2_MODEM) { chan->fsm_state = EICON_STATE_WMCONN; break; @@ -1065,19 +1398,35 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) 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 chan->queued = 0; chan->waitq = 0; chan->waitpq = 0; + 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); } +#ifdef CONFIG_ISDN_TTY_FAX + chan->fax = 0; +#endif break; case IDI_N_DISC_ACK: if (DebugVar & 16) printk(KERN_DEBUG"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: if (DebugVar & 16) @@ -1087,12 +1436,23 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) 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; + 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: if (DebugVar & 8) printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind); @@ -1105,90 +1465,138 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) if (free_buff) dev_kfree_skb(skb); } -void -idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) +int +idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack) { - 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); - } + if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) { + /* I dont know why this happens, just ignoring this RC */ + 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); + 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) { + 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); + return 0; } - } - 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) { + ccard->IdTable[ack->RcId] = NULL; + if (DebugVar & 16) + printk(KERN_DEBUG "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; + return 1; + } + + /* Signal layer */ + if (!chan->e.ReqCh) { + if (DebugVar & 16) + printk(KERN_DEBUG "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); + } + chan->waitpq = 0; +#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_BSENT; + cmd.command = ISDN_STAT_FAXIND; cmd.arg = chan->No; - cmd.parm.length = chan->waitpq; - chan->waitpq = 0; + chan->fax->r_code = ISDN_TTY_FAX_SENT; 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 & 1) + printk(KERN_DEBUG "idi_ack: Sent with NULL fax struct, ERROR\n"); + } + } } - } - 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); +#endif } + chan->queued -= chan->waitq; + if (chan->queued < 0) chan->queued = 0; + break; + default: + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No, + ack->RcId, ack->RcCh, ack->Reference); + } + } + return 1; +} - 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; +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; + int dCh = -1; + + if ((chan = ccard->IdTable[ack->RcId]) != NULL) + dCh = chan->No; + + + switch (ack->Rc) { + case OK_FC: + case N_FLOW_CONTROL: + case ASSIGN_RC: + if (DebugVar & 1) + printk(KERN_ERR "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) { + if (DebugVar & 1) + printk(KERN_ERR "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) { + if (DebugVar & 1) + printk(KERN_ERR "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n", + chan->No, chan->e.D3Id, chan->e.B2Id); } - } - else { for(j = 0; j < ccard->nchannels + 1; j++) { if (ccard->bch[j].e.ref == ack->Reference) { if (!ccard->bch[j].e.ReqCh) @@ -1199,7 +1607,7 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) 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, + printk(KERN_DEBUG"idi_ack: Ch%d: Id %x assigned (%s)\n", j, ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig"); break; } @@ -1209,14 +1617,39 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) printk(KERN_DEBUG"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: + if (DebugVar & 1) + printk(KERN_ERR "eicon_ack: Ch%d: Not OK !!: Rc=%d Id=%x Ch=%d\n", dCh, + ack->Rc, ack->RcId, ack->RcCh); + 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); + } } - dev_kfree_skb(skb); - eicon_schedule_tx(ccard); + if (chan) + chan->e.busy = 0; + dev_kfree_skb(skb); + eicon_schedule_tx(ccard); } int -idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) +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; @@ -1240,6 +1673,7 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) 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) { @@ -1249,10 +1683,14 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC); skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); - if ((!skb) || (!skb2)) { + if ((!xmit_skb) || (!skb2)) { restore_flags(flags); if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "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; } @@ -1278,7 +1716,8 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) offset += plen; } - chan->queued += len; + if (que) + chan->queued += len; restore_flags(flags); eicon_schedule_tx(card); dev_kfree_skb(skb); @@ -1286,7 +1725,6 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) } - int eicon_idi_manage_assign(eicon_card *card) { @@ -1303,7 +1741,11 @@ eicon_idi_manage_assign(eicon_card *card) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err: alloc_skb failed in manage_assign()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1342,7 +1784,11 @@ eicon_idi_manage_remove(eicon_card *card) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err: alloc_skb failed in manage_remove()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1379,7 +1825,8 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) chan = &(card->bch[card->nchannels]); - if (chan->e.D3Id) return -EBUSY; + if (chan->e.D3Id) + return -EBUSY; chan->e.D3Id = 1; while((skb2 = skb_dequeue(&chan->e.X))) dev_kfree_skb(skb2); @@ -1409,6 +1856,7 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) return -ENOMEM; } if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) { + kfree(manbuf); chan->e.D3Id = 0; return -EFAULT; } @@ -1418,7 +1866,11 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err_manif: alloc_skb failed\n"); + printk(KERN_WARNING "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; @@ -1464,11 +1916,13 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) } 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; } |