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.
5 #include "chromeos/accelerometer/accelerometer_reader.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"
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/");
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");
37 // The filename giving the path to read the scan index of each accelerometer
39 const char kAccelerometerScanIndexPath
[] =
40 "scan_elements/in_accel_%s_%s_index";
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;
49 // The length required to read uint values from configuration files.
50 const size_t kMaxAsciiUintLength
= 21;
52 // The time to wait between reading the accelerometer.
53 const int kDelayBetweenReadsMs
= 100;
55 // Reads |path| to the unsigned int pointed to by |value|. Returns true on
56 // success or false on failure.
57 bool ReadFileToUint(const base::FilePath
& path
, unsigned int* value
) {
60 if (!base::ReadFileToString(path
, &s
, kMaxAsciiUintLength
)) {
61 LOG(ERROR
) << "Failed to read " << path
.value();
64 base::TrimWhitespaceASCII(s
, base::TRIM_ALL
, &s
);
65 if (!base::StringToUint(s
, value
)) {
66 LOG(ERROR
) << "Failed to parse \"" << s
<< "\" from " << path
.value();
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
),
82 if (!base::PathExists(base::FilePath(kAccelerometerTriggerPath
))) {
83 LOG(ERROR
) << "Accelerometer trigger does not exist at"
84 << kAccelerometerTriggerPath
;
88 base::FilePath
iio_path(base::FilePath(kAccelerometerIioBasePath
).Append(
90 // Read accelerometer scales
91 if (!ReadFileToUint(iio_path
.Append(kAccelerometerBaseScaleName
),
92 &(configuration
->data
.base_scale
))) {
95 if (!ReadFileToUint(iio_path
.Append(kAccelerometerLidScaleName
),
96 &(configuration
->data
.lid_scale
))) {
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()),
112 if (index
>= kTriggerDataValues
) {
113 LOG(ERROR
) << "Field index from " << accelerometer_index_path
114 << " out of bounds: " << index
;
117 configuration
->data
.index
.push_back(index
);
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
;
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";
146 AccelerometerReader::ConfigurationData::ConfigurationData() {
149 AccelerometerReader::ConfigurationData::~ConfigurationData() {
152 AccelerometerReader::AccelerometerReader(
153 base::TaskRunner
* task_runner
,
154 AccelerometerReader::Delegate
* delegate
)
155 : task_runner_(task_runner
),
157 configuration_(new AccelerometerReader::Configuration()),
158 weak_factory_(this) {
159 DCHECK(task_runner_
);
161 // Asynchronously detect and initialize the accelerometer to avoid delaying
163 base::PostTaskAndReplyWithResult(task_runner_
.get(), FROM_HERE
,
164 base::Bind(&DetectAndReadAccelerometerConfiguration
, configuration_
),
165 base::Bind(&AccelerometerReader::OnInitialized
,
166 weak_factory_
.GetWeakPtr(), configuration_
));
169 AccelerometerReader::~AccelerometerReader() {
172 void AccelerometerReader::OnInitialized(
173 scoped_refptr
<AccelerometerReader::Configuration
> configuration
,
179 void AccelerometerReader::TriggerRead() {
180 DCHECK(!task_runner_
->RunsTasksOnCurrentThread());
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
));
190 void AccelerometerReader::OnDataRead(
191 scoped_refptr
<AccelerometerReader::Reading
> reading
,
193 DCHECK(!task_runner_
->RunsTasksOnCurrentThread());
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
);
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
);
210 // Trigger another read after the current sampling delay.
211 base::MessageLoop::current()->PostDelayedTask(
213 base::Bind(&AccelerometerReader::TriggerRead
,
214 weak_factory_
.GetWeakPtr()),
215 base::TimeDelta::FromMilliseconds(kDelayBetweenReadsMs
));
218 } // namespace chromeos