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