linux/drivers/net/wireless/intersil/hostap/hostap_plx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2#define PRISM2_PLX
   3
   4/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is
   5 * based on:
   6 * - Host AP driver patch from james@madingley.org
   7 * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc.
   8 */
   9
  10
  11#include <linux/module.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_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: %6ph ...\n", dev_info, cis);
 355
 356        /* set reasonable defaults for Prism2 cards just in case CIS parsing
 357         * fails */
 358        *cor_offset = 0x3e0;
 359        *cor_index = 0x01;
 360        manfid1 = manfid2 = 0;
 361
 362        pos = 0;
 363        while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) {
 364                if (pos + 2 + cis[pos + 1] > CIS_MAX_LEN)
 365                        goto cis_error;
 366
 367                switch (cis[pos]) {
 368                case CISTPL_CONFIG:
 369                        if (cis[pos + 1] < 2)
 370                                goto cis_error;
 371                        rmsz = (cis[pos + 2] & 0x3c) >> 2;
 372                        rasz = cis[pos + 2] & 0x03;
 373                        if (4 + rasz + rmsz > cis[pos + 1])
 374                                goto cis_error;
 375                        *cor_index = cis[pos + 3] & 0x3F;
 376                        *cor_offset = 0;
 377                        for (i = 0; i <= rasz; i++)
 378                                *cor_offset += cis[pos + 4 + i] << (8 * i);
 379                        printk(KERN_DEBUG "%s: cor_index=0x%x "
 380                               "cor_offset=0x%x\n", dev_info,
 381                               *cor_index, *cor_offset);
 382                        if (*cor_offset > attr_len) {
 383                                printk(KERN_ERR "%s: COR offset not within "
 384                                       "attr_mem\n", dev_info);
 385                                kfree(cis);
 386                                return -1;
 387                        }
 388                        break;
 389
 390                case CISTPL_MANFID:
 391                        if (cis[pos + 1] < 4)
 392                                goto cis_error;
 393                        manfid1 = cis[pos + 2] + (cis[pos + 3] << 8);
 394                        manfid2 = cis[pos + 4] + (cis[pos + 5] << 8);
 395                        printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n",
 396                               dev_info, manfid1, manfid2);
 397                        break;
 398                }
 399
 400                pos += cis[pos + 1] + 2;
 401        }
 402
 403        if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END)
 404                goto cis_error;
 405
 406        for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++)
 407                if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) {
 408                        kfree(cis);
 409                        return 0;
 410                }
 411
 412        printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is"
 413               " not supported card\n", dev_info, manfid1, manfid2);
 414        goto fail;
 415
 416 cis_error:
 417        printk(KERN_WARNING "%s: invalid CIS data\n", dev_info);
 418
 419 fail:
 420        kfree(cis);
 421        if (ignore_cis) {
 422                printk(KERN_INFO "%s: ignore_cis parameter set - ignoring "
 423                       "errors during CIS verification\n", dev_info);
 424                return 0;
 425        }
 426        return -1;
 427}
 428
 429
 430static int prism2_plx_probe(struct pci_dev *pdev,
 431                            const struct pci_device_id *id)
 432{
 433        unsigned int pccard_ioaddr, plx_ioaddr;
 434        unsigned long pccard_attr_mem;
 435        unsigned int pccard_attr_len;
 436        void __iomem *attr_mem = NULL;
 437        unsigned int cor_offset = 0, cor_index = 0;
 438        u32 reg;
 439        local_info_t *local = NULL;
 440        struct net_device *dev = NULL;
 441        struct hostap_interface *iface;
 442        static int cards_found /* = 0 */;
 443        int irq_registered = 0;
 444        int tmd7160;
 445        struct hostap_plx_priv *hw_priv;
 446
 447        hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
 448        if (hw_priv == NULL)
 449                return -ENOMEM;
 450
 451        if (pci_enable_device(pdev))
 452                goto err_out_free;
 453
 454        /* National Datacomm NCP130 based on TMD7160, not PLX9052. */
 455        tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
 456
 457        plx_ioaddr = pci_resource_start(pdev, 1);
 458        pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
 459
 460        if (tmd7160) {
 461                /* TMD7160 */
 462                attr_mem = NULL; /* no access to PC Card attribute memory */
 463
 464                printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
 465                       "irq=%d, pccard_io=0x%x\n",
 466                       plx_ioaddr, pdev->irq, pccard_ioaddr);
 467
 468                cor_offset = plx_ioaddr;
 469                cor_index = 0x04;
 470
 471                outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
 472                mdelay(1);
 473                reg = inb(plx_ioaddr);
 474                if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
 475                        printk(KERN_ERR "%s: Error setting COR (expected="
 476                               "0x%02x, was=0x%02x)\n", dev_info,
 477                               cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
 478                        goto fail;
 479                }
 480        } else {
 481                /* PLX9052 */
 482                pccard_attr_mem = pci_resource_start(pdev, 2);
 483                pccard_attr_len = pci_resource_len(pdev, 2);
 484                if (pccard_attr_len < PLX_MIN_ATTR_LEN)
 485                        goto fail;
 486
 487
 488                attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
 489                if (attr_mem == NULL) {
 490                        printk(KERN_ERR "%s: cannot remap attr_mem\n",
 491                               dev_info);
 492                        goto fail;
 493                }
 494
 495                printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
 496                       "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
 497                       pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
 498
 499                if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
 500                                         &cor_offset, &cor_index)) {
 501                        printk(KERN_INFO "Unknown PC Card CIS - not a "
 502                               "Prism2/2.5 card?\n");
 503                        goto fail;
 504                }
 505
 506                printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
 507                       "adapter\n");
 508
 509                /* Write COR to enable PC Card */
 510                writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
 511                       attr_mem + cor_offset);
 512
 513                /* Enable PCI interrupts if they are not already enabled */
 514                reg = inl(plx_ioaddr + PLX_INTCSR);
 515                printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
 516                if (!(reg & PLX_INTCSR_PCI_INTEN)) {
 517                        outl(reg | PLX_INTCSR_PCI_INTEN,
 518                             plx_ioaddr + PLX_INTCSR);
 519                        if (!(inl(plx_ioaddr + PLX_INTCSR) &
 520                              PLX_INTCSR_PCI_INTEN)) {
 521                                printk(KERN_WARNING "%s: Could not enable "
 522                                       "Local Interrupts\n", dev_info);
 523                                goto fail;
 524                        }
 525                }
 526
 527                reg = inl(plx_ioaddr + PLX_CNTRL);
 528                printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
 529                       "present=%d)\n",
 530                       reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
 531                /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
 532                 * not present; but are there really such cards in use(?) */
 533        }
 534
 535        dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
 536                                     &pdev->dev);
 537        if (dev == NULL)
 538                goto fail;
 539        iface = netdev_priv(dev);
 540        local = iface->local;
 541        local->hw_priv = hw_priv;
 542        cards_found++;
 543
 544        dev->irq = pdev->irq;
 545        dev->base_addr = pccard_ioaddr;
 546        hw_priv->attr_mem = attr_mem;
 547        hw_priv->cor_offset = cor_offset;
 548
 549        pci_set_drvdata(pdev, dev);
 550
 551        if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name,
 552                        dev)) {
 553                printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
 554                goto fail;
 555        } else
 556                irq_registered = 1;
 557
 558        if (prism2_hw_config(dev, 1)) {
 559                printk(KERN_DEBUG "%s: hardware initialization failed\n",
 560                       dev_info);
 561                goto fail;
 562        }
 563
 564        return hostap_hw_ready(dev);
 565
 566 fail:
 567        if (irq_registered && dev)
 568                free_irq(dev->irq, dev);
 569
 570        if (attr_mem)
 571                iounmap(attr_mem);
 572
 573        pci_disable_device(pdev);
 574        prism2_free_local_data(dev);
 575
 576 err_out_free:
 577        kfree(hw_priv);
 578
 579        return -ENODEV;
 580}
 581
 582
 583static void prism2_plx_remove(struct pci_dev *pdev)
 584{
 585        struct net_device *dev;
 586        struct hostap_interface *iface;
 587        struct hostap_plx_priv *hw_priv;
 588
 589        dev = pci_get_drvdata(pdev);
 590        iface = netdev_priv(dev);
 591        hw_priv = iface->local->hw_priv;
 592
 593        /* Reset the hardware, and ensure interrupts are disabled. */
 594        prism2_plx_cor_sreset(iface->local);
 595        hfa384x_disable_interrupts(dev);
 596
 597        if (hw_priv->attr_mem)
 598                iounmap(hw_priv->attr_mem);
 599        if (dev->irq)
 600                free_irq(dev->irq, dev);
 601
 602        prism2_free_local_data(dev);
 603        kfree(hw_priv);
 604        pci_disable_device(pdev);
 605}
 606
 607
 608MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
 609
 610static struct pci_driver prism2_plx_driver = {
 611        .name           = "hostap_plx",
 612        .id_table       = prism2_plx_id_table,
 613        .probe          = prism2_plx_probe,
 614        .remove         = prism2_plx_remove,
 615};
 616
 617module_pci_driver(prism2_plx_driver);
 618