Annotation of Gnu-Mach/linux/src/drivers/net/wd.c, revision 1.1.1.1

1.1       root        1: /* wd.c: A WD80x3 ethernet driver for linux. */
                      2: /*
                      3:        Written 1993-94 by Donald Becker.
                      4: 
                      5:        Copyright 1993 United States Government as represented by the
                      6:        Director, National Security Agency.
                      7: 
                      8:        This software may be used and distributed according to the terms
                      9:        of the GNU Public License, incorporated herein by reference.
                     10: 
                     11:        The author may be reached as [email protected], or C/O
                     12:        Center of Excellence in Space Data and Information Sciences
                     13:           Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
                     14: 
                     15:        This is a driver for WD8003 and WD8013 "compatible" ethercards.
                     16: 
                     17:        Thanks to Russ Nelson ([email protected]) for loaning me a WD8013.
                     18: 
                     19:        Changelog:
                     20: 
                     21:        Paul Gortmaker  : multiple card support for module users, support
                     22:                          for non-standard memory sizes.
                     23:                         
                     24: 
                     25: */
                     26: 
                     27: static const char *version =
                     28:        "wd.c:v1.10 9/23/94 Donald Becker ([email protected])\n";
                     29: 
                     30: #include <linux/module.h>
                     31: 
                     32: #include <linux/kernel.h>
                     33: #include <linux/sched.h>
                     34: #include <linux/errno.h>
                     35: #include <linux/string.h>
                     36: #include <asm/io.h>
                     37: #include <asm/system.h>
                     38: 
                     39: #include <linux/netdevice.h>
                     40: #include <linux/etherdevice.h>
                     41: #include "8390.h"
                     42: 
                     43: /* A zero-terminated list of I/O addresses to be probed. */
                     44: static unsigned int wd_portlist[] =
                     45: {0x300, 0x280, 0x380, 0x240, 0};
                     46: 
                     47: int wd_probe(struct device *dev);
                     48: int wd_probe1(struct device *dev, int ioaddr);
                     49: 
                     50: static int wd_open(struct device *dev);
                     51: static void wd_reset_8390(struct device *dev);
                     52: static void wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
                     53:                                                int ring_page);
                     54: static void wd_block_input(struct device *dev, int count,
                     55:                                                  struct sk_buff *skb, int ring_offset);
                     56: static void wd_block_output(struct device *dev, int count,
                     57:                                                        const unsigned char *buf, const int start_page);
                     58: static int wd_close_card(struct device *dev);
                     59: 
                     60: 
                     61: #define WD_START_PG            0x00    /* First page of TX buffer */
                     62: #define WD03_STOP_PG   0x20    /* Last page +1 of RX ring */
                     63: #define WD13_STOP_PG   0x40    /* Last page +1 of RX ring */
                     64: 
                     65: #define WD_CMDREG              0               /* Offset to ASIC command register. */
                     66: #define         WD_RESET               0x80    /* Board reset, in WD_CMDREG. */
                     67: #define         WD_MEMENB              0x40    /* Enable the shared memory. */
                     68: #define WD_CMDREG5             5               /* Offset to 16-bit-only ASIC register 5. */
                     69: #define         ISA16                  0x80    /* Enable 16 bit access from the ISA bus. */
                     70: #define         NIC16                  0x40    /* Enable 16 bit access from the 8390. */
                     71: #define WD_NIC_OFFSET  16              /* Offset to the 8390 from the base_addr. */
                     72: #define WD_IO_EXTENT   32
                     73: 
                     74: 
                     75: /*     Probe for the WD8003 and WD8013.  These cards have the station
                     76:        address PROM at I/O ports <base>+8 to <base>+13, with a checksum
                     77:        following. A Soundblaster can have the same checksum as an WDethercard,
                     78:        so we have an extra exclusionary check for it.
                     79: 
                     80:        The wd_probe1() routine initializes the card and fills the
                     81:        station address field. */
                     82: 
                     83: #ifdef HAVE_DEVLIST
                     84: struct netdev_entry wd_drv =
                     85: {"wd", wd_probe1, WD_IO_EXTENT, wd_portlist};
                     86: #else
                     87: 
                     88: int wd_probe(struct device *dev)
                     89: {
                     90:        int i;
                     91:        int base_addr = dev ? dev->base_addr : 0;
                     92: 
                     93:        if (base_addr > 0x1ff)          /* Check a single specified location. */
                     94:                return wd_probe1(dev, base_addr);
                     95:        else if (base_addr != 0)        /* Don't probe at all. */
                     96:                return ENXIO;
                     97: 
                     98:        for (i = 0; wd_portlist[i]; i++) {
                     99:                int ioaddr = wd_portlist[i];
                    100:                if (check_region(ioaddr, WD_IO_EXTENT))
                    101:                        continue;
                    102:                if (wd_probe1(dev, ioaddr) == 0)
                    103:                        return 0;
                    104:        }
                    105: 
                    106:        return ENODEV;
                    107: }
                    108: #endif
                    109: 
                    110: int wd_probe1(struct device *dev, int ioaddr)
                    111: {
                    112:        int i;
                    113:        int checksum = 0;
                    114:        int ancient = 0;                        /* An old card without config registers. */
                    115:        int word16 = 0;                         /* 0 = 8 bit, 1 = 16 bit */
                    116:        const char *model_name;
                    117:        static unsigned version_printed = 0;
                    118: 
                    119:        for (i = 0; i < 8; i++)
                    120:                checksum += inb(ioaddr + 8 + i);
                    121:        if (inb(ioaddr + 8) == 0xff     /* Extra check to avoid soundcard. */
                    122:                || inb(ioaddr + 9) == 0xff
                    123:                || (checksum & 0xff) != 0xFF)
                    124:                return ENODEV;
                    125: 
                    126:        /* We should have a "dev" from Space.c or the static module table. */
                    127:        if (dev == NULL) {
                    128:                printk("wd.c: Passed a NULL device.\n");
                    129:                dev = init_etherdev(0, 0);
                    130:        }
                    131: 
                    132:        /* Check for semi-valid mem_start/end values if supplied. */
                    133:        if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
                    134:                printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
                    135:                dev->mem_start = 0;
                    136:                dev->mem_end = 0;
                    137:        }
                    138: 
                    139:        if (ei_debug  &&  version_printed++ == 0)
                    140:                printk(version);
                    141: 
                    142:        printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
                    143:        for (i = 0; i < 6; i++)
                    144:                printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
                    145: 
                    146:        /* The following PureData probe code was contributed by
                    147:           Mike Jagdis <[email protected]>. Puredata does software
                    148:           configuration differently from others so we have to check for them.
                    149:           This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
                    150:           */
                    151:        if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
                    152:                unsigned char reg5 = inb(ioaddr+5);
                    153: 
                    154:                switch (inb(ioaddr+2)) {
                    155:                case 0x03: word16 = 0; model_name = "PDI8023-8";        break;
                    156:                case 0x05: word16 = 0; model_name = "PDUC8023"; break;
                    157:                case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
                    158:                        /* Either 0x01 (dumb) or they've released a new version. */
                    159:                default:         word16 = 0; model_name = "PDI8023";    break;
                    160:                }
                    161:                dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
                    162:                dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
                    163:        } else {                                                                /* End of PureData probe */
                    164:                /* This method of checking for a 16-bit board is borrowed from the
                    165:                   we.c driver.  A simpler method is just to look in ASIC reg. 0x03.
                    166:                   I'm comparing the two method in alpha test to make certain they
                    167:                   return the same result. */
                    168:                /* Check for the old 8 bit board - it has register 0/8 aliasing.
                    169:                   Do NOT check i>=6 here -- it hangs the old 8003 boards! */
                    170:                for (i = 0; i < 6; i++)
                    171:                        if (inb(ioaddr+i) != inb(ioaddr+8+i))
                    172:                                break;
                    173:                if (i >= 6) {
                    174:                        ancient = 1;
                    175:                        model_name = "WD8003-old";
                    176:                        word16 = 0;
                    177:                } else {
                    178:                        int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
                    179:                        outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
                    180:                        if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
                    181:                                && (tmp & 0x01) == 0x01 ) {                             /* In a 16 slot. */
                    182:                                int asic_reg5 = inb(ioaddr+WD_CMDREG5);
                    183:                                /* Magic to set ASIC to word-wide mode. */
                    184:                                outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
                    185:                                outb(tmp, ioaddr+1);
                    186:                                model_name = "WD8013";
                    187:                                word16 = 1;             /* We have a 16bit board here! */
                    188:                        } else {
                    189:                                model_name = "WD8003";
                    190:                                word16 = 0;
                    191:                        }
                    192:                        outb(tmp, ioaddr+1);                    /* Restore original reg1 value. */
                    193:                }
                    194: #ifndef final_version
                    195:                if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
                    196:                        printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
                    197:                                   word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
                    198: #endif
                    199:        }
                    200: 
                    201: #if defined(WD_SHMEM) && WD_SHMEM > 0x80000
                    202:        /* Allow a compile-time override.        */
                    203:        dev->mem_start = WD_SHMEM;
                    204: #else
                    205:        if (dev->mem_start == 0) {
                    206:                /* Sanity and old 8003 check */
                    207:                int reg0 = inb(ioaddr);
                    208:                if (reg0 == 0xff || reg0 == 0) {
                    209:                        /* Future plan: this could check a few likely locations first. */
                    210:                        dev->mem_start = 0xd0000;
                    211:                        printk(" assigning address %#lx", dev->mem_start);
                    212:                } else {
                    213:                        int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
                    214:                        /* Some boards don't have the register 5 -- it returns 0xff. */
                    215:                        if (high_addr_bits == 0x1f || word16 == 0)
                    216:                                high_addr_bits = 0x01;
                    217:                        dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
                    218:                }
                    219:        }
                    220: #endif
                    221: 
                    222:        /* The 8390 isn't at the base address -- the ASIC regs are there! */
                    223:        dev->base_addr = ioaddr+WD_NIC_OFFSET;
                    224: 
                    225:        if (dev->irq < 2) {
                    226:                int irqmap[] = {9,3,5,7,10,11,15,4};
                    227:                int reg1 = inb(ioaddr+1);
                    228:                int reg4 = inb(ioaddr+4);
                    229:                if (ancient || reg1 == 0xff) {  /* Ack!! No way to read the IRQ! */
                    230:                        short nic_addr = ioaddr+WD_NIC_OFFSET;
                    231: 
                    232:                        /* We have an old-style ethercard that doesn't report its IRQ
                    233:                           line.  Do autoirq to find the IRQ line. Note that this IS NOT
                    234:                           a reliable way to trigger an interrupt. */
                    235:                        outb_p(E8390_NODMA + E8390_STOP, nic_addr);
                    236:                        outb(0x00, nic_addr+EN0_IMR);   /* Disable all intrs. */
                    237:                        autoirq_setup(0);
                    238:                        outb_p(0xff, nic_addr + EN0_IMR);       /* Enable all interrupts. */
                    239:                        outb_p(0x00, nic_addr + EN0_RCNTLO);
                    240:                        outb_p(0x00, nic_addr + EN0_RCNTHI);
                    241:                        outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
                    242:                        dev->irq = autoirq_report(2);
                    243:                        outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */
                    244: 
                    245:                        if (ei_debug > 2)
                    246:                                printk(" autoirq is %d", dev->irq);
                    247:                        if (dev->irq < 2)
                    248:                                dev->irq = word16 ? 10 : 5;
                    249:                } else
                    250:                        dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
                    251:        } else if (dev->irq == 2)               /* Fixup bogosity: IRQ2 is really IRQ9 */
                    252:                dev->irq = 9;
                    253: 
                    254:        /* Snarf the interrupt now.  There's no point in waiting since we cannot
                    255:           share and the board will usually be enabled. */
                    256:        if (request_irq(dev->irq, ei_interrupt, 0, model_name, NULL)) {
                    257:                printk (" unable to get IRQ %d.\n", dev->irq);
                    258:                return EAGAIN;
                    259:        }
                    260: 
                    261:        /* Allocate dev->priv and fill in 8390 specific dev fields. */
                    262:        if (ethdev_init(dev)) { 
                    263:                printk (" unable to get memory for dev->priv.\n");
                    264:                free_irq(dev->irq, NULL);
                    265:                return -ENOMEM;
                    266:        }
                    267: 
                    268:        /* OK, were are certain this is going to work.  Setup the device. */
                    269:        request_region(ioaddr, WD_IO_EXTENT, model_name);
                    270: 
                    271:        ei_status.name = model_name;
                    272:        ei_status.word16 = word16;
                    273:        ei_status.tx_start_page = WD_START_PG;
                    274:        ei_status.rx_start_page = WD_START_PG + TX_PAGES;
                    275: 
                    276:        /* Don't map in the shared memory until the board is actually opened. */
                    277:        dev->rmem_start = dev->mem_start + TX_PAGES*256;
                    278: 
                    279:        /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
                    280:        if (dev->mem_end != 0) {
                    281:                ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
                    282:        } else {
                    283:                ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
                    284:                dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
                    285:        }
                    286:        dev->rmem_end = dev->mem_end;
                    287: 
                    288:        printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
                    289:                   model_name, dev->irq, dev->mem_start, dev->mem_end-1);
                    290: 
                    291:        ei_status.reset_8390 = &wd_reset_8390;
                    292:        ei_status.block_input = &wd_block_input;
                    293:        ei_status.block_output = &wd_block_output;
                    294:        ei_status.get_8390_hdr = &wd_get_8390_hdr;
                    295:        dev->open = &wd_open;
                    296:        dev->stop = &wd_close_card;
                    297:        NS8390_init(dev, 0);
                    298: 
                    299: #if 1
                    300:        /* Enable interrupt generation on softconfig cards -- M.U */
                    301:        /* .. but possibly potentially unsafe - Donald */
                    302:        if (inb(ioaddr+14) & 0x20)
                    303:                outb(inb(ioaddr+4)|0x80, ioaddr+4);
                    304: #endif
                    305: 
                    306:        return 0;
                    307: }
                    308: 
                    309: static int
                    310: wd_open(struct device *dev)
                    311: {
                    312:   int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
                    313: 
                    314:   /* Map in the shared memory. Always set register 0 last to remain
                    315:         compatible with very old boards. */
                    316:   ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
                    317:   ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
                    318: 
                    319:   if (ei_status.word16)
                    320:          outb(ei_status.reg5, ioaddr+WD_CMDREG5);
                    321:   outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
                    322: 
                    323:   ei_open(dev);
                    324:   MOD_INC_USE_COUNT;
                    325:   return 0;
                    326: }
                    327: 
                    328: static void
                    329: wd_reset_8390(struct device *dev)
                    330: {
                    331:        int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
                    332: 
                    333:        outb(WD_RESET, wd_cmd_port);
                    334:        if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies);
                    335:        ei_status.txing = 0;
                    336: 
                    337:        /* Set up the ASIC registers, just in case something changed them. */
                    338:        outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
                    339:        if (ei_status.word16)
                    340:                outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
                    341: 
                    342:        if (ei_debug > 1) printk("reset done\n");
                    343:        return;
                    344: }
                    345: 
                    346: /* Grab the 8390 specific header. Similar to the block_input routine, but
                    347:    we don't need to be concerned with ring wrap as the header will be at
                    348:    the start of a page, so we optimize accordingly. */
                    349: 
                    350: static void
                    351: wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
                    352: {
                    353: 
                    354:        int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
                    355:        unsigned long hdr_start = dev->mem_start + ((ring_page - WD_START_PG)<<8);
                    356: 
                    357:        /* We'll always get a 4 byte header read followed by a packet read, so
                    358:           we enable 16 bit mode before the header, and disable after the body. */
                    359:        if (ei_status.word16)
                    360:                outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
                    361: 
                    362: #ifdef notdef
                    363:        /* Officially this is what we are doing, but the readl() is faster */
                    364:        memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
                    365: #else
                    366:        ((unsigned int*)hdr)[0] = readl(hdr_start);
                    367: #endif
                    368: }
                    369: 
                    370: /* Block input and output are easy on shared memory ethercards, and trivial
                    371:    on the Western digital card where there is no choice of how to do it.
                    372:    The only complications are that the ring buffer wraps, and need to map
                    373:    switch between 8- and 16-bit modes. */
                    374: 
                    375: static void
                    376: wd_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
                    377: {
                    378:        int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
                    379:        unsigned long xfer_start = dev->mem_start + ring_offset - (WD_START_PG<<8);
                    380: 
                    381:        if (xfer_start + count > dev->rmem_end) {
                    382:                /* We must wrap the input move. */
                    383:                int semi_count = dev->rmem_end - xfer_start;
                    384:                memcpy_fromio(skb->data, xfer_start, semi_count);
                    385:                count -= semi_count;
                    386:                memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
                    387:        } else {
                    388:                /* Packet is in one chunk -- we can copy + cksum. */
                    389:                eth_io_copy_and_sum(skb, xfer_start, count, 0);
                    390:        }
                    391: 
                    392:        /* Turn off 16 bit access so that reboot works.  ISA brain-damage */
                    393:        if (ei_status.word16)
                    394:                outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
                    395: }
                    396: 
                    397: static void
                    398: wd_block_output(struct device *dev, int count, const unsigned char *buf,
                    399:                                int start_page)
                    400: {
                    401:        int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
                    402:        long shmem = dev->mem_start + ((start_page - WD_START_PG)<<8);
                    403: 
                    404: 
                    405:        if (ei_status.word16) {
                    406:                /* Turn on and off 16 bit access so that reboot works. */
                    407:                outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
                    408:                memcpy_toio(shmem, buf, count);
                    409:                outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
                    410:        } else
                    411:                memcpy_toio(shmem, buf, count);
                    412: }
                    413: 
                    414: 
                    415: static int
                    416: wd_close_card(struct device *dev)
                    417: {
                    418:        int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
                    419: 
                    420:        if (ei_debug > 1)
                    421:                printk("%s: Shutting down ethercard.\n", dev->name);
                    422:        ei_close(dev);
                    423: 
                    424:        /* Change from 16-bit to 8-bit shared memory so reboot works. */
                    425:        if (ei_status.word16)
                    426:                outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
                    427: 
                    428:        /* And disable the shared memory. */
                    429:        outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
                    430: 
                    431:        MOD_DEC_USE_COUNT;
                    432: 
                    433:        return 0;
                    434: }
                    435: 
                    436: 
                    437: #ifdef MODULE
                    438: #define MAX_WD_CARDS   4       /* Max number of wd cards per module */
                    439: #define NAMELEN                8       /* # of chars for storing dev->name */
                    440: static char namelist[NAMELEN * MAX_WD_CARDS] = { 0, };
                    441: static struct device dev_wd[MAX_WD_CARDS] = {
                    442:        {
                    443:                NULL,           /* assign a chunk of namelist[] below */
                    444:                0, 0, 0, 0,
                    445:                0, 0,
                    446:                0, 0, 0, NULL, NULL
                    447:        },
                    448: };
                    449: 
                    450: static int io[MAX_WD_CARDS] = { 0, };
                    451: static int irq[MAX_WD_CARDS]  = { 0, };
                    452: static int mem[MAX_WD_CARDS] = { 0, };
                    453: static int mem_end[MAX_WD_CARDS] = { 0, };     /* for non std. mem size */
                    454: 
                    455: /* This is set up so that only a single autoprobe takes place per call.
                    456: ISA device autoprobes on a running machine are not recommended. */
                    457: int
                    458: init_module(void)
                    459: {
                    460:        int this_dev, found = 0;
                    461: 
                    462:        for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
                    463:                struct device *dev = &dev_wd[this_dev];
                    464:                dev->name = namelist+(NAMELEN*this_dev);
                    465:                dev->irq = irq[this_dev];
                    466:                dev->base_addr = io[this_dev];
                    467:                dev->mem_start = mem[this_dev];
                    468:                dev->mem_end = mem_end[this_dev];
                    469:                dev->init = wd_probe;
                    470:                if (io[this_dev] == 0)  {
                    471:                        if (this_dev != 0) break; /* only autoprobe 1st one */
                    472:                        printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
                    473:                }
                    474:                if (register_netdev(dev) != 0) {
                    475:                        printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
                    476:                        if (found != 0) return 0;       /* Got at least one. */
                    477:                        return -ENXIO;
                    478:                }
                    479:                found++;
                    480:        }
                    481: 
                    482:        return 0;
                    483: }
                    484: 
                    485: void
                    486: cleanup_module(void)
                    487: {
                    488:        int this_dev;
                    489: 
                    490:        for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
                    491:                struct device *dev = &dev_wd[this_dev];
                    492:                if (dev->priv != NULL) {
                    493:                        int ioaddr = dev->base_addr - WD_NIC_OFFSET;
                    494:                        kfree(dev->priv);
                    495:                        dev->priv = NULL;
                    496:                        free_irq(dev->irq, NULL);
                    497:                        irq2dev_map[dev->irq] = NULL;
                    498:                        release_region(ioaddr, WD_IO_EXTENT);
                    499:                        unregister_netdev(dev);
                    500:                }
                    501:        }
                    502: }
                    503: #endif /* MODULE */
                    504: 
                    505: 
                    506: /*
                    507:  * Local variables:
                    508:  *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c wd.c"
                    509:  *  version-control: t
                    510:  *  tab-width: 4
                    511:  *  kept-new-versions: 5
                    512:  * End:
                    513:  */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.