Merge branch 'from-linus' into upstream
diff --git a/Documentation/networking/README.ipw2200 b/Documentation/networking/README.ipw2200
index acb30c5..4f2a40f 100644
--- a/Documentation/networking/README.ipw2200
+++ b/Documentation/networking/README.ipw2200
@@ -14,8 +14,8 @@
 
 README.ipw2200
 
-Version: 1.0.8
-Date   : October 20, 2005
+Version: 1.1.2
+Date   : March 30, 2006
 
 
 Index
@@ -103,7 +103,7 @@
 
 1.1. Overview of Features
 -----------------------------------------------
-The current release (1.0.8) supports the following features:
+The current release (1.1.2) supports the following features:
 
 + BSS mode (Infrastructure, Managed)
 + IBSS mode (Ad-Hoc)
@@ -247,8 +247,8 @@
 % cat /sys/bus/pci/drivers/ipw2200/debug_level
 
 Will report the current debug level of the driver's logging subsystem 
-(only available if CONFIG_IPW_DEBUG was configured when the driver was 
-built).
+(only available if CONFIG_IPW2200_DEBUG was configured when the driver
+was built).
 
 You can set the debug level via:
 
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index e0874cb..d7691c4 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -235,7 +235,35 @@
 	  promiscuous mode via the Wireless Tool's Monitor mode.  While in this
 	  mode, no packets can be sent.
 
-config IPW_QOS
+config IPW2200_RADIOTAP
+	bool "Enable radiotap format 802.11 raw packet support"
+	depends on IPW2200_MONITOR
+
+config IPW2200_PROMISCUOUS
+	bool "Enable creation of a RF radiotap promiscuous interface"
+	depends on IPW2200_MONITOR
+	select IPW2200_RADIOTAP
+	---help---
+          Enables the creation of a second interface prefixed 'rtap'. 
+          This second interface will provide every received in radiotap
+	  format.
+
+          This is useful for performing wireless network analysis while
+          maintaining an active association.
+
+          Example usage:
+
+            % modprobe ipw2200 rtap_iface=1
+            % ifconfig rtap0 up
+            % tethereal -i rtap0
+
+          If you do not specify 'rtap_iface=1' as a module parameter then 
+          the rtap interface will not be created and you will need to turn 
+          it on via sysfs:
+	
+            % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface
+
+config IPW2200_QOS
         bool "Enable QoS support"
         depends on IPW2200 && EXPERIMENTAL
 
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 00764dd..7f2dacf 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -47,6 +47,7 @@
 #include <linux/ioport.h>
 #include <linux/pci.h>
 #include <asm/uaccess.h>
+#include <net/ieee80211.h>
 
 #include "airo.h"
 
@@ -467,6 +468,8 @@
 #define RID_ECHOTEST_RESULTS 0xFF71
 #define RID_BSSLISTFIRST 0xFF72
 #define RID_BSSLISTNEXT  0xFF73
+#define RID_WPA_BSSLISTFIRST 0xFF74
+#define RID_WPA_BSSLISTNEXT  0xFF75
 
 typedef struct {
 	u16 cmd;
@@ -739,6 +742,14 @@
 	u16 extSoftCap;
 } CapabilityRid;
 
+
+/* Only present on firmware >= 5.30.17 */
+typedef struct {
+  u16 unknown[4];
+  u8 fixed[12]; /* WLAN management frame */
+  u8 iep[624];
+} BSSListRidExtra;
+
 typedef struct {
   u16 len;
   u16 index; /* First is 0 and 0xffff means end of list */
@@ -767,6 +778,9 @@
   } fh;
   u16 dsChannel;
   u16 atimWindow;
+
+  /* Only present on firmware >= 5.30.17 */
+  BSSListRidExtra extra;
 } BSSListRid;
 
 typedef struct {
@@ -1140,8 +1154,6 @@
 	char defindex; // Used with auto wep
 	struct proc_dir_entry *proc_entry;
         spinlock_t aux_lock;
-        unsigned long flags;
-#define FLAG_PROMISC	8	/* IFF_PROMISC 0x100 - include/linux/if.h */
 #define FLAG_RADIO_OFF	0	/* User disabling of MAC */
 #define FLAG_RADIO_DOWN	1	/* ifup/ifdown disabling of MAC */
 #define FLAG_RADIO_MASK 0x03
@@ -1151,6 +1163,7 @@
 #define FLAG_UPDATE_MULTI 5
 #define FLAG_UPDATE_UNI 6
 #define FLAG_802_11	7
+#define FLAG_PROMISC	8	/* IFF_PROMISC 0x100 - include/linux/if.h */
 #define FLAG_PENDING_XMIT 9
 #define FLAG_PENDING_XMIT11 10
 #define FLAG_MPI	11
@@ -1158,17 +1171,19 @@
 #define FLAG_COMMIT	13
 #define FLAG_RESET	14
 #define FLAG_FLASHING	15
-#define JOB_MASK	0x2ff0000
-#define JOB_DIE		16
-#define JOB_XMIT	17
-#define JOB_XMIT11	18
-#define JOB_STATS	19
-#define JOB_PROMISC	20
-#define JOB_MIC		21
-#define JOB_EVENT	22
-#define JOB_AUTOWEP	23
-#define JOB_WSTATS	24
-#define JOB_SCAN_RESULTS  25
+#define FLAG_WPA_CAPABLE	16
+	unsigned long flags;
+#define JOB_DIE	0
+#define JOB_XMIT	1
+#define JOB_XMIT11	2
+#define JOB_STATS	3
+#define JOB_PROMISC	4
+#define JOB_MIC	5
+#define JOB_EVENT	6
+#define JOB_AUTOWEP	7
+#define JOB_WSTATS	8
+#define JOB_SCAN_RESULTS  9
+	unsigned long jobs;
 	int (*bap_read)(struct airo_info*, u16 *pu16Dst, int bytelen,
 			int whichbap);
 	unsigned short *flash;
@@ -1208,6 +1223,11 @@
 #define	PCI_SHARED_LEN		2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
 	char			proc_name[IFNAMSIZ];
 
+	/* WPA-related stuff */
+	unsigned int bssListFirst;
+	unsigned int bssListNext;
+	unsigned int bssListRidLen;
+
 	struct list_head network_list;
 	struct list_head network_free_list;
 	BSSListElement *networks;
@@ -1264,7 +1284,7 @@
 {
 	MICRid mic_rid;
 
-	clear_bit(JOB_MIC, &ai->flags);
+	clear_bit(JOB_MIC, &ai->jobs);
 	PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
 	up(&ai->sem);
 
@@ -1705,24 +1725,24 @@
 static int readBSSListRid(struct airo_info *ai, int first,
 		      BSSListRid *list) {
 	int rc;
-			Cmd cmd;
-			Resp rsp;
+	Cmd cmd;
+	Resp rsp;
 
 	if (first == 1) {
-			if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
-			memset(&cmd, 0, sizeof(cmd));
-			cmd.cmd=CMD_LISTBSS;
-			if (down_interruptible(&ai->sem))
-				return -ERESTARTSYS;
-			issuecommand(ai, &cmd, &rsp);
-			up(&ai->sem);
-			/* Let the command take effect */
-			ai->task = current;
-			ssleep(3);
-			ai->task = NULL;
-		}
-	rc = PC4500_readrid(ai, first ? RID_BSSLISTFIRST : RID_BSSLISTNEXT,
-			    list, sizeof(*list), 1);
+		if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.cmd=CMD_LISTBSS;
+		if (down_interruptible(&ai->sem))
+			return -ERESTARTSYS;
+		issuecommand(ai, &cmd, &rsp);
+		up(&ai->sem);
+		/* Let the command take effect */
+		ai->task = current;
+		ssleep(3);
+		ai->task = NULL;
+	}
+	rc = PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
+			    list, ai->bssListRidLen, 1);
 
 	list->len = le16_to_cpu(list->len);
 	list->index = le16_to_cpu(list->index);
@@ -2112,7 +2132,7 @@
 	int fid = priv->xmit.fid;
 	u32 *fids = priv->fids;
 
-	clear_bit(JOB_XMIT, &priv->flags);
+	clear_bit(JOB_XMIT, &priv->jobs);
 	clear_bit(FLAG_PENDING_XMIT, &priv->flags);
 	status = transmit_802_3_packet (priv, fids[fid], skb->data);
 	up(&priv->sem);
@@ -2162,7 +2182,7 @@
 	if (down_trylock(&priv->sem) != 0) {
 		set_bit(FLAG_PENDING_XMIT, &priv->flags);
 		netif_stop_queue(dev);
-		set_bit(JOB_XMIT, &priv->flags);
+		set_bit(JOB_XMIT, &priv->jobs);
 		wake_up_interruptible(&priv->thr_wait);
 	} else
 		airo_end_xmit(dev);
@@ -2177,7 +2197,7 @@
 	int fid = priv->xmit11.fid;
 	u32 *fids = priv->fids;
 
-	clear_bit(JOB_XMIT11, &priv->flags);
+	clear_bit(JOB_XMIT11, &priv->jobs);
 	clear_bit(FLAG_PENDING_XMIT11, &priv->flags);
 	status = transmit_802_11_packet (priv, fids[fid], skb->data);
 	up(&priv->sem);
@@ -2233,7 +2253,7 @@
 	if (down_trylock(&priv->sem) != 0) {
 		set_bit(FLAG_PENDING_XMIT11, &priv->flags);
 		netif_stop_queue(dev);
-		set_bit(JOB_XMIT11, &priv->flags);
+		set_bit(JOB_XMIT11, &priv->jobs);
 		wake_up_interruptible(&priv->thr_wait);
 	} else
 		airo_end_xmit11(dev);
@@ -2244,7 +2264,7 @@
 	StatsRid stats_rid;
 	u32 *vals = stats_rid.vals;
 
-	clear_bit(JOB_STATS, &ai->flags);
+	clear_bit(JOB_STATS, &ai->jobs);
 	if (ai->power.event) {
 		up(&ai->sem);
 		return;
@@ -2272,10 +2292,10 @@
 {
 	struct airo_info *local =  dev->priv;
 
-	if (!test_bit(JOB_STATS, &local->flags)) {
+	if (!test_bit(JOB_STATS, &local->jobs)) {
 		/* Get stats out of the card if available */
 		if (down_trylock(&local->sem) != 0) {
-			set_bit(JOB_STATS, &local->flags);
+			set_bit(JOB_STATS, &local->jobs);
 			wake_up_interruptible(&local->thr_wait);
 		} else
 			airo_read_stats(local);
@@ -2290,7 +2310,7 @@
 
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.cmd=CMD_SETMODE;
-	clear_bit(JOB_PROMISC, &ai->flags);
+	clear_bit(JOB_PROMISC, &ai->jobs);
 	cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
 	issuecommand(ai, &cmd, &rsp);
 	up(&ai->sem);
@@ -2302,7 +2322,7 @@
 	if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
 		change_bit(FLAG_PROMISC, &ai->flags);
 		if (down_trylock(&ai->sem) != 0) {
-			set_bit(JOB_PROMISC, &ai->flags);
+			set_bit(JOB_PROMISC, &ai->jobs);
 			wake_up_interruptible(&ai->thr_wait);
 		} else
 			airo_set_promisc(ai);
@@ -2380,7 +2400,7 @@
 		}
 		clear_bit(FLAG_REGISTERED, &ai->flags);
 	}
-	set_bit(JOB_DIE, &ai->flags);
+	set_bit(JOB_DIE, &ai->jobs);
 	kill_proc(ai->thr_pid, SIGTERM, 1);
 	wait_for_completion(&ai->thr_exited);
 
@@ -2701,14 +2721,14 @@
 	return 0;
 }
 
-#define MAX_NETWORK_COUNT	64
+#define AIRO_MAX_NETWORK_COUNT	64
 static int airo_networks_allocate(struct airo_info *ai)
 {
 	if (ai->networks)
 		return 0;
 
 	ai->networks =
-	    kzalloc(MAX_NETWORK_COUNT * sizeof(BSSListElement),
+	    kzalloc(AIRO_MAX_NETWORK_COUNT * sizeof(BSSListElement),
 		    GFP_KERNEL);
 	if (!ai->networks) {
 		airo_print_warn(ai->dev->name, "Out of memory allocating beacons");
@@ -2732,11 +2752,33 @@
 
 	INIT_LIST_HEAD(&ai->network_free_list);
 	INIT_LIST_HEAD(&ai->network_list);
-	for (i = 0; i < MAX_NETWORK_COUNT; i++)
+	for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
 		list_add_tail(&ai->networks[i].list,
 			      &ai->network_free_list);
 }
 
+static int airo_test_wpa_capable(struct airo_info *ai)
+{
+	int status;
+	CapabilityRid cap_rid;
+	const char *name = ai->dev->name;
+
+	status = readCapabilityRid(ai, &cap_rid, 1);
+	if (status != SUCCESS) return 0;
+
+	/* Only firmware versions 5.30.17 or better can do WPA */
+	if ((cap_rid.softVer > 0x530)
+	  || ((cap_rid.softVer == 0x530) && (cap_rid.softSubVer >= 0x17))) {
+		airo_print_info(name, "WPA is supported.");
+		return 1;
+	}
+
+	/* No WPA support */
+	airo_print_info(name, "WPA unsupported (only firmware versions 5.30.17"
+		" and greater support WPA.  Detected %s)", cap_rid.prodVer);
+	return 0;
+}
+
 static struct net_device *_init_airo_card( unsigned short irq, int port,
 					   int is_pcmcia, struct pci_dev *pci,
 					   struct device *dmdev )
@@ -2759,6 +2801,7 @@
 	ai = dev->priv;
 	ai->wifidev = NULL;
 	ai->flags = 0;
+	ai->jobs = 0;
 	ai->dev = dev;
 	if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
 		airo_print_dbg(dev->name, "Found an MPI350 card");
@@ -2838,6 +2881,18 @@
 		set_bit(FLAG_FLASHING, &ai->flags);
 	}
 
+	/* Test for WPA support */
+	if (airo_test_wpa_capable(ai)) {
+		set_bit(FLAG_WPA_CAPABLE, &ai->flags);
+		ai->bssListFirst = RID_WPA_BSSLISTFIRST;
+		ai->bssListNext = RID_WPA_BSSLISTNEXT;
+		ai->bssListRidLen = sizeof(BSSListRid);
+	} else {
+		ai->bssListFirst = RID_BSSLISTFIRST;
+		ai->bssListNext = RID_BSSLISTNEXT;
+		ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
+	}
+
 	rc = register_netdev(dev);
 	if (rc) {
 		airo_print_err(dev->name, "Couldn't register_netdev");
@@ -2875,7 +2930,7 @@
 err_out_unlink:
 	del_airo_dev(dev);
 err_out_thr:
-	set_bit(JOB_DIE, &ai->flags);
+	set_bit(JOB_DIE, &ai->jobs);
 	kill_proc(ai->thr_pid, SIGTERM, 1);
 	wait_for_completion(&ai->thr_exited);
 err_out_free:
@@ -2933,7 +2988,7 @@
 	union iwreq_data wrqu;
 	StatusRid status_rid;
 
-	clear_bit(JOB_EVENT, &ai->flags);
+	clear_bit(JOB_EVENT, &ai->jobs);
 	PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
 	up(&ai->sem);
 	wrqu.data.length = 0;
@@ -2947,7 +3002,7 @@
 
 static void airo_process_scan_results (struct airo_info *ai) {
 	union iwreq_data	wrqu;
-	BSSListRid BSSList;
+	BSSListRid bss;
 	int rc;
 	BSSListElement * loop_net;
 	BSSListElement * tmp_net;
@@ -2960,15 +3015,15 @@
 	}
 
 	/* Try to read the first entry of the scan result */
-	rc = PC4500_readrid(ai, RID_BSSLISTFIRST, &BSSList, sizeof(BSSList), 0);
-	if((rc) || (BSSList.index == 0xffff)) {
+	rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
+	if((rc) || (bss.index == 0xffff)) {
 		/* No scan results */
 		goto out;
 	}
 
 	/* Read and parse all entries */
 	tmp_net = NULL;
-	while((!rc) && (BSSList.index != 0xffff)) {
+	while((!rc) && (bss.index != 0xffff)) {
 		/* Grab a network off the free list */
 		if (!list_empty(&ai->network_free_list)) {
 			tmp_net = list_entry(ai->network_free_list.next,
@@ -2977,19 +3032,19 @@
 		}
 
 		if (tmp_net != NULL) {
-			memcpy(tmp_net, &BSSList, sizeof(tmp_net->bss));
+			memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
 			list_add_tail(&tmp_net->list, &ai->network_list);
 			tmp_net = NULL;
 		}
 
 		/* Read next entry */
-		rc = PC4500_readrid(ai, RID_BSSLISTNEXT,
-				    &BSSList, sizeof(BSSList), 0);
+		rc = PC4500_readrid(ai, ai->bssListNext,
+				    &bss, ai->bssListRidLen, 0);
 	}
 
 out:
 	ai->scan_timeout = 0;
-	clear_bit(JOB_SCAN_RESULTS, &ai->flags);
+	clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
 	up(&ai->sem);
 
 	/* Send an empty event to user space.
@@ -3019,10 +3074,10 @@
 		/* make swsusp happy with our thread */
 		try_to_freeze();
 
-		if (test_bit(JOB_DIE, &ai->flags))
+		if (test_bit(JOB_DIE, &ai->jobs))
 			break;
 
-		if (ai->flags & JOB_MASK) {
+		if (ai->jobs) {
 			locked = down_interruptible(&ai->sem);
 		} else {
 			wait_queue_t wait;
@@ -3031,16 +3086,16 @@
 			add_wait_queue(&ai->thr_wait, &wait);
 			for (;;) {
 				set_current_state(TASK_INTERRUPTIBLE);
-				if (ai->flags & JOB_MASK)
+				if (ai->jobs)
 					break;
 				if (ai->expires || ai->scan_timeout) {
 					if (ai->scan_timeout &&
 							time_after_eq(jiffies,ai->scan_timeout)){
-						set_bit(JOB_SCAN_RESULTS,&ai->flags);
+						set_bit(JOB_SCAN_RESULTS, &ai->jobs);
 						break;
 					} else if (ai->expires &&
 							time_after_eq(jiffies,ai->expires)){
-						set_bit(JOB_AUTOWEP,&ai->flags);
+						set_bit(JOB_AUTOWEP, &ai->jobs);
 						break;
 					}
 					if (!signal_pending(current)) {
@@ -3069,7 +3124,7 @@
 		if (locked)
 			continue;
 
-		if (test_bit(JOB_DIE, &ai->flags)) {
+		if (test_bit(JOB_DIE, &ai->jobs)) {
 			up(&ai->sem);
 			break;
 		}
@@ -3079,23 +3134,23 @@
 			continue;
 		}
 
-		if (test_bit(JOB_XMIT, &ai->flags))
+		if (test_bit(JOB_XMIT, &ai->jobs))
 			airo_end_xmit(dev);
-		else if (test_bit(JOB_XMIT11, &ai->flags))
+		else if (test_bit(JOB_XMIT11, &ai->jobs))
 			airo_end_xmit11(dev);
-		else if (test_bit(JOB_STATS, &ai->flags))
+		else if (test_bit(JOB_STATS, &ai->jobs))
 			airo_read_stats(ai);
-		else if (test_bit(JOB_WSTATS, &ai->flags))
+		else if (test_bit(JOB_WSTATS, &ai->jobs))
 			airo_read_wireless_stats(ai);
-		else if (test_bit(JOB_PROMISC, &ai->flags))
+		else if (test_bit(JOB_PROMISC, &ai->jobs))
 			airo_set_promisc(ai);
-		else if (test_bit(JOB_MIC, &ai->flags))
+		else if (test_bit(JOB_MIC, &ai->jobs))
 			micinit(ai);
-		else if (test_bit(JOB_EVENT, &ai->flags))
+		else if (test_bit(JOB_EVENT, &ai->jobs))
 			airo_send_event(dev);
-		else if (test_bit(JOB_AUTOWEP, &ai->flags))
+		else if (test_bit(JOB_AUTOWEP, &ai->jobs))
 			timer_func(dev);
-		else if (test_bit(JOB_SCAN_RESULTS, &ai->flags))
+		else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
 			airo_process_scan_results(ai);
 		else  /* Shouldn't get here, but we make sure to unlock */
 			up(&ai->sem);
@@ -3133,7 +3188,7 @@
 		if ( status & EV_MIC ) {
 			OUT4500( apriv, EVACK, EV_MIC );
 			if (test_bit(FLAG_MIC_CAPABLE, &apriv->flags)) {
-				set_bit(JOB_MIC, &apriv->flags);
+				set_bit(JOB_MIC, &apriv->jobs);
 				wake_up_interruptible(&apriv->thr_wait);
 			}
 		}
@@ -3187,7 +3242,7 @@
 				set_bit(FLAG_UPDATE_MULTI, &apriv->flags);
 
 				if (down_trylock(&apriv->sem) != 0) {
-					set_bit(JOB_EVENT, &apriv->flags);
+					set_bit(JOB_EVENT, &apriv->jobs);
 					wake_up_interruptible(&apriv->thr_wait);
 				} else
 					airo_send_event(dev);
@@ -5485,7 +5540,7 @@
 	up(&apriv->sem);
 
 /* Schedule check to see if the change worked */
-	clear_bit(JOB_AUTOWEP, &apriv->flags);
+	clear_bit(JOB_AUTOWEP, &apriv->jobs);
 	apriv->expires = RUN_AT(HZ*3);
 }
 
@@ -6876,7 +6931,7 @@
 	}
 	range->num_txpower = i;
 	range->txpower_capa = IW_TXPOW_MWATT;
-	range->we_version_source = 12;
+	range->we_version_source = 19;
 	range->we_version_compiled = WIRELESS_EXT;
 	range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
 	range->retry_flags = IW_RETRY_LIMIT;
@@ -7152,6 +7207,7 @@
 	u16			capabilities;
 	char *			current_val;	/* For rates */
 	int			i;
+	char *		buf;
 
 	/* First entry *MUST* be the AP MAC address */
 	iwe.cmd = SIOCGIWAP;
@@ -7238,8 +7294,69 @@
 	if((current_val - current_ev) > IW_EV_LCP_LEN)
 		current_ev = current_val;
 
-	/* The other data in the scan result are not really
-	 * interesting, so for now drop it - Jean II */
+	/* Beacon interval */
+	buf = kmalloc(30, GFP_KERNEL);
+	if (buf) {
+		iwe.cmd = IWEVCUSTOM;
+		sprintf(buf, "bcn_int=%d", bss->beaconInterval);
+		iwe.u.data.length = strlen(buf);
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
+		kfree(buf);
+	}
+
+	/* Put WPA/RSN Information Elements into the event stream */
+	if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
+		unsigned int num_null_ies = 0;
+		u16 length = sizeof (bss->extra.iep);
+		struct ieee80211_info_element *info_element =
+			(struct ieee80211_info_element *) &bss->extra.iep;
+
+		while ((length >= sizeof(*info_element)) && (num_null_ies < 2)) {
+			if (sizeof(*info_element) + info_element->len > length) {
+				/* Invalid element, don't continue parsing IE */
+				break;
+			}
+
+			switch (info_element->id) {
+			case MFIE_TYPE_SSID:
+				/* Two zero-length SSID elements
+				 * mean we're done parsing elements */
+				if (!info_element->len)
+					num_null_ies++;
+				break;
+
+			case MFIE_TYPE_GENERIC:
+				if (info_element->len >= 4 &&
+				    info_element->data[0] == 0x00 &&
+				    info_element->data[1] == 0x50 &&
+				    info_element->data[2] == 0xf2 &&
+				    info_element->data[3] == 0x01) {
+					iwe.cmd = IWEVGENIE;
+					iwe.u.data.length = min(info_element->len + 2,
+								  MAX_WPA_IE_LEN);
+					current_ev = iwe_stream_add_point(current_ev, end_buf,
+							&iwe, (char *) info_element);
+				}
+				break;
+
+			case MFIE_TYPE_RSN:
+				iwe.cmd = IWEVGENIE;
+				iwe.u.data.length = min(info_element->len + 2,
+							  MAX_WPA_IE_LEN);
+				current_ev = iwe_stream_add_point(current_ev, end_buf,
+						&iwe, (char *) info_element);
+				break;
+
+			default:
+				break;
+			}
+
+			length -= sizeof(*info_element) + info_element->len;
+			info_element =
+			    (struct ieee80211_info_element *)&info_element->
+			    data[info_element->len];
+		}
+	}
 	return current_ev;
 }
 
@@ -7521,7 +7638,7 @@
 	u32 *vals = stats_rid.vals;
 
 	/* Get stats out of the card */
-	clear_bit(JOB_WSTATS, &local->flags);
+	clear_bit(JOB_WSTATS, &local->jobs);
 	if (local->power.event) {
 		up(&local->sem);
 		return;
@@ -7565,10 +7682,10 @@
 {
 	struct airo_info *local =  dev->priv;
 
-	if (!test_bit(JOB_WSTATS, &local->flags)) {
+	if (!test_bit(JOB_WSTATS, &local->jobs)) {
 		/* Get stats out of the card if available */
 		if (down_trylock(&local->sem) != 0) {
-			set_bit(JOB_WSTATS, &local->flags);
+			set_bit(JOB_WSTATS, &local->jobs);
 			wake_up_interruptible(&local->thr_wait);
 		} else
 			airo_read_wireless_stats(local);
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h
index 2e83083..e66fdb1 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx.h
@@ -645,7 +645,6 @@
 	unsigned int irq;
 
 	void __iomem *mmio_addr;
-	unsigned int mmio_len;
 
 	/* Do not use the lock directly. Use the bcm43xx_lock* helper
 	 * functions, to be MMIO-safe. */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
index 35a4fcb..7497fb1 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
@@ -92,7 +92,7 @@
 	fappend("subsystem_vendor: 0x%04x   subsystem_device: 0x%04x\n",
 		pci_dev->subsystem_vendor, pci_dev->subsystem_device);
 	fappend("IRQ: %d\n", bcm->irq);
-	fappend("mmio_addr: 0x%p   mmio_len: %u\n", bcm->mmio_addr, bcm->mmio_len);
+	fappend("mmio_addr: 0x%p\n", bcm->mmio_addr);
 	fappend("chip_id: 0x%04x   chip_rev: 0x%02x\n", bcm->chip_id, bcm->chip_rev);
 	if ((bcm->core_80211[0].rev >= 3) && (bcm43xx_read32(bcm, 0x0158) & (1 << 16)))
 		fappend("Radio disabled by hardware!\n");
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index 9a06e61..1985286 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -3288,8 +3288,7 @@
 
 	bcm43xx_chipset_detach(bcm);
 	/* Do _not_ access the chip, after it is detached. */
-	iounmap(bcm->mmio_addr);
-	
+	pci_iounmap(pci_dev, bcm->mmio_addr);
 	pci_release_regions(pci_dev);
 	pci_disable_device(pci_dev);
 
@@ -3379,40 +3378,26 @@
 	struct net_device *net_dev = bcm->net_dev;
 	int err;
 	int i;
-	unsigned long mmio_start, mmio_flags, mmio_len;
 	u32 coremask;
 
 	err = pci_enable_device(pci_dev);
 	if (err) {
-		printk(KERN_ERR PFX "unable to wake up pci device (%i)\n", err);
+		printk(KERN_ERR PFX "pci_enable_device() failed\n");
 		goto out;
 	}
-	mmio_start = pci_resource_start(pci_dev, 0);
-	mmio_flags = pci_resource_flags(pci_dev, 0);
-	mmio_len = pci_resource_len(pci_dev, 0);
-	if (!(mmio_flags & IORESOURCE_MEM)) {
-		printk(KERN_ERR PFX
-		       "%s, region #0 not an MMIO resource, aborting\n",
-		       pci_name(pci_dev));
-		err = -ENODEV;
-		goto err_pci_disable;
-	}
 	err = pci_request_regions(pci_dev, KBUILD_MODNAME);
 	if (err) {
-		printk(KERN_ERR PFX
-		       "could not access PCI resources (%i)\n", err);
+		printk(KERN_ERR PFX "pci_request_regions() failed\n");
 		goto err_pci_disable;
 	}
 	/* enable PCI bus-mastering */
 	pci_set_master(pci_dev);
-	bcm->mmio_addr = ioremap(mmio_start, mmio_len);
+	bcm->mmio_addr = pci_iomap(pci_dev, 0, ~0UL);
 	if (!bcm->mmio_addr) {
-		printk(KERN_ERR PFX "%s: cannot remap MMIO, aborting\n",
-		       pci_name(pci_dev));
+		printk(KERN_ERR PFX "pci_iomap() failed\n");
 		err = -EIO;
 		goto err_pci_release;
 	}
-	bcm->mmio_len = mmio_len;
 	net_dev->base_addr = (unsigned long)bcm->mmio_addr;
 
 	bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_VENDOR_ID,
@@ -3505,7 +3490,7 @@
 err_chipset_detach:
 	bcm43xx_chipset_detach(bcm);
 err_iounmap:
-	iounmap(bcm->mmio_addr);
+	pci_iounmap(pci_dev, bcm->mmio_addr);
 err_pci_release:
 	pci_release_regions(pci_dev);
 err_pci_disable:
diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c
index 346c6fe..2aa2f38 100644
--- a/drivers/net/wireless/hermes.c
+++ b/drivers/net/wireless/hermes.c
@@ -121,12 +121,6 @@
 	hw->iobase = address;
 	hw->reg_spacing = reg_spacing;
 	hw->inten = 0x0;
-
-#ifdef HERMES_DEBUG_BUFFER
-	hw->dbufp = 0;
-	memset(&hw->dbuf, 0xff, sizeof(hw->dbuf));
-	memset(&hw->profile, 0, sizeof(hw->profile));
-#endif
 }
 
 int hermes_init(hermes_t *hw)
@@ -347,19 +341,6 @@
 		reg = hermes_read_reg(hw, oreg);
 	}
 
-#ifdef HERMES_DEBUG_BUFFER
-	hw->profile[HERMES_BAP_BUSY_TIMEOUT - k]++;
-
-	if (k < HERMES_BAP_BUSY_TIMEOUT) {
-		struct hermes_debug_entry *e = 
-			&hw->dbuf[(hw->dbufp++) % HERMES_DEBUG_BUFSIZE];
-		e->bap = bap;
-		e->id = id;
-		e->offset = offset;
-		e->cycles = HERMES_BAP_BUSY_TIMEOUT - k;
-	}
-#endif
-
 	if (reg & HERMES_OFFSET_BUSY)
 		return -ETIMEDOUT;
 
@@ -419,8 +400,7 @@
 }
 
 /* Write a block of data to the chip's buffer, via the
- * BAP. Synchronization/serialization is the caller's problem. len
- * must be even.
+ * BAP. Synchronization/serialization is the caller's problem.
  *
  * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware
  */
@@ -430,7 +410,7 @@
 	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
 	int err = 0;
 
-	if ( (len < 0) || (len % 2) )
+	if (len < 0)
 		return -EINVAL;
 
 	err = hermes_bap_seek(hw, bap, id, offset);
@@ -438,49 +418,12 @@
 		goto out;
 	
 	/* Actually do the transfer */
-	hermes_write_words(hw, dreg, buf, len/2);
+	hermes_write_bytes(hw, dreg, buf, len);
 
  out:	
 	return err;
 }
 
-/* Write a block of data to the chip's buffer with padding if
- * neccessary, via the BAP. Synchronization/serialization is the
- * caller's problem. len must be even.
- *
- * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware
- */
-int hermes_bap_pwrite_pad(hermes_t *hw, int bap, const void *buf, unsigned data_len, int len,
-		      u16 id, u16 offset)
-{
-	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
-	int err = 0;
-
-	if (len < 0 || len % 2 || data_len > len)
-		return -EINVAL;
-
-	err = hermes_bap_seek(hw, bap, id, offset);
-	if (err)
-		goto out;
-
-	/* Transfer all the complete words of data */
-	hermes_write_words(hw, dreg, buf, data_len/2);
-	/* If there is an odd byte left over pad and transfer it */
-	if (data_len & 1) {
-		u8 end[2];
-		end[1] = 0;
-		end[0] = ((unsigned char *)buf)[data_len - 1];
-		hermes_write_words(hw, dreg, end, 1);
-		data_len ++;
-	}
-	/* Now send zeros for the padding */
-	if (data_len < len)
-		hermes_clear_words(hw, dreg, (len - data_len) / 2);
-	/* Complete */
- out:
-	return err;
-}
-
 /* Read a Length-Type-Value record from the card.
  *
  * If length is NULL, we ignore the length read from the card, and
@@ -553,7 +496,7 @@
 
 	count = length - 1;
 
-	hermes_write_words(hw, dreg, value, count);
+	hermes_write_bytes(hw, dreg, value, count << 1);
 
 	err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, 
 				rid, NULL);
@@ -568,7 +511,6 @@
 
 EXPORT_SYMBOL(hermes_bap_pread);
 EXPORT_SYMBOL(hermes_bap_pwrite);
-EXPORT_SYMBOL(hermes_bap_pwrite_pad);
 EXPORT_SYMBOL(hermes_read_ltv);
 EXPORT_SYMBOL(hermes_write_ltv);
 
diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h
index 7644f72..8e3f0e3 100644
--- a/drivers/net/wireless/hermes.h
+++ b/drivers/net/wireless/hermes.h
@@ -328,16 +328,6 @@
 	u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN];
 } __attribute__ ((packed));
 
-// #define HERMES_DEBUG_BUFFER 1
-#define HERMES_DEBUG_BUFSIZE 4096
-struct hermes_debug_entry {
-	int bap;
-	u16 id, offset;
-	int cycles;
-};
-
-#ifdef __KERNEL__
-
 /* Timeouts */
 #define HERMES_BAP_BUSY_TIMEOUT (10000) /* In iterations of ~1us */
 
@@ -347,14 +337,7 @@
 	int reg_spacing;
 #define HERMES_16BIT_REGSPACING	0
 #define HERMES_32BIT_REGSPACING	1
-
 	u16 inten; /* Which interrupts should be enabled? */
-
-#ifdef HERMES_DEBUG_BUFFER
-	struct hermes_debug_entry dbuf[HERMES_DEBUG_BUFSIZE];
-	unsigned long dbufp;
-	unsigned long profile[HERMES_BAP_BUSY_TIMEOUT+1];
-#endif
 } hermes_t;
 
 /* Register access convenience macros */
@@ -376,8 +359,6 @@
 		       u16 id, u16 offset);
 int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
 			u16 id, u16 offset);
-int hermes_bap_pwrite_pad(hermes_t *hw, int bap, const void *buf,
-			unsigned data_len, int len, u16 id, u16 offset);
 int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned buflen,
 		    u16 *length, void *buf);
 int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
@@ -425,10 +406,13 @@
 	ioread16_rep(hw->iobase + off, buf, count);
 }
 
-static inline void hermes_write_words(struct hermes *hw, int off, const void *buf, unsigned count)
+static inline void hermes_write_bytes(struct hermes *hw, int off,
+				      const char *buf, unsigned count)
 {
 	off = off << hw->reg_spacing;
-	iowrite16_rep(hw->iobase + off, buf, count);
+	iowrite16_rep(hw->iobase + off, buf, count >> 1);
+	if (unlikely(count & 1))
+		iowrite8(buf[count - 1], hw->iobase + off);
 }
 
 static inline void hermes_clear_words(struct hermes *hw, int off, unsigned count)
@@ -462,21 +446,4 @@
 	return HERMES_WRITE_RECORD(hw, bap, rid, &rec);
 }
 
-#else /* ! __KERNEL__ */
-
-/* These are provided for the benefit of userspace drivers and testing programs
-   which use ioperm() or iopl() */
-
-#define hermes_read_reg(base, off) (inw((base) + (off)))
-#define hermes_write_reg(base, off, val) (outw((val), (base) + (off)))
-
-#define hermes_read_regn(base, name) (hermes_read_reg((base), HERMES_##name))
-#define hermes_write_regn(base, name, val) (hermes_write_reg((base), HERMES_##name, (val)))
-
-/* Note that for the next two, the count is in 16-bit words, not bytes */
-#define hermes_read_data(base, off, buf, count) (insw((base) + (off), (buf), (count)))
-#define hermes_write_data(base, off, buf, count) (outsw((base) + (off), (buf), (count)))
-
-#endif /* ! __KERNEL__ */
-
 #endif  /* _HERMES_H */
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
index bca89cf..39f82f2 100644
--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -33,7 +33,44 @@
 #include "ipw2200.h"
 #include <linux/version.h>
 
-#define IPW2200_VERSION "git-1.1.1"
+
+#ifndef KBUILD_EXTMOD
+#define VK "k"
+#else
+#define VK
+#endif
+
+#ifdef CONFIG_IPW2200_DEBUG
+#define VD "d"
+#else
+#define VD
+#endif
+
+#ifdef CONFIG_IPW2200_MONITOR
+#define VM "m"
+#else
+#define VM
+#endif
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+#define VP "p"
+#else
+#define VP
+#endif
+
+#ifdef CONFIG_IPW2200_RADIOTAP
+#define VR "r"
+#else
+#define VR
+#endif
+
+#ifdef CONFIG_IPW2200_QOS
+#define VQ "q"
+#else
+#define VQ
+#endif
+
+#define IPW2200_VERSION "1.1.2" VK VD VM VP VR VQ
 #define DRV_DESCRIPTION	"Intel(R) PRO/Wireless 2200/2915 Network Driver"
 #define DRV_COPYRIGHT	"Copyright(c) 2003-2006 Intel Corporation"
 #define DRV_VERSION     IPW2200_VERSION
@@ -46,7 +83,9 @@
 MODULE_LICENSE("GPL");
 
 static int cmdlog = 0;
+#ifdef CONFIG_IPW2200_DEBUG
 static int debug = 0;
+#endif
 static int channel = 0;
 static int mode = 0;
 
@@ -61,8 +100,14 @@
 static const char ipw_modes[] = {
 	'a', 'b', 'g', '?'
 };
+static int antenna = CFG_SYS_ANTENNA_BOTH;
 
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static int rtap_iface = 0;     /* def: 0 -- do not create rtap interface */
+#endif
+
+
+#ifdef CONFIG_IPW2200_QOS
 static int qos_enable = 0;
 static int qos_burst_enable = 0;
 static int qos_no_ack_mask = 0;
@@ -126,7 +171,7 @@
 				       *qos_param);
 static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
 				     *qos_param);
-#endif				/* CONFIG_IPW_QOS */
+#endif				/* CONFIG_IPW2200_QOS */
 
 static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
 static void ipw_remove_current_network(struct ipw_priv *priv);
@@ -1269,6 +1314,105 @@
 
 static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL);
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static void ipw_prom_free(struct ipw_priv *priv);
+static int ipw_prom_alloc(struct ipw_priv *priv);
+static ssize_t store_rtap_iface(struct device *d,
+			 struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct ipw_priv *priv = dev_get_drvdata(d);
+	int rc = 0;
+
+	if (count < 1)
+		return -EINVAL;
+
+	switch (buf[0]) {
+	case '0':
+		if (!rtap_iface)
+			return count;
+
+		if (netif_running(priv->prom_net_dev)) {
+			IPW_WARNING("Interface is up.  Cannot unregister.\n");
+			return count;
+		}
+
+		ipw_prom_free(priv);
+		rtap_iface = 0;
+		break;
+
+	case '1':
+		if (rtap_iface)
+			return count;
+
+		rc = ipw_prom_alloc(priv);
+		if (!rc)
+			rtap_iface = 1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if (rc) {
+		IPW_ERROR("Failed to register promiscuous network "
+			  "device (error %d).\n", rc);
+	}
+
+	return count;
+}
+
+static ssize_t show_rtap_iface(struct device *d,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct ipw_priv *priv = dev_get_drvdata(d);
+	if (rtap_iface)
+		return sprintf(buf, "%s", priv->prom_net_dev->name);
+	else {
+		buf[0] = '-';
+		buf[1] = '1';
+		buf[2] = '\0';
+		return 3;
+	}
+}
+
+static DEVICE_ATTR(rtap_iface, S_IWUSR | S_IRUSR, show_rtap_iface,
+		   store_rtap_iface);
+
+static ssize_t store_rtap_filter(struct device *d,
+			 struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct ipw_priv *priv = dev_get_drvdata(d);
+
+	if (!priv->prom_priv) {
+		IPW_ERROR("Attempting to set filter without "
+			  "rtap_iface enabled.\n");
+		return -EPERM;
+	}
+
+	priv->prom_priv->filter = simple_strtol(buf, NULL, 0);
+
+	IPW_DEBUG_INFO("Setting rtap filter to " BIT_FMT16 "\n",
+		       BIT_ARG16(priv->prom_priv->filter));
+
+	return count;
+}
+
+static ssize_t show_rtap_filter(struct device *d,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct ipw_priv *priv = dev_get_drvdata(d);
+	return sprintf(buf, "0x%04X",
+		       priv->prom_priv ? priv->prom_priv->filter : 0);
+}
+
+static DEVICE_ATTR(rtap_filter, S_IWUSR | S_IRUSR, show_rtap_filter,
+		   store_rtap_filter);
+#endif
+
 static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
 			     char *buf)
 {
@@ -2025,16 +2169,11 @@
 	return ipw_send_cmd_simple(priv, IPW_CMD_HOST_COMPLETE);
 }
 
-static int ipw_send_system_config(struct ipw_priv *priv,
-				  struct ipw_sys_config *config)
+static int ipw_send_system_config(struct ipw_priv *priv)
 {
-	if (!priv || !config) {
-		IPW_ERROR("Invalid args\n");
-		return -1;
-	}
-
-	return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG, sizeof(*config),
-				config);
+	return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG,
+				sizeof(priv->sys_config),
+				&priv->sys_config);
 }
 
 static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
@@ -3104,10 +3243,10 @@
 
 
 struct ipw_fw {
-	u32 ver;
-	u32 boot_size;
-	u32 ucode_size;
-	u32 fw_size;
+	__le32 ver;
+	__le32 boot_size;
+	__le32 ucode_size;
+	__le32 fw_size;
 	u8 data[0];
 };
 
@@ -3131,8 +3270,8 @@
 
 	fw = (void *)(*raw)->data;
 
