You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Unable to use HID keyboard in boot OS (macOS boot screen) #1136
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()
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.
Thanks. I completely understand and figured that this wasn’t currently supported but thought filing a bug was the best way to ask/suggest implementation of the feature. Is there any roadmap for future HID support? And MIDI sounds quite awesome too :)
This is marked as closed - was it done? If the fix was the same that I used in the XBox stuff which requires a custom build, is there any example of making this work?
The fix requires a new descriptor and its own endpoint, at least. I'm not sure what else. Perhaps it could be added as a compile option for the descriptor generating Python script, and then later as a boot.py. We can reopen this as long term.
I understand this not being in the core CP, but I do think it should remain open until there's some way to do it. I don't mind having to do a custom build (like we do for the XBox Adaptive Controller), but we have found lots of folks who really want to use devices through KVMs, and the composite devices don't work through them.
Here's our current pressing issue: https://www.facebook.com/groups/ATMakers/permalink/815702142174560/
Um... I don't think so? I think the reason this is needed is that the KVM listens in on the keyboard commands and relays them. That lets it watch for trigger keystrokes ('scroll-scroll-enter') to jump between devices. We don't kneed the triggers, but the stupid KVM doesn't know what to do with composite devices. It might be that we just need a non-composite device w/no other profiles. Or only a single device (no serial/mass storage?)
I just know it is a known issue that "complicated" keyboards (even those with media keys) don't work.
Here is another vote for it :)
By now i've tried arduino, vusb, teensy, and pico pi and only teensy seems to have a full featured keyboard that works in BIOS and KVM devices. @ATMakersBill
The teensy itself is also a composite device, having keyboard, mouse, gamepad, serial and still works fine with a KVM. So my best guess is, that it only depends on a correct implementation of the boot keyboard. @dhalbert
As far as i know, a boot keyboard needs its own descriptor and endpoint and on top of it, it has two additional functions which are described in the usb docs.
We are currently out of endpoints on most boards with 8 endpoints, having added a second CDC. If someone would like to contribute boot keyboard and mouse descriptors and compile flags, we'd be happy to include them. We would not turn them on normally, since they would remove having MIDI or the second CDC available by default.
This is another indicator for user-configurable USB and USB HID descriptors. However, we are pretty much out of space on the smallest builds, and don't have room to add the code for this. It would have to be on something other than the non-Express SAMD21's.
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.
#usb_hid.enable((usb_hid.Device.KEYBOARD,), boot_device=1 )# above line does not work with Belkin SOHO KVM#usb_hid.enable((reference_keyboard,), boot_device=1 )# above line works fine with Belkin SOHO KVM
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!)
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.
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.
Did some further testing and i think this is a weird one. I didn't change the descriptor. I just added usb_hid.get_boot_device() to check if the host requests boot mode -> Yes it does.
So i said it locks up bios or grub boot loader. I found this is only half true.
After sending the first keypress it locks up. Nether from the cp-device nor from the original keyboard an input is accepted.
EXCEPT: pressing and releasing the left CTRL-key does unlock it. The normal keyboard starts working again and even better, the cp-device now acts as a keyboard perfectly fine.
I do not use CTRL key anywhere in my script and i don't have any issues without cp-device so i don't think the error comes from the built-in keyboard.
The behaviour is like a permanently pressed ctrl button. CTRL left button. But only one time after pluggin it in.
Pressing ALT + DEL while in bios reboots the pc.
But i can't find a part in my code where i send ctrl without releasing it.
So its fixable with kbd.release(Keycode.CONTROL) at the beginning of my script. But it doesn't explain why it is pressed in the first place. Even more confusing: https://github.com/adafruit/Adafruit_CircuitPython_HID/blob/main/adafruit_hid/keyboard.py does a release_all in the init part. Maybe something is going wrong there. I'll take a closer look tomorrow.
I'm using a KVM, but what you describe is the same behavior I was seeing before I started using the custom descriptor. https://w3c.github.io/uievents/tools/key-event-viewer.html would report left-control was always pressed before my code ran. Also, the KVM wouldn't forward any shift key presses to the host. So an "A" was always showing as control-a (lowercase), and so on.
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.
#usb_hid.enable((usb_hid.Device.KEYBOARD,), boot_device=1 )# above line does not work with Belkin SOHO KVM#usb_hid.enable((reference_keyboard,), boot_device=1 )# above line works fine with Belkin SOHO KVM
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!)
Just want to thank @retrospecced as using his boot.py resolved my issues whereby I couldn't access my ThinkPads BIOS or enter the grub menu on Arch Linux.
Now I just need to see if it's possible to change my Pico manufacturer info as lsusb shows Adafruit Pico and I'd like to change it to something else.
@retrospecced thx for your effort.
i tried your descriptor (and boot.py) but for some reason the device gets an exclamation mark in the device manager. So i was unable to test with the mentioned startech kvm switch.
Ah thx. I've thought the provided source does already contain the fix.
Meanwhile i found another descriptor and can confirm working with a startech kvm switch 👍
Other USB functions like storage, midi, and cdc needs to be turned off.
I'm using Circuitpython 7.2.0
I have a theory of the various keyboard descriptors discussed and the problem where the left Ctrl key seems to be pressed:
If the device sends reports prefixed with a 1-byte report ID of 0x01, the host is not expecting the prefixed report ID byte, and it interprets it as the modifiers field, and a modifiers field of 0x01 corresponds to the left Ctrl key. By calling usb_hid.enable((reference_keyboard,)...) with reference_keyboard having a report ID of 0, that causes CircuitPython to pass a report ID of 0 to TinyUSB which prevents it from prefixing reports with a report ID, solving the issue. The implication is that perhaps the contents of the keyboard descriptor doesn't matter as much as using a report ID of 0. Conclusion: for boot keyboards, always use a report ID of 0 for simple BIOSes. Maybe something to consider for CircuitPython's defaults, so that users don't need to specify a custom keyboard descriptor.
I may have found a way to keep CDC and possibly other functions enabled while also using a boot keyboard:
Keep the other functions enabled, but make the boot keyboard use an endpoint address of EP 1 IN. By doing this with plain Arduino (no CircuitPython), I was able to use a boot keyboard and CDC with my old PC.
Having tried a few variants of the boot descriptor from above comments and also just
usb_hid.enable((Device.KEYBOARD,), boot_device=1)
as documented here, I am still not able to get a working boot mode keyboard implementation to work with MacOS cold-boot (FileVault) login.
Can anyone confirm that they have boot-mode keyboard working for MacOS? (specifically a cold-boot, FileVault login).
Activity
dhalbert commentedon Aug 22, 2018
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.
bmosley commentedon Aug 22, 2018
Thanks. I completely understand and figured that this wasn’t currently supported but thought filing a bug was the best way to ask/suggest implementation of the feature. Is there any roadmap for future HID support? And MIDI sounds quite awesome too :)
dhalbert commentedon Aug 22, 2018
No specific timeline with dates, but your request is noted and it's a vote!
bmosley commentedon Aug 22, 2018
wonderful, thank you!
ATMakersBill commentedon Feb 9, 2020
This is marked as closed - was it done? If the fix was the same that I used in the XBox stuff which requires a custom build, is there any example of making this work?
dhalbert commentedon Feb 9, 2020
The fix requires a new descriptor and its own endpoint, at least. I'm not sure what else. Perhaps it could be added as a compile option for the descriptor generating Python script, and then later as a
boot.py. We can reopen this as long term.ATMakersBill commentedon Feb 9, 2020
I understand this not being in the core CP, but I do think it should remain open until there's some way to do it. I don't mind having to do a custom build (like we do for the XBox Adaptive Controller), but we have found lots of folks who really want to use devices through KVMs, and the composite devices don't work through them.
Here's our current pressing issue: https://www.facebook.com/groups/ATMakers/permalink/815702142174560/
ATMakersBill commentedon Feb 9, 2020
BTW, thanks Dan
dhalbert commentedon Feb 9, 2020
Do you need a bootmode mouse HID capability too?
ATMakersBill commentedon Feb 9, 2020
Um... I don't think so? I think the reason this is needed is that the KVM listens in on the keyboard commands and relays them. That lets it watch for trigger keystrokes ('scroll-scroll-enter') to jump between devices. We don't kneed the triggers, but the stupid KVM doesn't know what to do with composite devices. It might be that we just need a non-composite device w/no other profiles. Or only a single device (no serial/mass storage?)
I just know it is a known issue that "complicated" keyboards (even those with media keys) don't work.
bitboy85 commentedon Mar 5, 2021
Here is another vote for it :)
By now i've tried arduino, vusb, teensy, and pico pi and only teensy seems to have a full featured keyboard that works in BIOS and KVM devices.
@ATMakersBill
The teensy itself is also a composite device, having keyboard, mouse, gamepad, serial and still works fine with a KVM. So my best guess is, that it only depends on a correct implementation of the boot keyboard.
@dhalbert
As far as i know, a boot keyboard needs its own descriptor and endpoint and on top of it, it has two additional functions which are described in the usb docs.
dhalbert commentedon Mar 5, 2021
We are currently out of endpoints on most boards with 8 endpoints, having added a second CDC. If someone would like to contribute boot keyboard and mouse descriptors and compile flags, we'd be happy to include them. We would not turn them on normally, since they would remove having MIDI or the second CDC available by default.
This is another indicator for user-configurable USB and USB HID descriptors. However, we are pretty much out of space on the smallest builds, and don't have room to add the code for this. It would have to be on something other than the non-Express SAMD21's.
58 remaining items
retrospecced commentedon Dec 30, 2021
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!)
dhalbert commentedon Dec 30, 2021
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.bitboy85 commentedon Jan 2, 2022
Did some further testing and i think this is a weird one. I didn't change the descriptor. I just added usb_hid.get_boot_device() to check if the host requests boot mode -> Yes it does.
So i said it locks up bios or grub boot loader. I found this is only half true.
After sending the first keypress it locks up. Nether from the cp-device nor from the original keyboard an input is accepted.
EXCEPT: pressing and releasing the left CTRL-key does unlock it. The normal keyboard starts working again and even better, the cp-device now acts as a keyboard perfectly fine.
I do not use CTRL key anywhere in my script and i don't have any issues without cp-device so i don't think the error comes from the built-in keyboard.
The behaviour is like a permanently pressed ctrl button. CTRL left button. But only one time after pluggin it in.
Pressing ALT + DEL while in bios reboots the pc.
But i can't find a part in my code where i send ctrl without releasing it.
So its fixable with kbd.release(Keycode.CONTROL) at the beginning of my script. But it doesn't explain why it is pressed in the first place. Even more confusing: https://github.com/adafruit/Adafruit_CircuitPython_HID/blob/main/adafruit_hid/keyboard.py does a release_all in the init part. Maybe something is going wrong there. I'll take a closer look tomorrow.
retrospecced commentedon Jan 2, 2022
I'm using a KVM, but what you describe is the same behavior I was seeing before I started using the custom descriptor. https://w3c.github.io/uievents/tools/key-event-viewer.html would report left-control was always pressed before my code ran. Also, the KVM wouldn't forward any shift key presses to the host. So an "A" was always showing as control-a (lowercase), and so on.
Update boot.py to use modified boot descriptor
justinesmithies commentedon Feb 27, 2022
Just want to thank @retrospecced as using his boot.py resolved my issues whereby I couldn't access my ThinkPads BIOS or enter the grub menu on Arch Linux.
Now I just need to see if it's possible to change my Pico manufacturer info as lsusb shows Adafruit Pico and I'd like to change it to something else.
bitboy85 commentedon Feb 28, 2022
@retrospecced thx for your effort.
i tried your descriptor (and boot.py) but for some reason the device gets an exclamation mark in the device manager. So i was unable to test with the mentioned startech kvm switch.
dhalbert commentedon Feb 28, 2022
@retrospecced's original descriptor has some errors. Did you try the left-hand one in the diff above.
bitboy85 commentedon Mar 9, 2022
Ah thx. I've thought the provided source does already contain the fix.
Meanwhile i found another descriptor and can confirm working with a startech kvm switch 👍
Other USB functions like storage, midi, and cdc needs to be turned off.
I'm using Circuitpython 7.2.0
RefactorFactory commentedon Jul 8, 2022
I have a theory of the various keyboard descriptors discussed and the problem where the left Ctrl key seems to be pressed:
If the device sends reports prefixed with a 1-byte report ID of
0x01, the host is not expecting the prefixed report ID byte, and it interprets it as the modifiers field, and a modifiers field of0x01corresponds to the left Ctrl key. By callingusb_hid.enable((reference_keyboard,)...)withreference_keyboardhaving a report ID of 0, that causes CircuitPython to pass a report ID of 0 to TinyUSB which prevents it from prefixing reports with a report ID, solving the issue. The implication is that perhaps the contents of the keyboard descriptor doesn't matter as much as using a report ID of 0. Conclusion: for boot keyboards, always use a report ID of 0 for simple BIOSes. Maybe something to consider for CircuitPython's defaults, so that users don't need to specify a custom keyboard descriptor.I may have found a way to keep CDC and possibly other functions enabled while also using a boot keyboard:
Keep the other functions enabled, but make the boot keyboard use an endpoint address of EP 1 IN. By doing this with plain Arduino (no CircuitPython), I was able to use a boot keyboard and CDC with my old PC.
zan73 commentedon May 27, 2025
Having tried a few variants of the boot descriptor from above comments and also just
usb_hid.enable((Device.KEYBOARD,), boot_device=1)
as documented here, I am still not able to get a working boot mode keyboard implementation to work with MacOS cold-boot (FileVault) login.
Can anyone confirm that they have boot-mode keyboard working for MacOS? (specifically a cold-boot, FileVault login).
usb_hid: drop the report-ID prefix for a sole boot keyboard or mouse
usb_hid: honor a boot protocol request that arrives after startup
usb_hid: honor a boot protocol request that arrives after startup