首先來看一下,對於硬件操作,它是如何來進行處理的。在上篇文章中曾說過,在main函數里面它會調用硬件相關的代碼,調用操作系統相關的代碼。在BTStack中,可以搜索一下main.c,將會發現有很多main.c,都是為於port目錄下面。
1 Main.c (port\esp32\components\btstack) 2 Main.c (port\ez430-rf2560\src) 3 Main.c (port\libusb) 4 Main.c (port\libusb-intel) 5 Main.c (port\max32630-fthr\src) 6 Main.c (port\msp-exp430f5438-cc2564b\src) 7 Main.c (port\msp430f5229lp-cc2564b\src) 8 Main.c (port\nrf5-zephyr) 9 Main.c (port\nrf5x) 10 Main.c (port\pic32-harmony\src) 11 Main.c (port\posix-h4) 12 Main.c (port\posix-h4-atwilc3000) 13 Main.c (port\posix-h4-da14581) 14 Main.c (port\posix-h4-da14585) 15 Main.c (port\posix-h4-zephyr) 16 Main.c (port\posix-h5) 17 Main.c (port\posix-h5-bcm) 18 Main.c (port\raspi) 19 Main.c (port\samv71-xplained-atwilc3000) 20 Main.c (port\stm32-f103rb-nucleo) 21 Main.c (port\stm32-f4discovery-cc256x\eclipse-template\src) 22 Main.c (port\stm32-l053r8-em9304\cubemx-l053r8-em9304\src) 23 Main.c (port\wiced-h4) 24 Main.c (port\wiced-h5) 25 Main.c (port\windows-h4) 26 Main.c (port\windows-h4-zephyr) 27 Main.c (port\windows-winusb) 28 Main.c (port\windows-winusb-intel)
看一下windows,有Main.c (port\windows-h4)、Main.c (port\windows-winusb),使用的是usb口的藍牙模塊。注意后h4表示5線串口的藍牙模塊。
分析Main.c 中的main函數,按照上一篇文章中總結出來的框架,首先找到硬件操作的相關代碼,然后再看操作系統先關的代碼
1. 硬件相關的代碼:
a.使用usb口
分析Main.c (port\windows-winusb)
// setup USB Transport
transport = hci_transport_usb_instance();
const hci_transport_t * hci_transport_usb_instance(void) {
return &hci_transport_usb; //返回hci_transport_usb的結構體
}
hci_transport_usb的結構體定義如下:
1 // get usb singleton 2 static const hci_transport_t hci_transport_usb = { 3 /* const char * name; */ "H2_WINUSB", 4 /* void (*init) (const void *transport_config); */ &usb_init, 5 /* int (*open)(void); */ &usb_open, 6 /* int (*close)(void); */ &usb_close, 7 /* void (*register_packet_handler)(void (*handler)(...); */ &usb_register_packet_handler, 8 /* int (*can_send_packet_now)(uint8_t packet_type); */ &usb_can_send_packet_now, 9 /* int (*send_packet)(...); */ &usb_send_packet, 10 /* int (*set_baudrate)(uint32_t baudrate); */ NULL, 11 /* void (*reset_link)(void); */ NULL, 12 #ifdef ENABLE_SCO_OVER_HCI 13 /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ usb_set_sco_config, 14 #else 15 /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL, 16 #endif 17 };
在hci_transport_usb結構體中,有初始化函數、有open函數、有注冊函數、有發送包的函數等等,這些函數應該就是用來操作硬件的。
在main函數中,返回了一個結構體,以后將使用transport 這個結構體去操作硬件——從硬件里面得到數據或把數據發給硬件。
以上使用的是USB口,如果我使用的是串口呢?硬件操作的相關代碼又是怎樣的?
b. 使用串口
分析Main.c (port\windows-h4)
main
const btstack_uart_block_t * uart_driver = btstack_uart_block_windows_instance();
const hci_transport_t * transport = hci_transport_h4_instance(uart_driver); //同樣是返回一個transport結構體
// configure and return h4 singleton
const hci_transport_t * hci_transport_h4_instance(const btstack_uart_block_t * uart_driver) {
btstack_uart = uart_driver;
return &hci_transport_h4;//返回hci_transport_h4的結構體
}
hci_transport_h4結構體是什么樣的呢?定義如下:
1 static const hci_transport_t hci_transport_h4 = { 2 /* const char * name; */ "H4", 3 /* void (*init) (const void *transport_config); */ &hci_transport_h4_init, 4 /* int (*open)(void); */ &hci_transport_h4_open, 5 /* int (*close)(void); */ &hci_transport_h4_close, 6 /* void (*register_packet_handler)(void (*handler)(...); */ &hci_transport_h4_register_packet_handler, 7 /* int (*can_send_packet_now)(uint8_t packet_type); */ &hci_transport_h4_can_send_now, 8 /* int (*send_packet)(...); */ &hci_transport_h4_send_packet, 9 /* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h4_set_baudrate, 10 /* void (*reset_link)(void); */ NULL, 11 /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL, 12 };
注意:btstack_uart這個參數是用來干嘛的呢?
BTStack支持多種接口的藍牙模塊,比如USB口、3線串口、5線串口。對於3線串口和5線串口,它們之間有什么差別呢?
對於3線串口,它只有三條線:TxD、RxD、GND。5線串口比三線串口多了兩條線:CTS、RTS,用來控制流量。
使用三線串口和無線串口傳輸同一個數據時,它們使用的協議不一樣。

