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 <jorge.gzm@gmail.com>
This commit is contained in:
Jorge Guzman 2026-07-30 15:08:21 -03:00 committed by Alan C. Assis
parent 1e93506f84
commit e00a3a0a2f
2 changed files with 119 additions and 6 deletions

View file

@ -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"

View file

@ -33,6 +33,7 @@
#include <nuttx/input/keyboard.h>
#include <nuttx/input/kbd_codec.h>
#include <nuttx/streams.h>
#include <nuttx/kmalloc.h>
#include <nuttx/list.h>
#include <nuttx/circbuf.h>
@ -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)
{