* This file implement the Wireless Extensions APIs.
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved.
*
* (As all part of the Linux kernel, this file is GPL)
*/
* 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
+ *
+ * v6 - 18.06.04 - Jean II
+ * o Change get_spydata() method for added safety
+ * o Remove spy #ifdef, they are always on -> cleaner code
+ * o Allow any size GET request if user specifies length > max
+ * and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
+ * o Start migrating get_wireless_stats to struct iw_handler_def
+ * o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
+ * Based on patch from Pavel Roskin <proski@gnu.org> :
+ * o Fix kernel data leak to user space in private handler handling
+ *
+ * v7 - 18.3.05 - Jean II
+ * o Remove (struct iw_point *)->pointer from events and streams
+ * o Remove spy_offset from struct iw_handler_def
+ * o Start deprecating dev->get_wireless_stats, output a warning
+ * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
+ * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
*/
/***************************** INCLUDES *****************************/
#include <linux/seq_file.h>
#include <linux/init.h> /* for __init */
#include <linux/if_arp.h> /* ARPHRD_ETHER */
+#include <linux/etherdevice.h> /* compare_ether_addr */
#include <linux/wireless.h> /* Pretty obvious */
#include <net/iw_handler.h> /* New driver API */
/**************************** CONSTANTS ****************************/
-/* Enough lenience, let's make sure things are proper... */
-#define WE_STRICT_WRITE /* Check write buffer size */
-/* I'll probably drop both the define and kernel message in the next version */
-
/* Debugging stuff */
#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
#undef WE_EVENT_DEBUG /* Debug Event dispatcher */
.header_type = IW_HEADER_TYPE_ADDR,
.flags = IW_DESCR_FLAG_DUMP,
},
+ [SIOCSIWMLME - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_mlme),
+ .max_tokens = sizeof(struct iw_mlme),
+ },
[SIOCGIWAPLIST - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct sockaddr) +
sizeof(struct iw_quality),
.max_tokens = IW_MAX_AP,
+ .flags = IW_DESCR_FLAG_NOMAX,
},
[SIOCSIWSCAN - SIOCIWFIRST] = {
- .header_type = IW_HEADER_TYPE_PARAM,
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = 0,
+ .max_tokens = sizeof(struct iw_scan_req),
},
[SIOCGIWSCAN - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_SCAN_MAX_DATA,
+ .flags = IW_DESCR_FLAG_NOMAX,
},
[SIOCSIWESSID - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_POINT,
[SIOCGIWPOWER - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
+ [SIOCSIWGENIE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [SIOCGIWGENIE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [SIOCSIWAUTH - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCGIWAUTH - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCSIWENCODEEXT - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_encode_ext),
+ .max_tokens = sizeof(struct iw_encode_ext) +
+ IW_ENCODING_TOKEN_MAX,
+ },
+ [SIOCGIWENCODEEXT - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_encode_ext),
+ .max_tokens = sizeof(struct iw_encode_ext) +
+ IW_ENCODING_TOKEN_MAX,
+ },
+ [SIOCSIWPMKSA - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_pmksa),
+ .max_tokens = sizeof(struct iw_pmksa),
+ },
};
static const int standard_ioctl_num = (sizeof(standard_ioctl) /
sizeof(struct iw_ioctl_description));
[IWEVEXPIRED - IWEVFIRST] = {
.header_type = IW_HEADER_TYPE_ADDR,
},
+ [IWEVGENIE - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IWEVMICHAELMICFAILURE - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_michaelmicfailure),
+ },
+ [IWEVASSOCREQIE - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IWEVASSOCRESPIE - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IWEVPMKIDCAND - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_pmkid_cand),
+ },
};
static const int standard_event_num = (sizeof(standard_event) /
sizeof(struct iw_ioctl_description));
/* Size (in bytes) of the various private data types */
-static const char priv_type_size[] = {
+static const char iw_priv_type_size[] = {
0, /* IW_PRIV_TYPE_NONE */
1, /* IW_PRIV_TYPE_BYTE */
1, /* IW_PRIV_TYPE_CHAR */
*/
static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
{
- return (dev->get_wireless_stats ?
- dev->get_wireless_stats(dev) :
- (struct iw_statistics *) NULL);
- /* In the future, get_wireless_stats may move from 'struct net_device'
- * to 'struct iw_handler_def', to de-bloat struct net_device.
- * Definitely worse a thought... */
+ /* New location */
+ if((dev->wireless_handlers != NULL) &&
+ (dev->wireless_handlers->get_wireless_stats != NULL))
+ return dev->wireless_handlers->get_wireless_stats(dev);
+
+ /* Old location, field to be removed in next WE */
+ if(dev->get_wireless_stats) {
+ static int printed_message;
+
+ if (!printed_message++)
+ printk(KERN_DEBUG "%s (WE) : Driver using old /proc/net/wireless support, please fix driver !\n",
+ dev->name);
+
+ return dev->get_wireless_stats(dev);
+ }
+
+ /* Not found */
+ return (struct iw_statistics *) NULL;
}
/* ---------------------------------------------------------------- */
/* ---------------------------------------------------------------- */
/*
- * Number of private arguments
+ * Calculate size of private arguments
*/
static inline int get_priv_size(__u16 args)
{
int num = args & IW_PRIV_SIZE_MASK;
int type = (args & IW_PRIV_TYPE_MASK) >> 12;
- return num * priv_type_size[type];
+ return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Re-calculate the size of private arguments
+ */
+static inline int adjust_priv_size(__u16 args,
+ union iwreq_data * wrqu)
+{
+ int num = wrqu->data.length;
+ int max = args & IW_PRIV_SIZE_MASK;
+ int type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+ /* Make sure the driver doesn't goof up */
+ if (max < num)
+ num = max;
+
+ return num * iw_priv_type_size[type];
}
seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d "
"%6d %6d %6d\n",
dev->name, stats->status, stats->qual.qual,
- stats->qual.updated & 1 ? '.' : ' ',
- ((__u8) stats->qual.level),
- stats->qual.updated & 2 ? '.' : ' ',
- ((__u8) stats->qual.noise),
- stats->qual.updated & 4 ? '.' : ' ',
+ stats->qual.updated & IW_QUAL_QUAL_UPDATED
+ ? '.' : ' ',
+ ((__s32) stats->qual.level) -
+ ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+ stats->qual.updated & IW_QUAL_LEVEL_UPDATED
+ ? '.' : ' ',
+ ((__s32) stats->qual.noise) -
+ ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+ stats->qual.updated & IW_QUAL_NOISE_UPDATED
+ ? '.' : ' ',
stats->discard.nwid, stats->discard.code,
stats->discard.fragment, stats->discard.retries,
stats->discard.misc, stats->miss.beacon);
- stats->qual.updated = 0;
+ stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
}
}
return 0;
}
-extern void *dev_seq_start(struct seq_file *seq, loff_t *pos);
-extern void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos);
-extern void dev_seq_stop(struct seq_file *seq, void *v);
-
static struct seq_operations wireless_seq_ops = {
.start = dev_seq_start,
.next = dev_seq_next,
int __init wireless_proc_init(void)
{
+ /* Create /proc/net/wireless entry */
if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
return -ENOMEM;
sizeof(struct iw_statistics)))
return -EFAULT;
- /* Check if we need to clear the update flag */
+ /* Check if we need to clear the updated flag */
if(wrq->u.data.flags != 0)
- stats->qual.updated = 0;
+ stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
return 0;
} else
return -EOPNOTSUPP;
/* Check NULL pointer */
if(iwr->u.data.pointer == NULL)
return -EFAULT;
-#ifdef WE_STRICT_WRITE
+
/* Check if there is enough buffer up there */
if(iwr->u.data.length < dev->wireless_handlers->num_private_args) {
- printk(KERN_ERR "%s (WE) : Buffer for request SIOCGIWPRIV too small (%d<%d)\n", dev->name, iwr->u.data.length, dev->wireless_handlers->num_private_args);
+ /* User space can't know in advance how large the buffer
+ * needs to be. Give it a hint, so that we can support
+ * any size buffer we want somewhat efficiently... */
+ iwr->u.data.length = dev->wireless_handlers->num_private_args;
return -E2BIG;
}
-#endif /* WE_STRICT_WRITE */
/* Set the number of available ioctls. */
iwr->u.data.length = dev->wireless_handlers->num_private_args;
const struct iw_ioctl_description * descr;
struct iw_request_info info;
int ret = -EINVAL;
- int user_size = 0;
/* Get the description of the IOCTL */
if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
#endif /* WE_SET_EVENT */
} else {
char * extra;
+ int extra_size;
+ int user_length = 0;
int err;
+ /* Calculate space needed by arguments. Always allocate
+ * for max space. Easier, and won't last long... */
+ extra_size = descr->max_tokens * descr->token_size;
+
/* Check what user space is giving us */
if(IW_IS_SET(cmd)) {
/* Check NULL pointer */
if(iwr->u.data.pointer == NULL)
return -EFAULT;
/* Save user space buffer size for checking */
- user_size = iwr->u.data.length;
+ user_length = iwr->u.data.length;
+
+ /* Don't check if user_length > max to allow forward
+ * compatibility. The test user_length < min is
+ * implied by the test at the end. */
+
+ /* Support for very large requests */
+ if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+ (user_length > descr->max_tokens)) {
+ /* Allow userspace to GET more than max so
+ * we can support any size GET requests.
+ * There is still a limit : -ENOMEM. */
+ extra_size = user_length * descr->token_size;
+ /* Note : user_length is originally a __u16,
+ * and token_size is controlled by us,
+ * so extra_size won't get negative and
+ * won't overflow... */
+ }
}
#ifdef WE_IOCTL_DEBUG
printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
- dev->name, descr->max_tokens * descr->token_size);
+ dev->name, extra_size);
#endif /* WE_IOCTL_DEBUG */
- /* Always allocate for max space. Easier, and won't last
- * long... */
- extra = kmalloc(descr->max_tokens * descr->token_size,
- GFP_KERNEL);
+ /* Create the kernel buffer */
+ extra = kmalloc(extra_size, GFP_KERNEL);
if (extra == NULL) {
return -ENOMEM;
}
/* If we have something to return to the user */
if (!ret && IW_IS_GET(cmd)) {
-#ifdef WE_STRICT_WRITE
/* Check if there is enough buffer up there */
- if(user_size < iwr->u.data.length) {
- printk(KERN_ERR "%s (WE) : Buffer for request %04X too small (%d<%d)\n", dev->name, cmd, user_size, iwr->u.data.length);
+ if(user_length < iwr->u.data.length) {
kfree(extra);
return -E2BIG;
}
-#endif /* WE_STRICT_WRITE */
err = copy_to_user(iwr->u.data.pointer, extra,
iwr->u.data.length *
iw_handler handler)
{
struct iwreq * iwr = (struct iwreq *) ifr;
- struct iw_priv_args * descr = NULL;
+ const struct iw_priv_args * descr = NULL;
struct iw_request_info info;
int extra_size = 0;
int i;
((extra_size + offset) <= IFNAMSIZ))
extra_size = 0;
} else {
- /* Size of set arguments */
+ /* Size of get arguments */
extra_size = get_priv_size(descr->get_args);
/* Does it fits in iwr ? */
/* If we have something to return to the user */
if (!ret && IW_IS_GET(cmd)) {
+
+ /* Adjust for the actual length if it's variable,
+ * avoid leaking kernel bits outside. */
+ if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
+ extra_size = adjust_priv_size(descr->get_args,
+ &(iwr->u));
+ }
+
err = copy_to_user(iwr->u.data.pointer, extra,
extra_size);
if (err)
nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
r = NLMSG_DATA(nlh);
r->ifi_family = AF_UNSPEC;
+ r->__ifi_pad = 0;
r->ifi_type = dev->type;
r->ifi_index = dev->ifindex;
r->ifi_flags = dev->flags;
kfree_skb(skb);
return;
}
- NETLINK_CB(skb).dst_groups = RTMGRP_LINK;
- netlink_broadcast(rtnl, skb, 0, RTMGRP_LINK, GFP_ATOMIC);
+ NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
+ netlink_broadcast(rtnl, skb, 0, RTNLGRP_LINK, GFP_ATOMIC);
}
#endif /* WE_EVENT_NETLINK */
struct iw_event *event; /* Mallocated whole event */
int event_len; /* Its size */
int hdr_len; /* Size of the event header */
+ int wrqu_off = 0; /* Offset in wrqu */
/* Don't "optimise" the following variable, it will crash */
unsigned cmd_index; /* *MUST* be unsigned */
- /* Get the description of the IOCTL */
+ /* Get the description of the Event */
if(cmd <= SIOCIWLAST) {
cmd_index = cmd - SIOCIWFIRST;
if(cmd_index < standard_ioctl_num)
/* Calculate extra_len - extra is NULL for restricted events */
if(extra != NULL)
extra_len = wrqu->data.length * descr->token_size;
+ /* Always at an offset in wrqu */
+ wrqu_off = IW_EV_POINT_OFF;
#ifdef WE_EVENT_DEBUG
printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
#endif /* WE_EVENT_DEBUG */
event_len = hdr_len + extra_len;
#ifdef WE_EVENT_DEBUG
- printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, event_len %d\n", dev->name, cmd, hdr_len, event_len);
+ printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len);
#endif /* WE_EVENT_DEBUG */
/* Create temporary buffer to hold the event */
/* Fill event */
event->len = event_len;
event->cmd = cmd;
- memcpy(&event->u, wrqu, hdr_len - IW_EV_LCP_LEN);
+ memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
if(extra != NULL)
memcpy(((char *) event) + hdr_len, extra, extra_len);
* 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 wireless_spy_update() and include struct iw_spy_data
- * in its private part (and advertise it in iw_handler_def->spy_offset).
+ * in its private part (and export it in net_device->wireless_data->spy_data).
* 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
*/
+/* ---------------------------------------------------------------- */
+/*
+ * Return the pointer to the spy data in the driver.
+ * Because this is called on the Rx path via wireless_spy_update(),
+ * we want it to be efficient...
+ */
+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;
+}
+
/*------------------------------------------------------------------*/
/*
* Standard Wireless Handler : set Spy List
union iwreq_data * wrqu,
char * extra)
{
-#ifdef IW_WIRELESS_SPY
- struct iw_spy_data * spydata = (dev->priv +
- dev->wireless_handlers->spy_offset);
+ 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.
- * As we don't disable interrupts, we need to do this to avoid races.
- * As we are the only writer, this is good enough. */
+ * 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. */
+ wmb();
+
/* Are there are addresses to copy? */
if(wrqu->data.length > 0) {
int i;
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);
+ printk(KERN_DEBUG "iw_handler_set_spy() : wireless_data %p, spydata %p, num %d\n", dev->wireless_data, 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][5]);
#endif /* WE_SPY_DEBUG */
}
+
+ /* Make sure above is updated before re-enabling */
+ wmb();
+
/* Enable addresses */
spydata->spy_number = wrqu->data.length;
return 0;
-#else /* IW_WIRELESS_SPY */
- return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_SPY */
}
/*------------------------------------------------------------------*/
union iwreq_data * wrqu,
char * extra)
{
-#ifdef IW_WIRELESS_SPY
- struct iw_spy_data * spydata = (dev->priv +
- dev->wireless_handlers->spy_offset);
+ 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. */
sizeof(struct iw_quality) * spydata->spy_number);
/* Reset updated flags. */
for(i = 0; i < spydata->spy_number; i++)
- spydata->spy_stat[i].updated = 0;
+ spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
return 0;
-#else /* IW_WIRELESS_SPY */
- return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_SPY */
}
/*------------------------------------------------------------------*/
union iwreq_data * wrqu,
char * extra)
{
-#ifdef IW_WIRELESS_THRSPY
- struct iw_spy_data * spydata = (dev->priv +
- dev->wireless_handlers->spy_offset);
+ 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));
#endif /* WE_SPY_DEBUG */
return 0;
-#else /* IW_WIRELESS_THRSPY */
- return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_THRSPY */
}
/*------------------------------------------------------------------*/
union iwreq_data * wrqu,
char * extra)
{
-#ifdef IW_WIRELESS_THRSPY
- struct iw_spy_data * spydata = (dev->priv +
- dev->wireless_handlers->spy_offset);
+ 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;
-#else /* IW_WIRELESS_THRSPY */
- return -EOPNOTSUPP;
-#endif /* IW_WIRELESS_THRSPY */
}
-#ifdef IW_WIRELESS_THRSPY
/*------------------------------------------------------------------*/
/*
* Prepare and send a Spy Threshold event
/* Send event to user space */
wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
}
-#endif /* IW_WIRELESS_THRSPY */
/* ---------------------------------------------------------------- */
/*
unsigned char * address,
struct iw_quality * wstats)
{
-#ifdef IW_WIRELESS_SPY
- struct iw_spy_data * spydata = (dev->priv +
- dev->wireless_handlers->spy_offset);
+ 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;
+
#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]);
+ printk(KERN_DEBUG "wireless_spy_update() : wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, 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)) {
+ if(!compare_ether_addr(address, spydata->spy_address[i])) {
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
}
}
}
-#endif /* IW_WIRELESS_THRSPY */
-#endif /* IW_WIRELESS_SPY */
}
EXPORT_SYMBOL(iw_handler_get_spy);