diff --git a/repos/dde_linux/README b/repos/dde_linux/README index a0c648ea3..7f3f66b2a 100644 --- a/repos/dde_linux/README +++ b/repos/dde_linux/README @@ -131,3 +131,45 @@ Configuration snippet: ! ! ! +! +! +! +! + +Each accesspoint node has attributes that contain the SSID and the BSSID +of the accesspoint as well as the link quality (signal strength). These +attributes are mandatory. If the network is protected, the node will also +have an attribute describing the type of protection in addition. + +The second report provides information about the state of the connection +to the currently connected accesspoint: + +! +! +! + +Valid state values are 'connected', 'disconnected', 'connecting' and +'disconnecting'. + +In return, the wifi_drv get its configuration via a ROM module. This ROM +module contains the selected accesspoint. To connect to a accesspoint +a report like the following is used: + +! + +To disconnect from an accesspoint, a empty report is sent: + +! + +By subscribing to both reports and providing the required ROM module, a +component can control the wireless driver. An example therefore is the Qt +based component in 'src/app/qt_wifi_connect'. + +Currently only WPA/WPA2 protection using a pre-shared key is supported. diff --git a/repos/dde_linux/lib/mk/wpa_supplicant.mk b/repos/dde_linux/lib/mk/wpa_supplicant.mk index 7baf4d361..aaa32fea1 100644 --- a/repos/dde_linux/lib/mk/wpa_supplicant.mk +++ b/repos/dde_linux/lib/mk/wpa_supplicant.mk @@ -10,7 +10,8 @@ CC_OPT += -Wno-unused-function CC_CXX_OPT += -fpermissive -SRC_C += main.c +SRC_C += main.c +SRC_CC += reporter.cc # wpa_supplicant SRC_C_wpa_supplicant = blacklist.c \ @@ -103,4 +104,5 @@ INC_DIR += $(WS_CONTRIB_DIR)/src/utils CC_OPT += -DCONFIG_ELOOP_POLL vpath %.c $(WS_CONTRIB_DIR) -vpath %.c $(WS_DIR) +vpath %.c $(WS_DIR) +vpath %.cc $(WS_DIR) diff --git a/repos/dde_linux/patches/wpa_supplicant.patch b/repos/dde_linux/patches/wpa_supplicant.patch index 5ac9822c3..443e222b4 100644 --- a/repos/dde_linux/patches/wpa_supplicant.patch +++ b/repos/dde_linux/patches/wpa_supplicant.patch @@ -81,3 +81,116 @@ #endif /* CONFIG_ELOOP_POLL */ #ifdef CONFIG_ELOOP_EPOLL +@@ -801,7 +801,7 @@ + #endif /* CONFIG_NATIVE_WINDOWS */ + + +-static void eloop_handle_signal(int sig) ++void eloop_handle_signal(int sig) + { + int i; + +--- a/wpa_supplicant/events.c ++++ b/wpa_supplicant/events.c +@@ -1245,6 +1245,9 @@ + } + + ++extern void wpa_report_scan_results(struct wpa_supplicant *); ++ ++ + /* Return != 0 if no scan results could be fetched or if scan results should not + * be shared with other virtual interfaces. */ + static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, +@@ -1281,6 +1284,8 @@ + goto scan_work_done; + } + ++ wpa_report_scan_results(wpa_s); ++ + #ifndef CONFIG_NO_RANDOM_POOL + num = scan_res->num; + if (num > 10) +--- a/src/drivers/driver.h ++++ b/src/drivers/driver.h +@@ -267,6 +267,20 @@ + + #define WPAS_MAX_SCAN_SSIDS 16 + ++struct wpa_driver_scan_ssid { ++ /** ++ * ssid - specific SSID to scan for (ProbeReq) ++ * %NULL or zero-length SSID is used to indicate active scan ++ * with wildcard SSID. ++ */ ++ const u8 *ssid; ++ /** ++ * ssid_len: Length of the SSID in octets ++ */ ++ size_t ssid_len; ++}; ++ ++ + /** + * struct wpa_driver_scan_params - Scan parameters + * Data for struct wpa_driver_ops::scan2(). +@@ -275,18 +289,7 @@ + /** + * ssids - SSIDs to scan for + */ +- struct wpa_driver_scan_ssid { +- /** +- * ssid - specific SSID to scan for (ProbeReq) +- * %NULL or zero-length SSID is used to indicate active scan +- * with wildcard SSID. +- */ +- const u8 *ssid; +- /** +- * ssid_len: Length of the SSID in octets +- */ +- size_t ssid_len; +- } ssids[WPAS_MAX_SCAN_SSIDS]; ++ struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; + + /** + * num_ssids - Number of entries in ssids array +--- a/wpa_supplicant/events.c ++++ b/wpa_supplicant/events.c +@@ -2067,6 +2067,9 @@ static int disconnect_reason_recoverable(u16 reason_code) + } + + ++void wpa_report_disconnect_event(struct wpa_supplicant *); ++ ++ + static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, + u16 reason_code, + int locally_generated) +@@ -2088,6 +2091,7 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, + + if (!is_zero_ether_addr(bssid) || + wpa_s->wpa_state >= WPA_AUTHENTICATING) { ++ wpa_report_disconnect_event(wpa_s); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR + " reason=%d%s", + MAC2STR(bssid), reason_code, +--- a/wpa_supplicant/wpa_supplicant.c ++++ b/wpa_supplicant/wpa_supplicant.c +@@ -656,6 +656,9 @@ void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s) + } + + ++void wpa_report_connect_event(struct wpa_ssid *); ++ ++ + /** + * wpa_supplicant_set_state - Set current connection state + * @wpa_s: Pointer to wpa_supplicant data +@@ -689,6 +692,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, + + if (state == WPA_COMPLETED && wpa_s->new_connection) { + struct wpa_ssid *ssid = wpa_s->current_ssid; ++ wpa_report_connect_event(ssid); + #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to " + MACSTR " completed [id=%d id_str=%s]", diff --git a/repos/dde_linux/ports/dde_linux.hash b/repos/dde_linux/ports/dde_linux.hash index 4d4bfbb04..2488f3617 100644 --- a/repos/dde_linux/ports/dde_linux.hash +++ b/repos/dde_linux/ports/dde_linux.hash @@ -1 +1 @@ -8251d08813903fffb40bab8041618909d06e690a +1752fc50fccc55b94e45ca8c730df74e7c349a7b diff --git a/repos/dde_linux/src/drivers/wifi/main.cc b/repos/dde_linux/src/drivers/wifi/main.cc index fa7828c57..c1d10ff2e 100644 --- a/repos/dde_linux/src/drivers/wifi/main.cc +++ b/repos/dde_linux/src/drivers/wifi/main.cc @@ -14,13 +14,19 @@ /* Genode includes */ #include #include +#include #include +#include /* local includes */ #include "wpa.h" +typedef long long ssize_t; extern void wifi_init(Server::Entrypoint &, Genode::Lock &); +extern "C" void wpa_conf_reload(void); +extern "C" ssize_t wpa_write_conf(char const *, Genode::size_t); + static Genode::Lock &wpa_startup_lock() { @@ -28,11 +34,92 @@ static Genode::Lock &wpa_startup_lock() return _l; } + +static int update_conf(char **p, Genode::size_t *len, char const *ssid, + bool encryption = false, char const *psk = 0) +{ + char const *ssid_fmt = "network={\n\tssid=\"%s\"\n\tkey_mgmt=%s\n"; + char const *psk_fmt = "psk=\"%s\"\n"; + char const *end_fmt = "}\n"; + + static char buf[4096]; + Genode::size_t n = Genode::snprintf(buf, sizeof(buf), ssid_fmt, ssid, + encryption ? "WPA-PSK" : "NONE"); + if (encryption) { + n += Genode::snprintf(buf + n, sizeof(buf) - n, psk_fmt, psk); + } + + n += Genode::snprintf(buf + n, sizeof(buf) - n, end_fmt); + + *p = buf; + *len = n; + + return 0; +} + + +struct Wlan_configration +{ + Genode::Attached_rom_dataspace config_rom { "wlan_configuration" }; + Genode::Signal_rpc_member dispatcher; + + void _update_conf() + { + config_rom.update(); + Genode::size_t size = config_rom.size(); + + char *file_buffer; + Genode::size_t buffer_length; + + Genode::Xml_node node(config_rom.local_addr(), size); + + /** + * Since is empty we generate a dummy + * configuration to fool wpa_supplicant to keep it scanning for + * the non exisiting network. + */ + if (!node.has_attribute("ssid")) { + update_conf(&file_buffer, &buffer_length, "dummyssid"); + } else { + char ssid[32+1] = { 0 }; + node.attribute("ssid").value(ssid, sizeof(ssid)); + + bool use_protection = node.has_attribute("protection"); + + char psk[63+1] = { 0 }; + if (use_protection && node.has_attribute("psk")) + node.attribute("psk").value(psk, sizeof(psk)); + + if (update_conf(&file_buffer, &buffer_length, ssid, + use_protection, psk) != 0) + return; + } + + if (wpa_write_conf(file_buffer, buffer_length) == 0) { + PINF("reload wpa_supplicant configuration"); + wpa_conf_reload(); + } + } + + void _handle_update(unsigned) { _update_conf(); } + + Wlan_configration(Server::Entrypoint &ep) + : + dispatcher(ep, *this, &Wlan_configration::_handle_update) + { + config_rom.sigh(dispatcher); + _update_conf(); + } +}; + + struct Main { Server::Entrypoint &_ep; Wpa_thread *_wpa; + Wlan_configration *_wc; + Main(Server::Entrypoint &ep) : _ep(ep) @@ -41,6 +128,10 @@ struct Main _wpa->start(); + try { + _wc = new (Genode::env()->heap()) Wlan_configration(_ep); + } catch (...) { PWRN("could not create wlan_configration handler"); } + wifi_init(ep, wpa_startup_lock()); } }; diff --git a/repos/dde_linux/src/drivers/wifi/wpa.h b/repos/dde_linux/src/drivers/wifi/wpa.h index 063737fc7..c8ac211ea 100644 --- a/repos/dde_linux/src/drivers/wifi/wpa.h +++ b/repos/dde_linux/src/drivers/wifi/wpa.h @@ -19,6 +19,7 @@ /* entry function */ extern "C" int wpa_main(void); +extern "C" void wpa_conf_reload(void); class Wpa_thread : public Genode::Thread<8 * 1024 * sizeof(long)> { diff --git a/repos/dde_linux/src/lib/wpa_supplicant/main.c b/repos/dde_linux/src/lib/wpa_supplicant/main.c index 60c50e6ee..c776a5935 100644 --- a/repos/dde_linux/src/lib/wpa_supplicant/main.c +++ b/repos/dde_linux/src/lib/wpa_supplicant/main.c @@ -1,4 +1,19 @@ /* + * \brief WPA Supplicant frontend + * \author Josef Soentgen + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* + * based on: + * * WPA Supplicant / Example program entrypoint * Copyright (c) 2003-2005, Jouni Malinen * @@ -6,12 +21,20 @@ * See README for more details. */ -#include "includes.h" +/* libc includes */ +#include +#include +#include +#include +/* local includes */ +#include "includes.h" #include "common.h" #include "wpa_supplicant_i.h" -int wpa_main() +static char const *conf_file = "/config/wpa_supplicant.conf"; + +int wpa_main(void) { struct wpa_interface iface; int exitcode = 0; @@ -21,6 +44,7 @@ int wpa_main() memset(¶ms, 0, sizeof(params)); params.wpa_debug_level = MSG_INFO; + // params.wpa_debug_level = MSG_DEBUG; global = wpa_supplicant_init(¶ms); if (global == NULL) @@ -29,7 +53,7 @@ int wpa_main() memset(&iface, 0, sizeof(iface)); iface.ifname = "wlan0"; - iface.confname = "/wpa_supplicant.conf"; + iface.confname = conf_file; if (wpa_supplicant_add_iface(global, &iface) == NULL) exitcode = -1; @@ -41,3 +65,24 @@ int wpa_main() return exitcode; } + + +void eloop_handle_signal(int); +void wpa_conf_reload(void) +{ + /* (ab)use POSIX signal to trigger reloading the conf file */ + eloop_handle_signal(SIGHUP); +} + + +int wpa_write_conf(char const *buffer, size_t len) +{ + int fd = open(conf_file, O_TRUNC|O_WRONLY); + if (fd == -1) + return -1; + + ssize_t n = write(fd, buffer, len); + close(fd); + + return n > 0 ? 0 : -1; +} diff --git a/repos/dde_linux/src/lib/wpa_supplicant/reporter.cc b/repos/dde_linux/src/lib/wpa_supplicant/reporter.cc new file mode 100644 index 000000000..44af972c2 --- /dev/null +++ b/repos/dde_linux/src/lib/wpa_supplicant/reporter.cc @@ -0,0 +1,121 @@ +/* + * \brief WPA Supplicant frontend + * \author Josef Soentgen + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* WPA Supplicant includes */ +extern "C" { +#include "includes.h" +#include "common.h" +#include "drivers/driver.h" +#include "wpa_supplicant_i.h" +#include "bss.h" +#include "scan.h" +#include "common/ieee802_11_defs.h" +} + + +static Genode::Reporter accesspoints_reporter = { "wlan_accesspoints" }; +static Genode::Reporter state_reporter = { "wlan_state" }; + + +enum { SSID_MAX_LEN = 32 + 1, MAC_STR_LEN = 6*2 + 5 + 1}; + + +static inline void mac2str(char *buf, u8 const *mac) +{ + Genode::snprintf(buf, MAC_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + + +extern "C" void wpa_report_connect_event(struct wpa_ssid *wpa_ssid) +{ + state_reporter.enabled(true); + + try { + Genode::Reporter::Xml_generator xml(state_reporter, [&]() { + /* FIXME ssid may contain any characters, even NUL */ + Genode::String ssid((char const*)wpa_ssid->ssid, wpa_ssid->ssid_len); + + char bssid_buf[MAC_STR_LEN]; + mac2str(bssid_buf, wpa_ssid->bssid); + + xml.node("accesspoint", [&]() { + xml.attribute("ssid", ssid.string()); + xml.attribute("bssid", bssid_buf); + xml.attribute("state", "connected"); + }); + }); + } catch (...) { PWRN("could not report connected state"); } +} + + +extern "C" void wpa_report_disconnect_event(struct wpa_supplicant *wpa_s) +{ + state_reporter.enabled(true); + + try { + Genode::Reporter::Xml_generator xml(state_reporter, [&]() { + struct wpa_ssid *wpa_ssid = wpa_s->current_ssid; + + /* FIXME ssid may contain any characters, even NUL */ + Genode::String ssid((char const*)wpa_ssid->ssid, wpa_ssid->ssid_len); + + char bssid_buf[MAC_STR_LEN]; + mac2str(bssid_buf, wpa_ssid->bssid); + + xml.node("accesspoint", [&]() { + xml.attribute("ssid", ssid.string()); + xml.attribute("bssid", bssid_buf); + xml.attribute("state", "disconnected"); + }); + + }); + } catch (...) { PWRN("could not report disconnected state"); } +} + + +extern "C" void wpa_report_scan_results(struct wpa_supplicant *wpa_s) +{ + accesspoints_reporter.enabled(true); + + try { + Genode::Reporter::Xml_generator xml(accesspoints_reporter, [&]() { + for (unsigned i = 0; i < wpa_s->last_scan_res_used; i++) { + struct wpa_bss *bss = wpa_s->last_scan_res[i]; + + bool wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) != NULL; + bool wpa2 = wpa_bss_get_ie(bss, WLAN_EID_RSN) != NULL; + + char bssid_buf[MAC_STR_LEN]; + mac2str(bssid_buf, bss->bssid); + + Genode::String ssid((char const*)bss->ssid, bss->ssid_len); + + xml.node("accesspoint", [&]() { + xml.attribute("ssid", ssid.string()); + xml.attribute("bssid", bssid_buf); + + /* XXX we forcefully only support WPA/WPA2 psk for now */ + if (wpa || wpa2) + xml.attribute("protection", "WPA-PSK"); + }); + } + }); + } catch (...) { PWRN("could not report scan results"); } +}