主頁 >  其他 > 安卓平臺下的GPS架構介紹及驅動移植記錄

安卓平臺下的GPS架構介紹及驅動移植記錄

2020-11-12 10:06:07 其他

一、前言

我的作業是關于汽車車機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/qita/211753.html

標籤:其他

上一篇:iOS UIButton 擴大點擊區域,按鈕內部文字和切圖布局調整

下一篇:安卓一步一步搭建組件化

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more