aboutsummaryrefslogtreecommitdiff
path: root/net/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/Makefile2
-rw-r--r--net/wireless/core.c27
-rw-r--r--net/wireless/core.h15
-rw-r--r--net/wireless/ibss.c369
-rw-r--r--net/wireless/mlme.c50
-rw-r--r--net/wireless/nl80211.c639
-rw-r--r--net/wireless/nl80211.h32
-rw-r--r--net/wireless/reg.c35
-rw-r--r--net/wireless/scan.c53
-rw-r--r--net/wireless/util.c6
-rw-r--r--net/wireless/wext-compat.c236
11 files changed, 1336 insertions, 128 deletions
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 6d1e7b27b75..14ea01c4a10 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
-cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o
+cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o
cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/wireless/core.c b/net/wireless/core.c
index d1f556535f6..2006a4ee60e 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -14,7 +14,6 @@
#include <linux/device.h>
#include <net/genetlink.h>
#include <net/cfg80211.h>
-#include <net/wireless.h>
#include "nl80211.h"
#include "core.h"
#include "sysfs.h"
@@ -274,6 +273,16 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
drv->wiphy.dev.class = &ieee80211_class;
drv->wiphy.dev.platform_data = drv;
+ /*
+ * Initialize wiphy parameters to IEEE 802.11 MIB default values.
+ * Fragmentation and RTS threshold are disabled by default with the
+ * special -1 value.
+ */
+ drv->wiphy.retry_short = 7;
+ drv->wiphy.retry_long = 4;
+ drv->wiphy.frag_threshold = (u32) -1;
+ drv->wiphy.rts_threshold = (u32) -1;
+
return &drv->wiphy;
}
EXPORT_SYMBOL(wiphy_new);
@@ -450,6 +459,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
dev->ieee80211_ptr->netdev = dev;
mutex_unlock(&rdev->devlist_mtx);
break;
+ case NETDEV_GOING_DOWN:
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
+ break;
+ if (!dev->ieee80211_ptr->ssid_len)
+ break;
+ cfg80211_leave_ibss(rdev, dev, true);
+ break;
+ case NETDEV_UP:
+#ifdef CONFIG_WIRELESS_EXT
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
+ break;
+ if (!dev->ieee80211_ptr->wext.ssid_len)
+ break;
+ cfg80211_join_ibss(rdev, dev, &dev->ieee80211_ptr->wext);
+ break;
+#endif
case NETDEV_UNREGISTER:
mutex_lock(&rdev->devlist_mtx);
if (!list_empty(&dev->ieee80211_ptr->list)) {
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 0a592e4295f..3e49d339931 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -10,9 +10,7 @@
#include <linux/netdevice.h>
#include <linux/kref.h>
#include <linux/rbtree.h>
-#include <linux/mutex.h>
#include <net/genetlink.h>
-#include <net/wireless.h>
#include <net/cfg80211.h>
#include "reg.h"
@@ -74,10 +72,7 @@ bool wiphy_idx_valid(int wiphy_idx)
extern struct mutex cfg80211_mutex;
extern struct list_head cfg80211_drv_list;
-static inline void assert_cfg80211_lock(void)
-{
- WARN_ON(!mutex_is_locked(&cfg80211_mutex));
-}
+#define assert_cfg80211_lock() WARN_ON(!mutex_is_locked(&cfg80211_mutex))
/*
* You can use this to mark a wiphy_idx as not having an associated wiphy.
@@ -148,4 +143,12 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev);
void cfg80211_bss_age(struct cfg80211_registered_device *dev,
unsigned long age_secs);
+/* IBSS */
+int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *params);
+void cfg80211_clear_ibss(struct net_device *dev, bool nowext);
+int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool nowext);
+
#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
new file mode 100644
index 00000000000..3c38afaed28
--- /dev/null
+++ b/net/wireless/ibss.c
@@ -0,0 +1,369 @@
+/*
+ * Some IBSS support code for cfg80211.
+ *
+ * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <net/cfg80211.h>
+#include "nl80211.h"
+
+
+void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_bss *bss;
+#ifdef CONFIG_WIRELESS_EXT
+ union iwreq_data wrqu;
+#endif
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return;
+
+ if (WARN_ON(!wdev->ssid_len))
+ return;
+
+ if (memcmp(bssid, wdev->bssid, ETH_ALEN) == 0)
+ return;
+
+ bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
+ wdev->ssid, wdev->ssid_len,
+ WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
+
+ if (WARN_ON(!bss))
+ return;
+
+ if (wdev->current_bss) {
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(wdev->current_bss);
+ }
+
+ cfg80211_hold_bss(bss);
+ wdev->current_bss = bss;
+ memcpy(wdev->bssid, bssid, ETH_ALEN);
+
+ nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp);
+#ifdef CONFIG_WIRELESS_EXT
+ memset(&wrqu, 0, sizeof(wrqu));
+ memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
+ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+#endif
+}
+EXPORT_SYMBOL(cfg80211_ibss_joined);
+
+int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *params)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ if (wdev->ssid_len)
+ return -EALREADY;
+
+#ifdef CONFIG_WIRELESS_EXT
+ wdev->wext.channel = params->channel;
+#endif
+ err = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
+
+ if (err)
+ return err;
+
+ memcpy(wdev->ssid, params->ssid, params->ssid_len);
+ wdev->ssid_len = params->ssid_len;
+
+ return 0;
+}
+
+void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (wdev->current_bss) {
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(wdev->current_bss);
+ }
+
+ wdev->current_bss = NULL;
+ wdev->ssid_len = 0;
+ memset(wdev->bssid, 0, ETH_ALEN);
+#ifdef CONFIG_WIRELESS_EXT
+ if (!nowext)
+ wdev->wext.ssid_len = 0;
+#endif
+}
+
+int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool nowext)
+{
+ int err;
+
+ err = rdev->ops->leave_ibss(&rdev->wiphy, dev);
+
+ if (err)
+ return err;
+
+ cfg80211_clear_ibss(dev, nowext);
+
+ return 0;
+}
+
+#ifdef CONFIG_WIRELESS_EXT
+static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ enum ieee80211_band band;
+ int i;
+
+ if (!wdev->wext.beacon_interval)
+ wdev->wext.beacon_interval = 100;
+
+ /* try to find an IBSS channel if none requested ... */
+ if (!wdev->wext.channel) {
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+
+ sband = rdev->wiphy.bands[band];
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ chan = &sband->channels[i];
+ if (chan->flags & IEEE80211_CHAN_NO_IBSS)
+ continue;
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+ wdev->wext.channel = chan;
+ break;
+ }
+
+ if (wdev->wext.channel)
+ break;
+ }
+
+ if (!wdev->wext.channel)
+ return -EINVAL;
+ }
+
+ /* don't join -- SSID is not there */
+ if (!wdev->wext.ssid_len)
+ return 0;
+
+ if (!netif_running(wdev->netdev))
+ return 0;
+
+ return cfg80211_join_ibss(wiphy_to_dev(wdev->wiphy),
+ wdev->netdev, &wdev->wext);
+}
+
+int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct ieee80211_channel *chan;
+ int err;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
+ return -EOPNOTSUPP;
+
+ chan = cfg80211_wext_freq(wdev->wiphy, freq);
+ if (chan && IS_ERR(chan))
+ return PTR_ERR(chan);
+
+ if (chan &&
+ (chan->flags & IEEE80211_CHAN_NO_IBSS ||
+ chan->flags & IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+
+ if (wdev->wext.channel == chan)
+ return 0;
+
+ if (wdev->ssid_len) {
+ err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
+ dev, true);
+ if (err)
+ return err;
+ }
+
+ if (chan) {
+ wdev->wext.channel = chan;
+ wdev->wext.channel_fixed = true;
+ } else {
+ /* cfg80211_ibss_wext_join will pick one if needed */
+ wdev->wext.channel_fixed = false;
+ }
+
+ return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq);
+
+int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct ieee80211_channel *chan = NULL;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ if (wdev->current_bss)
+ chan = wdev->current_bss->channel;
+ else if (wdev->wext.channel)
+ chan = wdev->wext.channel;
+
+ if (chan) {
+ freq->m = chan->center_freq;
+ freq->e = 6;
+ return 0;
+ }
+
+ /* no channel if not joining */
+ return -EINVAL;
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq);
+
+int cfg80211_ibss_wext_siwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ size_t len = data->length;
+ int err;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
+ return -EOPNOTSUPP;
+
+ if (wdev->ssid_len) {
+ err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
+ dev, true);
+ if (err)
+ return err;
+ }
+
+ /* iwconfig uses nul termination in SSID.. */
+ if (len > 0 && ssid[len - 1] == '\0')
+ len--;
+
+ wdev->wext.ssid = wdev->ssid;
+ memcpy(wdev->wext.ssid, ssid, len);
+ wdev->wext.ssid_len = len;
+
+ return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid);
+
+int cfg80211_ibss_wext_giwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ data->flags = 0;
+
+ if (wdev->ssid_len) {
+ data->flags = 1;
+ data->length = wdev->ssid_len;
+ memcpy(ssid, wdev->ssid, data->length);
+ } else if (wdev->wext.ssid && wdev->wext.ssid_len) {
+ data->flags = 1;
+ data->length = wdev->wext.ssid_len;
+ memcpy(ssid, wdev->wext.ssid, data->length);
+ }
+
+ return 0;
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid);
+
+int cfg80211_ibss_wext_siwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ u8 *bssid = ap_addr->sa_data;
+ int err;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
+ return -EOPNOTSUPP;
+
+ if (ap_addr->sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ /* automatic mode */
+ if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
+ bssid = NULL;
+
+ /* both automatic */
+ if (!bssid && !wdev->wext.bssid)
+ return 0;
+
+ /* fixed already - and no change */
+ if (wdev->wext.bssid && bssid &&
+ compare_ether_addr(bssid, wdev->wext.bssid) == 0)
+ return 0;
+
+ if (wdev->ssid_len) {
+ err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
+ dev, true);
+ if (err)
+ return err;
+ }
+
+ if (bssid) {
+ memcpy(wdev->wext_bssid, bssid, ETH_ALEN);
+ wdev->wext.bssid = wdev->wext_bssid;
+ } else
+ wdev->wext.bssid = NULL;
+
+ return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap);
+
+int cfg80211_ibss_wext_giwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ /* call only for ibss! */
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
+ return -EINVAL;
+
+ ap_addr->sa_family = ARPHRD_ETHER;
+
+ if (wdev->wext.bssid) {
+ memcpy(ap_addr->sa_data, wdev->wext.bssid, ETH_ALEN);
+ return 0;
+ }
+
+ memcpy(ap_addr->sa_data, wdev->bssid, ETH_ALEN);
+ return 0;
+}
+/* temporary symbol - mark GPL - in the future the handler won't be */
+EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwap);
+#endif
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index bec5721b6f9..42184361a10 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -28,19 +28,55 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len)
}
EXPORT_SYMBOL(cfg80211_send_rx_assoc);
-void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf, size_t len)
+void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
{
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- nl80211_send_rx_deauth(rdev, dev, buf, len);
+ nl80211_send_deauth(rdev, dev, buf, len);
}
-EXPORT_SYMBOL(cfg80211_send_rx_deauth);
+EXPORT_SYMBOL(cfg80211_send_deauth);
-void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf,
- size_t len)
+void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
{
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- nl80211_send_rx_disassoc(rdev, dev, buf, len);
+ nl80211_send_disassoc(rdev, dev, buf, len);
}
-EXPORT_SYMBOL(cfg80211_send_rx_disassoc);
+EXPORT_SYMBOL(cfg80211_send_disassoc);
+
+static void cfg80211_wext_disconnected(struct net_device *dev)
+{
+#ifdef CONFIG_WIRELESS_EXT
+ union iwreq_data wrqu;
+ memset(&wrqu, 0, sizeof(wrqu));
+ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+#endif
+}
+
+void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ nl80211_send_auth_timeout(rdev, dev, addr);
+ cfg80211_wext_disconnected(dev);
+}
+EXPORT_SYMBOL(cfg80211_send_auth_timeout);
+
+void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ nl80211_send_assoc_timeout(rdev, dev, addr);
+ cfg80211_wext_disconnected(dev);
+}
+EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
+
+void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
+ enum nl80211_key_type key_type, int key_id,
+ const u8 *tsc)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc);
+}
+EXPORT_SYMBOL(cfg80211_michael_mic_failure);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 2456e4ee445..b1fc98225fd 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -61,6 +61,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
+ [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
+ [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
@@ -116,8 +120,40 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
.len = IEEE80211_MAX_SSID_LEN },
[NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
[NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
+ [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
+ [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
};
+/* IE validation */
+static bool is_valid_ie_attr(const struct nlattr *attr)
+{
+ const u8 *pos;
+ int len;
+
+ if (!attr)
+ return true;
+
+ pos = nla_data(attr);
+ len = nla_len(attr);
+
+ while (len) {
+ u8 elemlen;
+
+ if (len < 2)
+ return false;
+ len -= 2;
+
+ elemlen = pos[1];
+ if (elemlen > len)
+ return false;
+
+ len -= elemlen;
+ pos += 2 + elemlen;
+ }
+
+ return true;
+}
+
/* message building helper */
static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
int flags, u8 cmd)
@@ -126,6 +162,30 @@ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
}
+static int nl80211_msg_put_channel(struct sk_buff *msg,
+ struct ieee80211_channel *chan)
+{
+ NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
+ chan->center_freq);
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
+ if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+ NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
+ if (chan->flags & IEEE80211_CHAN_NO_IBSS)
+ NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
+ if (chan->flags & IEEE80211_CHAN_RADAR)
+ NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
+
+ NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+ DBM_TO_MBM(chan->max_power));
+
+ return 0;
+
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
/* netlink command implementations */
static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
@@ -149,8 +209,24 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
+
+ NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
+ dev->wiphy.retry_short);
+ NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
+ dev->wiphy.retry_long);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+ dev->wiphy.frag_threshold);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+ dev->wiphy.rts_threshold);
+
NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
dev->wiphy.max_scan_ssids);
+ NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
+ dev->wiphy.max_scan_ie_len);
+
+ NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
+ sizeof(u32) * dev->wiphy.n_cipher_suites,
+ dev->wiphy.cipher_suites);
nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
if (!nl_modes)
@@ -202,20 +278,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
goto nla_put_failure;
chan = &dev->wiphy.bands[band]->channels[i];
- NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
- chan->center_freq);
-
- if (chan->flags & IEEE80211_CHAN_DISABLED)
- NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
- if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
- NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
- if (chan->flags & IEEE80211_CHAN_NO_IBSS)
- NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
- if (chan->flags & IEEE80211_CHAN_RADAR)
- NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
- NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
- DBM_TO_MBM(chan->max_power));
+ if (nl80211_msg_put_channel(msg, chan))
+ goto nla_put_failure;
nla_nest_end(msg, nl_freq);
}
@@ -273,6 +338,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
CMD(assoc, ASSOCIATE);
CMD(deauth, DEAUTHENTICATE);
CMD(disassoc, DISASSOCIATE);
+ CMD(join_ibss, JOIN_IBSS);
#undef CMD
nla_nest_end(msg, nl_cmds);
@@ -365,6 +431,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev;
int result = 0, rem_txq_params = 0;
struct nlattr *nl_txq_params;
+ u32 changed;
+ u8 retry_short = 0, retry_long = 0;
+ u32 frag_threshold = 0, rts_threshold = 0;
rtnl_lock();
@@ -479,6 +548,84 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
goto bad_res;
}
+ changed = 0;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
+ retry_short = nla_get_u8(
+ info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
+ if (retry_short == 0) {
+ result = -EINVAL;
+ goto bad_res;
+ }
+ changed |= WIPHY_PARAM_RETRY_SHORT;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
+ retry_long = nla_get_u8(
+ info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
+ if (retry_long == 0) {
+ result = -EINVAL;
+ goto bad_res;
+ }
+ changed |= WIPHY_PARAM_RETRY_LONG;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
+ frag_threshold = nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
+ if (frag_threshold < 256) {
+ result = -EINVAL;
+ goto bad_res;
+ }
+ if (frag_threshold != (u32) -1) {
+ /*
+ * Fragments (apart from the last one) are required to
+ * have even length. Make the fragmentation code
+ * simpler by stripping LSB should someone try to use
+ * odd threshold value.
+ */
+ frag_threshold &= ~0x1;
+ }
+ changed |= WIPHY_PARAM_FRAG_THRESHOLD;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
+ rts_threshold = nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
+ changed |= WIPHY_PARAM_RTS_THRESHOLD;
+ }
+
+ if (changed) {
+ u8 old_retry_short, old_retry_long;
+ u32 old_frag_threshold, old_rts_threshold;
+
+ if (!rdev->ops->set_wiphy_params) {
+ result = -EOPNOTSUPP;
+ goto bad_res;
+ }
+
+ old_retry_short = rdev->wiphy.retry_short;
+ old_retry_long = rdev->wiphy.retry_long;
+ old_frag_threshold = rdev->wiphy.frag_threshold;
+ old_rts_threshold = rdev->wiphy.rts_threshold;
+
+ if (changed & WIPHY_PARAM_RETRY_SHORT)
+ rdev->wiphy.retry_short = retry_short;
+ if (changed & WIPHY_PARAM_RETRY_LONG)
+ rdev->wiphy.retry_long = retry_long;
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
+ rdev->wiphy.frag_threshold = frag_threshold;
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD)
+ rdev->wiphy.rts_threshold = rts_threshold;
+
+ result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
+ if (result) {
+ rdev->wiphy.retry_short = old_retry_short;
+ rdev->wiphy.retry_long = old_retry_long;
+ rdev->wiphy.frag_threshold = old_frag_threshold;
+ rdev->wiphy.rts_threshold = old_rts_threshold;
+ }
+ }
bad_res:
mutex_unlock(&rdev->mtx);
@@ -489,6 +636,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+ struct cfg80211_registered_device *rdev,
struct net_device *dev)
{
void *hdr;
@@ -498,6 +646,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
return -1;
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
return genlmsg_end(msg, hdr);
@@ -532,7 +681,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
}
if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
- wdev->netdev) < 0) {
+ dev, wdev->netdev) < 0) {
mutex_unlock(&dev->devlist_mtx);
goto out;
}
@@ -566,7 +715,8 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
if (!msg)
goto out_err;
- if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
+ if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
+ dev, netdev) < 0)
goto out_free;
dev_put(netdev);
@@ -616,7 +766,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *drv;
struct vif_params params;
int err, ifindex;
- enum nl80211_iftype type;
+ enum nl80211_iftype otype, ntype;
struct net_device *dev;
u32 _flags, *flags = NULL;
bool change = false;
@@ -630,30 +780,27 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
goto unlock_rtnl;
ifindex = dev->ifindex;
- type = dev->ieee80211_ptr->iftype;
+ otype = ntype = dev->ieee80211_ptr->iftype;
dev_put(dev);
if (info->attrs[NL80211_ATTR_IFTYPE]) {
- enum nl80211_iftype ntype;
-
ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
- if (type != ntype)
+ if (otype != ntype)
change = true;
- type = ntype;
- if (type > NL80211_IFTYPE_MAX) {
+ if (ntype > NL80211_IFTYPE_MAX) {
err = -EINVAL;
goto unlock;
}
}
if (!drv->ops->change_virtual_intf ||
- !(drv->wiphy.interface_modes & (1 << type))) {
+ !(drv->wiphy.interface_modes & (1 << ntype))) {
err = -EOPNOTSUPP;
goto unlock;
}
if (info->attrs[NL80211_ATTR_MESH_ID]) {
- if (type != NL80211_IFTYPE_MESH_POINT) {
+ if (ntype != NL80211_IFTYPE_MESH_POINT) {
err = -EINVAL;
goto unlock;
}
@@ -663,7 +810,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
}
if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
- if (type != NL80211_IFTYPE_MONITOR) {
+ if (ntype != NL80211_IFTYPE_MONITOR) {
err = -EINVAL;
goto unlock;
}
@@ -678,12 +825,17 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
if (change)
err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
- type, flags, &params);
+ ntype, flags, &params);
else
err = 0;
dev = __dev_get_by_index(&init_net, ifindex);
- WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type));
+ WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != ntype));
+
+ if (dev && !err && (ntype != otype)) {
+ if (otype == NL80211_IFTYPE_ADHOC)
+ cfg80211_clear_ibss(dev, false);
+ }
unlock:
cfg80211_put_dev(drv);
@@ -934,7 +1086,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *drv;
- int err;
+ int err, i;
struct net_device *dev;
struct key_params params;
u8 key_idx = 0;
@@ -1003,6 +1155,14 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
if (err)
goto unlock_rtnl;
+ for (i = 0; i < drv->wiphy.n_cipher_suites; i++)
+ if (params.cipher == drv->wiphy.cipher_suites[i])
+ break;
+ if (i == drv->wiphy.n_cipher_suites) {
+ err = -EINVAL;
+ goto out;
+ }
+
if (!drv->ops->add_key) {
err = -EOPNOTSUPP;
goto out;
@@ -1069,6 +1229,9 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
struct beacon_parameters params;
int haveinfo = 0;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2442,6 +2605,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
enum ieee80211_band band;
size_t ie_len;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2492,6 +2658,11 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
else
ie_len = 0;
+ if (ie_len > wiphy->max_scan_ie_len) {
+ err = -EINVAL;
+ goto out;
+ }
+
request = kzalloc(sizeof(*request)
+ sizeof(*ssid) * n_ssids
+ sizeof(channel) * n_channels
@@ -2554,7 +2725,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_IE]) {
request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
- memcpy(request->ie, nla_data(info->attrs[NL80211_ATTR_IE]),
+ memcpy((void *)request->ie,
+ nla_data(info->attrs[NL80211_ATTR_IE]),
request->ie_len);
}
@@ -2710,6 +2882,15 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
struct wiphy *wiphy;
int err;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2731,11 +2912,6 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- if (!info->attrs[NL80211_ATTR_MAC]) {
- err = -EINVAL;
- goto out;
- }
-
wiphy = &drv->wiphy;
memset(&req, 0, sizeof(req));
@@ -2761,13 +2937,10 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
- if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
- req.auth_type =
- nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
- if (!nl80211_valid_auth_type(req.auth_type)) {
- err = -EINVAL;
- goto out;
- }
+ req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+ if (!nl80211_valid_auth_type(req.auth_type)) {
+ err = -EINVAL;
+ goto out;
}
err = drv->ops->auth(&drv->wiphy, dev, &req);
@@ -2788,6 +2961,13 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
struct wiphy *wiphy;
int err;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_SSID])
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2809,12 +2989,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- if (!info->attrs[NL80211_ATTR_MAC] ||
- !info->attrs[NL80211_ATTR_SSID]) {
- err = -EINVAL;
- goto out;
- }
-
wiphy = &drv->wiphy;
memset(&req, 0, sizeof(req));
@@ -2856,6 +3030,15 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
struct wiphy *wiphy;
int err;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_REASON_CODE])
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2877,24 +3060,16 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- if (!info->attrs[NL80211_ATTR_MAC]) {
- err = -EINVAL;
- goto out;
- }
-
wiphy = &drv->wiphy;
memset(&req, 0, sizeof(req));
req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
- if (info->attrs[NL80211_ATTR_REASON_CODE]) {
- req.reason_code =
- nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
- if (req.reason_code == 0) {
- /* Reason Code 0 is reserved */
- err = -EINVAL;
- goto out;
- }
+ req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (req.reason_code == 0) {
+ /* Reason Code 0 is reserved */
+ err = -EINVAL;
+ goto out;
}
if (info->attrs[NL80211_ATTR_IE]) {
@@ -2920,6 +3095,15 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
struct wiphy *wiphy;
int err;
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_REASON_CODE])
+ return -EINVAL;
+
rtnl_lock();
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2941,24 +3125,16 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- if (!info->attrs[NL80211_ATTR_MAC]) {
- err = -EINVAL;
- goto out;
- }
-
wiphy = &drv->wiphy;
memset(&req, 0, sizeof(req));
req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
- if (info->attrs[NL80211_ATTR_REASON_CODE]) {
- req.reason_code =
- nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
- if (req.reason_code == 0) {
- /* Reason Code 0 is reserved */
- err = -EINVAL;
- goto out;
- }
+ req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (req.reason_code == 0) {
+ /* Reason Code 0 is reserved */
+ err = -EINVAL;
+ goto out;
}
if (info->attrs[NL80211_ATTR_IE]) {
@@ -2976,6 +3152,124 @@ unlock_rtnl:
return err;
}
+static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct cfg80211_ibss_params ibss;
+ struct wiphy *wiphy;
+ int err;
+
+ memset(&ibss, 0, sizeof(ibss));
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
+ !info->attrs[NL80211_ATTR_SSID] ||
+ !nla_len(info->attrs[NL80211_ATTR_SSID]))
+ return -EINVAL;
+
+ ibss.beacon_interval = 100;
+
+ if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
+ ibss.beacon_interval =
+ nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
+ if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
+ return -EINVAL;
+ }
+
+ rtnl_lock();
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto unlock_rtnl;
+
+ if (!drv->ops->join_ibss) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ wiphy = &drv->wiphy;
+
+ if (info->attrs[NL80211_ATTR_MAC])
+ ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+ ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ ibss.channel = ieee80211_get_channel(wiphy,
+ nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+ if (!ibss.channel ||
+ ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
+ ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
+
+ err = cfg80211_join_ibss(drv, dev, &ibss);
+
+out:
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+unlock_rtnl:
+ rtnl_unlock();
+ return err;
+}
+
+static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ int err;
+
+ rtnl_lock();
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto unlock_rtnl;
+
+ if (!drv->ops->leave_ibss) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ err = cfg80211_leave_ibss(drv, dev, false);
+
+out:
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+unlock_rtnl:
+ rtnl_unlock();
+ return err;
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -3177,6 +3471,18 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NL80211_CMD_JOIN_IBSS,
+ .doit = nl80211_join_ibss,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_LEAVE_IBSS,
+ .doit = nl80211_leave_ibss,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
.name = "mlme",
@@ -3375,22 +3681,197 @@ void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
}
-void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *buf,
- size_t len)
+void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf, size_t len)
{
nl80211_send_mlme_event(rdev, netdev, buf, len,
NL80211_CMD_DEAUTHENTICATE);
}
-void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, const u8 *buf,
- size_t len)
+void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf,
+ size_t len)
{
nl80211_send_mlme_event(rdev, netdev, buf, len,
NL80211_CMD_DISASSOCIATE);
}
+void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, int cmd,
+ const u8 *addr)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *addr)
+{
+ nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
+ addr);
+}
+
+void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *addr)
+{
+ nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, addr);
+}
+
+void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *bssid,
+ gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *addr,
+ enum nl80211_key_type key_type, int key_id,
+ const u8 *tsc)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ if (addr)
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
+ NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
+ if (tsc)
+ NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
+ struct ieee80211_channel *channel_before,
+ struct ieee80211_channel *channel_after)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ struct nlattr *nl_freq;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ /*
+ * Since we are applying the beacon hint to a wiphy we know its
+ * wiphy_idx is valid
+ */
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
+
+ /* Before */
+ nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
+ if (!nl_freq)
+ goto nla_put_failure;
+ if (nl80211_msg_put_channel(msg, channel_before))
+ goto nla_put_failure;
+ nla_nest_end(msg, nl_freq);
+
+ /* After */
+ nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
+ if (!nl_freq)
+ goto nla_put_failure;
+ if (nl80211_msg_put_channel(msg, channel_after))
+ goto nla_put_failure;
+ nla_nest_end(msg, nl_freq);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC);
+
+ return;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
/* initialisation/exit functions */
int nl80211_init(void)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index b77af4ab80b..5c12ad13499 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -17,11 +17,31 @@ extern void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
extern void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *buf, size_t len);
-extern void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev,
- struct net_device *netdev,
- const u8 *buf, size_t len);
-extern void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev,
- struct net_device *netdev,
- const u8 *buf, size_t len);
+extern void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len);
+extern void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len);
+extern void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *addr);
+extern void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *addr);
+extern void
+nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *addr,
+ enum nl80211_key_type key_type,
+ int key_id, const u8 *tsc);
+
+extern void
+nl80211_send_beacon_hint_event(struct wiphy *wiphy,
+ struct ieee80211_channel *channel_before,
+ struct ieee80211_channel *channel_after);
+
+void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *bssid,
+ gfp_t gfp);
#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 08265ca1578..139d13fdc94 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -37,7 +37,6 @@
#include <linux/random.h>
#include <linux/nl80211.h>
#include <linux/platform_device.h>
-#include <net/wireless.h>
#include <net/cfg80211.h>
#include "core.h"
#include "reg.h"
@@ -1050,18 +1049,10 @@ static void handle_reg_beacon(struct wiphy *wiphy,
unsigned int chan_idx,
struct reg_beacon *reg_beacon)
{
-#ifdef CONFIG_CFG80211_REG_DEBUG
-#define REG_DEBUG_BEACON_FLAG(desc) \
- printk(KERN_DEBUG "cfg80211: Enabling " desc " on " \
- "frequency: %d MHz (Ch %d) on %s\n", \
- reg_beacon->chan.center_freq, \
- ieee80211_frequency_to_channel(reg_beacon->chan.center_freq), \
- wiphy_name(wiphy));
-#else
-#define REG_DEBUG_BEACON_FLAG(desc) do {} while (0)
-#endif
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
+ bool channel_changed = false;
+ struct ieee80211_channel chan_before;
assert_cfg80211_lock();
@@ -1071,18 +1062,28 @@ static void handle_reg_beacon(struct wiphy *wiphy,
if (likely(chan->center_freq != reg_beacon->chan.center_freq))
return;
- if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
+ if (chan->beacon_found)
+ return;
+
+ chan->beacon_found = true;
+
+ chan_before.center_freq = chan->center_freq;
+ chan_before.flags = chan->flags;
+
+ if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
+ !(chan->orig_flags & IEEE80211_CHAN_PASSIVE_SCAN)) {
chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
- REG_DEBUG_BEACON_FLAG("active scanning");
+ channel_changed = true;
}
- if (chan->flags & IEEE80211_CHAN_NO_IBSS) {
+ if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
+ !(chan->orig_flags & IEEE80211_CHAN_NO_IBSS)) {
chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
- REG_DEBUG_BEACON_FLAG("beaconing");
+ channel_changed = true;
}
- chan->beacon_found = true;
-#undef REG_DEBUG_BEACON_FLAG
+ if (channel_changed)
+ nl80211_send_beacon_hint_event(wiphy, &chan_before, chan);
}
/*
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 1f260c40b6c..dfb53d19fd7 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -415,6 +415,55 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
return found;
}
+struct cfg80211_bss*
+cfg80211_inform_bss(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ const u8 *bssid,
+ u64 timestamp, u16 capability, u16 beacon_interval,
+ const u8 *ie, size_t ielen,
+ s32 signal, gfp_t gfp)
+{
+ struct cfg80211_internal_bss *res;
+ size_t privsz;
+
+ if (WARN_ON(!wiphy))
+ return NULL;
+
+ privsz = wiphy->bss_priv_size;
+
+ if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
+ (signal < 0 || signal > 100)))
+ return NULL;
+
+ res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
+ if (!res)
+ return NULL;
+
+ memcpy(res->pub.bssid, bssid, ETH_ALEN);
+ res->pub.channel = channel;
+ res->pub.signal = signal;
+ res->pub.tsf = timestamp;
+ res->pub.beacon_interval = beacon_interval;
+ res->pub.capability = capability;
+ /* point to after the private area */
+ res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
+ memcpy(res->pub.information_elements, ie, ielen);
+ res->pub.len_information_elements = ielen;
+
+ kref_init(&res->ref);
+
+ res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0);
+ if (!res)
+ return NULL;
+
+ if (res->pub.capability & WLAN_CAPABILITY_ESS)
+ regulatory_hint_found_beacon(wiphy, channel, gfp);
+
+ /* cfg80211_bss_update gives us a referenced result */
+ return &res->pub;
+}
+EXPORT_SYMBOL(cfg80211_inform_bss);
+
struct cfg80211_bss *
cfg80211_inform_bss_frame(struct wiphy *wiphy,
struct ieee80211_channel *channel,
@@ -605,7 +654,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
cfg80211_put_dev(rdev);
return err;
}
-EXPORT_SYMBOL(cfg80211_wext_siwscan);
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
static void ieee80211_scan_add_ies(struct iw_request_info *info,
struct cfg80211_bss *bss,
@@ -914,5 +963,5 @@ int cfg80211_wext_giwscan(struct net_device *dev,
cfg80211_put_dev(rdev);
return res;
}
-EXPORT_SYMBOL(cfg80211_wext_giwscan);
+EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan);
#endif
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 487cdd9bcff..5f7e997195c 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1,10 +1,10 @@
/*
* Wireless utility functions
*
- * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
*/
-#include <net/wireless.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
+#include <net/cfg80211.h>
#include "core.h"
struct ieee80211_rate *
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 0fd1db6e95b..5ef82f2ca88 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -10,8 +10,8 @@
#include <linux/wireless.h>
#include <linux/nl80211.h>
+#include <linux/if_arp.h>
#include <net/iw_handler.h>
-#include <net/wireless.h>
#include <net/cfg80211.h>
#include "core.h"
@@ -57,7 +57,7 @@ int cfg80211_wext_giwname(struct net_device *dev,
return 0;
}
-EXPORT_SYMBOL(cfg80211_wext_giwname);
+EXPORT_SYMBOL_GPL(cfg80211_wext_giwname);
int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
u32 *mode, char *extra)
@@ -108,7 +108,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
return ret;
}
-EXPORT_SYMBOL(cfg80211_wext_siwmode);
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwmode);
int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
u32 *mode, char *extra)
@@ -143,7 +143,7 @@ int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
}
return 0;
}
-EXPORT_SYMBOL(cfg80211_wext_giwmode);
+EXPORT_SYMBOL_GPL(cfg80211_wext_giwmode);
int cfg80211_wext_giwrange(struct net_device *dev,
@@ -206,7 +206,6 @@ int cfg80211_wext_giwrange(struct net_device *dev,
range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
-
for (band = 0; band < IEEE80211_NUM_BANDS; band ++) {
int i;
struct ieee80211_supported_band *sband;
@@ -240,4 +239,229 @@ int cfg80211_wext_giwrange(struct net_device *dev,
return 0;
}
-EXPORT_SYMBOL(cfg80211_wext_giwrange);
+EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange);
+
+int cfg80211_wext_siwmlme(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ struct cfg80211_registered_device *rdev;
+ union {
+ struct cfg80211_disassoc_request disassoc;
+ struct cfg80211_deauth_request deauth;
+ } cmd;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ rdev = wiphy_to_dev(wdev->wiphy);
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION)
+ return -EINVAL;
+
+ if (mlme->addr.sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ if (!rdev->ops->deauth)
+ return -EOPNOTSUPP;
+ cmd.deauth.peer_addr = mlme->addr.sa_data;
+ cmd.deauth.reason_code = mlme->reason_code;
+ return rdev->ops->deauth(wdev->wiphy, dev, &cmd.deauth);
+ case IW_MLME_DISASSOC:
+ if (!rdev->ops->disassoc)
+ return -EOPNOTSUPP;
+ cmd.disassoc.peer_addr = mlme->addr.sa_data;
+ cmd.disassoc.reason_code = mlme->reason_code;
+ return rdev->ops->disassoc(wdev->wiphy, dev, &cmd.disassoc);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme);
+
+
+/**
+ * cfg80211_wext_freq - get wext frequency for non-"auto"
+ * @wiphy: the wiphy
+ * @freq: the wext freq encoding
+ *
+ * Returns a channel, %NULL for auto, or an ERR_PTR for errors!
+ */
+struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy,
+ struct iw_freq *freq)
+{
+ if (freq->e == 0) {
+ if (freq->m < 0)
+ return NULL;
+ else
+ return ieee80211_get_channel(wiphy,
+ ieee80211_channel_to_frequency(freq->m));
+ } else {
+ int i, div = 1000000;
+ for (i = 0; i < freq->e; i++)
+ div /= 10;
+ if (div > 0)
+ return ieee80211_get_channel(wiphy, freq->m / div);
+ else
+ return ERR_PTR(-EINVAL);
+ }
+
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_freq);
+
+int cfg80211_wext_siwrts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ u32 orts = wdev->wiphy->rts_threshold;
+ int err;
+
+ if (rts->disabled || !rts->fixed)
+ wdev->wiphy->rts_threshold = (u32) -1;
+ else if (rts->value < 0)
+ return -EINVAL;
+ else
+ wdev->wiphy->rts_threshold = rts->value;
+
+ err = rdev->ops->set_wiphy_params(wdev->wiphy,
+ WIPHY_PARAM_RTS_THRESHOLD);
+ if (err)
+ wdev->wiphy->rts_threshold = orts;
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwrts);
+
+int cfg80211_wext_giwrts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ rts->value = wdev->wiphy->rts_threshold;
+ rts->disabled = rts->value == (u32) -1;
+ rts->fixed = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_giwrts);
+
+int cfg80211_wext_siwfrag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *frag, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ u32 ofrag = wdev->wiphy->frag_threshold;
+ int err;
+
+ if (frag->disabled || !frag->fixed)
+ wdev->wiphy->frag_threshold = (u32) -1;
+ else if (frag->value < 256)
+ return -EINVAL;
+ else {
+ /* Fragment length must be even, so strip LSB. */
+ wdev->wiphy->frag_threshold = frag->value & ~0x1;
+ }
+
+ err = rdev->ops->set_wiphy_params(wdev->wiphy,
+ WIPHY_PARAM_FRAG_THRESHOLD);
+ if (err)
+ wdev->wiphy->frag_threshold = ofrag;
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwfrag);
+
+int cfg80211_wext_giwfrag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *frag, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ frag->value = wdev->wiphy->frag_threshold;
+ frag->disabled = frag->value == (u32) -1;
+ frag->fixed = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_giwfrag);
+
+int cfg80211_wext_siwretry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *retry, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ u32 changed = 0;
+ u8 olong = wdev->wiphy->retry_long;
+ u8 oshort = wdev->wiphy->retry_short;
+ int err;
+
+ if (retry->disabled ||
+ (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
+ return -EINVAL;
+
+ if (retry->flags & IW_RETRY_LONG) {
+ wdev->wiphy->retry_long = retry->value;
+ changed |= WIPHY_PARAM_RETRY_LONG;
+ } else if (retry->flags & IW_RETRY_SHORT) {
+ wdev->wiphy->retry_short = retry->value;
+ changed |= WIPHY_PARAM_RETRY_SHORT;
+ } else {
+ wdev->wiphy->retry_short = retry->value;
+ wdev->wiphy->retry_long = retry->value;
+ changed |= WIPHY_PARAM_RETRY_LONG;
+ changed |= WIPHY_PARAM_RETRY_SHORT;
+ }
+
+ if (!changed)
+ return 0;
+
+ err = rdev->ops->set_wiphy_params(wdev->wiphy, changed);
+ if (err) {
+ wdev->wiphy->retry_short = oshort;
+ wdev->wiphy->retry_long = olong;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwretry);
+
+int cfg80211_wext_giwretry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *retry, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ retry->disabled = 0;
+
+ if (retry->flags == 0 || (retry->flags & IW_RETRY_SHORT)) {
+ /*
+ * First return short value, iwconfig will ask long value
+ * later if needed
+ */
+ retry->flags |= IW_RETRY_LIMIT;
+ retry->value = wdev->wiphy->retry_short;
+ if (wdev->wiphy->retry_long != wdev->wiphy->retry_short)
+ retry->flags |= IW_RETRY_LONG;
+
+ return 0;
+ }
+
+ if (retry->flags & IW_RETRY_LONG) {
+ retry->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+ retry->value = wdev->wiphy->retry_long;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry);