blob: 6ce65c69221e8a66b930e49fba464d12ac88bc84 [file] [log] [blame]
/*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ledstatus.h"
#include <string>
#include <base/files/file_path.h>
#include <base/format_macros.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <brillo/streams/file_stream.h>
#include <brillo/streams/stream_utils.h>
namespace {
brillo::StreamPtr GetLEDDataStream(size_t index, bool write) {
CHECK(index < LedStatus::num_leds);
std::string led_path;
if( index == 3 ) {
led_path = "/sys/class/leds/boot/brightness";
} else {
led_path = base::StringPrintf("/sys/class/leds/led%" PRIuS "/brightness",
index + 1);
}
base::FilePath dev_path{led_path};
auto access_mode = brillo::stream_utils::MakeAccessMode(!write, write);
return brillo::FileStream::Open(
dev_path, access_mode, brillo::FileStream::Disposition::OPEN_EXISTING,
nullptr);
}
} // anonymous namespace
LedStatus::LedStatus() {
// Try to open the lights hal.
int ret = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, &lights_hal_);
if (ret) {
LOG(ERROR) << "Failed to load the lights HAL.";
return;
}
CHECK(lights_hal_);
LOG(INFO) << "Loaded lights HAL.";
// If we can open the hal, then we map each number from 1 - 4 to one of the
// leds available on the board. Note, multiple numbers could be mapped to the
// same led.
const std::initializer_list<const char*> kLogicalLights = {
LIGHT_ID_BACKLIGHT, LIGHT_ID_KEYBOARD, LIGHT_ID_BUTTONS, LIGHT_ID_BATTERY,
LIGHT_ID_NOTIFICATIONS, LIGHT_ID_ATTENTION, LIGHT_ID_BLUETOOTH,
LIGHT_ID_WIFI};
size_t led_index = 0;
for (const char* light_name : kLogicalLights) {
light_device_t* light_device = nullptr;
ret = lights_hal_->methods->open(
lights_hal_, light_name,
reinterpret_cast<hw_device_t**>(&light_device));
// If a given light device couldn't be opened, don't map it to a number.
if (ret || !light_device) {
continue;
}
hal_led_map_.emplace(led_index, light_name);
led_index++;
if (led_index == num_leds) {
// We have already mapped all num_leds LEDs.
break;
}
}
// If the size of the map is zero, then the lights hal doesn't have any valid
// leds.
if (hal_led_map_.empty()) {
LOG(ERROR) << "Unable to open any light devices using the hal.";
lights_hal_ = nullptr;
return;
}
// If not all 4 numbers have been mapped, then we map them to the first led
// mapped.
for (size_t i = hal_led_map_.size(); i < num_leds; i++) {
hal_led_map_.emplace(i, hal_led_map_[0]);
}
hal_led_status_.resize(num_leds);
}
std::vector<bool> LedStatus::GetStatus() const {
if (lights_hal_)
return hal_led_status_;
std::vector<bool> leds(num_leds);
for (size_t index = 0; index < num_leds; index++)
leds[index] = IsLedOn(index);
return leds;
}
bool LedStatus::IsLedOn(size_t index) const {
if (lights_hal_)
return hal_led_status_[index];
brillo::StreamPtr stream = GetLEDDataStream(index, false);
if (!stream)
return false;
char buffer[10];
size_t size_read = 0;
if (!stream->ReadNonBlocking(buffer, sizeof(buffer), &size_read, nullptr,
nullptr)) {
return false;
}
std::string value{buffer, size_read};
base::TrimWhitespaceASCII(value, base::TrimPositions::TRIM_ALL, &value);
int brightness = 0;
if (!base::StringToInt(value, &brightness))
return false;
return brightness > 0;
}
void LedStatus::SetLedStatus(size_t index, bool on) {
if (lights_hal_) {
light_state_t state = {};
state.color = on;
state.flashMode = LIGHT_FLASH_NONE;
state.flashOnMS = 0;
state.flashOffMS = 0;
state.brightnessMode = BRIGHTNESS_MODE_USER;
light_device_t* light_device = nullptr;
int rc = lights_hal_->methods->open(
lights_hal_, hal_led_map_[index].c_str(),
reinterpret_cast<hw_device_t**>(&light_device));
if (rc) {
LOG(ERROR) << "Unable to open " << hal_led_map_[index];
return;
}
CHECK(light_device);
rc = light_device->set_light(light_device, &state);
if (rc) {
LOG(ERROR) << "Unable to set " << hal_led_map_[index];
return;
}
hal_led_status_[index] = on;
light_device->common.close(
reinterpret_cast<hw_device_t*>(light_device));
if (rc) {
LOG(ERROR) << "Unable to close " << hal_led_map_[index];
return;
}
return;
}
brillo::StreamPtr stream = GetLEDDataStream(index, true);
if (!stream)
return;
std::string brightness = on ? "255" : "0";
stream->WriteAllBlocking(brightness.data(), brightness.size(), nullptr);
}