-	if ((*raw)->size < sizeof(*fw) +
-	    fw->boot_size + fw->ucode_size + fw->fw_size) {
+	if ((*raw)->size < sizeof(*fw) + le32_to_cpu(fw->boot_size) +
+	    le32_to_cpu(fw->ucode_size) + le32_to_cpu(fw->fw_size)) {
 		IPW_ERROR("%s is too small or corrupt (%zd)\n",
 			  name, (*raw)->size);
 		return -EINVAL;
@@ -3237,8 +3376,9 @@
 
 	fw = (void *)raw->data;
 	boot_img = &fw->data[0];
-	ucode_img = &fw->data[fw->boot_size];
-	fw_img = &fw->data[fw->boot_size + fw->ucode_size];
+	ucode_img = &fw->data[le32_to_cpu(fw->boot_size)];
+	fw_img = &fw->data[le32_to_cpu(fw->boot_size) +
+			   le32_to_cpu(fw->ucode_size)];
 
 	if (rc < 0)
 		goto error;
@@ -3272,7 +3412,7 @@
 			IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
 
 	/* DMA the initial boot firmware into the device */
-	rc = ipw_load_firmware(priv, boot_img, fw->boot_size);
+	rc = ipw_load_firmware(priv, boot_img, le32_to_cpu(fw->boot_size));
 	if (rc < 0) {
 		IPW_ERROR("Unable to load boot firmware: %d\n", rc);
 		goto error;
@@ -3294,7 +3434,7 @@
 	ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
 
 	/* DMA the ucode into the device */
-	rc = ipw_load_ucode(priv, ucode_img, fw->ucode_size);
+	rc = ipw_load_ucode(priv, ucode_img, le32_to_cpu(fw->ucode_size));
 	if (rc < 0) {
 		IPW_ERROR("Unable to load ucode: %d\n", rc);
 		goto error;
@@ -3304,7 +3444,7 @@
 	ipw_stop_nic(priv);
 
 	/* DMA bss firmware into the device */
-	rc = ipw_load_firmware(priv, fw_img, fw->fw_size);
+	rc = ipw_load_firmware(priv, fw_img, le32_to_cpu(fw->fw_size));
 	if (rc < 0) {
 		IPW_ERROR("Unable to load firmware: %d\n", rc);
 		goto error;
@@ -3700,7 +3840,17 @@
 static void ipw_system_config(void *data)
 {
 	struct ipw_priv *priv = data;
-	ipw_send_system_config(priv, &priv->sys_config);
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+	if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
+		priv->sys_config.accept_all_data_frames = 1;
+		priv->sys_config.accept_non_directed_frames = 1;
+		priv->sys_config.accept_all_mgmt_bcpr = 1;
+		priv->sys_config.accept_all_mgmt_frames = 1;
+	}
+#endif
+
+	ipw_send_system_config(priv);
 }
 
 struct ipw_status_code {
@@ -3771,6 +3921,13 @@
 	memset(avg, 0, sizeof(*avg));
 }
 
+#define DEPTH_RSSI 8
+#define DEPTH_NOISE 16
+static s16 exponential_average(s16 prev_avg, s16 val, u8 depth)
+{
+	return ((depth-1)*prev_avg +  val)/depth;
+}
+
 static void average_add(struct average *avg, s16 val)
 {
 	avg->sum -= avg->entries[avg->pos];
@@ -3800,8 +3957,8 @@
 	priv->quality = 0;
 
 	average_init(&priv->average_missed_beacons);
-	average_init(&priv->average_rssi);
-	average_init(&priv->average_noise);
+	priv->exp_avg_rssi = -60;
+	priv->exp_avg_noise = -85 + 0x100;
 
 	priv->last_rate = 0;
 	priv->last_missed_beacons = 0;
@@ -4008,7 +4165,7 @@
 	IPW_DEBUG_STATS("Tx quality   : %3d%% (%u errors, %u packets)\n",
 			tx_quality, tx_failures_delta, tx_packets_delta);
 
-	rssi = average_value(&priv->average_rssi);
+	rssi = priv->exp_avg_rssi;
 	signal_quality =
 	    (100 *
 	     (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
@@ -4185,7 +4342,7 @@
 					queue_work(priv->workqueue,
 						   &priv->system_config);
 
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
 #define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
 			 le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_ctl))
 					if ((priv->status & STATUS_AUTH) &&
@@ -4482,6 +4639,24 @@
 				 && priv->status & STATUS_ASSOCIATED)
 				queue_delayed_work(priv->workqueue,
 						   &priv->request_scan, HZ);
+
+			/* Send an empty event to user space.
+			 * We don't send the received data on the event because
+			 * it would require us to do complex transcoding, and
+			 * we want to minimise the work done in the irq handler
+			 * Use a request to extract the data.
+			 * Also, we generate this even for any scan, regardless
+			 * on how the scan was initiated. User space can just
+			 * sync on periodic scan to get fresh data...
+			 * Jean II */
+			if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) {
+				union iwreq_data wrqu;
+
+				wrqu.data.length = 0;
+				wrqu.data.flags = 0;
+				wireless_send_event(priv->net_dev, SIOCGIWSCAN,
+						    &wrqu, NULL);
+			}
 			break;
 		}
 
@@ -4577,11 +4752,10 @@
 
 	case HOST_NOTIFICATION_NOISE_STATS:{
 			if (notif->size == sizeof(u32)) {
-				priv->last_noise =
-				    (u8) (le32_to_cpu(notif->u.noise.value) &
-					  0xff);
-				average_add(&priv->average_noise,
-					    priv->last_noise);
+				priv->exp_avg_noise =
+				    exponential_average(priv->exp_avg_noise,
+				    (u8) (le32_to_cpu(notif->u.noise.value) & 0xff),
+				    DEPTH_NOISE);
 				break;
 			}
 
@@ -6170,8 +6344,6 @@
 {
 	/* make sure WPA is enabled */
 	ipw_wpa_enable(priv, 1);
-
-	ipw_disassociate(priv);
 }
 
 static int ipw_set_rsn_capa(struct ipw_priv *priv,
@@ -6365,6 +6537,7 @@
 
 	case IW_AUTH_WPA_ENABLED:
 		ret = ipw_wpa_enable(priv, param->value);
+		ipw_disassociate(priv);
 		break;
 
 	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
@@ -6506,7 +6679,7 @@
 	return 0;
 }
 
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
 
 /* QoS */
 /*
@@ -6853,61 +7026,55 @@
 	return from_priority_to_tx_queue[priority] - 1;
 }
 
-/*
-* add QoS parameter to the TX command
-*/
-static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
-					u16 priority,
-					struct tfd_data *tfd, u8 unicast)
+static int ipw_is_qos_active(struct net_device *dev,
+			     struct sk_buff *skb)
 {
-	int ret = 0;
-	int tx_queue_id = 0;
+	struct ipw_priv *priv = ieee80211_priv(dev);
 	struct ieee80211_qos_data *qos_data = NULL;
 	int active, supported;
-	unsigned long flags;
+	u8 *daddr = skb->data + ETH_ALEN;
+	int unicast = !is_multicast_ether_addr(daddr);
 
 	if (!(priv->status & STATUS_ASSOCIATED))
 		return 0;
 
 	qos_data = &priv->assoc_network->qos_data;
 
-	spin_lock_irqsave(&priv->ieee->lock, flags);
-
 	if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
 		if (unicast == 0)
 			qos_data->active = 0;
 		else
 			qos_data->active = qos_data->supported;
 	}
-
 	active = qos_data->active;
 	supported = qos_data->supported;
-
-	spin_unlock_irqrestore(&priv->ieee->lock, flags);
-
 	IPW_DEBUG_QOS("QoS  %d network is QoS active %d  supported %d  "
 		      "unicast %d\n",
 		      priv->qos_data.qos_enable, active, supported, unicast);
-	if (active && priv->qos_data.qos_enable) {
-		ret = from_priority_to_tx_queue[priority];
-		tx_queue_id = ret - 1;
-		IPW_DEBUG_QOS("QoS packet priority is %d \n", priority);
-		if (priority <= 7) {
-			tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
-			tfd->tfd.tfd_26.mchdr.qos_ctrl = priority;
-			tfd->tfd.tfd_26.mchdr.frame_ctl |=
-			    IEEE80211_STYPE_QOS_DATA;
+	if (active && priv->qos_data.qos_enable)
+		return 1;
 
-			if (priv->qos_data.qos_no_ack_mask &
-			    (1UL << tx_queue_id)) {
-				tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
-				tfd->tfd.tfd_26.mchdr.qos_ctrl |=
-				    CTRL_QOS_NO_ACK;
-			}
-		}
+	return 0;
+
+}
+/*
+* add QoS parameter to the TX command
+*/
+static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
+					u16 priority,
+					struct tfd_data *tfd)
+{
+	int tx_queue_id = 0;
+
+
+	tx_queue_id = from_priority_to_tx_queue[priority] - 1;
+	tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
+
+	if (priv->qos_data.qos_no_ack_mask & (1UL << tx_queue_id)) {
+		tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
+		tfd->tfd.tfd_26.mchdr.qos_ctrl |= CTRL_QOS_NO_ACK;
 	}
-
-	return ret;
+	return 0;
 }
 
 /*
@@ -6977,7 +7144,7 @@
 				qos_param);
 }
 
-#endif				/* CONFIG_IPW_QOS */
+#endif				/* CONFIG_IPW2200_QOS */
 
 static int ipw_associate_network(struct ipw_priv *priv,
 				 struct ieee80211_network *network,
@@ -7116,7 +7283,7 @@
 	else
 		priv->sys_config.answer_broadcast_ssid_probe = 0;
 
-	err = ipw_send_system_config(priv, &priv->sys_config);
+	err = ipw_send_system_config(priv);
 	if (err) {
 		IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
 		return err;
@@ -7141,7 +7308,7 @@
 
 	priv->assoc_network = network;
 
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
 	ipw_qos_association(priv, network);
 #endif
 
@@ -7415,7 +7582,7 @@
 	}
 }
 
-#ifdef CONFIG_IEEE80211_RADIOTAP
+#ifdef CONFIG_IPW2200_RADIOTAP
 static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
 					   struct ipw_rx_mem_buffer *rxb,
 					   struct ieee80211_rx_stats *stats)
@@ -7432,15 +7599,7 @@
 	/* Magic struct that slots into the radiotap header -- no reason
 	 * to build this manually element by element, we can write it much
 	 * more efficiently than we can parse it. ORDER MATTERS HERE */
-	struct ipw_rt_hdr {
-		struct ieee80211_radiotap_header rt_hdr;
-		u8 rt_flags;	/* radiotap packet flags */
-		u8 rt_rate;	/* rate in 500kb/s */
-		u16 rt_channel;	/* channel in mhz */
-		u16 rt_chbitmask;	/* channel bitfield */
-		s8 rt_dbmsignal;	/* signal in dbM, kluged to signed */
-		u8 rt_antenna;	/* antenna number */
-	} *ipw_rt;
+	struct ipw_rt_hdr *ipw_rt;
 
 	short len = le16_to_cpu(pkt->u.frame.length);
 
@@ -7494,9 +7653,11 @@
 	/* Big bitfield of all the fields we provide in radiotap */
 	ipw_rt->rt_hdr.it_present =
 	    ((1 << IEEE80211_RADIOTAP_FLAGS) |
+	     (1 << IEEE80211_RADIOTAP_TSFT) |
 	     (1 << IEEE80211_RADIOTAP_RATE) |
 	     (1 << IEEE80211_RADIOTAP_CHANNEL) |
 	     (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+	     (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
 	     (1 << IEEE80211_RADIOTAP_ANTENNA));
 
 	/* Zero the flags, we'll add to them as we go */
@@ -7582,6 +7743,217 @@
 }
 #endif
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+#define ieee80211_is_probe_response(fc) \
+   ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && \
+    (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP )
+
+#define ieee80211_is_management(fc) \
+   ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)
+
+#define ieee80211_is_control(fc) \
+   ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
+
+#define ieee80211_is_data(fc) \
+   ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
+
+#define ieee80211_is_assoc_request(fc) \
+   ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ)
+
+#define ieee80211_is_reassoc_request(fc) \
+   ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ)
+
+static void ipw_handle_promiscuous_rx(struct ipw_priv *priv,
+				      struct ipw_rx_mem_buffer *rxb,
+				      struct ieee80211_rx_stats *stats)
+{
+	struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
+	struct ipw_rx_frame *frame = &pkt->u.frame;
+	struct ipw_rt_hdr *ipw_rt;
+
+	/* First cache any information we need before we overwrite
+	 * the information provided in the skb from the hardware */
+	struct ieee80211_hdr *hdr;
+	u16 channel = frame->received_channel;
+	u8 phy_flags = frame->antennaAndPhy;
+	s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM;
+	s8 noise = frame->noise;
+	u8 rate = frame->rate;
+	short len = le16_to_cpu(pkt->u.frame.length);
+	u64 tsf = 0;
+	struct sk_buff *skb;
+	int hdr_only = 0;
+	u16 filter = priv->prom_priv->filter;
+
+	/* If the filter is set to not include Rx frames then return */
+	if (filter & IPW_PROM_NO_RX)
+		return;
+
+	/* We received data from the HW, so stop the watchdog */
+	priv->prom_net_dev->trans_start = jiffies;
+
+	if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
+		priv->prom_priv->ieee->stats.rx_errors++;
+		IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
+		return;
+	}
+
+	/* We only process data packets if the interface is open */
+	if (unlikely(!netif_running(priv->prom_net_dev))) {
+		priv->prom_priv->ieee->stats.rx_dropped++;
+		IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+		return;
+	}
+
+	/* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
+	 * that now */
+	if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
+		/* FIXME: Should alloc bigger skb instead */
+		priv->prom_priv->ieee->stats.rx_dropped++;
+		IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
+		return;
+	}
+
+	hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE;
+	if (ieee80211_is_management(hdr->frame_ctl)) {
+		if (filter & IPW_PROM_NO_MGMT)
+			return;
+		if (filter & IPW_PROM_MGMT_HEADER_ONLY)
+			hdr_only = 1;
+	} else if (ieee80211_is_control(hdr->frame_ctl)) {
+		if (filter & IPW_PROM_NO_CTL)
+			return;
+		if (filter & IPW_PROM_CTL_HEADER_ONLY)
+			hdr_only = 1;
+	} else if (ieee80211_is_data(hdr->frame_ctl)) {
+		if (filter & IPW_PROM_NO_DATA)
+			return;
+		if (filter & IPW_PROM_DATA_HEADER_ONLY)
+			hdr_only = 1;
+	}
+
+	/* Copy the SKB since this is for the promiscuous side */
+	skb = skb_copy(rxb->skb, GFP_ATOMIC);
+	if (skb == NULL) {
+		IPW_ERROR("skb_clone failed for promiscuous copy.\n");
+		return;
+	}
+
+	/* copy the frame data to write after where the radiotap header goes */
+	ipw_rt = (void *)skb->data;
+
+	if (hdr_only)
+		len = ieee80211_get_hdrlen(hdr->frame_ctl);
+
+	memcpy(ipw_rt->payload, hdr, len);
+
+	/* Zero the radiotap static buffer  ...  We only need to zero the bytes
+	 * NOT part of our real header, saves a little time.
+	 *
+	 * No longer necessary since we fill in all our data.  Purge before
+	 * merging patch officially.
+	 * memset(rxb->skb->data + sizeof(struct ipw_rt_hdr), 0,
+	 *        IEEE80211_RADIOTAP_HDRLEN - sizeof(struct ipw_rt_hdr));
+	 */
+
+	ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+	ipw_rt->rt_hdr.it_pad = 0;	/* always good to zero */
+	ipw_rt->rt_hdr.it_len = sizeof(*ipw_rt);	/* total header+data */
+
+	/* Set the size of the skb to the size of the frame */
+	skb_put(skb, ipw_rt->rt_hdr.it_len + len);
+
+	/* Big bitfield of all the fields we provide in radiotap */
+	ipw_rt->rt_hdr.it_present =
+	    ((1 << IEEE80211_RADIOTAP_FLAGS) |
+	     (1 << IEEE80211_RADIOTAP_TSFT) |
+	     (1 << IEEE80211_RADIOTAP_RATE) |
+	     (1 << IEEE80211_RADIOTAP_CHANNEL) |
+	     (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+	     (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
+	     (1 << IEEE80211_RADIOTAP_ANTENNA));
+
+	/* Zero the flags, we'll add to them as we go */
+	ipw_rt->rt_flags = 0;
+
+	ipw_rt->rt_tsf = tsf;
+
+	/* Convert to DBM */
+	ipw_rt->rt_dbmsignal = signal;
+	ipw_rt->rt_dbmnoise = noise;
+
+	/* Convert the channel data and set the flags */
+	ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(channel));
+	if (channel > 14) {	/* 802.11a */
+		ipw_rt->rt_chbitmask =
+		    cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
+	} else if (phy_flags & (1 << 5)) {	/* 802.11b */
+		ipw_rt->rt_chbitmask =
+		    cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
+	} else {		/* 802.11g */
+		ipw_rt->rt_chbitmask =
+		    (IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
+	}
+
+	/* set the rate in multiples of 500k/s */
+	switch (rate) {
+	case IPW_TX_RATE_1MB:
+		ipw_rt->rt_rate = 2;
+		break;
+	case IPW_TX_RATE_2MB:
+		ipw_rt->rt_rate = 4;
+		break;
+	case IPW_TX_RATE_5MB:
+		ipw_rt->rt_rate = 10;
+		break;
+	case IPW_TX_RATE_6MB:
+		ipw_rt->rt_rate = 12;
+		break;
+	case IPW_TX_RATE_9MB:
+		ipw_rt->rt_rate = 18;
+		break;
+	case IPW_TX_RATE_11MB:
+		ipw_rt->rt_rate = 22;
+		break;
+	case IPW_TX_RATE_12MB:
+		ipw_rt->rt_rate = 24;
+		break;
+	case IPW_TX_RATE_18MB:
+		ipw_rt->rt_rate = 36;
+		break;
+	case IPW_TX_RATE_24MB:
+		ipw_rt->rt_rate = 48;
+		break;
+	case IPW_TX_RATE_36MB:
+		ipw_rt->rt_rate = 72;
+		break;
+	case IPW_TX_RATE_48MB:
+		ipw_rt->rt_rate = 96;
+		break;
+	case IPW_TX_RATE_54MB:
+		ipw_rt->rt_rate = 108;
+		break;
+	default:
+		ipw_rt->rt_rate = 0;
+		break;
+	}
+
+	/* antenna number */
+	ipw_rt->rt_antenna = (phy_flags & 3);
+
+	/* set the preamble flag if we have it */
+	if (phy_flags & (1 << 6))
+		ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+
+	IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len);
+
+	if (!ieee80211_rx(priv->prom_priv->ieee, skb, stats)) {
+		priv->prom_priv->ieee->stats.rx_errors++;
+		dev_kfree_skb_any(skb);
+	}
+}
+#endif
+
 static int is_network_packet(struct ipw_priv *priv,
 				    struct ieee80211_hdr_4addr *header)
 {
@@ -7808,15 +8180,21 @@
 
 				priv->rx_packets++;
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+	if (priv->prom_net_dev && netif_running(priv->prom_net_dev))
+		ipw_handle_promiscuous_rx(priv, rxb, &stats);
+#endif
+
 #ifdef CONFIG_IPW2200_MONITOR
 				if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
-#ifdef CONFIG_IEEE80211_RADIOTAP
-					ipw_handle_data_packet_monitor(priv,
-								       rxb,
-								       &stats);
+#ifdef CONFIG_IPW2200_RADIOTAP
+
+                ipw_handle_data_packet_monitor(priv,
+					       rxb,
+					       &stats);
 #else
-					ipw_handle_data_packet(priv, rxb,
-							       &stats);
+		ipw_handle_data_packet(priv, rxb,
+				       &stats);
 #endif
 					break;
 				}
@@ -7837,9 +8215,9 @@
 				if (network_packet && priv->assoc_network) {
 					priv->assoc_network->stats.rssi =
 					    stats.rssi;
-					average_add(&priv->average_rssi,
-						    stats.rssi);
-					priv->last_rx_rssi = stats.rssi;
+					priv->exp_avg_rssi =
+					    exponential_average(priv->exp_avg_rssi,
+					    stats.rssi, DEPTH_RSSI);
 				}
 
 				IPW_DEBUG_RX("Frame: len=%u\n",
@@ -7982,10 +8360,10 @@
 		IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
 		/* TODO: Validate that provided channel is in range */
 	}
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
 	ipw_qos_init(priv, qos_enable, qos_burst_enable,
 		     burst_duration_CCK, burst_duration_OFDM);
-#endif				/* CONFIG_IPW_QOS */
+#endif				/* CONFIG_IPW2200_QOS */
 
 	switch (mode) {
 	case 1:
@@ -7996,7 +8374,7 @@
 #ifdef CONFIG_IPW2200_MONITOR
 	case 2:
 		priv->ieee->iw_mode = IW_MODE_MONITOR;
-#ifdef CONFIG_IEEE80211_RADIOTAP
+#ifdef CONFIG_IPW2200_RADIOTAP
 		priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
 #else
 		priv->net_dev->type = ARPHRD_IEEE80211;
@@ -8251,7 +8629,7 @@
 		priv->net_dev->type = ARPHRD_ETHER;
 
 	if (wrqu->mode == IW_MODE_MONITOR)
-#ifdef CONFIG_IEEE80211_RADIOTAP
+#ifdef CONFIG_IPW2200_RADIOTAP
 		priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
 #else
 		priv->net_dev->type = ARPHRD_IEEE80211;
@@ -8379,7 +8757,8 @@
 	/* Event capability (kernel + driver) */
 	range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
 				IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
-				IW_EVENT_CAPA_MASK(SIOCGIWAP));
+				IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+				IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
 	range->event_capa[1] = IW_EVENT_CAPA_K_1;
 
 	range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
@@ -8734,6 +9113,7 @@
 	struct ipw_priv *priv = ieee80211_priv(dev);
 	mutex_lock(&priv->mutex);
 	wrqu->bitrate.value = priv->last_rate;
+	wrqu->bitrate.fixed = (priv->config & CFG_FIXED_RATE) ? 1 : 0;
 	mutex_unlock(&priv->mutex);
 	IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
 	return 0;
@@ -9351,7 +9731,7 @@
 	IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
 	if (enable) {
 		if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
-#ifdef CONFIG_IEEE80211_RADIOTAP
+#ifdef CONFIG_IPW2200_RADIOTAP
 			priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
 #else
 			priv->net_dev->type = ARPHRD_IEEE80211;
@@ -9579,8 +9959,8 @@
 	}
 
 	wstats->qual.qual = priv->quality;
-	wstats->qual.level = average_value(&priv->average_rssi);
-	wstats->qual.noise = average_value(&priv->average_noise);
+	wstats->qual.level = priv->exp_avg_rssi;
+	wstats->qual.noise = priv->exp_avg_noise;
 	wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
 	    IW_QUAL_NOISE_UPDATED | IW_QUAL_DBM;
 
@@ -9608,7 +9988,9 @@
 	sys_config->disable_unicast_decryption = 1;
 	sys_config->exclude_multicast_unencrypted = 0;
 	sys_config->disable_multicast_decryption = 1;
-	sys_config->antenna_diversity = CFG_SYS_ANTENNA_SLOW_DIV;
+	if (antenna < CFG_SYS_ANTENNA_BOTH || antenna > CFG_SYS_ANTENNA_B)
+		antenna = CFG_SYS_ANTENNA_BOTH;
+	sys_config->antenna_diversity = antenna;
 	sys_config->pass_crc_to_host = 0;	/* TODO: See if 1 gives us FCS */
 	sys_config->dot11g_auto_detection = 0;
 	sys_config->enable_cts_to_self = 0;
