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