From 5b0985618c8a49ea5ff872486672324120e25361 Mon Sep 17 00:00:00 2001 From: guowenxue <guowenxue@gmail.com> Date: Mon, 10 Jul 2023 15:24:32 +0800 Subject: [PATCH] add gpsd program and booster --- gpsd/booster/comport.h | 69 gpsd/makefile | 57 gpsd/booster/makefile | 18 gpsd/booster/logger.h | 65 gpsd/booster/logger.c | 276 +++ gpsd/gpsd.c | 245 ++ gpsd/booster/dictionary.h | 174 + gpsd/booster/iniparser.c | 837 +++++++++ gpsd/booster/linux_list.h | 723 +++++++ gpsd/booster/comport.c | 515 +++++ gpsd/booster/iniparser.h | 359 +++ gpsd/booster/at-esp32.h | 135 + gpsd/booster/atcmd.c | 296 +++ gpsd/booster/ringbuf.h | 57 gpsd/booster/atcmd.h | 87 gpsd/booster/at-esp32.c | 383 ++++ gpsd/booster/util_proc.h | 67 gpsd/booster/util_proc.c | 432 ++++ gpsd/booster/esp32.c | 161 + gpsd/booster/ringbuf.c | 106 + gpsd/booster/dictionary.c | 381 ++++ gpsd/booster/esp32.h | 30 22 files changed, 5,473 insertions(+), 0 deletions(-) diff --git a/gpsd/booster/at-esp32.c b/gpsd/booster/at-esp32.c new file mode 100644 index 0000000..00a3111 --- /dev/null +++ b/gpsd/booster/at-esp32.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2022 Guo Wenxue + * Author: Guo Wenxue <guowenxue@gmail.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GPL license. + */ + +#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; +} diff --git a/gpsd/booster/at-esp32.h b/gpsd/booster/at-esp32.h new file mode 100644 index 0000000..26febee --- /dev/null +++ b/gpsd/booster/at-esp32.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2022 Guo Wenxue + * Author: Guo Wenxue <guowenxue@gmail.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GPL license. + */ + +#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_ ----- */ diff --git a/gpsd/booster/atcmd.c b/gpsd/booster/atcmd.c new file mode 100644 index 0000000..623bd96 --- /dev/null +++ b/gpsd/booster/atcmd.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2023 Guo Wenxue + * Author: Guo Wenxue <guowenxue@gmail.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GPL license. + */ + +#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; +} + diff --git a/gpsd/booster/atcmd.h b/gpsd/booster/atcmd.h new file mode 100644 index 0000000..a84d564 --- /dev/null +++ b/gpsd/booster/atcmd.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2022 Guo Wenxue + * Author: Guo Wenxue <guowenxue@gmail.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GPL license. + */ + +#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_ ----- */ + diff --git a/gpsd/booster/comport.c b/gpsd/booster/comport.c new file mode 100644 index 0000000..27de29a --- /dev/null +++ b/gpsd/booster/comport.c @@ -0,0 +1,515 @@ +/* + * Copyright (c) 2022 Guo Wenxue + * Author: Guo Wenxue <guowenxue@gmail.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GPL license. + */ + +#include "comport.h" + +#define CONFIG_PRINT_LOGGER +//#define CONFIG_PRINT_STDOUT + +#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 + * + * input args: $comport: corresponding comport point + * $dev_name: 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' + * + * return value: The comport opened file description, <0 means failure + */ +int comport_open(comport_t *comport, const char *devname, long baudrate, const char *settings) +{ + int rv = -1; + struct termios old_cfg, new_cfg; + int old_flags; + long tmp; + + if( !comport || !devname ) + { + dbg_print("invalid input arugments\n"); + return -1; + } + + /*+-----------------------+ + *| open the serial port | + *+-----------------------+*/ + + memset(comport, 0, sizeof(*comport)); + strncpy(comport->devname, devname, sizeof(comport->devname)); + comport->baudrate = baudrate; + comport->fd = -1; + comport->fragsize = CONFIG_DEF_FRAGSIZE; + set_settings(comport, settings); + + if( !strstr(comport->devname, "tty") ) + { + 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 ) + { + dbg_print("comport open \"%s\" failed:%s\n", comport->devname, strerror(errno)); + return -3; + } + + if( (-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0))) + && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK)) ) + { + /* Flush input and output */ + tcflush(comport->fd, TCIOFLUSH); + } + else + { + rv = -4; + goto CleanUp; + } + + if (0 != tcgetattr(comport->fd, &old_cfg)) + { + rv = -5; + goto CleanUp; + } + + + /*+-----------------------+ + *| configure serial port | + *+-----------------------+*/ + + memset(&new_cfg, 0, sizeof(new_cfg)); + new_cfg.c_cflag &= ~CSIZE; + new_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + new_cfg.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + new_cfg.c_oflag &= ~(OPOST); + + /* Set the data bit */ + switch (comport->databit) + { + case 0x07: + new_cfg.c_cflag |= CS7; + break; + case 0x06: + new_cfg.c_cflag |= CS6; + break; + case 0x05: + new_cfg.c_cflag |= CS5; + break; + default: + new_cfg.c_cflag |= CS8; + break; + } + + /* Set the parity */ + switch (comport->parity) + { + case 0x01: /* Odd */ + new_cfg.c_cflag |= (PARENB | PARODD); + new_cfg.c_cflag |= (INPCK | ISTRIP); + break; + case 0x02: /* Even */ + new_cfg.c_cflag |= PARENB; + new_cfg.c_cflag &= ~PARODD;; + new_cfg.c_cflag |= (INPCK | ISTRIP); + break; + case 0x03: + new_cfg.c_cflag &= ~PARENB; + new_cfg.c_cflag &= ~CSTOPB; + break; + default: + new_cfg.c_cflag &= ~PARENB; + } + + /* Set Stop bit */ + if (0x01 != comport->stopbit) + { + new_cfg.c_cflag |= CSTOPB; + } + else + { + new_cfg.c_cflag &= ~CSTOPB; + } + + /* Set flow control */ + switch (comport->flowctrl) + { + 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; + new_cfg.c_iflag &= ~(IXON | IXOFF); + break; + default: /* NONE */ + new_cfg.c_cflag &= ~(CRTSCTS); + new_cfg.c_iflag &= ~(IXON | IXOFF); + break; + } + + /* 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; + case 115200: + tmp = B115200; + break; + case 57600: + tmp = B57600; + break; + + /* Below is POSIX(bits/termios.h) */ + case 38400: + tmp = B38400; + break; + case 19200: + tmp = B19200; + break; + case 9600: + tmp = B9600; + break; + case 4800: + tmp = B4800; + break; + case 2400: + tmp = B2400; + break; + case 1800: + tmp = B1800; + break; + case 1200: + tmp = B1200; + break; + case 600: + tmp = B600; + break; + case 300: + tmp = B300; + break; + case 200: + tmp = B200; + break; + case 150: + tmp = B150; + break; + case 134: + tmp = B134; + break; + case 110: + tmp = B110; + break; + case 75: + tmp = B75; + break; + case 50: + tmp = B50; + break; + default: + tmp = B115200; + } + cfsetispeed(&new_cfg, tmp); + cfsetispeed(&new_cfg, tmp); + + /* Set the Com port timeout settings */ + new_cfg.c_cc[VMIN] = 0; + new_cfg.c_cc[VTIME] = 0; + + tcflush(comport->fd, TCIFLUSH); + if (0 != tcsetattr(comport->fd, TCSANOW, &new_cfg)) + { + rv = -6; // Failed to set device com port settings + goto CleanUp; + } + + rv = comport->fd; + +CleanUp: + return rv; +} + + +/* + * description: close comport + * input args: $comport: corresponding comport point + */ + +void comport_close(comport_t *comport) +{ + if( !comport ) + { + dbg_print("invalid input arugments\n"); + return ; + } + + if ( comport->fd >= 0 ) + { + close(comport->fd); + } + + comport->fd = -1; + return ; +} + +/* + * description: write $data_bytes $data to $comport + * return value: 0: write ok <0: write failure + */ + +int comport_send(comport_t *comport, char *data, int data_bytes) +{ + char *ptr; + int left, bytes = 0; + int rv = 0; + + if( !comport || !data || data_bytes<=0 ) + { + dbg_print("invalid input arugments\n"); + return -1; + } + + if( comport->fd < 0 ) + { + dbg_print("Serail port not opened\n"); + return -2; + } + + ptr = data; + left = data_bytes; + + while( left > 0 ) + { + /* Large data, then slice them to frag and send */ + bytes = left>comport->fragsize ? comport->fragsize : left; + + rv = write(comport->fd, ptr, bytes); + if( rv<0 ) + { + rv = -3; + break; + } + + left -= rv; + ptr += rv; + } + + return rv; +} + + +/* + * 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 buf_size, unsigned long timeout) +{ + fd_set rdfds, exfds; + struct timeval to, *to_ptr = NULL; + int ret, rv = 0; + int bytes = 0; + + if ( !comport || !buf || buf_size<=0 ) + { + dbg_print("invalid input arugments\n"); + return -1; + } + + if ( comport->fd < 0 ) + { + 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( TIMEOUT_NONE != timeout ) + { + to.tv_sec = (time_t) (timeout / 1000); + to.tv_usec = (long)(1000 * (timeout % 1000)); + to_ptr = &to; + } + + while( 1 ) + { + /* check got data arrive or not */ + ret = select(comport->fd+1, &rdfds, 0, &exfds, to_ptr); + if( ret<0 ) + { + /* EINTR means catch interrupt signal */ + dbg_print("comport select() failed: %s\n", strerror(errno)); + rv = EINTR==errno ? 0 : -3; + break; + } + 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; + } + + if( !rv ) + rv = bytes; + + return rv; +} + + +/************************************************************************************** + * Description: Set the comport databit,parity,stopbit,flowctrl into the comport structure + * Input Args: comport: the comport_t pointer + * settings: The databit/parity/stopbit/flowctrl settings as like "8N1N" + * Output Args: NONE + * Return Value: NONE + *************************************************************************************/ +static inline void set_settings(comport_t * comport, const char *settings) +{ + if( !settings || !comport ) + { + dbg_print("invalid input arugments\n"); + return ; + } + + switch (settings[0]) /* data bit */ + { + case '7': + comport->databit = 7; + break; + case '8': + default: + comport->databit = 8; + break; + } + + switch (settings[1]) /* parity */ + { + case 'O': + case 'o': + comport->parity = 1; + break; + case 'E': + case 'e': + comport->parity = 2; + break; + case 'S': + case 's': + comport->parity = 3; + break; + case 'N': + case 'n': + default: + comport->parity = 0; + break; + } + + switch (settings[2]) /* stop bit */ + { + case '0': + comport->stopbit = 0; + break; + case '1': + default: + comport->stopbit = 1; + break; + } + + switch (settings[3]) /* flow control */ + { + case 'S': + case 's': + comport->flowctrl = 1; + break; + case 'H': + case 'h': + comport->flowctrl = 2; + break; + case 'B': + case 'b': + comport->flowctrl = 3; + break; + case 'N': + case 'n': + default: + comport->flowctrl = 0; + break; + } +} + diff --git a/gpsd/booster/comport.h b/gpsd/booster/comport.h new file mode 100644 index 0000000..a5a04b5 --- /dev/null +++ b/gpsd/booster/comport.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 Guo Wenxue + * Author: Guo Wenxue <guowenxue@gmail.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GPL license. + */ + +#ifndef _COMPORT_H_ +#define _COMPORT_H_ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/select.h> + +#define CONFIG_DEF_FRAGSIZE 128 +typedef struct comport_s +{ + char devname[32]; + unsigned char databit, parity, stopbit, flowctrl; + long baudrate; + + int fd; + int fragsize; /* frag size when do large data send */ +} comport_t; + + +/* + * description: Open the comport and returned by $comport + * + * 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' + * + * return value: The comport opened file description, <0 means failure + */ +extern int comport_open(comport_t *comport, const char *devname, long baudrate, const char *settings); + +/* + * description: close comport + * input args: $comport: corresponding comport handler + */ +extern void comport_close(comport_t *comport); + +/* + * description: write $bytes $data to $comport + * return value: 0: write ok <0: write failure + */ +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 $buf_size bytes + * return value: the actual read data bytes, <0: read failure + */ +#define TIMEOUT_NONE 0 +extern int comport_recv(comport_t *comport, char *buf, int buf_size, unsigned long timeout); + +#endif diff --git a/gpsd/booster/dictionary.c b/gpsd/booster/dictionary.c new file mode 100644 index 0000000..0fd5000 --- /dev/null +++ b/gpsd/booster/dictionary.c @@ -0,0 +1,381 @@ +/*-------------------------------------------------------------------------*/ +/** + @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. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ +#include "dictionary.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/** Maximum value size for integers and doubles. */ +#define MAXVALSZ 1024 + +/** Minimal allocated number of entries in a dictionary */ +#define DICTMINSZ 128 + +/** Invalid key token */ +#define DICT_INVALID_KEY ((char*)-1) + +/*--------------------------------------------------------------------------- + Private functions + ---------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ +/** + @brief Duplicate a string + @param s String to duplicate + @return Pointer to a newly allocated string, to be freed with free() + + This is a replacement for strdup(). This implementation is provided + for systems that do not have it. + */ +/*--------------------------------------------------------------------------*/ +static char * xstrdup(const char * s) +{ + char * t ; + size_t len ; + if (!s) + return NULL ; + + len = strlen(s) + 1 ; + t = (char*) malloc(len) ; + if (t) { + memcpy(t, s, len) ; + } + return t ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Double the size of the dictionary + @param d Dictionary to grow + @return This function returns non-zero in case of failure + */ +/*--------------------------------------------------------------------------*/ +static int dictionary_grow(dictionary * d) +{ + char ** new_val ; + char ** new_key ; + unsigned * new_hash ; + + new_val = (char**) calloc(d->size * 2, sizeof *d->val); + new_key = (char**) calloc(d->size * 2, sizeof *d->key); + new_hash = (unsigned*) calloc(d->size * 2, sizeof *d->hash); + if (!new_val || !new_key || !new_hash) { + /* An allocation failed, leave the dictionary unchanged */ + if (new_val) + free(new_val); + if (new_key) + free(new_key); + if (new_hash) + free(new_hash); + return -1 ; + } + /* Initialize the newly allocated space */ + memcpy(new_val, d->val, d->size * sizeof(char *)); + memcpy(new_key, d->key, d->size * sizeof(char *)); + memcpy(new_hash, d->hash, d->size * sizeof(unsigned)); + /* Delete previous data */ + free(d->val); + free(d->key); + free(d->hash); + /* Actually update the dictionary */ + d->size *= 2 ; + d->val = new_val; + d->key = new_key; + d->hash = new_hash; + return 0 ; +} + +/*--------------------------------------------------------------------------- + Function codes + ---------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ +/** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(const char * key) +{ + size_t len ; + unsigned hash ; + size_t i ; + + if (!key) + return 0 ; + + len = strlen(key); + for (hash=0, i=0 ; i<len ; i++) { + hash += (unsigned)key[i] ; + hash += (hash<<10); + hash ^= (hash>>6) ; + } + hash += (hash <<3); + hash ^= (hash >>11); + hash += (hash <<15); + return hash ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @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 + dictionary, give size=0. + */ +/*-------------------------------------------------------------------------*/ +dictionary * dictionary_new(size_t size) +{ + dictionary * d ; + + /* If no size was specified, allocate space for DICTMINSZ */ + if (size<DICTMINSZ) size=DICTMINSZ ; + + d = (dictionary*) calloc(1, sizeof *d) ; + + if (d) { + d->size = size ; + d->val = (char**) calloc(size, sizeof *d->val); + d->key = (char**) calloc(size, sizeof *d->key); + d->hash = (unsigned*) calloc(size, sizeof *d->hash); + } + return d ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * d) +{ + ssize_t i ; + + if (d==NULL) return ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]!=NULL) + free(d->key[i]); + if (d->val[i]!=NULL) + free(d->val[i]); + } + free(d->val); + free(d->key); + free(d->hash); + free(d); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +const char * dictionary_get(const dictionary * d, const char * key, const char * def) +{ + unsigned hash ; + ssize_t i ; + + hash = dictionary_hash(key); + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + /* Compare hash */ + if (hash==d->hash[i]) { + /* Compare string, to avoid hash collisions */ + if (!strcmp(key, d->key[i])) { + return d->val[i] ; + } + } + } + return def ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * d, const char * key, const char * val) +{ + ssize_t i ; + unsigned hash ; + + if (d==NULL || key==NULL) return -1 ; + + /* Compute hash for this key */ + hash = dictionary_hash(key) ; + /* Find if value is already in dictionary */ + if (d->n>0) { + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (hash==d->hash[i]) { /* Same hash value */ + if (!strcmp(key, d->key[i])) { /* Same key */ + /* Found a value: modify and return */ + if (d->val[i]!=NULL) + free(d->val[i]); + d->val[i] = (val ? xstrdup(val) : NULL); + /* Value has been modified: return */ + return 0 ; + } + } + } + } + /* Add a new value */ + /* See if dictionary needs to grow */ + if (d->n==d->size) { + /* Reached maximum size: reallocate dictionary */ + if (dictionary_grow(d) != 0) + return -1; + } + + /* Insert key in the first empty slot. Start at d->n and wrap at + d->size. Because d->n < d->size this will necessarily + terminate. */ + for (i=d->n ; d->key[i] ; ) { + if(++i == d->size) i = 0; + } + /* Copy key */ + d->key[i] = xstrdup(key); + d->val[i] = (val ? xstrdup(val) : NULL) ; + d->hash[i] = hash; + d->n ++ ; + return 0 ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, const char * key) +{ + unsigned hash ; + ssize_t i ; + + if (key == NULL || d == NULL) { + return; + } + + hash = dictionary_hash(key); + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + /* Compare hash */ + if (hash==d->hash[i]) { + /* Compare string, to avoid hash collisions */ + if (!strcmp(key, d->key[i])) { + /* Found key */ + break ; + } + } + } + if (i>=d->size) + /* Key not found */ + return ; + + free(d->key[i]); + d->key[i] = NULL ; + if (d->val[i]!=NULL) { + free(d->val[i]); + d->val[i] = NULL ; + } + d->hash[i] = 0 ; + d->n -- ; + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(const dictionary * d, FILE * out) +{ + ssize_t i ; + + if (d==NULL || out==NULL) return ; + if (d->n<1) { + fprintf(out, "empty dictionary\n"); + return ; + } + for (i=0 ; i<d->size ; i++) { + if (d->key[i]) { + fprintf(out, "%20s\t[%s]\n", + d->key[i], + d->val[i] ? d->val[i] : "UNDEF"); + } + } + return ; +} diff --git a/gpsd/booster/dictionary.h b/gpsd/booster/dictionary.h new file mode 100644 index 0000000..87d31d9 --- /dev/null +++ b/gpsd/booster/dictionary.h @@ -0,0 +1,174 @@ + +/*-------------------------------------------------------------------------*/ +/** + @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. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +#ifndef _DICTIONARY_H_ +#define _DICTIONARY_H_ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/*--------------------------------------------------------------------------- + New types + ---------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------*/ +/** + @brief Dictionary object + + This object contains a list of string/string associations. Each + association is identified by a unique string key. Looking up values + in the dictionary is speeded up by the use of a (hopefully collision-free) + hash function. + */ +/*-------------------------------------------------------------------------*/ +typedef struct _dictionary_ { + int n ; /** Number of entries in dictionary */ + ssize_t size ; /** Storage size */ + char ** val ; /** List of string values */ + char ** key ; /** List of string keys */ + unsigned * hash ; /** List of hash values for keys */ +} dictionary ; + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ +/** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(const char * key); + +/*-------------------------------------------------------------------------*/ +/** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @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 + dictionary, give size=0. + */ +/*--------------------------------------------------------------------------*/ +dictionary * dictionary_new(size_t size); + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * vd); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +const char * dictionary_get(const dictionary * d, const char * key, const char * def); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * vd, const char * key, const char * val); + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, const char * key); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(const dictionary * d, FILE * out); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gpsd/booster/esp32.c b/gpsd/booster/esp32.c new file mode 100644 index 0000000..58b3d6d --- /dev/null +++ b/gpsd/booster/esp32.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2022 Guo Wenxue + * Author: Guo Wenxue <guowenxue@gmail.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GPL license. + */ + +#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; +} + diff --git a/gpsd/booster/esp32.h b/gpsd/booster/esp32.h new file mode 100644 index 0000000..a6415a3 --- /dev/null +++ b/gpsd/booster/esp32.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022 Guo Wenxue + * Author: Guo Wenxue <guowenxue@gmail.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GPL license. + */ + +#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_ ----- */ diff --git a/gpsd/booster/iniparser.c b/gpsd/booster/iniparser.c new file mode 100644 index 0000000..3a446e5 --- /dev/null +++ b/gpsd/booster/iniparser.c @@ -0,0 +1,837 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.c + @author N. Devillard + @brief Parser for ini files. + @url https://github.com/ndevilla/iniparser +*/ +/*--------------------------------------------------------------------------*/ +/*---------------------------- Includes ------------------------------------*/ +#include <ctype.h> +#include <stdarg.h> +#include "iniparser.h" + +/*---------------------------- Defines -------------------------------------*/ +#define ASCIILINESZ (1024) +#define INI_INVALID_KEY ((char*)-1) + +/*--------------------------------------------------------------------------- + Private to this module + ---------------------------------------------------------------------------*/ +/** + * This enum stores the status for each parsed line (internal use only). + */ +typedef enum _line_status_ { + LINE_UNPROCESSED, + LINE_ERROR, + LINE_EMPTY, + LINE_COMMENT, + LINE_SECTION, + LINE_VALUE +} line_status ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Convert a string to lowercase. + @param in String to convert. + @param out Output buffer. + @param len Size of the out buffer. + @return ptr to the out buffer or NULL if an error occured. + + This function convert a string into lowercase. + At most len - 1 elements of the input string will be converted. + */ +/*--------------------------------------------------------------------------*/ +static const char * strlwc(const char * in, char *out, unsigned len) +{ + unsigned i ; + + if (in==NULL || out == NULL || len==0) return NULL ; + i=0 ; + while (in[i] != '\0' && i < len-1) { + out[i] = (char)tolower((int)in[i]); + i++ ; + } + out[i] = '\0'; + return out ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Duplicate a string + @param s String to duplicate + @return Pointer to a newly allocated string, to be freed with free() + + This is a replacement for strdup(). This implementation is provided + for systems that do not have it. + */ +/*--------------------------------------------------------------------------*/ +static char * xstrdup(const char * s) +{ + char * t ; + size_t len ; + if (!s) + return NULL ; + + len = strlen(s) + 1 ; + t = (char*) malloc(len) ; + if (t) { + memcpy(t, s, len) ; + } + return t ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Remove blanks at the beginning and the end of a string. + @param str String to parse and alter. + @return unsigned New size of the string. + */ +/*--------------------------------------------------------------------------*/ +static unsigned strstrip(char * s) +{ + char *last = NULL ; + char *dest = s; + + if (s==NULL) return 0; + + last = s + strlen(s); + while (isspace((int)*s) && *s) s++; + while (last > s) { + if (!isspace((int)*(last-1))) + break ; + last -- ; + } + *last = (char)0; + + memmove(dest,s,last - s + 1); + return last - s; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Default error callback for iniparser: wraps `fprintf(stderr, ...)`. + */ +/*--------------------------------------------------------------------------*/ +static int default_error_callback(const char *format, ...) +{ + int ret; + va_list argptr; + va_start(argptr, format); + ret = vfprintf(stderr, format, argptr); + va_end(argptr); + return ret; +} + +static int (*iniparser_error_callback)(const char*, ...) = default_error_callback; + +/*-------------------------------------------------------------------------*/ +/** + @brief Configure a function to receive the error messages. + @param errback Function to call. + + By default, the error will be printed on stderr. If a null pointer is passed + as errback the error callback will be switched back to default. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_set_error_callback(int (*errback)(const char *, ...)) +{ + if (errback) { + iniparser_error_callback = errback; + } else { + iniparser_error_callback = default_error_callback; + } +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getnsec(const dictionary * d) +{ + int i ; + int nsec ; + + if (d==NULL) return -1 ; + nsec=0 ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (strchr(d->key[i], ':')==NULL) { + nsec ++ ; + } + } + return nsec ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ +const char * iniparser_getsecname(const dictionary * d, int n) +{ + int i ; + int foundsec ; + + if (d==NULL || n<0) return NULL ; + foundsec=0 ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (strchr(d->key[i], ':')==NULL) { + foundsec++ ; + if (foundsec>n) + break ; + } + } + if (foundsec<=n) { + return NULL ; + } + return d->key[i] ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(const dictionary * d, FILE * f) +{ + int i ; + + if (d==NULL || f==NULL) return ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (d->val[i]!=NULL) { + fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); + } else { + fprintf(f, "[%s]=UNDEF\n", d->key[i]); + } + } + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump_ini(const dictionary * d, FILE * f) +{ + int i ; + int nsec ; + const char * secname ; + + if (d==NULL || f==NULL) return ; + + nsec = iniparser_getnsec(d); + if (nsec<1) { + /* No section in file: dump all keys as they are */ + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + fprintf(f, "%s = %s\n", d->key[i], d->val[i]); + } + return ; + } + for (i=0 ; i<nsec ; i++) { + secname = iniparser_getsecname(d, i) ; + iniparser_dumpsection_ini(d, secname, f); + } + fprintf(f, "\n"); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary section to a loadable ini file + @param d Dictionary to dump + @param s Section name of dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given section of a given dictionary into a loadable ini + file. It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f) +{ + int j ; + char keym[ASCIILINESZ+1]; + int seclen ; + + if (d==NULL || f==NULL) return ; + if (! iniparser_find_entry(d, s)) return ; + + seclen = (int)strlen(s); + fprintf(f, "\n[%s]\n", s); + sprintf(keym, "%s:", s); + for (j=0 ; j<d->size ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) { + fprintf(f, + "%-30s = %s\n", + d->key[j]+seclen+1, + d->val[j] ? d->val[j] : ""); + } + } + fprintf(f, "\n"); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return Number of keys in section + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getsecnkeys(const dictionary * d, const char * s) +{ + int seclen, nkeys ; + char keym[ASCIILINESZ+1]; + int j ; + + nkeys = 0; + + if (d==NULL) return nkeys; + if (! iniparser_find_entry(d, s)) return nkeys; + + seclen = (int)strlen(s); + strlwc(s, keym, sizeof(keym)); + keym[seclen] = ':'; + + for (j=0 ; j<d->size ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) + nkeys++; + } + + return nkeys; + +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @param keys Already allocated array to store the keys in + @return The pointer passed as `keys` argument or NULL in case of error + + This function queries a dictionary and finds all keys in a given section. + The keys argument should be an array of pointers which size has been + determined by calling `iniparser_getsecnkeys` function prior to this one. + + Each pointer in the returned char pointer-to-pointer is pointing to + a string allocated in the dictionary; do not free or modify them. + */ +/*--------------------------------------------------------------------------*/ +const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys) +{ + int i, j, seclen ; + char keym[ASCIILINESZ+1]; + + if (d==NULL || keys==NULL) return NULL; + if (! iniparser_find_entry(d, s)) return NULL; + + seclen = (int)strlen(s); + strlwc(s, keym, sizeof(keym)); + keym[seclen] = ':'; + + i = 0; + + for (j=0 ; j<d->size ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) { + keys[i] = d->key[j]; + i++; + } + } + + return keys; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +const char * iniparser_getstring(const dictionary * d, const char * key, const char * def) +{ + const char * lc_key ; + const char * sval ; + char tmp_str[ASCIILINESZ+1]; + + if (d==NULL || key==NULL) + return def ; + + lc_key = strlwc(key, tmp_str, sizeof(tmp_str)); + sval = dictionary_get(d, lc_key, def); + return sval ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an long int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return long integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + "42" -> 42 + "042" -> 34 (octal -> decimal) + "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound) +{ + const char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return strtol(str, NULL, 0); +} + + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + "42" -> 42 + "042" -> 34 (octal -> decimal) + "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(const dictionary * d, const char * key, int notfound) +{ + return (int)iniparser_getlongint(d, key, notfound); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(const dictionary * d, const char * key, double notfound) +{ + const char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return atof(str); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(const dictionary * d, const char * key, int notfound) +{ + int ret ; + const char * c ; + + c = iniparser_getstring(d, key, INI_INVALID_KEY); + if (c==INI_INVALID_KEY) return notfound ; + if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { + ret = 1 ; + } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { + ret = 0 ; + } else { + ret = notfound ; + } + return ret; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry(const dictionary * ini, const char * entry) +{ + int found=0 ; + if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { + found = 1 ; + } + return found ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, the entry is created. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, const char * entry, const char * val) +{ + char tmp_str[ASCIILINESZ+1]; + return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val) ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, const char * entry) +{ + char tmp_str[ASCIILINESZ+1]; + dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str))); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Load a single line from an INI file + @param input_line Input line, may be concatenated multi-line input + @param section Output space to store section + @param key Output space to store key + @param value Output space to store value + @return line_status value + */ +/*--------------------------------------------------------------------------*/ +static line_status iniparser_line( + const char * input_line, + char * section, + char * key, + char * value) +{ + line_status sta ; + char * line = NULL; + size_t len ; + + line = xstrdup(input_line); + len = strstrip(line); + + sta = LINE_UNPROCESSED ; + if (len<1) { + /* Empty line */ + sta = LINE_EMPTY ; + } else if (line[0]=='#' || line[0]==';') { + /* Comment line */ + sta = LINE_COMMENT ; + } else if (line[0]=='[' && line[len-1]==']') { + /* Section name */ + sscanf(line, "[%[^]]", section); + strstrip(section); + strlwc(section, section, len); + sta = LINE_SECTION ; + } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 + || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2) { + /* Usual key=value with quotes, with or without comments */ + strstrip(key); + strlwc(key, key, len); + /* Don't strip spaces from values surrounded with quotes */ + sta = LINE_VALUE ; + } else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { + /* Usual key=value without quotes, with or without comments */ + strstrip(key); + strlwc(key, key, len); + strstrip(value); + /* + * sscanf cannot handle '' or "" as empty values + * this is done here + */ + if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { + value[0]=0 ; + } + sta = LINE_VALUE ; + } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 + || sscanf(line, "%[^=] %[=]", key, value) == 2) { + /* + * Special cases: + * key= + * key=; + * key=# + */ + strstrip(key); + strlwc(key, key, len); + value[0]=0 ; + sta = LINE_VALUE ; + } else { + /* Generate syntax error */ + sta = LINE_ERROR ; + } + + free(line); + return sta ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(const char * ininame) +{ + FILE * in ; + + char line [ASCIILINESZ+1] ; + char section [ASCIILINESZ+1] ; + char key [ASCIILINESZ+1] ; + char tmp [(ASCIILINESZ * 2) + 2] ; + char val [ASCIILINESZ+1] ; + + int last=0 ; + int len ; + int lineno=0 ; + int errs=0; + int mem_err=0; + + dictionary * dict ; + + if ((in=fopen(ininame, "r"))==NULL) { + iniparser_error_callback("iniparser: cannot open %s\n", ininame); + return NULL ; + } + + dict = dictionary_new(0) ; + if (!dict) { + fclose(in); + return NULL ; + } + + memset(line, 0, ASCIILINESZ); + memset(section, 0, ASCIILINESZ); + memset(key, 0, ASCIILINESZ); + memset(val, 0, ASCIILINESZ); + last=0 ; + + while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { + lineno++ ; + len = (int)strlen(line)-1; + if (len<=0) + continue; + /* Safety check against buffer overflows */ + if (line[len]!='\n' && !feof(in)) { + iniparser_error_callback( + "iniparser: input line too long in %s (%d)\n", + ininame, + lineno); + dictionary_del(dict); + fclose(in); + return NULL ; + } + /* Get rid of \n and spaces at end of line */ + while ((len>=0) && + ((line[len]=='\n') || (isspace(line[len])))) { + line[len]=0 ; + len-- ; + } + if (len < 0) { /* Line was entirely \n and/or spaces */ + len = 0; + } + /* Detect multi-line */ + if (line[len]=='\\') { + /* Multi-line value */ + last=len ; + continue ; + } else { + last=0 ; + } + switch (iniparser_line(line, section, key, val)) { + case LINE_EMPTY: + case LINE_COMMENT: + break ; + + case LINE_SECTION: + mem_err = dictionary_set(dict, section, NULL); + break ; + + case LINE_VALUE: + sprintf(tmp, "%s:%s", section, key); + mem_err = dictionary_set(dict, tmp, val); + break ; + + case LINE_ERROR: + iniparser_error_callback( + "iniparser: syntax error in %s (%d):\n-> %s\n", + ininame, + lineno, + line); + errs++ ; + break; + + default: + break ; + } + memset(line, 0, ASCIILINESZ); + last=0; + if (mem_err<0) { + iniparser_error_callback("iniparser: memory allocation failure\n"); + break ; + } + } + if (errs) { + dictionary_del(dict); + dict = NULL ; + } + fclose(in); + return dict ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d) +{ + dictionary_del(d); +} diff --git a/gpsd/booster/iniparser.h b/gpsd/booster/iniparser.h new file mode 100644 index 0000000..984f4b2 --- /dev/null +++ b/gpsd/booster/iniparser.h @@ -0,0 +1,359 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.h + @author N. Devillard + @brief Parser for ini files. + @url https://github.com/ndevilla/iniparser +*/ +/*--------------------------------------------------------------------------*/ + +#ifndef _INIPARSER_H_ +#define _INIPARSER_H_ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* + * The following #include is necessary on many Unixes but not Linux. + * It is not needed for Windows platforms. + * Uncomment it if needed. + */ +/* #include <unistd.h> */ + +#include "dictionary.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*-------------------------------------------------------------------------*/ +/** + @brief Configure a function to receive the error messages. + @param errback Function to call. + + By default, the error will be printed on stderr. If a null pointer is passed + as errback the error callback will be switched back to default. + */ +/*--------------------------------------------------------------------------*/ + +void iniparser_set_error_callback(int (*errback)(const char *, ...)); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ + +int iniparser_getnsec(const dictionary * d); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ + +const char * iniparser_getsecname(const dictionary * d, int n); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ + +void iniparser_dump_ini(const dictionary * d, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary section to a loadable ini file + @param d Dictionary to dump + @param s Section name of dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given section of a given dictionary into a loadable ini + file. It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ + +void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(const dictionary * d, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return Number of keys in section + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getsecnkeys(const dictionary * d, const char * s); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @param keys Already allocated array to store the keys in + @return The pointer passed as `keys` argument or NULL in case of error + + This function queries a dictionary and finds all keys in a given section. + The keys argument should be an array of pointers which size has been + determined by calling `iniparser_getsecnkeys` function prior to this one. + + Each pointer in the returned char pointer-to-pointer is pointing to + a string allocated in the dictionary; do not free or modify them. + */ +/*--------------------------------------------------------------------------*/ +const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +const char * iniparser_getstring(const dictionary * d, const char * key, const char * def); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + - "42" -> 42 + - "042" -> 34 (octal -> decimal) + - "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(const dictionary * d, const char * key, int notfound); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an long int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + - "42" -> 42 + - "042" -> 34 (octal -> decimal) + - "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + */ +/*--------------------------------------------------------------------------*/ +long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(const dictionary * d, const char * key, double notfound); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(const dictionary * d, const char * key, int notfound); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, the entry is created. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, const char * entry, const char * val); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, const char * entry); + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry(const dictionary * ini, const char * entry) ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(const char * ininame); + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gpsd/booster/linux_list.h b/gpsd/booster/linux_list.h new file mode 100644 index 0000000..9ac0720 --- /dev/null +++ b/gpsd/booster/linux_list.h @@ -0,0 +1,723 @@ +/********************************************************************************* + * Copyright: (C) 2020 LingYun IoT System Studio + * All rights reserved. + * + * Filename: linux_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 + + diff --git a/gpsd/booster/logger.c b/gpsd/booster/logger.c new file mode 100644 index 0000000..116a529 --- /dev/null +++ b/gpsd/booster/logger.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2022 Guo Wenxue + * Author: Guo Wenxue <guowenxue@gmail.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GPL license. + */ + +#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" + +typedef void (*log_LockFn)(void *udata, int lock); + +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; + +static const char *level_names[] = { + "FATAL", + "ERROR", + "WARN", + "INFO", + "DEBUG", + "TRACE" +}; + +static const char *level_colors[] = { + "\x1b[35m", + "\x1b[31m", + "\x1b[33m", + "\x1b[32m", + "\x1b[36m", + "\x1b[94m" +}; + +static inline void time_to_str(char *buf) +{ + struct timeval tv; + struct tm *tm; + int len; + + 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) + { + 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)); + } +} + +int log_open(char *fname, int level, int size, int lock) +{ + FILE *fp; + + L.level = level; + L.size = size*1024; + + if( !fname || !strcmp(fname, "console") || !strcmp(fname, "stderr") ) + { + strcpy(L.file, "console"); + L.fp = stderr; + L.size = 0; /* console don't need rollback */ + } + else + { + if ( !(fp = fopen(fname, "a+")) ) + { + fprintf(stderr, "%s() failed: %s\n", __func__, strerror(errno)); + return -2; + } + L.fp = fp; + strncpy(L.file, fname, sizeof(L.file)); + } + + + if( lock ) + { + static pthread_mutex_t log_lock; + + pthread_mutex_init(&log_lock, NULL); + L.udata = (void *)&log_lock; + L.lockfn = mutex_lock; + } + + 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); + + return 0; +} + +void log_close(void) +{ + if( L.fp && L.fp!=stderr ) + fclose(L.fp); + + if (L.udata ) + pthread_mutex_destroy( L.udata); +} + +static void log_rollback(void) +{ + char cmd[128]={0}; + long fsize; + + /* 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 ; +} + +void _log_write(int level, const char *file, int line, const char *fmt, ...) +{ + va_list args; + char time_string[100]; + + if ( !L.fp || level>L.level ) + return; + + /* Acquire lock */ + if ( L.lockfn ) + L.lockfn(L.udata, 1); + + log_rollback(); + + /* check and rollback file */ + time_to_str(time_string); + + /* Log to stderr */ + if ( L.fp == stderr ) + { + fprintf(L.fp, "%s %s %-5s\x1b[0m \x1b[90m%s:%03d:\x1b[0m ", + time_string, level_colors[level], level_names[level], file, line); + } + else /* Log to file */ + { + fprintf(L.fp, "%s %-5s %s:%03d: ", time_string, level_names[level], file, line); + } + + 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); +} + +#define LINELEN 81 +#define CHARS_PER_LINE 16 +static char *print_char = +" " +" " +" !\"#$%&'()*+,-./" +"0123456789:;<=>?" +"@ABCDEFGHIJKLMNO" +"PQRSTUVWXYZ[\\]^_" +"`abcdefghijklmno" +"pqrstuvwxyz{|}~ " +" " +" " +" ???????????????" +"????????????????" +"????????????????" +"????????????????" +"????????????????" +"????????????????"; + +void log_dump(int level, const char *prompt, char *buf, size_t len) +{ + int rc; + int idx; + char prn[LINELEN]; + char lit[CHARS_PER_LINE + 2]; + char hc[4]; + short line_done = 1; + + if (!L.fp || level>L.level) + return; + + if( prompt ) + _log_write(level, __FILE__, __LINE__, "%s", prompt); + + rc = len; + idx = 0; + lit[CHARS_PER_LINE] = '\0'; + + while (rc > 0) + { + if (line_done) + snprintf(prn, LINELEN, "%08X: ", idx); + + do + { + unsigned char c = buf[idx]; + snprintf(hc, 4, "%02X ", c); + strncat(prn, hc, LINELEN); + + lit[idx % CHARS_PER_LINE] = print_char[c]; + } + while (--rc > 0 && (++idx % CHARS_PER_LINE != 0)); + + line_done = (idx % CHARS_PER_LINE) == 0; + if (line_done) + { + if (L.fp) + fprintf(L.fp, "%s %s\n", prn, lit); + } + } + + 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)-strlen(prn)); + + if (L.fp) + fprintf(L.fp, "%s %s\n", prn, lit); + + } +} diff --git a/gpsd/booster/logger.h b/gpsd/booster/logger.h new file mode 100644 index 0000000..947e8ef --- /dev/null +++ b/gpsd/booster/logger.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Guo Wenxue + * Author: Guo Wenxue <guowenxue@gmail.com> + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GPL license. + */ + +#ifndef _LOGGER_H_ +#define _LOGGER_H_ + +#include <stdio.h> +#include <stdarg.h> + +#define LOG_VERSION "v0.1" + +/* log level */ +enum { + LOG_LEVEL_FATAL, + 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); + + +/* description: Terminate the logger system */ +void log_close(void); + + +/* 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, ...); + + +/* description: dump a buffer in hex to logger file */ +void log_dump(int level, const char *prompt, char *buf, size_t len); + +/* 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__) +#define log_fatal(...) _log_write(LOG_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__) + +#endif diff --git a/gpsd/booster/makefile b/gpsd/booster/makefile new file mode 100644 index 0000000..e11a9de --- /dev/null +++ b/gpsd/booster/makefile @@ -0,0 +1,18 @@ + +PWD=$(shell pwd ) + +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 + +clean: + @rm -f *.o + @rm -f *.a + +distclean: + @make clean diff --git a/gpsd/booster/ringbuf.c b/gpsd/booster/ringbuf.c new file mode 100644 index 0000000..f79d496 --- /dev/null +++ b/gpsd/booster/ringbuf.c @@ -0,0 +1,106 @@ +/******************************************************************************** + * Copyright: (C) 2021 LingYun IoT System Studio + * All rights reserved. + * + * Filename: ringbuf.h + * Description: This head file + * + * Version: 1.0.0(2021年04月29日) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "2021年04月29日 12时18分32秒" + * + ********************************************************************************/ +#include <string.h> +#include <assert.h> +#include "ringbuf.h" + +void rb_init (struct ring_buffer *ring, unsigned char* buff, int size) +{ + memset (ring, 0, sizeof (struct ring_buffer)); + ring->rd_pointer = 0; + ring->wr_pointer = 0; + ring->buffer= buff; + ring->size = size; +} + + +int rb_write (struct ring_buffer *rb, unsigned char * buf, int len) +{ + int total; + int i; + + /* total = len = min(space, len) */ + total = rb_free_size(rb); + if(len > total) + len = total; + else + total = len; + + i = rb->wr_pointer; + if(i + len > rb->size) + { + memcpy(rb->buffer + i, buf, rb->size - i); + buf += rb->size - i; + len -= rb->size - i; + i = 0; + } + + memcpy(rb->buffer + i, buf, len); + rb->wr_pointer = i + len; + return total; +} + + +int rb_free_size (struct ring_buffer *rb) +{ + return (rb->size - 1 - rb_data_size(rb)); +} + + +int rb_read (struct ring_buffer *rb, unsigned char * buf, int max) +{ + int total; + int i; + + /* total = len = min(used, len) */ + total = rb_data_size(rb); + + if(max > total) + max = total; + else + total = max; + + + i = rb->rd_pointer; + if(i + max > rb->size) + { + memcpy(buf, rb->buffer + i, rb->size - i); + buf += rb->size - i; + max -= rb->size - i; + i = 0; + } + + memcpy(buf, rb->buffer + i, max); + rb->rd_pointer = i + max; + + return total; +} + +int rb_data_size (struct ring_buffer *rb) +{ + return ((rb->wr_pointer - rb->rd_pointer) & (rb->size-1)); +} + +void rb_clear (struct ring_buffer *rb) +{ + memset(rb->buffer,0,rb->size); + rb->rd_pointer=0; + rb->wr_pointer=0; +} + +unsigned char rb_peek(struct ring_buffer* rb, int index) +{ + assert(index < rb_data_size(rb)); + + return rb->buffer[((rb->rd_pointer + index) % rb->size)]; +} diff --git a/gpsd/booster/ringbuf.h b/gpsd/booster/ringbuf.h new file mode 100644 index 0000000..23553cc --- /dev/null +++ b/gpsd/booster/ringbuf.h @@ -0,0 +1,57 @@ +/******************************************************************************** + * Copyright: (C) 2021 LingYun IoT System Studio + * All rights reserved. + * + * Filename: ringbuf.h + * Description: This head file + * + * Version: 1.0.0(2021年04月29日) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "2021年04月29日 12时18分32秒" + * + ********************************************************************************/ + +#ifndef _RINGBUF_H_ +#define _RINGBUF_H_ + +struct ring_buffer +{ + unsigned char *buffer; + int wr_pointer; + int rd_pointer; + int size; +}; + + +/* Initial the ring buffer */ +void rb_init (struct ring_buffer *ring, unsigned char *buff, int size) ; + + +/* Description: Write $len bytes data in $buf into ring buffer $rb + * Return Value: The actual written into ring buffer data size, if ring buffer + * left space size small than $len, then only part of the data be written into. + */ +int rb_write (struct ring_buffer *rb, unsigned char *buf, int len) ; + + +/* Get ring buffer left free size */ +int rb_free_size (struct ring_buffer *rb); + + +/* Read $max bytes data from ring buffer $rb to $buf */ +int rb_read (struct ring_buffer *rb, unsigned char *buf, int max); + + +/* Read a specify $index byte data in ring buffer $rb */ +unsigned char rb_peek(struct ring_buffer *rb, int index); + + +/* Get data size in the ring buffer */ +int rb_data_size (struct ring_buffer *rb); + + +/* Clear the ring buffer data */ +void rb_clear (struct ring_buffer *rb) ; + +#endif /* ----- #ifndef _RINGBUF_H_ ----- */ + diff --git a/gpsd/booster/util_proc.c b/gpsd/booster/util_proc.c new file mode 100644 index 0000000..dfdabae --- /dev/null +++ b/gpsd/booster/util_proc.c @@ -0,0 +1,432 @@ +/********************************************************************************* + * Copyright: (C) 2020 LingYun IoT System Studio + * All rights reserved. + * + * Filename: util_proc.c + * Description: This file is the process API + * + * Version: 1.0.0(7/06/2020) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "7/06/2020 09:19:02 PM" + * + ********************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <pthread.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "util_proc.h" +#include "logger.h" + +proc_signal_t g_signal={0}; + +void proc_default_sighandler(int sig) +{ + switch(sig) + { + case SIGINT: + log_warn("SIGINT - stopping\n"); + g_signal.stop = 1; + break; + + case SIGTERM: + log_warn("SIGTERM - stopping\n"); + g_signal.stop = 1; + break; + + case SIGSEGV: + log_warn("SIGSEGV - stopping\n"); +#if 0 + if(g_signal.stop) + exit(0); + + g_signal.stop = 1; +#endif + break; + + case SIGPIPE: + log_warn("SIGPIPE - warnning\n"); + break; + + default: + break; + } +} + + +/* install default signal process functions */ +void install_default_signal(void) +{ + struct sigaction sigact, sigign; + + log_info("Install default signal handler.\n"); + + /* Initialize the catch signal structure. */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = proc_default_sighandler; + + /* Setup the ignore signal. */ + sigemptyset(&sigign.sa_mask); + sigign.sa_flags = 0; + sigign.sa_handler = SIG_IGN; + + sigaction(SIGTERM, &sigact, 0); /* catch terminate signal "kill" command */ + sigaction(SIGINT, &sigact, 0); /* catch interrupt signal CTRL+C */ + //sigaction(SIGSEGV, &sigact, 0); /* catch segmentation faults */ + sigaction(SIGPIPE, &sigact, 0); /* catch broken pipe */ +#if 0 + sigaction(SIGCHLD, &sigact, 0); /* catch child process return */ + sigaction(SIGUSR2, &sigact, 0); /* catch USER signal */ +#endif +} + + +/* **************************************************************************** + * FunctionName: daemonize + * Description : Set the programe runs as daemon in background + * Inputs : nodir: DON'T change the work directory to / : 1:NoChange 0:Change + * noclose: close the opened file descrtipion or not 1:Noclose 0:Close + * Output : NONE + * Return : NONE + * *****************************************************************************/ +void daemonize(int nochdir, int noclose) +{ + int rv, fd; + int i; + + /* already a daemon */ + if (1 == getppid()) + return; + + /* fork error */ + rv = fork(); + if (rv < 0) exit(1); + + /* parent process exit */ + if (rv > 0) + exit(0); + + /* obtain a new process session group */ + setsid(); + + if (!noclose) + { + /* close all descriptors */ + for (i = getdtablesize(); i >= 0; --i) + { + //if (i != g_logPtr->fd) + close(i); + } + + /* Redirect Standard input [0] to /dev/null */ + fd = open("/dev/null", O_RDWR); + + /* Redirect Standard output [1] to /dev/null */ + dup(fd); + + /* Redirect Standard error [2] to /dev/null */ + dup(fd); + } + + umask(0); + + if (!nochdir) + chdir("/"); + + return; +} + +/* **************************************************************************** + * FunctionName: check_set_program_running + * Description : check program already running or not, if not then run it and + * record pid into $pidfile + * Inputs : daemon: set program running in daemon or not + * pid_file:The record PID file path + * Output : NONE + * Return : 0: Record successfully Else: Failure + * *****************************************************************************/ + +int check_set_program_running(int daemon, char *pidfile) +{ + if( !pidfile ) + return 0; + + if( check_daemon_running(pidfile) ) + { + log_error("Program already running, process exit now"); + return -1; + } + + if( daemon ) + { + if( set_daemon_running(pidfile) < 0 ) + { + log_error("set program running as daemon failure\n"); + return -2; + } + } + else + { + if( record_daemon_pid(pidfile) < 0 ) + { + log_error("record program running PID failure\n"); + return -3; + } + } + + return 0; +} + + + +/* **************************************************************************** + * FunctionName: record_daemon_pid + * Description : Record the running daemon program PID to the file "pid_file" + * Inputs : pid_file:The record PID file path + * Output : NONE + * Return : 0: Record successfully Else: Failure + * *****************************************************************************/ +int record_daemon_pid(const char *pid_file) +{ + struct stat fStatBuf; + int fd = -1; + int mode = S_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | S_IRWXU; + char ipc_dir[64] = { 0 }; + + strncpy(ipc_dir, pid_file, 64); + + /* dirname() will modify ipc_dir and save the result */ + dirname(ipc_dir); + + /* If folder pid_file PATH doesnot exist, then we will create it" */ + if (stat(ipc_dir, &fStatBuf) < 0) + { + if (mkdir(ipc_dir, mode) < 0) + { + log_error("cannot create %s: %s\n", ipc_dir, strerror(errno)); + return -1; + } + + (void)chmod(ipc_dir, mode); + } + + /* Create the process running PID file */ + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + if ((fd = open(pid_file, O_RDWR | O_CREAT | O_TRUNC, mode)) >= 0) + { + char pid[PID_ASCII_SIZE]; + snprintf(pid, sizeof(pid), "%u\n", (unsigned)getpid()); + write(fd, pid, strlen(pid)); + close(fd); + + log_debug("Record PID<%u> to file %s.\n", getpid(), pid_file); + } + else + { + log_error("cannot create %s: %s\n", pid_file, strerror(errno)); + return -1; + } + + return 0; +} + +/* **************************************************************************** + * FunctionName: get_daemon_pid + * Description : Get the daemon process PID from the PID record file "pid_file" + * Inputs : pid_file: the PID record file + * Output : NONE + * Return : pid_t: The daemon process PID number + * *****************************************************************************/ +pid_t get_daemon_pid(const char *pid_file) +{ + FILE *f; + pid_t pid; + + if ((f = fopen(pid_file, "rb")) != NULL) + { + char pid_ascii[PID_ASCII_SIZE]; + (void)fgets(pid_ascii, PID_ASCII_SIZE, f); + (void)fclose(f); + pid = atoi(pid_ascii); + } + else + { + log_error("Can't open PID record file %s: %s\n", pid_file, strerror(errno)); + return -1; + } + return pid; +} + +/* **************************************************************************** + * FunctionName: check_daemon_running + * Description : Check the daemon program already running or not + * Inputs : pid_file: The record running daemon program PID + * Output : NONE + * Return : 1: The daemon program alread running 0: Not running + * *****************************************************************************/ +int check_daemon_running(const char *pid_file) +{ + int rv = -1; + struct stat fStatBuf; + + rv = stat(pid_file, &fStatBuf); + if (0 == rv) + { + pid_t pid = -1; + printf("PID record file \"%s\" exist.\n", pid_file); + + pid = get_daemon_pid(pid_file); + if (pid > 0) /* Process pid exist */ + { + if ((rv = kill(pid, 0)) == 0) + { + printf("Program with PID[%d] seems running.\n", pid); + return 1; + } + else /* Send signal to the old process get no reply. */ + { + printf("Program with PID[%d] seems exit.\n", pid); + remove(pid_file); + return 0; + } + } + else if (0 == pid) + { + printf("Can not read program PID form record file.\n"); + remove(pid_file); + return 0; + } + else /* Read pid from file "pid_file" failure */ + { + printf("Read record file \"%s\" failure, maybe program still running.\n", pid_file); + return 1; + } + } + + return 0; +} + +/* **************************************************************************** + * FunctionName: stop_daemon_running + * Description : Stop the daemon program running + * Inputs : pid_file: The record running daemon program PID + * Output : NONE + * Return : 1: The daemon program alread running 0: Not running + * *****************************************************************************/ +int stop_daemon_running(const char *pid_file) +{ + pid_t pid = -1; + struct stat fStatBuf; + + if ( stat(pid_file, &fStatBuf) < 0) + return 0; + + printf("PID record file \"%s\" exist.\n", pid_file); + pid = get_daemon_pid(pid_file); + if (pid > 0) /* Process pid exist */ + { + while ( (kill(pid, 0) ) == 0) + { + kill(pid, SIGTERM); + sleep(1); + } + + remove(pid_file); + } + + return 0; +} + + + +/* **************************************************************************** + * FunctionName: set_daemon_running + * Description : Set the programe running as daemon if it's not running and record + * its PID to the pid_file. + * Inputs : pid_file: The record running daemon program PID + * Output : NONE + * Return : 0: Successfully. 1: Failure + * *****************************************************************************/ +int set_daemon_running(const char *pid_file) +{ + daemonize(0, 1); + log_info("Program running as daemon [PID:%d].\n", getpid()); + + if (record_daemon_pid(pid_file) < 0) + { + log_error("Record PID to file \"%s\" failure.\n", pid_file); + return -2; + } + + return 0; +} + +/* start a new thread to run $thread_workbody point function */ +int thread_start(pthread_t *thread_id, thread_body_t thread_workbody, void *thread_arg) +{ + int rv = 0; + pthread_t tid; + + pthread_attr_t thread_attr; + + /* Initialize the thread attribute */ + rv = pthread_attr_init(&thread_attr); + if(rv) + return -1; + + /* Set the stack size of the thread */ + rv = pthread_attr_setstacksize(&thread_attr, 120 * 1024); + if(rv) + goto CleanUp; + + /* Set thread to detached state:Don`t need pthread_join */ + rv = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + if(rv) + goto CleanUp; + + /* Create the thread */ + rv = pthread_create(&tid, &thread_attr, thread_workbody, thread_arg); + if(rv) + goto CleanUp; + +CleanUp: + + + if( thread_id ) + { + if( rv ) + *thread_id = 0; + else + *thread_id = tid; + } + + /* Destroy the attributes of thread */ + pthread_attr_destroy(&thread_attr); + return rv; +} + + +/* excute a linux command by system() */ +void exec_system_cmd(const char *format, ...) +{ + char cmd[256]; + va_list args; + + memset(cmd, 0, sizeof(cmd)); + + va_start(args, format); + vsnprintf(cmd, sizeof(cmd), format, args); + va_end(args); + + system(cmd); +} + + diff --git a/gpsd/booster/util_proc.h b/gpsd/booster/util_proc.h new file mode 100644 index 0000000..4af3cf2 --- /dev/null +++ b/gpsd/booster/util_proc.h @@ -0,0 +1,67 @@ +/******************************************************************************** + * Copyright: (C) 2020 LingYun IoT System Studio + * All rights reserved. + * + * Filename: util_proc.h + * Description: This head file is for Linux process/thread API + * + * Version: 1.0.0(7/06/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "7/06/2012 09:21:33 PM" + * + ********************************************************************************/ + +#ifndef __UTIL_PROC_H_ +#define __UTIL_PROC_H_ + +#include <signal.h> + +#define PID_ASCII_SIZE 11 + +typedef struct proc_signal_s +{ + int signal; + unsigned stop; /* 0: Not term 1: Stop */ +} proc_signal_t; + +typedef void *(* thread_body_t) (void *thread_arg); + +extern proc_signal_t g_signal; + +/* install default signal process functions */ +extern void install_default_signal(void); + +/* excute a linux command by system() */ +extern void exec_system_cmd(const char *format, ...); + +/* check program already running or not, if not then run it and record pid into $pidfile */ +extern int check_set_program_running(int daemon, char *pidfile); + +/* check program already running or not from $pid_file */ +extern int check_daemon_running(const char *pid_file); + +/* set program daemon running and record pid in $pid_file */ +extern int set_daemon_running(const char *pid_file); + +/* record proces ID into $pid_file */ +extern int record_daemon_pid(const char *pid_file); + +/* stop program running from $pid_file */ +extern int stop_daemon_running(const char *pid_file); + +/* my implementation for set program running in daemon */ +extern void daemonize(int nochdir, int noclose); + +/* start a new thread to run $thread_workbody point function */ +extern int thread_start(pthread_t *thread_id, thread_body_t thread_workbody, void *thread_arg); + +/* +---------------------+ + * | Low level API | + * +---------------------+*/ + + + +/* get daemon process ID from $pid_file */ +extern pid_t get_daemon_pid(const char *pid_file); + +#endif diff --git a/gpsd/gpsd.c b/gpsd/gpsd.c new file mode 100644 index 0000000..c778be5 --- /dev/null +++ b/gpsd/gpsd.c @@ -0,0 +1,245 @@ +/********************************************************************************* + * Copyright: (C) 2023 LingYun IoT System Studio + * All rights reserved. + * + * Filename: gpsd.c + * Description: This file is GPS location parser program + * + * Version: 1.0.0(07/10/23) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "07/10/23 09:49:55" + * + ********************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "logger.h" +#include "comport.h" +#include "util_proc.h" + +typedef struct gps_fix_s +{ + char time[32]; /* GPS UTC time, formt: hhmmss */ + char status; /* A: Valid V:Invalid */ + float latitude; /* Latitude */ + char lat; /* N: North S: South */ + float longitude; /* Longitude */ + char lon; /* E: East W: West */ + float speed; /* Speed over ground, meters/sec */ + float track; /* Course made good (relative to true north) */ +} gps_fix_t; + + +int proc_gprmc(char *buf, int size, gps_fix_t *info); + +static inline void banner(const char *progname) +{ + printf("%s program Version v1.0.0 Build on (%s)\n", progname, __DATE__); + printf("Copyright (C) 2023 LingYun IoT System Studio.\n"); +} + +static void program_usage(const char *progname) +{ + banner(progname); + + printf("Usage: %s [OPTION]...\n", progname); + printf(" %s is a MQTT subscribe daemon program to control relay. \n", progname); + + printf("\nMandatory arguments to long options are mandatory for short options too:\n"); + printf(" -D[device ] Set GPS connect serial port device\n"); + printf(" -d[debug ] Running in debug mode\n"); + printf(" -l[level ] Set the log level as [0..%d]\n", LOG_LEVEL_MAX-1); + printf(" -h[help ] Display this help information\n"); + printf(" -v[version ] Display the program version\n"); + + return; +} + +int main (int argc, char **argv) +{ + const char *progname=NULL; + int opt = 0; + int rv = 0; + int debug = 0; + char pid_file[64] = { 0 }; /* The file used to record the PID */ + char *log_file = "/tmp/gpsd.log"; + int log_level = LOG_LEVEL_INFO; + int log_size = 10; + + char buf[4096]; + char *dev=NULL; + comport_t comport; + gps_fix_t info; + + struct option long_options[] = { + {"device", required_argument, NULL, 'D'}, + {"debug", no_argument, NULL, 'd'}, + {"level", required_argument, NULL, 'l'}, + {"version", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + progname = basename(argv[0]); + /* Parser the command line parameters */ + while ((opt = getopt_long(argc, argv, "D:dl:vh", long_options, NULL)) != -1) + { + switch (opt) + { + case 'D': /* Set serial port device */ + dev = optarg; + break; + + case 'd': /* Set debug running */ + debug = 1; + log_file = "console"; + log_level = LOG_LEVEL_DEBUG; + log_size = ROLLBACK_NONE; + break; + + case 'l': /* Set the log level */ + rv = atoi(optarg); + log_level = rv>LOG_LEVEL_MAX ? LOG_LEVEL_MAX-1 : rv; + break; + + case 'v': /* Get software version */ + banner(progname); /* Defined in version.h */ + return EXIT_SUCCESS; + + case 'h': /* Get help information */ + program_usage(progname); + return 0; + + default: + break; + } /* end of "switch(opt)" */ + } + + if( !dev ) + { + program_usage(progname); + return 0; + } + + if( (rv=log_open(log_file, log_level, log_size, LOG_LOCK_DISABLE)) < 0 ) + { + fprintf(stderr, "open logger failed, rv=%d\n", rv); + return 1; + } + + if( !debug ) + { + snprintf(pid_file, sizeof(pid_file), "/var/run/%s.pid", progname); + log_info("check program running in daemon or not by pidfile [%s]\n", pid_file); + if( check_daemon_running(pid_file) ) + { + printf("Programe already running, exit now.\n"); + return 3; + } + + if( set_daemon_running(pid_file) < 0 ) + { + printf("Programe already running, exit now.\n"); + return 3; + } + } + + if( (rv=comport_open(&comport, dev, 4800, "8N1N")) < 0 ) + { + log_error("Open serial port \"%s\" failed, rv=%d\n", dev, rv); + return 2; + } + log_info("Open serial port \"%s\" successfully\n", dev); + + while(1) + { + memset(buf, 0, sizeof(buf)); + rv = comport_recv(&comport, buf, sizeof(buf), 3000); + if( rv > 0 ) + { + proc_gprmc(buf, strlen(buf), &info); + } + } + + comport_close(&comport); + return 0; +} + +/* + * $GPRMC,024216.000,A,3029.6651,N,11423.6251,E,0.08, 156.95,100723,,,A*65 + * 1 024216.000 UTC time format: HHMMSS, 10:42:16(BeiJing) + * 2 A Status of Fix: + * A = Autonomous, valid; + * D = Differential, valid; + * V = invalid + * + * 3,4 3029.6651,N Latitude format: ddmm.mmmm, Latitude 30 deg. 29.6651 min North + * 5,6 11423.6251,E Longitude: dddmm.mmmm, Longitude 114 deg. 23.6251 min East + * 7 0.08 Speed over ground, Knots + * 8 156.95 Course Made Good, True north + * 9 100723 Date of fix ddmmyy. 2023-07-10 + * 10,11 ,, Magnetic variation + * 12 A FAA mode indicator (NMEA 2.3 and later) + * A=autonomous, + * D=differential, + * E=Estimated, + * M=Manual input mode, + * N=not valid, + * S=Simulator, + * V=Valid + * 13 *68 mandatory nmea_checksum + */ + +#define DD(s) ((int)((s)[0]-'0')*10+(int)((s)[1]-'0')) +int proc_gprmc(char *buf, int size, gps_fix_t *info) +{ + char *ptr_start=NULL; + char *ptr_end=NULL; + char hhmmss[12]={0x0}; + char ddmmyy[12]={0x0}; + + if( !buf || size<=0 || !info ) + { + log_error("Invalid input arguments\n"); + return -1; + } + + log_trace("GPS receive raw data\n:%s\n", buf); + + if( !(ptr_start=strstr(buf, "$GPRMC")) ) + { + log_warn("$GPRMC keywords not matched\n"); + return -2; + } + + if( !(ptr_end=strchr(ptr_start, 0x0D)) ) + { + log_warn("$GPRMC data not integrated\n", ptr_start); + } + + *ptr_end = '\0'; + + memset(info, 0, sizeof(*info)); + sscanf(ptr_start, "$GPRMC,%[^,],%c,%f,%c,%f,%c,%f,%f,%[^,]", + hhmmss, &info->status, &info->latitude, &info->lat, + &info->longitude, &info->lon, &info->speed, &info->track, ddmmyy); + + snprintf(info->time, sizeof(info->time), "%04d-%02d-%02d %02d:%02d:%02d", + DD(ddmmyy+4)+2000, DD(ddmmyy+2), DD(ddmmyy), + DD(hhmmss)+8, DD(hhmmss+2), DD(hhmmss+4)); + + if( info->status == 'A' ) + { + log_info("NMEA0183: %s lat=%.2f(%c) lon=%.2f(%c) speed=%.2f, track=%.2f\n", + info->time, info->latitude, info->lat, + info->longitude, info->lon, info->speed, info->track); + } + else if( info->status == 'V' ) + { + log_info("NMEA0183: Invalid data\n"); + } + + return 0; +} diff --git a/gpsd/makefile b/gpsd/makefile new file mode 100644 index 0000000..2a4a0f4 --- /dev/null +++ b/gpsd/makefile @@ -0,0 +1,57 @@ +#********************************************************************************* +# Copyright: (C) 2022 Avnet. All rights reserved. +# Author: Guo Wenxue<wenxue.guo@avnet.com> +# +# Filename: Makefile +# Description: This Makefile used to compile all the C source code file in +# current folder to a excutable binary file. +# +#********************************************************************************/ + +PRJ_PATH=$(shell pwd) +APP_NAME = gpsd +INST_PATH= /tftp + +BUILD_ARCH=$(shell uname -m) + +ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),) + CROSSTOOL=arm-linux-gnueabi- +endif + +# C source files in top-level directory +SRCFILES = $(wildcard *.c) + +# common CFLAGS for our source code +CFLAGS = -Wall -Wshadow -Wundef -Wmaybe-uninitialized -D_GNU_SOURCE + +# C source file in sub-directory +DIRS= booster +DIRS_PATH=$(patsubst %,${PRJ_PATH}/%,$(DIRS)) +CFLAGS+=$(patsubst %,-I%,$(DIRS_PATH)) +LDFLAGS+=$(patsubst %,-L%,$(DIRS_PATH)) +LIBS=$(patsubst %,-l%,$(DIRS)) + +LDFLAGS+=-lpthread + +.PHONY:libs +all: entry modules binary + +entry: + @echo "Building ${APP_NAME} on ${BUILD_ARCH}" + +modules: + @set -e; for d in ${DIRS}; do $(MAKE) CROSSTOOL=${CROSSTOOL} CFLAGS="${CFLAGS}" -C $${d}; done + +binary: ${SRCFILES} + $(CROSSTOOL)gcc $(CFLAGS) -o ${APP_NAME} $^ ${LDFLAGS} ${LIBS} + @echo " Compile over" + +clean: + set -e; for d in ${DIRS}; do $(MAKE) clean -C $${d}; done + @rm -f *.o $(APP_NAME) + +distclean: clean + @rm -rf cscope* tags + +install: + cp ${APP_NAME} ${INST_PATH} -- Gitblit v1.9.1