Merge with http://kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
[linux-2.6] / 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/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
27 static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
28 static char *dev_info = "hostap_plx";
29
30
31 MODULE_AUTHOR("Jouni Malinen");
32 MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
33                    "cards (PLX).");
34 MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
35 MODULE_LICENSE("GPL");
36 MODULE_VERSION(PRISM2_VERSION);
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 /* struct local_info::hw_priv */
45 struct 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
66 static 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. */
86 static 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
109 static 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
124 static 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
141 static 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
156 static 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
173 static 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
189 static 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
224 static 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
244 static 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
267 static 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
297 static 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
326 static 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
335 static 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] < 1)
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
432 static 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                 return -EIO;
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         prism2_free_local_data(dev);
571         kfree(hw_priv);
572
573         if (irq_registered && dev)
574                 free_irq(dev->irq, dev);
575
576         if (attr_mem)
577                 iounmap(attr_mem);
578
579         pci_disable_device(pdev);
580
581         return -ENODEV;
582 }
583
584
585 static 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
610 MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
611
612 static struct pci_driver prism2_plx_drv_id = {
613         .name           = "hostap_plx",
614         .id_table       = prism2_plx_id_table,
615         .probe          = prism2_plx_probe,
616         .remove         = prism2_plx_remove,
617         .suspend        = NULL,
618         .resume         = NULL,
619         .enable_wake    = NULL
620 };
621
622
623 static int __init init_prism2_plx(void)
624 {
625         printk(KERN_INFO "%s: %s\n", dev_info, version);
626
627         return pci_register_driver(&prism2_plx_drv_id);
628 }
629
630
631 static void __exit exit_prism2_plx(void)
632 {
633         pci_unregister_driver(&prism2_plx_drv_id);
634         printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
635 }
636
637
638 module_init(init_prism2_plx);
639 module_exit(exit_prism2_plx);