Author: iSoftStone Xia Dewang

foreword

There is too little content on the development of terminal (mobile phone) operating systems in the 3GPP protocol. Even if there is very little information about Android, I have never seen any books on Android protocol development. Maybe because of market demand, there are still the most front-end and back-end software development practitioners in the market, including myself. Based on the fact that I have worked in a mobile phone protocol development team for a period of time, the AP side and CP side of the protocol have been developed, so I want to try to write something based on the source code of OpenAtom OpenHarmony (hereinafter referred to as "OpenHarmony") to help you understand In the field of protocol development, the content of the 3gpp protocol is combined with the OpenHarmony telephony subsystem module as much as possible. As far as I know, terminal protocol development is very short of people right now. First of all, I'm not a protocol expert, and I've been out of the field for five or six years. If I'm wrong, please correct me.
When I think I understand it, I will consider the book "In-depth Analysis of OpenHarmony 3GPP Protocol Development".
When it comes to terminal protocol development, the first thing that comes to my mind is RIL.

proper noun

CP: Communication Processor (communication processor), I generally understand it simply as the modem side, and it can also be understood as the underlying protocol. This part is completed by various modem chip manufacturers (such as HiSilicon, Qualcomm).

AP: Application Processor (application processor), usually refers to the mobile phone terminal, I generally simply understand it as the upper-layer protocol, which is mainly processed by the operating system Telephony service.

RIL: Radio Interface Layer (Radio Interface Layer), I generally simply understand it as the hardware abstraction layer, that is, the middle layer where the AP side transmits the communication request to the CP side.

AT commands: AT commands are commands applied to the connection and communication between terminal equipment and PC applications.

design thinking

Conventional Modem development and debugging can use AT commands to operate, and the AT commands of each modem chip will have their own differences. Therefore, in order to integrate different modem chips into various types of products, mobile phone terminal manufacturers need to carry out decoupling design to shield the differences in AT commands. Therefore, OpenHarmony uses RIL to perform HAL (hardware abstraction) on Modem, as a communication bridge between the system and Modem, providing an interface for controlling Modem on the AP side, and each Modem manufacturer is responsible for providing Vender RIL corresponding to AT commands (these are generally encapsulated Good so library), so as to realize the decoupling between the operating system and Modem.

OpenHarmony RIL Architecture

Framework layer: Telephony Service, core service module of telephone subsystem, the main function is to initialize RIL management, SIM card and search network module. Corresponds to OpenHarmony's source code repository OpenHarmony / telephony_core_service. This module is also a very important module, which will be explained in detail later.

Hardware abstraction layer: the RIL we want to talk about, corresponding to OpenHarmony's source code repository OpenHarmony / telephony_ril_adapter. The RIL Adapter module mainly includes vendor library loading, business interface implementation and event scheduling management. It is mainly used to shield the hardware differences of different modem manufacturers, provide a unified interface for the upper layer, and communicate with the upper layer interface by registering the HDF service.

Chip layer: Modem chip related codes, namely the CP side, these codes are not open to each Modem manufacturer and do not appear in OpenHarmony.

hardware abstraction layer

The hardware abstraction layer is divided into hril_hdf layer, hril layer and venderlib layer.

hril_hdf layer: HDF service, based on the OpenHarmony HDF framework, provides the hril layer to communicate with the Telephony Service layer.

hril layer: The interface implementation of each service module of the hril layer, such as calls, SMS and MMS, and data services.

vendorlib layer: Corresponding to the AT command library provided by each Modem manufacturer, each manufacturer can provide it in the form of so library here due to the code closed source policy. At present, the source code warehouse has provided a set of AT command operations that provide codes. As for which model of modem chip this is for, I will add it after I understand it clearly.

The following is the source code structure of the ril_adapter warehouse:

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

Core business logic sorting

This article interprets a small part of the code of the RIL layer and how RIL is connected to Telephony through HDF. In the future, a more complete logical sorting will be accompanied by a sequence diagram explanation, which will be more clear. First of all, we need to have a certain understanding of OpenHarmony's HDF (Hardware Driver Foundation) driver framework. It is best to write a demo case. For details, you can go to the official website to check the HDF information.

First, find the code of the hril_hdf.c file. It is responsible for driving the business part. There are no Chinese comments in the source code. In order to sort out the process, I added Chinese comments to the key parts of the source code.

 /*
 * 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);

The moduleName corresponding to the driver is configured as "hril_hdf" in the above code, so we need to find the configuration file of the corresponding driver. Taking the Hi3516DV300 development board as an example, its driver configuration is in vendor_hisilicon/ Hi3516DV300 / hdf_config / uhdf / device_info.hcs code can be found in:

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

Here it can be found that the service name corresponding to the driver is cellular_radio1, then telephony_core_service will definitely call the service name when communicating with RIL through HDF, so there is no relevant code to find telephony_core_service, you can quickly locate telephony_core_service/ services / tel_ril / src / tel_ril_manager .cpp In this code, there is a key class TelRilManager in the code, which is used to manage tel_ril.

Look at a key function ConnectRilAdapterService in tel_ril_manager.cpp, which is used to obtain the service of RIL_ADAPTER through the HDF framework. The RIL_ADAPTER_SERVICE_NAME constant is defined as "cellular_radio1" before, which is configured in vendor_hisilicon/ XXXX / hdf_config / uhdf / device_info.hcs hril_hdf Driver corresponding service name.

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


OpenHarmony开发者
160 声望1.1k 粉丝

OpenHarmony是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目,


引用和评论

0 条评论