2015年1月21日 星期三

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

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

本文分析 L2CAP 底層的數據包發送函數。


l2c_link_check_send_pkts (tL2C_LCB p_lcb, tL2C_CCB p_ccb, BT_HDR *p_buf) 函數的主要作用是:
  1. 如果 p_buf 不為 null,p_ccb 為null(在signaling狀態中,通道是Fixed的) 說明這個 ACL 包是這類 L2CAP 的 command:command reject、configuration request、connection request、connection response、connection response neg、configuration response、configuration reject、disconnect response、echo request、echo response、info request、info response、disconnect request,或者說明這個 ACL 包是 S_frame(Supervisory Frame), 目前 Bluedroid 中只有這兩種情況;然後把這個包放到 p_lcb->link_xmit_data_q 中,注意是當前 Link 上的 link_xmit_data_q,跟 CCB 中的 xmit_hold_q 隊列是完全不同的兩個隊列。
  2. 檢查 l2cb.is_cong_cback_context 欄位,如果為 TRUE,說明當前 Link 擁堵,當前髮送過程結束,注意,這時候 ACL 包都已經進入隊列了,都在等待發送(即在此調用本函數)。
  3. 若 p_lcb 不為 null 或 p_lcb->link_xmit_quota 不為0:
    • 首先檢查 p_lcb 的 partial_segment_being_sent(為TRUE,說明 Segment還沒發完,底層來保證,所以當前函數返回)
    • p_lcb 的 link_state 不為 LST_CONNECTED,說明當前 Link 異常,當前函數返回
    • L2C_LINK_CHECK_POWER_MODE 默認是關閉的,關於電源管理的,暫時不分析
    • 步驟 1 分析知道,我們現在發送的是 當前 Link 上的 link_xmit_data_q 隊列裡的包,對於 BR/EDR來說,如果條件 l2cb.controller_xmit_window 不為 0(否則沒有窗口發送數據包了)並且 p_lcb->sent_not_acked < p_lcb->link_xmit_quota(沒回覆的包一定要小於當前Link最大發送值),不成立,那麼在這裡輪詢,直到有了發送窗口並且 sent_not_acked < link_xmit_quota 才將數據包交給 l2c_link_send_to_lower 函數做進一步處理。
    • 這時,Link 上的數據包已經發送了,若 single_write(斷開前最後CCB中的數據處理標誌)為false,我們要繼續去看看當前 Link 上的每個 Channel 上的 Queue 中有沒有數據包,一旦找到一個 p_buf 數據包,就發送(不會循環找,找到了就返回)
OK,上面流程 1 -> 3 是一個單獨的流程, 處理一些 L2CAP command 或 Response 的發送情況。
下面偶在來分析另外一個 case,這三個參數全部為非空的情況,這種情況只在 L2CAP 發送 disconnect request 命令時出現(關閉當前 Link 要確保對應上層應用的 CCB 中的數據要發完),主要作用是:
  1. 在函數 l2cu_send_peer_disc_req 中我們看到 CCB 中的數據包直接從 CCB 的 xmit_hold_q 中出隊列,設置 ACL 包頭就直接交給 l2c_link_check_send_pkts(p_ccb->p_lcb, p_ccb, p_buf2)發送了。
  2. 看到 l2c_link_check_send_pkts 的三個參數均不為 null,說明我們把當前的 CCB 中 xmit_hold_q 的數據包 當成 Link 上的 link_xmit_data_q 發送出去了(相當於優先發送了),single_write 是一個標誌位,代表當前數據包由 CCB -> LCB 代替發送。由於判斷了 single_write 這個標誌位,程序不會再去 CCB 中找數據包了。
