nl80211: Add support for multiple scan plans for scheduled scan
authorAvraham Stern <avraham.stern@intel.com>
Tue, 17 Nov 2015 13:08:23 +0000 (15:08 +0200)
committerJouni Malinen <j@w1.fi>
Mon, 30 Nov 2015 12:03:28 +0000 (14:03 +0200)
Add 'scan plans' to driver scan parameters for scheduled scan.
Each 'scan plan' specifies the number of iterations to run the scan
request and the interval between iterations. When a scan plan
finishes (i.e., it was run for the specified number of iterations),
the next scan plan is executed. The last scan plan will run
infinitely.

The maximum number of supported scan plans, the maximum number of
iterations for a single scan plan and the maximum scan interval
are advertised by the driver.

Signed-off-by: Avraham Stern <avraham.stern@intel.com>
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211.h
src/drivers/driver_nl80211_capa.c
src/drivers/driver_nl80211_scan.c
wpa_supplicant/driver_i.h
wpa_supplicant/scan.c

index 403dfa3..6fd72c5 100644 (file)
@@ -416,6 +416,28 @@ struct wpa_driver_scan_params {
         */
        const u8 *mac_addr_mask;
 
+       /**
+        * sched_scan_plans - Scan plans for scheduled scan
+        *
+        * Each scan plan consists of the number of iterations to scan and the
+        * interval between scans. When a scan plan finishes (i.e., it was run
+        * for the specified number of iterations), the next scan plan is
+        * executed. The scan plans are executed in the order they appear in
+        * the array (lower index first). The last scan plan will run infinitely
+        * (until requested to stop), thus must not specify the number of
+        * iterations. All other scan plans must specify the number of
+        * iterations.
+        */
+       struct sched_scan_plan {
+                u32 interval; /* In seconds */
+                u32 iterations; /* Zero to run infinitely */
+        } *sched_scan_plans;
+
+       /**
+        * sched_scan_plans_num - Number of scan plans in sched_scan_plans array
+        */
+        unsigned int sched_scan_plans_num;
+
        /*
         * NOTE: Whenever adding new parameters here, please make sure
         * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -1242,6 +1264,15 @@ struct wpa_driver_capa {
        /** Maximum number of supported active probe SSIDs for sched_scan */
        int max_sched_scan_ssids;
 
+       /** Maximum number of supported scan plans for scheduled scan */
+       unsigned int max_sched_scan_plans;
+
+       /** Maximum interval in a scan plan. In seconds */
+       u32 max_sched_scan_plan_interval;
+
+       /** Maximum number of iterations in a single scan plan */
+       u32 max_sched_scan_plan_iterations;
+
        /** Whether sched_scan (offloaded scanning) is supported */
        int sched_scan_supported;
 
@@ -3004,7 +3035,6 @@ struct wpa_driver_ops {
         * 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
@@ -3015,8 +3045,7 @@ struct wpa_driver_ops {
         * 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);
+       int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params);
 
        /**
         * stop_sched_scan - Request the driver to stop a scheduled scan
index bcb0921..0fa2c7e 100644 (file)
@@ -7545,7 +7545,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
                                  "capa.mac_addr_rand_scan_supported=%d\n"
                                  "capa.conc_capab=%u\n"
                                  "capa.max_conc_chan_2_4=%u\n"
-                                 "capa.max_conc_chan_5_0=%u\n",
+                                 "capa.max_conc_chan_5_0=%u\n"
+                                 "capa.max_sched_scan_plans=%u\n"
+                                 "capa.max_sched_scan_plan_interval=%u\n"
+                                 "capa.max_sched_scan_plan_iterations=%u\n",
                                  drv->capa.key_mgmt,
                                  drv->capa.enc,
                                  drv->capa.auth,
@@ -7564,7 +7567,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
                                  drv->capa.mac_addr_rand_scan_supported,
                                  drv->capa.conc_capab,
                                  drv->capa.max_conc_chan_2_4,
-                                 drv->capa.max_conc_chan_5_0);
+                                 drv->capa.max_conc_chan_5_0,
+                                 drv->capa.max_sched_scan_plans,
+                                 drv->capa.max_sched_scan_plan_interval,
+                                 drv->capa.max_sched_scan_plan_iterations);
                if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
index ed03ac4..f042c3c 100644 (file)
@@ -276,8 +276,7 @@ void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
 int wpa_driver_nl80211_scan(struct i802_bss *bss,
                            struct wpa_driver_scan_params *params);
 int wpa_driver_nl80211_sched_scan(void *priv,
-                                 struct wpa_driver_scan_params *params,
-                                 u32 interval);
+                                 struct wpa_driver_scan_params *params);
 int wpa_driver_nl80211_stop_sched_scan(void *priv);
 struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
 void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
index 1b5752f..c74ed5f 100644 (file)
@@ -499,6 +499,19 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                capa->max_sched_scan_ssids =
                        nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
 
+       if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] &&
+           tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] &&
+           tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) {
+               capa->max_sched_scan_plans =
+                       nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]);
+
+               capa->max_sched_scan_plan_interval =
+                       nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]);
+
+               capa->max_sched_scan_plan_iterations =
+                       nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+       }
+
        if (tb[NL80211_ATTR_MAX_MATCH_SETS])
                capa->max_match_sets =
                        nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
@@ -711,6 +724,12 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
                        drv->capa.max_csa_counters = 1;
        }
 
+       if (!drv->capa.max_sched_scan_plans) {
+               drv->capa.max_sched_scan_plans = 1;
+               drv->capa.max_sched_scan_plan_interval = UINT32_MAX;
+               drv->capa.max_sched_scan_plan_iterations = 0;
+       }
+
        return 0;
 }
 
index ec2eb28..2ff254e 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211 - Scanning
+ * Copyright(c) 2015 Intel Deutschland GmbH
  * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
  * Copyright (c) 2009-2010, Atheros Communications
@@ -308,16 +309,82 @@ fail:
 }
 
 
+static int
+nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data *drv,
+                                 struct nl_msg *msg,
+                                 struct wpa_driver_scan_params *params)
+{
+       struct nlattr *plans;
+       struct sched_scan_plan *scan_plans = params->sched_scan_plans;
+       unsigned int i;
+
+       plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS);
+       if (!plans)
+               return -1;
+
+       for (i = 0; i < params->sched_scan_plans_num; i++) {
+               struct nlattr *plan = nla_nest_start(msg, i + 1);
+
+               if (!plan)
+                       return -1;
+
+               if (!scan_plans[i].interval ||
+                   scan_plans[i].interval >
+                   drv->capa.max_sched_scan_plan_interval) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: sched scan plan no. %u: Invalid interval: %u",
+                                  i, scan_plans[i].interval);
+                       return -1;
+               }
+
+               if (nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
+                               scan_plans[i].interval))
+                       return -1;
+
+               if (scan_plans[i].iterations >
+                   drv->capa.max_sched_scan_plan_iterations) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: sched scan plan no. %u: Invalid number of iterations: %u",
+                                  i, scan_plans[i].iterations);
+                       return -1;
+               }
+
+               if (scan_plans[i].iterations &&
+                   nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+                               scan_plans[i].iterations))
+                       return -1;
+
+               nla_nest_end(msg, plan);
+
+               /*
+                * All the scan plans must specify the number of iterations
+                * except the last plan, which will run infinitely. So if the
+                * number of iterations is not specified, this ought to be the
+                * last scan plan.
+                */
+               if (!scan_plans[i].iterations)
+                       break;
+       }
+
+       if (i != params->sched_scan_plans_num - 1) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: All sched scan plans but the last must specify number of iterations");
+               return -1;
+       }
+
+       nla_nest_end(msg, plans);
+       return 0;
+}
+
+
 /**
  * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
  * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
  * @params: Scan parameters
- * @interval: Interval between scan cycles in milliseconds
  * Returns: 0 on success, -1 on failure or if not supported
  */
 int wpa_driver_nl80211_sched_scan(void *priv,
-                                 struct wpa_driver_scan_params *params,
-                                 u32 interval)
+                                 struct wpa_driver_scan_params *params)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -332,11 +399,27 @@ int wpa_driver_nl80211_sched_scan(void *priv,
                return android_pno_start(bss, params);
 #endif /* ANDROID */
 
+       if (!params->sched_scan_plans_num ||
+           params->sched_scan_plans_num > drv->capa.max_sched_scan_plans) {
+               wpa_printf(MSG_ERROR,
+                          "nl80211: Invalid number of sched scan plans: %u",
+                          params->sched_scan_plans_num);
+               return -1;
+       }
+
        msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
-       if (!msg ||
-           nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval))
+       if (!msg)
                goto fail;
 
