diff options
Diffstat (limited to 'net/wireless/wext-spy.c')
| -rw-r--r-- | net/wireless/wext-spy.c | 231 | 
1 files changed, 231 insertions, 0 deletions
diff --git a/net/wireless/wext-spy.c b/net/wireless/wext-spy.c new file mode 100644 index 00000000000..6dcfe65a2d1 --- /dev/null +++ b/net/wireless/wext-spy.c @@ -0,0 +1,231 @@ +/* + * This file implement the Wireless Extensions spy API. + * + * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com> + * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. + * + * (As all part of the Linux kernel, this file is GPL) + */ + +#include <linux/wireless.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <net/iw_handler.h> +#include <net/arp.h> +#include <net/wext.h> + +static inline struct iw_spy_data *get_spydata(struct net_device *dev) +{ +	/* This is the new way */ +	if (dev->wireless_data) +		return dev->wireless_data->spy_data; +	return NULL; +} + +int iw_handler_set_spy(struct net_device *	dev, +		       struct iw_request_info *	info, +		       union iwreq_data *	wrqu, +		       char *			extra) +{ +	struct iw_spy_data *	spydata = get_spydata(dev); +	struct sockaddr *	address = (struct sockaddr *) extra; + +	/* Make sure driver is not buggy or using the old API */ +	if (!spydata) +		return -EOPNOTSUPP; + +	/* Disable spy collection while we copy the addresses. +	 * While we copy addresses, any call to wireless_spy_update() +	 * will NOP. This is OK, as anyway the addresses are changing. */ +	spydata->spy_number = 0; + +	/* We want to operate without locking, because wireless_spy_update() +	 * most likely will happen in the interrupt handler, and therefore +	 * have its own locking constraints and needs performance. +	 * The rtnl_lock() make sure we don't race with the other iw_handlers. +	 * This make sure wireless_spy_update() "see" that the spy list +	 * is temporarily disabled. */ +	smp_wmb(); + +	/* 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); +	} + +	/* Make sure above is updated before re-enabling */ +	smp_wmb(); + +	/* Enable addresses */ +	spydata->spy_number = wrqu->data.length; + +	return 0; +} +EXPORT_SYMBOL(iw_handler_set_spy); + +int iw_handler_get_spy(struct net_device *	dev, +		       struct iw_request_info *	info, +		       union iwreq_data *	wrqu, +		       char *			extra) +{ +	struct iw_spy_data *	spydata = get_spydata(dev); +	struct sockaddr *	address = (struct sockaddr *) extra; +	int			i; + +	/* Make sure driver is not buggy or using the old API */ +	if (!spydata) +		return -EOPNOTSUPP; + +	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 &= ~IW_QUAL_ALL_UPDATED; +	return 0; +} +EXPORT_SYMBOL(iw_handler_get_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) +{ +	struct iw_spy_data *	spydata = get_spydata(dev); +	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra; + +	/* Make sure driver is not buggy or using the old API */ +	if (!spydata) +		return -EOPNOTSUPP; + +	/* 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)); + +	return 0; +} +EXPORT_SYMBOL(iw_handler_set_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) +{ +	struct iw_spy_data *	spydata = get_spydata(dev); +	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra; + +	/* Make sure driver is not buggy or using the old API */ +	if (!spydata) +		return -EOPNOTSUPP; + +	/* Just do it */ +	memcpy(&(threshold->low), &(spydata->spy_thr_low), +	       2 * sizeof(struct iw_quality)); + +	return 0; +} +EXPORT_SYMBOL(iw_handler_get_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)); + +	/* Send event to user space */ +	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); +} + +/* ---------------------------------------------------------------- */ +/* + * 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) +{ +	struct iw_spy_data *	spydata = get_spydata(dev); +	int			i; +	int			match = -1; + +	/* Make sure driver is not buggy or using the old API */ +	if (!spydata) +		return; + +	/* Update all records that match */ +	for (i = 0; i < spydata->spy_number; i++) +		if (!compare_ether_addr(address, spydata->spy_address[i])) { +			memcpy(&(spydata->spy_stat[i]), wstats, +			       sizeof(struct iw_quality)); +			match = i; +		} + +	/* 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); +			} +		} +	} +} +EXPORT_SYMBOL(wireless_spy_update);  |