3 * \brief Terminal emulator.
7 * Copyright (c) 2007, 2008, 2010, ETH Zurich.
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
15 #include <barrelfish/barrelfish.h>
16 #include <barrelfish/waitset.h>
17 #include <barrelfish/terminal.h>
18 #include <barrelfish/nameservice_client.h>
19 #include <if/serial_defs.h>
20 #include <if/keyboard_defs.h>
24 #define MAX_INPUT_HANDLERS 10
26 struct input_handler {
27 terminal_input_handler f;
31 struct terminal_state {
32 bool want_stdin, serial_bound, kbd_bound;
33 struct thread_mutex mutex;
34 struct serial_binding *serial;
35 struct keyboard_binding *kbd;
36 struct waitset waitset;
39 char stdin_buf[BUFSIZE];
40 unsigned int produced;
41 unsigned int consumed;
44 // this table maps keyboard scan codes to input characters
45 // XXX: this is completely bogus. we need termcap etc.
46 static char scancode_map[] = {
83 [0x0c] = '_', // XXX: really -, but this is more useful :)
96 [0x0e] = 0x08, // back-space / delete?
97 [0x1c] = '\r', // enter
98 [0x01] = 0x1b, // escape
99 [0x39] = ' ', // space
100 [0x0f] = '\t', // tab1
103 static struct input_handler input_handlers[MAX_INPUT_HANDLERS];
105 static void handle_stdin(struct serial_binding *b, char *data, size_t len)
107 terminal_input(data, len);
111 static struct serial_rx_vtbl serial_rx_vtbl = {
112 .input = handle_stdin,
115 static void handle_key_event(struct keyboard_binding *b, uint8_t scancode,
118 char sc = scancode < sizeof(scancode_map) ? scancode_map[scancode] : 0;
120 terminal_input(&sc, 1);
124 static struct keyboard_rx_vtbl keyboard_rx_vtbl = {
125 .key_event = handle_key_event,
128 /// Filter output for serial driver
129 // just converts \n to \r\n for the moment
130 static void filter_output(char *outbuf, size_t outbuflen,
131 const char *in, size_t inlen)
144 /// Returns length of buffer required by filter_output()
145 static size_t filter_output_len(const char *in, size_t inlen)
157 static void serial_write(struct terminal_state *st,
158 const char *data, size_t inlen)
161 assert(st->serial != NULL);
164 size_t outlen = filter_output_len(data, inlen);
165 // check sane size for buffer on stack; after all, this is the terminal!
166 assert(outlen <= 4096);
168 filter_output(outbuf, outlen, data, inlen);
171 assert(st->serial->can_send(st->serial));
172 err = st->serial->tx_vtbl.output(st->serial, NOP_CONT, outbuf, outlen);
173 if (err_is_fail(err)) {
174 USER_PANIC_ERR(err, "error sending terminal output to serial driver");
177 // block on output completion.
178 // this is necessary to maintain libc buffering semantics, and prevent
179 // output being lost if a dispatcher exit()s after a printf
180 while (!st->serial->can_send(st->serial)) {
181 err = event_dispatch(&st->waitset);
182 if (err_is_fail(err)) {
183 USER_PANIC_ERR(err, "error in event_dispatch on terminal waitset");
188 size_t terminal_write(const char *data, size_t length)
190 struct terminal_state *state = get_terminal_state();
193 assert(data != NULL);
195 if (state != NULL && state->serial != NULL) {
196 thread_mutex_lock(&state->mutex);
197 serial_write(state, data, length);
198 thread_mutex_unlock(&state->mutex);
200 sys_print(data, length);
207 size_t terminal_read(char *data, size_t count)
209 struct terminal_state *state = get_terminal_state();
213 thread_mutex_lock(&state->mutex);
215 for(i = 0; i < count; i++) {
216 while(state->consumed == state->produced) {
217 err = event_dispatch(&state->waitset);
218 if (err_is_fail(err)) {
219 USER_PANIC_ERR(err, "error in event_dispatch on terminal waitset");
223 data[i] = state->stdin_buf[(state->consumed++) % BUFSIZE];
226 thread_mutex_unlock(&state->mutex);
231 void terminal_input(char *data, size_t length)
233 struct terminal_state *state = get_terminal_state();
235 for (size_t i = 0; i < length; i++) {
236 /* XXX: translate \r to \n in place */
237 if (data[i] == '\r') {
241 state->stdin_buf[(state->produced++) % BUFSIZE] = data[i];
242 assert(state->produced != state->consumed); // FIXME!
245 for (int i = 0; i < MAX_INPUT_HANDLERS; i++) {
246 if (input_handlers[i].f != NULL) {
247 input_handlers[i].f(input_handlers[i].user_data, data, length);
252 static void serial_bind_cb(void *st, errval_t err, struct serial_binding *b)
254 struct terminal_state *state = st;
256 if (err_is_ok(err)) {
257 b->rx_vtbl = serial_rx_vtbl;
260 if (state->want_stdin) {
261 err = b->tx_vtbl.associate_stdin(b, NOP_CONT);
262 assert(err_is_ok(err)); // XXX: can fail!!
265 state->serial_bound = false;
266 USER_PANIC_ERR(err, "error binding to serial driver");
270 static void keyboard_bind_cb(void *st, errval_t err, struct keyboard_binding *b)
272 struct terminal_state *state = st;
274 if (err_is_ok(err)) {
275 b->rx_vtbl = keyboard_rx_vtbl;
279 state->kbd_bound = false;
280 USER_PANIC_ERR(err, "error binding to keyboard driver");
284 errval_t terminal_init(void)
286 memset(input_handlers, 0, sizeof(input_handlers));
288 /* Allocate and initialize dispatcher-specific state */
289 struct terminal_state *state = malloc(sizeof(struct terminal_state));
291 return LIB_ERR_MALLOC_FAIL;
293 set_terminal_state(state);
294 thread_mutex_init(&state->mutex);
295 state->want_stdin = false;
296 state->serial_bound = false;
297 state->kbd_bound = false;
298 state->produced = state->consumed = 0;
299 state->serial = NULL;
300 waitset_init(&state->waitset);
305 /* Connect to serial driver if possible */
306 err = nameservice_lookup("serial", &iref);
307 if (err_is_fail(err)) {
308 if (err_no(err) == LIB_ERR_NAMESERVICE_UNKNOWN_NAME) {
309 // serial not present, ignore it and continue
316 err = serial_bind(iref, serial_bind_cb, state, &state->waitset,
317 IDC_BIND_FLAGS_DEFAULT);
318 if (err_is_fail(err)) {
319 return err_push(err, LIB_ERR_SERIAL_BIND);
322 state->serial_bound = true;
327 errval_t terminal_want_stdin(unsigned sources)
329 struct terminal_state *state = get_terminal_state();
330 assert(state != NULL);
335 thread_mutex_lock(&state->mutex);
337 state->want_stdin = true;
339 if ((sources & TERMINAL_SOURCE_SERIAL) && !state->serial_bound) {
340 // didn't connect at init time, try again, blocking on the lookup
341 err = nameservice_blocking_lookup("serial", &iref);
342 if (err_is_fail(err)) {
346 err = serial_bind(iref, serial_bind_cb, state, &state->waitset,
347 IDC_BIND_FLAGS_DEFAULT);
348 if (err_is_fail(err)) {
349 err = err_push(err, LIB_ERR_SERIAL_BIND);
352 state->serial_bound = true;
355 // connect to keyboard driver if desired
356 if ((sources & TERMINAL_SOURCE_KEYBOARD) && !state->kbd_bound) {
357 err = nameservice_blocking_lookup("keyboard", &iref);
358 if (err_is_fail(err)) {
362 err = keyboard_bind(iref, keyboard_bind_cb, state,
363 &state->waitset, IDC_BIND_FLAGS_DEFAULT);
364 if (err_is_fail(err)) {
365 err = err_push(err, LIB_ERR_KBD_BIND);
368 state->kbd_bound = true;
371 // XXX: I don't believe this waiting is correct. It changes this from a
372 // non-blocking to a blocking API call. The caller should dispatch
373 // the default waitset, and the bind will eventually complete. -AB
374 while ((sources & TERMINAL_SOURCE_SERIAL) && state->serial == NULL) {
375 err = event_dispatch(get_default_waitset());
376 if (err_is_fail(err)) {
377 USER_PANIC_ERR(err, "event_dispatch on default waitset failed.");
381 if (sources & TERMINAL_SOURCE_SERIAL && state->serial != NULL) {
382 err = state->serial->tx_vtbl.associate_stdin(state->serial, NOP_CONT);
383 if (err_is_fail(err)) {
384 USER_PANIC_ERR(err, "sending associate_stdin failed");
391 thread_mutex_unlock(&state->mutex);
397 * \brief Register a handler to be called when input arrives at the terminal.
399 errval_t terminal_register_input_handler (terminal_input_handler handler,
402 for (int i = 0; i < MAX_INPUT_HANDLERS; i++) {
403 if (input_handlers[i].f == NULL) {
404 input_handlers[i].f = handler;
405 input_handlers[i].user_data = user_data;
410 return TERM_ERR_REGISTER_HANDLER;
414 * \brief Unregister a previously registered input handler.
416 void terminal_unregister_input_handler (terminal_input_handler handler)
418 for (int i = 0; i < MAX_INPUT_HANDLERS; i++) {
419 if (input_handlers[i].f == handler) {
420 input_handlers[i].f = NULL;
421 input_handlers[i].user_data = NULL;