On Fri, Jul 01, 2016 at 12:54:31PM +0200, Gerd Hoffmann wrote:
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
src/clock.c | 1 + src/serial.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 1 + 3 files changed, 257 insertions(+)
diff --git a/src/clock.c b/src/clock.c index e83e0f3..e44e112 100644 --- a/src/clock.c +++ b/src/clock.c @@ -295,6 +295,7 @@ clock_update(void) floppy_tick(); usb_check_event(); ps2_check_event();
- sercon_check_event();
}
// INT 08h System Timer ISR Entry Point diff --git a/src/serial.c b/src/serial.c index 74b91bb..d72dd01 100644 --- a/src/serial.c +++ b/src/serial.c @@ -655,3 +655,258 @@ void sercon_enable(void) outb(0x01, addr + 0x02); // enable fifo enable_vga_console(); }
+/****************************************************************
- serial input
- ****************************************************************/
+VARLOW u8 rx_buf[16]; +VARLOW u8 rx_bytes;
+VARLOW struct {
- char seq[4];
- u8 len;
- u8 scancode;
+} termseq[] = {
- { .seq = "OP", .len = 2, .scancode = 0x3b }, // F1
- { .seq = "OQ", .len = 2, .scancode = 0x3c }, // F2
- { .seq = "OR", .len = 2, .scancode = 0x3d }, // F3
- { .seq = "OS", .len = 2, .scancode = 0x3e }, // F4
- { .seq = "[A", .len = 2, .scancode = 0xc8 }, // up
- { .seq = "[B", .len = 2, .scancode = 0xd0 }, // down
- { .seq = "[C", .len = 2, .scancode = 0xcd }, // right
- { .seq = "[D", .len = 2, .scancode = 0xcb }, // left
+};
It would be preferable to mark constant data with "static VAR16" instead of VARLOW.
+#define FLAG_CTRL (1<<0) +#define FLAG_SHIFT (1<<1)
+VARLOW struct {
- u8 flags;
- u8 scancode;
+} termchr[256] = {
- [ '1' ] = { .scancode = 0x02, },
I think this table should be generated at runtime from kbd.c:scan_to_keycode[]. Since it doesn't change at runtime, malloc_fseg() / GET_GLOBAL() could be used instead of VARLOW.
[...]
+static void sercon_sendkey(u8 scancode, u8 flags) +{
- if (flags & FLAG_CTRL)
process_key(0x1d);
- if (flags & FLAG_SHIFT)
process_key(0x2a);
- if (scancode & 0x80) {
process_key(0xe0);
process_key(scancode & ~0x80);
process_key(0xe0);
process_key(scancode);
- } else {
process_key(scancode);
process_key(scancode | 0x80);
- }
- if (flags & FLAG_SHIFT)
process_key(0x2a | 0x80);
- if (flags & FLAG_CTRL)
process_key(0x1d | 0x80);
+}
Is it necessary to use process_key() here instead of injecting the keycode directly with enqueue_key()? I think the only difference is the CONFIG_KBD_CALL_INT15_4F stuff and I'm not sure if anything interesting needs that.
+void VISIBLE16 +sercon_check_event(void)
Does this need VISIBLE16?
+{
- u16 addr = GET_LOW(sercon_port);
- u8 byte, scancode, flags, count = 0;
- int seq, chr, len;
- // check to see if there is a active serial port
- if (!addr)
return;
- if (inb(addr + SEROFF_LSR) == 0xFF)
return;
- // flush pending output
- sercon_flush_lazy();
- // read all available data
- while (inb(addr + SEROFF_LSR) & 0x01) {
byte = inb(addr + SEROFF_DATA);
if (GET_LOW(rx_bytes) < sizeof(rx_buf)) {
SET_LOW(rx_buf[rx_bytes], byte);
SET_LOW(rx_bytes, GET_LOW(rx_bytes) + 1);
count++;
}
- }
+next_char:
- // no (more) input data
- if (!GET_LOW(rx_bytes))
return;
- // lookup escape sequences
- if (GET_LOW(rx_bytes) > 1 && GET_LOW(rx_buf[0]) == 0x1b) {
for (seq = 0; seq < ARRAY_SIZE(termseq); seq++) {
len = GET_LOW(termseq[seq].len);
if (GET_LOW(rx_bytes) < len + 1)
continue;
for (chr = 0; chr < len; chr++) {
if (GET_LOW(termseq[seq].seq[chr]) != GET_LOW(rx_buf[chr + 1]))
break;
}
if (chr == len) {
scancode = GET_LOW(termseq[seq].scancode);
sercon_sendkey(scancode, 0);
shiftbuf(len + 1);
goto next_char;
}
}
- }
- // Seems we got a escape sequence we didn't recognise.
- // -> If we received data wait for more, maybe it is just incomplete.
- if (GET_LOW(rx_buf[0]) == 0x1b && count)
return;
- // Handle input as individual chars.
- chr = GET_LOW(rx_buf[0]);
- scancode = GET_LOW(termchr[chr].scancode);
- flags = GET_LOW(termchr[chr].flags);
- if (scancode)
sercon_sendkey(scancode, flags);
- shiftbuf(1);
- goto next_char;
+}
If I understand correctly, most keys are sent on the serial port as single bytes, but there are a few keys that are sent as multi-byte sequences. There's a lot of complexity to implement buffering for that unusual case. I wonder if the buffer could be avoided - I played with it a little and came up with the below (totally untested). I'm not sure if it's an improvement.
-Kevin
u8 multibyte_read_pos VARLOW; u8 multibyte_read_count VARLOW;
void sercon_check_event(void) { u16 addr = GET_LOW(sercon_port); ...
u8 mb_pos = GET_LOW(multibyte_read_pos); u8 mb_count = GET_LOW(multibyte_read_count); u8 mustflush = mb_count != 0;
// read and process data while (inb(addr + SEROFF_LSR) & 0x01) { u8 byte = inb(addr + SEROFF_DATA); if (mb_count) { // In a multi-byte sequence while (GET_GLOBAL(termseq[mb_pos].seq[mb_count-1]) != byte) { // Byte didn't match this sequence - find one that does mb_pos++; if (mb_pos >= ARRAY_SIZE(termseq) || memcmp_far(GLOBAL_SEG, termseq[mb_pos-1].seq , GLOBAL_SEG, termseq[mb_pos].seq , mb_count-1) != 0) // No match - must flush previusly queued keys dump_multibyte_sequence(mb_pos, mb_count); mb_pos = mb_count = mustflush = 0; break; } } if (mb_count) { if (!GET_GLOBAL(termseq[mb_pos].seq[mb_count])) { // sequence complete sercon_sendkey(GET_GLOBAL(termseq[seq].scancode), 0); mb_pos = mb_count = mustflush = 0; } else { // Got another key in this sequence - continue checking mb_count++; } continue; } } if (byte == 0x1b) { // Start multi-byte sequence check; mb_pos = 0; mb_count = 1; continue; } // Send normal key sercon_sendkey(GET_LOW(termchr[chr].scancode), GET_LOW(termchr[chr].flags)); mustflush = 0; }
if (mustflush && mb_count) { // Too long to read multi-byte sequence - must flush dump_multibyte_sequence(mb_pos, mb_count); mb_count = mb_pos = 0; } SET_LOW(multibyte_read_count, mb_count); SET_LOW(multibyte_read_pos, mb_pos); }
static void dump_multibyte_sequence(u8 mb_pos, u8 mb_count) { sercon_sendkey(GET_LOW(termchr[0x1b].scancode), GET_LOW(termchr[0x1b].flags)); int i; for (i=0; i<mb_count-1; i++) { u8 key = GET_GLOBAL(termseq[mb_pos].seq[i]); sercon_sendkey(GET_LOW(termchr[key].scancode), GET_LOW(termchr[key].flags)); } }