Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / tools / testing / selftests / hid / tests / test_keyboard.py
blobb3b2bdbf63b7bb648c2497284e5f96f96c936519
1 #!/bin/env python3
2 # SPDX-License-Identifier: GPL-2.0
3 # -*- coding: utf-8 -*-
5 # Copyright (c) 2018 Benjamin Tissoires <benjamin.tissoires@gmail.com>
6 # Copyright (c) 2018 Red Hat, Inc.
9 from . import base
10 import hidtools.hid
11 import libevdev
12 import logging
14 logger = logging.getLogger("hidtools.test.keyboard")
17 class InvalidHIDCommunication(Exception):
18 pass
21 class KeyboardData(object):
22 pass
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 = {}
31 def _update_key_state(self, keys):
32 """
33 Update the internal state of keys with the new state given.
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]
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
47 # ...and press those that are in parameter
48 for key in keys:
49 self.keystates[key] = True
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
58 def create_array_report(self, keys, reportID=None, application=None):
59 """
60 Return an input report for this device.
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
70 keyboard = self._create_report_data()
71 return self.create_report(keyboard, reportID=reportID, application=application)
73 def event(self, keys, reportID=None, application=None):
74 """
75 Send an input event on the default report ID.
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]
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
110 # fmt: on
112 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
113 super().__init__(rdesc, name, input_info)
114 self.default_reportID = 1
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
141 # fmt: on
143 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
144 super().__init__(rdesc, name, input_info)
146 def _create_report_data(self):
147 data = KeyboardData()
148 array = []
150 hut = hidtools.hut.HUT
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
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)
165 # if array length is bigger than 6, report ErrorRollOver
166 if len(array) > 6:
167 array = ["ErrorRollOver"] * 6
169 data.keyboard = array
170 return data
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
209 # fmt: on
211 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
212 super().__init__(rdesc, name, input_info)
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
255 # fmt: on
257 def __init__(self, rdesc=report_descriptor, name=None, input_info=None):
258 super().__init__(rdesc, name, input_info)
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
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
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
285 def test_two_keys(self):
286 uhdev = self.uhdev
287 evdev = uhdev.get_evdev()
288 syn_event = self.syn_event
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
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
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
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
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
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
345 def test_modifiers(self):
346 # ctrl-alt-del would be very nice :)
347 uhdev = self.uhdev
348 syn_event = self.syn_event
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)
360 class TestPlainKeyboard(BaseTest.TestKeyboard):
361 def create_device(self):
362 return PlainKeyboard()
364 def test_10_keys(self):
365 uhdev = self.uhdev
366 syn_event = self.syn_event
368 r = uhdev.event(
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 )",
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)
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)
414 class TestArrayKeyboard(BaseTest.TestKeyboard):
415 def create_device(self):
416 return ArrayKeyboard()
418 def test_10_keys(self):
419 uhdev = self.uhdev
420 syn_event = self.syn_event
422 r = uhdev.event(
424 "1 and !",
425 "2 and @",
426 "3 and #",
427 "4 and $",
428 "5 and %",
429 "6 and ^",
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()
441 self.debug_reports(r, uhdev, events)
442 self.assertInputEventsIn(expected, events)
444 # ErrRollOver
445 r = uhdev.event(
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 )",
459 events = uhdev.next_sync_events()
461 self.debug_reports(r, uhdev, events)
463 assert len(events) == 0
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)
478 class TestLEDKeyboard(BaseTest.TestKeyboard):
479 def create_device(self):
480 return LEDKeyboard()
483 class TestPrimaxKeyboard(BaseTest.TestKeyboard):
484 def create_device(self):
485 return PrimaxKeyboard()