安卓平台下的GPS架构介绍及驱动移植记录
一、前言
我的工作是關于汽車車機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_versionuint16_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];
#elseuint32_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/vndbinderandroid::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/gryReturn<void> gnss_gyr_callback(const IGnssCallback::AutoNaviGyr& gry) override;Return<void> gnss_acc_callback(const IGnssCallback::AutoNaviAcc& acc) override;// New in 1.1Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;// TODO(b/73306084): Reconsider allocation cost vs threadsafety on these staticsstatic 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 pipeif (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所需要的才行。
總結
以上是生活随笔為你收集整理的安卓平台下的GPS架构介绍及驱动移植记录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手握鸡蛋而不碎,工程师开发出突破性的“机
- 下一篇: 散列表知识点总结