patch-2.2.19 linux/drivers/isdn/eicon/eicon_idi.c

Next file: linux/drivers/isdn/eicon/eicon_idi.h
Previous file: linux/drivers/isdn/eicon/eicon_dsp.h
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.18/drivers/isdn/eicon/eicon_idi.c linux/drivers/isdn/eicon/eicon_idi.c
@@ -1,4 +1,4 @@
-/* $Id: eicon_idi.c,v 1.33 2000/03/06 15:45:17 armin Exp $
+/* $Id: eicon_idi.c,v 1.41.6.1 2001/02/10 14:44:09 kai Exp $
  *
  * ISDN lowlevel-module for Eicon active cards.
  *        IDI interface 
@@ -25,135 +25,6 @@
  * 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.33  2000/03/06 15:45:17  armin
- * Fixed incomplete number handling with BRI PtP connection.
- *
- * Revision 1.32  2000/03/04 17:04:21  armin
- * Fix of statemachine, B-connect before D-connect,
- * thanks to Helmut Adams <adams@ipcon.de>
- * Minor change in send-data packet handling.
- *
- * Revision 1.31  2000/02/22 16:26:40  armin
- * Fixed membase error message.
- * Fixed missing log buffer struct.
- *
- * Revision 1.30  2000/02/16 16:08:46  armin
- * Fixed virtual channel handling of IDI.
- *
- * Revision 1.29  2000/01/23 21:21:23  armin
- * Added new trace capability and some updates.
- * DIVA Server BRI now supports data for ISDNLOG.
- *
- * Revision 1.28  2000/01/20 19:55:34  keil
- * Add FAX Class 1 support
- *
- * Revision 1.27  1999/11/29 13:12:03  armin
- * Autoconnect on L2_TRANS doesn't work with link_level correctly,
- * changed back to former mode.
- *
- * Revision 1.26  1999/11/25 11:43:27  armin
- * Fixed statectrl and connect message.
- * X.75 fix and HDLC/transparent with autoconnect.
- * Minor cleanup.
- *
- * Revision 1.25  1999/11/18 20:30:55  armin
- * removed old workaround for ISA cards.
- *
- * Revision 1.24  1999/10/26 21:15:33  armin
- * using define for checking phone number len to avoid buffer overflow.
- *
- * Revision 1.23  1999/10/11 18:13:25  armin
- * Added fax capabilities for Eicon Diva Server cards.
- *
- * Revision 1.22  1999/10/08 22:09:33  armin
- * Some fixes of cards interface handling.
- * Bugfix of NULL pointer occurence.
- * Changed a few log outputs.
- *
- * Revision 1.21  1999/09/26 14:17:53  armin
- * Improved debug and log via readstat()
- *
- * 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 <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.
- *
- * 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 <linux/config.h>
@@ -161,25 +32,14 @@
 #include "eicon.h"
 #include "eicon_idi.h"
 #include "eicon_dsp.h"
+#include "uxio.h"
 
 #undef EICON_FULL_SERVICE_OKTETT
 
-char *eicon_idi_revision = "$Revision: 1.33 $";
+char *eicon_idi_revision = "$Revision: 1.41.6.1 $";
 
 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);
@@ -209,7 +69,7 @@
 	reqbuf->XBuffer.P[l++] = 0; /* end */
 	reqbuf->Req = ASSIGN;
 	reqbuf->ReqCh = 0;
-	reqbuf->ReqId = 0;
+	reqbuf->ReqId = DSIG_ID;
 	reqbuf->XBuffer.length = l;
 	reqbuf->Reference = 0; /* Sig Entity */
   }
@@ -221,6 +81,9 @@
 	reqbuf->XBuffer.P[l++] = LLC;
 	reqbuf->XBuffer.P[l++] = 2;
 	switch(chan->l2prot) {
+		case ISDN_PROTO_L2_V11096:
+		case ISDN_PROTO_L2_V11019:
+		case ISDN_PROTO_L2_V11038:
 		case ISDN_PROTO_L2_TRANS:
 			reqbuf->XBuffer.P[l++] = 2; /* transparent */
 			break;
@@ -262,7 +125,7 @@
 	reqbuf->XBuffer.P[l++] = 0; /* end */
 	reqbuf->Req = ASSIGN;
 	reqbuf->ReqCh = 0;
-	reqbuf->ReqId = 0x20;
+	reqbuf->ReqId = NL_ID;
 	reqbuf->XBuffer.length = l;
 	reqbuf->Reference = 1; /* Net Entity */
   }
