0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一文读懂RIL - OpenHarmony 3GPP 协议开发解读

汽车电子技术 来源:软通夏德旺 作者:软通夏德旺 2023-01-16 16:58 次阅读

作者 软通夏德旺 在此特别鸣谢!

市面上关于终端(手机操作系统在 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架构

pYYBAGPFEeGAA4OkAADo4jb5pI8387.jpg

框架层: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

#include

#include

#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;

}

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 协议
    +关注

    关注

    2

    文章

    570

    浏览量

    38911
  • 3GPP
    +关注

    关注

    4

    文章

    412

    浏览量

    44872
  • HDF框架
    +关注

    关注

    0

    文章

    9

    浏览量

    2728
  • OpenHarmony
    +关注

    关注

    23

    文章

    3297

    浏览量

    15159
收藏 人收藏

    评论

    相关推荐

    华为公司在3GPP中的地位和作用及在3G专利方面的进展

    提交20余篇提案,其中3篇提案被接纳。2002年上半年向3GPP提交文稿30多篇。目前1人担任3GPP个Work Item的报告人。积极推动Iub接口规范化进程,与中国设备厂商
    发表于 06-13 22:44

    3GPP协议中哪些是手机的测试文档

    现在在了解些有关3GPP测试的文档,但是3GPP的内容很多,下子抓不住重点来看.我想看些有关手机测试的
    发表于 11-21 14:41

    3GPP协议中文版

    3GPP协议中文版.
    发表于 08-01 23:45

    3GPP的概念是什么?

    3GPP的目标是实现由2G网络到3G网络的平滑过渡,保证未来技术的后向兼容性,支持轻松建网及系统间的漫游和兼容性。
    发表于 09-20 09:10

    如何实现3GPP毫微微蜂窝测试?

    如何评估3GPP家庭节点B的性能?3GPP毫微微蜂窝技术是什么?毫微微蜂窝基站部署的重要组件有哪些?如何实现3GPP毫微微蜂窝测试?
    发表于 04-15 06:30

    3GPP R5中IMS的基本结构是怎样构成的?

    SIP协议是什么?3GPP R5中IMS的基本结构是怎样构成的?漫游用户的会话过程是如何建立起来的?
    发表于 05-27 06:02

    读懂什么是NEC协议

    读懂什么是NEC协议
    发表于 10-15 09:22

    OpenHarmony 3GPP协议开发资料分享

    1、OpenHarmony 3GPP协议开发RIL详解专有名词CP: Communication Processor(通信处理器),我
    发表于 03-18 10:37

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

    考虑出本《OpenHarmony 3GPP 协议开发深度剖析》书籍。提到终端协议开发,我首先想到
    发表于 05-06 11:06

    OpenHarmony 3GPP协议开发深度剖析》之--搜网流程之PLMN选择

    协议开发最大的法宝就是反复阅读3GPP协议标准,然后结合标准梳理信令流程,同时比对modem日志或者ap侧日志,阅读modem侧源码或者ap侧源码。本系列主题在操作系统
    发表于 09-14 10:31

    3GPP协议版本的发展路线

    3GPP协议版本的发展路线3GPP协议版本分为R99/R4/R5/R6等多个阶段,其中R99协议于2000年3月冻结功能,R4
    发表于 06-13 22:23 2538次阅读

    3GPP最基础的协议_英文版

    3GPP最基础的协议,通读后,即可对移动通信有大致的了解。
    发表于 03-01 09:39 0次下载

    3GPP协议中文版

    3GPP协议中文版,入网测试专用,各种测试指标
    发表于 05-09 09:41 0次下载

    一文读懂3GPP到底是什么

    业界了解3GPP如何开发4G和5G的标准越来越重要,因为移动生态系统的扩展远远超过了智能手机的范畴,从汽车到公共安全,再到物联网的兴起,越来越多的行业逐渐与3GPP生态系统发生关系。随着5G的到来,将生态系统推向新的发展务必重要
    发表于 09-29 08:59 1.9w次阅读

    3GPP协议 36.508版本pdf下载

    3GPP协议 36.508
    发表于 03-07 14:43 0次下载