📄 at1700.c
字号:
/* at1700.c: A network device driver for the Allied Telesis AT1700. Written 1993 by Donald Becker. This is a alpha test limited release. This version may only be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. The author may be reached as becker@super.org or C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 This is a device driver for the Allied Telesis AT1700, which is a straight-foward Fujitsu MB86965 implementation.*/static char *version = "at1700.c:v0.06 3/3/94 Donald Becker (becker@super.org)\n";#include <linux/config.h>/* Sources: The Fujitsu MB86695 datasheet. After this driver was written, ATI provided their EEPROM configuration code header file. Thanks to Gerry Sockins of ATI.*/#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/in.h>#include <linux/malloc.h>#include <linux/string.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/dma.h>#include <errno.h>#include "dev.h"#include "eth.h"#include "skbuff.h"#include "arp.h"#ifndef HAVE_AUTOIRQ/* From auto_irq.c, in ioport.h for later versions. */extern void autoirq_setup(int waittime);extern int autoirq_report(int waittime);/* The map from IRQ number (as passed to the interrupt handler) to 'struct device'. */extern struct device *irq2dev_map[16];#endif#ifndef HAVE_ALLOC_SKB#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)#define kfree_skbmem(addr, size) kfree_s(addr,size);#endif/* use 0 for production, 1 for verification, >2 for debug */#ifndef NET_DEBUG#define NET_DEBUG 2#endifstatic unsigned int net_debug = NET_DEBUG;typedef unsigned char uchar;/* Information that need to be kept for each board. */struct net_local { struct enet_statistics stats; long open_time; /* Useless example local info. */ uint tx_started:1; /* Number of packet on the Tx queue. */ uchar tx_queue; /* Number of packet on the Tx queue. */ ushort tx_queue_len; /* Current length of the Tx queue. */};/* Offsets from the base address. */#define STATUS 0#define TX_STATUS 0#define RX_STATUS 1#define TX_INTR 2 /* Bit-mapped interrupt enable registers. */#define RX_INTR 3#define TX_MODE 4#define RX_MODE 5#define CONFIG_0 6 /* Misc. configuration settings. */#define CONFIG_1 7/* Run-time register bank 2 definitions. */#define DATAPORT 8 /* Word-wide DMA or programmed-I/O dataport. */#define TX_START 10#define MODE13 13#define EEPROM_Ctrl 16#define EEPROM_Data 17/* 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. *//* Delay between EEPROM clock transitions. */#define eeprom_delay() do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)/* 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)/* Index to functions, as function prototypes. */extern int at1700_probe(struct device *dev);static int at1700_probe1(struct device *dev, short ioaddr);static int read_eeprom(int ioaddr, int location);static int net_open(struct device *dev);static int net_send_packet(struct sk_buff *skb, struct device *dev);static void net_interrupt(int reg_ptr);static void net_rx(struct device *dev);static int net_close(struct device *dev);static struct enet_statistics *net_get_stats(struct device *dev);#ifdef HAVE_MULTICASTstatic void set_multicast_list(struct device *dev, int num_addrs, void *addrs);#endif/* Check for a network adaptor of this type, and return '0' iff one exists. If dev->base_addr == 0, probe all likely locations. If dev->base_addr == 1, always return failure. If dev->base_addr == 2, alloate space for the device and return success (detachable devices only). */intat1700_probe(struct device *dev){ short ports[] = {0x300, 0x280, 0x380, 0x320, 0x340, 0x260, 0x2a0, 0x240, 0}; short *port, base_addr = dev->base_addr; if (base_addr > 0x1ff) /* Check a single specified location. */ return at1700_probe1(dev, base_addr); else if (base_addr > 0) /* Don't probe at all. */ return ENXIO; for (port = &ports[0]; *port; port++) { int ioaddr = *port; if (check_region(ioaddr, 32)) continue; if (at1700_probe1(dev, ioaddr) == 0) return 0; } return ENODEV;}/* The Fujitsu datasheet suggests that the NIC be probed for by checking its "signature", the default bit pattern after a reset. This *doesn't* work -- there is no way to reset the bus interface without a complete power-cycle! It turns out that ATI came to the same conclusion I did: the only thing that can be done is checking a few bits and then diving right into an EEPROM read. */int at1700_probe1(struct device *dev, short ioaddr){ unsigned short signature[4] = {0xffff, 0xffff, 0x7ff7, 0xff5f}; unsigned short signature_invalid[4] = {0xffff, 0xffff, 0x7ff7, 0xdf0f}; char irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15}; unsigned short *station_address = (unsigned short *)dev->dev_addr; unsigned int i, irq; /* Resetting the chip doesn't reset the ISA interface, so don't bother. That means we have to be careful with the register values we probe for. */ for (i = 0; i < 4; i++) if ((inw(ioaddr + 2*i) | signature_invalid[i]) != signature[i]) { if (net_debug > 2) printk("AT1700 signature match failed at %d (%04x vs. %04x)\n", i, inw(ioaddr + 2*i), signature[i]); return -ENODEV; } if (read_eeprom(ioaddr, 4) != 0x0000 || read_eeprom(ioaddr, 5) & 0x00ff != 0x00F4) return -ENODEV; /* Grab the region so that we can find another board if the IRQ request fails. */ snarf_region(ioaddr, 32); irq = irqmap[(read_eeprom(ioaddr, 12)&0x04) | (read_eeprom(ioaddr, 0)>>14)]; /* Snarf the interrupt vector now. */ if (request_irq(irq, &net_interrupt)) { printk ("AT1700 found at %#3x, but it's unusable due to a conflict on" "IRQ %d.\n", ioaddr, irq); return EAGAIN; } printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name, ioaddr, irq); dev->base_addr = ioaddr; dev->irq = irq; irq2dev_map[irq] = dev; for(i = 0; i < 3; i++) { unsigned short eeprom_val = read_eeprom(ioaddr, 4+i); printk("%04x", eeprom_val); station_address[i] = ntohs(eeprom_val); } /* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals, rather than 150 ohm shielded twisted pair compansation. 0x0000 == auto-sense the interface 0x0800 == use TP interface 0x1800 == use coax interface */ { char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2"}; ushort setup_value = read_eeprom(ioaddr, 12); dev->if_port = setup_value >> 8; printk(" %s interface (%04x).\n", porttype[(dev->if_port>>3) & 3], setup_value); } /* Set the station address in bank zero. */ outb(0xe0, ioaddr + 7); for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + 8 + i); /* Switch to bank 1 and set the multicast table to accept none. */ outb(0xe4, ioaddr + 7); for (i = 0; i < 8; i++) outb(0x00, ioaddr + 8 + i); /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit bus access, two 4K Tx queues, and disabled Tx and Rx. */ outb(0xda, ioaddr + CONFIG_0); /* Switch to bank 2 and lock our I/O address. */ outb(0xe8, ioaddr + 7); outb(dev->if_port, MODE13); /* Power-down the chip. Aren't we green! */ outb(0x00, ioaddr + CONFIG_1); if (net_debug) printk(version); /* Initialize the device structure. */ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); memset(dev->priv, 0, sizeof(struct net_local)); dev->open = net_open; dev->stop = net_close; dev->hard_start_xmit = net_send_packet; dev->get_stats = net_get_stats;#ifdef HAVE_MULTICAST dev->set_multicast_list = &set_multicast_list;#endif /* Fill in the fields of the device structure with ethernet-generic values. This should be in a common file instead of per-driver. */ for (i = 0; i < DEV_NUMBUFFS; i++) dev->buffs[i] = NULL; dev->hard_header = eth_header; dev->add_arp = eth_add_arp; dev->queue_xmit = dev_queue_xmit; dev->rebuild_header = eth_rebuild_header; dev->type_trans = eth_type_trans; dev->type = ARPHRD_ETHER; dev->hard_header_len = ETH_HLEN; dev->mtu = 1500; /* eth_mtu */ dev->addr_len = ETH_ALEN; for (i = 0; i < ETH_ALEN; i++) { dev->broadcast[i]=0xff; } /* New-style flags. */ dev->flags = IFF_BROADCAST; dev->family = AF_INET; dev->pa_addr = 0; dev->pa_brdaddr = 0; dev->pa_mask = 0; dev->pa_alen = sizeof(unsigned long); return 0;}static int read_eeprom(int ioaddr, int location){ int i; unsigned short retval = 0; short ee_addr = ioaddr + EEPROM_Ctrl; short ee_daddr = ioaddr + EEPROM_Data; int read_cmd = location | EE_READ_CMD; short ctrl_val = EE_CS; outb(ctrl_val, ee_addr); /* Shift the read command bits out. */ for (i = 9; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outb(dataval, ee_daddr); outb(EE_CS | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */ eeprom_delay(); outb(EE_CS, ee_addr); /* Finish EEPROM a clock tick. */ eeprom_delay(); } outb(EE_CS, ee_addr); for (i = 16; i > 0; i--) { outb(EE_CS | EE_SHIFT_CLK, ee_addr); eeprom_delay(); retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0); outb(EE_CS, ee_addr); eeprom_delay(); } /* Terminate the EEPROM access. */ ctrl_val &= ~EE_CS; outb(ctrl_val | EE_SHIFT_CLK, ee_addr); eeprom_delay(); outb(ctrl_val, ee_addr); eeprom_delay(); return retval;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -