cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hostap_plx.c (16440B)


      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);