Home | History | Annotate | Download | only in usb_gadget
      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