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 
26 static char *dev_info = "hostap_plx";
27 
28 
29 MODULE_AUTHOR("Jouni Malinen");
30 MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
31 		   "cards (PLX).");
32 MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
33 MODULE_LICENSE("GPL");
34 
35 
36 static int ignore_cis;
37 module_param(ignore_cis, int, 0444);
38 MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
39 
40 
41 /* struct local_info::hw_priv */
42 struct 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 
63 static 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. */
85 static 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 
108 static 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 
123 static 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 
140 static 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 
155 static 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 
172 static 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 
188 static 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 
223 static 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 
243 static 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 
266 static 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 
296 static 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 
325 static 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 
334 static 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 
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