第07節-開源藍牙協議BTStack框架代碼閱讀(上)


首先來看一下,對於硬件操作,它是如何來進行處理的。在上篇文章中曾說過,在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)    
View Code

看一下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 };
View Code

在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 };
View Code

注意: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 };
View Code

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 };
View Code

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函數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM