From 4e5aaaa700f4f6911514e7f469ba14201a66581c Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Wed, 12 Sep 2018 23:17:25 -0400 Subject: [PATCH] BNetworkDevice: Implement Scan(). This depends on the previous commit to return the correct error code from ioctl(). If there are no VAPs running (which is the case after a forced disconnect from an access point), scans will fail. In that case, we call IEEE80211_IOC_HAIKU_COMPAT_WLAN_UP, which will restart a VAP, and then initiate the scan. Change-Id: I732aefe67e386dbb0ed3d232ed9deda678132601 Reviewed-on: https://review.haiku-os.org/551 Reviewed-by: waddlesplash --- src/kits/network/libnetapi/NetworkDevice.cpp | 119 +++++++++++++++++-- 1 file changed, 109 insertions(+), 10 deletions(-) diff --git a/src/kits/network/libnetapi/NetworkDevice.cpp b/src/kits/network/libnetapi/NetworkDevice.cpp index 47b60741d8..f823e87bdb 100644 --- a/src/kits/network/libnetapi/NetworkDevice.cpp +++ b/src/kits/network/libnetapi/NetworkDevice.cpp @@ -13,9 +13,11 @@ #include #include +#include #include #include +#include extern "C" { # include @@ -42,6 +44,9 @@ struct ie_data { }; +// #pragma mark - private functions (code shared with net_server) + + static status_t get_80211(const char* name, int32 type, void* data, int32& length) { @@ -66,6 +71,30 @@ get_80211(const char* name, int32 type, void* data, int32& length) } +static status_t +set_80211(const char* name, int32 type, void* data, + int32 length = 0, int32 value = 0) +{ + int socket = ::socket(AF_INET, SOCK_DGRAM, 0); + if (socket < 0) + return errno; + + FileDescriptorCloser closer(socket); + + struct ieee80211req ireq; + strlcpy(ireq.i_name, name, IF_NAMESIZE); + ireq.i_type = type; + ireq.i_val = value; + ireq.i_len = length; + ireq.i_data = data; + + if (ioctl(socket, SIOCS80211, &ireq, sizeof(struct ieee80211req)) < 0) + return errno; + + return B_OK; +} + + template status_t do_request(T& request, const char* name, int option) { @@ -687,17 +716,87 @@ BNetworkDevice::Control(int option, void* request) status_t BNetworkDevice::Scan(bool wait, bool forceRescan) { -#if 0 - if (index == 0) { - struct ieee80211_scan_req request; - memset(&request, 0, sizeof(request)); - request.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE - | IEEE80211_IOC_SCAN_NOJOIN; - request.sr_duration = IEEE80211_IOC_SCAN_FOREVER; - set_80211(Name(), IEEE80211_IOC_SCAN_REQ, NULL); + // Network status listener for change notifications + class ScanListener : public BLooper { + public: + ScanListener(BString iface) + : + fInterface(iface) + { + start_watching_network(B_WATCH_NETWORK_WLAN_CHANGES, this); + } + virtual ~ScanListener() + { + stop_watching_network(this); + } + + protected: + virtual void MessageReceived(BMessage *message) + { + if (message->what != B_NETWORK_MONITOR) { + BLooper::MessageReceived(message); + return; + } + + BString interfaceName; + if (message->FindString("interface", &interfaceName) != B_OK) + return; + if (fInterface.FindFirst(interfaceName) < 0) + return; + if (message->FindInt32("opcode") != B_NETWORK_WLAN_SCANNED) + return; + + Lock(); + Quit(); + } + + private: + BString fInterface; + }; + + ScanListener* listener = NULL; + if (wait) + listener = new ScanListener(Name()); + + // Trigger the scan + struct ieee80211_scan_req request; + memset(&request, 0, sizeof(request)); + request.sr_flags = IEEE80211_IOC_SCAN_ACTIVE + | IEEE80211_IOC_SCAN_BGSCAN + | IEEE80211_IOC_SCAN_NOPICK + | IEEE80211_IOC_SCAN_ONCE + | (forceRescan ? IEEE80211_IOC_SCAN_FLUSH : 0); + request.sr_duration = IEEE80211_IOC_SCAN_FOREVER; + request.sr_nssid = 0; + + status_t status = set_80211(Name(), IEEE80211_IOC_SCAN_REQ, &request, + sizeof(request)); + + // If there are no VAPs running, the net80211 layer will return ENXIO. + // Try to bring up the interface (which should start a VAP) and try again. + if (status == ENXIO) { + struct ieee80211req dummy; + status = set_80211(Name(), IEEE80211_IOC_HAIKU_COMPAT_WLAN_UP, &dummy, + sizeof(dummy)); + if (status != B_OK) + return status; + + status = set_80211(Name(), IEEE80211_IOC_SCAN_REQ, &request, + sizeof(request)); } -#endif - return B_ERROR; + + // If there is already a scan currently running, it's probably an "infinite" + // one, which we of course don't want to wait for. So just return immediately + // if that's the case. + if (status == EINPROGRESS) + return B_OK; + + if (!wait || status != B_OK) + return status; + + while (wait_for_thread(listener->Run(), NULL) == B_INTERRUPTED) + ; + return B_OK; }