linux/drivers/net/wireless/intersil/hostap/hostap_plx.c
<<
>>
Prefs
   1#define PRISM2_PLX
   2
   3/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is
   4 * based on:
   5 * - Host AP driver patch from james@madingley.org
   6 * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc.
   7 */
   8
   9
  10#include <linux/module.h>
  11#include <linux/if.h>
  12#include <linux/skbuff.h>
  13#include <linux/netdevice.h>
  14#include <linux/slab.h>
  15#include <linux/workqueue.h>
  16#include <linux/wireless.h>
  17#include <net/iw_handler.h>
  18
  19#include <linux/ioport.h>
  20#include <linux/pci.h>
  21#include <asm/io.h>
  22
  23#include "hostap_wlan.h"
  24
  25
  26static char *dev_info = "hostap_plx";
  27
  28
  29MODULE_AUTHOR("Jouni Malinen");
  30MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
  31                   "cards (PLX).");
  32MODULE_LICENSE("GPL");
  33
  34
  35static int ignore_cis;
  36module_param(ignore_cis, int, 0444);
  37MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
  38
  39
  40/* struct local_info::hw_priv */
  41struct hostap_plx_priv {
  42        void __iomem *attr_mem;
  43        unsigned int cor_offset;
  44};
  45
  46
  47#define PLX_MIN_ATTR_LEN 512    /* at least 2 x 256 is needed for CIS */
  48#define COR_SRESET       0x80
  49#define COR_LEVLREQ      0x40
  50#define COR_ENABLE_FUNC  0x01
  51/* PCI Configuration Registers */
  52#define PLX_PCIIPR       0x3d   /* PCI Interrupt Pin */
  53/* Local Configuration Registers */
  54#define PLX_INTCSR       0x4c   /* Interrupt Control/Status Register */
  55#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */
  56#define PLX_CNTRL        0x50
  57#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
  58
  59
  60#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
  61
  62static const struct pci_device_id prism2_plx_id_table[] = {
  63        PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
  64        PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
  65        PLXDEV(0x126c, 0x8030, "Nortel emobility"),
  66        PLXDEV(0x1562, 0x0001, "Symbol LA-4123"),
  67        PLXDEV(0x1385, 0x4100, "Netgear MA301"),
  68        PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
  69        PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
  70        PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
  71        PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"),
  72        PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"),
  73        PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"),
  74        PLXDEV(0x16ab, 0x1103, "Longshine 8031"),
  75        PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"),
  76        PLXDEV(0xec80, 0xec00, "Belkin F5D6000"),
  77        { 0 }
  78};
  79
  80
  81/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid
  82 * is not listed here, you will need to add it here to get the driver
  83 * initialized. */
  84static struct prism2_plx_manfid {
  85        u16 manfid1, manfid2;
  86} prism2_plx_known_manfids[] = {
  87        { 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */,
  88        { 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */,
  89        { 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */,
  90        { 0x0126, 0x8000 } /* Proxim RangeLAN */,
  91        { 0x0138, 0x0002 } /* Compaq WL100 */,
  92        { 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */,
  93        { 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */,
  94        { 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */,
  95        { 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */,
  96        { 0x028a, 0x0002 } /* D-Link DRC-650 */,
  97        { 0x0250, 0x0002 } /* Samsung SWL2000-N */,
  98        { 0xc250, 0x0002 } /* EMTAC A2424i */,
  99        { 0xd601, 0x0002 } /* Z-Com XI300 */,
 100        { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */,
 101        { 0, 0}
 102};
 103
 104
 105#ifdef PRISM2_IO_DEBUG
 106
 107static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
 108{
 109        struct hostap_interface *iface;
 110        local_info_t *local;
 111        unsigned long flags;
 112
 113        iface = netdev_priv(dev);
 114        local = iface->local;
 115
 116        spin_lock_irqsave(&local->lock, flags);
 117        prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
 118        outb(v, dev->base_addr + a);
 119        spin_unlock_irqrestore(&local->lock, flags);
 120}
 121
 122static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
 123{
 124        struct hostap_interface *iface;
 125        local_info_t *local;
 126        unsigned long flags;
 127        u8 v;
 128
 129        iface = netdev_priv(dev);
 130        local = iface->local;
 131
 132        spin_lock_irqsave(&local->lock, flags);
 133        v = inb(dev->base_addr + a);
 134        prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
 135        spin_unlock_irqrestore(&local->lock, flags);
 136        return v;
 137}
 138
 139static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
 140{
 141        struct hostap_interface *iface;
 142        local_info_t *local;
 143        unsigned long flags;
 144
 145        iface = netdev_priv(dev);
 146        local = iface->local;
 147
 148        spin_lock_irqsave(&local->lock, flags);
 149        prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
 150        outw(v, dev->base_addr + a);
 151        spin_unlock_irqrestore(&local->lock, flags);
 152}
 153
 154static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
 155{
 156        struct hostap_interface *iface;
 157        local_info_t *local;
 158        unsigned long flags;
 159        u16 v;
 160
 161        iface = netdev_priv(dev);
 162        local = iface->local;
 163
 164        spin_lock_irqsave(&local->lock, flags);
 165        v = inw(dev->base_addr + a);
 166        prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
 167        spin_unlock_irqrestore(&local->lock, flags);
 168        return v;
 169}
 170
 171static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
 172                                       u8 *buf, int wc)
 173{
 174        struct hostap_interface *iface;
 175        local_info_t *local;
 176        unsigned long flags;
 177
 178        iface = netdev_priv(dev);
 179        local = iface->local;
 180
 181        spin_lock_irqsave(&local->lock, flags);
 182        prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
 183        outsw(dev->base_addr + a, buf, wc);
 184        spin_unlock_irqrestore(&local->lock, flags);
 185}
 186
 187static inline void hfa384x_insw_debug(struct net_device *dev, int a,
 188                                      u8 *buf, int wc)
 189{
 190        struct hostap_interface *iface;
 191        local_info_t *local;
 192        unsigned long flags;
 193
 194        iface = netdev_priv(dev);
 195        local = iface->local;
 196
 197        spin_lock_irqsave(&local->lock, flags);
 198        prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
 199        insw(dev->base_addr + a, buf, wc);
 200        spin_unlock_irqrestore(&local->lock, flags);
 201}
 202
 203#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
 204#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
 205#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
 206#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
 207#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
 208#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
 209
 210#else /* PRISM2_IO_DEBUG */
 211
 212#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
 213#define HFA384X_INB(a) inb(dev->base_addr + (a))
 214#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
 215#define HFA384X_INW(a) inw(dev->base_addr + (a))
 216#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
 217#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
 218
 219#endif /* PRISM2_IO_DEBUG */
 220
 221
 222static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
 223                            int len)
 224{
 225        u16 d_off;
 226        u16 *pos;
 227
 228        d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
 229        pos = (u16 *) buf;
 230
 231        if (len / 2)
 232                HFA384X_INSW(d_off, buf, len / 2);
 233        pos += len / 2;
 234
 235        if (len & 1)
 236                *((char *) pos) = HFA384X_INB(d_off);
 237
 238        return 0;
 239}
 240
 241
 242static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
 243{
 244        u16 d_off;
 245        u16 *pos;
 246
 247        d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
 248        pos = (u16 *) buf;
 249
 250        if (len / 2)
 251                HFA384X_OUTSW(d_off, buf, len / 2);
 252        pos += len / 2;
 253
 254        if (len & 1)
 255                HFA384X_OUTB(*((char *) pos), d_off);
 256
 257        return 0;
 258}
 259
 260
 261/* FIX: This might change at some point.. */
 262#include "hostap_hw.c"
 263
 264
 265static void prism2_plx_cor_sreset(local_info_t *local)
 266{
 267        unsigned char corsave;
 268        struct hostap_plx_priv *hw_priv = local->hw_priv;
 269
 270        printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n",
 271               dev_info);
 272
 273        /* Set sreset bit of COR and clear it after hold time */
 274
 275        if (hw_priv->attr_mem == NULL) {
 276                /* TMD7160 - COR at card's first I/O addr */
 277                corsave = inb(hw_priv->cor_offset);
 278                outb(corsave | COR_SRESET, hw_priv->cor_offset);
 279                mdelay(2);
 280                outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
 281                mdelay(2);
 282        } else {
 283                /* PLX9052 */
 284                corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
 285                writeb(corsave | COR_SRESET,
 286                       hw_priv->attr_mem + hw_priv->cor_offset);
 287                mdelay(2);
 288                writeb(corsave & ~COR_SRESET,
 289                       hw_priv->attr_mem + hw_priv->cor_offset);
 290                mdelay(2);
 291        }
 292}
 293
 294
 295static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
 296{
 297        unsigned char corsave;
 298        struct hostap_plx_priv *hw_priv = local->hw_priv;
 299
 300        if (hw_priv->attr_mem == NULL) {
 301                /* TMD7160 - COR at card's first I/O addr */
 302                corsave = inb(hw_priv->cor_offset);
 303                outb(corsave | COR_SRESET, hw_priv->cor_offset);
 304                mdelay(10);
 305                outb(hcr, hw_priv->cor_offset + 2);
 306                mdelay(10);
 307                outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
 308                mdelay(10);
 309        } else {
 310                /* PLX9052 */
 311                corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
 312                writeb(corsave | COR_SRESET,
 313                       hw_priv->attr_mem + hw_priv->cor_offset);
 314                mdelay(10);
 315                writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2);
 316                mdelay(10);
 317                writeb(corsave & ~COR_SRESET,
 318                       hw_priv->attr_mem + hw_priv->cor_offset);
 319                mdelay(10);
 320        }
 321}
 322
 323
 324static struct prism2_helper_functions prism2_plx_funcs =
 325{
 326        .card_present   = NULL,
 327        .cor_sreset     = prism2_plx_cor_sreset,
 328        .genesis_reset  = prism2_plx_genesis_reset,
 329        .hw_type        = HOSTAP_HW_PLX,
 330};
 331
 332
 333static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len,
 334                                unsigned int *cor_offset,
 335                                unsigned int *cor_index)
 336{
 337#define CISTPL_CONFIG 0x1A
 338#define CISTPL_MANFID 0x20
 339#define CISTPL_END 0xFF
 340#define CIS_MAX_LEN 256
 341        u8 *cis;
 342        int i, pos;
 343        unsigned int rmsz, rasz, manfid1, manfid2;
 344        struct prism2_plx_manfid *manfid;
 345
 346        cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL);
 347        if (cis == NULL)
 348                return -ENOMEM;
 349
 350        /* read CIS; it is in even offsets in the beginning of attr_mem */
 351        for (i = 0; i < CIS_MAX_LEN; i++)
 352                cis[i] = readb(attr_mem + 2 * i);
 353        printk(KERN_DEBUG "%s: CIS: %6ph ...\n", dev_info, cis);
 354
 355        /* set reasonable defaults for Prism2 cards just in case CIS parsing
 356         * fails */
 357        *cor_offset = 0x3e0;
 358        *cor_index = 0x01;
 359        manfid1 = manfid2 = 0;
 360
 361        pos = 0;
 362        while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) {
 363                if (pos + 2 + cis[pos + 1] > CIS_MAX_LEN)
 364                        goto cis_error;
 365
 366                switch (cis[pos]) {
 367                case CISTPL_CONFIG:
 368                        if (cis[pos + 1] < 2)
 369                                goto cis_error;
 370                        rmsz = (cis[pos + 2] & 0x3c) >> 2;
 371                        rasz = cis[pos + 2] & 0x03;
 372                        if (4 + rasz + rmsz > cis[pos + 1])
 373                                goto cis_error;
 374                        *cor_index = cis[pos + 3] & 0x3F;
 375                        *cor_offset = 0;
 376                        for (i = 0; i <= rasz; i++)
 377                                *cor_offset += cis[pos + 4 + i] << (8 * i);
 378                        printk(KERN_DEBUG "%s: cor_index=0x%x "
 379                               "cor_offset=0x%x\n", dev_info,
 380                               *cor_index, *cor_offset);
 381                        if (*cor_offset > attr_len) {
 382                                printk(KERN_ERR "%s: COR offset not within "
 383                                       "attr_mem\n", dev_info);
 384                                kfree(cis);
 385                                return -1;
 386                        }
 387                        break;
 388
 389                case CISTPL_MANFID:
 390                        if (cis[pos + 1] < 4)
 391                                goto cis_error;
 392                        manfid1 = cis[pos + 2] + (cis[pos + 3] << 8);
 393                        manfid2 = cis[pos + 4] + (cis[pos + 5] << 8);
 394                        printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n",
 395                               dev_info, manfid1, manfid2);
 396                        break;
 397                }
 398
 399                pos += cis[pos + 1] + 2;
 400        }
 401
 402        if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END)
 403                goto cis_error;
 404
 405        for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++)
 406                if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) {
 407                        kfree(cis);
 408                        return 0;
 409                }
 410
 411        printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is"
 412               " not supported card\n", dev_info, manfid1, manfid2);
 413        goto fail;
 414
 415 cis_error:
 416        printk(KERN_WARNING "%s: invalid CIS data\n", dev_info);
 417
 418 fail:
 419        kfree(cis);
 420        if (ignore_cis) {
 421                printk(KERN_INFO "%s: ignore_cis parameter set - ignoring "
 422                       "errors during CIS verification\n", dev_info);
 423                return 0;
 424        }
 425        return -1;
 426}
 427
 428
 429static int prism2_plx_probe(struct pci_dev *pdev,
 430                            const struct pci_device_id *id)
 431{
 432        unsigned int pccard_ioaddr, plx_ioaddr;
 433        unsigned long pccard_attr_mem;
 434        unsigned int pccard_attr_len;
 435        void __iomem *attr_mem = NULL;
 436        unsigned int cor_offset = 0, cor_index = 0;
 437        u32 reg;
 438        local_info_t *local = NULL;
 439        struct net_device *dev = NULL;
 440        struct hostap_interface *iface;
 441        static int cards_found /* = 0 */;
 442        int irq_registered = 0;
 443        int tmd7160;
 444        struct hostap_plx_priv *hw_priv;
 445
 446        hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
 447        if (hw_priv == NULL)
 448                return -ENOMEM;
 449
 450        if (pci_enable_device(pdev))
 451                goto err_out_free;
 452
 453        /* National Datacomm NCP130 based on TMD7160, not PLX9052. */
 454        tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
 455
 456        plx_ioaddr = pci_resource_start(pdev, 1);
 457        pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
 458
 459        if (tmd7160) {
 460                /* TMD7160 */
 461                attr_mem = NULL; /* no access to PC Card attribute memory */
 462
 463                printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
 464                       "irq=%d, pccard_io=0x%x\n",
 465                       plx_ioaddr, pdev->irq, pccard_ioaddr);
 466
 467                cor_offset = plx_ioaddr;
 468                cor_index = 0x04;
 469
 470                outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
 471                mdelay(1);
 472                reg = inb(plx_ioaddr);
 473                if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
 474                        printk(KERN_ERR "%s: Error setting COR (expected="
 475                               "0x%02x, was=0x%02x)\n", dev_info,
 476                               cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
 477                        goto fail;
 478                }
 479        } else {
 480                /* PLX9052 */
 481                pccard_attr_mem = pci_resource_start(pdev, 2);
 482                pccard_attr_len = pci_resource_len(pdev, 2);
 483                if (pccard_attr_len < PLX_MIN_ATTR_LEN)
 484                        goto fail;
 485
 486
 487                attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
 488                if (attr_mem == NULL) {
 489                        printk(KERN_ERR "%s: cannot remap attr_mem\n",
 490                               dev_info);
 491                        goto fail;
 492                }
 493
 494                printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
 495                       "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
 496                       pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
 497
 498                if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
 499                                         &cor_offset, &cor_index)) {
 500                        printk(KERN_INFO "Unknown PC Card CIS - not a "
 501                               "Prism2/2.5 card?\n");
 502                        goto fail;
 503                }
 504
 505                printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
 506                       "adapter\n");
 507
 508                /* Write COR to enable PC Card */
 509                writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
 510                       attr_mem + cor_offset);
 511
 512                /* Enable PCI interrupts if they are not already enabled */
 513                reg = inl(plx_ioaddr + PLX_INTCSR);
 514                printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
 515                if (!(reg & PLX_INTCSR_PCI_INTEN)) {
 516                        outl(reg | PLX_INTCSR_PCI_INTEN,
 517                             plx_ioaddr + PLX_INTCSR);
 518                        if (!(inl(plx_ioaddr + PLX_INTCSR) &
 519                              PLX_INTCSR_PCI_INTEN)) {
 520                                printk(KERN_WARNING "%s: Could not enable "
 521                                       "Local Interrupts\n", dev_info);
 522                                goto fail;
 523                        }
 524                }
 525
 526                reg = inl(plx_ioaddr + PLX_CNTRL);
 527                printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
 528                       "present=%d)\n",
 529                       reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
 530                /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
 531                 * not present; but are there really such cards in use(?) */
 532        }
 533
 534        dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
 535                                     &pdev->dev);
 536        if (dev == NULL)
 537                goto fail;
 538        iface = netdev_priv(dev);
 539        local = iface->local;
 540        local->hw_priv = hw_priv;
 541        cards_found++;
 542
 543        dev->irq = pdev->irq;
 544        dev->base_addr = pccard_ioaddr;
 545        hw_priv->attr_mem = attr_mem;
 546        hw_priv->cor_offset = cor_offset;
 547
 548        pci_set_drvdata(pdev, dev);
 549
 550        if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name,
 551                        dev)) {
 552                printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
 553                goto fail;
 554        } else
 555                irq_registered = 1;
 556
 557        if (prism2_hw_config(dev, 1)) {
 558                printk(KERN_DEBUG "%s: hardware initialization failed\n",
 559                       dev_info);
 560                goto fail;
 561        }
 562
 563        return hostap_hw_ready(dev);
 564
 565 fail:
 566        if (irq_registered && dev)
 567                free_irq(dev->irq, dev);
 568
 569        if (attr_mem)
 570                iounmap(attr_mem);
 571
 572        pci_disable_device(pdev);
 573        prism2_free_local_data(dev);
 574
 575 err_out_free:
 576        kfree(hw_priv);
 577
 578        return -ENODEV;
 579}
 580
 581
 582static void prism2_plx_remove(struct pci_dev *pdev)
 583{
 584        struct net_device *dev;
 585        struct hostap_interface *iface;
 586        struct hostap_plx_priv *hw_priv;
 587
 588        dev = pci_get_drvdata(pdev);
 589        iface = netdev_priv(dev);
 590        hw_priv = iface->local->hw_priv;
 591
 592        /* Reset the hardware, and ensure interrupts are disabled. */
 593        prism2_plx_cor_sreset(iface->local);
 594        hfa384x_disable_interrupts(dev);
 595
 596        if (hw_priv->attr_mem)
 597                iounmap(hw_priv->attr_mem);
 598        if (dev->irq)
 599                free_irq(dev->irq, dev);
 600
 601        prism2_free_local_data(dev);
 602        kfree(hw_priv);
 603        pci_disable_device(pdev);
 604}
 605
 606
 607MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
 608
 609static struct pci_driver prism2_plx_driver = {
 610        .name           = "hostap_plx",
 611        .id_table       = prism2_plx_id_table,
 612        .probe          = prism2_plx_probe,
 613        .remove         = prism2_plx_remove,
 614};
 615
 616module_pci_driver(prism2_plx_driver);
 617