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