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 <[email protected]>
This commit is contained in:
Augustin Cavalier
2018-09-13 04:13:19 +00:00
committed by waddlesplash
parent e9073260a7
commit 4e5aaaa700
+109 -10
View File
@@ -13,9 +13,11 @@
#include <sys/sockio.h>
#include <Messenger.h>
#include <Looper.h>
#include <AutoDeleter.h>
#include <NetServer.h>
#include <NetworkNotifications.h>
extern "C" {
# include <compat/sys/cdefs.h>
@@ -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<typename T> 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;
}