@@ -282,6 +145,21 @@
 }
 
 int
+idi_put_suspend_req(eicon_REQ *reqbuf, eicon_chan *chan)
+{
+	reqbuf->Req = SUSPEND;
+	reqbuf->ReqCh = 0;
+	reqbuf->ReqId = 1;
+	reqbuf->XBuffer.P[0] = CAI;
+	reqbuf->XBuffer.P[1] = 1;
+	reqbuf->XBuffer.P[2] = chan->No;
+	reqbuf->XBuffer.P[3] = 0;
+	reqbuf->XBuffer.length = 4;
+	reqbuf->Reference = 0; /* Sig Entity */
+   return(0);
+}
+
+int
 idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan)
 {
 	int l = 9;
@@ -295,7 +173,7 @@
 	reqbuf->XBuffer.P[4] = 0;
 	reqbuf->XBuffer.P[5] = 0;
 	reqbuf->XBuffer.P[6] = 32;
-	reqbuf->XBuffer.P[7] = 3;
+	reqbuf->XBuffer.P[7] = 0;
 	switch(chan->l2prot) {
 		case ISDN_PROTO_L2_X75I:
 		case ISDN_PROTO_L2_X75UI:
@@ -391,6 +269,12 @@
 		case HANGUP:
 			idi_put_req(reqbuf, HANGUP, 0, 0);
 			break;
+		case SUSPEND:
+			idi_put_suspend_req(reqbuf, chan);
+			break;
+		case RESUME:
+			idi_put_req(reqbuf, RESUME, 0 ,0);
+			break;
 		case REJECT:
 			idi_put_req(reqbuf, REJECT, 0 ,0);
 			break;
@@ -400,17 +284,20 @@
 		case CALL_RES:
 			idi_call_res_req(reqbuf, chan);
 			break;
-		case IDI_N_CONNECT|0x700:
-			idi_put_req(reqbuf, IDI_N_CONNECT, 1, 0);
+		case CALL_HOLD:
+			idi_put_req(reqbuf, CALL_HOLD, 0, 0);
+			break;
+		case N_CONNECT|0x700:
+			idi_put_req(reqbuf, N_CONNECT, 1, 0);
 			break;
-		case IDI_N_CONNECT_ACK|0x700:
-			idi_put_req(reqbuf, IDI_N_CONNECT_ACK, 1, 0);
+		case N_CONNECT_ACK|0x700:
+			idi_put_req(reqbuf, N_CONNECT_ACK, 1, 0);
 			break;
-		case IDI_N_DISC|0x700:
-			idi_put_req(reqbuf, IDI_N_DISC, 1, chan->e.IndCh);
+		case N_DISC|0x700:
+			idi_put_req(reqbuf, N_DISC, 1, chan->e.IndCh);
 			break;
-		case IDI_N_DISC_ACK|0x700:
-			idi_put_req(reqbuf, IDI_N_DISC_ACK, 1, chan->e.IndCh);
+		case N_DISC_ACK|0x700:
+			idi_put_req(reqbuf, N_DISC_ACK, 1, chan->e.IndCh);
 			break;
 		default:
 			eicon_log(card, 1, "idi_req: Ch%d: Unknown request\n", chan->No);
@@ -492,7 +379,7 @@
 
 	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, N_DISC, 1);
 	}
 	if (chan->e.B2Id) idi_do_req(card, chan, REMOVE, 1);
 	if (chan->fsm_state != EICON_STATE_NULL) {
@@ -508,6 +395,32 @@
 }
 
 int
+capipmsg(eicon_card *card, eicon_chan *chan, capi_msg *cm)
+{
+	if ((cm->para[0] != 3) || (cm->para[1] != 0))
+		return -1;
+	if (cm->para[2] < 3)
+		return -1;
+	if (cm->para[4] != 0)
+		return -1;
+	switch(cm->para[3]) {
+		case 4: /* Suspend */
+			eicon_log(card, 8, "idi_req: Ch%d: Call Suspend\n", chan->No);
+			if (cm->para[5]) {
+				idi_do_req(card, chan, SUSPEND, 0);
+			} else {
+				idi_do_req(card, chan, CALL_HOLD, 0);
+			}
+			break;
+		case 5: /* Resume */
+			eicon_log(card, 8, "idi_req: Ch%d: Call Resume\n", chan->No);
+			idi_do_req(card, chan, RESUME, 0);
+			break;
+        }
+	return 0;
+}
+
+int
 idi_connect_res(eicon_card *card, eicon_chan *chan)
 {
 	if ((!card) || (!chan))
@@ -612,7 +525,14 @@
 			reqbuf->XBuffer.P[l++] = *sub++ & 0x7f;
 	}
 
