Merge git://git.linux-nfs.org/pub/linux/nfs-2.6
[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/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 + 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] < 5)
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 = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
451         if (hw_priv == NULL)
452                 return -ENOMEM;
453         memset(hw_priv, 0, sizeof(*hw_priv));
454
455         if (pci_enable_device(pdev))
456                 goto err_out_free;
457
458         /* National Datacomm NCP130 based on TMD7160, not PLX9052. */
459         tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
460
461         plx_ioaddr = pci_resource_start(pdev, 1);
462         pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
463
464         if (tmd7160) {
465                 /* TMD7160 */
466                 attr_mem = NULL; /* no access to PC Card attribute memory */
467
468                 printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
469                        "irq=%d, pccard_io=0x%x\n",
470                        plx_ioaddr, pdev->irq, pccard_ioaddr);
471
472                 cor_offset = plx_ioaddr;
473                 cor_index = 0x04;
474
475                 outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
476                 mdelay(1);
477                 reg = inb(plx_ioaddr);
478                 if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
479                         printk(KERN_ERR "%s: Error setting COR (expected="
480                                "0x%02x, was=0x%02x)\n", dev_info,
481                                cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
482                         goto fail;
483                 }
484         } else {
485                 /* PLX9052 */
486                 pccard_attr_mem = pci_resource_start(pdev, 2);
487                 pccard_attr_len = pci_resource_len(pdev, 2);
488                 if (pccard_attr_len < PLX_MIN_ATTR_LEN)
489                         goto fail;
490
491
492                 attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
493                 if (attr_mem == NULL) {
494                         printk(KERN_ERR "%s: cannot remap attr_mem\n",
495                                dev_info);
496                         goto fail;
497                 }
498
499                 printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
500                        "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
501                        pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
502
503                 if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
504                                          &cor_offset, &cor_index)) {
505                         printk(KERN_INFO "Unknown PC Card CIS - not a "
506                                "Prism2/2.5 card?\n");
507                         goto fail;
508                 }
509
510                 printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
511                        "adapter\n");
512
513                 /* Write COR to enable PC Card */
514                 writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
515                        attr_mem + cor_offset);
516
517                 /* Enable PCI interrupts if they are not already enabled */
518                 reg = inl(plx_ioaddr + PLX_INTCSR);
519                 printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
520                 if (!(reg & PLX_INTCSR_PCI_INTEN)) {
521                         outl(reg | PLX_INTCSR_PCI_INTEN,
522                              plx_ioaddr + PLX_INTCSR);
523                         if (!(inl(plx_ioaddr + PLX_INTCSR) &
524                               PLX_INTCSR_PCI_INTEN)) {
525                                 printk(KERN_WARNING "%s: Could not enable "
526                                        "Local Interrupts\n", dev_info);
527                                 goto fail;
528                         }
529                 }
530
531                 reg = inl(plx_ioaddr + PLX_CNTRL);
532                 printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
533                        "present=%d)\n",
534                        reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
535                 /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
536                  * not present; but are there really such cards in use(?) */
537         }
538
539         dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
540                                      &pdev->dev);
541         if (dev == NULL)
542                 goto fail;
543         iface = netdev_priv(dev);
544         local = iface->local;
545         local->hw_priv = hw_priv;
546         cards_found++;
547
548         dev->irq = pdev->irq;
549         dev->base_addr = pccard_ioaddr;
550         hw_priv->attr_mem = attr_mem;
551         hw_priv->cor_offset = cor_offset;
552
553         pci_set_drvdata(pdev, dev);
554
555         if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name,
556                         dev)) {
557                 printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
558                 goto fail;
559         } else
560                 irq_registered = 1;
561
562         if (prism2_hw_config(dev, 1)) {
563                 printk(KERN_DEBUG "%s: hardware initialization failed\n",
564                        dev_info);
565                 goto fail;
566         }
567
568         return hostap_hw_ready(dev);
569
570  fail:
571         if (irq_registered && dev)
572                 free_irq(dev->irq, dev);
573
574         if (attr_mem)
575                 iounmap(attr_mem);
576
577         pci_disable_device(pdev);
578         prism2_free_local_data(dev);
579
580  err_out_free:
581         kfree(hw_priv);
582
583         return -ENODEV;
584 }
585
586
587 static void prism2_plx_remove(struct pci_dev *pdev)
588 {
589         struct net_device *dev;
590         struct hostap_interface *iface;
591         struct hostap_plx_priv *hw_priv;
592
593         dev = pci_get_drvdata(pdev);
594         iface = netdev_priv(dev);
595         hw_priv = iface->local->hw_priv;
596
597         /* Reset the hardware, and ensure interrupts are disabled. */
598         prism2_plx_cor_sreset(iface->local);
599         hfa384x_disable_interrupts(dev);
600
601         if (hw_priv->attr_mem)
602                 iounmap(hw_priv->attr_mem);
603         if (dev->irq)
604                 free_irq(dev->irq, dev);
605
606         prism2_free_local_data(dev);
607         kfree(hw_priv);
608         pci_disable_device(pdev);
609 }
610
611
612 MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
613
614 static struct pci_driver prism2_plx_drv_id = {
615         .name           = "hostap_plx",
616         .id_table       = prism2_plx_id_table,
617         .probe          = prism2_plx_probe,
618         .remove         = prism2_plx_remove,
619         .suspend        = NULL,
620         .resume         = NULL,
621         .enable_wake    = NULL
622 };
623
624
625 static int __init init_prism2_plx(void)
626 {
627         printk(KERN_INFO "%s: %s\n", dev_info, version);
628
629         return pci_register_driver(&prism2_plx_drv_id);
630 }
631
632
633 static void __exit exit_prism2_plx(void)
634 {
635         pci_unregister_driver(&prism2_plx_drv_id);
636         printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
637 }
638
639
640 module_init(init_prism2_plx);
641 module_exit(exit_prism2_plx);