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() and lwgsm_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 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 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:

  • It uses separate thread for received data processing. It uses lwgsm_input_process() or lwgsm_input() functions, based on application configuration of LWGSM_CFG_INPUT_USE_PROCESS parameter.

    • When LWGSM_CFG_INPUT_USE_PROCESS is disabled, dedicated receive buffer is created by LwGSM library and lwgsm_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 of LWGSM_MEM_SIZE size

  • It sets send and reset callback functions for LwGSM library

Actual implementation of low-level driver for WIN32
  1/**
  2 * \file            lwgsm_ll_win32.c
  3 * \brief           Low-level communication with GSM device for WIN32
  4 */
  5
  6/*
  7 * Copyright (c) 2022 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 buffer

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

  • It sets send and reset callback functions for LwGSM library

Actual implementation of low-level driver for STM32
  1/**
  2 * \file            lwgsm_ll_stm32.c
  3 * \brief           Generic STM32 driver, included in various STM32 driver variants
  4 */
  5
  6/*
  7 * Copyright (c) 2022 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

Actual header implementation of system functions for WIN32
 1/**
 2 * \file            lwgsm_sys_port.h
 3 * \brief           WIN32 based system file implementation
 4 */
 5
 6/*
 7 * Copyright (c) 2022 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_HDR_SYSTEM_PORT_H
35#define LWGSM_HDR_SYSTEM_PORT_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_HDR_SYSTEM_PORT_H */
Actual implementation of system functions for WIN32
  1/**
  2 * \file            lwgsm_sys_win32.c
  3 * \brief           System dependant functions for WIN32
  4 */
  5
  6/*
  7 * Copyright (c) 2022 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

Actual header implementation of system functions for CMSIS-OS based operating systems
 1/**
 2 * \file            lwgsm_sys_port.h
 3 * \brief           System dependent functions for CMSIS-OS based operating system
 4 */
 5
 6/*
 7 * Copyright (c) 2022 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_HDR_SYSTEM_PORT_H
35#define LWGSM_HDR_SYSTEM_PORT_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_HDR_SYSTEM_PORT_H */
Actual implementation of system functions for CMSIS-OS based operating systems
  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) 2022 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__ */