📄 avm_pci.c
字号:
/* $Id: avm_pci.c,v 1.22.6.2 2000/11/29 16:00:14 kai Exp $ * * avm_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards * Thanks to AVM, Berlin for informations * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * */#define __NO_VERSION__#include <linux/config.h>#include <linux/init.h>#include "hisax.h"#include "isac.h"#include "isdnl1.h"#include <linux/pci.h>#include <linux/interrupt.h>extern const char *CardType[];static const char *avm_pci_rev = "$Revision: 1.22.6.2 $";#define AVM_FRITZ_PCI 1#define AVM_FRITZ_PNP 2#define HDLC_FIFO 0x0#define HDLC_STATUS 0x4#define AVM_HDLC_1 0x00#define AVM_HDLC_2 0x01#define AVM_ISAC_FIFO 0x02#define AVM_ISAC_REG_LOW 0x04#define AVM_ISAC_REG_HIGH 0x06#define AVM_STATUS0_IRQ_ISAC 0x01#define AVM_STATUS0_IRQ_HDLC 0x02#define AVM_STATUS0_IRQ_TIMER 0x04#define AVM_STATUS0_IRQ_MASK 0x07#define AVM_STATUS0_RESET 0x01#define AVM_STATUS0_DIS_TIMER 0x02#define AVM_STATUS0_RES_TIMER 0x04#define AVM_STATUS0_ENA_IRQ 0x08#define AVM_STATUS0_TESTBIT 0x10#define AVM_STATUS1_INT_SEL 0x0f#define AVM_STATUS1_ENA_IOM 0x80#define HDLC_MODE_ITF_FLG 0x01#define HDLC_MODE_TRANS 0x02#define HDLC_MODE_CCR_7 0x04#define HDLC_MODE_CCR_16 0x08#define HDLC_MODE_TESTLOOP 0x80#define HDLC_INT_XPR 0x80#define HDLC_INT_XDU 0x40#define HDLC_INT_RPR 0x20#define HDLC_INT_MASK 0xE0#define HDLC_STAT_RME 0x01#define HDLC_STAT_RDO 0x10#define HDLC_STAT_CRCVFRRAB 0x0E#define HDLC_STAT_CRCVFR 0x06#define HDLC_STAT_RML_MASK 0x3f00#define HDLC_CMD_XRS 0x80#define HDLC_CMD_XME 0x01#define HDLC_CMD_RRS 0x20#define HDLC_CMD_XML_MASK 0x3f00/* Interface functions */static u_charReadISAC(struct IsdnCardState *cs, u_char offset){ register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; register u_char val; register long flags; save_flags(flags); cli(); outb(idx, cs->hw.avm.cfg_reg + 4); val = inb(cs->hw.avm.isac + (offset & 0xf)); restore_flags(flags); return (val);}static voidWriteISAC(struct IsdnCardState *cs, u_char offset, u_char value){ register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; register long flags; save_flags(flags); cli(); outb(idx, cs->hw.avm.cfg_reg + 4); outb(value, cs->hw.avm.isac + (offset & 0xf)); restore_flags(flags);}static voidReadISACfifo(struct IsdnCardState *cs, u_char * data, int size){ outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4); insb(cs->hw.avm.isac, data, size);}static voidWriteISACfifo(struct IsdnCardState *cs, u_char * data, int size){ outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4); outsb(cs->hw.avm.isac, data, size);}static inline u_intReadHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset){ register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; register u_int val; register long flags; save_flags(flags); cli(); outl(idx, cs->hw.avm.cfg_reg + 4); val = inl(cs->hw.avm.isac + offset); restore_flags(flags); return (val);}static inline voidWriteHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset, u_int value){ register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; register long flags; save_flags(flags); cli(); outl(idx, cs->hw.avm.cfg_reg + 4); outl(value, cs->hw.avm.isac + offset); restore_flags(flags);}static inline u_charReadHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset){ register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; register u_char val; register long flags; save_flags(flags); cli(); outb(idx, cs->hw.avm.cfg_reg + 4); val = inb(cs->hw.avm.isac + offset); restore_flags(flags); return (val);}static inline voidWriteHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset, u_char value){ register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; register long flags; save_flags(flags); cli(); outb(idx, cs->hw.avm.cfg_reg + 4); outb(value, cs->hw.avm.isac + offset); restore_flags(flags);}static u_charReadHDLC_s(struct IsdnCardState *cs, int chan, u_char offset){ return(0xff & ReadHDLCPCI(cs, chan, offset));}static voidWriteHDLC_s(struct IsdnCardState *cs, int chan, u_char offset, u_char value){ WriteHDLCPCI(cs, chan, offset, value);}static inlinestruct BCState *Sel_BCS(struct IsdnCardState *cs, int channel){ if (cs->bcs[0].mode && (cs->bcs[0].channel == channel)) return(&cs->bcs[0]); else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel)) return(&cs->bcs[1]); else return(NULL);}void inlinehdlc_sched_event(struct BCState *bcs, int event){ bcs->event |= 1 << event; queue_task(&bcs->tqueue, &tq_immediate); mark_bh(IMMEDIATE_BH);}voidwrite_ctrl(struct BCState *bcs, int which) { if (bcs->cs->debug & L1_DEB_HSCX) debugl1(bcs->cs, "hdlc %c wr%x ctrl %x", 'A' + bcs->channel, which, bcs->hw.hdlc.ctrl.ctrl); if (bcs->cs->subtyp == AVM_FRITZ_PCI) { WriteHDLCPCI(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.ctrl); } else { if (which & 4) WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 2, bcs->hw.hdlc.ctrl.sr.mode); if (which & 2) WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 1, bcs->hw.hdlc.ctrl.sr.xml); if (which & 1) WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.sr.cmd); }}voidmodehdlc(struct BCState *bcs, int mode, int bc){ struct IsdnCardState *cs = bcs->cs; int hdlc = bcs->channel; if (cs->debug & L1_DEB_HSCX) debugl1(cs, "hdlc %c mode %d --> %d ichan %d --> %d", 'A' + hdlc, bcs->mode, mode, hdlc, bc); bcs->hw.hdlc.ctrl.ctrl = 0; switch (mode) { case (-1): /* used for init */ bcs->mode = 1; bcs->channel = bc; bc = 0; case (L1_MODE_NULL): if (bcs->mode == L1_MODE_NULL) return; bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS; write_ctrl(bcs, 5); bcs->mode = L1_MODE_NULL; bcs->channel = bc; break; case (L1_MODE_TRANS): bcs->mode = mode; bcs->channel = bc; bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS; write_ctrl(bcs, 5); bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS; write_ctrl(bcs, 1); bcs->hw.hdlc.ctrl.sr.cmd = 0; hdlc_sched_event(bcs, B_XMTBUFREADY); break; case (L1_MODE_HDLC): bcs->mode = mode; bcs->channel = bc; bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG; write_ctrl(bcs, 5); bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS; write_ctrl(bcs, 1); bcs->hw.hdlc.ctrl.sr.cmd = 0; hdlc_sched_event(bcs, B_XMTBUFREADY); break; }}static inline voidhdlc_empty_fifo(struct BCState *bcs, int count){ register u_int *ptr; u_char *p; u_char idx = bcs->channel ? AVM_HDLC_2 : AVM_HDLC_1; int cnt=0; struct IsdnCardState *cs = bcs->cs; if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) debugl1(cs, "hdlc_empty_fifo %d", count); if (bcs->hw.hdlc.rcvidx + count > HSCX_BUFMAX) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "hdlc_empty_fifo: incoming packet too large"); return; } ptr = (u_int *) p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx; bcs->hw.hdlc.rcvidx += count; if (cs->subtyp == AVM_FRITZ_PCI) { outl(idx, cs->hw.avm.cfg_reg + 4); while (cnt < count) {#ifdef __powerpc__#ifdef CONFIG_APUS *ptr++ = in_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE));#else *ptr++ = in_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE));#endif /* CONFIG_APUS */#else *ptr++ = inl(cs->hw.avm.isac);#endif /* __powerpc__ */ cnt += 4; } } else { outb(idx, cs->hw.avm.cfg_reg + 4); while (cnt < count) { *p++ = inb(cs->hw.avm.isac); cnt++; } } if (cs->debug & L1_DEB_HSCX_FIFO) { char *t = bcs->blog; if (cs->subtyp == AVM_FRITZ_PNP) p = (u_char *) ptr; t += sprintf(t, "hdlc_empty_fifo %c cnt %d", bcs->channel ? 'B' : 'A', count); QuickHex(t, p, count); debugl1(cs, bcs->blog); }}static inline voidhdlc_fill_fifo(struct BCState *bcs){ struct IsdnCardState *cs = bcs->cs; int count, cnt =0; int fifo_size = 32; u_char *p; u_int *ptr; if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) debugl1(cs, "hdlc_fill_fifo"); if (!bcs->tx_skb) return; if (bcs->tx_skb->len <= 0) return; bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XME; if (bcs->tx_skb->len > fifo_size) { count = fifo_size; } else { count = bcs->tx_skb->len; if (bcs->mode != L1_MODE_TRANS) bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XME; } if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) debugl1(cs, "hdlc_fill_fifo %d/%ld", count, bcs->tx_skb->len); ptr = (u_int *) p = bcs->tx_skb->data; skb_pull(bcs->tx_skb, count); bcs->tx_cnt -= count; bcs->hw.hdlc.count += count; bcs->hw.hdlc.ctrl.sr.xml = ((count == fifo_size) ? 0 : count); write_ctrl(bcs, 3); /* sets the correct index too */ if (cs->subtyp == AVM_FRITZ_PCI) { while (cnt<count) {#ifdef __powerpc__#ifdef CONFIG_APUS out_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++);#else out_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++);#endif /* CONFIG_APUS */#else outl(*ptr++, cs->hw.avm.isac);#endif /* __powerpc__ */ cnt += 4; } } else { while (cnt<count) { outb(*p++, cs->hw.avm.isac); cnt++; } } if (cs->debug & L1_DEB_HSCX_FIFO) { char *t = bcs->blog; if (cs->subtyp == AVM_FRITZ_PNP) p = (u_char *) ptr; t += sprintf(t, "hdlc_fill_fifo %c cnt %d", bcs->channel ? 'B' : 'A', count); QuickHex(t, p, count); debugl1(cs, bcs->blog); }}static voidfill_hdlc(struct BCState *bcs){ long flags; save_flags(flags); cli(); hdlc_fill_fifo(bcs); restore_flags(flags);}static inline voidHDLC_irq(struct BCState *bcs, u_int stat) { int len; struct sk_buff *skb; if (bcs->cs->debug & L1_DEB_HSCX) debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat); if (stat & HDLC_INT_RPR) { if (stat & HDLC_STAT_RDO) { if (bcs->cs->debug & L1_DEB_HSCX) debugl1(bcs->cs, "RDO"); else debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat); bcs->hw.hdlc.ctrl.sr.xml = 0; bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_RRS; write_ctrl(bcs, 1); bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_RRS; write_ctrl(bcs, 1); bcs->hw.hdlc.rcvidx = 0; } else { if (!(len = (stat & HDLC_STAT_RML_MASK)>>8)) len = 32; hdlc_empty_fifo(bcs, len); if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) { if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) || (bcs->mode == L1_MODE_TRANS)) { if (!(skb = dev_alloc_skb(bcs->hw.hdlc.rcvidx))) printk(KERN_WARNING "HDLC: receive out of memory\n"); else { memcpy(skb_put(skb, bcs->hw.hdlc.rcvidx), bcs->hw.hdlc.rcvbuf, bcs->hw.hdlc.rcvidx); skb_queue_tail(&bcs->rqueue, skb);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -