]> Repositories - Adafruit_Blinka-hackapet.git/blob - src/usb_hid.py
Merge pull request #722 from Ryzer58/main
[Adafruit_Blinka-hackapet.git] / src / usb_hid.py
1 # SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
2 #
3 # SPDX-License-Identifier: MIT
4 """
5 `usb_hid` - support for usb hid devices via usb_gadget driver
6 ===========================================================
7 See `CircuitPython:usb_hid` in CircuitPython for more details.
8 For now using report ids in the descriptor
9
10 # regarding usb_gadget see https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt
11 * Author(s): Björn Bösel
12 """
13
14 from typing import Sequence
15 from pathlib import Path
16 import os
17 import atexit
18 import sys
19
20 for module in ["dwc2", "libcomposite"]:
21     if Path("/proc/modules").read_text(encoding="utf-8").find(module) == -1:
22         raise Exception(  # pylint: disable=broad-exception-raised
23             "%s module not present in your kernel. did you insmod it?" % module
24         )
25 this = sys.modules[__name__]
26
27 this.gadget_root = "/sys/kernel/config/usb_gadget/adafruit-blinka"
28 this.boot_device = 0
29 this.devices = []
30
31
32 class Device:
33     """
34     HID Device specification: see
35     https://github.com/adafruit/circuitpython/blob/main/shared-bindings/usb_hid/Device.c
36     """
37
38     def __init__(
39         self,
40         *,
41         descriptor: bytes,
42         usage_page: int,
43         usage: int,
44         report_ids: Sequence[int],
45         in_report_lengths: Sequence[int],
46         out_report_lengths: Sequence[int],
47     ) -> None:
48         self.out_report_lengths = out_report_lengths
49         self.in_report_lengths = in_report_lengths
50         self.report_ids = report_ids
51         self.usage = usage
52         self.usage_page = usage_page
53         self.descriptor = descriptor
54         self._last_received_report = None
55
56     def send_report(self, report: bytearray, report_id: int = None):
57         """Send an HID report. If the device descriptor specifies zero or one report id's,
58         you can supply `None` (the default) as the value of ``report_id``.
59         Otherwise you must specify which report id to use when sending the report.
60         """
61         report_id = report_id or self.report_ids[0]
62         device_path = self.get_device_path(report_id)
63         with open(device_path, "rb+") as fd:
64             if report_id > 0:
65                 report = bytearray(report_id.to_bytes(1, "big")) + report
66             fd.write(report)
67
68     @property
69     def last_received_report(
70         self,
71     ) -> bytes:
72         """The HID OUT report as a `bytes` (read-only). `None` if nothing received.
73         Same as `get_last_received_report()` with no argument.
74
75         Deprecated: will be removed in CircutPython 8.0.0. Use `get_last_received_report()` instead.
76         """
77         return self.get_last_received_report()
78
79     def get_last_received_report(self, report_id=None) -> bytes:
80         """Get the last received HID OUT or feature report for the given report ID.
81         The report ID may be omitted if there is no report ID, or only one report ID.
82         Return `None` if nothing received.
83         """
84         device_path = self.get_device_path(report_id or self.report_ids[0])
85         with open(device_path, "rb+") as fd:
86             os.set_blocking(fd.fileno(), False)
87             report = fd.read(self.out_report_lengths[0])
88             if report is not None:
89                 self._last_received_report = report
90         return self._last_received_report
91
92     def get_device_path(self, report_id):
93         """
94         translates the /dev/hidg device from the report id
95         """
96         device = (
97             Path(
98                 "%s/functions/hid.usb%s/dev"
99                 % (this.gadget_root, report_id or self.report_ids[0])
100             )
101             .read_text(encoding="utf-8")
102             .strip()
103             .split(":")[1]
104         )
105         device_path = "/dev/hidg%s" % device
106         return device_path
107
108     KEYBOARD = None
109     MOUSE = None
110     CONSUMER_CONTROL = None
111
112
113 Device.KEYBOARD = Device(
114     descriptor=bytes(
115         (
116             0x05,
117             0x01,  # usage page (generic desktop ctrls)
118             0x09,
119             0x06,  # usage (keyboard)
120             0xA1,
121             0x01,  # collection (application)
122             0x85,
123             0x01,  # Report ID (1)
124             0x05,
125             0x07,  # usage page (kbrd/keypad)
126             0x19,
127             0xE0,  # usage minimum (0xe0)
128             0x29,
129             0xE7,  # usage maximum (0xe7)
130             0x15,
131             0x00,  # logical minimum (0)
132             0x25,
133             0x01,  # logical maximum (1)
134             0x75,
135             0x01,  # report size (1)
136             0x95,
137             0x08,  # report count (8)
138             0x81,
139             0x02,  # input (data,var,abs,no wrap,linear,preferred state,no null position)
140             0x95,
141             0x01,  # report count (1)
142             0x75,
143             0x08,  # report size (8)
144             0x81,
145             0x01,  # input (const,array,abs,no wrap,linear,preferred state,no null position)
146             0x95,
147             0x03,  # report count (3)
148             0x75,
149             0x01,  # report size (1)
150             0x05,
151             0x08,  # usage page (leds)
152             0x19,
153             0x01,  # usage minimum (num lock)
154             0x29,
155             0x05,  # usage maximum (kana)
156             0x91,
157             0x02,  # output
158             # (data,var,abs,no wrap,linear,preferred state,no null position,non-volatile)
159             0x95,
160             0x01,  # report count (1)
161             0x75,
162             0x05,  # report size (5)
163             0x91,
164             0x01,  # output
165             # (const,array,abs,no wrap,linear,preferred state,no null position,non-volatile)
166             0x95,
167             0x06,  # report count (6)
168             0x75,
169             0x08,  # report size (8)
170             0x15,
171             0x00,  # logical minimum (0)
172             0x26,
173             0xFF,
174             0x00,  # logical maximum (255)
175             0x05,
176             0x07,  # usage page (kbrd/keypad)
177             0x19,
178             0x00,  # usage minimum (0x00)
179             0x2A,
180             0xFF,
181             0x00,  # usage maximum (0xff)
182             0x81,
183             0x00,  # input (data,array,abs,no wrap,linear,preferred state,no null position)
184             0xC0,  # end collection
185         )
186     ),
187     usage_page=0x1,
188     usage=0x6,
189     report_ids=[0x1],
190     in_report_lengths=[8],
191     out_report_lengths=[1],
192 )
193 Device.MOUSE = Device(
194     descriptor=bytes(
195         (
196             0x05,
197             0x01,  # Usage Page (Generic Desktop Ctrls)
198             0x09,
199             0x02,  # Usage (Mouse)
200             0xA1,
201             0x01,  # Collection (Application)
202             0x85,
203             0x02,  # Report ID (2)
204             0x09,
205             0x01,  # Usage (Pointer)
206             0xA1,
207             0x00,  # Collection (Physical)
208             0x05,
209             0x09,  # Usage Page (Button)
210             0x19,
211             0x01,  # Usage Minimum (0x01)
212             0x29,
213             0x05,  # Usage Maximum (0x05)
214             0x15,
215             0x00,  # Logical Minimum (0)
216             0x25,
217             0x01,  # Logical Maximum (1)
218             0x95,
219             0x05,  # Report Count (5)
220             0x75,
221             0x01,  # Report Size (1)
222             0x81,
223             0x02,  # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
224             0x95,
225             0x01,  # Report Count (1)
226             0x75,
227             0x03,  # Report Size (3)
228             0x81,
229             0x01,  # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
230             0x05,
231             0x01,  # Usage Page (Generic Desktop Ctrls)
232             0x09,
233             0x30,  # Usage (X)
234             0x09,
235             0x31,  # Usage (Y)
236             0x15,
237             0x81,  # Logical Minimum (-127)
238             0x25,
239             0x7F,  # Logical Maximum (127)
240             0x75,
241             0x08,  # Report Size (8)
242             0x95,
243             0x02,  # Report Count (2)
244             0x81,
245             0x06,  # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
246             0x09,
247             0x38,  # Usage (Wheel)
248             0x15,
249             0x81,  # Logical Minimum (-127)
250             0x25,
251             0x7F,  # Logical Maximum (127)
252             0x75,
253             0x08,  # Report Size (8)
254             0x95,
255             0x01,  # Report Count (1)
256             0x81,
257             0x06,  # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
258             0xC0,  # End Collection
259             0xC0,  # End Collection
260         )
261     ),
262     usage_page=0x1,
263     usage=0x02,
264     report_ids=[0x02],
265     in_report_lengths=[4],
266     out_report_lengths=[0],
267 )
268
269 Device.CONSUMER_CONTROL = Device(
270     descriptor=bytes(
271         (
272             0x05,
273             0x0C,  # Usage Page (Consumer)
274             0x09,
275             0x01,  # Usage (Consumer Control)
276             0xA1,
277             0x01,  # Collection (Application)
278             0x85,
279             0x03,  # Report ID (3)
280             0x75,
281             0x10,  # Report Size (16)
282             0x95,
283             0x01,  # Report Count (1)
284             0x15,
285             0x01,  # Logical Minimum (1)
286             0x26,
287             0x8C,
288             0x02,  # Logical Maximum (652)
289             0x19,
290             0x01,  # Usage Minimum (Consumer Control)
291             0x2A,
292             0x8C,
293             0x02,  # Usage Maximum (AC Send)
294             0x81,
295             0x00,  # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
296             0xC0,  # End Collection
297         )
298     ),
299     usage_page=0x0C,
300     usage=0x01,
301     report_ids=[3],
302     in_report_lengths=[2],
303     out_report_lengths=[0],
304 )
305
306 Device.BOOT_KEYBOARD = Device(
307     descriptor=bytes(
308         (
309             0x05,
310             0x01,  # usage page (generic desktop ctrls)
311             0x09,
312             0x06,  # usage (keyboard)
313             0xA1,
314             0x01,  # collection (application)
315             0x05,
316             0x07,  # usage page (kbrd/keypad)
317             0x19,
318             0xE0,  # usage minimum (0xe0)
319             0x29,
320             0xE7,  # usage maximum (0xe7)
321             0x15,
322             0x00,  # logical minimum (0)
323             0x25,
324             0x01,  # logical maximum (1)
325             0x75,
326             0x01,  # report size (1)
327             0x95,
328             0x08,  # report count (8)
329             0x81,
330             0x02,  # input (data,var,abs,no wrap,linear,preferred state,no null position)
331             0x95,
332             0x01,  # report count (1)
333             0x75,
334             0x08,  # report size (8)
335             0x81,
336             0x01,  # input (const,array,abs,no wrap,linear,preferred state,no null position)
337             0x95,
338             0x03,  # report count (3)
339             0x75,
340             0x01,  # report size (1)
341             0x05,
342             0x08,  # usage page (leds)
343             0x19,
344             0x01,  # usage minimum (num lock)
345             0x29,
346             0x05,  # usage maximum (kana)
347             0x91,
348             0x02,  # output
349             # (data,var,abs,no wrap,linear,preferred state,no null position,non-volatile)
350             0x95,
351             0x01,  # report count (1)
352             0x75,
353             0x05,  # report size (5)
354             0x91,
355             0x01,  # output
356             # (const,array,abs,no wrap,linear,preferred state,no null position,non-volatile)
357             0x95,
358             0x06,  # report count (6)
359             0x75,
360             0x08,  # report size (8)
361             0x15,
362             0x00,  # logical minimum (0)
363             0x26,
364             0xFF,
365             0x00,  # logical maximum (255)
366             0x05,
367             0x07,  # usage page (kbrd/keypad)
368             0x19,
369             0x00,  # usage minimum (0x00)
370             0x2A,
371             0xFF,
372             0x00,  # usage maximum (0xff)
373             0x81,
374             0x00,  # input (data,array,abs,no wrap,linear,preferred state,no null position)
375             0xC0,  # end collection
376         )
377     ),
378     usage_page=0x1,
379     usage=0x6,
380     report_ids=[0x0],
381     in_report_lengths=[8],
382     out_report_lengths=[1],
383 )
384 Device.BOOT_MOUSE = Device(
385     descriptor=bytes(
386         (
387             0x05,
388             0x01,  # Usage Page (Generic Desktop Ctrls)
389             0x09,
390             0x02,  # Usage (Mouse)
391             0xA1,
392             0x01,  # Collection (Application)
393             0x09,
394             0x01,  # Usage (Pointer)
395             0xA1,
396             0x00,  # Collection (Physical)
397             0x05,
398             0x09,  # Usage Page (Button)
399             0x19,
400             0x01,  # Usage Minimum (0x01)
401             0x29,
402             0x05,  # Usage Maximum (0x05)
403             0x15,
404             0x00,  # Logical Minimum (0)
405             0x25,
406             0x01,  # Logical Maximum (1)
407             0x95,
408             0x05,  # Report Count (5)
409             0x75,
410             0x01,  # Report Size (1)
411             0x81,
412             0x02,  # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
413             0x95,
414             0x01,  # Report Count (1)
415             0x75,
416             0x03,  # Report Size (3)
417             0x81,
418             0x01,  # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
419             0x05,
420             0x01,  # Usage Page (Generic Desktop Ctrls)
421             0x09,
422             0x30,  # Usage (X)
423             0x09,
424             0x31,  # Usage (Y)
425             0x15,
426             0x81,  # Logical Minimum (-127)
427             0x25,
428             0x7F,  # Logical Maximum (127)
429             0x75,
430             0x08,  # Report Size (8)
431             0x95,
432             0x02,  # Report Count (2)
433             0x81,
434             0x06,  # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
435             0x09,
436             0x38,  # Usage (Wheel)
437             0x15,
438             0x81,  # Logical Minimum (-127)
439             0x25,
440             0x7F,  # Logical Maximum (127)
441             0x75,
442             0x08,  # Report Size (8)
443             0x95,
444             0x01,  # Report Count (1)
445             0x81,
446             0x06,  # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
447             0xC0,  # End Collection
448             0xC0,  # End Collection
449         )
450     ),
451     usage_page=0x1,
452     usage=0x02,
453     report_ids=[0],
454     in_report_lengths=[4],
455     out_report_lengths=[0],
456 )
457
458
459 def disable() -> None:
460     """Do not present any USB HID devices to the host computer.
461     Can be called in ``boot.py``, before USB is connected.
462     The HID composite device is normally enabled by default,
463     but on some boards with limited endpoints, including STM32F4,
464     it is disabled by default. You must turn off another USB device such
465     as `usb_cdc` or `storage` to free up endpoints for use by `usb_hid`.
466     """
467     try:
468         Path("%s/UDC" % this.gadget_root).write_text("", encoding="utf-8")
469     except FileNotFoundError:
470         pass
471     for symlink in Path(this.gadget_root).glob("configs/**/hid.usb*"):
472         symlink.unlink()
473
474     for strings_file in Path(this.gadget_root).rglob("configs/*/strings/*/*"):
475         if strings_file.is_dir():
476             strings_file.rmdir()
477
478     for strings_file in Path(this.gadget_root).rglob("configs/*/strings/*"):
479         if strings_file.is_dir():
480             strings_file.rmdir()
481     for config_dir in Path(this.gadget_root).rglob("configs/*"):
482         if config_dir.is_dir():
483             config_dir.rmdir()
484     for function_dir in Path(this.gadget_root).rglob("functions/*"):
485         if function_dir.is_dir():
486             function_dir.rmdir()
487     try:
488         Path(this.gadget_root).rmdir()
489     except FileNotFoundError:
490         pass
491     this.devices = []
492
493
494 atexit.register(disable)
495
496
497 def enable(requested_devices: Sequence[Device], boot_device: int = 0) -> None:
498     """Specify which USB HID devices that will be available.
499     Can be called in ``boot.py``, before USB is connected.
500
501     :param Sequence devices: `Device` objects.
502       If `devices` is empty, HID is disabled. The order of the ``Devices``
503       may matter to the host. For instance, for MacOS, put the mouse device
504       before any Gamepad or Digitizer HID device or else it will not work.
505     :param int boot_device: If non-zero, inform the host that support for a
506       a boot HID device is available.
507       If ``boot_device=1``, a boot keyboard is available.
508       If ``boot_device=2``, a boot mouse is available. No other values are allowed.
509       See below.
510
511     If you enable too many devices at once, you will run out of USB endpoints.
512     The number of available endpoints varies by microcontroller.
513     CircuitPython will go into safe mode after running ``boot.py`` to inform you if
514     not enough endpoints are available.
515
516     **Boot Devices**
517
518     Boot devices implement a fixed, predefined report descriptor, defined in
519     https://www.usb.org/sites/default/files/hid1_12.pdf, Appendix B. A USB host
520     can request to use the boot device if the USB device says it is available.
521     Usually only a BIOS or other kind of limited-functionality
522     host needs boot keyboard support.
523
524     For example, to make a boot keyboard available, you can use this code::
525
526       usb_hid.enable((Device.KEYBOARD), boot_device=1)  # 1 for a keyboard
527
528     If the host requests the boot keyboard, the report descriptor provided by `Device.KEYBOARD`
529     will be ignored, and the predefined report descriptor will be used.
530     But if the host does not request the boot keyboard,
531     the descriptor provided by `Device.KEYBOARD` will be used.
532
533     The HID boot device must usually be the first or only device presented by CircuitPython.
534     The HID device will be USB interface number 0.
535     To make sure it is the first device, disable other USB devices, including CDC and MSC
536     (CIRCUITPY).
537     If you specify a non-zero ``boot_device``, and it is not the first device, CircuitPython
538     will enter safe mode to report this error.
539     """
540     this.boot_device = boot_device
541
542     if len(requested_devices) == 0:
543         disable()
544         return
545
546     if boot_device == 1:
547         requested_devices = [Device.BOOT_KEYBOARD]
548     if boot_device == 2:
549         requested_devices = [Device.BOOT_MOUSE]
550
551     # """
552     # 1. Creating the gadgets
553     # -----------------------
554     #
555     # For each gadget to be created its corresponding directory must be created::
556     #
557     #     $ mkdir $CONFIGFS_HOME/usb_gadget/<gadget name>
558     #
559     # e.g.::
560     #
561     #     $ mkdir $CONFIGFS_HOME/usb_gadget/g1
562     #
563     #     ...
564     #     ...
565     #     ...
566     #
567     #     $ cd $CONFIGFS_HOME/usb_gadget/g1
568     #
569     # Each gadget needs to have its vendor id <VID> and product id <PID> specified::
570     #
571     #     $ echo <VID> > idVendor
572     #     $ echo <PID> > idProduct
573     #
574     # A gadget also needs its serial number, manufacturer and product strings.
575     # In order to have a place to store them, a strings subdirectory must be created
576     # for each language, e.g.::
577     #
578     #     $ mkdir strings/0x409
579     #
580     # Then the strings can be specified::
581     #
582     #     $ echo <serial number> > strings/0x409/serialnumber
583     #     $ echo <manufacturer> > strings/0x409/manufacturer
584     #     $ echo <product> > strings/0x409/product
585     # """
586     Path("%s/functions" % this.gadget_root).mkdir(parents=True, exist_ok=True)
587     Path("%s/configs" % this.gadget_root).mkdir(parents=True, exist_ok=True)
588     Path("%s/bcdDevice" % this.gadget_root).write_text(
589         "%s" % 1, encoding="utf-8"
590     )  # Version 1.0.0
591     Path("%s/bcdUSB" % this.gadget_root).write_text(
592         "%s" % 0x0200, encoding="utf-8"
593     )  # USB 2.0
594     Path("%s/bDeviceClass" % this.gadget_root).write_text(
595         "%s" % 0x00, encoding="utf-8"
596     )  # multipurpose i guess?
597     Path("%s/bDeviceProtocol" % this.gadget_root).write_text(
598         "%s" % 0x00, encoding="utf-8"
599     )
600     Path("%s/bDeviceSubClass" % this.gadget_root).write_text(
601         "%s" % 0x00, encoding="utf-8"
602     )
603     Path("%s/bMaxPacketSize0" % this.gadget_root).write_text(
604         "%s" % 0x08, encoding="utf-8"
605     )
606     Path("%s/idProduct" % this.gadget_root).write_text(
607         "%s" % 0x0104, encoding="utf-8"
608     )  # Multifunction Composite Gadget
609     Path("%s/idVendor" % this.gadget_root).write_text(
610         "%s" % 0x1D6B, encoding="utf-8"
611     )  # Linux Foundation
612     # """
613     # 2. Creating the configurations
614     # ------------------------------
615     #
616     # Each gadget will consist of a number of configurations, their corresponding
617     # directories must be created:
618     #
619     # $ mkdir configs/<name>.<number>
620     #
621     # where <name> can be any string which is legal in a filesystem and the
622     # <number> is the configuration's number, e.g.::
623     #
624     #     $ mkdir configs/c.1
625     #
626     #     ...
627     #     ...
628     #     ...
629     #
630     # Each configuration also needs its strings, so a subdirectory must be created
631     # for each language, e.g.::
632     #
633     #     $ mkdir configs/c.1/strings/0x409
634     #
635     # Then the configuration string can be specified::
636     #
637     #     $ echo <configuration> > configs/c.1/strings/0x409/configuration
638     #
639     # Some attributes can also be set for a configuration, e.g.::
640     #
641     #     $ echo 120 > configs/c.1/MaxPower
642     #     """
643
644     for device in requested_devices:
645         config_root = "%s/configs/device.1" % this.gadget_root
646         Path("%s/" % config_root).mkdir(parents=True, exist_ok=True)
647         Path("%s/strings/0x409" % config_root).mkdir(parents=True, exist_ok=True)
648         Path("%s/strings/0x409/configuration" % config_root).write_text(
649             "my configuration", encoding="utf-8"
650         )
651         Path("%s/MaxPower" % config_root).write_text("150", encoding="utf-8")
652         Path("%s/bmAttributes" % config_root).write_text("%s" % 0x080, encoding="utf-8")
653         this.devices.append(device)
654         # """
655         # 3. Creating the functions
656         # -------------------------
657         #
658         # The gadget will provide some functions, for each function its corresponding
659         # directory must be created::
660         #
661         #     $ mkdir functions/<name>.<instance name>
662         #
663         # where <name> corresponds to one of allowed function names and instance name
664         # is an arbitrary string allowed in a filesystem, e.g.::
665         #
666         #   $ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()
667         #
668         #   ...
669         #   ...
670         #   ...
671         #
672         # Each function provides its specific set of attributes, with either read-only
673         # or read-write access. Where applicable they need to be written to as
674         # appropriate.
675         # Please refer to Documentation/ABI/*/configfs-usb-gadget* for more information.  """
676         for report_index, report_id in enumerate(device.report_ids):
677             function_root = "%s/functions/hid.usb%s" % (this.gadget_root, report_id)
678             try:
679                 Path("%s/" % function_root).mkdir(parents=True)
680             except FileExistsError:
681                 continue
682             Path("%s/protocol" % function_root).write_text(
683                 "%s" % report_id, encoding="utf-8"
684             )
685             Path("%s/report_length" % function_root).write_text(
686                 "%s" % device.in_report_lengths[report_index], encoding="utf-8"
687             )
688             Path("%s/subclass" % function_root).write_text("%s" % 1, encoding="utf-8")
689             Path("%s/report_desc" % function_root).write_bytes(device.descriptor)
690             # """
691             # 4. Associating the functions with their configurations
692             # ------------------------------------------------------
693             #
694             # At this moment a number of gadgets is created, each of which has a number of
695             # configurations specified and a number of functions available. What remains
696             # is specifying which function is available in which configuration (the same
697             # function can be used in multiple configurations). This is achieved with
698             # creating symbolic links::
699             #
700             #     $ ln -s functions/<name>.<instance name> configs/<name>.<number>
701             #
702             # e.g.::
703             #
704             #     $ ln -s functions/ncm.usb0 configs/c.1  """
705             try:
706                 Path("%s/hid.usb%s" % (config_root, report_id)).symlink_to(
707                     function_root
708                 )
709             except FileNotFoundError:
710                 pass
711     # """ 5. Enabling the gadget
712     # ----------------------
713     # Such a gadget must be finally enabled so that the USB host can enumerate it.
714     #
715     # In order to enable the gadget it must be bound to a UDC (USB Device
716     # Controller)::
717     #
718     #     $ echo <udc name> > UDC
719     #
720     # where <udc name> is one of those found in /sys/class/udc/*
721     # e.g.::
722     #
723     # $ echo s3c-hsotg > UDC  """
724     udc = next(Path("/sys/class/udc/").glob("*"))
725     Path("%s/UDC" % this.gadget_root).write_text("%s" % udc.name, encoding="utf-8")