]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
mac80211: support S1G association
authorThomas Pedersen <thomas@adapt-ip.com>
Tue, 22 Sep 2020 02:28:15 +0000 (19:28 -0700)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 28 Sep 2020 12:09:07 +0000 (14:09 +0200)
The changes required for associating in S1G are:

- apply S1G BSS channel info before assoc
- mark all S1G STAs as QoS STAs
- include and parse AID request element
- handle new Association Response format
- don't fail assoc if supported rates element is missing

Signed-off-by: Thomas Pedersen <thomas@adapt-ip.com>
Link: https://lore.kernel.org/r/20200922022818.15855-15-thomas@adapt-ip.com
[pass skb to ieee80211_add_aid_request_ie(), remove unused variable 'bss']
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/linux/ieee80211.h
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/mlme.c
net/mac80211/util.c

index 7b6af47dd2793bf42b4da1a1d558429f8c41e15c..f2f56b287aedc6a3d1d9f28ccc9019d037e3cf83 100644 (file)
@@ -987,6 +987,25 @@ enum ieee80211_vht_opmode_bits {
        IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF   = 0x80,
 };
 
+/**
+ * enum ieee80211_s1g_chanwidth
+ * These are defined in IEEE802.11-2016ah Table 10-20
+ * as BSS Channel Width
+ *
+ * @IEEE80211_S1G_CHANWIDTH_1MHZ: 1MHz operating channel
+ * @IEEE80211_S1G_CHANWIDTH_2MHZ: 2MHz operating channel
+ * @IEEE80211_S1G_CHANWIDTH_4MHZ: 4MHz operating channel
+ * @IEEE80211_S1G_CHANWIDTH_8MHZ: 8MHz operating channel
+ * @IEEE80211_S1G_CHANWIDTH_16MHZ: 16MHz operating channel
+ */
+enum ieee80211_s1g_chanwidth {
+       IEEE80211_S1G_CHANWIDTH_1MHZ = 0,
+       IEEE80211_S1G_CHANWIDTH_2MHZ = 1,
+       IEEE80211_S1G_CHANWIDTH_4MHZ = 3,
+       IEEE80211_S1G_CHANWIDTH_8MHZ = 7,
+       IEEE80211_S1G_CHANWIDTH_16MHZ = 15,
+};
+
 #define WLAN_SA_QUERY_TR_ID_LEN 2
 #define WLAN_MEMBERSHIP_LEN 8
 #define WLAN_USER_POSITION_LEN 16
@@ -2854,6 +2873,8 @@ enum ieee80211_eid {
 
        WLAN_EID_REDUCED_NEIGHBOR_REPORT = 201,
 
+       WLAN_EID_AID_REQUEST = 210,
+       WLAN_EID_AID_RESPONSE = 211,
        WLAN_EID_S1G_BCN_COMPAT = 213,
        WLAN_EID_S1G_SHORT_BCN_INTERVAL = 214,
        WLAN_EID_S1G_CAPABILITIES = 217,
index de22524e9270b36b0ae54bd70469b6a3a7dbde17..72bc877d2c22adb4e064845f543785d86eb74623 100644 (file)
@@ -627,6 +627,7 @@ struct ieee80211_fils_discovery {
  * @fils_discovery: FILS discovery configuration
  * @unsol_bcast_probe_resp_interval: Unsolicited broadcast probe response
  *     interval.
+ * @s1g: BSS is S1G BSS (affects Association Request format).
  */
 struct ieee80211_bss_conf {
        const u8 *bssid;
@@ -696,6 +697,7 @@ struct ieee80211_bss_conf {
        struct cfg80211_he_bss_color he_bss_color;
        struct ieee80211_fils_discovery fils_discovery;
        u32 unsol_bcast_probe_resp_interval;
+       bool s1g;
 };
 
 /**
index 8d75a4045d6e61cfbf6cfa5c40b23764420c2924..da70f174d629ff18875390626284338609a5c9ee 100644 (file)
@@ -1124,6 +1124,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
               sizeof(struct ieee80211_he_obss_pd));
        memcpy(&sdata->vif.bss_conf.he_bss_color, &params->he_bss_color,
               sizeof(struct ieee80211_he_bss_color));
+       sdata->vif.bss_conf.s1g = params->chandef.chan->band ==
+                                 NL80211_BAND_S1GHZ;
 
        sdata->vif.bss_conf.ssid_len = params->ssid_len;
        if (params->ssid_len)
index c0963969a4655541330983d18c10352963c8df5e..1f552f374e97d2e581375dc07b73f2f5c9c30c8f 100644 (file)
@@ -1037,7 +1037,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
        }
 
        if (sta && !sta->sta.wme &&
-           elems->wmm_info && local->hw.queues >= IEEE80211_NUM_ACS) {
+           (elems->wmm_info || elems->s1g_capab) &&
+           local->hw.queues >= IEEE80211_NUM_ACS) {
                sta->sta.wme = true;
                ieee80211_check_fast_xmit(sta);
        }
index 7b4a16a78fc84906cc29630fd37a22eedc10ecfa..c3e3578574a60099a48a4911221d6e69db1bde85 100644 (file)
@@ -1538,6 +1538,7 @@ struct ieee802_11_elems {
        const struct ieee80211_s1g_cap *s1g_capab;
        const struct ieee80211_s1g_oper_ie *s1g_oper;
        const struct ieee80211_s1g_bcn_compat_ie *s1g_bcn_compat;
+       const struct ieee80211_aid_response_ie *aid_resp;
 
        /* length of them, respectively */
        u8 ext_capab_len;
@@ -2213,6 +2214,8 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
 void ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata,
                                struct ieee80211_sta_s1g_cap *caps,
                                struct sk_buff *skb);
+void ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata,
+                                 struct sk_buff *skb);
 
 /* channel management */
 bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
@@ -2224,6 +2227,8 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
 bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
                                    const struct ieee80211_he_operation *he_oper,
                                    struct cfg80211_chan_def *chandef);
+bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper,
+                               struct cfg80211_chan_def *chandef);
 u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
 
 int __must_check
index fbe64a72eaff27042b142047ce1a73c46d39b64b..e2df4d54d9df4c7045b15090d80cab0535a176b5 100644 (file)
@@ -149,6 +149,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                             const struct ieee80211_ht_operation *ht_oper,
                             const struct ieee80211_vht_operation *vht_oper,
                             const struct ieee80211_he_operation *he_oper,
+                            const struct ieee80211_s1g_oper_ie *s1g_oper,
                             struct cfg80211_chan_def *chandef, bool tracking)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -176,6 +177,15 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
        memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
        ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
 
+       if (s1g_oper && sband->band == NL80211_BAND_S1GHZ) {
+               ieee80211_chandef_s1g_oper(s1g_oper, chandef);
+               ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_40MHZ |
+                     IEEE80211_STA_DISABLE_VHT |
+                     IEEE80211_STA_DISABLE_80P80MHZ |
+                     IEEE80211_STA_DISABLE_160MHZ;
+               goto out;
+       }
+
        if (!ht_oper || !sta_ht_cap.ht_supported) {
                ret = IEEE80211_STA_DISABLE_HT |
                      IEEE80211_STA_DISABLE_VHT |
@@ -347,6 +357,7 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
                               const struct ieee80211_ht_operation *ht_oper,
                               const struct ieee80211_vht_operation *vht_oper,
                               const struct ieee80211_he_operation *he_oper,
+                              const struct ieee80211_s1g_oper_ie *s1g_oper,
                               const u8 *bssid, u32 *changed)
 {
        struct ieee80211_local *local = sdata->local;
@@ -393,7 +404,7 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
        /* calculate new channel (type) based on HT/VHT/HE operation IEs */
        flags = ieee80211_determine_chantype(sdata, sband, chan, vht_cap_info,
                                             ht_oper, vht_oper, he_oper,
-                                            &chandef, true);
+                                            s1g_oper, &chandef, true);
 
        /*
         * Downgrade the new channel if we associated with restricted
@@ -811,6 +822,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        *pos++ = assoc_data->ssid_len;
        memcpy(pos, assoc_data->ssid, assoc_data->ssid_len);
 
+       if (sband->band == NL80211_BAND_S1GHZ)
+               goto skip_rates;
+
        /* add all rates which were marked to be used above */
        supp_rates_len = rates_len;
        if (supp_rates_len > 8)
@@ -846,6 +860,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                }
        }
 
+skip_rates:
        if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT ||
            capab & WLAN_CAPABILITY_RADIO_MEASURE) {
                pos = skb_put(skb, 4);
@@ -1020,8 +1035,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
        }
 
-       if (sband->band == NL80211_BAND_S1GHZ)
+       if (sband->band == NL80211_BAND_S1GHZ) {
+               ieee80211_add_aid_request_ie(sdata, skb);
                ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap, skb);
