Porting guide
High level of LwCELL library is platform independent, written in C (C11), however there is an important part where middleware needs to communicate with target GSM device and it must work under different optional operating systems selected by final customer.
Porting consists of:
Implementation of low-level part, for actual communication between host device and GSM device
Implementation of system functions, link between target operating system and middleware functions
Assignment of memory for allocation manager
Implement low-level driver
To successfully prepare all parts of low-level driver, application must take care of:
Implementing
lwcell_ll_init()
andlwcell_ll_deinit()
callback functionsImplement and assign send data and optional hardware reset function callbacks
Assign memory for allocation manager when using default allocator or use custom allocator
Process received data from ESP device and send it to input module for further processing
Tip
Port examples are available for STM32 and WIN32 architectures. Both actual working and up-to-date implementations are available within the library.
Note
Check Input module for more information about direct & indirect input processing.
Implement system functions
System functions are bridge between operating system calls and GSM middleware. GSM library relies on stable operating system features and its implementation and does not require any special features which do not normally come with operating systems.
Operating system must support:
Thread management functions
Mutex management functions
Binary semaphores only functions, no need for counting semaphores
Message queue management functions
Warning
If any of the features are not available within targeted operating system, customer needs to resolve it with care. As an example, message queue is not available in WIN32 OS API therefore custom message queue has been implemented using binary semaphores
Application needs to implement all system call functions, starting with lwcell_sys_
.
It must also prepare header file for standard types in order to support OS types within GSM middleware.
An example code is provided latter section of this page for WIN32 and STM32.
Note
Check System functions for function prototypes.
Steps to follow
Copy
lwcell/src/system/lwcell_sys_template.c
to the same folder and rename it to application port, eg.lwcell_sys_win32.c
Open newly created file and implement all system functions
Copy folder
lwcell/src/include/system/port/template/*
to the same folder and rename folder name to application port, eg.cmsis_os
Open
lwcell_sys_port.h
file from newly created folder and implement all typedefs and macros for specific targetAdd source file to compiler sources and add path to header file to include paths in compiler options
Note
Check System functions for function prototypes.
Example: Low-level driver for WIN32
Example code for low-level porting on WIN32 platform. It uses native Windows features to open COM port and read/write from/to it.
Notes:
It uses separate thread for received data processing. It uses
lwcell_input_process()
orlwcell_input()
functions, based on application configuration ofLWCELL_CFG_INPUT_USE_PROCESS
parameter.When
LWCELL_CFG_INPUT_USE_PROCESS
is disabled, dedicated receive buffer is created by LwCELL library andlwcell_input()
function just writes data to it and does not process received characters immediately. This is handled by Processing thread at later stage instead.When
LWCELL_CFG_INPUT_USE_PROCESS
is enabled,lwcell_input_process()
is used, which directly processes input data and sends potential callback/event functions to application layer.
Memory manager has been assigned to
1
region ofLWCELL_MEM_SIZE
sizeIt sets send and reset callback functions for LwCELL library
1/**
2 * \file lwcell_ll_win32.c
3 * \brief Low-level communication with GSM device for WIN32
4 */
5
6/*
7 * Copyright (c) 2024 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwCELL - Lightweight cellular modem AT library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v0.1.1
33 */
34#include "lwcell/lwcell_input.h"
35#include "lwcell/lwcell_mem.h"
36#include "lwcell/lwcell_types.h"
37#include "lwcell/lwcell_utils.h"
38#include "system/lwcell_ll.h"
39#include "system/lwcell_sys.h"
40#include "windows.h"
41
42#if !__DOXYGEN__
43
44static uint8_t initialized = 0;
45static HANDLE thread_handle;
46static volatile HANDLE com_port; /*!< COM port handle */
47static uint8_t data_buffer[0x1000]; /*!< Received data array */
48
49static void uart_thread(void* param);
50
51/**
52 * \brief Send data to GSM device, function called from GSM stack when we have data to send
53 * \param[in] data: Pointer to data to send
54 * \param[in] len: Number of bytes to send
55 * \return Number of bytes sent
56 */
57static size_t
58send_data(const void* data, size_t len) {
59 DWORD written;
60 if (com_port != NULL) {
61#if !LWCELL_CFG_AT_ECHO
62 const uint8_t* d = data;
63 HANDLE hConsole;
64
65 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
66 SetConsoleTextAttribute(hConsole, FOREGROUND_RED);
67 for (DWORD i = 0; i < len; ++i) {
68 printf("%c", d[i]);
69 }
70 SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
71#endif /* !LWCELL_CFG_AT_ECHO */
72
73 /* Write data to AT port */
74 WriteFile(com_port, data, len, &written, NULL);
75 FlushFileBuffers(com_port);
76 return written;
77 }
78 return 0;
79}
80
81/**
82 * \brief Configure UART (USB to UART)
83 */
84static uint8_t
85configure_uart(uint32_t baudrate) {
86 DCB dcb = {0};
87 dcb.DCBlength = sizeof(dcb);
88
89 /*
90 * On first call,
91 * create virtual file on selected COM port and open it
92 * as generic read and write
93 */
94 if (!initialized) {
95 static const char* com_ports[] = {"\\\\.\\COM23", "\\\\.\\COM12", "\\\\.\\COM9", "\\\\.\\COM8", "\\\\.\\COM4"};
96 for (size_t i = 0; i < sizeof(com_ports) / sizeof(com_ports[0]); ++i) {
97 com_port = CreateFileA(com_ports[i], GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);
98 if (GetCommState(com_port, &dcb)) {
99 printf("COM PORT %s opened!\r\n", (const char*)com_ports[i]);
100 break;
101 }
102 }
103 }
104
105 /* Configure COM port parameters */
106 if (GetCommState(com_port, &dcb)) {
107 COMMTIMEOUTS timeouts;
108
109 dcb.BaudRate = baudrate;
110 dcb.ByteSize = 8;
111 dcb.Parity = NOPARITY;
112 dcb.StopBits = ONESTOPBIT;
113
114 if (!SetCommState(com_port, &dcb)) {
115 printf("Cannot set COM PORT info\r\n");
116 return 0;
117 }
118 if (GetCommTimeouts(com_port, &timeouts)) {
119 /* Set timeout to return immediately from ReadFile function */
120 timeouts.ReadIntervalTimeout = MAXDWORD;
121 timeouts.ReadTotalTimeoutConstant = 0;
122 timeouts.ReadTotalTimeoutMultiplier = 0;
123 if (!SetCommTimeouts(com_port, &timeouts)) {
124 printf("Cannot set COM PORT timeouts\r\n");
125 }
126 GetCommTimeouts(com_port, &timeouts);
127 } else {
128 printf("Cannot get COM PORT timeouts\r\n");
129 return 0;
130 }
131 } else {
132 printf("Cannot get COM PORT info\r\n");
133 return 0;
134 }
135
136 /* On first function call, create a thread to read data from COM port */
137 if (!initialized) {
138 lwcell_sys_thread_create(&thread_handle, "lwcell_ll_thread", uart_thread, NULL, 0, 0);
139 }
140 return 1;
141}
142
143/**
144 * \brief UART thread
145 */
146static void
147uart_thread(void* param) {
148 DWORD bytes_read;
149 lwcell_sys_sem_t sem;
150 FILE* file = NULL;
151
152 LWCELL_UNUSED(param);
153
154 lwcell_sys_sem_create(&sem, 0); /* Create semaphore for delay functions */
155
156 while (com_port == NULL) {
157 lwcell_sys_sem_wait(&sem, 1); /* Add some delay with yield */
158 }
159
160 fopen_s(&file, "log_file.txt", "w+"); /* Open debug file in write mode */
161 while (1) {
162 /*
163 * Try to read data from COM port
164 * and send it to upper layer for processing
165 */
166 do {
167 ReadFile(com_port, data_buffer, sizeof(data_buffer), &bytes_read, NULL);
168 if (bytes_read > 0) {
169 HANDLE hConsole;
170 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
171 SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);
172 for (DWORD i = 0; i < bytes_read; ++i) {
173 printf("%c", data_buffer[i]);
174 }
175 SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
176
177 /* Send received data to input processing module */
178#if LWCELL_CFG_INPUT_USE_PROCESS
179 lwcell_input_process(data_buffer, (size_t)bytes_read);
180#else /* LWCELL_CFG_INPUT_USE_PROCESS */
181 lwcell_input(data_buffer, (size_t)bytes_read);
182#endif /* !LWCELL_CFG_INPUT_USE_PROCESS */
183
184 /* Write received data to output debug file */
185 if (file != NULL) {
186 fwrite(data_buffer, 1, bytes_read, file);
187 fflush(file);
188 }
189 }
190 } while (bytes_read == (DWORD)sizeof(data_buffer));
191
192 /* Implement delay to allow other tasks processing */
193 lwcell_sys_sem_wait(&sem, 1);
194 }
195}
196
197/**
198 * \brief Callback function called from initialization process
199 *
200 * \note This function may be called multiple times if AT baudrate is changed from application.
201 * It is important that every configuration except AT baudrate is configured only once!
202 *
203 * \note This function may be called from different threads in GSM stack when using OS.
204 * When \ref LWCELL_CFG_INPUT_USE_PROCESS is set to 1, this function may be called from user UART thread.
205 *
206 * \param[in,out] ll: Pointer to \ref lwcell_ll_t structure to fill data for communication functions
207 * \return \ref lwcellOK on success, member of \ref lwcellr_t enumeration otherwise
208 */
209lwcellr_t
210lwcell_ll_init(lwcell_ll_t* ll) {
211#if !LWCELL_CFG_MEM_CUSTOM
212 /* Step 1: Configure memory for dynamic allocations */
213 static uint8_t memory[0x10000]; /* Create memory for dynamic allocations with specific size */
214
215 /*
216 * Create memory region(s) of memory.
217 * If device has internal/external memory available,
218 * multiple memories may be used
219 */
220 lwcell_mem_region_t mem_regions[] = {{memory, sizeof(memory)}};
221 if (!initialized) {
222 lwcell_mem_assignmemory(mem_regions,
223 LWCELL_ARRAYSIZE(mem_regions)); /* Assign memory for allocations to GSM library */
224 }
225#endif /* !LWCELL_CFG_MEM_CUSTOM */
226
227 /* Step 2: Set AT port send function to use when we have data to transmit */
228 if (!initialized) {
229 ll->send_fn = send_data; /* Set callback function to send data */
230 }
231
232 /* Step 3: Configure AT port to be able to send/receive data to/from GSM device */
233 if (!configure_uart(ll->uart.baudrate)) { /* Initialize UART for communication */
234 return lwcellERR;
235 }
236 initialized = 1;
237 return lwcellOK;
238}
239
240#endif /* !__DOXYGEN__ */
Example: Low-level driver for STM32
Example code for low-level porting on STM32 platform. It uses CMSIS-OS based application layer functions for implementing threads & other OS dependent features.
Notes:
It uses separate thread for received data processing. It uses
lwcell_input_process()
function to directly process received data without using intermediate receive bufferMemory manager has been assigned to
1
region ofLWCELL_MEM_SIZE
sizeIt sets send and reset callback functions for LwCELL library
1/**
2 * \file lwcell_ll_stm32.c
3 * \brief Generic STM32 driver, included in various STM32 driver variants
4 */
5
6/*
7 * Copyright (c) 2024 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwCELL - Lightweight cellular modem AT library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v0.1.1
33 */
34
35/*
36 * How it works
37 *
38 * On first call to \ref lwcell_ll_init, new thread is created and processed in usart_ll_thread function.
39 * USART is configured in RX DMA mode and any incoming bytes are processed inside thread function.
40 * DMA and USART implement interrupt handlers to notify main thread about new data ready to send to upper layer.
41 *
42 * More about UART + RX DMA: https://github.com/MaJerle/stm32-usart-dma-rx-tx
43 *
44 * \ref LWCELL_CFG_INPUT_USE_PROCESS must be enabled in `lwcell_config.h` to use this driver.
45 */
46#include "lwcell/lwcell_input.h"
47#include "lwcell/lwcell_mem.h"
48#include "lwcell/lwcell_types.h"
49#include "lwcell/lwcell_utils.h"
50#include "system/lwcell_ll.h"
51#include "system/lwcell_sys.h"
52
53#if !__DOXYGEN__
54
55#if !LWCELL_CFG_INPUT_USE_PROCESS
56#error "LWCELL_CFG_INPUT_USE_PROCESS must be enabled in `lwcell_config.h` to use this driver."
57#endif /* LWCELL_CFG_INPUT_USE_PROCESS */
58
59#if !defined(LWCELL_USART_DMA_RX_BUFF_SIZE)
60#define LWCELL_USART_DMA_RX_BUFF_SIZE 0x1000
61#endif /* !defined(LWCELL_USART_DMA_RX_BUFF_SIZE) */
62
63#if !defined(LWCELL_MEM_SIZE)
64#define LWCELL_MEM_SIZE 0x1000
65#endif /* !defined(LWCELL_MEM_SIZE) */
66
67#if !defined(LWCELL_USART_RDR_NAME)
68#define LWCELL_USART_RDR_NAME RDR
69#endif /* !defined(LWCELL_USART_RDR_NAME) */
70
71/* USART memory */
72static uint8_t usart_mem[LWCELL_USART_DMA_RX_BUFF_SIZE];
73static uint8_t is_running, initialized;
74static size_t old_pos;
75
76/* USART thread */
77static void usart_ll_thread(void* arg);
78static osThreadId_t usart_ll_thread_id;
79
80/* Message queue */
81static osMessageQueueId_t usart_ll_mbox_id;
82
83/**
84 * \brief USART data processing
85 */
86static void
87usart_ll_thread(void* arg) {
88 size_t pos;
89
90 LWCELL_UNUSED(arg);
91
92 while (1) {
93 void* d;
94 /* Wait for the event message from DMA or USART */
95 osMessageQueueGet(usart_ll_mbox_id, &d, NULL, osWaitForever);
96
97 /* Read data */
98#if defined(LWCELL_USART_DMA_RX_STREAM)
99 pos = sizeof(usart_mem) - LL_DMA_GetDataLength(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_STREAM);
100#else
101 pos = sizeof(usart_mem) - LL_DMA_GetDataLength(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_CH);
102#endif /* defined(LWCELL_USART_DMA_RX_STREAM) */
103 if (pos != old_pos && is_running) {
104 if (pos > old_pos) {
105 lwcell_input_process(&usart_mem[old_pos], pos - old_pos);
106 } else {
107 lwcell_input_process(&usart_mem[old_pos], sizeof(usart_mem) - old_pos);
108 if (pos > 0) {
109 lwcell_input_process(&usart_mem[0], pos);
110 }
111 }
112 old_pos = pos;
113 if (old_pos == sizeof(usart_mem)) {
114 old_pos = 0;
115 }
116 }
117 }
118}
119
120/**
121 * \brief Configure UART using DMA for receive in double buffer mode and IDLE line detection
122 */
123static void
124configure_uart(uint32_t baudrate) {
125 static LL_USART_InitTypeDef usart_init;
126 static LL_DMA_InitTypeDef dma_init;
127 LL_GPIO_InitTypeDef gpio_init;
128
129 if (!initialized) {
130 /* Enable peripheral clocks */
131 LWCELL_USART_CLK;
132 LWCELL_USART_DMA_CLK;
133 LWCELL_USART_TX_PORT_CLK;
134 LWCELL_USART_RX_PORT_CLK;
135
136#if defined(LWCELL_RESET_PIN)
137 LWCELL_RESET_PORT_CLK;
138#endif /* defined(LWCELL_RESET_PIN) */
139
140 /* Global pin configuration */
141 LL_GPIO_StructInit(&gpio_init);
142 gpio_init.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
143 gpio_init.Pull = LL_GPIO_PULL_UP;
144 gpio_init.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
145 gpio_init.Mode = LL_GPIO_MODE_OUTPUT;
146
147#if defined(LWCELL_RESET_PIN)
148 /* Configure RESET pin */
149 gpio_init.Pin = LWCELL_RESET_PIN;
150 LL_GPIO_Init(LWCELL_RESET_PORT, &gpio_init);
151#endif /* defined(LWCELL_RESET_PIN) */
152
153 /* Configure USART pins */
154 gpio_init.Mode = LL_GPIO_MODE_ALTERNATE;
155
156 /* TX PIN */
157 gpio_init.Alternate = LWCELL_USART_TX_PIN_AF;
158 gpio_init.Pin = LWCELL_USART_TX_PIN;
159 LL_GPIO_Init(LWCELL_USART_TX_PORT, &gpio_init);
160
161 /* RX PIN */
162 gpio_init.Alternate = LWCELL_USART_RX_PIN_AF;
163 gpio_init.Pin = LWCELL_USART_RX_PIN;
164 LL_GPIO_Init(LWCELL_USART_RX_PORT, &gpio_init);
165
166 /* Configure UART */
167 LL_USART_DeInit(LWCELL_USART);
168 LL_USART_StructInit(&usart_init);
169 usart_init.BaudRate = baudrate;
170 usart_init.DataWidth = LL_USART_DATAWIDTH_8B;
171 usart_init.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
172 usart_init.OverSampling = LL_USART_OVERSAMPLING_16;
173 usart_init.Parity = LL_USART_PARITY_NONE;
174 usart_init.StopBits = LL_USART_STOPBITS_1;
175 usart_init.TransferDirection = LL_USART_DIRECTION_TX_RX;
176 LL_USART_Init(LWCELL_USART, &usart_init);
177
178 /* Enable USART interrupts and DMA request */
179 LL_USART_EnableIT_IDLE(LWCELL_USART);
180 LL_USART_EnableIT_PE(LWCELL_USART);
181 LL_USART_EnableIT_ERROR(LWCELL_USART);
182 LL_USART_EnableDMAReq_RX(LWCELL_USART);
183
184 /* Enable USART interrupts */
185 NVIC_SetPriority(LWCELL_USART_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
186 NVIC_EnableIRQ(LWCELL_USART_IRQ);
187
188 /* Configure DMA */
189 is_running = 0;
190#if defined(LWCELL_USART_DMA_RX_STREAM)
191 LL_DMA_DeInit(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_STREAM);
192 dma_init.Channel = LWCELL_USART_DMA_RX_CH;
193#else
194 LL_DMA_DeInit(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_CH);
195 dma_init.PeriphRequest = LWCELL_USART_DMA_RX_REQ_NUM;
196#endif /* defined(LWCELL_USART_DMA_RX_STREAM) */
197 dma_init.PeriphOrM2MSrcAddress = (uint32_t)&LWCELL_USART->LWCELL_USART_RDR_NAME;
198 dma_init.MemoryOrM2MDstAddress = (uint32_t)usart_mem;
199 dma_init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
200 dma_init.Mode = LL_DMA_MODE_CIRCULAR;
201 dma_init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
202 dma_init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
203 dma_init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
204 dma_init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
205 dma_init.NbData = sizeof(usart_mem);
206 dma_init.Priority = LL_DMA_PRIORITY_MEDIUM;
207#if defined(LWCELL_USART_DMA_RX_STREAM)
208 LL_DMA_Init(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_STREAM, &dma_init);
209#else
210 LL_DMA_Init(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_CH, &dma_init);
211#endif /* defined(LWCELL_USART_DMA_RX_STREAM) */
212
213 /* Enable DMA interrupts */
214#if defined(LWCELL_USART_DMA_RX_STREAM)
215 LL_DMA_EnableIT_HT(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_STREAM);
216 LL_DMA_EnableIT_TC(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_STREAM);
217 LL_DMA_EnableIT_TE(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_STREAM);
218 LL_DMA_EnableIT_FE(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_STREAM);
219 LL_DMA_EnableIT_DME(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_STREAM);
220#else
221 LL_DMA_EnableIT_HT(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_CH);
222 LL_DMA_EnableIT_TC(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_CH);
223 LL_DMA_EnableIT_TE(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_CH);
224#endif /* defined(LWCELL_USART_DMA_RX_STREAM) */
225
226 /* Enable DMA interrupts */
227 NVIC_SetPriority(LWCELL_USART_DMA_RX_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
228 NVIC_EnableIRQ(LWCELL_USART_DMA_RX_IRQ);
229
230 old_pos = 0;
231 is_running = 1;
232
233 /* Start DMA and USART */
234#if defined(LWCELL_USART_DMA_RX_STREAM)
235 LL_DMA_EnableStream(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_STREAM);
236#else
237 LL_DMA_EnableChannel(LWCELL_USART_DMA, LWCELL_USART_DMA_RX_CH);
238#endif /* defined(LWCELL_USART_DMA_RX_STREAM) */
239 LL_USART_Enable(LWCELL_USART);
240 } else {
241 osDelay(10);
242 LL_USART_Disable(LWCELL_USART);
243 usart_init.BaudRate = baudrate;
244 LL_USART_Init(LWCELL_USART, &usart_init);
245 LL_USART_Enable(LWCELL_USART);
246 }
247
248 /* Create mbox and start thread */
249 if (usart_ll_mbox_id == NULL) {
250 usart_ll_mbox_id = osMessageQueueNew(10, sizeof(void*), NULL);
251 }
252 if (usart_ll_thread_id == NULL) {
253 const osThreadAttr_t attr = {.stack_size = 1024};
254 usart_ll_thread_id = osThreadNew(usart_ll_thread, usart_ll_mbox_id, &attr);
255 }
256}
257
258#if defined(LWCELL_RESET_PIN)
259/**
260 * \brief Hardware reset callback
261 */
262static uint8_t
263reset_device(uint8_t state) {
264 if (state) { /* Activate reset line */
265 LL_GPIO_ResetOutputPin(LWCELL_RESET_PORT, LWCELL_RESET_PIN);
266 } else {
267 LL_GPIO_SetOutputPin(LWCELL_RESET_PORT, LWCELL_RESET_PIN);
268 }
269 return 1;
270}
271#endif /* defined(LWCELL_RESET_PIN) */
272
273/**
274 * \brief Send data to GSM device
275 * \param[in] data: Pointer to data to send
276 * \param[in] len: Number of bytes to send
277 * \return Number of bytes sent
278 */
279static size_t
280send_data(const void* data, size_t len) {
281 const uint8_t* d = data;
282
283 for (size_t i = 0; i < len; ++i, ++d) {
284 LL_USART_TransmitData8(LWCELL_USART, *d);
285 while (!LL_USART_IsActiveFlag_TXE(LWCELL_USART)) {}
286 }
287 return len;
288}
289
290/**
291 * \brief Callback function called from initialization process
292 * \note This function may be called multiple times if AT baudrate is changed from application
293 * \param[in,out] ll: Pointer to \ref lwcell_ll_t structure to fill data for communication functions
294 * \param[in] baudrate: Baudrate to use on AT port
295 * \return Member of \ref lwcellr_t enumeration
296 */
297lwcellr_t
298lwcell_ll_init(lwcell_ll_t* ll) {
299#if !LWCELL_CFG_MEM_CUSTOM
300 static uint8_t memory[LWCELL_MEM_SIZE];
301 lwcell_mem_region_t mem_regions[] = {{memory, sizeof(memory)}};
302
303 if (!initialized) {
304 lwcell_mem_assignmemory(mem_regions, LWCELL_ARRAYSIZE(mem_regions)); /* Assign memory for allocations */
305 }
306#endif /* !LWCELL_CFG_MEM_CUSTOM */
307
308 if (!initialized) {
309 ll->send_fn = send_data; /* Set callback function to send data */
310#if defined(LWCELL_RESET_PIN)
311 ll->reset_fn = reset_device; /* Set callback for hardware reset */
312#endif /* defined(LWCELL_RESET_PIN) */
313 }
314
315 configure_uart(ll->uart.baudrate); /* Initialize UART for communication */
316 initialized = 1;
317 return lwcellOK;
318}
319
320/**
321 * \brief Callback function to de-init low-level communication part
322 * \param[in,out] ll: Pointer to \ref lwcell_ll_t structure to fill data for communication functions
323 * \return \ref lwcellOK on success, member of \ref lwcellr_t enumeration otherwise
324 */
325lwcellr_t
326lwcell_ll_deinit(lwcell_ll_t* ll) {
327 if (usart_ll_mbox_id != NULL) {
328 osMessageQueueId_t tmp = usart_ll_mbox_id;
329 usart_ll_mbox_id = NULL;
330 osMessageQueueDelete(tmp);
331 }
332 if (usart_ll_thread_id != NULL) {
333 osThreadId_t tmp = usart_ll_thread_id;
334 usart_ll_thread_id = NULL;
335 osThreadTerminate(tmp);
336 }
337 initialized = 0;
338 LWCELL_UNUSED(ll);
339 return lwcellOK;
340}
341
342/**
343 * \brief UART global interrupt handler
344 */
345void
346LWCELL_USART_IRQHANDLER(void) {
347 LL_USART_ClearFlag_IDLE(LWCELL_USART);
348 LL_USART_ClearFlag_PE(LWCELL_USART);
349 LL_USART_ClearFlag_FE(LWCELL_USART);
350 LL_USART_ClearFlag_ORE(LWCELL_USART);
351 LL_USART_ClearFlag_NE(LWCELL_USART);
352
353 if (usart_ll_mbox_id != NULL) {
354 void* d = (void*)1;
355 osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
356 }
357}
358
359/**
360 * \brief UART DMA stream/channel handler
361 */
362void
363LWCELL_USART_DMA_RX_IRQHANDLER(void) {
364 LWCELL_USART_DMA_RX_CLEAR_TC;
365 LWCELL_USART_DMA_RX_CLEAR_HT;
366
367 if (usart_ll_mbox_id != NULL) {
368 void* d = (void*)1;
369 osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
370 }
371}
372
373#endif /* !__DOXYGEN__ */
Example: System functions for WIN32
1/**
2 * \file lwcell_sys_port.h
3 * \brief WIN32 based system file implementation
4 */
5
6/*
7 * Copyright (c) 2024 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwCELL - Lightweight cellular modem AT library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v0.1.1
33 */
34#ifndef LWCELL_SYSTEM_PORT_HDR_H
35#define LWCELL_SYSTEM_PORT_HDR_H
36
37#include <stdint.h>
38#include <stdlib.h>
39#include "lwcell/lwcell_opt.h"
40#include "windows.h"
41
42#ifdef __cplusplus
43extern "C" {
44#endif /* __cplusplus */
45
46#if LWCELL_CFG_OS && !__DOXYGEN__
47
48typedef HANDLE lwcell_sys_mutex_t;
49typedef HANDLE lwcell_sys_sem_t;
50typedef HANDLE lwcell_sys_mbox_t;
51typedef HANDLE lwcell_sys_thread_t;
52typedef int lwcell_sys_thread_prio_t;
53
54#define LWCELL_SYS_MUTEX_NULL ((HANDLE)0)
55#define LWCELL_SYS_SEM_NULL ((HANDLE)0)
56#define LWCELL_SYS_MBOX_NULL ((HANDLE)0)
57#define LWCELL_SYS_TIMEOUT (INFINITE)
58#define LWCELL_SYS_THREAD_PRIO (0)
59#define LWCELL_SYS_THREAD_SS (4096)
60
61#endif /* LWCELL_CFG_OS && !__DOXYGEN__ */
62
63#ifdef __cplusplus
64}
65#endif /* __cplusplus */
66
67#endif /* LWCELL_SYSTEM_PORT_HDR_H */
1/**
2 * \file lwcell_sys_win32.c
3 * \brief System dependant functions for WIN32
4 */
5
6/*
7 * Copyright (c) 2024 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwCELL - Lightweight cellular modem AT library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v0.1.1
33 */
34#include <stdlib.h>
35#include <string.h>
36#include "lwcell/lwcell_private.h"
37#include "system/lwcell_sys.h"
38
39#if !__DOXYGEN__
40
41/**
42 * \brief Custom message queue implementation for WIN32
43 */
44typedef struct {
45 lwcell_sys_sem_t sem_not_empty; /*!< Semaphore indicates not empty */
46 lwcell_sys_sem_t sem_not_full; /*!< Semaphore indicates not full */
47 lwcell_sys_sem_t sem; /*!< Semaphore to lock access */
48 size_t in, out, size;
49 void* entries[1];
50} win32_mbox_t;
51
52static LARGE_INTEGER freq, sys_start_time;
53static lwcell_sys_mutex_t sys_mutex; /* Mutex ID for main protection */
54
55static uint8_t
56mbox_is_full(win32_mbox_t* m) {
57 size_t size = 0;
58 if (m->in > m->out) {
59 size = (m->in - m->out);
60 } else if (m->out > m->in) {
61 size = m->size - m->out + m->in;
62 }
63 return size == m->size - 1;
64}
65
66static uint8_t
67mbox_is_empty(win32_mbox_t* m) {
68 return m->in == m->out;
69}
70
71static uint32_t
72osKernelSysTick(void) {
73 LONGLONG ret;
74 LARGE_INTEGER now;
75
76 QueryPerformanceFrequency(&freq); /* Get frequency */
77 QueryPerformanceCounter(&now); /* Get current time */
78 ret = now.QuadPart - sys_start_time.QuadPart;
79 return (uint32_t)(((ret)*1000) / freq.QuadPart);
80}
81
82uint8_t
83lwcell_sys_init(void) {
84 QueryPerformanceFrequency(&freq);
85 QueryPerformanceCounter(&sys_start_time);
86
87 lwcell_sys_mutex_create(&sys_mutex);
88 return 1;
89}
90
91uint32_t
92lwcell_sys_now(void) {
93 return osKernelSysTick();
94}
95
96uint8_t
97lwcell_sys_protect(void) {
98 lwcell_sys_mutex_lock(&sys_mutex);
99 return 1;
100}
101
102uint8_t
103lwcell_sys_unprotect(void) {
104 lwcell_sys_mutex_unlock(&sys_mutex);
105 return 1;
106}
107
108uint8_t
109lwcell_sys_mutex_create(lwcell_sys_mutex_t* p) {
110 *p = CreateMutex(NULL, FALSE, NULL);
111 return *p != NULL;
112}
113
114uint8_t
115lwcell_sys_mutex_delete(lwcell_sys_mutex_t* p) {
116 return CloseHandle(*p);
117}
118
119uint8_t
120lwcell_sys_mutex_lock(lwcell_sys_mutex_t* p) {
121 DWORD ret;
122 ret = WaitForSingleObject(*p, INFINITE);
123 if (ret != WAIT_OBJECT_0) {
124 return 0;
125 }
126 return 1;
127}
128
129uint8_t
130lwcell_sys_mutex_unlock(lwcell_sys_mutex_t* p) {
131 return (uint8_t)ReleaseMutex(*p);
132}
133
134uint8_t
135lwcell_sys_mutex_isvalid(lwcell_sys_mutex_t* p) {
136 return p != NULL && *p != NULL;
137}
138
139uint8_t
140lwcell_sys_mutex_invalid(lwcell_sys_mutex_t* p) {
141 *p = LWCELL_SYS_MUTEX_NULL;
142 return 1;
143}
144
145uint8_t
146lwcell_sys_sem_create(lwcell_sys_sem_t* p, uint8_t cnt) {
147 HANDLE h;
148 h = CreateSemaphore(NULL, !!cnt, 1, NULL);
149 *p = h;
150 return *p != NULL;
151}
152
153uint8_t
154lwcell_sys_sem_delete(lwcell_sys_sem_t* p) {
155 return CloseHandle(*p);
156}
157
158uint32_t
159lwcell_sys_sem_wait(lwcell_sys_sem_t* p, uint32_t timeout) {
160 DWORD ret;
161
162 if (timeout == 0) {
163 ret = WaitForSingleObject(*p, INFINITE);
164 return 1;
165 } else {
166 ret = WaitForSingleObject(*p, timeout);
167 if (ret == WAIT_OBJECT_0) {
168 return 1;
169 } else {
170 return LWCELL_SYS_TIMEOUT;
171 }
172 }
173}
174
175uint8_t
176lwcell_sys_sem_release(lwcell_sys_sem_t* p) {
177 return ReleaseSemaphore(*p, 1, NULL);
178}
179
180uint8_t
181lwcell_sys_sem_isvalid(lwcell_sys_sem_t* p) {
182 return p != NULL && *p != NULL;
183}
184
185uint8_t
186lwcell_sys_sem_invalid(lwcell_sys_sem_t* p) {
187 *p = LWCELL_SYS_SEM_NULL;
188 return 1;
189}
190
191uint8_t
192lwcell_sys_mbox_create(lwcell_sys_mbox_t* b, size_t size) {
193 win32_mbox_t* mbox;
194
195 *b = NULL;
196
197 mbox = malloc(sizeof(*mbox) + size * sizeof(void*));
198 if (mbox != NULL) {
199 memset(mbox, 0x00, sizeof(*mbox));
200 mbox->size = size + 1; /* Set it to 1 more as cyclic buffer has only one less than size */
201 lwcell_sys_sem_create(&mbox->sem, 1);
202 lwcell_sys_sem_create(&mbox->sem_not_empty, 0);
203 lwcell_sys_sem_create(&mbox->sem_not_full, 0);
204 *b = mbox;
205 }
206 return *b != NULL;
207}
208
209uint8_t
210lwcell_sys_mbox_delete(lwcell_sys_mbox_t* b) {
211 win32_mbox_t* mbox = *b;
212 lwcell_sys_sem_delete(&mbox->sem);
213 lwcell_sys_sem_delete(&mbox->sem_not_full);
214 lwcell_sys_sem_delete(&mbox->sem_not_empty);
215 free(mbox);
216 return 1;
217}
218
219uint32_t
220lwcell_sys_mbox_put(lwcell_sys_mbox_t* b, void* m) {
221 win32_mbox_t* mbox = *b;
222 uint32_t time = osKernelSysTick(); /* Get start time */
223
224 lwcell_sys_sem_wait(&mbox->sem, 0); /* Wait for access */
225
226 /*
227 * Since function is blocking until ready to write something to queue,
228 * wait and release the semaphores to allow other threads
229 * to process the queue before we can write new value.
230 */
231 while (mbox_is_full(mbox)) {
232 lwcell_sys_sem_release(&mbox->sem); /* Release semaphore */
233 lwcell_sys_sem_wait(&mbox->sem_not_full, 0); /* Wait for semaphore indicating not full */
234 lwcell_sys_sem_wait(&mbox->sem, 0); /* Wait availability again */
235 }
236 mbox->entries[mbox->in] = m;
237 if (++mbox->in >= mbox->size) {
238 mbox->in = 0;
239 }
240 lwcell_sys_sem_release(&mbox->sem_not_empty); /* Signal non-empty state */
241 lwcell_sys_sem_release(&mbox->sem); /* Release access for other threads */
242 return osKernelSysTick() - time;
243}
244
245uint32_t
246lwcell_sys_mbox_get(lwcell_sys_mbox_t* b, void** m, uint32_t timeout) {
247 win32_mbox_t* mbox = *b;
248 uint32_t time;
249
250 time = osKernelSysTick();
251
252 /* Get exclusive access to message queue */
253 if (lwcell_sys_sem_wait(&mbox->sem, timeout) == LWCELL_SYS_TIMEOUT) {
254 return LWCELL_SYS_TIMEOUT;
255 }
256 while (mbox_is_empty(mbox)) {
257 lwcell_sys_sem_release(&mbox->sem);
258 if (lwcell_sys_sem_wait(&mbox->sem_not_empty, timeout) == LWCELL_SYS_TIMEOUT) {
259 return LWCELL_SYS_TIMEOUT;
260 }
261 lwcell_sys_sem_wait(&mbox->sem, timeout);
262 }
263 *m = mbox->entries[mbox->out];
264 if (++mbox->out >= mbox->size) {
265 mbox->out = 0;
266 }
267 lwcell_sys_sem_release(&mbox->sem_not_full);
268 lwcell_sys_sem_release(&mbox->sem);
269
270 return osKernelSysTick() - time;
271}
272
273uint8_t
274lwcell_sys_mbox_putnow(lwcell_sys_mbox_t* b, void* m) {
275 win32_mbox_t* mbox = *b;
276
277 lwcell_sys_sem_wait(&mbox->sem, 0);
278 if (mbox_is_full(mbox)) {
279 lwcell_sys_sem_release(&mbox->sem);
280 return 0;
281 }
282 mbox->entries[mbox->in] = m;
283 if (mbox->in == mbox->out) {
284 lwcell_sys_sem_release(&mbox->sem_not_empty);
285 }
286 if (++mbox->in >= mbox->size) {
287 mbox->in = 0;
288 }
289 lwcell_sys_sem_release(&mbox->sem);
290 return 1;
291}
292
293uint8_t
294lwcell_sys_mbox_getnow(lwcell_sys_mbox_t* b, void** m) {
295 win32_mbox_t* mbox = *b;
296
297 lwcell_sys_sem_wait(&mbox->sem, 0); /* Wait exclusive access */
298 if (mbox->in == mbox->out) {
299 lwcell_sys_sem_release(&mbox->sem); /* Release access */
300 return 0;
301 }
302
303 *m = mbox->entries[mbox->out];
304 if (++mbox->out >= mbox->size) {
305 mbox->out = 0;
306 }
307 lwcell_sys_sem_release(&mbox->sem_not_full); /* Queue not full anymore */
308 lwcell_sys_sem_release(&mbox->sem); /* Release semaphore */
309 return 1;
310}
311
312uint8_t
313lwcell_sys_mbox_isvalid(lwcell_sys_mbox_t* b) {
314 return b != NULL && *b != NULL; /* Return status if message box is valid */
315}
316
317uint8_t
318lwcell_sys_mbox_invalid(lwcell_sys_mbox_t* b) {
319 *b = LWCELL_SYS_MBOX_NULL; /* Invalidate message box */
320 return 1;
321}
322
323uint8_t
324lwcell_sys_thread_create(lwcell_sys_thread_t* t, const char* name, lwcell_sys_thread_fn thread_func, void* const arg,
325 size_t stack_size, lwcell_sys_thread_prio_t prio) {
326 HANDLE h;
327 DWORD id;
328
329 LWCELL_UNUSED(name);
330 LWCELL_UNUSED(stack_size);
331 LWCELL_UNUSED(prio);
332
333 h = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_func, arg, 0, &id);
334 if (t != NULL) {
335 *t = h;
336 }
337 return h != NULL;
338}
339
340uint8_t
341lwcell_sys_thread_terminate(lwcell_sys_thread_t* t) {
342 if (t == NULL) { /* Shall we terminate ourself? */
343 ExitThread(0);
344 } else {
345 /* We have known thread, find handle by looking at ID */
346 TerminateThread(*t, 0);
347 }
348 return 1;
349}
350
351uint8_t
352lwcell_sys_thread_yield(void) {
353 /* Not implemented */
354 return 1;
355}
356
357#endif /* !__DOXYGEN__ */
Example: System functions for CMSIS-OS
1/**
2 * \file lwcell_sys_port.h
3 * \brief System dependent functions for CMSIS-OS based operating system
4 */
5
6/*
7 * Copyright (c) 2024 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwCELL - Lightweight cellular modem AT library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v0.1.1
33 */
34#ifndef LWCELL_SYSTEM_PORT_HDR_H
35#define LWCELL_SYSTEM_PORT_HDR_H
36
37#include <stdint.h>
38#include <stdlib.h>
39#include "cmsis_os.h"
40#include "lwcell/lwcell_opt.h"
41
42#ifdef __cplusplus
43extern "C" {
44#endif /* __cplusplus */
45
46#if LWCELL_CFG_OS && !__DOXYGEN__
47
48typedef osMutexId_t lwcell_sys_mutex_t;
49typedef osSemaphoreId_t lwcell_sys_sem_t;
50typedef osMessageQueueId_t lwcell_sys_mbox_t;
51typedef osThreadId_t lwcell_sys_thread_t;
52typedef osPriority_t lwcell_sys_thread_prio_t;
53
54#define LWCELL_SYS_MUTEX_NULL ((lwcell_sys_mutex_t)0)
55#define LWCELL_SYS_SEM_NULL ((lwcell_sys_sem_t)0)
56#define LWCELL_SYS_MBOX_NULL ((lwcell_sys_mbox_t)0)
57#define LWCELL_SYS_TIMEOUT ((uint32_t)osWaitForever)
58#define LWCELL_SYS_THREAD_PRIO (osPriorityNormal)
59#define LWCELL_SYS_THREAD_SS (512)
60
61#endif /* LWCELL_CFG_OS && !__DOXYGEN__ */
62
63#ifdef __cplusplus
64}
65#endif /* __cplusplus */
66
67#endif /* LWCELL_SYSTEM_PORT_HDR_H */
1/**
2 * \file lwcell_sys_cmsis_os.c
3 * \brief System dependent functions for CMSIS-OS based operating system
4 */
5
6/*
7 * Copyright (c) 2024 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwCELL - Lightweight cellular modem AT library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v0.1.1
33 */
34#include "cmsis_os.h"
35#include "system/lwcell_sys.h"
36
37#if !__DOXYGEN__
38
39static osMutexId_t sys_mutex;
40
41uint8_t
42lwcell_sys_init(void) {
43 lwcell_sys_mutex_create(&sys_mutex);
44 return 1;
45}
46
47uint32_t
48lwcell_sys_now(void) {
49 return osKernelGetTickCount();
50}
51
52uint8_t
53lwcell_sys_protect(void) {
54 lwcell_sys_mutex_lock(&sys_mutex);
55 return 1;
56}
57
58uint8_t
59lwcell_sys_unprotect(void) {
60 lwcell_sys_mutex_unlock(&sys_mutex);
61 return 1;
62}
63
64uint8_t
65lwcell_sys_mutex_create(lwcell_sys_mutex_t* p) {
66 const osMutexAttr_t attr = {
67 .attr_bits = osMutexRecursive,
68 .name = "lwcell_mutex",
69 };
70 return (*p = osMutexNew(&attr)) != NULL;
71}
72
73uint8_t
74lwcell_sys_mutex_delete(lwcell_sys_mutex_t* p) {
75 return osMutexDelete(*p) == osOK;
76}
77
78uint8_t
79lwcell_sys_mutex_lock(lwcell_sys_mutex_t* p) {
80 return osMutexAcquire(*p, osWaitForever) == osOK;
81}
82
83uint8_t
84lwcell_sys_mutex_unlock(lwcell_sys_mutex_t* p) {
85 return osMutexRelease(*p) == osOK;
86}
87
88uint8_t
89lwcell_sys_mutex_isvalid(lwcell_sys_mutex_t* p) {
90 return p != NULL && *p != NULL;
91}
92
93uint8_t
94lwcell_sys_mutex_invalid(lwcell_sys_mutex_t* p) {
95 *p = LWCELL_SYS_MUTEX_NULL;
96 return 1;
97}
98
99uint8_t
100lwcell_sys_sem_create(lwcell_sys_sem_t* p, uint8_t cnt) {
101 const osSemaphoreAttr_t attr = {
102 .name = "lwcell_sem",
103 };
104 return (*p = osSemaphoreNew(1, cnt > 0 ? 1 : 0, &attr)) != NULL;
105}
106
107uint8_t
108lwcell_sys_sem_delete(lwcell_sys_sem_t* p) {
109 return osSemaphoreDelete(*p) == osOK;
110}
111
112uint32_t
113lwcell_sys_sem_wait(lwcell_sys_sem_t* p, uint32_t timeout) {
114 uint32_t tick = osKernelSysTick();
115 return (osSemaphoreAcquire(*p, timeout == 0 ? osWaitForever : timeout) == osOK) ? (osKernelSysTick() - tick)
116 : LWCELL_SYS_TIMEOUT;
117}
118
119uint8_t
120lwcell_sys_sem_release(lwcell_sys_sem_t* p) {
121 return osSemaphoreRelease(*p) == osOK;
122}
123
124uint8_t
125lwcell_sys_sem_isvalid(lwcell_sys_sem_t* p) {
126 return p != NULL && *p != NULL;
127}
128
129uint8_t
130lwcell_sys_sem_invalid(lwcell_sys_sem_t* p) {
131 *p = LWCELL_SYS_SEM_NULL;
132 return 1;
133}
134
135uint8_t
136lwcell_sys_mbox_create(lwcell_sys_mbox_t* b, size_t size) {
137 const osMessageQueueAttr_t attr = {
138 .name = "lwcell_mbox",
139 };
140 return (*b = osMessageQueueNew(size, sizeof(void*), &attr)) != NULL;
141}
142
143uint8_t
144lwcell_sys_mbox_delete(lwcell_sys_mbox_t* b) {
145 if (osMessageQueueGetCount(*b) > 0) {
146 return 0;
147 }
148 return osMessageQueueDelete(*b) == osOK;
149}
150
151uint32_t
152lwcell_sys_mbox_put(lwcell_sys_mbox_t* b, void* m) {
153 uint32_t tick = osKernelSysTick();
154 return osMessageQueuePut(*b, &m, 0, osWaitForever) == osOK ? (osKernelSysTick() - tick) : LWCELL_SYS_TIMEOUT;
155}
156
157uint32_t
158lwcell_sys_mbox_get(lwcell_sys_mbox_t* b, void** m, uint32_t timeout) {
159 uint32_t tick = osKernelSysTick();
160 return osMessageQueueGet(*b, m, NULL, timeout == 0 ? osWaitForever : timeout) == osOK ? (osKernelSysTick() - tick)
161 : LWCELL_SYS_TIMEOUT;
162}
163
164uint8_t
165lwcell_sys_mbox_putnow(lwcell_sys_mbox_t* b, void* m) {
166 return osMessageQueuePut(*b, &m, 0, 0) == osOK;
167}
168
169uint8_t
170lwcell_sys_mbox_getnow(lwcell_sys_mbox_t* b, void** m) {
171 return osMessageQueueGet(*b, m, NULL, 0) == osOK;
172}
173
174uint8_t
175lwcell_sys_mbox_isvalid(lwcell_sys_mbox_t* b) {
176 return b != NULL && *b != NULL;
177}
178
179uint8_t
180lwcell_sys_mbox_invalid(lwcell_sys_mbox_t* b) {
181 *b = LWCELL_SYS_MBOX_NULL;
182 return 1;
183}
184
185uint8_t
186lwcell_sys_thread_create(lwcell_sys_thread_t* t, const char* name, lwcell_sys_thread_fn thread_func, void* const arg,
187 size_t stack_size, lwcell_sys_thread_prio_t prio) {
188 lwcell_sys_thread_t id;
189 const osThreadAttr_t thread_attr = {.name = (char*)name,
190 .priority = (osPriority)prio,
191 .stack_size = stack_size > 0 ? stack_size : LWCELL_SYS_THREAD_SS};
192
193 id = osThreadNew(thread_func, arg, &thread_attr);
194 if (t != NULL) {
195 *t = id;
196 }
197 return id != NULL;
198}
199
200uint8_t
201lwcell_sys_thread_terminate(lwcell_sys_thread_t* t) {
202 if (t != NULL) {
203 osThreadTerminate(*t);
204 } else {
205 osThreadExit();
206 }
207 return 1;
208}
209
210uint8_t
211lwcell_sys_thread_yield(void) {
212 osThreadYield();
213 return 1;
214}
215
216#endif /* !__DOXYGEN__ */