]> Repositories - Adafruit_Blinka-hackapet.git/blobdiff - src/usb_hid.py
Add line to remove existing .egg-info directories
[Adafruit_Blinka-hackapet.git] / src / usb_hid.py
index c55a28d18d2d75f41c30743563eb79df0b5b0d69..791647b288f15dd17c3d99df947b5831daf77934 100644 (file)
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
 """
 `usb_hid` - support for usb hid devices via usb_gadget driver
 ===========================================================
@@ -15,8 +18,8 @@ import atexit
 import sys
 
 for module in ["dwc2", "libcomposite"]:
-    if Path("/proc/modules").read_text().find(module) == -1:
-        raise Exception(
+    if Path("/proc/modules").read_text(encoding="utf-8").find(module) == -1:
+        raise Exception(  # pylint: disable=broad-exception-raised
             "%s module not present in your kernel. did you insmod it?" % module
         )
 this = sys.modules[__name__]
@@ -51,6 +54,10 @@ class Device:
         self._last_received_report = None
 
     def send_report(self, report: bytearray, report_id: int = None):
+        """Send an HID report. If the device descriptor specifies zero or one report id's,
+        you can supply `None` (the default) as the value of ``report_id``.
+        Otherwise you must specify which report id to use when sending the report.
+        """
         report_id = report_id or self.report_ids[0]
         device_path = self.get_device_path(report_id)
         with open(device_path, "rb+") as fd:
@@ -91,7 +98,7 @@ class Device:
                 "%s/functions/hid.usb%s/dev"
                 % (this.gadget_root, report_id or self.report_ids[0])
             )
-            .read_text()
+            .read_text(encoding="utf-8")
             .strip()
             .split(":")[1]
         )
@@ -254,7 +261,7 @@ Device.MOUSE = Device(
     ),
     usage_page=0x1,
     usage=0x02,
-    report_ids=[2],
+    report_ids=[0x02],
     in_report_lengths=[4],
     out_report_lengths=[0],
 )
@@ -443,22 +450,22 @@ Device.BOOT_MOUSE = Device(
     ),
     usage_page=0x1,
     usage=0x02,
-    report_ids=[1],
+    report_ids=[0],
     in_report_lengths=[4],
     out_report_lengths=[0],
 )
 
 
 def disable() -> None:
-    """Do not present any USB HID devices to the host computer.
-    Can be called in ``boot.py``, before USB is connected.
-    The HID composite device is normally enabled by default,
-    but on some boards with limited endpoints, including STM32F4,
-    it is disabled by default. You must turn off another USB device such
-    as `usb_cdc` or `storage` to free up endpoints for use by `usb_hid`.
-    """
+    """Do not present any USB HID devices to the host computer.
+    Can be called in ``boot.py``, before USB is connected.
+    The HID composite device is normally enabled by default,
+    but on some boards with limited endpoints, including STM32F4,
+    it is disabled by default. You must turn off another USB device such
+    as `usb_cdc` or `storage` to free up endpoints for use by `usb_hid`.
+    """
     try:
-        Path("%s/UDC" % this.gadget_root).write_text("")
+        Path("%s/UDC" % this.gadget_root).write_text("", encoding="utf-8")
     except FileNotFoundError:
         pass
     for symlink in Path(this.gadget_root).glob("configs/**/hid.usb*"):
@@ -483,51 +490,53 @@ def disable() -> None:
         pass
     this.devices = []
 
+
 atexit.register(disable)
 
 
 def enable(requested_devices: Sequence[Device], boot_device: int = 0) -> None:
-# """Specify which USB HID devices that will be available.
-# Can be called in ``boot.py``, before USB is connected.
-#
-# :param Sequence devices: `Device` objects.
-#   If `devices` is empty, HID is disabled. The order of the ``Devices``
-#   may matter to the host. For instance, for MacOS, put the mouse device
-#   before any Gamepad or Digitizer HID device or else it will not work.
-# :param int boot_device: If non-zero, inform the host that support for a
-#   a boot HID device is available.
-#   If ``boot_device=1``, a boot keyboard is available.
-#   If ``boot_device=2``, a boot mouse is available. No other values are allowed.
-#   See below.
-#
-# If you enable too many devices at once, you will run out of USB endpoints.
-# The number of available endpoints varies by microcontroller.
-# CircuitPython will go into safe mode after running ``boot.py`` to inform you if
-# not enough endpoints are available.
-#
-# **Boot Devices**
-#
-# Boot devices implement a fixed, predefined report descriptor, defined in
-# https://www.usb.org/sites/default/files/hid1_12.pdf, Appendix B. A USB host
-# can request to use the boot device if the USB device says it is available.
-# Usually only a BIOS or other kind of limited-functionality
-# host needs boot keyboard support.
-#
-# For example, to make a boot keyboard available, you can use this code::
-#
-#   usb_hid.enable((Device.KEYBOARD), boot_device=1)  # 1 for a keyboard
-#
-# If the host requests the boot keyboard, the report descriptor provided by `Device.KEYBOARD`
-# will be ignored, and the predefined report descriptor will be used.
-# But if the host does not request the boot keyboard,
-# the descriptor provided by `Device.KEYBOARD` will be used.
-#
-# The HID boot device must usually be the first or only device presented by CircuitPython.
-# The HID device will be USB interface number 0.
-# To make sure it is the first device, disable other USB devices, including CDC and MSC (CIRCUITPY).
-# If you specify a non-zero ``boot_device``, and it is not the first device, CircuitPython
-# will enter safe mode to report this error.
-# """
+    """Specify which USB HID devices that will be available.
+    Can be called in ``boot.py``, before USB is connected.
+
+    :param Sequence devices: `Device` objects.
+      If `devices` is empty, HID is disabled. The order of the ``Devices``
+      may matter to the host. For instance, for MacOS, put the mouse device
+      before any Gamepad or Digitizer HID device or else it will not work.
+    :param int boot_device: If non-zero, inform the host that support for a
+      a boot HID device is available.
+      If ``boot_device=1``, a boot keyboard is available.
+      If ``boot_device=2``, a boot mouse is available. No other values are allowed.
+      See below.
+
+    If you enable too many devices at once, you will run out of USB endpoints.
+    The number of available endpoints varies by microcontroller.
+    CircuitPython will go into safe mode after running ``boot.py`` to inform you if
+    not enough endpoints are available.
+
+    **Boot Devices**
+
+    Boot devices implement a fixed, predefined report descriptor, defined in
+    https://www.usb.org/sites/default/files/hid1_12.pdf, Appendix B. A USB host
+    can request to use the boot device if the USB device says it is available.
+    Usually only a BIOS or other kind of limited-functionality
+    host needs boot keyboard support.
+
+    For example, to make a boot keyboard available, you can use this code::
+
+      usb_hid.enable((Device.KEYBOARD), boot_device=1)  # 1 for a keyboard
+
+    If the host requests the boot keyboard, the report descriptor provided by `Device.KEYBOARD`
+    will be ignored, and the predefined report descriptor will be used.
+    But if the host does not request the boot keyboard,
+    the descriptor provided by `Device.KEYBOARD` will be used.
+
+    The HID boot device must usually be the first or only device presented by CircuitPython.
+    The HID device will be USB interface number 0.
+    To make sure it is the first device, disable other USB devices, including CDC and MSC
+    (CIRCUITPY).
+    If you specify a non-zero ``boot_device``, and it is not the first device, CircuitPython
+    will enter safe mode to report this error.
+    """
     this.boot_device = boot_device
 
     if len(requested_devices) == 0:
