Index: chromeos/accelerometer/accelerometer_reader.cc |
diff --git a/chromeos/accelerometer/accelerometer_reader.cc b/chromeos/accelerometer/accelerometer_reader.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f33514cca47ee2d2a01ff6277500ecb879fe08b0 |
--- /dev/null |
+++ b/chromeos/accelerometer/accelerometer_reader.cc |
@@ -0,0 +1,203 @@ |
+// 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 "base/bind.h" |
+#include "base/file_util.h" |
+#include "base/location.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_util.h" |
+#include "base/threading/sequenced_worker_pool.h" |
+#include "content/public/browser/browser_thread.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/"); |
+ |
+// Files within the device in kAccelerometerIioBasePath containing the scales of |
+// the accelerometers. |
+const base::FilePath::CharType kAccelerometerBaseScaleName[] = |
+ FILE_PATH_LITERAL("in_accel_base_scale"); |
+const base::FilePath::CharType kAccelerometerLidScaleName[] = |
+ FILE_PATH_LITERAL("in_accel_lid_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 and axes in the order we want to read them. |
+const char kAccelerometerNames[][5] = {"base", "lid"}; |
+const char kAccelerometerAxes[][2] = {"x", "y", "z"}; |
+const size_t kTriggerDataValues = |
+ arraysize(kAccelerometerNames) * arraysize(kAccelerometerAxes); |
+const size_t kTriggerDataLength = kTriggerDataValues * 2; |
+ |
+// The length required to read uint values from configuration files. |
+const size_t kMaxAsciiUintLength = 21; |
+ |
+// The time to wait between reading the accelerometer. |
+const int kDelayBetweenReadsMs = 100; |
+ |
+// Reads |path| to the unsigned int pointed to by |value|. Returns true on |
+// success or false on failure. |
+bool ReadFileToUint(const base::FilePath& path, unsigned int* value) { |
+ std::string s; |
+ DCHECK(value); |
+ if (!base::ReadFileToString(path, &s, kMaxAsciiUintLength)) { |
+ LOG(ERROR) << "Failed to read " << path.value(); |
+ return false; |
+ } |
+ base::TrimWhitespaceASCII(s, base::TRIM_ALL, &s); |
+ if (!base::StringToUint(s, value)) { |
+ LOG(ERROR) << "Failed to parse \"" << s << "\" from " << path.value(); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+using content::BrowserThread; |
+ |
+AccelerometerReader::AccelerometerReader( |
+ AccelerometerReader::Delegate* delegate) |
+ : delegate_(delegate), |
+ has_accelerometer_(false), |
+ weak_factory_(this) { |
+ // Asynchronously detect and initialize the accelerometer to avoid delaying |
+ // startup. |
+ BrowserThread::PostBlockingPoolTaskAndReply(FROM_HERE, |
+ base::Bind(&AccelerometerReader::Initialize, weak_factory_.GetWeakPtr()), |
+ base::Bind(&AccelerometerReader::OnInitialized, |
+ weak_factory_.GetWeakPtr())); |
+} |
+ |
+AccelerometerReader::~AccelerometerReader() { |
+} |
+ |
+void AccelerometerReader::Initialize() { |
+ DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
+ |
+ // 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; |
+ } |
+ |
+ if (!base::PathExists(base::FilePath(kAccelerometerTriggerPath))) { |
+ LOG(ERROR) << "Accelerometer trigger does not exist at" |
+ << kAccelerometerTriggerPath; |
+ return; |
+ } |
+ |
+ base::FilePath iio_path(base::FilePath(kAccelerometerIioBasePath).Append( |
+ device)); |
+ // Read accelerometer scales |
+ if (!ReadFileToUint(iio_path.Append(kAccelerometerBaseScaleName), |
+ &accelerometer_base_scale_)) { |
+ return; |
+ } |
+ if (!ReadFileToUint(iio_path.Append(kAccelerometerLidScaleName), |
+ &accelerometer_lid_scale_)) { |
+ return; |
+ } |
+ |
+ // Read indices of each accelerometer axis from each accelerometer from |
+ // /sys/bus/iio/devices/iio:deviceX/scan_elements/in_accel_{x,y,z}_%s_index |
+ for (size_t i = 0; i < arraysize(kAccelerometerNames); ++i) { |
+ for (size_t j = 0; j < arraysize(kAccelerometerAxes); ++j) { |
+ std::string accelerometer_index_path = base::StringPrintf( |
+ kAccelerometerScanIndexPath, kAccelerometerAxes[j], |
+ kAccelerometerNames[i]); |
+ unsigned int index = 0; |
+ if (!ReadFileToUint(iio_path.Append(accelerometer_index_path.c_str()), |
+ &index)) { |
+ return; |
+ } |
+ CHECK(index < kTriggerDataValues); |
Daniel Erat
2014/03/27 15:37:52
CHECK_LT() is better (as it'll log both values), b
flackr
2014/03/27 16:14:56
Done. Decided to go with bailing out.
|
+ accelerometer_index_.push_back(index); |
+ } |
+ } |
+ has_accelerometer_ = true; |
+} |
+ |
+void AccelerometerReader::OnInitialized() { |
+ if (has_accelerometer_) |
+ TriggerRead(); |
+} |
+ |
+void AccelerometerReader::TriggerRead() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ read_success_ = false; |
+ BrowserThread::PostBlockingPoolTaskAndReply(FROM_HERE, |
+ base::Bind(&AccelerometerReader::ReadAccelerometer, |
+ weak_factory_.GetWeakPtr()), |
+ base::Bind(&AccelerometerReader::OnDataRead, |
+ weak_factory_.GetWeakPtr())); |
+} |
+ |
+void AccelerometerReader::ReadAccelerometer() { |
+ DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
+ |
+ // 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; |
+ } |
+ |
+ // Read resulting sample from /dev/cros-ec-accel. |
+ char data[kTriggerDataLength]; |
+ int bytes_read = base::ReadFile(base::FilePath(kAccelerometerDevicePath), |
+ data, kTriggerDataLength); |
+ if (bytes_read < static_cast<int>(kTriggerDataLength)) { |
+ LOG(ERROR) << "Read " << bytes_read << " byte(s), expected " |
+ << kTriggerDataLength << " bytes from accelerometer"; |
+ return; |
+ } |
+ |
+ int16* values = reinterpret_cast<int16*>(data); |
+ base_reading_.set_x(values[accelerometer_index_[0]]); |
+ base_reading_.set_y(values[accelerometer_index_[1]]); |
+ base_reading_.set_z(values[accelerometer_index_[2]]); |
+ base_reading_.Scale(1.0f / accelerometer_base_scale_); |
+ |
+ lid_reading_.set_x(values[accelerometer_index_[3]]); |
+ lid_reading_.set_y(values[accelerometer_index_[4]]); |
+ lid_reading_.set_z(values[accelerometer_index_[5]]); |
+ lid_reading_.Scale(1.0f / accelerometer_lid_scale_); |
+ read_success_ = true; |
+} |
+ |
+void AccelerometerReader::OnDataRead() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ // If successful, notify the delegate. |
+ if (read_success_) |
+ delegate_->HandleAccelerometerReading(base_reading_, lid_reading_); |
+ |
+ // 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 |