Home | History | Annotate | Download | only in platform_UdevVars
      1 # Copyright 2014 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import logging
      6 import os
      7 import pyudev
      8 import re
      9 
     10 from autotest_lib.client.bin import test, utils
     11 from autotest_lib.client.common_lib import error
     12 from collections import defaultdict
     13 from operator import attrgetter
     14 
     15 def natural_key(string_):
     16     """
     17     Derive key for natural sorting.
     18     @param string_: String to derive sort key for.
     19     From http://stackoverflow.com/questions/34518/natural-sorting-algorithm.
     20     """
     21     return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', string_)]
     22 
     23 
     24 class platform_UdevVars(test.test):
     25     """Verify ChromeOS-specific udev variables."""
     26     version = 1
     27 
     28 
     29     def _input_devices(self):
     30         """Obtain a list of all /dev/input/event* udev devices."""
     31         devices = self.udev.list_devices(subsystem='input')
     32         # only consider the event devices
     33         devices = filter(attrgetter('device_node'), devices)
     34         devices = sorted(devices, key=lambda device: natural_key(device.device_node))
     35         return devices
     36 
     37 
     38     def _get_roles(self):
     39         """Get information on input devices and roles from udev."""
     40         self.devices_with_role = defaultdict(list)
     41 
     42         logging.debug('Input devices:')
     43         for device in self._input_devices():
     44             name = device.parent.attributes.get('name', '')
     45             logging.debug('  %s [%s]', device.device_node, name)
     46             role = device.get('POWERD_ROLE', None)
     47             if role:
     48                 logging.debug('    POWERD_ROLE=%s', role)
     49                 self.devices_with_role[role].append(device)
     50 
     51 
     52     def _dump_roles(self):
     53         """Log devices grouped by role for easier debugging."""
     54         logging.info('Roles:')
     55         for role in sorted(self.devices_with_role.keys()):
     56             for device in self.devices_with_role[role]:
     57                 path = device.device_node
     58                 name = device.parent.attributes.get('name', '')
     59                 logging.info('  %-21s %s [%s]', role + ':', path, name)
     60 
     61 
     62     def _dump_udev_attrs(self):
     63         """Log udev attributes for selected devices to the debug directory."""
     64         for device in self._input_devices():
     65             devname = os.path.basename(device.device_node)
     66 
     67             outfile = os.path.join(self.debugdir, "udevattrs.%s" % devname)
     68             utils.system('udevadm info --attribute-walk --path=%s > %s' % (
     69                     device.sys_path, outfile))
     70 
     71             outfile = os.path.join(self.debugdir, "udevprops.%s" % devname)
     72             utils.system('udevadm info --query=property --path=%s > %s' % (
     73                     device.sys_path, outfile))
     74 
     75 
     76     def _verify_roles(self):
     77         """Verify that POWERD_ROLE was set on devices as expected."""
     78 
     79         # TODO(chromium:410968): Consider moving this to USE flags instead of
     80         # listing devices here.
     81         boards_with_touchscreen = ['link', 'samus']
     82         boards_maybe_touchscreen = ['rambi', 'peppy', 'glimmer', 'clapper',
     83                                     'nyan_big', 'nyan_blaze', 'expresso']
     84         boards_chromebox = ['beltino', 'guado', 'mccloud', 'panther', 'rikku', 
     85                             'stumpy', 'tidus', 'tricky', 'zako']
     86         boards_aio = ['nyan_kitty', 'tiny', 'anglar', 'monroe']
     87 
     88         expect_keyboard = None
     89         expect_touchpad = None
     90         expect_touchscreen = None
     91 
     92         board = utils.get_board()
     93         if board in boards_chromebox or board in boards_aio:
     94             expect_keyboard = [0]
     95             expect_touchpad = [0]
     96         else:
     97             expect_keyboard = [1]
     98             expect_touchpad = [1]
     99 
    100         if board in boards_with_touchscreen:
    101             expect_touchscreen = [1]
    102         elif board in boards_maybe_touchscreen:
    103             expect_touchscreen = [0, 1]
    104         else:
    105             expect_touchscreen = [0]
    106 
    107         expected_num_per_role = [
    108                 ('internal_keyboard', expect_keyboard),
    109                 ('internal_touchpad', expect_touchpad),
    110                 ('internal_touchscreen', expect_touchscreen),
    111             ]
    112 
    113         for role, expected_num in expected_num_per_role:
    114             num = len(self.devices_with_role[role])
    115             if num not in expected_num:
    116                 self.errors += 1
    117                 logging.error('POWERD_ROLE=%s is present %d times, expected '
    118                               'one of %s', role, num, repr(expected_num))
    119 
    120         if len(self.devices_with_role['external_input']) != 0:
    121             logging.warn('%d external input devices detected',
    122                          len(self.devices_with_role['external_input']))
    123 
    124 
    125     def initialize(self):
    126         self.udev = pyudev.Context()
    127 
    128 
    129     def run_once(self):
    130         """
    131         Check that udev variables are assigned correctly by udev rules. In
    132         particular, verifies that powerd tags are set correctly.
    133         """
    134         logging.debug('Board: %s', utils.get_board())
    135         self._get_roles()
    136         self._dump_roles()
    137         self._dump_udev_attrs()
    138 
    139         self.errors = 0
    140         self._verify_roles()
    141 
    142         if self.errors != 0:
    143             raise error.TestFail('Verification of udev variables failed; see '
    144                                  'logs for details')
    145