// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chromeos/accelerometer/accelerometer_reader.h" #include #include "base/bind.h" #include "base/files/file_util.h" #include "base/location.h" #include "base/memory/singleton.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/task_runner.h" #include "base/task_runner_util.h" #include "base/threading/sequenced_worker_pool.h" namespace chromeos { namespace { // Paths to access necessary data from the accelerometer device. const base::FilePath::CharType kAccelerometerTriggerPath[] = FILE_PATH_LITERAL("/sys/bus/iio/devices/trigger0/trigger_now"); const base::FilePath::CharType kAccelerometerDevicePath[] = FILE_PATH_LITERAL("/dev/cros-ec-accel"); const base::FilePath::CharType kAccelerometerIioBasePath[] = FILE_PATH_LITERAL("/sys/bus/iio/devices/"); // File within the device in kAccelerometerIioBasePath containing the scale of // the accelerometers. const base::FilePath::CharType kScaleNameFormatString[] = "in_accel_%s_scale"; // The filename giving the path to read the scan index of each accelerometer // axis. const char kAccelerometerScanIndexPath[] = "scan_elements/in_accel_%s_%s_index"; // The names of the accelerometers. Matches up with the enum AccelerometerSource // in chromeos/accelerometer/accelerometer_types.h. const char kAccelerometerNames[ACCELEROMETER_SOURCE_COUNT][5] = {"lid", "base"}; // The axes on each accelerometer. const char kAccelerometerAxes[][2] = {"y", "x", "z"}; // The length required to read uint values from configuration files. const size_t kMaxAsciiUintLength = 21; // The size of individual values. const size_t kDataSize = 2; // The mean acceleration due to gravity on Earth in m/s^2. const float kMeanGravity = 9.80665f; // Reads |path| to the unsigned int pointed to by |value|. Returns true on // success or false on failure. bool ReadFileToInt(const base::FilePath& path, int* value) { std::string s; DCHECK(value); if (!base::ReadFileToString(path, &s, kMaxAsciiUintLength)) { return false; } base::TrimWhitespaceASCII(s, base::TRIM_ALL, &s); if (!base::StringToInt(s, value)) { LOG(ERROR) << "Failed to parse \"" << s << "\" from " << path.value(); return false; } return true; } bool DetectAndReadAccelerometerConfiguration( scoped_refptr configuration) { // Check for accelerometer symlink which will be created by the udev rules // file on detecting the device. base::FilePath device; if (!base::ReadSymbolicLink(base::FilePath(kAccelerometerDevicePath), &device)) { return false; } if (!base::PathExists(base::FilePath(kAccelerometerTriggerPath))) { LOG(ERROR) << "Accelerometer trigger does not exist at" << kAccelerometerTriggerPath; return false; } base::FilePath iio_path(base::FilePath(kAccelerometerIioBasePath).Append( device)); // Read configuration of each accelerometer axis from each accelerometer from // /sys/bus/iio/devices/iio:deviceX/. for (size_t i = 0; i < arraysize(kAccelerometerNames); ++i) { // Read scale of accelerometer. std::string accelerometer_scale_path = base::StringPrintf( kScaleNameFormatString, kAccelerometerNames[i]); int scale_divisor; if (!ReadFileToInt(iio_path.Append(accelerometer_scale_path.c_str()), &scale_divisor)) { configuration->data.has[i] = false; continue; } configuration->data.has[i] = true; configuration->data.count++; for (size_t j = 0; j < arraysize(kAccelerometerAxes); ++j) { configuration->data.scale[i][j] = kMeanGravity / scale_divisor; std::string accelerometer_index_path = base::StringPrintf( kAccelerometerScanIndexPath, kAccelerometerAxes[j], kAccelerometerNames[i]); if (!ReadFileToInt(iio_path.Append(accelerometer_index_path.c_str()), &(configuration->data.index[i][j]))) { return false; } } } // Adjust the directions of accelerometers to match the AccelerometerUpdate // type specified in chromeos/accelerometer/accelerometer_types.h. configuration->data.scale[ACCELEROMETER_SOURCE_SCREEN][0] *= -1.0f; for (int i = 0; i < 3; ++i) { configuration->data.scale[ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD][i] *= -1.0f; } // Verify indices are within bounds. for (int i = 0; i < ACCELEROMETER_SOURCE_COUNT; ++i) { if (!configuration->data.has[i]) continue; for (int j = 0; j < 3; ++j) { if (configuration->data.index[i][j] < 0 || configuration->data.index[i][j] >= 3 * static_cast(configuration->data.count)) { LOG(ERROR) << "Field index for " << kAccelerometerNames[i] << " " << kAccelerometerAxes[j] << " axis out of bounds."; return false; } } } configuration->data.length = kDataSize * 3 * configuration->data.count; return true; } bool ReadAccelerometer( scoped_refptr reading, size_t length) { // Initiate the trigger to read accelerometers simultaneously int bytes_written = base::WriteFile( base::FilePath(kAccelerometerTriggerPath), "1\n", 2); if (bytes_written < 2) { PLOG(ERROR) << "Accelerometer trigger failure: " << bytes_written; return false; } // Read resulting sample from /dev/cros-ec-accel. int bytes_read = base::ReadFile(base::FilePath(kAccelerometerDevicePath), reading->data, length); if (bytes_read < static_cast(length)) { LOG(ERROR) << "Read " << bytes_read << " byte(s), expected " << length << " bytes from accelerometer"; return false; } return true; } } // namespace const int AccelerometerReader::kDelayBetweenReadsMs = 100; AccelerometerReader::ConfigurationData::ConfigurationData() : count(0) { for (int i = 0; i < ACCELEROMETER_SOURCE_COUNT; ++i) { has[i] = false; for (int j = 0; j < 3; ++j) { scale[i][j] = 0; index[i][j] = -1; } } } AccelerometerReader::ConfigurationData::~ConfigurationData() { } // static AccelerometerReader* AccelerometerReader::GetInstance() { return Singleton::get(); } void AccelerometerReader::Initialize( scoped_refptr blocking_task_runner) { DCHECK(blocking_task_runner.get()); task_runner_ = blocking_task_runner; // Asynchronously detect and initialize the accelerometer to avoid delaying // startup. base::PostTaskAndReplyWithResult( task_runner_.get(), FROM_HERE, base::Bind(&DetectAndReadAccelerometerConfiguration, configuration_), base::Bind(&AccelerometerReader::OnInitialized, weak_factory_.GetWeakPtr(), configuration_)); } void AccelerometerReader::AddObserver(Observer* observer) { observers_->AddObserver(observer); if (update_) observer->OnAccelerometerUpdated(update_.get()); } void AccelerometerReader::RemoveObserver(Observer* observer) { observers_->RemoveObserver(observer); } AccelerometerReader::AccelerometerReader() : configuration_(new AccelerometerReader::Configuration()), observers_(new ObserverListThreadSafe()), weak_factory_(this) { } AccelerometerReader::~AccelerometerReader() { } void AccelerometerReader::OnInitialized( scoped_refptr configuration, bool success) { if (success) TriggerRead(); } void AccelerometerReader::TriggerRead() { DCHECK(!task_runner_->RunsTasksOnCurrentThread()); scoped_refptr reading( new AccelerometerReader::Reading()); base::PostTaskAndReplyWithResult(task_runner_.get(), FROM_HERE, base::Bind(&ReadAccelerometer, reading, configuration_->data.length), base::Bind(&AccelerometerReader::OnDataRead, weak_factory_.GetWeakPtr(), reading)); } void AccelerometerReader::OnDataRead( scoped_refptr reading, bool success) { DCHECK(!task_runner_->RunsTasksOnCurrentThread()); if (success) { update_ = new AccelerometerUpdate(); for (int i = 0; i < ACCELEROMETER_SOURCE_COUNT; ++i) { if (!configuration_->data.has[i]) continue; int16* values = reinterpret_cast(reading->data); update_->Set(static_cast(i), values[configuration_->data.index[i][0]] * configuration_->data.scale[i][0], values[configuration_->data.index[i][1]] * configuration_->data.scale[i][1], values[configuration_->data.index[i][2]] * configuration_->data.scale[i][2]); } // TODO(jonross): move this to the blocking thread (crbug.com/461433) observers_->Notify(FROM_HERE, &Observer::OnAccelerometerUpdated, update_); } // Trigger another read after the current sampling delay. base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&AccelerometerReader::TriggerRead, weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(kDelayBetweenReadsMs)); } } // namespace chromeos