Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
hostap_plx.c
Go to the documentation of this file.
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 [email protected]
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/slab.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 *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 
36 
37 static int ignore_cis;
38 module_param(ignore_cis, int, 0444);
39 MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
40 
41 
42 /* struct local_info::hw_priv */
45  unsigned int cor_offset;
46 };
47 
48 
49 #define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */
50 #define COR_SRESET 0x80
51 #define COR_LEVLREQ 0x40
52 #define COR_ENABLE_FUNC 0x01
53 /* PCI Configuration Registers */
54 #define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */
55 /* Local Configuration Registers */
56 #define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */
57 #define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */
58 #define PLX_CNTRL 0x50
59 #define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
60 
61 
62 #define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
63 
64 static DEFINE_PCI_DEVICE_TABLE(prism2_plx_id_table) = {
65  PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
66  PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
67  PLXDEV(0x126c, 0x8030, "Nortel emobility"),
68  PLXDEV(0x1562, 0x0001, "Symbol LA-4123"),
69  PLXDEV(0x1385, 0x4100, "Netgear MA301"),
70  PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
71  PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
72  PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
73  PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"),
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 + 2 + cis[pos + 1] > CIS_MAX_LEN)
367  goto cis_error;
368 
369  switch (cis[pos]) {
370  case CISTPL_CONFIG:
371  if (cis[pos + 1] < 2)
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 = 0, cor_index = 0;
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 = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
450  if (hw_priv == NULL)
451  return -ENOMEM;
452 
453  if (pci_enable_device(pdev))
454  goto err_out_free;
455 
456  /* National Datacomm NCP130 based on TMD7160, not PLX9052. */
457  tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
458 
459  plx_ioaddr = pci_resource_start(pdev, 1);
460  pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
461 
462  if (tmd7160) {
463  /* TMD7160 */
464  attr_mem = NULL; /* no access to PC Card attribute memory */
465 
466  printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
467  "irq=%d, pccard_io=0x%x\n",
468  plx_ioaddr, pdev->irq, pccard_ioaddr);
469 
470  cor_offset = plx_ioaddr;
471  cor_index = 0x04;
472 
473  outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
474  mdelay(1);
475  reg = inb(plx_ioaddr);
476  if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
477  printk(KERN_ERR "%s: Error setting COR (expected="
478  "0x%02x, was=0x%02x)\n", dev_info,
479  cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
480  goto fail;
481  }
482  } else {
483  /* PLX9052 */
484  pccard_attr_mem = pci_resource_start(pdev, 2);
485  pccard_attr_len = pci_resource_len(pdev, 2);
486  if (pccard_attr_len < PLX_MIN_ATTR_LEN)
487  goto fail;
488 
489 
490  attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
491  if (attr_mem == NULL) {
492  printk(KERN_ERR "%s: cannot remap attr_mem\n",
493  dev_info);
494  goto fail;
495  }
496 
497  printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
498  "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
499  pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
500 
501  if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
502  &cor_offset, &cor_index)) {
503  printk(KERN_INFO "Unknown PC Card CIS - not a "
504  "Prism2/2.5 card?\n");
505  goto fail;
506  }
507 
508  printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
509  "adapter\n");
510 
511  /* Write COR to enable PC Card */
512  writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
513  attr_mem + cor_offset);
514 
515  /* Enable PCI interrupts if they are not already enabled */
516  reg = inl(plx_ioaddr + PLX_INTCSR);
517  printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
518  if (!(reg & PLX_INTCSR_PCI_INTEN)) {
519  outl(reg | PLX_INTCSR_PCI_INTEN,
520  plx_ioaddr + PLX_INTCSR);
521  if (!(inl(plx_ioaddr + PLX_INTCSR) &
522  PLX_INTCSR_PCI_INTEN)) {
523  printk(KERN_WARNING "%s: Could not enable "
524  "Local Interrupts\n", dev_info);
525  goto fail;
526  }
527  }
528 
529  reg = inl(plx_ioaddr + PLX_CNTRL);
530  printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
531  "present=%d)\n",
532  reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
533  /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
534  * not present; but are there really such cards in use(?) */
535  }
536 
537  dev = prism2_init_local_data(&prism2_plx_funcs, cards_found,
538  &pdev->dev);
539  if (dev == NULL)
540  goto fail;
541  iface = netdev_priv(dev);
542  local = iface->local;
543  local->hw_priv = hw_priv;
544  cards_found++;
545 
546  dev->irq = pdev->irq;
547  dev->base_addr = pccard_ioaddr;
548  hw_priv->attr_mem = attr_mem;
549  hw_priv->cor_offset = cor_offset;
550 
551  pci_set_drvdata(pdev, dev);
552 
553  if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name,
554  dev)) {
555  printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
556  goto fail;
557  } else
558  irq_registered = 1;
559 
560  if (prism2_hw_config(dev, 1)) {
561  printk(KERN_DEBUG "%s: hardware initialization failed\n",
562  dev_info);
563  goto fail;
564  }
565 
566  return hostap_hw_ready(dev);
567 
568  fail:
569  if (irq_registered && dev)
570  free_irq(dev->irq, dev);
571 
572  if (attr_mem)
573  iounmap(attr_mem);
574 
575  pci_disable_device(pdev);
576  prism2_free_local_data(dev);
577 
578  err_out_free:
579  kfree(hw_priv);
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_driver = {
613  .name = "hostap_plx",
614  .id_table = prism2_plx_id_table,
615  .probe = prism2_plx_probe,
616  .remove = prism2_plx_remove,
617 };
618 
619 module_pci_driver(prism2_plx_driver);