10
10
#include < lib/driver/compat/cpp/logging.h> // nogncheck
11
11
#endif
12
12
13
+ #include < lib/zx/clock.h>
13
14
#include < threads.h>
14
15
#include < zircon/syscalls-next.h>
15
16
#include < zircon/threads.h>
@@ -48,19 +49,88 @@ fit::closure DriverTransportWriteOperation::MakeCallback(zx_status_t status) {
48
49
49
50
} // namespace internal
50
51
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)
55
60
: pdev_(std::move(pdev)),
56
61
serial_port_info_ (serial_port_info),
57
62
mmio_(std::move(mmio)),
58
63
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
+
59
72
if (lessor_client_end != std::nullopt) {
60
73
lessor_client_.Bind (std::move (*lessor_client_end));
61
74
} else {
62
75
zxlogf (INFO, " No lessor client end gets passed into AmlUart during initialization." );
63
76
}
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
+ });
64
134
}
65
135
66
136
constexpr auto kMinBaudRate = 2 ;
@@ -200,6 +270,10 @@ void AmlUart::EnableLocked(bool enable) {
200
270
}
201
271
}
202
272
273
+ fidl::ClientEnd<fuchsia_power_broker::ElementControl>& AmlUart::element_control_for_testing () {
274
+ return *element_control_client_end_;
275
+ }
276
+
203
277
void AmlUart::HandleTXRaceForTest () {
204
278
{
205
279
fbl::AutoLock al (&enable_lock_);
@@ -220,6 +294,12 @@ void AmlUart::HandleRXRaceForTest() {
220
294
HandleRX ();
221
295
}
222
296
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
+
223
303
zx_status_t AmlUart::Enable (bool enable) {
224
304
fbl::AutoLock al (&enable_lock_);
225
305
@@ -388,11 +468,60 @@ void AmlUart::handle_unknown_method(
388
468
zxlogf (WARNING, " handle_unknown_method in fuchsia_hardware_serialimpl::Device server." );
389
469
}
390
470
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
+
391
499
void AmlUart::HandleIrq (async_dispatcher_t * dispatcher, async::IrqBase* irq, zx_status_t status,
392
500
const zx_packet_interrupt_t * interrupt) {
393
501
if (status != ZX_OK) {
394
502
return ;
395
503
}
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
+ }
396
525
397
526
auto uart_status = Status::Get ().ReadFrom (&mmio_);
398
527
if (!uart_status.rx_empty ()) {
@@ -405,4 +534,19 @@ void AmlUart::HandleIrq(async_dispatcher_t* dispatcher, async::IrqBase* irq, zx_
405
534
irq_.ack ();
406
535
}
407
536
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
+ }
408
552
} // namespace serial
0 commit comments