Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 #define PRISM2_PLX
0003 
0004 /* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is
0005  * based on:
0006  * - Host AP driver patch from james@madingley.org
0007  * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc.
0008  */
0009 
0010 
0011 #include <linux/module.h>
0012 #include <linux/if.h>
0013 #include <linux/skbuff.h>
0014 #include <linux/netdevice.h>
0015 #include <linux/slab.h>
0016 #include <linux/workqueue.h>
0017 #include <linux/wireless.h>
0018 #include <net/iw_handler.h>
0019 
0020 #include <linux/ioport.h>
0021 #include <linux/pci.h>
0022 #include <asm/io.h>
0023 
0024 #include "hostap_wlan.h"
0025 
0026 
0027 static char *dev_info = "hostap_plx";
0028 
0029 
0030 MODULE_AUTHOR("Jouni Malinen");
0031 MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
0032            "cards (PLX).");
0033 MODULE_LICENSE("GPL");
0034 
0035 
0036 static int ignore_cis;
0037 module_param(ignore_cis, int, 0444);
0038 MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
0039 
0040 
0041 /* struct local_info::hw_priv */
0042 struct hostap_plx_priv {
0043     void __iomem *attr_mem;
0044     unsigned int cor_offset;
0045 };
0046 
0047 
0048 #define PLX_MIN_ATTR_LEN 512    /* at least 2 x 256 is needed for CIS */
0049 #define COR_SRESET       0x80
0050 #define COR_LEVLREQ      0x40
0051 #define COR_ENABLE_FUNC  0x01
0052 /* PCI Configuration Registers */
0053 #define PLX_PCIIPR       0x3d   /* PCI Interrupt Pin */
0054 /* Local Configuration Registers */
0055 #define PLX_INTCSR       0x4c   /* Interrupt Control/Status Register */
0056 #define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */
0057 #define PLX_CNTRL        0x50
0058 #define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
0059 
0060 
0061 #define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
0062 
0063 static const struct pci_device_id prism2_plx_id_table[] = {
0064     PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
0065     PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
0066     PLXDEV(0x126c, 0x8030, "Nortel emobility"),
0067     PLXDEV(0x1562, 0x0001, "Symbol LA-4123"),
0068     PLXDEV(0x1385, 0x4100, "Netgear MA301"),
0069     PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
0070     PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
0071     PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
0072     PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"),
0073     PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"),
0074     PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"),
0075     PLXDEV(0x16ab, 0x1103, "Longshine 8031"),
0076     PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"),
0077     PLXDEV(0xec80, 0xec00, "Belkin F5D6000"),
0078     { 0 }
0079 };
0080 
0081 
0082 /* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid
0083  * is not listed here, you will need to add it here to get the driver
0084  * initialized. */
0085 static struct prism2_plx_manfid {
0086     u16 manfid1, manfid2;
0087 } prism2_plx_known_manfids[] = {
0088     { 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */,
0089     { 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */,
0090     { 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */,
0091     { 0x0126, 0x8000 } /* Proxim RangeLAN */,
0092     { 0x0138, 0x0002 } /* Compaq WL100 */,
0093     { 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */,
0094     { 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */,
0095     { 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */,
0096     { 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */,
0097     { 0x028a, 0x0002 } /* D-Link DRC-650 */,
0098     { 0x0250, 0x0002 } /* Samsung SWL2000-N */,
0099     { 0xc250, 0x0002 } /* EMTAC A2424i */,
0100     { 0xd601, 0x0002 } /* Z-Com XI300 */,
0101     { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */,
0102     { 0, 0}
0103 };
0104 
0105 
0106 #ifdef PRISM2_IO_DEBUG
0107 
0108 static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
0109 {
0110     struct hostap_interface *iface;
0111     local_info_t *local;
0112     unsigned long flags;
0113 
0114     iface = netdev_priv(dev);
0115     local = iface->local;
0116 
0117     spin_lock_irqsave(&local->lock, flags);
0118     prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
0119     outb(v, dev->base_addr + a);
0120     spin_unlock_irqrestore(&local->lock, flags);
0121 }
0122 
0123 static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
0124 {
0125     struct hostap_interface *iface;
0126     local_info_t *local;
0127     unsigned long flags;
0128     u8 v;
0129 
0130     iface = netdev_priv(dev);
0131     local = iface->local;
0132 
0133     spin_lock_irqsave(&local->lock, flags);
0134     v = inb(dev->base_addr + a);
0135     prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
0136     spin_unlock_irqrestore(&local->lock, flags);
0137     return v;
0138 }
0139 
0140 static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
0141 {
0142     struct hostap_interface *iface;
0143     local_info_t *local;
0144     unsigned long flags;
0145 
0146     iface = netdev_priv(dev);
0147     local = iface->local;
0148 
0149     spin_lock_irqsave(&local->lock, flags);
0150     prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
0151     outw(v, dev->base_addr + a);
0152     spin_unlock_irqrestore(&local->lock, flags);
0153 }
0154 
0155 static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
0156 {
0157     struct hostap_interface *iface;
0158     local_info_t *local;
0159     unsigned long flags;
0160     u16 v;
0161 
0162     iface = netdev_priv(dev);
0163     local = iface->local;
0164 
0165     spin_lock_irqsave(&local->lock, flags);
0166     v = inw(dev->base_addr + a);
0167     prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
0168     spin_unlock_irqrestore(&local->lock, flags);
0169     return v;
0170 }
0171 
0172 static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
0173                        u8 *buf, int wc)
0174 {
0175     struct hostap_interface *iface;
0176     local_info_t *local;
0177     unsigned long flags;
0178 
0179     iface = netdev_priv(dev);
0180     local = iface->local;
0181 
0182     spin_lock_irqsave(&local->lock, flags);
0183     prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
0184     outsw(dev->base_addr + a, buf, wc);
0185     spin_unlock_irqrestore(&local->lock, flags);
0186 }
0187 
0188 static inline void hfa384x_insw_debug(struct net_device *dev, int a,
0189                       u8 *buf, int wc)
0190 {
0191     struct hostap_interface *iface;
0192     local_info_t *local;
0193     unsigned long flags;
0194 
0195     iface = netdev_priv(dev);
0196     local = iface->local;
0197 
0198     spin_lock_irqsave(&local->lock, flags);
0199     prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
0200     insw(dev->base_addr + a, buf, wc);
0201     spin_unlock_irqrestore(&local->lock, flags);
0202 }
0203 
0204 #define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
0205 #define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
0206 #define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
0207 #define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
0208 #define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
0209 #define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
0210 
0211 #else /* PRISM2_IO_DEBUG */
0212 
0213 #define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
0214 #define HFA384X_INB(a) inb(dev->base_addr + (a))
0215 #define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
0216 #define HFA384X_INW(a) inw(dev->base_addr + (a))
0217 #define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
0218 #define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
0219 
0220 #endif /* PRISM2_IO_DEBUG */
0221 
0222 
0223 static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
0224                 int len)
0225 {
0226     u16 d_off;
0227     u16 *pos;
0228 
0229     d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
0230     pos = (u16 *) buf;
0231 
0232     if (len / 2)
0233         HFA384X_INSW(d_off, buf, len / 2);
0234     pos += len / 2;
0235 
0236     if (len & 1)
0237         *((char *) pos) = HFA384X_INB(d_off);
0238 
0239     return 0;
0240 }
0241 
0242 
0243 static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
0244 {
0245     u16 d_off;
0246     u16 *pos;
0247 
0248     d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
0249     pos = (u16 *) buf;
0250 
0251     if (len / 2)
0252         HFA384X_OUTSW(d_off, buf, len / 2);
0253     pos += len / 2;
0254 
0255     if (len & 1)
0256         HFA384X_OUTB(*((char *) pos), d_off);
0257 
0258     return 0;
0259 }
0260 
0261 
0262 /* FIX: This might change at some point.. */
0263 #include "hostap_hw.c"
0264 
0265 
0266 static void prism2_plx_cor_sreset(local_info_t *local)
0267 {
0268     unsigned char corsave;
0269     struct hostap_plx_priv *hw_priv = local->hw_priv;
0270 
0271     printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n",
0272            dev_info);
0273 
0274     /* Set sreset bit of COR and clear it after hold time */
0275 
0276     if (hw_priv->attr_mem == NULL) {
0277         /* TMD7160 - COR at card's first I/O addr */
0278         corsave = inb(hw_priv->cor_offset);
0279         outb(corsave | COR_SRESET, hw_priv->cor_offset);
0280         mdelay(2);
0281         outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
0282         mdelay(2);
0283     } else {
0284         /* PLX9052 */
0285         corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
0286         writeb(corsave | COR_SRESET,
0287                hw_priv->attr_mem + hw_priv->cor_offset);
0288         mdelay(2);
0289         writeb(corsave & ~COR_SRESET,
0290                hw_priv->attr_mem + hw_priv->cor_offset);
0291         mdelay(2);
0292     }
0293 }
0294 
0295 
0296 static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
0297 {
0298     unsigned char corsave;
0299     struct hostap_plx_priv *hw_priv = local->hw_priv;
0300 
0301     if (hw_priv->attr_mem == NULL) {
0302         /* TMD7160 - COR at card's first I/O addr */
0303         corsave = inb(hw_priv->cor_offset);
0304         outb(corsave | COR_SRESET, hw_priv->cor_offset);
0305         mdelay(10);
0306         outb(hcr, hw_priv->cor_offset + 2);
0307         mdelay(10);
0308         outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
0309         mdelay(10);
0310     } else {
0311         /* PLX9052 */
0312         corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
0313         writeb(corsave | COR_SRESET,
0314                hw_priv->attr_mem + hw_priv->cor_offset);
0315         mdelay(10);
0316         writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2);
0317         mdelay(10);
0318         writeb(corsave & ~COR_SRESET,
0319                hw_priv->attr_mem + hw_priv->cor_offset);
0320         mdelay(10);
0321     }
0322 }
0323 
0324 
0325 static struct prism2_helper_functions prism2_plx_funcs =
0326 {
0327     .card_present   = NULL,
0328     .cor_sreset = prism2_plx_cor_sreset,
0329     .genesis_reset  = prism2_plx_genesis_reset,
0330     .hw_type    = HOSTAP_HW_PLX,
0331 };
0332 
0333 
0334 static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len,
0335                 unsigned int *cor_offset,
0336                 unsigned int *cor_index)
0337 {
0338 #define CISTPL_CONFIG 0x1A
0339 #define CISTPL_MANFID 0x20
0340 #define CISTPL_END 0xFF
0341 #define CIS_MAX_LEN 256
0342     u8 *cis;
0343     int i, pos;
0344     unsigned int rmsz, rasz, manfid1, manfid2;
0345     struct prism2_plx_manfid *manfid;
0346 
0347     cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL);
0348     if (cis == NULL)
0349         return -ENOMEM;
0350 
0351     /* read CIS; it is in even offsets in the beginning of attr_mem */
0352     for (i = 0; i < CIS_MAX_LEN; i++)
0353         cis[i] = readb(attr_mem + 2 * i);
0354     printk(KERN_DEBUG "%s: CIS: %6ph ...\n", dev_info, cis);
0355 
0356     /* set reasonable defaults for Prism2 cards just in case CIS parsing
0357      * fails */
0358     *cor_offset = 0x3e0;
0359     *cor_index = 0x01;
0360     manfid1 = manfid2 = 0;
0361 
0362     pos = 0;
0363     while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) {
0364         if (pos + 2 + cis[pos + 1] > CIS_MAX_LEN)
0365             goto cis_error;
0366 
0367         switch (cis[pos]) {
0368         case CISTPL_CONFIG:
0369             if (cis[pos + 1] < 2)
0370                 goto cis_error;
0371             rmsz = (cis[pos + 2] & 0x3c) >> 2;
0372             rasz = cis[pos + 2] & 0x03;
0373             if (4 + rasz + rmsz > cis[pos + 1])
0374                 goto cis_error;
0375             *cor_index = cis[pos + 3] & 0x3F;
0376             *cor_offset = 0;
0377             for (i = 0; i <= rasz; i++)
0378                 *cor_offset += cis[pos + 4 + i] << (8 * i);
0379             printk(KERN_DEBUG "%s: cor_index=0x%x "
0380                    "cor_offset=0x%x\n", dev_info,
0381                    *cor_index, *cor_offset);
0382             if (*cor_offset > attr_len) {
0383                 printk(KERN_ERR "%s: COR offset not within "
0384                        "attr_mem\n", dev_info);
0385                 kfree(cis);
0386                 return -1;
0387             }
0388             break;
0389 
0390         case CISTPL_MANFID:
0391             if (cis[pos + 1] < 4)
0392                 goto cis_error;
0393             manfid1 = cis[pos + 2] + (cis[pos + 3] << 8);
0394             manfid2 = cis[pos + 4] + (cis[pos + 5] << 8);
0395             printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n",
0396                    dev_info, manfid1, manfid2);
0397             break;
0398         }
0399 
0400         pos += cis[pos + 1] + 2;
0401     }
0402 
0403     if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END)
0404         goto cis_error;
0405 
0406     for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++)
0407         if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) {
0408             kfree(cis);
0409             return 0;
0410         }
0411 
0412     printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is"
0413            " not supported card\n", dev_info, manfid1, manfid2);
0414     goto fail;
0415 
0416  cis_error:
0417     printk(KERN_WARNING "%s: invalid CIS data\n", dev_info);
0418 
0419  fail:
0420     kfree(cis);
0421     if (ignore_cis) {
0422         printk(KERN_INFO "%s: ignore_cis parameter set - ignoring "
0423                "errors during CIS verification\n", dev_info);
0424         return 0;
0425     }
0426     return -1;
0427 }
0428 
0429 
0430 static int prism2_plx_probe(struct pci_dev *pdev,
0431                 const struct pci_device_id *id)
0432 {
0433     unsigned int pccard_ioaddr, plx_ioaddr;
0434     unsigned long pccard_attr_mem;
0435     unsigned int pccard_attr_len;
0436     void __iomem *attr_mem = NULL;
0437     unsigned int cor_offset = 0, cor_index = 0;
0438     u32 reg;
0439     local_info_t *local = NULL;
0440     struct net_device *dev = NULL;
0441     struct hostap_interface *iface;
0442     static int cards_found /* = 0 */;
0443     int irq_registered = 0;
0444     int tmd7160;
0445     struct hostap_plx_priv *hw_priv;
0446 
0447     hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
0448     if (hw_priv == NULL)
0449         return -ENOMEM;
0450 
0451     if (pci_enable_device(pdev))
0452         goto err_out_free;
0453 
0454     /* National Datacomm NCP130 based on TMD7160, not PLX9052. */
0455     tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
0456 
0457     plx_ioaddr = pci_resource_start(pdev, 1);
0458     pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
0459 
0460     if (tmd7160) {
0461         /* TMD7160 */
0462         attr_mem = NULL; /* no access to PC Card attribute memory */
0463 
0464         printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
0465                "irq=%d, pccard_io=0x%x\n",
0466                plx_ioaddr, pdev->irq, pccard_ioaddr);
0467 
0468         cor_offset = plx_ioaddr;
0469         cor_index = 0x04;
0470 
0471         outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
0472         mdelay(1);
0473         reg = inb(plx_ioaddr);
0474         if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
0475             printk(KERN_ERR "%s: Error setting COR (expected="
0476                    "0x%02x, was=0x%02x)\n", dev_info,
0477                    cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
0478             goto fail;
0479         }
0480     } else {
0481         /* PLX9052 */
0482         pccard_attr_mem = pci_resource_start(pdev, 2);
0483         pccard_attr_len = pci_resource_len(pdev, 2);
0484         if (pccard_attr_len < PLX_MIN_ATTR_LEN)
0485             goto fail;
0486 
0487 
0488         attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
0489         if (attr_mem == NULL) {
0490             printk(KERN_ERR "%s: cannot remap attr_mem\n",
0491                    dev_info);
0492             goto fail;
0493         }
0494 
0495         printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
0496                "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
0497                pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
0498 
0499         if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
0500                      &cor_offset, &cor_index)) {
0501             printk(KERN_INFO "Unknown PC Card CIS - not a "
0502                    "Prism2/2.5 card?\n");
0503             goto fail;
0504         }
0505 
0506         printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
0507                "adapter\n");
0508 
0509         /* Write COR to enable PC Card */
0510         writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
0511                attr_mem + cor_offset);
0512 
0513         /* Enable PCI interrupts if they are not already enabled */
0514         reg = inl(plx_ioaddr + PLX_INTCSR);
0515         printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
0516         if (!(reg & PLX_INTCSR_PCI_INTEN)) {
0517             outl(reg | PLX_INTCSR_PCI_INTEN,
0518                  plx_ioaddr + PLX_INTCSR);
0519             if (!(inl(plx_ioaddr + PLX_INTCSR) &
0520                   PLX_INTCSR_PCI_INTEN)) {
0521                 printk(KERN_WARNING "%s: Could not enable "
0522                        "Local Interrupts\n", dev_info);
0523                 goto fail;
0524             }
0525         }
0526 
0527         reg = inl(plx_ioaddr + PLX_CNTRL);
0528         printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
0529                "present=%d)\n",
0530                reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
0531         /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
0532          * not present; but are there really such cards in use(?) */
0533     }
0534 
0535     dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
0536                      &pdev->dev);
0537     if (dev == NULL)
0538         goto fail;
0539     iface = netdev_priv(dev);
0540     local = iface->local;
0541     local->hw_priv = hw_priv;
0542     cards_found++;
0543 
0544     dev->irq = pdev->irq;
0545     dev->base_addr = pccard_ioaddr;
0546     hw_priv->attr_mem = attr_mem;
0547     hw_priv->cor_offset = cor_offset;
0548 
0549     pci_set_drvdata(pdev, dev);
0550 
0551     if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name,
0552             dev)) {
0553         printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
0554         goto fail;
0555     } else
0556         irq_registered = 1;
0557 
0558     if (prism2_hw_config(dev, 1)) {
0559         printk(KERN_DEBUG "%s: hardware initialization failed\n",
0560                dev_info);
0561         goto fail;
0562     }
0563 
0564     return hostap_hw_ready(dev);
0565 
0566  fail:
0567     if (irq_registered && dev)
0568         free_irq(dev->irq, dev);
0569 
0570     if (attr_mem)
0571         iounmap(attr_mem);
0572 
0573     pci_disable_device(pdev);
0574     prism2_free_local_data(dev);
0575 
0576  err_out_free:
0577     kfree(hw_priv);
0578 
0579     return -ENODEV;
0580 }
0581 
0582 
0583 static void prism2_plx_remove(struct pci_dev *pdev)
0584 {
0585     struct net_device *dev;
0586     struct hostap_interface *iface;
0587     struct hostap_plx_priv *hw_priv;
0588 
0589     dev = pci_get_drvdata(pdev);
0590     iface = netdev_priv(dev);
0591     hw_priv = iface->local->hw_priv;
0592 
0593     /* Reset the hardware, and ensure interrupts are disabled. */
0594     prism2_plx_cor_sreset(iface->local);
0595     hfa384x_disable_interrupts(dev);
0596 
0597     if (hw_priv->attr_mem)
0598         iounmap(hw_priv->attr_mem);
0599     if (dev->irq)
0600         free_irq(dev->irq, dev);
0601 
0602     prism2_free_local_data(dev);
0603     kfree(hw_priv);
0604     pci_disable_device(pdev);
0605 }
0606 
0607 
0608 MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
0609 
0610 static struct pci_driver prism2_plx_driver = {
0611     .name       = "hostap_plx",
0612     .id_table   = prism2_plx_id_table,
0613     .probe      = prism2_plx_probe,
0614     .remove     = prism2_plx_remove,
0615 };
0616 
0617 module_pci_driver(prism2_plx_driver);