unsigned int flags;
int max_scan_ssids;
+ int max_sched_scan_ssids;
+ int sched_scan_supported;
/**
* max_remain_on_chan - Maximum remain-on-channel duration in msec
* the station gets added by FT-over-DS.
*/
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);
};
* completed Group Key Handshake while the host (including
* wpa_supplicant was sleeping).
*/
- EVENT_DRIVER_GTK_REKEY
+ EVENT_DRIVER_GTK_REKEY,
+
+ /**
+ * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
+ */
+ EVENT_SCHED_SCAN_STOPPED
};
}
+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 *
wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
{
/**
+ * 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_s: Pointer to wpa_supplicant data
*
}
+/**
+ * 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,
int scanning)
{