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