patch-2.4.21 linux-2.4.21/drivers/net/wireless/orinoco.c

Next file: linux-2.4.21/drivers/net/wireless/orinoco.h
Previous file: linux-2.4.21/drivers/net/wireless/ieee802_11.h
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/drivers/net/wireless/orinoco.c linux-2.4.21/drivers/net/wireless/orinoco.c
@@ -1,4 +1,4 @@
-/* orinoco.c 0.11b	- (formerly known as dldwd_cs.c and orinoco_cs.c)
+/* orinoco.c 0.13b	- (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.
@@ -240,7 +240,7 @@
  *	  was never very useful.
  *	o Make Rx errors less noisy.
  *
- * v0.10 -> v0.11 - 5 Apr Mar 2002 - David Gibson
+ * v0.10 -> v0.11 - 5 Apr 2002 - David Gibson
  *	o Laid the groundwork in hermes.[ch] for devices which map
  *	  into PCI memory space rather than IO space.
  *	o Fixed bug in multicast handling (cleared multicast list when
@@ -256,7 +256,7 @@
  *	o Fixes for recent Symbol firmwares which lack AP density
  *	  (Pavel Roskin).
  *
- * v0.11 -> v0.11b - 29 Apr 2002 - David Gibson
+ * v0.11 -> v0.11a - 29 Apr 2002 - David Gibson
  *	o Handle different register spacing, necessary for Prism 2.5
  *	  PCI adaptors (Steve Hill).
  *	o Cleaned up initialization of card structures in orinoco_cs
@@ -279,44 +279,101 @@
  *	o Fixed multiple bad kfree() bugs introduced by the
  *	  alloc_orinocodev() changes.
  *
+ * v0.11b -> v0.12 - 19 Jun 2002 - David Gibson
+ *	o Support changing the MAC address.
+ *	o Correct display of Intersil firmware revision numbers.
+ *	o Entirely revised locking scheme.  Should be both simpler and
+ *	   better.
+ *	o Merged some common code in orinoco_plx, orinoco_pci and
+ *	  airport by creating orinoco_default_{open,stop,reset}()
+ *	  which are used as the dev->open, dev->stop, priv->reset
+ *	  callbacks if none are specified when alloc_orinocodev() is
+ *	  called.
+ *	o Removed orinoco_plx_interupt() and orinoco_pci_interrupt().
+ *	  They didn't do anything.
+ *
+ * v0.12 -> v0.12a - 4 Jul 2002 - David Gibson
+ *	o Some rearrangement of code.
+ *	o Numerous fixups to locking and rest handling, particularly
+ *	  for PCMCIA.
+ *	o This allows open and stop net_device methods to be in
+ *	  orinoco.c now, rather than in the init modules.
+ *	o In orinoco_cs.c link->priv now points to the struct
+ *	  net_device not to the struct orinoco_private.
+ *	o Added a check for undersized SNAP frames, which could cause
+ *	  crashes.
+ *
+ * v0.12a -> v0.12b - 11 Jul 2002 - David Gibson
+ *	o Fix hw->num_init testing code, so num_init is actually
+ *	  incremented.
+ *	o Fix very stupid bug in orinoco_cs which broke compile with
+ *	  CONFIG_SMP.
+ *	o Squashed a warning.
+ *
+ * v0.12b -> v0.12c - 26 Jul 2002 - David Gibson
+ *	o Change to C9X style designated initializers.
+ *	o Add support for 3Com AirConnect PCI.
+ *	o No longer ignore the hard_reset argument to
+ *	  alloc_orinocodev().  Oops.
+ *
+ * v0.12c -> v0.13beta1 - 13 Sep 2002 - David Gibson
+ *	o Revert the broken 0.12* locking scheme and go to a new yet
+ *	  simpler scheme.
+ *	o Do firmware resets only in orinoco_init() and when waking
+ *	  the card from hard sleep.
+ *
+ * v0.13beta1 -> v0.13 - 27 Sep 2002 - David Gibson
+ *	o Re-introduced full resets (via schedule_task()) on Tx
+ *	  timeout.
+ *
+ * v0.13 -> v0.13a - 30 Sep 2002 - David Gibson
+ *	o Minor cleanups to info frame handling.  Add basic support
+ *	  for linkstatus info frames.
+ *	o Include required kernel headers in orinoco.h, to avoid
+ *	  compile problems.
+ *
+ * v0.13a -> v0.13b - 10 Feb 2003 - David Gibson
+ *	o Implemented hard reset for Airport cards
+ *	o Experimental suspend/resume implementation for orinoco_pci
+ *	o Abolished /proc debugging support, replaced with a debugging
+ *	  iwpriv.  Now it's ugly and simple instead of ugly and complex.
+ *	o Bugfix in hermes.c if the firmware returned a record length
+ *	  of 0, we could go clobbering memory.
+ *	o Bugfix in orinoco_stop() - it used to fail if hw_unavailable
+ *	  was set, which was usually true on PCMCIA hot removes.
+ * 	o Track LINKSTATUS messages, silently drop Tx packets before
+ * 	  we are connected (avoids cofusing the firmware), and only
+ * 	  give LINKSTATUS printk()s if the status has changed.
+ *
  * TODO
- *	o New wireless extensions API
+
+ *	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
+ *	  headers (patch from Thomas 'Dent' Mirlacher)
  *	o Fix possible races in SPY handling.
  *	o Disconnect wireless extensions from fundamental configuration.
- *
- *	o Convert /proc debugging stuff to seqfile
- *	o Use multiple Tx buffers
- */
-/* Notes on locking:
- *
- * The basic principle of operation is that everything except the
- * interrupt handler is serialized through a single spinlock in the
- * struct orinoco_private structure, using orinoco_lock() and
- * orinoco_unlock() (which in turn use spin_lock_bh() and
- * spin_unlock_bh()).
- *
- * The kernel's IRQ handling stuff ensures that the interrupt handler
- * does not re-enter itself. The interrupt handler is written such
- * that everything it does is safe without a lock: chiefly this means
- * that the Rx path uses one of the Hermes chipset's BAPs while
- * everything else uses the other.
- *
- * Actually, strictly speaking, the updating of the statistics from
- * the interrupt handler isn't safe without a lock.  However the worst
- * that can happen is that we perturb the packet/byte counts slightly.
- * We could fix this to use atomic types, but it's probably not worth
- * it.
- *
- * The big exception is that that we don't want the irq handler
- * running when we actually reset or shut down the card, because
- * strange things might happen (probably the worst would be one packet
- * of garbage, but you can't be too careful). For this we use
- * __orinoco_stop_irqs() which will set a flag to disable the interrupt
- * handler, and wait for any outstanding instances of the handler to
- * complete. THIS WILL LOSE INTERRUPTS! so it shouldn't be used except
- * for resets, where losing a few interrupts is acceptable. */
+ *	o (maybe) Software WEP support (patch from Stano Meduna).
+ *	o (maybe) Use multiple Tx buffers - driver handling queue
+ *	  rather than firmware. */
+
+/* Locking and synchronization:
+ *
+ * The basic principle is that everything is serialized through a
+ * single spinlock, priv->lock.  The lock is used in user, bh and irq
+ * context, so when taken outside hardirq context it should always be
+ * taken with interrupts disabled.  The lock protects both the
+ * hardware and the struct orinoco_private.
+ *
+ * Another flag, priv->hw_unavailable indicates that the hardware is
+ * unavailable for an extended period of time (e.g. suspended, or in
+ * the middle of a hard reset).  This flag is protected by the
+ * spinlock.  All code which touches the hardware should check the
+ * 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). */
 
 #include <linux/config.h>
 
@@ -332,32 +389,20 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
 #include <linux/wireless.h>
-#include <linux/list.h>
 
 #include "hermes.h"
 #include "hermes_rid.h"
 #include "orinoco.h"
 #include "ieee802_11.h"
 
-/* Wireless extensions backwards compatibility */
-#ifndef SIOCIWFIRSTPRIV
-#define SIOCIWFIRSTPRIV		SIOCDEVPRIVATE
-#endif /* SIOCIWFIRSTPRIV */
-
-/* We do this this way to avoid ifdefs in the actual code */
-#ifdef WIRELESS_SPY
-#define SPY_NUMBER(priv)	(priv->spy_number)
-#else
-#define SPY_NUMBER(priv)	0
-#endif /* WIRELESS_SPY */
+/********************************************************************/
+/* Module information                                               */
+/********************************************************************/
 
-static char version[] __initdata = "orinoco.c 0.11b (David Gibson <hermes@gibson.dropbear.id.au> and others)";
 MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
 MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards");
 #ifdef MODULE_LICENSE
@@ -371,6 +416,26 @@
 EXPORT_SYMBOL(orinoco_debug);
 #endif
 
+/********************************************************************/
+/* Compile time configuration and compatibility stuff               */
+/********************************************************************/
+
+/* Wireless extensions backwards compatibility */
+#ifndef SIOCIWFIRSTPRIV
+#define SIOCIWFIRSTPRIV		SIOCDEVPRIVATE
+#endif /* SIOCIWFIRSTPRIV */
+
+/* We do this this way to avoid ifdefs in the actual code */
+#ifdef WIRELESS_SPY
+#define SPY_NUMBER(priv)	(priv->spy_number)
+#else
+#define SPY_NUMBER(priv)	0
+#endif /* WIRELESS_SPY */
+
+/********************************************************************/
+/* Internal constants                                               */
+/********************************************************************/
+
 #define ORINOCO_MIN_MTU		256
 #define ORINOCO_MAX_MTU		(IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD)
 
@@ -379,13 +444,15 @@
 #define IRQ_BAP			1
 #define MAX_IRQLOOPS_PER_IRQ	10
 #define MAX_IRQLOOPS_PER_JIFFY	(20000/HZ)	/* Based on a guestimate of how many events the
-						   device can legitimately generate */
+						   device could legitimately generate */
 #define SMALL_KEY_SIZE		5
 #define LARGE_KEY_SIZE		13
 #define TX_NICBUF_SIZE_BUG	1585		/* Bug in Symbol firmware */
 
 #define DUMMY_FID		0xFFFF
 
+#define RUP_EVEN(a) (((a) + 1) & (~1))
+
 /*#define MAX_MULTICAST(priv)	(priv->firmware_type == FIRMWARE_TYPE_AGERE ? \
   HERMES_MAX_MULTICAST : 0)*/
 #define MAX_MULTICAST(priv)	(HERMES_MAX_MULTICAST)
@@ -403,7 +470,7 @@
 
 /* This tables gives the actual meanings of the bitrate IDs returned by the firmware. */
 struct {
-	int bitrate; /* in 100s of kilbits */
+	int bitrate; /* in 100s of kilobits */
 	int automatic;
 	u16 agere_txratectrl;
 	u16 intersil_txratectrl;
@@ -419,6 +486,10 @@
 };
 #define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0]))
 
+/********************************************************************/
+/* Data types                                                       */
+/********************************************************************/
+
 struct header_struct {
 	/* 802.3 */
 	u8 dest[ETH_ALEN];
@@ -438,27 +509,29 @@
 
 #define ENCAPS_OVERHEAD		(sizeof(encaps_hdr) + 2)
 
-/*
- * Function prototypes
- */
+/********************************************************************/
+/* Function prototypes                                              */
+/********************************************************************/
 
 static void orinoco_stat_gather(struct net_device *dev,
-			      struct sk_buff *skb,
-			      struct hermes_rx_descriptor *desc);
+				struct sk_buff *skb,
+				struct hermes_rx_descriptor *desc);
 
 static struct net_device_stats *orinoco_get_stats(struct net_device *dev);
 static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev);
 
 /* Hardware control routines */
 
+static int __orinoco_program_rids(struct orinoco_private *priv);
+
 static int __orinoco_hw_set_bitrate(struct orinoco_private *priv);
 static int __orinoco_hw_setup_wep(struct orinoco_private *priv);
 static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN]);
 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]);
 static long orinoco_hw_get_freq(struct orinoco_private *priv);
 static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates,
-				    s32 *rates, int max);
+				      s32 *rates, int max);
 static void __orinoco_set_multicast_list(struct net_device *dev);
 
 /* Interrupt handling routines */
@@ -471,6 +544,7 @@
 static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw);
 static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw);
 
+/* ioctl() routines */
 static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq);
 static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq);
 static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq);
@@ -491,164 +565,168 @@
 static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq);
 static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq);
 
-/* /proc debugging stuff */
-static int orinoco_proc_init(void);
-static void orinoco_proc_cleanup(void);
+static int orinoco_debug_dump_recs(struct orinoco_private *priv);
 
-/*
- * Inline functions
- */
-static inline void
-orinoco_lock(struct orinoco_private *priv)
-{
-	spin_lock_bh(&priv->lock);
-}
+/********************************************************************/
+/* Function prototypes                                              */
+/********************************************************************/
 
-static inline void
-orinoco_unlock(struct orinoco_private *priv)
+int __orinoco_up(struct net_device *dev)
 {
-	spin_unlock_bh(&priv->lock);
-}
+	struct orinoco_private *priv = dev->priv;
+	struct hermes *hw = &priv->hw;
+	int err;
 
-static inline int
-orinoco_irqs_allowed(struct orinoco_private *priv)
-{
-	return test_bit(ORINOCO_STATE_DOIRQ, &priv->state);
-}
+	err = __orinoco_program_rids(priv);
+	if (err) {
+		printk(KERN_ERR "%s: Error %d configuring card\n",
+		       dev->name, err);
+		return err;
+	}
 
-static inline void
-__orinoco_stop_irqs(struct orinoco_private *priv)
-{
-	hermes_t *hw = &priv->hw;
+	/* Fire things up again */
+	hermes_set_irqmask(hw, ORINOCO_INTEN);
+	err = hermes_enable_port(hw, 0);
+	if (err) {
+		printk(KERN_ERR "%s: Error %d enabling MAC port\n",
+		       dev->name, err);
+		return err;
+	}
 
-	hermes_set_irqmask(hw, 0);
-	clear_bit(ORINOCO_STATE_DOIRQ, &priv->state);
-	while (test_bit(ORINOCO_STATE_INIRQ, &priv->state))
-		;
+	netif_start_queue(dev);
+
+	return 0;
 }
 
-static inline void
-__orinoco_start_irqs(struct orinoco_private *priv, u16 irqmask)
+int __orinoco_down(struct net_device *dev)
 {
-	hermes_t *hw = &priv->hw;
+	struct orinoco_private *priv = dev->priv;
+	struct hermes *hw = &priv->hw;
+	int err;
 
-	TRACE_ENTER(priv->ndev->name);
+	netif_stop_queue(dev);
 
-	__cli(); /* FIXME: is this necessary? */
-	set_bit(ORINOCO_STATE_DOIRQ, &priv->state);
-	hermes_set_irqmask(hw, irqmask);
-	__sti();
+	err = hermes_disable_port(hw, 0);
+	if (err) {
+		printk(KERN_ERR "%s: Error %d disabling MAC port\n",
+		       dev->name, err);
+		return err;
+	}
+	hermes_set_irqmask(hw, 0);
+	hermes_write_regn(hw, EVACK, 0xffff);
 
-	TRACE_EXIT(priv->ndev->name);
+	return 0;
 }
 
-static inline void
-set_port_type(struct orinoco_private *priv)
+int orinoco_reinit_firmware(struct net_device *dev)
 {
-	switch (priv->iw_mode) {
-	case IW_MODE_INFRA:
-		priv->port_type = 1;
-		priv->allow_ibss = 0;
-		break;
-	case IW_MODE_ADHOC:
-		if (priv->prefer_port3) {
-			priv->port_type = 3;
-			priv->allow_ibss = 0;
-		} else {
-			priv->port_type = priv->ibss_port;
-			priv->allow_ibss = 1;
-		}
-		break;
-	default:
-		printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
-		       priv->ndev->name);
+	struct orinoco_private *priv = dev->priv;
+	struct hermes *hw = &priv->hw;
+	int err;
+
+	err = hermes_init(hw);
+	if (err)
+		return err;
+
+	err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
+	if (err == -EIO) {
+		/* Try workaround for old Symbol firmware bug */
+		printk(KERN_WARNING "%s: firmware ALLOC bug detected "
+		       "(old Symbol firmware?). Trying to work around... ",
+		       dev->name);
+		
+		priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
+		err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
+		if (err)
+			printk("failed!\n");
+		else
+			printk("ok.\n");
 	}
-}
 
-static inline int
-is_snap(struct header_struct *hdr)
-{
-	return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3);
+	return err;
 }
 
-static void
-orinoco_set_multicast_list(struct net_device *dev)
+static int orinoco_open(struct net_device *dev)
 {
 	struct orinoco_private *priv = dev->priv;
+	unsigned long flags;
+	int err;
 
-	orinoco_lock(priv);
-	__orinoco_set_multicast_list(dev);
-	orinoco_unlock(priv);
-}
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
-/*
- * Hardware control routines
- */
+        priv->open = 1;
 
-void
-orinoco_shutdown(struct orinoco_private *priv)
+	err = __orinoco_up(dev);
+
+	orinoco_unlock(priv, &flags);
+
+	return err;
+}
+
+static int orinoco_stop(struct net_device *dev)
 {
+	struct orinoco_private *priv = dev->priv;
 	int err = 0;
 
-	TRACE_ENTER(priv->ndev->name);
+	/* We mustn't use orinoco_lock() here, because we need to be
+	   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);
 
-	orinoco_lock(priv);
-	__orinoco_stop_irqs(priv);
+	priv->open = 0;
 
-	err = hermes_reset(&priv->hw);
-	if (err && err != -ENODEV) /* If the card is gone, we don't care about shutting it down */
-		printk(KERN_ERR "%s: Error %d shutting down Hermes chipset\n", priv->ndev->name, err);
+	if (! priv->hw_unavailable)
+		err = __orinoco_down(dev);
 
-	orinoco_unlock(priv);
+	spin_unlock_irq(&priv->lock);
 
-	TRACE_EXIT(priv->ndev->name);
+	return err;
 }
 
-int
-orinoco_reset(struct orinoco_private *priv)
+static int __orinoco_program_rids(struct orinoco_private *priv)
 {
 	struct net_device *dev = priv->ndev;
 	hermes_t *hw = &priv->hw;
-	int err = 0;
+	int err;
 	struct hermes_idstring idbuf;
 
-	TRACE_ENTER(priv->ndev->name);
-
-	/* Stop other people bothering us */
-	orinoco_lock(priv);
-	__orinoco_stop_irqs(priv);
-
-	/* Check if we need a card reset */
-	if (priv->hard_reset)
-		priv->hard_reset(priv);
-
-	/* Do standard firmware reset if we can */
-	err = hermes_reset(hw);
-	if (err)
-		goto out;
-
-	err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
-	if (err == -EIO) {
-		/* Try workaround for old Symbol firmware bug */
-		priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
-		err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
-		if (err)
-			goto out;
+	/* Set the MAC address */
+	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
+			       HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr);
+	if (err) {
+		printk(KERN_ERR "%s: Error %d setting MAC address\n", dev->name, err);
+		return err;
 	}
 
-	/* Now set up all the parameters on the card */
-	
 	/* Set up the link mode */
-	
 	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type);
-	if (err)
-		goto out;
+	if (err) {
+		printk(KERN_ERR "%s: Error %d setting port type\n", dev->name, err);
+		return err;
+	}
+	/* Set the channel/frequency */
+	if (priv->channel == 0) {
+		printk(KERN_DEBUG "%s: Channel is 0 in __orinoco_program_rids()\n", dev->name);
+		if (priv->createibss)
+			priv->channel = 10;
+	}
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel);
+	if (err) {
+		printk(KERN_ERR "%s: Error %d setting channel\n", dev->name, err);
+		return err;
+	}
+
 	if (priv->has_ibss) {
 		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFCREATEIBSS,
-					   priv->allow_ibss);
-		if (err)
-			goto out;
-		if((strlen(priv->desired_essid) == 0) && (priv->allow_ibss)
+					   priv->createibss);
+		if (err) {
+			printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", dev->name, err);
+			return err;
+		}
+
+		if ((strlen(priv->desired_essid) == 0) && (priv->createibss)
 		   && (!priv->has_ibss_any)) {
 			printk(KERN_WARNING "%s: This firmware requires an \
 ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
@@ -665,13 +743,17 @@
 	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
 			       HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
 			       &idbuf);
-	if (err)
-		goto out;
+	if (err) {
+		printk(KERN_ERR "%s: Error %d setting OWNSSID\n", dev->name, err);
+		return err;
+	}
 	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
 			       HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
 			       &idbuf);
-	if (err)
-		goto out;
+	if (err) {
+		printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", dev->name, err);
+		return err;
+	}
 
 	/* Set the station name */
 	idbuf.len = cpu_to_le16(strlen(priv->nick));
@@ -679,26 +761,29 @@
 	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
 			       HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
 			       &idbuf);
-	if (err)
-		goto out;
-
-	/* Set the channel/frequency */
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel);
-	if (err)
-		goto out;
+	if (err) {
+		printk(KERN_ERR "%s: Error %d setting nickname\n", dev->name, err);
+		return err;
+	}
 
 	/* Set AP density */
 	if (priv->has_sensitivity) {
 		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
 					   priv->ap_density);
-		if (err)
+		if (err) {
+			printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE.  "
+			       "Disabling sensitivity control\n", dev->name, err);
+
 			priv->has_sensitivity = 0;
+		}
 	}
 
 	/* Set RTS threshold */
 	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh);
-	if (err)
-		goto out;
+	if (err) {
+		printk(KERN_ERR "%s: Error %d setting RTS threshold\n", dev->name, err);
+		return err;
+	}
 
 	/* Set fragmentation threshold or MWO robustness */
 	if (priv->has_mwo)
@@ -709,35 +794,52 @@
 		err = hermes_write_wordrec(hw, USER_BAP,
 					   HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
 					   priv->frag_thresh);
-	if (err)
-		goto out;
+	if (err) {
+		printk(KERN_ERR "%s: Error %d setting framentation\n", dev->name, err);
+		return err;
+	}
 
 	/* Set bitrate */
 	err = __orinoco_hw_set_bitrate(priv);
-	if (err)
-		goto out;
+	if (err) {
+		printk(KERN_ERR "%s: Error %d setting bitrate\n", dev->name, err);
+		return err;
+	}
 
 	/* Set power management */
 	if (priv->has_pm) {
 		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED,
 					   priv->pm_on);
-		if (err)
-			goto out;
+		if (err) {
+			printk(KERN_ERR "%s: Error %d setting up PM\n",
+			       dev->name, err);
+			return err;
+		}
+
 		err = hermes_write_wordrec(hw, USER_BAP,
 					   HERMES_RID_CNFMULTICASTRECEIVE,
 					   priv->pm_mcast);
-		if (err)
-			goto out;
+		if (err) {
+			printk(KERN_ERR "%s: Error %d setting up PM\n",
+			       dev->name, err);
+			return err;
+		}
 		err = hermes_write_wordrec(hw, USER_BAP,
 					   HERMES_RID_CNFMAXSLEEPDURATION,
 					   priv->pm_period);
-		if (err)
-			goto out;
+		if (err) {
+			printk(KERN_ERR "%s: Error %d setting up PM\n",
+			       dev->name, err);
+			return err;
+		}
 		err = hermes_write_wordrec(hw, USER_BAP,
 					   HERMES_RID_CNFPMHOLDOVERDURATION,
 					   priv->pm_timeout);
-		if (err)
-			goto out;
+		if (err) {
+			printk(KERN_ERR "%s: Error %d setting up PM\n",
+			       dev->name, err);
+			return err;
+		}
 	}
 
 	/* Set preamble - only for Symbol so far... */
@@ -746,8 +848,9 @@
 					   HERMES_RID_CNFPREAMBLE_SYMBOL,
 					   priv->preamble);
 		if (err) {
-			printk(KERN_WARNING "%s: Can't set preamble!\n", dev->name);
-			goto out;
+			printk(KERN_ERR "%s: Error %d setting preamble\n",
+			       dev->name, err);
+			return err;
 		}
 	}
 
@@ -755,41 +858,159 @@
 	if (priv->has_wep) {
 		err = __orinoco_hw_setup_wep(priv);
 		if (err) {
-			printk(KERN_ERR "%s: Error %d activating WEP.\n",
+			printk(KERN_ERR "%s: Error %d activating WEP\n",
 			       dev->name, err);
-			goto out;
+			return err;
 		}
 	}
 
 	/* Set promiscuity / multicast*/
 	priv->promiscuous = 0;
 	priv->mc_count = 0;
-	__orinoco_set_multicast_list(dev);
-	
-	__orinoco_start_irqs(priv, HERMES_EV_RX | HERMES_EV_ALLOC |
-			   HERMES_EV_TX | HERMES_EV_TXEXC |
-			   HERMES_EV_WTERR | HERMES_EV_INFO |
-			   HERMES_EV_INFDROP);
+	__orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */
 
-	err = hermes_enable_port(hw, 0);
+	return 0;
+}
+
+/* xyzzy */
+static int orinoco_reconfigure(struct orinoco_private *priv)
+{
+	struct hermes *hw = &priv->hw;
+	unsigned long flags;
+	int err = 0;
+
+	orinoco_lock(priv, &flags);
+
+	err = hermes_disable_port(hw, 0);
+	if (err) {
+		printk(KERN_ERR "%s: Unable to disable port in orinco_reconfigure()\n",
+		       priv->ndev->name);
+		goto out;
+	}
+
+	err = __orinoco_program_rids(priv);
 	if (err)
 		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);
+		goto out;
+	}
+
  out:
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
+	return err;
 
-	TRACE_EXIT(priv->ndev->name);
+}
 
-	return err;
+/* This must be called from user context, without locks held - use
+ * schedule_task() */
+static void orinoco_reset(struct net_device *dev)
+{
+	struct orinoco_private *priv = dev->priv;
+	int err;
+	unsigned long flags;
+
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return;
+
+	priv->hw_unavailable = 1;
+	orinoco_unlock(priv, &flags);
+
+	if (priv->hard_reset)
+		err = (*priv->hard_reset)(priv);
+	if (err) {
+		printk(KERN_ERR "%s: orinoco_reset: Error %d performing hard reset\n",
+		       dev->name, err);
+		/* FIXME: shutdown of some sort */
+		return;
+	}
+
+	err = orinoco_reinit_firmware(dev);
+	if (err) {
+		printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
+		       dev->name, err);
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	priv->hw_unavailable = 0;
+
+	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);
+
+	return;
+}
+
+/********************************************************************/
+/* Internal helper functions                                        */
+/********************************************************************/
+
+static inline void
+set_port_type(struct orinoco_private *priv)
+{
+	switch (priv->iw_mode) {
+	case IW_MODE_INFRA:
+		priv->port_type = 1;
+		priv->createibss = 0;
+		break;
+	case IW_MODE_ADHOC:
+		if (priv->prefer_port3) {
+			priv->port_type = 3;
+			priv->createibss = 0;
+		} else {
+			priv->port_type = priv->ibss_port;
+			priv->createibss = 1;
+		}
+		break;
+	default:
+		printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
+		       priv->ndev->name);
+	}
+}
+
+static inline int
+is_snap(struct header_struct *hdr)
+{
+	return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3);
 }
 
+static void
+orinoco_set_multicast_list(struct net_device *dev)
+{
+	struct orinoco_private *priv = dev->priv;
+	unsigned long flags;
+
+	if (orinoco_lock(priv, &flags) != 0) {
+		printk(KERN_DEBUG "%s: orinoco_set_multicast_list() "
+		       "called when hw_unavailable\n", dev->name);
+		return;
+	}
+
+	__orinoco_set_multicast_list(dev);
+	orinoco_unlock(priv, &flags);
+}
+
+/********************************************************************/
+/* Hardware control functions                                       */
+/********************************************************************/
+
+
 static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 
-	TRACE_ENTER(priv->ndev->name);
-
 	if (priv->bitratemode >= BITRATE_TABLE_SIZE) {
 		printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
 		       priv->ndev->name, priv->bitratemode);
@@ -812,8 +1033,6 @@
 		BUG();
 	}
 
-	TRACE_EXIT(priv->ndev->name);
-
 	return err;
 }
 
@@ -825,8 +1044,6 @@
 	int	master_wep_flag;
 	int	auth_flag;
 
-	TRACE_ENTER(priv->ndev->name);
-
 	switch (priv->firmware_type) {
 	case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
 		if (priv->wep_on) {
@@ -920,8 +1137,6 @@
 		}
 	}
 
-	TRACE_EXIT(priv->ndev->name);
-
 	return 0;
 }
 
@@ -929,13 +1144,16 @@
 {
 	hermes_t *hw = &priv->hw;
 	int err = 0;
+	unsigned long flags;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
 	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
 			      ETH_ALEN, NULL, buf);
 
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return err;
 }
@@ -948,10 +1166,11 @@
 	struct hermes_idstring essidbuf;
 	char *p = (char *)(&essidbuf.val);
 	int len;
+	unsigned long flags;
 
-	TRACE_ENTER(priv->ndev->name);
-
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
 	if (strlen(priv->desired_essid) > 0) {
 		/* We read the desired SSID from the hardware rather
@@ -988,9 +1207,7 @@
 	buf[len] = '\0';
 
  fail_unlock:
-	orinoco_unlock(priv);
-
-	TRACE_EXIT(priv->ndev->name);
+	orinoco_unlock(priv, &flags);
 
 	return err;       
 }
@@ -1002,13 +1219,22 @@
 	int err = 0;
 	u16 channel;
 	long freq = 0;
+	unsigned long flags;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 	
 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel);
 	if (err)
 		goto out;
 
+	/* Intersil firmware 1.3.5 returns 0 when the interface is down */
+	if (channel == 0) {
+		err = -EBUSY;
+		goto out;
+	}
+
 	if ( (channel < 1) || (channel > NUM_CHANNELS) ) {
 		struct net_device *dev = priv->ndev;
 
@@ -1020,7 +1246,7 @@
 	freq = channel_frequency[channel-1] * 100000;
 
  out:
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	if (err > 0)
 		err = -EBUSY;
@@ -1036,11 +1262,15 @@
 	int err = 0;
 	int num;
 	int i;
+	unsigned long flags;
+
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
-	orinoco_lock(priv);
 	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
 			      sizeof(list), NULL, &list);
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	if (err)
 		return err;
@@ -1122,7 +1352,7 @@
 /*
  * Interrupt handler
  */
-void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs)
+void orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	struct orinoco_private *priv = (struct orinoco_private *) dev_id;
 	hermes_t *hw = &priv->hw;
@@ -1134,38 +1364,26 @@
 	 * we panic and shut down the hardware */
 	static int last_irq_jiffy = 0; /* jiffies value the last time we were called */
 	static int loops_this_jiffy = 0;
+	unsigned long flags;
 
-	if (test_and_set_bit(ORINOCO_STATE_INIRQ, &priv->state))
-		BUG();
-
-	if (! orinoco_irqs_allowed(priv)) {
-		clear_bit(ORINOCO_STATE_INIRQ, &priv->state);
+	if (orinoco_lock(priv, &flags) != 0) {
+		/* If hw is unavailable */
 		return;
 	}
 
-	DEBUG(3, "%s: orinoco_interrupt()\n", priv->ndev->name);
-
 	evstat = hermes_read_regn(hw, EVSTAT);
 	events = evstat & hw->inten;
 	
-/*  	if (! events) { */
-/*  		printk(KERN_WARNING "%s: Null event\n", dev->name); */
-/*  	} */
-
 	if (jiffies != last_irq_jiffy)
 		loops_this_jiffy = 0;
 	last_irq_jiffy = jiffies;
 
 	while (events && count--) {
-		DEBUG(3, "__orinoco_interrupt(): count=%d EVSTAT=0x%04x\n",
-		      count, evstat);
-		
 		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 */
-			clear_bit(ORINOCO_STATE_DOIRQ, &priv->state);
 			hermes_set_irqmask(hw, 0);
 			break;
 		}
@@ -1199,7 +1417,7 @@
 		events = evstat & hw->inten;
 	};
 
-	clear_bit(ORINOCO_STATE_INIRQ, &priv->state);
+	orinoco_unlock(priv, &flags);
 }
 
 static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw)
@@ -1211,8 +1429,8 @@
 {
 	/* This seems to happen a fair bit under load, but ignoring it
 	   seems to work fine...*/
-	DEBUG(1, "%s: MAC controller error (WTERR). Ignoring.\n",
-	      priv->ndev->name);
+	printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n",
+	       priv->ndev->name);
 }
 
 static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw)
@@ -1220,6 +1438,40 @@
 	printk(KERN_WARNING "%s: Information frame lost.\n", priv->ndev->name);
 }
 
+static void print_linkstatus(struct net_device *dev, u16 status)
+{
+	char * s;
+
+	switch (status) {
+	case HERMES_LINKSTATUS_NOT_CONNECTED:
+		s = "Not Connected";
+		break;
+	case HERMES_LINKSTATUS_CONNECTED:
+		s = "Connected";
+		break;
+	case HERMES_LINKSTATUS_DISCONNECTED:
+		s = "Disconnected";
+		break;
+	case HERMES_LINKSTATUS_AP_CHANGE:
+		s = "AP Changed";
+		break;
+	case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
+		s = "AP Out of Range";
+		break;
+	case HERMES_LINKSTATUS_AP_IN_RANGE:
+		s = "AP In Range";
+		break;
+	case HERMES_LINKSTATUS_ASSOC_FAILED:
+		s = "Association Failed";
+		break;
+	default:
+		s = "UNKNOWN";
+	}
+	
+	printk(KERN_INFO "%s: New link status: %s (%04x)\n",
+	       dev->name, s, status);
+}
+
 static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw)
 {
 	struct net_device *dev = priv->ndev;
@@ -1228,6 +1480,7 @@
 		u16 len;
 		u16 type;
 	} __attribute__ ((packed)) info;
+	int len, type;
 	int err;
 
 	/* This is an answer to an INQUIRE command that we did earlier,
@@ -1235,8 +1488,6 @@
 	 * The controller return to us a pseudo frame containing
 	 * the information in question - Jean II */
 	infofid = hermes_read_regn(hw, INFOFID);
-	DEBUG(3, "%s: __orinoco_ev_info(): INFOFID=0x%04x\n", dev->name,
-	      infofid);
 
 	/* Read the info frame header - don't try too hard */
 	err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
@@ -1247,25 +1498,29 @@
 		return;
 	}
 	
-	switch (le16_to_cpu(info.type)) {
+	len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len));
+	type = le16_to_cpu(info.type);
+
+	switch (type) {
 	case HERMES_INQ_TALLIES: {
 		struct hermes_tallies_frame tallies;
 		struct iw_statistics *wstats = &priv->wstats;
-		int len = le16_to_cpu(info.len) - 1;
 		
-		if (len > (sizeof(tallies) / 2)) {
-			DEBUG(1, "%s: tallies frame too long.\n", dev->name);
-			len = sizeof(tallies) / 2;
+		if (len > sizeof(tallies)) {
+			printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n",
+			       dev->name, len);
+			len = sizeof(tallies);
 		}
 		
 		/* Read directly the data (no seek) */
-		hermes_read_words(hw, HERMES_DATA1, (void *) &tallies, len);
+		hermes_read_words(hw, HERMES_DATA1, (void *) &tallies,
+				  len / 2); /* FIXME: blech! */
 		
 		/* Increment our various counters */
 		/* wstats->discard.nwid - no wrong BSSID stuff */
 		wstats->discard.code +=
 			le16_to_cpu(tallies.RxWEPUndecryptable);
-		if (len == (sizeof(tallies) / 2))  
+		if (len == sizeof(tallies))  
 			wstats->discard.code +=
 				le16_to_cpu(tallies.RxDiscards_WEPICVError) +
 				le16_to_cpu(tallies.RxDiscards_WEPExcluded);
@@ -1277,17 +1532,42 @@
 		wstats->discard.retries +=
 			le16_to_cpu(tallies.TxRetryLimitExceeded);
 		/* wstats->miss.beacon - no match */
-#if ORINOCO_DEBUG > 3
-		/* Hack for debugging - should not be taken as an example */
-		wstats->discard.nwid += le16_to_cpu(tallies.TxUnicastFrames);
-		wstats->miss.beacon += le16_to_cpu(tallies.RxUnicastFrames);
-#endif
 #endif /* WIRELESS_EXT > 11 */
 	}
 	break;
+	case HERMES_INQ_LINKSTATUS: {
+		struct hermes_linkstatus linkstatus;
+		u16 newstatus;
+		
+		if (len != sizeof(linkstatus)) {
+			printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
+			       dev->name, len);
+			break;
+		}
+
+		hermes_read_words(hw, HERMES_DATA1, (void *) &linkstatus,
+				  len / 2);
+		newstatus = le16_to_cpu(linkstatus.linkstatus);
+
+		if ( (newstatus == HERMES_LINKSTATUS_CONNECTED)
+		     || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
+		     || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE) )
+			priv->connected = 1;
+		else if ( (newstatus == HERMES_LINKSTATUS_NOT_CONNECTED)
+			  || (newstatus == HERMES_LINKSTATUS_DISCONNECTED)
+			  || (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE)
+			  || (newstatus == HERMES_LINKSTATUS_ASSOC_FAILED) )
+			priv->connected = 0;
+
+		if (newstatus != priv->last_linkstatus)
+			print_linkstatus(dev, newstatus);
+
+		priv->last_linkstatus = newstatus;
+	}
+	break;
 	default:
-		DEBUG(1, "%s: Unknown information frame received (type %04x).\n",
-		      priv->ndev->name, le16_to_cpu(info.type));
+		printk(KERN_DEBUG "%s: Unknown information frame received (type %04x).\n",
+		      dev->name, type);
 		/* We don't actually do anything about it */
 		break;
 	}
@@ -1308,7 +1588,6 @@
 	int err;
 
 	rxfid = hermes_read_regn(hw, RXFID);
-	DEBUG(3, "__orinoco_ev_rx(): RXFID=0x%04x\n", rxfid);
 
 	err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc),
 			       rxfid, 0);
@@ -1349,11 +1628,11 @@
 	length = ntohs(hdr.len);
 	
 	/* Sanity checks */
-	if (length < sizeof(struct header_struct)) {
-		printk(KERN_WARNING "%s: Undersized frame received (%d bytes)\n",
-		       dev->name, length);
-		stats->rx_length_errors++;
-		stats->rx_errors++;
+	if (length < 3) { /* No for even an 802.2 LLC header */
+		/* At least on Symbol firmware with PCF we get quite a
+                   lot of these legitimately - Poll frames with no
+                   data. */
+		stats->rx_dropped++;
 		goto drop;
 	}
 	if (length > IEEE802_11_DATA_LEN) {
@@ -1373,7 +1652,6 @@
 	if (!skb) {
 		printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
 		       dev->name);
-		stats->rx_dropped++;
 		goto drop;
 	}
 
@@ -1383,8 +1661,7 @@
 	 * In most cases, the firmware tell us about SNAP frames.
 	 * For some reason, the SNAP frames sent by LinkSys APs
 	 * are not properly recognised by most firmwares.
-	 * So, check ourselves (note : only 3 bytes out of 6).
-	 */
+	 * So, check ourselves */
 	if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
 	   ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
 	   is_snap(&hdr)) {
@@ -1392,7 +1669,7 @@
 		   802.11 frame which we'll need to de-encapsulate to
 		   the original EthernetII frame. */
 
-		if (length < ENCAPS_OVERHEAD) {
+		if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */
 			stats->rx_length_errors++;
 			stats->rx_dropped++;
 			goto drop;
@@ -1420,7 +1697,7 @@
 	err = hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len),
 			       rxfid, data_off);
 	if (err) {
-		printk(KERN_ERR "%s: error %d reading frame header. "
+		printk(KERN_ERR "%s: error %d reading frame. "
 		       "Frame dropped.\n", dev->name, err);
 		stats->rx_errors++;
 		goto drop;
@@ -1442,6 +1719,8 @@
 	return;
 
  drop:	
+	stats->rx_dropped++;
+
 	if (skb)
 		dev_kfree_skb_irq(skb);
 	return;
@@ -1475,11 +1754,7 @@
 
 static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw)
 {
-/*  	struct net_device *dev = priv->ndev; */
 	struct net_device_stats *stats = &priv->stats;
-/*  	u16 fid = hermes_read_regn(hw, TXCOMPLFID); */
-
-/*  	DEBUG(2, "%s: Transmit completed (FID=%04X)\n", priv->ndev->name, fid); */
 
 	stats->tx_packets++;
 
@@ -1491,8 +1766,6 @@
 	struct net_device *dev = priv->ndev;
 	u16 fid = hermes_read_regn(hw, ALLOCFID);
 
-	DEBUG(3, "%s: Allocation complete FID=0x%04x\n", priv->ndev->name, fid);
-
 	if (fid != priv->txfid) {
 		if (fid != DUMMY_FID)
 			printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
@@ -1506,16 +1779,17 @@
 }
 
 struct sta_id {
-	u16 id, vendor, major, minor;
+	u16 id, variant, major, minor;
 } __attribute__ ((packed));
 
 static int determine_firmware_type(struct net_device *dev, struct sta_id *sta_id)
 {
-	u32 firmver = ((u32)sta_id->major << 16) | sta_id->minor;
+	/* FIXME: this is fundamentally broken */
+	unsigned int firmver = ((u32)sta_id->major << 16) | sta_id->minor;
 	
-	if (sta_id->vendor == 1)
+	if (sta_id->variant == 1)
 		return FIRMWARE_TYPE_AGERE;
-	else if ((sta_id->vendor == 2) &&
+	else if ((sta_id->variant == 2) &&
 		   ((firmver == 0x10001) || (firmver == 0x20001)))
 		return FIRMWARE_TYPE_SYMBOL;
 	else
@@ -1528,7 +1802,7 @@
 	hermes_t *hw = &priv->hw;
 	int err;
 	struct sta_id sta_id;
-	u32 firmver;
+	unsigned int firmver;
 	char tmp[SYMBOL_MAX_VER_LEN+1];
 
 	/* Get the firmware version */
@@ -1539,14 +1813,12 @@
 		memset(&sta_id, 0, sizeof(sta_id));
 	}
 	le16_to_cpus(&sta_id.id);
-	le16_to_cpus(&sta_id.vendor);
+	le16_to_cpus(&sta_id.variant);
 	le16_to_cpus(&sta_id.major);
 	le16_to_cpus(&sta_id.minor);
 
-	firmver = ((u32)sta_id.major << 16) | sta_id.minor;
-
 	printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n",
-	       dev->name, sta_id.id, sta_id.vendor,
+	       dev->name, sta_id.id, sta_id.variant,
 	       sta_id.major, sta_id.minor);
 
 	if (! priv->firmware_type)
@@ -1561,7 +1833,6 @@
 	priv->has_ibss_any = 0;
 	priv->has_wep = 0;
 	priv->has_big_wep = 0;
-	priv->broken_cor_reset = 0;
 
 	/* Determine capabilities from the firmware version */
 	switch (priv->firmware_type) {
@@ -1572,6 +1843,8 @@
 		       "version %d.%02d\n", dev->name,
 		       sta_id.major, sta_id.minor);
 
+		firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
+
 		priv->has_ibss = (firmver >= 0x60006);
 		priv->has_ibss_any = (firmver >= 0x60010);
 		priv->has_wep = (firmver >= 0x40020);
@@ -1581,8 +1854,6 @@
 		priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
 		priv->ibss_port = 1;
 
-		/* FIXME: Which firmware really do have a broken reset */
-		priv->broken_cor_reset = (firmver < 0x60000);
 		/* Tested with Agere firmware :
 		 *	1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
 		 * Tested CableTron firmware : 4.32 => Anton */
@@ -1634,18 +1905,21 @@
 		/* D-Link MAC : 00:40:05:* */
 		/* Addtron MAC : 00:90:D1:* */
 		printk(KERN_DEBUG "%s: Looks like an Intersil firmware "
-		       "version %d.%02d\n", dev->name,
-		       sta_id.major, sta_id.minor);
+		       "version %d.%d.%d\n", dev->name,
+		       sta_id.major, sta_id.minor, sta_id.variant);
 
-		priv->has_ibss = (firmver >= 0x00007); /* FIXME */
-		priv->has_big_wep = priv->has_wep = (firmver >= 0x00008);
-		priv->has_pm = (firmver >= 0x00007);
+		firmver = ((unsigned long)sta_id.major << 16) |
+			((unsigned long)sta_id.minor << 8) | sta_id.variant;
 
-		if (firmver >= 0x00008)
+		priv->has_ibss = (firmver >= 0x000700); /* FIXME */
+		priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
+		priv->has_pm = (firmver >= 0x000700);
+
+		if (firmver >= 0x000800)
 			priv->ibss_port = 0;
 		else {
 			printk(KERN_NOTICE "%s: Intersil firmware earlier "
-			       "than v0.08 - several features not supported\n",
+			       "than v0.8.x - several features not supported\n",
 			       dev->name);
 			priv->ibss_port = 1;
 		}
@@ -1669,16 +1943,16 @@
 	u16 reclen;
 	int len;
 
-	TRACE_ENTER("orinoco");
-
-	orinoco_lock(priv);
+	TRACE_ENTER(dev->name);
 
+	/* No need to lock, the resetting flag is already set in
+	 * alloc_orinocodev() */
 	priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN;
 
-	/* Do standard firmware reset */
-	err = hermes_reset(hw);
+	/* Initialize the firmware */
+	err = hermes_init(hw);
 	if (err != 0) {
-		printk(KERN_ERR "%s: failed to reset hardware (err = %d)\n",
+		printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n",
 		       dev->name, err);
 		goto out;
 	}
@@ -1807,20 +2081,38 @@
 	priv->wep_on = 0;
 	priv->tx_key = 0;
 
-	printk(KERN_DEBUG "%s: ready\n", dev->name);
+	priv->hw_unavailable = 0;
 
- out:
-	orinoco_unlock(priv);
+	err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
+	if (err == -EIO) {
+		/* Try workaround for old Symbol firmware bug */
+		printk(KERN_WARNING "%s: firmware ALLOC bug detected "
+		       "(old Symbol firmware?). Trying to work around... ",
+		       dev->name);
+		
+		priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
+		err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
+		if (err)
+			printk("failed!\n");
+		else
+			printk("ok.\n");
+	}
+	if (err) {
+		printk("%s: Error %d allocating Tx buffer\n", dev->name, err);
+		goto out;
+	}
 
-	TRACE_EXIT("orinoco");
+	printk(KERN_DEBUG "%s: ready\n", dev->name);
 
+ out:
+	TRACE_EXIT(dev->name);
 	return err;
 }
 
 struct net_device_stats *
 orinoco_get_stats(struct net_device *dev)
 {
-	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	
 	return &priv->stats;
 }
@@ -1828,15 +2120,22 @@
 struct iw_statistics *
 orinoco_get_wireless_stats(struct net_device *dev)
 {
-	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	struct iw_statistics *wstats = &priv->wstats;
 	int err = 0;
+	unsigned long flags;
 
-	if (! netif_device_present(dev))
-		return NULL; /* FIXME: We may be able to do better than this */
+	if (! netif_device_present(dev)) {
+		printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n",
+		       dev->name);
+		return NULL; /* FIXME: Can we do better than this? */
+	}
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return NULL; /* FIXME: Erg, we've been signalled, how
+			      * do we propagate this back up? */
 
 	if (priv->iw_mode == IW_MODE_ADHOC) {
 		memset(&wstats->qual, 0, sizeof(wstats->qual));
@@ -1856,9 +2155,6 @@
 		err = HERMES_READ_RECORD(hw, USER_BAP,
 					 HERMES_RID_COMMSQUALITY, &cq);
 		
-		DEBUG(3, "%s: Global stats = %X-%X-%X\n", dev->name,
-		      cq.qual, cq.signal, cq.noise);
-
 		wstats->qual.qual = (int)le16_to_cpu(cq.qual);
 		wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95;
 		wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95;
@@ -1868,11 +2164,11 @@
 	/* We can't really wait for the tallies inquiry command to
 	 * complete, so we just use the previous results and trigger
 	 * a new tallies inquiry command for next time - Jean II */
-	/* FIXME: Hmm.. seems a bit ugly, I wonder if there's a way to
-	   do better - dgibson */
+	/* FIXME: We're in user context (I think?), so we should just
+           wait for the tallies to come through */
 	err = hermes_inquire(hw, HERMES_INQ_TALLIES);
                
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	if (err)
 		return NULL;
@@ -1932,21 +2228,42 @@
 	struct ethhdr *eh;
 	int len, data_len, data_off;
 	struct hermes_tx_descriptor desc;
+	unsigned long flags;
+
+	TRACE_ENTER(dev->name);
 
 	if (! netif_running(dev)) {
 		printk(KERN_ERR "%s: Tx on stopped device!\n",
 		       dev->name);
+		TRACE_EXIT(dev->name);
 		return 1;
-
 	}
 	
 	if (netif_queue_stopped(dev)) {
-		printk(KERN_ERR "%s: Tx while transmitter busy!\n", 
+		printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", 
 		       dev->name);
+		TRACE_EXIT(dev->name);
 		return 1;
 	}
 	
-	orinoco_lock(priv);
+	if (orinoco_lock(priv, &flags) != 0) {
+		printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
+		       dev->name);
+		TRACE_EXIT(dev->name);
+/*  		BUG(); */
+		return 1;
+	}
+
+	if (! priv->connected) {
+		/* Oops, the firmware hasn't established a connection,
+                   silently drop the packet (this seems to be the
+                   safest approach). */
+		stats->tx_errors++;
+		orinoco_unlock(priv, &flags);
+		dev_kfree_skb(skb);
+		TRACE_EXIT(dev->name);
+		return 0;
+	}
 
 	/* Length of the packet body */
 	/* FIXME: what if the skb is smaller than this? */
@@ -2017,14 +2334,17 @@
 	dev->trans_start = jiffies;
 	stats->tx_bytes += data_off + data_len;
 
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	dev_kfree_skb(skb);
 
+	TRACE_EXIT(dev->name);
+
 	return 0;
  fail:
+	TRACE_EXIT(dev->name);
 
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 	return err;
 }
 
@@ -2034,27 +2354,21 @@
 	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	struct net_device_stats *stats = &priv->stats;
 	struct hermes *hw = &priv->hw;
-	int err = 0;
 
-	printk(KERN_WARNING "%s: Tx timeout! Resetting card. ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", dev->name, hermes_read_regn(hw, ALLOCFID), hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT));
+	printk(KERN_WARNING "%s: Tx timeout! "
+	       "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n",
+	       dev->name, hermes_read_regn(hw, ALLOCFID),
+	       hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT));
 
 	stats->tx_errors++;
 
-	err = orinoco_reset(priv);
-	if (err)
-		printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n",
-		       dev->name, err);
-	else {
-		dev->trans_start = jiffies;
-		netif_wake_queue(dev);
-	}
+	schedule_task(&priv->timeout_task);
 }
 
 static int
 orinoco_change_mtu(struct net_device *dev, int new_mtu)
 {
 	struct orinoco_private *priv = dev->priv;
-	TRACE_ENTER(dev->name);
 
 	if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) )
 		return -EINVAL;
@@ -2065,11 +2379,10 @@
 
 	dev->mtu = new_mtu;
 
-	TRACE_EXIT(dev->name);
-
 	return 0;
 }
 
+/* FIXME: return int? */
 static void
 __orinoco_set_multicast_list(struct net_device *dev)
 {
@@ -2078,17 +2391,6 @@
 	int err = 0;
 	int promisc, mc_count;
 
-	/* We'll wait until it's ready. Anyway, the network doesn't call us
-	 * here until we are open - Jean II */
-	/* FIXME: do we need this test at all? */
-	if (! netif_device_present(dev)) {
-		printk(KERN_WARNING "%s: __orinoco_set_multicast_list() called while device "
-		       "not present.\n", dev->name);
-		return;
-	}
-
-	TRACE_ENTER(dev->name);
-
 	/* The Hermes doesn't seem to have an allmulti mode, so we go
 	 * into promiscuous mode and let the upper levels deal. */
 	if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
@@ -2146,8 +2448,6 @@
 		dev->flags |= IFF_PROMISC;
 	else
 		dev->flags &= ~IFF_PROMISC;
-
-	TRACE_EXIT(dev->name);
 }
 
 /********************************************************************/
@@ -2162,6 +2462,7 @@
 	struct iw_range range;
 	int numrates;
 	int i, k;
+	unsigned long flags;
 
 	TRACE_ENTER(dev->name);
 
@@ -2171,9 +2472,12 @@
 
 	rrq->length = sizeof(range);
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
+
 	mode = priv->iw_mode;
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	memset(&range, 0, sizeof(range));
 
@@ -2246,7 +2550,9 @@
 	range.min_frag = 256;
 	range.max_frag = 2346;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 	if (priv->has_wep) {
 		range.max_encoding_tokens = ORINOCO_MAX_KEYS;
 
@@ -2261,7 +2567,7 @@
 		range.num_encoding_sizes = 0;
 		range.max_encoding_tokens = 0;
 	}
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 		
 	range.min_pmp = 0;
 	range.max_pmp = 65535000;
@@ -2303,6 +2609,7 @@
 	u16 xlen = 0;
 	int err = 0;
 	char keybuf[ORINOCO_MAX_KEY_SIZE];
+	unsigned long flags;
 	
 	if (erq->pointer) {
 		/* We actually have a key to set */
@@ -2313,7 +2620,9 @@
 			return -EFAULT;
 	}
 	
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 	
 	if (erq->pointer) {
 		if (erq->length > ORINOCO_MAX_KEY_SIZE) {
@@ -2377,9 +2686,10 @@
 	priv->tx_key = setindex;
 	priv->wep_on = enable;
 	priv->wep_restrict = restricted;
+
 	
  out:
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return err;
 }
@@ -2390,9 +2700,12 @@
 	int index = (erq->flags & IW_ENCODE_INDEX) - 1;
 	u16 xlen = 0;
 	char keybuf[ORINOCO_MAX_KEY_SIZE];
-
+	int err;
+	unsigned long flags;
 	
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
 	if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
 		index = priv->tx_key;
@@ -2418,7 +2731,7 @@
 		memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
 	}
 	
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	if (erq->pointer) {
 		if (copy_to_user(erq->pointer, keybuf, xlen))
@@ -2432,6 +2745,8 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	char essidbuf[IW_ESSID_MAX_SIZE+1];
+	int err;
+	unsigned long flags;
 
 	/* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it
 	 * anyway... - Jean II */
@@ -2448,11 +2763,13 @@
 		essidbuf[erq->length] = '\0';
 	}
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
 	memcpy(priv->desired_essid, essidbuf, sizeof(priv->desired_essid));
 
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return 0;
 }
@@ -2463,6 +2780,7 @@
 	char essidbuf[IW_ESSID_MAX_SIZE+1];
 	int active;
 	int err = 0;
+	unsigned long flags;
 
 	TRACE_ENTER(dev->name);
 
@@ -2471,9 +2789,11 @@
 		if (err)
 			return err;
 	} else {
-		orinoco_lock(priv);
+		err = orinoco_lock(priv, &flags);
+		if (err)
+			return err;
 		memcpy(essidbuf, priv->desired_essid, sizeof(essidbuf));
-		orinoco_unlock(priv);
+		orinoco_unlock(priv, &flags);
 	}
 
 	erq->flags = 1;
@@ -2491,6 +2811,8 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	char nickbuf[IW_ESSID_MAX_SIZE+1];
+	int err;
+	unsigned long flags;
 
 	if (nrq->length > IW_ESSID_MAX_SIZE)
 		return -E2BIG;
@@ -2502,11 +2824,13 @@
 
 	nickbuf[nrq->length] = '\0';
 	
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
 	memcpy(priv->nick, nickbuf, sizeof(priv->nick));
 
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return 0;
 }
@@ -2515,10 +2839,15 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	char nickbuf[IW_ESSID_MAX_SIZE+1];
+	int err;
+	unsigned long flags;
+
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
-	orinoco_lock(priv);
 	memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE+1);
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	nrq->length = strlen(nickbuf)+1;
 
@@ -2532,6 +2861,8 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	int chan = -1;
+	int err;
+	unsigned long flags;
 
 	/* We can only use this in Ad-Hoc demo mode to set the operating
 	 * frequency, or in IBSS mode to set the frequency where the IBSS
@@ -2559,9 +2890,11 @@
 	     ! (priv->channel_mask & (1 << (chan-1)) ) )
 		return -EINVAL;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 	priv->channel = chan;
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return 0;
 }
@@ -2572,13 +2905,16 @@
 	hermes_t *hw = &priv->hw;
 	u16 val;
 	int err;
+	unsigned long flags;
 
 	if (!priv->has_sensitivity)
 		return -EOPNOTSUPP;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &val);
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	if (err)
 		return err;
@@ -2593,6 +2929,8 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	int val = srq->value;
+	int err;
+	unsigned long flags;
 
 	if (!priv->has_sensitivity)
 		return -EOPNOTSUPP;
@@ -2600,9 +2938,11 @@
 	if ((val < 1) || (val > 3))
 		return -EINVAL;
 	
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 	priv->ap_density = val;
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return 0;
 }
@@ -2611,6 +2951,8 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	int val = rrq->value;
+	int err;
+	unsigned long flags;
 
 	if (rrq->disabled)
 		val = 2347;
@@ -2618,9 +2960,12 @@
 	if ( (val < 0) || (val > 2347) )
 		return -EINVAL;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
+
 	priv->rts_thresh = val;
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return 0;
 }
@@ -2629,8 +2974,11 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	int err = 0;
+	unsigned long flags;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
 	if (priv->has_mwo) {
 		if (frq->disabled)
@@ -2652,7 +3000,7 @@
 		}
 	}
 
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return err;
 }
@@ -2663,8 +3011,11 @@
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 val;
+	unsigned long flags;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 	
 	if (priv->has_mwo) {
 		err = hermes_read_wordrec(hw, USER_BAP,
@@ -2687,7 +3038,7 @@
 		frq->fixed = 1;
 	}
 
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 	
 	return err;
 }
@@ -2699,6 +3050,7 @@
 	int ratemode = -1;
 	int bitrate; /* 100s of kilobits */
 	int i;
+	unsigned long flags;
 	
 	/* As the user space doesn't know our highest rate, it uses -1
 	 * to ask us to set the highest rate.  Test it using "iwconfig
@@ -2725,9 +3077,11 @@
 	if (ratemode == -1)
 		return -EINVAL;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 	priv->bitratemode = ratemode;
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return err;
 }
@@ -2740,12 +3094,15 @@
 	int ratemode;
 	int i;
 	u16 val;
+	unsigned long flags;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
 	ratemode = priv->bitratemode;
 
-	if ( (ratemode < 0) || (ratemode > BITRATE_TABLE_SIZE) )
+	if ( (ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE) )
 		BUG();
 
 	rrq->value = bitrate_table[ratemode].bitrate * 100000;
@@ -2791,7 +3148,7 @@
 	}
 
  out:
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return err;
 }
@@ -2800,9 +3157,11 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	int err = 0;
+	unsigned long flags;
 
-
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
 	if (prq->disabled) {
 		priv->pm_on = 0;
@@ -2842,7 +3201,7 @@
 	}
 
  out:
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return err;
 }
@@ -2853,8 +3212,11 @@
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 enable, period, timeout, mcast;
+	unsigned long flags;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 	
 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, &enable);
 	if (err)
@@ -2888,7 +3250,7 @@
 		prq->flags |= IW_POWER_UNICAST_R;
 
  out:
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return err;
 }
@@ -2900,8 +3262,11 @@
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 short_limit, long_limit, lifetime;
+	unsigned long flags;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 	
 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
 				  &short_limit);
@@ -2938,7 +3303,7 @@
 	}
 
  out:
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return err;
 }
@@ -2948,14 +3313,19 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	int val = *( (int *) wrq->u.name );
+	int err;
+	unsigned long flags;
+
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
-	orinoco_lock(priv);
 	priv->ibss_port = val ;
 
 	/* Actually update the mode we are using */
 	set_port_type(priv);
 
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 	return 0;
 }
 
@@ -2963,10 +3333,15 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	int *val = (int *)wrq->u.name;
+	int err;
+	unsigned long flags;
+
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
-	orinoco_lock(priv);
 	*val = priv->ibss_port;
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return 0;
 }
@@ -2976,15 +3351,18 @@
 	struct orinoco_private *priv = dev->priv;
 	int val = *( (int *) wrq->u.name );
 	int err = 0;
+	unsigned long flags;
+
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
-	orinoco_lock(priv);
 	switch (val) {
 	case 0: /* Try to do IEEE ad-hoc mode */
 		if (! priv->has_ibss) {
 			err = -EINVAL;
 			break;
 		}
-		DEBUG(2, "%s: Prefer IBSS Ad-Hoc mode\n", dev->name);
 		priv->prefer_port3 = 0;
 			
 		break;
@@ -2994,7 +3372,6 @@
 			err = -EINVAL;
 			break;
 		}
-		DEBUG(2, "%s: Prefer Ad-Hoc demo mode\n", dev->name);
 		priv->prefer_port3 = 1;
 		break;
 
@@ -3006,7 +3383,7 @@
 		/* Actually update the mode we are using */
 		set_port_type(priv);
 
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return err;
 }
@@ -3015,10 +3392,15 @@
 {
 	struct orinoco_private *priv = dev->priv;
 	int *val = (int *)wrq->u.name;
+	int err;
+	unsigned long flags;
+
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
-	orinoco_lock(priv);
 	*val = priv->prefer_port3;
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return 0;
 }
@@ -3032,6 +3414,7 @@
 	int number = srq->length;
 	int i;
 	int err = 0;
+	unsigned long flags;
 
 	/* Check the number of addresses */
 	if (number > IW_MAX_SPY)
@@ -3045,7 +3428,9 @@
 	}
 
 	/* Make sure nobody mess with the structure while we do */
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
 	/* orinoco_lock() doesn't disable interrupts, so make sure the
 	 * interrupt rx path don't get confused while we copy */
@@ -3063,18 +3448,8 @@
 		priv->spy_number = number;
 	}
 
-	/* Time to show what we have done... */
-	DEBUG(0, "%s: New spy list:\n", dev->name);
-	for (i = 0; i < number; i++) {
-		DEBUG(0, "%s: %d - %02x:%02x:%02x:%02x:%02x:%02x\n",
-		      dev->name, i+1,
-		      priv->spy_address[i][0], priv->spy_address[i][1],
-		      priv->spy_address[i][2], priv->spy_address[i][3],
-		      priv->spy_address[i][4], priv->spy_address[i][5]);
-	}
-
 	/* Now, let the others play */
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	return err;
 }
@@ -3086,8 +3461,12 @@
 	struct iw_quality spy_stat[IW_MAX_SPY];
 	int number;
 	int i;
+	int err;
+	unsigned long flags;
 
-	orinoco_lock(priv);
+	err = orinoco_lock(priv, &flags);
+	if (err)
+		return err;
 
 	number = priv->spy_number;
 	if ((number > 0) && (srq->pointer)) {
@@ -3107,7 +3486,7 @@
 			priv->spy_stat[i].updated = 0;
 	}
 
-	orinoco_unlock(priv);
+	orinoco_unlock(priv, &flags);
 
 	/* Push stuff to user space */
 	srq->length = number;
@@ -3127,39 +3506,39 @@
 	struct orinoco_private *priv = dev->priv;
 	struct iwreq *wrq = (struct iwreq *)rq;
 	int err = 0;
+	int tmp;
 	int changed = 0;
+	unsigned long flags;
 
 	TRACE_ENTER(dev->name);
 
 	/* In theory, we could allow most of the the SET stuff to be
-	 * done In practice, the laps of time at startup when the card
-	 * is not ready is very short, so why bother...  Note that
-	 * netif_device_present is different from up/down (ifconfig),
-	 * when the device is not yet up, it is usually already
-	 * ready...  Jean II */
+	 * done. In practice, the lapse of time at startup when the
+	 * card is not ready is very short, so why bother...  Note
+	 * that netif_device_present is different from up/down
+	 * (ifconfig), when the device is not yet up, it is usually
+	 * already ready...  Jean II */
 	if (! netif_device_present(dev))
 		return -ENODEV;
 
 	switch (cmd) {
 	case SIOCGIWNAME:
-		DEBUG(1, "%s: SIOCGIWNAME\n", dev->name);
 		strcpy(wrq->u.name, "IEEE 802.11-DS");
 		break;
 		
 	case SIOCGIWAP:
-		DEBUG(1, "%s: SIOCGIWAP\n", dev->name);
 		wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
 		err = orinoco_hw_get_bssid(priv, wrq->u.ap_addr.sa_data);
 		break;
 
 	case SIOCGIWRANGE:
-		DEBUG(1, "%s: SIOCGIWRANGE\n", dev->name);
 		err = orinoco_ioctl_getiwrange(dev, &wrq->u.data);
 		break;
 
 	case SIOCSIWMODE:
-		DEBUG(1, "%s: SIOCSIWMODE\n", dev->name);
-		orinoco_lock(priv);
+		err = orinoco_lock(priv, &flags);
+		if (err)
+			return err;
 		switch (wrq->u.mode) {
 		case IW_MODE_ADHOC:
 			if (! (priv->has_ibss || priv->has_port3) )
@@ -3180,18 +3559,18 @@
 			break;
 		}
 		set_port_type(priv);
-		orinoco_unlock(priv);
+		orinoco_unlock(priv, &flags);
 		break;
 
 	case SIOCGIWMODE:
-		DEBUG(1, "%s: SIOCGIWMODE\n", dev->name);
-		orinoco_lock(priv);
+		err = orinoco_lock(priv, &flags);
+		if (err)
+			return err;
 		wrq->u.mode = priv->iw_mode;
-		orinoco_unlock(priv);
+		orinoco_unlock(priv, &flags);
 		break;
 
 	case SIOCSIWENCODE:
-		DEBUG(1, "%s: SIOCSIWENCODE\n", dev->name);
 		if (! priv->has_wep) {
 			err = -EOPNOTSUPP;
 			break;
@@ -3203,7 +3582,6 @@
 		break;
 
 	case SIOCGIWENCODE:
-		DEBUG(1, "%s: SIOCGIWENCODE\n", dev->name);
 		if (! priv->has_wep) {
 			err = -EOPNOTSUPP;
 			break;
@@ -3218,106 +3596,94 @@
 		break;
 
 	case SIOCSIWESSID:
-		DEBUG(1, "%s: SIOCSIWESSID\n", dev->name);
 		err = orinoco_ioctl_setessid(dev, &wrq->u.essid);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWESSID:
-		DEBUG(1, "%s: SIOCGIWESSID\n", dev->name);
 		err = orinoco_ioctl_getessid(dev, &wrq->u.essid);
 		break;
 
 	case SIOCSIWNICKN:
-		DEBUG(1, "%s: SIOCSIWNICKN\n", dev->name);
 		err = orinoco_ioctl_setnick(dev, &wrq->u.data);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWNICKN:
-		DEBUG(1, "%s: SIOCGIWNICKN\n", dev->name);
 		err = orinoco_ioctl_getnick(dev, &wrq->u.data);
 		break;
 
 	case SIOCGIWFREQ:
-		DEBUG(1, "%s: SIOCGIWFREQ\n", dev->name);
-		wrq->u.freq.m = orinoco_hw_get_freq(priv);
-		wrq->u.freq.e = 1;
+		tmp = orinoco_hw_get_freq(priv);
+		if (tmp < 0) {
+			err = tmp;
+		} else {
+			wrq->u.freq.m = tmp;
+			wrq->u.freq.e = 1;
+		}
 		break;
 
 	case SIOCSIWFREQ:
-		DEBUG(1, "%s: SIOCSIWFREQ\n", dev->name);
 		err = orinoco_ioctl_setfreq(dev, &wrq->u.freq);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWSENS:
-		DEBUG(1, "%s: SIOCGIWSENS\n", dev->name);
 		err = orinoco_ioctl_getsens(dev, &wrq->u.sens);
 		break;
 
 	case SIOCSIWSENS:
-		DEBUG(1, "%s: SIOCSIWSENS\n", dev->name);
 		err = orinoco_ioctl_setsens(dev, &wrq->u.sens);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWRTS:
-		DEBUG(1, "%s: SIOCGIWRTS\n", dev->name);
 		wrq->u.rts.value = priv->rts_thresh;
 		wrq->u.rts.disabled = (wrq->u.rts.value == 2347);
 		wrq->u.rts.fixed = 1;
 		break;
 
 	case SIOCSIWRTS:
-		DEBUG(1, "%s: SIOCSIWRTS\n", dev->name);
 		err = orinoco_ioctl_setrts(dev, &wrq->u.rts);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCSIWFRAG:
-		DEBUG(1, "%s: SIOCSIWFRAG\n", dev->name);
 		err = orinoco_ioctl_setfrag(dev, &wrq->u.frag);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWFRAG:
-		DEBUG(1, "%s: SIOCGIWFRAG\n", dev->name);
 		err = orinoco_ioctl_getfrag(dev, &wrq->u.frag);
 		break;
 
 	case SIOCSIWRATE:
-		DEBUG(1, "%s: SIOCSIWRATE\n", dev->name);
 		err = orinoco_ioctl_setrate(dev, &wrq->u.bitrate);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWRATE:
-		DEBUG(1, "%s: SIOCGIWRATE\n", dev->name);
 		err = orinoco_ioctl_getrate(dev, &wrq->u.bitrate);
 		break;
 
 	case SIOCSIWPOWER:
-		DEBUG(1, "%s: SIOCSIWPOWER\n", dev->name);
 		err = orinoco_ioctl_setpower(dev, &wrq->u.power);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWPOWER:
-		DEBUG(1, "%s: SIOCGIWPOWER\n", dev->name);
 		err = orinoco_ioctl_getpower(dev, &wrq->u.power);
 		break;
 
 	case SIOCGIWTXPOW:
-		DEBUG(1, "%s: SIOCGIWTXPOW\n", dev->name);
 		/* The card only supports one tx power, so this is easy */
 		wrq->u.txpower.value = 15; /* dBm */
 		wrq->u.txpower.fixed = 1;
@@ -3327,30 +3693,23 @@
 
 #if WIRELESS_EXT > 10
 	case SIOCSIWRETRY:
-		DEBUG(1, "%s: SIOCSIWRETRY\n", dev->name);
 		err = -EOPNOTSUPP;
 		break;
 
 	case SIOCGIWRETRY:
-		DEBUG(1, "%s: SIOCGIWRETRY\n", dev->name);
 		err = orinoco_ioctl_getretry(dev, &wrq->u.retry);
 		break;
 #endif /* WIRELESS_EXT > 10 */
 
 	case SIOCSIWSPY:
-		DEBUG(1, "%s: SIOCSIWSPY\n", dev->name);
-
 		err = orinoco_ioctl_setspy(dev, &wrq->u.data);
 		break;
 
 	case SIOCGIWSPY:
-		DEBUG(1, "%s: SIOCGIWSPY\n", dev->name);
-
 		err = orinoco_ioctl_getspy(dev, &wrq->u.data);
 		break;
 
 	case SIOCGIWPRIV:
-		DEBUG(1, "%s: SIOCGIWPRIV\n", dev->name);
 		if (wrq->u.data.pointer) {
 			struct iw_priv_args privtab[] = {
 				{ SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" },
@@ -3372,7 +3731,8 @@
 				  0, "set_ibssport" },
 				{ SIOCIWFIRSTPRIV + 0x7, 0,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
-				  "get_ibssport" }
+				  "get_ibssport" },
+				{ SIOCIWLASTPRIV, 0, 0, "dump_recs" },
 			};
 
 			err = verify_area(VERIFY_WRITE, wrq->u.data.pointer, sizeof(privtab));
@@ -3387,25 +3747,17 @@
 	       
 	case SIOCIWFIRSTPRIV + 0x0: /* force_reset */
 	case SIOCIWFIRSTPRIV + 0x1: /* card_reset */
-		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x0 (force_reset)\n",
-		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
 			break;
 		}
 		
-		printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
+		printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
 
-		/* We need the xmit lock because it protects the
-		   multicast list which orinoco_reset() reads */
-		spin_lock_bh(&dev->xmit_lock);
-		orinoco_reset(priv);
-		spin_unlock_bh(&dev->xmit_lock);
+		schedule_task(&priv->timeout_task);
 		break;
 
 	case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */
-		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x2 (set_port3)\n",
-		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
 			break;
@@ -3417,14 +3769,10 @@
 		break;
 
 	case SIOCIWFIRSTPRIV + 0x3: /* get_port3 */
-		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x3 (get_port3)\n",
-		      dev->name);
 		err = orinoco_ioctl_getport3(dev, wrq);
 		break;
 
 	case SIOCIWFIRSTPRIV + 0x4: /* set_preamble */
-		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x4 (set_preamble)\n",
-		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
 			break;
@@ -3438,32 +3786,32 @@
 		if(priv->has_preamble) {
 			int val = *( (int *) wrq->u.name );
 
-			orinoco_lock(priv);
-			if(val)
+			err = orinoco_lock(priv, &flags);
+			if (err)
+				return err;
+			if (val)
 				priv->preamble = 1;
 			else
 				priv->preamble = 0;
-			orinoco_unlock(priv);
+			orinoco_unlock(priv, &flags);
 			changed = 1;
 		} else
 			err = -EOPNOTSUPP;
 		break;
 
 	case SIOCIWFIRSTPRIV + 0x5: /* get_preamble */
-		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x5 (get_preamble)\n",
-		      dev->name);
 		if(priv->has_preamble) {
 			int *val = (int *)wrq->u.name;
 
-			orinoco_lock(priv);
+			err = orinoco_lock(priv, &flags);
+			if (err)
+				return err;
 			*val = priv->preamble;
-			orinoco_unlock(priv);
+			orinoco_unlock(priv, &flags);
 		} else
 			err = -EOPNOTSUPP;
 		break;
 	case SIOCIWFIRSTPRIV + 0x6: /* set_ibssport */
-		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x6 (set_ibssport)\n",
-		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
 			break;
@@ -3475,109 +3823,28 @@
 		break;
 
 	case SIOCIWFIRSTPRIV + 0x7: /* get_ibssport */
-		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x7 (get_ibssport)\n",
-		      dev->name);
 		err = orinoco_ioctl_getibssport(dev, wrq);
 		break;
 
+	case SIOCIWLASTPRIV:
+		err = orinoco_debug_dump_recs(priv);
+		if (err)
+			printk(KERN_ERR "%s: Unable to dump records (%d)\n",
+			       dev->name, err);
+		break;
+
 
 	default:
 		err = -EOPNOTSUPP;
 	}
 	
 	if (! err && changed && netif_running(dev)) {
-		/* We need the xmit lock because it protects the
-		   multicast list which orinoco_reset() reads */
-		spin_lock_bh(&dev->xmit_lock);
-		err = orinoco_reset(priv);
-		spin_unlock_bh(&dev->xmit_lock);
-		if (err) {
-			/* Ouch ! What are we supposed to do ? */
-			printk(KERN_ERR "orinoco_cs: Failed to set parameters on %s\n",
-			       dev->name);
-			netif_device_detach(dev);
-			orinoco_shutdown(priv);
-		}
+		err = orinoco_reconfigure(priv);
 	}		
 
 	TRACE_EXIT(dev->name);
-		
-	return err;
-}
-
-/********************************************************************/
-/* procfs stuff                                                     */
-/********************************************************************/
-
-static struct proc_dir_entry *dir_base = NULL;
-
-#define PROC_LTV_SIZE		128
-
-/*
- * This function updates the total amount of data printed so far. It then
- * determines if the amount of data printed into a buffer  has reached the
- * offset requested. If it hasn't, then the buffer is shifted over so that
- * the next bit of data can be printed over the old bit. If the total
- * amount printed so far exceeds the total amount requested, then this
- * function returns 1, otherwise 0.
- */
-static int 
-shift_buffer(char *buffer, int requested_offset, int requested_len,
-	     int *total, int *slop, char **buf)
-{
-	int printed;
-	
-	printed = *buf - buffer;
-	if (*total + printed <= requested_offset) {
-		*total += printed;
-		*buf = buffer;
-	}
-	else {
-		if (*total < requested_offset) {
-			*slop = requested_offset - *total;
-		}
-		*total = requested_offset + printed - *slop;
-	}
-	if (*total > requested_offset + requested_len) {
-		return 1;
-	}
-	else {
-		return 0;
-	}
-}
-
-/*
- * This function calculates the actual start of the requested data
- * in the buffer. It also calculates actual length of data returned,
- * which could be less that the amount of data requested.
- */
-#define PROC_BUFFER_SIZE 4096
-#define PROC_SAFE_SIZE 3072
 
