1. HCI_Read_Local_Version_Information 命令介紹
1. 功能(Description)
HCI_Read_Local_Version_Information
命令用于讀取本地 Bluetooth Controller 的版本信息,包括 HCI 和 LMP 層的版本,以及廠商 ID 和子版本號。
這類信息用于 Host 識別當前控制器的 功能支持范圍、廠商來源 及其具體的實現版本,通常用于以下場景:
- 協議棧 兼容性判斷
- 對應廠商定制功能 條件啟用
- 調試定位 版本差異導致的問題
2.命令參數(Command Parameters)
11 2025-04-24 15:55:53.353695 host controller HCI_CMD 4 Sent Read Local Version InformationBluetooth HCI Command - Read Local Version InformationCommand 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)
12 2025-04-24 15:55:53.354267 controller host HCI_EVT 15 Rcvd Command Complete (Read Local Version Information)Bluetooth HCI Event - Command CompleteEvent Code: Command Complete (0x0e)Parameter Total Length: 12Number of Allowed Command Packets: 1Command 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: 0LMP 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 中的例子
// system/gd/hci/controller.ccstruct 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
// system/gd/hci/controller.ccvoid 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_ 中。
LocalVersionInformation Controller::GetLocalVersionInformation() const {return impl_->local_version_information_;
}
- 通過 Controller::GetLocalVersionInformation 來獲取版本信息
看看如何使用
// 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();// 獲取 localVersionInfoauto 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 中
// 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
// 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
// system/stack/btm/btm_sco.cctBTM_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
// 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);}
...
}
根據不同的廠商做不同的處理