2013年9月11日 星期三

android note

文章出處:http://wukiandroid.blogspot.tw/2012/05/ril-event.html

RIL daemon


RILD
rild.c, ril.cpp, ril_event.cpp, reference-ril.c(),qcril.c(以Qualcomm verdor 提供的qcril.c取代reference_ril.c)以上是相關的.c or .cpp檔
RILD上承RILJ,也就是ril.java,往下是將AT command我們用QMI送給modem
所以簡單來說,RILD的功能就是將RIL request轉成AT command

這個daemon在開機的時候就會init起來,其參數的定義放在init.rc裡面
class main
    socket rild stream 660 root radio
    socket rild-debug stream 660 radio system
    user root

首先,先看rild.c中的main()
RIL_startEventLoop()這個function的實作放在ril.cpp中
RIL_startEventLoop(void) {
    ....
    ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
    ...
}

他會create一個thread, 入口是eventLoop(),同樣實作在ril.cpp
static void *
eventLoop(void *param) {
    ...
    ril_event_init();
    ....
    ril_event_loop();
    ...
}

RILD裡頭有一套event的機制來處理上層送下來的request.
ril_event_init()用來init所需的event queue,總共有三種: timer list, pending list, watch_table

main()中的rilInit(&s_rilEnv, argc, rilArgv)
這個function會參照到reference-ril.c中的RIL_Init()
這裡頭又會create "mainLoop"這個thread
ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);

看一下mainLoop裡頭做的事情
有一部分的事情是要透過socket跟qemud溝通?? 這一部分還沒看熟
另外
ret = at_open(fd, onUnsolicited);
這個function內部會create "readLoop"這個thread,它用來讀從channel送上來的AT command,並且註冊用來處理unsolicited AT command的function

還有
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0)這個function會將會新增一event,並且填入callback function: initializeCallback(),看一下此callback做的事情

    at_send_command("ATE0Q0V1", NULL);
    ... 
    /*  No auto-answer */
    at_send_command("ATS0=0", NULL);
    ... 
    /*  Extended errors */
    at_send_command("AT+CMEE=1", NULL);
    ... 

看樣子是要intialize AT command channel

rilInit()所回傳的pointer是指向s_callbacks,定義在reference-ril.c



static const RIL_RadioFunctions s_callbacks = {
    RIL_VERSION,
    onRequest,
    currentState,
    onSupports,
    onCancel,
    getVersion
};


此pointer接著會被丟到RIL_register(),定義在ril.cpp中
看一下RIL_register()中所做的事情

s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
...
ret = listen(s_fdListen, 4);
...
ril_event_set (&s_listen_event, s_fdListen, false,
                listenCallback, NULL);
...
rilEventAddWakeup (&s_listen_event);

android_get_control_socket()取得rild socket的file descriptor,這邊的rild也就是之前提到定義在init.rc內的rild

接著,透過ril_event_set()新增一event,填入此rild的fd與callback function

    ril_event_set (&s_listen_event, s_fdListen, false,
                listenCallback, NULL);

rilEventAddWakeup()會把event放入watch_table裡面

下圖是關於以上說明的簡單示意圖






























所以會有3個thread被create出來,mainLoop, readLoop, eventLoop
藍色部分是會被set的RIL event. 到時候會被排入ril event loop中執行

event說明
1. ril_event_set (&s_listen_event, s_fdListen, false,listenCallback, NULL);
    s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
    // accept a connection並且取得new socket的file descriptor,此s_fd_Command接著會被傳入
    // s_commands_event裡面,用來read從RIL.java送下來的ril request 

    ret = fcntl(s_fdCommand, F_SETFL, O_NONBLOCK);
    // 設定s_fdCommand的屬性 (??)

     p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);
     // read data from s_fdCommand 

2. ril_event_set (&s_commands_event, s_fdCommand, 1,
        processCommandsCallback, p_rs);
for (;;) {
        ret = record_stream_get_next(p_rs, &p_record, &recordlen);

        // read data from s_fdCommand
       ...
        processCommandBuffer(p_record, recordlen);
        // data 在此function裡面會被打包成RequestInfo,也就是pRI
}

看一下processCommandBuffer()內所做的事情,它定義在ril.cpp中
先介紹兩種資料結構,Parcel與RequestInfo
1. Parcel: http://developer.android.com/reference/android/os/Parcel.html
用來放data的容器(資料結構),RILJ要往RILD送的data都會先封裝在Parcel裡面再透過socket傳送

2. RequestInfo:
    typedef struct RequestInfo {
        int32_t token;      //this is not RIL_Token
        CommandInfo *pCI;
        struct RequestInfo *p_next;
        char cancelled;
        char local;         // responses to local commands do not go back to command process
    } RequestInfo;

