zoukankan      html  css  js  c++  java
  • 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 线程继续处理。

  • 相关阅读:
    楼市十大卖楼花招曝光〔转载〕
    Redhat中通过命令工具和配置文件设置TCP/IP参数的方法
    远程桌面超大最大连接数,无法连接服务器解决办法[转载]
    七夕祝福
    决定成败的人生细节
    QTP总结〔转载〕
    如何理解Return的返回值?
    IT人士群聚喝酒的讲究(转载)
    20 个增强表单功能的 jQuery 插件
    Windows Phone灵魂诠释:Metro UI界面完全解析
  • 原文地址:https://www.cnblogs.com/libs-liu/p/9218460.html
Copyright © 2011-2022 走看看