mac80211: add cancel_hw_scan() callback
authorEliad Peller <eliad@wizery.com>
Mon, 13 Jun 2011 09:47:30 +0000 (12:47 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 17 Jun 2011 18:22:18 +0000 (14:22 -0400)
When suspending, __ieee80211_suspend() calls ieee80211_scan_cancel(),
which will only cancel sw scan. In order to cancel hw scan, the
low-level driver has to cancel it in the suspend() callback. however,
this is too late, as a new scan_work will be enqueued (while the driver
is going into suspend).

Add a new cancel_hw_scan() callback, asking the driver to cancel an
active hw scan, and call it in ieee80211_scan_cancel().

Signed-off-by: Eliad Peller <eliad@wizery.com>
Reviewed-by: Stanislaw Gruszka <sgruszka@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/net/mac80211.h
net/mac80211/driver-ops.h
net/mac80211/driver-trace.h
net/mac80211/scan.c

index e33fe795a3a4478550bd57d44f6712bba8449fc1..120f102814b6ed563cbdab8ace1448a97c7883ab 100644 (file)
@@ -1708,6 +1708,14 @@ enum ieee80211_ampdu_mlme_action {
  *     any error unless this callback returned a negative error code.
  *     The callback can sleep.
  *
+ * @cancel_hw_scan: Ask the low-level tp cancel the active hw scan.
+ *     The driver should ask the hardware to cancel the scan (if possible),
+ *     but the scan will be completed only after the driver will call
+ *     ieee80211_scan_completed().
+ *     This callback is needed for wowlan, to prevent enqueueing a new
+ *     scan_work after the low-level driver was already suspended.
+ *     The callback can sleep.
+ *
  * @sched_scan_start: Ask the hardware to start scanning repeatedly at
  *     specific intervals.  The driver must call the
  *     ieee80211_sched_scan_results() function whenever it finds results.
@@ -1900,6 +1908,8 @@ struct ieee80211_ops {
                                u32 iv32, u16 *phase1key);
        int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                       struct cfg80211_scan_request *req);
+       void (*cancel_hw_scan)(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif);
        int (*sched_scan_start)(struct ieee80211_hw *hw,
                                struct ieee80211_vif *vif,
                                struct cfg80211_sched_scan_request *req,
index eebf7a67daf73122ea7cf455e48abb8d8196c3be..0e7e4268ddf657e0739373f8541772c1b32d2ffe 100644 (file)
@@ -218,6 +218,16 @@ static inline int drv_hw_scan(struct ieee80211_local *local,
        return ret;
 }
 
+static inline void drv_cancel_hw_scan(struct ieee80211_local *local,
+                                     struct ieee80211_sub_if_data *sdata)
+{
+       might_sleep();
+
+       trace_drv_cancel_hw_scan(local, sdata);
+       local->ops->cancel_hw_scan(&local->hw, &sdata->vif);
+       trace_drv_return_void(local);
+}
+
 static inline int
 drv_sched_scan_start(struct ieee80211_local *local,
                     struct ieee80211_sub_if_data *sdata,
index ed9edcbd9aa588b9bb36181f23bbdaf178121c63..3cb6795e926db0cb0e834ce4df5087d598a4a571 100644 (file)
@@ -460,6 +460,12 @@ DEFINE_EVENT(local_sdata_evt, drv_hw_scan,
        TP_ARGS(local, sdata)
 );
 
+DEFINE_EVENT(local_sdata_evt, drv_cancel_hw_scan,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+       TP_ARGS(local, sdata)
+);
+
 DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata),
index 58ffa7d069c791c7d2c2c681861212d806260956..1758b463c5838e6b32d22c3d45d363e2a279a8c9 100644 (file)
@@ -821,10 +821,8 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
  */
 void ieee80211_scan_cancel(struct ieee80211_local *local)
 {
-       bool abortscan;
-
        /*
-        * We are only canceling software scan, or deferred scan that was not
+        * We are canceling software scan, or deferred scan that was not
         * yet really started (see __ieee80211_start_scan ).
         *
         * Regarding hardware scan:
@@ -836,23 +834,30 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
         * - we can not cancel scan_work since driver can schedule it
         *   by ieee80211_scan_completed(..., true) to finish scan
         *
-        * Hence low lever driver is responsible for canceling HW scan.
+        * Hence we only call the cancel_hw_scan() callback, but the low-level
+        * driver is still responsible for calling ieee80211_scan_completed()
+        * after the scan was completed/aborted.
         */
 
        mutex_lock(&local->mtx);
-       abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning);
-       if (abortscan) {
-               /*
-                * The scan is canceled, but stop work from being pending.
-                *
-                * If the work is currently running, it must be blocked on
-                * the mutex, but we'll set scan_sdata = NULL and it'll
-                * simply exit once it acquires the mutex.
-                */
-               cancel_delayed_work(&local->scan_work);
-               /* and clean up */
-               __ieee80211_scan_completed(&local->hw, true, false);
+       if (!local->scan_req)
+               goto out;
+
+       if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
+               if (local->ops->cancel_hw_scan)
+                       drv_cancel_hw_scan(local, local->scan_sdata);
+               goto out;
        }
+
+       /*
+        * If the work is currently running, it must be blocked on
+        * the mutex, but we'll set scan_sdata = NULL and it'll
+        * simply exit once it acquires the mutex.
+        */
+       cancel_delayed_work(&local->scan_work);
+       /* and clean up */
+       __ieee80211_scan_completed(&local->hw, true, false);
+out:
        mutex_unlock(&local->mtx);
 }