-	if ((tmp = idi_si2bc(si1, si2, bc, hlc)) > 0) {
+	if (si2 > 2) {
+		reqbuf->XBuffer.P[l++] = SHIFT|6;
+		reqbuf->XBuffer.P[l++] = SIN;
+		reqbuf->XBuffer.P[l++] = 2;
+		reqbuf->XBuffer.P[l++] = si1;
+		reqbuf->XBuffer.P[l++] = si2;
+	}
+	else 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++) 
@@ -632,7 +552,7 @@
 	reqbuf->XBuffer.P[l++] = 0;
 	reqbuf->XBuffer.P[l++] = 0;
 	reqbuf->XBuffer.P[l++] = 32;
-	reqbuf->XBuffer.P[l++] = 3;
+	reqbuf->XBuffer.P[l++] = 0;
         switch(chan->l2prot) {
 		case ISDN_PROTO_L2_X75I:
 		case ISDN_PROTO_L2_X75UI:
@@ -859,7 +779,7 @@
 				}
 				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],
+				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:
@@ -869,7 +789,7 @@
 				}
 				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,
+				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;
@@ -1061,31 +981,55 @@
 }
 
 void
-idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *si1, unsigned char *si2)
+idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *sin, unsigned char *si1, unsigned char *si2)
 {
-  si1[0] = 0;
-  si2[0] = 0;
-  if (memcmp(bc, BC_Speech, 3) == 0) {		/* Speech */
-	si1[0] = 1;
+	si1[0] = 0;
+	si2[0] = 0;
+
+	switch (bc[0] & 0x7f) {
+		case 0x00: /* Speech */
+			si1[0] = 1;
 #ifdef EICON_FULL_SERVICE_OKTETT
-	si2[0] = 1;
+			si1[0] = sin[0];
+			si2[0] = sin[1];
 #endif
-  }
-  if (memcmp(bc, BC_31khz, 3) == 0) {		/* 3.1kHz audio */
-	si1[0] = 1;
+			break;
+		case 0x10: /* 3.1 Khz 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;
-	}
+			si1[0] = sin[0];
+			si2[0] = sin[1];
 #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;
-  }
+			break;
+		case 0x08: /* Unrestricted digital information */
+			si1[0] = 7;
+			si2[0] = sin[1];
+			break;
+		case 0x09: /* Restricted digital information */
+			si1[0] = 2;
+			break;
+		case 0x11:
+			/* Unrestr. digital information  with
+			 * tones/announcements ( or 7 kHz audio
+			 */
+			si1[0] = 3;
+			break;
+		case 0x18: /* Video */
+			si1[0] = 4;
+			break;
+	}
+	switch (bc[1] & 0x7f) {
+		case 0x40: /* packed mode */
+			si1[0] = 8;
+			break;
+		case 0x10: /* 64 kbit */
+		case 0x11: /* 2*64 kbit */
+		case 0x13: /* 384 kbit */
+		case 0x15: /* 1536 kbit */
+		case 0x17: /* 1920 kbit */
+			/* moderate = bc[1] & 0x7f; */
+			break;
+	}
 }
 
 /********************* FAX stuff ***************************/
@@ -1225,7 +1169,7 @@
 
 	reqbuf = (eicon_REQ *)skb_put(skb, sizeof(eicon_t30_s) + sizeof(eicon_REQ));
 
-	reqbuf->Req = IDI_N_EDATA;
+	reqbuf->Req = N_EDATA;
 	reqbuf->ReqCh = chan->e.IndCh;
 	reqbuf->ReqId = 1;
 
@@ -1359,7 +1303,7 @@
 			eicon_log(card, 128, "sSFF-Head: pagelength = %d\n", page->pagelength);
 			break;
 	}
-	idi_send_data(card, chan, 0, skb, 0);
+	idi_send_data(card, chan, 0, skb, 0, 0);
 }
 
 void
@@ -1913,7 +1857,7 @@
 	OutBuf->Len = 0;
 	OutBuf->Next = OutBuf->Data;
 
-	return(idi_send_data(ccard, chan, 0, skb, 1));
+	return(idi_send_data(ccard, chan, 0, skb, 1, 0));
 }
 
 int
@@ -1958,6 +1902,8 @@
 
         if (chan->queued + skb->len > 1200)
                 return 0;
+	if (chan->pqueued > 1)
+		return 0;
 
 	InBuf.Data = skb->data;
 	InBuf.Size = skb->len;
@@ -2183,6 +2129,7 @@
 	}
 	if ((chan->fax->code > 1) && (chan->fax->code < 120))
 		chan->fax->code += 120;
+	eicon_log(ccard, 8, "idi_fax: Ch%d: Hangup (code=%d)\n", chan->No, chan->fax->code);
 	chan->fax->r_code = ISDN_TTY_FAX_HNG;
 	cmd.driver = ccard->myid;
 	cmd.command = ISDN_STAT_FAXIND;
@@ -2224,7 +2171,7 @@
 
 	reqbuf = (eicon_REQ *)skb_put(skb, 1 + len + sizeof(eicon_REQ));
 
-	reqbuf->Req = IDI_N_UDATA;
+	reqbuf->Req = N_UDATA;
 	reqbuf->ReqCh = chan->e.IndCh;
 	reqbuf->ReqId = 1;
 
@@ -2418,12 +2365,12 @@
 		                while((skb2 = skb_dequeue(&chan->e.X))) {
 					dev_kfree_skb(skb2);
 				}
-				save_flags(flags);
-				cli();
+				spin_lock_irqsave(&eicon_lock, flags);
 				chan->queued = 0;
+				chan->pqueued = 0;
 				chan->waitq = 0;
 				chan->waitpq = 0;
-				restore_flags(flags);
+				spin_unlock_irqrestore(&eicon_lock, flags);
 				if (message.e_cau[0] & 0x7f) {
 					cmd.driver = ccard->myid;
 					cmd.arg = chan->No;
@@ -2433,10 +2380,6 @@
 					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->l2prot == ISDN_PROTO_L2_FAX) &&
@@ -2446,6 +2389,7 @@
 					if (chan->e.B2Id)
 						idi_do_req(ccard, chan, REMOVE, 1);
 					chan->statectrl &= ~WAITING_FOR_HANGUP;
+					chan->statectrl &= ~IN_HOLD;
 					if (chan->statectrl & HAVE_CONN_REQ) {
 						eicon_log(ccard, 32, "idi_req: Ch%d: queueing delayed conn_req\n", chan->No);
 						chan->statectrl &= ~HAVE_CONN_REQ;
@@ -2463,6 +2407,9 @@
 						cmd.command = ISDN_STAT_DHUP;
 						ccard->interface.statcallb(&cmd);
 						eicon_idi_listen_req(ccard, chan);
+#ifdef CONFIG_ISDN_TTY_FAX
+						chan->fax = 0;
+#endif
 					}
 				}
 				break;
@@ -2475,7 +2422,7 @@
 					break;
 				}
 				chan->fsm_state = EICON_STATE_ICALL;
-				idi_bc2si(message.bc, message.hlc, &chan->si1, &chan->si2);
+				idi_bc2si(message.bc, message.hlc, message.sin, &chan->si1, &chan->si2);
 				strcpy(chan->cpn, message.cpn + 1);
 				strcpy(chan->oad, message.oad);
 				strcpy(chan->dsa, message.dsa);
@@ -2553,12 +2500,15 @@
 						case ISDN_PROTO_L2_MODEM:
 							/* do nothing, wait for connect */
 							break;
+						case ISDN_PROTO_L2_V11096:
+						case ISDN_PROTO_L2_V11019:
+						case ISDN_PROTO_L2_V11038:
 						case ISDN_PROTO_L2_TRANS:
-							idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+							idi_do_req(ccard, chan, N_CONNECT, 1);
 							break;
-						default:
+						default:;
 							/* On most incoming calls we use automatic connect */
