adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.04k stars 1.2k forks source link

Unable to use HID keyboard in boot OS (macOS boot screen) #1136

Open bmosley opened 6 years ago

bmosley commented 6 years ago

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:

  1. plug trinket m0 into usb on mac
  2. 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()
dhalbert commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

No specific timeline with dates, but your request is noted and it's a vote!

bmosley commented 6 years ago

wonderful, thank you!

ATMakersBill commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

BTW, thanks Dan

dhalbert commented 4 years ago

Do you need a bootmode mouse HID capability too?

ATMakersBill commented 4 years ago

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 commented 3 years ago

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 commented 3 years ago

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.

hathach commented 3 years ago

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

dhalbert commented 3 years ago

Aha, great, thanks! I will await your example. It would be more complicated to add a boot mouse, but at least having a keyboard will satisfy many needs.

hathach commented 3 years ago

each interface can only either support boot keyboard or mouse, to support both, we need an extra interface though.

deshipu commented 3 years ago

But when in boot mode, you don't need MSC and CDC and MIDI, no?

dhalbert commented 3 years ago

But when in boot mode, you don't need MSC and CDC and MIDI, no?

Good point - it's more a question of having code space on the smallest builds to switch back and forth.

bitboy85 commented 3 years ago

@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

tannewt commented 3 years ago

I believe that BOOT mode is missing n-key rollover so we'll want to switch between the two if we can.

hathach commented 3 years ago

@bitboy85 can you post the descriptor of your real keyboard here along with its hid descriptor for the reference. If it is too long, you could move it to a gist and have a link here.

bitboy85 commented 3 years ago

@hathach https://gist.github.com/bitboy85/543b34a9505280ead8103a4d4d9cab26 2 different keyboards, no mixed device.

I remember a doc that says, a boot keyboard endpoint requires a packet size of 8 byte.

As additional reference: https://www.usb.org/sites/default/files/hid1_11.pdf page 50-54: GET_PROTOCOL and SET_PROTOCOL request for boot device page 62: Keyboard implementation page 74: Boot keyboard requirements

hathach commented 3 years ago

thanks for the descriptor, and the reference. I just go through the hid 1.1 briefly again, and didn't see any requirement for boot protocol to has its own interface. The consumer keyboard has the luxury of additional endpoint to implement it in that way. Though we will see how this works out.

tannewt commented 3 years ago

@obra may know what we need to be a boot keyboard too.

akaenner commented 3 years ago

I also need a keyboard that works at boot time. I'm currently working on a software for a DIY split keyboard with each part having a Raspberry Pi Pico in it. All features I planned are now implemented and I will share the code on GitHub in the comming weeks. I just realized, that it does not work at boot time (at least not under macOS). I hope that this issue can be fixed, because otherwise building fully fledged custom keyboards is out of reach by using CircuitPython.

obra commented 3 years ago

You want to have a look at the descriptors we use for https://github.com/keyboardio/KeyboardioHID The idea is that you should offer up both a Boot Protocol descriptor and a bitmap descriptor.

On Thu, Mar 11, 2021, at 1:18 AM, akaenner wrote:

I also need a keyboard that works at boot time. I'm currently working on a software for a DIY split keyboard with each part having a Raspberry Pi Pico in it. All features I planned are now implemented and I will share the code on GitHub in the comming weeks. I just realized, that it does not work at boot time (at least not under macOS). I hope that this issue can be fixed, because otherwise building fully fledged custom keyboards is out of reach by using CircuitPython.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/adafruit/circuitpython/issues/1136#issuecomment-796591688, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALC2AOHJ7R2B6YSWKGPZ3TDCDGTANCNFSM4FQ4LPWA.

bitboy85 commented 3 years ago

As it is possible to config USB devices on a custom build, any hints where to start when trying to implement this? https://learn.adafruit.com/building-circuitpython/customizing-usb-devices

I've read some docs about the requirement.

dhalbert commented 3 years ago

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.

