1# Copyright (c) 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 6import logging, os 7import math 8from autotest_lib.client.bin import utils, test 9from autotest_lib.client.common_lib import error 10 11 12class kernel_CrosECSysfsAccel(test.test): 13 '''Make sure the EC sysfs accel interface provides meaningful output''' 14 version = 1 15 16 17 # For EC accelerometer, define the number of counts in 1G, and the number 18 # of counts that the magnitude of each sensor is allowed to be off from a 19 # magnitude of 1G. These values are not sensor dependent, they are based 20 # on the EC sysfs interface, which specifies number of counts in 1G. 21 _ACCEL_1G_IN_G = 1024 22 _ACCEL_1G_IN_MS2 = 9.8185 23 _ACCEL_MAG_VALID_OFFSET = .25 24 25 _ACCEL_BASE_LOC = 'base' 26 _ACCEL_LID_LOC = 'lid' 27 _ACCEL_LOCS = [_ACCEL_BASE_LOC, _ACCEL_LID_LOC] 28 29 30 sysfs_accel_search_path = '/sys/bus/iio/devices' 31 sysfs_accel_paths = {} 32 sysfs_accel_old_path = '' 33 new_sysfs_layout = True 34 35 @classmethod 36 def _read_sysfs_accel_file(cls, fullpath): 37 """ 38 Read the contents of the given accel sysfs file or fail 39 40 @param fullpath Name of the file within the accel sysfs interface 41 directory 42 """ 43 try: 44 content = utils.read_file(fullpath) 45 except Exception as err: 46 raise error.TestFail('sysfs file problem: %s' % err) 47 return content 48 49 50 def _find_sysfs_accel_dir(self): 51 """ 52 Return the sysfs directory for accessing EC accels 53 """ 54 for _, dirs, _ in os.walk(self.sysfs_accel_search_path): 55 for d in dirs: 56 dirpath = os.path.join(self.sysfs_accel_search_path, d) 57 namepath = os.path.join(dirpath, 'name') 58 59 try: 60 content = utils.read_file(namepath) 61 except IOError as err: 62 # errno 2 is code for file does not exist, which is ok 63 # here, just continue on to next directory. Any other 64 # error is a problem, raise an error. 65 if err.errno == 2: 66 continue 67 raise error.TestFail('IOError %d while searching for accel' 68 'sysfs dir in %s', err.errno, namepath) 69 70 # Correct directory has a file called 'name' with contents 71 # 'cros-ec-accel' 72 if content.strip() != 'cros-ec-accel': 73 continue 74 75 locpath = os.path.join(dirpath, 'location') 76 try: 77 location = utils.read_file(locpath) 78 except IOError as err: 79 if err.errno == 2: 80 # We have an older scheme 81 self.new_sysfs_layout = False 82 self.sysfs_accel_old_path = dirpath 83 return 84 raise error.TestFail('IOError %d while reading %s', 85 err.errno, locpath) 86 loc = location.strip() 87 if loc in self._ACCEL_LOCS: 88 self.sysfs_accel_paths[loc] = dirpath 89 90 if (not self.sysfs_accel_old_path and 91 len(self.sysfs_accel_paths) == 0): 92 raise error.TestFail('No sysfs interface to EC accels (cros-ec-accel)') 93 94 def _verify_accel_data(self, name): 95 """ 96 Verify one of the EC accelerometers through the sysfs interface. 97 """ 98 if self.new_sysfs_layout: 99 accel_scale = float(self._read_sysfs_accel_file( 100 os.path.join(self.sysfs_accel_paths[name], 101 'scale'))) 102 exp = self._ACCEL_1G_IN_MS2 103 else: 104 accel_scale = 1 105 exp = self._ACCEL_1G_IN_G 106 107 err = exp * self._ACCEL_MAG_VALID_OFFSET 108 value = {} 109 mag = 0 110 for axis in ['x', 'y', 'z']: 111 name_list = ['in', 'accel', axis] 112 if self.new_sysfs_layout: 113 base_path = self.sysfs_accel_paths[name] 114 else: 115 base_path = self.sysfs_accel_old_path 116 name_list.append(name) 117 name_list.append('raw') 118 axis_path = os.path.join(base_path, '_'.join(name_list)) 119 value[axis] = int(self._read_sysfs_accel_file(axis_path)) 120 value[axis] *= accel_scale 121 mag += value[axis] * value[axis] 122 123 mag = math.sqrt(mag) 124 125 # Accel data is out of range if magnitude is not close to 1G. 126 # Note, this means test will fail on the moon. 127 if abs(mag - exp) <= err: 128 logging.info("%s accel passed. Magnitude is %f.", name, mag) 129 else: 130 logging.info("%s accel bad data. Magnitude is %f, expected " 131 "%f +/-%f. Raw data is x:%f, y:%f, z:%f.", name, 132 mag, exp, err, value['x'], value['y'], value['z']) 133 raise error.TestFail("Accel magnitude out of range.") 134 135 136 def run_once(self): 137 """ 138 Check for accelerometers, and if present, check data is valid 139 """ 140 # First make sure that the motion sensors are active. If this 141 # check fails it means the EC motion sense task is not running and 142 # therefore not updating acceleration values in shared memory. 143 # Note that this check only works for x86 boards. 144 arch = utils.get_arch() 145 if arch.startswith('x86'): 146 active = utils.system_output('ectool motionsense active') 147 if active == "0": 148 raise error.TestFail("Motion sensing is inactive") 149 150 # Find the iio sysfs directory for EC accels 151 self._find_sysfs_accel_dir() 152 153 if self.sysfs_accel_old_path: 154 # Get all accelerometer data 155 accel_info = utils.system_output('ectool motionsense') 156 info = accel_info.splitlines() 157 158 # If the base accelerometer is present, then verify data 159 if 'None' not in info[1]: 160 self._verify_accel_data(self._ACCEL_BASE_LOC) 161 162 # If the lid accelerometer is present, then verify data 163 if 'None' not in info[2]: 164 self._verify_accel_data(self._ACCEL_LID_LOC) 165 else: 166 for loc in self.sysfs_accel_paths.keys(): 167 self._verify_accel_data(loc) 168