blob: 1b248cfe349490ffac74cd81d900f421306044ef [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.manager/cpp/wire.h>
#include <fidl/fuchsia.device.power.test/cpp/wire.h>
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fidl/fuchsia.hardware.power.statecontrol/cpp/wire.h>
#include <fidl/fuchsia.process.lifecycle/cpp/wire.h>
#include <lib/ddk/device.h>
#include <lib/ddk/platform-defs.h>
#include <lib/device-watcher/cpp/device-watcher.h>
#include <lib/driver-integration-test/fixture.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <zxtest/zxtest.h>
using driver_integration_test::IsolatedDevmgr;
using fuchsia_device::wire::DevicePerformanceStateInfo;
using fuchsia_device::wire::DevicePowerState;
using fuchsia_device::wire::DevicePowerStateInfo;
using fuchsia_device::wire::kDevicePerformanceStateP0;
using fuchsia_device::wire::kMaxDevicePowerStates;
using fuchsia_device::wire::SystemPowerStateInfo;
using fuchsia_device_manager::wire::SystemPowerState;
using fuchsia_device_power_test::TestDevice;
namespace device_manager_fidl = fuchsia_device_manager;
namespace lifecycle_fidl = fuchsia_process_lifecycle;
constexpr char kPowerTestPath[] = "sys/platform/11:0b:0/power-test";
constexpr char kPowerTestChild1Path[] = "sys/platform/11:0b:0/power-test/power-test-child";
constexpr char kPowerTestChild2Path[] = "sys/platform/11:0b:0/power-test/power-test-child-2";
class PowerTestCase : public zxtest::Test {
public:
~PowerTestCase() override = default;
void SetUp() override {
IsolatedDevmgr::Args args;
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);
zx::result parent_channel =
device_watcher::RecursiveWaitForFile(devmgr.devfs_root().get(), kPowerTestPath);
ASSERT_OK(parent_channel);
parent_device_client.Bind(fidl::ClientEnd<TestDevice>{std::move(parent_channel.value())});
std::string parent_controller_path = std::string(kPowerTestPath) + "/device_controller";
parent_channel = device_watcher::RecursiveWaitForFile(devmgr.devfs_root().get(),
parent_controller_path.c_str());
ASSERT_OK(parent_channel);
parent_device_controller.Bind(
fidl::ClientEnd<fuchsia_device::Controller>{std::move(parent_channel.value())});
zx::result child_channel =
device_watcher::RecursiveWaitForFile(devmgr.devfs_root().get(), kPowerTestChild1Path);
ASSERT_OK(child_channel);
child1_device_client.Bind(fidl::ClientEnd<TestDevice>{std::move(child_channel.value())});
std::string child_controller_path = std::string(kPowerTestChild1Path) + "/device_controller";
child_channel = device_watcher::RecursiveWaitForFile(devmgr.devfs_root().get(),
child_controller_path.c_str());
ASSERT_OK(child_channel);
child1_device_controller.Bind(
fidl::ClientEnd<fuchsia_device::Controller>{std::move(child_channel.value())});
}
void AddChildWithPowerArgs(DevicePowerStateInfo *states, uint8_t sleep_state_count,
bool add_invisible = false) {
auto power_states =
::fidl::VectorView<DevicePowerStateInfo>::FromExternal(states, sleep_state_count);
auto response = child1_device_client->AddDeviceWithPowerArgs(power_states, add_invisible);
ASSERT_OK(response.status());
zx_status_t call_status = ZX_OK;
if (response->is_error()) {
call_status = response->error_value();
}
ASSERT_OK(call_status);
zx::result channel =
device_watcher::RecursiveWaitForFile(devmgr.devfs_root().get(), kPowerTestChild2Path);
ASSERT_OK(channel);
child2_device_client.Bind(fidl::ClientEnd<TestDevice>{std::move(channel.value())});
std::string child_controller_path = std::string(kPowerTestChild2Path) + "/device_controller";
channel = device_watcher::RecursiveWaitForFile(devmgr.devfs_root().get(),
child_controller_path.c_str());
ASSERT_OK(channel);
child2_device_controller.Bind(
fidl::ClientEnd<fuchsia_device::Controller>{std::move(channel.value())});
}
void WaitForDeviceSuspendCompletion(const fidl::WireSyncClient<TestDevice> &client) {
const fidl::WireResult result = client->GetSuspendCompletionEvent();
ASSERT_OK(result.status());
const fit::result response = result.value();
ASSERT_TRUE(response.is_ok(), "%s", zx_status_get_string(response.error_value()));
zx_signals_t signals;
ASSERT_OK(response->event.wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), &signals));
}
fidl::WireSyncClient<TestDevice> parent_device_client;
fidl::WireSyncClient<fuchsia_device::Controller> parent_device_controller;
fidl::WireSyncClient<TestDevice> child1_device_client;
fidl::WireSyncClient<fuchsia_device::Controller> child1_device_controller;
fidl::WireSyncClient<TestDevice> child2_device_client;
fidl::WireSyncClient<fuchsia_device::Controller> child2_device_controller;
IsolatedDevmgr devmgr;
};
TEST_F(PowerTestCase, AddDevicePowerCaps_Success) {
std::array<DevicePowerStateInfo, 2> states;
states[0].state_id = DevicePowerState::kDevicePowerStateD0;
states[0].is_supported = true;
states[1].state_id = DevicePowerState::kDevicePowerStateD3Cold;
states[1].is_supported = true;
auto response = child1_device_client->AddDeviceWithPowerArgs(
fidl::VectorView<DevicePowerStateInfo>::FromExternal(states), false);
ASSERT_OK(response.status());
zx_status_t call_status = ZX_OK;
if (response->is_error()) {
call_status = response->error_value();
}
ASSERT_STATUS(call_status, ZX_OK);
}
TEST_F(PowerTestCase, AddDevicePowerCaps_MakeVisible_Success) {
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::kDevicePowerStateD0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::kDevicePowerStateD1;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::kDevicePowerStateD3Cold;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, std::size(states), true);
}
// TODO(https://fxbug.dev/42071033): Re-enable this test after fixing.
// This test is not easy to replicate without a lot of plumbing changes to allow test to modify the
// response to fuchsia.device.manager/SystemStateTransition.GetTerminationSystemState.
TEST_F(PowerTestCase, DISABLED_SystemSuspend_SuspendReasonReboot) {
// Add Capabilities
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::kDevicePowerStateD0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::kDevicePowerStateD2;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::kDevicePowerStateD3Cold;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, std::size(states));
ASSERT_OK(devmgr.SuspendDriverManager());
// Wait till child2's suspend event is called.
WaitForDeviceSuspendCompletion(child2_device_client);
auto child_dev_suspend_response = child2_device_client->GetCurrentDevicePowerState();
ASSERT_OK(child_dev_suspend_response.status());
auto call_status = ZX_OK;
if (child_dev_suspend_response->is_error()) {
call_status = child_dev_suspend_response->error_value();
}
ASSERT_OK(call_status);
ASSERT_EQ(child_dev_suspend_response->value()->cur_state,
DevicePowerState::kDevicePowerStateD3Cold);
// Verify that the suspend reason is received correctly
auto suspend_reason_response = child2_device_client->GetCurrentSuspendReason();
ASSERT_OK(suspend_reason_response.status());
call_status = ZX_OK;
if (suspend_reason_response->is_error()) {
call_status = suspend_reason_response->error_value();
}
ASSERT_OK(call_status);
ASSERT_EQ(suspend_reason_response->value()->cur_suspend_reason, DEVICE_SUSPEND_REASON_REBOOT);
// Wait till parent's suspend event is called.
WaitForDeviceSuspendCompletion(parent_device_client);
auto parent_dev_suspend_response = parent_device_client->GetCurrentDevicePowerState();
ASSERT_OK(parent_dev_suspend_response.status());
call_status = ZX_OK;
if (parent_dev_suspend_response->is_error()) {
call_status = parent_dev_suspend_response->error_value();
}
ASSERT_OK(call_status);
ASSERT_EQ(parent_dev_suspend_response->value()->cur_state,
DevicePowerState::kDevicePowerStateD3Cold);
}
// TODO(https://fxbug.dev/42071033): Re-enable this test after fixing.
// This test is not easy to replicate without a lot of plumbing changes to allow test to modify the
// response to fuchsia.device.manager/SystemStateTransition.GetTerminationSystemState.
TEST_F(PowerTestCase, DISABLED_SystemSuspend_SuspendReasonRebootRecovery) {
// Add Capabilities
DevicePowerStateInfo states[3];
states[0].state_id = DevicePowerState::kDevicePowerStateD0;
states[0].is_supported = true;
states[0].restore_latency = 0;
states[1].state_id = DevicePowerState::kDevicePowerStateD2;
states[1].is_supported = true;
states[1].restore_latency = 100;
states[2].state_id = DevicePowerState::kDevicePowerStateD3Cold;
states[2].is_supported = true;
states[2].restore_latency = 1000;
AddChildWithPowerArgs(states, std::size(states));
// TODO(https://fxbug.dev/42071033): Modify GetTerminationSystemState response.
ASSERT_OK(devmgr.SuspendDriverManager());
// Wait till child2's suspend event is called.
WaitForDeviceSuspendCompletion(child2_device_client);
auto child_dev_suspend_response = child2_device_client->GetCurrentDevicePowerState();
ASSERT_OK(child_dev_suspend_response.status());
auto call_status = ZX_OK;
if (child_dev_suspend_response->is_error()) {
call_status = child_dev_suspend_response->error_value();
}
ASSERT_OK(call_status);
ASSERT_EQ(child_dev_suspend_response->value()->cur_state,
DevicePowerState::kDevicePowerStateD3Cold);
auto suspend_reason_response = child2_device_client->GetCurrentSuspendReason();
ASSERT_OK(suspend_reason_response.status());
call_status = ZX_OK;
if (suspend_reason_response->is_error()) {
call_status = suspend_reason_response->error_value();
}
ASSERT_OK(call_status);
ASSERT_EQ(suspend_reason_response->value()->cur_suspend_reason,
DEVICE_SUSPEND_REASON_REBOOT_RECOVERY);
// Wait till parent's suspend event is called.
WaitForDeviceSuspendCompletion(parent_device_client);
auto parent_dev_suspend_response = parent_device_client->GetCurrentDevicePowerState();
ASSERT_OK(parent_dev_suspend_response.status());
call_status = ZX_OK;
if (parent_dev_suspend_response->is_error()) {
call_status = parent_dev_suspend_response->error_value();
}
ASSERT_OK(call_status);
ASSERT_EQ(parent_dev_suspend_response->value()->cur_state,
DevicePowerState::kDevicePowerStateD3Cold);
}