Network: Add a BNetworkDevice::GetNetworks() method and use it in the GUI.

The GetNextNetwork() method is really inefficient: it fetches all the
networks at once from the kernel every single time and then winds
up returning only one of them. In parts of the GUI that iterate over
all networks more than once per refresh (sometimes within a loop, even!)
this was often a noticeable lag on the GUI, especially with OpenBSD
drivers which have extra overhead to do struct translation in the
ioctl handler.

Now, we have a way to fetch all scan results at once and just iterate
over them as many times as we need, and this is what NetworkStatus
and Network preferences now do, saving lots of time and effort.
This commit is contained in:
Augustin Cavalier 2022-10-25 23:33:50 -04:00
parent 65c155bede
commit b2c77ad27a
4 changed files with 86 additions and 16 deletions

View File

@ -114,6 +114,8 @@ public:
status_t GetNextNetwork(uint32& cookie,
wireless_network& network);
status_t GetNetworks(wireless_network*& networks,
uint32& count);
status_t GetNetwork(const char* name,
wireless_network& network);
status_t GetNetwork(const BNetworkAddress& address,

View File

@ -401,14 +401,15 @@ NetworkStatusView::MouseDown(BPoint point)
if (!wifiInterface.IsEmpty()) {
std::set<BNetworkAddress> associated;
BNetworkAddress address;
wireless_network network;
uint32 cookie = 0;
while (device.GetNextAssociatedNetwork(cookie, address) == B_OK)
associated.insert(address);
int32 wifiCount = 0;
cookie = 0;
while (device.GetNextNetwork(cookie, network) == B_OK) {
uint32 networksCount = 0;
wireless_network* networks = NULL;
device.GetNetworks(networks, networksCount);
for (uint32 i = 0; i < networksCount; i++) {
const wireless_network& network = networks[i];
BMessage* message = new BMessage(kMsgJoinNetwork);
message->AddString("device", wifiInterface);
message->AddString("name", network.name);
@ -416,12 +417,12 @@ NetworkStatusView::MouseDown(BPoint point)
BMenuItem* item = new WirelessNetworkMenuItem(network, message);
menu->AddItem(item);
wifiCount++;
if (associated.find(network.address) != associated.end())
item->SetMarked(true);
}
delete[] networks;
if (wifiCount == 0) {
if (networksCount == 0) {
BMenuItem* item = new BMenuItem(
B_TRANSLATE("<no wireless networks found>"), NULL);
item->SetEnabled(false);

View File

@ -423,6 +423,59 @@ fill_wireless_network(wireless_network& network, const char* networkName,
}
static status_t
get_scan_results(const char* device, wireless_network*& networks, uint32& count)
{
if (networks != NULL)
return B_BAD_VALUE;
// TODO: Find some way to reduce code duplication with the following function!
const size_t kBufferSize = 64 * 1024;
uint8* buffer = (uint8*)malloc(kBufferSize);
if (buffer == NULL)
return B_NO_MEMORY;
MemoryDeleter deleter(buffer);
int32 length = kBufferSize;
status_t status = get_80211(device, IEEE80211_IOC_SCAN_RESULTS, buffer,
length);
if (status != B_OK)
return status;
BObjectList<wireless_network> networksList(true);
int32 bytesLeft = length;
uint8* entry = buffer;
while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) {
ieee80211req_scan_result* result
= (ieee80211req_scan_result*)entry;
char networkName[32];
strlcpy(networkName, (char*)(result + 1),
min_c((int)sizeof(networkName), result->isr_ssid_len + 1));
wireless_network* network = new wireless_network;
fill_wireless_network(*network, networkName, *result);
networksList.AddItem(network);
entry += result->isr_len;
bytesLeft -= result->isr_len;
}
count = 0;
if (!networksList.IsEmpty()) {
networks = new wireless_network[networksList.CountItems()];
for (int32 i = 0; i < networksList.CountItems(); i++) {
networks[i] = *networksList.ItemAt(i);
count++;
}
}
return B_OK;
}
static status_t
get_scan_result(const char* device, wireless_network& network, uint32 index,
const BNetworkAddress* address, const char* name)
@ -430,7 +483,8 @@ get_scan_result(const char* device, wireless_network& network, uint32 index,
if (address != NULL && address->Family() != AF_LINK)
return B_BAD_VALUE;
const size_t kBufferSize = 65535;
// TODO: Find some way to reduce code duplication with the preceding function!
const size_t kBufferSize = 64 * 1024;
uint8* buffer = (uint8*)malloc(kBufferSize);
if (buffer == NULL)
return B_NO_MEMORY;
@ -787,6 +841,13 @@ BNetworkDevice::GetNextNetwork(uint32& cookie, wireless_network& network)
}
status_t
BNetworkDevice::GetNetworks(wireless_network*& networks, uint32& count)
{
return get_scan_results(Name(), networks, count);
}
status_t
BNetworkDevice::GetNetwork(const char* name, wireless_network& network)
{

View File

@ -15,6 +15,7 @@
#include <net/if_media.h>
#include <AutoDeleter.h>
#include <Button.h>
#include <Catalog.h>
#include <ControlLook.h>
@ -270,10 +271,11 @@ InterfaceView::_Update(bool updateWirelessNetworks)
while (device.GetNextAssociatedNetwork(cookie, address) == B_OK)
associated.insert(address);
wireless_network network;
cookie = 0;
if ((fPulseCount % 15) == 0
&& device.GetNextNetwork(cookie, network) != B_OK) {
wireless_network* networks = NULL;
uint32 networksCount = 0;
device.GetNetworks(networks, networksCount);
if ((fPulseCount % 15) == 0 && networksCount == 0) {
// We don't seem to know of any networks, and it's been long
// enough since the last scan, so trigger one to try and
// find some networks.
@ -286,8 +288,12 @@ InterfaceView::_Update(bool updateWirelessNetworks)
// to merit such a wait. It's only just over ~4 vertical
// retraces, anyway.
snooze(50 * 1000);
device.GetNetworks(networks, networksCount);
}
ArrayDeleter networksDeleter(networks);
// go through menu items and remove networks that have dropped out
for (int32 index = 0; index < count; index++) {
WirelessNetworkMenuItem* networkItem =
@ -297,9 +303,8 @@ InterfaceView::_Update(bool updateWirelessNetworks)
break;
bool networkFound = false;
cookie = 0;
while (device.GetNextNetwork(cookie, network) == B_OK) {
if (networkItem->Network() == network) {
for (uint32 i = 0; i < networksCount; i++) {
if (networkItem->Network() == networks[i]) {
networkFound = true;
break;
}
@ -312,8 +317,9 @@ InterfaceView::_Update(bool updateWirelessNetworks)
}
// go through networks and add new ones to menu
cookie = 0;
while (device.GetNextNetwork(cookie, network) == B_OK) {
for (uint32 i = 0; i < networksCount; i++) {
const wireless_network& network = networks[i];
bool networkFound = false;
for (int32 index = 0; index < count; index++) {
WirelessNetworkMenuItem* networkItem =