Porting guide
High level of LwGSM library is platform independent, written in ANSI C99, 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
lwgsm_ll_init()
andlwgsm_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 lwgsm_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
lwgsm/src/system/lwgsm_sys_template.c
to the same folder and rename it to application port, eg.lwgsm_sys_win32.c
Open newly created file and implement all system functions
Copy folder
lwgsm/src/include/system/port/template/*
to the same folder and rename folder name to application port, eg.cmsis_os
Open
lwgsm_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
lwgsm_input_process()
orlwgsm_input()
functions, based on application configuration ofLWGSM_CFG_INPUT_USE_PROCESS
parameter.When
LWGSM_CFG_INPUT_USE_PROCESS
is disabled, dedicated receive buffer is created by LwGSM library andlwgsm_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
LWGSM_CFG_INPUT_USE_PROCESS
is enabled,lwgsm_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 ofLWGSM_MEM_SIZE
sizeIt sets send and reset callback functions for LwGSM library
1/**
2 * \file lwgsm_ll_win32.c
3 * \brief Low-level communication with GSM device for WIN32
4 */
5
6/*
7 * Copyright (c) 2023 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 LwGSM - Lightweight GSM-AT library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v0.1.1
33 */
34#include "lwgsm/lwgsm_types.h"
35#include "lwgsm/lwgsm_input.h"
36#include "lwgsm/lwgsm_mem.h"
37#include "lwgsm/lwgsm_utils.h"
38#include "system/lwgsm_ll.h"
39#include "system/lwgsm_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 !LWGSM_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 /* !LWGSM_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 lwgsm_sys_thread_create(&thread_handle, "lwgsm_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 lwgsm_sys_sem_t sem;
150 FILE* file = NULL;
151
152 LWGSM_UNUSED(param);
153
154 lwgsm_sys_sem_create(&sem, 0); /* Create semaphore for delay functions */
155
156 while (com_port == NULL) {
157 lwgsm_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 LWGSM_CFG_INPUT_USE_PROCESS
179 lwgsm_input_process(data_buffer, (size_t)bytes_read);
180#else /* LWGSM_CFG_INPUT_USE_PROCESS */
181 lwgsm_input(data_buffer, (size_t)bytes_read);
182#endif /* !LWGSM_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 lwgsm_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 LWGSM_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 lwgsm_ll_t structure to fill data for communication functions
207 * \return \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
208 */
209lwgsmr_t
210lwgsm_ll_init(lwgsm_ll_t* ll) {
211#if !LWGSM_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 lwgsm_mem_region_t mem_regions[] = {{memory, sizeof(memory)}};
221 if (!initialized) {
222 lwgsm_mem_assignmemory(mem_regions,
223 LWGSM_ARRAYSIZE(mem_regions)); /* Assign memory for allocations to GSM library */
224 }
225#endif /* !LWGSM_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 lwgsmERR;
235 }
236 initialized = 1;
237 return lwgsmOK;
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
lwgsm_input_process()
function to directly process received data without using intermediate receive bufferMemory manager has been assigned to
1
region ofLWGSM_MEM_SIZE
sizeIt sets send and reset callback functions for LwGSM library
1/**
2 * \file lwgsm_ll_stm32.c
3 * \brief Generic STM32 driver, included in various STM32 driver variants
4 */
5
6/*
7 * Copyright (c) 2023 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 LwGSM - Lightweight GSM-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 lwgsm_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 LWGSM_CFG_INPUT_USE_PROCESS must be enabled in `lwgsm_config.h` to use this driver.
45 */
46#include "lwgsm/lwgsm_types.h"
47#include "lwgsm/lwgsm_input.h"
48#include "lwgsm/lwgsm_mem.h"
49#include "system/lwgsm_ll.h"
50
51#if !__DOXYGEN__
52
53#if !LWGSM_CFG_INPUT_USE_PROCESS
54#error "LWGSM_CFG_INPUT_USE_PROCESS must be enabled in `lwgsm_config.h` to use this driver."
55#endif /* LWGSM_CFG_INPUT_USE_PROCESS */
56
57#if !defined(LWGSM_USART_DMA_RX_BUFF_SIZE)
58#define LWGSM_USART_DMA_RX_BUFF_SIZE 0x1000
59#endif /* !defined(LWGSM_USART_DMA_RX_BUFF_SIZE) */
60
61#if !defined(LWGSM_MEM_SIZE)
62#define LWGSM_MEM_SIZE 0x1000
63#endif /* !defined(LWGSM_MEM_SIZE) */
64
65#if !defined(LWGSM_USART_RDR_NAME)
66#define LWGSM_USART_RDR_NAME RDR
67#endif /* !defined(LWGSM_USART_RDR_NAME) */
68
69/* USART memory */
70static uint8_t usart_mem[LWGSM_USART_DMA_RX_BUFF_SIZE];
71static uint8_t is_running, initialized;
72static size_t old_pos;
73
74/* USART thread */
75static void usart_ll_thread(void* arg);
76static osThreadId_t usart_ll_thread_id;
77
78/* Message queue */
79static osMessageQueueId_t usart_ll_mbox_id;
80
81/**
82 * \brief USART data processing
83 */
84static void
85usart_ll_thread(void* arg) {
86 size_t pos;
87
88 LWGSM_UNUSED(arg);
89
90 while (1) {
91 void* d;
92 /* Wait for the event message from DMA or USART */
93 osMessageQueueGet(usart_ll_mbox_id, &d, NULL, osWaitForever);
94
95 /* Read data */
96#if defined(LWGSM_USART_DMA_RX_STREAM)
97 pos = sizeof(usart_mem) - LL_DMA_GetDataLength(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_STREAM);
98#else
99 pos = sizeof(usart_mem) - LL_DMA_GetDataLength(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_CH);
100#endif /* defined(LWGSM_USART_DMA_RX_STREAM) */
101 if (pos != old_pos && is_running) {
102 if (pos > old_pos) {
103 lwgsm_input_process(&usart_mem[old_pos], pos - old_pos);
104 } else {
105 lwgsm_input_process(&usart_mem[old_pos], sizeof(usart_mem) - old_pos);
106 if (pos > 0) {
107 lwgsm_input_process(&usart_mem[0], pos);
108 }
109 }
110 old_pos = pos;
111 if (old_pos == sizeof(usart_mem)) {
112 old_pos = 0;
113 }
114 }
115 }
116}
117
118/**
119 * \brief Configure UART using DMA for receive in double buffer mode and IDLE line detection
120 */
121static void
122configure_uart(uint32_t baudrate) {
123 static LL_USART_InitTypeDef usart_init;
124 static LL_DMA_InitTypeDef dma_init;
125 LL_GPIO_InitTypeDef gpio_init;
126
127 if (!initialized) {
128 /* Enable peripheral clocks */
129 LWGSM_USART_CLK;
130 LWGSM_USART_DMA_CLK;
131 LWGSM_USART_TX_PORT_CLK;
132 LWGSM_USART_RX_PORT_CLK;
133
134#if defined(LWGSM_RESET_PIN)
135 LWGSM_RESET_PORT_CLK;
136#endif /* defined(LWGSM_RESET_PIN) */
137
138 /* Global pin configuration */
139 LL_GPIO_StructInit(&gpio_init);
140 gpio_init.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
141 gpio_init.Pull = LL_GPIO_PULL_UP;
142 gpio_init.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
143 gpio_init.Mode = LL_GPIO_MODE_OUTPUT;
144
145#if defined(LWGSM_RESET_PIN)
146 /* Configure RESET pin */
147 gpio_init.Pin = LWGSM_RESET_PIN;
148 LL_GPIO_Init(LWGSM_RESET_PORT, &gpio_init);
149#endif /* defined(LWGSM_RESET_PIN) */
150
151 /* Configure USART pins */
152 gpio_init.Mode = LL_GPIO_MODE_ALTERNATE;
153
154 /* TX PIN */
155 gpio_init.Alternate = LWGSM_USART_TX_PIN_AF;
156 gpio_init.Pin = LWGSM_USART_TX_PIN;
157 LL_GPIO_Init(LWGSM_USART_TX_PORT, &gpio_init);
158
159 /* RX PIN */
160 gpio_init.Alternate = LWGSM_USART_RX_PIN_AF;
161 gpio_init.Pin = LWGSM_USART_RX_PIN;
162 LL_GPIO_Init(LWGSM_USART_RX_PORT, &gpio_init);
163
164 /* Configure UART */
165 LL_USART_DeInit(LWGSM_USART);
166 LL_USART_StructInit(&usart_init);
167 usart_init.BaudRate = baudrate;
168 usart_init.DataWidth = LL_USART_DATAWIDTH_8B;
169 usart_init.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
170 usart_init.OverSampling = LL_USART_OVERSAMPLING_16;
171 usart_init.Parity = LL_USART_PARITY_NONE;
172 usart_init.StopBits = LL_USART_STOPBITS_1;
173 usart_init.TransferDirection = LL_USART_DIRECTION_TX_RX;
174 LL_USART_Init(LWGSM_USART, &usart_init);
175
176 /* Enable USART interrupts and DMA request */
177 LL_USART_EnableIT_IDLE(LWGSM_USART);
178 LL_USART_EnableIT_PE(LWGSM_USART);
179 LL_USART_EnableIT_ERROR(LWGSM_USART);
180 LL_USART_EnableDMAReq_RX(LWGSM_USART);
181
182 /* Enable USART interrupts */
183 NVIC_SetPriority(LWGSM_USART_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
184 NVIC_EnableIRQ(LWGSM_USART_IRQ);
185
186 /* Configure DMA */
187 is_running = 0;
188#if defined(LWGSM_USART_DMA_RX_STREAM)
189 LL_DMA_DeInit(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_STREAM);
190 dma_init.Channel = LWGSM_USART_DMA_RX_CH;
191#else
192 LL_DMA_DeInit(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_CH);
193 dma_init.PeriphRequest = LWGSM_USART_DMA_RX_REQ_NUM;
194#endif /* defined(LWGSM_USART_DMA_RX_STREAM) */
195 dma_init.PeriphOrM2MSrcAddress = (uint32_t)&LWGSM_USART->LWGSM_USART_RDR_NAME;
196 dma_init.MemoryOrM2MDstAddress = (uint32_t)usart_mem;
197 dma_init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
198 dma_init.Mode = LL_DMA_MODE_CIRCULAR;
199 dma_init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
200 dma_init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
201 dma_init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
202 dma_init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
203 dma_init.NbData = sizeof(usart_mem);
204 dma_init.Priority = LL_DMA_PRIORITY_MEDIUM;
205#if defined(LWGSM_USART_DMA_RX_STREAM)
206 LL_DMA_Init(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_STREAM, &dma_init);
207#else
208 LL_DMA_Init(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_CH, &dma_init);
209#endif /* defined(LWGSM_USART_DMA_RX_STREAM) */
210
211 /* Enable DMA interrupts */
212#if defined(LWGSM_USART_DMA_RX_STREAM)
213 LL_DMA_EnableIT_HT(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_STREAM);
214 LL_DMA_EnableIT_TC(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_STREAM);
215 LL_DMA_EnableIT_TE(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_STREAM);
216 LL_DMA_EnableIT_FE(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_STREAM);
217 LL_DMA_EnableIT_DME(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_STREAM);
218#else
219 LL_DMA_EnableIT_HT(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_CH);
220 LL_DMA_EnableIT_TC(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_CH);
221 LL_DMA_EnableIT_TE(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_CH);
222#endif /* defined(LWGSM_USART_DMA_RX_STREAM) */
223
224 /* Enable DMA interrupts */
225 NVIC_SetPriority(LWGSM_USART_DMA_RX_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x07, 0x00));
226 NVIC_EnableIRQ(LWGSM_USART_DMA_RX_IRQ);
227
228 old_pos = 0;
229 is_running = 1;
230
231 /* Start DMA and USART */
232#if defined(LWGSM_USART_DMA_RX_STREAM)
233 LL_DMA_EnableStream(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_STREAM);
234#else
235 LL_DMA_EnableChannel(LWGSM_USART_DMA, LWGSM_USART_DMA_RX_CH);
236#endif /* defined(LWGSM_USART_DMA_RX_STREAM) */
237 LL_USART_Enable(LWGSM_USART);
238 } else {
239 osDelay(10);
240 LL_USART_Disable(LWGSM_USART);
241 usart_init.BaudRate = baudrate;
242 LL_USART_Init(LWGSM_USART, &usart_init);
243 LL_USART_Enable(LWGSM_USART);
244 }
245
246 /* Create mbox and start thread */
247 if (usart_ll_mbox_id == NULL) {
248 usart_ll_mbox_id = osMessageQueueNew(10, sizeof(void*), NULL);
249 }
250 if (usart_ll_thread_id == NULL) {
251 const osThreadAttr_t attr = {.stack_size = 1024};
252 usart_ll_thread_id = osThreadNew(usart_ll_thread, usart_ll_mbox_id, &attr);
253 }
254}
255
256#if defined(LWGSM_RESET_PIN)
257/**
258 * \brief Hardware reset callback
259 */
260static uint8_t
261reset_device(uint8_t state) {
262 if (state) { /* Activate reset line */
263 LL_GPIO_ResetOutputPin(LWGSM_RESET_PORT, LWGSM_RESET_PIN);
264 } else {
265 LL_GPIO_SetOutputPin(LWGSM_RESET_PORT, LWGSM_RESET_PIN);
266 }
267 return 1;
268}
269#endif /* defined(LWGSM_RESET_PIN) */
270
271/**
272 * \brief Send data to GSM device
273 * \param[in] data: Pointer to data to send
274 * \param[in] len: Number of bytes to send
275 * \return Number of bytes sent
276 */
277static size_t
278send_data(const void* data, size_t len) {
279 const uint8_t* d = data;
280
281 for (size_t i = 0; i < len; ++i, ++d) {
282 LL_USART_TransmitData8(LWGSM_USART, *d);
283 while (!LL_USART_IsActiveFlag_TXE(LWGSM_USART)) {}
284 }
285 return len;
286}
287
288/**
289 * \brief Callback function called from initialization process
290 * \note This function may be called multiple times if AT baudrate is changed from application
291 * \param[in,out] ll: Pointer to \ref lwgsm_ll_t structure to fill data for communication functions
292 * \param[in] baudrate: Baudrate to use on AT port
293 * \return Member of \ref lwgsmr_t enumeration
294 */
295lwgsmr_t
296lwgsm_ll_init(lwgsm_ll_t* ll) {
297#if !LWGSM_CFG_MEM_CUSTOM
298 static uint8_t memory[LWGSM_MEM_SIZE];
299 lwgsm_mem_region_t mem_regions[] = {{memory, sizeof(memory)}};
300
301 if (!initialized) {
302 lwgsm_mem_assignmemory(mem_regions, LWGSM_ARRAYSIZE(mem_regions)); /* Assign memory for allocations */
303 }
304#endif /* !LWGSM_CFG_MEM_CUSTOM */
305
306 if (!initialized) {
307 ll->send_fn = send_data; /* Set callback function to send data */
308#if defined(LWGSM_RESET_PIN)
309 ll->reset_fn = reset_device; /* Set callback for hardware reset */
310#endif /* defined(LWGSM_RESET_PIN) */
311 }
312
313 configure_uart(ll->uart.baudrate); /* Initialize UART for communication */
314 initialized = 1;
315 return lwgsmOK;
316}
317
318/**
319 * \brief Callback function to de-init low-level communication part
320 * \param[in,out] ll: Pointer to \ref lwgsm_ll_t structure to fill data for communication functions
321 * \return \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
322 */
323lwgsmr_t
324lwgsm_ll_deinit(lwgsm_ll_t* ll) {
325 if (usart_ll_mbox_id != NULL) {
326 osMessageQueueId_t tmp = usart_ll_mbox_id;
327 usart_ll_mbox_id = NULL;
328 osMessageQueueDelete(tmp);
329 }
330 if (usart_ll_thread_id != NULL) {
331 osThreadId_t tmp = usart_ll_thread_id;
332 usart_ll_thread_id = NULL;
333 osThreadTerminate(tmp);
334 }
335 initialized = 0;
336 LWGSM_UNUSED(ll);
337 return lwgsmOK;
338}
339
340/**
341 * \brief UART global interrupt handler
342 */
343void
344LWGSM_USART_IRQHANDLER(void) {
345 LL_USART_ClearFlag_IDLE(LWGSM_USART);
346 LL_USART_ClearFlag_PE(LWGSM_USART);
347 LL_USART_ClearFlag_FE(LWGSM_USART);
348 LL_USART_ClearFlag_ORE(LWGSM_USART);
349 LL_USART_ClearFlag_NE(LWGSM_USART);
350
351 if (usart_ll_mbox_id != NULL) {
352 void* d = (void*)1;
353 osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
354 }
355}
356
357/**
358 * \brief UART DMA stream/channel handler
359 */
360void
361LWGSM_USART_DMA_RX_IRQHANDLER(void) {
362 LWGSM_USART_DMA_RX_CLEAR_TC;
363 LWGSM_USART_DMA_RX_CLEAR_HT;
364
365 if (usart_ll_mbox_id != NULL) {
366 void* d = (void*)1;
367 osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
368 }
369}
370
371#endif /* !__DOXYGEN__ */
Example: System functions for WIN32
1/**
2 * \file lwgsm_sys_port.h
3 * \brief WIN32 based system file implementation
4 */
5
6/*
7 * Copyright (c) 2023 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 LwGSM - Lightweight GSM-AT library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v0.1.1
33 */
34#ifndef LWGSM_SYSTEM_PORT_HDR_H
35#define LWGSM_SYSTEM_PORT_HDR_H
36
37#include <stdint.h>
38#include <stdlib.h>
39#include "lwgsm/lwgsm_opt.h"
40#include "windows.h"
41
42#ifdef __cplusplus
43extern "C" {
44#endif /* __cplusplus */
45
46#if LWGSM_CFG_OS && !__DOXYGEN__
47
48typedef HANDLE lwgsm_sys_mutex_t;
49typedef HANDLE lwgsm_sys_sem_t;
50typedef HANDLE lwgsm_sys_mbox_t;
51typedef HANDLE lwgsm_sys_thread_t;
52typedef int lwgsm_sys_thread_prio_t;
53
54#define LWGSM_SYS_MUTEX_NULL ((HANDLE)0)
55#define LWGSM_SYS_SEM_NULL ((HANDLE)0)
56#define LWGSM_SYS_MBOX_NULL ((HANDLE)0)
57#define LWGSM_SYS_TIMEOUT (INFINITE)
58#define LWGSM_SYS_THREAD_PRIO (0)
59#define LWGSM_SYS_THREAD_SS (4096)
60
61#endif /* LWGSM_CFG_OS && !__DOXYGEN__ */
62
63#ifdef __cplusplus
64}
65#endif /* __cplusplus */
66
67#endif /* LWGSM_SYSTEM_PORT_HDR_H */
1/**
2 * \file lwgsm_sys_win32.c
3 * \brief System dependant functions for WIN32
4 */
5
6/*
7 * Copyright (c) 2023 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 LwGSM - Lightweight GSM-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 "lwgsm/lwgsm_private.h"
37#include "system/lwgsm_sys.h"
38
39#if !__DOXYGEN__
40
41/**
42 * \brief Custom message queue implementation for WIN32
43 */
44typedef struct {
45 lwgsm_sys_sem_t sem_not_empty; /*!< Semaphore indicates not empty */
46 lwgsm_sys_sem_t sem_not_full; /*!< Semaphore indicates not full */
47 lwgsm_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 lwgsm_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
83lwgsm_sys_init(void) {
84 QueryPerformanceFrequency(&freq);
85 QueryPerformanceCounter(&sys_start_time);
86
87 lwgsm_sys_mutex_create(&sys_mutex);
88 return 1;
89}
90
91uint32_t
92lwgsm_sys_now(void) {
93 return osKernelSysTick();
94}
95
96uint8_t
97lwgsm_sys_protect(void) {
98 lwgsm_sys_mutex_lock(&sys_mutex);
99 return 1;
100}
101
102uint8_t
103lwgsm_sys_unprotect(void) {
104 lwgsm_sys_mutex_unlock(&sys_mutex);
105 return 1;
106}
107
108uint8_t
109lwgsm_sys_mutex_create(lwgsm_sys_mutex_t* p) {
110 *p = CreateMutex(NULL, FALSE, NULL);
111 return *p != NULL;
112}
113
114uint8_t
115lwgsm_sys_mutex_delete(lwgsm_sys_mutex_t* p) {
116 return CloseHandle(*p);
117}
118
119uint8_t
120lwgsm_sys_mutex_lock(lwgsm_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
130lwgsm_sys_mutex_unlock(lwgsm_sys_mutex_t* p) {
131 return (uint8_t)ReleaseMutex(*p);
132}
133
134uint8_t
135lwgsm_sys_mutex_isvalid(lwgsm_sys_mutex_t* p) {
136 return p != NULL && *p != NULL;
137}
138
139uint8_t
140lwgsm_sys_mutex_invalid(lwgsm_sys_mutex_t* p) {
141 *p = LWGSM_SYS_MUTEX_NULL;
142 return 1;
143}
144
145uint8_t
146lwgsm_sys_sem_create(lwgsm_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
154lwgsm_sys_sem_delete(lwgsm_sys_sem_t* p) {
155 return CloseHandle(*p);
156}
157
158uint32_t
159lwgsm_sys_sem_wait(lwgsm_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 LWGSM_SYS_TIMEOUT;
171 }
172 }
173}
174
175uint8_t
176lwgsm_sys_sem_release(lwgsm_sys_sem_t* p) {
177 return ReleaseSemaphore(*p, 1, NULL);
178}
179
180uint8_t
181lwgsm_sys_sem_isvalid(lwgsm_sys_sem_t* p) {
182 return p != NULL && *p != NULL;
183}
184
185uint8_t
186lwgsm_sys_sem_invalid(lwgsm_sys_sem_t* p) {
187 *p = LWGSM_SYS_SEM_NULL;
188 return 1;
189}
190
191uint8_t
192lwgsm_sys_mbox_create(lwgsm_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 lwgsm_sys_sem_create(&mbox->sem, 1);
202 lwgsm_sys_sem_create(&mbox->sem_not_empty, 0);
203 lwgsm_sys_sem_create(&mbox->sem_not_full, 0);
204 *b = mbox;
205 }
206 return *b != NULL;
207}
208
209uint8_t
210lwgsm_sys_mbox_delete(lwgsm_sys_mbox_t* b) {
211 win32_mbox_t* mbox = *b;
212 lwgsm_sys_sem_delete(&mbox->sem);
213 lwgsm_sys_sem_delete(&mbox->sem_not_full);
214 lwgsm_sys_sem_delete(&mbox->sem_not_empty);
215 free(mbox);
216 return 1;
217}
218
219uint32_t
220lwgsm_sys_mbox_put(lwgsm_sys_mbox_t* b, void* m) {
221 win32_mbox_t* mbox = *b;
222 uint32_t time = osKernelSysTick(); /* Get start time */
223
224 lwgsm_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 lwgsm_sys_sem_release(&mbox->sem); /* Release semaphore */
233 lwgsm_sys_sem_wait(&mbox->sem_not_full, 0); /* Wait for semaphore indicating not full */
234 lwgsm_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 lwgsm_sys_sem_release(&mbox->sem_not_empty); /* Signal non-empty state */
241 lwgsm_sys_sem_release(&mbox->sem); /* Release access for other threads */
242 return osKernelSysTick() - time;
243}
244
245uint32_t
246lwgsm_sys_mbox_get(lwgsm_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 (lwgsm_sys_sem_wait(&mbox->sem, timeout) == LWGSM_SYS_TIMEOUT) {
254 return LWGSM_SYS_TIMEOUT;
255 }
256 while (mbox_is_empty(mbox)) {
257 lwgsm_sys_sem_release(&mbox->sem);
258 if (lwgsm_sys_sem_wait(&mbox->sem_not_empty, timeout) == LWGSM_SYS_TIMEOUT) {
259 return LWGSM_SYS_TIMEOUT;
260 }
261 lwgsm_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 lwgsm_sys_sem_release(&mbox->sem_not_full);
268 lwgsm_sys_sem_release(&mbox->sem);
269
270 return osKernelSysTick() - time;
271}
272
273uint8_t
274lwgsm_sys_mbox_putnow(lwgsm_sys_mbox_t* b, void* m) {
275 win32_mbox_t* mbox = *b;
276
277 lwgsm_sys_sem_wait(&mbox->sem, 0);
278 if (mbox_is_full(mbox)) {
279 lwgsm_sys_sem_release(&mbox->sem);
280 return 0;
281 }
282 mbox->entries[mbox->in] = m;
283 if (mbox->in == mbox->out) {
284 lwgsm_sys_sem_release(&mbox->sem_not_empty);
285 }
286 if (++mbox->in >= mbox->size) {
287 mbox->in = 0;
288 }
289 lwgsm_sys_sem_release(&mbox->sem);
290 return 1;
291}
292
293uint8_t
294lwgsm_sys_mbox_getnow(lwgsm_sys_mbox_t* b, void** m) {
295 win32_mbox_t* mbox = *b;
296
297 lwgsm_sys_sem_wait(&mbox->sem, 0); /* Wait exclusive access */
298 if (mbox->in == mbox->out) {
299 lwgsm_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 lwgsm_sys_sem_release(&mbox->sem_not_full); /* Queue not full anymore */
308 lwgsm_sys_sem_release(&mbox->sem); /* Release semaphore */
309 return 1;
310}
311
312uint8_t
313lwgsm_sys_mbox_isvalid(lwgsm_sys_mbox_t* b) {
314 return b != NULL && *b != NULL; /* Return status if message box is valid */
315}
316
317uint8_t
318lwgsm_sys_mbox_invalid(lwgsm_sys_mbox_t* b) {
319 *b = LWGSM_SYS_MBOX_NULL; /* Invalidate message box */
320 return 1;
321}
322
323uint8_t
324lwgsm_sys_thread_create(lwgsm_sys_thread_t* t, const char* name, lwgsm_sys_thread_fn thread_func, void* const arg,
325 size_t stack_size, lwgsm_sys_thread_prio_t prio) {
326 HANDLE h;
327 DWORD id;
328
329 LWGSM_UNUSED(name);
330 LWGSM_UNUSED(stack_size);
331 LWGSM_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
341lwgsm_sys_thread_terminate(lwgsm_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
352lwgsm_sys_thread_yield(void) {
353 /* Not implemented */
354 return 1;
355}
356
357#endif /* !__DOXYGEN__ */
Example: System functions for CMSIS-OS
1/**
2 * \file lwgsm_sys_port.h
3 * \brief System dependent functions for CMSIS-OS based operating system
4 */
5
6/*
7 * Copyright (c) 2023 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 LwGSM - Lightweight GSM-AT library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v0.1.1
33 */
34#ifndef LWGSM_SYSTEM_PORT_HDR_H
35#define LWGSM_SYSTEM_PORT_HDR_H
36
37#include <stdint.h>
38#include <stdlib.h>
39#include "cmsis_os.h"
40#include "lwgsm/lwgsm_opt.h"
41
42#ifdef __cplusplus
43extern "C" {
44#endif /* __cplusplus */
45
46#if LWGSM_CFG_OS && !__DOXYGEN__
47
48typedef osMutexId_t lwgsm_sys_mutex_t;
49typedef osSemaphoreId_t lwgsm_sys_sem_t;
50typedef osMessageQueueId_t lwgsm_sys_mbox_t;
51typedef osThreadId_t lwgsm_sys_thread_t;
52typedef osPriority_t lwgsm_sys_thread_prio_t;
53
54#define LWGSM_SYS_MUTEX_NULL ((lwgsm_sys_mutex_t)0)
55#define LWGSM_SYS_SEM_NULL ((lwgsm_sys_sem_t)0)
56#define LWGSM_SYS_MBOX_NULL ((lwgsm_sys_mbox_t)0)
57#define LWGSM_SYS_TIMEOUT ((uint32_t)osWaitForever)
58#define LWGSM_SYS_THREAD_PRIO (osPriorityNormal)
59#define LWGSM_SYS_THREAD_SS (512)
60
61#endif /* LWGSM_CFG_OS && !__DOXYGEN__ */
62
63#ifdef __cplusplus
64}
65#endif /* __cplusplus */
66
67#endif /* LWGSM_SYSTEM_PORT_HDR_H */
1/**
2 * \file lwgsm_sys_cmsis_os.c
3 * \brief System dependent functions for CMSIS-OS based operating system
4 */
5
6/*
7 * Copyright (c) 2023 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 LwGSM - Lightweight GSM-AT library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v0.1.1
33 */
34#include "cmsis_os.h"
35#include "system/lwgsm_sys.h"
36
37#if !__DOXYGEN__
38
39static osMutexId_t sys_mutex;
40
41uint8_t
42lwgsm_sys_init(void) {
43 lwgsm_sys_mutex_create(&sys_mutex);
44 return 1;
45}
46
47uint32_t
48lwgsm_sys_now(void) {
49 return osKernelGetTickCount();
50}
51
52uint8_t
53lwgsm_sys_protect(void) {
54 lwgsm_sys_mutex_lock(&sys_mutex);
55 return 1;
56}
57
58uint8_t
59lwgsm_sys_unprotect(void) {
60 lwgsm_sys_mutex_unlock(&sys_mutex);
61 return 1;
62}
63
64uint8_t
65lwgsm_sys_mutex_create(lwgsm_sys_mutex_t* p) {
66 const osMutexAttr_t attr = {
67 .attr_bits = osMutexRecursive,
68 .name = "lwgsm_mutex",
69 };
70 return (*p = osMutexNew(&attr)) != NULL;
71}
72
73uint8_t
74lwgsm_sys_mutex_delete(lwgsm_sys_mutex_t* p) {
75 return osMutexDelete(*p) == osOK;
76}
77
78uint8_t
79lwgsm_sys_mutex_lock(lwgsm_sys_mutex_t* p) {
80 return osMutexAcquire(*p, osWaitForever) == osOK;
81}
82
83uint8_t
84lwgsm_sys_mutex_unlock(lwgsm_sys_mutex_t* p) {
85 return osMutexRelease(*p) == osOK;
86}
87
88uint8_t
89lwgsm_sys_mutex_isvalid(lwgsm_sys_mutex_t* p) {
90 return p != NULL && *p != NULL;
91}
92
93uint8_t
94lwgsm_sys_mutex_invalid(lwgsm_sys_mutex_t* p) {
95 *p = LWGSM_SYS_MUTEX_NULL;
96 return 1;
97}
98
99uint8_t
100lwgsm_sys_sem_create(lwgsm_sys_sem_t* p, uint8_t cnt) {
101 const osSemaphoreAttr_t attr = {
102 .name = "lwgsm_sem",
103 };
104 return (*p = osSemaphoreNew(1, cnt > 0 ? 1 : 0, &attr)) != NULL;
105}
106
107uint8_t
108lwgsm_sys_sem_delete(lwgsm_sys_sem_t* p) {
109 return osSemaphoreDelete(*p) == osOK;
110}
111
112uint32_t
113lwgsm_sys_sem_wait(lwgsm_sys_sem_t* p, uint32_t timeout) {
114 uint32_t tick = osKernelSysTick();
115 return (osSemaphoreAcquire(*p, timeout == 0 ? osWaitForever : timeout) == osOK) ? (osKernelSysTick() - tick)
116 : LWGSM_SYS_TIMEOUT;
117}
118
119uint8_t
120lwgsm_sys_sem_release(lwgsm_sys_sem_t* p) {
121 return osSemaphoreRelease(*p) == osOK;
122}
123
124uint8_t
125lwgsm_sys_sem_isvalid(lwgsm_sys_sem_t* p) {
126 return p != NULL && *p != NULL;
127}
128
129uint8_t
130lwgsm_sys_sem_invalid(lwgsm_sys_sem_t* p) {
131 *p = LWGSM_SYS_SEM_NULL;
132 return 1;
133}
134
135uint8_t
136lwgsm_sys_mbox_create(lwgsm_sys_mbox_t* b, size_t size) {
137 const osMessageQueueAttr_t attr = {
138 .name = "lwgsm_mbox",
139 };
140 return (*b = osMessageQueueNew(size, sizeof(void*), &attr)) != NULL;
141}
142
143uint8_t
144lwgsm_sys_mbox_delete(lwgsm_sys_mbox_t* b) {
145 if (osMessageQueueGetCount(*b) > 0) {
146 return 0;
147 }
148 return osMessageQueueDelete(*b) == osOK;
149}
150
151uint32_t
152lwgsm_sys_mbox_put(lwgsm_sys_mbox_t* b, void* m) {
153 uint32_t tick = osKernelSysTick();
154 return osMessageQueuePut(*b, &m, 0, osWaitForever) == osOK ? (osKernelSysTick() - tick) : LWGSM_SYS_TIMEOUT;
155}
156
157uint32_t
158lwgsm_sys_mbox_get(lwgsm_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 : LWGSM_SYS_TIMEOUT;
162}
163
164uint8_t
165lwgsm_sys_mbox_putnow(lwgsm_sys_mbox_t* b, void* m) {
166 return osMessageQueuePut(*b, &m, 0, 0) == osOK;
167}
168
169uint8_t
170lwgsm_sys_mbox_getnow(lwgsm_sys_mbox_t* b, void** m) {
171 return osMessageQueueGet(*b, m, NULL, 0) == osOK;
172}
173
174uint8_t
175lwgsm_sys_mbox_isvalid(lwgsm_sys_mbox_t* b) {
176 return b != NULL && *b != NULL;
177}
178
179uint8_t
180lwgsm_sys_mbox_invalid(lwgsm_sys_mbox_t* b) {
181 *b = LWGSM_SYS_MBOX_NULL;
182 return 1;
183}
184
185uint8_t
186lwgsm_sys_thread_create(lwgsm_sys_thread_t* t, const char* name, lwgsm_sys_thread_fn thread_func, void* const arg,
187 size_t stack_size, lwgsm_sys_thread_prio_t prio) {
188 lwgsm_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 : LWGSM_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
201lwgsm_sys_thread_terminate(lwgsm_sys_thread_t* t) {
202 if (t != NULL) {
203 osThreadTerminate(*t);
204 } else {
205 osThreadExit();
206 }
207 return 1;
208}
209
210uint8_t
211lwgsm_sys_thread_yield(void) {
212 osThreadYield();
213 return 1;
214}
215
216#endif /* !__DOXYGEN__ */