藍牙進程中有多個線程,其中HCI 線程是負責處理藍牙主機端和控制器的數據處理和收發的工做。react
本篇文章就是分析一下該線程的數據處理流程。async
首先看看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,下面看看這個接口的結構:oop
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的上面。post
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,最後抵達controllerui
當controller有數據上傳的時候,底層的bt driver會將數據發送到host與controller的通訊節點。hci_thread會一直poll這個節點,而後讀出數據,通過hal以及fragment_and_dispatch,最後送到btu線程。spa
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等等。pwa
線程的建立在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就會被執行。code
看代碼能夠發現,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返回的節點描述符寫數據,這個數據最終會通過內核抵達硬件設備端。
發送數據的流程就結束了。
這裏應該首先分析一下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; } } }
從上面的代碼咱們發現,主要是通過兩個路徑來上報數據的:
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處理的數據流程:
當hci向下發送數據的時候,會將數據放置到packet_queue,而後調用到fragment_and_dispatch,而後通過HAL模塊發write到vendor,最後抵達controller
當controller有數據上傳的時候,底層的bt driver會將數據發送到host與controller的通訊節點。hci_thread會一直poll這個節點,調用 hal_says_data_ready讀出數據,數據通過hal以及fragment_and_dispatch(或者data_dispatcher_dispatch),將數據送到btu_hci_msg_queue,而後由btu 線程繼續處理。