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 "system/lwgsm_ll.h"
 35#include "lwgsm/lwgsm.h"
 36#include "lwgsm/lwgsm_mem.h"
 37#include "lwgsm/lwgsm_input.h"
 38
 39#if !__DOXYGEN__
 40
 41static uint8_t initialized = 0;
 42static HANDLE thread_handle;
 43static volatile HANDLE com_port;                /*!< COM port handle */
 44static uint8_t data_buffer[0x1000];             /*!< Received data array */
 45
 46static void uart_thread(void* param);
 47
 48/**
 49 * \brief           Send data to GSM device, function called from GSM stack when we have data to send
 50 * \param[in]       data: Pointer to data to send
 51 * \param[in]       len: Number of bytes to send
 52 * \return          Number of bytes sent
 53 */
 54static size_t
 55send_data(const void* data, size_t len) {
 56    DWORD written;
 57    if (com_port != NULL) {
 58#if !LWGSM_CFG_AT_ECHO
 59        const uint8_t* d = data;
 60        HANDLE hConsole;
 61
 62        hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
 63        SetConsoleTextAttribute(hConsole, FOREGROUND_RED);
 64        for (DWORD i = 0; i < len; ++i) {
 65            printf("%c", d[i]);
 66        }
 67        SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
 68#endif /* !LWGSM_CFG_AT_ECHO */
 69
 70        /* Write data to AT port */
 71        WriteFile(com_port, data, len, &written, NULL);
 72        FlushFileBuffers(com_port);
 73        return written;
 74    }
 75    return 0;
 76}
 77
 78/**
 79 * \brief           Configure UART (USB to UART)
 80 */
 81static uint8_t
 82configure_uart(uint32_t baudrate) {
 83    DCB dcb = { 0 };
 84    dcb.DCBlength = sizeof(dcb);
 85
 86    /*
 87     * On first call,
 88     * create virtual file on selected COM port and open it
 89     * as generic read and write
 90     */
 91    if (!initialized) {
 92        static const char* com_ports[] = {
 93            "\\\\.\\COM23",
 94            "\\\\.\\COM12",
 95            "\\\\.\\COM9",
 96            "\\\\.\\COM8",
 97            "\\\\.\\COM4"
 98        };
 99        for (size_t i = 0; i < sizeof(com_ports) / sizeof(com_ports[0]); ++i) {
100            com_port = CreateFileA(com_ports[i], GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);
101            if (GetCommState(com_port, &dcb)) {
102                printf("COM PORT %s opened!\r\n", (const char*)com_ports[i]);
103                break;
104            }
105        }
106    }
107
108    /* Configure COM port parameters */
109    if (GetCommState(com_port, &dcb)) {
110        COMMTIMEOUTS timeouts;
111
112        dcb.BaudRate = baudrate;
113        dcb.ByteSize = 8;
114        dcb.Parity = NOPARITY;
115        dcb.StopBits = ONESTOPBIT;
116
117        if (!SetCommState(com_port, &dcb)) {
118            printf("Cannot set COM PORT info\r\n");
119            return 0;
120        }
121        if (GetCommTimeouts(com_port, &timeouts)) {
122            /* Set timeout to return immediately from ReadFile function */
123            timeouts.ReadIntervalTimeout = MAXDWORD;
124            timeouts.ReadTotalTimeoutConstant = 0;
125            timeouts.ReadTotalTimeoutMultiplier = 0;
126            if (!SetCommTimeouts(com_port, &timeouts)) {
127                printf("Cannot set COM PORT timeouts\r\n");
128            }
129            GetCommTimeouts(com_port, &timeouts);
130        } else {
131            printf("Cannot get COM PORT timeouts\r\n");
132            return 0;
133        }
134    } else {
135        printf("Cannot get COM PORT info\r\n");
136        return 0;
137    }
138
139    /* On first function call, create a thread to read data from COM port */
140    if (!initialized) {
141        lwgsm_sys_thread_create(&thread_handle, "lwgsm_ll_thread", uart_thread, NULL, 0, 0);
142    }
143    return 1;
144}
145
146/**
147 * \brief            UART thread
148 */
149static void
150uart_thread(void* param) {
151    DWORD bytes_read;
152    lwgsm_sys_sem_t sem;
153    FILE* file = NULL;
154    
155    LWGSM_UNUSED(param);
156
157    lwgsm_sys_sem_create(&sem, 0);              /* Create semaphore for delay functions */
158
159    while (com_port == NULL) {
160        lwgsm_sys_sem_wait(&sem, 1);            /* Add some delay with yield */
161    }
162
163    fopen_s(&file, "log_file.txt", "w+");       /* Open debug file in write mode */
164    while (1) {
165        /*
166         * Try to read data from COM port
167         * and send it to upper layer for processing
168         */
169        do {
170            ReadFile(com_port, data_buffer, sizeof(data_buffer), &bytes_read, NULL);
171            if (bytes_read > 0) {
172                HANDLE hConsole;
173                hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
174                SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);
175                for (DWORD i = 0; i < bytes_read; ++i) {
176                    printf("%c", data_buffer[i]);
177                }
178                SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
179
180                /* Send received data to input processing module */
181#if LWGSM_CFG_INPUT_USE_PROCESS
182                lwgsm_input_process(data_buffer, (size_t)bytes_read);
183#else /* LWGSM_CFG_INPUT_USE_PROCESS */
184                lwgsm_input(data_buffer, (size_t)bytes_read);
185#endif /* !LWGSM_CFG_INPUT_USE_PROCESS */
186
187                /* Write received data to output debug file */
188                if (file != NULL) {
189                    fwrite(data_buffer, 1, bytes_read, file);
190                    fflush(file);
191                }
192            }
193        } while (bytes_read == (DWORD)sizeof(data_buffer));
194
195        /* Implement delay to allow other tasks processing */
196        lwgsm_sys_sem_wait(&sem, 1);
197    }
198}
199
200/**
201 * \brief           Callback function called from initialization process
202 *
203 * \note            This function may be called multiple times if AT baudrate is changed from application.
204 *                  It is important that every configuration except AT baudrate is configured only once!
205 *
206 * \note            This function may be called from different threads in GSM stack when using OS.
207 *                  When \ref LWGSM_CFG_INPUT_USE_PROCESS is set to 1, this function may be called from user UART thread.
208 *
209 * \param[in,out]   ll: Pointer to \ref lwgsm_ll_t structure to fill data for communication functions
210 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
211 */
212lwgsmr_t
213lwgsm_ll_init(lwgsm_ll_t* ll) {
214#if !LWGSM_CFG_MEM_CUSTOM
215    /* Step 1: Configure memory for dynamic allocations */
216    static uint8_t memory[0x10000];             /* Create memory for dynamic allocations with specific size */
217
218    /*
219     * Create memory region(s) of memory.
220     * If device has internal/external memory available,
221     * multiple memories may be used
222     */
223    lwgsm_mem_region_t mem_regions[] = {
224        { memory, sizeof(memory) }
225    };
226    if (!initialized) {
227        lwgsm_mem_assignmemory(mem_regions, LWGSM_ARRAYSIZE(mem_regions));  /* Assign memory for allocations to GSM library */
228    }
229#endif /* !LWGSM_CFG_MEM_CUSTOM */
230
231    /* Step 2: Set AT port send function to use when we have data to transmit */
232    if (!initialized) {
233        ll->send_fn = send_data;                /* Set callback function to send data */
234    }
235
236    /* Step 3: Configure AT port to be able to send/receive data to/from GSM device */
237    if (!configure_uart(ll->uart.baudrate)) {   /* Initialize UART for communication */
238        return lwgsmERR;
239    }
240    initialized = 1;
241    return lwgsmOK;
242}
243
244#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.h"
 47#include "lwgsm/lwgsm_mem.h"
 48#include "lwgsm/lwgsm_input.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 = {
