patch-2.1.27 linux/drivers/isdn/hisax/callc.c

Next file: linux/drivers/isdn/hisax/config.c
Previous file: linux/drivers/isdn/hisax/buffers.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c
@@ -0,0 +1,1946 @@
+/* $Id: callc.c,v 1.20 1997/02/17 00:32:47 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: callc.c,v $
+ * Revision 1.20  1997/02/17 00:32:47  keil
+ * Bugfix: No Busy reported to LL
+ *
+ * Revision 1.19  1997/02/14 12:23:10  fritz
+ * Added support for new insmod parameter handling.
+ *
+ * Revision 1.18  1997/02/11 01:36:58  keil
+ * Changed setup-interface (incoming and outgoing), cause reporting
+ *
+ * Revision 1.17  1997/02/09 00:23:10  keil
+ * new interface handling, one interface per card
+ * some changes in debug and leased line mode
+ *
+ * Revision 1.16  1997/01/27 23:17:03  keil
+ * delete timers while unloading
+ *
+ * Revision 1.15  1997/01/27 16:00:38  keil
+ * D-channel shutdown delay; improved callback
+ *
+ * Revision 1.14  1997/01/21 22:16:39  keil
+ * new statemachine; leased line support; cleanup for 2.0
+ *
+ * Revision 1.13  1996/12/08 19:51:17  keil
+ * bugfixes from Pekka Sarnila
+ *
+ * Revision 1.12  1996/11/26 20:20:03  keil
+ * fixed warning while compile
+ *
+ * Revision 1.11  1996/11/26 18:43:17  keil
+ * change ioctl 555 --> 55 (555 didn't work)
+ *
+ * Revision 1.10  1996/11/26 18:06:07  keil
+ * fixed missing break statement,ioctl 555 reset modcount
+ *
+ * Revision 1.9  1996/11/18 20:23:19  keil
+ * log writebuf channel not open changed
+ *
+ * Revision 1.8  1996/11/06 17:43:17  keil
+ * more changes for 2.1.X;block fixed ST_PRO_W
+ *
+ * Revision 1.7  1996/11/06 15:13:51  keil
+ * typo 0x64 --->64 in debug code
+ *
+ * Revision 1.6  1996/11/05 19:40:33  keil
+ * X.75 windowsize
+ *
+ * Revision 1.5  1996/10/30 10:11:06  keil
+ * debugging LOCK changed;ST_REL_W EV_HANGUP added
+ *
+ * Revision 1.4  1996/10/27 22:20:16  keil
+ * alerting bugfixes
+ * no static b-channel<->channel mapping
+ *
+ * Revision 1.2  1996/10/16 21:29:45  keil
+ * compile bug as "not module"
+ * Callback with euro
+ *
+ * Revision 1.1  1996/10/13 20:04:50  keil
+ * Initial revision
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < 0x020111)
+extern long mod_use_count_;
+#define MOD_USE_COUNT mod_use_count_
+#else
+#define MOD_USE_COUNT ((&__this_module)->usecount)
+#endif
+#endif				/* MODULE */
+
+const char *l4_revision = "$Revision: 1.20 $";
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+extern void HiSax_mod_dec_use_count(void);
+extern void HiSax_mod_inc_use_count(void);
+
+static int init_ds(struct Channel *chanp, int incoming);
+static void release_ds(struct Channel *chanp);
+
+static struct Fsm callcfsm =
+{NULL, 0, 0};
+static struct Fsm lcfsm =
+{NULL, 0, 0};
+
+static int chancount = 0;
+
+/* Flags for remembering action done in l4 */
+
+#define  FLG_START_D	0x0001
+#define  FLG_ESTAB_D	0x0002
+#define  FLG_CALL_SEND	0x0004
+#define  FLG_CALL_REC   0x0008
+#define  FLG_CALL_ALERT	0x0010
+#define  FLG_START_B	0x0020
+#define  FLG_CONNECT_B	0x0040
+#define  FLG_LL_DCONN	0x0080
+#define  FLG_LL_BCONN	0x0100
+#define  FLG_DISC_SEND	0x0200
+#define  FLG_DISC_REC	0x0400
+#define  FLG_REL_REC	0x0800
+
+#define  SETBIT(flg, item)  flg |= item
+#define  RESBIT(flg, item)  flg &= (~item)
+
+/*
+ * Because of callback it's a good idea to delay the shutdown of the d-channel
+ */
+#define	DREL_TIMER_VALUE 30000
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState
+*
+hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp)
+			if (cards[i].sp->myid == driverid)
+				return (cards[i].sp);
+	return (struct IsdnCardState *) 0;
+}
+
+static void
+link_debug(struct Channel *chanp, char *s, int direction)
+{
+	char tmp[100], tm[32];
+
+	jiftime(tm, jiffies);
+	sprintf(tmp, "%s Channel %d %s %s\n", tm, chanp->chan,
+		direction ? "LL->HL" : "HL->LL", s);
+	HiSax_putstatus(chanp->sp, tmp);
+}
+
+
+enum {
+	ST_NULL,		/*  0 inactive */
+	ST_OUT_WAIT_D,		/*  1 outgoing, awaiting d-channel establishment */
+	ST_IN_WAIT_D,		/*  2 incoming, awaiting d-channel establishment */
+	ST_OUT_DIAL,		/*  3 outgoing, SETUP send; awaiting confirm */
+	ST_IN_WAIT_LL,		/*  4 incoming call received; wait for LL confirm */
+	ST_IN_ALERT_SEND,	/*  5 incoming call received; ALERT send */
+	ST_IN_WAIT_CONN_ACK,	/*  6 incoming CONNECT send; awaiting CONN_ACK */
+	ST_WAIT_BCONN,		/*  7 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */
+	ST_ACTIVE,		/*  8 active, b channel prot. established */
+	ST_WAIT_BRELEASE,	/*  9 call clear. (initiator), awaiting b channel prot. rel. */
+	ST_WAIT_BREL_DISC,	/* 10 call clear. (receiver), DISCONNECT req. received */
+	ST_WAIT_DCOMMAND,	/* 11 call clear. (receiver), awaiting DCHANNEL message */
+	ST_WAIT_DRELEASE,	/* 12 DISCONNECT sent, awaiting RELEASE */
+	ST_WAIT_D_REL_CNF,	/* 13 RELEASE sent, awaiting RELEASE confirm */
+	ST_WAIT_DSHUTDOWN,	/*  14 awaiting d-channel shutdown */
+};
+
+#define STATE_COUNT (ST_WAIT_DSHUTDOWN +1)
+
+static char *strState[] =
+{
+	"ST_NULL",
+	"ST_OUT_WAIT_D",
+	"ST_IN_WAIT_D",
+	"ST_OUT_DIAL",
+	"ST_IN_WAIT_LL",
+	"ST_IN_ALERT_SEND",
+	"ST_IN_WAIT_CONN_ACK",
+	"ST_WAIT_BCONN",
+	"ST_ACTIVE",
+	"ST_WAIT_BRELEASE",
+	"ST_WAIT_BREL_DISC",
+	"ST_WAIT_DCOMMAND",
+	"ST_WAIT_DRELEASE",
+	"ST_WAIT_D_REL_CNF",
+	"ST_WAIT_DSHUTDOWN",
+};
+
+enum {
+	EV_DIAL,		/*  0 */
+	EV_SETUP_CNF,		/*  1 */
+	EV_ACCEPTB,		/*  2 */
+	EV_DISCONNECT_IND,	/*  3 */
+	EV_RELEASE_CNF,		/*  4 */
+	EV_DLEST,		/*  5 */
+	EV_DLRL,		/*  6 */
+	EV_SETUP_IND,		/*  7 */
+	EV_RELEASE_IND,		/*  8 */
+	EV_ACCEPTD,		/*  9 */
+	EV_SETUP_CMPL_IND,	/* 10 */
+	EV_BC_EST,		/* 11 */
+	EV_WRITEBUF,		/* 12 */
+	EV_DATAIN,		/* 13 */
+	EV_HANGUP,		/* 14 */
+	EV_BC_REL,		/* 15 */
+	EV_CINF,		/* 16 */
+	EV_SUSPEND,		/* 17 */
+	EV_RESUME,		/* 18 */
+	EV_SHUTDOWN_D,		/* 19 */
+	EV_NOSETUP_RSP,		/* 20 */
+	EV_SETUP_ERR,		/* 21 */
+	EV_CONNECT_ERR,		/* 22 */
+	EV_RELEASE_ERR,		/* 23 */
+};
+
+#define EVENT_COUNT (EV_RELEASE_ERR +1)
+
+static char *strEvent[] =
+{
+	"EV_DIAL",
+	"EV_SETUP_CNF",
+	"EV_ACCEPTB",
+	"EV_DISCONNECT_IND",
+	"EV_RELEASE_CNF",
+	"EV_DLEST",
+	"EV_DLRL",
+	"EV_SETUP_IND",
+	"EV_RELEASE_IND",
+	"EV_ACCEPTD",
+	"EV_SETUP_CMPL_IND",
+	"EV_BC_EST",
+	"EV_WRITEBUF",
+	"EV_DATAIN",
+	"EV_HANGUP",
+	"EV_BC_REL",
+	"EV_CINF",
+	"EV_SUSPEND",
+	"EV_RESUME",
+	"EV_SHUTDOWN_D",
+	"EV_NOSETUP_RSP",
+	"EV_SETUP_ERR",
+	"EV_CONNECT_ERR",
+	"EV_RELEASE_ERR",
+};
+
+enum {
+	ST_LC_NULL,
+	ST_LC_ACTIVATE_WAIT,
+	ST_LC_DELAY,
+	ST_LC_ESTABLISH_WAIT,
+	ST_LC_CONNECTED,
+	ST_LC_FLUSH_WAIT,
+	ST_LC_FLUSH_DELAY,
+	ST_LC_RELEASE_WAIT,
+};
+
+#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1)
+
+static char *strLcState[] =
+{
+	"ST_LC_NULL",
+	"ST_LC_ACTIVATE_WAIT",
+	"ST_LC_DELAY",
+	"ST_LC_ESTABLISH_WAIT",
+	"ST_LC_CONNECTED",
+	"ST_LC_FLUSH_WAIT",
+	"ST_LC_FLUSH_DELAY",
+	"ST_LC_RELEASE_WAIT",
+};
+
+enum {
+	EV_LC_ESTABLISH,
+	EV_LC_PH_ACTIVATE,
+	EV_LC_PH_DEACTIVATE,
+	EV_LC_DL_ESTABLISH,
+	EV_LC_TIMER,
+	EV_LC_DL_FLUSH,
+	EV_LC_DL_RELEASE,
+	EV_LC_FLUSH,
+	EV_LC_RELEASE,
+};
+
+#define LC_EVENT_COUNT (EV_LC_RELEASE+1)
+
+static char *strLcEvent[] =
+{
+	"EV_LC_ESTABLISH",
+	"EV_LC_PH_ACTIVATE",
+	"EV_LC_PH_DEACTIVATE",
+	"EV_LC_DL_ESTABLISH",
+	"EV_LC_TIMER",
+	"EV_LC_DL_FLUSH",
+	"EV_LC_DL_RELEASE",
+	"EV_LC_FLUSH",
+	"EV_LC_RELEASE",
+};
+
+#define LC_D  0
+#define LC_B  1
+
+static inline void
+l4_deliver_cause(struct Channel *chanp)
+{
+	isdn_ctrl ic;
+	
+	if (chanp->para.cause<0)
+		return;
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	if (chanp->sp->protocol == ISDN_PTYPE_EURO)
+		sprintf(ic.parm.num, "E%02X%02X", chanp->para.loc,
+			chanp->para.cause);
+	else
+		sprintf(ic.parm.num, "%02X%02X", chanp->para.loc,
+			chanp->para.cause);
+	chanp->sp->iif.statcallb(&ic);
+}
+
+/*
+ * Dial out
+ */
+static void
+l4_prep_dialout(struct FsmInst *fi, int event, void *arg)
+{
+	isdn_ctrl *ic = arg;
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_OUT_WAIT_D);
+	FsmDelTimer(&chanp->drel_timer, 60);
+	FsmDelTimer(&chanp->dial_timer, 73);
+	chanp->para.setup = ic->parm.setup;
+	if (!strcmp(chanp->para.setup.eazmsn, "0"))
+		chanp->para.setup.eazmsn[0] = '\0';
+
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = 0;
+	chanp->lc_b.l2_start = !0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			chanp->lc_b.l2_establish = !0;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_TRANS):
+			chanp->lc_b.l2_establish = 0;
+			break;
+		default:
+			printk(KERN_WARNING "l4_prep_dialout unknown protocol\n");
+			break;
+	}
+	if (chanp->Flags & FLG_ESTAB_D) {
+		FsmEvent(fi, EV_DLEST, NULL);
+	} else {
+		chanp->Flags = FLG_START_D;
+		if (chanp->leased) {
+			chanp->lc_d.l2_establish = 0;
+		}
+		FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+	}
+}
+
+static void
+l4_do_dialout(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_OUT_DIAL);
+	if (chanp->leased) {
+		chanp->para.bchannel = (chanp->chan & 1) + 1;
+		FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+	} else {
+		SETBIT(chanp->Flags, FLG_ESTAB_D);
+		chanp->para.callref = chanp->outcallref;
+		chanp->outcallref++;
+		if (chanp->outcallref == 128)
+			chanp->outcallref = 64;
+		chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL);
+		SETBIT(chanp->Flags, FLG_CALL_SEND);
+	}
+}
+
+static void
+l4_init_bchan_out(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	SETBIT(chanp->Flags, FLG_LL_DCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DCONN", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DCONN;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+	init_ds(chanp, 0);
+	SETBIT(chanp->Flags, FLG_START_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+l4_go_active(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_ACTIVE);
+	chanp->data_open = !0;
+	SETBIT(chanp->Flags, FLG_CONNECT_B);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_BCONN", 0);
+	SETBIT(chanp->Flags, FLG_LL_BCONN);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_BCONN;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+/* incomming call */
+
+static void
+l4_start_dchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_IN_WAIT_D);
+	FsmDelTimer(&chanp->drel_timer, 61);
+	if (chanp->Flags & FLG_ESTAB_D) {
+		FsmEvent(fi, EV_DLEST, NULL);
+	} else {
+		chanp->Flags = FLG_START_D;
+		FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+	}
+}
+
+static void
+l4_deliver_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+	int ret;
+	char txt[32];
+
+	/*
+	 * Report incoming calls only once to linklevel, use CallFlags
+	 * which is set to 3 with each broadcast message in isdnl1.c
+	 * and resetted if a interface  answered the STAT_ICALL.
+	 */
+	if ((chanp->sp) && (chanp->sp->CallFlags == 3)) {
+		FsmChangeState(fi, ST_IN_WAIT_LL);
+		SETBIT(chanp->Flags, FLG_ESTAB_D);
+		SETBIT(chanp->Flags, FLG_CALL_REC);
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_ICALL", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_ICALL;
+		ic.arg = chanp->chan;
+		/*
+		 * No need to return "unknown" for calls without OAD,
+		 * cause that's handled in linklevel now (replaced by '0')
+		 */
+		ic.parm.setup = chanp->para.setup;
+		ret = chanp->sp->iif.statcallb(&ic);
+		if (chanp->debug & 1) {
+			sprintf(txt, "statcallb ret=%d", ret);
+			link_debug(chanp, txt, 1);
+		}
+		if (ret)	/* if a interface knows this call, reset the CallFlag
+				   * to avoid a second Call report to the linklevel
+				 */
+			chanp->sp->CallFlags &= ~(chanp->chan + 1);
+		switch (ret) {
+			case 1:	/* OK, anybody likes this call */
+				FsmChangeState(fi, ST_IN_ALERT_SEND);
+				SETBIT(chanp->Flags, FLG_CALL_ALERT);
+				chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL);
+				break;
+			case 2:	/* Rejecting Call */
+				RESBIT(chanp->Flags, FLG_CALL_REC);
+				break;
+			case 0:	/* OK, nobody likes this call */
+			default:	/* statcallb problems */
+				chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL);
+				FsmChangeState(fi, ST_NULL);
+				chanp->Flags = FLG_ESTAB_D;
+				FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61);
+				break;
+		}
+	} else {
+		chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL);
+		FsmChangeState(fi, ST_NULL);
+		chanp->Flags = FLG_ESTAB_D;
+		FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62);
+	}
+}
+
+static void
+l4_send_dconnect(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+	chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL);
+}
+
+static void
+l4_init_bchan_in(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	SETBIT(chanp->Flags, FLG_LL_DCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DCONN", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DCONN;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = !0;
+	chanp->lc_b.l2_start = 0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			chanp->lc_b.l2_establish = !0;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_TRANS):
+			chanp->lc_b.l2_establish = 0;
+			break;
+		default:
+			printk(KERN_WARNING "r9 unknown protocol\n");
+			break;
+	}
+	init_ds(chanp, !0);
+	SETBIT(chanp->Flags, FLG_START_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+/* Call clearing */
+
+static void
+l4_reject_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	chanp->para.cause = 0x15;	/* Call Rejected */
+	chanp->is.l4.l4l3(&chanp->is, CC_REJECT_REQ, NULL);
+	SETBIT(chanp->Flags, FLG_DISC_SEND);
+}
+
+static void
+l4_cancel_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	chanp->para.cause = 0x10;	/* Normal Call Clearing */
+	chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+	SETBIT(chanp->Flags, FLG_DISC_SEND);
+}
+
+static void
+l4_shutdown_d(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+	FsmDelTimer(&chanp->drel_timer, 62);
+	RESBIT(chanp->Flags, FLG_ESTAB_D);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_timeout_d(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	FsmChangeState(fi, ST_NULL);
+	chanp->Flags = FLG_ESTAB_D;
+	FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
+}
+
+static void
+l4_go_null(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+}
+
+static void
+l4_disconn_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_BRELEASE);
+	RESBIT(chanp->Flags, FLG_CONNECT_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_send_d_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+
+	if (chanp->Flags & (FLG_DISC_REC | FLG_REL_REC))
+		return;
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	chanp->para.cause = 0x10;	/* Normal Call Clearing */
+	chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+	SETBIT(chanp->Flags, FLG_DISC_SEND);
+}
+
+static void
+l4_released_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DCOMMAND);
+	chanp->data_open = 0;
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	release_ds(chanp);
+	RESBIT(chanp->Flags, FLG_START_B);
+}
+
+
+static void
+l4_release_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	SETBIT(chanp->Flags, FLG_DISC_REC);
+	FsmChangeState(fi, ST_WAIT_BREL_DISC);
+	RESBIT(chanp->Flags, FLG_CONNECT_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_received_d_rel(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+	SETBIT(chanp->Flags, FLG_REL_REC);
+	if (chanp->Flags & FLG_CONNECT_B) {
+		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
+		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	}
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND)) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		RESBIT(chanp->Flags, FLG_CALL_SEND);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_FLUSH, NULL);
+	RESBIT(chanp->Flags, FLG_ESTAB_D);
+	RESBIT(chanp->Flags, FLG_DISC_SEND);
+	RESBIT(chanp->Flags, FLG_CALL_REC);
+	RESBIT(chanp->Flags, FLG_CALL_ALERT);
+}
+
+static void
+l4_received_d_relcnf(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+	if (chanp->Flags & FLG_CONNECT_B) {
+		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
+		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	}
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND)) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		RESBIT(chanp->Flags, FLG_CALL_SEND);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	RESBIT(chanp->Flags, FLG_ESTAB_D);
+	RESBIT(chanp->Flags, FLG_DISC_SEND);
+	RESBIT(chanp->Flags, FLG_CALL_REC);
+	RESBIT(chanp->Flags, FLG_CALL_ALERT);
+}
+
+static void
+l4_received_d_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_D_REL_CNF);
+	SETBIT(chanp->Flags, FLG_DISC_REC);
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND)) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		RESBIT(chanp->Flags, FLG_CALL_SEND);
+		RESBIT(chanp->Flags, FLG_CALL_ALERT);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	RESBIT(chanp->Flags, FLG_CALL_ALERT);
+	chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+}
+
+/* processing charge info */
+static void
+l4_charge_info(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_CINF;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "%d", chanp->para.chargeinfo);
+	chanp->sp->iif.statcallb(&ic);
+}
+
+/* error procedures */
+
+static void
+l4_no_dchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_NODCH", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_NODCH;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_no_dchan_ready(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DHUP", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+static void
+l4_no_dchan_in(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL);
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_no_setup_rsp(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DHUP", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+static void
+l4_setup_err(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	SETBIT(chanp->Flags, FLG_DISC_SEND);	/* DISCONN was sent from L3 */
+}
+
+static void
+l4_connect_err(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	SETBIT(chanp->Flags, FLG_DISC_SEND);	/* DISCONN was sent from L3 */
+}
+
+static void
+l4_active_dlrl(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_NULL);
+	if (chanp->Flags & FLG_CONNECT_B) {
+		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
+		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	}
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		if (chanp->sp->protocol == ISDN_PTYPE_EURO) {
+			chanp->para.cause  = 0x2f;
+			chanp->para.loc    = 0;
+		} else {
+			chanp->para.cause  = 0x70;
+			chanp->para.loc    = 0;
+		}
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	chanp->Flags = 0;
+	chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+/* *INDENT-OFF* */
+static struct FsmNode fnlist[] =
+{
+	{ST_NULL,		EV_DIAL,		l4_prep_dialout},
+	{ST_NULL,		EV_SETUP_IND,		l4_start_dchan},
+	{ST_NULL,		EV_SHUTDOWN_D,		l4_shutdown_d},
+	{ST_OUT_WAIT_D,		EV_DLEST,		l4_do_dialout},
+	{ST_OUT_WAIT_D,		EV_DLRL,		l4_no_dchan},
+	{ST_OUT_WAIT_D,		EV_HANGUP,		l4_no_dchan},
+	{ST_IN_WAIT_D,		EV_DLEST,		l4_deliver_call},
+	{ST_IN_WAIT_D,		EV_DLRL,		l4_no_dchan_in},
+	{ST_IN_WAIT_D,		EV_HANGUP,		l4_no_dchan_in},
+	{ST_OUT_DIAL,		EV_SETUP_CNF,		l4_init_bchan_out},
+	{ST_OUT_DIAL,		EV_HANGUP,		l4_cancel_call},
+	{ST_OUT_DIAL,		EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_OUT_DIAL,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_OUT_DIAL,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_OUT_DIAL,		EV_NOSETUP_RSP,		l4_no_setup_rsp},
+	{ST_OUT_DIAL,		EV_SETUP_ERR,		l4_setup_err},
+	{ST_IN_WAIT_LL,		EV_SETUP_CMPL_IND,	l4_init_bchan_in},
+	{ST_IN_WAIT_LL,		EV_ACCEPTD,		l4_send_dconnect},
+	{ST_IN_WAIT_LL,		EV_HANGUP,		l4_reject_call},
+	{ST_IN_WAIT_LL,		EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_IN_WAIT_LL,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_IN_WAIT_LL,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_IN_ALERT_SEND,	EV_SETUP_CMPL_IND,	l4_init_bchan_in},
+	{ST_IN_ALERT_SEND,	EV_ACCEPTD,		l4_send_dconnect},
+	{ST_IN_ALERT_SEND,	EV_HANGUP,		l4_send_d_disc},
+	{ST_IN_ALERT_SEND,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_IN_ALERT_SEND,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_IN_ALERT_SEND,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_IN_WAIT_CONN_ACK,	EV_SETUP_CMPL_IND,	l4_init_bchan_in},
+	{ST_IN_WAIT_CONN_ACK,	EV_HANGUP,		l4_send_d_disc},
+	{ST_IN_WAIT_CONN_ACK,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_IN_WAIT_CONN_ACK,	EV_CONNECT_ERR,		l4_connect_err},
+	{ST_WAIT_BCONN,		EV_BC_EST,		l4_go_active},
+	{ST_WAIT_BCONN,		EV_BC_REL,		l4_send_d_disc},
+	{ST_WAIT_BCONN,		EV_HANGUP,		l4_send_d_disc},
+	{ST_WAIT_BCONN,		EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_WAIT_BCONN,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_BCONN,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_ACTIVE,		EV_CINF,		l4_charge_info},
+	{ST_ACTIVE,		EV_BC_REL,		l4_released_bchan},
+	{ST_ACTIVE,		EV_HANGUP,		l4_disconn_bchan},
+	{ST_ACTIVE,		EV_DISCONNECT_IND,	l4_release_bchan},
+	{ST_ACTIVE,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_ACTIVE,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_ACTIVE,		EV_DLRL,		l4_active_dlrl},
+	{ST_WAIT_BRELEASE,	EV_BC_REL,		l4_send_d_disc},
+	{ST_WAIT_BRELEASE,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_WAIT_BRELEASE,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_WAIT_BRELEASE,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_BREL_DISC,	EV_BC_REL,		l4_received_d_disc},
+	{ST_WAIT_BREL_DISC,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_WAIT_BREL_DISC,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_DCOMMAND,	EV_HANGUP,		l4_send_d_disc},
+	{ST_WAIT_DCOMMAND,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_WAIT_DCOMMAND,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_WAIT_DCOMMAND,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_IND,		l4_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_CNF,		l4_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_ERR,		l4_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_DIAL,		l4_no_dchan_ready},
+	{ST_WAIT_D_REL_CNF,	EV_RELEASE_CNF,		l4_timeout_d},
+	{ST_WAIT_D_REL_CNF,	EV_RELEASE_ERR,		l4_timeout_d},
+	{ST_WAIT_D_REL_CNF,	EV_DIAL,		l4_no_dchan_ready},
+	{ST_WAIT_DSHUTDOWN,	EV_DLRL,		l4_go_null},
+	{ST_WAIT_DSHUTDOWN,	EV_DIAL,		l4_prep_dialout},
+	{ST_WAIT_DSHUTDOWN,	EV_SETUP_IND,		l4_start_dchan},
+};
+/* *INDENT-ON* */
+
+
+
+
+
+
+
+
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
+
+static void
+lc_r1(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_ACTIVATE_WAIT);
+	FsmAddTimer(&lf->act_timer, 1000, EV_LC_TIMER, NULL, 50);
+	lf->st->ma.manl1(lf->st, PH_ACTIVATE, NULL);
+
+}
+
+static void
+lc_r6(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmDelTimer(&lf->act_timer, 50);
+	FsmChangeState(fi, ST_LC_DELAY);
+	FsmAddTimer(&lf->act_timer, 40, EV_LC_TIMER, NULL, 51);
+}
+
+static void
+lc_r2(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	if (lf->l2_establish) {
+		FsmChangeState(fi, ST_LC_ESTABLISH_WAIT);
+		if (lf->l2_start)
+			lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL);
+	} else {
+		FsmChangeState(fi, ST_LC_CONNECTED);
+		lf->lccall(lf, LC_ESTABLISH, NULL);
+	}
+}
+
+static void
+lc_r3(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_CONNECTED);
+	lf->lccall(lf, LC_ESTABLISH, NULL);
+}
+
+static void
+lc_r7(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_FLUSH_WAIT);
+	lf->st->ma.manl2(lf->st, DL_FLUSH, NULL);
+}
+
+static void
+lc_r4(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	if (lf->l2_establish) {
+		FsmChangeState(fi, ST_LC_RELEASE_WAIT);
+		lf->st->ma.manl2(lf->st, DL_RELEASE, NULL);
+		/* This timer is for releasing the channel even
+		 * there is a hang in layer 2 ; 5 sec are a try
+		 */
+		FsmAddTimer(&lf->act_timer, 5000, EV_LC_TIMER, NULL, 53);
+	} else {
+		FsmChangeState(fi, ST_LC_NULL);
+		lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+		lf->lccall(lf, LC_RELEASE, NULL);
+	}
+}
+
+static void
+lc_r4_1(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_FLUSH_DELAY);
+	FsmAddTimer(&lf->act_timer, 50, EV_LC_TIMER, NULL, 52);
+}
+
+static void
+lc_r5_1(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_RELEASE_WAIT);
+	/* This delay is needed for send out the UA frame before
+	 * PH_DEACTIVATE the interface
+	 */
+	FsmAddTimer(&lf->act_timer, 10, EV_LC_TIMER, NULL, 54);
+}
+
+static void
+lc_r5(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmDelTimer(&lf->act_timer, 54);
+	FsmChangeState(fi, ST_LC_NULL);
+	lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+	lf->lccall(lf, LC_RELEASE, NULL);
+}
+/* *INDENT-OFF* */
+static struct FsmNode LcFnList[] =
+{
+	{ST_LC_NULL,  		EV_LC_ESTABLISH,	lc_r1},
+	{ST_LC_ACTIVATE_WAIT,	EV_LC_PH_ACTIVATE,	lc_r6},
+	{ST_LC_DELAY,		EV_LC_TIMER,		lc_r2},
+	{ST_LC_DELAY,		EV_LC_DL_ESTABLISH,	lc_r3},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_ESTABLISH,	lc_r3},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_RELEASE,		lc_r5},
+	{ST_LC_CONNECTED,	EV_LC_FLUSH,		lc_r7},
+	{ST_LC_CONNECTED,	EV_LC_RELEASE,		lc_r4},
+	{ST_LC_CONNECTED,	EV_LC_DL_RELEASE,	lc_r5_1},
+	{ST_LC_FLUSH_WAIT,	EV_LC_DL_FLUSH,		lc_r4_1},
+	{ST_LC_FLUSH_DELAY,	EV_LC_TIMER,		lc_r4},
+	{ST_LC_RELEASE_WAIT,	EV_LC_DL_RELEASE,	lc_r5},
+	{ST_LC_RELEASE_WAIT,	EV_LC_TIMER,		lc_r5},
+	{ST_LC_ACTIVATE_WAIT,	EV_LC_TIMER,		lc_r5},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_RELEASE,	lc_r5},
+};
+/* *INDENT-ON* */
+
+
+
+
+
+
+
+#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
+
+void
+CallcNew(void)
+{
+	callcfsm.state_count = STATE_COUNT;
+	callcfsm.event_count = EVENT_COUNT;
+	callcfsm.strEvent = strEvent;
+	callcfsm.strState = strState;
+	FsmNew(&callcfsm, fnlist, FNCOUNT);
+
+	lcfsm.state_count = LC_STATE_COUNT;
+	lcfsm.event_count = LC_EVENT_COUNT;
+	lcfsm.strEvent = strLcEvent;
+	lcfsm.strState = strLcState;
+	FsmNew(&lcfsm, LcFnList, LC_FN_COUNT);
+}
+
+void
+CallcFree(void)
+{
+	FsmFree(&lcfsm);
+	FsmFree(&callcfsm);
+}
+
+static void
+release_ds(struct Channel *chanp)
+{
+	struct PStack *st = &chanp->ds;
+	struct IsdnCardState *sp;
+	struct HscxState *hsp;
+
+	sp = st->l1.hardware;
+	hsp = sp->hs + chanp->hscx;
+
+	close_hscxstate(hsp);
+
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			releasestack_isdnl2(st);
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_TRANS):
+			releasestack_transl2(st);
+			break;
+	}
+}
+
+static void
+cc_l1man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+			break;
+	}
+}
+
+static void
+cc_l2man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (DL_ESTABLISH):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL);
+			break;
+		case (DL_RELEASE):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_RELEASE, NULL);
+			break;
+		case (DL_FLUSH):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_FLUSH, NULL);
+			break;
+	}
+}
+
+static void
+dcc_l1man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+			break;
+	}
+}
+
+static void
+dcc_l2man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (DL_ESTABLISH):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL);
+			break;
+		case (DL_RELEASE):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL);
+			break;
+	}
+}
+
+static void
+ll_handler(struct PStack *st, int pr,
+	   struct BufHeader *ibh)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	char tmp[64], tm[32];
+
+	switch (pr) {
+		case (CC_DISCONNECT_IND):
+			FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
+			break;
+		case (CC_RELEASE_CNF):
+			FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+			break;
+		case (CC_SETUP_IND):
+			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+			break;
+		case (CC_RELEASE_IND):
+			FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL);
+			break;
+		case (CC_SETUP_COMPLETE_IND):
+			FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+			break;
+		case (CC_SETUP_CNF):
+			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+			break;
+		case (CC_INFO_CHARGE):
+			FsmEvent(&chanp->fi, EV_CINF, NULL);
+			break;
+		case (CC_NOSETUP_RSP_ERR):
+			FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL);
+			break;
+		case (CC_SETUP_ERR):
+			FsmEvent(&chanp->fi, EV_SETUP_ERR, NULL);
+			break;
+		case (CC_CONNECT_ERR):
+			FsmEvent(&chanp->fi, EV_CONNECT_ERR, NULL);
+			break;
+		case (CC_RELEASE_ERR):
+			FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL);
+			break;
+		default:
+			jiftime(tm, jiffies);
+			sprintf(tmp, "%s Channel %d L3->L4 unknown primitiv %d\n", tm, chanp->chan, pr);
+			HiSax_putstatus(chanp->sp, tmp);
+	}
+}
+
+static void
+init_is(struct Channel *chanp, unsigned int ces)
+{
+	struct PStack *st = &chanp->is;
+	struct IsdnCardState *sp = chanp->sp;
+	char tmp[128];
+
+	setstack_HiSax(st, sp);
+	st->l2.sap = 0;
+	st->l2.tei = 255;
+	st->l2.ces = ces;
+	st->l2.extended = !0;
+	st->l2.laptype = LAPD;
+	st->l2.window = 1;
+	st->l2.orig = !0;
+	st->l2.t200 = 1000;	/* 1000 milliseconds  */
+	if (st->protocol == ISDN_PTYPE_1TR6) {
+		st->l2.n200 = 3;	/* try 3 times        */
+		st->l2.t203 = 10000;	/* 10000 milliseconds */
+	} else {
+		st->l2.n200 = 4;	/* try 4 times        */
+		st->l2.t203 = 5000;	/* 5000 milliseconds  */
+	}
+	sprintf(tmp, "Channel %d q.921", chanp->chan);
+	setstack_isdnl2(st, tmp);
+	setstack_isdnl3(st, chanp);
+	st->l4.userdata = chanp;
+	st->l4.l2writewakeup = NULL;
+	st->l3.l3l4 = ll_handler;
+	st->l1.l1man = cc_l1man;
+	st->l2.l2man = cc_l2man;
+	st->pa = &chanp->para;
+	HiSax_addlist(sp, st);
+}
+
+static void
+callc_debug(struct FsmInst *fi, char *s)
+{
+	char str[80], tm[32];
+	struct Channel *chanp = fi->userdata;
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
+	HiSax_putstatus(chanp->sp, str);
+}
+
+static void
+lc_debug(struct FsmInst *fi, char *s)
+{
+	char str[256], tm[32];
+	struct LcFsm *lf = fi->userdata;
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d lc %s\n", tm, lf->ch->chan, s);
+	HiSax_putstatus(lf->ch->sp, str);
+}
+
+static void
+dlc_debug(struct FsmInst *fi, char *s)
+{
+	char str[256], tm[32];
+	struct LcFsm *lf = fi->userdata;
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d dlc %s\n", tm, lf->ch->chan, s);
+	HiSax_putstatus(lf->ch->sp, str);
+}
+
+static void
+lccall_d(struct LcFsm *lf, int pr, void *arg)
+{
+	struct Channel *chanp = lf->ch;
+
+	switch (pr) {
+		case (LC_ESTABLISH):
+			FsmEvent(&chanp->fi, EV_DLEST, NULL);
+			break;
+		case (LC_RELEASE):
+			FsmEvent(&chanp->fi, EV_DLRL, NULL);
+			break;
+	}
+}
+
+static void
+lccall_b(struct LcFsm *lf, int pr, void *arg)
+{
+	struct Channel *chanp = lf->ch;
+
+	switch (pr) {
+		case (LC_ESTABLISH):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (LC_RELEASE):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
+	}
+}
+
+static void
+init_chan(int chan, struct IsdnCardState *csta, int hscx,
+	  unsigned int ces)
+{
+	struct Channel *chanp = csta->channel + chan;
+
+	chanp->sp = csta;
+	chanp->hscx = hscx;
+	chanp->chan = chan;
+	chanp->incoming = 0;
+	chanp->debug = 0;
+	chanp->Flags = 0;
+	chanp->leased = 0;
+	init_is(chanp, ces);
+
+	chanp->fi.fsm = &callcfsm;
+	chanp->fi.state = ST_NULL;
+	chanp->fi.debug = 0;
+	chanp->fi.userdata = chanp;
+	chanp->fi.printdebug = callc_debug;
+	FsmInitTimer(&chanp->fi, &chanp->dial_timer);
+	FsmInitTimer(&chanp->fi, &chanp->drel_timer);
+
+	chanp->lc_d.lcfi.fsm = &lcfsm;
+	chanp->lc_d.lcfi.state = ST_LC_NULL;
+	chanp->lc_d.lcfi.debug = 0;
+	chanp->lc_d.lcfi.userdata = &chanp->lc_d;
+	chanp->lc_d.lcfi.printdebug = lc_debug;
+	chanp->lc_d.type = LC_D;
+	chanp->lc_d.ch = chanp;
+	chanp->lc_d.st = &chanp->is;
+	chanp->lc_d.l2_establish = !0;
+	chanp->lc_d.l2_start = !0;
+	chanp->lc_d.lccall = lccall_d;
+	FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer);
+
+	chanp->lc_b.lcfi.fsm = &lcfsm;
+	chanp->lc_b.lcfi.state = ST_LC_NULL;
+	chanp->lc_b.lcfi.debug = 0;
+	chanp->lc_b.lcfi.userdata = &chanp->lc_b;
+	chanp->lc_b.lcfi.printdebug = dlc_debug;
+	chanp->lc_b.type = LC_B;
+	chanp->lc_b.ch = chanp;
+	chanp->lc_b.st = &chanp->ds;
+	chanp->lc_b.l2_establish = !0;
+	chanp->lc_b.l2_start = !0;
+	chanp->lc_b.lccall = lccall_b;
+	FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer);
+	chanp->outcallref = 64;
+	chanp->data_open = 0;
+}
+
+int
+CallcNewChan(struct IsdnCardState *csta)
+{
+	int ces;
+
+	chancount += 2;
+	ces = randomces();
+	init_chan(0, csta, 1, ces++);
+	ces %= 0xffff;
+	init_chan(1, csta, 0, ces++);
+	printk(KERN_INFO "HiSax: 2 channels added\n");
+	return (2);
+}
+
+static void
+release_is(struct Channel *chanp)
+{
+	struct PStack *st = &chanp->is;
+
+	releasestack_isdnl2(st);
+	releasestack_isdnl3(st);
+	HiSax_rmlist(st->l1.hardware, st);
+	BufQueueRelease(&st->l2.i_queue);
+}
+
+void
+CallcFreeChan(struct IsdnCardState *csta)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		FsmDelTimer(&csta->channel[i].drel_timer, 74);
+		FsmDelTimer(&csta->channel[i].dial_timer, 75);
+		FsmDelTimer(&csta->channel[i].lc_b.act_timer, 76);
+		FsmDelTimer(&csta->channel[i].lc_d.act_timer, 77);
+		if (csta->channel[i].Flags & FLG_START_B) {
+			release_ds(csta->channel + i);
+		}
+		release_is(csta->channel + i);
+	}
+}
+
+static void
+lldata_handler(struct PStack *st, int pr,
+	       void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	byte *ptr;
+	int size;
+	struct BufHeader *ibh = arg;
+
+	switch (pr) {
+		case (DL_DATA):
+			if (chanp->data_open) {
+				ptr = DATAPTR(ibh);
+				ptr += chanp->ds.l2.ihsize;
+				size = ibh->datasize - chanp->ds.l2.ihsize;
+				chanp->sp->iif.rcvcallb(chanp->sp->myid, chanp->chan, ptr, size);
+			}
+			BufPoolRelease(ibh);
+			break;
+		default:
+			printk(KERN_WARNING "lldata_handler unknown primitive %d\n",
+			       pr);
+			break;
+	}
+}
+
+static void
+lltrans_handler(struct PStack *st, int pr,
+		struct BufHeader *ibh)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	byte *ptr;
+
+	switch (pr) {
+		case (PH_DATA):
+			if (chanp->data_open) {
+				ptr = DATAPTR(ibh);
+				chanp->sp->iif.rcvcallb(chanp->sp->myid, chanp->chan, ptr, ibh->datasize);
+			}
+			BufPoolRelease(ibh);
+			break;
+		default:
+			printk(KERN_WARNING "lltrans_handler unknown primitive %d\n",
+			       pr);
+			break;
+	}
+}
+
+static void
+ll_writewakeup(struct PStack *st)
+{
+	struct Channel *chanp = st->l4.userdata;
+	isdn_ctrl ic;
+
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_BSENT;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+static int
+init_ds(struct Channel *chanp, int incoming)
+{
+	struct PStack *st = &chanp->ds;
+	struct IsdnCardState *sp = chanp->sp;
+	struct HscxState *hsp = sp->hs + chanp->hscx;
+	char tmp[128];
+
+	st->l1.hardware = sp;
+
+	hsp->mode = 2;
+	hsp->transbufsize = 4000;
+
+	if (setstack_hscx(st, hsp))
+		return (-1);
+
+	st->l2.extended = 0;
+	st->l2.laptype = LAPB;
+	st->l2.orig = !incoming;
+	st->l2.t200 = 1000;	/* 1000 milliseconds */
+	st->l2.window = 7;
+	st->l2.n200 = 4;	/* try 4 times       */
+	st->l2.t203 = 5000;	/* 5000 milliseconds */
+
+	st->l3.debug = 0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			sprintf(tmp, "Channel %d x.75", chanp->chan);
+			setstack_isdnl2(st, tmp);
+			st->l2.l2l3 = lldata_handler;
+			st->l1.l1man = dcc_l1man;
+			st->l2.l2man = dcc_l2man;
+			st->l4.userdata = chanp;
+			st->l4.l1writewakeup = NULL;
+			st->l4.l2writewakeup = ll_writewakeup;
+			st->l2.l2m.debug = chanp->debug & 16;
+			st->l2.debug = chanp->debug & 64;
+			st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+			st->l1.hscxmode = 2;	/* Packet-Mode ? */
+			st->l1.hscxchannel = chanp->para.bchannel - 1;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+			st->l1.l1l2 = lltrans_handler;
+			st->l1.l1man = dcc_l1man;
+			st->l4.userdata = chanp;
+			st->l4.l1writewakeup = ll_writewakeup;
+			st->l1.hscxmode = 2;
+			st->l1.hscxchannel = chanp->para.bchannel - 1;
+			break;
+		case (ISDN_PROTO_L2_TRANS):
+			st->l1.l1l2 = lltrans_handler;
+			st->l1.l1man = dcc_l1man;
+			st->l4.userdata = chanp;
+			st->l4.l1writewakeup = ll_writewakeup;
+			st->l1.hscxmode = 1;
+			st->l1.hscxchannel = chanp->para.bchannel - 1;
+			break;
+	}
+	return (0);
+}
+
+static void
+channel_report(struct Channel *chanp)
+{
+}
+
+static void
+distr_debug(struct IsdnCardState *csta, int debugflags)
+{
+	int i;
+	struct Channel *chanp = csta->channel;
+
+	for (i = 0; i < 2; i++) {
+		chanp[i].debug = debugflags;
+		chanp[i].fi.debug = debugflags & 2;
+		chanp[i].is.l2.l2m.debug = debugflags & 8;
+		chanp[i].ds.l2.l2m.debug = debugflags & 16;
+		chanp[i].is.l2.debug = debugflags & 32;
+		chanp[i].ds.l2.debug = debugflags & 64;
+		chanp[i].lc_d.lcfi.debug = debugflags & 128;
+		chanp[i].lc_b.lcfi.debug = debugflags & 256;
+	}
+	csta->dlogflag = debugflags & 4;
+	csta->teistack->l2.l2m.debug = debugflags & 512;
+}
+
+int
+HiSax_command(isdn_ctrl * ic)
+{
+	struct IsdnCardState *csta = hisax_findcard(ic->driver);
+	struct Channel *chanp;
+	char tmp[128];
+	int i;
+	unsigned int num;
+
+	if (!csta) {
+		printk(KERN_ERR
+		"HiSax: if_command %d called with invalid driverId %d!\n",
+		       ic->command, ic->driver);
+		return -ENODEV;
+	}
+	switch (ic->command) {
+		case (ISDN_CMD_SETEAZ):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "SETEAZ", 1);
+			break;
+		case (ISDN_CMD_SETL2):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1) {
+				sprintf(tmp, "SETL2 card %d %ld", csta->cardnr + 1,
+					ic->arg >> 8);
+				link_debug(chanp, tmp, 1);
+			}
+			chanp->l2_protocol = ic->arg >> 8;
+			break;
+		case (ISDN_CMD_DIAL):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1) {
+				sprintf(tmp, "DIAL %s -> %s (%d,%d)",
+					ic->parm.setup.eazmsn, ic->parm.setup.phone,
+					ic->parm.setup.si1, ic->parm.setup.si2);
+				link_debug(chanp, tmp, 1);
+			}
+			/* this solution is dirty and may be change, if
+			 * we make a callreference based callmanager */
+			if (chanp->fi.state == ST_NULL) {
+				FsmEvent(&chanp->fi, EV_DIAL, ic);
+			} else {
+				FsmDelTimer(&chanp->dial_timer, 70);
+				FsmAddTimer(&chanp->dial_timer, 50, EV_DIAL, ic, 71);
+			}
+			break;
+		case (ISDN_CMD_ACCEPTB):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "ACCEPTB", 1);
+			FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
+			break;
+		case (ISDN_CMD_ACCEPTD):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "ACCEPTD", 1);
+			FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
+			break;
+		case (ISDN_CMD_HANGUP):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "HANGUP", 1);
+			FsmEvent(&chanp->fi, EV_HANGUP, NULL);
+			break;
+		case (ISDN_CMD_SUSPEND):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1) {
+				sprintf(tmp, "SUSPEND %s", ic->parm.num);
+				link_debug(chanp, tmp, 1);
+			}
+			FsmEvent(&chanp->fi, EV_SUSPEND, ic);
+			break;
+		case (ISDN_CMD_RESUME):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1) {
+				sprintf(tmp, "RESUME %s", ic->parm.num);
+				link_debug(chanp, tmp, 1);
+			}
+			FsmEvent(&chanp->fi, EV_RESUME, ic);
+			break;
+		case (ISDN_CMD_LOCK):
+			HiSax_mod_inc_use_count();
+#ifdef MODULE
+			if (csta->channel[0].debug & 1024) {
+				jiftime(tmp, jiffies);
+				i = strlen(tmp);
+				sprintf(tmp + i, "   LOCK modcnt %lx\n", MOD_USE_COUNT);
+				HiSax_putstatus(csta, tmp);
+			}
+#endif				/* MODULE */
+			break;
+		case (ISDN_CMD_UNLOCK):
+			HiSax_mod_dec_use_count();
+#ifdef MODULE
+			if (csta->channel[0].debug & 1024) {
+				jiftime(tmp, jiffies);
+				i = strlen(tmp);
+				sprintf(tmp + i, " UNLOCK modcnt %lx\n", MOD_USE_COUNT);
+				HiSax_putstatus(csta, tmp);
+			}
+#endif				/* MODULE */
+			break;
+		case (ISDN_CMD_IOCTL):
+			switch (ic->arg) {
+				case (0):
+					HiSax_reportcard(csta->cardnr);
+					for (i = 0; i < 2; i++)
+						channel_report(&csta->channel[i]);
+					break;
+				case (1):
+					num = *(unsigned int *) ic->parm.num;
+					distr_debug(csta, num);
+					sprintf(tmp, "debugging flags card %d set to %x\n",
+						csta->cardnr + 1, num);
+					HiSax_putstatus(csta, tmp);
+					printk(KERN_DEBUG "HiSax: %s", tmp);
+					break;
+				case (2):
+					num = *(unsigned int *) ic->parm.num;
+					i = num >> 8;
+					if (i >= 2)
+						break;
+					chanp = csta->channel + i;
+					chanp->impair = num & 0xff;
+					if (chanp->debug & 1) {
+						sprintf(tmp, "IMPAIR %x", chanp->impair);
+						link_debug(chanp, tmp, 1);
+					}
+					break;
+				case (3):
+					for (i = 0; i < *(unsigned int *) ic->parm.num; i++)
+						HiSax_mod_dec_use_count();
+					break;
+				case (4):
+					for (i = 0; i < *(unsigned int *) ic->parm.num; i++)
+						HiSax_mod_inc_use_count();
+					break;
+				case (5):	/* set card in leased mode */
+					csta->channel[0].leased = 1;
+					csta->channel[1].leased = 1;
+					sprintf(tmp, "card %d set into leased mode\n",
+						csta->cardnr + 1);
+					HiSax_putstatus(csta, tmp);
+					break;
+#ifdef MODULE
+				case (55):
+#if (LINUX_VERSION_CODE < 0x020111)
+					MOD_USE_COUNT = MOD_VISITED;
+#else
+					MOD_USE_COUNT = 0;
+#endif
+					HiSax_mod_inc_use_count();
+					break;
+#endif				/* MODULE */
+				case (11):
+					csta->debug = *(unsigned int *) ic->parm.num;
+					sprintf(tmp, "l1 debugging flags card %d set to %x\n",
+						csta->cardnr + 1, csta->debug);
+					HiSax_putstatus(cards[0].sp, tmp);
+					printk(KERN_DEBUG "HiSax: %s", tmp);
+					break;
+				case (13):
+					csta->channel[0].is.l3.debug = *(unsigned int *) ic->parm.num;
+					csta->channel[1].is.l3.debug = *(unsigned int *) ic->parm.num;
+					sprintf(tmp, "l3 debugging flags card %d set to %x\n",
+						csta->cardnr + 1, *(unsigned int *) ic->parm.num);
+					HiSax_putstatus(cards[0].sp, tmp);
+					printk(KERN_DEBUG "HiSax: %s", tmp);
+					break;
+				default:
+					printk(KERN_DEBUG "HiSax: invalid ioclt %d\n",
+					       (int) ic->arg);
+					return (-EINVAL);
+			}
+			break;
+		default:
+			break;
+	}
+
+	return (0);
+}
+
+int
+HiSax_writebuf(int id, int chan, const u_char * buf, int count, int user)
+{
+	struct IsdnCardState *csta = hisax_findcard(id);
+	struct Channel *chanp;
+	struct PStack *st;
+	struct BufHeader *ibh;
+	int err, i;
+	byte *ptr;
+	char tmp[64];
+
+	if (!csta) {
+		printk(KERN_ERR
+		    "HiSax: if_sendbuf called with invalid driverId!\n");
+		return -ENODEV;
+	}
+	chanp = csta->channel + chan;
+	st = &chanp->ds;
+	if (!chanp->data_open) {
+		link_debug(chanp, "writebuf: channel not open", 1);
+		return -EIO;
+	}
+	err = BufPoolGet(&ibh, st->l1.sbufpool, GFP_ATOMIC, st, 21);
+	if (err) {
+		/* Must return 0 here, since this is not an error
+		 * but a temporary lack of resources.
+		 */
+		if (chanp->debug & 1) {
+			sprintf(tmp, "writebuf: no buffers for %d bytes", count);
+			link_debug(chanp, tmp, 1);
+		}
+		return 0;
+	}
+#if 0
+	if (chanp->debug & 1) {
+		sprintf(tmp, "writebuf: %d bytes", count);
+		link_debug(chanp, tmp, 1);
+	}
+#endif
+	ptr = DATAPTR(ibh);
+	if (chanp->lc_b.l2_establish)
+		i = st->l2.ihsize;
+	else
+		i = 0;
+
+	if ((count + i) > BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS)) {
+		sprintf(tmp, "writebuf: packet too large (%d bytes)", count + i);
+		printk(KERN_WARNING "HiSax_%s !\n", tmp);
+		link_debug(chanp, tmp, 1);
+		return (-EINVAL);
+	}
+	ptr += i;
+
+	if (user)
+		copy_from_user(ptr, buf, count);
+	else
+		memcpy(ptr, buf, count);
+	ibh->datasize = count + i;
+
+	if (chanp->data_open) {
+		if (chanp->lc_b.l2_establish)
+			chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, ibh);
+		else
+			chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, ibh);
+		return (count);
+	} else {
+		BufPoolRelease(ibh);
+		return (0);
+	}
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov