/* A zero-terminated list of I/O addresses to be probed. */ staticunsignedint wd_portlist[] __initdata =
{0x300, 0x280, 0x380, 0x240, 0};
staticint wd_probe1(struct net_device *dev, int ioaddr);
staticint wd_open(struct net_device *dev); staticvoid wd_reset_8390(struct net_device *dev); staticvoid wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page); staticvoid wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset); staticvoid wd_block_output(struct net_device *dev, int count, constunsignedchar *buf, int start_page); staticint wd_close(struct net_device *dev);
static u32 wd_msg_enable;
#define WD_START_PG 0x00 /* First page of TX buffer */ #define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */ #define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
#define WD_CMDREG 0/* Offset to ASIC command register. */ #define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */ #define WD_MEMENB 0x40 /* Enable the shared memory. */ #define WD_CMDREG5 5/* Offset to 16-bit-only ASIC register 5. */ #define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */ #define NIC16 0x40 /* Enable 16 bit access from the 8390. */ #define WD_NIC_OFFSET 16/* Offset to the 8390 from the base_addr. */ #define WD_IO_EXTENT 32
/* Probe for the WD8003 and WD8013. These cards have the station addressPROMatI/Oports<base>+8to<base>+13,withachecksum following.ASoundblastercanhavethesamechecksumasanWDethercard, sowehaveanextraexclusionarycheckforit.
Thewd_probe1()routineinitializesthecardandfillsthe
station address field. */
staticint __init do_wd_probe(struct net_device *dev)
{ int i; struct resource *r; int base_addr = dev->base_addr; int irq = dev->irq; int mem_start = dev->mem_start; int mem_end = dev->mem_end;
if (base_addr > 0x1ff) { /* Check a user specified location. */
r = request_region(base_addr, WD_IO_EXTENT, "wd-probe"); if ( r == NULL) return -EBUSY;
i = wd_probe1(dev, base_addr); if (i != 0)
release_region(base_addr, WD_IO_EXTENT); else
r->name = dev->name; return i;
} elseif (base_addr != 0) /* Don't probe at all. */ return -ENXIO;
for (i = 0; wd_portlist[i]; i++) { int ioaddr = wd_portlist[i];
r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe"); if (r == NULL) continue; if (wd_probe1(dev, ioaddr) == 0) {
r->name = dev->name; return0;
}
release_region(ioaddr, WD_IO_EXTENT);
dev->irq = irq;
dev->mem_start = mem_start;
dev->mem_end = mem_end;
}
staticint __init wd_probe1(struct net_device *dev, int ioaddr)
{ int i; int err; int checksum = 0; int ancient = 0; /* An old card without config registers. */ int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
u8 addr[ETH_ALEN]; constchar *model_name; staticunsigned version_printed; struct ei_device *ei_local = netdev_priv(dev);
for (i = 0; i < 8; i++)
checksum += inb(ioaddr + 8 + i); if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */
|| inb(ioaddr + 9) == 0xff
|| (checksum & 0xff) != 0xFF) return -ENODEV;
/* Check for semi-valid mem_start/end values if supplied. */ if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
netdev_warn(dev, "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
dev->mem_start = 0;
dev->mem_end = 0;
}
if ((wd_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
netdev_info(dev, version);
for (i = 0; i < 6; i++)
addr[i] = inb(ioaddr + 8 + i);
eth_hw_addr_set(dev, addr);
netdev_info(dev, "WD80x3 at %#3x, %pM", ioaddr, dev->dev_addr);
/* The following PureData probe code was contributed by MikeJagdis<jaggy@purplet.demon.co.uk>.Puredatadoessoftware configurationdifferentlyfromotherssowehavetocheckforthem. Thisdetectsan8bit,16bitordumb(Toshiba,jumpered)card.
*/ if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') { unsignedchar reg5 = inb(ioaddr+5);
switch (inb(ioaddr+2)) { case0x03: word16 = 0; model_name = "PDI8023-8"; break; case0x05: word16 = 0; model_name = "PDUC8023"; break; case0x0a: word16 = 1; model_name = "PDI8023-16"; break; /* Either 0x01 (dumb) or they've released a new version. */ default: word16 = 0; model_name = "PDI8023"; break;
}
dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
} else { /* End of PureData probe */ /* This method of checking for a 16-bit board is borrowed from the we.cdriver.AsimplermethodisjusttolookinASICreg.0x03. I'mcomparingthetwomethodinalphatesttomakecertainthey
return the same result. */ /* Check for the old 8 bit board - it has register 0/8 aliasing.
Do NOT check i>=6 here -- it hangs the old 8003 boards! */ for (i = 0; i < 6; i++) if (inb(ioaddr+i) != inb(ioaddr+8+i)) break; if (i >= 6) {
ancient = 1;
model_name = "WD8003-old";
word16 = 0;
} else { int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */ if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
&& (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */ int asic_reg5 = inb(ioaddr+WD_CMDREG5); /* Magic to set ASIC to word-wide mode. */
outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
outb(tmp, ioaddr+1);
model_name = "WD8013";
word16 = 1; /* We have a 16bit board here! */
} else {
model_name = "WD8003";
word16 = 0;
}
outb(tmp, ioaddr+1); /* Restore original reg1 value. */
} #ifndef final_version if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
pr_cont("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
word16 ? 16 : 8,
(inb(ioaddr+1) & 0x01) ? 16 : 8); #endif
}
#ifdefined(WD_SHMEM) && WD_SHMEM > 0x80000 /* Allow a compile-time override. */
dev->mem_start = WD_SHMEM; #else if (dev->mem_start == 0) { /* Sanity and old 8003 check */ int reg0 = inb(ioaddr); if (reg0 == 0xff || reg0 == 0) { /* Future plan: this could check a few likely locations first. */
dev->mem_start = 0xd0000;
pr_cont(" assigning address %#lx", dev->mem_start);
} else { int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f; /* Some boards don't have the register 5 -- it returns 0xff. */ if (high_addr_bits == 0x1f || word16 == 0)
high_addr_bits = 0x01;
dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
}
} #endif
/* The 8390 isn't at the base address -- the ASIC regs are there! */
dev->base_addr = ioaddr+WD_NIC_OFFSET;
if (dev->irq < 2) { staticconstint irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4}; int reg1 = inb(ioaddr+1); int reg4 = inb(ioaddr+4); if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */ short nic_addr = ioaddr+WD_NIC_OFFSET; unsignedlong irq_mask;
/* We have an old-style ethercard that doesn't report its IRQ line.DoautoirqtofindtheIRQline.NotethatthisISNOT
a reliable way to trigger an interrupt. */
outb_p(E8390_NODMA + E8390_STOP, nic_addr);
outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev); if (i) {
pr_cont(" unable to get IRQ %d.\n", dev->irq); return i;
}
/* OK, were are certain this is going to work. Setup the device. */
ei_status.name = model_name;
ei_status.word16 = word16;
ei_status.tx_start_page = WD_START_PG;
ei_status.rx_start_page = WD_START_PG + TX_PAGES;
/* Don't map in the shared memory until the board is actually opened. */
/* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */ if (dev->mem_end != 0) {
ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
ei_status.priv = dev->mem_end - dev->mem_start;
} else {
ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
ei_status.priv = (ei_status.stop_page - WD_START_PG)*256;
}
/* Map in the shared memory. Always set register 0 last to remain
compatible with very old boards. */
ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
if (ei_status.word16)
outb(ei_status.reg5, ioaddr+WD_CMDREG5);
outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
/* Set up the ASIC registers, just in case something changed them. */
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); if (ei_status.word16)
outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
netif_dbg(ei_local, hw, dev, "reset done\n");
}
/* Grab the 8390 specific header. Similar to the block_input routine, but wedon'tneedtobeconcernedwithringwrapastheheaderwillbeat
the start of a page, so we optimize accordingly. */
staticvoid
wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
{
/* We'll always get a 4 byte header read followed by a packet read, so
we enable 16 bit mode before the header, and disable after the body. */ if (ei_status.word16)
outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
#ifdef __BIG_ENDIAN /* Officially this is what we are doing, but the readl() is faster */ /* unfortunately it isn't endian aware of the struct */
memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
hdr->count = le16_to_cpu(hdr->count); #else
((unsignedint*)hdr)[0] = readl(hdr_start); #endif
}
/* Block input and output are easy on shared memory ethercards, and trivial ontheWesterndigitalcardwherethereisnochoiceofhowtodoit. Theonlycomplicationsarethattheringbufferwraps,andneedtomap
switch between 8- and 16-bit modes. */
if (offset + count > ei_status.priv) { /* We must wrap the input move. */ int semi_count = ei_status.priv - offset;
memcpy_fromio(skb->data, xfer_start, semi_count);
count -= semi_count;
memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
} else { /* Packet is in one chunk -- we can copy + cksum. */
memcpy_fromio(skb->data, xfer_start, count);
}
/* Turn off 16 bit access so that reboot works. ISA brain-damage */ if (ei_status.word16)
outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
}
staticvoid
wd_block_output(struct net_device *dev, int count, constunsignedchar *buf, int start_page)
{ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8);
if (ei_status.word16) { /* Turn on and off 16 bit access so that reboot works. */
outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
memcpy_toio(shmem, buf, count);
outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
} else
memcpy_toio(shmem, buf, count);
}
netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n");
ei_close(dev);
/* Change from 16-bit to 8-bit shared memory so reboot works. */ if (ei_status.word16)
outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
/* And disable the shared memory. */
outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
return0;
}
#ifdef MODULE #define MAX_WD_CARDS 4/* Max number of wd cards per module */ staticstruct net_device *dev_wd[MAX_WD_CARDS]; staticint io[MAX_WD_CARDS]; staticint irq[MAX_WD_CARDS]; staticint mem[MAX_WD_CARDS]; staticint mem_end[MAX_WD_CARDS]; /* for non std. mem size */
module_param_hw_array(io, int, ioport, NULL, 0);
module_param_hw_array(irq, int, irq, NULL, 0);
module_param_hw_array(mem, int, iomem, NULL, 0);
module_param_hw_array(mem_end, int, iomem, NULL, 0);
module_param_named(msg_enable, wd_msg_enable, uint, 0444);
MODULE_PARM_DESC(io, "I/O base address(es)");
MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)");
MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)");
MODULE_PARM_DESC(mem_end, "memory end address(es)");
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver");
MODULE_LICENSE("GPL");
/* This is set up so that only a single autoprobe takes place per call.
ISA device autoprobes on a running machine are not recommended. */
staticint __init wd_init_module(void)
{ struct net_device *dev; int this_dev, found = 0;
for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { if (io[this_dev] == 0) { if (this_dev != 0) break; /* only autoprobe 1st one */
printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
}
dev = alloc_ei_netdev(); if (!dev) break;
dev->irq = irq[this_dev];
dev->base_addr = io[this_dev];
dev->mem_start = mem[this_dev];
dev->mem_end = mem_end[this_dev]; if (do_wd_probe(dev) == 0) {
dev_wd[found++] = dev; continue;
}
free_netdev(dev);
printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]); break;
} if (found) return0; return -ENXIO;
}
module_init(wd_init_module);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.