diff -u -p -r linux/drivers/net/wireless-w13/hermes.h linux/drivers/net/wireless/hermes.h
--- linux/drivers/net/wireless-w13/hermes.h	Wed Jan 23 17:10:54 2002
+++ linux/drivers/net/wireless/hermes.h	Thu Jan 24 17:37:03 2002
@@ -233,23 +233,55 @@ struct hermes_tallies_frame {
 /* Grabbed from wlan-ng - Thanks Mark... - Jean II
  * This is the result of a scan inquiry command */
 /* Structure describing info about an Access Point */
-struct hermes_scan_apinfo {
+struct prism2_scan_apinfo {
 	u16 channel;		/* Channel where the AP sits */
 	u16 noise;		/* Noise level */
 	u16 level;		/* Signal level */
 	u8 bssid[ETH_ALEN];	/* MAC address of the Access Point */
 	u16 beacon_interv;	/* Beacon interval ? */
 	u16 capabilities;	/* Capabilities ? */
+	u16 essid_len;		/* ESSID length */
 	u8 essid[32];		/* ESSID of the network */
 	u8 rates[10];		/* Bit rate supported */
 	u16 proberesp_rate;	/* ???? */
 } __attribute__ ((packed));
 /* Container */
-struct hermes_scan_frame {
+struct prism2_scan_frame {
 	u16 rsvd;                   /* ??? */
 	u16 scanreason;             /* ??? */
-	struct hermes_scan_apinfo aps[35];        /* Scan result */
+	struct prism2_scan_apinfo aps[35];        /* Scan result */
 } __attribute__ ((packed));
+
+/* Same stuff for the Lucent/Agere card.
+ * Thanks to h1kari <h1kari@dachb0den.com> - Jean II */
+struct agere_scan_apinfo {
+	u16 channel;		/* Channel where the AP sits */
+	u16 noise;		/* Noise level */
+	u16 level;		/* Signal level */
+	u8 bssid[ETH_ALEN];	/* MAC address of the Access Point */
+	u16 beacon_interv;	/* Beacon interval ? */
+	u16 capabilities;	/* Capabilities ? */
+	/* bits: 0-ess, 1-ibss, 4-privacy [wep] */
+	u16 essid_len;		/* ESSID length */
+	u8 essid[32];		/* ESSID of the network */
+} __attribute__ ((packed));
+/* No container */
+
+/* Hack to be able to read both type */
+union hermes_scan_info {
+	struct agere_scan_apinfo	a;
+	struct prism2_scan_apinfo	p;
+};
+
+/* Link status. Once again, grabbed from wlan-ng - Thanks Mark... - Jean II */
+#define HERMES_LINK_NOTCONNECTED	(0)
+#define HERMES_LINK_CONNECTED		(1)
+#define HERMES_LINK_DISCONNECTED	(2)
+#define HERMES_LINK_AP_CHANGE		(3)
+#define HERMES_LINK_AP_OUTOFRANGE	(4)
+#define HERMES_LINK_AP_INRANGE		(5)
+#define HERMES_LINK_ASSOCFAIL		(6)
+
 
 #ifdef __KERNEL__
 
diff -u -p -r linux/drivers/net/wireless-w13/orinoco.c linux/drivers/net/wireless/orinoco.c
--- linux/drivers/net/wireless-w13/orinoco.c	Wed Jan 23 17:55:17 2002
+++ linux/drivers/net/wireless/orinoco.c	Fri Jan 25 15:00:00 2002
@@ -622,6 +622,14 @@ orinoco_reset(struct orinoco_private *pr
 	orinoco_lock(priv);
 	__orinoco_stop_irqs(priv);
 
+	/* Cleanup of driver struct */
+	if(priv->scan_result != NULL) {
+		DEBUG(1, "%s: scan KFREE %p\n", dev->name, priv->scan_result);
+		kfree(priv->scan_result);
+		priv->scan_result = NULL;
+	}
+	priv->scan_inprogress = 0;
+
 	/* Check if we need a card reset */
 	if((priv->need_card_reset) && (priv->card_reset_handler != NULL))
 		priv->card_reset_handler(priv);
@@ -1297,8 +1305,105 @@ static void __orinoco_ev_info(struct ori
 		wstats->miss.beacon += le16_to_cpu(tallies.RxUnicastFrames);
 #endif
 #endif /* WIRELESS_EXT > 11 */
+		break;
+	}
+	case HERMES_INQ_SCAN: {
+		/* Result of a scanning. Contains information about
+		 * cells in the vicinity - Jean II */
+#if WIRELESS_EXT > 13
+		int len = le16_to_cpu(info.len) - 1;
+		union iwreq_data	wrqu;
+		unsigned char *buf;
+
+		DEBUG(1, "%s: scan frame is %d words.\n", dev->name, len);
+
+		/* Keep sanity (2048 bytes) */
+		if(len > 1024) {
+			printk(KERN_WARNING "%s: Scan results too large (%d words).\n", dev->name, len);
+			break;
+		}
+
+		/* We are a strict producer. If the previous scan results
+		 * have not been consumed, we just have to drop this
+		 * frame. We can't remove the previous results ourselves,
+		 * that would be *very* racy... Jean II */
+		if(priv->scan_result != NULL) {
+			printk(KERN_WARNING "%s: Previous scan results not consumed, dropping info frame.\n", dev->name);
+			break;
+		}
+
+		/* Allocate buffer for results */
+		buf = kmalloc(len * 2, GFP_ATOMIC);
+		if(buf == NULL)
+			/* No memory, so can't printk()... */
+			break;
+		DEBUG(1, "%s: scan KMALLOC %p\n", dev->name, buf);
+
+		/* Read directly the data (no seek) */
+		hermes_read_words(hw, HERMES_DATA1, (void *) buf, len);
+
+#ifdef ORINOCO_DEBUG
+		{
+			int	i;
+			printk(KERN_DEBUG "Scan result [%02X", buf[0]);
+			for(i = 1; i < (len * 2); i++)
+				printk(":%02X", buf[i]);
+			printk("]\n");
+		}
+#endif	/* ORINOCO_DEBUG */
+
+		/* Allow the clients to access the results */
+		priv->scan_len = len * 2;
+		priv->scan_result = buf;
+
+		/* 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 - Jean II */
+		wrqu.data.length = 0;
+		wrqu.data.flags = 0;
+		wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
+#endif /* WIRELESS_EXT > 13 */
+		break;
+	}
+	case HERMES_INQ_LINKSTATUS: {
+		/* Link status information frame.
+		 * The link status has changed, and the card tell us about
+		 * it. Most likely, the card has just connected to the cell
+		 * or created an Ad-Hoc cell, or roamed... - Jean II */
+#if WIRELESS_EXT > 13
+		uint16_t status;
+		union iwreq_data	wrqu;
+
+		/* Read directly the data (no seek) */
+		hermes_read_words(hw, HERMES_DATA1, (void *) &status, 1);
+		printk(KERN_DEBUG "%s: Link status = %d.\n",
+		      priv->ndev.name, status);
+
+		/* The info frame contains only one word which is the
+		 * status (see hermes.h). The status is pretty boring
+		 * in itself, that's why we export the new BSSID...
+		 * Jean II */
+
+		/* Do we have a valid AP address ? */
+		if((status == HERMES_LINK_CONNECTED) ||
+		   (status == HERMES_LINK_AP_CHANGE) ||
+		   (status == HERMES_LINK_AP_INRANGE))
+			err = hermes_read_ltv(hw, IRQ_BAP,
+					      HERMES_RID_CURRENTBSSID,
+					      ETH_ALEN, NULL,
+					      wrqu.ap_addr.sa_data);
+		else
+			memset(wrqu.ap_addr.sa_data, '\0', ETH_ALEN);
+		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+		/* Send event to user space */
+		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+#endif /* WIRELESS_EXT > 13 */
+
+		break;
 	}
-	break;
 	default:
 		DEBUG(1, "%s: Unknown information frame received (type %04x).\n",
 		      priv->ndev.name, le16_to_cpu(info.type));
@@ -1458,20 +1563,50 @@ static void __orinoco_ev_txexc(struct or
 	struct net_device *dev = &priv->ndev;
 	struct net_device_stats *stats = &priv->stats;
 	u16 fid = hermes_read_regn(hw, TXCOMPLFID);
-	struct hermes_tx_descriptor desc;
+	struct orinoco_txframe_hdr hdr;
 	int err = 0;
 
 	if (fid == DUMMY_FID)
 		return; /* Nothing's really happened */
 
-	err = hermes_bap_pread(hw, USER_BAP, &desc, sizeof(desc), fid, 0);
+	/* Read the frame header */
+	err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
+			       sizeof(struct hermes_tx_descriptor) +
+			       sizeof(struct ieee802_11_hdr),
+			       fid, 0);
 	if (err) {
 		printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
 		       "(FID=%04X error %d)\n",
 		       dev->name, fid, err);
 	} else {
+		int status = le16_to_cpu(hdr.desc.status);
+
 		printk(KERN_INFO "%s: Tx error, status %d (FID=%04X)\n",
-		       dev->name, le16_to_cpu(desc.status), fid);
+		       dev->name, status, fid);
+
+#if WIRELESS_EXT > 13
+		/* We produce a TXDROP event only for retry or lifetime
+		 * 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 */
+		if(status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
+			union iwreq_data	wrqu;
+
+			/* Copy 802.11 dest address.
+			 * We use the 802.11 header because the frame may
+			 * not be 802.3 or may be mangled...
+			 * In Ad-Hoc mode, it will be the node address.
+			 * In managed mode, it will be most likely the AP addr
+			 * User space will figure out how to convert it to
+			 * whatever it needs (IP address or else).
+			 * - Jean II */
+			memcpy(wrqu.addr.sa_data, hdr.p80211.addr1, ETH_ALEN);
+			wrqu.addr.sa_family = ARPHRD_ETHER;
+
+			/* Send event to user space */
+			wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
+		}
+#endif /* WIRELESS_EXT > 13 */
 	}
 	
 	stats->tx_errors++;
@@ -3281,6 +3416,245 @@ static int orinoco_ioctl_getspy(struct n
 	return 0;
 }
 
+#if WIRELESS_EXT > 13
+/* Trigger a scan (look for other cells in the vicinity */
+static int orinoco_ioctl_setscan(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_param *srq,
+				 char *extra)
+{
+	struct orinoco_private *priv = dev->priv;
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+
+	/* Note : you may have realised that, as this is a SET operation,
+	 * this is priviledged and therefore a normal user can't
+	 * perform scanning.
+	 * This is not an error, while the device perform scanning,
+	 * traffic doesn't flow, so it's a perfect DoS...
+	 * Jean II */
+
+	orinoco_lock(priv);
+
+	/* Note : because we don't lock out the irq handler, the way
+	 * we access scan variables in priv is critical.
+	 *	o scan_inprogress : not touched by irq handler
+	 *	o scan_mode : not touched by irq handler
+	 *	o scan_result : irq is strict producer, non-irq is strict
+	 *		consumer.
+	 *	o scan_len : synchronised with scan_result
+	 * Before modifying anything on those variables, please think hard !
+	 * Jean II */
+
+	/* If there is still some left-over scan results, get rid of it */
+	if(priv->scan_result != NULL) {
+		/* What's likely is that a client did crash or was killed
+		 * between triggering the scan request and reading the
+		 * results, so we need to reset everything.
+		 * Some clients that are too slow may suffer from that...
+		 * Jean II */
+		DEBUG(1, "%s: scan KFREE %p\n", dev->name, priv->scan_result);
+		kfree(priv->scan_result);
+		priv->scan_result = NULL;
+	}
+
+	/* Save flags */
+	priv->scan_mode = srq->flags;
+
+	/* Always trigger scanning, even if it's in progress.
+	 * This way, if the info frame get lost, we will recover somewhat
+	 * gracefully  - Jean II */
+
+	/* Simple scanning for now...
+	 * We will do better later - Jean II */
+	err = hermes_inquire(hw, HERMES_INQ_SCAN);
+
+	/* One more client */
+	priv->scan_inprogress = 1;
+
+	orinoco_unlock(priv);
+	return 0;
+}
+
+/* Translate scan data returned from the card to a card independant
+ * format that the Wireless Tools will understand - Jean II */
+static inline int orinoco_translate_scan(struct net_device *dev,
+					 char *buffer,
+					 char *scan,
+					 int scan_len)
+{
+	struct orinoco_private *priv = dev->priv;
+	int			offset;		/* In the scan data */
+	union hermes_scan_info *atom;
+	int			atom_len;
+	u16			capabilities;
+	struct iw_event		iwe;		/* Temporary buffer */
+	char *			current_ev = buffer;
+	char *			end_buf = buffer + IW_SCAN_MAX_DATA;
+
+	if(priv->firmware_type == FIRMWARE_TYPE_AGERE) {
+		atom_len = sizeof(struct agere_scan_apinfo);
+		offset = 0;
+	} else {
+		atom_len = sizeof(struct prism2_scan_apinfo);
+		//offset = sizeof(struct prism2_scan_frame);
+		offset = 4;
+	}
+
+	DEBUG(4, "%s: scan_len = %d, atom_len = %d\n", dev->name,
+	     scan_len, atom_len);
+
+	/* Read the entries one by one */
+	for(; offset + atom_len <= scan_len; offset += atom_len) {
+		/* Get next atom */
+		atom = (union hermes_scan_info *) (scan + offset);
+
+		DEBUG(4, "%s: offset = %d, current_ev = %p, end_buf = %p, ap_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, offset, current_ev, end_buf, atom->a.bssid[0], atom->a.bssid[1], atom->a.bssid[2], atom->a.bssid[3], atom->a.bssid[4], atom->a.bssid[5]);
+
+		/* First entry *MUST* be the AP MAC address */
+		iwe.cmd = SIOCGIWAP;
+		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+		memcpy(iwe.u.ap_addr.sa_data, atom->a.bssid, ETH_ALEN);
+		current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
+
+		/* Other entries will be displayed in the order we give them */
+
+		/* Add the ESSID */
+		iwe.u.data.length = le16_to_cpu(atom->a.essid_len);
+		if(iwe.u.data.length > 32)
+			iwe.u.data.length = 32;
+		iwe.cmd = SIOCGIWESSID;
+		iwe.u.data.flags = 1;
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid);
+
+		/* Add mode */
+		iwe.cmd = SIOCGIWMODE;
+		capabilities = le16_to_cpu(atom->a.capabilities);
+		if(capabilities & 0x3) {
+			if(capabilities & 0x1)
+				iwe.u.mode = IW_MODE_INFRA;
+			else
+				iwe.u.mode = IW_MODE_ADHOC;
+			current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN);
+		}
+
+		/* Add frequency */
+		iwe.cmd = SIOCGIWFREQ;
+		iwe.u.freq.m = channel_frequency[le16_to_cpu(atom->a.channel)-1] * 100000;
+		iwe.u.freq.e = 1;
+		current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
+
+		/* Add quality statistics */
+		iwe.cmd = IWEVQUAL;
+		iwe.u.qual.level = (__u8) le16_to_cpu(atom->a.level) - 0x95;
+		iwe.u.qual.noise = (__u8) le16_to_cpu(atom->a.noise) - 0x95;
+		current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
+
+		/* Add encryption capability */
+		/* Note : it works on Lucent/Agere cards, need to check
+		 * on PrismII cards - Jean */
+		iwe.cmd = SIOCGIWENCODE;
+		if(capabilities & 0x10)
+			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+		else
+			iwe.u.data.flags = IW_ENCODE_DISABLED;
+		iwe.u.data.length = 0;
+		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid);
+
+		/* Bit rate is only available with PrismII firmwares */
+		if(priv->firmware_type != FIRMWARE_TYPE_AGERE) {
+			char *	current_val = current_ev + IW_EV_LCP_LEN;
+			int	i;
+
+			iwe.cmd = SIOCGIWRATE;
+			/* Those two flags are ignored... */
+			iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+			/* Max 10 values */
+			for(i = 0; i < 10; i++) {
+				/* NULL terminated */
+				if(atom->p.rates[i] == 0x0)
+					break;
+				/* Bit rate given in 500 kb/s units (+ 0x80) */
+				iwe.u.bitrate.value = ((atom->p.rates[i] & 0x7f) * 500000);
+				current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN);
+			}
+			/* Check if we added any event */
+			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 */
+	}
+	return current_ev - buffer;
+}
+
+/* Return results of a scan */
+static int orinoco_ioctl_getscan(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_point *srq,
+				 char *extra)
+{
+	struct orinoco_private *priv = dev->priv;
+	int err = 0;
+
+	orinoco_lock(priv);
+
+	/* If no results yet, ask to try again later */
+	if(priv->scan_result == NULL) {
+		if(priv->scan_inprogress)
+			/* Important note : we don't want to block the caller
+			 * until results are ready for various reasons.
+			 * First, managing wait queues is complex and racy.
+			 * Second, we grab some rtnetlink lock before comming
+			 * here (in dev_ioctl()).
+			 * Third, we generate an Wireless Event, so the
+			 * caller can wait itself on that - Jean II */
+			err = -EAGAIN;
+		else
+			/* Client error, no scan results...
+			 * The caller need to restart the scan. */
+			err = -ENODATA;
+	} else {
+		/* We have some results to push back to user space */
+
+		/* Translate to WE format */
+		srq->length = orinoco_translate_scan(dev, extra,
+						     priv->scan_result,
+						     priv->scan_len);
+
+		/* Return flags */
+		srq->flags = (__u16) priv->scan_mode;
+
+		/* Results are here, so scan no longer in progress */
+		priv->scan_inprogress = 0;
+
+		/* In any case, Scan results will be cleaned up in the
+		 * reset function and when exiting the driver.
+		 * The person triggering the scanning may never come to
+		 * pick the results, so we need to do it in those places.
+		 * Jean II */
+
+#ifdef SCAN_SINGLE_READ
+		/* If you enable this option, only one client (the first
+		 * one) will be able to read the result (and only one
+		 * time). If there is multiple concurent clients that
+		 * want to read scan results, this behavior is not
+		 * advisable - Jean II */
+		DEBUG(1, "%s: scan KFREE %p\n",
+		      dev->name, priv->scan_result);
+		kfree(priv->scan_result);
+		priv->scan_result = NULL;
+#endif /* SCAN_SINGLE_READ */
+		/* Here, if too much time has elapsed since last scan,
+		 * we may want to clean up scan results... - Jean II */
+	}
+	  
+	orinoco_unlock(priv);
+	return err;
+}
+#endif	/* WIRELESS_EXT > 13 */
+
 /* Commit handler, called after a bunch of SET operation */
 static int orinoco_ioctl_commit(struct net_device *dev,
 				struct iw_request_info *info,	/* NULL */
@@ -3359,8 +3733,13 @@ static const iw_handler		orinoco_handler
 	(iw_handler) orinoco_ioctl_getwap,		/* SIOCGIWAP */
 	(iw_handler) NULL,				/* -- hole -- */
 	(iw_handler) NULL,				/* SIOCGIWAPLIST */
-	(iw_handler) NULL,				/* -- hole -- */
-	(iw_handler) NULL,				/* -- hole -- */
+#if WIRELESS_EXT > 13
+	(iw_handler) orinoco_ioctl_setscan,		/* SIOCSIWSCAN */
+	(iw_handler) orinoco_ioctl_getscan,		/* SIOCGIWSCAN */
+#else	/* WIRELESS_EXT > 13 */
+	(iw_handler) NULL,				/* SIOCSIWSCAN */
+	(iw_handler) NULL,				/* SIOCGIWSCAN */
+#endif	/* WIRELESS_EXT > 13 */
 	(iw_handler) orinoco_ioctl_setessid,		/* SIOCSIWESSID */
 	(iw_handler) orinoco_ioctl_getessid,		/* SIOCGIWESSID */
 	(iw_handler) orinoco_ioctl_setnick,		/* SIOCSIWNICKN */
@@ -4249,6 +4628,17 @@ orinoco_proc_dev_cleanup(struct orinoco_
 	struct net_device *dev = &priv->ndev;
 
 	TRACE_ENTER(priv->ndev.name);
+
+	/* This seems to be the only function called when the driver exits,
+	 * so add my extra cleanup here - Jean II */
+	orinoco_lock(priv);
+	if(priv->scan_result != NULL) {
+		DEBUG(1, "%s: scan KFREE %p\n", dev->name, priv->scan_result);
+		priv->scan_inprogress = 0;
+		kfree(priv->scan_result);
+		priv->scan_result = NULL;
+	}
+	orinoco_unlock(priv);
 
 	if (priv->dir_regs) {
 		remove_proc_entry("regs", priv->dir_dev);
diff -u -p -r linux/drivers/net/wireless-w13/orinoco.h linux/drivers/net/wireless/orinoco.h
--- linux/drivers/net/wireless-w13/orinoco.h	Wed Jan 23 19:14:45 2002
+++ linux/drivers/net/wireless/orinoco.h	Fri Jan 25 16:32:11 2002
@@ -85,6 +85,13 @@ struct orinoco_private {
 	struct iw_quality	spy_stat[IW_MAX_SPY];
 #endif
 
+	/* Scanning support */
+	int	scan_inprogress;	/* Scan pending... */
+	__u32	scan_mode;		/* Type of scan done */
+	char *	scan_result;		/* Result of previous scan */
+	int	scan_len;		/* Lenght of result */
+	/* More to come related to monitor mode */
+
 	/* /proc based debugging stuff */
 	struct proc_dir_entry *dir_dev;
 	struct proc_dir_entry *dir_regs;
