@@ -40,6+40,7 @@ static const char *version = info that the casual reader might think that it documents the i82586 :-<.
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -49,6+50,7 @@ static const char *version = #include <linux/ioport.h>
#include <linux/in.h>
#include <linux/string.h>
+#include <linux/spinlock.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
@@ -59,6+61,7 @@ static const char *version = #include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/malloc.h>
+#include <linux/init.h>
/* use 0 for production, 1 for verification, 2..7 for debug */
@@ -68,7+71,7 @@ static const char *version = static unsigned int net_debug = NET_DEBUG;
/* A zero-terminated list of common I/O addresses to be probed. */
-static unsigned int netcard_portlist[] =
+static unsigned int netcard_portlist[] __initdata =
{ 0x300, 0x320, 0x340, 0x280, 0};
/*
@@ -115,13+118,14 @@ enum commands {
/* Information that need to be kept for each board. */
struct net_local {
- struct enet_statistics stats;
+ struct net_device_stats stats;
int last_restart;
ushort rx_head;
ushort rx_tail;
ushort tx_head;
ushort tx_cmd_link;
ushort tx_reap;
+ spinlock_t lock;
};
/*
@@ -229,7+233,7 @@ struct net_local {
*/
-unsigned short init_words[] = {
+static unsigned short init_words[] = {
/* System Configuration Pointer (SCP). */
0x0000, /* Set bus size to 16 bits. */
0,0, /* pad words. */
@@ -275,18+279,18 @@ unsigned short init_words[] = {
/* Index to functions, as function prototypes. */
-extern int el16_probe(struct device *dev); /* Called from Space.c */
+extern int el16_probe(struct net_device *dev); /* Called from Space.c */
-static int el16_probe1(struct device *dev, int ioaddr);
-static int el16_open(struct device *dev);
-static int el16_send_packet(struct sk_buff *skb, struct device *dev);
+static int el16_probe1(struct net_device *dev, int ioaddr);
+static int el16_open(struct net_device *dev);
+static int el16_send_packet(struct sk_buff *skb, struct net_device *dev);
static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static void el16_rx(struct device *dev);
-static int el16_close(struct device *dev);
-static struct enet_statistics *el16_get_stats(struct device *dev);
+static void el16_rx(struct net_device *dev);
+static int el16_close(struct net_device *dev);
+static struct net_device_stats *el16_get_stats(struct net_device *dev);
-static void hardware_send_packet(struct device *dev, void *buf, short length);
-void init_82586_mem(struct device *dev);
+static void hardware_send_packet(struct net_device *dev, void *buf, short length);
+static void init_82586_mem(struct net_device *dev);
\f
#ifdef HAVE_DEVLIST
@@ -300,8+304,8 @@ struct netdev_entry netcard_drv = If dev->base_addr == 2, (detachable devices only) allocate space for the
device and return success.
*/
-int
-el16_probe(struct device *dev)
+
+int __init el16_probe(struct net_device *dev)
{
int base_addr = dev ? dev->base_addr : 0;
int i;
@@ -322,7+326,7 @@ el16_probe(struct device *dev) return ENODEV;
}
-int el16_probe1(struct device *dev, int ioaddr)
+int __init el16_probe1(struct net_device *dev, int ioaddr)
{
static unsigned char init_ID_done = 0, version_printed = 0;
int i, irq, irqval;
@@ -361,7+365,7 @@ int el16_probe1(struct device *dev, int ioaddr)
irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
- irqval = request_irq(irq, &el16_interrupt, 0, "3c507", NULL);
+ irqval = request_irq(irq, &el16_interrupt, 0, "3c507", dev);
if (irqval) {
printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval);
return EAGAIN;
@@ -428,13+432,8 @@ int el16_probe1(struct device *dev, int ioaddr) return 0;
}
-\f
-
-static int
-el16_open(struct device *dev)
+static int el16_open(struct net_device *dev)
{
- irq2dev_map[dev->irq] = dev;
-
/* Initialize the 82586 memory and start it. */
init_82586_mem(dev);
@@ -447,14+446,15 @@ el16_open(struct device *dev) return 0;
}
-static int
-el16_send_packet(struct sk_buff *skb, struct device *dev)
+static int el16_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
- short *shmem = (short*)dev->mem_start;
+ unsigned long shmem = dev->mem_start;
+ unsigned long flags;
- if (dev->tbusy) {
+ if (dev->tbusy)
+ {
/* If we get here, some higher level has decided we are broken.
There should really be a "kick me" function call instead. */
int tickssofar = jiffies - dev->trans_start;
@@ -462,7+462,7 @@ el16_send_packet(struct sk_buff *skb, struct device *dev) return 1;
if (net_debug > 1)
printk("%s: transmit timed out, %s? ", dev->name,
- shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" :
+ readw(shmem+iSCB_STATUS) & 0x8000 ? "IRQ conflict" :
"network cable problem");
/* Try to restart the adaptor. */
if (lp->last_restart == lp->stats.tx_packets) {
@@ -472,7+472,7 @@ el16_send_packet(struct sk_buff *skb, struct device *dev) } else {
/* Issue the channel attention signal and hope it "gets better". */
if (net_debug > 1) printk("Kicking board.\n");
- shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START;
+ writew(0xf000|CUC_START|RX_START,shmem+iSCB_CMD);
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
lp->last_restart = lp->stats.tx_packets;
}
@@ -480,58+480,60 @@ el16_send_packet(struct sk_buff *skb, struct device *dev) dev->trans_start = jiffies;
}
- /* If some higher layer thinks we've missed an tx-done interrupt
- we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- itself. */
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
-
/* Block a timer-based transmit from overlapping. */
- if (set_bit(0, (void*)&dev->tbusy) != 0)
+ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
printk("%s: Transmitter access conflict.\n", dev->name);
- else {
+ else
+ {
short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
unsigned char *buf = skb->data;
+ lp->stats.tx_bytes+=length;
/* Disable the 82586's input to the interrupt line. */
outb(0x80, ioaddr + MISC_CTRL);
+#ifdef CONFIG_SMP
+ spin_lock_irqsave(&lp->lock, flags);
hardware_send_packet(dev, buf, length);
+ spin_unlock_irqrestore(&lp->lock, flags);
+#else
+ hardware_send_packet(dev, buf, length);
+#endif
dev->trans_start = jiffies;
/* Enable the 82586 interrupt input. */
outb(0x84, ioaddr + MISC_CTRL);
}
- dev_kfree_skb (skb, FREE_WRITE);
+ dev_kfree_skb (skb);
/* You might need to clean up and record Tx statistics here. */
return 0;
}
-\f
+
/* The typical workload of the driver:
Handle the network interface interrupts. */
-static void
-el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_device *dev = dev_id;
struct net_local *lp;
int ioaddr, status, boguscount = 0;
ushort ack_cmd = 0;
- ushort *shmem;
+ unsigned long shmem;
if (dev == NULL) {
printk ("net_interrupt(): irq %d for unknown device.\n", irq);
return;
}
dev->interrupt = 1;
+
ioaddr = dev->base_addr;
lp = (struct net_local *)dev->priv;
- shmem = ((ushort*)dev->mem_start);
+ shmem = dev->mem_start;
+
+ spin_lock(&lp->lock);
- status = shmem[iSCB_STATUS>>1];
+ status = readw(shmem+iSCB_STATUS);
if (net_debug > 4) {
printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
@@ -542,7+544,7 @@ el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* Reap the Tx packet buffers. */
while (lp->tx_reap != lp->tx_head) {
- unsigned short tx_status = shmem[lp->tx_reap>>1];
+ unsigned short tx_status = readw(shmem+lp->tx_reap);
if (tx_status == 0) {
if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap);
@@ -588,19+590,20 @@ el16_interrupt(int irq, void *dev_id, struct pt_regs *regs) ack_cmd |= CUC_RESUME;
}
- if ((status & 0x0070) != 0x0040 && dev->start) {
- static void init_rx_bufs(struct device *);
+ if ((status & 0x0070) != 0x0040 && dev->start)
+ {
+ static void init_rx_bufs(struct net_device *);
/* The Rx unit is not ready, it must be hung. Restart the receiver by
initializing the rx buffers, and issuing an Rx start command. */
if (net_debug)
printk("%s: Rx unit stopped, status %04x, restarting.\n",
dev->name, status);
init_rx_bufs(dev);
- shmem[iSCB_RFA >> 1] = RX_BUF_START;
+ writew(RX_BUF_START,shmem+iSCB_RFA);
ack_cmd |= RX_START;
}
- shmem[iSCB_CMD>>1] = ack_cmd;
+ writew(ack_cmd,shmem+iSCB_CMD);
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
/* Clear the latched interrupt. */
@@ -608,30+611,27 @@ el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* Enable the 82586's interrupt input. */
outb(0x84, ioaddr + MISC_CTRL);
+ spin_unlock(&lp->lock);
return;
}
-static int
-el16_close(struct device *dev)
+static int el16_close(struct net_device *dev)
{
int ioaddr = dev->base_addr;
- ushort *shmem = (short*)dev->mem_start;
+ unsigned long shmem = dev->mem_start;
dev->tbusy = 1;
dev->start = 0;
/* Flush the Tx and disable Rx. */
- shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND;
+ writew(RX_SUSPEND | CUC_SUSPEND,shmem+iSCB_CMD);
outb(0, ioaddr + SIGNAL_CA);
/* Disable the 82586's input to the interrupt line. */
outb(0x80, ioaddr + MISC_CTRL);
- /* We always physically use the IRQ line, so we don't do free_irq().
- We do remove ourselves from the map. */
-
- irq2dev_map[dev->irq] = 0;
+ /* We always physically use the IRQ line, so we don't do free_irq(). */
/* Update the statistics here. */
@@ -642,8+642,7 @@ el16_close(struct device *dev)
/* Get the current statistics. This may be called with the card open or
closed. */
-static struct enet_statistics *
-el16_get_stats(struct device *dev)
+static struct net_device_stats *el16_get_stats(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
@@ -653,11+652,10 @@ el16_get_stats(struct device *dev) }
/* Initialize the Rx-block list. */
-static void
-init_rx_bufs(struct device *dev)
+static void init_rx_bufs(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- unsigned short *write_ptr;
+ unsigned long write_ptr;
unsigned short SCB_base = SCB_BASE;
int cur_rxbuf = lp->rx_head = RX_BUF_START;
@@ -665,26+663,26 @@ init_rx_bufs(struct device *dev) /* Initialize each Rx frame + data buffer. */
do { /* While there is room for one more. */
- write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf);
-
- *write_ptr++ = 0x0000; /* Status */
- *write_ptr++ = 0x0000; /* Command */
- *write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */
- *write_ptr++ = cur_rxbuf + 22; /* Buffer offset */
- *write_ptr++ = 0x0000; /* Pad for dest addr. */
- *write_ptr++ = 0x0000;
- *write_ptr++ = 0x0000;
- *write_ptr++ = 0x0000; /* Pad for source addr. */
- *write_ptr++ = 0x0000;
- *write_ptr++ = 0x0000;
- *write_ptr++ = 0x0000; /* Pad for protocol. */
-
- *write_ptr++ = 0x0000; /* Buffer: Actual count */
- *write_ptr++ = -1; /* Buffer: Next (none). */
- *write_ptr++ = cur_rxbuf + 0x20 + SCB_base; /* Buffer: Address low */
- *write_ptr++ = 0x0000;
+ write_ptr = dev->mem_start + cur_rxbuf;
+
+ writew(0x0000,write_ptr); /* Status */
+ writew(0x0000,write_ptr+=2); /* Command */
+ writew(cur_rxbuf + RX_BUF_SIZE,write_ptr+=2); /* Link */
+ writew(cur_rxbuf + 22,write_ptr+=2); /* Buffer offset */
+ writew(0x0000,write_ptr+=2); /* Pad for dest addr. */
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2); /* Pad for source addr. */
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2); /* Pad for protocol. */
+
+ writew(0x0000,write_ptr+=2); /* Buffer: Actual count */
+ writew(-1,write_ptr+=2); /* Buffer: Next (none). */
+ writew(cur_rxbuf + 0x20 + SCB_base,write_ptr+=2);/* Buffer: Address low */
+ writew(0x0000,write_ptr+=2);
/* Finally, the number of bytes in the buffer. */
- *write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20;
+ writew(0x8000 + RX_BUF_SIZE-0x20,write_ptr+=2);
lp->rx_tail = cur_rxbuf;
cur_rxbuf += RX_BUF_SIZE;
@@ -692,19+690,16 @@ init_rx_bufs(struct device *dev)
/* Terminate the list by setting the EOL bit, and wrap the pointer to make
the list a ring. */
- write_ptr = (unsigned short *)
- (dev->mem_start + lp->rx_tail + 2);
- *write_ptr++ = 0xC000; /* Command, mark as last. */
- *write_ptr++ = lp->rx_head; /* Link */
-
+ write_ptr = dev->mem_start + lp->rx_tail + 2;
+ writew(0xC000,write_ptr); /* Command, mark as last. */
+ writew(lp->rx_head,write_ptr+2); /* Link */
}
-void
-init_82586_mem(struct device *dev)
+static void init_82586_mem(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
- ushort *shmem = (short*)dev->mem_start;
+ unsigned long shmem = dev->mem_start;
/* Enable loopback to protect the wire while starting up,
and hold the 586 in reset during the memory initialization. */
@@ -715,13+710,13 @@ init_82586_mem(struct device *dev) init_words[7] = SCB_BASE;
/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
- memcpy((void*)dev->mem_end-10, init_words, 10);
+ memcpy_toio(dev->mem_end-10, init_words, 10);
/* Write the words at 0x0000. */
- memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10);
+ memcpy_toio(dev->mem_start, init_words + 5, sizeof(init_words) - 10);
/* Fill in the station address. */
- memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr,
+ memcpy_toio(dev->mem_start+SA_OFFSET, dev->dev_addr,
sizeof(dev->dev_addr));
/* The Tx-block list is written as needed. We just set up the values. */
@@ -739,11+734,11 @@ init_82586_mem(struct device *dev)
{
int boguscnt = 50;
- while (shmem[iSCB_STATUS>>1] == 0)
+ while (readw(shmem+iSCB_STATUS) == 0)
if (--boguscnt == 0) {
printk("%s: i82586 initialization timed out with status %04x,"
"cmd %04x.\n", dev->name,
- shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
+ readw(shmem+iSCB_STATUS), readw(shmem+iSCB_CMD));
break;
}
/* Issue channel-attn -- the 82586 won't start. */
@@ -754,40+749,39 @@ init_82586_mem(struct device *dev) outb(0x84, ioaddr + MISC_CTRL);
if (net_debug > 4)
printk("%s: Initialized 82586, status %04x.\n", dev->name,
- shmem[iSCB_STATUS>>1]);
+ readw(shmem+iSCB_STATUS));
return;
}
-static void
-hardware_send_packet(struct device *dev, void *buf, short length)
+static void hardware_send_packet(struct net_device *dev, void *buf, short length)
{
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
ushort tx_block = lp->tx_head;
- ushort *write_ptr = (ushort *)(dev->mem_start + tx_block);
+ unsigned long write_ptr = dev->mem_start + tx_block;
/* Set the write pointer to the Tx block, and put out the header. */
- *write_ptr++ = 0x0000; /* Tx status */
- *write_ptr++ = CMD_INTR|CmdTx; /* Tx command */
- *write_ptr++ = tx_block+16; /* Next command is a NoOp. */
- *write_ptr++ = tx_block+8; /* Data Buffer offset. */
+ writew(0x0000,write_ptr); /* Tx status */
+ writew(CMD_INTR|CmdTx,write_ptr+=2); /* Tx command */
+ writew(tx_block+16,write_ptr+=2); /* Next command is a NoOp. */
+ writew(tx_block+8,write_ptr+=2); /* Data Buffer offset. */
/* Output the data buffer descriptor. */
- *write_ptr++ = length | 0x8000; /* Byte count parameter. */
- *write_ptr++ = -1; /* No next data buffer. */
- *write_ptr++ = tx_block+22+SCB_BASE;/* Buffer follows the NoOp command. */
- *write_ptr++ = 0x0000; /* Buffer address high bits (always zero). */
+ writew(length | 0x8000,write_ptr+=2); /* Byte count parameter. */
+ writew(-1,write_ptr+=2); /* No next data buffer. */
+ writew(tx_block+22+SCB_BASE,write_ptr+=2); /* Buffer follows the NoOp command. */
+ writew(0x0000,write_ptr+=2); /* Buffer address high bits (always zero). */
/* Output the Loop-back NoOp command. */
- *write_ptr++ = 0x0000; /* Tx status */
- *write_ptr++ = CmdNOp; /* Tx command */
- *write_ptr++ = tx_block+16; /* Next is myself. */
+ writew(0x0000,write_ptr+=2); /* Tx status */
+ writew(CmdNOp,write_ptr+=2); /* Tx command */
+ writew(tx_block+16,write_ptr+=2); /* Next is myself. */
/* Output the packet at the write pointer. */
- memcpy(write_ptr, buf, length);
+ memcpy_toio(write_ptr+2, buf, length);
/* Set the old command link pointing to this send packet. */
- *(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block;
+ writew(tx_block,dev->mem_start + lp->tx_cmd_link);
lp->tx_cmd_link = tx_block + 20;
/* Set the next free tx region. */
@@ -804,23+798,22 @@ hardware_send_packet(struct device *dev, void *buf, short length) dev->tbusy = 0;
}
-static void
-el16_rx(struct device *dev)
+static void el16_rx(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- short *shmem = (short*)dev->mem_start;
+ unsigned long shmem = dev->mem_start;
ushort rx_head = lp->rx_head;
ushort rx_tail = lp->rx_tail;
ushort boguscount = 10;
short frame_status;
- while ((frame_status = shmem[rx_head>>1]) < 0) { /* Command complete */
- ushort *read_frame = (short *)(dev->mem_start + rx_head);
- ushort rfd_cmd = read_frame[1];
- ushort next_rx_frame = read_frame[2];
- ushort data_buffer_addr = read_frame[3];
- ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr);
- ushort pkt_len = data_frame[0];
+ while ((frame_status = readw(shmem+rx_head)) < 0) { /* Command complete */
+ unsigned long read_frame = dev->mem_start + rx_head;
+ ushort rfd_cmd = readw(read_frame+2);
+ ushort next_rx_frame = readw(read_frame+4);
+ ushort data_buffer_addr = readw(read_frame+6);
+ unsigned long data_frame = dev->mem_start + data_buffer_addr;
+ ushort pkt_len = readw(data_frame);
if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
|| (pkt_len & 0xC000) != 0xC000) {
@@ -852,7+845,7 @@ el16_rx(struct device *dev) skb->dev = dev;
/* 'skb->data' points to the start of sk_buff data area. */
- memcpy(skb_put(skb,pkt_len), data_frame + 5, pkt_len);
+ memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len);
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
@@ -860,10+853,10 @@ el16_rx(struct device *dev) }
/* Clear the status word and set End-of-List on the rx frame. */
- read_frame[0] = 0;
- read_frame[1] = 0xC000;
+ writew(0,read_frame);
+ writew(0xC000,read_frame+2);
/* Clear the end-of-list on the prev. RFD. */
- *(short*)(dev->mem_start + rx_tail + 2) = 0x0000;
+ writew(0x0000,dev->mem_start + rx_tail + 2);
rx_tail = rx_head;
rx_head = next_rx_frame;
@@ -876,7+869,7 @@ el16_rx(struct device *dev) }
#ifdef MODULE
static char devicename[9] = { 0, };
-static struct device dev_3c507 = {
+static struct net_device dev_3c507 = {
devicename, /* device name is inserted by linux/drivers/net/net_init.c */
0, 0, 0, 0,
0, 0,
@@ -909,7+902,7 @@ cleanup_module(void) dev_3c507.priv = NULL;
/* If we don't do this, we can't re-insmod it later. */
- free_irq(dev_3c507.irq, NULL);
+ free_irq(dev_3c507.irq, &dev_3c507);
release_region(dev_3c507.base_addr, EL16_IO_EXTENT);
}
#endif /* MODULE */