[PATCH] Update my email address from jkmaline@cc.hut.fi to j@w1.fi
[linux-2.6-block.git] / drivers / net / wireless / hostap / hostap_plx.c
CommitLineData
ff1d2767
JM
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
ff1d2767
JM
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
85d32e7b 26static char *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)";
ff1d2767
JM
27static char *dev_info = "hostap_plx";
28
29
30MODULE_AUTHOR("Jouni Malinen");
31MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
32 "cards (PLX).");
33MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
34MODULE_LICENSE("GPL");
f06ac319 35MODULE_VERSION(PRISM2_VERSION);
ff1d2767
JM
36
37
38static int ignore_cis;
39module_param(ignore_cis, int, 0444);
40MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
41
42
67e0e473
JM
43/* struct local_info::hw_priv */
44struct hostap_plx_priv {
45 void __iomem *attr_mem;
46 unsigned int cor_offset;
47};
48
49
ff1d2767
JM
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
65static 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"),
d3cef4ee 69 PLXDEV(0x1562, 0x0001, "Symbol LA-4123"),
ff1d2767
JM
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"),
d3cef4ee 74 PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"),
ff1d2767
JM
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. */
87static 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
110static 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
125static 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
142static 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
157static 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
174static 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
190static 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
225static 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
245static 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
268static void prism2_plx_cor_sreset(local_info_t *local)
269{
270 unsigned char corsave;
67e0e473 271 struct hostap_plx_priv *hw_priv = local->hw_priv;
ff1d2767
JM
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
67e0e473 278 if (hw_priv->attr_mem == NULL) {
ff1d2767 279 /* TMD7160 - COR at card's first I/O addr */
67e0e473
JM
280 corsave = inb(hw_priv->cor_offset);
281 outb(corsave | COR_SRESET, hw_priv->cor_offset);
ff1d2767 282 mdelay(2);
67e0e473 283 outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
ff1d2767
JM
284 mdelay(2);
285 } else {
286 /* PLX9052 */
67e0e473 287 corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
ff1d2767 288 writeb(corsave | COR_SRESET,
67e0e473 289 hw_priv->attr_mem + hw_priv->cor_offset);
ff1d2767
JM
290 mdelay(2);
291 writeb(corsave & ~COR_SRESET,
67e0e473 292 hw_priv->attr_mem + hw_priv->cor_offset);
ff1d2767
JM
293 mdelay(2);
294 }
295}
296
297
298static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
299{
300 unsigned char corsave;
67e0e473 301 struct hostap_plx_priv *hw_priv = local->hw_priv;
ff1d2767 302
67e0e473 303 if (hw_priv->attr_mem == NULL) {
ff1d2767 304 /* TMD7160 - COR at card's first I/O addr */
67e0e473
JM
305 corsave = inb(hw_priv->cor_offset);
306 outb(corsave | COR_SRESET, hw_priv->cor_offset);
ff1d2767 307 mdelay(10);
67e0e473 308 outb(hcr, hw_priv->cor_offset + 2);
ff1d2767 309 mdelay(10);
67e0e473 310 outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
ff1d2767
JM
311 mdelay(10);
312 } else {
313 /* PLX9052 */
67e0e473 314 corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
ff1d2767 315 writeb(corsave | COR_SRESET,
67e0e473 316 hw_priv->attr_mem + hw_priv->cor_offset);
ff1d2767 317 mdelay(10);
67e0e473 318 writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2);
ff1d2767
JM
319 mdelay(10);
320 writeb(corsave & ~COR_SRESET,
67e0e473 321 hw_priv->attr_mem + hw_priv->cor_offset);
ff1d2767
JM
322 mdelay(10);
323 }
324}
325
326
327static struct prism2_helper_functions prism2_plx_funcs =
328{
329 .card_present = NULL,
330 .cor_sreset = prism2_plx_cor_sreset,
ff1d2767
JM
331 .genesis_reset = prism2_plx_genesis_reset,
332 .hw_type = HOSTAP_HW_PLX,
333};
334
335
336static 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) {
115e222d 367 if (pos + 2 + cis[pos + 1] > CIS_MAX_LEN)
ff1d2767
JM
368 goto cis_error;
369
370 switch (cis[pos]) {
371 case CISTPL_CONFIG:
3a1c42ad 372 if (cis[pos + 1] < 2)
ff1d2767
JM
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:
115e222d 394 if (cis[pos + 1] < 4)
ff1d2767
JM
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
433static 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;
67e0e473
JM
448 struct hostap_plx_priv *hw_priv;
449
b0471bb7 450 hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
67e0e473
JM
451 if (hw_priv == NULL)
452 return -ENOMEM;
ff1d2767
JM
453
454 if (pci_enable_device(pdev))
93201999 455 goto err_out_free;
ff1d2767
JM
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
0cd545d6
DH
538 dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
539 &pdev->dev);
ff1d2767
JM
540 if (dev == NULL)
541 goto fail;
542 iface = netdev_priv(dev);
543 local = iface->local;
67e0e473 544 local->hw_priv = hw_priv;
ff1d2767
JM
545 cards_found++;
546
547 dev->irq = pdev->irq;
548 dev->base_addr = pccard_ioaddr;
67e0e473
JM
549 hw_priv->attr_mem = attr_mem;
550 hw_priv->cor_offset = cor_offset;
ff1d2767
JM
551
552 pci_set_drvdata(pdev, dev);
553
1fb9df5d 554 if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name,
ff1d2767
JM
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:
ff1d2767
JM
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);
93201999
JM
577 prism2_free_local_data(dev);
578
579 err_out_free:
580 kfree(hw_priv);
ff1d2767
JM
581
582 return -ENODEV;
583}
584
585
586static void prism2_plx_remove(struct pci_dev *pdev)
587{
588 struct net_device *dev;
589 struct hostap_interface *iface;
67e0e473 590 struct hostap_plx_priv *hw_priv;
ff1d2767
JM
591
592 dev = pci_get_drvdata(pdev);
593 iface = netdev_priv(dev);
67e0e473 594 hw_priv = iface->local->hw_priv;
ff1d2767
JM
595
596 /* Reset the hardware, and ensure interrupts are disabled. */
597 prism2_plx_cor_sreset(iface->local);
598 hfa384x_disable_interrupts(dev);
599
67e0e473
JM
600 if (hw_priv->attr_mem)
601 iounmap(hw_priv->attr_mem);
ff1d2767
JM
602 if (dev->irq)
603 free_irq(dev->irq, dev);
604
605 prism2_free_local_data(dev);
c355184c 606 kfree(hw_priv);
ff1d2767
JM
607 pci_disable_device(pdev);
608}
609
610
611MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
612
613static struct pci_driver prism2_plx_drv_id = {
7a716536 614 .name = "hostap_plx",
ff1d2767
JM
615 .id_table = prism2_plx_id_table,
616 .probe = prism2_plx_probe,
617 .remove = prism2_plx_remove,
618 .suspend = NULL,
619 .resume = NULL,
620 .enable_wake = NULL
621};
622
623
624static int __init init_prism2_plx(void)
625{
626 printk(KERN_INFO "%s: %s\n", dev_info, version);
627
628 return pci_register_driver(&prism2_plx_drv_id);
629}
630
631
632static void __exit exit_prism2_plx(void)
633{
634 pci_unregister_driver(&prism2_plx_drv_id);
635 printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
636}
637
638
639module_init(init_prism2_plx);
640module_exit(exit_prism2_plx);