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