1 from typing import Sequence
2 from pathlib import Path
6 # https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt
8 gadget_root = "/sys/kernel/config/usb_gadget/adafruit-blinka"
18 report_ids: Sequence[int],
19 in_report_lengths: Sequence[int],
20 out_report_lengths: Sequence[int],
22 self.out_report_lengths = out_report_lengths
23 self.in_report_lengths = in_report_lengths
24 self.report_ids = report_ids
26 self.usage_page = usage_page
27 self.descriptor = descriptor
28 self._last_received_report = None
30 def send_report(self, report: bytearray, report_id: int = None):
31 device_path = self.gets_device_path(report_id)
32 with open(device_path, "rb+") as fd:
36 def last_received_report(
39 device_path = self.gets_device_path(self.report_ids[0])
40 with open(device_path, "rb+") as fd:
41 os.set_blocking(fd.fileno(), False)
42 report = fd.read(self.out_report_lengths[0])
43 if report is not None:
44 self._last_received_report = report
45 return self._last_received_report
47 def gets_device_path(self, report_id):
50 "%s/functions/hid.usb%s/dev"
51 % (gadget_root, report_id or self.report_ids[0])
57 device_path = "/dev/hidg%s" % device
61 Device.KEYBOARD = Device(
65 0x01, # usage page (generic desktop ctrls)
67 0x06, # usage (keyboard)
69 0x01, # collection (application)
71 0x07, # usage page (kbrd/keypad)
73 0xE0, # usage minimum (0xe0)
75 0xE7, # usage maximum (0xe7)
77 0x00, # logical minimum (0)
79 0x01, # logical maximum (1)
81 0x01, # report size (1)
83 0x08, # report count (8)
85 0x02, # input (data,var,abs,no wrap,linear,preferred state,no null position)
87 0x01, # report count (1)
89 0x08, # report size (8)
91 0x01, # input (const,array,abs,no wrap,linear,preferred state,no null position)
93 0x03, # report count (3)
95 0x01, # report size (1)
97 0x08, # usage page (leds)
99 0x01, # usage minimum (num lock)
101 0x05, # usage maximum (kana)
103 0x02, # output (data,var,abs,no wrap,linear,preferred state,no null position,non-volatile)
105 0x01, # report count (1)
107 0x05, # report size (5)
109 0x01, # output (const,array,abs,no wrap,linear,preferred state,no null position,non-volatile)
111 0x06, # report count (6)
113 0x08, # report size (8)
115 0x00, # logical minimum (0)
118 0x00, # logical maximum (255)
120 0x07, # usage page (kbrd/keypad)
122 0x00, # usage minimum (0x00)
125 0x00, # usage maximum (0xff)
127 0x00, # input (data,array,abs,no wrap,linear,preferred state,no null position)
128 0xC0, # end collection
134 in_report_lengths=[8],
135 out_report_lengths=[1],
137 Device.MOUSE = Device(
141 0x01, # Usage Page (Generic Desktop Ctrls)
143 0x02, # Usage (Mouse)
145 0x01, # Collection (Application)
147 0x01, # Usage (Pointer)
149 0x00, # Collection (Physical)
151 0x09, # Usage Page (Button)
153 0x01, # Usage Minimum (0x01)
155 0x05, # Usage Maximum (0x05)
157 0x00, # Logical Minimum (0)
159 0x01, # Logical Maximum (1)
161 0x05, # Report Count (5)
163 0x01, # Report Size (1)
165 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
167 0x01, # Report Count (1)
169 0x03, # Report Size (3)
171 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
173 0x01, # Usage Page (Generic Desktop Ctrls)
179 0x81, # Logical Minimum (-127)
181 0x7F, # Logical Maximum (127)
183 0x08, # Report Size (8)
185 0x02, # Report Count (2)
187 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
189 0x38, # Usage (Wheel)
191 0x81, # Logical Minimum (-127)
193 0x7F, # Logical Maximum (127)
195 0x08, # Report Size (8)
197 0x01, # Report Count (1)
199 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
200 0xC0, # End Collection
201 0xC0, # End Collection
204 # Omitted for brevity.
208 in_report_lengths=[4],
209 out_report_lengths=[0],
212 Device.CONSUMER_CONTROL = Device(
216 0x0C, # Usage Page (Consumer)
218 0x01, # Usage (Consumer Control)
220 0x01, # Collection (Application)
222 0x10, # Report Size (16)
224 0x01, # Report Count (1)
226 0x01, # Logical Minimum (1)
229 0x02, # Logical Maximum (652)
231 0x01, # Usage Minimum (Consumer Control)
234 0x02, # Usage Maximum (AC Send)
236 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
237 0xC0, # End Collection
240 # Omitted for brevity.
244 in_report_lengths=[2],
245 out_report_lengths=[0],
247 devices = [Device.KEYBOARD, Device.MOUSE, Device.CONSUMER_CONTROL]
250 def disable() -> None:
252 Path("%s/UDC" % gadget_root).write_text("")
253 except FileNotFoundError:
255 for symlink in Path(gadget_root).glob("configs/**/hid.usb*"):
258 for strings_file in Path(gadget_root).rglob("configs/*/strings/*/*"):
259 if strings_file.is_dir():
262 for strings_file in Path(gadget_root).rglob("configs/*/strings/*"):
263 if strings_file.is_dir():
265 for config_dir in Path(gadget_root).rglob("configs/*"):
266 if config_dir.is_dir():
268 for function_dir in Path(gadget_root).rglob("functions/*"):
269 if function_dir.is_dir():
272 Path(gadget_root).rmdir()
277 # atexit.register(disable)
280 def enable(devices: Sequence[Device], boot_device: int = 0) -> None:
281 if len(devices) == 0:
285 1. Creating the gadgets
286 -----------------------
288 For each gadget to be created its corresponding directory must be created::
290 $ mkdir $CONFIGFS_HOME/usb_gadget/<gadget name>
294 $ mkdir $CONFIGFS_HOME/usb_gadget/g1
300 $ cd $CONFIGFS_HOME/usb_gadget/g1
302 Each gadget needs to have its vendor id <VID> and product id <PID> specified::
304 $ echo <VID> > idVendor
305 $ echo <PID> > idProduct
307 A gadget also needs its serial number, manufacturer and product strings.
308 In order to have a place to store them, a strings subdirectory must be created
309 for each language, e.g.::
311 $ mkdir strings/0x409
313 Then the strings can be specified::
315 $ echo <serial number> > strings/0x409/serialnumber
316 $ echo <manufacturer> > strings/0x409/manufacturer
317 $ echo <product> > strings/0x409/product
319 Path("%s/functions" % gadget_root).mkdir(parents=True, exist_ok=True)
320 Path("%s/configs" % gadget_root).mkdir(parents=True, exist_ok=True)
321 Path("%s/bcdDevice" % gadget_root).write_text("%s" % 1) # Version 1.0.0
322 Path("%s/bcdUSB" % gadget_root).write_text("%s" % 0x0200) # USB 2.0
323 Path("%s/bDeviceClass" % gadget_root).write_text(
325 ) # multipurpose i guess?
326 Path("%s/bDeviceProtocol" % gadget_root).write_text("%s" % 0x00)
327 Path("%s/bDeviceSubClass" % gadget_root).write_text("%s" % 0x00)
328 Path("%s/bMaxPacketSize0" % gadget_root).write_text("%s" % 0x08)
329 Path("%s/idProduct" % gadget_root).write_text(
331 ) # Multifunction Composite Gadget
332 Path("%s/idVendor" % gadget_root).write_text("%s" % 0x1D6B) # Linux Foundation
334 2. Creating the configurations
335 ------------------------------
337 Each gadget will consist of a number of configurations, their corresponding
338 directories must be created:
340 $ mkdir configs/<name>.<number>
342 where <name> can be any string which is legal in a filesystem and the
343 <number> is the configuration's number, e.g.::
351 Each configuration also needs its strings, so a subdirectory must be created
352 for each language, e.g.::
354 $ mkdir configs/c.1/strings/0x409
356 Then the configuration string can be specified::
358 $ echo <configuration> > configs/c.1/strings/0x409/configuration
360 Some attributes can also be set for a configuration, e.g.::
362 $ echo 120 > configs/c.1/MaxPower
365 for i, device in enumerate(devices):
367 config_root = "%s/configs/device.%s" % (gadget_root, 1)
368 Path("%s/" % config_root).mkdir(parents=True, exist_ok=True)
369 Path("%s/strings/0x409" % config_root).mkdir(parents=True, exist_ok=True)
370 Path("%s/strings/0x409/configuration" % config_root).write_text(
373 Path("%s/MaxPower" % config_root).write_text("150")
374 Path("%s/bmAttributes" % config_root).write_text("%s" % 0x080)
376 3. Creating the functions
377 -------------------------
379 The gadget will provide some functions, for each function its corresponding
380 directory must be created::
382 $ mkdir functions/<name>.<instance name>
384 where <name> corresponds to one of allowed function names and instance name
385 is an arbitrary string allowed in a filesystem, e.g.::
387 $ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()
393 Each function provides its specific set of attributes, with either read-only
394 or read-write access. Where applicable they need to be written to as
396 Please refer to Documentation/ABI/*/configfs-usb-gadget* for more information.
401 for report_index, report_id in enumerate(device.report_ids):
402 function_root = "%s/functions/hid.usb%s" % (gadget_root, report_id)
404 Path("%s/" % function_root).mkdir(parents=True)
405 except FileExistsError:
407 Path("%s/protocol" % function_root).write_text("%s" % report_id)
408 Path("%s/report_length" % function_root).write_text(
409 "%s" % device.in_report_lengths[report_index]
411 Path("%s/subclass" % function_root).write_text("%s" % 1)
412 Path("%s/report_desc" % function_root).write_bytes(device.descriptor)
414 4. Associating the functions with their configurations
415 ------------------------------------------------------
417 At this moment a number of gadgets is created, each of which has a number of
418 configurations specified and a number of functions available. What remains
419 is specifying which function is available in which configuration (the same
420 function can be used in multiple configurations). This is achieved with
421 creating symbolic links::
423 $ ln -s functions/<name>.<instance name> configs/<name>.<number>
427 $ ln -s functions/ncm.usb0 configs/c.1
430 Path("%s/hid.usb%s" % (config_root, report_id)).symlink_to(function_root)
432 5. Enabling the gadget
433 ----------------------
434 Such a gadget must be finally enabled so that the USB host can enumerate it.
436 In order to enable the gadget it must be bound to a UDC (USB Device
439 $ echo <udc name> > UDC
441 where <udc name> is one of those found in /sys/class/udc/*
444 $ echo s3c-hsotg > UDC
447 udc = next(Path("/sys/class/udc/").glob("*"))
448 Path("%s/UDC" % gadget_root).write_text("%s" % udc.name)