source: mainline/uspace/srv/hid/console/console.c@ 111d2d6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 111d2d6 was 111d2d6, checked in by Jiri Svoboda <jiri@…>, 13 years ago

Factor out input protocol client code.

  • Property mode set to 100644
File size: 16.1 KB
Line 
1/*
2 * Copyright (c) 2011 Martin Decky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup console
30 * @{
31 */
32/** @file
33 */
34
35#include <async.h>
36#include <stdio.h>
37#include <adt/prodcons.h>
38#include <io/input.h>
39#include <ipc/console.h>
40#include <ipc/vfs.h>
41#include <errno.h>
42#include <str_error.h>
43#include <loc.h>
44#include <event.h>
45#include <io/kbd_event.h>
46#include <io/keycode.h>
47#include <io/chargrid.h>
48#include <io/console.h>
49#include <io/output.h>
50#include <align.h>
51#include <malloc.h>
52#include <as.h>
53#include <fibril_synch.h>
54#include "console.h"
55
56#define NAME "console"
57#define NAMESPACE "term"
58
59#define UTF8_CHAR_BUFFER_SIZE (STR_BOUNDS(1) + 1)
60
61typedef struct {
62 atomic_t refcnt; /**< Connection reference count */
63 prodcons_t input_pc; /**< Incoming keyboard events */
64
65 /**
66 * Not yet sent bytes of last char event.
67 */
68 char char_remains[UTF8_CHAR_BUFFER_SIZE];
69 size_t char_remains_len; /**< Number of not yet sent bytes. */
70
71 fibril_mutex_t mtx; /**< Lock protecting mutable fields */
72
73 size_t index; /**< Console index */
74 service_id_t dsid; /**< Service handle */
75
76 sysarg_t cols; /**< Number of columns */
77 sysarg_t rows; /**< Number of rows */
78 console_caps_t ccaps; /**< Console capabilities */
79
80 chargrid_t *frontbuf; /**< Front buffer */
81 frontbuf_handle_t fbid; /**< Front buffer handle */
82} console_t;
83
84/** Input server proxy */
85static input_t *input;
86
87/** Session to the output server */
88static async_sess_t *output_sess;
89
90/** Output dimensions */
91static sysarg_t cols;
92static sysarg_t rows;
93
94/** Array of data for virtual consoles */
95static console_t consoles[CONSOLE_COUNT];
96
97/** Mutex for console switching */
98static FIBRIL_MUTEX_INITIALIZE(switch_mtx);
99
100static console_t *prev_console = &consoles[0];
101static console_t *active_console = &consoles[0];
102static console_t *kernel_console = &consoles[KERNEL_CONSOLE];
103
104static int input_ev_key(input_t *, kbd_event_type_t, keycode_t, keymod_t, wchar_t);
105static int input_ev_move(input_t *, int, int);
106static int input_ev_abs_move(input_t *, unsigned, unsigned, unsigned, unsigned);
107static int input_ev_button(input_t *, int, int);
108
109static input_ev_ops_t input_ev_ops = {
110 .key = input_ev_key,
111 .move = input_ev_move,
112 .abs_move = input_ev_abs_move,
113 .button = input_ev_button
114};
115
116static void cons_update(console_t *cons)
117{
118 fibril_mutex_lock(&switch_mtx);
119 fibril_mutex_lock(&cons->mtx);
120
121 if ((cons == active_console) && (active_console != kernel_console)) {
122 output_update(output_sess, cons->fbid);
123 output_cursor_update(output_sess, cons->fbid);
124 }
125
126 fibril_mutex_unlock(&cons->mtx);
127 fibril_mutex_unlock(&switch_mtx);
128}
129
130static void cons_update_cursor(console_t *cons)
131{
132 fibril_mutex_lock(&switch_mtx);
133 fibril_mutex_lock(&cons->mtx);
134
135 if ((cons == active_console) && (active_console != kernel_console))
136 output_cursor_update(output_sess, cons->fbid);
137
138 fibril_mutex_unlock(&cons->mtx);
139 fibril_mutex_unlock(&switch_mtx);
140}
141
142static void cons_clear(console_t *cons)
143{
144 fibril_mutex_lock(&cons->mtx);
145 chargrid_clear(cons->frontbuf);
146 fibril_mutex_unlock(&cons->mtx);
147
148 cons_update(cons);
149}
150
151static void cons_damage(console_t *cons)
152{
153 fibril_mutex_lock(&switch_mtx);
154 fibril_mutex_lock(&cons->mtx);
155
156 if ((cons == active_console) && (active_console != kernel_console)) {
157 output_damage(output_sess, cons->fbid, 0, 0, cons->cols,
158 cons->rows);
159 output_cursor_update(output_sess, cons->fbid);
160 }
161
162 fibril_mutex_unlock(&cons->mtx);
163 fibril_mutex_unlock(&switch_mtx);
164}
165
166static void cons_switch(console_t *cons)
167{
168 fibril_mutex_lock(&switch_mtx);
169
170 if (cons == active_console) {
171 fibril_mutex_unlock(&switch_mtx);
172 return;
173 }
174
175 if (cons == kernel_console) {
176 output_yield(output_sess);
177 if (!console_kcon()) {
178 output_claim(output_sess);
179 fibril_mutex_unlock(&switch_mtx);
180 return;
181 }
182 }
183
184 if (active_console == kernel_console)
185 output_claim(output_sess);
186
187 prev_console = active_console;
188 active_console = cons;
189
190 fibril_mutex_unlock(&switch_mtx);
191
192 cons_damage(cons);
193}
194
195static console_t *cons_get_active_uspace(void)
196{
197 fibril_mutex_lock(&switch_mtx);
198
199 console_t *active_uspace = active_console;
200 if (active_uspace == kernel_console)
201 active_uspace = prev_console;
202
203 assert(active_uspace != kernel_console);
204
205 fibril_mutex_unlock(&switch_mtx);
206
207 return active_uspace;
208}
209
210static int input_ev_key(input_t *input, kbd_event_type_t type, keycode_t key,
211 keymod_t mods, wchar_t c)
212{
213 if ((key >= KC_F1) && (key < KC_F1 + CONSOLE_COUNT) &&
214 ((mods & KM_CTRL) == 0)) {
215 cons_switch(&consoles[key - KC_F1]);
216 } else {
217 /* Got key press/release event */
218 kbd_event_t *event =
219 (kbd_event_t *) malloc(sizeof(kbd_event_t));
220 if (event == NULL) {
221 return ENOMEM;
222 }
223
224 link_initialize(&event->link);
225 event->type = type;
226 event->key = key;
227 event->mods = mods;
228 event->c = c;
229
230 /*
231 * Kernel console does not read events
232 * from us, so we will redirect them
233 * to the (last) active userspace console
234 * if necessary.
235 */
236 console_t *target_console = cons_get_active_uspace();
237
238 prodcons_produce(&target_console->input_pc,
239 &event->link);
240 }
241
242 return EOK;
243}
244
245static int input_ev_move(input_t *input, int dx, int dy)
246{
247 return EOK;
248}
249
250static int input_ev_abs_move(input_t *input, unsigned x , unsigned y,
251 unsigned max_x, unsigned max_y)
252{
253 return EOK;
254}
255
256static int input_ev_button(input_t *input, int bnum, int bpress)
257{
258 return EOK;
259}
260
261/** Process a character from the client (TTY emulation). */
262static void cons_write_char(console_t *cons, wchar_t ch)
263{
264 sysarg_t updated = 0;
265
266 fibril_mutex_lock(&cons->mtx);
267
268 switch (ch) {
269 case '\n':
270 updated = chargrid_newline(cons->frontbuf);
271 break;
272 case '\r':
273 break;
274 case '\t':
275 updated = chargrid_tabstop(cons->frontbuf, 8);
276 break;
277 case '\b':
278 updated = chargrid_backspace(cons->frontbuf);
279 break;
280 default:
281 updated = chargrid_putchar(cons->frontbuf, ch, true);
282 }
283
284 fibril_mutex_unlock(&cons->mtx);
285
286 if (updated > 1)
287 cons_update(cons);
288}
289
290static void cons_set_cursor(console_t *cons, sysarg_t col, sysarg_t row)
291{
292 fibril_mutex_lock(&cons->mtx);
293 chargrid_set_cursor(cons->frontbuf, col, row);
294 fibril_mutex_unlock(&cons->mtx);
295
296 cons_update_cursor(cons);
297}
298
299static void cons_set_cursor_visibility(console_t *cons, bool visible)
300{
301 fibril_mutex_lock(&cons->mtx);
302 chargrid_set_cursor_visibility(cons->frontbuf, visible);
303 fibril_mutex_unlock(&cons->mtx);
304
305 cons_update_cursor(cons);
306}
307
308static void cons_get_cursor(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
309{
310 sysarg_t col;
311 sysarg_t row;
312
313 fibril_mutex_lock(&cons->mtx);
314 chargrid_get_cursor(cons->frontbuf, &col, &row);
315 fibril_mutex_unlock(&cons->mtx);
316
317 async_answer_2(iid, EOK, col, row);
318}
319
320static void cons_write(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
321{
322 void *buf;
323 size_t size;
324 int rc = async_data_write_accept(&buf, false, 0, 0, 0, &size);
325
326 if (rc != EOK) {
327 async_answer_0(iid, rc);
328 return;
329 }
330
331 size_t off = 0;
332 while (off < size)
333 cons_write_char(cons, str_decode(buf, &off, size));
334
335 async_answer_1(iid, EOK, size);
336 free(buf);
337}
338
339static void cons_read(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
340{
341 ipc_callid_t callid;
342 size_t size;
343 if (!async_data_read_receive(&callid, &size)) {
344 async_answer_0(callid, EINVAL);
345 async_answer_0(iid, EINVAL);
346 return;
347 }
348
349 char *buf = (char *) malloc(size);
350 if (buf == NULL) {
351 async_answer_0(callid, ENOMEM);
352 async_answer_0(iid, ENOMEM);
353 return;
354 }
355
356 size_t pos = 0;
357
358 /*
359 * Read input from keyboard and copy it to the buffer.
360 * We need to handle situation when wchar is split by 2 following
361 * reads.
362 */
363 while (pos < size) {
364 /* Copy to the buffer remaining characters. */
365 while ((pos < size) && (cons->char_remains_len > 0)) {
366 buf[pos] = cons->char_remains[0];
367 pos++;
368
369 /* Unshift the array. */
370 for (size_t i = 1; i < cons->char_remains_len; i++)
371 cons->char_remains[i - 1] = cons->char_remains[i];
372
373 cons->char_remains_len--;
374 }
375
376 /* Still not enough? Then get another key from the queue. */
377 if (pos < size) {
378 link_t *link = prodcons_consume(&cons->input_pc);
379 kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
380
381 /* Accept key presses of printable chars only. */
382 if ((event->type == KEY_PRESS) && (event->c != 0)) {
383 wchar_t tmp[2] = { event->c, 0 };
384 wstr_to_str(cons->char_remains, UTF8_CHAR_BUFFER_SIZE, tmp);
385 cons->char_remains_len = str_size(cons->char_remains);
386 }
387
388 free(event);
389 }
390 }
391
392 (void) async_data_read_finalize(callid, buf, size);
393 async_answer_1(iid, EOK, size);
394 free(buf);
395}
396
397static void cons_set_style(console_t *cons, console_style_t style)
398{
399 fibril_mutex_lock(&cons->mtx);
400 chargrid_set_style(cons->frontbuf, style);
401 fibril_mutex_unlock(&cons->mtx);
402}
403
404static void cons_set_color(console_t *cons, console_color_t bgcolor,
405 console_color_t fgcolor, console_color_attr_t attr)
406{
407 fibril_mutex_lock(&cons->mtx);
408 chargrid_set_color(cons->frontbuf, bgcolor, fgcolor, attr);
409 fibril_mutex_unlock(&cons->mtx);
410}
411
412static void cons_set_rgb_color(console_t *cons, pixel_t bgcolor,
413 pixel_t fgcolor)
414{
415 fibril_mutex_lock(&cons->mtx);
416 chargrid_set_rgb_color(cons->frontbuf, bgcolor, fgcolor);
417 fibril_mutex_unlock(&cons->mtx);
418}
419
420static void cons_get_event(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
421{
422 link_t *link = prodcons_consume(&cons->input_pc);
423 kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
424
425 async_answer_4(iid, EOK, event->type, event->key, event->mods, event->c);
426 free(event);
427}
428
429static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
430{
431 console_t *cons = NULL;
432
433 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
434 if (i == KERNEL_CONSOLE)
435 continue;
436
437 if (consoles[i].dsid == (service_id_t) IPC_GET_ARG1(*icall)) {
438 cons = &consoles[i];
439 break;
440 }
441 }
442
443 if (cons == NULL) {
444 async_answer_0(iid, ENOENT);
445 return;
446 }
447
448 if (atomic_postinc(&cons->refcnt) == 0)
449 cons_set_cursor_visibility(cons, true);
450
451 /* Accept the connection */
452 async_answer_0(iid, EOK);
453
454 while (true) {
455 ipc_call_t call;
456 ipc_callid_t callid = async_get_call(&call);
457
458 if (!IPC_GET_IMETHOD(call))
459 return;
460
461 switch (IPC_GET_IMETHOD(call)) {
462 case VFS_OUT_READ:
463 cons_read(cons, callid, &call);
464 break;
465 case VFS_OUT_WRITE:
466 cons_write(cons, callid, &call);
467 break;
468 case VFS_OUT_SYNC:
469 cons_update(cons);
470 async_answer_0(callid, EOK);
471 break;
472 case CONSOLE_CLEAR:
473 cons_clear(cons);
474 async_answer_0(callid, EOK);
475 break;
476 case CONSOLE_GOTO:
477 cons_set_cursor(cons, IPC_GET_ARG1(call), IPC_GET_ARG2(call));
478 async_answer_0(callid, EOK);
479 break;
480 case CONSOLE_GET_POS:
481 cons_get_cursor(cons, callid, &call);
482 break;
483 case CONSOLE_GET_SIZE:
484 async_answer_2(callid, EOK, cons->cols, cons->rows);
485 break;
486 case CONSOLE_GET_COLOR_CAP:
487 async_answer_1(callid, EOK, cons->ccaps);
488 break;
489 case CONSOLE_SET_STYLE:
490 cons_set_style(cons, IPC_GET_ARG1(call));
491 async_answer_0(callid, EOK);
492 break;
493 case CONSOLE_SET_COLOR:
494 cons_set_color(cons, IPC_GET_ARG1(call), IPC_GET_ARG2(call),
495 IPC_GET_ARG3(call));
496 async_answer_0(callid, EOK);
497 break;
498 case CONSOLE_SET_RGB_COLOR:
499 cons_set_rgb_color(cons, IPC_GET_ARG1(call), IPC_GET_ARG2(call));
500 async_answer_0(callid, EOK);
501 break;
502 case CONSOLE_CURSOR_VISIBILITY:
503 cons_set_cursor_visibility(cons, IPC_GET_ARG1(call));
504 async_answer_0(callid, EOK);
505 break;
506 case CONSOLE_GET_EVENT:
507 cons_get_event(cons, callid, &call);
508 break;
509 default:
510 async_answer_0(callid, EINVAL);
511 }
512 }
513}
514
515static int input_connect(const char *svc)
516{
517 async_sess_t *sess;
518 service_id_t dsid;
519
520 int rc = loc_service_get_id(svc, &dsid, 0);
521 if (rc != EOK) {
522 printf("%s: Input service %s not found\n", NAME, svc);
523 return rc;
524 }
525
526 sess = loc_service_connect(EXCHANGE_ATOMIC, dsid, 0);
527 if (sess == NULL) {
528 printf("%s: Unable to connect to input service %s\n", NAME,
529 svc);
530 return EIO;
531 }
532
533 rc = input_open(sess, &input_ev_ops, NULL, &input);
534 if (rc != EOK) {
535 async_hangup(sess);
536 printf("%s: Unable to communicate with service %s (%s)\n",
537 NAME, svc, str_error(rc));
538 return rc;
539 }
540
541 return EOK;
542}
543
544static void interrupt_received(ipc_callid_t callid, ipc_call_t *call)
545{
546 cons_switch(prev_console);
547}
548
549static async_sess_t *output_connect(const char *svc)
550{
551 async_sess_t *sess;
552 service_id_t dsid;
553
554 int rc = loc_service_get_id(svc, &dsid, 0);
555 if (rc == EOK) {
556 sess = loc_service_connect(EXCHANGE_SERIALIZE, dsid, 0);
557 if (sess == NULL) {
558 printf("%s: Unable to connect to output service %s\n",
559 NAME, svc);
560 return NULL;
561 }
562 } else
563 return NULL;
564
565 return sess;
566}
567
568static bool console_srv_init(char *input_svc, char *output_svc)
569{
570 int rc;
571
572 /* Connect to input service */
573 rc = input_connect(input_svc);
574 if (rc != EOK)
575 return false;
576
577 /* Connect to output service */
578 output_sess = output_connect(output_svc);
579 if (output_sess == NULL)
580 return false;
581
582 /* Register server */
583 async_set_client_connection(client_connection);
584 rc = loc_server_register(NAME);
585 if (rc != EOK) {
586 printf("%s: Unable to register server (%s)\n", NAME,
587 str_error(rc));
588 return false;
589 }
590
591 output_get_dimensions(output_sess, &cols, &rows);
592 output_set_style(output_sess, STYLE_NORMAL);
593
594 console_caps_t ccaps;
595 output_get_caps(output_sess, &ccaps);
596
597 /* Inititalize consoles */
598 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
599 consoles[i].index = i;
600 atomic_set(&consoles[i].refcnt, 0);
601 fibril_mutex_initialize(&consoles[i].mtx);
602 prodcons_initialize(&consoles[i].input_pc);
603 consoles[i].char_remains_len = 0;
604
605 if (i == KERNEL_CONSOLE)
606 continue;
607
608 consoles[i].cols = cols;
609 consoles[i].rows = rows;
610 consoles[i].ccaps = ccaps;
611 consoles[i].frontbuf =
612 chargrid_create(cols, rows, CHARGRID_FLAG_SHARED);
613
614 if (consoles[i].frontbuf == NULL) {
615 printf("%s: Unable to allocate frontbuffer %zu\n", NAME, i);
616 return false;
617 }
618
619 consoles[i].fbid = output_frontbuf_create(output_sess,
620 consoles[i].frontbuf);
621 if (consoles[i].fbid == 0) {
622 printf("%s: Unable to create frontbuffer %zu\n", NAME, i);
623 return false;
624 }
625
626 char vc[LOC_NAME_MAXLEN + 1];
627 snprintf(vc, LOC_NAME_MAXLEN, "%s/vc%zu", NAMESPACE, i);
628
629 if (loc_service_register(vc, &consoles[i].dsid) != EOK) {
630 printf("%s: Unable to register device %s\n", NAME, vc);
631 return false;
632 }
633 }
634
635 cons_damage(active_console);
636
637 /* Receive kernel notifications */
638 async_set_interrupt_received(interrupt_received);
639 rc = event_subscribe(EVENT_KCONSOLE, 0);
640 if (rc != EOK)
641 printf("%s: Failed to register kconsole notifications (%s)\n",
642 NAME, str_error(rc));
643
644 return true;
645}
646
647static void usage(char *name)
648{
649 printf("Usage: %s <input_dev> <output_dev>\n", name);
650}
651
652int main(int argc, char *argv[])
653{
654 if (argc < 3) {
655 usage(argv[0]);
656 return -1;
657 }
658
659 printf("%s: HelenOS Console service\n", NAME);
660
661 if (!console_srv_init(argv[1], argv[2]))
662 return -1;
663
664 printf("%s: Accepting connections\n", NAME);
665 task_retval(0);
666 async_manager();
667
668 /* Never reached */
669 return 0;
670}
671
672/** @}
673 */
Note: See TracBrowser for help on using the repository browser.