LingYun Studio embeded system framwork software, such as thirdparty build shell and lingyun library
guowenxue
2024-08-19 9ada9cdc52c22fabb658680d3869d3ab6ab87f61
update booster from booster project
9 files added
2 files deleted
15 files modified
3878 ■■■■■ changed files
booster/README.md 4 ●●●● patch | view | raw | blame | history
booster/at-esp32.c 388 ●●●●● patch | view | raw | blame | history
booster/at-esp32.h 140 ●●●●● patch | view | raw | blame | history
booster/atcmd.c 300 ●●●●● patch | view | raw | blame | history
booster/atcmd.h 93 ●●●●● patch | view | raw | blame | history
booster/comport.c 411 ●●●●● patch | view | raw | blame | history
booster/comport.h 52 ●●●●● patch | view | raw | blame | history
booster/dictionary.c 3 ●●●● patch | view | raw | blame | history
booster/dictionary.h 3 ●●●● patch | view | raw | blame | history
booster/esp32.c 166 ●●●●● patch | view | raw | blame | history
booster/esp32.h 35 ●●●●● patch | view | raw | blame | history
booster/iniparser.c 6 ●●●● patch | view | raw | blame | history
booster/iniparser.h 4 ●●●● patch | view | raw | blame | history
booster/linux_list.h 524 ●●●● patch | view | raw | blame | history
booster/list.h 723 ●●●●● patch | view | raw | blame | history
booster/logger.c 503 ●●●●● patch | view | raw | blame | history
booster/logger.h 123 ●●●●● patch | view | raw | blame | history
booster/makefile 25 ●●●● patch | view | raw | blame | history
booster/ringbuf.c 17 ●●●● patch | view | raw | blame | history
booster/ringbuf.h 12 ●●●● patch | view | raw | blame | history
booster/test/gpio-scripts/pinctrl-rzboard 144 ●●●●● patch | view | raw | blame | history
booster/test/logger.c 42 ●●●●● patch | view | raw | blame | history
booster/test/makefile 63 ●●●● patch | view | raw | blame | history
booster/test/test_logger.c 45 ●●●●● patch | view | raw | blame | history
booster/util_proc.c 26 ●●●● patch | view | raw | blame | history
booster/util_proc.h 26 ●●●●● patch | view | raw | blame | history
booster/README.md
New file
@@ -0,0 +1,4 @@
## booster
LingYun embedded C program basic library
booster/at-esp32.c
New file
@@ -0,0 +1,388 @@
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  at-esp32.c
 *    Description:  This file is ESP32 AT command low level API functions
 *
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#include "logger.h"
#include "at-esp32.h"
/*+------------------------+
 *|    Baisc AT command    |
 *+------------------------+*/
static inline int check_at_ready(comport_t *comport)
{
    int             times = 6;
    int             ready = 0;
    while( times-- )
    {
        if( 0 == send_atcmd_check_ok(comport, "AT", 500) )
        {
            ready = 1;
            break;
        }
    }
    return ready;
}
/* AT command: AT+RST */
int esp32_reset(comport_t *comport)
{
    int             rv;
    rv = send_atcmd(comport, "AT+RST", 5000, "ready", AT_ERRSTR, NULL, 0);
    if( rv < 0)
    {
        log_error("send AT command to reset ESP32 failed, rv=%d\n", rv);
        return -1;
    }
    if( check_at_ready(comport) )
    {
        log_info("send AT to reset ESP32 and AT command ready\n");
        return 0;
    }
    else
    {
        log_info("send AT to reset ESP32 but AT command not ready\n");
        return -3;
    }
}
/* AT command: AT+RESTORE */
int esp32_restore(comport_t *comport)
{
    int             rv;
    //rv = send_atcmd_check_ok(comport, "AT+RESTORE", 5000);
    rv = send_atcmd(comport, "AT+RESTORE", 5000, "ready", AT_ERRSTR, NULL, 0);
    if( rv < 0)
    {
        log_error("send AT command to restore ESP32 failed, rv=%d\n", rv);
        return -1;
    }
    if( check_at_ready(comport) )
    {
        log_info("send AT command to resstore ESP32 ready\n");
        return 0;
    }
    else
    {
        log_error("send AT command to restore ESP32 but AT not ready\n");
        return -3;
    }
}
/* AT command: ATE1 or ATE0 */
int esp32_set_echo(comport_t *comport, int enable)
{
    char           *at = enable? "ATE1" : "ATE0";
    return send_atcmd_check_ok(comport, at, 500);
}
/* AT command: AT+GMR */
int esp32_get_firmware(comport_t *comport, char *version, int size)
{
    if( !version || size<=0 )
        return -1;
    return send_atcmd_check_value(comport, "AT+GMR", 2000, version, size);
}
/* AT command: AT+SYSSTORE */
int esp32_set_sysstore(comport_t *comport, int enable)
{
    char            at[ATCMD_LEN]={'\0'};
    snprintf(at, sizeof(at), "AT+SYSSTORE=%d", enable?1:0);
    return send_atcmd_check_ok(comport, at, 1000);
}
/*+------------------------+
 *|    WiFi AT command     |
 *+------------------------+*/
/* AT command: AT+CWMODE */
int esp32_set_wmode(comport_t *comport, workmode_t mode, int autoconn)
{
    char            at[ATCMD_LEN]={'\0'};
    snprintf(at, sizeof(at), "AT+CWMODE=%d,%d", mode, autoconn?1:0);
    return send_atcmd_check_ok(comport, at, 1500);
}
/* AT command: AT+CIPSTAMAC/CIPAPMAC */
int esp32_get_macaddr(comport_t *comport, workmode_t mode, char *mac)
{
    if( !mac )
        return -1;
    if( MODE_SOFTAP == mode )
        return send_atcmd_check_request(comport, "AT+CIPAPMAC?", 3000, mac, MAC_LEN);
    else
        return send_atcmd_check_request(comport, "AT+CIPSTAMAC?", 3000, mac, MAC_LEN);
}
/* AT command: AT+CIPSTA/AT+CIPAP  */
int esp32_set_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway)
{
    char            at[ATCMD_LEN]={'\0'};
    if( !ip || !gateway )
        return -1;
    if( MODE_SOFTAP == mode )
        snprintf(at, sizeof(at), "AT+CIPAP=\"%s\",\"%s\"", ip, gateway);
    else
        snprintf(at, sizeof(at), "AT+CIPSTA=\"%s\",\"%s\"", ip, gateway);
    return send_atcmd_check_ok(comport, at, 2000);
}
/* AT command: AT+CIPSTA/AT+CIPAP  */
int esp32_get_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway)
{
    char           *at = MODE_SOFTAP==mode? "AT+CIPAP?" : "AT+CIPSTA?";
    char            buf[ATCMD_REPLY_LEN];
    int             rv;
    if( !ip || !gateway )
        return -1;
    rv = send_atcmd_check_value(comport, at, 3000, buf, sizeof(buf));
    if( rv < 0)
    {
        log_error("AT command query IP address failed, rv=%d\n", rv);
        return rv;
    }
    rv = parser_request_value(buf, "ip", ip, IP_LEN);
    if( rv < 0 )
    {
        log_error("Parser query IP address failed, rv=%d\n", rv);
        return rv;
    }
    rv = parser_request_value(buf, "gateway", gateway, IP_LEN);
    if( rv < 0 )
    {
        log_error("Parser query gateway address failed, rv=%d\n", rv);
        return rv;
    }
    return 0;
}
/* AT command: AT+CWDHCP */
int esp32_set_dhcp(comport_t *comport, workmode_t mode, int enable)
{
    char            at[ATCMD_LEN]={'\0'};
    snprintf(at, sizeof(at), "AT+CWDHCP=%d,%d", enable?1:0, mode);
    return send_atcmd_check_ok(comport, at, 1500);
}
/* AT command: AT+CWAUTOCONN */
int esp32_set_autoconn(comport_t *comport, int enable)
{
    char            at[ATCMD_LEN]={'\0'};
    snprintf(at, sizeof(at), "AT+CWAUTOCONN=%d", enable?1:0);
    return send_atcmd_check_ok(comport, at, 1500);
}
/* AT command: AT+CWLAP */
int esp32_list_ap(comport_t *comport, char *buf, int size)
{
    if( !buf || size<=0 )
        return -1;
    return send_atcmd_check_value(comport, "AT+CWLAP", 8000, buf, size);
}
/* AT command: AT+CWJAP */
int esp32_connect_ap(comport_t *comport, char *ssid, char *pwd)
{
    char            at[ATCMD_LEN]={'\0'};
    if( !ssid || !pwd )
        return -1;
    snprintf(at, sizeof(at), "AT+CWJAP=\"%s\",\"%s\"", ssid, pwd);
    return send_atcmd_check_ok(comport, at, 15000);
}
/* AT command: AT+CWQAP */
int esp32_disconn_ap(comport_t *comport)
{
    return send_atcmd_check_ok(comport, "AT+CWQAP", 5000);
}
/* AT command: AT+CWSTATE?  status value:
 * - 0: ESP32 station has not started any Wi-Fi connection.
 * - 1: ESP32 station has connected to an AP, but does not get an IPv4 address yet.
 * - 2: ESP32 station has connected to an AP, and got an IPv4 address.
 * - 3: ESP32 station is in Wi-Fi connecting or reconnecting state.
 * - 4: ESP32 station is in Wi-Fi disconnected state
 */
int esp32_join_status(comport_t *comport, int *status, char *ssid)
{
    char            buf[ATCMD_REPLY_LEN];
    int             rv;
    if( !status || !ssid )
        return -1;
    rv = send_atcmd_check_request(comport, "AT+CWSTATE?", 3000, buf, sizeof(buf));
    if( rv < 0)
    {
        log_error("AT command query join status failed, rv=%d\n", rv);
        return rv;
    }
    sscanf(buf, "%d,%s", status, ssid);
    return 0;
}
/* AT command:  AT+PING */
int esp32_ping(comport_t *comport, char *host, int timeout)
{
    char            at[ATCMD_LEN]={'\0'};
    if( !host )
        return -1;
    snprintf(at, sizeof(at), "AT+PING=\"%s\"", host);
    return send_atcmd_check_ok(comport, at, timeout);
}
/* AT command:  AT+CWSAP */
int esp32_set_softap(comport_t *comport, char *ssid, char *pwd, int channel, encmode_t encmode)
{
    char            at[ATCMD_LEN]={'\0'};
    if( !ssid || !pwd )
        return -1;
    snprintf(at, sizeof(at), "AT+CWSAP=\"%s\",\"%s\",%d,%d", ssid, pwd, channel, encmode);
    return send_atcmd_check_ok(comport, at, 5000);
}
/* AT command: AT+CWLIF */
int esp32_list_client(comport_t *comport, char *buf, int size)
{
    if( !buf || size<=0 )
        return -1;
    return send_atcmd_check_value(comport, "AT+CWLIF", 8000, buf, size);
}
/*+------------------------+
 *|   Socket AT command    |
 *+------------------------+*/
/* AT command: AT+CIPMUX */
int esp32_set_socket_mux(comport_t *comport, int enable)
{
    char            at[ATCMD_LEN]={'\0'};
    snprintf(at, sizeof(at), "AT+CIPMUX=%d", enable?1:0);
    return send_atcmd_check_ok(comport, at, 1500);
}
/* AT command: AT+CIPSERVERMAXCONN */
int esp32_set_socket_clients(comport_t *comport, int max)
{
    char            at[ATCMD_LEN]={'\0'};
    if( max <= 0 )
        return -1;
    snprintf(at, sizeof(at), "AT+CIPSERVERMAXCONN=%d", max);
    return send_atcmd_check_ok(comport, at, 1500);
}
/* AT command: AT+CIPSTO, timeout unit second */
int esp32_set_socket_timeout(comport_t *comport, int timeout)
{
    char            at[ATCMD_LEN]={'\0'};
    if( timeout <= 0 )
        return -1;
    snprintf(at, sizeof(at), "AT+CIPSTO=%d", timeout);
    return send_atcmd_check_ok(comport, at, 1500);
}
/* AT command: AT+CIPSERVER */
int esp32_set_tcp_server(comport_t *comport, int port)
{
    char            at[ATCMD_LEN]={'\0'};
    if( port <= 0 )
        return -1;
    snprintf(at, sizeof(at), "AT+CIPSERVER=1,%d,\"TCP\"", port);
    return send_atcmd_check_ok(comport, at, 1500);
}
/* AT command: AT+CIPSERVER */
int esp32_del_tcp_server(comport_t *comport, int port)
{
    char            at[ATCMD_LEN]={'\0'};
    if( port <= 0 )
        return -1;
    snprintf(at, sizeof(at), "AT+CIPSERVER=0,%d,\"TCP\"", port);
    return send_atcmd_check_ok(comport, at, 1500);
}
/* AT command: AT+CIPSTART */
int esp32_set_tcp_client(comport_t *comport, int mux, char *host, int port)
{
    char            at[ATCMD_LEN]={'\0'};
    char            buf[ATCMD_REPLY_LEN]={'\0'};
    int             keepalive = 60; /* unit second */
    int             rv,linkid = 0;
    if( !host || port <= 0 )
        return -1;
    rv = esp32_set_socket_mux(comport, mux);
    if(rv < 0)
        return rv;
    if( mux )
        snprintf(at, sizeof(at), "AT+CIPSTART=1,\"TCP\",\"%s\",%d,%d", host, port, keepalive);
    else
        snprintf(at, sizeof(at), "AT+CIPSTART=\"TCP\",\"%s\",%d,%d", host, port, keepalive);
    rv = send_atcmd_check_value(comport, at, 1500, buf, sizeof(buf));
    if(rv < 0)
        return rv;
    sscanf(buf, "%d,", &linkid);
    return linkid;
}
booster/at-esp32.h
New file
@@ -0,0 +1,140 @@
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  at-esp32.h
 *    Description:  This file is ESP32 AT command low level API functions
 *
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#ifndef  _AT_ESP32_H_
#define  _AT_ESP32_H_
#include "atcmd.h"
enum
{
    DISABLE = 0,    /* common disable */
    ENABLE,         /* common enable  */
};
#define MAC_LEN     18 /* 11:22:33:44:55:66 */
#define IP_LEN      16 /* 255.255.255.255 */
/*+------------------------+
 *|    Baisc AT command    |
 *+------------------------+*/
/* AT command: AT+RST */
extern int esp32_reset(comport_t *comport);
/* AT command: AT+RESTORE */
extern int esp32_restore(comport_t *comport);
/* AT command: ATE1 or ATE0 */
extern int esp32_set_echo(comport_t *comport, int enable);
/* AT command: AT+GMR */
extern int esp32_get_firmware(comport_t *comport, char *version, int size);
/* AT command: AT+SYSSTORE */
extern int esp32_set_sysstore(comport_t *comport, int enable);
/*+------------------------+
 *|    WiFi AT command     |
 *+------------------------+*/
