📄 wanxl.c
字号:
/* * wanXL serial card driver for Linux * host part * * Copyright (C) 2003 Krzysztof Halasa <khc@pm.waw.pl> * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License * as published by the Free Software Foundation. * * Status: * - Only DTE (external clock) support with NRZ and NRZI encodings * - wanXL100 will require minor driver modifications, no access to hw */#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/netdevice.h>#include <linux/hdlc.h>#include <linux/pci.h>#include <linux/dma-mapping.h>#include <asm/io.h>#include <asm/delay.h>#include "wanxl.h"static const char* version = "wanXL serial card driver version: 0.48";#define PLX_CTL_RESET 0x40000000 /* adapter reset */#undef DEBUG_PKT#undef DEBUG_PCI/* MAILBOX #1 - PUTS COMMANDS */#define MBX1_CMD_ABORTJ 0x85000000 /* Abort and Jump */#ifdef __LITTLE_ENDIAN#define MBX1_CMD_BSWAP 0x8C000001 /* little-endian Byte Swap Mode */#else#define MBX1_CMD_BSWAP 0x8C000000 /* big-endian Byte Swap Mode */#endif/* MAILBOX #2 - DRAM SIZE */#define MBX2_MEMSZ_MASK 0xFFFF0000 /* PUTS Memory Size Register mask */typedef struct { struct net_device *dev; struct card_t *card; spinlock_t lock; /* for wanxl_xmit */ int node; /* physical port #0 - 3 */ unsigned int clock_type; int tx_in, tx_out; struct sk_buff *tx_skbs[TX_BUFFERS];}port_t;typedef struct { desc_t rx_descs[RX_QUEUE_LENGTH]; port_status_t port_status[4];}card_status_t;typedef struct card_t { int n_ports; /* 1, 2 or 4 ports */ u8 irq; u8 __iomem *plx; /* PLX PCI9060 virtual base address */ struct pci_dev *pdev; /* for pci_name(pdev) */ int rx_in; struct sk_buff *rx_skbs[RX_QUEUE_LENGTH]; card_status_t *status; /* shared between host and card */ dma_addr_t status_address; port_t ports[0]; /* 1 - 4 port_t structures follow */}card_t;static inline port_t* dev_to_port(struct net_device *dev){ return (port_t *)dev_to_hdlc(dev)->priv;}static inline port_status_t* get_status(port_t *port){ return &port->card->status->port_status[port->node];}#ifdef DEBUG_PCIstatic inline dma_addr_t pci_map_single_debug(struct pci_dev *pdev, void *ptr, size_t size, int direction){ dma_addr_t addr = pci_map_single(pdev, ptr, size, direction); if (addr + size > 0x100000000LL) printk(KERN_CRIT "wanXL %s: pci_map_single() returned memory" " at 0x%LX!\n", pci_name(pdev), (unsigned long long)addr); return addr;}#undef pci_map_single#define pci_map_single pci_map_single_debug#endif/* Cable and/or personality module change interrupt service */static inline void wanxl_cable_intr(port_t *port){ u32 value = get_status(port)->cable; int valid = 1; const char *cable, *pm, *dte = "", *dsr = "", *dcd = ""; switch(value & 0x7) { case STATUS_CABLE_V35: cable = "V.35"; break; case STATUS_CABLE_X21: cable = "X.21"; break; case STATUS_CABLE_V24: cable = "V.24"; break; case STATUS_CABLE_EIA530: cable = "EIA530"; break; case STATUS_CABLE_NONE: cable = "no"; break; default: cable = "invalid"; } switch((value >> STATUS_CABLE_PM_SHIFT) & 0x7) { case STATUS_CABLE_V35: pm = "V.35"; break; case STATUS_CABLE_X21: pm = "X.21"; break; case STATUS_CABLE_V24: pm = "V.24"; break; case STATUS_CABLE_EIA530: pm = "EIA530"; break; case STATUS_CABLE_NONE: pm = "no personality"; valid = 0; break; default: pm = "invalid personality"; valid = 0; } if (valid) { if ((value & 7) == ((value >> STATUS_CABLE_PM_SHIFT) & 7)) { dsr = (value & STATUS_CABLE_DSR) ? ", DSR ON" : ", DSR off"; dcd = (value & STATUS_CABLE_DCD) ? ", carrier ON" : ", carrier off"; } dte = (value & STATUS_CABLE_DCE) ? " DCE" : " DTE"; } printk(KERN_INFO "%s: %s%s module, %s cable%s%s\n", port->dev->name, pm, dte, cable, dsr, dcd); hdlc_set_carrier(value & STATUS_CABLE_DCD, port->dev);}/* Transmit complete interrupt service */static inline void wanxl_tx_intr(port_t *port){ struct net_device *dev = port->dev; struct net_device_stats *stats = hdlc_stats(dev); while (1) { desc_t *desc = &get_status(port)->tx_descs[port->tx_in]; struct sk_buff *skb = port->tx_skbs[port->tx_in]; switch (desc->stat) { case PACKET_FULL: case PACKET_EMPTY: netif_wake_queue(dev); return; case PACKET_UNDERRUN: stats->tx_errors++; stats->tx_fifo_errors++; break; default: stats->tx_packets++; stats->tx_bytes += skb->len; } desc->stat = PACKET_EMPTY; /* Free descriptor */ pci_unmap_single(port->card->pdev, desc->address, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(skb); port->tx_in = (port->tx_in + 1) % TX_BUFFERS; }}/* Receive complete interrupt service */static inline void wanxl_rx_intr(card_t *card){ desc_t *desc; while (desc = &card->status->rx_descs[card->rx_in], desc->stat != PACKET_EMPTY) { if ((desc->stat & PACKET_PORT_MASK) > card->n_ports) printk(KERN_CRIT "wanXL %s: received packet for" " nonexistent port\n", pci_name(card->pdev)); else { struct sk_buff *skb = card->rx_skbs[card->rx_in]; port_t *port = &card->ports[desc->stat & PACKET_PORT_MASK]; struct net_device *dev = port->dev; struct net_device_stats *stats = hdlc_stats(dev); if (!skb) stats->rx_dropped++; else { pci_unmap_single(card->pdev, desc->address, BUFFER_LENGTH, PCI_DMA_FROMDEVICE); skb_put(skb, desc->length);#ifdef DEBUG_PKT printk(KERN_DEBUG "%s RX(%i):", dev->name, skb->len); debug_frame(skb);#endif stats->rx_packets++; stats->rx_bytes += skb->len; dev->last_rx = jiffies; skb->protocol = hdlc_type_trans(skb, dev); netif_rx(skb); skb = NULL; } if (!skb) { skb = dev_alloc_skb(BUFFER_LENGTH); desc->address = skb ? pci_map_single(card->pdev, skb->data, BUFFER_LENGTH, PCI_DMA_FROMDEVICE) : 0; card->rx_skbs[card->rx_in] = skb; } } desc->stat = PACKET_EMPTY; /* Free descriptor */ card->rx_in = (card->rx_in + 1) % RX_QUEUE_LENGTH; }}static irqreturn_t wanxl_intr(int irq, void* dev_id, struct pt_regs *regs){ card_t *card = dev_id; int i; u32 stat; int handled = 0; while((stat = readl(card->plx + PLX_DOORBELL_FROM_CARD)) != 0) { handled = 1; writel(stat, card->plx + PLX_DOORBELL_FROM_CARD); for (i = 0; i < card->n_ports; i++) { if (stat & (1 << (DOORBELL_FROM_CARD_TX_0 + i))) wanxl_tx_intr(&card->ports[i]); if (stat & (1 << (DOORBELL_FROM_CARD_CABLE_0 + i))) wanxl_cable_intr(&card->ports[i]); } if (stat & (1 << DOORBELL_FROM_CARD_RX)) wanxl_rx_intr(card); } return IRQ_RETVAL(handled);}static int wanxl_xmit(struct sk_buff *skb, struct net_device *dev){ port_t *port = dev_to_port(dev); desc_t *desc; spin_lock(&port->lock); desc = &get_status(port)->tx_descs[port->tx_out]; if (desc->stat != PACKET_EMPTY) { /* should never happen - previous xmit should stop queue */#ifdef DEBUG_PKT printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name);#endif netif_stop_queue(dev); spin_unlock_irq(&port->lock); return 1; /* request packet to be queued */ }#ifdef DEBUG_PKT printk(KERN_DEBUG "%s TX(%i):", dev->name, skb->len); debug_frame(skb);#endif port->tx_skbs[port->tx_out] = skb; desc->address = pci_map_single(port->card->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); desc->length = skb->len; desc->stat = PACKET_FULL; writel(1 << (DOORBELL_TO_CARD_TX_0 + port->node), port->card->plx + PLX_DOORBELL_TO_CARD); dev->trans_start = jiffies; port->tx_out = (port->tx_out + 1) % TX_BUFFERS; if (get_status(port)->tx_descs[port->tx_out].stat != PACKET_EMPTY) { netif_stop_queue(dev);#ifdef DEBUG_PKT printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name);#endif } spin_unlock(&port->lock); return 0;}static int wanxl_attach(struct net_device *dev, unsigned short encoding, unsigned short parity){ port_t *port = dev_to_port(dev); if (encoding != ENCODING_NRZ && encoding != ENCODING_NRZI) return -EINVAL; if (parity != PARITY_NONE && parity != PARITY_CRC32_PR1_CCITT && parity != PARITY_CRC16_PR1_CCITT && parity != PARITY_CRC32_PR0_CCITT && parity != PARITY_CRC16_PR0_CCITT) return -EINVAL; get_status(port)->encoding = encoding; get_status(port)->parity = parity; return 0;}static int wanxl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ const size_t size = sizeof(sync_serial_settings); sync_serial_settings line; port_t *port = dev_to_port(dev); if (cmd != SIOCWANDEV) return hdlc_ioctl(dev, ifr, cmd); switch (ifr->ifr_settings.type) { case IF_GET_IFACE: ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; if (ifr->ifr_settings.size < size) { ifr->ifr_settings.size = size; /* data size wanted */ return -ENOBUFS; } line.clock_type = get_status(port)->clocking; line.clock_rate = 0; line.loopback = 0; if (copy_to_user(ifr->ifr_settings.ifs_ifsu.sync, &line, size)) return -EFAULT; return 0; case IF_IFACE_SYNC_SERIAL: if (!capable(CAP_NET_ADMIN)) return -EPERM; if (dev->flags & IFF_UP) return -EBUSY; if (copy_from_user(&line, ifr->ifr_settings.ifs_ifsu.sync, size)) return -EFAULT; if (line.clock_type != CLOCK_EXT && line.clock_type != CLOCK_TXFROMRX) return -EINVAL; /* No such clock setting */ if (line.loopback != 0) return -EINVAL; get_status(port)->clocking = line.clock_type; return 0; default: return hdlc_ioctl(dev, ifr, cmd); }}static int wanxl_open(struct net_device *dev){ port_t *port = dev_to_port(dev); u8 __iomem *dbr = port->card->plx + PLX_DOORBELL_TO_CARD; unsigned long timeout; int i; if (get_status(port)->open) { printk(KERN_ERR "%s: port already open\n", dev->name); return -EIO; } if ((i = hdlc_open(dev)) != 0) return i; port->tx_in = port->tx_out = 0; for (i = 0; i < TX_BUFFERS; i++) get_status(port)->tx_descs[i].stat = PACKET_EMPTY; /* signal the card */ writel(1 << (DOORBELL_TO_CARD_OPEN_0 + port->node), dbr); timeout = jiffies + HZ; do if (get_status(port)->open) { netif_start_queue(dev); return 0; } while (time_after(timeout, jiffies)); printk(KERN_ERR "%s: unable to open port\n", dev->name); /* ask the card to close the port, should it be still alive */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -