~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/hid/tests/test_keyboard.py

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
  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()

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php