⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wanxl.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -