+static int bss_is_ibss(struct wpa_bss *bss)
+{
+ return (bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
+ IEEE80211_CAP_IBSS;
+}
+
+
+static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid)
+{
+ enum hostapd_hw_mode hw_mode;
+ struct hostapd_hw_modes *mode = NULL;
+ u8 channel;
+ int i;
+
+#ifdef CONFIG_HT_OVERRIDES
+ if (ssid->disable_ht)
+ return 0;
+#endif /* CONFIG_HT_OVERRIDES */
+
+ hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
+ if (hw_mode == NUM_HOSTAPD_MODES)
+ return 0;
+ for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].mode == hw_mode) {
+ mode = &wpa_s->hw.modes[i];
+ break;
+ }
+ }
+
+ if (!mode)
+ return 0;
+
+ return mode->vht_capab != 0;
+}
+
+
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ struct hostapd_freq_params *freq)
+{
+ enum hostapd_hw_mode hw_mode;
+ struct hostapd_hw_modes *mode = NULL;
+ int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+ 184, 192 };
+ int vht80[] = { 36, 52, 100, 116, 132, 149 };
+ struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+ u8 channel;
+ int i, chan_idx, ht40 = -1, res, obss_scan = 1;
+ unsigned int j, k;
+ struct hostapd_freq_params vht_freq;
+ int chwidth, seg0, seg1;
+ u32 vht_caps = 0;
+
+ freq->freq = ssid->frequency;
+
+ for (j = 0; j < wpa_s->last_scan_res_used; j++) {
+ struct wpa_bss *bss = wpa_s->last_scan_res[j];
+
+ if (ssid->mode != WPAS_MODE_IBSS)
+ break;
+
+ /* Don't adjust control freq in case of fixed_freq */
+ if (ssid->fixed_freq)
+ break;
+
+ if (!bss_is_ibss(bss))
+ continue;
+
+ if (ssid->ssid_len == bss->ssid_len &&
+ os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "IBSS already found in scan results, adjust control freq: %d",
+ bss->freq);
+ freq->freq = bss->freq;
+ obss_scan = 0;
+ break;
+ }
+ }
+
+ /* For IBSS check HT_IBSS flag */
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_HT_IBSS))
+ return;
+
+ if (wpa_s->group_cipher == WPA_CIPHER_WEP40 ||
+ wpa_s->group_cipher == WPA_CIPHER_WEP104 ||
+ wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+ wpa_printf(MSG_DEBUG,
+ "IBSS: WEP/TKIP detected, do not try to enable HT");
+ return;
+ }
+
+ hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+ for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].mode == hw_mode) {
+ mode = &wpa_s->hw.modes[i];
+ break;
+ }
+ }
+
+ if (!mode)
+ return;
+
+ freq->ht_enabled = ht_supported(mode);
+ if (!freq->ht_enabled)
+ return;
+
+ /* Setup higher BW only for 5 GHz */
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return;
+
+ for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) {
+ pri_chan = &mode->channels[chan_idx];
+ if (pri_chan->chan == channel)
+ break;
+ pri_chan = NULL;
+ }
+ if (!pri_chan)
+ return;
+
+ /* Check primary channel flags */
+ if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+ return;
+
+ /* Check/setup HT40+/HT40- */
+ for (j = 0; j < ARRAY_SIZE(ht40plus); j++) {
+ if (ht40plus[j] == channel) {
+ ht40 = 1;
+ break;
+ }
+ }
+
+ /* Find secondary channel */
+ for (i = 0; i < mode->num_channels; i++) {
+ sec_chan = &mode->channels[i];
+ if (sec_chan->chan == channel + ht40 * 4)
+ break;
+ sec_chan = NULL;
+ }
+ if (!sec_chan)
+ return;
+
+ /* Check secondary channel flags */
+ if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+ return;
+
+ freq->channel = pri_chan->chan;
+
+ switch (ht40) {
+ case -1:
+ if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
+ return;
+ freq->sec_channel_offset = -1;
+ break;
+ case 1:
+ if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS))
+ return;
+ freq->sec_channel_offset = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (freq->sec_channel_offset && obss_scan) {
+ struct wpa_scan_results *scan_res;
+
+ scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+ if (scan_res == NULL) {
+ /* Back to HT20 */
+ freq->sec_channel_offset = 0;
+ return;
+ }
+
+ res = check_40mhz_5g(mode, scan_res, pri_chan->chan,
+ sec_chan->chan);
+ switch (res) {
+ case 0:
+ /* Back to HT20 */
+ freq->sec_channel_offset = 0;
+ break;
+ case 1:
+ /* Configuration allowed */
+ break;
+ case 2:
+ /* Switch pri/sec channels */
+ freq->freq = hw_get_freq(mode, sec_chan->chan);
+ freq->sec_channel_offset = -freq->sec_channel_offset;
+ freq->channel = sec_chan->chan;
+ break;
+ default:
+ freq->sec_channel_offset = 0;
+ break;
+ }
+
+ wpa_scan_results_free(scan_res);
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
+ freq->channel, freq->sec_channel_offset);
+
+ if (!drv_supports_vht(wpa_s, ssid))
+ return;
+
+ /* For IBSS check VHT_IBSS flag */
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+ return;
+
+ vht_freq = *freq;
+
+ vht_freq.vht_enabled = vht_supported(mode);
+ if (!vht_freq.vht_enabled)
+ return;
+
+ /* setup center_freq1, bandwidth */
+ for (j = 0; j < ARRAY_SIZE(vht80); j++) {
+ if (freq->channel >= vht80[j] &&
+ freq->channel < vht80[j] + 16)
+ break;
+ }
+
+ if (j == ARRAY_SIZE(vht80))
+ return;
+
+ for (i = vht80[j]; i < vht80[j] + 16; i += 4) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_chan(mode, i, NULL);
+ if (!chan)
+ return;
+
+ /* Back to HT configuration if channel not usable */
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+ return;
+ }
+
+ chwidth = VHT_CHANWIDTH_80MHZ;
+ seg0 = vht80[j] + 6;
+ seg1 = 0;
+
+ if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) {
+ /* setup center_freq2, bandwidth */
+ for (k = 0; k < ARRAY_SIZE(vht80); k++) {
+ /* Only accept 80 MHz segments separated by a gap */
+ if (j == k || abs(vht80[j] - vht80[k]) == 16)
+ continue;
+ for (i = vht80[k]; i < vht80[k] + 16; i += 4) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_chan(mode, i, NULL);
+ if (!chan)
+ continue;
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+
+ /* Found a suitable second segment for 80+80 */
+ chwidth = VHT_CHANWIDTH_80P80MHZ;
+ vht_caps |=
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ seg1 = vht80[k] + 6;
+ }
+
+ if (chwidth == VHT_CHANWIDTH_80P80MHZ)
+ break;
+ }
+ } else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_160MHZ) {
+ if (freq->freq == 5180) {
+ chwidth = VHT_CHANWIDTH_160MHZ;
+ vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ seg0 = 50;
+ } else if (freq->freq == 5520) {
+ chwidth = VHT_CHANWIDTH_160MHZ;
+ vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ seg0 = 114;
+ }
+ }
+
+ if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
+ freq->channel, freq->ht_enabled,
+ vht_freq.vht_enabled,
+ freq->sec_channel_offset,
+ chwidth, seg0, seg1, vht_caps) != 0)
+ return;
+
+ *freq = vht_freq;
+
+ wpa_printf(MSG_DEBUG, "IBSS: VHT setup freq cf1 %d, cf2 %d, bw %d",
+ freq->center_freq1, freq->center_freq2, freq->bandwidth);
+}
+
+