Skip to content

Commit c627122

Browse files
Zhiyi ChenCQ Bot
authored andcommitted
[aml-uart] Add lease management based on timer
Introduce a timer in the driver so that the driver can hold the wake lease for a certain time period before dropping it. The driver resets the timer each time when the wake vector interrupt is asserted and drops the lease then the timer times out. Bug: b/339233136 Test: New unittests and observation on begonia(The driver doesn't drop the lease while packets coming in, and drops the lease a while after the last packet). Multiply: aml-uart-dfv2-test Change-Id: Icc047c9609d39f9973b52385752e3e6cce0abe05 Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/1049252 Commit-Queue: Zhiyi Chen <zhiyichen@google.com> Reviewed-by: Andres Oportus <andresoportus@google.com>
1 parent e4e9382 commit c627122

File tree

8 files changed

+524
-54
lines changed

8 files changed

+524
-54
lines changed

src/devices/serial/drivers/aml-uart/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ test("aml-uart-dfv2-test-bin") {
195195
sources = [
196196
"tests/aml-uart-dfv2-test.cc",
197197
"tests/device_state.h",
198+
"tests/fake_timer.cc",
198199
]
199200
deps = [
200201
":aml-uart-dfv2-source",

src/devices/serial/drivers/aml-uart/aml-uart-dfv2.cc

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <lib/ddk/metadata.h>
88
#include <lib/driver/component/cpp/driver_export.h>
99
#include <lib/driver/component/cpp/node_add_args.h>
10+
#include <lib/driver/power/cpp/element-description-builder.h>
1011
#include <lib/driver/power/cpp/power-support.h>
1112

1213
namespace serial {
@@ -50,7 +51,8 @@ void AmlUartV2::PrepareStop(fdf::PrepareStopCompleter completer) {
5051
prepare_stop_completer_.emplace(std::move(completer));
5152
irq_dispatcher_->ShutdownAsync();
5253
} else {
53-
// No irq_dispatcher_, just reply to the PrepareStopCompleter.
54+
// No irq_dispatcher_, just reply to the PrepareStopCompleter, timer_dispatcher_ is created
55+
// after irq_dispatcher_, so we assume that there's no timer_dispatcher_ either.
5456
completer(zx::ok());
5557
}
5658
}
@@ -60,10 +62,6 @@ AmlUart& AmlUartV2::aml_uart_for_testing() {
6062
return aml_uart_.value();
6163
}
6264

63-
fidl::ClientEnd<fuchsia_power_broker::ElementControl>& AmlUartV2::element_control_for_testing() {
64-
return element_control_client_end_;
65-
}
66-
6765
void AmlUartV2::OnReceivedMetadata(
6866
fidl::WireUnownedResult<fuchsia_driver_compat::Device::GetMetadata>& metadata_result) {
6967
if (!metadata_result.ok()) {
@@ -163,20 +161,19 @@ zx_status_t AmlUartV2::GetPowerConfiguration(
163161
return ZX_ERR_INTERNAL;
164162
}
165163

166-
zx::result lessor_endpoints = fidl::CreateEndpoints<fuchsia_power_broker::Lessor>();
167-
168-
auto result_add_element =
169-
fdf_power::AddElement(power_broker.value(), config, std::move(tokens.value()), {}, {}, {},
170-
std::move(lessor_endpoints->server));
164+
fdf_power::ElementDesc description =
165+
fdf_power::ElementDescBuilder(config, std::move(tokens.value())).Build();
166+
auto result_add_element = fdf_power::AddElement(power_broker.value(), description);
171167
if (result_add_element.is_error()) {
172168
FDF_LOG(ERROR, "Failed to add power element: %u",
173169
static_cast<uint8_t>(result_add_element.error_value()));
174170
return ZX_ERR_INTERNAL;
175171
}
176172

177173
element_control_client_end_ = std::move(result_add_element->element_control_channel());
178-
lessor_client_end_ = std::move(lessor_endpoints->client);
179-
174+
lessor_client_end_ = std::move(*description.lessor_client_);
175+
current_level_client_end_ = std::move(*description.current_level_client_);
176+
required_level_client_end_ = std::move(*description.required_level_client_);
180177
return ZX_OK;
181178
}
182179

@@ -216,6 +213,11 @@ void AmlUartV2::OnDeviceServerInitialized(zx::result<> device_server_init_result
216213

217214
zx::result irq_dispatcher_result =
218215
fdf::SynchronizedDispatcher::Create({}, "aml_uart_irq", [this](fdf_dispatcher_t*) {
216+
// Shutdown timer dispatcher if there is one, otherwise call prepare_stop_completer_.
217+
if (timer_dispatcher_) {
218+
timer_dispatcher_->ShutdownAsync();
219+
return;
220+
}
219221
if (prepare_stop_completer_.has_value()) {
220222
fdf::PrepareStopCompleter completer = std::move(prepare_stop_completer_.value());
221223
prepare_stop_completer_.reset();
@@ -229,8 +231,27 @@ void AmlUartV2::OnDeviceServerInitialized(zx::result<> device_server_init_result
229231
}
230232

231233
irq_dispatcher_.emplace(std::move(irq_dispatcher_result.value()));
234+
235+
zx::result timer_dispatcher_result =
236+
fdf::SynchronizedDispatcher::Create({}, "aml_uart_timer", [this](fdf_dispatcher_t*) {
237+
if (prepare_stop_completer_.has_value()) {
238+
fdf::PrepareStopCompleter completer = std::move(prepare_stop_completer_.value());
239+
prepare_stop_completer_.reset();
240+
completer(zx::ok());
241+
}
242+
});
243+
if (timer_dispatcher_result.is_error()) {
244+
FDF_LOG(ERROR, "Failed to create timer dispatcher: %s",
245+
timer_dispatcher_result.status_string());
246+
CompleteStart(timer_dispatcher_result.take_error());
247+
return;
248+
}
249+
250+
timer_dispatcher_.emplace(std::move(timer_dispatcher_result.value()));
232251
aml_uart_.emplace(std::move(pdev), serial_port_info_, std::move(mmio.value()),
233-
irq_dispatcher_->borrow(), std::move(lessor_client_end_));
252+
irq_dispatcher_->borrow(), timer_dispatcher_->borrow(),
253+
std::move(lessor_client_end_), std::move(current_level_client_end_),
254+
std::move(required_level_client_end_), std::move(element_control_client_end_));
234255

235256
// Default configuration for the case that serial_impl_config is not called.
236257
constexpr uint32_t kDefaultBaudRate = 115200;

src/devices/serial/drivers/aml-uart/aml-uart-dfv2.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ class AmlUartV2 : public fdf::DriverBase {
2626
// Used by the unit test to access the device.
2727
AmlUart& aml_uart_for_testing();
2828

29-
fidl::ClientEnd<fuchsia_power_broker::ElementControl>& element_control_for_testing();
30-
3129
private:
3230
zx_status_t GetPowerConfiguration(
3331
const fidl::WireSyncClient<fuchsia_hardware_platform_device::Device>& pdev);
@@ -47,6 +45,7 @@ class AmlUartV2 : public fdf::DriverBase {
4745
fidl::WireClient<fuchsia_driver_framework::Node> parent_node_client_;
4846
fuchsia_hardware_serial::wire::SerialPortInfo serial_port_info_;
4947
std::optional<fdf::SynchronizedDispatcher> irq_dispatcher_;
48+
std::optional<fdf::SynchronizedDispatcher> timer_dispatcher_;
5049
std::optional<AmlUart> aml_uart_;
5150
std::optional<fdf::PrepareStopCompleter> prepare_stop_completer_;
5251
compat::AsyncInitializedDeviceServer device_server_;
@@ -55,7 +54,9 @@ class AmlUartV2 : public fdf::DriverBase {
5554
aml_uart_dfv2_config::Config driver_config_;
5655

5756
// Client ends for talking to power broker.
58-
fidl::ClientEnd<fuchsia_power_broker::ElementControl> element_control_client_end_;
57+
std::optional<fidl::ClientEnd<fuchsia_power_broker::CurrentLevel>> current_level_client_end_;
58+
std::optional<fidl::ClientEnd<fuchsia_power_broker::RequiredLevel>> required_level_client_end_;
59+
std::optional<fidl::ClientEnd<fuchsia_power_broker::ElementControl>> element_control_client_end_;
5960
std::optional<fidl::ClientEnd<fuchsia_power_broker::Lessor>> lessor_client_end_;
6061
};
6162

src/devices/serial/drivers/aml-uart/aml-uart.cc

Lines changed: 148 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <lib/driver/compat/cpp/logging.h> // nogncheck
1111
#endif
1212

13+
#include <lib/zx/clock.h>
1314
#include <threads.h>
1415
#include <zircon/syscalls-next.h>
1516
#include <zircon/threads.h>
@@ -48,19 +49,88 @@ fit::closure DriverTransportWriteOperation::MakeCallback(zx_status_t status) {
4849

4950
} // namespace internal
5051

51-
AmlUart::AmlUart(ddk::PDevFidl pdev,
52-
const fuchsia_hardware_serial::wire::SerialPortInfo& serial_port_info,
53-
fdf::MmioBuffer mmio, fdf::UnownedSynchronizedDispatcher irq_dispatcher,
54-
std::optional<fidl::ClientEnd<fuchsia_power_broker::Lessor>> lessor_client_end)
52+
AmlUart::AmlUart(
53+
ddk::PDevFidl pdev, const fuchsia_hardware_serial::wire::SerialPortInfo& serial_port_info,
54+
fdf::MmioBuffer mmio, fdf::UnownedSynchronizedDispatcher irq_dispatcher,
55+
std::optional<fdf::UnownedSynchronizedDispatcher> timer_dispatcher,
56+
std::optional<fidl::ClientEnd<fuchsia_power_broker::Lessor>> lessor_client_end,
57+
std::optional<fidl::ClientEnd<fuchsia_power_broker::CurrentLevel>> current_level_client_end,
58+
std::optional<fidl::ClientEnd<fuchsia_power_broker::RequiredLevel>> required_level_client_end,
59+
std::optional<fidl::ClientEnd<fuchsia_power_broker::ElementControl>> element_control_client_end)
5560
: pdev_(std::move(pdev)),
5661
serial_port_info_(serial_port_info),
5762
mmio_(std::move(mmio)),
5863
irq_dispatcher_(std::move(irq_dispatcher)) {
64+
if (timer_dispatcher != std::nullopt) {
65+
timer_dispatcher_ = std::move(*timer_dispatcher);
66+
// Use ZX_TIMER_SLACK_LATE so that the timer will never fire earlier than the deadline.
67+
zx::timer::create(ZX_TIMER_SLACK_LATE, ZX_CLOCK_MONOTONIC, &lease_timer_);
68+
timer_waiter_.set_object(lease_timer_.get());
69+
timer_waiter_.set_trigger(ZX_TIMER_SIGNALED);
70+
}
71+
5972
if (lessor_client_end != std::nullopt) {
6073
lessor_client_.Bind(std::move(*lessor_client_end));
6174
} else {
6275
zxlogf(INFO, "No lessor client end gets passed into AmlUart during initialization.");
6376
}
77+
78+
if (required_level_client_end != std::nullopt) {
79+
required_level_client_.Bind(std::move(*required_level_client_end),
80+
fdf::Dispatcher::GetCurrent()->async_dispatcher());
81+
} else {
82+
zxlogf(INFO, "No required level client end gets passed into AmlUart during initialization.");
83+
}
84+
85+
if (current_level_client_end != std::nullopt) {
86+
current_level_client_.Bind(std::move(*current_level_client_end));
87+
} else {
88+
zxlogf(INFO, "No current level client end gets passed into AmlUart during initialization.");
89+
}
90+
91+
if (element_control_client_end != std::nullopt) {
92+
element_control_client_end_ = std::move(element_control_client_end);
93+
WatchRequiredLevel();
94+
} else {
95+
zxlogf(INFO, "No element control client end gets passed into AmlUart during initialization.");
96+
}
97+
}
98+
99+
// Keep watching the required level, since the driver doesn't toggle the hardware power state
100+
// according to power element levels, report the current level to power broker immediately with the
101+
// required level value received from power broker.
102+
void AmlUart::WatchRequiredLevel() {
103+
if (!required_level_client_.is_valid()) {
104+
zxlogf(ERROR, "RequiredLevel not valid, can't monitor it");
105+
element_control_client_end_.reset();
106+
return;
107+
}
108+
required_level_client_->Watch().Then(
109+
[this](fidl::Result<fuchsia_power_broker::RequiredLevel::Watch>& result) {
110+
if (result.is_error()) {
111+
if (result.error_value().is_framework_error() &&
112+
result.error_value().framework_error().status() != ZX_ERR_CANCELED) {
113+
zxlogf(ERROR, "Power level required call failed: %s. Stop monitoring required level",
114+
result.error_value().FormatDescription().c_str());
115+
}
116+
element_control_client_end_.reset();
117+
return;
118+
}
119+
zxlogf(DEBUG, "RequiredLevel : %u", static_cast<uint8_t>(result->required_level()));
120+
if (!current_level_client_.is_valid()) {
121+
zxlogf(ERROR, "CurrentLevel not valid. Stop monitoring required level");
122+
element_control_client_end_.reset();
123+
return;
124+
}
125+
auto update_result = current_level_client_->Update(result->required_level());
126+
if (update_result.is_error()) {
127+
zxlogf(ERROR, "CurrentLevel call failed: %s. Stop monitoring required level",
128+
update_result.error_value().FormatDescription().c_str());
129+
element_control_client_end_.reset();
130+
return;
131+
}
132+
WatchRequiredLevel();
133+
});
64134
}
65135

66136
constexpr auto kMinBaudRate = 2;
@@ -200,6 +270,10 @@ void AmlUart::EnableLocked(bool enable) {
200270
}
201271
}
202272

273+
fidl::ClientEnd<fuchsia_power_broker::ElementControl>& AmlUart::element_control_for_testing() {
274+
return *element_control_client_end_;
275+
}
276+
203277
void AmlUart::HandleTXRaceForTest() {
204278
{
205279
fbl::AutoLock al(&enable_lock_);
@@ -220,6 +294,12 @@ void AmlUart::HandleRXRaceForTest() {
220294
HandleRX();
221295
}
222296

297+
void AmlUart::InjectTimerForTest(zx_handle_t handle) {
298+
lease_timer_.reset(handle);
299+
timer_waiter_.set_object(lease_timer_.get());
300+
timer_waiter_.set_trigger(ZX_TIMER_SIGNALED);
301+
}
302+
223303
zx_status_t AmlUart::Enable(bool enable) {
224304
fbl::AutoLock al(&enable_lock_);
225305

@@ -388,11 +468,60 @@ void AmlUart::handle_unknown_method(
388468
zxlogf(WARNING, "handle_unknown_method in fuchsia_hardware_serialimpl::Device server.");
389469
}
390470

471+
zx::result<fidl::ClientEnd<fuchsia_power_broker::LeaseControl>> AmlUart::AcquireLease() {
472+
auto lease_result = lessor_client_->Lease(kPowerLevelHandling);
473+
if (lease_result.is_error()) {
474+
zxlogf(ERROR, "Failed to aquire lease: %s",
475+
lease_result.error_value().FormatDescription().c_str());
476+
return zx::error(ZX_ERR_BAD_STATE);
477+
}
478+
if (!lease_result->lease_control().is_valid()) {
479+
zxlogf(ERROR, "Lease returned invalid lease control client end.");
480+
return zx::error(ZX_ERR_BAD_STATE);
481+
}
482+
// Wait until the lease is actually granted.
483+
fidl::SyncClient<fuchsia_power_broker::LeaseControl> lease_control_client(
484+
std::move(lease_result->lease_control()));
485+
fuchsia_power_broker::LeaseStatus current_lease_status =
486+
fuchsia_power_broker::LeaseStatus::kUnknown;
487+
do {
488+
auto watch_result = lease_control_client->WatchStatus(current_lease_status);
489+
if (watch_result.is_error()) {
490+
zxlogf(ERROR, "WatchStatus returned error: %s",
491+
watch_result.error_value().FormatDescription().c_str());
492+
return zx::error(ZX_ERR_BAD_STATE);
493+
}
494+
current_lease_status = watch_result->status();
495+
} while (current_lease_status != fuchsia_power_broker::LeaseStatus::kSatisfied);
496+
return zx::ok(lease_control_client.TakeClientEnd());
497+
}
498+
391499
void AmlUart::HandleIrq(async_dispatcher_t* dispatcher, async::IrqBase* irq, zx_status_t status,
392500
const zx_packet_interrupt_t* interrupt) {
393501
if (status != ZX_OK) {
394502
return;
395503
}
504+
// If the element control client end is not available, it means that power management is not
505+
// enabled for this driver, the driver will not take a wake lease and set timer in this case.
506+
if (element_control_client_end_) {
507+
fbl::AutoLock lock(&timer_lock_);
508+
509+
if (lease_control_client_end_.is_valid()) {
510+
// Cancel the timer and set it again if the last one hasn't expired.
511+
lease_timer_.cancel();
512+
} else {
513+
// Take the wake lease and keep the lease control client.
514+
auto lease_control_result = AcquireLease();
515+
if (lease_control_result.is_error()) {
516+
return;
517+
}
518+
lease_control_client_end_ = std::move(*lease_control_result);
519+
}
520+
521+
timeout_ = zx::deadline_after(zx::msec(kPowerLeaseTimeoutMs));
522+
timer_waiter_.Begin(timer_dispatcher_->async_dispatcher());
523+
lease_timer_.set(timeout_, zx::duration());
524+
}
396525

397526
auto uart_status = Status::Get().ReadFrom(&mmio_);
398527
if (!uart_status.rx_empty()) {
@@ -405,4 +534,19 @@ void AmlUart::HandleIrq(async_dispatcher_t* dispatcher, async::IrqBase* irq, zx_
405534
irq_.ack();
406535
}
407536

537+
void AmlUart::HandleLeaseTimer(async_dispatcher_t* dispatcher, async::WaitBase* wait,
538+
zx_status_t status, const zx_packet_signal_t* signal) {
539+
fbl::AutoLock lock(&timer_lock_);
540+
if (status == ZX_ERR_CANCELED) {
541+
// Do nothing if the handler is triggered by the destroy of the dispatcher.
542+
return;
543+
}
544+
if (zx::clock::get_monotonic() < timeout_) {
545+
// If the current time is earlier than timeout, it means that this handler is triggered after
546+
// |HandleIrq| holds the lock, and when this handler get the lock, the timer has been reset, so
547+
// don't drop the lease in this case.
548+
return;
549+
}
550+
lease_control_client_end_.reset();
551+
}
408552
} // namespace serial

0 commit comments

Comments
 (0)