Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/hid/tests/test_keyboard.py
26308 views
1
#!/bin/env python3
2
# SPDX-License-Identifier: GPL-2.0
3
# -*- coding: utf-8 -*-
4
#
5
# Copyright (c) 2018 Benjamin Tissoires <[email protected]>
6
# Copyright (c) 2018 Red Hat, Inc.
7
#
8
9
from . import base
10
import hidtools.hid
11
import libevdev
12
import logging
13
14
logger = logging.getLogger("hidtools.test.keyboard")
15
16
17
class InvalidHIDCommunication(Exception):
18
pass
19
20
21
class KeyboardData(object):
22
pass
23
24
25
class 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
86
class 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
117
class 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
173
class 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.
219
class 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
261
class 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
360
class 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
414
class 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
478
class TestLEDKeyboard(BaseTest.TestKeyboard):
479
def create_device(self):
480
return LEDKeyboard()
481
482
483
class TestPrimaxKeyboard(BaseTest.TestKeyboard):
484
def create_device(self):
485
return PrimaxKeyboard()
486
487