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'
12 def __init__(self, *, descriptor: bytes, usage_page: int, usage: int, report_ids: Sequence[int],
13 in_report_lengths: Sequence[int], out_report_lengths: Sequence[int]) -> None:
14 self.out_report_lengths = out_report_lengths
15 self.in_report_lengths = in_report_lengths
16 self.report_ids = report_ids
18 self.usage_page = usage_page
19 self.descriptor = descriptor
20 self._last_received_report = None
22 def send_report(self, report: bytearray, report_id: int = None):
23 device_path = self.gets_device_path(report_id)
24 with open(device_path, 'rb+') as fd:
28 def last_received_report(self, ):
29 device_path = self.gets_device_path(self.report_ids[0])
30 with open(device_path, 'rb+') as fd:
31 os.set_blocking(fd.fileno(), False)
32 report = fd.read(self.out_report_lengths[0])
33 if report is not None:
34 self._last_received_report = report
35 return self._last_received_report
37 def gets_device_path(self, report_id):
39 Path('%s/functions/hid.usb%s/dev' % (gadget_root, report_id or self.report_ids[0])) \
43 device_path = '/dev/hidg%s' % device
47 Device.KEYBOARD = Device(
50 0x05, 0x01, # usage page (generic desktop ctrls)
51 0x09, 0x06, # usage (keyboard)
52 0xa1, 0x01, # collection (application)
53 0x05, 0x07, # usage page (kbrd/keypad)
54 0x19, 0xe0, # usage minimum (0xe0)
55 0x29, 0xe7, # usage maximum (0xe7)
56 0x15, 0x00, # logical minimum (0)
57 0x25, 0x01, # logical maximum (1)
58 0x75, 0x01, # report size (1)
59 0x95, 0x08, # report count (8)
60 0x81, 0x02, # input (data,var,abs,no wrap,linear,preferred state,no null position)
61 0x95, 0x01, # report count (1)
62 0x75, 0x08, # report size (8)
63 0x81, 0x01, # input (const,array,abs,no wrap,linear,preferred state,no null position)
64 0x95, 0x03, # report count (3)
65 0x75, 0x01, # report size (1)
66 0x05, 0x08, # usage page (leds)
67 0x19, 0x01, # usage minimum (num lock)
68 0x29, 0x05, # usage maximum (kana)
69 0x91, 0x02, # output (data,var,abs,no wrap,linear,preferred state,no null position,non-volatile)
70 0x95, 0x01, # report count (1)
71 0x75, 0x05, # report size (5)
72 0x91, 0x01, # output (const,array,abs,no wrap,linear,preferred state,no null position,non-volatile)
73 0x95, 0x06, # report count (6)
74 0x75, 0x08, # report size (8)
75 0x15, 0x00, # logical minimum (0)
76 0x26, 0xff, 0x00, # logical maximum (255)
77 0x05, 0x07, # usage page (kbrd/keypad)
78 0x19, 0x00, # usage minimum (0x00)
79 0x2a, 0xff, 0x00, # usage maximum (0xff)
80 0x81, 0x00, # input (data,array,abs,no wrap,linear,preferred state,no null position)
81 0xc0, # end collection
86 in_report_lengths=[8],
87 out_report_lengths=[1]
89 Device.MOUSE = Device(
92 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
93 0x09, 0x02, # Usage (Mouse)
94 0xA1, 0x01, # Collection (Application)
95 0x09, 0x01, # Usage (Pointer)
96 0xA1, 0x00, # Collection (Physical)
97 0x05, 0x09, # Usage Page (Button)
98 0x19, 0x01, # Usage Minimum (0x01)
99 0x29, 0x05, # Usage Maximum (0x05)
100 0x15, 0x00, # Logical Minimum (0)
101 0x25, 0x01, # Logical Maximum (1)
102 0x95, 0x05, # Report Count (5)
103 0x75, 0x01, # Report Size (1)
104 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
105 0x95, 0x01, # Report Count (1)
106 0x75, 0x03, # Report Size (3)
107 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
108 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
109 0x09, 0x30, # Usage (X)
110 0x09, 0x31, # Usage (Y)
111 0x15, 0x81, # Logical Minimum (-127)
112 0x25, 0x7F, # Logical Maximum (127)
113 0x75, 0x08, # Report Size (8)
114 0x95, 0x02, # Report Count (2)
115 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
116 0x09, 0x38, # Usage (Wheel)
117 0x15, 0x81, # Logical Minimum (-127)
118 0x25, 0x7F, # Logical Maximum (127)
119 0x75, 0x08, # Report Size (8)
120 0x95, 0x01, # Report Count (1)
121 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
122 0xC0, # End Collection
123 0xC0, # End Collection
125 # Omitted for brevity.
129 in_report_lengths=[4],
130 out_report_lengths=[0],
133 Device.CONSUMER_CONTROL = Device(
135 0x05, 0x0C, # Usage Page (Consumer)
136 0x09, 0x01, # Usage (Consumer Control)
137 0xA1, 0x01, # Collection (Application)
138 0x75, 0x10, # Report Size (16)
139 0x95, 0x01, # Report Count (1)
140 0x15, 0x01, # Logical Minimum (1)
141 0x26, 0x8C, 0x02, # Logical Maximum (652)
142 0x19, 0x01, # Usage Minimum (Consumer Control)
143 0x2A, 0x8C, 0x02, # Usage Maximum (AC Send)
144 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
145 0xC0, # End Collection
147 # Omitted for brevity.
151 in_report_lengths=[2],
152 out_report_lengths=[0],
154 devices = [Device.KEYBOARD, Device.MOUSE, Device.CONSUMER_CONTROL]
157 def disable() -> None:
159 Path('%s/UDC' % gadget_root).write_text('')
160 except FileNotFoundError:
162 for symlink in Path(gadget_root).glob('configs/**/hid.usb*'):
165 for strings_file in Path(gadget_root).rglob('configs/*/strings/*/*'):
166 if strings_file.is_dir():
169 for strings_file in Path(gadget_root).rglob('configs/*/strings/*'):
170 if strings_file.is_dir():
172 for config_dir in Path(gadget_root).rglob('configs/*'):
173 if config_dir.is_dir():
175 for function_dir in Path(gadget_root).rglob('functions/*'):
176 if function_dir.is_dir():
179 Path(gadget_root).rmdir()
184 # atexit.register(disable)
187 def enable(devices: Sequence[Device], boot_device: int = 0) -> None:
188 if len(devices) == 0:
192 1. Creating the gadgets
193 -----------------------
195 For each gadget to be created its corresponding directory must be created::
197 $ mkdir $CONFIGFS_HOME/usb_gadget/<gadget name>
201 $ mkdir $CONFIGFS_HOME/usb_gadget/g1
207 $ cd $CONFIGFS_HOME/usb_gadget/g1
209 Each gadget needs to have its vendor id <VID> and product id <PID> specified::
211 $ echo <VID> > idVendor
212 $ echo <PID> > idProduct
214 A gadget also needs its serial number, manufacturer and product strings.
215 In order to have a place to store them, a strings subdirectory must be created
216 for each language, e.g.::
218 $ mkdir strings/0x409
220 Then the strings can be specified::
222 $ echo <serial number> > strings/0x409/serialnumber
223 $ echo <manufacturer> > strings/0x409/manufacturer
224 $ echo <product> > strings/0x409/product
226 Path('%s/functions' % gadget_root).mkdir(parents=True, exist_ok=True)
227 Path('%s/configs' % gadget_root).mkdir(parents=True, exist_ok=True)
228 Path('%s/bcdDevice' % gadget_root).write_text('%s' % 1) # Version 1.0.0
229 Path('%s/bcdUSB' % gadget_root).write_text('%s' % 0x0200) # USB 2.0
230 Path('%s/bDeviceClass' % gadget_root).write_text('%s' % 0x00) # multipurpose i guess?
231 Path('%s/bDeviceProtocol' % gadget_root).write_text('%s' % 0x00)
232 Path('%s/bDeviceSubClass' % gadget_root).write_text('%s' % 0x00)
233 Path('%s/bMaxPacketSize0' % gadget_root).write_text('%s' % 0x08)
234 Path('%s/idProduct' % gadget_root).write_text('%s' % 0x0104) # Multifunction Composite Gadget
235 Path('%s/idVendor' % gadget_root).write_text('%s' % 0x1d6b) # Linux Foundation
237 2. Creating the configurations
238 ------------------------------
240 Each gadget will consist of a number of configurations, their corresponding
241 directories must be created:
243 $ mkdir configs/<name>.<number>
245 where <name> can be any string which is legal in a filesystem and the
246 <number> is the configuration's number, e.g.::
254 Each configuration also needs its strings, so a subdirectory must be created
255 for each language, e.g.::
257 $ mkdir configs/c.1/strings/0x409
259 Then the configuration string can be specified::
261 $ echo <configuration> > configs/c.1/strings/0x409/configuration
263 Some attributes can also be set for a configuration, e.g.::
265 $ echo 120 > configs/c.1/MaxPower
268 for i, device in enumerate(devices):
270 config_root = '%s/configs/device.%s' % (gadget_root, 1)
271 Path('%s/' % config_root).mkdir(parents=True, exist_ok=True)
272 Path('%s/strings/0x409' % config_root).mkdir(parents=True, exist_ok=True)
273 Path('%s/strings/0x409/configuration' % config_root).write_text('my configuration')
274 Path('%s/MaxPower' % config_root).write_text('150')
275 Path('%s/bmAttributes' % config_root).write_text('%s' % 0x080)
277 3. Creating the functions
278 -------------------------
280 The gadget will provide some functions, for each function its corresponding
281 directory must be created::
283 $ mkdir functions/<name>.<instance name>
285 where <name> corresponds to one of allowed function names and instance name
286 is an arbitrary string allowed in a filesystem, e.g.::
288 $ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()
294 Each function provides its specific set of attributes, with either read-only
295 or read-write access. Where applicable they need to be written to as
297 Please refer to Documentation/ABI/*/configfs-usb-gadget* for more information.
302 for report_index, report_id in enumerate(device.report_ids):
303 function_root = '%s/functions/hid.usb%s' % (gadget_root, report_id)
305 Path('%s/' % function_root).mkdir(parents=True)
306 except FileExistsError:
308 Path('%s/protocol' % function_root).write_text('%s' % report_id)
309 Path('%s/report_length' % function_root).write_text('%s' % device.in_report_lengths[report_index])
310 Path('%s/subclass' % function_root).write_text('%s' % 1)
311 Path('%s/report_desc' % function_root).write_bytes(device.descriptor)
313 4. Associating the functions with their configurations
314 ------------------------------------------------------
316 At this moment a number of gadgets is created, each of which has a number of
317 configurations specified and a number of functions available. What remains
318 is specifying which function is available in which configuration (the same
319 function can be used in multiple configurations). This is achieved with
320 creating symbolic links::
322 $ ln -s functions/<name>.<instance name> configs/<name>.<number>
326 $ ln -s functions/ncm.usb0 configs/c.1
329 Path('%s/hid.usb%s' % (config_root, report_id)).symlink_to(function_root)
331 5. Enabling the gadget
332 ----------------------
333 Such a gadget must be finally enabled so that the USB host can enumerate it.
335 In order to enable the gadget it must be bound to a UDC (USB Device
338 $ echo <udc name> > UDC
340 where <udc name> is one of those found in /sys/class/udc/*
343 $ echo s3c-hsotg > UDC
346 udc = next(Path('/sys/class/udc/').glob('*'))
347 Path('%s/UDC' % gadget_root).write_text('%s' % udc.name)