azcaban commented 3 years ago

interested in the request. i have a macropad that works with circuitpython, which i would like to use to access to bios. here the thing https://learn.adafruit.com/itsybitsy-keybow-mechanical-keypad?view=all

dhalbert commented 3 years ago

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 commented 3 years ago

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-R176

You could also use tud_hid_n_get_protocol() to get the current mode and send construct the report accordingly.

dhalbert commented 2 years ago

I am now working on this. I ran into an issue of BIOS's expecting the interface number to be 0 for the keyboard, and not handling non-zero values properly. See https://github.com/hathach/tinyusb/issues/1129. I can push the HID interface to position 0, but right now the Windows drivers package we have assumes the CDC is in position 0. I think I have a way of restructuring the drivers to handle that, though, by imitating what Windows 10 does for usbser.sys. I'll test that it, which might considerably simplify the Windows 10 driver package as well.

dhalbert commented 2 years ago

I am now working on this. I ran into an issue of BIOS's expecting the interface number to be 0 for the keyboard, and not handling non-zero values properly. See hathach/tinyusb#1129. I can push the HID interface to position 0, but right now the Windows drivers package we have assumes the CDC is in position 0. I think I have a way of restructuring the drivers to handle that, though, by imitating what Windows 10 does for usbser.sys. I'll test that it, which might considerably simplify the Windows 7/8.1 driver package as well.

It turns out this does not work for Windows security policy reasons. See fuller writeups in https://github.com/adafruit/Adafruit_Windows_Drivers/issues/34 and https://github.com/hathach/tinyusb/issues/1129#issuecomment-940498866

dhalbert commented 2 years ago

I am going to put this on ice for now. As long as we continue to support Windows 7 and 8.1, we cannot move the Device Interface numbers around so that HID is Interface 0 instead of CDC. In the long run, it could be possible, but boot keyboard support may become less important. I have some BIOS examples that don't need boot keyboards anyway.

akaenner commented 2 years ago

How difficult is it to create a branch of CircuitPython with the HID device at position 0?

I'm currently writing a documentation for my upcoming DIY keyboard. The firmware is based on CircuitPython and is much easier to use than others like QMK. Not being able to emulate a boot keyboard is of course a big show stopper. For example: under macOS you can not login without a boot keyboard if your hard disk is encrypted and most users encrypt their HD.

Kaenner-Keyboard

hathach commented 2 years ago

macOS may still work, I think the issue Dan mentioned above only happens with old PC bios ? And MAYBE we don't have to make HID as interface 0 in macOS bios

akaenner commented 2 years ago

I can assure you that you cannot log into a Mac if the hard drive is encrypted because I sit in front of a Mac all day.

"The only wrinkle in a CircuitPython-based keyboard is not working well in boot phase: things like UEFI/BIOS settings, waking up from hybernation, or FileVault password typing in macOS. It is probably related to device enumeration: a CircuitPython board enumerates several USB virtual devices (storage, keyboard, mouse, serial) and UEFI expects keyboards to be keyboards only, or the keyboard should be the first enumeration."

Found here - scroll to the bottom: KMK87

These are exactly the same problems I currently have with my keyboard.

dhalbert commented 2 years ago

I can make HID always be interface #0 just by reordering some code. But it will cause problems on Windows 7 and 8.1, which expect CDC to be at interface #0.

I have a branch where I did this work. I can give you a test UF2. Tell me the board you need a build for. If you are used to building CircuitPython yourself, the branch is https://github.com/dhalbert/circuitpython/tree/hid-boot-protocol .

Then you'll try this: in boot.py, turn off all other USB devices except HID. So something like:

# Normally, disable all devices except HID.
# But when booting, if a particular key is held down, don't hide CIRCUITPY and the REPL.
maintenance_mode = <some key pressed down> 

if not maintenance_mode:
    storage.disable_usb_drive()
    usb_cdc.disable()
    usb_midi.disable()

