乐趣区

关于openharmony:OpenHarmony-3GPP协议开发深度剖析一文读懂RIL

作者:软通夏德旺

前言

市面上对于终端(手机)操作系统在 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 仓的源码构造:

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

退出移动版