Cross-link terminal

This patch implements a service which allows two clients to communicate
with each other using the 'Terminal' interface.

Fixes #242.
This commit is contained in:
Christian Prochaska 2012-06-11 17:01:51 +02:00 committed by Norman Feske
parent 8a7fbe3973
commit ba0b20054e
8 changed files with 644 additions and 0 deletions

View File

@ -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 {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>}
append_if [have_spec sdl] config {
<start name="fb_sdl">
<resource name="RAM" quantum="4M"/>
<provides>
<service name="Input"/>
<service name="Framebuffer"/>
</provides>
</start>}
append_if [have_spec pci] config {
<start name="pci_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="PCI"/></provides>
</start>}
append_if [have_spec vesa] config {
<start name="vesa_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="Framebuffer"/></provides>
</start>}
append_if [have_spec pl11x] config {
<start name="pl11x_drv">
<resource name="RAM" quantum="2M"/>
<provides><service name="Framebuffer"/></provides>
</start>}
append_if [have_spec ps2] config {
<start name="ps2_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="Input"/></provides>
</start> }
append config {
<start name="timer">
<resource name="RAM" quantum="512K"/>
<provides> <service name="Timer"/> </provides>
</start>
<start name="terminal_crosslink">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Terminal"/> </provides>
</start>
<start name="test-terminal_crosslink">
<resource name="RAM" quantum="1M"/>
</start>
</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 :

View File

@ -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 <cap_session/connection.h>
#include <base/printf.h>
#include <base/rpc_server.h>
#include <base/sleep.h>
/* 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;
}

View File

@ -0,0 +1,4 @@
TARGET = terminal_crosslink
SRC_CC = main.cc \
terminal_session_component.cc
LIBS = cxx env server signal

View File

@ -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 <cap_session/cap_session.h>
#include <root/component.h>
/* local includes */
#include "terminal_session_component.h"
namespace Terminal {
using namespace Genode;
class Root : public Rpc_object<Typed_root<Session> >
{
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_ */

View File

@ -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 <base/env.h>
#include <base/printf.h>
#include <base/rpc_server.h>
#include <base/signal.h>
#include <util/misc_math.h>
/* 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<unsigned char>(), dst_len);
}
void Session_component::_write(Genode::size_t num_bytes)
{
unsigned char *src = _io_buffer.local_addr<unsigned char>();
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; }
}

View File

@ -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 <base/rpc_server.h>
#include <os/attached_ram_dataspace.h>
#include <os/ring_buffer.h>
#include <terminal_session/terminal_session.h>
namespace Terminal {
using namespace Genode;
enum { STACK_SIZE = sizeof(addr_t)*1024 };
enum { BUFFER_SIZE = 4096 };
class Session_component : public Rpc_object<Terminal::Session,
Session_component>
{
private:
Session_component &_partner;
Rpc_entrypoint _ep;
Genode::Session_capability _session_cap;
Attached_ram_dataspace _io_buffer;
typedef Ring_buffer<unsigned char, BUFFER_SIZE+1> 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_ */

View File

@ -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 <base/printf.h>
#include <base/signal.h>
#include <base/sleep.h>
#include <base/thread.h>
#include <terminal_session/connection.h>
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<STACK_SIZE>
{
protected:
Terminal::Connection _terminal;
char _read_buffer[READ_BUFFER_SIZE];
Signal_receiver _sig_rec;
Signal_context _sig_ctx;
public:
Partner(const char *name) : Thread<STACK_SIZE>(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;
}

View File

@ -0,0 +1,3 @@
TARGET = test-terminal_crosslink
LIBS = cxx env signal
SRC_CC = main.cc