@@ -576,18 +585,30 @@ def enable(requested_devices: Sequence[Device], boot_device: int = 0) -> None:
     # """
     Path("%s/functions" % this.gadget_root).mkdir(parents=True, exist_ok=True)
     Path("%s/configs" % this.gadget_root).mkdir(parents=True, exist_ok=True)
-    Path("%s/bcdDevice" % this.gadget_root).write_text("%s" % 1)  # Version 1.0.0
-    Path("%s/bcdUSB" % this.gadget_root).write_text("%s" % 0x0200)  # USB 2.0
+    Path("%s/bcdDevice" % this.gadget_root).write_text(
+        "%s" % 1, encoding="utf-8"
+    )  # Version 1.0.0
+    Path("%s/bcdUSB" % this.gadget_root).write_text(
+        "%s" % 0x0200, encoding="utf-8"
+    )  # USB 2.0
     Path("%s/bDeviceClass" % this.gadget_root).write_text(
-        "%s" % 0x00
+        "%s" % 0x00, encoding="utf-8"
     )  # multipurpose i guess?
-    Path("%s/bDeviceProtocol" % this.gadget_root).write_text("%s" % 0x00)
-    Path("%s/bDeviceSubClass" % this.gadget_root).write_text("%s" % 0x00)
-    Path("%s/bMaxPacketSize0" % this.gadget_root).write_text("%s" % 0x08)
+    Path("%s/bDeviceProtocol" % this.gadget_root).write_text(
+        "%s" % 0x00, encoding="utf-8"
+    )
+    Path("%s/bDeviceSubClass" % this.gadget_root).write_text(
+        "%s" % 0x00, encoding="utf-8"
+    )
+    Path("%s/bMaxPacketSize0" % this.gadget_root).write_text(
+        "%s" % 0x08, encoding="utf-8"
+    )
     Path("%s/idProduct" % this.gadget_root).write_text(
-        "%s" % 0x0104
+        "%s" % 0x0104, encoding="utf-8"
     )  # Multifunction Composite Gadget
