~galfy/helenos/bird-port-mainline

« back to all changes in this revision

Viewing changes to uspace/srv/console/console.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <fb.h>
 
37
#include <ipc/ipc.h>
 
38
#include <kbd.h>
 
39
#include <io/keycode.h>
 
40
#include <ipc/fb.h>
 
41
#include <ipc/services.h>
 
42
#include <errno.h>
 
43
#include <keybuffer.h>
 
44
#include <ipc/console.h>
 
45
#include <unistd.h>
 
46
#include <async.h>
 
47
#include <adt/fifo.h>
 
48
#include <sys/mman.h>
 
49
#include <stdio.h>
 
50
#include <string.h>
 
51
#include <sysinfo.h>
 
52
#include <event.h>
 
53
#include <devmap.h>
 
54
#include <fibril_sync.h>
 
55
 
 
56
#include "console.h"
 
57
#include "gcons.h"
 
58
#include "screenbuffer.h"
 
59
 
 
60
#define NAME  "console"
 
61
 
 
62
#define MAX_DEVICE_NAME  32
 
63
 
 
64
/** Phone to the keyboard driver. */
 
65
static int kbd_phone;
 
66
 
 
67
/** Information about framebuffer */
 
68
struct {
 
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) */
 
73
} fb_info;
 
74
 
 
75
typedef struct {
 
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. */
 
82
} console_t;
 
83
 
 
84
/** Array of data for virtual consoles */
 
85
static console_t consoles[CONSOLE_COUNT];
 
86
 
 
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];
 
90
 
 
91
/** Pointer to memory shared with framebufer used for
 
92
    faster virtual console switching */
 
93
static keyfield_t *interbuffer = NULL;
 
94
 
 
95
/** Information on row-span yet unsent to FB driver. */
 
96
struct {
 
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. */
 
100
} fb_pending;
 
101
 
 
102
static FIBRIL_MUTEX_INITIALIZE(input_mutex);
 
103
static FIBRIL_CONDVAR_INITIALIZE(input_cv);
 
104
 
 
105
static void curs_visibility(bool visible)
 
106
{
 
107
        async_msg_1(fb_info.phone, FB_CURSOR_VISIBILITY, visible); 
 
108
}
 
109
 
 
110
static void curs_hide_sync(void)
 
111
{
 
112
        ipc_call_sync_1_0(fb_info.phone, FB_CURSOR_VISIBILITY, false); 
 
113
}
 
114
 
 
115
static void curs_goto(size_t x, size_t y)
 
116
{
 
117
        async_msg_2(fb_info.phone, FB_CURSOR_GOTO, x, y);
 
118
}
 
119
 
 
120
static void screen_clear(void)
 
121
{
 
122
        async_msg_0(fb_info.phone, FB_CLEAR);
 
123
}
 
124
 
 
125
static void screen_yield(void)
 
126
{
 
127
        ipc_call_sync_0_0(fb_info.phone, FB_SCREEN_YIELD);
 
128
}
 
129
 
 
130
static void screen_reclaim(void)
 
131
{
 
132
        ipc_call_sync_0_0(fb_info.phone, FB_SCREEN_RECLAIM);
 
133
}
 
134
 
 
135
static void kbd_yield(void)
 
136
{
 
137
        ipc_call_sync_0_0(kbd_phone, KBD_YIELD);
 
138
}
 
139
 
 
140
static void kbd_reclaim(void)
 
141
{
 
142
        ipc_call_sync_0_0(kbd_phone, KBD_RECLAIM);
 
143
}
 
144
 
 
145
static void set_style(int style)
 
146
{
 
147
        async_msg_1(fb_info.phone, FB_SET_STYLE, style);
 
148
}
 
149
 
 
150
static void set_color(int fgcolor, int bgcolor, int flags)
 
151
{
 
152
        async_msg_3(fb_info.phone, FB_SET_COLOR, fgcolor, bgcolor, flags);
 
153
}
 
154
 
 
155
static void set_rgb_color(int fgcolor, int bgcolor)
 
156
{
 
157
        async_msg_2(fb_info.phone, FB_SET_RGB_COLOR, fgcolor, bgcolor); 
 
158
}
 
159
 
 
160
static void set_attrs(attrs_t *attrs)
 
161
{
 
162
        switch (attrs->t) {
 
163
        case at_style:
 
164
                set_style(attrs->a.s.style);
 
165
                break;
 
166
        case at_idx:
 
167
                set_color(attrs->a.i.fg_color, attrs->a.i.bg_color,
 
168
                    attrs->a.i.flags);
 
169
                break;
 
170
        case at_rgb:
 
171
                set_rgb_color(attrs->a.r.fg_color, attrs->a.r.bg_color);
 
172
                break;
 
173
        }
 
174
}
 
175
 
 
176
static int ccap_fb_to_con(int ccap_fb, int *ccap_con)
 
177
{
 
178
        switch (ccap_fb) {
 
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;
 
184
        }
 
185
 
 
186
        return EOK;
 
187
}
 
188
 
 
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)
 
191
{
 
192
        if (interbuffer) {
 
193
                ipcarg_t x;
 
194
                ipcarg_t y;
 
195
                
 
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);
 
200
                        }
 
201
                }
 
202
                
 
203
                async_req_4_0(fb_info.phone, FB_DRAW_TEXT_DATA,
 
204
                    x0, y0, width, height);
 
205
        }
 
206
}
 
207
 
 
208
/** Flush pending cells to FB. */
 
209
static void fb_pending_flush(void)
 
210
{
 
211
        if (fb_pending.cnt > 0) {
 
212
                fb_update_area(active_console, fb_pending.col,
 
213
                    fb_pending.row, fb_pending.cnt, 1);
 
214
                fb_pending.cnt = 0;
 
215
        }
 
216
}
 
217
 
 
218
/** Mark a character cell as changed.
 
219
 *
 
220
 * This adds the cell to the pending rowspan if possible. Otherwise
 
221
 * the old span is flushed first.
 
222
 *
 
223
 */
 
224
static void cell_mark_changed(size_t col, size_t row)
 
225
{
 
226
        if (fb_pending.cnt != 0) {
 
227
                if ((col != fb_pending.col + fb_pending.cnt)
 
228
                    || (row != fb_pending.row)) {
 
229
                        fb_pending_flush();
 
230
                }
 
231
        }
 
232
        
 
233
        if (fb_pending.cnt == 0) {
 
234
                fb_pending.col = col;
 
235
                fb_pending.row = row;
 
236
        }
 
237
        
 
238
        fb_pending.cnt++;
 
239
}
 
240
 
 
241
/** Print a character to the active VC with buffering. */
 
242
static void fb_putchar(wchar_t c, ipcarg_t col, ipcarg_t row)
 
243
{
 
244
        async_msg_3(fb_info.phone, FB_PUTCHAR, c, col, row);
 
245
}
 
246
 
 
247
/** Process a character from the client (TTY emulation). */
 
248
static void write_char(console_t *cons, wchar_t ch)
 
249
{
 
250
        bool flush_cursor = false;
 
251
 
 
252
        switch (ch) {
 
253
        case '\n':
 
254
                fb_pending_flush();
 
255
                flush_cursor = true;
 
256
                cons->scr.position_y++;
 
257
                cons->scr.position_x = 0;
 
258
                break;
 
259
        case '\r':
 
260
                break;
 
261
        case '\t':
 
262
                cons->scr.position_x += 8;
 
263
                cons->scr.position_x -= cons->scr.position_x % 8;
 
264
                break;
 
265
        case '\b':
 
266
                if (cons->scr.position_x == 0)
 
267
                        break;
 
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, ' ');
 
272
                break;
 
273
        default:
 
274
                if (cons == active_console)
 
275
                        cell_mark_changed(cons->scr.position_x, cons->scr.position_y);
 
276
                
 
277
                screenbuffer_putchar(&cons->scr, ch);
 
278
                cons->scr.position_x++;
 
279
        }
 
280
        
 
281
        if (cons->scr.position_x >= cons->scr.size_x) {
 
282
                flush_cursor = true;
 
283
                cons->scr.position_y++;
 
284
        }
 
285
        
 
286
        if (cons->scr.position_y >= cons->scr.size_y) {
 
287
                fb_pending_flush();
 
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;
 
291
                
 
292
                if (cons == active_console)
 
293
                        async_msg_1(fb_info.phone, FB_SCROLL, 1);
 
294
        }
 
295
 
 
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;
 
299
}
 
300
 
 
301
/** Switch to new console */
 
302
static void change_console(console_t *cons)
 
303
{
 
304
        if (cons == active_console)
 
305
                return;
 
306
        
 
307
        fb_pending_flush();
 
308
        
 
309
        if (cons == kernel_console) {
 
310
                async_serialize_start();
 
311
                curs_hide_sync();
 
312
                gcons_in_kernel();
 
313
                screen_yield();
 
314
                kbd_yield();
 
315
                async_serialize_end();
 
316
                
 
317
                if (__SYSCALL0(SYS_DEBUG_ENABLE_CONSOLE)) {
 
318
                        prev_console = active_console;
 
319
                        active_console = kernel_console;
 
320
                } else
 
321
                        cons = active_console;
 
322
        }
 
323
        
 
324
        if (cons != kernel_console) {
 
325
                size_t x;
 
326
                size_t y;
 
327
                int rc = 0;
 
328
                
 
329
                async_serialize_start();
 
330
                
 
331
                if (active_console == kernel_console) {
 
332
                        screen_reclaim();
 
333
                        kbd_reclaim();
 
334
                        gcons_redraw_console();
 
335
                }
 
336
                
 
337
                active_console = cons;
 
338
                gcons_change_console(cons->index);
 
339
                
 
340
                set_attrs(&cons->scr.attrs);
 
341
                curs_visibility(false);
 
342
                if (interbuffer) {
 
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);
 
347
                                }
 
348
                        }
 
349
                        
 
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,
 
353
                            cons->scr.size_y);
 
354
                }
 
355
                
 
356
                if ((!interbuffer) || (rc != 0)) {
 
357
                        set_attrs(&cons->scr.attrs);
 
358
                        screen_clear();
 
359
                        
 
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);
 
363
                                        
 
364
                                        if (!attrs_same(cons->scr.attrs, field->attrs))
 
365
                                                set_attrs(&field->attrs);
 
366
                                        
 
367
                                        cons->scr.attrs = field->attrs;
 
368
                                        if ((field->character == ' ') &&
 
369
                                            (attrs_same(field->attrs, cons->scr.attrs)))
 
370
                                                continue;
 
371
                                        
 
372
                                        fb_putchar(field->character, x, y);
 
373
                                }
 
374
                }
 
375
                
 
376
                curs_goto(cons->scr.position_x, cons->scr.position_y);
 
377
                curs_visibility(cons->scr.is_cursor_visible);
 
378
                
 
379
                async_serialize_end();
 
380
        }
 
381
}
 
382
 
 
383
/** Handler for keyboard */
 
384
static void keyboard_events(ipc_callid_t iid, ipc_call_t *icall)
 
385
{
 
386
        /* Ignore parameters, the connection is already opened */
 
387
        while (true) {
 
388
                
 
389
                ipc_call_t call;
 
390
                ipc_callid_t callid = async_get_call(&call);
 
391
                
 
392
                int retval;
 
393
                console_event_t ev;
 
394
                
 
395
                switch (IPC_GET_METHOD(call)) {
 
396
                case IPC_M_PHONE_HUNGUP:
 
397
                        /* TODO: Handle hangup */
 
398
                        return;
 
399
                case KBD_EVENT:
 
400
                        /* Got event from keyboard driver. */
 
401
                        retval = 0;
 
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);
 
406
                        
 
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);
 
411
                                else
 
412
                                        change_console(&consoles[ev.key - KC_F1]);
 
413
                                break;
 
414
                        }
 
415
                        
 
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);
 
420
                        break;
 
421
                default:
 
422
                        retval = ENOENT;
 
423
                }
 
424
                ipc_answer_0(callid, retval);
 
425
        }
 
426
}
 
427
 
 
428
static void cons_write(console_t *cons, ipc_callid_t rid, ipc_call_t *request)
 
429
{
 
430
        ipc_callid_t callid;
 
431
        size_t size;
 
432
        if (!ipc_data_write_receive(&callid, &size)) {
 
433
                ipc_answer_0(callid, EINVAL);
 
434
                ipc_answer_0(rid, EINVAL);
 
435
                return;
 
436
        }
 
437
        
 
438
        char *buf = (char *) malloc(size);
 
439
        if (buf == NULL) {
 
440
                ipc_answer_0(callid, ENOMEM);
 
441
                ipc_answer_0(rid, ENOMEM);
 
442
                return;
 
443
        }
 
444
        
 
445
        (void) ipc_data_write_finalize(callid, buf, size);
 
446
        
 
447
        async_serialize_start();
 
448
        
 
449
        size_t off = 0;
 
450
        while (off < size) {
 
451
                wchar_t ch = str_decode(buf, &off, size);
 
452
                write_char(cons, ch);
 
453
        }
 
454
        
 
455
        async_serialize_end();
 
456
        
 
457
        gcons_notify_char(cons->index);
 
458
        ipc_answer_1(rid, EOK, size);
 
459
        
 
460
        free(buf);
 
461
}
 
462
 
 
463
static void cons_read(console_t *cons, ipc_callid_t rid, ipc_call_t *request)
 
464
{
 
465
        ipc_callid_t callid;
 
466
        size_t size;
 
467
        if (!ipc_data_read_receive(&callid, &size)) {
 
468
                ipc_answer_0(callid, EINVAL);
 
469
                ipc_answer_0(rid, EINVAL);
 
470
                return;
 
471
        }
 
472
        
 
473
        char *buf = (char *) malloc(size);
 
474
        if (buf == NULL) {
 
475
                ipc_answer_0(callid, ENOMEM);
 
476
                ipc_answer_0(rid, ENOMEM);
 
477
                return;
 
478
        }
 
479
        
 
480
        size_t pos = 0;
 
481
        console_event_t ev;
 
482
        fibril_mutex_lock(&input_mutex);
 
483
recheck:
 
484
        while ((keybuffer_pop(&cons->keybuffer, &ev)) && (pos < size)) {
 
485
                if (ev.type == KEY_PRESS) {
 
486
                        buf[pos] = ev.c;
 
487
                        pos++;
 
488
                }
 
489
        }
 
490
        
 
491
        if (pos == size) {
 
492
                (void) ipc_data_read_finalize(callid, buf, size);
 
493
                ipc_answer_1(rid, EOK, size);
 
494
                free(buf);
 
495
        } else {
 
496
                fibril_condvar_wait(&input_cv, &input_mutex);
 
497
                goto recheck;
 
498
        }
 
499
        fibril_mutex_unlock(&input_mutex);
 
500
}
 
501
 
 
502
static void cons_get_event(console_t *cons, ipc_callid_t rid, ipc_call_t *request)
 
503
{
 
504
        console_event_t ev;
 
505
 
 
506
        fibril_mutex_lock(&input_mutex);
 
507
recheck:
 
508
        if (keybuffer_pop(&cons->keybuffer, &ev)) {
 
509
                ipc_answer_4(rid, EOK, ev.type, ev.key, ev.mods, ev.c);
 
510
        } else {
 
511
                fibril_condvar_wait(&input_cv, &input_mutex);
 
512
                goto recheck;
 
513
        }
 
514
        fibril_mutex_unlock(&input_mutex);
 
515
}
 
516
 
 
517
/** Default thread for new connections */
 
518
static void client_connection(ipc_callid_t iid, ipc_call_t *icall)
 
519
{
 
520
        console_t *cons = NULL;
 
521
        
 
522
        size_t i;
 
523
        for (i = 0; i < CONSOLE_COUNT; i++) {
 
524
                if (i == KERNEL_CONSOLE)
 
525
                        continue;
 
526
                
 
527
                if (consoles[i].dev_handle == (dev_handle_t) IPC_GET_ARG1(*icall)) {
 
528
                        cons = &consoles[i];
 
529
                        break;
 
530
                }
 
531
        }
 
532
        
 
533
        if (cons == NULL) {
 
534
                ipc_answer_0(iid, ENOENT);
 
535
                return;
 
536
        }
 
537
        
 
538
        ipc_callid_t callid;
 
539
        ipc_call_t call;
 
540
        ipcarg_t arg1;
 
541
        ipcarg_t arg2;
 
542
        ipcarg_t arg3;
 
543
 
 
544
        int cons_ccap;
 
545
        int rc;
 
546
        
 
547
        async_serialize_start();
 
548
        if (cons->refcount == 0)
 
549
                gcons_notify_connect(cons->index);
 
550
        
 
551
        cons->refcount++;
 
552
        
 
553
        /* Accept the connection */
 
554
        ipc_answer_0(iid, EOK);
 
555
        
 
556
        while (true) {
 
557
                async_serialize_end();
 
558
                callid = async_get_call(&call);
 
559
                async_serialize_start();
 
560
                
 
561
                arg1 = 0;
 
562
                arg2 = 0;
 
563
                arg3 = 0;
 
564
                
 
565
                switch (IPC_GET_METHOD(call)) {
 
566
                case IPC_M_PHONE_HUNGUP:
 
567
                        cons->refcount--;
 
568
                        if (cons->refcount == 0)
 
569
                                gcons_notify_disconnect(cons->index);
 
570
                        return;
 
571
                case VFS_OUT_READ:
 
572
                        async_serialize_end();
 
573
                        cons_read(cons, callid, &call);
 
574
                        async_serialize_start();
 
575
                        continue;
 
576
                case VFS_OUT_WRITE:
 
577
                        async_serialize_end();
 
578
                        cons_write(cons, callid, &call);
 
579
                        async_serialize_start();
 
580
                        continue;
 
581
                case VFS_OUT_SYNC:
 
582
                        fb_pending_flush();
 
583
                        if (cons == active_console) {
 
584
                                async_req_0_0(fb_info.phone, FB_FLUSH);
 
585
                                
 
586
                                curs_goto(cons->scr.position_x, cons->scr.position_y);
 
587
                        }
 
588
                        break;
 
589
                case CONSOLE_CLEAR:
 
590
                        /* Send message to fb */
 
591
                        if (cons == active_console)
 
592
                                async_msg_0(fb_info.phone, FB_CLEAR);
 
593
                        
 
594
                        screenbuffer_clear(&cons->scr);
 
595
                        
 
596
                        break;
 
597
                case CONSOLE_GOTO:
 
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),
 
602
                                    IPC_GET_ARG2(call));
 
603
                        break;
 
604
                case CONSOLE_GET_SIZE:
 
605
                        arg1 = fb_info.cols;
 
606
                        arg2 = fb_info.rows;
 
607
                        break;
 
608
                case CONSOLE_GET_COLOR_CAP:
 
609
                        rc = ccap_fb_to_con(fb_info.color_cap, &cons_ccap);
 
610
                        if (rc != EOK) {
 
611
                                ipc_answer_0(callid, rc);
 
612
                                continue;
 
613
                        }
 
614
                        arg1 = cons_ccap;
 
615
                        break;
 
616
                case CONSOLE_SET_STYLE:
 
617
                        fb_pending_flush();
 
618
                        arg1 = IPC_GET_ARG1(call);
 
619
                        screenbuffer_set_style(&cons->scr, arg1);
 
620
                        if (cons == active_console)
 
621
                                set_style(arg1);
 
622
                        break;
 
623
                case CONSOLE_SET_COLOR:
 
624
                        fb_pending_flush();
 
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);
 
631
                        break;
 
632
                case CONSOLE_SET_RGB_COLOR:
 
633
                        fb_pending_flush();
 
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);
 
639
                        break;
 
640
                case CONSOLE_CURSOR_VISIBILITY:
 
641
                        fb_pending_flush();
 
642
                        arg1 = IPC_GET_ARG1(call);
 
643
                        cons->scr.is_cursor_visible = arg1;
 
644
                        if (cons == active_console)
 
645
                                curs_visibility(arg1);
 
646
                        break;
 
647
                case CONSOLE_GET_EVENT:
 
648
                        async_serialize_end();
 
649
                        cons_get_event(cons, callid, &call);
 
650
                        async_serialize_start();
 
651
                        continue;
 
652
                case CONSOLE_KCON_ENABLE:
 
653
                        change_console(kernel_console);
 
654
                        break;
 
655
                }
 
656
                ipc_answer_3(callid, EOK, arg1, arg2, arg3);
 
657
        }
 
658
}
 
659
 
 
660
static void interrupt_received(ipc_callid_t callid, ipc_call_t *call)
 
661
{
 
662
        change_console(prev_console);
 
663
}
 
664
 
 
665
static bool console_init(void)
 
666
{
 
667
        ipcarg_t color_cap;
 
668
 
 
669
        /* Connect to keyboard driver */
 
670
        kbd_phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_KEYBOARD, 0, 0);
 
671
        if (kbd_phone < 0) {
 
672
                printf(NAME ": Failed to connect to keyboard service\n");
 
673
                return false;
 
674
        }
 
675
        
 
676
        ipcarg_t phonehash;
 
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");
 
679
                return false;
 
680
        }
 
681
        
 
682
        async_new_connection(phonehash, 0, NULL, keyboard_events);
 
683
 
 
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");
 
688
                return -1;
 
689
        }
 
690
        
 
691
        /* Register driver */
 
692
        int rc = devmap_driver_register(NAME, client_connection);
 
693
        if (rc < 0) {
 
694
                printf(NAME ": Unable to register driver (%d)\n", rc);
 
695
                return false;
 
696
        }
 
697
        
 
698
        /* Initialize gcons */
 
699
        gcons_init(fb_info.phone);
 
700
        
 
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;
 
706
        
 
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);
 
710
        
 
711
        if (as_area_create(interbuffer, ib_size, AS_AREA_READ |
 
712
            AS_AREA_WRITE | AS_AREA_CACHEABLE) != interbuffer)
 
713
                interbuffer = NULL;
 
714
        
 
715
        if (interbuffer) {
 
716
                if (ipc_share_out_start(fb_info.phone, interbuffer,
 
717
                    AS_AREA_READ) != EOK) {
 
718
                        as_area_destroy(interbuffer);
 
719
                        interbuffer = NULL;
 
720
                }
 
721
        }
 
722
        
 
723
        fb_pending.cnt = 0;
 
724
        
 
725
        /* Inititalize consoles */
 
726
        size_t i;
 
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);
 
732
                                return false;
 
733
                        }
 
734
                        screenbuffer_clear(&consoles[i].scr);
 
735
                        keybuffer_init(&consoles[i].keybuffer);
 
736
                        consoles[i].index = i;
 
737
                        consoles[i].refcount = 0;
 
738
                        
 
739
                        char vc[MAX_DEVICE_NAME];
 
740
                        snprintf(vc, MAX_DEVICE_NAME, "vc%u", i);
 
741
                        
 
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);
 
745
                                return false;
 
746
                        }
 
747
                }
 
748
        }
 
749
        
 
750
        /* Disable kernel output to the console */
 
751
        __SYSCALL0(SYS_DEBUG_DISABLE_CONSOLE);
 
752
        
 
753
        /* Initialize the screen */
 
754
        async_serialize_start();
 
755
        gcons_redraw_console();
 
756
        set_rgb_color(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND);
 
757
        screen_clear();
 
758
        curs_goto(0, 0);
 
759
        curs_visibility(active_console->scr.is_cursor_visible);
 
760
        async_serialize_end();
 
761
        
 
762
        /* Receive kernel notifications */
 
763
        if (event_subscribe(EVENT_KCONSOLE, 0) != EOK)
 
764
                printf(NAME ": Error registering kconsole notifications\n");
 
765
        
 
766
        async_set_interrupt_received(interrupt_received);
 
767
        
 
768
        return true;
 
769
}
 
770
 
 
771
int main(int argc, char *argv[])
 
772
{
 
773
        printf(NAME ": HelenOS Console service\n");
 
774
        
 
775
        if (!console_init())
 
776
                return -1;
 
777
        
 
778
        printf(NAME ": Accepting connections\n");
 
779
        async_manager();
 
780
        
 
781
        return 0;
 
782
}
 
783
 
 
784
/** @}
 
785
 */