At Commands:從白痴到大師的修煉歷程(三)

At Commands:從白痴到大師的修煉歷程(三)
AT Command流程分析之AtCop解析

   

這篇部落格是起步於At
Commands學習系列
的第三部分,該篇主題主要介紹的是ATCommandProcessor,那麼,什麼是ATCommandProcessor?懂的人自然就懂,不懂的人,那就看下去吧,總是會懂的!

ATCoP是什麼?ATCommandProcessor,是高通AMSS(modem)software下的一套對AT命令的具體實現模組,也只有真正弄懂ATCoP,才能真正的瞭解AT命令有關軟體的實現。

下面,我將就:

·AtCoP具體功能

·ATCoP實現架構

·ATCoP處理流程

·AT命令解析(ATCommand Parser

·AT命令表(ATCommand Table

這四個方面來做具體介紹,學習完塊,便能結合具體的例項具體的去分析整個的AT命令實現,也只有在深入瞭解ATCoP的基礎上,才能實現對ATCommand的修改和新增。

AtCoP具體功能:

ATCoP,ATCommand processor,AT命令處理器。是對AT命令具體軟體實現的模組,通過ATCoP,我們可以實現對AT命令的修改和新增。

簡單的來說,ATCoP接收串列埠(SerialPort)處傳來的ATCommand,進行解析(Parse),根據解析的結果到ATCommand
Tables中尋找相應的表項,若匹配,則執行對應的處理函式,處理完以後response其對應的返回資料到串列埠。

目前,Qualcomm(高通)DMSS採用IS-707AT
Command Set作為它的DataServices的命令集。

下面列舉出與ATCoP相關的一些主要的資源目錄:

filename

Description

Dsat.h

ATCoP外部模組使用的定義,函式和資料結構

Dsat.h

ATCoP外部模組使用的定義,函式和資料結構

Dsati.h

ATCoP內部使用的定義,函式和資料結構

Dsatprep.c

接收自串列埠裝置的資料的預處理

Dsatpar.c

AT命令解析器,將命令列的AT命令解析到token結構中

Dsatcmdp.c

AT命令處理器,查詢token結構中的包含的命令並從命令列表中呼叫相應的命令處理函式處理命令

Dsatrsp.c

產生AT命令響應和格式化

Dsatutil.c

產生AT命令處理器

Dsatparm.c

通用AT引數型別命令過程

Dsatarm.h

ATCoP內部使用的通用AT引數型別命令處理定義,函式和資料結構

Dsatact.c

通用ATactive type命令處理函式

Dsatact.h

通用AT動作型別命令處理的定義、函式和資料結構,供ATCoP內部模組使用

Dsatvend.c

通用AT指定提供商型別命令處理

Dsatvend.h

通用AT指定提供商型別命令處理的定義、函式和資料結構,供ATCoP內部模組使用

Dsatctab.c

通用AT命令表

Dsatctab.h

通用AT命令表定義、函式和資料結構,供ATCoP內部模組使用

Dsatcmif.c

通用呼叫管理介面

Dsatcmif.h

通用呼叫管理介面定義、函式和資料結構,供ATCoP內部模組使用

Dsatvoice.c

通用語音呼叫處理控制

Dsatvoice.h

通用語音呼叫處理控制的定義、函式和資料結構,供ATCoP內部模組使用

Dsatetsicall.c

ETSI呼叫控制命令處理

Dsatetsicall.h

ETSI呼叫控制命令處理的定義、函式和資料結構,供ATCoP內部模組使用

Dsatetsicmif.c

ETSI命令呼叫管理介面

Dsatetsicmif.h

ETSI命令呼叫管理介面的定義、函式和資料結構,供ATCoP內部模組使用

Dsatetsipkt.c

ETSI包資料命令處理

Dsatetsipkt.h

ETSI包資料命令處理的定義、函式和資料結構,供ATCoP內部模組使用

Dsatetsime.c

ETSI移動裝置命令處理

Dsatetsime.h

ETSI移動裝置命令處理的定義、函式和資料結構,供ATCoP內部模組使用

Dsatetsismsc.c

ETSI短訊息服務命令處理

Dsatetsismsa.c

ETSI短訊息服務非同步事件處理

Dsatetsismsu.c

ETSI短訊息服務命令處理實體

Dsatetsisms.h

ETSI短訊息服務命令處理的定義、函式和資料結構,供ATCoP內部模組使用

Dsatetsismsi.h

ETSI短訊息服務命令處理的定義、函式和資料結構,供ATCoP內部模組的短訊息服務單元使用

Dsatetsictab.c

ETSIAT命令表

Dsatetsictab.h

ETSIAT命令表的定義、函式和資料結構,供ATCoP內部模組使用

Dsatetsitgt.c

頂層AT命令表,命令表指標陣列,ETSI指定目標的命令表,同步事件處理表。定義ETSI目標支援的AT命令集

Dsatgsmfax.c

GSMfax命令處理

Dsatgsmfax.h

GSMfax命令處理的定義、函式和資料結構,供ATCoP內部模組使用

資料服務任務原始檔列表:

filename

Description

Dstask.h

資料服務任務的外部或內部模組使用的定義,函式和資料結構

Dsatsk.c

資料服務任務和頂層分發

對修改或者新增一個AT命令主要涉及到的一些檔案:

ATCoP實現架構:

首先我們來看一張圖:

所以說ATCoP的基本架構主要有一下幾個部分:

  • SIOData Preprocessor(SIO資料預處理模組)

  • ATCommand Parser(AT命令解析器)

  • ATCommand Processor(AT命令處理器)

  • ATCommand Response Generator(AT命令響應產生器)

  • ATCommand Tables and Command Processing Functionality(AT命令表及命令處理功能模組)

1.通過串列埠裝置(SIO)接收的AT命令資料,首先由SIO資料預處理,產生一個null-terminated命令列並由DS分發給AT命令解析器。

2.產生的null-terminated命令列由AT命令解析器解析,解析器為每個要解析的命令產生一個token結構,並送到處理佇列由AT命令處理器處理。在AT命令處理器被呼叫前,解析器將每個命令的token結構放入佇列中。

3.AT命令處理器完成對每個token結構進行表查詢,同時將該token結構從佇列中移除。如果查詢到,對應的處理函式被呼叫處理該命令;AT命令在命令表中定義,每個命令表入口包含對應命令執行函式的指標。

4.AT命令響應產生器將命令響應資料格式化,產生結果編碼,並將響應資料傳送給DTE。

5.ATCOP每次處理一條AT命令列命令,如果任何命令列的命令產生一個錯誤,在錯誤前就會產生命令處理的響應,同時產生一個錯誤程式碼,不再對該命令進行後續處理。

下面圖同樣展示的是ATCoP的實現框架,從USB接收到AT命令,到初始化建立SIO,再到命令的實際處理到返回,可詳讀下面這張圖。

ATCoP處理流程:

其實在ATCoP的實現架構就已經簡單介紹到了ATCoP對AT命令的處理流程,在這我們將對其進行更加具體的詳解。

我們來看下面一張圖:

從這張圖中我們可以看出來,ATCoP處理控制流大致可以分有三個步:

·Initial Parsing:初始化解析

·CommandParsing:命令解析

·Command Execution:命令執行處理(包括返回結果)

  1. InitialParsing:函式dsat_process_sio_command去掉命令列中的”AT”字首,然後把以NULL結尾的命令列傳給函式dsat_process_cmd_line來進行後續的分析和處理。dsatpar_parse_cmd_line函式完成對命令列的解析,檢查每個AT命令的首字元然後根據AT命令的型別呼叫相應的解析函式。每個AT命令名(包括首符號,如,$QCDMG)以及相關的命令引數都被從命令列中解析出來,然後放到一個tokendatastructure中。命令列中的每個命令都產生一個tokenstructure,放到token排隊上等待後續處理,此時一個命令列解析完成。一般新增或修改AT命令時不改動這部分程式碼。

  2. CommandParsing:根據命令的不同首字元,不同的解析函式解析AT命令後,把解析的資訊填充到上一步產生的tokendatastructure中,然後返回結果。如果結果是OK(意味著引數、引數個數以及特殊處理碼specialprocessingcode等等都已經存好),此時tokendata
    structure已放在佇列中等待AT命令處理器(ATcommand
    processor)後續處理。命令列中的每個命令都在佇列中放一個tokenstructure。例如,extendedor
    proprietary AT 命令呼叫的分析函式是parse_extended_cmd。

  3. 每個命令產生的tokenstructure被函式dsatcmdp_queue_token放入佇列中。命令解析完成後,呼叫函式process_at_cmd_line處理佇列中的每個tokenstructure。從佇列中取出並刪除一個命令tokenstructure後,在命令表中搜尋該命令。頂層命令表(toplevel
    command table)在檔案dsatetsitgt.c中。頂層命令表又指向檔案dsatctab.c和dsatetsictab.c中的多個命令表,這些表定義了所支援的AT命令集。

如果在表中查詢到該命令,呼叫表中對應的處理函式執行該命令。命令執行後如果有返回資料時,返回的響應資料在函式dsat_fmt_response中格式化。每個token結構都進行這樣的處理。最後呼叫函式dsatrsp_send_response把命令響應送到DTE。


注:對於非同步AT命令處理流程與正常AT命令略有不同,在命令預處理、命令解析過程都是一樣的,在命令處理過程中(process_at_cmd_line),如果命令處理函式返回DSAT_OK,說明命令處理完成呼叫dsat_fmt_response函式格式化響應資料併傳送,正常的命令處理流程;如果命令處理函式返回DSAT_ASYNC_CMD說明當
前 命 令 是 異 步 命 令 , 此 時 函 數process_at_cmd_line 設定 變 量dsatcmdp_processing_async_cmd= TRUE,表示當前正在處理非同步命令,然後返回,不再進行後續處理,直到該命令處理完成,函式返回DSAT_OK(未必一定是DSAT_OK,當返回不是DSAT_ASYNC_CMD和DSAT_ASYNC_EVENT時,說明非同步命令/事件處理完成)。當DS收到非同步事件經任務分發器,再次呼叫dsat_process_async_cmd函式,在該函式中通過查詢非同步事件表async_event_table,呼叫相應的事件處理函式繼續處理,如果事件處理函式返回值不是DSAT_ASYNC_CMD或DSAT_ASYNC_EVENT,說明非同步事件處理完成,呼叫process_at_cmd_line繼續處理命令列的命令。

下面我們可以通過一個流程圖更加直觀的瞭解ATCoP的處理流程:

基本遵循過程:

SIOData Preprocessor接收串列埠傳送過來的字串(這裡是AT Command),並向DS TASK傳送訊號要求其處理;DS
TASK 知曉並獲得控制權後,由AT Command Parser解析AT Command,將得到的結果存入相應的token結構中(包含了命令名稱,接收到的引數,以及response的buffer);AT
Command Processor到AT Command Tables匹配相應的表項;AT Command Response Generator根據匹配的結果呼叫對應的Command Processing Function進行處理;處理完成後產生相應的response給
TE。

AT命令解析(ATCommand Parser):

ATCommand Parser對命令列進行解析時,將解析的結果存到token中,並在下一步到Parse
Table中進行匹配。

下面就以程式碼流程具體展示:

DS_AUTODETECT_SRVC_MODE模式下,串列埠檢測到A字元,則傳送DS_1ST_SIO_RX_SIG給DSTASK

    DSTASK呼叫dsi_mgr()進行分發處理

    ds_process_rxbuf_event()

<span style="font-size:14px;">
switch(dsi_callstate){
caseDS_IDLE_CSTATE:
ds_atcop_process_sio_command();
}</span>

<span style="font-size:14px;">
/*從watermark中取出data嘗試建立一個命令列*/
while(cc!=NULL)
{
cc = cce & 0x7F		//只使用高7位
switch (at_state)
{
case HUNT:  
if (UPCASE (cc) == 'A')
//     轉到FOUND_A狀態
break;
case FOUND_A:
if (UPCASE (cc) == '/')
//從buf取出上個AT命令來執行(ds_atcop()),執行完畢後轉到HUNT狀態
else if (UPCASE (cc) == 'T')
//轉到CAT狀態
else if (UPCASE (cc) != 'A')
//轉到HUNT狀態
     break;
case CAT:
 if (cc != ds_atcop_s3_val) // if not EOL
{
if (cc != ds_atcop_s5_val) // if not backspace
{
if OVERFLOW
 //轉到ERROR狀態
else if (cc >= 0x20 && cc <= 0x7E)
//fill buffer;
} // if backspace
else
// remove the most immediate character from the buffer
}
else 
if EOL
        //對命令列進行處理(ds_atcop()),處理完畢後轉到HUNT狀態
break;
case ERROR:
       //執行相應出錯處理,處理完畢後轉到HUNT狀態
break;
}
}
 //ds_atcop() 對命令列進行處理
switch (UPCASE (*curr_char_ptr)){
case ' ':     /*  Extended format specified in IS-99  */
case '$':     /*  Extended format proprietary command */
//對命令列進行解析
curr_char_ptr = ds_atcop_parse_ex (curr_char_ptr, &tok);
if SUCCEED
 //根據解析的結果到parse table匹配,進行相應處理
ds_atcop_exec_param_cmd ();
 //若匹配不到,則強制解析
if (ds_atcop_result == DS_ATCOP_DO_HARD_PARSE)
ds_atcop_hard_to_parse();
break;
}
//If not originating or answering a call
  ds_atcop_fmt_response(); //generate a final result code
if (ds_atcop_result == DS_ATCOP_CXT_ORIG){
 ds_atcop_discard_results();
}
else if (mode == DS_ATCOP_CMD){
  ds_atcop_send_results();//傳送
}</span>

    其中涉及到的Token Struct資料結構:

<span style="font-size:14px;">typedef struct 
{
byte working_at_line[MAX_LINE_SIZE]; // Stores command lines to be processed. Each line
// is referenced by a line number
byte *name;                       // The name of the AT command
unsigned int op;                    // Syntax flags. Can be one of four valid values (NA, 
// EQ, QU, AR) or an invalid value
byte * arg[MAX_ARG];             // AT command arguments
unsigned int args_found;             // Keeps track of the number of AT command 
//arguments found
} tokens_struct_type;</span>

AT命令表(ATCommand Table):

AT命令的處理是由命令表驅動的,ATCOP實現的命令表是一個分級的表結構,主要分為:

·主表(master table)

·子表(sub table)

·命令表(command table)。

其中主表是一個二維的陣列,陣列的行表示AT命令的分類,分為:

·基本AT命令(basic_table)

·暫存器AT命令(sreg_table)

·擴充套件AT命令(extended_table)

·廠商AT命令(vendor_table)

四大類;陣列的列表示是ETSI模式還是其它模式的AT命令。

·AT命令表分類具體介紹:

1.基本命令表

基本命令的格式為:<command>[<number>]

其中<command>或者是單個字母(A-Z),或者是“&”字元接單個字母。

<number>是一個十進位制數,可以是一位,也可以是多位。<number>最前面的0會被忽略。預設<number>為0。如果一個不帶<number>的基本命令帶了<number>,則返回TOO
MANYPARAMETERS。

2.暫存器命令表

所有以字母“S”開頭的命令統稱為S暫存器命令,格式如下:

S<parameter number>? S<parameternumber>=<value>

S 暫存器命令名由字母“S”接上一個十進位制數構成,這個十進位制數稱為暫存器序號(parameternumber)。如果暫存器序號不被識別,說明不存在該命令,返回COMMANDNOT SUPPORT。

每個 S暫存器儲存一個字元。命令名後面如果接“?”表示是READ命令,返回此S暫存器當前儲存的字元的ASCII
碼值,以3 位十進位制數表示,位數不足的前面補0;如果接“=”表示是SET命令,將<value>值對應的字元替換此S暫存器儲存的字元。

3.擴充套件命令表和廠商提供的命令表

擴充套件命令均由“ ”開頭,廠商定義的命令也是由一個特殊符號開頭,例如“$”,“%”等。本文中所實現的命令均為擴充套件命令。所有的擴充套件命令和廠商定義命令又可以分為兩類:Actioncommand和Parameter
command。

1)actioncommand

action command
指完成某個具體的動作,而不僅僅是與MS 本地的引數打交道的命令,例如AT CMGS
等。actioncommand 可以帶引數也可以不帶。Actioncommand
包含EXECUTION 命令和TEST 命令。

(1)EXECUTION命令

EXECUTION 命令格式如下:

不帶引數:<name>

帶 1個引數:<name>[=<value>]

帶多個引數:<name>[=<compound_value>]

<compound_value>表示多個引數,中間以“,”分隔。對於有預設值的引數,可以在命令中省略,此時以預設值代替。

如果所有的引數都省略,則<name>後面的“=”也一併略去。如果<name>不被識別,則表示此命令不存在,返回COMMAND
NOTSUPPORT。<name>可識別的前提下,如果不能帶引數的命令帶了引數,或者引數個數超出定義,則返回TOOMANY PARAMETERS。

(2)TEST命令

TEST 命令格式:<name>=?

如果 MS不能識別<name>,返回COMMAND NOT SUPPORT。如果MS可以識別<name>,且命令是不帶引數的,則返回OK。如果命令帶引數,則先返回各個引數的可取值範圍,最後再加上OK。

2)parametercommand

parameter command包括與MS本地的引數打交道的命令,這些引數有些會影響到atcioncommand的執行。parametercommand又分為SET命令、READ命令和TEST命令。

(1)SET命令

命令格式為:帶1個引數:<name>[=<value>]

帶多個引數:<name>[=<compound_value>]

SET命令用於設定引數。<compound_value>表示多個引數,中間以“,”分隔。對於有預設值的引數,可以在命令中省略,此時以預設值代替。如果所有的引數都省略,則<name>後面的“=”也一併略去。如果<name>不被識別,則表示此命令不存在,返回COMMAND
NOTSUPPORT。<name>可識別的前提下,如果不能帶引數的命令 帶了 引數 ,或者 引數 個數 超出 定義, 則返回TOO MANYPARAMETERS。

(2)READ命令

命令格式:<name>?

READ 命令用於讀取引數當前值。

(3)TEST命令

命令格式:<name>=?

如果 MS不能識別<name>,返回COMMAND
NOTSUPPORT。如果MS可以識別<name>,且命令是不帶引數的,則返回OK。如果命令帶引數,則先返回各個引數的可取值範圍,最後再加上OK。

·命令表項(主表定義dsati.h)


<span style="font-size:14px;">typedef struct dsati_cmd_struct
{
byte name[20];           // AT cmd 的名字,包含" ", "$" 等
uint32 attrib;             // AT cmd 的屬性
byte special;             // AT cmd 的 special processing code
byte compound;          // 傳遞的引數個數(若引數為字串,則是它的最大長度)
const void *val_ptr;       // 指向引數的指標
const void *def_lim_ptr;   // 定義了引數的預設值以及取值範圍
dsat_result_enum_type (*proc_func)( dsat_mode_enum_type,
const struct dsati_cmd_struct*,
const tokens_struct_type*,
dsm_item_type* );
boolean (*abort_func)( const struct dsati_cmd_struct* );
} dsati_cmd_type;</span>

針對不同的軟體版本可能對應的dsati_cmd_type定義略有差別。

詳解:

1.name

AT命令名,包括需要處理的 ,$,&和終止的NULL。如 IPR,$QCDMG,

S6,&C,Z。

2.attitude

32位的掩碼,用來指定單個或多個命令屬性。表4.1列出了AT命令的所有屬性,後面給出了具有該屬性的命令。

3.special

如果有需要,就指定處理編碼,否則就是SPECIAL_NONE,指定處理編碼定義在dsati.h。只是用在與外部軟體的相容性時。

表1 AT命令屬性列表

4.compound

2)dsati_cmd_struct*是dsati_cmd_struct結構入口指標,對於包含這個命令表入口指定命令。

3)token_struct_type*是一個定義好的tokenstructure解析器指標,這個token結構包含處理該命令所要求的資訊。

4)dsm_item_type*是DSMbuffer的指標,儲存命令響應。如果命令響應超出了一個DSMbuffer的容量,可以將多個DSMbuffer可以連結到一起。函式返回型別

應該是表 .3列出值中的一個。

表4.3函式返回型別表

8.abortfunction ptr

函式指標通過命令表入口呼叫定義的abort命令,函式指標值不是NULL表示命令表入口定義的命令是可以abort的。函式指標引數是:dsati_cmd_struct*是dsati_cmd_struct結構入口指標,用於指示包含該命令表入口的指定命令。函式返回型別是一個Boolen表示:如果值是TRUE,表示資料呼叫可以通過DsmgrAbort,否則不需要任何動作。

