patch-2.4.11-dontuse linux/drivers/net/wireless/orinoco_plx.c
Next file: linux/drivers/net/yellowfin.c
Previous file: linux/drivers/net/wireless/orinoco_cs.c
Back to the patch index
Back to the overall index
- Lines: 307
- Date:
Tue Oct 9 15:13:03 2001
- Orig file:
v2.4.10/linux/drivers/net/wireless/orinoco_plx.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.10/linux/drivers/net/wireless/orinoco_plx.c linux/drivers/net/wireless/orinoco_plx.c
@@ -0,0 +1,306 @@
+/* orinoco_plx.c 0.01
+ *
+ * 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
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License
+ * at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the MPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the MPL or the GPL.
+
+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
+works:
+
+ - Two PCI I/O address spaces, one 0x80 long which contains the PLX9052
+ registers, and one that's 0x40 long mapped to the PCMCIA slot I/O
+ address space.
+
+ - One PCI memory address space, mapped to the PCMCIA memory space
+ (containing the CIS).
+
+After identifying the I/O and memory space, you can read through the
+memory space to confirm the CIS's device ID or manufacturer ID to make
+sure it's the expected card. Keep in mind that the PCMCIA spec specifies
+the CIS as the lower 8 bits of each word read from the CIS, so to read the
+bytes of the CIS, read every other byte (0,2,4,...). Passing that test,
+you need to enable the I/O address space on the PCMCIA card via the PCMCIA
+COR register. This is the first byte following the CIS. In my case
+(which may not have any relation to what's on the PRISM2 cards), COR was
+at offset 0x800 within the PCI memory space. Write 0x41 to the COR
+register to enable I/O mode and to select level triggered interrupts. To
+confirm you actually succeeded, read the COR register back and make sure
+it actually got set to 0x41, incase you have an unexpected card inserted.
+
+Following that, you can treat the second PCI I/O address space (the one
+that's not 0x80 in length) as the PCMCIA I/O space.
+
+Note that in the Eumitcom's source for their drivers, they register the
+interrupt as edge triggered when registering it with the Windows kernel. I
+don't recall how to register edge triggered on Linux (if it can be done at
+all). But in some experimentation, I don't see much operational
+difference between using either interrupt mode. Don't mess with the
+interrupt mode in the COR register though, as the PLX9052 wants level
+triggers with the way the serial EEPROM configures it on the WL11000.
+
+There's some other little quirks related to timing that I bumped into, but
+I don't recall right now. Also, there's two variants of the WL11000 I've
+seen, revision A1 and T2. These seem to differ slightly in the timings
+configured in the wait-state generator in the PLX9052. There have also
+been some comments from Eumitcom that cards shouldn't be hot swapped,
+apparently due to risk of cooking the PLX9052. I'm unsure why they
+believe this, as I can't see anything in the design that would really
+cause a problem, except for crashing drivers not written to expect it. And
+having developed drivers for the WL11000, I'd say it's quite tricky to
+write code that will successfully deal with a hot unplug. Very odd things
+happen on the I/O side of things. But anyway, be warned. Despite that,
+I've hot-swapped a number of times during debugging and driver development
+for various reasons (stuck WAIT# line after the radio card's firmware
+locks up).
+
+Hope this is enough info for someone to add PLX9052 support to the wlan-ng
+card. In the case of the WL11000, the PCI ID's are 0x1639/0x0200, with
+matching subsystem ID's. Other PLX9052-based manufacturers other than
+Eumitcom (or on cards other than the WL11000) may have different PCI ID's.
+
+If anyone needs any more specific info, let me know. I haven't had time
+to implement support myself yet, and with the way things are going, might
+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>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/wireless.h>
+#include <linux/fcntl.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/bus_ops.h>
+
+#include "hermes.h"
+#include "orinoco.h"
+
+MODULE_AUTHOR("Daniel Barlow <dan@telent.net>");
+MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge");
+MODULE_LICENSE("Dual MPL/GPL");
+
+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 */
+
+static int orinoco_plx_open(struct net_device *dev)
+{
+ dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv;
+ int err;
+
+ netif_device_attach(dev);
+
+ err = dldwd_reset(priv);
+ if (err)
+ printk(KERN_ERR "%s: dldwd_reset failed in orinoco_plx_open()",
+ dev->name);
+ else
+ netif_start_queue(dev);
+
+ return err;
+}
+
+static int orinoco_plx_stop(struct net_device *dev)
+{
+ dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv;
+ netif_stop_queue(dev);
+ dldwd_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);
+}
+
+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 i;
+ int reg;
+ unsigned char *attr_mem;
+ dldwd_priv_t *priv;
+
+ if ((i = pci_enable_device(pdev)))
+ 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);
+
+ /* 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) {
+ printk(KERN_ERR "orinoco_plx: The CIS value of Prism2 PC card is invalid.\n");
+ return -EIO;
+ }
+ /* 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;
+ 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)) {
+ printk(KERN_ERR "orinoco_plx: I/O resource 0x%lx @ 0x%lx busy\n",
+ pci_resource_len(pdev, 3), pccard_ioaddr);
+ return -EBUSY;
+ }
+ 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;
+ 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 */
+
+ 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);
+
+ 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;
+ }
+
+ SET_MODULE_OWNER(dev);
+ priv->hw_ready = 1;
+
+ /* if(reset_cor) dldwd_cs_cor_reset(priv); */
+ return 0; /* succeeded */
+}
+
+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;
+
+ if (!dev)
+ BUG();
+
+ dldwd_proc_dev_cleanup(priv);
+ free_irq(dev->irq, dev);
+ unregister_netdev(dev);
+ release_region(dev->base_addr, 0x40);
+ kfree(dev->priv);
+ pci_set_drvdata(pdev, NULL);
+}
+
+
+static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = {
+ {0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,},
+};
+
+MODULE_DEVICE_TABLE(pci, orinoco_plx_pci_id_table);
+
+static struct pci_driver orinoco_plx_driver = {
+ name:"orinoco_plx",
+ id_table:orinoco_plx_pci_id_table,
+ probe:orinoco_plx_init_one,
+ remove:orinoco_plx_remove_one,
+ suspend:0,
+ resume:0
+};
+
+static int __init orinoco_plx_init(void)
+{
+ return pci_module_init(&orinoco_plx_driver);
+}
+
+extern void __exit orinoco_plx_exit(void)
+{
+ pci_unregister_driver(&orinoco_plx_driver);
+}
+
+module_init(orinoco_plx_init);
+module_exit(orinoco_plx_exit);
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)