nl80211: Support vendor scan together with normal scan

Allow wpa_supplicant to use vendor scan (if supported by the driver)
together with the normal nl80211 scan and handling external scan events.
Since this results in possibility of concurrent scan operations, some of
the operations related to scan results need to check more carefully when
an event is relevant for a specific interface.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Kanchanapally, Vidyullatha 2015-09-16 17:45:05 +05:30 committed by Jouni Malinen
parent f22a080cdc
commit adcd7c4b0b
6 changed files with 90 additions and 29 deletions

View file

@ -4381,6 +4381,9 @@ union wpa_event_data {
* @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
* SSID) * SSID)
* @num_ssids: Number of entries in ssids array * @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 { struct scan_info {
int aborted; int aborted;
@ -4388,6 +4391,8 @@ union wpa_event_data {
size_t num_freqs; size_t num_freqs;
struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
size_t num_ssids; size_t num_ssids;
int external_scan;
int nl_scan_event;
} scan_info; } scan_info;
/** /**

View file

@ -7224,6 +7224,17 @@ static int driver_nl80211_scan2(void *priv,
struct wpa_driver_scan_params *params) struct wpa_driver_scan_params *params)
{ {
struct i802_bss *bss = priv; 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); return wpa_driver_nl80211_scan(bss, params);
} }

View file

@ -184,6 +184,13 @@ struct wpa_driver_nl80211_data {
int auth_wep_tx_keyidx; int auth_wep_tx_keyidx;
int auth_local_state_change; int auth_local_state_change;
int auth_p2p; 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; struct nl_msg;

View file

@ -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, 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; union wpa_event_data event;
struct nlattr *nl; 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 freqs[MAX_REPORT_FREQS];
int num_freqs = 0; int num_freqs = 0;
if (drv->scan_for_auth) { if (!external_scan && drv->scan_for_auth) {
drv->scan_for_auth = 0; drv->scan_for_auth = 0;
wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
"cfg80211 BSS entry"); "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)); os_memset(&event, 0, sizeof(event));
info = &event.scan_info; info = &event.scan_info;
info->aborted = aborted; info->aborted = aborted;
info->external_scan = external_scan;
info->nl_scan_event = 1;
if (tb[NL80211_ATTR_SCAN_SSIDS]) { if (tb[NL80211_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { 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]; struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
u64 cookie = 0; u64 cookie = 0;
union wpa_event_data event;
struct scan_info *info;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
(struct nlattr *) data, len, NULL) || (struct nlattr *) data, len, NULL) ||
@ -1703,13 +1707,20 @@ static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv,
return; 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; 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, 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; union wpa_event_data event;
struct nlattr *nl; 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)); os_memset(&event, 0, sizeof(event));
info = &event.scan_info; info = &event.scan_info;
info->aborted = aborted; info->aborted = aborted;
info->external_scan = external_scan;
if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) { if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(nl, 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]; struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
u64 cookie = 0; u64 cookie = 0;
enum scan_status status; enum scan_status status;
int external_scan;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
(struct nlattr *) data, len, NULL) || (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]); cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
if (cookie != drv->vendor_scan_cookie) { if (cookie != drv->vendor_scan_cookie) {
/* Event from an external scan, get scan results */ /* Event from an external scan, get scan results */
external_scan = 1;
} else { } else {
external_scan = 0;
if (status == VENDOR_SCAN_STATUS_NEW_RESULTS) if (status == VENDOR_SCAN_STATUS_NEW_RESULTS)
drv->scan_state = SCAN_COMPLETED; drv->scan_state = SCAN_COMPLETED;
else 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, eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
drv->ctx); drv->ctx);
drv->vendor_scan_cookie = 0; 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; struct wpa_driver_nl80211_data *drv = bss->drv;
union wpa_event_data data; union wpa_event_data data;
int external_scan_event = 0;
wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
cmd, nl80211_command_to_string(cmd), bss->ifname); 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: case NL80211_CMD_NEW_SCAN_RESULTS:
wpa_dbg(drv->ctx, MSG_DEBUG, wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: New scan results available"); "nl80211: New scan results available");
drv->scan_state = SCAN_COMPLETED;
drv->scan_complete_events = 1; drv->scan_complete_events = 1;
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
drv->ctx); drv->scan_state = SCAN_COMPLETED;
send_scan_event(drv, 0, tb); 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; break;
case NL80211_CMD_SCHED_SCAN_RESULTS: case NL80211_CMD_SCHED_SCAN_RESULTS:
wpa_dbg(drv->ctx, MSG_DEBUG, wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: New sched scan results available"); "nl80211: New sched scan results available");
drv->scan_state = SCHED_SCAN_RESULTS; drv->scan_state = SCHED_SCAN_RESULTS;
send_scan_event(drv, 0, tb); send_scan_event(drv, 0, tb, 0);
break; break;
case NL80211_CMD_SCAN_ABORTED: case NL80211_CMD_SCAN_ABORTED:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
drv->scan_state = SCAN_ABORTED; drv->scan_state = SCAN_ABORTED;
/* /*
* Need to indicate that scan results are available in order * Need to indicate that scan results are available in
* not to make wpa_supplicant stop its scanning. * order not to make wpa_supplicant stop its scanning.
*/ */
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
drv->ctx); drv, drv->ctx);
send_scan_event(drv, 1, tb); drv->last_scan_cmd = 0;
} else {
external_scan_event = 1;
}
send_scan_event(drv, 1, tb, external_scan_event);
break; break;
case NL80211_CMD_AUTHENTICATE: case NL80211_CMD_AUTHENTICATE:
case NL80211_CMD_ASSOCIATE: case NL80211_CMD_ASSOCIATE:

