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 """Implementation of a USB HID keyboard.
      6 
      7 Two classes are provided by this module. The KeyboardFeature class implements
      8 the core functionality of a HID keyboard and can be included in any HID gadget.
      9 The KeyboardGadget class implements an example keyboard gadget.
     10 """
     11 
     12 import struct
     13 
     14 import hid_constants
     15 import hid_descriptors
     16 import hid_gadget
     17 import usb_constants
     18 
     19 
     20 class KeyboardFeature(hid_gadget.HidFeature):
     21   """HID feature implementation for a keyboard.
     22 
     23   REPORT_DESC provides an example HID report descriptor for a device including
     24   this functionality.
     25   """
     26 
     27   REPORT_DESC = hid_descriptors.ReportDescriptor(
     28       hid_descriptors.UsagePage(0x01),  # Generic Desktop
     29       hid_descriptors.Usage(0x06),  # Keyboard
     30       hid_descriptors.Collection(
     31           hid_constants.CollectionType.APPLICATION,
     32           hid_descriptors.UsagePage(0x07),  # Key Codes
     33           hid_descriptors.UsageMinimum(224),
     34           hid_descriptors.UsageMaximum(231),
     35           hid_descriptors.LogicalMinimum(0, force_length=1),
     36           hid_descriptors.LogicalMaximum(1),
     37           hid_descriptors.ReportSize(1),
     38           hid_descriptors.ReportCount(8),
     39           hid_descriptors.Input(hid_descriptors.Data,
     40                                 hid_descriptors.Variable,
     41                                 hid_descriptors.Absolute),
     42           hid_descriptors.ReportCount(1),
     43           hid_descriptors.ReportSize(8),
     44           hid_descriptors.Input(hid_descriptors.Constant),
     45           hid_descriptors.ReportCount(5),
     46           hid_descriptors.ReportSize(1),
     47           hid_descriptors.UsagePage(0x08),  # LEDs
     48           hid_descriptors.UsageMinimum(1),
     49           hid_descriptors.UsageMaximum(5),
     50           hid_descriptors.Output(hid_descriptors.Data,
     51                                  hid_descriptors.Variable,
     52                                  hid_descriptors.Absolute),
     53           hid_descriptors.ReportCount(1),
     54           hid_descriptors.ReportSize(3),
     55           hid_descriptors.Output(hid_descriptors.Constant),
     56           hid_descriptors.ReportCount(6),
     57           hid_descriptors.ReportSize(8),
     58           hid_descriptors.LogicalMinimum(0, force_length=1),
     59           hid_descriptors.LogicalMaximum(101),
     60           hid_descriptors.UsagePage(0x07),  # Key Codes
     61           hid_descriptors.UsageMinimum(0, force_length=1),
     62           hid_descriptors.UsageMaximum(101),
     63           hid_descriptors.Input(hid_descriptors.Data, hid_descriptors.Array)
     64       )
     65   )
     66 
     67   def __init__(self):
     68     super(KeyboardFeature, self).__init__()
     69     self._modifiers = 0
     70     self._keys = [0, 0, 0, 0, 0, 0]
     71     self._leds = 0
     72 
     73   def ModifierDown(self, modifier):
     74     self._modifiers |= modifier
     75     if self.IsConnected():
     76       self.SendReport(self.GetInputReport())
     77 
     78   def ModifierUp(self, modifier):
     79     self._modifiers &= ~modifier
     80     if self.IsConnected():
     81       self.SendReport(self.GetInputReport())
     82 
     83   def KeyDown(self, keycode):
     84     free = self._keys.index(0)
     85     self._keys[free] = keycode
     86     if self.IsConnected():
     87       self.SendReport(self.GetInputReport())
     88 
     89   def KeyUp(self, keycode):
     90     free = self._keys.index(keycode)
     91     self._keys[free] = 0
     92     if self.IsConnected():
     93       self.SendReport(self.GetInputReport())
     94 
     95   def GetInputReport(self):
     96     """Construct an input report.
     97 
     98     See Device Class Definition for Human Interface Devices (HID) Version 1.11
     99     Appendix B.1.
    100 
    101     Returns:
    102       A packed input report.
    103     """
    104     return struct.pack('BBBBBBBB', self._modifiers, 0, *self._keys)
    105 
    106   def GetOutputReport(self):
    107     """Construct an output report.
    108 
    109     See Device Class Definition for Human Interface Devices (HID) Version 1.11
    110     Appendix B.1.
    111 
    112     Returns:
    113       A packed input report.
    114     """
    115     return struct.pack('B', self._leds)
    116 
    117   def SetOutputReport(self, data):
    118     """Handle an output report.
    119 
    120     See Device Class Definition for Human Interface Devices (HID) Version 1.11
    121     Appendix B.1.
    122 
    123     Args:
    124       data: Report data.
    125 
    126     Returns:
    127       True on success, None to stall the pipe.
    128     """
    129     if len(data) >= 1:
    130       self._leds, = struct.unpack('B', data)
    131     return True
    132 
    133 
    134 class KeyboardGadget(hid_gadget.HidGadget):
    135   """USB gadget implementation of a HID keyboard."""
    136 
    137   def __init__(self, vendor_id=0x18D1, product_id=0xFF02):
    138     self._feature = KeyboardFeature()
    139     super(KeyboardGadget, self).__init__(
    140         report_desc=KeyboardFeature.REPORT_DESC,
    141         features={0: self._feature},
    142         packet_size=8,
    143         interval_ms=1,
    144         out_endpoint=False,
    145         vendor_id=usb_constants.VendorID.GOOGLE,
    146         product_id=usb_constants.ProductID.GOOGLE_KEYBOARD_GADGET,
    147         device_version=0x0100)
    148     self.AddStringDescriptor(1, 'Google Inc.')
    149     self.AddStringDescriptor(2, 'Keyboard Gadget')
    150 
    151   def ModifierDown(self, modifier):
    152     self._feature.ModifierDown(modifier)
    153 
    154   def ModifierUp(self, modifier):
    155     self._feature.ModifierUp(modifier)
    156 
    157   def KeyDown(self, keycode):
    158     self._feature.KeyDown(keycode)
    159 
    160   def KeyUp(self, keycode):
    161     self._feature.KeyUp(keycode)
    162 
    163 
    164 def RegisterHandlers():
    165   """Registers web request handlers with the application server."""
    166 
    167   from tornado import web
    168 
    169   class WebConfigureHandler(web.RequestHandler):
    170 
    171     def post(self):
    172       server.SwitchGadget(KeyboardGadget())
    173 
    174   class WebTypeHandler(web.RequestHandler):
    175 
    176     def post(self):
    177       string = self.get_argument('string')
    178       for char in string:
    179         if char in hid_constants.KEY_CODES:
    180           code = hid_constants.KEY_CODES[char]
    181           server.gadget.KeyDown(code)
    182           server.gadget.KeyUp(code)
    183         elif char in hid_constants.SHIFT_KEY_CODES:
    184           code = hid_constants.SHIFT_KEY_CODES[char]
    185           server.gadget.ModifierDown(hid_constants.ModifierKey.L_SHIFT)
    186           server.gadget.KeyDown(code)
    187           server.gadget.KeyUp(code)
    188           server.gadget.ModifierUp(hid_constants.ModifierKey.L_SHIFT)
    189 
    190   class WebPressHandler(web.RequestHandler):
    191 
    192     def post(self):
    193       code = hid_constants.KEY_CODES[self.get_argument('key')]
    194       server.gadget.KeyDown(code)
    195       server.gadget.KeyUp(code)
    196 
    197   import server
    198   server.app.add_handlers('.*$', [
    199       (r'/keyboard/configure', WebConfigureHandler),
    200       (r'/keyboard/type', WebTypeHandler),
    201       (r'/keyboard/press', WebPressHandler),
    202   ])
    203