xref: /aosp_15_r20/external/pigweed/pw_channel/rp2_stdio_channel.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_channel/rp2_stdio_channel.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include "pico/stdlib.h"
18*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
19*61c4878aSAndroid Build Coastguard Worker #include "pw_async2/dispatcher_base.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_multibuf/allocator.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_multibuf/multibuf.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
24*61c4878aSAndroid Build Coastguard Worker 
25*61c4878aSAndroid Build Coastguard Worker namespace pw::channel {
26*61c4878aSAndroid Build Coastguard Worker namespace {
27*61c4878aSAndroid Build Coastguard Worker 
28*61c4878aSAndroid Build Coastguard Worker using ::pw::async2::Context;
29*61c4878aSAndroid Build Coastguard Worker using ::pw::async2::Pending;
30*61c4878aSAndroid Build Coastguard Worker using ::pw::async2::Poll;
31*61c4878aSAndroid Build Coastguard Worker using ::pw::async2::Ready;
32*61c4878aSAndroid Build Coastguard Worker using ::pw::async2::Waker;
33*61c4878aSAndroid Build Coastguard Worker using ::pw::multibuf::MultiBuf;
34*61c4878aSAndroid Build Coastguard Worker using ::pw::multibuf::MultiBufAllocationFuture;
35*61c4878aSAndroid Build Coastguard Worker using ::pw::multibuf::MultiBufAllocator;
36*61c4878aSAndroid Build Coastguard Worker 
37*61c4878aSAndroid Build Coastguard Worker Waker global_chars_available_waker;
38*61c4878aSAndroid Build Coastguard Worker 
InitStdio()39*61c4878aSAndroid Build Coastguard Worker void InitStdio() {
40*61c4878aSAndroid Build Coastguard Worker   stdio_init_all();
41*61c4878aSAndroid Build Coastguard Worker   stdio_set_chars_available_callback(
42*61c4878aSAndroid Build Coastguard Worker       []([[maybe_unused]] void* arg) {
43*61c4878aSAndroid Build Coastguard Worker         std::move(global_chars_available_waker).Wake();
44*61c4878aSAndroid Build Coastguard Worker       },
45*61c4878aSAndroid Build Coastguard Worker       nullptr);
46*61c4878aSAndroid Build Coastguard Worker }
47*61c4878aSAndroid Build Coastguard Worker 
WriteMultiBuf(const MultiBuf & buf)48*61c4878aSAndroid Build Coastguard Worker void WriteMultiBuf(const MultiBuf& buf) {
49*61c4878aSAndroid Build Coastguard Worker   for (std::byte b : buf) {
50*61c4878aSAndroid Build Coastguard Worker     putchar_raw(static_cast<int>(b));
51*61c4878aSAndroid Build Coastguard Worker   }
52*61c4878aSAndroid Build Coastguard Worker }
53*61c4878aSAndroid Build Coastguard Worker 
PollReadByte(Context & cx)54*61c4878aSAndroid Build Coastguard Worker Poll<std::byte> PollReadByte(Context& cx) {
55*61c4878aSAndroid Build Coastguard Worker   int c = getchar_timeout_us(0);
56*61c4878aSAndroid Build Coastguard Worker   if (c == PICO_ERROR_TIMEOUT) {
57*61c4878aSAndroid Build Coastguard Worker     PW_ASYNC_STORE_WAKER(
58*61c4878aSAndroid Build Coastguard Worker         cx,
59*61c4878aSAndroid Build Coastguard Worker         global_chars_available_waker,
60*61c4878aSAndroid Build Coastguard Worker         "RP2StdioChannel is waiting for stdio chars available");
61*61c4878aSAndroid Build Coastguard Worker     // Read again to ensure that no race occurred.
62*61c4878aSAndroid Build Coastguard Worker     //
63*61c4878aSAndroid Build Coastguard Worker     // The concern is an interleaving like this
64*61c4878aSAndroid Build Coastguard Worker     // Thread one: get_char is called and times out
65*61c4878aSAndroid Build Coastguard Worker     // Thread two: char becomes available, Wake is called
66*61c4878aSAndroid Build Coastguard Worker     // Thread one: sets Waker
67*61c4878aSAndroid Build Coastguard Worker     //
68*61c4878aSAndroid Build Coastguard Worker     // In this interleaving, the task on Thread one is never awoken,
69*61c4878aSAndroid Build Coastguard Worker     // so we must check for available characters *after* setting the Waker.
70*61c4878aSAndroid Build Coastguard Worker     c = getchar_timeout_us(0);
71*61c4878aSAndroid Build Coastguard Worker     if (c == PICO_ERROR_TIMEOUT) {
72*61c4878aSAndroid Build Coastguard Worker       return Pending();
73*61c4878aSAndroid Build Coastguard Worker     }
74*61c4878aSAndroid Build Coastguard Worker   }
75*61c4878aSAndroid Build Coastguard Worker   return static_cast<std::byte>(c);
76*61c4878aSAndroid Build Coastguard Worker }
77*61c4878aSAndroid Build Coastguard Worker 
78*61c4878aSAndroid Build Coastguard Worker // Channel implementation which writes to and reads from rp2040's stdio.
79*61c4878aSAndroid Build Coastguard Worker //
80*61c4878aSAndroid Build Coastguard Worker // NOTE: only one Rp2StdioChannel may be in existence.
81*61c4878aSAndroid Build Coastguard Worker class Rp2StdioChannel final : public pw::channel::Implement<ByteReaderWriter> {
82*61c4878aSAndroid Build Coastguard Worker  public:
Rp2StdioChannel(MultiBufAllocator & read_allocator,MultiBufAllocator & write_allocator)83*61c4878aSAndroid Build Coastguard Worker   Rp2StdioChannel(MultiBufAllocator& read_allocator,
84*61c4878aSAndroid Build Coastguard Worker                   MultiBufAllocator& write_allocator)
85*61c4878aSAndroid Build Coastguard Worker       : read_allocation_future_(read_allocator),
86*61c4878aSAndroid Build Coastguard Worker         write_allocation_future_(write_allocator),
87*61c4878aSAndroid Build Coastguard Worker         buffer_(std::nullopt) {}
88*61c4878aSAndroid Build Coastguard Worker 
89*61c4878aSAndroid Build Coastguard Worker   Rp2StdioChannel(const Rp2StdioChannel&) = delete;
90*61c4878aSAndroid Build Coastguard Worker   Rp2StdioChannel& operator=(const Rp2StdioChannel&) = delete;
91*61c4878aSAndroid Build Coastguard Worker 
92*61c4878aSAndroid Build Coastguard Worker   // This is a singleton static, so there's no need for move constructors.
93*61c4878aSAndroid Build Coastguard Worker   Rp2StdioChannel(Rp2StdioChannel&&) = delete;
94*61c4878aSAndroid Build Coastguard Worker   Rp2StdioChannel& operator=(Rp2StdioChannel&&) = delete;
95*61c4878aSAndroid Build Coastguard Worker 
96*61c4878aSAndroid Build Coastguard Worker  private:
97*61c4878aSAndroid Build Coastguard Worker   static constexpr size_t kMinimumReadSize = 64;
98*61c4878aSAndroid Build Coastguard Worker   static constexpr size_t kDesiredReadSize = 1024;
99*61c4878aSAndroid Build Coastguard Worker 
100*61c4878aSAndroid Build Coastguard Worker   Poll<Status> PendGetReadBuffer(Context& cx);
101*61c4878aSAndroid Build Coastguard Worker 
102*61c4878aSAndroid Build Coastguard Worker   Poll<Result<MultiBuf>> DoPendRead(Context& cx) override;
103*61c4878aSAndroid Build Coastguard Worker 
104*61c4878aSAndroid Build Coastguard Worker   Poll<Status> DoPendReadyToWrite(Context& cx) override;
105*61c4878aSAndroid Build Coastguard Worker 
DoPendAllocateWriteBuffer(Context & cx,size_t min_bytes)106*61c4878aSAndroid Build Coastguard Worker   Poll<std::optional<MultiBuf>> DoPendAllocateWriteBuffer(
107*61c4878aSAndroid Build Coastguard Worker       Context& cx, size_t min_bytes) override {
108*61c4878aSAndroid Build Coastguard Worker     write_allocation_future_.SetDesiredSize(min_bytes);
109*61c4878aSAndroid Build Coastguard Worker     return write_allocation_future_.Pend(cx);
110*61c4878aSAndroid Build Coastguard Worker   }
111*61c4878aSAndroid Build Coastguard Worker 
112*61c4878aSAndroid Build Coastguard Worker   Status DoStageWrite(MultiBuf&& data) override;
113*61c4878aSAndroid Build Coastguard Worker 
DoPendWrite(Context &)114*61c4878aSAndroid Build Coastguard Worker   Poll<Status> DoPendWrite(Context&) override { return OkStatus(); }
115*61c4878aSAndroid Build Coastguard Worker 
DoPendClose(Context &)116*61c4878aSAndroid Build Coastguard Worker   Poll<Status> DoPendClose(Context&) override { return Ready(OkStatus()); }
117*61c4878aSAndroid Build Coastguard Worker 
118*61c4878aSAndroid Build Coastguard Worker   MultiBufAllocationFuture read_allocation_future_;
119*61c4878aSAndroid Build Coastguard Worker   MultiBufAllocationFuture write_allocation_future_;
120*61c4878aSAndroid Build Coastguard Worker   std::optional<MultiBuf> buffer_;
121*61c4878aSAndroid Build Coastguard Worker };
122*61c4878aSAndroid Build Coastguard Worker 
PendGetReadBuffer(Context & cx)123*61c4878aSAndroid Build Coastguard Worker Poll<Status> Rp2StdioChannel::PendGetReadBuffer(Context& cx) {
124*61c4878aSAndroid Build Coastguard Worker   if (buffer_.has_value()) {
125*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
126*61c4878aSAndroid Build Coastguard Worker   }
127*61c4878aSAndroid Build Coastguard Worker 
128*61c4878aSAndroid Build Coastguard Worker   read_allocation_future_.SetDesiredSizes(
129*61c4878aSAndroid Build Coastguard Worker       kMinimumReadSize, kDesiredReadSize, pw::multibuf::kNeedsContiguous);
130*61c4878aSAndroid Build Coastguard Worker   Poll<std::optional<MultiBuf>> maybe_multibuf =
131*61c4878aSAndroid Build Coastguard Worker       read_allocation_future_.Pend(cx);
132*61c4878aSAndroid Build Coastguard Worker   if (maybe_multibuf.IsPending()) {
133*61c4878aSAndroid Build Coastguard Worker     return Pending();
134*61c4878aSAndroid Build Coastguard Worker   }
135*61c4878aSAndroid Build Coastguard Worker   if (!maybe_multibuf->has_value()) {
136*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Failed to allocate multibuf for reading");
137*61c4878aSAndroid Build Coastguard Worker     return Status::ResourceExhausted();
138*61c4878aSAndroid Build Coastguard Worker   }
139*61c4878aSAndroid Build Coastguard Worker 
140*61c4878aSAndroid Build Coastguard Worker   buffer_ = std::move(**maybe_multibuf);
141*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
142*61c4878aSAndroid Build Coastguard Worker }
143*61c4878aSAndroid Build Coastguard Worker 
DoPendRead(Context & cx)144*61c4878aSAndroid Build Coastguard Worker Poll<Result<MultiBuf>> Rp2StdioChannel::DoPendRead(Context& cx) {
145*61c4878aSAndroid Build Coastguard Worker   Poll<Status> buffer_ready = PendGetReadBuffer(cx);
146*61c4878aSAndroid Build Coastguard Worker   if (buffer_ready.IsPending() || !buffer_ready->ok()) {
147*61c4878aSAndroid Build Coastguard Worker     return buffer_ready;
148*61c4878aSAndroid Build Coastguard Worker   }
149*61c4878aSAndroid Build Coastguard Worker 
150*61c4878aSAndroid Build Coastguard Worker   size_t len = 0;
151*61c4878aSAndroid Build Coastguard Worker   for (std::byte& b : *buffer_) {
152*61c4878aSAndroid Build Coastguard Worker     Poll<std::byte> next = PollReadByte(cx);
153*61c4878aSAndroid Build Coastguard Worker     if (next.IsPending()) {
154*61c4878aSAndroid Build Coastguard Worker       break;
155*61c4878aSAndroid Build Coastguard Worker     }
156*61c4878aSAndroid Build Coastguard Worker     b = *next;
157*61c4878aSAndroid Build Coastguard Worker     ++len;
158*61c4878aSAndroid Build Coastguard Worker   }
159*61c4878aSAndroid Build Coastguard Worker   if (len == 0) {
160*61c4878aSAndroid Build Coastguard Worker     return Pending();
161*61c4878aSAndroid Build Coastguard Worker   }
162*61c4878aSAndroid Build Coastguard Worker   buffer_->Truncate(len);
163*61c4878aSAndroid Build Coastguard Worker   MultiBuf buffer = std::move(*buffer_);
164*61c4878aSAndroid Build Coastguard Worker   buffer_ = std::nullopt;
165*61c4878aSAndroid Build Coastguard Worker   return buffer;
166*61c4878aSAndroid Build Coastguard Worker }
167*61c4878aSAndroid Build Coastguard Worker 
DoPendReadyToWrite(Context &)168*61c4878aSAndroid Build Coastguard Worker Poll<Status> Rp2StdioChannel::DoPendReadyToWrite(Context&) {
169*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
170*61c4878aSAndroid Build Coastguard Worker }
171*61c4878aSAndroid Build Coastguard Worker 
DoStageWrite(MultiBuf && data)172*61c4878aSAndroid Build Coastguard Worker Status Rp2StdioChannel::DoStageWrite(MultiBuf&& data) {
173*61c4878aSAndroid Build Coastguard Worker   WriteMultiBuf(data);
174*61c4878aSAndroid Build Coastguard Worker 
175*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
176*61c4878aSAndroid Build Coastguard Worker }
177*61c4878aSAndroid Build Coastguard Worker 
178*61c4878aSAndroid Build Coastguard Worker }  // namespace
179*61c4878aSAndroid Build Coastguard Worker 
Rp2StdioChannelInit(MultiBufAllocator & read_allocator,MultiBufAllocator & write_allocator)180*61c4878aSAndroid Build Coastguard Worker ByteReaderWriter& Rp2StdioChannelInit(MultiBufAllocator& read_allocator,
181*61c4878aSAndroid Build Coastguard Worker                                       MultiBufAllocator& write_allocator) {
182*61c4878aSAndroid Build Coastguard Worker   static Rp2StdioChannel channel = [&] {
183*61c4878aSAndroid Build Coastguard Worker     InitStdio();
184*61c4878aSAndroid Build Coastguard Worker     return Rp2StdioChannel(read_allocator, write_allocator);
185*61c4878aSAndroid Build Coastguard Worker   }();
186*61c4878aSAndroid Build Coastguard Worker   return channel.channel();
187*61c4878aSAndroid Build Coastguard Worker }
188*61c4878aSAndroid Build Coastguard Worker 
Rp2StdioChannelInit(MultiBufAllocator & allocator)189*61c4878aSAndroid Build Coastguard Worker ByteReaderWriter& Rp2StdioChannelInit(MultiBufAllocator& allocator) {
190*61c4878aSAndroid Build Coastguard Worker   return Rp2StdioChannelInit(allocator, allocator);
191*61c4878aSAndroid Build Coastguard Worker }
192*61c4878aSAndroid Build Coastguard Worker 
193*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::channel
194