adafruit / Adafruit_Blinka

Add CircuitPython hardware API and libraries to MicroPython & CPython devices
https://learn.adafruit.com/circuitpython-on-raspberrypi-linux
MIT License
453 stars 340 forks source link

Support `supervisor.runtime.usb_connected`, at least #711

Open dhalbert opened 1 year ago

dhalbert commented 1 year ago

usb_hid is supported in a limited way in Blinka, using usb_gadget.

https://github.com/adafruit/Adafruit_CircuitPython_HID/pull/118 would like to test for USB enumeration having happened. Right now there is no support at all for supervisor in Blinka. But it would be nice to add some support for the USB-related supervisor operations.

eightycc commented 1 year ago

@dhalbert I've looked over Blinka HID support and my understanding is that it is implemented only for boards running Linux. Before I go any further, do you know if USB ports on any of these Raspberry Pi boards will work for testing: Pi 4, Pi Zero, Pi Zero 2? The USB ports on at least the Zeroes are suitable for use as devices. So, do I understand the limitation of Blinka HID support correctly?

dhalbert commented 1 year ago

Raspberry Pi Zeros definitely support OTG (USB gadget). RPI 4 does, but it appears to be only through the USB-C port. RPi 3 and older do not.

eightycc commented 1 year ago

@dhalbert The good news is that Adafruit_CircuitPython_HID works fine with the existing 1 second timeout or with no timeout at all. USB gadget allows instantiation and initialization of a HID device with no host connection. We get into the weeds when attempting to use a HID device with no connection. For example, keyboard.press() fails with:

Traceback (most recent call last):
  File "/home/rabeles/hid_kbd_demo.py", line 62, in <module>
    keyboard.press(control_key, key)  # "Press"...
  File "/home/rabeles/.local/lib/python3.9/site-packages/adafruit_hid/keyboard.py", line 96, in press
    self._keyboard_device.send_report(self.report)
  File "/home/rabeles/.local/lib/python3.9/site-packages/usb_hid.py", line 66, in send_report
    fd.write(report)
BrokenPipeError: [Errno 108] Cannot send after transport endpoint shutdown

We could wrap fd calls in Blinka's usb_hid with a try and throw a more informative error? For a use case where keeping the HID device alive across disconnects/connects is important, the user code could catch and retry. This would push the fail/retry decision to user code.

dhalbert commented 1 year ago

@eightycc You could wrap the calls, but the user code is still going to need to do something, as you point out. But even if you can create the device when it's not plugged in, is there some way to test whether an enumeration happened with a USB host so we can implement .usb_connected?

eightycc commented 1 year ago

@dhalbert The best we can do is implement a .usb_connected inside usb_hid. The reason is that until usb_hid starts, there is no USB gadget filesystem that we can look into and libcomposite is just hanging there quietly waiting for one to get built.

I think we'll still need some wrapping. It's not uncommon for a USB HID device to get hot plugged/unplugged.

dhalbert commented 1 year ago

The reason is that until usb_hid starts, there is no USB gadget filesystem

So if there is no gadget tree in the fileystem, then could supervisor.runtime.usb_connected check for its existence an d then confidently return false if there isn't one? And if there is one, then it could check something there?

eightycc commented 1 year ago

Believe me, I'm not trying to belabor this issue, just trying to think it through. Checking for the existence of the gadget fs is certainly feasible, getting it to tell us the state of the host connection short of pushing a null report is problematical. I'm looking at libcomposite in the Linux kernel source now to see what it exposes. Assuming there is something there that can be queried, there could be more than one HID device represented in the filesystem. Which one would we query?

dhalbert commented 1 year ago

Not trying to give you a hard time :laughing: . I haven't looked at this stuff at all, and it can be quite arcane. Trying to find out things about USB device was also arcane when I had to look at that. Pushing a null report is device-specific, and I was glad we got rid of that in the latest release.

eightycc commented 1 year ago

I haven't looked at this stuff at all, and it can be quite arcane.

Yes, it really is. The USB designers must have had access to some really good herb.

A cursory look at drivers/usb/gadget in the Linux kernel source tree leads me to think there is a way to get what we need. It's not going to win any prizes for clarity as it involves exchanging binary reports.

I don't want to burn up too many of your cycles on this, and I'm anxious to look into a more interesting issue, so let me put this aside for a few days (I don't think there's any urgency) and I'll get back to it later.