xref: /aosp_15_r20/external/pigweed/pw_uart_mcuxpresso/dma_uart.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_mcuxpresso/dma_uart.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <optional>
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include "fsl_flexcomm.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_preprocessor/util.h"
22*61c4878aSAndroid Build Coastguard Worker 
23*61c4878aSAndroid Build Coastguard Worker namespace pw::uart {
24*61c4878aSAndroid Build Coastguard Worker 
25*61c4878aSAndroid Build Coastguard Worker // Deinitialize the DMA channels and USART.
Deinit()26*61c4878aSAndroid Build Coastguard Worker void DmaUartMcuxpresso::Deinit() {
27*61c4878aSAndroid Build Coastguard Worker   if (!initialized_) {
28*61c4878aSAndroid Build Coastguard Worker     return;
29*61c4878aSAndroid Build Coastguard Worker   }
30*61c4878aSAndroid Build Coastguard Worker 
31*61c4878aSAndroid Build Coastguard Worker   config_.tx_dma_ch.Disable();
32*61c4878aSAndroid Build Coastguard Worker   config_.rx_dma_ch.Disable();
33*61c4878aSAndroid Build Coastguard Worker 
34*61c4878aSAndroid Build Coastguard Worker   USART_Deinit(config_.usart_base);
35*61c4878aSAndroid Build Coastguard Worker   clock_tree_element_controller_.Release().IgnoreError();
36*61c4878aSAndroid Build Coastguard Worker   initialized_ = false;
37*61c4878aSAndroid Build Coastguard Worker }
38*61c4878aSAndroid Build Coastguard Worker 
~DmaUartMcuxpresso()39*61c4878aSAndroid Build Coastguard Worker DmaUartMcuxpresso::~DmaUartMcuxpresso() { Deinit(); }
40*61c4878aSAndroid Build Coastguard Worker 
41*61c4878aSAndroid Build Coastguard Worker // Initialize the USART and DMA channels based on the configuration
42*61c4878aSAndroid Build Coastguard Worker // specified during object creation.
Init()43*61c4878aSAndroid Build Coastguard Worker Status DmaUartMcuxpresso::Init() {
44*61c4878aSAndroid Build Coastguard Worker   if (config_.usart_base == nullptr) {
45*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
46*61c4878aSAndroid Build Coastguard Worker   }
47*61c4878aSAndroid Build Coastguard Worker   if (config_.baud_rate == 0) {
48*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
49*61c4878aSAndroid Build Coastguard Worker   }
50*61c4878aSAndroid Build Coastguard Worker 
51*61c4878aSAndroid Build Coastguard Worker   usart_config_t defconfig;
52*61c4878aSAndroid Build Coastguard Worker   USART_GetDefaultConfig(&defconfig);
53*61c4878aSAndroid Build Coastguard Worker 
54*61c4878aSAndroid Build Coastguard Worker   defconfig.baudRate_Bps = config_.baud_rate;
55*61c4878aSAndroid Build Coastguard Worker   defconfig.enableHardwareFlowControl = config_.flow_control;
56*61c4878aSAndroid Build Coastguard Worker   defconfig.parityMode = config_.parity;
57*61c4878aSAndroid Build Coastguard Worker   defconfig.enableTx = true;
58*61c4878aSAndroid Build Coastguard Worker   defconfig.enableRx = true;
59*61c4878aSAndroid Build Coastguard Worker 
60*61c4878aSAndroid Build Coastguard Worker   PW_TRY(clock_tree_element_controller_.Acquire());
61*61c4878aSAndroid Build Coastguard Worker   flexcomm_clock_freq_ =
62*61c4878aSAndroid Build Coastguard Worker       CLOCK_GetFlexcommClkFreq(FLEXCOMM_GetInstance(config_.usart_base));
63*61c4878aSAndroid Build Coastguard Worker   status_t status =
64*61c4878aSAndroid Build Coastguard Worker       USART_Init(config_.usart_base, &defconfig, flexcomm_clock_freq_);
65*61c4878aSAndroid Build Coastguard Worker   if (status != kStatus_Success) {
66*61c4878aSAndroid Build Coastguard Worker     clock_tree_element_controller_.Release().IgnoreError();
67*61c4878aSAndroid Build Coastguard Worker     return Status::Internal();
68*61c4878aSAndroid Build Coastguard Worker   }
69*61c4878aSAndroid Build Coastguard Worker 
70*61c4878aSAndroid Build Coastguard Worker   // We need to touch register space that can be shared
71*61c4878aSAndroid Build Coastguard Worker   // among several DMA peripherals, hence we need to access
72*61c4878aSAndroid Build Coastguard Worker   // it exclusively. We achieve exclusive access on non-SMP systems as
73*61c4878aSAndroid Build Coastguard Worker   // a side effect of acquiring the interrupt_lock_, since acquiring the
74*61c4878aSAndroid Build Coastguard Worker   // interrupt_lock_ disables interrupts on the current CPU, which means
75*61c4878aSAndroid Build Coastguard Worker   // we cannot get descheduled until we release the interrupt_lock_.
76*61c4878aSAndroid Build Coastguard Worker   interrupt_lock_.lock();
77*61c4878aSAndroid Build Coastguard Worker 
78*61c4878aSAndroid Build Coastguard Worker   // Temporarily enable clock to inputmux, so that RX and TX DMA requests can
79*61c4878aSAndroid Build Coastguard Worker   // get enabled.
80*61c4878aSAndroid Build Coastguard Worker   INPUTMUX_Init(INPUTMUX);
81*61c4878aSAndroid Build Coastguard Worker   INPUTMUX_EnableSignal(
82*61c4878aSAndroid Build Coastguard Worker       INPUTMUX, config_.rx_input_mux_dmac_ch_request_en, true);
83*61c4878aSAndroid Build Coastguard Worker   INPUTMUX_EnableSignal(
84*61c4878aSAndroid Build Coastguard Worker       INPUTMUX, config_.tx_input_mux_dmac_ch_request_en, true);
85*61c4878aSAndroid Build Coastguard Worker   INPUTMUX_Deinit(INPUTMUX);
86*61c4878aSAndroid Build Coastguard Worker 
87*61c4878aSAndroid Build Coastguard Worker   interrupt_lock_.unlock();
88*61c4878aSAndroid Build Coastguard Worker 
89*61c4878aSAndroid Build Coastguard Worker   config_.tx_dma_ch.Enable();
90*61c4878aSAndroid Build Coastguard Worker   config_.rx_dma_ch.Enable();
91*61c4878aSAndroid Build Coastguard Worker 
92*61c4878aSAndroid Build Coastguard Worker   // Initialized enough for Deinit code to handle any errors from here.
93*61c4878aSAndroid Build Coastguard Worker   initialized_ = true;
94*61c4878aSAndroid Build Coastguard Worker 
95*61c4878aSAndroid Build Coastguard Worker   status = USART_TransferCreateHandleDMA(config_.usart_base,
96*61c4878aSAndroid Build Coastguard Worker                                          &uart_dma_handle_,
97*61c4878aSAndroid Build Coastguard Worker                                          TxRxCompletionCallback,
98*61c4878aSAndroid Build Coastguard Worker                                          this,
99*61c4878aSAndroid Build Coastguard Worker                                          config_.tx_dma_ch.handle(),
100*61c4878aSAndroid Build Coastguard Worker                                          config_.rx_dma_ch.handle());
101*61c4878aSAndroid Build Coastguard Worker 
102*61c4878aSAndroid Build Coastguard Worker   if (status != kStatus_Success) {
103*61c4878aSAndroid Build Coastguard Worker     Deinit();
104*61c4878aSAndroid Build Coastguard Worker     return Status::Internal();
105*61c4878aSAndroid Build Coastguard Worker   }
106*61c4878aSAndroid Build Coastguard Worker 
107*61c4878aSAndroid Build Coastguard Worker   // Read into the rx ring buffer.
108*61c4878aSAndroid Build Coastguard Worker   interrupt_lock_.lock();
109*61c4878aSAndroid Build Coastguard Worker   TriggerReadDma();
110*61c4878aSAndroid Build Coastguard Worker   interrupt_lock_.unlock();
111*61c4878aSAndroid Build Coastguard Worker 
112*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
113*61c4878aSAndroid Build Coastguard Worker }
114*61c4878aSAndroid Build Coastguard Worker 
115*61c4878aSAndroid Build Coastguard Worker // DMA usart data into ring buffer
116*61c4878aSAndroid Build Coastguard Worker //
117*61c4878aSAndroid Build Coastguard Worker // At most kUsartDmaMaxTransferCount bytes can be copied per DMA transfer.
118*61c4878aSAndroid Build Coastguard Worker // If completion_size is specified and dataSize is larger than completion_size,
119*61c4878aSAndroid Build Coastguard Worker // the dataSize will be limited to completion_size so that the completion
120*61c4878aSAndroid Build Coastguard Worker // callback will be called once completion_size bytes have been received.
TriggerReadDma()121*61c4878aSAndroid Build Coastguard Worker void DmaUartMcuxpresso::TriggerReadDma() {
122*61c4878aSAndroid Build Coastguard Worker   uint8_t* ring_buffer =
123*61c4878aSAndroid Build Coastguard Worker       reinterpret_cast<uint8_t*>(rx_data_.ring_buffer.data());
124*61c4878aSAndroid Build Coastguard Worker   rx_data_.transfer.data = &ring_buffer[rx_data_.ring_buffer_write_idx];
125*61c4878aSAndroid Build Coastguard Worker 
126*61c4878aSAndroid Build Coastguard Worker   if (rx_data_.ring_buffer_write_idx + kUsartDmaMaxTransferCount >
127*61c4878aSAndroid Build Coastguard Worker       rx_data_.ring_buffer.size_bytes()) {
128*61c4878aSAndroid Build Coastguard Worker     rx_data_.transfer.dataSize =
129*61c4878aSAndroid Build Coastguard Worker         rx_data_.ring_buffer.size_bytes() - rx_data_.ring_buffer_write_idx;
130*61c4878aSAndroid Build Coastguard Worker   } else {
131*61c4878aSAndroid Build Coastguard Worker     rx_data_.transfer.dataSize = kUsartDmaMaxTransferCount;
132*61c4878aSAndroid Build Coastguard Worker   }
133*61c4878aSAndroid Build Coastguard Worker 
134*61c4878aSAndroid Build Coastguard Worker   if (rx_data_.completion_size > 0 &&
135*61c4878aSAndroid Build Coastguard Worker       rx_data_.transfer.dataSize > rx_data_.completion_size) {
136*61c4878aSAndroid Build Coastguard Worker     // Completion callback will be called once this transfer completes.
137*61c4878aSAndroid Build Coastguard Worker     rx_data_.transfer.dataSize = rx_data_.completion_size;
138*61c4878aSAndroid Build Coastguard Worker   }
139*61c4878aSAndroid Build Coastguard Worker 
140*61c4878aSAndroid Build Coastguard Worker   USART_TransferReceiveDMA(
141*61c4878aSAndroid Build Coastguard Worker       config_.usart_base, &uart_dma_handle_, &rx_data_.transfer);
142*61c4878aSAndroid Build Coastguard Worker }
143*61c4878aSAndroid Build Coastguard Worker 
144*61c4878aSAndroid Build Coastguard Worker // DMA send buffer data
TriggerWriteDma()145*61c4878aSAndroid Build Coastguard Worker void DmaUartMcuxpresso::TriggerWriteDma() {
146*61c4878aSAndroid Build Coastguard Worker   const uint8_t* tx_buffer =
147*61c4878aSAndroid Build Coastguard Worker       reinterpret_cast<const uint8_t*>(tx_data_.buffer.data());
148*61c4878aSAndroid Build Coastguard Worker   tx_data_.transfer.txData = &tx_buffer[tx_data_.tx_idx];
149*61c4878aSAndroid Build Coastguard Worker   if (tx_data_.tx_idx + kUsartDmaMaxTransferCount >
150*61c4878aSAndroid Build Coastguard Worker       tx_data_.buffer.size_bytes()) {
151*61c4878aSAndroid Build Coastguard Worker     // Completion callback will be called once this transfer completes.
152*61c4878aSAndroid Build Coastguard Worker     tx_data_.transfer.dataSize = tx_data_.buffer.size_bytes() - tx_data_.tx_idx;
153*61c4878aSAndroid Build Coastguard Worker   } else {
154*61c4878aSAndroid Build Coastguard Worker     tx_data_.transfer.dataSize = kUsartDmaMaxTransferCount;
155*61c4878aSAndroid Build Coastguard Worker   }
156*61c4878aSAndroid Build Coastguard Worker 
157*61c4878aSAndroid Build Coastguard Worker   USART_TransferSendDMA(
158*61c4878aSAndroid Build Coastguard Worker       config_.usart_base, &uart_dma_handle_, &tx_data_.transfer);
159*61c4878aSAndroid Build Coastguard Worker }
160*61c4878aSAndroid Build Coastguard Worker 
161*61c4878aSAndroid Build Coastguard Worker // Completion callback for TX and RX transactions
TxRxCompletionCallback(USART_Type *,usart_dma_handle_t *,status_t status,void * param)162*61c4878aSAndroid Build Coastguard Worker void DmaUartMcuxpresso::TxRxCompletionCallback(USART_Type* /* base */,
163*61c4878aSAndroid Build Coastguard Worker                                                usart_dma_handle_t* /* state */,
164*61c4878aSAndroid Build Coastguard Worker                                                status_t status,
165*61c4878aSAndroid Build Coastguard Worker                                                void* param) {
166*61c4878aSAndroid Build Coastguard Worker   DmaUartMcuxpresso* stream = reinterpret_cast<DmaUartMcuxpresso*>(param);
167*61c4878aSAndroid Build Coastguard Worker 
168*61c4878aSAndroid Build Coastguard Worker   if (status == kStatus_USART_RxIdle) {
169*61c4878aSAndroid Build Coastguard Worker     // RX transfer
170*61c4878aSAndroid Build Coastguard Worker 
171*61c4878aSAndroid Build Coastguard Worker     // Acquire the interrupt_lock_ to ensure that on SMP systems
172*61c4878aSAndroid Build Coastguard Worker     // access to the rx_data is synchronized.
173*61c4878aSAndroid Build Coastguard Worker     stream->interrupt_lock_.lock();
174*61c4878aSAndroid Build Coastguard Worker 
175*61c4878aSAndroid Build Coastguard Worker     struct UsartDmaRxData* rx_data = &stream->rx_data_;
176*61c4878aSAndroid Build Coastguard Worker     rx_data->ring_buffer_write_idx += rx_data->transfer.dataSize;
177*61c4878aSAndroid Build Coastguard Worker     rx_data->data_received += rx_data->transfer.dataSize;
178*61c4878aSAndroid Build Coastguard Worker 
179*61c4878aSAndroid Build Coastguard Worker     PW_DCHECK_INT_LE(rx_data->ring_buffer_write_idx,
180*61c4878aSAndroid Build Coastguard Worker                      rx_data->ring_buffer.size_bytes());
181*61c4878aSAndroid Build Coastguard Worker     if (rx_data->ring_buffer_write_idx == rx_data->ring_buffer.size_bytes()) {
182*61c4878aSAndroid Build Coastguard Worker       rx_data->ring_buffer_write_idx = 0;
183*61c4878aSAndroid Build Coastguard Worker     }
184*61c4878aSAndroid Build Coastguard Worker 
185*61c4878aSAndroid Build Coastguard Worker     bool notify_rx_completion = false;
186*61c4878aSAndroid Build Coastguard Worker     if (rx_data->completion_size > 0) {
187*61c4878aSAndroid Build Coastguard Worker       PW_DCHECK_INT_GE(rx_data->completion_size, rx_data->transfer.dataSize);
188*61c4878aSAndroid Build Coastguard Worker       rx_data->completion_size -= rx_data->transfer.dataSize;
189*61c4878aSAndroid Build Coastguard Worker       if (rx_data->completion_size == 0) {
190*61c4878aSAndroid Build Coastguard Worker         // We have satisfied the receive request, we must wake up the receiver.
191*61c4878aSAndroid Build Coastguard Worker         // Before we can issue the wake up, we must trigger the next DMA read
192*61c4878aSAndroid Build Coastguard Worker         // operation, since the notification might yield the CPU.
193*61c4878aSAndroid Build Coastguard Worker         notify_rx_completion = true;
194*61c4878aSAndroid Build Coastguard Worker       }
195*61c4878aSAndroid Build Coastguard Worker     }
196*61c4878aSAndroid Build Coastguard Worker     stream->TriggerReadDma();
197*61c4878aSAndroid Build Coastguard Worker 
198*61c4878aSAndroid Build Coastguard Worker     stream->interrupt_lock_.unlock();
199*61c4878aSAndroid Build Coastguard Worker 
200*61c4878aSAndroid Build Coastguard Worker     if (notify_rx_completion) {
201*61c4878aSAndroid Build Coastguard Worker       rx_data->notification.release();
202*61c4878aSAndroid Build Coastguard Worker     }
203*61c4878aSAndroid Build Coastguard Worker   } else if (status == kStatus_USART_TxIdle) {
204*61c4878aSAndroid Build Coastguard Worker     // Tx transfer
205*61c4878aSAndroid Build Coastguard Worker     UsartDmaTxData* tx_data = &stream->tx_data_;
206*61c4878aSAndroid Build Coastguard Worker     tx_data->tx_idx += tx_data->transfer.dataSize;
207*61c4878aSAndroid Build Coastguard Worker     if (tx_data->tx_idx == tx_data->buffer.size_bytes()) {
208*61c4878aSAndroid Build Coastguard Worker       // We have completed the send request, we must wake up the sender.
209*61c4878aSAndroid Build Coastguard Worker       tx_data->notification.release();
210*61c4878aSAndroid Build Coastguard Worker     } else {
211*61c4878aSAndroid Build Coastguard Worker       PW_CHECK_INT_LT(tx_data->tx_idx, tx_data->buffer.size_bytes());
212*61c4878aSAndroid Build Coastguard Worker       stream->TriggerWriteDma();
213*61c4878aSAndroid Build Coastguard Worker     }
214*61c4878aSAndroid Build Coastguard Worker   }
215*61c4878aSAndroid Build Coastguard Worker }
216*61c4878aSAndroid Build Coastguard Worker 
217*61c4878aSAndroid Build Coastguard Worker // Get the amount of bytes that have been received, but haven't been copied yet
218*61c4878aSAndroid Build Coastguard Worker //
219*61c4878aSAndroid Build Coastguard Worker // Note: The caller must ensure that the interrupt handler cannot execute.
TransferGetReceiveDMACountLockHeld()220*61c4878aSAndroid Build Coastguard Worker StatusWithSize DmaUartMcuxpresso::TransferGetReceiveDMACountLockHeld() {
221*61c4878aSAndroid Build Coastguard Worker   uint32_t count = 0;
222*61c4878aSAndroid Build Coastguard Worker 
223*61c4878aSAndroid Build Coastguard Worker   // If no in-flight transfer is in progress, there is no pending data
224*61c4878aSAndroid Build Coastguard Worker   // available. We have initialized count to 0 to account for that.
225*61c4878aSAndroid Build Coastguard Worker   (void)USART_TransferGetReceiveCountDMA(
226*61c4878aSAndroid Build Coastguard Worker       config_.usart_base, &uart_dma_handle_, &count);
227*61c4878aSAndroid Build Coastguard Worker 
228*61c4878aSAndroid Build Coastguard Worker   // We must be executing with the interrupt_lock_ held, so that the interrupt
229*61c4878aSAndroid Build Coastguard Worker   // handler cannot change data_received.
230*61c4878aSAndroid Build Coastguard Worker   count += rx_data_.data_received - rx_data_.data_copied;
231*61c4878aSAndroid Build Coastguard Worker   // Check whether we hit an overflow condition
232*61c4878aSAndroid Build Coastguard Worker   if (count > rx_data_.ring_buffer.size_bytes()) {
233*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize(Status::DataLoss(), 0);
234*61c4878aSAndroid Build Coastguard Worker   }
235*61c4878aSAndroid Build Coastguard Worker   return StatusWithSize(count);
236*61c4878aSAndroid Build Coastguard Worker }
237*61c4878aSAndroid Build Coastguard Worker 
238*61c4878aSAndroid Build Coastguard Worker // Get the amount of bytes that have been received, but haven't been copied yet
TransferGetReceiveDMACount()239*61c4878aSAndroid Build Coastguard Worker StatusWithSize DmaUartMcuxpresso::TransferGetReceiveDMACount() {
240*61c4878aSAndroid Build Coastguard Worker   // We need to acquire the interrupt_lock_ , so that the interrupt handler
241*61c4878aSAndroid Build Coastguard Worker   // cannot run to change rxRingBufferWriteIdx.
242*61c4878aSAndroid Build Coastguard Worker   interrupt_lock_.lock();
243*61c4878aSAndroid Build Coastguard Worker   StatusWithSize status = TransferGetReceiveDMACountLockHeld();
244*61c4878aSAndroid Build Coastguard Worker   interrupt_lock_.unlock();
245*61c4878aSAndroid Build Coastguard Worker   return status;
246*61c4878aSAndroid Build Coastguard Worker }
247*61c4878aSAndroid Build Coastguard Worker 
248*61c4878aSAndroid Build Coastguard Worker // Get the amount of bytes that have not been yet received for the current
249*61c4878aSAndroid Build Coastguard Worker // transfer
250*61c4878aSAndroid Build Coastguard Worker //
251*61c4878aSAndroid Build Coastguard Worker // Note: This function may only be called once the RX transaction has been
252*61c4878aSAndroid Build Coastguard Worker // aborted.
GetReceiveTransferRemainingBytes()253*61c4878aSAndroid Build Coastguard Worker size_t DmaUartMcuxpresso::GetReceiveTransferRemainingBytes() {
254*61c4878aSAndroid Build Coastguard Worker   return DMA_GetRemainingBytes(uart_dma_handle_.rxDmaHandle->base,
255*61c4878aSAndroid Build Coastguard Worker                                uart_dma_handle_.rxDmaHandle->channel);
256*61c4878aSAndroid Build Coastguard Worker }
257*61c4878aSAndroid Build Coastguard Worker 
258*61c4878aSAndroid Build Coastguard Worker // Wait for more receive bytes to arrive to satisfy request
259*61c4878aSAndroid Build Coastguard Worker //
260*61c4878aSAndroid Build Coastguard Worker // Once we have acquired the interrupt_lock_, we check whether we can
261*61c4878aSAndroid Build Coastguard Worker // satisfy the request, and if not, we will abort the current
262*61c4878aSAndroid Build Coastguard Worker // transaction if the current transaction will be able to satisfy
263*61c4878aSAndroid Build Coastguard Worker // the outstanding request. Once the transaction has been aborted
264*61c4878aSAndroid Build Coastguard Worker // we can specify the completion_size, so that the completion callback
265*61c4878aSAndroid Build Coastguard Worker // can wake us up when the bytes_needed bytes have been received.
266*61c4878aSAndroid Build Coastguard Worker //
267*61c4878aSAndroid Build Coastguard Worker // If more than one transaction is required to satisfy the request,
268*61c4878aSAndroid Build Coastguard Worker // we don't need to abort the transaction and instead can leverage
269*61c4878aSAndroid Build Coastguard Worker // the fact that the completion callback won't be triggered since we
270*61c4878aSAndroid Build Coastguard Worker // have acquired the interrupt_lock_ . This allows us to specify
271*61c4878aSAndroid Build Coastguard Worker // the completion_size that will be seen by the completion callback
272*61c4878aSAndroid Build Coastguard Worker // when it executes. A subsequent completion callback will wake us up
273*61c4878aSAndroid Build Coastguard Worker // when the bytes_needed have been received.
WaitForReceiveBytes(size_t bytes_needed,std::optional<chrono::SystemClock::time_point> deadline)274*61c4878aSAndroid Build Coastguard Worker Status DmaUartMcuxpresso::WaitForReceiveBytes(
275*61c4878aSAndroid Build Coastguard Worker     size_t bytes_needed,
276*61c4878aSAndroid Build Coastguard Worker     std::optional<chrono::SystemClock::time_point> deadline) {
277*61c4878aSAndroid Build Coastguard Worker   // Acquire the interrupt_lock_, so that the interrupt handler cannot
278*61c4878aSAndroid Build Coastguard Worker   // execute and modify the shared state.
279*61c4878aSAndroid Build Coastguard Worker   interrupt_lock_.lock();
280*61c4878aSAndroid Build Coastguard Worker 
281*61c4878aSAndroid Build Coastguard Worker   // Recheck what the current amount of available bytes is.
282*61c4878aSAndroid Build Coastguard Worker   StatusWithSize rx_count_status = TransferGetReceiveDMACountLockHeld();
283*61c4878aSAndroid Build Coastguard Worker   if (!rx_count_status.ok()) {
284*61c4878aSAndroid Build Coastguard Worker     interrupt_lock_.unlock();
285*61c4878aSAndroid Build Coastguard Worker     return rx_count_status.status();
286*61c4878aSAndroid Build Coastguard Worker   }
287*61c4878aSAndroid Build Coastguard Worker 
288*61c4878aSAndroid Build Coastguard Worker   size_t rx_count = rx_count_status.size();
289*61c4878aSAndroid Build Coastguard Worker   if (rx_count >= bytes_needed) {
290*61c4878aSAndroid Build Coastguard Worker     interrupt_lock_.unlock();
291*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
292*61c4878aSAndroid Build Coastguard Worker   }
293*61c4878aSAndroid Build Coastguard Worker 
294*61c4878aSAndroid Build Coastguard Worker   // Not enough bytes available yet.
295*61c4878aSAndroid Build Coastguard Worker   // We check whether more bytes are needed than the transfer's
296*61c4878aSAndroid Build Coastguard Worker   // dataSize, which means that at least one more transfer must
297*61c4878aSAndroid Build Coastguard Worker   // complete to satisfy this receive request.
298*61c4878aSAndroid Build Coastguard Worker   size_t pos_in_transfer =
299*61c4878aSAndroid Build Coastguard Worker       rx_data_.data_copied + rx_count - rx_data_.data_received;
300*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK_INT_LE(pos_in_transfer, rx_data_.transfer.dataSize);
301*61c4878aSAndroid Build Coastguard Worker 
302*61c4878aSAndroid Build Coastguard Worker   size_t transfer_bytes_needed =
303*61c4878aSAndroid Build Coastguard Worker       bytes_needed + rx_data_.data_copied - rx_data_.data_received;
304*61c4878aSAndroid Build Coastguard Worker   bool aborted = false;
305*61c4878aSAndroid Build Coastguard Worker 
306*61c4878aSAndroid Build Coastguard Worker   if (transfer_bytes_needed < rx_data_.transfer.dataSize) {
307*61c4878aSAndroid Build Coastguard Worker     // Abort the current transfer, so that we can schedule a receive
308*61c4878aSAndroid Build Coastguard Worker     // transfer to satisfy this request.
309*61c4878aSAndroid Build Coastguard Worker     USART_TransferAbortReceiveDMA(config_.usart_base, &uart_dma_handle_);
310*61c4878aSAndroid Build Coastguard Worker     size_t remaining_transfer_bytes = GetReceiveTransferRemainingBytes();
311*61c4878aSAndroid Build Coastguard Worker     if (remaining_transfer_bytes == 0) {
312*61c4878aSAndroid Build Coastguard Worker       // We have received all bytes for the current transfer, we will
313*61c4878aSAndroid Build Coastguard Worker       // restart the loop in the caller's context.
314*61c4878aSAndroid Build Coastguard Worker       // The interrupt handler will execute and call TriggerReadDma
315*61c4878aSAndroid Build Coastguard Worker       // to schedule the next receive DMA transfer.
316*61c4878aSAndroid Build Coastguard Worker       interrupt_lock_.unlock();
317*61c4878aSAndroid Build Coastguard Worker       return OkStatus();
318*61c4878aSAndroid Build Coastguard Worker     }
319*61c4878aSAndroid Build Coastguard Worker     // We have successfully aborted an in-flight transfer. No interrupt
320*61c4878aSAndroid Build Coastguard Worker     // callback will be called for it.
321*61c4878aSAndroid Build Coastguard Worker     aborted = true;
322*61c4878aSAndroid Build Coastguard Worker     // We need to fix up the transfer size for the aborted transfer.
323*61c4878aSAndroid Build Coastguard Worker     rx_data_.transfer.dataSize -= remaining_transfer_bytes;
324*61c4878aSAndroid Build Coastguard Worker   } else {
325*61c4878aSAndroid Build Coastguard Worker     // We require at least as much data as provided by the current
326*61c4878aSAndroid Build Coastguard Worker     // transfer. We know that this code cannot execute while the
327*61c4878aSAndroid Build Coastguard Worker     // receive transaction isn't active, so we know that the
328*61c4878aSAndroid Build Coastguard Worker     // completion callback will still execute.
329*61c4878aSAndroid Build Coastguard Worker   }
330*61c4878aSAndroid Build Coastguard Worker 
331*61c4878aSAndroid Build Coastguard Worker   // Tell the transfer callback when to deliver the completion
332*61c4878aSAndroid Build Coastguard Worker   // notification.
333*61c4878aSAndroid Build Coastguard Worker   rx_data_.completion_size = transfer_bytes_needed;
334*61c4878aSAndroid Build Coastguard Worker 
335*61c4878aSAndroid Build Coastguard Worker   // Since a caller could request a receive amount that exceeds the ring
336*61c4878aSAndroid Build Coastguard Worker   // buffer size, we must cap the rxCompletionSize. In addition, we
337*61c4878aSAndroid Build Coastguard Worker   // don't want that the rxRingBuffer overflows, so we cap the
338*61c4878aSAndroid Build Coastguard Worker   // rxCompletionSize to 25% of the ringBufferSize to ensure that the
339*61c4878aSAndroid Build Coastguard Worker   // ring buffer gets drained frequently enough.
340*61c4878aSAndroid Build Coastguard Worker   if (rx_data_.completion_size >
341*61c4878aSAndroid Build Coastguard Worker       rx_data_.ring_buffer.size_bytes() / kUsartRxRingBufferSplitCount) {
342*61c4878aSAndroid Build Coastguard Worker     rx_data_.completion_size =
343*61c4878aSAndroid Build Coastguard Worker         rx_data_.ring_buffer.size_bytes() / kUsartRxRingBufferSplitCount;
344*61c4878aSAndroid Build Coastguard Worker   }
345*61c4878aSAndroid Build Coastguard Worker 
346*61c4878aSAndroid Build Coastguard Worker   interrupt_lock_.unlock();
347*61c4878aSAndroid Build Coastguard Worker 
348*61c4878aSAndroid Build Coastguard Worker   if (aborted) {
349*61c4878aSAndroid Build Coastguard Worker     // We have received data, but we haven't accounted for it, since the
350*61c4878aSAndroid Build Coastguard Worker     // callback won't execute due to the abort. Execute the callback
351*61c4878aSAndroid Build Coastguard Worker     // from here instead. Since the DMA transfer has been aborted, and
352*61c4878aSAndroid Build Coastguard Worker     // the available data isn't sufficient to satisfy this request, the
353*61c4878aSAndroid Build Coastguard Worker     // next receive DMA transfer will unblock this thread.
354*61c4878aSAndroid Build Coastguard Worker     TxRxCompletionCallback(
355*61c4878aSAndroid Build Coastguard Worker         config_.usart_base, &uart_dma_handle_, kStatus_USART_RxIdle, this);
356*61c4878aSAndroid Build Coastguard Worker   }
357*61c4878aSAndroid Build Coastguard Worker 
358*61c4878aSAndroid Build Coastguard Worker   // Wait for the interrupt handler to deliver the completion
359*61c4878aSAndroid Build Coastguard Worker   // notification.
360*61c4878aSAndroid Build Coastguard Worker   Status status = OkStatus();
361*61c4878aSAndroid Build Coastguard Worker   if (deadline.has_value()) {
362*61c4878aSAndroid Build Coastguard Worker     if (!rx_data_.notification.try_acquire_until(*deadline)) {
363*61c4878aSAndroid Build Coastguard Worker       // Timeout expired. No need to cancel the DMA; subsequent bytes will
364*61c4878aSAndroid Build Coastguard Worker       // just go into the ring buffer.
365*61c4878aSAndroid Build Coastguard Worker       status.Update(Status::DeadlineExceeded());
366*61c4878aSAndroid Build Coastguard Worker     }
367*61c4878aSAndroid Build Coastguard Worker   } else {
368*61c4878aSAndroid Build Coastguard Worker     rx_data_.notification.acquire();
369*61c4878aSAndroid Build Coastguard Worker   }
370*61c4878aSAndroid Build Coastguard Worker   // We have received bytes that can be copied out, we will restart
371*61c4878aSAndroid Build Coastguard Worker   // the loop in the caller's context.
372*61c4878aSAndroid Build Coastguard Worker   return status;
373*61c4878aSAndroid Build Coastguard Worker }
374*61c4878aSAndroid Build Coastguard Worker 
375*61c4878aSAndroid Build Coastguard Worker // Copy the data from the receive ring buffer into the destination data buffer
CopyReceiveData(ByteBuilder & bb,size_t copy_size)376*61c4878aSAndroid Build Coastguard Worker void DmaUartMcuxpresso::CopyReceiveData(ByteBuilder& bb, size_t copy_size) {
377*61c4878aSAndroid Build Coastguard Worker   ByteSpan ring_buffer = rx_data_.ring_buffer;
378*61c4878aSAndroid Build Coastguard Worker   // Check whether we need to perform a wrap around copy operation or end
379*61c4878aSAndroid Build Coastguard Worker   // right at the end of the buffer.
380*61c4878aSAndroid Build Coastguard Worker   if (rx_data_.ring_buffer_read_idx + copy_size >=
381*61c4878aSAndroid Build Coastguard Worker       rx_data_.ring_buffer.size_bytes()) {
382*61c4878aSAndroid Build Coastguard Worker     size_t first_copy_size =
383*61c4878aSAndroid Build Coastguard Worker         rx_data_.ring_buffer.size_bytes() - rx_data_.ring_buffer_read_idx;
384*61c4878aSAndroid Build Coastguard Worker     bb.append(
385*61c4878aSAndroid Build Coastguard Worker         ring_buffer.subspan(rx_data_.ring_buffer_read_idx, first_copy_size));
386*61c4878aSAndroid Build Coastguard Worker     size_t second_copy_size = copy_size - first_copy_size;
387*61c4878aSAndroid Build Coastguard Worker     // Source buffer is at offset 0.
388*61c4878aSAndroid Build Coastguard Worker     bb.append(ring_buffer.subspan(0, second_copy_size));
389*61c4878aSAndroid Build Coastguard Worker     rx_data_.ring_buffer_read_idx = second_copy_size;
390*61c4878aSAndroid Build Coastguard Worker   } else {
391*61c4878aSAndroid Build Coastguard Worker     // Normal copy operation
392*61c4878aSAndroid Build Coastguard Worker     PW_DCHECK_INT_LT(rx_data_.ring_buffer_read_idx + copy_size,
393*61c4878aSAndroid Build Coastguard Worker                      rx_data_.ring_buffer.size_bytes());
394*61c4878aSAndroid Build Coastguard Worker     bb.append(ring_buffer.subspan(rx_data_.ring_buffer_read_idx, copy_size));
395*61c4878aSAndroid Build Coastguard Worker     rx_data_.ring_buffer_read_idx += copy_size;
396*61c4878aSAndroid Build Coastguard Worker   }
397*61c4878aSAndroid Build Coastguard Worker   rx_data_.data_copied += copy_size;
398*61c4878aSAndroid Build Coastguard Worker }
399*61c4878aSAndroid Build Coastguard Worker 
DoEnable(bool enable)400*61c4878aSAndroid Build Coastguard Worker Status DmaUartMcuxpresso::DoEnable(bool enable) {
401*61c4878aSAndroid Build Coastguard Worker   if (enable == initialized_) {
402*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
403*61c4878aSAndroid Build Coastguard Worker   }
404*61c4878aSAndroid Build Coastguard Worker 
405*61c4878aSAndroid Build Coastguard Worker   if (enable) {
406*61c4878aSAndroid Build Coastguard Worker     return Init();
407*61c4878aSAndroid Build Coastguard Worker   } else {
408*61c4878aSAndroid Build Coastguard Worker     Deinit();
409*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
410*61c4878aSAndroid Build Coastguard Worker   }
411*61c4878aSAndroid Build Coastguard Worker }
412*61c4878aSAndroid Build Coastguard Worker 
DoSetBaudRate(uint32_t baud_rate)413*61c4878aSAndroid Build Coastguard Worker Status DmaUartMcuxpresso::DoSetBaudRate(uint32_t baud_rate) {
414*61c4878aSAndroid Build Coastguard Worker   if (baud_rate == 0) {
415*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
416*61c4878aSAndroid Build Coastguard Worker   }
417*61c4878aSAndroid Build Coastguard Worker 
418*61c4878aSAndroid Build Coastguard Worker   config_.baud_rate = baud_rate;
419*61c4878aSAndroid Build Coastguard Worker 
420*61c4878aSAndroid Build Coastguard Worker   if (!initialized_) {
421*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
422*61c4878aSAndroid Build Coastguard Worker   }
423*61c4878aSAndroid Build Coastguard Worker 
424*61c4878aSAndroid Build Coastguard Worker   status_t status = USART_SetBaudRate(
425*61c4878aSAndroid Build Coastguard Worker       config_.usart_base, config_.baud_rate, flexcomm_clock_freq_);
426*61c4878aSAndroid Build Coastguard Worker   switch (status) {
427*61c4878aSAndroid Build Coastguard Worker     default:
428*61c4878aSAndroid Build Coastguard Worker       return Status::Unknown();
429*61c4878aSAndroid Build Coastguard Worker     case kStatus_USART_BaudrateNotSupport:
430*61c4878aSAndroid Build Coastguard Worker     case kStatus_InvalidArgument:
431*61c4878aSAndroid Build Coastguard Worker       return Status::InvalidArgument();
432*61c4878aSAndroid Build Coastguard Worker     case kStatus_Success:
433*61c4878aSAndroid Build Coastguard Worker       return OkStatus();
434*61c4878aSAndroid Build Coastguard Worker   }
435*61c4878aSAndroid Build Coastguard Worker }
436*61c4878aSAndroid Build Coastguard Worker 
DoSetFlowControl(bool enable)437*61c4878aSAndroid Build Coastguard Worker Status DmaUartMcuxpresso::DoSetFlowControl(bool enable) {
438*61c4878aSAndroid Build Coastguard Worker   config_.flow_control = enable;
439*61c4878aSAndroid Build Coastguard Worker 
440*61c4878aSAndroid Build Coastguard Worker   if (!initialized_) {
441*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
442*61c4878aSAndroid Build Coastguard Worker   }
443*61c4878aSAndroid Build Coastguard Worker 
444*61c4878aSAndroid Build Coastguard Worker   USART_EnableCTS(config_.usart_base, enable);
445*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
446*61c4878aSAndroid Build Coastguard Worker }
447*61c4878aSAndroid Build Coastguard Worker 
448*61c4878aSAndroid Build Coastguard Worker // Copy data from the RX ring buffer into the caller provided buffer
449*61c4878aSAndroid Build Coastguard Worker //
450*61c4878aSAndroid Build Coastguard Worker // If the ring buffer can already satisfy the read request, the
451*61c4878aSAndroid Build Coastguard Worker // data will be copied from the ring buffer into the provided buffer.
452*61c4878aSAndroid Build Coastguard Worker // If no data, or not sufficient data is available to satisfy the
453*61c4878aSAndroid Build Coastguard Worker // read request, the caller will wait for the completion callback to
454*61c4878aSAndroid Build Coastguard Worker // signal that data is available and can be copied from the ring buffer
455*61c4878aSAndroid Build Coastguard Worker // to the provided buffer.
456*61c4878aSAndroid Build Coastguard Worker //
457*61c4878aSAndroid Build Coastguard Worker // Note: A reader may request to read more data than can be stored
458*61c4878aSAndroid Build Coastguard Worker // inside the RX ring buffer.
459*61c4878aSAndroid Build Coastguard Worker //
460*61c4878aSAndroid Build Coastguard Worker // Note: Only one thread should be calling this function,
461*61c4878aSAndroid Build Coastguard Worker // otherwise DoRead calls might fail due to contention for
462*61c4878aSAndroid Build Coastguard Worker // the USART RX channel.
DoTryReadFor(ByteSpan rx_buffer,size_t min_bytes,std::optional<chrono::SystemClock::duration> timeout)463*61c4878aSAndroid Build Coastguard Worker StatusWithSize DmaUartMcuxpresso::DoTryReadFor(
464*61c4878aSAndroid Build Coastguard Worker     ByteSpan rx_buffer,
465*61c4878aSAndroid Build Coastguard Worker     size_t min_bytes,
466*61c4878aSAndroid Build Coastguard Worker     std::optional<chrono::SystemClock::duration> timeout) {
467*61c4878aSAndroid Build Coastguard Worker   if (timeout.has_value() && *timeout < chrono::SystemClock::duration::zero()) {
468*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize(Status::InvalidArgument(), 0);
469*61c4878aSAndroid Build Coastguard Worker   }
470*61c4878aSAndroid Build Coastguard Worker 
471*61c4878aSAndroid Build Coastguard Worker   size_t length = rx_buffer.size();
472*61c4878aSAndroid Build Coastguard Worker   if (length == 0 || min_bytes > length) {
473*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize(Status::InvalidArgument(), 0);
474*61c4878aSAndroid Build Coastguard Worker   }
475*61c4878aSAndroid Build Coastguard Worker 
476*61c4878aSAndroid Build Coastguard Worker   std::optional<chrono::SystemClock::time_point> deadline = std::nullopt;
477*61c4878aSAndroid Build Coastguard Worker   if (timeout.has_value()) {
478*61c4878aSAndroid Build Coastguard Worker     deadline.emplace(*timeout + chrono::SystemClock::now());
479*61c4878aSAndroid Build Coastguard Worker   }
480*61c4878aSAndroid Build Coastguard Worker 
481*61c4878aSAndroid Build Coastguard Worker   // We only allow a single thread to read from the USART at a time.
482*61c4878aSAndroid Build Coastguard Worker   bool was_busy = rx_data_.busy.exchange(true);
483*61c4878aSAndroid Build Coastguard Worker   if (was_busy) {
484*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize(Status::FailedPrecondition(), 0);
485*61c4878aSAndroid Build Coastguard Worker   }
486*61c4878aSAndroid Build Coastguard Worker 
487*61c4878aSAndroid Build Coastguard Worker   Status status = OkStatus();
488*61c4878aSAndroid Build Coastguard Worker 
489*61c4878aSAndroid Build Coastguard Worker   ByteBuilder bb(rx_buffer);
490*61c4878aSAndroid Build Coastguard Worker   size_t bytes_copied = 0;
491*61c4878aSAndroid Build Coastguard Worker   while (bytes_copied < min_bytes) {
492*61c4878aSAndroid Build Coastguard Worker     // Get the number of bytes available to read.
493*61c4878aSAndroid Build Coastguard Worker     StatusWithSize rx_count_status = TransferGetReceiveDMACount();
494*61c4878aSAndroid Build Coastguard Worker     if (!rx_count_status.ok()) {
495*61c4878aSAndroid Build Coastguard Worker       status.Update(rx_count_status.status());
496*61c4878aSAndroid Build Coastguard Worker       break;
497*61c4878aSAndroid Build Coastguard Worker     }
498*61c4878aSAndroid Build Coastguard Worker     size_t rx_count = rx_count_status.size();
499*61c4878aSAndroid Build Coastguard Worker 
500*61c4878aSAndroid Build Coastguard Worker     // Copy available bytes out of the ring buffer.
501*61c4878aSAndroid Build Coastguard Worker     if (rx_count > 0) {
502*61c4878aSAndroid Build Coastguard Worker       // Allow copying more than min_bytes if they are available.
503*61c4878aSAndroid Build Coastguard Worker       size_t copy_size = std::min(length - bytes_copied, rx_count);
504*61c4878aSAndroid Build Coastguard Worker       CopyReceiveData(bb, copy_size);
505*61c4878aSAndroid Build Coastguard Worker       bytes_copied += copy_size;
506*61c4878aSAndroid Build Coastguard Worker     }
507*61c4878aSAndroid Build Coastguard Worker 
508*61c4878aSAndroid Build Coastguard Worker     // Do we still need more bytes?
509*61c4878aSAndroid Build Coastguard Worker     // We need to wait for more DMA bytes if so.
510*61c4878aSAndroid Build Coastguard Worker     if (bytes_copied < min_bytes) {
511*61c4878aSAndroid Build Coastguard Worker       // Check if the timeout has expired, if applicable.
512*61c4878aSAndroid Build Coastguard Worker       if (deadline.has_value() && chrono::SystemClock::now() >= *deadline) {
513*61c4878aSAndroid Build Coastguard Worker         status.Update(Status::DeadlineExceeded());
514*61c4878aSAndroid Build Coastguard Worker         break;
515*61c4878aSAndroid Build Coastguard Worker       }
516*61c4878aSAndroid Build Coastguard Worker 
517*61c4878aSAndroid Build Coastguard Worker       // Wait up to the timeout duration to receive more bytes.
518*61c4878aSAndroid Build Coastguard Worker       Status wait_status =
519*61c4878aSAndroid Build Coastguard Worker           WaitForReceiveBytes(min_bytes - bytes_copied, deadline);
520*61c4878aSAndroid Build Coastguard Worker       // Even if we exceeded the deadline, stay in the loop for one more
521*61c4878aSAndroid Build Coastguard Worker       // iteration to copy out any final bytes.
522*61c4878aSAndroid Build Coastguard Worker       if (!wait_status.ok() && !wait_status.IsDeadlineExceeded()) {
523*61c4878aSAndroid Build Coastguard Worker         status.Update(wait_status);
524*61c4878aSAndroid Build Coastguard Worker         break;
525*61c4878aSAndroid Build Coastguard Worker       }
526*61c4878aSAndroid Build Coastguard Worker 
527*61c4878aSAndroid Build Coastguard Worker       // At this point, we have new bytes to read, have timed out, or both.
528*61c4878aSAndroid Build Coastguard Worker       // Go back to the top of the loop to figure out which.
529*61c4878aSAndroid Build Coastguard Worker     }
530*61c4878aSAndroid Build Coastguard Worker   }
531*61c4878aSAndroid Build Coastguard Worker 
532*61c4878aSAndroid Build Coastguard Worker   rx_data_.busy.store(false);
533*61c4878aSAndroid Build Coastguard Worker   return StatusWithSize(status, bytes_copied);
534*61c4878aSAndroid Build Coastguard Worker }
535*61c4878aSAndroid Build Coastguard Worker 
536*61c4878aSAndroid Build Coastguard Worker // Write data to USART using DMA transactions
537*61c4878aSAndroid Build Coastguard Worker //
538*61c4878aSAndroid Build Coastguard Worker // Note: Only one thread should be calling this function,
539*61c4878aSAndroid Build Coastguard Worker // otherwise DoWrite calls might fail due to contention for
540*61c4878aSAndroid Build Coastguard Worker // the USART TX channel.
DoTryWriteFor(ConstByteSpan tx_buffer,std::optional<chrono::SystemClock::duration> timeout)541*61c4878aSAndroid Build Coastguard Worker StatusWithSize DmaUartMcuxpresso::DoTryWriteFor(
542*61c4878aSAndroid Build Coastguard Worker     ConstByteSpan tx_buffer,
543*61c4878aSAndroid Build Coastguard Worker     std::optional<chrono::SystemClock::duration> timeout) {
544*61c4878aSAndroid Build Coastguard Worker   if (tx_buffer.size() == 0) {
545*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize(0);
546*61c4878aSAndroid Build Coastguard Worker   }
547*61c4878aSAndroid Build Coastguard Worker 
548*61c4878aSAndroid Build Coastguard Worker   bool was_busy = tx_data_.busy.exchange(true);
549*61c4878aSAndroid Build Coastguard Worker   if (was_busy) {
550*61c4878aSAndroid Build Coastguard Worker     // Another thread is already transmitting data.
551*61c4878aSAndroid Build Coastguard Worker     return StatusWithSize::FailedPrecondition(0);
552*61c4878aSAndroid Build Coastguard Worker   }
553*61c4878aSAndroid Build Coastguard Worker 
554*61c4878aSAndroid Build Coastguard Worker   // Start the DMA. If multiple DMA transactions are needed, the completion
555*61c4878aSAndroid Build Coastguard Worker   // callback will set them up.
556*61c4878aSAndroid Build Coastguard Worker   tx_data_.buffer = tx_buffer;
557*61c4878aSAndroid Build Coastguard Worker   tx_data_.tx_idx = 0;
558*61c4878aSAndroid Build Coastguard Worker 
559*61c4878aSAndroid Build Coastguard Worker   TriggerWriteDma();
560*61c4878aSAndroid Build Coastguard Worker 
561*61c4878aSAndroid Build Coastguard Worker   // Wait for completion, optionally with timeout.
562*61c4878aSAndroid Build Coastguard Worker   Status status = OkStatus();
563*61c4878aSAndroid Build Coastguard Worker   if (timeout.has_value()) {
564*61c4878aSAndroid Build Coastguard Worker     if (!tx_data_.notification.try_acquire_for(*timeout)) {
565*61c4878aSAndroid Build Coastguard Worker       interrupt_lock_.lock();
566*61c4878aSAndroid Build Coastguard Worker       USART_TransferAbortSendDMA(config_.usart_base, &uart_dma_handle_);
567*61c4878aSAndroid Build Coastguard Worker       interrupt_lock_.unlock();
568*61c4878aSAndroid Build Coastguard Worker       status.Update(Status::DeadlineExceeded());
569*61c4878aSAndroid Build Coastguard Worker     }
570*61c4878aSAndroid Build Coastguard Worker   } else {
571*61c4878aSAndroid Build Coastguard Worker     tx_data_.notification.acquire();
572*61c4878aSAndroid Build Coastguard Worker   }
573*61c4878aSAndroid Build Coastguard Worker 
574*61c4878aSAndroid Build Coastguard Worker   size_t bytes_written = tx_data_.tx_idx;
575*61c4878aSAndroid Build Coastguard Worker 
576*61c4878aSAndroid Build Coastguard Worker   tx_data_.busy.store(false);
577*61c4878aSAndroid Build Coastguard Worker 
578*61c4878aSAndroid Build Coastguard Worker   return StatusWithSize(status, bytes_written);
579*61c4878aSAndroid Build Coastguard Worker }
580*61c4878aSAndroid Build Coastguard Worker 
DoConservativeReadAvailable()581*61c4878aSAndroid Build Coastguard Worker size_t DmaUartMcuxpresso::DoConservativeReadAvailable() {
582*61c4878aSAndroid Build Coastguard Worker   StatusWithSize status = TransferGetReceiveDMACount();
583*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
584*61c4878aSAndroid Build Coastguard Worker     return 0;
585*61c4878aSAndroid Build Coastguard Worker   }
586*61c4878aSAndroid Build Coastguard Worker   return status.size();
587*61c4878aSAndroid Build Coastguard Worker }
588*61c4878aSAndroid Build Coastguard Worker 
DoFlushOutput()589*61c4878aSAndroid Build Coastguard Worker Status DmaUartMcuxpresso::DoFlushOutput() {
590*61c4878aSAndroid Build Coastguard Worker   // Unsupported
591*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
592*61c4878aSAndroid Build Coastguard Worker }
593*61c4878aSAndroid Build Coastguard Worker 
DoClearPendingReceiveBytes()594*61c4878aSAndroid Build Coastguard Worker Status DmaUartMcuxpresso::DoClearPendingReceiveBytes() {
595*61c4878aSAndroid Build Coastguard Worker   bool was_busy = rx_data_.busy.exchange(true);
596*61c4878aSAndroid Build Coastguard Worker   if (was_busy) {
597*61c4878aSAndroid Build Coastguard Worker     return Status::FailedPrecondition();
598*61c4878aSAndroid Build Coastguard Worker   }
599*61c4878aSAndroid Build Coastguard Worker   size_t bytes_pending = rx_data_.data_received - rx_data_.data_copied;
600*61c4878aSAndroid Build Coastguard Worker   if (rx_data_.ring_buffer_read_idx + bytes_pending >=
601*61c4878aSAndroid Build Coastguard Worker       rx_data_.ring_buffer.size_bytes()) {
602*61c4878aSAndroid Build Coastguard Worker     rx_data_.ring_buffer_read_idx -= rx_data_.ring_buffer.size_bytes();
603*61c4878aSAndroid Build Coastguard Worker   }
604*61c4878aSAndroid Build Coastguard Worker   rx_data_.ring_buffer_read_idx += bytes_pending;
605*61c4878aSAndroid Build Coastguard Worker 
606*61c4878aSAndroid Build Coastguard Worker   rx_data_.data_copied = rx_data_.data_received;
607*61c4878aSAndroid Build Coastguard Worker   rx_data_.busy.store(false);
608*61c4878aSAndroid Build Coastguard Worker 
609*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
610*61c4878aSAndroid Build Coastguard Worker }
611*61c4878aSAndroid Build Coastguard Worker 
612*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::uart
613