【android bluetooth 协议分析 01】【HCI 层介绍 8】【ReadLocalVersionInformation命令介绍】

1. HCI_Read_Local_Version_Information 命令介绍

1. 功能(Description)

HCI_Read_Local_Version_Information 命令用于读取本地 Bluetooth Controller 的版本信息,包括 HCI 和 LMP 层的版本,以及厂商 ID 和子版本号。

这类信息用于 Host 识别当前控制器的 功能支持范围、厂商来源 及其具体的实现版本,通常用于以下场景:

  • 协议栈 兼容性判断
  • 对应厂商定制功能 条件启用
  • 调试定位 版本差异导致的问题

2.命令参数(Command Parameters)

c 复制代码
11	2025-04-24 15:55:53.353695	host	controller	HCI_CMD	4	Sent Read Local Version Information

Bluetooth HCI Command - Read Local Version Information
    Command Opcode: Read Local Version Information (0x1001)
        0001 00.. .... .... = Opcode Group Field: Informational Parameters (0x04)
        .... ..00 0000 0001 = Opcode Command Field: Read Local Version Information (0x001)
    Parameter Total Length: 0
    [Response in frame: 12]
    [Command-Response Delta: 0.572ms]

无参数

只需发送命令,不带任何附加内容。


3.返回参数(Return Parameters)

c 复制代码
12	2025-04-24 15:55:53.354267	controller	host	HCI_EVT	15	Rcvd Command Complete (Read Local Version Information)

Bluetooth HCI Event - Command Complete
    Event Code: Command Complete (0x0e)
    Parameter Total Length: 12
    Number of Allowed Command Packets: 1
    Command Opcode: Read Local Version Information (0x1001)
        0001 00.. .... .... = Opcode Group Field: Informational Parameters (0x04)
        .... ..00 0000 0001 = Opcode Command Field: Read Local Version Information (0x001)
    Status: Success (0x00)
    HCI Version: 5.3 (0x0c)
    HCI Revision: 0
    LMP Version: 5.3 (0x0c)
    Manufacturer Name: Qualcomm (0x001d)
    LMP Subversion: 29337
    [Command in frame: 11]
    [Command-Response Delta: 0.572ms]
参数名 大小 描述
Status 1 字节 表示命令执行结果,0x00 表示成功
HCI_Version 1 字节 控制器 HCI 层的版本号
HCI_Subversion 2 字节 控制器厂商定义的 HCI 子版本号
LMP_Version 1 字节 LMP(Link Manager Protocol)版本号
Company_Identifier 2 字节 控制器厂商 ID,定义于 Bluetooth SIG
LMP_Subversion 2 字节 厂商自定义的 LMP 子版本号

4. 事件

成功后,Controller 会通过 HCI_Command_Complete 事件返回这些参数。


2. aosp 中如何使用

1. 字段作用与 AOSP 中的意义

下面我们逐个解释这些字段在 AOSP 蓝牙协议栈(如 stack/bt) 中的用途和意义:


1. HCI_Version(1 byte)

  • 定义:控制器实现的 HCI 层版本
  • 可能值
含义
0x06 Bluetooth 4.0
0x07 Bluetooth 4.1
0x08 Bluetooth 4.2
0x09 Bluetooth 5.0
0x0A Bluetooth 5.1
... 持续增长
  • 在 AOSP 中的作用
    • 用于判断是否支持某些 HCI 命令或功能,比如 Extended Advertising(需要 BT5.0+)
    • 控制某些 feature 的使能与 fallback(降级)策略

2. HCI_Subversion(2 bytes)

  • 定义:控制器厂商定义的子版本号(可能代表固件版本)
  • 在 AOSP 中的作用
    • 主要用于 调试厂商定制功能的兼容适配
    • 某些厂商驱动层(如 Qualcomm 或 Broadcom)可能会用这个字段判断是否加载特定补丁

3. LMP_Version(1 byte)

  • 定义:Link Manager Protocol 的版本号,用于表示底层链路控制协议的版本

  • 常见值

LMP 版本 标准版本
0x06 LMP 6 BT 2.0
0x07 LMP 7 BT 2.1
0x08 LMP 8 BT 3.0
0x09 LMP 9 BT 4.0
... ... ...
  • 在 AOSP 中的作用
    • 判断是否支持特性如 eSCO、Secure Simple Pairing、LE、BR/EDR coexistence
    • 某些协议或逻辑的 fallback 依据

4. Company_Identifier(2 bytes)

  • 定义:厂商 ID,由 Bluetooth SIG 分配
  • 例子
ID 厂商
0x000F Broadcom
0x000C CSR
0x001D Apple
0x003D Intel
0x0001 Cambridge Silicon Radio (CSR)
  • 在 AOSP 中的作用
    • 用于厂商特定补丁加载
    • 在 log 中标记设备来源
    • 控制 chipset-specific workarounds

5. LMP_Subversion(2 bytes)

  • 定义:LMP 层的子版本号,由厂商定义
  • 在 AOSP 中的作用
    • 仅对特定厂商驱动有用
    • 通常用于识别固件版本差异
    • HCI_Subversion 一起,辅助调试判断"是否为某个具体平台"

2.这些版本信息"能干啥"?意义在哪里?

用途 说明
功能判断 判断 Controller 是否支持特定协议功能,如 LE Extended Advertising、Secure Connections 等
厂商识别 确定芯片是 Broadcom、Qualcomm、Intel 还是其他,从而决定加载哪些定制行为
平台兼容 在 AOSP 中决定是否使用某些 vendor hooks 或者是否 fallback 某些功能
调试分析 蓝牙功能异常时用于判断是否为固件版本问题
日志可读性 蓝牙连接日志中可以清晰显示 Controller 的版本与厂商,方便排查

3. aosp 中的例子

c 复制代码
  
// system/gd/hci/controller.cc

struct Controller::impl {

  void Start(hci::HciLayer* hci) {
...
    hci_->EnqueueCommand(ReadLocalVersionInformationBuilder::Create(),
                         handler->BindOnceOn(this, &Controller::impl::read_local_version_information_complete_handler));

...
}

在 Controller::impl::Start 函数中,我们会获取 本地蓝牙控制器的版本信息。

当我们获取到内容后,回调 read_local_version_information_complete_handler

1. read_local_version_information_complete_handler

c 复制代码
// system/gd/hci/controller.cc

  void read_local_version_information_complete_handler(CommandCompleteView view) {
    auto complete_view = ReadLocalVersionInformationCompleteView::Create(view);
    ASSERT(complete_view.IsValid());
    ErrorCode status = complete_view.GetStatus();
    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());

    local_version_information_ = complete_view.GetLocalVersionInformation();
    bluetooth::os::LogMetricBluetoothLocalVersions(
        local_version_information_.manufacturer_name_,
        static_cast<uint8_t>(local_version_information_.lmp_version_),
        local_version_information_.lmp_subversion_,
        static_cast<uint8_t>(local_version_information_.hci_version_),
        local_version_information_.hci_revision_);
  }
  
  • 最终将 controller 获取到的版本信息,保存在 local_version_information_ 中。
c 复制代码
LocalVersionInformation Controller::GetLocalVersionInformation() const {
  return impl_->local_version_information_;
}
  • 通过 Controller::GetLocalVersionInformation 来获取版本信息

看看如何使用

c 复制代码
// system/main/shim/controller.cc
static const char GD_CONTROLLER_MODULE[] = "gd_controller_module";

EXPORT_SYMBOL extern const module_t gd_controller_module = {
    .name = GD_CONTROLLER_MODULE,

    .start_up = start_up, // 这里

    };

static future_t* start_up(void) {
  LOG_INFO("%s Starting up", __func__);
  data_.ready = true;

  if (gd_rust_is_enabled()) {

  } else {
    // 获取 mac 地址
    std::string string_address = GetController()->GetMacAddress().ToString();
    RawAddress::FromString(string_address, data_.raw_address);

    data_.le_supported_states =
        bluetooth::shim::GetController()->GetLeSupportedStates();

    // 获取 localVersionInfo
    auto local_version_info =
        bluetooth::shim::GetController()->GetLocalVersionInformation();
    data_.bt_version.hci_version =
        static_cast<uint8_t>(local_version_info.hci_version_);
    data_.bt_version.hci_revision = local_version_info.hci_revision_;
    data_.bt_version.lmp_version =
        static_cast<uint8_t>(local_version_info.lmp_version_);
    data_.bt_version.lmp_subversion = local_version_info.lmp_subversion_;
    data_.bt_version.manufacturer = local_version_info.manufacturer_name_;

    LOG_INFO("Mac address:%s", string_address.c_str());
  }

在 gd_controller_module 模块的 start_up 函数中,我们会将 local version info 信息放置在 data_.bt_version 中

c 复制代码
// system/main/shim/controller.cc
static const RawAddress* get_address(void) { return &data_.raw_address; }

static const bt_version_t* get_bt_version(void) { return &data_.bt_version; }

2. 使用案例

1. BTM_SetBleDataLength
c 复制代码
// system/stack/btm/btm_ble.cc
tBTM_STATUS BTM_SetBleDataLength(const RawAddress& bd_addr,
                                 uint16_t tx_pdu_length) {
...

  if (controller_get_interface()->get_bt_version()->hci_version >=
      HCI_PROTO_VERSION_5_0)
    tx_time = BTM_BLE_DATA_TX_TIME_MAX;

...
  }

根据 hci_version 来调整 ble 数据发送最大时间。

2.BTM_CreateSco
c 复制代码
// system/stack/btm/btm_sco.cc

tBTM_STATUS BTM_CreateSco(const RawAddress* remote_bda, bool is_orig,
                          uint16_t pkt_types, uint16_t* p_sco_inx,
                          tBTM_SCO_CB* p_conn_cb, tBTM_SCO_CB* p_disc_cb) {
                 ...
      if (controller_get_interface()->get_bt_version()->hci_version >=
          HCI_PROTO_VERSION_2_0) {
        p_setup->packet_types |=
            (pkt_types & BTM_SCO_EXCEPTION_PKTS_MASK) |
            (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK);
      }
...
}
3.l2cu_set_acl_priority 和 l2cu_set_acl_latency
c 复制代码
// system/stack/l2cap/l2c_utils.cc
bool l2cu_set_acl_priority(const RawAddress& bd_addr, tL2CAP_PRIORITY priority,
                           bool reset_after_rs) {
	...
  if ((!reset_after_rs && (priority != p_lcb->acl_priority)) ||
      (reset_after_rs && p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)) {
    /* Use vendor specific commands to set the link priority */
    switch (controller_get_interface()->get_bt_version()->manufacturer) {
      case LMP_COMPID_BROADCOM:
        l2cu_set_acl_priority_latency_brcm(p_lcb, priority);
        break;

      case LMP_COMPID_SYNAPTICS:
        l2cu_set_acl_priority_syna(p_lcb->Handle(), priority);
        break;

      default:
        /* Not supported/required for other vendors */
        break;
    }
  }

...
}


bool l2cu_set_acl_latency(const RawAddress& bd_addr, tL2CAP_LATENCY latency) {
...
  /* only change controller's latency when stream using latency mode */
  if (p_lcb->use_latency_mode && p_lcb->is_high_priority() &&
      latency != p_lcb->acl_latency) {
    switch (controller_get_interface()->get_bt_version()->manufacturer) {
      case LMP_COMPID_BROADCOM:
        l2cu_set_acl_latency_brcm(p_lcb, latency);
        break;

      default:
        /* Not supported/required for other vendors */
        break;
    }
    p_lcb->set_latency(latency);
  }
...
}

根据不同的厂商做不同的处理

相关推荐
阿巴斯甜1 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker2 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95273 小时前
Andorid Google 登录接入文档
android
黄林晴4 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab16 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿19 小时前
Android MediaPlayer 笔记
android
Jony_20 小时前
Android 启动优化方案
android
阿巴斯甜20 小时前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇20 小时前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android