本篇博客由韋東山視頻整理所得
如何控制鏈路層讓其發出廣播包、數據包?
通過HCI層向它發出命令,也可以通過ATT層、L2CAP層向LL層發出數據。
學習資料:
藍牙協議core_v5.0.pdf 《Vol 4: Host Controller Interface [Transport Layer]》
BTStack源碼
回顧一下《開源藍牙協議棧BTStack框架代碼閱讀》。
BLE協議在硬件上分為上下兩部件:主機(Host,PC、單片機、Linux板)、控制器(藍牙模塊),如下圖所示:

Host和Controller之間,通過HCI(Host Controller Interface)相連。HCI實現硬件之間的數據傳輸,HCI的一部分位於Host,另一部分位於Controller(圖中的紅色區域)。
HCI有多種,一般有USB口、3線串口(TxD/RxD/Gnd)、5線串口(TxD/RxD/Gnd/CTS/RTS),如下圖:

Host和Controller之間的數據有命令(Command)、事件(Event)、ACL數據等等,如下圖:

在硬件線路上,怎么分辨這些不同的數據?
在《開源藍牙協議棧BTStack框架代碼閱讀》里,我們知道BTStack對每一種硬件接口都抽象出了一個hci_transport_t結構體,里面有send_packet函數,分析這個函數就可以知道在硬件線路上,怎么分辨、傳輸不同類型的數據。
參考文件:
btstack-master\src\hci_transport_h4.c // 5線串口
btstack-master\src\hci_transport_h5c // 3線串口
btstack-master\platform\windows\hci_transport_h2_winusb.c // usb
send_packet函數原型為:
int (*send_packet)(uint8_t packet_type, uint8_t *packet, int size);
其中的packet_type有4種取值,代表4種類型的數據(在BLE中只有3種,不支持SCO),這些值在頭文件btstack-master\src\bluetooth.h中定義:

一、最簡單的5線串口:
參考文件:
btstack-master\src\hci_transport_h4.c // 5線串口
在數據之前加上1字節的頭部,然后把頭部連同數據一起發送。
這個頭部就用來表示數據類型:
| 1字節頭部 |
表示的數據類型 |
| 0x01 |
Command (Host發給Controller) |
| 0x02 |
ACL Data |
| 0x04 |
Event (Controller發給Host) |
數據在TxD、RxD線路上傳輸,其中的RTS/CTS只是用來進行流量控制。它們的作用簡單介紹一下:
RTS (Require ToSend,發送請求)為輸出信號,用於指示本設備准備好可接收數據,低電平有效,低電平說明本設備可以接收數據。
CTS (Clear ToSend,發送允許)為輸入信號,用於判斷是否可以向對方發送數據,低電平有效,低電平說明本設備可以向對方發送數據。
可以參考btstack-master\src\hci_transport_h4.c的hci_transport_h4_send_packet函數:
//packet所指的內存區域為要發送的數據,packet_type為數據的類型。如何將類型和數據一起發送出去的呢?
看代碼,如下:
static int hci_transport_h4_send_packet(uint8_t packet_type, uint8_t * packet, int size){
// store packet type before actual data and increase size
size++;
packet--; //packet指向一個buffer的首地址,即存放數據的起始地址。然后packet--
*packet = packet_type;這個地方就將數據類型放在了數據的前面去了。
……
二、3線串口:
考文件:
btstack-master\src\hci_transport_h5c // 3線串口
在實際產品中,使用3線串口的藍牙控制器比較少,所以我們不深入分析它。
3線串口沒有硬件流量控制的功能,它使用SLIP協議傳輸數據,流程如下:
(參考hci_transport_link_send_queued_packet函數)
① 構建頭部:
uint8_t header[4];
hci_transport_link_calc_header(header, link_seq_nr, link_ack_nr, link_peer_supports_data_integrity_check, 1, hci_packet_type, hci_packet_size);
構建4字節的頭部,里面含有packet_type等信息。
② 使用SLIP協議發送頭部、數據本身:

③ SLIP協議:
有興趣的人,可以參考以下文章。
SLIP協議最好文章:
SLIP—串行線路上傳輸數據報的非標准協議
https://blog.csdn.net/pushilong/article/details/80358485
https://wenku.baidu.com/view/809a8a8e8bd63186bcebbcae.html
三、USB:
參考文件:
btstack-master\platform\windows\hci_transport_h2_winusb.c // usb
(1) 補充一些USB知識:

一個USB硬件,稱之為“設備”(Device);
一個Device內部,有1種或多種“配置”(Configuration)。比如MP3可以用作U盤,也可以用作播放器,這就是2種配置。同一時間,只能有一個配置起作用。
一個配置內部,有1個或多個接口(Interface)。比如USB聲卡有輸出、輸入2接口。一配置下的多個接口,可以同時使用。
一個接口內部,有1個或多個端點(Endpoint)。端點是數據傳輸的實體。
當我們說“給USB設備發數據”時,實際上是給某個Device的某個Interface的某個Endpoint發數據。
Endpint有4種:控制端點、BULK端點(批量傳輸端點)、中斷端點、實時端點。
每個Endpoint用一個數值表示,控制端點永遠是0,其他端點值由硬件決定。Endpoint值的bit7表示方向:1表示輸入(usb device傳給主機),0表示輸出(主機傳給usb device)。輸出和輸入是對於host來說的。
a. 控制端點可以雙向傳輸數據,其他端點都是單向的。
b. BULK端點:用於傳輸非實時數據
c. 中斷端點:用於傳輸時效性高的數據,比如鼠標、鍵盤數據
d. 實時端點:用於傳輸實時數據,比如視頻傳輸。
主機跟Endpoint之間傳輸數據時,對應數據通道就稱為Pipe。
(2) BLE數據在USB接口上的傳輸:
不同類型的數據,使用不同的Endpoint傳輸數據。
| 數據類型 |
Endpoint |
| command |
控制端點 |
| event |
中斷端點 |
| ACL Data (in, controller傳給host) |
BULK輸入端點 |
| ACL Data (out, host傳給controller) |
BULK輸出端點 |
| SCO Data(in, controller傳給host) |
實時輸入端點 (BLE不支持) |
| SCO Data (out, host傳給controller) |
實時輸出端點 (BLE不支持) |
(3) 參考代碼:btstack-master\platform\windows\hci_transport_h2_winusb.c
① 端點掃描:
usb_scan_for_bluetooth_endpoints函數
② 數據發送:packet_type只是用來調用不同的函數,不會作為數據的一部分發送。

總結:
HCI層描述了Host和Controller之間通過什么接口來連接,可以通過串口、usb口等進行連接。
當通過串口進行傳輸數據時,可以在數據的前面加上一個字節的頭部,用來分辨這個數據是command、event還是acl data。
對於usb口,在usb硬件里面有多個endpoint,command可以發送給某個端點,event可以從某個端點發給Host,不同的數據使用不同的端點。在硬件上傳輸這些數據時,就不需要加上頭部,只需發送數據本身就可以了。
