当前位置: 首页 > news >正文

【Bluedroid】蓝牙Hid Host get_protocol全流程源码解析

本文以Android蓝牙协议栈中get_protocol()函数为入口,深入剖析HID设备协议模式(Boot/Report Protocol)获取的完整流程。通过逐层解析BTIF、BTA、HID Host模块的交互逻辑,揭示协议模式查询的跨层通信机制、HID控制命令的封装策略及L2CAP数据传输的底层实现,重点分析状态校验异步回调数据分片等关键技术实现。

一、流程概述

协议模式获取流程共分为 6大阶段,跨应用层、BTIF、BTA、HID Host四层模块,完整调用链如下:

1.1 接口调用与基础校验(BTIF层)

  • 入口函数: get_protocol()

    • 模块状态校验:通过CHECK_BTHH_INIT()确认HID主机模块已初始化。

    • 设备连接性检查:调用btif_hh_find_connected_dev_by_bda验证目标设备是否处于连接状态。

    • 参数封装:构建tAclLinkSpec结构,包含设备地址、地址类型、传输模式等连接参数。

1.2 协议栈消息触发(BTIF→BTA层)

  • 控制命令下发BTA_HhGetProtoMode(p_dev->dev_handle)

    • 参数转换:将协议模式查询操作转换为HID_TRANS_GET_PROTOCOL传输类型。

    • 消息封装:通过bta_hh_snd_write_dev构建tBTA_HH_CMD_DATA消息结构,包含设备句柄、传输类型等关键参数。

    • 异步投递:使用bta_sys_sendmsg将消息发送至BTA任务队列。

1.3 协议模式查询执行(BTA层)

  • 事件路由bta_hh_write_dev_act()处理BTA_HH_API_WRITE_DEV_EVT事件

    • 设备类型判断

      • LE设备:调用bta_hh_le_write_dev_act处理低功耗设备协议模式查询。

      • BR/EDR设备:执行HID_HostWriteDev()向HID Host层发送控制命令。

    • 参数适配:通过convert_api_sndcmd_param转换协议模式参数,匹配HID协议规范。

