mac80211: build HE operation with 6 GHz oper information
[linux-2.6-microblaze.git] / net / mac80211 / util.c
index 048b385..87dd003 100644 (file)
@@ -3008,13 +3008,18 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
        return pos + sizeof(struct ieee80211_vht_operation);
 }
 
-u8 *ieee80211_ie_build_he_oper(u8 *pos)
+u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef)
 {
        struct ieee80211_he_operation *he_oper;
+       struct ieee80211_he_6ghz_oper *he_6ghz_op;
        u32 he_oper_params;
+       u8 ie_len = 1 + sizeof(struct ieee80211_he_operation);
+
+       if (chandef->chan->band == NL80211_BAND_6GHZ)
+               ie_len += sizeof(struct ieee80211_he_6ghz_oper);
 
        *pos++ = WLAN_EID_EXTENSION;
-       *pos++ = 1 + sizeof(struct ieee80211_he_operation);
+       *pos++ = ie_len;
        *pos++ = WLAN_EID_EXT_HE_OPERATION;
 
        he_oper_params = 0;
@@ -3024,16 +3029,68 @@ u8 *ieee80211_ie_build_he_oper(u8 *pos)
                                IEEE80211_HE_OPERATION_ER_SU_DISABLE);
        he_oper_params |= u32_encode_bits(1,
                                IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED);
+       if (chandef->chan->band == NL80211_BAND_6GHZ)
+               he_oper_params |= u32_encode_bits(1,
+                               IEEE80211_HE_OPERATION_6GHZ_OP_INFO);
 
        he_oper = (struct ieee80211_he_operation *)pos;
        he_oper->he_oper_params = cpu_to_le32(he_oper_params);
 
        /* don't require special HE peer rates */
        he_oper->he_mcs_nss_set = cpu_to_le16(0xffff);
+       pos += sizeof(struct ieee80211_he_operation);
 
-       /* TODO add VHT operational and 6GHz operational subelement? */
+       if (chandef->chan->band != NL80211_BAND_6GHZ)
+               goto out;
 
-       return pos + sizeof(struct ieee80211_vht_operation);
+       /* TODO add VHT operational */
+       he_6ghz_op = (struct ieee80211_he_6ghz_oper *)pos;
+       he_6ghz_op->minrate = 6; /* 6 Mbps */
+       he_6ghz_op->primary =
+               ieee80211_frequency_to_channel(chandef->chan->center_freq);
+       he_6ghz_op->ccfs0 =
+               ieee80211_frequency_to_channel(chandef->center_freq1);
+       if (chandef->center_freq2)
+               he_6ghz_op->ccfs1 =
+                       ieee80211_frequency_to_channel(chandef->center_freq2);
+       else
+               he_6ghz_op->ccfs1 = 0;
+
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_160:
+               /* Convert 160 MHz channel width to new style as interop
+                * workaround.
+                */
+               he_6ghz_op->control =
+                       IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
+               he_6ghz_op->ccfs1 = he_6ghz_op->ccfs0;
+               if (chandef->chan->center_freq < chandef->center_freq1)
+                       he_6ghz_op->ccfs0 -= 8;
+               else
+                       he_6ghz_op->ccfs0 += 8;
+               fallthrough;
+       case NL80211_CHAN_WIDTH_80P80:
+               he_6ghz_op->control =
+                       IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_80:
+               he_6ghz_op->control =
+                       IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_40:
+               he_6ghz_op->control =
+                       IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ;
+               break;
+       default:
+               he_6ghz_op->control =
+                       IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ;
+               break;
+       }
+
+       pos += sizeof(struct ieee80211_he_6ghz_oper);
+
+out:
+       return pos;
 }
 
 bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,