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 5import logging 6import os 7import pyudev 8import re 9 10from autotest_lib.client.bin import test, utils 11from autotest_lib.client.common_lib import error 12from collections import defaultdict 13from operator import attrgetter 14 15def 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 24class 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