-							/* idi_do_req(ccard, chan, IDI_N_CONNECT, 1); */
+							/* idi_do_req(ccard, chan, N_CONNECT, 1); */
 					}
 				} else {
 					if (chan->fsm_state != EICON_STATE_ACTIVE)
@@ -2568,33 +2518,50 @@
 			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, "eicon: Ch%d: old net_id %x still exist, removing.\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)
+						if (chan->fax) {
 							chan->fax->phase = ISDN_FAX_PHASE_A;
+						} else {
+							eicon_log(ccard, 1, "idi_ind: Call_Con with NULL fax struct, ERROR\n");
+							idi_hangup(ccard, chan);
+							break;
+						}
 					}
 #endif
+					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, N_CONNECT, 1);
 				} else
-				idi_hangup(ccard, chan);
+					idi_hangup(ccard, chan);
 				break;
 			case AOC_IND:
 				eicon_log(ccard, 8, "idi_ind: Ch%d: Advice of Charge\n", chan->No);
 				break;
+			case CALL_HOLD_ACK:
+				chan->statectrl |= IN_HOLD;
+				eicon_log(ccard, 8, "idi_ind: Ch%d: Call Hold Ack\n", chan->No);
+				break;
+			case SUSPEND_REJ:
+				eicon_log(ccard, 8, "idi_ind: Ch%d: Suspend Rejected\n", chan->No);
+				break;
+			case SUSPEND:
+				eicon_log(ccard, 8, "idi_ind: Ch%d: Suspend Ack\n", chan->No);
+				break;
+			case RESUME:
+				eicon_log(ccard, 8, "idi_ind: Ch%d: Resume Ack\n", chan->No);
+				break;
 			default:
 				eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED SigIndication 0x%02x\n", chan->No, ind->Ind);
 		}
@@ -2613,7 +2580,7 @@
 		} 
 		else
 		switch(ind->Ind) {
-			case IDI_N_CONNECT_ACK:
+			case 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;
@@ -2634,7 +2601,7 @@
 						}
 					}
 					else {
-						eicon_log(ccard, 1, "idi_ind: N_CONNECT_ACK with NULL fax struct, ERROR\n");
+						eicon_log(ccard, 1, "idi_ind: N_Connect_Ack with NULL fax struct, ERROR\n");
 					}
 #endif
 					break;
@@ -2646,10 +2613,10 @@
 				strcpy(cmd.parm.num, "64000");
 				ccard->interface.statcallb(&cmd);
 				break; 
-			case IDI_N_CONNECT:
+			case N_CONNECT:
 				eicon_log(ccard, 16,"idi_ind: Ch%d: N_Connect\n", chan->No);
 				chan->e.IndCh = ind->IndCh;
-				if (chan->e.B2Id) idi_do_req(ccard, chan, IDI_N_CONNECT_ACK, 1);
+				if (chan->e.B2Id) idi_do_req(ccard, chan, N_CONNECT_ACK, 1);
 				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
 					break;
 				}
@@ -2664,43 +2631,47 @@
 				strcpy(cmd.parm.num, "64000");
 				ccard->interface.statcallb(&cmd);
 				break; 
-			case IDI_N_DISC:
-				eicon_log(ccard, 16, "idi_ind: Ch%d: N_DISC\n", chan->No);
+			case N_DISC:
+				eicon_log(ccard, 16, "idi_ind: Ch%d: N_Disc\n", chan->No);
 				if (chan->e.B2Id) {
 		                	while((skb2 = skb_dequeue(&chan->e.X))) {
 						dev_kfree_skb(skb2);
 					}
-					idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1);
+					idi_do_req(ccard, chan, N_DISC_ACK, 1);
 					idi_do_req(ccard, chan, REMOVE, 1);
 				}
 #ifdef CONFIG_ISDN_TTY_FAX
-				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
+				if ((chan->l2prot == ISDN_PROTO_L2_FAX) && (chan->fax)){
 					idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
 					idi_fax_hangup(ccard, chan);
 				}
 #endif
 				chan->e.IndCh = 0;
-				save_flags(flags);
-				cli();
+				spin_lock_irqsave(&eicon_lock, flags);
 				chan->queued = 0;
+				chan->pqueued = 0;
 				chan->waitq = 0;
 				chan->waitpq = 0;
