2015年1月21日 星期三

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

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

ACL 包接收流程

有關 ACL 包接收的過程都是在 btu_task 這個守護線程中處理的。
我們看到 btu_task 處理數據包的過程:
  1. 等待事件,事件到來後,如果是 TASK_MBOX_0_EVT_MASK(是不是 MBOX裡的Task),那麼從 mbox 中取出這個數據包,並判斷是什麼類型的 Event。
  2. 如果是 BT_EVT_TO_BTU_HCI_ACL,說明是 ACL 數據,交給 l2cap 來處理。
  3. 如果是 BT_EVT_TO_BTU_L2C_SEG_XMIT,說明是 L2CAP 的分包數據沒有發送完,那繼續發送分包數據。
 1 //部分 btu_task 源碼
 2 ...........
 3    /* Wait for, and process, events */
 4     for (;;)
 5     {
 6         event = GKI_wait (0xFFFF, 0);
 7 
 8         if (event & TASK_MBOX_0_EVT_MASK)
 9         {
10             /* Process all messages in the queue */
11             while ((p_msg = (BT_HDR *) GKI_read_mbox (BTU_HCI_RCV_MBOX)) != NULL)
12             {
13                 /* Determine the input message type. */
14                 switch (p_msg->event & BT_EVT_MASK)
15                 {
16                     case BT_EVT_TO_BTU_HCI_ACL:
17                         /* All Acl Data goes to L2CAP */
18                         l2c_rcv_acl_data (p_msg);//我們的 ACL 數據來了,關鍵分析這個函數
19                         break;
20 
21                     case BT_EVT_TO_BTU_L2C_SEG_XMIT:
22                         /* L2CAP segment transmit complete */
23                         l2c_link_segments_xmitted (p_msg);
24                         break;
25 
26                     case BT_EVT_TO_BTU_HCI_SCO:
27 #if BTM_SCO_INCLUDED == TRUE
28                         btm_route_sco_data (p_msg);
29                         break;
30 #endif
31 
32                     case BT_EVT_TO_BTU_HCI_EVT:
33                         btu_hcif_process_event ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
34                         GKI_freebuf(p_msg);
35 
36 ....
l2c_rcv_acl_data 這個函數,處理收到的 ACL 包,下面我們來分析一下 l2c_rcv_acl_data 這個函數:
  1. 在收到的 ACL 包中找出 pkt_type(分包的話要另作處理) 和 handle。
  2. 若此 ACL 包是一個完整的數據包:
    • 首先通過 handle 找到 LCB
    • rcv_cid 大於 L2CAP_BASE_APPL_CID(0x0040),說明是上層應用普通數據包,通過 CID 找到當前包的 CCB。
    • hci_len 長度肯定要大於 L2CAP 頭長度,否則肯定頭部出錯了。
    • 如果 rcv_cid 是 L2CAP_SIGNALLING_CID,說明數據包是 創建和建立 Channel 用的(上層應用傳輸數據),使用函數 process_l2cap_cmd 來處理。
    • 如果 rcv_cid 是 L2CAP_CONNECTIONLESS_CID 說明是 廣播或單播,使用函數 tcs_proc_bcst_msg 處理。
    • 如果 rcv_cid 是 L2CAP_BLE_SIGNALLING_CID 說明是 BLE 的signalling包,交給函數 l2cble_process_sig_cmd 處理。
    • 普通數據包,直接交給 L2CAP 的數據流狀態機 l2c_csm_execute (p_ccb,L2CEVT_L2CAP_DATA, p_msg) 來處理,找到 L2CEVT_L2CAP_DATA 的這個 case,原來通過回調,將此數據包交給上層了(如 RFCOMM,最終是交給 RFCOMM_BufDataInd函數作進一步處理)
上述數據包,我們僅僅考慮了最普通的數據流,其他的控制數據包有時間在分析。
  1 void l2c_rcv_acl_data (BT_HDR *p_msg)
  2 {
  3     UINT8       *p = (UINT8 *)(p_msg + 1) + p_msg->offset;
  4     UINT16      handle, hci_len;
  5     UINT8       pkt_type;
  6     tL2C_LCB    *p_lcb;
  7     tL2C_CCB    *p_ccb = NULL;
  8     UINT16      l2cap_len, rcv_cid, psm;
  9 
 10     /* Extract the handle */
 11     STREAM_TO_UINT16 (handle, p);
 12     pkt_type = HCID_GET_EVENT (handle);
 13     handle   = HCID_GET_HANDLE (handle);
 14 
 15     /* Since the HCI Transport is putting segmented packets back together, we */
 16     /* should never get a valid packet with the type set to "continuation"    */
 17     if (pkt_type != L2CAP_PKT_CONTINUE)//數據包一定要是完整的,分包另作處理
 18     {
 19         /* Find the LCB based on the handle */
 20         if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL)
 21         {
 22             UINT8       cmd_code;
 23 
 24             /* There is a slight possibility (specifically with USB) that we get an */
 25             /* L2CAP connection request before we get the HCI connection complete.  */
 26             /* So for these types of messages, hold them for up to 2 seconds.       */
 27             STREAM_TO_UINT16 (hci_len, p);
 28             STREAM_TO_UINT16 (l2cap_len, p);
 29             STREAM_TO_UINT16 (rcv_cid, p);
 30             STREAM_TO_UINT8  (cmd_code, p);
 31 
 32             if ((p_msg->layer_specific == 0) && (rcv_cid == L2CAP_SIGNALLING_CID)
 33                 && (cmd_code == L2CAP_CMD_INFO_REQ || cmd_code == L2CAP_CMD_CONN_REQ))
 34             {
 35                 L2CAP_TRACE_WARNING5 ("L2CAP - holding ACL for unknown handle:%d ls:%d cid:%d opcode:%d cur count:%d",
 36                                     handle, p_msg->layer_specific, rcv_cid, cmd_code,
 37                                     l2cb.rcv_hold_q.count);
 38                 p_msg->layer_specific = 2;
 39                 GKI_enqueue (&l2cb.rcv_hold_q, p_msg);//添加到隊列中,等待 connect 數據包到達,有種可能發生的 case
 40 
 41                 if (l2cb.rcv_hold_q.count == 1)
 42                     btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT);
 43 
 44                 return;
 45             }
 46             else
 47             {
 48                 L2CAP_TRACE_ERROR5 ("L2CAP - rcvd ACL for unknown handle:%d ls:%d cid:%d opcode:%d cur count:%d",
 49                                     handle, p_msg->layer_specific, rcv_cid, cmd_code, l2cb.rcv_hold_q.count);
 50             }
 51             GKI_freebuf (p_msg);
 52             return;
 53         }
 54     }
 55     else
 56     {
 57         L2CAP_TRACE_WARNING1 ("L2CAP - expected pkt start or complete, got: %d", pkt_type);
 58         GKI_freebuf (p_msg);
 59         return;
 60     }
 61     //下面是我們把 ACL 數據包給部分拆包了,即除掉 L2CAP 的控制部分,還原上層的數據包。
 62     /* Extract the length and update the buffer header */
 63     STREAM_TO_UINT16 (hci_len, p);
 64     p_msg->offset += 4;
 65 
 66 #if (L2CAP_HOST_FLOW_CTRL == TRUE)
 67     /* Send ack if we hit the threshold */
 68     if (++p_lcb->link_pkts_unacked >= p_lcb->link_ack_thresh)
 69         btu_hcif_send_host_rdy_for_data();
 70 #endif
 71 
 72     /* Extract the length and CID */
 73     STREAM_TO_UINT16 (l2cap_len, p);
 74     STREAM_TO_UINT16 (rcv_cid, p);
 75 
 76     /* Find the CCB for this CID */
 77     if (rcv_cid >= L2CAP_BASE_APPL_CID)// 說明此 rcv_cid 是上層應用普通數據流的 CID 
 78     {
 79         if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, rcv_cid)) == NULL)
 80         {
 81             L2CAP_TRACE_WARNING1 ("L2CAP - unknown CID: 0x%04x", rcv_cid);
 82             GKI_freebuf (p_msg);
 83             return;
 84         }
 85     }
 86 
 87     if (hci_len >= L2CAP_PKT_OVERHEAD)  //數據包長度肯定要大於 Head的值,否則必然是個錯包
 88     {
 89         p_msg->len    = hci_len - L2CAP_PKT_OVERHEAD;
 90         p_msg->offset += L2CAP_PKT_OVERHEAD;
 91     }
 92     else
 93     {
 94         L2CAP_TRACE_WARNING0 ("L2CAP - got incorrect hci header" );
 95         GKI_freebuf (p_msg);
 96         return;
 97     }
 98 
 99     if (l2cap_len != p_msg->len) //長度不相等,那數據包傳送過程肯定出現了差錯,丟包吧
