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

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

diff -urN linux-2.4.18/drivers/net/wireless/orinoco.c linux-2.4.19/drivers/net/wireless/orinoco.c
@@ -1,11 +1,7 @@
-/* orinoco.c 0.09b	- (formerly known as dldwd_cs.c and orinoco_cs.c)
+/* orinoco.c 0.11b	- (formerly known as dldwd_cs.c and orinoco_cs.c)
  *
- * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
- * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
- * EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and others).
- * It should also be usable on various Prism II based cards such as the
- * Linksys, D-Link and Farallon Skyline. It should also work on Symbol
- * cards such as the 3Com AirConnect and Ericsson WLAN.
+ * A driver for Hermes or Prism 2 chipset based PCMCIA wireless
+ * adaptors, with Lucent/Agere, Intersil or Symbol firmware.
  *
  * Copyright (C) 2000 David Gibson, Linuxcare Australia <hermes@gibson.dropbear.id.au>
  *	With some help from :
@@ -41,12 +37,9 @@
  * 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.
- */
+ * under either the MPL or the GPL.  */
 
 /*
- * Tentative changelog...
- *
  * v0.01 -> v0.02 - 21/3/2001 - Jean II
  *	o Allow to use regular ethX device name instead of dldwdX
  *	o Warning on IBSS with ESSID=any for firmware 6.06
@@ -229,7 +222,7 @@
  * v0.09a -> v0.09b - 16/1/2002 - David Gibson
  *	o Fixed even stupider mistake in new interrupt handling, which
  *	  seriously broke things on big-endian machines.
- *	o Removed a bunch of redundand includes and exports.
+ *	o Removed a bunch of redundant includes and exports.
  *	o Removed a redundant MOD_{INC,DEC}_USE_COUNT pair in airport.c
  *	o Don't attempt to do hardware level multicast reception on
  *	  Intersil firmware, just go promisc instead.
@@ -239,24 +232,69 @@
  *	o Moved netif_wake_queue() from ALLOC interrupts to TX and TXEXC
  *	  interrupts, which should fix the timeouts we're seeing.
  *
+ * v0.09b -> v0.10 - 25 Feb 2002 - David Gibson
+ *	o Removed nested structures used for header parsing, so the
+ *	  driver should now work without hackery on ARM
+ *	o Fix for WEP handling on Intersil (Hawk Newton)
+ *	o Eliminated the /proc/hermes/ethXX/regs debugging file.  It
+ *	  was never very useful.
+ *	o Make Rx errors less noisy.
+ *
+ * v0.10 -> v0.11 - 5 Apr Mar 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
+ *	  leaving promiscuous mode).
+ *	o Relegated Tx error messages to debug.
+ *	o Cleaned up / corrected handling of allocation lengths.
+ *	o Set OWNSSID in IBSS mode for WinXP interoperability (jimc).
+ *	o Change to using alloc_etherdev() for structure allocations. 
+ *	o Check for and drop undersized packets.
+ *	o Fixed a race in stopping/waking the queue.  This should fix
+ *	  the timeout problems (Pavel Roskin)
+ *	o Reverted to netif_wake_queue() on the ALLOC event.
+ *	o Fixes for recent Symbol firmwares which lack AP density
+ *	  (Pavel Roskin).
+ *
+ * v0.11 -> v0.11b - 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
+ *	  and airport.  Removed card->priv field.
+ *	o Make response structure optional for hermes_docmd_wait()
+ *	  Pavel Roskin)
+ *	o Added PCI id for Nortel emobility to orinoco_plx.c.
+ *	o Cleanup to handling of Symbol's allocation bug. (Pavel Roskin)
+ *	o Cleanups to firmware capability detection.
+ *	o Arrange for orinoco_pci.c to override firmware detection.
+ *	  We should be able to support the PCI Intersil cards now.
+ *	o Cleanup handling of reset_cor and hard_reset (Pavel Roskin).
+ *	o Remove erroneous use of USER_BAP in the TxExc handler (Jouni
+ *	  Malinen).
+ *	o Makefile changes for better integration into David Hinds
+ *	  pcmcia-cs package.
+ *
+ * v0.11a -> v0.11b - 1 May 2002 - David Gibson
+ *	o Better error reporting in orinoco_plx_init_one()
+ *	o Fixed multiple bad kfree() bugs introduced by the
+ *	  alloc_orinocodev() changes.
+ *
  * TODO
- *	o Find and kill remaining Tx timeout problems
- *	o Fix WEP / order of iwconfig wierdness on Intersil firmware
- *	o Convert /proc debugging stuff to seqfile
- *	o Re-assess our encapsulation detection strategy
- *	o Handle de-encapsulation within NET framework, provide 802.11
+ *	o New wireless extensions API
+ *	o Handle de-encapsulation within network layer, provide 802.11
  *	  headers
  *	o Fix possible races in SPY handling.
- *	o Take the xmit lock when calling orinoco_reset from an
- *	  ioctl(), because that protects the multicast list.
- *	o Fix allocation lengths.
+ *	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 dldwd_lock() and
- * dldwd_unlock() (which in turn use spin_lock_bh() and
+ * 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
@@ -275,7 +313,7 @@
  * 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
- * __dldwd_stop_irqs() which will set a flag to disable the interrupt
+ * __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. */
@@ -295,6 +333,7 @@
 #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>
@@ -306,7 +345,7 @@
 #include "orinoco.h"
 #include "ieee802_11.h"
 
-/* Wireless extensions backwares compatibility */
+/* Wireless extensions backwards compatibility */
 #ifndef SIOCIWFIRSTPRIV
 #define SIOCIWFIRSTPRIV		SIOCDEVPRIVATE
 #endif /* SIOCIWFIRSTPRIV */
@@ -318,7 +357,7 @@
 #define SPY_NUMBER(priv)	0
 #endif /* WIRELESS_SPY */
 
-static char version[] __initdata = "orinoco.c 0.09b (David Gibson <hermes@gibson.dropbear.id.au> and others)";
+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
@@ -329,40 +368,37 @@
 #ifdef ORINOCO_DEBUG
 int orinoco_debug = ORINOCO_DEBUG;
 MODULE_PARM(orinoco_debug, "i");
+EXPORT_SYMBOL(orinoco_debug);
 #endif
 
 #define ORINOCO_MIN_MTU		256
-#define ORINOCO_MAX_MTU		(HERMES_FRAME_LEN_MAX - ENCAPS_OVERHEAD)
+#define ORINOCO_MAX_MTU		(IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD)
 
 #define SYMBOL_MAX_VER_LEN	(14)
-#define LTV_BUF_SIZE		128
 #define USER_BAP		0
 #define IRQ_BAP			1
-#define ORINOCO_MACPORT		0
 #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 */
-#define TX_NICBUF_SIZE		2048
-#define TX_NICBUF_SIZE_BUG	1585		/* Bug in Symbol firmware */
-#define LARGE_KEY_SIZE		13
 #define SMALL_KEY_SIZE		5
-#define MAX_FRAME_SIZE		2304
+#define LARGE_KEY_SIZE		13
+#define TX_NICBUF_SIZE_BUG	1585		/* Bug in Symbol firmware */
 
 #define DUMMY_FID		0xFFFF
 
-#define MAX_MULTICAST(priv)	(priv->firmware_type == FIRMWARE_TYPE_AGERE ? \
-				 HERMES_MAX_MULTICAST : 0)
-
-/*
- * Data tables
- */
+/*#define MAX_MULTICAST(priv)	(priv->firmware_type == FIRMWARE_TYPE_AGERE ? \
+  HERMES_MAX_MULTICAST : 0)*/
+#define MAX_MULTICAST(priv)	(HERMES_MAX_MULTICAST)
+
+/********************************************************************/
+/* Data tables                                                      */
+/********************************************************************/
 
 /* The frequency of each channel in MHz */
 const long channel_frequency[] = {
 	2412, 2417, 2422, 2427, 2432, 2437, 2442,
 	2447, 2452, 2457, 2462, 2467, 2472, 2484
 };
-
 #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
 
 /* This tables gives the actual meanings of the bitrate IDs returned by the firmware. */
@@ -381,44 +417,26 @@
 	{55, 1,  7,  7},
 	{110, 0,  5,  8},
 };
-
 #define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0]))
 