252            .stack_size = 1024
253        };
254        usart_ll_thread_id = osThreadNew(usart_ll_thread, usart_ll_mbox_id, &attr);
255    }
256}
257
258#if defined(LWGSM_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(LWGSM_RESET_PORT, LWGSM_RESET_PIN);
266    } else {
267        LL_GPIO_SetOutputPin(LWGSM_RESET_PORT, LWGSM_RESET_PIN);
268    }
269    return 1;
270}
271#endif /* defined(LWGSM_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(LWGSM_USART, *d);
285        while (!LL_USART_IsActiveFlag_TXE(LWGSM_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 lwgsm_ll_t structure to fill data for communication functions
294 * \param[in]       baudrate: Baudrate to use on AT port
295 * \return          Member of \ref lwgsmr_t enumeration
296 */
297lwgsmr_t
298lwgsm_ll_init(lwgsm_ll_t* ll) {
299#if !LWGSM_CFG_MEM_CUSTOM
300    static uint8_t memory[LWGSM_MEM_SIZE];
301    lwgsm_mem_region_t mem_regions[] = {
302        { memory, sizeof(memory) }
303    };
304
305    if (!initialized) {
306        lwgsm_mem_assignmemory(mem_regions, LWGSM_ARRAYSIZE(mem_regions));  /* Assign memory for allocations */
307    }
308#endif /* !LWGSM_CFG_MEM_CUSTOM */
309
310    if (!initialized) {
311        ll->send_fn = send_data;                /* Set callback function to send data */
312#if defined(LWGSM_RESET_PIN)
313        ll->reset_fn = reset_device;            /* Set callback for hardware reset */
314#endif /* defined(LWGSM_RESET_PIN) */
315    }
316
317    configure_uart(ll->uart.baudrate);          /* Initialize UART for communication */
318    initialized = 1;
319    return lwgsmOK;
320}
321
322/**
323 * \brief           Callback function to de-init low-level communication part
324 * \param[in,out]   ll: Pointer to \ref lwgsm_ll_t structure to fill data for communication functions
325 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
326 */
327lwgsmr_t
328lwgsm_ll_deinit(lwgsm_ll_t* ll) {
329    if (usart_ll_mbox_id != NULL) {
330        osMessageQueueId_t tmp = usart_ll_mbox_id;
331        usart_ll_mbox_id = NULL;
332        osMessageQueueDelete(tmp);
333    }
334    if (usart_ll_thread_id != NULL) {
335        osThreadId_t tmp = usart_ll_thread_id;
336        usart_ll_thread_id = NULL;
337        osThreadTerminate(tmp);
338    }
339    initialized = 0;
340    LWGSM_UNUSED(ll);
341    return lwgsmOK;
342}
343
344/**
345 * \brief           UART global interrupt handler
346 */
347void
348LWGSM_USART_IRQHANDLER(void) {
349    LL_USART_ClearFlag_IDLE(LWGSM_USART);
350    LL_USART_ClearFlag_PE(LWGSM_USART);
351    LL_USART_ClearFlag_FE(LWGSM_USART);
352    LL_USART_ClearFlag_ORE(LWGSM_USART);
353    LL_USART_ClearFlag_NE(LWGSM_USART);
354
355    if (usart_ll_mbox_id != NULL) {
356        void* d = (void*)1;
357        osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
358    }
359}
360
361/**
362 * \brief           UART DMA stream/channel handler
363 */
364void
365LWGSM_USART_DMA_RX_IRQHANDLER(void) {
366    LWGSM_USART_DMA_RX_CLEAR_TC;
367    LWGSM_USART_DMA_RX_CLEAR_HT;
368
369    if (usart_ll_mbox_id != NULL) {
370        void* d = (void*)1;
371        osMessageQueuePut(usart_ll_mbox_id, &d, 0, 0);
372    }
373}
374
375#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 <string.h>
 35#include <stdlib.h>
 36#include "system/lwgsm_sys.h"
 37#include "lwgsm/lwgsm_private.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, size_t stack_size, lwgsm_sys_thread_prio_t prio) {
325    HANDLE h;
326    DWORD id;
327
328    LWGSM_UNUSED(name);
329    LWGSM_UNUSED(stack_size);
330    LWGSM_UNUSED(prio);
331
332    h = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_func, arg, 0, &id);
333    if (t != NULL) {
334        *t = h;
335    }
336    return h != NULL;
337}
338
339uint8_t
340lwgsm_sys_thread_terminate(lwgsm_sys_thread_t* t) {
341    if (t == NULL) {                            /* Shall we terminate ourself? */
342        ExitThread(0);
343    } else {
344        /* We have known thread, find handle by looking at ID */
345        TerminateThread(*t, 0);
346    }
347    return 1;
348}
349
350uint8_t
351lwgsm_sys_thread_yield(void) {
352    /* Not implemented */
353    return 1;
354}
355
356#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 "lwgsm/lwgsm_opt.h"
40#include "cmsis_os.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 "system/lwgsm_sys.h"
 35#include "cmsis_os.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) : LWGSM_SYS_TIMEOUT;