-				restore_flags(flags);
-				idi_do_req(ccard, chan, HANGUP, 0);
+				spin_unlock_irqrestore(&eicon_lock, flags);
+				if (!(chan->statectrl & IN_HOLD)) {
+					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;
-					chan->statectrl |= WAITING_FOR_HANGUP;
+					if (!(chan->statectrl & IN_HOLD)) {
+						chan->statectrl |= WAITING_FOR_HANGUP;
+					}
 				}
 #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);
+			case 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);
@@ -2708,10 +2679,10 @@
 				}
 #endif
 				break; 
-			case IDI_N_DATA_ACK:
-				eicon_log(ccard, 128, "idi_ind: Ch%d: N_DATA_ACK\n", chan->No);
+			case N_DATA_ACK:
+				eicon_log(ccard, 128, "idi_ind: Ch%d: N_Data_Ack\n", chan->No);
 				break;
-			case IDI_N_DATA:
+			case 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) {
@@ -2723,11 +2694,11 @@
 					free_buff = 0; 
 				}
 				break; 
-			case IDI_N_UDATA:
+			case N_UDATA:
 				idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
 				break; 
 #ifdef CONFIG_ISDN_TTY_FAX
-			case IDI_N_EDATA:
+			case N_EDATA:
 				idi_edata_action(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
 				break; 
 #endif
@@ -2747,6 +2718,8 @@
 {
 	ulong flags;
 	isdn_ctrl cmd;
+	int tqueued = 0;
+	int twaitpq = 0;
 
 	if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) {
 		/* I dont know why this happens, should not ! */
@@ -2770,16 +2743,15 @@
 			eicon_log(ccard, 16, "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No,
 				ack->Reference, chan->e.ref);
 		}
-		save_flags(flags);
-		cli();
+		spin_lock_irqsave(&eicon_lock, flags);
 		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);
+		spin_unlock_irqrestore(&eicon_lock, flags);
+		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");
 		return 1;
 	}
 
@@ -2790,25 +2762,21 @@
 	} else {
 	/* Network layer */
 		switch(chan->e.Req & 0x0f) {
-			case IDI_N_CONNECT:
+			case N_CONNECT:
 				chan->e.IndCh = ack->RcCh;
 				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);
 				break;
-			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();
+			case N_MDATA:
+			case N_DATA:
+				tqueued = chan->queued;
+				twaitpq = chan->waitpq;
+				if ((chan->e.Req & 0x0f) == N_DATA) {
+					spin_lock_irqsave(&eicon_lock, flags);
 					chan->waitpq = 0;
-					restore_flags(flags);
+					if(chan->pqueued)
+						chan->pqueued--;
+					spin_unlock_irqrestore(&eicon_lock, flags);
 #ifdef CONFIG_ISDN_TTY_FAX
 					if (chan->l2prot == ISDN_PROTO_L2_FAX) {
 						if (((chan->queued - chan->waitq) < 1) &&
@@ -2828,11 +2796,17 @@
 					}
 #endif
 				}
-				save_flags(flags);
-				cli();
+				spin_lock_irqsave(&eicon_lock, flags);
 				chan->queued -= chan->waitq;
 				if (chan->queued < 0) chan->queued = 0;
-				restore_flags(flags);
+				spin_unlock_irqrestore(&eicon_lock, flags);
+				if (((chan->e.Req & 0x0f) == N_DATA) && (tqueued)) {
+					cmd.driver = ccard->myid;
+					cmd.command = ISDN_STAT_BSENT;
+					cmd.arg = chan->No;
+					cmd.parm.length = twaitpq;
+					ccard->interface.statcallb(&cmd);
+				}
 				break;
 			default:
 				eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
@@ -2858,11 +2832,10 @@
 		return;
 	}
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&eicon_lock, flags);
 	if ((chan = ccard->IdTable[ack->RcId]) != NULL)
 		dCh = chan->No;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&eicon_lock, flags);
 
 	switch (ack->Rc) {
 		case OK_FC:
@@ -2890,8 +2863,7 @@
 				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();
+			spin_lock_irqsave(&eicon_lock, flags);
 			for(j = 0; j < ccard->nchannels + 1; j++) {
 				if ((ccard->bch[j].e.ref == ack->Reference) &&
 					(ccard->bch[j].e.Req == ASSIGN)) {
@@ -2901,12 +2873,12 @@
 						ccard->bch[j].e.B2Id  = ack->RcId;
 					ccard->IdTable[ack->RcId] = &ccard->bch[j];
 					chan = &ccard->bch[j];
-					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);
+			}
+			spin_unlock_irqrestore(&eicon_lock, flags);
+			eicon_log(ccard, 16, "idi_ack: Ch%d: Id %x assigned (%s)\n", j, 
+				ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig");
 			if (j > ccard->nchannels) {
 				eicon_log(ccard, 24, "idi_ack: Ch??: ref %d not found for Id %d\n", 
 					ack->Reference, ack->RcId);
@@ -2917,6 +2889,7 @@
 		case UNKNOWN_COMMAND:
 		case WRONG_COMMAND:
 		case WRONG_ID:
+		case ADAPTER_DEAD:
 		case WRONG_CH:
 		case UNKNOWN_IE:
 		case WRONG_IE:
@@ -2949,19 +2922,18 @@
 				ccard->interface.statcallb(&cmd);
 			}
 	}
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&eicon_lock, flags);
 	if (chan) {
 		chan->e.ref = 0;
 		chan->e.busy = 0;
 	}
