Roll src/third_party/WebKit f007c95:0171005 (svn 185074:185088)
[chromium-blink-merge.git] / tools / usb_gadget / hid_gadget.py
blob82e4482e01cf147eb711c726753d54e489896d73
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Human Interface Device gadget module.
7 This gadget emulates a USB Human Interface Device. Multiple logical components
8 of a device can be composed together as separate "features" where each has its
9 own Report ID and will be called upon to answer get/set input/output/feature
10 report requests as necessary.
11 """
13 import math
14 import struct
15 import uuid
17 import gadget
18 import hid_constants
19 import usb_constants
20 import usb_descriptors
23 class HidGadget(gadget.Gadget):
24 """Generic HID gadget.
25 """
27 def __init__(self, report_desc, features, vendor_id, product_id,
28 packet_size=64, interval_ms=10, out_endpoint=True,
29 device_version=0x0100):
30 """Create a HID gadget.
32 Args:
33 report_desc: HID report descriptor.
34 features: Map between Report IDs and HidFeature objects to handle them.
35 vendor_id: Device Vendor ID.
36 product_id: Device Product ID.
37 packet_size: Maximum interrupt packet size.
38 interval_ms: Interrupt transfer interval in milliseconds.
39 out_endpoint: Should this device have an interrupt OUT endpoint?
40 device_version: Device version number.
42 Raises:
43 ValueError: If any of the parameters are out of range.
44 """
45 device_desc = usb_descriptors.DeviceDescriptor(
46 idVendor=vendor_id,
47 idProduct=product_id,
48 bcdUSB=0x0200,
49 iManufacturer=1,
50 iProduct=2,
51 iSerialNumber=3,
52 bcdDevice=device_version)
54 fs_config_desc = usb_descriptors.ConfigurationDescriptor(
55 bmAttributes=0x80,
56 MaxPower=50)
57 fs_interface_desc = usb_descriptors.InterfaceDescriptor(
58 bInterfaceNumber=0,
59 bInterfaceClass=usb_constants.DeviceClass.HID,
60 bInterfaceSubClass=0, # Non-bootable.
61 bInterfaceProtocol=0, # None.
63 fs_config_desc.AddInterface(fs_interface_desc)
65 hs_config_desc = usb_descriptors.ConfigurationDescriptor(
66 bmAttributes=0x80,
67 MaxPower=50)
68 hs_interface_desc = usb_descriptors.InterfaceDescriptor(
69 bInterfaceNumber=0,
70 bInterfaceClass=usb_constants.DeviceClass.HID,
71 bInterfaceSubClass=0, # Non-bootable.
72 bInterfaceProtocol=0, # None.
74 hs_config_desc.AddInterface(hs_interface_desc)
76 hid_desc = usb_descriptors.HidDescriptor()
77 hid_desc.AddDescriptor(hid_constants.DescriptorType.REPORT,
78 len(report_desc))
79 fs_interface_desc.Add(hid_desc)
80 hs_interface_desc.Add(hid_desc)
82 fs_interval = math.ceil(math.log(interval_ms, 2)) + 1
83 if fs_interval < 1 or fs_interval > 16:
84 raise ValueError('Full speed interval out of range: {} ({} ms)'
85 .format(fs_interval, interval_ms))
87 fs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
88 bEndpointAddress=0x81,
89 bmAttributes=usb_constants.TransferType.INTERRUPT,
90 wMaxPacketSize=packet_size,
91 bInterval=fs_interval
94 hs_interval = math.ceil(math.log(interval_ms, 2)) + 4
95 if hs_interval < 1 or hs_interval > 16:
96 raise ValueError('High speed interval out of range: {} ({} ms)'
97 .format(hs_interval, interval_ms))
99 hs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
100 bEndpointAddress=0x81,
101 bmAttributes=usb_constants.TransferType.INTERRUPT,
102 wMaxPacketSize=packet_size,
103 bInterval=hs_interval
106 if out_endpoint:
107 fs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
108 bEndpointAddress=0x01,
109 bmAttributes=usb_constants.TransferType.INTERRUPT,
110 wMaxPacketSize=packet_size,
111 bInterval=fs_interval
113 hs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
114 bEndpointAddress=0x01,
115 bmAttributes=usb_constants.TransferType.INTERRUPT,
116 wMaxPacketSize=packet_size,
117 bInterval=hs_interval
120 super(HidGadget, self).__init__(device_desc, fs_config_desc, hs_config_desc)
121 self.AddStringDescriptor(3, '{:06X}'.format(uuid.getnode()))
122 self._report_desc = report_desc
123 self._features = features
125 def Connected(self, chip, speed):
126 super(HidGadget, self).Connected(chip, speed)
127 for report_id, feature in self._features.iteritems():
128 feature.Connected(self, report_id)
130 def Disconnected(self):
131 super(HidGadget, self).Disconnected()
132 for feature in self._features.itervalues():
133 feature.Disconnected()
135 def GetDescriptor(self, recipient, typ, index, lang, length):
136 if recipient == usb_constants.Recipient.INTERFACE:
137 if typ == hid_constants.DescriptorType.REPORT:
138 if index == 0:
139 return self._report_desc[:length]
141 return super(HidGadget, self).GetDescriptor(recipient, typ, index, lang,
142 length)
144 def ClassControlRead(self, recipient, request, value, index, length):
145 """Handle class-specific control requests.
147 See Device Class Definition for Human Interface Devices (HID) Version 1.11
148 section 7.2.
150 Args:
151 recipient: Request recipient (device, interface, endpoint, etc.)
152 request: bRequest field of the setup packet.
153 value: wValue field of the setup packet.
154 index: wIndex field of the setup packet.
155 length: Maximum amount of data the host expects the device to return.
157 Returns:
158 A buffer to return to the USB host with len <= length on success or
159 None to stall the pipe.
161 if recipient != usb_constants.Recipient.INTERFACE:
162 return None
163 if index != 0:
164 return None
166 if request == hid_constants.Request.GET_REPORT:
167 report_type, report_id = value >> 8, value & 0xFF
168 print ('GetReport(type={}, id={}, length={})'
169 .format(report_type, report_id, length))
170 return self.GetReport(report_type, report_id, length)
172 def ClassControlWrite(self, recipient, request, value, index, data):
173 """Handle class-specific control requests.
175 See Device Class Definition for Human Interface Devices (HID) Version 1.11
176 section 7.2.
178 Args:
179 recipient: Request recipient (device, interface, endpoint, etc.)
180 request: bRequest field of the setup packet.
181 value: wValue field of the setup packet.
182 index: wIndex field of the setup packet.
183 data: Data stage of the request.
185 Returns:
186 True on success, None to stall the pipe.
188 if recipient != usb_constants.Recipient.INTERFACE:
189 return None
190 if index != 0:
191 return None
193 if request == hid_constants.Request.SET_REPORT:
194 report_type, report_id = value >> 8, value & 0xFF
195 print('SetReport(type={}, id={}, length={})'
196 .format(report_type, report_id, len(data)))
197 return self.SetReport(report_type, report_id, data)
198 elif request == hid_constants.Request.SET_IDLE:
199 duration, report_id = value >> 8, value & 0xFF
200 print('SetIdle(duration={}, report={})'
201 .format(duration, report_id))
202 return True
204 def GetReport(self, report_type, report_id, length):
205 """Handle GET_REPORT requests.
207 See Device Class Definition for Human Interface Devices (HID) Version 1.11
208 section 7.2.1.
210 Args:
211 report_type: Requested report type.
212 report_id: Requested report ID.
213 length: Maximum amount of data the host expects the device to return.
215 Returns:
216 A buffer to return to the USB host with len <= length on success or
217 None to stall the pipe.
219 feature = self._features.get(report_id, None)
220 if feature is None:
221 return None
223 if report_type == hid_constants.ReportType.INPUT:
224 return feature.GetInputReport()[:length]
225 elif report_type == hid_constants.ReportType.OUTPUT:
226 return feature.GetOutputReport()[:length]
227 elif report_type == hid_constants.ReportType.FEATURE:
228 return feature.GetFeatureReport()[:length]
230 def SetReport(self, report_type, report_id, data):
231 """Handle SET_REPORT requests.
233 See Device Class Definition for Human Interface Devices (HID) Version 1.11
234 section 7.2.2.
236 Args:
237 report_type: Report type.
238 report_id: Report ID.
239 data: Report data.
241 Returns:
242 True on success, None to stall the pipe.
244 feature = self._features.get(report_id, None)
245 if feature is None:
246 return None
248 if report_type == hid_constants.ReportType.INPUT:
249 return feature.SetInputReport(data)
250 elif report_type == hid_constants.ReportType.OUTPUT:
251 return feature.SetOutputReport(data)
252 elif report_type == hid_constants.ReportType.FEATURE:
253 return feature.SetFeatureReport(data)
255 def SendReport(self, report_id, data):
256 """Send a HID report.
258 See Device Class Definition for Human Interface Devices (HID) Version 1.11
259 section 8.
261 Args:
262 report_id: Report ID associated with the data.
263 data: Contents of the report.
265 if report_id == 0:
266 self.SendPacket(0x81, data)
267 else:
268 self.SendPacket(0x81, struct.pack('B', report_id) + data)
270 def ReceivePacket(self, endpoint, data):
271 """Dispatch a report to the appropriate feature.
273 See Device Class Definition for Human Interface Devices (HID) Version 1.11
274 section 8.
276 Args:
277 endpoint: Incoming endpoint (must be the Interrupt OUT pipe).
278 data: Interrupt packet data.
280 assert endpoint == 0x01
282 if 0 in self._features:
283 self._features[0].SetOutputReport(data)
284 elif len(data) >= 1:
285 report_id, = struct.unpack('B', data[0])
286 feature = self._features.get(report_id, None)
287 if feature is None or feature.SetOutputReport(data[1:]) is None:
288 self.HaltEndpoint(endpoint)
291 class HidFeature(object):
292 """Represents a component of a HID gadget.
294 A "feature" produces and consumes reports with a particular Report ID. For
295 example a keyboard, mouse or vendor specific functionality.
298 def __init__(self):
299 self._gadget = None
300 self._report_id = None
302 def Connected(self, my_gadget, report_id):
303 self._gadget = my_gadget
304 self._report_id = report_id
306 def Disconnected(self):
307 self._gadget = None
308 self._report_id = None
310 def IsConnected(self):
311 return self._gadget is not None
313 def SendReport(self, data):
314 """Send a report with this feature's Report ID.
316 Args:
317 data: Report to send. If necessary the Report ID will be added.
319 Raises:
320 RuntimeError: If a report cannot be sent at this time.
322 if not self.IsConnected():
323 raise RuntimeError('Device is not connected.')
324 self._gadget.SendReport(self._report_id, data)
326 def SetInputReport(self, data):
327 """Handle an input report sent from the host.
329 This function is called when a SET_REPORT(input) command for this class's
330 Report ID is received. It should be overridden by a subclass.
332 Args:
333 data: Contents of the input report.
335 pass # pragma: no cover
337 def SetOutputReport(self, data):
338 """Handle an feature report sent from the host.
340 This function is called when a SET_REPORT(output) command or interrupt OUT
341 transfer is received with this class's Report ID. It should be overridden
342 by a subclass.
344 Args:
345 data: Contents of the output report.
347 pass # pragma: no cover
349 def SetFeatureReport(self, data):
350 """Handle an feature report sent from the host.
352 This function is called when a SET_REPORT(feature) command for this class's
353 Report ID is received. It should be overridden by a subclass.
355 Args:
356 data: Contents of the feature report.
358 pass # pragma: no cover
360 def GetInputReport(self):
361 """Handle a input report request from the host.
363 This function is called when a GET_REPORT(input) command for this class's
364 Report ID is received. It should be overridden by a subclass.
366 Returns:
367 The input report or None to stall the pipe.
369 pass # pragma: no cover
371 def GetOutputReport(self):
372 """Handle a output report request from the host.
374 This function is called when a GET_REPORT(output) command for this class's
375 Report ID is received. It should be overridden by a subclass.
377 Returns:
378 The output report or None to stall the pipe.
380 pass # pragma: no cover
382 def GetFeatureReport(self):
383 """Handle a feature report request from the host.
385 This function is called when a GET_REPORT(feature) command for this class's
386 Report ID is received. It should be overridden by a subclass.
388 Returns:
389 The feature report or None to stall the pipe.
391 pass # pragma: no cover