diff --git a/repos/dde_linux/include/wifi/ctrl.h b/repos/dde_linux/include/wifi/ctrl.h new file mode 100644 index 0000000000..106764256a --- /dev/null +++ b/repos/dde_linux/include/wifi/ctrl.h @@ -0,0 +1,45 @@ +/* + * \brief Wpa_supplicant CTRL interface + * \author Josef Soentgen + * \date 2018-07-31 + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#ifndef _WIFI__CTRL_H_ +#define _WIFI__CTRL_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define WPA_CTRL_FD 51 + +struct Msg_buffer +{ + unsigned char recv[4096]; + unsigned char send[1024]; + unsigned recv_id; + unsigned send_id; + unsigned char event[1024]; + unsigned event_id; +} __attribute__((packed)); + + +void wpa_ctrl_set_fd(void); + +void *wifi_get_buffer(void); +void wifi_notify_cmd_result(void); +void wifi_notify_event(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _WIFI__CTRL_H_ */ diff --git a/repos/dde_linux/include/wifi/rfkill.h b/repos/dde_linux/include/wifi/rfkill.h new file mode 100644 index 0000000000..dd21c84979 --- /dev/null +++ b/repos/dde_linux/include/wifi/rfkill.h @@ -0,0 +1,26 @@ +/* + * \brief RFKILL interface + * \author Josef Soentgen + * \date 2018-07-11 + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#ifndef _WIFI__RFKILL_H_ +#define _WIFI__RFKILL_H_ + + +namespace Wifi { + + enum { RFKILL_FD = 42, }; +} + +bool wifi_get_rfkill(void); +void wifi_set_rfkill(bool); + +#endif /* _WIFI__RFKILL_H_ */ diff --git a/repos/dde_linux/lib/mk/spec/x86/wpa_driver_nl80211.mk b/repos/dde_linux/lib/mk/spec/x86/wpa_driver_nl80211.mk index 0844ed0559..23348776dc 100644 --- a/repos/dde_linux/lib/mk/spec/x86/wpa_driver_nl80211.mk +++ b/repos/dde_linux/lib/mk/spec/x86/wpa_driver_nl80211.mk @@ -8,6 +8,7 @@ SHARED_LIB = yes LD_OPT += --version-script=$(LIB_DIR)/symbol.map SRC_CC += dummies.cc ioctl.cc +SRC_CC += rfkill_genode.cc WS_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/app/wpa_supplicant @@ -22,8 +23,8 @@ SRC_C_drivers = drivers.c \ driver_nl80211_event.c \ driver_nl80211_monitor.c \ driver_nl80211_scan.c \ - netlink.c \ - rfkill.c + netlink.c + SRC_C += $(addprefix src/drivers/, $(SRC_C_drivers)) INC_DIR += $(WS_CONTRIB_DIR)/src/drivers \ $(WS_CONTRIB_DIR)/src/utils \ @@ -33,7 +34,7 @@ CC_OPT += -DCONFIG_DRIVER_NL80211 CC_OPT += -DCONFIG_LIBNL20 CC_OPT += -D_LINUX_SOCKET_H -vpath %.c $(WS_CONTRIB_DIR) +vpath %.c $(WS_CONTRIB_DIR) vpath %.cc $(LIB_DIR) CC_CXX_WARN_STRICT = diff --git a/repos/dde_linux/lib/mk/spec/x86/wpa_supplicant.mk b/repos/dde_linux/lib/mk/spec/x86/wpa_supplicant.mk index dda03b0110..f7d932a82a 100644 --- a/repos/dde_linux/lib/mk/spec/x86/wpa_supplicant.mk +++ b/repos/dde_linux/lib/mk/spec/x86/wpa_supplicant.mk @@ -10,11 +10,14 @@ CC_OPT += -Wno-unused-function CC_CXX_OPT += -fpermissive -SRC_C += main.c ctrl_iface_genode.c -SRC_CC += reporter.cc +SRC_C += main.c ctrl_iface_genode.c +INC_DIR += $(REP_DIR)/include + # wpa_supplicant SRC_C_wpa_supplicant = blacklist.c \ + bgscan.c \ + bgscan_simple.c \ bss.c \ config.c \ config_file.c \ @@ -32,7 +35,8 @@ SRC_C_wpa_supplicant = blacklist.c \ SRC_C += $(addprefix wpa_supplicant/, $(SRC_C_wpa_supplicant)) INC_DIR += $(WS_CONTRIB_DIR)/wpa_supplicant CC_OPT += -DCONFIG_BACKEND_FILE -DCONFIG_NO_CONFIG_WRITE \ - -DCONFIG_SME -DCONFIG_CTRL_IFACE + -DCONFIG_SME -DCONFIG_CTRL_IFACE \ + -DCONFIG_BGSCAN -DCONFIG_BGSCAN_SIMPLE CC_OPT += -DTLS_DEFAULT_CIPHERS=\"DEFAULT:!EXP:!LOW\" diff --git a/repos/dde_linux/lib/mk/wifi.inc b/repos/dde_linux/lib/mk/wifi.inc index ed8b41d631..28a56c534c 100644 --- a/repos/dde_linux/lib/mk/wifi.inc +++ b/repos/dde_linux/lib/mk/wifi.inc @@ -53,7 +53,7 @@ CC_OPT += -DCONFIG_MAC80211_MESH CC_OPT += -DCONFIG_PM -DCONFIG_PM_SLEEP # rfkill -CC_OPT += -DCONFIG_RFKILL +CC_OPT += -DCONFIG_RFKILL -DCONFIG_RFKILL_INPUT # choose default pid algorithm CC_OPT += -DCONFIG_MAC80211_RC_PID -DCONFIG_MAC80211_RC_DEFAULT=\"pid\" diff --git a/repos/dde_linux/patches/wifi.patch b/repos/dde_linux/patches/wifi.patch index 60e8d09f9b..c77310ce26 100644 --- a/repos/dde_linux/patches/wifi.patch +++ b/repos/dde_linux/patches/wifi.patch @@ -80,6 +80,19 @@ index 7152fdc..5d133e5 100644 .configure_filter = iwl_mvm_configure_filter, .config_iface_filter = iwl_mvm_config_iface_filter, .bss_info_changed = iwl_mvm_bss_info_changed, +diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +index 5d133e5..ec770d4 100644 +--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c ++++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +@@ -1573,7 +1573,7 @@ static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm) + + lockdep_assert_held(&mvm->mutex); + +- if (WARN_ON_ONCE(!mvm->mcast_filter_cmd)) ++ if (!mvm->mcast_filter_cmd) + return; + + ieee80211_iterate_active_interfaces_atomic( diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index f25ce3a..85007fc 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -106,27 +119,28 @@ index 99df171..4632cdf 100644 } delta = remcsum_adjust(ptr, skb->csum, start, offset); +diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h +index 1fdcde9..5f221bd 100644 +--- a/include/linux/rtnetlink.h ++++ b/include/linux/rtnetlink.h +@@ -97,9 +97,13 @@ void rtnetlink_init(void); + void __rtnl_unlock(void); + void rtnl_kfree_skbs(struct sk_buff *head, struct sk_buff *tail); + ++#if 1 ++#define ASSERT_RTNL() ++# else + #define ASSERT_RTNL() \ + WARN_ONCE(!rtnl_is_locked(), \ + "RTNL: assertion failed at %s (%d)\n", __FILE__, __LINE__) ++#endif + + extern int ndo_dflt_fdb_dump(struct sk_buff *skb, + struct netlink_callback *cb, diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index e0f3f4a..a309257 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c -@@ -3977,6 +3977,8 @@ static int packet_notifier(struct notifier_block *this, - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct net *net = dev_net(dev); - -+// XXX check if still needed -+#if 0 - rcu_read_lock(); - sk_for_each_rcu(sk, &net->packet.sklist) { - struct packet_sock *po = pkt_sk(sk); -@@ -4017,6 +4019,7 @@ static int packet_notifier(struct notifier_block *this, - } - } - rcu_read_unlock(); -+#endif - return NOTIFY_DONE; - } - @@ -4544,8 +4547,10 @@ static int __net_init packet_net_init(struct net *net) mutex_init(&net->packet.sklist_lock); INIT_HLIST_HEAD(&net->packet.sklist); diff --git a/repos/dde_linux/patches/wifi_rfkill.patch b/repos/dde_linux/patches/wifi_rfkill.patch new file mode 100644 index 0000000000..bd4c70f833 --- /dev/null +++ b/repos/dde_linux/patches/wifi_rfkill.patch @@ -0,0 +1,22 @@ +--- a/net/rfkill/core.c ++++ b/net/rfkill/core.c +@@ -911,6 +911,19 @@ bool rfkill_blocked(struct rfkill *rfkill) + } + EXPORT_SYMBOL(rfkill_blocked); + ++bool rfkill_get_any(enum rfkill_type type) ++{ ++ bool blocked = false; ++ ++ struct rfkill *rfkill; ++ list_for_each_entry(rfkill, &rfkill_list, node) { ++ if (rfkill->type != type && type != RFKILL_TYPE_ALL) ++ continue; ++ ++ blocked |= rfkill_blocked(rfkill); ++ } ++ return blocked; ++} + + struct rfkill * __must_check rfkill_alloc(const char *name, + struct device *parent, diff --git a/repos/dde_linux/patches/wpa_supplicant.patch b/repos/dde_linux/patches/wpa_supplicant.patch index 1e8bdeebac..31ae9411e7 100644 --- a/repos/dde_linux/patches/wpa_supplicant.patch +++ b/repos/dde_linux/patches/wpa_supplicant.patch @@ -1,7 +1,41 @@ diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c -index 5cff47fab..5cba03efe 100644 +index 5cff47fab..af08177b2 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c +@@ -1682,13 +1682,13 @@ static void wpa_driver_nl80211_rfkill_blocked(void *ctx) + { + struct wpa_driver_nl80211_data *drv = ctx; + +- wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); ++ wpa_printf(MSG_INFO, "nl80211: RFKILL blocked"); + + /* + * rtnetlink ifdown handler will report interfaces other than the P2P + * Device interface as disabled. + */ +- if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) ++ // if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); + } + +@@ -1696,7 +1696,7 @@ static void wpa_driver_nl80211_rfkill_blocked(void *ctx) + static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) + { + struct wpa_driver_nl80211_data *drv = ctx; +- wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); ++ wpa_printf(MSG_INFO, "nl80211: RFKILL unblocked"); + if (i802_set_iface_flags(drv->first_bss, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " + "after rfkill unblock"); +@@ -1710,7 +1710,7 @@ static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) + * rtnetlink ifup handler will report interfaces other than the P2P + * Device interface as enabled. + */ +- if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) ++ // if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); + } + @@ -7645,7 +7645,7 @@ static void * nl80211_global_init(void *ctx) if (wpa_driver_nl80211_init_nl_global(global) < 0) goto err; @@ -24,8 +58,30 @@ index 0e960f48c..38fb26c18 100644 struct netlink_data { struct netlink_config *cfg; +diff --git a/src/utils/common.c b/src/utils/common.c +index 1eb33705b..e4447306a 100644 +--- a/src/utils/common.c ++++ b/src/utils/common.c +@@ -498,12 +498,12 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) + *txt++ = 't'; + break; + default: +- if (data[i] >= 32 && data[i] <= 126) { ++ // if (data[i] >= 32 && data[i] <= 126) { + *txt++ = data[i]; +- } else { +- txt += os_snprintf(txt, end - txt, "\\x%02x", +- data[i]); +- } ++ // } else { ++ // txt += os_snprintf(txt, end - txt, "\\x%02x", ++ // data[i]); ++ // } + break; + } + } diff --git a/src/utils/eloop.c b/src/utils/eloop.c -index 436bc8c99..fd72eaef3 100644 +index 436bc8c99..f5ff4facb 100644 --- a/src/utils/eloop.c +++ b/src/utils/eloop.c @@ -28,7 +28,7 @@ @@ -37,75 +93,3 @@ index 436bc8c99..fd72eaef3 100644 #endif /* CONFIG_ELOOP_POLL */ #ifdef CONFIG_ELOOP_EPOLL -@@ -961,7 +961,7 @@ static void eloop_handle_alarm(int sig) - #endif /* CONFIG_NATIVE_WINDOWS */ - - --static void eloop_handle_signal(int sig) -+void eloop_handle_signal(int sig) - { - int i; - -diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c -index fb77f1dbd..9142f3f1b 100644 ---- a/wpa_supplicant/events.c -+++ b/wpa_supplicant/events.c -@@ -1754,6 +1754,9 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, - } - - -+extern void wpa_report_scan_results(struct wpa_supplicant *); -+ -+ - /* - * Return a negative value if no scan results could be fetched or if scan - * results should not be shared with other virtual interfaces. -@@ -1799,6 +1802,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, - goto scan_work_done; - } - -+ wpa_report_scan_results(wpa_s); -+ - #ifndef CONFIG_NO_RANDOM_POOL - num = scan_res->num; - if (num > 10) -@@ -2813,6 +2818,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) -@@ -2834,6 +2842,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, -diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c -index 185a8d50f..4baedabb3 100644 ---- a/wpa_supplicant/wpa_supplicant.c -+++ b/wpa_supplicant/wpa_supplicant.c -@@ -822,6 +822,9 @@ void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s) - } - - -+void wpa_report_connect_event(struct wpa_supplicant *); -+ -+ - /** - * wpa_supplicant_set_state - Set current connection state - * @wpa_s: Pointer to wpa_supplicant data -@@ -879,6 +882,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(wpa_s); - int fils_hlp_sent = 0; - - #ifdef CONFIG_SME diff --git a/repos/dde_linux/ports/dde_linux.hash b/repos/dde_linux/ports/dde_linux.hash index 47fb679ddd..7f5f02511b 100644 --- a/repos/dde_linux/ports/dde_linux.hash +++ b/repos/dde_linux/ports/dde_linux.hash @@ -1 +1 @@ -d2628a8fe8df14dd00d5fa8ef6bbea527d319bee +f8b3fc722728da35627dcd064209debe9e6aed92 diff --git a/repos/dde_linux/ports/dde_linux.port b/repos/dde_linux/ports/dde_linux.port index 566bd9c3a0..d5f3076ec6 100644 --- a/repos/dde_linux/ports/dde_linux.port +++ b/repos/dde_linux/ports/dde_linux.port @@ -129,6 +129,7 @@ PATCH_OPT(patches/lxip_skbuff_cast.patch) := $(LXIP_OPT) WIFI_OPT = -p1 -d$(SRC_DIR_WIFI) PATCH_OPT(patches/wifi.patch) := $(WIFI_OPT) PATCH_OPT(patches/wifi_mem.patch) := $(WIFI_OPT) +PATCH_OPT(patches/wifi_rfkill.patch) := $(WIFI_OPT) # libnl PATCH_OPT(patches/libnl.patch) := -p1 -d ${DIR(libnl)} diff --git a/repos/dde_linux/recipes/pkg/wifi/runtime b/repos/dde_linux/recipes/pkg/wifi/runtime index 2a65682750..e2324eefd6 100644 --- a/repos/dde_linux/recipes/pkg/wifi/runtime +++ b/repos/dde_linux/recipes/pkg/wifi/runtime @@ -1,17 +1,16 @@ - + - + - + - - + diff --git a/repos/dde_linux/run/wifi.run b/repos/dde_linux/run/wifi.run index e2f35b1d2e..84f1fd6a92 100644 --- a/repos/dde_linux/run/wifi.run +++ b/repos/dde_linux/run/wifi.run @@ -1,23 +1,36 @@ +# +# Configure wireless lan +# + +proc wifi_ssid { } { + return $::env(GENODE_WIFI_SSID) +} + +proc wifi_psk { } { + return $::env(GENODE_WIFI_PSK) +} + +# +# Restrict platforms +# +assert_spec x86 + # # Build # set build_components { core init - drivers/timer drivers/wifi drivers/rtc + drivers/timer + drivers/wifi server/report_rom server/dynamic_rom test/lwip/http_srv lib/vfs/jitterentropy + lib/vfs/lwip } -proc gpio_drv { } { if {[have_spec rpi] && [have_spec hw]} { return hw_gpio_drv } - if {[have_spec rpi] && [have_spec foc]} { return foc_gpio_drv } - return gpio_drv } - -lappend_if [have_spec gpio] build_components drivers/gpio - source ${genode_dir}/repos/base/run/platform_drv.inc append_platform_drv_build_components @@ -29,7 +42,7 @@ create_boot_directory # Generate config # -set config { +append config { @@ -49,69 +62,85 @@ set config { + + - + - - - + + + + + + - + + - - - - } -append config "" -append config { + + + + + - + + + + + + + + + + + + + + + + + + + - - + + + - - + + - + - - + } append_platform_drv_config -append_if [have_spec gpio] config " - - - - - " - append config { } @@ -137,19 +166,18 @@ set firmware_modules { # generic modules set boot_modules { core ld.lib.so init timer rtc_drv report_rom dynamic_rom - vfs_jitterentropy.lib.so posix.lib.so - libc.lib.so vfs.lib.so libm.lib.so libcrypto.lib.so libssl.lib.so + vfs_jitterentropy.lib.so + libc.lib.so vfs.lib.so libcrypto.lib.so libssl.lib.so wpa_driver_nl80211.lib.so wpa_supplicant.lib.so wifi.lib.so wifi_drv - test-lwip_httpsrv lwip_legacy.lib.so + test-lwip_httpsrv + vfs_lwip.lib.so } append boot_modules $firmware_modules -lappend_if [have_spec gpio] boot_modules [gpio_drv] - append_platform_drv_boot_modules build_boot_image $boot_modules diff --git a/repos/dde_linux/src/drivers/wifi/frontend.h b/repos/dde_linux/src/drivers/wifi/frontend.h new file mode 100644 index 0000000000..4aafd73d1f --- /dev/null +++ b/repos/dde_linux/src/drivers/wifi/frontend.h @@ -0,0 +1,1467 @@ +/* + * \brief Startup Wifi driver + * \author Josef Soentgen + * \date 2018-07-31 + * + * This wifi driver front end uses the CTRL interface of the wpa_supplicant via + * a Genode specific backend, which merely wraps a shared memory buffer, to + * manage the supplicant. + * + * Depending on the 'wifi_config' ROM content it will instruct the supplicant + * to enable, disable and connect to wireless networks. Commands and their + * corresponding execute result are handled by the '_cmd_handler' dispatcher. + * This handler drives the front end's state-machine. Any different type of + * action can only be initiated from the 'IDLE' state. Unsolicited events, e.g. + * a scan-results-available event, may influence the current state. Config + * updates are deferred in case the current state is not 'IDLE'. + * + * brain-dump + * ========== + * + * config update overview: + * [[block any new update]] > [mark stale] > [rm stale] > [add new] > [update new] > [[unblock update]] + * + * add new network: + * [[new ap]] > [ssid] > bssid? + [bssid] > [psk] > auto? + [enable] > new ap? + [[new ap]] + * + * update network: + * [[update ap] > bssid? + [bssid] > psk? + [psk] > auto? + [enable] > update ap? + [[update ap]] + * + * remove network: + * [[mark stale]] > [remove network] > stale? + [remove network] + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#ifndef _WIFI_FRONTEND_H_ +#define _WIFI_FRONTEND_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include + +/* rep includes */ +#include +#include + +/* local includes */ +#include + +/* declare manually as it is a internal hack^Winterface */ +extern void wifi_kick_socketcall(); + + +namespace Wifi { + struct Frontend; +} + + +/* keep ordered! */ +static struct Recv_msg_table { + char const *string; + size_t len; +} recv_table[] = { + { "OK", 2 }, + { "FAIL", 4 }, + { "CTRL-EVENT-SCAN-RESULTS", 23 }, + { "CTRL-EVENT-CONNECTED", 20 }, + { "CTRL-EVENT-DISCONNECTED", 23 }, + { "SME: Trying to authenticate", 27 }, +}; + +enum Rmi { + OK = 0, + FAIL, + SCAN_RESULTS, + CONNECTED, + DISCONNECTED, + SME_AUTH, +}; + + +static inline bool check_recv_msg(char const *msg, + Recv_msg_table const &entry) { + return Genode::strcmp(entry.string, msg, entry.len) == 0; } + + +static bool cmd_successful(char const *msg) { + return check_recv_msg(msg, recv_table[OK]); } + + +static bool cmd_fail(char const *msg) { + return check_recv_msg(msg, recv_table[FAIL]); } + + +static bool results_available(char const *msg) { + return check_recv_msg(msg, recv_table[SCAN_RESULTS]); } + + +static bool connecting_to_network(char const *msg) { + return check_recv_msg(msg, recv_table[SME_AUTH]); } + + +static bool scan_results(char const *msg) { + return Genode::strcmp("bssid", msg, 5) == 0; } + + +static bool list_network_results(char const *msg) { + return Genode::strcmp("network", msg, 7) == 0; } + + +/* + * Central network data structure + */ +struct Accesspoint +{ + using Bssid = Genode::String<17+1>; + using Freq = Genode::String< 4+1>; + using Prot = Genode::String< 7+1>; + using Ssid = Genode::String<32+1>; + using Pass = Genode::String<63+1>; + + /* + * Accesspoint information fields used by the front end + */ + Bssid bssid { }; + Freq freq { }; + Prot prot { }; + Ssid ssid { }; + Pass pass { }; + unsigned signal { 0 }; + + /* + * CTRL interface fields + * + * The 'enabled' field is set to true if ENABLE_NETWORK + * was successfully executed. The network itself might + * get disabled by wpa_supplicant itself in case it cannot + * connect to the network, which will _not_ be reflected + * here. + */ + int id { -1 }; + bool enabled { false }; + + /* + * Internal configuration fields + */ + bool auto_connect { false }; + bool update { false }; + bool stale { false }; + + /** + * Default constructor + */ + Accesspoint() { } + + /** + * Constructor that initializes information fields + */ + Accesspoint(char const *bssid, char const *freq, + char const *prot, char const *ssid, unsigned signal) + : bssid(bssid), freq(freq), prot(prot), ssid(ssid), signal(signal) + { } + + void invalidate() { ssid = Ssid(); } + + bool valid() const { return ssid.length() > 1; } + bool wpa() const { return prot != "NONE"; } + bool stored() const { return id != -1; } +}; + + +template +static void for_each_line(char const *msg, FUNC const &func) +{ + char line_buffer[1024]; + size_t cur = 0; + + while (msg[cur] != 0) { + size_t until = Util::next_char(msg, cur, '\n'); + Genode::memcpy(line_buffer, &msg[cur], until); + line_buffer[until] = 0; + cur += until + 1; + + func(line_buffer); + } +} + + +template +static void for_each_result_line(char const *msg, FUNC const &func) +{ + char line_buffer[1024]; + size_t cur = 0; + + /* skip headline */ + size_t until = Util::next_char(msg, cur, '\n'); + cur += until + 1; + + while (msg[cur] != 0) { + until = Util::next_char(msg, cur, '\n'); + Genode::memcpy(line_buffer, &msg[cur], until); + line_buffer[until] = 0; + cur += until + 1; + + char const *s[5] = { }; + + for (size_t c = 0, i = 0; i < 5; i++) { + size_t pos = Util::next_char(line_buffer, c, '\t'); + line_buffer[c+pos] = 0; + s[i] = (char const*)&line_buffer[c]; + c += pos + 1; + } + + bool const is_wpa1 = Util::string_contains((char const*)s[3], "WPA"); + bool const is_wpa2 = Util::string_contains((char const*)s[3], "WPA2"); + + unsigned signal = Util::approximate_quality(s[2]); + + char const *prot = is_wpa1 ? "WPA" : "NONE"; + prot = is_wpa2 ? "WPA2" : prot; + + Accesspoint ap(s[0], s[1], prot, s[4], signal); + + func(ap); + } +} + + +/* + * Wifi driver front end + */ +struct Wifi::Frontend +{ + /* accesspoint */ + + enum { MAX_ACCESSPOINTS = 16, }; + Accesspoint _aps[MAX_ACCESSPOINTS]; + + Accesspoint *_lookup_ap_by_ssid(Accesspoint::Ssid const &ssid) + { + for (Accesspoint &ap : _aps) { + if (ap.valid() && ap.ssid == ssid) { return ≈ } + } + return nullptr; + } + + Accesspoint *_lookup_ap_by_bssid(Accesspoint::Bssid const &bssid) + { + for (Accesspoint &ap : _aps) { + if (ap.valid() && ap.bssid == bssid) { return ≈ } + } + return nullptr; + } + + Accesspoint *_ap_slot() + { + for (Accesspoint &ap : _aps) { + if (!ap.valid()) { return ≈ } + } + return nullptr; + } + + void _free_ap_slot(Accesspoint &ap) + { + ap.ssid = Accesspoint::Ssid(); + ap.bssid = Accesspoint::Bssid(); + ap.freq = Accesspoint::Freq(); + ap.id = -1; + } + + template + void _for_each_ap(FUNC const &func) + { + for (Accesspoint &ap : _aps) { + if (!ap.valid()) { continue; } + func(ap); + } + } + + unsigned _count_to_be_enabled() + { + unsigned count = 0; + auto enable = [&](Accesspoint const &ap) { + count += ap.auto_connect; + }; + _for_each_ap(enable); + return count; + } + + unsigned _count_enabled() + { + unsigned count = 0; + auto enabled = [&](Accesspoint const &ap) { + count += ap.enabled; + }; + _for_each_ap(enabled); + return count; + } + + unsigned _count_stored() + { + unsigned count = 0; + auto enabled = [&](Accesspoint const &ap) { + count += ap.stored(); + }; + _for_each_ap(enabled); + return count; + } + + /* remaining stuff */ + + Msg_buffer _msg; + + Genode::Lock _notify_lock { Genode::Lock::UNLOCKED }; + + void _notify_lock_lock() { _notify_lock.lock(); } + void _notify_lock_unlock() { _notify_lock.unlock(); } + + bool _rfkilled { false }; + + Genode::Signal_handler _rfkill_handler; + + void _handle_rfkill() + { + _rfkilled = wifi_get_rfkill(); + + /* re-enable scan timer */ + if (!_rfkilled) { + _scan_timer.sigh(_scan_timer_sigh); + _arm_scan_timer(false); + } else { + _scan_timer.sigh(Genode::Signal_context_capability()); + } + + if (_rfkilled && _state != State::IDLE) { + Genode::warning(__func__, ": rfkilled with ", state_strings(_state)); + } + } + + /* config */ + + Genode::Attached_rom_dataspace _config_rom; + Genode::Signal_handler _config_sigh; + + bool _verbose { false }; + bool _verbose_state { false }; + bool _use_11n { true }; + + bool _deferred_config_update { false }; + bool _fake_connecting { false }; + + unsigned _connected_scan_interval { 15 }; + unsigned _scan_interval { 5 }; + + void _handle_config_update() + { + _config_rom.update(); + + if (!_config_rom.valid()) { return; } + + Genode::Xml_node config = _config_rom.xml(); + + _verbose = config.attribute_value("verbose", _verbose); + _verbose_state = config.attribute_value("verbose_state", _verbose_state); + + /* only evaluated at start-up */ + _use_11n = config.attribute_value("use_11n", _use_11n); + + _connected_scan_interval = + Util::check_time(config.attribute_value("connected_scan_interval", + _connected_scan_interval), + 0, 15*60); + + _scan_interval = + Util::check_time(config.attribute_value("scan_interval", + _scan_interval), + 5, 15*60); + + /* + * Always handle rfkill, regardless in which state we are currently in. + * When we come back from rfkill, will most certainly will be IDLE anyway. + */ + if (config.has_attribute("rfkill")) { + bool const blocked = config.attribute_value("rfkill", false); + wifi_set_rfkill(blocked); + + /* + * In case we get blocked set rfkilled immediately to prevent + * any further scanning operation. The actual value will be set + * by the singal handler but is not expected to be any different + * as the rfkill call is not supposed to fail. + */ + if (blocked) { _rfkilled = true; } + } + + /* + * Block any further config updates until we have finished applying + * the current one. + */ + if (_state != State::IDLE) { + Genode::warning("deferring config update (", state_strings(_state), ")"); + _deferred_config_update = true; + return; + } + + bool fake_connecting = false; + + /* update AP list */ + try { + config.for_each_sub_node("accesspoint", [&] ( Genode::Xml_node node) { + + Accesspoint ap; + ap.ssid = node.attribute_value("ssid", Accesspoint::Ssid("")); + ap.bssid = node.attribute_value("bssid", Accesspoint::Bssid("")); + + size_t const ssid_len = ap.ssid.length() - 1; + if (ssid_len == 0 || ssid_len > 32) { + Genode::warning("ignoring accesspoint with invalid ssid"); + return; + } + + Accesspoint *p = _lookup_ap_by_ssid(ap.ssid); + if (p) { + if (_verbose) { Genode::log("Update: '", p->ssid, "'"); } + /* mark for updating */ + p->update = true; + } else { + p = _ap_slot(); + if (!p) { + Genode::warning("could not add accesspoint, no slots left"); + return; + } + } + + ap.pass = node.attribute_value("passphrase", Accesspoint::Pass("")); + ap.prot = node.attribute_value("protection", Accesspoint::Prot("NONE")); + ap.auto_connect = node.attribute_value("auto_connect", true); + + if (ap.wpa()) { + size_t const psk_len = ap.pass.length() - 1; + if (psk_len < 8 || psk_len > 63) { + Genode::warning("ignoring accesspoint '", ap.ssid, + "' with invalid pass"); + return; + } + } + + + /* check if updating is really necessary */ + if (p->update) { + p->update = (ap.bssid != p->bssid + || ap.pass != p->pass + || ap.prot != p->prot + || ap.auto_connect != p->auto_connect); + } + + /* TODO add better way to check validity */ + if (ap.bssid.length() == 17 + 1) { p->bssid = ap.bssid; } + + p->ssid = ap.ssid; + p->prot = ap.prot; + p->pass = ap.pass; + p->auto_connect = ap.auto_connect; + + fake_connecting |= (p->update || p->auto_connect) && !_connected_ap.valid(); + }); + } catch (...) { Genode::warning("accesspoint list empty"); } + + /* + * To accomodate a management component that only deals + * with on network, e.g. the sculpt_manager, generate a + * fake connected event. + */ + if (_count_to_be_enabled() == 1 && fake_connecting) { + + auto lookup = [&] (Accesspoint const &ap) { + if (!ap.auto_connect) { return; } + + if (_verbose) { Genode::log("Fake connected event for '", ap.ssid, "'"); } + + try { + Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () { + xml.node("accesspoint", [&] () { + xml.attribute("ssid", ap.ssid); + xml.attribute("state", "connected"); + }); + }); + + _fake_connecting = true; + + } catch (...) { } + }; + _for_each_ap(lookup); + } + + /* + * Marking removes stale APs first and triggers adding of + * new ones afterwards. + */ + _mark_stale_aps(config); + } + + /* state */ + + Accesspoint *_processed_ap { nullptr }; + Accesspoint _connected_ap { }; + + enum State { + IDLE = 0x00, + SCAN = 0x01, + NETWORK = 0x02, + CONNECT = 0x03, + STATUS = 0x04, + INFO = 0x05, + + INITIATE_SCAN = 0x00|SCAN, + PENDING_RESULTS = 0x10|SCAN, + + ADD_NETWORK = 0x00|NETWORK, + FILL_NETWORK_SSID = 0x10|NETWORK, + FILL_NETWORK_BSSID = 0x20|NETWORK, + FILL_NETWORK_PSK = 0x30|NETWORK, + REMOVE_NETWORK = 0x40|NETWORK, + ENABLE_NETWORK = 0x50|NETWORK, + DISABLE_NETWORK = 0x60|NETWORK, + DISCONNECT_NETWORK = 0x70|NETWORK, + LIST_NETWORKS = 0x80|NETWORK, + + CONNECTING = 0x00|CONNECT, + CONNECTED = 0x10|CONNECT, + DISCONNECTED = 0x20|CONNECT, + }; + + State _state { State::IDLE }; + + char const *state_strings(State state) + { + switch (state) { + case IDLE: return "idle"; + case INITIATE_SCAN: return "initiate scan"; + case PENDING_RESULTS: return "pending results"; + case ADD_NETWORK: return "add network"; + case FILL_NETWORK_SSID: return "fill network ssid"; + case FILL_NETWORK_BSSID: return "fill network bssid"; + case FILL_NETWORK_PSK: return "fill network pass"; + case REMOVE_NETWORK: return "remove network"; + case ENABLE_NETWORK: return "enable network"; + case DISABLE_NETWORK: return "disable network"; + case CONNECTING: return "connecting"; + case CONNECTED: return "connected"; + case DISCONNECTED: return "disconnected"; + case STATUS: return "status"; + case LIST_NETWORKS: return "list networks"; + case INFO: return "info"; + default: return "unknown"; + }; + } + + void _state_transition(State ¤t, State next) + { + if (_verbose_state) { + using namespace Genode; + log("Transition: ", state_strings(current), " -> ", + state_strings(next)); + } + + current = next; + } + + using Cmd_str = Genode::String<1024>; + + void _submit_cmd(Cmd_str const &str) + { + Genode::memset(_msg.send, 0, sizeof(_msg.send)); + Genode::memcpy(_msg.send, str.string(), str.length()); + ++_msg.send_id; + + wpa_ctrl_set_fd(); + + /* + * We might have to pull the socketcall task out of poll_all() + * because otherwise we might be late and wpa_supplicant has + * already removed all scan results due to BSS age settings. + */ + wifi_kick_socketcall(); + } + + /* scan */ + + Timer::Connection _scan_timer; + Genode::Signal_handler _scan_timer_sigh; + + void _handle_scan_timer() + { + /* + * If we are blocked or currently trying to join a network + * suspend scanning. + */ + if (_rfkilled || _connecting.length() > 1) { + if (_verbose) { Genode::log("Suspend scan timer"); } + return; + } + + _arm_scan_timer(_connected_ap.valid()); + + /* skip as we will be scheduled some time soon(tm) anyway */ + if (_state != State::IDLE) { return; } + + /* TODO scan request/pending results timeout */ + _state_transition(_state, State::INITIATE_SCAN); + _submit_cmd(Cmd_str("SCAN")); + } + + void _arm_scan_timer(bool connected) + { + unsigned const sec = connected ? _connected_scan_interval : _scan_interval; + if (!sec) { return; } + + if (_verbose) { + Genode::log("Arm ", connected ? "connected " : "", + "scan: ", sec, " sec"); + } + + _scan_timer.trigger_once(sec * (1000 * 1000)); + } + + Genode::Constructible _ap_reporter; + + void _generate_scan_results_report(char const *msg) + { + unsigned count_lines = 0; + for_each_line(msg, [&] (char const*) { count_lines++; }); + + if (!count_lines) { return; } + + try { + Genode::Reporter::Xml_generator xml(*_ap_reporter, [&]() { + + for_each_result_line(msg, [&] (Accesspoint const &ap) { + + /* ignore potentially empty ssids */ + if (ap.ssid == "") { return; } + + xml.node("accesspoint", [&]() { + xml.attribute("ssid", ap.ssid); + xml.attribute("bssid", ap.bssid); + xml.attribute("freq", ap.freq); + xml.attribute("quality", ap.signal); + if (ap.wpa()) { xml.attribute("protection", ap.prot); } + }); + }); + }); + } catch (...) { /* silently omit report */ } + } + + /* network commands */ + + void _mark_stale_aps(Genode::Xml_node const &config) + { + auto mark_stale = [&] (Accesspoint &ap) { + ap.stale = true; + + config.for_each_sub_node("accesspoint", [&] ( Genode::Xml_node node) { + Accesspoint::Ssid ssid = node.attribute_value("ssid", Accesspoint::Ssid("")); + + if (ap.ssid == ssid) { ap.stale = false; } + }); + }; + _for_each_ap(mark_stale); + + _remove_stale_aps(); + } + + void _remove_stale_aps() + { + if (_state != State::IDLE) { + Genode::warning("cannot remove stale APs in non-idle state " + "(", state_strings(_state), ")"); + return; + } + + if (_processed_ap) { return; } + + for (Accesspoint &ap : _aps) { + if (ap.valid() && ap.stale) { + _processed_ap = ≈ + break; + } + } + + if (!_processed_ap) { + /* TODO move State transition somewhere more sane */ + _state_transition(_state, State::IDLE); + _add_new_aps(); + return; + } + + if (_verbose) { + Genode::log("Remove network: '", _processed_ap->ssid, "'"); + } + + _state_transition(_state, State::REMOVE_NETWORK); + _submit_cmd(Cmd_str("REMOVE_NETWORK ", _processed_ap->id)); + } + + void _update_aps() + { + if (_state != State::IDLE) { + Genode::warning("cannot enable network in non-idle state"); + return; + } + + if (_processed_ap) { return; } + + for (Accesspoint &ap : _aps) { + if (ap.valid() && ap.stored() && ap.update) { + _processed_ap = ≈ + break; + } + } + + if (!_processed_ap) { return; } + + if (_verbose) { + Genode::log("Update network: '", _processed_ap->ssid, "'"); + } + + // _processed_ap->update = false; + + /* re-use state to change PSK */ + _state_transition(_state, State::FILL_NETWORK_PSK); + _network_set_psk(); + } + + + void _add_new_aps() + { + if (_state != State::IDLE) { + Genode::warning("cannot enable network in non-idle state"); + return; + } + + if (_processed_ap) { return; } + + for (Accesspoint &ap : _aps) { + if (ap.valid() && !ap.stored()) { + _processed_ap = ≈ + break; + } + } + + if (!_processed_ap) { + /* XXX move State transition somewhere more sane */ + _state_transition(_state, State::IDLE); + _update_aps(); + return; + } + + if (_verbose) { + Genode::log("Add network: '", _processed_ap->ssid, "'"); + } + + _state_transition(_state, State::ADD_NETWORK); + _submit_cmd(Cmd_str("ADD_NETWORK")); + } + + void _network_enable() + { + if (_state != State::IDLE) { + Genode::warning("cannot enable network in non-idle state"); + return; + } + + if (_processed_ap) { return; } + + for (Accesspoint &ap : _aps) { + if ( ap.valid() + && !ap.enabled && ap.auto_connect) { + _processed_ap = ≈ + break; + } + } + + if (!_processed_ap) { + + return; + } + + if (_verbose) { + Genode::log("Enable network: '", _processed_ap->ssid, "'"); + } + + _state_transition(_state, State::ENABLE_NETWORK); + _submit_cmd(Cmd_str("ENABLE_NETWORK ", _processed_ap->id)); + } + + void _network_disable() + { + if (_state != State::IDLE) { + Genode::warning("cannot enable network in non-idle state"); + return; + } + + if (_processed_ap) { return; } + + for (Accesspoint &ap : _aps) { + if (ap.valid() && ap.enabled && !ap.auto_connect) { + _processed_ap = ≈ + break; + } + } + + if (!_processed_ap) { + /* XXX move State transition somewhere more sane */ + _state_transition(_state, State::IDLE); + _add_new_aps(); + return; + } + + if (_verbose) { + Genode::log("Disable network: '", _processed_ap->ssid, "'"); + } + + _state_transition(_state, State::DISABLE_NETWORK); + _submit_cmd(Cmd_str("DISABLE_NETWORK ", _processed_ap->id)); + } + + void _network_disconnect() + { + _state_transition(_state, State::DISCONNECT_NETWORK); + _submit_cmd(Cmd_str("DISCONNECT")); + } + + void _network_set_ssid(char const *msg) + { + long id = -1; + Genode::ascii_to(msg, id); + + _processed_ap->id = id; + _submit_cmd(Cmd_str("SET_NETWORK ", _processed_ap->id, + " ssid \"", _processed_ap->ssid, "\"")); + } + + void _network_set_bssid() + { + bool const valid = _processed_ap->bssid.length() == 17 + 1; + char const *bssid = valid ? _processed_ap->bssid.string() : ""; + + _submit_cmd(Cmd_str("SET_NETWORK ", _processed_ap->id, + " bssid \"", bssid, "\"")); + } + + void _network_set_psk() + { + if (_processed_ap->wpa()) { + _submit_cmd(Cmd_str("SET_NETWORK ", _processed_ap->id, + " psk \"", _processed_ap->pass, "\"")); + } else { + _submit_cmd(Cmd_str("SET_NETWORK ", _processed_ap->id, + " key_mgmt NONE")); + } + } + + /* result handling */ + + void _handle_scan_results(State state, char const *msg) + { + switch (state) { + case State::INITIATE_SCAN: + if (!cmd_successful(msg)) { + Genode::warning("could not initiate scan: ", msg); + } + _state_transition(_state, State::IDLE); + break; + case State::PENDING_RESULTS: + if (scan_results(msg)) { + _state_transition(_state, State::IDLE); + _generate_scan_results_report(msg); + } + break; + default: + Genode::warning("unknown SCAN state: ", msg); + break; + } + } + + void _handle_network_results(State state, char const *msg) + { + bool successfully = false; + + switch (state) { + case State::ADD_NETWORK: + if (cmd_fail(msg)) { + Genode::error("could not add network: ", msg); + _state_transition(_state, State::IDLE); + } else { + _state_transition(_state, State::FILL_NETWORK_SSID); + _network_set_ssid(msg); + + successfully = true; + } + break; + case State::REMOVE_NETWORK: + _state_transition(_state, State::IDLE); + + if (cmd_fail(msg)) { + Genode::error("could not remove network: ", msg); + } else { + _free_ap_slot(*_processed_ap); + + /* trigger the next round */ + _processed_ap = nullptr; + _remove_stale_aps(); + + successfully = true; + } + break; + case State::FILL_NETWORK_SSID: + _state_transition(_state, State::IDLE); + + if (!cmd_successful(msg)) { + Genode::error("could not set ssid for network: ", msg); + _state_transition(_state, State::IDLE); + } else { + _state_transition(_state, State::FILL_NETWORK_BSSID); + _network_set_bssid(); + + successfully = true; + } + break; + case State::FILL_NETWORK_BSSID: + _state_transition(_state, State::IDLE); + + if (!cmd_successful(msg)) { + Genode::error("could not set bssid for network: ", msg); + _state_transition(_state, State::IDLE); + } else { + + _state_transition(_state, State::FILL_NETWORK_PSK); + _network_set_psk(); + + successfully = true; + } + break; + case State::FILL_NETWORK_PSK: + _state_transition(_state, State::IDLE); + + if (!cmd_successful(msg)) { + Genode::error("could not set passphrase for network: ", msg); + } else { + + /* + * Disable network to trick wpa_supplicant into reloading + * the settings. + */ + if (_processed_ap->update) { + _processed_ap->enabled = false; + + _state_transition(_state, State::DISABLE_NETWORK); + _submit_cmd(Cmd_str("DISABLE_NETWORK ", _processed_ap->id)); + } else + + if (_processed_ap->auto_connect) { + + _state_transition(_state, State::ENABLE_NETWORK); + _submit_cmd(Cmd_str("ENABLE_NETWORK ", _processed_ap->id)); + } else { + /* trigger the next round */ + _processed_ap = nullptr; + _add_new_aps(); + } + + successfully = true; + } + break; + case State::ENABLE_NETWORK: + _state_transition(_state, State::IDLE); + + if (!cmd_successful(msg)) { + Genode::error("could not enable network: ", msg); + } else { + _processed_ap->enabled = true; + + /* trigger the next round */ + _processed_ap = nullptr; + _add_new_aps(); + + successfully = true; + } + break; + case State::DISABLE_NETWORK: + _state_transition(_state, State::IDLE); + + if (!cmd_successful(msg)) { + Genode::error("could not disable network: ", msg); + } else { + + /* + * Updated settings are applied, enable the network + * anew an try again. + */ + if (_processed_ap->update) { + _processed_ap->update = false; + + if (_processed_ap->auto_connect) { + _state_transition(_state, State::ENABLE_NETWORK); + _submit_cmd(Cmd_str("ENABLE_NETWORK ", _processed_ap->id)); + } + } else { + + _processed_ap->enabled = false; + + /* trigger the next round */ + _processed_ap = nullptr; + _network_disable(); + } + + successfully = true; + } + break; + case State::DISCONNECT_NETWORK: + _state_transition(_state, State::IDLE); + + if (!cmd_successful(msg)) { + Genode::error("could not disconnect from network: ", msg); + } else { + _network_disable(); + successfully = true; + } + break; + case State::LIST_NETWORKS: + _state_transition(_state, State::IDLE); + + if (list_network_results(msg)) { + Genode::error("List networks:\n", msg); + } + break; + default: + Genode::warning("unknown network state: ", msg); + break; + } + + /* + * If some step failed we have to generate a fake + * disconnect event. + */ + if (_fake_connecting && !successfully) { + try { + Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () { + xml.node("accesspoint", [&] () { + xml.attribute("state", "disconnected"); + xml.attribute("config_error", true); + }); + }); + + _fake_connecting = false; + } catch (...) { } + } + } + + void _handle_status_result(State state, char const *msg) + { + _state_transition(_state, State::IDLE); + + _state_transition(_state, State::LIST_NETWORKS); + _submit_cmd(Cmd_str("LIST_NETWORKS")); + } + + void _handle_info_result(State state, char const *msg) + { + _state_transition(_state, State::IDLE); + + if (!_connected_event && !_disconnected_event) { return; } + + Accesspoint ap { }; + + auto fill_ap = [&] (char const *line) { + if (Genode::strcmp(line, "ssid=", 5) == 0) { + ap.ssid = Accesspoint::Ssid(line+5); + } else + + if (Genode::strcmp(line, "bssid=", 6) == 0) { + ap.bssid = Accesspoint::Bssid(line+6); + } else + + if (Genode::strcmp(line, "freq=", 5) == 0) { + ap.freq = Accesspoint::Freq(line+5); + } + }; + for_each_line(msg, fill_ap); + + /* + * When the config is changed while we are still connecting and + * for some reasons the accesspoint does not get disabled + * a connected event could arrive and we will get a nullptr... + */ + Accesspoint *p = _lookup_ap_by_ssid(ap.ssid); + if (!p) { + Genode::warning("received connection event for unknown " + "network '", ap.ssid, "'"); + } + + /* + * ... but we still generate a report and let the management + * component deal with it. + */ + Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () { + xml.node("accesspoint", [&] () { + xml.attribute("ssid", ap.ssid); + xml.attribute("bssid", ap.bssid); + xml.attribute("freq", ap.freq); + xml.attribute("state", _connected_event ? "connected" + : "disconnected"); + if (!_connected_event) { + xml.attribute("rfkilled", _rfkilled); + xml.attribute("auth_failure", _disconnected_fail); + } + }); + }); + + if (_disconnected_fail) { + + if (!p || _processed_ap || _state != State::IDLE) { + Genode::error("cannot disabled failed network"); + } else { + _processed_ap = p; + _state_transition(_state, State::DISABLE_NETWORK); + _submit_cmd(Cmd_str("DISABLE_NETWORK ", p->id)); + } + } else + + if (_connected_event) { + if (!p) { + Genode::error("cannot lookup connected network"); + return; + } + + p->bssid = ap.bssid; + p->freq = ap.freq; + + _connected_ap = ap; + } else + + /* just your normal disconnect event */ + { + _connected_ap.invalidate(); + } + } + + /* connection state */ + + Genode::Constructible _state_reporter { }; + + Accesspoint::Bssid _connecting { }; + + Accesspoint::Bssid const _extract_bssid(char const *msg, State state) + { + char bssid[32] = { }; + /* by the power of wc -c, I have the start pos... */ + enum { BSSID_CONNECT = 37, BSSID_DISCONNECT = 30, BSSID_CONNECTING = 33, }; + + bool const connected = state == State::CONNECTED; + bool const connecting = state == State::CONNECTING; + + size_t const len = 17; + size_t const start = connected ? BSSID_CONNECT + : connecting ? BSSID_CONNECTING + : BSSID_DISCONNECT; + Genode::memcpy(bssid, msg + start, len); + return Accesspoint::Bssid((char const*)bssid); + } + + bool _auth_failure(char const *msg) + { + enum { REASON_OFFSET = 55, }; + unsigned reason = 0; + Genode::ascii_to((msg + REASON_OFFSET), reason); + switch (reason) { + case 2: /* prev auth no longer valid */ + case 15: /* 4-way handshake timeout/failed */ + return true; + default: + return false; + } + } + + /* events */ + + bool _connected_event { false }; + bool _disconnected_event { false }; + bool _disconnected_fail { false }; + + Accesspoint::Bssid _pending_bssid { }; + + bool _handle_connection_events(char const *msg) + { + bool const connected = check_recv_msg(msg, recv_table[Rmi::CONNECTED]); + bool const disconnected = check_recv_msg(msg, recv_table[Rmi::DISCONNECTED]); + + State state = connected ? State::CONNECTED : State::DISCONNECTED; + + Accesspoint::Bssid const &bssid = _extract_bssid(msg, state); + + _connected_event = connected; + _disconnected_event = disconnected; + _disconnected_fail = disconnected && _auth_failure(msg); + + if (connected) { + if (_state != State::IDLE) { + _pending_bssid = bssid; + } else { + _state_transition(_state, State::INFO); + _submit_cmd(Cmd_str("BSS ", bssid)); + } + } else { + + /* + * In case of disconnected event use stored information if we + * already were connected. + */ + Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () { + + xml.node("accesspoint", [&] () { + if (_connected_ap.valid()) { + xml.attribute("ssid", _connected_ap.ssid); + xml.attribute("bssid", _connected_ap.bssid); + xml.attribute("freq", _connected_ap.freq); + } else { + xml.attribute("bssid", bssid); + } + xml.attribute("state", "disconnected"); + xml.attribute("rfkilled", _rfkilled); + xml.attribute("auth_failure", _disconnected_fail); + }); + }); + + _connected_ap.invalidate(); + + /* arm scan timer implicitly */ + _handle_scan_timer(); + } + + /* reset */ + _fake_connecting = false; + + return connected || disconnected; + } + + Genode::Signal_handler _events_handler; + + unsigned _last_event_id { 0 }; + + void _handle_events() + { + char const *msg = reinterpret_cast(_msg.event); + unsigned const event_id = _msg.event_id; + + /* return early */ + if (_last_event_id == event_id) { + _notify_lock_unlock(); + return; + } + + if (_rfkilled) { + Genode::warning(__func__, ": rfkilled with ", state_strings(_state)); + } + + if (results_available(msg)) { + + /* + * We might have to pull the socketcall task out of poll_all() + * because otherwise we might be late and wpa_supplicant has + * already removed all scan results due to BSS age settings. + */ + wifi_kick_socketcall(); + + if (_state == State::IDLE) { + _state_transition(_state, State::PENDING_RESULTS); + _submit_cmd(Cmd_str("SCAN_RESULTS")); + } + } else + + if (connecting_to_network(msg)) { + if (!_fake_connecting) { + Accesspoint::Bssid const &bssid = _extract_bssid(msg, State::CONNECTING); + _connecting = bssid; + + Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () { + xml.node("accesspoint", [&] () { + xml.attribute("bssid", bssid); + xml.attribute("state", "connecting"); + }); + }); + } + } else + + { + _handle_connection_events(msg); + } + + _notify_lock_unlock(); + } + + Genode::Signal_handler _cmd_handler; + + unsigned _last_recv_id { 0 }; + + void _handle_cmds() + { + char const *msg = reinterpret_cast(_msg.recv); + unsigned const recv_id = _msg.recv_id; + + /* return early */ + if (_last_recv_id == recv_id) { + _notify_lock_unlock(); + return; + } + + if (_rfkilled) { + Genode::warning(__func__, ": rfkilled with ", state_strings(_state)); + } + + _last_recv_id = recv_id; + + switch (_state & 0xf) { + case State::SCAN: + _handle_scan_results(_state, msg); + break; + case State::NETWORK: + _handle_network_results(_state, msg); + break; + case State::STATUS: + _handle_status_result(_state, msg); + break; + case State::INFO: + _handle_info_result(_state, msg); + break; + case State::IDLE: + default: + break; + } + _notify_lock_unlock(); + + if (_verbose_state) { + Genode::log("State:", + " connected: ", _connected_ap.valid(), + " connecting: ", _connecting.length() > 1, + " enabled: ", _count_enabled(), + " stored: ", _count_stored(), + ""); + } + + if (_state == State::IDLE && _deferred_config_update) { + _deferred_config_update = false; + _handle_config_update(); + } + + if (_state == State::IDLE && _pending_bssid.length() > 1) { + _state_transition(_state, State::INFO); + _submit_cmd(Cmd_str("BSS ", _pending_bssid)); + + _pending_bssid = Accesspoint::Bssid(); + } + } + + /** + * Constructor + */ + Frontend(Genode::Env &env) + : + _rfkill_handler(env.ep(), *this, &Wifi::Frontend::_handle_rfkill), + _config_rom(env, "wifi_config"), + _config_sigh(env.ep(), *this, &Wifi::Frontend::_handle_config_update), + _scan_timer(env), + _scan_timer_sigh(env.ep(), *this, &Wifi::Frontend::_handle_scan_timer), + _events_handler(env.ep(), *this, &Wifi::Frontend::_handle_events), + _cmd_handler(env.ep(), *this, &Wifi::Frontend::_handle_cmds) + { + _config_rom.sigh(_config_sigh); + _scan_timer.sigh(_scan_timer_sigh); + + try { + _ap_reporter.construct(env, "accesspoints"); + _ap_reporter->enabled(true); + } catch (...) { + Genode::warning("no Report session available, scan results will " + "not be reported"); + } + + try { + _state_reporter.construct(env, "state"); + _state_reporter->enabled(true); + } catch (...) { + Genode::warning("no Report session available, connectivity will " + "not be reported"); + } + + /* read in list of APs */ + _handle_config_update(); + + /* kick-off initial scanning */ + _handle_scan_timer(); + } + + /** + * Return if 11n operation is enabled + */ + bool use_11n() const { return _use_11n; } + + /** + * Get RFKILL signal capability + * + * Used by the wifi_drv to notify front end. + */ + Genode::Signal_context_capability rfkill_sigh() + { + return _rfkill_handler; + } + + /** + * Get result signal capability + * + * Used by the wpa_supplicant to notify front end after processing + * a command. + */ + Genode::Signal_context_capability result_sigh() + { + return _cmd_handler; + } + + /** + * Get event signal capability + * + * Used by the wpa_supplicant to notify front whenever a event + * was triggered. + */ + Genode::Signal_context_capability event_sigh() + { + return _events_handler; + } + + /** + * Block until events were handled by the front end + * + * Used by the wpa_supplicant to wait for the front end. + */ + void block_for_processing() { _notify_lock_lock(); } + + /** + * Return shared memory message buffer + * + * Used for communication between front end and wpa_supplicant. + */ + Msg_buffer &msg_buffer() { return _msg; } +}; + +#endif /* _WIFI_FRONTEND_H_ */ diff --git a/repos/dde_linux/src/drivers/wifi/main.cc b/repos/dde_linux/src/drivers/wifi/main.cc index e6843612a7..b1d90cc144 100644 --- a/repos/dde_linux/src/drivers/wifi/main.cc +++ b/repos/dde_linux/src/drivers/wifi/main.cc @@ -13,247 +13,104 @@ /* Genode includes */ #include -#include +#include #include #include -#include +#include +#include #include -#include /* local includes */ -#include "wpa.h" - -typedef long long ssize_t; - -extern void wifi_init(Genode::Env&, Genode::Lock&, bool); -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() -{ - static Genode::Lock _l(Genode::Lock::LOCKED); - return _l; -} +#include +#include +#include -namespace { - template - class Buffer - { - private: - - char _data[CAPACITY] { 0 }; - Genode::size_t _length { 0 }; - - public: - - void reset() { _data[0] = 0; _length = 0; } - char const *data() const { return _data; } - Genode::size_t length() const { return _length; } - - void append(char const *format, ...) - { - va_list list; - va_start(list, format); - Genode::String_console sc(_data + _length, CAPACITY - _length); - sc.vprintf(format, list); - va_end(list); - - _length += sc.len(); - } - }; -} /* anonymous namespace */ +static Wifi::Frontend *_wifi_frontend = nullptr; /** - * Generate wpa_supplicant.conf file + * Notify front end about command processing + * + * Called by the CTRL interface after wpa_supplicant has processed + * the command. */ -static int generatewpa_supplicant_conf(char const **p, Genode::size_t *len, char const *ssid, - char const *bssid, bool protection = false, char const *psk = 0) +void wifi_notify_cmd_result(void) { - static char const *start_fmt = "network={\n\tscan_ssid=1\n"; - static char const *ssid_fmt = "\tssid=\"%s\"\n"; - static char const *bssid_fmt = "\tbssid=%s\n"; - static char const *prot_fmt = "\tkey_mgmt=%s\n"; - static char const *psk_fmt = "\tpsk=\"%s\"\n"; - static char const *end_fmt = "}\n"; + if (!_wifi_frontend) { + Genode::warning("frontend not available, dropping notification"); + return; + } - static Buffer<256> buffer; - buffer.reset(); + /* + * Next time we block as long as the front end has not finished + * handling our previous request + */ + _wifi_frontend->block_for_processing(); - buffer.append(start_fmt); + /* XXX hack to trick poll() into returning faster */ + wpa_ctrl_set_fd(); - if (ssid) - buffer.append(ssid_fmt, ssid); - - if (bssid) - buffer.append(bssid_fmt, bssid); - - if (protection) - buffer.append(psk_fmt, psk); - - buffer.append(prot_fmt, protection ? "WPA-PSK" : "NONE"); - - buffer.append(end_fmt); - - *p = buffer.data(); - *len = buffer.length(); - - return 0; + Genode::Signal_transmitter(_wifi_frontend->result_sigh()).submit(); } -struct Wlan_configration +/** + * Notify front end about triggered event + * + * Called by the CTRL interface whenever wpa_supplicant has triggered + * a event. + */ +void wifi_notify_event(void) { - Genode::Attached_rom_dataspace config_rom; - Genode::Signal_handler dispatcher; - Genode::Lock update_lock; - - char const *buffer; - Genode::size_t size; - - /** - * Write configuration buffer to conf file to activate the new configuration. - */ - void _activate_configuration() - { - if (wpa_write_conf(buffer, size) == 0) { - Genode::log("Reload wpa_supplicant configuration"); - wpa_conf_reload(); - } + if (!_wifi_frontend) { + Genode::warning("frontend not available, dropping notification"); + return; } - /** - * Write dummy configuration buffer to conf file to activate the new - * configuration. - */ - void _active_dummy_configuration() - { - generatewpa_supplicant_conf(&buffer, &size, "dummyssid", "00:00:00:00:00:00"); - _activate_configuration(); - } + Genode::Signal_transmitter(_wifi_frontend->event_sigh()).submit(); +} - /** - * Update the conf file used by the wpa_supplicant. - */ - void _update_configuration() - { - using namespace Genode; - Lock::Guard guard(update_lock); +/** + * Return shared-memory message buffer + * + * It is used by the wpa_supplicant CTRL interface. + */ +void *wifi_get_buffer(void) +{ + return _wifi_frontend ? &_wifi_frontend->msg_buffer() : nullptr; +} - config_rom.update(); - /** - * We generate a dummy configuration because there is no valid - * configuration yet to fool wpa_supplicant to keep it scanning - * for the non exisiting network. - */ - if (!config_rom.valid()) { - _active_dummy_configuration(); - return; - } - - Xml_node node(config_rom.local_addr(), config_rom.size()); - - /** - * Since is empty or missing an ssid attribute - * we also generate a dummy configuration. - */ - if (!node.has_attribute("ssid")) { - _active_dummy_configuration(); - return; - } - - /** - * Try to generate a valid configuration. - */ - enum { MAX_SSID_LENGTH = 32 + 1, - BSSID_LENGTH = 12 + 5 + 1, - PROT_LENGTH = 7 + 1, - MIN_PSK_LENGTH = 8 + 1, - MAX_PSK_LENGTH = 63 + 1}; - - String ssid; - node.attribute("ssid").value(&ssid); - - bool use_bssid = node.has_attribute("bssid"); - String bssid; - if (use_bssid) - node.attribute("bssid").value(&bssid); - - bool use_protection = false; - if (node.has_attribute("protection")) { - String prot; - node.attribute("protection").value(&prot); - use_protection = (prot == "WPA-PSK"); - } - - String psk; - if (use_protection && node.has_attribute("psk")) - node.attribute("psk").value(&psk); - - /* psk must be between 8 and 63 characters long */ - if (use_protection && (psk.length() < MIN_PSK_LENGTH)) { - Genode::error("given pre-shared key is too short"); - _active_dummy_configuration(); - return; - } - - if (generatewpa_supplicant_conf(&buffer, &size, ssid.string(), - use_bssid ? bssid.string() : 0, - use_protection, psk.string()) == 0) - _activate_configuration(); - } - - void _handle_update() - { - Libc::with_libc([&] () { _update_configuration(); }); - } - - Wlan_configration(Genode::Env &env) - : - config_rom(env, "wlan_configuration"), - dispatcher(env.ep(), *this, &Wlan_configration::_handle_update) - { - config_rom.sigh(dispatcher); - _update_configuration(); - } -}; +/* exported by wifi.lib.so */ +extern void wifi_init(Genode::Env&, Genode::Lock&, bool, Genode::Signal_context_capability); struct Main { Genode::Env &env; - Genode::Heap heap { env.ram(), env.rm() }; - Genode::Attached_rom_dataspace config_rom { env, "config" }; + Genode::Constructible _wpa; + Genode::Constructible _frontend; - Wpa_thread *wpa; - Wlan_configration *wlan_config; + Genode::Lock _wpa_startup_lock { Genode::Lock::LOCKED }; Main(Genode::Env &env) : env(env) { - bool const verbose = config_rom.xml().attribute_value("verbose", false); - long const interval = config_rom.xml().attribute_value("connected_scan_interval", 0L); + _frontend.construct(env); + _wifi_frontend = &*_frontend; + + _wpa.construct(env, _wpa_startup_lock); + _wpa->start(); /* * Forcefully disable 11n but for convenience the attribute is used the * other way araound. */ - bool const disable_11n = !config_rom.xml().attribute_value("use_11n", true); - - wpa = new (&heap) Wpa_thread(env, wpa_startup_lock(), verbose, interval); - - wpa->start(); - - try { - wlan_config = new (&heap) Wlan_configration(env); - } catch (...) { - Genode::warning("could not create Wlan_configration handler"); - } - - wifi_init(env, wpa_startup_lock(), disable_11n); + bool const disable_11n = !_frontend->use_11n(); + wifi_init(env, _wpa_startup_lock, disable_11n, + _frontend->rfkill_sigh()); } }; diff --git a/repos/dde_linux/src/drivers/wifi/target.mk b/repos/dde_linux/src/drivers/wifi/target.mk index 2e953c2a28..20b3d4e450 100644 --- a/repos/dde_linux/src/drivers/wifi/target.mk +++ b/repos/dde_linux/src/drivers/wifi/target.mk @@ -7,5 +7,6 @@ LIBS += wpa_supplicant # needed for firmware.h INC_DIR += $(REP_DIR)/src/lib/wifi/include +INC_DIR += $(PRG_DIR) CC_CXX_WARN_STRICT = diff --git a/repos/dde_linux/src/drivers/wifi/util.h b/repos/dde_linux/src/drivers/wifi/util.h new file mode 100644 index 0000000000..f5d07dda65 --- /dev/null +++ b/repos/dde_linux/src/drivers/wifi/util.h @@ -0,0 +1,90 @@ +/* + * \brief Wifi front end utilities + * \author Josef Soentgen + * \date 2018-07-23 + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#ifndef _WIFI__UTIL_H_ +#define _WIFI__UTIL_H_ + +/* Genode includes */ +#include + +typedef unsigned long size_t; +typedef long long ssize_t; +typedef unsigned char uint8_t; + + +namespace Util { + + using size_t = Genode::size_t; + using uint8_t = Genode::uint8_t; + + size_t next_char(char const *s, size_t start, char const c) + { + size_t v = start; + while (s[v]) { + if (s[v] == c) { break; } + v++; + } + return v - start; + } + + bool string_contains(char const *str, char const *pattern) + { + char const *p = pattern; + while (*str && *p) { + p = *str == *p ? p + 1 : pattern; + str++; + } + return !*p; + } + + void byte2hex(char *dest, uint8_t b) + { + int i = 1; + if (b < 16) { dest[i--] = '0'; } + + for (; b > 0; b /= 16) { + uint8_t const v = b % 16; + uint8_t const c = (v > 9) ? v + 'a' - 10 : v + '0'; + dest[i--] = (char)c; + } + } + + /********************************** + ** Front end specific utilities ** + **********************************/ + + inline unsigned approximate_quality(char const *str) + { + long level = 0; + Genode::ascii_to(str, level); + + /* + * We provide an quality value by transforming the actual + * signal level [-50,-100] (dBm) to [100,0] (%). + */ + if (level <= -100) { return 0; } + else if (level >= -50) { return 100; } + + return 2 * (level + 100); + } + + inline unsigned check_time(unsigned value, unsigned min, unsigned max) + { + if (value < min) { return min; } + else if (value > max) { return max; } + return value; + } + +} /* namespace Util */ + +#endif /* _WIFI__UTIL_H_ */ diff --git a/repos/dde_linux/src/drivers/wifi/wpa.h b/repos/dde_linux/src/drivers/wifi/wpa.h index 5670425e7f..2cc5f423cc 100644 --- a/repos/dde_linux/src/drivers/wifi/wpa.h +++ b/repos/dde_linux/src/drivers/wifi/wpa.h @@ -18,9 +18,7 @@ #include /* entry function */ -extern "C" int wpa_main(int debug_msg, int connected_scan_interval); -extern "C" void wpa_reporter_init(void *env); -extern "C" void wpa_conf_reload(void); +extern "C" int wpa_main(void); class Wpa_thread : public Genode::Thread { @@ -28,26 +26,20 @@ class Wpa_thread : public Genode::Thread Genode::Lock &_lock; int _exit; - bool _debug_msg; - int _connected_scan_interval; public: - Wpa_thread(Genode::Env &env, Genode::Lock &lock, - bool debug_msg, int connected_scan_interval) + Wpa_thread(Genode::Env &env, Genode::Lock &lock) : Thread(env, "wpa_supplicant", 8*1024*sizeof(long)), - _lock(lock), _exit(-1), - _debug_msg(debug_msg), _connected_scan_interval(connected_scan_interval) - { - wpa_reporter_init(&env); - } + _lock(lock), _exit(-1) + { } void entry() { /* wait until the wifi driver is up and running */ _lock.lock(); - _exit = wpa_main(_debug_msg, _connected_scan_interval); + _exit = wpa_main(); Genode::sleep_forever(); } }; diff --git a/repos/dde_linux/src/lib/libnl/socket.cc b/repos/dde_linux/src/lib/libnl/socket.cc index 61afe0c6af..4071244195 100644 --- a/repos/dde_linux/src/lib/libnl/socket.cc +++ b/repos/dde_linux/src/lib/libnl/socket.cc @@ -72,9 +72,9 @@ class Socket_registry { private : - /* abritary value (it goes to eleven!) */ enum { - SOCKETS_INITIAL_VALUE = 11, + /* lower FDs might be special */ + SOCKETS_OFFSET_VALUE = 100, MAX_SOCKETS = 7, }; @@ -99,7 +99,7 @@ class Socket_registry if (sfd.s != nullptr) { return false; } sfd.s = s; - sfd.fd = ++_sockets; + sfd.fd = (++_sockets & 0xff) + SOCKETS_OFFSET_VALUE; /* return fd */ fd = sfd.fd; @@ -144,7 +144,7 @@ class Socket_registry }; Socket_fd Socket_registry::_socket_fd[MAX_SOCKETS] = {}; -unsigned Socket_registry::_sockets = Socket_registry::SOCKETS_INITIAL_VALUE; +unsigned Socket_registry::_sockets = 0; extern "C" { @@ -480,10 +480,36 @@ int fcntl(int fd, int cmd, ... /* arg */ ) ** sys/poll.h ** ****************/ +static bool _ctrl_fd_set = false; + + +extern "C" void nl_set_wpa_ctrl_fd() +{ + _ctrl_fd_set = true; +} + +static bool special_fd(int fd) +{ + /* + * This range is used by the CTRL and RFKILL fds. + */ + return (fd > 40 && fd < 60); +} + int poll(struct pollfd *fds, nfds_t nfds, int timeout) { Poll_socket_fd sockets[Wifi::MAX_POLL_SOCKETS]; unsigned num = 0; + int nready = 0; + + /* handle special FDs first */ + for (nfds_t i = 0; i < nfds; i++) { + if (!special_fd(fds[i].fd)) { continue; } + + fds[i].revents = 0; + fds[i].revents |= POLLIN; + nready++; + } for (nfds_t i = 0; i < nfds; i++) { Socket *s = Socket_registry::find(fds[i].fd); @@ -504,11 +530,16 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout) num++; } - int nready = socket_call.poll_all(sockets, num, timeout); - if (!nready) - return 0; - if (nready < 0) - return -1; + /* make sure we do not block in poll_all */ + if (_ctrl_fd_set) { + _ctrl_fd_set = false; + timeout = 0; + } + + int sready = socket_call.poll_all(sockets, num, timeout); + if (sready < 0 || sready == 0) { return nready; } + + nready += sready; for (unsigned i = 0; i < num; i++) { int revents = sockets[i].revents; diff --git a/repos/dde_linux/src/lib/wifi/dummies.cc b/repos/dde_linux/src/lib/wifi/dummies.cc index 70b44e4c93..f39a59e427 100644 --- a/repos/dde_linux/src/lib/wifi/dummies.cc +++ b/repos/dde_linux/src/lib/wifi/dummies.cc @@ -132,18 +132,18 @@ DUMMY_SKIP(0, in_irq) /* XXX */ DUMMY_SKIP(0, local_bh_disable) DUMMY_SKIP(0, local_bh_enable) -DUMMY_SKIP(0, dma_unmap_page) DUMMY_SKIP(0, dma_set_coherent_mask) /* we set the mask always to ~0UL */ DUMMY_SKIP(0, dma_set_mask) /* in the PCI driver */ -DUMMY(-1, dma_sync_single_for_cpu) -DUMMY(-1, dma_sync_single_for_device) - - -/* XXX DUMMY_SKIP safe? */ +DUMMY_SKIP(0, dma_sync_single_for_cpu) +DUMMY_SKIP(0, dma_sync_single_for_device) +/* + * There is no actual mapping going on as the memory is always + * allocated from the DMA backend. It is safe to _not_ implement + * the unmap functions. + */ +DUMMY_SKIP(0, dma_unmap_page) DUMMY_SKIP(0, dma_unmap_single) -DUMMY_SKIP(0, kunmap) -DUMMY_SKIP(0, kunmap_atomic) DUMMY_SKIP(-1, dump_stack) DUMMY_SKIP(-1, gfp_pfmemalloc_allowed) @@ -189,9 +189,6 @@ DUMMY(0, device_rename) DUMMY(0, device_unregister) DUMMY(0, do_posix_clock_monotonic_gettime) DUMMY(0, do_softirq) -DUMMY(0, flush_delayed_work) -DUMMY(0, flush_work) -DUMMY(0, flush_workqueue) DUMMY(0, genl_dump_check_consistent) DUMMY(0, genl_info_net) DUMMY(0, genlmsg_cancel) @@ -370,9 +367,8 @@ DUMMY(0, __hw_addr_sync) DUMMY(0, __hw_addr_unsync) DUMMY_SKIP(0, dev_alloc_name) DUMMY(0, dev_change_net_namespace) -DUMMY(0, dev_close) DUMMY(0, dev_kfree_skb_any) -DUMMY_SKIP(0, dev_net_set) +DUMMY(0, dev_net_set) DUMMY(0, dev_open) DUMMY_SKIP(0, dev_hold) DUMMY_SKIP(0, dev_put) @@ -406,7 +402,6 @@ DUMMY(0, request_firmware) DUMMY(0, tcp_v4_check) DUMMY(0, sk_attach_filter) -DUMMY(0, __class_create) DUMMY(0, __module_get) DUMMY(0, __sock_recv_timestamp) DUMMY(0, __sock_recv_wifi_status) @@ -449,12 +444,6 @@ DUMMY(0, regulator_enable) DUMMY(0, regulator_get_exclusive) DUMMY(0, regulator_is_enabled) DUMMY(0, regulator_put) -DUMMY(0, rfkill_epo) -DUMMY(0, rfkill_get_global_sw_state) -DUMMY(0, rfkill_is_epo_lock_active) -DUMMY(0, rfkill_remove_epo_lock) -DUMMY(0, rfkill_restore_states) -DUMMY(0, rfkill_switch_all) DUMMY(0, send_sigurg) DUMMY(0, simple_strtoul) DUMMY(0, skb_gro_len) @@ -533,7 +522,6 @@ DUMMY(0, release_pages) DUMMY(0, sk_busy_loop) DUMMY(0, sk_can_busy_loop) -// DUMMY_SKIP(0, complete_all) DUMMY_SKIP(0, simple_strtol) DUMMY_SKIP(0, alg_test) @@ -550,7 +538,6 @@ DUMMY(0, config_enabled) DUMMY(0, dev_change_proto_down) DUMMY(0, dev_get_iflink) DUMMY(0, dev_get_phys_port_name) -DUMMY(-1, device_create_with_groups) DUMMY(0, device_enable_async_suspend) DUMMY(0, fatal_signal_pending) DUMMY_RET(1, file_ns_capable) @@ -586,7 +573,7 @@ DUMMY(0, netif_xmit_frozen_or_drv_stopped) DUMMY(0, netif_xmit_frozen_or_stopped) DUMMY_STOP(0, netif_rx_ni) DUMMY_STOP(0, netif_tx_start_all_queues) -DUMMY_STOP(0, netif_tx_stop_all_queues) +DUMMY(0, netif_tx_stop_all_queues) DUMMY(0, peernet_has_id) DUMMY(0, peernet2id_alloc) diff --git a/repos/dde_linux/src/lib/wifi/dummies_new.c b/repos/dde_linux/src/lib/wifi/dummies_new.c index ca47648a9a..01839c5346 100644 --- a/repos/dde_linux/src/lib/wifi/dummies_new.c +++ b/repos/dde_linux/src/lib/wifi/dummies_new.c @@ -14,7 +14,7 @@ /* local includes */ #include -#if 1 +#if 0 #define TRACE \ do { \ lx_printf("%s not implemented from: %p\n", __func__, \ @@ -141,12 +141,6 @@ unsigned int memalloc_noreclaim_save(void) } -u64 ktime_get_ns(void) -{ - TRACE; - return 0; -} - struct fq_flow *fq_flow_classify(struct fq *fq, struct fq_tin *tin, struct sk_buff *skb, fq_flow_get_default_t get_default_func) @@ -510,6 +504,13 @@ void wireless_nlevent_flush(void) bool wq_has_sleeper(struct wait_queue_head *wq_head) { - TRACE; + TRACE_OK; return true; } + + +bool sysfs_streq(const char *s1, const char *s2) +{ + TRACE; + return false; +} diff --git a/repos/dde_linux/src/lib/wifi/include/lx_emul.h b/repos/dde_linux/src/lib/wifi/include/lx_emul.h index 4b39725364..bb7b3e86af 100644 --- a/repos/dde_linux/src/lib/wifi/include/lx_emul.h +++ b/repos/dde_linux/src/lib/wifi/include/lx_emul.h @@ -927,7 +927,7 @@ static inline int no_printk(const char *fmt, ...) { return 0; } #define pr_warn(fmt, ...) printk(KERN_WARN fmt, ##__VA_ARGS__) #define pr_warn_once pr_warn #define pr_notice(fmt, ...) printk(KERN_NOTICE fmt, ##__VA_ARGS__) -#define pr_info(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__) +#define pr_info(fmt, ...) no_printk(KERN_INFO fmt, ##__VA_ARGS__) #define pr_cont(fmt, ...) printk(KERN_CONT fmt, ##__VA_ARGS__) /* pr_devel() should produce zero code unless DEBUG is defined */ #ifdef DEBUG @@ -2923,7 +2923,7 @@ unsigned int dev_get_flags(const struct net_device *); struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage); int dev_change_net_namespace(struct net_device *, struct net *, const char *); int dev_alloc_name(struct net_device *dev, const char *name); -int dev_close(struct net_device *dev); +void dev_close(struct net_device *dev); int dev_set_mac_address(struct net_device *, struct sockaddr *); int dev_set_mtu(struct net_device *, int); int dev_set_promiscuity(struct net_device *dev, int inc); @@ -3245,6 +3245,7 @@ struct file_operations { int (*release) (struct inode *, struct file *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); int (*fasync) (int, struct file *, int); + long (*compat_ioctl) (struct file *, unsigned int, unsigned long); }; static inline loff_t no_llseek(struct file *file, loff_t offset, int origin) { @@ -3287,13 +3288,6 @@ struct platform_device { }; -/* needed by net/rfkill/rfkill-gpio.c */ -struct platform_driver { - int (*probe)(struct platform_device *); - int (*remove)(struct platform_device *); - struct device_driver driver; -}; - void *platform_get_drvdata(const struct platform_device *pdev); void platform_set_drvdata(struct platform_device *pdev, void *data); struct platform_device *platform_device_register_simple( const char *name, int id, const struct resource *res, unsigned int num); @@ -5634,4 +5628,12 @@ int device_property_read_string(struct device *dev, const char *propname, const #include + +/****************************** + ** uapi/asm-generic/ioctl.h ** + ******************************/ + +#define _IOC_NR(nr) (nr) +#define _IOC_TYPE(nr) (nr) + #endif /* _LX_EMUL_H_ */ diff --git a/repos/dde_linux/src/lib/wifi/init.cc b/repos/dde_linux/src/lib/wifi/init.cc index e8f40cc10c..76f4a46e9f 100644 --- a/repos/dde_linux/src/lib/wifi/init.cc +++ b/repos/dde_linux/src/lib/wifi/init.cc @@ -29,6 +29,62 @@ #include +/********************* + ** RFKILL handling ** + *********************/ + +#include + +extern "C" void rfkill_switch_all(enum rfkill_type type, bool blocked); +extern "C" bool rfkill_get_any(enum rfkill_type type); + +#include + +bool wifi_get_rfkill(void) +{ + return rfkill_get_any(RFKILL_TYPE_WLAN); +} + + +static Lx::Task *_lx_task = nullptr; +static bool _lx_init_done = false; +static bool _switch_rfkill = false; +static bool _new_blocked = false; +static Genode::Signal_context_capability _rfkill_sig_ctx; + + +void wifi_set_rfkill(bool blocked) +{ + bool const cur = wifi_get_rfkill(); + + _switch_rfkill = blocked != cur; + if (_lx_init_done && _switch_rfkill) { + _new_blocked = blocked; + + _lx_task->unblock(); + Lx::scheduler().schedule(); + } +} + + +/************************** + ** socketcall poll hack ** + **************************/ + +void wifi_kick_socketcall() +{ + /* + * Kicking is going to unblock the socketcall task that + * probably is waiting in poll_all(). + */ + Lx::socket_kick(); +} + + +/***************************** + ** Initialization handling ** + *****************************/ + extern "C" void core_netlink_proto_init(void); extern "C" void core_sock_init(void); extern "C" void module_packet_init(void); @@ -44,6 +100,7 @@ extern "C" void module_aes_init(void); extern "C" void module_arc4_init(void); // extern "C" void module_chainiv_module_init(void); extern "C" void module_krng_mod_init(void); +extern "C" void subsys_leds_init(void); extern "C" unsigned int *module_param_11n_disable; @@ -69,6 +126,7 @@ static void run_linux(void *args) module_packet_init(); subsys_genl_init(); subsys_rfkill_init(); + subsys_leds_init(); fs_cfg80211_init(); subsys_ieee80211_init(); @@ -77,7 +135,6 @@ static void run_linux(void *args) module_crypto_ctr_module_init(); module_aes_init(); module_arc4_init(); - // module_chainiv_module_init(); try { int const err = module_iwl_drv_init(); @@ -91,15 +148,40 @@ static void run_linux(void *args) _wpa_lock->unlock(); + _lx_init_done = true; + while (1) { Lx::scheduler().current()->block_and_schedule(); + + if (!_switch_rfkill) { continue; } + + Genode::log("RFKILL: ", _new_blocked ? "BLOCKED" : "UNBLOCKED"); + rfkill_switch_all(RFKILL_TYPE_WLAN, _new_blocked); + + if (!_new_blocked) { + try { + bool const ok = Lx::open_device(); + if (!ok) { throw -1; } + + } catch (...) { + Genode::Env &env = *(Genode::Env*)args; + + env.parent().exit(1); + Genode::sleep_forever(); + } + } + + /* notify front end */ + Genode::Signal_transmitter(_rfkill_sig_ctx).submit(); } } + unsigned long jiffies; -void wifi_init(Genode::Env &env, Genode::Lock &lock, bool disable_11n) +void wifi_init(Genode::Env &env, Genode::Lock &lock, bool disable_11n, + Genode::Signal_context_capability rfkill) { Lx_kit::construct_env(env); @@ -128,9 +210,13 @@ void wifi_init(Genode::Env &env, Genode::Lock &lock, bool disable_11n) *module_param_11n_disable = 1; } + _rfkill_sig_ctx = rfkill; + /* Linux task (handles the initialization only currently) */ static Lx::Task linux(run_linux, &env, "linux", Lx::Task::PRIORITY_0, Lx::scheduler()); + + _lx_task = &linux; /* give all task a first kick before returning */ Lx::scheduler().schedule(); diff --git a/repos/dde_linux/src/lib/wifi/lx.h b/repos/dde_linux/src/lib/wifi/lx.h index 7ddd3af269..88ad334a7f 100644 --- a/repos/dde_linux/src/lib/wifi/lx.h +++ b/repos/dde_linux/src/lib/wifi/lx.h @@ -28,6 +28,7 @@ namespace Lx void emul_init(Genode::Env&, Genode::Allocator&); void socket_init(Genode::Entrypoint&, Genode::Allocator&); + void socket_kick(); void nic_init(Genode::Env&, Genode::Allocator&); @@ -35,6 +36,8 @@ namespace Lx void backend_free(Genode::Ram_dataspace_capability); void get_mac_address(unsigned char *); + + bool open_device(); } #endif /* _LX_H_ */ diff --git a/repos/dde_linux/src/lib/wifi/lxc_emul.c b/repos/dde_linux/src/lib/wifi/lxc_emul.c index 487be4766a..3fbc320762 100644 --- a/repos/dde_linux/src/lib/wifi/lxc_emul.c +++ b/repos/dde_linux/src/lib/wifi/lxc_emul.c @@ -443,7 +443,7 @@ core_initcall(sock_init); codel_time_t codel_get_time(void) { - u64 ns = ktime_get_ns(); + u64 ns = ktime_get(); return ns >> CODEL_SHIFT; } @@ -479,3 +479,41 @@ u64 ktime_get_boot_ns(void) { return (u64)ktime_get(); } + + +/******************** + ** linux/device.h ** + ********************/ + +struct device *device_create_with_groups(struct class *class, + struct device *parent, dev_t devt, + void *drvdata, + const struct attribute_group **groups, + const char *fmt, ...) +{ + long ret = -ENODEV; + if (class == NULL || IS_ERR(class)) { goto err; } + + struct device *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err; + } + + return dev; + +err: + return (void*)ret; +} + + +struct class *__class_create(struct module *owner, + const char *name, + struct lock_class_key *key) +{ + struct class *cls = kzalloc(sizeof(*cls), GFP_KERNEL); + if (!cls) { return (void*)-ENOMEM; } + + cls->name = name; + return cls; +} diff --git a/repos/dde_linux/src/lib/wifi/lxcc_emul.cc b/repos/dde_linux/src/lib/wifi/lxcc_emul.cc index fb5a1c21d1..38d4f603c2 100644 --- a/repos/dde_linux/src/lib/wifi/lxcc_emul.cc +++ b/repos/dde_linux/src/lib/wifi/lxcc_emul.cc @@ -303,8 +303,9 @@ size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i) { + /* XXX at some point check if i->count > bytes could be a problem */ if (bytes > i->count) - return false; + bytes = i->count; if (bytes == 0) return true; @@ -730,28 +731,6 @@ time64_t ktime_get_seconds(void) } -/*********************** - ** linux/workqueue.h ** - ***********************/ - -struct workqueue_struct *create_singlethread_workqueue(char const *) -{ - workqueue_struct *wq = (workqueue_struct *)kzalloc(sizeof(workqueue_struct), 0); - return wq; -} - -struct workqueue_struct *alloc_ordered_workqueue(char const *name , unsigned int flags, ...) -{ - return create_singlethread_workqueue(name); -} - -struct workqueue_struct *alloc_workqueue(const char *fmt, unsigned int flags, - int max_active, ...) -{ - return create_singlethread_workqueue(nullptr); -} - - /************************* ** linux/dma-mapping.h ** *************************/ @@ -1375,11 +1354,11 @@ void pci_dev_put(struct pci_dev *pci_dev) Genode::destroy(Lx_kit::env().heap(), pci_dev); } + /*********************** - ** linux/workquque.h ** + ** linux/workqueue.h ** ***********************/ -/* Linux emul includes */ #include @@ -1391,6 +1370,88 @@ bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, } +struct workqueue_struct *alloc_ordered_workqueue(char const *fmt , unsigned int flags, ...) +{ + return alloc_workqueue(fmt, flags, 1); +} + + +struct workqueue_struct *alloc_workqueue(const char *fmt, unsigned int flags, + int max_active, ...) +{ + workqueue_struct *wq = (workqueue_struct *)kzalloc(sizeof(workqueue_struct), 0); + Lx::Work *work = Lx::Work::alloc_work_queue(&Lx::Malloc::mem(), fmt); + wq->task = (void *)work; + + return wq; +} + + +void flush_workqueue(struct workqueue_struct *wq) +{ + Lx::Task *current = Lx::scheduler().current(); + if (!current) { + Genode::error("BUG: flush_workqueue executed without task"); + Genode::sleep_forever(); + } + + Lx::Work *lx_work = (wq && wq->task) ? (Lx::Work*) wq->task + : &Lx::Work::work_queue(); + + lx_work->flush(*current); + Lx::scheduler().current()->block_and_schedule(); +} + + +static inline bool work_queued(struct workqueue_struct *wq, void *work) +{ + Lx::Work *lx_work = (wq && wq->task) ? (Lx::Work*) wq->task + : &Lx::Work::work_queue(); + return lx_work->work_queued(work); +} + + +bool flush_work(struct work_struct *work) +{ + /* XXX AFAIU if the work was not queued it is already 'idle' and + * we just return false + */ + bool const queued = work_queued(work->wq, work); + if (queued) { + Genode::error(__func__, " work: ", work, " (", work->func, ") queued"); + + struct workqueue_struct *wq = work->wq; + + Lx::Work *lx_work = (wq && wq->task) ? (Lx::Work*) wq->task + : &Lx::Work::work_queue(); + + Lx::Task *current = Lx::scheduler().current(); + lx_work->wakeup_for(work, *current); + + Lx::scheduler().current()->block_and_schedule(); + return true; + } + + return false; +} + + +bool flush_delayed_work(struct delayed_work *dwork) +{ + /* XXX AFAIU if the work was not queued it is already 'idle' and + * we just return false + */ + bool const queued = work_queued(dwork->wq, dwork); + if (queued) { + Genode::error(__func__, " dwork: ", dwork, " (", dwork->work.func, ") queued"); + Genode::sleep_forever(); + return true; + } + + return false; +} + + /*********************** ** linux/interrupt.h ** ***********************/ @@ -1512,7 +1573,7 @@ struct Idr if (index == INVALID_ENTRY) { return INVALID_ENTRY; } _barray.set(index, 1); - _ptr[index] = ptr; + _ptr[index] = (addr_t) ptr; return index; } diff --git a/repos/dde_linux/src/lib/wifi/nic.cc b/repos/dde_linux/src/lib/wifi/nic.cc index 983ab91858..a5969cb91f 100644 --- a/repos/dde_linux/src/lib/wifi/nic.cc +++ b/repos/dde_linux/src/lib/wifi/nic.cc @@ -598,6 +598,57 @@ extern "C" size_t LL_RESERVED_SPACE(struct net_device *dev) } +extern "C" void dev_close(struct net_device *ndev) +{ + /* + * First instruct cfg80211 to leave the associated network + * and then shutdown the interface. + */ + net_notifier().call_all_blocks(NETDEV_GOING_DOWN, ndev); + net_notifier().call_all_blocks(NETDEV_DOWN, ndev); + + ndev->state &= ~(1UL << __LINK_STATE_START); + netif_carrier_off(ndev); + + const struct net_device_ops *ops = ndev->netdev_ops; + if (ops->ndo_stop) { ops->ndo_stop(ndev); } + + ndev->flags &= ~IFF_UP; +} + + +bool Lx::open_device() +{ + if (!Root::instance->device) { + Genode::error("no net_device available"); + return false; + } + + struct net_device * const ndev = Root::instance->device; + + int err = ndev->netdev_ops->ndo_open(ndev); + if (err) { + Genode::error("Open device failed"); + throw -1; + return err; + } + + /* + * Important, otherwise netif_running checks fail and AF_PACKET + * will not bind and EAPOL will cease to work. + */ + ndev->flags |= IFF_UP; + ndev->state |= (1UL << __LINK_STATE_START); + + if (ndev->netdev_ops->ndo_set_rx_mode) + ndev->netdev_ops->ndo_set_rx_mode(ndev); + + net_notifier().call_all_blocks(NETDEV_UP, ndev); + + return true; +} + + extern "C" int register_netdevice(struct net_device *ndev) { static bool already_registered = false; diff --git a/repos/dde_linux/src/lib/wifi/socket_call.cc b/repos/dde_linux/src/lib/wifi/socket_call.cc index 561842be64..61041b2375 100644 --- a/repos/dde_linux/src/lib/wifi/socket_call.cc +++ b/repos/dde_linux/src/lib/wifi/socket_call.cc @@ -358,8 +358,9 @@ class Lx::Socket case Call::NON_BLOCK: _do_non_block(); break; default: - _call.err = -EINVAL; Genode::warning("unknown opcode: ", (int)_call.opcode); + case Call::NONE: /* ignore silently */ + _call.err = -EINVAL; break; } @@ -372,6 +373,11 @@ class Lx::Socket _sender.submit(); _block.down(); } + + void unblock_task() + { + _task.unblock(); + } }; @@ -387,6 +393,15 @@ void Lx::socket_init(Genode::Entrypoint &ep, Genode::Allocator &alloc) } +void Lx::socket_kick() +{ + /* ignore silently, the function might be called to before init */ + if (!_socket) { return; } + + _socket->unblock_task(); +} + + static void run_socketcall(void *) { while (1) { diff --git a/repos/dde_linux/src/lib/wifi/symbol.map b/repos/dde_linux/src/lib/wifi/symbol.map index f2a585251c..261c62a9f3 100644 --- a/repos/dde_linux/src/lib/wifi/symbol.map +++ b/repos/dde_linux/src/lib/wifi/symbol.map @@ -10,10 +10,15 @@ *Socket_call*; /* Wifi::Socket_call instance */ socket_call; + /* rfkill interface */ + _*wifi_*_rfkill*; + _*wifi_kick_*; /* used by libnl's time() */ jiffies; + lx_printf*; + local: *; diff --git a/repos/dde_linux/src/lib/wpa_driver_nl80211/rfkill_genode.cc b/repos/dde_linux/src/lib/wpa_driver_nl80211/rfkill_genode.cc new file mode 100644 index 0000000000..3a4c6b3bb6 --- /dev/null +++ b/repos/dde_linux/src/lib/wpa_driver_nl80211/rfkill_genode.cc @@ -0,0 +1,89 @@ +/* + * \brief RFKILL backend + * \author Josef Soentgen + * \date 2018-07-11 + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +/* + * based on: + * + * Linux rfkill helper functions for driver wrappers + * Copyright (c) 2010, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +/* rep includes */ +#include + +extern "C" { +#include "includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" + +#include +} /* extern "C" */ + + +struct rfkill_data { + struct rfkill_config *cfg; + int fd; + bool blocked; +}; + + +static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct rfkill_data * const rfkill = (rfkill_data*)eloop_ctx; + bool const new_blocked = wifi_get_rfkill(); + + if (new_blocked != rfkill->blocked) { + rfkill->blocked = new_blocked; + + if (new_blocked) { + rfkill->cfg->blocked_cb(rfkill->cfg->ctx); + } else { + rfkill->cfg->unblocked_cb(rfkill->cfg->ctx); + } + } +} + + +struct rfkill_data * rfkill_init(struct rfkill_config *cfg) +{ + struct rfkill_data *rfkill = (rfkill_data*) os_zalloc(sizeof(*rfkill)); + if (!rfkill) { return NULL; } + + rfkill->cfg = cfg; + rfkill->fd = Wifi::RFKILL_FD; + + eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL); + + return rfkill; +} + + +void rfkill_deinit(struct rfkill_data *rfkill) +{ + if (!rfkill) { return; } + + eloop_unregister_read_sock(rfkill->fd); + + os_free(rfkill->cfg); + os_free(rfkill); +} + + +int rfkill_is_blocked(struct rfkill_data *rfkill) +{ + return rfkill ? rfkill->blocked : 0; +} diff --git a/repos/dde_linux/src/lib/wpa_driver_nl80211/symbol.map b/repos/dde_linux/src/lib/wpa_driver_nl80211/symbol.map index 7036f9efca..1b750f9d3b 100644 --- a/repos/dde_linux/src/lib/wpa_driver_nl80211/symbol.map +++ b/repos/dde_linux/src/lib/wpa_driver_nl80211/symbol.map @@ -12,6 +12,9 @@ poll; + /* needed by wpa_supplicant lib for wifi_drv */ + nl_set_wpa_ctrl_fd; + local: *; diff --git a/repos/dde_linux/src/lib/wpa_supplicant/ctrl_iface_genode.c b/repos/dde_linux/src/lib/wpa_supplicant/ctrl_iface_genode.c index 9bbad80ce7..5d9e2b725e 100644 --- a/repos/dde_linux/src/lib/wpa_supplicant/ctrl_iface_genode.c +++ b/repos/dde_linux/src/lib/wpa_supplicant/ctrl_iface_genode.c @@ -1,4 +1,19 @@ /* + * \brief WPA Supplicant frontend + * \author Josef Soentgen + * \date 2018-07-18 + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +/* + * based on: + * * WPA Supplicant / UNIX domain socket -based control interface * Copyright (c) 2004-2014, Jouni Malinen * @@ -6,8 +21,8 @@ * See README for more details. */ +/* wpa_supplicant includes */ #include "includes.h" - #include "utils/common.h" #include "utils/eloop.h" #include "utils/list.h" @@ -17,8 +32,29 @@ #include "wpa_supplicant_i.h" #include "ctrl_iface.h" +/* rep includes */ +#include + + struct ctrl_iface_priv { struct wpa_supplicant *wpa_s; + int fd; + int level; + + /* TODO replace w/ Msg_buffer */ + char *send_buffer; + size_t send_buffer_size; + unsigned *send_id; + + char *recv_buffer; + size_t recv_buffer_size; + unsigned *recv_id; + + unsigned last_recv_id; + + char *event_buffer; + size_t event_buffer_size; + unsigned *event_id; }; @@ -26,71 +62,201 @@ struct ctrl_iface_global_priv { struct wpa_global *global; }; -struct ctrl_iface_msg { - struct wpa_supplicant *wpa_s; -}; + +extern void nl_set_wpa_ctrl_fd(void); + + +void wpa_ctrl_set_fd() +{ + nl_set_wpa_ctrl_fd(); +} + + +static int send_reply(struct ctrl_iface_priv *priv, char const *txt, size_t len) +{ + char *msg = priv->send_buffer; + size_t mlen = priv->send_buffer_size; + + if (!msg || !len || (len > mlen)) { return -1; } + + memset(msg, 0, mlen); + memcpy(msg, txt, len); + + (*priv->send_id)++; + + return 0; +} + + +/* + * This function is called by wpa_supplicant whenever it receives a + * command via the CTRL interface, i.e. the front end has sent a new + * message. + */ +static void wpa_supplicant_ctrl_iface_receive(int fd, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct ctrl_iface_priv *priv = sock_ctx; + + char *msg = priv->recv_buffer; + + unsigned const recv_id = *priv->recv_id; + + char *reply = NULL; + size_t reply_len = 0; + + if (msg[0] == 0 || recv_id == priv->last_recv_id) { return; } + + priv->last_recv_id = recv_id; + + reply = wpa_supplicant_ctrl_iface_process(wpa_s, msg, + &reply_len); + // lx_printf("%s:%d %p %zu\n", __func__, __LINE__, reply, reply_len); + + if (reply) { + wifi_notify_cmd_result(); + send_reply(priv, reply, reply_len); + os_free(reply); + } else + + if (reply_len == 1) { + send_reply(priv, "FAIL", 4); + } else + + if (reply_len == 2) { + send_reply(priv, "OK", 2); + } +} + + +static void print_txt(char const *txt, size_t len) +{ + char buffer[256]; + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, txt, len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1); + lx_printf(" %s\n", buffer); +} + + +static int send_event(struct ctrl_iface_priv *priv, char const *txt, size_t len) +{ + char *msg = priv->event_buffer; + size_t mlen = priv->event_buffer_size; + + if (!msg || !len || (len > mlen)) { return -1; } + + memset(msg, 0, mlen); + memcpy(msg, txt, len); + + (*priv->event_id)++; + + return 0; +} + + +/* + * This function is called by wpa_supplicant whenever it wants to + * forward some message. We filter these messages and forward only + * those, which are of interest to the front end. + */ +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + enum wpa_msg_type type, + const char *txt, size_t len) +{ +#if 0 + int const dont_print = + strncmp(txt, "BSS:", 4) == 0 + || strncmp(txt, "BSS:", 4) == 0 + || strncmp(txt, "CTRL-EVENT-BSS", 14) == 0 + || strncmp(txt, " skip", 7) == 0 + ; + if (!dont_print) { print_txt(txt, len); } +#endif + + /* there is not global support */ + if (type == WPA_MSG_ONLY_GLOBAL) { return; } + + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s == NULL) { return; } + + struct ctrl_iface_priv *priv = wpa_s->ctrl_iface; + if (!priv || level < priv->level) { return; } + + /* + * Filter messages and only forward events the front end cares + * about or rather knows how to handle. + */ + int const forward = + strncmp(txt, "CTRL-EVENT-SCAN-RESULTS", 23) == 0 + || strncmp(txt, "CTRL-EVENT-CONNECTED", 20) == 0 + || strncmp(txt, "CTRL-EVENT-DISCONNECTED", 23) == 0 + /* needed to detect connecting state */ + || strncmp(txt, "SME: Trying to authenticate", 27) == 0 + ; + if (!forward) { return; } + + wifi_notify_event(); + send_event(priv, txt, len); +} struct ctrl_iface_priv * wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) { - printf("%s:%d\n", __func__, __LINE__); - struct ctrl_iface_priv *priv; priv = os_zalloc(sizeof(*priv)); - if (priv == NULL) - return NULL; + if (priv == NULL) { return NULL; } - if (wpa_s->conf->ctrl_interface == NULL) + if (wpa_s->conf->ctrl_interface == NULL) { return priv; + } + struct Msg_buffer *msg_buffer = (struct Msg_buffer*)wifi_get_buffer(); + priv->recv_buffer = (char *)msg_buffer->send; + priv->recv_buffer_size = sizeof(msg_buffer->send); + priv->send_buffer = (char *)msg_buffer->recv; + priv->send_buffer_size = sizeof(msg_buffer->recv); + priv->send_id = &msg_buffer->recv_id; + priv->recv_id = &msg_buffer->send_id; + + priv->event_buffer = (char *)msg_buffer->event; + priv->event_buffer_size = sizeof(msg_buffer->event); + priv->event_id = &msg_buffer->event_id; + + priv->level = MSG_INFO; + priv->fd = WPA_CTRL_FD; + + eloop_register_read_sock(priv->fd, + wpa_supplicant_ctrl_iface_receive, + wpa_s, priv); + + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); return priv; } void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) { - printf("%s:%d\n", __func__, __LINE__); - - struct wpa_ctrl_dst *dst, *prev; - struct ctrl_iface_msg *msg, *prev_msg; - struct ctrl_iface_global_priv *gpriv; - os_free(priv); } -void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) -{ - printf("%s:%d\n", __func__, __LINE__); -} +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) { } struct ctrl_iface_global_priv * wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) { - printf("%s:%d\n", __func__, __LINE__); - struct ctrl_iface_global_priv *priv; priv = os_zalloc(sizeof(*priv)); - if (priv == NULL) - return NULL; - - if (global->params.ctrl_interface == NULL) - return priv; - return priv; } -void wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) +void wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *p) { - printf("%s:%d\n", __func__, __LINE__); - - struct wpa_ctrl_dst *dst, *prev; - struct ctrl_iface_msg *msg, *prev_msg; - - os_free(priv); + os_free(p); } diff --git a/repos/dde_linux/src/lib/wpa_supplicant/main.c b/repos/dde_linux/src/lib/wpa_supplicant/main.c index 55a1ff5f88..0a18ba610f 100644 --- a/repos/dde_linux/src/lib/wpa_supplicant/main.c +++ b/repos/dde_linux/src/lib/wpa_supplicant/main.c @@ -36,25 +36,8 @@ #include "scan.h" -static char const *conf_file = "/config/wpa_supplicant.conf"; - -static int connected_scan_interval; - -static void connected_scan_handler(void *eloop_ctx, void *user_ctx) +int wpa_main(void) { - struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)eloop_ctx; - - if (wpa_s->wpa_state >= WPA_ASSOCIATED) - wpa_supplicant_req_scan(wpa_s, 0, 0); - - eloop_register_timeout(connected_scan_interval, 0, connected_scan_handler, wpa_s, 0); -} - - -int wpa_main(int debug_msg, int interval) -{ - connected_scan_interval = interval; - struct wpa_interface iface; int exitcode = 0; struct wpa_params params; @@ -62,7 +45,8 @@ int wpa_main(int debug_msg, int interval) memset(¶ms, 0, sizeof(params)); - params.wpa_debug_level = debug_msg ? MSG_DEBUG : MSG_INFO; + // TODO use CTRL interface for setting debug level + params.wpa_debug_level = 1 ? MSG_DEBUG : MSG_INFO; params.ctrl_interface = "GENODE"; global = wpa_supplicant_init(¶ms); @@ -72,15 +56,12 @@ int wpa_main(int debug_msg, int interval) memset(&iface, 0, sizeof(iface)); iface.ifname = "wlan0"; - iface.confname = conf_file; + iface.confname = 0; + iface.ctrl_interface = "GENODE"; if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL) exitcode = -1; - if (connected_scan_interval > 0) - eloop_register_timeout(connected_scan_interval, 0, - connected_scan_handler, global->ifaces, 0); - if (exitcode == 0) exitcode = wpa_supplicant_run(global); @@ -88,24 +69,3 @@ int wpa_main(int debug_msg, int interval) 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_CREAT|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 deleted file mode 100644 index ed41ee332c..0000000000 --- a/repos/dde_linux/src/lib/wpa_supplicant/reporter.cc +++ /dev/null @@ -1,153 +0,0 @@ -/* - * \brief WPA Supplicant frontend - * \author Josef Soentgen - * \date 2014-12-08 - */ - -/* - * Copyright (C) 2014-2017 Genode Labs GmbH - * - * This file 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::Constructible accesspoints_reporter; -static Genode::Constructible state_reporter; - - -extern "C" void wpa_reporter_init(void *env) -{ - accesspoints_reporter.construct(*static_cast(env), "wlan_accesspoints"); - accesspoints_reporter->enabled(true); - - state_reporter.construct(*static_cast(env), "wlan_state"); - state_reporter->enabled(true); -} - - -enum { SSID_STRING_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_supplicant *wpa_s) -{ - 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(Genode::Cstring((char *)wpa_ssid->ssid, wpa_ssid->ssid_len)); - - char bssid_buf[MAC_STR_LEN]; - mac2str(bssid_buf, wpa_s->bssid); - - xml.node("accesspoint", [&]() { - xml.attribute("ssid", ssid.string()); - xml.attribute("bssid", bssid_buf); - xml.attribute("state", "connected"); - }); - }); - } catch (...) { Genode::warning("could not report connected state"); } -} - - -extern "C" void wpa_report_disconnect_event(struct wpa_supplicant *wpa_s) -{ - 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(Genode::Cstring((char *)wpa_ssid->ssid, wpa_ssid->ssid_len)); - - char bssid_buf[MAC_STR_LEN]; - mac2str(bssid_buf, wpa_ssid->bssid); - - unsigned auth_failures = wpa_ssid->auth_failures; - - xml.node("accesspoint", [&]() { - xml.attribute("ssid", ssid.string()); - xml.attribute("bssid", bssid_buf); - xml.attribute("state", "disconnected"); - xml.attribute("auth_failures", auth_failures); - }); - - }); - } catch (...) { Genode::warning("could not report disconnected state"); } -} - - -static inline int approximate_quality(struct wpa_bss *bss) -{ - /* - * We provide an quality value by transforming the actual - * signal level [-50,-100] (dBm) to [100,0] (%). - */ - int level = bss->level; - - if (level <= -100) - return 0; - else if (level >= -50) - return 100; - - return 2 * (level + 100); -} - - -extern "C" void wpa_report_scan_results(struct wpa_supplicant *wpa_s) -{ - 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(Genode::Cstring((char *)bss->ssid, bss->ssid_len)); - - int quality = approximate_quality(bss); - - xml.node("accesspoint", [&]() { - xml.attribute("ssid", ssid.string()); - xml.attribute("bssid", bssid_buf); - xml.attribute("quality", quality); - - /* XXX we forcefully only support WPA/WPA2 psk for now */ - if (wpa || wpa2) - xml.attribute("protection", "WPA-PSK"); - }); - } - }); - } catch (...) { Genode::warning("could not report scan results"); } -} diff --git a/repos/dde_linux/src/lib/wpa_supplicant/symbol.map b/repos/dde_linux/src/lib/wpa_supplicant/symbol.map index bebca77a18..cb0f78a1dd 100644 --- a/repos/dde_linux/src/lib/wpa_supplicant/symbol.map +++ b/repos/dde_linux/src/lib/wpa_supplicant/symbol.map @@ -7,6 +7,7 @@ /* needed by wifi_drv */ wpa_main; wpa_reporter_init; + wpa_ctrl_set_fd; /* needed by wpa_driver_nl80211 */ __hide_aliasing_typecast; diff --git a/repos/dde_linux/wifi.list b/repos/dde_linux/wifi.list index a7a48951ff..c36173b633 100644 --- a/repos/dde_linux/wifi.list +++ b/repos/dde_linux/wifi.list @@ -359,7 +359,6 @@ linux-x.x.x/net/packet/af_packet.c linux-x.x.x/net/packet/internal.h linux-x.x.x/net/rfkill/core.c linux-x.x.x/net/rfkill/input.c -linux-x.x.x/net/rfkill/rfkill-gpio.c linux-x.x.x/net/rfkill/rfkill.h linux-x.x.x/net/wireless/ap.c linux-x.x.x/net/wireless/chan.c