orinoco_pci

Fumitoshi UKAI ukai at debian.or.jp
Fri Apr 5 01:17:40 EST 2002


Hi,

I wrote orinoco_pci module for PrismII PCI device. It works on my note PC
Sharp Mebius PC-MV1-C1W.  I'm sending this mail using this W-LAN driver :-)
Hope this works on other PCs, such as ThinkPad T23, as well.

% lspci -v

02:03.0 Network controller: Harris Semiconductor: Unknown device 3873 (rev 01)
        Subsystem: AMBIT Microsystem Corp.: Unknown device 0200
        Flags: bus master, fast Back2Back, medium devsel, latency 64, IRQ 5
        Memory at e0500000 (32-bit, prefetchable) [size=4K]
        Capabilities: <available only to root>

diff -Nru --exclude=.depend kernel-source-2.4.18/drivers/net/wireless/Config.in kernel-source-2.4.18+orinoco-pci/drivers/net/wireless/Config.in
--- kernel-source-2.4.18/drivers/net/wireless/Config.in	Wed Oct 10 07:13:03 2001
+++ kernel-source-2.4.18+orinoco-pci/drivers/net/wireless/Config.in	Wed Apr  3 00:52:03 2002
@@ -14,6 +14,7 @@
 
 if [ "$CONFIG_PCI" = "y" ]; then
    dep_tristate '    Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)' CONFIG_PLX_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL
+   dep_tristate '    Hermes PCI support' CONFIG_PCI_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL
 fi
 
 # If Pcmcia is compiled in, offer Pcmcia cards...
diff -Nru --exclude=.depend kernel-source-2.4.18/drivers/net/wireless/Makefile kernel-source-2.4.18+orinoco-pci/drivers/net/wireless/Makefile
--- kernel-source-2.4.18/drivers/net/wireless/Makefile	Wed Oct 10 07:13:03 2001
+++ kernel-source-2.4.18+orinoco-pci/drivers/net/wireless/Makefile	Wed Apr  3 00:50:44 2002
@@ -18,6 +18,7 @@
 obj-$(CONFIG_PCMCIA_HERMES)	+= orinoco_cs.o
 obj-$(CONFIG_APPLE_AIRPORT)	+= airport.o
 obj-$(CONFIG_PLX_HERMES)	+= orinoco_plx.o
+obj-$(CONFIG_PCI_HERMES)	+= orinoco_pci.o
 
 obj-$(CONFIG_AIRO)		+= airo.o
 obj-$(CONFIG_AIRO_CS)		+= airo_cs.o airo.o
diff -Nru --exclude=.depend kernel-source-2.4.18/drivers/net/wireless/hermes.c kernel-source-2.4.18+orinoco-pci/drivers/net/wireless/hermes.c
--- kernel-source-2.4.18/drivers/net/wireless/hermes.c	Tue Feb 26 04:38:03 2002
+++ kernel-source-2.4.18+orinoco-pci/drivers/net/wireless/hermes.c	Thu Apr  4 18:45:14 2002
@@ -131,6 +131,7 @@
 {
 	hw->iobase = io;
 	hw->inten = 0x0;
+	hw->mmio = 0;
 }
 
 int hermes_reset(hermes_t *hw)
diff -Nru --exclude=.depend kernel-source-2.4.18/drivers/net/wireless/hermes.h kernel-source-2.4.18+orinoco-pci/drivers/net/wireless/hermes.h
--- kernel-source-2.4.18/drivers/net/wireless/hermes.h	Mon Apr  1 20:53:10 2002
+++ kernel-source-2.4.18+orinoco-pci/drivers/net/wireless/hermes.h	Thu Apr  4 18:49:01 2002
@@ -258,6 +258,7 @@
 	uint iobase;
 
 	u16 inten; /* Which interrupts should be enabled? */
