cfg80211: add request id to cfg80211_sched_scan_*() api
[linux-2.6-block.git] / net / wireless / scan.c
index 6f4996c0f4df16c4e85fabef1bf4c16f8fce5538..14d5f0c8c45ff07224f2dc8e6310c2edc440874e 100644 (file)
@@ -300,92 +300,168 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request,
 }
 EXPORT_SYMBOL(cfg80211_scan_done);
 
-void __cfg80211_sched_scan_results(struct work_struct *wk)
+void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
+                                struct cfg80211_sched_scan_request *req)
 {
-       struct cfg80211_registered_device *rdev;
-       struct cfg80211_sched_scan_request *request;
+       ASSERT_RTNL();
 
-       rdev = container_of(wk, struct cfg80211_registered_device,
-                           sched_scan_results_wk);
+       list_add_rcu(&req->list, &rdev->sched_scan_req_list);
+}
 
-       rtnl_lock();
+static void cfg80211_del_sched_scan_req(struct cfg80211_registered_device *rdev,
+                                       struct cfg80211_sched_scan_request *req)
+{
+       ASSERT_RTNL();
 
-       request = rtnl_dereference(rdev->sched_scan_req);
+       list_del_rcu(&req->list);
+       kfree_rcu(req, rcu_head);
+}
 
-       /* we don't have sched_scan_req anymore if the scan is stopping */
-       if (request) {
-               if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
-                       /* flush entries from previous scans */
-                       spin_lock_bh(&rdev->bss_lock);
-                       __cfg80211_bss_expire(rdev, request->scan_start);
-                       spin_unlock_bh(&rdev->bss_lock);
-                       request->scan_start = jiffies;
-               }
-               nl80211_send_sched_scan(request, NL80211_CMD_SCHED_SCAN_RESULTS);
+static struct cfg80211_sched_scan_request *
+cfg80211_find_sched_scan_req(struct cfg80211_registered_device *rdev, u64 reqid)
+{
+       struct cfg80211_sched_scan_request *pos;
+
+       ASSERT_RTNL();
+
+       list_for_each_entry(pos, &rdev->sched_scan_req_list, list) {
+               if (pos->reqid == reqid)
+                       return pos;
        }
+       return NULL;
+}
+
+/*
+ * Determines if a scheduled scan request can be handled. When a legacy
+ * scheduled scan is running no other scheduled scan is allowed regardless
+ * whether the request is for legacy or multi-support scan. When a multi-support
+ * scheduled scan is running a request for legacy scan is not allowed. In this
+ * case a request for multi-support scan can be handled if resources are
+ * available, ie. struct wiphy::max_sched_scan_reqs limit is not yet reached.
+ */
+int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev,
+                                    bool want_multi)
+{
+       struct cfg80211_sched_scan_request *pos;
+       int i = 0;
+
+       list_for_each_entry(pos, &rdev->sched_scan_req_list, list) {
+               /* request id zero means legacy in progress */
+               if (!i && !pos->reqid)
+                       return -EINPROGRESS;
+               i++;
+       }
+
+       if (i) {
+               /* no legacy allowed when multi request(s) are active */
+               if (!want_multi)
+                       return -EINPROGRESS;
+
+               /* resource limit reached */
+               if (i == rdev->wiphy.max_sched_scan_reqs)
+                       return -ENOSPC;
+       }
+       return 0;
+}
+
+void cfg80211_sched_scan_results_wk(struct work_struct *work)
+{
+       struct cfg80211_registered_device *rdev;
+       struct cfg80211_sched_scan_request *req, *tmp;
 
+       rdev = container_of(work, struct cfg80211_registered_device,
+                          sched_scan_res_wk);
+
+       rtnl_lock();
+       list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) {
+               if (req->report_results) {
+                       req->report_results = false;
+                       if (req->flags & NL80211_SCAN_FLAG_FLUSH) {
+                               /* flush entries from previous scans */
+                               spin_lock_bh(&rdev->bss_lock);
+                               __cfg80211_bss_expire(rdev, req->scan_start);
+                               spin_unlock_bh(&rdev->bss_lock);
+                               req->scan_start = jiffies;
+                       }
+                       nl80211_send_sched_scan(req,
+                                               NL80211_CMD_SCHED_SCAN_RESULTS);
+               }
+       }
        rtnl_unlock();
 }
 
-void cfg80211_sched_scan_results(struct wiphy *wiphy)
+void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid)
 {
-       trace_cfg80211_sched_scan_results(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       struct cfg80211_sched_scan_request *request;
+
+       trace_cfg80211_sched_scan_results(wiphy, reqid);
        /* ignore if we're not scanning */
 
-       if (rcu_access_pointer(wiphy_to_rdev(wiphy)->sched_scan_req))
-               queue_work(cfg80211_wq,
-                          &wiphy_to_rdev(wiphy)->sched_scan_results_wk);
+       rtnl_lock();
+       request = cfg80211_find_sched_scan_req(rdev, reqid);
+       if (request) {
+               request->report_results = true;
+               queue_work(cfg80211_wq, &rdev->sched_scan_res_wk);
+       }
+       rtnl_unlock();
 }
 EXPORT_SYMBOL(cfg80211_sched_scan_results);
 
-void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy)
+void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy, u64 reqid)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        ASSERT_RTNL();
 
-       trace_cfg80211_sched_scan_stopped(wiphy);
+       trace_cfg80211_sched_scan_stopped(wiphy, reqid);
 
-       __cfg80211_stop_sched_scan(rdev, true);
+       __cfg80211_stop_sched_scan(rdev, reqid, true);
 }
 EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl);
 
-void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
+void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid)
 {
        rtnl_lock();
-       cfg80211_sched_scan_stopped_rtnl(wiphy);
+       cfg80211_sched_scan_stopped_rtnl(wiphy, reqid);
        rtnl_unlock();
 }
 EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
 
-int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
-                              bool driver_initiated)
+int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
+                                struct cfg80211_sched_scan_request *req,
+                                bool driver_initiated)
 {
-       struct cfg80211_sched_scan_request *sched_scan_req;
-       struct net_device *dev;
-
        ASSERT_RTNL();
 
-       if (!rdev->sched_scan_req)
-               return -ENOENT;
-
-       sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
-       dev = sched_scan_req->dev;
-
        if (!driver_initiated) {
-               int err = rdev_sched_scan_stop(rdev, dev);
+               int err = rdev_sched_scan_stop(rdev, req->dev, req->reqid);
                if (err)
                        return err;
        }
 
-       nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_SCHED_SCAN_STOPPED);
+       nl80211_send_sched_scan(req, NL80211_CMD_SCHED_SCAN_STOPPED);
 
-       RCU_INIT_POINTER(rdev->sched_scan_req, NULL);
-       kfree_rcu(sched_scan_req, rcu_head);
+       cfg80211_del_sched_scan_req(rdev, req);
 
        return 0;
 }
 
+int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
+                              u64 reqid, bool driver_initiated)
+{
+       struct cfg80211_sched_scan_request *sched_scan_req;
+
+       ASSERT_RTNL();
+
+       sched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid);
+       if (!sched_scan_req)
+               return -ENOENT;
+
+       return cfg80211_stop_sched_scan_req(rdev, sched_scan_req,
+                                           driver_initiated);
+}
+
 void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
                       unsigned long age_secs)
 {