Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
fork
Configure Feed
Select the types of activity you want to include in your feed.
1#!/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3# -*- coding: utf-8 -*-
4#
5# Copyright (c) 2018 Benjamin Tissoires <benjamin.tissoires@gmail.com>
6# Copyright (c) 2018 Red Hat, Inc.
7#
8
9from . import base
10import hidtools.hid
11import libevdev
12import logging
13
14logger = logging.getLogger("hidtools.test.keyboard")
15
16
17class InvalidHIDCommunication(Exception):
18 pass
19
20
21class KeyboardData(object):
22 pass
23
24
25class BaseKeyboard(base.UHIDTestDevice):
26 def __init__(self, rdesc, name=None, input_info=None):
27 assert rdesc is not None
28 super().__init__(name, "Key", input_info=input_info, rdesc=rdesc)
29 self.keystates = {}
30
31 def _update_key_state(self, keys):
32 """
33 Update the internal state of keys with the new state given.
34
35 :param key: a tuple of chars for the currently pressed keys.
36 """
37 # First remove the already released keys
38 unused_keys = [k for k, v in self.keystates.items() if not v]
39 for key in unused_keys:
40 del self.keystates[key]
41
42 # self.keystates contains now the list of currently pressed keys,
43 # release them...
44 for key in self.keystates.keys():
45 self.keystates[key] = False
46
47 # ...and press those that are in parameter
48 for key in keys:
49 self.keystates[key] = True
50
51 def _create_report_data(self):
52 keyboard = KeyboardData()
53 for key, value in self.keystates.items():
54 key = key.replace(" ", "").lower()
55 setattr(keyboard, key, value)
56 return keyboard
57
58 def create_array_report(self, keys, reportID=None, application=None):
59 """
60 Return an input report for this device.
61
62 :param keys: a tuple of chars for the pressed keys. The class maintains
63 the list of currently pressed keys, so to release a key, the caller
64 needs to call again this function without the key in this tuple.
65 :param reportID: the numeric report ID for this report, if needed
66 """
67 self._update_key_state(keys)
68 reportID = reportID or self.default_reportID
69
70 keyboard = self._create_report_data()
71 return self.create_report(keyboard, reportID=reportID, application=application)
72
73 def event(self, keys, reportID=None, application=None):
74 """
75 Send an input event on the default report ID.
76
77 :param keys: a tuple of chars for the pressed keys. The class maintains
78 the list of currently pressed keys, so to release a key, the caller
79 needs to call again this function without the key in this tuple.
80 """
81 r = self.create_array_report(keys, reportID, application)
82 self.call_input_event(r)
83 return [r]
84
85
86class PlainKeyboard(BaseKeyboard):
87 # fmt: off
88 report_descriptor = [
89 0x05, 0x01, # Usage Page (Generic Desktop)
90 0x09, 0x06, # Usage (Keyboard)
91 0xa1, 0x01, # Collection (Application)
92 0x85, 0x01, # .Report ID (1)
93 0x05, 0x07, # .Usage Page (Keyboard)
94 0x19, 0xe0, # .Usage Minimum (224)
95 0x29, 0xe7, # .Usage Maximum (231)
96 0x15, 0x00, # .Logical Minimum (0)
97 0x25, 0x01, # .Logical Maximum (1)
98 0x75, 0x01, # .Report Size (1)
99 0x95, 0x08, # .Report Count (8)
100 0x81, 0x02, # .Input (Data,Var,Abs)
101 0x19, 0x00, # .Usage Minimum (0)
102 0x29, 0x97, # .Usage Maximum (151)
103 0x15, 0x00, # .Logical Minimum (0)
104 0x25, 0x01, # .Logical Maximum (1)
105 0x75, 0x01, # .Report Size (1)
106 0x95, 0x98, # .Report Count (152)
107 0x81, 0x02, # .Input (Data,Var,Abs)
108 0xc0, # End Collection
109 ]
110 # fmt: on
111
112 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
113 super().__init__(rdesc, name, input_info)
114 self.default_reportID = 1
115
116
117class ArrayKeyboard(BaseKeyboard):
118 # fmt: off
119 report_descriptor = [
120 0x05, 0x01, # Usage Page (Generic Desktop)
121 0x09, 0x06, # Usage (Keyboard)
122 0xa1, 0x01, # Collection (Application)
123 0x05, 0x07, # .Usage Page (Keyboard)
124 0x19, 0xe0, # .Usage Minimum (224)
125 0x29, 0xe7, # .Usage Maximum (231)
126 0x15, 0x00, # .Logical Minimum (0)
127 0x25, 0x01, # .Logical Maximum (1)
128 0x75, 0x01, # .Report Size (1)
129 0x95, 0x08, # .Report Count (8)
130 0x81, 0x02, # .Input (Data,Var,Abs)
131 0x95, 0x06, # .Report Count (6)
132 0x75, 0x08, # .Report Size (8)
133 0x15, 0x00, # .Logical Minimum (0)
134 0x26, 0xa4, 0x00, # .Logical Maximum (164)
135 0x05, 0x07, # .Usage Page (Keyboard)
136 0x19, 0x00, # .Usage Minimum (0)
137 0x29, 0xa4, # .Usage Maximum (164)
138 0x81, 0x00, # .Input (Data,Arr,Abs)
139 0xc0, # End Collection
140 ]
141 # fmt: on
142
143 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
144 super().__init__(rdesc, name, input_info)
145
146 def _create_report_data(self):
147 data = KeyboardData()
148 array = []
149
150 hut = hidtools.hut.HUT
151
152 # strip modifiers from the array
153 for k, v in self.keystates.items():
154 # we ignore depressed keys
155 if not v:
156 continue
157
158 usage = hut[0x07].from_name[k].usage
159 if usage >= 224 and usage <= 231:
160 # modifier
161 setattr(data, k.lower(), 1)
162 else:
163 array.append(k)
164
165 # if array length is bigger than 6, report ErrorRollOver
166 if len(array) > 6:
167 array = ["ErrorRollOver"] * 6
168
169 data.keyboard = array
170 return data
171
172
173class LEDKeyboard(ArrayKeyboard):
174 # fmt: off
175 report_descriptor = [
176 0x05, 0x01, # Usage Page (Generic Desktop)
177 0x09, 0x06, # Usage (Keyboard)
178 0xa1, 0x01, # Collection (Application)
179 0x05, 0x07, # .Usage Page (Keyboard)
180 0x19, 0xe0, # .Usage Minimum (224)
181 0x29, 0xe7, # .Usage Maximum (231)
182 0x15, 0x00, # .Logical Minimum (0)
183 0x25, 0x01, # .Logical Maximum (1)
184 0x75, 0x01, # .Report Size (1)
185 0x95, 0x08, # .Report Count (8)
186 0x81, 0x02, # .Input (Data,Var,Abs)
187 0x95, 0x01, # .Report Count (1)
188 0x75, 0x08, # .Report Size (8)
189 0x81, 0x01, # .Input (Cnst,Arr,Abs)
190 0x95, 0x05, # .Report Count (5)
191 0x75, 0x01, # .Report Size (1)
192 0x05, 0x08, # .Usage Page (LEDs)
193 0x19, 0x01, # .Usage Minimum (1)
194 0x29, 0x05, # .Usage Maximum (5)
195 0x91, 0x02, # .Output (Data,Var,Abs)
196 0x95, 0x01, # .Report Count (1)
197 0x75, 0x03, # .Report Size (3)
198 0x91, 0x01, # .Output (Cnst,Arr,Abs)
199 0x95, 0x06, # .Report Count (6)
200 0x75, 0x08, # .Report Size (8)
201 0x15, 0x00, # .Logical Minimum (0)
202 0x26, 0xa4, 0x00, # .Logical Maximum (164)
203 0x05, 0x07, # .Usage Page (Keyboard)
204 0x19, 0x00, # .Usage Minimum (0)
205 0x29, 0xa4, # .Usage Maximum (164)
206 0x81, 0x00, # .Input (Data,Arr,Abs)
207 0xc0, # End Collection
208 ]
209 # fmt: on
210
211 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
212 super().__init__(rdesc, name, input_info)
213
214
215# Some Primax manufactured keyboards set the Usage Page after having defined
216# some local Usages. It relies on the fact that the specification states that
217# Usages are to be concatenated with Usage Pages upon finding a Main item (see
218# 6.2.2.8). This test covers this case.
219class PrimaxKeyboard(ArrayKeyboard):
220 # fmt: off
221 report_descriptor = [
222 0x05, 0x01, # Usage Page (Generic Desktop)
223 0x09, 0x06, # Usage (Keyboard)
224 0xA1, 0x01, # Collection (Application)
225 0x05, 0x07, # .Usage Page (Keyboard)
226 0x19, 0xE0, # .Usage Minimum (224)
227 0x29, 0xE7, # .Usage Maximum (231)
228 0x15, 0x00, # .Logical Minimum (0)
229 0x25, 0x01, # .Logical Maximum (1)
230 0x75, 0x01, # .Report Size (1)
231 0x95, 0x08, # .Report Count (8)
232 0x81, 0x02, # .Input (Data,Var,Abs)
233 0x75, 0x08, # .Report Size (8)
234 0x95, 0x01, # .Report Count (1)
235 0x81, 0x01, # .Input (Data,Var,Abs)
236 0x05, 0x08, # .Usage Page (LEDs)
237 0x19, 0x01, # .Usage Minimum (1)
238 0x29, 0x03, # .Usage Maximum (3)
239 0x75, 0x01, # .Report Size (1)
240 0x95, 0x03, # .Report Count (3)
241 0x91, 0x02, # .Output (Data,Var,Abs)
242 0x95, 0x01, # .Report Count (1)
243 0x75, 0x05, # .Report Size (5)
244 0x91, 0x01, # .Output (Constant)
245 0x15, 0x00, # .Logical Minimum (0)
246 0x26, 0xFF, 0x00, # .Logical Maximum (255)
247 0x19, 0x00, # .Usage Minimum (0)
248 0x2A, 0xFF, 0x00, # .Usage Maximum (255)
249 0x05, 0x07, # .Usage Page (Keyboard)
250 0x75, 0x08, # .Report Size (8)
251 0x95, 0x06, # .Report Count (6)
252 0x81, 0x00, # .Input (Data,Arr,Abs)
253 0xC0, # End Collection
254 ]
255 # fmt: on
256
257 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
258 super().__init__(rdesc, name, input_info)
259
260
261class BaseTest:
262 class TestKeyboard(base.BaseTestCase.TestUhid):
263 def test_single_key(self):
264 """check for key reliability."""
265 uhdev = self.uhdev
266 evdev = uhdev.get_evdev()
267 syn_event = self.syn_event
268
269 r = uhdev.event(["a and A"])
270 expected = [syn_event]
271 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1))
272 events = uhdev.next_sync_events()
273 self.debug_reports(r, uhdev, events)
274 self.assertInputEventsIn(expected, events)
275 assert evdev.value[libevdev.EV_KEY.KEY_A] == 1
276
277 r = uhdev.event([])
278 expected = [syn_event]
279 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0))
280 events = uhdev.next_sync_events()
281 self.debug_reports(r, uhdev, events)
282 self.assertInputEventsIn(expected, events)
283 assert evdev.value[libevdev.EV_KEY.KEY_A] == 0
284
285 def test_two_keys(self):
286 uhdev = self.uhdev
287 evdev = uhdev.get_evdev()
288 syn_event = self.syn_event
289
290 r = uhdev.event(["a and A", "q and Q"])
291 expected = [syn_event]
292 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1))
293 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 1))
294 events = uhdev.next_sync_events()
295 self.debug_reports(r, uhdev, events)
296 self.assertInputEventsIn(expected, events)
297 assert evdev.value[libevdev.EV_KEY.KEY_A] == 1
298
299 r = uhdev.event([])
300 expected = [syn_event]
301 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0))
302 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 0))
303 events = uhdev.next_sync_events()
304 self.debug_reports(r, uhdev, events)
305 self.assertInputEventsIn(expected, events)
306 assert evdev.value[libevdev.EV_KEY.KEY_A] == 0
307 assert evdev.value[libevdev.EV_KEY.KEY_Q] == 0
308
309 r = uhdev.event(["c and C"])
310 expected = [syn_event]
311 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 1))
312 events = uhdev.next_sync_events()
313 self.debug_reports(r, uhdev, events)
314 self.assertInputEventsIn(expected, events)
315 assert evdev.value[libevdev.EV_KEY.KEY_C] == 1
316
317 r = uhdev.event(["c and C", "Spacebar"])
318 expected = [syn_event]
319 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 1))
320 events = uhdev.next_sync_events()
321 self.debug_reports(r, uhdev, events)
322 assert libevdev.InputEvent(libevdev.EV_KEY.KEY_C) not in events
323 self.assertInputEventsIn(expected, events)
324 assert evdev.value[libevdev.EV_KEY.KEY_C] == 1
325 assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1
326
327 r = uhdev.event(["Spacebar"])
328 expected = [syn_event]
329 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 0))
330 events = uhdev.next_sync_events()
331 self.debug_reports(r, uhdev, events)
332 assert libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE) not in events
333 self.assertInputEventsIn(expected, events)
334 assert evdev.value[libevdev.EV_KEY.KEY_C] == 0
335 assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1
336
337 r = uhdev.event([])
338 expected = [syn_event]
339 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 0))
340 events = uhdev.next_sync_events()
341 self.debug_reports(r, uhdev, events)
342 self.assertInputEventsIn(expected, events)
343 assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 0
344
345 def test_modifiers(self):
346 # ctrl-alt-del would be very nice :)
347 uhdev = self.uhdev
348 syn_event = self.syn_event
349
350 r = uhdev.event(["LeftControl", "LeftShift", "= and +"])
351 expected = [syn_event]
352 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTCTRL, 1))
353 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTSHIFT, 1))
354 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_EQUAL, 1))
355 events = uhdev.next_sync_events()
356 self.debug_reports(r, uhdev, events)
357 self.assertInputEventsIn(expected, events)
358
359
360class TestPlainKeyboard(BaseTest.TestKeyboard):
361 def create_device(self):
362 return PlainKeyboard()
363
364 def test_10_keys(self):
365 uhdev = self.uhdev
366 syn_event = self.syn_event
367
368 r = uhdev.event(
369 [
370 "1 and !",
371 "2 and @",
372 "3 and #",
373 "4 and $",
374 "5 and %",
375 "6 and ^",
376 "7 and &",
377 "8 and *",
378 "9 and (",
379 "0 and )",
380 ]
381 )
382 expected = [syn_event]
383 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 1))
384 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1))
385 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1))
386 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1))
387 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1))
388 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1))
389 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1))
390 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 1))
391 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 1))
392 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 1))
393 events = uhdev.next_sync_events()
394 self.debug_reports(r, uhdev, events)
395 self.assertInputEventsIn(expected, events)
396
397 r = uhdev.event([])
398 expected = [syn_event]
399 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 0))
400 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0))
401 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0))
402 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0))
403 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0))
404 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0))
405 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0))
406 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 0))
407 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 0))
408 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 0))
409 events = uhdev.next_sync_events()
410 self.debug_reports(r, uhdev, events)
411 self.assertInputEventsIn(expected, events)
412
413
414class TestArrayKeyboard(BaseTest.TestKeyboard):
415 def create_device(self):
416 return ArrayKeyboard()
417
418 def test_10_keys(self):
419 uhdev = self.uhdev
420 syn_event = self.syn_event
421
422 r = uhdev.event(
423 [
424 "1 and !",
425 "2 and @",
426 "3 and #",
427 "4 and $",
428 "5 and %",
429 "6 and ^",
430 ]
431 )
432 expected = [syn_event]
433 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1))
434 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1))
435 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1))
436 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1))
437 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1))
438 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1))
439 events = uhdev.next_sync_events()
440
441 self.debug_reports(r, uhdev, events)
442 self.assertInputEventsIn(expected, events)
443
444 # ErrRollOver
445 r = uhdev.event(
446 [
447 "1 and !",
448 "2 and @",
449 "3 and #",
450 "4 and $",
451 "5 and %",
452 "6 and ^",
453 "7 and &",
454 "8 and *",
455 "9 and (",
456 "0 and )",
457 ]
458 )
459 events = uhdev.next_sync_events()
460
461 self.debug_reports(r, uhdev, events)
462
463 assert len(events) == 0
464
465 r = uhdev.event([])
466 expected = [syn_event]
467 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0))
468 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0))
469 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0))
470 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0))
471 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0))
472 expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0))
473 events = uhdev.next_sync_events()
474 self.debug_reports(r, uhdev, events)
475 self.assertInputEventsIn(expected, events)
476
477
478class TestLEDKeyboard(BaseTest.TestKeyboard):
479 def create_device(self):
480 return LEDKeyboard()
481
482
483class TestPrimaxKeyboard(BaseTest.TestKeyboard):
484 def create_device(self):
485 return PrimaxKeyboard()