Bluedroid協議棧HCI線程分析


藍牙進程中有多個線程,其中HCI 線程是負責處理藍牙主機端和控制器的數據處理和收發的工作。

本篇文章就是分析一下該線程的數據處理流程。

1.跟HCI相關的接口

首先看看hci的相關的接口:在hci_layer.c中:

const hci_t *hci_layer_get_interface() {
  buffer_allocator = buffer_allocator_get_interface();
  hal = hci_hal_get_interface();//hal模塊
  btsnoop = btsnoop_get_interface();
  hci_inject = hci_inject_get_interface();
  packet_fragmenter = packet_fragmenter_get_interface();//組裝分塊
  vendor = vendor_get_interface();//vendor模塊
  low_power_manager = low_power_manager_get_interface();
  init_layer_interface();
  return &interface;
}

主要是結構是:hal,packet_fragmenter以及vendor,下面看看這個接口的結構:

hal模塊接口

static const hci_hal_t interface = {
  hal_init,
  hal_open,//通過vendor模塊發送指令VENDOR_OPEN_USERIAL,打開host與controller的通信節點,並且在hci線程中一直poll該節點,有數據就傳上層協議棧
  hal_close,
  read_data,
  packet_finished,
  transmit_data,
};

const hci_hal_t *hci_hal_h4_get_interface() {
  vendor = vendor_get_interface();//獲取了vendor接口
  return &interface;
}

分析代碼發現hal_open主要是通過vendor來和底層的模塊通信的。可見hal層在vendor的上面。

packet_fragmenter模塊接口

static const packet_fragmenter_t interface = {
  init,
  cleanup,
  fragment_and_dispatch,//分片,然后回調到hci_layer,通過hal層發送
  reassemble_and_dispatch//重裝,然后回調到hci_layer,塞到btu_hci_queue隊列里面
};

const packet_fragmenter_t *packet_fragmenter_get_interface() {
  controller = controller_get_interface();//獲取控制器的接口
  buffer_allocator = buffer_allocator_get_interface();
  return &interface;
}

該模塊主要負責數據的分片和重組,當hci向下發送數據的時候,會將數據放置到packet_queue,然后調用到該模塊的fragment_and_dispatch,然后經過HAL模塊發送到vendor,最后抵達controller

當controller有數據上傳的時候,底層的bt driver會將數據發送到host與controller的通信節點。hci_thread會一直poll這個節點,然后讀出數據,經過hal以及fragment_and_dispatch,最后送到btu線程。

vendor模塊接口

static const vendor_t interface = {
  vendor_open,//加載libbt-vendor模塊並對模塊初始化
  vendor_close,
  send_command,//通過libbt-vendor進行發送op命令,非hci opcode
  send_async_command,
  set_callback,
};

const vendor_t *vendor_get_interface() {
  buffer_allocator = buffer_allocator_get_interface();
  return &interface;
}

vendor模塊主要是初始化libbt-vendor模塊,一些與廠商相關的接口定義。具體的實現是廠商自己的實現,比如打開底層的通信節點,downloaf 卡片的patch等等。

 

2.線程的創建

線程的創建在hci_layer.c里面,在hci 模塊的start_up函數里面:

