blob: c11858b433b828e716a32043943de1c266683ef7 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "rgbkbd/rgbkbd_daemon.h"
#include <memory>
#include <string>
#include <utility>
#include <base/task/single_thread_task_runner.h>
#include <chromeos/dbus/service_constants.h>
#include <chromeos-config/libcros_config/cros_config_interface.h>
#include <dbus/bus.h>
#include <dbus/rgbkbd/dbus-constants.h>
#include <libcrossystem/crossystem.h>
#include "base/check.h"
#include "base/files/file_path.h"
#include "rgbkbd/internal_rgb_keyboard.h"
#include "rgbkbd/keyboard_backlight_logger.h"
namespace {
constexpr char kLogFilePathForTesting[] = "/run/rgbkbd/log";
bool IsDevMode(crossystem::Crossystem* crossystem) {
std::optional<int> value = crossystem->VbGetSystemPropertyInt("cros_debug");
if (!value) {
LOG(INFO) << "crossystem cros_debug value: Empty";
} else {
LOG(INFO) << "crossystem cros_debug value: " << *value;
}
return value && *value == 1;
}
} // namespace
namespace rgbkbd {
DBusAdaptor::DBusAdaptor(scoped_refptr<dbus::Bus> bus,
brillo::CrosConfigInterface* cros_config,
crossystem::Crossystem* crossystem,
RgbkbdDaemon* daemon)
: org::chromium::RgbkbdAdaptor(this),
dbus_object_(nullptr, bus, dbus::ObjectPath(kRgbkbdServicePath)),
internal_keyboard_(std::make_unique<InternalRgbKeyboard>()),
rgb_keyboard_controller_(internal_keyboard_.get()),
cros_config_(cros_config),
crossystem_(crossystem),
daemon_(daemon) {}
DBusAdaptor::~DBusAdaptor() {
if (usb_device_event_notifier_) {
usb_device_event_notifier_->RemoveObserver(&rgb_keyboard_controller_);
}
}
void DBusAdaptor::RegisterAsync(
brillo::dbus_utils::AsyncEventSequencer::CompletionAction cb) {
RegisterWithDBusObject(&dbus_object_);
dbus_object_.RegisterAsync(std::move(cb));
}
void DBusAdaptor::InitializeForPrismUsbKeyboard() {
DCHECK(!udev_);
DCHECK(!usb_device_event_notifier_);
// A null cros_config is valid for testing.
if (!cros_config_) {
return;
}
std::string value;
const bool has_prism_usb_controller =
cros_config_->GetString("/keyboard", "mcutype", &value) &&
value == "prism_rgb_controller";
if (has_prism_usb_controller) {
LOG(INFO) << "Detected we are running with a prism kbmcu. Starting USB "
"event observing...";
udev_ = brillo::Udev::Create();
if (!udev_) {
LOG(ERROR) << "Could not create udev library context.";
return;
}
usb_device_event_notifier_ =
std::make_unique<brillo::UsbDeviceEventNotifier>(udev_.get());
if (!usb_device_event_notifier_->Initialize()) {
LOG(ERROR) << "Could not initialize USB device event notification.";
udev_.reset();
usb_device_event_notifier_.reset();
return;
}
rgb_keyboard_controller_.SetKeyboardCapabilityAsIndividualKey();
// Add the rgb controller as an observer and scan all connected devices to
// the system to properly initialize it.
usb_device_event_notifier_->AddObserver(&rgb_keyboard_controller_);
usb_device_event_notifier_->ScanExistingDevices();
}
}
void DBusAdaptor::GetRgbKeyboardCapabilities(
std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<uint32_t>>
response) {
const uint32_t capabilities =
rgb_keyboard_controller_.GetRgbKeyboardCapabilities();
response->Return(capabilities);
// After we return capabilities we want to schedule the Daemon to quit.
// DBusServiceDaemon runs tasks based on a sequential message loop so it is
// guaranteed RgbkbdDaemon will exit only after all tasks are completed.
// Note that a nullptr `daemon_` is valid for tests, tests will own lifetime
// of the daemon.
if (daemon_ &&
capabilities == static_cast<uint32_t>(RgbKeyboardCapabilities::kNone)) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&RgbkbdDaemon::Quit, base::Unretained(daemon_)));
}
}
void DBusAdaptor::SetCapsLockState(bool enabled) {
rgb_keyboard_controller_.SetCapsLockState(enabled);
}
void DBusAdaptor::SetStaticBackgroundColor(uint8_t r, uint8_t g, uint8_t b) {
rgb_keyboard_controller_.SetStaticBackgroundColor(r, g, b);
}
void DBusAdaptor::SetRainbowMode() {
rgb_keyboard_controller_.SetRainbowMode();
}
void DBusAdaptor::SetZoneColor(int zone_idx, uint8_t r, uint8_t g, uint8_t b) {
rgb_keyboard_controller_.SetStaticZoneColor(zone_idx, r, g, b);
}
void DBusAdaptor::SetTestingMode(bool enable_testing, uint32_t capability) {
// Null crossystem is valid for testing.
if (crossystem_ && !IsDevMode(crossystem_)) {
return;
}
if (enable_testing) {
if (capability >
static_cast<uint32_t>(RgbKeyboardCapabilities::kMaxValue)) {
LOG(ERROR)
<< "Attempted to set unsupported capability. Defaulting to kNone.";
capability = static_cast<uint32_t>(RgbKeyboardCapabilities::kNone);
}
const auto keyboard_capability =
static_cast<RgbKeyboardCapabilities>(capability);
if (!logger_keyboard_) {
logger_keyboard_ = std::make_unique<KeyboardBacklightLogger>(
base::FilePath(kLogFilePathForTesting), keyboard_capability);
}
rgb_keyboard_controller_.SetKeyboardClient(logger_keyboard_.get());
rgb_keyboard_controller_.SetKeyboardCapabilityForTesting(
keyboard_capability);
SendCapabilityUpdatedForTestingSignal(capability);
} else {
DCHECK(internal_keyboard_);
rgb_keyboard_controller_.SetKeyboardClient(internal_keyboard_.get());
}
}
// TODO(jimmyxgong): Implement switch case for different modes.
void DBusAdaptor::SetAnimationMode(uint32_t mode) {
rgb_keyboard_controller_.SetAnimationMode(
RgbAnimationMode::kBasicTestPattern);
}
RgbkbdDaemon::RgbkbdDaemon() : DBusServiceDaemon(kRgbkbdServiceName) {}
void RgbkbdDaemon::RegisterDBusObjectsAsync(
brillo::dbus_utils::AsyncEventSequencer* sequencer) {
adaptor_.reset(new DBusAdaptor(bus_, &cros_config_, &crossystem_, this));
adaptor_->RegisterAsync(
sequencer->GetHandler("RegisterAsync() failed", true));
adaptor_->InitializeForPrismUsbKeyboard();
}
} // namespace rgbkbd