Maps characters received from serial port as INT16h keypresses. This enables making choice of boot media over serial port.
Source extracted from SageBIOS release for PC Engines apu1. af6c8ab3b85d1a5a9fbeb41efa30a1ef pcengines.apu_139_osp.tar.gz
Signed-off-by: Kyösti Mälkki kyosti.malkki@gmail.com --- src/Kconfig | 6 +++ src/clock.c | 4 ++ src/kbd.c | 2 +- src/serial.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 2 + 5 files changed, 168 insertions(+), 1 deletion(-)
diff --git a/src/Kconfig b/src/Kconfig index e767be1..dedce08 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -411,6 +411,12 @@ menu "BIOS interfaces" default y help Support int 16 keyboard calls. + config INT16_SERIAL_KEYBOARD + bool "Keyboard on serial port" + depends on SERIAL && KEYBOARD + default n + help + Translate serial port input to keyboard scancodes. config KBD_CALL_INT15_4F depends on KEYBOARD bool "Keyboard hook interface" diff --git a/src/clock.c b/src/clock.c index e83e0f3..b69a139 100644 --- a/src/clock.c +++ b/src/clock.c @@ -295,6 +295,10 @@ clock_update(void) floppy_tick(); usb_check_event(); ps2_check_event(); + +#if CONFIG_INT16_SERIAL_KEYBOARD + uart_check_keystrokes(); +#endif }
// INT 08h System Timer ISR Entry Point diff --git a/src/kbd.c b/src/kbd.c index 61d9df0..48c7d06 100644 --- a/src/kbd.c +++ b/src/kbd.c @@ -51,7 +51,7 @@ kbd_init(void) , x + FIELD_SIZEOF(struct bios_data_area_s, kbd_buf)); }
-static u8 +u8 enqueue_key(u16 keycode) { u16 buffer_start = GET_BDA(kbd_buf_start_offset); diff --git a/src/serial.c b/src/serial.c index 88349c8..8c93411 100644 --- a/src/serial.c +++ b/src/serial.c @@ -315,3 +315,158 @@ handle_17(struct bregs *regs) default: handle_17XX(regs); break; } } + +#if CONFIG_INT16_SERIAL_KEYBOARD +static u8 UartToScanCode[] VAR16 = { +// x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf +// ===================================================================================================== + 0x0f, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, // 0x + 0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, // 1x + 0x39, 0x02, 0x28, 0x04, 0x05, 0x06, 0x08, 0x28, 0x0a, 0x0b, 0x09, 0x0d, 0x33, 0x0c, 0x34, 0x35, // 2x + 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x27, 0x27, 0x33, 0x0d, 0x34, 0x35, // 3x + 0x03, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, // 4x + 0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x1a, 0x2b, 0x1b, 0x07, 0x0c, // 5x + 0x29, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, // 6x + 0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x1a, 0x2b, 0x1b, 0x29, 0x00, // 7x +}; + +static u8 +inject_key(u8 scan_code, u8 ascii_code) +{ + return enqueue_key((u16) scan_code << 8 | ascii_code); +} + +void +uart_check_keystrokes(void) +{ + u8 rx_buf[5], rx_bytes = 0, ascii_code = 0, scan_code = 0; + + // check to see if there is a active serial port + if (inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_LSR) == 0xFF) + return; + + while (inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_LSR) & 0x01) { + if (rx_bytes > sizeof(rx_buf)) { + dprintf(1, "uart_check_keystrokes: error too many bytes are available\n"); + while (inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_LSR) & 0x01) + inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_DATA); + return; + } + else + rx_buf[rx_bytes++] = inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_DATA); + } + + if (!rx_bytes) + return; + + if (rx_bytes == 1) { + ascii_code = rx_buf[0]; + if (ascii_code >= ARRAY_SIZE(UartToScanCode)) { + dprintf(3, "uart_check_keystrokes: error UartToScanCode out of bounds index\n"); + return; + } + scan_code = GET_GLOBAL(UartToScanCode[ascii_code]); + inject_key(scan_code, ascii_code); + } + else if (rx_bytes == 2) { // assume it's actually 2 single-byte keystrokes + ascii_code = rx_buf[0]; + if (ascii_code >= ARRAY_SIZE(UartToScanCode)) { + dprintf(3, "uart_check_keystrokes: error in 1/2 byte UartToScanCode out of bounds index\n"); + return; + } + scan_code = GET_GLOBAL(UartToScanCode[ascii_code]); + inject_key(scan_code, ascii_code); + + ascii_code = rx_buf[1]; + if (ascii_code >= ARRAY_SIZE(UartToScanCode)) { + dprintf(3, "uart_check_keystrokes: error in 2/2 UartToScanCode out of bounds index\n"); + return; + } + scan_code = GET_GLOBAL(UartToScanCode[ascii_code]); + inject_key(scan_code, ascii_code); + } + else if (rx_bytes == 3) { + if ((rx_buf[0] == 0x1b) && (rx_buf[1] == 0x4f)) { // F1-F12 + ascii_code = 0; + if ((rx_buf[2] >= 0x50) && (rx_buf[2] <= 0x59)) + scan_code = (rx_buf[2] - 0x50) +0x3b; + else if ((rx_buf[2] >= 0x5a) && (rx_buf[2] <= 0x5b)) + scan_code = (rx_buf[2] - 0x5a) + 0x85; + else { + dprintf(3, "uart_check_keystrokes: error in Fkey handling %x\n",rx_buf[2]); + return; + } + } + else if ((rx_buf[0] == 0x1b) && (rx_buf[1] == 0x5b)) { // cursor keys + ascii_code = 0xe0; + if (rx_buf[2] == 0x41) scan_code = 0x48; // UP + else if (rx_buf[2] == 0x42) scan_code = 0x50; // DOWN + else if (rx_buf[2] == 0x43) scan_code = 0x4d; // LEFT + else if (rx_buf[2] == 0x44) scan_code = 0x4b; // RIGHT + else { + dprintf(3, "uart_check_keystrokes: error in cursor handling %x\n",rx_buf[2]); + return; + } + } + else { + dprintf(3, "uart_check_keystrokes: error in 3 byte key sequence\n"); + return; + } + inject_key(scan_code, ascii_code); + } + else if (rx_bytes == 4) { + if ((rx_buf[0] == 0x1b) && (rx_buf[1] == 0x5b) && (rx_buf[2] == 0x33) && (rx_buf[3] == 0x7e)) { // DEL + ascii_code = 0xe0; + scan_code = 0x53; + inject_key(scan_code, ascii_code); + } + else { + dprintf(3, "uart_check_keystrokes: unhandled 4 byte keystroke "); + dprintf(3, "%x %x %x %x\n",rx_buf[0],rx_buf[1],rx_buf[2],rx_buf[3]); + return; + } + } + /* these 5 byte scan codes are used by some terminal emulators */ + else if (rx_bytes == 5) { + if ((rx_buf[0] == 0x1b) && (rx_buf[1] == 0x5b) && (rx_buf[2] == 0x32) && (rx_buf[4] == 0x7e)) { // F9-F12 + ascii_code = 0x00; + if (rx_buf[3] == 0x30) scan_code = 0x43; // F9 + else if (rx_buf[3] == 0x31) scan_code = 0x44; // F10 + else if (rx_buf[3] == 0x33) scan_code = 0x85; // F11 + else if (rx_buf[3] == 0x34) scan_code = 0x86; // F12 + else { + dprintf(3, "uart_check_keystrokes: unhandled 5 byte keystroke F9-F12 "); + dprintf(3, "%x %x %x %x %x\n",rx_buf[0],rx_buf[1],rx_buf[2],rx_buf[3],rx_buf[4]); + return; + } + } + else if ((rx_buf[0] == 0x1b) && (rx_buf[1] == 0x5b) && (rx_buf[2] == 0x31) && (rx_buf[4] == 0x7e)) { // F1-F8 + ascii_code = 0x00; + if (rx_buf[3] == 0x31) scan_code = 0x3B; // F1 + else if (rx_buf[3] == 0x32) scan_code = 0x3C; // F2 + else if (rx_buf[3] == 0x33) scan_code = 0x3D; // F3 + else if (rx_buf[3] == 0x34) scan_code = 0x3E; // F4 + else if (rx_buf[3] == 0x35) scan_code = 0x3F; // F5 + else if (rx_buf[3] == 0x37) scan_code = 0x40; // F6 + else if (rx_buf[3] == 0x38) scan_code = 0x41; // F7 + else if (rx_buf[3] == 0x39) scan_code = 0x42; // F8 + else { + dprintf(3, "uart_check_keystrokes: unhandled 5 byte keystroke F1-F8 "); + dprintf(3, "%x %x %x %x %x\n",rx_buf[0],rx_buf[1],rx_buf[2],rx_buf[3],rx_buf[4]); + return; + } + } + else { + dprintf(3, "uart_check_keystrokes: unhandled 5 byte keystroke "); + dprintf(3, "%x %x %x %x %x\n",rx_buf[0],rx_buf[1],rx_buf[2],rx_buf[3],rx_buf[4]); + return; + } + inject_key(scan_code, ascii_code); + } + else { + dprintf(3, "uart_check_keystrokes: unhandled rx_bytes = %x\n",rx_bytes); + dprintf(3, "%x %x %x %x %x\n",rx_buf[0],rx_buf[1],rx_buf[2],rx_buf[3],rx_buf[4]); + return; + } +} +#endif /* CONFIG_INT16_SERIAL_KEYBOARD */ diff --git a/src/util.h b/src/util.h index 7b41207..9be308a 100644 --- a/src/util.h +++ b/src/util.h @@ -184,6 +184,7 @@ int jpeg_show(struct jpeg_decdata *jpeg, unsigned char *pic, int width void kbd_init(void); void handle_15c2(struct bregs *regs); void process_key(u8 key); +u8 enqueue_key(u16 keycode);
// misc.c extern int HaveRunPost; @@ -230,6 +231,7 @@ void code_mutable_preinit(void); // serial.c void serial_setup(void); void lpt_setup(void); +void uart_check_keystrokes(void);
// vgahooks.c void handle_155f(struct bregs *regs);
On Fri, May 20, 2016 at 02:31:09PM +0300, Kyösti Mälkki wrote:
Maps characters received from serial port as INT16h keypresses. This enables making choice of boot media over serial port.
Source extracted from SageBIOS release for PC Engines apu1. af6c8ab3b85d1a5a9fbeb41efa30a1ef pcengines.apu_139_osp.tar.gz
Is there a reason to do this in SeaBIOS versus just using sgabios? The sgabios code seems to have more features (eg, vga output over serial port).
See some additional comments below.
[...]
--- a/src/Kconfig +++ b/src/Kconfig @@ -411,6 +411,12 @@ menu "BIOS interfaces" default y help Support int 16 keyboard calls.
- config INT16_SERIAL_KEYBOARD
bool "Keyboard on serial port"
depends on SERIAL && KEYBOARD
default n
help
Translate serial port input to keyboard scancodes.
This would also need to depend on CONFIG_DEBUG_SERIAL (as the code makes references to CONFIG_DEBUG_SERIAL_PORT), so should probably be in the debug menu.
[...]
--- a/src/clock.c +++ b/src/clock.c @@ -295,6 +295,10 @@ clock_update(void) floppy_tick(); usb_check_event(); ps2_check_event();
+#if CONFIG_INT16_SERIAL_KEYBOARD
- uart_check_keystrokes();
+#endif
SeaBIOS avoids #if in code - it should read as "if (CONFIG_xyz) func()".
[...]
--- a/src/serial.c +++ b/src/serial.c @@ -315,3 +315,158 @@ handle_17(struct bregs *regs) default: handle_17XX(regs); break; } }
+#if CONFIG_INT16_SERIAL_KEYBOARD +static u8 UartToScanCode[] VAR16 = { +// x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf +// =====================================================================================================
- 0x0f, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, // 0x
To fit with SeaBIOS style, the code should be limited to 80 characters in length.
[...]
+void +uart_check_keystrokes(void) +{
- u8 rx_buf[5], rx_bytes = 0, ascii_code = 0, scan_code = 0;
- // check to see if there is a active serial port
- if (inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_LSR) == 0xFF)
return;
- while (inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_LSR) & 0x01) {
if (rx_bytes > sizeof(rx_buf)) {
dprintf(1, "uart_check_keystrokes: error too many bytes are available\n");
while (inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_LSR) & 0x01)
inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_DATA);
return;
}
else
rx_buf[rx_bytes++] = inb(CONFIG_DEBUG_SERIAL_PORT + SEROFF_DATA);
- }
- if (!rx_bytes)
return;
- if (rx_bytes == 1) {
This does not look correct to me - the number of serial port characters read at one time is likely to be pretty random - it's going to depend on the speed of the serial port vs the speed of the timer interrupt that polls the port. So, this seems like there is a good chance it would lead to some vt100 sequences not being interpretted and to some regular keys being dropped due to being misinterpreted as extended sequences.
FWIW, the sgabios code seems to handle the above correctly.
[...]
- }
- else if (rx_bytes == 2) { // assume it's actually 2 single-byte keystrokes
To keep with SeaBIOS style, "else" statements should be put on the same line as the preceeding curly bracket.
Thanks, -Kevin