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) 2020 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.0
 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 void
 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 LPCWSTR com_ports[] = {
 93            L"\\\\.\\COM23",
 94            L"\\\\.\\COM12",
 95            L"\\\\.\\COM9",
 96            L"\\\\.\\COM8",
 97            L"\\\\.\\COM4"
 98        };
 99        for (size_t i = 0; i < sizeof(com_ports) / sizeof(com_ports[0]); ++i) {
100            com_port = CreateFile(com_ports[i],
101                                  GENERIC_READ | GENERIC_WRITE,
102                                  0,
103                                  0,
104                                  OPEN_EXISTING,
105                                  0,
106                                  NULL
107                                 );
108            if (GetCommState(com_port, &dcb)) {
109                printf("COM PORT %s opened!\r\n", (const char*)com_ports[i]);
110                break;
111            }
112        }
113    }
114
115    /* Configure COM port parameters */
116    if (GetCommState(com_port, &dcb)) {
117        COMMTIMEOUTS timeouts;
118
119        dcb.BaudRate = baudrate;
120        dcb.ByteSize = 8;
121        dcb.Parity = NOPARITY;
122        dcb.StopBits = ONESTOPBIT;
123
124        if (!SetCommState(com_port, &dcb)) {
125            printf("Cannot set COM PORT info\r\n");
126        }
127        if (GetCommTimeouts(com_port, &timeouts)) {
128            /* Set timeout to return immediately from ReadFile function */
129            timeouts.ReadIntervalTimeout = MAXDWORD;
130            timeouts.ReadTotalTimeoutConstant = 0;
131            timeouts.ReadTotalTimeoutMultiplier = 0;
132            if (!SetCommTimeouts(com_port, &timeouts)) {
133                printf("Cannot set COM PORT timeouts\r\n");
134            }
135            GetCommTimeouts(com_port, &timeouts);
136        } else {
137            printf("Cannot get COM PORT timeouts\r\n");
138        }
139    } else {
140        printf("Cannot get COM PORT info\r\n");
141    }
142
143    /* On first function call, create a thread to read data from COM port */
144    if (!initialized) {
145        thread_handle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)uart_thread, NULL, 0, 0);
146    }
147}
148
149/**
150 * \brief            UART thread
151 */
152static void
153uart_thread(void* param) {
154    DWORD bytes_read;
155    lwgsm_sys_sem_t sem;
156    FILE* file = NULL;
157
158    lwgsm_sys_sem_create(&sem, 0);              /* Create semaphore for delay functions */
159
160    while (com_port == NULL) {
161        lwgsm_sys_sem_wait(&sem, 1);            /* Add some delay with yield */
162    }
163
164    fopen_s(&file, "log_file.txt", "w+");       /* Open debug file in write mode */
165    while (1) {
166        /*
167         * Try to read data from COM port
168         * and send it to upper layer for processing
169         */
170        do {
171            ReadFile(com_port, data_buffer, sizeof(data_buffer), &bytes_read, NULL);
172            if (bytes_read > 0) {
173                HANDLE hConsole;
174                hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
175                SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);
176                for (DWORD i = 0; i < bytes_read; ++i) {
177                    printf("%c", data_buffer[i]);
178                }
179                SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
180
181                /* Send received data to input processing module */
182#if LWGSM_CFG_INPUT_USE_PROCESS
183                lwgsm_input_process(data_buffer, (size_t)bytes_read);
184#else /* LWGSM_CFG_INPUT_USE_PROCESS */
185                lwgsm_input(data_buffer, (size_t)bytes_read);
186#endif /* !LWGSM_CFG_INPUT_USE_PROCESS */
187
188                /* Write received data to output debug file */
189                if (file != NULL) {
190                    fwrite(data_buffer, 1, bytes_read, file);
191                    fflush(file);
192                }
193            }
194        } while (bytes_read == (DWORD)sizeof(data_buffer));
195
196        /* Implement delay to allow other tasks processing */
197        lwgsm_sys_sem_wait(&sem, 1);
198    }
199}
200
201/**
202 * \brief           Callback function called from initialization process
203 *
204 * \note            This function may be called multiple times if AT baudrate is changed from application.
205 *                  It is important that every configuration except AT baudrate is configured only once!
206 *
207 * \note            This function may be called from different threads in GSM stack when using OS.
208 *                  When \ref LWGSM_CFG_INPUT_USE_PROCESS is set to 1, this function may be called from user UART thread.
209 *
210 * \param[in,out]   ll: Pointer to \ref lwgsm_ll_t structure to fill data for communication functions
211 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
212 */
213lwgsmr_t
214lwgsm_ll_init(lwgsm_ll_t* ll) {
215#if !LWGSM_CFG_MEM_CUSTOM
216    /* Step 1: Configure memory for dynamic allocations */
217    static uint8_t memory[0x10000];             /* Create memory for dynamic allocations with specific size */
218
219    /*
220     * Create memory region(s) of memory.
221     * If device has internal/external memory available,
222     * multiple memories may be used
223     */
224    lwgsm_mem_region_t mem_regions[] = {
225        { memory, sizeof(memory) }
226    };
227    if (!initialized) {
228        lwgsm_mem_assignmemory(mem_regions, LWGSM_ARRAYSIZE(mem_regions));  /* Assign memory for allocations to GSM library */
229    }
230#endif /* !LWGSM_CFG_MEM_CUSTOM */
231
232    /* Step 2: Set AT port send function to use when we have data to transmit */
233    if (!initialized) {
234        ll->send_fn = send_data;                /* Set callback function to send data */
235    }
236
237    /* Step 3: Configure AT port to be able to send/receive data to/from GSM device */
238    configure_uart(ll->uart.baudrate);          /* Initialize UART for communication */
239    initialized = 1;
240    return lwgsmOK;
241}
242
243#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) 2020 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.0
 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) 2020 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.0
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) 2020 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.0
 33 */
 34#include <string.h>
 35#include <stdlib.h>
 36#include "system/lwgsm_sys.h"
 37
 38#if !__DOXYGEN__
 39
 40/**
 41 * \brief           Custom message queue implementation for WIN32
 42 */
 43typedef struct {
 44    lwgsm_sys_sem_t sem_not_empty;              /*!< Semaphore indicates not empty */
 45    lwgsm_sys_sem_t sem_not_full;               /*!< Semaphore indicates not full */
 46    lwgsm_sys_sem_t sem;                        /*!< Semaphore to lock access */
 47    size_t in, out, size;
 48    void* entries[1];
 49} win32_mbox_t;
 50
 51static LARGE_INTEGER freq, sys_start_time;
 52static lwgsm_sys_mutex_t sys_mutex;             /* Mutex ID for main protection */
 53
 54static uint8_t
 55mbox_is_full(win32_mbox_t* m) {
 56    size_t size = 0;
 57    if (m->in > m->out) {
 58        size = (m->in - m->out);
 59    } else if (m->out > m->in) {
 60        size = m->size - m->out + m->in;
 61    }
 62    return size == m->size - 1;
 63}
 64
 65static uint8_t
 66mbox_is_empty(win32_mbox_t* m) {
 67    return m->in == m->out;
 68}
 69
 70static uint32_t
 71osKernelSysTick(void) {
 72    LONGLONG ret;
 73    LARGE_INTEGER now;
 74
 75    QueryPerformanceFrequency(&freq);           /* Get frequency */
 76    QueryPerformanceCounter(&now);              /* Get current time */
 77    ret = now.QuadPart - sys_start_time.QuadPart;
 78    return (uint32_t)(((ret) * 1000) / freq.QuadPart);
 79}
 80
 81uint8_t
 82lwgsm_sys_init(void) {
 83    QueryPerformanceFrequency(&freq);
 84    QueryPerformanceCounter(&sys_start_time);
 85
 86    lwgsm_sys_mutex_create(&sys_mutex);
 87    return 1;
 88}
 89
 90uint32_t
 91lwgsm_sys_now(void) {
 92    return osKernelSysTick();
 93}
 94
 95uint8_t
 96lwgsm_sys_protect(void) {
 97    lwgsm_sys_mutex_lock(&sys_mutex);
 98    return 1;
 99}