+       }
 
        /* add any remaining custom (i.e. vendor specific here) IEs */
        if (assoc_data->ie_len) {
@@ -3250,14 +3267,26 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        const struct cfg80211_bss_ies *bss_ies = NULL;
        struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
        bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
+       bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
        u32 changed = 0;
+       u8 *pos;
        int err;
        bool ret;
 
        /* AssocResp and ReassocResp have identical structure */
 
+       pos = mgmt->u.assoc_resp.variable;
        aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+       if (is_s1g) {
+               pos = (u8 *) mgmt->u.s1g_assoc_resp.variable;
+               aid = 0; /* TODO */
+       }
        capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, elems,
+                              mgmt->bssid, assoc_data->bss->bssid);
+
+       if (elems->aid_resp)
+               aid = le16_to_cpu(elems->aid_resp->aid);
 
        /*
         * The 5 MSB of the AID field are reserved
@@ -3274,7 +3303,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                ifmgd->broken_ap = true;
        }
 
-       if (!elems->supp_rates) {
+       if (!is_s1g && !elems->supp_rates) {
                sdata_info(sdata, "no SuppRates element in AssocResp\n");
                return false;
        }
@@ -3516,7 +3545,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                sta->sta.mfp = false;
        }
 
-       sta->sta.wme = elems->wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
+       sta->sta.wme = (elems->wmm_param || elems->s1g_capab) &&
+                      local->hw.queues >= IEEE80211_NUM_ACS;
 
        err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
        if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
@@ -3611,7 +3641,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        int ac, uapsd_queues = -1;
        u8 *pos;
        bool reassoc;
-       struct cfg80211_bss *bss;
+       struct cfg80211_bss *cbss;
        struct ieee80211_event event = {
                .type = MLME_EVENT,
                .u.mlme.data = ASSOC_EVENT,
@@ -3621,9 +3651,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
 
        if (!assoc_data)
                return;
+
        if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid))
                return;
 
+       cbss = assoc_data->bss;
+
        /*
         * AssocResp and ReassocResp have identical structure, so process both
         * of them in this function.
@@ -3635,7 +3668,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        reassoc = ieee80211_is_reassoc_resp(mgmt->frame_control);
        capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
        status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+       pos = mgmt->u.assoc_resp.variable;
        aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+       if (cbss->channel->band == NL80211_BAND_S1GHZ) {
+               pos = (u8 *) mgmt->u.s1g_assoc_resp.variable;
+               aid = 0; /* TODO */
+       }
 
        sdata_info(sdata,
                   "RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n",
@@ -3646,7 +3684,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
            fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0)
                return;
 
-       pos = mgmt->u.assoc_resp.variable;
        ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems,
                               mgmt->bssid, assoc_data->bss->bssid);
 
@@ -3666,8 +3703,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
-       bss = assoc_data->bss;
-
        if (status_code != WLAN_STATUS_SUCCESS) {
                sdata_info(sdata, "%pM denied association (code=%d)\n",
                           mgmt->sa, status_code);
@@ -3676,10 +3711,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                event.u.mlme.reason = status_code;
                drv_event_callback(sdata->local, sdata, &event);
        } else {
-               if (!ieee80211_assoc_success(sdata, bss, mgmt, len, &elems)) {
+               if (!ieee80211_assoc_success(sdata, cbss, mgmt, len, &elems)) {
                        /* oops -- internal error -- send timeout for now */
                        ieee80211_destroy_assoc_data(sdata, false, false);
-                       cfg80211_assoc_timeout(sdata->dev, bss);
+                       cfg80211_assoc_timeout(sdata->dev, cbss);
                        return;
                }
                event.u.mlme.status = MLME_SUCCESS;
@@ -3700,7 +3735,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                                uapsd_queues |= ieee80211_ac_to_qos_mask[ac];
        }
 
-       cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len, uapsd_queues,
+       cfg80211_rx_assoc_resp(sdata->dev, cbss, (u8 *)mgmt, len, uapsd_queues,
                               ifmgd->assoc_req_ies, ifmgd->assoc_req_ies_len);
 }
 
