@@ -63,6 +63,8 @@ struct cfg80211_registered_device {
unsigned long suspend_at;
struct work_struct scan_done_wk;
+ void (*async_scan_cb)(struct net_device *);
+
#ifdef CONFIG_NL80211_TESTMODE
struct genl_info *testmode_info;
#endif
@@ -372,6 +374,10 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
struct net_device *dev, enum nl80211_iftype ntype,
u32 *flags, struct vif_params *params);
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
+int cfg80211_async_scan(struct wireless_dev *wdev,
+ struct cfg80211_scan_request *request,
+ bool set_cb_on_busy,
+ void (*cb)(struct net_device *));
struct ieee80211_channel *
rdev_fixed_channel(struct cfg80211_registered_device *rdev,
@@ -40,7 +40,10 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
* Otherwise, wpa_supplicant gets completely confused with
* wext events.
*/
- cfg80211_sme_scan_done(dev);
+ if (rdev->async_scan_cb) {
+ rdev->async_scan_cb(dev);
+ rdev->async_scan_cb = NULL;
+ }
if (request->aborted)
nl80211_send_scan_aborted(rdev, dev);
@@ -655,6 +658,37 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
}
EXPORT_SYMBOL(cfg80211_unlink_bss);
+/* Do a scan, and schedule a callback to be run on completion. */
+int cfg80211_async_scan(struct wireless_dev *wdev,
+ struct cfg80211_scan_request *request,
+ bool set_cb_on_busy,
+ void (*cb)(struct net_device *))
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ int err;
+
+ ASSERT_RDEV_LOCK(rdev);
+
+ if (rdev->scan_req) {
+ if (set_cb_on_busy)
+ rdev->async_scan_cb = cb;
+ return -EBUSY;
+ }
+
+ rdev->scan_req = request;
+ rdev->async_scan_cb = cb;
+
+ err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request);
+ if (!err) {
+ nl80211_send_scan_start(rdev, wdev->netdev);
+ dev_hold(wdev->netdev);
+ } else {
+ rdev->scan_req = NULL;
+ }
+
+ return err;
+}
+
#ifdef CONFIG_CFG80211_WEXT
int cfg80211_wext_siwscan(struct net_device *dev,
struct iw_request_info *info,
@@ -75,17 +75,12 @@ static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
static int cfg80211_conn_scan(struct wireless_dev *wdev)
{
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
struct cfg80211_scan_request *request;
int n_channels, err;
ASSERT_RTNL();
- ASSERT_RDEV_LOCK(rdev);
ASSERT_WDEV_LOCK(wdev);
- if (rdev->scan_req)
- return -EBUSY;
-
if (wdev->conn->params.channel) {
n_channels = 1;
} else {
@@ -128,19 +123,14 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
request->dev = wdev->netdev;
- request->wiphy = &rdev->wiphy;
-
- rdev->scan_req = request;
+ request->wiphy = wdev->wiphy;
- err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request);
- if (!err) {
+ err = cfg80211_async_scan(wdev, request, true, cfg80211_sme_scan_done);
+ if (!err)
wdev->conn->state = CFG80211_CONN_SCANNING;
- nl80211_send_scan_start(rdev, wdev->netdev);
- dev_hold(wdev->netdev);
- } else {
- rdev->scan_req = NULL;
+ else
kfree(request);
- }
+
return err;
}