[SeaBIOS] [PATCH] keyboard: Inject serial port input as keypresses

Kyösti Mälkki kyosti.malkki at gmail.com
Fri May 20 13:31:09 CEST 2016


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 at 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);
-- 
1.9.1




More information about the SeaBIOS mailing list