+	uint mmio;
 } hermes_t;
 
 typedef struct hermes_response {
@@ -265,8 +266,8 @@
 } hermes_response_t;
 
 /* Register access convenience macros */
-#define hermes_read_reg(hw, off) (inw((hw)->iobase + (off)))
-#define hermes_write_reg(hw, off, val) (outw_p((val), (hw)->iobase + (off)))
+#define hermes_read_reg(hw, off) ((hw)->mmio ? readw((hw)->iobase + (off)*2) : inw((hw)->iobase + (off)))
+#define hermes_write_reg(hw, off, val) ((hw)->mmio ? writew(val, (hw)->iobase + (off)*2) : outw_p(val, (hw)->iobase + (off)))
 
 #define hermes_read_regn(hw, name) (hermes_read_reg((hw), HERMES_##name))
 #define hermes_write_regn(hw, name, val) (hermes_write_reg((hw), HERMES_##name, (val)))
@@ -335,8 +336,24 @@
 #define HERMES_RECLEN_TO_BYTES(n) ( ((n)-1) * 2 )
 
 /* Note that for the next two, the count is in 16-bit words, not bytes */
-#define hermes_read_words(hw, off, buf, count) (insw((hw)->iobase + (off), (buf), (count)))
-#define hermes_write_words(hw, off, buf, count) (outsw((hw)->iobase + (off), (buf), (count)))
+static inline void hermes_mmio_insw(void *ptr, u32 addr, int cnt) {
+    u16 *p = (u16 *)ptr;
+    int i;
+    for (i = 0; i < cnt; i++, p++) {
+	*p = readw(addr);
+    }
+}
+
+static inline void hermes_mmio_outsw(u32 addr, void *ptr, int cnt) {
+    u16 *p = (u16 *)ptr;
+    int i;
+    for (i = 0; i < cnt; i++, p++) {
+	writew(*p, addr);
+    }
+}
+
+#define hermes_read_words(hw, off, buf, count) ((hw)->mmio ? hermes_mmio_insw(buf, (hw)->iobase + (off)*2, (count)) : insw((hw)->iobase + (off), (buf), (count)))
+#define hermes_write_words(hw, off, buf, count) ((hw)->mmio ? hermes_mmio_outsw((hw)->iobase + (off)*2, buf, (count)) : outsw((hw)->iobase + (off), (buf), (count)))
 
 #define HERMES_READ_RECORD(hw, bap, rid, buf) \
 	(hermes_read_ltv((hw),(bap),(rid), sizeof(*buf), NULL, (buf)))
