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 392