Skip to content

Commit

Permalink
Merge pull request #118 from eightycc/issue_117
Browse files Browse the repository at this point in the history
Fix #117: Rework host USB ready timeout
  • Loading branch information
dhalbert authored Sep 14, 2023
2 parents 30097f7 + 9b398e0 commit da9fa58
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 42 deletions.
51 changes: 43 additions & 8 deletions adafruit_hid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@

# imports
from __future__ import annotations
import time

try:
import supervisor
except ImportError:
supervisor = None

try:
from typing import Sequence
Expand All @@ -31,17 +37,46 @@


def find_device(
devices: Sequence[usb_hid.Device], *, usage_page: int, usage: int
devices: Sequence[usb_hid.Device],
*,
usage_page: int,
usage: int,
timeout: int = None,
) -> usb_hid.Device:
"""Search through the provided sequence of devices to find the one with the matching
usage_page and usage."""
usage_page and usage.
:param timeout: Time in seconds to wait for USB to become ready before timing out.
Defaults to None to wait indefinitely."""

if hasattr(devices, "send_report"):
devices = [devices] # type: ignore
for device in devices:
device = None
for dev in devices:
if (
device.usage_page == usage_page
and device.usage == usage
and hasattr(device, "send_report")
dev.usage_page == usage_page
and dev.usage == usage
and hasattr(dev, "send_report")
):
return device
raise ValueError("Could not find matching HID device.")
device = dev
break
if device is None:
raise ValueError("Could not find matching HID device.")

if supervisor is None:
# Blinka doesn't have supervisor (see issue Adafruit_Blinka#711), so wait
# one second for USB to become ready
time.sleep(1.0)
elif timeout is None:
# default behavior: wait indefinitely for USB to become ready
while not supervisor.runtime.usb_connected:
time.sleep(1.0)
else:
# wait up to timeout seconds for USB to become ready
for _ in range(timeout):
if supervisor.runtime.usb_connected:
return device
time.sleep(1.0)
raise OSError("Failed to initialize HID device. Is USB connected?")

return device
18 changes: 7 additions & 11 deletions adafruit_hid/consumer_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

# pylint: disable=wrong-import-position
import struct
import time
from . import find_device

try:
Expand All @@ -31,26 +30,23 @@
class ConsumerControl:
"""Send ConsumerControl code reports, used by multimedia keyboards, remote controls, etc."""

def __init__(self, devices: Sequence[usb_hid.Device]) -> None:
def __init__(self, devices: Sequence[usb_hid.Device], timeout: int = None) -> None:
"""Create a ConsumerControl object that will send Consumer Control Device HID reports.
:param timeout: Time in seconds to wait for USB to become ready before timing out.
Defaults to None to wait indefinitely.
Devices can be a sequence of devices that includes a Consumer Control device or a CC device
itself. A device is any object that implements ``send_report()``, ``usage_page`` and
``usage``.
"""
self._consumer_device = find_device(devices, usage_page=0x0C, usage=0x01)
self._consumer_device = find_device(
devices, usage_page=0x0C, usage=0x01, timeout=timeout
)

# Reuse this bytearray to send consumer reports.
self._report = bytearray(2)

# Do a no-op to test if HID device is ready.
# If not, wait a bit and try once more.
try:
self.send(0x0)
except OSError:
time.sleep(1)
self.send(0x0)

def send(self, consumer_code: int) -> None:
"""Send a report to do the specified consumer control action,
and then stop the action (so it will not repeat).
Expand Down
18 changes: 7 additions & 11 deletions adafruit_hid/keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
* Author(s): Scott Shawcroft, Dan Halbert
"""

import time
from micropython import const
import usb_hid

Expand Down Expand Up @@ -39,14 +38,19 @@ class Keyboard:

# No more than _MAX_KEYPRESSES regular keys may be pressed at once.

def __init__(self, devices: Sequence[usb_hid.Device]) -> None:
def __init__(self, devices: Sequence[usb_hid.Device], timeout: int = None) -> None:
"""Create a Keyboard object that will send keyboard HID reports.
:param timeout: Time in seconds to wait for USB to become ready before timing out.
Defaults to None to wait indefinitely.
Devices can be a sequence of devices that includes a keyboard device or a keyboard device
itself. A device is any object that implements ``send_report()``, ``usage_page`` and
``usage``.
"""
self._keyboard_device = find_device(devices, usage_page=0x1, usage=0x06)
self._keyboard_device = find_device(
devices, usage_page=0x1, usage=0x06, timeout=timeout
)

# Reuse this bytearray to send keyboard reports.
self.report = bytearray(8)
Expand All @@ -65,14 +69,6 @@ def __init__(self, devices: Sequence[usb_hid.Device]) -> None:
# No keyboard LEDs on.
self._led_status = b"\x00"

# Do a no-op to test if HID device is ready.
# If not, wait a bit and try once more.
try:
self.release_all()
except OSError:
time.sleep(1)
self.release_all()

def press(self, *keycodes: int) -> None:
"""Send a report indicating that the given keys have been pressed.
Expand Down
19 changes: 7 additions & 12 deletions adafruit_hid/mouse.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
* Author(s): Dan Halbert
"""
import time

from . import find_device

try:
Expand All @@ -29,14 +27,19 @@ class Mouse:
MIDDLE_BUTTON = 4
"""Middle mouse button."""

def __init__(self, devices: Sequence[usb_hid.Device]):
def __init__(self, devices: Sequence[usb_hid.Device], timeout: int = None) -> None:
"""Create a Mouse object that will send USB mouse HID reports.
:param timeout: Time in seconds to wait for USB to become ready before timing out.
Defaults to None to wait indefinitely.
Devices can be a sequence of devices that includes a keyboard device or a keyboard device
itself. A device is any object that implements ``send_report()``, ``usage_page`` and
``usage``.
"""
self._mouse_device = find_device(devices, usage_page=0x1, usage=0x02)
self._mouse_device = find_device(
devices, usage_page=0x1, usage=0x02, timeout=timeout
)

# Reuse this bytearray to send mouse reports.
# report[0] buttons pressed (LEFT, MIDDLE, RIGHT)
Expand All @@ -45,14 +48,6 @@ def __init__(self, devices: Sequence[usb_hid.Device]):
# report[3] wheel movement
self.report = bytearray(4)

# Do a no-op to test if HID device is ready.
# If not, wait a bit and try once more.
try:
self._send_no_move()
except OSError:
time.sleep(1)
self._send_no_move()

def press(self, buttons: int) -> None:
"""Press the given mouse buttons.
Expand Down

0 comments on commit da9fa58

Please sign in to comment.