/*
* cfg80211 scan result handling
*
* Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <linux/nl80211.h>
#include <linux/etherdevice.h>
#include <net/arp.h>
#include <net/cfg80211.h>
#include <net/iw_handler.h>
#include "core.h"
#include "nl80211.h"
#include "wext-compat.h"
#define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ)
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
{
struct cfg80211_scan_request *request;
struct net_device *dev;
#ifdef CONFIG_CFG80211_WEXT
union iwreq_data wrqu;
#endif
ASSERT_RDEV_LOCK(rdev);
request = rdev->scan_req;
if (!request)
return;
dev = request->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);
#ifdef CONFIG_CFG80211_WEXT
if (!request->aborted) {
memset(&wrqu, 0, sizeof(wrqu));
wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
}
#endif
dev_put(dev);
rdev->scan_req = NULL;
/*
* OK. If this is invoked with "leak" then we can't
* free this ... but we've cleaned it up anyway. The
* driver failed to call the scan_done callback, so
* all bets are off, it might still be trying to use
* the scan request or not ... if it accesses the dev
* in there (it shouldn't anyway) then it may crash.
*/
if (!leak)
kfree(request);
}
void __cfg80211_scan_done(struct work_struct *wk)
{
struct cfg80211_registered_device *rdev;
rdev = container_of(wk, struct cfg80211_registered_device,
scan_done_wk);
cfg80211_lock_rdev(rdev);
___cfg80211_scan_done(rdev, false);
cfg80211_unlock_rdev(rdev);
}
void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
{
WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
request->aborted = aborted;
queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk);
}
EXPORT_SYMBOL(cfg80211_scan_done);
void __cfg80211_sched_scan_results(struct work_struct *wk)
{
struct cfg80211_registered_device *rdev;
rdev = container_of(wk, struct cfg80211_registered_device,
sched_scan_results_wk);
cfg80211_lock_rdev(rdev);
/* we don't have sched_scan_req anymore if the scan is stopping */
if (rdev->sched_scan_req)
nl80211_send_sched_scan_results(rdev,
rdev->sched_scan_req->dev);
cfg80211_unlock_rdev(rdev);
}
void cfg80211_sched_scan_results(struct wiphy *wiphy)
{
/* ignore if we're not scanning */
if (wiphy_to_dev(wiphy)->sched_scan_req)
queue_work(cfg80211_wq,
&wiphy_to_dev(wiphy)->sched_scan_results_wk);
}
EXPORT_SYMBOL(cfg80211_sched_scan_results);
void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
cfg80211_lock_rdev(rdev);
__cfg80211_stop_sched_scan(rdev, true);
cfg80211_unlock_rdev(rdev);
}
EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
bool driver_initiated)