100     {
101         L2CAP_TRACE_WARNING2 ("L2CAP - bad length in pkt. Exp: %d  Act: %d",
102                               l2cap_len, p_msg->len);
103 
104         GKI_freebuf (p_msg);
105         return;
106     }
107 
108     /* Send the data through the channel state machine */
109     if (rcv_cid == L2CAP_SIGNALLING_CID)//控制創建和建立 Channel的 signalling
110     {
111         process_l2cap_cmd (p_lcb, p, l2cap_len); //此函數專門處理這個Channel的事件
112         GKI_freebuf (p_msg);
113     }
114     else if (rcv_cid == L2CAP_CONNECTIONLESS_CID)
115     {
116         /* process_connectionless_data (p_lcb); */
117         STREAM_TO_UINT16 (psm, p);
118         L2CAP_TRACE_DEBUG1( "GOT CONNECTIONLESS DATA PSM:%d", psm ) ;
119 #if (TCS_BCST_SETUP_INCLUDED == TRUE && TCS_INCLUDED == TRUE)
120         if (psm == TCS_PSM_INTERCOM || psm == TCS_PSM_CORDLESS)
121         {
122             p_msg->offset += L2CAP_BCST_OVERHEAD;
123             p_msg->len -= L2CAP_BCST_OVERHEAD;
124             tcs_proc_bcst_msg( p_lcb->remote_bd_addr, p_msg ) ;
125             GKI_freebuf (p_msg);
126         }
127         else
128 #endif
129 
130 #if (L2CAP_UCD_INCLUDED == TRUE)
131         /* if it is not broadcast, check UCD registration */
132         if ( l2c_ucd_check_rx_pkts( p_lcb, p_msg ) )
133         {
134             /* nothing to do */
135         }
136         else
137 #endif
138             GKI_freebuf (p_msg);
139     }
140 #if (BLE_INCLUDED == TRUE)
141     else if (rcv_cid == L2CAP_BLE_SIGNALLING_CID) //LE 設備的專用 Channel
142     {
143         l2cble_process_sig_cmd (p_lcb, p, l2cap_len);
144         GKI_freebuf (p_msg);
145     }
146 #endif
147 #if (L2CAP_NUM_FIXED_CHNLS > 0)
148     else if ((rcv_cid >= L2CAP_FIRST_FIXED_CHNL) && (rcv_cid <= L2CAP_LAST_FIXED_CHNL) &&
149              (l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb != NULL) )
150     {
151         /* If no CCB for this channel, allocate one */
152         if (l2cu_initialize_fixed_ccb (p_lcb, rcv_cid, &l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts))
153         {
154             p_ccb = p_lcb->p_fixed_ccbs[rcv_cid - L2CAP_FIRST_FIXED_CHNL];
155 
156             if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
157                 l2c_fcr_proc_pdu (p_ccb, p_msg);
158             else
159                 (*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(p_lcb->remote_bd_addr, p_msg);
160         }
161         else
162             GKI_freebuf (p_msg);
163     }
164 #endif
165 
166     else
167     {
168         if (p_ccb == NULL)
169             GKI_freebuf (p_msg);
170         else
171         {
172             /* Basic mode packets go straight to the state machine */
173             if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE)
174             //普通的數據流都是經過這條通路的,下面這個函數覺得很熟悉吧,因為在發送數據包的時候也調用了他。
175                 l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg);
176             else
177             {
178                 /* eRTM or streaming mode, so we need to validate states first */
179                 if ((p_ccb->chnl_state == CST_OPEN) || (p_ccb->chnl_state == CST_CONFIG))
180                     l2c_fcr_proc_pdu (p_ccb, p_msg);
181                 else
182                     GKI_freebuf (p_msg);
183             }
184         }
185     }
186 }
下面我們分析一個 Event--number-of-completed-packets,是由函數 l2c_link_process_num_completed_pkts 處理。 這個 event 將會更新我們 LCB 上的數據包數量。
我們追蹤這個函數的調用鏈:
btu_task的一個 BT_EVT_TO_BTU_HCI_EVT case --> btu_hcif_process_event 的一個 HCI_NUM_COMPL_DATA_PKTS_EVT case --> l2c_link_process_num_completed_pkts
通過這個調用鏈,我們知道處理事件或數據流都在 btu_task 中進行。
l2c_link_process_num_completed_pkts這個函數都做了些什麼呢?我們來深入代碼瞭解一下整個過程。
  1. 通過 num_sent 來更新數據 controller_xmit_window,sent_not_acked。
  2. 既然又多了 credits(Snoop中的),那麼自然調用 l2c_link_check_send_pkts 去找更多的包發送下去。
  3. l2c_link_process_num_completed_pkts 和 l2c_link_check_send_pkts 形成了一個遞歸式的調用,l2c_link_check_send_pkts 會產生 process num complete 這個event,l2c_link_process_num_completed_pkts找到 Link後在讓 l2c_link_check_send_pkts 發包。
  1 void l2c_link_process_num_completed_pkts (UINT8 *p)
  2 {
  3     UINT8       num_handles, xx;
  4     UINT16      handle;
  5     UINT16      num_sent; //已經發下去的數據,這個數據要更新controller_xmit_window
  6     tL2C_LCB    *p_lcb;
  7 
  8     L2CAP_TRACE_DEBUG0("mike: l2c_link_process_num_completed_pkts");
  9     STREAM_TO_UINT8 (num_handles, p);
 10     L2CAP_TRACE_DEBUG1("mike: l2c_link_process_num_completed_pkts--number_handles:%d", num_handles);
 11     for (xx = 0; xx < num_handles; xx++)//handle 對應着每條邏輯鏈路(Link)
 12     {
 13         STREAM_TO_UINT16 (handle, p);
 14         STREAM_TO_UINT16 (num_sent, p);
 15 
 16         p_lcb = l2cu_find_lcb_by_handle (handle);
 17 
 18         /* Callback for number of completed packet event    */
 19         /* Originally designed for [3DSG]                   */
 20         if((p_lcb != NULL) && (p_lcb->p_nocp_cb))
 21         {
 22             L2CAP_TRACE_DEBUG0 ("L2CAP - calling NoCP callback");
 23             (*p_lcb->p_nocp_cb)(p_lcb->remote_bd_addr);
 24         }
 25 
 26         if (p_lcb)
 27         {
 28 #if (BLE_INCLUDED == TRUE)
 29             if (p_lcb->is_ble_link)
 30             {
 31                 l2cb.controller_le_xmit_window += num_sent;
 32             }
 33             else
 34 #endif
 35             {
 36                 /* Maintain the total window to the controller */
 37                 l2cb.controller_xmit_window += num_sent;
 38             }
 39             /* If doing round-robin, adjust communal counts */
 40             if (p_lcb->link_xmit_quota == 0)
 41             {
 42                 /* Don't go negative */
 43                 if (l2cb.round_robin_unacked > num_sent)
 44                     l2cb.round_robin_unacked -= num_sent;
 45                 else
 46                     l2cb.round_robin_unacked = 0;
 47             }
 48 
 49             /* Don't go negative */
 50             if (p_lcb->sent_not_acked > num_sent)
 51                 p_lcb->sent_not_acked -= num_sent; //更新
 52             else
 53                 p_lcb->sent_not_acked = 0;
 54 
 55             l2c_link_check_send_pkts (p_lcb, NULL, NULL);
 56 
 57             L2CAP_TRACE_DEBUG1("mike:l2c_link_process_num_completed_pkts--l2cb.controller_xmit_window=%d",l2cb.controller_xmit_window);
 58 
 59             /* If we were doing round-robin for low priority links, check 'em */
 60             if ( (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)
 61               && (l2cb.check_round_robin)
 62               && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
 63             {
 64               l2c_link_check_send_pkts (NULL, NULL, NULL);
 65             }
 66         }
 67 
 68 #if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE)
 69         if (p_lcb)
 70         {
 71 #if (BLE_INCLUDED == TRUE)
 72             if (p_lcb->is_ble_link)
 73             {
 74                 L2CAP_TRACE_DEBUG5 ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d",
 75                     l2cb.controller_le_xmit_window,
 76                     p_lcb->handle, p_lcb->sent_not_acked,
 77                     l2cb.check_round_robin, l2cb.round_robin_unacked);
 78             }
 79             else
 80 #endif
 81             {
 82                 L2CAP_TRACE_DEBUG5 ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d",
 83                     l2cb.controller_xmit_window,
 84                     p_lcb->handle, p_lcb->sent_not_acked,
 85                     l2cb.check_round_robin, l2cb.round_robin_unacked);
 86 
 87             }
 88         }
 89         else
 90         {
 91 #if (BLE_INCLUDED == TRUE)
 92             L2CAP_TRACE_DEBUG5 ("TotalWin=%d  LE_Win: %d, Handle=0x%x, RRCheck=%d, RRUnack=%d",
 93                 l2cb.controller_xmit_window,
 94                 l2cb.controller_le_xmit_window,
 95                 handle,
 96                 l2cb.check_round_robin, l2cb.round_robin_unacked);
 97 #else
 98             L2CAP_TRACE_DEBUG4 ("TotalWin=%d  Handle=0x%x  RRCheck=%d  RRUnack=%d",
 99                 l2cb.controller_xmit_window,
100                 handle,
101                 l2cb.check_round_robin, l2cb.round_robin_unacked);
102 #endif
103         }
104 #endif
105     }
106 
107 #if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE)
108     /* only full stack can enable sleep mode */
109     btu_check_bt_sleep ();
110 #endif
111 }
到此為止,我們已經把普通 ACL 包發送和接收部分源碼分析完了。後續有時間會繼續分析控制類型的ACL包,以及ACL鏈路建立的流程。

沒有留言:

張貼留言