-struct p8022_hdr {
+struct header_struct {
+	/* 802.3 */
+	u8 dest[ETH_ALEN];
+	u8 src[ETH_ALEN];
+	u16 len;
+	/* 802.2 */
 	u8 dsap;
 	u8 ssap;
 	u8 ctrl;
+	/* SNAP */
 	u8 oui[3];
-} __attribute__ ((packed));
-
-struct orinoco_rxframe_hdr {
-	struct hermes_rx_descriptor desc;
-	struct ieee802_11_hdr p80211;
-	struct ethhdr p8023;
-	struct p8022_hdr p8022;
 	u16 ethertype;
 } __attribute__ ((packed));
 
-struct orinoco_txframe_hdr {
-	struct hermes_tx_descriptor desc;
-	struct ieee802_11_hdr p80211;
-	struct ethhdr p8023;
-	struct p8022_hdr p8022;
-	u16 ethertype;
-} __attribute__ ((packed));
+/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
+u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
 
-#define P8023_OFFSET		(sizeof(struct hermes_rx_descriptor) + \
-				sizeof(struct ieee802_11_hdr))
-#define ENCAPS_OVERHEAD		(sizeof(struct p8022_hdr) + 2)
-
-/* 802.2 LLL header SNAP used for SNAP encapsulation over 802.11 */
-struct p8022_hdr encaps_hdr = {
-	0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}
-};
-
-typedef struct orinoco_commsqual {
-	u16 qual, signal, noise;
-} __attribute__ ((packed)) orinoco_commsqual_t;
+#define ENCAPS_OVERHEAD		(sizeof(encaps_hdr) + 2)
 
 /*
  * Function prototypes
@@ -426,14 +444,13 @@
 
 static void orinoco_stat_gather(struct net_device *dev,
 			      struct sk_buff *skb,
-			      struct orinoco_rxframe_hdr *hdr);
+			      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_hw_reset(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]);
@@ -441,7 +458,8 @@
 			      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,
-				    int32_t *rates, int max);
+				    s32 *rates, int max);
+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);
@@ -472,7 +490,6 @@
 static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq);
 static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq);
 static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq);
-static void __orinoco_set_multicast_list(struct net_device *dev);
 
 /* /proc debugging stuff */
 static int orinoco_proc_init(void);
@@ -515,14 +532,14 @@
 {
 	hermes_t *hw = &priv->hw;
 
-	TRACE_ENTER(priv->ndev.name);
+	TRACE_ENTER(priv->ndev->name);
 
-	__cli();
+	__cli(); /* FIXME: is this necessary? */
 	set_bit(ORINOCO_STATE_DOIRQ, &priv->state);
 	hermes_set_irqmask(hw, irqmask);
 	__sti();
 
-	TRACE_EXIT(priv->ndev.name);
+	TRACE_EXIT(priv->ndev->name);
 }
 
 static inline void
@@ -544,11 +561,17 @@
 		break;
 	default:
 		printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
-		       priv->ndev.name);
+		       priv->ndev->name);
 	}
 }
 
-extern void
+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;
@@ -562,66 +585,56 @@
  * Hardware control routines
  */
 
-static int
-__orinoco_hw_reset(struct orinoco_private *priv)
-{
-	hermes_t *hw = &priv->hw;
-
-	return hermes_reset(hw);
-}
-
 void
 orinoco_shutdown(struct orinoco_private *priv)
 {
-/* 	hermes_t *hw = &priv->hw; */
 	int err = 0;
 
-	TRACE_ENTER(priv->ndev.name);
+	TRACE_ENTER(priv->ndev->name);
 
 	orinoco_lock(priv);
 	__orinoco_stop_irqs(priv);
 
-	err = __orinoco_hw_reset(priv);
+	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);
+		printk(KERN_ERR "%s: Error %d shutting down Hermes chipset\n", priv->ndev->name, err);
 
 	orinoco_unlock(priv);
 
-	TRACE_EXIT(priv->ndev.name);
+	TRACE_EXIT(priv->ndev->name);
 }
 
 int
 orinoco_reset(struct orinoco_private *priv)
 {
-	struct net_device *dev = &priv->ndev;
+	struct net_device *dev = priv->ndev;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	struct hermes_idstring idbuf;
-	int frame_size;
 
-	TRACE_ENTER(priv->ndev.name);
+	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->need_card_reset) && (priv->card_reset_handler != NULL))
-		priv->card_reset_handler(priv);
+	if (priv->hard_reset)
+		priv->hard_reset(priv);
 
 	/* Do standard firmware reset if we can */
-	err = __orinoco_hw_reset(priv);
+	err = hermes_reset(hw);
 	if (err)
 		goto out;
 
-	frame_size = TX_NICBUF_SIZE;
-	/* This stupid bug is present in Intel firmware 1.10, and
-	 * may be fixed in later firmwares - Jean II */
-	if(priv->broken_allocate)
-		frame_size = TX_NICBUF_SIZE_BUG;
-	err = hermes_allocate(hw, frame_size, &priv->txfid);
-	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;
+	}
 
 	/* Now set up all the parameters on the card */
 	
@@ -645,21 +658,16 @@
 		}
 	}
 
-	/* Set up encryption */
-	if (priv->has_wep) {
-		err = __orinoco_hw_setup_wep(priv);
-		if (err) {
-			printk(KERN_ERR "%s: Error %d activating WEP.\n",
-			       dev->name, err);
-			goto out;
-		}
-	}
-
 	/* Set the desired ESSID */
 	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
 	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
-	err = hermes_write_ltv(hw, USER_BAP, (priv->port_type == 3) ?
-			       HERMES_RID_CNFOWNSSID : HERMES_RID_CNFDESIREDSSID,
+	/* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
+	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
+			       HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
+			       &idbuf);
+	if (err)
+		goto out;
+	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
 			       HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
 			       &idbuf);
 	if (err)
@@ -680,9 +688,12 @@
 		goto out;
 
 	/* Set AP density */
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, priv->ap_density);
-	if (err)
-		goto out;
+	if (priv->has_sensitivity) {
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
+					   priv->ap_density);
+		if (err)
+			priv->has_sensitivity = 0;
+	}
 
 	/* Set RTS threshold */
 	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh);
@@ -740,6 +751,16 @@
 		}
 	}
 
+	/* Set up encryption */
+	if (priv->has_wep) {
+		err = __orinoco_hw_setup_wep(priv);
+		if (err) {
+			printk(KERN_ERR "%s: Error %d activating WEP.\n",
+			       dev->name, err);
+			goto out;
+		}
+	}
+
 	/* Set promiscuity / multicast*/
 	priv->promiscuous = 0;
 	priv->mc_count = 0;
@@ -750,14 +771,14 @@
 			   HERMES_EV_WTERR | HERMES_EV_INFO |
 			   HERMES_EV_INFDROP);
 
-	err = hermes_enable_port(hw, ORINOCO_MACPORT);
+	err = hermes_enable_port(hw, 0);
 	if (err)
 		goto out;
 
  out:
 	orinoco_unlock(priv);
 
-	TRACE_EXIT(priv->ndev.name);
+	TRACE_EXIT(priv->ndev->name);
 
 	return err;
 }
@@ -767,11 +788,11 @@
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 
-	TRACE_ENTER(priv->ndev.name);
+	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);
+		       priv->ndev->name, priv->bitratemode);
 		return -EINVAL;
 	}
 
@@ -791,7 +812,7 @@
 		BUG();
 	}
 
-	TRACE_EXIT(priv->ndev.name);
+	TRACE_EXIT(priv->ndev->name);
 
 	return err;
 }
@@ -804,7 +825,7 @@
 	int	master_wep_flag;
 	int	auth_flag;
 
-	TRACE_ENTER(priv->ndev.name);
+	TRACE_ENTER(priv->ndev->name);
 
 	switch (priv->firmware_type) {
 	case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
@@ -837,19 +858,17 @@
 
 			/* Fudge around firmware weirdness */
 			keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
-
+			
 			/* Write all 4 keys */
 			for(i = 0; i < ORINOCO_MAX_KEYS; i++) {
 /*  				int keylen = le16_to_cpu(priv->keys[i].len); */
-
+				
 				if (keylen > LARGE_KEY_SIZE) {
 					printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
-					       priv->ndev.name, i, keylen);
+					       priv->ndev->name, i, keylen);
 					return -E2BIG;
 				}
 
-				printk("About to write key %d, keylen=%d\n",
-				       i, keylen);				     
 				err = hermes_write_ltv(hw, USER_BAP,
 						       HERMES_RID_CNFDEFAULTKEY0 + i,
 						       HERMES_BYTES_TO_RECLEN(keylen),
@@ -863,32 +882,25 @@
 						   priv->tx_key);
 			if (err)
 				return err;
