Path: blob/master/tools/testing/selftests/hid/tests/test_keyboard.py
26308 views
#!/bin/env python31# SPDX-License-Identifier: GPL-2.02# -*- coding: utf-8 -*-3#4# Copyright (c) 2018 Benjamin Tissoires <[email protected]>5# Copyright (c) 2018 Red Hat, Inc.6#78from . import base9import hidtools.hid10import libevdev11import logging1213logger = logging.getLogger("hidtools.test.keyboard")141516class InvalidHIDCommunication(Exception):17pass181920class KeyboardData(object):21pass222324class BaseKeyboard(base.UHIDTestDevice):25def __init__(self, rdesc, name=None, input_info=None):26assert rdesc is not None27super().__init__(name, "Key", input_info=input_info, rdesc=rdesc)28self.keystates = {}2930def _update_key_state(self, keys):31"""32Update the internal state of keys with the new state given.3334:param key: a tuple of chars for the currently pressed keys.35"""36# First remove the already released keys37unused_keys = [k for k, v in self.keystates.items() if not v]38for key in unused_keys:39del self.keystates[key]4041# self.keystates contains now the list of currently pressed keys,42# release them...43for key in self.keystates.keys():44self.keystates[key] = False4546# ...and press those that are in parameter47for key in keys:48self.keystates[key] = True4950def _create_report_data(self):51keyboard = KeyboardData()52for key, value in self.keystates.items():53key = key.replace(" ", "").lower()54setattr(keyboard, key, value)55return keyboard5657def create_array_report(self, keys, reportID=None, application=None):58"""59Return an input report for this device.6061:param keys: a tuple of chars for the pressed keys. The class maintains62the list of currently pressed keys, so to release a key, the caller63needs to call again this function without the key in this tuple.64:param reportID: the numeric report ID for this report, if needed65"""66self._update_key_state(keys)67reportID = reportID or self.default_reportID6869keyboard = self._create_report_data()70return self.create_report(keyboard, reportID=reportID, application=application)7172def event(self, keys, reportID=None, application=None):73"""74Send an input event on the default report ID.7576:param keys: a tuple of chars for the pressed keys. The class maintains77the list of currently pressed keys, so to release a key, the caller78needs to call again this function without the key in this tuple.79"""80r = self.create_array_report(keys, reportID, application)81self.call_input_event(r)82return [r]838485class PlainKeyboard(BaseKeyboard):86# fmt: off87report_descriptor = [880x05, 0x01, # Usage Page (Generic Desktop)890x09, 0x06, # Usage (Keyboard)900xa1, 0x01, # Collection (Application)910x85, 0x01, # .Report ID (1)920x05, 0x07, # .Usage Page (Keyboard)930x19, 0xe0, # .Usage Minimum (224)940x29, 0xe7, # .Usage Maximum (231)950x15, 0x00, # .Logical Minimum (0)960x25, 0x01, # .Logical Maximum (1)970x75, 0x01, # .Report Size (1)980x95, 0x08, # .Report Count (8)990x81, 0x02, # .Input (Data,Var,Abs)1000x19, 0x00, # .Usage Minimum (0)1010x29, 0x97, # .Usage Maximum (151)1020x15, 0x00, # .Logical Minimum (0)1030x25, 0x01, # .Logical Maximum (1)1040x75, 0x01, # .Report Size (1)1050x95, 0x98, # .Report Count (152)1060x81, 0x02, # .Input (Data,Var,Abs)1070xc0, # End Collection108]109# fmt: on110111def __init__(self, rdesc=report_descriptor, name=None, input_info=None):112super().__init__(rdesc, name, input_info)113self.default_reportID = 1114115116class ArrayKeyboard(BaseKeyboard):117# fmt: off118report_descriptor = [1190x05, 0x01, # Usage Page (Generic Desktop)1200x09, 0x06, # Usage (Keyboard)1210xa1, 0x01, # Collection (Application)1220x05, 0x07, # .Usage Page (Keyboard)1230x19, 0xe0, # .Usage Minimum (224)1240x29, 0xe7, # .Usage Maximum (231)1250x15, 0x00, # .Logical Minimum (0)1260x25, 0x01, # .Logical Maximum (1)1270x75, 0x01, # .Report Size (1)1280x95, 0x08, # .Report Count (8)1290x81, 0x02, # .Input (Data,Var,Abs)1300x95, 0x06, # .Report Count (6)1310x75, 0x08, # .Report Size (8)1320x15, 0x00, # .Logical Minimum (0)1330x26, 0xa4, 0x00, # .Logical Maximum (164)1340x05, 0x07, # .Usage Page (Keyboard)1350x19, 0x00, # .Usage Minimum (0)1360x29, 0xa4, # .Usage Maximum (164)1370x81, 0x00, # .Input (Data,Arr,Abs)1380xc0, # End Collection139]140# fmt: on141142def __init__(self, rdesc=report_descriptor, name=None, input_info=None):143super().__init__(rdesc, name, input_info)144145def _create_report_data(self):146data = KeyboardData()147array = []148149hut = hidtools.hut.HUT150151# strip modifiers from the array152for k, v in self.keystates.items():153# we ignore depressed keys154if not v:155continue156157usage = hut[0x07].from_name[k].usage158if usage >= 224 and usage <= 231:159# modifier160setattr(data, k.lower(), 1)161else:162array.append(k)163164# if array length is bigger than 6, report ErrorRollOver165if len(array) > 6:166array = ["ErrorRollOver"] * 6167168data.keyboard = array169return data170171172class LEDKeyboard(ArrayKeyboard):173# fmt: off174report_descriptor = [1750x05, 0x01, # Usage Page (Generic Desktop)1760x09, 0x06, # Usage (Keyboard)1770xa1, 0x01, # Collection (Application)1780x05, 0x07, # .Usage Page (Keyboard)1790x19, 0xe0, # .Usage Minimum (224)1800x29, 0xe7, # .Usage Maximum (231)1810x15, 0x00, # .Logical Minimum (0)1820x25, 0x01, # .Logical Maximum (1)1830x75, 0x01, # .Report Size (1)1840x95, 0x08, # .Report Count (8)1850x81, 0x02, # .Input (Data,Var,Abs)1860x95, 0x01, # .Report Count (1)1870x75, 0x08, # .Report Size (8)1880x81, 0x01, # .Input (Cnst,Arr,Abs)1890x95, 0x05, # .Report Count (5)1900x75, 0x01, # .Report Size (1)1910x05, 0x08, # .Usage Page (LEDs)1920x19, 0x01, # .Usage Minimum (1)1930x29, 0x05, # .Usage Maximum (5)1940x91, 0x02, # .Output (Data,Var,Abs)1950x95, 0x01, # .Report Count (1)1960x75, 0x03, # .Report Size (3)1970x91, 0x01, # .Output (Cnst,Arr,Abs)1980x95, 0x06, # .Report Count (6)1990x75, 0x08, # .Report Size (8)2000x15, 0x00, # .Logical Minimum (0)2010x26, 0xa4, 0x00, # .Logical Maximum (164)2020x05, 0x07, # .Usage Page (Keyboard)2030x19, 0x00, # .Usage Minimum (0)2040x29, 0xa4, # .Usage Maximum (164)2050x81, 0x00, # .Input (Data,Arr,Abs)2060xc0, # End Collection207]208# fmt: on209210def __init__(self, rdesc=report_descriptor, name=None, input_info=None):211super().__init__(rdesc, name, input_info)212213214# Some Primax manufactured keyboards set the Usage Page after having defined215# some local Usages. It relies on the fact that the specification states that216# Usages are to be concatenated with Usage Pages upon finding a Main item (see217# 6.2.2.8). This test covers this case.218class PrimaxKeyboard(ArrayKeyboard):219# fmt: off220report_descriptor = [2210x05, 0x01, # Usage Page (Generic Desktop)2220x09, 0x06, # Usage (Keyboard)2230xA1, 0x01, # Collection (Application)2240x05, 0x07, # .Usage Page (Keyboard)2250x19, 0xE0, # .Usage Minimum (224)2260x29, 0xE7, # .Usage Maximum (231)2270x15, 0x00, # .Logical Minimum (0)2280x25, 0x01, # .Logical Maximum (1)2290x75, 0x01, # .Report Size (1)2300x95, 0x08, # .Report Count (8)2310x81, 0x02, # .Input (Data,Var,Abs)2320x75, 0x08, # .Report Size (8)2330x95, 0x01, # .Report Count (1)2340x81, 0x01, # .Input (Data,Var,Abs)2350x05, 0x08, # .Usage Page (LEDs)2360x19, 0x01, # .Usage Minimum (1)2370x29, 0x03, # .Usage Maximum (3)2380x75, 0x01, # .Report Size (1)2390x95, 0x03, # .Report Count (3)2400x91, 0x02, # .Output (Data,Var,Abs)2410x95, 0x01, # .Report Count (1)2420x75, 0x05, # .Report Size (5)2430x91, 0x01, # .Output (Constant)2440x15, 0x00, # .Logical Minimum (0)2450x26, 0xFF, 0x00, # .Logical Maximum (255)2460x19, 0x00, # .Usage Minimum (0)2470x2A, 0xFF, 0x00, # .Usage Maximum (255)2480x05, 0x07, # .Usage Page (Keyboard)2490x75, 0x08, # .Report Size (8)2500x95, 0x06, # .Report Count (6)2510x81, 0x00, # .Input (Data,Arr,Abs)2520xC0, # End Collection253]254# fmt: on255256def __init__(self, rdesc=report_descriptor, name=None, input_info=None):257super().__init__(rdesc, name, input_info)258259260class BaseTest:261class TestKeyboard(base.BaseTestCase.TestUhid):262def test_single_key(self):263"""check for key reliability."""264uhdev = self.uhdev265evdev = uhdev.get_evdev()266syn_event = self.syn_event267268r = uhdev.event(["a and A"])269expected = [syn_event]270expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1))271events = uhdev.next_sync_events()272self.debug_reports(r, uhdev, events)273self.assertInputEventsIn(expected, events)274assert evdev.value[libevdev.EV_KEY.KEY_A] == 1275276r = uhdev.event([])277expected = [syn_event]278expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0))279events = uhdev.next_sync_events()280self.debug_reports(r, uhdev, events)281self.assertInputEventsIn(expected, events)282assert evdev.value[libevdev.EV_KEY.KEY_A] == 0283284def test_two_keys(self):285uhdev = self.uhdev286evdev = uhdev.get_evdev()287syn_event = self.syn_event288289r = uhdev.event(["a and A", "q and Q"])290expected = [syn_event]291expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1))292expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 1))293events = uhdev.next_sync_events()294self.debug_reports(r, uhdev, events)295self.assertInputEventsIn(expected, events)296assert evdev.value[libevdev.EV_KEY.KEY_A] == 1297298r = uhdev.event([])299expected = [syn_event]300expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0))301expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 0))302events = uhdev.next_sync_events()303self.debug_reports(r, uhdev, events)304self.assertInputEventsIn(expected, events)305assert evdev.value[libevdev.EV_KEY.KEY_A] == 0306assert evdev.value[libevdev.EV_KEY.KEY_Q] == 0307308r = uhdev.event(["c and C"])309expected = [syn_event]310expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 1))311events = uhdev.next_sync_events()312self.debug_reports(r, uhdev, events)313self.assertInputEventsIn(expected, events)314assert evdev.value[libevdev.EV_KEY.KEY_C] == 1315316r = uhdev.event(["c and C", "Spacebar"])317expected = [syn_event]318expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 1))319events = uhdev.next_sync_events()320self.debug_reports(r, uhdev, events)321assert libevdev.InputEvent(libevdev.EV_KEY.KEY_C) not in events322self.assertInputEventsIn(expected, events)323assert evdev.value[libevdev.EV_KEY.KEY_C] == 1324assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1325326r = uhdev.event(["Spacebar"])327expected = [syn_event]328expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 0))329events = uhdev.next_sync_events()330self.debug_reports(r, uhdev, events)331assert libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE) not in events332self.assertInputEventsIn(expected, events)333assert evdev.value[libevdev.EV_KEY.KEY_C] == 0334assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1335336r = uhdev.event([])337expected = [syn_event]338expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 0))339events = uhdev.next_sync_events()340self.debug_reports(r, uhdev, events)341self.assertInputEventsIn(expected, events)342assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 0343344def test_modifiers(self):345# ctrl-alt-del would be very nice :)346uhdev = self.uhdev347syn_event = self.syn_event348349r = uhdev.event(["LeftControl", "LeftShift", "= and +"])350expected = [syn_event]351expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTCTRL, 1))352expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTSHIFT, 1))353expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_EQUAL, 1))354events = uhdev.next_sync_events()355self.debug_reports(r, uhdev, events)356self.assertInputEventsIn(expected, events)357358359class TestPlainKeyboard(BaseTest.TestKeyboard):360def create_device(self):361return PlainKeyboard()362363def test_10_keys(self):364uhdev = self.uhdev365syn_event = self.syn_event366367r = uhdev.event(368[369"1 and !",370"2 and @",371"3 and #",372"4 and $",373"5 and %",374"6 and ^",375"7 and &",376"8 and *",377"9 and (",378"0 and )",379]380)381expected = [syn_event]382expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 1))383expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1))384expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1))385expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1))386expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1))387expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1))388expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1))389expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 1))390expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 1))391expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 1))392events = uhdev.next_sync_events()393self.debug_reports(r, uhdev, events)394self.assertInputEventsIn(expected, events)395396r = uhdev.event([])397expected = [syn_event]398expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 0))399expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0))400expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0))401expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0))402expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0))403expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0))404expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0))405expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 0))406expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 0))407expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 0))408events = uhdev.next_sync_events()409self.debug_reports(r, uhdev, events)410self.assertInputEventsIn(expected, events)411412413class TestArrayKeyboard(BaseTest.TestKeyboard):414def create_device(self):415return ArrayKeyboard()416417def test_10_keys(self):418uhdev = self.uhdev419syn_event = self.syn_event420421r = uhdev.event(422[423"1 and !",424"2 and @",425"3 and #",426"4 and $",427"5 and %",428"6 and ^",429]430)431expected = [syn_event]432expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1))433expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1))434expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1))435expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1))436expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1))437expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1))438events = uhdev.next_sync_events()439440self.debug_reports(r, uhdev, events)441self.assertInputEventsIn(expected, events)442443# ErrRollOver444r = uhdev.event(445[446"1 and !",447"2 and @",448"3 and #",449"4 and $",450"5 and %",451"6 and ^",452"7 and &",453"8 and *",454"9 and (",455"0 and )",456]457)458events = uhdev.next_sync_events()459460self.debug_reports(r, uhdev, events)461462assert len(events) == 0463464r = uhdev.event([])465expected = [syn_event]466expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0))467expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0))468expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0))469expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0))470expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0))471expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0))472events = uhdev.next_sync_events()473self.debug_reports(r, uhdev, events)474self.assertInputEventsIn(expected, events)475476477class TestLEDKeyboard(BaseTest.TestKeyboard):478def create_device(self):479return LEDKeyboard()480481482class TestPrimaxKeyboard(BaseTest.TestKeyboard):483def create_device(self):484return PrimaxKeyboard()485486487