usb_hid.enable((your_keyboard_device,), boot_device=1 if not maintenance_mode else 0)

boot_device is new in this branch. With the code above, and a build from the branch, HID should end up at interface #0, because no other devices will be put in the descriptor. The interface numbers are assigned sequentially in CircuitPython 7.0.0, so HID will be at #0. In 6.3.0 and earlier, they were given fixed assignments: CDC was always at #0, HID was at something else, etc. (EDIT: boot_protocol -> boot_device)

How do you get a Mac into this boot state? I assume you hold down some keys when power-cycling. I would like to have another test besides an older BIOS or UEFI.

akaenner commented 2 years ago

Thank you very much. I just managed to built your branch of CircuitPython. I will test it over the weekend.

With boot mode I simply mean: Boot the mac and try to login. A CircuitPython keyboard does not work at this point. It event does no work if the mac is in sleep mode and you try to wake it up with a CircuitPython keyboard. If you press a key in sleep mode, then the Mac does not wake up. But worse, the keyboard does not work after waking up. You then have to reconnect the keyboard or reset it. If you wake up the Mac and login by using a normal keyboard, the CircuitPython keyboard works.

akaenner commented 2 years ago

Hi Dan,

I compiled your hid-boot-protocol branch and tested it with my keyboard but I'm sure I'm doing something wrong. Maybe you can help me. Here is the message in boot_out.txt:

Adafruit CircuitPython 5.0.0-alpha.0-11771-g13210d2cc-dirty on 2021-10-15; Raspberry Pi Pico with rp2040
Board ID:raspberry_pi_pico
boot.py output:
Traceback (most recent call last):
  File "boot.py", line 41, in <module>
TypeError: extra keyword arguments given

Here is the code in line 41 in boot.py: usb_hid.enable((usb_hid.Device.KEYBOARD,), boot_protocol=1 if not maintenance_mode else 0)

dhalbert commented 2 years ago

@akaenner Ah, sorry, I changed the name of the argument from boot_protocol to boot_device for clarity but didn't remember that. Use:

usb_hid.enable((usb_hid.Device.KEYBOARD,), boot_device=1 if not maintenance_mode else 0)
akaenner commented 2 years ago

ok, here is the part of my boot.py which disables other devices and enables just the keyboard.

if not maintenance_mode:
    storage.disable_usb_drive()
    usb_cdc.disable()
    usb_midi.disable()

usb_hid.enable((usb_hid.Device.KEYBOARD,), boot_device=1 if not maintenance_mode else 0)

The USB-Stick does not show up in the Finder and if I'm already logged in to the mac, the keyboard works. But the keyboard still does not work when I try to login on a mac with an encrypted HD.

Here is the output from lsusb -v (only the pico part)

