Linux Audio

Check our new training course

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