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

发布于:2025-05-24 ⋅ 阅读:(15) ⋅ 点赞:(0)

1. 背景

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

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

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

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

我们继续接着 上篇 3.3.3 小节,讲解。
【android bluetooth 协议分析 02】【bluetooth hal 层详解 3】【高通蓝牙hal主要流程介绍-上】3.3.3 调用 controller_->Init 函数

// hidl_hci/1.0/default/uart_controller.cpp
bool UartController::Init(PacketReadCallback pkt_read_cb)
{

	power_manager_.Init(soc_type_);



  // 1. 给芯片 上电
  if (soc_need_reload_patch) {
    // power off the chip first
    power_manager_.SetPower(false);

    // power on the chip using power manager
    power_manager_.SetPower(true);
  }



  // 2. 初始化 HciTransport
  hci_transport_ = static_cast<HciTransport*> (uart_transport);
  ret = uart_transport->Init(soc_type_, soc_need_reload_patch);

  // 3. 创建 固件 补丁管理器
  patch_dl_manager = new (std::nothrow)PatchDLManager(soc_type_, uart_transport, &power_manager_);

  uart_transport->ClockOperation(USERIAL_OP_CLK_ON);



  //Download the NVM/RAM patch
  if (soc_need_reload_patch) {
    logger_->PropertyGet("vendor.wc_transport.skip_patch_dload", skip_patch_download, "false");
    if (strcmp(skip_patch_download, "true") != 0) {
      // 4. 开始打补丁, 下载固件
      if (patch_dl_manager->PerformChipInit() < 0) {
      }
      temp_add_on_features = patch_dl_manager->GetAddOnFeatureList();
    } else {}
  }


  // 获取 controller 的芯片版本, 做记录使用
  chipset_ver_ = patch_dl_manager->GetChipVersion();

  init_done_ = true;

  ALOGD("Init succeded");
  return init_done_;
}

将上面的操作总结为如下:

  1. 给芯片 上电
  2. 初始化 HciTransport
  3. 开始打补丁, 下载固件

下面分别展开讲解

2. 芯片上电

  UartController::PowerManager power_manager_;

power_manager_ 是一个变量,并不是一个指针。他伴随着 UartController 对象的创建而创建。

芯片上电主要调用如下步骤:

power_manager_.Init(soc_type_);

power_manager_.SetPower(false);

power_manager_.SetPower(true);

1. PowerManager :: Init

void PowerManager :: Init(BluetoothSocType soc_type)
{
  if (pm_state_ != POWER_MANAGER_OFF)
    return;
  bt_soc_type_ = soc_type;
}

这里仅仅是保存了当前 芯片soc 的类型。 我们这里的类型通过如下两个属性获得。

# bt0
persist.vendor.qcom.bluetooth.soc

# bt1
persist.vendor.qcom.bluetooth.soc1

bt0 :hastings
bt1: rome

2. PowerManager :: SetPower


bool SetPower(bool enable, bool retentionMode = false);

// 默认 retentionMode = false

bool PowerManager :: SetPower(bool enable, bool retentionMode)
{
  bool ret = false;
  ALOGD("%s: enable: %x", __func__, enable);
  switch (bt_soc_type_) {
    int rfkill_fd;

    case BT_SOC_ROME:
    case BT_SOC_HASTINGS:
      // 1.获取 rfkill_fd
      rfkill_fd = GetRfkillFd();
      if (rfkill_fd < 0)
        return false;

      // 2. 通过 rfkill 去控制电
      ret = ControlRfkill(rfkill_fd, enable);
      close(rfkill_fd);
      break;
  }


  return ret;
}

这里我只保留了最重要的code. 我们芯片类型是 BT_SOC_HASTINGS 和 BT_SOC_ROME

这里看到直接调用了 rfkill 相关的内容

1. PowerManager :: GetRfkillFd


int PowerManager :: GetRfkillFd()
{
  int rfkill_fd;

  rfkill_fd = InitializeRfkill();
  return rfkill_fd;
}
#define BT_RFKILL_NAME              "bt_power"
#define BT_RFKILL1_NAME             "bt_power_new"

const char* Util::getBtRfkillName()
{
  return isHwSlot1() ? BT_RFKILL1_NAME : BT_RFKILL_NAME;
}

#define RFKILL_STATE_PATH  "/sys/class/rfkill/rfkill%d/state"
#define RFKILL_TYPE_PATH  "/sys/class/rfkill/rfkill%d/type"
#define RFKILL_NAME_PATH  "/sys/class/rfkill/rfkill%d/name"

#define BT_RFKILL_TYPE    "bluetooth"

