当前位置: 星创客 > 学习资源 > 讲师博文 > Gps HAL层分析
Gps HAL层分析 时间:2018-02-02     来源:星创客

1. Android 6.0源码中Gps HAL层代码分析

我们知道gps在HAL层是库的方式存在的,它的库的名称是gps.default.so,我们可以根据这个命令来查找find –name Android.mk –exec grep –l “gps.default” {} \;,我们获取的文件的路径在如下位置:fspad-733-6.0/androidM/device/softwinner/common/hardware/libhardware/libgps

 

我们可以打开Android.mk查看这个动态库依赖的源码

 

从这个源码中我们不难发现这个动态库依赖的源码为gps_ql.c ql-log.c config.c这三个文件。我们先从gps_ql.c这个文件分析。Gps HAL定义的结构体是hw_module_t,按照以往的分析方法,这里我们重点关心methods。

 struct hw_module_t HAL_MODULE_INFO_SYM = {

     .tag = HARDWARE_MODULE_TAG,

     .version_major = 1,

     .version_minor = 0,

     .id = GPS_HARDWARE_MODULE_ID, 

     .name = "QUECTEL GPS Module",

     .author = "Joe.Wang",

     .methods = &gps_module_methods,

 };

 

 static struct hw_module_methods_t gps_module_methods = {

     .open = open_gps

 };

 

 static int open_gps(const struct hw_module_t* module, char const* name,

         struct hw_device_t** device)

 {

     struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));

     memset(dev, 0, sizeof(*dev));

 

    dev->common.tag = HARDWARE_DEVICE_TAG;

    dev->common.version = 0;

    dev->common.module = (struct hw_module_t*)module;

    dev->get_gps_interface = gps__get_gps_interface;

 

    *device = (struct hw_device_t*)dev;

    return 0;

}

 

在这个open函数中就是对gps_device_t的这个结构体初始化,在这个结构体中重要的就是get_gps_interface这个函数指针的初始化。dev->get_gps_interface = gps__get_gps_interface;所以接下来我们就要分析gps__get_gps_interface;函数。

struct gps_device_t {                                                                                                

    struct hw_device_t common;

    const GpsInterface* (*get_gps_interface)(struct gps_device_t* dev);

};

 

const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev)                                                 

{

    return &qlGpsInterface;

}

当jni代码代用这个函数的时候,它会向jni代码返回一个GpsInterface结构体,jni代码所在Android6.0的源码位置如下:frameworks/base/services/core/jni/com_android_server_location_GpsLocationProvider.cpp,GpsInterface这个结构体的成员如下:

typedef struct {

    size_t          size;     //此结构体所占内存大小

    int   (*init)( GpsCallbacks* callbacks ); //初始化,获取回调接口

    int   (*start)( void );  //开始采集数据

    int   (*stop)( void );   //停止采集数据                                                                             

    void  (*cleanup)( void ); //释放回调接口 

    int   (*inject_time)(GpsUtcTime time, int64_t timeReference,int uncertainty);  

//校准当前时间

    int  (*inject_location)(double latitude, double longitude, float accuracy);

//校准当前位置(经纬度)

......

} GpsInterface;

这个结构体被填充的成员如下,其实就是qlGpsInterface返回给上层的值。

static const GpsInterface  qlGpsInterface = {

    sizeof(GpsInterface),

    ql_gps_init,                                                                                                     

    ql_gps_start,

    ql_gps_stop,

    ql_gps_cleanup,                                                                                                            

    ql_gps_inject_time,

    ql_gps_inject_location,

    ql_gps_delete_aiding_data,

    ql_gps_set_position_mode,

    ql_gps_get_extension,

};

 

这个函数被填充好之后是共上层来回调的,那个jni代码是如何调用hal层?,jni代码的文件位置在前面我们已经说过了,接下来它的代码实现过程如下:

static JNINativeMethod sMethods[] = {

     /* name, signature, funcPtr */

    {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},

    {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init},

{"native_start", "()Z", (void*)android_location_GpsLocationProvider_start},

    {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop},

{"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time},

{"native_inject_location","(DDF)V",(void*)android_location_GpsLocationProvider_inject_location},

......

}

 

android_location_GpsLocationProvider_class_init_native函数完成的工作如下:

Hw_get_module通过ID = GPS_HARDWARE_MODULE_ID来查找硬件抽象层的模块

module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);回调硬件抽象层的open方法。

sGpsInterface = gps_device->get_gps_interface(gps_device);调用hal层get_gps_interface中的方法,返回前面我们看到的填充好的结构体。

 

android_location_GpsLocationProvider_init函数完成的功能:

回调上述初始化好的结构体中的init方法。    

if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)

向hal传递初始化好的sGpsCallbacks,这里面的函数是有hal层来回调的。

GpsCallbacks sGpsCallbacks = {

    sizeof(GpsCallbacks),

    location_callback,

    status_callback,

    sv_status_callback,

    nmea_callback,

    set_capabilities_callback,

    acquire_wakelock_callback,

    release_wakelock_callback,

    create_thread_callback,

    request_utc_time_callback,

};

其他的函数功能我们等下再分析,我们按照代码的执行流程来查看这个过程,我们接下来看硬件抽象层的init函数。

在init函数中首先会检查模块的类型,检查的过程如下

ret = check_module_type();

get_config();

read_config(GPS_CONFIG_FILE_PATH); // #define GPS_CONFIG_FILE_PATH "gps_cfg.inf"

#从这里我们能够清楚的看到它会读取当前目录下的gps_cfg.inf的配置文件。

 

读取过程如下:

定义二维数组,并对数组初始化为*

static char value[CFG_NUM][CFG_CONTENT_LENGTH];  // CFG_NUM 13

//CFG_CONTENT_LENGTH 50

memset(value,'*',CFG_NUM * CFG_CONTENT_LENGTH);

②打开gps_cfg.inf文件

file = fopen(config_file_path,"r");

③读取内容,每次读取127个字节

while(fgets(buf, sizeof(buf) - 1,file) != NULL)  

如果开头有”#”,”;”,’\0’,’\r’,’\n’等这些字符,那么就跳过该行的读取,接着读取下一行。

接着通过strstr(buf,QL_head[i]),pt = strchr(ph,' '); strcpy(value[i], ph);这三个函数终将读取的数据写到value这个二维数组中,按什么读取那?就是上面我们看到的QL_head[i]这个数组。数组的成员如下:

 

 

接着调用get_config函数将前面读取到的数据复制到字符串中,有MODULE_TYPE,GPS_CHANNEL,BAUD_RATE等内容。

 

 

在check_module_type(void)函数中会对数据头信息,驱动的设备节点检查,检测完成之后调用波特率初始化函数。但是我们发现这些检测并没有做直接通过return返回了。

 

 

检查完参数之后我们重新回到init函数中,将jni中传递过来的callbacks函数赋值给callback_p的这个变量中,接着定义GpsState的结构体,这是保存gps状态的,

 

 

当这些赋值完成之后,调用gps_state_init函数进行初始化,初始化的过程如下:

 

   

 

从上面的代码我们能够清楚的看到,它的功能如下:

初始化GpsState结构体

打开/dev/ttyUSB1

串口波特率的初始化(8位,波特率115200等)

使用Socketpair创建进程间通讯的全双工的管道

回调jni代码中创建线程的函数(为什么不在这里面直接创建线程那?因为这样创建的线程和java虚拟机没有关联,pthread_create创建的线程在内存中,所以这里需要回调来创建)。

我们可以看下线程体所做的工作,对应的函数是gps_state_thread。

 

 

我们发现这里的epoll监听了两个文件描述符,其中一个就是前面创建的管道描述符,其次是监听的poll(上报数据)的描述符。

 

在管道fd监听到事件之后通过如下步骤执行

ret = read( fd, &cmd, 1 );   //读取管道中的内容,其实就是如下的命令码

cmd == CMD_QUIT

cmd == CMD_START

cmd == CMD_STOP

 

但是在这个代码中此处的read并没有使用,在Android5.0在start函数中发信号,然后这个里接收信号,开始读取数据。而6.0是通过epoll函数来监听打开的/dev/ttyUSB1的方式来读取数据的。读取数据的过程如下:

nevents = epoll_wait( epoll_fd, events, 2, -1 );

if ((events[ne].events & EPOLLIN) != 0)

else if (fd == gps_fd)

ret = read( fd, buff, sizeof(buff) );       //读取数据

nmea_reader_addc( reader, buff[nn] );  //数据处理

nmea_reader_parse( r );          //数据解析

 

 

终我们可以看到将数据更新,这里更新完数据之后会调用回调函数,将数据传递给jni代码,jni代码通过如下函数将数据更新到应用层APP,至此整个代码就分析完成了。

前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2018 北京华清远见科技发展有限公司 版权所有 ,京ICP备16055225号,京公海网安备11010802025203号
返回

讲师博文

星创客 - 华清远见旗下高端IT培训品牌

当前位置: 星创客 > 学习资源 > 讲师博文 >

