]> Repositories - Adafruit_Blinka-hackapet.git/blobdiff - src/usb_hid.py
module-scoped variables without global
[Adafruit_Blinka-hackapet.git] / src / usb_hid.py
index 5fcd2a1f3bf2dc8bc70ad6a8dc50ea078022cbbf..c55a28d18d2d75f41c30743563eb79df0b5b0d69 100644 (file)
+"""
+`usb_hid` - support for usb hid devices via usb_gadget driver
+===========================================================
+See `CircuitPython:usb_hid` in CircuitPython for more details.
+For now using report ids in the descriptor
+
+# regarding usb_gadget see https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt
+* Author(s): Björn Bösel
+"""
+
 from typing import Sequence
 from pathlib import Path
+import os
 import atexit
+import sys
+
+for module in ["dwc2", "libcomposite"]:
+    if Path("/proc/modules").read_text().find(module) == -1:
+        raise Exception(
+            "%s module not present in your kernel. did you insmod it?" % module
+        )
+this = sys.modules[__name__]
 
-# https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt
+this.gadget_root = "/sys/kernel/config/usb_gadget/adafruit-blinka"
+this.boot_device = 0
+this.devices = []
 
-gadget_root = '/sys/kernel/config/usb_gadget/adafruit-blinka'
 
+class Device:
+    """
+    HID Device specification: see
+    https://github.com/adafruit/circuitpython/blob/main/shared-bindings/usb_hid/Device.c
+    """
 
-class Device():
-    def __init__(self, *, descriptor: bytes, usage_page: int, usage: int, report_ids: Sequence[int],
-                 in_report_lengths: Sequence[int], out_report_lengths: Sequence[int]) -> None:
+    def __init__(
+        self,
+        *,
+        descriptor: bytes,
+        usage_page: int,
+        usage: int,
+        report_ids: Sequence[int],
+        in_report_lengths: Sequence[int],
+        out_report_lengths: Sequence[int],
+    ) -> None:
         self.out_report_lengths = out_report_lengths
         self.in_report_lengths = in_report_lengths
         self.report_ids = report_ids
         self.usage = usage
         self.usage_page = usage_page
         self.descriptor = descriptor
-        self.handle = ''
+        self._last_received_report = None
 
     def send_report(self, report: bytearray, report_id: int = None):
-        device = \
-            Path('%s/functions/hid.usb%s/dev' % (gadget_root, report_id or self.report_ids[0])) \
-                .read_text() \
-                .strip() \
-                .split(':')[1]
-        with open('/dev/hidg%s' % device, 'rb+') as fd:
+        report_id = report_id or self.report_ids[0]
+        device_path = self.get_device_path(report_id)
+        with open(device_path, "rb+") as fd:
+            if report_id > 0:
+                report = bytearray(report_id.to_bytes(1, "big")) + report
             fd.write(report)
 
+    @property
+    def last_received_report(
+        self,
+    ) -> bytes:
+        """The HID OUT report as a `bytes` (read-only). `None` if nothing received.
+        Same as `get_last_received_report()` with no argument.
+
+        Deprecated: will be removed in CircutPython 8.0.0. Use `get_last_received_report()` instead.
+        """
+        return self.get_last_received_report()
+
+    def get_last_received_report(self, report_id=None) -> bytes:
+        """Get the last received HID OUT or feature report for the given report ID.
+        The report ID may be omitted if there is no report ID, or only one report ID.
+        Return `None` if nothing received.
+        """
+        device_path = self.get_device_path(report_id or self.report_ids[0])
+        with open(device_path, "rb+") as fd:
+            os.set_blocking(fd.fileno(), False)
+            report = fd.read(self.out_report_lengths[0])
+            if report is not None:
+                self._last_received_report = report
+        return self._last_received_report
+
+    def get_device_path(self, report_id):
+        """
+        translates the /dev/hidg device from the report id
+        """
+        device = (
+            Path(
+                "%s/functions/hid.usb%s/dev"
+                % (this.gadget_root, report_id or self.report_ids[0])
+            )
+            .read_text()
+            .strip()
+            .split(":")[1]
+        )
+        device_path = "/dev/hidg%s" % device
+        return device_path
+
+    KEYBOARD = None
+    MOUSE = None
+    CONSUMER_CONTROL = None
 
-Device.KEYBOARD = Device(
-    descriptor=bytes((
 
-        0x05, 0x01,  # usage page (generic desktop ctrls)
-        0x09, 0x06,  # usage (keyboard)
-        0xa1, 0x01,  # collection (application)
-        0x05, 0x07,  # usage page (kbrd/keypad)
-        0x19, 0xe0,  # usage minimum (0xe0)
-        0x29, 0xe7,  # usage maximum (0xe7)
-        0x15, 0x00,  # logical minimum (0)
-        0x25, 0x01,  # logical maximum (1)
-        0x75, 0x01,  # report size (1)
-        0x95, 0x08,  # report count (8)
-        0x81, 0x02,  # input (data,var,abs,no wrap,linear,preferred state,no null position)
-        0x95, 0x01,  # report count (1)
-        0x75, 0x08,  # report size (8)
-        0x81, 0x01,  # input (const,array,abs,no wrap,linear,preferred state,no null position)
-        0x95, 0x03,  # report count (3)
-        0x75, 0x01,  # report size (1)
-        0x05, 0x08,  # usage page (leds)
-        0x19, 0x01,  # usage minimum (num lock)
-        0x29, 0x05,  # usage maximum (kana)
-        0x91, 0x02,  # output (data,var,abs,no wrap,linear,preferred state,no null position,non-volatile)
-        0x95, 0x01,  # report count (1)
-        0x75, 0x05,  # report size (5)
-        0x91, 0x01,  # output (const,array,abs,no wrap,linear,preferred state,no null position,non-volatile)
-        0x95, 0x06,  # report count (6)
-        0x75, 0x08,  # report size (8)
-        0x15, 0x00,  # logical minimum (0)
-        0x26, 0xff, 0x00,  # logical maximum (255)
-        0x05, 0x07,  # usage page (kbrd/keypad)
-        0x19, 0x00,  # usage minimum (0x00)
-        0x2a, 0xff, 0x00,  # usage maximum (0xff)
-        0x81, 0x00,  # input (data,array,abs,no wrap,linear,preferred state,no null position)
-        0xc0,  # end collection
-    )),
+Device.KEYBOARD = Device(
+    descriptor=bytes(
+        (
+            0x05,
+            0x01,  # usage page (generic desktop ctrls)
+            0x09,
+            0x06,  # usage (keyboard)
+            0xA1,
+            0x01,  # collection (application)
+            0x85,
+            0x01,  # Report ID (1)
+            0x05,
+            0x07,  # usage page (kbrd/keypad)
+            0x19,
+            0xE0,  # usage minimum (0xe0)
+            0x29,
+            0xE7,  # usage maximum (0xe7)
+            0x15,
+            0x00,  # logical minimum (0)
+            0x25,
+            0x01,  # logical maximum (1)
+            0x75,
+            0x01,  # report size (1)
+            0x95,
+            0x08,  # report count (8)
+            0x81,
+            0x02,  # input (data,var,abs,no wrap,linear,preferred state,no null position)
+            0x95,
+            0x01,  # report count (1)
+            0x75,
+            0x08,  # report size (8)
+            0x81,
+            0x01,  # input (const,array,abs,no wrap,linear,preferred state,no null position)
+            0x95,
+            0x03,  # report count (3)
+            0x75,
+            0x01,  # report size (1)
+            0x05,
+            0x08,  # usage page (leds)
+            0x19,
+            0x01,  # usage minimum (num lock)
+            0x29,
+            0x05,  # usage maximum (kana)
+            0x91,
+            0x02,  # output
+            # (data,var,abs,no wrap,linear,preferred state,no null position,non-volatile)
+            0x95,
+            0x01,  # report count (1)
+            0x75,
+            0x05,  # report size (5)
+            0x91,
+            0x01,  # output
+            # (const,array,abs,no wrap,linear,preferred state,no null position,non-volatile)
+            0x95,
+            0x06,  # report count (6)
+            0x75,
+            0x08,  # report size (8)
+            0x15,
+            0x00,  # logical minimum (0)
+            0x26,
+            0xFF,
+            0x00,  # logical maximum (255)
+            0x05,
+            0x07,  # usage page (kbrd/keypad)
+            0x19,
+            0x00,  # usage minimum (0x00)
+            0x2A,
+            0xFF,
+            0x00,  # usage maximum (0xff)
+            0x81,
+            0x00,  # input (data,array,abs,no wrap,linear,preferred state,no null position)
+            0xC0,  # end collection
+        )
+    ),
     usage_page=0x1,
     usage=0x6,
     report_ids=[0x1],
     in_report_lengths=[8],
-    out_report_lengths=[1]
+    out_report_lengths=[1],
 )
 Device.MOUSE = Device(
-    descriptor=bytes((
-
-        0x05, 0x01,  # Usage Page (Generic Desktop Ctrls)
-        0x09, 0x02,  # Usage (Mouse)
-        0xA1, 0x01,  # Collection (Application)
-        0x09, 0x01,  # Usage (Pointer)
-        0xA1, 0x00,  # Collection (Physical)
-        0x05, 0x09,  # Usage Page (Button)
-        0x19, 0x01,  # Usage Minimum (0x01)
-        0x29, 0x05,  # Usage Maximum (0x05)
-        0x15, 0x00,  # Logical Minimum (0)
-        0x25, 0x01,  # Logical Maximum (1)
-        0x95, 0x05,  # Report Count (5)
-        0x75, 0x01,  # Report Size (1)
-        0x81, 0x02,  # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
-        0x95, 0x01,  # Report Count (1)
-        0x75, 0x03,  # Report Size (3)
-        0x81, 0x01,  # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
-        0x05, 0x01,  # Usage Page (Generic Desktop Ctrls)
-        0x09, 0x30,  # Usage (X)
-        0x09, 0x31,  # Usage (Y)
-        0x15, 0x81,  # Logical Minimum (-127)
-        0x25, 0x7F,  # Logical Maximum (127)
-        0x75, 0x08,  # Report Size (8)
-        0x95, 0x02,  # Report Count (2)
-        0x81, 0x06,  # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
-        0x09, 0x38,  # Usage (Wheel)
-        0x15, 0x81,  # Logical Minimum (-127)
-        0x25, 0x7F,  # Logical Maximum (127)
-        0x75, 0x08,  # Report Size (8)
-        0x95, 0x01,  # Report Count (1)
-        0x81, 0x06,  # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
-        0xC0,  # End Collection
-        0xC0,  # End Collection
-    )),
-    # Omitted for brevity.
+    descriptor=bytes(
+        (
+            0x05,
+            0x01,  # Usage Page (Generic Desktop Ctrls)
+            0x09,
+            0x02,  # Usage (Mouse)
+            0xA1,
+            0x01,  # Collection (Application)
+            0x85,
+            0x02,  # Report ID (2)
+            0x09,
+            0x01,  # Usage (Pointer)
+            0xA1,
+            0x00,  # Collection (Physical)
+            0x05,
+            0x09,  # Usage Page (Button)
+            0x19,
+            0x01,  # Usage Minimum (0x01)
+            0x29,
+            0x05,  # Usage Maximum (0x05)
+            0x15,
+            0x00,  # Logical Minimum (0)
+            0x25,
+            0x01,  # Logical Maximum (1)
+            0x95,
+            0x05,  # Report Count (5)
+            0x75,
+            0x01,  # Report Size (1)
+            0x81,
+            0x02,  # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+            0x95,
+            0x01,  # Report Count (1)
+            0x75,
+            0x03,  # Report Size (3)
+            0x81,
+            0x01,  # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+            0x05,
+            0x01,  # Usage Page (Generic Desktop Ctrls)
+            0x09,
+            0x30,  # Usage (X)
+            0x09,
+            0x31,  # Usage (Y)
+            0x15,
+            0x81,  # Logical Minimum (-127)
+            0x25,
+            0x7F,  # Logical Maximum (127)
+            0x75,
+            0x08,  # Report Size (8)
+            0x95,
+            0x02,  # Report Count (2)
+            0x81,
+            0x06,  # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+            0x09,
+            0x38,  # Usage (Wheel)
+            0x15,
+            0x81,  # Logical Minimum (-127)
+            0x25,
+            0x7F,  # Logical Maximum (127)
+            0x75,
+            0x08,  # Report Size (8)
+            0x95,
+            0x01,  # Report Count (1)
+            0x81,
+            0x06,  # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+            0xC0,  # End Collection
+            0xC0,  # End Collection
+        )
+    ),
     usage_page=0x1,
     usage=0x02,
     report_ids=[2],
@@ -115,217 +260,438 @@ Device.MOUSE = Device(
 )
 
 Device.CONSUMER_CONTROL = Device(
-    descriptor=bytes((
-        0x05, 0x0C,  # Usage Page (Consumer)
-        0x09, 0x01,  # Usage (Consumer Control)
-        0xA1, 0x01,  # Collection (Application)
-        0x75, 0x10,  # Report Size (16)
-        0x95, 0x01,  # Report Count (1)
-        0x15, 0x01,  # Logical Minimum (1)
-        0x26, 0x8C, 0x02,  # Logical Maximum (652)
-        0x19, 0x01,  # Usage Minimum (Consumer Control)
-        0x2A, 0x8C, 0x02,  # Usage Maximum (AC Send)
-        0x81, 0x00,  # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
-        0xC0,  # End Collection
-    )),
-    # Omitted for brevity.
+    descriptor=bytes(
+        (
+            0x05,
+            0x0C,  # Usage Page (Consumer)
+            0x09,
+            0x01,  # Usage (Consumer Control)
+            0xA1,
+            0x01,  # Collection (Application)
+            0x85,
+            0x03,  # Report ID (3)
+            0x75,
+            0x10,  # Report Size (16)
+            0x95,
+            0x01,  # Report Count (1)
+            0x15,
+            0x01,  # Logical Minimum (1)
+            0x26,
+            0x8C,
+            0x02,  # Logical Maximum (652)
+            0x19,
+            0x01,  # Usage Minimum (Consumer Control)
+            0x2A,
+            0x8C,
+            0x02,  # Usage Maximum (AC Send)
+            0x81,
+            0x00,  # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+            0xC0,  # End Collection
+        )
+    ),
     usage_page=0x0C,
     usage=0x01,
     report_ids=[3],
     in_report_lengths=[2],
     out_report_lengths=[0],
 )
-devices = [Device.KEYBOARD, Device.MOUSE, Device.CONSUMER_CONTROL]
+
+Device.BOOT_KEYBOARD = Device(
+    descriptor=bytes(
+        (
+            0x05,
+            0x01,  # usage page (generic desktop ctrls)
+            0x09,
+            0x06,  # usage (keyboard)
+            0xA1,
+            0x01,  # collection (application)
+            0x05,
+            0x07,  # usage page (kbrd/keypad)
+            0x19,
+            0xE0,  # usage minimum (0xe0)
+            0x29,
+            0xE7,  # usage maximum (0xe7)
+            0x15,
+            0x00,  # logical minimum (0)
+            0x25,
+            0x01,  # logical maximum (1)
+            0x75,
+            0x01,  # report size (1)
+            0x95,
+            0x08,  # report count (8)
+            0x81,
+            0x02,  # input (data,var,abs,no wrap,linear,preferred state,no null position)
+            0x95,
+            0x01,  # report count (1)
+            0x75,
+            0x08,  # report size (8)
+            0x81,
+            0x01,  # input (const,array,abs,no wrap,linear,preferred state,no null position)
+            0x95,
+            0x03,  # report count (3)
+            0x75,
+            0x01,  # report size (1)
+            0x05,
+            0x08,  # usage page (leds)
+            0x19,
+            0x01,  # usage minimum (num lock)
+            0x29,
+            0x05,  # usage maximum (kana)
+            0x91,
+            0x02,  # output
+            # (data,var,abs,no wrap,linear,preferred state,no null position,non-volatile)
+            0x95,
+            0x01,  # report count (1)
+            0x75,
+            0x05,  # report size (5)
+            0x91,
+            0x01,  # output
+            # (const,array,abs,no wrap,linear,preferred state,no null position,non-volatile)
+            0x95,
+            0x06,  # report count (6)
+            0x75,
+            0x08,  # report size (8)
+            0x15,
+            0x00,  # logical minimum (0)
+            0x26,
+            0xFF,
+            0x00,  # logical maximum (255)
+            0x05,
+            0x07,  # usage page (kbrd/keypad)
+            0x19,
+            0x00,  # usage minimum (0x00)
+            0x2A,
+            0xFF,
+            0x00,  # usage maximum (0xff)
+            0x81,
+            0x00,  # input (data,array,abs,no wrap,linear,preferred state,no null position)
+            0xC0,  # end collection
+        )
+    ),
+    usage_page=0x1,
+    usage=0x6,
+    report_ids=[0x0],
+    in_report_lengths=[8],
+    out_report_lengths=[1],
+)
+Device.BOOT_MOUSE = Device(
+    descriptor=bytes(
+        (
+            0x05,
+            0x01,  # Usage Page (Generic Desktop Ctrls)
+            0x09,
+            0x02,  # Usage (Mouse)
+            0xA1,
+            0x01,  # Collection (Application)
+            0x09,
+            0x01,  # Usage (Pointer)
+            0xA1,
+            0x00,  # Collection (Physical)
+            0x05,
+            0x09,  # Usage Page (Button)
+            0x19,
+            0x01,  # Usage Minimum (0x01)
+            0x29,
+            0x05,  # Usage Maximum (0x05)
+            0x15,
+            0x00,  # Logical Minimum (0)
+            0x25,
+            0x01,  # Logical Maximum (1)
+            0x95,
+            0x05,  # Report Count (5)
+            0x75,
+            0x01,  # Report Size (1)
+            0x81,
+            0x02,  # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+            0x95,
+            0x01,  # Report Count (1)
+            0x75,
+            0x03,  # Report Size (3)
+            0x81,
+            0x01,  # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+            0x05,
+            0x01,  # Usage Page (Generic Desktop Ctrls)
+            0x09,
+            0x30,  # Usage (X)
+            0x09,
+            0x31,  # Usage (Y)
+            0x15,
+            0x81,  # Logical Minimum (-127)
+            0x25,
+            0x7F,  # Logical Maximum (127)
+            0x75,
+            0x08,  # Report Size (8)
+            0x95,
+            0x02,  # Report Count (2)
+            0x81,
+            0x06,  # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+            0x09,
+            0x38,  # Usage (Wheel)
+            0x15,
+            0x81,  # Logical Minimum (-127)
+            0x25,
+            0x7F,  # Logical Maximum (127)
+            0x75,
+            0x08,  # Report Size (8)
+            0x95,
+            0x01,  # Report Count (1)
+            0x81,
+            0x06,  # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+            0xC0,  # End Collection
+            0xC0,  # End Collection
+        )
+    ),
+    usage_page=0x1,
+    usage=0x02,
+    report_ids=[1],
+    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`.
+    # """
     try:
-        Path('%s/UDC' % gadget_root).write_text('')
+        Path("%s/UDC" % this.gadget_root).write_text("")
     except FileNotFoundError:
         pass
-    for symlink in Path(gadget_root).glob('configs/**/hid.usb*'):
+    for symlink in Path(this.gadget_root).glob("configs/**/hid.usb*"):
         symlink.unlink()
 
-    for strings_file in Path(gadget_root).rglob('configs/*/strings/*/*'):
+    for strings_file in Path(this.gadget_root).rglob("configs/*/strings/*/*"):
         if strings_file.is_dir():
             strings_file.rmdir()
 
-    for strings_file in Path(gadget_root).rglob('configs/*/strings/*'):
+    for strings_file in Path(this.gadget_root).rglob("configs/*/strings/*"):
         if strings_file.is_dir():
             strings_file.rmdir()
-    for config_dir in Path(gadget_root).rglob('configs/*'):
+    for config_dir in Path(this.gadget_root).rglob("configs/*"):
         if config_dir.is_dir():
             config_dir.rmdir()
-    for function_dir in Path(gadget_root).rglob('functions/*'):
+    for function_dir in Path(this.gadget_root).rglob("functions/*"):
         if function_dir.is_dir():
             function_dir.rmdir()
     try:
-        Path(gadget_root).rmdir()
-    except:
+        Path(this.gadget_root).rmdir()
+    except FileNotFoundError:
         pass
-
-
-# atexit.register(disable)
-
-
-def enable(devices: Sequence[Device], boot_device: int = 0) -> None:
-    if len(devices) == 0:
+    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.
+# """
+    this.boot_device = boot_device
+
+    if len(requested_devices) == 0:
         disable()
         return
-    """
-    1. Creating the gadgets
-    -----------------------
-
-    For each gadget to be created its corresponding directory must be created::
-
-        $ mkdir $CONFIGFS_HOME/usb_gadget/<gadget name>
-
-    e.g.::
-
-        $ mkdir $CONFIGFS_HOME/usb_gadget/g1
-
-        ...
-        ...
-        ...
-
-        $ cd $CONFIGFS_HOME/usb_gadget/g1
-
-    Each gadget needs to have its vendor id <VID> and product id <PID> specified::
-
-        $ echo <VID> > idVendor
-        $ echo <PID> > idProduct
-
-    A gadget also needs its serial number, manufacturer and product strings.
-    In order to have a place to store them, a strings subdirectory must be created
-    for each language, e.g.::
-
-        $ mkdir strings/0x409
 
-    Then the strings can be specified::
-
-        $ echo <serial number> > strings/0x409/serialnumber
-        $ echo <manufacturer> > strings/0x409/manufacturer
-        $ echo <product> > strings/0x409/product
-    """
-    Path('%s/functions' % gadget_root).mkdir(parents=True, exist_ok=True)
-    Path('%s/configs' % gadget_root).mkdir(parents=True, exist_ok=True)
-    Path('%s/bcdDevice' % gadget_root).write_text('%s' % 1)  # Version 1.0.0
-    Path('%s/bcdUSB' % gadget_root).write_text('%s' % 0x0200)  # USB 2.0
-    Path('%s/bDeviceClass' % gadget_root).write_text('%s' % 0x00)  # multipurpose i guess?
-    Path('%s/bDeviceProtocol' % gadget_root).write_text('%s' % 0x00)
-    Path('%s/bDeviceSubClass' % gadget_root).write_text('%s' % 0x00)
-    Path('%s/bMaxPacketSize0' % gadget_root).write_text('%s' % 0x08)
-    Path('%s/idProduct' % gadget_root).write_text('%s' % 0x0104)  # Multifunction Composite Gadget
-    Path('%s/idVendor' % gadget_root).write_text('%s' % 0x1d6b)  # Linux Foundation
-    """
-    2. Creating the configurations
-    ------------------------------
-
-    Each gadget will consist of a number of configurations, their corresponding
-    directories must be created:
-
-    $ mkdir configs/<name>.<number>
-
-    where <name> can be any string which is legal in a filesystem and the
-    <number> is the configuration's number, e.g.::
-
-        $ mkdir configs/c.1
-
-        ...
-        ...
-        ...
-
-    Each configuration also needs its strings, so a subdirectory must be created
-    for each language, e.g.::
-
-        $ mkdir configs/c.1/strings/0x409
-
-    Then the configuration string can be specified::
-
-        $ echo <configuration> > configs/c.1/strings/0x409/configuration
-
-    Some attributes can also be set for a configuration, e.g.::
-
-        $ echo 120 > configs/c.1/MaxPower
-        """
-
-    for i, device in enumerate(devices):
-
-        config_root = '%s/configs/device.%s' % (gadget_root, 1)
-        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')
-        Path('%s/MaxPower' % config_root).write_text('150')
-        Path('%s/bmAttributes' % config_root).write_text('%s' % 0x080)
-        """
-        3. Creating the functions
-        -------------------------
-
-        The gadget will provide some functions, for each function its corresponding
-        directory must be created::
-
-            $ mkdir functions/<name>.<instance name>
-
-        where <name> corresponds to one of allowed function names and instance name
-        is an arbitrary string allowed in a filesystem, e.g.::
-
-          $ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()
-
-          ...
-          ...
-          ...
-
-        Each function provides its specific set of attributes, with either read-only
-        or read-write access. Where applicable they need to be written to as
-        appropriate.
-        Please refer to Documentation/ABI/*/configfs-usb-gadget* for more information.
-
-        """
-
-        # create functions
+    if boot_device == 1:
+        requested_devices = [Device.BOOT_KEYBOARD]
+    if boot_device == 2:
+        requested_devices = [Device.BOOT_MOUSE]
+
+    # """
+    # 1. Creating the gadgets
+    # -----------------------
+    #
+    # For each gadget to be created its corresponding directory must be created::
+    #
+    #     $ mkdir $CONFIGFS_HOME/usb_gadget/<gadget name>
+    #
+    # e.g.::
+    #
+    #     $ mkdir $CONFIGFS_HOME/usb_gadget/g1
+    #
+    #     ...
+    #     ...
+    #     ...
+    #
+    #     $ cd $CONFIGFS_HOME/usb_gadget/g1
+    #
+    # Each gadget needs to have its vendor id <VID> and product id <PID> specified::
+    #
+    #     $ echo <VID> > idVendor
+    #     $ echo <PID> > idProduct
+    #
+    # A gadget also needs its serial number, manufacturer and product strings.
+    # In order to have a place to store them, a strings subdirectory must be created
+    # for each language, e.g.::
+    #
+    #     $ mkdir strings/0x409
+    #
+    # Then the strings can be specified::
+    #
+    #     $ echo <serial number> > strings/0x409/serialnumber
+    #     $ echo <manufacturer> > strings/0x409/manufacturer
+    #     $ echo <product> > strings/0x409/product
+    # """
+    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/bDeviceClass" % this.gadget_root).write_text(
+        "%s" % 0x00
+    )  # 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/idProduct" % this.gadget_root).write_text(
+        "%s" % 0x0104
+    )  # Multifunction Composite Gadget
+    Path("%s/idVendor" % this.gadget_root).write_text("%s" % 0x1D6B)  # Linux Foundation
+    # """
+    # 2. Creating the configurations
+    # ------------------------------
+    #
+    # Each gadget will consist of a number of configurations, their corresponding
+    # directories must be created:
+    #
+    # $ mkdir configs/<name>.<number>
+    #
+    # where <name> can be any string which is legal in a filesystem and the
+    # <number> is the configuration's number, e.g.::
+    #
+    #     $ mkdir configs/c.1
+    #
+    #     ...
+    #     ...
+    #     ...
+    #
+    # Each configuration also needs its strings, so a subdirectory must be created
+    # for each language, e.g.::
+    #
+    #     $ mkdir configs/c.1/strings/0x409
+    #
+    # Then the configuration string can be specified::
+    #
+    #     $ echo <configuration> > configs/c.1/strings/0x409/configuration
+    #
+    # Some attributes can also be set for a configuration, e.g.::
+    #
+    #     $ echo 120 > configs/c.1/MaxPower
+    #     """
+
+    for i, device in enumerate(requested_devices):
+        config_root = "%s/configs/device.%s" % (this.gadget_root, i + 1)
+        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"
+        )
+        Path("%s/MaxPower" % config_root).write_text("150")
+        Path("%s/bmAttributes" % config_root).write_text("%s" % 0x080)
+        this.devices.append(device)
+        # """
+        # 3. Creating the functions
+        # -------------------------
+        #
+        # The gadget will provide some functions, for each function its corresponding
+        # directory must be created::
+        #
+        #     $ mkdir functions/<name>.<instance name>
+        #
+        # where <name> corresponds to one of allowed function names and instance name
+        # is an arbitrary string allowed in a filesystem, e.g.::
+        #
+        #   $ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()
+        #
+        #   ...
+        #   ...
+        #   ...
+        #
+        # Each function provides its specific set of attributes, with either read-only
+        # or read-write access. Where applicable they need to be written to as
+        # appropriate.
+        # Please refer to Documentation/ABI/*/configfs-usb-gadget* for more information.  """
         for report_index, report_id in enumerate(device.report_ids):
-            function_root = '%s/functions/hid.usb%s' % (gadget_root, report_id)
+            function_root = "%s/functions/hid.usb%s" % (this.gadget_root, report_id)
             try:
-                Path('%s/' % function_root).mkdir(parents=True)
+                Path("%s/" % function_root).mkdir(parents=True)
             except FileExistsError:
                 continue
-            Path('%s/protocol' % function_root).write_text('%s' % report_id)
-            Path('%s/report_length' % function_root).write_text('%s' % device.in_report_lengths[report_index])
-            Path('%s/subclass' % function_root).write_text('%s' % 1)
-            Path('%s/report_desc' % function_root).write_bytes(device.descriptor)
-            """
-            4. Associating the functions with their configurations
-            ------------------------------------------------------
-
-            At this moment a number of gadgets is created, each of which has a number of
-            configurations specified and a number of functions available. What remains
-            is specifying which function is available in which configuration (the same
-            function can be used in multiple configurations). This is achieved with
-            creating symbolic links::
-
-                $ ln -s functions/<name>.<instance name> configs/<name>.<number>
-
-            e.g.::
-
-                $ ln -s functions/ncm.usb0 configs/c.1
-            """
-
-            Path('%s/hid.usb%s' % (config_root, report_id)).symlink_to(function_root)
-    """
-    5. Enabling the gadget
-    ----------------------
-    Such a gadget must be finally enabled so that the USB host can enumerate it.
-
-    In order to enable the gadget it must be bound to a UDC (USB Device
-    Controller)::
-
-        $ echo <udc name> > UDC
-
-    where <udc name> is one of those found in /sys/class/udc/*
-    e.g.::
-
-    $ echo s3c-hsotg > UDC
-
-    """
-    udc = next(Path('/sys/class/udc/').glob('*'))
-    Path('%s/UDC' % gadget_root).write_text('%s' % udc.name)
+            Path("%s/protocol" % function_root).write_text("%s" % report_id)
+            Path("%s/report_length" % function_root).write_text(
+                "%s" % device.in_report_lengths[report_index]
+            )
+            Path("%s/subclass" % function_root).write_text("%s" % 1)
+            Path("%s/report_desc" % function_root).write_bytes(device.descriptor)
+            # """
+            # 4. Associating the functions with their configurations
+            # ------------------------------------------------------
+            #
+            # At this moment a number of gadgets is created, each of which has a number of
+            # configurations specified and a number of functions available. What remains
+            # is specifying which function is available in which configuration (the same
+            # function can be used in multiple configurations). This is achieved with
+            # creating symbolic links::
+            #
+            #     $ ln -s functions/<name>.<instance name> configs/<name>.<number>
+            #
+            # e.g.::
+            #
+            #     $ ln -s functions/ncm.usb0 configs/c.1  """
+            Path("%s/hid.usb%s" % (config_root, report_id)).symlink_to(function_root)
+    # """ 5. Enabling the gadget
+    # ----------------------
+    # Such a gadget must be finally enabled so that the USB host can enumerate it.
+    #
+    # In order to enable the gadget it must be bound to a UDC (USB Device
+    # Controller)::
+    #
+    #     $ echo <udc name> > UDC
+    #
+    # where <udc name> is one of those found in /sys/class/udc/*
+    # e.g.::
+    #
+    # $ echo s3c-hsotg > UDC  """
+    udc = next(Path("/sys/class/udc/").glob("*"))
+    Path("%s/UDC" % this.gadget_root).write_text("%s" % udc.name)