patch-2.4.23 linux-2.4.23/net/core/wireless.c

Next file: linux-2.4.23/net/ipv4/Config.in
Previous file: linux-2.4.23/net/core/sysctl_net_core.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.22/net/core/wireless.c linux-2.4.23/net/core/wireless.c
@@ -2,7 +2,7 @@
  * This file implement the Wireless Extensions APIs.
  *
  * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved.
  *
  * (As all part of the Linux kernel, this file is GPL)
  */
@@ -43,6 +43,11 @@
  *	o Turn on WE_STRICT_WRITE by default + kernel warning
  *	o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
  *	o Fix off-by-one in test (extra_size <= IFNAMSIZ)
+ *
+ * v6 - 9.01.03 - Jean II
+ *	o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
+ *	o Add enhanced spy support : iw_handler_set_thrspy() and event.
+ *	o Add WIRELESS_EXT version display in /proc/net/wireless
  */
 
 /***************************** INCLUDES *****************************/
@@ -52,6 +57,7 @@
 #include <linux/types.h>		/* off_t */
 #include <linux/netdevice.h>		/* struct ifreq, dev_get_by_name() */
 #include <linux/rtnetlink.h>		/* rtnetlink stuff */
+#include <linux/if_arp.h>		/* ARPHRD_ETHER */
 
 #include <linux/wireless.h>		/* Pretty obvious */
 #include <net/iw_handler.h>		/* New driver API */
@@ -65,6 +71,7 @@
 /* Debuging stuff */
 #undef WE_IOCTL_DEBUG		/* Debug IOCTL API */
 #undef WE_EVENT_DEBUG		/* Debug Event dispatcher */
+#undef WE_SPY_DEBUG		/* Debug enhanced spy support */
 
 /* Options */
 #define WE_EVENT_NETLINK	/* Propagate events using rtnetlink */
@@ -72,7 +79,7 @@
 
 /************************* GLOBAL VARIABLES *************************/
 /*
- * You should not use global variables, because or re-entrancy.
+ * You should not use global variables, because of re-entrancy.
  * On our case, it's only const, so it's OK...
  */
 /*
@@ -115,11 +122,11 @@
 	/* SIOCSIWSPY */
 	{ IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0},
 	/* SIOCGIWSPY */
-	{ IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_GET_SPY, 0},
-	/* -- hole -- */
-	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
-	/* -- hole -- */
-	{ IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
+	{ IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0},
+	/* SIOCSIWTHRSPY */
+	{ IW_HEADER_TYPE_POINT, 0, sizeof(struct iw_thrspy), 1, 1, 0},
+	/* SIOCGIWTHRSPY */
+	{ IW_HEADER_TYPE_POINT, 0, sizeof(struct iw_thrspy), 1, 1, 0},
 	/* SIOCSIWAP */
 	{ IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0},
 	/* SIOCGIWAP */
@@ -377,9 +384,9 @@
 	struct net_device *	dev;
 
 	size = sprintf(buffer,
-		       "Inter-| sta-|   Quality        |   Discarded packets               | Missed\n"
-		       " face | tus | link level noise |  nwid  crypt   frag  retry   misc | beacon\n"
-			);
+		       "Inter-| sta-|   Quality        |   Discarded packets               | Missed | WE\n"
+		       " face | tus | link level noise |  nwid  crypt   frag  retry   misc | beacon | %d\n",
+		       WIRELESS_EXT);
 	
 	pos += size;
 	len += size;
@@ -1024,3 +1031,252 @@
 
 	return;		/* Always success, I guess ;-) */
 }
+
+/********************** ENHANCED IWSPY SUPPORT **********************/
+/*
+ * In the old days, the driver was handling spy support all by itself.
+ * Now, the driver can delegate this task to Wireless Extensions.
+ * It needs to use those standard spy iw_handler in struct iw_handler_def,
+ * push data to us via XXX and include struct iw_spy_data in its
+ * private part.
+ * One of the main advantage of centralising spy support here is that
+ * it becomes much easier to improve and extend it without having to touch
+ * the drivers. One example is the addition of the Spy-Threshold events.
+ * Note : IW_WIRELESS_SPY is defined in iw_handler.h
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set Spy List
+ */
+int iw_handler_set_spy(struct net_device *	dev,
+		       struct iw_request_info *	info,
+		       union iwreq_data *	wrqu,
+		       char *			extra)
+{
+#ifdef IW_WIRELESS_SPY
+	struct iw_spy_data *	spydata = (dev->priv +
+					   dev->wireless_handlers->spy_offset);
+	struct sockaddr *	address = (struct sockaddr *) extra;
+
+	/* Disable spy collection while we copy the addresses.
+	 * As we don't disable interrupts, we need to do this to avoid races.
+	 * As we are the only writer, this is good enough. */
+	spydata->spy_number = 0;
+
+	/* Are there are addresses to copy? */
+	if(wrqu->data.length > 0) {
+		int i;
+
+		/* Copy addresses */
+		for(i = 0; i < wrqu->data.length; i++)
+			memcpy(spydata->spy_address[i], address[i].sa_data,
+			       ETH_ALEN);
+		/* Reset stats */
+		memset(spydata->spy_stat, 0,
+		       sizeof(struct iw_quality) * IW_MAX_SPY);
+
+#ifdef WE_SPY_DEBUG
+		printk(KERN_DEBUG "iw_handler_set_spy() :  offset %ld, spydata %p, num %d\n", dev->wireless_handlers->spy_offset, spydata, wrqu->data.length);
+		for (i = 0; i < wrqu->data.length; i++)
+			printk(KERN_DEBUG
+			       "%02X:%02X:%02X:%02X:%02X:%02X \n",
+			       spydata->spy_address[i][0],
+			       spydata->spy_address[i][1],
+			       spydata->spy_address[i][2],
+			       spydata->spy_address[i][3],
+			       spydata->spy_address[i][4],
+			       spydata->spy_address[i][5]);
+#endif	/* WE_SPY_DEBUG */
+	}
+	/* Enable addresses */
+	spydata->spy_number = wrqu->data.length;
+
+	return 0;
+#else /* IW_WIRELESS_SPY */
+	return -EOPNOTSUPP;
+#endif /* IW_WIRELESS_SPY */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get Spy List
+ */
+int iw_handler_get_spy(struct net_device *	dev,
+		       struct iw_request_info *	info,
+		       union iwreq_data *	wrqu,
+		       char *			extra)
+{
+#ifdef IW_WIRELESS_SPY
+	struct iw_spy_data *	spydata = (dev->priv +
+					   dev->wireless_handlers->spy_offset);
+	struct sockaddr *	address = (struct sockaddr *) extra;
+	int			i;
+
+	wrqu->data.length = spydata->spy_number;
+
+	/* Copy addresses. */
+	for(i = 0; i < spydata->spy_number; i++) 	{
+		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
+		address[i].sa_family = AF_UNIX;
+	}
+	/* Copy stats to the user buffer (just after). */
+	if(spydata->spy_number > 0)
+		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
+		       spydata->spy_stat,
+		       sizeof(struct iw_quality) * spydata->spy_number);
+	/* Reset updated flags. */
+	for(i = 0; i < spydata->spy_number; i++)
+		spydata->spy_stat[i].updated = 0;
+	return 0;
+#else /* IW_WIRELESS_SPY */
+	return -EOPNOTSUPP;
+#endif /* IW_WIRELESS_SPY */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set spy threshold
+ */
+int iw_handler_set_thrspy(struct net_device *	dev,
+			  struct iw_request_info *info,
+			  union iwreq_data *	wrqu,
+			  char *		extra)
+{
+#ifdef IW_WIRELESS_THRSPY
+	struct iw_spy_data *	spydata = (dev->priv +
+					   dev->wireless_handlers->spy_offset);
+	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
+
+	/* Just do it */
+	memcpy(&(spydata->spy_thr_low), &(threshold->low),
+	       2 * sizeof(struct iw_quality));
+
+	/* Clear flag */
+	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
+
+#ifdef WE_SPY_DEBUG
+	printk(KERN_DEBUG "iw_handler_set_thrspy() :  low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
+#endif	/* WE_SPY_DEBUG */
+
+	return 0;
+#else /* IW_WIRELESS_THRSPY */
+	return -EOPNOTSUPP;
+#endif /* IW_WIRELESS_THRSPY */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get spy threshold
+ */
+int iw_handler_get_thrspy(struct net_device *	dev,
+			  struct iw_request_info *info,
+			  union iwreq_data *	wrqu,
+			  char *		extra)
+{
+#ifdef IW_WIRELESS_THRSPY
+	struct iw_spy_data *	spydata = (dev->priv +
+					   dev->wireless_handlers->spy_offset);
+	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;
+
+	/* Just do it */
+	memcpy(&(threshold->low), &(spydata->spy_thr_low),
+	       2 * sizeof(struct iw_quality));
+
+	return 0;
+#else /* IW_WIRELESS_THRSPY */
+	return -EOPNOTSUPP;
+#endif /* IW_WIRELESS_THRSPY */
+}
+
+#ifdef IW_WIRELESS_THRSPY
+/*------------------------------------------------------------------*/
+/*
+ * Prepare and send a Spy Threshold event
+ */
+static void iw_send_thrspy_event(struct net_device *	dev,
+				 struct iw_spy_data *	spydata,
+				 unsigned char *	address,
+				 struct iw_quality *	wstats)
+{
+	union iwreq_data	wrqu;
+	struct iw_thrspy	threshold;
+
+	/* Init */
+	wrqu.data.length = 1;
+	wrqu.data.flags = 0;
+	/* Copy address */
+	memcpy(threshold.addr.sa_data, address, ETH_ALEN);
+	threshold.addr.sa_family = ARPHRD_ETHER;
+	/* Copy stats */
+	memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
+	/* Copy also thresholds */
+	memcpy(&(threshold.low), &(spydata->spy_thr_low),
+	       2 * sizeof(struct iw_quality));
+
+#ifdef WE_SPY_DEBUG
+	printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
+	       threshold.addr.sa_data[0],
+	       threshold.addr.sa_data[1],
+	       threshold.addr.sa_data[2],
+	       threshold.addr.sa_data[3],
+	       threshold.addr.sa_data[4],
+	       threshold.addr.sa_data[5], threshold.qual.level);
+#endif	/* WE_SPY_DEBUG */
+
+	/* Send event to user space */
+	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
+}
+#endif /* IW_WIRELESS_THRSPY */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call for the driver to update the spy data.
+ * For now, the spy data is a simple array. As the size of the array is
+ * small, this is good enough. If we wanted to support larger number of
+ * spy addresses, we should use something more efficient...
+ */
+void wireless_spy_update(struct net_device *	dev,
+			 unsigned char *	address,
+			 struct iw_quality *	wstats)
+{
+#ifdef IW_WIRELESS_SPY
+	struct iw_spy_data *	spydata = (dev->priv +
+					   dev->wireless_handlers->spy_offset);
+	int			i;
+	int			match = -1;
+
+#ifdef WE_SPY_DEBUG
+	printk(KERN_DEBUG "wireless_spy_update() :  offset %ld, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
+#endif	/* WE_SPY_DEBUG */
+
+	/* Update all records that match */
+	for(i = 0; i < spydata->spy_number; i++)
+		if(!memcmp(address, spydata->spy_address[i], ETH_ALEN)) {
+			memcpy(&(spydata->spy_stat[i]), wstats,
+			       sizeof(struct iw_quality));
+			match = i;
+		}
+#ifdef IW_WIRELESS_THRSPY
+	/* Generate an event if we cross the spy threshold.
+	 * To avoid event storms, we have a simple hysteresis : we generate
+	 * event only when we go under the low threshold or above the
+	 * high threshold. */
+	if(match >= 0) {
+		if(spydata->spy_thr_under[match]) {
+			if(wstats->level > spydata->spy_thr_high.level) {
+				spydata->spy_thr_under[match] = 0;
+				iw_send_thrspy_event(dev, spydata,
+						     address, wstats);
+			}
+		} else {
+			if(wstats->level < spydata->spy_thr_low.level) {
+				spydata->spy_thr_under[match] = 1;
+				iw_send_thrspy_event(dev, spydata,
+						     address, wstats);
+			}
+		}
+	}
+#endif /* IW_WIRELESS_THRSPY */
+#endif /* IW_WIRELESS_SPY */
+}

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