dde_linux: USB MBIM modem driver support

Thise driver supports USB LTE modems for Huawais' ME906s through MBIM
and provides a traditional Nic session. The "control" interface is a
Terminal session, which can be used via libmbim/mbimcli.

issue #3822
This commit is contained in:
Sebastian Sumpf 2020-07-09 15:10:21 +02:00 committed by Norman Feske
parent a0fb944721
commit 0beda6bca4
19 changed files with 4216 additions and 2 deletions

View File

@ -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

View File

@ -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 =

View File

@ -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);

View File

@ -1 +1 @@
1753147f7ebf2d67447c6175687cf031605031b3
17ca56af6e9fde7df9b2ab6bdaa8f9b65c3bc533

View File

@ -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)

View File

@ -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:
!<start name="usb_modem_drv">
! <resource name="RAM" quantum="10M"/>
! <provides>
! <service name="Nic"/>
! <service name="Terminal"/>
! </provides>
! <config mac="02:00:00:00:01:01" />
!</start>
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.

View File

@ -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 <component.h>
#include <lx_emul.h>
extern "C" {
#include <lxc.h>
};
void Session_component::_run_rx_task(void * args)
{
Rx_data *data = static_cast<Rx_data*>(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<Tx_data*>(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();
}

View File

@ -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 <nic/component.h>
#include <root/component.h>
#include <lx_kit/scheduler.h>
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<Session_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<Session_component>(&env.ep().rpc_ep(), &md_alloc),
_env(env), _md_alloc(md_alloc)
{ }
};
#endif /* _SRC__DRIVERS__NIC__FEC__COMPONENT_H_ */

View File

@ -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 <base/allocator_avl.h>
#include <base/attached_rom_dataspace.h>
#include <base/heap.h>
#include <usb_session/connection.h>
#include <lx_kit/scheduler.h>
#include <component.h>
#include <terminal.h>
#include <lx_emul.h>
#include <lx_emul/extern_c_begin.h>
#include <linux/usb.h>
#include <lx_emul/extern_c_end.h>
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<Task> handler;
void handle_signal()
{
task.unblock();
Lx::scheduler().schedule();
}
template <typename... ARGS>
Task(Genode::Entrypoint & ep, ARGS &&... args)
: task(args...), handler(ep, *this, &Task::handle_signal) {}
};
struct Device
{
using Le = Genode::List_element<Device>;
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<Device::Le>
{
template <typename FN>
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<Task> main_task;
Genode::Constructible<Genode::Attached_rom_dataspace> report_rom;
Driver(Genode::Env &env);
static void main_task_entry(void *);
};
#endif /* _SRC__DRIVERS__USB_HID__DRIVER_H_ */

View File

@ -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 <lx_emul.h>
#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;

View File

@ -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 <base/env.h>
#include <usb_session/client.h>
#include <base/snprintf.h>
#include <nic_session/nic_session.h>
#include <util/xml_node.h>
#include <driver.h>
#include <lx_emul.h>
#include <lx_emul/extern_c_begin.h>
#include <linux/usb.h>
#include <lx_emul/extern_c_end.h>
#define TRACE do { ; } while (0)
#include <lx_emul/impl/delay.h>
#include <lx_emul/impl/slab.h>
#include <lx_emul/impl/work.h>
#include <lx_emul/impl/spinlock.h>
#include <lx_emul/impl/mutex.h>
#include <lx_emul/impl/sched.h>
#include <lx_emul/impl/timer.h>
#include <lx_emul/impl/completion.h>
#include <lx_emul/impl/wait.h>
#include <lx_emul/impl/usb.h>
#include <lx_kit/backend_alloc.h>
#include <lx_emul/extern_c_begin.h>
#include <lx_emul/impl/kernel.h>
#include <linux/mii.h>
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 <lx_emul/extern_c_end.h>
class Addr_to_page_mapping : public Genode::List<Addr_to_page_mapping>::Element
{
private:
struct page *_page { nullptr };
static Genode::List<Addr_to_page_mapping> & _list()
{
static Genode::List<Addr_to_page_mapping> _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<Lx_driver>;
using List = Genode::List<Element>;
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<usb_device_id*>(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<Usb::Connection *>(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<Usb::Connection *>(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<Terminal::Root *>(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<Lx_driver>;
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 <nic> - 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<Session_component*>(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<Session_component*>(dev->session_component)->link_state(true);
}
int netif_rx(struct sk_buff * skb)
{
if (skb->dev->session_component)
reinterpret_cast<Session_component*>(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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <lx_emul.h>
#include <linux/skbuff.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/cdc.h>
/* local includes */
#include <lxc.h>
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);

View File

@ -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_ */

View File

@ -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 <base/component.h>
#include <driver.h>
#include <lx_emul.h>
#include <lx_kit/env.h>
#include <lx_kit/malloc.h>
#include <lx_kit/scheduler.h>
#include <lx_kit/timer.h>
#include <lx_kit/work.h>
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<Device*>(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<Device*>(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<void*>(this),
"usb_state", Lx::Task::PRIORITY_0, Lx::scheduler()),
urb_task(env.ep(), urb_task_entry, reinterpret_cast<void*>(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<Driver*>(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<void*>(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);
}

View File

@ -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

View File

@ -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 <terminal.h>
#include <lx_emul.h>
#include <lx_emul/extern_c_begin.h>
#include <linux/usb.h>
#include <lx_emul/extern_c_end.h>
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<Session_component *>(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<Session_component *>(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<Session_component *>(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();
}
}

View File

@ -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 <base/attached_ram_dataspace.h>
#include <base/log.h>
#include <os/session_policy.h>
#include <root/component.h>
#include <terminal_session/terminal_session.h>
#include <lx_kit/scheduler.h>
namespace Terminal {
class Session_component;
class Root;
}
extern "C" {
struct usb_class_driver;
}
class Terminal::Session_component : public Genode::Rpc_object<Session, Session_component>
{
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<char>(); }
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<Session_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<Session_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_ */

View File

@ -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