cfg80211: Support multiple CSA counters
authorAndrei Otcheretianski <andrei.otcheretianski@intel.com>
Fri, 9 May 2014 11:11:46 +0000 (14:11 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 15 May 2014 13:00:42 +0000 (15:00 +0200)
Change the type of NL80211_ATTR_CSA_C_OFF_BEACON and
NL80211_ATTR_CSA_C_OFF_PRESP to be NLA_BINARY which allows
userspace to use beacons and probe responses with
multiple CSA counters.
This isn't breaking the API since userspace can
continue to use nla_put_u16 for this attributes, which
is equivalent to a single element u16 array.
In addition advertise max number of supported CSA counters.
This is needed when using CSA and eCSA IEs together.

Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
Signed-off-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
include/uapi/linux/nl80211.h
net/mac80211/cfg.c
net/wireless/core.c
net/wireless/nl80211.c
net/wireless/trace.h

index f46e1e1..447cb58 100644 (file)
@@ -694,8 +694,10 @@ struct cfg80211_ap_settings {
  *
  * @chandef: defines the channel to use after the switch
  * @beacon_csa: beacon data while performing the switch
- * @counter_offset_beacon: offset for the counter within the beacon (tail)
- * @counter_offset_presp: offset for the counter within the probe response
+ * @counter_offsets_beacon: offsets of the counters within the beacon (tail)
+ * @counter_offsets_presp: offsets of the counters within the probe response
+ * @n_counter_offsets_beacon: number of csa counters the beacon (tail)
+ * @n_counter_offsets_presp: number of csa counters in the probe response
  * @beacon_after: beacon data to be used on the new channel
  * @radar_required: whether radar detection is required on the new channel
  * @block_tx: whether transmissions should be blocked while changing
@@ -704,7 +706,10 @@ struct cfg80211_ap_settings {
 struct cfg80211_csa_settings {
        struct cfg80211_chan_def chandef;
        struct cfg80211_beacon_data beacon_csa;
-       u16 counter_offset_beacon, counter_offset_presp;
+       const u16 *counter_offsets_beacon;
+       const u16 *counter_offsets_presp;
+       unsigned int n_counter_offsets_beacon;
+       unsigned int n_counter_offsets_presp;
        struct cfg80211_beacon_data beacon_after;
        bool radar_required;
        bool block_tx;
@@ -3048,6 +3053,13 @@ struct wiphy {
 
        u16 max_ap_assoc_sta;
 
+       /*
+        * Number of supported csa_counters in beacons and probe responses.
+        * This value should be set if the driver wishes to limit the number of
+        * csa counters. Default (0) means infinite.
+        */
+       u8 max_num_csa_counters;
+
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
index ec90fc9..0cfa827 100644 (file)
@@ -1528,10 +1528,10 @@ enum nl80211_commands {
  *     operation).
  * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
  *     for the time while performing a channel switch.
- * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
- *     field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
- * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
- *     field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: An array of offsets (u16) to the channel
+ *     switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel
+ *     switch counters in the probe response (%NL80211_ATTR_PROBE_RESP).
  *
  * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
  *     As specified in the &enum nl80211_rxmgmt_flags.
@@ -1581,6 +1581,8 @@ enum nl80211_commands {
  *
  * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which
  *     should be updated when the frame is transmitted.
+ * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum
+ *     supported number of csa counters.
  *
  * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
  *     As specified in the &enum nl80211_tdls_peer_capability.
@@ -1927,6 +1929,7 @@ enum nl80211_attrs {
        NL80211_ATTR_IFACE_SOCKET_OWNER,
 
        NL80211_ATTR_CSA_C_OFFSETS_TX,
+       NL80211_ATTR_MAX_CSA_COUNTERS,
 
        /* add attributes here, update the policy in nl80211.c */
 
index 8e4754e..7a6f8ab 100644 (file)
@@ -3192,8 +3192,14 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
                        break;
 
                sdata->csa_counter_offset_beacon =
-                       params->counter_offset_beacon;
-               sdata->csa_counter_offset_presp = params->counter_offset_presp;
+                       params->counter_offsets_beacon[0];
+
+               if (params->n_counter_offsets_presp)
+                       sdata->csa_counter_offset_presp =
+                               params->counter_offsets_presp[0];
+               else
+                       sdata->csa_counter_offset_presp = 0;
+
                err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
                if (err < 0) {
                        kfree(sdata->u.ap.next_beacon);
index 3978871..d03d8bd 100644 (file)
@@ -402,6 +402,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
        rdev->wiphy.rts_threshold = (u32) -1;
        rdev->wiphy.coverage_class = 0;
 
+       rdev->wiphy.max_num_csa_counters = 1;
+
        return &rdev->wiphy;
 }
 EXPORT_SYMBOL(wiphy_new);
index 4c0ca40..ca19b15 100644 (file)
@@ -371,8 +371,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
        [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
        [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
-       [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
-       [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
+       [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_BINARY },
+       [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_BINARY },
        [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
        [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
        [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
@@ -1670,6 +1670,13 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                        }
                        nla_nest_end(msg, nested);
                }
+               state->split_start++;
+               break;
+       case 12:
+               if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH &&
+                   nla_put_u8(msg, NL80211_ATTR_MAX_CSA_COUNTERS,
+                              rdev->wiphy.max_num_csa_counters))
+                       goto nla_put_failure;
 
                /* done */
                state->split_start = 0;
@@ -5864,6 +5871,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        u8 radar_detect_width = 0;
        int err;
        bool need_new_beacon = false;
+       int len, i;
 
        if (!rdev->ops->channel_switch ||
            !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
@@ -5922,26 +5930,55 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
                return -EINVAL;
 
-       params.counter_offset_beacon =
-               nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
-       if (params.counter_offset_beacon >= params.beacon_csa.tail_len)
+       len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+       if (!len || (len % sizeof(u16)))
                return -EINVAL;
 
-       /* sanity check - counters should be the same */
-       if (params.beacon_csa.tail[params.counter_offset_beacon] !=
-           params.count)
+       params.n_counter_offsets_beacon = len / sizeof(u16);
+       if (rdev->wiphy.max_num_csa_counters &&
+           (params.n_counter_offsets_beacon >
+            rdev->wiphy.max_num_csa_counters))
                return -EINVAL;
 
+       params.counter_offsets_beacon =
+               nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+
+       /* sanity checks - counters should fit and be the same */
+       for (i = 0; i < params.n_counter_offsets_beacon; i++) {
+               u16 offset = params.counter_offsets_beacon[i];
+
+               if (offset >= params.beacon_csa.tail_len)
+                       return -EINVAL;
+
+               if (params.beacon_csa.tail[offset] != params.count)
+                       return -EINVAL;
+       }
+
        if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
-               params.counter_offset_presp =
-                       nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
-               if (params.counter_offset_presp >=
-                   params.beacon_csa.probe_resp_len)
+               len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+               if (!len || (len % sizeof(u16)))
                        return -EINVAL;
 
-               if (params.beacon_csa.probe_resp[params.counter_offset_presp] !=
-                   params.count)
+               params.n_counter_offsets_presp = len / sizeof(u16);
+               if (rdev->wiphy.max_num_csa_counters &&
+                   (params.n_counter_offsets_beacon >
+                    rdev->wiphy.max_num_csa_counters))
                        return -EINVAL;
+
+               params.counter_offsets_presp =
+                       nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+
+               /* sanity checks - counters should fit and be the same */
+               for (i = 0; i < params.n_counter_offsets_presp; i++) {
+                       u16 offset = params.counter_offsets_presp[i];
+
+                       if (offset >= params.beacon_csa.probe_resp_len)
+                               return -EINVAL;
+
+                       if (params.beacon_csa.probe_resp[offset] !=
+                           params.count)
+                               return -EINVAL;
+               }
        }
 
 skip_beacons:
index cdfbb00..560ed77 100644 (file)
@@ -1876,29 +1876,33 @@ TRACE_EVENT(rdev_channel_switch,
                WIPHY_ENTRY
                NETDEV_ENTRY
                CHAN_DEF_ENTRY
-               __field(u16, counter_offset_beacon)
-               __field(u16, counter_offset_presp)
                __field(bool, radar_required)
                __field(bool, block_tx)
                __field(u8, count)
+               __dynamic_array(u16, bcn_ofs, params->n_counter_offsets_beacon)
+               __dynamic_array(u16, pres_ofs, params->n_counter_offsets_presp)
        ),
        TP_fast_assign(
                WIPHY_ASSIGN;
                NETDEV_ASSIGN;
                CHAN_DEF_ASSIGN(&params->chandef);
-               __entry->counter_offset_beacon = params->counter_offset_beacon;
-               __entry->counter_offset_presp = params->counter_offset_presp;
                __entry->radar_required = params->radar_required;
                __entry->block_tx = params->block_tx;
                __entry->count = params->count;
+               memcpy(__get_dynamic_array(bcn_ofs),
+                      params->counter_offsets_beacon,
+                      params->n_counter_offsets_beacon * sizeof(u16));
+
+               /* probe response offsets are optional */
+               if (params->n_counter_offsets_presp)
+                       memcpy(__get_dynamic_array(pres_ofs),
+                              params->counter_offsets_presp,
+                              params->n_counter_offsets_presp * sizeof(u16));
        ),
        TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
-                 ", block_tx: %d, count: %u, radar_required: %d"
-                 ", counter offsets (beacon/presp): %u/%u",
+                 ", block_tx: %d, count: %u, radar_required: %d",
                  WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
-                 __entry->block_tx, __entry->count, __entry->radar_required,
-                 __entry->counter_offset_beacon,
-                 __entry->counter_offset_presp)
+                 __entry->block_tx, __entry->count, __entry->radar_required)
 );
 
 TRACE_EVENT(rdev_set_qos_map,