diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index bc1b65e81..b0ea7a2a0 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Driver event processing - * Copyright (c) 2003-2012, Jouni Malinen + * Copyright (c) 2003-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1181,7 +1181,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data, int own_request) { - struct wpa_scan_results *scan_res; + struct wpa_scan_results *scan_res = NULL; + int ret = 0; int ap = 0; #ifndef CONFIG_NO_RANDOM_POOL size_t i, num; @@ -1206,7 +1207,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpa_s->scan_req = wpa_s->last_scan_req; wpa_s->sta_scan_pending = 1; wpa_supplicant_req_scan(wpa_s, 5, 0); - return -1; + ret = -1; + goto scan_work_done; } } wpa_s->sta_scan_pending = 0; @@ -1224,7 +1226,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try " "scanning again"); wpa_supplicant_req_new_scan(wpa_s, 1, 0); - return -1; + ret = -1; + goto scan_work_done; } #ifndef CONFIG_NO_RANDOM_POOL @@ -1251,9 +1254,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, scan_res_handler = wpa_s->scan_res_handler; wpa_s->scan_res_handler = NULL; scan_res_handler(wpa_s, scan_res); - - wpa_scan_results_free(scan_res); - return -2; + ret = -2; + goto scan_work_done; } if (ap) { @@ -1262,8 +1264,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, if (wpa_s->ap_iface->scan_cb) wpa_s->ap_iface->scan_cb(wpa_s->ap_iface); #endif /* CONFIG_AP */ - wpa_scan_results_free(scan_res); - return 0; + goto scan_work_done; } wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)", @@ -1286,38 +1287,44 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, return 0; } - if (sme_proc_obss_scan(wpa_s) > 0) { - wpa_scan_results_free(scan_res); - return 0; - } + if (sme_proc_obss_scan(wpa_s) > 0) + goto scan_work_done; - if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) { - wpa_scan_results_free(scan_res); - return 0; - } + if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) + goto scan_work_done; - if (autoscan_notify_scan(wpa_s, scan_res)) { - wpa_scan_results_free(scan_res); - return 0; - } + if (autoscan_notify_scan(wpa_s, scan_res)) + goto scan_work_done; if (wpa_s->disconnected) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); - wpa_scan_results_free(scan_res); - return 0; + goto scan_work_done; } if (!wpas_driver_bss_selection(wpa_s) && - bgscan_notify_scan(wpa_s, scan_res) == 1) { - wpa_scan_results_free(scan_res); - return 0; - } + bgscan_notify_scan(wpa_s, scan_res) == 1) + goto scan_work_done; wpas_wps_update_ap_info(wpa_s, scan_res); wpa_scan_results_free(scan_res); + if (wpa_s->scan_work) { + struct wpa_radio_work *work = wpa_s->scan_work; + wpa_s->scan_work = NULL; + radio_work_done(work); + } + return wpas_select_network_from_last_scan(wpa_s, 1, own_request); + +scan_work_done: + wpa_scan_results_free(scan_res); + if (wpa_s->scan_work) { + struct wpa_radio_work *work = wpa_s->scan_work; + wpa_s->scan_work = NULL; + radio_work_done(work); + } + return ret; } diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 428a66850..763fc133f 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -142,6 +142,40 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, } +static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpa_driver_scan_params *params = work->ctx; + int ret; + + if (deinit) { + wpa_scan_free_params(params); + return; + } + + wpa_supplicant_notify_scanning(wpa_s, 1); + + if (wpa_s->clear_driver_scan_cache) + params->only_new_results = 1; + ret = wpa_drv_scan(wpa_s, params); + wpa_scan_free_params(params); + work->ctx = NULL; + if (ret) { + wpa_supplicant_notify_scanning(wpa_s, 0); + wpas_notify_scan_done(wpa_s, 0); + radio_work_done(work); + return; + } + + os_get_reltime(&wpa_s->scan_trigger_time); + wpa_s->scan_runs++; + wpa_s->normal_scans++; + wpa_s->own_scan_requested = 1; + wpa_s->clear_driver_scan_cache = 0; + wpa_s->scan_work = work; +} + + /** * wpa_supplicant_trigger_scan - Request driver to start a scan * @wpa_s: Pointer to wpa_supplicant data @@ -151,25 +185,24 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { - int ret; + struct wpa_driver_scan_params *ctx; - wpa_supplicant_notify_scanning(wpa_s, 1); - - if (wpa_s->clear_driver_scan_cache) - params->only_new_results = 1; - ret = wpa_drv_scan(wpa_s, params); - if (ret) { - wpa_supplicant_notify_scanning(wpa_s, 0); - wpas_notify_scan_done(wpa_s, 0); - } else { - os_get_reltime(&wpa_s->scan_trigger_time); - wpa_s->scan_runs++; - wpa_s->normal_scans++; - wpa_s->own_scan_requested = 1; - wpa_s->clear_driver_scan_cache = 0; + if (wpa_s->scan_work) { + wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending"); + return -1; } - return ret; + ctx = wpa_scan_clone_params(params); + if (ctx == NULL) + return -1; + + if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0) + { + wpa_scan_free_params(ctx); + return -1; + } + + return 0; } @@ -1699,6 +1732,11 @@ void scan_only_handler(struct wpa_supplicant *wpa_s, } wpas_notify_scan_results(wpa_s); wpas_notify_scan_done(wpa_s, 1); + if (wpa_s->scan_work) { + struct wpa_radio_work *work = wpa_s->scan_work; + wpa_s->scan_work = NULL; + radio_work_done(work); + } } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index fa819ec7e..f04d60d49 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -2903,6 +2903,13 @@ static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s, static void radio_work_free(struct wpa_radio_work *work) { + if (work->wpa_s->scan_work == work) { + /* This should not really happen. */ + wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as scan_work", + work->type, work, work->started); + work->wpa_s->scan_work = NULL; + } + dl_list_del(&work->list); os_free(work); } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 46db3dca0..c2d0a36a8 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -442,6 +442,7 @@ struct wpa_supplicant { struct ctrl_iface_priv *ctrl_iface; enum wpa_states wpa_state; + struct wpa_radio_work *scan_work; int scanning; int sched_scanning; int new_connection;