at1700.c

来自「linux 内核源代码」· C语言 代码 · 共 914 行 · 第 1/2 页

C
914
字号
	dev->tx_timeout = net_tx_timeout;	dev->watchdog_timeo = TX_TIMEOUT;	spin_lock_init(&lp->lock);	lp->jumpered = is_fmv18x;	lp->mca_slot = slot;	/* Snarf the interrupt vector now. */	ret = request_irq(irq, &net_interrupt, 0, DRV_NAME, dev);	if (ret) {		printk ("  AT1700 at %#3x is unusable due to a conflict on"				"IRQ %d.\n", ioaddr, irq);		goto err_mca;	}	return 0;err_mca:#ifdef CONFIG_MCA_LEGACY	if (slot >= 0)		mca_mark_as_unused(slot);#endiferr_out:	release_region(ioaddr, AT1700_IO_EXTENT);	return ret;}/*  EEPROM_Ctrl bits. */#define EE_SHIFT_CLK	0x40	/* EEPROM shift clock, in reg. 16. */#define EE_CS			0x20	/* EEPROM chip select, in reg. 16. */#define EE_DATA_WRITE	0x80	/* EEPROM chip data in, in reg. 17. */#define EE_DATA_READ	0x80	/* EEPROM chip data out, in reg. 17. *//* The EEPROM commands include the alway-set leading bit. */#define EE_WRITE_CMD	(5 << 6)#define EE_READ_CMD		(6 << 6)#define EE_ERASE_CMD	(7 << 6)static int __init read_eeprom(long ioaddr, int location){	int i;	unsigned short retval = 0;	long ee_addr = ioaddr + EEPROM_Ctrl;	long ee_daddr = ioaddr + EEPROM_Data;	int read_cmd = location | EE_READ_CMD;	/* Shift the read command bits out. */	for (i = 9; i >= 0; i--) {		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;		outb(EE_CS, ee_addr);		outb(dataval, ee_daddr);		outb(EE_CS | EE_SHIFT_CLK, ee_addr);	/* EEPROM clock tick. */	}	outb(EE_DATA_WRITE, ee_daddr);	for (i = 16; i > 0; i--) {		outb(EE_CS, ee_addr);		outb(EE_CS | EE_SHIFT_CLK, ee_addr);		retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0);	}	/* Terminate the EEPROM access. */	outb(EE_CS, ee_addr);	outb(EE_SHIFT_CLK, ee_addr);	outb(0, ee_addr);	return retval;}static int net_open(struct net_device *dev){	struct net_local *lp = netdev_priv(dev);	int ioaddr = dev->base_addr;	/* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit	   bus access, and two 4K Tx queues. */	outb(0x5a, ioaddr + CONFIG_0);	/* Powerup, switch to register bank 2, and enable the Rx and Tx. */	outb(0xe8, ioaddr + CONFIG_1);	lp->tx_started = 0;	lp->tx_queue_ready = 1;	lp->rx_started = 0;	lp->tx_queue = 0;	lp->tx_queue_len = 0;	/* Turn on hardware Tx and Rx interrupts. */	outb(0x82, ioaddr + TX_INTR);	outb(0x81, ioaddr + RX_INTR);	/* Enable the IRQ on boards of fmv18x it is feasible. */	if (lp->jumpered) {		outb(0x80, ioaddr + IOCONFIG1);	}	netif_start_queue(dev);	return 0;}static void net_tx_timeout (struct net_device *dev){	struct net_local *lp = netdev_priv(dev);	int ioaddr = dev->base_addr;	printk ("%s: transmit timed out with status %04x, %s?\n", dev->name,		inw (ioaddr + STATUS), inb (ioaddr + TX_STATUS) & 0x80		? "IRQ conflict" : "network cable problem");	printk ("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",	 dev->name, inw(ioaddr + TX_STATUS), inw(ioaddr + TX_INTR), inw(ioaddr + TX_MODE),		inw(ioaddr + CONFIG_0), inw(ioaddr + DATAPORT), inw(ioaddr + TX_START),		inw(ioaddr + MODE13 - 1), inw(ioaddr + RX_CTRL));	dev->stats.tx_errors++;	/* ToDo: We should try to restart the adaptor... */	outw(0xffff, ioaddr + MODE24);	outw (0xffff, ioaddr + TX_STATUS);	outb (0x5a, ioaddr + CONFIG_0);	outb (0xe8, ioaddr + CONFIG_1);	outw (0x8182, ioaddr + TX_INTR);	outb (0x00, ioaddr + TX_START);	outb (0x03, ioaddr + COL16CNTL);	dev->trans_start = jiffies;	lp->tx_started = 0;	lp->tx_queue_ready = 1;	lp->rx_started = 0;	lp->tx_queue = 0;	lp->tx_queue_len = 0;	netif_wake_queue(dev);}static int net_send_packet (struct sk_buff *skb, struct net_device *dev){	struct net_local *lp = netdev_priv(dev);	int ioaddr = dev->base_addr;	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;	short len = skb->len;	unsigned char *buf = skb->data;	static u8 pad[ETH_ZLEN];	netif_stop_queue (dev);	/* We may not start transmitting unless we finish transferring	   a packet into the Tx queue. During executing the following	   codes we possibly catch a Tx interrupt. Thus we flag off	   tx_queue_ready, so that we prevent the interrupt routine	   (net_interrupt) to start transmitting. */	lp->tx_queue_ready = 0;	{		outw (length, ioaddr + DATAPORT);		/* Packet data */		outsw (ioaddr + DATAPORT, buf, len >> 1);		/* Check for dribble byte */		if (len & 1) {			outw(skb->data[skb->len-1], ioaddr + DATAPORT);			len++;		}		/* Check for packet padding */		if (length != skb->len)			outsw(ioaddr + DATAPORT, pad, (length - len + 1) >> 1);		lp->tx_queue++;		lp->tx_queue_len += length + 2;	}	lp->tx_queue_ready = 1;	if (lp->tx_started == 0) {		/* If the Tx is idle, always trigger a transmit. */		outb (0x80 | lp->tx_queue, ioaddr + TX_START);		lp->tx_queue = 0;		lp->tx_queue_len = 0;		dev->trans_start = jiffies;		lp->tx_started = 1;		netif_start_queue (dev);	} else if (lp->tx_queue_len < 4096 - 1502)		/* Yes, there is room for one more packet. */		netif_start_queue (dev);	dev_kfree_skb (skb);	return 0;}/* The typical workload of the driver:   Handle the network interface interrupts. */static irqreturn_t net_interrupt(int irq, void *dev_id){	struct net_device *dev = dev_id;	struct net_local *lp;	int ioaddr, status;	int handled = 0;	if (dev == NULL) {		printk ("at1700_interrupt(): irq %d for unknown device.\n", irq);		return IRQ_NONE;	}	ioaddr = dev->base_addr;	lp = netdev_priv(dev);	spin_lock (&lp->lock);	status = inw(ioaddr + TX_STATUS);	outw(status, ioaddr + TX_STATUS);	if (net_debug > 4)		printk("%s: Interrupt with status %04x.\n", dev->name, status);	if (lp->rx_started == 0 &&	    (status & 0xff00 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) {		/* Got a packet(s).		   We cannot execute net_rx more than once at the same time for		   the same device. During executing net_rx, we possibly catch a		   Tx interrupt. Thus we flag on rx_started, so that we prevent		   the interrupt routine (net_interrupt) to dive into net_rx		   again. */		handled = 1;		lp->rx_started = 1;		outb(0x00, ioaddr + RX_INTR);	/* Disable RX intr. */		net_rx(dev);		outb(0x81, ioaddr + RX_INTR);	/* Enable  RX intr. */		lp->rx_started = 0;	}	if (status & 0x00ff) {		handled = 1;		if (status & 0x02) {			/* More than 16 collisions occurred */			if (net_debug > 4)				printk("%s: 16 Collision occur during Txing.\n", dev->name);			/* Cancel sending a packet. */			outb(0x03, ioaddr + COL16CNTL);			dev->stats.collisions++;		}		if (status & 0x82) {			dev->stats.tx_packets++;			/* The Tx queue has any packets and is not being			   transferred a packet from the host, start			   transmitting. */			if (lp->tx_queue && lp->tx_queue_ready) {				outb(0x80 | lp->tx_queue, ioaddr + TX_START);				lp->tx_queue = 0;				lp->tx_queue_len = 0;				dev->trans_start = jiffies;				netif_wake_queue (dev);			} else {				lp->tx_started = 0;				netif_wake_queue (dev);			}		}	}	spin_unlock (&lp->lock);	return IRQ_RETVAL(handled);}/* We have a good packet(s), get it/them out of the buffers. */static voidnet_rx(struct net_device *dev){	int ioaddr = dev->base_addr;	int boguscount = 5;	while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {		ushort status = inw(ioaddr + DATAPORT);		ushort pkt_len = inw(ioaddr + DATAPORT);		if (net_debug > 4)			printk("%s: Rxing packet mode %02x status %04x.\n",				   dev->name, inb(ioaddr + RX_MODE), status);#ifndef final_version		if (status == 0) {			outb(0x05, ioaddr + RX_CTRL);			break;		}#endif		if ((status & 0xF0) != 0x20) {	/* There was an error. */			dev->stats.rx_errors++;			if (status & 0x08) dev->stats.rx_length_errors++;			if (status & 0x04) dev->stats.rx_frame_errors++;			if (status & 0x02) dev->stats.rx_crc_errors++;			if (status & 0x01) dev->stats.rx_over_errors++;		} else {			/* Malloc up new buffer. */			struct sk_buff *skb;			if (pkt_len > 1550) {				printk("%s: The AT1700 claimed a very large packet, size %d.\n",					   dev->name, pkt_len);				/* Prime the FIFO and then flush the packet. */				inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);				outb(0x05, ioaddr + RX_CTRL);				dev->stats.rx_errors++;				break;			}			skb = dev_alloc_skb(pkt_len+3);			if (skb == NULL) {				printk("%s: Memory squeeze, dropping packet (len %d).\n",					   dev->name, pkt_len);				/* Prime the FIFO and then flush the packet. */				inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);				outb(0x05, ioaddr + RX_CTRL);				dev->stats.rx_dropped++;				break;			}			skb_reserve(skb,2);			insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);			skb->protocol=eth_type_trans(skb, dev);			netif_rx(skb);			dev->last_rx = jiffies;			dev->stats.rx_packets++;			dev->stats.rx_bytes += pkt_len;		}		if (--boguscount <= 0)			break;	}	/* If any worth-while packets have been received, dev_rint()	   has done a mark_bh(NET_BH) for us and will work on them	   when we get to the bottom-half routine. */	{		int i;		for (i = 0; i < 20; i++) {			if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)				break;			inw(ioaddr + DATAPORT);				/* dummy status read */			outb(0x05, ioaddr + RX_CTRL);		}		if (net_debug > 5)			printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",				   dev->name, inb(ioaddr + RX_MODE), i);	}	return;}/* The inverse routine to net_open(). */static int net_close(struct net_device *dev){	struct net_local *lp = netdev_priv(dev);	int ioaddr = dev->base_addr;	netif_stop_queue(dev);	/* Set configuration register 0 to disable Tx and Rx. */	outb(0xda, ioaddr + CONFIG_0);	/* No statistic counters on the chip to update. */	/* Disable the IRQ on boards of fmv18x where it is feasible. */	if (lp->jumpered) {		outb(0x00, ioaddr + IOCONFIG1);		free_irq(dev->irq, dev);	}	/* Power-down the chip.  Green, green, green! */	outb(0x00, ioaddr + CONFIG_1);	return 0;}/*  Set the multicast/promiscuous mode for this adaptor.*/static voidset_rx_mode(struct net_device *dev){	int ioaddr = dev->base_addr;	struct net_local *lp = netdev_priv(dev);	unsigned char mc_filter[8];		 /* Multicast hash filter */	unsigned long flags;	int i;	if (dev->flags & IFF_PROMISC) {		memset(mc_filter, 0xff, sizeof(mc_filter));		outb(3, ioaddr + RX_MODE);	/* Enable promiscuous mode */	} else if (dev->mc_count > MC_FILTERBREAK			   ||  (dev->flags & IFF_ALLMULTI)) {		/* Too many to filter perfectly -- accept all multicasts. */		memset(mc_filter, 0xff, sizeof(mc_filter));		outb(2, ioaddr + RX_MODE);	/* Use normal mode. */	} else if (dev->mc_count == 0) {		memset(mc_filter, 0x00, sizeof(mc_filter));		outb(1, ioaddr + RX_MODE);	/* Ignore almost all multicasts. */	} else {		struct dev_mc_list *mclist;		int i;		memset(mc_filter, 0, sizeof(mc_filter));		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;			 i++, mclist = mclist->next) {			unsigned int bit =				ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 26;			mc_filter[bit >> 3] |= (1 << bit);		}		outb(0x02, ioaddr + RX_MODE);	/* Use normal mode. */	}	spin_lock_irqsave (&lp->lock, flags);	if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) {		int saved_bank = inw(ioaddr + CONFIG_0);		/* Switch to bank 1 and set the multicast table. */		outw((saved_bank & ~0x0C00) | 0x0480, ioaddr + CONFIG_0);		for (i = 0; i < 8; i++)			outb(mc_filter[i], ioaddr + PORT_OFFSET(8 + i));		memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter));		outw(saved_bank, ioaddr + CONFIG_0);	}	spin_unlock_irqrestore (&lp->lock, flags);	return;}#ifdef MODULEstatic struct net_device *dev_at1700;module_param(io, int, 0);module_param(irq, int, 0);module_param(net_debug, int, 0);MODULE_PARM_DESC(io, "AT1700/FMV18X I/O base address");MODULE_PARM_DESC(irq, "AT1700/FMV18X IRQ number");MODULE_PARM_DESC(net_debug, "AT1700/FMV18X debug level (0-6)");int __init init_module(void){	if (io == 0)		printk("at1700: You should not use auto-probing with insmod!\n");	dev_at1700 = at1700_probe(-1);	if (IS_ERR(dev_at1700))		return PTR_ERR(dev_at1700);	return 0;}void __exitcleanup_module(void){	unregister_netdev(dev_at1700);	cleanup_card(dev_at1700);	free_netdev(dev_at1700);}#endif /* MODULE */MODULE_LICENSE("GPL");/* * Local variables: *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c" *  alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c" *  tab-width: 4 *  c-basic-offset: 4 *  c-indent-level: 4 * End: */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?