-static int
-calc_start_len(char *buffer, char **start, int requested_offset,
-	       int requested_len, int total, char *buf)
-{
-	int return_len, buffer_len;
-	
-	buffer_len = buf - buffer;
-	if (buffer_len >= PROC_BUFFER_SIZE - 1) {
-		printk(KERN_ERR "calc_start_len: exceeded /proc buffer size\n");
-	}
-	
-	/*
-	 * There may be bytes before and after the
-	 * chunk that was actually requested.
-	 */
-	return_len = total - requested_offset;
-	if (return_len < 0) {
-		return_len = 0;
-	}
-	*start = buf - return_len;
-	if (return_len > requested_len) {
-		return_len = requested_len;
-	}
-	return return_len;
+	return err;
 }
 
 struct {
@@ -3589,140 +3856,134 @@
 #define DISPLAY_STRING	2
 #define DISPLAY_XSTRING	3
 } record_table[] = {
-#define PROC_REC(name,type) { HERMES_RID_##name, #name, DISPLAY_##type }
-	PROC_REC(CNFPORTTYPE,WORDS),
-	PROC_REC(CNFOWNMACADDR,BYTES),
-	PROC_REC(CNFDESIREDSSID,STRING),
-	PROC_REC(CNFOWNCHANNEL,WORDS),
-	PROC_REC(CNFOWNSSID,STRING),
-	PROC_REC(CNFOWNATIMWINDOW,WORDS),
-	PROC_REC(CNFSYSTEMSCALE,WORDS),
-	PROC_REC(CNFMAXDATALEN,WORDS),
-	PROC_REC(CNFPMENABLED,WORDS),
-	PROC_REC(CNFPMEPS,WORDS),
-	PROC_REC(CNFMULTICASTRECEIVE,WORDS),
-	PROC_REC(CNFMAXSLEEPDURATION,WORDS),
-	PROC_REC(CNFPMHOLDOVERDURATION,WORDS),
-	PROC_REC(CNFOWNNAME,STRING),
-	PROC_REC(CNFOWNDTIMPERIOD,WORDS),
-	PROC_REC(CNFMULTICASTPMBUFFERING,WORDS),
-	PROC_REC(CNFWEPENABLED_AGERE,WORDS),
-	PROC_REC(CNFMANDATORYBSSID_SYMBOL,WORDS),
-	PROC_REC(CNFWEPDEFAULTKEYID,WORDS),
-	PROC_REC(CNFDEFAULTKEY0,BYTES),
-	PROC_REC(CNFDEFAULTKEY1,BYTES),
-	PROC_REC(CNFMWOROBUST_AGERE,WORDS),
-	PROC_REC(CNFDEFAULTKEY2,BYTES),
-	PROC_REC(CNFDEFAULTKEY3,BYTES),
-	PROC_REC(CNFWEPFLAGS_INTERSIL,WORDS),
-	PROC_REC(CNFWEPKEYMAPPINGTABLE,WORDS),
-	PROC_REC(CNFAUTHENTICATION,WORDS),
-	PROC_REC(CNFMAXASSOCSTA,WORDS),
-	PROC_REC(CNFKEYLENGTH_SYMBOL,WORDS),
-	PROC_REC(CNFTXCONTROL,WORDS),
-	PROC_REC(CNFROAMINGMODE,WORDS),
-	PROC_REC(CNFHOSTAUTHENTICATION,WORDS),
-	PROC_REC(CNFRCVCRCERROR,WORDS),
-	PROC_REC(CNFMMLIFE,WORDS),
-	PROC_REC(CNFALTRETRYCOUNT,WORDS),
-	PROC_REC(CNFBEACONINT,WORDS),
-	PROC_REC(CNFAPPCFINFO,WORDS),
-	PROC_REC(CNFSTAPCFINFO,WORDS),
-	PROC_REC(CNFPRIORITYQUSAGE,WORDS),
-	PROC_REC(CNFTIMCTRL,WORDS),
-	PROC_REC(CNFTHIRTY2TALLY,WORDS),
-	PROC_REC(CNFENHSECURITY,WORDS),
-	PROC_REC(CNFGROUPADDRESSES,BYTES),
-	PROC_REC(CNFCREATEIBSS,WORDS),
-	PROC_REC(CNFFRAGMENTATIONTHRESHOLD,WORDS),
-	PROC_REC(CNFRTSTHRESHOLD,WORDS),
-	PROC_REC(CNFTXRATECONTROL,WORDS),
-	PROC_REC(CNFPROMISCUOUSMODE,WORDS),
-	PROC_REC(CNFBASICRATES_SYMBOL,WORDS),
-	PROC_REC(CNFPREAMBLE_SYMBOL,WORDS),
-	PROC_REC(CNFSHORTPREAMBLE,WORDS),
-	PROC_REC(CNFWEPKEYS_AGERE,BYTES),
-	PROC_REC(CNFEXCLUDELONGPREAMBLE,WORDS),
-	PROC_REC(CNFTXKEY_AGERE,WORDS),
-	PROC_REC(CNFAUTHENTICATIONRSPTO,WORDS),
-	PROC_REC(CNFBASICRATES,WORDS),
-	PROC_REC(CNFSUPPORTEDRATES,WORDS),
-	PROC_REC(CNFTICKTIME,WORDS),
-	PROC_REC(CNFSCANREQUEST,WORDS),
-	PROC_REC(CNFJOINREQUEST,WORDS),
-	PROC_REC(CNFAUTHENTICATESTATION,WORDS),
-	PROC_REC(CNFCHANNELINFOREQUEST,WORDS),
-	PROC_REC(MAXLOADTIME,WORDS),
-	PROC_REC(DOWNLOADBUFFER,WORDS),
-	PROC_REC(PRIID,WORDS),
-	PROC_REC(PRISUPRANGE,WORDS),
-	PROC_REC(CFIACTRANGES,WORDS),
-	PROC_REC(NICSERNUM,WORDS),
-	PROC_REC(NICID,WORDS),
-	PROC_REC(MFISUPRANGE,WORDS),
-	PROC_REC(CFISUPRANGE,WORDS),
-	PROC_REC(CHANNELLIST,WORDS),
-	PROC_REC(REGULATORYDOMAINS,WORDS),
-	PROC_REC(TEMPTYPE,WORDS),
-/*  	PROC_REC(CIS,BYTES), */
-	PROC_REC(STAID,WORDS),
-	PROC_REC(CURRENTSSID,STRING),
-	PROC_REC(CURRENTBSSID,BYTES),
-	PROC_REC(COMMSQUALITY,WORDS),
-	PROC_REC(CURRENTTXRATE,WORDS),
-	PROC_REC(CURRENTBEACONINTERVAL,WORDS),
-	PROC_REC(CURRENTSCALETHRESHOLDS,WORDS),
-	PROC_REC(PROTOCOLRSPTIME,WORDS),
-	PROC_REC(SHORTRETRYLIMIT,WORDS),
-	PROC_REC(LONGRETRYLIMIT,WORDS),
-	PROC_REC(MAXTRANSMITLIFETIME,WORDS),
-	PROC_REC(MAXRECEIVELIFETIME,WORDS),
-	PROC_REC(CFPOLLABLE,WORDS),
-	PROC_REC(AUTHENTICATIONALGORITHMS,WORDS),
-	PROC_REC(PRIVACYOPTIONIMPLEMENTED,WORDS),
-	PROC_REC(OWNMACADDR,BYTES),
-	PROC_REC(SCANRESULTSTABLE,WORDS),
-	PROC_REC(PHYTYPE,WORDS),
-	PROC_REC(CURRENTCHANNEL,WORDS),
-	PROC_REC(CURRENTPOWERSTATE,WORDS),
-	PROC_REC(CCAMODE,WORDS),
-	PROC_REC(SUPPORTEDDATARATES,WORDS),
-	PROC_REC(BUILDSEQ,BYTES),
-	PROC_REC(FWID,XSTRING)
-#undef PROC_REC
+#define DEBUG_REC(name,type) { HERMES_RID_##name, #name, DISPLAY_##type }
+	DEBUG_REC(CNFPORTTYPE,WORDS),
+	DEBUG_REC(CNFOWNMACADDR,BYTES),
+	DEBUG_REC(CNFDESIREDSSID,STRING),
+	DEBUG_REC(CNFOWNCHANNEL,WORDS),
+	DEBUG_REC(CNFOWNSSID,STRING),
+	DEBUG_REC(CNFOWNATIMWINDOW,WORDS),
+	DEBUG_REC(CNFSYSTEMSCALE,WORDS),
+	DEBUG_REC(CNFMAXDATALEN,WORDS),
+	DEBUG_REC(CNFPMENABLED,WORDS),
+	DEBUG_REC(CNFPMEPS,WORDS),
+	DEBUG_REC(CNFMULTICASTRECEIVE,WORDS),
+	DEBUG_REC(CNFMAXSLEEPDURATION,WORDS),
+	DEBUG_REC(CNFPMHOLDOVERDURATION,WORDS),
+	DEBUG_REC(CNFOWNNAME,STRING),
+	DEBUG_REC(CNFOWNDTIMPERIOD,WORDS),
+	DEBUG_REC(CNFMULTICASTPMBUFFERING,WORDS),
+	DEBUG_REC(CNFWEPENABLED_AGERE,WORDS),
+	DEBUG_REC(CNFMANDATORYBSSID_SYMBOL,WORDS),
+	DEBUG_REC(CNFWEPDEFAULTKEYID,WORDS),
+	DEBUG_REC(CNFDEFAULTKEY0,BYTES),
+	DEBUG_REC(CNFDEFAULTKEY1,BYTES),
+	DEBUG_REC(CNFMWOROBUST_AGERE,WORDS),
+	DEBUG_REC(CNFDEFAULTKEY2,BYTES),
+	DEBUG_REC(CNFDEFAULTKEY3,BYTES),
+	DEBUG_REC(CNFWEPFLAGS_INTERSIL,WORDS),
+	DEBUG_REC(CNFWEPKEYMAPPINGTABLE,WORDS),
+	DEBUG_REC(CNFAUTHENTICATION,WORDS),
+	DEBUG_REC(CNFMAXASSOCSTA,WORDS),
+	DEBUG_REC(CNFKEYLENGTH_SYMBOL,WORDS),
+	DEBUG_REC(CNFTXCONTROL,WORDS),
+	DEBUG_REC(CNFROAMINGMODE,WORDS),
+	DEBUG_REC(CNFHOSTAUTHENTICATION,WORDS),
+	DEBUG_REC(CNFRCVCRCERROR,WORDS),
+	DEBUG_REC(CNFMMLIFE,WORDS),
+	DEBUG_REC(CNFALTRETRYCOUNT,WORDS),
+	DEBUG_REC(CNFBEACONINT,WORDS),
+	DEBUG_REC(CNFAPPCFINFO,WORDS),
+	DEBUG_REC(CNFSTAPCFINFO,WORDS),
+	DEBUG_REC(CNFPRIORITYQUSAGE,WORDS),
+	DEBUG_REC(CNFTIMCTRL,WORDS),
+	DEBUG_REC(CNFTHIRTY2TALLY,WORDS),
+	DEBUG_REC(CNFENHSECURITY,WORDS),
+	DEBUG_REC(CNFGROUPADDRESSES,BYTES),
+	DEBUG_REC(CNFCREATEIBSS,WORDS),
+	DEBUG_REC(CNFFRAGMENTATIONTHRESHOLD,WORDS),
+	DEBUG_REC(CNFRTSTHRESHOLD,WORDS),
+	DEBUG_REC(CNFTXRATECONTROL,WORDS),
+	DEBUG_REC(CNFPROMISCUOUSMODE,WORDS),
+	DEBUG_REC(CNFBASICRATES_SYMBOL,WORDS),
+	DEBUG_REC(CNFPREAMBLE_SYMBOL,WORDS),
+	DEBUG_REC(CNFSHORTPREAMBLE,WORDS),
+	DEBUG_REC(CNFWEPKEYS_AGERE,BYTES),
+	DEBUG_REC(CNFEXCLUDELONGPREAMBLE,WORDS),
+	DEBUG_REC(CNFTXKEY_AGERE,WORDS),
+	DEBUG_REC(CNFAUTHENTICATIONRSPTO,WORDS),
+	DEBUG_REC(CNFBASICRATES,WORDS),
+	DEBUG_REC(CNFSUPPORTEDRATES,WORDS),
+	DEBUG_REC(CNFTICKTIME,WORDS),
+	DEBUG_REC(CNFSCANREQUEST,WORDS),
+	DEBUG_REC(CNFJOINREQUEST,WORDS),
+	DEBUG_REC(CNFAUTHENTICATESTATION,WORDS),
+	DEBUG_REC(CNFCHANNELINFOREQUEST,WORDS),
+	DEBUG_REC(MAXLOADTIME,WORDS),
+	DEBUG_REC(DOWNLOADBUFFER,WORDS),
+	DEBUG_REC(PRIID,WORDS),
+	DEBUG_REC(PRISUPRANGE,WORDS),
+	DEBUG_REC(CFIACTRANGES,WORDS),
+	DEBUG_REC(NICSERNUM,WORDS),
+	DEBUG_REC(NICID,WORDS),
+	DEBUG_REC(MFISUPRANGE,WORDS),
+	DEBUG_REC(CFISUPRANGE,WORDS),
+	DEBUG_REC(CHANNELLIST,WORDS),
+	DEBUG_REC(REGULATORYDOMAINS,WORDS),
+	DEBUG_REC(TEMPTYPE,WORDS),
+/*  	DEBUG_REC(CIS,BYTES), */
+	DEBUG_REC(STAID,WORDS),
+	DEBUG_REC(CURRENTSSID,STRING),
+	DEBUG_REC(CURRENTBSSID,BYTES),
+	DEBUG_REC(COMMSQUALITY,WORDS),
+	DEBUG_REC(CURRENTTXRATE,WORDS),
+	DEBUG_REC(CURRENTBEACONINTERVAL,WORDS),
+	DEBUG_REC(CURRENTSCALETHRESHOLDS,WORDS),
+	DEBUG_REC(PROTOCOLRSPTIME,WORDS),
+	DEBUG_REC(SHORTRETRYLIMIT,WORDS),
+	DEBUG_REC(LONGRETRYLIMIT,WORDS),
+	DEBUG_REC(MAXTRANSMITLIFETIME,WORDS),
+	DEBUG_REC(MAXRECEIVELIFETIME,WORDS),
+	DEBUG_REC(CFPOLLABLE,WORDS),
+	DEBUG_REC(AUTHENTICATIONALGORITHMS,WORDS),
+	DEBUG_REC(PRIVACYOPTIONIMPLEMENTED,WORDS),
+	DEBUG_REC(OWNMACADDR,BYTES),
+	DEBUG_REC(SCANRESULTSTABLE,WORDS),
+	DEBUG_REC(PHYTYPE,WORDS),
+	DEBUG_REC(CURRENTCHANNEL,WORDS),
+	DEBUG_REC(CURRENTPOWERSTATE,WORDS),
+	DEBUG_REC(CCAMODE,WORDS),
+	DEBUG_REC(SUPPORTEDDATARATES,WORDS),
+	DEBUG_REC(BUILDSEQ,BYTES),
+	DEBUG_REC(FWID,XSTRING)
+#undef DEBUG_REC
 };
-#define NUM_RIDS ( sizeof(record_table) / sizeof(record_table[0]) )
 
-static int
-orinoco_proc_get_hermes_recs(char *page, char **start, off_t requested_offset,
-			   int requested_len, int *eof, void *data)
+#define DEBUG_LTV_SIZE		128
+
+static int orinoco_debug_dump_recs(struct orinoco_private *priv)
 {
-	struct orinoco_private *priv = (struct orinoco_private *)data;
-	struct net_device *dev = priv->ndev;
 	hermes_t *hw = &priv->hw;
-	char *buf = page;
-	int total = 0, slop = 0;
 	u8 *val8;
 	u16 *val16;
 	int i,j;
 	u16 length;
 	int err;
 
-	if (! netif_device_present(dev))
-		return -ENODEV;
-
-	val8 = kmalloc(PROC_LTV_SIZE + 2, GFP_KERNEL);
+	/* I'm not sure: we might have a lock here, so we'd better go
+           atomic, just in case. */
+	val8 = kmalloc(DEBUG_LTV_SIZE + 2, GFP_ATOMIC);
 	if (! val8)
 		return -ENOMEM;
 	val16 = (u16 *)val8;
 
-	for (i = 0; i < NUM_RIDS; i++) {
+	for (i = 0; i < ARRAY_SIZE(record_table); i++) {
 		u16 rid = record_table[i].rid;
 		int len;
 
-		memset(val8, 0, PROC_LTV_SIZE + 2);
+		memset(val8, 0, DEBUG_LTV_SIZE + 2);
 
-		err = hermes_read_ltv(hw, USER_BAP, rid, PROC_LTV_SIZE,
+		err = hermes_read_ltv(hw, USER_BAP, rid, DEBUG_LTV_SIZE,
 				      &length, val8);
 		if (err) {
 			DEBUG(0, "Error %d reading RID 0x%04x\n", err, rid);
@@ -3732,210 +3993,46 @@
 		if (length == 0)
 			continue;
 
-		buf += sprintf(buf, "%-15s (0x%04x): length=%d (%d bytes)\tvalue=", record_table[i].name,
-			       rid, length, (length-1)*2);
-		len = min(((int)length-1)*2, PROC_LTV_SIZE);
+		printk(KERN_DEBUG "%-15s (0x%04x): length=%d (%d bytes)\tvalue=",
+		       record_table[i].name,
+		       rid, length, (length-1)*2);
+		len = min(((int)length-1)*2, DEBUG_LTV_SIZE);
 
 		switch (record_table[i].displaytype) {
 		case DISPLAY_WORDS:
-			for (j = 0; j < len / 2; j++) {
-				buf += sprintf(buf, "%04X-", le16_to_cpu(val16[j]));
-			}
-			buf--;
+			for (j = 0; j < len / 2; j++)
+				printk("%04X-", le16_to_cpu(val16[j]));
 			break;
 
 		case DISPLAY_BYTES:
 		default:
-			for (j = 0; j < len; j++) {
-				buf += sprintf(buf, "%02X:", val8[j]);
-			}
-			buf--;
+			for (j = 0; j < len; j++)
+				printk("%02X:", val8[j]);
 			break;
 
 		case DISPLAY_STRING:
 			len = min(len, le16_to_cpu(val16[0])+2);
 			val8[len] = '\0';
-			buf += sprintf(buf, "\"%s\"", (char *)&val16[1]);
+			printk("\"%s\"", (char *)&val16[1]);
 			break;
 
 		case DISPLAY_XSTRING:
-			buf += sprintf(buf, "'%s'", (char *)val8);
+			printk("'%s'", (char *)val8);
 		}
 
-		buf += sprintf(buf, "\n");
-
-		if (shift_buffer(page, requested_offset, requested_len,
-				 &total, &slop, &buf))
-			break;
-
-		if ( (buf - page) > PROC_SAFE_SIZE )
-			break;
+		printk("\n");
 	}
 
 	kfree(val8);
 
-	return calc_start_len(page, start, requested_offset, requested_len,
-			      total, buf);
-}
-
-#ifdef HERMES_DEBUG_BUFFER
-static int
-orinoco_proc_get_hermes_buf(char *page, char **start, off_t requested_offset,
-			    int requested_len, int *eof, void *data)
-{
-	struct orinoco_private *priv = (struct orinoco_private *)data;
-	hermes_t *hw = &priv->hw;
-	char *buf = page;
-	int total = 0, slop = 0;
-	int i;
-
-	for (i = 0; i < min_t(int,hw->dbufp, HERMES_DEBUG_BUFSIZE); i++) {
-		memcpy(buf, &hw->dbuf[i], sizeof(hw->dbuf[i]));
-		buf += sizeof(hw->dbuf[i]);
-
-		if (shift_buffer(page, requested_offset, requested_len,
-				 &total, &slop, &buf))
-			break;
-
-		if ( (buf - page) > PROC_SAFE_SIZE )
-			break;
-	}
-
-	return calc_start_len(page, start, requested_offset, requested_len,
-			      total, buf);
-}
-
-static int
-orinoco_proc_get_hermes_prof(char *page, char **start, off_t requested_offset,
-			    int requested_len, int *eof, void *data)
-{
-	struct orinoco_private *priv = (struct orinoco_private *)data;
-	hermes_t *hw = &priv->hw;
-	char *buf = page;
-	int total = 0, slop = 0;
-	int i;
-
-	for (i = 0; i < (HERMES_BAP_BUSY_TIMEOUT+1); i++) {
-		memcpy(buf, &hw->profile[i], sizeof(hw->profile[i]));
-		buf += sizeof(hw->profile[i]);
-
-		if (shift_buffer(page, requested_offset, requested_len,
-				 &total, &slop, &buf))
-			break;
-
-		if ( (buf - page) > PROC_SAFE_SIZE )
-			break;
-	}
-
-	return calc_start_len(page, start, requested_offset, requested_len,
-			      total, buf);
-}
-#endif /* HERMES_DEBUG_BUFFER */
-
-/* initialise the /proc subsystem for the hermes driver, creating the
- * separate entries */
-static int
-orinoco_proc_init(void)
-{
-	int err = 0;
-
-	TRACE_ENTER("orinoco");
-
-	/* create the directory for it to sit in */
-	dir_base = create_proc_entry("hermes", S_IFDIR, &proc_root);
-	if (dir_base == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/hermes.\n");
-		orinoco_proc_cleanup();
-		err = -ENOMEM;
-	}
-
-	TRACE_EXIT("orinoco");
-
-	return err;
-}
-
-int
-orinoco_proc_dev_init(struct orinoco_private *priv)
-{
-	struct net_device *dev = priv->ndev;
-	struct proc_dir_entry *e;
-
-	priv->dir_dev = NULL;
-
-	/* create the directory for it to sit in */
-	priv->dir_dev = create_proc_entry(dev->name, S_IFDIR | S_IRUGO | S_IXUGO,
-					  dir_base);
-	if (! priv->dir_dev) {
-		printk(KERN_ERR "Unable to initialize /proc/hermes/%s\n",  dev->name);
-		goto fail;
-	}
-
-	e = create_proc_read_entry("recs", S_IFREG | S_IRUGO,
-			       priv->dir_dev, orinoco_proc_get_hermes_recs, priv);
-	if (! e) {
-		printk(KERN_ERR "Unable to initialize /proc/hermes/%s/recs\n",  dev->name);
-		goto fail;
-	}
-
-#ifdef HERMES_DEBUG_BUFFER
-	e = create_proc_read_entry("buf", S_IFREG | S_IRUGO,
-					       priv->dir_dev, orinoco_proc_get_hermes_buf, priv);
-	if (! e) {
-		printk(KERN_ERR "Unable to intialize /proc/hermes/%s/buf\n", dev->name);
-		goto fail;
-	}
-
-	e = create_proc_read_entry("prof", S_IFREG | S_IRUGO,
-					       priv->dir_dev, orinoco_proc_get_hermes_prof, priv);
-	if (! e) {
-		printk(KERN_ERR "Unable to intialize /proc/hermes/%s/prof\n", dev->name);
-		goto fail;
-	}
-#endif /* HERMES_DEBUG_BUFFER */
-
 	return 0;
- fail:
-	orinoco_proc_dev_cleanup(priv);
-	return -ENOMEM;
-}
-
-void
-orinoco_proc_dev_cleanup(struct orinoco_private *priv)
-{
-	struct net_device *dev = priv->ndev;
-
-	TRACE_ENTER(priv->ndev->name);
-
-	if (priv->dir_dev) {
-		remove_proc_entry("prof", priv->dir_dev);
-		remove_proc_entry("buf", priv->dir_dev);
-		remove_proc_entry("recs", priv->dir_dev);
-		remove_proc_entry(dev->name, dir_base);
-		priv->dir_dev = NULL;
-	}
-
-	TRACE_EXIT(priv->ndev->name);
-}
-
-static void
-orinoco_proc_cleanup(void)
-{
-	TRACE_ENTER("orinoco");
-
-	if (dir_base) {
-		remove_proc_entry("hermes", &proc_root);
-		dir_base = NULL;
-	}
-	
-	TRACE_EXIT("orinoco");
 }
 
-struct net_device *alloc_orinocodev(int sizeof_card)
+struct net_device *alloc_orinocodev(int sizeof_card, int (*hard_reset)(struct orinoco_private *))
 {
 	struct net_device *dev;
 	struct orinoco_private *priv;
 
-	TRACE_ENTER("orinoco");
 	dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card);
 	priv = (struct orinoco_private *)dev->priv;
 	priv->ndev = dev;
@@ -3955,39 +4052,49 @@
 	dev->change_mtu = orinoco_change_mtu;
 	dev->set_multicast_list = orinoco_set_multicast_list;
 
-	dev->open = NULL;		/* Caller *must* override  these */
-	dev->stop = NULL;
-
-	/* Setup the private structure */
+	/* Set up default callbacks */
+	dev->open = orinoco_open;
+	dev->stop = orinoco_stop;
+	priv->hard_reset = hard_reset;
 
 	spin_lock_init(&priv->lock);
-	priv->hard_reset = NULL;	/* Caller may override */
-	
-	TRACE_EXIT("orinoco");
+	priv->open = 0;
+	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);
+
+	priv->last_linkstatus = 0xffff;
+	priv->connected = 0;
+
 	return dev;
 
 }
 
 /********************************************************************/
-/* module bookkeeping                                               */
+/* Module initialization                                            */
 /********************************************************************/
 
 EXPORT_SYMBOL(alloc_orinocodev);
-EXPORT_SYMBOL(orinoco_shutdown);
-EXPORT_SYMBOL(orinoco_reset);
-EXPORT_SYMBOL(orinoco_proc_dev_init);
-EXPORT_SYMBOL(orinoco_proc_dev_cleanup);
+
+EXPORT_SYMBOL(__orinoco_up);
+EXPORT_SYMBOL(__orinoco_down);
+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 int __init init_orinoco(void)
 {
 	printk(KERN_DEBUG "%s\n", version);
-	return orinoco_proc_init();
+	return 0;
 }
 
 static void __exit exit_orinoco(void)
 {
-	orinoco_proc_cleanup();
 }
 
 module_init(init_orinoco);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)