diff --git a/repos/dde_linux/recipes/raw/wifi_firmware/content.mk b/repos/dde_linux/recipes/raw/wifi_firmware/content.mk
new file mode 100644
index 0000000000..7855d1f15d
--- /dev/null
+++ b/repos/dde_linux/recipes/raw/wifi_firmware/content.mk
@@ -0,0 +1,17 @@
+PORT_DIR := $(call port_dir,$(REP_DIR)/ports/linux-firmware)
+
+content: ucode_files LICENSE.wifi_drv
+
+
+.PHONY: ucode_files
+ucode_files:
+ cp $(PORT_DIR)/firmware/*.ucode .
+ cp $(PORT_DIR)/firmware/regulatory.db .
+ cp $(PORT_DIR)/firmware/regulatory.db.p7s .
+
+LICENSE.wifi_drv:
+ for i in $(PORT_DIR)/firmware/LICEN*E.*; do \
+ echo "$${i##*/}:" >> $@; \
+ cat $$i >> $@; \
+ echo >> $@; \
+ done
diff --git a/repos/dde_linux/recipes/raw/wifi_firmware/hash b/repos/dde_linux/recipes/raw/wifi_firmware/hash
new file mode 100644
index 0000000000..c1f691ebb5
--- /dev/null
+++ b/repos/dde_linux/recipes/raw/wifi_firmware/hash
@@ -0,0 +1 @@
+2019-11-25 ff24f3bafaeeb47c053670264b5096dbc4b9a36d
diff --git a/repos/pc/lib/mk/spec/x86_32/wifi.mk b/repos/pc/lib/mk/spec/x86_32/wifi.mk
new file mode 100644
index 0000000000..6160802395
--- /dev/null
+++ b/repos/pc/lib/mk/spec/x86_32/wifi.mk
@@ -0,0 +1,5 @@
+include $(REP_DIR)/lib/mk/wifi.inc
+
+REQUIRES += 32bit
+
+SRC_C += lx_emul/spec/x86_32/atomic64_32.c
diff --git a/repos/pc/lib/mk/spec/x86_64/wifi.mk b/repos/pc/lib/mk/spec/x86_64/wifi.mk
new file mode 100644
index 0000000000..afe2b9b5dd
--- /dev/null
+++ b/repos/pc/lib/mk/spec/x86_64/wifi.mk
@@ -0,0 +1,3 @@
+include $(REP_DIR)/lib/mk/wifi.inc
+
+REQUIRES += 64bit
diff --git a/repos/pc/lib/mk/vfs_wifi.mk b/repos/pc/lib/mk/vfs_wifi.mk
new file mode 100644
index 0000000000..f2306c1676
--- /dev/null
+++ b/repos/pc/lib/mk/vfs_wifi.mk
@@ -0,0 +1,11 @@
+SRC_CC = vfs.cc
+
+DDE_LINUX_DIR := $(subst /src/include/lx_kit,,$(call select_from_repositories,src/include/lx_kit))
+
+INC_DIR += $(DDE_LINUX_DIR)/src/include
+
+LIBS := wifi
+
+vpath %.cc $(REP_DIR)/src/lib/vfs/wifi
+
+SHARED_LIB := yes
diff --git a/repos/pc/lib/mk/wifi.inc b/repos/pc/lib/mk/wifi.inc
new file mode 100644
index 0000000000..82c306a0b4
--- /dev/null
+++ b/repos/pc/lib/mk/wifi.inc
@@ -0,0 +1,48 @@
+REQUIRES := x86
+
+TARGET_LIB_DIR := $(REP_DIR)/src/lib/wifi
+
+SHARED_LIB := yes
+
+LD_OPT += --version-script=$(TARGET_LIB_DIR)/symbol.map
+
+LIBS += base pc_linux_generated pc_lx_emul
+INC_DIR := $(TARGET_LIB_DIR)
+SRC_CC += wlan.cc
+SRC_CC += misc.cc
+SRC_CC += time.cc
+SRC_CC += firmware.cc
+SRC_CC += socket_call.cc
+SRC_CC += random.cc
+
+SRC_C += dummies.c
+SRC_C += lx_emul.c
+SRC_C += uplink.c
+
+CC_OPT_lx_socket_call += -DKBUILD_MODNAME='"lx_socket_call"'
+SRC_C += lx_socket_call.c
+
+SRC_C += $(notdir $(wildcard $(TARGET_LIB_DIR)/generated_dummies.c))
+SRC_C += pc/lx_emul/common_dummies.c
+SRC_C += lx_emul/spec/x86/pci.c
+
+CC_C_OPT += -I$(LX_SRC_DIR)/drivers/net/wireless/intel/iwlwifi
+CC_C_OPT += -I$(LX_SRC_DIR)/include/linux
+
+CC_C_OPT += -Wno-address-of-packed-member
+
+#CC_OPT += -DCONFIG_IWLWIFI_DEBUG
+
+vpath %.c $(TARGET_LIB_DIR)
+vpath %.cc $(TARGET_LIB_DIR)
+vpath pc/lx_emul/common_dummies.c $(REP_DIR)/src/lib
+
+CUSTOM_TARGET_DEPS += $(TARGET_LIB_DIR)/symbol.map
+
+#
+# Genode C-API backends
+#
+
+SRC_CC += genode_c_api/uplink.cc
+
+vpath genode_c_api/uplink.cc $(subst /genode_c_api,,$(call select_from_repositories,src/lib/genode_c_api))
diff --git a/repos/pc/lib/mk/wifi_firmware.mk b/repos/pc/lib/mk/wifi_firmware.mk
new file mode 100644
index 0000000000..a00a214329
--- /dev/null
+++ b/repos/pc/lib/mk/wifi_firmware.mk
@@ -0,0 +1,24 @@
+#
+# Pseudo library to copy wireless LAN firmware to build directory
+#
+
+FW_CONTRIB_DIR := $(call select_from_ports,linux-firmware)
+
+IMAGES := $(notdir $(wildcard $(FW_CONTRIB_DIR)/firmware/*.ucode))
+IMAGES += $(notdir $(wildcard $(FW_CONTRIB_DIR)/firmware/*.db))
+IMAGES += $(notdir $(wildcard $(FW_CONTRIB_DIR)/firmware/*.p7s))
+BIN_DIR := $(BUILD_BASE_DIR)/bin
+FW_DIR := $(FW_CONTRIB_DIR)/firmware
+
+CUSTOM_TARGET_DEPS += $(addprefix $(BIN_DIR)/,$(IMAGES))
+
+$(BIN_DIR)/%.ucode: $(FW_DIR)/%.ucode
+ $(VERBOSE)cp $^ $@
+
+$(BIN_DIR)/%.db: $(FW_DIR)/%.db
+ $(VERBOSE)cp $^ $@
+
+$(BIN_DIR)/%.p7s: $(FW_DIR)/%.p7s
+ $(VERBOSE)cp $^ $@
+
+CC_CXX_WARN_STRICT =
diff --git a/repos/pc/recipes/pkg/wifi/README b/repos/pc/recipes/pkg/wifi/README
new file mode 100644
index 0000000000..574933d1f6
--- /dev/null
+++ b/repos/pc/recipes/pkg/wifi/README
@@ -0,0 +1,2 @@
+
+ Package for bundling pc_wifi_drv and wifi_firmware
diff --git a/repos/pc/recipes/pkg/wifi/archives b/repos/pc/recipes/pkg/wifi/archives
new file mode 100644
index 0000000000..406537fad5
--- /dev/null
+++ b/repos/pc/recipes/pkg/wifi/archives
@@ -0,0 +1,6 @@
+_/src/pc_wifi_drv
+_/src/openssl
+_/src/vfs
+_/src/vfs_jitterentropy
+_/src/libc
+_/raw/wifi_firmware
diff --git a/repos/pc/recipes/pkg/wifi/hash b/repos/pc/recipes/pkg/wifi/hash
new file mode 100644
index 0000000000..4b20d36a86
--- /dev/null
+++ b/repos/pc/recipes/pkg/wifi/hash
@@ -0,0 +1 @@
+2022-03-28-c 2f0c23734634d4c90d6c4ed2d08a5873bd3daed1
diff --git a/repos/pc/recipes/pkg/wifi/runtime b/repos/pc/recipes/pkg/wifi/runtime
new file mode 100644
index 0000000000..cb00960e49
--- /dev/null
+++ b/repos/pc/recipes/pkg/wifi/runtime
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/pc/recipes/src/pc_wifi_drv/content.mk b/repos/pc/recipes/src/pc_wifi_drv/content.mk
new file mode 100644
index 0000000000..f00b567bf0
--- /dev/null
+++ b/repos/pc/recipes/src/pc_wifi_drv/content.mk
@@ -0,0 +1,72 @@
+#
+# Driver portions
+#
+
+LIB_MK := $(addprefix lib/mk/,wifi_firmware.mk wifi.inc vfs_wifi.mk) \
+ $(foreach SPEC,x86_32 x86_64,lib/mk/spec/$(SPEC)/wifi.mk) \
+
+MIRROR_FROM_REP_DIR := src/drivers/wifi/pc \
+ src/lib/pc/lx_emul \
+ $(LIB_MK) \
+ $(shell cd $(REP_DIR); find src/drivers/wifi -type f) \
+ $(shell cd $(REP_DIR); find src/lib/wifi -type f) \
+ $(shell cd $(REP_DIR); find src/lib/vfs/wifi -type f)
+
+MIRROR_FROM_OS_DIR := src/lib/genode_c_api/uplink.cc
+
+#
+# DDE Linux portions (wpa_supplicant, libnl)
+#
+
+DDE_LINUX_REP_DIR := $(GENODE_DIR)/repos/dde_linux
+DDE_LINUX_PORT_DIR := $(call port_dir,$(DDE_LINUX_REP_DIR)/ports/dde_linux)
+
+DDE_LINUX_LIB_MK := \
+ $(addprefix lib/mk/,libnl.inc libnl_include.mk) \
+ $(foreach SPEC,x86_32 x86_64,lib/mk/spec/$(SPEC)/libnl.mk) \
+ $(addprefix lib/mk/spec/x86/,wpa_driver_nl80211.mk wpa_supplicant.mk)
+
+MIRROR_FROM_DDE_LINUX_DIR := $(DDE_LINUX_LIB_MK) \
+ lib/import/import-libnl_include.mk \
+ lib/import/import-libnl.mk \
+ include/wifi \
+ $(shell cd $(DDE_LINUX_REP_DIR); find src/lib/libnl -type f) \
+ $(shell cd $(DDE_LINUX_REP_DIR); find src/lib/wpa_driver_nl80211 -type f) \
+ $(shell cd $(DDE_LINUX_REP_DIR); find src/lib/wpa_supplicant -type f)
+
+MIRROR_FROM_DDE_LINUX_PORT_DIR := $(shell cd $(DDE_LINUX_PORT_DIR); find src/lib/libnl -type f) \
+ $(shell cd $(DDE_LINUX_PORT_DIR); find src/app/wpa_supplicant -type f)
+
+content: $(MIRROR_FROM_REP_DIR) $(MIRROR_FROM_OS_DIR) $(MIRROR_FROM_DDE_LINUX_DIR) \
+ $(MIRROR_FROM_DDE_LINUX_PORT_DIR) cleanup-wpa
+
+$(MIRROR_FROM_REP_DIR):
+ $(mirror_from_rep_dir)
+
+$(MIRROR_FROM_OS_DIR):
+ mkdir -p $(dir $@)
+ cp -r $(GENODE_DIR)/repos/os/$@ $@
+
+$(MIRROR_FROM_DDE_LINUX_DIR):
+ mkdir -p $(dir $@)
+ cp -r $(GENODE_DIR)/repos/dde_linux/$@ $@
+
+$(MIRROR_FROM_DDE_LINUX_PORT_DIR):
+ mkdir -p $(dir $@)
+ cp -r $(DDE_LINUX_PORT_DIR)/$@ $@
+
+cleanup-wpa: $(MIRROR_FROM_DDE_LINUX_PORT_DIR)
+ @for dir in .git doc eap_example hs20 mac80211_hwsim radius_example \
+ hostapd tests wlantest wpadebug wpaspy; do \
+ rm -rf src/app/wpa_supplicant/$$dir; done
+
+content: LICENSE
+LICENSE:
+ ( echo "Linux is subject to GNU General Public License version 2, see:"; \
+ echo "https://www.kernel.org/pub/linux/kernel/COPYING"; \
+ echo; \
+ echo "Libnl is subject to GNU LESSER GENERAL PUBLIC LICENSE Verson 2.1, see:"; \
+ echo " src/lib/libnl/COPYING"; \
+ echo; \
+ echo "Wpa_supplicant is subject to 3-clause BSD license, see:"; \
+ echo " src/app/wpa_supplicant/COPYING"; ) > $@
diff --git a/repos/pc/recipes/src/pc_wifi_drv/hash b/repos/pc/recipes/src/pc_wifi_drv/hash
new file mode 100644
index 0000000000..7ebc5c77cb
--- /dev/null
+++ b/repos/pc/recipes/src/pc_wifi_drv/hash
@@ -0,0 +1 @@
+2022-03-25-d 6b387b53c6390a75e1fdd55586aac54d3812271c
diff --git a/repos/pc/recipes/src/pc_wifi_drv/used_apis b/repos/pc/recipes/src/pc_wifi_drv/used_apis
new file mode 100644
index 0000000000..e00fd16fee
--- /dev/null
+++ b/repos/pc/recipes/src/pc_wifi_drv/used_apis
@@ -0,0 +1,12 @@
+base
+genode_c_api
+libc
+openssl
+os
+pc_linux
+nic_session
+platform_session
+report_session
+timer_session
+uplink_session
+vfs
diff --git a/repos/pc/run/wifi.run b/repos/pc/run/wifi.run
new file mode 100644
index 0000000000..65bb545e68
--- /dev/null
+++ b/repos/pc/run/wifi.run
@@ -0,0 +1,283 @@
+#
+# Configure wireless lan
+#
+
+proc wifi_ssid { } {
+ return $::env(GENODE_WIFI_SSID)
+}
+
+proc wifi_psk { } {
+ return $::env(GENODE_WIFI_PSK)
+}
+
+#
+# widi_drv config generator (supporting a network list)
+#
+# You may script your tests with this function in the dynamic_rom config below.
+# The syntax for the networks parameter is
+#
+# { ssid protection passphrase explicit_scan }
+#
+# Example dynamic_rom config:
+#
+# {
+# } [wifi_config 30 5 no [list "net1 WPA2 net1_psk no" "net2 WPA2 net2_psk no"]] {
+#
+#
+# } [wifi_config 30 5 no [list "net1 WPA2 net1_psk no" "net2 WPA2 net2_psk yes"]] {
+# }
+
+set wifi_verbose false
+set wifi_verbose_state false
+
+proc wifi_config { connected_scan_interval scan_interval rfkill networks } {
+ global wifi_verbose
+ global wifi_verbose_state
+
+ set config "\n"
+ foreach n $networks {
+ append config " \n"
+ }
+ append config "\n"
+
+ return $config
+}
+
+#
+# Restrict platforms
+#
+assert_spec x86
+
+#
+# Build
+#
+
+set build_components {
+ core init timer
+ drivers/rtc
+ drivers/wifi/pc
+ server/report_rom
+ server/dynamic_rom
+ server/nic_router
+ test/lwip/http_srv
+ lib/vfs/wifi
+ lib/vfs/jitterentropy
+ lib/vfs/lwip
+}
+
+source ${genode_dir}/repos/base/run/platform_drv.inc
+append_platform_drv_build_components
+
+build $build_components
+
+create_boot_directory
+
+#
+# Generate config
+#
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+} [wifi_config 30 5 no {}] {
+
+
+
+} [wifi_config 30 5 no [list "[wifi_ssid] WPA2 [wifi_psk] yes"]] {
+
+
+
+} [wifi_config 30 5 yes [list "[wifi_ssid] WPA2 [wifi_psk] yes"]] {
+
+
+
+} [wifi_config 30 5 no [list "[wifi_ssid] WPA2 [wifi_psk] yes"]] {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+append_platform_drv_config
+
+append config {
+
+}
+
+install_config $config
+
+set firmware_modules {
+ iwlwifi-1000-5.ucode
+ iwlwifi-3160-17.ucode
+ iwlwifi-3168-29.ucode
+ iwlwifi-5000-5.ucode
+ iwlwifi-6000-4.ucode
+ iwlwifi-7260-17.ucode
+ iwlwifi-7265-17.ucode
+ iwlwifi-7265D-29.ucode
+ iwlwifi-8000C-36.ucode
+ iwlwifi-8265-36.ucode
+ iwlwifi-9000-pu-b0-jf-b0-34.ucode
+ iwlwifi-9000-pu-b0-jf-b0-46.ucode
+ iwlwifi-QuZ-a0-hr-b0-63.ucode
+ regulatory.db
+ regulatory.db.p7s
+}
+
+#
+# Boot modules
+#
+
+# generic modules
+set boot_modules {
+ core ld.lib.so init timer rtc_drv report_rom dynamic_rom
+ 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
+ pc_wifi_drv wifi.lib.so vfs_wifi.lib.so
+ nic_router
+
+ test-lwip_httpsrv
+ vfs_lwip.lib.so
+}
+
+append boot_modules $firmware_modules
+
+append_platform_drv_boot_modules
+
+build_boot_image $boot_modules
+
+run_genode_until forever
+
+# vi: set ft=tcl :
diff --git a/repos/pc/src/drivers/wifi/pc/README b/repos/pc/src/drivers/wifi/pc/README
new file mode 100644
index 0000000000..982ed0c8f6
--- /dev/null
+++ b/repos/pc/src/drivers/wifi/pc/README
@@ -0,0 +1,108 @@
+The pc_wifi_drv component is a port of the Linux mac802.11 stack, including
+the iwlwifi driver as well as libnl and wpa_supplicant, to Genode.
+
+To start the component the following configuration snippet can be used:
+
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+
+To temporarily prevent any radio activity, the 'rfkill' attribute
+can be set to 'true'.
+
+If the network is protected by, e.g., WPA/WPA2, the protection type, either
+'WPA' or 'WPA2' as well as the the passphrase have to be specified.
+The 'bssid' attribute can be used to select a specifc accesspoint within a
+network. Of all attributes only the 'ssid' attribute is mandatory, all others
+are optional and should only be used when needed.
+
+The configuration may contain more than one network. In This case the driver
+will try to select the best one it gets a response from. To prevent it
+from automatically joining the network the 'auto_connect' attribute must be
+set to 'false'; the default value is 'true'. If the 'explicit_scan' attribute
+is set, the driver will pro-actively scan for a hidden network with the given
+SSID:
+
+!
+!
+!
+!
+
+By default, the driver scans for available networks only when not
+connected. This can be changed with the 'connected_scan_interval'
+attribute, which specifies the interval for connected scans in
+seconds and directly influences any roaming decision, i.e., select
+a better fit accesspoint for the configured network.
+
+Also, the driver can be switched to verbose logging during runtime
+by setting the 'verbose' or 'verbose_state' attribute to 'true'.
+
+The wifi_drv creates two distinct reports to communicate its state and
+information about the wireless infrastructure to other components. The
+first one is a list of all available accesspoints. The following examplary
+report shows its general structure:
+
+!
+!
+!
+!
+!
+
+Each accesspoint node has attributes that contain the SSID and the BSSID
+of the accesspoint as well as the link quality (signal strength). These
+attributes are mandatory. If the network is protected, the node will also
+have an attribute describing the type of protection in addition.
+
+The second report provides information about the state of the connection
+to the currently connected accesspoint:
+
+!
+!
+!
+
+Valid state values are 'connected', 'disconnected', 'connecting'. Depending
+on the state, there are additional attributes that can be checked. In case
+of an authentication error, e.g. the passphrase is wrong, the 'auth_failure'
+attribute will be set to 'true'. The 'rfkilled' attribute is set to 'true'
+if a disconnect was triggered by disabling the radio activity via setting
+the 'rfkill' attribute.
+
+By subscribing to both reports and providing the required 'wifi_config' ROM
+module, a component is able control the wireless driver.
+
+Currently only WPA/WPA2 protection using a passphrase is supported and the the
+SSID is copied verbatim. At the moment, there is no way to express or escape
+non alphanumeric characters.
+
+On certain cards, e.g. Intel Wireless 6200 ABG, it may be necessary to disable
+the 11n mode. This can be achieved by setting the 'use_11n' attribute in
+the 'wifi_config' node to 'no'.
+
+The driver optionally reports the following information under the
+label "devices" if requested in the config as depicted.
+
+!
+
+!
diff --git a/repos/pc/src/drivers/wifi/pc/frontend.h b/repos/pc/src/drivers/wifi/pc/frontend.h
new file mode 100644
index 0000000000..63ad6b8a2d
--- /dev/null
+++ b/repos/pc/src/drivers/wifi/pc/frontend.h
@@ -0,0 +1,1658 @@
+ /*
+ * \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-2022 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
+#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 },
+ { "CTRL-EVENT-NETWORK-NOT-FOUND", 28 },
+};
+
+enum Rmi {
+ OK = 0,
+ FAIL,
+ SCAN_RESULTS,
+ CONNECTED,
+ DISCONNECTED,
+ SME_AUTH,
+ NOT_FOUND,
+};
+
+
+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 network_not_found(char const *msg) {
+ return check_recv_msg(msg, recv_table[NOT_FOUND]); }
+
+
+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 : Genode::Interface
+{
+ 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 };
+ bool explicit_scan { 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(); bssid = Bssid(); }
+
+ bool valid() const { return ssid.length() > 1; }
+ bool bssid_valid() const { return bssid.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
+{
+ Frontend(const Frontend&) = delete;
+ Frontend& operator=(const Frontend&) = delete;
+
+ /* accesspoint */
+
+ Genode::Heap _ap_allocator;
+
+ using Accesspoint_r = Genode::Registered;
+
+ Genode::Registry _aps { };
+
+ Accesspoint *_lookup_ap_by_ssid(Accesspoint::Ssid const &ssid)
+ {
+ Accesspoint *p = nullptr;
+ _aps.for_each([&] (Accesspoint &ap) {
+ if (ap.valid() && ap.ssid == ssid) { p = ≈ }
+ });
+ return p;
+ }
+
+ Accesspoint *_lookup_ap_by_bssid(Accesspoint::Bssid const &bssid)
+ {
+ Accesspoint *p = nullptr;
+ _aps.for_each([&] (Accesspoint &ap) {
+ if (ap.valid() && ap.bssid == bssid) { p = ≈ }
+ });
+ return p;
+ }
+
+ Accesspoint *_alloc_ap()
+ {
+ return new (&_ap_allocator) Accesspoint_r(_aps);
+ }
+
+ void _free_ap(Accesspoint &ap)
+ {
+ Genode::destroy(&_ap_allocator, &ap);
+ }
+
+ template
+ void _for_each_ap(FUNC const &func)
+ {
+ _aps.for_each([&] (Accesspoint &ap) {
+ 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::Blockade _notify_blockade { };
+
+ void _notify_lock_lock() { _notify_blockade.block(); }
+ void _notify_lock_unlock() { _notify_blockade.wakeup(); }
+
+ 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("rfkilled in state ", 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 _single_autoconnect { false };
+
+ Genode::uint64_t _connected_scan_interval { 30 };
+ Genode::uint64_t _scan_interval { 5 };
+
+ void _config_update(bool signal)
+ {
+ _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);
+
+ Genode::uint64_t connected_scan_interval =
+ Util::check_time(config.attribute_value("connected_scan_interval",
+ _connected_scan_interval),
+ 0, 15*60);
+
+ Genode::uint64_t scan_interval =
+ Util::check_time(config.attribute_value("scan_interval",
+ _scan_interval),
+ 5, 15*60);
+
+ if ( connected_scan_interval > _connected_scan_interval
+ || scan_interval > _scan_interval) {
+ _arm_scan_timer(_connected_ap.bssid_valid());
+ }
+
+ _connected_scan_interval = connected_scan_interval;
+ _scan_interval = scan_interval;
+
+ /*
+ * 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) {
+ _rfkilled = true;
+
+ Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () {
+ xml.node("accesspoint", [&] () {
+ xml.attribute("state", "disconnected");
+ xml.attribute("rfkilled", _rfkilled);
+ });
+ });
+
+ _connected_ap.invalidate();
+ }
+ }
+
+ /*
+ * 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 single_autoconnect = false;
+
+ /* update AP list */
+ auto parse = [&] ( 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 = _alloc_ap();
+ 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);
+ ap.explicit_scan = node.attribute_value("explicit_scan", false);
+
+ 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.length() > 1 && 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;
+ p->explicit_scan = ap.explicit_scan;
+
+ single_autoconnect |= (p->update || p->auto_connect) && !_connected_ap.valid();
+ };
+ config.for_each_sub_node("network", parse);
+
+ /*
+ * To accomodate a management component that only deals
+ * with on network, e.g. the sculpt_manager, generate a
+ * fake connecting event. Either a connected or disconnected
+ * event will bring us to square one.
+ */
+ if (signal && _count_to_be_enabled() == 1 && single_autoconnect && !_rfkilled) {
+
+ auto lookup = [&] (Accesspoint const &ap) {
+ if (!ap.auto_connect) { return; }
+
+ if (_verbose) { Genode::log("Single autoconnect event for '", ap.ssid, "'"); }
+
+ try {
+ Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () {
+ xml.node("accesspoint", [&] () {
+ xml.attribute("ssid", ap.ssid);
+ xml.attribute("state", "connecting");
+ });
+ });
+
+ _single_autoconnect = true;
+
+ } catch (...) { }
+ };
+ _for_each_ap(lookup);
+ }
+
+ /*
+ * Marking removes stale APs first and triggers adding of
+ * new ones afterwards.
+ */
+ _mark_stale_aps(config);
+ }
+
+ void _handle_config_update() { _config_update(true); }
+
+ /* 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;
+
+ 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;
+ }
+
+ /* scanning was disabled, ignore current request */
+ if (!_arm_scan_timer(_connected_ap.bssid_valid())) {
+ if (_verbose) { Genode::log("Scanning disabled, ignore current scan request"); }
+ return;
+ }
+
+ /* skip as we will be scheduled some time soon(tm) anyway */
+ if (_state != State::IDLE) {
+ if (_verbose) {
+ Genode::log("Not idle, ignore scan request, state: ",
+ Genode::Hex((unsigned)_state));
+ }
+ return;
+ }
+
+ /* left one attempt out */
+ if (_scan_busy) {
+ if (_verbose) { Genode::log("Scan already pending, ignore scan request"); }
+ _scan_busy = false;
+ return;
+ }
+
+ enum { SSID_ARG_LEN = 6 + 64, /* " ssid " + "a5a5a5a5..." */ };
+ /* send buffer - 'SCAN ' + stuff */
+ char ssid_buffer[sizeof(Msg_buffer::send)-16] = { };
+ size_t buffer_pos = 0;
+
+ auto valid_ssid = [&] (Accesspoint const &ap) {
+
+ if (buffer_pos + SSID_ARG_LEN >= sizeof(ssid_buffer)) {
+ return;
+ }
+
+ if (!ap.explicit_scan) { return; }
+
+ char ssid_hex[64+1] = { };
+ char const *ssid = ap.ssid.string();
+
+ for (size_t i = 0; i < ap.ssid.length() - 1; i++) {
+ Util::byte2hex((ssid_hex + i * 2), ssid[i]);
+ }
+
+ Genode::String tmp(" ssid ", (char const*)ssid_hex);
+ size_t const tmp_len = tmp.length() - 1;
+
+ Genode::memcpy((ssid_buffer + buffer_pos), tmp.string(), tmp_len);
+ buffer_pos += tmp_len;
+ };
+ _for_each_ap(valid_ssid);
+
+ _state_transition(_state, State::INITIATE_SCAN);
+ _submit_cmd(Cmd_str("SCAN", (char const*)ssid_buffer));
+ }
+
+ bool _arm_scan_timer(bool connected)
+ {
+ Genode::uint64_t const sec = connected ? _connected_scan_interval : _scan_interval;
+ if (!sec) { return false; }
+
+ if (_verbose) {
+ Genode::log("Arm ", connected ? "connected " : "",
+ "scan: ", sec, " sec");
+ }
+
+ _scan_timer.trigger_once(sec * (1000 * 1000));
+ return true;
+ }
+
+ 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) {
+ if (_verbose) { Genode::log("Scan results empty"); }
+ return;
+ }
+
+ bool connecting_attempt = false;
+ try {
+
+ _ap_reporter->generate([&] (Genode::Xml_generator &xml) {
+
+ 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); }
+ });
+
+ auto check_existence = [&] (Accesspoint &lap) {
+ connecting_attempt |= (lap.ssid == ap.ssid) && ap.auto_connect;
+ };
+ _for_each_ap(check_existence);
+ });
+ });
+
+ } catch (...) { /* silently omit report */ }
+
+ try {
+ if (!_connected_ap.bssid_valid() && connecting_attempt) {
+
+ Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () {
+ xml.node("accesspoint", [&] () {
+ xml.attribute("state", "connecting");
+ });
+ });
+ }
+ } catch (...) { /* silently omit state */ }
+ }
+
+ /* network commands */
+
+ void _mark_stale_aps(Genode::Xml_node const &config)
+ {
+ auto mark_stale = [&] (Accesspoint &ap) {
+ ap.stale = true;
+
+ config.for_each_sub_node("network", [&] ( 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; }
+
+ _aps.for_each([&] (Accesspoint &ap) {
+ if (!_processed_ap && ap.valid() && ap.stale) {
+ _processed_ap = ≈
+ }
+ });
+
+ 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; }
+
+ _aps.for_each([&] (Accesspoint &ap) {
+ if (!_processed_ap && ap.stored() && ap.update) {
+ _processed_ap = ≈
+ }
+ });
+
+ if (!_processed_ap) { return; }
+
+ if (_verbose) {
+ Genode::log("Update network: '", _processed_ap->ssid, "'");
+ }
+
+ /* 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; }
+
+ _aps.for_each([&] (Accesspoint &ap) {
+ if (!_processed_ap && ap.valid() && !ap.stored()) {
+ _processed_ap = ≈
+ }
+ });
+
+ 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; }
+
+ _aps.for_each([&] (Accesspoint &ap) {
+ if ( !_processed_ap && ap.valid()
+ && !ap.enabled && ap.auto_connect) {
+ _processed_ap = ≈
+ }
+ });
+
+ 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; }
+
+ _aps.for_each([&] (Accesspoint &ap) {
+ if ( !_processed_ap && ap.valid()
+ && ap.enabled && ap.auto_connect) {
+ _processed_ap = ≈
+ }
+ });
+
+ 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 = static_cast(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 */
+
+ bool _scan_busy { false };
+
+ void _handle_scan_results(State state, char const *msg)
+ {
+ switch (state) {
+ case State::INITIATE_SCAN:
+ if (!cmd_successful(msg)) {
+ _scan_busy = Genode::strcmp(msg, "FAIL-BUSY");
+ if (!_scan_busy) {
+ 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(*_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 (_single_autoconnect && !successfully) {
+ try {
+ Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () {
+ xml.node("accesspoint", [&] () {
+ xml.attribute("state", "disconnected");
+ xml.attribute("rfkilled", _rfkilled);
+ xml.attribute("config_error", true);
+ });
+ });
+
+ _single_autoconnect = false;
+ } catch (...) { }
+ }
+ }
+
+ void _handle_status_result(State &state, char const *msg)
+ {
+ _state_transition(state, State::IDLE);
+
+ /*
+ * Querying the status might have failed but we already sent
+ * out a rudimentary report, just stop here.
+ */
+ if (0 == msg[0]) { 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);
+
+ if (!ap.ssid.valid()) {
+ Genode::error("Cannot query SSID :-(");
+ return;
+ }
+
+ Accesspoint *p = _lookup_ap_by_ssid(ap.ssid);
+ if (p) {
+ p->bssid = ap.bssid;
+ p->freq = ap.freq;
+ }
+
+ _connected_ap.ssid = ap.ssid;
+
+ 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");
+ });
+ });
+ }
+
+ void _handle_info_result(State &state, char const *msg)
+ {
+ _state_transition(state, State::IDLE);
+
+ if (!_connected_event && !_disconnected_event) { return; }
+
+ /*
+ * It might happen that the supplicant already flushed
+ * its internal BSS information and cannot help us out.
+ * Since we already sent out a rudimentary report, just
+ * stop here.
+ */
+ if (0 == msg[0]) { 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);
+
+ /*
+ * ... 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) {
+ /*
+ * Being able to remove a failed network from the internal
+ * state of the supplicant relies on a sucessful BSS request.
+ * In case that failes the supplicant will try to join the
+ * network again and again...
+ */
+ if (!p || _processed_ap) {
+ 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) {
+ /*
+ * In case the BSS cmd did not return a valid SSID, which
+ * was observed only with hidden networks so far, check the
+ * current status.
+ */
+ if (!p) {
+ _state_transition(state, State::STATUS);
+ _submit_cmd(Cmd_str("STATUS"));
+
+ } else {
+ p->bssid = ap.bssid;
+ p->freq = ap.freq;
+ }
+
+ _connected_ap = ap;
+ }
+ }
+
+ /* 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 };
+
+ enum { MAX_ATTEMPTS = 3, };
+ unsigned _scan_attempts { 0 };
+
+ Accesspoint::Bssid _pending_bssid { };
+
+ void _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]);
+ bool const auth_failed = disconnected && _auth_failure(msg);
+
+ State state = connected ? State::CONNECTED : State::DISCONNECTED;
+ Accesspoint::Bssid const &bssid = _extract_bssid(msg, state);
+
+ /*
+ * Always reset the "global" connection state first
+ */
+ _connected_ap.invalidate();
+ if (connected) { _connected_ap.bssid = bssid; }
+ if (connected || disconnected) { _connecting = Accesspoint::Bssid(); }
+
+ /*
+ * Save local connection state here for later re-use when
+ * the BSS information are handled.
+ */
+ _connected_event = connected;
+ _disconnected_event = disconnected;
+ _disconnected_fail = auth_failed;
+
+ if (!_rfkilled) {
+
+ /*
+ * As we only received the BSSID, try to gather more information
+ * so we may generate a more thorough follow-up state report.
+ */
+ if (_state != State::IDLE) {
+ _pending_bssid = bssid;
+ } else {
+ _state_transition(_state, State::INFO);
+ _submit_cmd(Cmd_str("BSS ", bssid));
+ }
+
+ _arm_scan_timer(connected);
+ }
+
+ /*
+ * Generate the first rudimentary report whose missing information
+ * are (potentially) filled in later (see above).
+ */
+ Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () {
+ xml.node("accesspoint", [&] () {
+ xml.attribute("bssid", bssid);
+ xml.attribute("state", connected ? "connected"
+ : "disconnected");
+ if (disconnected) {
+ xml.attribute("rfkilled", _rfkilled);
+ if (auth_failed) {
+ xml.attribute("auth_failure", auth_failed);
+ }
+ }
+ });
+ });
+
+ /* reset */
+ _single_autoconnect = false;
+ }
+
+ 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 (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 (!_single_autoconnect) {
+ 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
+
+ if (network_not_found(msg)) {
+
+ /* always try to update the accesspoint list */
+ if (_state == State::IDLE) {
+ _state_transition(_state, State::PENDING_RESULTS);
+ _submit_cmd(Cmd_str("SCAN_RESULTS"));
+ }
+
+ if (_single_autoconnect && ++_scan_attempts >= MAX_ATTEMPTS) {
+ _scan_attempts = 0;
+ _single_autoconnect = false;
+
+ Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () {
+ xml.node("accesspoint", [&] () {
+ xml.attribute("state", "disconnected");
+ xml.attribute("rfkilled", _rfkilled);
+ xml.attribute("not_found", true);
+ });
+ });
+ }
+
+ } 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;
+ }
+
+ _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.bssid_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)
+ :
+ _ap_allocator(env.ram(), env.rm()),
+ _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);
+
+ /* set/initialize as unblocked */
+ _notify_blockade.wakeup();
+
+ try {
+ _ap_reporter.construct(env, "accesspoints", "accesspoints");
+ _ap_reporter->generate([&] (Genode::Xml_generator &) { });
+ } catch (...) {
+ Genode::warning("no Report session available, scan results will "
+ "not be reported");
+ }
+
+ try {
+ _state_reporter.construct(env, "state");
+ _state_reporter->enabled(true);
+
+ Genode::Reporter::Xml_generator xml(*_state_reporter, [&] () {
+ xml.node("accesspoint", [&] () {
+ xml.attribute("state", "disconnected");
+ xml.attribute("rfkilled", _rfkilled);
+ });
+ });
+ } catch (...) {
+ Genode::warning("no Report session available, connectivity will "
+ "not be reported");
+ }
+
+ /* read in list of APs */
+ _config_update(false);
+
+ /* 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/pc/src/drivers/wifi/pc/main.cc b/repos/pc/src/drivers/wifi/pc/main.cc
new file mode 100644
index 0000000000..dc7a09a581
--- /dev/null
+++ b/repos/pc/src/drivers/wifi/pc/main.cc
@@ -0,0 +1,147 @@
+/*
+ * \brief Startup Wifi driver
+ * \author Josef Soentgen
+ * \date 2014-03-03
+ */
+
+/*
+ * 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
+#include
+#include
+#include
+
+/* local includes */
+#include
+#include
+#include
+
+
+using namespace Genode;
+
+
+static Wifi::Frontend *_wifi_frontend = nullptr;
+
+
+/**
+ * Notify front end about command processing
+ *
+ * Called by the CTRL interface after wpa_supplicant has processed
+ * the command.
+ */
+void wifi_block_for_processing(void)
+{
+ if (!_wifi_frontend) {
+ warning("frontend not available, dropping notification");
+ return;
+ }
+
+ /*
+ * Next time we block as long as the front end has not finished
+ * handling our previous request
+ */
+ _wifi_frontend->block_for_processing();
+
+ /* XXX hack to trick poll() into returning faster */
+ wpa_ctrl_set_fd();
+}
+
+
+void wifi_notify_cmd_result(void)
+{
+ if (!_wifi_frontend) {
+ warning("frontend not available, dropping notification");
+ return;
+ }
+
+ Signal_transmitter(_wifi_frontend->result_sigh()).submit();
+}
+
+
+/**
+ * Notify front end about triggered event
+ *
+ * Called by the CTRL interface whenever wpa_supplicant has triggered
+ * a event.
+ */
+void wifi_notify_event(void)
+{
+ if (!_wifi_frontend) {
+ Genode::warning("frontend not available, dropping notification");
+ return;
+ }
+
+ Signal_transmitter(_wifi_frontend->event_sigh()).submit();
+}
+
+
+/* exported by wifi.lib.so */
+extern void wifi_init(Genode::Env&,
+ Genode::Blockade&,
+ bool,
+ Genode::Signal_context_capability);
+
+struct Main
+{
+ Env &env;
+
+ Constructible _wpa;
+ Constructible _frontend;
+
+ Blockade _wpa_startup_blockade { };
+
+ Main(Genode::Env &env) : env(env)
+ {
+ _wpa.construct(env, _wpa_startup_blockade);
+
+ wifi_init(env, _wpa_startup_blockade, false,
+ Genode::Signal_context_capability());
+ }
+};
+
+static Main *_main;
+
+
+/**
+ * Return shared-memory message buffer
+ *
+ * It is used by the wpa_supplicant CTRL interface.
+ */
+void *wifi_get_buffer(void)
+{
+ /*
+ * XXX creating the front end at this point is merely a hack
+ * to post-pone its creation
+ */
+ if (_wifi_frontend)
+ return &_wifi_frontend->msg_buffer();
+
+ Libc::with_libc([&] () {
+
+ if (_main->_frontend.constructed())
+ return;
+
+ _main->_frontend.construct(_main->env);
+ _wifi_frontend = &*_main->_frontend;
+ });
+
+ return &_wifi_frontend->msg_buffer();
+}
+
+
+void Libc::Component::construct(Libc::Env &env)
+{
+ Libc::with_libc([&] () {
+ static Main server(env);
+ _main = &server;
+ });
+}
diff --git a/repos/pc/src/drivers/wifi/pc/target.mk b/repos/pc/src/drivers/wifi/pc/target.mk
new file mode 100644
index 0000000000..18c96e2f15
--- /dev/null
+++ b/repos/pc/src/drivers/wifi/pc/target.mk
@@ -0,0 +1,10 @@
+TARGET := pc_wifi_drv
+SRC_CC := main.cc wpa.cc
+LIBS := base wifi wifi_firmware
+LIBS += libc
+LIBS += wpa_supplicant
+LIBS += libcrypto libssl wpa_driver_nl80211
+
+INC_DIR += $(PRG_DIR)
+
+CC_CXX_WARN_STRICT :=
diff --git a/repos/pc/src/drivers/wifi/pc/util.h b/repos/pc/src/drivers/wifi/pc/util.h
new file mode 100644
index 0000000000..14833bcf31
--- /dev/null
+++ b/repos/pc/src/drivers/wifi/pc/util.h
@@ -0,0 +1,90 @@
+/*
+ * \brief Wifi front end utilities
+ * \author Josef Soentgen
+ * \date 2018-07-23
+ */
+
+/*
+ * Copyright (C) 2018-2022 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 * (unsigned)(level + 100);
+ }
+
+ inline Genode::uint64_t check_time(Genode::uint64_t value, Genode::uint64_t min, Genode::uint64_t max)
+ {
+ if (value < min) { return min; }
+ else if (value > max) { return max; }
+ return value;
+ }
+
+} /* namespace Util */
+
+#endif /* _WIFI__UTIL_H_ */
diff --git a/repos/pc/src/drivers/wifi/pc/wpa.cc b/repos/pc/src/drivers/wifi/pc/wpa.cc
new file mode 100644
index 0000000000..61c342281d
--- /dev/null
+++ b/repos/pc/src/drivers/wifi/pc/wpa.cc
@@ -0,0 +1,57 @@
+/*
+ * \brief Wpa_supplicant thread of the wifi driver
+ * \author Josef Soentgen
+ * \author Christian Helmuth
+ * \date 2019-12-18
+ */
+
+/*
+ * Copyright (C) 2019 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+
+/* libc includes */
+#include
+#include
+#include
+
+#include "wpa.h"
+
+
+/* entry function */
+extern "C" int wpa_main(void);
+
+
+void * Wpa_thread::_entry_trampoline(void *arg)
+{
+ Wpa_thread *t = (Wpa_thread *)arg;
+ t->_entry();
+ return nullptr;
+}
+
+
+void Wpa_thread::_entry()
+{
+ /* wait until the wifi driver is up and running */
+ _blockade.block();
+ _exit = wpa_main();
+ Genode::sleep_forever();
+}
+
+
+Wpa_thread::Wpa_thread(Genode::Env &env, Genode::Blockade &blockade)
+: _blockade(blockade), _exit(-1)
+{
+ pthread_t tid = 0;
+ if (pthread_create(&tid, 0, _entry_trampoline, this) != 0) {
+ printf("Error: pthread_create() failed\n");
+ exit(-1);
+ }
+}
diff --git a/repos/pc/src/drivers/wifi/pc/wpa.h b/repos/pc/src/drivers/wifi/pc/wpa.h
new file mode 100644
index 0000000000..3a124cc142
--- /dev/null
+++ b/repos/pc/src/drivers/wifi/pc/wpa.h
@@ -0,0 +1,39 @@
+/*
+ * \brief Wpa_supplicant thread of the wifi driver
+ * \author Josef Soentgen
+ * \date 2014-03-03
+ */
+
+/*
+ * Copyright (C) 2014-2017 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#ifndef _WIFI__WPA_H_
+#define _WIFI__WPA_H_
+
+
+namespace Genode {
+ struct Env;
+ struct Blockade;
+}
+
+class Wpa_thread
+{
+ private:
+
+ Genode::Blockade &_blockade;
+ int _exit;
+
+ static void * _entry_trampoline(void *arg);
+
+ void _entry();
+
+ public:
+
+ Wpa_thread(Genode::Env &, Genode::Blockade &);
+};
+
+#endif /* _WIFI__WPA_H_ */
diff --git a/repos/pc/src/lib/vfs/wifi/target.mk b/repos/pc/src/lib/vfs/wifi/target.mk
new file mode 100644
index 0000000000..8e08765e45
--- /dev/null
+++ b/repos/pc/src/lib/vfs/wifi/target.mk
@@ -0,0 +1 @@
+LIBS := vfs_wifi
diff --git a/repos/pc/src/lib/vfs/wifi/vfs.cc b/repos/pc/src/lib/vfs/wifi/vfs.cc
new file mode 100644
index 0000000000..e26c052f80
--- /dev/null
+++ b/repos/pc/src/lib/vfs/wifi/vfs.cc
@@ -0,0 +1,87 @@
+/*
+ * \brief Minimal VFS plugin for bringing up WLAN driver
+ * \author Josef Soentgen
+ * \date 2022-02-20
+ *
+ * The sole purpose of this VFS plugin is to call 'Lx_kit::initialize_env'
+ * at the right time before 'env.exec_static_constructors' is executed.
+ */
+
+/*
+ * Copyright (C) 2022 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* Genode includes */
+#include
+#include
+
+/* DDE Linux includes */
+#include
+
+
+namespace Vfs_wlan
+{
+ using namespace Vfs;
+ using namespace Genode;
+
+ struct File_system;
+}
+
+struct Vfs_wlan::File_system : Single_file_system
+{
+
+ File_system(Vfs::Env &env, Xml_node config)
+ :
+ Single_file_system { Vfs::Node_type::CONTINUOUS_FILE, name(),
+ Vfs::Node_rwx::ro(), config }
+ {
+ /*
+ * Various ports of a DDE Linux based library rely on the
+ * 'env' being set before any static constructor is executed.
+ * So we set it here and wait for the user of the library to
+ * execute the constructors at the proper time.
+ */
+
+ Lx_kit::initialize(env.env());
+ }
+
+ Open_result open(char const *, unsigned,
+ Vfs::Vfs_handle **,
+ Allocator &) override
+ {
+ return OPEN_ERR_UNACCESSIBLE;
+ }
+
+ Stat_result stat(char const *, Stat &) override
+ {
+ return STAT_ERR_NO_ENTRY;
+ }
+
+ static char const *name() { return "wlan"; }
+ char const *type() override { return name(); }
+};
+
+
+/**************************
+ ** VFS plugin interface **
+ **************************/
+
+extern "C" Vfs::File_system_factory *vfs_file_system_factory(void)
+{
+ struct Factory : Vfs::File_system_factory
+ {
+ Vfs::File_system *create(Vfs::Env &vfs_env,
+ Genode::Xml_node node) override
+ {
+ static Vfs::File_system *fs =
+ new (vfs_env.alloc()) Vfs_wlan::File_system(vfs_env, node);
+ return fs;
+ }
+ };
+
+ static Factory factory;
+ return &factory;
+}
diff --git a/repos/pc/src/lib/wifi/dummies.c b/repos/pc/src/lib/wifi/dummies.c
new file mode 100644
index 0000000000..5819fcd0ad
--- /dev/null
+++ b/repos/pc/src/lib/wifi/dummies.c
@@ -0,0 +1,798 @@
+/*
+ * \brief Dummy definitions of Linux Kernel functions - handled manually
+ * \author Josef Soentgen
+ * \date 2022-02-09
+ */
+
+/*
+ * Copyright (C) 2022 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#include
+
+#include
+
+int __cpuhp_setup_state(enum cpuhp_state state,const char * name,bool invoke,int (* startup)(unsigned int cpu),int (* teardown)(unsigned int cpu),bool multi_instance)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+void update_vsyscall(struct timekeeper * tk)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void clocksource_arch_init(struct clocksource * cs)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void ignore_signals(struct task_struct * t)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void calc_global_load(void)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void account_process_tick(struct task_struct * p,int user_tick)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void rcu_sched_clock_irq(int user)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+int sysfs_create_bin_file(struct kobject * kobj,const struct bin_attribute * attr)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+int sysfs_create_file_ns(struct kobject * kobj,const struct attribute * attr,const void * ns)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+int sysfs_create_groups(struct kobject * kobj,const struct attribute_group ** groups)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+int sysfs_create_group(struct kobject * kobj,const struct attribute_group * grp)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+int sysfs_create_link(struct kobject * kobj,struct kobject * target,const char * name)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+void sysfs_remove_link(struct kobject * kobj,const char * name)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void sysfs_remove_file_ns(struct kobject * kobj,const struct attribute * attr,const void * ns)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void sysfs_remove_groups(struct kobject * kobj,const struct attribute_group ** groups)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void sysfs_remove_dir(struct kobject * kobj)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void sysfs_remove_bin_file(struct kobject * kobj,const struct bin_attribute * attr)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void kernfs_get(struct kernfs_node * kn)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void kernfs_put(struct kernfs_node * kn)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+int kobject_uevent(struct kobject * kobj,enum kobject_action action)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+int add_random_ready_callback(struct random_ready_callback * rdy)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+void add_device_randomness(const void * buf,unsigned int size)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void add_interrupt_randomness(int irq,int irq_flags)
+{
+ lx_emul_trace(__func__);
+}
+
+
+extern bool irq_wait_for_poll(struct irq_desc * desc);
+bool irq_wait_for_poll(struct irq_desc * desc)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void note_interrupt(struct irq_desc * desc,irqreturn_t action_ret)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int __register_chrdev(unsigned int major,unsigned int baseminor,unsigned int count,const char * name,const struct file_operations * fops)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+int register_chrdev_region(dev_t from,unsigned count,const char * name)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+extern void register_handler_proc(unsigned int irq,struct irqaction * action);
+void register_handler_proc(unsigned int irq,struct irqaction * action)
+{
+ lx_emul_trace(__func__);
+}
+
+
+extern void register_irq_proc(unsigned int irq,struct irq_desc * desc);
+void register_irq_proc(unsigned int irq,struct irq_desc * desc)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void cdev_init(struct cdev * cdev,const struct file_operations * fops)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+int cdev_add(struct cdev * p,dev_t dev,unsigned count)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+void cdev_del(struct cdev * p)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void register_syscore_ops(struct syscore_ops * ops)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+struct proc_dir_entry { int dummy; };
+
+struct proc_dir_entry * proc_create_seq_private(const char * name,umode_t mode,struct proc_dir_entry * parent,const struct seq_operations * ops,unsigned int state_size,void * data)
+{
+ static struct proc_dir_entry ret;
+ lx_emul_trace(__func__);
+ return &ret;
+}
+
+
+#include
+
+int software_node_notify(struct device * dev,unsigned long action)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+#include
+
+struct user_namespace init_user_ns;
+
+struct uts_namespace init_uts_ns;
+
+
+
+/*
+ * linux/seq_file.h depends on user_namespace being defined, add
+ * all dummies pulling in this header below here
+ */
+
+
+#include
+
+void seq_vprintf(struct seq_file * m,const char * f,va_list args)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void unblank_screen(void)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern void pci_allocate_vc_save_buffers(struct pci_dev * dev);
+void pci_allocate_vc_save_buffers(struct pci_dev * dev)
+{
+ lx_emul_trace(__func__);
+}
+
+
+extern void pci_vpd_init(struct pci_dev * dev);
+void pci_vpd_init(struct pci_dev * dev)
+{
+ lx_emul_trace(__func__);
+}
+
+
+extern int pci_proc_attach_device(struct pci_dev * dev);
+int pci_proc_attach_device(struct pci_dev * dev)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+bool parse_option_str(const char * str,const char * option)
+{
+ lx_emul_trace(__func__);
+ return false;
+}
+
+
+extern bool pat_enabled(void);
+bool pat_enabled(void)
+{
+ // XXX pat_enabled necessary?
+ lx_emul_trace(__func__);
+ return false;
+}
+
+
+#include
+
+bool is_vmalloc_addr(const void * x)
+{
+ lx_emul_trace(__func__);
+ return false;
+}
+
+
+unsigned long init_stack[THREAD_SIZE / sizeof(unsigned long)];
+
+
+extern int pci_dev_specific_acs_enabled(struct pci_dev * dev,u16 acs_flags);
+int pci_dev_specific_acs_enabled(struct pci_dev * dev,u16 acs_flags)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+extern int pci_dev_specific_disable_acs_redir(struct pci_dev * dev);
+int pci_dev_specific_disable_acs_redir(struct pci_dev * dev)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+extern int pci_dev_specific_enable_acs(struct pci_dev * dev);
+int pci_dev_specific_enable_acs(struct pci_dev * dev)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+extern int pci_dev_specific_reset(struct pci_dev * dev,int probe);
+int pci_dev_specific_reset(struct pci_dev * dev,int probe)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+void pci_fixup_device(enum pci_fixup_pass pass,struct pci_dev * dev)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+int pci_disable_link_state(struct pci_dev * pdev,int state)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+const unsigned long module_cert_size = 0;
+const u8 system_certificate_list[] = { };
+const unsigned long system_certificate_list_size = sizeof (system_certificate_list);
+
+const u8 shipped_regdb_certs[] = { };
+unsigned int shipped_regdb_certs_len = sizeof (shipped_regdb_certs);
+
+
+
+/*
+ * Generate_dummies.c will otherwise pull in
+ * that clashes with rcutiny.h.
+ */
+void rcu_barrier(void)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+#include /* for DEFINE_STATIC_KEY_FALSE */
+
+void bpf_prog_change_xdp(struct bpf_prog *prev_prog, struct bpf_prog *prog)
+{
+ lx_emul_trace(__func__);
+}
+
+DEFINE_STATIC_KEY_FALSE(bpf_stats_enabled_key);
+
+
+asmlinkage __wsum csum_partial(const void * buff,int len,__wsum sum)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+struct static_key_false init_on_alloc;
+
+
+#include
+
+int proc_alloc_inum(unsigned int * inum)
+{
+ *inum = 1; /* according to linux/proc_ns.h without CONFIG_PROC_FS */
+ return 0;
+}
+
+
+#include
+
+__init int net_sysctl_init(void)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+struct proc_dir_entry * proc_create_net_data(const char * name,umode_t mode,struct proc_dir_entry * parent,const struct seq_operations * ops,unsigned int state_size,void * data)
+{
+ static struct proc_dir_entry _proc_dir_entry;
+ lx_emul_trace(__func__);
+ return &_proc_dir_entry;
+}
+
+
+#include
+
+unsigned int get_next_ino(void)
+{
+ static unsigned int count = 0;
+ return ++count;
+}
+
+
+#include
+
+int __init dev_proc_init(void)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+unsigned int full_name_hash(const void * salt,const char * name,unsigned int len)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+static struct key _key;
+
+struct key * keyring_alloc(const char * description,kuid_t uid,kgid_t gid,const struct cred * cred,key_perm_t perm,unsigned long flags,struct key_restriction * restrict_link,struct key * dest)
+{
+ lx_emul_trace(__func__);
+ return &_key;
+}
+
+
+#include
+
+int kobject_uevent_env(struct kobject * kobj,enum kobject_action action,char * envp_ext[])
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+void sched_set_fifo(struct task_struct * p)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void kernel_param_lock(struct module * mod)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void kernel_param_unlock(struct module * mod)
+{
+ lx_emul_trace(__func__);
+}
+
+
+unsigned long lpj_fine = 0;
+
+
+#include
+
+void put_pid(struct pid * pid)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+int sk_filter_trim_cap(struct sock * sk,struct sk_buff * skb,unsigned int cap)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+bool file_ns_capable(const struct file * file,struct user_namespace * ns,int cap)
+{
+ lx_emul_trace(__func__);
+ return true;
+}
+
+
+#include
+
+void synchronize_rcu(void)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+void __skb_get_hash(struct sk_buff * skb)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+bool __skb_flow_dissect(const struct net * net,const struct sk_buff * skb,struct flow_dissector * flow_dissector,void * target_container,const void * data,__be16 proto,int nhoff,int hlen,unsigned int flags)
+{
+ lx_emul_trace(__func__);
+ return false;
+}
+
+
+#include
+
+pid_t pid_vnr(struct pid * pid)
+{
+ lx_emul_trace(__func__);
+ return 0;
+}
+
+
+#include
+
+int verify_pkcs7_signature(const void *data, size_t len,
+ const void *raw_pkcs7, size_t pkcs7_len,
+ struct key *trusted_keys,
+ enum key_being_used_for usage,
+ int (*view_content)(void *ctx,
+ const void *data, size_t len,
+ size_t asn1hdrlen),
+ void *ctx)
+{
+ return true;
+}
+
+
+#include
+#include
+#include
+#include
+
+int acpi_device_modalias(struct device *d, char * s, int i)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+int acpi_device_uevent_modalias(struct device *d, struct kobj_uevent_env *k)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+int acpi_dma_configure_id(struct device *dev,
+ enum dev_dma_attr attr,
+ const u32 *input_id)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+bool acpi_driver_match_device(struct device *dev, const struct device_driver *drv)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+union acpi_object *acpi_evaluate_dsm(acpi_handle handle, const guid_t *guid,
+ u64 rev, u64 func, union acpi_object *argv4)
+{
+ return NULL;
+}
+
+
+acpi_status acpi_evaluate_object(acpi_handle handle,
+ acpi_string pathname,
+ struct acpi_object_list *external_params,
+ struct acpi_buffer *return_buffer)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+acpi_status acpi_get_handle(acpi_handle parent,acpi_string pathname,acpi_handle * ret_handle)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+int acpi_platform_notify(struct device *dev, enum kobject_action action)
+{
+ return 0;
+}
+
+
+bool is_acpi_device_node(const struct fwnode_handle *fwnode)
+{
+ return false;
+}
+
+#include
+
+const struct attribute_group pci_dev_acpi_attr_group;
+
+int pci_acpi_program_hp_params(struct pci_dev *dev)
+{
+ return -ENODEV;
+}
+
+
+struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus)
+{
+ return NULL;
+}
+
+
+bool pciehp_is_native(struct pci_dev *bridge)
+{
+ return true;
+}
+
+
+#include
+
+struct thermal_cooling_device *thermal_cooling_device_register(const char *s,
+ void *p, const struct thermal_cooling_device_ops *op)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+
+void thermal_cooling_device_unregister(struct thermal_cooling_device *tcd)
+{
+ lx_emul_trace(__func__);
+}
+
+
+int thermal_zone_device_enable(struct thermal_zone_device *tz)
+{
+ return -ENODEV;
+}
+
+
+struct thermal_zone_device *thermal_zone_device_register(const char *s, int i, int j,
+ void *p, struct thermal_zone_device_ops *ops,
+ struct thermal_zone_params *params, int x, int y)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+
+void thermal_zone_device_unregister(struct thermal_zone_device *tzd)
+{
+ lx_emul_trace(__func__);
+}
+
+
+void thermal_zone_device_update(struct thermal_zone_device *tzd,
+ enum thermal_notify_event e)
+{
+ lx_emul_trace(__func__);
+}
+
+
+#include
+
+int net_ratelimit(void)
+{
+ lx_emul_trace(__func__);
+ /* suppress */
+ return 0;
+}
diff --git a/repos/pc/src/lib/wifi/firmware.cc b/repos/pc/src/lib/wifi/firmware.cc
new file mode 100644
index 0000000000..e454ff7f54
--- /dev/null
+++ b/repos/pc/src/lib/wifi/firmware.cc
@@ -0,0 +1,106 @@
+/*
+ * \brief Linux wireless stack
+ * \author Josef Soentgen
+ * \date 2018-06-29
+ */
+
+/*
+ * Copyright (C) 2018-2022 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+/* Genode includes */
+#include
+#include
+
+/* local includes */
+#include
+#include
+
+
+Firmware_list fw_list[] = {
+ { "regulatory.db", 4144, nullptr },
+ { "regulatory.db.p7s", 1182, nullptr },
+
+ { "iwlwifi-1000-5.ucode", 337520, nullptr },
+ { "iwlwifi-3160-17.ucode", 918268, nullptr },
+ { "iwlwifi-5000-5.ucode", 340696, nullptr },
+ { "iwlwifi-6000-4.ucode", 454608, nullptr },
+ { "iwlwifi-6000-6.ucode", 454608, "iwlwifi-6000-4.ucode" },
+ { "iwlwifi-6000g2a-6.ucode", 677296, nullptr },
+ { "iwlwifi-6000g2b-6.ucode", 679436, nullptr },
+ { "iwlwifi-7260-17.ucode", 1049340, nullptr },
+ { "iwlwifi-7265-16.ucode", 1180412, nullptr },
+ { "iwlwifi-7265D-29.ucode", 1036772, nullptr },
+ { "iwlwifi-8000C-22.ucode", 2120860, nullptr },
+ { "iwlwifi-8000C-36.ucode", 2428004, nullptr },
+ { "iwlwifi-8265-22.ucode", 1811984, nullptr },
+ { "iwlwifi-8265-36.ucode", 2436632, nullptr },
+
+ { "iwlwifi-9000-pu-b0-jf-b0-34.ucode", 2678284, nullptr },
+ { "iwlwifi-9000-pu-b0-jf-b0-36.ucode", 2678284, "iwlwifi-9000-pu-b0-jf-b0-34.ucode" },
+ { "iwlwifi-9000-pu-b0-jf-b0-46.ucode", 1514876, nullptr },
+
+ { "iwlwifi-QuZ-a0-hr-b0-63.ucode", 1334804, nullptr },
+ { "iwlwifi-QuZ-a0-hr-b0-64.ucode", 1334804, "iwlwifi-QuZ-a0-hr-b0-63.ucode" },
+};
+
+
+size_t fw_list_len = sizeof(fw_list) / sizeof(fw_list[0]);
+
+
+/**********************
+ ** linux/firmware.h **
+ **********************/
+
+extern "C" int lx_emul_request_firmware_nowait(const char *name, void **dest, size_t *result)
+{
+ if (!dest || !result)
+ return -1;
+
+ /* only try to load known firmware images */
+ Firmware_list *fwl = 0;
+ for (size_t i = 0; i < fw_list_len; i++) {
+ if (Genode::strcmp(name, fw_list[i].requested_name) == 0) {
+ fwl = &fw_list[i];
+ break;
+ }
+ }
+
+ if (!fwl) {
+ Genode::error("firmware '", name, "' is not in the firmware white list");
+ return -1;
+ }
+
+ char const *fw_name = fwl->available_name
+ ? fwl->available_name : fwl->requested_name;
+ Genode::Rom_connection rom(Lx_kit::env().env, fw_name);
+ Genode::Dataspace_capability ds_cap = rom.dataspace();
+
+ if (!ds_cap.valid()) {
+ Genode::error("could not get firmware ROM dataspace");
+ return -1;
+ }
+
+ /* use allocator because fw is too big for slab */
+ void *data = Lx_kit::env().heap.alloc(fwl->size);
+ if (!data)
+ return -1;
+
+ void const *image = Lx_kit::env().env.rm().attach(ds_cap);
+ Genode::memcpy(data, image, fwl->size);
+ Lx_kit::env().env.rm().detach(image);
+
+ *dest = data;
+ *result = fwl->size;
+
+ return 0;
+}
+
+
+extern "C" void lx_emul_release_firmware(void const *data, size_t size)
+{
+ Lx_kit::env().heap.free(const_cast(data), size);
+}
diff --git a/repos/pc/src/lib/wifi/firmware_list.h b/repos/pc/src/lib/wifi/firmware_list.h
new file mode 100644
index 0000000000..50e960ad58
--- /dev/null
+++ b/repos/pc/src/lib/wifi/firmware_list.h
@@ -0,0 +1,26 @@
+/*
+ * \brief List for firmware images and their sizes
+ * \author Josef Soentgen
+ * \date 2014-03-26
+ */
+
+/*
+ * Copyright (C) 2014-2017 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#ifndef _FIRMWARE_LIST_H_
+#define _FIRMWARE_LIST_H_
+
+typedef __SIZE_TYPE__ size_t;
+
+struct Firmware_list
+{
+ char const *requested_name;
+ size_t size;
+ char const *available_name;
+};
+
+#endif /* _FIRMWARE_LIST_H_ */
diff --git a/repos/pc/src/lib/wifi/generated_dummies.c b/repos/pc/src/lib/wifi/generated_dummies.c
new file mode 100644
index 0000000000..73210cde4a
--- /dev/null
+++ b/repos/pc/src/lib/wifi/generated_dummies.c
@@ -0,0 +1,1346 @@
+/*
+ * \brief Dummy definitions of Linux Kernel functions
+ * \author Automatically generated file - do no edit
+ * \date 2022-02-28
+ */
+
+#include
+
+
+#include
+
+void * PDE_DATA(const struct inode * inode)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int ___ratelimit(struct ratelimit_state * rs,const char * func)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+const char * __clk_get_name(const struct clk * clk)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int __ethtool_get_link_ksettings(struct net_device * dev,struct ethtool_link_ksettings * link_ksettings)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+unsigned long __fdget(unsigned int fd)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+unsigned long __get_free_pages(gfp_t gfp_mask,unsigned int order)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int __get_unused_fd_flags(unsigned flags,unsigned long nofile)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void __gnet_stats_copy_basic(const seqcount_t * running,struct gnet_stats_basic_packed * bstats,struct gnet_stats_basic_cpu __percpu * cpu,struct gnet_stats_basic_packed * b)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void __gnet_stats_copy_queue(struct gnet_stats_queue * qstats,const struct gnet_stats_queue __percpu * cpu,const struct gnet_stats_queue * q,__u32 qlen)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void __put_cred(struct cred * cred)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void __put_task_struct(struct task_struct * tsk)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void __scm_destroy(struct scm_cookie * scm)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int __scm_send(struct socket * sock,struct msghdr * msg,struct scm_cookie * p)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+u32 __skb_get_hash_symmetric(const struct sk_buff * skb)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+u64 __sock_gen_cookie(struct sock * sk)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void __srcu_read_unlock(struct srcu_struct * ssp,int idx)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void * __vmalloc_node(unsigned long size,unsigned long align,gfp_t gfp_mask,int node,const void * caller)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+unsigned long _copy_to_user(void __user * to,const void * from,unsigned long n)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern void ack_bad_irq(unsigned int irq);
+void ack_bad_irq(unsigned int irq)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int add_uevent_var(struct kobj_uevent_env * env,const char * format,...)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct file * alloc_file_pseudo(struct inode * inode,struct vfsmount * mnt,const char * name,int flags,const struct file_operations * fops)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct msi_desc * alloc_msi_entry(struct device * dev,int nvec,const struct irq_affinity_desc * affinity)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void arc4_crypt(struct arc4_ctx * ctx,u8 * out,const u8 * in,unsigned int len)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int arc4_setkey(struct arc4_ctx * ctx,const u8 * in_key,unsigned int key_len)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+async_cookie_t async_schedule_node(async_func_t func,void * data,int node)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void async_synchronize_full(void)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int bpf_prog_create_from_user(struct bpf_prog ** pfp,struct sock_fprog * fprog,bpf_aux_classic_check_t trans,bool save_orig)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void bpf_prog_destroy(struct bpf_prog * fp)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void bpf_warn_invalid_xdp_action(u32 act)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void bust_spinlocks(int yes)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void console_flush_on_panic(enum con_flush_mode mode)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int console_printk[] = {};
+
+
+#include
+
+void console_unblank(void)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int copy_bpf_fprog_from_user(struct sock_fprog * dst,sockptr_t src,int len)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+size_t copy_page_from_iter(struct page * page,size_t offset,size_t bytes,struct iov_iter * i)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int dev_ifconf(struct net * net,struct ifconf * ifc,int size)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int dev_ioctl(struct net * net,unsigned int cmd,struct ifreq * ifr,bool * need_copyout)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+asmlinkage __visible void do_softirq(void)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void dst_release(struct dst_entry * dst)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+char * dynamic_dname(struct dentry * dentry,char * buffer,int buflen,const char * fmt,...)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void emergency_restart(void)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+u32 ethtool_op_get_link(struct net_device * dev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int ethtool_op_get_ts_info(struct net_device * dev,struct ethtool_ts_info * info)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+pid_t f_getown(struct file * filp)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int f_setown(struct file * filp,unsigned long arg,int force)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int fasync_helper(int fd,struct file * filp,int on,struct fasync_struct ** fapp)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void fd_install(unsigned int fd,struct file * file)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct task_struct * find_task_by_vpid(pid_t vnr)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct flow_dissector flow_keys_basic_dissector;
+
+
+#include
+
+void fput(struct file * file)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void free_msi_entry(struct msi_desc * entry)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void free_uid(struct user_struct * up)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct fwnode_handle * fwnode_create_software_node(const struct property_entry * properties,const struct fwnode_handle * parent)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void fwnode_remove_software_node(struct fwnode_handle * fwnode)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+unsigned long gcd(unsigned long a,unsigned long b)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void gen_kill_estimator(struct net_rate_estimator __rcu ** rate_est)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+ssize_t generic_file_splice_read(struct file * in,loff_t * ppos,struct pipe_inode_info * pipe,size_t len,unsigned int flags)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+ssize_t generic_splice_sendpage(struct pipe_inode_info * pipe,struct file * out,loff_t * ppos,size_t len,unsigned int flags)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int get_option(char ** str,int * pint)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+char * get_options(const char * str,int nints,int * ints)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int get_unused_fd_flags(unsigned flags)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+bool gfp_pfmemalloc_allowed(gfp_t gfp_mask)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int gnet_stats_copy_basic(const seqcount_t * running,struct gnet_dump * d,struct gnet_stats_basic_cpu __percpu * cpu,struct gnet_stats_basic_packed * b)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int gnet_stats_copy_queue(struct gnet_dump * d,struct gnet_stats_queue __percpu * cpu_q,struct gnet_stats_queue * q,__u32 qlen)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+const u8 guid_index[16] = {};
+
+
+#include
+
+ssize_t import_iovec(int type,const struct iovec __user * uvec,unsigned nr_segs,unsigned fast_segs,struct iovec ** iovp,struct iov_iter * i)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int import_single_range(int rw,void __user * buf,size_t len,struct iovec * iov,struct iov_iter * i)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+bool initcall_debug;
+
+
+#include
+
+void inode_init_once(struct inode * inode)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void io_schedule_finish(int token)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int io_schedule_prepare(void)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+long __sched io_schedule_timeout(long timeout)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void iov_iter_kvec(struct iov_iter * i,unsigned int direction,const struct kvec * kvec,unsigned long nr_segs,size_t count)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void iov_iter_revert(struct iov_iter * i,size_t unroll)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void iput(struct inode * inode)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void irq_work_tick(void)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+bool is_software_node(const struct fwnode_handle * fwnode)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;
+
+
+#include
+
+struct kobject *kernel_kobj;
+
+
+#include
+
+key_ref_t key_create_or_update(key_ref_t keyring_ref,const char * type,const char * description,const void * payload,size_t plen,key_perm_t perm,unsigned long flags)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void key_put(struct key * key)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void kill_anon_super(struct super_block * sb)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void kill_fasync(struct fasync_struct ** fp,int sig,int band)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int kmem_cache_alloc_bulk(struct kmem_cache * s,gfp_t flags,size_t size,void ** p)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void kmem_cache_destroy(struct kmem_cache * s)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void kmem_cache_free_bulk(struct kmem_cache * s,size_t size,void ** p)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void kmsg_dump(enum kmsg_dump_reason reason)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int kobject_synth_uevent(struct kobject * kobj,const char * buf,size_t count)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+unsigned long long memparse(const char * ptr,char ** retptr)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int msi_domain_alloc_irqs(struct irq_domain * domain,struct device * dev,int nvec)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void msi_domain_free_irqs(struct irq_domain * domain,struct device * dev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+const struct nla_policy nda_policy[] = {};
+
+
+#include
+
+void netdev_rss_key_fill(void * buffer,size_t len)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct irq_chip no_irq_chip;
+
+
+#include
+
+loff_t no_llseek(struct file * file,loff_t offset,int whence)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int open_related_ns(struct ns_common * ns,struct ns_common * (* get_ns)(struct ns_common * ns))
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+enum reboot_mode panic_reboot_mode;
+
+
+#include
+
+void pci_assign_unassigned_bridge_resources(struct pci_dev * bridge)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void pci_assign_unassigned_bus_resources(struct pci_bus * bus)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern unsigned long pci_cardbus_resource_alignment(struct resource * res);
+unsigned long pci_cardbus_resource_alignment(struct resource * res)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+unsigned int pci_flags;
+
+
+extern int pci_idt_bus_quirk(struct pci_bus * bus,int devfn,u32 * l,int timeout);
+int pci_idt_bus_quirk(struct pci_bus * bus,int devfn,u32 * l,int timeout)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int pci_mmap_resource_range(struct pci_dev * pdev,int bar,struct vm_area_struct * vma,enum pci_mmap_state mmap_state,int write_combine)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern void __init pci_realloc_get_opt(char * str);
+void __init pci_realloc_get_opt(char * str)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern void pci_restore_vc_state(struct pci_dev * dev);
+void pci_restore_vc_state(struct pci_dev * dev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern int pci_save_vc_state(struct pci_dev * dev);
+int pci_save_vc_state(struct pci_dev * dev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void pci_stop_and_remove_bus_device(struct pci_dev * dev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void pci_stop_and_remove_bus_device_locked(struct pci_dev * dev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern void pci_vpd_release(struct pci_dev * dev);
+void pci_vpd_release(struct pci_dev * dev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern unsigned int pcibios_assign_all_busses(void);
+unsigned int pcibios_assign_all_busses(void)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern void pcie_aspm_init_link_state(struct pci_dev * pdev);
+void pcie_aspm_init_link_state(struct pci_dev * pdev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern void pcie_aspm_pm_state_change(struct pci_dev * pdev);
+void pcie_aspm_pm_state_change(struct pci_dev * pdev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+extern void pcie_aspm_powersave_config_link(struct pci_dev * pdev);
+void pcie_aspm_powersave_config_link(struct pci_dev * pdev)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int poll_select_set_timeout(struct timespec64 * to,time64_t sec,long nsec)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int printk_deferred(const char * fmt,...)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void printk_safe_flush_on_panic(void)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void proc_free_inum(unsigned int inum)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int put_cmsg(struct msghdr * msg,int level,int type,int len,void * data)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void put_cmsg_scm_timestamping(struct msghdr * msg,struct scm_timestamping_internal * tss_internal)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void put_cmsg_scm_timestamping64(struct msghdr * msg,struct scm_timestamping_internal * tss_internal)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void put_unused_fd(unsigned int fd)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int raw_pci_read(unsigned int domain,unsigned int bus,unsigned int devfn,int reg,int len,u32 * val)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+enum reboot_mode reboot_mode;
+
+
+#include
+
+void remove_proc_entry(const char * name,struct proc_dir_entry * parent)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int reuseport_detach_prog(struct sock * sk)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void reuseport_detach_sock(struct sock * sk)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void scm_detach_fds(struct msghdr * msg,struct scm_cookie * scm)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct hlist_node * seq_hlist_next_rcu(void * v,struct hlist_head * head,loff_t * ppos)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct hlist_node * seq_hlist_start_head_rcu(struct hlist_head * head,loff_t pos)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct list_head * seq_list_next(void * v,struct list_head * head,loff_t * ppos)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct list_head * seq_list_start(struct list_head * head,loff_t pos)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+struct list_head * seq_list_start_head(struct list_head * head,loff_t pos)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void seq_printf(struct seq_file * m,const char * f,...)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void seq_putc(struct seq_file * m,char c)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void seq_puts(struct seq_file * m,const char * s)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void sha224_final(struct sha256_state * sctx,u8 * out)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void sha256_final(struct sha256_state * sctx,u8 * out)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void sha256_update(struct sha256_state * sctx,const u8 * data,unsigned int len)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void show_mem(unsigned int filter,nodemask_t * nodemask)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void show_state_filter(unsigned int state_filter)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int simple_setattr(struct user_namespace * mnt_userns,struct dentry * dentry,struct iattr * iattr)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int simple_statfs(struct dentry * dentry,struct kstatfs * buf)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int sk_attach_bpf(u32 ufd,struct sock * sk)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int sk_attach_filter(struct sock_fprog * fprog,struct sock * sk)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int sk_detach_filter(struct sock * sk)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+void sk_filter_uncharge(struct sock * sk,struct sk_filter * fp)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int sk_get_filter(struct sock * sk,struct sock_filter __user * ubuf,unsigned int len)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int sk_reuseport_attach_bpf(u32 ufd,struct sock * sk)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include
+
+int sk_reuseport_attach_filter(struct sock_fprog * fprog,struct sock * sk)
+{
+ lx_emul_trace_and_stop(__func__);
+}
+
+
+#include