Annotation of Gnu-Mach/linux/src/drivers/net/wd.c, revision 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.