Message ID | 1467370471-20554-3-git-send-email-kraxel@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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)); } }
On Fri, Jul 01, 2016 at 01:07:39PM -0400, Kevin O'Connor wrote: > 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. The version below might be slightly easier to understand (still totally untested). -Kevin u8 multibyte_read_count VARLOW; u8 multibyte_read_pos VARLOW; void sercon_check_event(void) { u16 addr = GET_LOW(sercon_port); ... // read and process data int readdata = 0; while (inb(addr + SEROFF_LSR) & 0x01) { u8 byte = inb(addr + SEROFF_DATA); readdata = 1; int ret = sercon_check_multibyte(byte); if (ret) // byte part of multi-byte sequence continue; if (byte == 0x1b) { // Start multi-byte sequence check SET_LOW(multibyte_read_count, 1); continue; } // Send normal key sercon_sendkey(GET_LOW(termchr[chr].scancode), GET_LOW(termchr[chr].flags)); } if (!readdata && GET_LOW(multibyte_read_count)) // Too long to read multi-byte sequence - must flush dump_multibyte_sequence(); } static int sercon_check_multibyte(u8 byte) { int mb_count = GET_LOW(multibyte_read_count); if (!mb_count) // Not in a multi-byte sequence return 0; int mb_pos = GET_LOW(multibyte_read_pos); while (GET_GLOBAL(termseq[mb_pos].seq[mb_count-1]) != byte) { // Byte didn't match this sequence - find a sequence 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(); return 0; } } mb_count++; if (!GET_GLOBAL(termseq[mb_pos].seq[mb_count-1])) { // sequence complete - send key sercon_sendkey(GET_GLOBAL(termseq[seq].scancode), 0); mb_count = mb_pos = 0; } SET_LOW(multibyte_read_count, mb_count); SET_LOW(multibyte_read_pos, mb_pos); return 1; } static void dump_multibyte_sequence(void) { sercon_sendkey(GET_LOW(termchr[0x1b].scancode), GET_LOW(termchr[0x1b].flags)); int i, mb_count = GET_LOW(multibyte_read_count); 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)); } SET_LOW(multibyte_read_count, 0); SET_LOW(multibyte_read_pos, 0); }
Hi, > > +#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. Ah, ok. Didn't notice this one can be used for ascii -> scancode lookups. I'm wondering whenever it makes sense to build a reverse table. We could simply search scan_to_keycode[] instead. Sure it is slower than a table lookup, but still _way_ faster than any human can type, and we would save some real-mode memory. > Is it necessary to use process_key() here instead of injecting the > keycode directly with enqueue_key()? From a brief look at the code I think I can use enqueue_key directly without anything breaking. I'll give it a try. > > +void VISIBLE16 > > +sercon_check_event(void) > > Does this need VISIBLE16? Don't think so, I'll drop it. > 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. Yes. Single-byte us-ascii for letters and numbers. multibyte sequences starting with ESC (0x1b) for almost everything else (function keys, cursor keys, home, end, pageup, pagedown, ...) The list in the patch isn't complete, I've only added the most important keys for initial testing. You may also see input with the 8th bit set, which has a high chance to be a multibyte sequence (utf8) too these days. But I think we can safely ignore any input with the 8th bit set, we can't map that to us keyboard layout anyway even if we manage to correctly identify the character typed. > 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. Hmm, so you avoid the buffer by maintaining an index into termseq and matched byte count instead. Hmm. Yes, avoids the buffer and also a few cpu cycles because you can continue searching where you left off. I find the code flow harder to follow though. cheers, Gerd
On Mon, Jul 04, 2016 at 11:16:45AM +0200, Gerd Hoffmann wrote: > Hi, > > > > +#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. > > Ah, ok. Didn't notice this one can be used for ascii -> scancode > lookups. > > I'm wondering whenever it makes sense to build a reverse table. We > could simply search scan_to_keycode[] instead. Sure it is slower than a > table lookup, but still _way_ faster than any human can type, and we > would save some real-mode memory. Agreed. [...] > > 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. > > Hmm, so you avoid the buffer by maintaining an index into termseq and > matched byte count instead. Hmm. Yes, avoids the buffer and also a few > cpu cycles because you can continue searching where you left off. I > find the code flow harder to follow though. Right. Yeah, also not sure it's an improvement. Does the original code flush the multi-byte sequence on a timeout? I suspect it is important that one can hit ESC without having to type another key. Also, I'd prefer to avoid backwards gotos if possible. -Kevin
Hi, > Does the original code flush the multi-byte sequence on a timeout? I > suspect it is important that one can hit ESC without having to type > another key. Also, I'd prefer to avoid backwards gotos if possible. Yes, sort of. If it didn't match an escape sequence and hasn't seen additional data on this particular sercon_handle_event call it goes on interpret the buffer content as single byte. So, effectively the timeout is the clock_update() call interval. cheers, Gerd
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 +}; + +#define FLAG_CTRL (1<<0) +#define FLAG_SHIFT (1<<1) + +VARLOW struct { + u8 flags; + u8 scancode; +} termchr[256] = { + [ '1' ] = { .scancode = 0x02, }, + [ '!' ] = { .scancode = 0x02, .flags = FLAG_SHIFT }, + [ '2' ] = { .scancode = 0x03, }, + [ '@' ] = { .scancode = 0x03, .flags = FLAG_SHIFT }, + [ '3' ] = { .scancode = 0x04, }, + [ '#' ] = { .scancode = 0x04, .flags = FLAG_SHIFT }, + [ '4' ] = { .scancode = 0x05, }, + [ '$' ] = { .scancode = 0x05, .flags = FLAG_SHIFT }, + [ '5' ] = { .scancode = 0x06, }, + [ '%' ] = { .scancode = 0x06, .flags = FLAG_SHIFT }, + [ '6' ] = { .scancode = 0x07, }, + [ '^' ] = { .scancode = 0x07, .flags = FLAG_SHIFT }, + [ '7' ] = { .scancode = 0x08, }, + [ '&' ] = { .scancode = 0x08, .flags = FLAG_SHIFT }, + [ '8' ] = { .scancode = 0x09, }, + [ '*' ] = { .scancode = 0x09, .flags = FLAG_SHIFT }, + [ '9' ] = { .scancode = 0x0a, }, + [ '(' ] = { .scancode = 0x0a, .flags = FLAG_SHIFT }, + [ '0' ] = { .scancode = 0x0b, }, + [ ')' ] = { .scancode = 0x0b, .flags = FLAG_SHIFT }, + [ '-' ] = { .scancode = 0x0c, }, + [ '=' ] = { .scancode = 0x0d, }, + [ '+' ] = { .scancode = 0x0d, .flags = FLAG_SHIFT }, + + [ 'q' ] = { .scancode = 0x10, }, + [ 'Q' ] = { .scancode = 0x10, .flags = FLAG_SHIFT }, + [ 'Q' & 0x1f ] = { .scancode = 0x10, .flags = FLAG_CTRL }, + [ 'w' ] = { .scancode = 0x11, }, + [ 'W' ] = { .scancode = 0x11, .flags = FLAG_SHIFT }, + [ 'W' & 0x1f ] = { .scancode = 0x11, .flags = FLAG_CTRL }, + [ 'e' ] = { .scancode = 0x12, }, + [ 'E' ] = { .scancode = 0x12, .flags = FLAG_SHIFT }, + [ 'E' & 0x1f ] = { .scancode = 0x12, .flags = FLAG_CTRL }, + [ 'r' ] = { .scancode = 0x13, }, + [ 'R' ] = { .scancode = 0x13, .flags = FLAG_SHIFT }, + [ 'R' & 0x1f ] = { .scancode = 0x13, .flags = FLAG_CTRL }, + [ 't' ] = { .scancode = 0x14, }, + [ 'T' ] = { .scancode = 0x14, .flags = FLAG_SHIFT }, + [ 'T' & 0x1f ] = { .scancode = 0x14, .flags = FLAG_CTRL }, + [ 'y' ] = { .scancode = 0x15, }, + [ 'Y' ] = { .scancode = 0x15, .flags = FLAG_SHIFT }, + [ 'Y' & 0x1f ] = { .scancode = 0x15, .flags = FLAG_CTRL }, + [ 'u' ] = { .scancode = 0x16, }, + [ 'U' ] = { .scancode = 0x16, .flags = FLAG_SHIFT }, + [ 'U' & 0x1f ] = { .scancode = 0x16, .flags = FLAG_CTRL }, + [ 'i' ] = { .scancode = 0x17, }, + [ 'I' ] = { .scancode = 0x17, .flags = FLAG_SHIFT }, + [ 'I' & 0x1f ] = { .scancode = 0x17, .flags = FLAG_CTRL }, + [ 'o' ] = { .scancode = 0x18, }, + [ 'O' ] = { .scancode = 0x18, .flags = FLAG_SHIFT }, + [ 'O' & 0x1f ] = { .scancode = 0x18, .flags = FLAG_CTRL }, + [ 'p' ] = { .scancode = 0x19, }, + [ 'P' ] = { .scancode = 0x19, .flags = FLAG_SHIFT }, + [ 'P' & 0x1f ] = { .scancode = 0x19, .flags = FLAG_CTRL }, + [ '[' ] = { .scancode = 0x1a, }, + [ '{' ] = { .scancode = 0x1a, .flags = FLAG_SHIFT }, + [ ']' ] = { .scancode = 0x1b, }, + [ '}' ] = { .scancode = 0x1b, .flags = FLAG_SHIFT }, + + [ 'a' ] = { .scancode = 0x1e, }, + [ 'A' ] = { .scancode = 0x1e, .flags = FLAG_SHIFT }, + [ 'A' & 0x1f ] = { .scancode = 0x1e, .flags = FLAG_CTRL }, + [ 's' ] = { .scancode = 0x1f, }, + [ 'S' ] = { .scancode = 0x1f, .flags = FLAG_SHIFT }, + [ 'S' & 0x1f ] = { .scancode = 0x1f, .flags = FLAG_CTRL }, + [ 'd' ] = { .scancode = 0x20, }, + [ 'D' ] = { .scancode = 0x20, .flags = FLAG_SHIFT }, + [ 'D' & 0x1f ] = { .scancode = 0x20, .flags = FLAG_CTRL }, + [ 'f' ] = { .scancode = 0x21, }, + [ 'F' ] = { .scancode = 0x21, .flags = FLAG_SHIFT }, + [ 'F' & 0x1f ] = { .scancode = 0x21, .flags = FLAG_CTRL }, + [ 'g' ] = { .scancode = 0x22, }, + [ 'G' ] = { .scancode = 0x22, .flags = FLAG_SHIFT }, + [ 'G' & 0x1f ] = { .scancode = 0x22, .flags = FLAG_CTRL }, + [ 'h' ] = { .scancode = 0x23, }, + [ 'H' ] = { .scancode = 0x23, .flags = FLAG_SHIFT }, + [ 'H' & 0x1f ] = { .scancode = 0x23, .flags = FLAG_CTRL }, + [ 'j' ] = { .scancode = 0x24, }, + [ 'J' ] = { .scancode = 0x24, .flags = FLAG_SHIFT }, + [ 'J' & 0x1f ] = { .scancode = 0x24, .flags = FLAG_CTRL }, + [ 'k' ] = { .scancode = 0x25, }, + [ 'K' ] = { .scancode = 0x25, .flags = FLAG_SHIFT }, + [ 'K' & 0x1f ] = { .scancode = 0x25, .flags = FLAG_CTRL }, + [ 'l' ] = { .scancode = 0x26, }, + [ 'L' ] = { .scancode = 0x26, .flags = FLAG_SHIFT }, + [ 'L' & 0x1f ] = { .scancode = 0x26, .flags = FLAG_CTRL }, + [ ';' ] = { .scancode = 0x27, }, + [ ':' ] = { .scancode = 0x27, .flags = FLAG_SHIFT }, + [ '\'' ] = { .scancode = 0x28, }, + [ '"' ] = { .scancode = 0x28, .flags = FLAG_SHIFT }, + + [ '\\' ] = { .scancode = 0x2b, }, + [ '|' ] = { .scancode = 0x2b, .flags = FLAG_SHIFT }, + [ 'z' ] = { .scancode = 0x2c, }, + [ 'Z' ] = { .scancode = 0x2c, .flags = FLAG_SHIFT }, + [ 'Z' & 0x1f ] = { .scancode = 0x2c, .flags = FLAG_CTRL }, + [ 'x' ] = { .scancode = 0x2d, }, + [ 'X' ] = { .scancode = 0x2d, .flags = FLAG_SHIFT }, + [ 'X' & 0x1f ] = { .scancode = 0x2d, .flags = FLAG_CTRL }, + [ 'c' ] = { .scancode = 0x2e, }, + [ 'C' ] = { .scancode = 0x2e, .flags = FLAG_SHIFT }, + [ 'C' & 0x1f ] = { .scancode = 0x2e, .flags = FLAG_CTRL }, + [ 'v' ] = { .scancode = 0x2f, }, + [ 'V' ] = { .scancode = 0x2f, .flags = FLAG_SHIFT }, + [ 'V' & 0x1f ] = { .scancode = 0x2f, .flags = FLAG_CTRL }, + [ 'b' ] = { .scancode = 0x30, }, + [ 'B' ] = { .scancode = 0x30, .flags = FLAG_SHIFT }, + [ 'B' & 0x1f ] = { .scancode = 0x30, .flags = FLAG_CTRL }, + [ 'n' ] = { .scancode = 0x31, }, + [ 'N' ] = { .scancode = 0x31, .flags = FLAG_SHIFT }, + [ 'N' & 0x1f ] = { .scancode = 0x31, .flags = FLAG_CTRL }, + [ 'm' ] = { .scancode = 0x32, }, + [ 'M' ] = { .scancode = 0x32, .flags = FLAG_SHIFT }, + [ 'M' & 0x1f ] = { .scancode = 0x32, .flags = FLAG_CTRL }, + [ ',' ] = { .scancode = 0x33, }, + [ '<' ] = { .scancode = 0x33, .flags = FLAG_SHIFT }, + [ '.' ] = { .scancode = 0x34, }, + [ '>' ] = { .scancode = 0x34, .flags = FLAG_SHIFT }, + [ '?' ] = { .scancode = 0x35, }, + [ '|' ] = { .scancode = 0x35, .flags = FLAG_SHIFT }, + + [ '\x1b' ] = { .scancode = 0x01, }, + [ '\t' ] = { .scancode = 0x0f, }, + [ '\r' ] = { .scancode = 0x1c, }, + [ '\n' ] = { .scancode = 0x1c, }, + [ ' ' ] = { .scancode = 0x39, }, +}; + +static void shiftbuf(int remove) +{ + int i, remaining; + + remaining = GET_LOW(rx_bytes) - remove; + SET_LOW(rx_bytes, remaining); + for (i = 0; i < remaining; i++) + SET_LOW(rx_buf[i], GET_LOW(rx_buf[i + remove])); +} + +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); +} + +void VISIBLE16 +sercon_check_event(void) +{ + 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; +} diff --git a/src/util.h b/src/util.h index 29f17be..3e7366b 100644 --- a/src/util.h +++ b/src/util.h @@ -232,6 +232,7 @@ void serial_setup(void); void lpt_setup(void); void sercon_10(struct bregs *regs); void sercon_enable(void); +void sercon_check_event(void); // vgahooks.c void handle_155f(struct bregs *regs);
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(+)