# Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Implementation of a USB HID keyboard. Two classes are provided by this module. The KeyboardFeature class implements the core functionality of a HID keyboard and can be included in any HID gadget. The KeyboardGadget class implements an example keyboard gadget. """ import struct import hid_constants import hid_descriptors import hid_gadget import usb_constants class KeyboardFeature(hid_gadget.HidFeature): """HID feature implementation for a keyboard. REPORT_DESC provides an example HID report descriptor for a device including this functionality. """ REPORT_DESC = hid_descriptors.ReportDescriptor( hid_descriptors.UsagePage(0x01), # Generic Desktop hid_descriptors.Usage(0x06), # Keyboard hid_descriptors.Collection( hid_constants.CollectionType.APPLICATION, hid_descriptors.UsagePage(0x07), # Key Codes hid_descriptors.UsageMinimum(224), hid_descriptors.UsageMaximum(231), hid_descriptors.LogicalMinimum(0, force_length=1), hid_descriptors.LogicalMaximum(1), hid_descriptors.ReportSize(1), hid_descriptors.ReportCount(8), hid_descriptors.Input(hid_descriptors.Data, hid_descriptors.Variable, hid_descriptors.Absolute), hid_descriptors.ReportCount(1), hid_descriptors.ReportSize(8), hid_descriptors.Input(hid_descriptors.Constant), hid_descriptors.ReportCount(5), hid_descriptors.ReportSize(1), hid_descriptors.UsagePage(0x08), # LEDs hid_descriptors.UsageMinimum(1), hid_descriptors.UsageMaximum(5), hid_descriptors.Output(hid_descriptors.Data, hid_descriptors.Variable, hid_descriptors.Absolute), hid_descriptors.ReportCount(1), hid_descriptors.ReportSize(3), hid_descriptors.Output(hid_descriptors.Constant), hid_descriptors.ReportCount(6), hid_descriptors.ReportSize(8), hid_descriptors.LogicalMinimum(0, force_length=1), hid_descriptors.LogicalMaximum(101), hid_descriptors.UsagePage(0x07), # Key Codes hid_descriptors.UsageMinimum(0, force_length=1), hid_descriptors.UsageMaximum(101), hid_descriptors.Input(hid_descriptors.Data, hid_descriptors.Array) ) ) def __init__(self): super(KeyboardFeature, self).__init__() self._modifiers = 0 self._keys = [0, 0, 0, 0, 0, 0] self._leds = 0 def ModifierDown(self, modifier): self._modifiers |= modifier if self.IsConnected(): self.SendReport(self.GetInputReport()) def ModifierUp(self, modifier): self._modifiers &= ~modifier if self.IsConnected(): self.SendReport(self.GetInputReport()) def KeyDown(self, keycode): free = self._keys.index(0) self._keys[free] = keycode if self.IsConnected(): self.SendReport(self.GetInputReport()) def KeyUp(self, keycode): free = self._keys.index(keycode) self._keys[free] = 0 if self.IsConnected(): self.SendReport(self.GetInputReport()) def GetInputReport(self): """Construct an input report. See Device Class Definition for Human Interface Devices (HID) Version 1.11 Appendix B.1. Returns: A packed input report. """ return struct.pack('BBBBBBBB', self._modifiers, 0, *self._keys) def GetOutputReport(self): """Construct an output report. See Device Class Definition for Human Interface Devices (HID) Version 1.11 Appendix B.1. Returns: A packed input report. """ return struct.pack('B', self._leds) def SetOutputReport(self, data): """Handle an output report. See Device Class Definition for Human Interface Devices (HID) Version 1.11 Appendix B.1. Args: data: Report data. Returns: True on success, None to stall the pipe. """ if len(data) >= 1: self._leds, = struct.unpack('B', data) return True class KeyboardGadget(hid_gadget.HidGadget): """USB gadget implementation of a HID keyboard.""" def __init__(self, vendor_id=0x18D1, product_id=0xFF02): self._feature = KeyboardFeature() super(KeyboardGadget, self).__init__( report_desc=KeyboardFeature.REPORT_DESC, features={0: self._feature}, packet_size=8, interval_ms=1, out_endpoint=False, vendor_id=usb_constants.VendorID.GOOGLE, product_id=usb_constants.ProductID.GOOGLE_KEYBOARD_GADGET, device_version=0x0100) self.AddStringDescriptor(1, 'Google Inc.') self.AddStringDescriptor(2, 'Keyboard Gadget') def ModifierDown(self, modifier): self._feature.ModifierDown(modifier) def ModifierUp(self, modifier): self._feature.ModifierUp(modifier) def KeyDown(self, keycode): self._feature.KeyDown(keycode) def KeyUp(self, keycode): self._feature.KeyUp(keycode) def RegisterHandlers(): """Registers web request handlers with the application server.""" from tornado import web class WebConfigureHandler(web.RequestHandler): def post(self): server.SwitchGadget(KeyboardGadget()) class WebTypeHandler(web.RequestHandler): def post(self): string = self.get_argument('string') for char in string: if char in hid_constants.KEY_CODES: code = hid_constants.KEY_CODES[char] server.gadget.KeyDown(code) server.gadget.KeyUp(code) elif char in hid_constants.SHIFT_KEY_CODES: code = hid_constants.SHIFT_KEY_CODES[char] server.gadget.ModifierDown(hid_constants.ModifierKey.L_SHIFT) server.gadget.KeyDown(code) server.gadget.KeyUp(code) server.gadget.ModifierUp(hid_constants.ModifierKey.L_SHIFT) class WebPressHandler(web.RequestHandler): def post(self): code = hid_constants.KEY_CODES[self.get_argument('key')] server.gadget.KeyDown(code) server.gadget.KeyUp(code) import server server.app.add_handlers('.*$', [ (r'/keyboard/configure', WebConfigureHandler), (r'/keyboard/type', WebTypeHandler), (r'/keyboard/press', WebPressHandler), ])