上一節講了數據流入口,本文分析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 欄位,來確定接下來數據包的走向。如下圖所示:
- CST_CLOSED 狀態:這個 case 處理 Channel 處於 CLOSED 狀態的事件。這個狀態僅僅存在於 L2CAP 的 Link 初始建立過程中。
- 如果發現事件是 L2CEVT_LP_DISCONNECT_IND,則當前 Link 已經斷開,則釋放當前 Channel的 ccb;
- 若事件是 L2CEVT_LP_CONNECT_CFM,則置 p_ccb->chnl_state 為 CST_ORIG_W4_SEC_COMP 狀態,下面會接着介紹這個。
- 如果是 L2CEVT_LP_CONNECT_CFM_NEG 則說明當前 Link 失敗,
- 如果是 L2CEVT_SEC_COMP 則說明 Security 已經清除成功。
- 若是 L2CEVT_L2CA_CONNECT_REQ 則說明 來自上層的 connect 請求,如果當前處於 sniff 狀態,要先取消 sniff。
- L2CEVT_SEC_COMP_NEG 說明 Security 失敗,清除當前 CCB,返回
- L2CEVT_L2CAP_CONNECT_REQ 說明是 Peer connect request,既然成功連接了,結束掉 timer
- L2CEVT_L2CA_DATA_WRITE,如果我們的數據從上層經過這裡,並且是 CST_CLOSED,由於當前的 Channel 沒有建立好,並且上層已經將數據丟給L2CAP了,只能將數據丟棄處理了(數據流不能逆向)。
- L2CEVT_L2CA_DISCONNECT_REQ,上層想斷開連結,會使用這個 Event來處理。
- CST_ORIG_W4_SEC_COMP 狀態:Originator(我的理解是 發起 link 建立的應用)等待 security 的間隙,這個間隙需要處理的事件。跟 CST_CLOSED 差不多,源碼很容易讀,這裡不再次做分析。注意,如果是 L2CEVT_SEC_COMP 事件(跟安全相關),會把 p_ccb->chnl_state 置為 CST_W4_L2CAP_CONNECT_RSP
- CST_TERM_W4_SEC_COMP狀態:Acceptor(接收者)等待 security 的間隙,源碼易讀,不再詳細展開分析,注意一個事件 L2CEVT_SEC_COMP 的處理,會將 p_ccb->chnl_state 置為 CST_W4_L2CA_CONNECT_RSP
- CST_W4_L2CAP_CONNECT_RSP: 經過 2 或 3 這個關於安全連結的步驟,當然要等待 Peer connect的回應(Response)。分為 獲取 peer connect的 confirm、pending、rejected connection等信息,作進一步的判斷。
- CST_CONFIG:商討配置的過程。
- CST_OPEN:Channel 處於 OPEN 狀態,我們可以發送上層傳過來的數據,我們的音樂數據就是從這個 case 裡發出去的。
- CST_W4_L2CAP_DISCONNECT_RSP:等待 peer(對方設備)斷開連結的 Response
- CST_W4_L2CA_DISCONNECT_RSP:等待上層 disc rsp
分析了這麼一大堆,我們的聽音樂那那筆數據包,走的是 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,我們下篇將分析數據包入隊列的函數。
沒有留言:
張貼留言