Add scheduled scan driver operations
In new Linux kernel versions (>=3.0), nl80211 adds scheduled scan capability. In order to use this feature to its full extent, we need to support it in the wpa_supplicant core, so that it can also be used by other drivers. This commit adds initial scheduled scan support operations and events. Signed-off-by: Luciano Coelho <coelho@ti.com>
This commit is contained in:
parent
5f738a21a6
commit
cbdf3507e9
8 changed files with 271 additions and 2 deletions
|
@ -694,6 +694,8 @@ struct wpa_driver_capa {
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
|
||||||
int max_scan_ssids;
|
int max_scan_ssids;
|
||||||
|
int max_sched_scan_ssids;
|
||||||
|
int sched_scan_supported;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* max_remain_on_chan - Maximum remain-on-channel duration in msec
|
* max_remain_on_chan - Maximum remain-on-channel duration in msec
|
||||||
|
@ -2455,6 +2457,35 @@ struct wpa_driver_ops {
|
||||||
* the station gets added by FT-over-DS.
|
* the station gets added by FT-over-DS.
|
||||||
*/
|
*/
|
||||||
int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
|
int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sched_scan - Request the driver to initiate scheduled scan
|
||||||
|
* @priv: Private driver interface data
|
||||||
|
* @params: Scan parameters
|
||||||
|
* @interval: Interval between scan cycles in milliseconds
|
||||||
|
* Returns: 0 on success, -1 on failure
|
||||||
|
*
|
||||||
|
* This operation should be used for scheduled scan offload to
|
||||||
|
* the hardware. Every time scan results are available, the
|
||||||
|
* driver should report scan results event for wpa_supplicant
|
||||||
|
* which will eventually request the results with
|
||||||
|
* wpa_driver_get_scan_results2(). This operation is optional
|
||||||
|
* and if not provided or if it returns -1, we fall back to
|
||||||
|
* normal host-scheduled scans.
|
||||||
|
*/
|
||||||
|
int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params,
|
||||||
|
u32 interval);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stop_sched_scan - Request the driver to stop a scheduled scan
|
||||||
|
* @priv: Private driver interface data
|
||||||
|
* Returns: 0 on success, -1 on failure
|
||||||
|
*
|
||||||
|
* This should cause the scheduled scan to be stopped and
|
||||||
|
* results should stop being sent. Must be supported if
|
||||||
|
* sched_scan is supported.
|
||||||
|
*/
|
||||||
|
int (*stop_sched_scan)(void *priv);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -2867,7 +2898,12 @@ enum wpa_event_type {
|
||||||
* completed Group Key Handshake while the host (including
|
* completed Group Key Handshake while the host (including
|
||||||
* wpa_supplicant was sleeping).
|
* wpa_supplicant was sleeping).
|
||||||
*/
|
*/
|
||||||
EVENT_DRIVER_GTK_REKEY
|
EVENT_DRIVER_GTK_REKEY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
|
||||||
|
*/
|
||||||
|
EVENT_SCHED_SCAN_STOPPED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3331,5 +3331,7 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
|
||||||
NULL /* sta_assoc */,
|
NULL /* sta_assoc */,
|
||||||
NULL /* sta_auth */,
|
NULL /* sta_auth */,
|
||||||
NULL /* add_tspec */,
|
NULL /* add_tspec */,
|
||||||
NULL /* add_sta_node */
|
NULL /* add_sta_node */,
|
||||||
|
NULL /* sched_scan */,
|
||||||
|
NULL /* stop_sched_scan */
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,6 +79,23 @@ static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
|
||||||
|
struct wpa_driver_scan_params *params,
|
||||||
|
u32 interval)
|
||||||
|
{
|
||||||
|
if (wpa_s->driver->sched_scan)
|
||||||
|
return wpa_s->driver->sched_scan(wpa_s->drv_priv,
|
||||||
|
params, interval);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int wpa_drv_stop_sched_scan(struct wpa_supplicant *wpa_s)
|
||||||
|
{
|
||||||
|
if (wpa_s->driver->stop_sched_scan)
|
||||||
|
return wpa_s->driver->stop_sched_scan(wpa_s->drv_priv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct wpa_scan_results * wpa_drv_get_scan_results2(
|
static inline struct wpa_scan_results * wpa_drv_get_scan_results2(
|
||||||
struct wpa_supplicant *wpa_s)
|
struct wpa_supplicant *wpa_s)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2263,6 +2263,17 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
|
||||||
wpa_sm_update_replay_ctr(wpa_s->wpa,
|
wpa_sm_update_replay_ctr(wpa_s->wpa,
|
||||||
data->driver_gtk_rekey.replay_ctr);
|
data->driver_gtk_rekey.replay_ctr);
|
||||||
break;
|
break;
|
||||||
|
case EVENT_SCHED_SCAN_STOPPED:
|
||||||
|
wpa_s->sched_scanning = 0;
|
||||||
|
wpa_supplicant_notify_scanning(wpa_s, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we timed out, start a new sched scan to continue
|
||||||
|
* searching for more SSIDs.
|
||||||
|
*/
|
||||||
|
if (wpa_s->sched_scan_timed_out)
|
||||||
|
wpa_supplicant_req_sched_scan(wpa_s);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
|
wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -215,6 +215,54 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
|
||||||
|
{
|
||||||
|
struct wpa_supplicant *wpa_s = eloop_ctx;
|
||||||
|
|
||||||
|
wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it");
|
||||||
|
|
||||||
|
wpa_s->sched_scan_timed_out = 1;
|
||||||
|
wpa_supplicant_cancel_sched_scan(wpa_s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
|
||||||
|
struct wpa_driver_scan_params *params,
|
||||||
|
int interval)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
wpa_supplicant_notify_scanning(wpa_s, 1);
|
||||||
|
ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
|
||||||
|
if (ret)
|
||||||
|
wpa_supplicant_notify_scanning(wpa_s, 0);
|
||||||
|
else
|
||||||
|
wpa_s->sched_scanning = 1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = wpa_drv_stop_sched_scan(wpa_s);
|
||||||
|
if (ret) {
|
||||||
|
wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!");
|
||||||
|
/* TODO: what to do if stopping fails? */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct wpa_driver_scan_filter *
|
static struct wpa_driver_scan_filter *
|
||||||
wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
|
wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
|
||||||
{
|
{
|
||||||
|
@ -566,6 +614,130 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
|
||||||
|
* @wpa_s: Pointer to wpa_supplicant data
|
||||||
|
*
|
||||||
|
* This function is used to schedule periodic scans for neighboring
|
||||||
|
* access points repeating the scan continuously.
|
||||||
|
*/
|
||||||
|
int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
|
||||||
|
{
|
||||||
|
struct wpa_driver_scan_params params;
|
||||||
|
enum wpa_states prev_state;
|
||||||
|
struct wpa_ssid *ssid;
|
||||||
|
struct wpabuf *wps_ie = NULL;
|
||||||
|
int ret;
|
||||||
|
int use_wildcard = 0;
|
||||||
|
unsigned int max_sched_scan_ssids;
|
||||||
|
|
||||||
|
if (!wpa_s->sched_scan_supported)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
|
||||||
|
max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
|
||||||
|
else
|
||||||
|
max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
|
||||||
|
|
||||||
|
if (wpa_s->sched_scanning)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
|
||||||
|
prev_state = wpa_s->wpa_state;
|
||||||
|
if (wpa_s->wpa_state == WPA_DISCONNECTED ||
|
||||||
|
wpa_s->wpa_state == WPA_INACTIVE)
|
||||||
|
wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
|
||||||
|
|
||||||
|
/* Find the starting point from which to continue scanning */
|
||||||
|
ssid = wpa_s->conf->ssid;
|
||||||
|
if (wpa_s->prev_sched_ssid) {
|
||||||
|
while (ssid) {
|
||||||
|
if (ssid == wpa_s->prev_sched_ssid) {
|
||||||
|
ssid = ssid->next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ssid = ssid->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ssid || !wpa_s->prev_sched_ssid) {
|
||||||
|
wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
|
||||||
|
|
||||||
|
wpa_s->sched_scan_interval = 2;
|
||||||
|
wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
|
||||||
|
wpa_s->first_sched_scan = 1;
|
||||||
|
ssid = wpa_s->conf->ssid;
|
||||||
|
wpa_s->prev_sched_ssid = ssid;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ssid) {
|
||||||
|
if (ssid->disabled) {
|
||||||
|
wpa_s->prev_sched_ssid = ssid;
|
||||||
|
ssid = ssid->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ssid->scan_ssid)
|
||||||
|
use_wildcard = 1;
|
||||||
|
else {
|
||||||
|
params.ssids[params.num_ssids].ssid =
|
||||||
|
ssid->ssid;
|
||||||
|
params.ssids[params.num_ssids].ssid_len =
|
||||||
|
ssid->ssid_len;
|
||||||
|
params.num_ssids++;
|
||||||
|
if (params.num_ssids + 1 >= max_sched_scan_ssids) {
|
||||||
|
wpa_s->prev_sched_ssid = ssid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wpa_s->prev_sched_ssid = ssid;
|
||||||
|
ssid = ssid->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssid || use_wildcard) {
|
||||||
|
wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
|
||||||
|
"the sched scan request");
|
||||||
|
params.num_ssids++;
|
||||||
|
} else {
|
||||||
|
wpa_dbg(wpa_s, MSG_DEBUG, "ssid %p - list ended", ssid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.num_ssids)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (wpa_s->wps)
|
||||||
|
wps_ie = wpa_supplicant_extra_ies(wpa_s, ¶ms);
|
||||||
|
|
||||||
|
wpa_dbg(wpa_s, MSG_DEBUG,
|
||||||
|
"Starting sched scan: interval %d timeout %d",
|
||||||
|
wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
|
||||||
|
|
||||||
|
ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms,
|
||||||
|
wpa_s->sched_scan_interval);
|
||||||
|
wpabuf_free(wps_ie);
|
||||||
|
if (ret) {
|
||||||
|
wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
|
||||||
|
if (prev_state != wpa_s->wpa_state)
|
||||||
|
wpa_supplicant_set_state(wpa_s, prev_state);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have more SSIDs to scan, add a timeout so we scan them too */
|
||||||
|
if (ssid || !wpa_s->first_sched_scan) {
|
||||||
|
wpa_s->sched_scan_timed_out = 0;
|
||||||
|
eloop_register_timeout(wpa_s->sched_scan_timeout, 0,
|
||||||
|
wpa_supplicant_sched_scan_timeout,
|
||||||
|
wpa_s, NULL);
|
||||||
|
wpa_s->first_sched_scan = 0;
|
||||||
|
wpa_s->sched_scan_timeout /= 2;
|
||||||
|
wpa_s->sched_scan_interval *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wpa_supplicant_cancel_scan - Cancel a scheduled scan request
|
* wpa_supplicant_cancel_scan - Cancel a scheduled scan request
|
||||||
* @wpa_s: Pointer to wpa_supplicant data
|
* @wpa_s: Pointer to wpa_supplicant data
|
||||||
|
@ -580,6 +752,23 @@ void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wpa_supplicant_cancel_sched_scan - Stop running scheduled scans
|
||||||
|
* @wpa_s: Pointer to wpa_supplicant data
|
||||||
|
*
|
||||||
|
* This function is used to stop a periodic scheduled scan.
|
||||||
|
*/
|
||||||
|
void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
|
||||||
|
{
|
||||||
|
if (!wpa_s->sched_scanning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
|
||||||
|
eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
|
||||||
|
wpa_supplicant_stop_sched_scan(wpa_s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
|
void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
|
||||||
int scanning)
|
int scanning)
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
|
|
||||||
int wpa_supplicant_enabled_networks(struct wpa_config *conf);
|
int wpa_supplicant_enabled_networks(struct wpa_config *conf);
|
||||||
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
|
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
|
||||||
|
int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s);
|
||||||
void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
|
void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
|
||||||
|
void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s);
|
||||||
void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
|
void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
|
||||||
int scanning);
|
int scanning);
|
||||||
struct wpa_driver_scan_params;
|
struct wpa_driver_scan_params;
|
||||||
|
|
|
@ -2114,6 +2114,7 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void)
|
||||||
wpa_s->scan_interval = 5;
|
wpa_s->scan_interval = 5;
|
||||||
wpa_s->new_connection = 1;
|
wpa_s->new_connection = 1;
|
||||||
wpa_s->parent = wpa_s;
|
wpa_s->parent = wpa_s;
|
||||||
|
wpa_s->sched_scanning = 0;
|
||||||
|
|
||||||
return wpa_s;
|
return wpa_s;
|
||||||
}
|
}
|
||||||
|
@ -2278,6 +2279,8 @@ next_driver:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
wpa_s->max_scan_ssids = capa.max_scan_ssids;
|
wpa_s->max_scan_ssids = capa.max_scan_ssids;
|
||||||
|
wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
|
||||||
|
wpa_s->sched_scan_supported = capa.sched_scan_supported;
|
||||||
wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
|
wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
|
||||||
wpa_s->max_stations = capa.max_stations;
|
wpa_s->max_stations = capa.max_stations;
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,6 +387,12 @@ struct wpa_supplicant {
|
||||||
*/
|
*/
|
||||||
#define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1)
|
#define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1)
|
||||||
|
|
||||||
|
struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
|
||||||
|
int sched_scan_timeout;
|
||||||
|
int sched_scan_interval;
|
||||||
|
int first_sched_scan;
|
||||||
|
int sched_scan_timed_out;
|
||||||
|
|
||||||
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);
|
||||||
struct dl_list bss; /* struct wpa_bss::list */
|
struct dl_list bss; /* struct wpa_bss::list */
|
||||||
|
@ -405,6 +411,7 @@ struct wpa_supplicant {
|
||||||
|
|
||||||
enum wpa_states wpa_state;
|
enum wpa_states wpa_state;
|
||||||
int scanning;
|
int scanning;
|
||||||
|
int sched_scanning;
|
||||||
int new_connection;
|
int new_connection;
|
||||||
int reassociated_connection;
|
int reassociated_connection;
|
||||||
|
|
||||||
|
@ -428,6 +435,8 @@ struct wpa_supplicant {
|
||||||
struct wpa_client_mlme mlme;
|
struct wpa_client_mlme mlme;
|
||||||
unsigned int drv_flags;
|
unsigned int drv_flags;
|
||||||
int max_scan_ssids;
|
int max_scan_ssids;
|
||||||
|
int max_sched_scan_ssids;
|
||||||
|
int sched_scan_supported;
|
||||||
unsigned int max_remain_on_chan;
|
unsigned int max_remain_on_chan;
|
||||||
unsigned int max_stations;
|
unsigned int max_stations;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue