hostapd: Allow ACS to be offloaded to the driver
[mech_eap.git] / src / ap / acs.c
index f58b091..97cf26f 100644 (file)
@@ -284,6 +284,7 @@ static void acs_fail(struct hostapd_iface *iface)
 {
        wpa_printf(MSG_ERROR, "ACS: Failed to start");
        acs_cleanup(iface);
+       hostapd_disable_iface(iface);
 }
 
 
@@ -367,6 +368,19 @@ static int acs_usable_ht40_chan(struct hostapd_channel_data *chan)
 }
 
 
+static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
+{
+       const int allowed[] = { 36, 52, 100, 116, 132, 149 };
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(allowed); i++)
+               if (chan->chan == allowed[i])
+                       return 1;
+
+       return 0;
+}
+
+
 static int acs_survey_is_sufficient(struct freq_survey *survey)
 {
        if (!(survey->filled & SURVEY_HAS_NF)) {
@@ -541,6 +555,15 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
                        continue;
                }
 
+               if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+                   iface->conf->ieee80211ac &&
+                   iface->conf->vht_oper_chwidth == 1 &&
+                   !acs_usable_vht80_chan(chan)) {
+                       wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80",
+                                  chan->chan);
+                       continue;
+               }
+
                factor = 0;
                if (acs_usable_chan(chan))
                        factor = chan->interference_factor;
@@ -616,23 +639,26 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
 
 static void acs_adjust_vht_center_freq(struct hostapd_iface *iface)
 {
+       int offset;
+
        wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
 
        switch (iface->conf->vht_oper_chwidth) {
        case VHT_CHANWIDTH_USE_HT:
-               iface->conf->vht_oper_centr_freq_seg0_idx =
-                       iface->conf->channel + 2;
+               offset = 2 * iface->conf->secondary_channel;
                break;
        case VHT_CHANWIDTH_80MHZ:
-               iface->conf->vht_oper_centr_freq_seg0_idx =
-                       iface->conf->channel + 6;
+               offset = 6;
                break;
        default:
                /* TODO: How can this be calculated? Adjust
                 * acs_find_ideal_chan() */
                wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now");
-               break;
+               return;
        }
+
+       iface->conf->vht_oper_centr_freq_seg0_idx =
+               iface->conf->channel + offset;
 }
 
 
@@ -723,7 +749,7 @@ static void acs_scan_complete(struct hostapd_iface *iface)
        err = hostapd_drv_get_survey(iface->bss[0], 0);
        if (err) {
                wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
-               acs_fail(iface);
+               goto fail;
        }
 
        if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
@@ -775,6 +801,7 @@ static int acs_request_scan(struct hostapd_iface *iface)
        if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
                wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
                acs_cleanup(iface);
+               os_free(params.freqs);
                return -1;
        }
 
@@ -789,6 +816,14 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
 
        wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
 
+       if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
+               wpa_printf(MSG_INFO, "ACS: Offloading to driver");
+               err = hostapd_drv_do_acs(iface->bss[0]);
+               if (err)
+                       return HOSTAPD_CHAN_INVALID;
+               return HOSTAPD_CHAN_ACS;
+       }
+
        acs_cleanup(iface);
 
        err = acs_request_scan(iface);