diff --git a/repos/dde_ipxe/recipes/src/ipxe_nic_drv/used_apis b/repos/dde_ipxe/recipes/src/ipxe_nic_drv/used_apis
index b343b02413..6c0feab87e 100644
--- a/repos/dde_ipxe/recipes/src/ipxe_nic_drv/used_apis
+++ b/repos/dde_ipxe/recipes/src/ipxe_nic_drv/used_apis
@@ -1,5 +1,7 @@
base
os
nic_session
+uplink_session
+nic_driver
platform_session
timer_session
diff --git a/repos/dde_ipxe/src/drivers/nic/main.cc b/repos/dde_ipxe/src/drivers/nic/main.cc
index aa574f1309..be816a42bf 100644
--- a/repos/dde_ipxe/src/drivers/nic/main.cc
+++ b/repos/dde_ipxe/src/drivers/nic/main.cc
@@ -12,14 +12,21 @@
* version 2.
*/
-/* Genode */
+/* Genode includes */
#include
#include
#include
#include
#include
#include
+#include
+#include
+/* NIC driver includes */
+#include
+#include
+
+/* DDE iPXE includes */
#include
#include
@@ -61,13 +68,13 @@ class Ipxe_session_component : public Nic::Session_component
Packet_descriptor packet = _tx.sink()->get_packet();
if (!packet.size()) {
- Genode::warning("Invalid tx packet");
+ warning("Invalid tx packet");
return true;
}
if (link_state()) {
if (dde_ipxe_nic_tx(1, _tx.sink()->packet_content(packet), packet.size()))
- Genode::warning("Sending packet failed!");
+ warning("Sending packet failed!");
}
_tx.sink()->acknowledge_packet(packet);
@@ -83,10 +90,10 @@ class Ipxe_session_component : public Nic::Session_component
try {
Nic::Packet_descriptor p = _rx.source()->alloc_packet(packet_len);
- Genode::memcpy(_rx.source()->packet_content(p), packet, packet_len);
+ memcpy(_rx.source()->packet_content(p), packet, packet_len);
_rx.source()->submit_packet(p);
} catch (...) {
- Genode::warning(__func__, ": failed to process received packet");
+ warning(__func__, ": failed to process received packet");
}
}
@@ -100,11 +107,11 @@ class Ipxe_session_component : public Nic::Session_component
public:
- Ipxe_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)
- : Session_component(tx_buf_size, rx_buf_size, Genode::CACHED,
+ Ipxe_session_component(size_t const tx_buf_size,
+ size_t const rx_buf_size,
+ Allocator &rx_block_md_alloc,
+ Env &env)
+ : Session_component(tx_buf_size, rx_buf_size, CACHED,
rx_block_md_alloc, env)
{
instance = this;
@@ -113,7 +120,7 @@ class Ipxe_session_component : public Nic::Session_component
dde_ipxe_nic_get_mac_addr(1, _mac_addr.addr);
- Genode::log("MAC address ", _mac_addr);
+ log("MAC address ", _mac_addr);
}
~Ipxe_session_component()
@@ -138,32 +145,126 @@ class Ipxe_session_component : public Nic::Session_component
Ipxe_session_component *Ipxe_session_component::instance;
+class Uplink_client : public Uplink_client_base
+{
+ public:
+
+ static Uplink_client *instance;
+
+ private:
+
+ Nic::Mac_address _init_drv_mac_addr()
+ {
+ instance = this;
+ dde_ipxe_nic_register_callbacks(
+ _drv_rx_callback, _drv_link_callback);
+
+ Nic::Mac_address mac_addr { };
+ dde_ipxe_nic_get_mac_addr(1, mac_addr.addr);
+ return mac_addr;
+ }
+
+
+ /***********************************
+ ** Interface towards iPXE driver **
+ ***********************************/
+
+ static void _drv_rx_callback(unsigned interface_idx,
+ const char *drv_rx_pkt_base,
+ unsigned drv_rx_pkt_size)
+ {
+ instance->_drv_rx_handle_pkt(
+ drv_rx_pkt_size,
+ [&] (void *conn_tx_pkt_base,
+ size_t &)
+ {
+ memcpy(conn_tx_pkt_base, drv_rx_pkt_base, drv_rx_pkt_size);
+ return Write_result::WRITE_SUCCEEDED;
+ });
+ }
+
+ static void _drv_link_callback()
+ {
+ instance->_drv_handle_link_state(dde_ipxe_nic_link_state(1));
+ }
+
+
+ /************************
+ ** Uplink_client_base **
+ ************************/
+
+ Transmit_result
+ _drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size) override
+ {
+ if (dde_ipxe_nic_tx(1, conn_rx_pkt_base, conn_rx_pkt_size) == 0) {
+
+ return Transmit_result::ACCEPTED;
+ }
+ return Transmit_result::REJECTED;
+ }
+
+ public:
+
+ Uplink_client(Env &env,
+ Allocator &alloc)
+ :
+ Uplink_client_base { env, alloc, _init_drv_mac_addr() }
+ {
+ _drv_handle_link_state(dde_ipxe_nic_link_state(1));
+ }
+
+ ~Uplink_client()
+ {
+ dde_ipxe_nic_unregister_callbacks();
+ instance = nullptr;
+ }
+};
+
+
+Uplink_client *Uplink_client::instance;
+
+
struct Main
{
- Env &_env;
-
- Heap _heap { _env.ram(), _env.rm() };
-
- Nic::Root root {_env, _heap };
+ Env &_env;
+ Heap _heap { _env.ram(), _env.rm() };
+ Attached_rom_dataspace _config_rom { _env, "config" };
Main(Env &env) : _env(env)
{
- Genode::log("--- iPXE NIC driver started ---");
+ log("--- iPXE NIC driver started ---");
- Genode::log("-- init iPXE NIC");
+ log("-- init iPXE NIC");
/* pass Env to backend */
dde_support_init(_env, _heap);
if (!dde_ipxe_nic_init()) {
- Genode::error("could not find usable NIC device");
+ error("could not find usable NIC device");
}
+ Nic_driver_mode const mode {
+ read_nic_driver_mode(_config_rom.xml()) };
- _env.parent().announce(_env.ep().manage(root));
+ switch (mode) {
+ case Nic_driver_mode::NIC_SERVER:
+ {
+ Nic::Root &root {
+ *new (_heap)
+ Nic::Root(_env, _heap) };
+
+ _env.parent().announce(_env.ep().manage(root));
+ break;
+ }
+ case Nic_driver_mode::UPLINK_CLIENT:
+
+ new (_heap) Uplink_client(_env, _heap);
+ break;
+ }
}
};
-void Component::construct(Genode::Env &env)
+void Component::construct(Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
diff --git a/repos/dde_ipxe/src/drivers/nic/target.mk b/repos/dde_ipxe/src/drivers/nic/target.mk
index 95f6bff4d8..facbd98758 100644
--- a/repos/dde_ipxe/src/drivers/nic/target.mk
+++ b/repos/dde_ipxe/src/drivers/nic/target.mk
@@ -1,6 +1,6 @@
TARGET = ipxe_nic_drv
REQUIRES = x86
-LIBS = base dde_ipxe_nic
+LIBS = base dde_ipxe_nic nic_driver
SRC_CC = main.cc
CC_CXX_WARN_STRICT =
diff --git a/repos/dde_linux/lib/mk/wifi.inc b/repos/dde_linux/lib/mk/wifi.inc
index dddb2a80f8..18922c6dbc 100644
--- a/repos/dde_linux/lib/mk/wifi.inc
+++ b/repos/dde_linux/lib/mk/wifi.inc
@@ -11,7 +11,7 @@ SHARED_LIB = yes
# wifi_include *must* be the first library, otherwise the include
# order is wrong
#
-LIBS += wifi_include lx_kit_setjmp
+LIBS += wifi_include lx_kit_setjmp nic_driver
LD_OPT += --version-script=$(LIB_DIR)/symbol.map
diff --git a/repos/dde_linux/recipes/src/fec_nic_drv/content.mk b/repos/dde_linux/recipes/src/fec_nic_drv/content.mk
index 1f17cba1a0..09b87aa2bb 100644
--- a/repos/dde_linux/recipes/src/fec_nic_drv/content.mk
+++ b/repos/dde_linux/recipes/src/fec_nic_drv/content.mk
@@ -4,6 +4,8 @@ LIB_MK := lib/mk/fec_nic_include.mk \
PORT_DIR := $(call port_dir,$(REP_DIR)/ports/dde_linux)
MIRROR_FROM_REP_DIR := $(LIB_MK) \
+ src/drivers/nic/linux_network_session_base.cc \
+ src/drivers/nic/linux_network_session_base.h \
lib/import/import-fec_nic_include.mk \
src/include src/lx_kit \
$(shell cd $(REP_DIR); find src/drivers/nic/fec -type f)
diff --git a/repos/dde_linux/recipes/src/fec_nic_drv/used_apis b/repos/dde_linux/recipes/src/fec_nic_drv/used_apis
index a985c7f217..fbea6143a0 100644
--- a/repos/dde_linux/recipes/src/fec_nic_drv/used_apis
+++ b/repos/dde_linux/recipes/src/fec_nic_drv/used_apis
@@ -3,4 +3,6 @@ os
platform_session
timer_session
nic_session
+uplink_session
+nic_driver
gpio_session
diff --git a/repos/dde_linux/recipes/src/usb_drv/used_apis b/repos/dde_linux/recipes/src/usb_drv/used_apis
index 3ef1daeb98..797b95b382 100644
--- a/repos/dde_linux/recipes/src/usb_drv/used_apis
+++ b/repos/dde_linux/recipes/src/usb_drv/used_apis
@@ -1,6 +1,8 @@
base
os
nic_session
+uplink_session
+nic_driver
usb_session
gpio_session
event_session
diff --git a/repos/dde_linux/recipes/src/wifi_drv/used_apis b/repos/dde_linux/recipes/src/wifi_drv/used_apis
index 718ec71d56..fc4a522bed 100644
--- a/repos/dde_linux/recipes/src/wifi_drv/used_apis
+++ b/repos/dde_linux/recipes/src/wifi_drv/used_apis
@@ -4,6 +4,8 @@ libc
libcrypto
libssl
nic_session
+uplink_session
+nic_driver
platform_session
report_session
timer_session
diff --git a/repos/dde_linux/src/drivers/nic/fec/component.cc b/repos/dde_linux/src/drivers/nic/fec/component.cc
index db7a41480d..6f131c5c9a 100644
--- a/repos/dde_linux/src/drivers/nic/fec/component.cc
+++ b/repos/dde_linux/src/drivers/nic/fec/component.cc
@@ -18,52 +18,6 @@ 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()
{
@@ -87,8 +41,6 @@ bool Session_component::_send()
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());
@@ -121,13 +73,6 @@ void Session_component::_handle_packet_stream()
}
-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();
@@ -169,15 +114,14 @@ void Session_component::link_state(bool link)
}
-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();
-}
+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 const &label)
+:
+ Nic::Session_component { tx_buf_size, rx_buf_size, Genode::CACHED,
+ rx_block_md_alloc, env },
+ Linux_network_session_base { label },
+ _has_link { _read_link_state_from_ndev() }
+{ }
diff --git a/repos/dde_linux/src/drivers/nic/fec/component.h b/repos/dde_linux/src/drivers/nic/fec/component.h
index 09ae289173..55ab7c8139 100644
--- a/repos/dde_linux/src/drivers/nic/fec/component.h
+++ b/repos/dde_linux/src/drivers/nic/fec/component.h
@@ -18,43 +18,16 @@
#include
#include
-#include
+/* local includes */
+#include
-extern "C" {
- struct net_device;
- struct napi_struct;
- struct sk_buff;
-}
-class Session_component : public Nic::Session_component
+class Session_component : public Nic::Session_component,
+ public Linux_network_session_base
{
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 _has_link = false;
bool _send();
void _handle_rx();
@@ -62,17 +35,27 @@ class Session_component : public Nic::Session_component
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);
+ 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 const &label);
+
+
+ /****************************
+ ** Nic::Session_component **
+ ****************************/
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);
+
+
+ /********************************
+ ** Linux_network_session_base **
+ ********************************/
+
+ void link_state(bool link) override;
+ void receive(struct sk_buff *skb) override;
};
@@ -97,15 +80,14 @@ class Root : public Genode::Root_component
/* deplete ram quota by the memory needed for the session structure */
size_t session_size = max(4096UL, (unsigned long)sizeof(Session_component));
- if (ram_quota < session_size)
- throw Genode::Insufficient_ram_quota();
/*
* Check if donated ram quota suffices for both communication
* buffers and check for overflow
*/
- if (tx_buf_size + rx_buf_size < tx_buf_size ||
- tx_buf_size + rx_buf_size > ram_quota - session_size) {
+ 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();
diff --git a/repos/dde_linux/src/drivers/nic/fec/lx_emul.cc b/repos/dde_linux/src/drivers/nic/fec/lx_emul.cc
index e3e285829a..38ebba643f 100644
--- a/repos/dde_linux/src/drivers/nic/fec/lx_emul.cc
+++ b/repos/dde_linux/src/drivers/nic/fec/lx_emul.cc
@@ -279,7 +279,7 @@ struct Fec : public Genode::List::Element
int tx_queues { 1 };
int rx_queues { 1 };
struct net_device * net_dev { nullptr };
- Session_component * session { nullptr };
+ Linux_network_session_base *session { nullptr };
Genode::Attached_dataspace io_ds { Lx_kit::env().env().rm(),
device.io_mem_dataspace() };
Genode::Constructible mdio;
@@ -319,8 +319,10 @@ static Genode::List & fec_devices()
}
-net_device * Session_component::_register_session_component(Session_component & s,
- Genode::Session_label policy)
+net_device *
+Linux_network_session_base::
+_register_session(Linux_network_session_base &session,
+ Genode::Session_label policy)
{
unsigned number = 0;
for (Fec * f = fec_devices().first(); f; f = f->next()) { number++; }
@@ -335,7 +337,7 @@ net_device * Session_component::_register_session_component(Session_component &
/* Session already in use? */
if (f->session) continue;
- f->session = &s;
+ f->session = &session;
return f->net_dev;
}
return nullptr;
diff --git a/repos/dde_linux/src/drivers/nic/fec/main.cc b/repos/dde_linux/src/drivers/nic/fec/main.cc
index f6e7b0d3ac..e77eaa347a 100644
--- a/repos/dde_linux/src/drivers/nic/fec/main.cc
+++ b/repos/dde_linux/src/drivers/nic/fec/main.cc
@@ -11,11 +11,17 @@
* version 2.
*/
+
/* Genode includes */
#include
#include
#include
+/* NIC driver includes */
+#include
+
+/* local includes */
+#include
#include
/* Linux emulation environment includes */
@@ -42,10 +48,13 @@ unsigned long jiffies;
struct Main
{
- Genode::Env &env;
- Genode::Entrypoint &ep { env.ep() };
- Genode::Heap heap { env.ram(), env.rm() };
- Root root { env, heap };
+ Genode::Env &env;
+ Genode::Entrypoint &ep { env.ep() };
+ Genode::Attached_rom_dataspace config_rom { env, "config" };
+ Genode::Nic_driver_mode const mode { read_nic_driver_mode(config_rom.xml()) };
+ Genode::Heap heap { env.ram(), env.rm() };
+ Genode::Constructible root { };
+ Genode::Constructible uplink_client { };
/* Linux task that handles the initialization */
Genode::Constructible linux;
@@ -54,6 +63,11 @@ struct Main
{
Genode::log("--- freescale ethernet driver ---");
+ if (mode == Genode::Nic_driver_mode::NIC_SERVER) {
+
+ root.construct(env, heap);
+ }
+
Lx_kit::construct_env(env);
LX_MUTEX_INIT(mdio_board_lock);
@@ -80,7 +94,23 @@ struct Main
Lx::scheduler().schedule();
}
- void announce() { env.parent().announce(ep.manage(root)); }
+ void announce()
+ {
+ switch (mode) {
+ case Genode::Nic_driver_mode::NIC_SERVER:
+
+ env.parent().announce(ep.manage(*root));
+ break;
+
+ case Genode::Nic_driver_mode::UPLINK_CLIENT:
+
+ uplink_client.construct(
+ env, heap,
+ config_rom.xml().attribute_value(
+ "uplink_label", Genode::Session_label::String { "" }));
+ break;
+ }
+ }
Lx::Task &linux_task() { return *linux; }
};
diff --git a/repos/dde_linux/src/drivers/nic/fec/target.inc b/repos/dde_linux/src/drivers/nic/fec/target.inc
index fd2328dae2..22bfc7ef3a 100644
--- a/repos/dde_linux/src/drivers/nic/fec/target.inc
+++ b/repos/dde_linux/src/drivers/nic/fec/target.inc
@@ -1,8 +1,10 @@
TARGET = fec_nic_drv
-LIBS = base lx_kit_setjmp fec_nic_include
-SRC_CC = main.cc platform.cc lx_emul.cc component.cc
+LIBS = base lx_kit_setjmp fec_nic_include nic_driver
+SRC_CC += main.cc platform.cc lx_emul.cc component.cc uplink_client.cc
+SRC_CC += linux_network_session_base.cc
SRC_C += dummy.c lxc.c
INC_DIR += $(PRG_DIR)/../..
+INC_DIR += $(REP_DIR)/src/drivers/nic
# lx_kit
SRC_CC += env.cc irq.cc malloc.cc scheduler.cc timer.cc work.cc printf.cc
@@ -36,6 +38,7 @@ CC_OPT_phy = -Wno-unused-function -Wno-unused-but-set-variable
CC_OPT_phy_device = -Wno-unused-function
CC_OPT_at803x = -Wno-unused-variable
+vpath linux_network_session_base.cc $(REP_DIR)/src/drivers/nic
vpath %.c $(LX_CONTRIB_DIR)/drivers/net/ethernet/freescale
vpath %.c $(LX_CONTRIB_DIR)/drivers/net/phy
vpath %.c $(LX_CONTRIB_DIR)/net/core
diff --git a/repos/dde_linux/src/drivers/nic/fec/uplink_client.cc b/repos/dde_linux/src/drivers/nic/fec/uplink_client.cc
new file mode 100644
index 0000000000..af6d1206de
--- /dev/null
+++ b/repos/dde_linux/src/drivers/nic/fec/uplink_client.cc
@@ -0,0 +1,93 @@
+/*
+ * \brief Uplink session client role of the driver
+ * \author Martin Stein
+ * \date 2020-12-10
+ */
+
+/*
+ * 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.
+ */
+
+/* local include */
+#include
+
+#include
+
+extern "C" {
+#include
+};
+
+
+Genode::Uplink_client::Transmit_result
+Genode::Uplink_client::_drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size)
+{
+ /*
+ * We must not be called from another task, just from the
+ * packet stream dispatcher.
+ */
+ if (Lx::scheduler().active()) {
+ warning("scheduler active");
+ return Transmit_result::RETRY;
+ }
+
+ struct sk_buff *skb {
+ lxc_alloc_skb(conn_rx_pkt_size +
+ HEAD_ROOM, HEAD_ROOM) };
+
+ unsigned char *data = lxc_skb_put(skb, conn_rx_pkt_size);
+ memcpy(data, conn_rx_pkt_base, conn_rx_pkt_size);
+
+ _tx_data.ndev = _ndev;
+ _tx_data.skb = skb;
+
+ _tx_task.unblock();
+ Lx::scheduler().schedule();
+ return Transmit_result::ACCEPTED;
+}
+
+
+Genode::Uplink_client::Uplink_client(Env &env,
+ Allocator &alloc,
+ Session_label const &label)
+:
+ Linux_network_session_base { label },
+ Uplink_client_base { env, alloc,
+ Net::Mac_address(_ndev->dev_addr) }
+{
+ _drv_handle_link_state(_read_link_state_from_ndev());
+}
+
+
+void Genode::Uplink_client::link_state(bool state)
+{
+ _drv_handle_link_state(state);
+}
+
+
+void Genode::Uplink_client::receive(struct sk_buff *skb)
+{
+ Skb skb_helpr { skb_helper(skb) };
+ _drv_rx_handle_pkt(
+ skb_helpr.packet_size + skb_helpr.frag_size,
+ [&] (void *conn_tx_pkt_base,
+ size_t &)
+ {
+ memcpy(
+ conn_tx_pkt_base,
+ skb_helpr.packet,
+ skb_helpr.packet_size);
+
+ if (skb_helpr.frag_size) {
+
+ memcpy(
+ (char *)conn_tx_pkt_base + skb_helpr.packet_size,
+ skb_helpr.frag,
+ skb_helpr.frag_size);
+ }
+ return Write_result::WRITE_SUCCEEDED;
+ });
+}
diff --git a/repos/dde_linux/src/drivers/nic/fec/uplink_client.h b/repos/dde_linux/src/drivers/nic/fec/uplink_client.h
new file mode 100644
index 0000000000..96e44aabe3
--- /dev/null
+++ b/repos/dde_linux/src/drivers/nic/fec/uplink_client.h
@@ -0,0 +1,58 @@
+/*
+ * \brief Uplink session client role of the driver
+ * \author Martin Stein
+ * \date 2020-12-10
+ */
+
+/*
+ * 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__NIC__FEC__UPLINK_CLIENT_H_
+#define _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_
+
+/* NIC driver includes */
+#include
+
+/* local include */
+#include
+
+namespace Genode {
+
+ class Uplink_client;
+}
+
+
+class Genode::Uplink_client : public Linux_network_session_base,
+ public Uplink_client_base
+{
+ private:
+
+ /************************
+ ** Uplink_client_base **
+ ************************/
+
+ Transmit_result
+ _drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size) override;
+
+ public:
+
+ Uplink_client(Env &env,
+ Allocator &alloc,
+ Session_label const &label);
+
+
+ /********************************
+ ** Linux_network_session_base **
+ ********************************/
+
+ void link_state(bool state) override;
+
+ void receive(struct sk_buff *skb) override;
+};
+
+#endif /* _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ */
diff --git a/repos/dde_linux/src/drivers/nic/linux_network_session_base.cc b/repos/dde_linux/src/drivers/nic/linux_network_session_base.cc
new file mode 100644
index 0000000000..737bf76864
--- /dev/null
+++ b/repos/dde_linux/src/drivers/nic/linux_network_session_base.cc
@@ -0,0 +1,89 @@
+/*
+ * \brief Generic base of a network session using a DDE Linux back end
+ * \author Martin Stein
+ * \date 2020-12-13
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#include
+#include
+#include
+
+
+void Linux_network_session_base::_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);
+ }
+}
+
+
+void Linux_network_session_base::_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");
+ }
+ }
+}
+
+
+Linux_network_session_base::
+Linux_network_session_base(Genode::Session_label const &label)
+:
+ _ndev { _register_session(*this, label) }
+{
+ if (!_ndev) {
+ error("failed to register session with label \"", label, "\"");
+ throw Genode::Service_denied();
+ }
+}
+
+
+bool Linux_network_session_base::_read_link_state_from_ndev() const
+{
+ return !(_ndev->state & (1UL << __LINK_STATE_NOCARRIER));
+}
+
+
+void Linux_network_session_base::unblock_rx_task(napi_struct * n)
+{
+ _rx_data.napi = n;
+ _rx_task.unblock();
+}
diff --git a/repos/dde_linux/src/drivers/nic/linux_network_session_base.h b/repos/dde_linux/src/drivers/nic/linux_network_session_base.h
new file mode 100644
index 0000000000..dca7faa1a1
--- /dev/null
+++ b/repos/dde_linux/src/drivers/nic/linux_network_session_base.h
@@ -0,0 +1,78 @@
+/*
+ * \brief Virtual interface of a network session connected to the driver
+ * \author Martin Stein
+ * \date 2020-12-13
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#ifndef _SRC__DRIVERS__NIC__LINUX_NETWORK_SESSION_BASE_H_
+#define _SRC__DRIVERS__NIC__LINUX_NETWORK_SESSION_BASE_H_
+
+#include
+
+/* forward declarations */
+extern "C" {
+ struct net_device;
+ struct napi_struct;
+ struct sk_buff;
+}
+
+
+class Linux_network_session_base
+{
+ protected:
+
+ enum { HEAD_ROOM = 8 };
+
+ struct Tx_data
+ {
+ net_device * ndev;
+ sk_buff * skb;
+ };
+
+ struct Rx_data
+ {
+ struct napi_struct * napi;
+ };
+
+ net_device * _ndev = nullptr;
+ Tx_data _tx_data;
+ Rx_data _rx_data;
+ 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() };
+
+ static void _run_tx_task(void * args);
+
+ static void _run_rx_task(void * args);
+
+ static net_device *
+ _register_session(Linux_network_session_base &session,
+ Genode::Session_label label);
+
+ bool _read_link_state_from_ndev() const;
+
+ public:
+
+ Linux_network_session_base(Genode::Session_label const &label);
+
+ void unblock_rx_task(napi_struct * n);
+
+
+ /***********************
+ ** Virtual interface **
+ ***********************/
+
+ virtual void receive(struct sk_buff *skb) = 0;
+
+ virtual void link_state(bool state) = 0;
+};
+
+#endif /* _SRC__DRIVERS__NIC__LINUX_NETWORK_SESSION_BASE_H_ */
diff --git a/repos/dde_linux/src/drivers/usb/include/usb_nic_component.h b/repos/dde_linux/src/drivers/usb/include/usb_nic_component.h
index 11ed11ff4a..2bd9426384 100644
--- a/repos/dde_linux/src/drivers/usb/include/usb_nic_component.h
+++ b/repos/dde_linux/src/drivers/usb/include/usb_nic_component.h
@@ -14,27 +14,54 @@
#ifndef _USB_NIC_COMPONENT_H_
#define _USB_NIC_COMPONENT_H_
+/* Genode includes */
#include
#include
#include
+/* Linux emulation environment includes */
#include
#include
#include
#include
#include
+/* NIC driver includes */
+#include
+
namespace Usb_nic {
+
using namespace Genode;
using Genode::size_t;
+
class Session_component;
struct Device;
};
+class Usb_network_session
+{
+ protected:
+
+ Usb_nic::Device &_device;
+
+ public:
+
+ Usb_network_session(Usb_nic::Device &device)
+ :
+ _device { device }
+ { }
+
+ virtual void link_state_changed() = 0;
+
+ virtual void rx(Genode::addr_t virt,
+ Genode::size_t size) = 0;
+};
+
+
struct Usb_nic::Device
{
- Session_component *_session;
+ Usb_network_session *_session;
/**
* Transmit data to driver
@@ -54,7 +81,7 @@ struct Usb_nic::Device
/**
* Set session belonging to this driver
*/
- void session(Session_component *s) { _session = s; }
+ void session(Usb_network_session *s) { _session = s; }
/**
* Check for session
@@ -91,12 +118,9 @@ struct Usb_nic::Device
};
-class Usb_nic::Session_component : public Nic::Session_component
+class Usb_nic::Session_component : public Usb_network_session,
+ public Nic::Session_component
{
- private:
-
- Device *_device; /* device this session is using */
-
protected:
void _send_burst()
@@ -112,7 +136,7 @@ class Usb_nic::Session_component : public Nic::Session_component
{
/* alloc skb */
if (!skb) {
- if (!(skb = _device->alloc_skb()))
+ if (!(skb = _device.alloc_skb()))
return;
ptr = skb->data;
@@ -122,9 +146,9 @@ class Usb_nic::Session_component : public Nic::Session_component
Packet_descriptor packet = save.size() ? save : _tx.sink()->get_packet();
save = Packet_descriptor();
- if (!_device->skb_fill(&work_skb, ptr, packet.size(), skb->end)) {
+ if (!_device.skb_fill(&work_skb, ptr, packet.size(), skb->end)) {
/* submit batch */
- _device->tx_skb(skb);
+ _device.tx_skb(skb);
skb = nullptr;
save = packet;
continue;
@@ -134,7 +158,7 @@ class Usb_nic::Session_component : public Nic::Session_component
try { Genode::memcpy(work_skb.data, _tx.sink()->packet_content(packet), packet.size()); }
catch (Genode::Packet_descriptor::Invalid_packet) { }
/* call fixup on dummy SKB */
- _device->tx_fixup(&work_skb);
+ _device.tx_fixup(&work_skb);
/* advance to next slot */
ptr = work_skb.end;
skb->len += work_skb.truesize;
@@ -144,7 +168,7 @@ class Usb_nic::Session_component : public Nic::Session_component
/* submit last skb */
if (skb)
- _device->tx_skb(skb);
+ _device.tx_skb(skb);
}
bool _send()
@@ -161,7 +185,7 @@ class Usb_nic::Session_component : public Nic::Session_component
return true;
}
- bool ret = _device->tx((addr_t)_tx.sink()->packet_content(packet), packet.size());
+ bool ret = _device.tx((addr_t)_tx.sink()->packet_content(packet), packet.size());
_tx.sink()->acknowledge_packet(packet);
return ret;
@@ -172,7 +196,7 @@ class Usb_nic::Session_component : public Nic::Session_component
while (_rx.source()->ack_avail())
_rx.source()->release_packet(_rx.source()->get_acked_packet());
- if (_device->burst())
+ if (_device.burst())
_send_burst();
else
while (_send());
@@ -187,21 +211,34 @@ class Usb_nic::Session_component : public Nic::Session_component
Genode::size_t const rx_buf_size,
Genode::Allocator &rx_block_md_alloc,
Genode::Env &env,
- Device *device)
- : Nic::Session_component(tx_buf_size, rx_buf_size, Genode::CACHED,
- rx_block_md_alloc, env),
- _device(device)
- { _device->session(this); }
+ Device &device)
+ :
+ Usb_network_session { device },
+ Nic::Session_component { tx_buf_size, rx_buf_size, Genode::CACHED,
+ rx_block_md_alloc, env }
+ {
+ _device.session(this);
+ }
- Nic::Mac_address mac_address() override { return _device->mac_address(); }
- bool link_state() override { return _device->link_state(); }
- void link_state_changed() { _link_state_changed(); }
+ /****************************
+ ** Nic::Session_component **
+ ****************************/
+
+ Nic::Mac_address mac_address() override { return _device.mac_address(); }
+ bool link_state() override { return _device.link_state(); }
+
+
+ /*************************
+ ** Usb_network_session **
+ *************************/
+
+ void link_state_changed() override { _link_state_changed(); }
/**
* Send packet to client (called from driver)
*/
- void rx(addr_t virt, size_t size)
+ void rx(addr_t virt, size_t size) override
{
_handle_packet_stream();
@@ -231,8 +268,8 @@ class Root : public Root_component
{
private:
- Genode::Env &_env;
- Usb_nic::Device *_device;
+ Genode::Env &_env;
+ Usb_nic::Device &_device;
protected:
@@ -262,17 +299,143 @@ class Root : public Root_component
}
return new (Root::md_alloc())
- Usb_nic::Session_component(tx_buf_size, rx_buf_size,
- Lx::Malloc::mem(), _env, _device);
+ Usb_nic::Session_component(tx_buf_size, rx_buf_size,
+ Lx::Malloc::mem(), _env,
+ _device);
}
public:
- Root(Genode::Env &env, Genode::Allocator &md_alloc,
- Usb_nic::Device *device)
- : Root_component(&env.ep().rpc_ep(), &md_alloc),
- _env(env), _device(device)
+ Root(Genode::Env &env,
+ Genode::Allocator &md_alloc,
+ Usb_nic::Device &device)
+ :
+ Root_component { &env.ep().rpc_ep(), &md_alloc },
+ _env { env },
+ _device { device }
{ }
};
+
+namespace Genode {
+
+ class Uplink_client;
+}
+
+
+class Genode::Uplink_client : public Usb_network_session,
+ public Uplink_client_base
+{
+ private:
+
+ sk_buff _burst_work_skb { };
+ sk_buff *_burst_skb { nullptr };
+ unsigned char *_burst_ptr { nullptr };
+
+
+ /************************
+ ** Uplink_client_base **
+ ************************/
+
+ Transmit_result
+ _drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size) override
+ {
+ if (_device.tx((addr_t)conn_rx_pkt_base, conn_rx_pkt_size) == 0) {
+ return Transmit_result::ACCEPTED;
+ }
+ return Transmit_result::REJECTED;
+ }
+
+ void _drv_transmit_pkt_burst_prepare() override
+ {
+ _burst_skb = nullptr;
+ _burst_ptr = nullptr;
+ }
+
+ Burst_result
+ _drv_transmit_pkt_burst_step(Packet_descriptor const &packet,
+ char const *packet_base,
+ Packet_descriptor &save) override
+ {
+ /* alloc _burst_skb */
+ if (!_burst_skb) {
+ if (!(_burst_skb = _device.alloc_skb()))
+ return Burst_result::BURST_FAILED;
+
+ _burst_ptr = _burst_skb->data;
+ _burst_work_skb.data = nullptr;
+ }
+
+ if (!_device.skb_fill(&_burst_work_skb, _burst_ptr, packet.size(), _burst_skb->end)) {
+
+ /* submit batch */
+ _device.tx_skb(_burst_skb);
+ _burst_skb = nullptr;
+ save = packet;
+ return Burst_result::BURST_CONTINUE;
+ }
+
+ /* copy packet to current data pos */
+ Genode::memcpy(_burst_work_skb.data, packet_base, packet.size());
+
+ /* call fixup on dummy SKB */
+ _device.tx_fixup(&_burst_work_skb);
+
+ /* advance to next slot */
+ _burst_ptr = _burst_work_skb.end;
+ _burst_skb->len += _burst_work_skb.truesize;
+
+ return Burst_result::BURST_SUCCEEDED;
+ }
+
+ void _drv_transmit_pkt_burst_finish() override
+ {
+ /* submit last _burst_skb */
+ if (_burst_skb)
+ _device.tx_skb(_burst_skb);
+ }
+
+ bool _drv_supports_transmit_pkt_burst() override
+ {
+ return _device.burst();
+ }
+
+ public:
+
+ Uplink_client(Env &env,
+ Allocator &alloc,
+ Usb_nic::Device &device)
+ :
+ Usb_network_session { device },
+ Uplink_client_base { env, alloc, _device.mac_address() }
+ {
+ _device.session(this);
+ _drv_handle_link_state(_device.link_state());
+ }
+
+
+ /*************************
+ ** Usb_network_session **
+ *************************/
+
+ void link_state_changed() override
+ {
+ _drv_handle_link_state(_device.link_state());
+ }
+
+ void rx(Genode::addr_t virt,
+ Genode::size_t size) override
+ {
+ _drv_rx_handle_pkt(
+ size,
+ [&] (void *conn_tx_pkt_base,
+ size_t &)
+ {
+ memcpy(conn_tx_pkt_base, (void *)virt, size);
+ return Write_result::WRITE_SUCCEEDED;
+ });
+ }
+};
+
#endif /* _USB_NIC_COMPONENT_H_ */
diff --git a/repos/dde_linux/src/drivers/usb/nic/nic.cc b/repos/dde_linux/src/drivers/usb/nic/nic.cc
index 2068237b4b..8900732dc8 100644
--- a/repos/dde_linux/src/drivers/usb/nic/nic.cc
+++ b/repos/dde_linux/src/drivers/usb/nic/nic.cc
@@ -11,17 +11,24 @@
* version 2.
*/
+/* Genode includes */
#include
#include
#include
#include
+/* Linux emulation environment includes */
#include
#include
+/* NIC driver includes */
+#include
+
+/* local includes */
#include
#include "signal.h"
+
static Signal_helper *_signal = 0;
enum {
@@ -307,27 +314,54 @@ void Nic::init(Genode::Env &env) {
int register_netdev(struct net_device *ndev)
{
using namespace Genode;
- static bool announce = false;
+ static bool registered = false;
int err = -ENODEV;
Nic_device *nic = Nic_device::add(ndev);
+ if (nic == nullptr) {
+
+ class Invalid_nic_device { };
+ throw Invalid_nic_device { };
+ }
/* XXX: move to 'main' */
- if (!announce) {
- static ::Root root(_signal->env(), Lx::Malloc::mem(), nic);
+ if (!registered) {
- announce = true;
+ registered = true;
- ndev->state |= 1 << __LINK_STATE_START;
+ Nic_driver_mode const mode {
+ read_nic_driver_mode(Lx_kit::env().config_rom().xml()) };
- if ((err = ndev->netdev_ops->ndo_open(ndev)))
- return err;
+ switch (mode) {
+ case Genode::Nic_driver_mode::NIC_SERVER:
- if (ndev->netdev_ops->ndo_set_rx_mode)
- ndev->netdev_ops->ndo_set_rx_mode(ndev);
+ static ::Root root(_signal->env(), Lx::Malloc::mem(), *nic);
+ ndev->state |= 1 << __LINK_STATE_START;
- _nic = nic;
- _signal->parent().announce(_signal->ep().rpc_ep().manage(&root));
+ if ((err = ndev->netdev_ops->ndo_open(ndev)))
+ return err;
+
+ if (ndev->netdev_ops->ndo_set_rx_mode)
+ ndev->netdev_ops->ndo_set_rx_mode(ndev);
+
+ _nic = nic;
+ _signal->parent().announce(_signal->ep().rpc_ep().manage(&root));
+ log("Acting as Nic server");
+ break;
+
+ case Genode::Nic_driver_mode::UPLINK_CLIENT:
+
+ ndev->state |= 1 << __LINK_STATE_START;
+ if ((err = ndev->netdev_ops->ndo_open(ndev)))
+ return err;
+
+ _nic = nic;
+ static Uplink_client uplink_client {
+ _signal->env(), Lx::Malloc::mem(), *nic };
+
+ log("Acting as Uplink client");
+ break;
+ }
}
return err;
diff --git a/repos/dde_linux/src/drivers/usb/target.inc b/repos/dde_linux/src/drivers/usb/target.inc
index f72e792cba..c6b4f5cedf 100644
--- a/repos/dde_linux/src/drivers/usb/target.inc
+++ b/repos/dde_linux/src/drivers/usb/target.inc
@@ -1,5 +1,5 @@
SRC_CC += main.cc
-LIBS = base
+LIBS = base nic_driver
CC_CXX_WARN_STRICT =
diff --git a/repos/dde_linux/src/drivers/usb_modem/component.cc b/repos/dde_linux/src/drivers/usb_modem/component.cc
index 16d3162007..9a7d7b20e5 100644
--- a/repos/dde_linux/src/drivers/usb_modem/component.cc
+++ b/repos/dde_linux/src/drivers/usb_modem/component.cc
@@ -19,52 +19,6 @@ 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()
{
@@ -88,8 +42,6 @@ bool Session_component::_send()
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());
@@ -122,13 +74,6 @@ void Session_component::_handle_packet_stream()
}
-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();
@@ -170,14 +115,14 @@ void Session_component::link_state(bool link)
}
-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();
-}
+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 const &label)
+:
+ Nic::Session_component { tx_buf_size, rx_buf_size, Genode::CACHED,
+ rx_block_md_alloc, env },
+ Fec_nic { label },
+ _has_link { _read_link_state_from_ndev() }
+{ }
diff --git a/repos/dde_linux/src/drivers/usb_modem/component.h b/repos/dde_linux/src/drivers/usb_modem/component.h
index 5b5224a8fa..7494243049 100644
--- a/repos/dde_linux/src/drivers/usb_modem/component.h
+++ b/repos/dde_linux/src/drivers/usb_modem/component.h
@@ -19,43 +19,16 @@
#include
#include
-#include
+/* local includes */
+#include
-extern "C" {
- struct net_device;
- struct napi_struct;
- struct sk_buff;
-}
-class Session_component : public Nic::Session_component
+class Session_component : public Nic::Session_component,
+ public Fec_nic
{
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 _has_link = false;
bool _send();
void _handle_rx();
@@ -63,17 +36,27 @@ class Session_component : public Nic::Session_component
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);
+ 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 const &label);
+
+
+ /****************************
+ ** Nic::Session_component **
+ ****************************/
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);
+
+
+ /*************
+ ** Fec_nic **
+ *************/
+
+ void link_state(bool link) override;
+ void receive(struct sk_buff *skb) override;
};
diff --git a/repos/dde_linux/src/drivers/usb_modem/driver.h b/repos/dde_linux/src/drivers/usb_modem/driver.h
index 4eae5d16aa..d1415bf519 100644
--- a/repos/dde_linux/src/drivers/usb_modem/driver.h
+++ b/repos/dde_linux/src/drivers/usb_modem/driver.h
@@ -15,20 +15,28 @@
#ifndef _SRC__DRIVERS__USB_MODEM__DRIVER_H_
#define _SRC__DRIVERS__USB_MODEM__DRIVER_H_
+/* Genode includes */
#include
#include
#include
#include
-#include
+#include
+/* local includes */
+#include
#include
#include
+/* Linux emulation environment includes */
+#include
#include
#include
#include
#include
+/* NIC driver includes */
+#include
+
struct usb_device_id;
struct usb_interface;
struct usb_device;
@@ -144,19 +152,39 @@ struct Driver
}
};
-
- 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;
+ Devices devices;
+ Genode::Env &env;
+ Genode::Entrypoint &ep { env.ep() };
+ Genode::Attached_rom_dataspace config_rom { env, "config" };
+ Genode::Nic_driver_mode const mode { read_nic_driver_mode(config_rom.xml()) };
+ Genode::Heap heap { env.ram(), env.rm() };
+ Genode::Allocator_avl alloc { &heap };
+ Genode::Constructible root { };
+ Genode::Constructible uplink_client { };
+ Terminal::Root terminal_root { env, heap };
+ Genode::Constructible main_task;
+ Genode::Constructible report_rom;
Driver(Genode::Env &env);
+ void activate_network_session()
+ {
+ switch (mode) {
+ case Genode::Nic_driver_mode::NIC_SERVER:
+
+ env.parent().announce(ep.manage(*root));
+ break;
+
+ case Genode::Nic_driver_mode::UPLINK_CLIENT:
+
+ uplink_client.construct(
+ env, heap,
+ config_rom.xml().attribute_value(
+ "uplink_label", Genode::Session_label::String { "" }));
+ break;
+ }
+ }
+
static void main_task_entry(void *);
};
diff --git a/repos/dde_linux/src/drivers/usb_modem/fec_nic.cc b/repos/dde_linux/src/drivers/usb_modem/fec_nic.cc
new file mode 100644
index 0000000000..06b1bdb201
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/fec_nic.cc
@@ -0,0 +1,89 @@
+/*
+ * \brief Virtual interface of a network session connected to the driver
+ * \author Martin Stein
+ * \date 2020-12-13
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#include
+#include
+#include
+
+
+void Fec_nic::_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);
+ }
+}
+
+
+void Fec_nic::_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");
+ }
+ }
+}
+
+
+Fec_nic::Fec_nic(Genode::Session_label const &label)
+:
+ _ndev { _register_fec_nic(*this, label) }
+{
+
+ if (!_ndev) {
+
+ throw Genode::Service_denied();
+ }
+}
+
+
+bool Fec_nic::_read_link_state_from_ndev() const
+{
+ return !(_ndev->state & (1UL << __LINK_STATE_NOCARRIER));
+}
+
+
+void Fec_nic::unblock_rx_task(napi_struct * n)
+{
+ _rx_data.napi = n;
+ _rx_task.unblock();
+}
diff --git a/repos/dde_linux/src/drivers/usb_modem/fec_nic.h b/repos/dde_linux/src/drivers/usb_modem/fec_nic.h
new file mode 100644
index 0000000000..4829e9e448
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/fec_nic.h
@@ -0,0 +1,77 @@
+/*
+ * \brief Virtual interface of a network session connected to the driver
+ * \author Martin Stein
+ * \date 2020-12-13
+ */
+
+/*
+ * Copyright (C) 2020 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+#ifndef _SRC__DRIVERS__NIC__FEC__FEC_NIC_H_
+#define _SRC__DRIVERS__NIC__FEC__FEC_NIC_H_
+
+#include
+
+/* forward declarations */
+extern "C" {
+ struct net_device;
+ struct napi_struct;
+ struct sk_buff;
+}
+
+
+class Fec_nic
+{
+ protected:
+
+ enum { HEAD_ROOM = 8 };
+
+ struct Tx_data
+ {
+ net_device * ndev;
+ sk_buff * skb;
+ };
+
+ struct Rx_data
+ {
+ struct napi_struct * napi;
+ };
+
+ net_device * _ndev = nullptr;
+ Tx_data _tx_data;
+ Rx_data _rx_data;
+ 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() };
+
+ static void _run_tx_task(void * args);
+
+ static void _run_rx_task(void * args);
+
+ static net_device *_register_fec_nic(Fec_nic &fec_nic,
+ Genode::Session_label label);
+
+ bool _read_link_state_from_ndev() const;
+
+ public:
+
+ Fec_nic(Genode::Session_label const &label);
+
+ void unblock_rx_task(napi_struct * n);
+
+
+ /***********************
+ ** Virtual interface **
+ ***********************/
+
+ virtual void receive(struct sk_buff *skb) = 0;
+
+ virtual void link_state(bool state) = 0;
+};
+
+#endif /* _SRC__DRIVERS__NIC__FEC__FEC_NIC_H_ */
diff --git a/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc b/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc
index b9aeba5438..3988dedd23 100644
--- a/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc
+++ b/repos/dde_linux/src/drivers/usb_modem/lx_emul.cc
@@ -438,10 +438,10 @@ int register_netdev(struct net_device *dev)
};
-net_device * Session_component::_register_session_component(Session_component & s,
- Genode::Session_label policy)
+net_device * Fec_nic::_register_fec_nic(Fec_nic &fec_nic,
+ Genode::Session_label policy)
{
- if (single_net_device) single_net_device->session_component = (void*) &s;
+ if (single_net_device) single_net_device->session_component = &fec_nic;
return single_net_device;
}
@@ -494,7 +494,7 @@ 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);
+ reinterpret_cast(dev->session_component)->link_state(false);
}
@@ -521,14 +521,14 @@ 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);
+ 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);
+ reinterpret_cast(skb->dev->session_component)->receive(skb);
dev_kfree_skb(skb);
return NET_RX_SUCCESS;
diff --git a/repos/dde_linux/src/drivers/usb_modem/main.cc b/repos/dde_linux/src/drivers/usb_modem/main.cc
index cf202014ab..3e87c95158 100644
--- a/repos/dde_linux/src/drivers/usb_modem/main.cc
+++ b/repos/dde_linux/src/drivers/usb_modem/main.cc
@@ -131,7 +131,7 @@ void Driver::Device::register_device()
probe_interface(udev->config->interface[i], &id);
}
- driver.env.parent().announce(driver.ep.manage(driver.root));
+ driver.activate_network_session();
driver.env.parent().announce(driver.ep.manage(driver.terminal_root));
}
@@ -226,6 +226,9 @@ Driver::Driver(Genode::Env &env) : env(env)
{
Genode::log("--- USB net driver ---");
+ if (mode == Genode::Nic_driver_mode::NIC_SERVER) {
+ root.construct(env, heap);
+ }
Lx_kit::construct_env(env);
Lx::scheduler(&env);
Lx::malloc_init(env, heap);
diff --git a/repos/dde_linux/src/drivers/usb_modem/target.mk b/repos/dde_linux/src/drivers/usb_modem/target.mk
index d02c441314..5f7fb1562e 100644
--- a/repos/dde_linux/src/drivers/usb_modem/target.mk
+++ b/repos/dde_linux/src/drivers/usb_modem/target.mk
@@ -1,9 +1,10 @@
TARGET := usb_modem_drv
SRC_C := dummies.c lxc.c
-SRC_CC := main.cc lx_emul.cc component.cc terminal.cc
+SRC_CC := main.cc lx_emul.cc component.cc terminal.cc fec_nic.cc
SRC_CC += printf.cc timer.cc scheduler.cc malloc.cc env.cc work.cc
+SRC_CC += uplink_client.cc
-LIBS := base usb_modem_include lx_kit_setjmp
+LIBS := base usb_modem_include lx_kit_setjmp nic_driver
USB_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_modem
diff --git a/repos/dde_linux/src/drivers/usb_modem/uplink_client.cc b/repos/dde_linux/src/drivers/usb_modem/uplink_client.cc
new file mode 100644
index 0000000000..84998c0ba8
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/uplink_client.cc
@@ -0,0 +1,93 @@
+/*
+ * \brief Uplink session client role of the driver
+ * \author Martin Stein
+ * \date 2020-12-10
+ */
+
+/*
+ * 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.
+ */
+
+/* local include */
+#include
+
+#include
+
+extern "C" {
+#include
+};
+
+
+Genode::Uplink_client::Transmit_result
+Genode::Uplink_client::_drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size)
+{
+ /*
+ * We must not be called from another task, just from the
+ * packet stream dispatcher.
+ */
+ if (Lx::scheduler().active()) {
+ warning("scheduler active");
+ return Transmit_result::RETRY;
+ }
+
+ struct sk_buff *skb {
+ lxc_alloc_skb(conn_rx_pkt_size +
+ HEAD_ROOM, HEAD_ROOM) };
+
+ unsigned char *data = lxc_skb_put(skb, conn_rx_pkt_size);
+ memcpy(data, conn_rx_pkt_base, conn_rx_pkt_size);
+
+ _tx_data.ndev = _ndev;
+ _tx_data.skb = skb;
+
+ _tx_task.unblock();
+ Lx::scheduler().schedule();
+ return Transmit_result::ACCEPTED;
+}
+
+
+Genode::Uplink_client::Uplink_client(Env &env,
+ Allocator &alloc,
+ Session_label const &label)
+:
+ Fec_nic { label },
+ Uplink_client_base { env, alloc,
+ Net::Mac_address(_ndev->dev_addr) }
+{
+ _drv_handle_link_state(_read_link_state_from_ndev());
+}
+
+
+void Genode::Uplink_client::link_state(bool state)
+{
+ _drv_handle_link_state(state);
+}
+
+
+void Genode::Uplink_client::receive(struct sk_buff *skb)
+{
+ Skb skb_helpr { skb_helper(skb) };
+ _drv_rx_handle_pkt(
+ skb_helpr.packet_size + skb_helpr.frag_size,
+ [&] (void *conn_tx_pkt_base,
+ size_t &)
+ {
+ memcpy(
+ conn_tx_pkt_base,
+ skb_helpr.packet,
+ skb_helpr.packet_size);
+
+ if (skb_helpr.frag_size) {
+
+ memcpy(
+ (char *)conn_tx_pkt_base + skb_helpr.packet_size,
+ skb_helpr.frag,
+ skb_helpr.frag_size);
+ }
+ return Write_result::WRITE_SUCCEEDED;
+ });
+}
diff --git a/repos/dde_linux/src/drivers/usb_modem/uplink_client.h b/repos/dde_linux/src/drivers/usb_modem/uplink_client.h
new file mode 100644
index 0000000000..e75c926d6e
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_modem/uplink_client.h
@@ -0,0 +1,58 @@
+/*
+ * \brief Uplink session client role of the driver
+ * \author Martin Stein
+ * \date 2020-12-10
+ */
+
+/*
+ * 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__NIC__FEC__UPLINK_CLIENT_H_
+#define _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_
+
+/* NIC driver includes */
+#include
+
+/* local include */
+#include
+
+namespace Genode {
+
+ class Uplink_client;
+}
+
+
+class Genode::Uplink_client : public Fec_nic,
+ public Uplink_client_base
+{
+ private:
+
+ /************************
+ ** Uplink_client_base **
+ ************************/
+
+ Transmit_result
+ _drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size) override;
+
+ public:
+
+ Uplink_client(Env &env,
+ Allocator &alloc,
+ Session_label const &label);
+
+
+ /*************
+ ** Fec_nic **
+ *************/
+
+ void link_state(bool state) override;
+
+ void receive(struct sk_buff *skb) override;
+};
+
+#endif /* _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ */
diff --git a/repos/dde_linux/src/drivers/usb_net/component.cc b/repos/dde_linux/src/drivers/usb_net/component.cc
index 6bb4ae1d5c..6f131c5c9a 100644
--- a/repos/dde_linux/src/drivers/usb_net/component.cc
+++ b/repos/dde_linux/src/drivers/usb_net/component.cc
@@ -18,52 +18,6 @@ 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()
{
@@ -87,8 +41,6 @@ bool Session_component::_send()
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());
@@ -121,13 +73,6 @@ void Session_component::_handle_packet_stream()
}
-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();
@@ -169,14 +114,14 @@ void Session_component::link_state(bool link)
}
-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();
-}
+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 const &label)
+:
+ Nic::Session_component { tx_buf_size, rx_buf_size, Genode::CACHED,
+ rx_block_md_alloc, env },
+ Linux_network_session_base { label },
+ _has_link { _read_link_state_from_ndev() }
+{ }
diff --git a/repos/dde_linux/src/drivers/usb_net/component.h b/repos/dde_linux/src/drivers/usb_net/component.h
index c9867692c0..55ab7c8139 100644
--- a/repos/dde_linux/src/drivers/usb_net/component.h
+++ b/repos/dde_linux/src/drivers/usb_net/component.h
@@ -18,43 +18,16 @@
#include
#include
-#include
+/* local includes */
+#include
-extern "C" {
- struct net_device;
- struct napi_struct;
- struct sk_buff;
-}
-class Session_component : public Nic::Session_component
+class Session_component : public Nic::Session_component,
+ public Linux_network_session_base
{
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 _has_link = false;
bool _send();
void _handle_rx();
@@ -62,17 +35,27 @@ class Session_component : public Nic::Session_component
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);
+ 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 const &label);
+
+
+ /****************************
+ ** Nic::Session_component **
+ ****************************/
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);
+
+
+ /********************************
+ ** Linux_network_session_base **
+ ********************************/
+
+ void link_state(bool link) override;
+ void receive(struct sk_buff *skb) override;
};
diff --git a/repos/dde_linux/src/drivers/usb_net/driver.h b/repos/dde_linux/src/drivers/usb_net/driver.h
index d0af2b0ad8..4ce0dcaf9c 100644
--- a/repos/dde_linux/src/drivers/usb_net/driver.h
+++ b/repos/dde_linux/src/drivers/usb_net/driver.h
@@ -14,13 +14,22 @@
#ifndef _SRC__DRIVERS__USB_NET__DRIVER_H_
#define _SRC__DRIVERS__USB_NET__DRIVER_H_
+/* Genode includes */
#include
#include
#include
#include
+#include
+
+/* local includes */
+#include
+#include
+
+/* Linux emulation environment includes */
#include
-#include
+/* NIC driver includes */
+#include
struct usb_device_id;
struct usb_interface;
@@ -88,17 +97,38 @@ struct Driver
}
};
- 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 };
- Genode::Constructible main_task;
- Genode::Constructible report_rom;
+ Devices devices;
+ Genode::Env &env;
+ Genode::Entrypoint &ep { env.ep() };
+ Genode::Attached_rom_dataspace config_rom { env, "config" };
+ Genode::Nic_driver_mode const mode { read_nic_driver_mode(config_rom.xml()) };
+ Genode::Heap heap { env.ram(), env.rm() };
+ Genode::Allocator_avl alloc { &heap };
+ Genode::Constructible root { };
+ Genode::Constructible uplink_client { };
+ Genode::Constructible main_task;
+ Genode::Constructible report_rom;
Driver(Genode::Env &env);
+ void activate_network_session()
+ {
+ switch (mode) {
+ case Genode::Nic_driver_mode::NIC_SERVER:
+
+ env.parent().announce(ep.manage(*root));
+ break;
+
+ case Genode::Nic_driver_mode::UPLINK_CLIENT:
+
+ uplink_client.construct(
+ env, heap,
+ config_rom.xml().attribute_value(
+ "uplink_label", Genode::Session_label::String { "" }));
+ break;
+ }
+ }
+
static void main_task_entry(void *);
};
diff --git a/repos/dde_linux/src/drivers/usb_net/lx_emul.cc b/repos/dde_linux/src/drivers/usb_net/lx_emul.cc
index 47545fc969..fe4513d113 100644
--- a/repos/dde_linux/src/drivers/usb_net/lx_emul.cc
+++ b/repos/dde_linux/src/drivers/usb_net/lx_emul.cc
@@ -380,10 +380,12 @@ int register_netdev(struct net_device *dev)
};
-net_device * Session_component::_register_session_component(Session_component & s,
- Genode::Session_label policy)
+net_device *
+Linux_network_session_base::
+_register_session(Linux_network_session_base &session,
+ Genode::Session_label policy)
{
- if (single_net_device) single_net_device->session_component = (void*) &s;
+ if (single_net_device) single_net_device->session_component = &session;
return single_net_device;
}
@@ -430,7 +432,8 @@ 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);
+ reinterpret_cast(dev->session_component)->
+ link_state(false);
}
@@ -457,14 +460,16 @@ 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);
+ 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);
+ reinterpret_cast(skb->dev->session_component)->
+ receive(skb);
dev_kfree_skb(skb);
return NET_RX_SUCCESS;
diff --git a/repos/dde_linux/src/drivers/usb_net/main.cc b/repos/dde_linux/src/drivers/usb_net/main.cc
index 5e4d28808f..421b88c27d 100644
--- a/repos/dde_linux/src/drivers/usb_net/main.cc
+++ b/repos/dde_linux/src/drivers/usb_net/main.cc
@@ -74,7 +74,7 @@ void Driver::Device::scan_interfaces(unsigned iface_idx)
probe_interface(iface, &id);
udev->config->interface[iface_idx] = iface;
- driver.env.parent().announce(driver.ep.manage(driver.root));
+ driver.activate_network_session();
};
@@ -195,6 +195,9 @@ Driver::Driver(Genode::Env &env) : env(env)
{
Genode::log("--- USB net driver ---");
+ if (mode == Genode::Nic_driver_mode::NIC_SERVER) {
+ root.construct(env, heap);
+ }
Lx_kit::construct_env(env);
Lx::scheduler(&env);
Lx::malloc_init(env, heap);
diff --git a/repos/dde_linux/src/drivers/usb_net/target.mk b/repos/dde_linux/src/drivers/usb_net/target.mk
index bbe981aae7..a2a50c4f51 100644
--- a/repos/dde_linux/src/drivers/usb_net/target.mk
+++ b/repos/dde_linux/src/drivers/usb_net/target.mk
@@ -1,14 +1,16 @@
TARGET := usb_net_drv
SRC_C := dummies.c lxc.c
-SRC_CC := main.cc lx_emul.cc component.cc
+SRC_CC := main.cc lx_emul.cc component.cc linux_network_session_base.cc
SRC_CC += printf.cc timer.cc scheduler.cc malloc.cc env.cc work.cc
+SRC_CC += uplink_client.cc
-LIBS := base usb_net_include lx_kit_setjmp
+LIBS := base usb_net_include lx_kit_setjmp nic_driver
USB_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_net
INC_DIR += $(PRG_DIR)
INC_DIR += $(REP_DIR)/src/include
+INC_DIR += $(REP_DIR)/src/drivers/nic
SRC_C += drivers/net/usb/asix_common.c
SRC_C += drivers/net/usb/asix_devices.c
@@ -28,5 +30,6 @@ CC_C_OPT += -Wno-comment -Wno-int-conversion -Wno-incompatible-pointer-types \
CC_CXX_WARN_STRICT =
+vpath linux_network_session_base.cc $(REP_DIR)/src/drivers/nic
vpath %.c $(USB_CONTRIB_DIR)
vpath %.cc $(REP_DIR)/src/lx_kit
diff --git a/repos/dde_linux/src/drivers/usb_net/uplink_client.cc b/repos/dde_linux/src/drivers/usb_net/uplink_client.cc
new file mode 100644
index 0000000000..af6d1206de
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_net/uplink_client.cc
@@ -0,0 +1,93 @@
+/*
+ * \brief Uplink session client role of the driver
+ * \author Martin Stein
+ * \date 2020-12-10
+ */
+
+/*
+ * 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.
+ */
+
+/* local include */
+#include
+
+#include
+
+extern "C" {
+#include
+};
+
+
+Genode::Uplink_client::Transmit_result
+Genode::Uplink_client::_drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size)
+{
+ /*
+ * We must not be called from another task, just from the
+ * packet stream dispatcher.
+ */
+ if (Lx::scheduler().active()) {
+ warning("scheduler active");
+ return Transmit_result::RETRY;
+ }
+
+ struct sk_buff *skb {
+ lxc_alloc_skb(conn_rx_pkt_size +
+ HEAD_ROOM, HEAD_ROOM) };
+
+ unsigned char *data = lxc_skb_put(skb, conn_rx_pkt_size);
+ memcpy(data, conn_rx_pkt_base, conn_rx_pkt_size);
+
+ _tx_data.ndev = _ndev;
+ _tx_data.skb = skb;
+
+ _tx_task.unblock();
+ Lx::scheduler().schedule();
+ return Transmit_result::ACCEPTED;
+}
+
+
+Genode::Uplink_client::Uplink_client(Env &env,
+ Allocator &alloc,
+ Session_label const &label)
+:
+ Linux_network_session_base { label },
+ Uplink_client_base { env, alloc,
+ Net::Mac_address(_ndev->dev_addr) }
+{
+ _drv_handle_link_state(_read_link_state_from_ndev());
+}
+
+
+void Genode::Uplink_client::link_state(bool state)
+{
+ _drv_handle_link_state(state);
+}
+
+
+void Genode::Uplink_client::receive(struct sk_buff *skb)
+{
+ Skb skb_helpr { skb_helper(skb) };
+ _drv_rx_handle_pkt(
+ skb_helpr.packet_size + skb_helpr.frag_size,
+ [&] (void *conn_tx_pkt_base,
+ size_t &)
+ {
+ memcpy(
+ conn_tx_pkt_base,
+ skb_helpr.packet,
+ skb_helpr.packet_size);
+
+ if (skb_helpr.frag_size) {
+
+ memcpy(
+ (char *)conn_tx_pkt_base + skb_helpr.packet_size,
+ skb_helpr.frag,
+ skb_helpr.frag_size);
+ }
+ return Write_result::WRITE_SUCCEEDED;
+ });
+}
diff --git a/repos/dde_linux/src/drivers/usb_net/uplink_client.h b/repos/dde_linux/src/drivers/usb_net/uplink_client.h
new file mode 100644
index 0000000000..96e44aabe3
--- /dev/null
+++ b/repos/dde_linux/src/drivers/usb_net/uplink_client.h
@@ -0,0 +1,58 @@
+/*
+ * \brief Uplink session client role of the driver
+ * \author Martin Stein
+ * \date 2020-12-10
+ */
+
+/*
+ * 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__NIC__FEC__UPLINK_CLIENT_H_
+#define _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_
+
+/* NIC driver includes */
+#include
+
+/* local include */
+#include
+
+namespace Genode {
+
+ class Uplink_client;
+}
+
+
+class Genode::Uplink_client : public Linux_network_session_base,
+ public Uplink_client_base
+{
+ private:
+
+ /************************
+ ** Uplink_client_base **
+ ************************/
+
+ Transmit_result
+ _drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size) override;
+
+ public:
+
+ Uplink_client(Env &env,
+ Allocator &alloc,
+ Session_label const &label);
+
+
+ /********************************
+ ** Linux_network_session_base **
+ ********************************/
+
+ void link_state(bool state) override;
+
+ void receive(struct sk_buff *skb) override;
+};
+
+#endif /* _SRC__DRIVERS__NIC__FEC__UPLINK_CLIENT_H_ */
diff --git a/repos/dde_linux/src/drivers/wifi/frontend.h b/repos/dde_linux/src/drivers/wifi/frontend.h
index b238ffd19d..8804ec8835 100644
--- a/repos/dde_linux/src/drivers/wifi/frontend.h
+++ b/repos/dde_linux/src/drivers/wifi/frontend.h
@@ -53,6 +53,9 @@
#include
#include
+/* NIC driver includes */
+#include
+
/* rep includes */
#include
#include
@@ -361,6 +364,8 @@ struct Wifi::Frontend
Genode::Attached_rom_dataspace _config_rom;
Genode::Signal_handler _config_sigh;
+ Genode::Nic_driver_mode const _mode;
+
bool _verbose { false };
bool _verbose_state { false };
bool _use_11n { true };
@@ -1560,6 +1565,9 @@ struct Wifi::Frontend
_rfkill_handler(env.ep(), *this, &Wifi::Frontend::_handle_rfkill),
_config_rom(env, "wifi_config"),
_config_sigh(env.ep(), *this, &Wifi::Frontend::_handle_config_update),
+ _mode(
+ read_nic_driver_mode(
+ Genode::Attached_rom_dataspace(env, "config").xml())),
_scan_timer(env),
_scan_timer_sigh(env.ep(), *this, &Wifi::Frontend::_handle_scan_timer),
_events_handler(env.ep(), *this, &Wifi::Frontend::_handle_events),
@@ -1651,6 +1659,8 @@ struct Wifi::Frontend
* Used for communication between front end and wpa_supplicant.
*/
Msg_buffer &msg_buffer() { return _msg; }
+
+ Genode::Nic_driver_mode mode() const { return _mode; }
};
#endif /* _WIFI_FRONTEND_H_ */
diff --git a/repos/dde_linux/src/drivers/wifi/main.cc b/repos/dde_linux/src/drivers/wifi/main.cc
index 9182d6f259..93336a16f7 100644
--- a/repos/dde_linux/src/drivers/wifi/main.cc
+++ b/repos/dde_linux/src/drivers/wifi/main.cc
@@ -93,8 +93,11 @@ void *wifi_get_buffer(void)
/* exported by wifi.lib.so */
-extern void wifi_init(Genode::Env&, Genode::Blockade&, bool,
- Genode::Signal_context_capability);
+extern void wifi_init(Genode::Env&,
+ Genode::Blockade&,
+ bool,
+ Genode::Signal_context_capability,
+ Genode::Nic_driver_mode);
struct Main
@@ -119,7 +122,7 @@ struct Main
*/
bool const disable_11n = !_frontend->use_11n();
wifi_init(env, _wpa_startup_blockade, disable_11n,
- _frontend->rfkill_sigh());
+ _frontend->rfkill_sigh(), _frontend->mode());
}
};
diff --git a/repos/dde_linux/src/drivers/wifi/target.mk b/repos/dde_linux/src/drivers/wifi/target.mk
index 631330579c..dd8be379e4 100644
--- a/repos/dde_linux/src/drivers/wifi/target.mk
+++ b/repos/dde_linux/src/drivers/wifi/target.mk
@@ -3,7 +3,7 @@ REQUIRES = x86
TARGET = wifi_drv
SRC_CC = main.cc wpa.cc
LIBS = base wifi iwl_firmware
-LIBS += wpa_supplicant libc
+LIBS += wpa_supplicant libc nic_driver
# needed for firmware.h
INC_DIR += $(REP_DIR)/src/lib/wifi/include
diff --git a/repos/dde_linux/src/lib/wifi/init.cc b/repos/dde_linux/src/lib/wifi/init.cc
index 8113e4b8f0..442f384341 100644
--- a/repos/dde_linux/src/lib/wifi/init.cc
+++ b/repos/dde_linux/src/lib/wifi/init.cc
@@ -180,8 +180,11 @@ static void run_linux(void *args)
unsigned long jiffies;
-void wifi_init(Genode::Env &env, Genode::Blockade &blockade, bool disable_11n,
- Genode::Signal_context_capability rfkill)
+void wifi_init(Genode::Env &env,
+ Genode::Blockade &blockade,
+ bool disable_11n,
+ Genode::Signal_context_capability rfkill,
+ Genode::Nic_driver_mode mode)
{
Lx_kit::construct_env(env);
@@ -208,7 +211,7 @@ void wifi_init(Genode::Env &env, Genode::Blockade &blockade, bool disable_11n,
Lx::Work::work_queue(&Lx_kit::env().heap());
Lx::socket_init(env.ep(), Lx_kit::env().heap());
- Lx::nic_init(env, Lx_kit::env().heap());
+ Lx::nic_init(env, Lx_kit::env().heap(), mode);
Lx::pci_init(env, env.ram(), Lx_kit::env().heap());
Lx::malloc_init(env, Lx_kit::env().heap());
diff --git a/repos/dde_linux/src/lib/wifi/lx.h b/repos/dde_linux/src/lib/wifi/lx.h
index 82e25a6fd9..7430558a70 100644
--- a/repos/dde_linux/src/lib/wifi/lx.h
+++ b/repos/dde_linux/src/lib/wifi/lx.h
@@ -17,6 +17,9 @@
/* Genode includes */
#include
+/* NIC driver includes */
+#include
+
/* local includes */
#include
@@ -33,7 +36,9 @@ namespace Lx
void socket_init(Genode::Entrypoint&, Genode::Allocator&);
void socket_kick();
- void nic_init(Genode::Env&, Genode::Allocator&);
+ void nic_init(Genode::Env&,
+ Genode::Allocator&,
+ Genode::Nic_driver_mode);
Genode::Ram_dataspace_capability backend_alloc(Genode::addr_t, Genode::Cache_attribute);
void backend_free(Genode::Ram_dataspace_capability);
diff --git a/repos/dde_linux/src/lib/wifi/nic.cc b/repos/dde_linux/src/lib/wifi/nic.cc
index d653a1d16e..6a32da392b 100644
--- a/repos/dde_linux/src/lib/wifi/nic.cc
+++ b/repos/dde_linux/src/lib/wifi/nic.cc
@@ -20,6 +20,10 @@
#include
#include
#include
+#include
+
+/* NIC driver includes */
+#include
/* local includes */
#include
@@ -99,6 +103,62 @@ bool tx_task_send(struct sk_buff *skb)
}
+class Wifi_nic
+{
+ private:
+
+ net_device *_device { nullptr };
+
+ static Wifi_nic *_instance;
+
+ public:
+
+ void device(net_device &device)
+ {
+ _device = &device;
+ }
+
+ bool device_set() const
+ {
+ return _device != nullptr;
+ }
+
+ net_device &device()
+ {
+ if (_device == nullptr) {
+
+ class Invalid { };
+ throw Invalid { };
+ }
+ return *_device;
+ }
+
+ static void instance(Wifi_nic &instance)
+ {
+ _instance = &instance;
+ }
+
+ static Wifi_nic &instance()
+ {
+ if (!_instance) {
+
+ class Invalid { };
+ throw Invalid { };
+ }
+ return *_instance;
+ }
+
+ virtual void activate() = 0;
+
+ virtual void handle_driver_rx_packet(struct sk_buff *skb) = 0;
+
+ virtual void handle_driver_link_state(bool state) = 0;
+};
+
+
+Wifi_nic *Wifi_nic::_instance { nullptr };
+
+
/**
* Nic::Session implementation
*/
@@ -249,7 +309,8 @@ class Wifi_session_component : public Nic::Session_component
* NIC root implementation
*/
class Root : public Genode::Root_component
+ Genode::Single_client>,
+ public Wifi_nic
{
private:
@@ -284,20 +345,19 @@ class Root : public Genode::Root_componentsession = nullptr;
+ session = nullptr;
Genode::Root_component::_destroy_session(session);
}
public:
- net_device *device = nullptr;
Wifi_session_component *session = nullptr;
static Root *instance;
@@ -306,23 +366,194 @@ class Root : public Genode::Root_componentreceive(skb);
+ }
+ }
+
+ void handle_driver_link_state(bool state) override
+ {
+ if (session) {
+ session->link_state(state);
+ }
+ }
};
-Root *Root::instance;
+namespace Genode { class Wifi_uplink; }
-void Lx::nic_init(Genode::Env &env, Genode::Allocator &alloc)
+class Genode::Wifi_uplink : public Wifi_nic
{
- static Root root(env, alloc);
- Root::instance = &root;
+ private:
+
+ class Uplink_client : public Uplink_client_base
+ {
+ private:
+
+ net_device &_ndev;
+
+ Nic::Mac_address _init_drv_mac_addr(net_device &ndev)
+ {
+ Nic::Mac_address mac_addr { };
+ memcpy(&mac_addr, ndev.perm_addr, ETH_ALEN);
+ return mac_addr;
+ }
+
+
+ /************************
+ ** Uplink_client_base **
+ ************************/
+
+ Transmit_result
+ _drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size) override
+ {
+ /*
+ * We must not be called from another task, just from the
+ * packet stream dispatcher.
+ */
+ if (Lx::scheduler().active()) {
+ warning("scheduler active");
+ return Transmit_result::RETRY;
+ }
+
+ struct sk_buff *skb {
+ lxc_alloc_skb(conn_rx_pkt_size +
+ HEAD_ROOM, HEAD_ROOM) };
+
+ skb->dev = &_ndev;
+
+ unsigned char *data = lxc_skb_put(skb, conn_rx_pkt_size);
+ memcpy(data, conn_rx_pkt_base, conn_rx_pkt_size);
+
+ _tx_data.ndev = &_ndev;
+ _tx_data.skb = skb;
+
+ _tx_task->unblock();
+ Lx::scheduler().schedule();
+ return Transmit_result::ACCEPTED;
+ }
+
+ public:
+
+ Uplink_client(Env &env,
+ Allocator &alloc,
+ net_device &ndev)
+ :
+ Uplink_client_base { env, alloc,
+ _init_drv_mac_addr(ndev) },
+ _ndev { ndev }
+ {
+ _drv_handle_link_state(
+ !(_ndev.state & 1UL << __LINK_STATE_NOCARRIER));
+ }
+
+ void handle_driver_link_state(bool state)
+ {
+ _drv_handle_link_state(state);
+ }
+
+ void handle_driver_rx_packet(struct sk_buff *skb)
+ {
+ Skb skbh { skb_helper(skb) };
+ _drv_rx_handle_pkt(
+ skbh.packet_size + skbh.frag_size,
+ [&] (void *conn_tx_pkt_base,
+ size_t &)
+ {
+ memcpy(
+ conn_tx_pkt_base,
+ skbh.packet,
+ skbh.packet_size);
+
+ if (skbh.frag_size) {
+
+ memcpy(
+ (char *)conn_tx_pkt_base + skbh.packet_size,
+ skbh.frag,
+ skbh.frag_size);
+ }
+ return Write_result::WRITE_SUCCEEDED;
+ });
+ }
+ };
+
+ Env &_env;
+ Allocator &_alloc;
+ Constructible _client { };
+
+ public:
+
+ Wifi_uplink(Env &env,
+ Allocator &alloc)
+ :
+ _env { env },
+ _alloc { alloc }
+ { }
+
+
+ /**************
+ ** Wifi_nic **
+ **************/
+
+ void activate() override
+ {
+ _client.construct(_env, _alloc, device());
+ }
+
+ void handle_driver_rx_packet(struct sk_buff *skb) override
+ {
+ if (_client.constructed()) {
+ _client->handle_driver_rx_packet(skb);
+ }
+ }
+
+ void handle_driver_link_state(bool state) override
+ {
+ if (_client.constructed()) {
+ _client->handle_driver_link_state(state);
+ }
+ }
+};
+
+
+void Lx::nic_init(Genode::Env &env,
+ Genode::Allocator &alloc,
+ Genode::Nic_driver_mode mode)
+{
+ switch (mode) {
+ case Genode::Nic_driver_mode::NIC_SERVER:
+
+ Genode::log("Acting as NIC server");
+ static Root root(env, alloc);
+ Wifi_nic::instance(root);
+ break;
+
+ case Genode::Nic_driver_mode::UPLINK_CLIENT:
+
+ Genode::log("Acting as Uplink client");
+ Wifi_nic::instance(*new (alloc) Genode::Wifi_uplink(env, alloc));
+ break;
+ }
}
void Lx::get_mac_address(unsigned char *addr)
{
- memcpy(addr, Root::instance->device->perm_addr, ETH_ALEN);
+ memcpy(addr, Wifi_nic::instance().device().perm_addr, ETH_ALEN);
}
@@ -535,12 +766,12 @@ extern "C" void __dev_remove_pack(struct packet_type *pt)
extern "C" struct net_device *__dev_get_by_index(struct net *net, int ifindex)
{
- if (!Root::instance->device) {
+ if (!Wifi_nic::instance().device_set()) {
Genode::error("no net device registered!");
return 0;
}
- return Root::instance->device;
+ return &Wifi_nic::instance().device();
}
@@ -580,8 +811,7 @@ extern "C" int dev_parse_header(const struct sk_buff *skb, unsigned char *haddr)
extern "C" int dev_queue_xmit(struct sk_buff *skb)
{
- struct net_device *dev = skb->dev;
- struct net_device_ops const *ops = dev->netdev_ops;
+ struct net_device *dev = skb->dev;
if (skb->next) {
Genode::warning("more skb's queued");
@@ -619,12 +849,12 @@ extern "C" void dev_close(struct net_device *ndev)
bool Lx::open_device()
{
- if (!Root::instance->device) {
+ if (!Wifi_nic::instance().device_set()) {
Genode::error("no net_device available");
return false;
}
- struct net_device * const ndev = Root::instance->device;
+ struct net_device * const ndev = &Wifi_nic::instance().device();
int err = ndev->netdev_ops->ndo_open(ndev);
if (err) {
@@ -660,7 +890,11 @@ extern "C" int register_netdevice(struct net_device *ndev)
already_registered = true;
- Root::instance->device = ndev;
+ if (ndev == nullptr) {
+ class Invalid_net_device { };
+ throw Invalid_net_device { };
+ }
+ Wifi_nic::instance().device(*ndev);
ndev->state |= 1UL << __LINK_STATE_START;
netif_carrier_off(ndev);
@@ -698,7 +932,7 @@ extern "C" int register_netdevice(struct net_device *ndev)
if (ndev->netdev_ops->ndo_set_rx_mode)
ndev->netdev_ops->ndo_set_rx_mode(ndev);
- Root::instance->announce();
+ Wifi_nic::instance().activate();
list_add_tail_rcu(&ndev->dev_list, &init_net.dev_base_head);
@@ -724,22 +958,14 @@ extern "C" int netif_carrier_ok(const struct net_device *dev)
extern "C" void netif_carrier_on(struct net_device *dev)
{
dev->state &= ~(1UL << __LINK_STATE_NOCARRIER);
-
- Wifi_session_component *session = (Wifi_session_component *)dev->lx_nic_device;
-
- if (session)
- session->link_state(true);
+ Wifi_nic::instance().handle_driver_link_state(true);
}
extern "C" void netif_carrier_off(struct net_device *dev)
{
dev->state |= 1UL << __LINK_STATE_NOCARRIER;
-
- Wifi_session_component *session = (Wifi_session_component *)dev->lx_nic_device;
-
- if (session)
- session->link_state(false);
+ Wifi_nic::instance().handle_driver_link_state(false);
}
@@ -754,14 +980,12 @@ extern "C" int netif_receive_skb(struct sk_buff *skb)
if (is_eapol(skb)) {
/* XXX call only AF_PACKET hook */
for (Proto_hook* ph = proto_hook_list().first(); ph; ph = ph->next()) {
- ph->pt.func(skb, Root::instance->device, &ph->pt, Root::instance->device);
+ ph->pt.func(skb, &Wifi_nic::instance().device(), &ph->pt, &Wifi_nic::instance().device());
}
return NET_RX_SUCCESS;
}
- if (Root::instance->session)
- Root::instance->session->receive(skb);
-
+ Wifi_nic::instance().handle_driver_rx_packet(skb);
dev_kfree_skb(skb);
return NET_RX_SUCCESS;
}
diff --git a/repos/os/lib/import/import-nic_driver.mk b/repos/os/lib/import/import-nic_driver.mk
new file mode 100644
index 0000000000..e9d49b5c35
--- /dev/null
+++ b/repos/os/lib/import/import-nic_driver.mk
@@ -0,0 +1 @@
+REP_INC_DIR += src/drivers/nic/include
diff --git a/repos/os/lib/mk/nic_driver.mk b/repos/os/lib/mk/nic_driver.mk
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/repos/os/recipes/api/nic_driver/content.mk b/repos/os/recipes/api/nic_driver/content.mk
new file mode 100644
index 0000000000..6b395e271a
--- /dev/null
+++ b/repos/os/recipes/api/nic_driver/content.mk
@@ -0,0 +1,12 @@
+MIRROR_FROM_REP_DIR := \
+ src/drivers/nic/include \
+ lib/mk/nic_driver.mk \
+ lib/import/import-nic_driver.mk
+
+content: $(MIRROR_FROM_REP_DIR) LICENSE
+
+$(MIRROR_FROM_REP_DIR):
+ $(mirror_from_rep_dir)
+
+LICENSE:
+ cp $(GENODE_DIR)/LICENSE $@
diff --git a/repos/os/recipes/api/nic_driver/hash b/repos/os/recipes/api/nic_driver/hash
new file mode 100644
index 0000000000..5367907f68
--- /dev/null
+++ b/repos/os/recipes/api/nic_driver/hash
@@ -0,0 +1 @@
+2020-12-09-b 594a8cbf9fc6c84f60947a4b940540b5aa6e1486
diff --git a/repos/os/recipes/src/lan9118_nic_drv/used_apis b/repos/os/recipes/src/lan9118_nic_drv/used_apis
index f135ea45d1..add8b7b85c 100644
--- a/repos/os/recipes/src/lan9118_nic_drv/used_apis
+++ b/repos/os/recipes/src/lan9118_nic_drv/used_apis
@@ -2,3 +2,5 @@ base
os
platform_session
nic_session
+uplink_session
+nic_driver
diff --git a/repos/os/recipes/src/linux_nic_drv/used_apis b/repos/os/recipes/src/linux_nic_drv/used_apis
index f8896ba8d7..37fabb6132 100644
--- a/repos/os/recipes/src/linux_nic_drv/used_apis
+++ b/repos/os/recipes/src/linux_nic_drv/used_apis
@@ -2,3 +2,5 @@ base
base-linux
os
nic_session
+uplink_session
+nic_driver
diff --git a/repos/os/recipes/src/virtio_nic_drv/used_apis b/repos/os/recipes/src/virtio_nic_drv/used_apis
index 626bf71970..3714501d9c 100644
--- a/repos/os/recipes/src/virtio_nic_drv/used_apis
+++ b/repos/os/recipes/src/virtio_nic_drv/used_apis
@@ -2,4 +2,6 @@ base
os
virtio
nic_session
+uplink_session
+nic_driver
platform_session
diff --git a/repos/os/recipes/src/zynq_nic_drv/used_apis b/repos/os/recipes/src/zynq_nic_drv/used_apis
index 51485221e5..61ee78a6b5 100644
--- a/repos/os/recipes/src/zynq_nic_drv/used_apis
+++ b/repos/os/recipes/src/zynq_nic_drv/used_apis
@@ -1,3 +1,5 @@
base
os
nic_session
+uplink_session
+nic_driver
diff --git a/repos/os/src/drivers/nic/include/drivers/nic/mode.h b/repos/os/src/drivers/nic/include/drivers/nic/mode.h
new file mode 100644
index 0000000000..85e7336c90
--- /dev/null
+++ b/repos/os/src/drivers/nic/include/drivers/nic/mode.h
@@ -0,0 +1,41 @@
+/*
+ * \brief NIC driver mode regarding the session used for packet transmission
+ * \author Martin Stein
+ * \date 2020-12-07
+ */
+
+/*
+ * 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 _DRIVERS__NIC__MODE_H_
+#define _DRIVERS__NIC__MODE_H_
+
+/* Genode includes */
+#include
+
+namespace Genode
+{
+ enum class Nic_driver_mode { NIC_SERVER, UPLINK_CLIENT };
+
+ inline Nic_driver_mode read_nic_driver_mode(Xml_node const &driver_cfg)
+ {
+ String<16> const mode_str {
+ driver_cfg.attribute_value("mode", String<16>("")) };
+
+ if (mode_str == "nic_server") {
+
+ return Nic_driver_mode::NIC_SERVER;
+ }
+ if (mode_str == "uplink_client") {
+
+ return Nic_driver_mode::UPLINK_CLIENT;
+ }
+ return Nic_driver_mode::NIC_SERVER;
+ }
+}
+
+#endif /* _DRIVERS__NIC__MODE_H_ */
diff --git a/repos/os/src/drivers/nic/include/drivers/nic/uplink_client_base.h b/repos/os/src/drivers/nic/include/drivers/nic/uplink_client_base.h
new file mode 100644
index 0000000000..a3b31fcda3
--- /dev/null
+++ b/repos/os/src/drivers/nic/include/drivers/nic/uplink_client_base.h
@@ -0,0 +1,341 @@
+/*
+ * \brief Generic base class for the Uplink client role of NIC drivers
+ * \author Martin Stein
+ * \date 2020-12-07
+ */
+
+/*
+ * 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 _DRIVERS__NIC__UPLINK_CLIENT_BASE_H_
+#define _DRIVERS__NIC__UPLINK_CLIENT_BASE_H_
+
+/* Genode includes */
+#include
+#include
+#include
+
+namespace Genode {
+
+ class Uplink_client_base;
+}
+
+class Genode::Uplink_client_base : Noncopyable
+{
+ protected:
+
+ enum class Transmit_result { ACCEPTED, REJECTED, RETRY };
+
+ enum class Write_result { WRITE_SUCCEEDED, WRITE_FAILED };
+
+ enum class Burst_result
+ {
+ BURST_SUCCEEDED,
+ BURST_FAILED,
+ BURST_CONTINUE
+ };
+
+ enum { PKT_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE };
+ enum { BUF_SIZE = Uplink::Session::QUEUE_SIZE * PKT_SIZE };
+
+ Env &_env;
+ Allocator &_alloc;
+ Net::Mac_address _drv_mac_addr;
+ bool _drv_mac_addr_used { false };
+ bool _drv_link_state { false };
+ Constructible _conn { };
+ Nic::Packet_allocator _conn_pkt_alloc { &_alloc };
+ Signal_handler _conn_rx_ready_to_ack_handler { _env.ep(), *this, &Uplink_client_base::_conn_rx_handle_ready_to_ack };
+ Signal_handler _conn_rx_packet_avail_handler { _env.ep(), *this, &Uplink_client_base::_conn_rx_handle_packet_avail };
+ Signal_handler _conn_tx_ack_avail_handler { _env.ep(), *this, &Uplink_client_base::_conn_tx_handle_ack_avail };
+ Signal_handler _conn_tx_ready_to_submit_handler { _env.ep(), *this, &Uplink_client_base::_conn_tx_handle_ready_to_submit };
+ Packet_descriptor _save { };
+
+
+ /*****************************************
+ ** Interface towards Uplink connection **
+ *****************************************/
+
+ void _conn_tx_handle_ready_to_submit() { }
+
+ void _conn_rx_handle_ready_to_ack() { }
+
+ void _conn_tx_handle_ack_avail()
+ {
+ while (_conn->tx()->ack_avail()) {
+
+ _conn->tx()->release_packet(_conn->tx()->get_acked_packet());
+ }
+ }
+
+ void _conn_rx_handle_packet_avail()
+ {
+ if (_custom_conn_rx_packet_avail_handler()) {
+
+ _custom_conn_rx_handle_packet_avail();
+
+ } else if (_drv_supports_transmit_pkt_burst()) {
+
+ _drv_transmit_pkt_burst_prepare();
+
+ /* submit received packets to lower layer */
+ while (((_conn->rx()->packet_avail() || _save.size()) && _conn->rx()->ready_to_ack())) {
+
+ Packet_descriptor packet = _save.size() ? _save : _conn->rx()->get_packet();
+ _save = Packet_descriptor();
+
+ if (!packet.size()) {
+
+ class Invalid_packet { };
+ throw Invalid_packet { };
+ }
+
+ char const *packet_base {
+ _conn->rx()->packet_content(packet) };
+
+ Burst_result const result {
+ _drv_transmit_pkt_burst_step(
+ packet, packet_base, _save) };
+
+ switch (result) {
+ case Burst_result::BURST_FAILED:
+ return;
+ case Burst_result::BURST_CONTINUE:
+ continue;
+ case Burst_result::BURST_SUCCEEDED:
+ break;
+ }
+
+ /* acknowledge to client */
+ _conn->rx()->acknowledge_packet(packet);
+ }
+ _drv_transmit_pkt_burst_finish();
+
+ } else {
+
+ bool drv_ready_to_transmit_pkt { _drv_link_state };
+ bool pkts_transmitted { false };
+
+ while (drv_ready_to_transmit_pkt &&
+ _conn->rx()->packet_avail() &&
+ _conn->rx()->ready_to_ack()) {
+
+ Packet_descriptor const conn_rx_pkt {
+ _conn->rx()->get_packet() };
+
+ if (conn_rx_pkt.size() > 0 &&
+ _conn->rx()->packet_valid(conn_rx_pkt)) {
+
+ const char *const conn_rx_pkt_base {
+ _conn->rx()->packet_content(conn_rx_pkt) };
+
+ switch (_drv_transmit_pkt(conn_rx_pkt_base,
+ conn_rx_pkt.size())) {
+
+ case Transmit_result::ACCEPTED:
+
+ pkts_transmitted = true;
+ _conn->rx()->acknowledge_packet(conn_rx_pkt);
+ break;
+
+ case Transmit_result::REJECTED:
+
+ warning("failed to forward packet from "
+ "Uplink-connection RX to driver");
+
+ _conn->rx()->acknowledge_packet(conn_rx_pkt);
+ break;
+
+ case Transmit_result::RETRY:
+
+ drv_ready_to_transmit_pkt = false;
+ break;
+ }
+
+ } else {
+
+ warning("ignoring invalid packet from Uplink-"
+ "connection RX");
+ }
+ }
+
+ if (pkts_transmitted) {
+
+ _drv_finish_transmitted_pkts();
+ }
+ }
+ }
+
+
+ /***************************************************
+ ** Generic back end for interface towards driver **
+ ***************************************************/
+
+ template
+ void _drv_rx_handle_pkt(size_t conn_tx_pkt_size,
+ FUNC && write_to_conn_tx_pkt)
+ {
+ if (!_conn.constructed()) {
+ return;
+ }
+ _conn_tx_handle_ack_avail();
+
+ if (!_conn->tx()->ready_to_submit()) {
+ return;
+ }
+ try {
+ Packet_descriptor conn_tx_pkt {
+ _conn->tx()->alloc_packet(conn_tx_pkt_size) };
+
+ void *conn_tx_pkt_base {
+ _conn->tx()->packet_content(conn_tx_pkt) };
+
+ size_t adjusted_conn_tx_pkt_size {
+ conn_tx_pkt_size };
+
+ Write_result write_result {
+ write_to_conn_tx_pkt(
+ conn_tx_pkt_base,
+ adjusted_conn_tx_pkt_size) };
+
+ switch (write_result) {
+ case Write_result::WRITE_SUCCEEDED:
+
+ if (adjusted_conn_tx_pkt_size == conn_tx_pkt_size) {
+
+ _conn->tx()->submit_packet(conn_tx_pkt);
+
+ } else if (adjusted_conn_tx_pkt_size < conn_tx_pkt_size) {
+
+ Packet_descriptor adjusted_conn_tx_pkt {
+ conn_tx_pkt.offset(), adjusted_conn_tx_pkt_size };
+
+ _conn->tx()->submit_packet(adjusted_conn_tx_pkt);
+
+ } else {
+
+ class Bad_size { };
+ throw Bad_size { };
+ }
+ break;
+
+ case Write_result::WRITE_FAILED:
+
+ _conn->tx()->release_packet(conn_tx_pkt);
+ }
+
+ } catch (...) {
+
+ warning("exception while trying to forward packet from driver"
+ "to Uplink connection TX");
+
+ return;
+ }
+ }
+
+ void _drv_handle_link_state(bool drv_link_state)
+ {
+ if (_drv_link_state == drv_link_state) {
+ return;
+ }
+ _drv_link_state = drv_link_state;
+ if (drv_link_state) {
+
+ /* create connection */
+ _drv_mac_addr_used = true;
+ _conn.construct(
+ _env, &_conn_pkt_alloc, BUF_SIZE, BUF_SIZE,
+ _drv_mac_addr);
+
+ /* install signal handlers at connection */
+ _conn->rx_channel()->sigh_ready_to_ack(
+ _conn_rx_ready_to_ack_handler);
+
+ _conn->rx_channel()->sigh_packet_avail(
+ _conn_rx_packet_avail_handler);
+
+ _conn->tx_channel()->sigh_ack_avail(
+ _conn_tx_ack_avail_handler);
+
+ _conn->tx_channel()->sigh_ready_to_submit(
+ _conn_tx_ready_to_submit_handler);
+ } else {
+
+ _conn.destruct();
+ }
+ }
+
+ virtual void _drv_finish_transmitted_pkts() { }
+
+ virtual bool _drv_supports_transmit_pkt_burst()
+ {
+ return false;
+ }
+
+ virtual void _drv_transmit_pkt_burst_finish()
+ {
+ class Unexpected_call { };
+ throw Unexpected_call { };
+ }
+
+ virtual void _drv_transmit_pkt_burst_prepare()
+ {
+ class Unexpected_call { };
+ throw Unexpected_call { };
+ }
+
+ virtual Burst_result
+ _drv_transmit_pkt_burst_step(Packet_descriptor const &packet,
+ char const *packet_base,
+ Packet_descriptor &save)
+ {
+ (void)packet;
+ (void)packet_base;
+ (void)save;
+ class Unexpected_call { };
+ throw Unexpected_call { };
+ }
+
+ virtual Transmit_result
+ _drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size) = 0;
+
+ virtual void _custom_conn_rx_handle_packet_avail()
+ {
+ class Unexpected_call { };
+ throw Unexpected_call { };
+ }
+
+ virtual bool _custom_conn_rx_packet_avail_handler() { return false; }
+
+ public:
+
+ Uplink_client_base(Env &env,
+ Allocator &alloc,
+ Net::Mac_address const &drv_mac_addr)
+ :
+ _env { env },
+ _alloc { alloc },
+ _drv_mac_addr { drv_mac_addr }
+ {
+ log("MAC address ", _drv_mac_addr);
+ }
+
+ void mac_address(Net::Mac_address const &mac_address)
+ {
+ if (_drv_mac_addr_used) {
+
+ class Already_in_use { };
+ throw Already_in_use { };
+ }
+ _drv_mac_addr = mac_address;
+ }
+
+ virtual ~Uplink_client_base() { }
+};
+
+#endif /* _DRIVERS__NIC__UPLINK_CLIENT_BASE_H_ */
diff --git a/repos/os/src/drivers/nic/lan9118/lan9118.h b/repos/os/src/drivers/nic/lan9118/lan9118.h
index 636db377e9..ebcd81e98f 100644
--- a/repos/os/src/drivers/nic/lan9118/lan9118.h
+++ b/repos/os/src/drivers/nic/lan9118/lan9118.h
@@ -15,6 +15,7 @@
#ifndef _DRIVERS__NIC__SPEC__LAN9118__LAN9118_H_
#define _DRIVERS__NIC__SPEC__LAN9118__LAN9118_H_
+/* Genode includes */
#include
#include
#include
@@ -22,15 +23,20 @@
#include
#include
-class Lan9118 : public Nic::Session_component
+/* NIC driver includes */
+#include
+
+class Lan9118_base
{
private:
/*
* Noncopyable
*/
- Lan9118(Lan9118 const &);
- Lan9118 &operator = (Lan9118 const &);
+ Lan9118_base(Lan9118_base const &);
+ Lan9118_base &operator = (Lan9118_base const &);
+
+ protected:
/**
* MMIO register offsets
@@ -74,13 +80,6 @@ class Lan9118 : public Nic::Session_component
MAC_CSR_CMD_WRITE = (0 << 30),
};
- Genode::Attached_dataspace _mmio;
- volatile Genode::uint32_t *_reg_base;
- Timer::Connection _timer;
- Nic::Mac_address _mac_addr { };
- Genode::Irq_session_client _irq;
- Genode::Signal_handler _irq_handler;
-
/**
* Information about a received packet, used internally
*/
@@ -93,6 +92,12 @@ class Lan9118 : public Nic::Session_component
{ }
};
+ Genode::Attached_dataspace _mmio;
+ volatile Genode::uint32_t *_reg_base;
+ Timer::Connection _timer;
+ Nic::Mac_address _mac_addr { };
+ Genode::Irq_session_client _irq;
+
/**
* Read 32-bit wide MMIO register
*/
@@ -193,46 +198,38 @@ class Lan9118 : public Nic::Session_component
return _reg_read(RX_FIFO_INF) & 0xffff;
}
- protected:
-
- bool _send()
+ void _drv_tx_transmit_pkt(Genode::size_t packet_size,
+ Genode::uint32_t const *src,
+ bool &continue_sending,
+ bool &ack_packet)
{
- if (!_tx.sink()->ready_to_ack())
- return false;
-
- if (!_tx.sink()->packet_avail())
- return false;
-
- Genode::Packet_descriptor packet = _tx.sink()->get_packet();
- if (!packet.size() || !_tx.sink()->packet_valid(packet)) {
- Genode::warning("Invalid tx packet");
- return true;
- }
-
/* limit size to 11 bits, the maximum supported by lan9118 */
enum { MAX_PACKET_SIZE_LOG2 = 11 };
Genode::size_t const max_size = (1 << MAX_PACKET_SIZE_LOG2) - 1;
- if (packet.size() > max_size) {
- Genode::error("packet size ", packet.size(), " too large, "
+ if (packet_size > max_size) {
+ Genode::error("packet size ", packet_size, " too large, "
"limit is ", max_size);
- return true;
+
+ continue_sending = true;
+ ack_packet = false;
+ return;
}
enum { FIRST_SEG = (1 << 13),
LAST_SEG = (1 << 12) };
- Genode::uint32_t const cmd_a = packet.size() | FIRST_SEG | LAST_SEG,
- cmd_b = packet.size();
+ Genode::uint32_t const cmd_a = packet_size | FIRST_SEG | LAST_SEG,
+ cmd_b = packet_size;
- unsigned count = Genode::align_addr(packet.size(), 2) >> 2;
- Genode::uint32_t *src = (Genode::uint32_t *)_tx.sink()->packet_content(packet);
+ unsigned count = Genode::align_addr(packet_size, 2) >> 2;
/* check space left in tx data fifo */
Genode::size_t const fifo_avail = _reg_read(TX_FIFO_INF) & 0xffff;
if (fifo_avail < count*4 + sizeof(cmd_a) + sizeof(cmd_b)) {
Genode::error("tx fifo overrun, ignore packet");
- _tx.sink()->acknowledge_packet(packet);
- return false;
+ continue_sending = false;
+ ack_packet = true;
+ return;
}
_reg_write(TX_DATA_FIFO, cmd_a);
@@ -242,56 +239,39 @@ class Lan9118 : public Nic::Session_component
for (; count--; src++)
_reg_write(TX_DATA_FIFO, *src);
- _tx.sink()->acknowledge_packet(packet);
- return true;
+ continue_sending = true;
+ ack_packet = true;
+ return;
}
- void _handle_packet_stream() override
+ void _finish_handle_irq()
{
- while (_rx.source()->ack_avail())
- _rx.source()->release_packet(_rx.source()->get_acked_packet());
-
- while (_send()) ;
- }
-
- void _handle_irq()
- {
- using namespace Genode;
-
- _handle_packet_stream();
-
- while (_rx_packet_avail() && _rx.source()->ready_to_submit()) {
-
- /* read packet from NIC, copy to client buffer */
- Rx_packet_info packet = _rx_packet_info();
-
- /* align size to 32-bit boundary */
- size_t const size = align_addr(packet.size, 2);
-
- /* allocate rx packet buffer */
- Nic::Packet_descriptor p;
- try {
- p = _rx.source()->alloc_packet(size);
- } catch (Session::Rx::Source::Packet_alloc_failed) { return; }
-
- uint32_t *dst = (uint32_t *)_rx.source()->packet_content(p);
-
- /* calculate number of words to be read from rx fifo */
- size_t count = min(size, _rx_data_pending()) >> 2;
-
- /* copy payload from rx fifo to client buffer */
- for (; count--; )
- *dst++ = _reg_read(RX_DATA_FIFO);
-
- _rx.source()->submit_packet(p);
- }
-
/* acknowledge all pending irqs */
_reg_write(INT_STS, ~0);
_irq.ack_irq();
}
+ void _drv_rx_copy_pkt(Genode::size_t size,
+ Genode::uint32_t *dst)
+ {
+ /* calculate number of words to be read from rx fifo */
+ Genode::size_t count = Genode::min(size, _rx_data_pending()) >> 2;
+
+ /* copy payload from rx fifo to client buffer */
+ for (; count--; )
+ *dst++ = _reg_read(RX_DATA_FIFO);
+ }
+
+ Genode::size_t _drv_rx_pkt_size()
+ {
+ /* read packet from NIC, copy to client buffer */
+ Rx_packet_info packet = _rx_packet_info();
+
+ /* align size to 32-bit boundary */
+ return Genode::align_addr(packet.size, 2);
+ }
+
public:
/**
@@ -299,26 +279,17 @@ class Lan9118 : public Nic::Session_component
*/
class Device_not_supported { };
- /**
- * Constructor
- *
- * \throw Device_not_supported
- */
- Lan9118(Genode::Io_mem_dataspace_capability ds_cap,
- Genode::Irq_session_capability irq_cap,
- Genode::size_t const tx_buf_size,
- Genode::size_t const rx_buf_size,
- Genode::Allocator & rx_block_md_alloc,
- Genode::Env & env)
- : Session_component(tx_buf_size, rx_buf_size, Genode::CACHED,
- rx_block_md_alloc, env),
- _mmio(env.rm(), ds_cap),
- _reg_base(_mmio.local_addr()),
- _timer(env),
- _irq(irq_cap),
- _irq_handler(env.ep(), *this, &Lan9118::_handle_irq)
+ Lan9118_base(Genode::Io_mem_dataspace_capability ds_cap,
+ Genode::Irq_session_capability irq_cap,
+ Genode::Signal_context_capability irq_handler,
+ Genode::Env & env)
+ :
+ _mmio { env.rm(), ds_cap },
+ _reg_base { _mmio.local_addr() },
+ _timer { env },
+ _irq { irq_cap }
{
- _irq.sigh(_irq_handler);
+ _irq.sigh(irq_handler);
_irq.ack_irq();
unsigned long const id_rev = _reg_read(ID_REV),
@@ -387,10 +358,7 @@ class Lan9118 : public Nic::Session_component
_reg_write(IRQ_CFG, IRQ_CFG_EN | IRQ_CFG_POL | IRQ_CFG_TYPE);
}
- /**
- * Destructor
- */
- ~Lan9118()
+ virtual ~Lan9118_base()
{
Genode::log("disable NIC");
@@ -400,6 +368,93 @@ class Lan9118 : public Nic::Session_component
/* disable rx and tx at MAX */
_mac_csr_write(MAC_CR, 0);
}
+};
+
+
+class Lan9118 : public Nic::Session_component,
+ public Genode::Signal_handler,
+ public Lan9118_base
+{
+ protected:
+
+ bool _send()
+ {
+ if (!_tx.sink()->ready_to_ack())
+ return false;
+
+ if (!_tx.sink()->packet_avail())
+ return false;
+
+ Genode::Packet_descriptor packet = _tx.sink()->get_packet();
+ if (!packet.size() || !_tx.sink()->packet_valid(packet)) {
+ Genode::warning("Invalid tx packet");
+ return true;
+ }
+
+ bool continue_sending { false };
+ bool ack_packet { false };
+ _drv_tx_transmit_pkt(
+ packet.size(),
+ (Genode::uint32_t *)_tx.sink()->packet_content(packet),
+ continue_sending,
+ ack_packet);
+
+ if (ack_packet) {
+ _tx.sink()->acknowledge_packet(packet);
+ }
+ return continue_sending;
+ }
+
+ void _handle_packet_stream() override
+ {
+ while (_rx.source()->ack_avail())
+ _rx.source()->release_packet(_rx.source()->get_acked_packet());
+
+ while (_send()) ;
+ }
+
+ void _handle_irq()
+ {
+ using namespace Genode;
+
+ _handle_packet_stream();
+
+ while (_rx_packet_avail() && _rx.source()->ready_to_submit()) {
+
+ /* allocate rx packet buffer */
+ size_t const size { _drv_rx_pkt_size() };
+ Nic::Packet_descriptor p;
+ try {
+ p = _rx.source()->alloc_packet(size);
+ } catch (Session::Rx::Source::Packet_alloc_failed) { return; }
+
+ uint32_t *dst = (uint32_t *)_rx.source()->packet_content(p);
+ _drv_rx_copy_pkt(size, dst);
+
+ _rx.source()->submit_packet(p);
+ }
+ _finish_handle_irq();
+ }
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \throw Device_not_supported
+ */
+ Lan9118(Genode::Io_mem_dataspace_capability ds_cap,
+ Genode::Irq_session_capability irq_cap,
+ Genode::size_t const tx_buf_size,
+ Genode::size_t const rx_buf_size,
+ Genode::Allocator & rx_block_md_alloc,
+ Genode::Env & env)
+ :
+ Session_component { tx_buf_size, rx_buf_size, Genode::CACHED,
+ rx_block_md_alloc, env },
+ Genode::Signal_handler { env.ep(), *this, &Lan9118::_handle_irq },
+ Lan9118_base { ds_cap, irq_cap, *this, env }
+ { }
/**************************************
** Nic::Session_component interface **
@@ -417,4 +472,98 @@ class Lan9118 : public Nic::Session_component
}
};
+
+namespace Genode {
+
+ class Uplink_client;
+}
+
+
+class Genode::Uplink_client : public Signal_handler,
+ public Lan9118_base,
+ public Uplink_client_base
+{
+ private:
+
+ /************************
+ ** Uplink_client_base **
+ ************************/
+
+ Transmit_result
+ _drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size) override
+ {
+ bool continue_sending { false };
+ bool ack_packet { false };
+ _drv_tx_transmit_pkt(
+ conn_rx_pkt_size,
+ (Genode::uint32_t const*)conn_rx_pkt_base,
+ continue_sending,
+ ack_packet);
+
+ /*
+ * FIXME This seems to be a bug in the original (NIC client)
+ * driver. When having packets that don't fit the maximum
+ * transmittable size (limited by hardware) the driver
+ * didn't acknowledge the packet but continued forwarding
+ * packets from the session to the driver. Normally, we
+ * would expect the driver to acknowledge the packet without
+ * transmitting it (i.e., reject it).
+ */
+ if (!ack_packet && continue_sending) {
+ return Transmit_result::REJECTED;
+ }
+ /*
+ * FIXME This seems to be a bug in the original (NIC client)
+ * driver. When the hardware TX FIFO doesn't have enough
+ * space left to transmit the packet, the driver acknowledge
+ * the packet but didn't continue forwarding packets from
+ * the session to the hardware. Normally, we would expect
+ * the driver to not acknowledge the packet nor continue
+ * forwarding other packets (i.e., retry later).
+ */
+ if (ack_packet && !continue_sending) {
+ return Transmit_result::RETRY;
+ }
+ if (ack_packet && continue_sending) {
+ return Transmit_result::ACCEPTED;
+ }
+ class Unexpected_transmit_result { };
+ throw Unexpected_transmit_result { };
+ }
+
+ void _handle_irq()
+ {
+ while (_rx_packet_avail()) {
+
+ _drv_rx_handle_pkt(
+ _drv_rx_pkt_size(),
+ [&] (void *conn_tx_pkt_base,
+ size_t &conn_tx_pkt_size)
+ {
+ _drv_rx_copy_pkt(
+ conn_tx_pkt_size,
+ (uint32_t *)conn_tx_pkt_base);
+
+ return Write_result::WRITE_SUCCEEDED;
+ });
+ }
+ _finish_handle_irq();
+ }
+
+ public:
+
+ Uplink_client(Env &env,
+ Allocator &alloc,
+ Io_mem_dataspace_capability ds_cap,
+ Irq_session_capability irq_cap)
+ :
+ Signal_handler { env.ep(), *this, &Uplink_client::_handle_irq },
+ Lan9118_base { ds_cap, irq_cap, *this, env },
+ Uplink_client_base { env, alloc, _mac_addr }
+ {
+ _drv_handle_link_state(true);
+ }
+};
+
#endif /* _DRIVERS__NIC__SPEC__LAN9118__LAN9118_H_ */
diff --git a/repos/os/src/drivers/nic/lan9118/main.cc b/repos/os/src/drivers/nic/lan9118/main.cc
index 231c88254c..bc9657bbd5 100644
--- a/repos/os/src/drivers/nic/lan9118/main.cc
+++ b/repos/os/src/drivers/nic/lan9118/main.cc
@@ -24,23 +24,28 @@
#include
#include
+/* NIC driver includes */
+#include
+
/* driver code */
#include
-class Root : public Genode::Root_component
+using namespace Genode;
+
+class Nic_root : public Root_component
{
private:
- Genode::Env & _env;
- Platform::Connection _platform { _env };
- Platform::Device_client _device { _platform.device_by_index(0) };
+ Env &_env;
+ Platform::Device_client &_device;
- protected:
+
+ /********************
+ ** Root_component **
+ ********************/
Lan9118 *_create_session(const char *args) override
{
- using namespace Genode;
-
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);
@@ -53,27 +58,68 @@ class Root : public Genode::Root_component
tx_buf_size + rx_buf_size > ram_quota) {
error("insufficient 'ram_quota', got ", ram_quota, ", "
"need ", tx_buf_size + rx_buf_size);
- throw Genode::Insufficient_ram_quota();
+ throw Insufficient_ram_quota();
}
- return new (Root::md_alloc())
+ return new (md_alloc())
Lan9118(_device.io_mem_dataspace(), _device.irq(),
tx_buf_size, rx_buf_size, *md_alloc(), _env);
}
public:
- Root(Genode::Env &env, Genode::Allocator &md_alloc)
- : Genode::Root_component(env.ep(), md_alloc),
- _env(env) { }
+ Nic_root(Env &env,
+ Allocator &md_alloc,
+ Platform::Device_client &device)
+ :
+ Root_component { env.ep(), md_alloc },
+ _env { env },
+ _device { device }
+ { }
+};
+
+class Main
+{
+ private:
+
+ Env &_env;
+ Heap _heap { _env.ram(), _env.rm() };
+ Platform::Connection _platform { _env };
+ Platform::Device_client _device { _platform.device_by_index(0) };
+ Constructible _nic_root { };
+ Constructible _uplink_client { };
+
+ public:
+
+ Main (Env &env)
+ :
+ _env { env }
+ {
+ log("--- LAN9118 NIC driver started ---");
+
+ Attached_rom_dataspace config_rom { _env, "config" };
+ Nic_driver_mode const mode {
+ read_nic_driver_mode(config_rom.xml()) };
+
+ switch (mode) {
+ case Nic_driver_mode::NIC_SERVER:
+
+ _nic_root.construct(_env, _heap, _device);
+ _env.parent().announce(_env.ep().manage(*_nic_root));
+ break;
+
+ case Nic_driver_mode::UPLINK_CLIENT:
+
+ _uplink_client.construct(
+ _env, _heap, _device.io_mem_dataspace(), _device.irq());
+
+ break;
+ }
+ }
};
-void Component::construct(Genode::Env &env)
+void Component::construct(Env &env)
{
- static Genode::Heap heap(env.ram(), env.rm());
- static Root nic_root(env, heap);
- Genode::log("--- LAN9118 NIC driver started ---");
- env.parent().announce(env.ep().manage(nic_root));
+ static Main main { env };
}
diff --git a/repos/os/src/drivers/nic/lan9118/target.mk b/repos/os/src/drivers/nic/lan9118/target.mk
index 25fcba4e77..9ecfe7f85d 100644
--- a/repos/os/src/drivers/nic/lan9118/target.mk
+++ b/repos/os/src/drivers/nic/lan9118/target.mk
@@ -1,5 +1,5 @@
REQUIRES = arm_v7
TARGET = lan9118_nic_drv
SRC_CC = main.cc
-LIBS = base
+LIBS = base nic_driver
INC_DIR += $(PRG_DIR)
diff --git a/repos/os/src/drivers/nic/spec/linux/main.cc b/repos/os/src/drivers/nic/spec/linux/main.cc
index 841f725e02..f309065cf4 100644
--- a/repos/os/src/drivers/nic/spec/linux/main.cc
+++ b/repos/os/src/drivers/nic/spec/linux/main.cc
@@ -32,6 +32,10 @@
#include
#include
+/* NIC driver includes */
+#include
+#include
+
/* Linux */
#include
#include
@@ -47,6 +51,20 @@ namespace Server {
}
+static Net::Mac_address default_mac_address()
+{
+ /* fall back to fake MAC address (unicast, locally managed) */
+ Nic::Mac_address mac_addr { };
+ mac_addr.addr[0] = 0x02;
+ mac_addr.addr[1] = 0x00;
+ mac_addr.addr[2] = 0x00;
+ mac_addr.addr[3] = 0x00;
+ mac_addr.addr[4] = 0x00;
+ mac_addr.addr[5] = 0x01;
+ return mac_addr;
+}
+
+
class Linux_session_component : public Nic::Session_component
{
private:
@@ -231,15 +249,10 @@ class Linux_session_component : public Nic::Session_component
:
Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, rx_block_md_alloc, env),
_config_rom(env, "config"),
- _tap_fd(_setup_tap_fd()), _rx_thread(env, _tap_fd, _packet_stream_dispatcher)
+ _tap_fd(_setup_tap_fd()),
+ _rx_thread(env, _tap_fd, _packet_stream_dispatcher)
{
- /* fall back to fake MAC address (unicast, locally managed) */
- _mac_addr.addr[0] = 0x02;
- _mac_addr.addr[1] = 0x00;
- _mac_addr.addr[2] = 0x00;
- _mac_addr.addr[3] = 0x00;
- _mac_addr.addr[4] = 0x00;
- _mac_addr.addr[5] = 0x01;
+ _mac_addr = default_mac_address();
/* try using configured MAC address */
try {
@@ -256,16 +269,198 @@ class Linux_session_component : public Nic::Session_component
};
+class Uplink_client : public Genode::Uplink_client_base
+{
+ private:
+
+ struct Rx_signal_thread : Genode::Thread
+ {
+ int fd;
+ Genode::Signal_context_capability sigh;
+ Genode::Blockade blockade { };
+
+ Rx_signal_thread(Genode::Env &env, int fd, Genode::Signal_context_capability sigh)
+ : Genode::Thread(env, "rx_signal", 0x1000), fd(fd), sigh(sigh) { }
+
+ void entry() override
+ {
+ while (true) {
+ /* wait for packet arrival on fd */
+ int ret;
+ fd_set rfds;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ do { ret = select(fd + 1, &rfds, 0, 0, 0); } while (ret < 0);
+
+ /* signal incoming packet */
+ Genode::Signal_transmitter(sigh).submit();
+
+ blockade.block();
+ }
+ }
+ };
+
+ int _tap_fd;
+ Genode::Signal_handler _rx_handler { _env.ep(), *this, &Uplink_client::_handle_rx };
+ Rx_signal_thread _rx_thread { _env, _tap_fd, _rx_handler };
+
+ static int _init_tap_fd(Genode::Xml_node const &config)
+ {
+ /* open TAP device */
+ int ret;
+ struct ifreq ifr;
+
+ int fd = open("/dev/net/tun", O_RDWR);
+ if (fd < 0) {
+ Genode::error("could not open /dev/net/tun: no virtual network emulation");
+ throw Genode::Exception();
+ }
+
+ /* set fd to non-blocking */
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ Genode::error("could not set /dev/net/tun to non-blocking");
+ throw Genode::Exception();
+ }
+
+ Genode::memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+
+ /* get tap device from config */
+ try {
+ Genode::Xml_node nic_node = config.sub_node("nic");
+ nic_node.attribute("tap").with_raw_value([&] (char const *ptr, size_t len) {
+ len = Genode::min(len, sizeof(ifr.ifr_name) - 1);
+ Genode::memcpy(ifr.ifr_name, ptr, len);
+ ifr.ifr_name[len] = 0;
+ });
+ Genode::log("using tap device \"", Genode::Cstring(ifr.ifr_name), "\"");
+ } catch (...) {
+ /* use tap0 if no config has been provided */
+ Genode::copy_cstring(ifr.ifr_name, "tap0", sizeof(ifr.ifr_name));
+ Genode::log("no config provided, using tap0");
+ }
+
+ ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
+ if (ret != 0) {
+ Genode::error("could not configure /dev/net/tun: no virtual network emulation");
+ close(fd);
+ /* this error is fatal */
+ throw Genode::Exception();
+ }
+
+ return fd;
+ }
+
+ static Net::Mac_address
+ _init_mac_address(Genode::Xml_node const &config)
+ {
+ try {
+ return
+ config.sub_node("nic").
+ attribute_value("mac", default_mac_address());
+
+ } catch (...) {
+
+ return default_mac_address();
+ }
+ }
+
+ void _handle_rx()
+ {
+ bool progress { true };
+ while (progress) {
+
+ progress = false;
+ size_t const max_pkt_size {
+ Nic::Packet_allocator::DEFAULT_PACKET_SIZE };
+
+ _drv_rx_handle_pkt(
+ max_pkt_size,
+ [&] (void *conn_tx_pkt_base,
+ size_t &adjusted_conn_tx_pkt_size)
+ {
+ long int const read_result {
+ read(_tap_fd, conn_tx_pkt_base, max_pkt_size) };
+
+ if (read_result <= 0) {
+
+ _rx_thread.blockade.wakeup();
+ return Write_result::WRITE_FAILED;
+ }
+ adjusted_conn_tx_pkt_size = read_result;
+ progress = true;
+ return Write_result::WRITE_SUCCEEDED;
+ });
+ }
+ }
+
+
+ /************************
+ ** Uplink_client_base **
+ ************************/
+
+ Transmit_result
+ _drv_transmit_pkt(const char *conn_rx_pkt_base,
+ size_t conn_rx_pkt_size) override
+ {
+ int ret;
+
+ /* non-blocking-write packet to TAP */
+ do {
+ ret = write(_tap_fd, conn_rx_pkt_base, conn_rx_pkt_size);
+ /* drop packet if write would block */
+ if (ret < 0 && errno == EAGAIN)
+ continue;
+
+ if (ret < 0) Genode::error("write: errno=", errno);
+ } while (ret < 0);
+
+ return Transmit_result::ACCEPTED;
+ }
+
+ public:
+
+ Uplink_client(Genode::Env &env,
+ Genode::Allocator &alloc,
+ Genode::Xml_node const &config)
+ :
+ Uplink_client_base {
+ env, alloc, _init_mac_address(config) },
+
+ _tap_fd { _init_tap_fd(config) }
+ {
+ _drv_handle_link_state(true);
+ _rx_thread.start();
+ }
+};
+
+
struct Server::Main
{
- Env &_env;
- Heap _heap { _env.ram(), _env.rm() };
-
- Nic::Root nic_root { _env, _heap };
+ Env &_env;
+ Heap _heap { _env.ram(), _env.rm() };
+ Genode::Attached_rom_dataspace _config_rom { _env, "config" };
Main(Env &env) : _env(env)
{
- _env.parent().announce(_env.ep().manage(nic_root));
+ Nic_driver_mode const mode {
+ read_nic_driver_mode(_config_rom.xml()) };
+
+ switch (mode) {
+ case Nic_driver_mode::NIC_SERVER:
+ {
+ Nic::Root &nic_root {
+ *new (_heap) Nic::Root(_env, _heap) };
+
+ _env.parent().announce(_env.ep().manage(nic_root));
+ break;
+ }
+ case Nic_driver_mode::UPLINK_CLIENT:
+
+ new (_heap) Uplink_client(_env, _heap, _config_rom.xml());
+ break;
+ }
}
};
diff --git a/repos/os/src/drivers/nic/spec/linux/target.mk b/repos/os/src/drivers/nic/spec/linux/target.mk
index 6d3ce2b422..b95e73002f 100644
--- a/repos/os/src/drivers/nic/spec/linux/target.mk
+++ b/repos/os/src/drivers/nic/spec/linux/target.mk
@@ -1,4 +1,4 @@
TARGET = linux_nic_drv
REQUIRES = linux
-LIBS = lx_hybrid
+LIBS = lx_hybrid nic_driver
SRC_CC = main.cc
diff --git a/repos/os/src/drivers/nic/spec/zynq/cadence_gem.h b/repos/os/src/drivers/nic/spec/zynq/cadence_gem.h
index bff42e275c..525df294fb 100644
--- a/repos/os/src/drivers/nic/spec/zynq/cadence_gem.h
+++ b/repos/os/src/drivers/nic/spec/zynq/cadence_gem.h
@@ -22,6 +22,9 @@
#include
#include
+/* NIC driver includes */
+#include
+
/* local includes */
#include "system_control.h"
#include "tx_buffer_descriptor.h"
@@ -30,16 +33,13 @@
namespace Genode
{
- /**
- * Base driver Xilinx EMAC PS module
- */
- class Cadence_gem
+ class Cadence_gem_base
:
- private Genode::Attached_mmio,
- public Nic::Session_component,
+ protected Genode::Attached_mmio,
public Phyio
{
private:
+
/**
* Control register
*/
@@ -96,8 +96,6 @@ namespace Genode
struct Phy_mgmt_idle : Bitfield<2, 1> {};
};
-
-
/**
* DMA Config register
*/
@@ -349,119 +347,13 @@ namespace Genode
class Phy_timeout_for_idle : public Genode::Exception {};
class Unkown_ethernet_speed : public Genode::Exception {};
-
- Timer::Connection _timer;
- System_control _sys_ctrl;
- Tx_buffer_descriptor _tx_buffer;
- Rx_buffer_descriptor _rx_buffer;
- Genode::Irq_connection _irq;
- Genode::Signal_handler _irq_handler;
- Marvel_phy _phy;
-
- addr_t _rx_buf_region;
- addr_t _tx_buf_region;
- size_t _rx_buf_size;
- size_t _tx_buf_size;
-
- void _init()
- {
- /* see 16.3.2 Configure the Controller */
-
- /* 1. Program the Network Configuration register (gem.net_cfg) */
- write(
- Config::Gige_en::bits(1) |
- Config::Speed_100::bits(1) |
- Config::Pause_en::bits(1) |
- Config::Full_duplex::bits(1) |
- Config::Multi_hash_en::bits(1) |
- Config::Mdc_clk_div::bits(Config::Mdc_clk_div::DIV_32) |
- Config::Dis_cp_pause::bits(1) |
- Config::Rx_chksum_en::bits(1) |
- Config::Fcs_remove::bits(1)
- );
-
-
- write( _rx_buffer.phys_addr() );
- write( _tx_buffer.phys_addr() );
-
-
- /* 3. Program the DMA Configuration register (gem.dma_cfg) */
- write( Dma_config::init() );
-
-
- /*
- * 4. Program the Network Control Register (gem.net_ctrl)
- * Enable MDIO, transmitter and receiver
- */
- write(Control::init());
-
-
-
- _phy.init();
-
- /* change emac clocks depending on phy autonegation result */
- uint32_t rclk = 0;
- uint32_t clk = 0;
- switch (_phy.eth_speed()) {
- case SPEED_1000:
- write(1);
- rclk = (0 << 4) | (1 << 0);
- clk = (1 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
- log("Autonegotiation result: 1Gbit/s");
- break;
- case SPEED_100:
- write(0);
- write(1);
- rclk = 1 << 0;
- clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
- log("Autonegotiation result: 100Mbit/s");
- break;
- case SPEED_10:
- write(0);
- write(0);
- rclk = 1 << 0;
- /* FIXME untested */
- clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
- log("Autonegotiation result: 10Mbit/s");
- break;
- default:
- throw Unkown_ethernet_speed();
- }
- _sys_ctrl.set_clk(clk, rclk);
-
-
- /* 16.3.6 Configure Interrupts */
- write(Interrupt_enable::Rx_complete::bits(1) |
- Interrupt_enable::Rx_overrun::bits(1) |
- Interrupt_enable::Pause_received::bits(1) |
- Interrupt_enable::Pause_zero::bits(1) |
- Interrupt_enable::Rx_used_read::bits(1));
- }
-
- void _deinit()
- {
- /* 16.3.1 Initialize the Controller */
-
- /* Disable all interrupts */
- write(0x7FFFEFF);
-
- /* Disable the receiver & transmitter */
- write(0);
- write(Control::Clear_statistics::bits(1));
-
- write(0xFF);
- write(0x0F);
- write(0);
-
- write(0);
- write(0);
-
- /* Clear the Hash registers for the mac address
- * pointed by AddressPtr
- */
- write(0);
- }
-
+ Timer::Connection _timer;
+ System_control _sys_ctrl;
+ Genode::Irq_connection _irq;
+ Marvel_phy _phy;
+ Tx_buffer_sink &_tx_buffer_sink;
+ Tx_buffer_descriptor _tx_buffer;
+ Rx_buffer_descriptor _rx_buffer;
void _mdio_wait()
{
@@ -479,7 +371,6 @@ namespace Genode
}
}
-
void _phy_setup_op(const uint8_t phyaddr, const uint8_t regnum, const uint16_t data, const Phy_maintenance::Operation::Type op)
{
_mdio_wait();
@@ -496,15 +387,69 @@ namespace Genode
_mdio_wait();
}
- inline void _handle_acks()
+
+ /***********
+ ** Phyio **
+ ***********/
+
+ void phy_write(const uint8_t phyaddr, const uint8_t regnum, const uint16_t data) override
{
- while (_rx.source()->ack_avail()) {
- Nic::Packet_descriptor p = _rx.source()->get_acked_packet();
- _rx_buffer.reset_descriptor(p);
- }
+ _phy_setup_op(phyaddr, regnum, data, Phy_maintenance::Operation::WRITE);
}
- virtual void _handle_irq()
+ void phy_read(const uint8_t phyaddr, const uint8_t regnum, uint16_t& data) override
+ {
+ _phy_setup_op(phyaddr, regnum, 0, Phy_maintenance::Operation::READ);
+
+ data = read();
+ }
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param base MMIO base address
+ */
+ Cadence_gem_base(Genode::Env &env,
+ addr_t const base,
+ size_t const size,
+ int const irq,
+ Tx_buffer_sink &tx_buffer_sink,
+ Rx_buffer_source &rx_buffer_source)
+ :
+ Genode::Attached_mmio(env, base, size),
+ _timer(env),
+ _sys_ctrl(env, _timer),
+ _irq(env, irq),
+ _phy(*this, _timer),
+ _tx_buffer_sink(tx_buffer_sink),
+ _tx_buffer(env, tx_buffer_sink, _timer),
+ _rx_buffer(env, rx_buffer_source)
+ { }
+
+ void transmit_packet(Packet_descriptor packet)
+ {
+ _tx_buffer.add_to_queue(packet);
+ write(Control::start_tx());
+ }
+
+ Nic::Mac_address read_mac_address()
+ {
+ Nic::Mac_address mac;
+ uint32_t* const low_addr_pointer = reinterpret_cast(&mac.addr[0]);
+ uint16_t* const high_addr_pointer = reinterpret_cast(&mac.addr[4]);
+
+ *low_addr_pointer = read();
+ *high_addr_pointer = read();
+
+ return mac;
+ }
+
+ template
+ void handle_irq(RECEIVE_PKT && receive_pkt,
+ HANDLE_ACKS && handle_acks)
{
/* 16.3.9 Receiving Frames */
/* read interrupt status, to detect the interrupt reason */
@@ -513,16 +458,11 @@ namespace Genode
const Tx_status::access_t txStatus = read();
if ( Interrupt_status::Rx_complete::get(status) ) {
+
while (_rx_buffer.next_packet()) {
- _handle_acks();
-
- Nic::Packet_descriptor p = _rx_buffer.get_packet_descriptor();
- if (_rx.source()->packet_valid(p))
- _rx.source()->submit_packet(p);
- else
- Genode::error("invalid packet descriptor ", Genode::Hex(p.offset()),
- " size ", Genode::Hex(p.size()));
+ handle_acks();
+ receive_pkt(_rx_buffer.get_packet_descriptor());
}
/* reset receive complete interrupt */
@@ -530,7 +470,7 @@ namespace Genode
write(Interrupt_status::Rx_complete::bits(1));
}
else {
- _handle_acks();
+ handle_acks();
}
/* handle Rx/Tx errors */
@@ -540,7 +480,7 @@ namespace Genode
write(0);
write(0);
- _tx_buffer.reset(*_tx.sink());
+ _tx_buffer.reset(_tx_buffer_sink);
_rx_buffer.reset();
write(1);
@@ -556,7 +496,7 @@ namespace Genode
|| Tx_status::Tx_err_bufexh::get(txStatus)) {
write(0);
- _tx_buffer.reset(*_tx.sink());
+ _tx_buffer.reset(_tx_buffer_sink);
write(1);
Genode::error("Tx error: resetting transceiver");
@@ -632,63 +572,109 @@ namespace Genode
_irq.ack_irq();
}
- public:
- /**
- * Constructor
- *
- * \param base MMIO base address
- */
- Cadence_gem(Genode::size_t const tx_buf_size,
- Genode::size_t const rx_buf_size,
- Genode::Allocator &rx_block_md_alloc,
- Genode::Env &env,
- addr_t const base, size_t const size, const int irq)
- :
- Genode::Attached_mmio(env, base, size),
- Session_component(tx_buf_size, rx_buf_size, Genode::UNCACHED,
- rx_block_md_alloc, env),
- _timer(env),
- _sys_ctrl(env, _timer),
- _tx_buffer(env, *_tx.sink(), _timer),
- _rx_buffer(env, *_rx.source()),
- _irq(env, irq),
- _irq_handler(env.ep(), *this, &Cadence_gem::_handle_irq),
- _phy(*this, _timer),
- _rx_buf_region((addr_t)_tx_ds.local_addr()),
- _tx_buf_region((addr_t)_rx_ds.local_addr()),
- _rx_buf_size(_tx_ds.size()),
- _tx_buf_size(_rx_ds.size())
+ void init(Signal_context_capability irq_handler)
{
- _irq.sigh(_irq_handler);
+ _irq.sigh(irq_handler);
_irq.ack_irq();
- _deinit();
- _init();
+
+ /* see 16.3.2 Configure the Controller */
+
+ /* 1. Program the Network Configuration register (gem.net_cfg) */
+ write(
+ Config::Gige_en::bits(1) |
+ Config::Speed_100::bits(1) |
+ Config::Pause_en::bits(1) |
+ Config::Full_duplex::bits(1) |
+ Config::Multi_hash_en::bits(1) |
+ Config::Mdc_clk_div::bits(Config::Mdc_clk_div::DIV_32) |
+ Config::Dis_cp_pause::bits(1) |
+ Config::Rx_chksum_en::bits(1) |
+ Config::Fcs_remove::bits(1)
+ );
+
+
+ write(_rx_buffer.phys_addr());
+ write(_tx_buffer.phys_addr());
+
+
+ /* 3. Program the DMA Configuration register (gem.dma_cfg) */
+ write( Dma_config::init() );
+
+
+ /*
+ * 4. Program the Network Control Register (gem.net_ctrl)
+ * Enable MDIO, transmitter and receiver
+ */
+ write(Control::init());
+
+
+
+ _phy.init();
+
+ /* change emac clocks depending on phy autonegation result */
+ uint32_t rclk = 0;
+ uint32_t clk = 0;
+ switch (_phy.eth_speed()) {
+ case SPEED_1000:
+ write(1);
+ rclk = (0 << 4) | (1 << 0);
+ clk = (1 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
+ log("Autonegotiation result: 1Gbit/s");
+ break;
+ case SPEED_100:
+ write(0);
+ write(1);
+ rclk = 1 << 0;
+ clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
+ log("Autonegotiation result: 100Mbit/s");
+ break;
+ case SPEED_10:
+ write(0);
+ write(0);
+ rclk = 1 << 0;
+ /* FIXME untested */
+ clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
+ log("Autonegotiation result: 10Mbit/s");
+ break;
+ default:
+ throw Unkown_ethernet_speed();
+ }
+ _sys_ctrl.set_clk(clk, rclk);
+
+
+ /* 16.3.6 Configure Interrupts */
+ write(Interrupt_enable::Rx_complete::bits(1) |
+ Interrupt_enable::Rx_overrun::bits(1) |
+ Interrupt_enable::Pause_received::bits(1) |
+ Interrupt_enable::Pause_zero::bits(1) |
+ Interrupt_enable::Rx_used_read::bits(1));
}
- ~Cadence_gem()
+ void deinit()
{
- // TODO dsiable tx and rx and clean up irq registration
- _deinit();
+ /* 16.3.1 Initialize the Controller */
+
+ /* Disable all interrupts */
+ write(0x7FFFEFF);
+
+ /* Disable the receiver & transmitter */
+ write(0);
+ write(Control::Clear_statistics::bits(1));
+
+ write(0xFF);
+ write(0x0F);
+ write(0);
+
+ write(0);
+ write(0);
+
+ /* Clear the Hash registers for the mac address
+ * pointed by AddressPtr
+ */
+ write(0);
}
- using Nic::Session_component::cap;
-
-
- void phy_write(const uint8_t phyaddr, const uint8_t regnum, const uint16_t data) override
- {
- _phy_setup_op(phyaddr, regnum, data, Phy_maintenance::Operation::WRITE);
- }
-
-
- void phy_read(const uint8_t phyaddr, const uint8_t regnum, uint16_t& data) override
- {
- _phy_setup_op(phyaddr, regnum, 0, Phy_maintenance::Operation::READ);
-
- data = read();
- }
-
-
- void mac_address(const Nic::Mac_address &mac)
+ void write_mac_address(const Nic::Mac_address &mac)
{
const uint32_t* const low_addr_pointer = reinterpret_cast(&mac.addr[0]);
const uint16_t* const high_addr_pointer = reinterpret_cast(&mac.addr[4]);
@@ -697,12 +683,158 @@ namespace Genode
write(*high_addr_pointer);
}
+ void rx_buffer_reset_pkt(Nic::Packet_descriptor pkt)
+ {
+ _rx_buffer.reset_descriptor(pkt);
+ }
+
+ void tx_buffer_submit_acks()
+ {
+ _tx_buffer.submit_acks(_tx_buffer_sink);
+ }
+ };
+
+ class Nic_rx_buffer_source : public Rx_buffer_source
+ {
+ private:
+
+ Nic::Session::Tx::Source &_source;
+
+ public:
+
+ Nic_rx_buffer_source(Nic::Session::Tx::Source &source)
+ :
+ _source { source }
+ { }
+
+
+ /**********************
+ ** Rx_buffer_source **
+ **********************/
+
+ Dataspace_capability dataspace() override
+ {
+ return _source.dataspace();
+ };
+
+ Packet_descriptor alloc_packet(size_t size) override
+ {
+ return _source.alloc_packet(size);
+ }
+ };
+
+ class Nic_tx_buffer_sink : public Tx_buffer_sink
+ {
+ private:
+
+ Nic::Session::Rx::Sink &_sink;
+
+ public:
+
+ Nic_tx_buffer_sink(Nic::Session::Rx::Sink &sink)
+ :
+ _sink { sink }
+ { }
+
+
+ /********************
+ ** Tx_buffer_sink **
+ ********************/
+
+ Dataspace_capability dataspace() override
+ {
+ return _sink.dataspace();
+ };
+
+ void acknowledge_packet(Packet_descriptor packet) override
+ {
+ _sink.acknowledge_packet(packet);
+ }
+
+ bool packet_valid(Packet_descriptor packet) override
+ {
+ return _sink.packet_valid(packet);
+ }
+ };
+
+ /**
+ * Base driver Xilinx EMAC PS module
+ */
+ class Cadence_gem : public Nic::Session_component
+ {
+ private:
+
+ Nic_rx_buffer_source _rx_buffer_source;
+ Nic_tx_buffer_sink _tx_buffer_sink;
+ Cadence_gem_base _cadence_gem;
+ Signal_handler _irq_handler;
+
+ void _handle_acks()
+ {
+ while (_rx.source()->ack_avail()) {
+ _cadence_gem.rx_buffer_reset_pkt(
+ _rx.source()->get_acked_packet());
+ }
+ }
+
+ void _handle_irq()
+ {
+ _cadence_gem.handle_irq(
+ [&] (Nic::Packet_descriptor pkt)
+ {
+ if (_rx.source()->packet_valid(pkt))
+ _rx.source()->submit_packet(pkt);
+ else
+ error(
+ "invalid packet descriptor ", Hex(pkt.offset()),
+ " size ", Hex(pkt.size()));
+ },
+ [&] ()
+ {
+ _handle_acks();
+ });
+ }
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param base MMIO base address
+ */
+ Cadence_gem(size_t const tx_buf_size,
+ size_t const rx_buf_size,
+ Allocator &rx_block_md_alloc,
+ Env &env,
+ addr_t const base,
+ size_t const size,
+ int const irq)
+ :
+ Session_component(tx_buf_size, rx_buf_size, Genode::UNCACHED,
+ rx_block_md_alloc, env),
+ _rx_buffer_source(*_rx.source()),
+ _tx_buffer_sink(*_tx.sink()),
+ _cadence_gem(env, base, size, irq, _tx_buffer_sink, _rx_buffer_source),
+ _irq_handler(env.ep(), *this, &Cadence_gem::_handle_irq)
+ {
+ _cadence_gem.deinit();
+ _cadence_gem.init(_irq_handler);
+ }
+
+ ~Cadence_gem()
+ {
+ // TODO disable tx and rx and clean up irq registration
+ _cadence_gem.deinit();
+ }
+
+ using Nic::Session_component::cap;
+
bool _send()
{
/* first, see whether we can acknowledge any
* previously sent packet */
- _tx_buffer.submit_acks(*_tx.sink());
+ _cadence_gem.tx_buffer_submit_acks();
if (!_tx.sink()->ready_to_ack())
return false;
@@ -710,15 +842,14 @@ namespace Genode
if (!_tx.sink()->packet_avail())
return false;
- Genode::Packet_descriptor packet = _tx.sink()->get_packet();
+ Packet_descriptor packet = _tx.sink()->get_packet();
if (!packet.size()) {
Genode::warning("Invalid tx packet");
return true;
}
try {
- _tx_buffer.add_to_queue(packet);
- write(Control::start_tx());
+ _cadence_gem.transmit_packet(packet);
} catch (Tx_buffer_descriptor::Package_send_timeout) {
Genode::warning("Package Tx timeout");
return false;
@@ -734,14 +865,7 @@ namespace Genode
virtual Nic::Mac_address mac_address() override
{
- Nic::Mac_address mac;
- uint32_t* const low_addr_pointer = reinterpret_cast(&mac.addr[0]);
- uint16_t* const high_addr_pointer = reinterpret_cast(&mac.addr[4]);
-
- *low_addr_pointer = read();
- *high_addr_pointer = read