hostap_plx.c revision 9320199957cebc39ccef372fa1fccf5ba3d3fd7d
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/config.h>
11#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/if.h>
14#include <linux/skbuff.h>
15#include <linux/netdevice.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 *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
28static char *dev_info = "hostap_plx";
29
30
31MODULE_AUTHOR("Jouni Malinen");
32MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
33		   "cards (PLX).");
34MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
35MODULE_LICENSE("GPL");
36MODULE_VERSION(PRISM2_VERSION);
37
38
39static int ignore_cis;
40module_param(ignore_cis, int, 0444);
41MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
42
43
44/* struct local_info::hw_priv */
45struct hostap_plx_priv {
46	void __iomem *attr_mem;
47	unsigned int cor_offset;
48};
49
50
51#define PLX_MIN_ATTR_LEN 512	/* at least 2 x 256 is needed for CIS */
52#define COR_SRESET       0x80
53#define COR_LEVLREQ      0x40
54#define COR_ENABLE_FUNC  0x01
55/* PCI Configuration Registers */
56#define PLX_PCIIPR       0x3d   /* PCI Interrupt Pin */
57/* Local Configuration Registers */
58#define PLX_INTCSR       0x4c   /* Interrupt Control/Status Register */
59#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */
60#define PLX_CNTRL        0x50
61#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
62
63
64#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
65
66static struct pci_device_id prism2_plx_id_table[] __devinitdata = {
67	PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
68	PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
69	PLXDEV(0x126c, 0x8030, "Nortel emobility"),
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, 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 + 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] < 5)
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, cor_index;
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 = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
450	if (hw_priv == NULL)
451		return -ENOMEM;
452	memset(hw_priv, 0, sizeof(*hw_priv));
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, SA_SHIRQ, 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