token: ril request (from RILJ)的sequence number
pCI: callback function for this ril request

    typedef struct {
        int requestNumber;
        void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
        int(*responseFunction) (Parcel &p, void *response, size_t responselen);
    } CommandInfo;

requestNumber: ril request ID
dipspatchFunction: callback function to handle request
responseFunction: callback function to handle response

首先,拿ril request ID與sequence number出來

    status = p.readInt32(&request);
    status = p.readInt32 (&token);

將token與查表取得callback function填入

    pRI->token = token;
    pRI->pCI = &(s_commands[request]);

s_commands內的值定義在ril_commands.h
example:
    {RIL_REQUEST_OPERATOR, dispatchVoid, responseStrings},
    {RIL_REQUEST_RADIO_POWER, dispatchInts, responseVoid},
    {RIL_REQUEST_DTMF, dispatchString, responseVoid},

接下來便會去執行dispatch function

    pRI->pCI->dispatchFunction(p, pRI);

以send SMS為例
{RIL_REQUEST_SEND_SMSdispatchStringsresponseSMS}

ril request ID: RIL_REQUEST_SEND_SMS
dispatch function: dispatchStrings
response function: responseSMS

進入dispatchStrings後Parcel內的data會被取出,最後會呼叫
s_callbacks.onRequest(pRI->pCI->requestNumber, pStrings, datalen, pRI);

onRequest定義在reference-ril.c,此時就會轉成AT command往modem送

** ril request的interface定義在ril.h中


2013年8月20日 星期二

[C] argc、argv

文章出處:http://aluba-pantene.blogspot.tw/2009/12/argc-argv.html

C語言提供一個可以在命令列中將參數引入的功能。也就是說,我們可以在MS-DOS模式下,在執行檔名稱的後面,可以填入需要的參數。

常見的為主程式 main() 有兩個引數 argc 與 argv [1],即函數 main 標題的定義為
main( int argc, char *argv[] )
其作用是 執行該檔 及 所相隨的 option。

如果將一程式 file.c 編譯完之後, 產生一 執行檔 file.exe。 當執行 file.exe 如
c:> file option1 option2 option3
則執行 file.exe 時, argc 的值為 4, argv 則 含 4 個字串,即
argv[0]="file"、 argv[1]="option1"、 argv[2]="option2"、 argv[3]="option3"。
如此一來 程式的設計, 可以有 較多的彈性。


【說明】[4]:
1、argc 與 argv為C語言的關鍵字,是專門用在命令列的參數名。
2、argc是argument count(參數總和)的縮寫,代表包括指令本身的參數個數。系統會自動計算所輸入的參數個數。
3、argv則是argument value的縮寫。代表參數值。也就是使用者在命令列中輸入的字串,每個字串以空白相隔。同時,系統會自動將程式本身的名稱指定給argv[0],再將程式名稱後面所接續的參數依序指定給argv[1]、argv[2]….。


試試看這個例子[3]:
#include ;

int main(int argc, char *argv[])
{
    printf("%d\n",argc);
    while(argc)
    printf("%s\n",argv[--argc]);

   return 0;
}

假設將其編譯為test.exe,
在命令行下,
test hello
得到的輸出結果為:
2
hello
test 

/*
main(int argc, char *argv[]),其中argc是指變量的個數,
本例中指test和hello,即為2,argc至少為1
argv是一個char *的數組,其中存放變量的內容,此處 argv[0]中存放test,argv[1]存放hello
*/



再來個例子,題目為把檔案file1 copy到檔案file2去
檔名為copyFile.c [1][2]
#include
   void main(int argc, char *argv[])
   { 
       char c;
       int toScreen = 1;
       FILE *fpin, *fpout;

       if(argc < 2 || argc > 3)
       { 
          printf("The correct format is: copyFile file1 file2\n");
          exit(1);
       }

       fpin = fopen(argv[1], "r");
       if( !fpin )
       { 
           printf("The file: %s is not found!\n", argv[1]);
           return;
       }
       if(argc == 3)
       { 
          fpout = fopen(argv[2], "w");
          if( !fpout )
          { 
              printf("The file: %s is not found, or no available space!\n", argv[2]);
              return;
          }
          toScreen = 0;
       }

       while( (c=getc(fpin)) != EOF)
       { 
           if( toScreen )
              putchar(c);
           else
              putc(c, fpout);
       }

       fclose(fpin);
       if( !toScreen)
          fclose(fpout);
   }

References:
[1]http://nknucc.nknu.edu.tw/~jwu/c/cpgch10.htm#third
[2]http://tw.myblog.yahoo.com/zentai-chang/article?mid=791&prev=797&next=-1
[3]http://www.lslnet.com/linux/f/docs1/i04/big5124525.htm
[4]http://www.eyny.com/thread-1158039-1-1.html