当前位置: 首页 > news >正文

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

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 firstpower_manager_.SetPower(false);// power on the chip using power managerpower_manager_.SetPower(true);}// 2. 初始化 HciTransporthci_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 patchif (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 = falsebool 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_fdrfkill_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/type2. /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/statesnprintf(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 继承于 HciTransportuart_transport = new (std::nothrow)HciUartTransport(health_info)hci_transport_ = static_cast<HciTransport*> (uart_transport);// 2. 调用 HciUartTransport->Initret = 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, bt1case 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. 打补丁,下载固件

这个下篇 中介绍

http://www.xdnf.cn/news/606997.html

相关文章:

  • 26考研|高等代数:线性变换
  • 纯虚函数必须在派生类中给出定义吗? 虚函数必须在派生类中给出定义吗?
  • Honeywell TK-PRS021 C200
  • Redis核心用法与通用命令全解析
  • Go语言中为什么map、slice、channel需要var之后还要make一下?
  • FTP Bounce Attack:原理、影响与防御
  • 如何安装和维护 Linux 系统?
  • 2025电工杯数学建模竞赛B题 城市垃圾分类运输的路径优化与调度 保姆级教程讲解|模型讲解
  • Missashe线代题型总结
  • 编译器ar命令参数
  • java中多线程的实现方式
  • 【算法篇】二分查找算法:基础篇
  • AES加密模式详解及OpenSSL C库函数指南
  • 【SSL部署与优化​】​​OCSP Stapling配置指南:减少证书验证延迟​​
  • 相机坐标系、图像坐标系和世界坐标系
  • redis 基本命令-17 (KEYS、EXISTS、TYPE、TTL)
  • 新手SEO高效入门实战精要
  • 40 岁 Windows 开启 AI 转型:从系统到生态的智能重构
  • 「MATLAB」计算校验和 Checksum
  • C++:动态刷新打印内容
  • 使用Gemini, LangChain, Gradio打造一个书籍推荐系统 (第二部分)
  • 【Elasticsearch】给所索引创建多个别名
  • 【Bluedroid】蓝牙HID Host disconnect流程源码解析
  • UE4游戏查找本地角色数据的方法-SDK
  • 从零开始的抽奖系统创作(4)
  • FPGA 42 ,时序约束深度解析与实战应用指南( FPGA 时序约束 )
  • 分享|16个含源码和数据集的计算机视觉实战项目
  • VMware虚拟机突然无法ssh连接
  • Spring Boot WebFlux流式返回全攻略:从基础到企业级实践
  • PHP7内核剖析 学习笔记 第八章 命名空间