patch-2.4.18 linux/drivers/net/wireless/orinoco_plx.c
Next file: linux/drivers/parport/ChangeLog
Previous file: linux/drivers/net/wireless/orinoco_cs.c
Back to the patch index
Back to the overall index
- Lines: 376
- Date:
Wed Feb 13 17:38:13 2002
- Orig file:
linux.orig/drivers/net/wireless/orinoco_plx.c
- Orig date:
Mon Feb 18 20:18:39 2002
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/orinoco_plx.c linux/drivers/net/wireless/orinoco_plx.c
@@ -1,16 +1,8 @@
-/* orinoco_plx.c 0.01
+/* orinoco_plx.c 0.09b
*
* Driver for Prism II devices which would usually be driven by orinoco_cs,
* but are connected to the PCI bus by a PLX9052.
*
- * Specifically here we're talking about the SMC2602W (EZConnect
- * Wireless PCI Adaptor)
- *
- * The actual driving is done by orinoco.c, this is just resource
- * allocation stuff. The explanation below is courtesy of Ryan Niemi
- * on the linux-wlan-ng list at
- * http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html
- *
* Copyright (C) 2001 Daniel Barlow <dan@telent.net>
*
* The contents of this file are subject to the Mozilla Public License
@@ -34,6 +26,22 @@
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
+ * Caution: this is experimental and probably buggy. For success and
+ * failure reports for different cards and adaptors, see
+ * orinoco_plx_pci_id_table near the end of the file. If you have a
+ * card we don't have the PCI id for, and looks like it should work,
+ * drop me mail with the id and "it works"/"it doesn't work".
+ *
+ * Note: if everything gets detected fine but it doesn't actually send
+ * or receive packets, your first port of call should probably be to
+ * try newer firmware in the card. Especially if you're doing Ad-Hoc
+ * modes
+ *
+ * The actual driving is done by orinoco.c, this is just resource
+ * allocation stuff. The explanation below is courtesy of Ryan Niemi
+ * on the linux-wlan-ng list at
+ * http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html
+
The PLX9052-based cards (WL11000 and several others) are a different
beast than the usual PCMCIA-based PRISM2 configuration expected by
wlan-ng. Here's the general details on how the WL11000 PCI adapter
@@ -95,14 +103,6 @@
not have time for a while..
---end of mail---
-
- Bus 0, device 4, function 0:
- Network controller: Unknown vendor Unknown device (rev 2).
- Vendor id=1638. Device id=1100.
- Medium devsel. Fast back-to-back capable. IRQ 10.
- I/O at 0x1000 [0x1001].
- Non-prefetchable 32 bit memory at 0x40000000 [0x40000000].
- I/O at 0x10c0 [0x10c1].
*/
#include <linux/config.h>
@@ -140,25 +140,31 @@
#include "hermes.h"
#include "orinoco.h"
+static char version[] __initdata = "orinoco_plx.c 0.09b (Daniel Barlow <dan@telent.net>)";
MODULE_AUTHOR("Daniel Barlow <dan@telent.net>");
MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge");
+#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual MPL/GPL");
+#endif
static dev_info_t dev_info = "orinoco_plx";
-#define COR_OFFSET 0x3e0 /* COR attribute offset of Prism2 PC card */
-#define COR_VALUE 0x41 /* Enable PC card with interrupt in level trigger */
+#define COR_OFFSET (0x3e0 / 2) /* COR attribute offset of Prism2 PC card */
+#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
+
+#define PLX_INTCSR 0x4c /* Interrupt Control and Status Register */
+#define PLX_INTCSR_INTEN (1<<6) /* Interrupt Enable bit */
static int orinoco_plx_open(struct net_device *dev)
{
- dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv;
+ struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
int err;
netif_device_attach(dev);
- err = dldwd_reset(priv);
+ err = orinoco_reset(priv);
if (err)
- printk(KERN_ERR "%s: dldwd_reset failed in orinoco_plx_open()",
+ printk(KERN_ERR "%s: orinoco_reset failed in orinoco_plx_open()",
dev->name);
else
netif_start_queue(dev);
@@ -168,108 +174,217 @@
static int orinoco_plx_stop(struct net_device *dev)
{
- dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv;
+ struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
netif_stop_queue(dev);
- dldwd_shutdown(priv);
+ orinoco_shutdown(priv);
return 0;
}
static void
orinoco_plx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- dldwd_interrupt(irq, ((struct net_device *) dev_id)->priv, regs);
+ orinoco_interrupt(irq, (struct orinoco_private *)dev_id, regs);
}
+static const u16 cis_magic[] = {
+ 0x0001, 0x0003, 0x0000, 0x0000, 0x00ff, 0x0017, 0x0004, 0x0067
+};
+
static int orinoco_plx_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- struct net_device *dev;
- unsigned long pccard_ioaddr;
+ int err = 0;
+ u16 *attr_mem = NULL;
+ u32 reg, addr;
+ struct orinoco_private *priv = NULL;
+ unsigned long pccard_ioaddr = 0;
+ unsigned long pccard_iolen = 0;
+ struct net_device *dev = NULL;
+ int netdev_registered = 0;
int i;
- int reg;
- unsigned char *attr_mem;
- dldwd_priv_t *priv;
- if ((i = pci_enable_device(pdev)))
+ TRACE_ENTER("orinoco_plx");
+
+ err = pci_enable_device(pdev);
+ if (err)
return -EIO;
/* Resource 2 is mapped to the PCMCIA space */
- attr_mem = ioremap(pci_resource_start(pdev, 2), 0x1000);
- /* and 3 to the PCMCIA slot I/O address space */
- pccard_ioaddr = pci_resource_start(pdev, 3);
+ attr_mem = ioremap(pci_resource_start(pdev, 2), PAGE_SIZE);
+ if (! attr_mem)
+ goto fail;
+
+ printk(KERN_DEBUG "orinoco_plx: CIS: ");
+ for (i = 0; i < 16; i++) {
+ printk("%02X:", (int)attr_mem[i]);
+ }
+ printk("\n");
/* Verify whether PC card is present */
- if (attr_mem[0] != 0x01 || attr_mem[2] != 0x03 ||
- attr_mem[4] != 0x00 || attr_mem[6] != 0x00 ||
- attr_mem[8] != 0xFF || attr_mem[10] != 0x17 ||
- attr_mem[12] != 0x04 || attr_mem[14] != 0x67) {
+ /* FIXME: we probably need to be smarted about this */
+ if (memcmp(attr_mem, cis_magic, sizeof(cis_magic)) != 0) {
printk(KERN_ERR "orinoco_plx: The CIS value of Prism2 PC card is invalid.\n");
- return -EIO;
+ err = -EIO;
+ goto fail;
}
+
/* PCMCIA COR is the first byte following CIS: this write should
* enable I/O mode and select level-triggered interrupts */
attr_mem[COR_OFFSET] = COR_VALUE;
+ mdelay(1);
reg = attr_mem[COR_OFFSET];
- /* assert(reg==COR_VALUE); doesn't work */
- iounmap(attr_mem); /* done with this now, it seems */
- if (!request_region(pccard_ioaddr,
- pci_resource_len(pdev, 3), dev_info)) {
+ if (reg != COR_VALUE) {
+ printk(KERN_ERR "orinoco_plx: Error setting COR value (reg=%x)\n", reg);
+ goto fail;
+ }
+
+ iounmap(attr_mem);
+ attr_mem = NULL; /* done with this now, it seems */
+
+ /* bjoern: We need to tell the card to enable interrupts, in
+ case the serial eprom didn't do this already. See the
+ PLX9052 data book, p8-1 and 8-24 for reference. */
+ addr = pci_resource_start(pdev, 1);
+ reg = 0;
+ reg = inl(addr+PLX_INTCSR);
+ if(reg & PLX_INTCSR_INTEN)
+ printk(KERN_DEBUG "orinoco_plx: "
+ "Local Interrupt already enabled\n");
+ else {
+ reg |= PLX_INTCSR_INTEN;
+ outl(reg, addr+PLX_INTCSR);
+ reg = inl(addr+PLX_INTCSR);
+ if(!(reg & PLX_INTCSR_INTEN)) {
+ printk(KERN_ERR "orinoco_plx: "
+ "Couldn't enable Local Interrupts\n");
+ goto fail;
+ }
+ }
+
+ /* and 3 to the PCMCIA slot I/O address space */
+ pccard_ioaddr = pci_resource_start(pdev, 3);
+ pccard_iolen = pci_resource_len(pdev, 3);
+ if (! request_region(pccard_ioaddr, pccard_iolen, dev_info)) {
printk(KERN_ERR "orinoco_plx: I/O resource 0x%lx @ 0x%lx busy\n",
- pci_resource_len(pdev, 3), pccard_ioaddr);
- return -EBUSY;
+ pccard_iolen, pccard_ioaddr);
+ pccard_ioaddr = 0;
+ err = -EBUSY;
+ goto fail;
+ }
+
+ priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+ if (! priv) {
+ err = -ENOMEM;
+ goto fail;
}
- if (!(priv = kmalloc(sizeof(*priv), GFP_KERNEL)))
- return -ENOMEM;
memset(priv, 0, sizeof(*priv));
+
dev = &priv->ndev;
- dldwd_setup(priv); /* XXX clean up if <0 */
- dev->irq = pdev->irq;
+ err = orinoco_setup(priv);
+ if (err)
+ goto fail;
dev->base_addr = pccard_ioaddr;
dev->open = orinoco_plx_open;
dev->stop = orinoco_plx_stop;
priv->card_reset_handler = NULL; /* We have no reset handler */
+ SET_MODULE_OWNER(dev);
printk(KERN_DEBUG
- "Detected Orinoco/Prism2 PCI device at %s, mem:0x%lx, irq:%d, io addr:0x%lx\n",
- pdev->slot_name, (long) attr_mem, pdev->irq, pccard_ioaddr);
+ "Detected Orinoco/Prism2 PLX device at %s irq:%d, io addr:0x%lx\n",
+ pdev->slot_name, pdev->irq, pccard_ioaddr);
- hermes_struct_init(&(priv->hw), dev->base_addr); /* XXX */
- dev->name[0] = '\0'; /* name defaults to ethX */
- register_netdev(dev);
- request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name,
- dev);
- if (dldwd_proc_dev_init(priv) != 0) {
- printk(KERN_ERR "%s: Failed to create /proc node\n", dev->name);
- return -EIO;
+ hermes_struct_init(&(priv->hw), dev->base_addr);
+ pci_set_drvdata(pdev, priv);
+
+ err = request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name, priv);
+ if (err) {
+ printk(KERN_ERR "orinoco_plx: Error allocating IRQ %d.\n", pdev->irq);
+ err = -EBUSY;
+ goto fail;
}
+ dev->irq = pdev->irq;
- SET_MODULE_OWNER(dev);
- priv->hw_ready = 1;
+ err = register_netdev(dev);
+ if (err)
+ goto fail;
+ netdev_registered = 1;
+
+ err = orinoco_proc_dev_init(priv);
+ if (err)
+ goto fail;
+
+ TRACE_EXIT("orinoco_plx");
- /* if(reset_cor) dldwd_cs_cor_reset(priv); */
return 0; /* succeeded */
+
+ fail:
+ printk(KERN_DEBUG "orinoco_plx: init_one(), FAIL!\n");
+
+ if (priv) {
+ orinoco_proc_dev_cleanup(priv);
+
+ if (netdev_registered)
+ unregister_netdev(dev);
+
+ if (dev->irq)
+ free_irq(dev->irq, priv);
+
+ kfree(priv);
+ }
+
+ if (pccard_ioaddr)
+ release_region(pccard_ioaddr, pccard_iolen);
+
+ if (attr_mem)
+ iounmap(attr_mem);
+
+ pci_disable_device(pdev);
+
+ TRACE_EXIT("orinoco_plx");
+
+ return err;
}
static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev)
{
- struct net_device *dev = pci_get_drvdata(pdev);
- dldwd_priv_t *priv = dev->priv;
+ struct orinoco_private *priv = pci_get_drvdata(pdev);
+ struct net_device *dev = &priv->ndev;
- if (!dev)
+ TRACE_ENTER("orinoco_plx");
+
+ if (!priv)
BUG();
- dldwd_proc_dev_cleanup(priv);
- free_irq(dev->irq, dev);
+ orinoco_proc_dev_cleanup(priv);
+
unregister_netdev(dev);
- release_region(dev->base_addr, 0x40);
- kfree(dev->priv);
- pci_set_drvdata(pdev, NULL);
+
+ if (dev->irq)
+ free_irq(dev->irq, priv);
+
+ kfree(priv);
+
+ release_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3));
+
+ pci_disable_device(pdev);
+
+ TRACE_EXIT("orinoco_plx");
}
static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = {
- {0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,},
+ {0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */
+#if 0
+ {0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga */
+#endif
+ {0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* SMC EZConnect SMC2602W,
+ Eumitcom PCI WL11000,
+ Addtron AWA-100*/
+ {0x16ab, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* Global Sun Tech GL24110P */
+ {0x16ab, 0x1101, PCI_ANY_ID, PCI_ANY_ID,}, /* Reported working, but unknown */
+ {0x16ab, 0x1102, PCI_ANY_ID, PCI_ANY_ID,}, /* Linksys WDT11 */
+ {0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,}, /* USR 2415 */
{0,},
};
@@ -286,12 +401,15 @@
static int __init orinoco_plx_init(void)
{
+ printk(KERN_DEBUG "%s\n", version);
return pci_module_init(&orinoco_plx_driver);
}
extern void __exit orinoco_plx_exit(void)
{
pci_unregister_driver(&orinoco_plx_driver);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(HZ);
}
module_init(orinoco_plx_init);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)