source: mainline/uspace/srv/hid/console/console.c@ 60e5a856

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

Rename 'kbd' server to 'input' server.

  • Property mode set to 100644
File size: 23.8 KB
Line 
1/*
2 * Copyright (c) 2006 Josef Cejka
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 <libc.h>
36#include <ipc/input.h>
37#include <io/keycode.h>
38#include <ipc/mouse.h>
39#include <ipc/fb.h>
40#include <ipc/services.h>
41#include <ns.h>
42#include <ns_obsolete.h>
43#include <errno.h>
44#include <str_error.h>
45#include <ipc/console.h>
46#include <unistd.h>
47#include <async.h>
48#include <async_obsolete.h>
49#include <adt/fifo.h>
50#include <sys/mman.h>
51#include <stdio.h>
52#include <str.h>
53#include <sysinfo.h>
54#include <event.h>
55#include <devmap.h>
56#include <devmap_obsolete.h>
57#include <fcntl.h>
58#include <vfs/vfs.h>
59#include <fibril_synch.h>
60#include <io/style.h>
61#include <io/screenbuffer.h>
62#include <inttypes.h>
63
64#include "console.h"
65#include "gcons.h"
66#include "keybuffer.h"
67
68// FIXME: remove this header
69#include <kernel/ipc/ipc_methods.h>
70
71#define NAME "console"
72#define NAMESPACE "term"
73
74/** Interval for checking for new keyboard (1/4s). */
75#define HOTPLUG_WATCH_INTERVAL (1000 * 250)
76
77/* Kernel defines 32 but does not export it. */
78#define MAX_IPC_OUTGOING_PHONES 128
79
80/** To allow proper phone closing. */
81static ipc_callid_t driver_phones[MAX_IPC_OUTGOING_PHONES] = { 0 };
82
83/** Phone to the keyboard driver. */
84static int kbd_phone;
85
86/** Phone to the mouse driver. */
87static int mouse_phone;
88
89/** Information about framebuffer */
90struct {
91 int phone; /**< Framebuffer phone */
92 sysarg_t cols; /**< Framebuffer columns */
93 sysarg_t rows; /**< Framebuffer rows */
94 sysarg_t color_cap; /**< Color capabilities (FB_CCAP_xxx) */
95} fb_info;
96
97typedef struct {
98 size_t index; /**< Console index */
99 size_t refcount; /**< Connection reference count */
100 devmap_handle_t devmap_handle; /**< Device handle */
101 keybuffer_t keybuffer; /**< Buffer for incoming keys. */
102 screenbuffer_t scr; /**< Screenbuffer for saving screen
103 contents and related settings. */
104} console_t;
105
106/** Array of data for virtual consoles */
107static console_t consoles[CONSOLE_COUNT];
108
109static console_t *active_console = &consoles[0];
110static console_t *prev_console = &consoles[0];
111static console_t *kernel_console = &consoles[KERNEL_CONSOLE];
112
113/** Pointer to memory shared with framebufer used for
114 faster virtual console switching */
115static keyfield_t *interbuffer = NULL;
116
117/** Information on row-span yet unsent to FB driver. */
118struct {
119 sysarg_t col; /**< Leftmost column of the span. */
120 sysarg_t row; /**< Row where the span lies. */
121 sysarg_t cnt; /**< Width of the span. */
122} fb_pending;
123
124static FIBRIL_MUTEX_INITIALIZE(input_mutex);
125static FIBRIL_CONDVAR_INITIALIZE(input_cv);
126
127static void curs_visibility(bool visible)
128{
129 async_obsolete_msg_1(fb_info.phone, FB_CURSOR_VISIBILITY, visible);
130}
131
132static void curs_hide_sync(void)
133{
134 async_obsolete_req_1_0(fb_info.phone, FB_CURSOR_VISIBILITY, false);
135}
136
137static void curs_goto(sysarg_t x, sysarg_t y)
138{
139 async_obsolete_msg_2(fb_info.phone, FB_CURSOR_GOTO, x, y);
140}
141
142static void screen_clear(void)
143{
144 async_obsolete_msg_0(fb_info.phone, FB_CLEAR);
145}
146
147static void screen_yield(void)
148{
149 async_obsolete_req_0_0(fb_info.phone, FB_SCREEN_YIELD);
150}
151
152static void screen_reclaim(void)
153{
154 async_obsolete_req_0_0(fb_info.phone, FB_SCREEN_RECLAIM);
155}
156
157static void kbd_yield(void)
158{
159 async_obsolete_req_0_0(kbd_phone, INPUT_YIELD);
160}
161
162static void kbd_reclaim(void)
163{
164 async_obsolete_req_0_0(kbd_phone, INPUT_RECLAIM);
165}
166
167static void set_style(uint8_t style)
168{
169 async_obsolete_msg_1(fb_info.phone, FB_SET_STYLE, style);
170}
171
172static void set_color(uint8_t fgcolor, uint8_t bgcolor, uint8_t flags)
173{
174 async_obsolete_msg_3(fb_info.phone, FB_SET_COLOR, fgcolor, bgcolor, flags);
175}
176
177static void set_rgb_color(uint32_t fgcolor, uint32_t bgcolor)
178{
179 async_obsolete_msg_2(fb_info.phone, FB_SET_RGB_COLOR, fgcolor, bgcolor);
180}
181
182static void set_attrs(attrs_t *attrs)
183{
184 switch (attrs->t) {
185 case at_style:
186 set_style(attrs->a.s.style);
187 break;
188 case at_idx:
189 set_color(attrs->a.i.fg_color, attrs->a.i.bg_color,
190 attrs->a.i.flags);
191 break;
192 case at_rgb:
193 set_rgb_color(attrs->a.r.fg_color, attrs->a.r.bg_color);
194 break;
195 }
196}
197
198static int ccap_fb_to_con(sysarg_t ccap_fb, sysarg_t *ccap_con)
199{
200 switch (ccap_fb) {
201 case FB_CCAP_NONE:
202 *ccap_con = CONSOLE_CCAP_NONE;
203 break;
204 case FB_CCAP_STYLE:
205 *ccap_con = CONSOLE_CCAP_STYLE;
206 break;
207 case FB_CCAP_INDEXED:
208 *ccap_con = CONSOLE_CCAP_INDEXED;
209 break;
210 case FB_CCAP_RGB:
211 *ccap_con = CONSOLE_CCAP_RGB;
212 break;
213 default:
214 return EINVAL;
215 }
216
217 return EOK;
218}
219
220/** Send an area of screenbuffer to the FB driver. */
221static void fb_update_area(console_t *cons, sysarg_t x0, sysarg_t y0, sysarg_t width, sysarg_t height)
222{
223 if (interbuffer) {
224 sysarg_t x;
225 sysarg_t y;
226
227 for (y = 0; y < height; y++) {
228 for (x = 0; x < width; x++) {
229 interbuffer[y * width + x] =
230 *get_field_at(&cons->scr, x0 + x, y0 + y);
231 }
232 }
233
234 async_obsolete_req_4_0(fb_info.phone, FB_DRAW_TEXT_DATA,
235 x0, y0, width, height);
236 }
237}
238
239/** Flush pending cells to FB. */
240static void fb_pending_flush(void)
241{
242 if (fb_pending.cnt > 0) {
243 fb_update_area(active_console, fb_pending.col,
244 fb_pending.row, fb_pending.cnt, 1);
245 fb_pending.cnt = 0;
246 }
247}
248
249/** Mark a character cell as changed.
250 *
251 * This adds the cell to the pending rowspan if possible. Otherwise
252 * the old span is flushed first.
253 *
254 */
255static void cell_mark_changed(sysarg_t col, sysarg_t row)
256{
257 if (fb_pending.cnt != 0) {
258 if ((col != fb_pending.col + fb_pending.cnt)
259 || (row != fb_pending.row)) {
260 fb_pending_flush();
261 }
262 }
263
264 if (fb_pending.cnt == 0) {
265 fb_pending.col = col;
266 fb_pending.row = row;
267 }
268
269 fb_pending.cnt++;
270}
271
272/** Print a character to the active VC with buffering. */
273static void fb_putchar(wchar_t c, sysarg_t col, sysarg_t row)
274{
275 async_obsolete_msg_3(fb_info.phone, FB_PUTCHAR, c, col, row);
276}
277
278/** Process a character from the client (TTY emulation). */
279static void write_char(console_t *cons, wchar_t ch)
280{
281 bool flush_cursor = false;
282
283 switch (ch) {
284 case '\n':
285 fb_pending_flush();
286 flush_cursor = true;
287 cons->scr.position_y++;
288 cons->scr.position_x = 0;
289 break;
290 case '\r':
291 break;
292 case '\t':
293 cons->scr.position_x += 8;
294 cons->scr.position_x -= cons->scr.position_x % 8;
295 break;
296 case '\b':
297 if (cons->scr.position_x == 0)
298 break;
299 cons->scr.position_x--;
300 if (cons == active_console)
301 cell_mark_changed(cons->scr.position_x, cons->scr.position_y);
302 screenbuffer_putchar(&cons->scr, ' ');
303 break;
304 default:
305 if (cons == active_console)
306 cell_mark_changed(cons->scr.position_x, cons->scr.position_y);
307
308 screenbuffer_putchar(&cons->scr, ch);
309 cons->scr.position_x++;
310 }
311
312 if (cons->scr.position_x >= cons->scr.size_x) {
313 flush_cursor = true;
314 cons->scr.position_y++;
315 }
316
317 if (cons->scr.position_y >= cons->scr.size_y) {
318 fb_pending_flush();
319 cons->scr.position_y = cons->scr.size_y - 1;
320 screenbuffer_clear_line(&cons->scr, cons->scr.top_line);
321 cons->scr.top_line = (cons->scr.top_line + 1) % cons->scr.size_y;
322
323 if (cons == active_console)
324 async_obsolete_msg_1(fb_info.phone, FB_SCROLL, 1);
325 }
326
327 if (cons == active_console && flush_cursor)
328 curs_goto(cons->scr.position_x, cons->scr.position_y);
329 cons->scr.position_x = cons->scr.position_x % cons->scr.size_x;
330}
331
332/** Switch to new console */
333static void change_console(console_t *cons)
334{
335 if (cons == active_console)
336 return;
337
338 fb_pending_flush();
339
340 if (cons == kernel_console) {
341 async_obsolete_serialize_start();
342 curs_hide_sync();
343 gcons_in_kernel();
344 screen_yield();
345 kbd_yield();
346 async_obsolete_serialize_end();
347
348 if (__SYSCALL0(SYS_DEBUG_ENABLE_CONSOLE)) {
349 prev_console = active_console;
350 active_console = kernel_console;
351 } else
352 cons = active_console;
353 }
354
355 if (cons != kernel_console) {
356 async_obsolete_serialize_start();
357
358 if (active_console == kernel_console) {
359 screen_reclaim();
360 kbd_reclaim();
361 gcons_redraw_console();
362 }
363
364 active_console = cons;
365 gcons_change_console(cons->index);
366
367 set_attrs(&cons->scr.attrs);
368 curs_visibility(false);
369
370 sysarg_t x;
371 sysarg_t y;
372 int rc = 0;
373
374 if (interbuffer) {
375 for (y = 0; y < cons->scr.size_y; y++) {
376 for (x = 0; x < cons->scr.size_x; x++) {
377 interbuffer[y * cons->scr.size_x + x] =
378 *get_field_at(&cons->scr, x, y);
379 }
380 }
381
382 /* This call can preempt, but we are already at the end */
383 rc = async_obsolete_req_4_0(fb_info.phone, FB_DRAW_TEXT_DATA,
384 0, 0, cons->scr.size_x,
385 cons->scr.size_y);
386 }
387
388 if ((!interbuffer) || (rc != 0)) {
389 set_attrs(&cons->scr.attrs);
390 screen_clear();
391
392 for (y = 0; y < cons->scr.size_y; y++)
393 for (x = 0; x < cons->scr.size_x; x++) {
394 keyfield_t *field = get_field_at(&cons->scr, x, y);
395
396 if (!attrs_same(cons->scr.attrs, field->attrs))
397 set_attrs(&field->attrs);
398
399 cons->scr.attrs = field->attrs;
400 if ((field->character == ' ') &&
401 (attrs_same(field->attrs, cons->scr.attrs)))
402 continue;
403
404 fb_putchar(field->character, x, y);
405 }
406 }
407
408 curs_goto(cons->scr.position_x, cons->scr.position_y);
409 curs_visibility(cons->scr.is_cursor_visible);
410
411 async_obsolete_serialize_end();
412 }
413}
414
415static void close_driver_phone(ipc_callid_t hash)
416{
417 int i;
418 for (i = 0; i < MAX_IPC_OUTGOING_PHONES; i++) {
419 if (driver_phones[i] == hash) {
420 printf("Device %" PRIxn " gone.\n", hash);
421 driver_phones[i] = 0;
422 async_obsolete_hangup(i);
423 return;
424 }
425 }
426}
427
428/** Handler for keyboard */
429static void keyboard_events(ipc_callid_t iid, ipc_call_t *icall)
430{
431 /* Ignore parameters, the connection is already opened */
432 while (true) {
433 ipc_call_t call;
434 ipc_callid_t callid = async_get_call(&call);
435
436 int retval;
437 kbd_event_t ev;
438
439 if (!IPC_GET_IMETHOD(call)) {
440 /* TODO: Handle hangup */
441 close_driver_phone(iid);
442 return;
443 }
444
445 switch (IPC_GET_IMETHOD(call)) {
446 case INPUT_EVENT:
447 /* Got event from keyboard driver. */
448 retval = 0;
449 ev.type = IPC_GET_ARG1(call);
450 ev.key = IPC_GET_ARG2(call);
451 ev.mods = IPC_GET_ARG3(call);
452 ev.c = IPC_GET_ARG4(call);
453
454 if ((ev.key >= KC_F1) && (ev.key < KC_F1 +
455 CONSOLE_COUNT) && ((ev.mods & KM_CTRL) == 0)) {
456 if (ev.key == KC_F1 + KERNEL_CONSOLE)
457 change_console(kernel_console);
458 else
459 change_console(&consoles[ev.key - KC_F1]);
460 break;
461 }
462
463 fibril_mutex_lock(&input_mutex);
464 keybuffer_push(&active_console->keybuffer, &ev);
465 fibril_condvar_broadcast(&input_cv);
466 fibril_mutex_unlock(&input_mutex);
467 break;
468 default:
469 retval = ENOENT;
470 }
471 async_answer_0(callid, retval);
472 }
473}
474
475/** Handler for mouse events */
476static void mouse_events(ipc_callid_t iid, ipc_call_t *icall)
477{
478 /* Ignore parameters, the connection is already opened */
479 while (true) {
480 ipc_call_t call;
481 ipc_callid_t callid = async_get_call(&call);
482
483 int retval;
484
485 if (!IPC_GET_IMETHOD(call)) {
486 /* TODO: Handle hangup */
487 close_driver_phone(iid);
488 return;
489 }
490
491 switch (IPC_GET_IMETHOD(call)) {
492 case MEVENT_BUTTON:
493 if (IPC_GET_ARG1(call) == 1) {
494 int newcon = gcons_mouse_btn((bool) IPC_GET_ARG2(call));
495 if (newcon != -1)
496 change_console(&consoles[newcon]);
497 }
498 retval = 0;
499 break;
500 case MEVENT_MOVE:
501 gcons_mouse_move((int) IPC_GET_ARG1(call),
502 (int) IPC_GET_ARG2(call));
503 retval = 0;
504 break;
505 default:
506 retval = ENOENT;
507 }
508
509 async_answer_0(callid, retval);
510 }
511}
512
513static void cons_write(console_t *cons, ipc_callid_t rid, ipc_call_t *request)
514{
515 void *buf;
516 size_t size;
517 int rc = async_data_write_accept(&buf, false, 0, 0, 0, &size);
518
519 if (rc != EOK) {
520 async_answer_0(rid, rc);
521 return;
522 }
523
524 async_obsolete_serialize_start();
525
526 size_t off = 0;
527 while (off < size) {
528 wchar_t ch = str_decode(buf, &off, size);
529 write_char(cons, ch);
530 }
531
532 async_obsolete_serialize_end();
533
534 gcons_notify_char(cons->index);
535 async_answer_1(rid, EOK, size);
536
537 free(buf);
538}
539
540static void cons_read(console_t *cons, ipc_callid_t rid, ipc_call_t *request)
541{
542 ipc_callid_t callid;
543 size_t size;
544 if (!async_data_read_receive(&callid, &size)) {
545 async_answer_0(callid, EINVAL);
546 async_answer_0(rid, EINVAL);
547 return;
548 }
549
550 char *buf = (char *) malloc(size);
551 if (buf == NULL) {
552 async_answer_0(callid, ENOMEM);
553 async_answer_0(rid, ENOMEM);
554 return;
555 }
556
557 size_t pos = 0;
558 kbd_event_t ev;
559 fibril_mutex_lock(&input_mutex);
560
561recheck:
562 while ((keybuffer_pop(&cons->keybuffer, &ev)) && (pos < size)) {
563 if (ev.type == KEY_PRESS) {
564 buf[pos] = ev.c;
565 pos++;
566 }
567 }
568
569 if (pos == size) {
570 (void) async_data_read_finalize(callid, buf, size);
571 async_answer_1(rid, EOK, size);
572 free(buf);
573 } else {
574 fibril_condvar_wait(&input_cv, &input_mutex);
575 goto recheck;
576 }
577
578 fibril_mutex_unlock(&input_mutex);
579}
580
581static void cons_get_event(console_t *cons, ipc_callid_t rid, ipc_call_t *request)
582{
583 kbd_event_t ev;
584
585 fibril_mutex_lock(&input_mutex);
586
587recheck:
588 if (keybuffer_pop(&cons->keybuffer, &ev)) {
589 async_answer_4(rid, EOK, ev.type, ev.key, ev.mods, ev.c);
590 } else {
591 fibril_condvar_wait(&input_cv, &input_mutex);
592 goto recheck;
593 }
594
595 fibril_mutex_unlock(&input_mutex);
596}
597
598/** Default thread for new connections */
599static void client_connection(ipc_callid_t iid, ipc_call_t *icall)
600{
601 console_t *cons = NULL;
602
603 size_t i;
604 for (i = 0; i < CONSOLE_COUNT; i++) {
605 if (i == KERNEL_CONSOLE)
606 continue;
607
608 if (consoles[i].devmap_handle == (devmap_handle_t) IPC_GET_ARG1(*icall)) {
609 cons = &consoles[i];
610 break;
611 }
612 }
613
614 if (cons == NULL) {
615 async_answer_0(iid, ENOENT);
616 return;
617 }
618
619 ipc_callid_t callid;
620 ipc_call_t call;
621 sysarg_t arg1;
622 sysarg_t arg2;
623 sysarg_t arg3;
624
625 int rc;
626
627 async_obsolete_serialize_start();
628 if (cons->refcount == 0)
629 gcons_notify_connect(cons->index);
630
631 cons->refcount++;
632
633 /* Accept the connection */
634 async_answer_0(iid, EOK);
635
636 while (true) {
637 async_obsolete_serialize_end();
638 callid = async_get_call(&call);
639 async_obsolete_serialize_start();
640
641 arg1 = 0;
642 arg2 = 0;
643 arg3 = 0;
644
645 if (!IPC_GET_IMETHOD(call)) {
646 cons->refcount--;
647 if (cons->refcount == 0)
648 gcons_notify_disconnect(cons->index);
649 return;
650 }
651
652 switch (IPC_GET_IMETHOD(call)) {
653 case VFS_OUT_READ:
654 async_obsolete_serialize_end();
655 cons_read(cons, callid, &call);
656 async_obsolete_serialize_start();
657 continue;
658 case VFS_OUT_WRITE:
659 async_obsolete_serialize_end();
660 cons_write(cons, callid, &call);
661 async_obsolete_serialize_start();
662 continue;
663 case VFS_OUT_SYNC:
664 fb_pending_flush();
665 if (cons == active_console) {
666 async_obsolete_req_0_0(fb_info.phone, FB_FLUSH);
667 curs_goto(cons->scr.position_x, cons->scr.position_y);
668 }
669 break;
670 case CONSOLE_CLEAR:
671 /* Send message to fb */
672 if (cons == active_console)
673 async_obsolete_msg_0(fb_info.phone, FB_CLEAR);
674
675 screenbuffer_clear(&cons->scr);
676
677 break;
678 case CONSOLE_GOTO:
679 screenbuffer_goto(&cons->scr,
680 IPC_GET_ARG1(call), IPC_GET_ARG2(call));
681 if (cons == active_console)
682 curs_goto(IPC_GET_ARG1(call),
683 IPC_GET_ARG2(call));
684 break;
685 case CONSOLE_GET_POS:
686 arg1 = cons->scr.position_x;
687 arg2 = cons->scr.position_y;
688 break;
689 case CONSOLE_GET_SIZE:
690 arg1 = fb_info.cols;
691 arg2 = fb_info.rows;
692 break;
693 case CONSOLE_GET_COLOR_CAP:
694 rc = ccap_fb_to_con(fb_info.color_cap, &arg1);
695 if (rc != EOK) {
696 async_answer_0(callid, rc);
697 continue;
698 }
699 break;
700 case CONSOLE_SET_STYLE:
701 fb_pending_flush();
702 arg1 = IPC_GET_ARG1(call);
703 screenbuffer_set_style(&cons->scr, arg1);
704 if (cons == active_console)
705 set_style(arg1);
706 break;
707 case CONSOLE_SET_COLOR:
708 fb_pending_flush();
709 arg1 = IPC_GET_ARG1(call);
710 arg2 = IPC_GET_ARG2(call);
711 arg3 = IPC_GET_ARG3(call);
712 screenbuffer_set_color(&cons->scr, arg1, arg2, arg3);
713 if (cons == active_console)
714 set_color(arg1, arg2, arg3);
715 break;
716 case CONSOLE_SET_RGB_COLOR:
717 fb_pending_flush();
718 arg1 = IPC_GET_ARG1(call);
719 arg2 = IPC_GET_ARG2(call);
720 screenbuffer_set_rgb_color(&cons->scr, arg1, arg2);
721 if (cons == active_console)
722 set_rgb_color(arg1, arg2);
723 break;
724 case CONSOLE_CURSOR_VISIBILITY:
725 fb_pending_flush();
726 arg1 = IPC_GET_ARG1(call);
727 cons->scr.is_cursor_visible = arg1;
728 if (cons == active_console)
729 curs_visibility(arg1);
730 break;
731 case CONSOLE_GET_EVENT:
732 async_obsolete_serialize_end();
733 cons_get_event(cons, callid, &call);
734 async_obsolete_serialize_start();
735 continue;
736 case CONSOLE_KCON_ENABLE:
737 change_console(kernel_console);
738 break;
739 }
740 async_answer_3(callid, EOK, arg1, arg2, arg3);
741 }
742}
743
744static void interrupt_received(ipc_callid_t callid, ipc_call_t *call)
745{
746 change_console(prev_console);
747}
748
749static int async_connect_to_me_hack(int phone, sysarg_t arg1, sysarg_t arg2,
750 sysarg_t arg3, async_client_conn_t client_receiver, ipc_callid_t *hash)
751{
752 sysarg_t task_hash;
753 sysarg_t phone_hash;
754 int rc = async_obsolete_req_3_5(phone, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
755 NULL, NULL, NULL, &task_hash, &phone_hash);
756 if (rc != EOK)
757 return rc;
758
759 if (client_receiver != NULL)
760 async_new_connection(task_hash, phone_hash, phone_hash, NULL,
761 client_receiver);
762
763 if (hash != NULL)
764 *hash = phone_hash;
765
766 return EOK;
767}
768
769static int connect_keyboard_or_mouse(const char *devname,
770 async_client_conn_t handler, const char *dev)
771{
772 int phone;
773 devmap_handle_t handle;
774
775 int rc = devmap_device_get_handle(dev, &handle, 0);
776 if (rc == EOK) {
777 phone = devmap_obsolete_device_connect(handle, 0);
778 if (phone < 0) {
779 printf("%s: Failed to connect to input device\n", NAME);
780 return phone;
781 }
782 } else
783 return rc;
784
785 /* NB: The callback connection is slotted for removal */
786 ipc_callid_t hash;
787 rc = async_connect_to_me_hack(phone, SERVICE_CONSOLE, 0, phone,
788 handler, &hash);
789 if (rc != EOK) {
790 async_obsolete_hangup(phone);
791 printf("%s: Failed to create callback from input device (%s).\n",
792 NAME, str_error(rc));
793 return rc;
794 }
795
796 driver_phones[phone] = hash;
797 printf("%s: found %s \"%s\" (%" PRIxn ").\n", NAME, devname, dev, hash);
798 return phone;
799}
800
801static int connect_keyboard(const char *dev)
802{
803 return connect_keyboard_or_mouse("keyboard", keyboard_events, dev);
804}
805
806static int connect_mouse(const char *dev)
807{
808 return connect_keyboard_or_mouse("mouse", mouse_events, dev);
809}
810
811struct hid_class_info {
812 char *classname;
813 int (*connection_func)(const char *);
814};
815
816/** Periodically check for new keyboards in /dev/class/.
817 *
818 * @param arg Class name.
819 *
820 * @return This function should never exit.
821 *
822 */
823static int check_new_device_fibril(void *arg)
824{
825 struct hid_class_info *dev_info = (struct hid_class_info *) arg;
826
827 size_t index = 1;
828
829 while (true) {
830 async_usleep(HOTPLUG_WATCH_INTERVAL);
831
832 char *dev;
833 int rc = asprintf(&dev, "class/%s\\%zu",
834 dev_info->classname, index);
835 if (rc < 0)
836 continue;
837
838 rc = dev_info->connection_func(dev);
839 if (rc > 0) {
840 /* We do not allow unplug. */
841 index++;
842 }
843
844 free(dev);
845 }
846
847 return EOK;
848}
849
850/** Start a fibril monitoring hot-plugged keyboards.
851 */
852static void check_new_devices_in_background(int (*connection_func)(const char *),
853 const char *classname)
854{
855 struct hid_class_info *dev_info = malloc(sizeof(struct hid_class_info));
856 if (dev_info == NULL) {
857 printf("%s: Out of memory, no hot-plug support.\n", NAME);
858 return;
859 }
860
861 int rc = asprintf(&dev_info->classname, "%s", classname);
862 if (rc < 0) {
863 printf("%s: Failed to format classname: %s.\n", NAME,
864 str_error(rc));
865 return;
866 }
867
868 dev_info->connection_func = connection_func;
869
870 fid_t fid = fibril_create(check_new_device_fibril, (void *) dev_info);
871 if (!fid) {
872 printf("%s: Failed to create hot-plug fibril for %s.\n", NAME,
873 classname);
874 return;
875 }
876
877 fibril_add_ready(fid);
878}
879
880static bool console_srv_init(char *kdev)
881{
882 /* Connect to input device */
883 kbd_phone = connect_keyboard(kdev);
884 if (kbd_phone < 0)
885 return false;
886
887 mouse_phone = connect_mouse("hid_in/mouse");
888 if (mouse_phone < 0) {
889 printf("%s: Failed to connect to mouse device %s\n", NAME,
890 str_error(mouse_phone));
891 }
892
893 /* Connect to framebuffer driver */
894 fb_info.phone = service_obsolete_connect_blocking(SERVICE_VIDEO, 0, 0);
895 if (fb_info.phone < 0) {
896 printf("%s: Failed to connect to video service\n", NAME);
897 return false;
898 }
899
900 /* Register driver */
901 int rc = devmap_driver_register(NAME, client_connection);
902 if (rc < 0) {
903 printf("%s: Unable to register driver (%d)\n", NAME, rc);
904 return false;
905 }
906
907 /* Initialize gcons */
908 gcons_init(fb_info.phone);
909
910 /* Synchronize, the gcons could put something in queue */
911 async_obsolete_req_0_0(fb_info.phone, FB_FLUSH);
912 async_obsolete_req_0_2(fb_info.phone, FB_GET_CSIZE, &fb_info.cols, &fb_info.rows);
913 async_obsolete_req_0_1(fb_info.phone, FB_GET_COLOR_CAP, &fb_info.color_cap);
914
915 /* Set up shared memory buffer. */
916 size_t ib_size = sizeof(keyfield_t) * fb_info.cols * fb_info.rows;
917 interbuffer = as_get_mappable_page(ib_size);
918
919 if (as_area_create(interbuffer, ib_size, AS_AREA_READ |
920 AS_AREA_WRITE | AS_AREA_CACHEABLE) != interbuffer)
921 interbuffer = NULL;
922
923 if (interbuffer) {
924 if (async_obsolete_share_out_start(fb_info.phone, interbuffer,
925 AS_AREA_READ) != EOK) {
926 as_area_destroy(interbuffer);
927 interbuffer = NULL;
928 }
929 }
930
931 fb_pending.cnt = 0;
932
933 /* Inititalize consoles */
934 size_t i;
935 for (i = 0; i < CONSOLE_COUNT; i++) {
936 if (i != KERNEL_CONSOLE) {
937 if (screenbuffer_init(&consoles[i].scr,
938 fb_info.cols, fb_info.rows) == NULL) {
939 printf("%s: Unable to allocate screen buffer %zu\n", NAME, i);
940 return false;
941 }
942 screenbuffer_clear(&consoles[i].scr);
943 keybuffer_init(&consoles[i].keybuffer);
944 consoles[i].index = i;
945 consoles[i].refcount = 0;
946
947 char vc[DEVMAP_NAME_MAXLEN + 1];
948 snprintf(vc, DEVMAP_NAME_MAXLEN, "%s/vc%zu", NAMESPACE, i);
949
950 if (devmap_device_register(vc, &consoles[i].devmap_handle) != EOK) {
951 printf("%s: Unable to register device %s\n", NAME, vc);
952 return false;
953 }
954 }
955 }
956
957 /* Disable kernel output to the console */
958 __SYSCALL0(SYS_DEBUG_DISABLE_CONSOLE);
959
960 /* Initialize the screen */
961 async_obsolete_serialize_start();
962 gcons_redraw_console();
963 set_style(STYLE_NORMAL);
964 screen_clear();
965 curs_goto(0, 0);
966 curs_visibility(active_console->scr.is_cursor_visible);
967 async_obsolete_serialize_end();
968
969 /* Receive kernel notifications */
970 async_set_interrupt_received(interrupt_received);
971 if (event_subscribe(EVENT_KCONSOLE, 0) != EOK)
972 printf("%s: Error registering kconsole notifications\n", NAME);
973
974 /* Start fibril for checking on hot-plugged keyboards. */
975 check_new_devices_in_background(connect_mouse, "mouse");
976
977 return true;
978}
979
980static void usage(void)
981{
982 printf("Usage: console <input>\n");
983}
984
985int main(int argc, char *argv[])
986{
987 if (argc < 2) {
988 usage();
989 return -1;
990 }
991
992 printf(NAME ": HelenOS Console service\n");
993
994 if (!console_srv_init(argv[1]))
995 return -1;
996
997 printf(NAME ": Accepting connections\n");
998 async_manager();
999
1000 return 0;
1001}
1002
1003/** @}
1004 */
Note: See TracBrowser for help on using the repository browser.