Gps HAL层分析
来源: 星创客 作者: 星创客 时间:2018-02-02

1. Android 6.0源码中Gps HAL层代码分析 我们知道gps在HAL层是库的方式存在的,它的库的名称是gps.default.so,我们可以根据这个命令来查找find name Android.mk exec grep l gps.default {} \;,我们获取的文件的路...

1. Android 6.0源码中Gps HAL层代码分析

我们知道gps在HAL层是库的方式存在的,它的库的名称是gps.default.so,我们可以根据这个命令来查找find –name Android.mk –exec grep –l “gps.default” {} \;,我们获取的文件的路径在如下位置:fspad-733-6.0/androidM/device/softwinner/common/hardware/libhardware/libgps

 

我们可以打开Android.mk查看这个动态库依赖的源码

 

从这个源码中我们不难发现这个动态库依赖的源码为gps_ql.c ql-log.c config.c这三个文件。我们先从gps_ql.c这个文件分析。Gps HAL定义的结构体是hw_module_t,按照以往的分析方法,这里我们重点关心methods。

 struct hw_module_t HAL_MODULE_INFO_SYM = {

     .tag = HARDWARE_MODULE_TAG,

     .version_major = 1,

     .version_minor = 0,

     .id = GPS_HARDWARE_MODULE_ID, 

     .name = "QUECTEL GPS Module",

     .author = "Joe.Wang",

     .methods = &gps_module_methods,

 };

 

 static struct hw_module_methods_t gps_module_methods = {

     .open = open_gps

 };

 

 static int open_gps(const struct hw_module_t* module, char const* name,

         struct hw_device_t** device)

 {

     struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));

     memset(dev, 0, sizeof(*dev));

 

    dev->common.tag = HARDWARE_DEVICE_TAG;

    dev->common.version = 0;

    dev->common.module = (struct hw_module_t*)module;

    dev->get_gps_interface = gps__get_gps_interface;

 

    *device = (struct hw_device_t*)dev;

    return 0;

}

 

在这个open函数中就是对gps_device_t的这个结构体初始化,在这个结构体中重要的就是get_gps_interface这个函数指针的初始化。dev->get_gps_interface = gps__get_gps_interface;所以接下来我们就要分析gps__get_gps_interface;函数。

struct gps_device_t {                                                                                                

    struct hw_device_t common;

    const GpsInterface* (*get_gps_interface)(struct gps_device_t* dev);

};

 

const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev)                                                 

{

    return &qlGpsInterface;

}

当jni代码代用这个函数的时候,它会向jni代码返回一个GpsInterface结构体,jni代码所在Android6.0的源码位置如下:frameworks/base/services/core/jni/com_android_server_location_GpsLocationProvider.cpp,GpsInterface这个结构体的成员如下:

typedef struct {

    size_t          size;     //此结构体所占内存大小

    int   (*init)( GpsCallbacks* callbacks ); //初始化,获取回调接口

    int   (*start)( void );  //开始采集数据

    int   (*stop)( void );   //停止采集数据                                                                             

    void  (*cleanup)( void ); //释放回调接口 

    int   (*inject_time)(GpsUtcTime time, int64_t timeReference,int uncertainty);  

//校准当前时间

    int  (*inject_location)(double latitude, double longitude, float accuracy);

//校准当前位置(经纬度)

......

} GpsInterface;

这个结构体被填充的成员如下,其实就是qlGpsInterface返回给上层的值。

static const GpsInterface  qlGpsInterface = {

    sizeof(GpsInterface),

    ql_gps_init,                                                                                                     

    ql_gps_start,

    ql_gps_stop,

    ql_gps_cleanup,                                                                                                            

    ql_gps_inject_time,

    ql_gps_inject_location,

    ql_gps_delete_aiding_data,

    ql_gps_set_position_mode,

    ql_gps_get_extension,

};

 

这个函数被填充好之后是共上层来回调的,那个jni代码是如何调用hal层?,jni代码的文件位置在前面我们已经说过了,接下来它的代码实现过程如下:

static JNINativeMethod sMethods[] = {

     /* name, signature, funcPtr */

    {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},

    {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init},

{"native_start", "()Z", (void*)android_location_GpsLocationProvider_start},

    {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop},

{"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time},

{"native_inject_location","(DDF)V",(void*)android_location_GpsLocationProvider_inject_location},

......

}

 

android_location_GpsLocationProvider_class_init_native函数完成的工作如下:

Hw_get_module通过ID = GPS_HARDWARE_MODULE_ID来查找硬件抽象层的模块

module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);回调硬件抽象层的open方法。