Bus 020 Device 014: ID 239a:80f4  
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x239a 
  idProduct          0x80f4 
  bcdDevice            1.00
  iManufacturer           1 Raspberry Pi
  iProduct                2 Pico
  iSerial                 3 E6609CB2D37A3F29
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength          195
    bNumInterfaces          5
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Association:
      bLength                 8
      bDescriptorType        11
      bFirstInterface         0
      bInterfaceCount         2
      bFunctionClass          2 Communications
      bFunctionSubClass       2 Abstract (modem)
      bFunctionProtocol       0 None
      iFunction               0 
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         2 Communications
      bInterfaceSubClass      2 Abstract (modem)
      bInterfaceProtocol      0 None
      iInterface              4 CircuitPython CDC control
      CDC Header:
        bcdCDC               1.10
      CDC Call Management:
        bmCapabilities       0x01
          call management
        bDataInterface          1
      CDC ACM:
        bmCapabilities       0x02
          line coding and serial state
      CDC Union:
        bMasterInterface        0
        bSlaveInterface         1 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval              16
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      0 
      iInterface              5 CircuitPython CDC data
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              6 CircuitPython HID
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength     156
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               8
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x03  EP 3 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               8
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        3
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass         1 Audio
      bInterfaceSubClass      1 Control Device
      bInterfaceProtocol      0 
      iInterface              8 CircuitPython Audio
      AudioControl Interface Descriptor:
        bLength                 9
        bDescriptorType        36
        bDescriptorSubtype      1 (HEADER)
        bcdADC               1.00
        wTotalLength            9
        bInCollection           1
        baInterfaceNr( 0)       4
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        4
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         1 Audio
      bInterfaceSubClass      3 MIDI Streaming
      bInterfaceProtocol      0 
      iInterface              7 CircuitPython MIDI
      MIDIStreaming Interface Descriptor:
        bLength                 7
        bDescriptorType        36
        bDescriptorSubtype      1 (HEADER)
        bcdADC               1.00
        wTotalLength           37
      MIDIStreaming Interface Descriptor:
        bLength                 6
        bDescriptorType        36
        bDescriptorSubtype      2 (MIDI_IN_JACK)
        bJackType               1 Embedded
        bJackID                 1
        iJack                   9 CircuitPython usb_midi.ports[0]
      MIDIStreaming Interface Descriptor:
        bLength                 6
        bDescriptorType        36
        bDescriptorSubtype      2 (MIDI_IN_JACK)
        bJackType               2 External
        bJackID                 2
        iJack                   0 
      MIDIStreaming Interface Descriptor:
        bLength                 9
        bDescriptorType        36
        bDescriptorSubtype      3 (MIDI_OUT_JACK)
        bJackType               1 Embedded
        bJackID                 3
        bNrInputPins            1
        baSourceID( 0)          2
        BaSourcePin( 0)         1
        iJack                  10 CircuitPython usb_midi.ports[0]
      MIDIStreaming Interface Descriptor:
        bLength                 9
        bDescriptorType        36
        bDescriptorSubtype      3 (MIDI_OUT_JACK)
        bJackType               2 External
        bJackID                 4
        bNrInputPins            1
        baSourceID( 0)          1
        BaSourcePin( 0)         1
        iJack                   0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x04  EP 4 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
        MIDIStreaming Endpoint Descriptor:
          bLength                 5
          bDescriptorType        37
          bDescriptorSubtype      1 (GENERAL)
          bNumEmbMIDIJack         1
          baAssocJackID( 0)       1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x84  EP 4 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
        MIDIStreaming Endpoint Descriptor:
          bLength                 5
          bDescriptorType        37
          bDescriptorSubtype      1 (GENERAL)
          bNumEmbMIDIJack         1
          baAssocJackID( 0)       3
Device Status:     0x0000
  (Bus Powered)
dhalbert commented 2 years ago

MIDI should not be showing up; you are trying to turn it off in boot.py. Check your boot_out.txt to see if there are errors reported in boot.py, or perhaps the "maintenance_mode" switch is being checked in a backwards way.

akaenner commented 2 years ago

I forgot to import usb_cdc. Now the output from lsusb command for the Pico looks better:

Bus 020 Device 013: ID 239a:80f4  
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x239a 
  idProduct          0x80f4 
  bcdDevice            1.00
  iManufacturer           1 Raspberry Pi
  iProduct                2 Pico
  iSerial                 3 E6609CB2D37A3F29
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           41
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      1 Keyboard
      iInterface              4 CircuitPython HID
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      67
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               8
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               8
Device Status:     0x0000
  (Bus Powered)

Unfortunately, the keyboard still doesn't work with a Mac. I can not login to the desktop and it does not wake up from sleep. Here you can see the output from lsusb for my other keyboard (a TypeMatrix) which works fine. Maybe you can compare the two descriptions and maybe then you have an idea of what needs to be changed. For example: at the end of the TypeMatrix output there is the text "Remote Wakeup Enabled" which is not in the output for the Pico.

I am happy to test other versions if you like.

