背景
在某個新基線上移植AT指令,發現有問題,因此收集了這個系列的 文章 作為 這方面的知識補充。
原文作者:laozhuxinlu,本文有刪改。
另外,還參考了:https://www.cnblogs.com/hengfeng/archive/2009/12/05/1617537.html
AT指令在產線中是一類比較重要的問題, 一天沒來得及解決,則會拖延生產的有關進度。
前言
該篇主題主要介紹的是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命令的修改和新增。
這個部分是由modem側(BP側)實現的。
基本上它遵循以下過程:
串口(sio)接收到串口發送過來的字符串--> AT Command Parser --> 產生一個重要的Token結構,它包含了命令名稱,接收到的參數,以及response的buffer--> 之后AT Command ProcessorProcess 通過Token里面的信息查調用相應的AT 命令處理函數 -->處理完成后產生相應的response給TE。
簡單的來說,ATCoP接收串口(Serial Port)處傳來的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命令主要涉及到的一些文件:
The files of interest for adding new AT commands or modifying existing AT commands are:
- dstask.c dstask.h dsat.h
- dsati.h dsatutil.c dsatparm.c
- dsatparm.h dsatact.c dsatact.h
- dsatvend.c dsatvend.h dsatctab.c
- dsatctab.h
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)以及相關的命令參數都被從命令行中解析出來,然后放到一個token data structure中。命令行中的每個命令都產生一個token structure,放到token排隊上等待后續處理,此時一個命令行解析完成。一般添加或修改AT命令時不改動這部分代碼。
2、CommandParsing:根據命令的不同首字符,不同的解析函數解析AT命令后,把解析的信息填充到上一步產生的token data structure中,然后返回結果。如果結果是OK(意味着參數、參數個數以及特殊處理碼special processing code等等都已經存好),此時token data structure已放在隊列中等待AT命令處理器(ATcommand processor)后續處理。命令行中的每個命令都在隊列中放一個token structure。例如,extendedor proprietary AT 命令調用的分析函數是parse_extended_cmd。
3、每個命令產生的token structure被函數dsatcmdp_queue_token放入隊列中。命令解析完成后,調用函數process_at_cmd_line處理隊列中的每個token structure。從隊列中取出並刪除一個命令token structure后,在命令表中搜索該命令。頂層命令表(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()
switch(dsi_callstate){
caseDS_IDLE_CSTATE:
ds_atcop_process_sio_command();
}
/*從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();//發送
}
其中涉及到的Token Struct數據結構:
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;
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命令。
基本命令表
基本命令的格式為:[]
其中或者是單個字母(A-Z),或者是“&”字符接單個字母。
是一個十進制數,可以是一位,也可以是多位。最前面的0會被忽略。默認為0。如果一個不帶的基本命令帶了,則返回TOO MANYPARAMETERS。
寄存器命令表
所有以字母“S”開頭的命令統稱為S寄存器命令,格式如下:
S? S=
S 寄存器命令名由字母“S”接上一個十進制數構成,這個十進制數稱為寄存器序號(parameternumber)。如果寄存器序號不被識別,說明不存在該命令,返回COMMANDNOT SUPPORT。
每個 S寄存器保存一個字符。命令名后面如果接“?”表示是READ命令,返回此S寄存器當前保存的字符的ASCII 碼值,以3 位十進制數表示,位數不足的前面補0;如果接“=”表示是SET命令,將值對應的字符替換此S寄存器保存的字符。
擴展命令和廠商命令表
擴展命令均由“+”開頭,廠商定義的命令也是由一個特殊符號開頭,例如“$”,“%”等。本文中所實現的命令均為擴展命令。
所有的擴展命令和廠商定義命令又可以分為兩類:Actioncommand
和Parameter command
。
actioncommand
action command 指完成某個具體的動作,而不僅僅是與MS 本地的參數打交道的命令,例如AT+CMGS 等。actioncommand 可以帶參數也可以不帶。
Actioncommand 包含EXECUTION 命令和TEST 命令。
(1)EXECUTION命令
EXECUTION 命令格式如下:
- 不帶參數
- 帶 1個參數:
[=]
- 帶多個參數:
[=]
, 中間以“,”分隔表示多個參數。
對於有默認值的參數,可以在命令中省略,此時以默認值代替。
如果所有的參數都省略,則后面的“=”也一並略去。如果不被識別,則表示此命令不存在,返回COMMAND NOTSUPPORT
。可識別的前提下,如果不能帶參數的命令帶了參數,或者參數個數超出定義,則返回TOOMANY PARAMETERS。
(2)TEST命令
TEST 命令格式:=?
如果 MS不能識別,返回COMMAND NOT SUPPORT。如果MS可以識別,且命令是不帶參數的,則返回OK。如果命令帶參數,則先返回各個參數的可取值范圍,最后再加上OK。
parametercommand
parameter command包括與MS本地的參數打交道的命令,這些參數有些會影響到atcioncommand的執行。
parametercommand又分為SET命令、READ命令和TEST命令。
1)SET命令
命令格式為:帶1個參數:[=]
帶多個參數:[=]
,表示多個參數,中間以“,”分隔。
SET命令用於設置參數。對於有默認值的參數,可以在命令中省略,此時以默認值代替。如果所有的參數都省略,則后面的“=”也一並略去。如果不被識別,則表示此命令不存在,返回COMMAND NOTSUPPORT。可識別的前提下,如果不能帶參數的命令 帶了 參數 ,或者 參數 個數 超出 定義, 則返回TOO MANYPARAMETERS。
2)READ命令
命令格式:?
READ 命令用於讀取參數當前值。
3)TEST命令
命令格式:=?
如果 MS不能識別,返回COMMAND NOTSUPPORT。如果MS可以識別,且命令是不帶參數的,則返回OK。如果命令帶參數,則先返回各個參數的可取值范圍,最后再加上OK。
命令表項(主表定義dsati.h)
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;
針對不同的軟件版本可能對應的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命令屬性列表
Attribute | Description | Examples |
---|---|---|
ATTRIB_ NODE | AT命令沒有屬性 | &V |
CONFIG | AT命令具有一個或多個參數 | S3 |
FCLASS2P0 | Fax Class 2.0命令,+FCLASS=2 0否則返回 錯誤代碼,ETSI命令沒有使用 | |
LOCAL_TEST | 測試命令在本地執行。該屬性應該設置在 命令中以后支持,在IS-707-A.3 | |
ALWAYS | 該參數通常給基站,其默認值為制造商指 定的 | |
SIMPLE _DEC | 該參數格式為前面補0的3位十進制數 | &C,E |
STRING | 字符型參數 | +CSCA,+CSMP |
HEX | 16進制格式的參數,不需要0x或是h | |
EXTENDED | 用於擴展語法(命令以+或是$開始),擴展 語法在ITUT V25ter中定義 | +IPR$QCDMG |
READ_ONLY | AT命令沒有任何參數,但返回一個值 | +GM,+GSN |
DOWN LOAD | 參數值(如果與默認值不同)傳送給基站, 以后支持 | S3 |
LIST | AT命令的參數是數字,字母或字符串 | +IPR |
MIXED__PARAM | AT侖令參數是規定值的字符串或是某一范 圍的數字,或是限定長度的字符串。每個 參數都根據類型單獨處理 | +CSCA,+CSMP |
NO_QUOTE | 用於顯示read命令中的字符串參數 | +CSCA,$QCDNSP |
YES_QUOTE | 與NO_QUOTE相反,字符串參數quote顯 示。NO_QUOTE和YES QUOTE | +CPBS,+CSCA |
RESTRICTED | 當ME在限制服務狀態時,AT命令被處理。 沒有設置該屬性的命令不會被處理 | +CGM,+CPAS |
NO_DISPLAY | Read命令的返回參數不顯示 | $QCPDPP |
BROKED_RANGE | AT命令的數字參數沒有范圍限制 | +CGQREQ |
NO _RESET | 重啟命令(Z,&F)不改變參數值 | +CGSMS |
4、compound
1)dsati_cmd_struct*是dsati_cmd_struct結構入口指針,對於包含這個命令表入口指定命令。
2)token_struct_type*是一個定義好的token structure解析器指針,這個token結構包含處理該命令所要求的信息。
3)dsm_item_type*是DSMbuffer的指針,存儲命令響應。如果命令響應超出了一個DSMbuffer的容量,可以將多個DSMbuffer可以鏈接到一起。函數返回類型
應該是表 .3列出值中的一個。
表4.3函數返回類型表
Value | Description |
---|---|
DSAT_OK |
OK返回表示命令執行成功 |
DSAT_ERROR |
ERROR返回表示命令執行失敗 |
DSTA_NO_REP |
返回表示沒有命令響應從DTE發送到AT命令處理器 |
DSAT_CMD_ERP_ RSP |
發送指定命令錯誤響應,存儲在DSM buffer中 |
DSAT_ASYNC_CMD |
表示 命令以異步方式處理, 並在隨后接收來自數據服務器任務分 發的異步命令事件。 當命令處理完成,這些異步事件被標識處理完成,這樣后續的命令將會被處理。 |
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的標准協議文檔進行翻譯理解,並結合網上和個人的一些總結進行整理,希望有幫助。
在ATCoP 中添加AT指令
參考:https://www.cnblogs.com/hengfeng/archive/2009/12/05/1617537.html
基本流程
ATCoP(AT Command Processor)是高通平台上對於AT命令處理的模塊。基本上它遵循以下過程:
串口(sio)接收到串口發送過來的字符串--> AT Command Parser --> 產生一個重要的Token結構,它包含了命令名稱,接收到的參數,以及response的buffer--> 之后AT Command ProcessorProcess 通過Token里面的信息查調用相應的AT 命令處理函數 -->處理完成后產生相應的response給TE。
具體代碼實現
例子1
我們在USB MODEM上需要實現一個對PIN碼校驗的功能,即UIM的PIN功能開啟以后,需要在PC端提示用戶輸入PIN碼,但連續輸入3次錯誤后需輸入PUK解鎖。
我們不需要修改高通默認的處理流程,只要在command table中添加相應的命令、處理函數:
{ "+CPIN", READ_ONLY|RESTRICTED,
SPECIAL_NONE, 0, NULL, NULL,
dsat707_exec_CPIN_cmd, NULL },
其中+CPIN就是我們定義的一個AT命令,dsat707_exec_CPIN_cmd就是對應的處理函數。
我們設想的+CPIN功能是:
A、查詢命令: AT+CPIN?
返回:
READY 表示UIM沒有
UIM PIN 表示UIM開啟了PIN碼功能,用戶插入USB MODEM后需要輸PIN碼
UIM PUK 表示連續3次輸入錯誤PIN,需要輸入PUK解鎖
VERIFIED 表示PIN碼輸入正確
UNBLOCKED 表示PUK輸入正確,解鎖成功
BLOCKED 如果是返回這個,很不幸的告訴你,你的卡GAME OVER!
B、設置命令: AT+CPIN=1234 用戶輸入PIN碼驗證
AT+CPIN=12345678,1234 用戶輸入PUK以及新PIN碼來解鎖
OK,知道了具體的需求,就可以在處理函數dsat707_exec_CPIN_cmd中實現了:
先判讀接收到的+CPIN是什么格式的,是查詢命令,設置命令,還是其他。
我們可以通過Token結構中的tok_ptr->op參數來判斷,如果tok_ptr->op == (NA|QU)表示查詢命令;tok_ptr->op == (NA|EQ|AR)表示設置命令。
在查詢命令中,我們只要通過獲得UIM卡的PIN狀態,把相應的狀態以字符串的形式告訴PC端就可以了。當然可以在這里調用GSDI 的接口來查詢PIN狀態,但是在dsatme_init_me中已經向GSDI注冊了一個回調函數:gsdi_pin_event_cb_func。 一旦GSDI檢測到PIN碼狀態發生改變時,就會調用這個回調函數。
所以我們可以定義一個全局變量,在回調函數中設置這個全局變量,之后在dsat707_exec_CPIN_cmd中判斷這個全局變量,發送相應字符串。
在設置命令中,我們要限制用戶只能在UIM PIN和UIM PUK 這兩種狀態才允許執行。在這里遇到了一個很大的難題,就是我們可以容易的通過GSDI的接口來解PIN和解PUK,但是要如何告知其他的service以及UI我們已經解鎖了呢?? 可以采用一種“笨”辦法,就是不通過直接調用GSDI接口來解PIN,而是模擬出一連串的按鍵,發送給UI解鎖。比如接收到AT+CPIN=1234后,我們可以連續發送1,2,3,4,SEL一共5個KEY EVENT給UI,這樣就如同用戶在手機上按鍵解鎖了。
例子2
來電---ATCoP中對同步事件的處理
同步事件的處理,我的理解就是ATCoP在其他task注冊了一個同步事件的回調函數,一旦同步事件發生時,其他task就會調用這個回調函數來通知ATCoP調用相應的同步事件處理函數。一個典型的例子就是來電過程的處理:
1、dsatcmif_init_cmif -> cmif_register_cb_func---向CM task注冊來電,或者對方應答回調
/* Register the CM call event callback function */
cm_status = cm_mm_client_call_reg (
dsatcm_client_id,
cmif_call_event_cb_func,
CM_CLIENT_EVENT_REG,
CM_CALL_EVENT_INCOM,
CM_CALL_EVENT_CONNECT,
NULL
);
2、一旦有來電消息,cmif_call_event_cb_func構造事件的命令放入cmd buffer
cmd_buf->hdr.cmd_id = DS_AT_CM_CALL_INFO_CMD;
cmd_buf->cmd.call_event.event_info = *event_ptr;
cmd_buf->cmd.call_event.event = event;
ds_put_cmd(cmd_buf);---->調用(void)rex_set_sigs( &ds_tcb, DS_CMD_Q_SIG );通知ds_task來處理
3、在 ds_task 中調用 dsi_process_cmds()處理命令
4、 調用dsat_process_async_cmd( cmd_ptr );
- 查找表async_event_table event_handler_func_ptr = async_event_table[i].event_handler[operating_mode];
- 調用相應的處理函數result_code = event_handler_func_ptr( dsatcmdp_at_state, cmd_ptr );
- 這里調用dsatcmif_cm_call_event_handler
- 調用來電處理函數 ,在這個函數里面會主動發送"Ring"字符串來通知PC端由來電了。
result = dsatvoice_call_event_incom(&cmd_ptr->cmd.call_event.event_info);