| # Copyright (c) 2014 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, os |
| import math |
| from autotest_lib.client.bin import utils, test |
| from autotest_lib.client.common_lib import error |
| |
| |
| class kernel_CrosECSysfsAccel(test.test): |
| '''Make sure the EC sysfs accel interface provides meaningful output''' |
| version = 1 |
| |
| |
| # For EC accelerometer, define the number of counts in 1G, and the number |
| # of counts that the magnitude of each sensor is allowed to be off from a |
| # magnitude of 1G. These values are not sensor dependent, they are based |
| # on the EC sysfs interface, which specifies number of counts in 1G. |
| _ACCEL_1G_IN_G = 1024 |
| _ACCEL_1G_IN_MS2 = 9.8185 |
| _ACCEL_MAG_VALID_OFFSET = .25 |
| |
| _ACCEL_BASE_LOC = 'base' |
| _ACCEL_LID_LOC = 'lid' |
| _ACCEL_LOCS = [_ACCEL_BASE_LOC, _ACCEL_LID_LOC] |
| |
| |
| sysfs_accel_search_path = '/sys/bus/iio/devices' |
| sysfs_accel_paths = {} |
| sysfs_accel_old_path = '' |
| new_sysfs_layout = True |
| |
| @classmethod |
| def _read_sysfs_accel_file(cls, fullpath): |
| """ |
| Read the contents of the given accel sysfs file or fail |
| |
| @param fullpath Name of the file within the accel sysfs interface |
| directory |
| """ |
| try: |
| content = utils.read_file(fullpath) |
| except Exception as err: |
| raise error.TestFail('sysfs file problem: %s' % err) |
| return content |
| |
| |
| def _find_sysfs_accel_dir(self): |
| """ |
| Return the sysfs directory for accessing EC accels |
| """ |
| for _, dirs, _ in os.walk(self.sysfs_accel_search_path): |
| for d in dirs: |
| dirpath = os.path.join(self.sysfs_accel_search_path, d) |
| namepath = os.path.join(dirpath, 'name') |
| |
| try: |
| content = utils.read_file(namepath) |
| except IOError as err: |
| # errno 2 is code for file does not exist, which is ok |
| # here, just continue on to next directory. Any other |
| # error is a problem, raise an error. |
| if err.errno == 2: |
| continue |
| raise error.TestFail('IOError %d while searching for accel' |
| 'sysfs dir in %s', err.errno, namepath) |
| |
| # Correct directory has a file called 'name' with contents |
| # 'cros-ec-accel' |
| if content.strip() != 'cros-ec-accel': |
| continue |
| |
| locpath = os.path.join(dirpath, 'location') |
| try: |
| location = utils.read_file(locpath) |
| except IOError as err: |
| if err.errno == 2: |
| # We have an older scheme |
| self.new_sysfs_layout = False |
| self.sysfs_accel_old_path = dirpath |
| return |
| raise error.TestFail('IOError %d while reading %s', |
| err.errno, locpath) |
| loc = location.strip() |
| if loc in self._ACCEL_LOCS: |
| self.sysfs_accel_paths[loc] = dirpath |
| |
| if (not self.sysfs_accel_old_path and |
| len(self.sysfs_accel_paths) == 0): |
| raise error.TestFail('No sysfs interface to EC accels (cros-ec-accel)') |
| |
| def _verify_accel_data(self, name): |
| """ |
| Verify one of the EC accelerometers through the sysfs interface. |
| """ |
| if self.new_sysfs_layout: |
| accel_scale = float(self._read_sysfs_accel_file( |
| os.path.join(self.sysfs_accel_paths[name], |
| 'scale'))) |
| exp = self._ACCEL_1G_IN_MS2 |
| else: |
| accel_scale = 1 |
| exp = self._ACCEL_1G_IN_G |
| |
| err = exp * self._ACCEL_MAG_VALID_OFFSET |
| value = {} |
| mag = 0 |
| for axis in ['x', 'y', 'z']: |
| name_list = ['in', 'accel', axis] |
| if self.new_sysfs_layout: |
| base_path = self.sysfs_accel_paths[name] |
| else: |
| base_path = self.sysfs_accel_old_path |
| name_list.append(name) |
| name_list.append('raw') |
| axis_path = os.path.join(base_path, '_'.join(name_list)) |
| value[axis] = int(self._read_sysfs_accel_file(axis_path)) |
| value[axis] *= accel_scale |
| mag += value[axis] * value[axis] |
| |
| mag = math.sqrt(mag) |
| |
| # Accel data is out of range if magnitude is not close to 1G. |
| # Note, this means test will fail on the moon. |
| if abs(mag - exp) <= err: |
| logging.info("%s accel passed. Magnitude is %f.", name, mag) |
| else: |
| logging.info("%s accel bad data. Magnitude is %f, expected " |
| "%f +/-%f. Raw data is x:%f, y:%f, z:%f.", name, |
| mag, exp, err, value['x'], value['y'], value['z']) |
| raise error.TestFail("Accel magnitude out of range.") |
| |
| |
| def run_once(self): |
| """ |
| Check for accelerometers, and if present, check data is valid |
| """ |
| # First make sure that the motion sensors are active. If this |
| # check fails it means the EC motion sense task is not running and |
| # therefore not updating acceleration values in shared memory. |
| # Note that this check only works for x86 boards. |
| arch = utils.get_arch() |
| if arch.startswith('x86'): |
| active = utils.system_output('ectool motionsense active') |
| if active == "0": |
| raise error.TestFail("Motion sensing is inactive") |
| |
| # Find the iio sysfs directory for EC accels |
| self._find_sysfs_accel_dir() |
| |
| if self.sysfs_accel_old_path: |
| # Get all accelerometer data |
| accel_info = utils.system_output('ectool motionsense') |
| info = accel_info.splitlines() |
| |
| # If the base accelerometer is present, then verify data |
| if 'None' not in info[1]: |
| self._verify_accel_data(self._ACCEL_BASE_LOC) |
| |
| # If the lid accelerometer is present, then verify data |
| if 'None' not in info[2]: |
| self._verify_accel_data(self._ACCEL_LID_LOC) |
| else: |
| for loc in self.sysfs_accel_paths.keys(): |
| self._verify_accel_data(loc) |