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/files/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
18namespace chromeos {
19
20namespace {
21
22// Paths to access necessary data from the accelerometer device.
23const base::FilePath::CharType kAccelerometerTriggerPath[] =
24    FILE_PATH_LITERAL("/sys/bus/iio/devices/trigger0/trigger_now");
25const base::FilePath::CharType kAccelerometerDevicePath[] =
26    FILE_PATH_LITERAL("/dev/cros-ec-accel");
27const base::FilePath::CharType kAccelerometerIioBasePath[] =
28    FILE_PATH_LITERAL("/sys/bus/iio/devices/");
29
30// File within the device in kAccelerometerIioBasePath containing the scale of
31// the accelerometers.
32const base::FilePath::CharType kScaleNameFormatString[] = "in_accel_%s_scale";
33
34// The filename giving the path to read the scan index of each accelerometer
35// axis.
36const char kAccelerometerScanIndexPath[] =
37    "scan_elements/in_accel_%s_%s_index";
38
39// The names of the accelerometers. Matches up with the enum AccelerometerSource
40// in ui/accelerometer/accelerometer_types.h.
41const char kAccelerometerNames[ui::ACCELEROMETER_SOURCE_COUNT][5] = {
42    "lid", "base"};
43
44// The axes on each accelerometer.
45const char kAccelerometerAxes[][2] = {"y", "x", "z"};
46
47// The length required to read uint values from configuration files.
48const size_t kMaxAsciiUintLength = 21;
49
50// The size of individual values.
51const size_t kDataSize = 2;
52
53// The time to wait between reading the accelerometer.
54const int kDelayBetweenReadsMs = 100;
55
56// The mean acceleration due to gravity on Earth in m/s^2.
57const float kMeanGravity = 9.80665f;
58
59// Reads |path| to the unsigned int pointed to by |value|. Returns true on
60// success or false on failure.
61bool ReadFileToInt(const base::FilePath& path, int* value) {
62  std::string s;
63  DCHECK(value);
64  if (!base::ReadFileToString(path, &s, kMaxAsciiUintLength)) {
65    return false;
66  }
67  base::TrimWhitespaceASCII(s, base::TRIM_ALL, &s);
68  if (!base::StringToInt(s, value)) {
69    LOG(ERROR) << "Failed to parse \"" << s << "\" from " << path.value();
70    return false;
71  }
72  return true;
73}
74
75bool DetectAndReadAccelerometerConfiguration(
76    scoped_refptr<AccelerometerReader::Configuration> configuration) {
77  // Check for accelerometer symlink which will be created by the udev rules
78  // file on detecting the device.
79  base::FilePath device;
80  if (!base::ReadSymbolicLink(base::FilePath(kAccelerometerDevicePath),
81                              &device)) {
82    return false;
83  }
84
85  if (!base::PathExists(base::FilePath(kAccelerometerTriggerPath))) {
86    LOG(ERROR) << "Accelerometer trigger does not exist at"
87               << kAccelerometerTriggerPath;
88    return false;
89  }
90
91  base::FilePath iio_path(base::FilePath(kAccelerometerIioBasePath).Append(
92      device));
93  // Read configuration of each accelerometer axis from each accelerometer from
94  // /sys/bus/iio/devices/iio:deviceX/.
95  for (size_t i = 0; i < arraysize(kAccelerometerNames); ++i) {
96    // Read scale of accelerometer.
97    std::string accelerometer_scale_path = base::StringPrintf(
98        kScaleNameFormatString, kAccelerometerNames[i]);
99    int scale_divisor;
100    if (!ReadFileToInt(iio_path.Append(accelerometer_scale_path.c_str()),
101        &scale_divisor)) {
102      configuration->data.has[i] = false;
103      continue;
104    }
105
106    configuration->data.has[i] = true;
107    configuration->data.count++;
108    for (size_t j = 0; j < arraysize(kAccelerometerAxes); ++j) {
109      configuration->data.scale[i][j] = kMeanGravity / scale_divisor;
110      std::string accelerometer_index_path = base::StringPrintf(
111          kAccelerometerScanIndexPath, kAccelerometerAxes[j],
112          kAccelerometerNames[i]);
113      if (!ReadFileToInt(iio_path.Append(accelerometer_index_path.c_str()),
114                         &(configuration->data.index[i][j]))) {
115        return false;
116      }
117    }
118  }
119
120  // Adjust the directions of accelerometers to match the AccelerometerUpdate
121  // type specified in ui/accelerometer/accelerometer_types.h.
122  configuration->data.scale[ui::ACCELEROMETER_SOURCE_SCREEN][0] *= -1.0f;
123  for (int i = 0; i < 3; ++i) {
124    configuration->data.scale[ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD][i] *=
125        -1.0f;
126  }
127
128  // Verify indices are within bounds.
129  for (int i = 0; i < ui::ACCELEROMETER_SOURCE_COUNT; ++i) {
130    if (!configuration->data.has[i])
131      continue;
132    for (int j = 0; j < 3; ++j) {
133      if (configuration->data.index[i][j] < 0 ||
134          configuration->data.index[i][j] >=
135              3 * static_cast<int>(configuration->data.count)) {
136        LOG(ERROR) << "Field index for " << kAccelerometerNames[i] << " "
137                   << kAccelerometerAxes[j] << " axis out of bounds.";
138        return false;
139      }
140    }
141  }
142  configuration->data.length = kDataSize * 3 * configuration->data.count;
143  return true;
144}
145
146bool ReadAccelerometer(
147    scoped_refptr<AccelerometerReader::Reading> reading,
148    size_t length) {
149  // Initiate the trigger to read accelerometers simultaneously
150  int bytes_written = base::WriteFile(
151      base::FilePath(kAccelerometerTriggerPath), "1\n", 2);
152  if (bytes_written < 2) {
153    PLOG(ERROR) << "Accelerometer trigger failure: " << bytes_written;
154    return false;
155  }
156
157  // Read resulting sample from /dev/cros-ec-accel.
158  int bytes_read = base::ReadFile(base::FilePath(kAccelerometerDevicePath),
159                                  reading->data, length);
160  if (bytes_read < static_cast<int>(length)) {
161    LOG(ERROR) << "Read " << bytes_read << " byte(s), expected "
162               << length << " bytes from accelerometer";
163    return false;
164  }
165  return true;
166}
167
168}  // namespace
169
170AccelerometerReader::ConfigurationData::ConfigurationData()
171    : count(0) {
172  for (int i = 0; i < ui::ACCELEROMETER_SOURCE_COUNT; ++i) {
173    has[i] = false;
174    for (int j = 0; j < 3; ++j) {
175      scale[i][j] = 0;
176      index[i][j] = -1;
177    }
178  }
179}
180
181AccelerometerReader::ConfigurationData::~ConfigurationData() {
182}
183
184AccelerometerReader::AccelerometerReader(
185    scoped_refptr<base::TaskRunner> blocking_task_runner,
186    AccelerometerReader::Delegate* delegate)
187    : task_runner_(blocking_task_runner),
188      delegate_(delegate),
189      configuration_(new AccelerometerReader::Configuration()),
190      weak_factory_(this) {
191  DCHECK(task_runner_.get());
192  DCHECK(delegate_);
193  // Asynchronously detect and initialize the accelerometer to avoid delaying
194  // startup.
195  base::PostTaskAndReplyWithResult(task_runner_.get(), FROM_HERE,
196      base::Bind(&DetectAndReadAccelerometerConfiguration, configuration_),
197      base::Bind(&AccelerometerReader::OnInitialized,
198                 weak_factory_.GetWeakPtr(), configuration_));
199}
200
201AccelerometerReader::~AccelerometerReader() {
202}
203
204void AccelerometerReader::OnInitialized(
205    scoped_refptr<AccelerometerReader::Configuration> configuration,
206    bool success) {
207  if (success)
208    TriggerRead();
209}
210
211void AccelerometerReader::TriggerRead() {
212  DCHECK(!task_runner_->RunsTasksOnCurrentThread());
213
214  scoped_refptr<AccelerometerReader::Reading> reading(
215      new AccelerometerReader::Reading());
216  base::PostTaskAndReplyWithResult(task_runner_.get(),
217                                   FROM_HERE,
218                                   base::Bind(&ReadAccelerometer, reading,
219                                              configuration_->data.length),
220                                   base::Bind(&AccelerometerReader::OnDataRead,
221                                              weak_factory_.GetWeakPtr(),
222                                              reading));
223}
224
225void AccelerometerReader::OnDataRead(
226    scoped_refptr<AccelerometerReader::Reading> reading,
227    bool success) {
228  DCHECK(!task_runner_->RunsTasksOnCurrentThread());
229
230  if (success) {
231    for (int i = 0; i < ui::ACCELEROMETER_SOURCE_COUNT; ++i) {
232      if (!configuration_->data.has[i])
233        continue;
234
235      int16* values = reinterpret_cast<int16*>(reading->data);
236      update_.Set(static_cast<ui::AccelerometerSource>(i),
237                  values[configuration_->data.index[i][0]] *
238                      configuration_->data.scale[i][0],
239                  values[configuration_->data.index[i][1]] *
240                      configuration_->data.scale[i][1],
241                  values[configuration_->data.index[i][2]] *
242                      configuration_->data.scale[i][2]);
243    }
244    delegate_->HandleAccelerometerUpdate(update_);
245  }
246
247  // Trigger another read after the current sampling delay.
248  base::MessageLoop::current()->PostDelayedTask(
249      FROM_HERE,
250      base::Bind(&AccelerometerReader::TriggerRead,
251                 weak_factory_.GetWeakPtr()),
252      base::TimeDelta::FromMilliseconds(kDelayBetweenReadsMs));
253}
254
255}  // namespace chromeos
256