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>
* @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
* SSID)
* @num_ssids: Number of entries in ssids array
* @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;
*/
struct scan_info {
int aborted;
size_t num_freqs;
struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
size_t num_ssids;
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;
struct wpa_driver_scan_params *params)
{
struct i802_bss *bss = 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);
}
return wpa_driver_nl80211_scan(bss, params);
}
int auth_wep_tx_keyidx;
int auth_local_state_change;
int auth_p2p;
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;
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[], int external_scan)
{
union wpa_event_data event;
struct nlattr *nl;
{
union wpa_event_data event;
struct nlattr *nl;
int freqs[MAX_REPORT_FREQS];
int num_freqs = 0;
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");
drv->scan_for_auth = 0;
wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
"cfg80211 BSS entry");
os_memset(&event, 0, sizeof(event));
info = &event.scan_info;
info->aborted = 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) {
if (tb[NL80211_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
{
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
u64 cookie = 0;
{
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) ||
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
(struct nlattr *) data, len, NULL) ||
+ /* 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;
struct nlattr *nl;
{
union wpa_event_data event;
struct nlattr *nl;
os_memset(&event, 0, sizeof(event));
info = &event.scan_info;
info->aborted = aborted;
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,
if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(nl,
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
u64 cookie = 0;
enum scan_status status;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
u64 cookie = 0;
enum scan_status status;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
(struct nlattr *) data, len, NULL) ||
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
(struct nlattr *) data, len, NULL) ||
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 */
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 */
if (status == VENDOR_SCAN_STATUS_NEW_RESULTS)
drv->scan_state = SCAN_COMPLETED;
else
if (status == VENDOR_SCAN_STATUS_NEW_RESULTS)
drv->scan_state = SCAN_COMPLETED;
else
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
drv->ctx);
drv->vendor_scan_cookie = 0;
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);
{
struct wpa_driver_nl80211_data *drv = bss->drv;
union wpa_event_data data;
{
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);
wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
cmd, nl80211_command_to_string(cmd), bss->ifname);
case NL80211_CMD_NEW_SCAN_RESULTS:
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: New scan results available");
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;
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;
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");
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:
break;
case NL80211_CMD_AUTHENTICATE:
case NL80211_CMD_ASSOCIATE:
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
eloop_register_timeout(timeout, 0, 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,
drv, drv->ctx);
+ drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN;
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
eloop_register_timeout(30, 0, 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,
drv, drv->ctx);
+ drv->last_scan_cmd = NL80211_CMD_VENDOR;
return -1;
if (!own_request)
return -1;
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);
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
"scanning again");
wpa_supplicant_req_new_scan(wpa_s, 1, 0);
#endif /* CONFIG_NO_RANDOM_POOL */
if (own_request && wpa_s->scan_res_handler &&
#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);
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
}
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_s->manual_scan_id);
wpa_s->manual_scan_use_id = 0;
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;
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_scan_results_free(scan_res);
return 0;
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;
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;
wpa_s->scan_work = NULL;
radio_work_done(work);
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
scan_work_done:
wpa_scan_results_free(scan_res);
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);
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
break;
#ifndef CONFIG_NO_SCAN_PROCESSING
case EVENT_SCAN_STARTED:
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)) {
+ 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",
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",
}
break;
case EVENT_SCAN_RESULTS:
}
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);
struct os_reltime now, diff;
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
}
if (wpa_supplicant_event_scan_results(wpa_s, data))
break; /* interface may have been removed */
}
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 */
radio_work_check_next(wpa_s);
break;
#endif /* CONFIG_NO_SCAN_PROCESSING */