typedef enum
{
    MODE_DISABLE=0, /* Wi-Fi RF will be disabled */
    MODE_STATION,   /* Station mode */
    MODE_SOFTAP,    /* SoftAP mode */
    MODE_WDS,       /* Wireless Distribution System: Both Station & SoftAP mode */
} workmode_t;
/* AT command: AT+CWMODE */
extern int esp32_set_wmode(comport_t *comport, workmode_t mode, int autoconn);
/* AT command: AT+CWDHCP */
extern int esp32_set_dhcp(comport_t *comport, workmode_t mode, int enable);
/* AT command: AT+CWAUTOCONN */
extern int esp32_set_autoconn(comport_t *comport, int enable);
/* AT command: AT+CIPSTAMAC/CIPAPMAC */
extern int esp32_get_macaddr(comport_t *comport, workmode_t mode, char *mac);
/* AT command: AT+CIPSTA/CIPAP */
extern int esp32_get_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway);
/* AT command: AT+CIPSTA/AT+CIPAP  */
extern int esp32_set_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway);
/* AT command: AT+CWLAP */
extern int esp32_list_ap(comport_t *comport, char *buf, int size);
/* AT command: AT+CWJAP */
extern int esp32_connect_ap(comport_t *comport, char *ssid, char *pwd);
/* AT command: AT+CWQAP */
extern int esp32_disconn_ap(comport_t *comport);
/* AT command: AT+CWSTATE? , status value:
 * - 0: ESP32 station has not started any Wi-Fi connection.
 * - 1: ESP32 station has connected to an AP, but does not get an IPv4 address yet.
 * - 2: ESP32 station has connected to an AP, and got an IPv4 address.
 * - 3: ESP32 station is in Wi-Fi connecting or reconnecting state.
 * - 4: ESP32 station is in Wi-Fi disconnected state
 */
extern int esp32_join_status(comport_t *comport, int *status, char *ssid);
/* AT command:  AT+PING */
extern int esp32_ping(comport_t *comport, char *host, int timeout);
/* AT command:  AT+CWSAP */
typedef enum
{
    MODE_OPEN,
    /* WEP not support */
    MODE_WPAPSK = 2,
    MODE_WPA2PSK,
    MODE_WPA_WPA2PSK,
} encmode_t;
extern int esp32_set_softap(comport_t *comport, char *ssid, char *pwd, int channel, encmode_t encmode);
/* AT command:  AT+CWLIF */
extern int esp32_list_client(comport_t *comport, char *buf, int size);
/*+------------------------+
 *|   Socket AT command    |
 *+------------------------+*/
/* AT command: CIPMUX */
extern int esp32_set_socket_mux(comport_t *comport, int enable);
/* AT command: AT+CIPSERVERMAXCONN */
extern int esp32_set_socket_clients(comport_t *comport, int max);
/* AT command: AT+CIPSTO, timeout unit second */
extern int esp32_set_socket_timeout(comport_t *comport, int timeout);
/* AT command: AT+CIPSERVER */
extern int esp32_set_tcp_server(comport_t *comport, int port);
/* AT command: AT+CIPSERVER */
extern int esp32_del_tcp_server(comport_t *comport, int port);
/* AT command: AT+CIPSTART */
extern int esp32_set_tcp_client(comport_t *comport, int mux, char *host, int port);
/*+------------------------+
 *|     BLE AT command     |
 *+------------------------+*/
// RFU
#endif   /* ----- #ifndef _AT_ESP32_H_  ----- */
booster/atcmd.c
New file
@@ -0,0 +1,300 @@
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  atcmd.c
 *    Description:  This file is lowlevel AT command send and parser API functions
 *
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#include <ctype.h>
#include "logger.h"
#include "atcmd.h"
/*  Description: this function used to send an AT command from serial port and wait for reply message from
 *               the communication module, and it will return once get expet/error string or timeout.
 *
 *    Arugments:
 *         $comport: the serial port which connected to GPRS/3G/4G/NB-IoT/WiFi/BLE/Zigbee/LoRa...
 *              $at: the AT command need to be sent, without "\r\n"
 *         $timeout: wait for module reply for AT command timeout value, unit micro seconds(ms)
 *          $exepct: the expect string reply from communication module
 *           $error: the error string reply from communication module
 *           $reply: the module reply message output buffer
 *            $size: the output buffer ($reply) size
 *
 * Return value: <0: Function error   0: Got error string   1: Got expect string 2: Receive util timeout
 *
 */
int send_atcmd(comport_t *comport, char *at, unsigned long timeout, char *expect, char *error, char *reply, int size)
{
    int                    i, rv = 0;
    int                    res = ATRES_TIMEOUT;
    int                    bytes = 0;
    char                   buf[ATCMD_REPLY_LEN] = {'\0'};
    char                   atcmd[ATCMD_LEN] = {'\0'};
    if( !comport || !at )
    {
        log_error("Invalid input arguments\n");
        return -1;
    }
    if( comport->fd <= 0 )
    {
        log_error("comport[%s] not opened\n");
        return -2;
    }
    /* flushes both data received but not read, and data written but not transmitted in serial port */
    tcflush(comport->fd, TCIOFLUSH);
    snprintf(atcmd, sizeof(atcmd), "%s%s", at, AT_SUFFIX);
    rv=comport_send( comport, atcmd, strlen(atcmd) );
    if(rv < 0)
    {
        log_error("send AT command \"%s\" to \"%s\" failed, rv=%d\n", at, comport->devname, rv);
        return -3;
    }
    res = ATRES_TIMEOUT;
    memset( buf, 0, sizeof(buf) );
    for(i=0; i<timeout/10; i++)
    {
        if( bytes >= sizeof(buf) )
            break;
        rv=comport_recv( comport, buf+bytes, sizeof(buf)-bytes, 10);
        if(rv < 0)
        {
            log_error("send AT command \'%s\' to \'%s\' failed, rv=%d\n", at, comport->devname, rv);
            return -3;
        }
        bytes += rv;
        if( expect && strstr(buf, expect) )
        {
            log_debug("send AT command \"%s\" and got reply \"OK\"\n", at);
            res = ATRES_EXPECT;
            break;
        }
        if( error && strstr(buf, error) )
        {
            log_debug("send AT command \"%s\" and got reply \"ERROR\"\n", at);
            res = ATRES_ERROR;
            break;
        }
    }
    if( bytes > 0 )
        log_trace("AT command reply:%s", buf);
    if( reply && size>0 )
    {
        bytes = strlen(buf)>size ? size : strlen(buf);
        memset(reply, 0, size);
        strncpy(reply, buf, bytes);
        log_debug("copy out AT command \"%s\" reply message: \n%s", at, reply);
    }
    return res;
}
/*
 *  Description: Send AT command which will only reply by "OK" or "ERROR", such as AT:
 *                 Reply:   \r\nOK\r\n
 * Return Value: 0: OK     -X: Failure
 */
int send_atcmd_check_ok(comport_t *comport, char *at, unsigned long timeout)
{
    int                     rv;
    if( !comport || !at )
    {
        log_error("Invalid input arguments\n");
        return -1;
    }
    rv=send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, NULL, 0);
    if( ATRES_EXPECT == rv )
    {
        return 0;
    }
    else
    {
        return -2;
    }
}
/*
 *  Description: Send AT command which will reply by a value directly in a single line, such as AT+CGMM:
 *                  Reply:   \r\nEC20F\r\nOK\r\n
 *
 * Return Value: 0: OK     -X: Failure
 */
int send_atcmd_check_value(comport_t *comport, char *at, unsigned long timeout, char *reply, int size)
{
    int                     rv, len;
    char                    buf[ATCMD_REPLY_LEN];
    char                   *ptr_start = buf;
    char                   *ptr_end;
    if( !comport || !at || !reply || size<=0 )
    {
        log_error("Invalid input arguments\n");
        return -1;
    }
    rv = send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, buf, ATCMD_REPLY_LEN);
    if( rv <= 0 )
    {
        return -2;
    }
    /* Skip the echo back command line */
    if( !strncmp(buf, at, strlen(at)) )
    {
        ptr_start=strchr(buf, '\n');
        if( !ptr_start )
        {
            log_error("reply message got wrong\n");
            return -3;
        }
        ptr_start++;   /* skip '\n'  */
    }
    /* find end reply string "\r\nOK\r\n"  */
    ptr_end = strstr(ptr_start, AT_OKSTR);
    if( ptr_end )
    {
        len = ptr_end - ptr_start;
    }
    else
    {
        len = strlen(buf) - (ptr_start-buf);
    }
    memset(reply, 0, size);
    len = len>size ? size : len;
    memcpy(reply, ptr_start, len);
    return 0;
}
/*
 *  Description: Parser the $value from $key like "xxx: " line, such as AT+CSQ:
 *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
 *
 * Return Value: 0: OK     -X: Failure
 */
int parser_request_value(char *buf, char *key, char *value, int size)
{
    char                   *ptr;
    int                    i = 0;
    if( !buf || !key || !value || size<=0 )
    {
        log_error("Invalid input arguments\n");
        return -1;
    }
    ptr = strstr(buf, key);
    if( !ptr )
    {
        log_debug("Not found key \"%s\" in %s\n", key, buf);
        return -2;
    }
    ptr=strchr(ptr, ':');  /* found ':' before the value */
    if( !ptr )
    {
        log_debug("Not found ':' before value\n");
        return -3;
    }
    ptr++;   /* skip ':'  */
    if( *ptr == '\"' ) /* skip " */
        ptr++;
    memset(value, 0, size);
    while(*ptr!='\r' && i<size-1)
    {
        if( !isspace(*ptr) && *ptr!='\"') /* skip space,\r,\n ...  */
            value[i++] = *ptr;
        ptr ++;
    }
    ptr++; /* skip  */
    return 0;
}
/*
 *  Description: Send AT command which will reply by the value  with a prefix "+CMD: " line, such as AT+CSQ:
 *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
 *
 * Return Value: 0: OK     -X: Failure
 */
int send_atcmd_check_request(comport_t *comport, char *at, unsigned long timeout, char *reply, int size)
{
    int                     i = 0;
    int                     rv;
    char                    buf[ATCMD_REPLY_LEN];
    char                   *ptr;
    if( !comport || !at || !reply || size<=0 )
    {
        log_error("Invalid input arguments\n");
        return -1;
    }
    rv = send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, buf, ATCMD_REPLY_LEN);
    if( rv <= 0 )
    {
        return -2;
    }
    ptr=strchr(buf, '+');  /* found '+' before the value */
    if( !ptr )
    {
        log_error("reply message got wrong\n");
        return -3;
    }
    ptr++;   /* skip '+'  */
    ptr=strchr(buf, ':');  /* found ':' before the value */
    if( !ptr )
    {
        log_error("reply message got wrong\n");
        return -3;
    }
    ptr++;   /* skip ':'  */
    if( *ptr == '\"' ) /* skip " */
        ptr++;
    memset(reply, 0, size);
    while(*ptr!='\r' && i<size-1)
    {
        if( !isspace(*ptr) && *ptr!='\"') /* skip space,\r,\n ...  */
            reply[i++] = *ptr;
        ptr ++;
    }
    return 0;
}
booster/atcmd.h
New file
@@ -0,0 +1,93 @@
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  atcmd.h
 *    Description:  This file is lowlevel AT command send and parser API functions
 *
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#ifndef  _ATCMD_H_
#define  _ATCMD_H_
#include "comport.h"
/* AT command common reply message max length  */
#define ATCMD_REPLY_LEN          1024 /* Max AT command reply message length */
#define ATCMD_LEN                256 /* Max AT command length */
/* AT command reply message got expect or error string */
#define AT_OKSTR                 "\r\nOK\r\n"     /* expect string always be OK */
#define AT_ERRSTR                "\r\nERROR\r\n"  /* error string always be ERROR */
/* AT command should be end by $AT_SUFFIX */
#define AT_SUFFIX                "\r\n"
/* send_atcmd)( return value status */
enum
{
    ATRES_ERROR,    /* AT command reply got error string, such as "ERROR\r\n" */
    ATRES_EXPECT,   /* AT command reply got expect string, such as "OK\r\n" */
    ATRES_TIMEOUT,  /* AT command not get error/expect string, receive util timeout */
};
/*  Description: this function used to send an AT command from serial port and wait for reply message from
 *               the communication module, and it will return once get expet/error string or timeout.
 *
 *    Arugments:
 *         $comport: the serial port which connected to GPRS/3G/4G/NB-IoT/WiFi/BLE/Zigbee/LoRa...
 *              $at: the AT command need to be sent, without "\r\n"
 *         $timeout: wait for module reply for AT command timeout value, unit micro seconds(ms)
 *          $exepct: the expect string reply from communication module
 *           $error: the error string reply from communication module
 *           $reply: the module reply message output buffer
 *            $size: the output buffer ($reply) size
 *
 * Return value: <0: Function error   0: Got error string   1: Got expect string 2: Receive util timeout
 *
 */
int send_atcmd(comport_t *comport, char *at, unsigned long timeout, char *expect, char *error, char *reply, int size);
/*
 *  Description: Send AT command which will only reply by "OK" or "ERROR", such as AT:
 *                  Reply:   \r\nOK\r\n
 *
 * Return Value:  0: OK     -X: ERROR
 */
int send_atcmd_check_ok(comport_t *comport, char *at, unsigned long timeout);
/*
 *  Description: Send AT command which will reply by a value directly in a single line, such as AT+CGMM:
 *                  Reply:   \r\nEC20F\r\nOK\r\n
 *
 * Return Value:  0: OK     -X: ERROR
 */
int send_atcmd_check_value(comport_t *comport, char *at, unsigned long timeout, char *reply, int size);
/*
 *  Description: Parser the $value from $key like "xxx: " line, such as AT+CSQ:
 *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
 *
 * Return Value: 0: OK     -X: Failure
 */
int parser_request_value(char *buf, char *key, char *value, int size);
/*
 *  Description: Send AT command which will reply by the value  with a prefix "+CMD: " line, such as AT+CSQ:
 *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
 *
 * Return Value:  0: OK     -X: ERROR
 */
int send_atcmd_check_request(comport_t *comport, char *at, unsigned long timeout, char *repy, int size);
#endif   /* ----- #ifndef _ATCMD_H_  ----- */
booster/comport.c
@@ -1,25 +1,34 @@
/*********************************************************************************
 *      Copyright:  (C) 2018 Guo Wenxue <guowenxue@gmail.com>
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  cp_comport.c
 *    Description:  It's the comport operate library.
 *       Filename:  comport.c
 *    Description:  This file is linux comport common API functions
 *
 *        Version:  2.0.0(10/17/2018~)
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "10/17/2018 03:33:25 PM"
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#include    "comport.h"
#include "comport.h"
static void set_settings(comport_t *comport, const char *settings);
#define CONFIG_PRINT_LOGGER
//#define CONFIG_PRINT_STDOUT
#ifdef  COM_DEBUG
void disp_settings(comport_t *comport);
#if ( defined CONFIG_PRINT_LOGGER )
#include "logger.h"
#define dbg_print(format,args...) log_error(format, ##args)
#elif ( defined CONFIG_PRINT_STDOUT )
#define dbg_print(format,args...) printf(format, ##args)
#else
#define dbg_print(format,args...) do{} while(0);
#endif
static inline void set_settings(comport_t * comport, const char *settings);
/*
 *  description: Open the serial port
@@ -40,64 +49,58 @@
    if( !comport || !devname )
    {
        COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__);
        dbg_print("invalid input arugments\n");
        return -1;
    }
    /*+-----------------------+
     *| open the serial port  |
     *+-----------------------+*/
    memset(comport, 0, sizeof(*comport));
    strncpy(comport->devname, devname, DEVNAME_LEN);
    strncpy(comport->devname, devname, sizeof(comport->devname));
    comport->baudrate = baudrate;
    comport->fd = -1;
    comport->frag_size = 128;
    comport->fragsize = CONFIG_DEF_FRAGSIZE;
    set_settings(comport, settings);
#ifdef  COM_DEBUG
    disp_settings(comport);
#endif
    /* Not a TTY device */
    if( !strstr(comport->devname, "tty"))
    if( !strstr(comport->devname, "tty") )
    {
        COM_PRINT("Open Not tty device \"%s\"\n", comport->devname);
        dbg_print("comport device \"%s\" is not tty device\n", comport->devname);
        return -2;
    }
    comport->fd = open(comport->devname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (comport->fd < 0)
    if( comport->fd<0 )
    {
        rv = -3;
        goto CleanUp;
        dbg_print("comport open \"%s\" failed:%s\n", comport->devname, strerror(errno));
        return -3;
    }
    COM_PRINT("Open device \"%s\"\n", comport->devname);
    if ((-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0)))
            && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK)))
    if(   (-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0)))
       && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK)) )
    {
        /* Flush input and output */
        if (-1 == tcflush(comport->fd, TCIOFLUSH))
        {
            rv = -4;
            goto CleanUp;
        }
        tcflush(comport->fd, TCIOFLUSH);
    }
    else
    {
        rv = -5;
        rv = -4;
        goto CleanUp;
    }
    if (0 != tcgetattr(comport->fd, &old_cfg))
    {
        rv = -6;          // Failed to get Com settings
        rv = -5;
        goto CleanUp;
    }
    /*+-----------------------+
     *| configure serial port |
     *+-----------------------+*/
    memset(&new_cfg, 0, sizeof(new_cfg));
    /*=====================================*/
    /*       Configure comport         */
    /*=====================================*/
    new_cfg.c_cflag &= ~CSIZE;
    new_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    new_cfg.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
@@ -123,11 +126,11 @@
    /* Set the parity */
    switch (comport->parity)
    {
        case 0x01:               // Odd
        case 0x01:    /* Odd */
            new_cfg.c_cflag |= (PARENB | PARODD);
            new_cfg.c_cflag |= (INPCK | ISTRIP);
            break;
        case 0x02:               // Even
        case 0x02:    /* Even */
            new_cfg.c_cflag |= PARENB;
            new_cfg.c_cflag &= ~PARODD;;
            new_cfg.c_cflag |= (INPCK | ISTRIP);
@@ -153,16 +156,16 @@
    /* Set flow control */
    switch (comport->flowctrl)
    {
        case 1:                  // Software control
        case 1:       /* Software control */
        case 3:
            new_cfg.c_cflag &= ~(CRTSCTS);
            new_cfg.c_iflag |= (IXON | IXOFF);
            break;
        case 2:                  // Hardware control
            new_cfg.c_cflag |= CRTSCTS;   // Also called CRTSCTS
        case 2:       /* Hardware control */
            new_cfg.c_cflag |= CRTSCTS;
            new_cfg.c_iflag &= ~(IXON | IXOFF);
            break;
        default:                 // NONE
        default:      /* NONE */
            new_cfg.c_cflag &= ~(CRTSCTS);
            new_cfg.c_iflag &= ~(IXON | IXOFF);
            break;
@@ -171,53 +174,54 @@
    /* Set baudrate */
    switch (comport->baudrate)
    {
        /*  Upper is not POSIX(bits/termios-baud.h) */
        case 4000000:
            tmp = B4000000;
            break;
        case 3500000:
            tmp = B3500000;
            break;
        case 3000000:
            tmp = B3000000;
            break;
        case 2500000:
            tmp = B2500000;
            break;
        case 2000000:
            tmp = B2000000;
            break;
        case 1500000:
            tmp = B1500000;
            break;
        case 1152000:
            tmp = B1152000;
            break;
        case 1000000:
            tmp = B1000000;
            break;
        case 921600:
            tmp = B921600;
            break;
        case 576000:
            tmp = B576000;
            break;
        case 500000:
            tmp = B500000;
            break;
        case 460800:
            tmp = B460800;
            break;
        case 230400:
            tmp = B230400;
            break;
        /* Upper is not POSIX(bits/termios-baud.h) */
        case 4000000:
            tmp = B4000000;
            break;
        case 3500000:
            tmp = B3500000;
            break;
        case 3000000:
            tmp = B3000000;
            break;
        case 2500000:
            tmp = B2500000;
            break;
        case 2000000:
            tmp = B2000000;
            break;
        case 1500000:
            tmp = B1500000;
            break;
        case 1152000:
            tmp = B1152000;
            break;
        case 1000000:
            tmp = B1000000;
            break;
        case 921600:
            tmp = B921600;
            break;
        case 576000:
            tmp = B576000;
            break;
        case 500000:
            tmp = B500000;
            break;
        case 460800:
            tmp = B460800;
            break;
        case 230400:
            tmp = B230400;
            break;
        case 115200:
            tmp = B115200;
            break;
        case 57600:
            tmp = B57600;
            break;
        /* Below is POSIX(bits/termios.h) */
        /* Below is POSIX(bits/termios.h) */
        case 38400:
            tmp = B38400;
            break;
@@ -276,19 +280,15 @@
    tcflush(comport->fd, TCIFLUSH);
    if (0 != tcsetattr(comport->fd, TCSANOW, &new_cfg))
    {
        rv = -7;          // Failed to set device com port settings
        rv = -6;          // Failed to set device com port settings
        goto CleanUp;
    }
    COM_PRINT("Connected device \"%s\".\n", comport->devname);
    rv = comport->fd;
CleanUp:
    COM_PRINT("Open device \"%s\" %s.\n", comport->devname, rv>0 ? "successfully" : "failure");
    return rv;
}
/*
@@ -300,13 +300,12 @@
{
    if( !comport )
    {
        COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__);
        dbg_print("invalid input arugments\n");
        return ;
    }
    if ( comport->fd >= 0 )
    {
        COM_PRINT("Close device \"%s\"\n", comport->devname);
        close(comport->fd);
    }
@@ -315,165 +314,127 @@
}
/*
 *  description: write $send_bytes bytes data from $buf to $comport
 *  description: write $data_bytes $data to $comport
 * return value: 0: write ok  <0: write failure
 */
int comport_send(comport_t *comport, char *buf, int send_bytes)
int comport_send(comport_t *comport, char *data, int data_bytes)
{
    char                       *ptr;
    int                         left, bytes = 0;
    int                         rv = 0;
    char                       *ptr, left_bytes;
    int                         send = 0;
    if ( !comport || !buf || send_bytes<=0 )
    if( !comport || !data || data_bytes<=0 )
    {
        COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__);
        rv = -1;
        goto CleanUp;
        dbg_print("invalid input arugments\n");
        return -1;
    }
    if ( comport->fd < 0 )    // Comport not opened ?
    if( comport->fd < 0 )
    {
        rv = -3;
        COM_PRINT("Serail not connected.\n");
        goto CleanUp;
        dbg_print("Serail port not opened\n");
        return -2;
    }
    //printf("Send %s with %d bytes.\n", buf, send_bytes);
    ptr = data;
    left = data_bytes;
    left_bytes = send_bytes;
    ptr = buf;
    while( left_bytes > 0 )
    while( left > 0 )
    {
        /* Large data, then slice them to frag and send */
    send = left_bytes>comport->frag_size ? comport->frag_size : left_bytes;
    rv = write(comport->fd, ptr, send);
           if( rv<0 )
           {
               rv = -4;
               goto CleanUp;
           }
    left_bytes -= rv;
    ptr += rv;
        bytes = left>comport->fragsize ? comport->fragsize : left;
        rv = write(comport->fd, ptr, bytes);
        if( rv<0 )
        {
            rv = -3;
            break;
        }
        left -= rv;
        ptr += rv;
    }
    rv = 0;
CleanUp:
    return rv;
}
/*
 *  description: read data from $comport in $timeout <ms> to $buf no more than $bufsize bytes
 *  description: read data from $comport in $timeout <ms> to $buf no more than $buf_size bytes
 * return value: the actual read data bytes, <0: read failure
 */
int comport_recv(comport_t *comport, char *buf, int bufsize, unsigned long timeout)
int comport_recv(comport_t *comport, char *buf, int buf_size, unsigned long timeout)
{
    int                         rv = 0;
    int                         iRet;
    fd_set                      rdfds, exfds;
    struct timeval              stTime;
    struct timeval              to, *to_ptr = NULL;
    int                         ret, rv = 0;
    int                         bytes = 0;
    if ( !comport || !buf || bufsize<=0 )
    if ( !comport || !buf || buf_size<=0 )
    {
        COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__);
        rv = -1;
        goto CleanUp;
        dbg_print("invalid input arugments\n");
        return -1;
    }
    if ( comport->fd < 0 )
    {
        COM_PRINT("%s() comport not connected.\n", __FUNCTION__);
        rv = -2;
        goto CleanUp;
        dbg_print("Serail port not opened\n");
        return -2;
    }
    memset(buf, 0, buf_size);
    FD_ZERO(&rdfds);
    FD_ZERO(&exfds);
    FD_SET(comport->fd, &rdfds);
    FD_SET(comport->fd, &exfds);
    if (0xFFFFFFFF != timeout)
    if( TIMEOUT_NONE != timeout )
    {
        stTime.tv_sec = (time_t) (timeout / 1000);
        stTime.tv_usec = (long)(1000 * (timeout % 1000));
        iRet = select(comport->fd + 1, &rdfds, 0, &exfds, &stTime);
        if (0 == iRet)
        {
            rv = 0;
            goto CleanUp;
        }
        else if (0 < iRet)
        {
            if (0 != FD_ISSET(comport->fd, &exfds))
            {
                rv = -6;
                COM_PRINT("Error checking recv status.\n");
                goto CleanUp;
            }
            if (0 == FD_ISSET(comport->fd, &rdfds))
            {
                rv = 0;
                COM_PRINT("No incoming data.\n");
                goto CleanUp;
            }
        }
        else
        {
            if (EINTR == errno)
            {
                COM_PRINT("catch interrupt signal.\n");
                rv = 0;
            }
            else
            {
                COM_PRINT("Check recv status failure.\n");
                rv = -7;
            }
            goto CleanUp;
        }
        to.tv_sec = (time_t) (timeout / 1000);
        to.tv_usec = (long)(1000 * (timeout % 1000));
        to_ptr = &to;
    }
    usleep(10000); /* sleep for 10ms for data incoming */
    // Get data from Com port
    iRet = read(comport->fd, buf, bufsize);
    if (0 > iRet)
    while( 1 )
    {
        if (EINTR == errno)
            rv = 0;      // Interrupted signal catched
        else
            rv = -3;      // Failed to read Com port
        goto CleanUp;
    }
#if 0
    {
        int   i=0;
        printf("Receive %d bytes data: \n", iRet);
        for(i=0; i<iRet; i++)
        /* check got data arrive or not */
        ret = select(comport->fd+1, &rdfds, 0, &exfds, to_ptr);
        if( ret<0 )
        {
            printf("0x%02x ", buf[i]);
            /* EINTR means catch interrupt signal */
            dbg_print("comport select() failed: %s\n", strerror(errno));
            rv = EINTR==errno ? 0 : -3;
            break;
        }
        printf("\n");
        else if( 0 == ret ) /* timeout */
        {
            break;
        }
        /* read data from comport */
        ret = read(comport->fd, buf+bytes, buf_size-bytes);
        if(ret <= 0)
        {
            dbg_print("comport read() failed: %s\n", strerror(errno));
            break;
        }
        bytes += ret;
        if( bytes >= buf_size )
            break;
        /* try to read data in 1ms again, if no data arrive it will break */
        to.tv_sec = 0;
        to.tv_usec = 10000;
        to_ptr = &to;
    }
#endif
    rv = iRet;
    if( !rv )
        rv = bytes;
CleanUp:
    return rv;
}
@@ -484,11 +445,11 @@
 *  Output Args: NONE
 * Return Value: NONE
 *************************************************************************************/
void set_settings(comport_t * comport, const char *settings)
static inline void set_settings(comport_t * comport, const char *settings)
{
    if( !settings || !comport )
    {
        COM_PRINT("%s() get invalid input arguments.\n", __FUNCTION__);
        dbg_print("invalid input arugments\n");
        return ;
    }
@@ -556,50 +517,4 @@
            break;
    }
}
#ifdef  COM_DEBUG
void disp_settings(comport_t *comport)
{
    COM_PRINT("Device:\t\t\t\"%s\"\n", comport->devname);
    COM_PRINT("Baudrate:\t\t%ld\n", comport->baudrate);
    COM_PRINT("DataBit:\t\t\'%d\'\n", comport->databit);
    switch (comport->parity)
    {
        case 0:
            COM_PRINT("Parity:\t\t\t\'N\'\n");
            break;
        case 1:
            COM_PRINT("Parity:\t\t\t\'O\'\n");
            break;
        case 2:
            COM_PRINT("Parity:\t\t\t\'E\'\n");
            break;
        case 3:
            COM_PRINT("Parity:\t\t\t\'S\'\n");
            break;
    }
    COM_PRINT("StopBit:\t\t\'%ld\'\n", (long int)comport->stopbit);
    switch (comport->flowctrl)
    {
        case 0:
            COM_PRINT("FlowCtrl:\t\t\'N\'\n");
            break;
        case 1:
            COM_PRINT("FlowCtrl:\t\t\'S\'\n");
            break;
        case 2:
            COM_PRINT("FlowCtrl:\t\t\'H\'\n");
            break;
        case 3:
            COM_PRINT("FlowCtrl:\t\t\'B\'\n");
            break;
    }
    COM_PRINT("\n");
    return;
}
#endif
booster/comport.h
@@ -1,17 +1,18 @@
/*********************************************************************************
 *      Copyright:  (C) 2018 LingYun IoT System Studio
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  comport.h
 *    Description:  This head file is for the common TTY/Serial port operator library
 *       Filename:  comport.c
 *    Description:  This file is linux comport common API functions
 *
 *        Version:  1.0.0(10/17/2018~)
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "10/17/2018 03:33:25 PM"
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#ifndef  __COMPORT_H_
#define  __COMPORT_H_
#ifndef  _COMPORT_H_
#define  _COMPORT_H_
#include  <stdio.h>
#include  <stdlib.h>
@@ -27,36 +28,25 @@
#include  <sys/stat.h>
#include  <sys/select.h>
#ifndef DEVNAME_LEN
#define DEVNAME_LEN          32
#endif
//#define COM_DEBUG
#ifdef  COM_DEBUG
#define COM_PRINT(format,args...) printf(format, ##args)
#else
#define COM_PRINT(format,args...) do{} while(0);
#endif
#define CONFIG_DEF_FRAGSIZE    128
typedef struct comport_s
{
    char           devname[DEVNAME_LEN];
    char           devname[32];
    unsigned char  databit, parity, stopbit, flowctrl;
    long           baudrate;
    int            fd;
    int            frag_size;
    int            fragsize; /* frag size when do large data send */
} comport_t;
/*
 *  description: Open the comport specified by $comport
 *  description: Open the comport and returned by $comport
 *
 *   input args: $comport:  corresponding comport point
 *   input args: $comport:  corresponding comport handler
 *               $devname:  The comport device name path, such as '/dev/ttyS3'
 *               $baudrate:  The baudrate, such as 115200
 *               $settings:  The databit,parity,stopbit,flowctrl settings, such as '8N1N'
 *               $baudrate: The baudrate, such as 115200
 *               $settings: The databit,parity,stopbit,flowctrl settings, such as '8N1N'
 *
 * return value: The comport opened file description, <0 means failure
 */
@@ -64,21 +54,21 @@
/*
 *  description: close comport
 *   input args: $comport:  corresponding comport point
 *   input args: $comport:  corresponding comport handler
 */
extern void comport_close(comport_t *comport);
/*
 *  description: write $send_bytes bytes data from $buf to $comport
 *  description: write $bytes $data to $comport
 * return value: 0: write ok  <0: write failure
 */
extern int  comport_send(comport_t *comport, char *buf, int send_bytes);
extern int  comport_send(comport_t *comport, char *data, int data_bytes);
/*
 *  description: read data from $comport in $timeout <ms> to $buf no more than $bufsize bytes
 *  description: read data from $comport in $timeout <ms> to $buf no more than $buf_size bytes
 * return value: the actual read data bytes, <0: read failure
 */
extern int  comport_recv(comport_t *comport, char *buf, int bufsize, unsigned long timeout);
#define TIMEOUT_NONE           0
extern int  comport_recv(comport_t *comport, char *buf, int buf_size, unsigned long timeout);
#endif
booster/dictionary.c
@@ -3,6 +3,7 @@
   @file    dictionary.c
   @author  N. Devillard
   @brief   Implements a dictionary for string variables.
   @url     https://github.com/ndevilla/iniparser
   This module implements a simple dictionary object, i.e. a list
   of string/string associations. This object is useful to store e.g.
@@ -140,7 +141,7 @@
/**
  @brief    Create a new dictionary object.
  @param    size    Optional initial size of the dictionary.
  @return   1 newly allocated dictionary objet.
  @return   1 newly allocated dictionary object.
  This function allocates a new dictionary object of given size and returns
  it. If you do not know in advance (roughly) the number of entries in the
booster/dictionary.h
@@ -4,6 +4,7 @@
   @file    dictionary.h
   @author  N. Devillard
   @brief   Implements a dictionary for string variables.
   @url     https://github.com/ndevilla/iniparser
   This module implements a simple dictionary object, i.e. a list
   of string/string associations. This object is useful to store e.g.
@@ -73,7 +74,7 @@
/**
  @brief    Create a new dictionary object.
  @param    size    Optional initial size of the dictionary.
  @return   1 newly allocated dictionary objet.
  @return   1 newly allocated dictionary object.
  This function allocates a new dictionary object of given size and returns
  it. If you do not know in advance (roughly) the number of entries in the
booster/esp32.c
New file
@@ -0,0 +1,166 @@
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  esp32.c
 *    Description:  This file is ESP32 high level logic API functions
 *
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#include "logger.h"
#include "esp32.h"
int esp32_init_module(comport_t *comport)
{
    int             rv;
    char            version[256] = {0};
    if( !comport )
        return -1;
    rv = esp32_reset(comport);
    if( rv < 0)
    {
        log_error("Reset ESP32 WiFi module failed: %d\n", rv);
        return rv;
    }
    esp32_set_echo(comport, DISABLE);
    esp32_set_sysstore(comport, ENABLE);
    rv = esp32_get_firmware(comport, version, sizeof(version));
    if( rv < 0)
    {
        log_error("Query ESP32 firmware version failed: %d\n", rv);
        return rv;
    }
    log_info("ESP32 firmware version:\n%s\n", version);
    return 0;
}
int esp32_setup_softap(comport_t *comport, char *ssid, char *pwd)
{
    esp32_set_wmode(comport, MODE_SOFTAP, ENABLE);
    esp32_set_ipaddr(comport, MODE_SOFTAP, DEF_SOFTAP_IPADDR, DEF_SOFTAP_IPADDR);
    esp32_set_dhcp(comport, MODE_SOFTAP, ENABLE);
    esp32_set_softap(comport, ssid, pwd, 11, MODE_WPA2PSK);
    return 0;
}
int esp32_join_network(comport_t *comport, char *ssid, char *pwd)
{
    int             rv, status = 0;
    int             times=10;
    char            buf[128] = {0};
    if( !comport )
        return -1;
    esp32_join_status(comport, &status, buf);
    if( 2==status && !strcmp(buf, ssid) )
    {
        log_info("ESP32 connected to \"%s\" already\n", ssid);
        return 0;
    }
    esp32_set_wmode(comport, MODE_STATION, ENABLE);
    esp32_set_dhcp(comport, MODE_STATION, ENABLE);
    rv = esp32_connect_ap(comport, ssid, pwd);
    if( rv < 0 )
    {
        log_error("connect to AP \"%s\" failed, rv=%d", ssid, rv);
        return rv;
    }
    while(times--)
    {
        rv = esp32_join_status(comport, &status, buf);
        if( 2 == status )
        {
            log_info("ESP32 connected to \"%s\" already\n", ssid);
            return 0;
        }
        rv = -3;
    }
    return rv;
}
int esp32_check_network(comport_t *comport)
{
    int             rv, times=5;
    char            ip[IP_LEN] = {0};
    char            gateway[IP_LEN] = {0};
    memset(ip, 0, sizeof(ip));
    memset(gateway, 0, sizeof(gateway));
    rv = esp32_get_ipaddr(comport, MODE_STATION, ip, gateway);
    if( rv<0 )
        return rv;
    if( !strlen(ip) || !strlen(gateway) )
        return -3;
    log_info("IP address: %s, netmask: %s\n", ip, gateway);
    while( times-- )
    {
        if( !(rv=esp32_ping(comport, gateway, 5000)) )
        {
            rv = 0;
            break;
        }
    }
    return 0;
}
int esp32_setup_tcp_server(comport_t *comport, int port)
{
    int             rv;
    rv = esp32_set_socket_mux(comport, ENABLE);
    if(rv<0)
        return rv;
    rv = esp32_set_socket_clients(comport, 2);
    if(rv<0)
        return rv;
    rv = esp32_set_tcp_server(comport, port);
    if(rv<0)
        return rv;
    rv = esp32_set_socket_timeout(comport, 600);
    if(rv<0)
        return rv;
    return 0;
}
int esp32_setup_tcp_client(comport_t *comport, char *host, int port)
{
    int             rv;
    rv = esp32_set_tcp_client(comport, DISABLE, host, port);
    if(rv<0)
        return rv;
    return 0;
}
booster/esp32.h
New file
@@ -0,0 +1,35 @@
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  esp32.h
 *    Description:  This file is ESP32 high level logic API functions
 *
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#ifndef  _ESP32_H_
#define  _ESP32_H_
#include "at-esp32.h"
#define DEF_SOFTAP_IPADDR    "192.168.8.1"
#define DEF_SOFTAP_SSID      "Router_ESP32"
#define DEF_SOFTAP_PWD       "12345678"
extern int esp32_init_module(comport_t *comport);
extern int esp32_setup_softap(comport_t *comport, char *ssid, char *pwd);
extern int esp32_join_network(comport_t *comport, char *ssid, char *pwd);
extern int esp32_check_network(comport_t *comport);
extern int esp32_setup_tcp_server(comport_t *comport, int port);
extern int esp32_setup_tcp_client(comport_t *comport, char *host, int port);
#endif   /* ----- #ifndef _ESP32_H_  ----- */
booster/iniparser.c
@@ -1,10 +1,10 @@
/*-------------------------------------------------------------------------*/
/**
   @file    iniparser.c v4.1
   @file    iniparser.c
   @author  N. Devillard
   @url     https://github.com/ndevilla/iniparser
   @brief   Parser for ini files.
   @url     https://github.com/ndevilla/iniparser
*/
/*--------------------------------------------------------------------------*/
/*---------------------------- Includes ------------------------------------*/
@@ -719,7 +719,7 @@
    char line    [ASCIILINESZ+1] ;
    char section [ASCIILINESZ+1] ;
    char key     [ASCIILINESZ+1] ;
    char tmp     [(ASCIILINESZ * 2) + 1] ;
    char tmp     [(ASCIILINESZ * 2) + 2] ;
    char val     [ASCIILINESZ+1] ;
    int  last=0 ;
booster/iniparser.h
@@ -1,10 +1,10 @@
/*-------------------------------------------------------------------------*/
/**
   @file    iniparser.h v4.1
   @file    iniparser.h
   @author  N. Devillard
   @url     https://github.com/ndevilla/iniparser
   @brief   Parser for ini files.
   @url     https://github.com/ndevilla/iniparser
*/
/*--------------------------------------------------------------------------*/
booster/linux_list.h
@@ -66,18 +66,18 @@
 */
struct list_head {
    struct list_head *next, *prev;
    struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)
    struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
    list->next = list;
    list->prev = list;
}
/*
@@ -87,13 +87,13 @@
 * the prev/next entries already!
 */
static inline void __list_add(struct list_head *new,
                  struct list_head *prev,
                  struct list_head *next)
                  struct list_head *prev,
                  struct list_head *next)
{
    next->prev = new;
    new->next = next;
    new->prev = prev;
    prev->next = new;
    next->prev = new;
    new->next = next;
    new->prev = prev;
    prev->next = new;
}
/**
@@ -106,7 +106,7 @@
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
    __list_add(new, head, head->next);
    __list_add(new, head, head->next);
}
/**
@@ -119,7 +119,7 @@
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
    __list_add(new, head->prev, head);
    __list_add(new, head->prev, head);
}
/*
@@ -131,8 +131,8 @@
 */
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
    next->prev = prev;
    prev->next = next;
    next->prev = prev;
    prev->next = next;
}
/**
@@ -143,9 +143,9 @@
 */
static inline void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    entry->next = LIST_POISON1;
    entry->prev = LIST_POISON2;
    __list_del(entry->prev, entry->next);
    entry->next = LIST_POISON1;
    entry->prev = LIST_POISON2;
}
/**
@@ -156,19 +156,19 @@
 * If @old was empty, it will be overwritten.
 */
static inline void list_replace(struct list_head *old,
                struct list_head *new)
                struct list_head *new)
{
    new->next = old->next;
    new->next->prev = new;
    new->prev = old->prev;
    new->prev->next = new;
    new->next = old->next;
    new->next->prev = new;
    new->prev = old->prev;
    new->prev->next = new;
}
static inline void list_replace_init(struct list_head *old,
                    struct list_head *new)
                    struct list_head *new)
{
    list_replace(old, new);
    INIT_LIST_HEAD(old);
    list_replace(old, new);
    INIT_LIST_HEAD(old);
}
/**
@@ -177,8 +177,8 @@
 */
static inline void list_del_init(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    INIT_LIST_HEAD(entry);
    __list_del(entry->prev, entry->next);
    INIT_LIST_HEAD(entry);
}
/**
@@ -188,8 +188,8 @@
 */
static inline void list_move(struct list_head *list, struct list_head *head)
{
    __list_del(list->prev, list->next);
    list_add(list, head);
    __list_del(list->prev, list->next);
    list_add(list, head);
}
/**
@@ -198,10 +198,10 @@
 * @head: the head that will follow our entry
 */
static inline void list_move_tail(struct list_head *list,
                  struct list_head *head)
                  struct list_head *head)
{
    __list_del(list->prev, list->next);
    list_add_tail(list, head);
    __list_del(list->prev, list->next);
    list_add_tail(list, head);
}
/**
@@ -210,9 +210,9 @@
 * @head: the head of the list
 */
static inline int list_is_last(const struct list_head *list,
                const struct list_head *head)
                const struct list_head *head)
{
    return list->next == head;
    return list->next == head;
}
/**
@@ -221,7 +221,7 @@
 */
static inline int list_empty(const struct list_head *head)
{
    return head->next == head;
    return head->next == head;
}
/**
@@ -239,8 +239,8 @@
 */
static inline int list_empty_careful(const struct list_head *head)
{
    struct list_head *next = head->next;
    return (next == head) && (next == head->prev);
    struct list_head *next = head->next;
    return (next == head) && (next == head->prev);
}
/**
@@ -249,19 +249,19 @@
 */
static inline int list_is_singular(const struct list_head *head)
{
    return !list_empty(head) && (head->next == head->prev);
    return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
        struct list_head *head, struct list_head *entry)
        struct list_head *head, struct list_head *entry)
{
    struct list_head *new_first = entry->next;
    list->next = head->next;
    list->next->prev = list;
    list->prev = entry;
    entry->next = list;
    head->next = new_first;
    new_first->prev = head;
    struct list_head *new_first = entry->next;
    list->next = head->next;
    list->next->prev = list;
    list->prev = entry;
    entry->next = list;
    head->next = new_first;
    new_first->prev = head;
}
/**
@@ -269,7 +269,7 @@
 * @list: a new list to add all removed entries
 * @head: a list with entries
 * @entry: an entry within head, could be the head itself
 *    and if so we won't cut the list
 *  and if so we won't cut the list
 *
 * This helper moves the initial part of @head, up to and
 * including @entry, from @head to @list. You should
@@ -279,31 +279,31 @@
 *
 */
static inline void list_cut_position(struct list_head *list,
        struct list_head *head, struct list_head *entry)
        struct list_head *head, struct list_head *entry)
{
    if (list_empty(head))
        return;
    if (list_is_singular(head) &&
        (head->next != entry && head != entry))
        return;
    if (entry == head)
        INIT_LIST_HEAD(list);
    else
        __list_cut_position(list, head, entry);
    if (list_empty(head))
        return;
    if (list_is_singular(head) &&
        (head->next != entry && head != entry))
        return;
    if (entry == head)
        INIT_LIST_HEAD(list);
    else
        __list_cut_position(list, head, entry);
}
static inline void __list_splice(const struct list_head *list,
                 struct list_head *prev,
                 struct list_head *next)
                 struct list_head *prev,
                 struct list_head *next)
{
    struct list_head *first = list->next;
    struct list_head *last = list->prev;
    struct list_head *first = list->next;
    struct list_head *last = list->prev;
    first->prev = prev;
    prev->next = first;
    first->prev = prev;
    prev->next = first;
    last->next = next;
    next->prev = last;
    last->next = next;
    next->prev = last;
}
/**
@@ -312,10 +312,10 @@
 * @head: the place to add it in the first list.
 */
static inline void list_splice(const struct list_head *list,
                struct list_head *head)
                struct list_head *head)
{
    if (!list_empty(list))
        __list_splice(list, head, head->next);
    if (!list_empty(list))
        __list_splice(list, head, head->next);
}
/**
@@ -324,10 +324,10 @@
 * @head: the place to add it in the first list.
 */
static inline void list_splice_tail(struct list_head *list,
                struct list_head *head)
                struct list_head *head)
{
    if (!list_empty(list))
        __list_splice(list, head->prev, head);
    if (!list_empty(list))
        __list_splice(list, head->prev, head);
}
/**
@@ -338,12 +338,12 @@
 * The list at @list is reinitialised
 */
