circuitpython: Unable to use HID keyboard in boot OS (macOS boot screen)
I started putting together a project with my Trinket M0 to use for switching boot volumes on macOS. My goal was to have it select a volume by keystrokes. However, it doesn’t seem to be working.Working correctly in macOS once booted
Using the same code for HID keyboard:
- plug trinket m0 into usb on mac
- reboot system
import time
import board
import digitalio
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode
# The keyboard object!
time.sleep(1) # Sleep for a bit to avoid a race condition on some systems
keyboard = Keyboard()
keyboard_layout = KeyboardLayoutUS(keyboard)
while True:
keyboard.press(Keycode.LEFT_ALT)
time.sleep(40)
led.value = False
keyboard.release_all()
About this issue
- Original URL
- State: open
- Created 6 years ago
- Comments: 68 (8 by maintainers)
I am starting to implement dynamic control over USB and USB HID descriptors right now. I may not get to boot keyboard immediately, but it will be possible. So I would say not to start on it yourself right now, because the underlying code is going to change a lot in the near future.
All that we really need is a separate USB descriptor (separate endpoint) with an appropriate USB HID descriptor. Almost any commercial keyboard does this and we can vet our choices against the descriptors from such keyboards.
I am adding the
hid_boot_interface
example here if anyone is interested to test https://github.com/hathach/tinyusb/pull/1025 . I don’t have any legacy bios PC to test with though. Therefore it may have an issue or two. For implementation it uses 1 interface for keyboard, and 1 for mouse. Per HID specs, device is default to report mode, Bios PC will send control request SET_PROTOCOL to change it to boot mode which cause this callback to invoked https://github.com/hathach/tinyusb/pull/1025/files#diff-197babc2310c6d123603f9f2df568b1c1f1857cee19037e747408f146a847d4fR170-R176You could also use
tud_hid_n_get_protocol()
to get the current mode and send construct the report accordingly.report
mode: report is sent accordingly to descriptor like we are currently doing (can be composite)I have just downloaded the automatic build and it seems to fix my problems on Windows. The keyboard can be used right after booting. Thanks a lot for the great work.
The HID keyboard currently presented by CircuitPython is not a boot device keyboard, with its own endpoint(s), which is what the Mac Open Firmware (“BIOS”) and regular PC BIOSes expect. It’s part of a composite HID device that includes mouse, multimedia keys, and a gamepad. This was to save endpoints for other purposes.
We’ve had one other person who wants a boot-compatible keyboard and we’re thinking about that - we have to count up the available endpoints and what we want for the future (e.g. USB MIDI). Also, in the long run, we are thinking about how to provide user-configurable HID devices.
You do need a descriptor. How it works is a bit more complicated than one might expect. If you set
boot_device=1
, then CircuitPython says it is capable of being a boot keyboard. But it doesn’t do so unless the host asks for it to be a boot keyboard. If the host doesn’t ask, then the supplied descriptor is used. If the host does ask, then the supplied descriptor is sent but ignored, and the host assumes the “standard” one.Thank you for the analysis on the descriptor, and your corrections to mine also work with my KVM, as you probably expected!
When I set boot_device=1, the KVM doesn’t work unless I supply your descriptor as a parameter. I don’t think it’s ignoring it.
boot.py (updated)
Again, thanks for your time, let me know if I can test anything else for you, as I am super-thankful of all of your time and effort spent on making CircuitPython work for us (especially for first-time Python programmers like myself!)
I am not implementing boot keyboard in the first pass of dynamic USB descriptors, which I am working on right now. But internally the implementation is flexible enough to add later. Once I get the first version working it can be revisited.
@hathach Are you sure about the not needed extra endpoint? I tried a few times getting this to work with the arduino environment and to have an idea where the issue is i used this tool https://www.thesycon.de/eng/usb_descriptordumper.shtml and a real keyboard always used an extra endpoint.
@dhalbert during my research i never found someone asking for a boot mouse. Most BIOSes are configured by keyboard and Hotkeys in KVMs are also done with a keyboard
Good point - it’s more a question of having code space on the smallest builds to switch back and forth.
@dhalbert boot keyboard doesn’t need to be on its own interface and/or take extra endpoint. It can be used with existing HID device. Basically the HID device has 2 mode (or protocol): boot mode, and report mode (default).
Most bios will always start with SET_PROTOCOL = BOOT upon enumeration. We only need to handle the callback, set the mode correctly then skip all other report except keyboard. I will update my hid_composite example in my repo so that you could have a clear example to follow in circuitpython later on. Will tag you on the modification later on, may try to submit PR myself as well. Though for the flash size, it may indeed increase a bit of space.