diff --git a/tty/pc-tty/event_queue.c b/tty/pc-tty/event_queue.c new file mode 100644 index 000000000..ff5e113a2 --- /dev/null +++ b/tty/pc-tty/event_queue.c @@ -0,0 +1,150 @@ +/* + * Phoenix-RTOS + * + * Simple circular buffer for enqueueing PS/2 events + * + * Copyright 2024 Phoenix Systems + * Author: Adam Greloch + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#include +#include +#include +#include +#include +#include + +#include "event_queue.h" + + +int event_queue_init(event_queue_t *eq) +{ + int err; + + if (eq == NULL) { + return -EINVAL; + } + + (void)memset(eq, 0, sizeof(event_queue_t)); + + err = mutexCreate(&eq->mutex); + if (err < 0) { + return err; + } + + err = condCreate(&eq->waitq); + if (err < 0) { + resourceDestroy(eq->mutex); + return err; + } + + return EOK; +} + + +int event_queue_put(event_queue_t *eq, unsigned char event, unsigned int notify_cnt) +{ + if (eq == NULL) { + return -EINVAL; + } + + int bytes = 0; + + mutexLock(eq->mutex); + if (eq->cnt < TTYPC_EQBUF_SIZE) { + eq->buf[eq->w] = event; + eq->w = (eq->w + 1u) % TTYPC_EQBUF_SIZE; + eq->cnt++; + if (eq->cnt >= notify_cnt) { + condSignal(eq->waitq); + } + bytes = 1; + } + mutexUnlock(eq->mutex); + + return bytes; +} + + +int event_queue_get(event_queue_t *eq, void *dest, unsigned int size, unsigned int flags) +{ + int err; + unsigned int bytes; + + if (eq == NULL || dest == NULL) { + return -EINVAL; + } + + if (size == 0u) { + return 0; + } + + if (size > TTYPC_EQBUF_SIZE) { + size = TTYPC_EQBUF_SIZE; + } + + mutexLock(eq->mutex); + do { + if ((flags & O_NONBLOCK) != 0 && eq->cnt < size) { + err = -EWOULDBLOCK; + break; + } + + while (eq->cnt < size) { + condWait(eq->waitq, eq->mutex, 0); + } + + if (eq->w > eq->r) { + (void)memcpy(dest, eq->buf + eq->r, bytes = size); + } + else { + (void)memcpy(dest, eq->buf + eq->r, bytes = min(size, TTYPC_EQBUF_SIZE - eq->r)); + + if (bytes < size) { + size -= bytes; + (void)memcpy((uintptr_t *)dest + bytes, eq->buf, size); + bytes += size; + } + } + + eq->r = (eq->r + bytes) % TTYPC_EQBUF_SIZE; + eq->cnt -= bytes; + + err = bytes; + } while (0); + mutexUnlock(eq->mutex); + + return err; +} + + +int event_queue_count(event_queue_t *eq) +{ + int res; + + if (eq == NULL) { + return -EINVAL; + } + + mutexLock(eq->mutex); + res = eq->cnt; + mutexUnlock(eq->mutex); + + return res; +} + + +void event_queue_destroy(event_queue_t *eq) +{ + if (eq == NULL) { + return; + } + + resourceDestroy(eq->waitq); + resourceDestroy(eq->mutex); +} diff --git a/tty/pc-tty/event_queue.h b/tty/pc-tty/event_queue.h new file mode 100644 index 000000000..fd4724f24 --- /dev/null +++ b/tty/pc-tty/event_queue.h @@ -0,0 +1,54 @@ +/* + * Phoenix-RTOS + * + * Simple circular buffer for enqueueing PS/2 events + * + * Copyright 2024 Phoenix Systems + * Author: Adam Greloch + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#ifndef _EVENT_QUEUE_H_ +#define _EVENT_QUEUE_H_ + +#include + +#define TTYPC_EQBUF_SIZE 258u /* should be a multiple of 3 for mouse events */ + +typedef struct _event_queue_t { + unsigned char buf[TTYPC_EQBUF_SIZE]; + unsigned int cnt; + unsigned int r; + unsigned int w; + handle_t mutex; + handle_t waitq; +} event_queue_t; + + +/* Initializes event queue */ +extern int event_queue_init(event_queue_t *eq); + + +/* Puts a byte to event queue. If the queue has notify_cnt bytes or more, wake + the readers. If the queue is full, does nothing and returns 0. If event was + put, returns 1. */ +extern int event_queue_put(event_queue_t *eq, unsigned char event, unsigned int notify_cnt); + + +/* Reads data from event queue */ +extern int event_queue_get(event_queue_t *eq, void *dest, unsigned int size, unsigned int flags); + + +/* Retrieves number of bytes stored in event queue */ +extern int event_queue_count(event_queue_t *eq); + + +/* Destroys event queue */ +extern void event_queue_destroy(event_queue_t *eq); + + +#endif diff --git a/tty/pc-tty/ttypc.h b/tty/pc-tty/ttypc.h index 131ae7e37..7e39b20f6 100644 --- a/tty/pc-tty/ttypc.h +++ b/tty/pc-tty/ttypc.h @@ -18,6 +18,8 @@ #include #include "ttypc_vt.h" +#include "board_config.h" +#include "event_queue.h" /* Number of virtual terminals */ @@ -25,54 +27,75 @@ /* Keyboard types */ -enum { KBD_BIOS, KBD_PS2 }; +enum { KBD_BIOS, + KBD_PS2 }; /* Keyboard key types */ enum { - KB_NONE = 0x0000, - KB_CTL = 0x0001, - KB_SHIFT = 0x0002, - KB_ALT = 0x0004, - KB_ALTGR = 0x0008, - KB_SCROLL = 0x0010, - KB_NUM = 0x0020, - KB_CAPS = 0x0040, - KB_FUNC = 0x0080, - KB_ASCII = 0x0100, - KB_KP = 0x0200, - KB_EXT = 0x0400 + KB_NONE = 0x0000, + KB_CTL = 0x0001, + KB_SHIFT = 0x0002, + KB_ALT = 0x0004, + KB_ALTGR = 0x0008, + KB_SCROLL = 0x0010, + KB_NUM = 0x0020, + KB_CAPS = 0x0040, + KB_FUNC = 0x0080, + KB_ASCII = 0x0100, + KB_KP = 0x0200, + KB_EXT = 0x0400 }; - struct _ttypc_t { - unsigned int port; /* Driver port */ + unsigned int port; /* Driver port */ /* VGA */ - volatile void *vga; /* VGA screen memory */ - void *crtc; /* Video Display Controller (CRTC) */ - unsigned color; /* Color support */ + volatile void *vga; /* VGA screen memory */ + void *crtc; /* Video Display Controller (CRTC) */ + unsigned color; /* Color support */ /* KBD */ volatile void *kbd; /* Keyboard controller */ unsigned char ktype; /* Keyboard type */ unsigned char lockst; /* Lock keys state */ unsigned char shiftst; /* Shift keys state */ - unsigned int kirq; /* Interrupt number */ - handle_t klock; /* Interrupt mutex */ - handle_t kcond; /* Interrupt condition variable */ - handle_t kinth; /* Interrupt handle */ + handle_t kmcond; /* Kbd/mouse interrupt condition variable */ + + unsigned int kirq; /* Kbd interrupt number */ + handle_t klock; /* Kbd interrupt mutex */ + handle_t kinth; /* Kbd interrupt handle */ + +#if PC_TTY_MOUSE_ENABLE + unsigned int mirq; /* Mouse interrupt number */ + handle_t minth; /* Mouse interrupt handle */ +#endif + +#if PC_TTY_CREATE_PS2_VDEVS + event_queue_t keq; /* Kbd event buffer */ + unsigned int kport; /* Kbd device port */ + + /* Kbd pool thread stack */ + char kpstack[1024] __attribute__((aligned(8))); +#if PC_TTY_MOUSE_ENABLE + event_queue_t meq; /* Mouse event buffer */ + unsigned int mport; /* Mouse device port */ + + /* Mouse pool thread stack */ + char mpstack[1024] __attribute__((aligned(8))); +#endif +#endif /* Virtual terminals */ - ttypc_vt_t *vt; /* Active virtual terminal */ - ttypc_vt_t vts[NVTS]; /* Virtual Terminals */ + ttypc_vt_t *vt; /* Active virtual terminal */ + ttypc_vt_t vts[NVTS]; /* Virtual Terminals */ /* Synchronization */ - handle_t lock; /* Access mutex */ + handle_t lock; /* Access mutex */ /* Thread stacks */ - char kstack[2048] __attribute__ ((aligned(8))); - char pstack[2048] __attribute__ ((aligned(8))); + char kstack[2048] __attribute__((aligned(8))); + char pstack[2048] __attribute__((aligned(8))); }; diff --git a/tty/pc-tty/ttypc_kbd.c b/tty/pc-tty/ttypc_kbd.c index 357e37de1..865065580 100644 --- a/tty/pc-tty/ttypc_kbd.c +++ b/tty/pc-tty/ttypc_kbd.c @@ -1,11 +1,11 @@ /* * Phoenix-RTOS * - * PS/2 101-key US keyboard (based on FreeBSD 4.4 pcvt) + * PS/2 101-key US keyboard and 3-button mouse (based on FreeBSD 4.4 pcvt) * * Copyright 2001, 2007-2008 Pawel Pisarczyk - * Copyright 2012, 2017, 2019, 2020 Phoenix Systems - * Author: Pawel Pisarczyk, Lukasz Kosinski + * Copyright 2012, 2017, 2019, 2020, 2024 Phoenix Systems + * Author: Pawel Pisarczyk, Lukasz Kosinski, Adam Greloch * * This file is part of Phoenix-RTOS. * @@ -16,14 +16,27 @@ #include #include #include +#include +#include #include #include #include #include +#include +#include +#include + #include "ttypc_kbd.h" +#include "ttypc_mouse.h" #include "ttypc_vga.h" +#include "ttypc_ps2.h" +#include "event_queue.h" + + +#define CTL_THREAD_PRIORITY 1 +#define POOL_THREAD_PRIORITY 1 /* Keyboard key map entry */ @@ -38,6 +51,7 @@ typedef struct { /* U.S 101 keys keyboard map */ +/* clang-format off */ static const ttypc_kbd_keymap_t scodes[] = { /*type unshift shift ctl altgr shift_altgr scancode */ { KB_NONE, "", "", "", "", "" }, /* 0 unused */ @@ -169,7 +183,7 @@ static const ttypc_kbd_keymap_t scodes[] = { { KB_NONE, "", "", "", "", "" }, /* 126 */ { KB_NONE, "", "", "", "", "" } /* 127 */ }; - +/* clang-format on */ /* KB_KP (keypad keys) modifiers map */ static const unsigned char kpmod[] = { @@ -202,6 +216,11 @@ static char *_ttypc_kbd_get(ttypc_t *ttypc) dt = inb((void *)ttypc->kbd); +#if PC_TTY_CREATE_PS2_VDEVS + /* Copy this event in raw form to queue */ + (void)event_queue_put(&ttypc->keq, dt, 1); +#endif + /* Extended scan code */ if (scodes[dt & 0x7f].type == KB_EXT) { ext = 1; @@ -213,36 +232,36 @@ static char *_ttypc_kbd_get(ttypc_t *ttypc) dt &= 0x7f; switch (scodes[dt].type) { - case KB_SCROLL: - if (!ext) - ttypc->shiftst &= ~KB_SCROLL; - break; + case KB_SCROLL: + if (!ext) + ttypc->shiftst &= ~KB_SCROLL; + break; - case KB_NUM: - if (!ext) - ttypc->shiftst &= ~KB_NUM; - break; + case KB_NUM: + if (!ext) + ttypc->shiftst &= ~KB_NUM; + break; - case KB_CAPS: - if (!ext) - ttypc->shiftst &= ~KB_CAPS; - break; + case KB_CAPS: + if (!ext) + ttypc->shiftst &= ~KB_CAPS; + break; - case KB_CTL: - ttypc->shiftst &= ~KB_CTL; - break; + case KB_CTL: + ttypc->shiftst &= ~KB_CTL; + break; - case KB_SHIFT: - if (!ext) - ttypc->shiftst &= ~KB_SHIFT; - break; + case KB_SHIFT: + if (!ext) + ttypc->shiftst &= ~KB_SHIFT; + break; - case KB_ALT: - if (ext) - ttypc->shiftst &= ~KB_ALTGR; - else - ttypc->shiftst &= ~KB_ALT; - break; + case KB_ALT: + if (ext) + ttypc->shiftst &= ~KB_ALTGR; + else + ttypc->shiftst &= ~KB_ALT; + break; } /* Last key released */ @@ -255,123 +274,123 @@ static char *_ttypc_kbd_get(ttypc_t *ttypc) /* Key is pressed */ else { switch (scodes[dt].type) { - /* Lock keys - Scroll, Num, Caps */ - case KB_SCROLL: - if (ttypc->shiftst & KB_SCROLL) + /* Lock keys - Scroll, Num, Caps */ + case KB_SCROLL: + if (ttypc->shiftst & KB_SCROLL) + break; + ttypc->shiftst |= KB_SCROLL; + ttypc->lockst ^= KB_SCROLL; + _ttypc_kbd_updateled(ttypc); break; - ttypc->shiftst |= KB_SCROLL; - ttypc->lockst ^= KB_SCROLL; - _ttypc_kbd_updateled(ttypc); - break; - case KB_NUM: - if (ttypc->shiftst & KB_NUM) + case KB_NUM: + if (ttypc->shiftst & KB_NUM) + break; + ttypc->shiftst |= KB_NUM; + ttypc->lockst ^= KB_NUM; + _ttypc_kbd_updateled(ttypc); break; - ttypc->shiftst |= KB_NUM; - ttypc->lockst ^= KB_NUM; - _ttypc_kbd_updateled(ttypc); - break; - case KB_CAPS: - if (ttypc->shiftst & KB_CAPS) + case KB_CAPS: + if (ttypc->shiftst & KB_CAPS) + break; + ttypc->shiftst |= KB_CAPS; + ttypc->lockst ^= KB_CAPS; + _ttypc_kbd_updateled(ttypc); break; - ttypc->shiftst |= KB_CAPS; - ttypc->lockst ^= KB_CAPS; - _ttypc_kbd_updateled(ttypc); - break; - /* Shift keys - Ctl, Shift, Alt */ - case KB_CTL: - ttypc->shiftst |= KB_CTL; - break; - - case KB_SHIFT: - ttypc->shiftst |= KB_SHIFT; - break; - - case KB_ALT: - if (ext) - ttypc->shiftst |= KB_ALTGR; - else - ttypc->shiftst |= KB_ALT; - break; - - /* Function keys */ - case KB_FUNC: - /* Regular ASCII */ - case KB_ASCII: - /* Keys with extended scan codes don't depend on any modifiers */ - if (ext) { - /* Handles keypad '/' key */ - s = scodes[dt].unshift; + /* Shift keys - Ctl, Shift, Alt */ + case KB_CTL: + ttypc->shiftst |= KB_CTL; break; - } - /* Control modifier */ - if (ttypc->shiftst & KB_CTL) { - s = scodes[dt].ctl; + + case KB_SHIFT: + ttypc->shiftst |= KB_SHIFT; break; - } - /* Right alt and right alt with shift modifiers */ - if (ttypc->shiftst & KB_ALTGR) { - if (ttypc->shiftst & KB_SHIFT) - s = scodes[dt].shift_altgr; + case KB_ALT: + if (ext) + ttypc->shiftst |= KB_ALTGR; else - s = scodes[dt].altgr; - } - /* Shift modifier */ - else if (ttypc->shiftst & KB_SHIFT) { - s = scodes[dt].shift; - } - /* No modifiers */ - else { - s = scodes[dt].unshift; - } + ttypc->shiftst |= KB_ALT; + break; - /* Caps lock */ - if ((ttypc->lockst & KB_CAPS) && (*scodes[dt].unshift >= 'a') && (*scodes[dt].unshift <= 'z')) { - if (s == scodes[dt].altgr) - s = scodes[dt].shift_altgr; - else if (s == scodes[dt].shift_altgr) - s = scodes[dt].altgr; - else if (s == scodes[dt].shift) + /* Function keys */ + case KB_FUNC: + /* Regular ASCII */ + case KB_ASCII: + /* Keys with extended scan codes don't depend on any modifiers */ + if (ext) { + /* Handles keypad '/' key */ s = scodes[dt].unshift; - else if (s == scodes[dt].unshift) + break; + } + /* Control modifier */ + if (ttypc->shiftst & KB_CTL) { + s = scodes[dt].ctl; + break; + } + + /* Right alt and right alt with shift modifiers */ + if (ttypc->shiftst & KB_ALTGR) { + if (ttypc->shiftst & KB_SHIFT) + s = scodes[dt].shift_altgr; + else + s = scodes[dt].altgr; + } + /* Shift modifier */ + else if (ttypc->shiftst & KB_SHIFT) { s = scodes[dt].shift; - } - break; + } + /* No modifiers */ + else { + s = scodes[dt].unshift; + } - /* Keys without meaning */ - case KB_NONE: - break; + /* Caps lock */ + if ((ttypc->lockst & KB_CAPS) && (*scodes[dt].unshift >= 'a') && (*scodes[dt].unshift <= 'z')) { + if (s == scodes[dt].altgr) + s = scodes[dt].shift_altgr; + else if (s == scodes[dt].shift_altgr) + s = scodes[dt].altgr; + else if (s == scodes[dt].shift) + s = scodes[dt].unshift; + else if (s == scodes[dt].unshift) + s = scodes[dt].shift; + } + break; - /* Keypad */ - case KB_KP: - /* Keys with extended scan codes don't depend on any modifiers */ - if (ext) { - /* Handles DEL, HOME, END, PU, PD and arrow (non keypad) keys */ - s = scodes[dt].shift; + /* Keys without meaning */ + case KB_NONE: break; - } - /* Shift modifier */ - if (ttypc->shiftst & KB_SHIFT) - s = scodes[dt].shift; - /* Control modifier */ - else if (ttypc->shiftst & KB_CTL) - s = scodes[dt].ctl; - /* No modifiers */ - else - s = scodes[dt].unshift; - - /* Num lock */ - if (ttypc->lockst & KB_NUM) { - if (s == scodes[dt].shift) - s = scodes[dt].unshift; - else if ((s == scodes[dt].ctl) || (s == scodes[dt].unshift)) + /* Keypad */ + case KB_KP: + /* Keys with extended scan codes don't depend on any modifiers */ + if (ext) { + /* Handles DEL, HOME, END, PU, PD and arrow (non keypad) keys */ s = scodes[dt].shift; - } - break; + break; + } + + /* Shift modifier */ + if (ttypc->shiftst & KB_SHIFT) + s = scodes[dt].shift; + /* Control modifier */ + else if (ttypc->shiftst & KB_CTL) + s = scodes[dt].ctl; + /* No modifiers */ + else + s = scodes[dt].unshift; + + /* Num lock */ + if (ttypc->lockst & KB_NUM) { + if (s == scodes[dt].shift) + s = scodes[dt].unshift; + else if ((s == scodes[dt].ctl) || (s == scodes[dt].unshift)) + s = scodes[dt].shift; + } + break; } } @@ -400,14 +419,17 @@ static char *_ttypc_kbd_get(ttypc_t *ttypc) /* Keyboard interrupt handler */ -static int ttypc_kbd_interrupt(unsigned int n, void *arg) +static int _ttypc_kbd_interrupt(unsigned int n, void *arg) { - ttypc_t *ttypc = (ttypc_t *)arg; - - return ttypc->kcond; + return 0; } +/* Macros for distinguishing the origin of pending output */ +#define KBD_OUTPUT_PENDING(status) (((status) & (1u << 0u)) && !((status) & (1u << 5u))) +#define MOUSE_OUTPUT_PENDING(status) (((status) & (1u << 0u)) && ((status) & (1u << 5u))) + + static void ttypc_kbd_ctlthr(void *arg) { ttypc_t *ttypc = (ttypc_t *)arg; @@ -415,12 +437,22 @@ static void ttypc_kbd_ctlthr(void *arg) char *s, k; char buff[10]; unsigned char m; + unsigned char status; mutexLock(ttypc->klock); for (;;) { - /* Wait for character codes to show up in keyboard output buffer */ - while (!(inb((void *)((uintptr_t)ttypc->kbd + 4)) & 0x01)) - condWait(ttypc->kcond, ttypc->klock, 0); + for (;;) { + status = ttypc_ps2_read_ctrl(ttypc); + if (KBD_OUTPUT_PENDING(status) != 0u) { + break; + } + else if (MOUSE_OUTPUT_PENDING(status) != 0u) { + ttypc_mouse_handle_event(ttypc); + } + else { + condWait(ttypc->kmcond, ttypc->klock, 0); + } + } mutexLock(ttypc->lock); mutexLock((cvt = ttypc->vt)->lock); @@ -431,6 +463,7 @@ static void ttypc_kbd_ctlthr(void *arg) continue; } + /* Scroll up one line */ if (!strcmp(s, "\033[A") && ((ttypc->lockst & KB_SCROLL) || (ttypc->shiftst == (KB_CTL | KB_SHIFT)))) { _ttypc_vga_scroll(cvt, 1); @@ -501,51 +534,44 @@ static void ttypc_kbd_ctlthr(void *arg) } -/* Waits for keyboard controller status bit with small timeout */ -static int ttypc_kbd_waitstatus(ttypc_t *ttypc, unsigned char bit, unsigned char state) +#if PC_TTY_CREATE_PS2_VDEVS +static void ttypc_kbd_poolthr(void *arg) { - unsigned int i; - - for (i = 0; i < 0xffff; i++) { - if (!(inb((void *)((uintptr_t)ttypc->kbd + 4)) & ((1 << bit) ^ (state << bit)))) - return EOK; - usleep(10); - } - - return -ETIMEDOUT; -} - - -/* Reads a byte from keyboard controller output buffer */ -/* - * FIXME: (unused) Function not to be removed, needs to be preserved - * for future implementation of ps2-aux (mouse device) support. - */ -__attribute__((unused)) static int ttypc_kbd_read(ttypc_t *ttypc) -{ - int err; + ttypc_t *ttypc = (ttypc_t *)arg; + msg_rid_t rid; + msg_t msg; - /* Wait for output buffer not to be empty */ - if ((err = ttypc_kbd_waitstatus(ttypc, 0, 1)) < 0) - return err; + for (;;) { + if (msgRecv(ttypc->kport, &msg, &rid) < 0) { + continue; + } - return inb((void *)ttypc->kbd); -} + if (libklog_ctrlHandle(ttypc->kport, &msg, rid) == 0) { + /* msg has been handled by libklog */ + continue; + } + switch (msg.type) { + case mtOpen: + msg.o.err = EOK; + break; -/* Writes a byte to keyboard controller input buffer */ -static int ttypc_kbd_write(ttypc_t *ttypc, unsigned char byte) -{ - int err; + case mtRead: + msg.o.err = event_queue_get(&ttypc->keq, msg.o.data, 1, 0); + break; - /* Wait for input buffer to be empty */ - if ((err = ttypc_kbd_waitstatus(ttypc, 1, 0)) < 0) - return err; + case mtClose: + break; - outb((void *)ttypc->kbd, byte); + default: + msg.o.err = -ENOSYS; + break; + } - return EOK; + msgRespond(ttypc->kport, &msg, rid); + } } +#endif /* May not work for PS/2 emulation through USB legacy support */ @@ -553,12 +579,15 @@ int _ttypc_kbd_updateled(ttypc_t *ttypc) { do { /* Send update LEDs command */ - if (ttypc_kbd_write(ttypc, 0xed) < 0) + if (ttypc_ps2_write(ttypc, 0xed) < 0) { break; + } /* Send LEDs state */ - if (ttypc_kbd_write(ttypc, (ttypc->lockst >> 4) & 0x07) < 0) + + if (ttypc_ps2_write(ttypc, (ttypc->lockst >> 4u) & 0x07) < 0) { break; + } return 1; } while (0); @@ -569,9 +598,14 @@ int _ttypc_kbd_updateled(ttypc_t *ttypc) void ttypc_kbd_destroy(ttypc_t *ttypc) { - resourceDestroy(ttypc->klock); - resourceDestroy(ttypc->kcond); + ttypc_mouse_destroy(ttypc); resourceDestroy(ttypc->kinth); + resourceDestroy(ttypc->kmcond); + resourceDestroy(ttypc->klock); +#if PC_TTY_CREATE_PS2_VDEVS + event_queue_destroy(&ttypc->keq); + portDestroy(ttypc->kport); +#endif } @@ -594,34 +628,89 @@ int ttypc_kbd_init(ttypc_t *ttypc) /* Configure typematic */ do { /* Send set typematic rate/delay command */ - if (ttypc_kbd_write(ttypc, 0xf3) < 0) + if (ttypc_ps2_write(ttypc, 0xf3) < 0) { break; + } /* 250 ms / 30.0 reports/sec */ - if (ttypc_kbd_write(ttypc, 0) < 0) + if (ttypc_ps2_write(ttypc, 0) < 0) { break; + } } while (0); - if ((err = mutexCreate(&ttypc->klock)) < 0) - return err; +#if PC_TTY_CREATE_PS2_VDEVS + oid_t oid; + + /* Wait for the filesystem */ + while (lookup("/", NULL, &oid) < 0) { + usleep(10000); + } - if ((err = condCreate(&ttypc->kcond)) < 0) { - resourceDestroy(ttypc->klock); + /* Create virtual keyboard device */ + err = portCreate(&ttypc->kport); + if (err < 0) { + (void)fprintf(stderr, "pc-tty: failed to create keyboard port\n"); return err; } - /* Attach interrupt */ - if ((err = interrupt((ttypc->kirq = 1), ttypc_kbd_interrupt, ttypc, ttypc->kcond, &ttypc->kinth)) < 0) { - resourceDestroy(ttypc->klock); - resourceDestroy(ttypc->kcond); + oid.port = ttypc->kport; + oid.id = 0; + + err = create_dev(&oid, "/dev/kbd"); + if (err < 0) { + (void)fprintf(stderr, "pc-tty: failed to register kbd device\n"); return err; } +#endif + + do { + err = mutexCreate(&ttypc->klock); + if (err < 0) { + break; + } + + err = condCreate(&ttypc->kmcond); + if (err < 0) { + break; + } + +#if PC_TTY_CREATE_PS2_VDEVS + err = event_queue_init(&ttypc->keq); + if (err < 0) { + break; + } +#endif + + /* Attach KIRQ1 (kbd event) interrupt handle */ + err = interrupt((ttypc->kirq = 1), _ttypc_kbd_interrupt, ttypc, ttypc->kmcond, &ttypc->kinth); + if (err < 0) { + break; + } + + /* Initialize mouse */ + err = ttypc_mouse_init(ttypc); + if (err < 0) { + break; + } + + /* Launch keyboard/mouse control thread */ + err = beginthread(ttypc_kbd_ctlthr, CTL_THREAD_PRIORITY, ttypc->kstack, sizeof(ttypc->kstack), ttypc); + if (err < 0) { + break; + } + +#if PC_TTY_CREATE_PS2_VDEVS + /* Launch keyboard pool thread */ + err = beginthread(ttypc_kbd_poolthr, POOL_THREAD_PRIORITY, ttypc->kpstack, sizeof(ttypc->kpstack), ttypc); + if (err < 0) { + break; + } +#endif + + } while (0); - /* Launch keyboard control thread */ - if ((err = beginthread(ttypc_kbd_ctlthr, 1, ttypc->kstack, sizeof(ttypc->kstack), ttypc)) < 0) { - resourceDestroy(ttypc->klock); - resourceDestroy(ttypc->kcond); - resourceDestroy(ttypc->kinth); + if (err < 0) { + ttypc_kbd_destroy(ttypc); return err; } diff --git a/tty/pc-tty/ttypc_mouse.c b/tty/pc-tty/ttypc_mouse.c new file mode 100644 index 000000000..e845ecb89 --- /dev/null +++ b/tty/pc-tty/ttypc_mouse.c @@ -0,0 +1,347 @@ +/* + * Phoenix-RTOS + * + * PS/2 3-button mouse + * + * Copyright 2024 Phoenix Systems + * Author: Adam Greloch + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include + +#include "board_config.h" +#include "ttypc_mouse.h" + +#if PC_TTY_MOUSE_ENABLE + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "event_queue.h" +#include "ttypc_ps2.h" + + +#define POOL_THREAD_PRIORITY 1 + + +static int _ttypc_mouse_setup(ttypc_t *ttypc) +{ + unsigned char status; + int err; + + do { + +/* Some BIOS-es do this part of configuration already and +redoing it may not be necessary or could even hang the PS/2 +controller completely (as is the case on ThinkPad/Phoenix BIOS 2.26) */ +#if !PC_TTY_MOUSE_SKIP_CONFIG + + /* Enable mouse. Should receive ACK (0xFA) afterwards */ + err = ttypc_ps2_write_ctrl(ttypc, 0xA8); + if (err < 0) { + break; + } + err = ttypc_ps2_read(ttypc); + if (err != 0xFA) { + (void)fprintf(stderr, "pc-tty: mouse not found\n"); + err = -1; + break; + } + + /* Get compaq status byte */ + err = ttypc_ps2_write_ctrl(ttypc, 0x20); + if (err < 0) { + break; + } + + status = ttypc_ps2_read(ttypc); + status |= (1u << 1u); /* Enable IRQ12 interrupt */ + +/* Some sources say the bit 5 should be unset for proper functioning, + but on QEMU and real hardware it is not necessary */ +#if 0 + status &= ~(1u << 5u); /* Enable mouse clock (unset bit 5) */ +#endif + + /* Set compaq status byte */ + err = ttypc_ps2_write_ctrl(ttypc, 0x60); + if (err < 0) { + break; + } + err = ttypc_ps2_write(ttypc, status); + if (err < 0) { + break; + } + +#endif + + /* Set defaults. Should receive ACK afterwards */ + err = ttypc_ps2_write_ctrl(ttypc, 0xD4); + if (err < 0) { + break; + } + err = ttypc_ps2_write(ttypc, 0xF6); + if (err < 0) { + break; + } + err = ttypc_ps2_read(ttypc); + if (err != 0xFA) { + (void)fprintf(stderr, "pc-tty: mouse initialization failed - no ACK after setting defaults\n"); + err = -1; + break; + } + + /* Send mouse cmd - enable packet streaming. Should receive ACK afterwards */ + err = ttypc_ps2_write_ctrl(ttypc, 0xD4); + if (err < 0) { + break; + } + err = ttypc_ps2_write(ttypc, 0xF4); + if (err < 0) { + break; + } + err = ttypc_ps2_read(ttypc); + if (err != 0xFA) { + (void)fprintf(stderr, "pc-tty: mouse initialization failed - no ACK after enable packet streaming cmd\n"); + err = -1; + break; + } + } while (0); + + if (err < 0) { + (void)fprintf(stderr, "pc-tty: failed to initialize mouse\n"); + return -1; + } + + return EOK; +} + + +#if PC_TTY_CREATE_PS2_VDEVS +static void _ttypc_kbd_mouse_poolthr(void *arg) +{ + ttypc_t *ttypc = (ttypc_t *)arg; + msg_rid_t rid; + msg_t msg; + + for (;;) { + if (msgRecv(ttypc->mport, &msg, &rid) < 0) { + continue; + } + + if (libklog_ctrlHandle(ttypc->mport, &msg, rid) == 0) { + /* msg has been handled by libklog */ + continue; + } + + switch (msg.type) { + case mtOpen: + msg.o.err = EOK; + break; + + case mtRead: + if (msg.o.size < 3) { + /* Pretend there's nothing to read. We don't want someone to + read partial mouse packets - this would mess up byte ordering + and confuse recipients. */ + msg.o.err = 0; + break; + } + + /* TODO allow for reading multiples of 3 */ + msg.o.err = event_queue_get(&ttypc->meq, msg.o.data, 3, msg.i.io.mode); + + /* 3rd bit should be set in the first byte of mouse package */ + while ((((char *)msg.o.data)[0] & 0x8) == 0) { + /* this is not a first byte, scroll through the buffer */ + + msg.o.err = event_queue_get(&ttypc->meq, msg.o.data, 1, msg.i.io.mode); + + if ((((char *)msg.o.data)[0] & 0x8) != 0) { + /* found the first byte, get the other two */ + msg.o.err += event_queue_get(&ttypc->meq, (char *)msg.o.data + 1, 2, msg.i.io.mode); + break; + } + } + + break; + + case mtWrite: + if (ttypc_ps2_write(ttypc, ((unsigned char *)msg.i.data)[0]) < 0) { + msg.o.err = -EINVAL; + break; + } + msg.o.err = 1; + break; + + case mtGetAttr: + if ((msg.i.attr.type != atPollStatus)) { + msg.o.err = -EINVAL; + break; + } + + msg.o.attr.val = 0; + if (event_queue_count(&ttypc->meq) >= 3) { + msg.o.attr.val |= POLLIN; + } + + msg.o.err = EOK; + break; + + case mtClose: + break; + + default: + msg.o.err = -ENOSYS; + break; + } + + msgRespond(ttypc->mport, &msg, rid); + } +} +#endif + + +/* Mouse interrupt handler */ +static int _ttypc_mouse_interrupt(unsigned int n, void *arg) +{ + return 0; +} + + +int ttypc_mouse_init(ttypc_t *ttypc) +{ + int err; + + do { + err = _ttypc_mouse_setup(ttypc); + if (err < 0) { + break; + } + +#if PC_TTY_CREATE_PS2_VDEVS + oid_t oid; + + /* Create virtual mouse device. Assumes the filesystem is available already */ + err = portCreate(&ttypc->mport); + if (err < 0) { + (void)fprintf(stderr, "pc-tty: failed to create mouse port\n"); + break; + } + oid.port = ttypc->mport; + oid.id = 0; + + err = create_dev(&oid, "/dev/mouse"); + if (err < 0) { + (void)fprintf(stderr, "pc-tty: failed to register mouse device\n"); + portDestroy(ttypc->mport); + break; + } + + err = event_queue_init(&ttypc->meq); + if (err < 0) { + portDestroy(ttypc->mport); + break; + } +#endif + + /* Attach KIRQ12 (mouse event) interrupt handle */ + err = interrupt((ttypc->mirq = 12), _ttypc_mouse_interrupt, ttypc, ttypc->kmcond, &ttypc->minth); + if (err < 0) { +#if PC_TTY_CREATE_PS2_VDEVS + portDestroy(ttypc->mport); + event_queue_destroy(&ttypc->meq); +#endif + break; + } + +#if PC_TTY_CREATE_PS2_VDEVS + /* Launch mouse pool thread */ + err = beginthread(_ttypc_kbd_mouse_poolthr, POOL_THREAD_PRIORITY, ttypc->mpstack, sizeof(ttypc->mpstack), ttypc); + if (err < 0) { + portDestroy(ttypc->mport); + event_queue_destroy(&ttypc->meq); + resourceDestroy(ttypc->minth); + break; + } +#endif + } while (0); + + if (err < 0) { + /* If mouse initialization has failed at any point, it's better to disable mouse + so that it doesn't clutter the I/O port with events we won't handle */ + + /* Disable mouse */ + err = ttypc_ps2_write_ctrl(ttypc, 0xA7); + if (err < 0) { + return err; + } + + /* Read a byte in case mouse responded. This could be ACK (0xFA) + or something else if mouse is broken. Ignore return value - if + read fails, it's ok. */ + (void)ttypc_ps2_read(ttypc); + } + + return err; +} + + +int ttypc_mouse_handle_event(ttypc_t *ttypc) +{ +#if PC_TTY_CREATE_PS2_VDEVS + unsigned char b; + int err; + + b = inb((void *)ttypc->kbd); + err = event_queue_put(&ttypc->meq, b, 3); + + return err; +#else + (void)inb((void *)ttypc->kbd); + return EOK; +#endif +} + + +void ttypc_mouse_destroy(ttypc_t *ttypc) +{ + resourceDestroy(ttypc->minth); +#if PC_TTY_CREATE_PS2_VDEVS + event_queue_destroy(&ttypc->meq); + portDestroy(ttypc->mport); +#endif +} + +#else + +int ttypc_mouse_handle_event(ttypc_t *ttypc) +{ + fprintf(stderr, "pc-tty: ttypc_mouse_handle_event() called while mouse disabled\n"); + return -1; +} + + +int ttypc_mouse_init(ttypc_t *ttypc) +{ + return EOK; +} + + +void ttypc_mouse_destroy(ttypc_t *ttypc) +{ +} + +#endif diff --git a/tty/pc-tty/ttypc_mouse.h b/tty/pc-tty/ttypc_mouse.h new file mode 100644 index 000000000..b06ed541f --- /dev/null +++ b/tty/pc-tty/ttypc_mouse.h @@ -0,0 +1,32 @@ +/* + * Phoenix-RTOS + * + * PS/2 3-button mouse + * + * Copyright 2024 Phoenix Systems + * Author: Adam Greloch + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _TTYPC_MOUSE_H_ +#define _TTYPC_MOUSE_H_ + +#include "ttypc_vt.h" + + +/* Initializes PS/2 mouse */ +extern int ttypc_mouse_init(ttypc_t *ttypc); + + +/* Reads a mouse event from I/O buffer and handles it */ +extern int ttypc_mouse_handle_event(ttypc_t *ttypc); + + +/* Destroys PS/2 mouse */ +extern void ttypc_mouse_destroy(ttypc_t *ttypc); + + +#endif diff --git a/tty/pc-tty/ttypc_ps2.c b/tty/pc-tty/ttypc_ps2.c new file mode 100644 index 000000000..0a927fa80 --- /dev/null +++ b/tty/pc-tty/ttypc_ps2.c @@ -0,0 +1,100 @@ +/* + * Phoenix-RTOS + * + * PS/2 common utilities + * + * Copyright 2024 Phoenix Systems + * Author: Adam Greloch + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include + +#include +#include + +#include "ttypc_vt.h" + + +#define SLEEP_MS 10u +#define MAX_TIMEOUT_DEFAULT_MS 10000u + + +/* Waits for PS/2 controller status bit with small timeout */ +static int _ttypc_ps2_waitstatus(ttypc_t *ttypc, unsigned int bit, unsigned int state, unsigned int max_timeout) +{ + unsigned int i, b, rounds; + + if (max_timeout == 0u) { + max_timeout = MAX_TIMEOUT_DEFAULT_MS; + } + + rounds = max(1, max_timeout / SLEEP_MS); + + for (i = 0; i < rounds; i++) { + b = inb((void *)((uintptr_t)ttypc->kbd + 4u)); + + b &= (1u << bit) ^ (state << bit); + if (b == 0u) { + return EOK; + } + + if (i < rounds - 1u) { + usleep(SLEEP_MS * 1000); + } + } + + return -ETIMEDOUT; +} + + +static int _ttypc_ps2_write_to_port(ttypc_t *ttypc, void *port, unsigned char byte) +{ + int err; + + /* Wait for input buffer to be empty */ + err = _ttypc_ps2_waitstatus(ttypc, 1, 0, 0); + if (err < 0) { + return err; + } + + outb(port, byte); + + return EOK; +} + + +int ttypc_ps2_write(ttypc_t *ttypc, unsigned char byte) +{ + return _ttypc_ps2_write_to_port(ttypc, (void *)ttypc->kbd, byte); +} + + +int ttypc_ps2_read(ttypc_t *ttypc) +{ + int err; + + /* Wait for output buffer not to be empty */ + err = _ttypc_ps2_waitstatus(ttypc, 0, 1, 0); + if (err < 0) { + return err; + } + + return inb((void *)ttypc->kbd); +} + + +int ttypc_ps2_write_ctrl(ttypc_t *ttypc, unsigned char byte) +{ + return _ttypc_ps2_write_to_port(ttypc, (void *)((uintptr_t)ttypc->kbd + 4u), byte); +} + + +unsigned char ttypc_ps2_read_ctrl(ttypc_t *ttypc) +{ + return inb((void *)((uintptr_t)ttypc->kbd + 4u)); +} diff --git a/tty/pc-tty/ttypc_ps2.h b/tty/pc-tty/ttypc_ps2.h new file mode 100644 index 000000000..5d5db1e20 --- /dev/null +++ b/tty/pc-tty/ttypc_ps2.h @@ -0,0 +1,36 @@ +/* + * Phoenix-RTOS + * + * PS/2 common utilities + * + * Copyright 2024 Phoenix Systems + * Author: Adam Greloch + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _TTYPC_PS2_H_ +#define _TTYPC_PS2_H_ + +#include "ttypc_vt.h" + + +/* Writes a byte to PS/2 control buffer */ +extern int ttypc_ps2_write_ctrl(ttypc_t *ttypc, unsigned char byte); + + +/* Reads a byte from PS/2 I/O buffer */ +extern int ttypc_ps2_read(ttypc_t *ttypc); + + +/* Writes a byte to PS/2 I/O buffer */ +extern int ttypc_ps2_write(ttypc_t *ttypc, unsigned char byte); + + +/* Reads a byte from PS/2 control buffer */ +extern unsigned char ttypc_ps2_read_ctrl(ttypc_t *ttypc); + + +#endif