mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-09 04:15:52 +00:00
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:
parent
8a7fbe3973
commit
ba0b20054e
119
os/run/terminal_crosslink.run
Normal file
119
os/run/terminal_crosslink.run
Normal 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 :
|
35
os/src/server/terminal_crosslink/main.cc
Normal file
35
os/src/server/terminal_crosslink/main.cc
Normal 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;
|
||||
}
|
4
os/src/server/terminal_crosslink/target.mk
Normal file
4
os/src/server/terminal_crosslink/target.mk
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = terminal_crosslink
|
||||
SRC_CC = main.cc \
|
||||
terminal_session_component.cc
|
||||
LIBS = cxx env server signal
|
79
os/src/server/terminal_crosslink/terminal_root.h
Normal file
79
os/src/server/terminal_crosslink/terminal_root.h
Normal 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_ */
|
159
os/src/server/terminal_crosslink/terminal_session_component.cc
Normal file
159
os/src/server/terminal_crosslink/terminal_session_component.cc
Normal 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; }
|
||||
|
||||
}
|
@ -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_ */
|
154
os/src/test/terminal_crosslink/main.cc
Normal file
154
os/src/test/terminal_crosslink/main.cc
Normal 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;
|
||||
}
|
3
os/src/test/terminal_crosslink/target.mk
Normal file
3
os/src/test/terminal_crosslink/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-terminal_crosslink
|
||||
LIBS = cxx env signal
|
||||
SRC_CC = main.cc
|
Loading…
x
Reference in New Issue
Block a user