1. 概述
上面的几篇文章论述了gps的打开启动初始化等动作,万事俱备只欠东风了。这一系列文章主要讲的是Position信息如何从modem层传递到loc eng层最后一直到Java上层的。由于loc eng层到modem层是属于消息触发的,也就是说正常的流程是:modem层传上来一个消息,经过一系列处理传递到loc eng层,loc eng层传递给gps库,android framework层,然后上层应用就可以获取到数据。
高通平台的GPS核心部分都在modem里面,这里面实现了GPS相关的协议,可以把这个部分看做是GPS service,另外一部分在ap侧,我们把这部分看做是GPS client。Client主要是通过QMI的通信方式接收service发过来的信息,client也可以通过QMI发送信息给service,QMI之上有一个loc_api层,具体作用是通过qmi读取service发送过来的信息,当然也可以通过qmi发送信息给service。
2. AP数据结构
这里我们主要涉及三个重要的数据结构,分别是qmiLocEventPositionReportIndMsgT_v02、UlpLocation、GpsLocation:qmiLocEventPositionReportIndMsgT_v02是一个很重要的数据结构,它是AP侧和Modem侧之间传递的Position数据结构,是一个连接两者的桥梁,由于结构比较庞大,截取一部分如下,完整结构具体请看:
vendor/qcom/opensource/location/loc_api/loc_api_v02/location_service_v02.h
UlpLocation*是loc eng层使用的格式, UlpLocation里包含了GpsLocation:
hardware/qcom/gps/core/gps_extended_c.h
GpsLocation*是hal层能够识别的格式:
hardware/libhardware/include/hardware/gps.h;
三个数据结构的大致关系是:解析qmiLocEventPositionReportIndMsgT_v02类型的event,
提取其中需要的数据,填充到UlpLocation结构中,由于包含了GpsLocation,因此GpsLocation也同时被填充;
小结:
数据库都准备好了,下面就开始分析数据是如何从modem到AP侧,最后到Java上层的。
3. Modem流程分析
modem_proc\gps\gnss\loc_mw\src\ loc_task.c
首先初始化middlewaremodule,设置IPC以及timers,初始化QMI等一系列工作:
通过os_IpcReceive接受消息,调用loc_middleware_proc对p_msg进行处理:
在loc_middleware_proc函数里根据p_msg的MsgId判断消息类型,再调用不同的处理函数:
在locQmiShimProcessIpc函数中,与location相关的命令再调用locQmiShimProcessInitQmiLoc函数处理:
modem_proc\gps\gnss\loc_mw\src\loc_qmi_shim.c
在这个函数里注册了locQmiShimClientConnectCb这个回调:
而这个回调函数主要的作用是通知shim层连接的到来,初始化连接,触发loc:
在locQmiShimEventCb回调中,第二个参数是e_LocEvent,即loction事件,函数根据这个事件的类型,调用相关的处理函数,我们这边只关注LOC_EVENT_PARSED_POSITION_REPORT事件类型,即解析后的position的report事件:
modem_proc/gps/gnss/loc_mw_inc/loc_api_2.h
往下调用了locQmiHandleParsedPositionReport函数,通过解析第二个参数pz_ParsedPositionRpt,
解析出经纬度等GPS信息,填充到pz_IndInfo:
这里我们看到这个数据结构:qmiLocEventPositionReportIndMsgT_v02,也就是在ap侧解析的GPS的event信息,
并最终剥离出上层能够识别的GpsLocation数据结构的:
这里使用到一个宏:LOC_QMI_SHIM_SET_VALUE,作用就是验证variable是否有效,如果有效,就把value的值赋值给variable:
在函数最后,将q_Id的值设为QMI_LOC_EVENT_POSITION_REPORT_IND_V02,
AP侧在eventCb里根据QMI_LOC_EVENT_POSITION_REPORT_IND_V02做判断,调用reportPosition方法继续传输数据。
4. NMEA数据的解析
先看一段QXDM log,是获取NMEA的一个大致流程:
loc_middleware_proc中打印Received IPC Message with id 10.Loc enabled 1
因为cmd_type是1,也就是LOC_CMD_TYPE_REPORT_POSITION:
modem_proc\gps\gnss\loc_mw\src\loc_task.h
因此接下来调用了loc_pd_process_cmd_request这个函数,在这个函数中,因为cmd_type是LOC_CMD_TYPE_REPORT_POSITION类型,因此调用loc_pd_notify_client函数:
modem_proc\gps\gnss\loc_mw\src\ loc_pd.c
在loc_pd_notify_client函数中,调用loc_pd_report获取position信息,对应输出的log:loc_pd_get_sv_repoet:health_svmask 0x0和gnss report, mask = 48, num_sv = 19,再调用loc_client_invoke_callback函数,eventtype是LOC_EVENT_NMEA_POSITION_REPORT,处理nmea的event事件,对应输出的log:“loc_client_invoke_callback, client_handle = 3, event_type = 2, maxclients = 5”
loc_pd_ext_event_cb函数中,调用了loc_pd_queue_report_position_request:
在loc_middleware_queue_cmd_internal函数里的处理,对应了log:“send msg_id=10, cmd=1 to locthread”。最后调用了tm_nmea_gpgga_put函数,对应输出”Generated NMEA $GPGGA”,在tm_nmea.c里,有分别处理GPGGA、GNGNS、GPGSV、GPGSA等格式的函数,感兴趣的可以深入学习了解:
modem_proc\gps\gnss\sm\tm\src\tm_nmea.c
modem_proc\gps\gnss\sm\tm\src\tm_util.c
最后格式化输出,也就是按照NMEA标准的格式输出。
5. gps数据从modem到AP
从loc_api层的locClientIndCb开始走起:
vendor/qcom/opensource/location/loc_api/loc_api_v02/loc_api_v02_client.c
locClientIndCb的主要作用是根据indications的类型,将解析出来的数据发给相应的callback处理:
首先获取message的size和indType,后面根据IndType判断indication的类型:
解析qmi message,ind_bug是传入数据,indBuffer是解析之后的数据;
Qmi_client_message_decode返回QMI_NO_ERR,并且解析的indType是eventIndType后,走下面的流程,eventCallback赋值给了localEventCallback,解析出来的数据indBuffer赋值给了eventIndUnion.pPositionReportEvent,
其中pPositionReportEvent的数据类型是qmiLocEventPositionReportIndMsgT_v02:
qmiLocEventPositionReportIndMsgT_v02里包含了latitude、longitude等位置信息:
上面eventCallback赋值给了localEventCallback,其实也就是赋值给了后面的globalEventCb,在继续往下之前,我们看一下globalEventCb的绑定过程:
vendor/qcom/opensource/location/loc_api/loc_api_v02/LocApiV02.cpp
globalCallbacks的赋值又是一个典型的callback绑定动作,由于类型是locClientCallbacksType,
所以是把globalEventCb绑定到locClientCallbacksType的一个成员函数上,这个成员函数就是eventIndCb。
vendor/qcom/opensource/location/loc_api/loc_api_v02/loc_api_v02_client.h
继续回到locClientOpen函数中,又调用了locClientOpenInstance:
vendor/qcom/opensource/location/loc_api/loc_api_v02/loc_api_v02_client.c
到此,eventCallback跟eventIndCb成功绑定,也就是说eventCallback 跟globalEventCb绑定在一起了,由于eventCallback在前面绑定到了localEventCallback,所以就是说localEventCallback绑定的是globalEventCb;因此对localEventCallback的调用,也就是调用了globalEventCb:
vendor/qcom/opensource/location/loc_api/loc_api_v02/LocApiV02.cpp
由于locApaV02Instance是LocApiV02类型的,所以调用到了LocApiV02的eventCb函数,在eventCb里根据eventId做判断,如果eventId是QMI_LOC_EVENT_POSITION_REPORT_IND_V02,则调用reportPosition: