基于ESP32的Bluedroid蓝牙协议栈架构分析(1) --- HCI线程分析

2023-05-16

目录

 

ESP32蓝⽛架构

BLUEDROID主机架构

BLUEDROID协议栈下HCI线程的数据处理流程。


ESP32蓝⽛架构

在 ESP32 的系统上,选择 BLUEDROID 为蓝⽛牙主机,并通过 VHCI(软件实现的虚拟 HCI 接⼝口)接⼝口,访问控制器器。此场景下,BLUEDROID 和控制器器都运⾏在同⼀宿主上(即 ESP32 芯⽚)

ESP-IDF 的默认运⾏行行环境为双核 FreeRTOS,ESP32 的蓝⽛牙可按照功能分为多个任务(task) 运⾏,不同任务的优先级也有不同,其中优先级最⾼高的为运⾏控制器的任务。控制任务对实时性的要求较⾼,在 FreeRTOS 系统中的优先级仅次于 IPC 任务(IPC 任务⽤于双核 CPU 的进程间通信)。BLUEDROID(ESP-IDF 默认蓝⽛牙主机)共包含 4 个任务,
分别运行 BTC、BTU、HCI UPWARD,及 HCI DOWNWARD。

BLUEDROID主机架构

从上图可以看到,BLUEDROID 内部⼤致分为 2 层:BTU 层和 BTC 层(除去 HCI ),每个层都有对应的任务来处理。

BTU 层主要负责蓝牙主机底层协议栈的处理,包括L2CAP、GATT/ATT、SMP、GAP 以及部分规范等,并向上提供以“bta”为前缀的接口;
BTC 层主要负责向应⽤用层提供接⼝支持、处理理基于 GATT 的规范、处理杂项等,并向应⽤层提供以“esp”为前缀的接⼝。所有的 API 都在 ESP_API 层,开发者应当使⽤“esp”为前缀的蓝⽛牙 API(特殊的除外)。
上图并未详细描述 HCI 部分,⽽事实上,HCI 具有 2 个任务(⾄少在 ESP-IDF V2.1 以前),分别处理 Downward 和 Upward 的数据。
此框架的其中一条设计思路路是尽量量将蓝⽛牙相关的任务交给 BTC 来处理,从而避免和降低用户任务 (User Task) 的负载,也使结构上更加简洁。
经典蓝⽛牙的部分规范,如 RFCOMM、A2DP 等,中层次偏协议偏底层的部分运行在 BTU 层,偏控制流程的以及需要提供 ESP-API 的运行在 BTC 层。
蓝⽛牙低功耗的部分规范或偏底层的功能,如 6LowPan 或 Dynamic L2CAP Channel 的功能,将运行在 BTU 层,再通过 BTC 向应⽤层提供 ESP-API。

BLUEDROID协议栈下HCI线程的数据处理流程。

跟HCI相关的接口文件

  • hci_layer.c

  • hci_hal_h4.c

##### 初始化流程--- hci_start_up()

static void bte_main_enable(void)
{
    APPL_TRACE_DEBUG("Enable HCI\n");
    if (hci_start_up()) {
        APPL_TRACE_ERROR("Start HCI Host Layer Failure\n");
        return;
    }
​
    //Now Test Case Not Supported BTU
    BTU_StartUp();
}
​
int hci_start_up(void)
{
    packet_fragmenter->init(&packet_fragmenter_callbacks); //分包与重组相关,发送相关
    hal->open(&hal_callbacks, hci_host_thread);//hal_callbacks()接收数据处理
​
}
hal_callbacks()
-------->>>>hal_says_packet_ready()
--------------------btu_task_post(SIG_BTU_HCI_MSG, packet)  //发送BTU层处理


关于hal 和packet_fragmenter结构体初始化

​
const hci_t *hci_layer_get_interface(void)
{
    hal = hci_hal_h4_get_interface();   
    packet_fragmenter = packet_fragmenter_get_interface();
​
    init_layer_interface();
    return &interface;
}
​
​
​
static bool hal_open(const hci_hal_callbacks_t *upper_callbacks, void *task_thread)
{
    assert(upper_callbacks != NULL);
    assert(task_thread != NULL);
   ##HAL-open---真实引用处理(callbacks 实际就是上层处理函数 hal_callbacks)
   
    callbacks = upper_callbacks;
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
    hci_hal_env_init(HCI_HAL_SERIAL_BUFFER_SIZE, BLE_ADV_REPORT_FLOW_CONTROL_NUM + L2CAP_HOST_FC_ACL_BUFS + QUEUE_SIZE_MAX); // adv flow control num + ACL flow control num + hci cmd numeber
#else
    hci_hal_env_init(HCI_HAL_SERIAL_BUFFER_SIZE, QUEUE_SIZE_MAX);
#endif
​
    hci_h4_thread = (osi_thread_t *)task_thread;
​
    //register vhci host cb
    //HCI交互消息接收的处理函数vhci_host_cb注册回调
    if (esp_vhci_host_register_callback(&vhci_host_cb) != ESP_OK) {
        return false;
    }
​
    return true;
}
​
static const esp_vhci_host_callback_t vhci_host_cb = {
    .notify_host_send_available = host_send_pkt_available_cb,
    .notify_host_recv = host_recv_pkt_cb,
};
​