diff -Nru --exclude=.depend kernel-source-2.4.18/drivers/net/wireless/orinoco_pci.c kernel-source-2.4.18+orinoco-pci/drivers/net/wireless/orinoco_pci.c
--- kernel-source-2.4.18/drivers/net/wireless/orinoco_pci.c	Thu Jan  1 09:00:00 1970
+++ kernel-source-2.4.18+orinoco-pci/drivers/net/wireless/orinoco_pci.c	Thu Apr  4 18:42:47 2002
@@ -0,0 +1,345 @@
+/* orinoco_pci.c
+ * 
+ * Driver for Prism II devices connected to the PCI bus.
+ *
+ * Copyright (C) 2002 Fumitoshi UKAI <ukai at debian.or.jp>
+ *
+ * based on orinoco_plx.c 0.09b
+ * Copyright (C) 2001 Daniel Barlow <dan at 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.
+
+ * Caution: this is experimental and probably buggy.  For success and
+ * failure reports for different cards and adaptors, see
+ * orinoco_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
+ */
+
+#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"
+
+static char version[] __initdata = "orinoco_pci.c 0.01 (Fumitoshi UKAI <ukai at debian.or.jp>)";
+MODULE_AUTHOR("Fumitoshi UKAI <ukai at debian.or.jp>");
+MODULE_DESCRIPTION("Driver for wireless LAN cards connected to the PCI bus");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual MPL/GPL");
+#endif
+
+#define	HERMES_PCICOR		(0x4c/2)
+#define MMIO_SIZE		0x1000		/* Memory size - 4K bytes */
+
+struct orinoco_pci {
+	dev_link_t link;
+	
+	struct orinoco_private priv;
+};
+
+static int orinoco_pci_open(struct net_device *dev)
+{
+	struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
+#if 0
+	struct orinoco_pci* card = (struct orinoco_pci *)priv->card;
+	dev_link_t *link = &card->link;
+#endif
+	int err;
+
+	netif_device_attach(dev);
+
+	err = orinoco_reset(priv);
+	if (err)
+		printk(KERN_ERR "%s: orinoco_reset failed in orinoco_pci_open()",
+		       dev->name);
+	else
+		netif_start_queue(dev);
+
+	return err;
+}
+
+static int orinoco_pci_stop(struct net_device *dev)
+{
+	struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
+	struct orinoco_pci* card = (struct orinoco_pci *)priv->card;
+	dev_link_t *link = &card->link;
+
+	netif_stop_queue(dev);
+	orinoco_shutdown(priv);
+
+	link->state &= ~DEV_CONFIG;
+	return 0;
+}
+
+static void
+orinoco_pci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	orinoco_interrupt(irq, (struct orinoco_private *)dev_id, regs);
+}
+
+static int
+orinoco_pci_reset(struct orinoco_private *priv)
+{
+	struct orinoco_pci* card = (struct orinoco_pci *)priv->card;
+	dev_link_t *link = &card->link;
+	int timeout;
+
+	TRACE_ENTER(priv->ndev.name);
+
+	/* Doing it if hardware is gone is guaranteed crash */
+	if (!(link->state & DEV_CONFIG))
+		return -ENODEV;
+
+	/* Assert reset and wait awhile 
+	 * (note: these delays are _really_ long, but they appear to be
+	 *        necessary.)
+	 */
+	hermes_write_reg(&priv->hw, HERMES_PCICOR, 0x0080);
+	timeout = jiffies + HZ/4;
+	while (time_before(jiffies, timeout)) udelay(5);
+
+	/* Clear the reset and wait some more 
+	 */
+	hermes_write_reg(&priv->hw, HERMES_PCICOR, 0x0);
+	timeout = jiffies + HZ/2;
+	while (time_before(jiffies, timeout)) udelay(5);
+
+	TRACE_EXIT(priv->ndev.name);
+	return 0;
+}
+
+static int orinoco_pci_init_one(struct pci_dev *pdev,
+				const struct pci_device_id *ent)
+{
+	int err = 0;
+	unsigned long phymem;
+	unsigned long addr = 0;
+	struct orinoco_pci *card = NULL;
+	struct orinoco_private *priv = NULL;
+	dev_link_t *link = NULL;
+	struct net_device *dev = NULL;
+	int netdev_registered = 0;
+
+	TRACE_ENTER("orinoco_pci");
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return -EIO;
+
+	phymem = pci_resource_start(pdev, 0);
+	addr = (unsigned long)ioremap_nocache(phymem, MMIO_SIZE);
+	if (addr == 0)
+		return -EIO;
+
+	printk("A Prism2.5 PCI device found, "
+	       "phymem:0x%lx, irq:%d\n, mem:0x%lx\n",
+	       phymem, pdev->irq, addr);
+
+	card = kmalloc(sizeof(*card), GFP_KERNEL);
+	if (!card) {
+		err = -ENOMEM;
+		goto fail;
+	}
+	memset(card, 0, sizeof(*card));
+	priv = &(card->priv);
+	priv->card = card;
+	link = &card->link;
+	link->priv = priv;
+
+	dev = &priv->ndev;
+
+	err = orinoco_setup(priv);
+	if (err)
+		goto fail;
+        dev->base_addr = (unsigned long)addr;
+	dev->open = orinoco_pci_open;
+	dev->stop = orinoco_pci_stop;
+	priv->card_reset_handler = orinoco_pci_reset;
+	SET_MODULE_OWNER(dev);
+
+	printk(KERN_DEBUG
+	       "Detected Orinoco/Prism2 PCI device at %s irq:%d, addr:0x%lx\n",
+	       pdev->slot_name, pdev->irq, addr);
+
+	hermes_struct_init(&(priv->hw), dev->base_addr);
+	priv->hw.mmio = 1;
+	pci_set_drvdata(pdev, priv);
+
+	/* really need this? - ukai */
+	hermes_write_regn(&priv->hw, PARAM2, 0);
+	hermes_write_regn(&priv->hw, PARAM1, 0);
+	hermes_write_regn(&priv->hw, PARAM0, 0);
+	hermes_write_regn(&priv->hw, CMD, HERMES_CMD_INIT);
+
+	err = request_irq(pdev->irq, orinoco_pci_interrupt, SA_SHIRQ, 
+			  dev->name, priv);
+	if (err) {
+		printk(KERN_ERR "orinoco_pci: Error allocating IRQ %d.\n", 
+		       pdev->irq);
+		err = -EBUSY;
+		goto fail;
+	}
+	dev->irq = pdev->irq;
+
+	err = register_netdev(dev);
+	if (err)
+		goto fail;
+	netdev_registered = 1;
+
+	err = orinoco_proc_dev_init(priv);
+	if (err)
+		goto fail;
+
+	link->state |= DEV_CONFIG;
+
+	TRACE_EXIT("orinoco_pci");
+
+	return 0;		/* succeeded */
+
+ fail:	
+	printk(KERN_DEBUG "orinoco_pci: 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);
+		
+	}
+	if (card)
+		kfree(card);
+
+	if (addr)
+		iounmap((void *)addr);
+
+	pci_disable_device(pdev);
+
+	TRACE_EXIT("orinoco_pci");
+	
+	return err;
+}
+
+static void __devexit orinoco_pci_remove_one(struct pci_dev *pdev)
+{
+	struct orinoco_private *priv = pci_get_drvdata(pdev);
+	struct orinoco_pci *card = (struct orinoco_pci *)priv->card;
+	struct net_device *dev = &priv->ndev;
+	unsigned long addr = priv->hw.iobase;
+
+	TRACE_ENTER("orinoco_pci");
+
+	if (!priv)
+		BUG();
+
+	orinoco_proc_dev_cleanup(priv);
+
+	unregister_netdev(dev);
+		
+	if (dev->irq)
+		free_irq(dev->irq, priv);
+		
+	kfree(card);
+
+	iounmap((void *)addr);
+
+	pci_disable_device(pdev);
+
+	TRACE_EXIT("orinoco_pci");
+}
+
+static struct pci_device_id orinoco_pci_id_table[] __devinitdata = {
+	{0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,},	/* Harris 3873 (Mebius)*/
+	{0,},
+};
+
+MODULE_DEVICE_TABLE(pci, orinoco_pci_id_table);
+
+static struct pci_driver orinoco_pci_driver = {
+	name:"orinoco_pci",
+	id_table:orinoco_pci_id_table,
+	probe:orinoco_pci_init_one,
+	remove:__devexit_p(orinoco_pci_remove_one),
+	suspend:0,
+	resume:0
+};
+
+static int __init orinoco_pci_init(void)
+{
+	printk(KERN_DEBUG "%s\n", version);
+	return pci_module_init(&orinoco_pci_driver);
+}
+
+extern void __exit orinoco_pci_exit(void)
+{
+	pci_unregister_driver(&orinoco_pci_driver);
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule_timeout(HZ);
+}
+
+module_init(orinoco_pci_init);
+module_exit(orinoco_pci_exit);
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */

Regards,
Fumitoshi UKAI




More information about the wireless mailing list