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 7This gadget emulates a USB Human Interface Device. Multiple logical components 8of a device can be composed together as separate "features" where each has its 9own Report ID and will be called upon to answer get/set input/output/feature 10report requests as necessary. 11""" 12 13from __future__ import print_function 14 15import math 16import struct 17import uuid 18 19import composite_gadget 20import hid_constants 21import usb_constants 22import usb_descriptors 23 24 25class HidCompositeFeature(composite_gadget.CompositeFeature): 26 """Generic HID feature for a composite device. 27 """ 28 29 def __init__(self, report_desc, features, 30 packet_size=64, interval_ms=10, interface_number=0, 31 interface_string=0, 32 in_endpoint=0x81, out_endpoint=0x01): 33 """Create a composite device feature implementing the HID protocol. 34 35 Args: 36 report_desc: HID report descriptor. 37 features: Map between Report IDs and HidFeature objects to handle them. 38 packet_size: Maximum interrupt packet size. 39 interval_ms: Interrupt transfer interval in milliseconds. 40 interface_number: Interface number for this feature (default 0). 41 in_endpoint: Endpoint number for the IN endpoint (default 0x81). 42 out_endpoint: Endpoint number for the OUT endpoint or None to disable 43 the endpoint (default 0x01). 44 45 Raises: 46 ValueError: If any of the parameters are out of range. 47 """ 48 fs_interface_desc = usb_descriptors.InterfaceDescriptor( 49 bInterfaceNumber=interface_number, 50 bInterfaceClass=usb_constants.DeviceClass.HID, 51 bInterfaceSubClass=0, # Non-bootable. 52 bInterfaceProtocol=0, # None. 53 iInterface=interface_string, 54 ) 55 56 hs_interface_desc = usb_descriptors.InterfaceDescriptor( 57 bInterfaceNumber=interface_number, 58 bInterfaceClass=usb_constants.DeviceClass.HID, 59 bInterfaceSubClass=0, # Non-bootable. 60 bInterfaceProtocol=0, # None. 61 iInterface=interface_string, 62 ) 63 64 hid_desc = usb_descriptors.HidDescriptor() 65 hid_desc.AddDescriptor(hid_constants.DescriptorType.REPORT, 66 len(report_desc)) 67 fs_interface_desc.Add(hid_desc) 68 hs_interface_desc.Add(hid_desc) 69 70 fs_interval = math.ceil(math.log(interval_ms, 2)) + 1 71 if fs_interval < 1 or fs_interval > 16: 72 raise ValueError('Full speed interval out of range: {} ({} ms)' 73 .format(fs_interval, interval_ms)) 74 75 fs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor( 76 bEndpointAddress=in_endpoint, 77 bmAttributes=usb_constants.TransferType.INTERRUPT, 78 wMaxPacketSize=packet_size, 79 bInterval=fs_interval 80 )) 81 82 hs_interval = math.ceil(math.log(interval_ms, 2)) + 4 83 if hs_interval < 1 or hs_interval > 16: 84 raise ValueError('High speed interval out of range: {} ({} ms)' 85 .format(hs_interval, interval_ms)) 86 87 hs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor( 88 bEndpointAddress=in_endpoint, 89 bmAttributes=usb_constants.TransferType.INTERRUPT, 90 wMaxPacketSize=packet_size, 91 bInterval=hs_interval 92 )) 93 94 if out_endpoint is not None: 95 fs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor( 96 bEndpointAddress=out_endpoint, 97 bmAttributes=usb_constants.TransferType.INTERRUPT, 98 wMaxPacketSize=packet_size, 99 bInterval=fs_interval 100 )) 101 hs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor( 102 bEndpointAddress=out_endpoint, 103 bmAttributes=usb_constants.TransferType.INTERRUPT, 104 wMaxPacketSize=packet_size, 105 bInterval=hs_interval 106 )) 107 108 super(HidCompositeFeature, self).__init__( 109 [fs_interface_desc], [hs_interface_desc]) 110 self._report_desc = report_desc 111 self._features = features 112 self._interface_number = interface_number 113 self._in_endpoint = in_endpoint 114 self._out_endpoint = out_endpoint 115 116 def Connected(self, gadget): 117 super(HidCompositeFeature, self).Connected(gadget) 118 for report_id, feature in self._features.iteritems(): 119 feature.Connected(self, report_id) 120 121 def Disconnected(self): 122 super(HidCompositeFeature, self).Disconnected() 123 for feature in self._features.itervalues(): 124 feature.Disconnected() 125 126 def StandardControlRead(self, recipient, request, value, index, length): 127 if recipient == usb_constants.Recipient.INTERFACE: 128 if index == self._interface_number: 129 desc_type = value >> 8 130 desc_index = value & 0xff 131 if desc_type == hid_constants.DescriptorType.REPORT: 132 if desc_index == 0: 133 return self._report_desc[:length] 134 135 return super(HidCompositeFeature, self).StandardControlRead( 136 recipient, request, value, index, length) 137 138 def ClassControlRead(self, recipient, request, value, index, length): 139 """Handle class-specific control requests. 140 141 See Device Class Definition for Human Interface Devices (HID) Version 1.11 142 section 7.2. 143 144 Args: 145 recipient: Request recipient (device, interface, endpoint, etc.) 146 request: bRequest field of the setup packet. 147 value: wValue field of the setup packet. 148 index: wIndex field of the setup packet. 149 length: Maximum amount of data the host expects the device to return. 150 151 Returns: 152 A buffer to return to the USB host with len <= length on success or 153 None to stall the pipe. 154 """ 155 if recipient != usb_constants.Recipient.INTERFACE: 156 return None 157 if index != self._interface_number: 158 return None 159 160 if request == hid_constants.Request.GET_REPORT: 161 report_type, report_id = value >> 8, value & 0xFF 162 print('GetReport(type={}, id={}, length={})'.format( 163 report_type, report_id, length)) 164 return self.GetReport(report_type, report_id, length) 165 166 def ClassControlWrite(self, recipient, request, value, index, data): 167 """Handle class-specific control requests. 168 169 See Device Class Definition for Human Interface Devices (HID) Version 1.11 170 section 7.2. 171 172 Args: 173 recipient: Request recipient (device, interface, endpoint, etc.) 174 request: bRequest field of the setup packet. 175 value: wValue field of the setup packet. 176 index: wIndex field of the setup packet. 177 data: Data stage of the request. 178 179 Returns: 180 True on success, None to stall the pipe. 181 """ 182 if recipient != usb_constants.Recipient.INTERFACE: 183 return None 184 if index != self._interface_number: 185 return None 186 187 if request == hid_constants.Request.SET_REPORT: 188 report_type, report_id = value >> 8, value & 0xFF 189 print('SetReport(type={}, id={}, length={})' 190 .format(report_type, report_id, len(data))) 191 return self.SetReport(report_type, report_id, data) 192 elif request == hid_constants.Request.SET_IDLE: 193 duration, report_id = value >> 8, value & 0xFF 194 print('SetIdle(duration={}, report={})' 195 .format(duration, report_id)) 196 return True 197 198 def GetReport(self, report_type, report_id, length): 199 """Handle GET_REPORT requests. 200 201 See Device Class Definition for Human Interface Devices (HID) Version 1.11 202 section 7.2.1. 203 204 Args: 205 report_type: Requested report type. 206 report_id: Requested report ID. 207 length: Maximum amount of data the host expects the device to return. 208 209 Returns: 210 A buffer to return to the USB host with len <= length on success or 211 None to stall the pipe. 212 """ 213 feature = self._features.get(report_id, None) 214 if feature is None: 215 return None 216 217 if report_type == hid_constants.ReportType.INPUT: 218 return feature.GetInputReport()[:length] 219 elif report_type == hid_constants.ReportType.OUTPUT: 220 return feature.GetOutputReport()[:length] 221 elif report_type == hid_constants.ReportType.FEATURE: 222 return feature.GetFeatureReport()[:length] 223 224 def SetReport(self, report_type, report_id, data): 225 """Handle SET_REPORT requests. 226 227 See Device Class Definition for Human Interface Devices (HID) Version 1.11 228 section 7.2.2. 229 230 Args: 231 report_type: Report type. 232 report_id: Report ID. 233 data: Report data. 234 235 Returns: 236 True on success, None to stall the pipe. 237 """ 238 feature = self._features.get(report_id, None) 239 if feature is None: 240 return None 241 242 if report_type == hid_constants.ReportType.INPUT: 243 return feature.SetInputReport(data) 244 elif report_type == hid_constants.ReportType.OUTPUT: 245 return feature.SetOutputReport(data) 246 elif report_type == hid_constants.ReportType.FEATURE: 247 return feature.SetFeatureReport(data) 248 249 def SendReport(self, report_id, data): 250 """Send a HID report. 251 252 See Device Class Definition for Human Interface Devices (HID) Version 1.11 253 section 8. 254 255 Args: 256 report_id: Report ID associated with the data. 257 data: Contents of the report. 258 """ 259 if report_id == 0: 260 self.SendPacket(self._in_endpoint, data) 261 else: 262 self.SendPacket(self._in_endpoint, struct.pack('B', report_id) + data) 263 264 def ReceivePacket(self, endpoint, data): 265 """Dispatch a report to the appropriate feature. 266 267 See Device Class Definition for Human Interface Devices (HID) Version 1.11 268 section 8. 269 270 Args: 271 endpoint: Incoming endpoint (must be the Interrupt OUT pipe). 272 data: Interrupt packet data. 273 """ 274 assert endpoint == self._out_endpoint 275 276 if 0 in self._features: 277 self._features[0].SetOutputReport(data) 278 elif len(data) >= 1: 279 report_id, = struct.unpack('B', data[0]) 280 feature = self._features.get(report_id, None) 281 if feature is None or feature.SetOutputReport(data[1:]) is None: 282 self.HaltEndpoint(endpoint) 283 284 285class HidFeature(object): 286 """Represents a component of a HID gadget. 287 288 A "feature" produces and consumes reports with a particular Report ID. For 289 example a keyboard, mouse or vendor specific functionality. 290 """ 291 292 def __init__(self): 293 self._gadget = None 294 self._report_id = None 295 296 def Connected(self, my_gadget, report_id): 297 self._gadget = my_gadget 298 self._report_id = report_id 299 300 def Disconnected(self): 301 self._gadget = None 302 self._report_id = None 303 304 def IsConnected(self): 305 return self._gadget is not None 306 307 def SendReport(self, data): 308 """Send a report with this feature's Report ID. 309 310 Args: 311 data: Report to send. If necessary the Report ID will be added. 312 313 Raises: 314 RuntimeError: If a report cannot be sent at this time. 315 """ 316 if not self.IsConnected(): 317 raise RuntimeError('Device is not connected.') 318 self._gadget.SendReport(self._report_id, data) 319 320 def SetInputReport(self, data): 321 """Handle an input report sent from the host. 322 323 This function is called when a SET_REPORT(input) command for this class's 324 Report ID is received. It should be overridden by a subclass. 325 326 Args: 327 data: Contents of the input report. 328 """ 329 pass # pragma: no cover 330 331 def SetOutputReport(self, data): 332 """Handle an feature report sent from the host. 333 334 This function is called when a SET_REPORT(output) command or interrupt OUT 335 transfer is received with this class's Report ID. It should be overridden 336 by a subclass. 337 338 Args: 339 data: Contents of the output report. 340 """ 341 pass # pragma: no cover 342 343 def SetFeatureReport(self, data): 344 """Handle an feature report sent from the host. 345 346 This function is called when a SET_REPORT(feature) command for this class's 347 Report ID is received. It should be overridden by a subclass. 348 349 Args: 350 data: Contents of the feature report. 351 """ 352 pass # pragma: no cover 353 354 def GetInputReport(self): 355 """Handle a input report request from the host. 356 357 This function is called when a GET_REPORT(input) command for this class's 358 Report ID is received. It should be overridden by a subclass. 359 360 Returns: 361 The input report or None to stall the pipe. 362 """ 363 pass # pragma: no cover 364 365 def GetOutputReport(self): 366 """Handle a output report request from the host. 367 368 This function is called when a GET_REPORT(output) command for this class's 369 Report ID is received. It should be overridden by a subclass. 370 371 Returns: 372 The output report or None to stall the pipe. 373 """ 374 pass # pragma: no cover 375 376 def GetFeatureReport(self): 377 """Handle a feature report request from the host. 378 379 This function is called when a GET_REPORT(feature) command for this class's 380 Report ID is received. It should be overridden by a subclass. 381 382 Returns: 383 The feature report or None to stall the pipe. 384 """ 385 pass # pragma: no cover 386 387class HidGadget(composite_gadget.CompositeGadget): 388 """Generic HID gadget. 389 """ 390 391 def __init__(self, report_desc, features, vendor_id, product_id, 392 packet_size=64, interval_ms=10, out_endpoint=True, 393 device_version=0x0100): 394 """Create a HID gadget. 395 396 Args: 397 report_desc: HID report descriptor. 398 features: Map between Report IDs and HidFeature objects to handle them. 399 vendor_id: Device Vendor ID. 400 product_id: Device Product ID. 401 packet_size: Maximum interrupt packet size. 402 interval_ms: Interrupt transfer interval in milliseconds. 403 out_endpoint: Should this device have an interrupt OUT endpoint? 404 device_version: Device version number. 405 406 Raises: 407 ValueError: If any of the parameters are out of range. 408 """ 409 device_desc = usb_descriptors.DeviceDescriptor( 410 idVendor=vendor_id, 411 idProduct=product_id, 412 bcdUSB=0x0200, 413 iManufacturer=1, 414 iProduct=2, 415 iSerialNumber=3, 416 bcdDevice=device_version) 417 418 if out_endpoint: 419 out_endpoint = 0x01 420 else: 421 out_endpoint = None 422 423 self._hid_feature = HidCompositeFeature( 424 report_desc=report_desc, 425 features=features, 426 packet_size=packet_size, 427 interval_ms=interval_ms, 428 out_endpoint=out_endpoint) 429 430 super(HidGadget, self).__init__(device_desc, [self._hid_feature]) 431 self.AddStringDescriptor(3, '{:06X}'.format(uuid.getnode())) 432 433 def SendReport(self, report_id, data): 434 self._hid_feature.SendReport(report_id, data) 435