-	restore_flags(flags);
+	spin_unlock_irqrestore(&eicon_lock, 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)
+idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que, int chk)
 {
         struct sk_buff *xmit_skb;
         struct sk_buff *skb2;
@@ -2985,13 +2957,14 @@
 		return -1;
         if (!len)
                 return 0;
-	if (chan->queued + len > EICON_MAX_QUEUE)
+
+	if ((chk) && (chan->pqueued > 1))
 		return 0;
 
-	eicon_log(card, 128, "idi_snd: Ch%d: %d bytes\n", chan->No, len);
+	eicon_log(card, 128, "idi_snd: Ch%d: %d bytes (Pqueue=%d)\n",
+		chan->No, len, chan->pqueued);
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&eicon_lock, flags);
 	while(offset < len) {
 
 		plen = ((len - offset) > 270) ? 270 : len - offset;
@@ -3000,7 +2973,7 @@
         	skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
 
 	        if ((!xmit_skb) || (!skb2)) {
-			restore_flags(flags);
+			spin_unlock_irqrestore(&eicon_lock, 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);
@@ -3013,13 +2986,10 @@
         	chan2->ptr = chan;
 
 	        reqbuf = (eicon_REQ *)skb_put(xmit_skb, plen + sizeof(eicon_REQ));
-		if (((len - offset) > 270) &&
-			(chan->l2prot != ISDN_PROTO_L2_MODEM) &&
-			(chan->l2prot != ISDN_PROTO_L2_FAX) &&
-			(chan->l2prot != ISDN_PROTO_L2_TRANS)) {
-		        reqbuf->Req = IDI_N_MDATA;
+		if ((len - offset) > 270) { 
+		        reqbuf->Req = N_MDATA;
 		} else {
-		        reqbuf->Req = IDI_N_DATA;
+		        reqbuf->Req = N_DATA;
 			/* if (ack) reqbuf->Req |= N_D_BIT; */
 		}	
         	reqbuf->ReqCh = chan->e.IndCh;
@@ -3033,9 +3003,11 @@
 
 		offset += plen;
 	}
-	if (que)
+	if (que) {
 		chan->queued += len;
-	restore_flags(flags);
+		chan->pqueued++;
+	}
+	spin_unlock_irqrestore(&eicon_lock, flags);
 	eicon_schedule_tx(card);
         dev_kfree_skb(skb);
         return len;
@@ -3073,7 +3045,7 @@
         reqbuf->XBuffer.P[0] = 0;
         reqbuf->Req = ASSIGN;
         reqbuf->ReqCh = 0;
-        reqbuf->ReqId = 0xe0;
+        reqbuf->ReqId = MAN_ID;
         reqbuf->XBuffer.length = 1;
         reqbuf->Reference = 2; /* Man Entity */
 
@@ -3199,7 +3171,7 @@
         reqbuf->XBuffer.P[1] = manbuf->length[0] + 1;
 
         reqbuf->XBuffer.P[l++] = 0;
-        reqbuf->Req = (manbuf->count) ? manbuf->count : 0x02; /* Request */
+        reqbuf->Req = (manbuf->count) ? manbuf->count : MAN_READ;
         reqbuf->ReqCh = 0;
         reqbuf->ReqId = 1;
         reqbuf->XBuffer.length = l;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)