int PowerManager :: InitializeRfkill()
{
  int fd;
  char rfkill_type[64] = {'\0'}, rfkill_name[64] = {'\0'}, rfkill_state[64] = {'\0'};

  /* 枚举所有 rfkill 设备,找到和蓝牙相关的 */
  for (int i = 0; rfkill_id_ == -1; i++) {
    /* 
     根据 i: 去拼接字符串:
	     1. /sys/class/rfkill/rfkill0/type
	     2. /sys/class/rfkill/rfkill0/name
    */
    snprintf(rfkill_type, sizeof(rfkill_type), (char *)RFKILL_TYPE_PATH, i);
    snprintf(rfkill_name, sizeof(rfkill_name), (char *)RFKILL_NAME_PATH, i);

    // 如果 type 和 name 文件不存在直接跳过
    if (!Util::fileExist(rfkill_type) ||
        !Util::fileExist(rfkill_name)) {
      break;
    }

    /* 
      如果  (cat /sys/class/rfkill/rfkill0/type) == (bluetooth) 并且
           (cat /sys/class/rfkill/rfkill0/name) == bt0(bt_power) == bt1(bt_power_new)
      说明已经找到了对应的蓝牙 rfkill 节点
    */
    if (Util::fileContainString(rfkill_type, BT_RFKILL_TYPE) &&
        Util::fileContainString(rfkill_name, Util::getBtRfkillName())) {
      rfkill_id_ = i;
      break;
    }
  }

  if (rfkill_id_ == -1) {
    /* rfkill device not found, return */
    return -1;
  }

  // 这里拼接出, 我们操作的节点: /sys/class/rfkill/rfkill0/state
  snprintf(rfkill_state, sizeof(rfkill_state), (char *)RFKILL_STATE_PATH, rfkill_id_);

  // 打开 /sys/class/rfkill/rfkill0/state 节点
  fd = open(rfkill_state, O_RDWR);
  if (fd < 0)
    ALOGE("open(%s) for write failed: %s (%d)", rfkill_state, strerror(errno), errno);

  return fd;
}
  • 通过 对 PowerManager :: InitializeRfkill 分析我们可以知道,我们蓝牙的电 是通过 操作 /sys/class/rfkill/rfkill0/state 节点

2. PowerManager :: ControlRfkill

bool PowerManager :: ControlRfkill(int rfkill_fd, bool enable)
{
  char power_on = enable ? '1' : '0';

  ALOGD("%s: rfkill_fd: %d, enable: %x", __func__, rfkill_fd, enable);

  if (write(rfkill_fd, &power_on, 1) < 0) {
    ALOGE("%s: write rfkill failed: %s (%d)", __func__, strerror(errno), errno);
    Cleanup(rfkill_fd);
    return false;
  }

  return true;
}
  • 通过 向 /sys/class/rfkill/rfkill0/state 节点 写1 上电, 写0 下电。

这里就算回答清楚了 开篇提到的,高通芯片电如何控制?

3. 初始化 HciTransport

接下来我们来看一下 uart_transport 中有干了那些不为认知的事情。

  // 1. 创建一个 HciUartTransport 对象, HciUartTransport 继承于 HciTransport
  uart_transport = new (std::nothrow)HciUartTransport(health_info)
  hci_transport_ = static_cast<HciTransport*> (uart_transport);

  // 2. 调用 HciUartTransport->Init
  ret = uart_transport->Init(soc_type_, soc_need_reload_patch);

1. 创建 HciUartTransport 对象

class HciUartTransport : public HciTransport {
  
  HciUartTransport(HealthInfoLog* theHealthInfo) {
    ctrl_fd_ = -1;
    data_fd_ = -1;
    health_info = theHealthInfo;
    Util::getUartDevice(uart_device_);
  };

}
#define BT_HIDL_UART_PROP "persist.vendor.bt.hidl.uart"
#define BT_HIDL1_UART_PROP "persist.vendor.bt.hidl1.uart"

#define UART_DEVICE                 "/dev/ttyHS0"
#define UART1_DEVICE                "/dev/ttyHS2"

int Util::getHwSlot()
{
  return hw_slot_;
}

bool Util::getUartDevice(char *device)
{
  return getUartDevice(getHwSlot(), device);
}

bool Util::getUartDevice(int slot, char *device)
{
  if (!device)
    return false;

  switch (slot) {
    // 根据不同的 slot, 来区分 bt0, bt1
    case BLUETOOTH_SLOT_0: {
      /*
	      如果 persist.vendor.bt.hidl.uart 属性中没有指定 串口,那就使用默认的串口 /dev/ttyHS0
      */
      property_get(BT_HIDL_UART_PROP, device, UART_DEVICE);
      return true;
    }
    case BLUETOOTH_SLOT_1: {
      /*
	      如果 persist.vendor.bt.hidl1.uart 属性中没有指定 串口,那就使用默认的串口 /dev/ttyHS2
      */
      property_get(BT_HIDL1_UART_PROP, device, UART1_DEVICE);
      return true;
    }
    default: {
      /* Invalid Bluetooth slot */
      return false;
    }
  }
}

bt0 和 bt1 串口 可以使用默认的配置, 同时也可以通过系统属性来配置。这里可以根据不同的项目。灵活配置。

会将 串口设备最终保存在 HciUartTransport::uart_device_ 变量中。

2. HciUartTransport::Init


ret = uart_transport->Init(soc_type_, soc_need_reload_patch);

bool HciUartTransport::Init(BluetoothSocType soc_type, bool need_reload)
{


  switch (soc_type) {


    case BT_SOC_HASTINGS:
    case BT_SOC_MOSELLE:
    case BT_SOC_HAMILTON:


      status = InitTransport(&normal_cfg);
    break;


    default: {
      ALOGE("Unknown chip type: %d", soc_type);
    }
  }
  return status;
}

// private functions
bool HciUartTransport::InitTransport(tUSERIAL_CFG *p_cfg)
{
  

    ALOGI("%s: opening %s", __func__, uart_device_);

	// 这里就会去 打开我们的串口。
    if ((ctrl_fd_ = open(uart_device_, O_RDWR | O_NOCTTY)) == -1) {
      ALOGE("%s: unable to open %s: %s(%d)", __func__, uart_device_,
            strerror(errno), errno);
      return false;
    }


  return true;
}

在 HciUartTransport::InitTransport 中涉及到 串口波特率 流控, 的初始化。 这里不在讨论。

看到这里,你是否对, 该问题: 串口是在哪里控制的?有答案了

4. 打补丁,下载固件

这个下篇 中介绍


网站公告

今日签到

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