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