sGpsInterface = gps_device->get_gps_interface(gps_device);调用hal层get_gps_interface中的方法,返回前面我们看到的填充好的结构体。

 

android_location_GpsLocationProvider_init函数完成的功能:

回调上述初始化好的结构体中的init方法。    

if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)

向hal传递初始化好的sGpsCallbacks,这里面的函数是有hal层来回调的。

GpsCallbacks sGpsCallbacks = {

    sizeof(GpsCallbacks),

    location_callback,

    status_callback,

    sv_status_callback,

    nmea_callback,

    set_capabilities_callback,

    acquire_wakelock_callback,

    release_wakelock_callback,

    create_thread_callback,

    request_utc_time_callback,

};

其他的函数功能我们等下再分析,我们按照代码的执行流程来查看这个过程,我们接下来看硬件抽象层的init函数。

在init函数中首先会检查模块的类型,检查的过程如下

ret = check_module_type();

get_config();

read_config(GPS_CONFIG_FILE_PATH); // #define GPS_CONFIG_FILE_PATH "gps_cfg.inf"

#从这里我们能够清楚的看到它会读取当前目录下的gps_cfg.inf的配置文件。

 

读取过程如下:

定义二维数组,并对数组初始化为*

static char value[CFG_NUM][CFG_CONTENT_LENGTH];  // CFG_NUM 13

//CFG_CONTENT_LENGTH 50

memset(value,'*',CFG_NUM * CFG_CONTENT_LENGTH);

②打开gps_cfg.inf文件

file = fopen(config_file_path,"r");

③读取内容,每次读取127个字节

while(fgets(buf, sizeof(buf) - 1,file) != NULL)  

如果开头有”#”,”;”,’\0’,’\r’,’\n’等这些字符,那么就跳过该行的读取,接着读取下一行。

接着通过strstr(buf,QL_head[i]),pt = strchr(ph,' '); strcpy(value[i], ph);这三个函数终将读取的数据写到value这个二维数组中,按什么读取那?就是上面我们看到的QL_head[i]这个数组。数组的成员如下:

 

 

接着调用get_config函数将前面读取到的数据复制到字符串中,有MODULE_TYPE,GPS_CHANNEL,BAUD_RATE等内容。

 

 

在check_module_type(void)函数中会对数据头信息,驱动的设备节点检查,检测完成之后调用波特率初始化函数。但是我们发现这些检测并没有做直接通过return返回了。

 

 

检查完参数之后我们重新回到init函数中,将jni中传递过来的callbacks函数赋值给callback_p的这个变量中,接着定义GpsState的结构体,这是保存gps状态的,

 

 

当这些赋值完成之后,调用gps_state_init函数进行初始化,初始化的过程如下:

 

   

 

从上面的代码我们能够清楚的看到,它的功能如下:

初始化GpsState结构体

打开/dev/ttyUSB1

串口波特率的初始化(8位,波特率115200等)

使用Socketpair创建进程间通讯的全双工的管道

回调jni代码中创建线程的函数(为什么不在这里面直接创建线程那?因为这样创建的线程和java虚拟机没有关联,pthread_create创建的线程在内存中,所以这里需要回调来创建)。

我们可以看下线程体所做的工作,对应的函数是gps_state_thread。

 

 

我们发现这里的epoll监听了两个文件描述符,其中一个就是前面创建的管道描述符,其次是监听的poll(上报数据)的描述符。

 

在管道fd监听到事件之后通过如下步骤执行

ret = read( fd, &cmd, 1 );   //读取管道中的内容,其实就是如下的命令码

cmd == CMD_QUIT

cmd == CMD_START

cmd == CMD_STOP

 

但是在这个代码中此处的read并没有使用,在Android5.0在start函数中发信号,然后这个里接收信号,开始读取数据。而6.0是通过epoll函数来监听打开的/dev/ttyUSB1的方式来读取数据的。读取数据的过程如下:

nevents = epoll_wait( epoll_fd, events, 2, -1 );

if ((events[ne].events & EPOLLIN) != 0)

else if (fd == gps_fd)

ret = read( fd, buff, sizeof(buff) );       //读取数据

nmea_reader_addc( reader, buff[nn] );  //数据处理

nmea_reader_parse( r );          //数据解析

 

 

终我们可以看到将数据更新,这里更新完数据之后会调用回调函数,将数据传递给jni代码,jni代码通过如下函数将数据更新到应用层APP,至此整个代码就分析完成了。

相关推荐

全国咨询热线:400-611-6270

?2004-2018华清远见教育科技集团 版权所有 京ICP备16055225号 京公海网安备11010802025203号