source: mainline/uspace/srv/hid/console/console.c@ b688fd8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b688fd8 was b688fd8, checked in by Martin Decky <martin@…>, 10 years ago

gradually introduce async ports, initial phase

The initial phase is to reimplement the traditional async client connections as an untyped fallback port. This creates the possibility to introduce ports typed by interface type gradually in later changesets.

  • Property mode set to 100644
File size: 16.0 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/vfs.h>
40#include <errno.h>
41#include <str_error.h>
42#include <loc.h>
43#include <io/con_srv.h>
44#include <io/kbd_event.h>
45#include <io/keycode.h>
46#include <io/chargrid.h>
47#include <io/output.h>
48#include <align.h>
49#include <malloc.h>
50#include <as.h>
51#include <fibril_synch.h>
52#include "console.h"
53
54#define NAME "console"
55#define NAMESPACE "term"
56
57#define UTF8_CHAR_BUFFER_SIZE (STR_BOUNDS(1) + 1)
58
59typedef struct {
60 atomic_t refcnt; /**< Connection reference count */
61 prodcons_t input_pc; /**< Incoming keyboard events */
62
63 /**
64 * Not yet sent bytes of last char event.
65 */
66 char char_remains[UTF8_CHAR_BUFFER_SIZE];
67 size_t char_remains_len; /**< Number of not yet sent bytes. */
68
69 fibril_mutex_t mtx; /**< Lock protecting mutable fields */
70
71 size_t index; /**< Console index */
72 service_id_t dsid; /**< Service handle */
73
74 sysarg_t cols; /**< Number of columns */
75 sysarg_t rows; /**< Number of rows */
76 console_caps_t ccaps; /**< Console capabilities */
77
78 chargrid_t *frontbuf; /**< Front buffer */
79 frontbuf_handle_t fbid; /**< Front buffer handle */
80 con_srvs_t srvs; /**< Console service setup */
81} console_t;
82
83/** Input server proxy */
84static input_t *input;
85static bool active = false;
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 *active_console = &consoles[0];
101
102static int input_ev_active(input_t *);
103static int input_ev_deactive(input_t *);
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 .active = input_ev_active,
111 .deactive = input_ev_deactive,
112 .key = input_ev_key,
113 .move = input_ev_move,
114 .abs_move = input_ev_abs_move,
115 .button = input_ev_button
116};
117
118static int cons_open(con_srvs_t *, con_srv_t *);
119static int cons_close(con_srv_t *);
120static int cons_read(con_srv_t *, void *, size_t);
121static int cons_write(con_srv_t *, void *, size_t);
122static void cons_sync(con_srv_t *);
123static void cons_clear(con_srv_t *);
124static void cons_set_pos(con_srv_t *, sysarg_t col, sysarg_t row);
125static int cons_get_pos(con_srv_t *, sysarg_t *, sysarg_t *);
126static int cons_get_size(con_srv_t *, sysarg_t *, sysarg_t *);
127static int cons_get_color_cap(con_srv_t *, console_caps_t *);
128static void cons_set_style(con_srv_t *, console_style_t);
129static void cons_set_color(con_srv_t *, console_color_t, console_color_t,
130 console_color_attr_t);
131static void cons_set_rgb_color(con_srv_t *, pixel_t, pixel_t);
132static void cons_set_cursor_visibility(con_srv_t *, bool);
133static int cons_get_event(con_srv_t *, cons_event_t *);
134
135static con_ops_t con_ops = {
136 .open = cons_open,
137 .close = cons_close,
138 .read = cons_read,
139 .write = cons_write,
140 .sync = cons_sync,
141 .clear = cons_clear,
142 .set_pos = cons_set_pos,
143 .get_pos = cons_get_pos,
144 .get_size = cons_get_size,
145 .get_color_cap = cons_get_color_cap,
146 .set_style = cons_set_style,
147 .set_color = cons_set_color,
148 .set_rgb_color = cons_set_rgb_color,
149 .set_cursor_visibility = cons_set_cursor_visibility,
150 .get_event = cons_get_event
151};
152
153static console_t *srv_to_console(con_srv_t *srv)
154{
155 return srv->srvs->sarg;
156}
157
158static void cons_update(console_t *cons)
159{
160 fibril_mutex_lock(&switch_mtx);
161 fibril_mutex_lock(&cons->mtx);
162
163 if ((active) && (cons == active_console)) {
164 output_update(output_sess, cons->fbid);
165 output_cursor_update(output_sess, cons->fbid);
166 }
167
168 fibril_mutex_unlock(&cons->mtx);
169 fibril_mutex_unlock(&switch_mtx);
170}
171
172static void cons_update_cursor(console_t *cons)
173{
174 fibril_mutex_lock(&switch_mtx);
175 fibril_mutex_lock(&cons->mtx);
176
177 if ((active) && (cons == active_console))
178 output_cursor_update(output_sess, cons->fbid);
179
180 fibril_mutex_unlock(&cons->mtx);
181 fibril_mutex_unlock(&switch_mtx);
182}
183
184static void cons_damage(console_t *cons)
185{
186 fibril_mutex_lock(&switch_mtx);
187 fibril_mutex_lock(&cons->mtx);
188
189 if ((active) && (cons == active_console)) {
190 output_damage(output_sess, cons->fbid, 0, 0, cons->cols,
191 cons->rows);
192 output_cursor_update(output_sess, cons->fbid);
193 }
194
195 fibril_mutex_unlock(&cons->mtx);
196 fibril_mutex_unlock(&switch_mtx);
197}
198
199static void cons_switch(unsigned int index)
200{
201 /*
202 * The first undefined index is reserved
203 * for switching to the kernel console.
204 */
205 if (index == CONSOLE_COUNT) {
206 if (console_kcon())
207 active = false;
208
209 return;
210 }
211
212 if (index > CONSOLE_COUNT)
213 return;
214
215 console_t *cons = &consoles[index];
216
217 fibril_mutex_lock(&switch_mtx);
218
219 if (cons == active_console) {
220 fibril_mutex_unlock(&switch_mtx);
221 return;
222 }
223
224 active_console = cons;
225
226 fibril_mutex_unlock(&switch_mtx);
227
228 cons_damage(cons);
229}
230
231static int input_ev_active(input_t *input)
232{
233 active = true;
234 output_claim(output_sess);
235 cons_damage(active_console);
236
237 return EOK;
238}
239
240static int input_ev_deactive(input_t *input)
241{
242 active = false;
243 output_yield(output_sess);
244
245 return EOK;
246}
247
248static int input_ev_key(input_t *input, kbd_event_type_t type, keycode_t key,
249 keymod_t mods, wchar_t c)
250{
251 if ((key >= KC_F1) && (key <= KC_F1 + CONSOLE_COUNT) &&
252 ((mods & KM_CTRL) == 0)) {
253 cons_switch(key - KC_F1);
254 } else {
255 /* Got key press/release event */
256 kbd_event_t *event =
257 (kbd_event_t *) malloc(sizeof(kbd_event_t));
258 if (event == NULL) {
259 return ENOMEM;
260 }
261
262 link_initialize(&event->link);
263 event->type = type;
264 event->key = key;
265 event->mods = mods;
266 event->c = c;
267
268 prodcons_produce(&active_console->input_pc,
269 &event->link);
270 }
271
272 return EOK;
273}
274
275static int input_ev_move(input_t *input, int dx, int dy)
276{
277 return EOK;
278}
279
280static int input_ev_abs_move(input_t *input, unsigned x , unsigned y,
281 unsigned max_x, unsigned max_y)
282{
283 return EOK;
284}
285
286static int input_ev_button(input_t *input, int bnum, int bpress)
287{
288 return EOK;
289}
290
291/** Process a character from the client (TTY emulation). */
292static void cons_write_char(console_t *cons, wchar_t ch)
293{
294 sysarg_t updated = 0;
295
296 fibril_mutex_lock(&cons->mtx);
297
298 switch (ch) {
299 case '\n':
300 updated = chargrid_newline(cons->frontbuf);
301 break;
302 case '\r':
303 break;
304 case '\t':
305 updated = chargrid_tabstop(cons->frontbuf, 8);
306 break;
307 case '\b':
308 updated = chargrid_backspace(cons->frontbuf);
309 break;
310 default:
311 updated = chargrid_putchar(cons->frontbuf, ch, true);
312 }
313
314 fibril_mutex_unlock(&cons->mtx);
315
316 if (updated > 1)
317 cons_update(cons);
318}
319
320static void cons_set_cursor_vis(console_t *cons, bool visible)
321{
322 fibril_mutex_lock(&cons->mtx);
323 chargrid_set_cursor_visibility(cons->frontbuf, visible);
324 fibril_mutex_unlock(&cons->mtx);
325
326 cons_update_cursor(cons);
327}
328
329static int cons_open(con_srvs_t *srvs, con_srv_t *srv)
330{
331 return EOK;
332}
333
334static int cons_close(con_srv_t *srv)
335{
336 return EOK;
337}
338
339static int cons_read(con_srv_t *srv, void *buf, size_t size)
340{
341 uint8_t *bbuf = buf;
342 console_t *cons = srv_to_console(srv);
343 size_t pos = 0;
344
345 /*
346 * Read input from keyboard and copy it to the buffer.
347 * We need to handle situation when wchar is split by 2 following
348 * reads.
349 */
350 while (pos < size) {
351 /* Copy to the buffer remaining characters. */
352 while ((pos < size) && (cons->char_remains_len > 0)) {
353 bbuf[pos] = cons->char_remains[0];
354 pos++;
355
356 /* Unshift the array. */
357 for (size_t i = 1; i < cons->char_remains_len; i++)
358 cons->char_remains[i - 1] = cons->char_remains[i];
359
360 cons->char_remains_len--;
361 }
362
363 /* Still not enough? Then get another key from the queue. */
364 if (pos < size) {
365 link_t *link = prodcons_consume(&cons->input_pc);
366 kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
367
368 /* Accept key presses of printable chars only. */
369 if ((event->type == KEY_PRESS) && (event->c != 0)) {
370 wchar_t tmp[2] = { event->c, 0 };
371 wstr_to_str(cons->char_remains, UTF8_CHAR_BUFFER_SIZE, tmp);
372 cons->char_remains_len = str_size(cons->char_remains);
373 }
374
375 free(event);
376 }
377 }
378
379 return size;
380}
381
382static int cons_write(con_srv_t *srv, void *data, size_t size)
383{
384 console_t *cons = srv_to_console(srv);
385
386 size_t off = 0;
387 while (off < size)
388 cons_write_char(cons, str_decode(data, &off, size));
389
390 return size;
391}
392
393static void cons_sync(con_srv_t *srv)
394{
395 console_t *cons = srv_to_console(srv);
396
397 cons_update(cons);
398}
399
400static void cons_clear(con_srv_t *srv)
401{
402 console_t *cons = srv_to_console(srv);
403
404 fibril_mutex_lock(&cons->mtx);
405 chargrid_clear(cons->frontbuf);
406 fibril_mutex_unlock(&cons->mtx);
407
408 cons_update(cons);
409}
410
411static void cons_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
412{
413 console_t *cons = srv_to_console(srv);
414
415 fibril_mutex_lock(&cons->mtx);
416 chargrid_set_cursor(cons->frontbuf, col, row);
417 fibril_mutex_unlock(&cons->mtx);
418
419 cons_update_cursor(cons);
420}
421
422static int cons_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
423{
424 console_t *cons = srv_to_console(srv);
425
426 fibril_mutex_lock(&cons->mtx);
427 chargrid_get_cursor(cons->frontbuf, col, row);
428 fibril_mutex_unlock(&cons->mtx);
429
430 return EOK;
431}
432
433static int cons_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
434{
435 console_t *cons = srv_to_console(srv);
436
437 fibril_mutex_lock(&cons->mtx);
438 *cols = cons->cols;
439 *rows = cons->rows;
440 fibril_mutex_unlock(&cons->mtx);
441
442 return EOK;
443}
444
445static int cons_get_color_cap(con_srv_t *srv, console_caps_t *ccaps)
446{
447 console_t *cons = srv_to_console(srv);
448
449 fibril_mutex_lock(&cons->mtx);
450 *ccaps = cons->ccaps;
451 fibril_mutex_unlock(&cons->mtx);
452
453 return EOK;
454}
455
456static void cons_set_style(con_srv_t *srv, console_style_t style)
457{
458 console_t *cons = srv_to_console(srv);
459
460 fibril_mutex_lock(&cons->mtx);
461 chargrid_set_style(cons->frontbuf, style);
462 fibril_mutex_unlock(&cons->mtx);
463}
464
465static void cons_set_color(con_srv_t *srv, console_color_t bgcolor,
466 console_color_t fgcolor, console_color_attr_t attr)
467{
468 console_t *cons = srv_to_console(srv);
469
470 fibril_mutex_lock(&cons->mtx);
471 chargrid_set_color(cons->frontbuf, bgcolor, fgcolor, attr);
472 fibril_mutex_unlock(&cons->mtx);
473}
474
475static void cons_set_rgb_color(con_srv_t *srv, pixel_t bgcolor,
476 pixel_t fgcolor)
477{
478 console_t *cons = srv_to_console(srv);
479
480 fibril_mutex_lock(&cons->mtx);
481 chargrid_set_rgb_color(cons->frontbuf, bgcolor, fgcolor);
482 fibril_mutex_unlock(&cons->mtx);
483}
484
485static void cons_set_cursor_visibility(con_srv_t *srv, bool visible)
486{
487 console_t *cons = srv_to_console(srv);
488
489 cons_set_cursor_vis(cons, visible);
490}
491
492static int cons_get_event(con_srv_t *srv, cons_event_t *event)
493{
494 console_t *cons = srv_to_console(srv);
495 link_t *link = prodcons_consume(&cons->input_pc);
496 kbd_event_t *kevent = list_get_instance(link, kbd_event_t, link);
497
498 event->type = CEV_KEY;
499 event->ev.key = *kevent;
500
501 free(kevent);
502 return EOK;
503}
504
505static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
506{
507 console_t *cons = NULL;
508
509 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
510 if (consoles[i].dsid == (service_id_t) IPC_GET_ARG1(*icall)) {
511 cons = &consoles[i];
512 break;
513 }
514 }
515
516 if (cons == NULL) {
517 async_answer_0(iid, ENOENT);
518 return;
519 }
520
521 if (atomic_postinc(&cons->refcnt) == 0)
522 cons_set_cursor_vis(cons, true);
523
524 con_conn(iid, icall, &cons->srvs);
525}
526
527static int input_connect(const char *svc)
528{
529 async_sess_t *sess;
530 service_id_t dsid;
531
532 int rc = loc_service_get_id(svc, &dsid, 0);
533 if (rc != EOK) {
534 printf("%s: Input service %s not found\n", NAME, svc);
535 return rc;
536 }
537
538 sess = loc_service_connect(EXCHANGE_ATOMIC, dsid, 0);
539 if (sess == NULL) {
540 printf("%s: Unable to connect to input service %s\n", NAME,
541 svc);
542 return EIO;
543 }
544
545 rc = input_open(sess, &input_ev_ops, NULL, &input);
546 if (rc != EOK) {
547 async_hangup(sess);
548 printf("%s: Unable to communicate with service %s (%s)\n",
549 NAME, svc, str_error(rc));
550 return rc;
551 }
552
553 return EOK;
554}
555
556static async_sess_t *output_connect(const char *svc)
557{
558 async_sess_t *sess;
559 service_id_t dsid;
560
561 int rc = loc_service_get_id(svc, &dsid, 0);
562 if (rc == EOK) {
563 sess = loc_service_connect(EXCHANGE_SERIALIZE, dsid, 0);
564 if (sess == NULL) {
565 printf("%s: Unable to connect to output service %s\n",
566 NAME, svc);
567 return NULL;
568 }
569 } else
570 return NULL;
571
572 return sess;
573}
574
575static bool console_srv_init(char *input_svc, char *output_svc)
576{
577 /* Connect to input service */
578 int rc = input_connect(input_svc);
579 if (rc != EOK)
580 return false;
581
582 /* Connect to output service */
583 output_sess = output_connect(output_svc);
584 if (output_sess == NULL)
585 return false;
586
587 /* Register server */
588 async_set_fallback_port_handler(client_connection, NULL);
589 rc = loc_server_register(NAME);
590 if (rc != EOK) {
591 printf("%s: Unable to register server (%s)\n", NAME,
592 str_error(rc));
593 return false;
594 }
595
596 output_get_dimensions(output_sess, &cols, &rows);
597 output_set_style(output_sess, STYLE_NORMAL);
598
599 console_caps_t ccaps;
600 output_get_caps(output_sess, &ccaps);
601
602 /*
603 * Inititalize consoles only if there are
604 * actually some output devices.
605 */
606 if (ccaps != 0) {
607 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
608 consoles[i].index = i;
609 atomic_set(&consoles[i].refcnt, 0);
610 fibril_mutex_initialize(&consoles[i].mtx);
611 prodcons_initialize(&consoles[i].input_pc);
612 consoles[i].char_remains_len = 0;
613
614 consoles[i].cols = cols;
615 consoles[i].rows = rows;
616 consoles[i].ccaps = ccaps;
617 consoles[i].frontbuf =
618 chargrid_create(cols, rows, CHARGRID_FLAG_SHARED);
619
620 if (consoles[i].frontbuf == NULL) {
621 printf("%s: Unable to allocate frontbuffer %zu\n", NAME, i);
622 return false;
623 }
624
625 consoles[i].fbid = output_frontbuf_create(output_sess,
626 consoles[i].frontbuf);
627 if (consoles[i].fbid == 0) {
628 printf("%s: Unable to create frontbuffer %zu\n", NAME, i);
629 return false;
630 }
631
632 con_srvs_init(&consoles[i].srvs);
633 consoles[i].srvs.ops = &con_ops;
634 consoles[i].srvs.sarg = &consoles[i];
635
636 char vc[LOC_NAME_MAXLEN + 1];
637 snprintf(vc, LOC_NAME_MAXLEN, "%s/vc%zu", NAMESPACE, i);
638
639 if (loc_service_register(vc, &consoles[i].dsid) != EOK) {
640 printf("%s: Unable to register device %s\n", NAME, vc);
641 return false;
642 }
643 }
644
645 input_activate(input);
646 }
647
648 return true;
649}
650
651static void usage(char *name)
652{
653 printf("Usage: %s <input_dev> <output_dev>\n", name);
654}
655
656int main(int argc, char *argv[])
657{
658 if (argc < 3) {
659 usage(argv[0]);
660 return -1;
661 }
662
663 printf("%s: HelenOS Console service\n", NAME);
664
665 if (!console_srv_init(argv[1], argv[2]))
666 return -1;
667
668 printf("%s: Accepting connections\n", NAME);
669 task_retval(0);
670 async_manager();
671
672 /* Never reached */
673 return 0;
674}
675
676/** @}
677 */
Note: See TracBrowser for help on using the repository browser.