1 from typing import Sequence
2 from pathlib import Path
5 # https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt
7 gadget_root = '/sys/kernel/config/usb_gadget/adafruit-blinka'
11 def __init__(self, *, descriptor: bytes, usage_page: int, usage: int, report_ids: Sequence[int],
12 in_report_lengths: Sequence[int], out_report_lengths: Sequence[int]) -> None:
13 self.out_report_lengths = out_report_lengths
14 self.in_report_lengths = in_report_lengths
15 self.report_ids = report_ids
17 self.usage_page = usage_page
18 self.descriptor = descriptor
21 def send_report(self, report: bytearray, report_id: int = None):
23 Path('%s/functions/hid.usb%s/dev' % (gadget_root, report_id or self.report_ids[0])) \
27 with open('/dev/hidg%s' % device, 'rb+') as fd:
31 Device.KEYBOARD = Device(
34 0x05, 0x01, # usage page (generic desktop ctrls)
35 0x09, 0x06, # usage (keyboard)
36 0xa1, 0x01, # collection (application)
37 0x05, 0x07, # usage page (kbrd/keypad)
38 0x19, 0xe0, # usage minimum (0xe0)
39 0x29, 0xe7, # usage maximum (0xe7)
40 0x15, 0x00, # logical minimum (0)
41 0x25, 0x01, # logical maximum (1)
42 0x75, 0x01, # report size (1)
43 0x95, 0x08, # report count (8)
44 0x81, 0x02, # input (data,var,abs,no wrap,linear,preferred state,no null position)
45 0x95, 0x01, # report count (1)
46 0x75, 0x08, # report size (8)
47 0x81, 0x01, # input (const,array,abs,no wrap,linear,preferred state,no null position)
48 0x95, 0x03, # report count (3)
49 0x75, 0x01, # report size (1)
50 0x05, 0x08, # usage page (leds)
51 0x19, 0x01, # usage minimum (num lock)
52 0x29, 0x05, # usage maximum (kana)
53 0x91, 0x02, # output (data,var,abs,no wrap,linear,preferred state,no null position,non-volatile)
54 0x95, 0x01, # report count (1)
55 0x75, 0x05, # report size (5)
56 0x91, 0x01, # output (const,array,abs,no wrap,linear,preferred state,no null position,non-volatile)
57 0x95, 0x06, # report count (6)
58 0x75, 0x08, # report size (8)
59 0x15, 0x00, # logical minimum (0)
60 0x26, 0xff, 0x00, # logical maximum (255)
61 0x05, 0x07, # usage page (kbrd/keypad)
62 0x19, 0x00, # usage minimum (0x00)
63 0x2a, 0xff, 0x00, # usage maximum (0xff)
64 0x81, 0x00, # input (data,array,abs,no wrap,linear,preferred state,no null position)
65 0xc0, # end collection
70 in_report_lengths=[8],
71 out_report_lengths=[1]
73 Device.MOUSE = Device(
76 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
77 0x09, 0x02, # Usage (Mouse)
78 0xA1, 0x01, # Collection (Application)
79 0x09, 0x01, # Usage (Pointer)
80 0xA1, 0x00, # Collection (Physical)
81 0x05, 0x09, # Usage Page (Button)
82 0x19, 0x01, # Usage Minimum (0x01)
83 0x29, 0x05, # Usage Maximum (0x05)
84 0x15, 0x00, # Logical Minimum (0)
85 0x25, 0x01, # Logical Maximum (1)
86 0x95, 0x05, # Report Count (5)
87 0x75, 0x01, # Report Size (1)
88 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
89 0x95, 0x01, # Report Count (1)
90 0x75, 0x03, # Report Size (3)
91 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
92 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
93 0x09, 0x30, # Usage (X)
94 0x09, 0x31, # Usage (Y)
95 0x15, 0x81, # Logical Minimum (-127)
96 0x25, 0x7F, # Logical Maximum (127)
97 0x75, 0x08, # Report Size (8)
98 0x95, 0x02, # Report Count (2)
99 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
100 0x09, 0x38, # Usage (Wheel)
101 0x15, 0x81, # Logical Minimum (-127)
102 0x25, 0x7F, # Logical Maximum (127)
103 0x75, 0x08, # Report Size (8)
104 0x95, 0x01, # Report Count (1)
105 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
106 0xC0, # End Collection
107 0xC0, # End Collection
109 # Omitted for brevity.
113 in_report_lengths=[4],
114 out_report_lengths=[0],
117 Device.CONSUMER_CONTROL = Device(
119 0x05, 0x0C, # Usage Page (Consumer)
120 0x09, 0x01, # Usage (Consumer Control)
121 0xA1, 0x01, # Collection (Application)
122 0x75, 0x10, # Report Size (16)
123 0x95, 0x01, # Report Count (1)
124 0x15, 0x01, # Logical Minimum (1)
125 0x26, 0x8C, 0x02, # Logical Maximum (652)
126 0x19, 0x01, # Usage Minimum (Consumer Control)
127 0x2A, 0x8C, 0x02, # Usage Maximum (AC Send)
128 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
129 0xC0, # End Collection
131 # Omitted for brevity.
135 in_report_lengths=[2],
136 out_report_lengths=[0],
138 devices = [Device.KEYBOARD, Device.MOUSE, Device.CONSUMER_CONTROL]
141 def disable() -> None:
143 Path('%s/UDC' % gadget_root).write_text('')
144 except FileNotFoundError:
146 for symlink in Path(gadget_root).glob('configs/**/hid.usb*'):
149 for strings_file in Path(gadget_root).rglob('configs/*/strings/*/*'):
150 if strings_file.is_dir():
153 for strings_file in Path(gadget_root).rglob('configs/*/strings/*'):
154 if strings_file.is_dir():
156 for config_dir in Path(gadget_root).rglob('configs/*'):
157 if config_dir.is_dir():
159 for function_dir in Path(gadget_root).rglob('functions/*'):
160 if function_dir.is_dir():
163 Path(gadget_root).rmdir()
168 # atexit.register(disable)
171 def enable(devices: Sequence[Device], boot_device: int = 0) -> None:
172 if len(devices) == 0:
176 1. Creating the gadgets
177 -----------------------
179 For each gadget to be created its corresponding directory must be created::
181 $ mkdir $CONFIGFS_HOME/usb_gadget/<gadget name>
185 $ mkdir $CONFIGFS_HOME/usb_gadget/g1
191 $ cd $CONFIGFS_HOME/usb_gadget/g1
193 Each gadget needs to have its vendor id <VID> and product id <PID> specified::
195 $ echo <VID> > idVendor
196 $ echo <PID> > idProduct
198 A gadget also needs its serial number, manufacturer and product strings.
199 In order to have a place to store them, a strings subdirectory must be created
200 for each language, e.g.::
202 $ mkdir strings/0x409
204 Then the strings can be specified::
206 $ echo <serial number> > strings/0x409/serialnumber
207 $ echo <manufacturer> > strings/0x409/manufacturer
208 $ echo <product> > strings/0x409/product
210 Path('%s/functions' % gadget_root).mkdir(parents=True, exist_ok=True)
211 Path('%s/configs' % gadget_root).mkdir(parents=True, exist_ok=True)
212 Path('%s/bcdDevice' % gadget_root).write_text('%s' % 1) # Version 1.0.0
213 Path('%s/bcdUSB' % gadget_root).write_text('%s' % 0x0200) # USB 2.0
214 Path('%s/bDeviceClass' % gadget_root).write_text('%s' % 0x00) # multipurpose i guess?
215 Path('%s/bDeviceProtocol' % gadget_root).write_text('%s' % 0x00)
216 Path('%s/bDeviceSubClass' % gadget_root).write_text('%s' % 0x00)
217 Path('%s/bMaxPacketSize0' % gadget_root).write_text('%s' % 0x08)
218 Path('%s/idProduct' % gadget_root).write_text('%s' % 0x0104) # Multifunction Composite Gadget
219 Path('%s/idVendor' % gadget_root).write_text('%s' % 0x1d6b) # Linux Foundation
221 2. Creating the configurations
222 ------------------------------
224 Each gadget will consist of a number of configurations, their corresponding
225 directories must be created:
227 $ mkdir configs/<name>.<number>
229 where <name> can be any string which is legal in a filesystem and the
230 <number> is the configuration's number, e.g.::
238 Each configuration also needs its strings, so a subdirectory must be created
239 for each language, e.g.::
241 $ mkdir configs/c.1/strings/0x409
243 Then the configuration string can be specified::
245 $ echo <configuration> > configs/c.1/strings/0x409/configuration
247 Some attributes can also be set for a configuration, e.g.::
249 $ echo 120 > configs/c.1/MaxPower
252 for i, device in enumerate(devices):
254 config_root = '%s/configs/device.%s' % (gadget_root, 1)
255 Path('%s/' % config_root).mkdir(parents=True, exist_ok=True)
256 Path('%s/strings/0x409' % config_root).mkdir(parents=True, exist_ok=True)
257 Path('%s/strings/0x409/configuration' % config_root).write_text('my configuration')
258 Path('%s/MaxPower' % config_root).write_text('150')
259 Path('%s/bmAttributes' % config_root).write_text('%s' % 0x080)
261 3. Creating the functions
262 -------------------------
264 The gadget will provide some functions, for each function its corresponding
265 directory must be created::
267 $ mkdir functions/<name>.<instance name>
269 where <name> corresponds to one of allowed function names and instance name
270 is an arbitrary string allowed in a filesystem, e.g.::
272 $ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()
278 Each function provides its specific set of attributes, with either read-only
279 or read-write access. Where applicable they need to be written to as
281 Please refer to Documentation/ABI/*/configfs-usb-gadget* for more information.
286 for report_index, report_id in enumerate(device.report_ids):
287 function_root = '%s/functions/hid.usb%s' % (gadget_root, report_id)
289 Path('%s/' % function_root).mkdir(parents=True)
290 except FileExistsError:
292 Path('%s/protocol' % function_root).write_text('%s' % report_id)
293 Path('%s/report_length' % function_root).write_text('%s' % device.in_report_lengths[report_index])
294 Path('%s/subclass' % function_root).write_text('%s' % 1)
295 Path('%s/report_desc' % function_root).write_bytes(device.descriptor)
297 4. Associating the functions with their configurations
298 ------------------------------------------------------
300 At this moment a number of gadgets is created, each of which has a number of
301 configurations specified and a number of functions available. What remains
302 is specifying which function is available in which configuration (the same
303 function can be used in multiple configurations). This is achieved with
304 creating symbolic links::
306 $ ln -s functions/<name>.<instance name> configs/<name>.<number>
310 $ ln -s functions/ncm.usb0 configs/c.1
313 Path('%s/hid.usb%s' % (config_root, report_id)).symlink_to(function_root)
315 5. Enabling the gadget
316 ----------------------
317 Such a gadget must be finally enabled so that the USB host can enumerate it.
319 In order to enable the gadget it must be bound to a UDC (USB Device
322 $ echo <udc name> > UDC
324 where <udc name> is one of those found in /sys/class/udc/*
327 $ echo s3c-hsotg > UDC
330 udc = next(Path('/sys/class/udc/').glob('*'))
331 Path('%s/UDC' % gadget_root).write_text('%s' % udc.name)