@@ -694,6 +694,17 @@ struct cfg80211_ssid {
u8 ssid_len;
};
+
+/**
+ * struct cfg80211_srd -- Scan-Req device container.
+ * @next: Next container in list.
+ * @dev: network device.
+ */
+struct cfg80211_srd {
+ struct cfg80211_srd *next;
+ struct net_device *dev;
+};
+
/**
* struct cfg80211_scan_request - scan request description
*
@@ -718,7 +729,7 @@ struct cfg80211_scan_request {
/* internal */
struct wiphy *wiphy;
- struct net_device *dev;
+ struct cfg80211_srd* devs;
bool aborted;
bool can_scan_one;
@@ -640,9 +640,19 @@ static void wdev_cleanup_work(struct work_struct *work)
cfg80211_lock_rdev(rdev);
- if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) {
- rdev->scan_req->aborted = true;
- ___cfg80211_scan_done(rdev, true);
+ if (rdev->scan_req) {
+ struct cfg80211_srd *ptr = rdev->scan_req->devs;
+ while (ptr) {
+ /* Maybe just remove from linked-list if not
+ * the primary?
+ */
+ if (WARN_ON(ptr->dev == wdev->netdev)) {
+ rdev->scan_req->aborted = true;
+ ___cfg80211_scan_done(rdev, true);
+ break;
+ }
+ ptr = ptr->next;
+ }
}
cfg80211_unlock_rdev(rdev);
@@ -2857,6 +2857,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
enum ieee80211_band band;
size_t ie_len;
bool do_all_chan = true;
+ struct cfg80211_srd *srd;
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
return -EINVAL;
@@ -2866,8 +2867,18 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->scan)
return -EOPNOTSUPP;
- if (rdev->scan_req)
- return -EBUSY;
+ srd = kmalloc(sizeof(*srd), GFP_KERNEL);
+ if (!srd)
+ return -ENOMEM;
+
+ if (rdev->scan_req) {
+ dev_hold(dev);
+ srd->dev = dev;
+ /* Initial requestor remains at the front of the list */
+ srd->next = rdev->scan_req->devs->next;
+ rdev->scan_req->devs->next = srd;
+ return 0;
+ }
if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
n_channels = validate_scan_freqs(
@@ -2993,7 +3004,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
request->ie_len);
}
- request->dev = dev;
+ srd->dev = dev;
+ srd->next = NULL;
+ request->devs = srd;
request->wiphy = &rdev->wiphy;
rdev->scan_req = request;
@@ -3006,6 +3019,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
out_free:
rdev->scan_req = NULL;
kfree(request);
+ kfree(srd);
}
return err;
@@ -22,6 +22,8 @@
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
{
struct cfg80211_scan_request *request;
+ struct cfg80211_srd *devs;
+ struct cfg80211_srd *tmp;
struct net_device *dev;
#ifdef CONFIG_CFG80211_WEXT
union iwreq_data wrqu;
@@ -34,29 +36,35 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
if (!request)
return;
- dev = request->dev;
+ devs = request->devs;
+ while (devs) {
+ dev = devs->dev;
- /*
- * This must be before sending the other events!
- * Otherwise, wpa_supplicant gets completely confused with
- * wext events.
- */
- cfg80211_sme_scan_done(dev);
+ /*
+ * This must be before sending the other events!
+ * Otherwise, wpa_supplicant gets completely confused with
+ * wext events.
+ */
+ cfg80211_sme_scan_done(dev);
- if (request->aborted)
- nl80211_send_scan_aborted(rdev, dev);
- else
- nl80211_send_scan_done(rdev, dev);
+ if (request->aborted)
+ nl80211_send_scan_aborted(rdev, dev);
+ else
+ nl80211_send_scan_done(rdev, dev);
#ifdef CONFIG_CFG80211_WEXT
- if (!request->aborted) {
- memset(&wrqu, 0, sizeof(wrqu));
+ if (!request->aborted) {
+ memset(&wrqu, 0, sizeof(wrqu));
- wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
- }
+ wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
+ }
#endif
- dev_put(dev);
+ dev_put(dev);
+ tmp = devs;
+ devs = devs->next;
+ kfree(tmp);
+ }
rdev->scan_req = NULL;
@@ -672,6 +680,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
struct cfg80211_scan_request *creq = NULL;
int i, err, n_channels = 0;
enum ieee80211_band band;
+ struct cfg80211_srd *srd = NULL;
if (!netif_running(dev))
return -ENETDOWN;
@@ -684,11 +693,22 @@ int cfg80211_wext_siwscan(struct net_device *dev,
if (IS_ERR(rdev))
return PTR_ERR(rdev);
- if (rdev->scan_req) {
- err = -EBUSY;
+ srd = kmalloc(sizeof(*srd), GFP_KERNEL);
+ if (!srd) {
+ err = -ENOMEM;
goto out;
}
+ if (rdev->scan_req) {
+ dev_hold(dev);
+ srd->dev = dev;
+ /* Initial requestor remains at the front of the list */
+ srd->next = rdev->scan_req->devs->next;
+ rdev->scan_req->devs->next = srd;
+ cfg80211_unlock_rdev(rdev);
+ return 0;
+ }
+
wiphy = &rdev->wiphy;
/* Determine number of channels, needed to allocate creq */
@@ -709,7 +729,9 @@ int cfg80211_wext_siwscan(struct net_device *dev,
}
creq->wiphy = wiphy;
- creq->dev = dev;
+ srd->dev = dev;
+ srd->next = NULL;
+ creq->devs = srd;
/* SSIDs come after channels */
creq->ssids = (void *)&creq->channels[n_channels];
creq->n_channels = n_channels;
@@ -782,9 +804,11 @@ int cfg80211_wext_siwscan(struct net_device *dev,
nl80211_send_scan_start(rdev, dev);
/* creq now owned by driver */
creq = NULL;
+ srd = NULL;
dev_hold(dev);
}
out:
+ kfree(srd);
kfree(creq);
cfg80211_unlock_rdev(rdev);
return err;
@@ -79,13 +79,24 @@ 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;
+ struct cfg80211_srd *srd;
ASSERT_RTNL();
ASSERT_RDEV_LOCK(rdev);
ASSERT_WDEV_LOCK(wdev);
- if (rdev->scan_req)
- return -EBUSY;
+ srd = kmalloc(sizeof(*srd), GFP_KERNEL);
+ if (!srd)
+ return -ENOMEM;
+
+ if (rdev->scan_req) {
+ dev_hold(wdev->netdev);
+ srd->dev = wdev->netdev;
+ /* Initial requestor remains at the front of the list */
+ srd->next = rdev->scan_req->devs->next;
+ rdev->scan_req->devs->next = srd;
+ return 0;
+ }
if (wdev->conn->params.channel) {
n_channels = 1;
@@ -133,7 +144,9 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
wdev->conn->params.ssid_len);
request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
- request->dev = wdev->netdev;
+ srd->dev = wdev->netdev;
+ srd->next = NULL;
+ request->devs = srd;
request->wiphy = &rdev->wiphy;
rdev->scan_req = request;
@@ -146,6 +159,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
} else {
rdev->scan_req = NULL;
kfree(request);
+ kfree(srd);
}
return err;
}