*/
/*
- * Copyright (c) 2007, 2008, 2010, ETH Zurich.
+ * Copyright (c) 2007, 2008, 2010, 2012, ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
+ * Attn: Systems Group.
*/
+#include <stdbool.h>
+
#include <barrelfish/barrelfish.h>
-#include <barrelfish/waitset.h>
#include <barrelfish/terminal.h>
-#include <barrelfish/nameservice_client.h>
-#include <if/serial_defs.h>
-#include <if/keyboard_defs.h>
-#include <string.h>
-
-#define BUFSIZE 256
-#define MAX_INPUT_HANDLERS 10
-
-struct input_handler {
- terminal_input_handler f;
- void *user_data;
-};
+#include <term/client/client_blocking.h>
struct terminal_state {
- bool want_stdin, serial_bound, kbd_bound;
- struct thread_mutex mutex;
- struct serial_binding *serial;
- struct keyboard_binding *kbd;
- struct waitset waitset;
-
- /* input */
- char stdin_buf[BUFSIZE];
- unsigned int produced;
- unsigned int consumed;
-};
-
-// this table maps keyboard scan codes to input characters
-// XXX: this is completely bogus. we need termcap etc.
-static char scancode_map[] = {
- [0x1E] = 'a',
- [0x30] = 'b',
- [0x2E] = 'c',
- [0x20] = 'd',
- [0x12] = 'e',
- [0x21] = 'f',
- [0x22] = 'g',
- [0x23] = 'h',
- [0x17] = 'i',
- [0x24] = 'j',
- [0x25] = 'k',
- [0x26] = 'l',
- [0x32] = 'm',
- [0x31] = 'n',
- [0x18] = 'o',
- [0x19] = 'p',
- [0x10] = 'q',
- [0x13] = 'r',
- [0x1F] = 's',
- [0x14] = 't',
- [0x16] = 'u',
- [0x2F] = 'v',
- [0x11] = 'w',
- [0x2D] = 'x',
- [0x15] = 'y',
- [0x2C] = 'z',
- [0x02] = '1',
- [0x03] = '2',
- [0x04] = '3',
- [0x05] = '4',
- [0x06] = '5',
- [0x07] = '6',
- [0x08] = '7',
- [0x09] = '8',
- [0x0a] = '9',
- [0x0b] = '0',
- [0x0c] = '_', // XXX: really -, but this is more useful :)
- [0x0d] = '=',
- [0x1a] = '[',
- [0x1b] = ']',
- [0x2b] = '\\',
- [0x27] = ';',
- [0x28] = '\'',
- [0x29] = '`',
- [0x33] = ',',
- [0x34] = '.',
- [0x35] = '/',
-
- // control characters
- [0x0e] = 0x08, // back-space / delete?
- [0x1c] = '\r', // enter
- [0x01] = 0x1b, // escape
- [0x39] = ' ', // space
- [0x0f] = '\t', // tab1
-};
-
-static struct input_handler input_handlers[MAX_INPUT_HANDLERS];
-
-static void handle_stdin(struct serial_binding *b, char *data, size_t len)
-{
- terminal_input(data, len);
- free(data);
-}
-
-static struct serial_rx_vtbl serial_rx_vtbl = {
- .input = handle_stdin,
-};
-
-static void handle_key_event(struct keyboard_binding *b, uint8_t scancode,
- bool extended)
-{
- char sc = scancode < sizeof(scancode_map) ? scancode_map[scancode] : 0;
- if (sc) {
- terminal_input(&sc, 1);
- }
-}
-
-static struct keyboard_rx_vtbl keyboard_rx_vtbl = {
- .key_event = handle_key_event,
+ /**
+ * Is domain part of a session or a daemon?
+ */
+ bool session_domain;
+
+ /**
+ * Terminal device used from stdin, stdout and stderr.
+ */
+ struct term_client client;
};
-/// Filter output for serial driver
-// just converts \n to \r\n for the moment
-static void filter_output(char *outbuf, size_t outbuflen,
- const char *in, size_t inlen)
-{
- while (inlen--) {
- char c = *in++;
- if (c == '\n') {
- assert(outbuflen--);
- *outbuf++ = '\r';
- }
- assert(outbuflen--);
- *outbuf++ = c;
- }
-}
-
-/// Returns length of buffer required by filter_output()
-static size_t filter_output_len(const char *in, size_t inlen)
-{
- size_t outlen = 0;
- while (inlen--) {
- outlen++;
- if (*in++ == '\n') {
- outlen++;
- }
- }
- return outlen;
-}
-
-static void serial_write(struct terminal_state *st,
- const char *data, size_t inlen)
-{
- assert(st != NULL);
- assert(st->serial != NULL);
- errval_t err;
-
- size_t outlen = filter_output_len(data, inlen);
- // check sane size for buffer on stack; after all, this is the terminal!
- assert(outlen <= 4096);
- char outbuf[outlen];
- filter_output(outbuf, outlen, data, inlen);
-
- // try to send
- assert(st->serial->can_send(st->serial));
- err = st->serial->tx_vtbl.output(st->serial, NOP_CONT, outbuf, outlen);
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "error sending terminal output to serial driver");
- }
-
- // block on output completion.
- // this is necessary to maintain libc buffering semantics, and prevent
- // output being lost if a dispatcher exit()s after a printf
- while (!st->serial->can_send(st->serial)) {
- err = event_dispatch(&st->waitset);
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "error in event_dispatch on terminal waitset");
- }
- }
-}
-
size_t terminal_write(const char *data, size_t length)
{
+ errval_t err;
+ size_t written = 0;
struct terminal_state *state = get_terminal_state();
- if (length > 0) {
- assert(data != NULL);
-
- if (state != NULL && state->serial != NULL) {
- thread_mutex_lock(&state->mutex);
- serial_write(state, data, length);
- thread_mutex_unlock(&state->mutex);
- } else {
- sys_print(data, length);
- }
+ if (state != NULL && state->session_domain) {
+ err = term_client_blocking_write(&state->client, data, length,
+ &written);
+ assert(err_is_ok(err));
+ return written;
+ } else {
+ sys_print(data, length);
+ return length;
}
-
- return length;
}
size_t terminal_read(char *data, size_t count)
{
- struct terminal_state *state = get_terminal_state();
errval_t err;
- size_t i;
-
- thread_mutex_lock(&state->mutex);
-
- for(i = 0; i < count; i++) {
- while(state->consumed == state->produced) {
- err = event_dispatch(&state->waitset);
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "error in event_dispatch on terminal waitset");
- }
- }
-
- data[i] = state->stdin_buf[(state->consumed++) % BUFSIZE];
- }
-
- thread_mutex_unlock(&state->mutex);
-
- return count;
-}
-
-void terminal_input(char *data, size_t length)
-{
+ size_t read = 0;
struct terminal_state *state = get_terminal_state();
- for (size_t i = 0; i < length; i++) {
- /* XXX: translate \r to \n in place */
- if (data[i] == '\r') {
- data[i] = '\n';
- }
-
- state->stdin_buf[(state->produced++) % BUFSIZE] = data[i];
- assert(state->produced != state->consumed); // FIXME!
- }
-
- for (int i = 0; i < MAX_INPUT_HANDLERS; i++) {
- if (input_handlers[i].f != NULL) {
- input_handlers[i].f(input_handlers[i].user_data, data, length);
- }
- }
-}
-
-static void serial_bind_cb(void *st, errval_t err, struct serial_binding *b)
-{
- struct terminal_state *state = st;
-
- if (err_is_ok(err)) {
- b->rx_vtbl = serial_rx_vtbl;
- b->st = state;
- state->serial = b;
- if (state->want_stdin) {
- err = b->tx_vtbl.associate_stdin(b, NOP_CONT);
- assert(err_is_ok(err)); // XXX: can fail!!
- }
- } else {
- state->serial_bound = false;
- USER_PANIC_ERR(err, "error binding to serial driver");
- }
-}
-
-static void keyboard_bind_cb(void *st, errval_t err, struct keyboard_binding *b)
-{
- struct terminal_state *state = st;
-
- if (err_is_ok(err)) {
- b->rx_vtbl = keyboard_rx_vtbl;
- b->st = state;
- state->kbd = b;
+ if (state->session_domain) {
+ err = term_client_blocking_read(&state->client, data, count, &read);
+ assert(err_is_ok(err));
+ return read;
} else {
- state->kbd_bound = false;
- USER_PANIC_ERR(err, "error binding to keyboard driver");
+ /**
+ * Only domains that are part of a session can read from a terminal
+ * device.
+ */
+ assert(!"Daemons can't read from a terminal.");
}
}
errval_t terminal_init(void)
{
- memset(input_handlers, 0, sizeof(input_handlers));
+ errval_t err;
+ struct capability cap;
- /* Allocate and initialize dispatcher-specific state */
+ /* Allocate and initialize dispatcher-specific state. */
struct terminal_state *state = malloc(sizeof(struct terminal_state));
if (!state) {
return LIB_ERR_MALLOC_FAIL;
}
set_terminal_state(state);
- thread_mutex_init(&state->mutex);
- state->want_stdin = false;
- state->serial_bound = false;
- state->kbd_bound = false;
- state->produced = state->consumed = 0;
- state->serial = NULL;
- waitset_init(&state->waitset);
-
- iref_t iref;
- errval_t err;
- /* Connect to serial driver if possible */
- err = nameservice_lookup("serial", &iref);
- if (err_is_fail(err)) {
- if (err_no(err) == LIB_ERR_NAMESERVICE_UNKNOWN_NAME) {
- // serial not present, ignore it and continue
- return SYS_ERR_OK;
- } else {
+ /* Check if domain is part of a session. */
+ err = debug_cap_identify(cap_sessionid, &cap);
+ if (err_is_ok(err)) {
+ /* Initialize libterm_client. */
+ err = term_client_blocking_init(&state->client, cap_sessionid);
+ if (err_is_fail(err)) {
return err;
}
- }
- err = serial_bind(iref, serial_bind_cb, state, &state->waitset,
- IDC_BIND_FLAGS_DEFAULT);
- if (err_is_fail(err)) {
- return err_push(err, LIB_ERR_SERIAL_BIND);
+ state->session_domain = true;
+ return SYS_ERR_OK;
+ } else {
+ state->session_domain = false;
+ return SYS_ERR_OK;
}
-
- state->serial_bound = true;
-
- return SYS_ERR_OK;
}
-errval_t terminal_want_stdin(unsigned sources)
+void terminal_exit(void)
{
struct terminal_state *state = get_terminal_state();
- assert(state != NULL);
-
- iref_t iref;
- errval_t err;
-
- thread_mutex_lock(&state->mutex);
-
- state->want_stdin = true;
-
- if ((sources & TERMINAL_SOURCE_SERIAL) && !state->serial_bound) {
- // didn't connect at init time, try again, blocking on the lookup
- err = nameservice_blocking_lookup("serial", &iref);
- if (err_is_fail(err)) {
- goto out;
- }
-
- err = serial_bind(iref, serial_bind_cb, state, &state->waitset,
- IDC_BIND_FLAGS_DEFAULT);
- if (err_is_fail(err)) {
- err = err_push(err, LIB_ERR_SERIAL_BIND);
- goto out;
- }
- state->serial_bound = true;
- }
- // connect to keyboard driver if desired
- if ((sources & TERMINAL_SOURCE_KEYBOARD) && !state->kbd_bound) {
- err = nameservice_blocking_lookup("keyboard", &iref);
- if (err_is_fail(err)) {
- goto out;
- }
-
- err = keyboard_bind(iref, keyboard_bind_cb, state,
- &state->waitset, IDC_BIND_FLAGS_DEFAULT);
- if (err_is_fail(err)) {
- err = err_push(err, LIB_ERR_KBD_BIND);
- goto out;
- }
- state->kbd_bound = true;
- }
-
- // XXX: I don't believe this waiting is correct. It changes this from a
- // non-blocking to a blocking API call. The caller should dispatch
- // the default waitset, and the bind will eventually complete. -AB
- while ((sources & TERMINAL_SOURCE_SERIAL) && state->serial == NULL) {
- err = event_dispatch(get_default_waitset());
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "event_dispatch on default waitset failed.");
- }
- }
-
- if (sources & TERMINAL_SOURCE_SERIAL && state->serial != NULL) {
- err = state->serial->tx_vtbl.associate_stdin(state->serial, NOP_CONT);
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "sending associate_stdin failed");
- }
- }
-
- err = SYS_ERR_OK;
-
- out:
- thread_mutex_unlock(&state->mutex);
-
- return err;
-}
-
-/**
- * \brief Register a handler to be called when input arrives at the terminal.
- */
-errval_t terminal_register_input_handler (terminal_input_handler handler,
- void * user_data)
-{
- for (int i = 0; i < MAX_INPUT_HANDLERS; i++) {
- if (input_handlers[i].f == NULL) {
- input_handlers[i].f = handler;
- input_handlers[i].user_data = user_data;
- return SYS_ERR_OK;
- }
- }
-
- return TERM_ERR_REGISTER_HANDLER;
-}
-
-/**
- * \brief Unregister a previously registered input handler.
- */
-void terminal_unregister_input_handler (terminal_input_handler handler)
-{
- for (int i = 0; i < MAX_INPUT_HANDLERS; i++) {
- if (input_handlers[i].f == handler) {
- input_handlers[i].f = NULL;
- input_handlers[i].user_data = NULL;
- }
+ if (state != NULL && state->session_domain) {
+ term_client_blocking_exit(&state->client);
}
}