一、前言
我的作業是關于汽車車機BSP部分,
汽車車機,其實基本和人們日常所用的手機一樣,也是安卓平臺的,所謂安卓,就是一層安卓服務包裹著Linux內核所形成的作業系統,
BSP組,主要作業內容就是負責soc的Linux系統部分的驅動移植、除錯,及BUG解決,
從畢業到現在,作業也有大半年了,跟著前輩學習GPS模塊的移植、除錯,和BUG解決也有差不多兩個月了,心里想著,是時候寫一篇關于GPS驅動移植學習的總結和筆記了,
于是今天,我嘗試著動手開始梳理這兩個月來的所學所知,
二、U-blox m8l導航模塊
目前汽車車機,所用的GPS都是屬于高度集成的模塊,所有的功能都在模塊內部實作,用戶只需要通過串口對資料進行讀取與決議,就可以獲取GPS資訊,
我們公司目前所用的GPS導航模塊有三種,這篇文章所要介紹,則是瑞士優北羅股份有限公司所研發制作的一款整合了運動、方向和高度傳感器的NEO-M8L汽車慣性導航(ADR, Automotive Dead Reckoning)模塊,該模塊將陀螺儀和加速度傳感器與u-blox領先的GNSS平臺 - u-blox M8集成在一起,使其成為市場上性能最佳的室內/室外定位解決方案,是所有道路車輛和高精度導航應用的理想選擇,
NEO-M8L模塊中內置了u-blox突破性的“3D汽車慣性導航”(3D ADR)芯片技術,它利用車輛的速度資訊以及模塊的內置傳感器,即使當衛星信號完全被遮蔽或終端設備沒有安裝于水平位置時,仍能提供準確的三維定位資訊,此外,基于ADR技術的里程表功能還能提供正確和連續的行駛距離,
該模塊能追蹤所有可視的GNSS衛星,包括GPS、GLONASS、北斗和所有的SBAS系統 (歐洲的伽利略系統將在未來的韌體版本中支持),該模塊目前支持兩種GNSS系統的并行接收,并且能以高達每秒20次的速度輸出定位資訊,
三、安卓平臺下的GPS架構
安卓系統,實作了一系列的架構和分層,我們BSP移植驅動,主要完成兩部分作業,
- 實作Linux驅動模塊,保證Linux驅動對硬體的驅動能力;
- 對接安卓hal層架構,實作hal層介面,
因為GPS屬于高度集成的一體化模塊,所有的功能都在模塊內部實作,所以,對GPS的驅動移植,主要是為了匹配和實作安卓系統提供的HAL層介面,然后呼叫串口驅動的介面去讀取對應串口地址的資料即可,
我們先來看一下安卓的GPS架構圖,

由圖可以知道,安卓的GPS架構由上到下是:app->framework->jni->hidl->hal,我們這篇文章,主要介紹從jni->hidl->hal層,
3.1、HAL層標準介面
HAL 可定義一個標準介面以供硬體供應商實作,這可讓 Android 忽略較低級別的驅動程式實作,借助 HAL,您可以順利實作相關功能,而不會影響或更改更高級別的系統,
為了保證 HAL 具有可預測的結構,每個硬體專用 HAL 介面都要具有在 hardware/libhardware/include/hardware/hardware.h 中定義的屬性,這類介面可讓 Android 系統以一致的方式加載 HAL 模塊的正確版本,HAL 介面包含兩個組件:模塊和設備,
3.1.1、模塊
模塊代表打包的 HAL 實作,這種實作存盤為共享庫 (.so file),hardware/libhardware/include/hardware/hardware.h 頭檔案可定義一個代表模塊的結構體 (hw_module_t),其中包含模塊的版本、名稱和作者等元資料,Android 會根據這些元資料來找到并正確加載 HAL 模塊,
另外,hw_module_t 結構體還包含指向另一個結構體 hw_module_methods_t 的指標,后面這個結構體包含指向相應模塊的 open 函式的指標,此 open 函式用于與相關硬體(此 HAL 是其抽象形式)建立通信,
typedef struct hw_module_t {
/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t tag;
uint16_t module_api_version;
#define version_major module_api_version
uint16_t hal_api_version;
#define version_minor hal_api_version
/** Identifier of module */
const char *id;
/** Name of this module */
const char *name;
/** Author/owner/implementor of the module */
const char *author;
/** Modules methods */
struct hw_module_methods_t* methods;
/** module's dso */
void* dso;
} hw_module_t;
typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
} hw_module_methods_t;
3.1.2、設備
設備是產品硬體的抽象表示,
設備由 hw_device_t 結構體表示,與模塊類似,每類設備都定義了一個通用 hw_device_t 的詳細版本,其中包含指向特定硬體功能的函式指標,
struct gps_device_t {
struct hw_device_t common;
const GpsInterface* (*get_gps_interface)(struct gps_device_t* dev);
};
typedef struct hw_device_t {
/** tag must be initialized to HARDWARE_DEVICE_TAG */
uint32_t tag;
uint32_t version;
/** reference to the module this device belongs to */
struct hw_module_t* module;
/** padding reserved for future use */
#ifdef __LP64__
uint64_t reserved[12];
#else
uint32_t reserved[12];
#endif
/** Close this device */
int (*close)(struct hw_device_t* device);
} hw_device_t;
3.2、HIDL — HIDL HAL
hidl的官方標準定義為:HIDL是HAL介面定義語言(簡稱 HIDL,發音為“hide-l”),是用于指定 HAL 和其用戶之間的介面的一種介面描述語言 (IDL),
安卓官方設計 HIDL 這個機制的目的,主要是想把框架(framework)與 HAL 進行隔離,使得框架部分可以直接被覆寫、更新,而不需要重新對 HAL 進行編譯,
3.2.1、客戶端和服務器實作
HIDL 介面具有客戶端和服務器實作:
- HIDL 介面的客戶端實作是指通過在該介面上呼叫方法來使用該介面的代碼,
- 服務器實作是指 HIDL 介面的實作,它可接收來自客戶端的呼叫并回傳結果(如有必要),
在從libhardware HAL 轉換為 HIDL HAL 的程序中,HAL 實作成為服務器,而呼叫 HAL 的行程則成為客戶端,默認實作可提供直通和 Binder 化 HAL,

上圖為HAL的幾個發展歷程,方式2為目前我的開發環境所使用的直通方式,框架和HAL之間通過HIDL介面實作通信,硬體廠商負責服務器的實作,
3.2.2、系統服務啟動gps.ublox.so
下面從系統服務方面介紹一下系統啟動,獲取gps.ublox.so(gps hal驅動編譯生成的檔案)流程,
①、首先介紹GNSS的HIDL介面,編譯生成"android.hardware.gnss@1.0"介面共享庫,客戶端和服務器之間就是通過這些介面來作業的,服務器端需要做的就是實作該介面,
介面編譯腳本如下:
hidl_interface {
name: "android.hardware.gnss@1.0",
root: "android.hardware",
vndk: {
enabled: true,
},
srcs: [
"types.hal",
"IAGnss.hal",
"IAGnssCallback.hal",
"IAGnssRil.hal",
"IAGnssRilCallback.hal",
"IGnss.hal",
"IGnssBatching.hal",
"IGnssBatchingCallback.hal",
"IGnssCallback.hal",
"IGnssConfiguration.hal",
"IGnssDebug.hal",
"IGnssGeofenceCallback.hal",
"IGnssGeofencing.hal",
"IGnssMeasurement.hal",
"IGnssMeasurementCallback.hal",
"IGnssNavigationMessage.hal",
"IGnssNavigationMessageCallback.hal",
"IGnssNi.hal",
"IGnssNiCallback.hal",
"IGnssXtra.hal",
"IGnssXtraCallback.hal",
],
interfaces: [
"android.hidl.base@1.0",
],
types: [
"GnssConstellationType",
"GnssLocation",
"GnssLocationFlags",
"GnssMax",
],
gen_java: true,
gen_java_constants: true,
}
②、rc檔案啟動服務android.hardware.gnss@1.0-service
android.hardware.gnss@1.0-service.rc啟動服務android.hardware.gnss@1.0-service
android.hardware.gnss@1.0-service.rc檔案內容如下:
service vendor.gnss_service /vendor/bin/hw/android.hardware.gnss@1.0-service
class hal
user gps
group system gps radio
android.hardware.gnss@1.0-service服務由Android.bp編譯得到,其次還包含了HIDL介面"android.hardware.gnss@1.0",Android.bp檔案部分內容如下:
cc_binary {
relative_install_path: "hw",
vendor: true,
name: "android.hardware.gnss@1.0-service",
defaults: ["hidl_defaults"],
init_rc: ["android.hardware.gnss@1.0-service.rc"],
srcs: ["service.cpp"],
shared_libs: [
"liblog",
"libcutils",
"libdl",
"libbase",
"libutils",
"libhardware",
"libbinder",
"libhidlbase",
"libhidltransport",
"android.hardware.gnss@1.0",
],
}
我們看到service.cpp,它將對提供的-impl 庫執行dlopen() 操作,并將其作為 Binder 化服務提供,service.cpp代碼如下:
#define LOG_TAG "android.hardware.gnss@1.0-service"
#include <android/hardware/gnss/1.0/IGnss.h>
#include <hidl/LegacySupport.h>
#include <binder/ProcessState.h>
using android::hardware::gnss::V1_0::IGnss;
using android::hardware::defaultPassthroughServiceImplementation;
int main()
{
// The GNSS HAL may communicate to other vendor components via
// /dev/vndbinder
android::ProcessState::initWithDriver("/dev/vndbinder");
return defaultPassthroughServiceImplementation<IGnss>();
}
③、"android.hardware.gnss@1.0-impl"是介面的具體實作,
-impl 庫也由Android.bp編譯而成,其部分內容如下:
cc_library_shared {
name: "android.hardware.gnss@1.0-impl",
defaults: ["hidl_defaults"],
vendor: true,
relative_install_path: "hw",
srcs: [
"ThreadCreationWrapper.cpp",
"AGnss.cpp",
"AGnssRil.cpp",
"Gnss.cpp",
"GnssBatching.cpp",
"GnssDebug.cpp",
"GnssGeofencing.cpp",
"GnssMeasurement.cpp",
"GnssNavigationMessage.cpp",
"GnssNi.cpp",
"GnssXtra.cpp",
"GnssConfiguration.cpp",
"GnssUtils.cpp",
],
shared_libs: [
"liblog",
"libhidlbase",
"libhidltransport",
"libutils",
"android.hardware.gnss@1.0",
"libhardware",
],
}
④、HIDL_FETCH_IModuleName 函式
為了讓 HAL 在直通模式下運行,Gnss.cpp 必須具有 HIDL_FETCH_IModuleName 函式,函式內容如下:
IGnss* HIDL_FETCH_IGnss(const char* /* hal */) {
hw_module_t* module;
IGnss* iface = nullptr;
int err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
hw_device_t* device;
err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
if (err == 0) {
iface = new Gnss(reinterpret_cast<gps_device_t*>(device));
} else {
ALOGE("gnssDevice open %s failed: %d", GPS_HARDWARE_MODULE_ID, err);
}
} else {
ALOGE("gnss hw_get_module %s failed: %d", GPS_HARDWARE_MODULE_ID, err);
}
return iface;
}
這個函式會加載gps.ublox.so庫,
3.3、JNI
jni是framework與hidl hal之間的一層,為java語言實作的framework呼叫c++語言實作的hidl hal代碼提供介面,
四、原始碼分析
4.1、獲取GPS驅動介面
4.1.1、hidl層獲取hal層介面
hidl檔案位置android/hardware/interface/gnss/1.0/default/Gnss.cpp,
hal層檔案位置android/hardware/u-blox/gps/,
由3.2內容,可以知道,系統服務起來之后,hidl層最開始運作起來的地方就是HIDL_FETCH_IModuleName函式了,通過名字,我們可以知道,這個函式的意思是獲取模塊名,換句話來說,也就是獲取3.1提到過的“hw_module_t”和“hw_device_t”這兩個結構體,
我們先看到這個HIDL_FETCH_IModuleName函式第5行,這里呼叫了hw_get_module函式,這個函式會根據GPS_HARDWARE_MODULE_ID去找到對應的“hw_module_t”,hal驅動層“hw_module_t”如下:
hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 2,
.version_minor = 0,
.id = GPS_HARDWARE_MODULE_ID,
.name = "u-blox GPS/GNSS library",
.author = "u-blox AG - Switzerland",
.methods = &CGpsIf::s_hwModuleMethods,
.dso = NULL,
.reserved = {0}
};
可以看到.id和hw_get_module第一個引數相同,也是GPS_HARDWARE_MODULE_ID這個宏,
繼續回到HIDL_FETCH_IModuleName函式,看到第8行,這里呼叫了module->methods->open函式,在函式內部,找到對應device之后,它會對第三個引數進行了填充,從而讓HIDL_FETCH_IModuleName函式獲取到對應的“hw_device_t”,open函式代碼如下:
struct hw_module_methods_t CGpsIf::s_hwModuleMethods = {
.open = CGpsIf::hwModuleOpen // open a specific device
};
int CGpsIf::hwModuleOpen(const struct hw_module_t *module, char const *name, struct hw_device_t **device)
{
((void)(name));
struct gps_device_t *dev = new (std::nothrow) gps_device_t{};
if (!dev)
return 1;
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = const_cast<struct hw_module_t *>(module);
dev->common.close = CGpsIf::hwModuleClose;
dev->get_gps_interface = CGpsIf::getIf;
*device = (struct hw_device_t *)(void *)dev;
return 0;
}
我們再看到HIDL_FETCH_IModuleName函式第10行,在這里,new了一個名叫Gnss的類物件出來,Gnss類物件就是hidl hal層的具體類,我們知道,一個類物件被new出來之后,它的建構式就會被呼叫,建構式內容如下:
Gnss::Gnss(gps_device_t* gnssDevice) : mDeathRecipient(new GnssHidlDeathRecipient(this)) {
/* Error out if an instance of the interface already exists. */
LOG_ALWAYS_FATAL_IF(sInterfaceExists);
sInterfaceExists = true;
if (gnssDevice == nullptr) {
ALOGE("%s: Invalid device_t handle", __func__);
return;
}
mGnssIface = gnssDevice->get_gps_interface(gnssDevice);
}
在Gnss的建構式第11行,它呼叫了gnssDevice->get_gps_interface函式,在上面hwModuleOpen函式第17行,可以看到有對gnssDevice->get_gps_interface賦值,那我們先來看看這個函式代碼內容:
const GpsInterface CGpsIf::s_interface = {
IF_ANDROID23(.size = sizeof(GpsInterface), ).init = CGpsIf::init,
.start = CGpsIf::start,
.stop = CGpsIf::stop,
#if (PLATFORM_SDK_VERSION <= 8 /* <=2.2 */)
.set_fix_frequency = CGpsIf::setFixFrequency,
#endif
.cleanup = CGpsIf::cleanup,
.inject_time = CGpsIf::injectTime,
IF_ANDROID23(.inject_location = CGpsIf::injectLocation, ).delete_aiding_data =
CGpsIf::deleteAidingData,
.set_position_mode = CGpsIf::setPositionMode,
.get_extension = CGpsIf::getExtension,
};
const GpsInterface *CGpsIf::getIf(struct gps_device_t * /*dev*/)
{
return &s_interface;
}
gnssDevice->get_gps_interface實際上指向的,就是上面的getIf,而在getIf中,直接回傳了一個名為s_interface的結構體,其實,這個就是gps hal層的介面,接下來hidl的所有操作都是通過呼叫這個介面的函式來進行實作,
4.1.2、jni層獲取hidl介面
jni層檔案位置:android/frameworks/base/services/core/jni/com_android_server_location_GnssLocationProvider.cpp,
jni若想使用hidl的各種函式,也需要獲取到hidl提供出來的介面,jni獲取hidl介面函式如下:
static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
gnssHal_V1_1 = IGnss_V1_1::getService();
if (gnssHal_V1_1 == nullptr) {
ALOGD("gnssHal 1.1 was null, trying 1.0");
gnssHal = IGnss_V1_0::getService();
} else {
gnssHal = gnssHal_V1_1;
ALOGD("gnssHal 1.1 is Ok!");
}
}
4.2、設定回呼函式
在安卓GPS架構中,非常重要的,無疑不是回呼操作了,所有GPS的上報,都是通過回呼函式完成的,
它的gps資料回呼流程是:hal->hidl->jni,可以看到,所謂回呼,其實就是由下而上,
不過,設定回呼結構體卻是由上而下:jni->hidl->hal,
4.2.1、安卓gps標準回呼結構體
gps回呼結構體在android/hardware/libhardware/inlcude/gps.h中定義,以下代碼為非標準結構體:
typedef struct {
/** set to sizeof(GpsCallbacks) */
size_t size;
gps_location_callback location_cb;
gps_status_callback status_cb;
gps_sv_status_callback sv_status_cb;
gps_nmea_callback nmea_cb;
gps_set_capabilities set_capabilities_cb;
gps_acquire_wakelock acquire_wakelock_cb;
gps_release_wakelock release_wakelock_cb;
gps_create_thread create_thread_cb;
gps_request_utc_time request_utc_time_cb;
gnss_gyr_callback gyr_cb;
gnss_acc_callback acc_cb;
gnss_set_system_info set_system_info_cb;
gnss_sv_status_callback gnss_sv_status_cb;
} GpsCallbacks;
可以看到,代碼第13、14行,這是我們自己添加的回呼,用于回呼加速度、陀螺儀的資訊,在標準結構體中沒有這兩行,
4.2.2、填充回呼函式指標
因為gps hal層驅動上報資訊需要用到回呼結構體,而這個結構體則需要hidl層定義實作這個結構體,然后再傳遞給hal層,讓hal層得到這個結構體指標,
而hidl回呼到jni層,也需要jni定義并傳遞一個類似的回呼結構體,
所以,接下來,我們就來看一下,代碼中是如何填充回呼函式指標的,
①、hidl hal
static GpsCallbacks sGnssCb;
GpsCallbacks Gnss::sGnssCb = {
.size = sizeof(GpsCallbacks),
.location_cb = locationCb,
.status_cb = statusCb,
.sv_status_cb = gpsSvStatusCb,
.nmea_cb = nmeaCb,
.set_capabilities_cb = setCapabilitiesCb,
.acquire_wakelock_cb = acquireWakelockCb,
.release_wakelock_cb = releaseWakelockCb,
.create_thread_cb = createThreadCb,
.request_utc_time_cb = requestUtcTimeCb,
.set_system_info_cb = setSystemInfoCb,
.gnss_sv_status_cb = gnssSvStatusCb,
.gyr_cb = gnssGyrCb,
.acc_cb = gnssAccCb,
};
Return<bool> Gnss::setCallback(const sp<IGnssCallback>& callback)
{
……
sGnssCbIface = callback;
……
return (mGnssIface->init(&sGnssCb) == 0);
}
可以看到第25行呼叫了mGnssIface->init函式,mGnssIface就是gps hal層的介面,忘記了的可以回到4.1看一下,
我們繼續看mGnssIface->init函式是如何實作的,代碼內容如下:
static CGpsIf s_myIf;
……
int CGpsIf::init(GpsCallbacks *callbacks)
{
……
initializeGpsCallbacks(*callbacks);
……
}
void CGpsIf::initializeGpsCallbacks(GpsCallbacks &callbacks)
{
int res = 0;
char buf[92];
if (callbacks.size == sizeof(GpsCallbacks))
{
s_myIf.m_callbacks = callbacks;
res = property_get("persist.vendor.gps.debug", buf, 0);
if (res && atoi(buf)){
s_myIf.UBLOX_UNNECESSARY_LOG_FLAG = true;
UBX_LOG(LCAT_WARNING, "UBLOX_UNNECESSARY_LOG_FLAG is ture");
} else {
s_myIf.UBLOX_UNNECESSARY_LOG_FLAG = false;
UBX_LOG(LCAT_WARNING, "UBLOX_UNNECESSARY_LOG_FLAG is false");
}
} else {
UBX_LOG(LCAT_WARNING, "callback size %zd != %zd", callbacks.size, sizeof(GpsCallbacks));
}
}
第4行,init函式呼叫initializeGpsCallbacks函式,而在第14行,initializeGpsCallbacks函式則對s_myIf.m_callbacks(s_myIf就是gps module的類物件)進行了賦值,也就是填充回呼函式結構體指標,
到這里,hal到hidl層的回呼指標就填充完畢了,接下來看看hidl層到jni層的回呼指標,
②、jni
struct GnssCallback : public IGnssCallback {
Return<void> gnssLocationCb(const GnssLocation& location) override;
Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue status) override;
Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override;
Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override;
Return<void> gnssAcquireWakelockCb() override;
Return<void> gnssReleaseWakelockCb() override;
Return<void> gnssRequestTimeCb() override;
Return<void> gnssRequestLocationCb(const bool independentFromGnss) override;
Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override;
//fce added for acc/gry
Return<void> gnss_gyr_callback(const IGnssCallback::AutoNaviGyr& gry) override;
Return<void> gnss_acc_callback(const IGnssCallback::AutoNaviAcc& acc) override;
// New in 1.1
Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
// TODO(b/73306084): Reconsider allocation cost vs threadsafety on these statics
static const char* sNmeaString;
static size_t sNmeaStringLength;
};
static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject obj)
{
……
sp<IGnssCallback> gnssCbIface = new GnssCallback();
Return<bool> result = false;
if (gnssHal_V1_1 != nullptr) {
result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
} else {
result = gnssHal->setCallback(gnssCbIface);
}
……
}
這是jni層中的代碼,可以看到上面代碼第32行呼叫了hidl介面的setCallback函式,其中傳遞的引數就是jni層所定義的回呼結構體,我們回到hidl層中的setCallback函式:
sp<IGnssCallback> Gnss::sGnssCbIface = nullptr;
……
Return<bool> Gnss::setCallback(const sp<IGnssCallback>& callback)
{
……
sGnssCbIface = callback;
……
return (mGnssIface->init(&sGnssCb) == 0);
}
可以看到hidl介面的setCallback函式其實就是hidl層為gps hal層填充回呼結構體指標的函式,
在這個函式中,有這么一段代碼——“sGnssCbIface = callback;”,顯然,這就是在設定hidl回呼到jni層的回呼結構體了,
4.3、ACC/GYR回呼流程范例分析
所謂ACC/GYR就是加速度/陀螺儀,
因為這個專案所帶的慣導功能需要將ACC/GYR資訊回呼給app(地圖軟體),所以,需要從底層回呼這些資訊,
這里,我們需要一段詳細的流程分析,從開始獲取資料到上報資料結束,
4.3.1、注冊gps hal層主執行緒
gps hal層主執行緒,在init函式中創建,主要功能就是監測串口是否有資料產生,當有資料到來時,就呼叫決議函式決議資料,然后通過回呼結構體中的各個回呼函式,將他們回呼到hidl層,然后再在hidl層中的函式中呼叫jni層的回呼函式,從而將資料傳遞給jni層,
int CGpsIf::init(GpsCallbacks *callbacks)
{
……
createGpsThread();
……
}
void CGpsIf::createGpsThread()
{
……
s_mainControlThread = s_myIf.m_callbacks.create_thread_cb("gps thread", ubx_thread, &s_controlThreadInfo);
……
}
看到以上代碼,首先這個init函式就是之前setCallback所呼叫的init函式,然后,再看到在這個init函式中,它呼叫了一個名為createGpsThread的函式,在這個函式中就創建了gps的thread,并將ubx_thread這個函式注冊為了gps的thread函式,
4.3.2、注冊串口監控 — 多路復用之select
void ubx_thread(void *pThreadData)
{
ControlThreadInfo *pState = (ControlThreadInfo *)pThreadData;
……
for(;;)
{
fd_set rfds;
int maxFd = 0;
FD_ZERO(&rfds);
FD_SET(pState->cmdPipes[0], &rfds); // Add cmd queue pipe
if (pState->cmdPipes[0] + 1 > maxFd)
maxFd = pState->cmdPipes[0] + 1;
int res = s_ser.rselect(maxFd, &rfds, &tv);
……
}
……
}
可以看到,在ubx_thread函式的死回圈中,注冊了一個select多路復用(不熟悉多路復用的,可以看我另外一篇文章),
而在上面代碼的16行,則是呼叫了我們自己封裝的一個select監測函式,對可讀集合進行監控,
4.3.3、讀取串口資料
void ubx_thread(void *pThreadData)
{
ControlThreadInfo *pState = (ControlThreadInfo *)pThreadData;
checkRecvInitReq(pState, rfds, maxFd, sinceLastNmeaMs > sinceLastUbxMs ? sinceLastNmeaMs : sinceLastUbxMs);
……
for(;;)
{
int res = s_ser.rselect(maxFd, &rfds, &tv);
if (res > 0)
{
if (s_ser.fdIsSet(rfds))
{
unsigned char *ptr = parser.GetPointer();
unsigned int space = (unsigned int)parser.GetSpace();
int iSize = s_ser.readSerial(ptr, space);
}
}
}
……
}
依舊是在ubx_thread函式中,首先是第5行,這里呼叫了一個叫checkRecvInitReq的函式,在這個函式里,呼叫了openSerial,打開gps和soc傳輸資料的串口,然后我們看到第18行,當監測到集合內可讀事件的時候,就會呼叫readSerial去讀取對應的串口,
這里的fdIsSet由我們呼叫標準的FD_ISSET函式封裝而成,readSerial也是由我們呼叫read函式封裝而成,
4.3.4、決議資料并發出回呼
void ubx_thread(void *pThreadData)
{
ControlThreadInfo *pState = (ControlThreadInfo *)pThreadData;
……
for(;;)
{
int res = s_ser.rselect(maxFd, &rfds, &tv);
if (res > 0)
{
if (s_ser.fdIsSet(rfds))
{
……
while (parser.Parse(pProtocol, pMsg, iMsg)) {
……
pUbxGps->onNewMsg(pMsg, (unsigned int)iMsg);
pProtocol->Process(pMsg, iMsg, pDatabase);
……
}
}
}
}
……
}
上述代碼第13行,決議資料,這個決議演算法由供應商提供,不需要我們關心,不過,我們需要關心一下第15行,這里呼叫了一個叫做onNewMsg的函式,函式內容很多,這里就不一一展示給大家看了,在我關注的重點里,這個函式主要是配置了gps模塊輸出何種資料,沒錯,gps要輸出哪些資料是需要配置的,不配置的話就沒有資料輸出,比如acc/gry資料是屬于UBX-ESF-MEAS資料,所以必須使能它才行,
再看到第16行,這里呼叫了一個Process函式,在這里,我們進行了gps資訊的回呼,
void CProtocolUBX::Process(const unsigned char *pBuffer, int iSize, CDatabase *pDatabase)
{
switch (getMessageId(pBuffer)){
……
case UBXID_ESF_MEAS:
ProcessEsfMeas(pBuffer);
processMessage<UBX_ESF_MEAS>(pBuffer, iSize, UBXID_ESF_MEAS);
break;
}
……
}
void CProtocolUBX::ProcessEsfMeas(const unsigned char* pBuffer)
{
……
if(CGpsIf::getInstance()->m_callbacks.acc_cb) {
//UBX_LOG(LCAT_VERBOSE, "update_gps_acc");
CGpsIf::getInstance()->m_callbacks.acc_cb(ACC);
}
……
if(CGpsIf::getInstance()->m_callbacks.gyr_cb) {
//UBX_LOG(LCAT_VERBOSE, "update_gps_gyr");
CGpsIf::getInstance()->m_callbacks.gyr_cb(GYR);
}
}
可以看到,上述代碼第18、23行呼叫了acc/gyr的回呼函式,這里的回呼函式會將資料回呼給hidl層,
4.3.5、hidl hal回呼
hal層發出回呼之后,會來到hidl層,hidl層代碼如下:
void Gnss::gnssGyrCb(AutoNavi_Gyr* gyr){
android::hardware::gnss::V1_0::IGnssCallback::AutoNaviGyr gyr_temp;
if (sGnssCbIface == nullptr || gyr == nullptr) {
ALOGE("%s: GNSS Callback Interface configured incorrectly", __func__);
return;
}
//ALOGE("%s x=%f, y=%f, z=%f", __func__, gyr->x, gyr->y, gyr->z);
gyr_temp.x = gyr->x;
gyr_temp.y = gyr->y;
gyr_temp.z = gyr->z;
gyr_temp.temp = gyr->temp;
gyr_temp.ticktime = gyr->ticktime;
gyr_temp.axis = gyr->axis;
gyr_temp.interval = gyr->interval;
auto ret = sGnssCbIface->gnss_gyr_callback(gyr_temp);
if (!ret.isOk()) {
ALOGE("%s: Unable to invoke callback", __func__);
}
}
void Gnss::gnssAccCb(AutoNavi_Acc* acc){
android::hardware::gnss::V1_0::IGnssCallback::AutoNaviAcc acc_temp;
if (sGnssCbIface == nullptr || acc == nullptr) {
ALOGE("%s: GNSS Callback Interface configured incorrectly", __func__);
return;
}
//ALOGE("%s x=%f, y=%f, z=%f", __func__, acc->x, acc->y, acc->z);
acc_temp.x = acc->x;
acc_temp.y = acc->y;
acc_temp.z = acc->z;
acc_temp.ticktime = acc->ticktime;
acc_temp.axis = acc->axis;
acc_temp.interval = acc->interval;
auto ret = sGnssCbIface->gnss_acc_callback(acc_temp);
if (!ret.isOk()) {
ALOGE("%s: Unable to invoke callback", __func__);
}
}
可以看到,第19行和第42行,分別呼叫了jni層的回呼介面,通過這個介面,就把資料給傳遞到了jni層,
五、總結
到這里,安卓平臺下的GPS架構介紹及驅動移植記錄大概就介紹完了,
實際專案中,還是要結合實際問題進行分析,
要做驅動移植,對代碼還是需要熟悉的,雖然大部分代碼都會由gps模塊供應商提供,但是在除錯的時候總不能遇到問題就問gps模塊供應商,而且gps模塊供應商提供的驅動代碼也只是標準代碼,并不一定適合你的平臺,還是需要部分調整的,
另外,在除錯好gps hal層驅動代碼之后,還需要對標地圖app,需要將我們上傳的資料格式和上報頻率都更改為地圖app所需要的才行,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/210038.html
標籤:java
