xref: /aosp_15_r20/external/pigweed/pw_uart/blocking_adapter.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_uart/blocking_adapter.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
18*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
19*61c4878aSAndroid Build Coastguard Worker #include "pw_status/try.h"
20*61c4878aSAndroid Build Coastguard Worker 
21*61c4878aSAndroid Build Coastguard Worker namespace pw::uart {
22*61c4878aSAndroid Build Coastguard Worker 
~UartBlockingAdapter()23*61c4878aSAndroid Build Coastguard Worker UartBlockingAdapter::~UartBlockingAdapter() {
24*61c4878aSAndroid Build Coastguard Worker   // We can't safely allow this because the driver still has a reference.
25*61c4878aSAndroid Build Coastguard Worker   // The safest thing to do here is crash.
26*61c4878aSAndroid Build Coastguard Worker   // Few applications are likely to ever call this destructor anyway.
27*61c4878aSAndroid Build Coastguard Worker   PW_CHECK(!rx_.pending());
28*61c4878aSAndroid Build Coastguard Worker   PW_CHECK(!tx_.pending());
29*61c4878aSAndroid Build Coastguard Worker }
30*61c4878aSAndroid Build Coastguard Worker 
31*61c4878aSAndroid Build Coastguard Worker // Uart impl.
DoTryReadFor(ByteSpan rx_buffer,size_t min_bytes,std::optional<chrono::SystemClock::duration> timeout)32*61c4878aSAndroid Build Coastguard Worker StatusWithSize UartBlockingAdapter::DoTryReadFor(
33*61c4878aSAndroid Build Coastguard Worker     ByteSpan rx_buffer,
34*61c4878aSAndroid Build Coastguard Worker     size_t min_bytes,
35*61c4878aSAndroid Build Coastguard Worker     std::optional<chrono::SystemClock::duration> timeout) {
36*61c4878aSAndroid Build Coastguard Worker   if (rx_.pending()) {
37*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("RX transaction already started");
38*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize::Unavailable();
39*61c4878aSAndroid Build Coastguard Worker   }
40*61c4878aSAndroid Build Coastguard Worker 
41*61c4878aSAndroid Build Coastguard Worker   // Start a new transfer.
42*61c4878aSAndroid Build Coastguard Worker   rx_.Start();
43*61c4878aSAndroid Build Coastguard Worker   auto status = uart_.ReadAtLeast(
44*61c4878aSAndroid Build Coastguard Worker       rx_buffer, min_bytes, [this](Status xfer_status, ConstByteSpan buffer) {
45*61c4878aSAndroid Build Coastguard Worker         rx_.Complete(StatusWithSize(xfer_status, buffer.size()));
46*61c4878aSAndroid Build Coastguard Worker       });
47*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
48*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize(status, 0);
49*61c4878aSAndroid Build Coastguard Worker   }
50*61c4878aSAndroid Build Coastguard Worker 
51*61c4878aSAndroid Build Coastguard Worker   // Wait for completion.
52*61c4878aSAndroid Build Coastguard Worker   if (rx_.WaitForCompletion(timeout)) {
53*61c4878aSAndroid Build Coastguard Worker     return rx_.result();
54*61c4878aSAndroid Build Coastguard Worker   }
55*61c4878aSAndroid Build Coastguard Worker 
56*61c4878aSAndroid Build Coastguard Worker   // Handle timeout by trying to cancel.
57*61c4878aSAndroid Build Coastguard Worker   return rx_.HandleTimeout(uart_.CancelRead());
58*61c4878aSAndroid Build Coastguard Worker }
59*61c4878aSAndroid Build Coastguard Worker 
DoTryWriteFor(ConstByteSpan tx_buffer,std::optional<chrono::SystemClock::duration> timeout)60*61c4878aSAndroid Build Coastguard Worker StatusWithSize UartBlockingAdapter::DoTryWriteFor(
61*61c4878aSAndroid Build Coastguard Worker     ConstByteSpan tx_buffer,
62*61c4878aSAndroid Build Coastguard Worker     std::optional<chrono::SystemClock::duration> timeout) {
63*61c4878aSAndroid Build Coastguard Worker   if (tx_.pending()) {
64*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("TX transaction already started");
65*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize::Unavailable();
66*61c4878aSAndroid Build Coastguard Worker   }
67*61c4878aSAndroid Build Coastguard Worker 
68*61c4878aSAndroid Build Coastguard Worker   // Start a new transfer.
69*61c4878aSAndroid Build Coastguard Worker   tx_.Start();
70*61c4878aSAndroid Build Coastguard Worker   auto status = uart_.Write(
71*61c4878aSAndroid Build Coastguard Worker       tx_buffer, [this](StatusWithSize result) { tx_.Complete(result); });
72*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
73*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize(status, 0);
74*61c4878aSAndroid Build Coastguard Worker   }
75*61c4878aSAndroid Build Coastguard Worker 
76*61c4878aSAndroid Build Coastguard Worker   // Wait for completion.
77*61c4878aSAndroid Build Coastguard Worker   if (tx_.WaitForCompletion(timeout)) {
78*61c4878aSAndroid Build Coastguard Worker     return tx_.result();
79*61c4878aSAndroid Build Coastguard Worker   }
80*61c4878aSAndroid Build Coastguard Worker 
81*61c4878aSAndroid Build Coastguard Worker   // Handle timeout by trying to cancel.
82*61c4878aSAndroid Build Coastguard Worker   return tx_.HandleTimeout(uart_.CancelWrite());
83*61c4878aSAndroid Build Coastguard Worker }
84*61c4878aSAndroid Build Coastguard Worker 
DoFlushOutput()85*61c4878aSAndroid Build Coastguard Worker Status UartBlockingAdapter::DoFlushOutput() {
86*61c4878aSAndroid Build Coastguard Worker   if (tx_.pending()) {
87*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Flush or write already started");
88*61c4878aSAndroid Build Coastguard Worker     return Status::Unavailable();
89*61c4878aSAndroid Build Coastguard Worker   }
90*61c4878aSAndroid Build Coastguard Worker 
91*61c4878aSAndroid Build Coastguard Worker   // Start a flush.
92*61c4878aSAndroid Build Coastguard Worker   tx_.Start();
93*61c4878aSAndroid Build Coastguard Worker   PW_TRY(uart_.FlushOutput([this](Status result) { tx_.Complete(result); }));
94*61c4878aSAndroid Build Coastguard Worker 
95*61c4878aSAndroid Build Coastguard Worker   // Wait for completion.
96*61c4878aSAndroid Build Coastguard Worker   tx_.WaitForCompletion();
97*61c4878aSAndroid Build Coastguard Worker   return tx_.result().status();
98*61c4878aSAndroid Build Coastguard Worker }
99*61c4878aSAndroid Build Coastguard Worker 
100*61c4878aSAndroid Build Coastguard Worker // UartBlockingAdapter::Transfer
Complete(StatusWithSize result)101*61c4878aSAndroid Build Coastguard Worker void UartBlockingAdapter::Transfer::Complete(StatusWithSize result) {
102*61c4878aSAndroid Build Coastguard Worker   result_ = result;
103*61c4878aSAndroid Build Coastguard Worker   pending_ = false;
104*61c4878aSAndroid Build Coastguard Worker   complete_.release();
105*61c4878aSAndroid Build Coastguard Worker }
106*61c4878aSAndroid Build Coastguard Worker 
WaitForCompletion(std::optional<chrono::SystemClock::duration> timeout)107*61c4878aSAndroid Build Coastguard Worker bool UartBlockingAdapter::Transfer::WaitForCompletion(
108*61c4878aSAndroid Build Coastguard Worker     std::optional<chrono::SystemClock::duration> timeout) {
109*61c4878aSAndroid Build Coastguard Worker   if (timeout) {
110*61c4878aSAndroid Build Coastguard Worker     return complete_.try_acquire_for(*timeout);
111*61c4878aSAndroid Build Coastguard Worker   }
112*61c4878aSAndroid Build Coastguard Worker   complete_.acquire();
113*61c4878aSAndroid Build Coastguard Worker   return true;
114*61c4878aSAndroid Build Coastguard Worker }
115*61c4878aSAndroid Build Coastguard Worker 
WaitForCompletion()116*61c4878aSAndroid Build Coastguard Worker void UartBlockingAdapter::Transfer::WaitForCompletion() {
117*61c4878aSAndroid Build Coastguard Worker   // With no timeout, this waits forever and must return true.
118*61c4878aSAndroid Build Coastguard Worker   PW_CHECK(WaitForCompletion(std::nullopt));
119*61c4878aSAndroid Build Coastguard Worker }
120*61c4878aSAndroid Build Coastguard Worker 
HandleTimeout(bool cancel_result)121*61c4878aSAndroid Build Coastguard Worker StatusWithSize UartBlockingAdapter::Transfer::HandleTimeout(
122*61c4878aSAndroid Build Coastguard Worker     bool cancel_result) {
123*61c4878aSAndroid Build Coastguard Worker   if (cancel_result) {
124*61c4878aSAndroid Build Coastguard Worker     // Cancelled successfully.
125*61c4878aSAndroid Build Coastguard Worker     //
126*61c4878aSAndroid Build Coastguard Worker     // The callback should have been invoked with a CANCELLED status, and
127*61c4878aSAndroid Build Coastguard Worker     // released the notification. Acquire the notification to safely retrieve
128*61c4878aSAndroid Build Coastguard Worker     // result.size().
129*61c4878aSAndroid Build Coastguard Worker     if (complete_.try_acquire()) {
130*61c4878aSAndroid Build Coastguard Worker       return StatusWithSize::DeadlineExceeded(result().size());
131*61c4878aSAndroid Build Coastguard Worker     }
132*61c4878aSAndroid Build Coastguard Worker 
133*61c4878aSAndroid Build Coastguard Worker     // We couldn't acquire the notification. The driver must be broken.
134*61c4878aSAndroid Build Coastguard Worker     PW_LOG_WARN(
135*61c4878aSAndroid Build Coastguard Worker         "Failed to acquire %s notification after successful cancel. "
136*61c4878aSAndroid Build Coastguard Worker         "UART driver seems to be broken!",
137*61c4878aSAndroid Build Coastguard Worker         what_);
138*61c4878aSAndroid Build Coastguard Worker   } else {
139*61c4878aSAndroid Build Coastguard Worker     // Couldn't cancel.
140*61c4878aSAndroid Build Coastguard Worker     //
141*61c4878aSAndroid Build Coastguard Worker     // Because we definitely started a transfer, either:
142*61c4878aSAndroid Build Coastguard Worker     // 1. The transaction finished just after the timeout. The callback ran
143*61c4878aSAndroid Build Coastguard Worker     //    (or is running); the notification was released (or is about to be
144*61c4878aSAndroid Build Coastguard Worker     //    released).
145*61c4878aSAndroid Build Coastguard Worker     // 2. The transaction couldn't be cancelled (past some point of no return).
146*61c4878aSAndroid Build Coastguard Worker     //    The callback will run with a non-CANCELLED status; the semaphore will
147*61c4878aSAndroid Build Coastguard Worker     //    be released.
148*61c4878aSAndroid Build Coastguard Worker     //
149*61c4878aSAndroid Build Coastguard Worker     // We could wait again, but there's really no point: If the completion
150*61c4878aSAndroid Build Coastguard Worker     // didn't already happen in the user-provided timeout, it seems unlikely to
151*61c4878aSAndroid Build Coastguard Worker     // happen now.
152*61c4878aSAndroid Build Coastguard Worker     //
153*61c4878aSAndroid Build Coastguard Worker     // Bail.
154*61c4878aSAndroid Build Coastguard Worker     PW_LOG_WARN("Failed to cancel %s transfer after timeout.", what_);
155*61c4878aSAndroid Build Coastguard Worker   }
156*61c4878aSAndroid Build Coastguard Worker 
157*61c4878aSAndroid Build Coastguard Worker   // Note that pending() may still be set, so future requests will fail.
158*61c4878aSAndroid Build Coastguard Worker   return StatusWithSize::DeadlineExceeded(0);
159*61c4878aSAndroid Build Coastguard Worker }
160*61c4878aSAndroid Build Coastguard Worker 
161*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::uart
162