diff --git a/repos/os/recipes/pkg/black_hole/runtime b/repos/os/recipes/pkg/black_hole/runtime
index f9ea659189..5b2ab031bb 100644
--- a/repos/os/recipes/pkg/black_hole/runtime
+++ b/repos/os/recipes/pkg/black_hole/runtime
@@ -5,6 +5,7 @@
+
@@ -12,6 +13,7 @@
+
diff --git a/repos/os/recipes/pkg/test-black_hole/runtime b/repos/os/recipes/pkg/test-black_hole/runtime
index 32a896e218..597e59ac8e 100644
--- a/repos/os/recipes/pkg/test-black_hole/runtime
+++ b/repos/os/recipes/pkg/test-black_hole/runtime
@@ -30,12 +30,14 @@
+
+
@@ -53,6 +55,7 @@
+
diff --git a/repos/os/recipes/src/black_hole/used_apis b/repos/os/recipes/src/black_hole/used_apis
index 74942fb292..a40b53f092 100644
--- a/repos/os/recipes/src/black_hole/used_apis
+++ b/repos/os/recipes/src/black_hole/used_apis
@@ -3,4 +3,5 @@ audio_in_session
audio_out_session
capture_session
event_session
+nic_session
os
diff --git a/repos/os/recipes/src/test-black_hole/used_apis b/repos/os/recipes/src/test-black_hole/used_apis
index 6c271c2f7a..1474ce00c2 100644
--- a/repos/os/recipes/src/test-black_hole/used_apis
+++ b/repos/os/recipes/src/test-black_hole/used_apis
@@ -5,3 +5,4 @@ audio_in_session
audio_out_session
event_session
capture_session
+nic_session
diff --git a/repos/os/src/server/black_hole/README b/repos/os/src/server/black_hole/README
index fe17834729..66f07dd720 100644
--- a/repos/os/src/server/black_hole/README
+++ b/repos/os/src/server/black_hole/README
@@ -8,10 +8,12 @@ in the configuration of the component:
* Audio_out
* Capture
* Event
+* Nic
+
diff --git a/repos/os/src/server/black_hole/config.xsd b/repos/os/src/server/black_hole/config.xsd
index ef050a6dfa..3919c4653e 100644
--- a/repos/os/src/server/black_hole/config.xsd
+++ b/repos/os/src/server/black_hole/config.xsd
@@ -4,6 +4,7 @@
+
diff --git a/repos/os/src/server/black_hole/main.cc b/repos/os/src/server/black_hole/main.cc
index 56991d459c..c1f78bbb05 100644
--- a/repos/os/src/server/black_hole/main.cc
+++ b/repos/os/src/server/black_hole/main.cc
@@ -25,6 +25,7 @@
#include "audio_out.h"
#include "capture.h"
#include "event.h"
+#include "nic.h"
/***************
@@ -43,6 +44,7 @@ struct Black_hole::Main
Genode::Constructible audio_out_root { };
Genode::Constructible capture_root { };
Genode::Constructible event_root { };
+ Genode::Constructible nic_root { };
Main(Genode::Env &env) : env(env)
{
@@ -66,6 +68,11 @@ struct Black_hole::Main
event_root.construct(env, heap);
env.parent().announce(env.ep().manage(*event_root));
}
+
+ if (_config_rom.xml().has_sub_node("nic")) {
+ nic_root.construct(env, heap);
+ env.parent().announce(env.ep().manage(*nic_root));
+ }
}
};
diff --git a/repos/os/src/server/black_hole/nic.h b/repos/os/src/server/black_hole/nic.h
new file mode 100644
index 0000000000..4b5e5b104b
--- /dev/null
+++ b/repos/os/src/server/black_hole/nic.h
@@ -0,0 +1,116 @@
+/*
+ * \brief Nic session component and root
+ * \author Martin Stein
+ * \date 2022-02-12
+ */
+
+/*
+ * Copyright (C) 2022 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _NIC_H_
+#define _NIC_H_
+
+/* base includes */
+#include
+#include
+
+/* os includes */
+#include
+
+namespace Black_hole {
+
+ using namespace Genode;
+
+ class Nic_session;
+ class Nic_root;
+}
+
+
+class Black_hole::Nic_session : public Nic::Session_component
+{
+ public:
+
+ Nic_session(size_t const tx_buf_size,
+ size_t const rx_buf_size,
+ Allocator &rx_block_md_alloc,
+ Env &env)
+ :
+ Nic::Session_component(tx_buf_size, rx_buf_size, CACHED,
+ rx_block_md_alloc, env)
+ { }
+
+ Nic::Mac_address mac_address() override
+ {
+ char buf[] = { 2, 3, 4, 5, 6, 7 };
+ Nic::Mac_address result((void*)buf);
+ return result;
+ }
+
+ bool link_state() override { return true; }
+
+ void _handle_packet_stream() override
+ {
+ while (_tx.sink()->packet_avail()) {
+
+ if (!_tx.sink()->ready_to_ack()) {
+ return;
+ }
+ Packet_descriptor const pkt { _tx.sink()->get_packet() };
+ if (!pkt.size() || !_tx.sink()->packet_valid(pkt)) {
+ continue;
+ }
+ _tx.sink()->acknowledge_packet(pkt);
+ }
+ }
+};
+
+
+class Black_hole::Nic_root : public Root_component
+{
+ private:
+
+ Env &_env;
+
+ protected:
+
+ Nic_session *_create_session(char const *args) override
+ {
+ size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
+ size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
+ size_t rx_buf_size = Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
+
+ /* deplete ram quota by the memory needed for the session structure */
+ size_t session_size = max(4096UL, (size_t)sizeof(Nic_session));
+ if (ram_quota < session_size)
+ throw 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) {
+ error("insufficient 'ram_quota', got ", ram_quota, ", "
+ "need ", tx_buf_size + rx_buf_size + session_size);
+ throw Insufficient_ram_quota();
+ }
+
+ return new (md_alloc()) Nic_session(tx_buf_size, rx_buf_size,
+ *md_alloc(), _env);
+ }
+
+ public:
+
+ Nic_root(Env &env,
+ Allocator &md_alloc)
+ :
+ Root_component(&env.ep().rpc_ep(), &md_alloc),
+ _env(env)
+ { }
+};
+
+#endif /* _NIC_H_ */
diff --git a/repos/os/src/test/black_hole/main.cc b/repos/os/src/test/black_hole/main.cc
index c78815df8e..0e73b4c0a9 100644
--- a/repos/os/src/test/black_hole/main.cc
+++ b/repos/os/src/test/black_hole/main.cc
@@ -13,13 +13,16 @@
/* base includes */
#include
+#include
/* os includes */
#include
#include
#include
#include
-
+#include
+#include
+#include
#include
using namespace Genode;
@@ -34,7 +37,13 @@ class Test::Main
{
private:
+ enum {
+ NIC_BUF_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE * 128,
+ NIC_PKT_SIZE = 100,
+ };
+
Env &_env;
+ Heap _heap { _env.ram(), _env.rm() };
Event::Connection _event { _env };
Capture::Connection _capture { _env };
Capture::Area _capture_screen_size { 1, 1 };
@@ -43,20 +52,93 @@ class Test::Main
Capture::Connection::Screen _capture_screen { _capture, _env.rm(), _capture_screen_size };
Audio_in::Connection _audio_in { _env, "left" };
Audio_out::Connection _audio_out { _env, "left" };
+ Allocator_avl _nic_tx_blk_alloc { &_heap };
+ Nic::Connection _nic { _env, &_nic_tx_blk_alloc, NIC_BUF_SIZE, NIC_BUF_SIZE };
+ Signal_handler _nic_handler { _env.ep(), *this, &Main::_handle_nic };
+ unsigned long _nic_pkt_count { 0 };
+
+ void _check_if_test_has_finished()
+ {
+ if (_nic_pkt_count > 100) {
+ log("Finished");
+ }
+ }
+
+ void _handle_nic()
+ {
+ if (_nic.rx()->packet_avail()) {
+ class Nic_rx_packet_avail { };
+ throw Nic_rx_packet_avail { };
+ }
+ if (!_nic.rx()->ack_slots_free()) {
+ class Nic_no_rx_ack_slots_free { };
+ throw Nic_no_rx_ack_slots_free { };
+ }
+ while (_nic.tx()->ack_avail()) {
+
+ Packet_descriptor const pkt { _nic.tx()->get_acked_packet() };
+ if (pkt.size() != NIC_PKT_SIZE) {
+ class Nic_packet_size_unexpected { };
+ throw Nic_packet_size_unexpected { };
+ }
+ _nic.tx()->release_packet(pkt);
+ _nic_pkt_count++;
+ }
+ _submit_nic_pkts();
+ _check_if_test_has_finished();
+ }
+
+ void _submit_nic_pkts()
+ {
+ for (unsigned idx { 0 }; idx < 40; idx++) {
+
+ if (!_nic.tx()->ready_to_submit()) {
+ class Nic_submit_queue_full { };
+ throw Nic_submit_queue_full { };
+ }
+ Packet_descriptor pkt;
+ try { pkt = _nic.tx()->alloc_packet(NIC_PKT_SIZE); }
+ catch (...) {
+ class Nic_packet_alloc_failed { };
+ throw Nic_packet_alloc_failed { };
+ }
+ _nic.tx()->submit_packet(pkt);
+ }
+ }
public:
Main(Env &env) : _env { env }
{
+ /* test-drive nic connection */
+ _nic.tx_channel()->sigh_ready_to_submit(_nic_handler);
+ _nic.tx_channel()->sigh_ack_avail(_nic_handler);
+ _nic.rx_channel()->sigh_ready_to_ack(_nic_handler);
+ _nic.rx_channel()->sigh_packet_avail(_nic_handler);
+ if (!_nic.link_state()) {
+ class Nic_link_down { };
+ throw Nic_link_down { };
+ }
+ if (_nic.mac_address() == Net::Mac_address { }) {
+ class Nic_mac_invalid { };
+ throw Nic_mac_invalid { };
+ }
+ _submit_nic_pkts();
+
+ /* test-drive event connection */
_event.with_batch([&] (Event::Session_client::Batch &batch) {
batch.submit(Input::Press {Input::KEY_1 });
batch.submit(Input::Release {Input::KEY_2 });
batch.submit(Input::Relative_motion { 3, 4 });
});
-
+ /* test-drive capture connection */
_capture_screen.apply_to_surface(_capture_surface);
- log("Finished");
+ /*
+ * FIXME
+ *
+ * Test-driving audio_in and audio_out connection is yet missing.
+ */
}
};