HCI接口:RX接收处理

static void hci_hal_env_init(
    size_t buffer_size,
    size_t max_buffer_count)
{
    assert(buffer_size > 0);
    assert(max_buffer_count > 0);
​
    hci_hal_env.buffer_size = buffer_size;
    hci_hal_env.adv_free_num = 0;
​
    hci_hal_env.rx_q = fixed_queue_new(max_buffer_count);
    if (hci_hal_env.rx_q) {
        fixed_queue_register_dequeue(hci_hal_env.rx_q, event_uart_has_bytes);//注册接收处理
    } else {
        HCI_TRACE_ERROR("%s unable to create rx queue.\n", __func__);
    }
​
    return;
}


再结合上面流程,所有HCI接收的消息都会回调event_uart_has_bytes()进行处理

static void event_uart_has_bytes(fixed_queue_t *queue)
{
    BT_HDR *packet;
    while (!fixed_queue_is_empty(queue)) {
        packet = fixed_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT);
        hci_hal_h4_hdl_rx_packet(packet);
    }
}
//流程
hci_hal_h4_hdl_rx_packet()
-------->>>> callbacks->packet_ready(packet);此接口就是上层流程初始化引入Callback,将数据传给BTU
​
​


HCI接口:TX接收处理



看代码可以发现,event_command_ready和event_packet_ready 他们都会调用同一个接口来发送数据,packet_fragmenter模块里面的:

static int hci_layer_init_env(void)
{
      fixed_queue_register_dequeue(hci_host_env.command_queue, event_command_ready);
      fixed_queue_register_dequeue(hci_host_env.packet_queue, event_packet_ready);
}
 //都会调用同一个接口如下:
 //packet_fragmenter->fragment_and_dispatch(wait_entry->command);
也就是说,所有的数据都会先进行fragment以及dispatch的过程,我们这里主要关注数据的流向,那么也就是dispatch的流程:

static void fragment_and_dispatch(BT_HDR *packet) 
{ 
  callbacks->fragmented(packet, true);
}
发现最后是通过回调函数来发送,追到真实的发送函数如下,hal的相关的接口:

// Callback for the fragmenter to send a fragment
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);
​
    hal->transmit_data(type, packet->data + packet->offset, packet->len);
​
    if (event != MSG_STACK_TO_HC_HCI_CMD && send_transmit_finished) {
        osi_free(packet);
    }
}
​
static uint16_t transmit_data(serial_data_type_t type,
                              uint8_t *data, uint16_t length)
{
    uint8_t previous_byte;
​
    assert(data != NULL);
    assert(length > 0);
​
    if (type < DATA_TYPE_COMMAND || type > DATA_TYPE_SCO) {
        HCI_TRACE_ERROR("%s invalid data type: %d", __func__, type);
        return 0;
    }
​
    // Write the signal byte right before the data
    --data;
    previous_byte = *data;
    *(data) = type;
    ++length;
​
    BTTRC_DUMP_BUFFER("Transmit Pkt", data, length);
​
    // TX Data to target
    esp_vhci_host_send_packet(data, length);
​
    // Be nice and restore the old value of that byte
    *(data) = previous_byte;
​
    return length - 1;
}
​
最后总结一下hci_thread处理的数据流程:
  1. 当hci向下发送数据的时候,会将数据放置到packet_queue,然后调用到fragment_and_dispatch,然后经过esp_vhci_host_send_packet(data, length);,最后抵达controller

  2. 当controller有数据上传的时候,hci_thread会一直poll这个节点,调用 hal_says_data_ready读出数据,数据经过 event_uart_has_bytes 将数据送到btu_hci_msg_queue,然后由btu 线程继续处理。

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于ESP32的Bluedroid蓝牙协议栈架构分析(1) --- HCI线程分析 的相关文章

