blob: 1c7e0a0ae9fc31028d2bc4cabb6300bb401d23c8 [file] [log] [blame]
Enrico Granata60b1cbc2019-05-20 11:06:46 -07001// Copyright 2019 The Chromium OS 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
Enrico Granata51cdb942019-06-18 16:40:17 -07005#include "mems_setup/configuration.h"
6
Enrico Granata064a25c2019-07-15 15:48:03 -07007#include <initializer_list>
Enrico Granata60b1cbc2019-05-20 11:06:46 -07008#include <vector>
9
Enrico Granata10e19de2019-05-21 14:17:36 -070010#include <base/files/file_util.h>
Enrico Granata60b1cbc2019-05-20 11:06:46 -070011#include <base/logging.h>
12#include <base/strings/string_number_conversions.h>
13#include <base/strings/stringprintf.h>
14
Enrico Granata51cdb942019-06-18 16:40:17 -070015#include <libmems/iio_channel.h>
16#include <libmems/iio_context.h>
17#include <libmems/iio_device.h>
Enrico Granata60b1cbc2019-05-20 11:06:46 -070018#include "mems_setup/sensor_location.h"
19
20namespace mems_setup {
21
22namespace {
23
Enrico Granata651d2572019-07-16 15:17:01 -070024struct ImuVpdCalibrationEntry {
25 std::string name;
26 std::string calib;
27 base::Optional<int> value;
Enrico Granatafc7227d2019-07-26 13:51:19 -070028 bool missing_is_error;
Enrico Granata651d2572019-07-16 15:17:01 -070029};
30
31struct LightVpdCalibrationEntry {
32 std::string vpd_name;
33 std::string iio_name;
Enrico Granata60b1cbc2019-05-20 11:06:46 -070034};
35
36constexpr char kCalibrationBias[] = "bias";
Enrico Granatafc7227d2019-07-26 13:51:19 -070037constexpr char kCalibrationScale[] = "scale";
Enrico Granata60b1cbc2019-05-20 11:06:46 -070038
Enrico Granata7a622342019-05-21 11:10:59 -070039constexpr int kGyroMaxVpdCalibration = 16384; // 16dps
Enrico Granata51cdb942019-06-18 16:40:17 -070040constexpr int kAccelMaxVpdCalibration = 256; // .250g
Enrico Granata10e19de2019-05-21 14:17:36 -070041constexpr int kAccelSysfsTriggerId = 0;
Enrico Granata064a25c2019-07-15 15:48:03 -070042
43constexpr std::initializer_list<const char*> kAccelAxes = {
44 "x",
45 "y",
46 "z",
47};
Enrico Granata60b1cbc2019-05-20 11:06:46 -070048} // namespace
49
Enrico Granata51cdb942019-06-18 16:40:17 -070050Configuration::Configuration(libmems::IioDevice* sensor,
51 SensorKind kind,
52 Delegate* del)
Enrico Granata60b1cbc2019-05-20 11:06:46 -070053 : delegate_(del), kind_(kind), sensor_(sensor) {}
54
55bool Configuration::Configure() {
56 switch (kind_) {
Enrico Granata7a622342019-05-21 11:10:59 -070057 case SensorKind::ACCELEROMETER:
58 return ConfigAccelerometer();
Enrico Granata60b1cbc2019-05-20 11:06:46 -070059 case SensorKind::GYROSCOPE:
60 return ConfigGyro();
Enrico Granata651d2572019-07-16 15:17:01 -070061 case SensorKind::LIGHT:
62 return ConfigIlluminance();
Enrico Granata60b1cbc2019-05-20 11:06:46 -070063 default:
Enrico Granata651d2572019-07-16 15:17:01 -070064 LOG(ERROR) << SensorKindToString(kind_) << " unimplemented";
65 return false;
Enrico Granata60b1cbc2019-05-20 11:06:46 -070066 }
Enrico Granata60b1cbc2019-05-20 11:06:46 -070067}
68
Enrico Granata651d2572019-07-16 15:17:01 -070069bool Configuration::CopyLightCalibrationFromVpd() {
70 std::vector<LightVpdCalibrationEntry> calib_attributes = {
71 {"als_cal_intercept", "in_illuminance_calibbias"},
72 {"als_cal_slope", "in_illuminance_calibscale"},
73 };
74
75 for (auto& calib_attribute : calib_attributes) {
76 auto attrib_value = delegate_->ReadVpdValue(calib_attribute.vpd_name);
77 if (!attrib_value.has_value()) {
78 LOG(ERROR) << "VPD missing calibration value "
79 << calib_attribute.vpd_name;
80 continue;
81 }
82
83 double value;
84 if (!base::StringToDouble(attrib_value.value(), &value)) {
85 LOG(ERROR) << "VPD calibration value " << calib_attribute.vpd_name
86 << " has invalid value " << attrib_value.value();
87 continue;
88 }
89 if (!sensor_->WriteDoubleAttribute(calib_attribute.iio_name, value))
90 LOG(ERROR) << "failed to set calibration value "
91 << calib_attribute.iio_name;
92 }
93
94 return true;
95}
96
97bool Configuration::CopyImuCalibationFromVpd(int max_value) {
Enrico Granata60b1cbc2019-05-20 11:06:46 -070098 if (sensor_->IsSingleSensor()) {
99 auto location = sensor_->ReadStringAttribute("location");
100 if (!location || location->empty()) {
101 LOG(ERROR) << "cannot read a valid sensor location";
102 return false;
103 }
Enrico Granata651d2572019-07-16 15:17:01 -0700104 return CopyImuCalibationFromVpd(max_value, location->c_str());
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700105 } else {
Enrico Granata651d2572019-07-16 15:17:01 -0700106 bool base_config = CopyImuCalibationFromVpd(max_value, kBaseSensorLocation);
107 bool lid_config = CopyImuCalibationFromVpd(max_value, kLidSensorLocation);
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700108 return base_config && lid_config;
109 }
110}
111
Enrico Granata651d2572019-07-16 15:17:01 -0700112bool Configuration::CopyImuCalibationFromVpd(int max_value,
113 const std::string& location) {
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700114 const bool is_single_sensor = sensor_->IsSingleSensor();
115 std::string kind = SensorKindToString(kind_);
116
Enrico Granata651d2572019-07-16 15:17:01 -0700117 std::vector<ImuVpdCalibrationEntry> calib_attributes = {
Enrico Granatafc7227d2019-07-26 13:51:19 -0700118 {"x", kCalibrationBias, base::nullopt, true},
119 {"y", kCalibrationBias, base::nullopt, true},
120 {"z", kCalibrationBias, base::nullopt, true},
121
122 {"x", kCalibrationScale, base::nullopt, false},
123 {"y", kCalibrationScale, base::nullopt, false},
124 {"z", kCalibrationScale, base::nullopt, false},
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700125 };
126
127 for (auto& calib_attribute : calib_attributes) {
128 auto attrib_name = base::StringPrintf(
Enrico Granata651d2572019-07-16 15:17:01 -0700129 "in_%s_%s_%s_calib%s", kind.c_str(), calib_attribute.name.c_str(),
130 location.c_str(), calib_attribute.calib.c_str());
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700131 auto attrib_value = delegate_->ReadVpdValue(attrib_name.c_str());
132 if (!attrib_value.has_value()) {
Enrico Granatafc7227d2019-07-26 13:51:19 -0700133 if (calib_attribute.missing_is_error)
134 LOG(ERROR) << "VPD missing calibration value " << attrib_name;
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700135 continue;
136 }
137
138 int value;
139 if (!base::StringToInt(attrib_value.value(), &value)) {
140 LOG(ERROR) << "VPD calibration value " << attrib_name
141 << " has invalid value " << attrib_value.value();
142 continue;
143 }
144 if (abs(value) > max_value) {
145 LOG(ERROR) << "VPD calibration value " << attrib_name
146 << " has out-of-range value " << attrib_value.value();
147 continue;
148 } else {
Enrico Granata651d2572019-07-16 15:17:01 -0700149 calib_attribute.value = value;
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700150 }
151 }
152
153 for (const auto& calib_attribute : calib_attributes) {
Enrico Granata651d2572019-07-16 15:17:01 -0700154 if (!calib_attribute.value)
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700155 continue;
156 auto attrib_name = base::StringPrintf("in_%s_%s", kind.c_str(),
Enrico Granata651d2572019-07-16 15:17:01 -0700157 calib_attribute.name.c_str());
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700158
159 if (!is_single_sensor)
160 attrib_name =
161 base::StringPrintf("%s_%s", attrib_name.c_str(), location.c_str());
162 attrib_name = base::StringPrintf("%s_calib%s", attrib_name.c_str(),
Enrico Granata651d2572019-07-16 15:17:01 -0700163 calib_attribute.calib.c_str());
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700164
Enrico Granata651d2572019-07-16 15:17:01 -0700165 if (!sensor_->WriteNumberAttribute(attrib_name, *calib_attribute.value))
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700166 LOG(ERROR) << "failed to set calibration value " << attrib_name;
167 }
168
169 LOG(INFO) << "VPD calibration complete";
170 return true;
171}
172
Enrico Granata10e19de2019-05-21 14:17:36 -0700173bool Configuration::AddSysfsTrigger(int trigger_id) {
174 auto iio_trig = sensor_->GetContext()->GetDevice("iio_sysfs_trigger");
175 if (iio_trig == nullptr) {
176 LOG(ERROR) << "cannot find iio_trig_sysfs kernel module";
177 return false;
178 }
179
180 // There is a potential cross-process race here, where multiple instances
181 // of this tool may be trying to access the trigger at once. To solve this,
182 // first see if the trigger is already there. If not, try to create it, and
183 // then try to access it again. Only if the latter access fails then
184 // error out.
185 std::string trigger_name = base::StringPrintf("trigger%d", trigger_id);
186 auto trigger = sensor_->GetContext()->GetDevice(trigger_name);
187 if (trigger == nullptr) {
188 LOG(INFO) << trigger_name << " not found; adding";
189 if (!iio_trig->WriteNumberAttribute("add_trigger", trigger_id)) {
190 LOG(WARNING) << "cannot instantiate trigger " << trigger_name;
191 }
192
193 sensor_->GetContext()->Reload();
194 trigger = sensor_->GetContext()->GetDevice(trigger_name);
195 if (trigger == nullptr) {
196 LOG(ERROR) << "cannot find trigger device " << trigger_name;
197 return false;
198 }
199 }
200
201 if (!sensor_->SetTrigger(trigger)) {
202 LOG(ERROR) << "cannot set sensor's trigger to " << trigger_name;
203 return false;
204 }
205
206 base::FilePath trigger_now = trigger->GetPath().Append("trigger_now");
207
208 base::Optional<gid_t> chronos_gid = delegate_->FindGroupId("chronos");
209 if (!chronos_gid) {
210 LOG(ERROR) << "chronos group not found";
211 return false;
212 }
213
214 if (!delegate_->SetOwnership(trigger_now, -1, chronos_gid.value())) {
215 LOG(ERROR) << "cannot configure ownership on the trigger";
216 return false;
217 }
218
219 int permission = delegate_->GetPermissions(trigger_now);
220 permission |= base::FILE_PERMISSION_WRITE_BY_GROUP;
221 if (!delegate_->SetPermissions(trigger_now, permission)) {
222 LOG(ERROR) << "cannot configure permissions on the trigger";
223 return false;
224 }
225
226 LOG(INFO) << "sysfs trigger setup complete";
227 return true;
228}
229
Enrico Granata064a25c2019-07-15 15:48:03 -0700230bool Configuration::EnableAccelScanElements() {
231 auto timestamp = sensor_->GetChannel("timestamp");
232 if (!timestamp) {
233 LOG(ERROR) << "cannot find timestamp channel";
234 return false;
235 }
236 if (!timestamp->SetEnabledAndCheck(false)) {
237 LOG(ERROR) << "failed to disable timestamp channel";
238 return false;
239 }
240
241 std::vector<std::string> channels_to_enable;
242
243 if (sensor_->IsSingleSensor()) {
244 for (const auto& axis : kAccelAxes) {
245 channels_to_enable.push_back(base::StringPrintf("accel_%s", axis));
246 }
247 } else {
248 for (const auto& axis : kAccelAxes) {
249 channels_to_enable.push_back(
250 base::StringPrintf("accel_%s_%s", axis, kBaseSensorLocation));
251 channels_to_enable.push_back(
252 base::StringPrintf("accel_%s_%s", axis, kLidSensorLocation));
253 }
254 }
255
256 for (const auto& chan_name : channels_to_enable) {
257 auto channel = sensor_->GetChannel(chan_name);
258 if (!channel) {
259 LOG(ERROR) << "cannot find channel " << chan_name;
260 return false;
261 }
262 if (!channel->SetEnabledAndCheck(true)) {
263 LOG(ERROR) << "failed to enable channel " << chan_name;
264 return false;
265 }
266 }
267
268 sensor_->EnableBuffer(1);
269 if (!sensor_->IsBufferEnabled()) {
270 LOG(ERROR) << "failed to enable buffer";
271 return false;
272 }
273
274 LOG(INFO) << "buffer enabled";
275 return true;
276}
277
Enrico Granatafc2e3472019-07-16 11:23:25 -0700278bool Configuration::EnableKeyboardAngle() {
279 base::FilePath kb_wake_angle;
280 if (sensor_->IsSingleSensor()) {
281 kb_wake_angle = base::FilePath("/sys/class/chromeos/cros_ec/kb_wake_angle");
282 } else {
283 kb_wake_angle = sensor_->GetPath().Append("in_angl_offset");
284 }
285
286 if (!delegate_->Exists(kb_wake_angle)) {
287 LOG(INFO) << kb_wake_angle.value()
288 << " not found; will not enable EC wake angle";
289 return true;
290 }
291
292 base::Optional<gid_t> power_gid = delegate_->FindGroupId("power");
293 if (!power_gid) {
294 LOG(ERROR) << "cannot configure ownership on the wake angle file";
295 return false;
296 }
297
298 delegate_->SetOwnership(kb_wake_angle, -1, power_gid.value());
299 int permission = delegate_->GetPermissions(kb_wake_angle);
300 permission |= base::FILE_PERMISSION_WRITE_BY_GROUP;
301 delegate_->SetPermissions(kb_wake_angle, permission);
302
303 LOG(INFO) << "keyboard angle enabled";
304 return true;
305}
306
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700307bool Configuration::ConfigGyro() {
Enrico Granata651d2572019-07-16 15:17:01 -0700308 if (!CopyImuCalibationFromVpd(kGyroMaxVpdCalibration))
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700309 return false;
310
311 LOG(INFO) << "gyroscope configuration complete";
312 return true;
313}
314
Enrico Granata7a622342019-05-21 11:10:59 -0700315bool Configuration::ConfigAccelerometer() {
Enrico Granata651d2572019-07-16 15:17:01 -0700316 if (!CopyImuCalibationFromVpd(kAccelMaxVpdCalibration))
Enrico Granata7a622342019-05-21 11:10:59 -0700317 return false;
318
Enrico Granata10e19de2019-05-21 14:17:36 -0700319 if (!AddSysfsTrigger(kAccelSysfsTriggerId))
320 return false;
321
Enrico Granata064a25c2019-07-15 15:48:03 -0700322 if (!EnableAccelScanElements())
323 return false;
324
Enrico Granatafc2e3472019-07-16 11:23:25 -0700325 if (!EnableKeyboardAngle())
326 return false;
327
Enrico Granata7a622342019-05-21 11:10:59 -0700328 LOG(INFO) << "accelerometer configuration complete";
329 return true;
330}
331
Enrico Granata651d2572019-07-16 15:17:01 -0700332bool Configuration::ConfigIlluminance() {
333 if (!CopyLightCalibrationFromVpd())
334 return false;
335
336 LOG(INFO) << "light configuration complete";
337 return true;
338}
339
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700340} // namespace mems_setup