patch-1.3.69 linux/drivers/isdn/teles/callc.c

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

diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/callc.c linux/drivers/isdn/teles/callc.c
@@ -0,0 +1,1316 @@
+#define __NO_VERSION__
+#include "teles.h"
+
+extern struct IsdnCard cards[];
+extern int      nrcards;
+extern int      drid;
+extern isdn_if  iif;
+extern void     teles_mod_dec_use_count(void);
+extern void     teles_mod_inc_use_count(void);
+
+static int      init_ds(int chan, int incoming);
+static void     release_ds(int chan);
+static char    *strcpyupto(char *dest, char *src, char upto);
+
+static struct Fsm callcfsm =
+{NULL, 0, 0},   lcfsm =
+{NULL, 0, 0};
+
+struct Channel *chanlist;
+static int      chancount = 0;
+unsigned int    debugflags = 0;
+
+#define TMR_DCHAN_EST 2000
+
+static void
+stat_debug(struct Channel *chanp, char *s)
+{
+	char            tmp[100], tm[32];
+
+	jiftime(tm, jiffies);
+	sprintf(tmp, "%s Channel %d HL->LL %s\n", tm, chanp->chan, s);
+	teles_putstatus(tmp);
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+stat_error(struct Channel *chanp, char *s)
+{
+        char            tmp[100];
+
+        sprintf(tmp, "Channel %d: %s\n", chanp->chan, s);
+        teles_putstatus(tmp);
+}
+#endif
+
+enum {
+        ST_NULL,           /*  0 inactive                                               */
+        ST_OUT,            /*  1 outgoing, awaiting SETUP confirm                       */
+        ST_CLEAR,          /*  2 call release, awaiting RELEASE confirm                 */
+        ST_OUT_W,          /*  3 outgoing, awaiting d-channel establishment             */
+        ST_REL_W,          /*  4 awaiting d-channel release                             */
+        ST_IN_W,           /*  5 incoming, awaiting d-channel establishment             */
+        ST_IN,             /*  6 incoming call received                                 */
+        ST_IN_SETUP,       /*  7 incoming, SETUP response sent                          */
+        ST_IN_DACT,        /*  8 incoming connected, no b-channel prot.                 */
+        ST_OUT_ESTB,       /* 10 outgoing connected, awaiting b-channel prot. estbl.    */
+        ST_ACTIVE,         /* 11 active, b channel prot. established                    */
+        ST_BC_HANGUP,      /* 12 call clear. (initiator), awaiting b channel prot. rel. */
+        ST_PRO_W,          /* 13 call clear. (initiator), DISCONNECT req. sent          */
+        ST_ANT_W,          /* 14 call clear. (receiver), awaiting DISCONNECT ind.       */
+        ST_DISC_BC_HANGUP, /*    d channel gone, wait for b channel deactivation        */
+        ST_D_ERR,          /*    d channel released while active                        */
+};
+
+#define STATE_COUNT (ST_D_ERR+1)
+
+static char    *strState[] =
+{
+        "ST_NULL",
+        "ST_OUT",
+        "ST_CLEAR",
+        "ST_OUT_W",
+        "ST_REL_W",
+        "ST_IN_W",
+        "ST_IN",
+        "ST_IN_SETUP",
+        "ST_IN_DACT",
+        "ST_OUT_ESTB",
+        "ST_ACTIVE",
+        "ST_BC_HANGUP",
+        "ST_PRO_W",
+        "ST_ANT_W",
+        "ST_DISC_BC_HANGUP",
+        "ST_D_ERR",
+};
+
+enum {
+        EV_DIAL,           /*  0 */
+        EV_SETUP_CNF,      /*  1 */
+        EV_ACCEPTB,        /*  2 */
+        EV_DISCONNECT_CNF, /*  5 */
+        EV_DISCONNECT_IND, /*  6 */
+        EV_RELEASE_CNF,    /*  7 */
+        EV_DLEST,          /*  8 */
+        EV_DLRL,           /*  9 */
+        EV_SETUP_IND,      /* 10 */
+        EV_RELEASE_IND,    /* 11 */
+        EV_ACCEPTD,        /* 12 */
+        EV_SETUP_CMPL_IND, /* 13 */
+        EV_BC_EST,         /* 14 */
+        EV_WRITEBUF,       /* 15 */
+        EV_DATAIN,         /* 16 */
+        EV_HANGUP,         /* 17 */
+        EV_BC_REL,         /* 18 */
+        EV_CINF,           /* 19 */
+};
+
+#define EVENT_COUNT (EV_CINF+1)
+
+static char    *strEvent[] =
+{
+        "EV_DIAL",
+        "EV_SETUP_CNF",
+        "EV_ACCEPTB",
+        "EV_DISCONNECT_CNF",
+        "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",
+};
+
+enum {
+        ST_LC_NULL,
+        ST_LC_ACTIVATE_WAIT,
+        ST_LC_DELAY,
+        ST_LC_ESTABLISH_WAIT,
+        ST_LC_CONNECTED,
+        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_RELEASE_WAIT",
+};
+
+enum {
+        EV_LC_ESTABLISH,
+        EV_LC_PH_ACTIVATE,
+        EV_LC_PH_DEACTIVATE,
+        EV_LC_DL_ESTABLISH,
+        EV_LC_TIMER,
+        EV_LC_DL_RELEASE,
+        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_RELEASE",
+        "EV_LC_RELEASE",
+};
+
+#define LC_D  0
+#define LC_B  1
+
+static int
+my_atoi(char *s)
+{
+        int             i, n;
+
+        n = 0;
+        if (!s)
+                return -1;
+        for (i = 0; *s >= '0' && *s <= '9'; i++, s++)
+                n = 10 * n + (*s - '0');
+        return n;
+}
+
+/*
+ * Dial out
+ */
+static void
+r1(struct FsmInst *fi, int event, void *arg)
+{
+        isdn_ctrl      *ic = arg;
+        struct Channel *chanp = fi->userdata;
+        char           *ptr;
+        char            sis[3];
+
+        /* Destination Phone-Number */
+        ptr = strcpyupto(chanp->para.called, ic->num, ',');
+        /* Source Phone-Number */
+        ptr = strcpyupto(chanp->para.calling, ptr + 1, ',');
+        if (!strcmp(chanp->para.calling, "0"))
+                chanp->para.calling[0] = '\0';
+
+        /* Service-Indicator 1 */
+        ptr = strcpyupto(sis, ptr + 1, ',');
+        chanp->para.info = my_atoi(sis);
+
+        /* Service-Indicator 2 */
+        ptr = strcpyupto(sis, ptr + 1, '\0');
+        chanp->para.info2 = my_atoi(sis);
+
+        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):
+                  chanp->lc_b.l2_establish = 0;
+                  break;
+          default:
+                  printk(KERN_WARNING "r1 unknown protocol\n");
+                  break;
+        }
+
+        FsmChangeState(fi, ST_OUT_W);
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+ll_hangup(struct Channel *chanp, int bchantoo)
+{
+        isdn_ctrl       ic;
+
+        if (bchantoo) {
+                if (chanp->debug & 1)
+                        stat_debug(chanp, "STAT_BHUP");
+                ic.driver = drid;
+                ic.command = ISDN_STAT_BHUP;
+                ic.arg = chanp->chan;
+                iif.statcallb(&ic);
+        }
+        if (chanp->debug & 1)
+                stat_debug(chanp, "STAT_DHUP");
+        ic.driver = drid;
+        ic.command = ISDN_STAT_DHUP;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+}
+
+static void
+r2(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+        FsmChangeState(fi, ST_CLEAR);
+        ll_hangup(chanp, 0);
+}
+
+static void
+r3(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+        FsmChangeState(fi, ST_REL_W);
+}
+
+static void
+r4(struct FsmInst *fi, int event, void *arg)
+{
+        FsmChangeState(fi, ST_NULL);
+}
+
+static void
+r5(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->para.callref = chanp->outcallref;
+
+        chanp->outcallref++;
+        if (chanp->outcallref == 128)
+                chanp->outcallref = 64;
+
+        chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL);
+
+        FsmChangeState(fi, ST_OUT);
+}
+
+static void
+r6(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_IN_W);
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+r7(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+        isdn_ctrl       ic;
+
+        chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL);
+
+        FsmChangeState(fi, ST_IN);
+
+        /*
+         * Report incoming calls only once to linklevel, use octet 3 of
+         * channel identification information element. (it's value
+         * is copied to chanp->para.bchannel in l3s12(), file isdnl3.c)
+         */
+        if (((chanp->chan & 1) + 1) & chanp->para.bchannel) {
+                if (chanp->debug & 1)
+                        stat_debug(chanp, "STAT_ICALL");
+                ic.driver = drid;
+                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')
+                 */
+                sprintf(ic.num, "%s,%d,0,%s", chanp->para.calling, chanp->para.info,
+                        chanp->para.called);
+                iif.statcallb(&ic);
+        }
+}
+
+static void
+r8(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_IN_SETUP);
+        chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL);
+
+}
+
+static void
+r9(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_IN_DACT);
+
+        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):
+                  chanp->lc_b.l2_establish = 0;
+                  break;
+          default:
+                  printk(KERN_WARNING "r9 unknown protocol\n");
+                  break;
+        }
+
+        init_ds(chanp->chan, !0);
+
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+r10(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_OUT_ESTB);
+
+        init_ds(chanp->chan, 0);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+
+}
+
+static void
+r12(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+        isdn_ctrl       ic;
+
+        FsmChangeState(fi, ST_ACTIVE);
+        chanp->data_open = !0;
+
+        if (chanp->debug & 1)
+                stat_debug(chanp, "STAT_DCONN");
+        ic.driver = drid;
+        ic.command = ISDN_STAT_DCONN;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+
+        if (chanp->debug & 1)
+                stat_debug(chanp, "STAT_BCONN");
+        ic.driver = drid;
+        ic.command = ISDN_STAT_BCONN;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+prp(byte * p, int size)
+{
+        while (size--)
+                printk("%2x ", *p++);
+        printk("\n");
+}
+#endif
+
+static void
+r15(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        FsmChangeState(fi, ST_BC_HANGUP);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r16(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_PRO_W);
+        chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+}
+
+static void
+r17(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_ANT_W);
+}
+
+static void
+r18(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_REL_W);
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r19(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_CLEAR);
+
+        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r20(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_NULL);
+
+        ll_hangup(chanp, 0);
+}
+
+static void
+r21(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        FsmChangeState(fi, ST_DISC_BC_HANGUP);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r22(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_CLEAR);
+
+        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r23(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_PRO_W);
+        chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+}
+
+static void
+r24(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        FsmChangeState(fi, ST_D_ERR);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r25(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r26(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+        isdn_ctrl       ic;
+
+
+        ic.driver = drid;
+        ic.command = ISDN_STAT_CINF;
+        ic.arg = chanp->chan;
+        sprintf(ic.num, "%d", chanp->para.chargeinfo);
+        iif.statcallb(&ic);
+}
+
+
+
+static struct FsmNode fnlist[] =
+{
+        {ST_NULL, EV_DIAL, r1},
+        {ST_OUT_W, EV_DLEST, r5},
+        {ST_OUT_W, EV_DLRL, r20},
+        {ST_OUT, EV_DISCONNECT_IND, r2},
+        {ST_CLEAR, EV_RELEASE_CNF, r3},
+        {ST_REL_W, EV_DLRL, r4},
+        {ST_NULL, EV_SETUP_IND, r6},
+        {ST_IN_W, EV_DLEST, r7},
+        {ST_IN, EV_RELEASE_IND, r3},
+        {ST_IN, EV_ACCEPTD, r8},
+        {ST_IN_SETUP, EV_SETUP_CMPL_IND, r9},
+        {ST_OUT, EV_SETUP_CNF, r10},
+        {ST_OUT_ESTB, EV_BC_EST, r12},
+        {ST_OUT_ESTB, EV_BC_REL, r23},
+        {ST_IN_DACT, EV_BC_EST, r12},
+        {ST_ACTIVE, EV_HANGUP, r15},
+        {ST_BC_HANGUP, EV_BC_REL, r16},
+        {ST_BC_HANGUP, EV_DISCONNECT_IND, r21},
+        {ST_ACTIVE, EV_BC_REL, r17},
+        {ST_ACTIVE, EV_DISCONNECT_IND, r21},
+        {ST_ACTIVE, EV_DLRL, r24},
+        {ST_ACTIVE, EV_CINF, r26},
+        {ST_PRO_W, EV_RELEASE_IND, r18},
+        {ST_ANT_W, EV_DISCONNECT_IND, r19},
+        {ST_DISC_BC_HANGUP, EV_BC_REL, r22},
+        {ST_D_ERR, EV_BC_REL, r25},
+};
+
+#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_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);
+        } else {
+                FsmChangeState(fi, ST_LC_NULL);
+                lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+                lf->lccall(lf, LC_RELEASE, NULL);
+        }
+}
+
+static void
+lc_r5(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        FsmChangeState(fi, ST_LC_NULL);
+        lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+        lf->lccall(lf, LC_RELEASE, NULL);
+}
+
+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_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_r3},
+        {ST_LC_CONNECTED, EV_LC_RELEASE, lc_r4},
+        {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_r5},
+        {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_r5},
+        {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_r5},
+        {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_r5},
+};
+
+#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(int chan)
+{
+        struct PStack  *st = &chanlist[chan].ds;
+        struct IsdnCardState *sp;
+        struct HscxState *hsp;
+
+        sp = st->l1.hardware;
+        hsp = sp->hs + chanlist[chan].hscx;
+
+        close_hscxstate(hsp);
+
+        switch (chanlist[chan].l2_active_protocol) {
+          case (ISDN_PROTO_L2_X75I):
+                  releasestack_isdnl2(st);
+                  break;
+          case (ISDN_PROTO_L2_HDLC):
+                  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;
+        }
+}
+
+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;
+
+        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;
+        }
+}
+
+static void
+init_is(int chan, unsigned int ces)
+{
+        struct PStack  *st = &(chanlist[chan].is);
+        struct IsdnCardState *sp = chanlist[chan].sp;
+        char            tmp[128];
+
+        setstack_teles(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", chan);
+        setstack_isdnl2(st, tmp);
+        setstack_isdnl3(st);
+        st->l2.debug = 2;
+        st->l3.debug = 2;
+        st->l2.debug = 0xff;
+        st->l3.debug = 0xff;
+        st->l4.userdata = chanlist + chan;
+        st->l4.l2writewakeup = NULL;
+
+        st->l3.l3l4 = ll_handler;
+        st->l1.l1man = cc_l1man;
+        st->l2.l2man = cc_l2man;
+
+        st->pa = &chanlist[chan].para;
+        teles_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);
+        teles_putstatus(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);
+        teles_putstatus(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);
+        teles_putstatus(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, int cardnr, int hscx,
+          unsigned int ces)
+{
+        struct IsdnCard *card = cards + cardnr;
+        struct Channel *chanp = chanlist + chan;
+
+        chanp->sp = card->sp;
+        chanp->hscx = hscx;
+        chanp->chan = chan;
+        chanp->incoming = 0;
+        chanp->debug = 0;
+        init_is(chan, ces);
+
+        chanp->fi.fsm = &callcfsm;
+        chanp->fi.state = ST_NULL;
+        chanp->fi.debug = 0;
+        chanp->fi.userdata = chanp;
+        chanp->fi.printdebug = callc_debug;
+
+        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(void)
+{
+        int             i, ces, c;
+
+        chancount = 0;
+        for (i = 0; i < nrcards; i++)
+                if (cards[i].sp)
+                        chancount += 2;
+
+        chanlist = (struct Channel *) Smalloc(sizeof(struct Channel) *
+                                      chancount, GFP_KERNEL, "chanlist");
+
+        c = 0;
+        ces = randomces();
+        for (i = 0; i < nrcards; i++)
+                if (cards[i].sp) {
+                        init_chan(c++, i, 1, ces++);
+                        ces %= 0xffff;
+                        init_chan(c++, i, 0, ces++);
+                        ces %= 0xffff;
+                }
+        printk(KERN_INFO "channels %d\n", chancount);
+        return (chancount);
+
+}
+
+static void
+release_is(int chan)
+{
+        struct PStack  *st = &chanlist[chan].is;
+
+        releasestack_isdnl2(st);
+        teles_rmlist(st->l1.hardware, st);
+        BufQueueRelease(&st->l2.i_queue);
+}
+
+static void
+release_chan(int chan)
+{
+#if 0
+        release_ds(chan);
+#endif
+        release_is(chan);
+}
+
+void
+CallcFreeChan(void)
+{
+        int             i;
+
+        for (i = 0; i < chancount; i++)
+                release_chan(i);
+        Sfree((void *) chanlist);
+}
+
+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;
+                          iif.rcvcallb(drid, chanp->chan, ptr, size);
+                  }
+                  BufPoolRelease(ibh);
+                  break;
+          default:
+                  printk(KERN_WARNING "lldata_handler unknown primitive\n");
+                  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);
+                          iif.rcvcallb(drid, chanp->chan, ptr, ibh->datasize);
+                  }
+                  BufPoolRelease(ibh);
+                  break;
+          default:
+                  printk(KERN_WARNING "lltrans_handler unknown primitive\n");
+                  break;
+        }
+}
+
+static void
+ll_writewakeup(struct PStack *st)
+{
+        struct Channel *chanp = st->l4.userdata;
+        isdn_ctrl       ic;
+
+        ic.driver = drid;
+        ic.command = ISDN_STAT_BSENT;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+}
+
+static int
+init_ds(int chan, int incoming)
+{
+        struct PStack  *st = &(chanlist[chan].ds);
+        struct IsdnCardState *sp = (struct IsdnCardState *)
+        chanlist[chan].is.l1.hardware;
+        struct HscxState *hsp = sp->hs + chanlist[chan].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 = 3;
+        st->l2.n200 = 4;           /* try 4 times       */
+        st->l2.t203 = 5000;        /* 5000 milliseconds */
+
+        st->l2.debug = 0xff;
+        st->l3.debug = 0xff;
+        switch (chanlist[chan].l2_active_protocol) {
+          case (ISDN_PROTO_L2_X75I):
+                  sprintf(tmp, "Channel %d x.75", chan);
+                  setstack_isdnl2(st, tmp);
+                  st->l2.l2l3 = lldata_handler;
+                  st->l1.l1man = dcc_l1man;
+                  st->l2.l2man = dcc_l2man;
+                  st->l4.userdata = chanlist + chan;
+                  st->l4.l1writewakeup = NULL;
+                  st->l4.l2writewakeup = ll_writewakeup;
+                  st->l2.l2m.debug = debugflags & 16;
+                  st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+                  st->l1.hscxmode = 2;        /* Packet-Mode ? */
+                  st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
+                  break;
+          case (ISDN_PROTO_L2_HDLC):
+                  st->l1.l1l2 = lltrans_handler;
+                  st->l1.l1man = dcc_l1man;
+                  st->l4.userdata = chanlist + chan;
+                  st->l4.l1writewakeup = ll_writewakeup;
+                  st->l1.hscxmode = 2;
+                  st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
+                  break;
+        }
+
+        return (0);
+
+}
+
+static void
+channel_report(int i)
+{
+}
+
+static void
+command_debug(struct Channel *chanp, char *s)
+{
+        char            tmp[64], tm[32];
+
+        jiftime(tm, jiffies);
+        sprintf(tmp, "%s Channel %d LL->HL %s\n", tm, chanp->chan, s);
+        teles_putstatus(tmp);
+}
+
+static void
+distr_debug(void)
+{
+        int             i;
+
+        for (i = 0; i < chancount; i++) {
+                chanlist[i].debug = debugflags & 1;
+                chanlist[i].fi.debug = debugflags & 2;
+                chanlist[i].is.l2.l2m.debug = debugflags & 8;
+                chanlist[i].ds.l2.l2m.debug = debugflags & 16;
+        }
+        for (i = 0; i < nrcards; i++)
+                if (cards[i].sp)
+                        cards[i].sp->dlogflag = debugflags & 4;
+}
+
+int
+teles_command(isdn_ctrl * ic)
+{
+        struct Channel *chanp;
+        char            tmp[64];
+        int             i;
+        unsigned int    num;
+
+        switch (ic->command) {
+          case (ISDN_CMD_SETEAZ):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "SETEAZ");
+                  return (0);
+          case (ISDN_CMD_DIAL):
+                  chanp = chanlist + (ic->arg & 0xff);
+                  if (chanp->debug & 1) {
+                          sprintf(tmp, "DIAL %s", ic->num);
+                          command_debug(chanp, tmp);
+                  }
+                  FsmEvent(&chanp->fi, EV_DIAL, ic);
+                  return (0);
+          case (ISDN_CMD_ACCEPTB):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "ACCEPTB");
+                  FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
+                  break;
+          case (ISDN_CMD_ACCEPTD):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "ACCEPTD");
+                  FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
+                  break;
+          case (ISDN_CMD_HANGUP):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "HANGUP");
+                  FsmEvent(&chanp->fi, EV_HANGUP, NULL);
+                  break;
+          case (ISDN_CMD_LOCK):
+                  teles_mod_inc_use_count();
+                  break;
+          case (ISDN_CMD_UNLOCK):
+                  teles_mod_dec_use_count();
+                  break;
+          case (ISDN_CMD_IOCTL):
+                  switch (ic->arg) {
+                    case (0):
+                            for (i = 0; i < nrcards; i++)
+                                    if (cards[i].sp)
+                                            teles_reportcard(i);
+                            for (i = 0; i < chancount; i++)
+                                    channel_report(i);
+                            break;
+                    case (1):
+                            debugflags = *(unsigned int *) ic->num;
+                            distr_debug();
+                            sprintf(tmp, "debugging flags set to %x\n", debugflags);
+                            teles_putstatus(tmp);
+                            break;
+                    case (2):
+                            num = *(unsigned int *) ic->num;
+                            i = num >> 8;
+                            if (i >= chancount)
+                                    break;
+                            chanp = chanlist + i;
+                            chanp->impair = num & 0xff;
+                            if (chanp->debug & 1) {
+                                    sprintf(tmp, "IMPAIR %x", chanp->impair);
+                                    command_debug(chanp, tmp);
+                            }
+                            break;
+                  }
+                  break;
+          case (ISDN_CMD_SETL2):
+                  chanp = chanlist + (ic->arg & 0xff);
+                  if (chanp->debug & 1) {
+                          sprintf(tmp, "SETL2 %ld", ic->arg >> 8);
+                          command_debug(chanp, tmp);
+                  }
+                  chanp->l2_protocol = ic->arg >> 8;
+                  break;
+          default:
+                  break;
+        }
+
+        return (0);
+}
+
+int
+teles_writebuf(int id, int chan, const u_char * buf, int count, int user)
+{
+        struct Channel *chanp = chanlist + chan;
+        struct PStack  *st = &chanp->ds;
+        struct BufHeader *ibh;
+        int             err, i;
+        byte           *ptr;
+
+        err = BufPoolGet(&ibh, st->l1.sbufpool, GFP_ATOMIC, st, 21);
+        if (err)
+                return (0);
+
+        if (count > BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS)) {
+                printk(KERN_WARNING "teles_writebuf: packet too large!\n");
+                return (-EINVAL);
+        }
+        ptr = DATAPTR(ibh);
+        if (chanp->lc_b.l2_establish)
+                i = st->l2.ihsize;
+        else
+                i = 0;
+
+        ptr += i;
+
+        if (user)
+                memcpy_fromfs(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);
+        }
+
+}
+
+static char    *
+strcpyupto(char *dest, char *src, char upto)
+{
+        while (*src && (*src != upto) && (*src != '\0'))
+                *dest++ = *src++;
+        *dest = '\0';
+        return (src);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this