📄 ipacx.c
字号:
/* * * IPACX specific routines * * Author Joerg Petersohn * Derived from hisax_isac.c, isac.c, hscx.c and others * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */#define __NO_VERSION__#include <linux/kernel.h>#include <linux/config.h>#include <linux/init.h>#include "hisax_if.h"#include "hisax.h"#include "isdnl1.h"#include "ipacx.h"#define DBUSY_TIMER_VALUE 80#define TIMER3_VALUE 7000#define MAX_DFRAME_LEN_L1 300#define B_FIFO_SIZE 64#define D_FIFO_SIZE 32// ipacx interrupt mask values #define _MASK_IMASK 0x2E // global mask#define _MASKB_IMASK 0x0B#define _MASKD_IMASK 0x03 // all on//----------------------------------------------------------// local function declarations//----------------------------------------------------------static void ph_command(struct IsdnCardState *cs, unsigned int command);static inline void cic_int(struct IsdnCardState *cs);static void dch_l2l1(struct PStack *st, int pr, void *arg);static void dbusy_timer_handler(struct IsdnCardState *cs);static void ipacx_new_ph(struct IsdnCardState *cs);static void dch_bh(struct IsdnCardState *cs);static void dch_sched_event(struct IsdnCardState *cs, int event);static void dch_empty_fifo(struct IsdnCardState *cs, int count);static void dch_fill_fifo(struct IsdnCardState *cs);static inline void dch_int(struct IsdnCardState *cs);static void __devinit dch_setstack(struct PStack *st, struct IsdnCardState *cs);static void __devinit dch_init(struct IsdnCardState *cs);static void bch_l2l1(struct PStack *st, int pr, void *arg);static void bch_sched_event(struct BCState *bcs, int event);static void bch_empty_fifo(struct BCState *bcs, int count);static void bch_fill_fifo(struct BCState *bcs);static void bch_int(struct IsdnCardState *cs, u_char hscx);static void bch_mode(struct BCState *bcs, int mode, int bc);static void bch_close_state(struct BCState *bcs);static int bch_open_state(struct IsdnCardState *cs, struct BCState *bcs);static int bch_setstack(struct PStack *st, struct BCState *bcs);static void __devinit bch_init(struct IsdnCardState *cs, int hscx);static void __init clear_pending_ints(struct IsdnCardState *cs);//----------------------------------------------------------// Issue Layer 1 command to chip//----------------------------------------------------------static void ph_command(struct IsdnCardState *cs, unsigned int command){ if (cs->debug &L1_DEB_ISAC) debugl1(cs, "ph_command (%#x) in (%#x)", command, cs->dc.isac.ph_state);//################################### // printk(KERN_INFO "ph_command (%#x)\n", command);//################################### cs->writeisac(cs, IPACX_CIX0, (command << 4) | 0x0E);}//----------------------------------------------------------// Transceiver interrupt handler//----------------------------------------------------------static inline void cic_int(struct IsdnCardState *cs){ u_char event; event = cs->readisac(cs, IPACX_CIR0) >> 4; if (cs->debug &L1_DEB_ISAC) debugl1(cs, "cic_int(event=%#x)", event);//######################################### // printk(KERN_INFO "cic_int(%x)\n", event);//######################################### cs->dc.isac.ph_state = event; dch_sched_event(cs, D_L1STATECHANGE);}//==========================================================// D channel functions//==========================================================//----------------------------------------------------------// Command entry point//----------------------------------------------------------static voiddch_l2l1(struct PStack *st, int pr, void *arg){ struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; struct sk_buff *skb = arg; u_char cda1_cr, cda2_cr; switch (pr) { case (PH_DATA |REQUEST): if (cs->debug &DEB_DLOG_HEX) LogFrame(cs, skb->data, skb->len); if (cs->debug &DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0); if (cs->tx_skb) { skb_queue_tail(&cs->sq, skb);#ifdef L2FRAME_DEBUG if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA Queued", 0);#endif } else { cs->tx_skb = skb; cs->tx_cnt = 0;#ifdef L2FRAME_DEBUG if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA", 0);#endif dch_fill_fifo(cs); } break; case (PH_PULL |INDICATION): if (cs->tx_skb) { if (cs->debug & L1_DEB_WARN) debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); skb_queue_tail(&cs->sq, skb); break; } if (cs->debug & DEB_DLOG_HEX) LogFrame(cs, skb->data, skb->len); if (cs->debug & DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0); cs->tx_skb = skb; cs->tx_cnt = 0;#ifdef L2FRAME_DEBUG if (cs->debug & L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);#endif dch_fill_fifo(cs); break; case (PH_PULL | REQUEST):#ifdef L2FRAME_DEBUG if (cs->debug & L1_DEB_LAPD) debugl1(cs, "-> PH_REQUEST_PULL");#endif if (!cs->tx_skb) { clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); } else set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; case (HW_RESET | REQUEST): case (HW_ENABLE | REQUEST): ph_command(cs, IPACX_CMD_TIM); break; case (HW_INFO3 | REQUEST): ph_command(cs, IPACX_CMD_AR8); break; case (HW_TESTLOOP | REQUEST): cs->writeisac(cs, IPACX_CDA_TSDP10, 0x80); // Timeslot 0 is B1 cs->writeisac(cs, IPACX_CDA_TSDP11, 0x81); // Timeslot 0 is B1 cda1_cr = cs->readisac(cs, IPACX_CDA1_CR); cda2_cr = cs->readisac(cs, IPACX_CDA2_CR); if ((long)arg &1) { // loop B1 cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x0a); } else { // B1 off cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x0a); } if ((long)arg &2) { // loop B2 cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x14); } else { // B2 off cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x14); } break; case (HW_DEACTIVATE | RESPONSE): skb_queue_purge(&cs->rq); skb_queue_purge(&cs->sq); if (cs->tx_skb) { dev_kfree_skb_any(cs->tx_skb); cs->tx_skb = NULL; } if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) del_timer(&cs->dbusytimer); break; default: if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_l2l1 unknown %04x", pr); break; }}//----------------------------------------------------------//----------------------------------------------------------static voiddbusy_timer_handler(struct IsdnCardState *cs){ struct PStack *st; int rbchd, stard; if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { rbchd = cs->readisac(cs, IPACX_RBCHD); stard = cs->readisac(cs, IPACX_STARD); if (cs->debug) debugl1(cs, "D-Channel Busy RBCHD %02x STARD %02x", rbchd, stard); if (!(stard &0x40)) { // D-Channel Busy set_bit(FLG_L1_DBUSY, &cs->HW_Flags); for (st = cs->stlist; st; st = st->next) { st->l1.l1l2(st, PH_PAUSE | INDICATION, NULL); // flow control on } } else { // seems we lost an interrupt; reset transceiver */ clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags); if (cs->tx_skb) { dev_kfree_skb_any(cs->tx_skb); cs->tx_cnt = 0; cs->tx_skb = NULL; } else { printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n"); debugl1(cs, "D-Channel Busy no skb"); } cs->writeisac(cs, IPACX_CMDRD, 0x01); // Tx reset, generates XPR } }}//----------------------------------------------------------// L1 state machine intermediate layer to isdnl1 module//----------------------------------------------------------static voidipacx_new_ph(struct IsdnCardState *cs){ switch (cs->dc.isac.ph_state) { case (IPACX_IND_RES): ph_command(cs, IPACX_CMD_DI); l1_msg(cs, HW_RESET | INDICATION, NULL); break; case (IPACX_IND_DC): l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL); break; case (IPACX_IND_DR): l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); break; case (IPACX_IND_PU): l1_msg(cs, HW_POWERUP | CONFIRM, NULL); break; case (IPACX_IND_RSY): l1_msg(cs, HW_RSYNC | INDICATION, NULL); break; case (IPACX_IND_AR): l1_msg(cs, HW_INFO2 | INDICATION, NULL); break; case (IPACX_IND_AI8): l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); break; case (IPACX_IND_AI10): l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL); break; default: break; }}//----------------------------------------------------------// bottom half handler for D channel//----------------------------------------------------------static voiddch_bh(struct IsdnCardState *cs){ struct PStack *st; if (!cs) return; if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { if (cs->debug) debugl1(cs, "D-Channel Busy cleared"); for (st = cs->stlist; st; st = st->next) { st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL); } } if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) { DChannel_proc_rcv(cs); } if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) { DChannel_proc_xmt(cs); } if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { ipacx_new_ph(cs); } }//----------------------------------------------------------// proceed with bottom half handler dch_bh()//----------------------------------------------------------static voiddch_sched_event(struct IsdnCardState *cs, int event){ set_bit(event, &cs->event); queue_task(&cs->tqueue, &tq_immediate); mark_bh(IMMEDIATE_BH);}//----------------------------------------------------------// Fill buffer from receive FIFO//----------------------------------------------------------static void dch_empty_fifo(struct IsdnCardState *cs, int count){ long flags; u_char *ptr; if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO)) debugl1(cs, "dch_empty_fifo()"); // message too large, remove if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) { if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_empty_fifo() incoming message too large"); cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC cs->rcvidx = 0; return; } ptr = cs->rcvbuf + cs->rcvidx; cs->rcvidx += count; save_flags(flags); cli(); cs->readisacfifo(cs, ptr, count); cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC restore_flags(flags); if (cs->debug &L1_DEB_ISAC_FIFO) { char *t = cs->dlog; t += sprintf(t, "dch_empty_fifo() cnt %d", count); QuickHex(t, ptr, count); debugl1(cs, cs->dlog); }}//----------------------------------------------------------// Fill transmit FIFO//----------------------------------------------------------static void dch_fill_fifo(struct IsdnCardState *cs){ long flags; int count; u_char cmd, *ptr; if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO)) debugl1(cs, "dch_fill_fifo()"); if (!cs->tx_skb) return; count = cs->tx_skb->len; if (count <= 0) return; if (count > D_FIFO_SIZE) { count = D_FIFO_SIZE; cmd = 0x08; // XTF } else { cmd = 0x0A; // XTF | XME } save_flags(flags); cli(); ptr = cs->tx_skb->data; skb_pull(cs->tx_skb, count); cs->tx_cnt += count; cs->writeisacfifo(cs, ptr, count); cs->writeisac(cs, IPACX_CMDRD, cmd); // set timeout for transmission contol if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { debugl1(cs, "dch_fill_fifo dbusytimer running"); del_timer(&cs->dbusytimer); } init_timer(&cs->dbusytimer); cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); add_timer(&cs->dbusytimer); restore_flags(flags); if (cs->debug &L1_DEB_ISAC_FIFO) { char *t = cs->dlog; t += sprintf(t, "dch_fill_fifo() cnt %d", count); QuickHex(t, ptr, count); debugl1(cs, cs->dlog); }}//----------------------------------------------------------// D channel interrupt handler//----------------------------------------------------------static inline void dch_int(struct IsdnCardState *cs){ struct sk_buff *skb; u_char istad, rstad; long flags; int count; istad = cs->readisac(cs, IPACX_ISTAD);//############################################## // printk(KERN_WARNING "dch_int(istad=%02x)\n", istad);//############################################## if (istad &0x80) { // RME rstad = cs->readisac(cs, IPACX_RSTAD); if ((rstad &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB) if (!(rstad &0x80)) if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): invalid frame"); if ((rstad &0x40)) if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): RDO"); if (!(rstad &0x20)) if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): CRC error"); cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC } else { // received frame ok count = cs->readisac(cs, IPACX_RBCLD); if (count) count--; // RSTAB is last byte count &= D_FIFO_SIZE-1; if (count == 0) count = D_FIFO_SIZE; dch_empty_fifo(cs, count); save_flags(flags); cli(); if ((count = cs->rcvidx) > 0) { cs->rcvidx = 0; if (!(skb = dev_alloc_skb(count))) printk(KERN_WARNING "HiSax dch_int(): receive out of memory\n"); else { memcpy(skb_put(skb, count), cs->rcvbuf, count); skb_queue_tail(&cs->rq, skb); } } restore_flags(flags); } cs->rcvidx = 0; dch_sched_event(cs, D_RCVBUFREADY); } if (istad &0x40) { // RPF dch_empty_fifo(cs, D_FIFO_SIZE); } if (istad &0x20) { // RFO if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): RFO"); cs->writeisac(cs, IPACX_CMDRD, 0x40); //RRES } if (istad &0x10) { // XPR if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) del_timer(&cs->dbusytimer); if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) dch_sched_event(cs, D_CLEARBUSY); if (cs->tx_skb) { if (cs->tx_skb->len) { dch_fill_fifo(cs); goto afterXPR; } else { dev_kfree_skb_irq(cs->tx_skb); cs->tx_skb = NULL; cs->tx_cnt = 0; } } if ((cs->tx_skb = skb_dequeue(&cs->sq))) { cs->tx_cnt = 0; dch_fill_fifo(cs); } else { dch_sched_event(cs, D_XMTBUFREADY); } } afterXPR: if (istad &0x0C) { // XDU or XMR if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): XDU"); if (cs->tx_skb) { skb_push(cs->tx_skb, cs->tx_cnt); // retransmit cs->tx_cnt = 0; dch_fill_fifo(cs); } else { printk(KERN_WARNING "HiSax: ISAC XDU no skb\n"); debugl1(cs, "ISAC XDU no skb"); } }}//----------------------------------------------------------//----------------------------------------------------------static void __devinitdch_setstack(struct PStack *st, struct IsdnCardState *cs){ st->l1.l1hw = dch_l2l1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -