libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
linux_wifi_client.hpp
1#pragma once
2
3#include "libxr_def.hpp"
4#if defined(HAVE_WPA_CLIENT)
5
6#include <NetworkManager.h>
7#include <unistd.h>
8#include <wpa_ctrl.h>
9
10#include <cstring>
11#include <sstream>
12#include <string>
13#include <vector>
14
15#include "logger.hpp"
16#include "net/wifi_client.hpp"
17#include "thread.hpp"
18
19namespace LibXR
20{
21
22class LinuxWifiClient : public WifiClient
23{
24 public:
25 LinuxWifiClient(const char* ifname = nullptr)
26 {
27 if (ifname)
28 {
29 strncpy(ifname_cstr_, ifname, sizeof(ifname_cstr_) - 1);
30 }
31 else
32 {
33 std::string iface = DetectWifiInterface();
34 if (iface.empty())
35 {
36 XR_LOG_ERROR("Wi-Fi interface not found");
37 ASSERT(false);
38 }
39 else
40 {
41 strncpy(ifname_cstr_, iface.c_str(), sizeof(ifname_cstr_) - 1);
42 }
43 }
44
45 socket_path_ = "/var/run/wpa_supplicant/" + std::string(ifname_cstr_);
46 }
47
48 ~LinuxWifiClient() override
49 {
50 Disconnect();
51 if (ctrl_)
52 {
53 wpa_ctrl_close(ctrl_);
54 }
55 }
56
57 bool Enable() override
58 {
59 if (ctrl_)
60 {
61 return true;
62 }
63 ctrl_ = wpa_ctrl_open(socket_path_.c_str());
64 if (ctrl_)
65 {
66 XR_LOG_PASS("Wi-Fi enabled: %s", socket_path_.c_str());
67 return true;
68 }
69 XR_LOG_ERROR("Wi-Fi enable failed: %s", socket_path_.c_str());
70 return false;
71 }
72
73 void Disable() override
74 {
75 Disconnect();
76 if (ctrl_)
77 {
78 wpa_ctrl_close(ctrl_);
79 ctrl_ = nullptr;
80 }
81 }
82
83 bool IsConnected() const override
84 {
85 std::string out;
86 if (!SendCommand("STATUS", out))
87 {
88 return false;
89 }
90 return out.find("wpa_state=COMPLETED") != std::string::npos;
91 }
92
93 IPAddressRaw GetIPAddress() const override
94 {
95 char cmd[128];
96 auto ans = snprintf(cmd, sizeof(cmd), "ip -4 addr show %s | grep inet", ifname_cstr_);
97 UNUSED(ans);
98 FILE* fp = popen(cmd, "r");
99 if (!fp)
100 {
101 return {};
102 }
103
104 char buf[64] = {};
105 IPAddressRaw ip = {};
106 if (fgets(buf, sizeof(buf), fp))
107 {
108 char ip_str[32] = {};
109 auto ret = sscanf(buf, " inet %[^/]", ip_str);
110 UNUSED(ret);
111 ip = IPAddressRaw::FromString(ip_str);
112 }
113 pclose(fp);
114 return ip;
115 }
116
117 MACAddressRaw GetMACAddress() const override
118 {
119 char path[128];
120 auto ans = snprintf(path, sizeof(path), "/sys/class/net/%s/address", ifname_cstr_);
121 UNUSED(ans);
122 FILE* fp = fopen(path, "r");
123 if (!fp)
124 {
125 return {};
126 }
127
128 char mac_str[32] = {};
129 if (!fgets(mac_str, sizeof(mac_str), fp))
130 {
131 auto ans = fclose(fp);
132 UNUSED(ans);
133 return {};
134 }
135 auto ret = fclose(fp);
136 UNUSED(ret);
137 mac_str[strcspn(mac_str, "\n")] = 0;
138 return MACAddressRaw::FromString(mac_str);
139 }
140
141 WifiError Connect(const Config& config) override
142 {
143 std::string out;
144 SendCommand("REMOVE_NETWORK all", out);
145 SendCommand("ADD_NETWORK", out);
146 int netid = atoi(out.c_str());
147 if (netid < 0)
148 {
149 XR_LOG_ERROR("ADD_NETWORK failed: %s", out.c_str());
150 return WifiError::HARDWARE_FAILURE;
151 }
152
153 std::stringstream cmd;
154 cmd << "SET_NETWORK " << netid << " ssid \"" << config.ssid << "\"";
155 if (!SendCommand(cmd.str().c_str(), out) || out.find("OK") == std::string::npos)
156 {
157 XR_LOG_ERROR("SET_NETWORK ssid failed: %s", out.c_str());
158 return WifiError::AUTHENTICATION_FAILED;
159 }
160
161 cmd.str("");
162 cmd << "SET_NETWORK " << netid << " psk \"" << config.password << "\"";
163 if (!SendCommand(cmd.str().c_str(), out) || out.find("OK") == std::string::npos)
164 {
165 XR_LOG_ERROR("SET_NETWORK psk failed: %s", out.c_str());
166 return WifiError::AUTHENTICATION_FAILED;
167 }
168
169 std::vector<std::string> cleanup_cmds = {
170 "key_mgmt WPA-PSK", "eap NONE", "phase1 \"\"", "identity \"\"", "password \"\"",
171 };
172 for (const auto& line : cleanup_cmds)
173 {
174 cmd.str("");
175 cmd << "SET_NETWORK " << netid << " " << line;
176 SendCommand(cmd.str().c_str(), out);
177 }
178
179 cmd.str("");
180 cmd << "ENABLE_NETWORK " << netid;
181 if (!SendCommand(cmd.str().c_str(), out) || out.find("OK") == std::string::npos)
182 {
183 XR_LOG_ERROR("ENABLE_NETWORK failed: %s", out.c_str());
184 return WifiError::HARDWARE_FAILURE;
185 }
186
187 cmd.str("");
188 cmd << "SELECT_NETWORK " << netid;
189 SendCommand(cmd.str().c_str(), out);
190
191 const int TIMEOUT_MS = 30000;
192 const int INTERVAL_MS = 300;
193 int elapsed = 0;
194
195 while (elapsed < TIMEOUT_MS)
196 {
197 std::string status;
198 SendCommand("STATUS", status);
199
200 if (status.find("wpa_state=COMPLETED") != std::string::npos)
201 {
202 XR_LOG_PASS("Wi-Fi Connected to SSID: %s", config.ssid);
203 return WifiError::NONE;
204 }
205
206 if (status.find("wpa_state=INACTIVE") != std::string::npos)
207 {
208 XR_LOG_ERROR("Wi-Fi Connection failed: %s", status.c_str());
209 return WifiError::AUTHENTICATION_FAILED;
210 }
211
212 LibXR::Thread::Sleep(INTERVAL_MS);
213 elapsed += INTERVAL_MS;
214 }
215
216 XR_LOG_ERROR("Wi-Fi Connection timeout");
217 return WifiError::CONNECTION_TIMEOUT;
218 }
219
220 WifiError Disconnect() override
221 {
222 std::string out;
223 SendCommand("DISCONNECT", out);
224 SendCommand("REMOVE_NETWORK all", out);
225 if (IsConnected())
226 {
227 return WifiError::UNKNOWN;
228 }
229 return WifiError::NONE;
230 }
231
232 WifiError Scan(ScanResult* out_list, size_t max_count, size_t& out_found) override
233 {
234 std::string out;
235 SendCommand("SCAN", out);
236 sleep(2);
237 SendCommand("SCAN_RESULTS", out);
238
239 std::istringstream ss(out);
240 std::string line;
241 out_found = 0;
242 std::getline(ss, line);
243 while (std::getline(ss, line) && out_found < max_count)
244 {
245 std::istringstream ls(line);
246 std::string bssid, freq, signal, flags, ssid;
247 ls >> bssid >> freq >> signal >> flags;
248 std::getline(ls, ssid);
249 if (!ssid.empty() && ssid[0] == '\t')
250 {
251 ssid.erase(0, 1);
252 }
253
254 auto& r = out_list[out_found++];
255 strncpy(r.ssid, ssid.c_str(), sizeof(r.ssid) - 1);
256 r.rssi = atoi(signal.c_str());
257 r.security =
258 (flags.find("WPA2") != std::string::npos) ? Security::WPA2_PSK : Security::OPEN;
259 }
260
261 return WifiError::NONE;
262 }
263
264 int GetRSSI() const override
265 {
266 std::string out;
267 SendCommand("SIGNAL_POLL", out);
268 auto pos = out.find("RSSI=");
269 return pos != std::string::npos ? atoi(out.c_str() + pos + 5) : 0;
270 }
271
272 private:
273 bool SendCommand(const char* cmd, std::string& result) const
274 {
275 if (!ctrl_)
276 {
277 return false;
278 }
279 char buf[4096];
280 size_t len = sizeof(buf);
281 int ret = wpa_ctrl_request(ctrl_, cmd, strlen(cmd), buf, &len, nullptr);
282 if (ret == 0)
283 {
284 result.assign(buf, len);
285 return true;
286 }
287 XR_LOG_ERROR("wpa_ctrl_request failed: %s\n", strerror(errno));
288 return false;
289 }
290
291 std::string DetectWifiInterface()
292 {
293 GError* error = nullptr;
294 NMClient* client = nm_client_new(nullptr, &error);
295 if (!client)
296 {
297 auto ans = fprintf(stderr, "Failed to create NMClient: %s\n",
298 error ? error->message : "unknown error");
299 UNUSED(ans);
300
301 if (error)
302 {
303 g_error_free(error);
304 }
305 return "";
306 }
307
308 const GPtrArray* devices = nm_client_get_devices(client);
309 for (guint i = 0; i < devices->len; ++i)
310 {
311 NMDevice* dev = (NMDevice*)g_ptr_array_index(devices, i);
312 if (NM_IS_DEVICE_WIFI(dev))
313 {
314 const char* ifname = nm_device_get_iface(dev);
315 if (ifname)
316 {
317 std::string iface_str(ifname);
318 g_object_unref(client);
319 return iface_str;
320 }
321 }
322 }
323
324 g_object_unref(client);
325 return "";
326 }
327
328 char ifname_cstr_[32] = {}; // 静态存储接口名,避免动态分配
329 std::string socket_path_;
330 wpa_ctrl* ctrl_ = nullptr;
331};
332
333} // namespace LibXR
334
335#endif
static void Sleep(uint32_t milliseconds)
让线程进入休眠状态 Puts the thread to sleep
Definition thread.cpp:16
LibXR 命名空间
Definition ch32_gpio.hpp:9