9.dflm_type

定義AT命令中數字引數的最大值和最小值引數,這裡的數字引數一定是連續的取值型別。如果引數取值為{0,1,255}這種引數應該設定為list型別。Default_v為預設值,lower和upper為最小和最大取值。

10.def_list_type

用於定義LIST型別的引數取值範圍,其中:

Default_v代表引數的預設值,它是指向list_v陣列的指標。

List_v是一個8-byte字串的陣列指標,代表該引數允許的所有值,陣列的最後一項必須是NULL來終止引數列表。AT命令處理器完成該陣列的字串匹配,以決定引數值是否在有效的範圍內。

List_t是一個字串指標,逐字返回測試命令的響應。

AT命令如果含有多個引數,每個引數都關聯於def_list_type結構。

11.mixed_def_s_type和dsat_mixed_param_val_type

Mixed_def_s_type用於儲存AT命令中混合引數的預設值和可能值,混合引數型別表示AT命令的引數可以是不同型別的,其允許值範圍也是個集合,如果是數值型別需要指定在某一範圍內,如果是字串型別則限定長度。如果命令有N個不同型別引數,預設的可能值範圍包含一個長度為N的mixed_def_s_type陣列,型別引數i是陣列下標,如mixed_argus[]定義為mixed_argus[i].attrib。如果AT命令有N種不同型別引數,引數的當前值包含在長度為N的dsat_mixed_param_val_type陣列中。該陣列用於聯絡mixed_def_s_type陣列Dsat_mixed_param_val_type陣列下標為i的元素,在mixed_def_s_type陣列對應i分量為該AT命令的預設和可能的範圍值。

其實,針對於ATCoP的學習是比較混亂的,因為本身的內容就比較混雜,如果不是對整個架構比較清楚的話,很難學得清楚,並且就後面所要展開的AT Commands的自定義實現是在這一章的基礎上實現的。在這我主要也是就ATCoP的標準協議文件進行翻譯理解,並結合網上和個人的一些總結進行整理,希望有幫助。

就這一塊的理解問題應該是挺多的,歡迎大家提出來一些探討探討!

接下來的學習:AT
Command流程分析之ATFWD解析