• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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