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