OpenHarmony 3GPP协议开发深度剖析——一文读懂RIL - HarmonyOS技术社区 - 电子技术论坛 - 广受欢迎的专业电子论坛
分享 收藏 返回

[文章]

OpenHarmony 3GPP协议开发深度剖析——一文读懂RIL

(以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点)
夏德旺
软通动力信息技术(集团)股份有限公司

前言

市面上关于终端(手机)操作系统在 3GPP 协议开发的内容太少了,即使 Android 相关的资料都很少,Android 协议开发书籍我是没有见过的。可能是市场需求的缘故吧,现在市场上还是前后端软件开发从业人员最多,包括我自己。

基于我曾经也在某手机协议开发团队干过一段时间,协议的 AP 侧和 CP 侧开发都整过,于是想尝试下基于 OpenAtom OpenHarmony(以下简称“OpenHarmony”)源码写点内容,帮助大家了解下协议开发领域,尽可能将 3gpp 协议内容与 OpenHarmony 电话子系统模块进行结合讲解。据我所知,现在终端协议开发非常缺人。首先声明我不是协议专家,我也离开该领域有五六年了,如有错误,欢迎指正。

等我觉得自己整明白了,就会考虑出本《OpenHarmony 3GPP 协议开发深度剖析》书籍。

提到终端协议开发,我首先想到的就是 RIL 了。

专有名词

CP:Communication Processor(通信处理器),我一般就简单理解为 modem 侧,也可以理解为底层协议,这部分由各个 modem 芯片厂商完成(比如海思、高通)。

AP:Application Processor(应用处理器),通常就是指的手机终端,我一般就简单理解为上层协议,主要由操作系统 Telephony 服务来进行处理。

RIL: Radio Interface Layer(无线电接口层),我一般就简单理解为硬件抽象层,即 AP 侧将通信请求传给 CP 侧的中间层。

AT指令: AT 指令是应用于终端设备与 PC 应用之间的连接与通信的指令。

设计思想

常规的 Modem 开发与调试可以使用 AT 指令来进行操作,而各家的 Modem 芯片的 AT 指令都会有各自的差异。因此手机终端厂商为了能在各种不同型号的产品中集成不同 modem 芯片,需要进行解耦设计来屏蔽各家 AT 指令的差异。

于是 OpenHarmony 采用 RIL 对 Modem 进行 HAL(硬件抽象),作为系统与 Modem 之间的通信桥梁,为 AP 侧提供控制 Modem 的接口,各 Modem 厂商则负责提供对应于 AT 命令的 Vender RIL(这些一般为封装好的 so 库),从而实现操作系统与 Modem 间的解耦。

OpenHarmony RIL架构


框架层:Telephony Service,电话子系统核心服务模块,主要功能是初始化 RIL 管理、SIM 卡和搜网模块。对应 OpenHarmony 的源码仓库 OpenHarmony / telephony_core_service。这个模块也是非常重要的一个模块,后期单独再做详细解读。

硬件抽象层:即我们要讲的 RIL,对应 OpenHarmony 的源码仓库 OpenHarmony / telephony_ril_adapter。RIL Adapter 模块主要包括厂商库加载,业务接口实现以及事件调度管理。主要用于屏蔽不同 modem 厂商硬件差异,为上层提供统一的接口,通过注册 HDF 服务与上层接口通讯。

芯片层:Modem 芯片相关代码,即 CP 侧,这些代码各个 Modem 厂商是不开放的,不出现在 OpenHarmony 中。

硬件抽象层

硬件抽象层又被划分为了 hril_hdf 层、hril 层和 venderlib 层。

hril_hdf层:HDF 服务,基于 OpenHarmony HDF 框架,提供 hril 层与 Telephony Service 层进行通讯。

hril 层:hril 层的各个业务模块接口实现,比如通话、短彩信、数据业务等。

vendorlib层:各 Modem 厂商提供的对应于 AT 命令库,各个厂商可以出于代码闭源政策,在这里以 so 库形式提供。目前源码仓中已经提供了一套提供代码的 AT 命令操作,至于这个是针对哪个型号 modem 芯片的,我后续了解清楚再补充。

下面是 ril_adapter 仓的源码结构:

  1. base/telephony/ril_adapter
  2. ├── figures                             # readme资源文件
  3. ├── frameworks
  4. │   ├── BUILD.gn
  5. │   └── src                             # 序列化文件
  6. ├── interfaces                          # 对应提供上层各业务内部接口
  7. │   └── innerkits
  8. ├── services                            # 服务
  9. │   ├── hril                            # hril层的各个业务模块接口实现
  10. │   ├── hril_hdf                        # HDF服务
  11. │   └── vendor                          # 厂商库文件
  12. └── test                                # 测试代码
  13.     ├── BUILD.gn
  14.     ├── mock
  15.     └── unittest                        # 单元测试代码

核心业务逻辑梳理

本文解读 RIL 层很小一部分代码,RIL 是如何通过 HDF 与 Telephony 连接上的,以后更加完整的逻辑梳理会配上时序图讲解,会更加清晰。首先我们要对 OpenHarmony 的 HDF(Hardware Driver Foundation)驱动框架做一定了解,最好是动手写一个 Demo 案例,具体的可以单独去官网查阅 HDF 资料。

首先,找到 hril_hdf.c 文件的代码,它承担的是驱动业务部分,源码中是不带中文注释的,为了梳理清楚流程,我给源码关键部分加上了中文注释。

  1. /*
  2. * Copyright (C) 2021 Huawei Device Co., Ltd.
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. *     http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */

  15. #include "hril_hdf.h"

  16. #include <stdlib.h>
  17. #include <libudev.h>
  18. #include <pthread.h>

  19. #include "dfx_signal_handler.h"
  20. #include "parameter.h"

  21. #include "modem_adapter.h"
  22. #include "telephony_log_c.h"

  23. #define RIL_VENDOR_LIB_PATH "persist.sys.radio.vendorlib.path"
  24. #define BASE_HEX 16

  25. static struct HRilReport g_reportOps = {
  26.     OnCallReport,
  27.     OnDataReport,
  28.     OnModemReport,
  29.     OnNetworkReport,
  30.     OnSimReport,
  31.     OnSmsReport,
  32.     OnTimerCallback
  33. };

  34. static int32_t GetVendorLibPath(char *path)
  35. {
  36.     int32_t code = GetParameter(RIL_VENDOR_LIB_PATH, "", path, PARAMETER_SIZE);
  37.     if (code <= 0) {
  38.         TELEPHONY_LOGE("Failed to get vendor library path through system properties. err:%{public}d", code);
  39.         return HDF_FAILURE;
  40.     }
  41.     return HDF_SUCCESS;
  42. }

  43. static U***DeviceInfo *GetPresetInformation(const char *vId, const char *pId)
  44. {
  45.     char *out = NULL;
  46.     U***DeviceInfo *uDevInfo = NULL;
  47.     int32_t idVendor = (int32_t)strtol(vId, &out, BASE_HEX);
  48.     int32_t idProduct = (int32_t)strtol(pId, &out, BASE_HEX);
  49.     for (uint32_t i = 0; i < sizeof(g_u***ModemVendorInfo) / sizeof(U***DeviceInfo); i++) {
  50.         if (g_u***ModemVendorInfo[i].idVendor == idVendor && g_u***ModemVendorInfo[i].idProduct == idProduct) {
  51.             TELEPHONY_LOGI("list index:%{public}d", i);
  52.             uDevInfo = &g_u***ModemVendorInfo[i];
  53.             break;
  54.         }
  55.     }
  56.     return uDevInfo;
  57. }

  58. static U***DeviceInfo *GetU***DeviceInfo(void)
  59. {
  60.     struct udev *udev;
  61.     struct udev_enumerate *enumerate;
  62.     struct udev_list_entry *devices, *dev_list_entry;
  63.     struct udev_device *dev;
  64.     U***DeviceInfo *uDevInfo = NULL;

  65.     udev = udev_new();
  66.     if (udev == NULL) {
  67.         TELEPHONY_LOGE("Can't create udev");
  68.         return uDevInfo;
  69.     }
  70.     enumerate = udev_enumerate_new(udev);
  71.     if (enumerate == NULL) {
  72.         TELEPHONY_LOGE("Can't create enumerate");
  73.         return uDevInfo;
  74.     }
  75.     udev_enumerate_add_match_subsystem(enumerate, "tty");
  76.     udev_enumerate_scan_devices(enumerate);
  77.     devices = udev_enumerate_get_list_entry(enumerate);
  78.     udev_list_entry_foreach(dev_list_entry, devices) {
  79.         const char *path = udev_list_entry_get_name(dev_list_entry);
  80.         if (path == NULL) {
  81.             continue;
  82.         }
  83.         dev = udev_device_new_from_syspath(udev, path);
  84.         if (dev == NULL) {
  85.             continue;
  86.         }
  87.         dev = udev_device_get_parent_with_subsystem_devtype(dev, "u***", "u***_device");
  88.         if (!dev) {
  89.             TELEPHONY_LOGE("Unable to find parent u*** device.");
  90.             return uDevInfo;
  91.         }
  92.         const char *cIdVendor = udev_device_get_sysattr_value(dev, "idVendor");
  93.         const char *cIdProduct = udev_device_get_sysattr_value(dev, "idProduct");
  94.         uDevInfo = GetPresetInformation(cIdVendor, cIdProduct);
  95.         udev_device_unref(dev);
  96.         if (uDevInfo != NULL) {
  97.             break;
  98.         }
  99.     }
  100.     udev_enumerate_unref(enumerate);
  101.     udev_unref(udev);
  102.     return uDevInfo;
  103. }

  104. static void LoadVendor(void)
  105. {
  106.     const char *rilLibPath = NULL;
  107.     char vendorLibPath[PARAMETER_SIZE] = {0};
  108.     // Pointer to ril init function in vendor ril
  109.     const HRilOps *(*rilInitOps)(const struct HRilReport *) = NULL;
  110.     // functions returned by ril init function in vendor ril
  111.     const HRilOps *ops = NULL;

  112.     U***DeviceInfo *uDevInfo = GetU***DeviceInfo();
  113.     if (GetVendorLibPath(vendorLibPath) == HDF_SUCCESS) {
  114.         rilLibPath = vendorLibPath;
  115.     } else if (uDevInfo != NULL) {
  116.         rilLibPath = uDevInfo->libPath;
  117.     } else {
  118.         TELEPHONY_LOGI("use default vendor lib.");
  119.         rilLibPath = g_u***ModemVendorInfo[DEFAULT_MODE_INDEX].libPath;
  120.     }
  121.     if (rilLibPath == NULL) {
  122.         TELEPHONY_LOGE("dynamic library path is empty");
  123.         return;
  124.     }

  125.     TELEPHONY_LOGI("RilInit LoadVendor start with rilLibPath:%{public}s", rilLibPath);
  126.     g_dlHandle = dlopen(rilLibPath, RTLD_NOW);
  127.     if (g_dlHandle == NULL) {
  128.         TELEPHONY_LOGE("dlopen %{public}s is fail. %{public}s", rilLibPath, dlerror());
  129.         return;
  130.     }
  131.     rilInitOps = (const HRilOps *(*)(const struct HRilReport *))dlsym(g_dlHandle, "RilInitOps");
  132.     if (rilInitOps == NULL) {
  133.         dlclose(g_dlHandle);
  134.         TELEPHONY_LOGE("RilInit not defined or exported");
  135.         return;
  136.     }
  137.     ops = rilInitOps(&g_reportOps);
  138.     HRilRegOps(ops);
  139.     TELEPHONY_LOGI("HRilRegOps completed");
  140. }

  141. // 用来处理用户态发下来的消息
  142. static int32_t RilAdapterDispatch(
  143.     struct HdfDeviceIoClient *client, int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
  144. {
  145.     int32_t ret;
  146.     static pthread_mutex_t dispatchMutex = PTHREAD_MUTEX_INITIALIZER;
  147.     pthread_mutex_lock(&dispatchMutex);
  148.     TELEPHONY_LOGI("RilAdapterDispatch cmd:%{public}d", cmd);
  149.     ret = DispatchRequest(cmd, data);
  150.     pthread_mutex_unlock(&dispatchMutex);
  151.     return ret;
  152. }

  153. static struct IDeviceIoService g_rilAdapterService = {
  154.     .Dispatch = RilAdapterDispatch,
  155.     .Open = NULL,
  156.     .Release = NULL,
  157. };

  158. //驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
  159. static int32_t RilAdapterBind(struct HdfDeviceObject *device)
  160. {
  161.     if (device == NULL) {
  162.         return HDF_ERR_INVALID_OBJECT;
  163.     }
  164.     device->service = &g_rilAdapterService;
  165.     return HDF_SUCCESS;
  166. }

  167. // 驱动自身业务初始的接口
  168. static int32_t RilAdapterInit(struct HdfDeviceObject *device)
  169. {
  170.     if (device == NULL) {
  171.         return HDF_ERR_INVALID_OBJECT;
  172.     }
  173.     DFX_InstallSignalHandler();
  174.     struct HdfSBuf ****uf = HdfSbufTypedObtain(SBUF_IPC);
  175.     if (***uf == NULL) {
  176.         TELEPHONY_LOGE("HdfSampleDriverBind, failed to obtain ipc ***uf");
  177.         return HDF_ERR_INVALID_OBJECT;
  178.     }
  179.     if (!HdfSbufWriteString(***uf, "string")) {
  180.         TELEPHONY_LOGE("HdfSampleDriverBind, failed to write string to ipc ***uf");
  181.         HdfSbufRecycle(***uf);
  182.         return HDF_FAILURE;
  183.     }
  184.     if (***uf != NULL) {
  185.         HdfSbufRecycle(***uf);
  186.     }
  187.     TELEPHONY_LOGI("***uf IPC obtain success!");
  188.     LoadVendor();
  189.     return HDF_SUCCESS;
  190. }

  191. // 驱动资源释放的接口
  192. static void RilAdapterRelease(struct HdfDeviceObject *device)
  193. {
  194.     if (device == NULL) {
  195.         return;
  196.     }
  197.     dlclose(g_dlHandle);
  198. }

  199. //驱动入口注册到HDF框架,这里配置的moduleName是找到Telephony模块与RIL进行通信的一个关键配置
  200. struct HdfDriverEntry g_rilAdapterDevEntry = {
  201.     .moduleVersion = 1,
  202.     .moduleName = "hril_hdf",
  203.     .Bind = RilAdapterBind,
  204.     .Init = RilAdapterInit,
  205.     .Release = RilAdapterRelease,
  206. };
  207. // 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
  208. HDF_INIT(g_rilAdapterDevEntry);

上述代码中配置了对应该驱动的 moduleName 为"hril_hdf",因此我们需要去找到对应驱动的配置文件,以 Hi3516DV300 开发板为例,它的驱动配置在 vendor_hisilicon/ Hi3516DV300 / hdf_config / uhdf / device_info.hcs 代码中可以找到,如下:

  1. riladapter :: host {
  2.             hostName = "riladapter_host";
  3.             priority = 50;
  4.             riladapter_device :: device {
  5.                 device0 :: deviceNode {
  6.                     policy = 2;
  7.                     priority = 100;
  8.                     moduleName = "libhril_hdf.z.so";
  9.                     serviceName = "cellular_radio1";
  10.                 }
  11.             }
  12.         }

这里可以发现该驱动对应的服务名称为 cellular_radio1,那么 telephony_core_service 通过 HDF 与 RIL 进行通信肯定会调用到该服务名称,因此无查找 telephony_core_service 的相关代码,可以很快定位到 telephony_core_service/ services / tel_ril / src / tel_ril_manager.cpp 该代码,改代码中有一个关键类 TelRilManager,它用来负责管理 tel_ril。

看 tel_ril_manager.cpp 中的一个关键函数 ConnectRilAdapterService,它就是用来通过 HDF 框架获取RIL_ADAPTER 的服务,之前定义过 RIL_ADAPTER_SERVICE_NAME 常量为 "cellular_radio1",它就是在 vendor_hisilicon/ XXXX / hdf_config / uhdf / device_info.hcs 中配置的 hril_hdf 驱动对应的服务名称。

  1. bool TelRilManager::ConnectRilAdapterService()
  2. {
  3.     std::lock_guard<std::mutex> lock_l(mutex_);
  4.     rilAdapterRemoteObj_ = nullptr;
  5.     auto servMgr_ = OHOS::HDI::ServiceManager::V1_0::IServiceManager::Get();
  6.     if (servMgr_ == nullptr) {
  7.         TELEPHONY_LOGI("Get service manager error!");
  8.         return false;
  9.     }

  10.     //通过HDF框架获取RIL_ADAPTER的服务,之前定义过RIL_ADAPTER_SERVICE_NAME常量为"cellular_radio1",它就是在 vendor_hisilicon/ XXXX / hdf_config / uhdf / device_info.hcs中配置的hril_hdf驱动对应的服务名称
  11.     rilAdapterRemoteObj_ = servMgr_->GetService(RIL_ADAPTER_SERVICE_NAME.c_str());
  12.     if (rilAdapterRemoteObj_ == nullptr) {
  13.         TELEPHONY_LOGE("bind hdf error!");
  14.         return false;
  15.     }
  16.     if (death_ == nullptr) {
  17.         TELEPHONY_LOGE("create HdfDeathRecipient object failed!");
  18.         rilAdapterRemoteObj_ = nullptr;
  19.         return false;
  20.     }
  21.     if (!rilAdapterRemoteObj_->AddDeathRecipient(death_)) {
  22.         TELEPHONY_LOGE("AddDeathRecipient hdf failed!");
  23.         rilAdapterRemoteObj_ = nullptr;
  24.         return false;
  25.     }

  26.     int32_t ret = SetCellularRadioIndication();
  27.     if (ret != CORE_SERVICE_SUCCESS) {
  28.         TELEPHONY_LOGE("SetCellularRadioIndication error, ret:%{public}d", ret);
  29.         return false;
  30.     }
  31.     ret = SetCellularRadioResponse();
  32.     if (ret != CORE_SERVICE_SUCCESS) {
  33.         TELEPHONY_LOGE("SetCellularRadioResponse error, ret:%{public}d", ret);
  34.         return false;
  35.     }

  36.     return true;
  37. }

回帖(1)

玩硬件的女孩纸

2022-5-24 17:10:52
感谢大佬分享,期待多多更新~

更多回帖

×
发帖