static inline void list_splice_init(struct list_head *list,
                    struct list_head *head)
                    struct list_head *head)
{
    if (!list_empty(list)) {
        __list_splice(list, head, head->next);
        INIT_LIST_HEAD(list);
    }
    if (!list_empty(list)) {
        __list_splice(list, head, head->next);
        INIT_LIST_HEAD(list);
    }
}
/**
@@ -355,47 +355,47 @@
 * The list at @list is reinitialised
 */
static inline void list_splice_tail_init(struct list_head *list,
                     struct list_head *head)
                     struct list_head *head)
{
    if (!list_empty(list)) {
        __list_splice(list, head->prev, head);
        INIT_LIST_HEAD(list);
    }
    if (!list_empty(list)) {
        __list_splice(list, head->prev, head);
        INIT_LIST_HEAD(list);
    }
}
/**
 * list_entry - get the struct for this entry
 * @ptr:    the &struct list_head pointer.
 * @type:    the type of the struct this is embedded in.
 * @member:    the name of the list_struct within the struct.
 * @ptr:    the &struct list_head pointer.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)
    container_of(ptr, type, member)
/**
 * list_first_entry - get the first element from a list
 * @ptr:    the list head to take the element from.
 * @type:    the type of the struct this is embedded in.
 * @member:    the name of the list_struct within the struct.
 * @ptr:    the list head to take the element from.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list_struct within the struct.
 *
 * Note, that list is expected to be not empty.
 */
#define list_first_entry(ptr, type, member) \
    list_entry((ptr)->next, type, member)
    list_entry((ptr)->next, type, member)
/**
 * list_for_each    -    iterate over a list
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:    the head for your list.
 * list_for_each    -   iterate over a list
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:   the head for your list.
 */
#define list_for_each(pos, head) \
    for (pos = (head)->next; prefetch(pos->next), pos != (head); \
        pos = pos->next)
    for (pos = (head)->next; prefetch(pos->next), pos != (head); \
        pos = pos->next)
/**
 * __list_for_each    -    iterate over a list
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:    the head for your list.
 * __list_for_each  -   iterate over a list
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:   the head for your list.
 *
 * This variant differs from list_for_each() in that it's the
 * simplest possible list iteration code, no prefetching is done.
@@ -403,170 +403,170 @@
 * or 1 entry) most of the time.
 */
#define __list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)
    for (pos = (head)->next; pos != (head); pos = pos->next)
/**
 * list_for_each_prev    -    iterate over a list backwards
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:    the head for your list.
 * list_for_each_prev   -   iterate over a list backwards
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:   the head for your list.
 */
#define list_for_each_prev(pos, head) \
    for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
        pos = pos->prev)
    for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
        pos = pos->prev)
/**
 * list_for_each_safe - iterate over a list safe against removal of list entry
 * @pos:    the &struct list_head to use as a loop cursor.
 * @n:        another &struct list_head to use as temporary storage
 * @head:    the head for your list.
 * @pos:    the &struct list_head to use as a loop cursor.
 * @n:      another &struct list_head to use as temporary storage
 * @head:   the head for your list.
 */
#define list_for_each_safe(pos, n, head) \
    for (pos = (head)->next, n = pos->next; pos != (head); \
        pos = n, n = pos->next)
    for (pos = (head)->next, n = pos->next; pos != (head); \
        pos = n, n = pos->next)
/**
 * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
 * @pos:    the &struct list_head to use as a loop cursor.
 * @n:        another &struct list_head to use as temporary storage
 * @head:    the head for your list.
 * @pos:    the &struct list_head to use as a loop cursor.
 * @n:      another &struct list_head to use as temporary storage
 * @head:   the head for your list.
 */
#define list_for_each_prev_safe(pos, n, head) \
    for (pos = (head)->prev, n = pos->prev; \
         prefetch(pos->prev), pos != (head); \
         pos = n, n = pos->prev)
    for (pos = (head)->prev, n = pos->prev; \
         prefetch(pos->prev), pos != (head); \
         pos = n, n = pos->prev)
/**
 * list_for_each_entry    -    iterate over list of given type
 * @pos:    the type * to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 * list_for_each_entry  -   iterate over list of given type
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 */
#define list_for_each_entry(pos, head, member)                \
    for (pos = list_entry((head)->next, typeof(*pos), member);    \
         prefetch(pos->member.next), &pos->member != (head);    \
         pos = list_entry(pos->member.next, typeof(*pos), member))
#define list_for_each_entry(pos, head, member)              \
    for (pos = list_entry((head)->next, typeof(*pos), member);  \
         prefetch(pos->member.next), &pos->member != (head);    \
         pos = list_entry(pos->member.next, typeof(*pos), member))
/**
 * list_for_each_entry_reverse - iterate backwards over list of given type.
 * @pos:    the type * to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 */
#define list_for_each_entry_reverse(pos, head, member)            \
    for (pos = list_entry((head)->prev, typeof(*pos), member);    \
         prefetch(pos->member.prev), &pos->member != (head);    \
         pos = list_entry(pos->member.prev, typeof(*pos), member))
#define list_for_each_entry_reverse(pos, head, member)          \
    for (pos = list_entry((head)->prev, typeof(*pos), member);  \
         prefetch(pos->member.prev), &pos->member != (head);    \
         pos = list_entry(pos->member.prev, typeof(*pos), member))
/**
 * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
 * @pos:    the type * to use as a start point
 * @head:    the head of the list
 * @member:    the name of the list_struct within the struct.
 * @pos:    the type * to use as a start point
 * @head:   the head of the list
 * @member: the name of the list_struct within the struct.
 *
 * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
 */
#define list_prepare_entry(pos, head, member) \
    ((pos) ? : list_entry(head, typeof(*pos), member))
    ((pos) ? : list_entry(head, typeof(*pos), member))
/**
 * list_for_each_entry_continue - continue iteration over list of given type
 * @pos:    the type * to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Continue to iterate over list of given type, continuing after
 * the current position.
 */
#define list_for_each_entry_continue(pos, head, member)         \
    for (pos = list_entry(pos->member.next, typeof(*pos), member);    \
         prefetch(pos->member.next), &pos->member != (head);    \
         pos = list_entry(pos->member.next, typeof(*pos), member))
#define list_for_each_entry_continue(pos, head, member)         \
    for (pos = list_entry(pos->member.next, typeof(*pos), member);  \
         prefetch(pos->member.next), &pos->member != (head);    \
         pos = list_entry(pos->member.next, typeof(*pos), member))
/**
 * list_for_each_entry_continue_reverse - iterate backwards from the given point
 * @pos:    the type * to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Start to iterate over list of given type backwards, continuing after
 * the current position.
 */
#define list_for_each_entry_continue_reverse(pos, head, member)        \
    for (pos = list_entry(pos->member.prev, typeof(*pos), member);    \
         prefetch(pos->member.prev), &pos->member != (head);    \
         pos = list_entry(pos->member.prev, typeof(*pos), member))
#define list_for_each_entry_continue_reverse(pos, head, member)     \
    for (pos = list_entry(pos->member.prev, typeof(*pos), member);  \
         prefetch(pos->member.prev), &pos->member != (head);    \
         pos = list_entry(pos->member.prev, typeof(*pos), member))
/**
 * list_for_each_entry_from - iterate over list of given type from the current point
 * @pos:    the type * to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Iterate over list of given type, continuing from current position.
 */
#define list_for_each_entry_from(pos, head, member)            \
    for (; prefetch(pos->member.next), &pos->member != (head);    \
         pos = list_entry(pos->member.next, typeof(*pos), member))
#define list_for_each_entry_from(pos, head, member)         \
    for (; prefetch(pos->member.next), &pos->member != (head);  \
         pos = list_entry(pos->member.next, typeof(*pos), member))
/**
 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @pos:    the type * to use as a loop cursor.
 * @n:        another type * to use as temporary storage
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 * @pos:    the type * to use as a loop cursor.
 * @n:      another type * to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 */
#define list_for_each_entry_safe(pos, n, head, member)            \
    for (pos = list_entry((head)->next, typeof(*pos), member),    \
        n = list_entry(pos->member.next, typeof(*pos), member);    \
         &pos->member != (head);                    \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
#define list_for_each_entry_safe(pos, n, head, member)          \
    for (pos = list_entry((head)->next, typeof(*pos), member),  \
        n = list_entry(pos->member.next, typeof(*pos), member); \
         &pos->member != (head);                    \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
 * list_for_each_entry_safe_continue
 * @pos:    the type * to use as a loop cursor.
 * @n:        another type * to use as temporary storage
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 * @pos:    the type * to use as a loop cursor.
 * @n:      another type * to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Iterate over list of given type, continuing after current point,
 * safe against removal of list entry.
 */
#define list_for_each_entry_safe_continue(pos, n, head, member)         \
    for (pos = list_entry(pos->member.next, typeof(*pos), member),        \
        n = list_entry(pos->member.next, typeof(*pos), member);        \
         &pos->member != (head);                        \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
#define list_for_each_entry_safe_continue(pos, n, head, member)         \
    for (pos = list_entry(pos->member.next, typeof(*pos), member),      \
        n = list_entry(pos->member.next, typeof(*pos), member);     \
         &pos->member != (head);                        \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
 * list_for_each_entry_safe_from
 * @pos:    the type * to use as a loop cursor.
 * @n:        another type * to use as temporary storage
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 * @pos:    the type * to use as a loop cursor.
 * @n:      another type * to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Iterate over list of given type from current point, safe against
 * removal of list entry.
 */
#define list_for_each_entry_safe_from(pos, n, head, member)            \
    for (n = list_entry(pos->member.next, typeof(*pos), member);        \
         &pos->member != (head);                        \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
#define list_for_each_entry_safe_from(pos, n, head, member)         \
    for (n = list_entry(pos->member.next, typeof(*pos), member);        \
         &pos->member != (head);                        \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
 * list_for_each_entry_safe_reverse
 * @pos:    the type * to use as a loop cursor.
 * @n:        another type * to use as temporary storage
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 * @pos:    the type * to use as a loop cursor.
 * @n:      another type * to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Iterate backwards over list of given type, safe against removal
 * of list entry.
 */
#define list_for_each_entry_safe_reverse(pos, n, head, member)        \
    for (pos = list_entry((head)->prev, typeof(*pos), member),    \
        n = list_entry(pos->member.prev, typeof(*pos), member);    \
         &pos->member != (head);                    \
         pos = n, n = list_entry(n->member.prev, typeof(*n), member))
#define list_for_each_entry_safe_reverse(pos, n, head, member)      \
    for (pos = list_entry((head)->prev, typeof(*pos), member),  \
        n = list_entry(pos->member.prev, typeof(*pos), member); \
         &pos->member != (head);                    \
         pos = n, n = list_entry(n->member.prev, typeof(*n), member))
/*
 * Double linked lists with a single pointer list head.
@@ -576,11 +576,11 @@
 */
struct hlist_head {
    struct hlist_node *first;
    struct hlist_node *first;
};
struct hlist_node {
    struct hlist_node *next, **pprev;
    struct hlist_node *next, **pprev;
};
#define HLIST_HEAD_INIT { .first = NULL }
@@ -588,134 +588,134 @@
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
    h->next = NULL;
    h->pprev = NULL;
    h->next = NULL;
    h->pprev = NULL;
}
static inline int hlist_unhashed(const struct hlist_node *h)
{
    return !h->pprev;
    return !h->pprev;
}
static inline int hlist_empty(const struct hlist_head *h)
{
    return !h->first;
    return !h->first;
}
static inline void __hlist_del(struct hlist_node *n)
{
    struct hlist_node *next = n->next;
    struct hlist_node **pprev = n->pprev;
    *pprev = next;
    if (next)
        next->pprev = pprev;
    struct hlist_node *next = n->next;
    struct hlist_node **pprev = n->pprev;
    *pprev = next;
    if (next)
        next->pprev = pprev;
}
static inline void hlist_del(struct hlist_node *n)
{
    __hlist_del(n);
    n->next = LIST_POISON1;
    n->pprev = LIST_POISON2;
    __hlist_del(n);
    n->next = LIST_POISON1;
    n->pprev = LIST_POISON2;
}
static inline void hlist_del_init(struct hlist_node *n)
{
    if (!hlist_unhashed(n)) {
        __hlist_del(n);
        INIT_HLIST_NODE(n);
    }
    if (!hlist_unhashed(n)) {
        __hlist_del(n);
        INIT_HLIST_NODE(n);
    }
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
    struct hlist_node *first = h->first;
    n->next = first;
    if (first)
        first->pprev = &n->next;
    h->first = n;
    n->pprev = &h->first;
    struct hlist_node *first = h->first;
    n->next = first;
    if (first)
        first->pprev = &n->next;
    h->first = n;
    n->pprev = &h->first;
}
/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
                    struct hlist_node *next)
                    struct hlist_node *next)
{
    n->pprev = next->pprev;
    n->next = next;
    next->pprev = &n->next;
    *(n->pprev) = n;
    n->pprev = next->pprev;
    n->next = next;
    next->pprev = &n->next;
    *(n->pprev) = n;
}
static inline void hlist_add_after(struct hlist_node *n,
                    struct hlist_node *next)
                    struct hlist_node *next)
{
    next->next = n->next;
    n->next = next;
    next->pprev = &n->next;
    next->next = n->next;
    n->next = next;
    next->pprev = &n->next;
    if(next->next)
        next->next->pprev  = &next->next;
    if(next->next)
        next->next->pprev  = &next->next;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
    for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
         pos = pos->next)
    for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
         pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
    for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
         pos = n)
    for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
         pos = n)
/**
 * hlist_for_each_entry    - iterate over list of given type
 * @tpos:    the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the hlist_node within the struct.
 * hlist_for_each_entry - iterate over list of given type
 * @tpos:   the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry(tpos, pos, head, member)             \
    for (pos = (head)->first;                     \
         pos && ({ prefetch(pos->next); 1;}) &&             \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
#define hlist_for_each_entry(tpos, pos, head, member)            \
    for (pos = (head)->first;                    \
         pos && ({ prefetch(pos->next); 1;}) &&          \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
/**
 * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
 * @tpos:    the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @member:    the name of the hlist_node within the struct.
 * @tpos:   the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @member: the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry_continue(tpos, pos, member)         \
    for (pos = (pos)->next;                         \
         pos && ({ prefetch(pos->next); 1;}) &&             \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
#define hlist_for_each_entry_continue(tpos, pos, member)         \
    for (pos = (pos)->next;                      \
         pos && ({ prefetch(pos->next); 1;}) &&          \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
/**
 * hlist_for_each_entry_from - iterate over a hlist continuing from current point
 * @tpos:    the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @member:    the name of the hlist_node within the struct.
 * @tpos:   the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @member: the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry_from(tpos, pos, member)             \
    for (; pos && ({ prefetch(pos->next); 1;}) &&             \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
#define hlist_for_each_entry_from(tpos, pos, member)             \
    for (; pos && ({ prefetch(pos->next); 1;}) &&            \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
/**
 * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @tpos:    the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @n:        another &struct hlist_node to use as temporary storage
 * @head:    the head for your list.
 * @member:    the name of the hlist_node within the struct.
 * @tpos:   the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @n:      another &struct hlist_node to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry_safe(tpos, pos, n, head, member)         \
    for (pos = (head)->first;                     \
         pos && ({ n = pos->next; 1; }) &&                 \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = n)
#define hlist_for_each_entry_safe(tpos, pos, n, head, member)        \
    for (pos = (head)->first;                    \
         pos && ({ n = pos->next; 1; }) &&               \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = n)
#endif
booster/list.h
New file
@@ -0,0 +1,723 @@
/*********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  list.h
 *    Description:  This file is copied from Linux kernel, which provide link list API.
 *
 *        Version:  1.0.0(08/09/2020)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "08/09/2020 02:24:34 AM"
 *
 ********************************************************************************/
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
#include <linux/stddef.h>
/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:   the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#undef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})
/*
 * Architectures might want to move the poison pointer offset
 * into some well-recognized area such as 0xdead000000000000,
 * that is also not mappable by user-space exploits:
 */
