Re: Update to orinoco driver (2.4)

From: David Gibson (hermes@gibson.dropbear.id.au)
Date: Wed Apr 23 2003 - 01:05:20 EST


On Wed, Apr 23, 2003 at 03:46:36PM +1000, David Gibson wrote:
> Hi Marcelo,
>
> The patch below updates the orinoco driver in 2.4 to 0.13d, the patch
> is against 2.4.21-rc1. You may want to postpone this update till
> after 2.4.21, but I'd consider it, since it fixes a fair slew of bugs.

Duh, sorry. And now with the actual patch:

diff -uNr linux-2.4-pristine/drivers/net/wireless/airport.c linux-orinoco/drivers/net/wireless/airport.c
--- linux-2.4-pristine/drivers/net/wireless/airport.c 2003-04-23 14:55:29.000000000 +1000
+++ linux-orinoco/drivers/net/wireless/airport.c 2003-04-23 14:59:19.000000000 +1000
@@ -1,4 +1,4 @@
-/* airport.c 0.13b
+/* airport.c 0.13d
  *
  * A driver for "Hermes" chipset based Apple Airport wireless
  * card.
@@ -95,7 +95,7 @@
 
                 netif_device_detach(dev);
 
- priv->hw_unavailable = 1;
+ priv->hw_unavailable++;
 
                 orinoco_unlock(priv, &flags);
 
@@ -121,14 +121,15 @@
 
                 netif_device_attach(dev);
 
- if (priv->open) {
+ priv->hw_unavailable--;
+
+ if (priv->open && (! priv->hw_unavailable)) {
                         err = __orinoco_up(dev);
                         if (err)
                                 printk(KERN_ERR "%s: Error %d restarting card on PBOOK_WAKE\n",
                                        dev->name, err);
                 }
 
- priv->hw_unavailable = 0;
 
                 spin_unlock_irqrestore(&priv->lock, flags);
 
@@ -140,8 +141,21 @@
 
 static int airport_hard_reset(struct orinoco_private *priv)
 {
+ /* It would be nice to power cycle the Airport for a real hard
+ * reset, but for some reason although it appears to
+ * re-initialize properly, it falls in a screaming heap
+ * shortly afterwards. */
+#if 0
+ struct net_device *dev = priv->ndev;
         struct airport *card = priv->card;
 
+ /* Vitally important. If we don't do this it seems we get an
+ * interrupt somewhere during the power cycle, since
+ * hw_unavailable is already set it doesn't get ACKed, we get
+ * into an interrupt loop and the the PMU decides to turn us
+ * off. */
+ disable_irq(dev->irq);
+
         pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0);
         current->state = TASK_UNINTERRUPTIBLE;
         schedule_timeout(HZ);
@@ -149,6 +163,10 @@
         current->state = TASK_UNINTERRUPTIBLE;
         schedule_timeout(HZ);
 
+ enable_irq(dev->irq);
+ schedule_timeout(HZ);
+#endif
+
         return 0;
 }
 
@@ -209,7 +227,7 @@
         /* Reset it before we get the interrupt */
         hermes_init(hw);
 
- if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", (void *)priv)) {
+ if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", dev)) {
                 printk(KERN_ERR "airport: Couldn't get IRQ %d\n", dev->irq);
                 goto failed;
         }
@@ -251,7 +269,7 @@
         card->ndev_registered = 0;
 
         if (card->irq_requested)
- free_irq(dev->irq, priv);
+ free_irq(dev->irq, dev);
         card->irq_requested = 0;
 
         if (card->vaddr)
@@ -269,11 +287,10 @@
         kfree(dev);
 } /* airport_detach */
 
-static char version[] __initdata = "airport.c 0.13b (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
+static char version[] __initdata = "airport.c 0.13d (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
 MODULE_LICENSE("Dual MPL/GPL");
-EXPORT_NO_SYMBOLS;
 
 static int __init
 init_airport(void)
@@ -282,15 +299,11 @@
 
         printk(KERN_DEBUG "%s\n", version);
 
- MOD_INC_USE_COUNT;
-
         /* Lookup card in device tree */
         airport_node = find_devices("radio");
         if (airport_node && !strcmp(airport_node->parent->name, "mac-io"))
                 airport_dev = airport_attach(airport_node);
 
- MOD_DEC_USE_COUNT;
-
         return airport_dev ? 0 : -ENODEV;
 }
 
diff -uNr linux-2.4-pristine/drivers/net/wireless/hermes.c linux-orinoco/drivers/net/wireless/hermes.c
--- linux-2.4-pristine/drivers/net/wireless/hermes.c 2003-04-23 14:55:29.000000000 +1000
+++ linux-orinoco/drivers/net/wireless/hermes.c 2003-04-23 14:59:19.000000000 +1000
@@ -544,4 +544,9 @@
         return 0;
 }
 
+static void __exit exit_hermes(void)
+{
+}
+
 module_init(init_hermes);
+module_exit(exit_hermes);
diff -uNr linux-2.4-pristine/drivers/net/wireless/hermes.h linux-orinoco/drivers/net/wireless/hermes.h
--- linux-2.4-pristine/drivers/net/wireless/hermes.h 2003-04-23 14:55:29.000000000 +1000
+++ linux-orinoco/drivers/net/wireless/hermes.h 2003-04-23 14:59:19.000000000 +1000
@@ -250,7 +250,6 @@
         u16 scanreason; /* ??? */
         struct hermes_scan_apinfo aps[35]; /* Scan result */
 } __attribute__ ((packed));
-
 #define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000)
 #define HERMES_LINKSTATUS_CONNECTED (0x0001)
 #define HERMES_LINKSTATUS_DISCONNECTED (0x0002)
@@ -368,7 +367,7 @@
         if (hw->io_space) {
                 insw(hw->iobase + off, buf, count);
         } else {
- int i;
+ unsigned i;
                 u16 *p;
 
                 /* This needs to *not* byteswap (like insw()) but
@@ -388,7 +387,7 @@
         if (hw->io_space) {
                 outsw(hw->iobase + off, buf, count);
         } else {
- int i;
+ unsigned i;
                 const u16 *p;
 
                 /* This needs to *not* byteswap (like outsw()) but
@@ -401,6 +400,21 @@
         }
 }
 
+static inline void hermes_clear_words(struct hermes *hw, int off, unsigned count)
+{
+ unsigned i;
+
+ off = off << hw->reg_spacing;;
+
+ if (hw->io_space) {
+ for (i = 0; i < count; i++)
+ outw(0, hw->iobase + off);
+ } else {
+ for (i = 0; i < count; i++)
+ writew(0, hw->iobase + off);
+ }
+}
+
 #define HERMES_READ_RECORD(hw, bap, rid, buf) \
         (hermes_read_ltv((hw),(bap),(rid), sizeof(*buf), NULL, (buf)))
 #define HERMES_WRITE_RECORD(hw, bap, rid, buf) \
diff -uNr linux-2.4-pristine/drivers/net/wireless/orinoco.c linux-orinoco/drivers/net/wireless/orinoco.c
--- linux-2.4-pristine/drivers/net/wireless/orinoco.c 2003-04-23 14:55:29.000000000 +1000
+++ linux-orinoco/drivers/net/wireless/orinoco.c 2003-04-23 14:59:19.000000000 +1000
@@ -1,4 +1,4 @@
-/* orinoco.c 0.13b - (formerly known as dldwd_cs.c and orinoco_cs.c)
+/* orinoco.c 0.13d - (formerly known as dldwd_cs.c and orinoco_cs.c)
  *
  * A driver for Hermes or Prism 2 chipset based PCMCIA wireless
  * adaptors, with Lucent/Agere, Intersil or Symbol firmware.
@@ -345,11 +345,45 @@
  * we are connected (avoids cofusing the firmware), and only
  * give LINKSTATUS printk()s if the status has changed.
  *
+ * v0.13b -> v0.13c - 11 Mar 2003 - David Gibson
+ * o Cleanup: use dev instead of priv in various places.
+ * o Bug fix: Don't ReleaseConfiguration on RESET_PHYSICAL event
+ * if we're in the middle of a (driver initiated) hard reset.
+ * o Bug fix: ETH_ZLEN is supposed to include the header
+ * (Dionysus Blazakis & Manish Karir)
+ * o Convert to using workqueues instead of taskqueues (and
+ * backwards compatibility macros for pre 2.5.41 kernels).
+ * o Drop redundant (I think...) MOD_{INC,DEC}_USE_COUNT in
+ * airport.c
+ * o New orinoco_tmd.c init module from Joerg Dorchain for
+ * TMD7160 based PCI to PCMCIA bridges (similar to
+ * orinoco_plx.c).
+ *
+ * v0.13c -> v0.13d - 22 Apr 2003 - David Gibson
+ * o Make hw_unavailable a counter, rather than just a flag, this
+ * is necessary to avoid some races (such as a card being
+ * removed in the middle of orinoco_reset().
+ * o Restore Release/RequestConfiguration in the PCMCIA event handler
+ * when dealing with a driver initiated hard reset. This is
+ * necessary to prevent hangs due to a spurious interrupt while
+ * the reset is in progress.
+ * o Clear the 802.11 header when transmitting, even though we
+ * don't use it. This fixes a long standing bug on some
+ * firmwares, which seem to get confused if that isn't done.
+ * o Be less eager to de-encapsulate SNAP frames, only do so if
+ * the OUI is 00:00:00 or 00:00:f8, leave others alone. The old
+ * behaviour broke CDP (Cisco Discovery Protocol).
+ * o Use dev instead of priv for free_irq() as well as
+ * request_irq() (oops).
+ * o Attempt to reset rather than giving up if we get too many
+ * IRQs.
+ * o Changed semantics of __orinoco_down() so it can be called
+ * safely with hw_unavailable set. It also now clears the
+ * linkstatus (since we're going to have to reassociate).
+ *
  * TODO
-
  * o New wireless extensions API (patch from Moustafa
  * Youssef, updated by Jim Carter).
- * o Fix PCMCIA hard resets with pcmcia-cs.
  * o Handle de-encapsulation within network layer, provide 802.11
  * headers (patch from Thomas 'Dent' Mirlacher)
  * o Fix possible races in SPY handling.
@@ -373,7 +407,7 @@
  * flag after taking the lock, and if it is set, give up on whatever
  * they are doing and drop the lock again. The orinoco_lock()
  * function handles this (it unlocks and returns -EBUSY if
- * hw_unavailable is true). */
+ * hw_unavailable is non-zero). */
 
 #include <linux/config.h>
 
@@ -522,7 +556,7 @@
 
 /* Hardware control routines */
 
-static int __orinoco_program_rids(struct orinoco_private *priv);
+static int __orinoco_program_rids(struct net_device *dev);
 
 static int __orinoco_hw_set_bitrate(struct orinoco_private *priv);
 static int __orinoco_hw_setup_wep(struct orinoco_private *priv);
@@ -535,14 +569,14 @@
 static void __orinoco_set_multicast_list(struct net_device *dev);
 
 /* Interrupt handling routines */
-static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw);
-static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw);
-static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw);
-static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw);
-static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw);
-static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw);
-static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw);
-static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw);
+static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw);
+static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw);
+static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw);
+static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw);
+static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw);
+static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw);
+static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw);
 
 /* ioctl() routines */
 static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq);
@@ -577,7 +611,7 @@
         struct hermes *hw = &priv->hw;
         int err;
 
- err = __orinoco_program_rids(priv);
+ err = __orinoco_program_rids(dev);
         if (err) {
                 printk(KERN_ERR "%s: Error %d configuring card\n",
                        dev->name, err);
@@ -606,14 +640,25 @@
 
         netif_stop_queue(dev);
 
- err = hermes_disable_port(hw, 0);
- if (err) {
- printk(KERN_ERR "%s: Error %d disabling MAC port\n",
- dev->name, err);
- return err;
+ if (! priv->hw_unavailable) {
+ if (! priv->broken_disableport) {
+ err = hermes_disable_port(hw, 0);
+ if (err) {
+ /* Some firmwares (e.g. Intersil 1.3.x) seem
+ * to have problems disabling the port, oh
+ * well, too bad. */
+ printk(KERN_WARNING "%s: Error %d disabling MAC port\n",
+ dev->name, err);
+ priv->broken_disableport = 1;
+ }
+ }
+ hermes_set_irqmask(hw, 0);
+ hermes_write_regn(hw, EVACK, 0xffff);
         }
- hermes_set_irqmask(hw, 0);
- hermes_write_regn(hw, EVACK, 0xffff);
+
+ /* firmware will have to reassociate */
+ priv->last_linkstatus = 0xffff;
+ priv->connected = 0;
 
         return 0;
 }
@@ -656,38 +701,38 @@
         if (err)
                 return err;
 
- priv->open = 1;
-
         err = __orinoco_up(dev);
 
+ if (! err)
+ priv->open = 1;
+
         orinoco_unlock(priv, &flags);
 
         return err;
 }
 
-static int orinoco_stop(struct net_device *dev)
+int orinoco_stop(struct net_device *dev)
 {
         struct orinoco_private *priv = dev->priv;
         int err = 0;
 
         /* We mustn't use orinoco_lock() here, because we need to be
- able to close the interface, even if hw_unavailable is set
+ able to close the interface even if hw_unavailable is set
            (e.g. as we're released after a PC Card removal) */
         spin_lock_irq(&priv->lock);
 
         priv->open = 0;
 
- if (! priv->hw_unavailable)
- err = __orinoco_down(dev);
+ err = __orinoco_down(dev);
 
         spin_unlock_irq(&priv->lock);
 
         return err;
 }
 
-static int __orinoco_program_rids(struct orinoco_private *priv)
+static int __orinoco_program_rids(struct net_device *dev)
 {
- struct net_device *dev = priv->ndev;
+ struct orinoco_private *priv = dev->priv;
         hermes_t *hw = &priv->hw;
         int err;
         struct hermes_idstring idbuf;
@@ -873,51 +918,84 @@
 }
 
 /* xyzzy */
-static int orinoco_reconfigure(struct orinoco_private *priv)
+static int orinoco_reconfigure(struct net_device *dev)
 {
+ struct orinoco_private *priv = dev->priv;
         struct hermes *hw = &priv->hw;
         unsigned long flags;
         int err = 0;
 
- orinoco_lock(priv, &flags);
+ if (priv->broken_disableport) {
+ schedule_work(&priv->reset_work);
+ return 0;
+ }
 
+ err = orinoco_lock(priv, &flags);
+ if (err)
+ return err;
+
+
         err = hermes_disable_port(hw, 0);
         if (err) {
- printk(KERN_ERR "%s: Unable to disable port in orinco_reconfigure()\n",
- priv->ndev->name);
+ printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n",
+ dev->name);
+ priv->broken_disableport = 1;
                 goto out;
         }
 
- err = __orinoco_program_rids(priv);
- if (err)
+ err = __orinoco_program_rids(dev);
+ if (err) {
+ printk(KERN_WARNING "%s: Unable to reconfigure card\n",
+ dev->name);
                 goto out;
+ }
 
         err = hermes_enable_port(hw, 0);
         if (err) {
- printk(KERN_ERR "%s: Unable to enable port in orinco_reconfigure()\n",
- priv->ndev->name);
+ printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
+ dev->name);
                 goto out;
         }
 
  out:
+ if (err) {
+ printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
+ schedule_work(&priv->reset_work);
+ err = 0;
+ }
+
         orinoco_unlock(priv, &flags);
         return err;
 
 }
 
 /* This must be called from user context, without locks held - use
- * schedule_task() */
+ * schedule_work() */
 static void orinoco_reset(struct net_device *dev)
 {
         struct orinoco_private *priv = dev->priv;
+ struct hermes *hw = &priv->hw;
         int err;
         unsigned long flags;
 
         err = orinoco_lock(priv, &flags);
         if (err)
+ /* When the hardware becomes available again, whatever
+ * detects that is responsible for re-initializing
+ * it. So no need for anything further*/
                 return;
 
- priv->hw_unavailable = 1;
+ netif_stop_queue(dev);
+
+ /* Shut off interrupts. Depending on what state the hardware
+ * is in, this might not work, but we'll try anyway */
+ hermes_set_irqmask(hw, 0);
+ hermes_write_regn(hw, EVACK, 0xffff);
+
+ priv->hw_unavailable++;
+ priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */
+ priv->connected = 0;
+
         orinoco_unlock(priv, &flags);
 
         if (priv->hard_reset)
@@ -936,18 +1014,22 @@
                 return;
         }
 
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irq(&priv->lock); /* This has to be called from user context */
 
- priv->hw_unavailable = 0;
+ priv->hw_unavailable--;
 
- err = __orinoco_up(dev);
- if (err) {
- printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
- dev->name, err);
- } else
- dev->trans_start = jiffies;
+ /* priv->open or priv->hw_unavailable might have changed while
+ * we dropped the lock */
+ if (priv->open && (! priv->hw_unavailable)) {
+ err = __orinoco_up(dev);
+ if (err) {
+ printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
+ dev->name, err);
+ } else
+ dev->trans_start = jiffies;
+ }
 
- orinoco_unlock(priv, &flags);
+ spin_unlock_irq(&priv->lock);
 
         return;
 }
@@ -979,10 +1061,18 @@
         }
 }
 
+/* Does the frame have a SNAP header indicating it should be
+ * de-encapsulated to Ethernet-II? */
 static inline int
-is_snap(struct header_struct *hdr)
+is_ethersnap(struct header_struct *hdr)
 {
- return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3);
+ /* We de-encapsulate all packets which, a) have SNAP headers
+ * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
+ * and where b) the OUI of the SNAP header is 00:00:00 or
+ * 00:00:f8 - we need both because different APs appear to use
+ * different OUIs for some reason */
+ return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0)
+ && ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) );
 }
 
 static void
@@ -1140,7 +1230,8 @@
         return 0;
 }
 
-static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN])
+static int orinoco_hw_get_bssid(struct orinoco_private *priv,
+ char buf[ETH_ALEN])
 {
         hermes_t *hw = &priv->hw;
         int err = 0;
@@ -1159,7 +1250,7 @@
 }
 
 static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
- char buf[IW_ESSID_MAX_SIZE+1])
+ char buf[IW_ESSID_MAX_SIZE+1])
 {
         hermes_t *hw = &priv->hw;
         int err = 0;
@@ -1236,9 +1327,8 @@
         }
 
         if ( (channel < 1) || (channel > NUM_CHANNELS) ) {
- struct net_device *dev = priv->ndev;
-
- printk(KERN_WARNING "%s: Channel out of range (%d)!\n", dev->name, channel);
+ printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
+ priv->ndev->name, channel);
                 err = -EBUSY;
                 goto out;
 
@@ -1253,8 +1343,8 @@
         return err ? err : freq;
 }
 
-static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates,
- s32 *rates, int max)
+static int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
+ int *numrates, s32 *rates, int max)
 {
         hermes_t *hw = &priv->hw;
         struct hermes_idstring list;
@@ -1354,9 +1444,9 @@
  */
 void orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
- struct orinoco_private *priv = (struct orinoco_private *) dev_id;
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct orinoco_private *priv = dev->priv;
         hermes_t *hw = &priv->hw;
- struct net_device *dev = priv->ndev;
         int count = MAX_IRQLOOPS_PER_IRQ;
         u16 evstat, events;
         /* These are used to detect a runaway interrupt situation */
@@ -1380,11 +1470,11 @@
 
         while (events && count--) {
                 if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
- printk(KERN_CRIT "%s: IRQ handler is looping too \
-much! Shutting down.\n",
- dev->name);
- /* Perform an emergency shutdown */
+ printk(KERN_WARNING "%s: IRQ handler is looping too "
+ "much! Resetting.\n", dev->name);
+ /* Disable interrupts for now */
                         hermes_set_irqmask(hw, 0);
+ schedule_work(&priv->reset_work);
                         break;
                 }
 
@@ -1395,21 +1485,21 @@
                 }
 
                 if (events & HERMES_EV_TICK)
- __orinoco_ev_tick(priv, hw);
+ __orinoco_ev_tick(dev, hw);
                 if (events & HERMES_EV_WTERR)
- __orinoco_ev_wterr(priv, hw);
+ __orinoco_ev_wterr(dev, hw);
                 if (events & HERMES_EV_INFDROP)
- __orinoco_ev_infdrop(priv, hw);
+ __orinoco_ev_infdrop(dev, hw);
                 if (events & HERMES_EV_INFO)
- __orinoco_ev_info(priv, hw);
+ __orinoco_ev_info(dev, hw);
                 if (events & HERMES_EV_RX)
- __orinoco_ev_rx(priv, hw);
+ __orinoco_ev_rx(dev, hw);
                 if (events & HERMES_EV_TXEXC)
- __orinoco_ev_txexc(priv, hw);
+ __orinoco_ev_txexc(dev, hw);
                 if (events & HERMES_EV_TX)
- __orinoco_ev_tx(priv, hw);
+ __orinoco_ev_tx(dev, hw);
                 if (events & HERMES_EV_ALLOC)
- __orinoco_ev_alloc(priv, hw);
+ __orinoco_ev_alloc(dev, hw);
                 
                 hermes_write_regn(hw, EVACK, events);
 
@@ -1420,22 +1510,22 @@
         orinoco_unlock(priv, &flags);
 }
 
-static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw)
+static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw)
 {
- printk(KERN_DEBUG "%s: TICK\n", priv->ndev->name);
+ printk(KERN_DEBUG "%s: TICK\n", dev->name);
 }
 
-static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw)
+static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw)
 {
         /* This seems to happen a fair bit under load, but ignoring it
            seems to work fine...*/
         printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n",
- priv->ndev->name);
+ dev->name);
 }
 
-static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw)
+static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
 {
- printk(KERN_WARNING "%s: Information frame lost.\n", priv->ndev->name);
+ printk(KERN_WARNING "%s: Information frame lost.\n", dev->name);
 }
 
 static void print_linkstatus(struct net_device *dev, u16 status)
@@ -1472,9 +1562,9 @@
                dev->name, s, status);
 }
 
-static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw)
+static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
 {
- struct net_device *dev = priv->ndev;
+ struct orinoco_private *priv = dev->priv;
         u16 infofid;
         struct {
                 u16 len;
@@ -1573,9 +1663,9 @@
         }
 }
 
-static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw)
+static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
 {
- struct net_device *dev = priv->ndev;
+ struct orinoco_private *priv = dev->priv;
         struct net_device_stats *stats = &priv->stats;
         struct iw_statistics *wstats = &priv->wstats;
         struct sk_buff *skb = NULL;
@@ -1664,7 +1754,7 @@
          * So, check ourselves */
         if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
            ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
- is_snap(&hdr)) {
+ is_ethersnap(&hdr)) {
                 /* These indicate a SNAP within 802.2 LLC within
                    802.11 frame which we'll need to de-encapsulate to
                    the original EthernetII frame. */
@@ -1726,9 +1816,9 @@
         return;
 }
 
-static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw)
+static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
 {
- struct net_device *dev = priv->ndev;
+ struct orinoco_private *priv = dev->priv;
         struct net_device_stats *stats = &priv->stats;
         u16 fid = hermes_read_regn(hw, TXCOMPLFID);
         struct hermes_tx_descriptor desc;
@@ -1752,8 +1842,9 @@
         hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
-static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw)
+static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
 {
+ struct orinoco_private *priv = dev->priv;
         struct net_device_stats *stats = &priv->stats;
 
         stats->tx_packets++;
@@ -1761,9 +1852,10 @@
         hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
-static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw)
+static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
 {
- struct net_device *dev = priv->ndev;
+ struct orinoco_private *priv = dev->priv;
+
         u16 fid = hermes_read_regn(hw, ALLOCFID);
 
         if (fid != priv->txfid) {
@@ -1945,7 +2037,7 @@
 
         TRACE_ENTER(dev->name);
 
- /* No need to lock, the resetting flag is already set in
+ /* No need to lock, the hw_unavailable flag is already set in
          * alloc_orinocodev() */
         priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN;
 
@@ -2081,8 +2173,6 @@
         priv->wep_on = 0;
         priv->tx_key = 0;
 
- priv->hw_unavailable = 0;
-
         err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
         if (err == -EIO) {
                 /* Try workaround for old Symbol firmware bug */
@@ -2102,6 +2192,12 @@
                 goto out;
         }
 
+ /* Make the hardware available, as long as it hasn't been
+ * removed elsewhere (e.g. by PCMCIA hot unplug) */
+ spin_lock_irq(&priv->lock);
+ priv->hw_unavailable--;
+ spin_unlock_irq(&priv->lock);
+
         printk(KERN_DEBUG "%s: ready\n", dev->name);
 
  out:
@@ -2267,7 +2363,7 @@
 
         /* Length of the packet body */
         /* FIXME: what if the skb is smaller than this? */
- len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN);
+ len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN);
 
         eh = (struct ethhdr *)skb->data;
 
@@ -2281,6 +2377,12 @@
                 goto fail;
         }
 
+ /* Clear the 802.11 header and data length fields - some
+ * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
+ * if this isn't done. */
+ hermes_clear_words(hw, HERMES_DATA0,
+ HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
+
         /* Encapsulate Ethernet-II frames */
         if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */
                 struct header_struct hdr;
@@ -2362,7 +2464,7 @@
 
         stats->tx_errors++;
 
- schedule_task(&priv->timeout_task);
+ schedule_work(&priv->reset_work);
 }
 
 static int
@@ -2532,7 +2634,7 @@
         }
 
         err = orinoco_hw_get_bitratelist(priv, &numrates,
- range.bitrate, IW_MAX_BITRATES);
+ range.bitrate, IW_MAX_BITRATES);
         if (err)
                 return err;
         range.num_bitrates = numrates;
@@ -3128,7 +3230,7 @@
                                 rrq->value = 5500000;
                         else
                                 rrq->value = val * 1000000;
- break;
+ break;
                 case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
                 case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
                         for (i = 0; i < BITRATE_TABLE_SIZE; i++)
@@ -3754,7 +3856,7 @@
                 
                 printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
 
- schedule_task(&priv->timeout_task);
+ schedule_work(&priv->reset_work);
                 break;
 
         case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */
@@ -3839,7 +3941,7 @@
         }
         
         if (! err && changed && netif_running(dev)) {
- err = orinoco_reconfigure(priv);
+ err = orinoco_reconfigure(dev);
         }
 
         TRACE_EXIT(dev->name);
@@ -3924,7 +4026,7 @@
         DEBUG_REC(PRIID,WORDS),
         DEBUG_REC(PRISUPRANGE,WORDS),
         DEBUG_REC(CFIACTRANGES,WORDS),
- DEBUG_REC(NICSERNUM,WORDS),
+ DEBUG_REC(NICSERNUM,XSTRING),
         DEBUG_REC(NICID,WORDS),
         DEBUG_REC(MFISUPRANGE,WORDS),
         DEBUG_REC(CFISUPRANGE,WORDS),
@@ -4062,7 +4164,7 @@
         priv->hw_unavailable = 1; /* orinoco_init() must clear this
                                    * before anything else touches the
                                    * hardware */
- INIT_TQUEUE(&priv->timeout_task, (void (*)(void *))orinoco_reset, dev);
+ INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev);
 
         priv->last_linkstatus = 0xffff;
         priv->connected = 0;
@@ -4079,13 +4181,14 @@
 
 EXPORT_SYMBOL(__orinoco_up);
 EXPORT_SYMBOL(__orinoco_down);
+EXPORT_SYMBOL(orinoco_stop);
 EXPORT_SYMBOL(orinoco_reinit_firmware);
 
 EXPORT_SYMBOL(orinoco_interrupt);
 
 /* Can't be declared "const" or the whole __initdata section will
  * become const */
-static char version[] __initdata = "orinoco.c 0.13b (David Gibson <hermes@gibson.dropbear.id.au> and others)";
+static char version[] __initdata = "orinoco.c 0.13d (David Gibson <hermes@gibson.dropbear.id.au> and others)";
 
 static int __init init_orinoco(void)
 {
diff -uNr linux-2.4-pristine/drivers/net/wireless/orinoco.h linux-orinoco/drivers/net/wireless/orinoco.h
--- linux-2.4-pristine/drivers/net/wireless/orinoco.h 2003-04-23 14:55:29.000000000 +1000
+++ linux-orinoco/drivers/net/wireless/orinoco.h 2003-04-23 14:59:19.000000000 +1000
@@ -11,9 +11,20 @@
 #include <linux/spinlock.h>
 #include <linux/netdevice.h>
 #include <linux/wireless.h>
-#include <linux/tqueue.h>
+#include <linux/version.h>
 #include "hermes.h"
 
+/* Workqueue / task queue backwards compatibility stuff */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
+#include <linux/workqueue.h>
+#else
+#include <linux/tqueue.h>
+#define work_struct tq_struct
+#define INIT_WORK INIT_TQUEUE
+#define schedule_work schedule_task
+#endif
+
 /* To enable debug messages */
 //#define ORINOCO_DEBUG 3
 
@@ -42,7 +53,7 @@
         /* Synchronisation stuff */
         spinlock_t lock;
         int hw_unavailable;
- struct tq_struct timeout_task;
+ struct work_struct reset_work;
 
         /* driver state */
         int open;
@@ -72,6 +83,7 @@
         int has_sensitivity;
         int nicbuf_size;
         u16 channel_mask;
+ int broken_disableport;
 
         /* Configuration paramaters */
         u32 iw_mode;
@@ -111,8 +123,8 @@
                                            int (*hard_reset)(struct orinoco_private *));
 extern int __orinoco_up(struct net_device *dev);
 extern int __orinoco_down(struct net_device *dev);
-int orinoco_reinit_firmware(struct net_device *dev);
-
+extern int orinoco_stop(struct net_device *dev);
+extern int orinoco_reinit_firmware(struct net_device *dev);
 extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs);
 
 /********************************************************************/
diff -uNr linux-2.4-pristine/drivers/net/wireless/orinoco_cs.c linux-orinoco/drivers/net/wireless/orinoco_cs.c
--- linux-2.4-pristine/drivers/net/wireless/orinoco_cs.c 2003-04-23 14:55:29.000000000 +1000
+++ linux-orinoco/drivers/net/wireless/orinoco_cs.c 2003-04-23 14:59:19.000000000 +1000
@@ -1,4 +1,4 @@
-/* orinoco_cs.c 0.13b - (formerly known as dldwd_cs.c)
+/* orinoco_cs.c 0.13d - (formerly known as dldwd_cs.c)
  *
  * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
  * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -22,7 +22,6 @@
 #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>
@@ -38,7 +37,6 @@
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cisreg.h>
 #include <pcmcia/ds.h>
-#include <pcmcia/bus_ops.h>
 
 #include "orinoco.h"
 
@@ -269,19 +267,12 @@
                 return;
         }
 
- /*
- If the device is currently configured and active, we won't
- actually delete it yet. Instead, it is marked so that when
- the release() function is called, that will trigger a proper
- detach().
- */
         if (link->state & DEV_CONFIG) {
-#ifdef PCMCIA_DEBUG
- printk(KERN_DEBUG "orinoco_cs: detach postponed, '%s' "
- "still locked\n", link->dev->dev_name);
-#endif
- link->state |= DEV_STALE_LINK;
- return;
+ orinoco_cs_release((u_long)link);
+ if (link->state & DEV_CONFIG) {
+ link->state |= DEV_STALE_LINK;
+ return;
+ }
         }
 
         /* Break the link with Card Services */
@@ -472,7 +463,7 @@
                                 link->irq.IRQInfo2 |= 1 << irq_list[i];
                 
                   link->irq.Handler = orinoco_interrupt;
- link->irq.Instance = priv;
+ link->irq.Instance = dev;
                 
                 CS_CHECK(RequestIRQ, link->handle, &link->irq);
         }
@@ -549,18 +540,13 @@
         dev_link_t *link = (dev_link_t *) arg;
         struct net_device *dev = link->priv;
         struct orinoco_private *priv = dev->priv;
+ unsigned long flags;
 
- /*
- If the device is currently in use, we won't release until it
- is actually closed, because until then, we can't be sure that
- no one will try to access the device or its data structures.
- */
- if (priv->open) {
- DEBUG(0, "orinoco_cs: release postponed, '%s' still open\n",
- link->dev->dev_name);
- link->state |= DEV_STALE_CONFIG;
- return;
- }
+ /* We're committed to taking the device away now, so mark the
+ * hardware as unavailable */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->hw_unavailable++;
+ spin_unlock_irqrestore(&priv->lock, flags);
 
         /* Don't bother checking to see if these succeed or not */
         CardServices(ReleaseConfiguration, link->handle);
@@ -593,14 +579,9 @@
                         orinoco_lock(priv, &flags);
 
                         netif_device_detach(dev);
- priv->hw_unavailable = 1;
+ priv->hw_unavailable++;
 
                         orinoco_unlock(priv, &flags);
-
-/* if (link->open) */
-/* orinoco_cs_stop(dev); */
-
- mod_timer(&link->release, jiffies + HZ / 20);
                 }
                 break;
 
@@ -619,13 +600,8 @@
                            a better way, short of rewriting the PCMCIA
                            layer to not suck :-( */
                         if (! test_bit(0, &card->hard_reset_in_progress)) {
- err = orinoco_lock(priv, &flags);
- if (err) {
- printk(KERN_ERR "%s: hw_unavailable on SUSPEND/RESET_PHYSICAL\n",
- dev->name);
- break;
- }
-
+ spin_lock_irqsave(&priv->lock, flags);
+
                                 err = __orinoco_down(dev);
                                 if (err)
                                         printk(KERN_WARNING "%s: %s: Error %d downing interface\n",
@@ -634,9 +610,9 @@
                                                err);
                                 
                                 netif_device_detach(dev);
- priv->hw_unavailable = 1;
-
- orinoco_unlock(priv, &flags);
+ priv->hw_unavailable++;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
                         }
 
                         CardServices(ReleaseConfiguration, link->handle);
@@ -653,10 +629,6 @@
                         CardServices(RequestConfiguration, link->handle,
                                      &link->conf);
 
- /* If we're only getting these events because
- of the ResetCard in the hard reset, we
- don't need to do anything - orinoco_reset()
- will handle reinitialization. */
                         if (! test_bit(0, &card->hard_reset_in_progress)) {
                                 err = orinoco_reinit_firmware(dev);
                                 if (err) {
@@ -668,9 +640,9 @@
                                 spin_lock_irqsave(&priv->lock, flags);
                                 
                                 netif_device_attach(dev);
- priv->hw_unavailable = 0;
+ priv->hw_unavailable--;
                                 
- if (priv->open) {
+ if (priv->open && ! priv->hw_unavailable) {
                                         err = __orinoco_up(dev);
                                         if (err)
                                                 printk(KERN_ERR "%s: Error %d restarting card\n",
@@ -678,7 +650,7 @@
                                         
                                 }
 
- orinoco_unlock(priv, &flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
                         }
                 }
                 break;
@@ -693,7 +665,7 @@
 
 /* Can't be declared "const" or the whole __initdata section will
  * become const */
-static char version[] __initdata = "orinoco_cs.c 0.13b (David Gibson <hermes@gibson.dropbear.id.au> and others)";
+static char version[] __initdata = "orinoco_cs.c 0.13d (David Gibson <hermes@gibson.dropbear.id.au> and others)";
 
 static int __init
 init_orinoco_cs(void)
@@ -722,7 +694,6 @@
         if (dev_list)
                 DEBUG(0, "orinoco_cs: Removing leftover devices.\n");
         while (dev_list != NULL) {
- del_timer(&dev_list->release);
                 if (dev_list->state & DEV_CONFIG)
                         orinoco_cs_release((u_long) dev_list);
                 orinoco_cs_detach(dev_list);
diff -uNr linux-2.4-pristine/drivers/net/wireless/orinoco_pci.c linux-orinoco/drivers/net/wireless/orinoco_pci.c
--- linux-2.4-pristine/drivers/net/wireless/orinoco_pci.c 2003-04-23 14:55:29.000000000 +1000
+++ linux-orinoco/drivers/net/wireless/orinoco_pci.c 2003-04-23 14:59:19.000000000 +1000
@@ -1,4 +1,4 @@
-/* orinoco_pci.c 0.13b
+/* orinoco_pci.c 0.13d
  *
  * Driver for Prism II devices that have a direct PCI interface
  * (i.e., not in a Pcmcia or PLX bridge)
@@ -143,8 +143,6 @@
         unsigned long timeout;
         u16 reg;
 
- TRACE_ENTER(priv->ndev->name);
-
         /* Assert the reset until the card notice */
         hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK);
         printk(KERN_NOTICE "Reset done");
@@ -181,8 +179,6 @@
         }
         printk(KERN_NOTICE "pci_cor : reg = 0x%X - %lX - %lX\n", reg, timeout, jiffies);
 
- TRACE_EXIT(priv->ndev->name);
-
         return 0;
 }
 
@@ -232,7 +228,7 @@
         hermes_struct_init(&(priv->hw), dev->base_addr, HERMES_MEM, HERMES_32BIT_REGSPACING);
         pci_set_drvdata(pdev, dev);
 
- err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, priv);
+ err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, dev);
         if (err) {
                 printk(KERN_ERR "orinoco_pci: Error allocating IRQ %d.\n", pdev->irq);
                 err = -EBUSY;
@@ -264,7 +260,7 @@
                         unregister_netdev(dev);
 
                 if (dev->irq)
- free_irq(dev->irq, priv);
+ free_irq(dev->irq, dev);
 
                 kfree(dev);
         }
@@ -286,7 +282,7 @@
         unregister_netdev(dev);
 
         if (dev->irq)
- free_irq(dev->irq, priv);
+ free_irq(dev->irq, dev);
 
         if (priv->hw.iobase)
                 iounmap((unsigned char *) priv->hw.iobase);
@@ -321,7 +317,7 @@
         
         netif_device_detach(dev);
 
- priv->hw_unavailable = 1;
+ priv->hw_unavailable++;
         
         orinoco_unlock(priv, &flags);
 
@@ -348,15 +344,15 @@
 
         netif_device_attach(dev);
 
- if (priv->open) {
+ priv->hw_unavailable--;
+
+ if (priv->open && (! priv->hw_unavailable)) {
                 err = __orinoco_up(dev);
                 if (err)
                         printk(KERN_ERR "%s: Error %d restarting card on orinoco_pci_resume()\n",
                                dev->name, err);
         }
         
- priv->hw_unavailable = 0;
-
         spin_unlock_irqrestore(&priv->lock, flags);
 
         return 0;
@@ -378,7 +374,7 @@
         .resume = orinoco_pci_resume,
 };
 
-static char version[] __initdata = "orinoco_pci.c 0.13b (David Gibson <hermes@gibson.dropbear.id.au> & Jean Tourrilhes <jt@hpl.hp.com>)";
+static char version[] __initdata = "orinoco_pci.c 0.13d (David Gibson <hermes@gibson.dropbear.id.au> & Jean Tourrilhes <jt@hpl.hp.com>)";
 MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
 MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface");
 MODULE_LICENSE("Dual MPL/GPL");
diff -uNr linux-2.4-pristine/drivers/net/wireless/orinoco_plx.c linux-orinoco/drivers/net/wireless/orinoco_plx.c
--- linux-2.4-pristine/drivers/net/wireless/orinoco_plx.c 2003-04-23 14:55:29.000000000 +1000
+++ linux-orinoco/drivers/net/wireless/orinoco_plx.c 2003-04-23 14:59:19.000000000 +1000
@@ -1,4 +1,4 @@
-/* orinoco_plx.c 0.13b
+/* orinoco_plx.c 0.13d
  *
  * Driver for Prism II devices which would usually be driven by orinoco_cs,
  * but are connected to the PCI bus by a PLX9052.
@@ -243,7 +243,7 @@
                         HERMES_IO, HERMES_16BIT_REGSPACING);
         pci_set_drvdata(pdev, dev);
 
- err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, priv);
+ err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, dev);
         if (err) {
                 printk(KERN_ERR "orinoco_plx: Error allocating IRQ %d.\n", pdev->irq);
                 err = -EBUSY;
@@ -266,7 +266,7 @@
                         unregister_netdev(dev);
                 
                 if (dev->irq)
- free_irq(dev->irq, priv);
+ free_irq(dev->irq, dev);
                 
                 kfree(priv);
         }
@@ -285,7 +285,6 @@
 static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev)
 {
         struct net_device *dev = pci_get_drvdata(pdev);
- struct orinoco_private *priv = dev->priv;
 
         if (! dev)
                 BUG();
@@ -293,7 +292,7 @@
         unregister_netdev(dev);
                 
         if (dev->irq)
- free_irq(dev->irq, priv);
+ free_irq(dev->irq, dev);
                 
         pci_set_drvdata(pdev, NULL);
 
@@ -334,7 +333,7 @@
         .resume = 0,
 };
 
-static char version[] __initdata = "orinoco_plx.c 0.13b (Daniel Barlow <dan@telent.net>, David Gibson <hermes@gibson.dropbear.id.au>)";
+static char version[] __initdata = "orinoco_plx.c 0.13d (Daniel Barlow <dan@telent.net>, David Gibson <hermes@gibson.dropbear.id.au>)";
 MODULE_AUTHOR("Daniel Barlow <dan@telent.net>");
 MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge");
 #ifdef MODULE_LICENSE
diff -uNr linux-2.4-pristine/drivers/net/wireless/orinoco_tmd.c linux-orinoco/drivers/net/wireless/orinoco_tmd.c
--- linux-2.4-pristine/drivers/net/wireless/orinoco_tmd.c 1970-01-01 10:00:00.000000000 +1000
+++ linux-orinoco/drivers/net/wireless/orinoco_tmd.c 2003-04-23 14:59:19.000000000 +1000
@@ -0,0 +1,240 @@
+/* orinoco_tmd.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 TMD7160.
+ *
+ * Copyright (C) 2003 Joerg Dorchain <joerg@dorchain.net>
+ * based heavily upon orinoco_plx.c 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.
+
+ * Caution: this is experimental and probably buggy. For success and
+ * failure reports for different cards and adaptors, see
+ * orinoco_tmd_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.
+ *
+ * This driver is modeled after the orinoco_plx driver. The main
+ * difference is that the TMD chip has only IO port ranges and no
+ * memory space, i.e. no access to the CIS. Compared to the PLX chip,
+ * the io range functionalities are exchanged.
+ *
+ * Pheecom sells cards with the TMD chip as "ASIC version"
+ */
+
+#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/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/cisreg.h>
+
+#include "hermes.h"
+#include "orinoco.h"
+
+static char dev_info[] = "orinoco_tmd";
+
+#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA | COR_FUNC_ENA) /* Enable PC card with level triggered irqs and irq requests */
+
+
+static int orinoco_tmd_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err = 0;
+ 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;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return -EIO;
+
+ printk(KERN_DEBUG "TMD setup\n");
+ pccard_ioaddr = pci_resource_start(pdev, 2);
+ pccard_iolen = pci_resource_len(pdev, 2);
+ if (! request_region(pccard_ioaddr, pccard_iolen, dev_info)) {
+ printk(KERN_ERR "orinoco_tmd: I/O resource at 0x%lx len 0x%lx busy\n",
+ pccard_ioaddr, pccard_iolen);
+ pccard_ioaddr = 0;
+ err = -EBUSY;
+ goto fail;
+ }
+ addr = pci_resource_start(pdev, 1);
+ outb(COR_VALUE, addr);
+ mdelay(1);
+ reg = inb(addr);
+ if (reg != COR_VALUE) {
+ printk(KERN_ERR "orinoco_tmd: Error setting TMD COR values %x should be %x\n", reg, COR_VALUE);
+ err = -EIO;
+ goto fail;
+ }
+
+ dev = alloc_orinocodev(0, NULL);
+ if (! dev) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ priv = dev->priv;
+ dev->base_addr = pccard_ioaddr;
+ SET_MODULE_OWNER(dev);
+
+ printk(KERN_DEBUG
+ "Detected Orinoco/Prism2 TMD 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,
+ HERMES_IO, HERMES_16BIT_REGSPACING);
+ pci_set_drvdata(pdev, dev);
+
+ err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name,
+ dev);
+ if (err) {
+ printk(KERN_ERR "orinoco_tmd: 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;
+
+ return 0; /* succeeded */
+
+ fail:
+ printk(KERN_DEBUG "orinoco_tmd: init_one(), FAIL!\n");
+
+ if (priv) {
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+
+ kfree(priv);
+ }
+
+ if (pccard_ioaddr)
+ release_region(pccard_ioaddr, pccard_iolen);
+
+ pci_disable_device(pdev);
+
+ return err;
+}
+
+static void __devexit orinoco_tmd_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (! dev)
+ BUG();
+
+ unregister_netdev(dev);
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+
+ pci_set_drvdata(pdev, NULL);
+
+ kfree(dev);
+
+ release_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2));
+
+ pci_disable_device(pdev);
+}
+
+
+static struct pci_device_id orinoco_tmd_pci_id_table[] __devinitdata = {
+ {0x15e8, 0x0131, PCI_ANY_ID, PCI_ANY_ID,}, /* NDC and OEMs, e.g. pheecom */
+ {0,},
+};
+
+MODULE_DEVICE_TABLE(pci, orinoco_tmd_pci_id_table);
+
+static struct pci_driver orinoco_tmd_driver = {
+ .name = "orinoco_tmd",
+ .id_table = orinoco_tmd_pci_id_table,
+ .probe = orinoco_tmd_init_one,
+ .remove = __devexit_p(orinoco_tmd_remove_one),
+ .suspend = 0,
+ .resume = 0,
+};
+
+static char version[] __initdata = "orinoco_tmd.c 0.01 (Joerg Dorchain <joerg@dorchain.net>)";
+MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>");
+MODULE_DESCRIPTION("Driver for wireless LAN cards using the TMD7160 PCI bridge");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual MPL/GPL");
+#endif
+
+static int __init orinoco_tmd_init(void)
+{
+ printk(KERN_DEBUG "%s\n", version);
+ return pci_module_init(&orinoco_tmd_driver);
+}
+
+extern void __exit orinoco_tmd_exit(void)
+{
+ pci_unregister_driver(&orinoco_tmd_driver);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(HZ);
+}
+
+module_init(orinoco_tmd_init);
+module_exit(orinoco_tmd_exit);
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */

-- 
David Gibson			| For every complex problem there is a
david@gibson.dropbear.id.au	| solution which is simple, neat and
				| wrong.
http://www.ozlabs.org/people/dgibson
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Wed Apr 23 2003 - 22:00:35 EST