add gpsd program and booster
New file |
| | |
| | | /* |
| | | * 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; |
| | | } |
New file |
| | |
| | | /* |
| | | * 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_ ----- */ |
New file |
| | |
| | | /* |
| | | * 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; |
| | | } |
| | | |
New file |
| | |
| | | /* |
| | | * 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_ ----- */ |
| | | |
New file |
| | |
| | | /* |
| | | * 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; |
| | | } |
| | | } |
| | | |
New file |
| | |
| | | /* |
| | | * 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 |
New file |
| | |
| | | /*-------------------------------------------------------------------------*/ |
| | | /** |
| | | @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 ; |
| | | } |
New file |
| | |
| | | |
| | | /*-------------------------------------------------------------------------*/ |
| | | /** |
| | | @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 |
New file |
| | |
| | | /* |
| | | * 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; |
| | | } |
| | | |
New file |
| | |
| | | /* |
| | | * 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_ ----- */ |
New file |
| | |
| | | |
| | | /*-------------------------------------------------------------------------*/ |
| | | /** |
| | | @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); |
| | | } |
New file |
| | |
| | | |
| | | /*-------------------------------------------------------------------------*/ |
| | | /** |
| | | @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 |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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 |
| | | |
| | | |
New file |
| | |
| | | /* |
| | | * 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); |
| | | |
| | | } |
| | | } |
New file |
| | |
| | | /* |
| | | * 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 |
New file |
| | |
| | | |
| | | 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 |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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)]; |
| | | } |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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_ ----- */ |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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); |
| | | } |
| | | |
| | | |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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 |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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; |
| | | } |
New file |
| | |
| | | #********************************************************************************* |
| | | # 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} |