diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 3cdab5a7a..6668a5e6d 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -4381,6 +4381,9 @@ union wpa_event_data { * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard * SSID) * @num_ssids: Number of entries in ssids array + * @external_scan: Whether the scan info is for an external scan + * @nl_scan_event: 1 if the source of this scan event is a normal scan, + * 0 if the source of the scan event is a vendor scan */ struct scan_info { int aborted; @@ -4388,6 +4391,8 @@ union wpa_event_data { size_t num_freqs; struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; size_t num_ssids; + int external_scan; + int nl_scan_event; } scan_info; /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 00b173f3f..59e880ab4 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -7224,6 +7224,17 @@ static int driver_nl80211_scan2(void *priv, struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + /* + * Do a vendor specific scan if possible. If only_new_results is + * set, do a normal scan since a kernel (cfg80211) BSS cache flush + * cannot be achieved through a vendor scan. The below condition may + * need to be modified if new scan flags are added in the future whose + * functionality can only be achieved through a normal scan. + */ + if (drv->scan_vendor_cmd_avail && !params->only_new_results) + return wpa_driver_nl80211_vendor_scan(bss, params); return wpa_driver_nl80211_scan(bss, params); } diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index d9cb4397d..ea5f0583e 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -184,6 +184,13 @@ struct wpa_driver_nl80211_data { int auth_wep_tx_keyidx; int auth_local_state_change; int auth_p2p; + + /* + * Tells whether the last scan issued from wpa_supplicant was a normal + * scan (NL80211_CMD_TRIGGER_SCAN) or a vendor scan + * (NL80211_CMD_VENDOR). 0 if no pending scan request. + */ + int last_scan_cmd; }; struct nl_msg; diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 647280c8f..5f2811cec 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -968,7 +968,7 @@ static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, - struct nlattr *tb[]) + struct nlattr *tb[], int external_scan) { union wpa_event_data event; struct nlattr *nl; @@ -978,7 +978,7 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; - if (drv->scan_for_auth) { + if (!external_scan && drv->scan_for_auth) { drv->scan_for_auth = 0; wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " "cfg80211 BSS entry"); @@ -989,6 +989,8 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->aborted = aborted; + info->external_scan = external_scan; + info->nl_scan_event = 1; if (tb[NL80211_ATTR_SCAN_SSIDS]) { nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { @@ -1691,6 +1693,8 @@ static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv, { struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; u64 cookie = 0; + union wpa_event_data event; + struct scan_info *info; if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, (struct nlattr *) data, len, NULL) || @@ -1703,13 +1707,20 @@ static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv, return; } + /* Cookie match, own scan */ + os_memset(&event, 0, sizeof(event)); + info = &event.scan_info; + info->external_scan = 0; + info->nl_scan_event = 0; + drv->scan_state = SCAN_STARTED; - wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event); } static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv, - int aborted, struct nlattr *tb[]) + int aborted, struct nlattr *tb[], + int external_scan) { union wpa_event_data event; struct nlattr *nl; @@ -1721,6 +1732,7 @@ static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv, os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->aborted = aborted; + info->external_scan = external_scan; if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) { nla_for_each_nested(nl, @@ -1774,6 +1786,7 @@ static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; u64 cookie = 0; enum scan_status status; + int external_scan; if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, (struct nlattr *) data, len, NULL) || @@ -1788,7 +1801,9 @@ static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv, cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); if (cookie != drv->vendor_scan_cookie) { /* Event from an external scan, get scan results */ + external_scan = 1; } else { + external_scan = 0; if (status == VENDOR_SCAN_STATUS_NEW_RESULTS) drv->scan_state = SCAN_COMPLETED; else @@ -1797,9 +1812,11 @@ static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv, eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); drv->vendor_scan_cookie = 0; + drv->last_scan_cmd = 0; } - send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb); + send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb, + external_scan); } @@ -1954,6 +1971,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, { struct wpa_driver_nl80211_data *drv = bss->drv; union wpa_event_data data; + int external_scan_event = 0; wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", cmd, nl80211_command_to_string(cmd), bss->ifname); @@ -2006,28 +2024,38 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, case NL80211_CMD_NEW_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New scan results available"); - drv->scan_state = SCAN_COMPLETED; drv->scan_complete_events = 1; - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); - send_scan_event(drv, 0, tb); + if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { + drv->scan_state = SCAN_COMPLETED; + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + drv->last_scan_cmd = 0; + } else { + external_scan_event = 1; + } + send_scan_event(drv, 0, tb, external_scan_event); break; case NL80211_CMD_SCHED_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New sched scan results available"); drv->scan_state = SCHED_SCAN_RESULTS; - send_scan_event(drv, 0, tb); + send_scan_event(drv, 0, tb, 0); break; case NL80211_CMD_SCAN_ABORTED: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); - drv->scan_state = SCAN_ABORTED; - /* - * Need to indicate that scan results are available in order - * not to make wpa_supplicant stop its scanning. - */ - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); - send_scan_event(drv, 1, tb); + if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { + drv->scan_state = SCAN_ABORTED; + /* + * Need to indicate that scan results are available in + * order not to make wpa_supplicant stop its scanning. + */ + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + drv->last_scan_cmd = 0; + } else { + external_scan_event = 1; + } + send_scan_event(drv, 1, tb, external_scan_event); break; case NL80211_CMD_AUTHENTICATE: case NL80211_CMD_ASSOCIATE: diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index b3b495557..1ef08261c 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -298,6 +298,7 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN; fail: nlmsg_free(msg); @@ -963,6 +964,7 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + drv->last_scan_cmd = NL80211_CMD_VENDOR; fail: nlmsg_free(msg); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 3af1c7d89..23cf127f2 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1417,6 +1417,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, return -1; if (!own_request) return -1; + if (data && data->scan_info.external_scan) + return -1; wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try " "scanning again"); wpa_supplicant_req_new_scan(wpa_s, 1, 0); @@ -1441,7 +1443,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, #endif /* CONFIG_NO_RANDOM_POOL */ if (own_request && wpa_s->scan_res_handler && - (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) { + !(data && data->scan_info.external_scan)) { void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); @@ -1462,9 +1464,11 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, } wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)", - wpa_s->own_scan_running, wpa_s->radio->external_scan_running); + wpa_s->own_scan_running, + data ? data->scan_info.external_scan : 0); if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && - wpa_s->manual_scan_use_id && wpa_s->own_scan_running) { + wpa_s->manual_scan_use_id && wpa_s->own_scan_running && + own_request && !(data && data->scan_info.external_scan)) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", wpa_s->manual_scan_id); wpa_s->manual_scan_use_id = 0; @@ -1475,7 +1479,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpas_notify_scan_done(wpa_s, 1); - if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) { + if (data && data->scan_info.external_scan) { wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection"); wpa_scan_results_free(scan_res); return 0; @@ -1506,7 +1510,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpa_scan_results_free(scan_res); - if (wpa_s->scan_work) { + if (own_request && wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); @@ -1516,7 +1520,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, scan_work_done: wpa_scan_results_free(scan_res); - if (wpa_s->scan_work) { + if (own_request && wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); @@ -3257,10 +3261,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #ifndef CONFIG_NO_SCAN_PROCESSING case EVENT_SCAN_STARTED: - os_get_reltime(&wpa_s->scan_start_time); - if (wpa_s->own_scan_requested) { + if (wpa_s->own_scan_requested || + (data && !data->scan_info.external_scan)) { struct os_reltime diff; + os_get_reltime(&wpa_s->scan_start_time); os_reltime_sub(&wpa_s->scan_start_time, &wpa_s->scan_trigger_time, &diff); wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds", @@ -3283,7 +3288,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } break; case EVENT_SCAN_RESULTS: - if (os_reltime_initialized(&wpa_s->scan_start_time)) { + if (!(data && data->scan_info.external_scan) && + os_reltime_initialized(&wpa_s->scan_start_time)) { struct os_reltime now, diff; os_get_reltime(&now); os_reltime_sub(&now, &wpa_s->scan_start_time, &diff); @@ -3294,8 +3300,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } if (wpa_supplicant_event_scan_results(wpa_s, data)) break; /* interface may have been removed */ - wpa_s->own_scan_running = 0; - wpa_s->radio->external_scan_running = 0; + if (!(data && data->scan_info.external_scan)) + wpa_s->own_scan_running = 0; + if (data && data->scan_info.nl_scan_event) + wpa_s->radio->external_scan_running = 0; radio_work_check_next(wpa_s); break; #endif /* CONFIG_NO_SCAN_PROCESSING */