-    Path("%s/idVendor" % this.gadget_root).write_text("%s" % 0x1D6B)  # Linux Foundation
+    Path("%s/idVendor" % this.gadget_root).write_text(
+        "%s" % 0x1D6B, encoding="utf-8"
+    )  # Linux Foundation
     # """
     # 2. Creating the configurations
     # ------------------------------
@@ -620,15 +641,15 @@ def enable(requested_devices: Sequence[Device], boot_device: int = 0) -> None:
     #     $ echo 120 > configs/c.1/MaxPower
     #     """
 
-    for i, device in enumerate(requested_devices):
-        config_root = "%s/configs/device.%s" % (this.gadget_root, i + 1)
+    for device in requested_devices:
+        config_root = "%s/configs/device.1" % this.gadget_root
         Path("%s/" % config_root).mkdir(parents=True, exist_ok=True)
         Path("%s/strings/0x409" % config_root).mkdir(parents=True, exist_ok=True)
         Path("%s/strings/0x409/configuration" % config_root).write_text(
-            "my configuration"
+            "my configuration", encoding="utf-8"
         )
-        Path("%s/MaxPower" % config_root).write_text("150")
-        Path("%s/bmAttributes" % config_root).write_text("%s" % 0x080)
+        Path("%s/MaxPower" % config_root).write_text("150", encoding="utf-8")
+        Path("%s/bmAttributes" % config_root).write_text("%s" % 0x080, encoding="utf-8")
         this.devices.append(device)
         # """
         # 3. Creating the functions
@@ -658,11 +679,13 @@ def enable(requested_devices: Sequence[Device], boot_device: int = 0) -> None:
                 Path("%s/" % function_root).mkdir(parents=True)
             except FileExistsError:
                 continue
-            Path("%s/protocol" % function_root).write_text("%s" % report_id)
+            Path("%s/protocol" % function_root).write_text(
+                "%s" % report_id, encoding="utf-8"
+            )
             Path("%s/report_length" % function_root).write_text(
-                "%s" % device.in_report_lengths[report_index]
+                "%s" % device.in_report_lengths[report_index], encoding="utf-8"
             )
-            Path("%s/subclass" % function_root).write_text("%s" % 1)
+            Path("%s/subclass" % function_root).write_text("%s" % 1, encoding="utf-8")
             Path("%s/report_desc" % function_root).write_bytes(device.descriptor)
             # """
             # 4. Associating the functions with their configurations
@@ -679,7 +702,12 @@ def enable(requested_devices: Sequence[Device], boot_device: int = 0) -> None:
             # e.g.::
             #
             #     $ ln -s functions/ncm.usb0 configs/c.1  """
-            Path("%s/hid.usb%s" % (config_root, report_id)).symlink_to(function_root)
+            try:
+                Path("%s/hid.usb%s" % (config_root, report_id)).symlink_to(
+                    function_root
+                )
+            except FileNotFoundError:
+                pass
     # """ 5. Enabling the gadget
     # ----------------------
     # Such a gadget must be finally enabled so that the USB host can enumerate it.
@@ -694,4 +722,4 @@ def enable(requested_devices: Sequence[Device], boot_device: int = 0) -> None:
     #
     # $ echo s3c-hsotg > UDC  """
     udc = next(Path("/sys/class/udc/").glob("*"))
-    Path("%s/UDC" % this.gadget_root).write_text("%s" % udc.name)
+    Path("%s/UDC" % this.gadget_root).write_text("%s" % udc.name, encoding="utf-8")