Bus 020 Device 007: ID 1e54:2030 TypeMatrix 2030 USB Keyboard
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x1e54 TypeMatrix
  idProduct          0x2030 2030 USB Keyboard
  bcdDevice            1.50
  iManufacturer           1 TypeMatrix.com
  iProduct                2 USB Keyboard
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           59
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      1 Keyboard
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      64
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval              10
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      94
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval              10
Device Status:     0x0002
  (Bus Powered)
  Remote Wakeup Enabled
dhalbert commented 2 years ago

The wakeup problem is different than boot keyboard, and I need to work on that separately. If you plug in the RP2040 when the password prompt appears, does it still not work?

My Macs are not set up for this disk encryption, but there may be other boot-time choices (option-shift-whatever, etc.) that I can use to test without having to set one up for encryption. Do you know of some boot-time entry screens similar to the password request?

akaenner commented 2 years ago

If you plug in the RP2040 when the password prompt appears, does it still not work?

No, this also does not work.

Do you know of some boot-time entry screens similar to the password request?

I don"t know how to reproduce this behaviour without an encrypted HD. But you can encrypt your HD at any time. It just takes some time to finish. See System Preferences > Security & Privacy > File Vault

dhalbert commented 2 years ago

@akaenner I have a simpler test of this, which is to start the Mac while holding down the "D" key, to get it into diagnostics mode. Then it lists the available wifi networks (wait for the spinning to stop), and an external keyboard's up and down arrow keys can go up and down the list of networks.

The "D" key is for Intel Macs. For M1 ("Apple silicon"), you just keep holding down the power button to get into a startup menu. https://support.apple.com/en-us/HT202731

Anyway, what I see on an Intel MacBook is that a MacroPad set up for boot keyboard and sending up and down arrows does not work, but a vanilla Dell external keyboard does. So this gives me something to work toward.

DietmarSchwertberger commented 2 years ago

I just wanted to tell that I'm also looking forward to a better HID support. I'm on Windows and often after booting I need to un- and re-plug my Raspberry Pi Pico based keyboard first. (No password or keypress required for booting into Windows desktop.)

Also, usage with the KVM switch is not robust (the KVM is built into my monitor). Thanks for your work on this issue.

bitboy85 commented 2 years ago

So currently the default firmware has cdc hard coded to interface 0 because the windows driver is also hard coded to interface 0. Is this a requirement by windows or could it also use a hardcoded interface number 1? (having 0 as unused/reserved for boot keyboard).

What about making it the default firmware where the user can choose the interface number?

dhalbert commented 2 years ago

Fixing CDC to interface is a requirement due to the limitations of third-party drivers in Windows. A third-party cannot write a "class driver", which identifies the device by class and subclass. That is reserved for system-supplied drivers. I can only identify the device by PID/VID, and (optionally) interface number. If I scramble the interface numbers, then it can assign the wrong driver (e.g. MSC for CDC). I have also seen that at least some versions of Windows expected CDC on interface 0 always (incorrectly). In addition, at least some OS's expect the interface numbers to be sequential, so I assign them sequentially. And it would be a big bang to move CDC away from interface 0, and could cause trouble for current Windows users.

Windows driver assignments can get confused if the same device starts presenting different devices on varying interface numbers.

We don't need the Adafruit drivers package on Windows 10, because it has a built-in class driver for CDC. So if we stopped supporting Windows 7 and 8.1, the problem would go away.

dhalbert commented 2 years ago

Boot keyboard support on interface 0 is now merged into the main branch on CircuitPython, and will be in CircuitPython 7.1.0. Right now the order of the interfaces is fixed, so we are just turning off enough to bring HID to the top as interface 0. Since most keyboard projects are standalone things, and you don't want CIRCUITPY and CDC to be present anyway, especially in the case of a boot keyboard, this didn't seem like a serious limitation.

The boot keyboard suipport works on a few PC BIOS's I tested it on. It does not work on Mac yet, so I am leaving this issue open. I don't understand why, and the errors appear to be below the level of the descriptors: I am seeing unusual USB transmissions, so the issue may be at the USB protocol level.