blob: 7a9cb8cfe47f6e6ac7324a74157e9a47cdf4dabe [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 <fuchsia/device/power/test/llcpp/fidl.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/spawn.h>
#include <lib/fdio/unsafe.h>
#include <lib/fdio/watcher.h>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/platform-defs.h>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_call.h>
using llcpp::fuchsia::device::DevicePerformanceStateInfo;
using llcpp::fuchsia::device::DevicePowerStateInfo;
using llcpp::fuchsia::device::power::test::TestDevice;
class TestPowerDriverChild;
using DeviceType = ddk::Device<TestPowerDriverChild, ddk::UnbindableNew, ddk::Messageable,
ddk::Suspendable, ddk::Resumable, ddk::PerformanceTunable,
ddk::AutoSuspendable, ddk::Initializable>;
class TestPowerDriverChild : public DeviceType, public TestDevice::Interface {
public:
TestPowerDriverChild(zx_device_t* parent) : DeviceType(parent) {}
static zx_status_t Create(void* ctx, zx_device_t* device);
zx_status_t Bind();
void AddDeviceWithPowerArgs(::fidl::VectorView<DevicePowerStateInfo> info,
::fidl::VectorView<DevicePerformanceStateInfo> perf_states,
bool add_invisible,
AddDeviceWithPowerArgsCompleter::Sync completer) override;
void GetCurrentDevicePowerState(GetCurrentDevicePowerStateCompleter::Sync completer) override;
void GetCurrentSuspendReason(GetCurrentSuspendReasonCompleter::Sync completer) override;
void GetCurrentDeviceAutoSuspendConfig(
GetCurrentDeviceAutoSuspendConfigCompleter::Sync completer) override;
void SetTestStatusInfo(::llcpp::fuchsia::device::power::test::TestStatusInfo test_info,
SetTestStatusInfoCompleter::Sync completer) override;
void DdkUnbindNew(ddk::UnbindTxn txn) { txn.Reply(); }
zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
DdkTransaction transaction(txn);
::llcpp::fuchsia::device::power::test::TestDevice::Dispatch(this, msg, &transaction);
return transaction.Status();
}
void DdkInit(ddk::InitTxn txn);
void DdkRelease() { delete this; }
void DdkSuspend(ddk::SuspendTxn txn);
zx_status_t DdkSetPerformanceState(uint32_t requested_state, uint32_t* out_state);
void DdkResume(ddk::ResumeTxn txn);
zx_status_t DdkConfigureAutoSuspend(bool enable, uint8_t deepest_sleep_state);
void SavePowerStateInfo(std::unique_ptr<device_power_state_info_t[]> states, uint8_t states_count,
std::unique_ptr<device_performance_state_info_t[]> perf_states,
uint8_t perf_states_count) {
states_ = std::move(states);
states_count_ = states_count;
perf_states_ = std::move(perf_states);
perf_states_count_ = perf_states_count;
}
private:
uint8_t current_power_state_ = 0;
uint32_t current_performance_state_ = 0;
uint8_t auto_suspend_sleep_state_ = 0;
bool auto_suspend_enabled_ = false;
uint8_t current_suspend_reason_ = 0;
zx_status_t reply_suspend_status_ = ZX_OK;
zx_status_t reply_resume_status_ = ZX_OK;
uint8_t reply_out_power_state_ = DEV_POWER_STATE_D0;
uint32_t reply_out_performance_state_ = DEV_PERFORMANCE_STATE_P0;
std::unique_ptr<device_power_state_info_t[]> states_;
uint8_t states_count_ = 0;
std::unique_ptr<device_performance_state_info_t[]> perf_states_;
uint8_t perf_states_count_ = 0;
};
void TestPowerDriverChild::DdkInit(ddk::InitTxn txn) {
txn.Reply(ZX_OK, states_.get(), states_count_, perf_states_.get(), perf_states_count_);
}
void TestPowerDriverChild::DdkSuspend(ddk::SuspendTxn txn) {
if (reply_suspend_status_ == ZX_OK) {
reply_out_power_state_ = txn.requested_state();
}
current_suspend_reason_ = txn.suspend_reason();
current_power_state_ = reply_out_power_state_;
txn.Reply(reply_suspend_status_, reply_out_power_state_);
reply_suspend_status_ = ZX_OK;
reply_out_power_state_ = DEV_POWER_STATE_D0;
reply_out_performance_state_ = DEV_PERFORMANCE_STATE_P0;
}
zx_status_t TestPowerDriverChild::DdkSetPerformanceState(uint32_t requested_state,
uint32_t* out_state) {
current_performance_state_ = requested_state;
*out_state = requested_state;
return ZX_OK;
}
void TestPowerDriverChild::DdkResume(ddk::ResumeTxn txn) {
if (reply_resume_status_ == ZX_OK) {
reply_out_power_state_ = DEV_POWER_STATE_D0;
reply_out_performance_state_ = txn.requested_state();
}
current_power_state_ = reply_out_power_state_;
current_performance_state_ = reply_out_performance_state_;
// In a successful response, power state is a working state.
txn.Reply(reply_resume_status_, reply_out_power_state_, reply_out_performance_state_);
reply_resume_status_ = ZX_OK;
reply_out_power_state_ = DEV_POWER_STATE_D0;
reply_out_performance_state_ = DEV_PERFORMANCE_STATE_P0;
}
zx_status_t TestPowerDriverChild::DdkConfigureAutoSuspend(bool enable,
uint8_t deepest_sleep_state) {
auto_suspend_enabled_ = enable;
auto_suspend_sleep_state_ = deepest_sleep_state;
return ZX_OK;
}
void TestPowerDriverChild::AddDeviceWithPowerArgs(
::fidl::VectorView<DevicePowerStateInfo> info,
::fidl::VectorView<DevicePerformanceStateInfo> perf_states, bool add_invisible,
AddDeviceWithPowerArgsCompleter::Sync completer) {
fbl::AllocChecker ac;
auto child2 = fbl::make_unique_checked<TestPowerDriverChild>(&ac, this->parent());
if (!ac.check()) {
completer.ReplyError(ZX_ERR_NO_MEMORY);
return;
}
auto state_info = info.data();
auto states = std::make_unique<device_power_state_info_t[]>(info.count());
auto count = static_cast<uint8_t>(info.count());
for (uint8_t i = 0; i < count; i++) {
states[i].state_id = static_cast<fuchsia_device_DevicePowerState>(state_info[i].state_id);
states[i].restore_latency = state_info[i].restore_latency;
states[i].wakeup_capable = state_info[i].wakeup_capable;
states[i].system_wake_state = state_info[i].system_wake_state;
}
auto perf_state_info = perf_states.data();
auto performance_states =
std::make_unique<device_performance_state_info_t[]>(perf_states.count());
auto perf_state_count = static_cast<uint8_t>(perf_states.count());
for (uint8_t i = 0; i < perf_state_count; i++) {
performance_states[i].state_id = perf_state_info[i].state_id;
performance_states[i].restore_latency = perf_state_info[i].restore_latency;
}
zx_status_t status;
if (!add_invisible) {
status = child2->DdkAdd("power-test-child-2", 0, nullptr, 0, 0, nullptr, ZX_HANDLE_INVALID,
states.get(), count, performance_states.get(), perf_state_count);
} else {
child2->SavePowerStateInfo(std::move(states), count, std::move(performance_states),
perf_state_count);
status = child2->DdkAdd("power-test-child-2", DEVICE_ADD_INVISIBLE);
}
if (status != ZX_OK) {
completer.ReplyError(status);
} else {
completer.ReplySuccess();
__UNUSED auto ptr = child2.release();
}
}
void TestPowerDriverChild::SetTestStatusInfo(
llcpp::fuchsia::device::power::test::TestStatusInfo status_info,
SetTestStatusInfoCompleter::Sync completer) {
reply_suspend_status_ = status_info.suspend_status;
reply_resume_status_ = status_info.resume_status;
reply_out_power_state_ = status_info.out_power_state;
reply_out_performance_state_ = status_info.out_performance_state;
completer.ReplySuccess();
}
void TestPowerDriverChild::GetCurrentDevicePowerState(
GetCurrentDevicePowerStateCompleter::Sync completer) {
completer.ReplySuccess(
static_cast<llcpp::fuchsia::device::DevicePowerState>(current_power_state_));
}
void TestPowerDriverChild::GetCurrentSuspendReason(
GetCurrentSuspendReasonCompleter::Sync completer) {
completer.ReplySuccess(current_suspend_reason_);
}
void TestPowerDriverChild::GetCurrentDeviceAutoSuspendConfig(
GetCurrentDeviceAutoSuspendConfigCompleter::Sync completer) {
completer.ReplySuccess(
auto_suspend_enabled_,
static_cast<llcpp::fuchsia::device::DevicePowerState>(auto_suspend_sleep_state_));
}
zx_status_t TestPowerDriverChild::Bind() { return DdkAdd("power-test-child"); }
zx_status_t TestPowerDriverChild::Create(void* ctx, zx_device_t* device) {
fbl::AllocChecker ac;
auto dev = fbl::make_unique_checked<TestPowerDriverChild>(&ac, device);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
auto status = dev->Bind();
if (status == ZX_OK) {
// devmgr is now in charge of the memory for dev
__UNUSED auto ptr = dev.release();
}
return status;
}
static zx_driver_ops_t test_power_child_driver_ops = []() -> zx_driver_ops_t {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = TestPowerDriverChild::Create;
return ops;
}();
// clang-format off
ZIRCON_DRIVER_BEGIN(TestPowerChild, test_power_child_driver_ops, "zircon", "0.1", 1)
BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_TEST_POWER_CHILD),
ZIRCON_DRIVER_END(TestPowerChild)
// clang-format on