@@ -9647,11 +10029,11 @@
 static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
 			     int pri)
 {
-	struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)
+	struct ieee80211_hdr_3addrqos *hdr = (struct ieee80211_hdr_3addrqos *)
 	    txb->fragments[0]->data;
 	int i = 0;
 	struct tfd_frame *tfd;
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
 	int tx_id = ipw_get_tx_queue_number(priv, pri);
 	struct clx2_tx_queue *txq = &priv->txq[tx_id];
 #else
@@ -9662,9 +10044,9 @@
 	u16 remaining_bytes;
 	int fc;
 
+	hdr_len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
 	switch (priv->ieee->iw_mode) {
 	case IW_MODE_ADHOC:
-		hdr_len = IEEE80211_3ADDR_LEN;
 		unicast = !is_multicast_ether_addr(hdr->addr1);
 		id = ipw_find_station(priv, hdr->addr1);
 		if (id == IPW_INVALID_STATION) {
@@ -9681,7 +10063,6 @@
 	case IW_MODE_INFRA:
 	default:
 		unicast = !is_multicast_ether_addr(hdr->addr3);
-		hdr_len = IEEE80211_3ADDR_LEN;
 		id = 0;
 		break;
 	}
@@ -9759,9 +10140,10 @@
 		/* No hardware encryption */
 		tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP;
 
-#ifdef CONFIG_IPW_QOS
-	ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data), unicast);
-#endif				/* CONFIG_IPW_QOS */
+#ifdef CONFIG_IPW2200_QOS
+	if (fc & IEEE80211_STYPE_QOS_DATA)
+		ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data));
+#endif				/* CONFIG_IPW2200_QOS */
 
 	/* payload */
 	tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
@@ -9841,12 +10223,12 @@
 static int ipw_net_is_queue_full(struct net_device *dev, int pri)
 {
 	struct ipw_priv *priv = ieee80211_priv(dev);
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
 	int tx_id = ipw_get_tx_queue_number(priv, pri);
 	struct clx2_tx_queue *txq = &priv->txq[tx_id];
 #else
 	struct clx2_tx_queue *txq = &priv->txq[0];
-#endif				/* CONFIG_IPW_QOS */
+#endif				/* CONFIG_IPW2200_QOS */
 
 	if (ipw_queue_space(&txq->q) < txq->q.high_mark)
 		return 1;
@@ -9854,6 +10236,88 @@
 	return 0;
 }
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static void ipw_handle_promiscuous_tx(struct ipw_priv *priv,
+				      struct ieee80211_txb *txb)
+{
+	struct ieee80211_rx_stats dummystats;
+	struct ieee80211_hdr *hdr;
+	u8 n;
+	u16 filter = priv->prom_priv->filter;
+	int hdr_only = 0;
+
+	if (filter & IPW_PROM_NO_TX)
+		return;
+
+	memset(&dummystats, 0, sizeof(dummystats));
+
+	/* Filtering of fragment chains is done agains the first fragment */
+	hdr = (void *)txb->fragments[0]->data;
+	if (ieee80211_is_management(hdr->frame_ctl)) {
+		if (filter & IPW_PROM_NO_MGMT)
+			return;
+		if (filter & IPW_PROM_MGMT_HEADER_ONLY)
+			hdr_only = 1;
+	} else if (ieee80211_is_control(hdr->frame_ctl)) {
+		if (filter & IPW_PROM_NO_CTL)
+			return;
+		if (filter & IPW_PROM_CTL_HEADER_ONLY)
+			hdr_only = 1;
+	} else if (ieee80211_is_data(hdr->frame_ctl)) {
+		if (filter & IPW_PROM_NO_DATA)
+			return;
+		if (filter & IPW_PROM_DATA_HEADER_ONLY)
+			hdr_only = 1;
+	}
+
+	for(n=0; n<txb->nr_frags; ++n) {
+		struct sk_buff *src = txb->fragments[n];
+		struct sk_buff *dst;
+		struct ieee80211_radiotap_header *rt_hdr;
+		int len;
+
+		if (hdr_only) {
+			hdr = (void *)src->data;
+			len = ieee80211_get_hdrlen(hdr->frame_ctl);
+		} else
+			len = src->len;
+
+		dst = alloc_skb(
+			len + IEEE80211_RADIOTAP_HDRLEN, GFP_ATOMIC);
+		if (!dst) continue;
+
+		rt_hdr = (void *)skb_put(dst, sizeof(*rt_hdr));
+
+		rt_hdr->it_version = PKTHDR_RADIOTAP_VERSION;
+		rt_hdr->it_pad = 0;
+		rt_hdr->it_present = 0; /* after all, it's just an idea */
+		rt_hdr->it_present |=  (1 << IEEE80211_RADIOTAP_CHANNEL);
+
+		*(u16*)skb_put(dst, sizeof(u16)) = cpu_to_le16(
+			ieee80211chan2mhz(priv->channel));
+		if (priv->channel > 14) 	/* 802.11a */
+			*(u16*)skb_put(dst, sizeof(u16)) =
+				cpu_to_le16(IEEE80211_CHAN_OFDM |
+					     IEEE80211_CHAN_5GHZ);
+		else if (priv->ieee->mode == IEEE_B) /* 802.11b */
+			*(u16*)skb_put(dst, sizeof(u16)) =
+				cpu_to_le16(IEEE80211_CHAN_CCK |
+					     IEEE80211_CHAN_2GHZ);
+		else 		/* 802.11g */
+			*(u16*)skb_put(dst, sizeof(u16)) =
+				cpu_to_le16(IEEE80211_CHAN_OFDM |
+				 IEEE80211_CHAN_2GHZ);
+
+		rt_hdr->it_len = dst->len;
+
+		memcpy(skb_put(dst, len), src->data, len);
+
+		if (!ieee80211_rx(priv->prom_priv->ieee, dst, &dummystats))
+			dev_kfree_skb_any(dst);
+	}
+}
+#endif
+
 static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
 				   struct net_device *dev, int pri)
 {
@@ -9871,6 +10335,11 @@
 		goto fail_unlock;
 	}
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+	if (rtap_iface && netif_running(priv->prom_net_dev))
+		ipw_handle_promiscuous_tx(priv, txb);
+#endif
+
 	ret = ipw_tx_skb(priv, txb, pri);
 	if (ret == NETDEV_TX_OK)
 		__ipw_led_activity_on(priv);
@@ -10169,10 +10638,10 @@
 	INIT_WORK(&priv->merge_networks,
 		  (void (*)(void *))ipw_merge_adhoc_network, priv);
 
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
 	INIT_WORK(&priv->qos_activate, (void (*)(void *))ipw_bg_qos_activate,
 		  priv);
-#endif				/* CONFIG_IPW_QOS */
+#endif				/* CONFIG_IPW2200_QOS */
 
 	tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
 		     ipw_irq_tasklet, (unsigned long)priv);
@@ -10318,12 +10787,21 @@
 			    |= CFG_BT_COEXISTENCE_OOB;
 	}
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+	if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
+		priv->sys_config.accept_all_data_frames = 1;
+		priv->sys_config.accept_non_directed_frames = 1;
+		priv->sys_config.accept_all_mgmt_bcpr = 1;
+		priv->sys_config.accept_all_mgmt_frames = 1;
+	}
+#endif
+
 	if (priv->ieee->iw_mode == IW_MODE_ADHOC)
 		priv->sys_config.answer_broadcast_ssid_probe = 1;
 	else
 		priv->sys_config.answer_broadcast_ssid_probe = 0;
 
-	if (ipw_send_system_config(priv, &priv->sys_config))
+	if (ipw_send_system_config(priv))
 		goto error;
 
 	init_supported_rates(priv, &priv->rates);
@@ -10335,10 +10813,10 @@
 		if (ipw_send_rts_threshold(priv, priv->rts_threshold))
 			goto error;
 	}
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
 	IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n");
 	ipw_qos_activate(priv, NULL);
-#endif				/* CONFIG_IPW_QOS */
+#endif				/* CONFIG_IPW2200_QOS */
 
 	if (ipw_set_random_seed(priv))
 		goto error;
@@ -10639,6 +11117,7 @@
 		if (priv->cmdlog == NULL) {
 			IPW_ERROR("Error allocating %d command log entries.\n",
 				  cmdlog);
+			return -ENOMEM;
 		} else {
 			memset(priv->cmdlog, 0, sizeof(*priv->cmdlog) * cmdlog);
 			priv->cmdlog_len = cmdlog;
@@ -10860,6 +11339,10 @@
 	&dev_attr_led.attr,
 	&dev_attr_speed_scan.attr,
 	&dev_attr_net_stats.attr,
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+	&dev_attr_rtap_iface.attr,
+	&dev_attr_rtap_filter.attr,
+#endif
 	NULL
 };
 
@@ -10868,6 +11351,109 @@
 	.attrs = ipw_sysfs_entries,
 };
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static int ipw_prom_open(struct net_device *dev)
+{
+	struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
+	struct ipw_priv *priv = prom_priv->priv;
+
+	IPW_DEBUG_INFO("prom dev->open\n");
+	netif_carrier_off(dev);
+	netif_stop_queue(dev);
+
+	if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+		priv->sys_config.accept_all_data_frames = 1;
+		priv->sys_config.accept_non_directed_frames = 1;
+		priv->sys_config.accept_all_mgmt_bcpr = 1;
+		priv->sys_config.accept_all_mgmt_frames = 1;
+
+		ipw_send_system_config(priv);
+	}
+
+	return 0;
+}
+
+static int ipw_prom_stop(struct net_device *dev)
+{
+	struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
+	struct ipw_priv *priv = prom_priv->priv;
+
+	IPW_DEBUG_INFO("prom dev->stop\n");
+
+	if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+		priv->sys_config.accept_all_data_frames = 0;
+		priv->sys_config.accept_non_directed_frames = 0;
+		priv->sys_config.accept_all_mgmt_bcpr = 0;
+		priv->sys_config.accept_all_mgmt_frames = 0;
+
+		ipw_send_system_config(priv);
+	}
+
+	return 0;
+}
+
+static int ipw_prom_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	IPW_DEBUG_INFO("prom dev->xmit\n");
+	netif_stop_queue(dev);
+	return -EOPNOTSUPP;
+}
+
+static struct net_device_stats *ipw_prom_get_stats(struct net_device *dev)
+{
+	struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
+	return &prom_priv->ieee->stats;
+}
+
+static int ipw_prom_alloc(struct ipw_priv *priv)
+{
+	int rc = 0;
+
+	if (priv->prom_net_dev)
+		return -EPERM;
+
+	priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv));
+	if (priv->prom_net_dev == NULL)
+		return -ENOMEM;
+
+	priv->prom_priv = ieee80211_priv(priv->prom_net_dev);
+	priv->prom_priv->ieee = netdev_priv(priv->prom_net_dev);
+	priv->prom_priv->priv = priv;
+
+	strcpy(priv->prom_net_dev->name, "rtap%d");
+
+	priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+	priv->prom_net_dev->open = ipw_prom_open;
+	priv->prom_net_dev->stop = ipw_prom_stop;
+	priv->prom_net_dev->get_stats = ipw_prom_get_stats;
+	priv->prom_net_dev->hard_start_xmit = ipw_prom_hard_start_xmit;
+
+	priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR;
+
+	rc = register_netdev(priv->prom_net_dev);
+	if (rc) {
+		free_ieee80211(priv->prom_net_dev);
+		priv->prom_net_dev = NULL;
+		return rc;
+	}
+
+	return 0;
+}
+
+static void ipw_prom_free(struct ipw_priv *priv)
+{
+	if (!priv->prom_net_dev)
+		return;
+
+	unregister_netdev(priv->prom_net_dev);
+	free_ieee80211(priv->prom_net_dev);
+
+	priv->prom_net_dev = NULL;
+}
+
+#endif
+
+
 static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	int err = 0;
@@ -10959,11 +11545,12 @@
 	priv->ieee->set_security = shim__set_security;
 	priv->ieee->is_queue_full = ipw_net_is_queue_full;
 
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_QOS
+	priv->ieee->is_qos_active = ipw_is_qos_active;
 	priv->ieee->handle_probe_response = ipw_handle_beacon;
 	priv->ieee->handle_beacon = ipw_handle_probe_response;
 	priv->ieee->handle_assoc_response = ipw_handle_assoc_response;
-#endif				/* CONFIG_IPW_QOS */
+#endif				/* CONFIG_IPW2200_QOS */
 
 	priv->ieee->perfect_rssi = -20;
 	priv->ieee->worst_rssi = -85;
@@ -10997,6 +11584,18 @@
 		goto out_remove_sysfs;
 	}
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+	if (rtap_iface) {
+	        err = ipw_prom_alloc(priv);
+		if (err) {
+			IPW_ERROR("Failed to register promiscuous network "
+				  "device (error %d).\n", err);
+			unregister_netdev(priv->net_dev);
+			goto out_remove_sysfs;
+		}
+	}
+#endif
+
 	printk(KERN_INFO DRV_NAME ": Detected geography %s (%d 802.11bg "
 	       "channels, %d 802.11a channels)\n",
 	       priv->ieee->geo.name, priv->ieee->geo.bg_channels,
@@ -11076,6 +11675,10 @@
 		priv->error = NULL;
 	}
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+	ipw_prom_free(priv);
+#endif
+
 	free_irq(pdev->irq, priv);
 	iounmap(priv->hw_base);
 	pci_release_regions(pdev);
@@ -11200,7 +11803,12 @@
 module_param(channel, int, 0444);
 MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
 
-#ifdef CONFIG_IPW_QOS
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+module_param(rtap_iface, int, 0444);
+MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)");
+#endif
+
+#ifdef CONFIG_IPW2200_QOS
 module_param(qos_enable, int, 0444);
 MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis");
 
@@ -11215,7 +11823,7 @@
 
 module_param(burst_duration_OFDM, int, 0444);
 MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value");
-#endif				/* CONFIG_IPW_QOS */
+#endif				/* CONFIG_IPW2200_QOS */
 
 #ifdef CONFIG_IPW2200_MONITOR
 module_param(mode, int, 0444);
@@ -11238,5 +11846,8 @@
 module_param(roaming, int, 0444);
 MODULE_PARM_DESC(roaming, "enable roaming support (default on)");
 
+module_param(antenna, int, 0444);
+MODULE_PARM_DESC(antenna, "select antenna 1=Main, 3=Aux, default 0 [both], 2=slow_diversity (choose the one with lower background noise)");
+
 module_exit(ipw_exit);
 module_init(ipw_init);
diff --git a/drivers/net/wireless/ipw2200.h b/drivers/net/wireless/ipw2200.h
index 4b98049..6044c0b 100644
--- a/drivers/net/wireless/ipw2200.h
+++ b/drivers/net/wireless/ipw2200.h
@@ -789,7 +789,7 @@
 	u8 bt_coexist_collision_thr;
 	u8 silence_threshold;
 	u8 accept_all_mgmt_bcpr;
-	u8 accept_all_mgtm_frames;
+	u8 accept_all_mgmt_frames;
 	u8 pass_noise_stats_to_host;
 	u8 reserved3;
 } __attribute__ ((packed));
@@ -1122,6 +1122,52 @@
 	u8 payload[0];
 } __attribute__ ((packed));
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+
+enum ipw_prom_filter {
+	IPW_PROM_CTL_HEADER_ONLY = (1 << 0),
+	IPW_PROM_MGMT_HEADER_ONLY = (1 << 1),
+	IPW_PROM_DATA_HEADER_ONLY = (1 << 2),
+	IPW_PROM_ALL_HEADER_ONLY = 0xf, /* bits 0..3 */
+	IPW_PROM_NO_TX = (1 << 4),
+	IPW_PROM_NO_RX = (1 << 5),
+	IPW_PROM_NO_CTL = (1 << 6),
+	IPW_PROM_NO_MGMT = (1 << 7),
+	IPW_PROM_NO_DATA = (1 << 8),
+};
+
+struct ipw_priv;
+struct ipw_prom_priv {
+	struct ipw_priv *priv;
+	struct ieee80211_device *ieee;
+	enum ipw_prom_filter filter;
+	int tx_packets;
+	int rx_packets;
+};
+#endif
+
+#if defined(CONFIG_IPW2200_RADIOTAP) || defined(CONFIG_IPW2200_PROMISCUOUS)
+/* Magic struct that slots into the radiotap header -- no reason
+ * to build this manually element by element, we can write it much
+ * more efficiently than we can parse it. ORDER MATTERS HERE
+ *
+ * When sent to us via the simulated Rx interface in sysfs, the entire
+ * structure is provided regardless of any bits unset.
+ */
+struct ipw_rt_hdr {
+	struct ieee80211_radiotap_header rt_hdr;
+	u64 rt_tsf;      /* TSF */
+	u8 rt_flags;	/* radiotap packet flags */
+	u8 rt_rate;	/* rate in 500kb/s */
+	u16 rt_channel;	/* channel in mhz */
+	u16 rt_chbitmask;	/* channel bitfield */
+	s8 rt_dbmsignal;	/* signal in dbM, kluged to signed */
+	s8 rt_dbmnoise;
+	u8 rt_antenna;	/* antenna number */
+	u8 payload[0];  /* payload... */
+} __attribute__ ((packed));
+#endif
+
 struct ipw_priv {
 	/* ieee device used by generic ieee processing code */
 	struct ieee80211_device *ieee;
@@ -1133,6 +1179,12 @@
 	struct pci_dev *pci_dev;
 	struct net_device *net_dev;
 
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+	/* Promiscuous mode */
+	struct ipw_prom_priv *prom_priv;
+	struct net_device *prom_net_dev;
+#endif
+
 	/* pci hardware address support */
 	void __iomem *hw_base;
 	unsigned long hw_len;
@@ -1153,11 +1205,9 @@
 	u32 config;
 	u32 capability;
 
-	u8 last_rx_rssi;
-	u8 last_noise;
 	struct average average_missed_beacons;
-	struct average average_rssi;
-	struct average average_noise;
+	s16 exp_avg_rssi;
+	s16 exp_avg_noise;
 	u32 port_type;
 	int rx_bufs_min;	  /**< minimum number of bufs in Rx queue */
 	int rx_pend_max;	  /**< maximum pending buffers for one IRQ */
@@ -1308,6 +1358,29 @@
 
 /* debug macros */
 
+/* Debug and printf string expansion helpers for printing bitfields */
+#define BIT_FMT8 "%c%c%c%c-%c%c%c%c"
+#define BIT_FMT16 BIT_FMT8 ":" BIT_FMT8
+#define BIT_FMT32 BIT_FMT16 " " BIT_FMT16
+
+#define BITC(x,y) (((x>>y)&1)?'1':'0')
+#define BIT_ARG8(x) \
+BITC(x,7),BITC(x,6),BITC(x,5),BITC(x,4),\
+BITC(x,3),BITC(x,2),BITC(x,1),BITC(x,0)
+
+#define BIT_ARG16(x) \
+BITC(x,15),BITC(x,14),BITC(x,13),BITC(x,12),\
+BITC(x,11),BITC(x,10),BITC(x,9),BITC(x,8),\
+BIT_ARG8(x)
+
+#define BIT_ARG32(x) \
+BITC(x,31),BITC(x,30),BITC(x,29),BITC(x,28),\
+BITC(x,27),BITC(x,26),BITC(x,25),BITC(x,24),\
+BITC(x,23),BITC(x,22),BITC(x,21),BITC(x,20),\
+BITC(x,19),BITC(x,18),BITC(x,17),BITC(x,16),\
+BIT_ARG16(x)
+
+
 #ifdef CONFIG_IPW2200_DEBUG
 #define IPW_DEBUG(level, fmt, args...) \
 do { if (ipw_debug_level & (level)) \
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 06523e2..d4c13ff 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -201,41 +201,12 @@
 /* Data types                                                       */
 /********************************************************************/
 
-/* Used in Event handling.
- * We avoid nested structures as they break on ARM -- Moustafa */
-struct hermes_tx_descriptor_802_11 {
-	/* hermes_tx_descriptor */
-	__le16 status;
-	__le16 reserved1;
-	__le16 reserved2;
-	__le32 sw_support;
-	u8 retry_count;
-	u8 tx_rate;
-	__le16 tx_control;
-
-	/* ieee80211_hdr */
+/* Beginning of the Tx descriptor, used in TxExc handling */
+struct hermes_txexc_data {
+	struct hermes_tx_descriptor desc;
 	__le16 frame_ctl;
 	__le16 duration_id;
 	u8 addr1[ETH_ALEN];
-	u8 addr2[ETH_ALEN];
-	u8 addr3[ETH_ALEN];
-	__le16 seq_ctl;
-	u8 addr4[ETH_ALEN];
-
-	__le16 data_len;
-
-	/* ethhdr */
-	u8 h_dest[ETH_ALEN];	/* destination eth addr */
-	u8 h_source[ETH_ALEN];	/* source ether addr    */
-	__be16 h_proto;		/* packet type ID field */
-
-	/* p8022_hdr */
-	u8 dsap;
-	u8 ssap;
-	u8 ctrl;
-	u8 oui[3];
-
-	__be16 ethertype;
 } __attribute__ ((packed));
 
 /* Rx frame header except compatibility 802.3 header */
@@ -450,53 +421,39 @@
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 txfid = priv->txfid;
-	char *p;
 	struct ethhdr *eh;
-	int len, data_len, data_off;
+	int 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;
+		return NETDEV_TX_BUSY;
 	}
 	
 	if (netif_queue_stopped(dev)) {
 		printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", 
 		       dev->name);
-		TRACE_EXIT(dev->name);
-		return 1;
+		return NETDEV_TX_BUSY;
 	}
 	
 	if (orinoco_lock(priv, &flags) != 0) {
 		printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
 		       dev->name);
-		TRACE_EXIT(dev->name);
-		return 1;
+		return NETDEV_TX_BUSY;
 	}
 
 	if (! netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) {
 		/* 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;
+		goto drop;
 	}
 
-	/* Length of the packet body */
-	/* FIXME: what if the skb is smaller than this? */
-	len = max_t(int, ALIGN(skb->len, 2), ETH_ZLEN);
-	skb = skb_padto(skb, len);
-	if (skb == NULL)
-		goto fail;
-	len -= ETH_HLEN;
+	/* Check packet length */
+	if (skb->len < ETH_HLEN)
+		goto drop;
 
 	eh = (struct ethhdr *)skb->data;
 
@@ -507,8 +464,7 @@
 		if (net_ratelimit())
 			printk(KERN_ERR "%s: Error %d writing Tx descriptor "
 			       "to BAP\n", dev->name, err);
-		stats->tx_errors++;
-		goto fail;
+		goto busy;
 	}
 
 	/* Clear the 802.11 header and data length fields - some
@@ -519,50 +475,38 @@
 
 	/* Encapsulate Ethernet-II frames */
 	if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
