【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

cpp 复制代码
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 received
  link_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

cpp 复制代码
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

cpp 复制代码
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)

cpp 复制代码
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

cpp 复制代码
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 callback
                                    function 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

cpp 复制代码
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);
  else
    status =
        hidh_conn_snd_data(dev_handle, t_type, param, data, report_id, pbuf);

  return status;
}

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

hidh_conn_snd_data

cpp 复制代码
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设备的管理提供了标准化解决方案。


相关推荐
reduceanxiety2 小时前
机试 | vector/array Minimum Glutton C++
数据结构·c++·算法
小黄人软件3 小时前
OpenSSL 与 C++ 搭建一个支持 TLS 1.3 的服务器
服务器·开发语言·c++
blog_wanghao4 小时前
MFC: 文件加解密(单元测试模块)
c++·单元测试·mfc
CodeWithMe5 小时前
【C/C++】深入解析Linux下C/C++内存管理全攻略(纲要)
linux·c语言·c++
m0_635647486 小时前
Qt找不到windows API报错:error: LNK2019: 无法解析的外部符号 __imp_OpenClipboard
开发语言·c++·ide·qt
15Moonlight6 小时前
C++ : list
开发语言·c++
字节高级特工7 小时前
【C++篇】list模拟实现
linux·运维·c语言·开发语言·c++
写代码写到手抽筋7 小时前
C++ ——new和malloc的区别(详细)
c++
AgilityBaby7 小时前
UE 5 C++设置物体位置和旋转,初始化虚幻引擎样条线、加载引用虚幻编辑器中的蓝图、设置虚幻编辑器中Actor大小
c++·3d·ue5·游戏引擎
楼田莉子8 小时前
C++学习之STL学习:string类使用
开发语言·c++·学习·容器·visual studio