15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# found in the LICENSE file.
45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)"""Implementation of a USB HID keyboard.
65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)Two classes are provided by this module. The KeyboardFeature class implements
85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)the core functionality of a HID keyboard and can be included in any HID gadget.
95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)The KeyboardGadget class implements an example keyboard gadget.
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)"""
115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import struct
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import hid_constants
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import hid_descriptors
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import hid_gadget
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import usb_constants
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class KeyboardFeature(hid_gadget.HidFeature):
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  """HID feature implementation for a keyboard.
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  REPORT_DESC provides an example HID report descriptor for a device including
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  this functionality.
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  """
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  REPORT_DESC = hid_descriptors.ReportDescriptor(
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      hid_descriptors.UsagePage(0x01),  # Generic Desktop
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      hid_descriptors.Usage(0x06),  # Keyboard
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      hid_descriptors.Collection(
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_constants.CollectionType.APPLICATION,
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.UsagePage(0x07),  # Key Codes
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.UsageMinimum(224),
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.UsageMaximum(231),
355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.LogicalMinimum(0, force_length=1),
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.LogicalMaximum(1),
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportSize(1),
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportCount(8),
395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Input(hid_descriptors.Data,
405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                hid_descriptors.Variable,
415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                hid_descriptors.Absolute),
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportCount(1),
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportSize(8),
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Input(hid_descriptors.Constant),
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportCount(5),
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportSize(1),
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.UsagePage(0x08),  # LEDs
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.UsageMinimum(1),
495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.UsageMaximum(5),
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Output(hid_descriptors.Data,
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                 hid_descriptors.Variable,
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                 hid_descriptors.Absolute),
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportCount(1),
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportSize(3),
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Output(hid_descriptors.Constant),
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportCount(6),
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportSize(8),
585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.LogicalMinimum(0, force_length=1),
595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.LogicalMaximum(101),
605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.UsagePage(0x07),  # Key Codes
615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.UsageMinimum(0, force_length=1),
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.UsageMaximum(101),
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Input(hid_descriptors.Data, hid_descriptors.Array)
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      )
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  )
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def __init__(self):
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    super(KeyboardFeature, self).__init__()
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._modifiers = 0
705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._keys = [0, 0, 0, 0, 0, 0]
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._leds = 0
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def ModifierDown(self, modifier):
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._modifiers |= modifier
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if self.IsConnected():
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self.SendReport(self.GetInputReport())
775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def ModifierUp(self, modifier):
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._modifiers &= ~modifier
805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if self.IsConnected():
815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self.SendReport(self.GetInputReport())
825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def KeyDown(self, keycode):
845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    free = self._keys.index(0)
855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._keys[free] = keycode
865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if self.IsConnected():
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self.SendReport(self.GetInputReport())
885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def KeyUp(self, keycode):
905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    free = self._keys.index(keycode)
915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._keys[free] = 0
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if self.IsConnected():
935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self.SendReport(self.GetInputReport())
945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def GetInputReport(self):
965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """Construct an input report.
975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    See Device Class Definition for Human Interface Devices (HID) Version 1.11
995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Appendix B.1.
1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Returns:
1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      A packed input report.
1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """
1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return struct.pack('BBBBBBBB', self._modifiers, 0, *self._keys)
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def GetOutputReport(self):
1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """Construct an output report.
1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    See Device Class Definition for Human Interface Devices (HID) Version 1.11
1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Appendix B.1.
1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Returns:
1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      A packed input report.
1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """
1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return struct.pack('B', self._leds)
1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def SetOutputReport(self, data):
1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """Handle an output report.
1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    See Device Class Definition for Human Interface Devices (HID) Version 1.11
1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Appendix B.1.
1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Args:
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      data: Report data.
1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Returns:
1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      True on success, None to stall the pipe.
1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """
1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if len(data) >= 1:
1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._leds, = struct.unpack('B', data)
1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return True
1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class KeyboardGadget(hid_gadget.HidGadget):
1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  """USB gadget implementation of a HID keyboard."""
1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def __init__(self, vendor_id=0x18D1, product_id=0xFF02):
1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._feature = KeyboardFeature()
1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    super(KeyboardGadget, self).__init__(
1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        report_desc=KeyboardFeature.REPORT_DESC,
1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        features={0: self._feature},
1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        packet_size=8,
1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        interval_ms=1,
1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        out_endpoint=False,
1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        vendor_id=usb_constants.VendorID.GOOGLE,
1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        product_id=usb_constants.ProductID.GOOGLE_KEYBOARD_GADGET,
1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        device_version=0x0100)
1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.AddStringDescriptor(1, 'Google Inc.')
1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.AddStringDescriptor(2, 'Keyboard Gadget')
1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def ModifierDown(self, modifier):
1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._feature.ModifierDown(modifier)
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def ModifierUp(self, modifier):
1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._feature.ModifierUp(modifier)
1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def KeyDown(self, keycode):
1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._feature.KeyDown(keycode)
1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def KeyUp(self, keycode):
1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._feature.KeyUp(keycode)
1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def RegisterHandlers():
1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  """Registers web request handlers with the application server."""
1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  from tornado import web
1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  class WebConfigureHandler(web.RequestHandler):
1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    def post(self):
1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      server.SwitchGadget(KeyboardGadget())
1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  class WebTypeHandler(web.RequestHandler):
1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    def post(self):
1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      string = self.get_argument('string')
1785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      for char in string:
1795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if char in hid_constants.KEY_CODES:
1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          code = hid_constants.KEY_CODES[char]
1815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          server.gadget.KeyDown(code)
1825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          server.gadget.KeyUp(code)
1835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        elif char in hid_constants.SHIFT_KEY_CODES:
1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          code = hid_constants.SHIFT_KEY_CODES[char]
1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          server.gadget.ModifierDown(hid_constants.ModifierKey.L_SHIFT)
1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          server.gadget.KeyDown(code)
1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          server.gadget.KeyUp(code)
1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          server.gadget.ModifierUp(hid_constants.ModifierKey.L_SHIFT)
1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  class WebPressHandler(web.RequestHandler):
1915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    def post(self):
1935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      code = hid_constants.KEY_CODES[self.get_argument('key')]
1945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      server.gadget.KeyDown(code)
1955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      server.gadget.KeyUp(code)
1965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  import server
1985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  server.app.add_handlers('.*$', [
1995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      (r'/keyboard/configure', WebConfigureHandler),
2005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      (r'/keyboard/type', WebTypeHandler),
2015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      (r'/keyboard/press', WebPressHandler),
2025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ])
203