source: mainline/uspace/srv/console/console.c@ 907bb49

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 907bb49 was 84afc7b, checked in by Martin Decky <martin@…>, 17 years ago

as kernel little brother drivers are not needed anymore, the device numbers do not have to be correlated between kernel and uspace in any way
introduce new syscall sys_device_assign_devno() for generating system-wide unique device numbers for uspace

  • Property mode set to 100644
File size: 14.9 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 <fb.h>
37#include <ipc/ipc.h>
38#include <kbd.h>
39#include <kbd/keycode.h>
40#include <ipc/fb.h>
41#include <ipc/services.h>
42#include <errno.h>
43#include <key_buffer.h>
44#include <ipc/console.h>
45#include <unistd.h>
46#include <async.h>
47#include <libadt/fifo.h>
48#include <screenbuffer.h>
49#include <sys/mman.h>
50#include <stdio.h>
51#include <sysinfo.h>
52
53#include "console.h"
54#include "gcons.h"
55
56#define MAX_KEYREQUESTS_BUFFERED 32
57
58#define NAME "console"
59
60/** Index of currently used virtual console.
61 */
62int active_console = 0;
63int prev_console = 0;
64
65/** Information about framebuffer
66 */
67struct {
68 int phone; /**< Framebuffer phone */
69 ipcarg_t rows; /**< Framebuffer rows */
70 ipcarg_t cols; /**< Framebuffer columns */
71} fb_info;
72
73typedef struct {
74 keybuffer_t keybuffer; /**< Buffer for incoming keys. */
75 /** Buffer for unsatisfied request for keys. */
76 FIFO_CREATE_STATIC(keyrequests, ipc_callid_t,
77 MAX_KEYREQUESTS_BUFFERED);
78 int keyrequest_counter; /**< Number of requests in buffer. */
79 int client_phone; /**< Phone to connected client. */
80 int used; /**< 1 if this virtual console is
81 * connected to some client.*/
82 screenbuffer_t screenbuffer; /**< Screenbuffer for saving screen
83 * contents and related settings. */
84} connection_t;
85
86static connection_t connections[CONSOLE_COUNT]; /**< Array of data for virtual
87 * consoles */
88static keyfield_t *interbuffer = NULL; /**< Pointer to memory shared
89 * with framebufer used for
90 * faster virtual console
91 * switching */
92
93
94/** Find unused virtual console.
95 *
96 */
97static int find_free_connection(void)
98{
99 int i;
100
101 for (i = 0; i < CONSOLE_COUNT; i++) {
102 if (!connections[i].used)
103 return i;
104 }
105 return -1;
106}
107
108static void clrscr(void)
109{
110 async_msg_0(fb_info.phone, FB_CLEAR);
111}
112
113static void curs_visibility(bool visible)
114{
115 async_msg_1(fb_info.phone, FB_CURSOR_VISIBILITY, visible);
116}
117
118static void curs_hide_sync(void)
119{
120 ipc_call_sync_1_0(fb_info.phone, FB_CURSOR_VISIBILITY, false);
121}
122
123static void curs_goto(int row, int col)
124{
125 async_msg_2(fb_info.phone, FB_CURSOR_GOTO, row, col);
126}
127
128static void set_style(int style)
129{
130 async_msg_1(fb_info.phone, FB_SET_STYLE, style);
131}
132
133static void set_color(int fgcolor, int bgcolor, int flags)
134{
135 async_msg_3(fb_info.phone, FB_SET_COLOR, fgcolor, bgcolor, flags);
136}
137
138static void set_rgb_color(int fgcolor, int bgcolor)
139{
140 async_msg_2(fb_info.phone, FB_SET_RGB_COLOR, fgcolor, bgcolor);
141}
142
143static void set_attrs(attrs_t *attrs)
144{
145 switch (attrs->t) {
146 case at_style:
147 set_style(attrs->a.s.style);
148 break;
149
150 case at_idx:
151 set_color(attrs->a.i.fg_color, attrs->a.i.bg_color,
152 attrs->a.i.flags);
153 break;
154
155 case at_rgb:
156 set_rgb_color(attrs->a.r.fg_color, attrs->a.r.bg_color);
157 break;
158 }
159}
160
161static void prtchr(char c, int row, int col)
162{
163 async_msg_3(fb_info.phone, FB_PUTCHAR, c, row, col);
164}
165
166/** Check key and process special keys.
167 *
168 *
169 */
170static void write_char(int console, char key)
171{
172 screenbuffer_t *scr = &(connections[console].screenbuffer);
173
174 switch (key) {
175 case '\n':
176 scr->position_y++;
177 scr->position_x = 0;
178 break;
179 case '\r':
180 break;
181 case '\t':
182 scr->position_x += 8;
183 scr->position_x -= scr->position_x % 8;
184 break;
185 case '\b':
186 if (scr->position_x == 0)
187 break;
188 scr->position_x--;
189 if (console == active_console)
190 prtchr(' ', scr->position_y, scr->position_x);
191 screenbuffer_putchar(scr, ' ');
192 break;
193 default:
194 if (console == active_console)
195 prtchr(key, scr->position_y, scr->position_x);
196
197 screenbuffer_putchar(scr, key);
198 scr->position_x++;
199 }
200
201 scr->position_y += (scr->position_x >= scr->size_x);
202
203 if (scr->position_y >= scr->size_y) {
204 scr->position_y = scr->size_y - 1;
205 screenbuffer_clear_line(scr, scr->top_line);
206 scr->top_line = (scr->top_line + 1) % scr->size_y;
207 if (console == active_console)
208 async_msg_1(fb_info.phone, FB_SCROLL, 1);
209 }
210
211 scr->position_x = scr->position_x % scr->size_x;
212
213 if (console == active_console)
214 curs_goto(scr->position_y, scr->position_x);
215
216}
217
218/** Switch to new console */
219static void change_console(int newcons)
220{
221 connection_t *conn;
222 int i, j, rc;
223 keyfield_t *field;
224 attrs_t *attrs;
225
226 if (newcons == active_console)
227 return;
228
229 if (newcons == KERNEL_CONSOLE) {
230 async_serialize_start();
231 curs_hide_sync();
232 gcons_in_kernel();
233 async_serialize_end();
234
235 if (__SYSCALL0(SYS_DEBUG_ENABLE_CONSOLE)) {
236 prev_console = active_console;
237 active_console = KERNEL_CONSOLE;
238 } else
239 newcons = active_console;
240 }
241
242 if (newcons != KERNEL_CONSOLE) {
243 async_serialize_start();
244
245 if (active_console == KERNEL_CONSOLE)
246 gcons_redraw_console();
247
248 active_console = newcons;
249 gcons_change_console(newcons);
250 conn = &connections[active_console];
251
252 set_attrs(&conn->screenbuffer.attrs);
253 curs_visibility(false);
254 if (interbuffer) {
255 for (i = 0; i < conn->screenbuffer.size_x; i++)
256 for (j = 0; j < conn->screenbuffer.size_y; j++) {
257 unsigned int size_x;
258
259 size_x = conn->screenbuffer.size_x;
260 interbuffer[i + j * size_x] =
261 *get_field_at(&conn->screenbuffer, i, j);
262 }
263 /* This call can preempt, but we are already at the end */
264 rc = async_req_0_0(fb_info.phone, FB_DRAW_TEXT_DATA);
265 }
266
267 if ((!interbuffer) || (rc != 0)) {
268 set_attrs(&conn->screenbuffer.attrs);
269 clrscr();
270 attrs = &conn->screenbuffer.attrs;
271
272 for (j = 0; j < conn->screenbuffer.size_y; j++)
273 for (i = 0; i < conn->screenbuffer.size_x; i++) {
274 field = get_field_at(&conn->screenbuffer, i, j);
275 if (!attrs_same(*attrs, field->attrs))
276 set_attrs(&field->attrs);
277 attrs = &field->attrs;
278 if ((field->character == ' ') &&
279 (attrs_same(field->attrs,
280 conn->screenbuffer.attrs)))
281 continue;
282
283 prtchr(field->character, j, i);
284 }
285 }
286
287 curs_goto(conn->screenbuffer.position_y,
288 conn->screenbuffer.position_x);
289 curs_visibility(conn->screenbuffer.is_cursor_visible);
290
291 async_serialize_end();
292 }
293}
294
295/** Handler for keyboard */
296static void keyboard_events(ipc_callid_t iid, ipc_call_t *icall)
297{
298 ipc_callid_t callid;
299 ipc_call_t call;
300 int retval;
301 kbd_event_t ev;
302 connection_t *conn;
303 int newcon;
304
305 /* Ignore parameters, the connection is alread opened */
306 while (1) {
307 callid = async_get_call(&call);
308 switch (IPC_GET_METHOD(call)) {
309 case IPC_M_PHONE_HUNGUP:
310 /* TODO: Handle hangup */
311 return;
312 case KBD_MS_LEFT:
313 newcon = gcons_mouse_btn(IPC_GET_ARG1(call));
314 if (newcon != -1)
315 change_console(newcon);
316 retval = 0;
317 break;
318 case KBD_MS_MOVE:
319 gcons_mouse_move(IPC_GET_ARG1(call),
320 IPC_GET_ARG2(call));
321 retval = 0;
322 break;
323 case KBD_EVENT:
324 /* Got event from keyboard driver. */
325 retval = 0;
326 ev.type = IPC_GET_ARG1(call);
327 ev.key = IPC_GET_ARG2(call);
328 ev.mods = IPC_GET_ARG3(call);
329 ev.c = IPC_GET_ARG4(call);
330
331 /* switch to another virtual console */
332
333 conn = &connections[active_console];
334
335 if ((ev.key >= KC_F1) && (ev.key < KC_F1 +
336 CONSOLE_COUNT)) {
337 if (ev.key == KC_F12)
338 change_console(KERNEL_CONSOLE);
339 else
340 change_console(ev.key - KC_F1);
341 break;
342 }
343
344 /* if client is awaiting key, send it */
345 if (conn->keyrequest_counter > 0) {
346 conn->keyrequest_counter--;
347 ipc_answer_4(fifo_pop(conn->keyrequests), EOK,
348 ev.type, ev.key, ev.mods, ev.c);
349 break;
350 }
351
352 keybuffer_push(&conn->keybuffer, &ev);
353 retval = 0;
354
355 break;
356 default:
357 retval = ENOENT;
358 }
359 ipc_answer_0(callid, retval);
360 }
361}
362
363/** Default thread for new connections */
364static void client_connection(ipc_callid_t iid, ipc_call_t *icall)
365{
366 ipc_callid_t callid;
367 ipc_call_t call;
368 int consnum;
369 ipcarg_t arg1, arg2, arg3, arg4;
370 connection_t *conn;
371
372 if ((consnum = find_free_connection()) == -1) {
373 ipc_answer_0(iid, ELIMIT);
374 return;
375 }
376 conn = &connections[consnum];
377 conn->used = 1;
378
379 async_serialize_start();
380 gcons_notify_connect(consnum);
381 conn->client_phone = IPC_GET_ARG5(*icall);
382 screenbuffer_clear(&conn->screenbuffer);
383
384 /* Accept the connection */
385 ipc_answer_0(iid, EOK);
386
387 while (1) {
388 async_serialize_end();
389 callid = async_get_call(&call);
390 async_serialize_start();
391
392 arg1 = 0;
393 arg2 = 0;
394 arg3 = 0;
395 arg4 = 0;
396
397 switch (IPC_GET_METHOD(call)) {
398 case IPC_M_PHONE_HUNGUP:
399 gcons_notify_disconnect(consnum);
400
401 /* Answer all pending requests */
402 while (conn->keyrequest_counter > 0) {
403 conn->keyrequest_counter--;
404 ipc_answer_0(fifo_pop(conn->keyrequests),
405 ENOENT);
406 break;
407 }
408 conn->used = 0;
409 return;
410 case CONSOLE_PUTCHAR:
411 write_char(consnum, IPC_GET_ARG1(call));
412 gcons_notify_char(consnum);
413 break;
414 case CONSOLE_CLEAR:
415 /* Send message to fb */
416 if (consnum == active_console) {
417 async_msg_0(fb_info.phone, FB_CLEAR);
418 }
419
420 screenbuffer_clear(&conn->screenbuffer);
421
422 break;
423 case CONSOLE_GOTO:
424 screenbuffer_goto(&conn->screenbuffer,
425 IPC_GET_ARG2(call), IPC_GET_ARG1(call));
426 if (consnum == active_console)
427 curs_goto(IPC_GET_ARG1(call),
428 IPC_GET_ARG2(call));
429 break;
430 case CONSOLE_GETSIZE:
431 arg1 = fb_info.rows;
432 arg2 = fb_info.cols;
433 break;
434 case CONSOLE_FLUSH:
435 if (consnum == active_console)
436 async_req_0_0(fb_info.phone, FB_FLUSH);
437 break;
438 case CONSOLE_SET_STYLE:
439 arg1 = IPC_GET_ARG1(call);
440 screenbuffer_set_style(&conn->screenbuffer, arg1);
441 if (consnum == active_console)
442 set_style(arg1);
443 break;
444 case CONSOLE_SET_COLOR:
445 arg1 = IPC_GET_ARG1(call);
446 arg2 = IPC_GET_ARG2(call);
447 arg3 = IPC_GET_ARG3(call);
448 screenbuffer_set_color(&conn->screenbuffer, arg1,
449 arg2, arg3);
450 if (consnum == active_console)
451 set_color(arg1, arg2, arg3);
452 break;
453 case CONSOLE_SET_RGB_COLOR:
454 arg1 = IPC_GET_ARG1(call);
455 arg2 = IPC_GET_ARG2(call);
456 screenbuffer_set_rgb_color(&conn->screenbuffer, arg1,
457 arg2);
458 if (consnum == active_console)
459 set_rgb_color(arg1, arg2);
460 break;
461 case CONSOLE_CURSOR_VISIBILITY:
462 arg1 = IPC_GET_ARG1(call);
463 conn->screenbuffer.is_cursor_visible = arg1;
464 if (consnum == active_console)
465 curs_visibility(arg1);
466 break;
467 case CONSOLE_GETKEY:
468 if (keybuffer_empty(&conn->keybuffer)) {
469 /* buffer is empty -> store request */
470 if (conn->keyrequest_counter <
471 MAX_KEYREQUESTS_BUFFERED) {
472 fifo_push(conn->keyrequests, callid);
473 conn->keyrequest_counter++;
474 } else {
475 /*
476 * No key available and too many
477 * requests => fail.
478 */
479 ipc_answer_0(callid, ELIMIT);
480 }
481 continue;
482 }
483 kbd_event_t ev;
484 keybuffer_pop(&conn->keybuffer, &ev);
485 arg1 = ev.type;
486 arg2 = ev.key;
487 arg3 = ev.mods;
488 arg4 = ev.c;
489 break;
490 }
491 ipc_answer_4(callid, EOK, arg1, arg2, arg3, arg4);
492 }
493}
494
495static void interrupt_received(ipc_callid_t callid, ipc_call_t *call)
496{
497 change_console(prev_console);
498}
499
500int main(int argc, char *argv[])
501{
502 printf(NAME ": HelenOS Console service\n");
503
504 ipcarg_t phonehash;
505 int kbd_phone;
506 size_t ib_size;
507 int i;
508
509 async_set_client_connection(client_connection);
510
511 /* Connect to keyboard driver */
512 kbd_phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_KEYBOARD, 0, 0);
513 if (kbd_phone < 0) {
514 printf(NAME ": Failed to connect to keyboard service\n");
515 return -1;
516 }
517
518 if (ipc_connect_to_me(kbd_phone, SERVICE_CONSOLE, 0, 0, &phonehash) != 0) {
519 printf(NAME ": Failed to create callback from keyboard service\n");
520 return -1;
521 }
522
523 async_new_connection(phonehash, 0, NULL, keyboard_events);
524
525 /* Connect to framebuffer driver */
526 fb_info.phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_VIDEO, 0, 0);
527 if (fb_info.phone < 0) {
528 printf(NAME ": Failed to connect to video service\n");
529 return -1;
530 }
531
532 /* Disable kernel output to the console */
533 __SYSCALL0(SYS_DEBUG_DISABLE_CONSOLE);
534
535 /* Initialize gcons */
536 gcons_init(fb_info.phone);
537 /* Synchronize, the gcons can have something in queue */
538 async_req_0_0(fb_info.phone, FB_FLUSH);
539
540 async_req_0_2(fb_info.phone, FB_GET_CSIZE, &fb_info.rows,
541 &fb_info.cols);
542 set_rgb_color(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND);
543 clrscr();
544
545 /* Init virtual consoles */
546 for (i = 0; i < CONSOLE_COUNT; i++) {
547 connections[i].used = 0;
548 keybuffer_init(&connections[i].keybuffer);
549
550 connections[i].keyrequests.head = 0;
551 connections[i].keyrequests.tail = 0;
552 connections[i].keyrequests.items = MAX_KEYREQUESTS_BUFFERED;
553 connections[i].keyrequest_counter = 0;
554
555 if (screenbuffer_init(&connections[i].screenbuffer,
556 fb_info.cols, fb_info.rows) == NULL) {
557 /* FIXME: handle error */
558 return -1;
559 }
560 }
561 connections[KERNEL_CONSOLE].used = 1;
562
563 /* Set up shared memory buffer. */
564 ib_size = sizeof(keyfield_t) * fb_info.cols * fb_info.rows;
565 interbuffer = as_get_mappable_page(ib_size);
566
567 if (as_area_create(interbuffer, ib_size, AS_AREA_READ |
568 AS_AREA_WRITE | AS_AREA_CACHEABLE) != interbuffer) {
569 interbuffer = NULL;
570 }
571
572 if (interbuffer) {
573 if (ipc_share_out_start(fb_info.phone, interbuffer,
574 AS_AREA_READ) != EOK) {
575 as_area_destroy(interbuffer);
576 interbuffer = NULL;
577 }
578 }
579
580 curs_goto(0, 0);
581 curs_visibility(
582 connections[active_console].screenbuffer.is_cursor_visible);
583
584 /* Register at NS */
585 if (ipc_connect_to_me(PHONE_NS, SERVICE_CONSOLE, 0, 0, &phonehash) != 0)
586 return -1;
587
588 /* Receive kernel notifications */
589// if (sysinfo_value("kconsole.present")) {
590// int inr = sysinfo_value("kconsole.inr");
591// if (ipc_register_irq(inr, device_assign_devno(), 0, NULL) != EOK)
592// printf(NAME ": Error registering kconsole notifications\n");
593//
594// async_set_interrupt_received(interrupt_received);
595// }
596
597 // FIXME: avoid connectiong to itself, keep using klog
598 // printf(NAME ": Accepting connections\n");
599 async_manager();
600
601 return 0;
602}
603
604/** @}
605 */
Note: See TracBrowser for help on using the repository browser.