2015年1月21日 星期三

bluedroid源碼分析之ACL包發送和接收(二) (轉)

原 http://stackvoid.com/ACL-Send-Recv-Data_2/

上一節講了數據流入口,本文分析L2CAP的處理函數。

L2CAP層的處理

我們的音樂數據,通過 L2CAP 入口函數 l2c_data_write 的層層“考驗”,已經順利進入到 L2CAP 裡了,下面我們來看看 L2CAP 層具體是怎麼處理數據的。
首先我們進入了 L2CAP 層的狀態機。
 1 void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
 2 {
 3     switch (p_ccb->chnl_state)
 4     {
 5     case CST_CLOSED:
 6         l2c_csm_closed (p_ccb, event, p_data);
 7         break;
 8 
 9     case CST_ORIG_W4_SEC_COMP:
10         l2c_csm_orig_w4_sec_comp (p_ccb, event, p_data);
11         break;
12 
13     case CST_TERM_W4_SEC_COMP:
14         l2c_csm_term_w4_sec_comp (p_ccb, event, p_data);
15         break;
16 
17     case CST_W4_L2CAP_CONNECT_RSP:
18         l2c_csm_w4_l2cap_connect_rsp (p_ccb, event, p_data);
19         break;
20 
21     case CST_W4_L2CA_CONNECT_RSP:
22         l2c_csm_w4_l2ca_connect_rsp (p_ccb, event, p_data);
23         break;
24 
25     case CST_CONFIG:
26         l2c_csm_config (p_ccb, event, p_data);
27         break;
28 
29     case CST_OPEN:
30         l2c_csm_open (p_ccb, event, p_data);
31         break;
32 
33     case CST_W4_L2CAP_DISCONNECT_RSP:
34         l2c_csm_w4_l2cap_disconnect_rsp (p_ccb, event, p_data);
35         break;
36 
37     case CST_W4_L2CA_DISCONNECT_RSP:
38         l2c_csm_w4_l2ca_disconnect_rsp (p_ccb, event, p_data);
39         break;
40 
41     default:
42         break;
43     }
44 }
具體的 Channel 狀態信息如下
 1 typedef enum
 2 {
 3     CST_CLOSED,                           /* Channel is in clodes state           */
 4     CST_ORIG_W4_SEC_COMP,                 /* Originator waits security clearence  */
 5     CST_TERM_W4_SEC_COMP,                 /* Acceptor waits security clearence    */
 6     CST_W4_L2CAP_CONNECT_RSP,             /* Waiting for peer conenct response    */
 7     CST_W4_L2CA_CONNECT_RSP,              /* Waiting for upper layer connect rsp  */
 8     CST_CONFIG,                           /* Negotiating configuration            */
 9     CST_OPEN,                             /* Data transfer state                  */
10     CST_W4_L2CAP_DISCONNECT_RSP,          /* Waiting for peer disconnect rsp      */
11     CST_W4_L2CA_DISCONNECT_RSP            /* Waiting for upper layer disc rsp     */
12 } tL2C_CHNL_STATE;
l2c_csm_execute 函數通過 p_ccb 中的 chnl_state 欄位,來確定接下來數據包的走向。如下圖所示:
  1. CST_CLOSED 狀態:這個 case 處理 Channel 處於 CLOSED 狀態的事件。這個狀態僅僅存在於 L2CAP 的 Link 初始建立過程中。
  2. 如果發現事件是 L2CEVT_LP_DISCONNECT_IND,則當前 Link 已經斷開,則釋放當前 Channel的 ccb;
  3. 若事件是 L2CEVT_LP_CONNECT_CFM,則置 p_ccb->chnl_state 為 CST_ORIG_W4_SEC_COMP 狀態,下面會接着介紹這個。
  4. 如果是 L2CEVT_LP_CONNECT_CFM_NEG 則說明當前 Link 失敗,
  5. 如果是 L2CEVT_SEC_COMP 則說明 Security 已經清除成功。
  6. 若是 L2CEVT_L2CA_CONNECT_REQ 則說明 來自上層的 connect 請求,如果當前處於 sniff 狀態,要先取消 sniff。
  7. L2CEVT_SEC_COMP_NEG 說明 Security 失敗,清除當前 CCB,返回
  8. L2CEVT_L2CAP_CONNECT_REQ 說明是 Peer connect request,既然成功連接了,結束掉 timer
  9. L2CEVT_L2CA_DATA_WRITE,如果我們的數據從上層經過這裡,並且是 CST_CLOSED,由於當前的 Channel 沒有建立好,並且上層已經將數據丟給L2CAP了,只能將數據丟棄處理了(數據流不能逆向)。
  10. L2CEVT_L2CA_DISCONNECT_REQ,上層想斷開連結,會使用這個 Event來處理。
  11. CST_ORIG_W4_SEC_COMP 狀態:Originator(我的理解是 發起 link 建立的應用)等待 security 的間隙,這個間隙需要處理的事件。跟 CST_CLOSED 差不多,源碼很容易讀,這裡不再次做分析。注意,如果是 L2CEVT_SEC_COMP 事件(跟安全相關),會把 p_ccb->chnl_state 置為 CST_W4_L2CAP_CONNECT_RSP
  12. CST_TERM_W4_SEC_COMP狀態:Acceptor(接收者)等待 security 的間隙,源碼易讀,不再詳細展開分析,注意一個事件 L2CEVT_SEC_COMP 的處理,會將 p_ccb->chnl_state 置為 CST_W4_L2CA_CONNECT_RSP
  13. CST_W4_L2CAP_CONNECT_RSP: 經過 2 或 3 這個關於安全連結的步驟,當然要等待 Peer connect的回應(Response)。分為 獲取 peer connect的 confirm、pending、rejected connection等信息,作進一步的判斷。
  14. CST_CONFIG:商討配置的過程。
  15. CST_OPEN:Channel 處於 OPEN 狀態,我們可以發送上層傳過來的數據,我們的音樂數據就是從這個 case 裡發出去的。
  16. CST_W4_L2CAP_DISCONNECT_RSP:等待 peer(對方設備)斷開連結的 Response
  17. CST_W4_L2CA_DISCONNECT_RSP:等待上層 disc rsp
  18. 分析了這麼一大堆,我們的聽音樂那那筆數據包,走的是 CST_OPEN 這個case,因為別的幾個 case 在link 建立完成時就走完了。
    我們的音樂數據包走的是 CST_OPEN 這個 case,這個 case 調用的是 l2c_csm_open 這個函數,接下來我們繼續分析這個函數。
    我們的音樂數據包在函數 l2c_csm_open 中流轉,經過各種選擇和判斷,最後走的是 L2CEVT_L2CA_DATA_WRITE 這個 case。這個 case 調用了 l2c_enqueue_peer_data 讓數據進入到當前 ccb 的 xmit_hold_q 隊列中,暫存此數據包。l2c_link_check_send_pkts 這個函數發送數據包。下面我們會繼續分析這兩個函數。
      1 //l2c_csm_open 處理 Channel 處於 OPEN 狀態下的各種 Event
      2 static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
      3 {
      4     UINT16                  local_cid = p_ccb->local_cid;
      5     tL2CAP_CFG_INFO         *p_cfg;
      6     tL2C_CHNL_STATE         tempstate;
      7     UINT8                   tempcfgdone;
      8     UINT8                   cfg_result;
      9 
     10 #if (BT_TRACE_VERBOSE == TRUE)
     11     L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x  st: OPEN  evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
     12 #else
     13     L2CAP_TRACE_EVENT1 ("L2CAP - st: OPEN evt: %d", event);
     14 #endif
     15 
     16 #if (L2CAP_UCD_INCLUDED == TRUE) //默認 UCD 是關閉的
     17     if ( local_cid == L2CAP_CONNECTIONLESS_CID )
     18     {
     19         /* check if this event can be processed by UCD */
     20         if ( l2c_ucd_process_event (p_ccb, event, p_data) )
     21         {
     22             /* The event is processed by UCD state machine */
     23             return;
     24         }
     25     }
     26 #endif
     27 
     28     switch (event)
     29     {
     30     case L2CEVT_LP_DISCONNECT_IND:  //Link 都斷開連接了,自然 Channel也沒有存在的必要了,各種清除 CCB 的工作
     31         L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x  No Conf Needed", p_ccb->local_cid);
     32         l2cu_release_ccb (p_ccb);//釋放 當前的 CCB 
     33         if (p_ccb->p_rcb)
     34             (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(local_cid, FALSE);
     35         break;
     36 
     37     case L2CEVT_LP_QOS_VIOLATION_IND:               /* QOS violation         */
     38         /* Tell upper layer. If service guaranteed, then clear the channel   */
     39         if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)
     40             (*p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)(p_ccb->p_lcb->remote_bd_addr);
     41         break;
     42 
     43     case L2CEVT_L2CAP_CONFIG_REQ:                  /* Peer config request   */
     44         p_cfg = (tL2CAP_CFG_INFO *)p_data;
     45 
     46         tempstate = p_ccb->chnl_state;
     47         tempcfgdone = p_ccb->config_done;
     48         p_ccb->chnl_state = CST_CONFIG; //如果數據流中的數據是 L2CEVT_L2CAP_CONFIG_REQ,當然要轉到 CST_CONFIG中繼續處理
     49         p_ccb->config_done &= ~CFG_DONE_MASK;
     50         //啟動一個 timer ,一段時間後,查看 cfg 的狀態
     51         //如果配置處於 L2CAP_PEER_CFG_UNACCEPTABLE,繼續嘗試配置
     52         //如果配置處於斷開狀態,那當前 Channel 直接斷開連接。
     53         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
     54 
     55         if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK)
     56         {
     57             (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg);
     58         }
     59 
     60         /* Error in config parameters: reset state and config flag */
     61         else if (cfg_result == L2CAP_PEER_CFG_UNACCEPTABLE)
     62         {
     63             btu_stop_timer(&p_ccb->timer_entry);
     64             p_ccb->chnl_state = tempstate;
     65             p_ccb->config_done = tempcfgdone;
     66             l2cu_send_peer_config_rsp (p_ccb, p_cfg);
     67         }
     68         else    /* L2CAP_PEER_CFG_DISCONNECT */
     69         {
     70             /* Disconnect if channels are incompatible
     71              * Note this should not occur if reconfigure
     72              * since this should have never passed original config.
     73              */
     74             l2cu_disconnect_chnl (p_ccb);
     75         }
     76         break;
     77 
     78     case L2CEVT_L2CAP_DISCONNECT_REQ:                  /* Peer disconnected request */
     79 // btla-specific ++
     80         /* Make sure we are not in sniff mode */
     81 #if BTM_PWR_MGR_INCLUDED == TRUE
     82         {
     83             tBTM_PM_PWR_MD settings;
     84             settings.mode = BTM_PM_MD_ACTIVE;
     85             BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
     86         }
     87 #else
     88         BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
     89 #endif
     90 // btla-specific --
     91 
     92         p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP; //Peer 發送 Disconnect,我們要對此發 Response
     93         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
     94         L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x  Conf Needed", p_ccb->local_cid);
     95         (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE);
     96         break;
     97 
     98     case L2CEVT_L2CAP_DATA:                         /* Peer data packet rcvd    */
     99         //收到 Peer 傳來的數據,當然要把這個數據通過回調送到上層應用去
    100         //pL2CA_DataInd_Cb 中定義了回調,交給上層處理收到的數據
    101         (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data);
    102         break;
    103 
    104     case L2CEVT_L2CA_DISCONNECT_REQ:                 /* Upper wants to disconnect */
    105         /* Make sure we are not in sniff mode */
    106 #if BTM_PWR_MGR_INCLUDED == TRUE
    107         {
    108             tBTM_PM_PWR_MD settings;
    109             settings.mode = BTM_PM_MD_ACTIVE;
    110             BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
    111         }
    112 #else
    113         BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
    114 #endif
    115 
    116         l2cu_send_peer_disc_req (p_ccb);
    117         p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
    118         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
    119         break;
    120 
    121     case L2CEVT_L2CA_DATA_WRITE:                    /* Upper layer data to send */   //mike mark l2c
    122         //上層將數據發送給下層
    123         //我們的音樂數據就是走這個 case(為什麼?看整個函數的參數就明白了)
    124         //首先將數據入隊,下面會展開分析這個函數
    125         l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data);
    126         //最終調用 l2c_link_check_send_pkts 來發送我們的音樂數據包
    127         l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
    128         break;
    129 
    130     case L2CEVT_L2CA_CONFIG_REQ:                   /* Upper layer config req   */
    131         p_ccb->chnl_state = CST_CONFIG;
    132         p_ccb->config_done &= ~CFG_DONE_MASK;
    133         l2cu_process_our_cfg_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
    134         l2cu_send_peer_config_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
    135         btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
    136         break;
    137 
    138     case L2CEVT_TIMEOUT:
    139         /* Process the monitor/retransmission time-outs in flow control/retrans mode */
    140         if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
    141             l2c_fcr_proc_tout (p_ccb);
    142         break;
    143 
    144     case L2CEVT_ACK_TIMEOUT:
    145         l2c_fcr_proc_ack_tout (p_ccb);
    146         break;
    147     }
    148 }
    OK,我們下篇將分析數據包入隊列的函數。

沒有留言:

張貼留言