116}
117
118uint8_t
119lwgsm_sys_sem_release(lwgsm_sys_sem_t* p) {
120    return osSemaphoreRelease(*p) == osOK;
121}
122
123uint8_t
124lwgsm_sys_sem_isvalid(lwgsm_sys_sem_t* p) {
125    return p != NULL && *p != NULL;
126}
127
128uint8_t
129lwgsm_sys_sem_invalid(lwgsm_sys_sem_t* p) {
130    *p = LWGSM_SYS_SEM_NULL;
131    return 1;
132}
133
134uint8_t
135lwgsm_sys_mbox_create(lwgsm_sys_mbox_t* b, size_t size) {
136    const osMessageQueueAttr_t attr = {
137        .name = "lwgsm_mbox",
138    };
139    return (*b = osMessageQueueNew(size, sizeof(void*), &attr)) != NULL;
140}
141
142uint8_t
143lwgsm_sys_mbox_delete(lwgsm_sys_mbox_t* b) {
144    if (osMessageQueueGetCount(*b) > 0) {
145        return 0;
146    }
147    return osMessageQueueDelete(*b) == osOK;
148}
149
150uint32_t
151lwgsm_sys_mbox_put(lwgsm_sys_mbox_t* b, void* m) {
152    uint32_t tick = osKernelSysTick();
153    return osMessageQueuePut(*b, &m, 0, osWaitForever) == osOK ? (osKernelSysTick() - tick) : LWGSM_SYS_TIMEOUT;
154}
155
156uint32_t
157lwgsm_sys_mbox_get(lwgsm_sys_mbox_t* b, void** m, uint32_t timeout) {
158    uint32_t tick = osKernelSysTick();
159    return osMessageQueueGet(*b, m, NULL, timeout == 0 ? osWaitForever : timeout) == osOK ? (osKernelSysTick() - tick) : LWGSM_SYS_TIMEOUT;
160}
161
162uint8_t
163lwgsm_sys_mbox_putnow(lwgsm_sys_mbox_t* b, void* m) {
164    return osMessageQueuePut(*b, &m, 0, 0) == osOK;
165}
166
167uint8_t
168lwgsm_sys_mbox_getnow(lwgsm_sys_mbox_t* b, void** m) {
169    return osMessageQueueGet(*b, m, NULL, 0) == osOK;
170}
171
172uint8_t
173lwgsm_sys_mbox_isvalid(lwgsm_sys_mbox_t* b) {
174    return b != NULL && *b != NULL;
175}
176
177uint8_t
178lwgsm_sys_mbox_invalid(lwgsm_sys_mbox_t* b) {
179    *b = LWGSM_SYS_MBOX_NULL;
180    return 1;
181}
182
183uint8_t
184lwgsm_sys_thread_create(lwgsm_sys_thread_t* t, const char* name, lwgsm_sys_thread_fn thread_func, void* const arg, size_t stack_size, lwgsm_sys_thread_prio_t prio) {
185    lwgsm_sys_thread_t id;
186    const osThreadAttr_t thread_attr = {
187        .name = (char*)name,
188        .priority = (osPriority)prio,
189        .stack_size = stack_size > 0 ? stack_size : LWGSM_SYS_THREAD_SS
190    };
191
192    id = osThreadNew(thread_func, arg, &thread_attr);
193    if (t != NULL) {
194        *t = id;
195    }
196    return id != NULL;
197}
198
199uint8_t
200lwgsm_sys_thread_terminate(lwgsm_sys_thread_t* t) {
201    if (t != NULL) {
202        osThreadTerminate(*t);
203    } else {
204        osThreadExit();
205    }
206    return 1;
207}
208
209uint8_t
210lwgsm_sys_thread_yield(void) {
211    osThreadYield();
212    return 1;
213}
214
215#endif /* !__DOXYGEN__ */