-
-			/* Authentication is where Intersil and Symbol
-			 * firmware differ... */
-			if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) {
-				/* Symbol cards : set the authentication :
-				 * 0 -> no encryption, 1 -> open,
-				 * 2 -> shared key
-				 * 3 -> shared key 128 -> AP only */
-				if(priv->wep_restrict)
-					auth_flag = 2;
-				else
-					auth_flag = 1;
-				err = hermes_write_wordrec(hw, USER_BAP,
-							   HERMES_RID_CNFAUTHENTICATION, auth_flag);
-				if (err)
-					return err;
-				/* Master WEP setting is always 3 */
+			
+			if (priv->wep_restrict) {
+				auth_flag = 2;
 				master_wep_flag = 3;
 			} else {
-				/* Prism2 card : we need to modify master
-				 * WEP setting */
-				if(priv->wep_restrict)
-					master_wep_flag = 3;
-				else
-					master_wep_flag = 1;
+				/* Authentication is where Intersil and Symbol
+				 * firmware differ... */
+				auth_flag = 1;
+				if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
+					master_wep_flag = 3; /* Symbol */ 
+				else 
+					master_wep_flag = 1; /* Intersil */
 			}
+
+
+			err = hermes_write_wordrec(hw, USER_BAP,
+						   HERMES_RID_CNFAUTHENTICATION, auth_flag);
+			if (err)
+				return err;
 		}
 		
 		/* Master WEP setting : on/off */
@@ -903,12 +915,12 @@
 	default:
 		if (priv->wep_on) {
 			printk(KERN_ERR "%s: WEP enabled, although not supported!\n",
-			       priv->ndev.name);
+			       priv->ndev->name);
 			return -EINVAL;
 		}
 	}
 
-	TRACE_EXIT(priv->ndev.name);
+	TRACE_EXIT(priv->ndev->name);
 
 	return 0;
 }
@@ -937,7 +949,7 @@
 	char *p = (char *)(&essidbuf.val);
 	int len;
 
-	TRACE_ENTER(priv->ndev.name);
+	TRACE_ENTER(priv->ndev->name);
 
 	orinoco_lock(priv);
 
@@ -971,14 +983,14 @@
 
 	len = le16_to_cpu(essidbuf.len);
 
-	memset(buf, 0, sizeof(buf));
+	memset(buf, 0, IW_ESSID_MAX_SIZE+1);
 	memcpy(buf, p, len);
 	buf[len] = '\0';
 
  fail_unlock:
 	orinoco_unlock(priv);
 
-	TRACE_EXIT(priv->ndev.name);
+	TRACE_EXIT(priv->ndev->name);
 
 	return err;       
 }
@@ -998,7 +1010,7 @@
 		goto out;
 
 	if ( (channel < 1) || (channel > NUM_CHANNELS) ) {
-		struct net_device *dev = &priv->ndev;
+		struct net_device *dev = priv->ndev;
 
 		printk(KERN_WARNING "%s: Channel out of range (%d)!\n", dev->name, channel);
 		err = -EBUSY;
@@ -1016,7 +1028,7 @@
 }
 
 static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates,
-				    int32_t *rates, int max)
+				    s32 *rates, int max)
 {
 	hermes_t *hw = &priv->hw;
 	struct hermes_idstring list;
@@ -1114,7 +1126,7 @@
 {
 	struct orinoco_private *priv = (struct orinoco_private *) dev_id;
 	hermes_t *hw = &priv->hw;
-	struct net_device *dev = &priv->ndev;
+	struct net_device *dev = priv->ndev;
 	int count = MAX_IRQLOOPS_PER_IRQ;
 	u16 evstat, events;
 	/* These are used to detect a runaway interrupt situation */
@@ -1131,23 +1143,21 @@
 		return;
 	}
 
-	DEBUG(3, "%s: orinoco_interrupt()\n", priv->ndev.name);
+	DEBUG(3, "%s: orinoco_interrupt()\n", priv->ndev->name);
 
 	evstat = hermes_read_regn(hw, EVSTAT);
 	events = evstat & hw->inten;
 	
-	if (! events) { /* Sometimes the card generates Tx interrupts without setting EVSTAT,
-			   or so I've heard - FIXME does it really happen? */
-		printk(KERN_WARNING "%s: Null event in orinoco_interrupt!\n", priv->ndev.name);
-		__orinoco_ev_alloc(priv, hw);
-	}
+/*  	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(4, "__orinoco_interrupt(): count=%d EVSTAT=0x%04x\n",
+		DEBUG(3, "__orinoco_interrupt(): count=%d EVSTAT=0x%04x\n",
 		      count, evstat);
 		
 		if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
@@ -1194,7 +1204,7 @@
 
 static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw)
 {
-	printk(KERN_DEBUG "%s: TICK\n", priv->ndev.name);
+	printk(KERN_DEBUG "%s: TICK\n", priv->ndev->name);
 }
 
 static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw)
@@ -1202,17 +1212,17 @@
 	/* 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);
+	      priv->ndev->name);
 }
 
 static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw)
 {
-	printk(KERN_WARNING "%s: Information frame lost.\n", priv->ndev.name);
+	printk(KERN_WARNING "%s: Information frame lost.\n", priv->ndev->name);
 }
 
 static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw)
 {
-	struct net_device *dev = &priv->ndev;
+	struct net_device *dev = priv->ndev;
 	u16 infofid;
 	struct {
 		u16 len;
@@ -1225,7 +1235,7 @@
 	 * The controller return to us a pseudo frame containing
 	 * the information in question - Jean II */
 	infofid = hermes_read_regn(hw, INFOFID);
-	DEBUG(3, "%s: __dldwd_ev_info(): INFOFID=0x%04x\n", dev->name,
+	DEBUG(3, "%s: __orinoco_ev_info(): INFOFID=0x%04x\n", dev->name,
 	      infofid);
 
 	/* Read the info frame header - don't try too hard */
@@ -1277,7 +1287,7 @@
 	break;
 	default:
 		DEBUG(1, "%s: Unknown information frame received (type %04x).\n",
-		      priv->ndev.name, le16_to_cpu(info.type));
+		      priv->ndev->name, le16_to_cpu(info.type));
 		/* We don't actually do anything about it */
 		break;
 	}
@@ -1285,62 +1295,68 @@
 
 static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw)
 {
-	struct net_device *dev = &priv->ndev;
+	struct net_device *dev = priv->ndev;
 	struct net_device_stats *stats = &priv->stats;
 	struct iw_statistics *wstats = &priv->wstats;
 	struct sk_buff *skb = NULL;
 	u16 rxfid, status;
 	int length, data_len, data_off;
 	char *p;
-	struct orinoco_rxframe_hdr hdr;
+	struct hermes_rx_descriptor desc;
+	struct header_struct hdr;
 	struct ethhdr *eh;
 	int err;
 
 	rxfid = hermes_read_regn(hw, RXFID);
 	DEBUG(3, "__orinoco_ev_rx(): RXFID=0x%04x\n", rxfid);
 
-	/* We read in the entire frame header here. This isn't really
-	   necessary, since we ignore most of it, but it's
-	   conceptually simpler. We can tune this later if
-	   necessary. */
-	err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr), rxfid, 0);
+	err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc),
+			       rxfid, 0);
 	if (err) {
-		printk(KERN_ERR "%s: error %d reading frame header. "
+		printk(KERN_ERR "%s: error %d reading Rx descriptor. "
 		       "Frame dropped.\n", dev->name, err);
 		stats->rx_errors++;
 		goto drop;
 	}
 
-	status = le16_to_cpu(hdr.desc.status);
+	status = le16_to_cpu(desc.status);
 	
 	if (status & HERMES_RXSTAT_ERR) {
-		if ((status & HERMES_RXSTAT_ERR) == HERMES_RXSTAT_BADCRC) {
-			stats->rx_crc_errors++;
-			DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name);
-		} else if ((status & HERMES_RXSTAT_ERR)
-			   == HERMES_RXSTAT_UNDECRYPTABLE) {
+		if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
 			wstats->discard.code++;
-			printk(KERN_WARNING "%s: Undecryptable frame on Rx. Frame dropped.\n",
+			DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
 			       dev->name);
 		} else {
-			wstats->discard.misc++;
-			printk("%s: Unknown Rx error (0x%x). Frame dropped.\n",
-			       dev->name, status & HERMES_RXSTAT_ERR);
+			stats->rx_crc_errors++;
+			DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name);
 		}
 		stats->rx_errors++;
 		goto drop;
 	}
 
