1 // SPDX-License-Identifier: GPL-2.0-only 2 #define PRISM2_PLX 3 4 /* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is 5 * based on: 6 * - Host AP driver patch from james@madingley.org 7 * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc. 8 */ 9 10 11 #include <linux/module.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 */ 43 struct hostap_plx_priv { 44 void __iomem *attr_mem; 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 const struct pci_device_id 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); 620