📄 w6692.c
字号:
/* $Id: w6692.c,v 1.18.2.4 2004/02/11 13:21:34 keil Exp $ * * Winbond W6692 specific routines * * Author Petr Novak * Copyright by Petr Novak <petr.novak@i.cz> * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */#include <linux/config.h>#include <linux/init.h>#include "hisax.h"#include "w6692.h"#include "isdnl1.h"#include <linux/interrupt.h>#include <linux/pci.h>/* table entry in the PCI devices list */typedef struct { int vendor_id; int device_id; char *vendor_name; char *card_name;} PCI_ENTRY;static const PCI_ENTRY id_list[] ={ {PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, "Winbond", "W6692"}, {PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, "Dynalink/AsusCom", "IS64PH"}, {0, 0, "U.S.Robotics", "ISDN PCI Card TA"}};#define W6692_SV_USR 0x16ec#define W6692_SD_USR 0x3409#define W6692_WINBOND 0#define W6692_DYNALINK 1#define W6692_USR 2extern const char *CardType[];static const char *w6692_revision = "$Revision: 1.18.2.4 $";#define DBUSY_TIMER_VALUE 80static char *W6692Ver[] __initdata ={"W6692 V00", "W6692 V01", "W6692 V10", "W6692 V11"};static void __initW6692Version(struct IsdnCardState *cs, char *s){ int val; val = cs->readW6692(cs, W_D_RBCH); printk(KERN_INFO "%s Winbond W6692 version (%x): %s\n", s, val, W6692Ver[(val >> 6) & 3]);}static voidph_command(struct IsdnCardState *cs, unsigned int command){ if (cs->debug & L1_DEB_ISAC) debugl1(cs, "ph_command %x", command); cs->writeisac(cs, W_CIX, command);}static voidW6692_new_ph(struct IsdnCardState *cs){ switch (cs->dc.w6692.ph_state) { case (W_L1CMD_RST): ph_command(cs, W_L1CMD_DRC); l1_msg(cs, HW_RESET | INDICATION, NULL); /* fallthru */ case (W_L1IND_CD): l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL); break; case (W_L1IND_DRD): l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); break; case (W_L1IND_CE): l1_msg(cs, HW_POWERUP | CONFIRM, NULL); break; case (W_L1IND_LD): l1_msg(cs, HW_RSYNC | INDICATION, NULL); break; case (W_L1IND_ARD): l1_msg(cs, HW_INFO2 | INDICATION, NULL); break; case (W_L1IND_AI8): l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); break; case (W_L1IND_AI10): l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL); break; default: break; }}static voidW6692_bh(struct IsdnCardState *cs){ struct PStack *stptr; if (!cs) return; if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { if (cs->debug) debugl1(cs, "D-Channel Busy cleared"); stptr = cs->stlist; while (stptr != NULL) { stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); stptr = stptr->next; } } if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) W6692_new_ph(cs); 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_RX_MON1, &cs->event)) arcofi_fsm(cs, ARCOFI_RX_END, NULL); if (test_and_clear_bit(D_TX_MON1, &cs->event)) arcofi_fsm(cs, ARCOFI_TX_END, NULL); */}static voidW6692_empty_fifo(struct IsdnCardState *cs, int count){ u_char *ptr; if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) debugl1(cs, "W6692_empty_fifo"); if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692_empty_fifo overrun %d", cs->rcvidx + count); cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK); cs->rcvidx = 0; return; } ptr = cs->rcvbuf + cs->rcvidx; cs->rcvidx += count; cs->readW6692fifo(cs, ptr, count); cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK); if (cs->debug & L1_DEB_ISAC_FIFO) { char *t = cs->dlog; t += sprintf(t, "W6692_empty_fifo cnt %d", count); QuickHex(t, ptr, count); debugl1(cs, cs->dlog); }}static voidW6692_fill_fifo(struct IsdnCardState *cs){ int count, more; u_char *ptr; if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) debugl1(cs, "W6692_fill_fifo"); if (!cs->tx_skb) return; count = cs->tx_skb->len; if (count <= 0) return; more = 0; if (count > W_D_FIFO_THRESH) { more = !0; count = W_D_FIFO_THRESH; } ptr = cs->tx_skb->data; skb_pull(cs->tx_skb, count); cs->tx_cnt += count; cs->writeW6692fifo(cs, ptr, count); cs->writeW6692(cs, W_D_CMDR, more ? W_D_CMDR_XMS : (W_D_CMDR_XMS | W_D_CMDR_XME)); if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { debugl1(cs, "W6692_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, "W6692_fill_fifo cnt %d", count); QuickHex(t, ptr, count); debugl1(cs, cs->dlog); }}static voidW6692B_empty_fifo(struct BCState *bcs, int count){ u_char *ptr; struct IsdnCardState *cs = bcs->cs; if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) debugl1(cs, "W6692B_empty_fifo"); if (bcs->hw.w6692.rcvidx + count > HSCX_BUFMAX) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692B_empty_fifo: incoming packet too large"); cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); bcs->hw.w6692.rcvidx = 0; return; } ptr = bcs->hw.w6692.rcvbuf + bcs->hw.w6692.rcvidx; bcs->hw.w6692.rcvidx += count; READW6692BFIFO(cs, bcs->channel, ptr, count); cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); if (cs->debug & L1_DEB_HSCX_FIFO) { char *t = bcs->blog; t += sprintf(t, "W6692B_empty_fifo %c cnt %d", bcs->channel + '1', count); QuickHex(t, ptr, count); debugl1(cs, bcs->blog); }}static voidW6692B_fill_fifo(struct BCState *bcs){ struct IsdnCardState *cs = bcs->cs; int more, count; u_char *ptr; if (!bcs->tx_skb) return; if (bcs->tx_skb->len <= 0) return; more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; if (bcs->tx_skb->len > W_B_FIFO_THRESH) { more = 1; count = W_B_FIFO_THRESH; } else count = bcs->tx_skb->len; if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) debugl1(cs, "W6692B_fill_fifo%s%d", (more ? " ": " last "), count); ptr = bcs->tx_skb->data; skb_pull(bcs->tx_skb, count); bcs->tx_cnt -= count; bcs->hw.w6692.count += count; WRITEW6692BFIFO(cs, bcs->channel, ptr, count); cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACT | W_B_CMDR_XMS | (more ? 0 : W_B_CMDR_XME)); if (cs->debug & L1_DEB_HSCX_FIFO) { char *t = bcs->blog; t += sprintf(t, "W6692B_fill_fifo %c cnt %d", bcs->channel + '1', count); QuickHex(t, ptr, count); debugl1(cs, bcs->blog); }}static voidW6692B_interrupt(struct IsdnCardState *cs, u_char bchan){ u_char val; u_char r; struct BCState *bcs; struct sk_buff *skb; int count; bcs = (cs->bcs->channel == bchan) ? cs->bcs : (cs->bcs+1); val = cs->BC_Read_Reg(cs, bchan, W_B_EXIR); debugl1(cs, "W6692B chan %d B_EXIR 0x%02X", bchan, val); if (!test_bit(BC_FLG_INIT, &bcs->Flag)) { debugl1(cs, "W6692B not INIT yet"); return; } if (val & W_B_EXI_RME) { /* RME */ r = cs->BC_Read_Reg(cs, bchan, W_B_STAR); if (r & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 B STAR %x", r); if ((r & W_B_STAR_RDOV) && bcs->mode) if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 B RDOV mode=%d", bcs->mode); if (r & W_B_STAR_CRCE) if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 B CRC error"); cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT); } else { count = cs->BC_Read_Reg(cs, bchan, W_B_RBCL) & (W_B_FIFO_THRESH - 1); if (count == 0) count = W_B_FIFO_THRESH; W6692B_empty_fifo(bcs, count); if ((count = bcs->hw.w6692.rcvidx) > 0) { if (cs->debug & L1_DEB_HSCX_FIFO) debugl1(cs, "W6692 Bchan Frame %d", count); if (!(skb = dev_alloc_skb(count))) printk(KERN_WARNING "W6692: Bchan receive out of memory\n"); else { memcpy(skb_put(skb, count), bcs->hw.w6692.rcvbuf, count); skb_queue_tail(&bcs->rqueue, skb); } } } bcs->hw.w6692.rcvidx = 0; schedule_event(bcs, B_RCVBUFREADY); } if (val & W_B_EXI_RMR) { /* RMR */ W6692B_empty_fifo(bcs, W_B_FIFO_THRESH); r = cs->BC_Read_Reg(cs, bchan, W_B_STAR); if (r & W_B_STAR_RDOV) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 B RDOV(RMR) mode=%d",bcs->mode); cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT); if (bcs->mode != L1_MODE_TRANS) bcs->hw.w6692.rcvidx = 0; } if (bcs->mode == L1_MODE_TRANS) { /* receive audio data */ if (!(skb = dev_alloc_skb(W_B_FIFO_THRESH))) printk(KERN_WARNING "HiSax: receive out of memory\n"); else { memcpy(skb_put(skb, W_B_FIFO_THRESH), bcs->hw.w6692.rcvbuf, W_B_FIFO_THRESH); skb_queue_tail(&bcs->rqueue, skb); } bcs->hw.w6692.rcvidx = 0; schedule_event(bcs, B_RCVBUFREADY); } } if (val & W_B_EXI_XDUN) { /* XDUN */ cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT); if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 B EXIR %x Lost TX", val); if (bcs->mode == 1) W6692B_fill_fifo(bcs); else { /* Here we lost an TX interrupt, so * restart transmitting the whole frame. */ if (bcs->tx_skb) { skb_push(bcs->tx_skb, bcs->hw.w6692.count); bcs->tx_cnt += bcs->hw.w6692.count; bcs->hw.w6692.count = 0; } } return; } if (val & W_B_EXI_XFR) { /* XFR */ r = cs->BC_Read_Reg(cs, bchan, W_B_STAR); if (r & W_B_STAR_XDOW) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 B STAR %x XDOW", r); cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT); if (bcs->tx_skb && (bcs->mode != 1)) { skb_push(bcs->tx_skb, bcs->hw.w6692.count); bcs->tx_cnt += bcs->hw.w6692.count; bcs->hw.w6692.count = 0; } } if (bcs->tx_skb) { if (bcs->tx_skb->len) { W6692B_fill_fifo(bcs); return; } else { if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) && (PACKET_NOACK != bcs->tx_skb->pkt_type)) { u_long flags; spin_lock_irqsave(&bcs->aclock, flags); bcs->ackcnt += bcs->hw.w6692.count; spin_unlock_irqrestore(&bcs->aclock, flags); schedule_event(bcs, B_ACKPENDING); } dev_kfree_skb_irq(bcs->tx_skb); bcs->hw.w6692.count = 0; bcs->tx_skb = NULL; } } if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { bcs->hw.w6692.count = 0; test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); W6692B_fill_fifo(bcs); } else { test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); schedule_event(bcs, B_XMTBUFREADY); } }}static irqreturn_tW6692_interrupt(int intno, void *dev_id, struct pt_regs *regs){ struct IsdnCardState *cs = dev_id; u_char val, exval, v1; struct sk_buff *skb; u_int count; u_long flags; int icnt = 5; spin_lock_irqsave(&cs->lock, flags); val = cs->readW6692(cs, W_ISTA); if (!val) { spin_unlock_irqrestore(&cs->lock, flags); return IRQ_NONE; } StartW6692: if (cs->debug & L1_DEB_ISAC) debugl1(cs, "W6692 ISTA %x", val); if (val & W_INT_D_RME) { /* RME */ exval = cs->readW6692(cs, W_D_RSTA); if (exval & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) { if (exval & W_D_RSTA_RDOV) if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 RDOV"); if (exval & W_D_RSTA_CRCE) if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 D-channel CRC error"); if (exval & W_D_RSTA_RMB) if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 D-channel ABORT"); cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST); } else { count = cs->readW6692(cs, W_D_RBCL) & (W_D_FIFO_THRESH - 1); if (count == 0) count = W_D_FIFO_THRESH; W6692_empty_fifo(cs, count); if ((count = cs->rcvidx) > 0) { cs->rcvidx = 0; if (!(skb = alloc_skb(count, GFP_ATOMIC))) printk(KERN_WARNING "HiSax: D 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 (val & W_INT_D_RMR) { /* RMR */ W6692_empty_fifo(cs, W_D_FIFO_THRESH); } if (val & W_INT_D_XFR) { /* XFR */ 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) { W6692_fill_fifo(cs); goto afterXFR; } else { dev_kfree_skb_irq(cs->tx_skb); cs->tx_cnt = 0; cs->tx_skb = NULL; } } if ((cs->tx_skb = skb_dequeue(&cs->sq))) { cs->tx_cnt = 0; W6692_fill_fifo(cs); } else schedule_event(cs, D_XMTBUFREADY); } afterXFR: if (val & (W_INT_XINT0 | W_INT_XINT1)) { /* XINT0/1 - never */ if (cs->debug & L1_DEB_ISAC) debugl1(cs, "W6692 spurious XINT!"); } if (val & W_INT_D_EXI) { /* EXI */ exval = cs->readW6692(cs, W_D_EXIR); if (cs->debug & L1_DEB_WARN) debugl1(cs, "W6692 D_EXIR %02x", exval); if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) { /* Transmit underrun/collision */ debugl1(cs, "W6692 D-chan underrun/collision"); printk(KERN_WARNING "HiSax: W6692 XDUN/XCOL\n"); 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) { /* Restart frame */ skb_push(cs->tx_skb, cs->tx_cnt); cs->tx_cnt = 0; W6692_fill_fifo(cs); } else { printk(KERN_WARNING "HiSax: W6692 XDUN/XCOL no skb\n"); debugl1(cs, "W6692 XDUN/XCOL no skb"); cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_XRST); } } if (exval & W_D_EXI_RDOV) { /* RDOV */ debugl1(cs, "W6692 D-channel RDOV"); printk(KERN_WARNING "HiSax: W6692 D-RDOV\n"); cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RRST); } if (exval & W_D_EXI_TIN2) { /* TIN2 - never */ debugl1(cs, "W6692 spurious TIN2 interrupt"); } if (exval & W_D_EXI_MOC) { /* MOC - not supported */ debugl1(cs, "W6692 spurious MOC interrupt"); v1 = cs->readW6692(cs, W_MOSR); debugl1(cs, "W6692 MOSR %02x", v1); } if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */ v1 = cs->readW6692(cs, W_CIR); if (cs->debug & L1_DEB_ISAC) debugl1(cs, "W6692 ISC CIR=0x%02X", v1); if (v1 & W_CIR_ICC) { cs->dc.w6692.ph_state = v1 & W_CIR_COD_MASK; if (cs->debug & L1_DEB_ISAC) debugl1(cs, "ph_state_change %x", cs->dc.w6692.ph_state); schedule_event(cs, D_L1STATECHANGE); } if (v1 & W_CIR_SCC) { v1 = cs->readW6692(cs, W_SQR); debugl1(cs, "W6692 SCC SQR=0x%02X", v1); } } if (exval & W_D_EXI_WEXP) { debugl1(cs, "W6692 spurious WEXP interrupt!"); } if (exval & W_D_EXI_TEXP) { debugl1(cs, "W6692 spurious TEXP interrupt!"); } } if (val & W_INT_B1_EXI) { debugl1(cs, "W6692 B channel 1 interrupt"); W6692B_interrupt(cs, 0); } if (val & W_INT_B2_EXI) { debugl1(cs, "W6692 B channel 2 interrupt"); W6692B_interrupt(cs, 1); } val = cs->readW6692(cs, W_ISTA);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -