From f027a791458c79b576aa772651b00dabb69f3d87 Mon Sep 17 00:00:00 2001 From: Jorge Guzman Date: Thu, 30 Jul 2026 15:08:21 -0300 Subject: [PATCH] drivers/input: add a byte stream mode to the keyboard upper half The USB HID keyboard driver is about to report through the upper half rather than through a character device of its own, which changes what read() returns from a byte stream to struct keyboard_event_s. Ten in-tree configurations have an application that consumes the byte stream. Add INPUT_KEYBOARD_BYTESTREAM, which renders each event with the keyboard codec instead of copying the event structure, so those applications keep working while they are converted. Only the press events are rendered. A byte stream has no way to say that a key came up, which is exactly what a keyboard reporting through a character device has always delivered, so this reproduces the previous behaviour rather than adding to it. Signed-off-by: Jorge Guzman --- drivers/input/Kconfig | 28 ++++++++-- drivers/input/keyboard_upper.c | 97 ++++++++++++++++++++++++++++++++-- 2 files changed, 119 insertions(+), 6 deletions(-) diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index bf769337398..59d2d989088 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -89,6 +89,25 @@ config INPUT_KEYBOARD bool default n +config INPUT_KEYBOARD_BYTESTREAM + bool "Deliver a byte stream instead of keyboard events" + default n + depends on INPUT_KEYBOARD && LIBC_KBDCODEC + ---help--- + By default a keyboard device delivers struct keyboard_event_s + events on read(). Select this to make it deliver the byte stream + defined by include/nuttx/input/kbd_codec.h instead, which is what + the USB HID keyboard driver provided before it moved to the + keyboard upper half. + + This is meant for applications that have not been converted yet. + The byte stream cannot express a key release, so an application + that needs one has to read the events. + + This applies to every keyboard in the build, so it cannot be used + to mix a byte stream consumer and an event consumer in the same + configuration. + config INPUT_UINPUT bool default n @@ -723,7 +742,7 @@ config INPUT_SPQ10KBD select I2C ---help--- Enable the Solder Party Q10 BlackBerry Keyboard support. This - exposes itself as a standard keyboard at /dev/keypadN. + exposes itself as a standard keyboard at /dev/kbdN. This keyboard exists both as a standalone module and integrated into the Solder Party Keyboard FeatherWing. Information on this can be found at https://www.solder.party/docs/keyboard-pmod/ @@ -783,9 +802,12 @@ config INPUT_KMATRIX_DEBOUNCE config INPUT_KMATRIX_DEVPATH string "Device path" - default "/dev/keypad0" + default "/dev/kbd0" ---help--- - Path where the keyboard matrix device will be registered. Default: /dev/keypad0 + Path where the keyboard matrix device will be registered. This is + the name that applications expect a keyboard to have, so keep the + /dev/kbdN form unless the board has a reason to differ. + Default: /dev/kbd0 config INPUT_KMATRIX_I2C bool "Keyboard Matrix via I2C GPIO Expander" diff --git a/drivers/input/keyboard_upper.c b/drivers/input/keyboard_upper.c index 994190e57d1..2291b62f3be 100644 --- a/drivers/input/keyboard_upper.c +++ b/drivers/input/keyboard_upper.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,16 @@ * Pre-processor Definitions ****************************************************************************/ +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM + +/* The longest sequence that one event can produce in the byte stream. A + * normal key is a single byte; a special key is the four byte escape + * sequence emitted by kbd_specpress(). + */ + +# define KEYBOARD_BYTESTREAM_MAX 4 +#endif + /**************************************************************************** * Private Types ****************************************************************************/ @@ -384,6 +395,66 @@ int keyboard_unregister(FAR struct keyboard_lowerhalf_s *lower, return 0; } +/**************************************************************************** + * Name: keyboard_encode + * + * Description: + * Render one keyboard event as the byte stream that the keyboard codec + * defines, for applications that consume characters rather than events. + * + * Only the press events are rendered. A byte stream has no way to say + * that a key came up: a normal key contributes its character and nothing + * more, which is what a keyboard reporting through a character device has + * always delivered. An application that needs key releases has to read + * the events instead. + * + * Input Parameters: + * stream - Memory stream to render into + * buf - Buffer of KEYBOARD_BYTESTREAM_MAX bytes backing the stream + * keycode - The key + * type - The event type + * + * Returned Value: + * The number of bytes rendered, zero if this event has no representation + * in the byte stream. + * + ****************************************************************************/ + +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM +static size_t keyboard_encode(FAR struct lib_memoutstream_s *stream, + FAR char *buf, uint32_t keycode, uint32_t type) +{ + lib_memoutstream(stream, buf, KEYBOARD_BYTESTREAM_MAX); + + switch (type) + { + case KEYBOARD_PRESS: + kbd_press(keycode, &stream->common); + break; + + case KEYBOARD_SPECPRESS: + + /* Out of range keycodes would trip an assertion in the codec. A + * lower half that reports something the codec does not know about + * simply does not appear in the byte stream. + */ + + if (keycode < FIRST_KEYCODE || keycode > LAST_KEYCODE) + { + return 0; + } + + kbd_specpress(keycode, &stream->common); + break; + + default: + return 0; + } + + return stream->common.nput; +} +#endif + /**************************************************************************** * keyboard_event ****************************************************************************/ @@ -393,22 +464,42 @@ void keyboard_event(FAR struct keyboard_lowerhalf_s *lower, uint32_t keycode, { FAR struct keyboard_upperhalf_s *upper = lower->priv; FAR struct keyboard_opriv_s *opriv; - struct keyboard_event_s key; int semcount; +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM + struct lib_memoutstream_s stream; + char buf[KEYBOARD_BYTESTREAM_MAX]; + size_t buflen; + + buflen = keyboard_encode(&stream, buf, keycode, type); + if (buflen == 0) + { + /* This event has no representation in the byte stream */ + + return; + } +#else + struct keyboard_event_s key; + + key.code = keycode; + key.type = type; +#endif + if (nxmutex_lock(&upper->lock) < 0) { return; } - key.code = keycode; - key.type = type; list_for_every_entry(&upper->head, opriv, struct keyboard_opriv_s, node) { if (nxmutex_lock(&opriv->lock) == 0) { +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM + circbuf_overwrite(&opriv->circ, buf, buflen); +#else circbuf_overwrite(&opriv->circ, &key, sizeof(struct keyboard_event_s)); +#endif nxsem_get_value(&opriv->waitsem, &semcount); if (semcount < 1) {