View file

@ -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_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
drv, drv->ctx); drv, drv->ctx);
drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN;
fail: fail:
nlmsg_free(msg); 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_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout, eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout,
drv, drv->ctx); drv, drv->ctx);
drv->last_scan_cmd = NL80211_CMD_VENDOR;
fail: fail:
nlmsg_free(msg); nlmsg_free(msg);

View file

@ -1417,6 +1417,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
return -1; return -1;
if (!own_request) if (!own_request)
return -1; return -1;
if (data && data->scan_info.external_scan)
return -1;
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try " wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
"scanning again"); "scanning again");
wpa_supplicant_req_new_scan(wpa_s, 1, 0); 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 */ #endif /* CONFIG_NO_RANDOM_POOL */
if (own_request && wpa_s->scan_res_handler && 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, void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res); 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_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 && 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_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
wpa_s->manual_scan_id); wpa_s->manual_scan_id);
wpa_s->manual_scan_use_id = 0; 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); 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_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
wpa_scan_results_free(scan_res); wpa_scan_results_free(scan_res);
return 0; return 0;
@ -1506,7 +1510,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
wpa_scan_results_free(scan_res); 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; struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL; wpa_s->scan_work = NULL;
radio_work_done(work); radio_work_done(work);
@ -1516,7 +1520,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
scan_work_done: scan_work_done:
wpa_scan_results_free(scan_res); 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; struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL; wpa_s->scan_work = NULL;
radio_work_done(work); radio_work_done(work);
@ -3257,10 +3261,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break; break;
#ifndef CONFIG_NO_SCAN_PROCESSING #ifndef CONFIG_NO_SCAN_PROCESSING
case EVENT_SCAN_STARTED: 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; struct os_reltime diff;
os_get_reltime(&wpa_s->scan_start_time);
os_reltime_sub(&wpa_s->scan_start_time, os_reltime_sub(&wpa_s->scan_start_time,
&wpa_s->scan_trigger_time, &diff); &wpa_s->scan_trigger_time, &diff);
wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds", 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; break;
case EVENT_SCAN_RESULTS: 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; struct os_reltime now, diff;
os_get_reltime(&now); os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->scan_start_time, &diff); os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
@ -3294,7 +3300,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
} }
if (wpa_supplicant_event_scan_results(wpa_s, data)) if (wpa_supplicant_event_scan_results(wpa_s, data))
break; /* interface may have been removed */ break; /* interface may have been removed */
if (!(data && data->scan_info.external_scan))
wpa_s->own_scan_running = 0; wpa_s->own_scan_running = 0;
if (data && data->scan_info.nl_scan_event)
wpa_s->radio->external_scan_running = 0; wpa_s->radio->external_scan_running = 0;
radio_work_check_next(wpa_s); radio_work_check_next(wpa_s);
break; break;