假設圖中紅色的部分就是要發送的數據,當使用三線串口時可能給它加上頭部、尾部后再發送給硬件,當使用五線串口時可能將數據直接發給硬件。
從這個地方可以產出,無論是三線串口還是五線串口,它們的底層硬件操作都是一樣的。因此在硬件的這一層,又抽象出了一個結構體:uart_driver。使用該結構體來操作硬件。
H5協議只是將數據加上各種頭部和各種尾部,H4協議也只是對數據進行了某種處理。
因此在main函數中,首先得到了uart_driver,然后再將該結構體作為hci_transport_h4_instance的參數傳進去。
看一下hci_transport_h4_open()函數:
hci_transport_h4_open
btstack_uart->open();//直接調用了btstack_uart的open函數。
從中可以看出,H4、H5協議通過 btstack_uart_block_t結構體來操作硬件。
2. 操作系統相關的代碼
a.windows操作系統
分析Main.c (port\windows-winusb)
main
btstack_run_loop_init(btstack_run_loop_windows_get_instance());
通過btstack_run_loop_windows_get_instance()來獲取一個結構體,
/**
* Provide btstack_run_loop_windows instance
*/
const btstack_run_loop_t * btstack_run_loop_windows_get_instance(void){
return &btstack_run_loop_windows;
}
btstack_run_loop_windows結構體定義如下:定義了操作系統先關的循環函數。
1 static const btstack_run_loop_t btstack_run_loop_windows = { 2 &btstack_run_loop_windows_init, 3 &btstack_run_loop_windows_add_data_source, 4 &btstack_run_loop_windows_remove_data_source, 5 &btstack_run_loop_windows_enable_data_source_callbacks, 6 &btstack_run_loop_windows_disable_data_source_callbacks, 7 &btstack_run_loop_windows_set_timer, 8 &btstack_run_loop_windows_add_timer, 9 &btstack_run_loop_windows_remove_timer, 10 &btstack_run_loop_windows_execute, 11 &btstack_run_loop_windows_dump_timer, 12 &btstack_run_loop_windows_get_time_ms, 13 };
b. linux操作系統
Main.c (port\posix-h4)
main
btstack_run_loop_init(btstack_run_loop_posix_get_instance());
通過btstack_run_loop_posix_get_instance()來獲取一個結構體
/**
* Provide btstack_run_loop_posix instance
*/
const btstack_run_loop_t * btstack_run_loop_posix_get_instance(void){
return &btstack_run_loop_posix;
}
btstack_run_loop_posix結構體定義如下:定義了操作系統先關的循環函數。
1 static const btstack_run_loop_t btstack_run_loop_posix = { 2 &btstack_run_loop_posix_init, 3 &btstack_run_loop_posix_add_data_source, 4 &btstack_run_loop_posix_remove_data_source, 5 &btstack_run_loop_posix_enable_data_source_callbacks, 6 &btstack_run_loop_posix_disable_data_source_callbacks, 7 &btstack_run_loop_posix_set_timer, 8 &btstack_run_loop_posix_add_timer, 9 &btstack_run_loop_posix_remove_timer, 10 &btstack_run_loop_posix_execute, 11 &btstack_run_loop_posix_dump_timer, 12 &btstack_run_loop_posix_get_time_ms, 13 };
3. 在主循環中讀取數據、處理數據,它是如何用代碼來實現的呢?
例如:如果我使用H5協議的話,從硬件中得到數據,需要將這個數據的頭部去掉,才能得到真正的數據。如果我使用H4協議的話,從硬件中得到的數據就是真正的數據。
如果我使用usb協議的話,得到的數據又需要作另一種處理。從這個地方就可以看出某種端倪來,什么端倪呢?得到數據和處理數據應該綁定在一起。
即使用A協議得到數據,就使用process_A來處理;使用B協議來得到數據,就使用process_B來處理。在BTStack中,又抽象處理另外一個結構體,該結構體就是btstack_data_source。

