2
* Copyright (c) 2006 Josef Cejka
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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.
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.
29
/** @addtogroup console
39
#include <io/keycode.h>
41
#include <ipc/services.h>
43
#include <keybuffer.h>
44
#include <ipc/console.h>
54
#include <fibril_sync.h>
58
#include "screenbuffer.h"
60
#define NAME "console"
62
#define MAX_DEVICE_NAME 32
64
/** Phone to the keyboard driver. */
67
/** Information about framebuffer */
69
int phone; /**< Framebuffer phone */
70
ipcarg_t cols; /**< Framebuffer columns */
71
ipcarg_t rows; /**< Framebuffer rows */
72
int color_cap; /**< Color capabilities (FB_CCAP_xxx) */
76
size_t index; /**< Console index */
77
size_t refcount; /**< Connection reference count */
78
dev_handle_t dev_handle; /**< Device handle */
79
keybuffer_t keybuffer; /**< Buffer for incoming keys. */
80
screenbuffer_t scr; /**< Screenbuffer for saving screen
81
contents and related settings. */
84
/** Array of data for virtual consoles */
85
static console_t consoles[CONSOLE_COUNT];
87
static console_t *active_console = &consoles[0];
88
static console_t *prev_console = &consoles[0];
89
static console_t *kernel_console = &consoles[KERNEL_CONSOLE];
91
/** Pointer to memory shared with framebufer used for
92
faster virtual console switching */
93
static keyfield_t *interbuffer = NULL;
95
/** Information on row-span yet unsent to FB driver. */
97
size_t col; /**< Leftmost column of the span. */
98
size_t row; /**< Row where the span lies. */
99
size_t cnt; /**< Width of the span. */
102
static FIBRIL_MUTEX_INITIALIZE(input_mutex);
103
static FIBRIL_CONDVAR_INITIALIZE(input_cv);
105
static void curs_visibility(bool visible)
107
async_msg_1(fb_info.phone, FB_CURSOR_VISIBILITY, visible);
110
static void curs_hide_sync(void)
112
ipc_call_sync_1_0(fb_info.phone, FB_CURSOR_VISIBILITY, false);
115
static void curs_goto(size_t x, size_t y)
117
async_msg_2(fb_info.phone, FB_CURSOR_GOTO, x, y);
120
static void screen_clear(void)
122
async_msg_0(fb_info.phone, FB_CLEAR);
125
static void screen_yield(void)
127
ipc_call_sync_0_0(fb_info.phone, FB_SCREEN_YIELD);
130
static void screen_reclaim(void)
132
ipc_call_sync_0_0(fb_info.phone, FB_SCREEN_RECLAIM);
135
static void kbd_yield(void)
137
ipc_call_sync_0_0(kbd_phone, KBD_YIELD);
140
static void kbd_reclaim(void)
142
ipc_call_sync_0_0(kbd_phone, KBD_RECLAIM);
145
static void set_style(int style)
147
async_msg_1(fb_info.phone, FB_SET_STYLE, style);
150
static void set_color(int fgcolor, int bgcolor, int flags)
152
async_msg_3(fb_info.phone, FB_SET_COLOR, fgcolor, bgcolor, flags);
155
static void set_rgb_color(int fgcolor, int bgcolor)
157
async_msg_2(fb_info.phone, FB_SET_RGB_COLOR, fgcolor, bgcolor);
160
static void set_attrs(attrs_t *attrs)
164
set_style(attrs->a.s.style);
167
set_color(attrs->a.i.fg_color, attrs->a.i.bg_color,
171
set_rgb_color(attrs->a.r.fg_color, attrs->a.r.bg_color);
176
static int ccap_fb_to_con(int ccap_fb, int *ccap_con)
179
case FB_CCAP_NONE: *ccap_con = CONSOLE_CCAP_NONE; break;
180
case FB_CCAP_STYLE: *ccap_con = CONSOLE_CCAP_STYLE; break;
181
case FB_CCAP_INDEXED: *ccap_con = CONSOLE_CCAP_INDEXED; break;
182
case FB_CCAP_RGB: *ccap_con = CONSOLE_CCAP_RGB; break;
183
default: return EINVAL;
189
/** Send an area of screenbuffer to the FB driver. */
190
static void fb_update_area(console_t *cons, ipcarg_t x0, ipcarg_t y0, ipcarg_t width, ipcarg_t height)
196
for (y = 0; y < height; y++) {
197
for (x = 0; x < width; x++) {
198
interbuffer[y * width + x] =
199
*get_field_at(&cons->scr, x0 + x, y0 + y);
203
async_req_4_0(fb_info.phone, FB_DRAW_TEXT_DATA,
204
x0, y0, width, height);
208
/** Flush pending cells to FB. */
209
static void fb_pending_flush(void)
211
if (fb_pending.cnt > 0) {
212
fb_update_area(active_console, fb_pending.col,
213
fb_pending.row, fb_pending.cnt, 1);
218
/** Mark a character cell as changed.
220
* This adds the cell to the pending rowspan if possible. Otherwise
221
* the old span is flushed first.
224
static void cell_mark_changed(size_t col, size_t row)
226
if (fb_pending.cnt != 0) {
227
if ((col != fb_pending.col + fb_pending.cnt)
228
|| (row != fb_pending.row)) {
233
if (fb_pending.cnt == 0) {
234
fb_pending.col = col;
235
fb_pending.row = row;
241
/** Print a character to the active VC with buffering. */
242
static void fb_putchar(wchar_t c, ipcarg_t col, ipcarg_t row)
244
async_msg_3(fb_info.phone, FB_PUTCHAR, c, col, row);
247
/** Process a character from the client (TTY emulation). */
248
static void write_char(console_t *cons, wchar_t ch)
250
bool flush_cursor = false;
256
cons->scr.position_y++;
257
cons->scr.position_x = 0;
262
cons->scr.position_x += 8;
263
cons->scr.position_x -= cons->scr.position_x % 8;
266
if (cons->scr.position_x == 0)
268
cons->scr.position_x--;
269
if (cons == active_console)
270
cell_mark_changed(cons->scr.position_x, cons->scr.position_y);
271
screenbuffer_putchar(&cons->scr, ' ');
274
if (cons == active_console)
275
cell_mark_changed(cons->scr.position_x, cons->scr.position_y);
277
screenbuffer_putchar(&cons->scr, ch);
278
cons->scr.position_x++;
281
if (cons->scr.position_x >= cons->scr.size_x) {
283
cons->scr.position_y++;
286
if (cons->scr.position_y >= cons->scr.size_y) {
288
cons->scr.position_y = cons->scr.size_y - 1;
289
screenbuffer_clear_line(&cons->scr, cons->scr.top_line);
290
cons->scr.top_line = (cons->scr.top_line + 1) % cons->scr.size_y;
292
if (cons == active_console)
293
async_msg_1(fb_info.phone, FB_SCROLL, 1);
296
if (cons == active_console && flush_cursor)
297
curs_goto(cons->scr.position_x, cons->scr.position_y);
298
cons->scr.position_x = cons->scr.position_x % cons->scr.size_x;
301
/** Switch to new console */
302
static void change_console(console_t *cons)
304
if (cons == active_console)
309
if (cons == kernel_console) {
310
async_serialize_start();
315
async_serialize_end();
317
if (__SYSCALL0(SYS_DEBUG_ENABLE_CONSOLE)) {
318
prev_console = active_console;
319
active_console = kernel_console;
321
cons = active_console;
324
if (cons != kernel_console) {
329
async_serialize_start();
331
if (active_console == kernel_console) {
334
gcons_redraw_console();
337
active_console = cons;
338
gcons_change_console(cons->index);
340
set_attrs(&cons->scr.attrs);
341
curs_visibility(false);
343
for (y = 0; y < cons->scr.size_y; y++) {
344
for (x = 0; x < cons->scr.size_x; x++) {
345
interbuffer[y * cons->scr.size_x + x] =
346
*get_field_at(&cons->scr, x, y);
350
/* This call can preempt, but we are already at the end */
351
rc = async_req_4_0(fb_info.phone, FB_DRAW_TEXT_DATA,
352
0, 0, cons->scr.size_x,
356
if ((!interbuffer) || (rc != 0)) {
357
set_attrs(&cons->scr.attrs);
360
for (y = 0; y < cons->scr.size_y; y++)
361
for (x = 0; x < cons->scr.size_x; x++) {
362
keyfield_t *field = get_field_at(&cons->scr, x, y);
364
if (!attrs_same(cons->scr.attrs, field->attrs))
365
set_attrs(&field->attrs);
367
cons->scr.attrs = field->attrs;
368
if ((field->character == ' ') &&
369
(attrs_same(field->attrs, cons->scr.attrs)))
372
fb_putchar(field->character, x, y);
376
curs_goto(cons->scr.position_x, cons->scr.position_y);
377
curs_visibility(cons->scr.is_cursor_visible);
379
async_serialize_end();
383
/** Handler for keyboard */
384
static void keyboard_events(ipc_callid_t iid, ipc_call_t *icall)
386
/* Ignore parameters, the connection is already opened */
390
ipc_callid_t callid = async_get_call(&call);
395
switch (IPC_GET_METHOD(call)) {
396
case IPC_M_PHONE_HUNGUP:
397
/* TODO: Handle hangup */
400
/* Got event from keyboard driver. */
402
ev.type = IPC_GET_ARG1(call);
403
ev.key = IPC_GET_ARG2(call);
404
ev.mods = IPC_GET_ARG3(call);
405
ev.c = IPC_GET_ARG4(call);
407
if ((ev.key >= KC_F1) && (ev.key < KC_F1 +
408
CONSOLE_COUNT) && ((ev.mods & KM_CTRL) == 0)) {
409
if (ev.key == KC_F1 + KERNEL_CONSOLE)
410
change_console(kernel_console);
412
change_console(&consoles[ev.key - KC_F1]);
416
fibril_mutex_lock(&input_mutex);
417
keybuffer_push(&active_console->keybuffer, &ev);
418
fibril_condvar_broadcast(&input_cv);
419
fibril_mutex_unlock(&input_mutex);
424
ipc_answer_0(callid, retval);
428
static void cons_write(console_t *cons, ipc_callid_t rid, ipc_call_t *request)
432
if (!ipc_data_write_receive(&callid, &size)) {
433
ipc_answer_0(callid, EINVAL);
434
ipc_answer_0(rid, EINVAL);
438
char *buf = (char *) malloc(size);
440
ipc_answer_0(callid, ENOMEM);
441
ipc_answer_0(rid, ENOMEM);
445
(void) ipc_data_write_finalize(callid, buf, size);
447
async_serialize_start();
451
wchar_t ch = str_decode(buf, &off, size);
452
write_char(cons, ch);
455
async_serialize_end();
457
gcons_notify_char(cons->index);
458
ipc_answer_1(rid, EOK, size);
463
static void cons_read(console_t *cons, ipc_callid_t rid, ipc_call_t *request)
467
if (!ipc_data_read_receive(&callid, &size)) {
468
ipc_answer_0(callid, EINVAL);
469
ipc_answer_0(rid, EINVAL);
473
char *buf = (char *) malloc(size);
475
ipc_answer_0(callid, ENOMEM);
476
ipc_answer_0(rid, ENOMEM);
482
fibril_mutex_lock(&input_mutex);
484
while ((keybuffer_pop(&cons->keybuffer, &ev)) && (pos < size)) {
485
if (ev.type == KEY_PRESS) {
492
(void) ipc_data_read_finalize(callid, buf, size);
493
ipc_answer_1(rid, EOK, size);
496
fibril_condvar_wait(&input_cv, &input_mutex);
499
fibril_mutex_unlock(&input_mutex);
502
static void cons_get_event(console_t *cons, ipc_callid_t rid, ipc_call_t *request)
506
fibril_mutex_lock(&input_mutex);
508
if (keybuffer_pop(&cons->keybuffer, &ev)) {
509
ipc_answer_4(rid, EOK, ev.type, ev.key, ev.mods, ev.c);
511
fibril_condvar_wait(&input_cv, &input_mutex);
514
fibril_mutex_unlock(&input_mutex);
517
/** Default thread for new connections */
518
static void client_connection(ipc_callid_t iid, ipc_call_t *icall)
520
console_t *cons = NULL;
523
for (i = 0; i < CONSOLE_COUNT; i++) {
524
if (i == KERNEL_CONSOLE)
527
if (consoles[i].dev_handle == (dev_handle_t) IPC_GET_ARG1(*icall)) {
534
ipc_answer_0(iid, ENOENT);
547
async_serialize_start();
548
if (cons->refcount == 0)
549
gcons_notify_connect(cons->index);
553
/* Accept the connection */
554
ipc_answer_0(iid, EOK);
557
async_serialize_end();
558
callid = async_get_call(&call);
559
async_serialize_start();
565
switch (IPC_GET_METHOD(call)) {
566
case IPC_M_PHONE_HUNGUP:
568
if (cons->refcount == 0)
569
gcons_notify_disconnect(cons->index);
572
async_serialize_end();
573
cons_read(cons, callid, &call);
574
async_serialize_start();
577
async_serialize_end();
578
cons_write(cons, callid, &call);
579
async_serialize_start();
583
if (cons == active_console) {
584
async_req_0_0(fb_info.phone, FB_FLUSH);
586
curs_goto(cons->scr.position_x, cons->scr.position_y);
590
/* Send message to fb */
591
if (cons == active_console)
592
async_msg_0(fb_info.phone, FB_CLEAR);
594
screenbuffer_clear(&cons->scr);
598
screenbuffer_goto(&cons->scr,
599
IPC_GET_ARG1(call), IPC_GET_ARG2(call));
600
if (cons == active_console)
601
curs_goto(IPC_GET_ARG1(call),
604
case CONSOLE_GET_SIZE:
608
case CONSOLE_GET_COLOR_CAP:
609
rc = ccap_fb_to_con(fb_info.color_cap, &cons_ccap);
611
ipc_answer_0(callid, rc);
616
case CONSOLE_SET_STYLE:
618
arg1 = IPC_GET_ARG1(call);
619
screenbuffer_set_style(&cons->scr, arg1);
620
if (cons == active_console)
623
case CONSOLE_SET_COLOR:
625
arg1 = IPC_GET_ARG1(call);
626
arg2 = IPC_GET_ARG2(call);
627
arg3 = IPC_GET_ARG3(call);
628
screenbuffer_set_color(&cons->scr, arg1, arg2, arg3);
629
if (cons == active_console)
630
set_color(arg1, arg2, arg3);
632
case CONSOLE_SET_RGB_COLOR:
634
arg1 = IPC_GET_ARG1(call);
635
arg2 = IPC_GET_ARG2(call);
636
screenbuffer_set_rgb_color(&cons->scr, arg1, arg2);
637
if (cons == active_console)
638
set_rgb_color(arg1, arg2);
640
case CONSOLE_CURSOR_VISIBILITY:
642
arg1 = IPC_GET_ARG1(call);
643
cons->scr.is_cursor_visible = arg1;
644
if (cons == active_console)
645
curs_visibility(arg1);
647
case CONSOLE_GET_EVENT:
648
async_serialize_end();
649
cons_get_event(cons, callid, &call);
650
async_serialize_start();
652
case CONSOLE_KCON_ENABLE:
653
change_console(kernel_console);
656
ipc_answer_3(callid, EOK, arg1, arg2, arg3);
660
static void interrupt_received(ipc_callid_t callid, ipc_call_t *call)
662
change_console(prev_console);
665
static bool console_init(void)
669
/* Connect to keyboard driver */
670
kbd_phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_KEYBOARD, 0, 0);
672
printf(NAME ": Failed to connect to keyboard service\n");
677
if (ipc_connect_to_me(kbd_phone, SERVICE_CONSOLE, 0, 0, &phonehash) != 0) {
678
printf(NAME ": Failed to create callback from keyboard service\n");
682
async_new_connection(phonehash, 0, NULL, keyboard_events);
684
/* Connect to framebuffer driver */
685
fb_info.phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_VIDEO, 0, 0);
686
if (fb_info.phone < 0) {
687
printf(NAME ": Failed to connect to video service\n");
691
/* Register driver */
692
int rc = devmap_driver_register(NAME, client_connection);
694
printf(NAME ": Unable to register driver (%d)\n", rc);
698
/* Initialize gcons */
699
gcons_init(fb_info.phone);
701
/* Synchronize, the gcons could put something in queue */
702
async_req_0_0(fb_info.phone, FB_FLUSH);
703
async_req_0_2(fb_info.phone, FB_GET_CSIZE, &fb_info.cols, &fb_info.rows);
704
async_req_0_1(fb_info.phone, FB_GET_COLOR_CAP, &color_cap);
705
fb_info.color_cap = color_cap;
707
/* Set up shared memory buffer. */
708
size_t ib_size = sizeof(keyfield_t) * fb_info.cols * fb_info.rows;
709
interbuffer = as_get_mappable_page(ib_size);
711
if (as_area_create(interbuffer, ib_size, AS_AREA_READ |
712
AS_AREA_WRITE | AS_AREA_CACHEABLE) != interbuffer)
716
if (ipc_share_out_start(fb_info.phone, interbuffer,
717
AS_AREA_READ) != EOK) {
718
as_area_destroy(interbuffer);
725
/* Inititalize consoles */
727
for (i = 0; i < CONSOLE_COUNT; i++) {
728
if (i != KERNEL_CONSOLE) {
729
if (screenbuffer_init(&consoles[i].scr,
730
fb_info.cols, fb_info.rows) == NULL) {
731
printf(NAME ": Unable to allocate screen buffer %u\n", i);
734
screenbuffer_clear(&consoles[i].scr);
735
keybuffer_init(&consoles[i].keybuffer);
736
consoles[i].index = i;
737
consoles[i].refcount = 0;
739
char vc[MAX_DEVICE_NAME];
740
snprintf(vc, MAX_DEVICE_NAME, "vc%u", i);
742
if (devmap_device_register(vc, &consoles[i].dev_handle) != EOK) {
743
devmap_hangup_phone(DEVMAP_DRIVER);
744
printf(NAME ": Unable to register device %s\n", vc);
750
/* Disable kernel output to the console */
751
__SYSCALL0(SYS_DEBUG_DISABLE_CONSOLE);
753
/* Initialize the screen */
754
async_serialize_start();
755
gcons_redraw_console();
756
set_rgb_color(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND);
759
curs_visibility(active_console->scr.is_cursor_visible);
760
async_serialize_end();
762
/* Receive kernel notifications */
763
if (event_subscribe(EVENT_KCONSOLE, 0) != EOK)
764
printf(NAME ": Error registering kconsole notifications\n");
766
async_set_interrupt_received(interrupt_received);
771
int main(int argc, char *argv[])
773
printf(NAME ": HelenOS Console service\n");
778
printf(NAME ": Accepting connections\n");