#ifdef CONFIG_ILLEGAL_POINTER_VALUE
# define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
#else
# define POISON_POINTER_DELTA 0
#endif
/*
 * These are non-NULL pointers that will result in page faults
 * under normal circumstances, used to verify that nobody uses
 * non-initialized list entries.
 */
#define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2  ((void *) 0x00200200 + POISON_POINTER_DELTA)
#ifndef ARCH_HAS_PREFETCH
#define ARCH_HAS_PREFETCH
static inline void prefetch(const void *x) {;}
#endif
/*
 * Simple doubly linked list implementation.
 *
 * Some of the internal functions ("__xxx") are useful when
 * manipulating whole lists rather than single entries, as
 * sometimes we already know the next/prev entries and we can
 * generate better code by using them directly rather than
 * using the generic single-entry routines.
 */
struct list_head {
    struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}
/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_add(struct list_head *new,
                  struct list_head *prev,
                  struct list_head *next)
{
    next->prev = new;
    new->next = next;
    new->prev = prev;
    prev->next = new;
}
/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
    __list_add(new, head, head->next);
}
/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
    __list_add(new, head->prev, head);
}
/*
 * Delete a list entry by making the prev/next entries
 * point to each other.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
    next->prev = prev;
    prev->next = next;
}
/**
 * list_del - deletes entry from list.
 * @entry: the element to delete from the list.
 * Note: list_empty() on entry does not return true after this, the entry is
 * in an undefined state.
 */
static inline void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    entry->next = LIST_POISON1;
    entry->prev = LIST_POISON2;
}
/**
 * list_replace - replace old entry by new one
 * @old : the element to be replaced
 * @new : the new element to insert
 *
 * If @old was empty, it will be overwritten.
 */
static inline void list_replace(struct list_head *old,
                struct list_head *new)
{
    new->next = old->next;
    new->next->prev = new;
    new->prev = old->prev;
    new->prev->next = new;
}
static inline void list_replace_init(struct list_head *old,
                    struct list_head *new)
{
    list_replace(old, new);
    INIT_LIST_HEAD(old);
}
/**
 * list_del_init - deletes entry from list and reinitialize it.
 * @entry: the element to delete from the list.
 */
static inline void list_del_init(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    INIT_LIST_HEAD(entry);
}
/**
 * list_move - delete from one list and add as another's head
 * @list: the entry to move
 * @head: the head that will precede our entry
 */
static inline void list_move(struct list_head *list, struct list_head *head)
{
    __list_del(list->prev, list->next);
    list_add(list, head);
}
/**
 * list_move_tail - delete from one list and add as another's tail
 * @list: the entry to move
 * @head: the head that will follow our entry
 */
static inline void list_move_tail(struct list_head *list,
                  struct list_head *head)
{
    __list_del(list->prev, list->next);
    list_add_tail(list, head);
}
/**
 * list_is_last - tests whether @list is the last entry in list @head
 * @list: the entry to test
 * @head: the head of the list
 */
static inline int list_is_last(const struct list_head *list,
                const struct list_head *head)
{
    return list->next == head;
}
/**
 * list_empty - tests whether a list is empty
 * @head: the list to test.
 */
static inline int list_empty(const struct list_head *head)
{
    return head->next == head;
}
/**
 * list_empty_careful - tests whether a list is empty and not being modified
 * @head: the list to test
 *
 * Description:
 * tests whether a list is empty _and_ checks that no other CPU might be
 * in the process of modifying either member (next or prev)
 *
 * NOTE: using list_empty_careful() without synchronization
 * can only be safe if the only activity that can happen
 * to the list entry is list_del_init(). Eg. it cannot be used
 * if another CPU could re-list_add() it.
 */
static inline int list_empty_careful(const struct list_head *head)
{
    struct list_head *next = head->next;
    return (next == head) && (next == head->prev);
}
/**
 * list_is_singular - tests whether a list has just one entry.
 * @head: the list to test.
 */
static inline int list_is_singular(const struct list_head *head)
{
    return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
        struct list_head *head, struct list_head *entry)
{
    struct list_head *new_first = entry->next;
    list->next = head->next;
    list->next->prev = list;
    list->prev = entry;
    entry->next = list;
    head->next = new_first;
    new_first->prev = head;
}
/**
 * list_cut_position - cut a list into two
 * @list: a new list to add all removed entries
 * @head: a list with entries
 * @entry: an entry within head, could be the head itself
 *  and if so we won't cut the list
 *
 * This helper moves the initial part of @head, up to and
 * including @entry, from @head to @list. You should
 * pass on @entry an element you know is on @head. @list
 * should be an empty list or a list you do not care about
 * losing its data.
 *
 */
static inline void list_cut_position(struct list_head *list,
        struct list_head *head, struct list_head *entry)
{
    if (list_empty(head))
        return;
    if (list_is_singular(head) &&
        (head->next != entry && head != entry))
        return;
    if (entry == head)
        INIT_LIST_HEAD(list);
    else
        __list_cut_position(list, head, entry);
}
static inline void __list_splice(const struct list_head *list,
                 struct list_head *prev,
                 struct list_head *next)
{
    struct list_head *first = list->next;
    struct list_head *last = list->prev;
    first->prev = prev;
    prev->next = first;
    last->next = next;
    next->prev = last;
}
/**
 * list_splice - join two lists, this is designed for stacks
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
static inline void list_splice(const struct list_head *list,
                struct list_head *head)
{
    if (!list_empty(list))
        __list_splice(list, head, head->next);
}
/**
 * list_splice_tail - join two lists, each list being a queue
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
static inline void list_splice_tail(struct list_head *list,
                struct list_head *head)
{
    if (!list_empty(list))
        __list_splice(list, head->prev, head);
}
/**
 * list_splice_init - join two lists and reinitialise the emptied list.
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * The list at @list is reinitialised
 */
static inline void list_splice_init(struct list_head *list,
                    struct list_head *head)
{
    if (!list_empty(list)) {
        __list_splice(list, head, head->next);
        INIT_LIST_HEAD(list);
    }
}
/**
 * list_splice_tail_init - join two lists and reinitialise the emptied list
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * Each of the lists is a queue.
 * The list at @list is reinitialised
 */
static inline void list_splice_tail_init(struct list_head *list,
                     struct list_head *head)
{
    if (!list_empty(list)) {
        __list_splice(list, head->prev, head);
        INIT_LIST_HEAD(list);
    }
}
/**
 * list_entry - get the struct for this entry
 * @ptr:    the &struct list_head pointer.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)
/**
 * list_first_entry - get the first element from a list
 * @ptr:    the list head to take the element from.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list_struct within the struct.
 *
 * Note, that list is expected to be not empty.
 */
#define list_first_entry(ptr, type, member) \
    list_entry((ptr)->next, type, member)
/**
 * list_for_each    -   iterate over a list
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:   the head for your list.
 */
#define list_for_each(pos, head) \
    for (pos = (head)->next; prefetch(pos->next), pos != (head); \
        pos = pos->next)
/**
 * __list_for_each  -   iterate over a list
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:   the head for your list.
 *
 * This variant differs from list_for_each() in that it's the
 * simplest possible list iteration code, no prefetching is done.
 * Use this for code that knows the list to be very short (empty
 * or 1 entry) most of the time.
 */
#define __list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)
/**
 * list_for_each_prev   -   iterate over a list backwards
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:   the head for your list.
 */
#define list_for_each_prev(pos, head) \
    for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
        pos = pos->prev)
/**
 * list_for_each_safe - iterate over a list safe against removal of list entry
 * @pos:    the &struct list_head to use as a loop cursor.
 * @n:      another &struct list_head to use as temporary storage
 * @head:   the head for your list.
 */
#define list_for_each_safe(pos, n, head) \
    for (pos = (head)->next, n = pos->next; pos != (head); \
        pos = n, n = pos->next)
/**
 * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
 * @pos:    the &struct list_head to use as a loop cursor.
 * @n:      another &struct list_head to use as temporary storage
 * @head:   the head for your list.
 */
#define list_for_each_prev_safe(pos, n, head) \
    for (pos = (head)->prev, n = pos->prev; \
         prefetch(pos->prev), pos != (head); \
         pos = n, n = pos->prev)
/**
 * list_for_each_entry  -   iterate over list of given type
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 */
#define list_for_each_entry(pos, head, member)              \
    for (pos = list_entry((head)->next, typeof(*pos), member);  \
         prefetch(pos->member.next), &pos->member != (head);    \
         pos = list_entry(pos->member.next, typeof(*pos), member))
/**
 * list_for_each_entry_reverse - iterate backwards over list of given type.
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 */
#define list_for_each_entry_reverse(pos, head, member)          \
    for (pos = list_entry((head)->prev, typeof(*pos), member);  \
         prefetch(pos->member.prev), &pos->member != (head);    \
         pos = list_entry(pos->member.prev, typeof(*pos), member))
/**
 * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
 * @pos:    the type * to use as a start point
 * @head:   the head of the list
 * @member: the name of the list_struct within the struct.
 *
 * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
 */
#define list_prepare_entry(pos, head, member) \
    ((pos) ? : list_entry(head, typeof(*pos), member))
/**
 * list_for_each_entry_continue - continue iteration over list of given type
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Continue to iterate over list of given type, continuing after
 * the current position.
 */
#define list_for_each_entry_continue(pos, head, member)         \
    for (pos = list_entry(pos->member.next, typeof(*pos), member);  \
         prefetch(pos->member.next), &pos->member != (head);    \
         pos = list_entry(pos->member.next, typeof(*pos), member))
/**
 * list_for_each_entry_continue_reverse - iterate backwards from the given point
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Start to iterate over list of given type backwards, continuing after
 * the current position.
 */
#define list_for_each_entry_continue_reverse(pos, head, member)     \
    for (pos = list_entry(pos->member.prev, typeof(*pos), member);  \
         prefetch(pos->member.prev), &pos->member != (head);    \
         pos = list_entry(pos->member.prev, typeof(*pos), member))
/**
 * list_for_each_entry_from - iterate over list of given type from the current point
 * @pos:    the type * to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Iterate over list of given type, continuing from current position.
 */
#define list_for_each_entry_from(pos, head, member)         \
    for (; prefetch(pos->member.next), &pos->member != (head);  \
         pos = list_entry(pos->member.next, typeof(*pos), member))
/**
 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @pos:    the type * to use as a loop cursor.
 * @n:      another type * to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 */
#define list_for_each_entry_safe(pos, n, head, member)          \
    for (pos = list_entry((head)->next, typeof(*pos), member),  \
        n = list_entry(pos->member.next, typeof(*pos), member); \
         &pos->member != (head);                    \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
 * list_for_each_entry_safe_continue
 * @pos:    the type * to use as a loop cursor.
 * @n:      another type * to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Iterate over list of given type, continuing after current point,
 * safe against removal of list entry.
 */
#define list_for_each_entry_safe_continue(pos, n, head, member)         \
    for (pos = list_entry(pos->member.next, typeof(*pos), member),      \
        n = list_entry(pos->member.next, typeof(*pos), member);     \
         &pos->member != (head);                        \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
 * list_for_each_entry_safe_from
 * @pos:    the type * to use as a loop cursor.
 * @n:      another type * to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Iterate over list of given type from current point, safe against
 * removal of list entry.
 */
#define list_for_each_entry_safe_from(pos, n, head, member)         \
    for (n = list_entry(pos->member.next, typeof(*pos), member);        \
         &pos->member != (head);                        \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
 * list_for_each_entry_safe_reverse
 * @pos:    the type * to use as a loop cursor.
 * @n:      another type * to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the list_struct within the struct.
 *
 * Iterate backwards over list of given type, safe against removal
 * of list entry.
 */
#define list_for_each_entry_safe_reverse(pos, n, head, member)      \
    for (pos = list_entry((head)->prev, typeof(*pos), member),  \
        n = list_entry(pos->member.prev, typeof(*pos), member); \
         &pos->member != (head);                    \
         pos = n, n = list_entry(n->member.prev, typeof(*n), member))
/*
 * Double linked lists with a single pointer list head.
 * Mostly useful for hash tables where the two pointer list head is
 * too wasteful.
 * You lose the ability to access the tail in O(1).
 */
struct hlist_head {
    struct hlist_node *first;
};
struct hlist_node {
    struct hlist_node *next, **pprev;
};
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
    h->next = NULL;
    h->pprev = NULL;
}
static inline int hlist_unhashed(const struct hlist_node *h)
{
    return !h->pprev;
}
static inline int hlist_empty(const struct hlist_head *h)
{
    return !h->first;
}
static inline void __hlist_del(struct hlist_node *n)
{
    struct hlist_node *next = n->next;
    struct hlist_node **pprev = n->pprev;
    *pprev = next;
    if (next)
        next->pprev = pprev;
}
static inline void hlist_del(struct hlist_node *n)
{
    __hlist_del(n);
    n->next = LIST_POISON1;
    n->pprev = LIST_POISON2;
}
static inline void hlist_del_init(struct hlist_node *n)
{
    if (!hlist_unhashed(n)) {
        __hlist_del(n);
        INIT_HLIST_NODE(n);
    }
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
    struct hlist_node *first = h->first;
    n->next = first;
    if (first)
        first->pprev = &n->next;
    h->first = n;
    n->pprev = &h->first;
}
/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
                    struct hlist_node *next)
{
    n->pprev = next->pprev;
    n->next = next;
    next->pprev = &n->next;
    *(n->pprev) = n;
}
static inline void hlist_add_after(struct hlist_node *n,
                    struct hlist_node *next)
{
    next->next = n->next;
    n->next = next;
    next->pprev = &n->next;
    if(next->next)
        next->next->pprev  = &next->next;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
    for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
         pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
    for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
         pos = n)
/**
 * hlist_for_each_entry - iterate over list of given type
 * @tpos:   the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @head:   the head for your list.
 * @member: the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry(tpos, pos, head, member)            \
    for (pos = (head)->first;                    \
         pos && ({ prefetch(pos->next); 1;}) &&          \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
/**
 * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
 * @tpos:   the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @member: the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry_continue(tpos, pos, member)         \
    for (pos = (pos)->next;                      \
         pos && ({ prefetch(pos->next); 1;}) &&          \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
/**
 * hlist_for_each_entry_from - iterate over a hlist continuing from current point
 * @tpos:   the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @member: the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry_from(tpos, pos, member)             \
    for (; pos && ({ prefetch(pos->next); 1;}) &&            \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
/**
 * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @tpos:   the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @n:      another &struct hlist_node to use as temporary storage
 * @head:   the head for your list.
 * @member: the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry_safe(tpos, pos, n, head, member)        \
    for (pos = (head)->first;                    \
         pos && ({ n = pos->next; 1; }) &&               \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = n)
#endif
booster/logger.c
@@ -1,383 +1,250 @@
/*********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  logger.c
 *    Description:  This file is the linux infrastructural logger system library
 *    Description:  This file is common logger API functions
 *
 *        Version:  1.0.0(08/08/2020~)
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "08/08/2020 04:24:01 PM"
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <pthread.h>
#include "logger.h"
#define PRECISE_TIME_FACTOR 1000
typedef void (*log_LockFn)(void *udata, int lock);
static unsigned long log_rollback_size = LOG_ROLLBACK_NONE;
static struct {
    char        file[32]; /* logger file name */
    FILE       *fp;       /* logger file pointer */
    long        size;     /* logger file max size */
    int         level;    /* logger level */
    log_LockFn  lockfn;   /* lock function */
    void       *udata;    /* lock data */
} L;
/* This library is not thread safe */
static logger_t *logger = NULL;
static const char *level_names[] = {
    "ERROR",
    "WARN",
    "INFO",
    "DEBUG",
    "TRACE"
};
char *log_str[LOG_LEVEL_MAX + 1] = { "", "E", "W", "N", "D", "I", "M" };
static const char *level_colors[] = {
    "\x1b[31m",
    "\x1b[33m",
    "\x1b[32m",
    "\x1b[36m",
    "\x1b[94m"
};
#define LOG_TIME_FMT       "%Y-%m-%d %H:%M:%S"
static void log_signal_handler(int sig)
static inline void time_to_str(char *buf)
{
    if(!logger)
        return ;
    struct timeval   tv;
    struct tm       *tm;
    int              len;
    if (sig == SIGHUP)
    gettimeofday(&tv, NULL);
    tm = localtime(&tv.tv_sec);
    len = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%06d ",
            tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
            tm->tm_hour, tm->tm_min, tm->tm_sec, (int)tv.tv_usec);
    buf[len] = '\0';
}
static void mutex_lock(void *udata, int lock)
{
    int              err;
    pthread_mutex_t *l = (pthread_mutex_t *) udata;
    if (lock)
    {
        signal(SIGHUP, log_signal_handler);
        log_err("SIGHUP received - reopenning log file [%s]", logger->file);
        log_reopen();
        if ( (err = pthread_mutex_lock(l)) != 0 )
            log_error("Unable to lock log lock: %s", strerror(err));
    }
    else
    {
        if ( (err = pthread_mutex_unlock(l) != 0) )
            log_error("Unable to unlock log lock: %s", strerror(err));
    }
}
static void logger_banner(char *prefix)
int log_open(char *fname, int level, int size, int lock)
{
    if(!logger)
        return ;
    FILE            *fp;
    fprintf(logger->fp, "%s log \"%s\" on level [%s] size [%lu] KiB, log system version %s\n",
            prefix, logger->file, log_str[logger->level], log_rollback_size / 1024, LOG_VERSION_STR);
#ifdef LOG_FILE_LINE
    fprintf(logger->fp, " [ Date ]    [ Time ]   [ Level ]  [ File/Line ]  [ Message ]\n");
#else
    fprintf(logger->fp, " [ Date ]    [ Time ]   [ Level ]  [ Message ]\n");
#endif
    fprintf(logger->fp, "-------------------------------------------------------------\n");
}
    L.level = level;
    L.size = size*1024;