-	length = le16_to_cpu(hdr.p80211.data_len);
-	/* Yes, you heard right, that's le16. 802.2 and 802.3 are
-	   big-endian, but 802.11 is little-endian believe it or
-	   not. */
-	/* Correct. 802.3 is big-endian byte order and little endian bit
-	 * order, whereas 802.11 is little endian for both byte and bit
-	 * order. That's specified in the 802.11 spec. - Jean II */
+	/* For now we ignore the 802.11 header completely, assuming
+           that the card's firmware has handled anything vital */
+
+	err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr),
+			       rxfid, HERMES_802_3_OFFSET);
+	if (err) {
+		printk(KERN_ERR "%s: error %d reading frame header. "
+		       "Frame dropped.\n", dev->name, err);
+		stats->rx_errors++;
+		goto drop;
+	}
+
+	length = ntohs(hdr.len);
 	
-	/* Sanity check */
-	if (length > MAX_FRAME_SIZE) {
+	/* 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++;
+		goto drop;
+	}
+	if (length > IEEE802_11_DATA_LEN) {
 		printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
 		       dev->name, length);
 		stats->rx_length_errors++;
@@ -1371,39 +1387,41 @@
 	 */
 	if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
 	   ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
-	   (!memcmp(&hdr.p8022, &encaps_hdr, 3))) {
+	   is_snap(&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. */
 
+		if (length < ENCAPS_OVERHEAD) {
+			stats->rx_length_errors++;
+			stats->rx_dropped++;
+			goto drop;
+		}
+
 		/* Remove SNAP header, reconstruct EthernetII frame */
 		data_len = length - ENCAPS_OVERHEAD;
-		data_off = sizeof(hdr);
+		data_off = HERMES_802_3_OFFSET + sizeof(hdr);
 
 		eh = (struct ethhdr *)skb_put(skb, ETH_HLEN);
 
-		memcpy(eh, &hdr.p8023, sizeof(hdr.p8023));
+		memcpy(eh, &hdr, 2 * ETH_ALEN);
 		eh->h_proto = hdr.ethertype;
 	} else {
-		/* All other cases indicate a genuine 802.3 frame.
-		 * No decapsulation needed */
-
-		/* Otherwise, we just throw the whole thing in,
-		 * and hope the protocol layer can deal with it
-		 * as 802.3 */
+		/* All other cases indicate a genuine 802.3 frame.  No
+		   decapsulation needed.  We just throw the whole
+		   thing in, and hope the protocol layer can deal with
+		   it as 802.3 */
 		data_len = length;
-		data_off = P8023_OFFSET;
+		data_off = HERMES_802_3_OFFSET;
+		/* FIXME: we re-read from the card data we already read here */
 	}
 
 	p = skb_put(skb, data_len);
 	err = hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len),
 			       rxfid, data_off);
 	if (err) {
-		if (err == -EIO)
-			DEBUG(1, "%s: EIO reading frame header.\n", dev->name);
-		else
-			printk(KERN_ERR "%s: error %d reading frame header. "
-			       "Frame dropped.\n", dev->name, err);
+		printk(KERN_ERR "%s: error %d reading frame header. "
+		       "Frame dropped.\n", dev->name, err);
 		stats->rx_errors++;
 		goto drop;
 	}
@@ -1414,7 +1432,7 @@
 	skb->ip_summed = CHECKSUM_NONE;
 	
 	/* Process the wireless stats if needed */
-	orinoco_stat_gather(dev, skb, &hdr);
+	orinoco_stat_gather(dev, skb, &desc);
 
 	/* Pass the packet to the networking stack */
 	netif_rx(skb);
@@ -1431,7 +1449,7 @@
 
 static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw)
 {
-	struct net_device *dev = &priv->ndev;
+	struct net_device *dev = priv->ndev;
 	struct net_device_stats *stats = &priv->stats;
 	u16 fid = hermes_read_regn(hw, TXCOMPLFID);
 	struct hermes_tx_descriptor desc;
@@ -1440,72 +1458,78 @@
 	if (fid == DUMMY_FID)
 		return; /* Nothing's really happened */
 
-	err = hermes_bap_pread(hw, USER_BAP, &desc, sizeof(desc), fid, 0);
+	err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), fid, 0);
 	if (err) {
 		printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
 		       "(FID=%04X error %d)\n",
 		       dev->name, fid, err);
 	} else {
-		printk(KERN_INFO "%s: Tx error, status %d (FID=%04X)\n",
-		       dev->name, le16_to_cpu(desc.status), fid);
+		DEBUG(1, "%s: Tx error, status %d\n",
+		      dev->name, le16_to_cpu(desc.status));
 	}
 	
 	stats->tx_errors++;
-	netif_wake_queue(dev);
 
 	hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
 static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw)
 {
-	struct net_device *dev = &priv->ndev;
+/*  	struct net_device *dev = priv->ndev; */
 	struct net_device_stats *stats = &priv->stats;
 /*  	u16 fid = hermes_read_regn(hw, TXCOMPLFID); */
 
-	/* We don't generally use the Tx event (to cut down on
-	   interrupts) - we do the transmit complet processing once
-	   the transmit buffer is reclaimed in __orinoco_ev_alloc() ,
-	   hence nothing here */
-
-/*  	DEBUG(2, "%s: Transmit completed (FID=%04X)\n", priv->ndev.name, fid); */
+/*  	DEBUG(2, "%s: Transmit completed (FID=%04X)\n", priv->ndev->name, fid); */
 
 	stats->tx_packets++;
-	netif_wake_queue(dev);
 
 	hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
 static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw)
 {
-	struct net_device *dev = &priv->ndev;
+	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);
-
-	/* We don't generally request Tx complete events to cut down
-	   on the number of interrupts, so we do trasmit complete
-	   processing here, which happens once the firmware is done
-	   with the transmit buffer */
+	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",
 			       dev->name, fid);
 		return;
+	} else {
+		netif_wake_queue(dev);
 	}
 
 	hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
 }
 
+struct sta_id {
+	u16 id, vendor, 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;
+	
+	if (sta_id->vendor == 1)
+		return FIRMWARE_TYPE_AGERE;
+	else if ((sta_id->vendor == 2) &&
+		   ((firmver == 0x10001) || (firmver == 0x20001)))
+		return FIRMWARE_TYPE_SYMBOL;
+	else
+		return FIRMWARE_TYPE_INTERSIL;
+}
+
 static void determine_firmware(struct net_device *dev)
 {
 	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err;
-	struct sta_id {
-		u16 id, vendor, major, minor;
-	} __attribute__ ((packed)) sta_id;
+	struct sta_id sta_id;
 	u32 firmver;
+	char tmp[SYMBOL_MAX_VER_LEN+1];
 
 	/* Get the firmware version */
 	err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
@@ -1525,19 +1549,29 @@
 	       dev->name, sta_id.id, sta_id.vendor,
 	       sta_id.major, sta_id.minor);
 
-	/* Determine capabilities from the firmware version */
+	if (! priv->firmware_type)
+		priv->firmware_type = determine_firmware_type(dev, &sta_id);
+
+	/* Default capabilities */
+	priv->has_sensitivity = 1;
+	priv->has_mwo = 0;
+	priv->has_preamble = 0;
+	priv->has_port3 = 1;
+	priv->has_ibss = 1;
+	priv->has_ibss_any = 0;
+	priv->has_wep = 0;
+	priv->has_big_wep = 0;
+	priv->broken_cor_reset = 0;
 
