nl80211: Add set/get for frag/rts threshold and retry limits
authorJouni Malinen <j@w1.fi>
Mon, 20 Apr 2009 16:39:05 +0000 (18:39 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 22 Apr 2009 20:57:17 +0000 (16:57 -0400)
Add new nl80211 attributes that can be used with NL80211_CMD_SET_WIPHY
and NL80211_CMD_GET_WIPHY to manage fragmentation/RTS threshold and
retry limits.

Since these values are stored in struct wiphy, remove the local copy
from mac80211 where feasible (frag & rts threshold). The retry limits
are currently needed in struct ieee80211_conf, but these could be
eventually removed since the driver should have access to the values
in struct wiphy.

Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
12 files changed:
include/linux/nl80211.h
include/net/cfg80211.h
net/mac80211/cfg.c
net/mac80211/debugfs.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wext.c
net/wireless/core.c
net/wireless/nl80211.c
net/wireless/wext-compat.c

index 25ce3e42bd1022583b1deb4e683df5a907ff33a3..dc9d9ec5d1aeae71a7103940e1c719753e5d11e4 100644 (file)
  *     to get a list of all present wiphys.
  * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
  *     %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
- *     %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, and/or
- *     %NL80211_ATTR_WIPHY_CHANNEL_TYPE.
+ *     %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ,
+ *     %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT,
+ *     %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+ *     and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
  * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
  *     or rename notification. Has attributes %NL80211_ATTR_WIPHY and
  *     %NL80211_ATTR_WIPHY_NAME.
@@ -337,6 +339,18 @@ enum nl80211_commands {
  *     NL80211_CHAN_HT20 = HT20 only
  *     NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
  *     NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
+ * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is
+ *     less than or equal to the RTS threshold; allowed range: 1..255;
+ *     dot11ShortRetryLimit; u8
+ * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is
+ *     greater than the RTS threshold; allowed range: 1..255;
+ *     dot11ShortLongLimit; u8
+ * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum
+ *     length in octets for frames; allowed range: 256..8000, disable
+ *     fragmentation with (u32)-1; dot11FragmentationThreshold; u32
+ * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length
+ *     larger than or equal to this use RTS/CTS handshake); allowed range:
+ *     0..65536, disable with (u32)-1; dot11RTSThreshold; u32
  *
  * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on
  * @NL80211_ATTR_IFNAME: network interface name
@@ -565,6 +579,12 @@ enum nl80211_attrs {
 
        NL80211_ATTR_FREQ_FIXED,
 
+
+       NL80211_ATTR_WIPHY_RETRY_SHORT,
+       NL80211_ATTR_WIPHY_RETRY_LONG,
+       NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+       NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
index 601eac64b02d6fac4f16436be425c364620cdaa7..54bc69c836914b0902b7b76ee28b799c8496b6b2 100644 (file)
@@ -743,6 +743,20 @@ struct cfg80211_ibss_params {
        bool channel_fixed;
 };
 
+/**
+ * enum wiphy_params_flags - set_wiphy_params bitfield values
+ * WIPHY_PARAM_RETRY_SHORT: wiphy->retry_short has changed
+ * WIPHY_PARAM_RETRY_LONG: wiphy->retry_long has changed
+ * WIPHY_PARAM_FRAG_THRESHOLD: wiphy->frag_threshold has changed
+ * WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed
+ */
+enum wiphy_params_flags {
+       WIPHY_PARAM_RETRY_SHORT         = 1 << 0,
+       WIPHY_PARAM_RETRY_LONG          = 1 << 1,
+       WIPHY_PARAM_FRAG_THRESHOLD      = 1 << 2,
+       WIPHY_PARAM_RTS_THRESHOLD       = 1 << 3,
+};
+
 /**
  * struct cfg80211_ops - backend description for wireless configuration
  *
@@ -823,6 +837,11 @@ struct cfg80211_ibss_params {
  *     cfg80211_ibss_joined(), also call that function when changing BSSID due
  *     to a merge.
  * @leave_ibss: Leave the IBSS.
+ *
+ * @set_wiphy_params: Notify that wiphy parameters have changed;
+ *     @changed bitfield (see &enum wiphy_params_flags) describes which values
+ *     have changed. The actual parameter values are available in
+ *     struct wiphy. If returning an error, no value should be changed.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy);
@@ -912,6 +931,8 @@ struct cfg80211_ops {
        int     (*join_ibss)(struct wiphy *wiphy, struct net_device *dev,
                             struct cfg80211_ibss_params *params);
        int     (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev);
+
+       int     (*set_wiphy_params)(struct wiphy *wiphy, u32 changed);
 };
 
 /*
@@ -945,6 +966,11 @@ struct cfg80211_ops {
  * @signal_type: signal type reported in &struct cfg80211_bss.
  * @cipher_suites: supported cipher suites
  * @n_cipher_suites: number of supported cipher suites
+ * @retry_short: Retry limit for short frames (dot11ShortRetryLimit)
+ * @retry_long: Retry limit for long frames (dot11LongRetryLimit)
+ * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold);
+ *     -1 = fragmentation disabled, only odd values >= 256 used
+ * @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -967,6 +993,11 @@ struct wiphy {
        int n_cipher_suites;
        const u32 *cipher_suites;
 
+       u8 retry_short;
+       u8 retry_long;
+       u32 frag_threshold;
+       u32 rts_threshold;
+
        /* If multiple wiphys are registered and you're handed e.g.
         * a regular netdev with assigned ieee80211_ptr, you won't
         * know whether it points to a wiphy your driver has registered
@@ -1345,6 +1376,25 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev,
 struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy,
                                             struct iw_freq *freq);
 
+int cfg80211_wext_siwrts(struct net_device *dev,
+                        struct iw_request_info *info,
+                        struct iw_param *rts, char *extra);
+int cfg80211_wext_giwrts(struct net_device *dev,
+                        struct iw_request_info *info,
+                        struct iw_param *rts, char *extra);
+int cfg80211_wext_siwfrag(struct net_device *dev,
+                         struct iw_request_info *info,
+                         struct iw_param *frag, char *extra);
+int cfg80211_wext_giwfrag(struct net_device *dev,
+                         struct iw_request_info *info,
+                         struct iw_param *frag, char *extra);
+int cfg80211_wext_siwretry(struct net_device *dev,
+                          struct iw_request_info *info,
+                          struct iw_param *retry, char *extra);
+int cfg80211_wext_giwretry(struct net_device *dev,
+                          struct iw_request_info *info,
+                          struct iw_param *retry, char *extra);
+
 /*
  * callbacks for asynchronous cfg80211 methods, notification
  * functions and BSS handling helpers
index 14013dc64474e672dc7be586d4e140535793f4a1..5e1c230744b57763f8f1227775c92ffa91859123 100644 (file)
@@ -1298,6 +1298,32 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
        return ieee80211_ibss_leave(sdata);
 }
 
+static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+
+       if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+               int err;
+
+               if (local->ops->set_rts_threshold) {
+                       err = local->ops->set_rts_threshold(
+                               local_to_hw(local), wiphy->rts_threshold);
+                       if (err)
+                               return err;
+               }
+       }
+
+       if (changed & WIPHY_PARAM_RETRY_SHORT)
+               local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
+       if (changed & WIPHY_PARAM_RETRY_LONG)
+               local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
+       if (changed &
+           (WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
+
+       return 0;
+}
+
 struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -1336,4 +1362,5 @@ struct cfg80211_ops mac80211_config_ops = {
        .disassoc = ieee80211_disassoc,
        .join_ibss = ieee80211_join_ibss,
        .leave_ibss = ieee80211_leave_ibss,
+       .set_wiphy_params = ieee80211_set_wiphy_params,
 };
index 210b9b6fecd2a3676c8fe23393c22d87700228b4..5001328be46b3b5b3fa4bc00c8ad93b9ffb0a1d2 100644 (file)
@@ -52,13 +52,13 @@ static const struct file_operations name## _ops = {                 \
 DEBUGFS_READONLY_FILE(frequency, 20, "%d",
                      local->hw.conf.channel->center_freq);
 DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
-                     local->rts_threshold);
+                     local->hw.wiphy->rts_threshold);
 DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
-                     local->fragmentation_threshold);
+                     local->hw.wiphy->frag_threshold);
 DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
-                     local->hw.conf.short_frame_max_tx_count);
+                     local->hw.wiphy->retry_short);
 DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
-                     local->hw.conf.long_frame_max_tx_count);
+                     local->hw.wiphy->retry_long);
 DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d",
                      local->total_ps_buffered);
 DEBUGFS_READONLY_FILE(wep_iv, 20, "%#08x",
index 92a573bf103534b6c7ec1e14eab103c228731d04..dba78d89a10c95953b97ceb98f8a804d6fd650a0 100644 (file)
@@ -643,9 +643,6 @@ struct ieee80211_local {
 
        struct rate_control_ref *rate_ctrl;
 
-       int rts_threshold;
-       int fragmentation_threshold;
-
        struct crypto_blkcipher *wep_tx_tfm;
        struct crypto_blkcipher *wep_rx_tfm;
        u32 wep_iv;
index d26fc399285ebf7f408aac01afa6be479796fd41..5320e08434acc398f822797a9886463e2781332b 100644 (file)
@@ -776,10 +776,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
        /* set up some defaults */
        local->hw.queues = 1;
        local->hw.max_rates = 1;
-       local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
-       local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
-       local->hw.conf.long_frame_max_tx_count = 4;
-       local->hw.conf.short_frame_max_tx_count = 7;
+       local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
+       local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
        local->hw.conf.radio_enabled = true;
 
        INIT_LIST_HEAD(&local->interfaces);
index c53d77db3e4faf8f5c8ce61115216becd6b2bc20..9ab49826c15a8cdd078710533287bd3063631531 100644 (file)
@@ -516,7 +516,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
        sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
        len = min_t(int, tx->skb->len + FCS_LEN,
-                        tx->local->fragmentation_threshold);
+                        tx->local->hw.wiphy->frag_threshold);
 
        /* set up the tx rate control struct we give the RC algo */
        txrc.hw = local_to_hw(tx->local);
@@ -527,8 +527,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
        txrc.max_rate_idx = tx->sdata->max_ratectrl_rateidx;
 
        /* set up RTS protection if desired */
-       if (tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD &&
-           len > tx->local->rts_threshold) {
+       if (len > tx->local->hw.wiphy->rts_threshold) {
                txrc.rts = rts = true;
        }
 
@@ -770,7 +769,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
        struct sk_buff *skb = tx->skb;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (void *)skb->data;
-       int frag_threshold = tx->local->fragmentation_threshold;
+       int frag_threshold = tx->local->hw.wiphy->frag_threshold;
        int hdrlen;
        int fragnum;
 
@@ -1088,7 +1087,7 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
 
        if (tx->flags & IEEE80211_TX_FRAGMENTED) {
                if ((tx->flags & IEEE80211_TX_UNICAST) &&
-                   skb->len + FCS_LEN > local->fragmentation_threshold &&
+                   skb->len + FCS_LEN > local->hw.wiphy->frag_threshold &&
                    !(info->flags & IEEE80211_TX_CTL_AMPDU))
                        tx->flags |= IEEE80211_TX_FRAGMENTED;
                else
index 3dd490fa4b68aa58b6c21db03f9fbc85846532b5..11244212f41ddc62c69fc10ddaf7208d625dbdbf 100644 (file)
@@ -1044,7 +1044,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 
        /* setup RTS threshold */
        if (local->ops->set_rts_threshold)
-               local->ops->set_rts_threshold(hw, local->rts_threshold);
+               local->ops->set_rts_threshold(hw, hw->wiphy->rts_threshold);
 
        /* reconfigure hardware */
        ieee80211_hw_config(local, ~0);
index eb63fc148019a6a59b143a18823513462d65bdb2..1eb6d8642a77138510bc9eb6ecaf420c57369676 100644 (file)
@@ -472,132 +472,6 @@ static int ieee80211_ioctl_giwtxpower(struct net_device *dev,
        return 0;
 }
 
-static int ieee80211_ioctl_siwrts(struct net_device *dev,
-                                 struct iw_request_info *info,
-                                 struct iw_param *rts, char *extra)
-{
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
-       if (rts->disabled)
-               local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
-       else if (!rts->fixed)
-               /* if the rts value is not fixed, then take default */
-               local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
-       else if (rts->value < 0 || rts->value > IEEE80211_MAX_RTS_THRESHOLD)
-               return -EINVAL;
-       else
-               local->rts_threshold = rts->value;
-
-       /* If the wlan card performs RTS/CTS in hardware/firmware,
-        * configure it here */
-
-       if (local->ops->set_rts_threshold)
-               local->ops->set_rts_threshold(local_to_hw(local),
-                                            local->rts_threshold);
-
-       return 0;
-}
-
-static int ieee80211_ioctl_giwrts(struct net_device *dev,
-                                 struct iw_request_info *info,
-                                 struct iw_param *rts, char *extra)
-{
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
-       rts->value = local->rts_threshold;
-       rts->disabled = (rts->value >= IEEE80211_MAX_RTS_THRESHOLD);
-       rts->fixed = 1;
-
-       return 0;
-}
-
-
-static int ieee80211_ioctl_siwfrag(struct net_device *dev,
-                                  struct iw_request_info *info,
-                                  struct iw_param *frag, char *extra)
-{
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
-       if (frag->disabled)
-               local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
-       else if (!frag->fixed)
-               local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
-       else if (frag->value < 256 ||
-                frag->value > IEEE80211_MAX_FRAG_THRESHOLD)
-               return -EINVAL;
-       else {
-               /* Fragment length must be even, so strip LSB. */
-               local->fragmentation_threshold = frag->value & ~0x1;
-       }
-
-       return 0;
-}
-
-static int ieee80211_ioctl_giwfrag(struct net_device *dev,
-                                  struct iw_request_info *info,
-                                  struct iw_param *frag, char *extra)
-{
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
-       frag->value = local->fragmentation_threshold;
-       frag->disabled = (frag->value >= IEEE80211_MAX_FRAG_THRESHOLD);
-       frag->fixed = 1;
-
-       return 0;
-}
-
-
-static int ieee80211_ioctl_siwretry(struct net_device *dev,
-                                   struct iw_request_info *info,
-                                   struct iw_param *retry, char *extra)
-{
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
-       if (retry->disabled ||
-           (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
-               return -EINVAL;
-
-       if (retry->flags & IW_RETRY_MAX) {
-               local->hw.conf.long_frame_max_tx_count = retry->value;
-       } else if (retry->flags & IW_RETRY_MIN) {
-               local->hw.conf.short_frame_max_tx_count = retry->value;
-       } else {
-               local->hw.conf.long_frame_max_tx_count = retry->value;
-               local->hw.conf.short_frame_max_tx_count = retry->value;
-       }
-
-       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
-
-       return 0;
-}
-
-
-static int ieee80211_ioctl_giwretry(struct net_device *dev,
-                                   struct iw_request_info *info,
-                                   struct iw_param *retry, char *extra)
-{
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
-       retry->disabled = 0;
-       if (retry->flags == 0 || retry->flags & IW_RETRY_MIN) {
-               /* first return min value, iwconfig will ask max value
-                * later if needed */
-               retry->flags |= IW_RETRY_LIMIT;
-               retry->value = local->hw.conf.short_frame_max_tx_count;
-               if (local->hw.conf.long_frame_max_tx_count !=
-                   local->hw.conf.short_frame_max_tx_count)
-                       retry->flags |= IW_RETRY_MIN;
-               return 0;
-       }
-       if (retry->flags & IW_RETRY_MAX) {
-               retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
-               retry->value = local->hw.conf.long_frame_max_tx_count;
-       }
-
-       return 0;
-}
-
-
 static int ieee80211_ioctl_siwencode(struct net_device *dev,
                                     struct iw_request_info *info,
                                     struct iw_point *erq, char *keybuf)
@@ -1050,14 +924,14 @@ static const iw_handler ieee80211_handler[] =
        (iw_handler) NULL,                              /* -- hole -- */
        (iw_handler) ieee80211_ioctl_siwrate,           /* SIOCSIWRATE */
        (iw_handler) ieee80211_ioctl_giwrate,           /* SIOCGIWRATE */
-       (iw_handler) ieee80211_ioctl_siwrts,            /* SIOCSIWRTS */
-       (iw_handler) ieee80211_ioctl_giwrts,            /* SIOCGIWRTS */
-       (iw_handler) ieee80211_ioctl_siwfrag,           /* SIOCSIWFRAG */
-       (iw_handler) ieee80211_ioctl_giwfrag,           /* SIOCGIWFRAG */
+       (iw_handler) cfg80211_wext_siwrts,              /* SIOCSIWRTS */
+       (iw_handler) cfg80211_wext_giwrts,              /* SIOCGIWRTS */
+       (iw_handler) cfg80211_wext_siwfrag,             /* SIOCSIWFRAG */
+       (iw_handler) cfg80211_wext_giwfrag,             /* SIOCGIWFRAG */
        (iw_handler) ieee80211_ioctl_siwtxpower,        /* SIOCSIWTXPOW */
        (iw_handler) ieee80211_ioctl_giwtxpower,        /* SIOCGIWTXPOW */
-       (iw_handler) ieee80211_ioctl_siwretry,          /* SIOCSIWRETRY */
-       (iw_handler) ieee80211_ioctl_giwretry,          /* SIOCGIWRETRY */
+       (iw_handler) cfg80211_wext_siwretry,            /* SIOCSIWRETRY */
+       (iw_handler) cfg80211_wext_giwretry,            /* SIOCGIWRETRY */
        (iw_handler) ieee80211_ioctl_siwencode,         /* SIOCSIWENCODE */
        (iw_handler) ieee80211_ioctl_giwencode,         /* SIOCGIWENCODE */
        (iw_handler) ieee80211_ioctl_siwpower,          /* SIOCSIWPOWER */
index 827a56263551a1a33ce03e4437aedaeea646d2d5..f256b4f7e83325d1701a9df5baf4f741208f42d7 100644 (file)
@@ -273,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);
index 16f86356ac97c7b07edd1d435bc1024cde853afe..5a9a5c6c71dbd351a8c6359806a6c18d8602f268 100644 (file)
@@ -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 },
@@ -204,6 +208,16 @@ 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,
@@ -416,6 +430,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();
 
@@ -530,6 +547,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);
index 4e054ea9c0a06131a2959a24727018453205f099..3279e7f038dc4cad3e6a5e36b81c1b99469755b4 100644 (file)
@@ -314,3 +314,154 @@ struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy,
 
 }
 EXPORT_SYMBOL(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(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(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(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(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(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(cfg80211_wext_giwretry);