telephony-RIL适配方法介绍
RIL适配方法介绍
在OpenHarmony中,适配不同modem芯片,主要工作是适配OpenHarmony中的telephony中的RIL模块。
1. telephony架构图
在OpenHarmony中,telephony子系统架构图如下
在telephony架构中,分为应用层,框架层,系统服务层,RIL适配层。
-
应用层
上层应用接口支持,对上层应用提供功能接口
-
框架层
提供电话业务相关功能,包含通话管理,蜂窝通话,蜂窝数据,短彩信等功能。
-
系统服务层
电话服务核心层,包含SIM卡管理和搜网,以及底层接口支持。
-
RIL适配层
ril功能适配,提供电话服务和modem硬件之间的通信桥梁。实现包括芯片适配,厂商库加载,时间调度管理,业务抽象接口,接口实现等功能。

telephony框架图
在telephony框架中,分为应用层,框架层,电话服务层,RIL适配层。

各组件功能
| 部件名称 | 组件职责 |
|---|---|
| call_manager | 通话管理 主要管理CS(Circuit Switch,电路交换)、IMS(IP Multimedia Subsystem,IP多媒体子系统)和OTT(over the top,OTT解决方案)三种类型的通话,负责申请通话所需要的音视频资源,并处理多路通话时产生的各种冲突。通话管理主要分为UI交互、服务管理、系统通话管理、通话音频管理、通话视频管理和蓝牙通话管理六大模块。 |
| cellular_call | 蜂窝通话 提供基于运营商网络的基础通话实现,包含基于2G/3G的CS(Circuit Switch,电路交换)通话和基于4G/5G的IMS(IP Multimedia Subsystem,IP多媒体子系统)通话,包含VoLTE/ VoWIFI/ VoNR语音、视频、会议,支持CS和IMS通话之间的域选控制和切换,支持紧急通话。 |
| cellular_data | 蜂窝数据 提供蜂窝数据联网能力,包括蜂窝数据开启和关闭、蜂窝数据异常检测与恢复、蜂窝数据状态管理、蜂窝数据漫游管理、APN管理、网络管理交互等功能。蜂窝数据模块作为电话子系统可裁剪部件,依赖于core_service核心服务、ril_adapter。 |
| sms_mms | 短彩信 提供短信收发和彩信编解码功能,主要功能有GSM/CDMA短信收发、短信PDU(Protocol data unit,协议数据单元)编解码、Wap Push接收处理 、小区广播接收、彩信通知、 彩信编解码和SIM卡短信记录增删改查等。 |
| state_registry | 状态注册 提供各种消息事件的订阅以及取消订阅的能力。事件类型包括网络状态变化、信号强度变化、小区信息变化、蜂窝数据连接状态变化、通话状态变化等等。 |
| telephony_data | 蜂窝数据存储 提供电话服务子系统中的SIM卡、短彩信、APN、SIM卡账户等模块持久化数据存储,提供DataAbility访问接口。 |
| core_service | 蜂窝通信核心服务 提供SIM卡服务、搜网服务和RIL管理,以及获取RIL Adapter服务。 通过HDI实现与RIL Adapter进行通信;通过发布订阅,来实现与各功能模块的通信。 |
| ril_adapter | RIL适配 提供厂商库加载,业务接口实现以及事件调度管理。主要用于屏蔽不同Modem厂商硬件差异,为上层提供统一的接口,通过注册HDF服务与上层接口通讯。 |
2. RIL架构图
在RIL适配层中,RIl Adapter使用分层模块化设计,分为hril_hdf层,hril层(接口实现)和vendorlib层(RIL适配库)3个模块,其中vendorlib层(RIL适配库)文件是我们需要实现的部分。
hril_hdf将动态加载vendorlib库
hril_hdf可以通过vendorlib来控制modem
vendorlib从hril_hdf获取上层的请求和上报的函数,并在vendorlib实现这些函数。

hril层
对请求的业务进行具体业务识别,并将具体的业务分配到对应的业务模块中。
包含6个业务处理能力(network搜网,call蜂窝通话,data蜂窝数据,sms短彩信,simSIM卡信息,modem设备管理)和业务分发/整合能力(HRilManager),该模块为业务接口实现功能。上层下发请求时通过HRilManager分发到对应的业务处理能力通道里。响应时通过HRilManager整合业务处理能力通道里的数据,并将数据传给上层。
vendorlib层
对具体的业务请求进行实现,对下发到modem设备以及modem上报的事件进行处理。
包含8个业务处理能力(network搜网,call蜂窝通话,data蜂窝数据,sms短彩信,simSIM卡信息,modem设备管理),数据通信能力(support)和mdoem存活与事件监听能力(readLoop),共8个子模块组成。
RIL库加载流程
vendor库的加载流程如下
1.HDF加载ril_driver库
2.ril_driver调用hril_hdf的init()接口
3.hril_hdf调用LoadVendor(),加载RIL适配库

