]> Git Repo - J-linux.git/blob - tools/testing/selftests/hid/tests/test_keyboard.py
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / tools / testing / selftests / hid / tests / test_keyboard.py
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()
This page took 0.05762 seconds and 4 git commands to generate.