• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/src/linux/linux-2.6/drivers/net/wireless/hostap/
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/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 *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)";
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");
35MODULE_VERSION(PRISM2_VERSION);
36
37
38static int ignore_cis;
39module_param(ignore_cis, int, 0444);
40MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
41
42
43/* struct local_info::hw_priv */
44struct hostap_plx_priv {
45	void __iomem *attr_mem;
46	unsigned int cor_offset;
47};
48
49
50#define PLX_MIN_ATTR_LEN 512	/* at least 2 x 256 is needed for CIS */
51#define COR_SRESET       0x80
52#define COR_LEVLREQ      0x40
53#define COR_ENABLE_FUNC  0x01
54/* PCI Configuration Registers */
55#define PLX_PCIIPR       0x3d   /* PCI Interrupt Pin */
56/* Local Configuration Registers */
57#define PLX_INTCSR       0x4c   /* Interrupt Control/Status Register */
58#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */
59#define PLX_CNTRL        0x50
60#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
61
62
63#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
64
65static struct pci_device_id prism2_plx_id_table[] __devinitdata = {
66	PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
67	PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
68	PLXDEV(0x126c, 0x8030, "Nortel emobility"),
69	PLXDEV(0x1562, 0x0001, "Symbol LA-4123"),
70	PLXDEV(0x1385, 0x4100, "Netgear MA301"),
71	PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
72	PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
73	PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
74	PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"),
75	PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"),
76	PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"),
77	PLXDEV(0x16ab, 0x1103, "Longshine 8031"),
78	PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"),
79	PLXDEV(0xec80, 0xec00, "Belkin F5D6000"),
80	{ 0 }
81};
82
83
84/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid
85 * is not listed here, you will need to add it here to get the driver
86 * initialized. */
87static struct prism2_plx_manfid {
88	u16 manfid1, manfid2;
89} prism2_plx_known_manfids[] = {
90	{ 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */,
91	{ 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */,
92	{ 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */,
93	{ 0x0126, 0x8000 } /* Proxim RangeLAN */,
94	{ 0x0138, 0x0002 } /* Compaq WL100 */,
95	{ 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */,
96	{ 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */,
97	{ 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */,
98	{ 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */,
99	{ 0x028a, 0x0002 } /* D-Link DRC-650 */,
100	{ 0x0250, 0x0002 } /* Samsung SWL2000-N */,
101	{ 0xc250, 0x0002 } /* EMTAC A2424i */,
102	{ 0xd601, 0x0002 } /* Z-Com XI300 */,
103	{ 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */,
104	{ 0, 0}
105};
106
107
108#ifdef PRISM2_IO_DEBUG
109
110static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
111{
112	struct hostap_interface *iface;
113	local_info_t *local;
114	unsigned long flags;
115
116	iface = netdev_priv(dev);
117	local = iface->local;
118
119	spin_lock_irqsave(&local->lock, flags);
120	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
121	outb(v, dev->base_addr + a);
122	spin_unlock_irqrestore(&local->lock, flags);
123}
124
125static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
126{
127	struct hostap_interface *iface;
128	local_info_t *local;
129	unsigned long flags;
130	u8 v;
131
132	iface = netdev_priv(dev);
133	local = iface->local;
134
135	spin_lock_irqsave(&local->lock, flags);
136	v = inb(dev->base_addr + a);
137	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
138	spin_unlock_irqrestore(&local->lock, flags);
139	return v;
140}
141
142static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
143{
144	struct hostap_interface *iface;
145	local_info_t *local;
146	unsigned long flags;
147
148	iface = netdev_priv(dev);
149	local = iface->local;
150
151	spin_lock_irqsave(&local->lock, flags);
152	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
153	outw(v, dev->base_addr + a);
154	spin_unlock_irqrestore(&local->lock, flags);
155}
156
157static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
158{
159	struct hostap_interface *iface;
160	local_info_t *local;
161	unsigned long flags;
162	u16 v;
163
164	iface = netdev_priv(dev);
165	local = iface->local;
166
167	spin_lock_irqsave(&local->lock, flags);
168	v = inw(dev->base_addr + a);
169	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
170	spin_unlock_irqrestore(&local->lock, flags);
171	return v;
172}
173
174static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
175				       u8 *buf, int wc)
176{
177	struct hostap_interface *iface;
178	local_info_t *local;
179	unsigned long flags;
180
181	iface = netdev_priv(dev);
182	local = iface->local;
183
184	spin_lock_irqsave(&local->lock, flags);
185	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
186	outsw(dev->base_addr + a, buf, wc);
187	spin_unlock_irqrestore(&local->lock, flags);
188}
189
190static inline void hfa384x_insw_debug(struct net_device *dev, int a,
191				      u8 *buf, int wc)
192{
193	struct hostap_interface *iface;
194	local_info_t *local;
195	unsigned long flags;
196
197	iface = netdev_priv(dev);
198	local = iface->local;
199
200	spin_lock_irqsave(&local->lock, flags);
201	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
202	insw(dev->base_addr + a, buf, wc);
203	spin_unlock_irqrestore(&local->lock, flags);
204}
205
206#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
207#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
208#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
209#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
210#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
211#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
212
213#else /* PRISM2_IO_DEBUG */
214
215#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
216#define HFA384X_INB(a) inb(dev->base_addr + (a))
217#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
218#define HFA384X_INW(a) inw(dev->base_addr + (a))
219#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
220#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
221
222#endif /* PRISM2_IO_DEBUG */
223
224
225static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
226			    int len)
227{
228	u16 d_off;
229	u16 *pos;
230
231	d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
232	pos = (u16 *) buf;
233
234	if (len / 2)
235		HFA384X_INSW(d_off, buf, len / 2);
236	pos += len / 2;
237
238	if (len & 1)
239		*((char *) pos) = HFA384X_INB(d_off);
240
241	return 0;
242}
243
244
245static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
246{
247	u16 d_off;
248	u16 *pos;
249
250	d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
251	pos = (u16 *) buf;
252
253	if (len / 2)
254		HFA384X_OUTSW(d_off, buf, len / 2);
255	pos += len / 2;
256
257	if (len & 1)
258		HFA384X_OUTB(*((char *) pos), d_off);
259
260	return 0;
261}
262
263
264/* FIX: This might change at some point.. */
265#include "hostap_hw.c"
266
267
268static void prism2_plx_cor_sreset(local_info_t *local)
269{
270	unsigned char corsave;
271	struct hostap_plx_priv *hw_priv = local->hw_priv;
272
273	printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n",
274	       dev_info);
275
276	/* Set sreset bit of COR and clear it after hold time */
277
278	if (hw_priv->attr_mem == NULL) {
279		/* TMD7160 - COR at card's first I/O addr */
280		corsave = inb(hw_priv->cor_offset);
281		outb(corsave | COR_SRESET, hw_priv->cor_offset);
282		mdelay(2);
283		outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
284		mdelay(2);
285	} else {
286		/* PLX9052 */
287		corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
288		writeb(corsave | COR_SRESET,
289		       hw_priv->attr_mem + hw_priv->cor_offset);
290		mdelay(2);
291		writeb(corsave & ~COR_SRESET,
292		       hw_priv->attr_mem + hw_priv->cor_offset);
293		mdelay(2);
294	}
295}
296
297
298static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
299{
300	unsigned char corsave;
301	struct hostap_plx_priv *hw_priv = local->hw_priv;
302
303	if (hw_priv->attr_mem == NULL) {
304		/* TMD7160 - COR at card's first I/O addr */
305		corsave = inb(hw_priv->cor_offset);
306		outb(corsave | COR_SRESET, hw_priv->cor_offset);
307		mdelay(10);
308		outb(hcr, hw_priv->cor_offset + 2);
309		mdelay(10);
310		outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
311		mdelay(10);
312	} else {
313		/* PLX9052 */
314		corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
315		writeb(corsave | COR_SRESET,
316		       hw_priv->attr_mem + hw_priv->cor_offset);
317		mdelay(10);
318		writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2);
319		mdelay(10);
320		writeb(corsave & ~COR_SRESET,
321		       hw_priv->attr_mem + hw_priv->cor_offset);
322		mdelay(10);
323	}
324}
325
326
327static struct prism2_helper_functions prism2_plx_funcs =
328{
329	.card_present	= NULL,
330	.cor_sreset	= prism2_plx_cor_sreset,
331	.genesis_reset	= prism2_plx_genesis_reset,
332	.hw_type	= HOSTAP_HW_PLX,
333};
334
335
336static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len,
337				unsigned int *cor_offset,
338				unsigned int *cor_index)
339{
340#define CISTPL_CONFIG 0x1A
341#define CISTPL_MANFID 0x20
342#define CISTPL_END 0xFF
343#define CIS_MAX_LEN 256
344	u8 *cis;
345	int i, pos;
346	unsigned int rmsz, rasz, manfid1, manfid2;
347	struct prism2_plx_manfid *manfid;
348
349	cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL);
350	if (cis == NULL)
351		return -ENOMEM;
352
353	/* read CIS; it is in even offsets in the beginning of attr_mem */
354	for (i = 0; i < CIS_MAX_LEN; i++)
355		cis[i] = readb(attr_mem + 2 * i);
356	printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n",
357	       dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]);
358
359	/* set reasonable defaults for Prism2 cards just in case CIS parsing
360	 * fails */
361	*cor_offset = 0x3e0;
362	*cor_index = 0x01;
363	manfid1 = manfid2 = 0;
364
365	pos = 0;
366	while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) {
367		if (pos + 2 + cis[pos + 1] > CIS_MAX_LEN)
368			goto cis_error;
369
370		switch (cis[pos]) {
371		case CISTPL_CONFIG:
372			if (cis[pos + 1] < 2)
373				goto cis_error;
374			rmsz = (cis[pos + 2] & 0x3c) >> 2;
375			rasz = cis[pos + 2] & 0x03;
376			if (4 + rasz + rmsz > cis[pos + 1])
377				goto cis_error;
378			*cor_index = cis[pos + 3] & 0x3F;
379			*cor_offset = 0;
380			for (i = 0; i <= rasz; i++)
381				*cor_offset += cis[pos + 4 + i] << (8 * i);
382			printk(KERN_DEBUG "%s: cor_index=0x%x "
383			       "cor_offset=0x%x\n", dev_info,
384			       *cor_index, *cor_offset);
385			if (*cor_offset > attr_len) {
386				printk(KERN_ERR "%s: COR offset not within "
387				       "attr_mem\n", dev_info);
388				kfree(cis);
389				return -1;
390			}
391			break;
392
393		case CISTPL_MANFID:
394			if (cis[pos + 1] < 4)
395				goto cis_error;
396			manfid1 = cis[pos + 2] + (cis[pos + 3] << 8);
397			manfid2 = cis[pos + 4] + (cis[pos + 5] << 8);
398			printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n",
399			       dev_info, manfid1, manfid2);
400			break;
401		}
402
403		pos += cis[pos + 1] + 2;
404	}
405
406	if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END)
407		goto cis_error;
408
409	for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++)
410		if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) {
411			kfree(cis);
412			return 0;
413		}
414
415	printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is"
416	       " not supported card\n", dev_info, manfid1, manfid2);
417	goto fail;
418
419 cis_error:
420	printk(KERN_WARNING "%s: invalid CIS data\n", dev_info);
421
422 fail:
423	kfree(cis);
424	if (ignore_cis) {
425		printk(KERN_INFO "%s: ignore_cis parameter set - ignoring "
426		       "errors during CIS verification\n", dev_info);
427		return 0;
428	}
429	return -1;
430}
431
432
433static int prism2_plx_probe(struct pci_dev *pdev,
434			    const struct pci_device_id *id)
435{
436	unsigned int pccard_ioaddr, plx_ioaddr;
437	unsigned long pccard_attr_mem;
438	unsigned int pccard_attr_len;
439	void __iomem *attr_mem = NULL;
440	unsigned int cor_offset, cor_index;
441	u32 reg;
442	local_info_t *local = NULL;
443	struct net_device *dev = NULL;
444	struct hostap_interface *iface;
445	static int cards_found /* = 0 */;
446	int irq_registered = 0;
447	int tmd7160;
448	struct hostap_plx_priv *hw_priv;
449
450	hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
451	if (hw_priv == NULL)
452		return -ENOMEM;
453
454	if (pci_enable_device(pdev))
455		goto err_out_free;
456
457	/* National Datacomm NCP130 based on TMD7160, not PLX9052. */
458	tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
459
460	plx_ioaddr = pci_resource_start(pdev, 1);
461	pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
462
463	if (tmd7160) {
464		/* TMD7160 */
465		attr_mem = NULL; /* no access to PC Card attribute memory */
466
467		printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
468		       "irq=%d, pccard_io=0x%x\n",
469		       plx_ioaddr, pdev->irq, pccard_ioaddr);
470
471		cor_offset = plx_ioaddr;
472		cor_index = 0x04;
473
474		outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
475		mdelay(1);
476		reg = inb(plx_ioaddr);
477		if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
478			printk(KERN_ERR "%s: Error setting COR (expected="
479			       "0x%02x, was=0x%02x)\n", dev_info,
480			       cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
481			goto fail;
482		}
483	} else {
484		/* PLX9052 */
485		pccard_attr_mem = pci_resource_start(pdev, 2);
486		pccard_attr_len = pci_resource_len(pdev, 2);
487		if (pccard_attr_len < PLX_MIN_ATTR_LEN)
488			goto fail;
489
490
491		attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
492		if (attr_mem == NULL) {
493			printk(KERN_ERR "%s: cannot remap attr_mem\n",
494			       dev_info);
495			goto fail;
496		}
497
498		printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
499		       "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
500		       pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
501
502		if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
503					 &cor_offset, &cor_index)) {
504			printk(KERN_INFO "Unknown PC Card CIS - not a "
505			       "Prism2/2.5 card?\n");
506			goto fail;
507		}
508
509		printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
510		       "adapter\n");
511
512		/* Write COR to enable PC Card */
513		writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
514		       attr_mem + cor_offset);
515
516		/* Enable PCI interrupts if they are not already enabled */
517		reg = inl(plx_ioaddr + PLX_INTCSR);
518		printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
519		if (!(reg & PLX_INTCSR_PCI_INTEN)) {
520			outl(reg | PLX_INTCSR_PCI_INTEN,
521			     plx_ioaddr + PLX_INTCSR);
522			if (!(inl(plx_ioaddr + PLX_INTCSR) &
523			      PLX_INTCSR_PCI_INTEN)) {
524				printk(KERN_WARNING "%s: Could not enable "
525				       "Local Interrupts\n", dev_info);
526				goto fail;
527			}
528		}
529
530		reg = inl(plx_ioaddr + PLX_CNTRL);
531		printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
532		       "present=%d)\n",
533		       reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
534		/* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
535		 * not present; but are there really such cards in use(?) */
536	}
537
538	dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
539				     &pdev->dev);
540	if (dev == NULL)
541		goto fail;
542	iface = netdev_priv(dev);
543	local = iface->local;
544	local->hw_priv = hw_priv;
545	cards_found++;
546
547	dev->irq = pdev->irq;
548	dev->base_addr = pccard_ioaddr;
549	hw_priv->attr_mem = attr_mem;
550	hw_priv->cor_offset = cor_offset;
551
552	pci_set_drvdata(pdev, dev);
553
554	if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name,
555			dev)) {
556		printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
557		goto fail;
558	} else
559		irq_registered = 1;
560
561	if (prism2_hw_config(dev, 1)) {
562		printk(KERN_DEBUG "%s: hardware initialization failed\n",
563		       dev_info);
564		goto fail;
565	}
566
567	return hostap_hw_ready(dev);
568
569 fail:
570	if (irq_registered && dev)
571		free_irq(dev->irq, dev);
572
573	if (attr_mem)
574		iounmap(attr_mem);
575
576	pci_disable_device(pdev);
577	prism2_free_local_data(dev);
578
579 err_out_free:
580	kfree(hw_priv);
581
582	return -ENODEV;
583}
584
585
586static void prism2_plx_remove(struct pci_dev *pdev)
587{
588	struct net_device *dev;
589	struct hostap_interface *iface;
590	struct hostap_plx_priv *hw_priv;
591
592	dev = pci_get_drvdata(pdev);
593	iface = netdev_priv(dev);
594	hw_priv = iface->local->hw_priv;
595
596	/* Reset the hardware, and ensure interrupts are disabled. */
597	prism2_plx_cor_sreset(iface->local);
598	hfa384x_disable_interrupts(dev);
599
600	if (hw_priv->attr_mem)
601		iounmap(hw_priv->attr_mem);
602	if (dev->irq)
603		free_irq(dev->irq, dev);
604
605	prism2_free_local_data(dev);
606	kfree(hw_priv);
607	pci_disable_device(pdev);
608}
609
610
611MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
612
613static struct pci_driver prism2_plx_drv_id = {
614	.name		= "hostap_plx",
615	.id_table	= prism2_plx_id_table,
616	.probe		= prism2_plx_probe,
617	.remove		= prism2_plx_remove,
618	.suspend	= NULL,
619	.resume		= NULL,
620	.enable_wake	= NULL
621};
622
623
624static int __init init_prism2_plx(void)
625{
626	printk(KERN_INFO "%s: %s\n", dev_info, version);
627
628	return pci_register_driver(&prism2_plx_drv_id);
629}
630
631
632static void __exit exit_prism2_plx(void)
633{
634	pci_unregister_driver(&prism2_plx_drv_id);
635	printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
636}
637
638
639module_init(init_prism2_plx);
640module_exit(exit_prism2_plx);
641