-	if (sta_id.vendor == 1) {
+	/* Determine capabilities from the firmware version */
+	switch (priv->firmware_type) {
+	case FIRMWARE_TYPE_AGERE:
 		/* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
 		   ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
 		printk(KERN_DEBUG "%s: Looks like a Lucent/Agere firmware "
 		       "version %d.%02d\n", dev->name,
 		       sta_id.major, sta_id.minor);
 
-		priv->firmware_type = FIRMWARE_TYPE_AGERE;
-		priv->need_card_reset = 0;
-		priv->broken_allocate = 0;
-		priv->has_port3 = 1;		/* Still works in 7.28 */
 		priv->has_ibss = (firmver >= 0x60006);
 		priv->has_ibss_any = (firmver >= 0x60010);
 		priv->has_wep = (firmver >= 0x40020);
@@ -1545,18 +1579,18 @@
 					  Gold cards from the others? */
 		priv->has_mwo = (firmver >= 0x60000);
 		priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
-		priv->has_preamble = 0;
 		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 */
-	} else if ((sta_id.vendor == 2) &&
-		   ((firmver == 0x10001) || (firmver == 0x20001))) {
+		break;
+	case FIRMWARE_TYPE_SYMBOL:
 		/* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
 		/* Intel MAC : 00:02:B3:* */
 		/* 3Com MAC : 00:50:DA:* */
-		char tmp[SYMBOL_MAX_VER_LEN+1];
-
 		memset(tmp, 0, sizeof(tmp));
 		/* Get the Symbol firmware version */
 		err = hermes_read_ltv(hw, USER_BAP,
@@ -1584,20 +1618,16 @@
 		       "version [%s] (parsing to %X)\n", dev->name,
 		       tmp, firmver);
 
-		priv->firmware_type = FIRMWARE_TYPE_SYMBOL;
-		priv->need_card_reset = 1;
-		priv->broken_allocate = 1;
-		priv->has_port3 = 1;
 		priv->has_ibss = (firmver >= 0x20000);
 		priv->has_wep = (firmver >= 0x15012);
 		priv->has_big_wep = (firmver >= 0x20000);
-		priv->has_mwo = 0;
 		priv->has_pm = (firmver >= 0x20000) && (firmver < 0x22000);
 		priv->has_preamble = (firmver >= 0x20000);
 		priv->ibss_port = 4;
 		/* Tested with Intel firmware : 0x20015 => Jean II */
 		/* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
-	} else {
+		break;
+	case FIRMWARE_TYPE_INTERSIL:
 		/* D-Link, Linksys, Adtron, ZoomAir, and many others...
 		 * Samsung, Compaq 100/200 and Proxim are slightly
 		 * different and less well tested */
@@ -1607,16 +1637,9 @@
 		       "version %d.%02d\n", dev->name,
 		       sta_id.major, sta_id.minor);
 
-		priv->firmware_type = FIRMWARE_TYPE_INTERSIL;
-		priv->need_card_reset = 0;
-		priv->broken_allocate = 0;
-		priv->has_port3 = 1;
 		priv->has_ibss = (firmver >= 0x00007); /* FIXME */
-		priv->has_wep = (firmver >= 0x00008);
-		priv->has_big_wep = priv->has_wep;
-		priv->has_mwo = 0;
+		priv->has_big_wep = priv->has_wep = (firmver >= 0x00008);
 		priv->has_pm = (firmver >= 0x00007);
-		priv->has_preamble = 0;
 
 		if (firmver >= 0x00008)
 			priv->ibss_port = 0;
@@ -1626,6 +1649,9 @@
 			       dev->name);
 			priv->ibss_port = 1;
 		}
+		break;
+	default:
+		break;
 	}
 }
 
@@ -1633,7 +1659,7 @@
  * struct net_device methods
  */
 
-int
+static int
 orinoco_init(struct net_device *dev)
 {
 	struct orinoco_private *priv = dev->priv;
@@ -1647,6 +1673,8 @@
 
 	orinoco_lock(priv);
 
+	priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN;
+
 	/* Do standard firmware reset */
 	err = hermes_reset(hw);
 	if (err != 0) {
@@ -1711,10 +1739,10 @@
 	}
 
 	/* Get initial AP density */
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &priv->ap_density);
-	if (err) {
-		printk(KERN_ERR "%s: failed to read AP density!\n", dev->name);
-		goto out;
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
+				  &priv->ap_density);
+	if (err || priv->ap_density < 1 || priv->ap_density > 3) {
+		priv->has_sensitivity = 0;
 	}
 
 	/* Get initial RTS threshold */
@@ -1773,6 +1801,7 @@
 	/* By default use IEEE/IBSS ad-hoc mode if we have it */
 	priv->prefer_port3 = priv->has_port3 && (! priv->has_ibss);
 	set_port_type(priv);
+	priv->channel = 10; /* default channel, more-or-less arbitrary */
 
 	priv->promiscuous = 0;
 	priv->wep_on = 0;
@@ -1820,7 +1849,9 @@
 			wstats->qual.updated = priv->spy_stat[0].updated;
 		}
 	} else {
-		orinoco_commsqual_t cq;
+		struct {
+			u16 qual, signal, noise;
+		} __attribute__ ((packed)) cq;
 
 		err = HERMES_READ_RECORD(hw, USER_BAP,
 					 HERMES_RID_COMMSQUALITY, &cq);
@@ -1861,15 +1892,15 @@
 		if (!memcmp(mac, priv->spy_address[i], ETH_ALEN)) {
 			priv->spy_stat[i].level = level - 0x95;
 			priv->spy_stat[i].noise = noise - 0x95;
-			priv->spy_stat[i].qual = level - noise;
+			priv->spy_stat[i].qual = (level > noise) ? (level - noise) : 0;
 			priv->spy_stat[i].updated = 7;
 		}
 }
 
 void
-orinoco_stat_gather( struct net_device *dev,
-		   struct sk_buff *skb,
-		   struct orinoco_rxframe_hdr *hdr)
+orinoco_stat_gather(struct net_device *dev,
+		    struct sk_buff *skb,
+		    struct hermes_rx_descriptor *desc)
 {
 	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 
@@ -1885,11 +1916,11 @@
 	 * WIRELESS_SPY is not defined... - Jean II */
 	if (SPY_NUMBER(priv)) {
 		orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN,
-				   hdr->desc.signal, hdr->desc.silence);
+				   desc->signal, desc->silence);
 	}
 }
 
-int
+static int
 orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
@@ -1900,8 +1931,7 @@
 	char *p;
 	struct ethhdr *eh;
 	int len, data_len, data_off;
-	struct orinoco_txframe_hdr hdr;
-	hermes_response_t resp;
+	struct hermes_tx_descriptor desc;
 
 	if (! netif_running(dev)) {
 		printk(KERN_ERR "%s: Tx on stopped device!\n",
@@ -1924,83 +1954,69 @@
 
 	eh = (struct ethhdr *)skb->data;
 
-	/* Build the IEEE 802.11 header */
-	memset(&hdr, 0, sizeof(hdr));
-	memcpy(hdr.p80211.addr1, eh->h_dest, ETH_ALEN);
-	memcpy(hdr.p80211.addr2, eh->h_source, ETH_ALEN);
-	hdr.p80211.frame_ctl = IEEE802_11_FTYPE_DATA;
-
-	/* Request an interrupt on Tx failures, but not sucesses (we
-           use the buffer reclaim allocation event instead */
-	hdr.desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_EX | HERMES_TXCTRL_TX_OK);
+	memset(&desc, 0, sizeof(desc));
+ 	desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX);
+	err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0);
+	if (err) {
+		printk(KERN_ERR "%s: Error %d writing Tx descriptor to BAP\n",
+		       dev->name, err);
+		stats->tx_errors++;
+		goto fail;
+	}
 
 	/* Encapsulate Ethernet-II frames */
 	if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */
+		struct header_struct hdr;
 		data_len = len;
-		data_off = sizeof(hdr);
+		data_off = HERMES_802_3_OFFSET + sizeof(hdr);
 		p = skb->data + ETH_HLEN;
 
-		/* 802.11 header */
-		hdr.p80211.data_len = cpu_to_le16(data_len + ENCAPS_OVERHEAD);
-
 		/* 802.3 header */
-		memcpy(hdr.p8023.h_dest, eh->h_dest, ETH_ALEN);
-		memcpy(hdr.p8023.h_source, eh->h_source, ETH_ALEN);
-		hdr.p8023.h_proto = htons(data_len + ENCAPS_OVERHEAD);
+		memcpy(hdr.dest, eh->h_dest, ETH_ALEN);
+		memcpy(hdr.src, eh->h_source, ETH_ALEN);
+		hdr.len = htons(data_len + ENCAPS_OVERHEAD);
 		
 		/* 802.2 header */
-		memcpy(&hdr.p8022, &encaps_hdr, sizeof(encaps_hdr));
+		memcpy(&hdr.dsap, &encaps_hdr, sizeof(encaps_hdr));
 			
 		hdr.ethertype = eh->h_proto;
 		err  = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
