30e0cd4f2fbf729a587602eaf3df8b5cd2262367
[barrelfish] / lib / barrelfish / terminal.c
1 /**
2  * \file
3  * \brief Terminal emulator.
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2010, ETH Zurich.
8  * All rights reserved.
9  *
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.
13  */
14
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>
21 #include <string.h>
22
23 #define BUFSIZE         256
24 #define MAX_INPUT_HANDLERS  10
25
26 struct input_handler {
27     terminal_input_handler      f;
28     void                        *user_data;
29 };
30
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;
37
38     /* input */
39     char stdin_buf[BUFSIZE];
40     unsigned int produced;
41     unsigned int consumed;
42 };
43
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[] = {
47     [0x1E] = 'a',
48     [0x30] = 'b',
49     [0x2E] = 'c',
50     [0x20] = 'd',
51     [0x12] = 'e',
52     [0x21] = 'f',
53     [0x22] = 'g',
54     [0x23] = 'h',
55     [0x17] = 'i',
56     [0x24] = 'j',
57     [0x25] = 'k',
58     [0x26] = 'l',
59     [0x32] = 'm',
60     [0x31] = 'n',
61     [0x18] = 'o',
62     [0x19] = 'p',
63     [0x10] = 'q',
64     [0x13] = 'r',
65     [0x1F] = 's',
66     [0x14] = 't',
67     [0x16] = 'u',
68     [0x2F] = 'v',
69     [0x11] = 'w',
70     [0x2D] = 'x',
71     [0x15] = 'y',
72     [0x2C] = 'z',
73     [0x02] = '1',
74     [0x03] = '2',
75     [0x04] = '3',
76     [0x05] = '4',
77     [0x06] = '5',
78     [0x07] = '6',
79     [0x08] = '7',
80     [0x09] = '8',
81     [0x0a] = '9',
82     [0x0b] = '0',
83     [0x0c] = '_', // XXX: really -, but this is more useful :)
84     [0x0d] = '=',
85     [0x1a] = '[',
86     [0x1b] = ']',
87     [0x2b] = '\\',
88     [0x27] = ';',
89     [0x28] = '\'',
90     [0x29] = '`',
91     [0x33] = ',',
92     [0x34] = '.',
93     [0x35] = '/',
94
95     // control characters
96     [0x0e] = 0x08,    // back-space / delete?
97     [0x1c] = '\r',    // enter
98     [0x01] = 0x1b,    // escape
99     [0x39] = ' ',     // space
100     [0x0f] = '\t',    // tab1
101 };
102
103 static struct input_handler input_handlers[MAX_INPUT_HANDLERS];
104
105 static void handle_stdin(struct serial_binding *b, char *data, size_t len)
106 {
107     terminal_input(data, len);
108     free(data);
109 }
110
111 static struct serial_rx_vtbl serial_rx_vtbl = {
112     .input = handle_stdin,
113 };
114
115 static void handle_key_event(struct keyboard_binding *b, uint8_t scancode,
116                              bool extended)
117 {
118     char sc = scancode < sizeof(scancode_map) ? scancode_map[scancode] : 0;
119     if (sc) {
120         terminal_input(&sc, 1);
121     }
122 }
123
124 static struct keyboard_rx_vtbl keyboard_rx_vtbl = {
125     .key_event = handle_key_event,
126 };
127
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)
132 {
133     while (inlen--) {
134         char c = *in++;
135         if (c == '\n') {
136             assert(outbuflen--);
137             *outbuf++ = '\r';
138         }
139         assert(outbuflen--);
140         *outbuf++ = c;
141     }
142 }
143
144 /// Returns length of buffer required by filter_output()
145 static size_t filter_output_len(const char *in, size_t inlen)
146 {
147     size_t outlen = 0;
148     while (inlen--) {
149         outlen++;
150         if (*in++ == '\n') {
151             outlen++;
152         }
153     }
154     return outlen;
155 }
156
157 static void serial_write(struct terminal_state *st,
158                          const char *data, size_t inlen)
159 {
160     assert(st != NULL);
161     assert(st->serial != NULL);
162     errval_t err;
163
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);
167     char outbuf[outlen];
168     filter_output(outbuf, outlen, data, inlen);
169
170     // try to send
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");
175     }
176
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");
184         }
185     }
186 }
187
188 size_t terminal_write(const char *data, size_t length)
189 {
190     struct terminal_state *state = get_terminal_state();
191
192     if (length > 0) {
193         assert(data != NULL);
194
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);
199         } else {
200             sys_print(data, length);
201         }
202     }
203
204     return length;
205 }
206
207 size_t terminal_read(char *data, size_t count)
208 {
209     struct terminal_state *state = get_terminal_state();
210     errval_t err;
211     size_t i;
212
213     thread_mutex_lock(&state->mutex);
214
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");
220             }
221         }
222
223         data[i] = state->stdin_buf[(state->consumed++) % BUFSIZE];
224     }
225
226     thread_mutex_unlock(&state->mutex);
227
228     return count;
229 }
230
231 void terminal_input(char *data, size_t length)
232 {
233     struct terminal_state *state = get_terminal_state();
234
235     for (size_t i = 0; i < length; i++) {
236         /* XXX: translate \r to \n in place */
237         if (data[i] == '\r') {
238             data[i] = '\n';
239         }
240
241         state->stdin_buf[(state->produced++) % BUFSIZE] = data[i];
242         assert(state->produced != state->consumed); // FIXME!
243     }
244
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);
248         }
249     }
250 }
251
252 static void serial_bind_cb(void *st, errval_t err, struct serial_binding *b)
253 {
254     struct terminal_state *state = st;
255
256     if (err_is_ok(err)) {
257         b->rx_vtbl = serial_rx_vtbl;
258         b->st = state;
259         state->serial = b;
260         if (state->want_stdin) {
261             err = b->tx_vtbl.associate_stdin(b, NOP_CONT);
262             assert(err_is_ok(err)); // XXX: can fail!!
263         }
264     } else {
265         state->serial_bound = false;
266         USER_PANIC_ERR(err, "error binding to serial driver");
267     }
268 }
269
270 static void keyboard_bind_cb(void *st, errval_t err, struct keyboard_binding *b)
271 {
272     struct terminal_state *state = st;
273
274     if (err_is_ok(err)) {
275         b->rx_vtbl = keyboard_rx_vtbl;
276         b->st = state;
277         state->kbd = b;
278     } else {
279         state->kbd_bound = false;
280         USER_PANIC_ERR(err, "error binding to keyboard driver");
281     }
282 }
283
284 errval_t terminal_init(void)
285 {
286     memset(input_handlers, 0, sizeof(input_handlers));
287
288     /* Allocate and initialize dispatcher-specific state */
289     struct terminal_state *state = malloc(sizeof(struct terminal_state));
290     if (!state) {
291         return LIB_ERR_MALLOC_FAIL;
292     }
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);
301
302     iref_t iref;
303     errval_t err;
304
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
310             return SYS_ERR_OK;
311         } else {
312             return err;
313         }
314     }
315
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);
320     }
321
322     state->serial_bound = true;
323
324     return SYS_ERR_OK;
325 }
326
327 errval_t terminal_want_stdin(unsigned sources)
328 {
329     struct terminal_state *state = get_terminal_state();
330     assert(state != NULL);
331
332     iref_t iref;
333     errval_t err;
334
335     thread_mutex_lock(&state->mutex);
336
337     state->want_stdin = true;
338
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)) {
343             goto out;
344         }
345
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);
350             goto out;
351         }
352         state->serial_bound = true;
353     }
354
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)) {
359             goto out;
360         }
361
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);
366             goto out;
367         }
368         state->kbd_bound = true;
369     }
370
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.");
378         }
379     }
380
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");
385         }
386     }
387
388     err = SYS_ERR_OK;
389
390  out:
391     thread_mutex_unlock(&state->mutex);
392
393     return err;
394 }
395
396 /**
397  * \brief Register a handler to be called when input arrives at the terminal.
398  */
399 errval_t terminal_register_input_handler (terminal_input_handler handler,
400                                           void * user_data)
401 {
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;
406             return SYS_ERR_OK;
407         }
408     }
409
410     return TERM_ERR_REGISTER_HANDLER;
411 }
412
413 /**
414  * \brief Unregister a previously registered input handler.
415  */
416 void terminal_unregister_input_handler (terminal_input_handler handler)
417 {
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;
422         }
423     }
424 }