blob: 2e1eeecf5d4dbeff23dd9adc26612c6d08b3d8dc [file] [log] [blame]
// Copyright 2024 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.hardware.platform.device/cpp/fidl.h>
#include <fidl/fuchsia.hardware.power/cpp/fidl.h>
#include <fidl/fuchsia.power.broker/cpp/fidl.h>
#include <fidl/fuchsia.power.system/cpp/fidl.h>
#include <fidl/fuchsia.power.system/cpp/test_base.h>
#include <lib/ddk/platform-defs.h>
#include <lib/driver/testing/cpp/driver_test.h>
#include <lib/fake-bti/bti.h>
#include <lib/fpromise/result.h>
#include <lib/fpromise/single_threaded_executor.h>
#include <lib/fzl/vmo-mapper.h>
#include <lib/inspect/cpp/reader.h>
#include <gtest/gtest.h>
#include "src/devices/hrtimer/drivers/aml-hrtimer/aml-hrtimer.h"
#include "src/devices/hrtimer/drivers/aml-hrtimer/aml_hrtimer_config.h"
namespace hrtimer {
class FakePlatformDevice : public fidl::Server<fuchsia_hardware_platform_device::Device> {
public:
fuchsia_hardware_platform_device::Service::InstanceHandler GetInstanceHandler() {
return fuchsia_hardware_platform_device::Service::InstanceHandler({
.device = bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->async_dispatcher(),
fidl::kIgnoreBindingClosure),
});
}
void InitResources() {
zx::vmo::create(kMmioSize, 0, &mmio_);
fake_bti_create(bti_.reset_and_get_address());
}
cpp20::span<uint32_t> mmio() {
// The test has to wait for the driver to set the MMIO cache policy before mapping.
if (!mapped_mmio_.start()) {
MapMmio();
}
return {reinterpret_cast<uint32_t*>(mapped_mmio_.start()), kMmioSize / sizeof(uint32_t)};
}
void TriggerIrq(size_t timer_index) {
ASSERT_TRUE(timer_index < kNumberOfTimers);
ASSERT_EQ(fake_interrupts_[*kTimerToIrqsIndexes[timer_index]].trigger(0, zx::clock::get_boot()),
ZX_OK);
}
void TriggerAllIrqs() {
for (size_t i = 0; i < AmlHrtimer::GetNumberOfIrqs(); ++i) {
ASSERT_EQ(fake_interrupts_[i].trigger(0, zx::clock::get_boot()), ZX_OK);
}
}
private:
static constexpr size_t kMmioSize = 0x10000;
std::optional<size_t> kTimerToIrqsIndexes[kNumberOfTimers] = {0, 1, 2, 3, std::nullopt,
4, 5, 6, 7};
void GetMmioById(GetMmioByIdRequest& request, GetMmioByIdCompleter::Sync& completer) override {
if (request.index() != 0) {
return completer.Reply(zx::error(ZX_ERR_OUT_OF_RANGE));
}
zx::vmo vmo;
if (zx_status_t status = mmio_.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo); status != ZX_OK) {
return completer.Reply(zx::error(status));
}
completer.Reply(zx::ok(fuchsia_hardware_platform_device::Mmio{{
.offset = 0,
.size = kMmioSize,
.vmo = std::move(vmo),
}}));
}
void GetMmioByName(GetMmioByNameRequest& request,
GetMmioByNameCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void GetInterruptById(GetInterruptByIdRequest& request,
GetInterruptByIdCompleter::Sync& completer) override {
if (request.index() >= AmlHrtimer::GetNumberOfIrqs()) {
completer.Reply(zx::error(ZX_ERR_INVALID_ARGS));
return;
}
zx::interrupt interrupt;
ASSERT_EQ(zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL,
&fake_interrupts_[request.index()]),
ZX_OK);
zx_status_t status =
fake_interrupts_[request.index()].duplicate(ZX_RIGHT_SAME_RIGHTS, &interrupt);
if (status != ZX_OK) {
completer.Reply(zx::error(status));
return;
}
completer.Reply(zx::ok(std::move(interrupt)));
}
void GetInterruptByName(GetInterruptByNameRequest& request,
GetInterruptByNameCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void GetBtiById(GetBtiByIdRequest& request, GetBtiByIdCompleter::Sync& completer) override {
zx::bti bti;
if (zx_status_t status = bti_.duplicate(ZX_RIGHT_SAME_RIGHTS, &bti); status != ZX_OK) {
return completer.Reply(zx::error(status));
}
completer.Reply(zx::ok((std::move(bti))));
}
void GetBtiByName(GetBtiByNameRequest& request, GetBtiByNameCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void GetSmcById(GetSmcByIdRequest& request, GetSmcByIdCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void GetNodeDeviceInfo(GetNodeDeviceInfoCompleter::Sync& completer) override {
fuchsia_hardware_platform_device::NodeDeviceInfo info;
info.vid(PDEV_VID_AMLOGIC).pid(PDEV_PID_AMLOGIC_A311D);
completer.Reply(zx::ok(std::move(info)));
}
void GetSmcByName(GetSmcByNameRequest& request, GetSmcByNameCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void GetBoardInfo(GetBoardInfoCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void GetMetadata(GetMetadataRequest& request, GetMetadataCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_hardware_platform_device::Device> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override {}
void MapMmio() { mapped_mmio_.Map(mmio_); }
void GetPowerConfiguration(GetPowerConfigurationCompleter::Sync& completer) override {
// fuchsia_hardware_power uses FIDL uint8 for power levels matching fuchsia_power_broker's.
constexpr uint8_t kPowerLevelOff =
static_cast<uint8_t>(fuchsia_power_broker::BinaryPowerLevel::kOff);
constexpr uint8_t kPowerLevelOn =
static_cast<uint8_t>(fuchsia_power_broker::BinaryPowerLevel::kOn);
constexpr char kPowerElementName[] = "aml-hrtimer-wake";
fuchsia_hardware_power::LevelTuple wake_handling_on = {{
.child_level = kPowerLevelOn,
.parent_level =
static_cast<uint8_t>(fuchsia_power_system::ExecutionStateLevel::kSuspending),
}};
fuchsia_hardware_power::PowerDependency wake_handling = {{
.child = kPowerElementName,
.parent = fuchsia_hardware_power::ParentElement::WithSag(
fuchsia_hardware_power::SagElement::kExecutionState),
.level_deps = {{std::move(wake_handling_on)}},
.strength = fuchsia_hardware_power::RequirementType::kAssertive,
}};
fuchsia_hardware_power::PowerLevel off = {{.level = kPowerLevelOff, .name = "off"}};
fuchsia_hardware_power::PowerLevel on = {{.level = kPowerLevelOn, .name = "on"}};
fuchsia_hardware_power::PowerElement element = {
{.name = kPowerElementName, .levels = {{std::move(off), std::move(on)}}}};
fuchsia_hardware_power::PowerElementConfiguration wake_config = {
{.element = std::move(element), .dependencies = {{std::move(wake_handling)}}}};
completer.Reply(zx::ok(
std::vector<fuchsia_hardware_power::PowerElementConfiguration>{{std::move(wake_config)}}));
}
zx::vmo mmio_;
fzl::VmoMapper mapped_mmio_;
zx::bti bti_;
zx::interrupt fake_interrupts_[AmlHrtimer::GetNumberOfIrqs()];
fidl::ServerBindingGroup<fuchsia_hardware_platform_device::Device> bindings_;
};
// Power Specific.
// TODO(https://fxbug.dev/342124966): Move to the Power Framework Testing Client
// at //src/power/testing/client.
class FakeSystemActivityGovernor
: public fidl::testing::TestBase<fuchsia_power_system::ActivityGovernor> {
public:
FakeSystemActivityGovernor(zx::event wake_handling) : wake_handling_(std::move(wake_handling)) {}
fidl::ProtocolHandler<fuchsia_power_system::ActivityGovernor> CreateHandler() {
return bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->async_dispatcher(),
fidl::kIgnoreBindingClosure);
}
void GetPowerElements(GetPowerElementsCompleter::Sync& completer) override {
fuchsia_power_system::PowerElements elements;
zx::event duplicate;
wake_handling_.duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate);
completer.Reply({{std::move(elements)}});
}
zx::eventpair AcquireWakeLease() {
zx::eventpair wake_lease_remote, wake_lease_local;
zx::eventpair::create(0, &wake_lease_local, &wake_lease_remote);
wake_leases_.push_back(std::move(wake_lease_local));
lease_requested_ = true;
return wake_lease_remote;
}
void AcquireWakeLease(AcquireWakeLeaseRequest& request,
AcquireWakeLeaseCompleter::Sync& completer) override {
completer.Reply(fit::ok(AcquireWakeLease()));
}
void TakeWakeLease(TakeWakeLeaseRequest& request,
TakeWakeLeaseCompleter::Sync& completer) override {
completer.Reply(AcquireWakeLease());
}
void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override {
ADD_FAILURE() << name << " is not implemented";
}
void handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_power_system::ActivityGovernor> md,
fidl::UnknownMethodCompleter::Sync& completer) override {}
bool GetLeaseRequested() { return lease_requested_; }
private:
bool lease_requested_ = false;
zx::event wake_handling_;
std::vector<zx::eventpair> wake_leases_;
fidl::ServerBindingGroup<fuchsia_power_system::ActivityGovernor> bindings_;
};
class TestEnvironment : public fdf_testing::Environment {
public:
zx::result<> Serve(fdf::OutgoingDirectory& to_driver_vfs) override {
platform_device_.InitResources();
auto result = to_driver_vfs.AddService<fuchsia_hardware_platform_device::Service>(
platform_device_.GetInstanceHandler());
EXPECT_EQ(ZX_OK, result.status_value());
// Power specific.
zx::event::create(0, &wake_handling_);
zx::event duplicate;
EXPECT_EQ(wake_handling_.duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate), ZX_OK);
system_activity_governor_.emplace(std::move(duplicate));
auto result_sag =
to_driver_vfs.component().AddUnmanagedProtocol<fuchsia_power_system::ActivityGovernor>(
system_activity_governor_->CreateHandler());
EXPECT_EQ(ZX_OK, result_sag.status_value());
return zx::ok();
}
FakePlatformDevice& platform_device() { return platform_device_; }
FakeSystemActivityGovernor& system_activity_governor() { return *system_activity_governor_; }
private:
FakePlatformDevice platform_device_;
std::optional<FakeSystemActivityGovernor> system_activity_governor_;
zx::event wake_handling_;
};
class FixtureConfig final {
public:
using DriverType = AmlHrtimer;
using EnvironmentType = TestEnvironment;
};
class DriverTest : public ::testing::Test {
public:
void TearDown() override {
zx::result<> result = driver_test().StopDriver();
ASSERT_EQ(ZX_OK, result.status_value());
}
void SetUp() override {
zx::result<> result =
driver_test().StartDriverWithCustomStartArgs([](fdf::DriverStartArgs& start_args) mutable {
aml_hrtimer_config::Config fake_config;
fake_config.enable_suspend() = true;
start_args.config(fake_config.ToVmo());
});
ASSERT_EQ(ZX_OK, result.status_value());
zx::result device_result =
driver_test().ConnectThroughDevfs<fuchsia_hardware_hrtimer::Device>("aml-hrtimer");
ASSERT_EQ(ZX_OK, device_result.status_value());
client_.Bind(std::move(device_result.value()));
}
void CheckLeaseRequested(size_t timer_id) {
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
ASSERT_FALSE(env.system_activity_governor().GetLeaseRequested());
});
zx::eventpair lease;
std::thread thread([this, timer_id, &lease]() {
zx::event setup_event;
ASSERT_EQ(ZX_OK, zx::event::create(0, &setup_event));
auto result_start = client_->StartAndWait(
{timer_id, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0,
std::move(setup_event)});
ASSERT_FALSE(result_start.is_error());
ASSERT_TRUE(result_start->keep_alive().is_valid());
lease = std::move(result_start->keep_alive());
});
// Wait until the driver has acquired the timer wait completer before triggering the IRQ.
bool has_wait_completer = false;
while (!has_wait_completer) {
driver_test().RunInDriverContext([timer_id, &has_wait_completer](AmlHrtimer& driver) {
has_wait_completer = driver.HasWaitCompleter(timer_id);
});
zx::nanosleep(zx::deadline_after(zx::msec(1)));
}
driver_test().RunInEnvironmentTypeContext(
[timer_id](TestEnvironment& env) { env.platform_device().TriggerIrq(timer_id); });
thread.join();
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
ASSERT_TRUE(env.system_activity_governor().GetLeaseRequested());
});
}
void CheckInspect(const char* path, const char* type, uint64_t id, uint64_t data) {
driver_test().RunInDriverContext([&](AmlHrtimer& driver) {
auto& inspector = driver.inspect();
fpromise::single_threaded_executor executor;
executor.schedule_task(inspect::ReadFromInspector(inspector).then(
[&](fpromise::result<inspect::Hierarchy>& hierarchy) {
ASSERT_TRUE(hierarchy.is_ok());
const inspect::Hierarchy* events =
hierarchy.value().GetByPath({"hrtimer-trace", "events"});
ASSERT_TRUE(events);
const auto* event = events->GetByPath({path});
auto local_id = event->node().get_property<inspect::UintPropertyValue>("id")->value();
auto local_type =
event->node().get_property<inspect::StringPropertyValue>("type")->value();
auto local_data =
event->node().get_property<inspect::UintPropertyValue>("data")->value();
ASSERT_EQ(local_type.compare(type), 0);
ASSERT_EQ(local_id, id);
ASSERT_EQ(local_data, data);
}));
executor.run();
});
}
fdf_testing::BackgroundDriverTest<FixtureConfig>& driver_test() { return driver_test_; }
fdf_testing::BackgroundDriverTest<FixtureConfig> driver_test_;
fidl::SyncClient<fuchsia_hardware_hrtimer::Device> client_;
};
TEST_F(DriverTest, Properties) {
auto result = client_->GetProperties();
ASSERT_FALSE(result.is_error());
ASSERT_FALSE(result->properties().IsEmpty());
ASSERT_TRUE(result->properties().timers_properties().has_value());
ASSERT_EQ(result->properties().timers_properties()->size(), std::size_t{9});
auto& timers = result->properties().timers_properties().value();
// Resolutions and range for all timers, except timer id 4.
for (auto& i : kTimersAll) {
ASSERT_TRUE(timers[i].id());
if (timers[i].id().value() == 4) {
continue;
}
ASSERT_EQ(timers[i].id().value(), static_cast<uint64_t>(i));
ASSERT_EQ(timers[i].supported_resolutions()->size(), 4ULL);
auto& resolutions = timers[i].supported_resolutions().value();
ASSERT_EQ(resolutions[0].duration().value(), 1'000);
ASSERT_EQ(resolutions[1].duration().value(), 10'000);
ASSERT_EQ(resolutions[2].duration().value(), 100'000);
ASSERT_EQ(resolutions[3].duration().value(), 1'000'000);
if (i >= 5 && i <= 8) {
ASSERT_EQ(timers[i].max_ticks().value(), 0xffff'ffff'ffff'ffffULL); // extended max ticks.
} else {
ASSERT_EQ(timers[i].max_ticks().value(), 0xffffULL);
}
ASSERT_TRUE(timers[i].supports_event().value());
}
/// Timer id 4 has no IRQ and higher max_range.
ASSERT_EQ(timers[4].id().value(), static_cast<uint64_t>(4));
ASSERT_EQ(timers[4].supported_resolutions()->size(), 3ULL);
auto& resolutions = timers[4].supported_resolutions().value();
ASSERT_EQ(resolutions[0].duration().value(), 1'000);
ASSERT_EQ(resolutions[1].duration().value(), 10'000);
ASSERT_EQ(resolutions[2].duration().value(), 100'000);
ASSERT_EQ(timers[4].max_ticks().value(), 0xffff'ffff'ffff'ffffULL);
ASSERT_FALSE(timers[4].supports_event().value());
}
TEST_F(DriverTest, StartTimerNoticks) {
// All timers are able to take a 0 ticks expiration request for durations 1, 10, and 100 usecs.
for (auto& i : kTimersAll) {
auto result0 =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0});
ASSERT_FALSE(result0.is_error());
auto result1 =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(10'000ULL), 0});
ASSERT_FALSE(result1.is_error());
auto result2 =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(100'000ULL), 0});
ASSERT_FALSE(result2.is_error());
}
/// Timer id 4 does not support 1 msec.
auto result =
client_->Start({4ULL, fuchsia_hardware_hrtimer::Resolution::WithDuration(1000'000ULL), 0});
ASSERT_TRUE(result.is_error());
ASSERT_EQ(result.error_value().domain_error(),
fuchsia_hardware_hrtimer::DriverError::kInvalidArgs);
// Timers id 0 to 8 inclusive but not 4 support 1 msec.
for (uint64_t i = 0; i < 9; ++i) {
if (i == 4) {
continue;
}
auto result =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000'000ULL), 0});
ASSERT_FALSE(result.is_error());
}
}
TEST_F(DriverTest, StartTimerMaxTicks) {
// All timers are able to take a up to 0xffff ticks expiration request for durations 1, 10, and
// 100 usecs.
for (auto& i : kTimersAll) {
auto result0 =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0xffff});
ASSERT_FALSE(result0.is_error());
auto result1 =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(10'000ULL), 0xffff});
ASSERT_FALSE(result1.is_error());
auto result2 =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(100'000ULL), 0xffff});
ASSERT_FALSE(result2.is_error());
}
// Timers id 0 to 3 inclusive error on 0xffff+1 ticks.
for (uint64_t i = 0; i < 4; ++i) {
auto result =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0x1'0000});
ASSERT_TRUE(result.is_error());
}
// Timer id 4 supports 64 bits of ticks for 1, 10 and 100 usecs.
auto result0 = client_->Start({4ULL, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL),
0xffff'ffff'ffff'ffffULL});
ASSERT_FALSE(result0.is_error());
auto result1 =
client_->Start({4ULL, fuchsia_hardware_hrtimer::Resolution::WithDuration(10'000ULL),
0xffff'ffff'ffff'ffffULL});
ASSERT_FALSE(result1.is_error());
auto result2 =
client_->Start({4ULL, fuchsia_hardware_hrtimer::Resolution::WithDuration(100'000ULL),
0xffff'ffff'ffff'ffffULL});
ASSERT_FALSE(result2.is_error());
// Timers id 5 to 8 inclusive have no error on 0xffff+1 ticks.
for (uint64_t i = 5; i < 9; ++i) {
auto result =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0x1'0000});
ASSERT_FALSE(result.is_error());
}
}
TEST_F(DriverTest, StartStop) {
// All timers support start/stop.
for (auto& i : kTimersAll) {
auto result_start =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 1});
ASSERT_FALSE(result_start.is_error());
}
// Timers are started.
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
ASSERT_EQ(env.platform_device().mmio()[0x3c50], 0x000f'0100UL); // Timers A, B, C and D.
// Timer E is always started.
ASSERT_EQ(env.platform_device().mmio()[0x3c64], 0x000f'0000UL); // Timers F, G, H and I.
});
for (auto& i : kTimersAll) {
auto result_stop = client_->Stop(i);
ASSERT_FALSE(result_stop.is_error());
}
// Timers are stopped.
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
ASSERT_EQ(env.platform_device().mmio()[0x3c50], 0x0000'0100UL); // Timers A, B, C and D.
// Timer E can't actually be stopped.
ASSERT_EQ(env.platform_device().mmio()[0x3c64], 0x0000'0000UL); // Timers F, G, H and I.
});
CheckInspect("0", "Start", 0, 1);
CheckInspect("1", "StartHardware", 0, 1);
CheckInspect("2", "Start", 1, 1);
CheckInspect("3", "StartHardware", 1, 1);
CheckInspect("4", "Start", 2, 1);
CheckInspect("5", "StartHardware", 2, 1);
CheckInspect("6", "Start", 3, 1);
CheckInspect("7", "StartHardware", 3, 1);
CheckInspect("8", "Start", 4, 1);
CheckInspect("9", "StartHardware", 4, 0); // Timer 4 does not set ticks in the HW.
CheckInspect("10", "Start", 5, 1);
CheckInspect("11", "StartHardware", 5, 1);
CheckInspect("12", "Start", 6, 1);
CheckInspect("13", "StartHardware", 6, 1);
CheckInspect("14", "Start", 7, 1);
CheckInspect("15", "StartHardware", 7, 1);
CheckInspect("16", "Start", 8, 1);
CheckInspect("17", "StartHardware", 8, 1);
CheckInspect("18", "Stop", 0, 0);
CheckInspect("19", "Stop", 1, 0);
CheckInspect("20", "Stop", 2, 0);
CheckInspect("21", "Stop", 3, 0);
CheckInspect("22", "Stop", 4, 0);
CheckInspect("23", "Stop", 5, 0);
CheckInspect("24", "Stop", 6, 0);
CheckInspect("25", "Stop", 7, 0);
CheckInspect("26", "Stop", 8, 0);
}
TEST_F(DriverTest, EventTriggering) {
zx::event events[kNumberOfTimers];
for (auto& i : kTimersSupportWait) {
ASSERT_EQ(zx::event::create(0, &events[i]), ZX_OK);
zx::event duplicate_event;
events[i].duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate_event);
auto result_event = client_->SetEvent({i, std::move(duplicate_event)});
ASSERT_FALSE(result_event.is_error());
auto result_start =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0});
ASSERT_FALSE(result_start.is_error());
}
driver_test().RunInEnvironmentTypeContext(
[](TestEnvironment& env) { env.platform_device().TriggerAllIrqs(); });
for (auto& i : kTimersSupportWait) {
zx_signals_t signals = {};
ASSERT_EQ(events[i].wait_one(ZX_EVENT_SIGNALED, zx::time::infinite(), &signals), ZX_OK);
}
}
TEST_F(DriverTest, GetTicksTimers0123) {
// Can start up to 16 bits.
constexpr uint64_t kArbitraryTicksRequest = 0xffff;
constexpr uint32_t kArbitraryCount16bits0 = 0x1234;
constexpr uint32_t kArbitraryCount16bits1 = 0x5678;
constexpr uint32_t kArbitraryCount16bits2 = 0x90ab;
constexpr uint32_t kArbitraryCount16bits3 = 0xcdef;
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
env.platform_device().mmio()[0x3c51] = kArbitraryCount16bits0 << 16; // Timer A.
env.platform_device().mmio()[0x3c52] = kArbitraryCount16bits1 << 16; // Timer B.
env.platform_device().mmio()[0x3c53] = kArbitraryCount16bits2 << 16; // Timer C.
env.platform_device().mmio()[0x3c54] = kArbitraryCount16bits3 << 16; // Timer D.
});
for (uint64_t i = 0; i < 4; ++i) {
auto result_start = client_->Start(
{i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), kArbitraryTicksRequest});
ASSERT_FALSE(result_start.is_error());
}
// Reads from the registers.
{
auto result = client_->GetTicksLeft(0);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryCount16bits0);
}
{
auto result = client_->GetTicksLeft(1);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryCount16bits1);
}
{
auto result = client_->GetTicksLeft(2);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryCount16bits2);
}
{
auto result = client_->GetTicksLeft(3);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryCount16bits3);
}
}
TEST_F(DriverTest, GetTicksTimer4) {
// Can start up to 64 bits.
constexpr uint64_t kArbitraryTicksRequest = 0x1234'5678'90ab'cdef;
auto result_start = client_->Start(
{4, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), kArbitraryTicksRequest});
ASSERT_FALSE(result_start.is_error());
// We set the amount read after starting the timer since starting the timer writes to the
// register we read upon GetTicksLeft.
constexpr uint64_t kArbitraryCount64bits = 0x1234'5678'0000'0000;
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
env.platform_device().mmio()[0x3c62] =
static_cast<uint32_t>(kArbitraryCount64bits); // Timer E.
env.platform_device().mmio()[0x3c63] =
static_cast<uint32_t>(kArbitraryCount64bits >> 32); // Timer E High.
});
auto result_ticks = client_->GetTicksLeft(4);
ASSERT_FALSE(result_ticks.is_error());
// This timer counts up so the driver subtracts the read from the registers.
ASSERT_EQ(result_ticks->ticks(), kArbitraryTicksRequest - kArbitraryCount64bits);
// Since we can't really stop the timer 4 from ticking after a Stop(), GetTicksLeft() starts
// to return 0.
auto result_stop = client_->Stop(4);
ASSERT_FALSE(result_stop.is_error());
{
auto result_ticks = client_->GetTicksLeft(4);
ASSERT_FALSE(result_ticks.is_error());
ASSERT_EQ(result_ticks->ticks(), 0ULL);
}
}
TEST_F(DriverTest, GetTicksTimers5678TicksStayAtRequested) {
// Can start up to 64 bits because they support ticks extension.
constexpr uint64_t kArbitraryTicksRequest = 0x1234'5678'90ab'cdef;
// The count starts at max for the register since the request goes beyond the register max.
constexpr uint64_t kMaxCount = 0xffff;
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
env.platform_device().mmio()[0x3c65] = kMaxCount << 16; // Timer F.
env.platform_device().mmio()[0x3c66] = kMaxCount << 16; // Timer G.
env.platform_device().mmio()[0x3c67] = kMaxCount << 16; // Timer H.
env.platform_device().mmio()[0x3c68] = kMaxCount << 16; // Timer I.
});
for (uint64_t i = 5; i < 9; ++i) {
auto result_start = client_->Start(
{i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), kArbitraryTicksRequest});
ASSERT_FALSE(result_start.is_error());
// Ticks left stay at the ticks requested since the register reads 0xffff.
auto result = client_->GetTicksLeft(i);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryTicksRequest);
}
}
TEST_F(DriverTest, GetTicksTimers5678TicksDownBy0xffff) {
// Can start up to 64 bits because they support ticks extension.
constexpr uint64_t kArbitraryTicksRequest = 0x1234'5678'90ab'cdef;
// The count has decreased by 0xffff to 0.
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
env.platform_device().mmio()[0x3c65] = 0 << 16; // Timer F.
env.platform_device().mmio()[0x3c66] = 0 << 16; // Timer G.
env.platform_device().mmio()[0x3c67] = 0 << 16; // Timer H.
env.platform_device().mmio()[0x3c68] = 0 << 16; // Timer I.
});
for (uint64_t i = 5; i < 9; ++i) {
auto result_start = client_->Start(
{i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), kArbitraryTicksRequest});
ASSERT_FALSE(result_start.is_error());
// Ticks have decreased by 0xffff since the register reads 0.
auto result = client_->GetTicksLeft(i);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryTicksRequest - 0xffff);
}
}
TEST_F(DriverTest, GetTicksTimers5678ArbitraryCount) {
// Can start up to 64 bits because they support ticks extension.
constexpr uint64_t kArbitraryTicksRequest = 0x1234'5678'90ab'cdef;
constexpr uint64_t kArbitraryCount5 = 0x1234;
constexpr uint64_t kArbitraryCount6 = 0x5678;
constexpr uint64_t kArbitraryCount7 = 0x90ab;
constexpr uint64_t kArbitraryCount8 = 0xcdef;
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
env.platform_device().mmio()[0x3c65] = kArbitraryCount5 << 16; // Timer F.
env.platform_device().mmio()[0x3c66] = kArbitraryCount6 << 16; // Timer G.
env.platform_device().mmio()[0x3c67] = kArbitraryCount7 << 16; // Timer H.
env.platform_device().mmio()[0x3c68] = kArbitraryCount8 << 16; // Timer I.
});
for (uint64_t i = 5; i < 9; ++i) {
auto result_start = client_->Start(
{i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), kArbitraryTicksRequest});
ASSERT_FALSE(result_start.is_error());
}
// Ticks have decreased by 0xffff - kArbitraryCount (register read).
{
auto result = client_->GetTicksLeft(5);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryTicksRequest - (0xffff - kArbitraryCount5));
}
{
auto result = client_->GetTicksLeft(6);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryTicksRequest - (0xffff - kArbitraryCount6));
}
{
auto result = client_->GetTicksLeft(7);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryTicksRequest - (0xffff - kArbitraryCount7));
}
{
auto result = client_->GetTicksLeft(8);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryTicksRequest - (0xffff - kArbitraryCount8));
}
}
TEST_F(DriverTest, GetTicksTimers5678ArbitraryCountWithIrq) {
// Can start up to 64 bits because they support ticks extension.
constexpr uint64_t kTicksRequestEnoughFor2Irqs = 0x1'1235;
constexpr uint64_t kArbitraryCount = 0x1234;
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
env.platform_device().mmio()[0x3c65] = kArbitraryCount << 16; // Timer F.
env.platform_device().mmio()[0x3c66] = kArbitraryCount << 16; // Timer G.
env.platform_device().mmio()[0x3c67] = kArbitraryCount << 16; // Timer H.
env.platform_device().mmio()[0x3c68] = kArbitraryCount << 16; // Timer I.
});
for (uint64_t i = 5; i < 9; ++i) {
auto result_start =
client_->Start({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL),
kTicksRequestEnoughFor2Irqs});
ASSERT_FALSE(result_start.is_error());
// Because the requested ticks is biggger than 0xffff, before any IRQ triggers we'll get
// a decrease of 0xffff - kArbitraryCount (register read).
auto result = client_->GetTicksLeft(i);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kTicksRequestEnoughFor2Irqs - (0xffff - kArbitraryCount));
}
// Trigger IRQs, indicates that the first 0xffff passed.
driver_test().RunInEnvironmentTypeContext(
[](TestEnvironment& env) { env.platform_device().TriggerAllIrqs(); });
for (uint64_t i = 5; i < 9; ++i) {
// Wait until after the IRQ is handled and start ticks left fit in the hardware capabilities.
bool start_ticks_left_fit = false;
while (!start_ticks_left_fit) {
driver_test().RunInDriverContext([i, &start_ticks_left_fit](AmlHrtimer& driver) {
start_ticks_left_fit = driver.StartTicksLeftFitInHardware(i);
});
zx::nanosleep(zx::deadline_after(zx::msec(1)));
}
// Now that we have received at least an IRQ for the first 0xffff passed, GetTicksLeft starts
// returning the read from the register.
auto result = client_->GetTicksLeft(i);
ASSERT_FALSE(result.is_error());
ASSERT_EQ(result->ticks(), kArbitraryCount);
}
}
TEST_F(DriverTest, StartAndWaitTriggering) {
std::vector<std::thread> threads;
for (auto& i : kTimersSupportWait) {
threads.emplace_back([this, i]() {
zx::event setup_event;
ASSERT_EQ(ZX_OK, zx::event::create(0, &setup_event));
zx::event duplicate_event;
setup_event.duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate_event);
auto result_start =
client_->StartAndWait({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0,
std::move(duplicate_event)});
ASSERT_FALSE(result_start.is_error());
ASSERT_TRUE(result_start->keep_alive().is_valid());
// setup_event must have been signaled since the timer expired.
zx_signals_t signals = {};
ASSERT_EQ(setup_event.wait_one(ZX_EVENT_SIGNALED, zx::time::infinite(), &signals), ZX_OK);
});
// Wait until the driver has acquired the timer wait completer before triggering the IRQ.
bool has_wait_completer = false;
while (!has_wait_completer) {
driver_test().RunInDriverContext([i, &has_wait_completer](AmlHrtimer& driver) {
has_wait_completer = driver.HasWaitCompleter(i);
});
zx::nanosleep(zx::deadline_after(zx::msec(1)));
}
}
driver_test().RunInEnvironmentTypeContext(
[](TestEnvironment& env) { env.platform_device().TriggerAllIrqs(); });
// Join the threads such that we check for timers triggered.
for (auto& thread : threads) {
thread.join();
}
CheckInspect("0", "StartAndWait", 0, 0);
CheckInspect("1", "StartHardware", 0, 0);
CheckInspect("2", "StartAndWait", 1, 0);
CheckInspect("3", "StartHardware", 1, 0);
CheckInspect("4", "StartAndWait", 2, 0);
CheckInspect("5", "StartHardware", 2, 0);
CheckInspect("6", "StartAndWait", 3, 0);
CheckInspect("7", "StartHardware", 3, 0);
CheckInspect("8", "StartAndWait", 5, 0);
CheckInspect("9", "StartHardware", 5, 0);
CheckInspect("10", "StartAndWait", 6, 0);
CheckInspect("11", "StartHardware", 6, 0);
CheckInspect("12", "StartAndWait", 7, 0);
CheckInspect("13", "StartHardware", 7, 0);
CheckInspect("14", "StartAndWait", 8, 0);
CheckInspect("15", "StartHardware", 8, 0);
// Not checking TriggerIrqWait since we are not ordering IRQ triggers.
}
TEST_F(DriverTest, StartAndWait2Triggering) {
std::vector<std::thread> threads;
for (auto& i : kTimersSupportWait) {
threads.emplace_back([this, i]() {
zx::eventpair local_wake_lease, remote_wake_lease;
ASSERT_TRUE(fuchsia_power_system::LeaseToken::create(0, &local_wake_lease,
&remote_wake_lease) == ZX_OK);
auto result_start =
client_->StartAndWait2({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL),
0, std::move(remote_wake_lease)});
ASSERT_FALSE(result_start.is_error());
ASSERT_TRUE(result_start->expiration_keep_alive().is_valid());
});
// Wait until the driver has acquired the timer wait completer before triggering the IRQ.
bool has_wait_completer = false;
while (!has_wait_completer) {
driver_test().RunInDriverContext([i, &has_wait_completer](AmlHrtimer& driver) {
has_wait_completer = driver.HasWaitCompleter(i);
});
zx::nanosleep(zx::deadline_after(zx::msec(1)));
}
}
driver_test().RunInEnvironmentTypeContext(
[](TestEnvironment& env) { env.platform_device().TriggerAllIrqs(); });
// Join the threads such that we check for timers triggered.
for (auto& thread : threads) {
thread.join();
}
CheckInspect("0", "StartAndWait2", 0, 0);
CheckInspect("1", "StartHardware", 0, 0);
CheckInspect("2", "StartAndWait2", 1, 0);
CheckInspect("3", "StartHardware", 1, 0);
CheckInspect("4", "StartAndWait2", 2, 0);
CheckInspect("5", "StartHardware", 2, 0);
CheckInspect("6", "StartAndWait2", 3, 0);
CheckInspect("7", "StartHardware", 3, 0);
CheckInspect("8", "StartAndWait2", 5, 0);
CheckInspect("9", "StartHardware", 5, 0);
CheckInspect("10", "StartAndWait2", 6, 0);
CheckInspect("11", "StartHardware", 6, 0);
CheckInspect("12", "StartAndWait2", 7, 0);
CheckInspect("13", "StartHardware", 7, 0);
CheckInspect("14", "StartAndWait2", 8, 0);
CheckInspect("15", "StartHardware", 8, 0);
// Not checking TriggerIrqWait2 since we are not ordering IRQ triggers.
}
TEST_F(DriverTest, StartAndWaitStop) {
for (auto& i : kTimersSupportWait) {
std::thread thread([this, i]() {
zx::event setup_event;
ASSERT_EQ(ZX_OK, zx::event::create(0, &setup_event));
zx::event duplicate_event;
setup_event.duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate_event);
auto result_start =
client_->StartAndWait({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0,
std::move(duplicate_event)});
ASSERT_TRUE(result_start.is_error());
ASSERT_EQ(result_start.error_value().domain_error(),
fuchsia_hardware_hrtimer::DriverError::kCanceled);
// setup_event must have been signaled since the timer expired.
zx_signals_t signals = {};
ASSERT_EQ(setup_event.wait_one(ZX_EVENT_SIGNALED, zx::time::infinite(), &signals), ZX_OK);
});
// Wait until the driver has acquired a wait completer such that we can cancel the timer.
bool has_wait_completer = false;
while (!has_wait_completer) {
driver_test().RunInDriverContext([i, &has_wait_completer](AmlHrtimer& driver) {
has_wait_completer = driver.HasWaitCompleter(i);
});
zx::nanosleep(zx::deadline_after(zx::msec(1)));
}
auto result_start_stop = client_->Stop(i);
ASSERT_FALSE(result_start_stop.is_error());
thread.join();
}
CheckInspect("0", "StartAndWait", 0, 0);
CheckInspect("1", "StartHardware", 0, 0);
CheckInspect("2", "StopWait", 0, 0);
CheckInspect("3", "StartAndWait", 1, 0);
CheckInspect("4", "StartHardware", 1, 0);
CheckInspect("5", "StopWait", 1, 0);
CheckInspect("6", "StartAndWait", 2, 0);
CheckInspect("7", "StartHardware", 2, 0);
CheckInspect("8", "StopWait", 2, 0);
CheckInspect("9", "StartAndWait", 3, 0);
CheckInspect("10", "StartHardware", 3, 0);
CheckInspect("11", "StopWait", 3, 0);
CheckInspect("12", "StartAndWait", 5, 0);
CheckInspect("13", "StartHardware", 5, 0);
CheckInspect("14", "StopWait", 5, 0);
CheckInspect("15", "StartAndWait", 6, 0);
CheckInspect("16", "StartHardware", 6, 0);
CheckInspect("17", "StopWait", 6, 0);
CheckInspect("18", "StartAndWait", 7, 0);
CheckInspect("19", "StartHardware", 7, 0);
CheckInspect("20", "StopWait", 7, 0);
CheckInspect("21", "StartAndWait", 8, 0);
CheckInspect("22", "StartHardware", 8, 0);
CheckInspect("23", "StopWait", 8, 0);
}
TEST_F(DriverTest, StartAndWait2Stop) {
for (auto& i : kTimersSupportWait) {
std::thread thread([this, i]() {
zx::eventpair local_wake_lease, remote_wake_lease;
ASSERT_TRUE(fuchsia_power_system::LeaseToken::create(0, &local_wake_lease,
&remote_wake_lease) == ZX_OK);
auto result_start =
client_->StartAndWait2({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL),
0, std::move(remote_wake_lease)});
ASSERT_TRUE(result_start.is_error());
ASSERT_EQ(result_start.error_value().domain_error(),
fuchsia_hardware_hrtimer::DriverError::kCanceled);
});
// Wait until the driver has acquired a wait completer such that we can cancel the timer.
bool has_wait_completer = false;
while (!has_wait_completer) {
driver_test().RunInDriverContext([i, &has_wait_completer](AmlHrtimer& driver) {
has_wait_completer = driver.HasWaitCompleter(i);
});
zx::nanosleep(zx::deadline_after(zx::msec(1)));
}
auto result_start_stop = client_->Stop(i);
ASSERT_FALSE(result_start_stop.is_error());
thread.join();
}
CheckInspect("0", "StartAndWait2", 0, 0);
CheckInspect("1", "StartHardware", 0, 0);
CheckInspect("2", "StopWait2", 0, 0);
CheckInspect("3", "StartAndWait2", 1, 0);
CheckInspect("4", "StartHardware", 1, 0);
CheckInspect("5", "StopWait2", 1, 0);
CheckInspect("6", "StartAndWait2", 2, 0);
CheckInspect("7", "StartHardware", 2, 0);
CheckInspect("8", "StopWait2", 2, 0);
CheckInspect("9", "StartAndWait2", 3, 0);
CheckInspect("10", "StartHardware", 3, 0);
CheckInspect("11", "StopWait2", 3, 0);
CheckInspect("12", "StartAndWait2", 5, 0);
CheckInspect("13", "StartHardware", 5, 0);
CheckInspect("14", "StopWait2", 5, 0);
CheckInspect("15", "StartAndWait2", 6, 0);
CheckInspect("16", "StartHardware", 6, 0);
CheckInspect("17", "StopWait2", 6, 0);
CheckInspect("18", "StartAndWait2", 7, 0);
CheckInspect("19", "StartHardware", 7, 0);
CheckInspect("20", "StopWait2", 7, 0);
CheckInspect("21", "StartAndWait2", 8, 0);
CheckInspect("22", "StartHardware", 8, 0);
CheckInspect("23", "StopWait2", 8, 0);
}
class DriverTestNoAutoStop : public ::testing::Test {
public:
void SetUp() override {
zx::result<> result =
driver_test().StartDriverWithCustomStartArgs([](fdf::DriverStartArgs& start_args) mutable {
aml_hrtimer_config::Config fake_config;
fake_config.enable_suspend() = true;
start_args.config(fake_config.ToVmo());
});
ASSERT_EQ(ZX_OK, result.status_value());
zx::result device_result =
driver_test().ConnectThroughDevfs<fuchsia_hardware_hrtimer::Device>("aml-hrtimer");
ASSERT_EQ(ZX_OK, device_result.status_value());
client_.Bind(std::move(device_result.value()));
}
fdf_testing::BackgroundDriverTest<FixtureConfig>& driver_test() { return driver_test_; }
fdf_testing::BackgroundDriverTest<FixtureConfig> driver_test_;
fidl::SyncClient<fuchsia_hardware_hrtimer::Device> client_;
};
TEST_F(DriverTestNoAutoStop, CancelOnDriverStop) {
std::vector<std::thread> threads;
zx::event events[kNumberOfTimers];
for (auto& i : kTimersSupportWait) {
ASSERT_EQ(zx::event::create(0, &events[i]), ZX_OK);
zx::event duplicate_event;
events[i].duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate_event);
auto result_event = client_->SetEvent({i, std::move(duplicate_event)});
ASSERT_FALSE(result_event.is_error());
threads.emplace_back([this, i]() {
zx::event setup_event;
ASSERT_EQ(ZX_OK, zx::event::create(0, &setup_event));
auto result_start =
client_->StartAndWait({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0,
std::move(setup_event)});
ASSERT_TRUE(result_start.is_error());
// Check that we cancel on driver stop.
ASSERT_EQ(result_start.error_value().domain_error(),
fuchsia_hardware_hrtimer::DriverError::kCanceled);
});
// Wait until the driver has acquired a wait completer such that it can be canceled.
bool has_wait_completer = false;
while (!has_wait_completer) {
driver_test().RunInDriverContext([i, &has_wait_completer](AmlHrtimer& driver) {
has_wait_completer = driver.HasWaitCompleter(i);
});
zx::nanosleep(zx::deadline_after(zx::msec(1)));
}
}
// Start timer 4 as well.
auto result_start =
client_->Start({4ULL, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL),
0xffff'ffff'ffff'ffffULL});
ASSERT_FALSE(result_start.is_error());
// Force driver stop.
auto result_stop_driver = driver_test().StopDriver();
ASSERT_FALSE(result_stop_driver.is_error());
// Join the threads such that we check for timers canceled.
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(DriverTest, LeaseRequested0) { CheckLeaseRequested(0); }
TEST_F(DriverTest, LeaseRequested1) { CheckLeaseRequested(1); }
TEST_F(DriverTest, LeaseRequested2) { CheckLeaseRequested(2); }
TEST_F(DriverTest, LeaseRequested3) { CheckLeaseRequested(3); }
TEST_F(DriverTest, LeaseNotRequested4) {
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
ASSERT_FALSE(env.system_activity_governor().GetLeaseRequested());
});
zx::event setup_event;
ASSERT_EQ(ZX_OK, zx::event::create(0, &setup_event));
auto result_start = client_->StartAndWait(
{4, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0, std::move(setup_event)});
ASSERT_TRUE(result_start.is_error());
driver_test().RunInEnvironmentTypeContext([](TestEnvironment& env) {
ASSERT_FALSE(env.system_activity_governor().GetLeaseRequested());
});
}
TEST_F(DriverTest, LeaseRequested5) { CheckLeaseRequested(5); }
TEST_F(DriverTest, LeaseRequested6) { CheckLeaseRequested(6); }
TEST_F(DriverTest, LeaseRequested7) { CheckLeaseRequested(7); }
TEST_F(DriverTest, LeaseRequested8) { CheckLeaseRequested(8); }
class TestEnvironmentNoPower : public fdf_testing::Environment {
public:
zx::result<> Serve(fdf::OutgoingDirectory& to_driver_vfs) override {
platform_device_.InitResources();
auto result = to_driver_vfs.AddService<fuchsia_hardware_platform_device::Service>(
platform_device_.GetInstanceHandler());
EXPECT_EQ(ZX_OK, result.status_value());
return zx::ok();
}
FakePlatformDevice& platform_device() { return platform_device_; }
private:
FakePlatformDevice platform_device_;
};
class FixtureConfigNoPower final {
public:
using DriverType = AmlHrtimer;
using EnvironmentType = TestEnvironmentNoPower;
};
class DriverTestNoPower : public ::testing::Test {
public:
void TearDown() override {
zx::result<> result = driver_test().StopDriver();
ASSERT_EQ(ZX_OK, result.status_value());
}
void SetUp() override {
zx::result<> result =
driver_test().StartDriverWithCustomStartArgs([](fdf::DriverStartArgs& start_args) mutable {
aml_hrtimer_config::Config fake_config;
fake_config.enable_suspend() = false;
start_args.config(fake_config.ToVmo());
});
ASSERT_EQ(ZX_OK, result.status_value());
zx::result device_result =
driver_test().ConnectThroughDevfs<fuchsia_hardware_hrtimer::Device>("aml-hrtimer");
ASSERT_EQ(ZX_OK, device_result.status_value());
client_.Bind(std::move(device_result.value()));
}
fdf_testing::BackgroundDriverTest<FixtureConfigNoPower>& driver_test() { return driver_test_; }
fdf_testing::BackgroundDriverTest<FixtureConfigNoPower> driver_test_;
fidl::SyncClient<fuchsia_hardware_hrtimer::Device> client_;
};
TEST_F(DriverTestNoPower, StartAndWaitTriggeringNoPower) {
for (auto& i : kTimersSupportWait) {
zx::event setup_event;
ASSERT_EQ(ZX_OK, zx::event::create(0, &setup_event));
auto result_start =
client_->StartAndWait({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0,
std::move(setup_event)});
ASSERT_TRUE(result_start.is_error()); // Must fail, no power configuration.
ASSERT_EQ(result_start.error_value().domain_error(),
fuchsia_hardware_hrtimer::DriverError::kBadState);
// setup_event is not signaled since the timer was not setup.
}
}
TEST_F(DriverTestNoPower, StartAndWait2TriggeringNoPower) {
for (auto& i : kTimersSupportWait) {
zx::eventpair local_wake_lease, remote_wake_lease;
ASSERT_TRUE(fuchsia_power_system::LeaseToken::create(0, &local_wake_lease,
&remote_wake_lease) == ZX_OK);
auto result_start =
client_->StartAndWait2({i, fuchsia_hardware_hrtimer::Resolution::WithDuration(1'000ULL), 0,
std::move(remote_wake_lease)});
ASSERT_TRUE(result_start.is_error()); // Must fail, no power configuration.
ASSERT_EQ(result_start.error_value().domain_error(),
fuchsia_hardware_hrtimer::DriverError::kBadState);
}
}
} // namespace hrtimer