OpenHarmony 3GPP协议开发深度剖析--不得不弄明白的RIL

系统 OpenHarmony
基于我曾经也在某手机协议开发团队干过一段时间,协议的AP侧和CP侧开发都整过,于是想尝试下基于OpenHarmony源码写点内容,帮助大家了解下协议开发领域。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

前言

市面上关于终端(手机)操作系统在3GPP协议开发的内容太少了,即使Android相关的资料都很少,Android协议开发书籍我是没有见过的。可能是市场需求的缘故吧,现在市场上还是前后端软件开发从业人员最多,包括我自己。基于我曾经也在某手机协议开发团队干过一段时间,协议的AP侧和CP侧开发都整过,于是想尝试下基于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仓的源码结构:

base/telephony/ril_adapter
├── figures # readme资源文件
├── frameworks
├── BUILD.gn
└── src # 序列化文件
├── interfaces # 对应提供上层各业务内部接口
└── innerkits
├── services # 服务
├── hril # hril层的各个业务模块接口实现
├── hril_hdf # HDF服务
└── vendor # 厂商库文件
└── test # 测试代码
├── BUILD.gn
├── mock
└── unittest # 单元测试代码

核心业务逻辑梳理

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

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

/*
* Copyright (C) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hril_hdf.h"
#include <stdlib.h>
#include <libudev.h>
#include <pthread.h>
#include "dfx_signal_handler.h"
#include "parameter.h"
#include "modem_adapter.h"
#include "telephony_log_c.h"
#define RIL_VENDOR_LIB_PATH "persist.sys.radio.vendorlib.path"
#define BASE_HEX 16
static struct HRilReport g_reportOps = {
OnCallReport,
OnDataReport,
OnModemReport,
OnNetworkReport,
OnSimReport,
OnSmsReport,
OnTimerCallback
};
static int32_t GetVendorLibPath(char *path)
{
int32_t code = GetParameter(RIL_VENDOR_LIB_PATH, "", path, PARAMETER_SIZE);
if (code <= 0) {
TELEPHONY_LOGE("Failed to get vendor library path through system properties. err:%{public}d", code);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
static UsbDeviceInfo *GetPresetInformation(const char *vId, const char *pId)
{
char *out = NULL;
UsbDeviceInfo *uDevInfo = NULL;
int32_t idVendor = (int32_t)strtol(vId, &out, BASE_HEX);
int32_t idProduct = (int32_t)strtol(pId, &out, BASE_HEX);
for (uint32_t i = 0; i < sizeof(g_usbModemVendorInfo) / sizeof(UsbDeviceInfo); i++) {
if (g_usbModemVendorInfo[i].idVendor == idVendor && g_usbModemVendorInfo[i].idProduct == idProduct) {
TELEPHONY_LOGI("list index:%{public}d", i);
uDevInfo = &g_usbModemVendorInfo[i];
break;
}
}
return uDevInfo;
}
static UsbDeviceInfo *GetUsbDeviceInfo(void)
{
struct udev *udev;
struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *dev_list_entry;
struct udev_device *dev;
UsbDeviceInfo *uDevInfo = NULL;
udev = udev_new();
if (udev == NULL) {
TELEPHONY_LOGE("Can't create udev");
return uDevInfo;
}
enumerate = udev_enumerate_new(udev);
if (enumerate == NULL) {
TELEPHONY_LOGE("Can't create enumerate");
return uDevInfo;
}
udev_enumerate_add_match_subsystem(enumerate, "tty");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices) {
const char *path = udev_list_entry_get_name(dev_list_entry);
if (path == NULL) {
continue;
}
dev = udev_device_new_from_syspath(udev, path);
if (dev == NULL) {
continue;
}
dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");
if (!dev) {
TELEPHONY_LOGE("Unable to find parent usb device.");
return uDevInfo;
}
const char *cIdVendor = udev_device_get_sysattr_value(dev, "idVendor");
const char *cIdProduct = udev_device_get_sysattr_value(dev, "idProduct");
uDevInfo = GetPresetInformation(cIdVendor, cIdProduct);
udev_device_unref(dev);
if (uDevInfo != NULL) {
break;
}
}
udev_enumerate_unref(enumerate);
udev_unref(udev);
return uDevInfo;
}
static void LoadVendor(void)
{
const char *rilLibPath = NULL;
char vendorLibPath[PARAMETER_SIZE] = {0};
// Pointer to ril init function in vendor ril
const HRilOps *(*rilInitOps)(const struct HRilReport *) = NULL;
// functions returned by ril init function in vendor ril
const HRilOps *ops = NULL;
UsbDeviceInfo *uDevInfo = GetUsbDeviceInfo();
if (GetVendorLibPath(vendorLibPath) == HDF_SUCCESS) {
rilLibPath = vendorLibPath;
} else if (uDevInfo != NULL) {
rilLibPath = uDevInfo->libPath;
} else {
TELEPHONY_LOGI("use default vendor lib.");
rilLibPath = g_usbModemVendorInfo[DEFAULT_MODE_INDEX].libPath;
}
if (rilLibPath == NULL) {
TELEPHONY_LOGE("dynamic library path is empty");
return;
}
TELEPHONY_LOGI("RilInit LoadVendor start with rilLibPath:%{public}s", rilLibPath);
g_dlHandle = dlopen(rilLibPath, RTLD_NOW);
if (g_dlHandle == NULL) {
TELEPHONY_LOGE("dlopen %{public}s is fail. %{public}s", rilLibPath, dlerror());
return;
}
rilInitOps = (const HRilOps *(*)(const struct HRilReport *))dlsym(g_dlHandle, "RilInitOps");
if (rilInitOps == NULL) {
dlclose(g_dlHandle);
TELEPHONY_LOGE("RilInit not defined or exported");
return;
}
ops = rilInitOps(&g_reportOps);
HRilRegOps(ops);
TELEPHONY_LOGI("HRilRegOps completed");
}
// 用来处理用户态发下来的消息
static int32_t RilAdapterDispatch(
struct HdfDeviceIoClient *client, int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
{
int32_t ret;
static pthread_mutex_t dispatchMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&dispatchMutex);
TELEPHONY_LOGI("RilAdapterDispatch cmd:%{public}d", cmd);
ret = DispatchRequest(cmd, data);
pthread_mutex_unlock(&dispatchMutex);
return ret;
}
static struct IDeviceIoService g_rilAdapterService = {
.Dispatch = RilAdapterDispatch,
.Open = NULL,
.Release = NULL,
};
//驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
static int32_t RilAdapterBind(struct HdfDeviceObject *device)
{
if (device == NULL) {
return HDF_ERR_INVALID_OBJECT;
}
device->service = &g_rilAdapterService;
return HDF_SUCCESS;
}
// 驱动自身业务初始的接口
static int32_t RilAdapterInit(struct HdfDeviceObject *device)
{
if (device == NULL) {
return HDF_ERR_INVALID_OBJECT;
}
DFX_InstallSignalHandler();
struct HdfSBuf *sbuf = HdfSbufTypedObtain(SBUF_IPC);
if (sbuf == NULL) {
TELEPHONY_LOGE("HdfSampleDriverBind, failed to obtain ipc sbuf");
return HDF_ERR_INVALID_OBJECT;
}
if (!HdfSbufWriteString(sbuf, "string")) {
TELEPHONY_LOGE("HdfSampleDriverBind, failed to write string to ipc sbuf");
HdfSbufRecycle(sbuf);
return HDF_FAILURE;
}
if (sbuf != NULL) {
HdfSbufRecycle(sbuf);
}
TELEPHONY_LOGI("sbuf IPC obtain success!");
LoadVendor();
return HDF_SUCCESS;
}
// 驱动资源释放的接口
static void RilAdapterRelease(struct HdfDeviceObject *device)
{
if (device == NULL) {
return;
}
dlclose(g_dlHandle);
}
//驱动入口注册到HDF框架,这里配置的moduleName是找到Telephony模块与RIL进行通信的一个关键配置
struct HdfDriverEntry g_rilAdapterDevEntry = {
.moduleVersion = 1,
.moduleName = "hril_hdf",
.Bind = RilAdapterBind,
.Init = RilAdapterInit,
.Release = RilAdapterRelease,
};
// 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
HDF_INIT(g_rilAdapterDevEntry);

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

 riladapter :: host {
hostName = "riladapter_host";
priority = 50;
riladapter_device :: device {
device0 :: deviceNode {
policy = 2;
priority = 100;
moduleName = "libhril_hdf.z.so";
serviceName = "cellular_radio1";
}
}
}

这里可以发现该驱动对应的服务名称为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驱动对应的服务名称 。

bool TelRilManager::ConnectRilAdapterService()
{
std::lock_guard<std::mutex> lock_l(mutex_);
rilAdapterRemoteObj_ = nullptr;
auto servMgr_ = OHOS::HDI::ServiceManager::V1_0::IServiceManager::Get();
if (servMgr_ == nullptr) {
TELEPHONY_LOGI("Get service manager error!");
return false;
}
//通过HDF框架获取RIL_ADAPTER的服务,之前定义过RIL_ADAPTER_SERVICE_NAME常量为"cellular_radio1",它就是在 vendor_hisilicon/ XXXX / hdf_config / uhdf / device_info.hcs中配置的hril_hdf驱动对应的服务名称
rilAdapterRemoteObj_ = servMgr_->GetService(RIL_ADAPTER_SERVICE_NAME.c_str());
if (rilAdapterRemoteObj_ == nullptr) {
TELEPHONY_LOGE("bind hdf error!");
return false;
}
if (death_ == nullptr) {
TELEPHONY_LOGE("create HdfDeathRecipient object failed!");
rilAdapterRemoteObj_ = nullptr;
return false;
}
if (!rilAdapterRemoteObj_->AddDeathRecipient(death_)) {
TELEPHONY_LOGE("AddDeathRecipient hdf failed!");
rilAdapterRemoteObj_ = nullptr;
return false;
}
int32_t ret = SetCellularRadioIndication();
if (ret != CORE_SERVICE_SUCCESS) {
TELEPHONY_LOGE("SetCellularRadioIndication error, ret:%{public}d", ret);
return false;
}
ret = SetCellularRadioResponse();
if (ret != CORE_SERVICE_SUCCESS) {
TELEPHONY_LOGE("SetCellularRadioResponse error, ret:%{public}d", ret);
return false;
}
return true;
}

暂时先开个篇,光一个RIL的完整代码篇幅都很大,一篇博文篇幅显然不够。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-03-24 14:40:31

开发Harmony鸿蒙

2022-03-08 15:02:00

Harmony3GPP协议操作系统

2022-07-15 13:06:46

3GPPVR移动通信

2010-11-02 14:51:11

职场

2010-05-27 15:36:01

IPv6协议

2021-04-12 08:56:00

多线程Future模式

2020-07-09 12:50:29

JVM内存管理Java

2019-10-18 17:55:03

安全运营

2019-12-24 14:04:59

PythonExcel数据处理

2020-06-15 08:19:00

ZooKeeperEureka

2019-11-14 15:38:46

AndroidRelease项目

2011-03-31 10:46:54

LinuxCLI软件

2010-05-26 15:58:52

MySQL远程连接

2010-05-10 13:01:03

OracleDBA面试

2010-05-25 09:58:43

MySQL数据库

2010-05-21 09:40:57

MySQL出错代码列表

2020-09-19 17:59:21

sorted()Python函数

2020-09-22 08:16:20

软件开发原则

2017-04-01 09:22:38

Android开发知识点

2010-05-26 15:17:06

Windows Emb
点赞
收藏

51CTO技术栈公众号