isdn4linux/drivers/isdn/teles/callc.c

1483 lines
42 KiB
C

/* $Id$
*
* $Log$
* Revision 1.14 1996/10/22 23:14:14 fritz
* Changes for compatibility to 2.0.X and 2.1.X kernels.
*
* Revision 1.13 1996/06/24 17:15:55 fritz
* corrected return code of teles_writebuf()
*
* Revision 1.12 1996/06/12 16:15:33 fritz
* Extended user-configurable debugging flags.
*
* Revision 1.11 1996/06/07 12:32:20 fritz
* More changes to support suspend/resume.
*
* Revision 1.10 1996/06/06 21:24:21 fritz
* Started adding support for suspend/resume.
*
* Revision 1.9 1996/05/31 12:23:57 jdenoud
* Jan: added channel open check to teles_writebuf
*
* Revision 1.8 1996/05/31 01:00:38 fritz
* Changed return code of teles_writebuf, when out of memory.
*
* Revision 1.7 1996/05/17 03:40:37 fritz
* General cleanup.
*
* Revision 1.6 1996/05/10 22:42:07 fritz
* Added entry for EV_RELEASE_CNF in ST_OUT (if no D-Channel avail.)
*
* Revision 1.5 1996/05/06 10:16:15 fritz
* Added voice stuff.
*
* Revision 1.4 1996/04/30 22:04:05 isdn4dev
* improved callback Karsten Keil
*
* Revision 1.3 1996/04/30 10:04:19 fritz
* Started voice support.
* Added printk() to debug-switcher for easier
* synchronization between printk()'s and output
* of /dev/isdnctrl.
*
* Revision 1.2 1996/04/20 16:42:29 fritz
* Changed statemachine to allow reject of incoming calls.
*
* Revision 1.1 1996/04/13 10:20:59 fritz
* Initial revision
*
*
*/
#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);
}
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_OUT_W_HANGUP, /* Outgoing waiting for D-Channel hangup received */
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_OUT_W_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 */
EV_SUSPEND, /* 20 */
EV_RESUME, /* 21 */
};
#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",
"EV_SUSPEND",
"EV_RESUME",
};
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):
case (ISDN_PROTO_L2_TRANS):
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
r2_1(struct FsmInst *fi, int event, void *arg)
{
struct Channel *chanp = fi->userdata;
chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
FsmChangeState(fi, ST_OUT_W_HANGUP);
}
static void
r2_2(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
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
r3_1(struct FsmInst *fi, int event, void *arg)
{
struct Channel *chanp = fi->userdata;
chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
FsmChangeState(fi, ST_REL_W);
ll_hangup(chanp, 0);
}
static void
r4(struct FsmInst *fi, int event, void *arg)
{
struct Channel *chanp=fi->userdata;
chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
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;
/*
* 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) {
chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL);
FsmChangeState(fi, ST_IN);
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);
} else {
chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
FsmChangeState(fi, ST_REL_W);
}
}
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):
case (ISDN_PROTO_L2_TRANS):
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);
}
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
r17_1(struct FsmInst *fi, int event, void *arg)
{
struct Channel *chanp = fi->userdata;
chanp->data_open = 0;
release_ds(chanp->chan);
chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
FsmEvent(&chanp->lc_d.lcfi,EV_LC_RELEASE,NULL);
FsmChangeState(fi, ST_NULL);
ll_hangup(chanp,!0);
}
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;
chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
FsmEvent(&chanp->lc_d.lcfi,EV_LC_RELEASE,NULL);
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
r23_1(struct FsmInst *fi, int event, void *arg)
{
struct Channel *chanp = fi->userdata;
release_ds(chanp->chan);
chanp->is.l4.l4l3(&chanp->is, CC_DLRL,NULL);
FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE,NULL);
FsmChangeState(fi, ST_NULL);
ll_hangup(chanp,!0);
}
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_W, EV_RELEASE_CNF, r2_2 },
{ST_OUT, EV_DISCONNECT_IND, r2},
{ST_OUT, EV_SETUP_CNF, r10},
{ST_OUT, EV_HANGUP, r2_1},
{ST_OUT, EV_RELEASE_IND, r20},
{ST_OUT, EV_RELEASE_CNF, r20},
{ST_OUT, EV_DLRL, r2_2},
{ST_OUT_W_HANGUP, EV_RELEASE_IND, r2_2},
{ST_OUT_W_HANGUP, EV_DLRL, r20},
{ST_CLEAR, EV_RELEASE_CNF, r3},
{ST_CLEAR, EV_DLRL, r20},
{ST_REL_W, EV_DLRL, r4},
{ST_NULL, EV_SETUP_IND, r6},
{ST_IN_W, EV_DLEST, r7},
{ST_IN_W, EV_DLRL, r3_1},
{ST_IN, EV_DLRL, r3_1},
{ST_IN, EV_HANGUP, r2_1},
{ST_IN, EV_RELEASE_IND, r2_2},
{ST_IN, EV_RELEASE_CNF, r2_2},
{ST_IN, EV_ACCEPTD, r8},
{ST_IN_SETUP, EV_HANGUP, r2_1},
{ST_IN_SETUP, EV_SETUP_CMPL_IND, r9},
{ST_IN_SETUP, EV_RELEASE_IND, r2_2},
{ST_IN_SETUP, EV_DISCONNECT_IND, r2},
{ST_IN_SETUP, EV_DLRL, r20},
{ST_OUT_ESTB, EV_BC_EST, r12},
{ST_OUT_ESTB, EV_BC_REL, r23},
{ST_OUT_ESTB, EV_DLRL, r23_1},
{ST_IN_DACT, EV_BC_EST, r12},
{ST_IN_DACT, EV_BC_REL, r17},
{ST_IN_DACT, EV_DLRL, r17_1},
{ST_ACTIVE, EV_HANGUP, r15},
{ST_ACTIVE, EV_BC_REL, r17},
{ST_ACTIVE, EV_DISCONNECT_IND, r21},
{ST_ACTIVE, EV_DLRL, r24},
{ST_ACTIVE, EV_CINF, r26},
{ST_ACTIVE, EV_RELEASE_IND, r17},
{ST_BC_HANGUP, EV_BC_REL, r16},
{ST_BC_HANGUP, EV_DISCONNECT_IND, r21},
{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):
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;
}
}
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);
}
void
CallcFreeChan(void)
{
int i;
for (i = 0; i < chancount; i++)
release_is(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 = 7;
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;
case (ISDN_PROTO_L2_TRANS):
st->l1.l1l2 = lltrans_handler;
st->l1.l1man = dcc_l1man;
st->l4.userdata = chanlist + chan;
st->l4.l1writewakeup = ll_writewakeup;
st->l1.hscxmode = 1;
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;
cards[i].sp->debug = debugflags & 32;
}
}
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_SUSPEND):
chanp = chanlist + ic->arg;
if (chanp->debug & 1) {
sprintf(tmp, "SUSPEND %s", ic->num);
command_debug(chanp, tmp);
}
FsmEvent(&chanp->fi, EV_SUSPEND, ic);
break;
case (ISDN_CMD_RESUME):
chanp = chanlist + ic->arg;
if (chanp->debug & 1) {
sprintf(tmp, "RESUME %s", ic->num);
command_debug(chanp, tmp);
}
FsmEvent(&chanp->fi, EV_RESUME, ic);
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);
printk(KERN_DEBUG "%s", 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;
if (!chanp->data_open) {
printk(KERN_DEBUG "teles_writebuf: channel not open\n");
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.
*/
return 0;
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)) {
printk(KERN_WARNING "teles_writebuf: packet too large!\n");
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);
}
}
static char *
strcpyupto(char *dest, char *src, char upto)
{
while (*src && (*src != upto) && (*src != '\0'))
*dest++ = *src++;
*dest = '\0';
return (src);
}