📄 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. * */#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 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_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; schedule_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): if ((cs->dc.isac.ph_state == IPACX_IND_RES) || (cs->dc.isac.ph_state == IPACX_IND_DR) || (cs->dc.isac.ph_state == IPACX_IND_DC)) ph_command(cs, IPACX_CMD_TIM); else ph_command(cs, IPACX_CMD_RES); 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 } }}//----------------------------------------------------------// Fill buffer from receive FIFO//----------------------------------------------------------static void dch_empty_fifo(struct IsdnCardState *cs, int count){ 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; cs->readisacfifo(cs, ptr, count); cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC 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){ 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 } 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); 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; 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); 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); } } } cs->rcvidx = 0; schedule_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)) schedule_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 { schedule_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;}//----------------------------------------------------------//----------------------------------------------------------static void __devinitdch_init(struct IsdnCardState *cs){ printk(KERN_INFO "HiSax: IPACX ISDN driver v0.1.0\n"); cs->setstack_d = dch_setstack; cs->dbusytimer.function = (void *) dbusy_timer_handler; cs->dbusytimer.data = (long) cs; init_timer(&cs->dbusytimer); cs->writeisac(cs, IPACX_TR_CONF0, 0x00); // clear LDD cs->writeisac(cs, IPACX_TR_CONF2, 0x00); // enable transmitter cs->writeisac(cs, IPACX_MODED, 0xC9); // transparent mode 0, RAC, stop/go cs->writeisac(cs, IPACX_MON_CR, 0x00); // disable monitor channel}//==========================================================// B channel functions//==========================================================//----------------------------------------------------------// Entry point for commands//----------------------------------------------------------static voidbch_l2l1(struct PStack *st, int pr, void *arg){ struct BCState *bcs = st->l1.bcs; struct sk_buff *skb = arg; u_long flags; switch (pr) { case (PH_DATA | REQUEST): spin_lock_irqsave(&bcs->cs->lock, flags); if (bcs->tx_skb) { skb_queue_tail(&bcs->squeue, skb); } else { bcs->tx_skb = skb;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -