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. 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 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 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 146 AccelerometerReader::ConfigurationData::ConfigurationData() { 147 } 148 149 AccelerometerReader::ConfigurationData::~ConfigurationData() { 150 } 151 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 169 AccelerometerReader::~AccelerometerReader() { 170 } 171 172 void AccelerometerReader::OnInitialized( 173 scoped_refptr<AccelerometerReader::Configuration> configuration, 174 bool success) { 175 if (success) 176 TriggerRead(); 177 } 178 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 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