還有一種 case 是全部為空的情況,這是觸發了 RR 機制所致,正常情況還有發送窗口的情況下,會遍歷每個 Link ,然後先發當前 Link上的 Queue裡的數據發送出去,然後再發送 CCB 中 Queue 的數據。
OK,這個函數還剩最後一個 case,就是正常情況下,我們聽音樂的數據流發送的情況,這是最常見的一種情況,調用形式為 l2c_link_check_send_pkts (p_lcb, NULL, NULL)。
  1. 判斷當前 Link 是否擁堵。
  2. 首先看看 Link 上的 link_xmit_data_q 隊列有沒有數據,有的話發送
  3. 如果 link_xmit_data_q 沒有數據,在看看 Link 上的 CCB,通過 CCB 的鏈表,找到 CCB 上的隊列,一直找,直到找到一個 數據包,發送之。
  4. 注意:上層在發送數據包,僅僅是調用一下這個函數,至於 是不是這個數據包,那不一定;反正每次上層調用都會發送一個,上層發下來的包總會發出去的。
  1 void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf)
  2 {
  3     int         xx;
  4     BOOLEAN     single_write = FALSE; //最後 Link Disc 用來把 CCB 中的數據包放到 Link 上的隊列發,速度加快
  5     L2CAP_TRACE_DEBUG0("mike: in func-- l2c_link_check_send_pkts");
  6     /* Save the channel ID for faster counting */
  7     if (p_buf) //一般數據包都為空,只有發送 L2CAP 發送 command/response 或 發送 S-Frame 才用到
  8     {
  9         if (p_ccb != NULL) //這個 case 就是 當前 Link 即將斷開的情況了
 10         {
 11             p_buf->event = p_ccb->local_cid;
 12             single_write = TRUE; //見上面註釋
 13             L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts-- p_buf p_ccb not null");
 14         }
 15         else
 16             p_buf->event = 0;
 17 
 18         p_buf->layer_specific = 0;
 19         L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts-- p_buf->layer_specific=0");
 20         GKI_enqueue (&p_lcb->link_xmit_data_q, p_buf); //把這個數據包放到 當前 link 上的 link_xmit_data_q隊列中
 21 
 22         if (p_lcb->link_xmit_quota == 0){
 23             l2cb.check_round_robin = TRUE; // 沒有發送窗口了,需要 RR 看看有沒有別的數據包可以發送的
 24             L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts-- p_lcb->link_xmit_quota");
 25         }
 26     }
 27 
 28     /* If this is called from uncongested callback context break recursive calling.
 29     ** This LCB will be served when receiving number of completed packet event.
 30     */
 31     if (l2cb.is_cong_cback_context){//當前 Link 擁堵了,不發送數據包直接返回,幾乎不會發生
 32         L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts-- l2cd.is_cong_cback_context");
 33         return;
 34     }
 35     /* If we are in a scenario where there are not enough buffers for each link to
 36     ** have at least 1, then do a round-robin for all the LCBs
 37     */
 38     if ( (p_lcb == NULL) || (p_lcb->link_xmit_quota == 0) )
 39     {
 40         L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts-- (p_lcb == NULL) ||(p_lcb->link_xmit_quota == 0)");
 41         if (p_lcb == NULL)
 42             p_lcb = l2cb.lcb_pool;
 43         else if (!single_write)
 44             p_lcb++;
 45 
 46         /* Loop through, starting at the next */
 47         //哎呀,沒有足夠發送窗口了,在所有的 Link 上做一次 RR
 48         for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
 49         {
 50             L2CAP_TRACE_DEBUG1("mike: l2c_link_check_send_pkts--Loop through: xx = %d",xx);
 51             /* If controller window is full, nothing to do */
 52             if ( (l2cb.controller_xmit_window == 0
 53 #if (BLE_INCLUDED == TRUE)
 54                   && !p_lcb->is_ble_link
 55 #endif
 56                 )
 57 #if (BLE_INCLUDED == TRUE)
 58                 || (p_lcb->is_ble_link && l2cb.controller_le_xmit_window == 0 )
 59 #endif
 60               || (l2cb.round_robin_unacked >= l2cb.round_robin_quota) )
 61                 break;
 62 
 63             /* Check for wraparound */
 64             if (p_lcb == &l2cb.lcb_pool[MAX_L2CAP_LINKS])
 65                 p_lcb = &l2cb.lcb_pool[0];
 66 
 67             if ( (!p_lcb->in_use)
 68                || (p_lcb->partial_segment_being_sent)
 69                || (p_lcb->link_state != LST_CONNECTED)
 70                || (p_lcb->link_xmit_quota != 0)
 71                || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) )
 72                 continue;
 73 
 74             //首先從 當前 Link 上的 link_xmit_data_q 中取出數據包並發送  
 75             if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) != NULL)
 76             {
 77                 L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts--if ((p_buf = (BT_HDR*)GKI_dequeue (&p_lcb->link_xmit_data_q)) != NULL)");
 78                 l2c_link_send_to_lower (p_lcb, p_buf);
 79             }
 80             else if (single_write) //如果是 single_write 設置為 TRUE,說明數據包 已經在 link_xmit_data_q 發送了,沒必要在執行下面的 code 了
 81             {
 82                 /* If only doing one write, break out */
 83                 L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts--single write is true then break");
 84                 break;
 85             }
 86             //Link 上的 Queue 中沒有東西可以發送,查找 CCB 中的 Queue,直到找到一個為止。
 87             else if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) != NULL)
 88             {
 89                 L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts--(p_buf=l2cu_get_next_buffer_to_send (p_lcb)) != NULL");
 90                 l2c_link_send_to_lower (p_lcb, p_buf);
 91             }
 92         }
 93 
 94         /* If we finished without using up our quota, no need for a safety check */
 95 #if (BLE_INCLUDED == TRUE)
 96         if ( ((l2cb.controller_xmit_window > 0 && !p_lcb->is_ble_link) ||
 97              (l2cb.controller_le_xmit_window > 0 && p_lcb->is_ble_link))
 98           && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
 99 #else
100         if ( (l2cb.controller_xmit_window > 0)
101           && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
102 
103 #endif
104             l2cb.check_round_robin = FALSE;
105     }
106     else /* if this is not round-robin service */
107     {
108         /* If a partial segment is being sent, can't send anything else */
109         if ( (p_lcb->partial_segment_being_sent)
110           || (p_lcb->link_state != LST_CONNECTED)
111           || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) )
112             return;
113 
114         /* See if we can send anything from the link queue */
115 #if (BLE_INCLUDED == TRUE)
116         while ( ((l2cb.controller_xmit_window != 0 && !p_lcb->is_ble_link) ||
117                  (l2cb.controller_le_xmit_window != 0 && p_lcb->is_ble_link))
118              && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
119 #else
120         while ( (l2cb.controller_xmit_window != 0)
121              && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
122 #endif
123         {
124             if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) == NULL)//發送Link上的數據包
125                 break;
126 
127             if (!l2c_link_send_to_lower (p_lcb, p_buf))
128                 break;
129         }
130 
131         if (!single_write)//確保不是在 鏈路 disc 狀態下
132         {
133             /* See if we can send anything for any channel */
134 #if (BLE_INCLUDED == TRUE)
135             while ( ((l2cb.controller_xmit_window != 0 && !p_lcb->is_ble_link) ||
136                     (l2cb.controller_le_xmit_window != 0 && p_lcb->is_ble_link))
137                     && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
138 #else
139             while ((l2cb.controller_xmit_window != 0) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
140 #endif
141             {
142                 if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) == NULL)//找到一個數據包來發送
143                     break;
144 
145                 if (!l2c_link_send_to_lower (p_lcb, p_buf))
146                     break;
147             }
148         }
149 
150         /* There is a special case where we have readjusted the link quotas and  */
151         /* this link may have sent anything but some other link sent packets so  */
152         /* so we may need a timer to kick off this link's transmissions.         */
153         if ( (p_lcb->link_xmit_data_q.count) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) )
154             L2CAP_TRACE_DEBUG0("mike: l2c_link_check_send_pkts--a timer to kick off this link's transmissions");
155             btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT);
156     }
157 
158 }
最終 l2c_link_check_send_pkts 把數據包交給了 l2c_link_send_to_lower 來做處理,我們的音樂數據包最終也被從某個 CCB 中的隊列出隊列給了 l2c_link_send_to_lower。l2c_link_send_to_lower 主要做了這些事情:
  1. 如果當前數據包 p_buf 的長度小於 ACL 包的最大值,sent_not_acked 加1,整個 L2CAP 的 controller_xmit_window 減1。然後通過 L2C_LINK_SEND_ACL_DATA 將此數據包發送出去。
  2. 如果當前數據包 p_buf 的長度大於 ACL 包的最大值,先看看能分成幾個分包(為了求的幾個窗口能容下),然後窗口值減掉這些分包個數,然後將整個數據包交給 L2C_LINK_SEND_ACL_DATA (大於ACL包長度),具體分包發送由 H5(串口) 部分來負責。
  1 /*******************************************************************************
  2 **
  3 ** Function         l2c_link_send_to_lower
  4 **
  5 ** Description      This function queues the buffer for HCI transmission
  6 **
  7 ** Returns          TRUE for success, FALSE for fail
  8 **
  9 *******************************************************************************/
 10 static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf)
 11 {
 12     UINT16      num_segs;
 13     UINT16      xmit_window, acl_data_size;
 14     L2CAP_TRACE_DEBUG0("mike: l2c_link_send_to_lower");
 15 #if (BLE_INCLUDED == TRUE)
 16     if ((!p_lcb->is_ble_link && (p_buf->len <= btu_cb.hcit_acl_pkt_size)) ||
 17         (p_lcb->is_ble_link && (p_buf->len <= btu_cb.hcit_ble_acl_pkt_size)))
 18 #else
 19     if (p_buf->len <= btu_cb.hcit_acl_pkt_size) //一般都是走這條路徑,p_buf一般不會超過 ACL 長度最大值
 20 #endif
 21     {
 22         if (p_lcb->link_xmit_quota == 0){ // Link 上沒有窗口了,controller_xmit_window 窗口還是有的,此時就是 round_roubin_unack了(因為上面的數據已經下來了,必須得發送)
 23             L2CAP_TRACE_DEBUG0("mike: l2c_link_send_to_lower--if (p_lcb->link_xmit_quota == 0)");
 24             l2cb.round_robin_unacked++;
 25             L2CAP_TRACE_DEBUG1("mike: l2c_link_send_to_lower--l2cb.round_robin_unacked=%d",l2cb.round_robin_unacked);
 26         }
 27         p_lcb->sent_not_acked++; //整個 Link 已經發送但是沒有回覆的數據包個數
 28         L2CAP_TRACE_DEBUG1("mike:l2c_link_send_to_lower--p_lcb->sent_not_acked:",p_lcb->sent_not_acked);
 29         p_buf->layer_specific = 0;
 30 
 31 #if (BLE_INCLUDED == TRUE)
 32         if (p_lcb->is_ble_link)
 33         {
 34             l2cb.controller_le_xmit_window--;
 35             L2C_LINK_SEND_BLE_ACL_DATA (p_buf);
 36         }
 37         else
 38 #endif
 39         {
 40             l2cb.controller_xmit_window--; //當前 controller 發送窗口減1
 41             L2CAP_TRACE_DEBUG1("mike:l2c_link_send_to_lower--,l2cb.controller_xmit_window=%d",l2cb.controller_xmit_window);
 42             L2CAP_TRACE_DEBUG0("mike: l2c_link_send_to_lower--L2C_LINK_SEND_ACL_DATA");
 43             L2C_LINK_SEND_ACL_DATA (p_buf); //發送當前這個數據包
 44         }
 45     }
 46     else
 47     {
 48 #if BLE_INCLUDED == TRUE
 49         if (p_lcb->is_ble_link)
 50         {
 51             acl_data_size = btu_cb.hcit_ble_acl_data_size;
 52             xmit_window = l2cb.controller_le_xmit_window;
 53 
 54         }
 55         else
 56 #endif
 57         {
 58             acl_data_size = btu_cb.hcit_acl_data_size;//ACL 包額度最大值
 59             xmit_window = l2cb.controller_xmit_window; //controller目前為止的可用窗口
 60         }
 61         num_segs = (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size - 1) / acl_data_size;
 62         L2CAP_TRACE_DEBUG3("mike: l2c_link_send_to_lower-- num_segs:%d, acl_data_size:%d,xmit_window=%d", num_segs,acl_data_size, xmit_window);
 63 
 64         /* If doing round-robin, then only 1 segment each time */
 65         if (p_lcb->link_xmit_quota == 0)
 66         {
 67             num_segs = 1;
 68             p_lcb->partial_segment_being_sent = TRUE;
 69         }
 70         else
 71         {
 72             /* Multi-segment packet. Make sure it can fit */
 73             if (num_segs > xmit_window)
 74             {
 75                 num_segs = xmit_window;//分包個數比 controller 窗口的個數還多,只能發 controller 個數的包
 76                 p_lcb->partial_segment_being_sent = TRUE; //標誌位,還有分包,需要繼續發送,Btu_task 中有個 Event 就是處理分包的
 77             }
 78 
 79             if (num_segs > (p_lcb->link_xmit_quota - p_lcb->sent_not_acked))
 80             {
 81                 num_segs = (p_lcb->link_xmit_quota - p_lcb->sent_not_acked);
 82                 p_lcb->partial_segment_being_sent = TRUE;
 83             }
 84         }
 85 
 86         p_buf->layer_specific        = num_segs;
 87 #if BLE_INCLUDED == TRUE
 88         if (p_lcb->is_ble_link)
 89         {
 90             l2cb.controller_le_xmit_window -= num_segs;
 91 
 92         }
 93         else
 94 #endif
 95         l2cb.controller_xmit_window -= num_segs;//分包占用的窗口數
 96 
 97         if (p_lcb->link_xmit_quota == 0)
 98             l2cb.round_robin_unacked += num_segs;
 99 
100         p_lcb->sent_not_acked += num_segs;
101 #if BLE_INCLUDED == TRUE
102         if (p_lcb->is_ble_link)
103         {
104             L2C_LINK_SEND_BLE_ACL_DATA(p_buf);
105         }
106         else
107 #endif
108         {
109             L2C_LINK_SEND_ACL_DATA (p_buf);//發送數據包
110         }
111     }
112 
113 #if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE)
114 #if (BLE_INCLUDED == TRUE)
115     if (p_lcb->is_ble_link)
116     {
117         L2CAP_TRACE_DEBUG6 ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d",
118                 l2cb.controller_le_xmit_window,
119                 p_lcb->handle,
120                 p_lcb->link_xmit_quota, p_lcb->sent_not_acked,
121                 l2cb.round_robin_quota, l2cb.round_robin_unacked);
122     }
123     else
124 #endif
125     {
126         L2CAP_TRACE_DEBUG6 ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d",
127                 l2cb.controller_xmit_window,
128                 p_lcb->handle,
129                 p_lcb->link_xmit_quota, p_lcb->sent_not_acked,
130                 l2cb.round_robin_quota, l2cb.round_robin_unacked);
131     }
132 #endif
133 
134     return TRUE;
135 }
l2c_link_send_to_lower 把數據交給了 L2C_LINK_SEND_ACL_DATA,L2C_LINK_SEND_ACL_DATA 其實是 bte_main_hci_send 函數,bte_main_hci_send 函數通過調用 hci 的接口 transmit_buf 來轉送數據包。 transmit_buf 函數作用比較簡單,將此數據包入 tx_q 隊列,串口(H5)的守護線程 bt_hc_worker_thread 會從 tx_q 隊列中獲取數據包,並將其發送。
1 static int transmit_buf(TRANSAC transac, char *p_buf, int len)
2 {
3     utils_enqueue(&tx_q, (void *) transac);
4 
5     bthc_signal_event(HC_EVENT_TX);
6 
7     return BT_HC_STATUS_SUCCESS;
8 }
到這裡為止,我們就把 ACL 包整個發送流程分析完了。

沒有留言:

張貼留言