@@ -4149,7 +4184,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        if (ieee80211_config_bw(sdata, sta, elems.ht_cap_elem,
                                elems.vht_cap_elem, elems.ht_operation,
                                elems.vht_operation, elems.he_operation,
-                               bssid, &changed)) {
+                               elems.s1g_oper, bssid, &changed)) {
                mutex_unlock(&local->sta_mtx);
                sdata_info(sdata,
                           "failed to follow AP %pM bandwidth change, disconnect\n",
@@ -4902,6 +4937,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
        const struct ieee80211_ht_operation *ht_oper = NULL;
        const struct ieee80211_vht_operation *vht_oper = NULL;
        const struct ieee80211_he_operation *he_oper = NULL;
+       const struct ieee80211_s1g_oper_ie *s1g_oper = NULL;
        struct ieee80211_supported_band *sband;
        struct cfg80211_chan_def chandef;
        bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
@@ -5005,10 +5041,23 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
        if (!have_80mhz)
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
 
+       if (sband->band == NL80211_BAND_S1GHZ) {
+               const u8 *s1g_oper_ie;
+
+               s1g_oper_ie = ieee80211_bss_get_ie(cbss,
+                                                  WLAN_EID_S1G_OPERATION);
+               if (s1g_oper_ie && s1g_oper_ie[1] >= sizeof(*s1g_oper))
+                       s1g_oper = (void *)(s1g_oper_ie + 2);
+               else
+                       sdata_info(sdata,
+                                  "AP missing S1G operation element?\n");
+       }
+
        ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
                                                     cbss->channel,
                                                     bss->vht_cap_info,
                                                     ht_oper, vht_oper, he_oper,
+                                                    s1g_oper,
                                                     &chandef, false);
 
        sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
@@ -5135,6 +5184,10 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                const struct cfg80211_bss_ies *ies;
                int shift = ieee80211_vif_get_shift(&sdata->vif);
 
+               /* TODO: S1G Basic Rate Set is expressed elsewhere */
+               if (cbss->channel->band == NL80211_BAND_S1GHZ)
+                       goto skip_rates;
+
                ieee80211_get_rates(sband, bss->supp_rates,
                                    bss->supp_rates_len,
                                    &rates, &basic_rates,
@@ -5179,6 +5232,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                else
                        sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
 
+skip_rates:
                memcpy(ifmgd->bssid, cbss->bssid, ETH_ALEN);
 
                /* set timing information */
index 70865f316d89cf8df4dbc1926fb045ed417a6499..99d584fe49e81730e2cbad0dce895d24e457273f 100644 (file)
@@ -1058,6 +1058,7 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                case WLAN_EID_S1G_BCN_COMPAT:
                case WLAN_EID_S1G_CAPABILITIES:
                case WLAN_EID_S1G_OPERATION:
+               case WLAN_EID_AID_RESPONSE:
                case WLAN_EID_S1G_SHORT_BCN_INTERVAL:
                /*
                 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
@@ -1362,6 +1363,12 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                        else
                                elem_parse_failed = true;
                        break;
+               case WLAN_EID_AID_RESPONSE:
+                       if (elen == sizeof(struct ieee80211_aid_response_ie))
+                               elems->aid_resp = (void *)pos;
+                       else
+                               elem_parse_failed = true;
+                       break;
                default:
                        break;
                }
@@ -3445,6 +3452,42 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
 
        *chandef = he_chandef;
 
+       return false;
+}
+
+bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper,
+                               struct cfg80211_chan_def *chandef)
+{
+       u32 oper_freq;
+
+       if (!oper)
+               return false;
+
+       switch (FIELD_GET(S1G_OPER_CH_WIDTH_OPER, oper->ch_width)) {
+       case IEEE80211_S1G_CHANWIDTH_1MHZ:
+               chandef->width = NL80211_CHAN_WIDTH_1;
+               break;
+       case IEEE80211_S1G_CHANWIDTH_2MHZ:
+               chandef->width = NL80211_CHAN_WIDTH_2;
+               break;
+       case IEEE80211_S1G_CHANWIDTH_4MHZ:
+               chandef->width = NL80211_CHAN_WIDTH_4;
+               break;
+       case IEEE80211_S1G_CHANWIDTH_8MHZ:
+               chandef->width = NL80211_CHAN_WIDTH_8;
+               break;
+       case IEEE80211_S1G_CHANWIDTH_16MHZ:
+               chandef->width = NL80211_CHAN_WIDTH_16;
+               break;
+       default:
+               return false;
+       }
+
+       oper_freq = ieee80211_channel_to_freq_khz(oper->oper_ch,
+                                                 NL80211_BAND_S1GHZ);
+       chandef->center_freq1 = KHZ_TO_MHZ(oper_freq);
+       chandef->freq1_offset = oper_freq % 1000;
+
        return true;
 }
 
@@ -4393,6 +4436,16 @@ void ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata,
        memcpy(pos, &s1g_capab, sizeof(s1g_capab));
 }
 
+void ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata,
+                                 struct sk_buff *skb)
+{
+       u8 *pos = skb_put(skb, 3);
+
+       *pos++ = WLAN_EID_AID_REQUEST;
+       *pos++ = 1;
+       *pos++ = 0;
+}
+
 u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
 {
        *buf++ = WLAN_EID_VENDOR_SPECIFIC;