From: Johannes Berg <johannes@xxxxxxxxxxxxxxxx> Signed-off-by: Johannes Berg <johannes@xxxxxxxxxxxxxxxx> Signed-off-by: Jiri Benc <jbenc@xxxxxxx> Signed-off-by: John W. Linville <linville@xxxxxxxxxxxxx> --- include/linux/netdevice.h | 7 +- include/net/cfg80211.h | 34 +- net/Kconfig | 28 + net/core/dev.c | 38 +- net/core/net-sysfs.c | 4 +- net/core/rtnetlink.c | 42 ++- net/wireless/Makefile | 10 + net/wireless/core.c | 15 +- net/wireless/core.h | 21 + net/wireless/wext-common.c | 610 ++++++++++++++++ net/wireless/wext-compat.c | 1646 +++++++++++++++++++++++++++++++++++++++++++- net/wireless/wext-old.c | 629 +----------------- net/wireless/wext.h | 13 + 13 files changed, 2433 insertions(+), 664 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c1e9962..6a9b4c8 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -348,12 +348,17 @@ struct net_device struct net_device_stats* (*get_stats)(struct net_device *dev); +#ifdef CONFIG_WIRELESS_EXT /* List of functions to handle Wireless Extensions (instead of ioctl). * See <net/iw_handler.h> for details. Jean II */ const struct iw_handler_def * wireless_handlers; /* Instance data managed by the core of Wireless Extensions. */ struct iw_public_data * wireless_data; - +#endif +#ifdef CONFIG_CFG80211_WEXT_COMPAT + /* pending config used by cfg80211/wext compat code only */ + void *cfg80211_wext_pending_config; +#endif const struct ethtool_ops *ethtool_ops; /* diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c0bc9d4..353d3ce 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5,6 +5,7 @@ #include <linux/skbuff.h> #include <linux/netdevice.h> #include <net/genetlink.h> +#include <linux/wireless.h> /* * 802.11 configuration in-kernel interface @@ -16,21 +17,12 @@ * struct cfg80211_config - description of a configuration (request) */ struct cfg80211_config { - /* first fields with 'internal' validity */ + /* see below */ + u32 valid; - /* SSID to use, valid if not NULL, ssid_len then - * contains the length. change forces reassociation */ s8 ssid_len; u8 *ssid; - /* now fields with explicit validity */ -#define CFG80211_CFG_VALID_NWID (1<<0) -#define CFG80211_CFG_VALID_RX_SENSITIVITY (1<<1) -#define CFG80211_CFG_VALID_TRANSMIT_POWER (1<<2) -#define CFG80211_CFG_VALID_FRAG_THRESHOLD (1<<3) -#define CFG80211_CFG_VALID_CHANNEL (1<<4) - unsigned int valid; - u16 network_id; s32 rx_sensitivity; u32 transmit_power; @@ -38,6 +30,13 @@ struct cfg80211_config { u32 channel; }; +#define CFG80211_CFG_VALID_SSID (1<<0) +#define CFG80211_CFG_VALID_NWID (1<<1) +#define CFG80211_CFG_VALID_RX_SENSITIVITY (1<<2) +#define CFG80211_CFG_VALID_TRANSMIT_POWER (1<<3) +#define CFG80211_CFG_VALID_FRAG_THRESHOLD (1<<4) +#define CFG80211_CFG_VALID_CHANNEL (1<<5) + struct scan_channel { u32 channel; int active; @@ -86,6 +85,9 @@ struct scan_params { * * @get_config: fill the given config structure with the current configuration * + * @get_config_valid: return a bitmask of CFG80211_CFG_VALID_* indicating + * which parameters can be set. + * * @reassociate: reassociate with current settings (SSID, BSSID if * userspace roaming is enabled) * @@ -132,6 +134,7 @@ struct cfg80211_ops { struct cfg80211_config *cfg); void (*get_config)(void *priv, struct net_device *dev, struct cfg80211_config *cfg); + u32 (*get_config_valid)(void *priv, struct net_device *dev); int (*reassociate)(void *priv, struct net_device *dev); @@ -183,4 +186,13 @@ extern int cfg80211_register(struct cfg80211_ops *ops, void *priv); */ extern void cfg80211_unregister(void *priv); +#ifdef CONFIG_CFG80211_WEXT_COMPAT +extern int cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd); +#ifdef CONFIG_CFG80211_WEXTNL_COMPAT +int cfg80211_wext_nl_set(struct net_device *dev, char *data, int len); +int cfg80211_wext_nl_get(struct net_device *dev, char *data, int len, + char **p_buf, int *p_len); +#endif +#endif + #endif /* __NET_CFG80211_H */ diff --git a/net/Kconfig b/net/Kconfig index 8d121a5..fade3fd 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -229,6 +229,34 @@ config FIB_RULES config CFG80211 tristate "Improved wireless configuration API" +config CFG80211_WEXT_COMPAT + bool "cfg80211 Wireless Extensions compatibility" + depends CFG80211 + default y + ---help--- + This option allows using devices whose drivers have been + converted to use the new cfg80211 with wireless extensions, + providing WE-20 compatibility. Note that cfg80211's "native" + interface is nl80211 using generic netlink. The wireless + extensions are being deprecated, but userspace tools may still + be using them. + + If unsure, say Y. + +config CFG80211_WEXTNL_COMPAT + bool "cfg80211 WE-netlink compatibility" + depends CFG80211 && CFG80211_WEXT_COMPAT + ---help--- + This option allows using devices whose drivers have been + converted to use the new cfg80211 with wireless extensions + over rtnetlink, providing WE-20 compatibility. Note that + cfg80211's "native" interface is nl80211 using generic netlink. + The wireless extensions are being deprecated and the netlink + based API for WE was never configured by default, nor do any + userspace tools use this feature. + + This option exists only to make Jean happy. Say N. + endif # if NET endmenu # Networking diff --git a/net/core/dev.c b/net/core/dev.c index c8822aa..31a3cba 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -116,6 +116,7 @@ #include <linux/dmaengine.h> #include <linux/err.h> #include <linux/ctype.h> +#include <net/cfg80211.h> /* * The list of packet types we will receive (as opposed to discard) @@ -2228,7 +2229,7 @@ static struct file_operations softnet_seq_fops = { .release = seq_release, }; -#ifdef CONFIG_WIRELESS_EXT +#if defined(CONFIG_WIRELESS_EXT) || defined(CFG80211_WEXT_COMPAT) extern int wireless_proc_init(void); #else #define wireless_proc_init() 0 @@ -2798,6 +2799,39 @@ int dev_ioctl(unsigned int cmd, void __user *arg) ret = -EFAULT; return ret; } +#ifdef CONFIG_CFG80211_WEXT_COMPAT + /* Take care of cfg80211 WE compatibility */ + if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { + /* If command is `set a parameter', or + * `get the encoding parameters', check if + * the user has the right to do it */ + if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE + || cmd == SIOCGIWENCODEEXT) { + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + } + dev_load(ifr.ifr_name); + rtnl_lock(); + /* Follow me in net/wireless/wext-compat.c */ + ret = cfg80211_wext_ioctl(&ifr, cmd); + rtnl_unlock(); + if (ret == 0 && IW_IS_GET(cmd) && + copy_to_user(arg, &ifr, + sizeof(struct ifreq))) + ret = -EFAULT; + /* haha, I cheat here by allowing a driver or + * stack to have both WE or CFG80211-WE for + * a little while during conversion... hope that + * ENOSYS is only used to indicate not implemented + * + * if wireless extensions are not configured + * then this is the last thing here so that + * if we fall through we return -EINVAL + */ + if (ret != -ENOSYS) + return ret; + } +#endif #ifdef CONFIG_WIRELESS_EXT /* Take care of Wireless Extensions */ if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { @@ -2814,7 +2848,7 @@ int dev_ioctl(unsigned int cmd, void __user *arg) /* Follow me in net/wireless/wext-old.c */ ret = wireless_process_ioctl(&ifr, cmd); rtnl_unlock(); - if (IW_IS_GET(cmd) && + if (ret == 0 && IW_IS_GET(cmd) && copy_to_user(arg, &ifr, sizeof(struct ifreq))) ret = -EFAULT; diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index f47f319..44e69a2 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -329,7 +329,7 @@ static struct attribute_group netstat_group = { .attrs = netstat_attrs, }; -#ifdef WIRELESS_EXT +#ifdef CONFIG_WIRELESS_EXT /* helper function that does all the locking etc for wireless stats */ static ssize_t wireless_show(struct class_device *cd, char *buf, ssize_t (*format)(const struct iw_statistics *, @@ -462,7 +462,7 @@ int netdev_register_sysfs(struct net_device *net) if (net->get_stats) *groups++ = &netstat_group; -#ifdef WIRELESS_EXT +#ifdef CONFIG_WIRELESS_EXT if (net->wireless_handlers && net->wireless_handlers->get_wireless_stats) *groups++ = &wireless_group; #endif diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index e76539a..45c3d39 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -56,6 +56,9 @@ #include <linux/wireless.h> #include <net/iw_handler.h> #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ +#ifdef CONFIG_CFG80211_WEXTNL_COMPAT +#include <net/cfg80211.h> +#endif static DEFINE_MUTEX(rtnl_mutex); static struct sock *rtnl; @@ -536,6 +539,20 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) modified = 1; } +#ifdef CONFIG_CFG80211_WEXTNL_COMPAT + if (tb[IFLA_WIRELESS]) { + /* Call cfg80211 WE backward compat code. + * Various stuff checked in there... */ + err = cfg80211_wext_nl_set(dev, nla_data(tb[IFLA_WIRELESS]), + nla_len(tb[IFLA_WIRELESS])); + if (err < 0 && err != -ENOSYS) + goto errout_dev; +#ifdef CONFIG_NET_WIRELESS_RTNETLINK + if (err == 0) + goto skip_old_wext_nl; +#endif + } +#endif #ifdef CONFIG_NET_WIRELESS_RTNETLINK if (tb[IFLA_WIRELESS]) { /* Call Wireless Extensions. @@ -545,8 +562,10 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) if (err < 0) goto errout_dev; } +#ifdef CONFIG_CFG80211_WEXTNL_COMPAT + skip_old_wext_nl: +#endif #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ - if (tb[IFLA_BROADCAST]) { nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len); send_addr_notify = 1; @@ -611,6 +630,24 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) return -EINVAL; +#ifdef CONFIG_CFG80211_WEXTNL_COMPAT + if (tb[IFLA_WIRELESS]) { + /* Call Wireless Extensions. We need to know the size before + * we can alloc. Various stuff checked in there... */ + err = cfg80211_wext_nl_get(dev, nla_data(tb[IFLA_WIRELESS]), + nla_len(tb[IFLA_WIRELESS]), + &iw_buf, &iw_buf_len); + if (err < 0 && err != -ENOSYS) + goto errout; + + iw += IW_EV_POINT_OFF; +#ifdef CONFIG_NET_WIRELESS_RTNETLINK + if (err == 0) + goto skip_old_wext_nl; + iw -= IW_EV_POINT_OFF; +#endif + } +#endif #ifdef CONFIG_NET_WIRELESS_RTNETLINK if (tb[IFLA_WIRELESS]) { /* Call Wireless Extensions. We need to know the size before @@ -623,6 +660,9 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) iw += IW_EV_POINT_OFF; } +#ifdef CONFIG_CFG80211_WEXTNL_COMPAT + skip_old_wext_nl: +#endif #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ nskb = nlmsg_new(if_nlmsg_size(iw_buf_len), GFP_KERNEL); diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 663a7d8..62e67b7 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -4,3 +4,13 @@ cfg80211-objs := \ core.o obj-$(CONFIG_WIRELESS_EXT) += wext-old.o + +obj-nn := +obj-yy := +obj-yn := +obj-ny := + +# this needs to be compiled in... +obj-$(CONFIG_CFG80211_WEXT_COMPAT) += wext-compat.o +obj-$(CONFIG_CFG80211_WEXT_COMPAT)$(CONFIG_NET_WIRELESS) += wext-common.o +obj-y += $(obj-yy) $(obj-yn) $(obj-ny) diff --git a/net/wireless/core.c b/net/wireless/core.c index 60b280e..ce50ae4 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -56,7 +56,7 @@ cfg80211_get_drv_from_ifindex(int ifindex) if (drv) mutex_lock(&drv->mtx); else - drv = ERR_PTR(-ENODEV); + drv = ERR_PTR(-ENOSYS); dev_put(dev); out: mutex_unlock(&cfg80211_drv_mutex); @@ -146,12 +146,23 @@ EXPORT_SYMBOL_GPL(cfg80211_unregister); static int cfg80211_init(void) { - /* possibly need to do more later */ +#ifdef CONFIG_CFG80211_WEXT_COMPAT + cfg80211_core_ops.get_drv_from_ifidx = cfg80211_get_drv_from_ifindex; + cfg80211_core_ops.put_drv = cfg80211_put_drv; + cfg80211_core_ops.module = THIS_MODULE; + cfg80211_core_ops.loaded = 1; +#endif return 0; } static void cfg80211_exit(void) { +#ifdef CONFIG_CFG80211_WEXT_COMPAT + cfg80211_core_ops.loaded = 0; + cfg80211_core_ops.module = NULL; + cfg80211_core_ops.get_drv_from_ifidx = NULL; + cfg80211_core_ops.put_drv = NULL; +#endif } module_init(cfg80211_init); diff --git a/net/wireless/core.h b/net/wireless/core.h index 562c476..595f184 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -9,6 +9,7 @@ #include <linux/mutex.h> #include <linux/list.h> #include <net/genetlink.h> +#include <linux/module.h> struct cfg80211_registered_driver { struct cfg80211_ops *ops; @@ -25,6 +26,26 @@ struct cfg80211_registered_driver { extern struct mutex cfg80211_drv_mutex; extern struct list_head cfg80211_drv_list; +#ifdef CONFIG_CFG80211_WEXT_COMPAT +/* wext compatibility must be compiled in... + * this extern is in wext-compat.c */ +struct cfg80211_core_ops { + /* flag to see if cfg80211 is there. + * FIXME: isn't that racy? */ + int loaded; + + /* used to make sure the module isn't going away + * can't really happen, except if no driver has cfg80211 + * in use, but in that case */ + struct module *module; + + /* and finally these are used to do work */ + struct cfg80211_registered_driver *(*get_drv_from_ifidx)(int ifidx); + void (*put_drv)(struct cfg80211_registered_driver *drv); +}; +extern struct cfg80211_core_ops cfg80211_core_ops; +#endif + /* * This function returns a pointer to the driver * that the genl_info item that is passed refers to. diff --git a/net/wireless/wext-common.c b/net/wireless/wext-common.c new file mode 100644 index 0000000..c1de1d7 --- /dev/null +++ b/net/wireless/wext-common.c @@ -0,0 +1,610 @@ +/* common wext support routines, proc interface and events */ + +#include <linux/proc_fs.h> +#include <linux/netdevice.h> +#include <linux/module.h> +#include <linux/wireless.h> +#include <linux/types.h> +#include <net/iw_handler.h> +#include <linux/seq_file.h> +#include <net/netlink.h> +#include <linux/rtnetlink.h> +#include "wext.h" + +/* common data */ +/* + * Meta-data about all the standard Wireless Extension request we + * know about. + */ +const struct iw_ioctl_description standard_ioctl[] = { + [SIOCSIWCOMMIT - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWNAME - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_CHAR, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWNWID - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWNWID - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWFREQ - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_FREQ, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWFREQ - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_FREQ, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWMODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_UINT, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWMODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_UINT, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWSENS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWSENS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWRANGE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWRANGE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_range), + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWPRIV - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_priv_args), + .max_tokens = 16, + .flags = IW_DESCR_FLAG_NOMAX, + }, + [SIOCSIWSTATS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_statistics), + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr), + .max_tokens = IW_MAX_SPY, + }, + [SIOCGIWSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr) + + sizeof(struct iw_quality), + .max_tokens = IW_MAX_SPY, + }, + [SIOCSIWTHRSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_thrspy), + .min_tokens = 1, + .max_tokens = 1, + }, + [SIOCGIWTHRSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_thrspy), + .min_tokens = 1, + .max_tokens = 1, + }, + [SIOCSIWAP - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [SIOCGIWAP - SIOCIWFIRST] = { + .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_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, + .token_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, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWNICKN - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_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, + }, + [SIOCSIWRATE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWRATE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWRTS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWRTS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWFRAG - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWFRAG - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWTXPOW - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWTXPOW - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWRETRY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWRETRY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWENCODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ENCODING_TOKEN_MAX, + .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, + }, + [SIOCGIWENCODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ENCODING_TOKEN_MAX, + .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, + }, + [SIOCSIWPOWER - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [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), + }, +}; +const unsigned standard_ioctl_num = (sizeof(standard_ioctl) / + sizeof(struct iw_ioctl_description)); + +/* + * Meta-data about all the additional standard Wireless Extension events + * we know about. + */ +const struct iw_ioctl_description standard_event[] = { + [IWEVTXDROP - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IWEVQUAL - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_QUAL, + }, + [IWEVCUSTOM - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_CUSTOM_MAX, + }, + [IWEVREGISTERED - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [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), + }, +}; +unsigned standard_event_num = (sizeof(standard_event) / + sizeof(struct iw_ioctl_description)); + +/* Size (in bytes) of various events */ +const int event_type_size[] = { + IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ + 0, + IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ + 0, + IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ + IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ + IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ + 0, + IW_EV_POINT_LEN, /* Without variable payload */ + IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ + IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ +}; + + +struct iw_statistics *get_wireless_stats(struct net_device *dev, + struct iw_statistics *out) +{ +#ifdef CONFIG_CFG80211 + if (dev->ieee80211_ptr && out) { + /* bah, just fake some stuff for now */ + memset(out, 0, sizeof(*out)); + return out; + } +#endif +#ifdef CONFIG_WIRELESS_EXT + if ((dev->wireless_handlers != NULL) && + (dev->wireless_handlers->get_wireless_stats != NULL)) + return dev->wireless_handlers->get_wireless_stats(dev); +#endif + return NULL; +} + +/* + * The /proc/net/wireless file is a human readable user-space interface + * exporting various wireless specific statistics from the wireless devices. + * This is the most popular part of the Wireless Extensions ;-) + * + * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). + * The content of the file is basically the content of "struct iw_statistics". + */ + +#ifdef CONFIG_PROC_FS + +/* + * Print one entry (line) of /proc/net/wireless + */ +static void wireless_seq_printf_stats(struct seq_file *seq, + struct net_device *dev) +{ + /* Get stats from the driver */ + struct iw_statistics stats_buf; + struct iw_statistics *stats = get_wireless_stats(dev, &stats_buf); + + if (stats) { + 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 & 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 &= ~IW_QUAL_ALL_UPDATED; + } +} + +/* + * Print info for /proc/net/wireless (print all entries) + */ +static int wireless_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_printf(seq, "Inter-| sta-| Quality | Discarded " + "packets | Missed | WE\n" + " face | tus | link level noise | nwid " + "crypt frag retry misc | beacon | %d\n", + WIRELESS_EXT); + else + wireless_seq_printf_stats(seq, v); + return 0; +} + +static struct seq_operations wireless_seq_ops = { + .start = dev_seq_start, + .next = dev_seq_next, + .stop = dev_seq_stop, + .show = wireless_seq_show, +}; + +static int wireless_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &wireless_seq_ops); +} + +static struct file_operations wireless_seq_fops = { + .owner = THIS_MODULE, + .open = wireless_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +int __init wireless_proc_init(void) +{ + /* Create /proc/net/wireless entry */ + if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops)) + return -ENOMEM; + + return 0; +} +#endif /* CONFIG_PROC_FS */ + +/* ---------------------------------------------------------------- */ +/* + * Locking... + * ---------- + * + * Thanks to Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx> 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))) + rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); +} + +static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); + +/* ---------------------------------------------------------------- */ +/* + * Fill a rtnetlink message with our event data. + * Note that we propage only the specified event and don't dump the + * current wireless config. Dumping the wireless config is far too + * expensive (for each parameter, the driver need to query the hardware). + */ +static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb, + struct net_device * dev, + int type, + char * event, + int event_len) +{ + struct ifinfomsg *r; + struct nlmsghdr *nlh; + unsigned char *b = skb->tail; + + 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_get_flags(dev); + r->ifi_change = 0; /* Wireless changes don't affect those flags */ + + /* Add the wireless events in the netlink packet */ + RTA_PUT(skb, IFLA_WIRELESS, event_len, event); + + nlh->nlmsg_len = skb->tail - b; + return skb->len; + +nlmsg_failure: +rtattr_failure: + skb_trim(skb, b - skb->data); + return -1; +} + +/* ---------------------------------------------------------------- */ +/* + * Create and broadcast and send it on the standard rtnetlink socket + * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c + * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field + * within a RTM_NEWLINK event. + */ +static inline void rtmsg_iwinfo(struct net_device * dev, + char * event, + int event_len) +{ + struct sk_buff *skb; + int size = NLMSG_GOODSIZE; + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) + return; + + if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, + event, event_len) < 0) { + kfree_skb(skb); + return; + } + NETLINK_CB(skb).dst_group = RTNLGRP_LINK; + skb_queue_tail(&wireless_nlevent_queue, skb); + tasklet_schedule(&wireless_nlevent_tasklet); +} + +/* ---------------------------------------------------------------- */ +/* + * Main event dispatcher. Called from other parts and drivers. + * Send the event on the appropriate channels. + * May be called from interrupt context. + */ +void wireless_send_event(struct net_device * dev, + unsigned int cmd, + union iwreq_data * wrqu, + char * extra) +{ + const struct iw_ioctl_description * descr = NULL; + int extra_len = 0; + 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 Event */ + if(cmd <= SIOCIWLAST) { + cmd_index = cmd - SIOCIWFIRST; + if(cmd_index < standard_ioctl_num) + descr = &(standard_ioctl[cmd_index]); + } else { + cmd_index = cmd - IWEVFIRST; + if(cmd_index < standard_event_num) + descr = &(standard_event[cmd_index]); + } + /* Don't accept unknown events */ + 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 + * initiated by a user request. + * 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", + dev->name, cmd); + return; + } + + /* Check extra parameters and set extra_len */ + 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); + return; + } + 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) + extra_len = wrqu->data.length * descr->token_size; + /* Always at an offset in wrqu */ + wrqu_off = IW_EV_POINT_OFF; + } + + /* Total length of the event */ + hdr_len = event_type_size[descr->header_type]; + event_len = hdr_len + extra_len; + + /* Create temporary buffer to hold the event */ + event = kmalloc(event_len, GFP_ATOMIC); + 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) + memcpy(((char *) event) + hdr_len, extra, extra_len); + + /* Send via the RtNetlink event channel */ + rtmsg_iwinfo(dev, (char *) event, event_len); + + /* Cleanup */ + kfree(event); + + return; /* Always success, I guess ;-) */ +} diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 1c7c361..0cf6ce9 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1,25 +1,1633 @@ -/* NOT YET */ +/* + * wireless extensions compatibility for cfg80211. + * + * Lots of code from the original wireless.c: + * Copyright 1997-2006 Jean Tourrilhes <jt@xxxxxxxxxx> + * + * Copyright 2006 Johannes Berg <johannes@xxxxxxxxxxxxxxxx> + * + * GPLv2. + * + * Theory of operation, so to speak: + * + * To implement compatibility, I added a new field to struct net_device + * that contains the pending configuration structure. This is dynamically + * allocated when needed and freed when committed. + * + * Commit is done some time after the last parameter was changed + * (with each parameter change simply (re-)schedule a timer) or + * if explicitly asked for. This is probably not what most people + * would expect, but perfectly fine in the WE API. + * + * NB: we leak memory if the user + * - changes some settings + * - quickly rmmod's the module so that the net device is destroyed + * Since only root can do it and I don't see a way to hook into + * the net device's destruction... tough. + * + * NB2: Note that each of the wrappers should check if the cfg80211 + * user provides the command, and for configure() it must also check + * if that parameter can be set or not via get_config_valid() + * + * NB3: It's really bad that we can't report an error from the timer- + * based commit... Hopefully get_config_valid() can catch everything? + * + * see set_essid for an example + * + * another question I just thought about.. does wext expect to see + * the new config even if it wasn't committed... if so, we need to + * look at the pending config in various _get_ calls... + */ -To implement compatibility, we add a new field to struct net_device -that contains the pending configuration structure. This is dynamically -allocated when needed and freed when committed. -In a way it replaces the wireless_handlers field in there which is now -done by dynamic lookup. No worries. No one is going to have thousands -of wireless devices, and if that changes we can still trivially change -this assumption :) +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/rtnetlink.h> +#include <linux/if_arp.h> +#include <linux/etherdevice.h> +#include <linux/wireless.h> +#include <net/iw_handler.h> +#include <net/netlink.h> +#include <asm/uaccess.h> +#include <net/cfg80211.h> -Commit is done some time after the last parameter was changed -(with each parameter change simply (re-)schedule a timer) or -if explicitly asked for. This is probably not what most people -would expect, but perfectly fine in the WE API. +#include "core.h" +#include "wext.h" -compatibility mappings: +/* The cfg80211 driver assigns callbacks in this + * if it is loaded. If not, then we can't config + * anything anyway... */ +struct cfg80211_core_ops cfg80211_core_ops; +EXPORT_SYMBOL_GPL(cfg80211_core_ops); -SIOCSIWAP - -> if bssid is all-ones: set roaming to kernel, reassociate - -> if bssid is all-zeroes: set roaming to kernel - -> otherwise: set roaming to userspace, set bssid +static struct cfg80211_registered_driver *cfg80211_wx_setup(int ifindex) +{ + if (!cfg80211_core_ops.loaded) + return ERR_PTR(-ENOSYS); + if (!try_module_get(cfg80211_core_ops.module)) + return ERR_PTR(-ENOSYS); -SIOCGIWAP - -> get association parameters and fill return bssid appropriately + return cfg80211_core_ops.get_drv_from_ifidx(ifindex); +} +static void cfg80211_wx_teardown(struct cfg80211_registered_driver *drv) +{ + if (!IS_ERR(drv)) + cfg80211_core_ops.put_drv(drv); + module_put(cfg80211_core_ops.module); +} + +/* internal API: use this function when changing + * some parameter that needs to be committed */ +static void cfg80211_wx_start_commit_timer(int ifindex) +{ + /* TODO: + * start a timer associate with this interface + * and then when it expires commit the pending + * data... + * This function must be callable when the timer + * is already running, and the timer must + * be able to deal with an unassigned + * dev->cfg80211_wext_pending_config pointer + * as well as taking the rtnl lock (due to wext)! */ +} + +static void cfg80211_ensure_netdev_pending_cfg(struct net_device *dev) +{ + struct cfg80211_config *cfg = dev->cfg80211_wext_pending_config; + if (!cfg) { + cfg = kmalloc(sizeof(*cfg)+32, GFP_KERNEL); + cfg->ssid = (char*)cfg + sizeof(*cfg); + dev->cfg80211_wext_pending_config = cfg; + } +} + +/* operations we implement. whew, I machine-generated these */ +static int cfg80211_wx_set_commit(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + if (!net_dev->cfg80211_wext_pending_config) { + err = 0; + goto out; + } + + err = drv->ops->configure(drv->priv, net_dev, + net_dev->cfg80211_wext_pending_config); + + kfree(net_dev->cfg80211_wext_pending_config); + net_dev->cfg80211_wext_pending_config = NULL; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_name(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_nwid(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_nwid(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_freq(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_freq(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_mode(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_mode(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_sens(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_sens(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_range(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_range(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_ap(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + /* SIOCSIWAP + * -> if bssid is all-ones: set roaming to kernel, reassociate + * -> if bssid is all-zeroes: set roaming to kernel + * -> otherwise: set roaming to userspace, set bssid + */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_ap(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + /* SIOCGIWAP + * -> get association parameters and fill return bssid appropriately + */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_mlme(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_waplist(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_scan(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_scan(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_essid(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + struct cfg80211_config *cfg; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + err = -ENOSYS; + if (!drv->ops->configure || !drv->ops->get_config_valid) + goto out; + if (!(drv->ops->get_config_valid(drv->priv, net_dev) + & CFG80211_CFG_VALID_SSID)) + goto out; + + cfg80211_ensure_netdev_pending_cfg(net_dev); + cfg = net_dev->cfg80211_wext_pending_config; + if (!cfg) { + err = -ENOMEM; + goto out; + } + + memcpy(cfg->ssid, extra, data->essid.length); + cfg->ssid_len = data->essid.length; + cfg->valid |= CFG80211_CFG_VALID_SSID; + + cfg80211_wx_start_commit_timer(net_dev->ifindex); + err = 0; + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_essid(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_rate(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_rate(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_rts(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_rts(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_frag(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_frag(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_txpow(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_txpow(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_retry(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_retry(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_encode(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_encode(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_power(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_power(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_genie(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_genie(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_auth(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_auth(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_encodeext(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_get_encodeext(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + +static int cfg80211_wx_set_wpmksa(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct cfg80211_registered_driver *drv; + int err; + + drv = cfg80211_wx_setup(net_dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out; + } + + /* TODO: DO SOMETHING */ + err = -ENOSYS; + + out: + cfg80211_wx_teardown(drv); + return err; +} + + + +/* operations array */ +#ifdef WX +# undef WX +#endif +#define WX(ioctl) [(ioctl) - SIOCIWFIRST] +static const iw_handler cfg80211_wx_handlers[] = { + WX(SIOCSIWCOMMIT) = cfg80211_wx_set_commit, + WX(SIOCGIWNAME) = cfg80211_wx_get_name, + WX(SIOCSIWNWID) = cfg80211_wx_set_nwid, + WX(SIOCGIWNWID) = cfg80211_wx_get_nwid, + WX(SIOCSIWFREQ) = cfg80211_wx_set_freq, + WX(SIOCGIWFREQ) = cfg80211_wx_get_freq, + WX(SIOCSIWMODE) = cfg80211_wx_set_mode, + WX(SIOCGIWMODE) = cfg80211_wx_get_mode, + WX(SIOCSIWSENS) = cfg80211_wx_set_sens, + WX(SIOCGIWSENS) = cfg80211_wx_get_sens, + WX(SIOCSIWRANGE) = cfg80211_wx_set_range, + WX(SIOCGIWRANGE) = cfg80211_wx_get_range, + WX(SIOCSIWAP) = cfg80211_wx_set_ap, + WX(SIOCGIWAP) = cfg80211_wx_get_ap, + WX(SIOCSIWMLME) = cfg80211_wx_set_mlme, + WX(SIOCGIWAPLIST) = cfg80211_wx_get_waplist, + WX(SIOCSIWSCAN) = cfg80211_wx_set_scan, + WX(SIOCGIWSCAN) = cfg80211_wx_get_scan, + WX(SIOCSIWESSID) = cfg80211_wx_set_essid, + WX(SIOCGIWESSID) = cfg80211_wx_get_essid, + WX(SIOCSIWRATE) = cfg80211_wx_set_rate, + WX(SIOCGIWRATE) = cfg80211_wx_get_rate, + WX(SIOCSIWRTS) = cfg80211_wx_set_rts, + WX(SIOCGIWRTS) = cfg80211_wx_get_rts, + WX(SIOCSIWFRAG) = cfg80211_wx_set_frag, + WX(SIOCGIWFRAG) = cfg80211_wx_get_frag, + WX(SIOCSIWTXPOW) = cfg80211_wx_set_txpow, + WX(SIOCGIWTXPOW) = cfg80211_wx_get_txpow, + WX(SIOCSIWRETRY) = cfg80211_wx_set_retry, + WX(SIOCGIWRETRY) = cfg80211_wx_get_retry, + WX(SIOCSIWENCODE) = cfg80211_wx_set_encode, + WX(SIOCGIWENCODE) = cfg80211_wx_get_encode, + WX(SIOCSIWPOWER) = cfg80211_wx_set_power, + WX(SIOCGIWPOWER) = cfg80211_wx_get_power, + WX(SIOCSIWGENIE) = cfg80211_wx_set_genie, + WX(SIOCGIWGENIE) = cfg80211_wx_get_genie, + WX(SIOCSIWAUTH) = cfg80211_wx_set_auth, + WX(SIOCGIWAUTH) = cfg80211_wx_get_auth, + WX(SIOCSIWENCODEEXT) = cfg80211_wx_set_encodeext, + WX(SIOCGIWENCODEEXT) = cfg80211_wx_get_encodeext, + WX(SIOCSIWPMKSA) = cfg80211_wx_set_wpmksa, +}; + +/* dummy so I didn't have to change that much code... */ +static iw_handler get_handler(struct net_device *dev, unsigned int cmd) +{ + int idx = cmd - SIOCIWFIRST; + if (idx < ARRAY_SIZE(cfg80211_wx_handlers)) + return cfg80211_wx_handlers[idx]; + return NULL; +} + +/* + * this is sort of backwards and wouldn't need to call + * get_wireless_stats, but it was easier to just copy the code... + */ +static int iw_handler_get_iwstats(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + /* Get stats from the driver */ + struct iw_statistics stats_buf; + struct iw_statistics *stats; + + stats = get_wireless_stats(dev, &stats_buf); + if (stats != (struct iw_statistics *) NULL) { + + /* Copy statistics to extra */ + memcpy(extra, stats, sizeof(struct iw_statistics)); + wrqu->data.length = sizeof(struct iw_statistics); + + /* Check if we need to clear the updated flag */ + if(wrqu->data.flags != 0) + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; + return 0; + } else + return -EOPNOTSUPP; +} + +#ifdef CONFIG_CFG80211_WEXTNL_COMPAT +/* + * Wrapper to call a standard Wireless Extension GET handler. + * We do various checks and call the handler with the proper args. + */ +static int rtnetlink_standard_get(struct net_device * dev, + struct iw_event * request, + int request_len, + iw_handler handler, + char ** p_buf, + int * p_len) +{ + const struct iw_ioctl_description * descr = NULL; + unsigned int cmd; + union iwreq_data * wrqu; + int hdr_len; + struct iw_request_info info; + char * buffer = NULL; + int buffer_size = 0; + int ret = -EINVAL; + + /* Get the description of the Request */ + cmd = request->cmd; + 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) + return -EINVAL; + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have extra data in the reply or not */ + 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... */ + buffer_size = request_len + IW_EV_POINT_OFF; + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (buffer == NULL) { + return -ENOMEM; + } + /* Copy event data */ + 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); + + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, wrqu, NULL); + + } else { + union iwreq_data wrqu_point; + char * extra = NULL; + int extra_size = 0; + + /* 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); + + /* 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) && + (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; + + /* Create the kernel buffer that we will return */ + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (buffer == NULL) { + return -ENOMEM; + } + + /* 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... + */ + memcpy(buffer + IW_EV_LCP_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); + + /* Extra comes logically after that. Offset +12 bytes. */ + extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_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; + + /* Put the iwe header where it should, i.e. scrap the + * dummy pointer. */ + memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN); + + /* Check if there is enough buffer up there */ + if(wrqu_point.data.length < wrqu->data.length) + ret = -E2BIG; + } + + /* Return the buffer to the caller */ + if (!ret) { + *p_buf = buffer; + *p_len = request->len; + } else { + /* Cleanup */ + if(buffer) + kfree(buffer); + } + + return ret; +} + +/* + * Wrapper to call a standard Wireless Extension SET handler. + * We do various checks and call the handler with the proper args. + */ +static inline int rtnetlink_standard_set(struct net_device * dev, + struct iw_event * request, + int request_len, + iw_handler handler) +{ + const struct iw_ioctl_description * descr = NULL; + unsigned int cmd; + union iwreq_data * wrqu; + union iwreq_data wrqu_point; + int hdr_len; + char * extra = NULL; + int extra_size = 0; + struct iw_request_info info; + int ret = -EINVAL; + + /* Get the description of the Request */ + cmd = request->cmd; + if((cmd - SIOCIWFIRST) >= standard_ioctl_num) + return -EOPNOTSUPP; + descr = &(standard_ioctl[cmd - SIOCIWFIRST]); + + /* Extract fixed header from request. This is properly aligned. */ + wrqu = &request->u; + + /* Check if wrqu is complete */ + hdr_len = event_type_size[descr->header_type]; + if(request_len < hdr_len) + return -EINVAL; + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have extra data in the request or not */ + if(descr->header_type != IW_HEADER_TYPE_POINT) { + + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, wrqu, NULL); + + } else { + int extra_len; + + /* 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); + /* 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) + return -E2BIG; + 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) + return -EINVAL; + + /* Always allocate for max space. Easier, and won't last + * long... */ + extra_size = descr->max_tokens * descr->token_size; + extra = kmalloc(extra_size, GFP_KERNEL); + if (extra == NULL) + return -ENOMEM; + + /* Copy extra in aligned buffer */ + memcpy(extra, ((char *) request) + hdr_len, extra_len); + + /* Call the handler */ + ret = handler(dev, &info, &wrqu_point, extra); + } + + /* Generate an event to notify listeners of the change */ + if ((descr->flags & IW_DESCR_FLAG_EVENT) && + ((ret == 0) || (ret == -EIWCOMMIT))) { + if(descr->flags & IW_DESCR_FLAG_RESTRICT) + /* If the event is restricted, don't + * export the payload */ + wireless_send_event(dev, cmd, wrqu, NULL); + else + wireless_send_event(dev, cmd, wrqu, extra); + } + + /* Cleanup - I told you it wasn't that long ;-) */ + if(extra) + kfree(extra); + + return ret; +} + +/* + * Main RtNetlink dispatcher. Called from the main networking code + * (do_getlink() in net/core/rtnetlink.c). + * Check the type of Request and call the appropriate wrapper... + */ +int cfg80211_wext_nl_get(struct net_device * dev, + char * data, + int len, + char ** p_buf, + int * p_len) +{ + struct iw_event * request = (struct iw_event *) data; + iw_handler handler; + + /* Check length */ + if(len < IW_EV_LCP_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) { + 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)) + return -EOPNOTSUPP; + + /* If command is `get the encoding parameters', check if + * the user has the right to do it */ + if (request->cmd == SIOCGIWENCODE || + request->cmd == SIOCGIWENCODEEXT) { + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + } + + /* Special cases */ + 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) + return -EOPNOTSUPP; + + /* Basic check */ + if (!netif_device_present(dev)) + return -ENODEV; + + /* Try to find the handler */ + handler = get_handler(dev, request->cmd); + if (handler != NULL && request->cmd < SIOCIWFIRSTPRIV) + return rtnetlink_standard_get(dev, + request, + request->len, + handler, + p_buf, p_len); + + return -EOPNOTSUPP; +} + +/* + * Main RtNetlink dispatcher. Called from the main networking code + * (do_setlink() in net/core/rtnetlink.c). + * Check the type of Request and call the appropriate wrapper... + */ +int cfg80211_wext_nl_set(struct net_device * dev, + char * data, + int len) +{ + struct iw_event * request = (struct iw_event *) data; + iw_handler handler; + + /* Check length */ + if(len < IW_EV_LCP_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) { + 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)) + return -EOPNOTSUPP; + + /* Basic check */ + if (!netif_device_present(dev)) + return -ENODEV; + + /* New driver API : try to find the handler */ + handler = get_handler(dev, request->cmd); + if(handler != NULL && request->cmd < SIOCIWFIRSTPRIV) + return rtnetlink_standard_set(dev, + request, + request->len, + handler); + + return -EOPNOTSUPP; +} +#endif + +/* + * Wrapper to call a standard Wireless Extension handler. + * We do various checks and also take care of moving data between + * user space and kernel space. + */ +static int ioctl_standard_call(struct net_device * dev, + struct ifreq * ifr, + unsigned int cmd, + iw_handler handler) +{ + struct iwreq * iwr = (struct iwreq *) ifr; + const struct iw_ioctl_description * descr; + struct iw_request_info info; + int ret = -EINVAL; + + /* Get the description of the IOCTL */ + if((cmd - SIOCIWFIRST) >= standard_ioctl_num) + return -EOPNOTSUPP; + descr = &(standard_ioctl[cmd - SIOCIWFIRST]); + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not */ + if(descr->header_type != IW_HEADER_TYPE_POINT) { + + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), NULL); + + /* Generate an event to notify listeners of the change */ + if((descr->flags & IW_DESCR_FLAG_EVENT) && + ((ret == 0) || (ret == -EIWCOMMIT))) + wireless_send_event(dev, cmd, &(iwr->u), NULL); + } 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) && + (iwr->u.data.length != 0)) + return -EFAULT; + /* Check if number of token fits within bounds */ + if(iwr->u.data.length > descr->max_tokens) + return -E2BIG; + if(iwr->u.data.length < descr->min_tokens) + return -EINVAL; + } else { + /* Check NULL pointer */ + if(iwr->u.data.pointer == NULL) + return -EFAULT; + /* Save user space buffer size for checking */ + 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... */ + } + } + + /* Create the kernel buffer */ + extra = kmalloc(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)) { + err = copy_from_user(extra, iwr->u.data.pointer, + iwr->u.data.length * + descr->token_size); + if (err) { + kfree(extra); + return -EFAULT; + } + } + + /* Call the handler */ + ret = handler(dev, &info, &(iwr->u), extra); + + /* 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) { + kfree(extra); + return -E2BIG; + } + + err = copy_to_user(iwr->u.data.pointer, extra, + iwr->u.data.length * + descr->token_size); + if (err) + ret = -EFAULT; + } + + /* Generate an event to notify listeners of the change */ + if((descr->flags & IW_DESCR_FLAG_EVENT) && + ((ret == 0) || (ret == -EIWCOMMIT))) { + 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); + else + wireless_send_event(dev, cmd, &(iwr->u), + extra); + } + + /* Cleanup - I told you it wasn't that long ;-) */ + kfree(extra); + } + + return ret; +} + +/* and finally the ioctl wrapper */ +int cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd) +{ + struct net_device *dev; + iw_handler handler; + + /* Permissions are already checked in dev_ioctl() before calling us. + * The copy_to/from_user() of ifr is also dealt with in there */ + + /* Make sure the device exist */ + if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL) + return -ENODEV; + + /* 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 */ + return ioctl_standard_call(dev, + ifr, + cmd, + &iw_handler_get_iwstats); + + case SIOCGIWPRIV: + return -EOPNOTSUPP; + default: + /* Basic check */ + if (!netif_device_present(dev)) + return -ENODEV; + handler = get_handler(dev, cmd); + if(cmd < SIOCIWFIRSTPRIV && handler != NULL) + return ioctl_standard_call(dev, ifr, cmd, + handler); + return -EOPNOTSUPP; + } + return -EINVAL; +} diff --git a/net/wireless/wext-old.c b/net/wireless/wext-old.c index f69ab7b..9892396 100644 --- a/net/wireless/wext-old.c +++ b/net/wireless/wext-old.c @@ -83,9 +83,7 @@ #include <linux/module.h> #include <linux/types.h> /* off_t */ #include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */ -#include <linux/proc_fs.h> #include <linux/rtnetlink.h> /* rtnetlink stuff */ -#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 */ @@ -97,6 +95,8 @@ #include <asm/uaccess.h> /* copy_to_user() */ +#include "wext.h" + /**************************** CONSTANTS ****************************/ /* Debugging stuff */ @@ -111,294 +111,6 @@ #define WE_SET_EVENT /* Generate an event on some set commands */ /************************* GLOBAL VARIABLES *************************/ -/* - * You should not use global variables, because of re-entrancy. - * On our case, it's only const, so it's OK... - */ -/* - * Meta-data about all the standard Wireless Extension request we - * know about. - */ -static const struct iw_ioctl_description standard_ioctl[] = { - [SIOCSIWCOMMIT - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [SIOCGIWNAME - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_CHAR, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWNWID - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - .flags = IW_DESCR_FLAG_EVENT, - }, - [SIOCGIWNWID - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWFREQ - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_FREQ, - .flags = IW_DESCR_FLAG_EVENT, - }, - [SIOCGIWFREQ - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_FREQ, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWMODE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_UINT, - .flags = IW_DESCR_FLAG_EVENT, - }, - [SIOCGIWMODE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_UINT, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWSENS - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWSENS - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWRANGE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [SIOCGIWRANGE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_range), - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWPRIV - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_priv_args), - .max_tokens = 16, - .flags = IW_DESCR_FLAG_NOMAX, - }, - [SIOCSIWSTATS - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_statistics), - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWSPY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr), - .max_tokens = IW_MAX_SPY, - }, - [SIOCGIWSPY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr) + - sizeof(struct iw_quality), - .max_tokens = IW_MAX_SPY, - }, - [SIOCSIWTHRSPY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_thrspy), - .min_tokens = 1, - .max_tokens = 1, - }, - [SIOCGIWTHRSPY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_thrspy), - .min_tokens = 1, - .max_tokens = 1, - }, - [SIOCSIWAP - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [SIOCGIWAP - SIOCIWFIRST] = { - .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_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, - .token_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, - .flags = IW_DESCR_FLAG_DUMP, - }, - [SIOCSIWNICKN - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_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, - }, - [SIOCSIWRATE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWRATE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWRTS - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWRTS - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWFRAG - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWFRAG - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWTXPOW - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWTXPOW - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWRETRY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCGIWRETRY - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [SIOCSIWENCODE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ENCODING_TOKEN_MAX, - .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, - }, - [SIOCGIWENCODE - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ENCODING_TOKEN_MAX, - .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, - }, - [SIOCSIWPOWER - SIOCIWFIRST] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [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 unsigned standard_ioctl_num = (sizeof(standard_ioctl) / - sizeof(struct iw_ioctl_description)); - -/* - * Meta-data about all the additional standard Wireless Extension events - * we know about. - */ -static const struct iw_ioctl_description standard_event[] = { - [IWEVTXDROP - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IWEVQUAL - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_QUAL, - }, - [IWEVCUSTOM - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_CUSTOM_MAX, - }, - [IWEVREGISTERED - IWEVFIRST] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [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 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[] = { @@ -412,21 +124,6 @@ static const char iw_priv_type_size[] = { 0, /* Not defined */ }; -/* Size (in bytes) of various events */ -static const int event_type_size[] = { - IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ - 0, - IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ - 0, - IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ - IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ - IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ - 0, - IW_EV_POINT_LEN, /* Without variable payload */ - IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ - IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ -}; - /************************ COMMON SUBROUTINES ************************/ /* * Stuff that may be used in various place or doesn't fit in one @@ -464,21 +161,6 @@ static inline iw_handler get_handler(struct net_device *dev, /* ---------------------------------------------------------------- */ /* - * Get statistics out of the driver - */ -static inline struct iw_statistics *get_wireless_stats(struct net_device *dev) -{ - /* New location */ - if((dev->wireless_handlers != NULL) && - (dev->wireless_handlers->get_wireless_stats != NULL)) - return dev->wireless_handlers->get_wireless_stats(dev); - - /* Not found */ - return (struct iw_statistics *) NULL; -} - -/* ---------------------------------------------------------------- */ -/* * Call the commit handler in the driver * (if exist and if conditions are right) * @@ -551,7 +233,7 @@ static int iw_handler_get_iwstats(struct net_device * dev, /* Get stats from the driver */ struct iw_statistics *stats; - stats = get_wireless_stats(dev); + stats = get_wireless_stats(dev, NULL); if (stats != (struct iw_statistics *) NULL) { /* Copy statistics to extra */ @@ -601,97 +283,6 @@ static int iw_handler_get_private(struct net_device * dev, return 0; } - -/******************** /proc/net/wireless SUPPORT ********************/ -/* - * The /proc/net/wireless file is a human readable user-space interface - * exporting various wireless specific statistics from the wireless devices. - * This is the most popular part of the Wireless Extensions ;-) - * - * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). - * The content of the file is basically the content of "struct iw_statistics". - */ - -#ifdef CONFIG_PROC_FS - -/* ---------------------------------------------------------------- */ -/* - * Print one entry (line) of /proc/net/wireless - */ -static __inline__ void wireless_seq_printf_stats(struct seq_file *seq, - struct net_device *dev) -{ - /* Get stats from the driver */ - struct iw_statistics *stats = get_wireless_stats(dev); - - if (stats) { - 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 & 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 &= ~IW_QUAL_ALL_UPDATED; - } -} - -/* ---------------------------------------------------------------- */ -/* - * Print info for /proc/net/wireless (print all entries) - */ -static int wireless_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_printf(seq, "Inter-| sta-| Quality | Discarded " - "packets | Missed | WE\n" - " face | tus | link level noise | nwid " - "crypt frag retry misc | beacon | %d\n", - WIRELESS_EXT); - else - wireless_seq_printf_stats(seq, v); - return 0; -} - -static struct seq_operations wireless_seq_ops = { - .start = dev_seq_start, - .next = dev_seq_next, - .stop = dev_seq_stop, - .show = wireless_seq_show, -}; - -static int wireless_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &wireless_seq_ops); -} - -static struct file_operations wireless_seq_fops = { - .owner = THIS_MODULE, - .open = wireless_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -int __init wireless_proc_init(void) -{ - /* Create /proc/net/wireless entry */ - if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops)) - return -ENOMEM; - - return 0; -} -#endif /* CONFIG_PROC_FS */ - /************************** IOCTL SUPPORT **************************/ /* * The original user space API to configure all those Wireless Extensions @@ -1863,220 +1454,6 @@ int wireless_rtnetlink_set(struct net_device * dev, } #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ - -/************************* EVENT PROCESSING *************************/ -/* - * Process events generated by the wireless layer or the driver. - * Most often, the event will be propagated through rtnetlink - */ - -#ifdef WE_EVENT_RTNETLINK -/* ---------------------------------------------------------------- */ -/* - * Locking... - * ---------- - * - * Thanks to Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx> 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))) - rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); -} - -static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); - -/* ---------------------------------------------------------------- */ -/* - * Fill a rtnetlink message with our event data. - * Note that we propage only the specified event and don't dump the - * current wireless config. Dumping the wireless config is far too - * expensive (for each parameter, the driver need to query the hardware). - */ -static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb, - struct net_device * dev, - int type, - char * event, - int event_len) -{ - struct ifinfomsg *r; - struct nlmsghdr *nlh; - unsigned char *b = skb->tail; - - 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_get_flags(dev); - r->ifi_change = 0; /* Wireless changes don't affect those flags */ - - /* Add the wireless events in the netlink packet */ - RTA_PUT(skb, IFLA_WIRELESS, event_len, event); - - nlh->nlmsg_len = skb->tail - b; - return skb->len; - -nlmsg_failure: -rtattr_failure: - skb_trim(skb, b - skb->data); - return -1; -} - -/* ---------------------------------------------------------------- */ -/* - * Create and broadcast and send it on the standard rtnetlink socket - * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c - * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field - * within a RTM_NEWLINK event. - */ -static inline void rtmsg_iwinfo(struct net_device * dev, - char * event, - int event_len) -{ - struct sk_buff *skb; - int size = NLMSG_GOODSIZE; - - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) - return; - - if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, - event, event_len) < 0) { - kfree_skb(skb); - return; - } - NETLINK_CB(skb).dst_group = RTNLGRP_LINK; - skb_queue_tail(&wireless_nlevent_queue, skb); - tasklet_schedule(&wireless_nlevent_tasklet); -} - -#endif /* WE_EVENT_RTNETLINK */ - -/* ---------------------------------------------------------------- */ -/* - * Main event dispatcher. Called from other parts and drivers. - * Send the event on the appropriate channels. - * May be called from interrupt context. - */ -void wireless_send_event(struct net_device * dev, - unsigned int cmd, - union iwreq_data * wrqu, - char * extra) -{ - const struct iw_ioctl_description * descr = NULL; - int extra_len = 0; - 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 Event */ - if(cmd <= SIOCIWLAST) { - cmd_index = cmd - SIOCIWFIRST; - if(cmd_index < standard_ioctl_num) - descr = &(standard_ioctl[cmd_index]); - } else { - cmd_index = cmd - IWEVFIRST; - if(cmd_index < standard_event_num) - descr = &(standard_event[cmd_index]); - } - /* Don't accept unknown events */ - 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 - * initiated by a user request. - * 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", - dev->name, cmd); - return; - } -#ifdef WE_EVENT_DEBUG - printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n", - dev->name, cmd); - printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); -#endif /* WE_EVENT_DEBUG */ - - /* Check extra parameters and set extra_len */ - 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); - return; - } - 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) - 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 */ - } - - /* Total length of the event */ - hdr_len = event_type_size[descr->header_type]; - event_len = hdr_len + extra_len; - -#ifdef WE_EVENT_DEBUG - 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 */ - event = kmalloc(event_len, GFP_ATOMIC); - 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) - memcpy(((char *) event) + hdr_len, extra, extra_len); - -#ifdef WE_EVENT_RTNETLINK - /* Send via the RtNetlink event channel */ - rtmsg_iwinfo(dev, (char *) event, event_len); -#endif /* WE_EVENT_RTNETLINK */ - - /* Cleanup */ - kfree(event); - - return; /* Always success, I guess ;-) */ -} - /********************** ENHANCED IWSPY SUPPORT **********************/ /* * In the old days, the driver was handling spy support all by itself. diff --git a/net/wireless/wext.h b/net/wireless/wext.h new file mode 100644 index 0000000..fcf1c5a --- /dev/null +++ b/net/wireless/wext.h @@ -0,0 +1,13 @@ +/* + * some foo for wext compat/wext interoperability + */ +#ifndef _WEXT_H +#define _WEXT_H +#include <linux/wireless.h> +extern struct iw_statistics *get_wireless_stats(struct net_device *dev, + struct iw_statistics *out); +extern const struct iw_ioctl_description standard_ioctl[]; +extern const unsigned standard_ioctl_num; +extern const struct iw_ioctl_description standard_event[]; +extern const int event_type_size[]; +#endif /* _WEXT_H */ -- 1.4.4.2 -- John W. Linville linville@xxxxxxxxxxxxx - To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html