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)"""A HID-class echo device.
65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)This module provides a HID feature and HID device that can be used as an
85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)echo test for HID drivers. The device exposes vendor-specific input, output
95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)and feature usages that transmit 8 bytes of data. Data written sent as an
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)output report is echoed as an input report. The value of the feature report
115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)can be written and read with control transfers.
125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)"""
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import struct
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import hid_constants
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import hid_descriptors
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import hid_gadget
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import usb_constants
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class EchoFeature(hid_gadget.HidFeature):
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  REPORT_DESC = hid_descriptors.ReportDescriptor(
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      hid_descriptors.UsagePage(0xFF00),  # Vendor Defined
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      hid_descriptors.Usage(0),
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      hid_descriptors.Collection(
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_constants.CollectionType.APPLICATION,
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.LogicalMinimum(0, force_length=1),
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.LogicalMaximum(255, force_length=2),
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportSize(8),
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.ReportCount(8),
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Usage(0),
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Input(hid_descriptors.Data,
355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                hid_descriptors.Variable,
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                hid_descriptors.Absolute),
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Usage(0),
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Output(hid_descriptors.Data,
395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                 hid_descriptors.Variable,
405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                 hid_descriptors.Absolute),
415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Usage(0),
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          hid_descriptors.Feature(hid_descriptors.Data,
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                  hid_descriptors.Variable,
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                  hid_descriptors.Absolute)
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      )
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  )
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def __init__(self):
495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    super(EchoFeature, self).__init__()
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._input_output_report = 0
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._feature_report = 0
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def SetInputReport(self, data):
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._input_output_report, = struct.unpack('<Q', data)
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.SendReport(struct.pack('<Q', self._input_output_report))
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return True
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def SetOutputReport(self, data):
595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._input_output_report, = struct.unpack('<Q', data)
605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.SendReport(struct.pack('<Q', self._input_output_report))
615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return True
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def SetFeatureReport(self, data):
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._feature_report, = struct.unpack('<Q', data)
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return True
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def GetInputReport(self):
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return struct.pack('<Q', self._input_output_report)
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def GetOutputReport(self):
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return struct.pack('<Q', self._input_output_report)
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def GetFeatureReport(self):
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return struct.pack('<Q', self._feature_report)
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class EchoGadget(hid_gadget.HidGadget):
785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def __init__(self):
805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._feature = EchoFeature()
815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    super(EchoGadget, self).__init__(
825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        report_desc=EchoFeature.REPORT_DESC,
835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        features={0: self._feature},
845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        packet_size=8,
855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        interval_ms=1,
865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        out_endpoint=True,
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        vendor_id=usb_constants.VendorID.GOOGLE,
885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        product_id=usb_constants.ProductID.GOOGLE_HID_ECHO_GADGET,
895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        device_version=0x0100)
905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.AddStringDescriptor(1, 'Google Inc.')
915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.AddStringDescriptor(2, 'HID Echo Gadget')
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def RegisterHandlers():
955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  from tornado import web
965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  class WebConfigureHandler(web.RequestHandler):
985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    def post(self):
1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      server.SwitchGadget(EchoGadget())
1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  import server
1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  server.app.add_handlers('.*$', [
1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      (r'/hid_echo/configure', WebConfigureHandler),
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ])
106