RIL库加载函数调用流程
1.HDF加载ril_driver。 HDF--->ril_driver
2.ril_driver被加载时,调用初始化函数DriverInit()函数。
3.在DriveInit()中,调用hril_hdf的InitRilAdapter()函数。 ril_driver--->hril_hdf
4.在InitRilAdapter()中,调用LoadVendor()函数,加载RIL适配库。 hril_hdf--->vendorlib

hril_hdf的作用
-
加载厂商库
hril的作用
-
对HDI提供接口,供上层调用
-
对vendor提供上报接口,把vendor的信息上报给HDI
vendor的作用
-
RIL适配库,连接不同modem硬件和OpenHarmony系统
-
函数接口实现,给modem下发请求,把mdoem的信息上报给系统。

3. RIL库加载
3.1 配置host节点
OpenHarmony默认没有riladapter_host节点,需要开发者把ril节点配置出来。
配置riladapter_host节点后,HDF就会加载ril_driver库,进而加载RIL适配库,整个加载过程就此完成。
下面以rk3568为例,在device_info.hcs文件中配置riladapter_host节点。用户根据实际情况,修改对应device_info.hcs文件,配置出riladapter_host节点。
rk3568的device_info.hcs路径如下
vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs
riladapter :: host {
hostName = "riladapter_host"; //进程名称
priority = 50;
riladapter_device :: device {
device0 :: deviceNode {
policy = 2;
priority = 100;
moduleName = "libril_driver.z.so";
serviceName = "ril_service";
}
}
}
配置好后,重新编译镜像,烧录镜像,HDF就会拉起riladapter_host进程,libril_driver.z.so库会被加载。
设备启动后,查看riladapter_host进程是否正确启动。
riladapter_host进程是否启动如下图