static void check_and_rollback(void)
{
    if(!logger)
        return ;
    if (log_rollback_size != LOG_ROLLBACK_NONE)
    if( !fname || !strcmp(fname, "console") || !strcmp(fname, "stderr") )
    {
        long _curOffset = ftell(logger->fp);
        if ((_curOffset != -1) && (_curOffset >= log_rollback_size))
        strcpy(L.file, "console");
        L.fp = stderr;
        L.size = 0; /* console don't need rollback */
    }
    else
    {
        if ( !(fp = fopen(fname, "a+")) )
        {
            char cmd[512];
            snprintf(cmd, sizeof(cmd), "cp -f %s %s.roll", logger->file, logger->file);
            system(cmd);
            if (-1 == fseek(logger->fp, 0L, SEEK_SET))
                fprintf(logger->fp, "log rollback fseek failed \n");
            rewind(logger->fp);
            truncate(logger->file, 0);
            logger_banner("Already rollback");
            fprintf(stderr, "%s() failed: %s\n", __func__, strerror(errno));
            return -2;
        }
        L.fp = fp;
        strncpy(L.file, fname, sizeof(L.file));
    }
}
/* log_size unit is KB */
int log_open(logger_t *log, char *log_file, int level, int log_size)
{
    struct sigaction              act;
    char                         *filemode;
    if(!log)
    if( lock )
    {
        printf("ERROR: Invalid input arguments\n");
        return -1;
        static pthread_mutex_t     log_lock;
        pthread_mutex_init(&log_lock, NULL);
        L.udata = (void *)&log_lock;
        L.lockfn = mutex_lock;
    }
    /* set static global $logger point to argument $log  */
    logger = log;
    fprintf(L.fp, "\n");
    log_info("logger system(%s) start: file:\"%s\", level:%s, maxsize:%luKiB\n\n",
            LOG_VERSION, L.file, level_names[level], size);
    /* use standard error as output */
    if( !log_file || log_file[0]=='\0' || !strcmp(log_file, LOG_STDERR))
    {
        strncpy(logger->file, LOG_STDERR, FILENAME_LEN);
        logger->flag |= FLAG_LOGGER_CONSOLE;
        logger->level = level;
        logger->fp = stderr;
        log_rollback_size = LOG_ROLLBACK_NONE;
        goto OUT;
    }
    strncpy(logger->file, log_file, FILENAME_LEN);
    logger->flag |= FLAG_LOGGER_FILE;
    logger->level = level;
    logger->size = log_size;
    log_rollback_size = log_size <= 0 ? LOG_ROLLBACK_NONE : log_size*1024;    /* Unit KiB */
    filemode = (log_rollback_size==LOG_ROLLBACK_NONE) ? "a+" : "w+";
    logger->fp = fopen(logger->file, filemode);
    if ( !logger->fp )
    {
        fprintf(stderr, "Open log file \"%s\" in %s failure: %s\n", logger->file, filemode, strerror(errno));
        return -2;
    }
    act.sa_handler = log_signal_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGHUP, &act, NULL);
  OUT:
    logger_banner("Initialize");
    return 0;
}
void log_close(void)
{
    if (!logger || !logger->fp )
        return;
    if( L.fp && L.fp!=stderr )
        fclose(L.fp);
    logger_banner("\nTerminate");
    logger_raw("\n\n\n\n");
    if (L.udata )
        pthread_mutex_destroy( L.udata);
}
    fflush(logger->fp);
static void log_rollback(void)
{
    char       cmd[128]={0};
    long       fsize;
    fclose(logger->fp);
    logger->fp = NULL;
    /* don't need rollback */
    if(L.size <= 0 )
        return ;
    fsize = ftell(L.fp);
    if( fsize < L.size )
        return ;
    /* backup current log file  */
    snprintf(cmd, sizeof(cmd), "cp %s %s.bak", L.file, L.file);
    system(cmd);
    /* rollback file */
    fseek(L.fp, 0, SEEK_SET);
    truncate(L.file, 0);
    fprintf(L.fp, "\n");
    log_info("logger system(%s) rollback: file:\"%s\", level:%s, maxsize:%luKiB\n\n",
            LOG_VERSION, L.file, level_names[L.level], L.size/1024);
    return ;
}
int log_reopen(void)
void _log_write(int level, const char *file, int line, const char *fmt, ...)
{
    int rc = 0;
    char *filemode;
    va_list    args;
    char       time_string[100];
    if( !logger )
        return -1;
    if (logger->flag & FLAG_LOGGER_CONSOLE )
    {
        fflush(logger->fp);
        logger->fp = stderr;
        return 0;
    }
    if (logger->fp)
    {
        log_close();
        filemode = log_rollback_size==LOG_ROLLBACK_NONE ? "a+" : "w+";
        logger->fp = fopen(logger->file, filemode);
        if (logger->fp == NULL)
            rc = -2;
    }
    else
    {
        rc = -3;
    }
    if (!rc)
    {
        logger_banner("\nReopen");
    }
    return rc;
}
void logger_raw(const char *fmt, ...)
{
    va_list argp;
    if (!logger || !logger->fp)
    if ( !L.fp || level>L.level )
        return;
    check_and_rollback();
    /* Acquire lock */
    if ( L.lockfn )
        L.lockfn(L.udata, 1);
    va_start(argp, fmt);
    vfprintf(logger->fp, fmt, argp);
    va_end(argp);
}
    log_rollback();
static void cp_printout(char *level, char *fmt, va_list argp)
{
    char buf[MAX_LOG_MESSAGE_LEN];
    struct tm *local;
    struct timeval now;
    char timestr[256];
    /* check and rollback file */
    time_to_str(time_string);
    if(!logger)
        return ;
    check_and_rollback();
    gettimeofday(&now, NULL);
    local = localtime(&now.tv_sec);
    strftime(timestr, 256, LOG_TIME_FMT, local);
    vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp);
#ifdef DUMPLICATE_OUTPUT
    printf("%s.%03ld [%s] : %s",
           timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, buf);
#endif
    if (logger->fp)
        fprintf(logger->fp, "%s.%03ld [%s] : %s", timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, buf);
    if (logger->fp)
        fflush(logger->fp);
}
static void cp_printout_line(char *level, char *fmt, char *file, int line, va_list argp)
{
    char buf[MAX_LOG_MESSAGE_LEN];
    struct tm *local;
    struct timeval now;
    char timestr[256];
    if(!logger)
        return ;
    check_and_rollback();
    gettimeofday(&now, NULL);
    local = localtime(&now.tv_sec);
    strftime(timestr, 256, LOG_TIME_FMT, local);
    vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp);
#ifdef DUMPLICATE_OUTPUT
    printf("[%s.%03ld] <%s> <%s:%04d> : %s",
           timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, file, line, buf);
#endif
    if (logger->fp)
    /* Log to stderr */
    if ( L.fp == stderr )
    {
        fprintf(logger->fp, "[%s.%03ld] <%s> <%s:%04d> : %s",
                timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, file, line, buf);
        fflush(logger->fp);
        fprintf(L.fp, "%s %s %-5s\x1b[0m \x1b[90m%s:%03d:\x1b[0m ",
                time_string, level_colors[level], level_names[level], file, line);
    }
}
void logger_str(int level, const char *msg)
{
    if (!logger || level>logger->level)
        return;
    check_and_rollback();
    if (logger->fp)
        fwrite(msg, 1, strlen(msg), logger->fp);
    if(logger->fp)
        fflush(logger->fp);
}
void logger_min(int level, char *fmt, ...)
{
    va_list argp;
    if (!logger || level>logger->level)
        return;
    va_start(argp, fmt);
    cp_printout(log_str[level], fmt, argp);
    va_end(argp);
}
void logger_line(int level, char *file, int line, char *fmt, ...)
{
    va_list argp;
    if (!logger || level>logger->level)
        return;
    va_start(argp, fmt);
    cp_printout_line(log_str[level], fmt, file, line, argp);
    va_end(argp);
}
#define LINELEN 81
#define CHARS_PER_LINE 16
static char *print_char =
    "                "
    "                "
    " !\"#$%&'()*+,-./"
    "0123456789:;<=>?"
    "@ABCDEFGHIJKLMNO"
    "PQRSTUVWXYZ[\\]^_"
    "`abcdefghijklmno"
    "pqrstuvwxyz{|}~ "
    "                "
    "                "
    " ???????????????"
    "????????????????"
    "????????????????"
    "????????????????"
    "????????????????"
    "????????????????";
