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