|  | /* $Id: isdnl1.c,v 2.46.2.5 2004/02/11 13:21:34 keil Exp $ | 
|  | * | 
|  | * common low level stuff for Siemens Chipsetbased isdn cards | 
|  | * | 
|  | * Author       Karsten Keil | 
|  | *              based on the teles driver from Jan den Ouden | 
|  | * Copyright    by Karsten Keil      <keil@isdn4linux.de> | 
|  | * | 
|  | * This software may be used and distributed according to the terms | 
|  | * of the GNU General Public License, incorporated herein by reference. | 
|  | * | 
|  | * For changes and modifications please read | 
|  | * Documentation/isdn/HiSax.cert | 
|  | * | 
|  | * Thanks to    Jan den Ouden | 
|  | *              Fritz Elfert | 
|  | *              Beat Doebeli | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <linux/init.h> | 
|  | #include <linux/gfp.h> | 
|  | #include "hisax.h" | 
|  | #include "isdnl1.h" | 
|  |  | 
|  | const char *l1_revision = "$Revision: 2.46.2.5 $"; | 
|  |  | 
|  | #define TIMER3_VALUE 7000 | 
|  |  | 
|  | static struct Fsm l1fsm_b; | 
|  | static struct Fsm l1fsm_s; | 
|  |  | 
|  | enum { | 
|  | ST_L1_F2, | 
|  | ST_L1_F3, | 
|  | ST_L1_F4, | 
|  | ST_L1_F5, | 
|  | ST_L1_F6, | 
|  | ST_L1_F7, | 
|  | ST_L1_F8, | 
|  | }; | 
|  |  | 
|  | #define L1S_STATE_COUNT (ST_L1_F8+1) | 
|  |  | 
|  | static char *strL1SState[] = | 
|  | { | 
|  | "ST_L1_F2", | 
|  | "ST_L1_F3", | 
|  | "ST_L1_F4", | 
|  | "ST_L1_F5", | 
|  | "ST_L1_F6", | 
|  | "ST_L1_F7", | 
|  | "ST_L1_F8", | 
|  | }; | 
|  |  | 
|  | #ifdef HISAX_UINTERFACE | 
|  | static | 
|  | struct Fsm l1fsm_u = | 
|  | {NULL, 0, 0, NULL, NULL}; | 
|  |  | 
|  | enum { | 
|  | ST_L1_RESET, | 
|  | ST_L1_DEACT, | 
|  | ST_L1_SYNC2, | 
|  | ST_L1_TRANS, | 
|  | }; | 
|  |  | 
|  | #define L1U_STATE_COUNT (ST_L1_TRANS+1) | 
|  |  | 
|  | static char *strL1UState[] = | 
|  | { | 
|  | "ST_L1_RESET", | 
|  | "ST_L1_DEACT", | 
|  | "ST_L1_SYNC2", | 
|  | "ST_L1_TRANS", | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | enum { | 
|  | ST_L1_NULL, | 
|  | ST_L1_WAIT_ACT, | 
|  | ST_L1_WAIT_DEACT, | 
|  | ST_L1_ACTIV, | 
|  | }; | 
|  |  | 
|  | #define L1B_STATE_COUNT (ST_L1_ACTIV+1) | 
|  |  | 
|  | static char *strL1BState[] = | 
|  | { | 
|  | "ST_L1_NULL", | 
|  | "ST_L1_WAIT_ACT", | 
|  | "ST_L1_WAIT_DEACT", | 
|  | "ST_L1_ACTIV", | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | EV_PH_ACTIVATE, | 
|  | EV_PH_DEACTIVATE, | 
|  | EV_RESET_IND, | 
|  | EV_DEACT_CNF, | 
|  | EV_DEACT_IND, | 
|  | EV_POWER_UP, | 
|  | EV_RSYNC_IND, | 
|  | EV_INFO2_IND, | 
|  | EV_INFO4_IND, | 
|  | EV_TIMER_DEACT, | 
|  | EV_TIMER_ACT, | 
|  | EV_TIMER3, | 
|  | }; | 
|  |  | 
|  | #define L1_EVENT_COUNT (EV_TIMER3 + 1) | 
|  |  | 
|  | static char *strL1Event[] = | 
|  | { | 
|  | "EV_PH_ACTIVATE", | 
|  | "EV_PH_DEACTIVATE", | 
|  | "EV_RESET_IND", | 
|  | "EV_DEACT_CNF", | 
|  | "EV_DEACT_IND", | 
|  | "EV_POWER_UP", | 
|  | "EV_RSYNC_IND", | 
|  | "EV_INFO2_IND", | 
|  | "EV_INFO4_IND", | 
|  | "EV_TIMER_DEACT", | 
|  | "EV_TIMER_ACT", | 
|  | "EV_TIMER3", | 
|  | }; | 
|  |  | 
|  | void | 
|  | debugl1(struct IsdnCardState *cs, char *fmt, ...) | 
|  | { | 
|  | va_list args; | 
|  | char tmp[8]; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | sprintf(tmp, "Card%d ", cs->cardnr + 1); | 
|  | VHiSax_putstatus(cs, tmp, fmt, args); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1m_debug(struct FsmInst *fi, char *fmt, ...) | 
|  | { | 
|  | va_list args; | 
|  | struct PStack *st = fi->userdata; | 
|  | struct IsdnCardState *cs = st->l1.hardware; | 
|  | char tmp[8]; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | sprintf(tmp, "Card%d ", cs->cardnr + 1); | 
|  | VHiSax_putstatus(cs, tmp, fmt, args); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | static void | 
|  | L1activated(struct IsdnCardState *cs) | 
|  | { | 
|  | struct PStack *st; | 
|  |  | 
|  | st = cs->stlist; | 
|  | while (st) { | 
|  | if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) | 
|  | st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); | 
|  | else | 
|  | st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL); | 
|  | st = st->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | L1deactivated(struct IsdnCardState *cs) | 
|  | { | 
|  | struct PStack *st; | 
|  |  | 
|  | st = cs->stlist; | 
|  | while (st) { | 
|  | if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | 
|  | st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL); | 
|  | st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL); | 
|  | st = st->next; | 
|  | } | 
|  | test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags); | 
|  | } | 
|  |  | 
|  | void | 
|  | DChannel_proc_xmt(struct IsdnCardState *cs) | 
|  | { | 
|  | struct PStack *stptr; | 
|  |  | 
|  | if (cs->tx_skb) | 
|  | return; | 
|  |  | 
|  | stptr = cs->stlist; | 
|  | while (stptr != NULL) { | 
|  | if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) { | 
|  | stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL); | 
|  | break; | 
|  | } else | 
|  | stptr = stptr->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | DChannel_proc_rcv(struct IsdnCardState *cs) | 
|  | { | 
|  | struct sk_buff *skb, *nskb; | 
|  | struct PStack *stptr = cs->stlist; | 
|  | int found, tei, sapi; | 
|  |  | 
|  | if (stptr) | 
|  | if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags)) | 
|  | FsmEvent(&stptr->l1.l1m, EV_TIMER_ACT, NULL); | 
|  | while ((skb = skb_dequeue(&cs->rq))) { | 
|  | #ifdef L2FRAME_DEBUG		/* psa */ | 
|  | if (cs->debug & L1_DEB_LAPD) | 
|  | Logl2Frame(cs, skb, "PH_DATA", 1); | 
|  | #endif | 
|  | stptr = cs->stlist; | 
|  | if (skb->len<3) { | 
|  | debugl1(cs, "D-channel frame too short(%d)",skb->len); | 
|  | dev_kfree_skb(skb); | 
|  | return; | 
|  | } | 
|  | if ((skb->data[0] & 1) || !(skb->data[1] &1)) { | 
|  | debugl1(cs, "D-channel frame wrong EA0/EA1"); | 
|  | dev_kfree_skb(skb); | 
|  | return; | 
|  | } | 
|  | sapi = skb->data[0] >> 2; | 
|  | tei = skb->data[1] >> 1; | 
|  | if (cs->debug & DEB_DLOG_HEX) | 
|  | LogFrame(cs, skb->data, skb->len); | 
|  | if (cs->debug & DEB_DLOG_VERBOSE) | 
|  | dlogframe(cs, skb, 1); | 
|  | if (tei == GROUP_TEI) { | 
|  | if (sapi == CTRL_SAPI) { /* sapi 0 */ | 
|  | while (stptr != NULL) { | 
|  | if ((nskb = skb_clone(skb, GFP_ATOMIC))) | 
|  | stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb); | 
|  | else | 
|  | printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n"); | 
|  | stptr = stptr->next; | 
|  | } | 
|  | } else if (sapi == TEI_SAPI) { | 
|  | while (stptr != NULL) { | 
|  | if ((nskb = skb_clone(skb, GFP_ATOMIC))) | 
|  | stptr->l1.l1tei(stptr, PH_DATA | INDICATION, nskb); | 
|  | else | 
|  | printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n"); | 
|  | stptr = stptr->next; | 
|  | } | 
|  | } | 
|  | dev_kfree_skb(skb); | 
|  | } else if (sapi == CTRL_SAPI) { /* sapi 0 */ | 
|  | found = 0; | 
|  | while (stptr != NULL) | 
|  | if (tei == stptr->l2.tei) { | 
|  | stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb); | 
|  | found = !0; | 
|  | break; | 
|  | } else | 
|  | stptr = stptr->next; | 
|  | if (!found) | 
|  | dev_kfree_skb(skb); | 
|  | } else | 
|  | dev_kfree_skb(skb); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | BChannel_proc_xmt(struct BCState *bcs) | 
|  | { | 
|  | struct PStack *st = bcs->st; | 
|  |  | 
|  | if (test_bit(BC_FLG_BUSY, &bcs->Flag)) { | 
|  | debugl1(bcs->cs, "BC_BUSY Error"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) | 
|  | st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); | 
|  | if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) { | 
|  | if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && | 
|  | skb_queue_empty(&bcs->squeue)) { | 
|  | st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | BChannel_proc_rcv(struct BCState *bcs) | 
|  | { | 
|  | struct sk_buff *skb; | 
|  |  | 
|  | if (bcs->st->l1.l1m.state == ST_L1_WAIT_ACT) { | 
|  | FsmDelTimer(&bcs->st->l1.timer, 4); | 
|  | FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL); | 
|  | } | 
|  | while ((skb = skb_dequeue(&bcs->rqueue))) { | 
|  | bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | BChannel_proc_ack(struct BCState *bcs) | 
|  | { | 
|  | u_long	flags; | 
|  | int	ack; | 
|  |  | 
|  | spin_lock_irqsave(&bcs->aclock, flags); | 
|  | ack = bcs->ackcnt; | 
|  | bcs->ackcnt = 0; | 
|  | spin_unlock_irqrestore(&bcs->aclock, flags); | 
|  | if (ack) | 
|  | lli_writewakeup(bcs->st, ack); | 
|  | } | 
|  |  | 
|  | void | 
|  | BChannel_bh(struct work_struct *work) | 
|  | { | 
|  | struct BCState *bcs = container_of(work, struct BCState, tqueue); | 
|  |  | 
|  | if (!bcs) | 
|  | return; | 
|  | if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event)) | 
|  | BChannel_proc_rcv(bcs); | 
|  | if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event)) | 
|  | BChannel_proc_xmt(bcs); | 
|  | if (test_and_clear_bit(B_ACKPENDING, &bcs->event)) | 
|  | BChannel_proc_ack(bcs); | 
|  | } | 
|  |  | 
|  | void | 
|  | HiSax_addlist(struct IsdnCardState *cs, | 
|  | struct PStack *st) | 
|  | { | 
|  | st->next = cs->stlist; | 
|  | cs->stlist = st; | 
|  | } | 
|  |  | 
|  | void | 
|  | HiSax_rmlist(struct IsdnCardState *cs, | 
|  | struct PStack *st) | 
|  | { | 
|  | struct PStack *p; | 
|  |  | 
|  | FsmDelTimer(&st->l1.timer, 0); | 
|  | if (cs->stlist == st) | 
|  | cs->stlist = st->next; | 
|  | else { | 
|  | p = cs->stlist; | 
|  | while (p) | 
|  | if (p->next == st) { | 
|  | p->next = st->next; | 
|  | return; | 
|  | } else | 
|  | p = p->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | init_bcstate(struct IsdnCardState *cs, int bc) | 
|  | { | 
|  | struct BCState *bcs = cs->bcs + bc; | 
|  |  | 
|  | bcs->cs = cs; | 
|  | bcs->channel = bc; | 
|  | INIT_WORK(&bcs->tqueue, BChannel_bh); | 
|  | spin_lock_init(&bcs->aclock); | 
|  | bcs->BC_SetStack = NULL; | 
|  | bcs->BC_Close = NULL; | 
|  | bcs->Flag = 0; | 
|  | } | 
|  |  | 
|  | #ifdef L2FRAME_DEBUG		/* psa */ | 
|  |  | 
|  | static char * | 
|  | l2cmd(u_char cmd) | 
|  | { | 
|  | switch (cmd & ~0x10) { | 
|  | case 1: | 
|  | return "RR"; | 
|  | case 5: | 
|  | return "RNR"; | 
|  | case 9: | 
|  | return "REJ"; | 
|  | case 0x6f: | 
|  | return "SABME"; | 
|  | case 0x0f: | 
|  | return "DM"; | 
|  | case 3: | 
|  | return "UI"; | 
|  | case 0x43: | 
|  | return "DISC"; | 
|  | case 0x63: | 
|  | return "UA"; | 
|  | case 0x87: | 
|  | return "FRMR"; | 
|  | case 0xaf: | 
|  | return "XID"; | 
|  | default: | 
|  | if (!(cmd & 1)) | 
|  | return "I"; | 
|  | else | 
|  | return "invalid command"; | 
|  | } | 
|  | } | 
|  |  | 
|  | static char tmpdeb[32]; | 
|  |  | 
|  | static char * | 
|  | l2frames(u_char * ptr) | 
|  | { | 
|  | switch (ptr[2] & ~0x10) { | 
|  | case 1: | 
|  | case 5: | 
|  | case 9: | 
|  | sprintf(tmpdeb, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1); | 
|  | break; | 
|  | case 0x6f: | 
|  | case 0x0f: | 
|  | case 3: | 
|  | case 0x43: | 
|  | case 0x63: | 
|  | case 0x87: | 
|  | case 0xaf: | 
|  | sprintf(tmpdeb, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4); | 
|  | break; | 
|  | default: | 
|  | if (!(ptr[2] & 1)) { | 
|  | sprintf(tmpdeb, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1); | 
|  | break; | 
|  | } else | 
|  | return "invalid command"; | 
|  | } | 
|  |  | 
|  |  | 
|  | return tmpdeb; | 
|  | } | 
|  |  | 
|  | void | 
|  | Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir) | 
|  | { | 
|  | u_char *ptr; | 
|  |  | 
|  | ptr = skb->data; | 
|  |  | 
|  | if (ptr[0] & 1 || !(ptr[1] & 1)) | 
|  | debugl1(cs, "Address not LAPD"); | 
|  | else | 
|  | debugl1(cs, "%s %s: %s%c (sapi %d, tei %d)", | 
|  | (dir ? "<-" : "->"), buf, l2frames(ptr), | 
|  | ((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void | 
|  | l1_reset(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | FsmChangeState(fi, ST_L1_F3); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_deact_cnf(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | FsmChangeState(fi, ST_L1_F3); | 
|  | if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) | 
|  | st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_deact_req_s(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | FsmChangeState(fi, ST_L1_F3); | 
|  | FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2); | 
|  | test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_power_up_s(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) { | 
|  | FsmChangeState(fi, ST_L1_F4); | 
|  | st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); | 
|  | FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); | 
|  | test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags); | 
|  | } else | 
|  | FsmChangeState(fi, ST_L1_F3); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_go_F5(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | FsmChangeState(fi, ST_L1_F5); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_go_F8(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | FsmChangeState(fi, ST_L1_F8); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_info2_ind(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | #ifdef HISAX_UINTERFACE | 
|  | if (test_bit(FLG_L1_UINT, &st->l1.Flags)) | 
|  | FsmChangeState(fi, ST_L1_SYNC2); | 
|  | else | 
|  | #endif | 
|  | FsmChangeState(fi, ST_L1_F6); | 
|  | st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_info4_ind(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | #ifdef HISAX_UINTERFACE | 
|  | if (test_bit(FLG_L1_UINT, &st->l1.Flags)) | 
|  | FsmChangeState(fi, ST_L1_TRANS); | 
|  | else | 
|  | #endif | 
|  | FsmChangeState(fi, ST_L1_F7); | 
|  | st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); | 
|  | if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) | 
|  | FsmDelTimer(&st->l1.timer, 4); | 
|  | if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) { | 
|  | if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags)) | 
|  | FsmDelTimer(&st->l1.timer, 3); | 
|  | FsmRestartTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2); | 
|  | test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_timer3(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags); | 
|  | if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) | 
|  | L1deactivated(st->l1.hardware); | 
|  |  | 
|  | #ifdef HISAX_UINTERFACE | 
|  | if (!test_bit(FLG_L1_UINT, &st->l1.Flags)) | 
|  | #endif | 
|  | if (st->l1.l1m.state != ST_L1_F6) { | 
|  | FsmChangeState(fi, ST_L1_F3); | 
|  | st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_timer_act(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags); | 
|  | test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags); | 
|  | L1activated(st->l1.hardware); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_timer_deact(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); | 
|  | test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags); | 
|  | L1deactivated(st->l1.hardware); | 
|  | st->l1.l1hw(st, HW_DEACTIVATE | RESPONSE, NULL); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_activate_s(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | st->l1.l1hw(st, HW_RESET | REQUEST, NULL); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_activate_no(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | if ((!test_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) && (!test_bit(FLG_L1_T3RUN, &st->l1.Flags))) { | 
|  | test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags); | 
|  | L1deactivated(st->l1.hardware); | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct FsmNode L1SFnList[] __initdata = | 
|  | { | 
|  | {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s}, | 
|  | {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, | 
|  | {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, | 
|  | {ST_L1_F3, EV_RESET_IND, l1_reset}, | 
|  | {ST_L1_F4, EV_RESET_IND, l1_reset}, | 
|  | {ST_L1_F5, EV_RESET_IND, l1_reset}, | 
|  | {ST_L1_F6, EV_RESET_IND, l1_reset}, | 
|  | {ST_L1_F7, EV_RESET_IND, l1_reset}, | 
|  | {ST_L1_F8, EV_RESET_IND, l1_reset}, | 
|  | {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, | 
|  | {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, | 
|  | {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, | 
|  | {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, | 
|  | {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, | 
|  | {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, | 
|  | {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s}, | 
|  | {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s}, | 
|  | {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s}, | 
|  | {ST_L1_F3, EV_POWER_UP, l1_power_up_s}, | 
|  | {ST_L1_F4, EV_RSYNC_IND, l1_go_F5}, | 
|  | {ST_L1_F6, EV_RSYNC_IND, l1_go_F8}, | 
|  | {ST_L1_F7, EV_RSYNC_IND, l1_go_F8}, | 
|  | {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, | 
|  | {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, | 
|  | {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, | 
|  | {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, | 
|  | {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, | 
|  | {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, | 
|  | {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, | 
|  | {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, | 
|  | {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, | 
|  | {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, | 
|  | {ST_L1_F3, EV_TIMER3, l1_timer3}, | 
|  | {ST_L1_F4, EV_TIMER3, l1_timer3}, | 
|  | {ST_L1_F5, EV_TIMER3, l1_timer3}, | 
|  | {ST_L1_F6, EV_TIMER3, l1_timer3}, | 
|  | {ST_L1_F8, EV_TIMER3, l1_timer3}, | 
|  | {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, | 
|  | {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, | 
|  | {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, | 
|  | {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, | 
|  | {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, | 
|  | {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, | 
|  | {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, | 
|  | }; | 
|  |  | 
|  | #ifdef HISAX_UINTERFACE | 
|  | static void | 
|  | l1_deact_req_u(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | FsmChangeState(fi, ST_L1_RESET); | 
|  | FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2); | 
|  | test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); | 
|  | st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_power_up_u(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); | 
|  | test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_info0_ind(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | FsmChangeState(fi, ST_L1_DEACT); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1_activate_u(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | st->l1.l1hw(st, HW_INFO1 | REQUEST, NULL); | 
|  | } | 
|  |  | 
|  | static struct FsmNode L1UFnList[] __initdata = | 
|  | { | 
|  | {ST_L1_RESET, EV_DEACT_IND, l1_deact_req_u}, | 
|  | {ST_L1_DEACT, EV_DEACT_IND, l1_deact_req_u}, | 
|  | {ST_L1_SYNC2, EV_DEACT_IND, l1_deact_req_u}, | 
|  | {ST_L1_TRANS, EV_DEACT_IND, l1_deact_req_u}, | 
|  | {ST_L1_DEACT, EV_PH_ACTIVATE, l1_activate_u}, | 
|  | {ST_L1_DEACT, EV_POWER_UP, l1_power_up_u}, | 
|  | {ST_L1_DEACT, EV_INFO2_IND, l1_info2_ind}, | 
|  | {ST_L1_TRANS, EV_INFO2_IND, l1_info2_ind}, | 
|  | {ST_L1_RESET, EV_DEACT_CNF, l1_info0_ind}, | 
|  | {ST_L1_DEACT, EV_INFO4_IND, l1_info4_ind}, | 
|  | {ST_L1_SYNC2, EV_INFO4_IND, l1_info4_ind}, | 
|  | {ST_L1_RESET, EV_INFO4_IND, l1_info4_ind}, | 
|  | {ST_L1_DEACT, EV_TIMER3, l1_timer3}, | 
|  | {ST_L1_SYNC2, EV_TIMER3, l1_timer3}, | 
|  | {ST_L1_TRANS, EV_TIMER_ACT, l1_timer_act}, | 
|  | {ST_L1_DEACT, EV_TIMER_DEACT, l1_timer_deact}, | 
|  | {ST_L1_SYNC2, EV_TIMER_DEACT, l1_timer_deact}, | 
|  | {ST_L1_RESET, EV_TIMER_DEACT, l1_timer_deact}, | 
|  | }; | 
|  |  | 
|  | #endif | 
|  |  | 
|  | static void | 
|  | l1b_activate(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | FsmChangeState(fi, ST_L1_WAIT_ACT); | 
|  | FsmRestartTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1b_deactivate(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | FsmChangeState(fi, ST_L1_WAIT_DEACT); | 
|  | FsmRestartTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1b_timer_act(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | FsmChangeState(fi, ST_L1_ACTIV); | 
|  | st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); | 
|  | } | 
|  |  | 
|  | static void | 
|  | l1b_timer_deact(struct FsmInst *fi, int event, void *arg) | 
|  | { | 
|  | struct PStack *st = fi->userdata; | 
|  |  | 
|  | FsmChangeState(fi, ST_L1_NULL); | 
|  | st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL); | 
|  | } | 
|  |  | 
|  | static struct FsmNode L1BFnList[] __initdata = | 
|  | { | 
|  | {ST_L1_NULL, EV_PH_ACTIVATE, l1b_activate}, | 
|  | {ST_L1_WAIT_ACT, EV_TIMER_ACT, l1b_timer_act}, | 
|  | {ST_L1_ACTIV, EV_PH_DEACTIVATE, l1b_deactivate}, | 
|  | {ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact}, | 
|  | }; | 
|  |  | 
|  | int __init | 
|  | Isdnl1New(void) | 
|  | { | 
|  | int retval; | 
|  |  | 
|  | l1fsm_s.state_count = L1S_STATE_COUNT; | 
|  | l1fsm_s.event_count = L1_EVENT_COUNT; | 
|  | l1fsm_s.strEvent = strL1Event; | 
|  | l1fsm_s.strState = strL1SState; | 
|  | retval = FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList)); | 
|  | if (retval) | 
|  | return retval; | 
|  |  | 
|  | l1fsm_b.state_count = L1B_STATE_COUNT; | 
|  | l1fsm_b.event_count = L1_EVENT_COUNT; | 
|  | l1fsm_b.strEvent = strL1Event; | 
|  | l1fsm_b.strState = strL1BState; | 
|  | retval = FsmNew(&l1fsm_b, L1BFnList, ARRAY_SIZE(L1BFnList)); | 
|  | if (retval) { | 
|  | FsmFree(&l1fsm_s); | 
|  | return retval; | 
|  | } | 
|  | #ifdef HISAX_UINTERFACE | 
|  | l1fsm_u.state_count = L1U_STATE_COUNT; | 
|  | l1fsm_u.event_count = L1_EVENT_COUNT; | 
|  | l1fsm_u.strEvent = strL1Event; | 
|  | l1fsm_u.strState = strL1UState; | 
|  | retval = FsmNew(&l1fsm_u, L1UFnList, ARRAY_SIZE(L1UFnList)); | 
|  | if (retval) { | 
|  | FsmFree(&l1fsm_s); | 
|  | FsmFree(&l1fsm_b); | 
|  | return retval; | 
|  | } | 
|  | #endif | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void Isdnl1Free(void) | 
|  | { | 
|  | #ifdef HISAX_UINTERFACE | 
|  | FsmFree(&l1fsm_u); | 
|  | #endif | 
|  | FsmFree(&l1fsm_s); | 
|  | FsmFree(&l1fsm_b); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dch_l2l1(struct PStack *st, int pr, void *arg) | 
|  | { | 
|  | struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; | 
|  |  | 
|  | switch (pr) { | 
|  | case (PH_DATA | REQUEST): | 
|  | case (PH_PULL | REQUEST): | 
|  | case (PH_PULL |INDICATION): | 
|  | st->l1.l1hw(st, pr, arg); | 
|  | break; | 
|  | case (PH_ACTIVATE | REQUEST): | 
|  | if (cs->debug) | 
|  | debugl1(cs, "PH_ACTIVATE_REQ %s", | 
|  | st->l1.l1m.fsm->strState[st->l1.l1m.state]); | 
|  | if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) | 
|  | st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); | 
|  | else { | 
|  | test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags); | 
|  | FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg); | 
|  | } | 
|  | break; | 
|  | case (PH_TESTLOOP | REQUEST): | 
|  | if (1 & (long) arg) | 
|  | debugl1(cs, "PH_TEST_LOOP B1"); | 
|  | if (2 & (long) arg) | 
|  | debugl1(cs, "PH_TEST_LOOP B2"); | 
|  | if (!(3 & (long) arg)) | 
|  | debugl1(cs, "PH_TEST_LOOP DISABLED"); | 
|  | st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg); | 
|  | break; | 
|  | default: | 
|  | if (cs->debug) | 
|  | debugl1(cs, "dch_l2l1 msg %04X unhandled", pr); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | l1_msg(struct IsdnCardState *cs, int pr, void *arg) { | 
|  | struct PStack *st; | 
|  |  | 
|  | st = cs->stlist; | 
|  |  | 
|  | while (st) { | 
|  | switch(pr) { | 
|  | case (HW_RESET | INDICATION): | 
|  | FsmEvent(&st->l1.l1m, EV_RESET_IND, arg); | 
|  | break; | 
|  | case (HW_DEACTIVATE | CONFIRM): | 
|  | FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg); | 
|  | break; | 
|  | case (HW_DEACTIVATE | INDICATION): | 
|  | FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg); | 
|  | break; | 
|  | case (HW_POWERUP | CONFIRM): | 
|  | FsmEvent(&st->l1.l1m, EV_POWER_UP, arg); | 
|  | break; | 
|  | case (HW_RSYNC | INDICATION): | 
|  | FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg); | 
|  | break; | 
|  | case (HW_INFO2 | INDICATION): | 
|  | FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg); | 
|  | break; | 
|  | case (HW_INFO4_P8 | INDICATION): | 
|  | case (HW_INFO4_P10 | INDICATION): | 
|  | FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg); | 
|  | break; | 
|  | default: | 
|  | if (cs->debug) | 
|  | debugl1(cs, "l1msg %04X unhandled", pr); | 
|  | break; | 
|  | } | 
|  | st = st->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | l1_msg_b(struct PStack *st, int pr, void *arg) { | 
|  | switch(pr) { | 
|  | case (PH_ACTIVATE | REQUEST): | 
|  | FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, NULL); | 
|  | break; | 
|  | case (PH_DEACTIVATE | REQUEST): | 
|  | FsmEvent(&st->l1.l1m, EV_PH_DEACTIVATE, NULL); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | setstack_HiSax(struct PStack *st, struct IsdnCardState *cs) | 
|  | { | 
|  | st->l1.hardware = cs; | 
|  | st->protocol = cs->protocol; | 
|  | st->l1.l1m.fsm = &l1fsm_s; | 
|  | st->l1.l1m.state = ST_L1_F3; | 
|  | st->l1.Flags = 0; | 
|  | #ifdef HISAX_UINTERFACE | 
|  | if (test_bit(FLG_HW_L1_UINT, &cs->HW_Flags)) { | 
|  | st->l1.l1m.fsm = &l1fsm_u; | 
|  | st->l1.l1m.state = ST_L1_RESET; | 
|  | st->l1.Flags = FLG_L1_UINT; | 
|  | } | 
|  | #endif | 
|  | st->l1.l1m.debug = cs->debug; | 
|  | st->l1.l1m.userdata = st; | 
|  | st->l1.l1m.userint = 0; | 
|  | st->l1.l1m.printdebug = l1m_debug; | 
|  | FsmInitTimer(&st->l1.l1m, &st->l1.timer); | 
|  | setstack_tei(st); | 
|  | setstack_manager(st); | 
|  | st->l1.stlistp = &(cs->stlist); | 
|  | st->l2.l2l1  = dch_l2l1; | 
|  | if (cs->setstack_d) | 
|  | cs->setstack_d(st, cs); | 
|  | } | 
|  |  | 
|  | void | 
|  | setstack_l1_B(struct PStack *st) | 
|  | { | 
|  | struct IsdnCardState *cs = st->l1.hardware; | 
|  |  | 
|  | st->l1.l1m.fsm = &l1fsm_b; | 
|  | st->l1.l1m.state = ST_L1_NULL; | 
|  | st->l1.l1m.debug = cs->debug; | 
|  | st->l1.l1m.userdata = st; | 
|  | st->l1.l1m.userint = 0; | 
|  | st->l1.l1m.printdebug = l1m_debug; | 
|  | st->l1.Flags = 0; | 
|  | FsmInitTimer(&st->l1.l1m, &st->l1.timer); | 
|  | } |