OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 """Human Interface Device gadget module. |
| 6 |
| 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 """ |
| 12 |
| 13 import math |
| 14 import struct |
| 15 import uuid |
| 16 |
| 17 import gadget |
| 18 import hid_constants |
| 19 import usb_constants |
| 20 import usb_descriptors |
| 21 |
| 22 |
| 23 class HidGadget(gadget.Gadget): |
| 24 """Generic HID gadget. |
| 25 """ |
| 26 |
| 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. |
| 31 |
| 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. |
| 41 |
| 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) |
| 53 |
| 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. |
| 62 ) |
| 63 fs_config_desc.AddInterface(fs_interface_desc) |
| 64 |
| 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. |
| 73 ) |
| 74 hs_config_desc.AddInterface(hs_interface_desc) |
| 75 |
| 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) |
| 81 |
| 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)) |
| 86 |
| 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 |
| 92 )) |
| 93 |
| 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)) |
| 98 |
| 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 |
| 104 )) |
| 105 |
| 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 |
| 112 )) |
| 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 |
| 118 )) |
| 119 |
| 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 |
| 124 |
| 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) |
| 129 |
| 130 def Disconnected(self): |
| 131 super(HidGadget, self).Disconnected() |
| 132 for feature in self._features.itervalues(): |
| 133 feature.Disconnected() |
| 134 |
| 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] |
| 140 |
| 141 return super(HidGadget, self).GetDescriptor(recipient, typ, index, lang, |
| 142 length) |
| 143 |
| 144 def ClassControlRead(self, recipient, request, value, index, length): |
| 145 """Handle class-specific control requests. |
| 146 |
| 147 See Device Class Definition for Human Interface Devices (HID) Version 1.11 |
| 148 section 7.2. |
| 149 |
| 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. |
| 156 |
| 157 Returns: |
| 158 A buffer to return to the USB host with len <= length on success or |
| 159 None to stall the pipe. |
| 160 """ |
| 161 if recipient != usb_constants.Recipient.INTERFACE: |
| 162 return None |
| 163 if index != 0: |
| 164 return None |
| 165 |
| 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) |
| 171 |
| 172 def ClassControlWrite(self, recipient, request, value, index, data): |
| 173 """Handle class-specific control requests. |
| 174 |
| 175 See Device Class Definition for Human Interface Devices (HID) Version 1.11 |
| 176 section 7.2. |
| 177 |
| 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. |
| 184 |
| 185 Returns: |
| 186 True on success, None to stall the pipe. |
| 187 """ |
| 188 if recipient != usb_constants.Recipient.INTERFACE: |
| 189 return None |
| 190 if index != 0: |
| 191 return None |
| 192 |
| 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 |
| 203 |
| 204 def GetReport(self, report_type, report_id, length): |
| 205 """Handle GET_REPORT requests. |
| 206 |
| 207 See Device Class Definition for Human Interface Devices (HID) Version 1.11 |
| 208 section 7.2.1. |
| 209 |
| 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. |
| 214 |
| 215 Returns: |
| 216 A buffer to return to the USB host with len <= length on success or |
| 217 None to stall the pipe. |
| 218 """ |
| 219 feature = self._features.get(report_id, None) |
| 220 if feature is None: |
| 221 return None |
| 222 |
| 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] |
| 229 |
| 230 def SetReport(self, report_type, report_id, data): |
| 231 """Handle SET_REPORT requests. |
| 232 |
| 233 See Device Class Definition for Human Interface Devices (HID) Version 1.11 |
| 234 section 7.2.2. |
| 235 |
| 236 Args: |
| 237 report_type: Report type. |
| 238 report_id: Report ID. |
| 239 data: Report data. |
| 240 |
| 241 Returns: |
| 242 True on success, None to stall the pipe. |
| 243 """ |
| 244 feature = self._features.get(report_id, None) |
| 245 if feature is None: |
| 246 return None |
| 247 |
| 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) |
| 254 |
| 255 def SendReport(self, report_id, data): |
| 256 """Send a HID report. |
| 257 |
| 258 See Device Class Definition for Human Interface Devices (HID) Version 1.11 |
| 259 section 8. |
| 260 |
| 261 Args: |
| 262 report_id: Report ID associated with the data. |
| 263 data: Contents of the report. |
| 264 """ |
| 265 if report_id == 0: |
| 266 self.SendPacket(0x81, data) |
| 267 else: |
| 268 self.SendPacket(0x81, struct.pack('B', report_id) + data) |
| 269 |
| 270 def ReceivePacket(self, endpoint, data): |
| 271 """Dispatch a report to the appropriate feature. |
| 272 |
| 273 See Device Class Definition for Human Interface Devices (HID) Version 1.11 |
| 274 section 8. |
| 275 |
| 276 Args: |
| 277 endpoint: Incoming endpoint (must be the Interrupt OUT pipe). |
| 278 data: Interrupt packet data. |
| 279 """ |
| 280 assert endpoint == 0x01 |
| 281 |
| 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) |
| 289 |
| 290 |
| 291 class HidFeature(object): |
| 292 """Represents a component of a HID gadget. |
| 293 |
| 294 A "feature" produces and consumes reports with a particular Report ID. For |
| 295 example a keyboard, mouse or vendor specific functionality. |
| 296 """ |
| 297 |
| 298 def __init__(self): |
| 299 self._gadget = None |
| 300 self._report_id = None |
| 301 |
| 302 def Connected(self, my_gadget, report_id): |
| 303 self._gadget = my_gadget |
| 304 self._report_id = report_id |
| 305 |
| 306 def Disconnected(self): |
| 307 self._gadget = None |
| 308 self._report_id = None |
| 309 |
| 310 def IsConnected(self): |
| 311 return self._gadget is not None |
| 312 |
| 313 def SendReport(self, data): |
| 314 """Send a report with this feature's Report ID. |
| 315 |
| 316 Args: |
| 317 data: Report to send. If necessary the Report ID will be added. |
| 318 |
| 319 Raises: |
| 320 RuntimeError: If a report cannot be sent at this time. |
| 321 """ |
| 322 if not self.IsConnected(): |
| 323 raise RuntimeError('Device is not connected.') |
| 324 self._gadget.SendReport(self._report_id, data) |
| 325 |
| 326 def SetInputReport(self, data): |
| 327 """Handle an input report sent from the host. |
| 328 |
| 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. |
| 331 |
| 332 Args: |
| 333 data: Contents of the input report. |
| 334 """ |
| 335 pass # pragma: no cover |
| 336 |
| 337 def SetOutputReport(self, data): |
| 338 """Handle an feature report sent from the host. |
| 339 |
| 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. |
| 343 |
| 344 Args: |
| 345 data: Contents of the output report. |
| 346 """ |
| 347 pass # pragma: no cover |
| 348 |
| 349 def SetFeatureReport(self, data): |
| 350 """Handle an feature report sent from the host. |
| 351 |
| 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. |
| 354 |
| 355 Args: |
| 356 data: Contents of the feature report. |
| 357 """ |
| 358 pass # pragma: no cover |
| 359 |
| 360 def GetInputReport(self): |
| 361 """Handle a input report request from the host. |
| 362 |
| 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. |
| 365 |
| 366 Returns: |
| 367 The input report or None to stall the pipe. |
| 368 """ |
| 369 pass # pragma: no cover |
| 370 |
| 371 def GetOutputReport(self): |
| 372 """Handle a output report request from the host. |
| 373 |
| 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. |
| 376 |
| 377 Returns: |
| 378 The output report or None to stall the pipe. |
| 379 """ |
| 380 pass # pragma: no cover |
| 381 |
| 382 def GetFeatureReport(self): |
| 383 """Handle a feature report request from the host. |
| 384 |
| 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. |
| 387 |
| 388 Returns: |
| 389 The feature report or None to stall the pipe. |
| 390 """ |
| 391 pass # pragma: no cover |
OLD | NEW |