【android bluetooth 协议分析 02】【bluetooth hal 层详解 6】【高通蓝牙hal主要流程介绍-下】

发布于:2025-05-28 ⋅ 阅读:(16) ⋅ 点赞:(0)

1. 背景

本节主要讨论 高通 蓝牙 hal 中,的一些流程。 看看你是否都清楚如下问题:

  1. 高通芯片电如何控制?
  2. 串口是在哪里控制的?
  3. 固件如何下载?
  4. 初始化流程是怎么样的?

如果你已经对上述讨论的问题,已经很清楚了,那你无需阅读该文章,请自行忽略。当然,也可以给笨叔挑挑错。 欢迎评论,一起探讨,毕竟都是笨叔自己的理解,难免有点出入,我也想进步!!!

阅读 本篇内容前, 请先阅读
【android bluetooth 协议分析 02】【bluetooth hal 层详解 3】【高通蓝牙hal主要流程介绍-上】
【android bluetooth 协议分析 02】【bluetooth hal 层详解 4】【高通蓝牙hal主要流程介绍-中】

我们继续接着 中篇 第4.小节,讲解。

本文讨论的重点是,如何打 patch

// hidl_hci/1.0/default/uart_controller.cpp

bool UartController::Init(PacketReadCallback pkt_read_cb)
{
...
  patch_dl_manager = new (std::nothrow)PatchDLManager(soc_type_, uart_transport, &power_manager_);

  patch_dl_manager->PerformChipInit()

...
}

下载固件其实也很简单, 这里主要分为 两步:

  1. 创建 PatchDLManager 对象
  2. 执行 该对象的 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 设置为 false
  memset(&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_0
  auto itr = PatchPathInfoMap_.find(chipset_ver_);
  if (itr != PatchPathInfoMap_.end())
    info = itr->second;

  // 2. 向芯片设置 波特率为 3M
  err = 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 函数总结为如下 几个步骤:

  1. 获取芯片 版本信息,并找到对应的 PatchPathManager
  2. 向芯片设置 波特率
  3. 下载 tlv 文件
  4. 关闭内部 LDO, 改为使用外部 LDO
  5. 获取当前固件(Firmware, FW)支持的附加功能(Add-on Features)列表
  6. 发送 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_0
  auto 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; // 0x01
  cmd_hdr->opcode = cmd_opcode_pack(HCI_VENDOR_CMD_OGF/*0x3f*/, HCI_PATCH_CMD_OCF/*0*/); // 0xfc00
  cmd_hdr->plen   = size; // 0x01
  cmd[4]      = edl_cmd; // 0x19

  switch (edl_cmd) {
    case EDL_PATCH_VER_REQ_CMD:
      // 将 cmd 打印出来方便调试:
      // HCI-CMD -1:     0x1         0x0      0xfc        0x1       0x19
      ALOGI("%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  通过 串口发送给 controller
  ret = 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_HASTINGS
  if (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 cmd
          ALOGI("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: // 0x19
    case 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(); // 获取最大 波特率 3M
  struct 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_MBPS
      return (uint8_t) USERIAL_BAUD_3M;
#else
      return (uint8_t) USERIAL_BAUD_3_2M;
#endif
      break;

    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 65
  if ((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;
  else
    return -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 path
    if (!(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.tlv
      return 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 downloaded
      if (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:243

01-14 17:15:30.263877   714  2620 I vendor.running.bluetooth@1.0-patch_dl_manager: FrameHciPkt: Sending EDL_PATCH_TLV_REQ_CMD

01-14 17:15:30.263884   714  2620 I vendor.running.bluetooth@1.0-patch_dl_manager: HCI-CMD 0:	0x1 	0x0 	0xfc 	0xf5 	0x1e 	0xf3

01-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_EXTVDD_LDO_IN 引脚)

这通常在 芯片数据手册参考设计 中有说明。 这里可以对比 高通的参考理解。


5. 实际应用举例

举例:某蓝牙芯片的数据手册中写道:

If an external LDO is used to supply the RF domain, set LDO_RF_EN = 0 and connect external 1.2V to VDD_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);
  

}

网站公告

今日签到

点亮在社区的每一天
去签到