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