随机推荐

  • 简单git多人协同

    git网上的资料非常多 xff0c 本文只希望能提供一个简单直观的使用教程 xff0c 可能不严谨 如果本文有任何问题 xff0c 请及时指正 git简介 本章介绍git的基本概念 xff0c 了解git就可以跳过 什么是git xff1f
  • Arch 安装 Visual Studio Code(支持Wayland)

    文章目录 安装vscode在Wayland下模糊的解决方法在Wayland下无法使用输入法的解决方法 安装vscode 虽然官网下载一般是首先 xff0c 但是比较复杂 xff0c 所以推荐使用AUR xff1a yay S visual
  • 安装并使用NoMachine

    1 进入NoMachine官网下载NoMachine安装包 2 安装NoMachine xff0c 一路到底 xff0c 不需更改其他设置 xff08 安装目录随意 xff09 3 下载IP扫描工具 xff1a Advanced IP Sc
  • Wayland环境下通过xwayland支持docker图形界面

    写在前面 在之前的文章里已经讲解了怎么直接在docker中使用wayland支持图形界面 xff08 docker内外都使用wayland xff09 xff0c 文章链接 xff1a 使用Docker安装ROS2 ros humble 并
  • ZigBee生产测试项说明

  • C#窗体缩放时窗体上控件同缩放比例变化

    1 定义2个全局float 变量X Y xff0c 用来记录窗体默认窗口时的宽度和高度 float X 默认窗口的宽度 float Y 默认窗口的高度 2 窗体Load的时候将窗体的宽度 高度赋值给X Y private void Form
  • C# winform存储配置文件之App.config文件读写

    有时候一些用户的配置需要写入在本地 xff0c 不能每次程序启动都让用户重新设置一下吧 下面先说基本用法 xff1a 1 创建winform项目之后自动会生成App config文件 xff0c 如果默认没有 就对项目右键 新建项 APP配
  • C# 解决串口接收数据不完整处理方法

    使用缓存机制完成 首先通过定义一个成员变量List lt byte gt buffer 61 new List lt byte gt 4096 用来存放所有的数据 xff0c 在接收函数里 xff0c 通过buffer AddRange 方
  • ble-mesh笔记整理(1) ble-mesh通信理解

    最近有时间将之前整理的 xff0c 关于蓝牙mesh开发学习过程的一些笔记 xff0c 以及一些对mesh协议文档理解的翻译文档 xff0c 发布出来给大家一起学习和参考 xff0c 主要是一个学习的过程参考 蓝牙Mesh通过网状网的组网结
  • ble-mesh 学习笔记(2)-如何给mesh-node配网

    1 配网方式选择 基于手机设备为provisioner进行进行PB GATT 配网 如果一个蓝牙4 0的手机需要用作Provisioner xff0c 并完成对其他节点的配网过程 则需要使用Mesh Provisioning Service
  • ble mesh学习笔记(3)mesh节点设备广播类型

    蓝牙Mesh网络一共有3种广播包 xff0c 如下图所示 1 PB ADV的包是用在Provisioning过程的 xff0c Provisioning是一个设备加入Mesh网络的过程 2 Mesh Message xff0c 这个是在设备
  • ble mesh学习笔记(4)元素model的理解

    说明 xff1a 涉及代码模块为Telink mesh sdk 一 Composition Data 结构定义 mesh节点Composition Data 数据定义元素个数以及元素下model个数与model id数据结构如下 xff1a
  • ble mesh学习笔记(5) mesh数据PDU封包理解

    mesh 网络数据协议格式 xff1a access payload 协议格式 xff1a Upper transport layer协议格式 xff1a 消息分为 xff1a control message friendship cont
  • 交换机SNMP V3版本配置

    SNMPV3版本安全性和远端配置强化 xff0c 目前公司已经禁止SNMPV3以下版本 xff08 H3C交换机 xff09 xff0c 配置步骤如下 1 开启SNMP协议 snmp agent 2 设置使用的SNMP版本 snmp age
  • ble mesh 学习笔记(6) 如何对接天猫精灵蓝牙mesh

    MESH设备接入天猫精灵的步骤 1 设备在天猫精灵mesh 网络中所具有的怎么样功能 xff1f 1 1 目前根据mesh协议能实现如下功能节点 xff08 1 xff09 Node xff1a 网络边缘的节点 xff0c 没有relay功
  • ble mesh 学习笔记(7)理解mesh协议proxy代理

    1 Proxy 简介 Proxy 协议能够让节点收发Network PDUs mesh beacons proxy configuration Provisioning PDUs通过面向连接的承载层 Proxy protocol 定义了两个
  • ble mesh学习笔记(10)mesh网络各种key的应用

    一 网络密钥 xff08 Network Key xff09 应使用符合核心规范要求的随机数生成器生成 网络密钥层次结构的图示如图所示 1 用NID EncryptionKey PrivacyKey 来对mesh网络数据加密 2 Netwo
  • 通用LED闪烁代码模块(也可实现软件PWM)

    仅提供参考文件 xff1a 1 需要修改gpio引脚 2 需要根据平台实现软件定时器接口 xff0c 启动 xff0c 停止 xff0c 回调 xff0c 三个API 3 根据平台优化相关错误即可 pwm drv c Created on
  • ble-mesh学习笔记(11)小米平台ble-mesh概述

    小米平台ble mesh开发 1 方案选择 2 支持特性说明 xff1a 必须用小米的模组 xff0c 才能接入小米的ble mesh平台 目前模块只支持手机gatt升级 xff0c 网关ota升级方案在Q3上线 目前模块只支持relay功
  • 基于ESP32的Bluedroid蓝牙协议栈架构分析(1) --- HCI线程分析

    目录 ESP32蓝 架构 BLUEDROID主机架构 BLUEDROID协议栈下HCI线程的数据处理流程 ESP32蓝 架构 在 ESP32 的系统上 xff0c 选择 BLUEDROID 为蓝 牙主机 xff0c 并通过 VHCI xff