diff --git a/repos/dde_linux/lib/import/import-usb_modem_include.mk b/repos/dde_linux/lib/import/import-usb_modem_include.mk
new file mode 100644
index 0000000000..6515c15ee1
--- /dev/null
+++ b/repos/dde_linux/lib/import/import-usb_modem_include.mk
@@ -0,0 +1,11 @@
+USB_MODEM_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_modem
+
+include $(call select_from_repositories,lib/import/import-usb_arch_include.mk)
+
+#
+# The order of include-search directories is important, we need to look into
+# 'contrib' before falling back to our custom 'lx_emul.h' header.
+#
+INC_DIR += $(ARCH_SRC_INC_DIR)
+INC_DIR += $(USB_MODEM_CONTRIB_DIR)/include
+INC_DIR += $(LIB_CACHE_DIR)/usb_modem_include/include/include/include
diff --git a/repos/dde_linux/lib/mk/usb_modem_include.mk b/repos/dde_linux/lib/mk/usb_modem_include.mk
new file mode 100644
index 0000000000..e36843b855
--- /dev/null
+++ b/repos/dde_linux/lib/mk/usb_modem_include.mk
@@ -0,0 +1,37 @@
+ifeq ($(called_from_lib_mk),yes)
+
+USB_MODEM_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_modem
+LX_EMUL_H := $(REP_DIR)/src/drivers/usb_modem/lx_emul.h
+
+#
+# Determine the header files included by the contrib code. For each
+# of these header files we create a symlink to 'lx_emul.h'.
+#
+SCAN_DIRS := $(addprefix $(USB_MODEM_CONTRIB_DIR)/include/, asm-generic linux uapi net) \
+ $(addprefix $(USB_MODEM_CONTRIB_DIR)/, drivers net)
+GEN_INCLUDES := $(shell grep -rIh "^\#include .*\/" $(SCAN_DIRS) |\
+ sed "s/^\#include [^<\"]*[<\"]\([^>\"]*\)[>\"].*/\1/" |\
+ sort | uniq)
+#
+# Filter out original Linux headers that exist in the contrib directory
+#
+NO_GEN_INCLUDES := $(shell cd $(USB_MODEM_CONTRIB_DIR)/; find include -name "*.h" |\
+ sed "s/.\///" | sed "s/.*include\///")
+GEN_INCLUDES := $(filter-out $(NO_GEN_INCLUDES),$(GEN_INCLUDES))
+
+#
+# Put Linux headers in 'GEN_INC' dir, since some include use "../../" paths use
+# three level include hierarchy
+#
+GEN_INC := $(shell pwd)/include/include/include
+GEN_INCLUDES := $(addprefix $(GEN_INC)/,$(GEN_INCLUDES))
+
+all: $(GEN_INCLUDES)
+
+$(GEN_INCLUDES):
+ $(VERBOSE)mkdir -p $(dir $@)
+ $(VERBOSE)ln -s $(LX_EMUL_H) $@
+
+endif
+
+CC_CXX_WARN_STRICT =
diff --git a/repos/dde_linux/patches/usb_modem_skbuff_cast.patch b/repos/dde_linux/patches/usb_modem_skbuff_cast.patch
new file mode 100644
index 0000000000..5e63596db9
--- /dev/null
+++ b/repos/dde_linux/patches/usb_modem_skbuff_cast.patch
@@ -0,0 +1,13 @@
+diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
+index 9147f9f..c63ee5c 100644
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -3281,7 +3281,7 @@ static inline void skb_remcsum_process(struct sk_buff *skb, void *ptr,
+
+ if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE)) {
+ __skb_checksum_complete(skb);
+- skb_postpull_rcsum(skb, skb->data, ptr - (void *)skb->data);
++ skb_postpull_rcsum(skb, skb->data, (unsigned char*)ptr - skb->data);
+ }
+
+ delta = remcsum_adjust(ptr, skb->csum, start, offset);
diff --git a/repos/dde_linux/ports/dde_linux.hash b/repos/dde_linux/ports/dde_linux.hash
index 14e347bed8..38d42a3c8a 100644
--- a/repos/dde_linux/ports/dde_linux.hash
+++ b/repos/dde_linux/ports/dde_linux.hash
@@ -1 +1 @@
-1753147f7ebf2d67447c6175687cf031605031b3
+17ca56af6e9fde7df9b2ab6bdaa8f9b65c3bc533
diff --git a/repos/dde_linux/ports/dde_linux.port b/repos/dde_linux/ports/dde_linux.port
index 75210c15cd..ebeb65b0a7 100644
--- a/repos/dde_linux/ports/dde_linux.port
+++ b/repos/dde_linux/ports/dde_linux.port
@@ -3,7 +3,7 @@ VERSION := 2
DOWNLOADS := dwc_otg.git usb.archive intel_fb.archive lxip.archive \
wifi.archive fec.archive libnl.archive wpa_supplicant.git \
fw.archive usb_host.archive dwc_otg_host.git usb_hid.archive \
- usb_net.archive imx8_fb.archive
+ usb_modem.archive usb_net.archive imx8_fb.archive
#
# Tools
@@ -60,6 +60,14 @@ DIR(usb_net) := $(SRC_DIR_USB_NET)
TAR_OPT(usb_net) := --strip-components=1 --files-from - < <(sed 's/-x.x.x/-$(VERSION_USB_NET)/g' $(REP_DIR)/usb_net.list)
HASH_INPUT += $(REP_DIR)/usb_net.list
+SRC_DIR_USB_MODEM := src/drivers/usb_modem
+VERSION_USB_MODEM := 4.16.3
+URL(usb_modem) := https://www.kernel.org/pub/linux/kernel/v4.x/linux-$(VERSION_USB_MODEM).tar.xz
+SHA(usb_modem) := 0d6971a81da97e38b974c5eba31a74803bfe41aabc46d406c3acda56306c81a3
+DIR(usb_modem) := $(SRC_DIR_USB_MODEM)
+TAR_OPT(usb_modem) := --strip-components=1 --files-from - < <(sed 's/-x.x.x/-$(VERSION_USB_NET)/g' $(REP_DIR)/usb_modem.list)
+HASH_INPUT += $(REP_DIR)/usb_modem.list
+
#
# Raspberry Pi USB controller
#
@@ -158,6 +166,7 @@ PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/lxip*.pat
PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/intel*.patch)))
PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/usb_host*.patch)))
PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/usb*.patch)))
+PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/usb_modem*.patch)))
PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/intel*.patch)))
PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/fec_*.patch)))
PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/usb_hid*.patch)))
@@ -211,6 +220,10 @@ PATCH_OPT(patches/usb_hid_evdev.patch) := $(USB_HID_OPT)
USB_NET_OPT = -p1 -d$(SRC_DIR_USB_NET)
PATCH_OPT(patches/usb_net_skbuff_cast.patch) := $(USB_NET_OPT)
+# USB MODEM
+USB_MODEM_OPT = -p1 -d$(SRC_DIR_USB_MODEM)
+PATCH_OPT(patches/usb_modem_skbuff_cast.patch) := $(USB_MODEM_OPT)
+
# INTEL FB
PATCH_OPT(patches/intel_fb_backlight.patch) := -p1 -d$(SRC_DIR_INTEL_FB)
PATCH_OPT(patches/intel_fb_drm.patch) := -p1 -d$(SRC_DIR_INTEL_FB)
diff --git a/repos/dde_linux/src/drivers/usb_modem/README b/repos/dde_linux/src/drivers/usb_modem/README
new file mode 100644
index 0000000000..81e7a6b950
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/README
@@ -0,0 +1,30 @@
+USB GSM modem driver
+####################
+
+Driver for GSM modem cards connected via USB/M.2
+
+Currently there is support for MBIM (Mobile Broadband Interface Model) devices
+only. QMI devices might be supported in the future.
+
+Tested devices:
+Huawai ME906s (P/V: 12d1:15c1)
+
+Other devices can be added by enabling the MBIM configuration profile for the
+USB device in ' Driver::Device::set_config' (main.cc) of this driver.
+
+Configuration snippet:
+
+!
+!
+!
+!
+!
+!
+!
+!
+
+The driver offers two Genode sessions: The first one is a terminal session where
+raw packet data, for example, MBIM packets can be send to the device via CDC WDM
+(wireless communication device class). An example can be found in Genode World
+(_src/app/mbimcli_). Once a data connection is established via the Terminal
+session, the NIC session can be used to transfer ethernet frames.
diff --git a/repos/dde_linux/src/drivers/usb_modem/component.cc b/repos/dde_linux/src/drivers/usb_modem/component.cc
new file mode 100644
index 0000000000..16d3162007
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/component.cc
@@ -0,0 +1,183 @@
+/*
+ * \brief Ethernet driver component
+ * \author Stefan Kalkowski
+ * \author Sebastian Sumpf
+ * \date 2017-11-01
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#include
+#include
+
+extern "C" {
+#include
+};
+
+void Session_component::_run_rx_task(void * args)
+{
+ Rx_data *data = static_cast(args);
+
+ while (1) {
+ Lx::scheduler().current()->block_and_schedule();
+
+ int ret = 0;
+ struct napi_struct * n = data->napi;
+
+ for (;;) {
+
+ /* This NAPI_STATE_SCHED test is for avoiding a race
+ * with netpoll's poll_napi(). Only the entity which
+ * obtains the lock and sees NAPI_STATE_SCHED set will
+ * actually make the ->poll() call. Therefore we avoid
+ * accidentally calling ->poll() when NAPI is not scheduled.
+ */
+ if (!test_bit(NAPI_STATE_SCHED, &n->state)) break;
+
+ int weight = n->weight;
+ int work = n->poll(n, weight);
+ ret += work;
+
+ if (work < weight) break;
+
+ Genode::warning("Too much incoming traffic, we should schedule RX more intelligent");
+ }
+ }
+}
+
+
+void Session_component::_run_tx_task(void * args)
+{
+ Tx_data *data = static_cast(args);
+
+ while (1) {
+ Lx::scheduler().current()->block_and_schedule();
+
+ net_device * ndev = data->ndev;
+ struct sk_buff * skb = data->skb;
+
+ ndev->netdev_ops->ndo_start_xmit(skb, ndev);
+ }
+}
+
+
+bool Session_component::_send()
+{
+ using namespace Genode;
+
+ /*
+ * We must not be called from another task, just from the
+ * packet stream dispatcher.
+ */
+ if (Lx::scheduler().active()) {
+ warning("scheduler active");
+ return false;
+ }
+
+ if (!_tx.sink()->ready_to_ack()) { return false; }
+ if (!_tx.sink()->packet_avail()) { return false; }
+
+ Packet_descriptor packet = _tx.sink()->get_packet();
+ if (!packet.size() || !_tx.sink()->packet_valid(packet)) {
+ warning("invalid tx packet");
+ return true;
+ }
+
+ enum { HEAD_ROOM = 8 };
+
+ struct sk_buff *skb = lxc_alloc_skb(packet.size() + HEAD_ROOM, HEAD_ROOM);
+
+ unsigned char *data = lxc_skb_put(skb, packet.size());
+ Genode::memcpy(data, _tx.sink()->packet_content(packet), packet.size());
+
+ _tx_data.ndev = _ndev;
+ _tx_data.skb = skb;
+
+ _tx_task.unblock();
+ Lx::scheduler().schedule();
+
+ _tx.sink()->acknowledge_packet(packet);
+
+ return true;
+}
+
+
+void Session_component::_handle_rx()
+{
+ while (_rx.source()->ack_avail())
+ _rx.source()->release_packet(_rx.source()->get_acked_packet());
+}
+
+
+void Session_component::_handle_packet_stream()
+{
+ _handle_rx();
+
+ while (_send()) continue;
+}
+
+
+void Session_component::unblock_rx_task(napi_struct * n)
+{
+ _rx_data.napi = n;
+ _rx_task.unblock();
+}
+
+
+Nic::Mac_address Session_component::mac_address()
+{
+ return _ndev ? Nic::Mac_address(_ndev->dev_addr) : Nic::Mac_address();
+}
+
+
+void Session_component::receive(struct sk_buff *skb)
+{
+ _handle_rx();
+
+ if (!_rx.source()->ready_to_submit()) {
+ Genode::warning("not ready to receive packet");
+ return;
+ }
+
+ Skb s = skb_helper(skb);
+
+ try {
+ Nic::Packet_descriptor p = _rx.source()->alloc_packet(s.packet_size + s.frag_size);
+ void *buffer = _rx.source()->packet_content(p);
+ memcpy(buffer, s.packet, s.packet_size);
+
+ if (s.frag_size)
+ memcpy((char *)buffer + s.packet_size, s.frag, s.frag_size);
+
+ _rx.source()->submit_packet(p);
+ } catch (...) {
+ Genode::warning("failed to process received packet");
+ }
+}
+
+
+void Session_component::link_state(bool link)
+{
+ if (link == _has_link) return;
+
+ _has_link = link;
+ _link_state_changed();
+}
+
+
+Session_component::Session_component(Genode::size_t const tx_buf_size,
+ Genode::size_t const rx_buf_size,
+ Genode::Allocator & rx_block_md_alloc,
+ Genode::Env & env,
+ Genode::Session_label label)
+: Nic::Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, rx_block_md_alloc, env),
+ _ndev(_register_session_component(*this, label)),
+ _has_link(_ndev ? !(_ndev->state & (1UL << __LINK_STATE_NOCARRIER)) : false)
+{
+ if (!_ndev) throw Genode::Service_denied();
+}
diff --git a/repos/dde_linux/src/drivers/usb_modem/component.h b/repos/dde_linux/src/drivers/usb_modem/component.h
new file mode 100644
index 0000000000..5b5224a8fa
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/component.h
@@ -0,0 +1,127 @@
+/*
+ * \brief Freescale ethernet driver component
+ * \author Stefan Kalkowski
+ * \author Sebastian Sumpf
+ * \date 2017-11-01
+ */
+
+/*
+ * Copyright (C) 2017 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#ifndef _SRC__DRIVERS__NIC__FEC__COMPONENT_H_
+#define _SRC__DRIVERS__NIC__FEC__COMPONENT_H_
+
+/* Genode includes */
+#include
+#include
+
+#include
+
+extern "C" {
+ struct net_device;
+ struct napi_struct;
+ struct sk_buff;
+}
+
+class Session_component : public Nic::Session_component
+{
+ private:
+
+ struct Tx_data
+ {
+ net_device * ndev;
+ sk_buff * skb;
+ };
+
+ struct Rx_data
+ {
+ struct napi_struct * napi;
+ };
+
+ net_device * _ndev = nullptr;
+ bool _has_link = false;
+ Tx_data _tx_data;
+ Rx_data _rx_data;
+
+ static void _run_tx_task(void * args);
+ static void _run_rx_task(void * args);
+ static net_device * _register_session_component(Session_component &,
+ Genode::Session_label);
+
+ Lx::Task _tx_task { _run_tx_task, &_tx_data, "tx_task",
+ Lx::Task::PRIORITY_1, Lx::scheduler() };
+ Lx::Task _rx_task { _run_rx_task, &_rx_data, "rx_task",
+ Lx::Task::PRIORITY_1, Lx::scheduler() };
+
+ bool _send();
+ void _handle_rx();
+ void _handle_packet_stream() override;
+
+ public:
+
+ Session_component(Genode::size_t const tx_buf_size,
+ Genode::size_t const rx_buf_size,
+ Genode::Allocator & rx_block_md_alloc,
+ Genode::Env & env,
+ Genode::Session_label label);
+
+ Nic::Mac_address mac_address() override;
+ bool link_state() override { return _has_link; }
+ void link_state(bool link);
+ void receive(struct sk_buff *skb);
+ void unblock_rx_task(napi_struct * n);
+};
+
+
+class Root : public Genode::Root_component
+{
+ private:
+
+ Genode::Env &_env;
+ Genode::Allocator &_md_alloc;
+
+ protected:
+
+ Session_component *_create_session(const char *args)
+ {
+ using namespace Genode;
+
+ Session_label const label = label_from_args(args);
+
+ size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
+ size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
+ size_t rx_buf_size = Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
+
+ /* deplete ram quota by the memory needed for the session structure */
+ size_t session_size = max(4096UL, (unsigned long)sizeof(Session_component));
+
+ /*
+ * Check if donated ram quota suffices for both communication
+ * buffers and check for overflow
+ */
+ if ((ram_quota < session_size) ||
+ (tx_buf_size + rx_buf_size < tx_buf_size ||
+ tx_buf_size + rx_buf_size > ram_quota - session_size)) {
+ Genode::error("insufficient 'ram_quota', got ", ram_quota, ", "
+ "need ", tx_buf_size + rx_buf_size + session_size);
+ throw Genode::Insufficient_ram_quota();
+ }
+
+ return new (Root::md_alloc())
+ Session_component(tx_buf_size, rx_buf_size,
+ _md_alloc, _env, label);
+ }
+
+ public:
+
+ Root(Genode::Env &env, Genode::Allocator &md_alloc)
+ : Genode::Root_component(&env.ep().rpc_ep(), &md_alloc),
+ _env(env), _md_alloc(md_alloc)
+ { }
+};
+
+#endif /* _SRC__DRIVERS__NIC__FEC__COMPONENT_H_ */
diff --git a/repos/dde_linux/src/drivers/usb_modem/driver.h b/repos/dde_linux/src/drivers/usb_modem/driver.h
new file mode 100644
index 0000000000..4eae5d16aa
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/driver.h
@@ -0,0 +1,163 @@
+/*
+ * \brief USB net driver
+ * \author Stefan Kalkowski
+ * \author Sebastian Sumpf
+ * \date 2018-06-27
+ */
+
+/*
+ * Copyright (C) 2018-2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#ifndef _SRC__DRIVERS__USB_MODEM__DRIVER_H_
+#define _SRC__DRIVERS__USB_MODEM__DRIVER_H_
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+struct usb_device_id;
+struct usb_interface;
+struct usb_device;
+
+struct Driver
+{
+ using Label = Genode::String<64>;
+
+ struct Task
+ {
+ Lx::Task task;
+ Genode::Signal_handler handler;
+
+ void handle_signal()
+ {
+ task.unblock();
+ Lx::scheduler().schedule();
+ }
+
+ template
+ Task(Genode::Entrypoint & ep, ARGS &&... args)
+ : task(args...), handler(ep, *this, &Task::handle_signal) {}
+ };
+
+ struct Device
+ {
+ using Le = Genode::List_element;
+
+ Le le { this };
+ Label label;
+ Driver &driver;
+ Genode::Env &env;
+ Genode::Allocator_avl &alloc;
+ Task state_task;
+ Task urb_task;
+ Usb::Connection usb { env, &alloc, label.string(),
+ 512 * 1024, state_task.handler };
+ usb_device * udev = nullptr;
+ bool updated = true;
+
+ Device(Driver & drv, Label label);
+ ~Device();
+
+ static void state_task_entry(void *);
+ static void urb_task_entry(void *);
+ void register_device();
+ void unregister_device();
+ void scan_interfaces(unsigned iface_idx);
+ void scan_altsettings(usb_interface * iface,
+ unsigned iface_idx, unsigned alt_idx);
+ void probe_interface(usb_interface *, usb_device_id *);
+ void remove_interface(usb_interface *);
+ void set_config(Usb::Device_descriptor const &desc);
+ };
+
+ struct Devices : Genode::List
+ {
+ template
+ void for_each(FN const & fn)
+ {
+ Device::Le * cur = first();
+ for (Device::Le * next = cur ? cur->next() : nullptr; cur;
+ cur = next, next = cur ? cur->next() : nullptr)
+ fn(*cur->object());
+ }
+ };
+
+ class Sync_packet : public Usb::Completion
+ {
+ private:
+
+ Usb::Session_client & _usb;
+ Usb::Packet_descriptor _packet { _usb.source()->alloc_packet(0) };
+ completion _comp;
+
+ public:
+
+ Sync_packet(Usb::Session_client &usb) : _usb(usb)
+ {
+ init_completion(&_comp);
+ }
+
+ virtual ~Sync_packet()
+ {
+ _usb.source()->release_packet(_packet);
+ }
+
+ void complete(Usb::Packet_descriptor &p) override
+ {
+ ::complete(&_comp);
+ }
+
+ void send()
+ {
+ _packet.completion = this;
+ _usb.source()->submit_packet(_packet);
+ wait_for_completion(&_comp);
+ }
+
+ void config(int configuration)
+ {
+ _packet.type = Usb::Packet_descriptor::CONFIG;
+ _packet.number = configuration;
+ send();
+ }
+
+ void alt_setting(int interface, int alt_setting)
+ {
+ _packet.type = Usb::Packet_descriptor::ALT_SETTING;
+ _packet.interface.number = interface;
+ _packet.interface.alt_setting = alt_setting;
+ send();
+ }
+ };
+
+
+ Devices devices;
+ Genode::Env &env;
+ Genode::Entrypoint &ep { env.ep() };
+ Genode::Heap heap { env.ram(), env.rm() };
+ Genode::Allocator_avl alloc { &heap };
+ Root root { env, heap };
+ Terminal::Root terminal_root { env, heap };
+ Genode::Constructible main_task;
+ Genode::Constructible report_rom;
+
+ Driver(Genode::Env &env);
+
+ static void main_task_entry(void *);
+};
+
+#endif /* _SRC__DRIVERS__USB_HID__DRIVER_H_ */
diff --git a/repos/dde_linux/src/drivers/usb_modem/dummies.c b/repos/dde_linux/src/drivers/usb_modem/dummies.c
new file mode 100644
index 0000000000..ac662901bf
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/dummies.c
@@ -0,0 +1,624 @@
+/*
+ * \brief Dummies
+ * \author Stefan Kalkowski
+ * \author Sebastian Sumpf
+ * \date 2020-12-02
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#include
+
+#if 0
+#define TRACE \
+ do { \
+ lx_printf("%s not implemented from %p\n", __func__, __builtin_return_address(0)); \
+ } while (0)
+#else
+#define TRACE do { ; } while (0)
+#endif
+
+#define TRACE_AND_STOP \
+ do { \
+ lx_printf("%s not implemented from %p\n", __func__, __builtin_return_address(0)); \
+ BUG(); \
+ } while (0)
+
+struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)
+{
+ TRACE_AND_STOP;
+ return NULL;
+}
+
+u16 bitrev16(u16 in)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+u16 crc16(u16 crc, const u8 *buffer, size_t len)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+__sum16 csum_fold(__wsum csum)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+__wsum csum_partial(const void *buff, int len, __wsum sum)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void dev_hold(struct net_device *dev)
+{
+ TRACE_AND_STOP;
+}
+
+void dev_put(struct net_device *dev)
+{
+ TRACE_AND_STOP;
+}
+
+const char *dev_name(const struct device *dev)
+{
+ TRACE;
+ return NULL;
+}
+
+int device_set_wakeup_enable(struct device *dev, bool enable)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+
+void dst_release(struct dst_entry *dst)
+{
+ TRACE_AND_STOP;
+}
+
+struct net_device;
+u32 ethtool_op_get_link(struct net_device *dev)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *eti)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void free_netdev(struct net_device * ndev)
+{
+ TRACE_AND_STOP;
+}
+
+void free_uid(struct user_struct * user)
+{
+ TRACE_AND_STOP;
+}
+
+struct mii_if_info;
+int generic_mii_ioctl(struct mii_if_info *mii_if, struct mii_ioctl_data *mii_data, int cmd, unsigned int *duplex_changed)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void get_page(struct page *page)
+{
+ TRACE_AND_STOP;
+}
+
+bool gfpflags_allow_blocking(const gfp_t gfp_flags)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+bool gfp_pfmemalloc_allowed(gfp_t gfp_flags)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+int hex2bin(u8 *dst, const char *src, size_t count)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+int in_irq()
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void *kmap_atomic(struct page *page)
+{
+ TRACE_AND_STOP;
+ return NULL;
+}
+
+int memcmp(const void *s1, const void *s2, size_t size)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void mii_ethtool_get_link_ksettings( struct mii_if_info *mii, struct ethtool_link_ksettings *cmd)
+{
+ TRACE_AND_STOP;
+}
+
+int mii_ethtool_set_link_ksettings( struct mii_if_info *mii, const struct ethtool_link_ksettings *cmd)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+unsigned netdev_mc_count(struct net_device * dev)
+{
+ TRACE;
+ return 1;
+}
+
+int netdev_mc_empty(struct net_device * ndev)
+{
+ TRACE;
+ return 1;
+}
+
+void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, const struct net_device_stats *netdev_stats)
+{
+ TRACE_AND_STOP;
+}
+
+bool netdev_uses_dsa(struct net_device *dev)
+{
+ TRACE;
+ return false;
+}
+
+void netif_device_attach(struct net_device *dev)
+{
+ TRACE;
+}
+
+void netif_device_detach(struct net_device *dev)
+{
+ TRACE_AND_STOP;
+}
+
+int netif_device_present(struct net_device * d)
+{
+ TRACE;
+ return 1;
+}
+
+u32 netif_msg_init(int arg0, int arg1)
+{
+ TRACE;
+ return 0;
+}
+
+void netif_start_queue(struct net_device *dev)
+{
+ TRACE;
+}
+
+void netif_stop_queue(struct net_device *dev)
+{
+ TRACE_AND_STOP;
+}
+
+void netif_trans_update(struct net_device *dev)
+{
+ TRACE;
+}
+
+void netif_tx_wake_all_queues(struct net_device *dev)
+{
+ TRACE_AND_STOP;
+}
+
+void netif_wake_queue(struct net_device * d)
+{
+ TRACE;
+}
+
+void netif_tx_lock_bh(struct net_device *dev)
+{
+ TRACE;
+}
+
+void netif_tx_unlock_bh(struct net_device *dev)
+{
+ TRACE;
+}
+
+const void *of_get_mac_address(struct device_node *np)
+{
+ TRACE;
+ return NULL;
+}
+
+void pm_runtime_enable(struct device *dev)
+{
+ TRACE;
+}
+
+void read_lock_bh(rwlock_t * l)
+{
+ TRACE_AND_STOP;
+}
+
+void read_unlock_bh(rwlock_t * l)
+{
+ TRACE_AND_STOP;
+}
+
+void unregister_netdev(struct net_device * dev)
+{
+ TRACE_AND_STOP;
+}
+
+void secpath_reset(struct sk_buff * skb)
+{
+ TRACE;
+}
+
+void __set_current_state(int state)
+{
+ TRACE_AND_STOP;
+}
+
+void sg_init_table(struct scatterlist *sg, unsigned int arg)
+{
+ TRACE_AND_STOP;
+}
+
+void sg_set_buf(struct scatterlist *sg, const void *buf, unsigned int buflen)
+{
+ TRACE_AND_STOP;
+}
+
+void sg_set_page(struct scatterlist *sg, struct page *page, unsigned int len, unsigned int offset)
+{
+ TRACE_AND_STOP;
+}
+
+void sk_free(struct sock *sk)
+{
+ TRACE_AND_STOP;
+}
+
+void sock_efree(struct sk_buff *skb)
+{
+ TRACE_AND_STOP;
+}
+
+void spin_lock(spinlock_t *lock)
+{
+ TRACE;
+}
+
+void spin_lock_irq(spinlock_t *lock)
+{
+ TRACE;
+}
+
+void spin_lock_nested(spinlock_t *lock, int subclass)
+{
+ TRACE;
+}
+
+void spin_unlock_irq(spinlock_t *lock)
+{
+ TRACE;
+}
+
+void spin_lock_bh(spinlock_t *lock)
+{
+ TRACE;
+}
+
+void spin_unlock_bh(spinlock_t *lock)
+{
+ TRACE;
+}
+
+size_t strlcpy(char *dest, const char *src, size_t size)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void tasklet_kill(struct tasklet_struct *t)
+{
+ TRACE_AND_STOP;
+}
+
+void trace_consume_skb(struct sk_buff *skb)
+{
+ TRACE;
+}
+
+void trace_kfree_skb(struct sk_buff *skb, void *arg)
+{
+ TRACE;
+}
+
+unsigned int u64_stats_fetch_begin_irq(const struct u64_stats_sync *p)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+bool u64_stats_fetch_retry_irq(const struct u64_stats_sync *p, unsigned int s)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+struct usb_device;
+int usb_clear_halt(struct usb_device *dev, int pipe)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+struct usb_driver;
+struct usb_interface;
+void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface)
+{
+ TRACE_AND_STOP;
+}
+
+struct usb_anchor;
+struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
+{
+ TRACE_AND_STOP;
+ return NULL;
+}
+
+struct urb *usb_get_urb(struct urb *urb)
+{
+ TRACE_AND_STOP;
+ return NULL;
+}
+
+int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
+{
+ TRACE_AND_STOP;
+}
+
+int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+const struct usb_device_id *usb_match_id(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ TRACE_AND_STOP;
+ return NULL;
+}
+
+struct usb_host_interface *usb_altnum_to_altsetting(
+ const struct usb_interface *intf, unsigned int altnum)
+{
+ TRACE_AND_STOP;
+ return NULL;
+}
+
+ktime_t ktime_get_real(void)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void kunmap_atomic(void *addr)
+{
+ TRACE_AND_STOP;
+}
+
+int kstrtoul(const char *s, unsigned int base, unsigned long *res)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void might_sleep()
+{
+ TRACE_AND_STOP;
+}
+
+bool page_is_pfmemalloc(struct page *page)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void put_page(struct page *page)
+{
+ TRACE_AND_STOP;
+}
+
+void usb_kill_urb(struct urb *urb)
+{
+ TRACE_AND_STOP;
+}
+
+int usb_unlink_urb(struct urb *urb)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+void usleep_range(unsigned long min, unsigned long max)
+{
+ TRACE;
+}
+
+void phy_stop(struct phy_device *phydev)
+{
+ TRACE;
+}
+
+void phy_disconnect(struct phy_device *phydev)
+{
+ TRACE;
+}
+
+void phy_print_status(struct phy_device *phydev)
+{
+ TRACE;
+}
+
+int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
+{
+ TRACE;
+ return 0;
+}
+
+typedef int phy_interface_t;
+struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, void (*handler)(struct net_device *), phy_interface_t interface)
+{
+ TRACE;
+ return 0;
+}
+
+int genphy_resume(struct phy_device *phydev) { TRACE; return 0; }
+
+void phy_start(struct phy_device *phydev) { TRACE; }
+
+struct mii_bus;
+void mdiobus_free(struct mii_bus *bus) { TRACE; }
+
+void mdiobus_unregister(struct mii_bus *bus) { TRACE; }
+
+struct mii_bus *mdiobus_alloc_size(size_t size)
+{
+ TRACE_AND_STOP;
+ return NULL;
+}
+
+int __mdiobus_register(struct mii_bus *bus, struct module *owner)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+int phy_ethtool_get_link_ksettings(struct net_device *ndev, struct ethtool_link_ksettings *cmd)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+int phy_ethtool_set_link_ksettings(struct net_device *ndev, const struct ethtool_link_ksettings *cmd)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+int phy_ethtool_nway_reset(struct net_device *ndev)
+{
+ TRACE_AND_STOP;
+ return -1;
+}
+
+int __vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci)
+{
+ TRACE;
+ return -1;
+}
+
+void __vlan_hwaccel_put_tag(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci)
+{
+ TRACE;
+}
+
+struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb)
+{
+ TRACE_AND_STOP;
+ return NULL;
+}
+
+
+struct net_device *
+__vlan_find_dev_deep_rcu(struct net_device *real_dev, __be16 vlan_proto, u16 vlan_id)
+{
+ TRACE_AND_STOP;
+ return NULL;
+}
+
+bool ipv6_addr_is_solict_mult(const struct in6_addr *addr)
+{
+ TRACE;
+ return false;
+}
+
+int ipv6_addr_type(const struct in6_addr *addr)
+{
+ TRACE;
+ return -1;
+}
+
+struct inet6_dev *in6_dev_get(const struct net_device *dev)
+{
+ TRACE_AND_STOP;
+ return NULL;
+}
+
+void in6_dev_put(struct inet6_dev *idev)
+{
+ TRACE_AND_STOP;
+}
+
+
+struct usb_class_driver;
+void usb_deregister_dev(struct usb_interface *intf,struct usb_class_driver *class_driver)
+{
+ TRACE_AND_STOP;
+}
+
+void poll_wait(struct file *f, wait_queue_head_t *w, poll_table *p)
+{
+ TRACE_AND_STOP;
+}
+
+loff_t noop_llseek(struct file *file, loff_t offset, int whence)
+{
+ TRACE_AND_STOP;
+ return 0;
+}
+
+int sysctl_tstamp_allow_data;
+struct user_namespace init_user_ns;
+const struct ipv6_stub *ipv6_stub;
diff --git a/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc b/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc
new file mode 100644
index 0000000000..b9aeba5438
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc
@@ -0,0 +1,654 @@
+/*
+ * \brief Linux emulation
+ * \author Stefan Kalkowski
+ * \author Sebastian Sumpf
+ * \date 2020-12-02
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+#define TRACE do { ; } while (0)
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+
+static int usb_match_device(struct usb_device *dev,
+ const struct usb_device_id *id)
+{
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+ id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
+ id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
+ (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
+ (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+ (id->bDeviceClass != dev->descriptor.bDeviceClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
+ (id->bDeviceSubClass != dev->descriptor.bDeviceSubClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
+ (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
+ return 0;
+
+ return 1;
+}
+
+
+static int usb_match_one_id_intf(struct usb_device *dev,
+ struct usb_host_interface *intf,
+ const struct usb_device_id *id)
+{
+ if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
+ !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+ (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS |
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL |
+ USB_DEVICE_ID_MATCH_INT_NUMBER)))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
+ (id->bInterfaceClass != intf->desc.bInterfaceClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
+ (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
+ (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&
+ (id->bInterfaceNumber != intf->desc.bInterfaceNumber))
+ return 0;
+
+ return 1;
+}
+
+
+int usb_match_one_id(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *intf;
+ struct usb_device *dev;
+
+ if (id == NULL)
+ return 0;
+
+ intf = interface->cur_altsetting;
+ dev = interface_to_usbdev(interface);
+
+ if (!usb_match_device(dev, id))
+ return 0;
+
+ return usb_match_one_id_intf(dev, intf, id);
+}
+
+#include
+
+
+class Addr_to_page_mapping : public Genode::List::Element
+{
+ private:
+
+ struct page *_page { nullptr };
+
+ static Genode::List & _list()
+ {
+ static Genode::List _l;
+ return _l;
+ }
+
+ public:
+
+ Addr_to_page_mapping(struct page *page)
+ : _page(page) { }
+
+ static void insert(struct page * page)
+ {
+ Addr_to_page_mapping *m = (Addr_to_page_mapping*)
+ Lx::Malloc::mem().alloc(sizeof (Addr_to_page_mapping));
+ m->_page = page;
+ _list().insert(m);
+ }
+
+ static struct page * remove(unsigned long addr)
+ {
+ for (Addr_to_page_mapping *m = _list().first(); m; m = m->next())
+ if ((unsigned long)m->_page->addr == addr) {
+ struct page * ret = m->_page;
+ _list().remove(m);
+ Lx::Malloc::mem().free(m);
+ return ret;
+ }
+
+ return nullptr;
+ }
+};
+
+struct Lx_driver
+{
+ using Element = Genode::List_element;
+ using List = Genode::List;
+
+ usb_driver & drv;
+ Element le { this };
+
+ Lx_driver(usb_driver & drv) : drv(drv) { list().insert(&le); }
+
+ usb_device_id * match(usb_interface * iface)
+ {
+ struct usb_device_id * id = const_cast(drv.id_table);
+ for (; id->idVendor || id->idProduct || id->bDeviceClass ||
+ id->bInterfaceClass || id->driver_info; id++)
+ if (usb_match_one_id(iface, id)) return id;
+ return nullptr;
+ }
+
+ int probe(usb_interface * iface, usb_device_id * id)
+ {
+ iface->dev.driver = &drv.drvwrap.driver;
+ if (drv.probe) return drv.probe(iface, id);
+ return 0;
+ }
+
+ static List & list()
+ {
+ static List _list;
+ return _list;
+ }
+};
+
+struct task_struct *current;
+struct workqueue_struct *system_wq;
+unsigned long jiffies;
+
+Genode::Ram_dataspace_capability Lx::backend_alloc(Genode::addr_t size, Genode::Cache_attribute cached) {
+ return Lx_kit::env().env().ram().alloc(size, cached); }
+
+
+
+int usb_register_driver(struct usb_driver * driver, struct module *, const char *)
+{
+ INIT_LIST_HEAD(&driver->dynids.list);
+ if (driver) new (Lx::Malloc::mem()) Lx_driver(*driver);
+ return 0;
+}
+
+
+int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void *priv)
+{
+ usb_device *udev = interface_to_usbdev(iface);
+ Usb::Connection *usb = reinterpret_cast(udev->bus->controller);
+ try {
+ usb->claim_interface(iface->cur_altsetting->desc.bInterfaceNumber);
+ } catch (...) {
+ return -1;
+ }
+ return 0;
+}
+
+
+int usb_set_interface(struct usb_device *udev, int ifnum, int alternate)
+{
+ Usb::Connection *usb = reinterpret_cast(udev->bus->controller);
+ Driver::Sync_packet packet { *usb };
+ packet.alt_setting(ifnum, alternate);
+ usb_interface *iface = udev->config->interface[ifnum];
+ iface->cur_altsetting = &iface->altsetting[alternate];
+
+ return 0;
+}
+
+
+int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver)
+{
+ usb_device *udev = interface_to_usbdev(intf);
+ if (Genode::strcmp(class_driver->name, "cdc-wdm", 7) == 0) {
+ Terminal::Root *terminal = reinterpret_cast(udev->bus->sysdev);
+ terminal->class_driver(class_driver);
+ return 0;
+ }
+
+ Genode::error(__func__, " no device for class driver '",
+ (char const*)class_driver->name, "'");
+ return -1;
+}
+
+
+void Driver::Device::probe_interface(usb_interface * iface, usb_device_id * id)
+{
+ using Le = Genode::List_element;
+ for (Le *le = Lx_driver::list().first(); le; le = le->next()) {
+ usb_device_id * id = le->object()->match(iface);
+
+ if (id) {
+ int ret = le->object()->probe(iface, id);
+ if (ret == 0) return;
+ }
+ }
+}
+
+
+void Driver::Device::remove_interface(usb_interface * iface)
+{
+ to_usb_driver(iface->dev.driver)->disconnect(iface);
+ for (unsigned i = 0; i < iface->num_altsetting; i++) {
+ if (iface->altsetting[i].extra)
+ kfree(iface->altsetting[i].extra);
+ kfree(iface->altsetting[i].endpoint);
+ kfree(iface->altsetting);
+ }
+ kfree(iface);
+}
+
+
+long __wait_completion(struct completion *work, unsigned long timeout)
+{
+ Lx::timer_update_jiffies();
+ struct process_timer timer { *Lx::scheduler().current() };
+ unsigned long expire = timeout + jiffies;
+
+ if (timeout) {
+ timer_setup(&timer.timer, process_timeout, 0);
+ mod_timer(&timer.timer, expire);
+ }
+
+ while (!work->done) {
+
+ if (timeout && expire <= jiffies) return 0;
+
+ Lx::Task * task = Lx::scheduler().current();
+ work->task = (void *)task;
+ task->block_and_schedule();
+ }
+
+ if (timeout) del_timer(&timer.timer);
+
+ work->done = 0;
+ return (expire > jiffies) ? (expire - jiffies) : 1;
+}
+
+
+u16 get_unaligned_le16(const void *p)
+{
+ const struct __una_u16 *ptr = (const struct __una_u16 *)p;
+ return ptr->x;
+}
+
+
+u32 get_unaligned_le32(const void *p)
+{
+ const struct __una_u32 *ptr = (const struct __una_u32 *)p;
+ return ptr->x;
+}
+
+
+enum { MAC_LEN = 17 };
+
+
+static void snprint_mac(char *buf, u8 *mac)
+{
+ for (int i = 0; i < ETH_ALEN; i++) {
+ Genode::snprintf((char *)&buf[i * 3], 3, "%02x", mac[i]);
+ if ((i * 3) < MAC_LEN)
+ buf[(i * 3) + 2] = ':';
+ }
+
+ buf[MAC_LEN] = 0;
+}
+
+
+static void random_ether_addr(u8 *addr)
+{
+ using namespace Genode;
+
+ char str[MAC_LEN + 1];
+ u8 fallback[] = { 0x2e, 0x60, 0x90, 0x0c, 0x4e, 0x01 };
+ Nic::Mac_address mac;
+
+ Xml_node config_node = Lx_kit::env().config_rom().xml();
+
+ /* try using configured mac */
+ try {
+ Xml_node::Attribute mac_node = config_node.attribute("mac");
+ mac_node.value(mac);
+ } catch (...) {
+ /* use fallback mac */
+ snprint_mac(str, fallback);
+ Genode::warning("No mac address or wrong format attribute in - using fallback (", Genode::Cstring(str), ")");
+
+ Genode::memcpy(addr, fallback, ETH_ALEN);
+ return;
+ }
+
+ /* use configured mac*/
+ Genode::memcpy(addr, mac.addr, ETH_ALEN);
+ snprint_mac(str, (u8 *)mac.addr);
+ Genode::log("Using configured mac: ", Genode::Cstring(str));
+}
+
+
+void eth_hw_addr_random(struct net_device *dev)
+{
+ random_ether_addr(dev->dev_addr);
+}
+
+
+void eth_random_addr(u8 *addr)
+{
+ random_ether_addr(addr);
+}
+
+
+struct net_device *alloc_etherdev(int sizeof_priv)
+{
+ net_device *dev = (net_device*) kzalloc(sizeof(net_device), 0);
+ dev->mtu = 1500;
+ dev->hard_header_len = 0;
+ dev->priv = kzalloc(sizeof_priv, 0);
+ dev->dev_addr = (unsigned char*) kzalloc(ETH_ALEN, 0);
+ //memset(dev->_dev_addr, 0, sizeof(dev->_dev_addr));
+ return dev;
+}
+
+
+void *__alloc_percpu(size_t size, size_t align)
+{
+ return kmalloc(size, 0);
+}
+
+
+int mii_nway_restart(struct mii_if_info *mii)
+{
+ /* if autoneg is off, it's an error */
+ int bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
+
+ if (bmcr & BMCR_ANENABLE) {
+ printk("Reenable\n");
+ bmcr |= BMCR_ANRESTART;
+ mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+static net_device * single_net_device = nullptr;
+
+int register_netdev(struct net_device *dev)
+{
+ dev->state |= 1 << __LINK_STATE_START;
+
+ int err = dev->netdev_ops->ndo_open(dev);
+ if (err) return err;
+
+ if (dev->netdev_ops->ndo_set_rx_mode)
+ dev->netdev_ops->ndo_set_rx_mode(dev);
+
+ single_net_device = dev;
+ return 0;
+};
+
+
+net_device * Session_component::_register_session_component(Session_component & s,
+ Genode::Session_label policy)
+{
+ if (single_net_device) single_net_device->session_component = (void*) &s;
+ return single_net_device;
+}
+
+
+void tasklet_schedule(struct tasklet_struct *t)
+{
+ Lx::Work *lx_work = (Lx::Work *)tasklet_wq->task;
+ lx_work->schedule_tasklet(t);
+ lx_work->unblock();
+}
+
+
+struct workqueue_struct *create_singlethread_workqueue(char const *name)
+{
+ workqueue_struct *wq = (workqueue_struct *)kzalloc(sizeof(workqueue_struct), 0);
+ Lx::Work *work = Lx::Work::alloc_work_queue(&Lx::Malloc::mem(), name);
+ wq->task = (void *)work;
+
+ return wq;
+}
+
+
+struct workqueue_struct *alloc_workqueue(const char *fmt, unsigned int flags,
+ int max_active, ...)
+{
+ return create_singlethread_workqueue(fmt);
+}
+
+
+int dev_set_drvdata(struct device *dev, void *data)
+{
+ dev->driver_data = data;
+ return 0;
+}
+
+
+void * dev_get_drvdata(const struct device *dev)
+{
+ return dev->driver_data;
+}
+
+
+int netif_running(const struct net_device *dev)
+{
+ return dev->state & (1 << __LINK_STATE_START);
+}
+
+
+void netif_carrier_off(struct net_device *dev)
+{
+ dev->state |= 1 << __LINK_STATE_NOCARRIER;
+ if (dev->session_component)
+ reinterpret_cast(dev->session_component)->link_state(false);
+}
+
+
+int netif_carrier_ok(const struct net_device *dev)
+{
+ return !(dev->state & (1 << __LINK_STATE_NOCARRIER));
+}
+
+
+void *kmem_cache_alloc_node(struct kmem_cache *cache, gfp_t gfp_flags, int arg)
+{
+ return (void*)cache->alloc();
+}
+
+
+int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
+{
+ ecmd->duplex = DUPLEX_FULL;
+ return 0;
+}
+
+
+void netif_carrier_on(struct net_device *dev)
+{
+ dev->state &= ~(1 << __LINK_STATE_NOCARRIER);
+ if (dev->session_component)
+ reinterpret_cast(dev->session_component)->link_state(true);
+}
+
+
+int netif_rx(struct sk_buff * skb)
+{
+ if (skb->dev->session_component)
+ reinterpret_cast(skb->dev->session_component)->receive(skb);
+
+ dev_kfree_skb(skb);
+ return NET_RX_SUCCESS;
+}
+
+
+struct sk_buff;
+void dev_kfree_skb_any(struct sk_buff *skb)
+{
+ TRACE;
+ kfree_skb(skb);
+}
+
+
+int is_valid_ether_addr(const u8 * a)
+{
+ for (unsigned i = 0; i < ETH_ALEN; i++)
+ if (a[i] != 0 && a[i] != 255) return 1;
+ return 0;
+}
+
+
+unsigned int mii_check_media (struct mii_if_info *mii, unsigned int ok_to_print, unsigned int init_media)
+{
+ if (mii_link_ok(mii)) netif_carrier_on(mii->dev);
+ else netif_carrier_off(mii->dev);
+ return 0;
+}
+
+
+int mii_link_ok (struct mii_if_info *mii)
+{
+ /* first, a dummy read, needed to latch some MII phys */
+ mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
+ if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
+ return 1;
+ return 0;
+}
+
+
+static struct page *allocate_pages(gfp_t gfp_mask, unsigned int size)
+{
+ struct page *page = (struct page *)kzalloc(sizeof(struct page), 0);
+
+ page->addr = Lx::Malloc::dma().alloc_large(size);
+ page->size = size;
+
+ if (!page->addr) {
+ Genode::error("alloc_pages: ", size, " failed");
+ kfree(page);
+ return 0;
+ }
+
+ Addr_to_page_mapping::insert(page);
+
+ atomic_set(&page->_count, 1);
+
+ return page;
+}
+
+
+void *page_frag_alloc(struct page_frag_cache *nc, unsigned int fragsz, gfp_t gfp_mask)
+{
+ struct page *page = allocate_pages(gfp_mask, fragsz);
+ if (!page) return nullptr;
+
+ return page->addr;
+}
+
+
+void page_frag_free(void *addr)
+{
+ struct page *page = Addr_to_page_mapping::remove((unsigned long)addr);
+
+ if (!atomic_dec_and_test(&page->_count))
+ Genode::error("page reference count != 0");
+
+ Lx::Malloc::dma().free_large(page->addr);
+ kfree(page);
+}
+
+
+unsigned iminor(const struct inode *inode)
+{
+ return 0;
+}
+
+
+void *memdup_user(const void *src, size_t len)
+{
+ void *out = kmalloc(len, 0);
+ memcpy(out, src, len);
+ return out;
+}
+
+
+long copy_to_user(void *to, const void *from, unsigned long n)
+{
+ memcpy(to, from, n);
+ return 0;
+}
+
+
+
+int mutex_lock_interruptible(struct mutex *m)
+{
+ mutex_lock(m);
+ return 0;
+}
+
+
+void hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode)
+{
+ hrtimer_start_range_ns(timer, tim, 0, mode);
+}
+
+
+int vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci)
+{
+ TRACE;
+ *vlan_tci = 0;
+ return 0;
+}
diff --git a/repos/dde_linux/src/drivers/usb_modem/lx_emul.h b/repos/dde_linux/src/drivers/usb_modem/lx_emul.h
new file mode 100644
index 0000000000..299b4061ac
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/lx_emul.h
@@ -0,0 +1,1448 @@
+/*
+ * \brief USB net driver Linux emulation environment
+ * \author Stefan Kalkowski
+ * \author Sebastian Sumpf
+ * \date 2018-06-13
+ */
+
+/*
+ * Copyright (C) 2018-2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#ifndef _SRC__DRIVERS__USB_NET__LX_EMUL_H_
+#define _SRC__DRIVERS__USB_NET__LX_EMUL_H_
+
+#include
+#include
+
+#include
+
+#define __KERNEL__ 1
+
+#include
+#include
+#include
+#include
+
+#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+enum { HZ = 100UL };
+
+#include
+#include
+#include
+
+typedef int clockid_t;
+
+#include
+#include
+
+typedef __u16 __le16;
+typedef __u32 __le32;
+typedef __u64 __le64;
+typedef __u64 __be64;
+
+#define U32_MAX ((u32)~0U)
+
+#define __aligned_u64 __u64 __attribute__((aligned(8)))
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define READ_ONCE(x) x
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct user_namespace {};
+
+struct cred {
+ struct user_namespace * user_ns;
+};
+
+struct file
+{
+ unsigned int f_flags;
+ void * private_data;
+ const struct cred * f_cred;
+};
+
+typedef unsigned fl_owner_t;
+
+struct device;
+struct device_driver;
+
+typedef struct { __u8 b[16]; } uuid_le;
+void * dev_get_drvdata(const struct device *dev);
+int dev_set_drvdata(struct device *dev, void *data);
+
+#define netdev_dbg(dev, fmt, args...)
+#define netdev_warn(dev, fmt, args...) lx_printf("netdev_warn: " fmt, ##args)
+#define netdev_err(dev, fmt, args...) lx_printf("netdev_err: " fmt, ##args)
+#define netdev_info(dev, fmt, args...) lx_printf("netdev_info: " fmt, ##args)
+
+#define dev_info(dev, format, arg...) lx_printf("dev_info: " format , ## arg)
+#define dev_warn(dev, format, arg...) lx_printf("dev_warn: " format , ## arg)
+#define dev_err( dev, format, arg...) lx_printf("dev_err: " format , ## arg)
+#if 0
+#define dev_dbg( dev, format, arg...) lx_printf("dev_dbg: " format , ## arg)
+#else
+#define dev_dbg(dev, format, arg...)
+#endif
+
+#define netif_info(priv, type, dev, fmt, args...) lx_printf("netif_info: " fmt, ## args);
+#define netif_dbg(priv, type, dev, fmt, args...)
+#define netif_err(priv, type, dev, fmt, args...) lx_printf("netif_err: " fmt, ## args);
+
+#define pr_debug(fmt, ...)
+#define pr_info(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__)
+#define pr_err(fmt, ...) printk(KERN_ERR fmt, ##__VA_ARGS__)
+#define pr_warn(fmt, ...) printk(KERN_ERR fmt, ##__VA_ARGS__)
+#define pr_info_once(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__)
+#define pr_notice(fmt, ...) printk(KERN_NOTICE fmt, ##__VA_ARGS__)
+#define pr_emerg(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__)
+
+#define __stringify_1(x...) #x
+#define __stringify(x...) __stringify_1(x)
+
+enum { O_NONBLOCK = 0x4000 };
+
+enum {
+ S_IRUGO = 444,
+ S_IWUSR = 200,
+};
+
+#define _IOR(type,nr,size) (type+nr+sizeof(size)+10)
+
+struct bus_type
+{
+ int (*match)(struct device *dev, struct device_driver *drv);
+ int (*probe)(struct device *dev);
+};
+
+struct device_driver
+{
+ char const *name;
+ struct bus_type *bus;
+ struct module *owner;
+ const char *mod_name;
+};
+
+typedef int devt;
+
+ struct device_type {
+ const char *name;
+};
+
+struct class
+{
+ const char *name;
+ char *(*devnode)(struct device *dev, mode_t *mode);
+};
+
+struct device_node;
+
+struct device
+{
+ char const * name;
+ struct device * parent;
+ struct kobject * kobj;
+ struct device_driver * driver;
+ struct bus_type * bus;
+ dev_t devt;
+ struct class * class;
+ const struct device_type * type;
+ void (*release)(struct device *dev);
+ void * driver_data;
+ struct device_node * of_node;
+};
+
+struct attribute
+{
+ const char * name;
+ mode_t mode;
+};
+
+struct attribute_group {
+ const char *name;
+ mode_t (*is_visible)(struct kobject *, struct attribute *, int);
+ struct attribute **attrs;
+};
+
+struct device_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct device *dev, struct device_attribute *attr,
+ char *buf);
+ ssize_t (*store)(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+};
+
+#define __ATTR(_name,_mode,_show,_store) { \
+ .attr = {.name = #_name, .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+}
+
+#define __ATTR_NULL { .attr = { .name = NULL } }
+#define __ATTR_RW(name) __ATTR_NULL
+
+#define DEVICE_ATTR(_name, _mode, _show, _store) \
+struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
+
+#define DEVICE_ATTR_RW(_name) \
+struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
+
+#define module_driver(__driver, __register, __unregister, ...) \
+ static int __init __driver##_init(void) \
+ { \
+ return __register(&(__driver) , ##__VA_ARGS__); \
+ } \
+ module_init(__driver##_init); \
+ static void __exit __driver##_exit(void) \
+ { \
+ __unregister(&(__driver) , ##__VA_ARGS__); \
+ } \
+ module_exit(__driver##_exit);
+
+#define KBUILD_MODNAME ""
+
+void kfree(const void *);
+
+#define from_timer(var, callback_timer, timer_fieldname) \
+ container_of(callback_timer, typeof(*var), timer_fieldname)
+
+void hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode);
+
+void *kmalloc(size_t size, gfp_t flags);
+void *kzalloc(size_t size, gfp_t flags);
+
+int kstrtoul(const char *s, unsigned int base, unsigned long *res);
+
+int snprintf(char *buf, size_t size, const char *fmt, ...);
+int sprintf(char *buf, const char *fmt, ...);
+int strtobool(const char *, bool *);
+
+const char *dev_name(const struct device *dev);
+
+struct __una_u16 { u16 x; } __attribute__((packed));
+struct __una_u32 { u32 x; } __attribute__((packed));
+
+u16 get_unaligned_le16(const void *p);
+u32 get_unaligned_le32(const void *p);
+
+struct completion
+{
+ unsigned int done;
+ void * task;
+};
+
+struct notifier_block;
+
+enum {
+ EPOLLIN = 0x00000001,
+ EPOLLOUT = 0x00000004,
+ EPOLLERR = 0x00000008,
+ EPOLLHUP = 0x00000010,
+ EPOLLRDNORM = 0x00000040,
+ EPOLLWRNORM = 0x00000100,
+ ESHUTDOWN = 58,
+};
+
+void msleep(unsigned int);
+
+#define PAGE_SIZE 4096
+
+#define rcu_assign_pointer(p,v) p = v
+
+signed long schedule_timeout(signed long timeout);
+
+int device_set_wakeup_enable(struct device *dev, bool enable);
+
+struct tasklet_struct
+{
+ void (*func)(unsigned long);
+ unsigned long data;
+};
+
+struct net_device;
+struct ifreq;
+struct sk_buff;
+struct rtnl_link_stats64;
+
+enum netdev_tx {
+ NETDEV_TX_OK = 0x00,
+ NETDEV_TX_BUSY = 0x10,
+ NETDEV_TX_LOCKED = 0x20,
+};
+typedef enum netdev_tx netdev_tx_t;
+
+#include
+
+struct net_device_ops {
+ int (*ndo_open) (struct net_device *dev);
+ int (*ndo_stop) (struct net_device *dev);
+ netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev);
+ void (*ndo_set_rx_mode) (struct net_device *dev);
+ int (*ndo_change_mtu) (struct net_device *dev, int new_mtu);
+ int (*ndo_validate_addr) (struct net_device *dev);
+ void (*ndo_tx_timeout) (struct net_device *dev);
+ int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
+ int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
+ int (*ndo_set_features)(struct net_device *dev, netdev_features_t features);
+ void (*ndo_get_stats64)(struct net_device *dev, struct rtnl_link_stats64 *storage);
+ int (*ndo_vlan_rx_add_vid)(struct net_device *dev, __be16 proto, u16 vid);
+ int (*ndo_vlan_rx_kill_vid)(struct net_device *dev, __be16 proto, u16 vid);
+};
+
+struct net_device_stats
+{
+ unsigned long rx_packets;
+ unsigned long tx_packets;
+ unsigned long rx_bytes;
+ unsigned long tx_bytes;
+ unsigned long rx_errors;
+ unsigned long tx_errors;
+ unsigned long rx_dropped;
+ unsigned long tx_dropped;
+ unsigned long rx_length_errors;
+ unsigned long rx_over_errors;
+ unsigned long rx_crc_errors;
+ unsigned long rx_frame_errors;
+};
+
+enum netdev_state_t {
+ __LINK_STATE_START,
+ __LINK_STATE_PRESENT,
+ __LINK_STATE_NOCARRIER,
+ __LINK_STATE_LINKWATCH_PENDING,
+ __LINK_STATE_DORMANT,
+};
+
+enum { MAX_ADDR_LEN = 32, IFNAMESZ = 16 };
+
+struct net_device
+{
+ char name[IFNAMESZ];
+ unsigned long state;
+ netdev_features_t features;
+ struct net_device_stats stats;
+ netdev_features_t hw_features;
+ const struct net_device_ops *netdev_ops;
+ const struct ethtool_ops *ethtool_ops;
+ const struct header_ops *header_ops;
+ unsigned int flags;
+ unsigned int priv_flags;
+ unsigned short hard_header_len;
+ unsigned char min_header_len;
+ unsigned long mtu;
+ unsigned long min_mtu;
+ unsigned long max_mtu;
+ unsigned short type;
+ unsigned char addr_len;
+ unsigned char *dev_addr;
+ unsigned char broadcast[MAX_ADDR_LEN];
+ unsigned long tx_queue_len;
+ int watchdog_timeo;
+ struct timer_list watchdog_timer;
+ struct device dev;
+ const struct attribute_group *sysfs_groups[4];
+ u16 gso_max_segs;
+ struct phy_device *phydev;
+ unsigned short needed_headroom;
+ unsigned short needed_tailroom;
+ void *priv;
+ unsigned char perm_addr[MAX_ADDR_LEN];
+ unsigned char addr_assign_type;
+ int ifindex;
+ void *session_component;
+};
+
+#define to_net_dev(d) container_of(d, struct net_device, dev)
+
+struct usbnet;
+
+struct ethtool_eeprom;
+struct ethtool_drvinfo;
+
+struct sock;
+
+struct kvec
+{
+ void *iov_base;
+ size_t iov_len;
+};
+
+struct iov_iter {};
+size_t iov_iter_count(struct iov_iter *i);
+
+typedef int raw_hdlc_proto;
+typedef int cisco_proto;
+typedef int fr_proto;
+typedef int fr_proto_pvc;
+typedef int fr_proto_pvc_info;
+typedef int sync_serial_settings;
+typedef int te1_settings;
+
+struct rndis_indicate;
+
+enum { ETH_ALEN = 6 };
+
+int netif_running(const struct net_device *dev);
+int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd);
+static inline void *netdev_priv(const struct net_device *dev) { return dev->priv; }
+
+int usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, u16 value, u16 index, void *data, u16 size);
+int usbnet_read_cmd_nopm(struct usbnet *dev, u8 cmd, u8 reqtype, u16 value, u16 index, void *data, u16 size);
+
+typedef __u32 __wsum;
+
+static inline int rcu_read_lock_held(void) { return 1; }
+static inline int rcu_read_lock_bh_held(void) { return 1; }
+
+enum { NUMA_NO_NODE = -1 };
+
+struct ts_state
+{
+ char cb[40];
+};
+
+struct ts_config
+{
+ unsigned int (*get_next_block)(unsigned int consumed,
+ const u8 **dst,
+ struct ts_config *conf,
+ struct ts_state *state);
+ void (*finish)(struct ts_config *conf,
+ struct ts_state *state);
+};
+
+struct flow_dissector_key_control
+{
+ u16 thoff;
+ u16 addr_type;
+ u32 flags;
+};
+
+struct flow_keys
+{
+ struct flow_dissector_key_control control;
+};
+
+struct flow_dissector_key {};
+
+struct flow_dissector {};
+
+extern struct flow_dissector flow_keys_dissector;
+extern struct flow_dissector flow_keys_buf_dissector;
+
+struct flowi4 {};
+struct flowi6 {};
+
+__u32 __get_hash_from_flowi6(const struct flowi6 *fl6, struct flow_keys *keys);
+
+bool flow_keys_have_l4(struct flow_keys *keys);
+
+__u32 __get_hash_from_flowi4(const struct flowi4 *fl4, struct flow_keys *keys);
+
+bool gfpflags_allow_blocking(const gfp_t gfp_flags);
+
+struct lock_class_key { };
+
+#define lockdep_set_class(lock, key)
+
+struct page
+{
+ atomic_t _count;
+ void *addr;
+ dma_addr_t paddr;
+ unsigned long private;
+ unsigned long size;
+} __attribute((packed));
+
+static inline struct page *compound_head(struct page *page) { return page; }
+
+bool page_is_pfmemalloc(struct page *page);
+
+void __free_page_frag(void *addr);
+
+struct page *alloc_pages_node(int nid, gfp_t gfp_mask, unsigned int order);
+
+void get_page(struct page *page);
+void put_page(struct page *page);
+
+static inline void *page_address(struct page *page) { return page->addr; };
+
+struct page_frag
+{
+ struct page *page;
+ __u16 offset;
+ __u16 size;
+};
+
+enum dma_data_direction { DMA_FROM_DEVICE = 2 };
+
+dma_addr_t dma_map_page(struct device *dev, struct page *page, size_t offset, size_t size, enum dma_data_direction dir);
+
+void dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir);
+
+#define L1_CACHE_BYTES 32
+
+size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i);
+
+__wsum csum_block_add(__wsum csum, __wsum csum2, int offset);
+__wsum csum_sub(__wsum csum, __wsum addend);
+__wsum csum_partial(const void *buff, int len, __wsum sum);
+
+bool csum_and_copy_from_iter_full(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i);
+bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i);
+__wsum csum_block_sub(__wsum, __wsum, int);
+typedef unsigned __poll_t;
+
+typedef struct poll_table_struct { } poll_table;
+size_t copy_to_iter(void *addr, size_t bytes, struct iov_iter *i);
+struct timespec ktime_to_timespec(const ktime_t kt);
+
+struct socket;
+typedef u64 netdev_features_t;
+typedef __u16 __sum16;
+
+__sum16 csum_fold(__wsum csum);
+__wsum csum_unfold(__sum16 n);
+__wsum csum_add(__wsum csum, __wsum addend);
+__wsum remcsum_adjust(void *ptr, __wsum csum, int start, int offset);
+
+struct sk_buff;
+
+void dev_kfree_skb_any(struct sk_buff *);
+
+
+extern void page_frag_free(void *addr);
+
+#define DECLARE_BITMAP(name,bits) unsigned long name[BITS_TO_LONGS(bits)]
+
+struct rtnl_link_stats64;
+struct ethtool_link_ksettings;
+int netif_carrier_ok(const struct net_device *dev);
+
+int is_valid_ether_addr(const u8 *);
+
+void phy_print_status(struct phy_device *phydev);
+
+enum {
+ MII_BMCR = 0x0,
+ MII_BMSR = 0x1,
+ MII_PHYSID1 = 0x2,
+ MII_PHYSID2 = 0x3,
+ MII_ADVERTISE = 0x4,
+ MII_LPA = 0x5,
+ MII_CTRL1000 = 0x9,
+ MII_MMD_CTRL = 0xd,
+ MII_MMD_DATA = 0xe,
+ MII_PHYADDR = 0x19,
+ MII_MMD_CTRL_NOINCR = 0x4000,
+};
+
+enum { VLAN_HLEN = 4 };
+
+void udelay(unsigned long usecs);
+
+int eth_validate_addr(struct net_device *);
+
+int netdev_mc_empty(struct net_device *);
+unsigned netdev_mc_count(struct net_device * dev);
+
+#define netdev_for_each_mc_addr(a, b) if (0)
+
+void usleep_range(unsigned long min, unsigned long max);
+
+void eth_hw_addr_random(struct net_device *dev);
+
+static inline void ether_addr_copy(u8 *dst, const u8 *src)
+{
+ *(u32 *)dst = *(const u32 *)src;
+ *(u16 *)(dst+ 4) = *(const u16 *)(src + 4);
+}
+
+u32 ether_crc(int, unsigned char *);
+
+struct netdev_hw_addr
+{
+ unsigned char addr[MAX_ADDR_LEN];
+};
+
+void mdelay(unsigned long usecs);
+
+int eth_mac_addr(struct net_device *, void *);
+
+void netif_carrier_on(struct net_device *dev);
+void netif_carrier_off(struct net_device *dev);
+
+const void *of_get_mac_address(struct device_node *np);
+
+u16 bitrev16(u16 in);
+u16 crc16(u16 crc, const u8 *buffer, size_t len);
+int hex2bin(u8 *dst, const char *src, size_t count);
+
+#define this_cpu_ptr(ptr) ptr
+
+__be16 eth_type_trans(struct sk_buff *, struct net_device *);
+
+struct u64_stats_sync {};
+
+static inline unsigned long u64_stats_update_begin_irqsave(struct u64_stats_sync *syncp) {
+ return 0; }
+
+static inline void u64_stats_update_end_irqrestore(struct u64_stats_sync *syncp, unsigned long flags) { }
+
+struct pcpu_sw_netstats
+{
+ u64 rx_packets;
+ u64 rx_bytes;
+ u64 tx_packets;
+ u64 tx_bytes;
+ struct u64_stats_sync syncp;
+};
+
+int netif_rx(struct sk_buff *);
+
+enum { NET_RX_SUCCESS = 0 };
+enum { SINGLE_DEPTH_NESTING = 1 };
+
+void tasklet_schedule(struct tasklet_struct *t);
+void tasklet_kill(struct tasklet_struct *t);
+
+int netif_device_present(struct net_device * d);
+void netif_device_detach(struct net_device *dev);
+void netif_stop_queue(struct net_device *);
+void netif_start_queue(struct net_device *);
+void netif_wake_queue(struct net_device * d);
+
+void netif_tx_lock_bh(struct net_device *dev);
+void netif_tx_unlock_bh(struct net_device *dev);
+
+
+void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, const struct net_device_stats *netdev_stats);
+
+enum { TASK_RUNNING = 0, TASK_INTERRUPTIBLE = 1, TASK_UNINTERRUPTIBLE = 2, TASK_NORMAL = 3 };
+
+void __set_current_state(int state);
+#define set_current_state(state) __set_current_state(state)
+
+extern const struct cpumask *const cpu_possible_mask;
+
+#define for_each_cpu(cpu, mask) \
+ for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)
+
+#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
+
+#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu);(typeof(*(ptr)) *)(ptr); })
+
+u32 netif_msg_init(int, int);
+#define netif_msg_tx_err(p) ({ printk("netif_msg_tx_err called not implemented\n"); 0; })
+#define netif_msg_rx_err(p) ({ printk("netif_msg_rx_err called not implemented\n"); 0; })
+
+unsigned int u64_stats_fetch_begin_irq(const struct u64_stats_sync *p);
+bool u64_stats_fetch_retry_irq(const struct u64_stats_sync *p, unsigned int s);
+
+void unregister_netdev(struct net_device *);
+
+#define free_percpu(pdata) kfree(pdata)
+
+void free_netdev(struct net_device *);
+
+void netif_trans_update(struct net_device *dev);
+
+void pm_runtime_enable(struct device *dev);
+
+struct net_device *alloc_etherdev(int);
+
+#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))
+
+void *__alloc_percpu(size_t size, size_t align);
+
+#define alloc_percpu(type) \
+ (typeof(type) __percpu *)__alloc_percpu(sizeof(type), __alignof__(type))
+
+
+#define netdev_alloc_pcpu_stats(type) alloc_percpu(type)
+
+enum {
+ NETIF_MSG_DRV = 0x1,
+ NETIF_MSG_PROBE = 0x2,
+ NETIF_MSG_LINK = 0x4,
+};
+
+static inline bool ether_addr_equal(const u8 *addr1, const u8 *addr2)
+{
+ const u16 *a = (const u16 *)addr1;
+ const u16 *b = (const u16 *)addr2;
+
+ return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) == 0;
+}
+
+enum { NET_ADDR_RANDOM = 1 };
+
+#define SET_NETDEV_DEVTYPE(net, devtype) ((net)->dev.type = (devtype))
+
+int register_netdev(struct net_device *);
+
+void netif_device_attach(struct net_device *dev);
+
+enum { GFP_NOIO = GFP_LX_DMA };
+
+void netif_tx_wake_all_queues(struct net_device *dev);
+
+void eth_random_addr(u8 *addr);
+
+long __wait_completion(struct completion *work, unsigned long timeout);
+
+struct mii_ioctl_data;
+
+typedef int possible_net_t;
+
+void *kmalloc_node_track_caller(size_t size, gfp_t flags, int node);
+bool gfp_pfmemalloc_allowed(gfp_t);
+
+struct callback_head {
+ struct callback_head *next;
+ void (*func)(struct callback_head *head);
+};
+#define rcu_head callback_head
+
+typedef int rwlock_t;
+
+struct task_struct;
+
+typedef struct { } read_descriptor_t;
+
+#define SMP_CACHE_BYTES L1_CACHE_BYTES
+#define ____cacheline_aligned __attribute__((aligned(SMP_CACHE_BYTES)))
+#define ____cacheline_aligned_in_smp __attribute__((aligned(SMP_CACHE_BYTES)))
+
+struct percpu_counter {
+ s64 count;
+};
+
+static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp)
+{
+ fbc->count = amount;
+ return 0;
+}
+
+static inline s64 percpu_counter_read(struct percpu_counter *fbc)
+{
+ return fbc->count;
+}
+
+static inline
+void percpu_counter_add(struct percpu_counter *fbc, s64 amount)
+{
+ fbc->count += amount;
+}
+
+static inline
+void __percpu_counter_add(struct percpu_counter *fbc, s64 amount, s32 batch)
+{
+ percpu_counter_add(fbc, amount);
+}
+
+s64 percpu_counter_sum_positive(struct percpu_counter *fbc);
+
+static inline void percpu_counter_inc(struct percpu_counter *fbc)
+{
+ percpu_counter_add(fbc, 1);
+}
+
+static inline void percpu_counter_dec(struct percpu_counter *fbc)
+{
+ percpu_counter_add(fbc, -1);
+}
+
+static inline
+s64 percpu_counter_read_positive(struct percpu_counter *fbc)
+{
+ return fbc->count;
+}
+
+void percpu_counter_destroy(struct percpu_counter *fbc);
+
+s64 percpu_counter_sum(struct percpu_counter *fbc);
+
+void bitmap_fill(unsigned long *dst, int nbits);
+void bitmap_zero(unsigned long *dst, int nbits);
+
+typedef unsigned seqlock_t;
+
+enum { LL_MAX_HEADER = 96 };
+
+struct hh_cache
+{
+ u16 hh_len;
+ u16 __pad;
+ seqlock_t hh_lock;
+
+#define HH_DATA_MOD 16
+#define HH_DATA_OFF(__len) \
+ (HH_DATA_MOD - (((__len - 1) & (HH_DATA_MOD - 1)) + 1))
+#define HH_DATA_ALIGN(__len) \
+ (((__len)+(HH_DATA_MOD-1))&~(HH_DATA_MOD - 1))
+ unsigned long hh_data[HH_DATA_ALIGN(LL_MAX_HEADER) / sizeof(long)];
+};
+
+struct seq_net_private {
+ struct net *net;
+};
+
+struct seq_file;
+struct ctl_table;
+
+typedef int proc_handler (struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos);
+
+unsigned read_seqbegin(const seqlock_t *sl);
+unsigned read_seqretry(const seqlock_t *sl, unsigned start);
+
+int dev_queue_xmit(struct sk_buff *skb);
+
+#define raw_smp_processor_id() 0
+
+#define rcu_dereference_bh(p) p
+#define rcu_dereference_raw(p) p
+#define rcu_dereference_check(p, c) p
+#define rcu_dereference(p) p
+
+#define read_pnet(pnet) (&init_net)
+
+static inline int net_eq(const struct net *net1, const struct net *net2) {
+ return net1 == net2; }
+
+extern struct net init_net;
+
+struct net *dev_net(const struct net_device *dev);
+
+#define __randomize_layout
+
+struct cgroup;
+
+#define mem_cgroup_sockets_enabled 0
+
+struct mem_cgroup;
+
+static inline bool mem_cgroup_under_socket_pressure(struct mem_cgroup *memcg) {
+ return false; }
+
+struct inode
+{
+//umode_t i_mode;
+ kuid_t i_uid;
+//unsigned long i_ino;
+};
+
+loff_t noop_llseek(struct file *file, loff_t offset, int whence);
+unsigned iminor(const struct inode *inode);
+
+struct file_operations
+{
+ struct module *owner;
+ int (*open) (struct inode *, struct file *);
+ ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
+ loff_t (*llseek) (struct file *, loff_t, int);
+ unsigned int (*poll) (struct file *, struct poll_table_struct *);
+ long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
+ long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
+ int (*flush) (struct file *, fl_owner_t id);
+ int (*release) (struct inode *, struct file *);
+ ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
+};
+
+LX_MUTEX_INIT_DECLARE(wdm_mutex);
+
+#define wdm_mutex LX_MUTEX(wdm_mutex)
+
+#define mutex_release(l, n, i)
+int mutex_lock_interruptible(struct mutex *);
+
+int spin_is_locked(spinlock_t *lock);
+
+void write_lock_bh(rwlock_t *);
+void write_unlock_bh(rwlock_t *);
+
+void security_sock_graft(struct sock *, struct socket *);
+
+typedef unsigned kgid_t;
+kuid_t make_kuid(struct user_namespace *from, uid_t uid);
+
+struct net
+{
+ struct user_namespace * user_ns;
+};
+
+u32 prandom_u32(void);
+static inline void rcu_read_lock(void) { }
+static inline void rcu_read_unlock(void) { }
+#define rcu_dereference_protected(p, c) p
+bool net_gso_ok(netdev_features_t features, int gso_type);
+bool copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i);
+bool lockdep_is_held(void *l);
+
+long copy_to_user(void *to, const void *from, unsigned long n);
+
+extern int debug_locks;
+bool wq_has_sleeper(struct wait_queue_head *wq_head);
+bool poll_does_not_wait(const poll_table *p);
+void poll_wait(struct file *f, wait_queue_head_t *w, poll_table *p);
+
+struct task_struct
+{
+ unsigned int flags;
+ struct page_frag task_frag;
+};
+
+extern struct task_struct *current;
+int in_softirq(void);
+
+enum { MAX_SCHEDULE_TIMEOUT = 1000 };
+
+#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
+
+#define write_pnet(pnet, net) do { (void)(net);} while (0)
+
+int l3mdev_master_ifindex_by_index(struct net *net, int ifindex);
+
+void *memdup_user(const void *, size_t);
+
+struct kmem_cache;
+void *kmem_cache_alloc_node(struct kmem_cache *cache, gfp_t, int);
+void kmem_cache_free(struct kmem_cache *, void *);
+void *kmem_cache_alloc(struct kmem_cache *, gfp_t);
+struct page *virt_to_head_page(const void *x);
+
+struct page_frag_cache
+{
+ bool pfmemalloc;
+};
+
+#define prefetchw(x) __builtin_prefetch(x,1)
+
+size_t ksize(void *);
+
+#define DEFINE_PER_CPU(type, name) \
+ typeof(type) name
+
+static inline unsigned long local_irq_save(unsigned long flags) { return flags; }
+static inline void local_irq_restore(unsigned long f) { }
+
+void *page_frag_alloc(struct page_frag_cache *nc, unsigned int fragsz, gfp_t gfp_mask);
+
+enum {
+ NAPI_STATE_SCHED,
+ NAPI_STATE_DISABLE,
+ NAPI_STATE_NPSVC,
+ NAPI_STATE_HASHED,
+};
+
+struct napi_struct
+{
+ struct net_device * dev;
+ int (*poll)(struct napi_struct *, int);
+ unsigned long state;
+ int weight;
+};
+
+void secpath_reset(struct sk_buff *);
+int in_irq();
+
+void trace_kfree_skb(struct sk_buff *, void *);
+void trace_consume_skb(struct sk_buff *);
+
+void kmem_cache_free_bulk(struct kmem_cache *, size_t, void **);
+
+void dev_consume_skb_any(struct sk_buff *skb);
+
+bool capable(int);
+
+enum { PAGE_SHIFT = 12 };
+
+unsigned long rlimit(unsigned int limit);
+
+enum { RLIMIT_MEMLOCK = 8 };
+
+struct user_struct
+{
+ atomic_long_t locked_vm;
+};
+
+struct user_struct *current_user();
+
+static inline int atomic_long_cmpxchg(atomic_long_t *v, long old, long n) {
+ return cmpxchg(&v->counter, old, n); }
+
+struct user_struct *get_uid(struct user_struct *u);
+void free_uid(struct user_struct *);
+
+#define in_task() (1)
+
+struct inet_skb_parm
+{
+ int iif;
+};
+
+struct page *alloc_pages(gfp_t gfp_mask, unsigned int order);
+#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
+
+#define page_private(page) ((page)->private)
+#define set_page_private(page, v) ((page)->private = (v))
+
+void *kmap_atomic(struct page *page);
+void kunmap_atomic(void *addr);
+
+#define CONFIG_LOCKDEP 1
+
+struct partial_page
+{
+ unsigned int offset;
+ unsigned int len;
+};
+
+struct splice_pipe_desc
+{
+ struct page **pages;
+ struct partial_page *partial;
+ int nr_pages;
+ unsigned int nr_pages_max;
+ //unsigned int flags;
+ const struct pipe_buf_operations *ops;
+ void (*spd_release)(struct splice_pipe_desc *, unsigned int);
+};
+
+struct page *virt_to_page(const void *x);
+
+extern const struct pipe_buf_operations nosteal_pipe_buf_ops;
+
+struct pipe_inode_info;
+ssize_t splice_to_pipe(struct pipe_inode_info *, struct splice_pipe_desc *);
+
+bool check_copy_size(const void *addr, size_t bytes, bool is_source);
+
+__wsum csum_partial_ext(const void *buff, int len, __wsum sum);
+__wsum csum_block_add_ext(__wsum csum, __wsum csum2, int offset, int len);
+__wsum csum_partial_copy(const void *src, void *dst, int len, __wsum sum);
+#define csum_partial_copy_nocheck(src, dst, len, sum) \
+ csum_partial_copy((src), (dst), (len), (sum))
+
+unsigned int textsearch_find(struct ts_config *, struct ts_state *);
+
+__be16 skb_network_protocol(struct sk_buff *skb, int *depth);
+
+bool can_checksum_protocol(netdev_features_t features, __be16 protocol);
+
+unsigned int skb_gro_offset(const struct sk_buff *skb);
+
+unsigned int skb_gro_len(const struct sk_buff *skb);
+
+#define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb)
+
+enum { NAPI_GRO_FREE = 1, NAPI_GRO_FREE_STOLEN_HEAD = 2, };
+
+struct napi_gro_cb
+{
+ u16 flush;
+ u16 count;
+ u8 same_flow;
+ u8 free;
+ struct sk_buff *last;
+};
+
+enum {
+ SLAB_HWCACHE_ALIGN = 0x00002000ul,
+ SLAB_CACHE_DMA = 0x00004000ul,
+ SLAB_PANIC = 0x00040000ul,
+ SLAB_LX_DMA = 0x80000000ul,
+};
+
+#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
+
+struct kmem_cache *kmem_cache_create_usercopy(const char *name, size_t size, size_t align, slab_flags_t flags, size_t useroffset, size_t usersize, void (*ctor)(void *));
+struct kmem_cache *kmem_cache_create(const char *, size_t, size_t, unsigned long, void (*)(void *));
+
+#define sg_is_last(sg) ((sg)->page_link & 0x02)
+void sg_mark_end(struct scatterlist *sg);
+//void sg_set_buf(struct scatterlist *, const void *, unsigned int);
+//void sg_set_page(struct scatterlist *, struct page *, unsigned int, unsigned int);
+
+enum {
+ IPPROTO_IP = 0,
+ IPPROTO_TCP = 6,
+ IPPROTO_UDP = 17,
+ IPPROTO_AH = 51,
+ IPPROTO_ICMPV6 = 58,
+};
+
+enum {
+ IPPROTO_HOPOPTS = 0,
+ IPPROTO_ROUTING = 43,
+ IPPROTO_FRAGMENT = 44,
+ IPPROTO_DSTOPTS = 60,
+};
+
+void read_lock_bh(rwlock_t *);
+void read_unlock_bh(rwlock_t *);
+
+bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
+
+extern struct user_namespace init_user_ns;
+
+struct tcphdr
+{
+ __be16 source;
+ __be16 dest;
+ __be32 seq;
+ __be32 ack_seq;
+ __u16 res1:4,
+ doff:4,
+ fin:1,
+ syn:1,
+ rst:1,
+ psh:1,
+ ack:1,
+ urg:1,
+ ece:1,
+ cwr:1;
+ __be16 window;
+ __sum16 check;
+};
+
+struct tcphdr *tcp_hdr(const struct sk_buff *skb);
+
+unsigned int tcp_hdrlen(const struct sk_buff *skb);
+
+struct udphdr
+{
+ __sum16 check;
+};
+
+struct udphdr *udp_hdr(const struct sk_buff *skb);
+
+struct iphdr {
+ __u8 ihl:4;
+ __u8 version:4;
+ __u8 tos;
+ __be16 tot_len;
+ __be16 frag_off;
+ __u8 ttl;
+ __u8 protocol;
+ __sum16 check;
+ __be32 saddr;
+ __be32 daddr;
+};
+
+struct iphdr *ip_hdr(const struct sk_buff *skb);
+
+struct in6_addr {};
+
+struct ipv6hdr
+{
+ __be16 payload_len;
+ __u8 nexthdr;
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+};
+
+struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb);
+
+struct ipv6_opt_hdr
+{
+ __u8 nexthdr;
+ __u8 hdrlen;
+} __attribute__((packed));
+
+struct ip_auth_hdr
+{
+ __u8 nexthdr;
+ __u8 hdrlen;
+};
+
+struct frag_hdr
+{
+ __u8 nexthdr;
+ __be16 frag_off;
+};
+
+#define ipv6_optlen(p) (((p)->hdrlen+1) << 3)
+#define ipv6_authlen(p) (((p)->hdrlen+2) << 2)
+
+int ipv6_addr_type(const struct in6_addr *addr);
+bool ipv6_addr_is_solict_mult(const struct in6_addr *addr);
+
+enum { IP_OFFSET = 0x1FFF, IP_MF = 0x2000 };
+
+enum { IP6_MF = 0x0001, IP6_OFFSET = 0xfff8 };
+
+unsigned int ip_hdrlen(const struct sk_buff *skb);
+
+__sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len, unsigned short proto, __wsum sum);
+
+__sum16 csum_ipv6_magic(const struct in6_addr *saddr, const struct in6_addr *daddr, __u32 len, unsigned short proto, __wsum csum);
+
+void secpath_reset(struct sk_buff *);
+
+struct tcphdr *inner_tcp_hdr(const struct sk_buff *skb);
+unsigned int inner_tcp_hdrlen(const struct sk_buff *skb);
+
+#define htons(x) __cpu_to_be16(x)
+#define ntohs(x) __be16_to_cpu(x)
+
+struct sctphdr
+{
+ unsigned unused;
+};
+
+enum {
+ VLAN_CFI_MASK = 0x1000,
+ VLAN_TAG_PRESENT = VLAN_CFI_MASK
+};
+
+struct vlan_hdr
+{
+ __be16 h_vlan_TCI;
+};
+
+#define skb_vlan_tag_present(__skb) ((__skb)->vlan_tci & VLAN_TAG_PRESENT)
+
+void __vlan_hwaccel_put_tag(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci);
+
+void vlan_set_encap_proto(struct sk_buff *skb, struct vlan_hdr *vhdr);
+
+enum { VLAN_ETH_HLEN = 18 };
+
+static inline bool eth_type_vlan(__be16 ethertype) { return false; }
+
+static inline int __vlan_insert_tag(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) {
+ return 1; }
+
+struct vlan_ethhdr
+{
+ __be16 h_vlan_encapsulated_proto;
+};
+
+struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb);
+
+int __vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci);
+int vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci);
+struct net_device *
+__vlan_find_dev_deep_rcu(struct net_device *real_dev, __be16 vlan_proto, u16 vlan_id);
+
+#define skb_vlan_tag_get(__skb) ((__skb)->vlan_tci & ~VLAN_TAG_PRESENT)
+
+extern struct workqueue_struct *tasklet_wq;
+
+int __init netdev_boot_setup(char *str);
+
+static inline void eth_zero_addr(u8 *addr) {
+ memset(addr, 0x00, ETH_ALEN); }
+
+#define FLOW_DISSECTOR_F_PARSE_1ST_FRAG BIT(0)
+
+static inline void eth_broadcast_addr(u8 *addr) {
+ memset(addr, 0xff, ETH_ALEN); }
+
+static inline bool is_multicast_ether_addr(const u8 *addr)
+{
+ return 0x01 & addr[0];
+}
+
+static inline bool is_multicast_ether_addr_64bits(const u8 addr[6+2])
+{
+ return is_multicast_ether_addr(addr);
+}
+
+static inline bool ether_addr_equal_64bits(const u8 addr1[6+2], const u8 addr2[6+2])
+{
+ const u16 *a = (const u16 *)addr1;
+ const u16 *b = (const u16 *)addr2;
+
+ return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) == 0;
+}
+
+bool netdev_uses_dsa(struct net_device *dev);
+
+#include
+
+static inline bool eth_proto_is_802_3(__be16 proto)
+{
+ proto &= htons(0xFF00);
+ return (u16)proto >= (u16)htons(ETH_P_802_3_MIN);
+}
+
+enum {
+ IFF_LIVE_ADDR_CHANGE = 0x100000,
+ IFF_TX_SKB_SHARING = 0x10000,
+};
+
+enum { ARPHRD_ETHER = 1, };
+
+struct neighbour;
+
+struct header_ops
+{
+ int (*create) (struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned int len);
+ int (*parse)(const struct sk_buff *skb, unsigned char *haddr);
+ int (*rebuild)(struct sk_buff *skb);
+ int (*cache)(const struct neighbour *neigh, struct hh_cache *hh, __be16 type);
+ void (*cache_update)(struct hh_cache *hh,
+ const struct net_device *dev,
+ const unsigned char *haddr);
+};
+
+#define DEFAULT_TX_QUEUE_LEN 1000
+
+struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, unsigned char name_assign_type, void (*setup)(struct net_device *), unsigned int txqs, unsigned int rxqs);
+
+enum { NET_NAME_UNKNOWN = 0 };
+
+typedef void (*dr_release_t)(struct device *dev, void *res);
+void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp);
+void devres_free(void *res);
+void devres_add(struct device *dev, void *res);
+
+int scnprintf(char *buf, size_t size, const char *fmt, ...);
+
+void *skb_gro_header_fast(struct sk_buff *skb, unsigned int offset);
+void *skb_gro_header_hard(struct sk_buff *skb, unsigned int hlen);
+void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen, unsigned int offset);
+
+static inline unsigned long compare_ether_header(const void *a, const void *b)
+{
+ u32 *a32 = (u32 *)((u8 *)a + 2);
+ u32 *b32 = (u32 *)((u8 *)b + 2);
+
+ return (*(u16 *)a ^ *(u16 *)b) | (a32[0] ^ b32[0]) |
+ (a32[1] ^ b32[1]) | (a32[2] ^ b32[2]);
+
+}
+
+typedef struct sk_buff **(*gro_receive_t)(struct sk_buff **, struct sk_buff *);
+
+struct offload_callbacks
+{
+ struct sk_buff **(*gro_receive)(struct sk_buff **head, struct sk_buff *skb);
+ int (*gro_complete)(struct sk_buff *skb, int nhoff);
+};
+
+struct packet_offload
+{
+ __be16 type;
+ u16 priority;
+ struct offload_callbacks callbacks;
+};
+
+struct packet_offload *gro_find_receive_by_type(__be16 type);
+
+void skb_gro_pull(struct sk_buff *skb, unsigned int len);
+void skb_gro_postpull_rcsum(struct sk_buff *skb, const void *start, unsigned int len);
+
+struct sk_buff **call_gro_receive(gro_receive_t cb, struct sk_buff **head, struct sk_buff *skb);
+
+void skb_gro_flush_final(struct sk_buff *skb, struct sk_buff **pp, int flush);
+
+struct packet_offload *gro_find_complete_by_type(__be16 type);
+
+
+void dev_hold(struct net_device *dev);
+void dev_put(struct net_device *dev);
+void dev_add_offload(struct packet_offload *po);
+
+#define fs_initcall(x)
+
+#define __weak __attribute__((weak))
+
+unsigned char *arch_get_platform_mac_address(void);
+
+#define to_pci_dev(n) NULL
+struct pci_dev;
+struct device_node * pci_device_to_OF_node(const struct pci_dev *pdev);
+
+int dev_is_pci(struct device *dev);
+
+void skb_init();
+int module_usbnet_init();
+int module_wdm_driver_init();
+int module_cdc_ncm_driver_init();
+int module_cdc_mbim_driver_init();
+
+enum {
+ IPV6_ADDR_UNICAST = 0x1
+};
+
+enum {
+ NDISC_NEIGHBOUR_SOLICITATION = 135
+};
+
+struct ipv6_devconf
+{
+ __s32 forwarding;
+};
+
+struct inet6_dev
+{
+ struct ipv6_devconf cnf;
+};
+
+struct ipv6_stub
+{
+ void (*ndisc_send_na)(struct net_device *dev, const struct in6_addr *daddr,
+ const struct in6_addr *solicited_addr,
+ bool router, bool solicited, bool override, bool inc_opt);
+};
+
+extern const struct ipv6_stub *ipv6_stub;
+
+struct icmp6hdr
+{
+ __u8 icmp6_type;
+ __u8 icmp6_code;
+};
+
+
+struct inet6_dev *in6_dev_get(const struct net_device *dev);
+void in6_dev_put(struct inet6_dev *idev);
+
+struct nd_msg
+{
+ struct icmp6hdr icmph;
+ struct in6_addr target;
+};
+
+#define PMSG_SUSPEND ((struct pm_message) { .event = PM_EVENT_SUSPEND } )
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#endif /* _SRC__DRIVERS__USB_HID__LX_EMUL_H_ */
diff --git a/repos/dde_linux/src/drivers/usb_modem/lxc.c b/repos/dde_linux/src/drivers/usb_modem/lxc.c
new file mode 100644
index 0000000000..c78512773e
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/lxc.c
@@ -0,0 +1,247 @@
+/*
+ * \brief Linux emulation code
+ * \author Josef Soentgen
+ * \author Sebastian Sumpf
+ * \date 2014-03-07
+ */
+
+/*
+ * Copyright (C) 2014-2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+/* linux includes */
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+
+struct Skb skb_helper(struct sk_buff *skb)
+{
+ struct Skb helper;
+
+ skb_push(skb, ETH_HLEN);
+
+ helper.packet = skb->data;
+ helper.packet_size = ETH_HLEN;
+ helper.frag = 0;
+ helper.frag_size = 0;
+
+ /**
+ * If received packets are too large (as of now 128 bytes) the actually
+ * payload is put into a fragment. Otherwise the payload is stored directly
+ * in the sk_buff.
+ */
+ if (skb_shinfo(skb)->nr_frags) {
+ if (skb_shinfo(skb)->nr_frags > 1)
+ printk("more than 1 fragment in skb: %p nr_frags: %d", skb,
+ skb_shinfo(skb)->nr_frags);
+
+ skb_frag_t *f = &skb_shinfo(skb)->frags[0];
+ helper.frag = skb_frag_address(f);
+ helper.frag_size = skb_frag_size(f);
+ }
+ else
+ helper.packet_size += skb->len;
+
+ return helper;
+}
+
+
+struct sk_buff *lxc_alloc_skb(size_t len, size_t headroom)
+{
+ struct sk_buff *skb = alloc_skb(len + headroom, GFP_KERNEL | GFP_LX_DMA);
+ skb_reserve(skb, headroom);
+ return skb;
+}
+
+
+unsigned char *lxc_skb_put(struct sk_buff *skb, size_t len)
+{
+ return skb_put(skb, len);
+}
+
+
+/**
+ * cdc_parse_cdc_header - parse the extra headers present in CDC devices
+ * @hdr: the place to put the results of the parsing
+ * @intf: the interface for which parsing is requested
+ * @buffer: pointer to the extra headers to be parsed
+ * @buflen: length of the extra headers
+ *
+ * This evaluates the extra headers present in CDC devices which
+ * bind the interfaces for data and control and provide details
+ * about the capabilities of the device.
+ *
+ * Return: number of descriptors parsed or -EINVAL
+ * if the header is contradictory beyond salvage
+ */
+
+int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
+ struct usb_interface *intf,
+ u8 *buffer,
+ int buflen)
+{
+ /* duplicates are ignored */
+ struct usb_cdc_union_desc *union_header = NULL;
+
+ /* duplicates are not tolerated */
+ struct usb_cdc_header_desc *header = NULL;
+ struct usb_cdc_ether_desc *ether = NULL;
+ struct usb_cdc_mdlm_detail_desc *detail = NULL;
+ struct usb_cdc_mdlm_desc *desc = NULL;
+
+ unsigned int elength;
+ int cnt = 0;
+
+ memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header));
+ hdr->phonet_magic_present = false;
+ while (buflen > 0) {
+ elength = buffer[0];
+ if (!elength) {
+ dev_err(&intf->dev, "skipping garbage byte\n");
+ elength = 1;
+ goto next_desc;
+ }
+ if ((buflen < elength) || (elength < 3)) {
+ dev_err(&intf->dev, "invalid descriptor buffer length\n");
+ break;
+ }
+ if (buffer[1] != USB_DT_CS_INTERFACE) {
+ dev_err(&intf->dev, "skipping garbage\n");
+ goto next_desc;
+ }
+
+ switch (buffer[2]) {
+ case USB_CDC_UNION_TYPE: /* we've found it */
+ if (elength < sizeof(struct usb_cdc_union_desc))
+ goto next_desc;
+ if (union_header) {
+ dev_err(&intf->dev, "More than one union descriptor, skipping ...\n");
+ goto next_desc;
+ }
+ union_header = (struct usb_cdc_union_desc *)buffer;
+ break;
+ case USB_CDC_COUNTRY_TYPE:
+ if (elength < sizeof(struct usb_cdc_country_functional_desc))
+ goto next_desc;
+ hdr->usb_cdc_country_functional_desc =
+ (struct usb_cdc_country_functional_desc *)buffer;
+ break;
+ case USB_CDC_HEADER_TYPE:
+ if (elength != sizeof(struct usb_cdc_header_desc))
+ goto next_desc;
+ if (header)
+ return -EINVAL;
+ header = (struct usb_cdc_header_desc *)buffer;
+ break;
+ case USB_CDC_ACM_TYPE:
+ if (elength < sizeof(struct usb_cdc_acm_descriptor))
+ goto next_desc;
+ hdr->usb_cdc_acm_descriptor =
+ (struct usb_cdc_acm_descriptor *)buffer;
+ break;
+ case USB_CDC_ETHERNET_TYPE:
+ if (elength != sizeof(struct usb_cdc_ether_desc))
+ goto next_desc;
+ if (ether)
+ return -EINVAL;
+ ether = (struct usb_cdc_ether_desc *)buffer;
+ break;
+ case USB_CDC_CALL_MANAGEMENT_TYPE:
+ if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor))
+ goto next_desc;
+ hdr->usb_cdc_call_mgmt_descriptor =
+ (struct usb_cdc_call_mgmt_descriptor *)buffer;
+ break;
+ case USB_CDC_DMM_TYPE:
+ if (elength < sizeof(struct usb_cdc_dmm_desc))
+ goto next_desc;
+ hdr->usb_cdc_dmm_desc =
+ (struct usb_cdc_dmm_desc *)buffer;
+ break;
+ case USB_CDC_MDLM_TYPE:
+ if (elength < sizeof(struct usb_cdc_mdlm_desc *))
+ goto next_desc;
+ if (desc)
+ return -EINVAL;
+ desc = (struct usb_cdc_mdlm_desc *)buffer;
+ break;
+ case USB_CDC_MDLM_DETAIL_TYPE:
+ if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *))
+ goto next_desc;
+ if (detail)
+ return -EINVAL;
+ detail = (struct usb_cdc_mdlm_detail_desc *)buffer;
+ break;
+ case USB_CDC_NCM_TYPE:
+ if (elength < sizeof(struct usb_cdc_ncm_desc))
+ goto next_desc;
+ hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer;
+ break;
+ case USB_CDC_MBIM_TYPE:
+ if (elength < sizeof(struct usb_cdc_mbim_desc))
+ goto next_desc;
+
+ hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer;
+ break;
+ case USB_CDC_MBIM_EXTENDED_TYPE:
+ if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
+ break;
+ hdr->usb_cdc_mbim_extended_desc =
+ (struct usb_cdc_mbim_extended_desc *)buffer;
+ break;
+ case CDC_PHONET_MAGIC_NUMBER:
+ hdr->phonet_magic_present = true;
+ break;
+ default:
+ /*
+ * there are LOTS more CDC descriptors that
+ * could legitimately be found here.
+ */
+ dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n",
+ buffer[2], elength);
+ goto next_desc;
+ }
+ cnt++;
+next_desc:
+ buflen -= elength;
+ buffer += elength;
+ }
+ hdr->usb_cdc_union_desc = union_header;
+ hdr->usb_cdc_header_desc = header;
+ hdr->usb_cdc_mdlm_detail_desc = detail;
+ hdr->usb_cdc_mdlm_desc = desc;
+ hdr->usb_cdc_ether_desc = ether;
+ return cnt;
+}
+
+struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
+ unsigned ifnum)
+{
+ struct usb_host_config *config = dev->actconfig;
+ int i;
+
+ if (!config) {
+ lx_printf("No config for %u\n", ifnum);
+ return NULL;
+ }
+
+ for (i = 0; i < config->desc.bNumInterfaces; i++) {
+ if (config->interface[i]->altsetting[0]
+ .desc.bInterfaceNumber == ifnum) {
+ return config->interface[i];
+ }
+ }
+
+ lx_printf("No interface for %u\n");
+ return NULL;
+}
+EXPORT_SYMBOL(cdc_parse_cdc_header);
diff --git a/repos/dde_linux/src/drivers/usb_modem/lxc.h b/repos/dde_linux/src/drivers/usb_modem/lxc.h
new file mode 100644
index 0000000000..96c9b55abb
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/lxc.h
@@ -0,0 +1,41 @@
+/*
+ * \brief Lx C env
+ * \author Josef Soentgen
+ * \author Sebastian Sumpf
+ * \date 2016-03-04
+ */
+
+/*
+ * Copyright (C) 2016-2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#ifndef _LXC_H_
+#define _LXC_H_
+
+/*
+ * The sk_buff struct contains empty array members whose
+ * size differs between C and C++. Since we want to access
+ * certain members of the sk_buff from C++ we have to use
+ * a uniform format useable from both languages.W
+ *
+ * Note: we pull struct skb_buff as well as size_t from
+ * headers included before this one.
+ */
+struct Skb
+{
+ void *packet;
+ size_t packet_size;
+ void *frag;
+ size_t frag_size;
+};
+
+struct Skb skb_helper(struct sk_buff *skb);
+bool is_eapol(struct sk_buff *skb);
+
+struct sk_buff *lxc_alloc_skb(size_t len, size_t headroom);
+unsigned char *lxc_skb_put(struct sk_buff *skb, size_t len);
+
+#endif /* _LXC_H_ */
diff --git a/repos/dde_linux/src/drivers/usb_modem/main.cc b/repos/dde_linux/src/drivers/usb_modem/main.cc
new file mode 100644
index 0000000000..cf202014ab
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/main.cc
@@ -0,0 +1,247 @@
+/*
+ * \brief USB modem driver
+ * \author Stefan Kalkowski
+ * \author Sebastian Sumpf
+ * \date 2018-06-07
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+struct workqueue_struct *tasklet_wq;
+
+
+void Driver::Device::scan_altsettings(usb_interface * iface,
+ unsigned iface_idx, unsigned alt_idx)
+{
+ Usb::Interface_descriptor iface_desc;
+ usb.interface_descriptor(iface_idx, alt_idx, &iface_desc);
+ Genode::memcpy(&iface->altsetting[alt_idx].desc, &iface_desc,
+ sizeof(usb_interface_descriptor));
+ if (iface_desc.active)
+ iface->cur_altsetting = &iface->altsetting[alt_idx];
+
+ Usb::Interface_extra iface_extra;
+ if (usb.interface_extra(iface_idx, alt_idx, &iface_extra)) {
+ iface->altsetting[alt_idx].extra = (unsigned char *)kzalloc(iface_extra.length, 0);
+ Genode::memcpy(iface->altsetting[alt_idx].extra, iface_extra.data,
+ iface_extra.length);
+ iface->altsetting[alt_idx].extralen = iface_extra.length;
+ }
+
+ iface->altsetting[alt_idx].endpoint = (usb_host_endpoint*)
+ kzalloc(sizeof(usb_host_endpoint)*iface->altsetting[alt_idx].desc.bNumEndpoints, GFP_KERNEL);
+
+ for (unsigned i = 0; i < iface->altsetting[alt_idx].desc.bNumEndpoints; i++) {
+ Usb::Endpoint_descriptor ep_desc;
+ usb.endpoint_descriptor(iface_idx, alt_idx, i, &ep_desc);
+ Genode::memcpy(&iface->altsetting[alt_idx].endpoint[i].desc,
+ &ep_desc, sizeof(usb_endpoint_descriptor));
+ int epnum = usb_endpoint_num(&iface->altsetting[alt_idx].endpoint[i].desc);
+ if (usb_endpoint_dir_out(&iface->altsetting[alt_idx].endpoint[i].desc))
+ udev->ep_out[epnum] = &iface->altsetting[alt_idx].endpoint[i];
+ else
+ udev->ep_in[epnum] = &iface->altsetting[alt_idx].endpoint[i];
+ }
+}
+
+
+void Driver::Device::scan_interfaces(unsigned iface_idx)
+{
+ struct usb_interface * iface =
+ (usb_interface*) kzalloc(sizeof(usb_interface), GFP_KERNEL);
+ iface->num_altsetting = usb.alt_settings(iface_idx);
+ iface->altsetting = (usb_host_interface*)
+ kzalloc(sizeof(usb_host_interface)*iface->num_altsetting, GFP_KERNEL);
+ iface->dev.parent = &udev->dev;
+ iface->dev.bus = (bus_type*) 0xdeadbeef;
+
+ for (unsigned i = 0; i < iface->num_altsetting; i++)
+ scan_altsettings(iface, iface_idx, i);
+
+ udev->config->interface[iface_idx] = iface;
+}
+
+
+void Driver::Device::set_config(Usb::Device_descriptor const &desc)
+{
+ /* Huawei ME906s */
+ if (desc.vendor_id == 0x12d1 && desc.product_id == 0x15c1) {
+ Genode::log("Found Huawei ME906s choosing configuration #3");
+ /* set unconfigured first */
+ Sync_packet(usb).config(0);
+ /* configuration 3 is MBIM for this modem */
+ Sync_packet(usb).config(3);
+ }
+}
+
+
+void Driver::Device::register_device()
+{
+ if (udev) {
+ Genode::error("device already registered!");
+ return;
+ }
+
+ Usb::Device_descriptor dev_desc;
+ Usb::Config_descriptor config_desc;
+ usb.config_descriptor(&dev_desc, &config_desc);
+
+ udev = (usb_device*) kzalloc(sizeof(usb_device), GFP_KERNEL);
+ udev->bus = (usb_bus*) kzalloc(sizeof(usb_bus), GFP_KERNEL);
+ udev->config = (usb_host_config*) kzalloc(sizeof(usb_host_config), GFP_KERNEL);
+ udev->bus->bus_name = "usbbus";
+ udev->bus->controller = (device*) (&usb);
+ udev->bus->sysdev = (device*) (&driver.terminal_root);
+
+ udev->descriptor.idVendor = dev_desc.vendor_id;
+ udev->descriptor.idProduct = dev_desc.product_id;
+ udev->descriptor.bcdDevice = dev_desc.device_release;
+
+
+ set_config(dev_desc);
+ /* re-read config */
+ usb.config_descriptor(&dev_desc, &config_desc);
+
+ /* device specific configuration profile */
+
+ for (unsigned i = 0; i < config_desc.num_interfaces; i++)
+ scan_interfaces(i);
+
+ udev->actconfig = udev->config;
+ udev->config->desc.bNumInterfaces = config_desc.num_interfaces;
+
+ for (unsigned i = 0; i < config_desc.num_interfaces; i++) {
+ struct usb_device_id id;
+ probe_interface(udev->config->interface[i], &id);
+ }
+
+ driver.env.parent().announce(driver.ep.manage(driver.root));
+ driver.env.parent().announce(driver.ep.manage(driver.terminal_root));
+}
+
+
+void Driver::Device::unregister_device()
+{
+ for (unsigned i = 0; i < USB_MAXINTERFACES; i++) {
+ if (!udev->config->interface[i]) break;
+ else remove_interface(udev->config->interface[i]);
+ }
+ kfree(udev->bus);
+ kfree(udev->config);
+ kfree(udev);
+ udev = nullptr;
+}
+
+
+void Driver::Device::state_task_entry(void * arg)
+{
+ Device & dev = *reinterpret_cast(arg);
+
+ for (;;) {
+ if (dev.usb.plugged() && !dev.udev)
+ dev.register_device();
+
+ if (!dev.usb.plugged() && dev.udev)
+ dev.unregister_device();
+
+ Lx::scheduler().current()->block_and_schedule();
+ }
+}
+
+
+void Driver::Device::urb_task_entry(void * arg)
+{
+ Device & dev = *reinterpret_cast(arg);
+
+ for (;;) {
+ while (dev.udev && dev.usb.source()->ack_avail()) {
+ Usb::Packet_descriptor p = dev.usb.source()->get_acked_packet();
+ if (p.completion) p.completion->complete(p);
+ dev.usb.source()->release_packet(p);
+ }
+ Lx::scheduler().current()->block_and_schedule();
+ }
+}
+
+
+Driver::Device::Device(Driver & driver, Label label)
+: label(label),
+ driver(driver),
+ env(driver.env),
+ alloc(driver.alloc),
+ state_task(env.ep(), state_task_entry, reinterpret_cast(this),
+ "usb_state", Lx::Task::PRIORITY_0, Lx::scheduler()),
+ urb_task(env.ep(), urb_task_entry, reinterpret_cast(this),
+ "usb_urb", Lx::Task::PRIORITY_0, Lx::scheduler())
+{
+ usb.tx_channel()->sigh_ack_avail(urb_task.handler);
+ driver.devices.insert(&le);
+}
+
+
+Driver::Device::~Device()
+{
+ driver.devices.remove(&le);
+ if (udev) unregister_device();
+}
+
+
+void Driver::main_task_entry(void * arg)
+{
+ LX_MUTEX_INIT(wdm_mutex);
+
+ Driver * driver = reinterpret_cast(arg);
+
+ tasklet_wq = alloc_workqueue("tasklet_wq", 0, 0);
+
+ skb_init();
+ module_usbnet_init();
+ module_wdm_driver_init();
+ module_cdc_ncm_driver_init();
+ module_cdc_mbim_driver_init();
+
+ static Device dev(*driver, Label(""));
+
+ for (;;) Lx::scheduler().current()->block_and_schedule();
+}
+
+
+Driver::Driver(Genode::Env &env) : env(env)
+{
+ Genode::log("--- USB net driver ---");
+
+ Lx_kit::construct_env(env);
+ Lx::scheduler(&env);
+ Lx::malloc_init(env, heap);
+ Lx::timer(&env, &ep, &heap, &jiffies);
+ Lx::Work::work_queue(&heap);
+
+ main_task.construct(env.ep(), main_task_entry, reinterpret_cast(this),
+ "main", Lx::Task::PRIORITY_0, Lx::scheduler());
+
+ /* give all task a first kick before returning */
+ Lx::scheduler().schedule();
+}
+
+
+void Component::construct(Genode::Env &env)
+{
+ env.exec_static_constructors();
+ static Driver driver(env);
+}
diff --git a/repos/dde_linux/src/drivers/usb_modem/target.mk b/repos/dde_linux/src/drivers/usb_modem/target.mk
new file mode 100644
index 0000000000..d02c441314
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/target.mk
@@ -0,0 +1,28 @@
+TARGET := usb_modem_drv
+SRC_C := dummies.c lxc.c
+SRC_CC := main.cc lx_emul.cc component.cc terminal.cc
+SRC_CC += printf.cc timer.cc scheduler.cc malloc.cc env.cc work.cc
+
+LIBS := base usb_modem_include lx_kit_setjmp
+
+USB_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_modem
+
+INC_DIR += $(PRG_DIR)
+INC_DIR += $(REP_DIR)/src/include
+
+SRC_C += drivers/net/usb/usbnet.c
+SRC_C += drivers/net/usb/cdc_ncm.c
+SRC_C += drivers/net/usb/cdc_mbim.c
+SRC_C += drivers/usb/class/cdc-wdm.c
+SRC_C += net/core/skbuff.c
+SRC_C += net/ethernet/eth.c
+
+CC_C_OPT += -Wno-comment -Wno-int-conversion -Wno-incompatible-pointer-types \
+ -Wno-unused-variable -Wno-pointer-sign -Wno-uninitialized \
+ -Wno-maybe-uninitialized -Wno-format -Wno-discarded-qualifiers \
+ -Wno-unused-function -Wno-unused-but-set-variable
+
+CC_CXX_WARN_STRICT =
+
+vpath %.c $(USB_CONTRIB_DIR)
+vpath %.cc $(REP_DIR)/src/lx_kit
diff --git a/repos/dde_linux/src/drivers/usb_modem/terminal.cc b/repos/dde_linux/src/drivers/usb_modem/terminal.cc
new file mode 100644
index 0000000000..c65281c95b
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/terminal.cc
@@ -0,0 +1,100 @@
+/*
+ * \brief USB modem driver terminal service
+ * \author Sebastian Sumpf
+ * \author Josef Soentgen
+ * \date 2020-12-02
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+
+using namespace Terminal;
+
+
+Session_component::Session_component(Genode::Env &env,
+ Genode::size_t io_buffer_size,
+ usb_class_driver *class_driver)
+:
+ _io_buffer(env.ram(), env.rm(), io_buffer_size),
+ _io_buffer_size(io_buffer_size),
+ _class_driver(class_driver)
+{
+ if (class_driver == nullptr) {
+ Genode::error("No class driver for terminal");
+ throw Genode::Service_denied();
+ }
+
+ Lx::scheduler().schedule();
+}
+
+void Session_component::_run_wdm_device(void *args)
+{
+ Session_component *session = static_cast(args);
+
+ usb_class_driver *driver = session->_class_driver;
+
+ int err = -1;
+ struct file file { };
+
+ if ((err = driver->fops->open(nullptr, &file))) {
+ Genode::error("Could not open WDM device: ", err);
+ return;
+ }
+
+ session->_wdm_device = file.private_data;
+ Lx::scheduler().current()->block_and_schedule();
+ //XXX: close
+}
+
+
+void Session_component::_run_wdm_write(void *args)
+{
+ Lx::scheduler().current()->block_and_schedule();
+
+ Session_component *session = static_cast(args);
+ usb_class_driver *driver = session->_class_driver;
+
+ struct file file { .private_data = session->_wdm_device };
+
+ while (1) {
+ ssize_t length = driver->fops->write(&file, session->buffer(),
+ session->_data_avail, nullptr);
+ if (length < 0) {
+ Genode::error("WDM write error: ", length);
+ }
+
+ session->_schedule_read();
+ Lx::scheduler().current()->block_and_schedule();
+ }
+}
+
+
+void Session_component::_run_wdm_read(void *args)
+{
+ Lx::scheduler().current()->block_and_schedule();
+ Session_component *session = static_cast(args);
+
+ usb_class_driver *driver = session->_class_driver;
+
+ struct file file { .private_data = session->_wdm_device };
+
+ while (1) {
+ ssize_t length = driver->fops->read(&file, session->buffer(), 0x1000, nullptr);
+ if (length > 0) {
+ session->_data_avail = length;
+ session->signal_data_avail();
+ }
+ Lx::scheduler().current()->block_and_schedule();
+ }
+}
diff --git a/repos/dde_linux/src/drivers/usb_modem/terminal.h b/repos/dde_linux/src/drivers/usb_modem/terminal.h
new file mode 100644
index 0000000000..a015e9ca09
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/terminal.h
@@ -0,0 +1,180 @@
+/*
+ * \brief Service providing the 'Terminal_session' interface for a Linux file
+ * \author Josef Soentgen
+ * \author Sebastian Sumpf
+ * \date 2020-07-09
+ */
+
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef _SRC__DRIVERS__USB_MODEM__TERMINAL_H_
+#define _SRC__DRIVERS__USB_MODEM__TERMINAL_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace Terminal {
+ class Session_component;
+ class Root;
+}
+
+extern "C" {
+ struct usb_class_driver;
+}
+
+class Terminal::Session_component : public Genode::Rpc_object
+{
+ using size_t = Genode::size_t;
+
+ private:
+
+ enum State { WRITE, READ };
+
+ Genode::Attached_ram_dataspace _io_buffer;
+ Genode::Signal_context_capability _read_avail_sigh { };
+
+ State _state { WRITE };
+ size_t _data_avail { 0 };
+ size_t _io_buffer_size;
+ void *_wdm_device { nullptr };
+ usb_class_driver *_class_driver { nullptr };
+
+ static void _run_wdm_device(void *args);
+ static void _run_wdm_write(void *args);
+ static void _run_wdm_read(void *args);
+
+ Lx::Task _task_write { _run_wdm_write, this, "wdm_task_write",
+ Lx::Task::PRIORITY_1, Lx::scheduler() };
+
+ Lx::Task _task_read { _run_wdm_read, this, "wdm_task_read",
+ Lx::Task::PRIORITY_1, Lx::scheduler() };
+
+ Lx::Task _task_device { _run_wdm_device, this, "wdm_task_devie",
+ Lx::Task::PRIORITY_1, Lx::scheduler() };
+ void _schedule_read()
+ {
+ _task_read.unblock();
+ }
+
+ public:
+
+ Session_component(Genode::Env &env,
+ Genode::size_t io_buffer_size,
+ usb_class_driver *class_driver);
+
+ /********************************
+ ** Terminal session interface **
+ ********************************/
+
+ Size size() override { return Size(0, 0); }
+
+ bool avail() override
+ {
+ return _data_avail > 0;
+ }
+
+ Genode::size_t _read(Genode::size_t dst_len)
+ {
+ if (_state != READ) return 0;
+
+ size_t length = Genode::min(dst_len, _data_avail);
+ if (dst_len < _data_avail)
+ Genode::warning("dst_len < data_avail (", dst_len, " < ", _data_avail, ") not supported");
+
+ _data_avail -= length;
+
+ if (_data_avail == 0) {
+ _state = WRITE;
+ _schedule_read();
+ }
+
+ return length;
+ }
+
+ Genode::size_t _write(Genode::size_t num_bytes)
+ {
+ if (_state == READ) return 0;
+
+ _data_avail = num_bytes;
+ _state = WRITE;
+
+ _task_write.unblock();
+ Lx::scheduler().schedule();
+
+ return 0;
+ }
+
+ Genode::Dataspace_capability _dataspace()
+ {
+ return _io_buffer.cap();
+ }
+
+ void read_avail_sigh(Genode::Signal_context_capability sigh) override
+ {
+ _read_avail_sigh = sigh;
+ }
+
+ void connected_sigh(Genode::Signal_context_capability sigh) override
+ {
+ Genode::Signal_transmitter(sigh).submit();
+ }
+
+ void size_changed_sigh(Genode::Signal_context_capability) override { }
+
+ size_t read(void *, size_t) override { return 0; }
+ size_t write(void const *, size_t) override { return 0; }
+
+ char *buffer() { return _io_buffer.local_addr(); }
+
+ void signal_data_avail()
+ {
+ if (_read_avail_sigh.valid() == false) return;
+ _state = READ;
+ Genode::Signal_transmitter(_read_avail_sigh).submit();
+ }
+};
+
+
+class Terminal::Root : public Genode::Root_component
+{
+ private:
+
+ Genode::Env &_env;
+ usb_class_driver *_class_driver { nullptr };
+
+ protected:
+
+ Session_component *_create_session(const char *args) override
+ {
+ Genode::size_t const io_buffer_size = 4096ul;
+ return new (md_alloc())
+ Session_component(_env, io_buffer_size, _class_driver);
+ }
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Root(Genode::Env &env,
+ Genode::Allocator &md_alloc)
+ :
+ Genode::Root_component(&env.ep().rpc_ep(), &md_alloc),
+ _env(env)
+ { }
+
+ void class_driver(usb_class_driver *class_driver) { _class_driver = class_driver; }
+};
+
+#endif /* _SRC__DRIVERS__USB_MODEM__TERMINAL_H_ */
diff --git a/repos/dde_linux/usb_modem.list b/repos/dde_linux/usb_modem.list
new file mode 100644
index 0000000000..d7a931dfe9
--- /dev/null
+++ b/repos/dde_linux/usb_modem.list
@@ -0,0 +1,68 @@
+linux-x.x.x/drivers/net/usb/cdc_mbim.c
+linux-x.x.x/drivers/net/usb/cdc_ncm.c
+linux-x.x.x/drivers/net/usb/usbnet.c
+linux-x.x.x/drivers/usb/class/cdc-wdm.c
+linux-x.x.x/net/core/skbuff.c
+linux-x.x.x/net/ethernet/eth.c
+linux-x.x.x/include/asm-generic/atomic64.h
+linux-x.x.x/include/asm-generic/bitops/__ffs.h
+linux-x.x.x/include/asm-generic/bitops/__fls.h
+linux-x.x.x/include/asm-generic/bitops/ffs.h
+linux-x.x.x/include/asm-generic/bitops/fls.h
+linux-x.x.x/include/asm-generic/bitops/fls64.h
+linux-x.x.x/include/asm-generic/bitops/non-atomic.h
+linux-x.x.x/include/linux/cgroup-defs.h
+linux-x.x.x/include/linux/errqueue.h
+linux-x.x.x/include/linux/ethtool.h
+linux-x.x.x/include/linux/if_ether.h
+linux-x.x.x/include/linux/kfifo.h
+linux-x.x.x/include/linux/list.h
+linux-x.x.x/include/linux/list_nulls.h
+linux-x.x.x/include/linux/log2.h
+linux-x.x.x/include/linux/mii.h
+linux-x.x.x/include/linux/mdio.h
+linux-x.x.x/include/linux/mod_devicetable.h
+linux-x.x.x/include/linux/netdev_features.h
+linux-x.x.x/include/linux/net.h
+linux-x.x.x/include/linux/phy.h
+linux-x.x.x/include/linux/rbtree.h
+linux-x.x.x/include/linux/rculist.h
+linux-x.x.x/include/linux/rculist_nulls.h
+linux-x.x.x/include/linux/refcount.h
+linux-x.x.x/include/linux/rndis.h
+linux-x.x.x/include/linux/skbuff.h
+linux-x.x.x/include/linux/socket.h
+linux-x.x.x/include/linux/swab.h
+linux-x.x.x/include/linux/usb.h
+linux-x.x.x/include/linux/usb/ch9.h
+linux-x.x.x/include/linux/usb/cdc.h
+linux-x.x.x/include/linux/usb/cdc_ncm.h
+linux-x.x.x/include/linux/usb/cdc-wdm.h
+linux-x.x.x/include/linux/usb/rndis_host.h
+linux-x.x.x/include/linux/usb/usbnet.h
+linux-x.x.x/include/net/dst.h
+linux-x.x.x/include/net/dst_ops.h
+linux-x.x.x/include/net/neighbour.h
+linux-x.x.x/include/net/sock.h
+linux-x.x.x/include/net/tcp_states.h
+linux-x.x.x/include/net/tso.h
+linux-x.x.x/include/uapi/linux/byteorder/little_endian.h
+linux-x.x.x/include/uapi/linux/capability.h
+linux-x.x.x/include/uapi/linux/errqueue.h
+linux-x.x.x/include/uapi/linux/ethtool.h
+linux-x.x.x/include/uapi/linux/if.h
+linux-x.x.x/include/uapi/linux/if_ether.h
+linux-x.x.x/include/uapi/linux/if_link.h
+linux-x.x.x/include/uapi/linux/if_packet.h
+linux-x.x.x/include/uapi/linux/libc-compat.h
+linux-x.x.x/include/uapi/linux/mdio.h
+linux-x.x.x/include/uapi/linux/mii.h
+linux-x.x.x/include/uapi/linux/neighbour.h
+linux-x.x.x/include/uapi/linux/net.h
+linux-x.x.x/include/uapi/linux/rtnetlink.h
+linux-x.x.x/include/uapi/linux/socket.h
+linux-x.x.x/include/uapi/linux/swab.h
+linux-x.x.x/include/uapi/linux/net_tstamp.h
+linux-x.x.x/include/uapi/linux/usb/cdc.h
+linux-x.x.x/include/uapi/linux/usb/ch9.h
+linux-x.x.x/include/uapi/linux/usb/cdc-wdm.h