blob: 94744e8b36a8d1c1165dfab76f2e44e80428e18e [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 <fidl/fuchsia.device.power.test/cpp/wire.h>
#include <lib/ddk/binding_driver.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/device.h>
#include <lib/ddk/driver.h>
#include <lib/ddk/platform-defs.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 <ddktl/device.h>
#include <ddktl/fidl.h>
#include <fbl/alloc_checker.h>
using fuchsia_device::wire::DevicePowerState;
using fuchsia_device::wire::DevicePowerStateInfo;
using fuchsia_device_power_test::TestDevice;
class TestPowerDriverChild;
using DeviceType =
ddk::Device<TestPowerDriverChild, ddk::Messageable<TestDevice>::Mixin, ddk::Suspendable,
ddk::Resumable, ddk::AutoSuspendable, ddk::Initializable>;
class TestPowerDriverChild : public DeviceType {
public:
TestPowerDriverChild(zx_device_t* parent) : DeviceType(parent) {
zx::event::create(0, &suspend_completion_event_);
}
static zx_status_t Create(void* ctx, zx_device_t* device);
zx_status_t Bind();
void AddDeviceWithPowerArgs(AddDeviceWithPowerArgsRequestView request,
AddDeviceWithPowerArgsCompleter::Sync& completer) override;
void GetCurrentDevicePowerState(GetCurrentDevicePowerStateCompleter::Sync& completer) override;
void GetCurrentSuspendReason(GetCurrentSuspendReasonCompleter::Sync& completer) override;
void GetCurrentDeviceAutoSuspendConfig(
GetCurrentDeviceAutoSuspendConfigCompleter::Sync& completer) override;
void SetTestStatusInfo(SetTestStatusInfoRequestView request,
SetTestStatusInfoCompleter::Sync& completer) override;
void DdkInit(ddk::InitTxn txn);
void DdkRelease() { delete this; }
void DdkSuspend(ddk::SuspendTxn txn);
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) {
states_ = std::move(states);
states_count_ = states_count;
}
void GetSuspendCompletionEvent(GetSuspendCompletionEventCompleter::Sync& completer) override {
zx::event complete;
zx_status_t status =
suspend_completion_event_.duplicate(ZX_RIGHT_WAIT | ZX_RIGHT_TRANSFER, &complete);
if (status != ZX_OK) {
completer.ReplyError(status);
return;
}
completer.ReplySuccess(std::move(complete));
}
private:
uint8_t current_power_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;
zx::event suspend_completion_event_;
};
void TestPowerDriverChild::DdkInit(ddk::InitTxn txn) {
txn.Reply(ZX_OK, states_.get(), 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_;
suspend_completion_event_.signal(0, ZX_USER_SIGNAL_0);
txn.Reply(reply_suspend_status_, reply_out_power_state_);
// Reset for next test.
reply_suspend_status_ = ZX_OK;
reply_out_power_state_ = DEV_POWER_STATE_D0;
reply_out_performance_state_ = DEV_PERFORMANCE_STATE_P0;
}
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_;
// 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(
AddDeviceWithPowerArgsRequestView request, 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 = request->info.data();
auto states = std::make_unique<device_power_state_info_t[]>(request->info.count());
auto count = static_cast<uint8_t>(request->info.count());
for (uint8_t i = 0; i < count; i++) {
states[i].state_id = static_cast<device_power_state_t>(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;
}
zx_status_t status;
if (!request->make_visible) {
status = child2->DdkAdd(
ddk::DeviceAddArgs("power-test-child-2").set_power_states({states.get(), count}));
} else {
child2->SavePowerStateInfo(std::move(states), count);
status = child2->DdkAdd("power-test-child-2");
}
if (status != ZX_OK) {
completer.ReplyError(status);
} else {
completer.ReplySuccess();
[[maybe_unused]] auto ptr = child2.release();
}
}
void TestPowerDriverChild::SetTestStatusInfo(SetTestStatusInfoRequestView request,
SetTestStatusInfoCompleter::Sync& completer) {
reply_suspend_status_ = request->test_info.suspend_status;
reply_resume_status_ = request->test_info.resume_status;
reply_out_power_state_ = request->test_info.out_power_state;
reply_out_performance_state_ = request->test_info.out_performance_state;
completer.ReplySuccess();
}
void TestPowerDriverChild::GetCurrentDevicePowerState(
GetCurrentDevicePowerStateCompleter::Sync& completer) {
completer.ReplySuccess(static_cast<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<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
[[maybe_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;
}();
ZIRCON_DRIVER(TestPowerChild, test_power_child_driver_ops, "zircon", "0.1");