本篇博客根據韋東山的視頻整理所得。
在上篇博客,通過閱讀BTStack的源碼,大體了解了其框架,對於任何一個BTStack的應用程序都有一個main函數,這個main函數是統一的。這個main函數做了某些初始化之后,最終會調用到應用程序提供的btstack_main,在btstack_main里面首先做一些初始化,然后調用hci_power_on函數去打開藍牙模塊。
一. 數據類型
運行BTStack程序時,會生成hci_dump.pklg文件,可以使用WireShark打開此文件,截圖如下:

怎么理解上圖中的數據呢?
BTStack中涉及的數據有2類:
1.從硬件上獲得的數據、發給硬件的數據
2.為更新系統狀態而虛構的數據

1. 跟硬件相關的數據有4類:
① 發送給藍牙控制器的Command
② 從藍牙控制器獲得的Event,藍牙控制器收到Command后會回復Event
③ ACL數據,這涉及收、發兩個方向
④ SCO數據,這涉及收、發兩個方向
注意:ACL、SCO數據的含義以后再講。
這4種數據類型,用一個頭部信息來表示,參考bluetooth.h:
#define HCI_COMMAND_DATA_PACKET 0x01
#define HCI_ACL_DATA_PACKET 0x02
#define HCI_SCO_DATA_PACKET 0x03
#define HCI_EVENT_PACKET 0x04
但是在程序中,單憑這4個數值無法分辨數據的流向,比如ACL數據的類型是0x03,我們單憑0x03無法知道這數據是發給硬件、還是從硬件讀到。
為了便於調試,BTStack在打印Log信息時,把這些硬件數據類型轉換為新數值:
參考函數: hci_dump_packetlogger_setup_header
1. Command : 0x00
2. Event: 0x01
3. ACL out 0x02
4. ACL in 0x03
5. SCO out 0x08
6. SCO in 0x09
7. Log Message 0xfc
我們可以使用WireShark打開Log文件hci_dump.pklg時,觀察里面原始數據。
2. 為更新系統狀態而虛構的數據:
有很多種虛構的數據,下面舉幾個例子:
① 提示狀態發生了變化:
在BTStack中,可能有很多層對hci_stack->state感興趣,所以當hci_stack->state發生變化時,可以使用hci_emit_state發送一個虛擬的Event數據包,這會導致這些層的處理函數被調用。
BTStack中使用下面函數發送state信息:

在WireShark中看到的原始數據為:01 60 01 xx,
第1個01表示Event,60表示BTSTACK_EVENT_STATE,第2個01表示數據長度為1, xx表示數據即state值。
② 當一個數據包已經成功發給硬件之后,我們要通知上層:你可以繼續發送數據給硬件了。這通過hci_emit_transport_packet_sent函數來實現:

在WireShark中看到的原始數據為:01 6e 00,
第1個01表示Event,6e表示HCI_EVENT_TRANSPORT_PACKET_SENT,00表示后續數據長度為0。
二、狀態機:
我們常說:初始化好藍牙模塊后,就可以使用它了。
仔細琢磨這句話,藍牙模塊至少有這2個狀態:
1. 初始化狀態:HCI_STATE_INITIALIZING
2. 工作狀態:HCI_STATE_WORKING
當然,還有其他狀態,在代碼中如下表示(hci_cmd.h):

在HCI_STATE_INITIALIZING狀態下,需要跟藍牙模塊多次交互,才可以完成藍牙模塊的初始化。使用“子狀態”來表示這些多次交互,在代碼中如下表示(hci.h):

舉個例子,子狀態中有“HCI_INIT_SEND_RESET”和“HCI_INIT_W4_SEND_RESET”:
1.當子狀態為HCI_INIT_SEND_RESET時:
我們發送復位命令給藍牙模塊,然后子狀態變為HCI_INIT_W4_SEND_RESET,它的意思是“wait for”,等待收到復位命令的回復信息。
2.收到該回復信息后,子狀態變為HCI_INIT_SEND_READ_LOCAL_VERSION_INFORMATION:
於是繼續給藍牙模塊發送“read loacal version”命令,然后子狀態變為HCI_INIT_W4_SEND_READ_LOCAL_VERSION_INFORMATION,表示等待回復信息
如此繼續,直到子狀態變為“HCI_INIT_DONE”,初始化才結束,藍牙模塊的“狀態”才放為HCI_STATE_WORKING。
代碼中有一個結構體:
static hci_stack_t * hci_stack
hci_stack->state表示“狀態”,hci_stack->substate表示“子狀態”。
BTStack的代碼有函數hci_run,它就是根據hci_stack結構體中的這些狀態、其他值來收發數據的。
注意:hci.c中的hci_run是核心函數,它根據hci_stack的狀態進行不同的處理。
舉例說明:
1.例子1:hci_power_control(HCI_POWER_ON);

hci_stack->state初始值為0,即HCI_STATE_OFF;
調用hci_power_transition_to_initializing后,各狀態值如下:
// set up state machine
hci_stack->num_cmd_packets = 1; // assume that one cmd can be sent
hci_stack->hci_packet_buffer_reserved = 0;
hci_stack->state = HCI_STATE_INITIALIZING;
hci_stack->substate = HCI_INIT_SEND_RESET;
接着調用如下代碼:
// trigger next/first action
hci_run();
hci_run函數中,在hci_stack->state等於HCI_STATE_INITIALIZING時,調用:hci_initializing_run();
hci_initializing_run()函數內部,會根據hci_stack->substate等於HCI_INIT_SEND_RESET而發出復位命令,並令substate等於HCI_INIT_W4_SEND_RESET,這表示等待收到該命令的回復信息。
在等待過程中,程序休眠。
當收到數據時,程序的主循環繼續執行,根據上節內容,將會調用hci.c中的event_handler函數來處理
該函數有如下代碼:
// handle BT initialization
if (hci_stack->state == HCI_STATE_INITIALIZING){
hci_initializing_event_handler(packet, size);
}
……
hci_run( );
模塊的當前狀態仍為HCI_STATE_INITIALIZING,進而調用hci_initializing_event_handler(packet, size)。
hci_initializing_event_handler將調用hci_initializing_next_state(),把subsate設置為HCI_INIT_SEND_READ_LOCAL_VERSION_INFORMATION。
后續的hci_run會根據這個substate發出READ_LOCAL_VERSION_INFORMATION的命令。
