There are multiple threads in the Bluetooth process, where the HCI thread is responsible for processing and sending and receiving the Bluetooth host side and controller.
This article analyzes the process of data processing for this thread.
1. HCI-related interfaces
First look at the related interfaces of HCI: in HCI_LAYER.C:
const hci_t *hci_layer_get_interface () { Buffer_allocator = Buffer_allocator_get_interface (); Hal = Hci_hal_get_interface (); // hal module btsnoop = Btsnoop_get_interface (); Hci_inject = Hci_inject_get_interface (); Packet_fragmenter = Packet_fragmenter_get_interface (); // assembly chunking vendor = Vendor_get_interface (); // vendor module low_power_manager = Low_power_manager_ Get_interface (); Init_layer_interface (); return &interface
The main structure is: Hal,packet_fragmenter and vendor, the following look at the structure of this interface:
Hal Module Interface
Static Const interface = { hal_init, hal_open,//sends instruction vendor_open_userial via vendor module, opens the Communication node for host and controller , and in the HCI thread has been poll the node, there is data on the upper stack hal_close, read_data, packet_finished, Transmit_data,}; Const hci_hal_t *hci_hal_h4_get_interface () { = vendor_get_interface ();// Get the Vendor interface return &interface;}
Analysis Code Discovery Hal_open is mainly through the vendor to communicate with the underlying module. The HAL layer is visible above the vendor.
Packet_fragmenter Module Interface
static const packet_fragmenter_t Span style= "COLOR: #0000ff" >interface = {init, cleanup, fragment_and_dispatch,
// Shard, then callback to Hci_layer, sent through the HAL layer Reassemble_and_dispatch// Reload, then callback to Hci_layer, Plug into the btu_hci_queue queue }; const packet_fragmenter_t *packet_fragmenter_ Get_interface () {Controller = Controller_get_interface (); // Get the interface of the controller buffer_allocator = Buffer_allocator_get _interface (); return &interface
The module is mainly responsible for the fragmentation and reorganization of data, when the HCI sends data down, it will put the data into Packet_queue, then call to the module's Fragment_and_dispatch, and then through the HAL module sent to Vendor, Finally, the controller.
When the controller has data upload, the underlying BT driver will send the data to the host and Controller's communication node. Hci_thread will always poll this node, then read the data, go through Hal and Fragment_and_dispatch, and finally send it to the BTU thread.
Vendor Module Interface
Static Const interface = { vendor_open,// load Libbt-vendor module and initialize the module vendor_close , Send_command,// send op command via Libbt-vendor, non-HCI opcode send_async_command , Set_callback,}; Const vendor_t *vendor_get_interface () { = buffer_allocator_get_interface (); return &interface;}
The vendor module is primarily the initialization of the Libbt-vendor module, and some vendor-related interface definitions. The specific implementation is the manufacturer's own implementation, such as open the underlying communication node, DOWNLOAF card patch and so on.
2. Creation of Threads
The thread is created inside the HCI_LAYER.C, inside the START_UP function of the HCI module:
Staticfuture_t *start_up (void) {Log_info ("%s", __func__); Command_queue= Fixed_queue_new (Size_max);//Create a command queue for sending commandsPacket_queue = Fixed_queue_new (Size_max);//Create a data queue for sending dataThread = Thread_new ("Hci_thread");//Creating an HCI thread. .. packet_fragmenter->init (&packet_fragmenter_callbacks);//Initialize the "assembly block" moduleFixed_queue_register_dequeue (Command_queue, Thread_get_reactor (thread), Event_command_ready, NULL);//hci_thread binding Command QueueFixed_queue_register_dequeue (Packet_queue, Thread_get_reactor (thread), Event_packet_ready, NULL);//hci_thread binding Data Queue. .. vendor->open (btif_local_bd_addr.address, &Interface);//Call the Vendor module's openHal->init (&hal_callbacks, thread);//Initializing the HAL module... thread_post (thread, Event_finish_startup, NULL);//continue to complete the start-up of the HCI module, where the main thing is to continue initializing the vendor module...
The main focus here is the binding of the queue, when the data is plugged into the command_queue, Event_command_ready will be used to handle this data, note that this is done in hci_thread inside. Similarly, when data is plugged into the data queue, Event_packet_ready will be executed.
3. Sending and receiving of data3.1 Sending of data
Look at the code can be found, event_command_ready and Event_packet_ready they will call the same interface to send data, Packet_fragmenter module inside:
Packet_fragmenter->fragment_and_dispatch (Wait_entry->command);
In other words, all data is fragment and dispatch, and we focus on the flow of data, which is the dispatch process:
Static void Fragment_and_dispatch (BT_HDR *packet) {... Callbackstrue);}
The discovery is finally sent by a callback function, packet_fragmenter_callbacks_t:, look at its structure:
struct { PACKET_FRAGMENTED_CB fragmented; PACKET_REASSEMBLED_CB reassembled; TRANSMIT_FINISHED_CB transmit_finished;} packet_fragmenter_callbacks_t;
Static void BOOL send_transmit_finished) { event = packet->event & msg_evt_mask; = Event_to_data_type (event); Btsnoopfalse); // record Btsnoop data Hal->transmit_data (type, Packet->data + Packet->offset, Packet->len); // Call the HAL interface to send data }
We continue to look at the relevant interfaces of Hal:
static uint16_t transmit_data (serial_data_type_t type, uint8_t *data, uint16_t length) {... while 0 { = write (uart_fd, data + transmitted_length, length); .. return transmitted_length;}
The interface called by the HAL layer is simple, mainly the node descriptor write data returned to Hal_open, which eventually passes through the kernel to the hardware device side.
The process of sending the data is over.
3.2 Reception of data
Here you should first analyze the process of Hal_open: The process is executed in the Event_finish_startup function, which is the function that the Hci_thread thread executes at the beginning:
Static void void *context) { log_info ("%s", __func__); if (!hal->Open ()) return ; Vendor-Send_async_command (Vendor_configure_firmware, NULL);}
Static BOOLHal_open () {intFd_array[ch_max]; intNumber_of_ports = Vendor->send_command (vendor_open_userial, &fd_array);//Open the underlying device node through the vendor interface, stored in the Fd_arrayUART_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);//the Hci_thread thread has been poll the device node, and the data is called Event_uart_has_bytes to handle return true;}
Now we know that as long as the underlying is rumored, then the function event_uart_has_bytes of the HAL layer will deal with the data, then look at the Event_uart_has_bytes implementation:
Static voidEvent_uart_has_bytes (eager_reader_t *reader, unused_attrvoid*context) { if(stream_has_interpretation) {callbacks->data_ready (Current_data_type);//The function is finally called}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; }}
Final Call:
Static Const hci_hal_callbacks_t hal_callbacks = { Hal_says_data_ready};
This function has been analyzed before, and here is a brief description of its process:
Static voidhal_says_data_ready (serial_data_type_t type) {packet_receive_data_t*incoming = &Incoming_packets[packet_type_to_inbound_index (TYPE)]; uint8_tbyte; while(Hal->read_data (Type, &byte,1,false) !=0) { Switch(incoming->State ) { Casebrand_new: ... Casebody:incoming->buffer->data[incoming->index] =byte;... Break; Caseignore:incoming->bytes_remaining--;. .. Hal-packet_finished (type); return; } Break; CaseFinished:log_error ("%s The state machine should not has been left in the finished state.", __func__); Break; } if(Incoming->state = =finished) {Incoming->buffer->len = incoming->index; Btsnoop->capture (Incoming->buffer,true);//Save Btsnoop File if(Type! =data_type_event) {Packet_fragmenter->reassemble_and_dispatch (Incoming->buffer);//ACL data processing flow}Else if(!filter_incoming_event (Incoming->buffer)) {//Event Processing Flow//Dispatch the event by event codeuint8_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 anymoreIncoming->buffer =NULL; Incoming->state =brand_new; Hal-packet_finished (type); //We return after a packet are finished for both reasons://1. The type of the next packet could be different. //2. We don ' t want to hog CPU time. return; } }}
From the above code we found that the main is to go through two paths to report the data:
- Packet_fragmenter->reassemble_and_dispatch (Incoming->buffer);
Data_dispatcher_dispatch (Interface.event_dispatcher,event_code,incoming->buffer);
First take a look at the first path:
Static void Reassemble_and_dispatch (unused_attr BT_HDR *packet) {... Callbacks-reassembled (packet);}
The above callback is defined in HCI_LAYER.C
Static void dispatch_reassembled (BT_HDR *packet) {... if (upwards_data_queue) { fixed_queue_enqueue (upwards_data_queue, packet); // Put the data in Upwards_data_queue, this queue is actually btu_hci_msg_queue }}
This queue was registered at the time of Bte_main_boot_entry:
Bluedroid protocol stack HCI threading analysis