In London we agreed that it would be good to go ahead and merge
cfg80211. This provides some exposure to the API and eases some of
the pain for driver maintainers (who won't have to follow wireless-dev
to get it).
I have refactored the cfg80211-related patches down to three patches.
This eliminates some of the churn in the series from wireless-dev,
and each patch leaves a buildable tree.
Let me know if I've left anything out, screwed anything up, or if
you no longer think a cfg80211 merge is appropriate at this time.
Thanks,
John
--
John W. Linville
[email protected]
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
From: Johannes Berg <[email protected]>
This patch adds cfg80211, a new configuration system for wireless hardware
as well as nl80211, the netlink-based userspace interface for it.
It currently features a bunch of configuration requests, support for
adding and removing virtual interfaces, the ability to inject packets and
more.
Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
---
include/linux/Kbuild | 1 +
include/linux/netdevice.h | 1 +
include/linux/nl80211.h | 276 ++++++++++++
include/net/cfg80211.h | 193 ++++++++
net/Kconfig | 3 +
net/Makefile | 1 +
net/wireless/Makefile | 4 +
net/wireless/core.c | 235 ++++++++++
net/wireless/core.h | 57 +++
net/wireless/nl80211.c | 1049 ++++++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.h | 7 +
net/wireless/wext-compat.c | 25 +
12 files changed, 1852 insertions(+), 0 deletions(-)
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 157db77..a0657c6 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -115,6 +115,7 @@ header-y += netrom.h
header-y += nfs2.h
header-y += nfs4_mount.h
header-y += nfs_mount.h
+header-y += nl80211.h
header-y += oom.h
header-y += param.h
header-y += pci_regs.h
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index fea0d9d..c1e9962 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -398,6 +398,7 @@ struct net_device
void *ip6_ptr; /* IPv6 specific data */
void *ec_ptr; /* Econet specific data */
void *ax25_ptr; /* AX.25 specific data */
+ void *ieee80211_ptr; /* IEEE 802.11 specific data */
/*
* Cache line mostly used on receive path (including eth_type_trans())
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
new file mode 100644
index 0000000..7bb84bb
--- /dev/null
+++ b/include/linux/nl80211.h
@@ -0,0 +1,276 @@
+#ifndef __LINUX_NL80211_H
+#define __LINUX_NL80211_H
+/*
+ * 802.11 netlink interface public header
+ *
+ * Copyright 2006 Johannes Berg <[email protected]>
+ */
+
+/* currently supported commands
+ * don't change the order or add anything inbetween, this is ABI! */
+enum {
+ /* There's no technical reason to not use command 0 but malformed
+ * zeroed messages may have it and this catches that */
+ NL80211_CMD_UNSPEC,
+
+ /* Get supported commands by ifindex,
+ * uses NL80211_ATTR_CMDS (output) and NL80211_ATTR_IFINDEX (input) */
+ NL80211_CMD_GET_CMDLIST,
+
+ /* Supported commands returned */
+ NL80211_CMD_NEW_CMDLIST,
+
+ /* Inject a frame using NL80211_ATTR_FLAGS and NL80211_ATTR_FRAME.
+ * If kernel sends this, it's a status notification for the injected
+ * frame. */
+ NL80211_CMD_INJECT,
+
+ /* add a virtual interface to a group that is identified by any
+ * other ifindex in the group of a wiphy index, needs the
+ * NL80211_IF_NAME attribute */
+ NL80211_CMD_ADD_VIRTUAL_INTERFACE,
+
+ /* remove a given (with NL80211_ATTR_IFINDEX) virtual device */
+ NL80211_CMD_DEL_VIRTUAL_INTERFACE,
+
+ /* get list of all wiphys */
+ NL80211_CMD_GET_WIPHYS,
+
+ /* get list of all wiphys */
+ NL80211_CMD_NEW_WIPHYS,
+
+ /* get list of all interfaces belonging to a wiphy */
+ NL80211_CMD_GET_INTERFACES,
+
+ /* get list of all interfaces belonging to a wiphy */
+ NL80211_CMD_NEW_INTERFACES,
+
+ /* configure device */
+ NL80211_CMD_CONFIGURE,
+
+ /* request configuration */
+ NL80211_CMD_GET_CONFIG,
+
+ /* configuration sent from kernel */
+ NL80211_CMD_NEW_CONFIG,
+
+ /* initiate scan.
+ * Takes a CHANNEL_LIST attribute containing nested
+ * attributes which in turn contain CHANNEL and FLAGS
+ * attributes.
+ * The top level can also contain a FLAGS attribute
+ * which is then the default for each channel.
+ * If no channel list is given (or it is empty)
+ * all channels shall be scanned. */
+ NL80211_CMD_INITIATE_SCAN,
+
+ /* scan result (kernel -> userspace) */
+ NL80211_CMD_SCAN_RESULT,
+
+ /* change roaming control */
+ NL80211_CMD_SET_ROAMING_CONTROL,
+
+ /* get roaming control setting */
+ NL80211_CMD_GET_ROAMING_CONTROL,
+
+ /* answer to that */
+ NL80211_CMD_ROAMING_CONTROL,
+
+ /* set access point BSSID for userspace roaming */
+ NL80211_CMD_SET_FIXED_BSSID,
+
+ /* get currently set userspace roaming BSSID */
+ NL80211_CMD_GET_FIXED_BSSID,
+
+ /* currently set roaming BSSID */
+ NL80211_CMD_FIXED_BSSID,
+
+ /* get current association information, if not associated then
+ * the BSSID attribute is not present in response */
+ NL80211_CMD_GET_ASSOCIATION,
+
+ /* association notification and response to GET_BSSID */
+ NL80211_CMD_ASSOCIATION_CHANGED,
+
+ /* disassociate from current AP */
+ NL80211_CMD_DISASSOCIATE,
+
+ /* deauth from current AP */
+ NL80211_CMD_DEAUTH,
+
+ /* re-associate with current settings
+ * (SSID and BSSID if roaming control in userspace) */
+ NL80211_CMD_REASSOCIATE,
+
+ /* request the full list of BSSs the device is
+ * authenticated with */
+ NL80211_CMD_GET_AUTH_LIST,
+
+ /* sent as a response to GET_AUTH_LIST containing
+ * an ATTR_BSSID_LIST */
+ NL80211_CMD_AUTH_LIST,
+
+ /* sent when authenticating/deauthenticating.
+ * contains an ATTR_BSSID and possibly an
+ * ATTR_DEAUTHENTICATED */
+ NL80211_CMD_AUTHENTICATION_CHANGED,
+
+ /* add commands here */
+
+ /* used to define NL80211_CMD_MAX below */
+ __NL80211_CMD_AFTER_LAST,
+};
+#define NL80211_CMD_MAX (__NL80211_CMD_AFTER_LAST - 1)
+
+
+/* currently supported attributes.
+ * don't change the order or add anything inbetween, this is ABI! */
+enum {
+ NL80211_ATTR_UNSPEC,
+
+ /* network device (ifindex) to operate on */
+ NL80211_ATTR_IFINDEX,
+
+ /* wiphy index to operate on */
+ NL80211_ATTR_WIPHY,
+
+ /* list of u8 cmds that a given device implements */
+ NL80211_ATTR_CMDS,
+
+ /* flags for injection and other commands, see below */
+ NL80211_ATTR_FLAGS,
+
+ /* which hardware queue to use */
+ NL80211_ATTR_QUEUE,
+
+ /* frame to inject or received frame for mgmt frame subscribers */
+ NL80211_ATTR_FRAME,
+
+ /* interface name */
+ NL80211_ATTR_IFNAME,
+
+ /* type of (virtual) interface */
+ NL80211_ATTR_IFTYPE,
+
+ /* interface list */
+ NL80211_ATTR_INTERFACE_LIST,
+
+ /* wiphy list */
+ NL80211_ATTR_WIPHY_LIST,
+
+ /* attributes used for configuration */
+ /* network ID (pre 802.11 HW) */
+ NL80211_ATTR_NETWORK_ID,
+
+ /* channel, 1-14 are B/G */
+ NL80211_ATTR_CHANNEL,
+
+ /* channel list for scan determination */
+ NL80211_ATTR_CHANNEL_LIST,
+
+ /* receiver sensitivity in dBm */
+ NL80211_ATTR_RX_SENSITIVITY,
+
+ /* BSSID to associate to, only used when roaming control
+ * is in userspace */
+ NL80211_ATTR_BSSID,
+
+ /* list of multiple BSSIDs, this is a nested attribute
+ * containing an index->(attrs) mapping */
+ NL80211_ATTR_BSSID_LIST,
+
+ /* this is a flag for when an authentication is lost */
+ NL80211_ATTR_DEAUTHENTICATED,
+
+ /* SSID of ESS to associate to */
+ NL80211_ATTR_SSID,
+
+ /* transmit power in mW */
+ NL80211_ATTR_TRANSMIT_POWER,
+
+ /* fragmentation threshold in bytes */
+ NL80211_ATTR_FRAG_THRESHOLD,
+
+ /* one or more information elements */
+ NL80211_ATTR_INFORMATION_ELEMENT,
+
+ NL80211_ATTR_ROAMING_CONTROL,
+
+ NL80211_ATTR_SCAN_TYPE,
+
+ /* add attributes here */
+
+ /* used to define NL80211_ATTR_MAX below */
+ __NL80211_ATTR_AFTER_LAST,
+};
+#define NL80211_ATTR_MAX (__NL80211_ATTR_AFTER_LAST - 1)
+
+/**
+ * NL80211_FLAG_TXSTATUS - send transmit status indication
+ */
+#define NL80211_FLAG_TXSTATUS (1<<0)
+/**
+ * NL80211_FLAG_ENCRYPT - encrypt this packet
+ * Warning: This looks inside the packet header!
+ */
+#define NL80211_FLAG_ENCRYPT (1<<1)
+
+/**
+ * NL80211_FLAG_SCAN_TYPE_ACTIVE - set this with a scan
+ * request to have it scan actively, can also be used
+ * within the nested CHANNEL_LIST...
+ */
+#define NL80211_FLAG_SCAN_TYPE_ACTIVE (1<<2)
+
+/**
+ * maximum length of a frame that can be injected
+ */
+#define NL80211_MAX_FRAME_LEN 2500
+
+/* this is an arbitrary limit, 516 means two full-length
+ * IEs would fit... */
+/**
+ * maximum length of IE(s) passed in an NL80211_ATTR_INFORMATION_ELEMENT.
+ */
+#define NL80211_MAX_IE_LEN 516
+
+/* may need to be bumped? */
+/**
+ * maximum number of items in an ATTR_CHANNEL_LIST
+ */
+#define NL80211_MAX_CHANNEL_LIST_ITEM 20
+
+/**
+ * &enum nl80211_iftype - (virtual) interface types
+ *
+ * This structure is used with the NL80211_ATTR_IFTYPE
+ * to set the type of an interface.
+ * Note that these are intentionally compatible with
+ * the IW_MODE_* constants except for the removal of
+ * IW_MODE_AUTO.
+ *
+ */
+enum {
+ NL80211_IFTYPE_UNSPECIFIED,
+ NL80211_IFTYPE_ADHOC,
+ NL80211_IFTYPE_STATION,
+ NL80211_IFTYPE_AP,
+ NL80211_IFTYPE_WDS,
+ NL80211_IFTYPE_SECONDARY,
+ NL80211_IFTYPE_MONITOR,
+
+ /* keep last */
+ __NL80211_IFTYPE_AFTER_LAST
+};
+#define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1)
+
+enum {
+ NL80211_ROAMING_CONTROL_KERNEL,
+ NL80211_ROAMING_CONTROL_USERSPACE,
+
+ /* keep last */
+ __NL80211_ROAMING_CONTROL_AFTER_LAST
+};
+#define NL80211_ROAMING_CONTROL_MAX (__NL80211_ROAMING_CONTROL_AFTER_LAST-1)
+
+#endif /* __LINUX_NL80211_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
new file mode 100644
index 0000000..d83c47f
--- /dev/null
+++ b/include/net/cfg80211.h
@@ -0,0 +1,193 @@
+#ifndef __NET_CFG80211_H
+#define __NET_CFG80211_H
+
+#include <linux/netlink.h>
+#include <linux/nl80211.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+
+/*
+ * 802.11 configuration in-kernel interface
+ *
+ * Copyright 2006 Johannes Berg <[email protected]>
+ */
+
+/**
+ * struct cfg80211_config - description of a configuration (request)
+ */
+struct cfg80211_config {
+ /* first fields with 'internal' validity */
+
+ /* 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;
+ u32 fragmentation_threshold;
+ u32 channel;
+};
+
+struct scan_channel {
+ u32 channel;
+ int active;
+};
+
+struct scan_params {
+ /* number of items in 'channels' array
+ * or -1 to indicate scanning all channels
+ * (in that case 'channels' is NULL) */
+ int n_channels;
+
+ /* use only when n_channels is -1 to determine
+ * whether scanning should be active or not */
+ int active;
+
+ /* the channel list if any */
+ struct scan_channel *channels;
+};
+
+/**
+ * struct cfg80211_ops - backend description for wireless configuration
+ *
+ * This struct is registered by fullmac card drivers and/or wireless stacks
+ * in order to handle configuration requests on their interfaces.
+ *
+ * The priv pointer passed to each call is the pointer that was
+ * registered in cfg80211_register_driver().
+ *
+ * All callbacks except where otherwise noted should return 0
+ * on success or a negative error code.
+ *
+ * @list_interfaces: for each interfaces belonging to the wiphy identified
+ * by the priv pointer, call the one() function with the
+ * given data and the ifindex. This callback is required.
+ *
+ * @inject_packet: inject the given frame with the NL80211_FLAG_*
+ * flags onto the given queue.
+ *
+ * @add_virtual_intf: create a new virtual interface with the given name
+ *
+ * @del_virtual_intf: remove the virtual interface determined by ifindex.
+ *
+ * @configure: configure the given interface as requested in the config struct.
+ * must not ignore any configuration item, if something is
+ * is requested that cannot be fulfilled return an error
+ *
+ * @get_config: fill the given config structure with the current configuration
+ *
+ * @reassociate: reassociate with current settings (SSID, BSSID if
+ * userspace roaming is enabled)
+ *
+ * @disassociate: disassociate from current AP
+ *
+ * @deauth: deauth from current AP
+ *
+ * @initiate_scan: ...
+ *
+ * @set_roaming: set who gets to control roaming, the roaming_control
+ * parameter is passed NL80211_ROAMING_CONTROL_* values.
+ *
+ * @get_roaming: return where roaming control currently is done or
+ * a negative error.
+ *
+ * @set_fixed_bssid: set BSSID to use with userspace roaming, forces
+ * reassociation if changing.
+ * @get_fixed_bssid: get BSSID that is used with userspace roaming,
+ * the bssid parameter has space for 6 bytes
+ *
+ * @get_association: get BSSID of the BSS that the device is currently
+ * associated to and return 1, or return 0 if not
+ * associated (or a negative error code)
+ * @get_auth_list: get list of BSSIDs of all BSSs the device has
+ * authenticated with, must call next_bssid for each,
+ * next_bssid returns non-zero on error, the given data
+ * is to be passed to that callback
+ */
+struct cfg80211_ops {
+ int (*list_interfaces)(void *priv, void *data,
+ int (*one)(void *data, int ifindex));
+
+
+ int (*inject_packet)(void *priv, void *frame, int framelen,
+ u32 flags, int queue);
+
+
+ int (*add_virtual_intf)(void *priv, char *name,
+ unsigned int type);
+ int (*del_virtual_intf)(void *priv, int ifindex);
+
+
+ int (*configure)(void *priv, struct net_device *dev,
+ struct cfg80211_config *cfg);
+ void (*get_config)(void *priv, struct net_device *dev,
+ struct cfg80211_config *cfg);
+
+
+ int (*reassociate)(void *priv, struct net_device *dev);
+ int (*disassociate)(void *priv, struct net_device *dev);
+ int (*deauth)(void *priv, struct net_device *dev);
+
+
+ int (*initiate_scan)(void *priv, struct net_device *dev,
+ struct scan_params *params);
+
+
+ int (*set_roaming)(void *priv, struct net_device *dev,
+ int roaming_control);
+ int (*get_roaming)(void *priv, struct net_device *dev);
+ int (*set_fixed_bssid)(void *priv, struct net_device *dev,
+ u8 *bssid);
+ int (*get_fixed_bssid)(void *priv, struct net_device *dev,
+ u8 *bssid);
+
+
+ int (*get_association)(void *priv, struct net_device *dev,
+ u8 *bssid);
+
+ int (*get_auth_list)(void *priv, struct net_device *dev,
+ void *data,
+ int (*next_bssid)(void *data, u8 *bssid));
+};
+
+/**
+ * cfg80211_register - register a wiphy with cfg80211
+ *
+ * register a given method structure with the cfg80211 system
+ * and associate the 'priv' pointer with it.
+ *
+ * Returns a non-negative wiphy index or a negative error code.
+ *
+ * NOTE: for proper operation, this priv pointer MUST also be
+ * assigned to each &struct net_device's @ieee80211_ptr member!
+ */
+extern int cfg80211_register(struct cfg80211_ops *ops, void *priv);
+
+/**
+ * cfg80211_unregister - deregister a wiphy from cfg80211
+ *
+ * unregister a device with the given priv pointer.
+ * After this call, no more requests can be made with this priv
+ * pointer, but the call may sleep to wait for an outstanding
+ * request that is being handled.
+ */
+extern void cfg80211_unregister(void *priv);
+
+/* helper functions specific to nl80211 */
+extern void *nl80211hdr_put(struct sk_buff *skb, u32 pid,
+ u32 seq, int flags, u8 cmd);
+extern void *nl80211msg_new(struct sk_buff **skb, u32 pid,
+ u32 seq, int flags, u8 cmd);
+
+#endif /* __NET_CFG80211_H */
diff --git a/net/Kconfig b/net/Kconfig
index 7dfc949..8d121a5 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -226,6 +226,9 @@ config WIRELESS_EXT
config FIB_RULES
bool
+config CFG80211
+ tristate "Improved wireless configuration API"
+
endif # if NET
endmenu # Networking
diff --git a/net/Makefile b/net/Makefile
index ad4d14f..278d6bf 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_ECONET) += econet/
obj-$(CONFIG_VLAN_8021Q) += 8021q/
obj-$(CONFIG_IP_DCCP) += dccp/
obj-$(CONFIG_IP_SCTP) += sctp/
+obj-$(CONFIG_CFG80211) += wireless/
obj-$(CONFIG_IEEE80211) += ieee80211/
obj-$(CONFIG_TIPC) += tipc/
obj-$(CONFIG_NETLABEL) += netlabel/
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
new file mode 100644
index 0000000..c030b12
--- /dev/null
+++ b/net/wireless/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_CFG80211) += cfg80211.o
+
+cfg80211-objs := \
+ core.o nl80211.o
diff --git a/net/wireless/core.c b/net/wireless/core.c
new file mode 100644
index 0000000..4a10ec2
--- /dev/null
+++ b/net/wireless/core.c
@@ -0,0 +1,235 @@
+/*
+ * This is the new wireless configuration interface.
+ *
+ * Copyright 2006 Johannes Berg <[email protected]>
+ */
+
+#include "core.h"
+#include "nl80211.h"
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+MODULE_AUTHOR("Johannes Berg");
+MODULE_LICENSE("GPL");
+
+/* RCU might be appropriate here since we usually
+ * only read the list, and that can happen quite
+ * often because we need to do it for each command */
+LIST_HEAD(cfg80211_drv_list);
+DEFINE_MUTEX(cfg80211_drv_mutex);
+static int wiphy_counter;
+
+/* requires nl80211_drv_mutex to be held! */
+static struct cfg80211_registered_driver *cfg80211_drv_by_priv(void *priv)
+{
+ struct cfg80211_registered_driver *result = NULL, *drv;
+
+ if (!priv)
+ return NULL;
+
+ list_for_each_entry(drv, &cfg80211_drv_list, list) {
+ if (drv->priv == priv) {
+ result = drv;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/* requires cfg80211_drv_mutex to be held! */
+static struct cfg80211_registered_driver *cfg80211_drv_by_wiphy(int wiphy)
+{
+ struct cfg80211_registered_driver *result = NULL, *drv;
+
+ list_for_each_entry(drv, &cfg80211_drv_list, list) {
+ if (drv->wiphy == wiphy) {
+ result = drv;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/* requires cfg80211_drv_mutex to be held! */
+static struct cfg80211_registered_driver *
+__cfg80211_drv_from_info(struct genl_info *info)
+{
+ int ifindex;
+ struct cfg80211_registered_driver *bywiphy = NULL, *byifidx = NULL;
+ struct net_device *dev;
+ int err = -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_WIPHY]) {
+ bywiphy = cfg80211_drv_by_wiphy(
+ nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
+ err = -ENODEV;
+ }
+
+ if (info->attrs[NL80211_ATTR_IFINDEX]) {
+ ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
+ dev = dev_get_by_index(ifindex);
+ if (dev) {
+ byifidx = cfg80211_drv_by_priv(dev->ieee80211_ptr);
+ dev_put(dev);
+ }
+ err = -ENODEV;
+ }
+
+ if (bywiphy && byifidx) {
+ if (bywiphy != byifidx)
+ return ERR_PTR(-EINVAL);
+ else
+ return bywiphy; /* == byifidx */
+ }
+ if (bywiphy)
+ return bywiphy;
+
+ if (byifidx)
+ return byifidx;
+
+ return ERR_PTR(err);
+}
+
+struct cfg80211_registered_driver *
+cfg80211_get_drv_from_info(struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+
+ mutex_lock(&cfg80211_drv_mutex);
+ drv = __cfg80211_drv_from_info(info);
+
+ /* if it is not an error we grab the lock on
+ * it to assure it won't be going away while
+ * we operate on it */
+ if (!IS_ERR(drv))
+ mutex_lock(&drv->mtx);
+
+ mutex_unlock(&cfg80211_drv_mutex);
+
+ return drv;
+}
+
+/* wext will need this */
+struct cfg80211_registered_driver *
+cfg80211_get_drv_from_ifindex(int ifindex)
+{
+ struct cfg80211_registered_driver *drv = ERR_PTR(-ENODEV);
+ struct net_device *dev;
+
+ mutex_lock(&cfg80211_drv_mutex);
+ dev = dev_get_by_index(ifindex);
+ if (!dev)
+ goto out;
+ drv = cfg80211_drv_by_priv(dev->ieee80211_ptr);
+ if (drv)
+ mutex_lock(&drv->mtx);
+ else
+ drv = ERR_PTR(-ENODEV);
+ dev_put(dev);
+ out:
+ mutex_unlock(&cfg80211_drv_mutex);
+ return drv;
+}
+
+void cfg80211_put_drv(struct cfg80211_registered_driver *drv)
+{
+ BUG_ON(IS_ERR(drv));
+ mutex_unlock(&drv->mtx);
+}
+
+/* exported functions */
+
+int cfg80211_register(struct cfg80211_ops *ops, void *priv)
+{
+ struct cfg80211_registered_driver *drv;
+ int res;
+
+ if (!priv || !ops->list_interfaces)
+ return -EINVAL;
+
+ mutex_lock(&cfg80211_drv_mutex);
+
+ if (cfg80211_drv_by_priv(priv)) {
+ res = -EALREADY;
+ goto out_unlock;
+ }
+
+ drv = kzalloc(sizeof(struct cfg80211_registered_driver), GFP_KERNEL);
+ if (!drv) {
+ res = -ENOMEM;
+ goto out_unlock;
+ }
+
+ drv->ops = ops;
+ drv->priv = priv;
+
+ if (unlikely(wiphy_counter<0)) {
+ /* ugh, wrapped! */
+ kfree(drv);
+ res = -ENOSPC;
+ goto out_unlock;
+ }
+ mutex_init(&drv->mtx);
+ drv->wiphy = wiphy_counter;
+ list_add(&drv->list, &cfg80211_drv_list);
+ /* return wiphy number */
+ res = drv->wiphy;
+
+ /* now increase counter for the next time */
+ wiphy_counter++;
+
+ out_unlock:
+ mutex_unlock(&cfg80211_drv_mutex);
+ return res;
+}
+EXPORT_SYMBOL_GPL(cfg80211_register);
+
+void cfg80211_unregister(void *priv)
+{
+ struct cfg80211_registered_driver *drv;
+
+ mutex_lock(&cfg80211_drv_mutex);
+ drv = cfg80211_drv_by_priv(priv);
+ if (!drv) {
+ printk(KERN_ERR "deregistering cfg80211 backend that "
+ " was never registered!\n");
+ mutex_unlock(&cfg80211_drv_mutex);
+ return;
+ }
+
+ /* hold registered driver mutex during list removal as well
+ * to make sure no commands are in progress at the moment */
+ mutex_lock(&drv->mtx);
+ list_del(&drv->list);
+ mutex_unlock(&drv->mtx);
+
+ mutex_unlock(&cfg80211_drv_mutex);
+
+ mutex_destroy(&drv->mtx);
+ kfree(drv);
+}
+EXPORT_SYMBOL_GPL(cfg80211_unregister);
+
+/* module initialisation/exit functions */
+
+static int cfg80211_init(void)
+{
+ /* possibly need to do more later */
+ return nl80211_init();
+}
+
+static void cfg80211_exit(void)
+{
+ /* possibly need to do more later */
+ nl80211_exit();
+}
+
+module_init(cfg80211_init);
+module_exit(cfg80211_exit);
diff --git a/net/wireless/core.h b/net/wireless/core.h
new file mode 100644
index 0000000..562c476
--- /dev/null
+++ b/net/wireless/core.h
@@ -0,0 +1,57 @@
+/*
+ * Wireless configuration interface internals.
+ *
+ * Copyright 2006 Johannes Berg <[email protected]>
+ */
+#ifndef __NET_WIRELESS_CORE_H
+#define __NET_WIRELESS_CORE_H
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <net/genetlink.h>
+
+struct cfg80211_registered_driver {
+ struct cfg80211_ops *ops;
+ int wiphy;
+ void *priv;
+ struct list_head list;
+ /* we hold this mutex during any call so that
+ * we cannot do multiple calls at once, and also
+ * to avoid the deregister call to proceed while
+ * any call is in progress */
+ struct mutex mtx;
+};
+
+extern struct mutex cfg80211_drv_mutex;
+extern struct list_head cfg80211_drv_list;
+
+/*
+ * This function returns a pointer to the driver
+ * that the genl_info item that is passed refers to.
+ * If successful, it returns non-NULL and also locks
+ * the driver's mutex!
+ *
+ * This means that you need to call cfg80211_put_drv()
+ * before being allowed to acquire &cfg80211_drv_mutex!
+ *
+ * This is necessary because we need to lock the global
+ * mutex to get an item off the list safely, and then
+ * we lock the drv mutex so it doesn't go away under us.
+ *
+ * We don't want to keep cfg80211_drv_mutex locked
+ * for all the time in order to allow requests on
+ * other interfaces to go through at the same time.
+ *
+ * The result of this can be a PTR_ERR and hence must
+ * be checked with IS_ERR() for errors.
+ */
+extern struct cfg80211_registered_driver *
+cfg80211_get_drv_from_info(struct genl_info *info);
+
+/* identical to cfg80211_get_drv_from_info but only operate on ifindex */
+extern struct cfg80211_registered_driver *
+cfg80211_get_drv_from_ifindex(int ifindex);
+
+extern void cfg80211_put_drv(struct cfg80211_registered_driver *drv);
+
+#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
new file mode 100644
index 0000000..7a1a888
--- /dev/null
+++ b/net/wireless/nl80211.c
@@ -0,0 +1,1049 @@
+/*
+ * This is the new netlink-based wireless configuration interface.
+ *
+ * Copyright 2006 Johannes Berg <[email protected]>
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include "core.h"
+#include "nl80211.h"
+
+/* the netlink family */
+static struct genl_family nl80211_fam = {
+ .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
+ .name = "nl80211", /* have users key off the name instead */
+ .hdrsize = 0, /* no private header */
+ .version = 1, /* no particular meaning now */
+ .maxattr = NL80211_ATTR_MAX,
+};
+
+/* internal helper: validate an information element attribute */
+static int check_information_element(struct nlattr *nla)
+{
+ int len = nla_len(nla);
+ u8 *data = nla_data(nla);
+ int elementlen;
+
+ while (len >= 2) {
+ /* 1 byte ID, 1 byte len, `len' bytes data */
+ elementlen = *(data+1) + 2;
+ data += elementlen;
+ len -= elementlen;
+ }
+ return len ? -EINVAL : 0;
+}
+
+/* internal helper: get drv and dev */
+static int get_drv_dev_by_info_ifindex(struct genl_info *info,
+ struct cfg80211_registered_driver **drv,
+ struct net_device **dev)
+{
+ int ifindex;
+
+ if (!info->attrs[NL80211_ATTR_IFINDEX])
+ return -EINVAL;
+
+ ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
+ *dev = dev_get_by_index(ifindex);
+ if (!dev)
+ return -ENODEV;
+
+ *drv = cfg80211_get_drv_from_ifindex(ifindex);
+ if (IS_ERR(*drv)) {
+ dev_put(*dev);
+ return PTR_ERR(*drv);
+ }
+
+ return 0;
+}
+
+/* policy for the attributes */
+static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
+ [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
+ [NL80211_ATTR_FLAGS] = { .type = NLA_U32 },
+ [NL80211_ATTR_QUEUE] = { .type = NLA_U32 },
+ [NL80211_ATTR_FRAME] = { .type = NLA_STRING,
+ .len = NL80211_MAX_FRAME_LEN },
+ [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
+ [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_NETWORK_ID] = { .type = NLA_U16 },
+ [NL80211_ATTR_CHANNEL] = { .type = NLA_U32 },
+ [NL80211_ATTR_RX_SENSITIVITY] = { .type = NLA_U32 },
+ [NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
+ [NL80211_ATTR_SSID] = { .type = NLA_STRING, .len = 32 },
+ [NL80211_ATTR_TRANSMIT_POWER] = { .type = NLA_U32 },
+ [NL80211_ATTR_FRAG_THRESHOLD] = { .type = NLA_U32 },
+ [NL80211_ATTR_INFORMATION_ELEMENT] = { .type = NLA_STRING,
+ .len = NL80211_MAX_IE_LEN },
+ [NL80211_ATTR_ROAMING_CONTROL] = { .type = NLA_U32 },
+ [NL80211_ATTR_SCAN_TYPE] = { .type = NLA_U32 },
+};
+
+/* netlink command implementations */
+
+#define CHECK_CMD(ptr, cmd) \
+ if (drv->ops->ptr) \
+ NLA_PUT_FLAG(msg, NL80211_CMD_##cmd);
+
+static int nl80211_get_cmdlist(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ struct sk_buff *msg;
+ void *hdr;
+ int err;
+ struct nlattr *start;
+
+ drv = cfg80211_get_drv_from_info(info);
+ if (IS_ERR(drv))
+ return PTR_ERR(drv);
+
+ hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_NEW_CMDLIST);
+ if (IS_ERR(hdr)) {
+ err = PTR_ERR(hdr);
+ goto put_drv;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
+
+ start = nla_nest_start(msg, NL80211_ATTR_CMDS);
+ if (!start)
+ goto nla_put_failure;
+
+ /* unconditionally allow some common commands we handle centrally
+ * or where we require the implementation */
+ NLA_PUT_FLAG(msg, NL80211_CMD_GET_CMDLIST);
+ NLA_PUT_FLAG(msg, NL80211_CMD_GET_WIPHYS);
+ NLA_PUT_FLAG(msg, NL80211_CMD_GET_INTERFACES);
+
+ CHECK_CMD(inject_packet, INJECT);
+ CHECK_CMD(add_virtual_intf, ADD_VIRTUAL_INTERFACE);
+ CHECK_CMD(del_virtual_intf, DEL_VIRTUAL_INTERFACE);
+ CHECK_CMD(configure, CONFIGURE);
+ CHECK_CMD(get_config, GET_CONFIG);
+ CHECK_CMD(reassociate, REASSOCIATE);
+ CHECK_CMD(disassociate, DISASSOCIATE);
+ CHECK_CMD(deauth, DEAUTH);
+ CHECK_CMD(initiate_scan, INITIATE_SCAN);
+ CHECK_CMD(set_roaming, SET_ROAMING_CONTROL);
+ CHECK_CMD(get_roaming, GET_ROAMING_CONTROL);
+ CHECK_CMD(set_fixed_bssid, SET_FIXED_BSSID);
+ CHECK_CMD(get_fixed_bssid, GET_FIXED_BSSID);
+ CHECK_CMD(get_association, GET_ASSOCIATION);
+ CHECK_CMD(get_auth_list, GET_AUTH_LIST);
+
+ nla_nest_end(msg, start);
+
+ genlmsg_end(msg, hdr);
+
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto put_drv;
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ nlmsg_free(msg);
+ put_drv:
+ cfg80211_put_drv(drv);
+ return err;
+}
+#undef CHECK_CMD
+
+static int nl80211_get_wiphys(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ struct nlattr *start, *indexstart;
+ struct cfg80211_registered_driver *drv;
+ int idx = 1;
+
+ hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_NEW_WIPHYS);
+ if (IS_ERR(hdr))
+ return PTR_ERR(hdr);
+
+ start = nla_nest_start(msg, NL80211_ATTR_WIPHY_LIST);
+ if (!start)
+ goto nla_outer_nest_failure;
+
+ mutex_lock(&cfg80211_drv_mutex);
+ list_for_each_entry(drv, &cfg80211_drv_list, list) {
+ indexstart = nla_nest_start(msg, idx++);
+ if (!indexstart)
+ goto nla_put_failure;
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
+ nla_nest_end(msg, indexstart);
+ }
+ mutex_unlock(&cfg80211_drv_mutex);
+
+ nla_nest_end(msg, start);
+
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_unicast(msg, info->snd_pid);
+
+ nla_put_failure:
+ mutex_unlock(&cfg80211_drv_mutex);
+ nla_outer_nest_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+struct add_cb_data {
+ int idx;
+ struct sk_buff *skb;
+};
+
+static int addifidx(void *data, int ifidx)
+{
+ struct add_cb_data *cb = data;
+ struct net_device *dev = dev_get_by_index(ifidx);
+ int err = -ENOBUFS;
+ struct nlattr *start;
+
+ /* not that this can happen, since the caller
+ * should hold the device open... */
+ if (!dev)
+ return -ENODEV;
+
+ start = nla_nest_start(cb->skb, cb->idx++);
+ if (!start)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(cb->skb, NL80211_ATTR_IFINDEX, ifidx);
+ NLA_PUT_STRING(cb->skb, NL80211_ATTR_IFNAME, dev->name);
+
+ nla_nest_end(cb->skb, start);
+ err = 0;
+
+ nla_put_failure:
+ dev_put(dev);
+ return err;
+}
+
+static int nl80211_get_intfs(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ struct sk_buff *msg;
+ void *hdr;
+ int err;
+ struct nlattr *start;
+ struct add_cb_data cb;
+
+ drv = cfg80211_get_drv_from_info(info);
+ if (IS_ERR(drv))
+ return PTR_ERR(drv);
+
+ hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_NEW_INTERFACES);
+ if (IS_ERR(hdr)) {
+ err = PTR_ERR(hdr);
+ goto put_drv;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
+
+ start = nla_nest_start(msg, NL80211_ATTR_INTERFACE_LIST);
+ if (!start) {
+ err = -ENOBUFS;
+ goto msg_free;
+ }
+
+ cb.skb = msg;
+ cb.idx = 1;
+ err = drv->ops->list_interfaces(drv->priv, &cb, addifidx);
+ if (err)
+ goto msg_free;
+
+ nla_nest_end(msg, start);
+
+ genlmsg_end(msg, hdr);
+
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto put_drv;
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ msg_free:
+ nlmsg_free(msg);
+ put_drv:
+ cfg80211_put_drv(drv);
+ return err;
+}
+
+static int nl80211_do_inject(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ u32 flags = 0;
+ int err, queue = -1;
+
+ if (!info->attrs[NL80211_ATTR_FRAME])
+ return -EINVAL;
+ if (info->attrs[NL80211_ATTR_FLAGS])
+ flags = nla_get_u32(info->attrs[NL80211_ATTR_FLAGS]);
+ if (info->attrs[NL80211_ATTR_QUEUE])
+ queue = (int) nla_get_u32(info->attrs[NL80211_ATTR_QUEUE]);
+
+ drv = cfg80211_get_drv_from_info(info);
+ if (IS_ERR(drv))
+ return PTR_ERR(drv);
+
+ if (!drv->ops->inject_packet) {
+ err = -ENOSYS;
+ goto unlock;
+ }
+
+ err = drv->ops->inject_packet(drv->priv,
+ nla_data(info->attrs[NL80211_ATTR_FRAME]),
+ nla_len(info->attrs[NL80211_ATTR_FRAME]),
+ flags,
+ queue);
+ unlock:
+ cfg80211_put_drv(drv);
+ return err;
+}
+
+static int nl80211_add_virt_intf(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int err;
+ unsigned int type = NL80211_IFTYPE_UNSPECIFIED;
+
+ if (!info->attrs[NL80211_ATTR_IFNAME])
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_IFTYPE]) {
+ type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+ if (type > NL80211_IFTYPE_MAX)
+ return -EINVAL;
+ }
+
+ drv = cfg80211_get_drv_from_info(info);
+ if (IS_ERR(drv))
+ return PTR_ERR(drv);
+
+ if (!drv->ops->add_virtual_intf) {
+ err = -ENOSYS;
+ goto unlock;
+ }
+
+ err = drv->ops->add_virtual_intf(drv->priv,
+ nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
+
+ unlock:
+ cfg80211_put_drv(drv);
+ return err;
+}
+
+static int nl80211_del_virt_intf(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int ifindex, err;
+ struct net_device *dev;
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+ ifindex = dev->ifindex;
+ dev_put(dev);
+
+ if (!drv->ops->del_virtual_intf) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = drv->ops->del_virtual_intf(drv->priv, ifindex);
+
+ out:
+ cfg80211_put_drv(drv);
+ return err;
+}
+
+static int nl80211_configure(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int err;
+ struct net_device *dev;
+ struct cfg80211_config config;
+ struct nlattr *attr;
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->configure) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ memset(&config, 0, sizeof(config));
+
+ attr = info->attrs[NL80211_ATTR_SSID];
+ if (attr) {
+ config.ssid = nla_data(attr);
+ config.ssid_len = nla_len(attr);
+ }
+
+ attr = info->attrs[NL80211_ATTR_NETWORK_ID];
+ if (attr) {
+ config.valid |= CFG80211_CFG_VALID_NWID;
+ config.network_id = nla_get_u16(attr);
+ }
+
+ attr = info->attrs[NL80211_ATTR_RX_SENSITIVITY];
+ if (attr) {
+ config.valid |= CFG80211_CFG_VALID_RX_SENSITIVITY;
+ config.rx_sensitivity = (s32) nla_get_u32(attr);
+ }
+
+ attr = info->attrs[NL80211_ATTR_TRANSMIT_POWER];
+ if (attr) {
+ config.valid |= CFG80211_CFG_VALID_TRANSMIT_POWER;
+ config.transmit_power = nla_get_u32(attr);
+ }
+
+ attr = info->attrs[NL80211_ATTR_FRAG_THRESHOLD];
+ if (attr) {
+ config.valid |= CFG80211_CFG_VALID_FRAG_THRESHOLD;
+ config.fragmentation_threshold = nla_get_u32(attr);
+ }
+
+ attr = info->attrs[NL80211_ATTR_CHANNEL];
+ if (attr) {
+ config.valid |= CFG80211_CFG_VALID_CHANNEL;
+ config.channel = nla_get_u32(attr);
+ }
+
+ err = drv->ops->configure(drv->priv, dev, &config);
+ out:
+ cfg80211_put_drv(drv);
+ dev_put(dev);
+ return err;
+}
+
+static int nl80211_get_config(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int err;
+ struct net_device *dev;
+ struct cfg80211_config config;
+ struct sk_buff *msg;
+ void *hdr;
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->get_config) {
+ err = -EOPNOTSUPP;
+ goto out_put_drv;
+ }
+
+ memset(&config, 0, sizeof(config));
+
+ drv->ops->get_config(drv->priv, dev, &config);
+
+ hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_NEW_CONFIG);
+
+ if (IS_ERR(hdr)) {
+ err = PTR_ERR(hdr);
+ goto out_put_drv;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+
+ if (config.ssid)
+ NLA_PUT_STRING(msg, NL80211_ATTR_SSID, config.ssid);
+
+ if (config.valid & CFG80211_CFG_VALID_NWID)
+ NLA_PUT_U16(msg, NL80211_ATTR_NETWORK_ID, config.network_id);
+
+ if (config.valid & CFG80211_CFG_VALID_RX_SENSITIVITY)
+ NLA_PUT_U32(msg, NL80211_ATTR_RX_SENSITIVITY, (u32)config.rx_sensitivity);
+
+ if (config.valid & CFG80211_CFG_VALID_TRANSMIT_POWER)
+ NLA_PUT_U32(msg, NL80211_ATTR_TRANSMIT_POWER, config.transmit_power);
+
+ if (config.valid & CFG80211_CFG_VALID_FRAG_THRESHOLD)
+ NLA_PUT_U32(msg, NL80211_ATTR_FRAG_THRESHOLD, config.fragmentation_threshold);
+
+ if (config.valid & CFG80211_CFG_VALID_CHANNEL)
+ NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL, config.channel);
+
+ genlmsg_end(msg, hdr);
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto out_put_drv;
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ nlmsg_free(msg);
+ out_put_drv:
+ cfg80211_put_drv(drv);
+ dev_put(dev);
+ return err;
+}
+
+static int nl80211_set_roaming(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int err;
+ struct net_device *dev;
+ int roaming_control;
+
+ if (!info->attrs[NL80211_ATTR_ROAMING_CONTROL])
+ return -EINVAL;
+ roaming_control = nla_get_u32(info->attrs[NL80211_ATTR_ROAMING_CONTROL]);
+
+ if (roaming_control > NL80211_ROAMING_CONTROL_MAX)
+ return -EINVAL;
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->set_roaming) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = drv->ops->set_roaming(drv->priv, dev, roaming_control);
+ out:
+ cfg80211_put_drv(drv);
+ dev_put(dev);
+ return err;
+}
+
+static int nl80211_get_roaming(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int err;
+ struct net_device *dev;
+ struct sk_buff *msg;
+ void *hdr;
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->get_roaming) {
+ err = -EOPNOTSUPP;
+ goto out_put_drv;
+ }
+
+ err = drv->ops->get_roaming(drv->priv, dev);
+ if (err < 0)
+ goto out_put_drv;
+
+ hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_ROAMING_CONTROL);
+
+ if (IS_ERR(hdr)) {
+ err = PTR_ERR(hdr);
+ goto out_put_drv;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_ROAMING_CONTROL, err);
+
+ genlmsg_end(msg, hdr);
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto out_put_drv;
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ nlmsg_free(msg);
+ out_put_drv:
+ cfg80211_put_drv(drv);
+ dev_put(dev);
+ return err;
+}
+
+static int nl80211_set_fixed_bssid(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int err;
+ struct net_device *dev;
+ u8 *bssid;
+
+ if (!info->attrs[NL80211_ATTR_BSSID])
+ return -EINVAL;
+ bssid = nla_data(info->attrs[NL80211_ATTR_BSSID]);
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->set_fixed_bssid) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = drv->ops->set_fixed_bssid(drv->priv, dev, bssid);
+ out:
+ cfg80211_put_drv(drv);
+ dev_put(dev);
+ return err;
+}
+
+static int nl80211_get_fixed_bssid(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int err;
+ struct net_device *dev;
+ struct sk_buff *msg;
+ void *hdr;
+ u8 bssid[ETH_ALEN];
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->get_fixed_bssid) {
+ err = -EOPNOTSUPP;
+ goto out_put_drv;
+ }
+
+ err = drv->ops->get_fixed_bssid(drv->priv, dev, bssid);
+ if (err < 0)
+ goto out_put_drv;
+
+ hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_FIXED_BSSID);
+
+ if (IS_ERR(hdr)) {
+ err = PTR_ERR(hdr);
+ goto out_put_drv;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
+
+ genlmsg_end(msg, hdr);
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto out_put_drv;
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ nlmsg_free(msg);
+ out_put_drv:
+ cfg80211_put_drv(drv);
+ dev_put(dev);
+ return err;
+}
+
+static int nl80211_get_association(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int err;
+ struct net_device *dev;
+ struct sk_buff *msg;
+ void *hdr;
+ u8 bssid[ETH_ALEN];
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->get_association) {
+ err = -EOPNOTSUPP;
+ goto out_put_drv;
+ }
+
+ err = drv->ops->get_association(drv->priv, dev, bssid);
+ if (err < 0)
+ goto out_put_drv;
+
+ hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_ASSOCIATION_CHANGED);
+
+ if (IS_ERR(hdr)) {
+ err = PTR_ERR(hdr);
+ goto out_put_drv;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ if (err == 1)
+ NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
+
+ genlmsg_end(msg, hdr);
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto out_put_drv;
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ nlmsg_free(msg);
+ out_put_drv:
+ cfg80211_put_drv(drv);
+ dev_put(dev);
+ return err;
+}
+
+static int nl80211_assoc_deauth(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int err;
+ struct net_device *dev;
+ int (*act)(void *priv, struct net_device *dev);
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+
+ switch (info->genlhdr->cmd) {
+ case NL80211_CMD_DISASSOCIATE:
+ act = drv->ops->disassociate;
+ break;
+ case NL80211_CMD_REASSOCIATE:
+ act = drv->ops->reassociate;
+ break;
+ case NL80211_CMD_DEAUTH:
+ act = drv->ops->deauth;
+ break;
+ default:
+ act = NULL;
+ }
+
+ if (!act) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = act(drv->priv, dev);
+ out:
+ cfg80211_put_drv(drv);
+ dev_put(dev);
+ return err;
+}
+
+static int add_bssid(void *data, u8 *bssid)
+{
+ struct add_cb_data *cb = data;
+ int err = -ENOBUFS;
+ struct nlattr *start;
+
+ start = nla_nest_start(cb->skb, cb->idx++);
+ if (!start)
+ goto nla_put_failure;
+
+ NLA_PUT(cb->skb, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
+
+ nla_nest_end(cb->skb, start);
+ err = 0;
+
+ nla_put_failure:
+ return err;
+}
+
+static int nl80211_get_auth_list(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ struct net_device *dev;
+ struct sk_buff *msg;
+ void *hdr;
+ int err;
+ struct nlattr *start;
+ struct add_cb_data cb;
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->get_auth_list) {
+ err = -EOPNOTSUPP;
+ goto put_drv;
+ }
+
+ hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_AUTH_LIST);
+ if (IS_ERR(hdr)) {
+ err = PTR_ERR(hdr);
+ goto put_drv;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+
+ start = nla_nest_start(msg, NL80211_ATTR_BSSID_LIST);
+ if (!start) {
+ err = -ENOBUFS;
+ goto msg_free;
+ }
+
+ cb.skb = msg;
+ cb.idx = 1;
+ err = drv->ops->get_auth_list(drv->priv, dev, &cb, add_bssid);
+ if (err)
+ goto msg_free;
+
+ nla_nest_end(msg, start);
+
+ genlmsg_end(msg, hdr);
+
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto put_drv;
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ msg_free:
+ nlmsg_free(msg);
+ put_drv:
+ cfg80211_put_drv(drv);
+ dev_put(dev);
+ return err;
+}
+
+static int nl80211_initiate_scan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_driver *drv;
+ int err;
+ struct net_device *dev;
+ struct scan_params params;
+ struct scan_channel *channels = NULL;
+ int count = -1;
+
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->initiate_scan) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ params.active = 0;
+
+ if (info->attrs[NL80211_ATTR_FLAGS])
+ params.active = !!(nla_get_u32(info->attrs[NL80211_ATTR_FLAGS])
+ & NL80211_FLAG_SCAN_TYPE_ACTIVE);
+
+ if (info->attrs[NL80211_ATTR_CHANNEL_LIST]) {
+ struct nlattr *attr = info->attrs[NL80211_ATTR_CHANNEL_LIST];
+ struct nlattr *nla;
+ int rem;
+ struct nlattr **tb;
+
+ /* let's count first */
+ count = 0;
+ nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem)
+ count++;
+
+ if (count == 0) {
+ /* assume we should actually scan all channels,
+ * scanning no channels make no sense */
+ count = -1;
+ goto done_channels;
+ }
+
+ channels = kmalloc(count * sizeof(struct scan_channel),
+ GFP_KERNEL);
+ tb = kmalloc((NL80211_ATTR_MAX+1) * sizeof(struct nlattr),
+ GFP_KERNEL);
+
+ count = 0;
+ nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) {
+ err = nla_parse(tb, NL80211_ATTR_MAX, nla_data(nla),
+ nla_len(nla), nl80211_policy);
+ if (err || !tb[NL80211_ATTR_CHANNEL]) {
+ err = -EINVAL;
+ kfree(tb);
+ kfree(channels);
+ goto out;
+ }
+ channels[count].channel =
+ nla_get_u32(tb[NL80211_ATTR_CHANNEL]);
+
+ channels[count].active = params.active;
+
+ if (tb[NL80211_ATTR_FLAGS])
+ channels[count].active =
+ !!(nla_get_u32(tb[NL80211_ATTR_FLAGS])
+ & NL80211_FLAG_SCAN_TYPE_ACTIVE);
+ count++;
+ }
+ kfree(tb);
+ }
+
+ done_channels:
+ params.channels = channels;
+ params.n_channels = count;
+
+ err = drv->ops->initiate_scan(drv->priv, dev, ¶ms);
+
+ kfree(channels);
+ out:
+ cfg80211_put_drv(drv);
+ dev_put(dev);
+ return err;
+}
+
+static struct genl_ops nl80211_ops[] = {
+ {
+ .cmd = NL80211_CMD_GET_CMDLIST,
+ .doit = nl80211_get_cmdlist,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_GET_WIPHYS,
+ .doit = nl80211_get_wiphys,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_GET_INTERFACES,
+ .doit = nl80211_get_intfs,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_INJECT,
+ .doit = nl80211_do_inject,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_ADD_VIRTUAL_INTERFACE,
+ .doit = nl80211_add_virt_intf,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_VIRTUAL_INTERFACE,
+ .doit = nl80211_del_virt_intf,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_CONFIGURE,
+ .doit = nl80211_configure,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_GET_CONFIG,
+ .doit = nl80211_get_config,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_SET_ROAMING_CONTROL,
+ .doit = nl80211_set_roaming,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_GET_ROAMING_CONTROL,
+ .doit = nl80211_get_roaming,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_SET_FIXED_BSSID,
+ .doit = nl80211_set_fixed_bssid,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_GET_FIXED_BSSID,
+ .doit = nl80211_get_fixed_bssid,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_GET_ASSOCIATION,
+ .doit = nl80211_get_association,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_DISASSOCIATE,
+ .doit = nl80211_assoc_deauth,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_DEAUTH,
+ .doit = nl80211_assoc_deauth,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_REASSOCIATE,
+ .doit = nl80211_assoc_deauth,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_GET_AUTH_LIST,
+ .doit = nl80211_get_auth_list,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_INITIATE_SCAN,
+ .doit = nl80211_initiate_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+};
+
+
+/* exported functions */
+
+void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, int flags, u8 cmd)
+{
+ /* since there is no private header just add the generic one */
+ return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
+}
+EXPORT_SYMBOL_GPL(nl80211hdr_put);
+
+void *nl80211msg_new(struct sk_buff **skb, u32 pid, u32 seq, int flags, u8 cmd)
+{
+ void *hdr;
+
+ *skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!*skb)
+ return ERR_PTR(-ENOBUFS);
+
+ hdr = nl80211hdr_put(*skb, pid, seq, flags, cmd);
+ if (!hdr) {
+ nlmsg_free(*skb);
+ return ERR_PTR(-ENOBUFS);
+ }
+
+ return hdr;
+}
+EXPORT_SYMBOL_GPL(nl80211msg_new);
+
+/* initialisation/exit functions */
+
+int nl80211_init(void)
+{
+ int err, i;
+
+ err = genl_register_family(&nl80211_fam);
+ if (err)
+ return err;
+
+ for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
+ err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
+ if (err)
+ goto err_out;
+ }
+ return 0;
+ err_out:
+ genl_unregister_family(&nl80211_fam);
+ return err;
+}
+
+void nl80211_exit(void)
+{
+ genl_unregister_family(&nl80211_fam);
+}
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
new file mode 100644
index 0000000..0edc7a4
--- /dev/null
+++ b/net/wireless/nl80211.h
@@ -0,0 +1,7 @@
+#ifndef __NET_WIRELESS_NL80211_H
+#define __NET_WIRELESS_NL80211_H
+
+extern int nl80211_init(void);
+extern void nl80211_exit(void);
+
+#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
new file mode 100644
index 0000000..1c7c361
--- /dev/null
+++ b/net/wireless/wext-compat.c
@@ -0,0 +1,25 @@
+/* NOT YET */
+
+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 :)
+
+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.
+
+compatibility mappings:
+
+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
+
+SIOCGIWAP
+ -> get association parameters and fill return bssid appropriately
+
--
1.4.4.2
--
John W. Linville
[email protected]
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Wed, 31 Jan 2007 13:32:55 -0500, John W. Linville wrote:
> OK, where were you guys in London? I don't recall any disagreement
> there?
Hm, I remember we agreed that merging of d80211 and cfg80211/nl80211
are two separate things. And a discussion about cfg80211/nl80211 not
being quite ready yet (if I remember correctly, Jouni participated in
that discussion). But well, it was just informal and maybe I missed
something else.
> The WEXT compat code should be enough to drive the cfg80211 code.
> And we would mark it CONFIG_EXPERIMENTAL (which I left out of the
> patch) for now. I suppose we could leave-out the nl80211 part to
> avoid userland lock-in for now?
Yes, that could solve that I think.
> This also has the advantage of including the ieee80211_ptr in the
> net_device structure, hopefully easing the task of packaging the rest
> of the stack for external builds.
Definitely.
> I suppose we could wait for at least one driver to be cfg80211-capable.
> Would that satisfy everyone?
If cfg80211-capable means also WPA-capable and if nl80211 is left out
until user space counterpart exists, yes, I'm okay with that.
Thanks,
Jiri
--
Jiri Benc
SUSE Labs
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Tue, Jan 30, 2007 at 08:37:17PM -0500, John W. Linville wrote:
> In London we agreed that it would be good to go ahead and merge
> cfg80211.
> Let me know if I've left anything out, screwed anything up, or if
> you no longer think a cfg80211 merge is appropriate at this time.
Merge to the mainline kernel? Do we really believe that this is ready
for this now? I have to admit that I have not personally tested this,
but based on what I've heard, this does not seem to be complete yet and
there are some areas that may need to be changed or at least there has
been some comments on desire to change something in the design, e.g.,
for the MLME requests and recently for the SSID handling. In addition, I
have not heard of any user space programs that would actually be using
this interface.
What would happen if this gets merged in now and some major problems are
found when people start implementing programs using this interface?
Would we be stuck with maintaining this user space interface as-is for
years without having a chance of easily fixing something?
I'm all for getting this into the mainline kernel as soon as possible,
but I do not think that this should happen before there are user space
programs that demonstrate that cfg80211 can be used without problems and
that the kernel side implementation is complete enough to allow some
meaningful operations to be completed with cfg80211 (likely with WEXT
disabled, i.e., cfg80211 being feature complete for, say, WPA
authentication).
I have been waiting for cfg80211 to become bit more stable and
functional before implementing support for it as an alternative driver
interface in wpa_supplicant. Based on what I heard in London, this would
not work yet, but maybe now would be a suitable time to at least start
experimenting with this by making a copy of the WEXT interface code and
starting to change operations one by one from WEXT to cfg80211 based on
what actually is available in the kernel.
--
Jouni Malinen PGP id EFC895FA
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Wed, Jan 31, 2007 at 08:25:49PM +0100, Jiri Benc wrote:
> On Wed, 31 Jan 2007 13:32:55 -0500, John W. Linville wrote:
> > OK, where were you guys in London? I don't recall any disagreement
> > there?
>
> Hm, I remember we agreed that merging of d80211 and cfg80211/nl80211
> are two separate things. And a discussion about cfg80211/nl80211 not
> being quite ready yet (if I remember correctly, Jouni participated in
> that discussion). But well, it was just informal and maybe I missed
> something else.
Yes, cfg80211 should go in first so we can use it to avoid all the
boilerplate code in existing and new drivers. Once we have netlink-capable
userspace nl80211 can be added ontop of that. This also allows for
possibly needed adjustments of the nl interface when writing the
tools actually using it.
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Wed, 2007-01-31 at 15:44 -0500, John W. Linville wrote:
> Agreed. I (and/or Johannes?) will rework the patches to remove the
> nl bits.
Starting tomorrow I can devote some time again.
Maybe we should resurrect those wiphy patches I had first?
johannes
On Tue, Jan 30, 2007 at 08:39:43PM -0500, John W. Linville wrote:
> From: Johannes Berg <[email protected]>
>
> This patch moves net/core/wireless.c to net/wireless/wext.c. I
> refrained from further cleanups though I was tempted. Hence this
> is a pure file move plus various build system adjustments.
Is this the right name? Shouldn't it be wext-ioctl.c given that
we'll hopefull soon grow a wext implementation with a proper method
table interface for drivers.
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Tue, 30 Jan 2007 21:46:01 -0500, Michael Wu wrote:
> I think drivers that currently support frame injection do it by allowing TX on
> a monitor interface w/ AVS or radiotap header. I would rather have this than
> requiring the use of NL80211 to inject frames since userspace won't have to
> do as much to continue supporting frame injection. It also makes more sense
> to me. Status notification can be done by a 802.11 ACK frame. Radiotap might
> need to be extended to support toggling the ACK frame reporting for TX and
> whatever else userspace wants to set.
Please, don't do that. You need to get a success/failure report from
the driver/stack. Doing that by dumping fake frames through the network
interfaces isn't something I could consider as a right behaviour. Also,
you need to match the sent frame and the report from the driver. This
gets really tricky when you use network interfaces.
Jiri
--
Jiri Benc
SUSE Labs
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Tue, 30 Jan 2007 18:48:07 -0800, Jouni Malinen wrote:
> I'm all for getting this into the mainline kernel as soon as possible,
> but I do not think that this should happen before there are user space
> programs that demonstrate that cfg80211 can be used without problems and
> that the kernel side implementation is complete enough to allow some
> meaningful operations to be completed with cfg80211 (likely with WEXT
> disabled, i.e., cfg80211 being feature complete for, say, WPA
> authentication).
I fully agree with Jouni.
Jiri
--
Jiri Benc
SUSE Labs
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Wed, Jan 31, 2007 at 08:07:41PM +0000, Christoph Hellwig wrote:
> On Wed, Jan 31, 2007 at 08:25:49PM +0100, Jiri Benc wrote:
> > On Wed, 31 Jan 2007 13:32:55 -0500, John W. Linville wrote:
> > > OK, where were you guys in London? I don't recall any disagreement
> > > there?
> >
> > Hm, I remember we agreed that merging of d80211 and cfg80211/nl80211
> > are two separate things. And a discussion about cfg80211/nl80211 not
> > being quite ready yet (if I remember correctly, Jouni participated in
> > that discussion). But well, it was just informal and maybe I missed
> > something else.
>
> Yes, cfg80211 should go in first so we can use it to avoid all the
> boilerplate code in existing and new drivers. Once we have netlink-capable
> userspace nl80211 can be added ontop of that. This also allows for
> possibly needed adjustments of the nl interface when writing the
> tools actually using it.
Agreed. I (and/or Johannes?) will rework the patches to remove the
nl bits.
Thanks,
John
--
John W. Linville
[email protected]
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Wed, Jan 31, 2007 at 06:29:08PM +0100, Jiri Benc wrote:
> On Tue, 30 Jan 2007 18:48:07 -0800, Jouni Malinen wrote:
> > I'm all for getting this into the mainline kernel as soon as possible,
> > but I do not think that this should happen before there are user space
> > programs that demonstrate that cfg80211 can be used without problems and
> > that the kernel side implementation is complete enough to allow some
> > meaningful operations to be completed with cfg80211 (likely with WEXT
> > disabled, i.e., cfg80211 being feature complete for, say, WPA
> > authentication).
>
> I fully agree with Jouni.
OK, where were you guys in London? I don't recall any disagreement
there?
The WEXT compat code should be enough to drive the cfg80211 code.
And we would mark it CONFIG_EXPERIMENTAL (which I left out of the
patch) for now. I suppose we could leave-out the nl80211 part to
avoid userland lock-in for now?
This also has the advantage of including the ieee80211_ptr in the
net_device structure, hopefully easing the task of packaging the rest
of the stack for external builds.
I suppose we could wait for at least one driver to be cfg80211-capable.
Would that satisfy everyone?
John
--
John W. Linville
[email protected]
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Wednesday 31 January 2007 18:37, Jiri Benc wrote:
> On Tue, 30 Jan 2007 21:46:01 -0500, Michael Wu wrote:
> > I think drivers that currently support frame injection do it by allowing TX on
> > a monitor interface w/ AVS or radiotap header. I would rather have this than
> > requiring the use of NL80211 to inject frames since userspace won't have to
> > do as much to continue supporting frame injection. It also makes more sense
> > to me. Status notification can be done by a 802.11 ACK frame. Radiotap might
> > need to be extended to support toggling the ACK frame reporting for TX and
> > whatever else userspace wants to set.
>
> Please, don't do that. You need to get a success/failure report from
> the driver/stack. Doing that by dumping fake frames through the network
> interfaces isn't something I could consider as a right behaviour. Also,
> you need to match the sent frame and the report from the driver. This
> gets really tricky when you use network interfaces.
TX control information also needs to be passed somehow.
So you would require some TXcontrol header or something
like that for the injected data. Not really scalable.
--
Greetings Michael.
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
From: Johannes Berg <[email protected]>
This patch moves net/core/wireless.c to net/wireless/wext.c. I
refrained from further cleanups though I was tempted. Hence this
is a pure file move plus various build system adjustments.
Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
---
net/Makefile | 2 +-
net/core/Makefile | 1 -
net/core/dev.c | 2 +-
net/core/wireless.c | 2353 -----------------------------------------------
net/wireless/Makefile | 2 +
net/wireless/wext-old.c | 2353 +++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 2357 insertions(+), 2356 deletions(-)
diff --git a/net/Makefile b/net/Makefile
index 278d6bf..ed07ec0 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -44,7 +44,7 @@ obj-$(CONFIG_ECONET) += econet/
obj-$(CONFIG_VLAN_8021Q) += 8021q/
obj-$(CONFIG_IP_DCCP) += dccp/
obj-$(CONFIG_IP_SCTP) += sctp/
-obj-$(CONFIG_CFG80211) += wireless/
+obj-y += wireless/
obj-$(CONFIG_IEEE80211) += ieee80211/
obj-$(CONFIG_TIPC) += tipc/
obj-$(CONFIG_NETLABEL) += netlabel/
diff --git a/net/core/Makefile b/net/core/Makefile
index 73272d5..4751613 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -13,7 +13,6 @@ obj-y += dev.o ethtool.o dev_mcast.o dst.o netevent.o \
obj-$(CONFIG_XFRM) += flow.o
obj-$(CONFIG_SYSFS) += net-sysfs.o
obj-$(CONFIG_NET_PKTGEN) += pktgen.o
-obj-$(CONFIG_WIRELESS_EXT) += wireless.o
obj-$(CONFIG_NETPOLL) += netpoll.o
obj-$(CONFIG_NET_DMA) += user_dma.o
obj-$(CONFIG_FIB_RULES) += fib_rules.o
diff --git a/net/core/dev.c b/net/core/dev.c
index e660cb5..c8822aa 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2811,7 +2811,7 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
}
dev_load(ifr.ifr_name);
rtnl_lock();
- /* Follow me in net/core/wireless.c */
+ /* Follow me in net/wireless/wext-old.c */
ret = wireless_process_ioctl(&ifr, cmd);
rtnl_unlock();
if (IW_IS_GET(cmd) &&
diff --git a/net/core/wireless.c b/net/core/wireless.c
deleted file mode 100644
index f69ab7b..0000000
--- a/net/core/wireless.c
+++ /dev/null
@@ -1,2353 +0,0 @@
-/*
- * This file implement the Wireless Extensions APIs.
- *
- * Authors : Jean Tourrilhes - HPL - <[email protected]>
- * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
- *
- * (As all part of the Linux kernel, this file is GPL)
- */
-
-/************************** DOCUMENTATION **************************/
-/*
- * API definition :
- * --------------
- * See <linux/wireless.h> for details of the APIs and the rest.
- *
- * History :
- * -------
- *
- * v1 - 5.12.01 - Jean II
- * o Created this file.
- *
- * v2 - 13.12.01 - Jean II
- * o Move /proc/net/wireless stuff from net/core/dev.c to here
- * o Make Wireless Extension IOCTLs go through here
- * o Added iw_handler handling ;-)
- * o Added standard ioctl description
- * o Initial dumb commit strategy based on orinoco.c
- *
- * v3 - 19.12.01 - Jean II
- * o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
- * o Add event dispatcher function
- * o Add event description
- * o Propagate events as rtnetlink IFLA_WIRELESS option
- * o Generate event on selected SET requests
- *
- * v4 - 18.04.02 - Jean II
- * o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
- *
- * v5 - 21.06.02 - Jean II
- * o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup)
- * o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
- * o Add IWEVCUSTOM for driver specific event/scanning token
- * o Turn on WE_STRICT_WRITE by default + kernel warning
- * o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
- * o Fix off-by-one in test (extra_size <= IFNAMSIZ)
- *
- * v6 - 9.01.03 - Jean II
- * o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
- * o Add enhanced spy support : iw_handler_set_thrspy() and event.
- * o Add WIRELESS_EXT version display in /proc/net/wireless
- *
- * v6 - 18.06.04 - Jean II
- * o Change get_spydata() method for added safety
- * o Remove spy #ifdef, they are always on -> cleaner code
- * o Allow any size GET request if user specifies length > max
- * and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
- * o Start migrating get_wireless_stats to struct iw_handler_def
- * o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
- * Based on patch from Pavel Roskin <[email protected]> :
- * o Fix kernel data leak to user space in private handler handling
- *
- * v7 - 18.3.05 - Jean II
- * o Remove (struct iw_point *)->pointer from events and streams
- * o Remove spy_offset from struct iw_handler_def
- * o Start deprecating dev->get_wireless_stats, output a warning
- * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
- * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
- *
- * 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()
- */
-
-/***************************** INCLUDES *****************************/
-
-#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 */
-#include <linux/interrupt.h>
-
-#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() */
-
-/**************************** CONSTANTS ****************************/
-
-/* Debugging stuff */
-#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
-#undef WE_RTNETLINK_DEBUG /* Debug RtNetlink API */
-#undef WE_EVENT_DEBUG /* Debug Event dispatcher */
-#undef WE_SPY_DEBUG /* Debug enhanced spy support */
-
-/* Options */
-//CONFIG_NET_WIRELESS_RTNETLINK /* Wireless requests over RtNetlink */
-#define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */
-#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[] = {
- 0, /* IW_PRIV_TYPE_NONE */
- 1, /* IW_PRIV_TYPE_BYTE */
- 1, /* IW_PRIV_TYPE_CHAR */
- 0, /* Not defined */
- sizeof(__u32), /* IW_PRIV_TYPE_INT */
- sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */
- sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */
- 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
- * of the section below.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Return the driver handler associated with a specific Wireless Extension.
- * Called from various place, so make sure it remains efficient.
- */
-static inline iw_handler get_handler(struct net_device *dev,
- unsigned int cmd)
-{
- /* Don't "optimise" the following variable, it will crash */
- unsigned int index; /* *MUST* be unsigned */
-
- /* Check if we have some wireless handlers defined */
- if(dev->wireless_handlers == NULL)
- return NULL;
-
- /* Try as a standard command */
- index = cmd - SIOCIWFIRST;
- 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)
- return dev->wireless_handlers->private[index];
-
- /* Not found */
- return NULL;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * 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)
- *
- * Note : our current commit strategy is currently pretty dumb,
- * but we will be able to improve on that...
- * The goal is to try to agreagate as many changes as possible
- * before doing the commit. Drivers that will define a commit handler
- * are usually those that need a reset after changing parameters, so
- * we want to minimise the number of reset.
- * A cool idea is to use a timer : at each "set" command, we re-set the
- * timer, when the timer eventually fires, we call the driver.
- * Hopefully, more on that later.
- *
- * Also, I'm waiting to see how many people will complain about the
- * netif_running(dev) test. I'm open on that one...
- * Hopefully, the driver will remember to do a commit in "open()" ;-)
- */
-static inline int call_commit_handler(struct net_device * 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,
- NULL, NULL);
- } else
- return 0; /* Command completed successfully */
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Calculate size of private arguments
- */
-static inline int get_priv_size(__u16 args)
-{
- int num = args & IW_PRIV_SIZE_MASK;
- int type = (args & IW_PRIV_TYPE_MASK) >> 12;
-
- return num * iw_priv_type_size[type];
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Re-calculate the size of private arguments
- */
-static inline int adjust_priv_size(__u16 args,
- union iwreq_data * wrqu)
-{
- int num = wrqu->data.length;
- int max = args & IW_PRIV_SIZE_MASK;
- int type = (args & IW_PRIV_TYPE_MASK) >> 12;
-
- /* Make sure the driver doesn't goof up */
- if (max < num)
- num = max;
-
- return num * iw_priv_type_size[type];
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Standard Wireless Handler : get wireless stats
- * Allow programatic access to /proc/net/wireless even if /proc
- * doesn't exist... Also more efficient...
- */
-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;
-
- stats = get_wireless_stats(dev);
- 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;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Standard Wireless Handler : get iwpriv definitions
- * Export the driver private handler definition
- * They will be picked up by tools like iwpriv...
- */
-static int iw_handler_get_private(struct net_device * dev,
- struct iw_request_info * info,
- union iwreq_data * wrqu,
- char * extra)
-{
- /* Check if the driver has something to export */
- 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) {
- /* 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... */
- wrqu->data.length = dev->wireless_handlers->num_private_args;
- return -E2BIG;
- }
-
- /* Set the number of available ioctls. */
- wrqu->data.length = dev->wireless_handlers->num_private_args;
-
- /* Copy structure to the user buffer. */
- memcpy(extra, dev->wireless_handlers->private_args,
- sizeof(struct iw_priv_args) * wrqu->data.length);
-
- 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
- * is through IOCTLs.
- * In there, we check if we need to call the new driver API (iw_handler)
- * or just call the driver ioctl handler.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * 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]);
-
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n",
- ifr->ifr_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_IOCTL_DEBUG */
-
- /* 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);
-
-#ifdef WE_SET_EVENT
- /* 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);
-#endif /* WE_SET_EVENT */
- } else {
- char * extra;
- 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)) {
- /* 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... */
- }
- }
-
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
- dev->name, extra_size);
-#endif /* WE_IOCTL_DEBUG */
-
- /* Create the kernel buffer */
- /* 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)) {
- err = copy_from_user(extra, iwr->u.data.pointer,
- iwr->u.data.length *
- descr->token_size);
- if (err) {
- kfree(extra);
- return -EFAULT;
- }
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Got %d bytes\n",
- dev->name,
- iwr->u.data.length * descr->token_size);
-#endif /* WE_IOCTL_DEBUG */
- }
-
- /* 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) {
- kfree(extra);
- return -E2BIG;
- }
-
- err = copy_to_user(iwr->u.data.pointer, extra,
- iwr->u.data.length *
- descr->token_size);
- if (err)
- ret = -EFAULT;
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
- dev->name,
- iwr->u.data.length * descr->token_size);
-#endif /* WE_IOCTL_DEBUG */
- }
-
-#ifdef WE_SET_EVENT
- /* 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);
- }
-#endif /* WE_SET_EVENT */
-
- /* Cleanup - I told you it wasn't that long ;-) */
- kfree(extra);
- }
-
- /* Call commit handler if needed and defined */
- if(ret == -EIWCOMMIT)
- ret = call_commit_handler(dev);
-
- /* Here, we will generate the appropriate event if needed */
-
- return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a private Wireless Extension handler.
- * We do various checks and also take care of moving data between
- * user space and kernel space.
- * It's not as nice and slimline as the standard wrapper. The cause
- * is struct iw_priv_args, which was not really designed for the
- * job we are going here.
- *
- * IMPORTANT : This function prevent to set and get data on the same
- * IOCTL and enforce the SET/GET convention. Not doing it would be
- * far too hairy...
- * If you need to set and get data at the same time, please don't use
- * a iw_handler but process it in your ioctl handler (i.e. use the
- * old driver API).
- */
-static inline int ioctl_private_call(struct net_device * dev,
- struct ifreq * ifr,
- unsigned int cmd,
- iw_handler handler)
-{
- struct iwreq * iwr = (struct iwreq *) ifr;
- const struct iw_priv_args * descr = NULL;
- struct iw_request_info info;
- int extra_size = 0;
- int i;
- 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) {
- 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) {
- 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)) {
- int offset = 0; /* For sub-ioctls */
- /* Check for sub-ioctl handler */
- if(descr->name[0] == '\0')
- /* Reserve one int for sub-ioctl index */
- offset = sizeof(__u32);
-
- /* Size of set arguments */
- extra_size = get_priv_size(descr->set_args);
-
- /* Does it fits in iwr ? */
- if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
- ((extra_size + offset) <= IFNAMSIZ))
- extra_size = 0;
- } else {
- /* Size of get arguments */
- extra_size = get_priv_size(descr->get_args);
-
- /* Does it fits in iwr ? */
- if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
- (extra_size <= IFNAMSIZ))
- extra_size = 0;
- }
- }
-
- /* Prepare the call */
- info.cmd = cmd;
- info.flags = 0;
-
- /* Check if we have a pointer to user space data or not. */
- if(extra_size == 0) {
- /* No extra arguments. Trivial to handle */
- ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
- } else {
- char * extra;
- int err;
-
- /* 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;
-
- /* Does it fits within bounds ? */
- if(iwr->u.data.length > (descr->set_args &
- IW_PRIV_SIZE_MASK))
- return -E2BIG;
- } else {
- /* Check NULL pointer */
- if(iwr->u.data.pointer == NULL)
- return -EFAULT;
- }
-
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
- dev->name, extra_size);
-#endif /* WE_IOCTL_DEBUG */
-
- /* Always allocate for max space. Easier, and won't last
- * long... */
- 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,
- extra_size);
- if (err) {
- kfree(extra);
- return -EFAULT;
- }
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Got %d elem\n",
- dev->name, iwr->u.data.length);
-#endif /* WE_IOCTL_DEBUG */
- }
-
- /* 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)) {
-
- /* Adjust for the actual length if it's variable,
- * avoid leaking kernel bits outside. */
- if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
- extra_size = adjust_priv_size(descr->get_args,
- &(iwr->u));
- }
-
- err = copy_to_user(iwr->u.data.pointer, extra,
- extra_size);
- if (err)
- ret = -EFAULT;
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
- dev->name, iwr->u.data.length);
-#endif /* WE_IOCTL_DEBUG */
- }
-
- /* Cleanup - I told you it wasn't that long ;-) */
- kfree(extra);
- }
-
-
- /* Call commit handler if needed and defined */
- if(ret == -EIWCOMMIT)
- ret = call_commit_handler(dev);
-
- return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Main IOCTl dispatcher. Called from the main networking code
- * (dev_ioctl() in net/core/dev.c).
- * Check the type of IOCTL and call the appropriate wrapper...
- */
-int wireless_process_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:
- /* 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_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;
- }
- /* Not reached */
- return -EINVAL;
-}
-
-/********************** RTNETLINK REQUEST API **********************/
-/*
- * The alternate user space API to configure all those Wireless Extensions
- * is through RtNetlink.
- * This API support only the new driver API (iw_handler).
- *
- * This RtNetlink API use the same query/reply model as the ioctl API.
- * Maximum effort has been done to fit in the RtNetlink model, and
- * we support both RtNetlink Set and RtNelink Get operations.
- * On the other hand, we don't offer Dump operations because of the
- * following reasons :
- * o Large number of parameters, most optional
- * o Large size of some parameters (> 100 bytes)
- * o Each parameters need to be extracted from hardware
- * o Scan requests can take seconds and disable network activity.
- * Because of this high cost/overhead, we want to return only the
- * parameters the user application is really interested in.
- * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag.
- *
- * The API uses the standard RtNetlink socket. When the RtNetlink code
- * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request,
- * it calls here.
- */
-
-#ifdef CONFIG_NET_WIRELESS_RTNETLINK
-/* ---------------------------------------------------------------- */
-/*
- * 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]);
-
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n",
- dev->name, cmd);
- printk(KERN_DEBUG "%s (WE.r) : 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_RTNETLINK_DEBUG */
-
- /* Check if wrqu is complete */
- hdr_len = event_type_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",
- dev->name, request_len);
-#endif /* WE_RTNETLINK_DEBUG */
- 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;
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
- dev->name, extra_size, buffer_size);
-#endif /* WE_RTNETLINK_DEBUG */
-
- /* 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);
-
-#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)
- 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]);
-
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n",
- dev->name, cmd);
- printk(KERN_DEBUG "%s (WE.r) : 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_RTNETLINK_DEBUG */
-
- /* 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) {
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG
- "%s (WE.r) : Wireless request too short (%d)\n",
- dev->name, request_len);
-#endif /* WE_RTNETLINK_DEBUG */
- 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) {
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
- dev->name, extra_size);
-#endif /* WE_RTNETLINK_DEBUG */
- return -EINVAL;
- }
-
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
- dev->name, extra_size);
-#endif /* WE_RTNETLINK_DEBUG */
-
- /* 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);
- }
-
-#ifdef WE_SET_EVENT
- /* 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);
- }
-#endif /* WE_SET_EVENT */
-
- /* Cleanup - I told you it wasn't that long ;-) */
- if(extra)
- kfree(extra);
-
- /* Call commit handler if needed and defined */
- if(ret == -EIWCOMMIT)
- ret = call_commit_handler(dev);
-
- return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a private Wireless Extension GET handler.
- * Same as above...
- * It's not as nice and slimline as the standard wrapper. The cause
- * is struct iw_priv_args, which was not really designed for the
- * job we are going here.
- *
- * IMPORTANT : This function prevent to set and get data on the same
- * IOCTL and enforce the SET/GET convention. Not doing it would be
- * far too hairy...
- * If you need to set and get data at the same time, please don't use
- * a iw_handler but process it in your ioctl handler (i.e. use the
- * old driver API).
- */
-static inline int rtnetlink_private_get(struct net_device * dev,
- struct iw_event * request,
- int request_len,
- iw_handler handler,
- char ** p_buf,
- int * p_len)
-{
- const struct iw_priv_args * descr = NULL;
- unsigned int cmd;
- union iwreq_data * wrqu;
- int hdr_len;
- struct iw_request_info info;
- int extra_size = 0;
- int i;
- char * buffer = NULL;
- int buffer_size = 0;
- int ret = -EINVAL;
-
- /* 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) {
- descr = &(dev->wireless_handlers->private_args[i]);
- break;
- }
- if(descr == NULL)
- return -EOPNOTSUPP;
-
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
- dev->name, cmd);
- printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
- dev->name, descr->name, descr->set_args, descr->get_args);
-#endif /* WE_RTNETLINK_DEBUG */
-
- /* Compute the max size of the get arguments */
- extra_size = get_priv_size(descr->get_args);
-
- /* Does it fits in wrqu ? */
- 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;
- }
-
- /* Check if wrqu is complete */
- if(request_len < hdr_len) {
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG
- "%s (WE.r) : Wireless request too short (%d)\n",
- dev->name, request_len);
-#endif /* WE_RTNETLINK_DEBUG */
- return -EINVAL;
- }
-
- /* Prepare the call */
- info.cmd = cmd;
- info.flags = 0;
-
- /* Check if we have a pointer to user space data or not. */
- if(extra_size == 0) {
-
- /* 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, (char *) wrqu);
-
- } else {
- char * extra;
-
- /* Buffer for full reply */
- buffer_size = extra_size + IW_EV_POINT_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);
-#endif /* WE_RTNETLINK_DEBUG */
-
- /* 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 *) request) + IW_EV_LCP_LEN,
- 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);
-
- /* Adjust for the actual length if it's variable,
- * avoid leaking kernel bits outside. */
- if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
- extra_size = adjust_priv_size(descr->get_args, wrqu);
- /* 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);
-
-#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 */
- }
-
- /* 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 private Wireless Extension SET handler.
- * Same as above...
- * It's not as nice and slimline as the standard wrapper. The cause
- * is struct iw_priv_args, which was not really designed for the
- * job we are going here.
- *
- * IMPORTANT : This function prevent to set and get data on the same
- * IOCTL and enforce the SET/GET convention. Not doing it would be
- * far too hairy...
- * If you need to set and get data at the same time, please don't use
- * a iw_handler but process it in your ioctl handler (i.e. use the
- * old driver API).
- */
-static inline int rtnetlink_private_set(struct net_device * dev,
- struct iw_event * request,
- int request_len,
- iw_handler handler)
-{
- const struct iw_priv_args * descr = NULL;
- unsigned int cmd;
- union iwreq_data * wrqu;
- union iwreq_data wrqu_point;
- int hdr_len;
- char * extra = NULL;
- int extra_size = 0;
- int offset = 0; /* For sub-ioctls */
- struct iw_request_info info;
- int i;
- int ret = -EINVAL;
-
- /* 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) {
- descr = &(dev->wireless_handlers->private_args[i]);
- break;
- }
- if(descr == NULL)
- return -EOPNOTSUPP;
-
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
- ifr->ifr_name, cmd);
- printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
- dev->name, descr->name, descr->set_args, descr->get_args);
-#endif /* WE_RTNETLINK_DEBUG */
-
- /* Compute the size of the set arguments */
- /* Check for sub-ioctl handler */
- if(descr->name[0] == '\0')
- /* Reserve one int for sub-ioctl index */
- offset = sizeof(__u32);
-
- /* Size of set arguments */
- extra_size = get_priv_size(descr->set_args);
-
- /* Does it fits in wrqu ? */
- if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
- (extra_size <= IFNAMSIZ)) {
- hdr_len = IW_EV_LCP_LEN + extra_size;
- extra_size = 0;
- } else {
- hdr_len = IW_EV_POINT_LEN;
- }
-
- /* Extract fixed header from request. This is properly aligned. */
- wrqu = &request->u;
-
- /* Check if wrqu is complete */
- if(request_len < hdr_len) {
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG
- "%s (WE.r) : Wireless request too short (%d)\n",
- dev->name, request_len);
-#endif /* WE_RTNETLINK_DEBUG */
- return -EINVAL;
- }
-
- /* Prepare the call */
- info.cmd = cmd;
- info.flags = 0;
-
- /* Check if we have a pointer to user space data or not. */
- if(extra_size == 0) {
-
- /* No extra arguments. Trivial to handle */
- ret = handler(dev, &info, wrqu, (char *) wrqu);
-
- } 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);
-
- /* Does it fits within bounds ? */
- if(wrqu_point.data.length > (descr->set_args &
- IW_PRIV_SIZE_MASK))
- return -E2BIG;
-
- /* Real length of payload */
- extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
-
- /* Check if request is self consistent */
- 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);
-#endif /* WE_RTNETLINK_DEBUG */
- return -EINVAL;
- }
-
-#ifdef WE_RTNETLINK_DEBUG
- printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
- dev->name, extra_size);
-#endif /* WE_RTNETLINK_DEBUG */
-
- /* Always allocate for max space. Easier, and won't last
- * long... */
- 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);
-
- /* Cleanup - I told you it wasn't that long ;-) */
- kfree(extra);
- }
-
- /* Call commit handler if needed and defined */
- if(ret == -EIWCOMMIT)
- ret = call_commit_handler(dev);
-
- 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 wireless_rtnetlink_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) {
- /* Check if we have some wireless handlers defined */
- if(dev->wireless_handlers == NULL)
- return -EOPNOTSUPP;
- /* Get Wireless Stats */
- return rtnetlink_standard_get(dev,
- request,
- request->len,
- &iw_handler_get_private,
- p_buf, p_len);
- }
-
- /* Basic check */
- if (!netif_device_present(dev))
- return -ENODEV;
-
- /* Try to find the handler */
- handler = get_handler(dev, request->cmd);
- if(handler != NULL) {
- /* Standard and private are not the same */
- if(request->cmd < SIOCIWFIRSTPRIV)
- return rtnetlink_standard_get(dev,
- request,
- request->len,
- handler,
- p_buf, p_len);
- else
- return rtnetlink_private_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 wireless_rtnetlink_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) {
- /* Standard and private are not the same */
- if(request->cmd < SIOCIWFIRSTPRIV)
- return rtnetlink_standard_set(dev,
- request,
- request->len,
- handler);
- else
- return rtnetlink_private_set(dev,
- request,
- request->len,
- handler);
- }
-
- return -EOPNOTSUPP;
-}
-#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 <[email protected]> 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.
- * Now, the driver can delegate this task to Wireless Extensions.
- * It needs to use those standard spy iw_handler in struct iw_handler_def,
- * push data to us via wireless_spy_update() and include struct iw_spy_data
- * in its private part (and export it in net_device->wireless_data->spy_data).
- * One of the main advantage of centralising spy support here is that
- * it becomes much easier to improve and extend it without having to touch
- * the drivers. One example is the addition of the Spy-Threshold events.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Return the pointer to the spy data in the driver.
- * Because this is called on the Rx path via wireless_spy_update(),
- * we want it to be efficient...
- */
-static inline struct iw_spy_data * get_spydata(struct net_device *dev)
-{
- /* This is the new way */
- if(dev->wireless_data)
- return(dev->wireless_data->spy_data);
- return NULL;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : set Spy List
- */
-int iw_handler_set_spy(struct net_device * dev,
- struct iw_request_info * info,
- union iwreq_data * wrqu,
- char * extra)
-{
- struct iw_spy_data * spydata = get_spydata(dev);
- struct sockaddr * address = (struct sockaddr *) extra;
-
- /* Make sure driver is not buggy or using the old API */
- if(!spydata)
- return -EOPNOTSUPP;
-
- /* Disable spy collection while we copy the addresses.
- * While we copy addresses, any call to wireless_spy_update()
- * will NOP. This is OK, as anyway the addresses are changing. */
- spydata->spy_number = 0;
-
- /* We want to operate without locking, because wireless_spy_update()
- * most likely will happen in the interrupt handler, and therefore
- * have its own locking constraints and needs performance.
- * The rtnl_lock() make sure we don't race with the other iw_handlers.
- * This make sure wireless_spy_update() "see" that the spy list
- * is temporarily disabled. */
- smp_wmb();
-
- /* Are there are addresses to copy? */
- if(wrqu->data.length > 0) {
- int i;
-
- /* Copy addresses */
- for(i = 0; i < wrqu->data.length; i++)
- memcpy(spydata->spy_address[i], address[i].sa_data,
- ETH_ALEN);
- /* Reset stats */
- memset(spydata->spy_stat, 0,
- sizeof(struct iw_quality) * IW_MAX_SPY);
-
-#ifdef WE_SPY_DEBUG
- printk(KERN_DEBUG "iw_handler_set_spy() : wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length);
- for (i = 0; i < wrqu->data.length; i++)
- printk(KERN_DEBUG
- "%02X:%02X:%02X:%02X:%02X:%02X \n",
- spydata->spy_address[i][0],
- spydata->spy_address[i][1],
- spydata->spy_address[i][2],
- spydata->spy_address[i][3],
- spydata->spy_address[i][4],
- spydata->spy_address[i][5]);
-#endif /* WE_SPY_DEBUG */
- }
-
- /* Make sure above is updated before re-enabling */
- smp_wmb();
-
- /* Enable addresses */
- spydata->spy_number = wrqu->data.length;
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : get Spy List
- */
-int iw_handler_get_spy(struct net_device * dev,
- struct iw_request_info * info,
- union iwreq_data * wrqu,
- char * extra)
-{
- struct iw_spy_data * spydata = get_spydata(dev);
- struct sockaddr * address = (struct sockaddr *) extra;
- int i;
-
- /* Make sure driver is not buggy or using the old API */
- if(!spydata)
- return -EOPNOTSUPP;
-
- wrqu->data.length = spydata->spy_number;
-
- /* Copy addresses. */
- for(i = 0; i < spydata->spy_number; i++) {
- memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
- address[i].sa_family = AF_UNIX;
- }
- /* Copy stats to the user buffer (just after). */
- if(spydata->spy_number > 0)
- memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number),
- spydata->spy_stat,
- sizeof(struct iw_quality) * spydata->spy_number);
- /* Reset updated flags. */
- for(i = 0; i < spydata->spy_number; i++)
- spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : set spy threshold
- */
-int iw_handler_set_thrspy(struct net_device * dev,
- struct iw_request_info *info,
- union iwreq_data * wrqu,
- char * extra)
-{
- struct iw_spy_data * spydata = get_spydata(dev);
- struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
-
- /* Make sure driver is not buggy or using the old API */
- if(!spydata)
- return -EOPNOTSUPP;
-
- /* Just do it */
- memcpy(&(spydata->spy_thr_low), &(threshold->low),
- 2 * sizeof(struct iw_quality));
-
- /* Clear flag */
- memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
-
-#ifdef WE_SPY_DEBUG
- printk(KERN_DEBUG "iw_handler_set_thrspy() : low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
-#endif /* WE_SPY_DEBUG */
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : get spy threshold
- */
-int iw_handler_get_thrspy(struct net_device * dev,
- struct iw_request_info *info,
- union iwreq_data * wrqu,
- char * extra)
-{
- struct iw_spy_data * spydata = get_spydata(dev);
- struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
-
- /* Make sure driver is not buggy or using the old API */
- if(!spydata)
- return -EOPNOTSUPP;
-
- /* Just do it */
- memcpy(&(threshold->low), &(spydata->spy_thr_low),
- 2 * sizeof(struct iw_quality));
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Prepare and send a Spy Threshold event
- */
-static void iw_send_thrspy_event(struct net_device * dev,
- struct iw_spy_data * spydata,
- unsigned char * address,
- struct iw_quality * wstats)
-{
- union iwreq_data wrqu;
- struct iw_thrspy threshold;
-
- /* Init */
- wrqu.data.length = 1;
- wrqu.data.flags = 0;
- /* Copy address */
- memcpy(threshold.addr.sa_data, address, ETH_ALEN);
- threshold.addr.sa_family = ARPHRD_ETHER;
- /* Copy stats */
- memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
- /* Copy also thresholds */
- memcpy(&(threshold.low), &(spydata->spy_thr_low),
- 2 * sizeof(struct iw_quality));
-
-#ifdef WE_SPY_DEBUG
- printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
- threshold.addr.sa_data[0],
- threshold.addr.sa_data[1],
- threshold.addr.sa_data[2],
- threshold.addr.sa_data[3],
- threshold.addr.sa_data[4],
- threshold.addr.sa_data[5], threshold.qual.level);
-#endif /* WE_SPY_DEBUG */
-
- /* Send event to user space */
- wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Call for the driver to update the spy data.
- * For now, the spy data is a simple array. As the size of the array is
- * small, this is good enough. If we wanted to support larger number of
- * spy addresses, we should use something more efficient...
- */
-void wireless_spy_update(struct net_device * dev,
- unsigned char * address,
- struct iw_quality * wstats)
-{
- struct iw_spy_data * spydata = get_spydata(dev);
- int i;
- int match = -1;
-
- /* Make sure driver is not buggy or using the old API */
- if(!spydata)
- return;
-
-#ifdef WE_SPY_DEBUG
- printk(KERN_DEBUG "wireless_spy_update() : wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
-#endif /* WE_SPY_DEBUG */
-
- /* Update all records that match */
- for(i = 0; i < spydata->spy_number; i++)
- if(!compare_ether_addr(address, spydata->spy_address[i])) {
- memcpy(&(spydata->spy_stat[i]), wstats,
- sizeof(struct iw_quality));
- match = i;
- }
-
- /* Generate an event if we cross the spy threshold.
- * To avoid event storms, we have a simple hysteresis : we generate
- * event only when we go under the low threshold or above the
- * high threshold. */
- if(match >= 0) {
- if(spydata->spy_thr_under[match]) {
- if(wstats->level > spydata->spy_thr_high.level) {
- spydata->spy_thr_under[match] = 0;
- iw_send_thrspy_event(dev, spydata,
- address, wstats);
- }
- } else {
- if(wstats->level < spydata->spy_thr_low.level) {
- spydata->spy_thr_under[match] = 1;
- iw_send_thrspy_event(dev, spydata,
- address, wstats);
- }
- }
- }
-}
-
-EXPORT_SYMBOL(iw_handler_get_spy);
-EXPORT_SYMBOL(iw_handler_get_thrspy);
-EXPORT_SYMBOL(iw_handler_set_spy);
-EXPORT_SYMBOL(iw_handler_set_thrspy);
-EXPORT_SYMBOL(wireless_send_event);
-EXPORT_SYMBOL(wireless_spy_update);
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index c030b12..b039edd 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -2,3 +2,5 @@ obj-$(CONFIG_CFG80211) += cfg80211.o
cfg80211-objs := \
core.o nl80211.o
+
+obj-$(CONFIG_WIRELESS_EXT) += wext-old.o
diff --git a/net/wireless/wext-old.c b/net/wireless/wext-old.c
new file mode 100644
index 0000000..f69ab7b
--- /dev/null
+++ b/net/wireless/wext-old.c
@@ -0,0 +1,2353 @@
+/*
+ * This file implement the Wireless Extensions APIs.
+ *
+ * Authors : Jean Tourrilhes - HPL - <[email protected]>
+ * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+
+/************************** DOCUMENTATION **************************/
+/*
+ * API definition :
+ * --------------
+ * See <linux/wireless.h> for details of the APIs and the rest.
+ *
+ * History :
+ * -------
+ *
+ * v1 - 5.12.01 - Jean II
+ * o Created this file.
+ *
+ * v2 - 13.12.01 - Jean II
+ * o Move /proc/net/wireless stuff from net/core/dev.c to here
+ * o Make Wireless Extension IOCTLs go through here
+ * o Added iw_handler handling ;-)
+ * o Added standard ioctl description
+ * o Initial dumb commit strategy based on orinoco.c
+ *
+ * v3 - 19.12.01 - Jean II
+ * o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
+ * o Add event dispatcher function
+ * o Add event description
+ * o Propagate events as rtnetlink IFLA_WIRELESS option
+ * o Generate event on selected SET requests
+ *
+ * v4 - 18.04.02 - Jean II
+ * o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
+ *
+ * v5 - 21.06.02 - Jean II
+ * o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup)
+ * o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
+ * o Add IWEVCUSTOM for driver specific event/scanning token
+ * o Turn on WE_STRICT_WRITE by default + kernel warning
+ * o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
+ * o Fix off-by-one in test (extra_size <= IFNAMSIZ)
+ *
+ * v6 - 9.01.03 - Jean II
+ * o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
+ * o Add enhanced spy support : iw_handler_set_thrspy() and event.
+ * o Add WIRELESS_EXT version display in /proc/net/wireless
+ *
+ * v6 - 18.06.04 - Jean II
+ * o Change get_spydata() method for added safety
+ * o Remove spy #ifdef, they are always on -> cleaner code
+ * o Allow any size GET request if user specifies length > max
+ * and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
+ * o Start migrating get_wireless_stats to struct iw_handler_def
+ * o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
+ * Based on patch from Pavel Roskin <[email protected]> :
+ * o Fix kernel data leak to user space in private handler handling
+ *
+ * v7 - 18.3.05 - Jean II
+ * o Remove (struct iw_point *)->pointer from events and streams
+ * o Remove spy_offset from struct iw_handler_def
+ * o Start deprecating dev->get_wireless_stats, output a warning
+ * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
+ * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
+ *
+ * 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()
+ */
+
+/***************************** INCLUDES *****************************/
+
+#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 */
+#include <linux/interrupt.h>
+
+#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() */
+
+/**************************** CONSTANTS ****************************/
+
+/* Debugging stuff */
+#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
+#undef WE_RTNETLINK_DEBUG /* Debug RtNetlink API */
+#undef WE_EVENT_DEBUG /* Debug Event dispatcher */
+#undef WE_SPY_DEBUG /* Debug enhanced spy support */
+
+/* Options */
+//CONFIG_NET_WIRELESS_RTNETLINK /* Wireless requests over RtNetlink */
+#define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */
+#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[] = {
+ 0, /* IW_PRIV_TYPE_NONE */
+ 1, /* IW_PRIV_TYPE_BYTE */
+ 1, /* IW_PRIV_TYPE_CHAR */
+ 0, /* Not defined */
+ sizeof(__u32), /* IW_PRIV_TYPE_INT */
+ sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */
+ sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */
+ 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
+ * of the section below.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Return the driver handler associated with a specific Wireless Extension.
+ * Called from various place, so make sure it remains efficient.
+ */
+static inline iw_handler get_handler(struct net_device *dev,
+ unsigned int cmd)
+{
+ /* Don't "optimise" the following variable, it will crash */
+ unsigned int index; /* *MUST* be unsigned */
+
+ /* Check if we have some wireless handlers defined */
+ if(dev->wireless_handlers == NULL)
+ return NULL;
+
+ /* Try as a standard command */
+ index = cmd - SIOCIWFIRST;
+ 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)
+ return dev->wireless_handlers->private[index];
+
+ /* Not found */
+ return NULL;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * 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)
+ *
+ * Note : our current commit strategy is currently pretty dumb,
+ * but we will be able to improve on that...
+ * The goal is to try to agreagate as many changes as possible
+ * before doing the commit. Drivers that will define a commit handler
+ * are usually those that need a reset after changing parameters, so
+ * we want to minimise the number of reset.
+ * A cool idea is to use a timer : at each "set" command, we re-set the
+ * timer, when the timer eventually fires, we call the driver.
+ * Hopefully, more on that later.
+ *
+ * Also, I'm waiting to see how many people will complain about the
+ * netif_running(dev) test. I'm open on that one...
+ * Hopefully, the driver will remember to do a commit in "open()" ;-)
+ */
+static inline int call_commit_handler(struct net_device * 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,
+ NULL, NULL);
+ } else
+ return 0; /* Command completed successfully */
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Calculate size of private arguments
+ */
+static inline int get_priv_size(__u16 args)
+{
+ int num = args & IW_PRIV_SIZE_MASK;
+ int type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+ return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Re-calculate the size of private arguments
+ */
+static inline int adjust_priv_size(__u16 args,
+ union iwreq_data * wrqu)
+{
+ int num = wrqu->data.length;
+ int max = args & IW_PRIV_SIZE_MASK;
+ int type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+ /* Make sure the driver doesn't goof up */
+ if (max < num)
+ num = max;
+
+ return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get wireless stats
+ * Allow programatic access to /proc/net/wireless even if /proc
+ * doesn't exist... Also more efficient...
+ */
+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;
+
+ stats = get_wireless_stats(dev);
+ 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;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get iwpriv definitions
+ * Export the driver private handler definition
+ * They will be picked up by tools like iwpriv...
+ */
+static int iw_handler_get_private(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ /* Check if the driver has something to export */
+ 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) {
+ /* 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... */
+ wrqu->data.length = dev->wireless_handlers->num_private_args;
+ return -E2BIG;
+ }
+
+ /* Set the number of available ioctls. */
+ wrqu->data.length = dev->wireless_handlers->num_private_args;
+
+ /* Copy structure to the user buffer. */
+ memcpy(extra, dev->wireless_handlers->private_args,
+ sizeof(struct iw_priv_args) * wrqu->data.length);
+
+ 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
+ * is through IOCTLs.
+ * In there, we check if we need to call the new driver API (iw_handler)
+ * or just call the driver ioctl handler.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * 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]);
+
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n",
+ ifr->ifr_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_IOCTL_DEBUG */
+
+ /* 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);
+
+#ifdef WE_SET_EVENT
+ /* 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);
+#endif /* WE_SET_EVENT */
+ } else {
+ char * extra;
+ 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)) {
+ /* 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... */
+ }
+ }
+
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
+ dev->name, extra_size);
+#endif /* WE_IOCTL_DEBUG */
+
+ /* Create the kernel buffer */
+ /* 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)) {
+ err = copy_from_user(extra, iwr->u.data.pointer,
+ iwr->u.data.length *
+ descr->token_size);
+ if (err) {
+ kfree(extra);
+ return -EFAULT;
+ }
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "%s (WE) : Got %d bytes\n",
+ dev->name,
+ iwr->u.data.length * descr->token_size);
+#endif /* WE_IOCTL_DEBUG */
+ }
+
+ /* 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) {
+ kfree(extra);
+ return -E2BIG;
+ }
+
+ err = copy_to_user(iwr->u.data.pointer, extra,
+ iwr->u.data.length *
+ descr->token_size);
+ if (err)
+ ret = -EFAULT;
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
+ dev->name,
+ iwr->u.data.length * descr->token_size);
+#endif /* WE_IOCTL_DEBUG */
+ }
+
+#ifdef WE_SET_EVENT
+ /* 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);
+ }
+#endif /* WE_SET_EVENT */
+
+ /* Cleanup - I told you it wasn't that long ;-) */
+ kfree(extra);
+ }
+
+ /* Call commit handler if needed and defined */
+ if(ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ /* Here, we will generate the appropriate event if needed */
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int ioctl_private_call(struct net_device * dev,
+ struct ifreq * ifr,
+ unsigned int cmd,
+ iw_handler handler)
+{
+ struct iwreq * iwr = (struct iwreq *) ifr;
+ const struct iw_priv_args * descr = NULL;
+ struct iw_request_info info;
+ int extra_size = 0;
+ int i;
+ 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) {
+ 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) {
+ 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)) {
+ int offset = 0; /* For sub-ioctls */
+ /* Check for sub-ioctl handler */
+ if(descr->name[0] == '\0')
+ /* Reserve one int for sub-ioctl index */
+ offset = sizeof(__u32);
+
+ /* Size of set arguments */
+ extra_size = get_priv_size(descr->set_args);
+
+ /* Does it fits in iwr ? */
+ if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+ ((extra_size + offset) <= IFNAMSIZ))
+ extra_size = 0;
+ } else {
+ /* Size of get arguments */
+ extra_size = get_priv_size(descr->get_args);
+
+ /* Does it fits in iwr ? */
+ if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+ (extra_size <= IFNAMSIZ))
+ extra_size = 0;
+ }
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have a pointer to user space data or not. */
+ if(extra_size == 0) {
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
+ } else {
+ char * extra;
+ int err;
+
+ /* 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;
+
+ /* Does it fits within bounds ? */
+ if(iwr->u.data.length > (descr->set_args &
+ IW_PRIV_SIZE_MASK))
+ return -E2BIG;
+ } else {
+ /* Check NULL pointer */
+ if(iwr->u.data.pointer == NULL)
+ return -EFAULT;
+ }
+
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
+ dev->name, extra_size);
+#endif /* WE_IOCTL_DEBUG */
+
+ /* Always allocate for max space. Easier, and won't last
+ * long... */
+ 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,
+ extra_size);
+ if (err) {
+ kfree(extra);
+ return -EFAULT;
+ }
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "%s (WE) : Got %d elem\n",
+ dev->name, iwr->u.data.length);
+#endif /* WE_IOCTL_DEBUG */
+ }
+
+ /* 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)) {
+
+ /* Adjust for the actual length if it's variable,
+ * avoid leaking kernel bits outside. */
+ if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
+ extra_size = adjust_priv_size(descr->get_args,
+ &(iwr->u));
+ }
+
+ err = copy_to_user(iwr->u.data.pointer, extra,
+ extra_size);
+ if (err)
+ ret = -EFAULT;
+#ifdef WE_IOCTL_DEBUG
+ printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
+ dev->name, iwr->u.data.length);
+#endif /* WE_IOCTL_DEBUG */
+ }
+
+ /* Cleanup - I told you it wasn't that long ;-) */
+ kfree(extra);
+ }
+
+
+ /* Call commit handler if needed and defined */
+ if(ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main IOCTl dispatcher. Called from the main networking code
+ * (dev_ioctl() in net/core/dev.c).
+ * Check the type of IOCTL and call the appropriate wrapper...
+ */
+int wireless_process_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:
+ /* 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_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;
+ }
+ /* Not reached */
+ return -EINVAL;
+}
+
+/********************** RTNETLINK REQUEST API **********************/
+/*
+ * The alternate user space API to configure all those Wireless Extensions
+ * is through RtNetlink.
+ * This API support only the new driver API (iw_handler).
+ *
+ * This RtNetlink API use the same query/reply model as the ioctl API.
+ * Maximum effort has been done to fit in the RtNetlink model, and
+ * we support both RtNetlink Set and RtNelink Get operations.
+ * On the other hand, we don't offer Dump operations because of the
+ * following reasons :
+ * o Large number of parameters, most optional
+ * o Large size of some parameters (> 100 bytes)
+ * o Each parameters need to be extracted from hardware
+ * o Scan requests can take seconds and disable network activity.
+ * Because of this high cost/overhead, we want to return only the
+ * parameters the user application is really interested in.
+ * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag.
+ *
+ * The API uses the standard RtNetlink socket. When the RtNetlink code
+ * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request,
+ * it calls here.
+ */
+
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+/* ---------------------------------------------------------------- */
+/*
+ * 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]);
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n",
+ dev->name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : 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_RTNETLINK_DEBUG */
+
+ /* Check if wrqu is complete */
+ hdr_len = event_type_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",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ 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;
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
+ dev->name, extra_size, buffer_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* 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);
+
+#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)
+ 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]);
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n",
+ dev->name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : 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_RTNETLINK_DEBUG */
+
+ /* 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) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ 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) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* 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);
+ }
+
+#ifdef WE_SET_EVENT
+ /* 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);
+ }
+#endif /* WE_SET_EVENT */
+
+ /* Cleanup - I told you it wasn't that long ;-) */
+ if(extra)
+ kfree(extra);
+
+ /* Call commit handler if needed and defined */
+ if(ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension GET handler.
+ * Same as above...
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int rtnetlink_private_get(struct net_device * dev,
+ struct iw_event * request,
+ int request_len,
+ iw_handler handler,
+ char ** p_buf,
+ int * p_len)
+{
+ const struct iw_priv_args * descr = NULL;
+ unsigned int cmd;
+ union iwreq_data * wrqu;
+ int hdr_len;
+ struct iw_request_info info;
+ int extra_size = 0;
+ int i;
+ char * buffer = NULL;
+ int buffer_size = 0;
+ int ret = -EINVAL;
+
+ /* 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) {
+ descr = &(dev->wireless_handlers->private_args[i]);
+ break;
+ }
+ if(descr == NULL)
+ return -EOPNOTSUPP;
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
+ dev->name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
+ dev->name, descr->name, descr->set_args, descr->get_args);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Compute the max size of the get arguments */
+ extra_size = get_priv_size(descr->get_args);
+
+ /* Does it fits in wrqu ? */
+ 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;
+ }
+
+ /* Check if wrqu is complete */
+ if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have a pointer to user space data or not. */
+ if(extra_size == 0) {
+
+ /* 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, (char *) wrqu);
+
+ } else {
+ char * extra;
+
+ /* Buffer for full reply */
+ buffer_size = extra_size + IW_EV_POINT_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);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* 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 *) request) + IW_EV_LCP_LEN,
+ 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);
+
+ /* Adjust for the actual length if it's variable,
+ * avoid leaking kernel bits outside. */
+ if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
+ extra_size = adjust_priv_size(descr->get_args, wrqu);
+ /* 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);
+
+#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 */
+ }
+
+ /* 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 private Wireless Extension SET handler.
+ * Same as above...
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int rtnetlink_private_set(struct net_device * dev,
+ struct iw_event * request,
+ int request_len,
+ iw_handler handler)
+{
+ const struct iw_priv_args * descr = NULL;
+ unsigned int cmd;
+ union iwreq_data * wrqu;
+ union iwreq_data wrqu_point;
+ int hdr_len;
+ char * extra = NULL;
+ int extra_size = 0;
+ int offset = 0; /* For sub-ioctls */
+ struct iw_request_info info;
+ int i;
+ int ret = -EINVAL;
+
+ /* 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) {
+ descr = &(dev->wireless_handlers->private_args[i]);
+ break;
+ }
+ if(descr == NULL)
+ return -EOPNOTSUPP;
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
+ ifr->ifr_name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
+ dev->name, descr->name, descr->set_args, descr->get_args);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Compute the size of the set arguments */
+ /* Check for sub-ioctl handler */
+ if(descr->name[0] == '\0')
+ /* Reserve one int for sub-ioctl index */
+ offset = sizeof(__u32);
+
+ /* Size of set arguments */
+ extra_size = get_priv_size(descr->set_args);
+
+ /* Does it fits in wrqu ? */
+ if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+ (extra_size <= IFNAMSIZ)) {
+ hdr_len = IW_EV_LCP_LEN + extra_size;
+ extra_size = 0;
+ } else {
+ hdr_len = IW_EV_POINT_LEN;
+ }
+
+ /* Extract fixed header from request. This is properly aligned. */
+ wrqu = &request->u;
+
+ /* Check if wrqu is complete */
+ if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have a pointer to user space data or not. */
+ if(extra_size == 0) {
+
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, wrqu, (char *) wrqu);
+
+ } 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);
+
+ /* Does it fits within bounds ? */
+ if(wrqu_point.data.length > (descr->set_args &
+ IW_PRIV_SIZE_MASK))
+ return -E2BIG;
+
+ /* Real length of payload */
+ extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
+
+ /* Check if request is self consistent */
+ 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);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Always allocate for max space. Easier, and won't last
+ * long... */
+ 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);
+
+ /* Cleanup - I told you it wasn't that long ;-) */
+ kfree(extra);
+ }
+
+ /* Call commit handler if needed and defined */
+ if(ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ 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 wireless_rtnetlink_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) {
+ /* Check if we have some wireless handlers defined */
+ if(dev->wireless_handlers == NULL)
+ return -EOPNOTSUPP;
+ /* Get Wireless Stats */
+ return rtnetlink_standard_get(dev,
+ request,
+ request->len,
+ &iw_handler_get_private,
+ p_buf, p_len);
+ }
+
+ /* Basic check */
+ if (!netif_device_present(dev))
+ return -ENODEV;
+
+ /* Try to find the handler */
+ handler = get_handler(dev, request->cmd);
+ if(handler != NULL) {
+ /* Standard and private are not the same */
+ if(request->cmd < SIOCIWFIRSTPRIV)
+ return rtnetlink_standard_get(dev,
+ request,
+ request->len,
+ handler,
+ p_buf, p_len);
+ else
+ return rtnetlink_private_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 wireless_rtnetlink_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) {
+ /* Standard and private are not the same */
+ if(request->cmd < SIOCIWFIRSTPRIV)
+ return rtnetlink_standard_set(dev,
+ request,
+ request->len,
+ handler);
+ else
+ return rtnetlink_private_set(dev,
+ request,
+ request->len,
+ handler);
+ }
+
+ return -EOPNOTSUPP;
+}
+#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 <[email protected]> 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.
+ * Now, the driver can delegate this task to Wireless Extensions.
+ * It needs to use those standard spy iw_handler in struct iw_handler_def,
+ * push data to us via wireless_spy_update() and include struct iw_spy_data
+ * in its private part (and export it in net_device->wireless_data->spy_data).
+ * One of the main advantage of centralising spy support here is that
+ * it becomes much easier to improve and extend it without having to touch
+ * the drivers. One example is the addition of the Spy-Threshold events.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Return the pointer to the spy data in the driver.
+ * Because this is called on the Rx path via wireless_spy_update(),
+ * we want it to be efficient...
+ */
+static inline struct iw_spy_data * get_spydata(struct net_device *dev)
+{
+ /* This is the new way */
+ if(dev->wireless_data)
+ return(dev->wireless_data->spy_data);
+ return NULL;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set Spy List
+ */
+int iw_handler_set_spy(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ struct iw_spy_data * spydata = get_spydata(dev);
+ struct sockaddr * address = (struct sockaddr *) extra;
+
+ /* Make sure driver is not buggy or using the old API */
+ if(!spydata)
+ return -EOPNOTSUPP;
+
+ /* Disable spy collection while we copy the addresses.
+ * While we copy addresses, any call to wireless_spy_update()
+ * will NOP. This is OK, as anyway the addresses are changing. */
+ spydata->spy_number = 0;
+
+ /* We want to operate without locking, because wireless_spy_update()
+ * most likely will happen in the interrupt handler, and therefore
+ * have its own locking constraints and needs performance.
+ * The rtnl_lock() make sure we don't race with the other iw_handlers.
+ * This make sure wireless_spy_update() "see" that the spy list
+ * is temporarily disabled. */
+ smp_wmb();
+
+ /* Are there are addresses to copy? */
+ if(wrqu->data.length > 0) {
+ int i;
+
+ /* Copy addresses */
+ for(i = 0; i < wrqu->data.length; i++)
+ memcpy(spydata->spy_address[i], address[i].sa_data,
+ ETH_ALEN);
+ /* Reset stats */
+ memset(spydata->spy_stat, 0,
+ sizeof(struct iw_quality) * IW_MAX_SPY);
+
+#ifdef WE_SPY_DEBUG
+ printk(KERN_DEBUG "iw_handler_set_spy() : wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length);
+ for (i = 0; i < wrqu->data.length; i++)
+ printk(KERN_DEBUG
+ "%02X:%02X:%02X:%02X:%02X:%02X \n",
+ spydata->spy_address[i][0],
+ spydata->spy_address[i][1],
+ spydata->spy_address[i][2],
+ spydata->spy_address[i][3],
+ spydata->spy_address[i][4],
+ spydata->spy_address[i][5]);
+#endif /* WE_SPY_DEBUG */
+ }
+
+ /* Make sure above is updated before re-enabling */
+ smp_wmb();
+
+ /* Enable addresses */
+ spydata->spy_number = wrqu->data.length;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get Spy List
+ */
+int iw_handler_get_spy(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ struct iw_spy_data * spydata = get_spydata(dev);
+ struct sockaddr * address = (struct sockaddr *) extra;
+ int i;
+
+ /* Make sure driver is not buggy or using the old API */
+ if(!spydata)
+ return -EOPNOTSUPP;
+
+ wrqu->data.length = spydata->spy_number;
+
+ /* Copy addresses. */
+ for(i = 0; i < spydata->spy_number; i++) {
+ memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
+ address[i].sa_family = AF_UNIX;
+ }
+ /* Copy stats to the user buffer (just after). */
+ if(spydata->spy_number > 0)
+ memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number),
+ spydata->spy_stat,
+ sizeof(struct iw_quality) * spydata->spy_number);
+ /* Reset updated flags. */
+ for(i = 0; i < spydata->spy_number; i++)
+ spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set spy threshold
+ */
+int iw_handler_set_thrspy(struct net_device * dev,
+ struct iw_request_info *info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ struct iw_spy_data * spydata = get_spydata(dev);
+ struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
+
+ /* Make sure driver is not buggy or using the old API */
+ if(!spydata)
+ return -EOPNOTSUPP;
+
+ /* Just do it */
+ memcpy(&(spydata->spy_thr_low), &(threshold->low),
+ 2 * sizeof(struct iw_quality));
+
+ /* Clear flag */
+ memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
+
+#ifdef WE_SPY_DEBUG
+ printk(KERN_DEBUG "iw_handler_set_thrspy() : low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
+#endif /* WE_SPY_DEBUG */
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get spy threshold
+ */
+int iw_handler_get_thrspy(struct net_device * dev,
+ struct iw_request_info *info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ struct iw_spy_data * spydata = get_spydata(dev);
+ struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
+
+ /* Make sure driver is not buggy or using the old API */
+ if(!spydata)
+ return -EOPNOTSUPP;
+
+ /* Just do it */
+ memcpy(&(threshold->low), &(spydata->spy_thr_low),
+ 2 * sizeof(struct iw_quality));
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Prepare and send a Spy Threshold event
+ */
+static void iw_send_thrspy_event(struct net_device * dev,
+ struct iw_spy_data * spydata,
+ unsigned char * address,
+ struct iw_quality * wstats)
+{
+ union iwreq_data wrqu;
+ struct iw_thrspy threshold;
+
+ /* Init */
+ wrqu.data.length = 1;
+ wrqu.data.flags = 0;
+ /* Copy address */
+ memcpy(threshold.addr.sa_data, address, ETH_ALEN);
+ threshold.addr.sa_family = ARPHRD_ETHER;
+ /* Copy stats */
+ memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
+ /* Copy also thresholds */
+ memcpy(&(threshold.low), &(spydata->spy_thr_low),
+ 2 * sizeof(struct iw_quality));
+
+#ifdef WE_SPY_DEBUG
+ printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
+ threshold.addr.sa_data[0],
+ threshold.addr.sa_data[1],
+ threshold.addr.sa_data[2],
+ threshold.addr.sa_data[3],
+ threshold.addr.sa_data[4],
+ threshold.addr.sa_data[5], threshold.qual.level);
+#endif /* WE_SPY_DEBUG */
+
+ /* Send event to user space */
+ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call for the driver to update the spy data.
+ * For now, the spy data is a simple array. As the size of the array is
+ * small, this is good enough. If we wanted to support larger number of
+ * spy addresses, we should use something more efficient...
+ */
+void wireless_spy_update(struct net_device * dev,
+ unsigned char * address,
+ struct iw_quality * wstats)
+{
+ struct iw_spy_data * spydata = get_spydata(dev);
+ int i;
+ int match = -1;
+
+ /* Make sure driver is not buggy or using the old API */
+ if(!spydata)
+ return;
+
+#ifdef WE_SPY_DEBUG
+ printk(KERN_DEBUG "wireless_spy_update() : wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
+#endif /* WE_SPY_DEBUG */
+
+ /* Update all records that match */
+ for(i = 0; i < spydata->spy_number; i++)
+ if(!compare_ether_addr(address, spydata->spy_address[i])) {
+ memcpy(&(spydata->spy_stat[i]), wstats,
+ sizeof(struct iw_quality));
+ match = i;
+ }
+
+ /* Generate an event if we cross the spy threshold.
+ * To avoid event storms, we have a simple hysteresis : we generate
+ * event only when we go under the low threshold or above the
+ * high threshold. */
+ if(match >= 0) {
+ if(spydata->spy_thr_under[match]) {
+ if(wstats->level > spydata->spy_thr_high.level) {
+ spydata->spy_thr_under[match] = 0;
+ iw_send_thrspy_event(dev, spydata,
+ address, wstats);
+ }
+ } else {
+ if(wstats->level < spydata->spy_thr_low.level) {
+ spydata->spy_thr_under[match] = 1;
+ iw_send_thrspy_event(dev, spydata,
+ address, wstats);
+ }
+ }
+ }
+}
+
+EXPORT_SYMBOL(iw_handler_get_spy);
+EXPORT_SYMBOL(iw_handler_get_thrspy);
+EXPORT_SYMBOL(iw_handler_set_spy);
+EXPORT_SYMBOL(iw_handler_set_thrspy);
+EXPORT_SYMBOL(wireless_send_event);
+EXPORT_SYMBOL(wireless_spy_update);
--
1.4.4.2
--
John W. Linville
[email protected]
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Wed, 2007-01-31 at 14:40 +0000, Christoph Hellwig wrote:
> Is this the right name? Shouldn't it be wext-ioctl.c given that
> we'll hopefull soon grow a wext implementation with a proper method
> table interface for drivers.
No, wext is dead, the replacement is a mix of cfg80211 and nl80211.
Besides, wext isn't strictly ioctl-based, it does netlink too (but just
puts the ioctl data into netlink messages and has the same braindamage.)
johannes
From: Johannes Berg <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: Jiri Benc <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
---
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 | 16 +-
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(+), 665 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 d83c47f..2a78a6d 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -6,6 +6,7 @@
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <net/genetlink.h>
+#include <linux/wireless.h>
/*
* 802.11 configuration in-kernel interface
@@ -17,21 +18,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;
@@ -39,6 +31,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;
@@ -87,6 +86,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)
*
@@ -133,6 +135,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);
@@ -190,4 +193,13 @@ extern void *nl80211hdr_put(struct sk_buff *skb, u32 pid,
extern void *nl80211msg_new(struct sk_buff **skb, u32 pid,
u32 seq, int flags, u8 cmd);
+#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..8584046 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 b039edd..f285440 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -4,3 +4,13 @@ cfg80211-objs := \
core.o nl80211.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 4a10ec2..ebf312a 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -131,7 +131,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);
@@ -221,13 +221,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 nl80211_init();
}
static void cfg80211_exit(void)
{
- /* possibly need to do more later */
+#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
nl80211_exit();
}
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..d9786db
--- /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 <[email protected]> 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 <[email protected]>
+ *
+ * Copyright 2006 Johannes Berg <[email protected]>
+ *
+ * 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 <[email protected]> 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
[email protected]
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
On Tuesday 30 January 2007 20:38, John W. Linville wrote:
> From: Johannes Berg <[email protected]>
>
> + /* Inject a frame using NL80211_ATTR_FLAGS and NL80211_ATTR_FRAME.
> + * If kernel sends this, it's a status notification for the injected
> + * frame. */
> + NL80211_CMD_INJECT,
> +
I think drivers that currently support frame injection do it by allowing TX on
a monitor interface w/ AVS or radiotap header. I would rather have this than
requiring the use of NL80211 to inject frames since userspace won't have to
do as much to continue supporting frame injection. It also makes more sense
to me. Status notification can be done by a 802.11 ACK frame. Radiotap might
need to be extended to support toggling the ACK frame reporting for TX and
whatever else userspace wants to set.
[snip]
> + /* configure device */
> + NL80211_CMD_CONFIGURE,
> +
> + /* request configuration */
> + NL80211_CMD_GET_CONFIG,
> +
> + /* configuration sent from kernel */
> + NL80211_CMD_NEW_CONFIG,
> +
Why NEW?
[snip]
> + /* currently set roaming BSSID */
> + NL80211_CMD_FIXED_BSSID,
> +
Wasn't immediately obvious to me that this is from GET_FIXED_BSSID.
> + /* get current association information, if not associated then
> + * the BSSID attribute is not present in response */
> + NL80211_CMD_GET_ASSOCIATION,
> +
> + /* association notification and response to GET_BSSID */
> + NL80211_CMD_ASSOCIATION_CHANGED,
> +
I don't see a GET_BSSID. Did you mean GET_ASSOCIATION?
[snip]
> + /* attributes used for configuration */
> + /* network ID (pre 802.11 HW) */
> + NL80211_ATTR_NETWORK_ID,
> +
We really want to support (and port) pre 802.11 drivers?
[snip]
> + /* SSID of ESS to associate to */
BSS?
> + NL80211_ATTR_SSID,
> +
[snip]
> + * @list_interfaces: for each interfaces belonging to the wiphy identified
> + * by the priv pointer, call the one() function with the
> + * given data and the ifindex. This callback is required.
> + *
Do we really have to have this?
Thanks,
-Michael Wu
As far as I tested cfg80211 compatibility mode is incomplete. You cannot run
hostapd over it
If I recall correctly PRISM2_IOCTL_PRISM2_PARAM and friends are not passed
to d80211
Tomas.
On 1/31/07, Johannes Berg <[email protected]> wrote:
>
> On Wed, 2007-01-31 at 15:44 -0500, John W. Linville wrote:
>
> > Agreed. I (and/or Johannes?) will rework the patches to remove the
> > nl bits.
>
> Starting tomorrow I can devote some time again.
>
> Maybe we should resurrect those wiphy patches I had first?
>
> johannes
>
>
> _______________________________________________
> wireless mailing list
> [email protected]
> http://lists.tuxdriver.org/mailman/listinfo/wireless
>
>
>
>
On Thu, 2007-02-01 at 16:04 +0200, Tomas Winkler wrote:
> Can you elaborate shortly. I'm not from time 0 on this list.
Well. cfg80211 will replace the common wext ioctls. All the private wext
ioctls will not be handled there.
The rationale is that
(1) it doesn't delete priv ioctls, they'll still work for the migration
period
(2) you can't rely on private ioctls having a certain meaning for each
driver, ie. other drivers are free the reassign numbers of
sub-ioctls
(3) any behaviour that is generally useful should be standardised.
johannes
On Tue, 2007-01-30 at 21:46 -0500, Michael Wu wrote:
> I think drivers that currently support frame injection do it by allowing TX on
> a monitor interface w/ AVS or radiotap header. I would rather have this than
> requiring the use of NL80211 to inject frames since userspace won't have to
> do as much to continue supporting frame injection. It also makes more sense
> to me. Status notification can be done by a 802.11 ACK frame. Radiotap might
> need to be extended to support toggling the ACK frame reporting for TX and
> whatever else userspace wants to set.
I disagree. Generic netlink is trivially extensible while radiotap
isn't, and I really don't want a radiotap parser in the kernel.
Secondly, a lot of notification items will only be given via netlink
anyway, like failed decryption or whatever, and we don't want to keep
all this in fake management frames.
> > + /* configuration sent from kernel */
> > + NL80211_CMD_NEW_CONFIG,
> > +
> Why NEW?
I think Thomas suggested the scheme GET/SET/NEW but I don't really care.
> [snip]
> > + /* currently set roaming BSSID */
> > + NL80211_CMD_FIXED_BSSID,
> > +
> Wasn't immediately obvious to me that this is from GET_FIXED_BSSID.
should get a NEW prefix as well then? :)
> > + /* get current association information, if not associated then
> > + * the BSSID attribute is not present in response */
> > + NL80211_CMD_GET_ASSOCIATION,
> > +
> > + /* association notification and response to GET_BSSID */
> > + NL80211_CMD_ASSOCIATION_CHANGED,
> > +
> I don't see a GET_BSSID. Did you mean GET_ASSOCIATION?
Yeah, I suppose I do.
> [snip]
> > + /* attributes used for configuration */
> > + /* network ID (pre 802.11 HW) */
> > + NL80211_ATTR_NETWORK_ID,
> > +
> We really want to support (and port) pre 802.11 drivers?
Not really sure. Do we? This is probably the only thing required for
that... We can leave it out and if someone wants to port them later add
it in.
> [snip]
> > + /* SSID of ESS to associate to */
> BSS?
Well, I think that comes down to what happens if we don't set a BSSID
but do set an SSID and then tell it to associate. In that case, it
should chose a BSS from the ESS with that SSID.
> > + * @list_interfaces: for each interfaces belonging to the wiphy identified
> > + * by the priv pointer, call the one() function with the
> > + * given data and the ifindex. This callback is required.
> > + *
> Do we really have to have this?
We can only not have this if we mandate that all add/remove interface
operations go through cfg80211. I was afraid that wouldn't be too
popular. If we do mandate that then we can keep the list in cfg80211
instead of having to query the driver/stack.
However, d80211 at least will need access to the list. So then we have
to add API for that. And all the legacy non-d80211 drivers (except for
ipw2200) will ever only return a single interface so it's trivial to
implement there. Hence, I stuck with this. I'm open for changes, of
course.
If I now misunderstood the question... Yes, we do need to know the list
of devices associated to a certain wiphy, if only to be able to have
userspace print it out :)
johannes
On Thu, 2007-02-01 at 01:54 +0200, Tomas Winkler wrote:
> As far as I tested cfg80211 compatibility mode is incomplete. You
> cannot run hostapd over it
> If I recall correctly PRISM2_IOCTL_PRISM2_PARAM and friends are not
> passed to d80211
It's a priv ioctl, it's not supposed to be passed on.
johannes (who is getting positively pissed off about such requests)
On Thursday 01 February 2007 05:18, Johannes Berg wrote:
> I disagree. Generic netlink is trivially extensible while radiotap
> isn't, and I really don't want a radiotap parser in the kernel.
> Secondly, a lot of notification items will only be given via netlink
> anyway, like failed decryption or whatever, and we don't want to keep
> all this in fake management frames.
>
Okay then, let's not use fake frames then. Let's make monitor interfaces
resemble monitoring ethernet a little more by reporting both RXed frames and
TXed frames. *BSD does this too and it makes running ethereal on monitor
interfaces much more useful. TX frame reporting can be done simply in the
ieee80211_tx_status call, allowing the TX status to be reported on real
frames.
Doing this is actually somewhat orthogonal to packet injection via netlink or
monitor interface, but if we are reporting TXed frames with a radiotap
header, we might as well allow frame injection with a radiotap frame for
consistency. It seems far better than receiving frames on one interface and
then using netlink to TX on some wiphy. Not to mention that using a
configuration interface to send data doesn't make much sense when there's a
perfectly fine network interface available. Radiotap parsing isn't much
harder than EID parsing, allows current frame injection programs to continue
working, and might even make it easier to run those programs on BSD too. (I
don't know what mechanism BSD uses for frame injection)
-Michael Wu
On Thu, 1 Feb 2007 16:04:53 +0200, Tomas Winkler wrote:
> Can you elaborate shortly. I'm not from time 0 on this list.
> Thanks.
Use Google and list archives, that's why their exist.
Jiri
--
Jiri Benc
SUSE Labs
_______________________________________________
wireless mailing list
[email protected]
http://lists.tuxdriver.org/mailman/listinfo/wireless
Can you elaborate shortly. I'm not from time 0 on this list.
Thanks.
On 2/1/07, Johannes Berg <[email protected]> wrote:
>
> On Thu, 2007-02-01 at 01:54 +0200, Tomas Winkler wrote:
> > As far as I tested cfg80211 compatibility mode is incomplete. You
> > cannot run hostapd over it
> > If I recall correctly PRISM2_IOCTL_PRISM2_PARAM and friends are not
> > passed to d80211
>
> It's a priv ioctl, it's not supposed to be passed on.
>
> johannes (who is getting positively pissed off about such requests)
>
>
>
Johannes
I don't know who is missing something here, but what I'm saying that (1) is
not correct. priv ioctls don't work with compatibility set on.
In any case I'll check it again to be sure and I will apologize if I'm not
correct.
I definitely agree on two other points.
Thanks
Tomas
On 2/1/07, Johannes Berg <[email protected]> wrote:
>
> On Thu, 2007-02-01 at 16:04 +0200, Tomas Winkler wrote:
> > Can you elaborate shortly. I'm not from time 0 on this list.
>
> Well. cfg80211 will replace the common wext ioctls. All the private wext
> ioctls will not be handled there.
>
> The rationale is that
> (1) it doesn't delete priv ioctls, they'll still work for the migration
> period
> (2) you can't rely on private ioctls having a certain meaning for each
> driver, ie. other drivers are free the reassign numbers of
> sub-ioctls
> (3) any behaviour that is generally useful should be standardised.
>
> johannes
>
>
>