hostap_plx.c revision 6ab3d5624e172c553004ecc862bfeac16d9d68b7
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 26static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)"; 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"); 35MODULE_VERSION(PRISM2_VERSION); 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 43/* struct local_info::hw_priv */ 44struct 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 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"), 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, 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; 269 struct hostap_plx_priv *hw_priv = local->hw_priv; 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 276 if (hw_priv->attr_mem == NULL) { 277 /* TMD7160 - COR at card's first I/O addr */ 278 corsave = inb(hw_priv->cor_offset); 279 outb(corsave | COR_SRESET, hw_priv->cor_offset); 280 mdelay(2); 281 outb(corsave & ~COR_SRESET, hw_priv->cor_offset); 282 mdelay(2); 283 } else { 284 /* PLX9052 */ 285 corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset); 286 writeb(corsave | COR_SRESET, 287 hw_priv->attr_mem + hw_priv->cor_offset); 288 mdelay(2); 289 writeb(corsave & ~COR_SRESET, 290 hw_priv->attr_mem + hw_priv->cor_offset); 291 mdelay(2); 292 } 293} 294 295 296static void prism2_plx_genesis_reset(local_info_t *local, int hcr) 297{ 298 unsigned char corsave; 299 struct hostap_plx_priv *hw_priv = local->hw_priv; 300 301 if (hw_priv->attr_mem == NULL) { 302 /* TMD7160 - COR at card's first I/O addr */ 303 corsave = inb(hw_priv->cor_offset); 304 outb(corsave | COR_SRESET, hw_priv->cor_offset); 305 mdelay(10); 306 outb(hcr, hw_priv->cor_offset + 2); 307 mdelay(10); 308 outb(corsave & ~COR_SRESET, hw_priv->cor_offset); 309 mdelay(10); 310 } else { 311 /* PLX9052 */ 312 corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset); 313 writeb(corsave | COR_SRESET, 314 hw_priv->attr_mem + hw_priv->cor_offset); 315 mdelay(10); 316 writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2); 317 mdelay(10); 318 writeb(corsave & ~COR_SRESET, 319 hw_priv->attr_mem + hw_priv->cor_offset); 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, 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) { 365 if (pos + cis[pos + 1] >= CIS_MAX_LEN) 366 goto cis_error; 367 368 switch (cis[pos]) { 369 case CISTPL_CONFIG: 370 if (cis[pos + 1] < 2) 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: 392 if (cis[pos + 1] < 5) 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; 438 unsigned int cor_offset, cor_index; 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; 446 struct hostap_plx_priv *hw_priv; 447 448 hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL); 449 if (hw_priv == NULL) 450 return -ENOMEM; 451 memset(hw_priv, 0, sizeof(*hw_priv)); 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, SA_SHIRQ, 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 585static 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 610MODULE_DEVICE_TABLE(pci, prism2_plx_id_table); 611 612static struct pci_driver prism2_plx_drv_id = { 613 .name = "hostap_plx", 614 .id_table = prism2_plx_id_table, 615 .probe = prism2_plx_probe, 616 .remove = prism2_plx_remove, 617 .suspend = NULL, 618 .resume = NULL, 619 .enable_wake = NULL 620}; 621 622 623static int __init init_prism2_plx(void) 624{ 625 printk(KERN_INFO "%s: %s\n", dev_info, version); 626 627 return pci_register_driver(&prism2_plx_drv_id); 628} 629 630 631static void __exit exit_prism2_plx(void) 632{ 633 pci_unregister_driver(&prism2_plx_drv_id); 634 printk(KERN_INFO "%s: Driver unloaded\n", dev_info); 635} 636 637 638module_init(init_prism2_plx); 639module_exit(exit_prism2_plx); 640