blob: e8581a4ee866b960b342b413f697bc823dd15722 [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;
28};
29
30struct LightVpdCalibrationEntry {
31 std::string vpd_name;
32 std::string iio_name;
Enrico Granata60b1cbc2019-05-20 11:06:46 -070033};
34
35constexpr char kCalibrationBias[] = "bias";
36
Enrico Granata7a622342019-05-21 11:10:59 -070037constexpr int kGyroMaxVpdCalibration = 16384; // 16dps
Enrico Granata51cdb942019-06-18 16:40:17 -070038constexpr int kAccelMaxVpdCalibration = 256; // .250g
Enrico Granata10e19de2019-05-21 14:17:36 -070039constexpr int kAccelSysfsTriggerId = 0;
Enrico Granata064a25c2019-07-15 15:48:03 -070040
41constexpr std::initializer_list<const char*> kAccelAxes = {
42 "x",
43 "y",
44 "z",
45};
Enrico Granata60b1cbc2019-05-20 11:06:46 -070046} // namespace
47
Enrico Granata51cdb942019-06-18 16:40:17 -070048Configuration::Configuration(libmems::IioDevice* sensor,
49 SensorKind kind,
50 Delegate* del)
Enrico Granata60b1cbc2019-05-20 11:06:46 -070051 : delegate_(del), kind_(kind), sensor_(sensor) {}
52
53bool Configuration::Configure() {
54 switch (kind_) {
Enrico Granata7a622342019-05-21 11:10:59 -070055 case SensorKind::ACCELEROMETER:
56 return ConfigAccelerometer();
Enrico Granata60b1cbc2019-05-20 11:06:46 -070057 case SensorKind::GYROSCOPE:
58 return ConfigGyro();
Enrico Granata651d2572019-07-16 15:17:01 -070059 case SensorKind::LIGHT:
60 return ConfigIlluminance();
Enrico Granata60b1cbc2019-05-20 11:06:46 -070061 default:
Enrico Granata651d2572019-07-16 15:17:01 -070062 LOG(ERROR) << SensorKindToString(kind_) << " unimplemented";
63 return false;
Enrico Granata60b1cbc2019-05-20 11:06:46 -070064 }
Enrico Granata60b1cbc2019-05-20 11:06:46 -070065}
66
Enrico Granata651d2572019-07-16 15:17:01 -070067bool Configuration::CopyLightCalibrationFromVpd() {
68 std::vector<LightVpdCalibrationEntry> calib_attributes = {
69 {"als_cal_intercept", "in_illuminance_calibbias"},
70 {"als_cal_slope", "in_illuminance_calibscale"},
71 };
72
73 for (auto& calib_attribute : calib_attributes) {
74 auto attrib_value = delegate_->ReadVpdValue(calib_attribute.vpd_name);
75 if (!attrib_value.has_value()) {
76 LOG(ERROR) << "VPD missing calibration value "
77 << calib_attribute.vpd_name;
78 continue;
79 }
80
81 double value;
82 if (!base::StringToDouble(attrib_value.value(), &value)) {
83 LOG(ERROR) << "VPD calibration value " << calib_attribute.vpd_name
84 << " has invalid value " << attrib_value.value();
85 continue;
86 }
87 if (!sensor_->WriteDoubleAttribute(calib_attribute.iio_name, value))
88 LOG(ERROR) << "failed to set calibration value "
89 << calib_attribute.iio_name;
90 }
91
92 return true;
93}
94
95bool Configuration::CopyImuCalibationFromVpd(int max_value) {
Enrico Granata60b1cbc2019-05-20 11:06:46 -070096 if (sensor_->IsSingleSensor()) {
97 auto location = sensor_->ReadStringAttribute("location");
98 if (!location || location->empty()) {
99 LOG(ERROR) << "cannot read a valid sensor location";
100 return false;
101 }
Enrico Granata651d2572019-07-16 15:17:01 -0700102 return CopyImuCalibationFromVpd(max_value, location->c_str());
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700103 } else {
Enrico Granata651d2572019-07-16 15:17:01 -0700104 bool base_config = CopyImuCalibationFromVpd(max_value, kBaseSensorLocation);
105 bool lid_config = CopyImuCalibationFromVpd(max_value, kLidSensorLocation);
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700106 return base_config && lid_config;
107 }
108}
109
Enrico Granata651d2572019-07-16 15:17:01 -0700110bool Configuration::CopyImuCalibationFromVpd(int max_value,
111 const std::string& location) {
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700112 const bool is_single_sensor = sensor_->IsSingleSensor();
113 std::string kind = SensorKindToString(kind_);
114
Enrico Granata651d2572019-07-16 15:17:01 -0700115 std::vector<ImuVpdCalibrationEntry> calib_attributes = {
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700116 {"x", kCalibrationBias, base::nullopt},
117 {"y", kCalibrationBias, base::nullopt},
118 {"z", kCalibrationBias, base::nullopt},
119 };
120
121 for (auto& calib_attribute : calib_attributes) {
122 auto attrib_name = base::StringPrintf(
Enrico Granata651d2572019-07-16 15:17:01 -0700123 "in_%s_%s_%s_calib%s", kind.c_str(), calib_attribute.name.c_str(),
124 location.c_str(), calib_attribute.calib.c_str());
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700125 auto attrib_value = delegate_->ReadVpdValue(attrib_name.c_str());
126 if (!attrib_value.has_value()) {
127 LOG(ERROR) << "VPD missing calibration value " << attrib_name;
128 continue;
129 }
130
131 int value;
132 if (!base::StringToInt(attrib_value.value(), &value)) {
133 LOG(ERROR) << "VPD calibration value " << attrib_name
134 << " has invalid value " << attrib_value.value();
135 continue;
136 }
137 if (abs(value) > max_value) {
138 LOG(ERROR) << "VPD calibration value " << attrib_name
139 << " has out-of-range value " << attrib_value.value();
140 continue;
141 } else {
Enrico Granata651d2572019-07-16 15:17:01 -0700142 calib_attribute.value = value;
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700143 }
144 }
145
146 for (const auto& calib_attribute : calib_attributes) {
Enrico Granata651d2572019-07-16 15:17:01 -0700147 if (!calib_attribute.value)
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700148 continue;
149 auto attrib_name = base::StringPrintf("in_%s_%s", kind.c_str(),
Enrico Granata651d2572019-07-16 15:17:01 -0700150 calib_attribute.name.c_str());
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700151
152 if (!is_single_sensor)
153 attrib_name =
154 base::StringPrintf("%s_%s", attrib_name.c_str(), location.c_str());
155 attrib_name = base::StringPrintf("%s_calib%s", attrib_name.c_str(),
Enrico Granata651d2572019-07-16 15:17:01 -0700156 calib_attribute.calib.c_str());
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700157
Enrico Granata651d2572019-07-16 15:17:01 -0700158 if (!sensor_->WriteNumberAttribute(attrib_name, *calib_attribute.value))
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700159 LOG(ERROR) << "failed to set calibration value " << attrib_name;
160 }
161
162 LOG(INFO) << "VPD calibration complete";
163 return true;
164}
165
Enrico Granata10e19de2019-05-21 14:17:36 -0700166bool Configuration::AddSysfsTrigger(int trigger_id) {
167 auto iio_trig = sensor_->GetContext()->GetDevice("iio_sysfs_trigger");
168 if (iio_trig == nullptr) {
169 LOG(ERROR) << "cannot find iio_trig_sysfs kernel module";
170 return false;
171 }
172
173 // There is a potential cross-process race here, where multiple instances
174 // of this tool may be trying to access the trigger at once. To solve this,
175 // first see if the trigger is already there. If not, try to create it, and
176 // then try to access it again. Only if the latter access fails then
177 // error out.
178 std::string trigger_name = base::StringPrintf("trigger%d", trigger_id);
179 auto trigger = sensor_->GetContext()->GetDevice(trigger_name);
180 if (trigger == nullptr) {
181 LOG(INFO) << trigger_name << " not found; adding";
182 if (!iio_trig->WriteNumberAttribute("add_trigger", trigger_id)) {
183 LOG(WARNING) << "cannot instantiate trigger " << trigger_name;
184 }
185
186 sensor_->GetContext()->Reload();
187 trigger = sensor_->GetContext()->GetDevice(trigger_name);
188 if (trigger == nullptr) {
189 LOG(ERROR) << "cannot find trigger device " << trigger_name;
190 return false;
191 }
192 }
193
194 if (!sensor_->SetTrigger(trigger)) {
195 LOG(ERROR) << "cannot set sensor's trigger to " << trigger_name;
196 return false;
197 }
198
199 base::FilePath trigger_now = trigger->GetPath().Append("trigger_now");
200
201 base::Optional<gid_t> chronos_gid = delegate_->FindGroupId("chronos");
202 if (!chronos_gid) {
203 LOG(ERROR) << "chronos group not found";
204 return false;
205 }
206
207 if (!delegate_->SetOwnership(trigger_now, -1, chronos_gid.value())) {
208 LOG(ERROR) << "cannot configure ownership on the trigger";
209 return false;
210 }
211
212 int permission = delegate_->GetPermissions(trigger_now);
213 permission |= base::FILE_PERMISSION_WRITE_BY_GROUP;
214 if (!delegate_->SetPermissions(trigger_now, permission)) {
215 LOG(ERROR) << "cannot configure permissions on the trigger";
216 return false;
217 }
218
219 LOG(INFO) << "sysfs trigger setup complete";
220 return true;
221}
222
Enrico Granata064a25c2019-07-15 15:48:03 -0700223bool Configuration::EnableAccelScanElements() {
224 auto timestamp = sensor_->GetChannel("timestamp");
225 if (!timestamp) {
226 LOG(ERROR) << "cannot find timestamp channel";
227 return false;
228 }
229 if (!timestamp->SetEnabledAndCheck(false)) {
230 LOG(ERROR) << "failed to disable timestamp channel";
231 return false;
232 }
233
234 std::vector<std::string> channels_to_enable;
235
236 if (sensor_->IsSingleSensor()) {
237 for (const auto& axis : kAccelAxes) {
238 channels_to_enable.push_back(base::StringPrintf("accel_%s", axis));
239 }
240 } else {
241 for (const auto& axis : kAccelAxes) {
242 channels_to_enable.push_back(
243 base::StringPrintf("accel_%s_%s", axis, kBaseSensorLocation));
244 channels_to_enable.push_back(
245 base::StringPrintf("accel_%s_%s", axis, kLidSensorLocation));
246 }
247 }
248
249 for (const auto& chan_name : channels_to_enable) {
250 auto channel = sensor_->GetChannel(chan_name);
251 if (!channel) {
252 LOG(ERROR) << "cannot find channel " << chan_name;
253 return false;
254 }
255 if (!channel->SetEnabledAndCheck(true)) {
256 LOG(ERROR) << "failed to enable channel " << chan_name;
257 return false;
258 }
259 }
260
261 sensor_->EnableBuffer(1);
262 if (!sensor_->IsBufferEnabled()) {
263 LOG(ERROR) << "failed to enable buffer";
264 return false;
265 }
266
267 LOG(INFO) << "buffer enabled";
268 return true;
269}
270
Enrico Granatafc2e3472019-07-16 11:23:25 -0700271bool Configuration::EnableKeyboardAngle() {
272 base::FilePath kb_wake_angle;
273 if (sensor_->IsSingleSensor()) {
274 kb_wake_angle = base::FilePath("/sys/class/chromeos/cros_ec/kb_wake_angle");
275 } else {
276 kb_wake_angle = sensor_->GetPath().Append("in_angl_offset");
277 }
278
279 if (!delegate_->Exists(kb_wake_angle)) {
280 LOG(INFO) << kb_wake_angle.value()
281 << " not found; will not enable EC wake angle";
282 return true;
283 }
284
285 base::Optional<gid_t> power_gid = delegate_->FindGroupId("power");
286 if (!power_gid) {
287 LOG(ERROR) << "cannot configure ownership on the wake angle file";
288 return false;
289 }
290
291 delegate_->SetOwnership(kb_wake_angle, -1, power_gid.value());
292 int permission = delegate_->GetPermissions(kb_wake_angle);
293 permission |= base::FILE_PERMISSION_WRITE_BY_GROUP;
294 delegate_->SetPermissions(kb_wake_angle, permission);
295
296 LOG(INFO) << "keyboard angle enabled";
297 return true;
298}
299
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700300bool Configuration::ConfigGyro() {
Enrico Granata651d2572019-07-16 15:17:01 -0700301 if (!CopyImuCalibationFromVpd(kGyroMaxVpdCalibration))
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700302 return false;
303
304 LOG(INFO) << "gyroscope configuration complete";
305 return true;
306}
307
Enrico Granata7a622342019-05-21 11:10:59 -0700308bool Configuration::ConfigAccelerometer() {
Enrico Granata651d2572019-07-16 15:17:01 -0700309 if (!CopyImuCalibationFromVpd(kAccelMaxVpdCalibration))
Enrico Granata7a622342019-05-21 11:10:59 -0700310 return false;
311
Enrico Granata10e19de2019-05-21 14:17:36 -0700312 if (!AddSysfsTrigger(kAccelSysfsTriggerId))
313 return false;
314
Enrico Granata064a25c2019-07-15 15:48:03 -0700315 if (!EnableAccelScanElements())
316 return false;
317
Enrico Granatafc2e3472019-07-16 11:23:25 -0700318 if (!EnableKeyboardAngle())
319 return false;
320
Enrico Granata7a622342019-05-21 11:10:59 -0700321 LOG(INFO) << "accelerometer configuration complete";
322 return true;
323}
324
Enrico Granata651d2572019-07-16 15:17:01 -0700325bool Configuration::ConfigIlluminance() {
326 if (!CopyLightCalibrationFromVpd())
327 return false;
328
329 LOG(INFO) << "light configuration complete";
330 return true;
331}
332
Enrico Granata60b1cbc2019-05-20 11:06:46 -0700333} // namespace mems_setup