📄 hisax_fcpcipnp.c
字号:
/* * Driver for AVM Fritz!PCI, Fritz!PCI v2, Fritz!PnP ISDN cards * * Author Kai Germaschewski * Copyright 2001 by Kai Germaschewski <kai.germaschewski@gmx.de> * 2001 by Karsten Keil <keil@isdn4linux.de> * * based upon Karsten Keil's original avm_pci.c driver * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * * Thanks to Wizard Computersysteme GmbH, Bremervoerde and * SoHaNet Technology GmbH, Berlin * for supporting the development of this driver *//* TODO: * * o POWER PC * o clean up debugging * o tx_skb at PH_DEACTIVATE time */#include <linux/module.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/isapnp.h>#include <linux/kmod.h>#include <linux/slab.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/delay.h>#include <asm/io.h>#include "hisax_fcpcipnp.h"// debugging cruft#define __debug_variable debug#include "hisax_debug.h"#ifdef CONFIG_HISAX_DEBUGstatic int debug = 0;/* static int hdlcfifosize = 32; */module_param(debug, int, 0);/* module_param(hdlcfifosize, int, 0); */#endifMODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");MODULE_DESCRIPTION("AVM Fritz!PCI/PnP ISDN driver");static struct pci_device_id fcpci_ids[] = { { .vendor = PCI_VENDOR_ID_AVM, .device = PCI_DEVICE_ID_AVM_A1, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = (unsigned long) "Fritz!Card PCI", }, { .vendor = PCI_VENDOR_ID_AVM, .device = PCI_DEVICE_ID_AVM_A1_V2, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = (unsigned long) "Fritz!Card PCI v2" }, {}};MODULE_DEVICE_TABLE(pci, fcpci_ids);#ifdef __ISAPNP__static struct pnp_device_id fcpnp_ids[] __devinitdata = { { .id = "AVM0900", .driver_data = (unsigned long) "Fritz!Card PnP", },};MODULE_DEVICE_TABLE(isapnp, fcpnp_ids);#endifstatic int protocol = 2; /* EURO-ISDN Default */module_param(protocol, int, 0);MODULE_LICENSE("GPL");// ----------------------------------------------------------------------#define AVM_INDEX 0x04#define AVM_DATA 0x10#define AVM_IDX_HDLC_1 0x00#define AVM_IDX_HDLC_2 0x01#define AVM_IDX_ISAC_FIFO 0x02#define AVM_IDX_ISAC_REG_LOW 0x04#define AVM_IDX_ISAC_REG_HIGH 0x06#define AVM_STATUS0 0x02#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 0x03#define AVM_STATUS1_ENA_IOM 0x80#define HDLC_FIFO 0x0#define HDLC_STATUS 0x4#define HDLC_CTRL 0x4#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 0xff00#define HDLC_CMD_XRS 0x80#define HDLC_CMD_XME 0x01#define HDLC_CMD_RRS 0x20#define HDLC_CMD_XML_MASK 0xff00#define AVM_HDLC_FIFO_1 0x10#define AVM_HDLC_FIFO_2 0x18#define AVM_HDLC_STATUS_1 0x14#define AVM_HDLC_STATUS_2 0x1c#define AVM_ISACSX_INDEX 0x04#define AVM_ISACSX_DATA 0x08// ----------------------------------------------------------------------// Fritz!PCIstatic unsigned char fcpci_read_isac(struct isac *isac, unsigned char offset){ struct fritz_adapter *adapter = isac->priv; unsigned char idx = (offset > 0x2f) ? AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW; unsigned char val; unsigned long flags; spin_lock_irqsave(&adapter->hw_lock, flags); outb(idx, adapter->io + AVM_INDEX); val = inb(adapter->io + AVM_DATA + (offset & 0xf)); spin_unlock_irqrestore(&adapter->hw_lock, flags); DBG(0x1000, " port %#x, value %#x", offset, val); return val;}static void fcpci_write_isac(struct isac *isac, unsigned char offset, unsigned char value){ struct fritz_adapter *adapter = isac->priv; unsigned char idx = (offset > 0x2f) ? AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW; unsigned long flags; DBG(0x1000, " port %#x, value %#x", offset, value); spin_lock_irqsave(&adapter->hw_lock, flags); outb(idx, adapter->io + AVM_INDEX); outb(value, adapter->io + AVM_DATA + (offset & 0xf)); spin_unlock_irqrestore(&adapter->hw_lock, flags);}static void fcpci_read_isac_fifo(struct isac *isac, unsigned char * data, int size){ struct fritz_adapter *adapter = isac->priv; unsigned long flags; spin_lock_irqsave(&adapter->hw_lock, flags); outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX); insb(adapter->io + AVM_DATA, data, size); spin_unlock_irqrestore(&adapter->hw_lock, flags);}static void fcpci_write_isac_fifo(struct isac *isac, unsigned char * data, int size){ struct fritz_adapter *adapter = isac->priv; unsigned long flags; spin_lock_irqsave(&adapter->hw_lock, flags); outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX); outsb(adapter->io + AVM_DATA, data, size); spin_unlock_irqrestore(&adapter->hw_lock, flags);}static u32 fcpci_read_hdlc_status(struct fritz_adapter *adapter, int nr){ u32 val; int idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1; unsigned long flags; spin_lock_irqsave(&adapter->hw_lock, flags); outl(idx, adapter->io + AVM_INDEX); val = inl(adapter->io + AVM_DATA + HDLC_STATUS); spin_unlock_irqrestore(&adapter->hw_lock, flags); return val;}static void __fcpci_write_ctrl(struct fritz_bcs *bcs, int which){ struct fritz_adapter *adapter = bcs->adapter; int idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1; DBG(0x40, "hdlc %c wr%x ctrl %x", 'A' + bcs->channel, which, bcs->ctrl.ctrl); outl(idx, adapter->io + AVM_INDEX); outl(bcs->ctrl.ctrl, adapter->io + AVM_DATA + HDLC_CTRL);}static void fcpci_write_ctrl(struct fritz_bcs *bcs, int which){ struct fritz_adapter *adapter = bcs->adapter; unsigned long flags; spin_lock_irqsave(&adapter->hw_lock, flags); __fcpci_write_ctrl(bcs, which); spin_unlock_irqrestore(&adapter->hw_lock, flags);}// ----------------------------------------------------------------------// Fritz!PCI v2static unsigned char fcpci2_read_isac(struct isac *isac, unsigned char offset){ struct fritz_adapter *adapter = isac->priv; unsigned char val; unsigned long flags; spin_lock_irqsave(&adapter->hw_lock, flags); outl(offset, adapter->io + AVM_ISACSX_INDEX); val = inl(adapter->io + AVM_ISACSX_DATA); spin_unlock_irqrestore(&adapter->hw_lock, flags); DBG(0x1000, " port %#x, value %#x", offset, val); return val;}static void fcpci2_write_isac(struct isac *isac, unsigned char offset, unsigned char value){ struct fritz_adapter *adapter = isac->priv; unsigned long flags; DBG(0x1000, " port %#x, value %#x", offset, value); spin_lock_irqsave(&adapter->hw_lock, flags); outl(offset, adapter->io + AVM_ISACSX_INDEX); outl(value, adapter->io + AVM_ISACSX_DATA); spin_unlock_irqrestore(&adapter->hw_lock, flags);}static void fcpci2_read_isac_fifo(struct isac *isac, unsigned char * data, int size){ struct fritz_adapter *adapter = isac->priv; int i; unsigned long flags; spin_lock_irqsave(&adapter->hw_lock, flags); outl(0, adapter->io + AVM_ISACSX_INDEX); for (i = 0; i < size; i++) data[i] = inl(adapter->io + AVM_ISACSX_DATA); spin_unlock_irqrestore(&adapter->hw_lock, flags);}static void fcpci2_write_isac_fifo(struct isac *isac, unsigned char * data, int size){ struct fritz_adapter *adapter = isac->priv; int i; unsigned long flags; spin_lock_irqsave(&adapter->hw_lock, flags); outl(0, adapter->io + AVM_ISACSX_INDEX); for (i = 0; i < size; i++) outl(data[i], adapter->io + AVM_ISACSX_DATA); spin_unlock_irqrestore(&adapter->hw_lock, flags);}static u32 fcpci2_read_hdlc_status(struct fritz_adapter *adapter, int nr){ int offset = nr ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1; return inl(adapter->io + offset);}static void fcpci2_write_ctrl(struct fritz_bcs *bcs, int which){ struct fritz_adapter *adapter = bcs->adapter; int offset = bcs->channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1; DBG(0x40, "hdlc %c wr%x ctrl %x", 'A' + bcs->channel, which, bcs->ctrl.ctrl); outl(bcs->ctrl.ctrl, adapter->io + offset);}// ----------------------------------------------------------------------// Fritz!PnP (ISAC access as for Fritz!PCI)static u32 fcpnp_read_hdlc_status(struct fritz_adapter *adapter, int nr){ unsigned char idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1; u32 val; unsigned long flags; spin_lock_irqsave(&adapter->hw_lock, flags); outb(idx, adapter->io + AVM_INDEX); val = inb(adapter->io + AVM_DATA + HDLC_STATUS); if (val & HDLC_INT_RPR) val |= inb(adapter->io + AVM_DATA + HDLC_STATUS + 1) << 8; spin_unlock_irqrestore(&adapter->hw_lock, flags); return val;}static void __fcpnp_write_ctrl(struct fritz_bcs *bcs, int which){ struct fritz_adapter *adapter = bcs->adapter; unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1; DBG(0x40, "hdlc %c wr%x ctrl %x", 'A' + bcs->channel, which, bcs->ctrl.ctrl); outb(idx, adapter->io + AVM_INDEX); if (which & 4) outb(bcs->ctrl.sr.mode, adapter->io + AVM_DATA + HDLC_STATUS + 2); if (which & 2) outb(bcs->ctrl.sr.xml, adapter->io + AVM_DATA + HDLC_STATUS + 1); if (which & 1) outb(bcs->ctrl.sr.cmd, adapter->io + AVM_DATA + HDLC_STATUS + 0);}static void fcpnp_write_ctrl(struct fritz_bcs *bcs, int which){ struct fritz_adapter *adapter = bcs->adapter; unsigned long flags; spin_lock_irqsave(&adapter->hw_lock, flags); __fcpnp_write_ctrl(bcs, which); spin_unlock_irqrestore(&adapter->hw_lock, flags);}// ----------------------------------------------------------------------static inline void B_L1L2(struct fritz_bcs *bcs, int pr, void *arg){ struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if; DBG(2, "pr %#x", pr); ifc->l1l2(ifc, pr, arg);}static void hdlc_fill_fifo(struct fritz_bcs *bcs){ struct fritz_adapter *adapter = bcs->adapter; struct sk_buff *skb = bcs->tx_skb; int count; unsigned long flags; unsigned char *p; DBG(0x40, "hdlc_fill_fifo"); if (skb->len == 0) BUG(); bcs->ctrl.sr.cmd &= ~HDLC_CMD_XME; if (bcs->tx_skb->len > bcs->fifo_size) { count = bcs->fifo_size; } else { count = bcs->tx_skb->len; if (bcs->mode != L1_MODE_TRANS) bcs->ctrl.sr.cmd |= HDLC_CMD_XME; } DBG(0x40, "hdlc_fill_fifo %d/%d", count, bcs->tx_skb->len); p = bcs->tx_skb->data; skb_pull(bcs->tx_skb, count); bcs->tx_cnt += count; bcs->ctrl.sr.xml = ((count == bcs->fifo_size) ? 0 : count); switch (adapter->type) { case AVM_FRITZ_PCI: spin_lock_irqsave(&adapter->hw_lock, flags); // sets the correct AVM_INDEX, too __fcpci_write_ctrl(bcs, 3); outsl(adapter->io + AVM_DATA + HDLC_FIFO, p, (count + 3) / 4); spin_unlock_irqrestore(&adapter->hw_lock, flags); break; case AVM_FRITZ_PCIV2: fcpci2_write_ctrl(bcs, 3); outsl(adapter->io + (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1), p, (count + 3) / 4); break; case AVM_FRITZ_PNP: spin_lock_irqsave(&adapter->hw_lock, flags); // sets the correct AVM_INDEX, too __fcpnp_write_ctrl(bcs, 3); outsb(adapter->io + AVM_DATA, p, count); spin_unlock_irqrestore(&adapter->hw_lock, flags); break; }}static inline void hdlc_empty_fifo(struct fritz_bcs *bcs, int count){ struct fritz_adapter *adapter = bcs->adapter; unsigned char *p; unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1; DBG(0x10, "hdlc_empty_fifo %d", count); if (bcs->rcvidx + count > HSCX_BUFMAX) { DBG(0x10, "hdlc_empty_fifo: incoming packet too large"); return; } p = bcs->rcvbuf + bcs->rcvidx; bcs->rcvidx += count; switch (adapter->type) { case AVM_FRITZ_PCI: spin_lock(&adapter->hw_lock); outl(idx, adapter->io + AVM_INDEX); insl(adapter->io + AVM_DATA + HDLC_FIFO, p, (count + 3) / 4); spin_unlock(&adapter->hw_lock); break; case AVM_FRITZ_PCIV2: insl(adapter->io + (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1), p, (count + 3) / 4); break; case AVM_FRITZ_PNP: spin_lock(&adapter->hw_lock); outb(idx, adapter->io + AVM_INDEX); insb(adapter->io + AVM_DATA, p, count); spin_unlock(&adapter->hw_lock); break; }}static inline void hdlc_rpr_irq(struct fritz_bcs *bcs, u32 stat){ struct fritz_adapter *adapter = bcs->adapter; struct sk_buff *skb; int len; if (stat & HDLC_STAT_RDO) { DBG(0x10, "RDO"); bcs->ctrl.sr.xml = 0; bcs->ctrl.sr.cmd |= HDLC_CMD_RRS; adapter->write_ctrl(bcs, 1); bcs->ctrl.sr.cmd &= ~HDLC_CMD_RRS; adapter->write_ctrl(bcs, 1); bcs->rcvidx = 0; return; } len = (stat & HDLC_STAT_RML_MASK) >> 8; if (len == 0) len = bcs->fifo_size; 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)) { skb = dev_alloc_skb(bcs->rcvidx); if (!skb) { printk(KERN_WARNING "HDLC: receive out of memory\n"); } else { memcpy(skb_put(skb, bcs->rcvidx), bcs->rcvbuf, bcs->rcvidx); DBG_SKB(1, skb); B_L1L2(bcs, PH_DATA | INDICATION, skb); } bcs->rcvidx = 0; } else { DBG(0x10, "ch%d invalid frame %#x", bcs->channel, stat); bcs->rcvidx = 0; } }}static inline void hdlc_xdu_irq(struct fritz_bcs *bcs){ struct fritz_adapter *adapter = bcs->adapter;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -