* This file implement the Wireless Extensions APIs.
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
*
* (As all part of the Linux kernel, this file is GPL)
*/
*
* v8 - 17.02.06 - Jean II
* o RtNetlink requests support (SET/GET)
+ *
+ * v8b - 03.08.06 - Herbert Xu
+ * o Fix Wireless Event locking issues.
+ *
+ * v9 - 14.3.06 - Jean II
+ * o Change length in ESSID and NICK to strlen() instead of strlen()+1
+ * o Make standard_ioctl_num and standard_event_num unsigned
+ * o Remove (struct net_device *)->get_wireless_stats()
+ *
+ * v10 - 16.3.07 - Jean II
+ * o Prevent leaking of kernel space in stream on 64 bits.
*/
/***************************** INCLUDES *****************************/
-#include <linux/config.h> /* Not needed ??? */
#include <linux/module.h>
#include <linux/types.h> /* off_t */
#include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */
#include <linux/wireless.h> /* Pretty obvious */
#include <net/iw_handler.h> /* New driver API */
+#include <net/netlink.h>
#include <asm/uaccess.h> /* copy_to_user() */
[SIOCSIWESSID - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
- .max_tokens = IW_ESSID_MAX_SIZE + 1,
+ .max_tokens = IW_ESSID_MAX_SIZE,
.flags = IW_DESCR_FLAG_EVENT,
},
[SIOCGIWESSID - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
- .max_tokens = IW_ESSID_MAX_SIZE + 1,
+ .max_tokens = IW_ESSID_MAX_SIZE,
.flags = IW_DESCR_FLAG_DUMP,
},
[SIOCSIWNICKN - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
- .max_tokens = IW_ESSID_MAX_SIZE + 1,
+ .max_tokens = IW_ESSID_MAX_SIZE,
},
[SIOCGIWNICKN - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
- .max_tokens = IW_ESSID_MAX_SIZE + 1,
+ .max_tokens = IW_ESSID_MAX_SIZE,
},
[SIOCSIWRATE - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_PARAM,
.max_tokens = sizeof(struct iw_pmksa),
},
};
-static const int standard_ioctl_num = (sizeof(standard_ioctl) /
- sizeof(struct iw_ioctl_description));
+static const unsigned standard_ioctl_num = (sizeof(standard_ioctl) /
+ sizeof(struct iw_ioctl_description));
/*
* Meta-data about all the additional standard Wireless Extension events
.header_type = IW_HEADER_TYPE_ADDR,
},
[IWEVEXPIRED - IWEVFIRST] = {
- .header_type = IW_HEADER_TYPE_ADDR,
+ .header_type = IW_HEADER_TYPE_ADDR,
},
[IWEVGENIE - IWEVFIRST] = {
.header_type = IW_HEADER_TYPE_POINT,
.max_tokens = IW_GENERIC_IE_MAX,
},
[IWEVMICHAELMICFAILURE - IWEVFIRST] = {
- .header_type = IW_HEADER_TYPE_POINT,
+ .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = sizeof(struct iw_michaelmicfailure),
},
.max_tokens = sizeof(struct iw_pmkid_cand),
},
};
-static const int standard_event_num = (sizeof(standard_event) /
- sizeof(struct iw_ioctl_description));
+static const unsigned standard_event_num = (sizeof(standard_event) /
+ sizeof(struct iw_ioctl_description));
/* Size (in bytes) of the various private data types */
static const char iw_priv_type_size[] = {
IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
};
+/* Size (in bytes) of various events, as packed */
+static const int event_type_pk_size[] = {
+ IW_EV_LCP_PK_LEN, /* IW_HEADER_TYPE_NULL */
+ 0,
+ IW_EV_CHAR_PK_LEN, /* IW_HEADER_TYPE_CHAR */
+ 0,
+ IW_EV_UINT_PK_LEN, /* IW_HEADER_TYPE_UINT */
+ IW_EV_FREQ_PK_LEN, /* IW_HEADER_TYPE_FREQ */
+ IW_EV_ADDR_PK_LEN, /* IW_HEADER_TYPE_ADDR */
+ 0,
+ IW_EV_POINT_PK_LEN, /* Without variable payload */
+ IW_EV_PARAM_PK_LEN, /* IW_HEADER_TYPE_PARAM */
+ IW_EV_QUAL_PK_LEN, /* IW_HEADER_TYPE_QUAL */
+};
+
/************************ COMMON SUBROUTINES ************************/
/*
* Stuff that may be used in various place or doesn't fit in one
unsigned int index; /* *MUST* be unsigned */
/* Check if we have some wireless handlers defined */
- if(dev->wireless_handlers == NULL)
+ if (dev->wireless_handlers == NULL)
return NULL;
/* Try as a standard command */
index = cmd - SIOCIWFIRST;
- if(index < dev->wireless_handlers->num_standard)
+ if (index < dev->wireless_handlers->num_standard)
return dev->wireless_handlers->standard[index];
/* Try as a private command */
index = cmd - SIOCIWFIRSTPRIV;
- if(index < dev->wireless_handlers->num_private)
+ if (index < dev->wireless_handlers->num_private)
return dev->wireless_handlers->private[index];
/* Not found */
static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
{
/* New location */
- if((dev->wireless_handlers != NULL) &&
+ 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;
}
*/
static inline int call_commit_handler(struct net_device * dev)
{
- if((netif_running(dev)) &&
+ if ((netif_running(dev)) &&
(dev->wireless_handlers->standard[0] != NULL)) {
/* Call the commit handler on the driver */
return dev->wireless_handlers->standard[0](dev, NULL,
wrqu->data.length = sizeof(struct iw_statistics);
/* Check if we need to clear the updated flag */
- if(wrqu->data.flags != 0)
+ if (wrqu->data.flags != 0)
stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
return 0;
} else
char * extra)
{
/* Check if the driver has something to export */
- if((dev->wireless_handlers->num_private_args == 0) ||
+ if ((dev->wireless_handlers->num_private_args == 0) ||
(dev->wireless_handlers->private_args == NULL))
return -EOPNOTSUPP;
/* Check if there is enough buffer up there */
- if(wrqu->data.length < dev->wireless_handlers->num_private_args) {
+ if (wrqu->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... */
dev->name, stats->status, stats->qual.qual,
stats->qual.updated & IW_QUAL_QUAL_UPDATED
? '.' : ' ',
- ((__s32) stats->qual.level) -
+ ((__s32) stats->qual.level) -
((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
stats->qual.updated & IW_QUAL_LEVEL_UPDATED
? '.' : ' ',
- ((__s32) stats->qual.noise) -
+ ((__s32) stats->qual.noise) -
((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
stats->qual.updated & IW_QUAL_NOISE_UPDATED
? '.' : ' ',
return seq_open(file, &wireless_seq_ops);
}
-static struct file_operations wireless_seq_fops = {
+static const struct file_operations wireless_seq_fops = {
.owner = THIS_MODULE,
.open = wireless_seq_open,
.read = seq_read,
int ret = -EINVAL;
/* Get the description of the IOCTL */
- if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+ if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
return -EOPNOTSUPP;
descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
info.flags = 0;
/* Check if we have a pointer to user space data or not */
- if(descr->header_type != IW_HEADER_TYPE_POINT) {
+ if (descr->header_type != IW_HEADER_TYPE_POINT) {
/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, &(iwr->u), NULL);
#ifdef WE_SET_EVENT
/* Generate an event to notify listeners of the change */
- if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+ if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
((ret == 0) || (ret == -EIWCOMMIT)))
wireless_send_event(dev, cmd, &(iwr->u), NULL);
#endif /* WE_SET_EVENT */
int extra_size;
int user_length = 0;
int err;
+ int essid_compat = 0;
/* 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 need for ESSID compatibility for WE < 21 */
+ switch (cmd) {
+ case SIOCSIWESSID:
+ case SIOCGIWESSID:
+ case SIOCSIWNICKN:
+ case SIOCGIWNICKN:
+ if (iwr->u.data.length == descr->max_tokens + 1)
+ essid_compat = 1;
+ else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+ char essid[IW_ESSID_MAX_SIZE + 1];
+
+ err = copy_from_user(essid, iwr->u.data.pointer,
+ iwr->u.data.length *
+ descr->token_size);
+ if (err)
+ return -EFAULT;
+
+ if (essid[iwr->u.data.length - 1] == '\0')
+ essid_compat = 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ iwr->u.data.length -= essid_compat;
+
/* Check what user space is giving us */
- if(IW_IS_SET(cmd)) {
+ if (IW_IS_SET(cmd)) {
/* Check NULL pointer */
- if((iwr->u.data.pointer == NULL) &&
+ if ((iwr->u.data.pointer == NULL) &&
(iwr->u.data.length != 0))
return -EFAULT;
/* Check if number of token fits within bounds */
- if(iwr->u.data.length > descr->max_tokens)
+ if (iwr->u.data.length > descr->max_tokens)
return -E2BIG;
- if(iwr->u.data.length < descr->min_tokens)
+ if (iwr->u.data.length < descr->min_tokens)
return -EINVAL;
} else {
/* Check NULL pointer */
- if(iwr->u.data.pointer == NULL)
+ if (iwr->u.data.pointer == NULL)
return -EFAULT;
/* Save user space buffer size for checking */
user_length = iwr->u.data.length;
* implied by the test at the end. */
/* Support for very large requests */
- if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+ 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.
#endif /* WE_IOCTL_DEBUG */
/* Create the kernel buffer */
- extra = kmalloc(extra_size, GFP_KERNEL);
+ /* kzalloc ensures NULL-termination for essid_compat */
+ extra = kzalloc(extra_size, GFP_KERNEL);
if (extra == NULL) {
return -ENOMEM;
}
/* If it is a SET, get all the extra data in here */
- if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+ if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
err = copy_from_user(extra, iwr->u.data.pointer,
iwr->u.data.length *
descr->token_size);
/* Call the handler */
ret = handler(dev, &info, &(iwr->u), extra);
+ iwr->u.data.length += essid_compat;
+
/* If we have something to return to the user */
if (!ret && IW_IS_GET(cmd)) {
/* Check if there is enough buffer up there */
- if(user_length < iwr->u.data.length) {
+ if (user_length < iwr->u.data.length) {
kfree(extra);
return -E2BIG;
}
iwr->u.data.length *
descr->token_size);
if (err)
- ret = -EFAULT;
+ ret = -EFAULT;
#ifdef WE_IOCTL_DEBUG
printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
dev->name,
#ifdef WE_SET_EVENT
/* Generate an event to notify listeners of the change */
- if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+ if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
((ret == 0) || (ret == -EIWCOMMIT))) {
- if(descr->flags & IW_DESCR_FLAG_RESTRICT)
+ if (descr->flags & IW_DESCR_FLAG_RESTRICT)
/* If the event is restricted, don't
* export the payload */
wireless_send_event(dev, cmd, &(iwr->u), NULL);
}
/* Call commit handler if needed and defined */
- if(ret == -EIWCOMMIT)
+ if (ret == -EIWCOMMIT)
ret = call_commit_handler(dev);
/* Here, we will generate the appropriate event if needed */
int ret = -EINVAL;
/* Get the description of the IOCTL */
- for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
- if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+ for (i = 0; i < dev->wireless_handlers->num_private_args; i++)
+ if (cmd == dev->wireless_handlers->private_args[i].cmd) {
descr = &(dev->wireless_handlers->private_args[i]);
break;
}
#ifdef WE_IOCTL_DEBUG
printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n",
ifr->ifr_name, cmd);
- if(descr) {
+ if (descr) {
printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n",
dev->name, descr->name,
descr->set_args, descr->get_args);
#endif /* WE_IOCTL_DEBUG */
/* Compute the size of the set/get arguments */
- if(descr != NULL) {
- if(IW_IS_SET(cmd)) {
+ if (descr != NULL) {
+ if (IW_IS_SET(cmd)) {
int offset = 0; /* For sub-ioctls */
/* Check for sub-ioctl handler */
- if(descr->name[0] == '\0')
+ if (descr->name[0] == '\0')
/* Reserve one int for sub-ioctl index */
offset = sizeof(__u32);
extra_size = get_priv_size(descr->set_args);
/* Does it fits in iwr ? */
- if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+ if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
((extra_size + offset) <= IFNAMSIZ))
extra_size = 0;
} else {
extra_size = get_priv_size(descr->get_args);
/* Does it fits in iwr ? */
- if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+ if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
(extra_size <= IFNAMSIZ))
extra_size = 0;
}
info.flags = 0;
/* Check if we have a pointer to user space data or not. */
- if(extra_size == 0) {
+ if (extra_size == 0) {
/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
} else {
int err;
/* Check what user space is giving us */
- if(IW_IS_SET(cmd)) {
+ if (IW_IS_SET(cmd)) {
/* Check NULL pointer */
- if((iwr->u.data.pointer == NULL) &&
+ if ((iwr->u.data.pointer == NULL) &&
(iwr->u.data.length != 0))
return -EFAULT;
/* Does it fits within bounds ? */
- if(iwr->u.data.length > (descr->set_args &
+ if (iwr->u.data.length > (descr->set_args &
IW_PRIV_SIZE_MASK))
return -E2BIG;
} else {
/* Check NULL pointer */
- if(iwr->u.data.pointer == NULL)
+ if (iwr->u.data.pointer == NULL)
return -EFAULT;
}
}
/* If it is a SET, get all the extra data in here */
- if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+ if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
err = copy_from_user(extra, iwr->u.data.pointer,
extra_size);
if (err) {
err = copy_to_user(iwr->u.data.pointer, extra,
extra_size);
if (err)
- ret = -EFAULT;
+ ret = -EFAULT;
#ifdef WE_IOCTL_DEBUG
printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
dev->name, iwr->u.data.length);
/* Call commit handler if needed and defined */
- if(ret == -EIWCOMMIT)
+ if (ret == -EIWCOMMIT)
ret = call_commit_handler(dev);
return ret;
/* A bunch of special cases, then the generic case...
* Note that 'cmd' is already filtered in dev_ioctl() with
* (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
- switch(cmd)
- {
- case SIOCGIWSTATS:
- /* Get Wireless Stats */
+ switch (cmd) {
+ case SIOCGIWSTATS:
+ /* Get Wireless Stats */
+ return ioctl_standard_call(dev,
+ ifr,
+ cmd,
+ &iw_handler_get_iwstats);
+
+ case SIOCGIWPRIV:
+ /* Check if we have some wireless handlers defined */
+ if (dev->wireless_handlers != NULL) {
+ /* We export to user space the definition of
+ * the private handler ourselves */
return ioctl_standard_call(dev,
ifr,
cmd,
- &iw_handler_get_iwstats);
-
- case SIOCGIWPRIV:
- /* Check if we have some wireless handlers defined */
- if(dev->wireless_handlers != NULL) {
- /* We export to user space the definition of
- * the private handler ourselves */
+ &iw_handler_get_private);
+ }
+ // ## Fall-through for old API ##
+ default:
+ /* Generic IOCTL */
+ /* Basic check */
+ if (!netif_device_present(dev))
+ return -ENODEV;
+ /* New driver API : try to find the handler */
+ handler = get_handler(dev, cmd);
+ if (handler != NULL) {
+ /* Standard and private are not the same */
+ if (cmd < SIOCIWFIRSTPRIV)
return ioctl_standard_call(dev,
ifr,
cmd,
- &iw_handler_get_private);
- }
- // ## Fall-through for old API ##
- default:
- /* Generic IOCTL */
- /* Basic check */
- if (!netif_device_present(dev))
- return -ENODEV;
- /* New driver API : try to find the handler */
- handler = get_handler(dev, cmd);
- if(handler != NULL) {
- /* Standard and private are not the same */
- if(cmd < SIOCIWFIRSTPRIV)
- return ioctl_standard_call(dev,
- ifr,
- cmd,
- handler);
- else
- return ioctl_private_call(dev,
- ifr,
- cmd,
- handler);
- }
- /* Old driver API : call driver ioctl handler */
- if (dev->do_ioctl) {
- return dev->do_ioctl(dev, ifr, cmd);
- }
- return -EOPNOTSUPP;
+ handler);
+ else
+ return ioctl_private_call(dev,
+ ifr,
+ cmd,
+ handler);
+ }
+ /* Old driver API : call driver ioctl handler */
+ if (dev->do_ioctl) {
+ return dev->do_ioctl(dev, ifr, cmd);
+ }
+ return -EOPNOTSUPP;
}
/* Not reached */
return -EINVAL;
/* Get the description of the Request */
cmd = request->cmd;
- if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+ if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
return -EOPNOTSUPP;
descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
/* Check if wrqu is complete */
hdr_len = event_type_size[descr->header_type];
- if(request_len < hdr_len) {
+ if (request_len < hdr_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG
"%s (WE.r) : Wireless request too short (%d)\n",
info.flags = 0;
/* Check if we have extra data in the reply or not */
- if(descr->header_type != IW_HEADER_TYPE_POINT) {
+ if (descr->header_type != IW_HEADER_TYPE_POINT) {
/* Create the kernel buffer that we will return.
* It's at an offset to match the TYPE_POINT case... */
memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
/* Use our own copy of wrqu */
wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
- + IW_EV_LCP_LEN);
+ + IW_EV_LCP_PK_LEN);
/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, wrqu, NULL);
/* Get a temp copy of wrqu (skip pointer) */
memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
- ((char *) request) + IW_EV_LCP_LEN,
- IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+ ((char *) request) + IW_EV_LCP_PK_LEN,
+ IW_EV_POINT_LEN - IW_EV_LCP_PK_LEN);
/* Calculate space needed by arguments. Always allocate
* for max space. Easier, and won't last long... */
extra_size = descr->max_tokens * descr->token_size;
/* Support for very large requests */
- if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+ if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
(wrqu_point.data.length > descr->max_tokens))
extra_size = (wrqu_point.data.length
* descr->token_size);
- buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
+ buffer_size = extra_size + IW_EV_POINT_PK_LEN + IW_EV_POINT_OFF;
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
dev->name, extra_size, buffer_size);
/* Put wrqu in the right place (just before extra).
* Leave space for IWE header and dummy pointer...
- * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
+ * Note that IW_EV_LCP_PK_LEN==4 bytes, so it's still aligned.
*/
- memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+ memcpy(buffer + IW_EV_LCP_PK_LEN + IW_EV_POINT_OFF,
((char *) &wrqu_point) + IW_EV_POINT_OFF,
- IW_EV_POINT_LEN - IW_EV_LCP_LEN);
- wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
+ IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
+ wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_PK_LEN);
/* Extra comes logically after that. Offset +12 bytes. */
- extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
+ extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_PK_LEN;
/* Call the handler */
ret = handler(dev, &info, wrqu, extra);
/* Calculate real returned length */
extra_size = (wrqu->data.length * descr->token_size);
/* Re-adjust reply size */
- request->len = extra_size + IW_EV_POINT_LEN;
+ request->len = extra_size + IW_EV_POINT_PK_LEN;
/* Put the iwe header where it should, i.e. scrap the
* dummy pointer. */
- memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
+ memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_PK_LEN);
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
#endif /* WE_RTNETLINK_DEBUG */
/* Check if there is enough buffer up there */
- if(wrqu_point.data.length < wrqu->data.length)
+ if (wrqu_point.data.length < wrqu->data.length)
ret = -E2BIG;
}
*p_len = request->len;
} else {
/* Cleanup */
- if(buffer)
+ if (buffer)
kfree(buffer);
}
/* Get the description of the Request */
cmd = request->cmd;
- if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+ if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
return -EOPNOTSUPP;
descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
#endif /* WE_RTNETLINK_DEBUG */
/* Extract fixed header from request. This is properly aligned. */
- wrqu = &request->u;
+ wrqu = (union iwreq_data *) (((char *) request) + IW_EV_LCP_PK_LEN);
/* Check if wrqu is complete */
- hdr_len = event_type_size[descr->header_type];
- if(request_len < hdr_len) {
+ hdr_len = event_type_pk_size[descr->header_type];
+ if (request_len < hdr_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG
"%s (WE.r) : Wireless request too short (%d)\n",
info.flags = 0;
/* Check if we have extra data in the request or not */
- if(descr->header_type != IW_HEADER_TYPE_POINT) {
+ if (descr->header_type != IW_HEADER_TYPE_POINT) {
/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, wrqu, NULL);
/* Put wrqu in the right place (skip pointer) */
memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
- wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+ wrqu, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
/* Don't forget about the event code... */
wrqu = &wrqu_point;
/* Check if number of token fits within bounds */
- if(wrqu_point.data.length > descr->max_tokens)
+ if (wrqu_point.data.length > descr->max_tokens)
return -E2BIG;
- if(wrqu_point.data.length < descr->min_tokens)
+ if (wrqu_point.data.length < descr->min_tokens)
return -EINVAL;
/* Real length of payload */
extra_len = wrqu_point.data.length * descr->token_size;
/* Check if request is self consistent */
- if((request_len - hdr_len) < extra_len) {
+ if ((request_len - hdr_len) < extra_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
dev->name, extra_size);
#ifdef WE_SET_EVENT
/* Generate an event to notify listeners of the change */
- if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+ if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
((ret == 0) || (ret == -EIWCOMMIT))) {
- if(descr->flags & IW_DESCR_FLAG_RESTRICT)
+ if (descr->flags & IW_DESCR_FLAG_RESTRICT)
/* If the event is restricted, don't
* export the payload */
wireless_send_event(dev, cmd, wrqu, NULL);
#endif /* WE_SET_EVENT */
/* Cleanup - I told you it wasn't that long ;-) */
- if(extra)
+ if (extra)
kfree(extra);
/* Call commit handler if needed and defined */
- if(ret == -EIWCOMMIT)
+ if (ret == -EIWCOMMIT)
ret = call_commit_handler(dev);
return ret;
/* Get the description of the Request */
cmd = request->cmd;
- for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
- if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+ for (i = 0; i < dev->wireless_handlers->num_private_args; i++)
+ if (cmd == dev->wireless_handlers->private_args[i].cmd) {
descr = &(dev->wireless_handlers->private_args[i]);
break;
}
- if(descr == NULL)
+ if (descr == NULL)
return -EOPNOTSUPP;
#ifdef WE_RTNETLINK_DEBUG
extra_size = get_priv_size(descr->get_args);
/* Does it fits in wrqu ? */
- if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+ if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
(extra_size <= IFNAMSIZ)) {
hdr_len = extra_size;
extra_size = 0;
} else {
- hdr_len = IW_EV_POINT_LEN;
+ hdr_len = IW_EV_POINT_PK_LEN;
}
/* Check if wrqu is complete */
- if(request_len < hdr_len) {
+ if (request_len < hdr_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG
"%s (WE.r) : Wireless request too short (%d)\n",
info.flags = 0;
/* Check if we have a pointer to user space data or not. */
- if(extra_size == 0) {
+ if (extra_size == 0) {
/* Create the kernel buffer that we will return.
* It's at an offset to match the TYPE_POINT case... */
memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
/* Use our own copy of wrqu */
wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
- + IW_EV_LCP_LEN);
+ + IW_EV_LCP_PK_LEN);
/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, wrqu, (char *) wrqu);
char * extra;
/* Buffer for full reply */
- buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
+ buffer_size = extra_size + IW_EV_POINT_PK_LEN + IW_EV_POINT_OFF;
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
/* Put wrqu in the right place (just before extra).
* Leave space for IWE header and dummy pointer...
- * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
+ * Note that IW_EV_LCP_PK_LEN==4 bytes, so it's still aligned.
*/
- memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
- ((char *) request) + IW_EV_LCP_LEN,
- IW_EV_POINT_LEN - IW_EV_LCP_LEN);
- wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
+ memcpy(buffer + IW_EV_LCP_PK_LEN + IW_EV_POINT_OFF,
+ ((char *) request) + IW_EV_LCP_PK_LEN,
+ IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
+ wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_PK_LEN);
/* Extra comes logically after that. Offset +12 bytes. */
- extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
+ extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_PK_LEN;
/* Call the handler */
ret = handler(dev, &info, wrqu, extra);
if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
extra_size = adjust_priv_size(descr->get_args, wrqu);
/* Re-adjust reply size */
- request->len = extra_size + IW_EV_POINT_LEN;
+ request->len = extra_size + IW_EV_POINT_PK_LEN;
/* Put the iwe header where it should, i.e. scrap the
* dummy pointer. */
- memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
+ memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_PK_LEN);
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
*p_len = request->len;
} else {
/* Cleanup */
- if(buffer)
+ if (buffer)
kfree(buffer);
}
/* Get the description of the Request */
cmd = request->cmd;
- for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
- if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+ for (i = 0; i < dev->wireless_handlers->num_private_args; i++)
+ if (cmd == dev->wireless_handlers->private_args[i].cmd) {
descr = &(dev->wireless_handlers->private_args[i]);
break;
}
- if(descr == NULL)
+ if (descr == NULL)
return -EOPNOTSUPP;
#ifdef WE_RTNETLINK_DEBUG
/* Compute the size of the set arguments */
/* Check for sub-ioctl handler */
- if(descr->name[0] == '\0')
+ if (descr->name[0] == '\0')
/* Reserve one int for sub-ioctl index */
offset = sizeof(__u32);
extra_size = get_priv_size(descr->set_args);
/* Does it fits in wrqu ? */
- if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+ if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
(extra_size <= IFNAMSIZ)) {
- hdr_len = IW_EV_LCP_LEN + extra_size;
+ hdr_len = IW_EV_LCP_PK_LEN + extra_size;
extra_size = 0;
} else {
- hdr_len = IW_EV_POINT_LEN;
+ hdr_len = IW_EV_POINT_PK_LEN;
}
/* Extract fixed header from request. This is properly aligned. */
- wrqu = &request->u;
+ wrqu = (union iwreq_data *) (((char *) request) + IW_EV_LCP_PK_LEN);
/* Check if wrqu is complete */
- if(request_len < hdr_len) {
+ if (request_len < hdr_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG
"%s (WE.r) : Wireless request too short (%d)\n",
info.flags = 0;
/* Check if we have a pointer to user space data or not. */
- if(extra_size == 0) {
+ if (extra_size == 0) {
/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, wrqu, (char *) wrqu);
/* Put wrqu in the right place (skip pointer) */
memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
- wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+ wrqu, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
/* Does it fits within bounds ? */
- if(wrqu_point.data.length > (descr->set_args &
+ if (wrqu_point.data.length > (descr->set_args &
IW_PRIV_SIZE_MASK))
return -E2BIG;
extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
/* Check if request is self consistent */
- if((request_len - hdr_len) < extra_len) {
+ if ((request_len - hdr_len) < extra_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
dev->name, extra_size);
}
/* Call commit handler if needed and defined */
- if(ret == -EIWCOMMIT)
+ if (ret == -EIWCOMMIT)
ret = call_commit_handler(dev);
return ret;
iw_handler handler;
/* Check length */
- if(len < IW_EV_LCP_LEN) {
+ if (len < IW_EV_LCP_PK_LEN) {
printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
dev->name, len);
return -EINVAL;
}
/* ReCheck length (len may have padding) */
- if(request->len > len) {
+ if (request->len > len) {
printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
dev->name, request->len, len);
return -EINVAL;
}
/* Only accept GET requests in here */
- if(!IW_IS_GET(request->cmd))
+ if (!IW_IS_GET(request->cmd))
return -EOPNOTSUPP;
/* If command is `get the encoding parameters', check if
}
/* Special cases */
- if(request->cmd == SIOCGIWSTATS)
+ if (request->cmd == SIOCGIWSTATS)
/* Get Wireless Stats */
return rtnetlink_standard_get(dev,
request,
request->len,
&iw_handler_get_iwstats,
p_buf, p_len);
- if(request->cmd == SIOCGIWPRIV) {
+ if (request->cmd == SIOCGIWPRIV) {
/* Check if we have some wireless handlers defined */
- if(dev->wireless_handlers == NULL)
+ if (dev->wireless_handlers == NULL)
return -EOPNOTSUPP;
/* Get Wireless Stats */
return rtnetlink_standard_get(dev,
/* Try to find the handler */
handler = get_handler(dev, request->cmd);
- if(handler != NULL) {
+ if (handler != NULL) {
/* Standard and private are not the same */
- if(request->cmd < SIOCIWFIRSTPRIV)
+ if (request->cmd < SIOCIWFIRSTPRIV)
return rtnetlink_standard_get(dev,
request,
request->len,
iw_handler handler;
/* Check length */
- if(len < IW_EV_LCP_LEN) {
+ if (len < IW_EV_LCP_PK_LEN) {
printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
dev->name, len);
return -EINVAL;
}
/* ReCheck length (len may have padding) */
- if(request->len > len) {
+ if (request->len > len) {
printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
dev->name, request->len, len);
return -EINVAL;
}
/* Only accept SET requests in here */
- if(!IW_IS_SET(request->cmd))
+ if (!IW_IS_SET(request->cmd))
return -EOPNOTSUPP;
/* Basic check */
/* New driver API : try to find the handler */
handler = get_handler(dev, request->cmd);
- if(handler != NULL) {
+ if (handler != NULL) {
/* Standard and private are not the same */
- if(request->cmd < SIOCIWFIRSTPRIV)
+ if (request->cmd < SIOCIWFIRSTPRIV)
return rtnetlink_standard_set(dev,
request,
request->len,
*/
#ifdef WE_EVENT_RTNETLINK
+/* ---------------------------------------------------------------- */
+/*
+ * Locking...
+ * ----------
+ *
+ * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
+ * the locking issue in here and implementing this code !
+ *
+ * The issue : wireless_send_event() is often called in interrupt context,
+ * while the Netlink layer can never be called in interrupt context.
+ * The fully formed RtNetlink events are queued, and then a tasklet is run
+ * to feed those to Netlink.
+ * The skb_queue is interrupt safe, and its lock is not held while calling
+ * Netlink, so there is no possibility of dealock.
+ * Jean II
+ */
+
static struct sk_buff_head wireless_nlevent_queue;
+static int __init wireless_nlevent_init(void)
+{
+ skb_queue_head_init(&wireless_nlevent_queue);
+ return 0;
+}
+
+subsys_initcall(wireless_nlevent_init);
+
static void wireless_nlevent_process(unsigned long data)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&wireless_nlevent_queue)))
- netlink_broadcast(rtnl, skb, 0, RTNLGRP_LINK, GFP_ATOMIC);
+ rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
}
static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
tasklet_schedule(&wireless_nlevent_tasklet);
}
-static int __init wireless_nlevent_init(void)
-{
- skb_queue_head_init(&wireless_nlevent_queue);
- return 0;
-}
-
-subsys_initcall(wireless_nlevent_init);
#endif /* WE_EVENT_RTNETLINK */
/* ---------------------------------------------------------------- */
unsigned cmd_index; /* *MUST* be unsigned */
/* Get the description of the Event */
- if(cmd <= SIOCIWLAST) {
+ if (cmd <= SIOCIWLAST) {
cmd_index = cmd - SIOCIWFIRST;
- if(cmd_index < standard_ioctl_num)
+ if (cmd_index < standard_ioctl_num)
descr = &(standard_ioctl[cmd_index]);
} else {
cmd_index = cmd - IWEVFIRST;
- if(cmd_index < standard_event_num)
+ if (cmd_index < standard_event_num)
descr = &(standard_event[cmd_index]);
}
/* Don't accept unknown events */
- if(descr == NULL) {
+ if (descr == NULL) {
/* Note : we don't return an error to the driver, because
* the driver would not know what to do about it. It can't
* return an error to the user, because the event is not
* The best the driver could do is to log an error message.
* We will do it ourselves instead...
*/
- printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
+ printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
dev->name, cmd);
return;
}
#endif /* WE_EVENT_DEBUG */
/* Check extra parameters and set extra_len */
- if(descr->header_type == IW_HEADER_TYPE_POINT) {
+ if (descr->header_type == IW_HEADER_TYPE_POINT) {
/* Check if number of token fits within bounds */
- if(wrqu->data.length > descr->max_tokens) {
- printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
+ if (wrqu->data.length > descr->max_tokens) {
+ printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
return;
}
- if(wrqu->data.length < descr->min_tokens) {
- printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
+ if (wrqu->data.length < descr->min_tokens) {
+ printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
return;
}
/* Calculate extra_len - extra is NULL for restricted events */
- if(extra != NULL)
+ if (extra != NULL)
extra_len = wrqu->data.length * descr->token_size;
/* Always at an offset in wrqu */
wrqu_off = IW_EV_POINT_OFF;
/* Create temporary buffer to hold the event */
event = kmalloc(event_len, GFP_ATOMIC);
- if(event == NULL)
+ if (event == NULL)
return;
/* Fill event */
event->len = event_len;
event->cmd = cmd;
memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
- if(extra != NULL)
+ if (extra != NULL)
memcpy(((char *) event) + hdr_len, extra, extra_len);
#ifdef WE_EVENT_RTNETLINK
static inline struct iw_spy_data * get_spydata(struct net_device *dev)
{
/* This is the new way */
- if(dev->wireless_data)
+ if (dev->wireless_data)
return(dev->wireless_data->spy_data);
return NULL;
}
struct sockaddr * address = (struct sockaddr *) extra;
/* Make sure driver is not buggy or using the old API */
- if(!spydata)
+ if (!spydata)
return -EOPNOTSUPP;
/* Disable spy collection while we copy the addresses.
* 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();
+ smp_wmb();
/* Are there are addresses to copy? */
- if(wrqu->data.length > 0) {
+ if (wrqu->data.length > 0) {
int i;
/* Copy addresses */
- for(i = 0; i < wrqu->data.length; i++)
+ for (i = 0; i < wrqu->data.length; i++)
memcpy(spydata->spy_address[i], address[i].sa_data,
ETH_ALEN);
/* Reset stats */
}
/* Make sure above is updated before re-enabling */
- wmb();
+ smp_wmb();
/* Enable addresses */
spydata->spy_number = wrqu->data.length;
int i;
/* Make sure driver is not buggy or using the old API */
- if(!spydata)
+ if (!spydata)
return -EOPNOTSUPP;
wrqu->data.length = spydata->spy_number;
/* Copy addresses. */
- for(i = 0; i < spydata->spy_number; i++) {
+ 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)
+ 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++)
+ for (i = 0; i < spydata->spy_number; i++)
spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
return 0;
}
struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
/* Make sure driver is not buggy or using the old API */
- if(!spydata)
+ if (!spydata)
return -EOPNOTSUPP;
/* Just do it */
struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
/* Make sure driver is not buggy or using the old API */
- if(!spydata)
+ if (!spydata)
return -EOPNOTSUPP;
/* Just do it */
int match = -1;
/* Make sure driver is not buggy or using the old API */
- if(!spydata)
+ if (!spydata)
return;
#ifdef WE_SPY_DEBUG
#endif /* WE_SPY_DEBUG */
/* Update all records that match */
- for(i = 0; i < spydata->spy_number; i++)
- if(!compare_ether_addr(address, spydata->spy_address[i])) {
+ 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;
* 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) {
+ 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) {
+ if (wstats->level < spydata->spy_thr_low.level) {
spydata->spy_thr_under[match] = 1;
iw_send_thrspy_event(dev, spydata,
address, wstats);