+       if (drv->capa.max_sched_scan_plan_iterations) {
+               if (nl80211_sched_scan_add_scan_plans(drv, msg, params))
+                       goto fail;
+       } else {
+               if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL,
+                               params->sched_scan_plans[0].interval * 1000))
+                       goto fail;
+       }
+
        if ((drv->num_filter_ssids &&
            (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
            params->filter_rssi) {
@@ -399,8 +482,7 @@ int wpa_driver_nl80211_sched_scan(void *priv,
                goto fail;
        }
 
-       wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
-                  "scan interval %d msec", ret, interval);
+       wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret);
 
 fail:
        nlmsg_free(msg);
index b9d213a..699fd4f 100644 (file)
@@ -100,12 +100,10 @@ static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
 }
 
 static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
-                                    struct wpa_driver_scan_params *params,
-                                    u32 interval)
+                                    struct wpa_driver_scan_params *params)
 {
        if (wpa_s->driver->sched_scan)
-               return wpa_s->driver->sched_scan(wpa_s->drv_priv,
-                                                params, interval);
+               return wpa_s->driver->sched_scan(wpa_s->drv_priv, params);
        return -1;
 }
 
index a39922f..25a1011 100644 (file)
@@ -271,14 +271,23 @@ int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
                                    int interval)
 {
        int ret;
+       struct sched_scan_plan scan_plan = {
+               .interval = interval,
+               .iterations = 0,
+       };
+
+       params->sched_scan_plans = &scan_plan;
+       params->sched_scan_plans_num = 1;
 
        wpa_supplicant_notify_scanning(wpa_s, 1);
-       ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
+       ret = wpa_drv_sched_scan(wpa_s, params);
        if (ret)
                wpa_supplicant_notify_scanning(wpa_s, 0);
        else
                wpa_s->sched_scanning = 1;
 
+       params->sched_scan_plans = NULL;
+       params->sched_scan_plans_num = 0;
        return ret;
 }
 
@@ -2217,6 +2226,19 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
        params->only_new_results = src->only_new_results;
        params->low_priority = src->low_priority;
 
+       if (src->sched_scan_plans_num > 0) {
+               params->sched_scan_plans =
+                       os_malloc(sizeof(*src->sched_scan_plans) *
+                                 src->sched_scan_plans_num);
+               if (!params->sched_scan_plans)
+                       goto failed;
+
+               os_memcpy(params->sched_scan_plans, src->sched_scan_plans,
+                         sizeof(*src->sched_scan_plans) *
+                         src->sched_scan_plans_num);
+               params->sched_scan_plans_num = src->sched_scan_plans_num;
+       }
+
        if (src->mac_addr_rand) {
                params->mac_addr_rand = src->mac_addr_rand;
 
@@ -2254,6 +2276,7 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params)
        os_free((u8 *) params->extra_ies);
        os_free(params->freqs);
        os_free(params->filter_ssids);
+       os_free(params->sched_scan_plans);
 
        /*
         * Note: params->mac_addr_mask points to same memory allocation and