-					 txfid, 0);
+					 txfid, HERMES_802_3_OFFSET);
 		if (err) {
-				printk(KERN_ERR "%s: Error %d writing packet header to BAP\n",
-				       dev->name, err);
+			printk(KERN_ERR "%s: Error %d writing packet header to BAP\n",
+			       dev->name, err);
 			stats->tx_errors++;
 			goto fail;
 		}
 	} else { /* IEEE 802.3 frame */
 		data_len = len + ETH_HLEN;
-		data_off = P8023_OFFSET;
+		data_off = HERMES_802_3_OFFSET;
 		p = skb->data;
-		
-		/* 802.11 header */
-		hdr.p80211.data_len = cpu_to_le16(len);
-		err = hermes_bap_pwrite(hw, USER_BAP, &hdr, P8023_OFFSET,
-					txfid, 0);
-		if (err) {
-			printk(KERN_ERR
-			       "%s: Error %d writing packet header to BAP\n",
-			       dev->name, err);
-			stats->tx_errors++;
-			goto fail;
-		}
 	}
 
 	/* Round up for odd length packets */
 	err = hermes_bap_pwrite(hw, USER_BAP, p, RUP_EVEN(data_len), txfid, data_off);
 	if (err) {
-		printk(KERN_ERR "%s: Error %d writing packet header to BAP\n",
+		printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
 		       dev->name, err);
 		stats->tx_errors++;
 		goto fail;
 	}
 
 	/* Finally, we actually initiate the send */
-	err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, txfid, &resp);
+	netif_stop_queue(dev);
+
+	err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, txfid, NULL);
 	if (err) {
+		netif_start_queue(dev);
 		printk(KERN_ERR "%s: Error %d transmitting packet\n", dev->name, err);
 		stats->tx_errors++;
 		goto fail;
 	}
 
-	atomic_inc(&priv->queue_length);
 	dev->trans_start = jiffies;
 	stats->tx_bytes += data_off + data_len;
 
-	netif_stop_queue(dev);
-
 	orinoco_unlock(priv);
 
 	dev_kfree_skb(skb);
@@ -2012,14 +2028,15 @@
 	return err;
 }
 
-void
+static void
 orinoco_tx_timeout(struct net_device *dev)
 {
 	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.\n", dev->name);
+	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));
 
 	stats->tx_errors++;
 
@@ -2033,6 +2050,110 @@
 	}
 }
 
+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;
+
+	if ( (new_mtu + ENCAPS_OVERHEAD + IEEE802_11_HLEN) >
+	     (priv->nicbuf_size - ETH_HLEN) )
+		return -EINVAL;
+
+	dev->mtu = new_mtu;
+
+	TRACE_EXIT(dev->name);
+
+	return 0;
+}
+
+static void
+__orinoco_set_multicast_list(struct net_device *dev)
+{
+	struct orinoco_private *priv = dev->priv;
+	hermes_t *hw = &priv->hw;
+	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) ||
+	     (dev->mc_count > MAX_MULTICAST(priv)) ) {
+		promisc = 1;
+		mc_count = 0;
+	} else {
+		promisc = 0;
+		mc_count = dev->mc_count;
+	}
+
+	if (promisc != priv->promiscuous) {
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFPROMISCUOUSMODE,
+					   promisc);
+		if (err) {
+			printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
+			       dev->name, err);
+		} else 
+			priv->promiscuous = promisc;
+	}
+
+	if (! promisc && (mc_count || priv->mc_count) ) {
+		struct dev_mc_list *p = dev->mc_list;
+		hermes_multicast_t mclist;
+		int i;
+
+		for (i = 0; i < mc_count; i++) {
+			/* Paranoia: */
+			if (! p)
+				BUG(); /* Multicast list shorter than mc_count */
+			if (p->dmi_addrlen != ETH_ALEN)
+				BUG(); /* Bad address size in multicast list */
+			
+			memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
+			p = p->next;
+		}
+		
+		if (p)
+			printk(KERN_WARNING "Multicast list is longer than mc_count\n");
+
+		err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES,
+				       HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN),
+				       &mclist);
+		if (err)
+			printk(KERN_ERR "%s: Error %d setting multicast list.\n",
+			       dev->name, err);
+		else
+			priv->mc_count = mc_count;
+	}
+
+	/* Since we can set the promiscuous flag when it wasn't asked
+	   for, make sure the net_device knows about it. */
+	if (priv->promiscuous)
+		dev->flags |= IFF_PROMISC;
+	else
+		dev->flags &= ~IFF_PROMISC;
+
+	TRACE_EXIT(dev->name);
+}
+
+/********************************************************************/
+/* Wireless extensions support                                      */
+/********************************************************************/
+
 static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq)
 {
 	struct orinoco_private *priv = dev->priv;
@@ -2345,9 +2466,15 @@
 
 	TRACE_ENTER(dev->name);
 
-	err = orinoco_hw_get_essid(priv, &active, essidbuf);
-	if (err)
-		return err;
+	if (netif_running(dev)) {
+		err = orinoco_hw_get_essid(priv, &active, essidbuf);
+		if (err)
+			return err;
+	} else {
+		orinoco_lock(priv);
+		memcpy(essidbuf, priv->desired_essid, sizeof(essidbuf));
+		orinoco_unlock(priv);
+	}
 
 	erq->flags = 1;
 	erq->length = strlen(essidbuf) + 1;
@@ -2446,6 +2573,9 @@
 	u16 val;
 	int err;
 
+	if (!priv->has_sensitivity)
+		return -EOPNOTSUPP;
+
 	orinoco_lock(priv);
 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &val);
 	orinoco_unlock(priv);
@@ -2464,6 +2594,9 @@
 	struct orinoco_private *priv = dev->priv;
 	int val = srq->value;
 
+	if (!priv->has_sensitivity)
+		return -EOPNOTSUPP;
+
 	if ((val < 1) || (val > 3))
 		return -EINVAL;
 	
@@ -2988,7 +3121,7 @@
 	return 0;
 }
 
-int
+static int
 orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	struct orinoco_private *priv = dev->priv;
@@ -3253,6 +3386,7 @@
 		break;
 	       
 	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)) {
@@ -3261,21 +3395,12 @@
 		}
 		
 		printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
-		orinoco_reset(priv);
-		break;
 
-	case SIOCIWFIRSTPRIV + 0x1: /* card_reset */
-		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x1 (card_reset)\n",
-		      dev->name);
-		if (! capable(CAP_NET_ADMIN)) {
-			err = -EPERM;
-			break;
-		}
-		
-		printk(KERN_DEBUG "%s: Forcing card reset!\n", dev->name);
-		if(priv->card_reset_handler != NULL)
-			priv->card_reset_handler(priv);
+		/* 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);
 		break;
 
 	case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */
@@ -3361,7 +3486,11 @@
 	}
 	
 	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",
@@ -3376,106 +3505,14 @@
 	return err;
 }
 
-int
-orinoco_change_mtu(struct net_device *dev, int new_mtu)
-{
-	TRACE_ENTER(dev->name);
-
-	if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) )
-		return -EINVAL;
-
-	dev->mtu = new_mtu;
-
-	TRACE_EXIT(dev->name);
-
-	return 0;
-}
-
-static void
-__orinoco_set_multicast_list(struct net_device *dev)
-{
-	struct orinoco_private *priv = dev->priv;
-	hermes_t *hw = &priv->hw;
-	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))
-		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) ||
-	     (dev->mc_count > MAX_MULTICAST(priv)) ) {
-		promisc = 1;
-		mc_count = 0;
-	} else {
-		promisc = 0;
-		mc_count = dev->mc_count;
-	}
-
-	if (promisc != priv->promiscuous) {
-		err = hermes_write_wordrec(hw, USER_BAP,
-					   HERMES_RID_CNFPROMISCUOUSMODE,
-					   promisc);
-		if (err) {
-			printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
-			       dev->name, err);
-		} else 
-			priv->promiscuous = promisc;
-
-		mc_count = 0;
-	}
-
-	if (mc_count || priv->mc_count) {
-		struct dev_mc_list *p = dev->mc_list;
-		hermes_multicast_t mclist;
-		int i;
-
-		for (i = 0; i < mc_count; i++) {
-			/* Paranoia: */
-			if (! p)
-				BUG(); /* Multicast list shorter than mc_count */
-			if (p->dmi_addrlen != ETH_ALEN)
-				BUG(); /* Bad address size in multicast list */
-			
-			memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
-			p = p->next;
-		}
-		
-		if (p)
-			printk(KERN_WARNING "Multicast list is longer than mc_count\n");
-
-		err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES,
-				       HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN),
-				       &mclist);
-		if (err)
-			printk(KERN_ERR "%s: Error %d setting multicast list.\n",
-			       dev->name, err);
-		else
-			priv->mc_count = mc_count;
-	}
-
-	/* Since we can set the promiscuous flag when it wasn't asked
-	   for, make sure the net_device knows about it. */
-	if (priv->promiscuous)
-		dev->flags |= IFF_PROMISC;
-	else
-		dev->flags &= ~IFF_PROMISC;
-
-	TRACE_EXIT(dev->name);
-}
-
-/*
- * procfs stuff
- */
+/********************************************************************/
+/* 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
@@ -3485,7 +3522,6 @@
  * function returns 1, otherwise 0.
  */
 static int 
-
 shift_buffer(char *buffer, int requested_offset, int requested_len,
 	     int *total, int *slop, char **buf)
 {
@@ -3544,180 +3580,116 @@
 	return return_len;
 }
 
-static int
-orinoco_proc_get_hermes_regs(char *page, char **start, off_t requested_offset,
-			   int requested_len, int *eof, void *data)
-{
-	struct orinoco_private *priv = (struct orinoco_private *)data;
-	struct net_device *dev = &priv->ndev;
-	hermes_t *hw = &priv->hw;
-	char *buf;
-	int total = 0, slop = 0;
-
-	/* Hum, in this case hardware register are probably not readable... */
-	if (! netif_device_present(dev))
-		return -ENODEV;
-
-	buf = page;
-
-#define DHERMESREG(name) buf += sprintf(buf, "%-16s: %04x\n", #name, hermes_read_regn(hw, name))
-
-	DHERMESREG(CMD);
-	DHERMESREG(PARAM0);
-	DHERMESREG(PARAM1);
-	DHERMESREG(PARAM2);
-	DHERMESREG(STATUS);
-	DHERMESREG(RESP0);
-	DHERMESREG(RESP1);
-	DHERMESREG(RESP2);
-	DHERMESREG(INFOFID);
-	DHERMESREG(RXFID);
-	DHERMESREG(ALLOCFID);
-	DHERMESREG(TXCOMPLFID);
-	DHERMESREG(SELECT0);
-	DHERMESREG(OFFSET0);
-	DHERMESREG(SELECT1);
-	DHERMESREG(OFFSET1);
-	DHERMESREG(EVSTAT);
-	DHERMESREG(INTEN);
-	DHERMESREG(EVACK);
-	DHERMESREG(CONTROL);
-	DHERMESREG(SWSUPPORT0);
-	DHERMESREG(SWSUPPORT1);
-	DHERMESREG(SWSUPPORT2);
-	DHERMESREG(AUXPAGE);
-	DHERMESREG(AUXOFFSET);
-	DHERMESREG(AUXDATA);
-#undef DHERMESREG
-
-	shift_buffer(page, requested_offset, requested_len, &total,
-		     &slop, &buf);
-	return calc_start_len(page, start, requested_offset, requested_len,
-			      total, buf);
-}
-
 struct {
 	u16 rid;
 	char *name;
-	int minlen, maxlen;
 	int displaytype;
 #define DISPLAY_WORDS	0
 #define DISPLAY_BYTES	1
 #define DISPLAY_STRING	2
 #define DISPLAY_XSTRING	3
 } record_table[] = {
-#define CNF_WORDS(name) { HERMES_RID_CNF##name, #name, 0, LTV_BUF_SIZE, DISPLAY_WORDS }
-#define CNF_BYTES(name) { HERMES_RID_CNF##name, #name, 0, LTV_BUF_SIZE, DISPLAY_BYTES }
-#define CNF_STRING(name) { HERMES_RID_CNF##name, #name, 0, LTV_BUF_SIZE, DISPLAY_STRING }
-	CNF_WORDS(PORTTYPE),
-	CNF_BYTES(OWNMACADDR),
-	CNF_STRING(DESIREDSSID),
-	CNF_WORDS(OWNCHANNEL),
-	CNF_STRING(OWNSSID),
-	CNF_WORDS(OWNATIMWINDOW),
-	CNF_WORDS(SYSTEMSCALE),
-	CNF_WORDS(MAXDATALEN),
-	CNF_WORDS(PMENABLED),
-	CNF_WORDS(PMEPS),
-	CNF_WORDS(MULTICASTRECEIVE),
-	CNF_WORDS(MAXSLEEPDURATION),
-	CNF_WORDS(PMHOLDOVERDURATION),
-	CNF_STRING(OWNNAME),
-	CNF_WORDS(OWNDTIMPERIOD),
-	CNF_WORDS(MULTICASTPMBUFFERING),
-	CNF_WORDS(WEPENABLED_AGERE),
-	CNF_WORDS(MANDATORYBSSID_SYMBOL),
-	CNF_WORDS(WEPDEFAULTKEYID),
-	CNF_BYTES(DEFAULTKEY0),
-	CNF_BYTES(DEFAULTKEY1),
-	CNF_WORDS(MWOROBUST_AGERE),
-	CNF_BYTES(DEFAULTKEY2),
-	CNF_BYTES(DEFAULTKEY3),
-	CNF_WORDS(WEPFLAGS_INTERSIL),
-	CNF_WORDS(WEPKEYMAPPINGTABLE),
-	CNF_WORDS(AUTHENTICATION),
-	CNF_WORDS(MAXASSOCSTA),
-	CNF_WORDS(KEYLENGTH_SYMBOL),
-	CNF_WORDS(TXCONTROL),
-	CNF_WORDS(ROAMINGMODE),
-	CNF_WORDS(HOSTAUTHENTICATION),
-	CNF_WORDS(RCVCRCERROR),
-	CNF_WORDS(MMLIFE),
-	CNF_WORDS(ALTRETRYCOUNT),
-	CNF_WORDS(BEACONINT),
-	CNF_WORDS(APPCFINFO),
-	CNF_WORDS(STAPCFINFO),
-	CNF_WORDS(PRIORITYQUSAGE),
-	CNF_WORDS(TIMCTRL),
-	CNF_WORDS(THIRTY2TALLY),
-	CNF_WORDS(ENHSECURITY),
-	CNF_BYTES(GROUPADDRESSES),
-	CNF_WORDS(CREATEIBSS),
-	CNF_WORDS(FRAGMENTATIONTHRESHOLD),
-	CNF_WORDS(RTSTHRESHOLD),
-	CNF_WORDS(TXRATECONTROL),
-	CNF_WORDS(PROMISCUOUSMODE),
-	CNF_WORDS(BASICRATES_SYMBOL),
-	CNF_WORDS(PREAMBLE_SYMBOL),
-	CNF_WORDS(SHORTPREAMBLE),
-	CNF_BYTES(WEPKEYS_AGERE),
-	CNF_WORDS(EXCLUDELONGPREAMBLE),
-	CNF_WORDS(TXKEY_AGERE),
-	CNF_WORDS(AUTHENTICATIONRSPTO),
-	CNF_WORDS(BASICRATES),
-	CNF_WORDS(SUPPORTEDRATES),
-	CNF_WORDS(TICKTIME),
-	CNF_WORDS(SCANREQUEST),
-	CNF_WORDS(JOINREQUEST),
-	CNF_WORDS(AUTHENTICATESTATION),
-	CNF_WORDS(CHANNELINFOREQUEST),
-#undef CNF_WORDS
-#undef CNF_BYTES
-#undef CNF_STRING
-#define INF_WORDS(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_WORDS }
-#define INF_BYTES(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_BYTES }
-#define INF_STRING(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_STRING }
-#define INF_XSTRING(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_XSTRING }
-	INF_WORDS(MAXLOADTIME),
-	INF_WORDS(DOWNLOADBUFFER),
-	INF_WORDS(PRIID),
-	INF_WORDS(PRISUPRANGE),
-	INF_WORDS(CFIACTRANGES),
-	INF_WORDS(NICSERNUM),
-	INF_WORDS(NICID),
-	INF_WORDS(MFISUPRANGE),
-	INF_WORDS(CFISUPRANGE),
-	INF_WORDS(CHANNELLIST),
-	INF_WORDS(REGULATORYDOMAINS),
-	INF_WORDS(TEMPTYPE),
-/*  	INF_BYTES(CIS), */
-	INF_WORDS(STAID),
-	INF_STRING(CURRENTSSID),
-	INF_BYTES(CURRENTBSSID),
-	INF_WORDS(COMMSQUALITY),
-	INF_WORDS(CURRENTTXRATE),
-	INF_WORDS(CURRENTBEACONINTERVAL),
-	INF_WORDS(CURRENTSCALETHRESHOLDS),
-	INF_WORDS(PROTOCOLRSPTIME),
-	INF_WORDS(SHORTRETRYLIMIT),
-	INF_WORDS(LONGRETRYLIMIT),
-	INF_WORDS(MAXTRANSMITLIFETIME),
-	INF_WORDS(MAXRECEIVELIFETIME),
-	INF_WORDS(CFPOLLABLE),
-	INF_WORDS(AUTHENTICATIONALGORITHMS),
-	INF_WORDS(PRIVACYOPTIONIMPLEMENTED),
-	INF_BYTES(OWNMACADDR),
-	INF_WORDS(SCANRESULTSTABLE),
-	INF_WORDS(PHYTYPE),
-	INF_WORDS(CURRENTCHANNEL),
-	INF_WORDS(CURRENTPOWERSTATE),
-	INF_WORDS(CCAMODE),
-	INF_WORDS(SUPPORTEDDATARATES),
-	INF_BYTES(BUILDSEQ),
-	INF_XSTRING(FWID)
-#undef INF_WORDS
-#undef INF_BYTES
-#undef INF_STRING
+#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 NUM_RIDS ( sizeof(record_table) / sizeof(record_table[0]) )
 
@@ -3726,36 +3698,31 @@
 			   int requested_len, int *eof, void *data)
 {
 	struct orinoco_private *priv = (struct orinoco_private *)data;
-	struct net_device *dev = &priv->ndev;
+	struct net_device *dev = priv->ndev;
 	hermes_t *hw = &priv->hw;
-	char *buf;
+	char *buf = page;
 	int total = 0, slop = 0;
-	int i;
+	u8 *val8;
+	u16 *val16;
+	int i,j;
 	u16 length;
 	int err;
 
-	/* Hum, in this case hardware register are probably not readable... */
 	if (! netif_device_present(dev))
 		return -ENODEV;
-		
-	buf = page;
 
-	/* print out all the config RIDs */
+	val8 = kmalloc(PROC_LTV_SIZE + 2, GFP_KERNEL);
+	if (! val8)
+		return -ENOMEM;
+	val16 = (u16 *)val8;
+
 	for (i = 0; i < NUM_RIDS; i++) {
 		u16 rid = record_table[i].rid;
-		int minlen = record_table[i].minlen;
-		int maxlen = record_table[i].maxlen;
 		int len;
-		u8 *val8;
-		u16 *val16;
-		int j;
-
-		val8 = kmalloc(maxlen + 2, GFP_KERNEL);
-		if (! val8)
-			return -ENOMEM;
-		memset(val8, 0, maxlen + 2);
 
-		err = hermes_read_ltv(hw, USER_BAP, rid, maxlen,
+		memset(val8, 0, PROC_LTV_SIZE + 2);
+
+		err = hermes_read_ltv(hw, USER_BAP, rid, PROC_LTV_SIZE,
 				      &length, val8);
 		if (err) {
 			DEBUG(0, "Error %d reading RID 0x%04x\n", err, rid);
@@ -3767,7 +3734,7 @@
 
 		buf += sprintf(buf, "%-15s (0x%04x): length=%d (%d bytes)\tvalue=", record_table[i].name,
 			       rid, length, (length-1)*2);
-		len = min( (int)max(minlen, ((int)length-1)*2), maxlen);
+		len = min(((int)length-1)*2, PROC_LTV_SIZE);
 
 		switch (record_table[i].displaytype) {
 		case DISPLAY_WORDS:
@@ -3790,14 +3757,67 @@
 			val8[len] = '\0';
 			buf += sprintf(buf, "\"%s\"", (char *)&val16[1]);
 			break;
+
 		case DISPLAY_XSTRING:
-			
 			buf += sprintf(buf, "'%s'", (char *)val8);
 		}
 
 		buf += sprintf(buf, "\n");
 
-		kfree(val8);
+		if (shift_buffer(page, requested_offset, requested_len,
+				 &total, &slop, &buf))
+			break;
+
+		if ( (buf - page) > PROC_SAFE_SIZE )
+			break;
+	}
+
+	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))
@@ -3810,6 +3830,7 @@
 	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 */
@@ -3836,32 +3857,41 @@
 int
 orinoco_proc_dev_init(struct orinoco_private *priv)
 {
-	struct net_device *dev = &priv->ndev;
+	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 == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/hermes/%s.\n",  dev->name);
+	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;
 	}
 
-	priv->dir_regs = NULL;
-	priv->dir_regs = create_proc_read_entry("regs", S_IFREG | S_IRUGO,
-						priv->dir_dev, orinoco_proc_get_hermes_regs, priv);
-	if (priv->dir_regs == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/hermes/%s/regs.\n",  dev->name);
+#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;
 	}
 
-	priv->dir_recs = NULL;
-	priv->dir_recs = create_proc_read_entry("recs", S_IFREG | S_IRUGO,
-						priv->dir_dev, orinoco_proc_get_hermes_recs, priv);
-	if (priv->dir_recs == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/hermes/%s/recs.\n",  dev->name);
+	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:
@@ -3872,24 +3902,19 @@
 void
 orinoco_proc_dev_cleanup(struct orinoco_private *priv)
 {
-	struct net_device *dev = &priv->ndev;
+	struct net_device *dev = priv->ndev;
 
-	TRACE_ENTER(priv->ndev.name);
+	TRACE_ENTER(priv->ndev->name);
 
-	if (priv->dir_regs) {
-		remove_proc_entry("regs", priv->dir_dev);
-		priv->dir_regs = NULL;
-	}		
-	if (priv->dir_recs) {
-		remove_proc_entry("recs", priv->dir_dev);
-		priv->dir_recs = NULL;
-	}		
 	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);
+	TRACE_EXIT(priv->ndev->name);
 }
 
 static void
@@ -3905,45 +3930,51 @@
 	TRACE_EXIT("orinoco");
 }
 
-int
-orinoco_setup(struct orinoco_private* priv)
+struct net_device *alloc_orinocodev(int sizeof_card)
 {
-	struct net_device *dev = &priv->ndev;;
-
-	spin_lock_init(&priv->lock);
+	struct net_device *dev;
+	struct orinoco_private *priv;
 
-	/* Set up the net_device */
-	ether_setup(dev);
-	dev->priv = priv;
+	TRACE_ENTER("orinoco");
+	dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card);
+	priv = (struct orinoco_private *)dev->priv;
+	priv->ndev = dev;
+	if (sizeof_card)
+		priv->card = (void *)((unsigned long)dev->priv + sizeof(struct orinoco_private));
+	else
+		priv->card = NULL;
 
-	/* Setup up default routines */
-	priv->card_reset_handler = NULL;	/* Caller may override */
+	/* Setup / override net_device fields */
 	dev->init = orinoco_init;
-	dev->open = NULL;		/* Caller *must* override */
-	dev->stop = NULL;
 	dev->hard_start_xmit = orinoco_xmit;
 	dev->tx_timeout = orinoco_tx_timeout;
 	dev->watchdog_timeo = HZ; /* 1 second timeout */
-
 	dev->get_stats = orinoco_get_stats;
 	dev->get_wireless_stats = orinoco_get_wireless_stats;
-
 	dev->do_ioctl = orinoco_ioctl;
-
 	dev->change_mtu = orinoco_change_mtu;
 	dev->set_multicast_list = orinoco_set_multicast_list;
 
-	netif_stop_queue(dev);
+	dev->open = NULL;		/* Caller *must* override  these */
+	dev->stop = NULL;
+
+	/* Setup the private structure */
+
+	spin_lock_init(&priv->lock);
+	priv->hard_reset = NULL;	/* Caller may override */
+	
+	TRACE_EXIT("orinoco");
+	return dev;
 
-	return 0;
 }
 
-#ifdef ORINOCO_DEBUG
-EXPORT_SYMBOL(orinoco_debug);
-#endif
+/********************************************************************/
+/* module bookkeeping                                               */
+/********************************************************************/
+
+EXPORT_SYMBOL(alloc_orinocodev);
 EXPORT_SYMBOL(orinoco_shutdown);
 EXPORT_SYMBOL(orinoco_reset);
-EXPORT_SYMBOL(orinoco_setup);
 EXPORT_SYMBOL(orinoco_proc_dev_init);
 EXPORT_SYMBOL(orinoco_proc_dev_cleanup);
 EXPORT_SYMBOL(orinoco_interrupt);

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