100
101uint8_t
102lwgsm_sys_unprotect(void) {
103    lwgsm_sys_mutex_unlock(&sys_mutex);
104    return 1;
105}
106
107uint8_t
108lwgsm_sys_mutex_create(lwgsm_sys_mutex_t* p) {
109    *p = CreateMutex(NULL, FALSE, NULL);
110    return *p != NULL;
111}
112
113uint8_t
114lwgsm_sys_mutex_delete(lwgsm_sys_mutex_t* p) {
115    return CloseHandle(*p);
116}
117
118uint8_t
119lwgsm_sys_mutex_lock(lwgsm_sys_mutex_t* p) {
120    DWORD ret;
121    ret = WaitForSingleObject(*p, INFINITE);
122    if (ret != WAIT_OBJECT_0) {
123        return 0;
124    }
125    return 1;
126}
127
128uint8_t
129lwgsm_sys_mutex_unlock(lwgsm_sys_mutex_t* p) {
130    return (uint8_t)ReleaseMutex(*p);
131}
132
133uint8_t
134lwgsm_sys_mutex_isvalid(lwgsm_sys_mutex_t* p) {
135    return p != NULL && *p != NULL;
136}
137
138uint8_t
139lwgsm_sys_mutex_invalid(lwgsm_sys_mutex_t* p) {
140    *p = LWGSM_SYS_MUTEX_NULL;
141    return 1;
142}
143
144uint8_t
145lwgsm_sys_sem_create(lwgsm_sys_sem_t* p, uint8_t cnt) {
146    HANDLE h;
147    h = CreateSemaphore(NULL, !!cnt, 1, NULL);
148    *p = h;
149    return *p != NULL;
150}
151
152uint8_t
153lwgsm_sys_sem_delete(lwgsm_sys_sem_t* p) {
154    return CloseHandle(*p);
155}
156
157uint32_t
158lwgsm_sys_sem_wait(lwgsm_sys_sem_t* p, uint32_t timeout) {
159    DWORD ret;
160    uint32_t tick = osKernelSysTick();
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    h = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_func, arg, 0, &id);
328    if (t != NULL) {
329        *t = h;
330    }
331    return h != NULL;
332}
333
334uint8_t
335lwgsm_sys_thread_terminate(lwgsm_sys_thread_t* t) {
336    HANDLE h = NULL;
337
338    if (t == NULL) {                            /* Shall we terminate ourself? */
339        h = GetCurrentThread();                 /* Get current thread handle */
340    } else {                                    /* We have known thread, find handle by looking at ID */
341        h = *t;
342    }
343    TerminateThread(h, 0);
344    return 1;
345}
346
347uint8_t
348lwgsm_sys_thread_yield(void) {
349    /* Not implemented */
350    return 1;
351}
352
353#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) 2020 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.0
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) 2020 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.0
 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 osKernelSysTick();
 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__ */