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
- Lines: 1947
- Date:
Tue Feb 25 17:12:49 1997
- Orig file:
v2.1.26/linux/drivers/isdn/hisax/callc.c
- Orig date:
Wed Dec 31 16:00:00 1969
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