-		struct header_struct hdr;
-		data_len = len;
-		data_off = HERMES_802_3_OFFSET + sizeof(hdr);
-		p = skb->data + ETH_HLEN;
+		struct header_struct {
+			struct ethhdr eth;	/* 802.3 header */
+			u8 encap[6];		/* 802.2 header */
+		} __attribute__ ((packed)) hdr;
 
-		/* 802.3 header */
-		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.dsap, &encaps_hdr, sizeof(encaps_hdr));
-			
-		hdr.ethertype = eh->h_proto;
-		err  = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
-					 txfid, HERMES_802_3_OFFSET);
+		/* Strip destination and source from the data */
+		skb_pull(skb, 2 * ETH_ALEN);
+		data_off = HERMES_802_2_OFFSET + sizeof(encaps_hdr);
+
+		/* And move them to a separate header */
+		memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
+		hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len);
+		memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
+
+		err = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
+					txfid, HERMES_802_3_OFFSET);
 		if (err) {
 			if (net_ratelimit())
 				printk(KERN_ERR "%s: Error %d writing packet "
 				       "header to BAP\n", dev->name, err);
-			stats->tx_errors++;
-			goto fail;
+			goto busy;
 		}
-		/* Actual xfer length - allow for padding */
-		len = ALIGN(data_len, 2);
-		if (len < ETH_ZLEN - ETH_HLEN)
-			len = ETH_ZLEN - ETH_HLEN;
 	} else { /* IEEE 802.3 frame */
-		data_len = len + ETH_HLEN;
 		data_off = HERMES_802_3_OFFSET;
-		p = skb->data;
-		/* Actual xfer length - round up for odd length packets */
-		len = ALIGN(data_len, 2);
-		if (len < ETH_ZLEN)
-			len = ETH_ZLEN;
 	}
 
-	err = hermes_bap_pwrite_pad(hw, USER_BAP, p, data_len, len,
+	err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
 				txfid, data_off);
 	if (err) {
 		printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
 		       dev->name, err);
-		stats->tx_errors++;
-		goto fail;
+		goto busy;
 	}
 
 	/* Finally, we actually initiate the send */
@@ -575,25 +519,27 @@
 		if (net_ratelimit())
 			printk(KERN_ERR "%s: Error %d transmitting packet\n",
 				dev->name, err);
-		stats->tx_errors++;
-		goto fail;
+		goto busy;
 	}
 
 	dev->trans_start = jiffies;
-	stats->tx_bytes += data_off + data_len;
+	stats->tx_bytes += data_off + skb->len;
+	goto ok;
 
+ drop:
+	stats->tx_errors++;
+	stats->tx_dropped++;
+
+ ok:
 	orinoco_unlock(priv, &flags);
-
 	dev_kfree_skb(skb);
+	return NETDEV_TX_OK;
 
-	TRACE_EXIT(dev->name);
-
-	return 0;
- fail:
-	TRACE_EXIT(dev->name);
-
+ busy:
+	if (err == -EIO)
+		schedule_work(&priv->reset_work);
 	orinoco_unlock(priv, &flags);
-	return err;
+	return NETDEV_TX_BUSY;
 }
 
 static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
@@ -629,7 +575,7 @@
 	struct net_device_stats *stats = &priv->stats;
 	u16 fid = hermes_read_regn(hw, TXCOMPLFID);
 	u16 status;
-	struct hermes_tx_descriptor_802_11 hdr;
+	struct hermes_txexc_data hdr;
 	int err = 0;
 
 	if (fid == DUMMY_FID)
@@ -637,8 +583,7 @@
 
 	/* Read part of the frame header - we need status and addr1 */
 	err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
-			       offsetof(struct hermes_tx_descriptor_802_11,
-					addr2),
+			       sizeof(struct hermes_txexc_data),
 			       fid, 0);
 
 	hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
@@ -658,7 +603,7 @@
 	 * exceeded, because that's the only status that really mean
 	 * that this particular node went away.
 	 * Other errors means that *we* screwed up. - Jean II */
-	status = le16_to_cpu(hdr.status);
+	status = le16_to_cpu(hdr.desc.status);
 	if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
 		union iwreq_data	wrqu;
 
@@ -1400,16 +1345,12 @@
 	return 0;
 }
 
-int orinoco_reinit_firmware(struct net_device *dev)
+static int orinoco_allocate_fid(struct net_device *dev)
 {
 	struct orinoco_private *priv = netdev_priv(dev);
 	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 && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
 		/* Try workaround for old Symbol firmware bug */
@@ -1428,6 +1369,19 @@
 	return err;
 }
 
+int orinoco_reinit_firmware(struct net_device *dev)
+{
+	struct orinoco_private *priv = netdev_priv(dev);
+	struct hermes *hw = &priv->hw;
+	int err;
+
+	err = hermes_init(hw);
+	if (!err)
+		err = orinoco_allocate_fid(dev);
+
+	return err;
+}
+
 static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
@@ -2274,14 +2228,12 @@
 	u16 reclen;
 	int len;
 
-	TRACE_ENTER(dev->name);
-
 	/* No need to lock, the hw_unavailable flag is already set in
 	 * alloc_orinocodev() */
 	priv->nicbuf_size = IEEE80211_FRAME_LEN + ETH_HLEN;
 
 	/* Initialize the firmware */
-	err = orinoco_reinit_firmware(dev);
+	err = hermes_init(hw);
 	if (err != 0) {
 		printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n",
 		       dev->name, err);
@@ -2339,6 +2291,13 @@
 
 	printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick);
 
+	err = orinoco_allocate_fid(dev);
+	if (err) {
+		printk(KERN_ERR "%s: failed to allocate NIC buffer!\n",
+		       dev->name);
+		goto out;
+	}
+
 	/* Get allowed channels */
 	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
 				  &priv->channel_mask);
@@ -2429,7 +2388,6 @@
 	printk(KERN_DEBUG "%s: ready\n", dev->name);
 
  out:
-	TRACE_EXIT(dev->name);
 	return err;
 }
 
@@ -2797,8 +2755,6 @@
 	int numrates;
 	int i, k;
 
-	TRACE_ENTER(dev->name);
-
 	rrq->length = sizeof(struct iw_range);
 	memset(range, 0, sizeof(struct iw_range));
 
@@ -2888,8 +2844,6 @@
 	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
 	IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
 
-	TRACE_EXIT(dev->name);
-
 	return 0;
 }
 
@@ -3071,8 +3025,6 @@
 	int err = 0;
 	unsigned long flags;
 
-	TRACE_ENTER(dev->name);
-
 	if (netif_running(dev)) {
 		err = orinoco_hw_get_essid(priv, &active, essidbuf);
 		if (err)
@@ -3087,8 +3039,6 @@
 	erq->flags = 1;
 	erq->length = strlen(essidbuf) + 1;
 
-	TRACE_EXIT(dev->name);
-	
 	return 0;
 }
 
@@ -4349,69 +4299,6 @@
 };
 
 /********************************************************************/
-/* Debugging                                                        */
-/********************************************************************/
-
-#if 0
-static void show_rx_frame(struct orinoco_rxframe_hdr *frame)
-{
-	printk(KERN_DEBUG "RX descriptor:\n");
-	printk(KERN_DEBUG "  status      = 0x%04x\n", frame->desc.status);
-	printk(KERN_DEBUG "  time        = 0x%08x\n", frame->desc.time);
-	printk(KERN_DEBUG "  silence     = 0x%02x\n", frame->desc.silence);
-	printk(KERN_DEBUG "  signal      = 0x%02x\n", frame->desc.signal);
-	printk(KERN_DEBUG "  rate        = 0x%02x\n", frame->desc.rate);
-	printk(KERN_DEBUG "  rxflow      = 0x%02x\n", frame->desc.rxflow);
-	printk(KERN_DEBUG "  reserved    = 0x%08x\n", frame->desc.reserved);
-
-	printk(KERN_DEBUG "IEEE 802.11 header:\n");
-	printk(KERN_DEBUG "  frame_ctl   = 0x%04x\n",
-	       frame->p80211.frame_ctl);
-	printk(KERN_DEBUG "  duration_id = 0x%04x\n",
-	       frame->p80211.duration_id);
-	printk(KERN_DEBUG "  addr1       = %02x:%02x:%02x:%02x:%02x:%02x\n",
-	       frame->p80211.addr1[0], frame->p80211.addr1[1],
-	       frame->p80211.addr1[2], frame->p80211.addr1[3],
-	       frame->p80211.addr1[4], frame->p80211.addr1[5]);
-	printk(KERN_DEBUG "  addr2       = %02x:%02x:%02x:%02x:%02x:%02x\n",
-	       frame->p80211.addr2[0], frame->p80211.addr2[1],
-	       frame->p80211.addr2[2], frame->p80211.addr2[3],
-	       frame->p80211.addr2[4], frame->p80211.addr2[5]);
-	printk(KERN_DEBUG "  addr3       = %02x:%02x:%02x:%02x:%02x:%02x\n",
-	       frame->p80211.addr3[0], frame->p80211.addr3[1],
-	       frame->p80211.addr3[2], frame->p80211.addr3[3],
-	       frame->p80211.addr3[4], frame->p80211.addr3[5]);
-	printk(KERN_DEBUG "  seq_ctl     = 0x%04x\n",
-	       frame->p80211.seq_ctl);
-	printk(KERN_DEBUG "  addr4       = %02x:%02x:%02x:%02x:%02x:%02x\n",
-	       frame->p80211.addr4[0], frame->p80211.addr4[1],
-	       frame->p80211.addr4[2], frame->p80211.addr4[3],
-	       frame->p80211.addr4[4], frame->p80211.addr4[5]);
-	printk(KERN_DEBUG "  data_len    = 0x%04x\n",
-	       frame->p80211.data_len);
-
-	printk(KERN_DEBUG "IEEE 802.3 header:\n");
-	printk(KERN_DEBUG "  dest        = %02x:%02x:%02x:%02x:%02x:%02x\n",
-	       frame->p8023.h_dest[0], frame->p8023.h_dest[1],
-	       frame->p8023.h_dest[2], frame->p8023.h_dest[3],
-	       frame->p8023.h_dest[4], frame->p8023.h_dest[5]);
-	printk(KERN_DEBUG "  src         = %02x:%02x:%02x:%02x:%02x:%02x\n",
-	       frame->p8023.h_source[0], frame->p8023.h_source[1],
-	       frame->p8023.h_source[2], frame->p8023.h_source[3],
-	       frame->p8023.h_source[4], frame->p8023.h_source[5]);
-	printk(KERN_DEBUG "  len         = 0x%04x\n", frame->p8023.h_proto);
-
-	printk(KERN_DEBUG "IEEE 802.2 LLC/SNAP header:\n");
-	printk(KERN_DEBUG "  DSAP        = 0x%02x\n", frame->p8022.dsap);
-	printk(KERN_DEBUG "  SSAP        = 0x%02x\n", frame->p8022.ssap);
-	printk(KERN_DEBUG "  ctrl        = 0x%02x\n", frame->p8022.ctrl);
-	printk(KERN_DEBUG "  OUI         = %02x:%02x:%02x\n",
-	       frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]);
-	printk(KERN_DEBUG "  ethertype  = 0x%04x\n", frame->ethertype);
-}
-#endif /* 0 */
-
-/********************************************************************/
 /* Module initialization                                            */
 /********************************************************************/
 
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index f5d856d..16db3e1 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -7,7 +7,7 @@
 #ifndef _ORINOCO_H
 #define _ORINOCO_H
 
-#define DRIVER_VERSION "0.15rc3"
+#define DRIVER_VERSION "0.15"
 
 #include <linux/netdevice.h>
 #include <linux/wireless.h>
@@ -30,20 +30,6 @@
 	char data[ORINOCO_MAX_KEY_SIZE];
 } __attribute__ ((packed));
 
-struct header_struct {
-	/* 802.3 */
-	u8 dest[ETH_ALEN];
-	u8 src[ETH_ALEN];
-	__be16 len;
-	/* 802.2 */
-	u8 dsap;
-	u8 ssap;
-	u8 ctrl;
-	/* SNAP */
-	u8 oui[3];
-	unsigned short ethertype;
-} __attribute__ ((packed));
-
 typedef enum {
 	FIRMWARE_TYPE_AGERE,
 	FIRMWARE_TYPE_INTERSIL,
@@ -132,9 +118,6 @@
 #define DEBUG(n, args...) do { } while (0)
 #endif	/* ORINOCO_DEBUG */
 
-#define TRACE_ENTER(devname) DEBUG(2, "%s: -> %s()\n", devname, __FUNCTION__);
-#define TRACE_EXIT(devname)  DEBUG(2, "%s: <- %s()\n", devname, __FUNCTION__);
-
 /********************************************************************/
 /* Exported prototypes                                              */
 /********************************************************************/
diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c
index 434f7d7..d2c48ac 100644
--- a/drivers/net/wireless/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco_cs.c
@@ -178,13 +178,10 @@
 	int last_fn, last_ret;
 	u_char buf[64];
 	config_info_t conf;
-	cisinfo_t info;
 	tuple_t tuple;
 	cisparse_t parse;
 	void __iomem *mem;
 
-	CS_CHECK(ValidateCIS, pcmcia_validate_cis(link, &info));
-
 	/*
 	 * This reads the card's CONFIG tuple to find its
 	 * configuration registers.
@@ -234,12 +231,6 @@
 			goto next_entry;
 		link->conf.ConfigIndex = cfg->index;
 
-		/* Does this card need audio output? */
-		if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
-			link->conf.Attributes |= CONF_ENABLE_SPKR;
-			link->conf.Status = CCSR_AUDIO_ENA;
-		}
-
 		/* Use power settings for Vcc and Vpp if present */
 		/* Note that the CIS values need to be rescaled */
 		if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
diff --git a/drivers/net/wireless/orinoco_nortel.c b/drivers/net/wireless/orinoco_nortel.c
index d1a670b..1596182 100644
--- a/drivers/net/wireless/orinoco_nortel.c
+++ b/drivers/net/wireless/orinoco_nortel.c
@@ -1,9 +1,8 @@
 /* orinoco_nortel.c
- * 
+ *
  * Driver for Prism II devices which would usually be driven by orinoco_cs,
  * but are connected to the PCI bus by a PCI-to-PCMCIA adapter used in
  * Nortel emobility, Symbol LA-4113 and Symbol LA-4123.
- * but are connected to the PCI bus by a Nortel PCI-PCMCIA-Adapter. 
  *
  * Copyright (C) 2002 Tobias Hoffmann
  *           (C) 2003 Christoph Jungegger <disdos@traum404.de>
@@ -50,67 +49,62 @@
 #include <pcmcia/cisreg.h>
 
 #include "orinoco.h"
+#include "orinoco_pci.h"
 
 #define COR_OFFSET    (0xe0)	/* COR attribute offset of Prism2 PC card */
 #define COR_VALUE     (COR_LEVEL_REQ | COR_FUNC_ENA)	/* Enable PC card with interrupt in level trigger */
 
 
-/* Nortel specific data */
-struct nortel_pci_card {
-	unsigned long iobase1;
-	unsigned long iobase2;
-};
-
 /*
- * Do a soft reset of the PCI card using the Configuration Option Register
+ * Do a soft reset of the card using the Configuration Option Register
  * We need this to get going...
  * This is the part of the code that is strongly inspired from wlan-ng
  *
  * Note bis : Don't try to access HERMES_CMD during the reset phase.
  * It just won't work !
  */
-static int nortel_pci_cor_reset(struct orinoco_private *priv)
+static int orinoco_nortel_cor_reset(struct orinoco_private *priv)
 {
-	struct nortel_pci_card *card = priv->card;
+	struct orinoco_pci_card *card = priv->card;
 
-	/* Assert the reset until the card notice */
-	outw_p(8, card->iobase1 + 2);
-	inw(card->iobase2 + COR_OFFSET);
-	outw_p(0x80, card->iobase2 + COR_OFFSET);
+	/* Assert the reset until the card notices */
+	iowrite16(8, card->bridge_io + 2);
+	ioread16(card->attr_io + COR_OFFSET);
+	iowrite16(0x80, card->attr_io + COR_OFFSET);
 	mdelay(1);
 
 	/* Give time for the card to recover from this hard effort */
-	outw_p(0, card->iobase2 + COR_OFFSET);
-	outw_p(0, card->iobase2 + COR_OFFSET);
+	iowrite16(0, card->attr_io + COR_OFFSET);
+	iowrite16(0, card->attr_io + COR_OFFSET);
 	mdelay(1);
 
-	/* set COR as usual */
-	outw_p(COR_VALUE, card->iobase2 + COR_OFFSET);
-	outw_p(COR_VALUE, card->iobase2 + COR_OFFSET);
+	/* Set COR as usual */
+	iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
+	iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
 	mdelay(1);
 
-	outw_p(0x228, card->iobase1 + 2);
+	iowrite16(0x228, card->bridge_io + 2);
 
 	return 0;
 }
 
