| // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| // 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 <string> |
| #include <vector> |
| |
| #include <base/check.h> |
| #include <base/files/file_util.h> |
| #include <base/files/file_path.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 "mems_setup/sensor_location.h" |
| |
| namespace mems_setup { |
| |
| namespace { |
| |
| struct ImuVpdCalibrationEntry { |
| std::string name; |
| std::string calib; |
| base::Optional<int> max_value; |
| base::Optional<int> value; |
| bool missing_is_error; |
| }; |
| |
| struct LightVpdCalibrationEntry { |
| std::string vpd_name; |
| std::string iio_name; |
| }; |
| |
| struct LightColorCalibrationEntry { |
| std::string iio_name; |
| base::Optional<double> value; |
| libmems::IioChannel* chn; |
| }; |
| |
| #if USE_IIOSERVICE |
| constexpr char kIioServiceGroupName[] = "iioservice"; |
| #else |
| constexpr char kArcSensorGroupName[] = "arc-sensor"; |
| #endif // USE_IIOSERVICE |
| |
| constexpr char kCalibrationBias[] = "bias"; |
| constexpr char kCalibrationScale[] = "scale"; |
| constexpr char kSysfsTriggerPrefix[] = "sysfstrig"; |
| |
| constexpr int kGyroMaxVpdCalibration = 16384; // 16dps |
| constexpr int kAccelMaxVpdCalibration = 103; // .100g |
| constexpr int kAccelSysfsTriggerId = 0; |
| |
| constexpr int kSysfsTriggerId = -1; |
| |
| constexpr std::initializer_list<const char*> kAccelAxes = { |
| "x", |
| "y", |
| "z", |
| }; |
| |
| constexpr char kTriggerString[] = "trigger"; |
| |
| #if USE_IIOSERVICE |
| constexpr char kDevString[] = "/dev/"; |
| #endif // USE_IIOSERVICE |
| |
| 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"; |
| |
| } // namespace |
| |
| // static |
| const char* Configuration::GetGroupNameForSysfs() { |
| #if USE_IIOSERVICE |
| return kIioServiceGroupName; |
| #else |
| return kArcSensorGroupName; |
| #endif // USE_IIOSERVICE |
| } |
| |
| 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 USE_IIOSERVICE |
| // 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); |
| } |
| #endif // USE_IIOSERVICE |
| |
| // 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", base::nullopt, nullptr}, |
| {"illuminance_green", base::nullopt, nullptr}, |
| {"illuminance_blue", base::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_->ReadStringAttribute("location"); |
| 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, base::nullopt, true}, |
| {"y", kCalibrationBias, max_value, base::nullopt, true}, |
| {"z", kCalibrationBias, max_value, base::nullopt, true}, |
| |
| {"x", kCalibrationScale, base::nullopt, base::nullopt, false}, |
| {"y", kCalibrationScale, base::nullopt, base::nullopt, false}, |
| {"z", kCalibrationScale, base::nullopt, base::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"); |
| |
| base::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; |
| } |
| |
| base::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::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 (!USE_IIOSERVICE && !EnableAccelScanElements()) |
| 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. |
| */ |
| int range = 0; |
| auto location = sensor_->ReadStringAttribute("location"); |
| if (location && !location->empty()) { |
| auto gyros = context_->GetDevicesByName("cros-ec-gyro"); |
| if (gyros.size() != 1 && strcmp(location->c_str(), kLidSensorLocation) == 0) |
| range = 4; |
| else if (gyros.size() == 1 && |
| strcmp(location->c_str(), |
| gyros[0]->ReadStringAttribute("location")->c_str()) == 0) |
| range = 4; |
| else |
| range = 2; |
| |
| if (!sensor_->WriteNumberAttribute(kCalibrationScale, range)) |
| return false; |
| } |
| |
| LOG(INFO) << "accelerometer configuration complete"; |
| return true; |
| } |
| |
| bool Configuration::ConfigIlluminance() { |
| if (USE_IIOSERVICE && 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::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()); |
| #if USE_IIOSERVICE |
| // /dev/iio:deviceX |
| base::FilePath dev_path = base::FilePath(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); |
| #endif // USE_IIOSERVICE |
| |
| // /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); |
| } |
| |
| 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 |