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 
27 static char *dev_info = "hostap_plx";
28 
29 
30 MODULE_AUTHOR("Jouni Malinen");
31 MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
32 		   "cards (PLX).");
33 MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
34 MODULE_LICENSE("GPL");
35 
36 
37 static int ignore_cis;
38 module_param(ignore_cis, int, 0444);
39 MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
40 
41 
42 /* struct local_info::hw_priv */
43 struct 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 
64 static const struct pci_device_id 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. */
86 static 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 
109 static 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 
124 static 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 
141 static 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 
156 static 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 
173 static 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 
189 static 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 
224 static 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 
244 static 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 
267 static 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 
297 static 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 
326 static 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 
335 static 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: %6ph ...\n", dev_info, cis);
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 
431 static 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 
584 static 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 
609 MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
610 
611 static 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 
618 module_pci_driver(prism2_plx_driver);
619