On Fri, Jul 01, 2016 at 12:54:31PM +0200, Gerd Hoffmann wrote: > Signed-off-by: Gerd Hoffmann <kra...@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)); } }