void logger_dump(int level, char *buf, int len)
{
    int rc;
    int idx;
    char prn[LINELEN];
    char lit[CHARS_PER_LINE + 2];
    char hc[4];
    short line_done = 1;
    if (!logger || level>logger->level)
        return;
    rc = len;
    idx = 0;
    lit[CHARS_PER_LINE] = '\0';
    while (rc > 0)
    else /* Log to file */
    {
        if (line_done)
            snprintf(prn, LINELEN, "%08X: ", idx);
        fprintf(L.fp, "%s %-5s %s:%03d: ", time_string, level_names[level], file, line);
    }
        do
    va_start(args, fmt);
    vfprintf(L.fp, fmt, args);
    va_end(args);
    fflush(L.fp);
    /* Release lock */
    if ( L.lockfn )
        L.lockfn(L.udata, 0);
}
void log_dump(int level, const char *prompt, char *buf, size_t len)
{
    int                 i, j, ofset;
    char                line[256];
    unsigned char       c;
    unsigned char      *buffer = (unsigned char *)buf;
    if (!L.fp || level>L.level)
        return;
    if( prompt )
        _log_write(level, __FILE__, __LINE__, "%s\r\n", prompt);
    for(i=0; i<len; i+=16)
    {
        ofset = snprintf(line, sizeof(line), "%04x: ", i);
        /* print hex representation, and print spaces if end of buffer */
        for(j=0; j<16; j++)
        {
            unsigned char c = buf[idx];
            snprintf(hc, 4, "%02X ", c);
            strncat(prn, hc, LINELEN);
            lit[idx % CHARS_PER_LINE] = print_char[c];
            if(i+j < len)
                ofset += snprintf(line+ofset, sizeof(line)-ofset, "%02x ", buffer[i+j]);
            else
                ofset += snprintf(line+ofset, sizeof(line)-ofset, "   ");
        }
        while (--rc > 0 && (++idx % CHARS_PER_LINE != 0));
        ofset += snprintf(line+ofset, sizeof(line)-ofset, " ");
        line_done = (idx % CHARS_PER_LINE) == 0;
        if (line_done)
        /* print ASCII representation */
        for(j=0; j<16; j++)
        {
#ifdef DUMPLICATE_OUTPUT
            printf("%s  %s\n", prn, lit);
#endif
            if (logger->fp)
                fprintf(logger->fp, "%s  %s\n", prn, lit);
            if (i+j < len)
            {
                c = buffer[i+j];
                ofset += snprintf(line+ofset, sizeof(line)-ofset, "%c", (c>=32 && c<=126) ? c : '.');
            }
            else
            {
                ofset += snprintf(line+ofset, sizeof(line)-ofset, " ");
            }
        }
    }
    if (!line_done)
    {
        int ldx = idx % CHARS_PER_LINE;
        lit[ldx++] = print_char[(int)buf[idx]];
        lit[ldx] = '\0';
        while ((++idx % CHARS_PER_LINE) != 0)
            strncat(prn, "   ", sizeof(prn));
#ifdef DUMPLICATE_OUTPUT
        printf("%s  %s\n", prn, lit);
#endif
        if (logger->fp)
            fprintf(logger->fp, "%s  %s\n", prn, lit);
        if (L.fp)
            fprintf(L.fp, "%s\r\n", line);
    }
}
booster/logger.h
@@ -1,105 +1,68 @@
/********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  logger.h
 *    Description:  This file is the linux infrastructural logger system library
 *    Description:  This file is common logger API functions
 *
 *        Version:  1.0.0(08/08/2020~)
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "08/08/2020 05:16:56 PM"
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#ifndef  _LOGGER_H_
#define  _LOGGER_H_
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/time.h>
#define LOG_VERSION "v0.1"
#define LOG_VERSION_STR             "1.0.0"
#ifndef FILENAME_LEN
#define FILENAME_LEN                64
#endif
#define LOG_STDERR                 "stderr"  /*  Debug mode log file is console */
#define LOG_ROLLBACK_SIZE           512    /* Default rollback log size  */
#define LOG_ROLLBACK_NONE           0      /* Set rollback size to 0 will not rollback  */
#define DEFAULT_TIME_FORMAT         "%Y-%m-%d %H:%M:%S"
#define MAX_LOG_MESSAGE_LEN         0x1000
//#define DUMPLICATE_OUTPUT  /* Log to file and printf on console  */
enum
{
    LOG_LEVEL_DISB = 0,               /*  Disable "Debug" */
    LOG_LEVEL_ERROR,                  /*  Debug Level "ERROR" */
    LOG_LEVEL_WARN,                   /*  Debug Level "warnning" */
    LOG_LEVEL_NRML,                   /*  Debug Level "Normal" */
    LOG_LEVEL_DEBUG,                  /*  Debug Level "Debug" */
    LOG_LEVEL_INFO,                   /*  Debug Level "Information" */
    LOG_LEVEL_MAX,
/* log level */
enum {
    LOG_LEVEL_ERROR,
    LOG_LEVEL_WARN,
    LOG_LEVEL_INFO,
    LOG_LEVEL_DEBUG,
    LOG_LEVEL_TRACE,
    LOG_LEVEL_MAX
};
enum {
    LOG_LOCK_DISABLE, /* disable lock */
    LOG_LOCK_ENABLE,  /* enable lock */
};
#define ROLLBACK_NONE   0
/* description: Initial the logger system
 * arguments  :
 *             $fname: logger file name, NULL/"console"/"stderr" will log to console
 *             $level: logger level above;
 *             $size : logger file max size in KiB
 *             $lock : thread lock enable or not
 * return     : <0: Failed  ==0: Sucessfully
 */
int log_open(char *fname, int level, int size, int lock);
/* logger->flag definition */
#define FLAG_LOGGER_LEVEL_OPT           1<<0 /*  The log level is sepcified by the command option */
/* description: Terminate the logger system */
void log_close(void);
#define FLAG_LOGGER_CONSOLE             1<<1
#define FLAG_LOGGER_FILE                0<<1
typedef struct logger_s
{
    unsigned char      flag;
    char               file[FILENAME_LEN];
    int                level;
    int                size;
/* description: log message into log file. Don't call this function directly. */
void _log_write(int level, const char *file, int line, const char *fmt, ...);
    FILE               *fp;
} logger_t;
extern char *log_str[];
/* description: dump a buffer in hex to logger file */
void log_dump(int level, const char *prompt, char *buf, size_t len);
/* log_size unit is KB */
extern int  log_open(logger_t *logger, char *filename, int level, int log_size);
extern int  log_reopen(void);
extern void log_close(void);
/* function: log message into logger file with different log level */
#define log_trace(...) _log_write(LOG_LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define log_debug(...) _log_write(LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define log_info(...)  _log_write(LOG_LEVEL_INFO,  __FILE__, __LINE__, __VA_ARGS__)
#define log_warn(...)  _log_write(LOG_LEVEL_WARN,  __FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) _log_write(LOG_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__)
/* lowlvel API */
extern void logger_raw(const char *fmt, ...);
extern void logger_min(int level, char *fmt, ...);
extern void logger_line(int level, char *file, int line, char *fmt, ...);
extern void logger_str(int level, const char *msg);
extern void logger_dump(int level, char *buf, int len);
#define LOG_FILE_LINE      /* Log the file and line */
#ifdef LOG_FILE_LINE
#define log_info(fmt, ...)  logger_line(LOG_LEVEL_INFO,  __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define log_dbg(fmt, ...)   logger_line(LOG_LEVEL_DEBUG, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define log_nrml(fmt, ...)  logger_line(LOG_LEVEL_NRML,  __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define log_warn(fmt, ...)  logger_line(LOG_LEVEL_WARN,  __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define log_err(fmt, ...)   logger_line(LOG_LEVEL_ERROR, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#else
#define log_info(fmt, ...)  logger_min(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define log_dbg(fmt, ...)   logger_min(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
#define log_nrml(fmt, ...)  logger_min(LOG_LEVEL_NRML, fmt, ##__VA_ARGS__)
#define log_warn(fmt, ...)  logger_min(LOG_LEVEL_WARN, fmt, ##__VA_ARGS__)
#define log_err(fmt, ...)   logger_min(LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
#endif
#endif   /* ----- #ifndef _LOGGER_H_  ----- */
booster/makefile
@@ -1,13 +1,33 @@
#********************************************************************************
#      Copyright:  (C) 2023 LingYun IoT System Studio
#                  All rights reserved.
#
#       Filename:  Makefile
#    Description:  This file used compile all the source code to static library
#
#        Version:  1.0.0(11/08/23)
#         Author:  Guo Wenxue <guowenxue@gmail.com>
#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
#
#*******************************************************************************
PWD=$(shell pwd )
BUILD_ARCH=$(shell uname -m)
ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
    CROSS_COMPILE?=arm-linux-gnueabihf-
endif
#CROSS_COMPILE=
LIBNAME=$(shell basename ${PWD} )
TOPDIR=$(shell dirname ${PWD} )
CFLAGS+=-D_GNU_SOURCE
all: clean
    @rm -f *.o
    @${CROSSTOOL}gcc ${CFLAGS} -I${TOPDIR} -c *.c
    ${CROSSTOOL}ar -rcs  lib${LIBNAME}.a *.o
    @${CROSS_COMPILE}gcc ${CFLAGS} -I${TOPDIR} -c *.c
    ${CROSS_COMPILE}ar -rcs  lib${LIBNAME}.a *.o
clean:
    @rm -f *.o
@@ -15,4 +35,3 @@
distclean:
    @make clean
    @rm -f cscope.* tags
booster/ringbuf.c
@@ -1,15 +1,16 @@
/********************************************************************************
 *      Copyright:  (C) 2021 LingYun IoT System Studio
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  ringbuf.h
 *    Description:  This head file
 *       Filename:  ringbuf.c
 *    Description:  This file is common ring buffer API functions
 *
 *        Version:  1.0.0(2021年04月29日)
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2021年04月29日 12时18分32秒"
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
#include <string.h>
#include <assert.h>
#include "ringbuf.h"
@@ -100,7 +101,7 @@
unsigned char rb_peek(struct ring_buffer* rb, int index)
{
    assert(index < rb_data_size(rb));
    assert(index < rb_data_size(rb));
    return rb->buffer[((rb->rd_pointer + index) % rb->size)];
    return rb->buffer[((rb->rd_pointer + index) % rb->size)];
}
booster/ringbuf.h
@@ -1,13 +1,13 @@
/********************************************************************************
 *      Copyright:  (C) 2021 LingYun IoT System Studio
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio.
 *                  All rights reserved.
 *
 *       Filename:  ringbuf.h
 *    Description:  This head file
 *    Description:  This file is common ring buffer API functions
 *
 *        Version:  1.0.0(2021年04月29日)
 *        Version:  1.0.0(11/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2021年04月29日 12时18分32秒"
 *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
 *
 ********************************************************************************/
@@ -16,7 +16,7 @@
struct ring_buffer
{
    unsigned char      *buffer;
    unsigned char      *buffer;
    int                 wr_pointer;
    int                 rd_pointer;
    int                 size;
booster/test/gpio-scripts/pinctrl-rzboard
File was deleted
booster/test/logger.c
File was deleted
booster/test/makefile
@@ -1,37 +1,64 @@
#*********************************************************************************
#      Copyright:  (C) 2022 Guo Wenxue
#                  All rights reserved.
#
#       Filename:  Makefile
#    Description:  This Makefile used to compile all the C source code file in current
#                  folder to respective excutable binary files.
#
#        Version:  1.0.0(03/15/2022~)
#                  Author:  Guo Wenxue <guowenxue@gmail.com>
#      ChangeLog:  1, Release initial version on "03/15/2022 01:29:33 PM"
#
#********************************************************************************/
PWD=$(shell pwd)
LIB_PATH=$(shell dirname ${PWD})
LIB_NAME=$(shell basename ${LIB_PATH})
INSTPATH=/tftp
INST_PATH=/tftp
#ARCH ?= i386
#ARCH?=arm926t
ARCH?=arm920t
LIB_PATH=$(shell dirname ${PWD} )
LIB_NAME=$(shell basename ${LIB_PATH} )
#LINK_MODE=STATIC
MODE=PRODUCTION
DEBUG=1
CFLAGS+=-I${LIB_PATH}
LDFLAGS+=-L${LIB_PATH} -l${LIB_NAME} -lpthread
INSTPATH=/tftp
CROSS_COMPILE?=arm-linux-gnueabihf-
export CC=${CROSS_COMPILE}gcc
export CXX=${CROSS_COMPILE}g++
export AR=${CROSS_COMPILE}ar
export AS=${CROSS_COMPILE}as
export RANLIB=${CROSS_COMPILE}ranlib
export STRIP=${CROSS_COMPILE}strip
SRCS = $(wildcard ${VPATH}/*.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
SRCFILES = $(wildcard *.c)
BINARIES=$(SRCFILES:%.c=%)
all: libs binaries
    @make install
CFLAGS+=-I${LIB_PATH}
LDFLAGS+=-L${LIB_PATH} -l${LIB_NAME}
libs:
    make -C ${LIB_PATH}
all: binaries install
binaries:  ${BINARIES}
    @echo " Compile over"
%:  %.c
    ${CROSSTOOL}gcc $(CFLAGS) -o $@ $< $(LDFLAGS)
    $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
install:
    cp ${BINARIES} ${INST_PATH}
    cp $(BINARIES) ${INSTPATH}
clean:
    @rm -f ${BINARIES}
    @rm -f *.log
    @rm -f *.o *.log $(BINARIES)
distclean:
    @make clean
    @make clean -C ${LIB_PATH}
    @rm -f cscope.* tags
distclean: clean
    @rm -f  tags cscope*
.PHONY: clean entry
booster/test/test_logger.c
New file
@@ -0,0 +1,45 @@
/*********************************************************************************
 *      Copyright:  (C) 2012 Guo Wenxue <guowenxue@gmail.com>
 *                  All rights reserved.
 *
 *       Filename:  test_logger.c
 *    Description:  This is the linux logger system test code.
 *
 *        Version:  1.0.0(08/08/2012~)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "08/08/2012 06:51:40 PM"
 *
 ********************************************************************************/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include "logger.h"
int main (int argc, char **argv)
{
    char    buf[256];
    int     i;
    for(i=0; i<sizeof(buf); i++)
        buf[i] = i;
#if 1
    log_open("console", LOG_LEVEL_DEBUG, 0, LOG_LOCK_DISABLE);
#else
    log_open("test.log", LOG_LEVEL_DEBUG, 10, LOG_LOCK_DISABLE);
#endif
    log_error("This is a errorr message\n");
    log_warn("This is a warnning message\n");
    log_info("This is a informat message\n");
    log_debug("This is a debug message\n");
    log_trace("This is a trace message\n");
    log_dump(LOG_LEVEL_DEBUG, "Hex dump buffer content:", buf, sizeof(buf));
    log_close();
    return 0;
}
booster/util_proc.c
@@ -13,12 +13,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include "util_proc.h"
#include "logger.h"
@@ -64,7 +66,7 @@
{
    struct sigaction sigact, sigign;
    log_nrml("Install default signal handler.\n");
    log_info("Install default signal handler.\n");
    /*  Initialize the catch signal structure. */
    sigemptyset(&sigact.sa_mask);
@@ -159,7 +161,7 @@
    if( check_daemon_running(pidfile) )
    {
        log_err("Program already running, process exit now");
        log_error("Program already running, process exit now");
        return -1;
    }
@@ -167,7 +169,7 @@
    {
        if( set_daemon_running(pidfile) < 0 )
        {
            log_err("set program running as daemon failure\n");
            log_error("set program running as daemon failure\n");
            return -2;
        }
    }
@@ -175,7 +177,7 @@
    {
        if( record_daemon_pid(pidfile) < 0 )
        {
            log_err("record program running PID failure\n");
            log_error("record program running PID failure\n");
            return -3;
        }
    }
@@ -209,7 +211,7 @@
    {
        if (mkdir(ipc_dir, mode) < 0)
        {
            log_err("cannot create %s: %s\n", ipc_dir, strerror(errno));
            log_error("cannot create %s: %s\n", ipc_dir, strerror(errno));
            return -1;
        }
@@ -225,11 +227,11 @@
        write(fd, pid, strlen(pid));
        close(fd);
        log_dbg("Record PID<%u> to file %s.\n", getpid(), pid_file);
        log_debug("Record PID<%u> to file %s.\n", getpid(), pid_file);
    }
    else
    {
        log_err("cannot create %s: %s\n", pid_file, strerror(errno));
        log_error("cannot create %s: %s\n", pid_file, strerror(errno));
        return -1;
    }
@@ -257,7 +259,7 @@
    }
    else
    {
        log_err("Can't open PID record file %s: %s\n", pid_file, strerror(errno));
        log_error("Can't open PID record file %s: %s\n", pid_file, strerror(errno));
        return -1;
    }
    return pid;
@@ -356,11 +358,11 @@
int set_daemon_running(const char *pid_file)
{
    daemonize(0, 1);
    log_nrml("Program running as daemon [PID:%d].\n", getpid());
    log_info("Program running as daemon [PID:%d].\n", getpid());
    if (record_daemon_pid(pid_file) < 0)
    {
        log_err("Record PID to file \"%s\" failure.\n", pid_file);
        log_error("Record PID to file \"%s\" failure.\n", pid_file);
        return -2;
    }
booster/util_proc.h
@@ -15,6 +15,7 @@
#define __UTIL_PROC_H_
#include <signal.h>
#include <time.h>
#define PID_ASCII_SIZE  11
@@ -59,9 +60,30 @@
 * |   Low level API     |
 * +---------------------+*/
/* get daemon process ID from $pid_file   */
extern pid_t get_daemon_pid(const char *pid_file);
/* +------------------------+
 * |  inline functions API  |
 * +------------------------+*/
static inline void msleep(unsigned long ms)
{
    struct timespec cSleep;
    unsigned long ulTmp;
    cSleep.tv_sec = ms / 1000;
    if (cSleep.tv_sec == 0)
    {
        ulTmp = ms * 10000;
        cSleep.tv_nsec = ulTmp * 100;
    }
    else
    {
        cSleep.tv_nsec = 0;
    }
    nanosleep(&cSleep, 0);
    return ;
}
#endif