static future_t *start_up(void) {
  LOG_INFO("%s", __func__);
...  
command_queue = fixed_queue_new(SIZE_MAX);//創建命令隊列,用於發送命令
packet_queue = fixed_queue_new(SIZE_MAX);//創建數據隊列,用於發送數據
  thread = thread_new("hci_thread");//創建hci線程
...
  packet_fragmenter->init(&packet_fragmenter_callbacks);//初始化“組裝分塊”模塊

  fixed_queue_register_dequeue(command_queue, thread_get_reactor(thread), event_command_ready, NULL);//hci_thread綁定命令隊列
  fixed_queue_register_dequeue(packet_queue, thread_get_reactor(thread), event_packet_ready, NULL);//hci_thread 綁定數據隊列
...
  vendor->open(btif_local_bd_addr.address, &interface);//調用vendor模塊的open
  hal->init(&hal_callbacks, thread);//初始化hal模塊
...
 thread_post(thread, event_finish_startup, NULL);//繼續完成hci模塊的啟動工作,這里主要做的是繼續初始化vendor模塊
...

 這里主要關注一下隊列的綁定,當往command_queue里面塞數據的時候,event_command_ready就會被調用來處理這個數據,注意這里都是在hci_thread 里面執行的。同理往數據隊列里面塞數據,event_packet_ready就會被執行。

3.數據的發送和接收

  3.1數據的發送

看代碼可以發現,event_command_ready和event_packet_ready 他們都會調用同一個接口來發送數據,packet_fragmenter模塊里面的:

packet_fragmenter->fragment_and_dispatch(wait_entry->command);

 

也就是說,所有的數據都會先進行fragment以及dispatch的過程,我們這里主要關注數據的流向,那么也就是dispatch的流程:

static void fragment_and_dispatch(BT_HDR *packet) {
...
  callbacks->fragmented(packet, true);
}

 

發現最后是通過回調函數來發送,packet_fragmenter_callbacks_t:,看看其結構:

typedef struct {
  packet_fragmented_cb fragmented;
 packet_reassembled_cb reassembled;
  transmit_finished_cb transmit_finished;
} packet_fragmenter_callbacks_t;
static void transmit_fragment(BT_HDR *packet, bool send_transmit_finished) {
  uint16_t event = packet->event & MSG_EVT_MASK;
  serial_data_type_t type = event_to_data_type(event);
  btsnoop->capture(packet, false);//記錄btsnoop數據
  hal->transmit_data(type, packet->data + packet->offset, packet->len);//調用hal接口發送數據
}

 

我們繼續看hal的相關的接口:

static uint16_t transmit_data(serial_data_type_t type, uint8_t *data, uint16_t length) {
...
  while (length > 0) {
    ssize_t ret = write(uart_fd, data + transmitted_length, length);
...
  return transmitted_length;
}

 

hal層調用的接口很簡單,主要就是往hal_open返回的節點描述符寫數據,這個數據最終會經過內核抵達硬件設備端。

發送數據的流程就結束了。

3.2數據的接收

這里應該首先分析一下hal_open的流程:該流程是在event_finish_startup函數里執行,是hci_thread線程一開始就執行的函數:

static void event_finish_startup(UNUSED_ATTR void *context) {
  LOG_INFO("%s", __func__);
  if(!hal->open())
      return;
  vendor->send_async_command(VENDOR_CONFIGURE_FIRMWARE, NULL);
}
static bool hal_open() {
  int fd_array[CH_MAX];
  int number_of_ports = vendor->send_command(VENDOR_OPEN_USERIAL, &fd_array);//通過vendor接口去打開底層的設備節點,存儲在fd_array中

  uart_fd = fd_array[0];
  uart_stream = eager_reader_new(uart_fd, &allocator_malloc, HCI_HAL_SERIAL_BUFFER_SIZE, SIZE_MAX, "hci_single_channel");
  eager_reader_register(uart_stream, thread_get_reactor(thread), event_uart_has_bytes, NULL);//hci_thread線程一直poll設備節點,有數據就會調用event_uart_has_bytes來處理
  return true;
}

 

現在我們知道,只要底層有數據傳上來,那么hal層的函數event_uart_has_bytes就會去處理這些數據,那么看看event_uart_has_bytes的實現:

static void event_uart_has_bytes(eager_reader_t *reader, UNUSED_ATTR void *context) {
  if (stream_has_interpretation) {
    callbacks->data_ready(current_data_type);//最終調用該函數
  } else {
    uint8_t type_byte;
    if (eager_reader_read(reader, &type_byte, 1, true) == 0) {
      LOG_ERROR("%s could not read HCI message type", __func__);
      return;
    }
...
    stream_has_interpretation = true;
    current_data_type = type_byte;
  }
}

 

最終調用:

static const hci_hal_callbacks_t hal_callbacks = {
hal_says_data_ready
};

 

這個函數之前有分析過,這里簡單介紹其流程:

static void hal_says_data_ready(serial_data_type_t type) {
  packet_receive_data_t *incoming = &incoming_packets[PACKET_TYPE_TO_INBOUND_INDEX(type)];
  uint8_t byte;
  while (hal->read_data(type, &byte, 1, false) != 0) {
    switch (incoming->state) {
      case BRAND_NEW:
...
      case BODY:
        incoming->buffer->data[incoming->index] = byte;
...
        break;
      case IGNORE:
        incoming->bytes_remaining--;
...
          hal->packet_finished(type);
          return;
        }
        break;
      case FINISHED:
        LOG_ERROR("%s the state machine should not have been left in the finished state.", __func__);
        break;
    }

    if (incoming->state == FINISHED) {
      incoming->buffer->len = incoming->index;
      btsnoop->capture(incoming->buffer, true);//保存btsnoop文件

      if (type != DATA_TYPE_EVENT) {
        packet_fragmenter->reassemble_and_dispatch(incoming->buffer);//acl data處理流程
      } else if (!filter_incoming_event(incoming->buffer)) {//event 處理流程
        // Dispatch the event by event code
        uint8_t *stream = incoming->buffer->data;
        uint8_t event_code;
        STREAM_TO_UINT8(event_code, stream);

        data_dispatcher_dispatch(
          interface.event_dispatcher,
          event_code,
          incoming->buffer
        );
      }

      // We don't control the buffer anymore
      incoming->buffer = NULL;
      incoming->state = BRAND_NEW;
      hal->packet_finished(type);

      // We return after a packet is finished for two reasons:
      // 1. The type of the next packet could be different.
      // 2. We don't want to hog cpu time.
      return;
    }
  }

}

 

從上面的代碼我們發現,主要是經過兩個路徑來上報數據的:

  1. packet_fragmenter->reassemble_and_dispatch(incoming->buffer); 
  2. data_dispatcher_dispatch(interface.event_dispatcher,event_code,incoming->buffer);

首先看一下 第一個路徑:

static void reassemble_and_dispatch(UNUSED_ATTR BT_HDR *packet) {
...
    callbacks->reassembled(packet);
}

 上面的callback 定義在hci_layer.c

static void dispatch_reassembled(BT_HDR *packet) {
...
  if (upwards_data_queue) {
    fixed_queue_enqueue(upwards_data_queue, packet);//把數據放到upwards_data_queue,這個隊列其實就是btu_hci_msg_queue
  }
}

 這個隊列是在bte_main_boot_entry 時候注冊的:

hci = hci_layer_get_interface();
    btu_hci_msg_queue = fixed_queue_new(SIZE_MAX);
    data_dispatcher_register_default(hci->event_dispatcher, btu_hci_msg_queue);
    hci->set_data_queue(btu_hci_msg_queue);//設置upwards_data_queue

 

從這里我們知道,最終的數據送到了btu_hci_msg_queue,那么就由btu 線程繼續處理了。 

下面繼續看看data_dispatcher_dispatch(interface.event_dispatcher,event_code,incoming->buffer); 

bool data_dispatcher_dispatch(data_dispatcher_t *dispatcher, data_dispatcher_type_t type, void *data) {
  fixed_queue_t *queue = hash_map_get(dispatcher->dispatch_table, (void *)type);
  if (!queue)
    queue = dispatcher->default_queue;//這里的queue其實也是btu_hci_msg_queue

  if (queue)
    fixed_queue_enqueue(queue, data);

  return queue != NULL;
}

 

上面的queue也是在bte_main_boot_entry 里面注冊的。

  data_dispatcher_register_default(hci->event_dispatcher, btu_hci_msg_queue);

 

void data_dispatcher_register_default(data_dispatcher_t *dispatcher, fixed_queue_t *queue) {
  assert(dispatcher != NULL);
  dispatcher->default_queue = queue;
}

 

我們知道hci->event_dispatcher->default_queue = btu_hci_msg_queue

那和上面的第一種case一樣:最終的數據送到了btu_hci_msg_queue,那么就由btu 線程繼續處理了。

總結

最后總結一下hci_thread處理的數據流程:

  1. 當hci向下發送數據的時候,會將數據放置到packet_queue,然后調用到fragment_and_dispatch,然后經過HAL模塊發write到vendor,最后抵達controller

  2. 當controller有數據上傳的時候,底層的bt driver會將數據發送到host與controller的通信節點。hci_thread會一直poll這個節點,調用 hal_says_data_ready讀出數據,數據經過hal以及fragment_and_dispatch(或者data_dispatcher_dispatch),將數據送到btu_hci_msg_queue,然后由btu 線程繼續處理。


免責聲明!

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



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