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() and lwcell_ll_deinit() callback functions

  • Implement 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 target

  • Add 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:

Actual implementation of low-level driver for WIN32
  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 buffer

  • Memory manager has been assigned to 1 region of LWCELL_MEM_SIZE size

  • It sets send and reset callback functions for LwCELL library

Actual implementation of low-level driver for STM32
  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

Actual header implementation of 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 */
Actual implementation of system functions for WIN32
  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

Actual header implementation of system functions for CMSIS-OS based operating systems
 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 */
Actual implementation of system functions for CMSIS-OS based operating systems
  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__ */