Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / examples / platform / efr32 / uart.c
1 /*
2  *
3  *    Copyright (c) 2021 Project CHIP Authors
4  *    All rights reserved.
5  *
6  *    Licensed under the Apache License, Version 2.0 (the "License");
7  *    you may not use this file except in compliance with the License.
8  *    You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *    Unless required by applicable law or agreed to in writing, software
13  *    distributed under the License is distributed on an "AS IS" BASIS,
14  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *    See the License for the specific language governing permissions and
16  *    limitations under the License.
17  */
18 #include "uart.h"
19 #include "AppConfig.h"
20 #include "assert.h"
21 #include "em_core.h"
22 #include "em_usart.h"
23 #include "hal-config.h"
24 #include "uartdrv.h"
25 #include <stddef.h>
26 #include <string.h>
27
28 #if !defined(MIN)
29 #define MIN(A, B) ((A) < (B) ? (A) : (B))
30 #endif
31
32 DEFINE_BUF_QUEUE(EMDRV_UARTDRV_MAX_CONCURRENT_RX_BUFS, sUartRxQueue);
33 DEFINE_BUF_QUEUE(EMDRV_UARTDRV_MAX_CONCURRENT_TX_BUFS, sUartTxQueue);
34
35 typedef struct
36 {
37     // The data buffer
38     uint8_t * pBuffer;
39     // The offset of the first item written to the list.
40     volatile uint16_t Head;
41     // The offset of the next item to be written to the list.
42     volatile uint16_t Tail;
43     // Maxium size of data that can be hold in buffer before overwriting
44     uint16_t MaxSize;
45 } Fifo_t;
46
47 #define UART_CONSOLE_ERR -1 // Negative value in case of UART Console action failed. Triggers a failure for PW_RPC
48 #define MAX_BUFFER_SIZE 256
49 #define MAX_DMA_BUFFER_SIZE (MAX_BUFFER_SIZE / 2)
50 // In order to reduce the probability of data loss during the dmaFull callback handler we use
51 // two duplicate receive buffers so we can always have one "active" receive queue.
52 static uint8_t sRxDmaBuffer[MAX_DMA_BUFFER_SIZE];
53 static uint8_t sRxDmaBuffer2[MAX_DMA_BUFFER_SIZE];
54 static uint16_t lastCount; // Nb of bytes already processed from the active dmaBuffer
55
56 // Rx buffer for the receive Fifo
57 static uint8_t sRxFifoBuffer[MAX_BUFFER_SIZE];
58 static Fifo_t sReceiveFifo;
59
60 static UARTDRV_HandleData_t sUartHandleData;
61 static UARTDRV_Handle_t sUartHandle = &sUartHandleData;
62
63 static void UART_rx_callback(UARTDRV_Handle_t handle, Ecode_t transferStatus, uint8_t * data, UARTDRV_Count_t transferCount);
64
65 static bool InitFifo(Fifo_t * fifo, uint8_t * pDataBuffer, uint16_t bufferSize)
66 {
67     if (fifo == NULL || pDataBuffer == NULL)
68     {
69         return false;
70     }
71
72     fifo->pBuffer = pDataBuffer;
73     fifo->MaxSize = bufferSize;
74     fifo->Tail = fifo->Head = 0;
75
76     return true;
77 }
78
79 /*
80  *   @brief Get the amount of unprocessed bytes in the fifo buffer
81  *   @param Ptr to the fifo
82  *   @return Nb of "unread" bytes available in the fifo
83  */
84 static uint16_t AvailableDataCount(Fifo_t * fifo)
85 {
86     uint16_t size = 0;
87
88     // if equal there is no data return 0 directly
89     if (fifo->Tail != fifo->Head)
90     {
91         // determine if a wrap around occured to get the right data size avalaible.
92         size = (fifo->Tail < fifo->Head) ? (fifo->MaxSize - fifo->Head + fifo->Tail) : (fifo->Tail - fifo->Head);
93     }
94
95     return size;
96 }
97
98 /*
99  *   @brief Get the available space in the fifo buffer to insert new data
100  *   @param Ptr to the fifo
101  *   @return Nb of free bytes left in te buffer
102  */
103 static uint16_t RemainingSpace(Fifo_t * fifo)
104 {
105     return fifo->MaxSize - AvailableDataCount(fifo);
106 }
107
108 /*
109  *   @brief Write data in the fifo as a circular buffer
110  *   @param Ptr to the fifo, ptr of the data to write, nb of bytes to write
111  */
112 static void WriteToFifo(Fifo_t * fifo, uint8_t * pDataToWrite, uint16_t SizeToWrite)
113 {
114     assert(fifo);
115     assert(pDataToWrite);
116     assert(SizeToWrite <= fifo->MaxSize);
117
118     // Overwrite is not allowed
119     if (RemainingSpace(fifo) >= SizeToWrite)
120     {
121         uint16_t nBytesBeforWrap = (fifo->MaxSize - fifo->Tail);
122         if (SizeToWrite > nBytesBeforWrap)
123         {
124             // The number of bytes to write is bigger than the remaining bytes
125             // in the buffer, we have to wrap around
126             memcpy(fifo->pBuffer + fifo->Tail, pDataToWrite, nBytesBeforWrap);
127             memcpy(fifo->pBuffer, pDataToWrite + nBytesBeforWrap, SizeToWrite - nBytesBeforWrap);
128         }
129         else
130         {
131             memcpy(fifo->pBuffer + fifo->Tail, pDataToWrite, SizeToWrite);
132         }
133
134         fifo->Tail = (fifo->Tail + SizeToWrite) % fifo->MaxSize; // increment tail with wraparound
135     }
136 }
137
138 /*
139  *   @brief Write data in the fifo as a circular buffer
140  *   @param Ptr to the fifo, ptr to contain the data to process, nb of bytes to pull from the fifo
141  *   @return Nb of bytes that were retrieved.
142  */
143 static uint8_t RetrieveFromFifo(Fifo_t * fifo, uint8_t * pData, uint16_t SizeToRead)
144 {
145     assert(fifo);
146     assert(pData);
147     assert(SizeToRead <= fifo->MaxSize);
148
149     uint16_t ReadSize        = MIN(SizeToRead, AvailableDataCount(fifo));
150     uint16_t nBytesBeforWrap = (fifo->MaxSize - fifo->Head);
151
152     if (ReadSize > nBytesBeforWrap)
153     {
154         memcpy(pData, fifo->pBuffer + fifo->Head, nBytesBeforWrap);
155         memcpy(pData + nBytesBeforWrap, fifo->pBuffer, ReadSize - nBytesBeforWrap);
156     }
157     else
158     {
159         memcpy(pData, (fifo->pBuffer + fifo->Head), ReadSize);
160     }
161
162     fifo->Head = (fifo->Head + ReadSize) % fifo->MaxSize; // increment tail with wraparound
163
164     return ReadSize;
165 }
166
167 /*
168  *   @brief Init the the UART for serial communication, Start DMA reception
169  *          and init Fifo to handle the received data from this uart
170  *
171  *   @Note This UART is used for pigweed rpc
172  */
173 void uartConsoleInit(void)
174 {
175     UARTDRV_Init_t uartInit = {
176         .port     = USART0,
177         .baudRate = HAL_SERIAL_APP_BAUD_RATE,
178 #if defined(_USART_ROUTELOC0_MASK)
179         .portLocationTx = BSP_SERIAL_APP_TX_LOC,
180         .portLocationRx = BSP_SERIAL_APP_RX_LOC,
181 #elif defined(_USART_ROUTE_MASK)
182         portLocation = BSP_SERIAL_APP_PORT,
183 #endif
184 #if defined(USART_CTRL_MVDIS)
185         .mvdis = false,
186 #endif
187         .stopBits     = (USART_Stopbits_TypeDef) USART_FRAME_STOPBITS_ONE,
188         .parity       = (USART_Parity_TypeDef) USART_FRAME_PARITY_NONE,
189         .oversampling = (USART_OVS_TypeDef) USART_CTRL_OVS_X16,
190         .fcType       = HAL_SERIAL_APP_FLOW_CONTROL,
191         .ctsPort      = BSP_SERIAL_APP_CTS_PORT,
192         .ctsPin       = BSP_SERIAL_APP_CTS_PIN,
193         .rtsPort      = BSP_SERIAL_APP_RTS_PORT,
194         .rtsPin       = BSP_SERIAL_APP_RTS_PIN,
195         .rxQueue      = (UARTDRV_Buffer_FifoQueue_t *) &sUartRxQueue,
196         .txQueue      = (UARTDRV_Buffer_FifoQueue_t *) &sUartTxQueue,
197 #if defined(_USART_ROUTELOC1_MASK)
198         .portLocationCts = BSP_SERIAL_APP_CTS_LOC,
199         .portLocationRts = BSP_SERIAL_APP_RTS_LOC,
200 #endif
201     };
202
203     // Init a fifo for the data received on the uart
204     InitFifo(&sReceiveFifo, sRxFifoBuffer, MAX_BUFFER_SIZE);
205
206     UARTDRV_InitUart(sUartHandle, &uartInit);
207     // Activate 2 dma queues to always have one active
208     UARTDRV_Receive(sUartHandle, sRxDmaBuffer, MAX_DMA_BUFFER_SIZE, UART_rx_callback);
209     UARTDRV_Receive(sUartHandle, sRxDmaBuffer2, MAX_DMA_BUFFER_SIZE, UART_rx_callback);
210 }
211
212 /*
213  *   @brief Callback triggered when a UARTDRV DMA buffer is full
214  */
215 static void UART_rx_callback(UARTDRV_Handle_t handle, Ecode_t transferStatus, uint8_t * data, UARTDRV_Count_t transferCount)
216 {
217     (void) transferStatus;
218
219     uint8_t writeSize = (transferCount - lastCount);
220     if (RemainingSpace(&sReceiveFifo) >= writeSize)
221     {
222         WriteToFifo(&sReceiveFifo, data + lastCount, writeSize);
223         lastCount = 0;
224     }
225
226     UARTDRV_Receive(sUartHandle, data, transferCount, UART_rx_callback);
227 }
228
229 /*
230  *   @brief Read the data available from the console Uart
231  *   @param Buffer that contains the data to write, number bytes to write.
232  *   @return Amount of bytes written or ERROR (-1)
233  */
234 int16_t uartConsoleWrite(const char * Buf, uint16_t BufLength)
235 {
236     if (Buf == NULL || BufLength < 1)
237     {
238         return UART_CONSOLE_ERR;
239     }
240
241     // Use of ForceTransmit here. Transmit with DMA was causing errors with PW_RPC
242     // TODO Use DMA and find/fix what causes the issue with PW
243     if (UARTDRV_ForceTransmit(sUartHandle, (uint8_t *) Buf, BufLength) == ECODE_EMDRV_UARTDRV_OK)
244     {
245         return BufLength;
246     }
247
248     return UART_CONSOLE_ERR;
249 }
250
251 /*
252  *   @brief Read the data available from the console Uart
253  *   @param Buffer for the data to be read, number bytes to read.
254  *   @return Amount of bytes that was read from the rx fifo or ERROR (-1)
255  */
256 int16_t uartConsoleRead(char * Buf, uint16_t NbBytesToRead)
257 {
258     uint8_t * data;
259     UARTDRV_Count_t count, remaining;
260
261     if (Buf == NULL || NbBytesToRead < 1)
262     {
263         return UART_CONSOLE_ERR;
264     }
265
266     if (NbBytesToRead > AvailableDataCount(&sReceiveFifo))
267     {
268         // Not enough data available in the fifo for the read size request
269         // If there is data available in dma buffer, get it now.
270         CORE_ATOMIC_SECTION(UARTDRV_GetReceiveStatus(sUartHandle, &data, &count, &remaining); if (count > lastCount) {
271             WriteToFifo(&sReceiveFifo, data + lastCount, count - lastCount);
272             lastCount = count;
273         })
274     }
275
276     return (int16_t) RetrieveFromFifo(&sReceiveFifo, (uint8_t *) Buf, NbBytesToRead);
277 }