1 // Copyright 2014 The Chromium 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 #include "chromeos/accelerometer/accelerometer_reader.h"
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/location.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/task_runner.h"
15 #include "base/task_runner_util.h"
16 #include "base/threading/sequenced_worker_pool.h"
17
18 namespace chromeos {
19
20 namespace {
21
22 // Paths to access necessary data from the accelerometer device.
23 const base::FilePath::CharType kAccelerometerTriggerPath[] =
24 FILE_PATH_LITERAL("/sys/bus/iio/devices/trigger0/trigger_now");
25 const base::FilePath::CharType kAccelerometerDevicePath[] =
26 FILE_PATH_LITERAL("/dev/cros-ec-accel");
27 const base::FilePath::CharType kAccelerometerIioBasePath[] =
28 FILE_PATH_LITERAL("/sys/bus/iio/devices/");
29
30 // Files within the device in kAccelerometerIioBasePath containing the scales of
31 // the accelerometers.
32 const base::FilePath::CharType kAccelerometerBaseScaleName[] =
33 FILE_PATH_LITERAL("in_accel_base_scale");
34 const base::FilePath::CharType kAccelerometerLidScaleName[] =
35 FILE_PATH_LITERAL("in_accel_lid_scale");
36
37 // The filename giving the path to read the scan index of each accelerometer
38 // axis.
39 const char kAccelerometerScanIndexPath[] =
40 "scan_elements/in_accel_%s_%s_index";
41
42 // The names of the accelerometers and axes in the order we want to read them.
43 const char kAccelerometerNames[][5] = {"base", "lid"};
44 const char kAccelerometerAxes[][2] = {"x", "y", "z"};
45 const size_t kTriggerDataValues =
46 arraysize(kAccelerometerNames) * arraysize(kAccelerometerAxes);
47 const size_t kTriggerDataLength = kTriggerDataValues * 2;
48
49 // The length required to read uint values from configuration files.
50 const size_t kMaxAsciiUintLength = 21;
51
52 // The time to wait between reading the accelerometer.
53 const int kDelayBetweenReadsMs = 100;
54
55 // Reads |path| to the unsigned int pointed to by |value|. Returns true on
56 // success or false on failure.
ReadFileToUint(const base::FilePath & path,unsigned int * value)57 bool ReadFileToUint(const base::FilePath& path, unsigned int* value) {
58 std::string s;
59 DCHECK(value);
60 if (!base::ReadFileToString(path, &s, kMaxAsciiUintLength)) {
61 LOG(ERROR) << "Failed to read " << path.value();
62 return false;
63 }
64 base::TrimWhitespaceASCII(s, base::TRIM_ALL, &s);
65 if (!base::StringToUint(s, value)) {
66 LOG(ERROR) << "Failed to parse \"" << s << "\" from " << path.value();
67 return false;
68 }
69 return true;
70 }
71
DetectAndReadAccelerometerConfiguration(scoped_refptr<AccelerometerReader::Configuration> configuration)72 bool DetectAndReadAccelerometerConfiguration(
73 scoped_refptr<AccelerometerReader::Configuration> configuration) {
74 // Check for accelerometer symlink which will be created by the udev rules
75 // file on detecting the device.
76 base::FilePath device;
77 if (!base::ReadSymbolicLink(base::FilePath(kAccelerometerDevicePath),
78 &device)) {
79 return false;
80 }
81
82 if (!base::PathExists(base::FilePath(kAccelerometerTriggerPath))) {
83 LOG(ERROR) << "Accelerometer trigger does not exist at"
84 << kAccelerometerTriggerPath;
85 return false;
86 }
87
88 base::FilePath iio_path(base::FilePath(kAccelerometerIioBasePath).Append(
89 device));
90 // Read accelerometer scales
91 if (!ReadFileToUint(iio_path.Append(kAccelerometerBaseScaleName),
92 &(configuration->data.base_scale))) {
93 return false;
94 }
95 if (!ReadFileToUint(iio_path.Append(kAccelerometerLidScaleName),
96 &(configuration->data.lid_scale))) {
97 return false;
98 }
99
100 // Read indices of each accelerometer axis from each accelerometer from
101 // /sys/bus/iio/devices/iio:deviceX/scan_elements/in_accel_{x,y,z}_%s_index
102 for (size_t i = 0; i < arraysize(kAccelerometerNames); ++i) {
103 for (size_t j = 0; j < arraysize(kAccelerometerAxes); ++j) {
104 std::string accelerometer_index_path = base::StringPrintf(
105 kAccelerometerScanIndexPath, kAccelerometerAxes[j],
106 kAccelerometerNames[i]);
107 unsigned int index = 0;
108 if (!ReadFileToUint(iio_path.Append(accelerometer_index_path.c_str()),
109 &index)) {
110 return false;
111 }
112 if (index >= kTriggerDataValues) {
113 LOG(ERROR) << "Field index from " << accelerometer_index_path
114 << " out of bounds: " << index;
115 return false;
116 }
117 configuration->data.index.push_back(index);
118 }
119 }
120 return true;
121 }
122
ReadAccelerometer(scoped_refptr<AccelerometerReader::Reading> reading)123 bool ReadAccelerometer(
124 scoped_refptr<AccelerometerReader::Reading> reading) {
125 // Initiate the trigger to read accelerometers simultaneously
126 int bytes_written = base::WriteFile(
127 base::FilePath(kAccelerometerTriggerPath), "1\n", 2);
128 if (bytes_written < 2) {
129 PLOG(ERROR) << "Accelerometer trigger failure: " << bytes_written;
130 return false;
131 }
132
133 // Read resulting sample from /dev/cros-ec-accel.
134 int bytes_read = base::ReadFile(base::FilePath(kAccelerometerDevicePath),
135 reading->data, kTriggerDataLength);
136 if (bytes_read < static_cast<int>(kTriggerDataLength)) {
137 LOG(ERROR) << "Read " << bytes_read << " byte(s), expected "
138 << kTriggerDataLength << " bytes from accelerometer";
139 return false;
140 }
141 return true;
142 }
143
144 } // namespace
145
ConfigurationData()146 AccelerometerReader::ConfigurationData::ConfigurationData() {
147 }
148
~ConfigurationData()149 AccelerometerReader::ConfigurationData::~ConfigurationData() {
150 }
151
AccelerometerReader(base::TaskRunner * task_runner,AccelerometerReader::Delegate * delegate)152 AccelerometerReader::AccelerometerReader(
153 base::TaskRunner* task_runner,
154 AccelerometerReader::Delegate* delegate)
155 : task_runner_(task_runner),
156 delegate_(delegate),
157 configuration_(new AccelerometerReader::Configuration()),
158 weak_factory_(this) {
159 DCHECK(task_runner_);
160 DCHECK(delegate_);
161 // Asynchronously detect and initialize the accelerometer to avoid delaying
162 // startup.
163 base::PostTaskAndReplyWithResult(task_runner_.get(), FROM_HERE,
164 base::Bind(&DetectAndReadAccelerometerConfiguration, configuration_),
165 base::Bind(&AccelerometerReader::OnInitialized,
166 weak_factory_.GetWeakPtr(), configuration_));
167 }
168
~AccelerometerReader()169 AccelerometerReader::~AccelerometerReader() {
170 }
171
OnInitialized(scoped_refptr<AccelerometerReader::Configuration> configuration,bool success)172 void AccelerometerReader::OnInitialized(
173 scoped_refptr<AccelerometerReader::Configuration> configuration,
174 bool success) {
175 if (success)
176 TriggerRead();
177 }
178
TriggerRead()179 void AccelerometerReader::TriggerRead() {
180 DCHECK(!task_runner_->RunsTasksOnCurrentThread());
181
182 scoped_refptr<AccelerometerReader::Reading> reading(
183 new AccelerometerReader::Reading());
184 base::PostTaskAndReplyWithResult(task_runner_, FROM_HERE,
185 base::Bind(&ReadAccelerometer, reading),
186 base::Bind(&AccelerometerReader::OnDataRead,
187 weak_factory_.GetWeakPtr(), reading));
188 }
189
OnDataRead(scoped_refptr<AccelerometerReader::Reading> reading,bool success)190 void AccelerometerReader::OnDataRead(
191 scoped_refptr<AccelerometerReader::Reading> reading,
192 bool success) {
193 DCHECK(!task_runner_->RunsTasksOnCurrentThread());
194
195 if (success) {
196 gfx::Vector3dF base_reading, lid_reading;
197 int16* values = reinterpret_cast<int16*>(reading->data);
198 base_reading.set_x(values[configuration_->data.index[0]]);
199 base_reading.set_y(values[configuration_->data.index[1]]);
200 base_reading.set_z(values[configuration_->data.index[2]]);
201 base_reading.Scale(1.0f / configuration_->data.base_scale);
202
203 lid_reading.set_x(values[configuration_->data.index[3]]);
204 lid_reading.set_y(values[configuration_->data.index[4]]);
205 lid_reading.set_z(values[configuration_->data.index[5]]);
206 lid_reading.Scale(1.0f / configuration_->data.lid_scale);
207 delegate_->HandleAccelerometerReading(base_reading, lid_reading);
208 }
209
210 // Trigger another read after the current sampling delay.
211 base::MessageLoop::current()->PostDelayedTask(
212 FROM_HERE,
213 base::Bind(&AccelerometerReader::TriggerRead,
214 weak_factory_.GetWeakPtr()),
215 base::TimeDelta::FromMilliseconds(kDelayBetweenReadsMs));
216 }
217
218 } // namespace chromeos
219