summaryrefslogtreecommitdiffstats
path: root/package/mac80211/src/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'package/mac80211/src/wireless')
-rw-r--r--package/mac80211/src/wireless/Makefile3
-rw-r--r--package/mac80211/src/wireless/core.c11
-rw-r--r--package/mac80211/src/wireless/nl80211.c919
-rw-r--r--package/mac80211/src/wireless/radiotap.c261
-rw-r--r--package/mac80211/src/wireless/sysfs.c52
-rw-r--r--package/mac80211/src/wireless/wext.c1509
6 files changed, 450 insertions, 2305 deletions
diff --git a/package/mac80211/src/wireless/Makefile b/package/mac80211/src/wireless/Makefile
index e746b3af8..5664c2cfd 100644
--- a/package/mac80211/src/wireless/Makefile
+++ b/package/mac80211/src/wireless/Makefile
@@ -1,5 +1,4 @@
-obj-$(CONFIG_WIRELESS_EXT) += wext.o
obj-$(CONFIG_CFG80211) += cfg80211.o
-cfg80211-y += core.o sysfs.o
+cfg80211-y += core.o sysfs.o radiotap.o
cfg80211-$(CONFIG_NL80211) += nl80211.o
diff --git a/package/mac80211/src/wireless/core.c b/package/mac80211/src/wireless/core.c
index 46e5ae070..35b79bee3 100644
--- a/package/mac80211/src/wireless/core.c
+++ b/package/mac80211/src/wireless/core.c
@@ -162,10 +162,15 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
/* this will check for collisions */
result = device_rename(&rdev->wiphy.dev, newname);
- if (!result)
+ if (result)
return result;
- /* TODO: do debugfs rename! */
+ if (!debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
+ rdev->wiphy.debugfsdir,
+ rdev->wiphy.debugfsdir->d_parent,
+ newname))
+ printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n",
+ newname);
nl80211_notify_dev_rename(rdev);
@@ -355,7 +360,7 @@ out_fail_notifier:
out_fail_sysfs:
return err;
}
-module_init(cfg80211_init);
+subsys_initcall(cfg80211_init);
static void cfg80211_exit(void)
{
diff --git a/package/mac80211/src/wireless/nl80211.c b/package/mac80211/src/wireless/nl80211.c
index ffbe6288a..58717f303 100644
--- a/package/mac80211/src/wireless/nl80211.c
+++ b/package/mac80211/src/wireless/nl80211.c
@@ -1,7 +1,7 @@
/*
* This is the new netlink-based wireless configuration interface.
*
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/if.h>
@@ -13,6 +13,7 @@
#include <linux/ieee80211.h>
#include <linux/nl80211.h>
#include <linux/rtnetlink.h>
+#include <linux/netlink.h>
#include <net/genetlink.h>
#include <net/cfg80211.h>
#include "core.h"
@@ -27,22 +28,6 @@ static struct genl_family nl80211_fam = {
.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_device **drv,
@@ -55,7 +40,7 @@ static int get_drv_dev_by_info_ifindex(struct genl_info *info,
ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
*dev = dev_get_by_index(ifindex);
- if (!dev)
+ if (!*dev)
return -ENODEV;
*drv = cfg80211_get_dev_from_ifindex(ifindex);
@@ -69,287 +54,194 @@ static int get_drv_dev_by_info_ifindex(struct genl_info *info,
/* policy for the attributes */
static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
- [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
- [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = BUS_ID_SIZE-1 },
+
[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
- [NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
- [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
- .len = IEEE80211_MAX_SSID_LEN },
- [NL80211_ATTR_CHANNEL] = { .type = NLA_U32 },
- [NL80211_ATTR_PHYMODE] = { .type = NLA_U32 },
- [NL80211_ATTR_CHANNEL_LIST] = { .type = NLA_NESTED },
- [NL80211_ATTR_BSS_LIST] = { .type = NLA_NESTED },
- [NL80211_ATTR_BSSTYPE] = { .type = NLA_U32 },
- [NL80211_ATTR_BEACON_PERIOD] = { .type = NLA_U32 },
- [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
- [NL80211_ATTR_TIMESTAMP] = { .type = NLA_U64 },
- [NL80211_ATTR_IE] = { .type = NLA_BINARY, .len = NL80211_MAX_IE_LEN },
- [NL80211_ATTR_AUTH_ALGORITHM] = { .type = NLA_U32 },
- [NL80211_ATTR_TIMEOUT_TU] = { .type = NLA_U32 },
- [NL80211_ATTR_REASON_CODE] = { .type = NLA_U32 },
- [NL80211_ATTR_ASSOCIATION_ID] = { .type = NLA_U16 },
- [NL80211_ATTR_DEAUTHENTICATED] = { .type = NLA_FLAG },
- [NL80211_ATTR_RX_SENSITIVITY] = { .type = NLA_U32 },
- [NL80211_ATTR_TRANSMIT_POWER] = { .type = NLA_U32 },
- [NL80211_ATTR_FRAG_THRESHOLD] = { .type = NLA_U32 },
- [NL80211_ATTR_FLAG_SCAN_ACTIVE] = { .type = NLA_FLAG },
- [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY },
- [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY },
- [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
- .len = WLAN_MAX_KEY_LEN },
- [NL80211_ATTR_KEY_ID] = { .type = NLA_U32 },
- [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
- [NL80211_ATTR_MAC] = { .len = ETH_ALEN },
- [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
+ [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
+ [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
};
-/* netlink command implementations */
+/* message building helper */
+static inline 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);
+}
-#define CHECK_CMD(ptr, cmd) \
- if (drv->ops->ptr) \
- NLA_PUT_FLAG(msg, NL80211_CMD_##cmd);
+/* netlink command implementations */
-static int nl80211_get_cmdlist(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+ struct cfg80211_registered_device *dev)
{
- struct cfg80211_registered_device *drv;
- struct sk_buff *msg;
void *hdr;
- int err;
- struct nlattr *start;
- drv = cfg80211_get_dev_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;
- }
+ hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
+ if (!hdr)
+ return -1;
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
+ NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
+ return genlmsg_end(msg, hdr);
- 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);
- NLA_PUT_FLAG(msg, NL80211_CMD_RENAME_WIPHY);
-
- CHECK_CMD(add_virtual_intf, ADD_VIRTUAL_INTERFACE);
- CHECK_CMD(del_virtual_intf, DEL_VIRTUAL_INTERFACE);
- CHECK_CMD(associate, ASSOCIATE);
- CHECK_CMD(disassociate, DISASSOCIATE);
- CHECK_CMD(deauth, DEAUTH);
- CHECK_CMD(initiate_scan, INITIATE_SCAN);
- CHECK_CMD(get_association, GET_ASSOCIATION);
- CHECK_CMD(get_auth_list, GET_AUTH_LIST);
- CHECK_CMD(add_key, ADD_KEY);
- CHECK_CMD(del_key, DEL_KEY);
+ nla_put_failure:
+ return genlmsg_cancel(msg, hdr);
+}
- nla_nest_end(msg, start);
+static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx = 0;
+ int start = cb->args[0];
+ struct cfg80211_registered_device *dev;
- genlmsg_end(msg, hdr);
+ mutex_lock(&cfg80211_drv_mutex);
+ list_for_each_entry(dev, &cfg80211_drv_list, list) {
+ if (++idx < start)
+ continue;
+ if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ dev) < 0)
+ break;
+ }
+ mutex_unlock(&cfg80211_drv_mutex);
- err = genlmsg_unicast(msg, info->snd_pid);
- goto put_drv;
+ cb->args[0] = idx;
- nla_put_failure:
- err = -ENOBUFS;
- nlmsg_free(msg);
- put_drv:
- cfg80211_put_dev(drv);
- return err;
+ return skb->len;
}
-#undef CHECK_CMD
-static int nl80211_get_wiphys(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
{
struct sk_buff *msg;
- void *hdr;
- struct nlattr *start, *indexstart;
- struct cfg80211_registered_device *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);
+ struct cfg80211_registered_device *dev;
- start = nla_nest_start(msg, NL80211_ATTR_WIPHY_LIST);
- if (!start)
- goto nla_outer_nest_failure;
+ dev = cfg80211_get_dev_from_info(info);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
- 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->idx);
- nla_nest_end(msg, indexstart);
- }
- mutex_unlock(&cfg80211_drv_mutex);
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ goto out_err;
- nla_nest_end(msg, start);
+ if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
+ goto out_free;
- genlmsg_end(msg, hdr);
+ cfg80211_put_dev(dev);
return genlmsg_unicast(msg, info->snd_pid);
- nla_put_failure:
- mutex_unlock(&cfg80211_drv_mutex);
- nla_outer_nest_failure:
+ out_free:
nlmsg_free(msg);
+ out_err:
+ cfg80211_put_dev(dev);
return -ENOBUFS;
}
-static int addifidx(struct net_device *dev, struct sk_buff *skb, int *idx)
+static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
{
- int err = -ENOBUFS;
- struct nlattr *start;
-
- dev_hold(dev);
+ struct cfg80211_registered_device *rdev;
+ int result;
- start = nla_nest_start(skb, *idx++);
- if (!start)
- goto nla_put_failure;
+ if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
+ return -EINVAL;
- NLA_PUT_U32(skb, NL80211_ATTR_IFINDEX, dev->ifindex);
- NLA_PUT_STRING(skb, NL80211_ATTR_IFNAME, dev->name);
+ rdev = cfg80211_get_dev_from_info(info);
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
- nla_nest_end(skb, start);
- err = 0;
+ result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
- nla_put_failure:
- dev_put(dev);
- return err;
+ cfg80211_put_dev(rdev);
+ return result;
}
-static int nl80211_get_intfs(struct sk_buff *skb, struct genl_info *info)
+
+static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+ struct net_device *dev)
{
- struct cfg80211_registered_device *drv;
- struct sk_buff *msg;
void *hdr;
- int err, array_idx;
- struct nlattr *start;
- struct wireless_dev *wdev;
-
- drv = cfg80211_get_dev_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->idx);
+ hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
+ if (!hdr)
+ return -1;
- start = nla_nest_start(msg, NL80211_ATTR_INTERFACE_LIST);
- if (!start) {
- err = -ENOBUFS;
- goto msg_free;
- }
-
- array_idx = 1;
- err = 0;
- mutex_lock(&drv->devlist_mtx);
- list_for_each_entry(wdev, &drv->netdev_list, list) {
- err = addifidx(wdev->netdev, msg, &array_idx);
- if (err)
- break;
- }
- mutex_unlock(&drv->devlist_mtx);
- 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_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
+ /* TODO: interface type */
+ return genlmsg_end(msg, hdr);
nla_put_failure:
- err = -ENOBUFS;
- msg_free:
- nlmsg_free(msg);
- put_drv:
- cfg80211_put_dev(drv);
- return err;
+ return genlmsg_cancel(msg, hdr);
}
-static int nl80211_add_virt_intf(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct cfg80211_registered_device *drv;
- int err;
- enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
-
- if (!info->attrs[NL80211_ATTR_IFNAME])
- return -EINVAL;
+ int wp_idx = 0;
+ int if_idx = 0;
+ int wp_start = cb->args[0];
+ int if_start = cb->args[1];
+ struct cfg80211_registered_device *dev;
+ struct wireless_dev *wdev;
- if (info->attrs[NL80211_ATTR_IFTYPE]) {
- type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
- if (type > NL80211_IFTYPE_MAX)
- return -EINVAL;
+ mutex_lock(&cfg80211_drv_mutex);
+ list_for_each_entry(dev, &cfg80211_drv_list, list) {
+ if (++wp_idx < wp_start)
+ continue;
+ if_idx = 0;
+
+ mutex_lock(&dev->devlist_mtx);
+ list_for_each_entry(wdev, &dev->netdev_list, list) {
+ if (++if_idx < if_start)
+ continue;
+ if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ wdev->netdev) < 0)
+ break;
+ }
+ mutex_unlock(&dev->devlist_mtx);
}
+ mutex_unlock(&cfg80211_drv_mutex);
- drv = cfg80211_get_dev_from_info(info);
- if (IS_ERR(drv))
- return PTR_ERR(drv);
-
- if (!drv->ops->add_virtual_intf) {
- err = -EOPNOTSUPP;
- goto unlock;
- }
+ cb->args[0] = wp_idx;
+ cb->args[1] = if_idx;
- rtnl_lock();
- err = drv->ops->add_virtual_intf(&drv->wiphy,
- nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
- rtnl_unlock();
-
- unlock:
- cfg80211_put_dev(drv);
- return err;
+ return skb->len;
}
-static int nl80211_del_virt_intf(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
{
- struct cfg80211_registered_device *drv;
- int ifindex, err;
- struct net_device *dev;
+ struct sk_buff *msg;
+ struct cfg80211_registered_device *dev;
+ struct net_device *netdev;
+ int err;
- err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+ err = get_drv_dev_by_info_ifindex(info, &dev, &netdev);
if (err)
return err;
- ifindex = dev->ifindex;
- dev_put(dev);
- if (!drv->ops->del_virtual_intf) {
- err = -EOPNOTSUPP;
- goto out;
- }
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ goto out_err;
- rtnl_lock();
- err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
- rtnl_unlock();
+ if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
+ goto out_free;
- out:
- cfg80211_put_dev(drv);
- return err;
+ dev_put(netdev);
+ cfg80211_put_dev(dev);
+
+ return genlmsg_unicast(msg, info->snd_pid);
+
+ out_free:
+ nlmsg_free(msg);
+ out_err:
+ dev_put(netdev);
+ cfg80211_put_dev(dev);
+ return -ENOBUFS;
}
-static int nl80211_change_virt_intf(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *drv;
int err, ifindex;
@@ -383,588 +275,128 @@ static int nl80211_change_virt_intf(struct sk_buff *skb, struct genl_info *info)
return err;
}
-static int nl80211_get_association(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *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;
- }
-
- rtnl_lock();
- err = drv->ops->get_association(&drv->wiphy, dev, bssid);
- rtnl_unlock();
- 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_dev(drv);
- dev_put(dev);
- return err;
-}
-
-static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
-{
- struct cfg80211_registered_device *drv;
- int err;
- struct net_device *dev;
- struct association_params assoc_params;
-
- memset(&assoc_params, 0, sizeof(assoc_params));
-
- err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
- if (err)
- return err;
-
- if (!drv->ops->associate) {
- err = -EOPNOTSUPP;
- goto out;
- }
+ enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
- if (!info->attrs[NL80211_ATTR_SSID])
+ if (!info->attrs[NL80211_ATTR_IFNAME])
return -EINVAL;
- assoc_params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
- assoc_params.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
-
- if (info->attrs[NL80211_ATTR_BSSID])
- assoc_params.bssid = nla_data(info->attrs[NL80211_ATTR_BSSID]);
-
- if (info->attrs[NL80211_ATTR_IE]) {
- err = check_information_element(info->attrs[NL80211_ATTR_IE]);
- if (err)
- goto out;
- assoc_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
- assoc_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
- }
-
- if (info->attrs[NL80211_ATTR_TIMEOUT_TU]) {
- assoc_params.timeout =
- nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT_TU]);
- assoc_params.valid |= ASSOC_PARAMS_TIMEOUT;
- }
-
- rtnl_lock();
- err = drv->ops->associate(&drv->wiphy, dev, &assoc_params);
- rtnl_unlock();
-
- out:
- cfg80211_put_dev(drv);
- dev_put(dev);
- return err;
-}
-
-static int nl80211_disassoc_deauth(struct sk_buff *skb, struct genl_info *info)
-{
- struct cfg80211_registered_device *drv;
- int err;
- struct net_device *dev;
- int (*act)(struct wiphy *wiphy, 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_DEAUTH:
- act = drv->ops->deauth;
- break;
- default:
- act = NULL;
- }
-
- if (!act) {
- err = -EOPNOTSUPP;
- goto out;
+ if (info->attrs[NL80211_ATTR_IFTYPE]) {
+ type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+ if (type > NL80211_IFTYPE_MAX)
+ return -EINVAL;
}
- rtnl_lock();
- err = act(&drv->wiphy, dev);
- rtnl_unlock();
- out:
- cfg80211_put_dev(drv);
- dev_put(dev);
- return err;
-}
-
-struct add_cb_data {
- int idx;
- struct sk_buff *skb;
-};
-
-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_device *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;
+ drv = cfg80211_get_dev_from_info(info);
+ if (IS_ERR(drv))
+ return PTR_ERR(drv);
- if (!drv->ops->get_auth_list) {
+ if (!drv->ops->add_virtual_intf) {
err = -EOPNOTSUPP;
- goto put_drv;
- }
-
- hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
- NL80211_CMD_NEW_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_BSS_LIST);
- if (!start) {
- err = -ENOBUFS;
- goto msg_free;
+ goto unlock;
}
- cb.skb = msg;
- cb.idx = 1;
rtnl_lock();
- err = drv->ops->get_auth_list(&drv->wiphy, dev, &cb, add_bssid);
+ err = drv->ops->add_virtual_intf(&drv->wiphy,
+ nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
rtnl_unlock();
- 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:
+ unlock:
cfg80211_put_dev(drv);
- dev_put(dev);
return err;
}
-static int nl80211_initiate_scan(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *drv;
- int err;
+ int ifindex, err;
struct net_device *dev;
- struct scan_params params;
- struct scan_channel *channels = NULL;
- int count = -1;
-
- if (info->attrs[NL80211_ATTR_PHYMODE])
- params.phymode = nla_get_u32(info->attrs[NL80211_ATTR_PHYMODE]);
-
- if (params.phymode > NL80211_PHYMODE_MAX)
- return -EINVAL;
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 = nla_get_flag(info->attrs[NL80211_ATTR_FLAG_SCAN_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;
- }
-
- if (count > NL80211_MAX_CHANNEL_LIST_ITEM) {
- err = -EINVAL;
- goto out;
- }
-
- 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].phymode = params.phymode;
-
- if (tb[NL80211_ATTR_PHYMODE])
- channels[count].phymode =
- nla_get_u32(tb[NL80211_ATTR_PHYMODE]);
-
- if (channels[count].phymode > NL80211_PHYMODE_MAX) {
- err = -EINVAL;
- kfree(tb);
- kfree(channels);
- goto out;
- }
-
- channels[count].channel =
- nla_get_u32(tb[NL80211_ATTR_CHANNEL]);
-
- channels[count].active =
- nla_get_flag(tb[NL80211_ATTR_FLAG_SCAN_ACTIVE]);
- count++;
- }
- kfree(tb);
- }
-
- done_channels:
- params.channels = channels;
- params.n_channels = count;
-
- rtnl_lock();
- err = drv->ops->initiate_scan(&drv->wiphy, dev, &params);
- rtnl_unlock();
-
- kfree(channels);
- out:
- cfg80211_put_dev(drv);
+ ifindex = dev->ifindex;
dev_put(dev);
- return err;
-}
-
-static int nl80211_rename_wiphy(struct sk_buff *skb, struct genl_info *info)
-{
- struct cfg80211_registered_device *rdev;
- int result;
-
- if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
- return -EINVAL;
-
- rdev = cfg80211_get_dev_from_info(info);
- if (IS_ERR(rdev))
- return PTR_ERR(rdev);
-
- result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
-
- cfg80211_put_dev(rdev);
- return result;
-}
-static int nl80211_key_cmd(struct sk_buff *skb, struct genl_info *info)
-{
- struct cfg80211_registered_device *drv;
- int err, del;
- struct net_device *dev;
- struct key_params params;
- int (*act)(struct wiphy *wiphy, struct net_device *dev,
- struct key_params *params);
-
- memset(&params, 0, sizeof(params));
-
- if (!info->attrs[NL80211_ATTR_KEY_TYPE])
- return -EINVAL;
-
- if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
- return -EINVAL;
-
- params.key_type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
- if (params.key_type > NL80211_KEYTYPE_MAX)
- return -EINVAL;
-
- err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
- if (err)
- return err;
-
- switch (info->genlhdr->cmd) {
- case NL80211_CMD_ADD_KEY:
- act = drv->ops->add_key;
- del = 0;
- break;
- case NL80211_CMD_DEL_KEY:
- act = drv->ops->del_key;
- del = 1;
- break;
- default:
- act = NULL;
- }
-
- if (!act) {
+ if (!drv->ops->del_virtual_intf) {
err = -EOPNOTSUPP;
goto out;
}
- if (info->attrs[NL80211_ATTR_KEY_DATA]) {
- params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
- params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
- }
-
- if (info->attrs[NL80211_ATTR_KEY_ID]) {
- params.key_id = nla_get_u32(info->attrs[NL80211_ATTR_KEY_ID]);
- } else {
- params.key_id = -1;
- }
-
- params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
-
- if (info->attrs[NL80211_ATTR_MAC]) {
- params.macaddress = nla_data(info->attrs[NL80211_ATTR_MAC]);
- } else {
- params.macaddress = NULL;
- }
-
rtnl_lock();
- err = act(&drv->wiphy, dev, &params);
+ err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
rtnl_unlock();
out:
cfg80211_put_dev(drv);
- dev_put(dev);
return err;
}
static struct genl_ops nl80211_ops[] = {
{
- .cmd = NL80211_CMD_RENAME_WIPHY,
- .doit = nl80211_rename_wiphy,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_GET_CMDLIST,
- .doit = nl80211_get_cmdlist,
- .policy = nl80211_policy,
- /* can be retrieved by unprivileged users */
- },
- {
- .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_CHANGE_VIRTUAL_INTERFACE,
- .doit = nl80211_change_virt_intf,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_GET_WIPHYS,
- .doit = nl80211_get_wiphys,
+ .cmd = NL80211_CMD_GET_WIPHY,
+ .doit = nl80211_get_wiphy,
+ .dumpit = nl80211_dump_wiphy,
.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_INITIATE_SCAN,
- .doit = nl80211_initiate_scan,
+ .cmd = NL80211_CMD_SET_WIPHY,
+ .doit = nl80211_set_wiphy,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
{
- .cmd = NL80211_CMD_GET_ASSOCIATION,
- .doit = nl80211_get_association,
+ .cmd = NL80211_CMD_GET_INTERFACE,
+ .doit = nl80211_get_interface,
+ .dumpit = nl80211_dump_interface,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
},
{
- .cmd = NL80211_CMD_ASSOCIATE,
- .doit = nl80211_associate,
+ .cmd = NL80211_CMD_SET_INTERFACE,
+ .doit = nl80211_set_interface,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
{
- .cmd = NL80211_CMD_DISASSOCIATE,
- .doit = nl80211_disassoc_deauth,
+ .cmd = NL80211_CMD_NEW_INTERFACE,
+ .doit = nl80211_new_interface,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
{
- .cmd = NL80211_CMD_DEAUTH,
- .doit = nl80211_disassoc_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_AP_SET_BEACON,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_AP_ADD_STA,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_AP_UPDATE_STA,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_AP_GET_STA_INFO,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_AP_SET_RATESETS,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
-*/
- {
- .cmd = NL80211_CMD_ADD_KEY,
- .doit = nl80211_key_cmd,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_DEL_KEY,
- .doit = nl80211_key_cmd,
+ .cmd = NL80211_CMD_DEL_INTERFACE,
+ .doit = nl80211_del_interface,
.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);
+/* multicast groups */
+static struct genl_multicast_group nl80211_config_mcgrp = {
+ .name = "config",
+};
/* notification functions */
void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
{
struct sk_buff *msg;
- void *hdr;
- hdr = nl80211msg_new(&msg, 0, 0, 0, NL80211_CMD_WIPHY_NEWNAME);
- if (IS_ERR(hdr))
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
return;
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->idx);
- NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&rdev->wiphy));
-
- genlmsg_end(msg, hdr);
- genlmsg_multicast(msg, 0, NL80211_GROUP_CONFIG, GFP_KERNEL);
-
- return;
+ if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
- nla_put_failure:
- nlmsg_free(msg);
+ genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
}
/* initialisation/exit functions */
@@ -982,6 +414,11 @@ int nl80211_init(void)
if (err)
goto err_out;
}
+
+ err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
+ if (err)
+ goto err_out;
+
return 0;
err_out:
genl_unregister_family(&nl80211_fam);
diff --git a/package/mac80211/src/wireless/radiotap.c b/package/mac80211/src/wireless/radiotap.c
new file mode 100644
index 000000000..28fbd0b0b
--- /dev/null
+++ b/package/mac80211/src/wireless/radiotap.c
@@ -0,0 +1,261 @@
+/*
+ * Radiotap parser
+ *
+ * Copyright 2007 Andy Green <andy@warmcat.com>
+ */
+
+#include <net/cfg80211.h>
+#include <net/ieee80211_radiotap.h>
+#include <asm/unaligned.h>
+
+/* function prototypes and related defs are in include/net/cfg80211.h */
+
+/**
+ * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
+ * @iterator: radiotap_iterator to initialize
+ * @radiotap_header: radiotap header to parse
+ * @max_length: total length we can parse into (eg, whole packet length)
+ *
+ * Returns: 0 or a negative error code if there is a problem.
+ *
+ * This function initializes an opaque iterator struct which can then
+ * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
+ * argument which is present in the header. It knows about extended
+ * present headers and handles them.
+ *
+ * How to use:
+ * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
+ * struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
+ * checking for a good 0 return code. Then loop calling
+ * __ieee80211_radiotap_iterator_next()... it returns either 0,
+ * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
+ * The iterator's @this_arg member points to the start of the argument
+ * associated with the current argument index that is present, which can be
+ * found in the iterator's @this_arg_index member. This arg index corresponds
+ * to the IEEE80211_RADIOTAP_... defines.
+ *
+ * Radiotap header length:
+ * You can find the CPU-endian total radiotap header length in
+ * iterator->max_length after executing ieee80211_radiotap_iterator_init()
+ * successfully.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned. Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ *
+ * Example code:
+ * See Documentation/networking/radiotap-headers.txt
+ */
+
+int ieee80211_radiotap_iterator_init(
+ struct ieee80211_radiotap_iterator *iterator,
+ struct ieee80211_radiotap_header *radiotap_header,
+ int max_length)
+{
+ /* Linux only supports version 0 radiotap format */
+ if (radiotap_header->it_version)
+ return -EINVAL;
+
+ /* sanity check for allowed length and radiotap length field */
+ if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len)))
+ return -EINVAL;
+
+ iterator->rtheader = radiotap_header;
+ iterator->max_length = le16_to_cpu(get_unaligned(
+ &radiotap_header->it_len));
+ iterator->arg_index = 0;
+ iterator->bitmap_shifter = le32_to_cpu(get_unaligned(
+ &radiotap_header->it_present));
+ iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header);
+ iterator->this_arg = NULL;
+
+ /* find payload start allowing for extended bitmap(s) */
+
+ if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) {
+ while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) &
+ (1<<IEEE80211_RADIOTAP_EXT)) {
+ iterator->arg += sizeof(u32);
+
+ /*
+ * check for insanity where the present bitmaps
+ * keep claiming to extend up to or even beyond the
+ * stated radiotap header length
+ */
+
+ if (((ulong)iterator->arg -
+ (ulong)iterator->rtheader) > iterator->max_length)
+ return -EINVAL;
+ }
+
+ iterator->arg += sizeof(u32);
+
+ /*
+ * no need to check again for blowing past stated radiotap
+ * header length, because ieee80211_radiotap_iterator_next
+ * checks it before it is dereferenced
+ */
+ }
+
+ /* we are all initialized happily */
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_radiotap_iterator_init);
+
+
+/**
+ * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
+ * @iterator: radiotap_iterator to move to next arg (if any)
+ *
+ * Returns: 0 if there is an argument to handle,
+ * -ENOENT if there are no more args or -EINVAL
+ * if there is something else wrong.
+ *
+ * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
+ * in @this_arg_index and sets @this_arg to point to the
+ * payload for the field. It takes care of alignment handling and extended
+ * present fields. @this_arg can be changed by the caller (eg,
+ * incremented to move inside a compound argument like
+ * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in
+ * little-endian format whatever the endianess of your CPU.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned. Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ */
+
+int ieee80211_radiotap_iterator_next(
+ struct ieee80211_radiotap_iterator *iterator)
+{
+
+ /*
+ * small length lookup table for all radiotap types we heard of
+ * starting from b0 in the bitmap, so we can walk the payload
+ * area of the radiotap header
+ *
+ * There is a requirement to pad args, so that args
+ * of a given length must begin at a boundary of that length
+ * -- but note that compound args are allowed (eg, 2 x u16
+ * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not
+ * a reliable indicator of alignment requirement.
+ *
+ * upper nybble: content alignment for arg
+ * lower nybble: content length for arg
+ */
+
+ static const u8 rt_sizes[] = {
+ [IEEE80211_RADIOTAP_TSFT] = 0x88,
+ [IEEE80211_RADIOTAP_FLAGS] = 0x11,
+ [IEEE80211_RADIOTAP_RATE] = 0x11,
+ [IEEE80211_RADIOTAP_CHANNEL] = 0x24,
+ [IEEE80211_RADIOTAP_FHSS] = 0x22,
+ [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11,
+ [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11,
+ [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22,
+ [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22,
+ [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22,
+ [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11,
+ [IEEE80211_RADIOTAP_ANTENNA] = 0x11,
+ [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11,
+ [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11,
+ [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22,
+ [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22,
+ [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11,
+ [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11,
+ /*
+ * add more here as they are defined in
+ * include/net/ieee80211_radiotap.h
+ */
+ };
+
+ /*
+ * for every radiotap entry we can at
+ * least skip (by knowing the length)...
+ */
+
+ while (iterator->arg_index < sizeof(rt_sizes)) {
+ int hit = 0;
+ int pad;
+
+ if (!(iterator->bitmap_shifter & 1))
+ goto next_entry; /* arg not present */
+
+ /*
+ * arg is present, account for alignment padding
+ * 8-bit args can be at any alignment
+ * 16-bit args must start on 16-bit boundary
+ * 32-bit args must start on 32-bit boundary
+ * 64-bit args must start on 64-bit boundary
+ *
+ * note that total arg size can differ from alignment of
+ * elements inside arg, so we use upper nybble of length
+ * table to base alignment on
+ *
+ * also note: these alignments are ** relative to the
+ * start of the radiotap header **. There is no guarantee
+ * that the radiotap header itself is aligned on any
+ * kind of boundary.
+ *
+ * the above is why get_unaligned() is used to dereference
+ * multibyte elements from the radiotap area
+ */
+
+ pad = (((ulong)iterator->arg) -
+ ((ulong)iterator->rtheader)) &
+ ((rt_sizes[iterator->arg_index] >> 4) - 1);
+
+ if (pad)
+ iterator->arg +=
+ (rt_sizes[iterator->arg_index] >> 4) - pad;
+
+ /*
+ * this is what we will return to user, but we need to
+ * move on first so next call has something fresh to test
+ */
+ iterator->this_arg_index = iterator->arg_index;
+ iterator->this_arg = iterator->arg;
+ hit = 1;
+
+ /* internally move on the size of this arg */
+ iterator->arg += rt_sizes[iterator->arg_index] & 0x0f;
+
+ /*
+ * check for insanity where we are given a bitmap that
+ * claims to have more arg content than the length of the
+ * radiotap section. We will normally end up equalling this
+ * max_length on the last arg, never exceeding it.
+ */
+
+ if (((ulong)iterator->arg - (ulong)iterator->rtheader) >
+ iterator->max_length)
+ return -EINVAL;
+
+ next_entry:
+ iterator->arg_index++;
+ if (unlikely((iterator->arg_index & 31) == 0)) {
+ /* completed current u32 bitmap */
+ if (iterator->bitmap_shifter & 1) {
+ /* b31 was set, there is more */
+ /* move to next u32 bitmap */
+ iterator->bitmap_shifter = le32_to_cpu(
+ get_unaligned(iterator->next_bitmap));
+ iterator->next_bitmap++;
+ } else
+ /* no more bitmaps: end */
+ iterator->arg_index = sizeof(rt_sizes);
+ } else /* just try the next bit */
+ iterator->bitmap_shifter >>= 1;
+
+ /* if we found a valid arg earlier, return it now */
+ if (hit)
+ return 0;
+ }
+
+ /* we don't know how to handle any more args, we're done */
+ return -ENOENT;
+}
+EXPORT_SYMBOL(ieee80211_radiotap_iterator_next);
diff --git a/package/mac80211/src/wireless/sysfs.c b/package/mac80211/src/wireless/sysfs.c
index 374d16db7..2d5d2255a 100644
--- a/package/mac80211/src/wireless/sysfs.c
+++ b/package/mac80211/src/wireless/sysfs.c
@@ -39,59 +39,9 @@ static ssize_t _show_permaddr(struct device *dev,
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
}
-static ssize_t _store_add_iface(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
- int res;
-
- if (len > IFNAMSIZ)
- return -EINVAL;
-
- if (!rdev->ops->add_virtual_intf)
- return -ENOSYS;
-
- rtnl_lock();
- res = rdev->ops->add_virtual_intf(&rdev->wiphy, (char*)buf,
- NL80211_IFTYPE_UNSPECIFIED);
- rtnl_unlock();
-
- return res ? res : len;
-}
-
-static ssize_t _store_remove_iface(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
- int res, ifidx;
- struct net_device *netdev;
-
- if (len > IFNAMSIZ)
- return -EINVAL;
-
- if (!rdev->ops->del_virtual_intf)
- return -ENOSYS;
-
- netdev = dev_get_by_name(buf);
- if (!netdev)
- return -ENODEV;
- ifidx = netdev->ifindex;
- dev_put(netdev);
-
- rtnl_lock();
- res = rdev->ops->del_virtual_intf(&rdev->wiphy, ifidx);
- rtnl_unlock();
-
- return res ? res : len;
-}
-
static struct device_attribute ieee80211_dev_attrs[] = {
__ATTR(index, S_IRUGO, _show_index, NULL),
__ATTR(macaddress, S_IRUGO, _show_permaddr, NULL),
- __ATTR(add_iface, S_IWUGO, NULL, _store_add_iface),
- __ATTR(remove_iface, S_IWUGO, NULL, _store_remove_iface),
{}
};
@@ -102,12 +52,14 @@ static void wiphy_dev_release(struct device *dev)
cfg80211_dev_free(rdev);
}
+#ifdef CONFIG_HOTPLUG
static int wiphy_uevent(struct device *dev, char **envp,
int num_envp, char *buf, int size)
{
/* TODO, we probably need stuff here */
return 0;
}
+#endif
struct class ieee80211_class = {
.name = "ieee80211",
diff --git a/package/mac80211/src/wireless/wext.c b/package/mac80211/src/wireless/wext.c
deleted file mode 100644
index d6aaf6519..000000000
--- a/package/mac80211/src/wireless/wext.c
+++ /dev/null
@@ -1,1509 +0,0 @@
-/*
- * This file implement the Wireless Extensions APIs.
- *
- * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
- *
- * (As all part of the Linux kernel, this file is GPL)
- */
-
-/************************** 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 <proski@gnu.org> :
- * o Fix kernel data leak to user space in private handler handling
- *
- * v7 - 18.3.05 - Jean II
- * o Remove (struct iw_point *)->pointer from events and streams
- * o Remove spy_offset from struct iw_handler_def
- * o Start deprecating dev->get_wireless_stats, output a warning
- * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
- * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
- *
- * v8 - 17.02.06 - Jean II
- * o RtNetlink requests support (SET/GET)
- *
- * v8b - 03.08.06 - Herbert Xu
- * o Fix Wireless Event locking issues.
- *
- * v9 - 14.3.06 - Jean II
- * o Change length in ESSID and NICK to strlen() instead of strlen()+1
- * o Make standard_ioctl_num and standard_event_num unsigned
- * o Remove (struct net_device *)->get_wireless_stats()
- *
- * v10 - 16.3.07 - Jean II
- * o Prevent leaking of kernel space in stream on 64 bits.
- */
-
-/***************************** INCLUDES *****************************/
-
-#include <linux/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 <net/wext.h>
-
-#include <asm/uaccess.h> /* copy_to_user() */
-
-/************************* 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 = ARRAY_SIZE(standard_ioctl);
-
-/*
- * 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 = ARRAY_SIZE(standard_event);
-
-/* 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 */
-};
-
-/* Size (in bytes) of various events, as packed */
-static const int event_type_pk_size[] = {
- IW_EV_LCP_PK_LEN, /* IW_HEADER_TYPE_NULL */
- 0,
- IW_EV_CHAR_PK_LEN, /* IW_HEADER_TYPE_CHAR */
- 0,
- IW_EV_UINT_PK_LEN, /* IW_HEADER_TYPE_UINT */
- IW_EV_FREQ_PK_LEN, /* IW_HEADER_TYPE_FREQ */
- IW_EV_ADDR_PK_LEN, /* IW_HEADER_TYPE_ADDR */
- 0,
- IW_EV_POINT_PK_LEN, /* Without variable payload */
- IW_EV_PARAM_PK_LEN, /* IW_HEADER_TYPE_PARAM */
- IW_EV_QUAL_PK_LEN, /* IW_HEADER_TYPE_QUAL */
-};
-
-/************************ COMMON SUBROUTINES ************************/
-/*
- * Stuff that may be used in various place or doesn't fit in one
- * of the section below.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Return the driver handler associated with a specific Wireless Extension.
- */
-static 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 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 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 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) {
- /* 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 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 const 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 const struct file_operations wireless_seq_fops = {
- .owner = THIS_MODULE,
- .open = wireless_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-int __init wext_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]);
-
- /* 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;
- 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... */
- }
- }
-
- /* 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;
- }
- }
-
- /* 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;
- }
-
- /* 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);
- }
-
- /* 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 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;
- }
-
- /* 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 if (iwr->u.data.pointer == NULL)
- return -EFAULT;
-
- /* 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;
- }
- }
-
- /* 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;
- }
-
- /* 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.
- * Check the type of IOCTL and call the appropriate wrapper...
- */
-static 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) */
- if (cmd == SIOCGIWSTATS)
- return ioctl_standard_call(dev, ifr, cmd,
- &iw_handler_get_iwstats);
-
- if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
- return ioctl_standard_call(dev, ifr, cmd,
- &iw_handler_get_private);
-
- /* Basic check */
- if (!netif_device_present(dev))
- return -ENODEV;
-
- /* New driver API : try to find the handler */
- handler = get_handler(dev, cmd);
- if (handler) {
- /* 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;
-}
-
-/* entry point from dev ioctl */
-int wext_handle_ioctl(struct ifreq *ifr, unsigned int cmd,
- void __user *arg)
-{
- int ret;
-
- /* 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)
- && !capable(CAP_NET_ADMIN))
- return -EPERM;
-
- dev_load(ifr->ifr_name);
- rtnl_lock();
- ret = wireless_process_ioctl(ifr, cmd);
- rtnl_unlock();
- if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct ifreq)))
- return -EFAULT;
- return ret;
-}
-
-/************************* EVENT PROCESSING *************************/
-/*
- * Process events generated by the wireless layer or the driver.
- * Most often, the event will be propagated through rtnetlink
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Locking...
- * ----------
- *
- * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
- * the locking issue in here and implementing this code !
- *
- * The issue : wireless_send_event() is often called in interrupt context,
- * while the Netlink layer can never be called in interrupt context.
- * The fully formed RtNetlink events are queued, and then a tasklet is run
- * to feed those to Netlink.
- * The skb_queue is interrupt safe, and its lock is not held while calling
- * Netlink, so there is no possibility of dealock.
- * Jean II
- */
-
-static struct sk_buff_head wireless_nlevent_queue;
-
-static int __init wireless_nlevent_init(void)
-{
- skb_queue_head_init(&wireless_nlevent_queue);
- return 0;
-}
-
-subsys_initcall(wireless_nlevent_init);
-
-static void wireless_nlevent_process(unsigned long data)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&wireless_nlevent_queue)))
- 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 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_pointer(skb);
-
- 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_pointer(skb) - b;
- return skb->len;
-
-nlmsg_failure:
-rtattr_failure:
- nlmsg_trim(skb, b);
- 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 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)
- 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 ;-) */
-}
-EXPORT_SYMBOL(wireless_send_event);
-
-/********************** 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);
- }
-
- /* Make sure above is updated before re-enabling */
- smp_wmb();
-
- /* Enable addresses */
- spydata->spy_number = wrqu->data.length;
-
- return 0;
-}
-EXPORT_SYMBOL(iw_handler_set_spy);
-
-/*------------------------------------------------------------------*/
-/*
- * 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;
-}
-EXPORT_SYMBOL(iw_handler_get_spy);
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : set spy threshold
- */
-int iw_handler_set_thrspy(struct net_device * dev,
- struct iw_request_info *info,
- union iwreq_data * wrqu,
- char * extra)
-{
- struct iw_spy_data * spydata = get_spydata(dev);
- struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
-
- /* Make sure driver is not buggy or using the old API */
- if (!spydata)
- return -EOPNOTSUPP;
-
- /* Just do it */
- memcpy(&(spydata->spy_thr_low), &(threshold->low),
- 2 * sizeof(struct iw_quality));
-
- /* Clear flag */
- memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
-
- return 0;
-}
-EXPORT_SYMBOL(iw_handler_set_thrspy);
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : get spy threshold
- */
-int iw_handler_get_thrspy(struct net_device * dev,
- struct iw_request_info *info,
- union iwreq_data * wrqu,
- char * extra)
-{
- struct iw_spy_data * spydata = get_spydata(dev);
- struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
-
- /* Make sure driver is not buggy or using the old API */
- if (!spydata)
- return -EOPNOTSUPP;
-
- /* Just do it */
- memcpy(&(threshold->low), &(spydata->spy_thr_low),
- 2 * sizeof(struct iw_quality));
-
- return 0;
-}
-EXPORT_SYMBOL(iw_handler_get_thrspy);
-
-/*------------------------------------------------------------------*/
-/*
- * Prepare and send a Spy Threshold event
- */
-static void iw_send_thrspy_event(struct net_device * dev,
- struct iw_spy_data * spydata,
- unsigned char * address,
- struct iw_quality * wstats)
-{
- union iwreq_data wrqu;
- struct iw_thrspy threshold;
-
- /* Init */
- wrqu.data.length = 1;
- wrqu.data.flags = 0;
- /* Copy address */
- memcpy(threshold.addr.sa_data, address, ETH_ALEN);
- threshold.addr.sa_family = ARPHRD_ETHER;
- /* Copy stats */
- memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
- /* Copy also thresholds */
- memcpy(&(threshold.low), &(spydata->spy_thr_low),
- 2 * sizeof(struct iw_quality));
-
- /* Send event to user space */
- wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Call for the driver to update the spy data.
- * For now, the spy data is a simple array. As the size of the array is
- * small, this is good enough. If we wanted to support larger number of
- * spy addresses, we should use something more efficient...
- */
-void wireless_spy_update(struct net_device * dev,
- unsigned char * address,
- struct iw_quality * wstats)
-{
- struct iw_spy_data * spydata = get_spydata(dev);
- int i;
- int match = -1;
-
- /* Make sure driver is not buggy or using the old API */
- if (!spydata)
- return;
-
- /* Update all records that match */
- for (i = 0; i < spydata->spy_number; i++)
- if (!compare_ether_addr(address, spydata->spy_address[i])) {
- memcpy(&(spydata->spy_stat[i]), wstats,
- sizeof(struct iw_quality));
- match = i;
- }
-
- /* Generate an event if we cross the spy threshold.
- * To avoid event storms, we have a simple hysteresis : we generate
- * event only when we go under the low threshold or above the
- * high threshold. */
- if (match >= 0) {
- if (spydata->spy_thr_under[match]) {
- if (wstats->level > spydata->spy_thr_high.level) {
- spydata->spy_thr_under[match] = 0;
- iw_send_thrspy_event(dev, spydata,
- address, wstats);
- }
- } else {
- if (wstats->level < spydata->spy_thr_low.level) {
- spydata->spy_thr_under[match] = 1;
- iw_send_thrspy_event(dev, spydata,
- address, wstats);
- }
- }
- }
-}
-EXPORT_SYMBOL(wireless_spy_update);