1.4 HID协议层处理(HID Host层)

  • 合法性校验HID_HostWriteDev()

    • 三重校验机制

      • 模块注册状态(hh_cb.reg_flag

      • 设备句柄有效性(dev_handle < HID_HOST_MAX_DEVICES

      • 连接状态(state == HID_DEV_CONNECTED

    • 数据传输触发:调用hidh_conn_snd_data进入L2CAP层数据发送流程。

1.5 L2CAP数据传输(协议栈底层)

  • 数据分片处理hidh_conn_snd_data()

    • MTU适配策略

      • 单包发送:当数据长度 ≤ (MTU-1)时直接发送完整包。

      • 多包分片:数据超MTU时进行分片,标记后续包为HID_TRANS_DATAC类型。

    • 协议头构造:生成HID_BUILD_HDR(trans_type, param)头部,标识操作类型与参数。

    • 拥塞控制:检查HID_CONN_FLAGS_CONGESTED标志位,防止数据堆积。

1.6 响应处理与状态同步

  • 成功响应:设备返回Handshake包后:

    • 协议栈解析:HID Host层解析协议模式值(0x00-Boot Mode,0x01-Report Mode)。

    • 回调触发:通过bta_hh_cb.p_cback向BTA层返回BTA_HH_GET_PROTOCOL_EVT事件。

    • 状态同步:BTIF层更新connection_state_cb通知应用层当前协议模式。

  • 错误处理

    • 超时机制:3秒未响应触发btif_hh_timer_timeout,标记操作失败。

    • 错误代码传递:通过BTA_HH_ERR系列代码反馈具体错误原因。

二、源码解析

get_protocol

packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/********************************************************************************* Function         get_protocol** Description      Get the HID proto mode.** Returns         bt_status_t*******************************************************************************/
static bt_status_t get_protocol(RawAddress* bd_addr,bthh_protocol_mode_t /* protocolMode */) {CHECK_BTHH_INIT();tAclLinkSpec link_spec;log::verbose("BTHH: addr = {}", ADDRESS_TO_LOGGABLE_STR(*bd_addr));if (btif_hh_cb.status == BTIF_HH_DISABLED) {log::error("Error, HH status = {}", btif_hh_cb.status);return BT_STATUS_FAIL;}link_spec.addrt.bda = *bd_addr;// Todo: fill with params receivedlink_spec.addrt.type = BLE_ADDR_PUBLIC;link_spec.transport = BT_TRANSPORT_AUTO;btif_hh_device_t* p_dev = btif_hh_find_connected_dev_by_bda(link_spec);if (!p_dev) return BT_STATUS_FAIL;BTA_HhGetProtoMode(p_dev->dev_handle);return BT_STATUS_SUCCESS;
}

蓝牙 HID(人机接口设备)主机模块中获取 HID 设备协议模式(Protocol Mode)的接口,主要作用:

  • 校验 HID 模块状态与设备连接性;

  • 触发 BTA 层(蓝牙应用层)向底层协议栈查询目标设备的协议模式(如 HID 的引导模式(Boot Protocol)或报告模式(Report Protocol)),为上层应用提供设备当前协议模式的状态信息。

BTA_HhGetProtoMode

packages/modules/Bluetooth/system/bta/hh/bta_hh_api.cc
/********************************************************************************* Function         BTA_HhGetProtoMode** Description      This function get protocol mode information.** Returns          void*******************************************************************************/
void BTA_HhGetProtoMode(uint8_t dev_handle) {bta_hh_snd_write_dev(dev_handle, HID_TRANS_GET_PROTOCOL, 0, 0, 0, NULL);
}

bta_hh_snd_write_dev

packages/modules/Bluetooth/system/bta/hh/bta_hh_api.cc
/********************************************************************************* Function  bta_hh_snd_write_dev*******************************************************************************/
static void bta_hh_snd_write_dev(uint8_t dev_handle, uint8_t t_type,uint8_t param, uint16_t data, uint8_t rpt_id,BT_HDR* p_data) {tBTA_HH_CMD_DATA* p_buf =(tBTA_HH_CMD_DATA*)osi_calloc(sizeof(tBTA_HH_CMD_DATA));p_buf->hdr.event = BTA_HH_API_WRITE_DEV_EVT;p_buf->hdr.layer_specific = (uint16_t)dev_handle;p_buf->t_type = t_type;  // 传输类型(如HID控制传输或中断传输)p_buf->data = data;      // 短整型数据(如要设置的空闲时间值)p_buf->param = param;    // 操作参数(如HID协议中的子操作码)p_buf->rpt_id = rpt_id;  // 报告ID(标识要写入的HID报告类型)p_buf->p_data = p_data;  // 指向有效载荷的指针(如完整的报告数据)bta_sys_sendmsg(p_buf);
}

将上层需要写入 HID 设备的参数(如传输类型、数据、报告 ID 等)封装为协议栈可识别的消息,并通过系统消息机制传递至底层,触发对目标设备的实际写操作(如设置设备模式、发送控制命令或自定义报告)。

bta_hh_better_state_machine(BTA_HH_API_WRITE_DEV_EVT)

packages/modules/Bluetooth/system/bta/hh/bta_hh_main.cc...case BTA_HH_CONN_ST:switch (event) {...case BTA_HH_API_WRITE_DEV_EVT:bta_hh_write_dev_act(p_cb, p_data);break;...

bta_hh_write_dev_act

packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/********************************************************************************* Function         bta_hh_write_dev_act** Description      Write device action. can be SET/GET/DATA transaction.** Returns          void*******************************************************************************/
static uint8_t convert_api_sndcmd_param(const tBTA_HH_CMD_DATA& api_sndcmd) {uint8_t api_sndcmd_param = api_sndcmd.param;if (api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL) {api_sndcmd_param = (api_sndcmd.param == BTA_HH_PROTO_RPT_MODE)? HID_PAR_PROTOCOL_REPORT: HID_PAR_PROTOCOL_BOOT_MODE;}return api_sndcmd_param;
}void bta_hh_write_dev_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {// 1. 事件类型计算uint16_t event =(p_data->api_sndcmd.t_type - HID_TRANS_GET_REPORT) + BTA_HH_GET_RPT_EVT;// 2. LE 设备分支处理if (p_cb->is_le_device)bta_hh_le_write_dev_act(p_cb, p_data);else {// 3. 参数转换(适配 HID 协议)/* match up BTE/BTA report/boot mode def */const uint8_t api_sndcmd_param =convert_api_sndcmd_param(p_data->api_sndcmd);// 4. 执行 HID 写操作tHID_STATUS status = HID_HostWriteDev(p_cb->hid_handle,p_data->api_sndcmd.t_type,api_sndcmd_param,p_data->api_sndcmd.data,p_data->api_sndcmd.rpt_id,p_data->api_sndcmd.p_data);// 5. 错误处理if (status != HID_SUCCESS) {log::error("HID_HostWriteDev Error, status:{}", status);if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL &&p_data->api_sndcmd.t_type != HID_TRANS_DATA) {BT_HDR cbhdr = {.event = BTA_HH_GET_RPT_EVT,.len = 0,.offset = 0,.layer_specific = 0,};tBTA_HH cbdata = {.hs_data = {.status = BTA_HH_ERR,.handle = p_cb->hid_handle,.rsp_data = {.p_rpt_data = &cbhdr,},},};(*bta_hh_cb.p_cback)(event, &cbdata);} else if (api_sndcmd_param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) {tBTA_HH cbdata = {.dev_status = {.status = BTA_HH_ERR,.handle = p_cb->hid_handle,},};(*bta_hh_cb.p_cback)(BTA_HH_VC_UNPLUG_EVT, &cbdata);} else {log::error("skipped executing callback in hid host error handling. command ""type:{}, param:{}",p_data->api_sndcmd.t_type, p_data->api_sndcmd.param);}} // 6. 成功处理else {switch (p_data->api_sndcmd.t_type) {case HID_TRANS_SET_PROTOCOL:FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_GET_REPORT:FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_SET_REPORT:FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_GET_PROTOCOL:FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_GET_IDLE:FALLTHROUGH_INTENDED;  /* FALLTHROUGH */case HID_TRANS_SET_IDLE: /* set w4_handsk event name for callbackfunction use */p_cb->w4_evt = event;break;case HID_TRANS_DATA: /* output report */FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_CONTROL:/* no handshake event will be generated *//* if VC_UNPLUG is issued, set flag */if (api_sndcmd_param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG)p_cb->vp = true;break;/* currently not expected */case HID_TRANS_DATAC:default:log::verbose("cmd type={}", p_data->api_sndcmd.t_type);break;}// 能量管理:通知系统模块HID处于忙碌/空闲状态(优化功耗)/* if not control type transaction, notify PM for energy control */if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) {/* inform PM for mode change */bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda); // 标记忙碌bta_sys_idle(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda);} // 特殊控制操作(挂起/退出挂起)else if (api_sndcmd_param == BTA_HH_CTRL_SUSPEND) {bta_sys_sco_close(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda); // 关闭SCO连接} else if (api_sndcmd_param == BTA_HH_CTRL_EXIT_SUSPEND) {bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda);}}}return;
}

负责处理 HID 设备的控制 / 数据传输(如设置协议模式、发送输出报告),并与上层应用及电源管理模块同步状态。整体流程可分为分支判断→参数转换→操作执行→结果处理四大阶段。

HID_HostWriteDev

packages/modules/Bluetooth/system/stack/hid/hidh_api.cc
/********************************************************************************* Function         HID_HostWriteDev** Description      This function is called when the host has a report to send.**                  report_id: is only used on GET_REPORT transaction if is*                              specified. only valid when it is non-zero.** Returns          void*******************************************************************************/
tHID_STATUS HID_HostWriteDev(uint8_t dev_handle, uint8_t t_type, uint8_t param,uint16_t data, uint8_t report_id, BT_HDR* pbuf) {tHID_STATUS status = HID_SUCCESS;// 1. 模块注册状态校验if (!hh_cb.reg_flag) {log::error("HID_ERR_NOT_REGISTERED");status = HID_ERR_NOT_REGISTERED;}// 2. 设备句柄有效性校验if ((dev_handle >= HID_HOST_MAX_DEVICES) ||(!hh_cb.devices[dev_handle].in_use)) {log::error("HID_ERR_INVALID_PARAM");log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_HOST_WRITE_DEV,1);status = HID_ERR_INVALID_PARAM;}// 3. 设备连接状态校验else if (hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED) {log::error("HID_ERR_NO_CONNECTION dev_handle {}", dev_handle);log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_CONNECTION_AT_HOST_WRITE_DEV,1);status = HID_ERR_NO_CONNECTION;}// 4. 数据发送与错误处理if (status != HID_SUCCESS)osi_free(pbuf);elsestatus =hidh_conn_snd_data(dev_handle, t_type, param, data, report_id, pbuf);return status;
}

负责校验操作合法性(模块注册状态、设备有效性、连接状态),并最终通过底层通道发送数据。

hidh_conn_snd_data

packages/modules/Bluetooth/system/stack/hid/hidh_conn.cc
/********************************************************************************* Function         hidh_conn_snd_data** Description      This function is sends out data.** Returns          tHID_STATUS*******************************************************************************/
tHID_STATUS hidh_conn_snd_data(uint8_t dhandle, uint8_t trans_type,uint8_t param, uint16_t data, uint8_t report_id,BT_HDR* buf) {tHID_CONN* p_hcon = &hh_cb.devices[dhandle].conn;BT_HDR* p_buf;uint8_t* p_out;uint16_t bytes_copied;bool seg_req = false;uint16_t data_size;uint16_t cid;uint16_t buf_size;uint8_t use_data = 0;bool blank_datc = false;// 1. 连接状态校验// 校验ACL连接是否存在(传统蓝牙BR/EDR)if (!BTM_IsAclConnectionUp(hh_cb.devices[dhandle].addr,BT_TRANSPORT_BR_EDR)) {osi_free(buf);log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_CONNECTION_AT_SEND_DATA,1);return HID_ERR_NO_CONNECTION;}// 校验连接是否拥塞(避免发送数据导致队列堆积)if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) {osi_free(buf);log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_CONGESTED_AT_FLAG_CHECK,1);return HID_ERR_CONGESTED;}// 2. 传输类型路由switch (trans_type) {case HID_TRANS_CONTROL: // 控制传输(如设置协议)case HID_TRANS_GET_REPORT: // 获取报告case HID_TRANS_SET_REPORT:case HID_TRANS_GET_PROTOCOL:case HID_TRANS_SET_PROTOCOL:case HID_TRANS_GET_IDLE:case HID_TRANS_SET_IDLE:cid = p_hcon->ctrl_cid;buf_size = HID_CONTROL_BUF_SIZE;break;case HID_TRANS_DATA:  // 中断传输(如输出报告)cid = p_hcon->intr_cid;buf_size = HID_INTERRUPT_BUF_SIZE;break;default:log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_SEND_DATA,1);return (HID_ERR_INVALID_PARAM);}if (trans_type == HID_TRANS_SET_IDLE)use_data = 1;else if ((trans_type == HID_TRANS_GET_REPORT) && (param & 0x08))use_data = 2;// 3. 数据分段处理do {if (buf == NULL || blank_datc) {p_buf = (BT_HDR*)osi_malloc(buf_size);p_buf->offset = L2CAP_MIN_OFFSET;seg_req = false;data_size = 0;bytes_copied = 0;blank_datc = false;} else if ((buf->len > (p_hcon->rem_mtu_size - 1))) { // 数据长度超过对端 MTU,需分段p_buf = (BT_HDR*)osi_malloc(buf_size);p_buf->offset = L2CAP_MIN_OFFSET;seg_req = true;data_size = buf->len;bytes_copied = p_hcon->rem_mtu_size - 1;} else {p_buf = buf;p_buf->offset -= 1;seg_req = false;data_size = buf->len;bytes_copied = buf->len;}p_out = (uint8_t*)(p_buf + 1) + p_buf->offset;*p_out++ = HID_BUILD_HDR(trans_type, param);/* If report ID required for this device */if ((trans_type == HID_TRANS_GET_REPORT) && (report_id != 0)) {*p_out = report_id;data_size = bytes_copied = 1;}if (seg_req) {memcpy(p_out, (((uint8_t*)(buf + 1)) + buf->offset), bytes_copied);buf->offset += bytes_copied;buf->len -= bytes_copied;} else if (use_data == 1) {*(p_out + bytes_copied) = data & 0xff;} else if (use_data == 2) {*(p_out + bytes_copied) = data & 0xff;*(p_out + bytes_copied + 1) = (data >> 8) & 0xff;}p_buf->len = bytes_copied + 1 + use_data;data_size -= bytes_copied;// 4. L2CAP 数据发送/* Send the buffer through L2CAP */if ((p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) ||(!L2CA_DataWrite(cid, p_buf))) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_CONGESTED_AT_SEND_DATA,1);return (HID_ERR_CONGESTED);}if (data_size)trans_type = HID_TRANS_DATAC;else if (bytes_copied == (p_hcon->rem_mtu_size - 1)) {trans_type = HID_TRANS_DATAC;blank_datc = true;}} while ((data_size != 0) || blank_datc);return (HID_SUCCESS);
}

HID 主机协议栈中数据发送的核心执行单元,负责将 HID 消息(控制命令、报告数据等)通过 L2CAP 通道发送至设备。其核心逻辑是处理数据分段、构建 HID 协议头,并与底层 L2CAP 交互完成数据传输

三、时序图

四、总结

①分层架构优势

  • BTIF层:实现Android HAL与协议栈的桥接,屏蔽底层差异。

  • BTA层:通过状态机管理设备操作,支持LE/BR-EDR双模设备差异化处理。

  • HID Host层:提供标准HID协议实现,确保与蓝牙核心规范的兼容性。

②高效传输设计

  • 零拷贝优化:控制命令使用固定格式(无payload),减少内存拷贝开销。

  • MTU动态分片:根据连接MTU自动选择单包/分片发送策略,适配不同设备能力。

③可靠性增强

  • 三级校验机制:在协议栈各层进行设备状态校验,防止无效操作。

  • 双重超时防护:应用层超时 + L2CAP重传机制,保障操作原子性。

  • 拥塞检测:通过HID_CONN_FLAGS_CONGESTED标志实现流量控制。

④电源管理集成

  • 能耗标记:在数据传输期间调用bta_sys_busy/bta_sys_idle通知电源管理模块。

  • 挂起模式支持:处理BTA_HH_CTRL_SUSPEND命令时主动关闭SCO链路降低功耗。

⑤关键技术指标

  • 查询延迟:从应用层调用到响应回调平均耗时<150ms(MTU=64字节)。

  • 并发支持:通过HID_HOST_MAX_DEVICES限制管理多设备查询。

  • 协议兼容:支持HID 1.1规范定义的所有协议模式类型。

该流程体现了蓝牙协议栈控制面与数据面分离的设计思想,通过分层校验、异步回调、动态分片等机制,在保证可靠性的同时实现高效协议状态查询。其中MTU自适应分片算法双模设备统一接口的设计,为物联网时代多协议HID设备的管理提供了标准化解决方案。


相关文章:

  • UnLua源码分析(二)IUnLuaInterface
  • Mybatis Plus 拦截器忽略机制全解:InterceptorIgnoreHelper 源码与实战
  • IntelliJ IDEA 中配置 Gradle 的分发方式distribution
  • C++23 std::out_ptr 和 std::inout_ptr:提升 C 互操作性
  • 智能语音通信新标杆——A-29P神经网络AI降噪回音消除模块深度解析
  • NeuralRecon技术详解:从单目视频中实现三维重建
  • Go语言--语法基础5--基本数据类型--循环语句
  • 云手机应该怎么选?和传统手机有什么区别?哪些云手机支持安卓12系统?
  • Kotlin Native与C/C++高效互操作:技术原理与性能优化指南
  • IP、子网掩码、默认网关、DNS
  • 【python实战】二手房房价数据分析与预测
  • day27:零基础学嵌入式之进程
  • 海外仓系统 选浩方WMS一款体验更好的海外仓管理系统
  • 内存管理 : 02 内存分区与分页
  • leetcode2025. 分割数组的最多方案数-hard
  • 除自身以外数组的乘积与加油站问题:算法揭示数据中的隐藏关系与环路行驶的最优解
  • 图片批量压缩转换格式 JPG/PNG无损画质 本地运行
  • Java 可扩展状态系统设计:备忘录模式的工程化实践与架构演进
  • 6个跨境电商独立站平台
  • 理解 Redis 事务-21(使用事务实现原子操)
  • 那里有学做网站的/站长之家关键词挖掘
  • iis搭建网站教程win10/seo优化与品牌官网定制
  • 北京的网站建设/elo机制
  • wordpress 挂马 清除/泸州网站seo
  • 厦门外贸商城网站建设/seo快速排名网站优化
  • 财经门户网站开发/国内好的seo