blob: acd4b95e3279d2c9ca92f0df70273df96d4166b2 [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/llcpp/fidl.h>
#include <fuchsia/device/manager/c/fidl.h>
#include <fuchsia/device/manager/llcpp/fidl.h>
#include <fuchsia/device/power/test/llcpp/fidl.h>
#include <lib/driver-integration-test/fixture.h>
#include <lib/fdio/directory.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <ddk/platform-defs.h>
#include <zxtest/zxtest.h>
using driver_integration_test::IsolatedDevmgr;
using llcpp::fuchsia::device::Controller;
using llcpp::fuchsia::device::DevicePowerState;
using llcpp::fuchsia::device::DevicePowerStateInfo;
using llcpp::fuchsia::device::MAX_DEVICE_POWER_STATES;
using llcpp::fuchsia::device::SystemPowerStateInfo;
using llcpp::fuchsia::device::manager::Administrator;
using llcpp::fuchsia::device::manager::MAX_SYSTEM_POWER_STATES;
using llcpp::fuchsia::device::manager::SystemPowerState;
using llcpp::fuchsia::device::power::test::TestDevice;
class PowerTestCase : public zxtest::Test {
public:
~PowerTestCase() override = default;
void SetUp() override {
IsolatedDevmgr::Args args;
args.load_drivers.push_back("/boot/driver/ddk-power-test.so");
args.load_drivers.push_back("/boot/driver/ddk-power-test-child.so");
board_test::DeviceEntry dev = {};
dev.vid = PDEV_VID_TEST;
dev.pid = PDEV_PID_POWER_TEST;
dev.did = 0;
args.device_list.push_back(dev);
zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr);
ASSERT_OK(status);
fbl::unique_fd parent_fd, child_fd;
ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0b:0/power-test", &parent_fd));
ASSERT_GT(parent_fd.get(), 0);
ASSERT_OK(
fdio_get_service_handle(parent_fd.release(), parent_device_handle.reset_and_get_address()));
ASSERT_NE(parent_device_handle.get(), ZX_HANDLE_INVALID);
ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0b:0/power-test/power-test-child", &child_fd));
ASSERT_GT(child_fd.get(), 0);
ASSERT_OK(
fdio_get_service_handle(child_fd.release(), child_device_handle.reset_and_get_address()));
ASSERT_NE(child_device_handle.get(), ZX_HANDLE_INVALID);
}
void AddChildWithPowerArgs(DevicePowerStateInfo *states, uint8_t count) {
auto power_states = ::fidl::VectorView(states, count);
auto response =
TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned(child_device_handle), power_states);
ASSERT_OK(response.status());
zx_status_t call_status = ZX_OK;
if (response->result.is_err()) {
call_status = response->result.err();
}
ASSERT_OK(call_status);
fbl::unique_fd child2_fd;
ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0b:0/power-test/power-test-child-2", &child2_fd));
ASSERT_GT(child2_fd.get(), 0);
ASSERT_OK(
fdio_get_service_handle(child2_fd.release(), child2_device_handle.reset_and_get_address()));
ASSERT_NE(child2_device_handle.get(), ZX_HANDLE_INVALID);
}
zx::channel child_device_handle;
zx::channel parent_device_handle;
zx::channel child2_device_handle;
IsolatedDevmgr devmgr;
};
TEST_F(PowerTestCase, InvalidDevicePowerCaps_Less) {
DevicePowerStateInfo states[1];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
states[0].is_supported = true;
auto power_states = ::fidl::VectorView(states);
auto response =
TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned(child_device_handle), power_states);
ASSERT_OK(response.status());
zx_status_t call_status = ZX_OK;
if (response->result.is_err()) {
call_status = response->result.err();
}
ASSERT_STATUS(call_status, ZX_ERR_INVALID_ARGS);
}
TEST_F(PowerTestCase, InvalidDevicePowerCaps_More) {
DevicePowerStateInfo states[MAX_DEVICE_POWER_STATES + 1];
for (uint8_t i = 0; i < MAX_DEVICE_POWER_STATES + 1; i++) {
states[i].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
states[i].is_supported = true;
}
auto power_states = ::fidl::VectorView(states);
auto response =
TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned(child_device_handle), power_states);
ASSERT_OK(response.status());
zx_status_t call_status = ZX_OK;
if (response->result.is_err()) {
call_status = response->result.err();
}
ASSERT_STATUS(call_status, ZX_ERR_INVALID_ARGS);
}
TEST_F(PowerTestCase, InvalidDevicePowerCaps_MissingRequired) {
DevicePowerStateInfo states[MAX_DEVICE_POWER_STATES];
for (uint8_t i = 0; i < MAX_DEVICE_POWER_STATES; i++) {
// Missing D0 and D3COLD
states[i].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
states[i].is_supported = true;
}
auto power_states = ::fidl::VectorView(states);
auto response =
TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned(child_device_handle), power_states);
ASSERT_OK(response.status());
zx_status_t call_status = ZX_OK;
if (response->result.is_err()) {
call_status = response->result.err();
}
ASSERT_STATUS(call_status, ZX_ERR_INVALID_ARGS);
}
TEST_F(PowerTestCase, InvalidDevicePowerCaps_DuplicateCaps) {
DevicePowerStateInfo states[MAX_DEVICE_POWER_STATES];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[1].is_supported = true;
// Repeat
states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[2].is_supported = true;
auto power_states = ::fidl::VectorView(states);
auto response =
TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned(child_device_handle), power_states);
ASSERT_OK(response.status());
zx_status_t call_status = ZX_OK;
if (response->result.is_err()) {
call_status = response->result.err();
}
ASSERT_STATUS(call_status, ZX_ERR_INVALID_ARGS);
}
TEST_F(PowerTestCase, AddDevicePowerCaps_Success) {
DevicePowerStateInfo states[2];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[1].is_supported = true;
auto power_states = ::fidl::VectorView(states);
auto response =
TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned(child_device_handle), power_states);
ASSERT_OK(response.status());
zx_status_t call_status = ZX_OK;
if (response->result.is_err()) {
call_status = response->result.err();
}
ASSERT_STATUS(call_status, ZX_OK);
}
TEST_F(PowerTestCase, GetDevicePowerCaps_Success) {
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, fbl::count_of(states));
const DevicePowerStateInfo *out_dpstates;
auto response2 = Controller::Call::GetDevicePowerCaps(zx::unowned(child2_device_handle));
ASSERT_OK(response2.status());
zx_status_t call_status = ZX_OK;
if (response2->result.is_err()) {
call_status = response2->result.err();
}
ASSERT_OK(call_status);
out_dpstates = &response2->result.response().dpstates[0];
ASSERT_TRUE(
out_dpstates[static_cast<uint8_t>(DevicePowerState::DEVICE_POWER_STATE_D0)].is_supported);
ASSERT_TRUE(
out_dpstates[static_cast<uint8_t>(DevicePowerState::DEVICE_POWER_STATE_D1)].is_supported);
ASSERT_EQ(
out_dpstates[static_cast<uint8_t>(DevicePowerState::DEVICE_POWER_STATE_D1)].restore_latency,
100);
ASSERT_TRUE(
out_dpstates[static_cast<uint8_t>(DevicePowerState::DEVICE_POWER_STATE_D3COLD)].is_supported);
ASSERT_EQ(out_dpstates[static_cast<uint8_t>(DevicePowerState::DEVICE_POWER_STATE_D3COLD)]
.restore_latency,
1000);
}
TEST_F(PowerTestCase, Suspend_Success) {
// Add Capabilities
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, fbl::count_of(states));
auto suspend_result = Controller::Call::Suspend(zx::unowned(child2_device_handle),
DevicePowerState::DEVICE_POWER_STATE_D3COLD);
ASSERT_OK(suspend_result.status());
const auto &suspend_response = suspend_result.value();
ASSERT_OK(suspend_response.status);
ASSERT_EQ(suspend_response.out_state, DevicePowerState::DEVICE_POWER_STATE_D3COLD);
auto response2 = TestDevice::Call::GetCurrentDevicePowerState(zx::unowned(child2_device_handle));
ASSERT_OK(response2.status());
zx_status_t call_status = ZX_OK;
if (response2->result.is_err()) {
call_status = response2->result.err();
}
ASSERT_OK(call_status);
ASSERT_EQ(response2->result.response().cur_state, DevicePowerState::DEVICE_POWER_STATE_D3COLD);
}
TEST_F(PowerTestCase, Resume_Success) {
// Add Capabilities
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, fbl::count_of(states));
auto suspend_result = Controller::Call::Suspend(zx::unowned(child2_device_handle),
DevicePowerState::DEVICE_POWER_STATE_D3COLD);
ASSERT_OK(suspend_result.status());
const auto &suspend_response = suspend_result.value();
ASSERT_OK(suspend_response.status);
ASSERT_EQ(suspend_response.out_state, DevicePowerState::DEVICE_POWER_STATE_D3COLD);
auto response2 = TestDevice::Call::GetCurrentDevicePowerState(zx::unowned(child2_device_handle));
ASSERT_OK(response2.status());
zx_status_t call_status = ZX_OK;
if (response2->result.is_err()) {
call_status = response2->result.err();
}
ASSERT_OK(call_status);
ASSERT_EQ(response2->result.response().cur_state, DevicePowerState::DEVICE_POWER_STATE_D3COLD);
auto resume_result = Controller::Call::Resume(zx::unowned(child2_device_handle),
DevicePowerState::DEVICE_POWER_STATE_D0);
ASSERT_OK(resume_result.status());
call_status = ZX_OK;
if (resume_result->result.is_err()) {
call_status = resume_result->result.err();
}
ASSERT_OK(call_status);
ASSERT_EQ(resume_result->result.response().out_state, DevicePowerState::DEVICE_POWER_STATE_D0);
auto response3 = TestDevice::Call::GetCurrentDevicePowerState(zx::unowned(child2_device_handle));
ASSERT_OK(response3.status());
call_status = ZX_OK;
if (response3->result.is_err()) {
call_status = response3->result.err();
}
ASSERT_OK(call_status);
ASSERT_EQ(response3->result.response().cur_state, DevicePowerState::DEVICE_POWER_STATE_D0);
}
TEST_F(PowerTestCase, DefaultSystemPowerStatesMapping) {
// Add Capabilities
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, fbl::count_of(states));
const SystemPowerStateInfo *states_mapping;
auto response2 = Controller::Call::GetPowerStateMapping(zx::unowned(child2_device_handle));
ASSERT_OK(response2.status());
zx_status_t call_status = ZX_OK;
if (response2->result.is_err()) {
call_status = response2->result.err();
}
ASSERT_STATUS(call_status, ZX_OK);
states_mapping = &response2->result.response().mapping[0];
// Test Default mapping. For now, the default device power state is D3COLD and
// wakeup_enable is false.
for (size_t i = 0; i < MAX_SYSTEM_POWER_STATES; i++) {
ASSERT_EQ(states_mapping[i].dev_state, DevicePowerState::DEVICE_POWER_STATE_D3COLD);
ASSERT_FALSE(states_mapping[i].wakeup_enable);
}
}
TEST_F(PowerTestCase, UpdatePowerStatesMapping_UnsupportedDeviceState) {
// Add Capabilities
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, fbl::count_of(states));
::fidl::Array<SystemPowerStateInfo, MAX_SYSTEM_POWER_STATES> mapping{};
for (size_t i = 0; i < MAX_SYSTEM_POWER_STATES; i++) {
mapping[i].dev_state = DevicePowerState::DEVICE_POWER_STATE_D2;
mapping[i].wakeup_enable = false;
}
auto update_result =
Controller::Call::UpdatePowerStateMapping(zx::unowned(child2_device_handle), mapping);
ASSERT_OK(update_result.status());
zx_status_t call_status = ZX_OK;
if (update_result->result.is_err()) {
call_status = update_result->result.err();
}
ASSERT_EQ(call_status, ZX_ERR_INVALID_ARGS);
const SystemPowerStateInfo *states_mapping;
auto response2 = Controller::Call::GetPowerStateMapping(zx::unowned(child2_device_handle));
ASSERT_OK(response2.status());
call_status = ZX_OK;
if (response2->result.is_err()) {
call_status = response2->result.err();
}
ASSERT_STATUS(call_status, ZX_OK);
states_mapping = &response2->result.response().mapping[0];
ASSERT_EQ(
states_mapping[static_cast<uint8_t>(SystemPowerState::SYSTEM_POWER_STATE_REBOOT)].dev_state,
DevicePowerState::DEVICE_POWER_STATE_D3COLD);
ASSERT_FALSE(states_mapping[static_cast<uint8_t>(SystemPowerState::SYSTEM_POWER_STATE_REBOOT)]
.wakeup_enable);
}
TEST_F(PowerTestCase, UpdatePowerStatesMapping_UnsupportedWakeConfig) {
// Add Capabilities
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[1].wakeup_capable = false;
states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, fbl::count_of(states));
::fidl::Array<SystemPowerStateInfo, MAX_SYSTEM_POWER_STATES> mapping{};
for (size_t i = 0; i < MAX_SYSTEM_POWER_STATES; i++) {
mapping[i].dev_state = DevicePowerState::DEVICE_POWER_STATE_D1;
mapping[i].wakeup_enable = true;
}
auto update_result =
Controller::Call::UpdatePowerStateMapping(zx::unowned(child2_device_handle), mapping);
ASSERT_OK(update_result.status());
zx_status_t call_status = ZX_OK;
if (update_result->result.is_err()) {
call_status = update_result->result.err();
}
ASSERT_EQ(call_status, ZX_ERR_INVALID_ARGS);
const SystemPowerStateInfo *states_mapping;
auto response2 = Controller::Call::GetPowerStateMapping(zx::unowned(child2_device_handle));
ASSERT_OK(response2.status());
call_status = ZX_OK;
if (response2->result.is_err()) {
call_status = response2->result.err();
}
ASSERT_STATUS(call_status, ZX_OK);
states_mapping = &response2->result.response().mapping[0];
ASSERT_EQ(
states_mapping[static_cast<uint8_t>(SystemPowerState::SYSTEM_POWER_STATE_REBOOT)].dev_state,
DevicePowerState::DEVICE_POWER_STATE_D3COLD);
ASSERT_FALSE(states_mapping[static_cast<uint8_t>(SystemPowerState::SYSTEM_POWER_STATE_REBOOT)]
.wakeup_enable);
}
TEST_F(PowerTestCase, UpdatePowerStatesMapping_Success) {
// Add Capabilities
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, fbl::count_of(states));
::fidl::Array<SystemPowerStateInfo, MAX_SYSTEM_POWER_STATES> mapping{};
for (size_t i = 0; i < MAX_SYSTEM_POWER_STATES; i++) {
mapping[i].dev_state = DevicePowerState::DEVICE_POWER_STATE_D1;
mapping[i].wakeup_enable = false;
}
auto update_result =
Controller::Call::UpdatePowerStateMapping(zx::unowned(child2_device_handle), mapping);
ASSERT_OK(update_result.status());
zx_status_t call_status = ZX_OK;
if (update_result->result.is_err()) {
call_status = update_result->result.err();
}
ASSERT_OK(call_status);
const SystemPowerStateInfo *states_mapping;
auto response2 = Controller::Call::GetPowerStateMapping(zx::unowned(child2_device_handle));
ASSERT_OK(response2.status());
call_status = ZX_OK;
if (response2->result.is_err()) {
call_status = response2->result.err();
}
ASSERT_STATUS(call_status, ZX_OK);
states_mapping = &response2->result.response().mapping[0];
ASSERT_EQ(
states_mapping[static_cast<uint8_t>(SystemPowerState::SYSTEM_POWER_STATE_REBOOT)].dev_state,
DevicePowerState::DEVICE_POWER_STATE_D1);
ASSERT_FALSE(states_mapping[static_cast<uint8_t>(SystemPowerState::SYSTEM_POWER_STATE_REBOOT)]
.wakeup_enable);
}
TEST_F(PowerTestCase, SystemSuspend) {
// Add Capabilities
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D2;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, fbl::count_of(states));
::fidl::Array<SystemPowerStateInfo, MAX_SYSTEM_POWER_STATES> mapping{};
for (size_t i = 0; i < MAX_SYSTEM_POWER_STATES; i++) {
mapping[i].dev_state = DevicePowerState::DEVICE_POWER_STATE_D2;
mapping[i].wakeup_enable = false;
}
auto update_result =
Controller::Call::UpdatePowerStateMapping(zx::unowned(child2_device_handle), mapping);
ASSERT_OK(update_result.status());
zx_status_t call_status = ZX_OK;
if (update_result->result.is_err()) {
call_status = update_result->result.err();
}
ASSERT_OK(call_status);
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
char service_name[100];
snprintf(service_name, sizeof(service_name), "svc/%s",
::llcpp::fuchsia::device::manager::Administrator::Name);
ASSERT_OK(fdio_service_connect_at(devmgr.svc_root_dir().get(), service_name, remote.release()));
ASSERT_NE(devmgr.svc_root_dir().get(), ZX_HANDLE_INVALID);
auto suspend_result = Administrator::Call::Suspend(
zx::unowned(local), ::llcpp::fuchsia::device::manager::SUSPEND_FLAG_REBOOT);
ASSERT_OK(suspend_result.status());
const auto &suspend_response = suspend_result.value();
ASSERT_OK(suspend_response.status);
// Verify the child's DdkSuspendNew routine gets called.
auto child_dev_suspend_response =
TestDevice::Call::GetCurrentDevicePowerState(zx::unowned(child2_device_handle));
ASSERT_OK(child_dev_suspend_response.status());
call_status = ZX_OK;
if (child_dev_suspend_response->result.is_err()) {
call_status = child_dev_suspend_response->result.err();
}
ASSERT_OK(call_status);
ASSERT_EQ(child_dev_suspend_response->result.response().cur_state,
DevicePowerState::DEVICE_POWER_STATE_D2);
// Verify the parent'd DdkSuspend routine gets called.
auto parent_dev_suspend_response =
TestDevice::Call::GetCurrentDevicePowerState(zx::unowned(parent_device_handle));
ASSERT_OK(parent_dev_suspend_response.status());
call_status = ZX_OK;
if (parent_dev_suspend_response->result.is_err()) {
call_status = parent_dev_suspend_response->result.err();
}
ASSERT_OK(call_status);
ASSERT_EQ(parent_dev_suspend_response->result.response().cur_state,
DevicePowerState::DEVICE_POWER_STATE_D1);
}
TEST_F(PowerTestCase, SystemResume) {
// Add Capabilities
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D2;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, fbl::count_of(states));
::fidl::Array<SystemPowerStateInfo, MAX_SYSTEM_POWER_STATES> mapping{};
for (size_t i = 0; i < MAX_SYSTEM_POWER_STATES; i++) {
mapping[i].dev_state = DevicePowerState::DEVICE_POWER_STATE_D2;
mapping[i].wakeup_enable = false;
}
mapping[static_cast<size_t>(SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON)].dev_state =
DevicePowerState::DEVICE_POWER_STATE_D0;
auto update_result =
Controller::Call::UpdatePowerStateMapping(zx::unowned(child2_device_handle), mapping);
ASSERT_OK(update_result.status());
zx_status_t call_status = ZX_OK;
if (update_result->result.is_err()) {
call_status = update_result->result.err();
}
ASSERT_OK(call_status);
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
char service_name[100];
snprintf(service_name, sizeof(service_name), "svc/%s",
::llcpp::fuchsia::device::manager::Administrator::Name);
ASSERT_OK(fdio_service_connect_at(devmgr.svc_root_dir().get(), service_name, remote.release()));
ASSERT_NE(devmgr.svc_root_dir().get(), ZX_HANDLE_INVALID);
auto suspend_result = Administrator::Call::Suspend(
zx::unowned(local), ::llcpp::fuchsia::device::manager::SUSPEND_FLAG_REBOOT);
ASSERT_OK(suspend_result.status());
const auto &suspend_response = suspend_result.value();
ASSERT_OK(suspend_response.status);
// Verify the child's DdkSuspendNew routine gets called.
auto child_dev_suspend_response =
TestDevice::Call::GetCurrentDevicePowerState(zx::unowned(child2_device_handle));
ASSERT_OK(child_dev_suspend_response.status());
call_status = ZX_OK;
if (child_dev_suspend_response->result.is_err()) {
call_status = child_dev_suspend_response->result.err();
}
ASSERT_OK(call_status);
ASSERT_EQ(child_dev_suspend_response->result.response().cur_state,
DevicePowerState::DEVICE_POWER_STATE_D2);
// Verify the parent'd DdkSuspend routine gets called.
auto parent_dev_suspend_response =
TestDevice::Call::GetCurrentDevicePowerState(zx::unowned(parent_device_handle));
ASSERT_OK(parent_dev_suspend_response.status());
call_status = ZX_OK;
if (parent_dev_suspend_response->result.is_err()) {
call_status = parent_dev_suspend_response->result.err();
}
ASSERT_OK(call_status);
ASSERT_EQ(parent_dev_suspend_response->result.response().cur_state,
DevicePowerState::DEVICE_POWER_STATE_D1);
auto resume_result = Administrator::Call::Resume(zx::unowned(local),
SystemPowerState::SYSTEM_POWER_STATE_FULLY_ON);
ASSERT_OK(resume_result.status());
const auto &resume_response = resume_result.value();
ASSERT_OK(resume_response.status);
// Verify the child's DdkResumeNew routine gets called.
auto child_dev_resume_response =
TestDevice::Call::GetCurrentDevicePowerState(zx::unowned(child2_device_handle));
ASSERT_OK(child_dev_resume_response.status());
call_status = ZX_OK;
if (child_dev_resume_response->result.is_err()) {
call_status = child_dev_resume_response->result.err();
}
ASSERT_OK(call_status);
ASSERT_EQ(child_dev_resume_response->result.response().cur_state,
DevicePowerState::DEVICE_POWER_STATE_D0);
// Verify the parent'd DdkSuspend routine gets called.
auto parent_dev_resume_response =
TestDevice::Call::GetCurrentDevicePowerState(zx::unowned(parent_device_handle));
ASSERT_OK(parent_dev_resume_response.status());
call_status = ZX_OK;
if (parent_dev_resume_response->result.is_err()) {
call_status = parent_dev_resume_response->result.err();
}
ASSERT_OK(call_status);
ASSERT_EQ(parent_dev_resume_response->result.response().cur_state,
DevicePowerState::DEVICE_POWER_STATE_D0);
}