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