如果riladapter_host进程没有启动,需要排查原因,可能是device_info.hcs文件配置有问题,或者编译时修改没有生效等。
3.2 RIL入口注册
入口函数在libril_driver.z.so库中,如下
static struct HdfDriverEntry g_RilDriverEntry = {
.moduleVersion = 1,
.moduleName = "ril_service",
.Bind = HdfRilDriverBind,
.Init = HdfRilDriverInit, //HDF初始化RIL函数
.Release = HdfRilDriverRelease, //HDF释放RIL函数
};
libril_driver.z.so库的源码路径如下
/drivers/peripheral/ril/interfaces/hdi_service/src/ril_driver.cpp
其中Init是驱动初始化函数,Release是驱动释放函数
3.3 RIL库加载
在驱动初始化函数中,会调用HdfRilDriverInit()函数,在函数中会加载RIL适配库。
调用hril_hdf的InitRilAdapter()函数加载RIL适配库
static int32_t HdfRilDriverInit(struct HdfDeviceObject *deviceObject)
{
InitRilAdapter(); //调用hril_hdf的Init()函数
return HDF_SUCCESS;
}
4. hril_hdf
在InitRilAdapter()函数中,调用LoadVendor()来加载RIL适配库。
void InitRilAdapter(void)
{
LoadVendor();
}
在LoadVendor()函数中,先获取RIL适配库的路径,然后再加载RIL适配库。
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) { //获取RIL适配库的路径
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 || rilLibPath[0] == '\0') {
TELEPHONY_LOGE("dynamic library path is empty");
return;
}
TELEPHONY_LOGI("RilInit LoadVendor start with rilLibPath:%{public}s", rilLibPath);
g_dlHandle = dlopen(rilLibPath, RTLD_NOW); //加载RIL适配库
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;
}
HRilInit();
ops = rilInitOps(&g_reportOps);
HRilRegOps(ops);
TELEPHONY_LOGI("HRilRegOps completed");
}
在GetVendorLibPath()函数中,获取RIL适配库路径。
先读取VENDOR_LIB_PATH系统参数,来获取RIL适配库的路径。
如果失败,再读取RIL_VENDOR_LIB_PATH系统参数,来获取RIL适配库的路径。
static int32_t GetVendorLibPath(char *path)
{
int32_t code = GetParameter(VENDOR_LIB_PATH, "", path, PARAMETER_SIZE);
if (code > 0) {
return HDF_SUCCESS;
} else {
TELEPHONY_LOGE("Failed to get const vendor library path through system properties. err:%{public}d", code);
}
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;
}
5. vendor
OpenHarmony中的RIL适配需要适配的OpenHarmony的接口,大概有100多个接口函数。开发者需要在RIL库把这100多个接口实现出来。
这些接口中按照功能可以分为“下发”和“上报”2种类型。
在初始化函数RilInitOps()中,把接口函数保存下来。
const HRilOps *RilInitOps(const struct HRilReport *reportOps)
{
pthread_attr_t attr;
SetReportOps(reportOps); //设置上报函数
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int32_t ret = pthread_create(&g_eventListeners, &attr, (void *(*)(void *))EventListeners, NULL);
if (ret < 0) {
TELEPHONY_LOGE("EventListeners create failed %d \n", ret);
}
return &g_hrilOps; //返回RIL库实现的接口
}
5.1 上报函数
RIL库收到modem消息时,需要上报给上层,此时就调用上报函数,上报给上层。
static struct HRilReport g_reportOps = {
OnCallReport,
OnDataReport,
OnModemReport,
OnNetworkReport,
OnSimReport,
OnSmsReport,
OnTimerCallback
};
调用SetReportOps()函数,把上报函数传给RIL适配库。
void SetReportOps(const struct HRilReport *reportOps)
{
g_reportOps = reportOps;
}
当modem需要上报消息时,调用调用对应上报函数(以OnCallReport为例),把消息上报给上层。
以OnCallReport()为例
void OnCallReport(int32_t slotId, struct ReportInfo reportInfo, const uint8_t *response, size_t responseLen)
{
if (g_reportOps != NULL) {
g_reportOps->OnCallReport(slotId, reportInfo, response, responseLen);
} else {
TELEPHONY_LOGE("g_reportOps is NULL");
}
}
上报分别2种类型
1.执行AT命令的返回结果。---被动上报
2.信号强度,网络制式等。---主动上报
5.2 下发函数
下发函数是上层下发请求时,需要RIL适配库把请求下发到modem芯片。
下发请求接口如下
vendor_adapter.c
HRilOps g_hrilOps = {
.callOps = &g_callReqOps,
.simOps = &g_simReqOps,
.smsOps = &g_smsReqOps,
.networkOps = &g_networkReqOps,
.dataOps = &g_dataReqOps,
.modemOps = &g_modemReqOps,
};
vendor_adapter.c文件中的下发接口函数需要开发者根据不同的modem芯片,来实现具体的功能。
以CallReq为例,CallReq需要实现下面这些接口,来实现通话相关的功能。
static const HRilCallReq g_callReqOps = {
.GetCallList = ReqGetCallList,
.Dial = ReqDial,
.Hangup = ReqHangup,
.Reject = ReqReject,
.Answer = ReqAnswer,
.GetClip = ReqGetClip,
.SetClip = ReqSetClip,
.HoldCall = ReqHoldCall,
.UnHoldCall = ReqUnHoldCall,
.SwitchCall = ReqSwitchCall,
.CombineConference = ReqCombineConference,
.SeparateConference = ReqSeparateConference,
.CallSupplement = ReqCallSupplement,
.GetCallWaiting = ReqGetCallWaiting,
.SetCallWaiting = ReqSetCallWaiting,
.GetCallTransferInfo = ReqGetCallTransferInfo,
.SetCallTransferInfo = ReqSetCallTransferInfo,
.GetCallRestriction = ReqGetCallRestriction,
.SetCallRestriction = ReqSetCallRestriction,
.GetClir = ReqGetClir,
.SetClir = ReqSetClir,
.StartDtmf = ReqStartDtmf,
.SendDtmf = ReqSendDtmf,
.StopDtmf = ReqStopDtmf,
.GetCallPreferenceMode = ReqGetCallPreferenceMode,
.SetCallPreferenceMode = ReqSetCallPreferenceMode,
.SetUssd = ReqSetUssd,
.GetUssd = ReqGetUssd,
.GetMute = ReqGetMute,
.SetMute = ReqSetMute,
.GetEmergencyCallList = ReqGetEmergencyCallList,
.GetCallFailReason = ReqGetCallFailReason,
.SetEmergencyCallList = ReqSetEmergencyCallList,
.SetBarringPassword = ReqSetBarringPassword,
.CloseUnFinishedUssd = ReqCloseUnFinishedUssd,
.SetVonrSwitch = ReqSetVonrSwitch,
};
把这些函数全部适配完成后,RIL库的适配工作就完成了。
开发者可以测试telephony相关的功能,来验证适配是否正确。比如拨打电话是否成功,开启蜂窝上网是否成功等。
更多推荐

所有评论(0)