From ba0b20054e142f89128af5d52d5f66b529f5d493 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Mon, 11 Jun 2012 17:01:51 +0200 Subject: [PATCH] Cross-link terminal This patch implements a service which allows two clients to communicate with each other using the 'Terminal' interface. Fixes #242. --- os/run/terminal_crosslink.run | 119 +++++++++++++ os/src/server/terminal_crosslink/main.cc | 35 ++++ os/src/server/terminal_crosslink/target.mk | 4 + .../server/terminal_crosslink/terminal_root.h | 79 +++++++++ .../terminal_session_component.cc | 159 ++++++++++++++++++ .../terminal_session_component.h | 91 ++++++++++ os/src/test/terminal_crosslink/main.cc | 154 +++++++++++++++++ os/src/test/terminal_crosslink/target.mk | 3 + 8 files changed, 644 insertions(+) create mode 100644 os/run/terminal_crosslink.run create mode 100644 os/src/server/terminal_crosslink/main.cc create mode 100644 os/src/server/terminal_crosslink/target.mk create mode 100644 os/src/server/terminal_crosslink/terminal_root.h create mode 100644 os/src/server/terminal_crosslink/terminal_session_component.cc create mode 100644 os/src/server/terminal_crosslink/terminal_session_component.h create mode 100644 os/src/test/terminal_crosslink/main.cc create mode 100644 os/src/test/terminal_crosslink/target.mk diff --git a/os/run/terminal_crosslink.run b/os/run/terminal_crosslink.run new file mode 100644 index 0000000000..de3bf11a5e --- /dev/null +++ b/os/run/terminal_crosslink.run @@ -0,0 +1,119 @@ +# +# \brief Cross-link terminal test +# \author Christian Prochaska +# \date 2012-05-16 +# + +# +# Build +# + +build { + core init + drivers/timer drivers/pci drivers/framebuffer drivers/input server/terminal_crosslink + test/terminal_crosslink +} + +create_boot_directory + +# +# Generate config +# + +append config { + + + + + + + + + + + + + + + + + } + +append_if [have_spec sdl] config { + + + + + + + } + +append_if [have_spec pci] config { + + + + } + +append_if [have_spec vesa] config { + + + + } + +append_if [have_spec pl11x] config { + + + + } + +append_if [have_spec ps2] config { + + + + } + +append config { + + + + + + + + + + + + +} + +install_config $config + +# +# Boot modules +# + +# generic modules +set boot_modules { + core init timer terminal_crosslink + test-terminal_crosslink +} + +# platform-specific modules +lappend_if [have_spec linux] boot_modules fb_sdl +lappend_if [have_spec pci] boot_modules pci_drv +lappend_if [have_spec vesa] boot_modules vesa_drv +lappend_if [have_spec ps2] boot_modules ps2_drv +lappend_if [have_spec pl11x] boot_modules pl11x_drv + +build_boot_image $boot_modules + +append qemu_args "-nographic -m 64" + +# +# Execute test case +# + +run_genode_until "Test succeeded.*" 5 + +# vi: set ft=tcl : diff --git a/os/src/server/terminal_crosslink/main.cc b/os/src/server/terminal_crosslink/main.cc new file mode 100644 index 0000000000..5bb0fdeb73 --- /dev/null +++ b/os/src/server/terminal_crosslink/main.cc @@ -0,0 +1,35 @@ +/* + * \brief A server for connecting two 'Terminal' sessions + * \author Christian Prochaska + * \date 2012-05-16 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* local includes */ +#include "terminal_root.h" + +int main(int argc, char **argv) +{ + using namespace Genode; + + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, Terminal::STACK_SIZE, "terminal_ep"); + + static Terminal::Root terminal_root(&ep, env()->heap(), cap); + env()->parent()->announce(ep.manage(&terminal_root)); + + sleep_forever(); + return 0; +} diff --git a/os/src/server/terminal_crosslink/target.mk b/os/src/server/terminal_crosslink/target.mk new file mode 100644 index 0000000000..38a810cc50 --- /dev/null +++ b/os/src/server/terminal_crosslink/target.mk @@ -0,0 +1,4 @@ +TARGET = terminal_crosslink +SRC_CC = main.cc \ + terminal_session_component.cc +LIBS = cxx env server signal diff --git a/os/src/server/terminal_crosslink/terminal_root.h b/os/src/server/terminal_crosslink/terminal_root.h new file mode 100644 index 0000000000..57f34d3127 --- /dev/null +++ b/os/src/server/terminal_crosslink/terminal_root.h @@ -0,0 +1,79 @@ +/* + * \brief Terminal root + * \author Christian Prochaska + * \date 2012-05-16 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _TERMINAL_ROOT_H_ +#define _TERMINAL_ROOT_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include "terminal_session_component.h" + + +namespace Terminal { + + using namespace Genode; + + class Root : public Rpc_object > + { + private: + + Session_component _session_component1, _session_component2; + + enum Session_state { + FIRST_SESSION_OPEN = 1 << 0, + SECOND_SESSION_OPEN = 1 << 1 + }; + + int _session_state; + + public: + + Session_capability session(Root::Session_args const &args) + { + if (!(_session_state & FIRST_SESSION_OPEN)) { + _session_state |= FIRST_SESSION_OPEN; + return _session_component1.cap(); + } else if (!(_session_state & SECOND_SESSION_OPEN)) { + _session_state |= SECOND_SESSION_OPEN; + return _session_component2.cap(); + } + + return Session_capability(); + } + + void upgrade(Genode::Session_capability, Root::Upgrade_args const &) { } + + void close(Genode::Session_capability session) + { + if (_session_component1.belongs_to(session)) + _session_state &= ~FIRST_SESSION_OPEN; + else + _session_state &= ~SECOND_SESSION_OPEN; + } + + /** + * Constructor + */ + Root(Rpc_entrypoint *ep, Allocator *md_alloc, + Cap_session &cap_session) + : _session_component1(_session_component2, cap_session, "terminal_ep1"), + _session_component2(_session_component1, cap_session, "terminal_ep2"), + _session_state(0) + { } + }; +} + +#endif /* _TERMINAL_ROOT_H_ */ diff --git a/os/src/server/terminal_crosslink/terminal_session_component.cc b/os/src/server/terminal_crosslink/terminal_session_component.cc new file mode 100644 index 0000000000..f730fd3fd3 --- /dev/null +++ b/os/src/server/terminal_crosslink/terminal_session_component.cc @@ -0,0 +1,159 @@ +/* + * \brief Terminal session component + * \author Christian Prochaska + * \date 2012-05-16 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* local includes */ +#include "terminal_session_component.h" + +using namespace Genode; + +namespace Terminal { + +Session_component::Session_component(Session_component &partner, Cap_session &cap_session, const char *ep_name) +: _partner(partner), + _ep(&cap_session, STACK_SIZE, ep_name), + _session_cap(_ep.manage(this)), + _io_buffer(Genode::env()->ram_session(), BUFFER_SIZE), + _cross_num_bytes_avail(0) +{ +} + + +Genode::Session_capability Session_component::cap() +{ + return _session_cap; +} + + +bool Session_component::belongs_to(Genode::Session_capability cap) +{ + return _ep.obj_by_cap(cap) == this; +} + + +bool Session_component::cross_avail() +{ + return (_cross_num_bytes_avail > 0); +} + + +size_t Session_component::cross_read(unsigned char *buf, size_t dst_len) +{ + size_t num_bytes_read; + + for (num_bytes_read = 0; + (num_bytes_read < dst_len) && !_buffer.empty(); + num_bytes_read++) + buf[num_bytes_read] = _buffer.get(); + + _cross_num_bytes_avail -= num_bytes_read; + + _write_avail_lock.unlock(); + + return num_bytes_read; +} + +void Session_component::cross_write() +{ + Signal_transmitter(_read_avail_sigh).submit(); +} + + +Session::Size Session_component::size() { return Size(0, 0); } + + +bool Session_component::avail() +{ + return _partner.cross_avail(); +} + + +Genode::size_t Session_component::_read(Genode::size_t dst_len) +{ + return _partner.cross_read(_io_buffer.local_addr(), dst_len); +} + + +void Session_component::_write(Genode::size_t num_bytes) +{ + unsigned char *src = _io_buffer.local_addr(); + + size_t num_bytes_written = 0; + size_t src_index = 0; + while (num_bytes_written < num_bytes) + try { + _buffer.add(src[src_index]); + ++src_index; + ++num_bytes_written; + } catch(Local_buffer::Overflow) { + _cross_num_bytes_avail += num_bytes_written; + + /* Lock the lock (always succeeds) */ + _write_avail_lock.lock(); + + _partner.cross_write(); + + /* + * This lock operation blocks or not, depending on whether the + * partner already has called 'cross_read()' in the meantime + */ + _write_avail_lock.lock(); + + /* + * Unlock the lock, so it is unlocked the next time the exception + * triggers + */ + _write_avail_lock.unlock(); + + num_bytes -= num_bytes_written; + num_bytes_written = 0; + } + + _cross_num_bytes_avail += num_bytes_written; + _partner.cross_write(); +} + + +Genode::Dataspace_capability Session_component::_dataspace() { return _io_buffer.cap(); } + + +void Session_component::connected_sigh(Genode::Signal_context_capability sigh) +{ + /* + * Immediately reflect connection-established signal to the + * client because the session is ready to use immediately after + * creation. + */ + Genode::Signal_transmitter(sigh).submit(); +} + + +void Session_component::read_avail_sigh(Genode::Signal_context_capability sigh) +{ + _read_avail_sigh = sigh; +} + + +Genode::size_t Session_component::read(void *, Genode::size_t) { return 0; } + + +Genode::size_t Session_component::write(void const *, Genode::size_t) { return 0; } + +} diff --git a/os/src/server/terminal_crosslink/terminal_session_component.h b/os/src/server/terminal_crosslink/terminal_session_component.h new file mode 100644 index 0000000000..9a8cf0fd11 --- /dev/null +++ b/os/src/server/terminal_crosslink/terminal_session_component.h @@ -0,0 +1,91 @@ +/* + * \brief Terminal session component + * \author Christian Prochaska + * \date 2012-05-16 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _TERMINAL_SESSION_COMPONENT_H_ +#define _TERMINAL_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include + +namespace Terminal { + + using namespace Genode; + + enum { STACK_SIZE = sizeof(addr_t)*1024 }; + enum { BUFFER_SIZE = 4096 }; + + class Session_component : public Rpc_object + { + private: + + Session_component &_partner; + Rpc_entrypoint _ep; + Genode::Session_capability _session_cap; + + Attached_ram_dataspace _io_buffer; + + typedef Ring_buffer Local_buffer; + + Local_buffer _buffer; + size_t _cross_num_bytes_avail; + Lock _write_avail_lock; + Signal_context_capability _read_avail_sigh; + + public: + + /** + * Constructor + */ + Session_component(Session_component &partner, Cap_session &cap_session, const char *ep_name); + + Session_capability cap(); + + /** + * Return true if capability belongs to session object + */ + bool belongs_to(Genode::Session_capability cap); + + /* to be called by the partner component */ + bool cross_avail(); + size_t cross_read(unsigned char *buf, size_t dst_len); + void cross_write(); + + /******************************** + ** Terminal session interface ** + ********************************/ + + Size size(); + + bool avail(); + + Genode::size_t _read(Genode::size_t dst_len); + + void _write(Genode::size_t num_bytes); + + Genode::Dataspace_capability _dataspace(); + + void connected_sigh(Genode::Signal_context_capability sigh); + + void read_avail_sigh(Genode::Signal_context_capability sigh); + + Genode::size_t read(void *, Genode::size_t); + Genode::size_t write(void const *, Genode::size_t); + }; + +} + +#endif /* _TERMINAL_SESSION_COMPONENT_H_ */ diff --git a/os/src/test/terminal_crosslink/main.cc b/os/src/test/terminal_crosslink/main.cc new file mode 100644 index 0000000000..75113e5111 --- /dev/null +++ b/os/src/test/terminal_crosslink/main.cc @@ -0,0 +1,154 @@ +/* + * \brief Crosslink terminal test + * \author Christian Prochaska + * \date 2012-05-21 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +using namespace Genode; + + +enum { + STACK_SIZE = sizeof(addr_t)*1024, + SERVICE_BUFFER_SIZE = 4096, + TEST_DATA_SIZE = 4097, + READ_BUFFER_SIZE = 8192 +}; + +static const char *client_text = "Hello from client."; +static const char *server_text = "Hello from server, too."; + +static char test_data[TEST_DATA_SIZE]; + + +class Partner : public Thread +{ + protected: + + Terminal::Connection _terminal; + + char _read_buffer[READ_BUFFER_SIZE]; + + Signal_receiver _sig_rec; + Signal_context _sig_ctx; + + public: + + Partner(const char *name) : Thread(name) + { + _terminal.read_avail_sigh(_sig_rec.manage(&_sig_ctx)); + } +}; + + +class Client : public Partner +{ + public: + + Client() : Partner("client") { } + + void entry() + { + printf("Short message test\n"); + + /* write client text */ + + _terminal.write(client_text, strlen(client_text) + 1); + + /* read server text */ + + _sig_rec.wait_for_signal(); + _terminal.read(_read_buffer, sizeof(_read_buffer)); + + printf("Client received: %s\n", _read_buffer); + + if (strcmp(_read_buffer, server_text) != 0) { + printf("Error: received data is not as expected\n"); + sleep_forever(); + } + + /* write test data */ + + printf("Long message test\n"); + + memset(test_data, 5, sizeof(test_data)); + _terminal.write(test_data, sizeof(test_data)); + } +}; + + +class Server : public Partner +{ + public: + + Server() : Partner("server") { } + + void entry() + { + /* read client text */ + + _sig_rec.wait_for_signal(); + _terminal.read(_read_buffer, sizeof(_read_buffer)); + + printf("Server received: %s\n", _read_buffer); + + if (strcmp(_read_buffer, client_text) != 0) { + printf("Error: received data is not as expected\n"); + sleep_forever(); + } + + /* write server text */ + + _terminal.write(server_text, strlen(server_text) + 1); + + /* read test data */ + + size_t num_read = 0; + size_t num_read_total = 0; + + do { + _sig_rec.wait_for_signal(); + num_read = _terminal.read(_read_buffer, sizeof(_read_buffer)); + num_read_total += num_read; + + for (size_t i = 0; i < num_read; i++) + if (_read_buffer[i] != 5) { + printf("Error: received data is not as expected\n"); + sleep_forever(); + } + } while(num_read == SERVICE_BUFFER_SIZE); + + if (num_read_total != TEST_DATA_SIZE) { + printf("Error: received an unexpected number of bytes\n"); + sleep_forever(); + } + + printf("Test succeeded\n"); + } +}; + + +int main(int, char **) +{ + static Server server; + static Client client; + + server.start(); + client.start(); + + sleep_forever(); + + return 0; +} diff --git a/os/src/test/terminal_crosslink/target.mk b/os/src/test/terminal_crosslink/target.mk new file mode 100644 index 0000000000..779c023c68 --- /dev/null +++ b/os/src/test/terminal_crosslink/target.mk @@ -0,0 +1,3 @@ +TARGET = test-terminal_crosslink +LIBS = cxx env signal +SRC_CC = main.cc