Feature/kbd upperhalf - #19586
Open
JorgeGzm wants to merge 10 commits into
Open
Conversation
A keyboard driver that tracks modifier state has no way to report it. Folding the modifier into the character that it produces loses the fact that the key is down, so an application cannot bind an action to Ctrl or Shift, and cannot tell that one is being held. Add the eight modifiers to enum kbd_keycode_e and move LAST_KEYCODE to the new end of the enumeration. Leaving LAST_KEYCODE behind would make kbd_specpress() assert and kbd_decode() reject the new keycodes, since both range check against it. The keycodes are appended, so the values of the existing ones do not change. Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
keyboard.h declares KEYBOARD_PRESS and KEYBOARD_RELEASE only, but the type field of struct keyboard_event_s has four values: the two special key types live in kbd_codec.h, under a different prefix. Somebody writing a keyboard driver reads keyboard.h, sees two types, and implements two types. Six of the nine drivers that register a keyboard never report a special key at all, and the failure is silent: the build is clean and the symptom is a key that does nothing. Define all four here, as aliases of the kbd_decode() return values so that a driver can feed both this interface and the byte stream codec from a single source. Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
JorgeGzm
requested review from
Donny9,
GUIDINGLI,
Ouss4,
acassis,
btashton,
jerpelea and
xiaoxiang781216
as code owners
July 31, 2026 00:23
JorgeGzm
force-pushed
the
feature/kbd-upperhalf
branch
2 times, most recently
from
July 31, 2026 01:23
e4a99c9 to
bb21a91
Compare
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>
Both switches end with a bare default label and no statement after it, which a compiler is entitled to reject: a label has to label something. GCC for MIPS does, and the file is new enough that no configuration had compiled it yet. The next commit makes USBHOST_HIDKBD select INPUT_KEYBOARD, which pulls this file into twenty five configurations for the first time, ci20:jumbo among them, so fix it here rather than let that commit break them. Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
The driver kept a character device, a ring buffer, a poll waiter list and an encoder of its own, in parallel with everything the keyboard upper half already provides. A USB keyboard was therefore the one keyboard an application could not read like any other. Register with keyboard_register() and report with keyboard_event(), which removes the private character device and the four hundred lines that served it. Special keys are reported with the SPEC event types carrying a keycode, so an application no longer has to guess whether a value in the character range is a character or an arrow key. HIDKBD_ENCODED and HIDKBD_NODEBOUNCE go away with the code they guarded. Encoding is now inherent to the event, and the previous report is no longer an optimisation: a HID keyboard reports the keys that are down rather than the transitions, so it is what tells a new press from a key still held, and what tells that a key has been released. Reporting the modifiers as keys is new, so it is behind HIDKBD_REPORT_MODIFIERS and off by default. Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
JorgeGzm
force-pushed
the
feature/kbd-upperhalf
branch
from
July 31, 2026 03:31
bb21a91 to
bd96abb
Compare
These are the configurations whose application reads the USB HID keyboard as a byte stream. Now that the driver reports through the keyboard upper half, they need INPUT_KEYBOARD_BYTESTREAM to keep behaving as before. INPUT drops out of the normalised defconfigs because USBHOST_HIDKBD now selects it. Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
The matrix driver reported every key with KEYBOARD_PRESS and KEYBOARD_RELEASE, so a board whose matrix has arrows or function keys had no way to say so: the keycode ranges overlap the character range, and the event type is what tells them apart. A keymap entry is a uint32_t, so wrap the entry in KMATRIX_SPECIAL() to declare that it holds a value from enum kbd_keycode_e. Existing keymaps hold characters and are unaffected. While here, drop the cast that truncated the keycode to sixteen bits, and default the device to /dev/kbd0. Applications look for a keyboard under that name, and /dev/keypad0 kept the matrix out of reach of every one of them. Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
The keyboard driver documentation described the byte stream codec and nothing else. It never mentioned keyboard_register(), keyboard_event() or the event types, so somebody writing a driver had no way to find the interface that every keyboard in the tree actually uses. That omission has a cost that can be counted: six of the nine drivers that register a keyboard report only the press and release types and never the special ones, which means their arrow keys are silently dropped by any application that follows the contract. The header they would read to find out declares two of the four types. Document the contract, why the event type is what tells an arrow key from the character that shares its value, what to name the device, how to get a matrix keyboard working without writing a driver at all, and how to test the result with or without the hardware. Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
The X11 keyboard reported any keysym that its translation table did not know as an ordinary key press carrying the raw keysym. An application looking for a byte of text got 65307 for Escape and 65289 for Tab, and the modifiers arrived as 65505 and friends. That is not a character, so it cannot be a KEYBOARD_PRESS. NXDoom on the simulator has therefore had no menu, no map and no fire, which is most of the game. Escape and Tab are control characters and are reported as such. The eight modifiers get the keycodes that the codec now has for them. And a keysym above the Latin-1 range that no table knows is not reported at all, which closes the case rather than the three instances of it. Verified by dumping the events while typing: Escape arrives as 27, Tab as 9, the modifiers as keycodes 88 to 95, and the game plays. Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
… keycodes The Fn cursor cluster was reported as ordinary key presses carrying 0x80 to 0x83, chosen to sit above the printable range so that an application could tell them apart from characters. That is the collision this work exists to remove: a key that produces no character is a special key, and the event type is what says so. Report them as KEYBOARD_SPECPRESS carrying KEYCODE_UP and friends, which is what every other keyboard does and what the applications now expect. The character tables are untouched, so the layout and the shift map behave exactly as before. Only the four Fn cursor keys change. Not tested on hardware: the board was not available. It builds, and the change is confined to the two lines that pick the event type. Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
JorgeGzm
force-pushed
the
feature/kbd-upperhalf
branch
from
July 31, 2026 04:03
bd96abb to
4a128c6
Compare
xiaoxiang781216
approved these changes
Jul 31, 2026
jerpelea
approved these changes
Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is the change asked for in the review of #19579, done on
its own rather than inside a board port.
usbhost_hidkbdkept a character device, a ring buffer, a poll waiter listand an encoder of its own, in parallel with everything the keyboard upper
half already provides. A USB keyboard was therefore the one keyboard an
application could not read like any other. It now registers with
keyboard_register()and reports withkeyboard_event(), which removes theprivate character device and the four hundred lines that served it.
While looking at why six of the nine drivers that register a keyboard never
report a special key, the answer turned out to be in the header:
The type field of
struct keyboard_event_shas four values, not two. Theother two live in
kbd_codec.hunder a different prefix. Somebody writing adriver reads
keyboard.h, sees two types and implements two types, and thefailure is silent: the build is clean and the symptom is a key that does
nothing. All four are declared here now, as aliases of the
kbd_decode()return values so that a driver can feed both interfaces from one source.
The rest follows from that:
kmatrixreported every key as a plain press, so a board whose matrix hasarrows had no way to say so. A keymap entry wrapped in
KMATRIX_SPECIAL()now declares that it holds a keycode rather than a character.
sim_keyboardreported any X11 keysym its table did not know as anordinary key press carrying the raw keysym, so an application looking for
a byte of text got 65307 for Escape and 65289 for Tab, and the modifiers
arrived as 65505 and friends. NXDoom on the simulator has therefore had no
menu, no map and no fire, which is most of the game. Escape and Tab are
control characters and are reported as such, the modifiers get the
keycodes the codec now has for them, and an unknown keysym above the
Latin-1 range is not reported at all, which closes the case rather than
the three instances of it.
The keyboard driver documentation described the byte stream codec and
nothing else. It never mentioned
keyboard_register(), so there wasnowhere to look up the interface that every keyboard actually uses. The
contract is written down now, along with why the event type matters, what
to name the device, how to get a matrix keyboard working without writing a
driver, and how to test the result with or without the hardware.
The M5Stack Cardputer reported its Fn cursor cluster as ordinary presses
carrying 0x80 to 0x83, chosen to sit above the printable range so that an
application could tell them apart from characters. That is the same
collision, solved by hand. They are keycodes now.
This is the first of three parts, and it stands on its own.
apache/nuttx-apps#PENDING is the application half and needs this one merged
first, since it uses the keycodes added here. A follow-up here will then
update the two board configurations that name an application option.
Impact
Nine configurations have an application that reads the USB keyboard as a
byte stream. They keep working through
INPUT_KEYBOARD_BYTESTREAM, whichrenders each event with the keyboard codec instead of copying the event
structure. Only presses are rendered, which is what a keyboard reporting
through a character device has always delivered, so this reproduces the
previous behaviour rather than adding to it. The option is selected in those
nine defconfigs here.
Out of tree,
NSH_USBKBDandMICROWINDOWS_KBD_RAWalso read the bytestream and need the same option. Both now depend on it, in the
applications PR, so Kconfig refuses the combination that cannot work. No
in-tree configuration selects either.
HIDKBD_ENCODEDandHIDKBD_NODEBOUNCEare gone, with the code theyguarded. Encoding is inherent to the event now. The previous report is no
longer an optimisation either: a HID keyboard reports the keys that are
down rather than the transitions, so it is what tells a new press from a key
still held, and what tells that a key has been released.
Reporting the modifiers as keys is new, so it is behind
HIDKBD_REPORT_MODIFIERSand off by default. It matters for a game, wherefire, run and strafe are bound to Ctrl, Shift and Alt, and not for a
terminal.
USBHOST_HIDKBDnow selectsINPUTandINPUT_KEYBOARD, the same wayUSBHOST_HIDMOUSEalready selectsINPUTandINPUT_MOUSE.INPUT_KMATRIX_DEVPATHdefaults to/dev/kbd0instead of/dev/keypad0. Applications look for a keyboard under the former, so thematrix driver was out of reach of all of them. No in-tree configuration
enables
INPUT_KMATRIX.LAST_KEYCODEmoves to the end of the enumeration, which it has to: thecodec range checks against it, so the new keycodes would otherwise trip an
assertion in
kbd_specpress()and be rejected bykbd_decode().Testing
Validated on hardware in two independent backends, a USB HID keyboard on a
Linum STM32H753BI and X11 on the simulator, in both the event model and the
byte stream compatibility mode.
Counted rather than eyeballed. Typing normally on the Linum produced 40
presses and 40 releases, perfectly paired, with no key pressed twice while
held, no orphan release, nothing stuck down at the end, and four keys held
at once reported in the right order. The arrows, Ctrl and Shift gave 29
press and release pairs on the same terms, including two arrows held
together, and arrived as keycodes 5 to 8, 88 and 90.
On the simulator, dumping the events while typing shows Escape as 27, Tab as
9 and the modifiers as keycodes 88 to 95, none of which were reachable
before. NXDoom runs there and plays.
Two things worth knowing for anybody repeating this, both now documented:
Without
HIDKBD_NOGETREPORTthe driver samples the keyboard over thecontrol pipe every 40 ms, and a key pressed and released between two samples
is simply not there. Six keys arrived in seventy five seconds of typing.
That is the sampling rate, not the reporting, and it behaves the same way on
master. The Linum configuration enables it.
UINPUT_KEYBOARD_BUFNUMBERcounts events rather than keys, so its defaultof eight holds four keystrokes, and a console hands over a whole line at
once. The upper half overwrites the oldest event when the buffer is full, so
a typed line arrives with its beginning missing and nothing says so. This is
pre-existing and only shows up when uinput is actually used as a keyboard.
The Linum configuration raises it.
Built every configuration in the tree that enables
USBHOST_HIDKBD,INPUT_KEYBOARD,INPUT_KMATRIX,UINPUT_KEYBOARD,EXAMPLES_LVGLTERM,EXAMPLES_KEYBOARD,EXAMPLES_HIDKBDorSYSTEM_KBD. 21 of them were alsobuilt inside the CI container, which covers the Xtensa and x86_64 toolchains
that are not on my machine, and all 21 pass. That includes both Cardputer
configurations, which is the only way I could check the driver change above,
since the board was not available.
The remaining failures are
stm32butterfly2:nshand:nshusbhost, whichselect
ARM_TOOLCHAIN_BUILDROOTand need a compiler that is not in the CIcontainer either (the CI builds them with
ARM_TOOLCHAIN_CLANG), andci20:jumbo, which needs MIPS. All three fail the same way on master.sim:nxdoombuilds and runs.nxstyleis clean on every file touched.