blob: ad03b911d7609ea230459c2d7e0d73a5b4844af1 [file] [log] [blame]
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mems_setup/configuration.h"
#include <algorithm>
#include <initializer_list>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <base/check.h>
#include <base/files/file_util.h>
#include <base/files/file_path.h>
#include <base/json/json_reader.h>
#include <base/logging.h>
#include <base/process/launch.h>
#include <base/stl_util.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/stringprintf.h>
#include <re2/re2.h>
#include <libmems/common_types.h>
#include <libmems/iio_channel.h>
#include <libmems/iio_context.h>
#include <libmems/iio_device.h>
#include <libmems/iio_device_impl.h>
#include <libsar/sar_config_reader.h>
#include "mems_setup/sensor_location.h"
namespace mems_setup {
namespace {
struct ImuVpdCalibrationEntry {
std::string name;
std::string calib;
std::optional<int> max_value;
std::optional<int> value;
bool missing_is_error;
};
struct LightVpdCalibrationEntry {
std::string vpd_name;
std::string iio_name;
};
struct LightColorCalibrationEntry {
std::string iio_name;
std::optional<double> value;
libmems::IioChannel* chn;
};
#if !USE_IIOSERVICE_PROXIMITY
constexpr char kPowerGroupName[] = "power";
#endif // !USE_IIOSERVICE_PROXIMITY
constexpr char kIioServiceGroupName[] = "iioservice";
constexpr char kCalibrationBias[] = "bias";
constexpr char kCalibrationScale[] = "scale";
constexpr char kSysfsTriggerPrefix[] = "sysfstrig";
constexpr int kGyroMaxVpdCalibration = 16384; // 16dps
constexpr int kAccelMaxVpdCalibration = 256; // .250g
constexpr int kAccelSysfsTriggerId = 0;
constexpr int kSysfsTriggerId = -1;
constexpr std::initializer_list<const char*> kAccelAxes = {
"x",
"y",
"z",
};
constexpr char kTriggerString[] = "trigger";
constexpr char kDevlinkPrefix[] = "/dev/proximity";
constexpr char kFilesToSetReadAndOwnership[][28] = {
"buffer/hwfifo_timeout", "buffer/hwfifo_watermark_max", "buffer/enable",
"buffer/length", "trigger/current_trigger"};
constexpr char kFilesToSetWriteAndOwnership[][24] = {"sampling_frequency",
"buffer/hwfifo_timeout",
"buffer/hwfifo_flush",
"buffer/enable",
"buffer/length",
"trigger/current_trigger",
"flush",
"frequency"};
constexpr char kScanElementsString[] = "scan_elements";
constexpr char kEventsString[] = "events";
} // namespace
const char* Configuration::GetGroupNameForSysfs() {
#if !USE_IIOSERVICE_PROXIMITY
if (kind_ == SensorKind::PROXIMITY)
return kPowerGroupName;
#endif // !USE_IIOSERVICE_PROXIMITY
return kIioServiceGroupName;
}
Configuration::Configuration(libmems::IioContext* context,
libmems::IioDevice* sensor,
Delegate* del)
: delegate_(del), sensor_(sensor), context_(context) {
DCHECK(sensor_);
kind_ = mems_setup::SensorKindFromString(
sensor_->GetName() ? sensor_->GetName() : "");
}
bool Configuration::Configure() {
iioservice_gid_ = delegate_->FindGroupId(GetGroupNameForSysfs());
if (!iioservice_gid_.has_value()) {
LOG(ERROR) << "iioservice group not found";
return false;
}
if (!ConfigureOnKind())
return false;
if (!SetupPermissions())
return false;
// If the buffer is enabled, which means mems_setup has already been used on
// this sensor and iioservice is reading the samples from it, skip setting the
// frequency.
if (!sensor_->IsBufferEnabled()) {
sensor_->WriteDoubleAttribute(libmems::kSamplingFrequencyAttr, 0.0);
for (auto& channel : sensor_->GetAllChannels())
channel->WriteDoubleAttribute(libmems::kSamplingFrequencyAttr, 0.0);
}
// Ignores the error as it may fail on kernel 4.4 or HID stack sensors.
sensor_->WriteStringAttribute("current_timestamp_clock", "boottime");
return true;
}
bool Configuration::CopyLightCalibrationFromVpd() {
std::vector<LightVpdCalibrationEntry> calib_attributes = {
{"als_cal_intercept", "calibbias"},
{"als_cal_slope", "calibscale"},
};
auto chn = sensor_->GetChannel("illuminance");
if (!chn) {
LOG(ERROR) << "No channel illuminance";
return false;
}
for (auto& calib_attribute : calib_attributes) {
auto attrib_value = delegate_->ReadVpdValue(calib_attribute.vpd_name);
if (!attrib_value.has_value()) {
LOG(ERROR) << "VPD missing calibration value "
<< calib_attribute.vpd_name;
continue;
}
double value;
if (!base::StringToDouble(attrib_value.value(), &value)) {
LOG(ERROR) << "VPD calibration value " << calib_attribute.vpd_name
<< " has invalid value " << attrib_value.value();
continue;
}
if (!chn->WriteDoubleAttribute(calib_attribute.iio_name, value))
LOG(ERROR) << "failed to set calibration value "
<< calib_attribute.iio_name;
}
/*
* RGB sensors may need per channel calibration.
*/
std::vector<LightColorCalibrationEntry> calib_color_entries = {
{"illuminance_red", std::nullopt, nullptr},
{"illuminance_green", std::nullopt, nullptr},
{"illuminance_blue", std::nullopt, nullptr},
};
for (auto& color_entry : calib_color_entries) {
color_entry.chn = sensor_->GetChannel(color_entry.iio_name);
if (!color_entry.chn)
return true;
}
auto attrib_value = delegate_->ReadVpdValue("als_cal_slope_color");
if (attrib_value.has_value()) {
/*
* Split the attributes in 3 doubles.
*/
std::vector<std::string> attrs =
base::SplitString(attrib_value.value(), " ", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
if (attrs.size() == 3) {
for (int i = 0; i < 3; i++) {
double value;
if (!base::StringToDouble(attrs[i], &value)) {
LOG(ERROR) << "VPD_entry " << i << " of als_cal_slope_color "
<< "is not a float: " << attrs[i];
break;
}
calib_color_entries[i].value = value;
}
for (auto& color_entry : calib_color_entries) {
if (!color_entry.value) {
LOG(ERROR) << "No value set for " << color_entry.iio_name;
continue;
}
if (!color_entry.chn->WriteDoubleAttribute("calibscale",
*color_entry.value))
LOG(WARNING) << "failed to to set calibration value "
<< color_entry.iio_name << " to " << *color_entry.value;
}
} else {
LOG(ERROR) << "VPD_entry als_cal_slope_color is malformed : "
<< attrib_value.value();
}
}
return true;
}
bool Configuration::CopyImuCalibationFromVpd(int max_value) {
if (sensor_->IsSingleSensor()) {
auto location = sensor_->GetLocation();
if (!location || location->empty()) {
LOG(ERROR) << "cannot read a valid sensor location";
return false;
}
return CopyImuCalibationFromVpd(max_value, location->c_str());
} else {
bool base_config = CopyImuCalibationFromVpd(max_value, kBaseSensorLocation);
bool lid_config = CopyImuCalibationFromVpd(max_value, kLidSensorLocation);
return base_config && lid_config;
}
}
bool Configuration::CopyImuCalibationFromVpd(int max_value,
const std::string& location) {
const bool is_single_sensor = sensor_->IsSingleSensor();
std::string kind = SensorKindToString(kind_);
std::vector<ImuVpdCalibrationEntry> calib_attributes = {
{"x", kCalibrationBias, max_value, std::nullopt, true},
{"y", kCalibrationBias, max_value, std::nullopt, true},
{"z", kCalibrationBias, max_value, std::nullopt, true},
{"x", kCalibrationScale, std::nullopt, std::nullopt, false},
{"y", kCalibrationScale, std::nullopt, std::nullopt, false},
{"z", kCalibrationScale, std::nullopt, std::nullopt, false},
};
for (auto& calib_attribute : calib_attributes) {
auto attrib_name = base::StringPrintf(
"in_%s_%s_%s_calib%s", kind.c_str(), calib_attribute.name.c_str(),
location.c_str(), calib_attribute.calib.c_str());
auto attrib_value = delegate_->ReadVpdValue(attrib_name.c_str());
LOG(INFO) << attrib_name
<< " attrib_value: " << attrib_value.value_or("nan");
if (!attrib_value.has_value()) {
if (calib_attribute.missing_is_error)
LOG(ERROR) << "VPD missing calibration value " << attrib_name;
continue;
}
int value;
if (!base::StringToInt(attrib_value.value(), &value)) {
LOG(ERROR) << "VPD calibration value " << attrib_name
<< " has invalid value " << attrib_value.value();
// TODO(crbug/1039454: gwendal): Add uma stats.
continue;
}
if (calib_attribute.max_value && abs(value) > calib_attribute.max_value) {
LOG(ERROR) << "VPD calibration value " << attrib_name
<< " has out-of-range value " << attrib_value.value();
// TODO(crbug/1039454: gwendal): Add uma stats.
return false;
} else {
calib_attribute.value = value;
}
}
for (const auto& calib_attribute : calib_attributes) {
if (!calib_attribute.value)
continue;
auto chn_id =
base::StringPrintf("%s_%s", kind.c_str(), calib_attribute.name.c_str());
if (!is_single_sensor)
chn_id = base::StringPrintf("%s_%s", chn_id.c_str(), location.c_str());
auto chn = sensor_->GetChannel(chn_id);
if (!chn) {
LOG(ERROR) << "No channel with id " << chn_id;
return false;
}
auto attrib_name =
base::StringPrintf("calib%s", calib_attribute.calib.c_str());
if (!chn->WriteNumberAttribute(attrib_name, *calib_attribute.value)) {
LOG(ERROR) << "failed to set calibration value " << attrib_name;
return false;
}
LOG(INFO) << attrib_name << ": "
<< chn->ReadNumberAttribute(attrib_name).value_or(-88888);
}
LOG(INFO) << "VPD calibration complete";
return true;
}
bool Configuration::AddSysfsTrigger(int sysfs_trigger_id) {
std::string dev_name =
libmems::IioDeviceImpl::GetStringFromId(sensor_->GetId());
// /sys/bus/iio/devices/iio:deviceX
base::FilePath sys_dev_path = sensor_->GetPath();
if (!delegate_->Exists(sys_dev_path.Append(kTriggerString))) {
// Uses FIFO and doesn't need a trigger.
return true;
}
// There is a potential cross-process race here, where multiple instances
// of this tool may be trying to access the trigger at once. To solve this,
// first see if the trigger is already there. If not, try to create it, and
// then try to access it again. Only if the latter access fails then
// error out.
auto trigger_name =
base::StringPrintf("%s%d", kSysfsTriggerPrefix, sysfs_trigger_id);
auto triggers = context_->GetTriggersByName(trigger_name);
if (triggers.size() > 1) {
LOG(ERROR) << "Several triggers with the same name " << trigger_name
<< " is not expected.";
return false;
}
if (triggers.size() == 0) {
LOG(INFO) << "trigger " << trigger_name << " not found; adding";
auto iio_sysfs_trigger = context_->GetTriggerById(kSysfsTriggerId);
if (iio_sysfs_trigger == nullptr) {
LOG(ERROR) << "cannot find iio_trig_sysfs kernel module";
return false;
}
if (!iio_sysfs_trigger->WriteNumberAttribute("add_trigger",
sysfs_trigger_id)) {
// It may happen if another instance of mems_setup is running in parallel.
LOG(WARNING) << "cannot instantiate trigger " << trigger_name;
}
context_->Reload();
triggers = context_->GetTriggersByName(trigger_name);
if (triggers.size() != 1) {
LOG(ERROR) << "Trigger " << trigger_name << " not been created properly";
return false;
}
}
if (!sensor_->SetTrigger(triggers[0])) {
LOG(ERROR) << "cannot set sensor's trigger to " << trigger_name;
return false;
}
base::FilePath trigger_now = triggers[0]->GetPath().Append("trigger_now");
std::optional<gid_t> chronos_gid = delegate_->FindGroupId("chronos");
if (!chronos_gid) {
LOG(ERROR) << "chronos group not found";
return false;
}
if (!delegate_->SetOwnership(trigger_now, -1, chronos_gid.value())) {
LOG(ERROR) << "cannot configure ownership on the trigger";
return false;
}
int permission = delegate_->GetPermissions(trigger_now);
permission |= base::FILE_PERMISSION_WRITE_BY_GROUP;
if (!delegate_->SetPermissions(trigger_now, permission)) {
LOG(ERROR) << "cannot configure permissions on the trigger";
return false;
}
LOG(INFO) << "sysfs trigger setup complete";
return true;
}
bool Configuration::EnableAccelScanElements() {
auto timestamp = sensor_->GetChannel("timestamp");
if (!timestamp) {
LOG(ERROR) << "cannot find timestamp channel";
return false;
}
if (!timestamp->SetScanElementsEnabled(false)) {
LOG(ERROR) << "failed to disable timestamp channel";
return false;
}
std::vector<std::string> channels_to_enable;
if (sensor_->IsSingleSensor()) {
for (const auto& axis : kAccelAxes) {
channels_to_enable.push_back(base::StringPrintf("accel_%s", axis));
}
} else {
for (const auto& axis : kAccelAxes) {
channels_to_enable.push_back(
base::StringPrintf("accel_%s_%s", axis, kBaseSensorLocation));
channels_to_enable.push_back(
base::StringPrintf("accel_%s_%s", axis, kLidSensorLocation));
}
}
for (const auto& chan_name : channels_to_enable) {
auto channel = sensor_->GetChannel(chan_name);
if (!channel) {
LOG(ERROR) << "cannot find channel " << chan_name;
return false;
}
if (!channel->SetScanElementsEnabled(true)) {
LOG(ERROR) << "failed to enable channel " << chan_name;
return false;
}
}
sensor_->EnableBuffer(1);
if (!sensor_->IsBufferEnabled()) {
LOG(ERROR) << "failed to enable buffer";
return false;
}
LOG(INFO) << "buffer enabled";
return true;
}
bool Configuration::EnableCalibration(bool enable) {
auto calibration = sensor_->GetChannel("calibration");
if (!calibration) {
LOG(ERROR) << "cannot find calibration channel";
return false;
}
return calibration->SetScanElementsEnabled(enable);
}
bool Configuration::EnableKeyboardAngle() {
base::FilePath kb_wake_angle =
base::FilePath("/sys/class/chromeos/cros_ec/kb_wake_angle");
if (!delegate_->Exists(kb_wake_angle)) {
LOG(INFO) << kb_wake_angle.value()
<< " not found; will not enable EC wake angle";
return true;
}
std::optional<gid_t> power_gid = delegate_->FindGroupId("power");
if (!power_gid) {
LOG(ERROR) << "cannot configure ownership on the wake angle file";
return false;
}
delegate_->SetOwnership(kb_wake_angle, -1, power_gid.value());
int permission = delegate_->GetPermissions(kb_wake_angle);
permission |= base::FILE_PERMISSION_WRITE_BY_GROUP;
delegate_->SetPermissions(kb_wake_angle, permission);
LOG(INFO) << "keyboard angle enabled";
return true;
}
bool Configuration::ConfigureOnKind() {
switch (kind_) {
case SensorKind::ACCELEROMETER:
return ConfigAccelerometer();
case SensorKind::GYROSCOPE:
return ConfigGyro();
case SensorKind::LIGHT:
return ConfigIlluminance();
case SensorKind::SYNC:
// No other configs needed.
return true;
case SensorKind::MAGNETOMETER:
// No other configs needed.
return true;
case SensorKind::LID_ANGLE:
// No other configs needed.
return true;
case SensorKind::PROXIMITY:
return ConfigProximity();
case SensorKind::BAROMETER:
// TODO(chenghaoyang): Setup calibrations for the barometer.
return true;
case SensorKind::HID_OTHERS:
// No other configs needed.
return true;
default:
CHECK(kind_ == SensorKind::OTHERS);
LOG(ERROR) << sensor_->GetName() << " unimplemented";
return false;
}
}
bool Configuration::ConfigGyro() {
CopyImuCalibationFromVpd(kGyroMaxVpdCalibration);
LOG(INFO) << "gyroscope configuration complete";
return true;
}
bool Configuration::ConfigAccelerometer() {
CopyImuCalibationFromVpd(kAccelMaxVpdCalibration);
if (!AddSysfsTrigger(kAccelSysfsTriggerId))
return false;
if (!EnableKeyboardAngle())
return false;
/*
* Gather gyroscope. If one of them is on the same plane, set
* accelerometer range to 4g to meet Android 10 CCD Requirements
* (Section 7.1.4, C.1.4).
* If no gyro found, set range to 4g on the lid accel if there are 2 accels
*/
int range = 0;
auto location = sensor_->GetLocation();
if (location && !location->empty()) {
auto gyros = context_->GetDevicesByName("cros-ec-gyro");
if (gyros.size() > 1) {
range = 4;
} else if (gyros.size() == 1) {
if (strcmp(location->c_str(), gyros[0]->GetLocation()->c_str()) == 0)
range = 4;
else
range = 2;
} else {
auto accels = context_->GetDevicesByName("cros-ec-accel");
if (accels.size() == 1)
range = 4;
else if (accels.size() > 1 &&
strcmp(location->c_str(), kLidSensorLocation) == 0)
range = 4;
else
range = 2;
}
if (!sensor_->WriteNumberAttribute(kCalibrationScale, range))
return false;
}
LOG(INFO) << "accelerometer configuration complete";
return true;
}
bool Configuration::ConfigIlluminance() {
if (strcmp(sensor_->GetName(), "acpi-als") == 0) {
std::string trigger_name =
base::StringPrintf(libmems::kHrtimerNameFormatString, sensor_->GetId());
if (context_->GetTriggersByName(trigger_name).empty()) {
base::FilePath hrtimer_path("/sys/kernel/config/iio/triggers/hrtimer");
hrtimer_path = hrtimer_path.Append(trigger_name);
if (!delegate_->Exists(hrtimer_path) &&
!delegate_->ProbeKernelModule("iio-trig-hrtimer")) {
LOG(ERROR) << "cannot load iio-trig-hrtimer module";
return false;
}
if (!delegate_->CreateDirectory(hrtimer_path)) {
LOG(ERROR) << "cannot mkdir " << hrtimer_path.value()
<< " to create the hrtimer device";
return false;
}
}
context_->Reload();
auto triggers = context_->GetTriggersByName(trigger_name);
if (triggers.empty()) {
LOG(ERROR) << "cannot find acpi-als's trigger";
return false;
}
// Don't set |trigger| as |sensor_|'s trigger, or else the samples start
// flowing.
auto trigger = triggers.front();
// /sys/bus/iio/devices/triggerX
base::FilePath sys_trg_path =
trigger->GetPath().Append(libmems::kSamplingFrequencyAttr);
SetReadPermissionAndOwnership(sys_trg_path);
SetWritePermissionAndOwnership(sys_trg_path);
}
if (!CopyLightCalibrationFromVpd())
return false;
// Disable calibration: it can fail if the light sensor does not support
// calibration mode.
EnableCalibration(false);
LOG(INFO) << "light configuration complete";
return true;
}
bool Configuration::ConfigProximity() {
auto* cros_config = delegate_->GetCrosConfig();
auto sys_path = sensor_->GetAbsoluteSysPath();
if (!sys_path.has_value()) {
LOG(ERROR) << "Invalid absolute SysPath";
return false;
}
bool isSar;
auto devlink_opt = delegate_->GetIioSarSensorDevlink(sys_path->value());
if (devlink_opt.has_value())
isSar = true;
else if (IsIioActivitySensor(sys_path->value()))
isSar = false;
else
return false;
if (isSar) {
// |devlink_opt.value()| should have prefix "/dev/proximity_" or
// "/dev/proximity-".
if (devlink_opt.value().compare(0, std::strlen(kDevlinkPrefix),
kDevlinkPrefix) != 0) {
LOG(ERROR) << "Devlink isn't in the proper format: "
<< devlink_opt.value();
return false;
}
auto sar_config_reader =
libsar::SarConfigReader(cros_config, devlink_opt.value(),
delegate_->GetSarConfigReaderDelegate());
if (!sar_config_reader.isCellular() && !sar_config_reader.isWifi()) {
LOG(ERROR) << "Invalid devlink: " << devlink_opt.value()
<< ", neither lte nor wifi";
return false;
}
auto config_dict_opt = sar_config_reader.GetSarConfigDict();
if (!config_dict_opt.has_value())
return false;
const base::Value::Dict& config_dict = config_dict_opt.value();
std::optional<double> sampling_frequency =
config_dict.FindDouble("samplingFrequency");
if (sampling_frequency.has_value()) {
if (!sensor_->WriteDoubleAttribute(libmems::kSamplingFrequencyAttr,
sampling_frequency.value())) {
LOG(ERROR) << "Could not set proximity sensor sampling frequency";
return false;
}
}
const base::Value::List* channel_list =
config_dict.FindList("channelConfig");
if (channel_list) {
// Semtech supports multiple channels, a given observer may received
// FAR/NEAR message from multiple channels.
for (const base::Value& channel : *channel_list) {
const base::Value::Dict& channel_dict = channel.GetDict();
const std::string* channel_name = channel_dict.FindString("channel");
if (!channel_name) {
LOG(ERROR) << "channel identifier required";
return false;
}
int channel_int;
if (!base::StringToInt(*channel_name, &channel_int)) {
LOG(ERROR) << "Invalid channel_name: " << channel_name;
return false;
}
std::optional<int> hardwaregain = channel_dict.FindInt("hardwaregain");
if (hardwaregain.has_value()) {
auto* iio_channel = sensor_->GetChannel("proximity" + *channel_name);
if (!iio_channel || !iio_channel->WriteNumberAttribute(
"hardwaregain", hardwaregain.value())) {
LOG(ERROR) << "Could not set proximity sensor hardware gain";
return false;
}
}
if (!SetIioRisingFallingValue(
channel_dict, "", "events/in_proximity" + *channel_name + "_",
"_value")) {
return false;
}
if (!SetIioRisingFallingValue(
channel_dict, "Hysteresis",
"events/in_proximity" + *channel_name + "_", "_hysteresis")) {
return false;
}
}
}
if (!SetIioRisingFallingValue(config_dict, "Period", "events/", "_period"))
return false;
}
LOG(INFO) << "proximity configuration complete";
return true;
}
bool Configuration::IsIioActivitySensor(const std::string& sys_path) {
return sys_path.find("-activity") != std::string::npos;
}
bool Configuration::SetIioRisingFallingValue(
const base::Value::Dict& config_dict,
const std::string& config_postfix,
const std::string& path_prefix,
const std::string& postfix) {
std::string rising_config = "threshRising" + config_postfix;
std::string falling_config = "threshFalling" + config_postfix;
std::optional<int> rising_value = config_dict.FindInt(rising_config);
std::optional<int> falling_value = config_dict.FindInt(falling_config);
if (!rising_value.has_value() && !falling_value.has_value())
return true;
bool try_either = rising_value.has_value() && falling_value.has_value() &&
falling_value.value() == rising_value.value();
std::string prefix = path_prefix + "thresh_";
std::string falling_path = prefix + "falling" + postfix;
std::string rising_path = prefix + "rising" + postfix;
std::string either_path = prefix + "either" + postfix;
if (!try_either ||
!sensor_->WriteNumberAttribute(either_path, rising_value.value())) {
if (rising_value.has_value() &&
!sensor_->WriteNumberAttribute(rising_path, rising_value.value())) {
LOG(ERROR) << "Could not set proximity sensor " << rising_path << " to "
<< rising_value.value();
return false;
}
if (falling_value.has_value() &&
!sensor_->WriteNumberAttribute(falling_path, falling_value.value())) {
LOG(ERROR) << "Could not set proximity sensor " << falling_path << " to "
<< falling_value.value();
return false;
}
}
return true;
}
bool Configuration::SetupPermissions() {
std::vector<base::FilePath> files_to_set_read_own;
std::vector<base::FilePath> files_to_set_write_own;
std::string dev_name =
libmems::IioDeviceImpl::GetStringFromId(sensor_->GetId());
// /dev/iio:deviceX
base::FilePath dev_path =
base::FilePath(libmems::kDevString).Append(dev_name.c_str());
if (!delegate_->Exists(dev_path)) {
LOG(ERROR) << "Missing path: " << dev_path.value();
return false;
}
files_to_set_read_own.push_back(dev_path);
files_to_set_write_own.push_back(dev_path);
// /sys/bus/iio/devices/iio:deviceX
base::FilePath sys_dev_path = sensor_->GetPath();
// Files under /sys/bus/iio/devices/iio:deviceX/.
auto files = delegate_->EnumerateAllFiles(sys_dev_path);
files_to_set_read_own.insert(files_to_set_read_own.end(), files.begin(),
files.end());
for (const base::FilePath& file : files) {
std::string name = file.BaseName().value();
if (RE2::FullMatch(name, "in_.*_sampling_frequency"))
files_to_set_write_own.push_back(file);
}
// Files under /sys/bus/iio/devices/iio:deviceX/scan_elements/.
files =
delegate_->EnumerateAllFiles(sys_dev_path.Append(kScanElementsString));
files_to_set_read_own.insert(files_to_set_read_own.end(), files.begin(),
files.end());
for (const base::FilePath& file : files) {
std::string name = file.BaseName().value();
if (RE2::FullMatch(name, "in_.*_en"))
files_to_set_write_own.push_back(file);
}
// Files under /sys/bus/iio/devices/iio:deviceX/events/.
files = delegate_->EnumerateAllFiles(sys_dev_path.Append(kEventsString));
files_to_set_read_own.insert(files_to_set_read_own.end(), files.begin(),
files.end());
for (const base::FilePath& file : files) {
std::string name = file.BaseName().value();
if (RE2::FullMatch(name, "in_.*_en"))
files_to_set_write_own.push_back(file);
}
for (auto file : kFilesToSetReadAndOwnership)
files_to_set_read_own.push_back(sys_dev_path.Append(file));
for (auto file : kFilesToSetWriteAndOwnership)
files_to_set_write_own.push_back(sys_dev_path.Append(file));
// Set permissions and ownerships.
bool result = true;
for (base::FilePath path : files_to_set_read_own)
result &= SetReadPermissionAndOwnership(path);
for (base::FilePath path : files_to_set_write_own)
result &= SetWritePermissionAndOwnership(path);
return result;
}
bool Configuration::SetReadPermissionAndOwnership(base::FilePath file_path) {
DCHECK(iioservice_gid_.has_value());
if (!delegate_->Exists(file_path))
return true;
bool result = true;
int permission = delegate_->GetPermissions(file_path);
permission |= base::FILE_PERMISSION_READ_BY_GROUP;
if (!delegate_->SetPermissions(file_path, permission)) {
LOG(ERROR) << "cannot configure permissions on " << file_path.value();
result = false;
}
if (!delegate_->SetOwnership(file_path, -1, iioservice_gid_.value())) {
LOG(ERROR) << "cannot configure ownership on " << file_path.value();
result = false;
}
return result;
}
bool Configuration::SetWritePermissionAndOwnership(base::FilePath file_path) {
DCHECK(iioservice_gid_.has_value());
if (!delegate_->Exists(file_path))
return true;
bool result = true;
int permission = delegate_->GetPermissions(file_path);
permission |= base::FILE_PERMISSION_WRITE_BY_GROUP;
if (!delegate_->SetPermissions(file_path, permission)) {
LOG(ERROR) << "cannot configure permissions on " << file_path.value();
result = false;
}
if (!delegate_->SetOwnership(file_path, -1, iioservice_gid_.value())) {
LOG(ERROR) << "cannot configure ownership on " << file_path.value();
result = false;
}
return result;
}
} // namespace mems_setup