-static int nortel_pci_hw_init(struct nortel_pci_card *card)
+static int orinoco_nortel_hw_init(struct orinoco_pci_card *card)
 {
 	int i;
 	u32 reg;
 
-	/* setup bridge */
-	if (inw(card->iobase1) & 1) {
+	/* Setup bridge */
+	if (ioread16(card->bridge_io) & 1) {
 		printk(KERN_ERR PFX "brg1 answer1 wrong\n");
 		return -EBUSY;
 	}
-	outw_p(0x118, card->iobase1 + 2);
-	outw_p(0x108, card->iobase1 + 2);
+	iowrite16(0x118, card->bridge_io + 2);
+	iowrite16(0x108, card->bridge_io + 2);
 	mdelay(30);
-	outw_p(0x8, card->iobase1 + 2);
+	iowrite16(0x8, card->bridge_io + 2);
 	for (i = 0; i < 30; i++) {
 		mdelay(30);
-		if (inw(card->iobase1) & 0x10) {
+		if (ioread16(card->bridge_io) & 0x10) {
 			break;
 		}
 	}
@@ -118,42 +112,42 @@
 		printk(KERN_ERR PFX "brg1 timed out\n");
 		return -EBUSY;
 	}
-	if (inw(card->iobase2 + 0xe0) & 1) {
+	if (ioread16(card->attr_io + COR_OFFSET) & 1) {
 		printk(KERN_ERR PFX "brg2 answer1 wrong\n");
 		return -EBUSY;
 	}
-	if (inw(card->iobase2 + 0xe2) & 1) {
+	if (ioread16(card->attr_io + COR_OFFSET + 2) & 1) {
 		printk(KERN_ERR PFX "brg2 answer2 wrong\n");
 		return -EBUSY;
 	}
-	if (inw(card->iobase2 + 0xe4) & 1) {
+	if (ioread16(card->attr_io + COR_OFFSET + 4) & 1) {
 		printk(KERN_ERR PFX "brg2 answer3 wrong\n");
 		return -EBUSY;
 	}
 
-	/* set the PCMCIA COR-Register */
-	outw_p(COR_VALUE, card->iobase2 + COR_OFFSET);
+	/* Set the PCMCIA COR register */
+	iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
 	mdelay(1);
-	reg = inw(card->iobase2 + COR_OFFSET);
+	reg = ioread16(card->attr_io + COR_OFFSET);
 	if (reg != COR_VALUE) {
 		printk(KERN_ERR PFX "Error setting COR value (reg=%x)\n",
 		       reg);
 		return -EBUSY;
 	}
 
-	/* set leds */
-	outw_p(1, card->iobase1 + 10);
+	/* Set LEDs */
+	iowrite16(1, card->bridge_io + 10);
 	return 0;
 }
 
-static int nortel_pci_init_one(struct pci_dev *pdev,
-			       const struct pci_device_id *ent)
+static int orinoco_nortel_init_one(struct pci_dev *pdev,
+				   const struct pci_device_id *ent)
 {
 	int err;
 	struct orinoco_private *priv;
-	struct nortel_pci_card *card;
+	struct orinoco_pci_card *card;
 	struct net_device *dev;
-	void __iomem *iomem;
+	void __iomem *hermes_io, *bridge_io, *attr_io;
 
 	err = pci_enable_device(pdev);
 	if (err) {
@@ -162,19 +156,34 @@
 	}
 
 	err = pci_request_regions(pdev, DRIVER_NAME);
-	if (err != 0) {
+	if (err) {
 		printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
 		goto fail_resources;
 	}
 
-	iomem = pci_iomap(pdev, 2, 0);
-	if (!iomem) {
-		err = -ENOMEM;
-		goto fail_map_io;
+	bridge_io = pci_iomap(pdev, 0, 0);
+	if (!bridge_io) {
+		printk(KERN_ERR PFX "Cannot map bridge registers\n");
+		err = -EIO;
+		goto fail_map_bridge;
+	}
+
+	attr_io = pci_iomap(pdev, 1, 0);
+	if (!attr_io) {
+		printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n");
+		err = -EIO;
+		goto fail_map_attr;
+	}
+
+	hermes_io = pci_iomap(pdev, 2, 0);
+	if (!hermes_io) {
+		printk(KERN_ERR PFX "Cannot map chipset registers\n");
+		err = -EIO;
+		goto fail_map_hermes;
 	}
 
 	/* Allocate network device */
-	dev = alloc_orinocodev(sizeof(*card), nortel_pci_cor_reset);
+	dev = alloc_orinocodev(sizeof(*card), orinoco_nortel_cor_reset);
 	if (!dev) {
 		printk(KERN_ERR PFX "Cannot allocate network device\n");
 		err = -ENOMEM;
@@ -183,16 +192,12 @@
 
 	priv = netdev_priv(dev);
 	card = priv->card;
-	card->iobase1 = pci_resource_start(pdev, 0);
-	card->iobase2 = pci_resource_start(pdev, 1);
-	dev->base_addr = pci_resource_start(pdev, 2);
+	card->bridge_io = bridge_io;
+	card->attr_io = attr_io;
 	SET_MODULE_OWNER(dev);
 	SET_NETDEV_DEV(dev, &pdev->dev);
 
-	hermes_struct_init(&priv->hw, iomem, HERMES_16BIT_REGSPACING);
-
-	printk(KERN_DEBUG PFX "Detected Nortel PCI device at %s irq:%d, "
-	       "io addr:0x%lx\n", pci_name(pdev), pdev->irq, dev->base_addr);
+	hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING);
 
 	err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
 			  dev->name, dev);
@@ -201,21 +206,20 @@
 		err = -EBUSY;
 		goto fail_irq;
 	}
-	dev->irq = pdev->irq;
+	orinoco_pci_setup_netdev(dev, pdev, 2);
 
-	err = nortel_pci_hw_init(card);
+	err = orinoco_nortel_hw_init(card);
 	if (err) {
 		printk(KERN_ERR PFX "Hardware initialization failed\n");
 		goto fail;
 	}
 
-	err = nortel_pci_cor_reset(priv);
+	err = orinoco_nortel_cor_reset(priv);
 	if (err) {
 		printk(KERN_ERR PFX "Initial reset failed\n");
 		goto fail;
 	}
 
-
 	err = register_netdev(dev);
 	if (err) {
 		printk(KERN_ERR PFX "Cannot register network device\n");
@@ -234,9 +238,15 @@
 	free_orinocodev(dev);
 
  fail_alloc:
-	pci_iounmap(pdev, iomem);
+	pci_iounmap(pdev, hermes_io);
 
- fail_map_io:
+ fail_map_hermes:
+	pci_iounmap(pdev, attr_io);
+
+ fail_map_attr:
+	pci_iounmap(pdev, bridge_io);
+
+ fail_map_bridge:
 	pci_release_regions(pdev);
 
  fail_resources:
@@ -245,26 +255,27 @@
 	return err;
 }
 
-static void __devexit nortel_pci_remove_one(struct pci_dev *pdev)
+static void __devexit orinoco_nortel_remove_one(struct pci_dev *pdev)
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
 	struct orinoco_private *priv = netdev_priv(dev);
-	struct nortel_pci_card *card = priv->card;
+	struct orinoco_pci_card *card = priv->card;
 
-	/* clear leds */
-	outw_p(0, card->iobase1 + 10);
+	/* Clear LEDs */
+	iowrite16(0, card->bridge_io + 10);
 
 	unregister_netdev(dev);
 	free_irq(dev->irq, dev);
 	pci_set_drvdata(pdev, NULL);
 	free_orinocodev(dev);
 	pci_iounmap(pdev, priv->hw.iobase);
+	pci_iounmap(pdev, card->attr_io);
+	pci_iounmap(pdev, card->bridge_io);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
 }
 
-
-static struct pci_device_id nortel_pci_id_table[] = {
+static struct pci_device_id orinoco_nortel_id_table[] = {
 	/* Nortel emobility PCI */
 	{0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,},
 	/* Symbol LA-4123 PCI */
@@ -272,13 +283,15 @@
 	{0,},
 };
 
-MODULE_DEVICE_TABLE(pci, nortel_pci_id_table);
+MODULE_DEVICE_TABLE(pci, orinoco_nortel_id_table);
 
-static struct pci_driver nortel_pci_driver = {
-	.name = DRIVER_NAME,
-	.id_table = nortel_pci_id_table,
-	.probe = nortel_pci_init_one,
-	.remove = __devexit_p(nortel_pci_remove_one),
+static struct pci_driver orinoco_nortel_driver = {
+	.name		= DRIVER_NAME,
+	.id_table	= orinoco_nortel_id_table,
+	.probe		= orinoco_nortel_init_one,
+	.remove		= __devexit_p(orinoco_nortel_remove_one),
+	.suspend	= orinoco_pci_suspend,
+	.resume		= orinoco_pci_resume,
 };
 
 static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
@@ -288,20 +301,19 @@
     ("Driver for wireless LAN cards using the Nortel PCI bridge");
 MODULE_LICENSE("Dual MPL/GPL");
 
-static int __init nortel_pci_init(void)
+static int __init orinoco_nortel_init(void)
 {
 	printk(KERN_DEBUG "%s\n", version);
-	return pci_module_init(&nortel_pci_driver);
+	return pci_module_init(&orinoco_nortel_driver);
 }
 
-static void __exit nortel_pci_exit(void)
+static void __exit orinoco_nortel_exit(void)
 {
-	pci_unregister_driver(&nortel_pci_driver);
-	ssleep(1);
+	pci_unregister_driver(&orinoco_nortel_driver);
 }
 
-module_init(nortel_pci_init);
-module_exit(nortel_pci_exit);
+module_init(orinoco_nortel_init);
+module_exit(orinoco_nortel_exit);
 
 /*
  * Local variables:
diff --git a/drivers/net/wireless/orinoco_pci.c b/drivers/net/wireless/orinoco_pci.c
index 5362c21..df37b95 100644
--- a/drivers/net/wireless/orinoco_pci.c
+++ b/drivers/net/wireless/orinoco_pci.c
@@ -1,11 +1,11 @@
 /* orinoco_pci.c
  * 
- * Driver for Prism II devices that have a direct PCI interface
- * (i.e., not in a Pcmcia or PLX bridge)
+ * Driver for Prism 2.5/3 devices that have a direct PCI interface
+ * (i.e. these are not PCMCIA cards in a PCMCIA-to-PCI bridge).
+ * The card contains only one PCI region, which contains all the usual
+ * hermes registers, as well as the COR register.
  *
- * Specifically here we're talking about the Linksys WMP11
- *
- * Current maintainers (as of 29 September 2003) are:
+ * Current maintainers are:
  * 	Pavel Roskin <proski AT gnu.org>
  * and	David Gibson <hermes AT gibson.dropbear.id.au>
  *
@@ -41,54 +41,6 @@
  * under either the MPL or the GPL.
  */
 
-/*
- * Theory of operation...
- * -------------------
- * Maybe you had a look in orinoco_plx. Well, this is totally different...
- *
- * The card contains only one PCI region, which contains all the usual
- * hermes registers.
- *
- * The driver will memory map this region in normal memory. Because
- * the hermes registers are mapped in normal memory and not in ISA I/O
- * post space, we can't use the usual inw/outw macros and we need to
- * use readw/writew.
- * This slight difference force us to compile our own version of
- * hermes.c with the register access macro changed. That's a bit
- * hackish but works fine.
- *
- * Note that the PCI region is pretty big (4K). That's much more than
- * the usual set of hermes register (0x0 -> 0x3E). I've got a strong
- * suspicion that the whole memory space of the adapter is in fact in
- * this region. Accessing directly the adapter memory instead of going
- * through the usual register would speed up significantely the
- * operations...
- *
- * Finally, the card looks like this :
------------------------
-  Bus  0, device  14, function  0:
-    Network controller: PCI device 1260:3873 (Harris Semiconductor) (rev 1).
-      IRQ 11.
-      Master Capable.  Latency=248.  
-      Prefetchable 32 bit memory at 0xffbcc000 [0xffbccfff].
------------------------
-00:0e.0 Network controller: Harris Semiconductor: Unknown device 3873 (rev 01)
-        Subsystem: Unknown device 1737:3874
-        Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B-
-        Status: Cap+ 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR-
-        Latency: 248 set, cache line size 08
-        Interrupt: pin A routed to IRQ 11
-        Region 0: Memory at ffbcc000 (32-bit, prefetchable) [size=4K]
-        Capabilities: [dc] Power Management version 2
-                Flags: PMEClk- AuxPwr- DSI- D1+ D2+ PME+
-                Status: D0 PME-Enable- DSel=0 DScale=0 PME-
------------------------
- *
- * That's all..
- *
- * Jean II
- */
-
 #define DRIVER_NAME "orinoco_pci"
 #define PFX DRIVER_NAME ": "
 
@@ -100,12 +52,14 @@
 #include <linux/pci.h>
 
 #include "orinoco.h"
+#include "orinoco_pci.h"
 
-/* All the magic there is from wlan-ng */
-/* Magic offset of the reset register of the PCI card */
+/* Offset of the COR register of the PCI card */
 #define HERMES_PCI_COR		(0x26)
-/* Magic bitmask to reset the card */
+
+/* Bitmask to reset the card */
 #define HERMES_PCI_COR_MASK	(0x0080)
+
 /* Magic timeouts for doing the reset.
  * Those times are straight from wlan-ng, and it is claimed that they
  * are necessary. Alan will kill me. Take your time and grab a coffee. */
@@ -113,13 +67,8 @@
 #define HERMES_PCI_COR_OFFT	(500)		/* ms */
 #define HERMES_PCI_COR_BUSYT	(500)		/* ms */
 
-/* Orinoco PCI specific data */
-struct orinoco_pci_card {
-	void __iomem *pci_ioaddr;
-};
-
 /*
- * Do a soft reset of the PCI card using the Configuration Option Register
+ * Do a soft reset of the card using the Configuration Option Register
  * We need this to get going...
  * This is the part of the code that is strongly inspired from wlan-ng
  *
@@ -131,14 +80,13 @@
  * Note bis : Don't try to access HERMES_CMD during the reset phase.
  * It just won't work !
  */
-static int
-orinoco_pci_cor_reset(struct orinoco_private *priv)
+static int orinoco_pci_cor_reset(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
-	unsigned long	timeout;
-	u16	reg;
+	unsigned long timeout;
+	u16 reg;
 
-	/* Assert the reset until the card notice */
+	/* Assert the reset until the card notices */
 	hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK);
 	mdelay(HERMES_PCI_COR_ONT);
 
@@ -163,19 +111,14 @@
 	return 0;
 }
 
-/*
- * Initialise a card. Mostly similar to PLX code.
- */
 static int orinoco_pci_init_one(struct pci_dev *pdev,
 				const struct pci_device_id *ent)
 {
-	int err = 0;
-	unsigned long pci_iorange;
-	u16 __iomem *pci_ioaddr = NULL;
-	unsigned long pci_iolen;
-	struct orinoco_private *priv = NULL;
+	int err;
+	struct orinoco_private *priv;
 	struct orinoco_pci_card *card;
-	struct net_device *dev = NULL;
+	struct net_device *dev;
+	void __iomem *hermes_io;
 
 	err = pci_enable_device(pdev);
 	if (err) {
@@ -184,39 +127,32 @@
 	}
 
 	err = pci_request_regions(pdev, DRIVER_NAME);
-	if (err != 0) {
+	if (err) {
 		printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
 		goto fail_resources;
 	}
 
-	/* Resource 0 is mapped to the hermes registers */
-	pci_iorange = pci_resource_start(pdev, 0);
-	pci_iolen = pci_resource_len(pdev, 0);
-	pci_ioaddr = ioremap(pci_iorange, pci_iolen);
-	if (!pci_iorange) {
-		printk(KERN_ERR PFX "Cannot remap hardware registers\n");
-		goto fail_map;
+	hermes_io = pci_iomap(pdev, 0, 0);
+	if (!hermes_io) {
+		printk(KERN_ERR PFX "Cannot remap chipset registers\n");
+		err = -EIO;
+		goto fail_map_hermes;
 	}
 
 	/* Allocate network device */
 	dev = alloc_orinocodev(sizeof(*card), orinoco_pci_cor_reset);
-	if (! dev) {
+	if (!dev) {
+		printk(KERN_ERR PFX "Cannot allocate network device\n");
 		err = -ENOMEM;
 		goto fail_alloc;
 	}
 
 	priv = netdev_priv(dev);
 	card = priv->card;
-	card->pci_ioaddr = pci_ioaddr;
-	dev->mem_start = pci_iorange;
-	dev->mem_end = pci_iorange + pci_iolen - 1;
 	SET_MODULE_OWNER(dev);
 	SET_NETDEV_DEV(dev, &pdev->dev);
 
-	hermes_struct_init(&priv->hw, pci_ioaddr, HERMES_32BIT_REGSPACING);
-
-	printk(KERN_DEBUG PFX "Detected device %s, mem:0x%lx-0x%lx, irq %d\n",
-	       pci_name(pdev), dev->mem_start, dev->mem_end, pdev->irq);
+	hermes_struct_init(&priv->hw, hermes_io, HERMES_32BIT_REGSPACING);
 
 	err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
 			  dev->name, dev);
@@ -225,9 +161,8 @@
 		err = -EBUSY;
 		goto fail_irq;
 	}
-	dev->irq = pdev->irq;
+	orinoco_pci_setup_netdev(dev, pdev, 0);
 
-	/* Perform a COR reset to start the card */
 	err = orinoco_pci_cor_reset(priv);
 	if (err) {
 		printk(KERN_ERR PFX "Initial reset failed\n");
@@ -236,7 +171,7 @@
 
 	err = register_netdev(dev);
 	if (err) {
-		printk(KERN_ERR PFX "Failed to register net device\n");
+		printk(KERN_ERR PFX "Cannot register network device\n");
 		goto fail;
 	}
 
@@ -252,9 +187,9 @@
 	free_orinocodev(dev);
 
  fail_alloc:
-	iounmap(pci_ioaddr);
+	pci_iounmap(pdev, hermes_io);
 
- fail_map:
+ fail_map_hermes:
 	pci_release_regions(pdev);
 
  fail_resources:
@@ -267,87 +202,17 @@
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
 	struct orinoco_private *priv = netdev_priv(dev);
-	struct orinoco_pci_card *card = priv->card;
 
 	unregister_netdev(dev);
 	free_irq(dev->irq, dev);
 	pci_set_drvdata(pdev, NULL);
 	free_orinocodev(dev);
-	iounmap(card->pci_ioaddr);
+	pci_iounmap(pdev, priv->hw.iobase);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
 }
 
-static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state)
-{
-	struct net_device *dev = pci_get_drvdata(pdev);
-	struct orinoco_private *priv = netdev_priv(dev);
-	unsigned long flags;
-	int err;
-	
-
-	err = orinoco_lock(priv, &flags);
-	if (err) {
-		printk(KERN_ERR "%s: hw_unavailable on orinoco_pci_suspend\n",
-		       dev->name);
-		return err;
-	}
-
-	err = __orinoco_down(dev);
-	if (err)
-		printk(KERN_WARNING "%s: orinoco_pci_suspend(): Error %d downing interface\n",
-		       dev->name, err);
-	
-	netif_device_detach(dev);
-
-	priv->hw_unavailable++;
-	
-	orinoco_unlock(priv, &flags);
-
-	pci_save_state(pdev);
-	pci_set_power_state(pdev, PCI_D3hot);
-
-	return 0;
-}
-
-static int orinoco_pci_resume(struct pci_dev *pdev)
-{
-	struct net_device *dev = pci_get_drvdata(pdev);
-	struct orinoco_private *priv = netdev_priv(dev);
-	unsigned long flags;
-	int err;
-
-	printk(KERN_DEBUG "%s: Orinoco-PCI waking up\n", dev->name);
-
-	pci_set_power_state(pdev, 0);
-	pci_restore_state(pdev);
-
-	err = orinoco_reinit_firmware(dev);
-	if (err) {
-		printk(KERN_ERR "%s: Error %d re-initializing firmware on orinoco_pci_resume()\n",
-		       dev->name, err);
-		return err;
-	}
-
-	spin_lock_irqsave(&priv->lock, flags);
-
-	netif_device_attach(dev);
-
-	priv->hw_unavailable--;
-
-	if (priv->open && (! priv->hw_unavailable)) {
-		err = __orinoco_up(dev);
-		if (err)
-			printk(KERN_ERR "%s: Error %d restarting card on orinoco_pci_resume()\n",
-			       dev->name, err);
-	}
-	
-	spin_unlock_irqrestore(&priv->lock, flags);
-
-	return 0;
-}
-
-static struct pci_device_id orinoco_pci_pci_id_table[] = {
+static struct pci_device_id orinoco_pci_id_table[] = {
 	/* Intersil Prism 3 */
 	{0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,},
 	/* Intersil Prism 2.5 */
@@ -357,11 +222,11 @@
 	{0,},
 };
 
-MODULE_DEVICE_TABLE(pci, orinoco_pci_pci_id_table);
+MODULE_DEVICE_TABLE(pci, orinoco_pci_id_table);
 
 static struct pci_driver orinoco_pci_driver = {
 	.name		= DRIVER_NAME,
-	.id_table	= orinoco_pci_pci_id_table,
+	.id_table	= orinoco_pci_id_table,
 	.probe		= orinoco_pci_init_one,
 	.remove		= __devexit_p(orinoco_pci_remove_one),
 	.suspend	= orinoco_pci_suspend,
diff --git a/drivers/net/wireless/orinoco_pci.h b/drivers/net/wireless/orinoco_pci.h
new file mode 100644
index 0000000..b05a9a5
--- /dev/null
+++ b/drivers/net/wireless/orinoco_pci.h
@@ -0,0 +1,125 @@
+/* orinoco_pci.h
+ * 
+ * Common code for all Orinoco drivers for PCI devices, including
+ * both native PCI and PCMCIA-to-PCI bridges.
+ *
+ * Copyright (C) 2005, Pavel Roskin.
+ * See orinoco.c for license.
+ */
+
+#ifndef _ORINOCO_PCI_H
+#define _ORINOCO_PCI_H
+
+#include <linux/netdevice.h>
+
+/* Driver specific data */
+struct orinoco_pci_card {
+	void __iomem *bridge_io;
+	void __iomem *attr_io;
+};
+
+/* Set base address or memory range of the network device based on
+ * the PCI device it's using.  Specify BAR of the "main" resource.
+ * To be used after request_irq().  */
+static inline void orinoco_pci_setup_netdev(struct net_device *dev,
+					    struct pci_dev *pdev, int bar)
+{
+	char *range_type;
+	unsigned long start = pci_resource_start(pdev, bar);
+	unsigned long len = pci_resource_len(pdev, bar);
+	unsigned long flags = pci_resource_flags(pdev, bar);
+	unsigned long end = start + len - 1;
+
+	dev->irq = pdev->irq;
+	if (flags & IORESOURCE_IO) {
+		dev->base_addr = start;
+		range_type = "ports";
+	} else {
+		dev->mem_start = start;
+		dev->mem_end = end;
+		range_type = "memory";
+	}
+
+	printk(KERN_DEBUG PFX "%s: irq %d, %s 0x%lx-0x%lx\n",
+	       pci_name(pdev), pdev->irq, range_type, start, end);
+}
+
+static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct orinoco_private *priv = netdev_priv(dev);
+	unsigned long flags;
+	int err;
+
+	err = orinoco_lock(priv, &flags);
+	if (err) {
+		printk(KERN_ERR "%s: cannot lock hardware for suspend\n",
+		       dev->name);
+		return err;
+	}
+
+	err = __orinoco_down(dev);
+	if (err)
+		printk(KERN_WARNING "%s: error %d bringing interface down "
+		       "for suspend\n", dev->name, err);
+	
+	netif_device_detach(dev);
+
+	priv->hw_unavailable++;
+	
+	orinoco_unlock(priv, &flags);
+
+	free_irq(pdev->irq, dev);
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, PCI_D3hot);
+
+	return 0;
+}
+
+static int orinoco_pci_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct orinoco_private *priv = netdev_priv(dev);
+	unsigned long flags;
+	int err;
+
+	pci_set_power_state(pdev, 0);
+	pci_enable_device(pdev);
+	pci_restore_state(pdev);
+
+	err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
+			  dev->name, dev);
+	if (err) {
+		printk(KERN_ERR "%s: cannot re-allocate IRQ on resume\n",
+		       dev->name);
+		pci_disable_device(pdev);
+		return -EBUSY;
+	}
+
+	err = orinoco_reinit_firmware(dev);
+	if (err) {
+		printk(KERN_ERR "%s: error %d re-initializing firmware "
+		       "on resume\n", dev->name, err);
+		return err;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	netif_device_attach(dev);
+
+	priv->hw_unavailable--;
+
+	if (priv->open && (! priv->hw_unavailable)) {
+		err = __orinoco_up(dev);
+		if (err)
+			printk(KERN_ERR "%s: Error %d restarting card on resume\n",
+			       dev->name, err);
+	}
+	
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+#endif /* _ORINOCO_PCI_H */
diff --git a/drivers/net/wireless/orinoco_plx.c b/drivers/net/wireless/orinoco_plx.c
index 210e737..7b94050 100644
--- a/drivers/net/wireless/orinoco_plx.c
+++ b/drivers/net/wireless/orinoco_plx.c
@@ -3,7 +3,7 @@
  * Driver for Prism II devices which would usually be driven by orinoco_cs,
  * but are connected to the PCI bus by a PLX9052.
  *
- * Current maintainers (as of 29 September 2003) are:
+ * Current maintainers are:
  * 	Pavel Roskin <proski AT gnu.org>
  * and	David Gibson <hermes AT gibson.dropbear.id.au>
  *
@@ -30,38 +30,18 @@
  * other provisions required by the GPL.  If you do not delete the
  * provisions above, a recipient may use your version of this file
  * under either the MPL or the GPL.
-
- * Caution: this is experimental and probably buggy.  For success and
- * failure reports for different cards and adaptors, see
- * orinoco_plx_pci_id_table near the end of the file.  If you have a
- * card we don't have the PCI id for, and looks like it should work,
- * drop me mail with the id and "it works"/"it doesn't work".
  *
- * Note: if everything gets detected fine but it doesn't actually send
- * or receive packets, your first port of call should probably be to
- * try newer firmware in the card.  Especially if you're doing Ad-Hoc
- * modes.
- *
- * The actual driving is done by orinoco.c, this is just resource
- * allocation stuff.  The explanation below is courtesy of Ryan Niemi
- * on the linux-wlan-ng list at
- * http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html
- *
- * The PLX9052-based cards (WL11000 and several others) are a
- * different beast than the usual PCMCIA-based PRISM2 configuration
- * expected by wlan-ng.  Here's the general details on how the WL11000
- * PCI adapter works:
+ * Here's the general details on how the PLX9052 adapter works:
  *
  * - Two PCI I/O address spaces, one 0x80 long which contains the
  * PLX9052 registers, and one that's 0x40 long mapped to the PCMCIA
  * slot I/O address space.
  *
- * - One PCI memory address space, mapped to the PCMCIA memory space
+ * - One PCI memory address space, mapped to the PCMCIA attribute space
  * (containing the CIS).
  *
- * After identifying the I/O and memory space, you can read through
- * the memory space to confirm the CIS's device ID or manufacturer ID
- * to make sure it's the expected card.  qKeep in mind that the PCMCIA
+ * Using the later, you can read through the CIS data to make sure the
+ * card is compatible with the driver. Keep in mind that the PCMCIA
  * spec specifies the CIS as the lower 8 bits of each word read from
  * the CIS, so to read the bytes of the CIS, read every other byte
  * (0,2,4,...). Passing that test, you need to enable the I/O address
@@ -71,7 +51,7 @@
  * within the PCI memory space. Write 0x41 to the COR register to
  * enable I/O mode and to select level triggered interrupts. To
  * confirm you actually succeeded, read the COR register back and make
- * sure it actually got set to 0x41, incase you have an unexpected
+ * sure it actually got set to 0x41, in case you have an unexpected
  * card inserted.
  *
  * Following that, you can treat the second PCI I/O address space (the
@@ -101,16 +81,6 @@
  * that, I've hot-swapped a number of times during debugging and
  * driver development for various reasons (stuck WAIT# line after the
  * radio card's firmware locks up).
- *
- * Hope this is enough info for someone to add PLX9052 support to the
- * wlan-ng card. In the case of the WL11000, the PCI ID's are
- * 0x1639/0x0200, with matching subsystem ID's. Other PLX9052-based
- * manufacturers other than Eumitcom (or on cards other than the
- * WL11000) may have different PCI ID's.
- *
- * If anyone needs any more specific info, let me know. I haven't had
- * time to implement support myself yet, and with the way things are
- * going, might not have time for a while..
  */
 
 #define DRIVER_NAME "orinoco_plx"
@@ -125,6 +95,7 @@
 #include <pcmcia/cisreg.h>
 
 #include "orinoco.h"
+#include "orinoco_pci.h"
 
 #define COR_OFFSET	(0x3e0)	/* COR attribute offset of Prism2 PC card */
 #define COR_VALUE	(COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
@@ -134,30 +105,20 @@
 #define PLX_INTCSR		0x4c /* Interrupt Control & Status Register */
 #define PLX_INTCSR_INTEN	(1<<6) /* Interrupt Enable bit */
 
-static const u8 cis_magic[] = {
-	0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67
-};
-
-/* Orinoco PLX specific data */
-struct orinoco_plx_card {
-	void __iomem *attr_mem;
-};
-
 /*
  * Do a soft reset of the card using the Configuration Option Register
  */
 static int orinoco_plx_cor_reset(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
-	struct orinoco_plx_card *card = priv->card;
-	u8 __iomem *attr_mem = card->attr_mem;
+	struct orinoco_pci_card *card = priv->card;
 	unsigned long timeout;
 	u16 reg;
 
-	writeb(COR_VALUE | COR_RESET, attr_mem + COR_OFFSET);
+	iowrite8(COR_VALUE | COR_RESET, card->attr_io + COR_OFFSET);
 	mdelay(1);
 
-	writeb(COR_VALUE, attr_mem + COR_OFFSET);
+	iowrite8(COR_VALUE, card->attr_io + COR_OFFSET);
 	mdelay(1);
 
 	/* Just in case, wait more until the card is no longer busy */
@@ -168,7 +129,7 @@
 		reg = hermes_read_regn(hw, CMD);
 	}
 
-	/* Did we timeout ? */
+	/* Still busy? */
 	if (reg & HERMES_CMD_BUSY) {
 		printk(KERN_ERR PFX "Busy timeout\n");
 		return -ETIMEDOUT;
@@ -177,20 +138,55 @@
 	return 0;
 }
 
+static int orinoco_plx_hw_init(struct orinoco_pci_card *card)
+{
+	int i;
+	u32 csr_reg;
+	static const u8 cis_magic[] = {
+		0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67
+	};
+
+	printk(KERN_DEBUG PFX "CIS: ");
+	for (i = 0; i < 16; i++) {
+		printk("%02X:", ioread8(card->attr_io + (i << 1)));
+	}
+	printk("\n");
+
+	/* Verify whether a supported PC card is present */
+	/* FIXME: we probably need to be smarted about this */
+	for (i = 0; i < sizeof(cis_magic); i++) {
+		if (cis_magic[i] != ioread8(card->attr_io + (i << 1))) {
+			printk(KERN_ERR PFX "The CIS value of Prism2 PC "
+			       "card is unexpected\n");
+			return -ENODEV;
+		}
+	}
+
+	/* bjoern: We need to tell the card to enable interrupts, in
+	   case the serial eprom didn't do this already.  See the
+	   PLX9052 data book, p8-1 and 8-24 for reference. */
+	csr_reg = ioread32(card->bridge_io + PLX_INTCSR);
+	if (!(csr_reg & PLX_INTCSR_INTEN)) {
+		csr_reg |= PLX_INTCSR_INTEN;
+		iowrite32(csr_reg, card->bridge_io + PLX_INTCSR);
+		csr_reg = ioread32(card->bridge_io + PLX_INTCSR);
+		if (!(csr_reg & PLX_INTCSR_INTEN)) {
+			printk(KERN_ERR PFX "Cannot enable interrupts\n");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
 
 static int orinoco_plx_init_one(struct pci_dev *pdev,
 				const struct pci_device_id *ent)
 {
-	int err = 0;
-	u8 __iomem *attr_mem = NULL;
-	u32 csr_reg, plx_addr;
-	struct orinoco_private *priv = NULL;
-	struct orinoco_plx_card *card;
-	unsigned long pccard_ioaddr = 0;
-	unsigned long pccard_iolen = 0;
-	struct net_device *dev = NULL;
-	void __iomem *mem;
-	int i;
+	int err;
+	struct orinoco_private *priv;
+	struct orinoco_pci_card *card;
+	struct net_device *dev;
+	void __iomem *hermes_io, *attr_io, *bridge_io;
 
 	err = pci_enable_device(pdev);
 	if (err) {
@@ -199,30 +195,30 @@
 	}
 
 	err = pci_request_regions(pdev, DRIVER_NAME);
-	if (err != 0) {
+	if (err) {
 		printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
 		goto fail_resources;
 	}
 
-	/* Resource 1 is mapped to PLX-specific registers */
-	plx_addr = pci_resource_start(pdev, 1);
+	bridge_io = pci_iomap(pdev, 1, 0);
+	if (!bridge_io) {
+		printk(KERN_ERR PFX "Cannot map bridge registers\n");
+		err = -EIO;
+		goto fail_map_bridge;
+	}
 
-	/* Resource 2 is mapped to the PCMCIA attribute memory */
-	attr_mem = ioremap(pci_resource_start(pdev, 2),
-			   pci_resource_len(pdev, 2));
-	if (!attr_mem) {
-		printk(KERN_ERR PFX "Cannot remap PCMCIA space\n");
+	attr_io = pci_iomap(pdev, 2, 0);
+	if (!attr_io) {
+		printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n");
+		err = -EIO;
 		goto fail_map_attr;
 	}
 
-	/* Resource 3 is mapped to the PCMCIA I/O address space */
-	pccard_ioaddr = pci_resource_start(pdev, 3);
-	pccard_iolen = pci_resource_len(pdev, 3);
-
-	mem = pci_iomap(pdev, 3, 0);
-	if (!mem) {
-		err = -ENOMEM;
-		goto fail_map_io;
+	hermes_io = pci_iomap(pdev, 3, 0);
+	if (!hermes_io) {
+		printk(KERN_ERR PFX "Cannot map chipset registers\n");
+		err = -EIO;
+		goto fail_map_hermes;
 	}
 
 	/* Allocate network device */
@@ -235,16 +231,12 @@
 
 	priv = netdev_priv(dev);
 	card = priv->card;
-	card->attr_mem = attr_mem;
-	dev->base_addr = pccard_ioaddr;
+	card->bridge_io = bridge_io;
+	card->attr_io = attr_io;
 	SET_MODULE_OWNER(dev);
 	SET_NETDEV_DEV(dev, &pdev->dev);
 
-	hermes_struct_init(&priv->hw, mem, HERMES_16BIT_REGSPACING);
-
-	printk(KERN_DEBUG PFX "Detected Orinoco/Prism2 PLX device "
-	       "at %s irq:%d, io addr:0x%lx\n", pci_name(pdev), pdev->irq,
-	       pccard_ioaddr);
+	hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING);
 
 	err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
 			  dev->name, dev);
@@ -253,20 +245,12 @@
 		err = -EBUSY;
 		goto fail_irq;
 	}
-	dev->irq = pdev->irq;
+	orinoco_pci_setup_netdev(dev, pdev, 2);
 
-	/* bjoern: We need to tell the card to enable interrupts, in
-	   case the serial eprom didn't do this already.  See the
-	   PLX9052 data book, p8-1 and 8-24 for reference. */
-	csr_reg = inl(plx_addr + PLX_INTCSR);
-	if (!(csr_reg & PLX_INTCSR_INTEN)) {
-		csr_reg |= PLX_INTCSR_INTEN;
-		outl(csr_reg, plx_addr + PLX_INTCSR);
-		csr_reg = inl(plx_addr + PLX_INTCSR);
-		if (!(csr_reg & PLX_INTCSR_INTEN)) {
-			printk(KERN_ERR PFX "Cannot enable interrupts\n");
-			goto fail;
-		}
+	err = orinoco_plx_hw_init(card);
+	if (err) {
+		printk(KERN_ERR PFX "Hardware initialization failed\n");
+		goto fail;
 	}
 
 	err = orinoco_plx_cor_reset(priv);
@@ -275,23 +259,6 @@
 		goto fail;
 	}
 
-	printk(KERN_DEBUG PFX "CIS: ");
-	for (i = 0; i < 16; i++) {
-		printk("%02X:", readb(attr_mem + 2*i));
-	}
-	printk("\n");
-
-	/* Verify whether a supported PC card is present */
-	/* FIXME: we probably need to be smarted about this */
-	for (i = 0; i < sizeof(cis_magic); i++) {
-		if (cis_magic[i] != readb(attr_mem +2*i)) {
-			printk(KERN_ERR PFX "The CIS value of Prism2 PC "
-			       "card is unexpected\n");
-			err = -EIO;
-			goto fail;
-		}
-	}
-
 	err = register_netdev(dev);
 	if (err) {
 		printk(KERN_ERR PFX "Cannot register network device\n");
@@ -310,12 +277,15 @@
 	free_orinocodev(dev);
 
  fail_alloc:
-	pci_iounmap(pdev, mem);
+	pci_iounmap(pdev, hermes_io);
 
- fail_map_io:
-	iounmap(attr_mem);
+ fail_map_hermes:
+	pci_iounmap(pdev, attr_io);
 
  fail_map_attr:
+	pci_iounmap(pdev, bridge_io);
+
+ fail_map_bridge:
 	pci_release_regions(pdev);
 
  fail_resources:
@@ -328,23 +298,20 @@
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
 	struct orinoco_private *priv = netdev_priv(dev);
-	struct orinoco_plx_card *card = priv->card;
-	u8 __iomem *attr_mem = card->attr_mem;
-
-	BUG_ON(! dev);
+	struct orinoco_pci_card *card = priv->card;
 
 	unregister_netdev(dev);
 	free_irq(dev->irq, dev);
 	pci_set_drvdata(pdev, NULL);
 	free_orinocodev(dev);
 	pci_iounmap(pdev, priv->hw.iobase);
-	iounmap(attr_mem);
+	pci_iounmap(pdev, card->attr_io);
+	pci_iounmap(pdev, card->bridge_io);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
 }
 
-
-static struct pci_device_id orinoco_plx_pci_id_table[] = {
+static struct pci_device_id orinoco_plx_id_table[] = {
 	{0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,},	/* Siemens SpeedStream SS1023 */
 	{0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,},	/* Netgear MA301 */
 	{0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,},	/* Correga  - does this work? */
@@ -362,13 +329,15 @@
 	{0,},
 };
 
-MODULE_DEVICE_TABLE(pci, orinoco_plx_pci_id_table);
+MODULE_DEVICE_TABLE(pci, orinoco_plx_id_table);
 
 static struct pci_driver orinoco_plx_driver = {
 	.name		= DRIVER_NAME,
-	.id_table	= orinoco_plx_pci_id_table,
+	.id_table	= orinoco_plx_id_table,
 	.probe		= orinoco_plx_init_one,
 	.remove		= __devexit_p(orinoco_plx_remove_one),
+	.suspend	= orinoco_pci_suspend,
+	.resume		= orinoco_pci_resume,
 };
 
 static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
@@ -388,7 +357,6 @@
 static void __exit orinoco_plx_exit(void)
 {
 	pci_unregister_driver(&orinoco_plx_driver);
-	ssleep(1);
 }
 
 module_init(orinoco_plx_init);
diff --git a/drivers/net/wireless/orinoco_tmd.c b/drivers/net/wireless/orinoco_tmd.c
index 5e68b70..0496663 100644
--- a/drivers/net/wireless/orinoco_tmd.c
+++ b/drivers/net/wireless/orinoco_tmd.c
@@ -1,5 +1,5 @@
 /* orinoco_tmd.c
- * 
+ *
  * Driver for Prism II devices which would usually be driven by orinoco_cs,
  * but are connected to the PCI bus by a TMD7160. 
  *
@@ -26,25 +26,13 @@
  * other provisions required by the GPL.  If you do not delete the
  * provisions above, a recipient may use your version of this file
  * under either the MPL or the GPL.
-
- * Caution: this is experimental and probably buggy.  For success and
- * failure reports for different cards and adaptors, see
- * orinoco_tmd_pci_id_table near the end of the file.  If you have a
- * card we don't have the PCI id for, and looks like it should work,
- * drop me mail with the id and "it works"/"it doesn't work".
- *
- * Note: if everything gets detected fine but it doesn't actually send
- * or receive packets, your first port of call should probably be to   
- * try newer firmware in the card.  Especially if you're doing Ad-Hoc
- * modes
  *
  * The actual driving is done by orinoco.c, this is just resource
  * allocation stuff.
  *
  * This driver is modeled after the orinoco_plx driver. The main
- * difference is that the TMD chip has only IO port ranges and no
- * memory space, i.e.  no access to the CIS. Compared to the PLX chip,
- * the io range functionalities are exchanged.
+ * difference is that the TMD chip has only IO port ranges and doesn't
+ * provide access to the PCMCIA attribute space.
  *
  * Pheecom sells cards with the TMD chip as "ASIC version"
  */
@@ -61,32 +49,26 @@
 #include <pcmcia/cisreg.h>
 
 #include "orinoco.h"
+#include "orinoco_pci.h"
 
 #define COR_VALUE	(COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
 #define COR_RESET     (0x80)	/* reset bit in the COR register */
 #define TMD_RESET_TIME	(500)	/* milliseconds */
 
-/* Orinoco TMD specific data */
-struct orinoco_tmd_card {
-	u32 tmd_io;
-};
-
-
 /*
  * Do a soft reset of the card using the Configuration Option Register
  */
 static int orinoco_tmd_cor_reset(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
-	struct orinoco_tmd_card *card = priv->card;
-	u32 addr = card->tmd_io;
+	struct orinoco_pci_card *card = priv->card;
 	unsigned long timeout;
 	u16 reg;
 
-	outb(COR_VALUE | COR_RESET, addr);
+	iowrite8(COR_VALUE | COR_RESET, card->bridge_io);
 	mdelay(1);
 
-	outb(COR_VALUE, addr);
+	iowrite8(COR_VALUE, card->bridge_io);
 	mdelay(1);
 
 	/* Just in case, wait more until the card is no longer busy */
@@ -97,7 +79,7 @@
 		reg = hermes_read_regn(hw, CMD);
 	}
 
-	/* Did we timeout ? */
+	/* Still busy? */
 	if (reg & HERMES_CMD_BUSY) {
 		printk(KERN_ERR PFX "Busy timeout\n");
 		return -ETIMEDOUT;
@@ -110,11 +92,11 @@
 static int orinoco_tmd_init_one(struct pci_dev *pdev,
 				const struct pci_device_id *ent)
 {
-	int err = 0;
-	struct orinoco_private *priv = NULL;
-	struct orinoco_tmd_card *card;
-	struct net_device *dev = NULL;
-	void __iomem *mem;
+	int err;
+	struct orinoco_private *priv;
+	struct orinoco_pci_card *card;
+	struct net_device *dev;
+	void __iomem *hermes_io, *bridge_io;
 
 	err = pci_enable_device(pdev);
 	if (err) {
@@ -123,20 +105,28 @@
 	}
 
 	err = pci_request_regions(pdev, DRIVER_NAME);
-	if (err != 0) {
+	if (err) {
 		printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
 		goto fail_resources;
 	}
 
-	mem = pci_iomap(pdev, 2, 0);
-	if (! mem) {
-		err = -ENOMEM;
-		goto fail_iomap;
+	bridge_io = pci_iomap(pdev, 1, 0);
+	if (!bridge_io) {
+		printk(KERN_ERR PFX "Cannot map bridge registers\n");
+		err = -EIO;
+		goto fail_map_bridge;
+	}
+
+	hermes_io = pci_iomap(pdev, 2, 0);
+	if (!hermes_io) {
+		printk(KERN_ERR PFX "Cannot map chipset registers\n");
+		err = -EIO;
+		goto fail_map_hermes;
 	}
 
 	/* Allocate network device */
 	dev = alloc_orinocodev(sizeof(*card), orinoco_tmd_cor_reset);
-	if (! dev) {
+	if (!dev) {
 		printk(KERN_ERR PFX "Cannot allocate network device\n");
 		err = -ENOMEM;
 		goto fail_alloc;
@@ -144,16 +134,11 @@
 
 	priv = netdev_priv(dev);
 	card = priv->card;
-	card->tmd_io = pci_resource_start(pdev, 1);
-	dev->base_addr = pci_resource_start(pdev, 2);
+	card->bridge_io = bridge_io;
 	SET_MODULE_OWNER(dev);
 	SET_NETDEV_DEV(dev, &pdev->dev);
 
-	hermes_struct_init(&priv->hw, mem, HERMES_16BIT_REGSPACING);
-
-	printk(KERN_DEBUG PFX "Detected Orinoco/Prism2 TMD device "
-	       "at %s irq:%d, io addr:0x%lx\n", pci_name(pdev), pdev->irq,
-	       dev->base_addr);
+	hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING);
 
 	err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
 			  dev->name, dev);
@@ -162,7 +147,7 @@
 		err = -EBUSY;
 		goto fail_irq;
 	}
-	dev->irq = pdev->irq;
+	orinoco_pci_setup_netdev(dev, pdev, 2);
 
 	err = orinoco_tmd_cor_reset(priv);
 	if (err) {
@@ -188,9 +173,12 @@
 	free_orinocodev(dev);
 
  fail_alloc:
-	pci_iounmap(pdev, mem);
+	pci_iounmap(pdev, hermes_io);
 
- fail_iomap:
+ fail_map_hermes:
+	pci_iounmap(pdev, bridge_io);
+
+ fail_map_bridge:
 	pci_release_regions(pdev);
 
  fail_resources:
@@ -203,31 +191,32 @@
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
 	struct orinoco_private *priv = dev->priv;
-
-	BUG_ON(! dev);
+	struct orinoco_pci_card *card = priv->card;
 
 	unregister_netdev(dev);
 	free_irq(dev->irq, dev);
 	pci_set_drvdata(pdev, NULL);
 	free_orinocodev(dev);
 	pci_iounmap(pdev, priv->hw.iobase);
+	pci_iounmap(pdev, card->bridge_io);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
 }
 
-
-static struct pci_device_id orinoco_tmd_pci_id_table[] = {
+static struct pci_device_id orinoco_tmd_id_table[] = {
 	{0x15e8, 0x0131, PCI_ANY_ID, PCI_ANY_ID,},      /* NDC and OEMs, e.g. pheecom */
 	{0,},
 };
 
-MODULE_DEVICE_TABLE(pci, orinoco_tmd_pci_id_table);
+MODULE_DEVICE_TABLE(pci, orinoco_tmd_id_table);
 
 static struct pci_driver orinoco_tmd_driver = {
 	.name		= DRIVER_NAME,
-	.id_table	= orinoco_tmd_pci_id_table,
+	.id_table	= orinoco_tmd_id_table,
 	.probe		= orinoco_tmd_init_one,
 	.remove		= __devexit_p(orinoco_tmd_remove_one),
+	.suspend	= orinoco_pci_suspend,
+	.resume		= orinoco_pci_resume,
 };
 
 static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
@@ -245,7 +234,6 @@
 static void __exit orinoco_tmd_exit(void)
 {
 	pci_unregister_driver(&orinoco_tmd_driver);
-	ssleep(1);
 }
 
 module_init(orinoco_tmd_init);
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
index f7b77ce..aeb38d9 100644
--- a/drivers/net/wireless/spectrum_cs.c
+++ b/drivers/net/wireless/spectrum_cs.c
@@ -1,6 +1,6 @@
 /*
  * Driver for 802.11b cards using RAM-loadable Symbol firmware, such as
- * Symbol Wireless Networker LA4100, CompactFlash cards by Socket
+ * Symbol Wireless Networker LA4137, CompactFlash cards by Socket
  * Communications and Intel PRO/Wireless 2011B.
  *
  * The driver implements Symbol firmware download.  The rest is handled
@@ -120,8 +120,8 @@
  * Each block has the following structure.
  */
 struct dblock {
-	__le32 _addr;		/* adapter address where to write the block */
-	__le16 _len;		/* length of the data only, in bytes */
+	__le32 addr;		/* adapter address where to write the block */
+	__le16 len;		/* length of the data only, in bytes */
 	char data[0];		/* data to be written */
 } __attribute__ ((packed));
 
@@ -131,9 +131,9 @@
  * items with matching ID should be written.
  */
 struct pdr {
-	__le32 _id;		/* record ID */
-	__le32 _addr;		/* adapter address where to write the data */
-	__le32 _len;		/* expected length of the data, in bytes */
+	__le32 id;		/* record ID */
+	__le32 addr;		/* adapter address where to write the data */
+	__le32 len;		/* expected length of the data, in bytes */
 	char next[0];		/* next PDR starts here */
 } __attribute__ ((packed));
 
@@ -144,8 +144,8 @@
  * be plugged into the secondary firmware.
  */
 struct pdi {
-	__le16 _len;		/* length of ID and data, in words */
-	__le16 _id;		/* record ID */
+	__le16 len;		/* length of ID and data, in words */
+	__le16 id;		/* record ID */
 	char data[0];		/* plug data */
 } __attribute__ ((packed));
 
@@ -154,44 +154,44 @@
 static inline u32
 dblock_addr(const struct dblock *blk)
 {
-	return le32_to_cpu(blk->_addr);
+	return le32_to_cpu(blk->addr);
 }
 
 static inline u32
 dblock_len(const struct dblock *blk)
 {
-	return le16_to_cpu(blk->_len);
+	return le16_to_cpu(blk->len);
 }
 
 static inline u32
 pdr_id(const struct pdr *pdr)
 {
-	return le32_to_cpu(pdr->_id);
+	return le32_to_cpu(pdr->id);
 }
 
 static inline u32
 pdr_addr(const struct pdr *pdr)
 {
-	return le32_to_cpu(pdr->_addr);
+	return le32_to_cpu(pdr->addr);
 }
 
 static inline u32
 pdr_len(const struct pdr *pdr)
 {
-	return le32_to_cpu(pdr->_len);
+	return le32_to_cpu(pdr->len);
 }
 
 static inline u32
 pdi_id(const struct pdi *pdi)
 {
-	return le16_to_cpu(pdi->_id);
+	return le16_to_cpu(pdi->id);
 }
 
 /* Return length of the data only, in bytes */
 static inline u32
 pdi_len(const struct pdi *pdi)
 {
-	return 2 * (le16_to_cpu(pdi->_len) - 1);
+	return 2 * (le16_to_cpu(pdi->len) - 1);
 }
 
 
@@ -343,8 +343,7 @@
 
 	/* do the actual plugging */
 	spectrum_aux_setaddr(hw, pdr_addr(pdr));
-	hermes_write_words(hw, HERMES_AUXDATA, pdi->data,
-			   pdi_len(pdi) / 2);
+	hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
 
 	return 0;
 }
@@ -424,8 +423,8 @@
 
 	while (dblock_addr(blk) != BLOCK_END) {
 		spectrum_aux_setaddr(hw, blkaddr);
-		hermes_write_words(hw, HERMES_AUXDATA, blk->data,
-				   blklen / 2);
+		hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
+				   blklen);
 
 		blk = (struct dblock *) &blk->data[blklen];
 		blkaddr = dblock_addr(blk);
@@ -653,13 +652,10 @@
 	int last_fn, last_ret;
 	u_char buf[64];
 	config_info_t conf;
-	cisinfo_t info;
 	tuple_t tuple;
 	cisparse_t parse;
 	void __iomem *mem;
 
-	CS_CHECK(ValidateCIS, pcmcia_validate_cis(link, &info));
-
 	/*
 	 * This reads the card's CONFIG tuple to find its
 	 * configuration registers.
@@ -709,12 +705,6 @@
 			goto next_entry;
 		link->conf.ConfigIndex = cfg->index;
 
-		/* Does this card need audio output? */
-		if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
-			link->conf.Attributes |= CONF_ENABLE_SPKR;
-			link->conf.Status = CCSR_AUDIO_ENA;
-		}
-
 		/* Use power settings for Vcc and Vpp if present */
 		/* Note that the CIS values need to be rescaled */
 		if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
@@ -932,7 +922,7 @@
 	" David Gibson <hermes@gibson.dropbear.id.au>, et al)";
 
 static struct pcmcia_device_id spectrum_cs_ids[] = {
-	PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4100 */
+	PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */
 	PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */
 	PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */
 	PCMCIA_DEVICE_NULL,
diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h
index 4725ff8..4087dfc 100644
--- a/include/net/ieee80211.h
+++ b/include/net/ieee80211.h
@@ -29,7 +29,7 @@
 #include <linux/kernel.h>	/* ARRAY_SIZE */
 #include <linux/wireless.h>
 
-#define IEEE80211_VERSION "git-1.1.7"
+#define IEEE80211_VERSION "git-1.1.13"
 
 #define IEEE80211_DATA_LEN		2304
 /* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
@@ -104,6 +104,9 @@
 #define IEEE80211_SCTL_FRAG		0x000F
 #define IEEE80211_SCTL_SEQ		0xFFF0
 
+/* QOS control */
+#define IEEE80211_QCTL_TID		0x000F
+
 /* debug macros */
 
 #ifdef CONFIG_IEEE80211_DEBUG
@@ -1073,6 +1076,7 @@
 
 	int (*handle_management) (struct net_device * dev,
 				  struct ieee80211_network * network, u16 type);
+	int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb);
 
 	/* Typical STA methods */
 	int (*handle_auth) (struct net_device * dev,
diff --git a/include/net/ieee80211softmac_wx.h b/include/net/ieee80211softmac_wx.h
index 3e0be45..4ee3ad5 100644
--- a/include/net/ieee80211softmac_wx.h
+++ b/include/net/ieee80211softmac_wx.h
@@ -91,4 +91,9 @@
 			      struct iw_request_info *info,
 			      union iwreq_data *wrqu,
 			      char *extra);
+extern int
+ieee80211softmac_wx_set_mlme(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu,
+			     char *extra);
 #endif /* _IEEE80211SOFTMAC_WX */
diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c
index 93def94..3fa5df2 100644
--- a/net/ieee80211/ieee80211_crypt_tkip.c
+++ b/net/ieee80211/ieee80211_crypt_tkip.c
@@ -501,8 +501,11 @@
 static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
 {
 	struct ieee80211_hdr_4addr *hdr11;
+	u16 stype;
 
 	hdr11 = (struct ieee80211_hdr_4addr *)skb->data;
+	stype  = WLAN_FC_GET_STYPE(le16_to_cpu(hdr11->frame_ctl));
+
 	switch (le16_to_cpu(hdr11->frame_ctl) &
 		(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
 	case IEEE80211_FCTL_TODS:
@@ -523,7 +526,13 @@
 		break;
 	}
 
-	hdr[12] = 0;		/* priority */
+	if (stype & IEEE80211_STYPE_QOS_DATA) {
+		const struct ieee80211_hdr_3addrqos *qoshdr =
+			(struct ieee80211_hdr_3addrqos *)skb->data;
+		hdr[12] = le16_to_cpu(qoshdr->qos_ctl) & IEEE80211_QCTL_TID;
+	} else
+		hdr[12] = 0;		/* priority */
+
 	hdr[13] = hdr[14] = hdr[15] = 0;	/* reserved */
 }
 
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c
index 604b7b0..2bf567f 100644
--- a/net/ieee80211/ieee80211_rx.c
+++ b/net/ieee80211/ieee80211_rx.c
@@ -369,7 +369,6 @@
 
 	/* Put this code here so that we avoid duplicating it in all
 	 * Rx paths. - Jean II */
-#ifdef CONFIG_WIRELESS_EXT
 #ifdef IW_WIRELESS_SPY		/* defined in iw_handler.h */
 	/* If spy monitoring on */
 	if (ieee->spy_data.spy_number > 0) {
@@ -398,7 +397,6 @@
 		wireless_spy_update(ieee->dev, hdr->addr2, &wstats);
 	}
 #endif				/* IW_WIRELESS_SPY */
-#endif				/* CONFIG_WIRELESS_EXT */
 
 #ifdef NOT_YET
 	hostap_update_rx_stats(local->ap, hdr, rx_stats);
@@ -1692,8 +1690,8 @@
 				     WLAN_FC_GET_STYPE(le16_to_cpu
 						       (header->frame_ctl)));
 
-		IEEE80211_WARNING("%s: IEEE80211_REASSOC_REQ received\n",
-				  ieee->dev->name);
+		IEEE80211_DEBUG_MGMT("%s: IEEE80211_REASSOC_REQ received\n",
+				     ieee->dev->name);
 		if (ieee->handle_reassoc_request != NULL)
 			ieee->handle_reassoc_request(ieee->dev,
 						    (struct ieee80211_reassoc_request *)
@@ -1705,8 +1703,8 @@
 				     WLAN_FC_GET_STYPE(le16_to_cpu
 						       (header->frame_ctl)));
 
-		IEEE80211_WARNING("%s: IEEE80211_ASSOC_REQ received\n",
-				  ieee->dev->name);
+		IEEE80211_DEBUG_MGMT("%s: IEEE80211_ASSOC_REQ received\n",
+				     ieee->dev->name);
 		if (ieee->handle_assoc_request != NULL)
 			ieee->handle_assoc_request(ieee->dev);
 		break;
@@ -1722,10 +1720,10 @@
 		IEEE80211_DEBUG_MGMT("received UNKNOWN (%d)\n",
 				     WLAN_FC_GET_STYPE(le16_to_cpu
 						       (header->frame_ctl)));
-		IEEE80211_WARNING("%s: Unknown management packet: %d\n",
-				  ieee->dev->name,
-				  WLAN_FC_GET_STYPE(le16_to_cpu
-						    (header->frame_ctl)));
+		IEEE80211_DEBUG_MGMT("%s: Unknown management packet: %d\n",
+				     ieee->dev->name,
+				     WLAN_FC_GET_STYPE(le16_to_cpu
+						       (header->frame_ctl)));
 		break;
 	}
 }
diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c
index 8b4332f..233d527 100644
--- a/net/ieee80211/ieee80211_tx.c
+++ b/net/ieee80211/ieee80211_tx.c
@@ -220,13 +220,43 @@
 	return txb;
 }
 
+static int ieee80211_classify(struct sk_buff *skb)
+{
+	struct ethhdr *eth;
+	struct iphdr *ip;
+
+	eth = (struct ethhdr *)skb->data;
+	if (eth->h_proto != __constant_htons(ETH_P_IP))
+		return 0;
+
+	ip = skb->nh.iph;
+	switch (ip->tos & 0xfc) {
+	case 0x20:
+		return 2;
+	case 0x40:
+		return 1;
+	case 0x60:
+		return 3;
+	case 0x80:
+		return 4;
+	case 0xa0:
+		return 5;
+	case 0xc0:
+		return 6;
+	case 0xe0:
+		return 7;
+	default:
+		return 0;
+	}
+}
+
 /* Incoming skb is converted to a txb which consists of
  * a block of 802.11 fragment packets (stored as skbs) */
 int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct ieee80211_device *ieee = netdev_priv(dev);
 	struct ieee80211_txb *txb = NULL;
-	struct ieee80211_hdr_3addr *frag_hdr;
+	struct ieee80211_hdr_3addrqos *frag_hdr;
 	int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
 	    rts_required;
 	unsigned long flags;
@@ -234,9 +264,10 @@
 	int ether_type, encrypt, host_encrypt, host_encrypt_msdu, host_build_iv;
 	int bytes, fc, hdr_len;
 	struct sk_buff *skb_frag;
-	struct ieee80211_hdr_3addr header = {	/* Ensure zero initialized */
+	struct ieee80211_hdr_3addrqos header = {/* Ensure zero initialized */
 		.duration_id = 0,
-		.seq_ctl = 0
+		.seq_ctl = 0,
+		.qos_ctl = 0
 	};
 	u8 dest[ETH_ALEN], src[ETH_ALEN];
 	struct ieee80211_crypt_data *crypt;
@@ -282,12 +313,6 @@
 	memcpy(dest, skb->data, ETH_ALEN);
 	memcpy(src, skb->data + ETH_ALEN, ETH_ALEN);
 
-	/* Advance the SKB to the start of the payload */
-	skb_pull(skb, sizeof(struct ethhdr));
-
-	/* Determine total amount of storage required for TXB packets */
-	bytes = skb->len + SNAP_SIZE + sizeof(u16);
-
 	if (host_encrypt || host_build_iv)
 		fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
 		    IEEE80211_FCTL_PROTECTED;
@@ -306,9 +331,23 @@
 		memcpy(header.addr2, src, ETH_ALEN);
 		memcpy(header.addr3, ieee->bssid, ETH_ALEN);
 	}
-	header.frame_ctl = cpu_to_le16(fc);
 	hdr_len = IEEE80211_3ADDR_LEN;
 
+	if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) {
+		fc |= IEEE80211_STYPE_QOS_DATA;
+		hdr_len += 2;
+
+		skb->priority = ieee80211_classify(skb);
+		header.qos_ctl |= skb->priority & IEEE80211_QCTL_TID;
+	}
+	header.frame_ctl = cpu_to_le16(fc);
+
+	/* Advance the SKB to the start of the payload */
+	skb_pull(skb, sizeof(struct ethhdr));
+
+	/* Determine total amount of storage required for TXB packets */
+	bytes = skb->len + SNAP_SIZE + sizeof(u16);
+
 	/* Encrypt msdu first on the whole data packet. */
 	if ((host_encrypt || host_encrypt_msdu) &&
 	    crypt && crypt->ops && crypt->ops->encrypt_msdu) {
@@ -402,7 +441,7 @@
 	if (rts_required) {
 		skb_frag = txb->fragments[0];
 		frag_hdr =
-		    (struct ieee80211_hdr_3addr *)skb_put(skb_frag, hdr_len);
+		    (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
 
 		/*
 		 * Set header frame_ctl to the RTS.
@@ -433,7 +472,7 @@
 				    crypt->ops->extra_mpdu_prefix_len);
 
 		frag_hdr =
-		    (struct ieee80211_hdr_3addr *)skb_put(skb_frag, hdr_len);
+		    (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
 		memcpy(frag_hdr, &header, hdr_len);
 
 		/* If this is not the last fragment, then add the MOREFRAGS
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c
index b885fd1..0ea55cb 100644
--- a/net/ieee80211/ieee80211_wx.c
+++ b/net/ieee80211/ieee80211_wx.c
@@ -50,7 +50,8 @@
 	char *p;
 	struct iw_event iwe;
 	int i, j;
-	u8 max_rate, rate;
+	char *current_val;	/* For rates */
+	u8 rate;
 
 	/* First entry *MUST* be the AP MAC address */
 	iwe.cmd = SIOCGIWAP;
@@ -107,9 +108,13 @@
 	start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
 
 	/* Add basic and extended rates */
-	max_rate = 0;
-	p = custom;
-	p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
+	/* Rate : stuffing multiple values in a single event require a bit
+	 * more of magic - Jean II */
+	current_val = start + IW_EV_LCP_LEN;
+	iwe.cmd = SIOCGIWRATE;
+	/* Those two flags are ignored... */
+	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
 	for (i = 0, j = 0; i < network->rates_len;) {
 		if (j < network->rates_ex_len &&
 		    ((network->rates_ex[j] & 0x7F) <
@@ -117,28 +122,21 @@
 			rate = network->rates_ex[j++] & 0x7F;
 		else
 			rate = network->rates[i++] & 0x7F;
-		if (rate > max_rate)
-			max_rate = rate;
-		p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
-			      "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
+		/* Bit rate given in 500 kb/s units (+ 0x80) */
+		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
+		/* Add new value to event */
+		current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN);
 	}
 	for (; j < network->rates_ex_len; j++) {
 		rate = network->rates_ex[j] & 0x7F;
-		p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
-			      "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
-		if (rate > max_rate)
-			max_rate = rate;
+		/* Bit rate given in 500 kb/s units (+ 0x80) */
+		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
+		/* Add new value to event */
+		current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN);
 	}
-
-	iwe.cmd = SIOCGIWRATE;
-	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
-	iwe.u.bitrate.value = max_rate * 500000;
-	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
-
-	iwe.cmd = IWEVCUSTOM;
-	iwe.u.data.length = p - custom;
-	if (iwe.u.data.length)
-		start = iwe_stream_add_point(start, stop, &iwe, custom);
+	/* Check if we added any rate */
+	if((current_val - start) > IW_EV_LCP_LEN)
+		start = current_val;
 
 	/* Add quality statistics */
 	iwe.cmd = IWEVQUAL;
diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c
index fb79ce7..ea9f5aa 100644
--- a/net/ieee80211/softmac/ieee80211softmac_assoc.c
+++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c
@@ -82,7 +82,7 @@
 }
 
 /* Sends out a disassociation request to the desired AP */
-static void
+void
 ieee80211softmac_disassoc(struct ieee80211softmac_device *mac, u16 reason)
 {
 	unsigned long flags;
diff --git a/net/ieee80211/softmac/ieee80211softmac_event.c b/net/ieee80211/softmac/ieee80211softmac_event.c
index 8cc8f3f..4b153f7 100644
--- a/net/ieee80211/softmac/ieee80211softmac_event.c
+++ b/net/ieee80211/softmac/ieee80211softmac_event.c
@@ -38,7 +38,8 @@
  * The event context is private and can only be used from
  * within this module. Its meaning varies with the event
  * type:
- *  SCAN_FINISHED:	no special meaning
+ *  SCAN_FINISHED,
+ *  DISASSOCIATED:	NULL
  *  ASSOCIATED,
  *  ASSOCIATE_FAILED,
  *  ASSOCIATE_TIMEOUT,
@@ -59,15 +60,15 @@
  */
 
 static char *event_descriptions[IEEE80211SOFTMAC_EVENT_LAST+1] = {
-	"scan finished",
-	"associated",
+	NULL, /* scan finished */
+	NULL, /* associated */
 	"associating failed",
 	"associating timed out",
 	"authenticated",
 	"authenticating failed",
 	"authenticating timed out",
 	"associating failed because no suitable network was found",
-	"disassociated",
+	NULL, /* disassociated */
 };
 
 
@@ -136,30 +137,24 @@
 		int we_event;
 		char *msg = NULL;
 
+		memset(&wrqu, '\0', sizeof (union iwreq_data));
+
 		switch(event) {
 		case IEEE80211SOFTMAC_EVENT_ASSOCIATED:
 			network = (struct ieee80211softmac_network *)event_ctx;
-			wrqu.data.length = 0;
-			wrqu.data.flags = 0;
 			memcpy(wrqu.ap_addr.sa_data, &network->bssid[0], ETH_ALEN);
-			wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-			we_event = SIOCGIWAP;
-			break;
+			/* fall through */
 		case IEEE80211SOFTMAC_EVENT_DISASSOCIATED:
-			wrqu.data.length = 0;
-			wrqu.data.flags = 0;
-			memset(&wrqu, '\0', sizeof (union iwreq_data));
 			wrqu.ap_addr.sa_family = ARPHRD_ETHER;
 			we_event = SIOCGIWAP;
 			break;
 		case IEEE80211SOFTMAC_EVENT_SCAN_FINISHED:
-			wrqu.data.length = 0;
-			wrqu.data.flags = 0;
-			memset(&wrqu, '\0', sizeof (union iwreq_data));
 			we_event = SIOCGIWSCAN;
 			break;
 		default:
 			msg = event_descriptions[event];
+			if (!msg)
+				msg = "SOFTMAC EVENT BUG";
 			wrqu.data.length = strlen(msg);
 			we_event = IWEVCUSTOM;
 			break;
diff --git a/net/ieee80211/softmac/ieee80211softmac_priv.h b/net/ieee80211/softmac/ieee80211softmac_priv.h
index 65d9816..8c95b3a 100644
--- a/net/ieee80211/softmac/ieee80211softmac_priv.h
+++ b/net/ieee80211/softmac/ieee80211softmac_priv.h
@@ -150,6 +150,7 @@
 int ieee80211softmac_handle_reassoc_req(struct net_device * dev,
 				        struct ieee80211_reassoc_request * reassoc);
 void ieee80211softmac_assoc_timeout(void *d);
+void ieee80211softmac_disassoc(struct ieee80211softmac_device *mac, u16 reason);
 
 /* some helper functions */
 static inline int ieee80211softmac_scan_handlers_check_self(struct ieee80211softmac_device *sm)
diff --git a/net/ieee80211/softmac/ieee80211softmac_wx.c b/net/ieee80211/softmac/ieee80211softmac_wx.c
index 27edb2b..8d0c226 100644
--- a/net/ieee80211/softmac/ieee80211softmac_wx.c
+++ b/net/ieee80211/softmac/ieee80211softmac_wx.c
@@ -431,3 +431,35 @@
 }
 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
 
+int
+ieee80211softmac_wx_set_mlme(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu,
+			     char *extra)
+{
+	struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+	struct iw_mlme *mlme = (struct iw_mlme *)extra;
+	u16 reason = cpu_to_le16(mlme->reason_code);
+	struct ieee80211softmac_network *net;
+
+	if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) {
+		printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n");
+		return -EINVAL;
+	}
+
+	switch (mlme->cmd) {
+	case IW_MLME_DEAUTH:
+		net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data);
+		if (!net) {
+			printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n");
+			return -EINVAL;
+		}
+		return ieee80211softmac_deauth_req(mac, net, reason);
+	case IW_MLME_DISASSOC:
+		ieee80211softmac_disassoc(mac, reason);
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);