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,至此整个代码就分析完成了。