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