source: mainline/uspace/srv/hid/console/console.c@ 88ab6e9

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

unify interface API

  • introduce new interfaces
  • unify location service clients to always expect service ID as the second argument
  • remove obsolete methods that take explicit exchange management arguments (first phase)
  • use interfaces in device drivers, devman, location service, logger, inet
  • 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_ARG2(*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(dsid, INTERFACE_INPUT, 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(dsid, INTERFACE_OUTPUT, 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.