From ec8c799d8bb2ee69b5e6f56201231e8c905edeb1 Mon Sep 17 00:00:00 2001 From: guowenxue <guowenxue@gmail.com> Date: Mon, 19 Nov 2018 20:13:32 +0800 Subject: [PATCH] Add cp_library library --- src/cp_library/test/test_queue.c | 90 src/cp_library/test/makefile | 122 src/cp_library/test/sample.conf | 29 src/cp_library/cp_comport.c | 600 ++++ src/cp_library/cp_comport.h | 67 src/cp_library/cp_logger.h | 110 src/cp_library/test/test_ini.c | 90 src/cp_library/test/test_klist.c | 92 src/cp_library/cp_common.h | 71 src/cp_library/cp_string.c | 673 ++++ src/cp_library/cp_ringbuf.c | 103 src/cp_library/cp_atcmd.c | 668 ++++ src/cp_library/cp_ppp.h | 138 src/cp_library/cp_string.h | 86 src/cp_library/cp_gprs.h | 128 src/cp_library/cp_ringbuf.h | 57 src/cp_library/cp_queue.h | 49 src/cp_library/cp_dictionary.c | 398 ++ src/cp_library/cp_gprs.c | 677 ++++ src/cp_library/cp_logger.c | 420 ++ src/cp_library/cp_atcmd.h | 68 src/cp_library/cp_dictionary.h | 165 + src/cp_library/cp_proc.c | 340 ++ src/cp_library/test/test_logger.c | 58 src/cp_library/cp_queue.c | 155 + src/cp_library/cp_proc.h | 42 src/cp_library/cp_time.h | 123 src/cp_library/cp_iniparser.h | 308 ++ src/cp_library/cp_ppp.c | 596 ++++ src/cp_library/test/comport.c | 238 + src/cp_library/cp_iniparser.c | 807 +++++ src/cp_library/makefile | 108 src/cp_library/test/test_string.c | 77 src/cp_library/cp_klist.h | 723 +++++ 34 files changed, 8,476 insertions(+), 0 deletions(-) diff --git a/src/cp_library/cp_atcmd.c b/src/cp_library/cp_atcmd.c new file mode 100644 index 0000000..12749c2 --- /dev/null +++ b/src/cp_library/cp_atcmd.c @@ -0,0 +1,668 @@ +/********************************************************************************* + * Copyright: (C) guowenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_atcmd.c + * Description: This is file is used to send AT command to GPRS module. + * + * Version: 1.0.0(02/02/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "02/02/2012 10:28:44 AM" + * + ********************************************************************************/ + +#include "cp_atcmd.h" +#include "cp_string.h" +#include "cp_logger.h" +#include "cp_time.h" + +#ifndef ATCMD_REPLY_LEN +#define ATCMD_REPLY_LEN 512 +#endif + +/* + * Description: Send the AT command which modem will only reply "OK" or "ERROR", + * such as AT,ATE0,AT+CNMP=2 etc. + * + * Return Value: 0: OK -X: ERROR + */ +int send_atcmd_check_ok(comport_t *comport, char *atcmd, unsigned long timeout) +{ + int retval; + retval = send_atcmd(comport, atcmd, "OK\r\n", "ERROR\r\n", timeout, NULL, 0); + + return 0==retval ? 0 : -2; +} + +/* + * Description: Send the AT command which will reply the value directly in a + * single line, such as AT+CGMM, AT+CGSN + * + * Output Value: Buf: The AT command query value + * Return Value: 0: OK -X: ERROR + * + */ +int send_atcmd_check_value(comport_t *comport, char *atcmd, unsigned long timeout, char *buf, int buf_len) +{ + int i = 0; + int retval=0; + char tmp[ATCMD_REPLY_LEN]; + char *start; + + if(NULL==buf) + { + log_err("Function call arguments error!\n"); + return -1; + } + + memset(tmp, 0, sizeof(tmp)); + retval = send_atcmd(comport, atcmd, "OK\r\n", "ERROR\r\n", timeout, tmp, sizeof(tmp)); + if( 0 != retval) + { + return -2; /* AT command can not get reply */ + } + + /* Parser the receive string to get the expect value*/ + if(NULL != (start=strchr(tmp, '\n')) ) + { + start ++; /* Skip '\n' character */ + while(*start!='\r' && i<buf_len-1) + { + buf[i++] = *start; + start ++; + } + } + buf[i] = '\0'; /* End of the string */ + + return ('\0'==buf[0] ? -3 : 0); +} + +/* + * Description: Send the AT command which will reply the value with a prefix "+CMD: " + * single line, such as AT+CGMR AT+CPIN? AT+CNMP? AT+CSQ + * + * Output Value: Buf: The AT command query value by remove "+XXX:" prefix + * Return Value: 0: OK -X: ERROR + * + */ +int send_atcmd_check_request(comport_t *comport, char *atcmd, unsigned long timeout, char *buf, int buf_len) +{ + int retval; + int i = 0; + char tmp[ATCMD_REPLY_LEN]; + char *ptr = NULL; + + if(NULL==buf) + { + log_err("%s() call arguments error!\n", __FUNCTION__); + return -1; + } + + memset(tmp, 0, sizeof(tmp)); + retval = send_atcmd(comport, atcmd, "OK\r\n", "ERROR\r\n", timeout, tmp, sizeof(tmp)); + if( 0 != retval) + { + return -2; + } + + /* Parser the receive string to get the expect value*/ + if(NULL != (ptr=strchr (tmp, ':')) ) + { + ptr += 2; /* Skip the ':' and SPACE character */ + while(*ptr!='\r' && i<buf_len-1) + { + buf[i++] = *ptr; + ptr ++; + } + } + buf[i] = '\0'; /* End of the string */ + + return ('\0'==buf[0] ? -3 : 0); +} + + +/* + * Description: Send ATE0 command to the GSM module to check GSM module ready or not + * Return Value: 0: Success !0:Failure + */ +int atcmd_check_at_ready(comport_t *comport) +{ + int retval; + retval=send_atcmd_check_ok(comport, "ATE0\r", 500); + if(retval) + { + log_err("ATE0 check AT command ready [FAILED]\n"); + } + else + log_nrml("ATE0 check AT command ready [OK]\n"); + + return retval; +} + +/* + * Description: Send AT+CPIN? command to the GSM module to check SIM card exist or not + * Return Value: 0: Success !0:Failure + */ +int atcmd_check_sim_valid(comport_t *comport) +{ + int retval = 0; + char recv_buf[ATCMD_REPLY_LEN]; + + retval = send_atcmd_check_request(comport, "AT+CPIN?\r", 800, recv_buf, sizeof(recv_buf)); + if(0x00 != retval) + { + retval = 1; + log_warn("AT+CPIN? Check SIM Validation: [FAILED]\n"); + return retval; + } + + if(strstr(recv_buf, "READY")) + { + log_nrml("AT+CPIN? Check SIM Validataion: [OK]\n"); + return 0; + } + else + return -1; +} + +/* Send AT+CSQ command to check GPRS signal*/ +int atcmd_check_gprs_signal(comport_t *comport) +{ + char recv_buf[ATCMD_REPLY_LEN]; + int retval; + int signal = -1; + + memset(recv_buf, 0, sizeof(recv_buf)); + retval = send_atcmd_check_request(comport, "AT+CSQ\r", 3000, recv_buf, sizeof(recv_buf)); + if( 0 != retval) + { + log_err("AT+CSQ Check Signal Strength: [FAILED]\n"); + return retval; + } + + split_string_to_value(recv_buf, "%d,%d", &signal, NULL); + + log_nrml("AT+CSQ Check Signal Strength: [%d]\n", signal); + + return signal; +} + +/* Send AT+CREG? command to check SIM card register status */ +int atcmd_check_gprs_register(comport_t *comport) +{ + int retval, stat = -1; + char recv_buf[ATCMD_REPLY_LEN]; + + memset(recv_buf, 0, sizeof(recv_buf)); + retval = send_atcmd_check_request(comport, "AT+CREG?\r", 3000, recv_buf, sizeof(recv_buf)); + if( 0 != retval) + { + log_err("AT+CREG? Check SIM card Register: [FAILED]\n"); + return stat; + } + + split_string_to_value(recv_buf, "%d,%d", NULL, &stat); + log_nrml("AT+CREG? Check SIM Card Register: [%d]\n", stat); + + return stat; +} + + +int atcmd_check_gprs_carrier(comport_t *comport, char *carrier) +{ + int retval; + char recv_buf[ATCMD_REPLY_LEN]; + + if(carrier == NULL) + { + return -1; + } + + retval = send_atcmd_check_request(comport, "AT+COPS?\r", 5000, recv_buf, sizeof(recv_buf)); + if(retval) + { + log_warn("AT+COPS? Check SIM Card Carrier: [FAILED]\n"); + return retval; + } + + split_string_to_value(recv_buf, "%d,%d,%s,%d", NULL, NULL, carrier, NULL); + del_char_from_string(carrier, '\"'); + + log_nrml("AT+COPS? Check SIM Card Carrier: [%s]\n", carrier); + + return 0; +} + +int atcmd_check_gprs_mcc_mnc(comport_t *comport, char *mcc_mnc) +{ + int retval; + char recv_buf[ATCMD_REPLY_LEN]; + int status; + char mcc[5]; + char mnc[5]; + char *ptr = NULL; + + retval=send_atcmd_check_ok(comport, "AT+QENG=1\r", 1000); + if(retval) + { + log_err("Send AT command AT+QENG=1 failure\n"); + return retval; + } + + retval = send_atcmd(comport, "AT+QENG?\r", "OK\r\n", "ERROR\r\n", 5000, recv_buf, sizeof(recv_buf)); + if(retval) + { + log_err("AT+QENG? Check GPRS MCC and MNC failure\n"); + return retval; + } + + /* AT+QENG? respond value: + * +QENG: 1,0 + * +QENG: 0,460,00,7108,80c4,554,41,-95,18,50,0,10,x,x,x,x,x,x,x + */ + if(NULL != (ptr=strrchr (recv_buf, ':')) ) + { + split_string_to_value(ptr, "%d,%s,%s,%s", &status, mcc, mnc, NULL); + if(!status) + { + sprintf(mcc_mnc, "%s-%s", mcc, mnc); + log_nrml("AT+QGSMLOC=1 Check GPRS Location MCC-MNC: %s\n", mcc_mnc); + return 0; + } + } + + printf("ptr: %s\n", ptr); + + return -1; +} + + +int atcmd_check_gprs_location(comport_t *comport, gprs_location_t *loc) +{ + int retval; + char recv_buf[ATCMD_REPLY_LEN]; + int status; + + retval=send_atcmd_check_ok(comport, "AT+QIFGCNT=0\r", 1000); + if(retval) + { + log_warn("Send AT command AT+QIFGCNT=0 failure\n"); + return retval; + } + + retval = send_atcmd_check_request(comport, "AT+QGSMLOC=1\r", 10000, recv_buf, sizeof(recv_buf)); + if(retval) + { + log_warn("AT+QGSMLOC=1 Check GPRS Location: [FAILED]\n"); + return retval; + } + + /* +QGSMLOC: 0,114.389210,30.500380,2013/01/16,14:03:12, it's GMT time */ + split_string_to_value(recv_buf, "%d,%s,%s,%s,%s", &status, loc->longitude, loc->latitude, loc->date, loc->time); + + if(status) /* Get LOC failure */ + { + memset(loc->longitude, 0, sizeof(loc->longitude)); + memset(loc->latitude, 0, sizeof(loc->latitude)); + memset(loc->date, 0, sizeof(loc->date)); + memset(loc->time, 0, sizeof(loc->time)); + log_warn("AT+QGSMLOC=1 Check GPRS Location failure: %s\n", recv_buf); + return -1; + } + + log_nrml("GPRS location result=%d latitude,longitude: [%s,%s]\n", status, loc->latitude, loc->longitude); + log_nrml("GPRS Date and time: %s,%s\n", loc->date, loc->time); + + return retval; +} + + +int atcmd_set_network_mode(comport_t *comport, int mode) +{ + int retval; + char atcmd[64]={0}; + + sprintf (atcmd, "AT+CNMP=%d\r", mode); + if(0 != (retval=send_atcmd_check_ok(comport, atcmd, 3000)) ) + { + log_warn("AT+CNMP Set Network Mode as %d: [FAILED]\n", mode); + return retval; + } + log_nrml("AT+CNMP=%d Set Network Mode: [OK]\n", mode); + + /* AT+CNAOP=?: 0->Automatic, 1->GSM,WCDMA, 2->WCDMA,GSM */ + strncpy (atcmd, "AT+CNAOP=2\r", sizeof(atcmd)); + if(0 != (retval=send_atcmd_check_ok(comport, atcmd, 3000)) ) + { + log_warn("AT+CNAOP=2 Set Acquisitions order preference to WCDMA,GSM [FAILED]\n"); + return retval; + } + log_nrml("AT+CNAOP=2 Set Network Preference [OK]\n"); + + return 0; +} + +int atcmd_check_gprs_name(comport_t *comport, char *name) +{ + int retval; + char recv_buf[ATCMD_REPLY_LEN]; + + if(name == NULL) + { + return -1; + } + + retval = send_atcmd_check_value(comport, "AT+CGMM\r", 5000, recv_buf, sizeof(recv_buf)); + if(retval) + { + log_warn("AT+CGMM Check GPRS Module Name: [FAILED]\n"); + return retval; + } + + strcpy(name, recv_buf); + + log_nrml("AT+CGMM Check GPRS Module Name: [%s]\n", name); + + return 0; +} + + +int atcmd_check_gprs_version(comport_t *comport, char *version) +{ + int retval; + char recv_buf[ATCMD_REPLY_LEN]; + + if(version == NULL) + { + return -1; + } + + retval = send_atcmd_check_request(comport, "AT+CGMR\r", 5000, recv_buf, sizeof(recv_buf)); + if(retval) + { + log_warn("AT+CGMR Check GPRS Module Version: [FAILED]\n"); + return retval; + } + + strcpy(version, recv_buf); + log_nrml("AT+CGMR Check GPRS Module Version: [%s]\n", version); + + return 0; +} + +int atcmd_check_gprs_iemi(comport_t *comport, char *iemi) +{ + int retval; + char recv_buf[ATCMD_REPLY_LEN]; + + if(iemi == NULL) + { + return -1; + } + + retval = send_atcmd_check_value(comport, "AT+CGSN\r", 5000, recv_buf, sizeof(recv_buf)); + if(retval) + { + log_warn("AT+CGSN Check GPRS Module IEMI: [FAILED]\n"); + return retval; + } + + strcpy(iemi, recv_buf); + log_nrml("AT+CGSN Check GPRS Module IEMI: [%s]\n", iemi); + + return 0; +} + + +int atcmd_check_gprs_network(comport_t *comport, int *network) +{ + int retval; + char recv_buf[ATCMD_REPLY_LEN]; + + if(network == NULL) + { + return -1; + } + + retval = send_atcmd_check_request(comport, "AT+CNSMOD?\r", 5000, recv_buf, sizeof(recv_buf)); + if(retval) + { + log_warn("AT+CNSMOD Check Network Mode: [FAILED]\n"); + return retval; + } + + split_string_to_value(recv_buf, "%d,%d", NULL, network); + + log_nrml("AT+CNSMOD? Check Network Mode: [%d]\n", *network); + return 0; +} + + +int atcmd_set_apn(comport_t *comport, char *apn) +{ + char atcmd[64]={0}; + int retval; + + sprintf (atcmd, "AT+CGDCONT=1,\"IP\",\"%s\"\r", apn); + if(0 != (retval=send_atcmd_check_ok(comport, atcmd, 2000)) ) + { + log_err("AT+CGDCONT Set APN as \"%s\" [FAILED]\n", apn); + return retval; + } + + log_nrml("AT+CGDCONT Set APN as \"%s\" [OK]\n"); + return retval; +} + + +unsigned char at_match (char *p_pcStr, char *p_pcMatch) +{ + char acBuf [256], + *pcStart = NULL, + *pcTab = NULL; + + pcStart = p_pcMatch; + + while (0 != pcStart) + { + memset (acBuf, 0x00, sizeof (acBuf)); + + pcTab = strchr (pcStart, 9); // Find for TAB + if (0 != pcTab) + { + if (pcTab != pcStart) + { + strncpy (acBuf, pcStart, pcTab - pcStart); + } + pcStart = (0 != *(++pcTab)) ? pcTab : 0; + } + else + { + strcpy (acBuf, pcStart); + pcStart = NULL; + } + if (0 != acBuf [0] && 0 != strstr (p_pcStr, acBuf)) + { + return 0x00; + } + } + return 0x01; +} + + +/*========================================================================================================= + * Parameter Description: + * comport_t *comport: The GPRS module data port(/dev/ttyS2); + * char *atCmd: The AT command which will be sent to GPRS module + * char *expect: The EXPECT reply string by GPRS module for the AT command, such as "OK" + * char *error: The ERROR reply string by GPRS module for the AT command, such as "ERROR" + * unsigned long timeout: Read from data port timeout value + * char reply: The AT command reply output buffer + * int reply_len: The AT command reply output buffer length + * + * Return Value: + * int retval: 0->command send OK and "expect" string mached. !0->failure + * char *content: The AT command reply string by modem. + * + *=========================================================================================================*/ + +int send_atcmd(comport_t *comport, char *atCmd, char *expect, char *error, + unsigned long timeout, char *reply, int reply_len) +{ + int retval = -1; + unsigned long delay = 200; + unsigned long gap = 300; + unsigned long ulStartTime; + + int iCmdLen = 0, + iRecvLen = 0, + iRecvSize = 0, + iSize = 0; + + + char acRecv[1024]; + char *pcRecvPtr = NULL; + + if(comport->is_connted != 0x01) /* Comport not opened */ + { + log_dbg("Comport not opened.\n"); + return -1; + } + +#if 0 + /*========================================= + *= Pause a while before send AT command = + *=========================================*/ + if(0 != delay) + { + ulStartTime = time_now(); + while (time_elapsed(ulStartTime) < delay) + { + micro_second_sleep(1); + } + } +#endif + + /*==================== + *= Throw Rubbish = + *====================*/ + ulStartTime = time_now(); + memset(&acRecv, 0, sizeof(acRecv)); + + while (time_elapsed(ulStartTime) < delay) + { + iSize = comport_recv(comport, acRecv, 1, 50); + if(iSize <= 0) + { + break; + } + micro_second_sleep(1); + } + + /*==================== + *= Send AT command = + *====================*/ + + iCmdLen = strlen(atCmd); + retval = comport_send (comport, atCmd, iCmdLen); + if (0 != retval) + { + retval = 0x02; + goto CleanUp; + } + + /*=================================================== + *= Pause a while before read command response. + *===================================================*/ + if(0 != gap) + { + ulStartTime = time_now(); + while (time_elapsed(ulStartTime) < gap) + { + micro_second_sleep(1); + } + } + + memset (acRecv, 0, sizeof (acRecv)); + pcRecvPtr = acRecv; + iRecvLen = 0; + iRecvSize = sizeof (acRecv); + + retval = -1; + ulStartTime = time_now(); + + while (time_elapsed(ulStartTime) < timeout) + { + if ( iRecvLen < (iRecvSize-1) ) + { + iSize = comport_recv (comport, pcRecvPtr, 1, 50); + if (iSize >0) + { + iRecvLen += iSize; + pcRecvPtr += iSize; + acRecv [iRecvSize-1] = 0; + + /*======================================== + * Match the received with expect String = + *========================================*/ + if(NULL != expect) + { + if (0x00 == at_match(acRecv, expect)) + { + retval = 0; + goto CleanUp; + } + } + + /*======================================== + * Match the received with error String = + *========================================*/ + if(NULL != error) + { + if (0x00 == at_match(acRecv, error)) + { + retval = -3; + goto CleanUp; + } + } + } /*End of (iSize > 0) */ + } /* End of (iRecvLen < (iRecvSize-1)) */ + + micro_second_sleep(1); + } /* End of time_elapsed(ulStartTime) < timeout */ + + + if(NULL==expect) + retval = 0x00; + else + retval = -4; + +CleanUp: + //printf("acRecv:\n %s\n", acRecv); + if( NULL != reply) + { + strncpy(reply, acRecv, reply_len); + } + +#if 1 /* Log the command result to log system */ + { + char log[512] = {0}; + snprintf(log, 512, "Send AT command: \"%s\" get reply \"%s\"", atCmd, acRecv); + int i = 0; + for (i=0; i<512; i++) + { + if('\r'==log[i] || '\n'==log[i] || '\t'==log[i]) + { + log[i]=' '; + } + else if (0 == log[i]) + { + break; + } + } + log_info("%s\n", log); + } +#endif + + return retval; +} diff --git a/src/cp_library/cp_atcmd.h b/src/cp_library/cp_atcmd.h new file mode 100644 index 0000000..21dbca7 --- /dev/null +++ b/src/cp_library/cp_atcmd.h @@ -0,0 +1,68 @@ +/******************************************************************************** + * Copyright: (C) guowenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: atcmd.h + * Description: This is the head file for atcmd.c + * + * Version: 1.0.0(05/15/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "05/15/2012 05:31:10 PM" + * + ********************************************************************************/ + +#ifndef __CP_ATCMD_H +#define __CP_ATCMD_H + +#include "cp_comport.h" + +/* AT+CPSI command return important result*/ +typedef struct gprs_location_s +{ + char longitude[15]; + char latitude[15]; + char date[15]; + char time[15]; + char mcc_mnc[16];/* Mobile Country Code, China is 460 */ +} gprs_location_t; + +typedef struct reginfo_s +{ + int type; /* SIM card register type: REG_HOMEWORK,REG_SEARCHING... */ + int signal; /* GPRS signal */ + char carrier[64];/* Network operator */ + gprs_location_t loc; /* Location */ +} reginfo_t; + + +typedef struct _hwinfo_s +{ + char model[64]; /* AT+CGMM check GPRS module model */ + char mrev[64]; /* AT+CGMR check GPRS module revision */ + char iemi[64]; /* AT+CGSN check GPRS module IEMI(International Mobile station Equipment Identity) number */ +} hwinfo_t; + +extern int send_atcmd(comport_t *comport, char *atCmd, char *expect, char *error, + unsigned long timeout, char *reply, int reply_len); + +extern int send_atcmd_check_ok(comport_t *comport, char *atcmd, unsigned long timeout); +extern int send_atcmd_check_value(comport_t *comport, char *atcmd, unsigned long timeout, char *buf, int buf_len); +extern int send_atcmd_check_request(comport_t *comport, char *atcmd, unsigned long timeout, char *buf, int buf_len); + + +extern int atcmd_check_at_ready(comport_t *comport); /* Send ATE0 command */ +extern int atcmd_check_sim_valid(comport_t *comport); /* Send AT+CPIN? command */ +extern int atcmd_check_gprs_signal(comport_t *comport); /* Send AT+CSQ command */ +extern int atcmd_check_gprs_register(comport_t *comport); +extern int atcmd_check_gprs_carrier(comport_t *comport, char *carrier); +extern int atcmd_check_gprs_name(comport_t *comport, char *name); +extern int atcmd_check_gprs_version(comport_t *comport, char *version); +extern int atcmd_check_gprs_iemi(comport_t *comport, char *iemi); +extern int atcmd_check_gprs_network(comport_t *comport, int *network); +extern int atcmd_check_gprs_location(comport_t *comport, gprs_location_t *location); +extern int atcmd_check_gprs_mcc_mnc(comport_t *comport, char *mcc_mnc); + +extern int atcmd_set_network_mode(comport_t *comport, int mode); +extern int atcmd_set_apn(comport_t *comport, char *apn); + +#endif /* _CP_ATCMD_H */ diff --git a/src/cp_library/cp_common.h b/src/cp_library/cp_common.h new file mode 100644 index 0000000..66576b5 --- /dev/null +++ b/src/cp_library/cp_common.h @@ -0,0 +1,71 @@ +/******************************************************************************** + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_common.h + * Description: This head file is for some common definition + * + * Version: 1.0.0(11/13/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/13/2012 01:48:01 PM" + * + ********************************************************************************/ + +#ifndef __CP_COMMON_H +#define __CP_COMMON_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +//#define MEM_LEAK_CHECK + +#if 0 +static inline void *_t_malloc(size_t size +#ifdef MEM_LEAK_CHECK +, const char *file, unsigned int line +#endif + ) +{ + void *ptr; + + if ((ptr = malloc (size))) + memset (ptr, 0, size); +#ifdef MEM_LEAK_CHECK + printf ("MALLOC,0x%p @%s:%u\n", ptr, file, line); +#endif + return ptr; +} + +static inline void _t_free(void *ptr +#ifdef MEM_LEAK_CHECK +, const char *file, unsigned int line +#endif + ) +{ +#ifdef MEM_LEAK_CHECK + printf ("FREE,0x%p @%s:%u\n", ptr, file, line); +#endif + free(ptr); +} + +#ifdef MEM_LEAK_CHECK +#define t_free(p) if(p){ _t_free(p, __FILE__, __LINE__); p=NULL; } +#define t_malloc(s) _t_malloc(s, __FILE__, __LINE__) +#else +#define t_free(p) if(p){ _t_free(p); p=NULL; } +#define t_malloc(s) _t_malloc(s) +#endif + +#endif + +#endif /* end of __CP_COMMON_H */ + diff --git a/src/cp_library/cp_comport.c b/src/cp_library/cp_comport.c new file mode 100644 index 0000000..95991cf --- /dev/null +++ b/src/cp_library/cp_comport.c @@ -0,0 +1,600 @@ +/* ******************************************************************************** + * Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_comport.c + * Description: It's the comport operate library. + * + * Version: 1.0.0(10/17/2011~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "10/17/2011 03:33:25 PM" + * + ********************************************************************************/ + +#include "cp_comport.h" + +/************************************************************************************** + * Description: Set the comport structure + * Input Args: dev_name: The comport device name path, such as '/dev/ttyS3' + * baudrate: The baudrate, such as 115200 + * settings: The databit,parity,stopbit,flowctrl settings, such as '8N1N' + * Output Args: NONE + * Return Value: The comport_t structure pointer. + *************************************************************************************/ +comport_t *comport_init(const char *dev_name, int baudrate, const char *settings) +{ + comport_t *comport = NULL; + if (NULL == (comport = (comport_t *) malloc(sizeof(comport_t)))) + { + return NULL; + } + memset(comport, 0, sizeof(comport_t)); + comport->is_connted = 0; + comport->frag_size = 128; + + strncpy(comport->dev_name, dev_name, DEVNAME_LEN); + comport->baudrate = baudrate; + + set_settings(comport, settings); +#ifdef COMPORT_DEBUG + disp_settings(comport); +#endif + + return comport; +} + +#ifdef COMPORT_DEBUG +void disp_settings(comport_t * comport) +{ + comport_print("Device:\t\t\t\"%s\"\n", comport->dev_name); + comport_print("Baudrate:\t\t%ld\n", comport->baudrate); + comport_print("DataBit:\t\t\'%d\'\n", comport->databit); + switch (comport->parity) + { + case 0: + comport_print("Parity:\t\t\t\'N\'\n"); + break; + case 1: + comport_print("Parity:\t\t\t\'O\'\n"); + break; + case 2: + comport_print("Parity:\t\t\t\'E\'\n"); + break; + case 3: + comport_print("Parity:\t\t\t\'S\'\n"); + break; + } + comport_print("StopBit:\t\t\'%ld\'\n", (long int)comport->stopbit); + switch (comport->flowctrl) + { + case 0: + comport_print("FlowCtrl:\t\t\'N\'\n"); + break; + case 1: + comport_print("FlowCtrl:\t\t\'S\'\n"); + break; + case 2: + comport_print("FlowCtrl:\t\t\'H\'\n"); + break; + case 3: + comport_print("FlowCtrl:\t\t\'B\'\n"); + break; + } + comport_print("\n"); + return; +} +#endif + +/************************************************************************************** + * Description: Set the comport databit,parity,stopbit,flowctrl + * Input Args: comport: the comport_t pointer + * settings: The databit/parity/stopbit/flowctrl settings as like "8N1N" + * Output Args: NONE + * Return Value: NONE + *************************************************************************************/ +void set_settings(comport_t * comport, const char *settings) +{ + if(NULL==settings || NULL==comport) + return ; + + switch (settings[0]) /* data bit */ + { + case '7': + comport->databit = 7; + break; + case '8': + default: + comport->databit = 8; + break; + } + + switch (settings[1]) /* parity */ + { + case 'O': + case 'o': + comport->parity = 1; + break; + case 'E': + case 'e': + comport->parity = 2; + break; + case 'S': + case 's': + comport->parity = 3; + break; + case 'N': + case 'n': + default: + comport->parity = 0; + break; + } + + switch (settings[2]) /* stop bit */ + { + case '0': + comport->stopbit = 0; + break; + case '1': + default: + comport->stopbit = 1; + break; + } + + switch (settings[3]) /* flow control */ + { + case 'S': + case 's': + comport->flowctrl = 1; + break; + case 'H': + case 'h': + comport->flowctrl = 2; + break; + case 'B': + case 'b': + comport->flowctrl = 3; + break; + case 'N': + case 'n': + default: + comport->flowctrl = 0; + break; + } +} + +void comport_close(comport_t * comport) +{ + if (0 != comport->fd) + { + comport_print("Close device \"%s\"\n", comport->dev_name); + close(comport->fd); + } + comport->is_connted = 0x00; + comport->fd = -1; +} + +void comport_term(comport_t * comport) +{ + if(NULL == comport) + return; + + if (0 != comport->fd) + { + comport_close(comport); + } + memset(comport, 0x00, sizeof(comport_t)); + free(comport); + comport = NULL; + + return; +} + +int comport_open(comport_t * comport) +{ + int retval = -1; + struct termios old_cfg, new_cfg; + int old_flags; + long tmp; + + if(NULL==comport) + return -1; + + comport_close(comport); + + + /* Not a TTY device */ + if( !strstr(comport->dev_name, "tty")) + { + comport_print("Open Not tty device \"%s\"\n", comport->dev_name); + comport->fd = open(comport->dev_name, O_RDWR); + retval = comport->fd<0 ? -2 : comport->fd; + goto CleanUp; + } + + comport->fd = open(comport->dev_name, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (comport->fd < 0) + { + retval = -3; + goto CleanUp; + } + comport_print("Open device \"%s\"\n", comport->dev_name); + + 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)) + { + retval = -4; + goto CleanUp; + } + } + else // Failure + { + retval = -5; + goto CleanUp; + } + + if (0 != tcgetattr(comport->fd, &old_cfg)) + { + retval = -6; // Failed to get Com settings + goto CleanUp; + } + + 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); + new_cfg.c_oflag &= ~(OPOST); + + /* Set the data bit */ + switch (comport->databit) + { + case 0x07: + new_cfg.c_cflag |= CS7; + break; + case 0x06: + new_cfg.c_cflag |= CS6; + break; + case 0x05: + new_cfg.c_cflag |= CS5; + break; + default: + new_cfg.c_cflag |= CS8; + break; + } + + /* Set the parity */ + switch (comport->parity) + { + case 0x01: // Odd + new_cfg.c_cflag |= (PARENB | PARODD); + new_cfg.c_cflag |= (INPCK | ISTRIP); + break; + case 0x02: // Even + new_cfg.c_cflag |= PARENB; + new_cfg.c_cflag &= ~PARODD;; + new_cfg.c_cflag |= (INPCK | ISTRIP); + break; + case 0x03: + new_cfg.c_cflag &= ~PARENB; + new_cfg.c_cflag &= ~CSTOPB; + break; + default: + new_cfg.c_cflag &= ~PARENB; + } + + /* Set Stop bit */ + if (0x01 != comport->stopbit) + { + new_cfg.c_cflag |= CSTOPB; + } + else + { + new_cfg.c_cflag &= ~CSTOPB; + } + + /* Set flow control */ + switch (comport->flowctrl) + { + case 1: // Software control + case 3: + new_cfg.c_cflag &= ~(CRTSCTS); + new_cfg.c_iflag |= (IXON | IXOFF); + break; + case 2: // Hardware control + new_cfg.c_cflag |= CRTSCTS; // Also called CRTSCTS + new_cfg.c_iflag &= ~(IXON | IXOFF); + break; + default: // NONE + new_cfg.c_cflag &= ~(CRTSCTS); + new_cfg.c_iflag &= ~(IXON | IXOFF); + break; + } + + /* Set baudrate */ + switch (comport->baudrate) + { + case 115200: + tmp = B115200; + break; + case 57600: + tmp = B57600; + break; + case 38400: + tmp = B38400; + break; + case 19200: + tmp = B19200; + break; + case 9600: + tmp = B9600; + break; + case 4800: + tmp = B4800; + break; + case 2400: + tmp = B2400; + break; + case 1800: + tmp = B1800; + break; + case 1200: + tmp = B1200; + break; + case 600: + tmp = B600; + break; + case 300: + tmp = B300; + break; + case 200: + tmp = B200; + break; + case 150: + tmp = B150; + break; + case 134: + tmp = B134; + break; + case 110: + tmp = B110; + break; + case 75: + tmp = B75; + break; + case 50: + tmp = B50; + break; + default: + tmp = B115200; + } + cfsetispeed(&new_cfg, tmp); + cfsetispeed(&new_cfg, tmp); + + /* Set the Com port timeout settings */ + new_cfg.c_cc[VMIN] = 0; + new_cfg.c_cc[VTIME] = 0; + + tcflush(comport->fd, TCIFLUSH); + if (0 != tcsetattr(comport->fd, TCSANOW, &new_cfg)) + { + retval = -7; // Failed to set device com port settings + goto CleanUp; + } + + comport_print("Connected device \"%s\".\n", comport->dev_name); + comport->is_connted = 0x01; + retval = comport->fd; + +CleanUp: + comport_print("Open device \"%s\" %s.\n", comport->dev_name, retval>0 ? "successfully" : "failure"); + return retval; +} + +void nonblock() +{ + struct termios ttystate; + + //get the terminal state + tcgetattr(STDIN_FILENO, &ttystate); + + //turn off canonical mode + ttystate.c_lflag &= ~ICANON; + //minimum of number input read. + ttystate.c_cc[VMIN] = 1; + + //set the terminal attributes. + tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); +} + +int kbhit() +{ + struct timeval tv; + fd_set fds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0 + select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); + return FD_ISSET(STDIN_FILENO, &fds); +} + +int comport_recv(comport_t * comport, char *buf, int buf_size, unsigned long timeout) +{ + int retval = 0; // Function return value + int iRet; + fd_set stReadFds, stExcpFds; + struct timeval stTime; + + if (NULL == buf || 0 >= buf_size) + { + comport_print("%s() usage error.\n", __FUNCTION__); + retval = -1; + goto CleanUp; + } + + if (0x01 != comport->is_connted) + { + comport_print("%s() comport not connected.\n", __FUNCTION__); + retval = -2; + goto CleanUp; + } + + //printf("bufsize=%d timeout=%lu\n", buf_size, timeout); + + FD_ZERO(&stReadFds); + FD_ZERO(&stExcpFds); + FD_SET(comport->fd, &stReadFds); + FD_SET(comport->fd, &stExcpFds); + + if (0xFFFFFFFF != timeout) + { + stTime.tv_sec = (time_t) (timeout / 1000); + stTime.tv_usec = (long)(1000 * (timeout % 1000)); + + iRet = select(comport->fd + 1, &stReadFds, 0, &stExcpFds, &stTime); + if (0 == iRet) + { + retval = 0; // No data in Com port buffer + goto CleanUp; + } + else if (0 < iRet) + { + if (0 != FD_ISSET(comport->fd, &stExcpFds)) + { + retval = -6; // Error during checking recv status + comport_print("Error checking recv status.\n"); + goto CleanUp; + } + + if (0 == FD_ISSET(comport->fd, &stReadFds)) + { + retval = 0; // No incoming data + comport_print("No incoming data.\n"); + goto CleanUp; + } + } + else + { + if (EINTR == errno) + { + comport_print("catch interrupt signal.\n"); + retval = 0; // Interrupted signal catched + } + else + { + comport_print("Check recv status failure.\n"); + retval = -7; // Error during checking recv status + } + + goto CleanUp; + } + } + + usleep(10000); /* sleep for 10ms for data incoming */ + + // Get data from Com port + iRet = read(comport->fd, buf, buf_size); + if (0 > iRet) + { + if (EINTR == errno) + retval = 0; // Interrupted signal catched + else + retval = -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++) + { + printf("0x%02x ", buf[i]); + } + printf("\n"); + } +#endif + + retval = iRet; + + CleanUp: + return retval; + +} + +int comport_send(comport_t * comport, char *buf, int send_bytes) +{ + char *ptr, *end; + int retval = 0; + int send = 0; + + if (NULL == buf || 0 >= send_bytes) + { + comport_print("%s() Usage error.\n", __FUNCTION__); + retval = -1; + goto CleanUp; + } + + if (0x01 != comport->is_connted) // Comport not opened ? + { + retval = -3; + comport_print("Serail not connected.\n"); + goto CleanUp; + } + + //printf("Send %s with %d bytes.\n", buf, send_bytes); + + // Large data, then slice them and send + if (comport->frag_size < send_bytes) + { + ptr = buf; + end = buf + send_bytes; + + do + { + // Large than frag_size + if (comport->frag_size < (end - ptr)) + { + send = write(comport->fd, ptr, comport->frag_size); + if (0 >= send || comport->frag_size != send) + { + retval = -4; + goto CleanUp; + } + ptr += comport->frag_size; + } + else // Less than frag_size, maybe last fragmention. + { + send = write(comport->fd, ptr, (end - ptr)); + if (0 >= send || (end - ptr) != send) + { + retval = -4; + goto CleanUp; + } + ptr += (end - ptr); + } + } + while (ptr < end); + } + else // The send data is not large than a fragmention. + { + send = write(comport->fd, buf, send_bytes); + if (0 >= send || send_bytes != send) + { + retval = -5; + goto CleanUp; + } + } + + CleanUp: + return retval; +} + diff --git a/src/cp_library/cp_comport.h b/src/cp_library/cp_comport.h new file mode 100644 index 0000000..8d93d5c --- /dev/null +++ b/src/cp_library/cp_comport.h @@ -0,0 +1,67 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_comport.h + * Description: This head file is for the common TTY/Serial port operator library + * + * Version: 1.0.0(10/17/2011~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "10/17/2011 03:33:25 PM" + * + ********************************************************************************/ +#ifndef __CP_COMPORT_H +#define __CP_COMPORT_H + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/select.h> + +#define BUF_64 64 + +#ifndef DEVNAME_LEN +#define DEVNAME_LEN 64 +#endif + +//#define COMPORT_DEBUG +#ifdef COMPORT_DEBUG +#define comport_print(format,args...) printf(format, ##args) +#else +#define comport_print(format,args...) do{} while(0); +#endif + +//#define msleep(m) {struct timespec cSleep; cSleep.tv_sec = 0; cSleep.tv_nsec = m * 1000; nanosleep(&cSleep, 0);} + +typedef struct comport_s +{ + unsigned char databit, parity, stopbit, flowctrl, is_connted; + char dev_name[DEVNAME_LEN]; + unsigned char used; /* This comport used or not now */ + int fd; + int frag_size; + long baudrate; +} comport_t; + +comport_t *comport_init(const char *dev_name, int baudrate, const char *settings); +void comport_close(comport_t * comport); +int comport_open(comport_t * comport); +void comport_term(comport_t * comport); +int comport_recv(comport_t * comport, char *buf, int buf_size, unsigned long timeout); +int comport_send(comport_t * comport, char *buf, int send_bytes); + +void set_settings(comport_t * comport, const char *settings); +void disp_settings(comport_t * comport); +void nonblock(); +int kbhit(); + +#endif diff --git a/src/cp_library/cp_dictionary.c b/src/cp_library/cp_dictionary.c new file mode 100644 index 0000000..96fb783 --- /dev/null +++ b/src/cp_library/cp_dictionary.c @@ -0,0 +1,398 @@ +/*-------------------------------------------------------------------------*/ +/** + @file cp_dictionary.c + @author N. Devillard + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ +#include "cp_dictionary.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/** Maximum value size for integers and doubles. */ +#define MAXVALSZ 1024 + +/** Minimal allocated number of entries in a dictionary */ +#define DICTMINSZ 128 + +/** Invalid key token */ +#define DICT_INVALID_KEY ((char*)-1) + +/*--------------------------------------------------------------------------- + Private functions + ---------------------------------------------------------------------------*/ + +/* Doubles the allocated size associated to a pointer */ +/* 'size' is the current allocated size. */ +static void * mem_double(void * ptr, int size) +{ + void * newptr ; + + newptr = calloc(2*size, 1); + if (newptr==NULL) { + return NULL ; + } + memcpy(newptr, ptr, size); + free(ptr); + return newptr ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Duplicate a string + @param s String to duplicate + @return Pointer to a newly allocated string, to be freed with free() + + This is a replacement for strdup(). This implementation is provided + for systems that do not have it. + */ +/*--------------------------------------------------------------------------*/ +static char * xstrdup(const char * s) +{ + char * t ; + if (!s) + return NULL ; + t = (char*)malloc(strlen(s)+1) ; + if (t) { + strcpy(t,s); + } + return t ; +} + +/*--------------------------------------------------------------------------- + Function codes + ---------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ +/** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(const char * key) +{ + int len ; + unsigned hash ; + int i ; + + len = strlen(key); + for (hash=0, i=0 ; i<len ; i++) { + hash += (unsigned)key[i] ; + hash += (hash<<10); + hash ^= (hash>>6) ; + } + hash += (hash <<3); + hash ^= (hash >>11); + hash += (hash <<15); + return hash ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @return 1 newly allocated dictionary objet. + + This function allocates a new dictionary object of given size and returns + it. If you do not know in advance (roughly) the number of entries in the + dictionary, give size=0. + */ +/*--------------------------------------------------------------------------*/ +dictionary * dictionary_new(int size) +{ + dictionary * d ; + + /* If no size was specified, allocate space for DICTMINSZ */ + if (size<DICTMINSZ) size=DICTMINSZ ; + + if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) { + return NULL; + } + d->size = size ; + d->val = (char **)calloc(size, sizeof(char*)); + d->key = (char **)calloc(size, sizeof(char*)); + d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); + return d ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * d) +{ + int i ; + + if (d==NULL) return ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]!=NULL) + free(d->key[i]); + if (d->val[i]!=NULL) + free(d->val[i]); + } + free(d->val); + free(d->key); + free(d->hash); + free(d); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * dictionary_get(dictionary * d, const char * key, char * def) +{ + unsigned hash ; + int i ; + + hash = dictionary_hash(key); + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + /* Compare hash */ + if (hash==d->hash[i]) { + /* Compare string, to avoid hash collisions */ + if (!strcmp(key, d->key[i])) { + return d->val[i] ; + } + } + } + return def ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * d, const char * key, const char * val) +{ + int i ; + unsigned hash ; + + if (d==NULL || key==NULL) return -1 ; + + /* Compute hash for this key */ + hash = dictionary_hash(key) ; + /* Find if value is already in dictionary */ + if (d->n>0) { + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (hash==d->hash[i]) { /* Same hash value */ + if (!strcmp(key, d->key[i])) { /* Same key */ + /* Found a value: modify and return */ + if (d->val[i]!=NULL) + free(d->val[i]); + d->val[i] = val ? xstrdup(val) : NULL ; + /* Value has been modified: return */ + return 0 ; + } + } + } + } + /* Add a new value */ + /* See if dictionary needs to grow */ + if (d->n==d->size) { + + /* Reached maximum size: reallocate dictionary */ + d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ; + d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ; + d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; + if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) { + /* Cannot grow dictionary */ + return -1 ; + } + /* Double size */ + d->size *= 2 ; + } + + /* Insert key in the first empty slot. Start at d->n and wrap at + d->size. Because d->n < d->size this will necessarily + terminate. */ + for (i=d->n ; d->key[i] ; ) { + if(++i == d->size) i = 0; + } + /* Copy key */ + d->key[i] = xstrdup(key); + d->val[i] = val ? xstrdup(val) : NULL ; + d->hash[i] = hash; + d->n ++ ; + return 0 ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, const char * key) +{ + unsigned hash ; + int i ; + + if (key == NULL) { + return; + } + + hash = dictionary_hash(key); + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + /* Compare hash */ + if (hash==d->hash[i]) { + /* Compare string, to avoid hash collisions */ + if (!strcmp(key, d->key[i])) { + /* Found key */ + break ; + } + } + } + if (i>=d->size) + /* Key not found */ + return ; + + free(d->key[i]); + d->key[i] = NULL ; + if (d->val[i]!=NULL) { + free(d->val[i]); + d->val[i] = NULL ; + } + d->hash[i] = 0 ; + d->n -- ; + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(dictionary * d, FILE * out) +{ + int i ; + + if (d==NULL || out==NULL) return ; + if (d->n<1) { + fprintf(out, "empty dictionary\n"); + return ; + } + for (i=0 ; i<d->size ; i++) { + if (d->key[i]) { + fprintf(out, "%20s\t[%s]\n", + d->key[i], + d->val[i] ? d->val[i] : "UNDEF"); + } + } + return ; +} + + +/* Test code */ +#ifdef TESTDIC +#define NVALS 20000 +int main(int argc, char *argv[]) +{ + dictionary * d ; + char * val ; + int i ; + char cval[90] ; + + /* Allocate dictionary */ + printf("allocating...\n"); + d = dictionary_new(0); + + /* Set values in dictionary */ + printf("setting %d values...\n", NVALS); + for (i=0 ; i<NVALS ; i++) { + sprintf(cval, "%04d", i); + dictionary_set(d, cval, "salut"); + } + printf("getting %d values...\n", NVALS); + for (i=0 ; i<NVALS ; i++) { + sprintf(cval, "%04d", i); + val = dictionary_get(d, cval, DICT_INVALID_KEY); + if (val==DICT_INVALID_KEY) { + printf("cannot get value for key [%s]\n", cval); + } + } + printf("unsetting %d values...\n", NVALS); + for (i=0 ; i<NVALS ; i++) { + sprintf(cval, "%04d", i); + dictionary_unset(d, cval); + } + if (d->n != 0) { + printf("error deleting values\n"); + } + printf("deallocating...\n"); + dictionary_del(d); + return 0 ; +} +#endif +/* vim: set ts=4 et sw=4 tw=75 */ diff --git a/src/cp_library/cp_dictionary.h b/src/cp_library/cp_dictionary.h new file mode 100644 index 0000000..70fd42c --- /dev/null +++ b/src/cp_library/cp_dictionary.h @@ -0,0 +1,165 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file cp_dictionary.h + @author N. Devillard + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +#ifndef _CP_DICTIONARY_H_ +#define _CP_DICTIONARY_H_ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/*--------------------------------------------------------------------------- + New types + ---------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------*/ +/** + @brief Dictionary object + + This object contains a list of string/string associations. Each + association is identified by a unique string key. Looking up values + in the dictionary is speeded up by the use of a (hopefully collision-free) + hash function. + */ +/*-------------------------------------------------------------------------*/ +typedef struct _dictionary_ { + int n ; /** Number of entries in dictionary */ + int size ; /** Storage size */ + char ** val ; /** List of string values */ + char ** key ; /** List of string keys */ + unsigned * hash ; /** List of hash values for keys */ +} dictionary ; + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ +/** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(const char * key); + +/*-------------------------------------------------------------------------*/ +/** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @return 1 newly allocated dictionary objet. + + This function allocates a new dictionary object of given size and returns + it. If you do not know in advance (roughly) the number of entries in the + dictionary, give size=0. + */ +/*--------------------------------------------------------------------------*/ +dictionary * dictionary_new(int size); + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * vd); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * dictionary_get(dictionary * d, const char * key, char * def); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * vd, const char * key, const char * val); + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, const char * key); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(dictionary * d, FILE * out); + +#endif diff --git a/src/cp_library/cp_gprs.c b/src/cp_library/cp_gprs.c new file mode 100644 index 0000000..46c8345 --- /dev/null +++ b/src/cp_library/cp_gprs.c @@ -0,0 +1,677 @@ +/********************************************************************************* + * Copyright: (C) guowenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: module.c + * Description: This is the GPRS Power/SIM card control source code + * + * Version: 1.0.0(02/07/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "02/07/2012 03:08:25 PM" + * + ********************************************************************************/ + +#include "cp_gprs.h" +#include "cp_proc.h" + +int init_gsm_module(modinfo_t *module) +{ + int i=0; + char devname[DEVNAME_LEN]; + + log_info("Initialize GSM module context\n"); + + memset(module, 0, sizeof(*module)); + + module->event = REQ_POWER_RESET; /* Request to power on GPRS */ + + pthread_mutex_init(&module->lock, NULL); + + if( !(module->gsmport=comport_init(GSM_DATAPORT, 115200, "8N1N")) ) + { + return -1; + } + + /* Initialize the GPRS dataport pointer */ + module->comport_cnt = MAX_DATAPORT; + for(i=0; i<module->comport_cnt; i++) + { + snprintf(devname, DEVNAME_LEN, "%s%d", CMUX_DATAPORT, i+CMUX_PORT_START_INDEX); + module->comport[i] = comport_init(devname, 115200, "8N1N"); + if(NULL == module->comport[i]) + goto ERROR; + + //log_dbg("Initialise comport [%p] on %s on baudrate 115200 with 8N1N\n", module->comport[i], devname); + } + + return 0; + +ERROR: + while(i-- >= 0) + { + comport_term(module->comport[i]); + } + return -2; +} + + +/* + * Description: close all the GPRS control port device. + * + * Return Value: NONE + */ +void close_gsm_dataport(modinfo_t *module) +{ + int i; + + for(i=0; i<module->comport_cnt; i++) + { + log_nrml("Close GPRS dataport %s=>[fd:%d]\n", module->comport[i]->dev_name, module->comport[i]->fd); + comport_close(module->comport[i]); + } + + return ; +} + +/* + * Description: open all the GPRS data port device. + * + * Return Value: 0: Open successfully !0: Open failure + */ +int open_gsm_dataport(modinfo_t *module) +{ + int i, retval = -1; + + for(i=0; i<module->comport_cnt; i++) + { + if( (retval=comport_open(module->comport[i])) < 0) + { + log_err("Open GPRS dataport %s failure: %d\n", module->comport[i]->dev_name, retval); + goto ERROR; + } + log_nrml("Open GPRS dataport[%p] %s=>[fd:%d]\n", module->comport, module->comport[i]->dev_name, module->comport[i]->fd); + } + + return 0; + +ERROR: + close_gsm_dataport(module); + return retval; +} + + +/* + * Description: Alloc a comport from the comport poor for used, for we must make sure + * PPP thread can grasp a comport to work, so we will always preserved + * first comport for PPP thread. + * + * Return Value: NULL: No comport left !NULL: The alloced comport pointer + */ +comport_t *alloc_gsm_dataport(char *who, modinfo_t *module) +{ + static int lock = 0; + int i; + comport_t *comport = NULL; + + if(lock) + return NULL; + + lock = 1; + + /* Preserve first comport for PPP thread */ + for(i=0; i<module->comport_cnt; i++) + { + if(module->comport[i]->used != YES) + { + comport = module->comport[i]; + comport->used = YES; + break; + } + } + + lock = 0; + + if(comport) + { + log_dbg("%s allocate GPRS dataport[%d] %s=>[fd:%d] ok\n", who, i, comport->dev_name, comport->fd); + module->users++; + } + else + { + log_dbg("%s allocate GPRS dataport failure\n", who); + } + + + return comport; +} + +/* + * Description: Free a comport to the comport poor when we no need it. + * + * Return Value: NONE + */ +void free_gsm_dataport(char *who, modinfo_t *module, comport_t **comport) +{ + int i; + + /* Preserve first comport for PPP thread */ + for(i=0; i<module->comport_cnt; i++) + { + if(module->comport[i] == *comport) + { + log_dbg("%s free GPRS dataport %s=>[fd:%d]\n", who, (*comport)->dev_name, (*comport)->fd); + module->comport[i]->used = NO; + module->users--; + *comport = NULL; + break; + } + } + + return ; +} + + +/* + * Description: Power on the GPRS module and open all the gprs data port. For when GPRS power off, + * the GPRS module USB convert serial comport device lost, so we must bind them together. + * Return Value: 0: Open successfully !0: Open failure + */ +int power_on_module(modinfo_t *module) +{ + int rv = 0; + log_dbg("Turn on GPRS module now\n"); + + micro_second_sleep(500); + + if( comport_open(module->gsmport) < 0) + { + log_err("Open GSM serial port [%s] failure\n", module->gsmport->dev_name); + return -2; + } + + if( !atcmd_check_power_on(module->gsmport) ) + { + log_err("Send ATE0 check GPRS module AT command failure\n", module->gsmport->dev_name); + return -2; + } + + if( 0 != open_gsm_dataport(module) ) + { + rv = -3; + log_err("Open GPRS module comport failure\n"); + goto cleanup; + } + +cleanup: + + if(!rv) + { + module->ready = MODULE_READY; + log_nrml("Turn on GPRS module succesfully\n"); + } + else + { + module->ready = NOT_READY; + log_nrml("Turn on GPRS module failed, rv=%d\n", rv); + } + + module->ready = 0!=rv ? NOT_READY : MODULE_READY; + memset(&(module->reg), 0, sizeof(reginfo_t)); + + return rv; +} + +/* + * Description: Power off the GPRS module and close all the gprs data port. For when GPRS power off, + * the GPRS module USB convert serial comport device lost, so we must bind them together. + * Return Value: 0: Open successfully !0: Open failure + */ +int power_off_module(modinfo_t *module) +{ + while(module->users) + { + log_warn("%d application still use the GPRS module dataport, wait released...\n", module->users); + sleep(1); + } + + log_nrml("Turn off GPRS module now\n"); + close_gsm_dataport(module); + + comport_close(module->gsmport); + + module->ready = NOT_READY; + memset(&(module->reg), 0, sizeof(reginfo_t)); + + micro_second_sleep(800); + return 0; +} + +void set_module_event(modinfo_t *module, int request) +{ + log_dbg("Set the module event request[%d]\n", request); + pthread_mutex_lock(&module->lock); + module->event = request; + pthread_mutex_unlock(&module->lock); +} + +/* + * Description: Set the GPRS power status according to the module request event. + * the GPRS module USB convert serial comport device lost, so we must bind them together. + * Return Value: 0:Set GPRS power status successfully !0: Open failure + */ +int set_module_event_power(modinfo_t *module) +{ + int rv = 0; + switch (module->event) + { + case REQ_POWER_ON: + rv = power_on_module(module); + break; + + case REQ_POWER_OFF: + rv = power_off_module(module); + break; + + case REQ_POWER_RESET: + rv = power_off_module(module); + rv = power_on_module(module); + break; + } + + if( rv ) + { + log_nrml("Response for the GPRS request event %d failure, rv=%d\n", module->event, rv); + return rv; + } + else + { + log_nrml("Response for the GPRS request event %d and clear this event ok\n", module->event); + set_module_event(module, REQ_EVENT_NONE); + return 0; + } +} + + +int atcmd_inspect_status(modinfo_t *module) +{ + comport_t *comport; + reginfo_t *reg = &(module->reg); /* SIM card Register information */ + char *who = "atcmd_inspect_status()"; + + static unsigned long start_time ; + + /* Every 10 seconds we will inspect it */ + if(start_time && time_elapsed(start_time) < 10000) + { + return 0; + } + + start_time= time_now(); + + comport = alloc_gsm_dataport(who, module); + if(NULL != comport) + { + log_dbg("Alloc dataport %s=>[fd:%d]\n", comport->dev_name, comport->fd); + } + else + return -1; + + reg->signal = atcmd_check_gprs_signal(comport); + + atcmd_check_gprs_location(comport, ®->loc); + + log_dbg("Free dataport %s=>[fd:%d]\n", comport->dev_name, comport->fd); + free_gsm_dataport(who, module, &comport); + + return 0; +} + + +/* + * Description: Check the AT command and SIM card ready or not + * Input value: times: Max check times + *Return Value: 1: Already OK 0: Still on test -1: Failure + * + */ +int atcmd_check_module_ready(comport_t *comport, int times) +{ + int rv; + static int fail_cnt = 0; + + /* Send ATE0 to check GPRS module AT ready or not */ + rv = atcmd_check_at_ready(comport); + if(rv) + goto cleanup; + + /* Send AT+CPIN? to check SIM card valid or not */ + rv = atcmd_check_sim_valid(comport); + +cleanup: + if(rv) + { + if(fail_cnt < times) + { + fail_cnt ++; + return 0; + } + else + { + fail_cnt = 0; + return -1; + } + } + else + { + fail_cnt = 0; + return 1; + } +} + +int atcmd_check_power_on(comport_t *comport) +{ + int i, rv = 0; + + for(i=0; i<10; i++) + { + if( !atcmd_check_at_ready(comport) ) + { + rv = 1; + break; + } + } + + return rv; +} + +/* + * Some preset command here, such as set Module as 2G mode + */ +int atcmd_module_preset(comport_t *comport) +{ + int retval = 0; + + /* Send AT+COPS=0 to set GPRS module auto select an operator */ + if(0 != (retval=send_atcmd_check_ok(comport, "AT+COPS=0\r", 3000)) ) + { + log_warn("AT+COPS=0 Set Auto Select Carrier: [FAILED]\n"); + return -2; + } + log_nrml("AT+COPS=0 Set Auto Select Carrier: [OK]\n"); + + return retval; +} + + +int atcmd_check_hwinfo(comport_t *comport, hwinfo_t *hw, int times) +{ + int rv; + static int fail_cnt = 0; + + /* Send AT+CGMM to check GPRS module hardware name and type */ + rv = atcmd_check_gprs_name(comport, hw->model); + if(rv) + goto cleanup; + + /* Send AT+CGMR to check GPRS module hardware version */ + rv = atcmd_check_gprs_version(comport, hw->mrev); + if(rv) + goto cleanup; + + /* Send AT+CGSN to check GPRS module hardware IEMI number*/ + rv = atcmd_check_gprs_iemi(comport, hw->iemi); + +cleanup: + if(rv) + { + if(fail_cnt < times) + { + fail_cnt ++; + return 0; + } + else + { + fail_cnt = 0; + return -1; + } + } + else + { + fail_cnt = 0; + return 1; + } +} + + +int atcmd_check_regist(comport_t *comport, reginfo_t *reg, int times) +{ + int rv = 0; + static int fail_cnt = 0; + + /* Send AT+CSQ to check GPRS signal strength */ + reg->signal = atcmd_check_gprs_signal(comport); + if(reg->signal<2 && reg->signal>31) + { + rv = -1; + goto cleanup; + } + + /* Send AT+CREG? to check SIM card regist to network or not */ + reg->type = atcmd_check_gprs_register(comport); + + if(REG_HOMEWORK==reg->type || REG_ROAMING== reg->type) + { + /* SIM card register successfully */ + log_nrml("SIM card register successfully.\n"); + rv = 0; + } + else if(REG_DENIED==reg->type) + { + /* SIM card can not register, so request to switch SIM card */ + log_err("SIM card regist denied.\n"); + rv = -2; + goto cleanup; + } + else + { + log_err("SIM card regist failure.\n"); + rv = -3; + goto cleanup; + } + + rv = atcmd_check_gprs_carrier(comport, reg->carrier); + if(rv) + { + rv = -4; + log_err("Check SIM card acrrier failure.\n"); + goto cleanup; + } + + rv = atcmd_check_gprs_mcc_mnc(comport, reg->loc.mcc_mnc); + if(rv) + { + rv = -4; + log_err("Check SIM card register MCC-MNC failure\n"); + goto cleanup; + } + + +cleanup: + if(rv) + { + if(fail_cnt < times) + { + fail_cnt ++; + return 0; + } + else + { + fail_cnt = 0; + return -1; + } + } + else + { + fail_cnt = 0; + return 1; + } + +} + +int atcmd_check_network_info(comport_t *comport, reginfo_t *reg) +{ + int retval = 0; + + /* Send AT+COPS to check SIM card registed network carrier */ + retval = atcmd_check_gprs_carrier(comport, reg->carrier); + if(retval) return -1; + + /* Send AT+QGSMLOC=1 to check GPRS module location */ + retval = atcmd_check_gprs_location(comport, &(reg->loc)); + if(retval) return -3; + + return retval; +} + + +int atcmd_check_ready(modinfo_t *module) +{ + int rv; + comport_t *comport; + char *who = "atcmd_check_ready()"; + + log_nrml("AT command check GPRS registry status now\n"); + /* Alloc a comport to send AT command */ + comport = alloc_gsm_dataport(who, module); + if(NULL == comport) + { + log_warn("Alloc dataport for %s() failure.\n", __FUNCTION__); + return -1; + } + log_dbg("Alloc dataport %s=>[fd:%d]\n", comport->dev_name, comport->fd); + + switch(module->ready) + { + case MODULE_READY: + rv = atcmd_check_module_ready(comport, 10); + if(rv > 0) + { + log_nrml("GPRS AT command ready and SIM card valid successfully\n"); + module->ready++; /* Continue to check, not break */ + } + else if(rv == 0) + { + log_nrml("GPRS AT command ready and SIM card valid check continue...\n"); + } + else if(rv < 0) + { + log_nrml("GPRS AT command ready and SIM card valid check failed\n"); + set_module_event(module, REQ_POWER_RESET); + } + break; + + case CHECK_HWINFO: + rv = atcmd_check_hwinfo(comport, &(module->hw), 10); + if(rv > 0) + { + log_nrml("Check the GPRS Module hardware information successfully\n"); + module->ready++; /* Continue to check, not break */ + } + else if(rv == 0) + { + log_nrml("Check the GPRS Module hardware information continue...\n"); + } + else if(rv < 0) + { + log_nrml("Check the GPRS Module hardware information failed...\n"); + set_module_event(module, REQ_POWER_RESET); + } + break; + + case CHECK_REGISTRY: + rv = atcmd_check_regist(comport, &(module->reg), 10); + if(rv > 0) + { + log_nrml("Check the SIM card registry successfully\n"); + module->ready++; /* Continue to check, not break */ + } + else if(rv == 0) + { + log_nrml("Check the SIM card registry continue...\n"); + } + else if(rv < 0) + { + log_nrml("Check the SIM card registry failed\n"); + set_module_event(module, REQ_POWER_RESET); + } + break; + } + + free_gsm_dataport(who, module, &comport); + return rv; +} + + +/* + * Description: Terminate the control thread work context + * Return Value: NONE + */ +void ctrl_thread_term(void *thread_arg) +{ + int i=0; + modinfo_t *module = (modinfo_t *)thread_arg; + + log_nrml("start terminate GPRS main(control) thread\n"); + + power_off_module(module); + + for(i=0; i<module->comport_cnt; i++) + { + comport_term(module->comport[i]); + } +} + + +/* Description: Continue the main thread as the control thread, which used to control/inspect the GPRS + * module power status. If it exit, then whole program exit.So when it exit, we must + * make sure all other threads exit. + * + *Return Value: NONE + */ +void ctrl_thread_start(void *thread_arg) +{ + modinfo_t *module = (modinfo_t *)thread_arg; + + log_nrml("GPRS power control thread start running\n"); + + while( !g_cp_signal.stop ) + { + /* If someone request to change the power status, then set the power*/ + if(module->event) + { + set_module_event_power(module); + } + + if(ALL_READY == module->ready) + { + /* If GPRS already registed then we just need to inspect the GPRS + * module status, such as signal, location */ + atcmd_inspect_status(module); + } + else + { + /* If GPRS module just already power on, then we use AT command to + * check the SIM card can work or not now. */ + if( ON == module->pwr_status ) + { + atcmd_check_ready(module); + } + } + + sleep(1); + } + + /* thread exit */ + ctrl_thread_term(thread_arg); + + return ; +} diff --git a/src/cp_library/cp_gprs.h b/src/cp_library/cp_gprs.h new file mode 100644 index 0000000..bb3f075 --- /dev/null +++ b/src/cp_library/cp_gprs.h @@ -0,0 +1,128 @@ +/******************************************************************************** + * Copyright: (C) guowenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: module.h + * Description: This is the GPRS module head file + * + * Version: 1.0.0(02/02/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "02/02/2012 11:33:33 AM" + * + ********************************************************************************/ + +#ifndef __CP_GPRS_H +#define __CP_GPRS_H + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <pthread.h> + +#include "cp_common.h" +#include "cp_logger.h" +#include "cp_comport.h" +#include "cp_atcmd.h" +#include "cp_time.h" + +#define YES 1 +#define NO 0 + +#define OFF 0 +#define ON 1 + +#define GSM_CTRLPORT "/dev/gprs" +#define GSM_DATAPORT "/dev/ttyS2" + +#define MAX_DATAPORT 4 /* CMUX driver will generate 4 TTY port */ +#define CMUX_PORT_START_INDEX 1 +#define CMUX_DATAPORT "/dev/gsmtty" + +/*GPRS module power status control request event*/ +enum +{ + REQ_EVENT_NONE = 0, + REQ_POWER_OFF, + REQ_POWER_ON, + REQ_POWER_RESET, +}; + +/* GPRS Module Ready status */ +enum +{ + NOT_READY, + MODULE_READY, /* GPRS AT command active and SIM card valid */ + CHECK_HWINFO, /* Get GPRS hardware Information */ + CHECK_REGISTRY, /* Check GPRS register network information */ + ALL_READY, +}; + +/* AT+CREG? command show SIM card register status */ +enum +{ + REG_UNREGIST = 0, /* not registered, ME is not currently searching a new operator to register to */ + REG_HOMEWORK, /* registered, home network */ + REG_SEARCHING, /* not registered, but ME is currently searching a new operator to register to */ + REG_DENIED, /* registration denied */ + REG_UNKNOW, /* unknow */ + REG_ROAMING, /* registered, roaming */ +}; + +/* AT+CNMP=? command to set GPRS module work on which mode */ +#define MODE_AUTO 2 /* Automatic */ +#define MODE_GSM 13 /* GSM Only */ +#define MODE_WCDMA 14 /* WCDMA Only */ + +/* AT+CNSMOD? command show network system mode */ +enum +{ + NS_NONE = 0, /* No service */ + NS_GSM, /* GSM */ + NS_GPRS, /* GPRS */ + NS_EGPRS, /* EGPRS(EDGE) */ + NS_WCDMA, /* WCDMA */ + NS_HSDPA, /* HSDPA only */ + NS_HSUPA, /* HSUPA only */ + NS_HSPA, /* HSDPA and HSUPA */ +}; + +typedef struct modinfo_s +{ + int users; /* How many users use the module now */ + unsigned char ready; /* SIM card regist and can work or not */ + unsigned char event; /* Request to set GPRS power REQ_POWER_ON, REQ_POWER_OFF or REQ_POWER_RESET */ + unsigned char pwr_status; /* Current module power status */ + + pthread_mutex_t lock; /* Module control mutex lock */ + + hwinfo_t hw; /* GPRS Hardware information */ + reginfo_t reg; /* SIM card register network information */ + + comport_t *gsmport; /* The GSM hardware UART port */ + + int comport_cnt; /* GPRS data channel count */ + comport_t *comport[MAX_DATAPORT]; /* CMUX driver generate CMUX port */ +} modinfo_t; + + +extern int init_gsm_module(modinfo_t *module); +extern int open_gsm_dataport(modinfo_t *module); +extern void close_gsm_dataport(modinfo_t *module); +extern comport_t *alloc_gsm_dataport(char *who, modinfo_t *module); +extern void free_gsm_dataport(char *who, modinfo_t *module, comport_t **comport); + +extern int power_on_module(modinfo_t *module); +extern int power_off_module(modinfo_t *module); + +extern int atcmd_check_ready(modinfo_t *module); +extern int atcmd_inspect_status(modinfo_t *module); +extern int atcmd_check_power_on(comport_t *comport); +//unsigned char atcmd_inspect(modinfo_t *module, comport_t *comport); + +extern void set_module_event(modinfo_t *module, int request); +extern int set_module_event_power(modinfo_t *module); + +extern void ctrl_thread_start(void *thread_arg); +extern void ctrl_thread_term(void *thread_arg); + +#endif /* End of __CP_GPRS_H */ diff --git a/src/cp_library/cp_iniparser.c b/src/cp_library/cp_iniparser.c new file mode 100644 index 0000000..b18cf84 --- /dev/null +++ b/src/cp_library/cp_iniparser.c @@ -0,0 +1,807 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file cp_iniparser.c + @author N. Devillard + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ +/*---------------------------- Includes ------------------------------------*/ +#include <ctype.h> +#include "cp_iniparser.h" + +/*---------------------------- Defines -------------------------------------*/ +#define ASCIILINESZ (1024) +#define INI_INVALID_KEY ((char*)-1) + +/*--------------------------------------------------------------------------- + Private to this module + ---------------------------------------------------------------------------*/ +/** + * This enum stores the status for each parsed line (internal use only). + */ +typedef enum _line_status_ { + LINE_UNPROCESSED, + LINE_ERROR, + LINE_EMPTY, + LINE_COMMENT, + LINE_SECTION, + LINE_VALUE +} line_status ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Convert a string to lowercase. + @param s String to convert. + @return ptr to statically allocated string. + + This function returns a pointer to a statically allocated string + containing a lowercased version of the input string. Do not free + or modify the returned string! Since the returned string is statically + allocated, it will be modified at each function call (not re-entrant). + */ +/*--------------------------------------------------------------------------*/ +static char * strlwc(const char * s) +{ + static char l[ASCIILINESZ+1]; + int i ; + + if (s==NULL) return NULL ; + memset(l, 0, ASCIILINESZ+1); + i=0 ; + while (s[i] && i<ASCIILINESZ) { + l[i] = (char)tolower((int)s[i]); + i++ ; + } + l[ASCIILINESZ]=(char)0; + return l ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Remove blanks at the beginning and the end of a string. + @param s String to parse. + @return ptr to statically allocated string. + + This function returns a pointer to a statically allocated string, + which is identical to the input string, except that all blank + characters at the end and the beg. of the string have been removed. + Do not free or modify the returned string! Since the returned string + is statically allocated, it will be modified at each function call + (not re-entrant). + */ +/*--------------------------------------------------------------------------*/ +static char * strstrip(const char * s) +{ + static char l[ASCIILINESZ+1]; + char * last ; + + if (s==NULL) return NULL ; + + while (isspace((int)*s) && *s) s++; + memset(l, 0, ASCIILINESZ+1); + strcpy(l, s); + last = l + strlen(l); + while (last > l) { + if (!isspace((int)*(last-1))) + break ; + last -- ; + } + *last = (char)0; + return (char*)l ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getnsec(dictionary * d) +{ + int i ; + int nsec ; + + if (d==NULL) return -1 ; + nsec=0 ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (strchr(d->key[i], ':')==NULL) { + nsec ++ ; + } + } + return nsec ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getsecname(dictionary * d, int n) +{ + int i ; + int foundsec ; + + if (d==NULL || n<0) return NULL ; + foundsec=0 ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (strchr(d->key[i], ':')==NULL) { + foundsec++ ; + if (foundsec>n) + break ; + } + } + if (foundsec<=n) { + return NULL ; + } + return d->key[i] ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(dictionary * d, FILE * f) +{ + int i ; + + if (d==NULL || f==NULL) return ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (d->val[i]!=NULL) { + fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); + } else { + fprintf(f, "[%s]=UNDEF\n", d->key[i]); + } + } + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump_ini(dictionary * d, FILE * f) +{ + int i ; + int nsec ; + char * secname ; + + if (d==NULL || f==NULL) return ; + + nsec = iniparser_getnsec(d); + if (nsec<1) { + /* No section in file: dump all keys as they are */ + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + fprintf(f, "%s = %s\n", d->key[i], d->val[i]); + } + return ; + } + for (i=0 ; i<nsec ; i++) { + secname = iniparser_getsecname(d, i) ; + iniparser_dumpsection_ini(d, secname, f) ; + } + fprintf(f, "\n"); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary section to a loadable ini file + @param d Dictionary to dump + @param s Section name of dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given section of a given dictionary into a loadable ini + file. It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f) +{ + int j ; + char keym[ASCIILINESZ+1]; + int seclen ; + + if (d==NULL || f==NULL) return ; + if (! iniparser_find_entry(d, s)) return ; + + seclen = (int)strlen(s); + fprintf(f, "\n[%s]\n", s); + sprintf(keym, "%s:", s); + for (j=0 ; j<d->size ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) { + fprintf(f, + "%-30s = %s\n", + d->key[j]+seclen+1, + d->val[j] ? d->val[j] : ""); + } + } + fprintf(f, "\n"); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return Number of keys in section + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getsecnkeys(dictionary * d, char * s) +{ + int seclen, nkeys ; + char keym[ASCIILINESZ+1]; + int j ; + + nkeys = 0; + + if (d==NULL) return nkeys; + if (! iniparser_find_entry(d, s)) return nkeys; + + seclen = (int)strlen(s); + sprintf(keym, "%s:", s); + + for (j=0 ; j<d->size ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) + nkeys++; + } + + return nkeys; + +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return pointer to statically allocated character strings + + This function queries a dictionary and finds all keys in a given section. + Each pointer in the returned char pointer-to-pointer is pointing to + a string allocated in the dictionary; do not free or modify them. + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ +char ** iniparser_getseckeys(dictionary * d, char * s) +{ + + char **keys; + + int i, j ; + char keym[ASCIILINESZ+1]; + int seclen, nkeys ; + + keys = NULL; + + if (d==NULL) return keys; + if (! iniparser_find_entry(d, s)) return keys; + + nkeys = iniparser_getsecnkeys(d, s); + + keys = (char**) malloc(nkeys*sizeof(char*)); + + seclen = (int)strlen(s); + sprintf(keym, "%s:", s); + + i = 0; + + for (j=0 ; j<d->size ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) { + keys[i] = d->key[j]; + i++; + } + } + + return keys; + +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getstring(dictionary * d, const char * key, char * def) +{ + char * lc_key ; + char * sval ; + + if (d==NULL || key==NULL) + return def ; + + lc_key = strlwc(key); + sval = dictionary_get(d, lc_key, def); + return sval ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + "42" -> 42 + "042" -> 34 (octal -> decimal) + "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(dictionary * d, const char * key, int notfound) +{ + char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return (int)strtol(str, NULL, 0); +} + +int iniparser_getlong(dictionary * d, const char * key, int notfound) +{ + char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return strtol(str, NULL, 0); +} +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(dictionary * d, const char * key, double notfound) +{ + char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return atof(str); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(dictionary * d, const char * key, int notfound) +{ + char * c ; + int ret ; + + c = iniparser_getstring(d, key, INI_INVALID_KEY); + if (c==INI_INVALID_KEY) return notfound ; + if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { + ret = 1 ; + } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { + ret = 0 ; + } else { + ret = notfound ; + } + return ret; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry( + dictionary * ini, + const char * entry +) +{ + int found=0 ; + if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { + found = 1 ; + } + return found ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, -1 is returned. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, const char * entry, const char * val) +{ + return dictionary_set(ini, strlwc(entry), val) ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, const char * entry) +{ + dictionary_unset(ini, strlwc(entry)); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Load a single line from an INI file + @param input_line Input line, may be concatenated multi-line input + @param section Output space to store section + @param key Output space to store key + @param value Output space to store value + @return line_status value + */ +/*--------------------------------------------------------------------------*/ +static line_status iniparser_line( + char * input_line, + char * section, + char * key, + char * value) +{ + line_status sta ; + char line[ASCIILINESZ+1]; + static char left_line[ASCIILINESZ+1]; + int len, offset ; + + strcpy(line, strstrip(input_line)); + len = (int)strlen(line); + + sta = LINE_UNPROCESSED ; + if (len<1) { + /* Empty line */ + sta = LINE_EMPTY ; + memset(input_line, 0, len); + } else if (line[0]=='#' || line[0]==';') { + /* Comment line */ + sta = LINE_COMMENT ; + memset(input_line, 0, len); + } else if (line[0]=='[') { + /* Section name */ + sscanf(line, "[%[^]]", section); + strcpy(section, strstrip(section)); + strcpy(section, strlwc(section)); + + /* Left configure will go to next time to parser */ + offset = strlen(section) + 2; + strcpy( left_line, strstrip(&(line[offset])) ); + strcpy( left_line, strstrip(left_line)); + + if( strlen(left_line) > 0) + { + strcpy(input_line, left_line); + strcat(input_line, "\n"); + } + else + { + memset(input_line, 0, len); + } + sta = LINE_SECTION ; + } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 + || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 + || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { + char *ptr = NULL; + + /* Usual key=value, with or without comments */ + strcpy(key, strstrip(key)); + strcpy(key, strlwc(key)); + strcpy(value, strstrip(value)); + /* + * sscanf cannot handle '' or "" as empty values + * this is done here + */ + if (!strncmp(value, "\"\"", 2) || (!strncmp(value, "''", 2)) ) { + value[0]=0 ; + } + + ptr = strchr(line, '='); + if('\''==*(ptr+1) || '\"'==*(ptr+1)) + { + offset = strlen(key)+strlen(value) + 1 + 2; /* Skip $key='$val' */ + } + else + { + offset = strlen(key)+strlen(value) + 1; /* Skip $key=$val */ + } + strcpy( left_line, strstrip(&(line[offset])) ); + + if( strlen(left_line) > 0) + { + strcpy(input_line, left_line); + strcat(input_line, "\n"); + } + else + { + memset(input_line, 0, len); + } + sta = LINE_VALUE ; + } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 + || sscanf(line, "%[^=] %[=]", key, value) == 2) { + /* + * Special cases: + * key= + * key=; + * key=# + */ + strcpy(key, strstrip(key)); + strcpy(key, strlwc(key)); + value[0]=0 ; + sta = LINE_VALUE ; + } else { + /* Generate syntax error */ + sta = LINE_ERROR ; + memset(input_line, 0, len); + } + return sta ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(const char * ininame) +{ + FILE * in ; + + char line [ASCIILINESZ+1] ; + char section [ASCIILINESZ+1] ; + char key [ASCIILINESZ+1] ; + char tmp [ASCIILINESZ+1] ; + char val [ASCIILINESZ+1] ; + + int last=0 ; + int len ; + int lineno=0 ; + int errs=0; + + dictionary * dict ; + + if ((in=fopen(ininame, "r"))==NULL) { + fprintf(stderr, "iniparser: cannot open %s\n", ininame); + return NULL ; + } + + dict = dictionary_new(0) ; + if (!dict) { + fclose(in); + return NULL ; + } + + memset(line, 0, ASCIILINESZ); + memset(section, 0, ASCIILINESZ); + memset(key, 0, ASCIILINESZ); + memset(val, 0, ASCIILINESZ); + last=0 ; + + while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { + lineno++ ; +CONTINUE_PARSER: + len = (int)strlen(line)-1; + if (len==0) + continue; + /* Safety check against buffer overflows */ + if (line[len]!='\n') { + fprintf(stderr, + "iniparser: input line too long in %s (%d)\n", + ininame, + lineno); + dictionary_del(dict); + fclose(in); + return NULL ; + } + /* Get rid of \n and spaces at end of line */ + while ((len>=0) && + ((line[len]=='\n') || (isspace(line[len])))) { + line[len]=0 ; + len-- ; + } + /* Detect multi-line */ + if (line[len]=='\\') { + /* Multi-line value */ + last=len ; + continue ; + } else { + last=0 ; + } + + switch ( iniparser_line(line, section, key, val) ) { + case LINE_EMPTY: + case LINE_COMMENT: + break ; + + case LINE_SECTION: + errs = dictionary_set(dict, section, NULL); + break ; + + case LINE_VALUE: + sprintf(tmp, "%s:%s", section, key); + errs = dictionary_set(dict, tmp, val) ; + break ; + + case LINE_ERROR: + fprintf(stderr, "iniparser: syntax error in %s (%d):\n", + ininame, + lineno); + fprintf(stderr, "-> %s\n", line); + errs++ ; + break; + + default: + break ; + } + + if( strlen(line) > 0) + { + goto CONTINUE_PARSER; + } + + memset(line, 0, ASCIILINESZ); + last=0; + if (errs<0) { + fprintf(stderr, "iniparser: memory allocation failure\n"); + break ; + } + } + if (errs) { + dictionary_del(dict); + dict = NULL ; + } + fclose(in); + return dict ; +} + + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d) +{ + dictionary_del(d); +} + +/* vim: set ts=4 et sw=4 tw=75 */ diff --git a/src/cp_library/cp_iniparser.h b/src/cp_library/cp_iniparser.h new file mode 100644 index 0000000..1e5de9c --- /dev/null +++ b/src/cp_library/cp_iniparser.h @@ -0,0 +1,308 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file cp_iniparser.h + @author N. Devillard + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ + +#ifndef _CP_INIPARSER_H_ +#define _CP_INIPARSER_H_ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* + * The following #include is necessary on many Unixes but not Linux. + * It is not needed for Windows platforms. + * Uncomment it if needed. + */ +/* #include <unistd.h> */ + +#include "cp_dictionary.h" + +/*-------------------------------------------------------------------------*/ +/** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ + +int iniparser_getnsec(dictionary * d); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ + +char * iniparser_getsecname(dictionary * d, int n); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ + +void iniparser_dump_ini(dictionary * d, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary section to a loadable ini file + @param d Dictionary to dump + @param s Section name of dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given section of a given dictionary into a loadable ini + file. It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ + +void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(dictionary * d, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return Number of keys in section + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getsecnkeys(dictionary * d, char * s); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return pointer to statically allocated character strings + + This function queries a dictionary and finds all keys in a given section. + Each pointer in the returned char pointer-to-pointer is pointing to + a string allocated in the dictionary; do not free or modify them. + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ +char ** iniparser_getseckeys(dictionary * d, char * s); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getstring(dictionary * d, const char * key, char * def); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + - "42" -> 42 + - "042" -> 34 (octal -> decimal) + - "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(dictionary * d, const char * key, int notfound); +int iniparser_getlong(dictionary * d, const char * key, int notfound); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(dictionary * d, const char * key, double notfound); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(dictionary * d, const char * key, int notfound); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, -1 is returned. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, const char * entry, const char * val); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, const char * entry); + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry(dictionary * ini, const char * entry) ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(const char * ininame); + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d); + +#endif diff --git a/src/cp_library/cp_klist.h b/src/cp_library/cp_klist.h new file mode 100644 index 0000000..a75bdd1 --- /dev/null +++ b/src/cp_library/cp_klist.h @@ -0,0 +1,723 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_list.h + * Description: This file is copied from Linux kernel, which provide link list API. + * + * Version: 1.0.0(08/09/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "08/09/2012 02:24:34 AM" + * + ********************************************************************************/ + +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include <linux/stddef.h> + + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#undef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + + +/* + * Architectures might want to move the poison pointer offset + * into some well-recognized area such as 0xdead000000000000, + * that is also not mappable by user-space exploits: + */ +#ifdef CONFIG_ILLEGAL_POINTER_VALUE +# define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL) +#else +# define POISON_POINTER_DELTA 0 +#endif + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA) +#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA) + +#ifndef ARCH_HAS_PREFETCH +#define ARCH_HAS_PREFETCH +static inline void prefetch(const void *x) {;} +#endif + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +/** + * list_is_singular - tests whether a list has just one entry. + * @head: the list to test. + */ +static inline int list_is_singular(const struct list_head *head) +{ + return !list_empty(head) && (head->next == head->prev); +} + +static inline void __list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + struct list_head *new_first = entry->next; + list->next = head->next; + list->next->prev = list; + list->prev = entry; + entry->next = list; + head->next = new_first; + new_first->prev = head; +} + +/** + * list_cut_position - cut a list into two + * @list: a new list to add all removed entries + * @head: a list with entries + * @entry: an entry within head, could be the head itself + * and if so we won't cut the list + * + * This helper moves the initial part of @head, up to and + * including @entry, from @head to @list. You should + * pass on @entry an element you know is on @head. @list + * should be an empty list or a list you do not care about + * losing its data. + * + */ +static inline void list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + if (list_empty(head)) + return; + if (list_is_singular(head) && + (head->next != entry && head != entry)) + return; + if (entry == head) + INIT_LIST_HEAD(list); + else + __list_cut_position(list, head, entry); +} + +static inline void __list_splice(const struct list_head *list, + struct list_head *prev, + struct list_head *next) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + + first->prev = prev; + prev->next = first; + + last->next = next; + next->prev = last; +} + +/** + * list_splice - join two lists, this is designed for stacks + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(const struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head, head->next); +} + +/** + * list_splice_tail - join two lists, each list being a queue + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head->prev, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head, head->next); + INIT_LIST_HEAD(list); + } +} + +/** + * list_splice_tail_init - join two lists and reinitialise the emptied list + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * Each of the lists is a queue. + * The list at @list is reinitialised + */ +static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head->prev, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; prefetch(pos->next), pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + prefetch(pos->prev), pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +static inline void INIT_HLIST_NODE(struct hlist_node *h) +{ + h->next = NULL; + h->pprev = NULL; +} + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = LIST_POISON1; + n->pprev = LIST_POISON2; +} + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (!hlist_unhashed(n)) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ + pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + + +#endif + + diff --git a/src/cp_library/cp_logger.c b/src/cp_library/cp_logger.c new file mode 100644 index 0000000..7eb89aa --- /dev/null +++ b/src/cp_library/cp_logger.c @@ -0,0 +1,420 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_log.c + * Description: This file is the linux infrastructural logger system library + * + * Version: 1.0.0(08/08/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "08/08/2012 04:24:01 PM" + * + ********************************************************************************/ + +#include "cp_logger.h" +#include "cp_common.h" + +#define PRECISE_TIME_FACTOR 1000 + +static unsigned long log_rollback_size = LOG_ROLLBACK_NONE; + +static cp_logger *logger = NULL; + +char *log_str[LOG_LEVEL_MAX + 1] = { "", "F", "E", "W", "N", "D", "I", "T", "M" }; + +static char *cp_time_format = DEFAULT_TIME_FORMAT; + +void cp_log_set_time_format(char *time_format) +{ + cp_time_format = time_format; +} + +static void cp_log_default_signal_handler(int sig) +{ + if(!logger) + return ; + + if (sig == SIGHUP) + { + signal(SIGHUP, cp_log_default_signal_handler); + log_fatal("SIGHUP received - reopenning log file [%s]", logger->file); + cp_log_reopen(); + } +} + +static void log_banner(char *prefix) +{ + if(!logger) + return ; + + fprintf(logger->fp, "%s log \"%s\" on level [%s] size [%lu], 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] [PID/TID] [File/Line] [Content]\n"); +#else + fprintf(logger->fp, " [Date] [Time] [Level] [PID/TID] [Content]\n"); +#endif + fprintf(logger->fp, "-------------------------------------------------------------\n"); +} + +static void check_and_rollback(void) +{ + if(!logger) + return ; + + if (log_rollback_size != LOG_ROLLBACK_NONE) + { + long _curOffset = ftell(logger->fp); + + if ((_curOffset != -1) && (_curOffset >= log_rollback_size)) + { + 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); + log_banner("Already rollback"); + } + } +} + +cp_logger *cp_log_init(cp_logger *log, char *filename, int level, int log_size) +{ + if(NULL == log) + { + logger = malloc(sizeof(cp_logger)); + memset(logger, 0, sizeof(cp_logger)); + logger->flag |= CP_LOGGER_MALLOC; + } + else + { + logger = log; + memset(logger, 0, sizeof(cp_logger)); + logger->flag |= CP_LOGGER_ARGUMENT; + } + + if(NULL == logger) + { + return NULL; + } + + strncpy(logger->file, filename, FILENAME_LEN); + logger->level = level; + logger->size = log_size; + + return logger; +} + +int cp_log_open(void) +{ + struct sigaction act; + char *filemode; + + if(!logger) + { + return -1; + } + + log_rollback_size = logger->size <= 0 ? LOG_ROLLBACK_NONE : logger->size*1024; /* Unit KiB */ + + if ('\0' == logger->file) + return -1; + + if (!strcmp(logger->file, DBG_LOG_FILE)) + { + logger->fp = stderr; + log_rollback_size = LOG_ROLLBACK_NONE; + logger->flag |= CP_LOGGER_CONSOLE; + goto OUT; + } + + //filemode = (log_rollback_size==LOG_ROLLBACK_NONE) ? "a+" : "w+"; + filemode = "a+"; + + logger->fp = fopen(logger->file, filemode); + if (NULL == logger->fp) + { + fprintf(stderr, "Open log file \"%s\" in %s failure\n", logger->file, filemode); + return -2; + } + + act.sa_handler = cp_log_default_signal_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGHUP, &act, NULL); + + OUT: + log_banner("Initialize"); + + return 0; +} + +void cp_log_close(void) +{ + if (!logger || !logger->fp ) + return; + + log_banner("\nTerminate"); + cp_log_raw("\n\n\n\n"); + + fflush(logger->fp); + + fclose(logger->fp); + logger->fp = NULL; + + return ; +} + +int cp_log_reopen(void) +{ + int rc = 0; + char *filemode; + + if( !logger ) + return -1; + + if (logger->flag & CP_LOGGER_CONSOLE ) + { + fflush(logger->fp); + logger->fp = stderr; + return 0; + } + + if (logger->fp) + { + cp_log_close(); + //filemode = log_rollback_size == LOG_ROLLBACK_NONE ? "a+" : "w+"; + filemode = "a+"; + logger->fp = fopen(logger->file, filemode); + + if (logger->fp == NULL) + rc = -2; + } + else + { + rc = -3; + } + + if (!rc) + { + log_banner("\nReopen"); + } + return rc; +} + +void cp_log_term(void) +{ + if(!logger) + return ; + + cp_log_close(); + + if (logger->flag & CP_LOGGER_MALLOC ) + { + free(logger); + } + logger = NULL; +} + +void cp_log_raw(const char *fmt, ...) +{ + va_list argp; + + if (!logger || !logger->fp) + return; + + check_and_rollback(); + + va_start(argp, fmt); + vfprintf(logger->fp, fmt, argp); + va_end(argp); +} + +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]; + + if(!logger) + return ; + + pthread_t tid; + + check_and_rollback(); + +#ifdef MULTHREADS + tid = pthread_self(); +#else + tid = getpid(); +#endif + + gettimeofday(&now, NULL); + local = localtime(&now.tv_sec); + + strftime(timestr, 256, cp_time_format, local); + vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp); + +#ifdef DUMPLICATE_OUTPUT + printf("%s.%03ld [%s] [%06lu]: %s", + timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, tid, buf); +#endif + + if (logger->fp) + fprintf(logger->fp, "%s.%03ld [%s] [%06lu]: %s", timestr, now.tv_usec / PRECISE_TIME_FACTOR, + level, tid, 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 ; + + pthread_t tid; + + check_and_rollback(); + +#ifdef MULTHREADS + tid = pthread_self(); +#else + tid = getpid(); +#endif + + gettimeofday(&now, NULL); + local = localtime(&now.tv_sec); + + strftime(timestr, 256, cp_time_format, local); + vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp); + +#ifdef DUMPLICATE_OUTPUT + printf("%s.%03ld [%s] [%06lu] (%s [%04d]) : %s", + timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, tid, file, line, buf); +#endif + + if (logger->fp) + fprintf(logger->fp, "%s.%03ld [%s] [%06lu] (%s [%04d]) : %s", + timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, tid, file, line, buf); + + if (logger->fp) + fflush(logger->fp); +} + +void cp_log(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 cp_log_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 cp_log_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) + { + if (line_done) + snprintf(prn, LINELEN, "%08X: ", idx); + + do + { + unsigned char c = buf[idx]; + snprintf(hc, 4, "%02X ", c); + strncat(prn, hc, LINELEN); + + lit[idx % CHARS_PER_LINE] = print_char[c]; + } + while (--rc > 0 && (++idx % CHARS_PER_LINE != 0)); + + line_done = (idx % CHARS_PER_LINE) == 0; + if (line_done) + { +#ifdef DUMPLICATE_OUTPUT + printf("%s %s\n", prn, lit); +#endif + if (logger->fp) + fprintf(logger->fp, "%s %s\n", prn, lit); + } + } + + if (!line_done) + { + int ldx = idx % CHARS_PER_LINE; + lit[ldx++] = print_char[(int)buf[idx]]; + lit[ldx] = '\0'; + + while ((++idx % CHARS_PER_LINE) != 0) + strncat(prn, " ", LINELEN); + +#ifdef DUMPLICATE_OUTPUT + printf("%s %s\n", prn, lit); +#endif + if (logger->fp) + fprintf(logger->fp, "%s %s\n", prn, lit); + + } +} diff --git a/src/cp_library/cp_logger.h b/src/cp_library/cp_logger.h new file mode 100644 index 0000000..471fe90 --- /dev/null +++ b/src/cp_library/cp_logger.h @@ -0,0 +1,110 @@ +/******************************************************************************** + * Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_logger.h + * Description: This file is the linux infrastructural logger system library + * + * Version: 1.0.0(08/08/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "08/08/2012 05:16:56 PM" + * + ********************************************************************************/ + +#ifndef __CP_LOG_H +#define __CP_LOG_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 <pthread.h> +#include <sys/types.h> +#include <sys/time.h> + +#define LOG_VERSION_STR "1.0.0" + +#ifndef FILENAME_LEN +#define FILENAME_LEN 64 +#endif + +#define DEFAULT_LOGFILE "cp_logger.log" +#define DBG_LOG_FILE "console" /* 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 */ +#define LOG_FILE_LINE /* Log the file and line */ + +enum +{ + LOG_LEVEL_DISB = 0, /* Disable "Debug" */ + LOG_LEVEL_FATAL, /* Debug Level "Fatal" */ + 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_TRACE, /* Debug Level "Trace" */ + LOG_LEVEL_MAX, +}; + +#define CP_LOGGER_MALLOC 1<<0 +#define CP_LOGGER_ARGUMENT 0<<0 + +#define CP_LOGGER_CONSOLE 1<<1 +#define CP_LOGGER_FILE 0<<1 + +#define CP_LOGGER_LEVEL_OPT 1<<2 /* The log level is sepcified by the command option */ +typedef struct _cp_logger +{ + unsigned char flag; /* This logger pointer is malloc() or passed by argument */ + char file[FILENAME_LEN]; + int level; + int size; + + FILE *fp; +} cp_logger; + +extern char *log_str[]; + +extern cp_logger *cp_log_init(cp_logger *log, char *filename, int level, int log_size); +extern int cp_log_open(void); +extern void cp_log_set_time_format(char *time_format); +extern int cp_log_reopen(void); +extern void cp_log_term(void); +extern void cp_log_raw(const char *fmt, ...); +extern void cp_log(int level, char *fmt, ...); +extern void cp_log_line(int level, char *file, int line, char *fmt, ...); + +extern void cp_log_dump(int level, char *buf, int len); + +#ifdef LOG_FILE_LINE +#define log_trace(fmt, ...) cp_log_line(LOG_LEVEL_TRACE, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_info(fmt, ...) cp_log_line(LOG_LEVEL_INFO, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_dbg(fmt, ...) cp_log_line(LOG_LEVEL_DEBUG, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_nrml(fmt, ...) cp_log_line(LOG_LEVEL_NRML, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_warn(fmt, ...) cp_log_line(LOG_LEVEL_WARN, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_err(fmt, ...) cp_log_line(LOG_LEVEL_ERROR, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_fatal(fmt, ...) cp_log_line(LOG_LEVEL_FATAL, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#else +#define log_trace(fmt, ...) cp_log(LOG_LEVEL_TRACE, fmt, ##__VA_ARGS__) +#define log_info(fmt, ...) cp_log(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__) +#define log_dbg(fmt, ...) cp_log(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__) +#define log_nrml(fmt, ...) cp_log(LOG_LEVEL_NRML, fmt, ##__VA_ARGS__) +#define log_warn(fmt, ...) cp_log(LOG_LEVEL_WARN, fmt, ##__VA_ARGS__) +#define log_err(fmt, ...) cp_log(LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__) +#define log_fatal(fmt, ...) cp_log(LOG_LEVEL_FATAL, fmt, ##__VA_ARGS__) +#endif + + +#endif /* __CP_LOG_H */ diff --git a/src/cp_library/cp_ppp.c b/src/cp_library/cp_ppp.c new file mode 100644 index 0000000..b0d85ef --- /dev/null +++ b/src/cp_library/cp_ppp.c @@ -0,0 +1,596 @@ +/******************************************************************************** + * Copyright: (C) guowenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: ppp.c + * Description: This file is the ppp thread file,used to do PPP dial up + * + * Version: 1.0.0(02/02/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "02/02/2012 11:33:33 AM" + * + ********************************************************************************/ +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <arpa/inet.h> +#include "cp_ppp.h" +#include "cp_string.h" +#include "cp_iniparser.h" +//#include "cp_network.h" +//#include "gsmd.h" + +/* + * Description: Check PPP10 device exist or not, and set the PPP context status + * + * + * Return Value: 0: PPP10 device exist -1:PPP10 device not exist + * + */ +int check_ppp_interface(char *ppp_dev) +{ + int fd = -1; + int retval = -1; + char buf[512]; + + if ((fd = open("/proc/net/dev", O_RDONLY, 0666)) < 0) + { + return -1; /* System error, take as PPP10 device not exist too */ + } + + while (read(fd, buf, sizeof(buf)) > 0) + { + if (NULL != strstr(buf, ppp_dev)) + { + retval = 0; + break; + } + } + + close(fd); + return retval; +} + +int check_ppp_ipaddr(pppd_info_t *pppd_info, char *ppp_dev) +{ + int fd; + struct ifreq ifr; + struct sockaddr_in *sin; + + if(check_ppp_interface(ppp_dev)) + return -2; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + strcpy(ifr.ifr_name, ppp_dev); + + if ((ioctl(fd, SIOCGIFADDR, (caddr_t) & ifr, sizeof(struct ifreq))) < 0) + { + log_fatal("System call ioctl() with SIOCGIFADDR failure.\n", ppp_dev); + return -1; + } + + sin = (struct sockaddr_in *)&ifr.ifr_addr; + strcpy(pppd_info->netaddr, (const char *)inet_ntoa(sin->sin_addr)); + log_nrml("Get %s local IP address: %s\n", ppp_dev, pppd_info->netaddr); + + if ((ioctl(fd, SIOCGIFDSTADDR, (caddr_t) & ifr, sizeof(struct ifreq))) < 0) + { + log_fatal("System call ioctl() with SIOCGIFDSTADDR failure.\n", ppp_dev); + return -1; + } + sin = (struct sockaddr_in *)&ifr.ifr_dstaddr; + strcpy(pppd_info->ptpaddr, (const char *)inet_ntoa(sin->sin_addr)); + log_nrml("Get %s remote IP address: %s\n", ppp_dev, pppd_info->ptpaddr); + + return 0; +} + + +/* Description: Use ping do network test + * Input args: $from: : use which NIC(Network Interface Card: eth0, eth1) or + * source IP address do ping test. if it's NULL, use default route + * $ping_ip: The ping test destination IP address + * Output args: NONE + * Return Value: >=0 ping test packet lost percent, <0: failure + */ +int network_ping_test(char *from, char *ping_ip) +{ + FILE *fp; + char cmd[256]; + char buf[512]; + int lost_percent = 100; + unsigned long tx_packets = 0; + unsigned long rx_packets = 0; + + memset(cmd, 0, sizeof(cmd)); + if(from) + { + snprintf(cmd, sizeof(cmd), "ping -W1 -c5 -s4 %s -I %s", ping_ip, from); + } + else + { + snprintf(cmd, sizeof(cmd), "ping -W1 -c5 -s4 %s", ping_ip); + } + + if (NULL == (fp = popen(cmd, "r"))) + { + return -2; + } + + while (NULL != fgets(buf, sizeof(buf), fp)) + { + if (strstr(buf, "transmitted")) + { + split_string_to_value(buf, "%l,%l,%d", &tx_packets, &rx_packets, &lost_percent); + break; + } + } + + pclose(fp); + return lost_percent; +} + +int check_ppp_stat(ppp_stat_t *ppp_stat, char *ppp_dev) +{ + int retval; + FILE *fp; + char *ptr; + char buf[512]; + + if( NULL == (fp=fopen(PPP_PROC_NET_DEV, "r")) ) + { + log_err("Can not open %s.\n", PPP_PROC_NET_DEV); + return -1; + } + + fgets(buf, sizeof(buf), fp); + fgets(buf, sizeof(buf), fp); + + while (fgets(buf, sizeof(buf), fp)) + { + ptr=strstr(buf, ppp_dev); + if(NULL == ptr) + { + log_err("Can not find %s interface statistic data in %s\n", ppp_dev, PPP_PROC_NET_DEV); + retval = -1; + break; + } + else + { + ptr = strchr(ptr, ':'); + ptr++; + sscanf(ptr, "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu", + &(ppp_stat->ullRx_Bytes), + &(ppp_stat->ullRx_Packets), + &(ppp_stat->ulRx_Errors), + &(ppp_stat->ulRx_Dropped), + &(ppp_stat->ulRx_FIFO_Errors), + &(ppp_stat->ulRx_Frame_Errors), + &(ppp_stat->ulRx_Compressed), + &(ppp_stat->ulRx_Multicast), + &(ppp_stat->ullTx_Bytes), + &(ppp_stat->ullTx_Packets), + &(ppp_stat->ulTx_Errors), + &(ppp_stat->ulTx_Dropped), + &(ppp_stat->ulTx_FIFO_Errors), + &(ppp_stat->ulCollisions), + &(ppp_stat->ulTx_Carrier_Errors), + &(ppp_stat->ulTx_Compressed) + ); + retval = 0; + break; + } + } + + if(0 == retval ) + log_dbg("Check PPP network traffic: Tx %llu bytes, Rx %llu bytes.\n", ppp_stat->ullTx_Bytes, ppp_stat->ullRx_Bytes); + fclose(fp); + return retval; +} + +int kill_pppd_process(comport_t *comport) +{ + int pid = -1; + + while( (pid=check_pppd_pid(comport->dev_name)) > 0) + { + log_nrml("Terminate pppd process [pid=%d]\n", pid); + kill(pid, SIGTERM); + sleep(1); /* Wait pppd process exit */ + } + + return 0; +} + +int stop_ppp_connect(ppp_ctx_t *ppp_ctx) +{ + log_warn("Stop PPP connection now.\n"); + + if(check_ppp_interface(ppp_ctx->dev)) + return 0; + + kill_pppd_process(ppp_ctx->comport); + + if(ppp_ctx->comport) + { + free_gsm_dataport("stop_ppp_connect()", ppp_ctx->module, &(ppp_ctx->comport)); + } + return 0; +} + +int get_apn_conf(char *ini_name, char *ini_key, apn_account_t *apn, unsigned char type) +{ + dictionary *ini ; + char tmp[64]; + char *str; + + if(NULL==ini_name || NULL==ini_key || NULL==apn) + { + return -1; + } + + ini = iniparser_load(ini_name); + if (ini==NULL) + { + log_err("cannot load default APN configure file: %s\n", ini_name); + return -2 ; + } + log_dbg("Parser default APN configure file %s\n", ini_name); + + /* Parser APN configure */ + snprintf(tmp, 64, "%s:%s", ini_key, APN_3G==type?"3g_apn":"apn"); + if( !(str=iniparser_getstring(ini, tmp, NULL)) ) + { + log_err("cannot find APN setting for SIM [%s] in file: %s\n", ini_key, ini_name); + memset(apn, 0, sizeof(apn_account_t)); + return -1; + } + else + { + strncpy(apn->apn, str, APN_LEN); + } + + /* Paser APN username information */ + snprintf(tmp, 64, "%s:%s", ini_key, APN_3G==type?"3g_uid":"uid"); + str = iniparser_getstring(ini, tmp, NULL); + strncpy(apn->uid, str, UID_LEN); + + + /* Paser APN password information */ + snprintf(tmp, 64, "%s:%s", ini_key, APN_3G==type?"3g_pwd":"pwd"); + str = iniparser_getstring(ini, tmp, NULL); + strncpy(apn->pwd, str, PWD_LEN); + + iniparser_freedict(ini); + return 0; +} + + +int select_apn(ppp_ctx_t *ppp_ctx, char *key) +{ + int retval; + unsigned char type = APN_2G; + + log_nrml("GPRS module work on GPRS mode, select APN now.\n"); + + retval = get_apn_conf(APN_DEF_CONF_FILE, key, &(ppp_ctx->apn), type); + if(!retval) + { + log_err("Get default APN [%s] from %s ok\n", ppp_ctx->apn.apn, APN_DEF_CONF_FILE); + return 0; + } + + return -1; +} + +#define PPPD_CMD_LEN 512 +int start_ppp_dial_cmd(comport_t *comport, apn_account_t *apn, char *ppp_dev) +{ + char tmp[64]; + char pppd_cmd[PPPD_CMD_LEN]; + pid_t pid; + + if( !comport || !comport->is_connted ) + { + log_err("GPRS module data port %s is not opened.\n", comport->dev_name); + return -1; + } + + if( !apn || strlen(apn->apn)<=0 ) + { + log_err("No valid APN was set.\n"); + return -1; + } + + log_nrml("Start PPP connection with APN \"%s\" now.\n", apn->apn); + + memset(pppd_cmd, 0, PPPD_CMD_LEN); + snprintf(pppd_cmd, PPPD_CMD_LEN, "%s -d %s ", PPPD_DIAL_SCRIPTS, comport->dev_name); + + snprintf(tmp, sizeof(tmp), " -a %s ", apn->apn); + strncat(pppd_cmd, tmp, PPPD_CMD_LEN); + + if( strlen(apn->uid) > 0 ) + { + snprintf(tmp, sizeof(tmp), " -u %s ", apn->uid); + strncat(pppd_cmd, tmp, PPPD_CMD_LEN); + } + + if( strlen(apn->pwd) > 0 ) + { + snprintf(tmp, sizeof(tmp), " -p %s ", apn->pwd); + strncat(pppd_cmd, tmp, PPPD_CMD_LEN); + } + + if( strlen(apn->auth) > 0 ) + { + snprintf(tmp, sizeof(tmp), " -v %s ", apn->auth); + strncat(pppd_cmd, tmp, PPPD_CMD_LEN); + } + + strncat(pppd_cmd, ppp_dev, PPPD_CMD_LEN); + + log_nrml("ppp dial up command: %s\n", pppd_cmd); + + pid = fork(); + if( 0 == pid ) /* Child process */ + { + system(pppd_cmd); + exit(0); + } + else if (pid < 0) + { + log_err("Create new process failure.\n"); + return -1; + } + else + { + log_dbg("pppd program already running.\n"); + micro_second_sleep(1000); + } + + return 0; +} + +int check_pppd_pid(char *find_key) +{ + char buf[10]; + char cmd[128]; + FILE *fp = NULL; + int pid = -1; + + snprintf(cmd, sizeof(cmd), "ps | grep -v grep | grep pppd | grep %s | awk '{print $1}'", find_key); + + fp = popen(cmd, "r"); + if(NULL == fp) + { + log_fatal("popen() to check the pppd process ID failure.\n"); + return -1; + } + + memset(buf, 0, sizeof(buf)); + fgets(buf, sizeof(buf), fp); + + pid = atoi(buf); + + if(pid <= 1) + { + log_err("Get pppd program running pid failure\n"); + pid = -1; + } +#if 0 + else + { + log_trace("pppd program running pid is [%d]\n", pid); + } +#endif + + pclose(fp); + return pid; +} + +void set_ppp_disconnect(ppp_ctx_t *ppp_ctx) +{ + ppp_ctx->status = DISCONNECT; + ppp_ctx->pppd_info.start_time = 0; + + memset(&(ppp_ctx->pppd_info), 0, sizeof(pppd_info_t)); + memset(&(ppp_ctx->ppp_stat), 0, sizeof(ppp_stat_t)); + ppp_ctx->network = PPP_STOP; +} + + +int ppp_context_init(ppp_ctx_t *ppp_ctx, modinfo_t *module) +{ + memset(ppp_ctx, 0, sizeof(*ppp_ctx)); + + ppp_ctx->thread_id = pthread_self(); + ppp_ctx->module = module; + strncpy(ppp_ctx->dev, PPP_INTERFACE_NAME, sizeof(ppp_ctx->dev)); + strncpy(ppp_ctx->ping_ip, DEF_PING_DST, sizeof(ppp_ctx->ping_ip)); + ppp_ctx->ping_interval = PING_INTERVAL_TIME; + set_ppp_disconnect(ppp_ctx); + + log_nrml("Initialize PPP thread context ok\n"); + return 0; +} + +void ppp_context_term(ppp_ctx_t *ppp_ctx) +{ + stop_ppp_connect(ppp_ctx); + set_ppp_disconnect(ppp_ctx); + + log_nrml("Terminate PPP thread context ok\n"); +} + +int start_ppp_connect(ppp_ctx_t *ppp_ctx) +{ + int retval; + modinfo_t *module = ppp_ctx->module; + + if( !ppp_ctx->comport ) + { + ppp_ctx->comport=alloc_gsm_dataport("start_ppp_connect()", ppp_ctx->module); + if(!ppp_ctx->comport) + return -1; + + log_dbg("PPP thread alloc GPRS dataport %s=>[fd:%d]\n", ppp_ctx->comport->dev_name, ppp_ctx->comport->fd); + } + + + switch(ppp_ctx->status) + { + case DISCONNECT: /* Select the APN now */ + set_ppp_disconnect(ppp_ctx); + retval = select_apn(ppp_ctx, module->reg.loc.mcc_mnc); + if(!retval) + { + log_nrml("Select APN successfully, go to next stage.\n"); + ppp_ctx->fail_cnt = 0; + ppp_ctx->status++; + } + else + { + log_warn("Select APN failure, request to reset GPRS module\n"); + ppp_ctx->fail_cnt ++; + set_module_event(module, REQ_POWER_RESET); + break; + } + + case SELECTED_APN: /* Select the APN now */ + log_warn("Run pppd program to start PPP connection now\n"); + ppp_ctx->network = PPP_CONN; + start_ppp_dial_cmd(ppp_ctx->comport, &(ppp_ctx->apn), ppp_ctx->dev); + ppp_ctx->status++; + break; + } + + return 0; +} + +int inspect_network_status(ppp_ctx_t *ppp_ctx) +{ + pppd_info_t *pppd_info = &(ppp_ctx->pppd_info); + ppp_stat_t *ppp_stat = &(ppp_ctx->ppp_stat); + int lost_percent; + unsigned long long last_tx_bytes= ppp_stat->ullTx_Bytes; + unsigned long long last_rx_bytes= ppp_stat->ullRx_Bytes; + + if( CONNECTED != ppp_ctx->status) + { + ppp_ctx->pppd_info.ping_time = 0; + memset(ppp_stat, 0, sizeof(*ppp_stat)); + + pppd_info->pid = check_pppd_pid(ppp_ctx->comport->dev_name); + if(pppd_info->pid <= 0) + { + /* pppd can not connect in 30s, then set it to DISCONNECT status */ + if(time_elapsed(pppd_info->start_time) >= PPPD_DIAL_TIMEOUT) + { + /* Sleep for 2 seconds, make sure the exit pppd process release the TTY device */ + micro_second_sleep(2000); + log_err("pppd process exit when do ppp dialing, set status to DISCONNECT.\n"); + set_ppp_disconnect(ppp_ctx); + } + } + else + { + if(strlen(pppd_info->netaddr) <=0 ) + { + check_ppp_ipaddr(pppd_info, ppp_ctx->dev); + log_dbg("pppd running process ID [%d].\n", pppd_info->pid); + } + } + } + + if( time_elapsed(pppd_info->ping_time) >= ppp_ctx->ping_interval*1000 ) + { + if(ppp_ctx->fail_cnt >= MAX_PPP_FAIL_CNT) + { + /* Sleep for 2 seconds, make sure the exit pppd process release the TTY device */ + micro_second_sleep(2000); + log_warn("PPP network inspect status failure, set status to DISCONNECT.\n"); + set_ppp_disconnect(ppp_ctx); + return 0; + } + + lost_percent = network_ping_test(ppp_ctx->dev, ppp_ctx->ping_ip); + ppp_ctx->network = lost_percent<=60 ? PPP_GOOD : PPP_BAD; + + if(lost_percent <= 80) + { + check_ppp_stat(ppp_stat, ppp_ctx->dev); + if(last_tx_bytes==ppp_stat->ullTx_Bytes || last_rx_bytes==ppp_stat->ullRx_Bytes) + { + log_warn("PPP interface traffic not increased, maybe ppp disconnected?\n"); + ppp_ctx->fail_cnt++; + } + else + { + log_nrml("Set PPP connection status to CONNECTED.\n"); + set_ppp_disconnect(ppp_ctx); + ppp_ctx->fail_cnt=0; + } + } + else + { + ppp_ctx->fail_cnt++; + } + ppp_ctx->pppd_info.ping_time = time_now(); + } + + return 0; +} + + +void *ppp_thread_workbody(void *thread_arg) +{ + ppp_ctx_t ppp_ctx; + modinfo_t *module; + + module = (modinfo_t *)thread_arg; + ppp_context_init(&ppp_ctx, module); + + log_nrml("GPRS PPP thread start running\n"); + + while( !g_cp_signal.stop ) + { + if(ALL_READY != module->ready) + { + if(ppp_ctx.status>=CONNECTING) + { + log_nrml("Stop exist PPP connection and free TTY data port\n"); + stop_ppp_connect(&ppp_ctx); + } + + /* GPRS not ready, sleep for 200ms and check again */ + micro_second_sleep(200); + continue; + } + else + { + if(ppp_ctx.status>=CONNECTING) + { + inspect_network_status(&ppp_ctx); + } + else + { + start_ppp_connect(&ppp_ctx); + } + } + + sleep(1); + } + + ppp_context_term(&ppp_ctx); + pthread_exit(0); + return NULL; +} + +int start_thread_ppp(modinfo_t *module) +{ + pthread_t tid; + + return thread_start(&tid, ppp_thread_workbody, module); +} diff --git a/src/cp_library/cp_ppp.h b/src/cp_library/cp_ppp.h new file mode 100644 index 0000000..c06937e --- /dev/null +++ b/src/cp_library/cp_ppp.h @@ -0,0 +1,138 @@ +/******************************************************************************** + * Copyright: (C) guowenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: ppp.h + * Description: This head file is for PPP dial up thread. + * + * Version: 1.0.0(02/17/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "02/17/2012 11:11:54 AM" + * + ********************************************************************************/ +#ifndef __PPP_H +#define __PPP_H + +#include <netinet/in.h> + +#include "cp_comport.h" +#include "cp_proc.h" +#include "cp_gprs.h" + +#define PPPD_DIAL_TIMEOUT 20000 /* 20 seconds */ +#define PING_INTERVAL_TIME 10000 /* 10 seconds */ + +#define DEFAULT_PING_INTERVAL 30 +#define APN_LEN 128 +#define UID_LEN 64 +#define PWD_LEN 64 + +#define APN_3G 3 +#define APN_2G 2 + +#define PPPD_DIAL_SCRIPTS "/apps/tools/ifup-ppp" +#define PPP_INTERFACE_NAME "ppp10" +#define PPP_PROC_NET_DEV "/proc/net/dev" + +#define DEF_PING_DST "4.2.2.2" + +#define APN_DEF_CONF_FILE "/apps/etc/ppp/default_apn.conf" + +enum +{ + DISCONNECT = 0, + SELECTED_APN, + CONNECTING, + CONNECTED, +}; + + +typedef struct apn_account_s +{ + char apn[APN_LEN]; + char uid[UID_LEN]; + char pwd[PWD_LEN]; + char auth[10]; /* PAP or CHAP */ +} apn_account_t; + +typedef struct pppd_info_s +{ + char netaddr[INET_ADDRSTRLEN]; + char ptpaddr[INET_ADDRSTRLEN]; + unsigned long start_time; + unsigned long ping_time; + pid_t pid; +} pppd_info_t; + +typedef struct ppp_stat_s +{ + unsigned long long ullRx_Packets; // total packets received + unsigned long long ullTx_Packets; // total packets transmitted + unsigned long long ullRx_Bytes; // total bytes received + unsigned long long ullTx_Bytes; // total bytes transmitted + + unsigned long ulRx_Errors; // bad packets received + unsigned long ulTx_Errors; // packet transmit problems + unsigned long ulRx_Dropped; // no space in linux buffers + unsigned long ulTx_Dropped; // no space available in linux + unsigned long ulRx_Multicast; // multicast packets received + unsigned long ulRx_Compressed; + unsigned long ulTx_Compressed; + unsigned long ulCollisions; + + /* detailed rx_errors */ + unsigned long ulRx_Length_Errors; + unsigned long ulRx_Over_Errors; // receiver ring buff overflow + unsigned long ulRx_CRC_Errors; // recved pkt with crc error + unsigned long ulRx_Frame_Errors; // recv'd frame alignment error + unsigned long ulRx_FIFO_Errors; // recv'r fifo overrun + unsigned long ulRx_Missed_Errors; // receiver missed packet + + /* detailed tx_errors */ + unsigned long ulTx_Aborted_Errors; + unsigned long ulTx_Carrier_Errors; + unsigned long ulTx_FIFO_Errors; + unsigned long ulTx_Heartbeat_Errors; + unsigned long ulTx_Window_Errors; +} ppp_stat_t; + +#define MAX_PPP_FAIL_CNT 3 + +#define PPP_STOP 0 /* PPP not work, but signal good */ +#define SIG_WEAK 1 /* PPP not work and signal is very weak */ +#define PPP_CONN 2 /* PPP is connecting */ +#define PPP_BAD 3 /* PPP is connected, but network/signal not good */ +#define PPP_GOOD 4 /* PPP is connected and network/signal good */ +typedef struct ppp_ctx_s +{ + unsigned char enable; /* Enable PPP thread running or not, RFU */ + unsigned char status; /* Current PPP connection status */ + unsigned char network; /* PPP network status: PPP_FAIL,PPP_BAD,PPP_GOOD */ + char dev[10]; /* PPP dial up device name, such as PPP10 */ + + pthread_t thread_id; /* PPP Thread ID, RFU */ + + pppd_info_t pppd_info; /* pppd process information */ + ppp_stat_t ppp_stat; /* PPP network statistic */ + unsigned char fail_cnt; /* PPP failure count */ + + apn_account_t apn; /* PPP dial up APN */ + + char ping_ip[INET_ADDRSTRLEN]; + long ping_interval; + + comport_t *comport; + modinfo_t *module; +} ppp_ctx_t; + +int get_apn_conf(char *ini_name, char *ini_key, apn_account_t *apn, unsigned char type); +int check_pppd_pid(char *find_key); +int check_ppp_interface(char *ppp_inf); +int start_ppp_dial_cmd(comport_t *comport, apn_account_t *apn, char *ppp_inf); +int check_ppp_ipaddr(pppd_info_t *pppd_info, char *ppp_inf); +int check_ppp_stat_t(ppp_stat_t *ppp_stat, char *ppp_inf); +int stop_ppp_connect(ppp_ctx_t *ppp_ctx); + +int start_thread_ppp(modinfo_t *module); + +#endif /* End of __PPP_H */ diff --git a/src/cp_library/cp_proc.c b/src/cp_library/cp_proc.c new file mode 100644 index 0000000..6fb05f3 --- /dev/null +++ b/src/cp_library/cp_proc.c @@ -0,0 +1,340 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_proc.c + * Description: This file is the process API + * + * Version: 1.0.0(11/06/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/06/2012 09:19:02 PM" + * + ********************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <libgen.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "cp_proc.h" +#include "cp_logger.h" + +CP_PROC_SIG g_cp_signal={0}; + +void cp_proc_sighandler(int sig) +{ + switch(sig) + { + case SIGINT: + log_warn("SIGINT - stopping\n"); + g_cp_signal.stop = 1; + break; + + case SIGTERM: + //log_warn("SIGTERM - stopping\n"); + g_cp_signal.stop = 1; + break; +#if 0 + case SIGSEGV: + log_err("SIGSEGV - stopping\n"); + if(g_cp_signal.stop) + exit(0); + + g_cp_signal.stop = 1; + break; +#endif + + case SIGPIPE: + log_warn("SIGPIPE - warnning\n"); + g_cp_signal.stop = 1; + break; + + default: + break; + } +} + + +void cp_install_proc_signal(void) +{ + struct sigaction sigact, sigign; + + log_nrml("Install default signal handler.\n"); + + /* Initialize the catch signal structure. */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = cp_proc_sighandler; + + /* Setup the ignore signal. */ + sigemptyset(&sigign.sa_mask); + sigign.sa_flags = 0; + sigign.sa_handler = SIG_IGN; + + sigaction(SIGTERM, &sigact, 0); /* catch terminate signal "kill" command */ + sigaction(SIGINT, &sigact, 0); /* catch interrupt signal CTRL+C */ + //sigaction(SIGSEGV, &sigact, 0); /* catch segmentation faults */ + sigaction(SIGPIPE, &sigact, 0); /* catch broken pipe */ +#if 0 + sigaction(SIGCHLD, &sigact, 0); /* catch child process return */ + sigaction(SIGUSR2, &sigact, 0); /* catch USER signal */ +#endif +} + + +/* **************************************************************************** + * FunctionName: daemonize + * Description : Set the programe runs as daemon in background + * Inputs : nodir: DON'T change the work directory to / : 1:NoChange 0:Change + * noclose: close the opened file descrtipion or not 1:Noclose 0:Close + * Output : NONE + * Return : NONE + * *****************************************************************************/ +void daemonize(int nochdir, int noclose) +{ + int retval, fd; + int i; + + /* already a daemon */ + if (1 == getppid()) + return; + + /* fork error */ + retval = fork(); + if (retval < 0) exit(1); + + /* parent process exit */ + if (retval > 0) + exit(0); + + /* obtain a new process session group */ + setsid(); + + if (!noclose) + { + /* close all descriptors */ + for (i = getdtablesize(); i >= 0; --i) + { + //if (i != g_logPtr->fd) + close(i); + } + + /* Redirect Standard input [0] to /dev/null */ + fd = open("/dev/null", O_RDWR); + + /* Redirect Standard output [1] to /dev/null */ + dup(fd); + + /* Redirect Standard error [2] to /dev/null */ + dup(fd); + } + + umask(0); + + if (!nochdir) + chdir("/"); + + return; +} + +/* **************************************************************************** + * FunctionName: record_daemon_pid + * Description : Record the running daemon program PID to the file "pid_file" + * Inputs : pid_file:The record PID file path + * Output : NONE + * Return : 0: Record successfully Else: Failure + * *****************************************************************************/ +int record_daemon_pid(const char *pid_file) +{ + struct stat fStatBuf; + int fd = -1; + int mode = S_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | S_IRWXU; + char ipc_dir[64] = { 0 }; + + strncpy(ipc_dir, pid_file, 64); + + /* dirname() will modify ipc_dir and save the result */ + dirname(ipc_dir); + + /* If folder pid_file PATH doesnot exist, then we will create it" */ + if (stat(ipc_dir, &fStatBuf) < 0) + { + if (mkdir(ipc_dir, mode) < 0) + { + log_fatal("cannot create %s: %s\n", ipc_dir, strerror(errno)); + return -1; + } + + (void)chmod(ipc_dir, mode); + } + + /* Create the process running PID file */ + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + if ((fd = open(pid_file, O_RDWR | O_CREAT | O_TRUNC, mode)) >= 0) + { + char pid[PID_ASCII_SIZE]; + snprintf(pid, sizeof(pid), "%u\n", (unsigned)getpid()); + write(fd, pid, strlen(pid)); + close(fd); + + log_dbg("Record PID<%u> to file %s.\n", getpid(), pid_file); + } + else + { + log_fatal("cannot create %s: %s\n", pid_file, strerror(errno)); + return -1; + } + + return 0; +} + +/* **************************************************************************** + * FunctionName: get_daemon_pid + * Description : Get the daemon process PID from the PID record file "pid_file" + * Inputs : pid_file: the PID record file + * Output : NONE + * Return : pid_t: The daemon process PID number + * *****************************************************************************/ +pid_t get_daemon_pid(const char *pid_file) +{ + FILE *f; + pid_t pid; + + if ((f = fopen(pid_file, "rb")) != NULL) + { + char pid_ascii[PID_ASCII_SIZE]; + (void)fgets(pid_ascii, PID_ASCII_SIZE, f); + (void)fclose(f); + pid = atoi(pid_ascii); + } + else + { + log_fatal("Can't open PID record file %s: %s\n", pid_file, strerror(errno)); + return -1; + } + return pid; +} + +/* **************************************************************************** + * FunctionName: check_daemon_running + * Description : Check the daemon program already running or not + * Inputs : pid_file: The record running daemon program PID + * Output : NONE + * Return : 1: The daemon program alread running 0: Not running + * *****************************************************************************/ +int check_daemon_running(const char *pid_file) +{ + int retVal = -1; + struct stat fStatBuf; + + retVal = stat(pid_file, &fStatBuf); + if (0 == retVal) + { + pid_t pid = -1; + printf("PID record file \"%s\" exist.\n", pid_file); + + pid = get_daemon_pid(pid_file); + if (pid > 0) /* Process pid exist */ + { + if ((retVal = kill(pid, 0)) == 0) + { + printf("Program with PID[%d] seems running.\n", pid); + return 1; + } + else /* Send signal to the old process get no reply. */ + { + printf("Program with PID[%d] seems exit.\n", pid); + remove(pid_file); + return 0; + } + } + else if (0 == pid) + { + printf("Can not read program PID form record file.\n"); + remove(pid_file); + return 0; + } + else /* Read pid from file "pid_file" failure */ + { + printf("Read record file \"%s\" failure, maybe program still running.\n", pid_file); + return 1; + } + } + + return 0; +} + +/* **************************************************************************** + * FunctionName: set_daemon_running + * Description : Set the programe running as daemon if it's not running and record + * its PID to the pid_file. + * Inputs : pid_file: The record running daemon program PID + * Output : NONE + * Return : 0: Successfully. 1: Failure + * *****************************************************************************/ +int set_daemon_running(const char *pid_file) +{ + daemonize(0, 1); + log_nrml("Program running as daemon [PID:%d].\n", getpid()); + + if (record_daemon_pid(pid_file) < 0) + { + log_fatal("Record PID to file \"%s\" failure.\n", pid_file); + return -2; + } + + return 0; +} + + +int thread_start(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg) +{ + int retval = 0; + + pthread_attr_t thread_attr; + + /* Initialize the thread attribute */ + retval = pthread_attr_init(&thread_attr); + if(retval) + return -1; + + /* Set the stack size of the thread */ + retval = pthread_attr_setstacksize(&thread_attr, 120 * 1024); + if(retval) + goto CleanUp; + + /* Set thread to detached state:Don`t need pthread_join */ + retval = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + if(retval) + goto CleanUp; + + /* Create the thread */ + retval = pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg); + if(retval) + goto CleanUp; + +CleanUp: + /* Destroy the attributes of thread */ + pthread_attr_destroy(&thread_attr); + return retval; +} + + +void exec_system_cmd(const char *format, ...) +{ + char cmd[256]; + va_list args; + + memset(cmd, 0, sizeof(cmd)); + + va_start(args, format); + vsnprintf(cmd, sizeof(cmd), format, args); + va_end(args); + + system(cmd); +} + + diff --git a/src/cp_library/cp_proc.h b/src/cp_library/cp_proc.h new file mode 100644 index 0000000..f79abe8 --- /dev/null +++ b/src/cp_library/cp_proc.h @@ -0,0 +1,42 @@ +/******************************************************************************** + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_proc.h + * Description: This head file is for Linux process API + * + * Version: 1.0.0(11/06/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/06/2012 09:21:33 PM" + * + ********************************************************************************/ + +#ifndef __CP_PROC_H +#define __CP_PROC_H + +#include <signal.h> + +#define PID_ASCII_SIZE 11 + +typedef struct __CP_PROC_SIG +{ + int signal; + unsigned stop; /* 0: Not term 1: Stop */ +} CP_PROC_SIG; + +typedef void *(THREAD_BODY) (void *thread_arg); + +extern CP_PROC_SIG g_cp_signal; +extern void cp_install_proc_signal(void); + +extern void daemonize(int nochdir, int noclose); +extern int record_daemon_pid(const char *pid_file); +extern pid_t get_daemon_pid(const char *pid_file); +extern int check_daemon_running(const char *pid_file); +extern int set_daemon_running(const char *pid_file); + +extern void exec_system_cmd(const char *format, ...); + +extern int thread_start(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg); + +#endif diff --git a/src/cp_library/cp_queue.c b/src/cp_library/cp_queue.c new file mode 100644 index 0000000..7f5f03e --- /dev/null +++ b/src/cp_library/cp_queue.c @@ -0,0 +1,155 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_queue.c + * Description: This file is the queue implement based on link list. + * + * Version: 1.0.0(11/12/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/12/2012 01:25:26 PM" + * + ********************************************************************************/ + +#include "cp_queue.h" +#include "cp_common.h" + +CP_QUEUE *cp_queue_init(CP_QUEUE *queue, int size) +{ + if(!queue) + { + queue = malloc(sizeof(*queue)); + } + memset(queue, 0, sizeof(*queue)); + + queue->size = size; + queue->items = 0; + queue->rear = queue->front = NULL; + + return queue; +} + +void *cp_enqueue(CP_QUEUE *queue, void *data) +{ + CP_QNODE *node; + + if(!queue || cp_queue_is_full(queue)) + { + return NULL; + } + + node = malloc(sizeof(*node)); + if(NULL == node) + { + return NULL; + } + + node->item = data; + node->next = NULL; + + if(cp_queue_is_empty(queue)) + { + queue->front = node; + } + else + { + queue->rear->next = node; + } + + queue->rear = node; + queue->items++; + + return data; +} + +void cp_travel_queue(CP_QUEUE *queue) +{ + CP_QNODE *node; + + for(node=queue->front; node!=NULL; node=node->next) + { + printf("queue node[%p] save data [%p]\n", node, node->item); + } +} + +void *cp_rmqueue(CP_QUEUE *queue, void *data) +{ + CP_QNODE *node, *tmp; + + if(!queue || cp_queue_is_empty(queue)) + { + return NULL; + } + + /* The first node->item is the data, we find it */ + if(queue->front->item==data) + { + tmp = queue->front; + queue->front = queue->front->next; + queue->items -= 1; + free(tmp); + return data; + } + + for(node=queue->front; node!=NULL; node=node->next) + { + if(node->next->item == data) + { + tmp = node->next; + queue->items -= 1; + + if(node->next != queue->rear) + node->next = node->next->next; + else + { + queue->rear = node; + queue->rear->next = NULL; + } + + free(tmp); + return data; + } + } + + return data; +} + +void *cp_dequeue(CP_QUEUE *queue) +{ + CP_QNODE *node; + void *item; + + if(!queue || cp_queue_is_empty(queue)) + { + return NULL; + } + + node = queue->front; + queue->front = queue->front->next; + + item = node->item; + free(node); + + queue->items--; + + if(queue->items == 0) + queue->rear = NULL; + + return item; +} + +void cp_queue_destroy(CP_QUEUE *queue) +{ + if(!queue) + return ; + + while( !cp_queue_is_empty(queue) ) + { + cp_dequeue(queue); + } + + free(queue); + + return ; +} + diff --git a/src/cp_library/cp_queue.h b/src/cp_library/cp_queue.h new file mode 100644 index 0000000..c64cd06 --- /dev/null +++ b/src/cp_library/cp_queue.h @@ -0,0 +1,49 @@ +/******************************************************************************** + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_queue.h + * Description: This head file is for the queue implement based on link list. + * + * Version: 1.0.0(11/12/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/12/2012 01:26:34 PM" + * + ********************************************************************************/ +#ifndef __CP_QUEUE +#define __CP_QUEUE + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +typedef struct _CP_QNODE +{ + void *item; + struct _CP_QNODE *next; +} CP_QNODE; + + +typedef struct _CP_QUEUE +{ + CP_QNODE *front; + CP_QNODE *rear; + int items; + int size; +} CP_QUEUE; + +#define cp_queue_is_full(pq) ( (pq)->size==(pq)->items ? 1 :0 ) +#define cp_queue_is_empty(pq) ( 0==(pq)->items ? 1 : 0) +#define cp_queue_count(pq) ( (pq)->items ) +#define cp_queue_size(pq) ( (pq)->size ) + +CP_QUEUE *cp_queue_init(CP_QUEUE *queue, int size); +void *cp_enqueue(CP_QUEUE *queue, void *data); +void cp_travel_queue(CP_QUEUE *queue); +void *cp_rmqueue(CP_QUEUE *queue, void *data); +void *cp_dequeue(CP_QUEUE *queue); +void cp_queue_destroy(CP_QUEUE *queue); +#define cp_queue_destroy_clear(queue) {cp_queue_destroy(queue); queue=NULL;} + +#endif diff --git a/src/cp_library/cp_ringbuf.c b/src/cp_library/cp_ringbuf.c new file mode 100644 index 0000000..54d5e09 --- /dev/null +++ b/src/cp_library/cp_ringbuf.c @@ -0,0 +1,103 @@ +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Based on ringbuffer.c by Patrick Prasse (patrick.prasse@gmx.net). Code + has been modified by Telenor and Gemalto. + + */ + + +#include <string.h> +#include <assert.h> +#include "cp_ringbuf.h" + +void rb_init (struct ring_buffer *ring, u_char* buff, int size) { + memset (ring, 0, sizeof (struct ring_buffer)); + ring->rd_pointer = 0; + ring->wr_pointer = 0; + ring->buffer= buff; + ring->size = size; +} + +int +rb_write (struct ring_buffer *rb, u_char * buf, int len) +{ + int total; + int i; + + /* total = len = min(space, len) */ + total = rb_free_size(rb); + if(len > total) + len = total; + else + total = len; + + i = rb->wr_pointer; + if(i + len > rb->size) + { + memcpy(rb->buffer + i, buf, rb->size - i); + buf += rb->size - i; + len -= rb->size - i; + i = 0; + } + memcpy(rb->buffer + i, buf, len); + rb->wr_pointer = i + len; + return total; +} + +int rb_free_size (struct ring_buffer *rb){ + return (rb->size - 1 - rb_data_size(rb)); +} + +int rb_read (struct ring_buffer *rb, u_char * buf, int max) { + int total; + int i; + /* total = len = min(used, len) */ + total = rb_data_size(rb); + + if(max > total) + max = total; + else + total = max; + + i = rb->rd_pointer; + if(i + max > rb->size) + { + memcpy(buf, rb->buffer + i, rb->size - i); + buf += rb->size - i; + max -= rb->size - i; + i = 0; + } + memcpy(buf, rb->buffer + i, max); + rb->rd_pointer = i + max; + + return total; +} + +int rb_data_size (struct ring_buffer *rb) { + return ((rb->wr_pointer - rb->rd_pointer) & (rb->size-1)); +} + +void rb_clear (struct ring_buffer *rb) { + memset(rb->buffer,0,rb->size); + rb->rd_pointer=0; + rb->wr_pointer=0; +} + +u_char rb_peek(struct ring_buffer* rb, int index) { + assert(index < rb_data_size(rb)); + + return rb->buffer[((rb->rd_pointer + index) % rb->size)]; +} diff --git a/src/cp_library/cp_ringbuf.h b/src/cp_library/cp_ringbuf.h new file mode 100644 index 0000000..617d187 --- /dev/null +++ b/src/cp_library/cp_ringbuf.h @@ -0,0 +1,57 @@ +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Based on ringbuffer.h by Patrick Prasse (patrick.prasse@gmx.net). Code + has been modified by Telenor and Gemalto. + + */ + +#ifndef __CP_RINGBUF_H_ +#define __CP_RINGBUF_H_ + +#include <sys/types.h> + +struct ring_buffer { + u_char *buffer; + int wr_pointer; + int rd_pointer; + int size; +}; + +/* Initial the ring buffer */ +void rb_init (struct ring_buffer *ring, u_char* buff, int size) ; + +/* Description: Write $len bytes data in $buf into ring buffer $rb + * Return Value: The actual written into ring buffer data size, if ring buffer + * left space size small than $len, then only part of the data be written into. + */ +int rb_write (struct ring_buffer *rb, u_char * buf, int len) ; + +/* Get ring buffer left free size */ +int rb_free_size (struct ring_buffer *rb); + +/* Read $max bytes data from ring buffer $rb to $buf */ +int rb_read (struct ring_buffer *rb, u_char * buf, int max); + +/* Read a specify $index byte data in ring buffer $rb */ +u_char rb_peek(struct ring_buffer* rb, int index); + +/* Get data size in the ring buffer */ +int rb_data_size (struct ring_buffer *); + +/* Clear the ring buffer data */ +void rb_clear (struct ring_buffer *rb) ; + +#endif /* __CP_RINGBUF_H_ */ diff --git a/src/cp_library/cp_string.c b/src/cp_library/cp_string.c new file mode 100644 index 0000000..666e557 --- /dev/null +++ b/src/cp_library/cp_string.c @@ -0,0 +1,673 @@ + +/** + * @addtogroup cp_string + */ +/** @{ */ +/** + * @file + * cp_string - 'safe' string implementation + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "cp_string.h" +#include "cp_common.h" + +cp_string *cp_string_create(char *data, int len) +{ + cp_string *str; + +#ifdef DEBUG + if (len < 0) + { + return NULL; + } +#endif + + str = malloc(sizeof(cp_string)); + + if (str) + { + str->len = len; + str->size = str->len + 1; + str->data = malloc(str->size * sizeof(char)); + if (str->data) + memcpy(str->data, data, str->len); + else + { + free(str); + str = NULL; + } + } + + return str; +} + +cp_string *cp_string_create_empty(int initial_size) +{ + cp_string *str = malloc(sizeof(cp_string)); + + if (str) + { + str->len = 0; + str->size = initial_size; + str->data = (char *) malloc(str->size * sizeof(char)); + if (str->data == NULL) + { + free(str); + str = NULL; + } + } + + return str; +} + +void cp_string_destroy(cp_string *str) +{ + if (str) + { + if (str->data) + { + free(str->data); + } + + free(str); + } +} + + +void cp_string_clear_data(cp_string *str) +{ + if (str && str->data) + { + memset(str->data, 0, str->size); + str->len = 0; + } +} + +cp_string *cp_string_cstrcpy(cp_string *str, char *cstr) +{ + if (str) + { + str->len = strlen(cstr); + if (str->size < str->len + 1) + { + str->size = str->len + 1; + str->data = malloc(str->size * sizeof(char)); + } + if (str->data) + memcpy(str->data, cstr, str->size * sizeof(char)); + else + { + free(str); + str = NULL; + } + } + + return str; +} + +int cp_string_copy(cp_string *dst, cp_string *src) +{ + int left; + int size; + + if(!dst || !dst->data || !src || !src->data) + return 0; + + left=dst->size - dst->len; + size = left>src->len ? src->len : left; + + memcpy(&dst->data[dst->len], src->data, size); + dst->len += size; + + return size; +} + +int cp_string_cstrcopy(cp_string *dst, char *string, int len) +{ + int left; + int size; + + if(!dst || !dst->data || !string || len<=0) + return 0; + + left=dst->size - dst->len; + size = left>len ? len : left; + + memcpy(&dst->data[dst->len], string, size); + dst->len += size; + + return size; +} + + +int cp_string_move(cp_string *dst, cp_string *src) +{ + int left; + int size; + + if(!dst || !dst->data || !src || !src->data) + return 0; + + /* Check how many left size in $dst and set the size */ + left=dst->size - dst->len; + size = left>src->len ? src->len : left; + + /* copy the $src data to $dst */ + memcpy(&dst->data[dst->len], src->data, size); + dst->len += size; + + /* remove the $src copied data in it*/ + src->len -= size; + memmove (src->data, src->data+size, src->len); + + return size; +} + +cp_string *cp_string_dup(cp_string *src) +{ + cp_string *str = malloc(sizeof(cp_string)); + + if (str) + { + *str = *src; /* bitwise copy */ + str->data = malloc((str->len + 1) * sizeof(char)); + if (str->data) + memcpy(str->data, src->data, (str->len + 1) * sizeof(char)); + else + { + free(str); + str = NULL; + } + } + + return str; +} + +cp_string *cp_string_cstrdup(char *src) +{ + cp_string *str = malloc(sizeof(cp_string)); + + if (str) + { + str->len = strlen(src); + str->size = str->len + 1; + str->data = malloc(str->size * sizeof(char)); + if (str->data == NULL) + { + free(str); + return NULL; + } + memcpy(str->data, src, str->size); + } + + return str; +} + +cp_string *cp_string_cat(cp_string *str, cp_string *appendum) +{ + int len = str->len; + str->len += appendum->len; + if (str->len + 1 > str->size) + { + str->size = str->len + 1; + str->data = realloc(str->data, str->size * sizeof(char)); + } + if (str->data) + memcpy(str->data + len * sizeof(char), appendum->data, + appendum->len * sizeof(char)); + + return str; +} + +cp_string *cp_string_cstrcat(cp_string *str, char *cstr) +{ + int len = str->len; + int clen = strlen(cstr); + + str->len += clen * sizeof(char); + if (str->len + 1 > str->size) + { +// str->size = str->len + 0x400 - (str->len % 0x400); /* align to 1kb block */ + str->size = str->len + 1; + str->data = realloc(str->data, str->size * sizeof(char)); + } + if (str->data) + memcpy(str->data + len * sizeof(char), cstr, clen); + + return str; +} + +cp_string *cp_string_append_char(cp_string *str, char ch) +{ + if (str->len + 1 > str->size) + { + str->size = str->len + 0x100; + str->data = realloc(str->data, str->size * sizeof(char)); + if (str->data == NULL) return NULL; + } + str->data[str->len++] = ch; + + return str; +} + +cp_string *cp_string_cat_bin(cp_string *str, void *bin, int len) +{ + int olen = str->len; + str->len += len; + + if (str->len > str->size) + { + str->size = str->len + 0x400 - (str->len % 0x400); /* align to 1kb block */ + str->data = realloc(str->data, str->size * sizeof(char)); + } + memcpy(&str->data[olen], bin, len); + + return str; +} + +int cp_string_cmp(cp_string *s1, cp_string *s2) +{ + if (s1 == s2) return 0; //~~ implies cp_string_cmp(NULL, NULL) == 0 + + if (s1 == NULL) return -1; + if (s2 == NULL) return 1; + + if (s1->len == s2->len) + return memcmp(s1->data, s2->data, s1->len); + else + { + int p = (s1->len > s2->len) ? s2->len : s1->len; + int rc = memcmp(s1->data, s2->data, p); + if (rc == 0) + return s1->len - s2->len; + return rc; + } +} + +char *cp_string_tocstr(cp_string *str) +{ + char *cstr = NULL; + + if (str) + { + str->data[str->len * sizeof(char)] = '\0'; +// str->data[str->len * sizeof(char) + 1] = '\0'; + cstr = str->data; + } + + return cstr; +} + +int cp_string_len(cp_string *s) +{ + return s->len; +} + +char *cp_string_data(cp_string *s) +{ + return s->data; +} + +#define CHUNK 0x1000 +cp_string *cp_string_read(int fd, int len) +{ + char buf[CHUNK]; + int read_len; + cp_string *res = NULL; + + if (len == 0) + read_len = CHUNK; + else + read_len = len < CHUNK ? len : CHUNK; + + while (len == 0 || res == NULL || res->len < len) + { + int rc = + read(fd, buf, read_len); + if (rc <= 0) break; + if (res == NULL) + { + res = cp_string_create(buf, rc); + if (res == NULL) return NULL; + } + else + cp_string_cat_bin(res, buf, rc); + } + + return res; +} + +int cp_string_write(cp_string *str, int fd) +{ + int rc; + int total = 0; + + while (total < str->len) + { + rc = write(fd, &str->data[total], str->len - total); + + /* write sets EAGAIN when a socket is marked non-blocking and the + * write would block. trying to write again could result in spinning + * on the write call. + */ + if (rc == -1) + { + if (errno == EINTR /* || errno == EAGAIN */) /* try again */ + continue; + else + break; + } + total += rc; + } + + return total; +} + +cp_string *cp_string_read_file(char *filename) +{ + cp_string *res; + FILE *fp = fopen(filename, "rb"); + if (fp == NULL) return NULL; + + res = cp_string_read(fileno(fp), 0); + fclose(fp); + + return res; +} + +int cp_string_write_file(cp_string *str, char *filename) +{ + int rc; + FILE *fp = fopen(filename, "wb"); + if (fp == NULL) return 0; + + rc = cp_string_write(str, fileno(fp)); + fclose(fp); + + return rc; +} + +#define LINELEN 81 +#define CHARS_PER_LINE 16 + +static char *print_char = + " " + " " + " !\"#$%&'()*+,-./" + "0123456789:;<=>?" + "@ABCDEFGHIJKLMNO" + "PQRSTUVWXYZ[\\]^_" + "`abcdefghijklmno" + "pqrstuvwxyz{|}~ " + " " + " " + " ???????????????" + "????????????????" + "????????????????" + "????????????????" + "????????????????" + "????????????????"; + +void cp_cstring_dump(char *data, int len) +{ + int rc; + int idx; + char prn[LINELEN]; + char lit[CHARS_PER_LINE + 1]; + char hc[4]; + short line_done = 1; + + rc = len; + idx = 0; + lit[CHARS_PER_LINE] = '\0'; + while (rc > 0) + { + if (line_done) + snprintf(prn, LINELEN, "%08X: ", idx); + do + { + unsigned char c = data[idx]; + snprintf(hc, 4, "%02X ", c); + strncat(prn, hc, 4); + lit[idx % CHARS_PER_LINE] = print_char[c]; + ++idx; + } while (--rc > 0 && (idx % CHARS_PER_LINE != 0)); + line_done = (idx % CHARS_PER_LINE) == 0; + if (line_done) + printf("%s %s\n", prn, lit); + else if (rc == 0) + strncat(prn, " ", LINELEN); + } + if (!line_done) + { + lit[(idx % CHARS_PER_LINE)] = '\0'; + while ((++idx % CHARS_PER_LINE) != 0) + strncat(prn, " ", LINELEN); + + printf("%s %s\n", prn, lit); + + } +} + +const char *cp_hexdump_string(const void *data, size_t len) +{ + static char string[1024]; + unsigned char *d = (unsigned char *)data; + unsigned int i; + + string[0] = '\0'; + + for (i = 0; len--; i += 3) { + if (i >= sizeof(string) - 4) + break; + snprintf(string + i, 4, " %02x", *d++); + } + + return string; +} + + +void cp_string_dump(cp_string *str) +{ + int rc; + int idx; + char prn[LINELEN]; + char lit[CHARS_PER_LINE + 1]; + char hc[4]; + short line_done = 1; + + rc = str->len; + idx = 0; + lit[CHARS_PER_LINE] = '\0'; + while (rc > 0) + { + if (line_done) + snprintf(prn, LINELEN, "%08X: ", idx); + do + { + unsigned char c = str->data[idx]; + snprintf(hc, 4, "%02X ", c); + strncat(prn, hc, 4); + lit[idx % CHARS_PER_LINE] = print_char[c]; + ++idx; + } while (--rc > 0 && (idx % CHARS_PER_LINE != 0)); + line_done = (idx % CHARS_PER_LINE) == 0; + if (line_done) + printf("%s %s\n", prn, lit); + else if (rc == 0) + strncat(prn, " ", LINELEN); + } + if (!line_done) + { + lit[(idx % CHARS_PER_LINE)] = '\0'; + while ((++idx % CHARS_PER_LINE) != 0) + strncat(prn, " ", LINELEN); + + printf("%s %s\n", prn, lit); + } +} + +/** flip the contents of a cp_string */ +void cp_string_flip(cp_string *str) +{ + if (str->len) + { + char *i, *f, ch; + f = &str->data[str->len - 1]; + i = str->data; + while (i < f) + { + ch = *i; + *i = *f; + *f = ch; + i++; + f--; + } + } +} + +/* remove all occurrences of letters from str */ +cp_string *cp_string_filter(cp_string *str, char *letters) +{ + char *i; + char *f; + + str->data[str->len] = '\0'; + i = str->data; + while ((f = strpbrk(i, letters))) + { + i = f; + while (*f && strchr(letters, *f)) f++; + if (*f) + { + memmove(i, f, str->len - (f - str->data)); + str->len -= f - i; + str->data[str->len] = '\0'; + } + else + { + *i = '\0'; + str->len -= str->len - (i - str->data); + break; + } + } + + return str; +} + +/** @} */ + + +char *del_char_from_string(char *str, char delchar) +{ + char *idx = str; + char *end = str; + while (*idx) + { + if (*idx == delchar) + { + ++idx; + } + else + { + *end = *idx; + ++end; + ++idx; + } + } + *end = '\0'; + return str; +} + +int split_string_to_value(char *str, char *fmt, ...) +{ + va_list ap; + + int *iPtr; + long *lPtr; + char *pcPtr; + + char delim[2]={*(fmt+2), '\0'}; + char *result; + + va_start(ap, fmt); + + result = strtok( str, delim ); + + while(*fmt) + { + switch (*fmt++) + { + case 's': /* string */ + pcPtr = va_arg(ap, char *); + if(NULL!= result) + { + if(NULL!=pcPtr) + { + strcpy(pcPtr, result); + } + result = strtok( NULL, delim ); + } + else + goto OUT; + + break; + + case 'd': /* int */ + iPtr = va_arg(ap, int *); + if(NULL!= result) + { + if(NULL!=iPtr) + { + *iPtr = atoi(result); + } + result = strtok( NULL, delim ); + } + else + goto OUT; + + break; + + case 'l': /* long */ + lPtr = va_arg(ap, long *); + if(NULL!= result) + { + if(NULL!=lPtr) + { + *lPtr = strtol(result, NULL, 10); + } + result = strtok( NULL, delim ); + } + else + goto OUT; + + break; + + case 'x': /* long hex*/ + lPtr = va_arg(ap, long *); + if(NULL!= result) + { + if(NULL!=lPtr) + { + *lPtr = strtol(result, NULL, 16); + } + result = strtok( NULL, delim ); + } + else + goto OUT; + + break; + } + } + +OUT: + va_end(ap); + return 0; +} + diff --git a/src/cp_library/cp_string.h b/src/cp_library/cp_string.h new file mode 100644 index 0000000..aeea28f --- /dev/null +++ b/src/cp_library/cp_string.h @@ -0,0 +1,86 @@ +#ifndef _CP_STRING_H +#define _CP_STRING_H + +/** @{ */ +/** + * @file + * cp_string - 'safe' string allowing binary content + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> + +/** cp_string definition */ +typedef struct _cp_string +{ + int size; /**< size allocated */ + int len; /**< size used */ + char *data; /**< internal buffer */ +} cp_string; + +/** allocate a new cp_string */ +cp_string *cp_string_create(char *data, int len); +/** allocate an empty cp_string with a given buffer size */ +cp_string *cp_string_create_empty(int initial_size); +/** deallocate a cp_string */ +void cp_string_destroy(cp_string *str); +/** Sets string data to 0 */ +void cp_string_clear_data(cp_string *str); +/** copies the content of a null terminated c string */ +cp_string *cp_string_cstrcpy(cp_string *str, char *cstr); +/** copies the content of a cp_string */ +//cp_string *cp_string_cp(cp_string *dst, cp_string *src); +int cp_string_copy(cp_string *dst, cp_string *src); +int cp_string_move(cp_string *dst, cp_string *src); +/** copy the string into *dst */ +int cp_string_cstrcopy(cp_string *dst, char *string, int len); +/** creates a copy of src string. internal buffer is duplicated. */ +cp_string *cp_string_dup(cp_string *src); +/** creates a cp_string with src as its content */ +cp_string *cp_string_cstrdup(char *src); +/** concatenate cp_strings */ +cp_string *cp_string_cat(cp_string *str, cp_string *appendum); +/** append data from a buffer */ +cp_string *cp_string_cat_bin(cp_string *str, void *bin, int len); +/** append data from a null terminated c string */ +cp_string *cp_string_cstrcat(cp_string *str, char *cstr); +/** append a character to a string */ +cp_string *cp_string_append_char(cp_string *str, char ch); +/** compare cp_strings */ +int cp_string_cmp(cp_string *s1, cp_string *s2); +/** return a pointer to the internal buffer */ +char *cp_string_tocstr(cp_string *str); +/** return the length of the internal buffer */ +int cp_string_len(cp_string *s); +/** return the internal buffer */ +char *cp_string_data(cp_string *s); + +/** read len bytes from an open file descriptor (blocking) */ +cp_string *cp_string_read(int fd, int len); +/** write the content of a cp_string to a file descriptor (blocking) */ +int cp_string_write(cp_string *str, int fd); +/** read the contents of a file into a cp_string */ +cp_string *cp_string_read_file(char *filename); +/** write the contents of a cp_string to a file */ +int cp_string_write_file(cp_string *str, char *filename); + +/** flip the contents of a cp_string */ +void cp_string_flip(cp_string *str); +/** remove all occurrences of letters from str */ +cp_string *cp_string_filter(cp_string *str, char *letters); + +/** dump a cp_string to stdout */ +const char *cp_hexdump_string(const void *data, size_t len); +void cp_string_dump(cp_string *str); +void cp_cstring_dump(char *data, int len); + +char *del_char_from_string(char *str, char delchar); +int split_string_to_value(char *str, char *fmt, ...); + +/** @} */ + +#endif + diff --git a/src/cp_library/cp_time.h b/src/cp_library/cp_time.h new file mode 100644 index 0000000..3a35e1e --- /dev/null +++ b/src/cp_library/cp_time.h @@ -0,0 +1,123 @@ +/******************************************************************************** + * Copyright: (C) 2012 CoherentPlus Sdn. Bhd. + * All rights reserved. + * + * Filename: cp_time.h + * Description: This head file + * + * Version: 1.0.0(02/23/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "02/23/2012 07:46:37 AM" + * + ********************************************************************************/ +#ifndef __CP_TIME_H +#define __CP_TIME_H + +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <time.h> + +#include <linux/rtc.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> + +typedef struct __DATE_TIME +{ + int iYear; + int iMonth; + int iDay; + int iHour; + int iMinute; + int iSecond; + int iDayOfWeek; +} DATE_TIME; + +static inline void micro_second_sleep(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); +} + +/*UNIT: micro second*/ +static inline unsigned long time_now() +{ + struct timeval now; + gettimeofday(&now, 0); + return (now.tv_sec * 1000) + (now.tv_usec / 1000); +} + +/*UNIT: micro second*/ +static inline unsigned long time_elapsed(unsigned long start) +{ + unsigned long current = time_now(); + + if (current < start) + { + return (0xFFFFFFFF - start) + current; + } + + //printf("time_elapsed: %ld\n", current-start); + return current - start; +} + +static inline void get_current_time(DATE_TIME * date) +{ + time_t now = time(NULL); + struct tm *tnow = localtime(&now); + + memset(date, 0, sizeof(DATE_TIME)); + date->iYear = 1900 + tnow->tm_year; + date->iMonth = 1 + tnow->tm_mon; + date->iDay = tnow->tm_mday; + + date->iHour = tnow->tm_hour; + date->iMinute = tnow->tm_min; + date->iSecond = tnow->tm_sec; + date->iDayOfWeek = tnow->tm_wday; + return; +} +#define get_sys_time(date) get_current_time(date) + +static inline int get_rtc_time(DATE_TIME *date) +{ + int rv, fd = -1; + struct rtc_time rtc_tm; + + memset(date, 0, sizeof(DATE_TIME)); + + if ((fd=open("/dev/rtc0", O_RDONLY)) < 0) + return -1; + + if((rv=ioctl(fd, RTC_RD_TIME, &rtc_tm)) < 0) + return -2; + + date->iYear = 1900 + rtc_tm.tm_year; + date->iMonth = 1 + rtc_tm.tm_mon; + date->iDay = rtc_tm.tm_mday; + + date->iHour = rtc_tm.tm_hour; + date->iMinute = rtc_tm.tm_min; + date->iSecond = rtc_tm.tm_sec; + date->iDayOfWeek = rtc_tm.tm_wday; + + close(fd); + + return 0; +} + +#endif diff --git a/src/cp_library/makefile b/src/cp_library/makefile new file mode 100644 index 0000000..9479da4 --- /dev/null +++ b/src/cp_library/makefile @@ -0,0 +1,108 @@ +#********************************************************************************* +# Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> +# All rights reserved. +# +# Filename: Makefile +# Description: This is the common subdir Makefile which to compile all the C +# source code to object files and then generate the shared or +# static library named lib$(FOLDER_NAME).a orlib $(FOLDER_NAME).so, +# which depends on the variable $LINK_MODE. +# +# Version: 1.0.0(10/08/2011~) +# Author: Guo Wenxue <guowenxue@gmail.com> +# ChangeLog: 1, Release initial version on "10/08/2011 01:29:33 AM" +# +#********************************************************************************/ + +PWD=$(shell pwd) + +#If wanna compile in the subdir, not called by top makefile, uncomment it +ifneq (${TOP_COMPILE}, YES) +LOCAL_COMPILE=YES +endif + +LINK_MODE=STATIC +#MULTHREADS=YES + +LIBNAME=$(shell basename ${PWD}) +STALIB=lib${LIBNAME}.a +DYNLIB=lib${LIBNAME}.so + +VPATH= . +SRCS = $(wildcard ${VPATH}/*.c) +OBJS = $(patsubst %.c,%.o,$(SRCS)) + +#====================================================== +# ---> Doesn't call by top makefile, compile by local +#====================================================== +ifeq (${LOCAL_COMPILE}, YES) +ARCH?=arm +#ARCH?=i386 +CFLAGS+=-fPIC +TMP=$(shell echo $(ARCH) | tr "[A-Z]" "[a-z]") +ifneq (,$(filter i386,$(TMP))) + CROSS_COMPILE= +else + CROSS_COMPILE=/opt/rpi/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf- +endif + +PRJDIR?=$(shell dirname ${PWD}) +CFLAGS+=-I${PRJDIR} -g -Wall -Werror +CC = ${CROSS_COMPILE}gcc +AR = ${CROSS_COMPILE}ar + +endif #End local compile + +ifeq ("${LINK_MODE}", "STATIC") + LIBS = ${STALIB} +else + LIBS=${DYNLIB} ${STALIB} +endif + +ifeq ("${MULTHREADS}", "YES") +LDFLAGS+=-lpthread +CFLAGS+=-DMULTHREADS +endif + +all: entry clean ${LIBS} install + +entry: + @echo " "; + @echo " ========================================================="; + @echo " ** Compile subdir ${LIBNAME} for ${ARCH} "; + @echo " ========================================================="; + +#$(LD) -g --relocatable $(OBJS) -o lib${LIBNAME}.o +${STALIB}: $(OBJS) + $(AR) -rcu $@ $(OBJS) + +${DYNLIB}: $(OBJS) + $(CC) -fPIC -shared -o $@ $(OBJS) + +%.o : %.c + $(CC) -c $< $(CFLAGS) + +test: clean ${LIBS} + make -C test ARCH=${ARCH} + +tag: + @ctags --c-kinds=+defglmnstuvx --langmap=c:.c.h.ho.hem.het.hec.hev.him.hit.hic.hiv -R . + @cscope -Rbq + +install: + @if [ ! -z "${LIBS_PATH}" ] ; then \ + mkdir -p ${LIBS_PATH} ; \ + cp ${LIBS} ${LIBS_PATH}; \ + fi; + +clean: + @rm -f *.o *.lo *~ + @rm -rf *.gdb *.a *.so + @make clean -C test + @rm -f *.log + +distclean: clean + @rm -f tags cscope* + +.PHONY: clean entry + diff --git a/src/cp_library/test/comport.c b/src/cp_library/test/comport.c new file mode 100644 index 0000000..162becc --- /dev/null +++ b/src/cp_library/test/comport.c @@ -0,0 +1,238 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: comport.c + * Description: This file used to do ioctl() on common device or communicate + * with serial port/TTY device. + * + * Version: 1.0.0(10/18/2011~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "10/18/2011 10:08:05 AM" + * + ********************************************************************************/ +#include <getopt.h> +#include <libgen.h> +#include <sys/ioctl.h> + +#include "cp_comport.h" +#include "cp_common.h" +#include "version.h" + +unsigned char g_ucProcToken = 0x01; +unsigned char g_ucCtrlZ; + +void print_version(char *name); +void usage(char *name); +int do_ioctl(char *dev_name, int cmd, int arg); +void signal_handler(int i_sig); + +int main(int argc, char **argv) +{ + int opt = 0; + int retval = 0; + int recv_size = 0; + int i; + char *dev_name = NULL; + int baudrate = 115200; + char *settings = "8N1N"; + char buf[512]; + unsigned char disp_mode = 0x00; + + struct sigaction sigact; + + struct option long_options[] = { + {"device", required_argument, NULL, 'd'}, + {"baudrate", required_argument, NULL, 'b'}, + {"settings", required_argument, NULL, 's'}, + {"ioctl", required_argument, NULL, 'i'}, + {"hex", no_argument, NULL, 'x'}, + {"version", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + while ((opt = getopt_long(argc, argv, "d:b:s:ivh", long_options, NULL)) != -1) + { + switch (opt) + { + case 'd': + dev_name = optarg; + break; + case 'b': + baudrate = atoi(optarg); + break; + case 's': /* Default settings as 8N1N */ + settings = optarg; + break; + case 'i': + if (5 != argc) + { + usage(argv[0]); + } + else + { + do_ioctl(argv[2], atoi(argv[3]), atoi(argv[4])); + } + return 0; + case 'x': /* Display receive data as Hex mode */ + disp_mode = 0x01; + break; + case 'v': /* version */ + print_version(argv[0]); + return 0; + case 'h': /* help */ + usage(argv[0]); + return 0; + default: + break; + } /* end of "switch(opt)" */ + } + + if (argc < 2) + { + usage(argv[0]); + return 0; + } + + comport_t *comport = NULL; + if (NULL == (comport = comport_init(dev_name, baudrate, settings))) + { + printf("Comport initialize failure.\n"); + return -1; + } + + if ( (retval=comport_open(comport)) < 0) + { + printf("Failed to open %s with baudrate %d, %s. RetCode [%d]\n", dev_name, baudrate, + settings, retval); + return -1; + } + + nonblock(); + + /* Process level signal handler */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = signal_handler; + + sigaction(SIGTERM, &sigact, NULL); /* catch terminate signal */ + sigaction(SIGINT, &sigact, NULL); /* catch interrupt signal */ + sigaction(SIGSEGV, &sigact, NULL); /* catch segmentation faults */ + sigaction(SIGTSTP, &sigact, NULL); /* catch ctrl+Z */ + sigaction(SIGSTOP, &sigact, NULL); /* catch ctrl+Z */ + + while (0x01 == g_ucProcToken) + { + recv_size = comport_recv(comport, buf, sizeof(buf) - 1, 10); + if (recv_size > 0) + { + for (i = 0; i < recv_size; i++) + { + if (0 == disp_mode) + printf("%c", buf[i]); + else + printf("%02X ", buf[i]); + } + fflush(stdout); + } + if (0 != kbhit()) + { + retval = fgetc(stdin); + + if (0x0A == retval) + { + buf[0] = 0x0D; /* 13 == 0x0D */ + } + else + { + buf[0] = retval; + } + + comport_send(comport, buf, 1); + } + else if (0x00 != g_ucCtrlZ) + { + g_ucCtrlZ = 0x00; + buf[0] = 0x1A; + comport_send(comport, buf, 1); + } + } + + comport_term(comport); + return 0; +} /* ----- End of main() ----- */ + +void print_version(char *name) +{ + char *progname = NULL; + char *ptr = NULL; + + ptr = strdup(name); + progname = basename(ptr); + + printf("%s version: %d.%d.%d Build %04d on %s\n", progname, MAJOR, MINOR, REVER, SVNVER, DATE); + printf("Copyright (C) 2010 guowenxue <guowenxue@gmail.com>\n"); + + free(ptr); + return; +} + +void usage(char *name) +{ + char *progname = NULL; + char *ptr = NULL; + + ptr = strdup(name); + progname = basename(ptr); + printf("Usage1: comport -d <device> [-b <baudrate>][-s <settings>] [-x]\n"); + printf("Usage2: comport [-i <driver port> <cmd> <arg>][--help][--version]\n"); + printf(" -d[device ] device name\n"); + printf(" -b[baudrate] device baudrate (115200, 57600, 19200, 9600), default is 115200\n"); + printf(" -s[settings] device settings as like 8N1N(default setting)\n"); + printf(" - data bits: 8, 7\n"); + printf(" - parity: N=None, O=Odd, E=Even, S=Space\n"); + printf(" - stop bits: 1, 0\n"); + printf(" - flow control: N=None, H=Hardware, S=Software, B=Both\n"); + printf(" -x[hex ] display received data in hex format\n"); + printf(" -i[ioctl ] ioctl system call (cmd & arg only support int)\n"); + printf(" -v[version ] Display the program version\n"); + printf(" -h[help ] Display this help information\n"); + + print_version(progname); + + free(ptr); + + return; +} + +int do_ioctl(char *dev_name, int cmd, int arg) +{ + int fd = -1; + int retval = -1; + if (((fd = open(dev_name, O_RDWR)) < 0)) + { + printf("Open device \"%s\" failure: %s\n", dev_name, strerror(errno)); + return -1; + } + + retval = ioctl(fd, cmd, arg); + printf("ioctl (%s, %d, %d) returned %d\n", dev_name, cmd, arg, retval); + + close(fd); + return retval; +} + +void signal_handler(int i_sig) +{ + if (SIGTERM == i_sig || SIGINT == i_sig) + { + g_ucProcToken = 0x00; + } + else if (20 == i_sig) + { + g_ucCtrlZ = 0x01; + } +} + + diff --git a/src/cp_library/test/makefile b/src/cp_library/test/makefile new file mode 100644 index 0000000..f1d11c8 --- /dev/null +++ b/src/cp_library/test/makefile @@ -0,0 +1,122 @@ +#********************************************************************************* +# Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> +# 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(10/08/2011~) +# Author: Guo Wenxue <guowenxue@gmail.com> +# ChangeLog: 1, Release initial version on "11/11/2011 01:29:33 PM" +# +#********************************************************************************/ + +PWD=$(shell pwd) +LIB_PATH=$(shell dirname ${PWD}) +LIB_NAME=$(shell basename ${LIB_PATH}) +INSTPATH=/tftp + +#ARCH ?= i386 +ARCH?=arm + +LINK_MODE=STATIC +MODE=PRODUCTION +DEBUG=1 + +CFLAGS+=-Wall -Werror +#CFLAGS+=-Wno-unused + +ifeq ("${MODE}", "PRODUCTION") + CFLAGS+=-DPRODUCTION_MODE +endif +ifdef DEBUG + CFLAGS+=-g -DDEBUG +endif + +COMPILE_DATE=$(shell date -u +"%Y-%m-%d %H:%M") +VPATH= . +SRCS = $(wildcard ${VPATH}/*.c) +OBJS = $(patsubst %.c,%.o,$(SRCS)) + +TMP=$(shell echo $(ARCH) | tr "[A-Z]" "[a-z]") +ifneq (,$(filter i386,$(TMP))) + CROSS_COMPILE= +else + CROSS_COMPILE=/opt/rpi/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf- +endif + + +CFLAGS+=-I${LIB_PATH} +LDFLAGS+=-L${LIB_PATH} -l${LIB_NAME} + +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 +export CFLAGS +export LDFLAGS +export ARCH +export LINK_MODE + +LDFLAGS+=-lpthread +ifeq ("${LINK_MODE}", "STATIC") + CFLAGS+=--static + LDFLAGS+=-static +else + LDFLAGS+=-ldl +endif + +SRCFILES = $(wildcard *.c) +BINARIES=$(SRCFILES:%.c=%) + +all: entry version binaries install +entry: + @echo "#### ${LIB_PATH}" + @echo " "; + @echo " ========================================================="; + @echo " ** Compile \"${BINARIES}\" for ${ARCH} "; + @echo " ========================================================="; + +version: + @echo "/* Generated by makefile, don't Edit it by hand */" > version.h + @echo "#define DATE \"$(COMPILE_DATE)\"" >> version.h + @echo "#define MAJOR 1" >>version.h + @echo "#define MINOR 0" >>version.h + @echo "#define REVER 0" >>version.h + @if [ -f .svn/entries ] ; then \ + echo "#define SVNVER `sed -n -e 11p .svn/entries`" >>version.h; \ + else \ + echo "#define SVNVER 0" >>version.h; \ + fi; + @echo "" >> version.h + @echo '#define version(progname) printf("%s Version %d.%d.%d Build @%05d (%s)\\n", progname, MAJOR, MINOR, REVER,SVNVER, DATE)' >> version.h + @echo '#define copyright() printf("Copyright: (C) 2012 Guo Wenxue<Email:guowenxue@gmail.com\\n")' >>version.h + @echo '#define banner(progname) {version(progname); copyright(); printf("\\n");}' >>version.h + @echo "" >> version.h + +binaries: ${BINARIES} + @echo " Compile over" + +%: %.c + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) + +tag: + @ctags --c-kinds=+defglmnstuvx --langmap=c:.c.h.ho.hem.het.hec.hev.him.hit.hic.hiv -R . + @cscope -Rbq + +install: + @cp $(BINARIES) ${INSTPATH} + +clean: + @rm -f version.h + @rm -f *.o *.lo $(BINARIES) + @rm -rf *.gdb *.a *.so *.elf* + @rm -f *.log + +distclean: clean + @rm -f tags cscope* + +.PHONY: clean entry diff --git a/src/cp_library/test/sample.conf b/src/cp_library/test/sample.conf new file mode 100644 index 0000000..b59acc9 --- /dev/null +++ b/src/cp_library/test/sample.conf @@ -0,0 +1,29 @@ +# Program configure file sample +[log] +file=/var/log/testd.log + +#Loglevel: 0:Disable 1:Fatal 2:ERROR 3:warnning 4:Normal 5:Debug 6:Infor 7:Trace +level=5 + +#Unit Kib +size=1024 + +[comport] +#Comport device +devname="/dev/ttyS1" + +#Baudrate: 115200,57600,38400,19200,9600,4800,2400,1800,1200,... +baudrate=115200 + +#Settings format is: Databit[8/7],Parity[O/E/S/N],StopBit[1/0],FlowControl[S/H/N] +settings="8N1N" + +timeout=1000 + + +[server] +hostname=stuidio.iot-yun.com +port=9999 +heartbeat=30 + + diff --git a/src/cp_library/test/test_ini.c b/src/cp_library/test/test_ini.c new file mode 100644 index 0000000..a5ac019 --- /dev/null +++ b/src/cp_library/test/test_ini.c @@ -0,0 +1,90 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: test_ini.c + * Description: This file + * + * Version: 1.0.0(12/18/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "12/18/2012 10:54:09 AM" + * + ********************************************************************************/ + +#include "cp_iniparser.h" + + +#define SAMPLE_CONF "sample.conf" + +/******************************************************************************** + * Description: + * Input Args: + * Output Args: + * Return Value: + ********************************************************************************/ +int main (int argc, char **argv) +{ + dictionary *ini; + char *ini_name = SAMPLE_CONF; + char *str; + int data; + + ini = iniparser_load(ini_name); + if (ini==NULL) + { + printf("cannot parse file: %s\n", ini_name); + return -2 ; + } + + printf("+----------------------------------+\n" + "| Program logger configuration |\n" + "+----------------------------------+\n"); + + str = iniparser_getstring(ini, "log:file", NULL); + printf("Configure logger file name: %s\n", str); + + data = iniparser_getint(ini, "log:level", 0); + printf("Configure logger level: %d\n", data); + + data = iniparser_getint(ini, "log:size", 1024); + printf("Configure logger size: %d\n", data); + + + + printf("\n+----------------------------------+\n" + "| Comport configuration |\n" + "+----------------------------------+\n"); + + str = iniparser_getstring(ini, "comport:devname", NULL); + printf("Comport device name: %s\n", str); + + data = iniparser_getint(ini, "comport:baudrate", 115200); + printf("Comport baudrate: %d\n", data); + + str = iniparser_getstring(ini, "comport:settings", NULL); + printf("Comport settings: %s\n", str); + + data = iniparser_getint(ini, "comport:timeout", 3000); + printf("Comport timeout: %d\n", data); + + + + printf("\n+----------------------------------+\n" + "| Server host configuration |\n" + "+----------------------------------+\n"); + str = iniparser_getstring(ini, "server:hostname", NULL); + printf("Connect server hostname: %s\n", str); + + data = iniparser_getint(ini, "server:port", 8000); + printf("Connect server port: %d\n", data); + + data = iniparser_getint(ini, "server:heartbeat", 60); + printf("Connect server heartbeat: %d\n", data); + + + + iniparser_freedict(ini); + + return 0; +} /* ----- End of main() ----- */ + diff --git a/src/cp_library/test/test_klist.c b/src/cp_library/test/test_klist.c new file mode 100644 index 0000000..fdf23f3 --- /dev/null +++ b/src/cp_library/test/test_klist.c @@ -0,0 +1,92 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: test_klist.c + * Description: This file is for test kernel space double linked list. + * + * Version: 1.0.0(11/12/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/12/2012 04:26:39 PM" + * + ********************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "cp_klist.h" + +typedef struct node_s +{ + int data; + struct list_head link; +} node_t; + + + + +void travel_list(struct list_head *head) +{ + node_t *new_node; + node_t *node, *tmp; + + + if( (new_node=malloc(sizeof(node_t))) ) + { + list_add_tail(&new_node->link, head); + printf("Add new node %p to list \n", new_node); + } + + /* Use list_for_each_entry to travel the list, we can not remove the node in it */ + list_for_each_entry_safe(node, tmp, head, link) + { + printf("Travel2 list on node %p\n", node); + } +} + + +/******************************************************************************** + * Description: + * Input Args: + * Output Args: + * Return Value: + ********************************************************************************/ +int main (int argc, char **argv) +{ + int i; + struct list_head header; + node_t *node; + node_t *tmp; + + INIT_LIST_HEAD(&header); + + for(i=0; i<10; i++) + { + node=malloc(sizeof(node_t)); + memset(node, 0, sizeof(node_t)); + node->data=i; + if( node ) + { + list_add_tail(&node->link, &header); + printf("Add node %p to list \n", node); + } + } + + /* Use list_for_each_entry to travel the list, we can not remove the node in it */ + list_for_each_entry(node, &header, link) + { + printf("Travel list on node %p\n", node); + } + travel_list(&header); + + /* Use list_for_each_entry_safe to travel the list and destroy the node */ + list_for_each_entry_safe(node, tmp, &header, link) + { + list_del(&node->link); + printf("Remove and destroy node %p from list\n", node); + free(node); + } + + return 0; +} /* ----- End of main() ----- */ + diff --git a/src/cp_library/test/test_logger.c b/src/cp_library/test/test_logger.c new file mode 100644 index 0000000..e22b622 --- /dev/null +++ b/src/cp_library/test/test_logger.c @@ -0,0 +1,58 @@ +/********************************************************************************* + * 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 "cp_logger.h" + + +/******************************************************************************** + * Description: + * Input Args: + * Output Args: + * Return Value: + ********************************************************************************/ +int main (int argc, char **argv) +{ + char buf[30]="Hello World!\n"; + char file[FILENAME_LEN]; + + snprintf(file, FILENAME_LEN, "%s.log", basename(argv[0]) ); + + //if (! cp_log_init(NULL, DBG_LOG_FILE, LOG_LEVEL_NRML, LOG_ROLLBACK_NONE) ) + //if (! cp_log_init(NULL, DEFAULT_LOGFILE, LOG_LEVEL_MAX, LOG_ROLLBACK_SIZE) ) + if(! cp_log_init(NULL, file, LOG_LEVEL_MAX, 12) || cp_log_open() ) + return 0; + + + while(1) + { + log_info("1connection.\n"); + log_dbg("2connection.\n"); + log_nrml("3connection.\n"); + log_warn("4connection.\n"); + log_err("5connection.\n"); + log_fatal("6connection.\n"); + + cp_log_dump(LOG_LEVEL_DEBUG, buf, 30); + + sleep(1); + } + + cp_log_term(); + + return 0; +} /* ----- End of main() ----- */ + diff --git a/src/cp_library/test/test_queue.c b/src/cp_library/test/test_queue.c new file mode 100644 index 0000000..0d345d2 --- /dev/null +++ b/src/cp_library/test/test_queue.c @@ -0,0 +1,90 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: test_queue.c + * Description: This file used to test the queue library + * + * Version: 1.0.0(08/14/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "08/14/2012 05:17:03 PM" + * + ********************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "cp_queue.h" + +typedef struct node_s +{ + int data; + CP_QNODE *link; +} node_t; + +#define MAX_ITEMS 10 + +/******************************************************************************** + * Description: + * Input Args: + * Output Args: + * Return Value: + ********************************************************************************/ +int main (int argc, char **argv) +{ + int i; + + node_t *node = NULL; + node_t *tmp = NULL; + + CP_QUEUE *queue = NULL; + + queue = cp_queue_init(NULL, MAX_ITEMS); + + for (i=0; i<MAX_ITEMS+10; i++) + { + node = malloc(sizeof(node_t)); + if( !node ) + break; + + if( NULL==cp_enqueue(queue, node) ) + { + free(node); + } + else + { + if(i==3) + { + tmp = node; + } + printf("enqueue node %p\n", node); + } + } + + + printf("queue usage %d/%d \n", cp_queue_count(queue), cp_queue_size(queue)); + + cp_travel_queue(queue); + if( cp_rmqueue(queue, tmp) ) + { + printf("remove and terminate node [%p] from queue\n", tmp); + free(tmp); + } + cp_travel_queue(queue); + + while(!cp_queue_is_empty(queue)) + { + if( (node=cp_dequeue(queue)) ) + { + printf("Terminate node: %p\n", node); + free(node); + } + } + + cp_queue_destroy(queue); + + return 0; + +} /* ----- End of main() ----- */ + diff --git a/src/cp_library/test/test_string.c b/src/cp_library/test/test_string.c new file mode 100644 index 0000000..0b8d4f3 --- /dev/null +++ b/src/cp_library/test/test_string.c @@ -0,0 +1,77 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: test_string.c + * Description: This file + * + * Version: 1.0.0(11/27/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/27/2012 01:28:39 PM" + * + ********************************************************************************/ + +#include <cp_string.h> + + +/******************************************************************************** + * Description: + * Input Args: + * Output Args: + * Return Value: + ********************************************************************************/ +int main (int argc, char **argv) +{ + int i; + cp_string *rcv; + cp_string *snd; + + rcv = cp_string_create_empty(64); + snd = cp_string_create_empty(64); + + printf("=======================\n"); + printf("Test cp_string_copy \n"); + printf("=======================\n"); + cp_string_clear_data(rcv); + cp_string_cstrcpy(rcv, "Hello world!"); + printf("Receive buffer data:\n"); + cp_string_dump(rcv); + + for(i=0; i<20; i++) + { + if( cp_string_copy(snd, rcv) > 0) + { + printf("[%d] Send buffer data:\n", i); + cp_string_dump(snd); + } + } + + printf("\n=======================\n"); + printf("Test cp_string_move \n"); + printf("=======================\n"); + + cp_string_clear_data(snd); + cp_string_clear_data(rcv); + for(i=0; i<20; i++) + { + cp_string_cstrcpy(rcv, "Hello world!"); + //printf("Set new receive buffer data [%d] bytes:\n", cp_string_len(rcv)); + cp_string_dump(rcv); + + if( cp_string_move(snd, rcv) > 0) + { + printf("[%d] Send buffer [%d] bytes data:\n", i, cp_string_len(snd)); + cp_string_dump(snd); + + printf("[%d] receive buffer [%d] bytes data:\n", i, cp_string_len(rcv)); + cp_string_dump(rcv); + } + } + + cp_string_destroy(rcv); + cp_string_destroy(snd); + + + return 0; +} /* ----- End of main() ----- */ + -- Gitblit v1.9.1