00001 #define PRISM2_PLX
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <linux/config.h>
00011 #include <linux/version.h>
00012 #include <linux/module.h>
00013 #include <linux/init.h>
00014 #include <linux/if.h>
00015 #include <linux/skbuff.h>
00016 #include <linux/netdevice.h>
00017 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44))
00018 #include <linux/tqueue.h>
00019 #else
00020 #include <linux/workqueue.h>
00021 #endif
00022 #include "hostap_wext.h"
00023
00024 #include <linux/ioport.h>
00025 #include <linux/pci.h>
00026
00027 #include "hostap_wlan.h"
00028
00029
00030 static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
00031 static char *dev_info = "hostap_plx";
00032
00033
00034 MODULE_AUTHOR("SSH Communications Security Corp, Jouni Malinen");
00035 MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
00036 "cards (PLX).");
00037 MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
00038 MODULE_LICENSE("GPL");
00039
00040
00041 static int ignore_cis = 0;
00042 MODULE_PARM(ignore_cis, "i");
00043 MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
00044
00045
00046 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0))
00047
00048
00049 #error PLX9052 version requires at least Linux kernel version 2.4.0
00050 #endif
00051
00052
00053 #define PLX_MIN_ATTR_LEN 512
00054 #define COR_SRESET 0x80
00055 #define COR_LEVLREQ 0x40
00056 #define COR_ENABLE_FUNC 0x01
00057
00058 #define PLX_PCIIPR 0x3d
00059
00060 #define PLX_INTCSR 0x4c
00061 #define PLX_INTCSR_PCI_INTEN BIT(6)
00062 #define PLX_CNTRL 0x50
00063 #define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
00064
00065
00066 #define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
00067
00068 static struct pci_device_id prism2_plx_id_table[] __devinitdata = {
00069 PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
00070 PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
00071 PLXDEV(0x126c, 0x8030, "Nortel emobility"),
00072 PLXDEV(0x1385, 0x4100, "Netgear MA301"),
00073 PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
00074 PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
00075 PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
00076 PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"),
00077 PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"),
00078 PLXDEV(0x16ab, 0x1103, "Longshine 8031"),
00079 PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"),
00080 PLXDEV(0xec80, 0xec00, "Belkin F5D6000"),
00081 { 0 }
00082 };
00083
00084
00085
00086
00087
00088 static struct prism2_plx_manfid {
00089 u16 manfid1, manfid2;
00090 } prism2_plx_known_manfids[] = {
00091 { 0x000b, 0x7300 } ,
00092 { 0x0101, 0x0777 } ,
00093 { 0x0126, 0x8000 } ,
00094 { 0x0138, 0x0002 } ,
00095 { 0x0156, 0x0002 } ,
00096 { 0x026f, 0x030b } ,
00097 { 0x0274, 0x1612 } ,
00098 { 0x0274, 0x1613 } ,
00099 { 0x028a, 0x0002 } ,
00100 { 0x0250, 0x0002 } ,
00101 { 0xc250, 0x0002 } ,
00102 { 0xd601, 0x0002 } ,
00103 { 0xd601, 0x0005 } ,
00104 { 0, 0}
00105 };
00106
00107
00108 #ifdef PRISM2_IO_DEBUG
00109
00110 static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
00111 {
00112 struct hostap_interface *iface = dev->priv;
00113 local_info_t *local = iface->local;
00114 unsigned long flags;
00115
00116 spin_lock_irqsave(&local->lock, flags);
00117 prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
00118 outb(v, dev->base_addr + a);
00119 spin_unlock_irqrestore(&local->lock, flags);
00120 }
00121
00122 static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
00123 {
00124 struct hostap_interface *iface = dev->priv;
00125 local_info_t *local = iface->local;
00126 unsigned long flags;
00127 u8 v;
00128
00129 spin_lock_irqsave(&local->lock, flags);
00130 v = inb(dev->base_addr + a);
00131 prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
00132 spin_unlock_irqrestore(&local->lock, flags);
00133 return v;
00134 }
00135
00136 static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
00137 {
00138 struct hostap_interface *iface = dev->priv;
00139 local_info_t *local = iface->local;
00140 unsigned long flags;
00141
00142 spin_lock_irqsave(&local->lock, flags);
00143 prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
00144 outw(v, dev->base_addr + a);
00145 spin_unlock_irqrestore(&local->lock, flags);
00146 }
00147
00148 static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
00149 {
00150 struct hostap_interface *iface = dev->priv;
00151 local_info_t *local = iface->local;
00152 unsigned long flags;
00153 u16 v;
00154
00155 spin_lock_irqsave(&local->lock, flags);
00156 v = inw(dev->base_addr + a);
00157 prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
00158 spin_unlock_irqrestore(&local->lock, flags);
00159 return v;
00160 }
00161
00162 static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
00163 u8 *buf, int wc)
00164 {
00165 struct hostap_interface *iface = dev->priv;
00166 local_info_t *local = iface->local;
00167 unsigned long flags;
00168
00169 spin_lock_irqsave(&local->lock, flags);
00170 prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
00171 outsw(dev->base_addr + a, buf, wc);
00172 spin_unlock_irqrestore(&local->lock, flags);
00173 }
00174
00175 static inline void hfa384x_insw_debug(struct net_device *dev, int a,
00176 u8 *buf, int wc)
00177 {
00178 struct hostap_interface *iface = dev->priv;
00179 local_info_t *local = iface->local;
00180 unsigned long flags;
00181
00182 spin_lock_irqsave(&local->lock, flags);
00183 prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
00184 insw(dev->base_addr + a, buf, wc);
00185 spin_unlock_irqrestore(&local->lock, flags);
00186 }
00187
00188 #define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
00189 #define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
00190 #define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
00191 #define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
00192 #define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
00193 #define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
00194
00195 #else
00196
00197 #define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
00198 #define HFA384X_INB(a) inb(dev->base_addr + (a))
00199 #define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
00200 #define HFA384X_INW(a) inw(dev->base_addr + (a))
00201 #define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
00202 #define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
00203
00204 #endif
00205
00206
00207 static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
00208 int len)
00209 {
00210 u16 d_off;
00211 u16 *pos;
00212
00213 d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
00214 pos = (u16 *) buf;
00215
00216 if (len / 2)
00217 HFA384X_INSW(d_off, buf, len / 2);
00218 pos += len / 2;
00219
00220 if (len & 1)
00221 *((char *) pos) = HFA384X_INB(d_off);
00222
00223 return 0;
00224 }
00225
00226
00227 static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
00228 {
00229 u16 d_off;
00230 u16 *pos;
00231
00232 d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
00233 pos = (u16 *) buf;
00234
00235 if (len / 2)
00236 HFA384X_OUTSW(d_off, buf, len / 2);
00237 pos += len / 2;
00238
00239 if (len & 1)
00240 HFA384X_OUTB(*((char *) pos), d_off);
00241
00242 return 0;
00243 }
00244
00245
00246
00247 #include "hostap_hw.c"
00248
00249
00250 static void prism2_plx_cor_sreset(local_info_t *local)
00251 {
00252 unsigned char corsave;
00253
00254 printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n",
00255 dev_info);
00256
00257
00258
00259 if (local->attr_mem == 0) {
00260
00261 corsave = inb(local->cor_offset);
00262 outb(corsave | COR_SRESET, local->cor_offset);
00263 mdelay(1);
00264 outb(corsave & ~COR_SRESET, local->cor_offset);
00265 mdelay(1);
00266 } else {
00267
00268 corsave = readb(local->attr_mem + local->cor_offset);
00269 writeb(corsave | COR_SRESET,
00270 local->attr_mem + local->cor_offset);
00271 mdelay(1);
00272 writeb(corsave & ~COR_SRESET,
00273 local->attr_mem + local->cor_offset);
00274 mdelay(1);
00275 }
00276 }
00277
00278
00279 static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
00280 {
00281 unsigned char corsave;
00282
00283 if (local->attr_mem == 0) {
00284
00285 corsave = inb(local->cor_offset);
00286 outb(corsave | COR_SRESET, local->cor_offset);
00287 mdelay(10);
00288 outb(hcr, local->cor_offset + 2);
00289 mdelay(10);
00290 outb(corsave & ~COR_SRESET, local->cor_offset);
00291 mdelay(10);
00292 } else {
00293
00294 corsave = readb(local->attr_mem + local->cor_offset);
00295 writeb(corsave | COR_SRESET,
00296 local->attr_mem + local->cor_offset);
00297 mdelay(10);
00298 writeb(hcr, local->attr_mem + local->cor_offset + 2);
00299 mdelay(10);
00300 writeb(corsave & ~COR_SRESET,
00301 local->attr_mem + local->cor_offset);
00302 mdelay(10);
00303 }
00304 }
00305
00306
00307 static struct prism2_helper_functions prism2_plx_funcs =
00308 {
00309 .card_present = NULL,
00310 .cor_sreset = prism2_plx_cor_sreset,
00311 .dev_open = NULL,
00312 .dev_close = NULL,
00313 .genesis_reset = prism2_plx_genesis_reset,
00314 };
00315
00316
00317 static int prism2_plx_check_cis(unsigned long attr_mem, int attr_len,
00318 unsigned int *cor_offset,
00319 unsigned int *cor_index)
00320 {
00321 #define CISTPL_CONFIG 0x1A
00322 #define CISTPL_MANFID 0x20
00323 #define CISTPL_END 0xFF
00324 #define CIS_MAX_LEN 256
00325 u8 cis[CIS_MAX_LEN];
00326 int i, pos;
00327 unsigned int rmsz, rasz, manfid1, manfid2;
00328 struct prism2_plx_manfid *manfid;
00329
00330
00331 for (i = 0; i < CIS_MAX_LEN; i++)
00332 cis[i] = readb(attr_mem + 2 * i);
00333 printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n",
00334 dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]);
00335
00336
00337
00338 *cor_offset = 0x3e0;
00339 *cor_index = 0x01;
00340 manfid1 = manfid2 = 0;
00341
00342 pos = 0;
00343 while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) {
00344 if (pos + cis[pos + 1] >= CIS_MAX_LEN)
00345 goto cis_error;
00346
00347 switch (cis[pos]) {
00348 case CISTPL_CONFIG:
00349 if (cis[pos + 1] < 1)
00350 goto cis_error;
00351 rmsz = (cis[pos + 2] & 0x3c) >> 2;
00352 rasz = cis[pos + 2] & 0x03;
00353 if (4 + rasz + rmsz > cis[pos + 1])
00354 goto cis_error;
00355 *cor_index = cis[pos + 3] & 0x3F;
00356 *cor_offset = 0;
00357 for (i = 0; i <= rasz; i++)
00358 *cor_offset += cis[pos + 4 + i] << (8 * i);
00359 printk(KERN_DEBUG "%s: cor_index=0x%x "
00360 "cor_offset=0x%x\n", dev_info,
00361 *cor_index, *cor_offset);
00362 if (*cor_offset > attr_len) {
00363 printk(KERN_ERR "%s: COR offset not within "
00364 "attr_mem\n", dev_info);
00365 return -1;
00366 }
00367 break;
00368
00369 case CISTPL_MANFID:
00370 if (cis[pos + 1] < 4)
00371 goto cis_error;
00372 manfid1 = cis[pos + 2] + (cis[pos + 3] << 8);
00373 manfid2 = cis[pos + 4] + (cis[pos + 5] << 8);
00374 printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n",
00375 dev_info, manfid1, manfid2);
00376 break;
00377 }
00378
00379 pos += cis[pos + 1] + 2;
00380 }
00381
00382 if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END)
00383 goto cis_error;
00384
00385 for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++)
00386 if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2)
00387 return 0;
00388
00389 printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is"
00390 " not supported card\n", dev_info, manfid1, manfid2);
00391 goto fail;
00392
00393 cis_error:
00394 printk(KERN_WARNING "%s: invalid CIS data\n", dev_info);
00395
00396 fail:
00397 if (ignore_cis) {
00398 printk(KERN_INFO "%s: ignore_cis parameter set - ignoring "
00399 "errors during CIS verification\n", dev_info);
00400 return 0;
00401 }
00402 return -1;
00403 }
00404
00405
00406 static int prism2_plx_probe(struct pci_dev *pdev,
00407 const struct pci_device_id *id)
00408 {
00409 unsigned int pccard_ioaddr, plx_ioaddr;
00410 unsigned long pccard_attr_mem;
00411 unsigned int pccard_attr_len;
00412 unsigned long attr_mem = 0;
00413 unsigned int cor_offset, cor_index;
00414 u32 reg;
00415 local_info_t *local = NULL;
00416 struct net_device *dev = NULL;
00417 struct hostap_interface *iface;
00418 static int cards_found ;
00419 int irq_registered = 0;
00420 int tmd7160;
00421
00422 if (pci_enable_device(pdev))
00423 return -EIO;
00424
00425
00426 tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
00427
00428 plx_ioaddr = pci_resource_start(pdev, 1);
00429 pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
00430
00431 if (tmd7160) {
00432
00433 attr_mem = 0;
00434
00435 printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
00436 "irq=%d, pccard_io=0x%x\n",
00437 plx_ioaddr, pdev->irq, pccard_ioaddr);
00438
00439 cor_offset = plx_ioaddr;
00440 cor_index = 0x04;
00441
00442 outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
00443 mdelay(1);
00444 reg = inb(plx_ioaddr);
00445 if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
00446 printk(KERN_ERR "%s: Error setting COR (expected="
00447 "0x%02x, was=0x%02x)\n", dev_info,
00448 cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
00449 goto fail;
00450 }
00451 } else {
00452
00453 pccard_attr_mem = pci_resource_start(pdev, 2);
00454 pccard_attr_len = pci_resource_len(pdev, 2);
00455 if (pccard_attr_len < PLX_MIN_ATTR_LEN)
00456 goto fail;
00457
00458
00459 attr_mem = (unsigned long) ioremap(pccard_attr_mem,
00460 pccard_attr_len);
00461 if (!attr_mem) {
00462 printk(KERN_ERR "%s: cannot remap attr_mem\n",
00463 dev_info);
00464 goto fail;
00465 }
00466
00467 printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
00468 "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
00469 pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
00470
00471 if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
00472 &cor_offset, &cor_index)) {
00473 printk(KERN_INFO "Unknown PC Card CIS - not a "
00474 "Prism2/2.5 card?\n");
00475 goto fail;
00476 }
00477
00478 printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
00479 "adapter\n");
00480
00481
00482 writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
00483 attr_mem + cor_offset);
00484
00485
00486 reg = inl(plx_ioaddr + PLX_INTCSR);
00487 printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
00488 if (!(reg & PLX_INTCSR_PCI_INTEN)) {
00489 outl(reg | PLX_INTCSR_PCI_INTEN,
00490 plx_ioaddr + PLX_INTCSR);
00491 if (!(inl(plx_ioaddr + PLX_INTCSR) &
00492 PLX_INTCSR_PCI_INTEN)) {
00493 printk(KERN_WARNING "%s: Could not enable "
00494 "Local Interrupts\n", dev_info);
00495 goto fail;
00496 }
00497 }
00498
00499 reg = inl(plx_ioaddr + PLX_CNTRL);
00500 printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
00501 "present=%d)\n",
00502 reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
00503
00504
00505 }
00506
00507 dev = prism2_init_local_data(&prism2_plx_funcs, cards_found);
00508 if (dev == NULL)
00509 goto fail;
00510 iface = dev->priv;
00511 local = iface->local;
00512 cards_found++;
00513
00514 dev->irq = pdev->irq;
00515 dev->base_addr = pccard_ioaddr;
00516 local->attr_mem = attr_mem;
00517 local->cor_offset = cor_offset;
00518
00519 if (prism2_init_dev(local))
00520 goto fail;
00521
00522 pci_set_drvdata(pdev, dev);
00523
00524 if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name,
00525 dev)) {
00526 printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
00527 goto fail;
00528 } else
00529 irq_registered = 1;
00530
00531 if (prism2_hw_config(dev, 1)) {
00532 printk(KERN_DEBUG "%s: hardware initialization failed\n",
00533 dev_info);
00534 goto fail;
00535 }
00536
00537 return 0;
00538
00539 fail:
00540 prism2_free_local_data(dev);
00541
00542 if (irq_registered && dev)
00543 free_irq(dev->irq, dev);
00544
00545 if (attr_mem)
00546 iounmap((void *) attr_mem);
00547
00548 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4))
00549 pci_disable_device(pdev);
00550 #endif
00551
00552 return -ENODEV;
00553 }
00554
00555
00556 static void prism2_plx_remove(struct pci_dev *pdev)
00557 {
00558 struct net_device *dev = pci_get_drvdata(pdev);
00559 struct hostap_interface *iface = dev->priv;
00560
00561
00562 prism2_plx_cor_sreset(iface->local);
00563 hfa384x_disable_interrupts(dev);
00564
00565 if (iface->local->attr_mem)
00566 iounmap((void *) iface->local->attr_mem);
00567 if (dev->irq)
00568 free_irq(dev->irq, dev);
00569
00570 prism2_free_local_data(dev);
00571
00572 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4))
00573 pci_disable_device(pdev);
00574 #endif
00575 }
00576
00577
00578 MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
00579
00580 static struct pci_driver prism2_plx_drv_id = {
00581 .name = "prism2_plx",
00582 .id_table = prism2_plx_id_table,
00583 .probe = prism2_plx_probe,
00584 .remove = prism2_plx_remove,
00585 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6))
00586 .suspend = NULL,
00587 .resume = NULL,
00588 .enable_wake = NULL
00589 #else
00590 .suspend = NULL,
00591 .resume = NULL
00592 #endif
00593 };
00594
00595
00596 static int __init init_prism2_plx(void)
00597 {
00598 printk(KERN_INFO "%s: %s\n", dev_info, version);
00599
00600 if (pci_register_driver(&prism2_plx_drv_id) <= 0) {
00601 printk("hostap_plx: No devices found, driver not "
00602 "installed.\n");
00603 pci_unregister_driver(&prism2_plx_drv_id);
00604 return -ENODEV;
00605 }
00606
00607 return 0;
00608 }
00609
00610
00611 static void __exit exit_prism2_plx(void)
00612 {
00613 pci_unregister_driver(&prism2_plx_drv_id);
00614 printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
00615 }
00616
00617
00618 module_init(init_prism2_plx);
00619 module_exit(exit_prism2_plx);