【android bluetooth 协议分析 02】【bluetooth hal 层详解 6】【高通蓝牙hal主要流程介绍-下】
1. 背景
本节主要讨论 高通 蓝牙 hal 中,的一些流程。 看看你是否都清楚如下问题:
- 高通芯片电如何控制?
- 串口是在哪里控制的?
- 固件如何下载?
- 初始化流程是怎么样的?
如果你已经对上述讨论的问题,已经很清楚了,那你无需阅读该文章,请自行忽略。当然,也可以给笨叔挑挑错。 欢迎评论,一起探讨,毕竟都是笨叔自己的理解,难免有点出入,我也想进步!!!
阅读 本篇内容前, 请先阅读
【android bluetooth 协议分析 02】【bluetooth hal 层详解 3】【高通蓝牙hal主要流程介绍-上】
【android bluetooth 协议分析 02】【bluetooth hal 层详解 4】【高通蓝牙hal主要流程介绍-中】
我们继续接着 中篇 第4.小节,讲解。
本文讨论的重点是,如何打 patch
// hidl_hci/1.0/default/uart_controller.cppbool UartController::Init(PacketReadCallback pkt_read_cb)
{
...patch_dl_manager = new (std::nothrow)PatchDLManager(soc_type_, uart_transport, &power_manager_);patch_dl_manager->PerformChipInit()...
}
下载固件其实也很简单, 这里主要分为 两步:
- 创建 PatchDLManager 对象
- 执行 该对象的 PerformChipInit 方法
2. 创建PatchDLManager
// hidl_hci/1.0/default/patch_dl_manager.cpp
PatchDLManager::PatchDLManager(BluetoothSocType soc_type, HciUartTransport* transport, PowerManager* power_manager) :soc_type_(soc_type), uart_transport_(transport), power_manager_(power_manager), dnld_fd_in_progress_(-1)
{ALOGI("%s", __func__);bt_logger_ = Logger::Get();fd_transport_ = uart_transport_->GetCtrlFd(),wait_vsc_evt_ = true;patch_dnld_pending_ = false;secure_bridge_enabled = false;is_mode_change_needed = false;elf_config_read_ = false;unified_hci = false; // 这里将 unified_hci 设置为 falsememset(&add_on_features, 0, sizeof(add_on_features));LoadPatchMaptable(); // 主要关注这里
}
在 PatchDLManager 构造函数里面,最重要的是 LoadPatchMaptable
// hidl_hci/1.0/default/patch_dl_manager.cpp
void PatchDLManager::LoadPatchMaptable() {
...PatchPathInfoMap_.insert(std::make_pair<uint64_t, PatchPathManager*>(HASTINGS_VER_2_0,new XmemPatchPathManager("HST2_0", "htbtfw20.tlv", "htnv20.bin","htbtxm.tlv", "htnv20xm.bin")));
...
}
- 在 LoadPatchMaptable 中,会有很多 PatchPathInfoMap_.insert。 我们只关注我们用到的 HASTINGS_VER_2_0
其次 我们发现在构造函数中将 unified_hci 设置为 false. 表面我们默认使用 标准的 HCI 命令格式
1. unified hci 和 hci 之间的区别
蓝牙中的 Unified HCI(统一 HCI) 和传统 HCI(标准 HCI) 的区别对比表格
对比项 | 标准 HCI(HCI) | 统一 HCI(Unified HCI) |
---|---|---|
定义 | Bluetooth 规范中定义的标准 Host Controller Interface | 厂商扩展的一种统一命令格式接口(通常基于 Vendor Command) |
命令结构 | 多种标准命令组,如 Controller & Baseband、LE Controller、Link Control 等 | 将所有厂商扩展命令统一到一个格式中,如 HCI_VS_UNIFIED_CMD |
适配性 | 不同厂商命令格式差异大,适配复杂 | 命令格式标准化,适配多个芯片更容易 |
命令数量 | 由蓝牙核心规范定义,命令数量固定 | 支持厂商自定义子命令(Sub-opcode),命令功能更丰富 |
使用方式 | 主机通过标准 HCI 命令与控制器通信 | 主机通过统一入口发送子命令(统一接口 + 参数区) |
扩展能力 | 扩展性有限,复杂功能需额外定义 Vendor Command | 扩展性强,便于支持 LE Audio、功耗调节、天线切换等新特性 |
兼容性 | 所有蓝牙控制器都支持 | 仅部分 SoC 支持,需查看芯片手册或 FW 支持情况 |
典型命令 | HCI_Read_Local_Version , HCI_LE_Set_Adv_Params | HCI_VS_UNIFIED_CMD + sub-opcode = READ_FEATURES |
开发成本 | 跨平台适配较复杂 | 同一套命令可适配多个型号,开发效率更高 |
应用场景 | 标准蓝牙功能(连接、配对、广播等) | 高级特性配置(LE Audio, TWS 配对、板载 LDO 切换等) |
总结 :
标准 HCI 是蓝牙协议规定的基础接口,而 Unified HCI 是厂商为简化命令扩展和多型号芯片适配而提供的统一入口方式,便于驱动层统一管理高级特性。
3. 调用 PerformChipInit
int PatchDLManager::PerformChipInit()
{// 这里会向芯片 获取芯片内部 mac 地址BluetoothAddress::GetLocalAddress(vnd_local_bd_addr_);// 确保 rts 使能, 告诉蓝牙芯片,主机已经准备好接受数据,你可以发送数据给我/* Workaround UART issue: Make sure RTS is flowed ON in case it was not flowed on during cleanup due to UART issue */err = uart_transport_->Ioctl(USERIAL_OP_FLOW_ON, &flags);ret = SocInit(vnd_local_bd_addr_, is_emb_wp_mode);return ret;
}
int PatchDLManager::SocInit(uint8_t *bdaddr, bool is_emb_wp_mode)
{// 1.1 获取芯片 版本信息if ((err = PatchVerReq()) < 0) {}ALOGI("%s: Chipset Version (0x%16llx)", __func__,(unsigned long long)chipset_ver_); // Chipset Version (0x400a020000100200)// 1.2. 根据 chipset version 从 PatchPathInfoMap_ 找到对应 的patch 路径信息, 这里找到的就是 HASTINGS_VER_2_0auto itr = PatchPathInfoMap_.find(chipset_ver_);if (itr != PatchPathInfoMap_.end())info = itr->second;// 2. 向芯片设置 波特率为 3Merr = SetBaudRateReq();ALOGI("%s: Baud rate changed successfully ", __func__);// 3. 下载 tlv 文件err = DownloadTlvFile();ALOGI("%s: Download TLV file successfully ", __func__);/* 4. 关闭内部 LDO(一种线性稳压器,用于将较高的输入电压稳压成较低的输出电压),改为使用外部 LDO*/err = DisableInternalLdo();/* get chipset supported feature request */ALOGI("%s: chipset_ver_: 0x%16llx Calling get addon feature",__func__,(unsigned long long)chipset_ver_);// 5. 获取当前固件(Firmware, FW)支持的附加功能(Add-on Features)列表GetAddOnFeatureList();// 7. 发送 reset 命令err = HciReset();if ( err < 0 ) {} else {ALOGI("HCI Reset is done\n");}dnld_fd_in_progress_ = -1;return err;
}
我们可以将 PatchDLManager::SocInit 函数总结为如下 几个步骤:
- 获取芯片 版本信息,并找到对应的 PatchPathManager
- 向芯片设置 波特率
- 下载 tlv 文件
- 关闭内部 LDO, 改为使用外部 LDO
- 获取当前固件(Firmware, FW)支持的附加功能(Add-on Features)列表
- 发送 reset 命令
1. 获取芯片 版本信息,并找到对应的 PatchPathManager
// 1.1 获取芯片 版本信息if ((err = PatchVerReq()) < 0) {}ALOGI("%s: Chipset Version (0x%16llx)", __func__,(unsigned long long)chipset_ver_); // Chipset Version (0x400a020000100200)// 1.2. 根据 chipset version 从 PatchPathInfoMap_ 找到对应 的patch 路径信息, 这里找到的就是 HASTINGS_VER_2_0auto itr = PatchPathInfoMap_.find(chipset_ver_);if (itr != PatchPathInfoMap_.end())info = itr->second;
#define EDL_PATCH_VER_REQ_CMD (0x19)int PatchDLManager::PatchVerReq()
{int size, err = 0;unsigned char cmd[HCI_MAX_CMD_SIZE];unsigned char rsp[HCI_MAX_EVENT_SIZE];char dst_buff[MAX_BUFF_SIZE] = {'\0'};char res_buff[MAX_BUFF_SIZE] = {'\0'};struct timeval tv;ALOGI("%s: Sending Get Version CMD to SOC", __func__);// 组合要给 controller 发送的数据格式FrameHciPkt(cmd, EDL_PATCH_VER_REQ_CMD/*0x19*/, 0, -1, EDL_PATCH_CMD_LEN/*1*/);/* Total length of the packet to be sent to the Controller */size = (HCI_CMD_IND/*1*/ + HCI_COMMAND_HDR_SIZE/*3*/ + EDL_PATCH_CMD_LEN/*1*/); // 5/* 将上述 cmd 命令 通过串口发送给 控制器 */err = HciSendVsCmd((unsigned char*)cmd, rsp, size);...return err;}
1. PatchDLManager::FrameHciPkt
/* HCI Packet types */
#define HCI_COMMAND_PKT 0x01
#define HCI_VENDOR_CMD_OGF 0x3F
#define HCI_PATCH_CMD_OCF (0)#define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10))void PatchDLManager::FrameHciPkt(unsigned char *cmd,int edl_cmd, unsigned int p_base_addr,int segtNo, int size
)
{int offset = 0;hci_command_hdr *cmd_hdr;memset(cmd, 0x0, HCI_MAX_CMD_SIZE);cmd_hdr = (hci_command_hdr*)(cmd + 1);cmd[0] = HCI_COMMAND_PKT; // 0x01cmd_hdr->opcode = cmd_opcode_pack(HCI_VENDOR_CMD_OGF/*0x3f*/, HCI_PATCH_CMD_OCF/*0*/); // 0xfc00cmd_hdr->plen = size; // 0x01cmd[4] = edl_cmd; // 0x19switch (edl_cmd) {case EDL_PATCH_VER_REQ_CMD:// 将 cmd 打印出来方便调试:// HCI-CMD -1: 0x1 0x0 0xfc 0x1 0x19ALOGI("%s: Sending EDL_PATCH_VER_REQ_CMD", __func__);ALOGI("HCI-CMD %d:\t0x%x \t0x%x \t0x%x \t0x%x \t0x%x",segtNo, cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]);break;default:ALOGE("%s: Unknown EDL CMD !!!", __func__);}
}
2. PatchDLManager::HciSendVsCmd
int PatchDLManager::HciSendVsCmd(unsigned char *cmd, unsigned char *rsp, int size)
{int ret = 0;char dst_buff[MAX_BUFF_SIZE] = {'\0'};struct timeval tv;// 将 cmd 通过 串口发送给 controllerret = uart_transport_->UartWrite(cmd, size);if (wait_vsc_evt_) {/* Check for response from the Controller */if (!unified_hci) {// 读取该命令所对应的 控制器 返回的事件if (ReadVsHciEvent(rsp, HCI_MAX_EVENT_SIZE) < 0) {}ALOGI("%s: Received HCI-Vendor Specific Event from SOC", __func__);}else{}}failed:return ret;
}
1. HciUartTransport::UartWrite
int HciUartTransport::UartWrite(const uint8_t *buf, int len)
{std::unique_lock<std::mutex> guard(internal_mutex_);return WriteSafely(buf, len);
}int HciUartTransport::WriteSafely(const uint8_t *data, int length)
{int write_len = 0;while (length > 0) {ssize_t ret = write(ctrl_fd_, data + write_len, length);write_len += ret;length -= ret;}return write_len;
}
- 最终 通过 ctrl_fd_ 节点写入
- 那这里的 ctrl_fd_ 是谁呢? /dev/ttyHS0
回顾一下之前的流程:
bool HciUartTransport::InitTransport(tUSERIAL_CFG *p_cfg)
{uint32_t baud;uint8_t data_bits;uint16_t parity;uint8_t stop_bits;struct termios termios;ctrl_fd_ = -1;// 当前 我们的 soc_type_ = BT_SOC_HASTINGSif (soc_type_ == BT_SOC_CHEROKEE) {} else {ctrl_fd_ = OpenUart(uart_device_, p_cfg);ALOGD("%s: soc_type(%d), opening '%s' return fd=%d", __func__, soc_type_,uart_device_, ctrl_fd_);if (ctrl_fd_ < 0)return false;}return true;
}HciUartTransport(HealthInfoLog* theHealthInfo) {ctrl_fd_ = -1;data_fd_ = -1;health_info = theHealthInfo;Util::getUartDevice(uart_device_); // 这里就已经获取到 我们的 /dev/ttyHS0 设备了};
2. PatchDLManager::ReadVsHciEvent
int PatchDLManager::ReadVsHciEvent(unsigned char* buf, int size)
{int tot_len;bool collecting_ram_dump = false;unsigned short int opcode;do {tot_len = ReadNewHciEventWithTimer(buf, size);if (buf[1] == LOG_BT_EVT_VENDOR_SPECIFIC) {...} else if (buf[1] == EVT_CMD_COMPLETE) {ALOGI("%s: Expected CC", __func__);if (tot_len > UNIFIED_HCI_CC_MIN_LENGTH) {opcode = (buf[4] | (buf[5] << 8));if (((HCI_VS_WIPOWER_CMD_OPCODE == opcode) && (UNIFIED_HCI_CODE == buf[6])) ||((HCI_VS_GET_VER_CMD_OPCODE == opcode) && (buf[7] == EDL_PATCH_VER_REQ_CMD) /*这里会满足这个条件*/)) {unified_hci = true; // 将当前命令的事件 标识为 一个 unified hci cmdALOGI("HCI Unified command interface supported");}}} else {}} while (collecting_ram_dump);/* Check if the set patch command is successful or not */if (GetVsHciEvent(buf) != HCI_CMD_SUCCESS)return -1;return tot_len;
}
1. PatchDLManager::ReadNewHciEventWithTimer
int PatchDLManager::ReadNewHciEventWithTimer(unsigned char* buf, int size)
{int retval;unsigned char protocol_byte;unsigned char hdr[BT_EVT_HDR_SIZE];unsigned char packet_len;unsigned short tot_len;fd_set set;struct timeval timeout;ALOGI("%s: >", __func__);FD_ZERO(&set);FD_SET(fd_transport_, &set);/* timer value */timeout.tv_sec = 0; /* in second */timeout.tv_usec = HCI_EVENT_READ_TIMEOUT_IN_US; /* in usecond */do {// 通过 select 监听 串口, 如果有数据上来就返回retval = select(fd_transport_ + 1, &set, NULL, NULL, &timeout);// 从串口读一个字节retval = uart_transport_->Read(&protocol_byte, 1);// 如果是一个 hci event ,就退出 循环。否则继续 循环读if (protocol_byte == LOG_BT_EVT_PACKET_TYPE) {break;} else {ALOGI("%s: Got an invalid proto byte: %d", __func__, protocol_byte);}} while (1);// 继续 select 监听串口retval = select(fd_transport_ + 1, &set, NULL, NULL, &timeout);// 读两个字节 表示头retval = uart_transport_->Read(hdr, BT_EVT_HDR_SIZE/*2*/);ALOGI("read scucesssssfully HDR");packet_len = hdr[BT_EVT_HDR_LEN_OFFSET/*1*/]; // 读取包的长度ALOGI("packet_len: %d\n", packet_len);buf[0] = protocol_byte; // 将 0 , 1, 2字节拷贝到 rsp 中。memcpy(buf + 1, hdr, BT_EVT_HDR_SIZE);// 继续 select 监听串口retval = select(fd_transport_ + 1, &set, NULL, NULL, &timeout);// 读取 包中所指定的消息的长度retval = uart_transport_->Read(buf + BT_EVT_HDR_SIZE + 1, packet_len);tot_len = packet_len + BT_EVT_HDR_SIZE + 1; // 总的包长度ALOGI("read scucesssssfully payload: tot_len: %d", tot_len);return tot_len;
}
controller 返回给我们的事件内容,全部保存在 rsp 数组中了。
2. PatchDLManager::GetVsHciEvent
01-14 17:15:30.214035 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: HCI Unified command interface supported
01-14 17:15:30.214042 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: GetVsHciEvent: Received HCI-Vendor Specific event
01-14 17:15:30.214049 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: GetVsHciEvent: Opcode: 0xfc00
01-14 17:15:30.214056 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: GetVsHciEvent: ocf: 0x0
01-14 17:15:30.214062 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: GetVsHciEvent: ogf: 0x3f
01-14 17:15:30.214068 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: GetVsHciEvent: Status: 0x0
01-14 17:15:30.214074 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: GetVsHciEvent: Sub-Opcode: 0x19
01-14 17:15:30.214081 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: GetVsHciEvent: Parameter Length: 0x12
01-14 17:15:30.214087 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: GetVsHciEvent: Command Request Response
01-14 17:15:30.214094 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: unified Current Product ID : 0x00000010
01-14 17:15:30.214100 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: unified Current Patch Version : 0x0d2b
01-14 17:15:30.214106 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: unified Current ROM Build Version : 0x0200
01-14 17:15:30.214113 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: unified Current SOC Version : 0x400a0200
int PatchDLManager::GetVsHciEvent(unsigned char *rsp)
{int err = 0;unsigned char paramlen = 0;unsigned char EMBEDDED_MODE_CHECK = 0x02;unsigned int opcode = 0;unsigned char subOpcode = 0;unsigned int ocf = 0;unsigned int ogf = 0;unsigned char status = 0;uint8_t baudrate_rsp_status_offset = 0;uint8_t addon_features_bitmask_offset = 0;if ( (rsp[EVENTCODE_OFFSET] == VSEVENT_CODE) || (rsp[EVENTCODE_OFFSET] == EVT_CMD_COMPLETE))ALOGI("%s: Received HCI-Vendor Specific event", __func__);else {...}if (!unified_hci) {...} else {paramlen = rsp[EVT_PLEN];opcode = rsp[5]<<8 | rsp[4];ocf = opcode & 0x03ff;ogf = opcode >> 10;status = rsp[6];subOpcode = rsp[7];ALOGI("%s: Opcode: 0x%x", __func__, opcode);ALOGI("%s: ocf: 0x%x", __func__, ocf);ALOGI("%s: ogf: 0x%x", __func__, ogf);ALOGI("%s: Status: 0x%x", __func__, status);ALOGI("%s: Sub-Opcode: 0x%x", __func__, subOpcode);ALOGI("%s: Parameter Length: 0x%x", __func__, paramlen);}switch ( ocf ) {case EDL_CMD_REQ_RES_EVT:ALOGI("%s: Command Request Response", __func__);HandleEdlCmdResEvt(subOpcode, paramlen, rsp); // 这个函数就是来具体解析 我们收到的 rsp 数组里面的数据。从中提取出 我们的版本信息。break;...default:ALOGE("%s: Not a valid status!!!", __func__);err = -1;break;}failed:return err;
}void PatchDLManager::HandleEdlCmdResEvt(unsigned char subOpcode, unsigned char paramlen,unsigned char* rsp)
{
...switch (subOpcode) {case EDL_PATCH_VER_RES_EVT: // 0x19case EDL_APP_VER_RES_EVT:if (!unified_hci) {} else {productid = (unsigned int)(rsp[PATCH_PROD_ID_OFFSET_UNIFIED + 3] << 24 |rsp[PATCH_PROD_ID_OFFSET_UNIFIED + 2] << 16 |rsp[PATCH_PROD_ID_OFFSET_UNIFIED + 1] << 8 |rsp[PATCH_PROD_ID_OFFSET_UNIFIED] );ALOGI("\t unified Current Product ID\t\t: 0x%08x", productid);/* Patch Version indicates FW patch version */patchversion = (unsigned short)(rsp[PATCH_PATCH_VER_OFFSET_UNIFIED + 1] << 8 |rsp[PATCH_PATCH_VER_OFFSET_UNIFIED] );ALOGI("\t unified Current Patch Version\t\t: 0x%04x", patchversion);/* ROM Build Version indicates ROM build version like 1.0/1.1/2.0 */buildversion =(int)(rsp[PATCH_ROM_BUILD_VER_OFFSET_UNIFIED + 1] << 8 |rsp[PATCH_ROM_BUILD_VER_OFFSET_UNIFIED] );ALOGI("\t unified Current ROM Build Version\t: 0x%04x", buildversion);if (paramlen - 10) {soc_id =(unsigned int)(rsp[PATCH_SOC_VER_OFFSET_UNIFIED + 3] << 24 |rsp[PATCH_SOC_VER_OFFSET_UNIFIED + 2] << 16 |rsp[PATCH_SOC_VER_OFFSET_UNIFIED + 1] << 8 |rsp[PATCH_SOC_VER_OFFSET_UNIFIED] );ALOGI("\t unified Current SOC Version\t\t: 0x%08x", soc_id);}}chipset_ver_ = QCA_BT_VER(soc_id, productid, buildversion);break;}
}
上面我完整的分析了 高通的hal 如何 发送数据给 controller 并,解析对应的 hci event 事件。
2. 向芯片设置 波特率
// 2. 向芯片设置 波特率为 3M/* Change baud rate 115.2 kbps to 3Mbps*/err = SetBaudRateReq();ALOGI("%s: Baud rate changed successfully ", __func__);
int PatchDLManager::SetBaudRateReq()
{int size, err = 0;unsigned char cmd[HCI_MAX_CMD_SIZE];unsigned char rsp[HCI_MAX_EVENT_SIZE];hci_command_hdr *cmd_hdr;int flags;uint8_t bt_baud_rate = uart_transport_->GetMaxBaudrate(); // 获取最大 波特率 3Mstruct timeval tv;memset(cmd, 0x0, HCI_MAX_CMD_SIZE);cmd_hdr = (hci_command_hdr*)(cmd + 1);cmd[0] = HCI_COMMAND_PKT; /*0x01*/cmd_hdr->opcode = cmd_opcode_pack(HCI_VENDOR_CMD_OGF/*0x3f*/, EDL_SET_BAUDRATE_CMD_OCF/*0x48*/);cmd_hdr->plen = VSC_SET_BAUDRATE_REQ_LEN; /*1*/cmd[4] = bt_baud_rate;/* Total length of the packet to be sent to the Controller */size = (HCI_CMD_IND + HCI_COMMAND_HDR_SIZE + VSC_SET_BAUDRATE_REQ_LEN);// 设置波特率前,先关闭流控/* Flow off during baudrate change */if ((err = uart_transport_->Ioctl(USERIAL_OP_FLOW_OFF, &flags)) < 0) {}// 通过串口将命令发送给 controller/* Send the HCI command packet to UART for transmission */err = uart_transport_->UartWrite(cmd, size);// 设置最大波特率/* Change Local UART baudrate to high speed UART */uart_transport_->SetBaudRate(bt_baud_rate);// 查看是否设置成功/* Check current Baudrate */uart_transport_->GetBaudRate();// 打开流控/* Flow on after changing local uart baudrate */if ((err = uart_transport_->Ioctl(USERIAL_OP_FLOW_ON, &flags)) < 0) {ALOGE("%s: HW Flow-on error: 0x%x \n", __func__, err);goto error;}// 接收 controller 返回的事件/* Wait for command complete event */err = ReadHciEvent(rsp, HCI_MAX_EVENT_SIZE);return err;
}
- uart_transport_->UartWrite 向 controller 发送cmd 和 ReadHciEvent 在上面介绍过,这里不再介绍。都类似。
- 我们这里关注一下,波特率 和流程相关的内容
1. HciUartTransport::GetMaxBaudrate
uint8_t HciUartTransport::GetMaxBaudrate()
{switch (soc_type_) {case BT_SOC_CHEROKEE:
#ifdef UART_BAUDRATE_3_0_MBPSreturn (uint8_t) USERIAL_BAUD_3M;
#elsereturn (uint8_t) USERIAL_BAUD_3_2M;
#endifbreak;case BT_SOC_MOSELLE:case BT_SOC_HAMILTON:return (uint8_t) USERIAL_BAUD_3_2M;break;case BT_SOC_HASTINGS: // 这个类型case BT_SOC_ROME:case BT_SOC_GENOA:/* fall through */default:return (uint8_t) USERIAL_BAUD_3M;}
}
- 这里的最大波特率,是根据, 芯片 支持的最大波特率。 固定死的。 和在任何平台 例如,是高通的平台,还是 mtk 的平台,没有关系。
2. 开关流控
int HciUartTransport::Ioctl(userial_vendor_ioctl_op_t op, int *p_data)
{int err = -1;struct timeval tv;char dst_buff[MAX_BUFF_SIZE];switch (op) {case USERIAL_OP_FLOW_ON:ALOGI("## userial_vendor_ioctl: UART Flow On ");ioctl(ctrl_fd_, TIOCMGET/*0x5415*/, p_data);*p_data |= TIOCM_RTS/*0x004*/;err = ioctl(ctrl_fd_, TIOCMSET/*0x5418*/, p_data);break;case USERIAL_OP_FLOW_OFF:ALOGI("## userial_vendor_ioctl: UART Flow Off ");ioctl(ctrl_fd_, TIOCMGET/*0x5415*/, p_data);*p_data &= ~TIOCM_RTS/*0x004*/;err = ioctl(ctrl_fd_, TIOCMSET/*0x5418*/, p_data);break;case USERIAL_GET_ERR_CODE:err = ioctl(ctrl_fd_, MSM_GENI_SERIAL_TIOCFAULT/*0x54EC*/, NULL);break;default:break;}return err;
}
开关流控在高通平台 也是很容易做的:
- 开流程 : 将 TIOCM_RTS 置位
- 关流程: 清除 TIOCM_RTS 位
同时也可以通过 MSM_GENI_SERIAL_TIOCFAULT 命令 ,读取高通串口的异常错误。
3. 设置串口波特率
void HciUartTransport::SetBaudRate(uint8_t userial_baud)
{uint32_t tcio_baud;struct termios termios;struct timeval tv;ALOGI("## userial_vendor_set_baud: %d", userial_baud);userial_to_tcio_baud(userial_baud, &tcio_baud);{tcgetattr(ctrl_fd_, &termios);cfsetospeed(&termios, tcio_baud);cfsetispeed(&termios, tcio_baud);tcsetattr(ctrl_fd_, TCSADRAIN, &termios); /* don't change speed until last write done */}
}
3. 下载 tlv 文件
// 3. 下载 tlv 文件err = DownloadTlvFile();ALOGI("%s: Download TLV file successfully ", __func__);
int PatchDLManager::DownloadTlvFile()
{int tlv_size = -1;int err = -1;char nvm_file_path_bid[256] = { 0, };char nvm_alt_file_path_bid[256] = { 0, };int nvm_file_path_len = strlen(nvm_file_path);int nvm_alt_file_path_len = 0;int xmem_nvm_file_path_len = 0;int board_id_cmd_status = -1;char dst_buff[MAX_BUFF_SIZE];struct timeval tv;// 打开我们 tlv 文件,并将 tlv 文件的内容读到缓存中。if ((tlv_size = GetTlvFile(rampatch_file_path, rampatch_alt_file_path)) > 0) {...err = TlvDnldReq(tlv_size); // 这里开始下载 tlv 文件} // 获取 board id, 也是发送高通自定义的一些命令 Board Id 88 65if ((board_id_cmd_status = GetBoardIdReq()) < 0) {ALOGE("%s: failed to get board id(0x%x)", __func__, err);}tlv_size = -1;err = -1;if (IsXmemDownload(&tlv_size)) {...} else {/* NVM TLV file Downloading */tlv_size = -1;err = -1;nvm_alt_file_path_len = strlen(nvm_alt_file_path);nvm_file_path_len = strlen(nvm_file_path);if (board_id_cmd_status != -1) {if (nvm_file_path_len != 0) {memcpy(nvm_file_path_bid, nvm_file_path, nvm_file_path_len - 2);strlcat(nvm_file_path_bid, (char*)board_id_, sizeof(nvm_file_path_bid));}if (nvm_alt_file_path_len != 0){memcpy(nvm_alt_file_path_bid, nvm_alt_file_path, nvm_alt_file_path_len - 2);strlcat(nvm_alt_file_path_bid, (char*)board_id_, sizeof(nvm_alt_file_path_bid));}// 这里最终会 打开并加载 /vendor/bt_firmware/image/htnv20.bin 文件if ((tlv_size = GetTlvFile(nvm_file_path_bid/* /bt_firmware/image/htnv20.bin */,nvm_alt_file_path_bid /* /vendor/bt_firmware/image/htnv20.bin */)) < 0);}err = TlvDnldReq(tlv_size); // 走下载流程}return err;
}
1. PatchDLManager::GetTlvFile
int PatchDLManager::GetTlvFile(const char *file_path/* /bt_firmware/image/htbtfw20.tlv */, const char* alt_file_path/* /vendor/bt_firmware/image/htbtfw20.tlv */)
{FILE * pFile = NULL;int fileSize;int readSize;if (pFile == NULL) {// 打开 tlv 文件pFile = OpenPatchFile(file_path, alt_file_path);}if( pFile == NULL) {return -1;}/* Get File Size */fseek(pFile, 0, SEEK_END);fileSize = ftell(pFile); // 获取文件大小rewind(pFile);// 在栈上 分配内存, 这里感觉高通 做的不是很好。 如果 tlv 文件过大, 会导致 栈溢出。 可以使用malloc 分配在 堆上。pdata_buffer_ = (unsigned char*)new char[fileSize];if (pdata_buffer_ == NULL) {ALOGE("Allocated Memory failed");fclose(pFile);return -1;}/* Copy file into allocated buffer */readSize = fread(pdata_buffer_, 1, fileSize, pFile); // 将 tlv 文件内容全部读出来/* File Close */fclose(pFile); // 关闭文件if (readSize != fileSize) {ALOGE("Read file size(%d) not matched with actual file size (%d bytes)", readSize, fileSize);delete []pdata_buffer_;return -1;}if (ReadTlvInfo()) // 读 tlv 文件信息return readSize;elsereturn -1;
}
1. PatchDLManager::OpenPatchFile
01-14 17:15:30.262455 714 2620 E vendor.running.bluetooth@1.0-patch_dl_manager: /bt_firmware/image/htbtfw20.tlv File Open Fail No such file or directory (2)01-14 17:15:30.262504 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: File open /vendor/bt_firmware/image/htbtfw20.tlv succeeded
FILE* PatchDLManager::OpenPatchFile(const char *file_path, const char* alt_file_path) {FILE *pFile = NULL;if (!(file_path && (pFile = fopen( file_path, "r" )))) {ALOGE("%s File Open Fail %s (%d)", file_path, strerror(errno), errno); // 打开 /bt_firmware/image/htbtfw20.tlv 失败//Try opening from alternate pathif (!(alt_file_path && (pFile = fopen(alt_file_path, "r")))) {ALOGE("%s File Opening from alternate path: Fail %s (%d)", alt_file_path,strerror(errno), errno);return NULL;} else {ALOGI("File open %s succeeded", alt_file_path); // 最终 成功打开 /vendor/bt_firmware/image/htbtfw20.tlvreturn pFile;}} else {ALOGI("File open %s succeeded", file_path);return pFile;}
}
2. PatchDLManager::ReadTlvInfo
01-14 17:15:30.263698 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: ====================================================
01-14 17:15:30.263754 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: TLV Type : 0x1
01-14 17:15:30.263764 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Length : 204988 bytes
01-14 17:15:30.263772 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Total Length : 204956 bytes
01-14 17:15:30.263779 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Patch Data Length : 204920 bytes
01-14 17:15:30.263785 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Signing Format Version : 0x1
01-14 17:15:30.263795 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Signature Algorithm : 0x0
01-14 17:15:30.263800 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Event Handling : 0x3
01-14 17:15:30.263805 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Reserved : 0x0
01-14 17:15:30.263810 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Product ID : 0x0010
01-14 17:15:30.263815 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Rom Build Version : 0x0200
01-14 17:15:30.263820 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Patch Version : 0x6bb7
01-14 17:15:30.263825 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Reserved : 0x0
01-14 17:15:30.263831 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: Patch Entry Address : 0x0
01-14 17:15:30.263836 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: ====================================================
bool PatchDLManager::ReadTlvInfo() {int nvm_length, nvm_tot_len, nvm_index, i;bool status = false;unsigned short nvm_tag_len;tlv_patch_info *ptlv_header;tlv_nvm_hdr *nvm_ptr;unsigned char data_buf[PRINT_BUF_SIZE] = { 0, };unsigned char *nvm_byte_ptr;ptlv_header = (tlv_patch_info*)pdata_buffer_; // 将 tlv 文件 内容,强制格式转换/* checking for type of patch file */if (pdata_buffer_[0] == ELF_FLAG && !memcmp(&pdata_buffer_[1], "ELF", 3)) {} else {/* To handle different event between rampatch and NVM */tlv_type_ = ptlv_header->tlv_type;tlv_dwn_cfg_ = ptlv_header->tlv.patch.dwnd_cfg;}if (tlv_type_ == ELF_TYPE_PATCH) {} else if (ptlv_header->tlv_type == TLV_TYPE_PATCH ||ptlv_header->tlv_type == TLV_TYPE_PATCH_XMEM) {ALOGI("====================================================");ALOGI("TLV Type\t\t\t : 0x%x", ptlv_header->tlv_type);ALOGI("Length\t\t\t : %d bytes", (ptlv_header->tlv_length1) |(ptlv_header->tlv_length2 << 8) |(ptlv_header->tlv_length3 << 16));ALOGI("Total Length\t\t\t : %d bytes", ptlv_header->tlv.patch.tlv_data_len);ALOGI("Patch Data Length\t\t\t : %d bytes", ptlv_header->tlv.patch.tlv_patch_data_len);ALOGI("Signing Format Version\t : 0x%x", ptlv_header->tlv.patch.sign_ver);ALOGI("Signature Algorithm\t\t : 0x%x", ptlv_header->tlv.patch.sign_algorithm);ALOGI("Event Handling\t\t\t : 0x%x", ptlv_header->tlv.patch.dwnd_cfg);ALOGI("Reserved\t\t\t : 0x%x", ptlv_header->tlv.patch.reserved1);ALOGI("Product ID\t\t\t : 0x%04x\n", ptlv_header->tlv.patch.prod_id);ALOGI("Rom Build Version\t\t : 0x%04x\n", ptlv_header->tlv.patch.build_ver);ALOGI("Patch Version\t\t : 0x%04x\n", ptlv_header->tlv.patch.patch_ver);ALOGI("Reserved\t\t\t : 0x%x\n", ptlv_header->tlv.patch.reserved2);ALOGI("Patch Entry Address\t\t : 0x%x\n", (ptlv_header->tlv.patch.patch_entry_addr));ALOGI("====================================================");status = true;} else if ( (ptlv_header->tlv_type >= TLV_TYPE_BT_NVM) &&((ptlv_header->tlv_type <= TLV_TYPE_BT_FM_NVM)) ) {} else {ALOGE("TLV Header type is unknown (%d) ", ptlv_header->tlv_type);}return status;
}
2.PatchDLManager::TlvDnldReq
01-14 17:15:30.263854 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: TlvDnldReq: TLV size: 204992, Total Seg num: 843, remain size: 143
int PatchDLManager::TlvDnldReq(int tlv_size)
{int total_segment, remain_size, i, err = -1;unsigned char wait_cc_evt = true;bool is_last_seg = false;int segment_download_len = MAX_SIZE_PER_TLV_SEGMENT; // 每段 的大小 243 字节total_segment = tlv_size / MAX_SIZE_PER_TLV_SEGMENT; // 计算当前 tlv 文件有多少个段remain_size = (tlv_size < MAX_SIZE_PER_TLV_SEGMENT) ? \tlv_size : (tlv_size % MAX_SIZE_PER_TLV_SEGMENT); // 不够 一个段 的字节个数ALOGI("%s: TLV size: %d, Total Seg num: %d, remain size: %d",__func__, tlv_size, total_segment, remain_size);if (tlv_type_ == TLV_TYPE_PATCH || tlv_type_ == ELF_TYPE_PATCH|| tlv_type_ == TLV_TYPE_PATCH_XMEM) {/* Prior to Rome version 3.2(including inital few rampatch release of Rome 3.2), the event* handling mechanism is SKIP_EVT_NONE. After few release of rampatch for Rome 3.2, the* mechamism is changed to SKIP_EVT_VSE_CC. Rest of the mechanism is not used for now*/switch (tlv_dwn_cfg_) {case SKIP_EVT_VSE_CC:wait_vsc_evt_ = false;wait_cc_evt = false;ALOGI("Event handling type: SKIP_EVT_VSE_CC");break;}} // 开始逐个段 开始下载for (i = 0; i <= total_segment && !is_last_seg; i++) {/* check for last segment based on remaining size* and total number of segments.*/if ((remain_size && i == total_segment) ||(!remain_size && (i + 1) == total_segment)) {// 如果检测到当前 已经下载到最后一个段。is_last_seg = true;// Update segment download len if last segment is being downloadedif (remain_size)segment_download_len = remain_size;ALOGI("%s: Updating seg len to %d as last segment",__func__, segment_download_len);}if ((tlv_type_ == TLV_TYPE_PATCH || tlv_type_ == TLV_TYPE_PATCH_XMEM|| tlv_type_ == ELF_TYPE_PATCH) && is_last_seg) {/** 1. None of the command segments receive CCE* 2. No command segments receive VSE except the last one* 3. If tlv_dwn_cfg_ is SKIP_EVT_NONE then wait for VSE and CCE* ( except CCE is not received for last segment)*/wait_cc_evt = false;wait_vsc_evt_ = true;}patch_dnld_pending_ = true;// 调用 TlvDnldSegment 来下载 对应的段if ((err = TlvDnldSegment(i, segment_download_len, wait_cc_evt )) < 0) {}patch_dnld_pending_ = false;}error:if (patch_dnld_pending_)patch_dnld_pending_ = false;return err;
}
1. PatchDLManager::TlvDnldSegment
01-14 17:15:30.263872 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: TlvDnldSegment: Downloading TLV Patch segment no.0, size:24301-14 17:15:30.263877 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: FrameHciPkt: Sending EDL_PATCH_TLV_REQ_CMD01-14 17:15:30.263884 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: HCI-CMD 0: 0x1 0x0 0xfc 0xf5 0x1e 0xf301-14 17:15:30.263915 714 2620 I vendor.running.bluetooth@1.0-patch_dl_manager: TlvDnldSegment: Successfully downloaded patch segment: 0
int PatchDLManager::TlvDnldSegment(int index, int seg_size, unsigned char wait_cc_evt)
{int size = 0, err = -1;unsigned char cmd[HCI_MAX_CMD_SIZE];unsigned char rsp[HCI_MAX_EVENT_SIZE];// 将当前 下载段的序号 和 段的 大小 打印出来ALOGI("%s: Downloading TLV Patch segment no.%d, size:%d", __func__, index, seg_size);// 前面介绍过,这里会将 我们要发送的 cmd 组合好后, 打印出来/* Frame the HCI CMD PKT to be sent to Controller*/FrameHciPkt(cmd, EDL_PATCH_TLV_REQ_CMD/*0x1E*/, 0, index, seg_size);/* Total length of the packet to be sent to the Controller */size = (HCI_CMD_IND + HCI_COMMAND_HDR_SIZE + cmd[PLEN]);/* Initialize the RSP packet everytime to 0 */memset(rsp, 0x0, HCI_MAX_EVENT_SIZE);/* Send HCI Command packet to Controller */err = HciSendVsCmd((unsigned char*)cmd, rsp, size); // 这个前面也介绍过, 通过串口发送出去if ( err != size) {ALOGE("Failed to send the patch payload to the Controller! 0x%x", err);return err;}// 在下载 /vendor/bt_firmware/image/htbtfw20.tlv 文件时: 不用等待 命令返回事件。 所以不会执行到这里 if (!unified_hci) {if (wait_cc_evt) { // 在下载 /vendor/bt_firmware/image/htnv20.bin 时,需要确保每个命令,都有事件响应。所以会执行到这里err = ReadHciEvent(rsp, HCI_MAX_EVENT_SIZE);if ( err < 0) {ALOGE("%s: Failed to downlaod patch segment: %d!", __func__, index);return err;}}}ALOGI("%s: Successfully downloaded patch segment: %d", __func__, index);return err;
}
4. 关闭内部 LDO, 改为使用外部 LDO
/* 4. 关闭内部 LDO(一种线性稳压器,用于将较高的输入电压稳压成较低的输出电压),改为使用外部 LDO*/err = DisableInternalLdo();
int PatchDLManager::DisableInternalLdo()
{int ret = 0;if (IsExtldoEnabled()) {unsigned char cmd[5] = { 0x01, 0x0C, 0xFC, 0x01, 0x32 }; // 也是向 controller 发送 厂商自定义命令unsigned char rsp[HCI_MAX_EVENT_SIZE];ALOGI(" %s ", __func__);ret = HciSendVsCmd(cmd, rsp, 5);if (ret != 5) {ALOGE("%s: Send failed with ret value: %d", __func__, ret);ret = -1;} else {/* Wait for command complete event */ret = ReadHciEvent(rsp, HCI_MAX_EVENT_SIZE);if ( ret < 0) {ALOGE("%s: Failed to get response from controller", __func__);}}}return ret;
}
代码很简单。不再 表述。 但是我们搞软件的 很少有人知道 LDO. 我在这里简单 介绍一下。
1. LDO 相关介绍
1. LDO(Low Dropout Regulator)
LDO 是一种线性稳压器,用于将较高的输入电压稳压成较低的输出电压。特点是压差小、噪声低,适合对电压稳定性和噪声敏感的模拟/射频电路。
蓝牙芯片内部通常集成了多个 LDO,用于给不同模块(射频、基带、存储等)提供稳定电源。
2.内部 LDO vs 外部 LDO
项目 | 内部 LDO(Internal) | 外部 LDO(External) |
---|---|---|
集成度 | 高(芯片内自带) | 低(需要外部器件) |
成本 | 较低 | 成本略高 |
灵活性 | 低(固定参数) | 高(可选型号、调节性能) |
效率 | 一般 | 更高(可选更高效率型号) |
发热 | 芯片本身发热 | 外部分担发热 |
3. 为什么要“Disable internal LDO”?
有以下几个典型场景:
1. 外部 LDO 性能更好
- 某些应用(如车规、音频设备)对电源噪声、稳定性要求高,外部 LDO 可以提供更低噪声、更稳定的供电。
- 有些外部 LDO 支持更高电流、或热性能更好,适用于高负载场景。
2. 降低芯片内部发热
- 内部 LDO 是线性稳压器,不如 DC-DC 效率高。长时间使用容易导致芯片局部发热。
- 使用外部 LDO 可以将发热“搬”到芯片外部,有助于系统热管理。
3. 系统中已有 LDO 共用
- 某些主板(如智能手机、汽车主机)已经集成高性能电源管理芯片(PMIC),其中包含多个 LDO,可以统一管理多个模块电压,避免重复供电。
4. 如何实现?
在蓝牙芯片硬件设计或固件初始化过程中,通常会有一个配置寄存器或引脚选择项来:
-
禁用内部 LDO(比如设置一个 LDO_EN 寄存器为 0)
-
启用芯片对外部电源引脚的使用(通常芯片会提供
VDD_EXT
或VDD_LDO_IN
引脚)
这通常在 芯片数据手册 或 参考设计 中有说明。 这里可以对比 高通的参考理解。
5. 实际应用举例
举例:某蓝牙芯片的数据手册中写道:
If an external LDO is used to supply the RF domain, set
LDO_RF_EN = 0
and connect external 1.2V toVDD_RF
.
意思是:如果打算使用外部 1.2V LDO 来给 RF 区域供电,必须通过寄存器禁用内部 LDO,然后把外部 LDO 输出接到指定引脚上。
6.总结
“Disable internal LDO to use external LDO instead” 的含义是:
- 关闭蓝牙芯片内部的电压稳压器(LDO);
- 改为使用板级电路中外接的 LDO 提供稳定电压;
- 目的在于获得更好的电源性能、更低的噪声或更佳的热设计。
这是一种常见的硬件设计策略,尤其是在高端或对电源敏感的应用中,比如车载蓝牙、音频设备、工业通信模块等。
5. 获取当前固件(Firmware, FW)支持的附加功能(Add-on Features)列表
// 5. 获取当前固件(Firmware, FW)支持的附加功能(Add-on Features)列表GetAddOnFeatureList(); // 同样也是 发送高通自定义的命令
6. 发送 reset 命令
// 7. 发送 reset 命令err = HciReset(); // 这个是标准的 hci 命令
int PatchDLManager::HciReset()
{int size, err = 0;unsigned char cmd[HCI_MAX_CMD_SIZE];unsigned char rsp[HCI_MAX_EVENT_SIZE];hci_command_hdr *cmd_hdr;ALOGI("%s: HCI RESET ", __func__);memset(cmd, 0x0, HCI_MAX_CMD_SIZE);cmd_hdr = (hci_command_hdr*)(cmd + 1);cmd[0] = HCI_COMMAND_PKT;cmd_hdr->opcode = HCI_RESET;cmd_hdr->plen = 0;size = (HCI_CMD_IND + HCI_COMMAND_HDR_SIZE);err = uart_transport_->UartWrite(cmd, size);err = ReadCmdCmplEvent(rsp, HCI_MAX_EVENT_SIZE);}