作者 軟通夏德旺 在此特別鳴謝!
市面上關(guān)于終端(手機(jī))操作系統(tǒng)在 3GPP 協(xié)議開發(fā)的內(nèi)容太少了,即使 Android 相關(guān)的資料都很少,Android 協(xié)議開發(fā)書籍我是沒有見過的。可能是市場(chǎng)需求的緣故吧,現(xiàn)在市場(chǎng)上還是前后端軟件開發(fā)從業(yè)人員最多,包括我自己。
基于我曾經(jīng)也在某手機(jī)協(xié)議開發(fā)團(tuán)隊(duì)干過一段時(shí)間,協(xié)議的 AP 側(cè)和 CP 側(cè)開發(fā)都整過,于是想嘗試下基于 OpenAtom OpenHarmony(以下簡(jiǎn)稱“OpenHarmony”)源碼寫點(diǎn)內(nèi)容,幫助大家了解下協(xié)議開發(fā)領(lǐng)域,盡可能將 3gpp 協(xié)議內(nèi)容與 OpenHarmony 電話子系統(tǒng)模塊進(jìn)行結(jié)合講解。據(jù)我所知,現(xiàn)在終端協(xié)議開發(fā)非常缺人。首先聲明我不是協(xié)議專家,我也離開該領(lǐng)域有五六年了,如有錯(cuò)誤,歡迎指正。
等我覺得自己整明白了,就會(huì)考慮出本《OpenHarmony 3GPP 協(xié)議開發(fā)深度剖析》書籍。
提到終端協(xié)議開發(fā),我首先想到的就是 RIL 了。
專有名詞
CP:Communication Processor(通信處理器),我一般就簡(jiǎn)單理解為 modem 側(cè),也可以理解為底層協(xié)議,這部分由各個(gè) modem 芯片廠商完成(比如海思、高通)。
AP:Application Processor(應(yīng)用處理器),通常就是指的手機(jī)終端,我一般就簡(jiǎn)單理解為上層協(xié)議,主要由操作系統(tǒng) Telephony 服務(wù)來(lái)進(jìn)行處理。
RIL: Radio Interface Layer(無(wú)線電接口層),我一般就簡(jiǎn)單理解為硬件抽象層,即 AP 側(cè)將通信請(qǐng)求傳給 CP 側(cè)的中間層。
AT指令: AT 指令是應(yīng)用于終端設(shè)備與 PC 應(yīng)用之間的連接與通信的指令。
設(shè)計(jì)思想
常規(guī)的 Modem 開發(fā)與調(diào)試可以使用 AT 指令來(lái)進(jìn)行操作,而各家的 Modem 芯片的 AT 指令都會(huì)有各自的差異。因此手機(jī)終端廠商為了能在各種不同型號(hào)的產(chǎn)品中集成不同 modem 芯片,需要進(jìn)行解耦設(shè)計(jì)來(lái)屏蔽各家 AT 指令的差異。于是 OpenHarmony 采用 RIL 對(duì) Modem 進(jìn)行 HAL(硬件抽象),作為系統(tǒng)與 Modem 之間的通信橋梁,為 AP 側(cè)提供控制 Modem 的接口,各 Modem 廠商則負(fù)責(zé)提供對(duì)應(yīng)于 AT 命令的 Vender RIL(這些一般為封裝好的 so 庫(kù)),從而實(shí)現(xiàn)操作系統(tǒng)與 Modem 間的解耦。
OpenHarmony RIL架構(gòu)
框架層:Telephony Service,電話子系統(tǒng)核心服務(wù)模塊,主要功能是初始化 RIL 管理、SIM 卡和搜網(wǎng)模塊。對(duì)應(yīng) OpenHarmony 的源碼倉(cāng)庫(kù) OpenHarmony / telephony_core_service。這個(gè)模塊也是非常重要的一個(gè)模塊,后期單獨(dú)再做詳細(xì)解讀。
硬件抽象層:即我們要講的 RIL,對(duì)應(yīng) OpenHarmony 的源碼倉(cāng)庫(kù) OpenHarmony / telephony_ril_adapter。RIL Adapter 模塊主要包括廠商庫(kù)加載,業(yè)務(wù)接口實(shí)現(xiàn)以及事件調(diào)度管理。主要用于屏蔽不同 modem 廠商硬件差異,為上層提供統(tǒng)一的接口,通過注冊(cè) HDF 服務(wù)與上層接口通訊。
芯片層:Modem 芯片相關(guān)代碼,即 CP 側(cè),這些代碼各個(gè) Modem 廠商是不開放的,不出現(xiàn)在 OpenHarmony 中。
硬件抽象層
硬件抽象層又被劃分為了 hril_hdf 層、hril 層和 venderlib 層。
hril_hdf層:HDF 服務(wù),基于 OpenHarmony HDF 框架,提供 hril 層與 Telephony Service 層進(jìn)行通訊。
hril 層:hril 層的各個(gè)業(yè)務(wù)模塊接口實(shí)現(xiàn),比如通話、短彩信、數(shù)據(jù)業(yè)務(wù)等。
vendorlib層:各 Modem 廠商提供的對(duì)應(yīng)于 AT 命令庫(kù),各個(gè)廠商可以出于代碼閉源政策,在這里以 so 庫(kù)形式提供。目前源碼倉(cāng)中已經(jīng)提供了一套提供代碼的 AT 命令操作,至于這個(gè)是針對(duì)哪個(gè)型號(hào) modem 芯片的,我后續(xù)了解清楚再補(bǔ)充。
下面是 ril_adapter 倉(cāng)的源碼結(jié)構(gòu):
base/telephony/ril_adapter | |
├── figures # readme資源文件 | |
├── frameworks | |
│ ├── BUILD.gn | |
│ └── src # 序列化文件 | |
├── interfaces # 對(duì)應(yīng)提供上層各業(yè)務(wù)內(nèi)部接口 | |
│ └── innerkits | |
├── services # 服務(wù) | |
│ ├── hril # hril層的各個(gè)業(yè)務(wù)模塊接口實(shí)現(xiàn) | |
│ ├── hril_hdf # HDF服務(wù) | |
│ └── vendor # 廠商庫(kù)文件 | |
└── test # 測(cè)試代碼 | |
├── BUILD.gn | |
├── mock | |
└── unittest # 單元測(cè)試代碼 |
核心業(yè)務(wù)邏輯梳理
本文解讀 RIL 層很小一部分代碼,RIL 是如何通過 HDF 與 Telephony 連接上的,以后更加完整的邏輯梳理會(huì)配上時(shí)序圖講解,會(huì)更加清晰。首先我們要對(duì) OpenHarmony 的 HDF(Hardware Driver Foundation)驅(qū)動(dòng)框架做一定了解,最好是動(dòng)手寫一個(gè) Demo 案例,具體的可以單獨(dú)去官網(wǎng)查閱 HDF 資料。
首先,找到 hril_hdf.c 文件的代碼,它承擔(dān)的是驅(qū)動(dòng)業(yè)務(wù)部分,源碼中是不帶中文注釋的,為了梳理清楚流程,我給源碼關(guān)鍵部分加上了中文注釋。
/*
* 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(“l(fā)ist 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”);
}
// 用來(lái)處理用戶態(tài)發(fā)下來(lái)的消息
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,
};
//驅(qū)動(dòng)對(duì)外提供的服務(wù)能力,將相關(guān)的服務(wù)接口綁定到HDF框架
static int32_t RilAdapterBind(struct HdfDeviceObject *device)
{
if (device == NULL) {
return HDF_ERR_INVALID_OBJECT;
}
device-》service = &g_rilAdapterService;
return HDF_SUCCESS;
}
// 驅(qū)動(dòng)自身業(yè)務(wù)初始的接口
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;
}
// 驅(qū)動(dòng)資源釋放的接口
static void RilAdapterRelease(struct HdfDeviceObject *device)
{
if (device == NULL) {
return;
}
dlclose(g_dlHandle);
}
//驅(qū)動(dòng)入口注冊(cè)到HDF框架,這里配置的moduleName是找到Telephony模塊與RIL進(jìn)行通信的一個(gè)關(guān)鍵配置
struct HdfDriverEntry g_rilAdapterDevEntry = {
.moduleVersion = 1,
.moduleName = “hril_hdf”,
.Bind = RilAdapterBind,
.Init = RilAdapterInit,
.Release = RilAdapterRelease,
};
// 調(diào)用HDF_INIT將驅(qū)動(dòng)入口注冊(cè)到HDF框架中,在加載驅(qū)動(dòng)時(shí)HDF框架會(huì)先調(diào)用Bind函數(shù),再調(diào)用Init函數(shù)加載該驅(qū)動(dòng),當(dāng)Init調(diào)用異常時(shí),HDF框架會(huì)調(diào)用Release釋放驅(qū)動(dòng)資源并退出。
HDF_INIT(g_rilAdapterDevEntry);
上述代碼中配置了對(duì)應(yīng)該驅(qū)動(dòng)的 moduleName 為"hril_hdf",因此我們需要去找到對(duì)應(yīng)驅(qū)動(dòng)的配置文件,以 Hi3516DV300 開發(fā)板為例,它的驅(qū)動(dòng)配置在 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"; | |
} | |
} | |
} |
這里可以發(fā)現(xiàn)該驅(qū)動(dòng)對(duì)應(yīng)的服務(wù)名稱為 cellular_radio1,那么 telephony_core_service 通過 HDF 與 RIL 進(jìn)行通信肯定會(huì)調(diào)用到該服務(wù)名稱,因此無(wú)查找 telephony_core_service 的相關(guān)代碼,可以很快定位到 telephony_core_service/ services / tel_ril / src / tel_ril_manager.cpp 該代碼,改代碼中有一個(gè)關(guān)鍵類 TelRilManager,它用來(lái)負(fù)責(zé)管理 tel_ril。
看 tel_ril_manager.cpp 中的一個(gè)關(guān)鍵函數(shù) ConnectRilAdapterService,它就是用來(lái)通過 HDF 框架獲取RIL_ADAPTER 的服務(wù),之前定義過 RIL_ADAPTER_SERVICE_NAME 常量為 "cellular_radio1",它就是在 vendor_hisilicon/ XXXX / hdf_config / uhdf / device_info.hcs 中配置的 hril_hdf 驅(qū)動(dòng)對(duì)應(yīng)的服務(wù)名稱。
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的服務(wù),之前定義過RIL_ADAPTER_SERVICE_NAME常量為“cellular_radio1”,它就是在 vendor_hisilicon/ XXXX / hdf_config / uhdf / device_info.hcs中配置的hril_hdf驅(qū)動(dòng)對(duì)應(yīng)的服務(wù)名稱
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;
}
-
協(xié)議
+關(guān)注
關(guān)注
2文章
612瀏覽量
39729 -
3GPP
+關(guān)注
關(guān)注
4文章
419瀏覽量
45766 -
HDF框架
+關(guān)注
關(guān)注
0文章
10瀏覽量
2870 -
OpenHarmony
+關(guān)注
關(guān)注
26文章
3806瀏覽量
17956
發(fā)布評(píng)論請(qǐng)先 登錄
評(píng)論