diff options
| author | Maufeat <sahyno1996@gmail.com> | 2025-06-26 18:55:34 +0000 |
|---|---|---|
| committer | crueter <crueter@noreply.localhost> | 2025-06-26 18:55:34 +0000 |
| commit | 2e6a289a0b4e53098d4ee4a9f6baf038d21981f8 (patch) | |
| tree | 8be8276b6d13e8383babefa2bb40264a7ac34c4a /src/core/internal_network/wifi_scanner.cpp | |
| parent | b2e602325cf3ed80ac81b7501465ef8eae567007 (diff) | |
Add Airplane Mode + Host Network Interface Details (#204)
Adds Airplane Mode function to settings, host states, etc.
Windows implemented only for now.
Closes #203
Co-authored-by: crueter <swurl@swurl.xyz>
Co-authored-by: Aleksandr Popovich <alekpopo@pm.me>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/204
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Co-committed-by: Maufeat <sahyno1996@gmail.com>
Diffstat (limited to 'src/core/internal_network/wifi_scanner.cpp')
| -rw-r--r-- | src/core/internal_network/wifi_scanner.cpp | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/src/core/internal_network/wifi_scanner.cpp b/src/core/internal_network/wifi_scanner.cpp new file mode 100644 index 0000000000..bab644d161 --- /dev/null +++ b/src/core/internal_network/wifi_scanner.cpp @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <chrono> +#include <cstring> +#include <thread> +#include <vector> + +#include "common/logging/log.h" +#include "core/internal_network/wifi_scanner.h" + +using namespace std::chrono_literals; + +namespace Network { + +#ifdef _WIN32 +#define NOMINMAX +#include <windows.h> +#include <wlanapi.h> +#pragma comment(lib, "wlanapi.lib") + +static u8 QualityToPercent(DWORD q) { + return static_cast<u8>(q); +} + +static std::vector<ScanData> ScanWifiWin(std::chrono::milliseconds deadline) { + std::vector<ScanData> out; + + HANDLE hClient{}; + DWORD ver{}; + if (WlanOpenHandle(2, nullptr, &ver, &hClient) != ERROR_SUCCESS) + return out; + + PWLAN_INTERFACE_INFO_LIST ifs{}; + if (WlanEnumInterfaces(hClient, nullptr, &ifs) != ERROR_SUCCESS || ifs->dwNumberOfItems == 0) { + WlanCloseHandle(hClient, nullptr); + return out; + } + + // fire a scan on every adapter + for (DWORD i = 0; i < ifs->dwNumberOfItems; ++i) + WlanScan(hClient, &ifs->InterfaceInfo[i].InterfaceGuid, nullptr, nullptr, nullptr); + + const auto start = std::chrono::steady_clock::now(); + bool have = false; + + while (!have && std::chrono::steady_clock::now() - start < deadline) { + std::this_thread::sleep_for(100ms); + out.clear(); + + for (DWORD i = 0; i < ifs->dwNumberOfItems; ++i) { + PWLAN_AVAILABLE_NETWORK_LIST list{}; + if (WlanGetAvailableNetworkList(hClient, &ifs->InterfaceInfo[i].InterfaceGuid, + WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES, + nullptr, &list) != ERROR_SUCCESS) + continue; + + for (DWORD n = 0; n < list->dwNumberOfItems; ++n) { + const auto& nw = list->Network[n]; + ScanData sd{}; + sd.ssid_len = static_cast<u8>(nw.dot11Ssid.uSSIDLength); + std::memcpy(sd.ssid, nw.dot11Ssid.ucSSID, sd.ssid_len); + sd.quality = QualityToPercent(nw.wlanSignalQuality); + if (nw.bNetworkConnectable) + sd.flags |= 1; + if (nw.bSecurityEnabled) + sd.flags |= 2; + out.emplace_back(sd); + + char tmp[0x22]{}; + std::memcpy(tmp, sd.ssid, sd.ssid_len); + LOG_INFO(Network, "[WifiScan] +%u \"%s\" quality=%u flags=0x%X", sd.ssid_len, tmp, + sd.quality, sd.flags); + } + WlanFreeMemory(list); + } + have = !out.empty(); + } + + WlanFreeMemory(ifs); + WlanCloseHandle(hClient, nullptr); + return out; +} +#endif /* _WIN32 */ + +#if defined(__linux__) && !defined(_WIN32) && !defined(ANDROID) +#include <iwlib.h> + +static u8 QualityToPercent(const iwrange& r, const wireless_scan* ws) { + const iw_quality qual = ws->stats.qual; + const int lvl = qual.level; + const int max = r.max_qual.level ? r.max_qual.level : 100; + return static_cast<u8>(std::clamp(100 * lvl / max, 0, 100)); +} + +static int wifi_callback(int skfd, char* ifname, char* args[], int count) +{ + iwrange range; + + int res = iw_get_range_info(skfd, ifname, &range); + + LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", ifname, res); + + if (res >= 0) { + strncpy(args[0], ifname, IFNAMSIZ - 1); + args[0][IFNAMSIZ - 1] = 0; + + return 1; + } + + return 0; +} + +// TODO(crueter, Maufeat): Check if driver supports wireless extensions, fallback to nl80211 if not +static std::vector<ScanData> ScanWifiLinux(std::chrono::milliseconds deadline) { + std::vector<ScanData> out; + int sock = iw_sockets_open(); + if (sock < 0) { + LOG_ERROR(Network, "iw_sockets_open() failed"); + return out; + } + + char ifname[IFNAMSIZ] = {0}; + char *args[1] = {ifname}; + + iw_enum_devices(sock, &wifi_callback, args, 0); + + if (strlen(ifname) == 0) { + LOG_WARNING(Network, "No wireless interface found"); + iw_sockets_close(sock); + return out; + } + + iwrange range{}; + if (iw_get_range_info(sock, ifname, &range) < 0) { + LOG_WARNING(Network, "iw_get_range_info failed on {}", ifname); + iw_sockets_close(sock); + return out; + } + + wireless_scan_head head{}; + const auto start = std::chrono::steady_clock::now(); + bool have = false; + + while (!have && std::chrono::steady_clock::now() - start < deadline) { + std::this_thread::sleep_for(100ms); + if (iw_scan(sock, ifname, range.we_version_compiled, &head) != 0) + continue; + + out.clear(); + for (auto* ws = head.result; ws; ws = ws->next) { + if (!ws->b.has_essid) + continue; + + ScanData sd{}; + sd.ssid_len = static_cast<u8>(std::min<int>(ws->b.essid_len, 0x20)); + std::memcpy(sd.ssid, ws->b.essid, sd.ssid_len); + sd.quality = QualityToPercent(range, ws); + sd.flags |= 1; + if (ws->b.has_key) + sd.flags |= 2; + + out.emplace_back(sd); + char tmp[0x22]{}; + std::memcpy(tmp, sd.ssid, sd.ssid_len); + } + have = !out.empty(); + } + + iw_sockets_close(sock); + return out; +} +#endif /* linux */ + +std::vector<ScanData> ScanWifiNetworks(std::chrono::milliseconds deadline) { +#ifdef _WIN32 + return ScanWifiWin(deadline); +#elif defined(__linux__) && !defined(ANDROID) + return ScanWifiLinux(deadline); +#else + std::this_thread::sleep_for(deadline); + return {}; // unsupported host, pretend no results +#endif +} + +} // namespace Network |