上面已經提到操作系統相關的代碼時,在結構體btstack_run_loop中它有一個函數指針void (*add_data_source)(btstack_data_source_t * data_source),就是給這個循環添加一個數據來源。這個數據來源里面有文件句柄或handle、process函數。以后在循環里面,它可以通過文件句柄或handle中獲取數據,得到數據后馬上調用它里面的process函數。問題來了,btstack_data_source結構體是在什么時候創建的?顯然應該在打開硬件設備時,就會創建這個結構體,並且把這格結構體添加到btstack_run_loop中。
4.數據如何進行處理的呢?
process函數就是數據處理的起點,前面已經說過,對數據的處理分為兩部分:一部分是藍牙協議棧中各個層次的處理,另一個部分是APP的處理。
data_source結構體中有process函數,process函數就是數據處理的起點,在這里會干什么事情呢?它會調用各個層的處理函數,也會調用APP的處理函數。
看一下函數usb_process_event_in,在里面會做什么事情呢?
usb_process_event_in
// notify uppper 通知更上面的層次
packet_handler(HCI_EVENT_PACKET, hci_event_in_buffer, bytes_read);
packet_handler是一個函數指針,static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = &usb_dummy_handler;
該函數指針在哪里被設置呢?
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
log_info("registering packet handler");
packet_handler = handler;
}
在硬件相關的結構體hci_transport_usb里面,有一個注冊函數usb_register_packet_handler。

對於usb藍牙模塊,它使用hci_transport_h2_winusb.c文件中抽象出來的hci_transport_usb結構體,在這個結構體里面有usb_register_packet_handler函數,
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
log_info("registering packet handler");
packet_handler = handler; 對函數指針packet_handler進行賦值。
}
此處的handler是什么?就需要看看誰調用了register_packet_handler函數指針,調用了register_packet_handler函數指針,就相當於調用了usb_register_packet_handler函數
在hci.c文件中的hci_init函數中調用了register_packet_handler函數指針。
hci_init
// register packet handlers with transport
transport->register_packet_handler(&packet_handler); 從這個地方可以看出,上面的handler就是hci.c文件中的packet_handler
再看一下參數packet_handler
static void packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
hci_dump_packet(packet_type, 1, packet, size);
switch (packet_type) {
case HCI_EVENT_PACKET:
event_handler(packet, size);
break;
case HCI_ACL_DATA_PACKET:
acl_handler(packet, size);
break;
#ifdef ENABLE_CLASSIC
case HCI_SCO_DATA_PACKET:
sco_handler(packet, size);
break;
#endif
default:
break;
}
}
在主循環中,得到數據之后,會調用btstack_data_source中的process函數,在process函數中最終會進行packet_handler = handler這樣的賦值操作。最終會調用到hci.c中的packet_handler函數。在該函數中將數據分為了三類:HCI_EVENT_PACKET、HCI_ACL_DATA_PACKET、HCI_SCO_DATA_PACKET。根據類別的不同調用不同的處理函數。
event_handler(packet, size);
hci_emit_event
// dispatch to all event handlers 分發給所有的事件處理器,那么這些event handler保存在event_handlers鏈表中。
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &hci_stack->event_handlers);從鏈表中將handler一個個取出來,調用那些結構體中的callback函數來處理那些數據。
while (btstack_linked_list_iterator_has_next(&it)){
btstack_packet_callback_registration_t * entry = (btstack_packet_callback_registration_t*) btstack_linked_list_iterator_next(&it);
entry->callback(HCI_EVENT_PACKET, 0, event, size);
}
問題:誰向hci_stack->event_handlers鏈表中放入handler的呢?
從圖中可以看出,上面的各個層都調用了hci_add_event_handler,上面的各層如果對數據感興趣的話就調用hci_add_event_handler函數在鏈表中添加自己的處理函數。
void hci_add_event_handler(btstack_packet_callback_registration_t * callback_handler){
btstack_linked_list_add_tail(&hci_stack->event_handlers, (btstack_linked_item_t*) callback_handler); 向event_handler鏈表中添加了btstack_packet_callback_registration_t結構體,這個結構體是什么樣的呢?
}
typedef struct {
btstack_linked_item_t item;
btstack_packet_handler_t callback; 有callback函數,剛好與上面對應起來。
} btstack_packet_callback_registration_t;
5. 誰來啟動數據傳輸?
hci_power_control會啟動藍牙模塊,向藍牙模塊發送第一個數據,以后所有的數據都會在主循環中進行的,收到數據后將調用process函數。
