参考:https://blog.csdn.net/lewanhah01/article/details/104029135
一、bstack支持的设备类型
BTStack支持多种接口的蓝牙模块,比如USB口、3线串口(TxD/RxD/Gnd)、5线串口(TxD/RxD/Gnd/CTS/RTS);所以每种接口都会有对应的代码,还区分linux和windows;
如linux: port/libusb/main.c
port/posix-h4/h5/main.c
Windows: port/windows-h4/main.c
port/windows-winusb/main.c
因为我们用的是windows的usb蓝牙模块,所以我们看port/windows-winusb/main.c
二. 分析mian函数流程
int main(int argc, const char * argv[]){
// Prevent stdout buffering
setvbuf(stdout, NULL, _IONBF, 0);
main_argc = argc;
main_argv = argv;
printf("BTstack/windows-winusb booting up\n");
#if 0
int usb_path_len = 0;
uint8_t usb_path[USB_MAX_PATH_LEN];
if (argc >= 3 && strcmp(argv[1], "-u") == 0){
// parse command line options for "-u 11:22:33"
const char * port_str = argv[2];
printf("Specified USB Path: ");
while (1){
char * delimiter;
int port = strtol(port_str, &delimiter, 16);
usb_path[usb_path_len] = port;
usb_path_len++;
printf("%02x ", port);
if (!delimiter) break;
if (*delimiter != ':' && *delimiter != '-') break;
port_str = delimiter+1;
}
printf("\n");
}
#endif
/// GET STARTED with BTstack ///
btstack_memory_init();
btstack_run_loop_init(btstack_run_loop_windows_get_instance());
// if (usb_path_len){
// hci_transport_usb_set_path(usb_path_len, usb_path);
// }
// use logger: format HCI_DUMP_PACKETLOGGER, HCI_DUMP_BLUEZ or HCI_DUMP_STDOUT
#if 1
char pklg_path[100];
strcpy(pklg_path, "hci_dump");
#if 0
if (usb_path_len){
strcat(pklg_path, "_");
strcat(pklg_path, argv[2]);
}
#endif
strcat(pklg_path, ".pklg");
printf("Packet Log: %s\n", pklg_path);
hci_dump_open(pklg_path, HCI_DUMP_PACKETLOGGER);
#else
hci_dump_open(NULL, HCI_DUMP_STDOUT);
#endif
// handle CTRL-c
signal(SIGINT, sigint_handler);
// setup USB Transport
transport = hci_transport_usb_instance();
btstack_chipset_intel_download_firmware(hci_transport_usb_instance(), &intel_firmware_done);
// go
btstack_run_loop_execute();
return 0;
}
在看main函数主体前,我们猜想,蓝牙设备的工作模式应该是处于一个循环等待数据和处理数据的状态
(1).读取数据:可以读取用户输入的指示,也可以读取蓝牙模块的数据
(2).处理数据:根据用户的指示发送数据给蓝牙模块,或处理从蓝牙模块读到的数据
要点是:硬件操作、循环、处理
所以这些应该会在代码中体现
简单看一下mian函数,大致分为下面几点
- 初始化循环结构体
- 初始化USB操作结构体
- 调用结构体的open函数
- 进入循环函数,监听数据和处理数据
- 控制器复位后的流程
2. 1. 初始化循环结构体
刚才说到,蓝牙工作模式在一个循环中,这里初始化了btstack_run_loop_t循环结构体
int main(int argc, const char * argv[]){
....
btstack_run_loop_init(btstack_run_loop_windows_get_instance());
....
}
// init must be called before any other run_loop call
void btstack_run_loop_init(const btstack_run_loop_t * run_loop){
if (the_run_loop){
log_error("ERROR: run loop initialized twice!");
while(1);
}
the_run_loop = run_loop;
the_run_loop->init();
}
传进的参数是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是一个结构体,用于循环相关
结构体的定义在:
typedef struct btstack_run_loop {
void (*init)(void);
void (*add_data_source)(btstack_data_source_t * data_source);
int (*remove_data_source)(btstack_data_source_t * data_source);
void (*enable_data_source_callbacks)(btstack_data_source_t * data_source, uint16_t callbacks);
void (*disable_data_source_callbacks)(btstack_data_source_t * data_source, uint16_t callbacks);
void (*set_timer)(btstack_timer_source_t * timer, uint32_t timeout_in_ms);
void (*add_timer)(btstack_timer_source_t *timer);
int (*remove_timer)(btstack_timer_source_t *timer);
void (*execute)(void);
void (*dump_timer)(void);
uint32_t (*get_time_ms)(void);
} btstack_run_loop_t;
对于winusb,实例化为
static const btstack_run_loop_t btstack_run_loop_windows = {
&btstack_run_loop_windows_init,
&btstack_run_loop_windows_add_data_source,
&btstack_run_loop_windows_remove_data_source,
&btstack_run_loop_windows_enable_data_source_callbacks,
&btstack_run_loop_windows_disable_data_source_callbacks,
&btstack_run_loop_windows_set_timer,
&btstack_run_loop_windows_add_timer,
&btstack_run_loop_windows_remove_timer,
&btstack_run_loop_windows_execute,
&btstack_run_loop_windows_dump_timer,
&btstack_run_loop_windows_get_time_ms,
};
这个结构体用于初始化循环所需要的参数或函数,如add_data_source,btstack_run_loop_windows_execute等。btstack_run_loop_windows_execute是循环函数,用于循环监听和处理数据,这个后面再提,我们只需要知道这里初始化了循环结构体
2. 2. 初始化USB操作结构体
int main(int argc, const char * argv[]){
....
transport = hci_transport_usb_instance();
....
}
// get usb singleton
static const hci_transport_t hci_transport_usb = {
/* const char * name; */ "H2_WINUSB",
/* void (*init) (const void *transport_config); */ &usb_init,
/* int (*open)(void); */ &usb_open,
/* int (*close)(void); */ &usb_close,
/* void (*register_packet_handler)(void (*handler)(...); */ &usb_register_packet_handler,
/* int (*can_send_packet_now)(uint8_t packet_type); */ &usb_can_send_packet_now,
/* int (*send_packet)(...); */ &usb_send_packet,
/* int (*set_baudrate)(uint32_t baudrate); */ NULL,
/* void (*reset_link)(void); */ NULL,
#ifdef ENABLE_SCO_OVER_HCI
/* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ usb_set_sco_config,
#else
/* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL,
#endif
};
const hci_transport_t * hci_transport_usb_instance(void) {
return &hci_transport_usb;
}
这里定义了对USB接口的操作方法,如打开,关闭,注册数据handler,发数据等等
usb_register_packet_handler比较重要,但是这里只是初始化,所以我们后面再将,现在猜想后面可能会先去open它
2. 3. 调用结构体的open函数
int main(int argc, const char * argv[]){
....
btstack_chipset_intel_download_firmware(hci_transport_usb_instance(), &intel_firmware_done);
....
}
这个函数有两个参数,一个参数是transport,一个是函数指针,这个函数指针目前没用调用,所以直接看进去btstack_chipset_intel_download_firmware里
void btstack_chipset_intel_download_firmware(const hci_transport_t * hci_transport, void (*callback)(int result)){
done = callback;
transport = hci_transport;;
// transport->init(NULL);
transport->register_packet_handler(&transport_packet_handler);
transport->open();
// get started
state = 0;
state_machine(NULL);
}
这里做了如下工作:
- transport = hci_transport;
- 调用hci_transport_usb的register_packet_handler注册回调函数,到数据包到达时调用transport_packet_handler,然后调用transport_send_cmd发生命令(host->controller)
- 调用transport->open()执行usb_open()函数,打开usb设备,并注册了数据处理对象struct btstack_data_source到data_sources
- 下面两句语句会导致Host向Controller发送
复位命令
,数据传输从此开始
state = 0;
state_machine(NULL);
transport->register_packet_handler(packet_handler)
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;
}
上面transport->register_packet_handler(&transport_packet_handler)注册回调函数,作为参数传入的transport_packet_handler函数定义如下:
static void transport_packet_handler (uint8_t packet_type, uint8_t *packet, uint16_t size){
UNUSED(packet_type);
// we also get events with packet_type ACL from the controller
hci_dump_packet(HCI_EVENT_PACKET, 1, packet, size);
switch (hci_event_packet_get_type(packet)){
case HCI_EVENT_COMMAND_COMPLETE:
case HCI_EVENT_VENDOR_SPECIFIC:
state_machine(packet);
break;
default:
break;
}
}
这里设置了
state = 0;
state_machine(NULL);
我们看state_machine
static void state_machine(uint8_t * packet){
intel_version_t * version;
intel_boot_params_t * boot_params;
int res;
uint16_t buffer_offset;
bd_addr_t addr;
char fw_path[300];
if (packet){
// firmware upload complete event?
if (packet[0] == 0xff && packet[2] == 0x06) {
vendor_firmware_complete_received = 1;
}
// command complete
if (packet[0] == 0x0e){
waiting_for_command_complete = 0;
}
}
switch (state){
case 0:
state++;
transport_send_cmd(&hci_reset);
break;
case 1:
// check if HCI Reset was supported
if (packet[0] == 0x0e && packet[1] == 0x04 && packet[3] == 0x03 && packet[4] == 0x0c && packet[5] == 0x00){
log_info("HCI Reset was successful, no need for firmware upload / or not an Intel chipset");
(*done)(0);
break;
}
// Read Intel Version
state++;
transport_send_cmd(&hci_intel_read_version);
break;
}
case 3:
省略.....
}
发现执行了state++,state变成了1,然后调用transport_send_cmd(&hci_reset);
transport_send_cmd是host层向controller层通过HCI发生指令的函数
transport_send_cmd(&hci_reset);
transport_send_cmd_va_arg(cmd, argptr);
transport_send_packet(HCI_COMMAND_DATA_PACKET, packet, size);
transport->send_packet(packet_type, (uint8_t *) packet, size);
最终会调用到transport->send_packet
static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){
switch (packet_type){
case HCI_COMMAND_DATA_PACKET:
return usb_send_cmd_packet(packet, size);
case HCI_ACL_DATA_PACKET:
return usb_send_acl_packet(packet, size);
#ifdef ENABLE_SCO_OVER_HCI
case HCI_SCO_DATA_PACKET:
return usb_send_sco_packet(packet, size);
#endif
default:
return -1;
}
}
即usb_send_cmd_packet(packet, size);
static int usb_send_cmd_packet(uint8_t *packet, int size){
// update stata before submitting transfer
usb_command_out_active = 1;
// Start trasnsfer
WINUSB_SETUP_PACKET setup_packet;
memset(&setup_packet, 0, sizeof(setup_packet));
setup_packet.RequestType = USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_INTERFACE;
setup_packet.Length = sizeof(size);
BOOL result = WinUsb_ControlTransfer(usb_interface_0_handle, setup_packet, packet, size, NULL, &usb_overlapped_command_out);
if (!result) {
if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
}
// IO_PENDING -> wait for completed
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_command_out, DATA_SOURCE_CALLBACK_WRITE);
return 0;
exit_on_error:
log_error("winusb: last error %lu", GetLastError());
return -1;
}
通过windows的函数WinUsb_ControlTransfer把命令发送过去
这里我们可以猜到前面为什么要先调用transport->open()执行usb_open(),原来是想先发生复位命令到蓝牙控制器
刚才的usb_open还没看完
// returns 0 on success, -1 otherwise
static int usb_open(void){
省略....
// try all devices
while(GetLastError() != ERROR_NO_MORE_ITEMS){
BOOL result = usb_try_open_device();
}
省略....
}
usb_open会调用usb_try_open_device去尝试打开所有的蓝牙设备(蓝牙设备通过usb插在电脑上),
static btstack_data_source_t usb_data_source_event_in;
static btstack_data_source_t usb_data_source_command_out;
static btstack_data_source_t usb_data_source_acl_in;
static btstack_data_source_t usb_data_source_acl_out;
static btstack_data_source_t usb_data_source_sco_out[SCO_RING_BUFFER_COUNT];
usb_try_open_device(){
// setup btstack data soures
usb_data_source_event_in.source.handle = usb_overlapped_event_in.hEvent;
btstack_run_loop_set_data_source_handler(&usb_data_source_event_in, &usb_process_event_in);
btstack_run_loop_add_data_source(&usb_data_source_event_in);
usb_data_source_command_out.source.handle = usb_overlapped_command_out.hEvent;
btstack_run_loop_set_data_source_handler(&usb_data_source_command_out, &usb_process_command_out);
btstack_run_loop_add_data_source(&usb_data_source_command_out);
usb_data_source_acl_in.source.handle = usb_overlapped_acl_in.hEvent;
btstack_run_loop_set_data_source_handler(&usb_data_source_acl_in, &usb_process_acl_in);
btstack_run_loop_add_data_source(&usb_data_source_acl_in);
usb_data_source_acl_out.source.handle = usb_overlapped_acl_out.hEvent;
btstack_run_loop_set_data_source_handler(&usb_data_source_acl_out, &usb_process_acl_out);
btstack_run_loop_add_data_source(&usb_data_source_acl_out);
}
这里看不太懂,看起来是去先set_data_source_handler和add_data_source,这个data_source是啥,不懂,先跳过,后面自会揭晓
数据的通讯方Host、Controller之间,对于BLE,有如下几种数据的传输:
command (Host -> Controller)
event (Controller -> Host)
acl_out (Host -> Controller)
acl_in (Controller -> Host)
command 是用于发送命令
event 是用于发送事件
acl_out和acl_in是发送数据和数据
在主单元和从单元之间,可以确定不同的类型的蓝牙物理链路:ACL(AsynchronousConnectionless),和另一种链路是SCO(Synchronous
Connection Oriented)。SCO主要用于同步话音传送,ACL主要用于分组数据传送。
目前作为Host端为什么还有usb_data_source_command_out、usb_data_source_acl_out?不是接收到Controller的数据才产生事件吗?原因是为了通知上层command和acl data已经发送完成,可以继续发送,所以发送完成后也加入事件触发。
触发流程是这样的(以command为例,acl_out 也一样):
usb_send_packet(hci_transport_h2_winusb.c)
usb_send_cmd_packet(packet, size);
//传输完成时触发usb_overlapped_command_out的事件
WinUsb_ControlTransfer(usb_interface_0_handle, setup_packet, packet, size, NULL, &usb_overlapped_command_out);
2. 4. 进入循环函数,监听数据和处理数据
int main(int argc, const char * argv[]){
....
btstack_run_loop_execute();
....
}
在函数btstack_run_loop_execute中运行的the_run_loop->execute(),其定义如下:
static void btstack_run_loop_windows_execute(void) {
btstack_timer_source_t *ts;
btstack_linked_list_iterator_t it;
while (1) {
// collect handles to wait for
HANDLE handles[100];
memset(handles, 0, sizeof(handles));
int num_handles = 0;
btstack_linked_list_iterator_init(&it, &data_sources);
while (btstack_linked_list_iterator_has_next(&it)){
btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it);
if (ds->source.handle == 0) continue;
if (ds->flags & (DATA_SOURCE_CALLBACK_READ | DATA_SOURCE_CALLBACK_WRITE)){
handles[num_handles++] = ds->source.handle;
log_debug("btstack_run_loop_execute adding handle %p", ds->source.handle);
}
}
// get next timeout
int32_t timeout_ms = INFINITE;
if (timers) {
ts = (btstack_timer_source_t *) timers;
uint32_t now_ms = btstack_run_loop_windows_get_time_ms();
timeout_ms = ts->timeout - now_ms;
if (timeout_ms < 0){
timeout_ms = 0;
}
log_debug("btstack_run_loop_execute next timeout in %u ms", timeout_ms);
}
int res;
if (num_handles){
// wait for ready Events or timeout
res = WaitForMultipleObjects(num_handles, &handles[0], 0, timeout_ms);
} else {
// just wait for timeout
Sleep(timeout_ms);
res = WAIT_TIMEOUT;
}
// process data source
if (WAIT_OBJECT_0 <= res && res < (WAIT_OBJECT_0 + num_handles)){
void * triggered_handle = handles[res - WAIT_OBJECT_0];
btstack_linked_list_iterator_init(&it, &data_sources);
while (btstack_linked_list_iterator_has_next(&it)){
btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it);
log_debug("btstack_run_loop_windows_execute: check ds %p with handle %p\n", ds, ds->source.handle);
if (triggered_handle == ds->source.handle){
if (ds->flags & DATA_SOURCE_CALLBACK_READ){
log_debug("btstack_run_loop_windows_execute: process read ds %p with handle %p\n", ds, ds->source.handle);
ds->process(ds, DATA_SOURCE_CALLBACK_READ);
} else if (ds->flags & DATA_SOURCE_CALLBACK_WRITE){
log_debug("btstack_run_loop_windows_execute: process write ds %p with handle %p\n", ds, ds->source.handle);
ds->process(ds, DATA_SOURCE_CALLBACK_WRITE);
}
break;
}
}
}
// process timers
uint32_t now_ms = btstack_run_loop_windows_get_time_ms();
while (timers) {
ts = (btstack_timer_source_t *) timers;
if (ts->timeout > now_ms) break;
log_debug("btstack_run_loop_windows_execute: process timer %p\n", ts);
// remove timer before processing it to allow handler to re-register with run loop
btstack_run_loop_windows_remove_timer(ts);
ts->process(ts);
}
}
}
btstack_run_loop_execute的工作可总结为如下:
- 循环遍历data_sources,把挂到链表的所有struct btstack_data_source结构体实例的handle成员保存到数组handles中;
- 调用WaitForMultipleObjects(参数为handles数组和超时时间),等待事件或超时,该函数类似于linux的select函数(port/libusb/main.c);
- 当有事件发生或者超时,遍历data_sources链表中的struct btstack_data_source结构体实例,找到产生event的实例,调用其process回调函数;
- 如果设置了超时处理并且超时发生,则当调用struct btstack_timer_source结构体实例的process回调函数;
- 循环上述过程…
可知btstack_run_loop_windows_execute和 data_sources密切相关,在while循环里面取得data_sources链表的成员调用。
typedef struct btstack_data_source {
// linked item
btstack_linked_item_t item;
// item to watch in run loop
union {
// file descriptor for posix systems
int fd;
// handle on windows
void * handle;
} source; //windows的WaitForMultipleObjects接口通过handle/fd等待事件产生
// callback to call for enabled callback types
//btstack_run_loop_windows_execute中事件产生后回调process
void (*process)(struct btstack_data_source *ds, btstack_data_source_callback_type_t callback_type);
// flags storing enabled callback types
uint16_t flags;
} btstack_data_source_t;
这里看到一个fd,handle,process。
那data_sources的成员由哪里加入链表呢?通过下面的接口btstack_run_loop_windows_add_data_source:
static const btstack_run_loop_t btstack_run_loop_windows = {
&btstack_run_loop_windows_init,
&btstack_run_loop_windows_add_data_source,
&btstack_run_loop_windows_remove_data_source,
&btstack_run_loop_windows_enable_data_source_callbacks,
&btstack_run_loop_windows_disable_data_source_callbacks,
&btstack_run_loop_windows_set_timer,
&btstack_run_loop_windows_add_timer,
&btstack_run_loop_windows_remove_timer,
&btstack_run_loop_windows_execute,
&btstack_run_loop_windows_dump_timer,
&btstack_run_loop_windows_get_time_ms,
};
想到刚才usb_try_open_device里不理解的btstack_run_loop_add_data_source,现在对应上了
/**
* Add data_source to run_loop
*/
void btstack_run_loop_add_data_source(btstack_data_source_t *ds){
btstack_run_loop_assert();
if (the_run_loop->add_data_source){
the_run_loop->add_data_source(ds);
} else {
log_error("btstack_run_loop_add_data_source not implemented");
}
}
看到这里,我们发现蓝牙循环处理数据,需要去open蓝牙设备,获取fd的句柄,以区分不懂的蓝牙设备,然后去获取它的data_source,并根据data_source里的事件,去调用process回调函数
总结一下
-
transport->open();打开usb设备,并往data_sources链入struct btstack_data_source结构体对象(针对command、event 、acl data,acl data包括acl in 和 acl out)
-
state = 0; state_machine(NULL);Host向Controller发送复位命令,Host与Controller交互开始,随后Controller返回event
-
btstack_run_loop_execute();遍历data_sources中的struct btstack_data_source结构体对象,等待事件产生,调用struct btstack_data_source结构体对象的process函数。此步循环进行
2. 5. 控制器复位后的流程
前面说到主机发出复位命令后,控制器复位,复位后应该会响应数据回主机,由transport_packet_handler 处理
static void transport_packet_handler (uint8_t packet_type, uint8_t *packet, uint16_t size){
UNUSED(packet_type);
// we also get events with packet_type ACL from the controller
hci_dump_packet(HCI_EVENT_PACKET, 1, packet, size);
switch (hci_event_packet_get_type(packet)){
case HCI_EVENT_COMMAND_COMPLETE:
case HCI_EVENT_VENDOR_SPECIFIC:
state_machine(packet);
break;
default:
break;
}
注意注释// we also get events with packet_type ACL from the controller
进入state_machine(packet)
static void state_machine(uint8_t * packet){
if (packet){
...
// command complete
if (packet[0] == 0x0e){
waiting_for_command_complete = 0;
}
}
switch (state){
...
case 1:
// check if HCI Reset was supported
if (packet[0] == 0x0e && packet[1] == 0x04 && packet[3] == 0x03 && packet[4] == 0x0c && packet[5] == 0x00){
log_info("HCI Reset was successful, no need for firmware upload / or not an Intel chipset");
(*done)(0);
break;
}
// Read Intel Version
state++;
transport_send_cmd(&hci_intel_read_version);
break;
...
}
}
前面说到state已经变成1了,百度搜得知控制器复位成功后,控制器响应数据,此时packet的内容(0e 04 01 03 0c 00),于是调用(*done)(0),执行函数指针指向的intel_firmware_done函数,intel_firmware_done是一开始main函数里面的btstack_chipset_intel_download_firmware里的函数指针参数
btstack_chipset_intel_download_firmware(hci_transport_usb_instance(), &intel_firmware_done);
intel_firmware_done
static void intel_firmware_done(int result){
printf("Done %x\n", result);
// close
transport->close();
//
// init HCI
hci_init(transport, NULL);
#ifdef ENABLE_CLASSIC
hci_set_link_key_db(btstack_link_key_db_fs_instance());
#endif
// inform about BTstack state
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
// setup app
btstack_main(main_argc, main_argv);
}
这个函数做了如下几件大事:
intel_firmware_done
关闭usb设备,后面重新打开
transport->close();
hci_init(transport, NULL);
创建hci_stack并进行初始化
transport->register_packet_handler(&packet_handler); 重新注册了usb的回调函数
/*
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;
}
}
*/
这是上层应用的入口,上层应用可以是可client、service、可以是包含电池服务的外围设备或心跳服务的外围设备等等。
btstack_main(main_argc, main_argv);
hci.c提供的packet_handler函数,根据packet_type的不同,分别处理。
有3类packet_type:event、acl data、soc data。
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_t结构体中的process函数是数据处理的起点
,该“起点
”会调用hci.c中的packet_handler函数。
那么hci.c中的packet_handler可以认为是数据处理的分发站。
三、蓝牙协议栈主函数btstack_main
前面说到的大部分工作都是HCI和控制器的事,好像涉及到协议栈的其他层,到了btstack_main函数,开始涉及蓝牙协议栈的各个层次。
在btstack/example目录下,每个文件都是一个示例程序,每个示例程序都有一个btstack_main函数,每个示例程序可以扮演client、service、可以是包含电池服务的外围设备或心跳服务的外围设备等等。
本文分析以le_data_channel_client.c为例,其btstack_main定义如下:
int btstack_main(int argc, const char * argv[]){
#ifdef HAVE_BTSTACK_STDIN
int arg = 1;
cmdline_addr_found = 0;
while (arg < argc) {
if(!strcmp(argv[arg], "-a") || !strcmp(argv[arg], "--address")){
arg++;
cmdline_addr_found = sscanf_bd_addr(argv[arg], cmdline_addr);
arg++;
if (!cmdline_addr_found) exit(1);
continue;
}
usage(argv[0]);
return 0;
}
#else
(void)argc;
(void)argv;
#endif
l2cap_init();
// Initialize GATT client
gatt_client_init();
sm_init();
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
btstack_stdin_setup(stdin_process);
// turn on!
hci_power_control(HCI_POWER_ON);
return 0;
}
//l2cap初始化
l2cap_init();
//gatt client 初始化
gatt_client_init();
//安全管理模块初始化
sm_init();
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
这些先不具体分析,后面的详细介绍,先主要分析下:
// turn on!
hci_power_control(HCI_POWER_ON);
hci_power_control根据hci_stack->state的值进入相应的分支,当前hci_stack->state = HCI_STATE_OFF,所以执行如下:
int hci_power_control(HCI_POWER_MODE power_mode){
log_info("hci_power_control: %d, current mode %u", power_mode, hci_stack->state);
int err = 0;
switch (hci_stack->state){
case HCI_STATE_OFF:
switch (power_mode){
case HCI_POWER_ON:
err = hci_power_control_on();
if (err) {
log_error("hci_power_control_on() error %d", err);
return err;
}
hci_power_transition_to_initializing();
break;
case HCI_POWER_OFF:
// do nothing
break;
case HCI_POWER_SLEEP:
// do nothing (with SLEEP == OFF)
break;
}
break;
...
}
// create internal event
hci_emit_state();
// trigger next/first action
hci_run();
return 0;
}
上述过程中,usb被重新打开:
hci_power_control_on();
hci_stack->hci_transport->init(hci_stack->config);
hci_stack->hci_transport->open();
hci_stack相关状态设置:
hci_power_transition_to_initializing();
hci_stack->state = HCI_STATE_INITIALIZING;
hci_stack->substate = HCI_INIT_SEND_RESET;
创建内部事件:
hci_emit_state();
event[0] = BTSTACK_EVENT_STATE; // BTSTACK_EVENT_STATE = 0x60
event[1] = sizeof(event) - 2;
event[2] = hci_stack->state;
hci_emit_event(event, sizeof(event), 1);
运行
// trigger next/first action
hci_run();
先上一个图
hci_run是根据hci_stack结构体的各种状态值条件执行的,在运行hci_run()之前,hci_stack主要的状态值如下:
hci_stack->state = HCI_STATE_INITIALIZING;
hci_stack->substate = HCI_INIT_SEND_RESET;
hci_run函数非常长,它根据hci_stack各个状态值选择执行不同的流程,根据当前hci_stack的值,hci_run执行如下:
static void hci_run(void){
...
switch (hci_stack->state){
case HCI_STATE_INITIALIZING:
hci_initializing_run();
break;
...
}
}
hci_initializing_run函数定义如下:
static void hci_initializing_run(void){
switch (hci_stack->substate){
case HCI_INIT_SEND_RESET:
hci_state_reset();
hci_stack->substate = HCI_INIT_W4_SEND_RESET;
hci_send_cmd(&hci_reset);
break;
case HCI_INIT_SEND_READ_LOCAL_VERSION_INFORMATION:
hci_send_cmd(&hci_read_local_version_information);
hci_stack->substate = HCI_INIT_W4_SEND_READ_LOCAL_VERSION_INFORMATION;
break;
case HCI_INIT_SEND_READ_LOCAL_NAME:
hci_send_cmd(&hci_read_local_name);
hci_stack->substate = HCI_INIT_W4_SEND_READ_LOCAL_NAME;
break;
...
default:
return;
}
}
因为 hci_stack->substate = HCI_INIT_SEND_RESET,所以这里主机发送了软复位命令,并且hci_stack->substate设置为HCI_INIT_W4_SEND_RESET。
之后便是控制器返回响应数据包(事件),导致event_handler被调用,event_handler函数也很长,但很好理解:
static void event_handler(uint8_t *packet, int size){
switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_COMMAND_COMPLETE:
hci_stack->num_cmd_packets = packet[2] ? 1 : 0;
//判断是否为读取本地名的返回数据包
if (HCI_EVENT_IS_COMMAND_COMPLETE(packet, hci_read_local_name)){
if (packet[5]) break;
// terminate, name 248 chars
packet[6+248] = 0;
log_info("local name: %s", &packet[6]);
}
//判断是否读取缓存大小的返回数据包
if (HCI_EVENT_IS_COMMAND_COMPLETE(packet, hci_read_buffer_size)){
...
}
...
break;
case HCI_EVENT_COMMAND_STATUS:
...
break;
case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS:
...
break;
...
default:
break;
}
// handle BT initialization
if (hci_stack->state == HCI_STATE_INITIALIZING){
hci_initializing_event_handler(packet, size);
}
// help with BT sleep
if (hci_stack->state == HCI_STATE_FALLING_ASLEEP
&& hci_stack->substate == HCI_FALLING_ASLEEP_W4_WRITE_SCAN_ENABLE
&& HCI_EVENT_IS_COMMAND_COMPLETE(packet, hci_write_scan_enable)){
hci_initializing_next_state();
}
// notify upper stack
hci_emit_event(packet, size, 0); // don't dump, already happened in packet handler
// moved here to give upper stack a chance to close down everything with hci_connection_t intact
if (hci_event_packet_get_type(packet) == HCI_EVENT_DISCONNECTION_COMPLETE){
if (!packet[2]){
handle = little_endian_read_16(packet, 3);
hci_connection_t * aConn = hci_connection_for_handle(handle);
if (aConn) {
uint8_t status = aConn->bonding_status;
uint16_t flags = aConn->bonding_flags;
bd_addr_t bd_address;
memcpy(&bd_address, aConn->address, 6);
hci_shutdown_connection(aConn);
// connection struct is gone, don't access anymore
if (flags & BONDING_EMIT_COMPLETE_ON_DISCONNECT){
hci_emit_dedicated_bonding_result(bd_address, status);
}
}
}
}
// execute main loop
hci_run();
}
前面有提到过,对于软复位控制器返回的数据包数据为(0e 04 01 03 0c 00),为HCI_EVENT_COMMAND_COMPLETE通用命令完成事件,所以在上面会进入对应的switch分支(但实际后面不满足条件判断,没有处理),记住此时hci_stack->state为HCI_STATE_INITIALIZING,hci_stack->substate设置为HCI_INIT_W4_SEND_RESET。所以将执行hci_initializing_event_handler(packet, size);
static void hci_initializing_event_handler(uint8_t * packet, uint16_t size){
if (hci_event_packet_get_type(packet) == HCI_EVENT_COMMAND_COMPLETE){
uint16_t opcode = little_endian_read_16(packet,3);
if (opcode == hci_stack->last_cmd_opcode){
command_completed = 1;
} else {
}
}
if (hci_event_packet_get_type(packet) == HCI_EVENT_COMMAND_STATUS){
uint8_t status = packet[2];
uint16_t opcode = little_endian_read_16(packet,4);
if (opcode == hci_stack->last_cmd_opcode){
if (status){
command_completed = 1;
}
}
}
if (!command_completed) return;
switch(hci_stack->substate){
case HCI_INIT_W4_SEND_RESET:
hci_stack->substate = HCI_INIT_READ_LOCAL_SUPPORTED_COMMANDS;
return ;
case HCI_INIT_W4_READ_LOCAL_SUPPORTED_COMMANDS:
if (need_baud_change &&
((hci_stack->manufacturer == BLUETOOTH_COMPANY_ID_BROADCOM_CORPORATION) ||
(hci_stack->manufacturer == BLUETOOTH_COMPANY_ID_EM_MICROELECTRONIC_MARIN_SA))) {
hci_stack->substate = HCI_INIT_SEND_BAUD_CHANGE_BCM;
return;
}
if (need_addr_change){
hci_stack->substate = HCI_INIT_SET_BD_ADDR;
return;
}
hci_stack->substate = HCI_INIT_READ_BD_ADDR;
return;
case HCI_INIT_W4_READ_BD_ADDR:
// only read buffer size if supported
if (hci_stack->local_supported_commands[0] & 0x01) {
hci_stack->substate = HCI_INIT_READ_BUFFER_SIZE;
return;
}
// skipping read buffer size
hci_stack->substate = HCI_INIT_READ_LOCAL_SUPPORTED_FEATURES;
return;
case HCI_INIT_W4_SET_EVENT_MASK:
// skip Classic init commands for LE only chipsets
if (!hci_classic_supported()){
#ifdef ENABLE_BLE
if (hci_le_supported()){
hci_stack->substate = HCI_INIT_LE_READ_BUFFER_SIZE; // skip all classic command
return;
}
#endif
log_error("Neither BR/EDR nor LE supported");
hci_init_done();
return;
}
if (!gap_ssp_supported()){
hci_stack->substate = HCI_INIT_WRITE_PAGE_TIMEOUT;
return;
}
break;
#ifdef ENABLE_BLE
case HCI_INIT_W4_LE_READ_BUFFER_SIZE:
// skip write le host if not supported (e.g. on LE only EM9301)
if (hci_stack->local_supported_commands[0] & 0x02) break;
hci_stack->substate = HCI_INIT_LE_SET_EVENT_MASK;
return;
#ifdef ENABLE_LE_DATA_LENGTH_EXTENSION
case HCI_INIT_W4_WRITE_LE_HOST_SUPPORTED:
log_info("Supported commands %x", hci_stack->local_supported_commands[0] & 0x30);
if ((hci_stack->local_supported_commands[0] & 0x30) == 0x30){
hci_stack->substate = HCI_INIT_LE_SET_EVENT_MASK;
return;
}
// explicit fall through to reduce repetitions
#ifdef ENABLE_LE_CENTRAL
hci_stack->substate = HCI_INIT_READ_WHITE_LIST_SIZE;
#else
hci_init_done();
#endif
return;
#endif /* ENABLE_LE_DATA_LENGTH_EXTENSION */
#endif /* ENABLE_BLE */
// avoid compile error due to duplicate cases: HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT == HCI_INIT_DONE-1
#if defined(ENABLE_BLE) || defined(ENABLE_LE_DATA_LENGTH_EXTENSION) || defined(ENABLE_LE_CENTRAL)
// Response to command before init done state -> init done
case (HCI_INIT_DONE-1):
hci_init_done();
return;
#endif
default:
break;
}
hci_initializing_next_state();
}
控制器正常返回的情况下,hci_stack->substate设置为HCI_INIT_READ_LOCAL_SUPPORTED_COMMANDS。
回到前面event_handler将继续向下执行,将执行:
hci_emit_event(packet, size, 0);
之后又是:
hci_run(); // hci_stack->state == HCI_STATE_INITIALIZING
...
hci_initializing_run(); // hci_stack->substate == HCI_INIT_READ_LOCAL_SUPPORTED_COMMANDS
hci_stack->substate = HCI_INIT_W4_READ_LOCAL_SUPPORTED_COMMANDS;
hci_send_cmd(&hci_read_local_supported_commands);
就这样,主机会像控制器发送一系列的初始化命令,直到初始化完成,然后将hci_stack->state设置为HCI_STATE_WORKING。
event_handler
hci_initializing_event_handler
//完成必要的初始化设置后,就可以结束初始化
hci_init_done();
当蓝牙模块返回数据给应用程序时,应用程序的packet_handler都会被调用,我们看一下应用层的packet_handler做什么事情
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
bd_addr_t event_address;
uint16_t psm;
uint16_t cid;
uint16_t conn_interval;
hci_con_handle_t handle;
switch (packet_type) {
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case BTSTACK_EVENT_STATE:
// BTstack activated, get started
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
le_data_channel_client_start();
} else {
state = TC_OFF;
}
break;
case GAP_EVENT_ADVERTISING_REPORT:
if (state != TC_W4_SCAN_RESULT) return;
// check name in advertisement
if (!advertisement_report_contains_name("LE Data Channel", packet)) return;
// store address and type
gap_event_advertising_report_get_address(packet, le_data_channel_addr);
le_data_channel_addr_type = gap_event_advertising_report_get_address_type(packet);
// stop scanning, and connect to the device
state = TC_W4_CONNECT;
gap_stop_scan();
printf("Stop scan. Connect to device with addr %s.\n", bd_addr_to_str(le_data_channel_addr));
gap_connect(le_data_channel_addr,le_data_channel_addr_type);
break;
case HCI_EVENT_LE_META:
// wait for connection complete
if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break;
if (state != TC_W4_CONNECT) return;
connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet);
// print connection parameters (without using float operations)
conn_interval = hci_subevent_le_connection_complete_get_conn_interval(packet);
printf("Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
printf("Connection Latency: %u\n", hci_subevent_le_connection_complete_get_conn_latency(packet));
// initialize gatt client context with handle, and add it to the list of active clients
// query primary services
printf("Connect to performance test channel.\n");
state = TC_W4_CHANNEL;
l2cap_le_create_channel(&packet_handler, connection_handle, TSPX_le_psm, data_channel_buffer,
sizeof(data_channel_buffer), L2CAP_LE_AUTOMATIC_CREDITS, LEVEL_0, &le_data_channel_connection.cid);
break;
case HCI_EVENT_DISCONNECTION_COMPLETE:
if (cmdline_addr_found){
printf("Disconnected %s\n", bd_addr_to_str(cmdline_addr));
return;
}
printf("Disconnected %s\n", bd_addr_to_str(le_data_channel_addr));
if (state == TC_OFF) break;
le_data_channel_client_start();
break;
case L2CAP_EVENT_LE_CHANNEL_OPENED:
// inform about new l2cap connection
l2cap_event_le_channel_opened_get_address(packet, event_address);
psm = l2cap_event_le_channel_opened_get_psm(packet);
cid = l2cap_event_le_channel_opened_get_local_cid(packet);
handle = l2cap_event_le_channel_opened_get_handle(packet);
if (packet[2] == 0) {
printf("L2CAP: LE Data Channel successfully opened: %s, handle 0x%02x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x\n",
bd_addr_to_str(event_address), handle, psm, cid, little_endian_read_16(packet, 15));
le_data_channel_connection.cid = cid;
le_data_channel_connection.connection_handle = handle;
le_data_channel_connection.test_data_len = btstack_min(l2cap_event_le_channel_opened_get_remote_mtu(packet), sizeof(le_data_channel_connection.test_data));
state = TC_TEST_DATA;
printf("Test packet size: %u\n", le_data_channel_connection.test_data_len);
test_reset(&le_data_channel_connection);
#ifdef TEST_STREAM_DATA
l2cap_le_request_can_send_now_event(le_data_channel_connection.cid);
#endif
} else {
printf("L2CAP: LE Data Channel connection to device %s failed. status code %u\n", bd_addr_to_str(event_address), packet[2]);
}
break;
#ifdef TEST_STREAM_DATA
case L2CAP_EVENT_LE_CAN_SEND_NOW:
streamer();
break;
#endif
case L2CAP_EVENT_LE_CHANNEL_CLOSED:
cid = l2cap_event_le_channel_closed_get_local_cid(packet);
printf("L2CAP: LE Data Channel closed 0x%02x\n", cid);
break;
default:
break;
}
break;
case L2CAP_DATA_PACKET:
test_track_data(&le_data_channel_connection, size);
break;
default:
break;
}
}
发现hci_event为BTSTACK_EVENT_STATE时,会去判断BTstack的状态是不是HCI_STATE_WORKING,是的话,调用le_data_channel_client_start(),否则设置state = TC_OFF
我们应用程序不关心整个初始化过程,初始化过程是在hci.c来实现的
,并且使用状态机hci_run来处理和更新各种状态
四、各层如何处理数据
hci.c提供的packet_handler函数,根据packet_type的不同,分别处理。
有3类packet_type:event、acl data、soc data。
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;
}
}
hci.c中的packet_handler可以认为是数据处理的分发站。
HCI层可以处理这些数据时,就由HCI层来处理,处理不了就上报。
L2CAP、SM、GATT、GAP、APP等等,都可以提供处理函数。
① 对于EVENT数据:
hci.c中的event_handler函数用来处理这些数据,
在HCI层能处理的数据将被用来设置hci_stack结构体;
有必要上报的数据,通过hci_emit_event函数上报。
hci_emit_event函数会调用hci_stack->event_handlers链表中的各个callback函数。该链表中的函数,来自上面各层,它们通过hci_add_event_handler注册这些callback函数。
调用hci_add_event_handler的文件有:
- le_data_channel_client.c属于APP,即APP可以处理感兴趣的数据;
- main.c中注册的函数没什么用处,就是在蓝牙模块初始化完毕后,打印一个提示信息:“BTstack up and running at …”;
- gatt_client.c这个文件,在le_data_channel_client.c这个APP中没被使用;
- att_Server.c这个文件是用于ATT Server的,在le_data_channel_client.c这个APP中没被使用;
- btstack_crypto.c、sm.c都是用于安全管理;
- l2cap.c,这很重要,ATT、GATT、GAP、SM各层都要使用L2CAP来收、发数据。
② 对于ACL数据:
hci.c中的acl_handler函数用来处理这些数据,
在HCI能处理的数据将被用来设置对应的hci_connection_t结构体,
有必要上报的数据,通过hci_emit_acl_packet函数上报。
hci_emit_acl_packet函数会调用hci_stack->acl_packet_handler来处理。
在l2cap.c中,通过如下调用设置hci_stack->acl_packet_handler:
hci_register_acl_packet_handler(&l2cap_acl_handler);
即:上报的ACL数据,将由L2CAP的l2cap_acl_handler函数来处理。
③ 对于SCO数据:
hci.c中的sco_handler函数用来处理这些数据,
在HCI能处理的数据将被用来设置对应的hci_connection_t结构体,
有必要上报的数据,通过hci_stack->sco_packet_handler函数上报。
APP如果要处理SCO数据,可以通过hci_register_sco_packet_handler提供自己的处理函数。hci_register_sco_packet_handler就是直接设置hci_stack->sco_packet_handler。
前面说到HCI给蓝牙模块发送的第1个命令是hci_power_control(HCI_POWER_ON)
这会导致给蓝牙模块发送一系列的初始化命令,初始化成功后,APP通过hci_add_event_handler注册的函数就会被调用,在这个函数里就可以执行我们自己的代码,比如发起scan、发起connection等等。
五、状态机
我们常说:初始化好蓝牙模块后,就可以使用它了。
仔细琢磨这句话,蓝牙模块至少有这2个状态:
- 初始化状态:HCI_STATE_INITIALIZING
- 工作状态:HCI_STATE_WORKING
当然,还有其他状态,在代码中如下表示(hci_cmd.h):
在HCI_STATE_INITIALIZING状态下,需要跟蓝牙模块多次交互,才可以完成蓝牙模块的初始化。之后将状态变为HCI_STATE_WORKING
hci_run是hci状态的状态机,根据状态来处理不同的操作
其实不知道hci,协议栈的各层也有类似的状态机,如
l2cap_run
sm_run
他们都维持自己的状态,并根据状态进行处理,如sm_run根据sm_sc_oob_state来执行不同的安全机制,l2cap_run根据L2CAP_STATE执行不同的通道处理等
六、通过hci_dump.pklg解读程序
前面在mian.c中,会调用hci_dump_open,这个会在运行BTStack程序时生成hci_dump.pklg文件,这个文件保留了HCI的log,即可以通过它来查看Host和controller的数据交互,我们用WireShark打开
截图如下:
怎么理解上图中的数据呢?
BTStack中涉及的数据有2类:
1.从硬件上获得的数据、发给硬件的数据
2.为更新系统状态而虚构的数据
跟硬件相关的数据有4类:
① 发送给蓝牙控制器的Command
② 从蓝牙控制器获得的Event,蓝牙控制器收到Command后会回复Event
③ 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数据的类型是0x02,我们单凭0x02无法知道这数据是发给硬件、还是从硬件读到。
为了便于调试,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时,观察里面原始数据。
1.从硬件上获得的数据、发给硬件的数据
对于第一个Send_Reset指令,怎么解读
根据截图可知,类型属于Command : 0x00类型,数据是03 0c 00
对于数据的定义,可以从hci_cmd.c中查到
- 为更新系统状态而虚构的数据:
有很多种虚构的数据,下面举几个例子:
① 提示状态发生了变化:
在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。
还有就是Rcvd Command Complete (Reset),它的类型是01,即Event
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。