LingYun IoT Studio NB-IoT research project
guowenxue
2018-11-19 ec8c799d8bb2ee69b5e6f56201231e8c905edeb1
Add cp_library library
34 files added
8476 ■■■■■ changed files
src/cp_library/cp_atcmd.c 668 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_atcmd.h 68 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_common.h 71 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_comport.c 600 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_comport.h 67 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_dictionary.c 398 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_dictionary.h 165 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_gprs.c 677 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_gprs.h 128 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_iniparser.c 807 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_iniparser.h 308 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_klist.h 723 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_logger.c 420 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_logger.h 110 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_ppp.c 596 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_ppp.h 138 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_proc.c 340 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_proc.h 42 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_queue.c 155 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_queue.h 49 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_ringbuf.c 103 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_ringbuf.h 57 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_string.c 673 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_string.h 86 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_time.h 123 ●●●●● patch | view | raw | blame | history
src/cp_library/makefile 108 ●●●●● patch | view | raw | blame | history
src/cp_library/test/comport.c 238 ●●●●● patch | view | raw | blame | history
src/cp_library/test/makefile 122 ●●●●● patch | view | raw | blame | history
src/cp_library/test/sample.conf 29 ●●●●● patch | view | raw | blame | history
src/cp_library/test/test_ini.c 90 ●●●●● patch | view | raw | blame | history
src/cp_library/test/test_klist.c 92 ●●●●● patch | view | raw | blame | history
src/cp_library/test/test_logger.c 58 ●●●●● patch | view | raw | blame | history
src/cp_library/test/test_queue.c 90 ●●●●● patch | view | raw | blame | history
src/cp_library/test/test_string.c 77 ●●●●● patch | view | raw | blame | history
src/cp_library/cp_atcmd.c
New file
@@ -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;
}
src/cp_library/cp_atcmd.h
New file
@@ -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 */
src/cp_library/cp_common.h
New file
@@ -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 */
src/cp_library/cp_comport.c
New file
@@ -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;
}
src/cp_library/cp_comport.h
New file
@@ -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
src/cp_library/cp_dictionary.c
New file
@@ -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 */
src/cp_library/cp_dictionary.h
New file
@@ -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
src/cp_library/cp_gprs.c
New file
@@ -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, &reg->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 ;
}
src/cp_library/cp_gprs.h
New file
@@ -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 */
src/cp_library/cp_iniparser.c
New file
@@ -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 */
src/cp_library/cp_iniparser.h
New file
@@ -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
src/cp_library/cp_klist.h
New file
@@ -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
src/cp_library/cp_logger.c
New file
@@ -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);
    }
}
src/cp_library/cp_logger.h
New file
@@ -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  */
src/cp_library/cp_ppp.c
New file
@@ -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);
}
src/cp_library/cp_ppp.h
New file
@@ -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 */
src/cp_library/cp_proc.c
New file
@@ -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);
}
src/cp_library/cp_proc.h
New file
@@ -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
src/cp_library/cp_queue.c
New file
@@ -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 ;
}
src/cp_library/cp_queue.h
New file
@@ -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
src/cp_library/cp_ringbuf.c
New file
@@ -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)];
}
src/cp_library/cp_ringbuf.h
New file
@@ -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_ */
src/cp_library/cp_string.c
New file
@@ -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;
}
src/cp_library/cp_string.h
New file
@@ -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
src/cp_library/cp_time.h
New file
@@ -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
src/cp_library/makefile
New file
@@ -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
src/cp_library/test/comport.c
New file
@@ -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;
    }
}
src/cp_library/test/makefile
New file
@@ -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
src/cp_library/test/sample.conf
New file
@@ -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
src/cp_library/test/test_ini.c
New file
@@ -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() ----- */
src/cp_library/test/test_klist.c
New file
@@ -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() ----- */
src/cp_library/test/test_logger.c
New file
@@ -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() ----- */
src/cp_library/test/test_queue.c
New file
@@ -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() ----- */
src/cp_library/test/test_string.c
New file
@@ -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() ----- */