blob: 77426c6aaefcdca2bb5dbace531e2bab6aa56ee3 [file] [log] [blame]
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import os
import re
from cros.factory.probe.lib import cached_probe_function
from cros.factory.test.utils import evdev_utils
from cros.factory.utils.arg_utils import Arg
from cros.factory.utils import type_utils
from cros.factory.external import evdev
INPUT_DEVICE_PATH = '/proc/bus/input/devices'
KNOWN_DEVICE_TYPES = type_utils.Enum(['touchscreen', 'touchpad', 'stylus'])
def GetInputDevices():
"""Returns all input devices connected to the machine."""
dataset = []
data = {}
entry = None
with open(INPUT_DEVICE_PATH) as f:
for line in f:
prefix = line[0]
content = line[3:].strip()
# Format: PREFIX: Key=Value
# I: Bus=HHHH Vendor=HHHH Product=HHHH Version=HHHH
# N: Name="XXXX"
# P: Phys=XXXX
# S: Sysfs=XXXX
if prefix == 'I':
if data:
dataset.append(data)
data = {}
for entry in content.split():
key, value = entry.split('=', 1)
data[key.lower()] = value
elif prefix in ['N', 'S']:
key, value = content.split('=', 1)
data[key.lower()] = value.strip('"')
elif prefix == 'H':
for handler in line[3:].split('=', 1)[1].split():
if re.match(r'event\d+', handler):
data['event'] = handler
break
# Flush output.
if data:
dataset.append(data)
return dataset
def GetDeviceType(device):
evdev_device = evdev.InputDevice(os.path.join('/dev/input', device['event']))
ret = 'unknown'
if evdev_utils.IsStylusDevice(evdev_device):
ret = KNOWN_DEVICE_TYPES.stylus
if evdev_utils.IsTouchpadDevice(evdev_device):
ret = KNOWN_DEVICE_TYPES.touchpad
if evdev_utils.IsTouchscreenDevice(evdev_device):
ret = KNOWN_DEVICE_TYPES.touchscreen
logging.debug('device %s type: %s', device['event'], ret)
return ret
class InputDeviceFunction(cached_probe_function.CachedProbeFunction):
"""Probes the information of input devices.
Description
-----------
This function gets information of all input devices connected to the machine
by parsing the file ``/proc/bus/input/devices``, and then filters the results
by the given arguments.
The probed result for one input device is a dictionary which contains
following fields:
- ``product``: The product code in a string of 16-bits hex number.
- ``vendor``: The vendor code in a string of 16-bits hex number.
- ``version``: The version number in a string of 16-bits hex number.
- ``name``: The name of the device.
- ``bus``: The bus number in a string of 16-bits hex number.
- ``sysfs``: The pathname of the sysfs entry of that device.
- ``event``
Because values in ``/proc/bus/input/devices`` are exported by the driver of
each input device, an input device can not be probed correctly by this
function if its driver doesn't export correct values.
Examples
--------
Without specifying the device type, the probe statement ::
{
"eval": "input_device"
}
will have the corresponding probed results like ::
[
{
"product": "3043",
"version": "0100",
"vendor": "2345",
"name": "Google Inc. XXYY",
"bus": "0003",
"sysfs": "/devices/pci0000:00/0000:00:34.0/usb3/......",
"event": "event3"
},
{
"product": "3044",
"version": "0001",
"vendor": "2347",
"name": "elgooG Inc. AABB",
"bus": "0002",
"sysfs": "/devices/pci0000:00/0000:00:32.0/usb3/......",
"event": "event1"
},
...
]
To strict the probe results to ``touchscreen`` type, the probe statement
is::
{
"eval": "input_device:touchscreen"
}
"""
ARGS = [
Arg('device_type', KNOWN_DEVICE_TYPES, 'The type of input device.',
default=None)
]
def GetCategoryFromArgs(self):
if (self.args.device_type is not None and
self.args.device_type not in KNOWN_DEVICE_TYPES):
raise cached_probe_function.InvalidCategoryError(
'The type of input device must be one of %r' % KNOWN_DEVICE_TYPES)
return self.args.device_type
@classmethod
def ProbeAllDevices(cls):
ret = {}
for dev in GetInputDevices():
ret.setdefault(GetDeviceType(dev), []).append(dev)
return ret