fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.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/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
26 static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
27 static char *dev_info = "hostap_plx";
28
29
30 MODULE_AUTHOR("Jouni Malinen");
31 MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
32                    "cards (PLX).");
33 MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
34 MODULE_LICENSE("GPL");
35 MODULE_VERSION(PRISM2_VERSION);
36
37
38 static int ignore_cis;
39 module_param(ignore_cis, int, 0444);
40 MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
41
42
43 /* struct local_info::hw_priv */
44 struct 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
65 static 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. */
87 static 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
110 static 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
125 static 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
142 static 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
157 static 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
174 static 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
190 static 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
225 static 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
245 static 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
268 static 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
298 static 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
327 static 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
336 static 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
433 static 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
586 static 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
611 MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
612
613 static 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
624 static 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
632 static 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
639 module_init(init_prism2_plx);
640 module_exit(exit_prism2_plx);