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.
5 """Implementation of a USB HID keyboard.
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.
15 import hid_descriptors
20 class KeyboardFeature(hid_gadget
.HidFeature
):
21 """HID feature implementation for a keyboard.
23 REPORT_DESC provides an example HID report descriptor for a device including
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
)
68 super(KeyboardFeature
, self
).__init
__()
70 self
._keys
= [0, 0, 0, 0, 0, 0]
73 def ModifierDown(self
, modifier
):
74 self
._modifiers |
= modifier
75 if self
.IsConnected():
76 self
.SendReport(self
.GetInputReport())
78 def ModifierUp(self
, modifier
):
79 self
._modifiers
&= ~modifier
80 if self
.IsConnected():
81 self
.SendReport(self
.GetInputReport())
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())
89 def KeyUp(self
, keycode
):
90 free
= self
._keys
.index(keycode
)
92 if self
.IsConnected():
93 self
.SendReport(self
.GetInputReport())
95 def GetInputReport(self
):
96 """Construct an input report.
98 See Device Class Definition for Human Interface Devices (HID) Version 1.11
102 A packed input report.
104 return struct
.pack('BBBBBBBB', self
._modifiers
, 0, *self
._keys
)
106 def GetOutputReport(self
):
107 """Construct an output report.
109 See Device Class Definition for Human Interface Devices (HID) Version 1.11
113 A packed input report.
115 return struct
.pack('B', self
._leds
)
117 def SetOutputReport(self
, data
):
118 """Handle an output report.
120 See Device Class Definition for Human Interface Devices (HID) Version 1.11
127 True on success, None to stall the pipe.
130 self
._leds
, = struct
.unpack('B', data
)
134 class KeyboardGadget(hid_gadget
.HidGadget
):
135 """USB gadget implementation of a HID keyboard."""
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
},
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')
151 def ModifierDown(self
, modifier
):
152 self
._feature
.ModifierDown(modifier
)
154 def ModifierUp(self
, modifier
):
155 self
._feature
.ModifierUp(modifier
)
157 def KeyDown(self
, keycode
):
158 self
._feature
.KeyDown(keycode
)
160 def KeyUp(self
, keycode
):
161 self
._feature
.KeyUp(keycode
)
164 def RegisterHandlers():
165 """Registers web request handlers with the application server."""
167 from tornado
import web
169 class WebConfigureHandler(web
.RequestHandler
):
172 server
.SwitchGadget(KeyboardGadget())
174 class WebTypeHandler(web
.RequestHandler
):
177 string
= self
.get_argument('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
)
190 class WebPressHandler(web
.RequestHandler
):
193 code
= hid_constants
.KEY_CODES
[self
.get_argument('key')]
194 server
.gadget
.KeyDown(code
)
195 server
.gadget
.KeyUp(code
)
198 server
.app
.add_handlers('.*$', [
199 (r
'/keyboard/configure', WebConfigureHandler
),
200 (r
'/keyboard/type', WebTypeHandler
),
201 (r
'/keyboard/press', WebPressHandler
),