刚刚开始接触 ANDROID GPS 的时候一头雾水,只好在网上找了很多关于GPS流程的说明。大致了解了GPS于哪几个文件有关
/framework/base/services/jni/com_android_server_location_GpsLocationProvider.cpp
hardware/libhardware_legacy/gps
hardware/libhardware_legacy/include/hardware_legacy/gps.h
通过查看文件,发现我的源码包里面 hardware/libhardware_legacy 目录里面没 GPS 文件夹。
咋整呢?
没办法,自己创建一个
hardware/libhardware/modules/ 下创建一个 GPS 文件夹, 在 hardware/libhardware_legacy/ 这下面也一样的,
记得修改 Android.mk 就可以了。
修改 hardware/libhardware/ 下的 Android.mk 文件
include $(addsuffix /Android.mk, $(addprefix $(LOCAL_PATH)/, \
modules/gralloc \
modules/gps \
tests \
))
把GPS文件夹包含的编译文件里面。
由于 android 模拟器有模拟 GPS 的源码,依葫芦画瓢呗。
把 gps_qemu.c 文件 COPY 到 刚刚创建的 GPS 文件夹内。
这样文件有了,就差一个 Android.mk 文件了。
本人对这个文件并不熟悉,没办法,还是 COPY 把,把类似的 文件夹里面的 Android.mk 文件 COPY 过来做一些修改。
修改里面包含的文件啊,路径之类的。
我的 Android.mk 文件如下:
LOCAL_PATH := $(call my-dir)
# HAL module implemenation, not prelinked and stored in
# hw/<OVERLAY_HARDWARE_MODULE_ID>.<ro.product.board>.so
include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_SRC_FILES := \
gps_qemu.c
LOCAL_MODULE := gps.default
#LOCAL_CFLAGS:= -DLOG_TAG=\"gps\"
ifeq ($(BOARD_NO_PAGE_FLIPPING),true)
LOCAL_CFLAGS += -DNO_PAGE_FLIPPING
endif
include $(BUILD_SHARED_LIBRARY)
下一步就是 gps_qemu.c 的修改了
首先是 gps_state_init 打开串口设备
state->fd = open(GPS_Serial_Name, O_RDONLY );
这里打开串口,GPS_Serial_Name 这根据自己的设备查找对应串口的设备节点。
设置串口事件
// disable echo on serial lines
if ( isatty( state->fd ) ) {
struct termios ios;
tcgetattr( state->fd, &ios );
ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */
ios.c_oflag &= (~ONLCR); /* Stop \n -> \r\n translation on output */
ios.c_iflag &= (~(ICRNL | INLCR)); /* Stop \r -> \n & \n -> \r translation on input */
ios.c_iflag |= (IGNCR | IXOFF); /* Ignore \r & XON/XOFF on input */
cfsetispeed(&ios, B38400);
cfsetospeed(&ios, B38400);
tcsetattr( state->fd, TCSANOW, &ios );
}
这一步非常重要,如果没有这一步,那么在 GPS HAL 线程里面不会接收串口数据。
这样 Android 的 HAL 层于串口就接通了。
接下来就是解析串口数据,由于模拟器里面已经有解析 RMC 的GPS结构。
所以直接测试就可以了。
直接测试后发现当有 GPS 数据调用 CALLBACK 函数
if (r->callback)
{
r->callback( &r->fix );
r->fix.flags = 0;
}
后 ANDROID 重启了,查询网上资料,发现很多同志在 2.3 也碰到这问题,主要是创建线程的问题。
把创建线程的函数换一下就好了。
/*
if ( pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0 ) {
LOGE("could not create gps thread: %s", strerror(errno));
goto Fail;
}
*/
state->thread = state->callbacks.create_thread_cb("qemu_gps", gps_state_thread, state);
这样系统就能正常启动并接收经纬度了。
接收经纬度后 关闭 GPS 再打开,发现 GPS 没有经纬度了,奇怪
通过跟踪,发现在 GPS 关闭后会调用 HAL 层 qemu_gps_cleanup 把串口关闭,
当重新开启的时候 调用 JNI
/framework/base/services/jni/com_android_server_location_GpsLocationProvider.cpp
里面的 android_location_GpsLocationProvider_init,发现里面的 sGpsInterface 指针已经存在,
就不会调用 sGpsInterface->init 函数。
所以在 JNI 层的 cleanup 函数里面把这个指针清除。
static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj)
{
const GpsInterface* interface = GetGpsInterface(env, obj);
LOGE("%s \n", __FUNCTION__);
if (interface)
interface->cleanup();
sGpsInterface = NULL ;
}
这样就可以啦, GPS 移植已经 OK 现在还有一个 BUG 就是状态栏那个 GPS 图标,
关闭 GPS 后不知道为什么不会消失,还需要进一步研究。
下面是我的 gps_qemu.c 文件。
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* this implements a GPS hardware library for the Android emulator.
* the following code should be built as a shared library that will be
* placed into /system/lib/hw/gps.goldfish.so
*
* it will be loaded by the code in hardware/libhardware/hardware.c
* which is itself called from android_location_GpsLocationProvider.cpp
*/
#include <errno.h>
#include <pthread.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <math.h>
#include <time.h>
#include <semaphore.h>
#include <signal.h>
#include <unistd.h>
#define LOG_TAG "gps_qemu"
#include <cutils/log.h>
#include <cutils/sockets.h>
#include <cutils/properties.h>
//#include <hardware_legacy/gps.h>
#include <hardware/gps.h>
//frank
/* the name of the qemud-controlled socket */
#define QEMU_CHANNEL_NAME "gps"
#define GPS_DEBUG 1
#define GPS_TestData "$GPRMC,053322.682,A,2502.6538,N,12121.4838,E,0.00,080905,,,A*6F\n"
#define GPS_Serial_Name "/dev/s3c2410_serial3"
#if GPS_DEBUG
# define D(...) LOGD(__VA_ARGS__)
#else
# define D(...) ((void)0)
#endif
/*****************************************************************/
/*****************************************************************/
/***** *****/
/***** N M E A T O K E N I Z E R *****/
/***** *****/
/*****************************************************************/
/*****************************************************************/
static const void * qemu_gps_get_extension(const char* name);
/* this is the state of our connection to the qemu_gpsd daemon */
typedef struct {
int init;
int fd;
GpsCallbacks callbacks;
pthread_t thread;
sem_t fix_sem;
int control[2];
} GpsState;
static GpsState _gps_state[1];
static GpsState *gps_state = _gps_state;
/* Since NMEA parser requires lcoks */
#define GPS_STATE_LOCK_FIX(_s) \
{ \
int ret; \
do { \
ret = sem_wait(&(_s)->fix_sem); \
} while (ret < 0 && errno == EINTR); \
}
#define GPS_STATE_UNLOCK_FIX(_s) \
sem_post(&(_s)->fix_sem)
typedef struct {
const char* p;
const char* end;
} Token;
#define MAX_NMEA_TOKENS 16
typedef struct {
int count;
Token tokens[ MAX_NMEA_TOKENS ];
} NmeaTokenizer;
static int
nmea_tokenizer_init( NmeaTokenizer* t, const char* p, const char* end )
{
int count = 0;
char* q;
// the initial '$' is optional
if (p < end && p[0] == '$')
p += 1;
// remove trailing newline
if (end > p && end[-1] == '\n') {
end -= 1;
if (end > p && end[-1] == '\r')
end -= 1;
}
// get rid of checksum at the end of the sentecne
if (end >= p+3 && end[-3] == '*') {
end -= 3;
}
while (p < end) {
const char* q = p;
q = memchr(p, ',', end-p);
if (q == NULL)
q = end;
if (q > p) {
if (count < MAX_NMEA_TOKENS) {
t->tokens[count].p = p;
t->tokens[count].end = q;
count += 1;
}
}
if (q < end)
q += 1;
p = q;
}
t->count = count;
return count;
}
static Token
nmea_tokenizer_get( NmeaTokenizer* t, int index )
{
Token tok;
static const char* dummy = "";
if (index < 0 || index >= t->count) {
tok.p = tok.end = dummy;
} else
tok = t->tokens[index];
return tok;
}
static int
str2int( const char* p, const char* end )
{
int result = 0;
int len = end - p;
for ( ; len > 0; len--, p++ )
{
int c;
if (p >= end)
goto Fail;
c = *p - '0';
if ((unsigned)c >= 10)
goto Fail;
result = result*10 + c;
}
return result;
Fail:
return -1;
}
static double
str2float( const char* p, const char* end )
{
int result = 0;
int len = end - p;
char temp[16];
if (len >= (int)sizeof(temp))
return 0.;
memcpy( temp, p, len );
temp[len] = 0;
return strtod( temp, NULL );
}
/*****************************************************************/
/*****************************************************************/
/***** *****/
/***** N M E A P A R S E R *****/
/***** *****/
/*****************************************************************/
/*****************************************************************/
#define NMEA_MAX_SIZE 83
typedef struct {
int pos;
int overflow;
int utc_year;
int utc_mon;
int utc_day;
int utc_diff;
GpsLocation fix;
GpsSvStatus sv_status;
gps_location_callback callback;
int sv_status_changed;
char in[ NMEA_MAX_SIZE+1 ];
} NmeaReader;
static void
nmea_reader_update_utc_diff( NmeaReader* r )
{
time_t now = time(NULL);
struct tm tm_local;
struct tm tm_utc;
long time_local, time_utc;
gmtime_r( &now, &tm_utc );
localtime_r( &now, &tm_local );
time_local = tm_local.tm_sec +
60*(tm_local.tm_min +
60*(tm_local.tm_hour +
24*(tm_local.tm_yday +
365*tm_local.tm_year)));
time_utc = tm_utc.tm_sec +
60*(tm_utc.tm_min +
60*(tm_utc.tm_hour +
24*(tm_utc.tm_yday +
365*tm_utc.tm_year)));
r->utc_diff = time_utc - time_local;
}
static void
nmea_reader_init( NmeaReader* r )
{
memset( r, 0, sizeof(*r) );
r->pos = 0;
r->overflow = 0;
r->utc_year = -1;
r->utc_mon = -1;
r->utc_day = -1;
r->callback = NULL;
r->fix.size = sizeof(r->fix);
nmea_reader_update_utc_diff( r );
}
static void
nmea_reader_set_callback( NmeaReader* r, gps_location_callback cb )
{
r->callback = cb;
//r->fix.flags=0x0008;//data for test callback
// D("%s: callback=%x flags=%d \n", __FUNCTION__,cb,r->fix.flags);
if (cb != NULL && r->fix.flags != 0) {
D("%s: sending latest fix to new callback", __FUNCTION__);
r->callback( &r->fix );
r->fix.flags = 0;
}
}
static int
nmea_reader_update_time( NmeaReader* r, Token tok )
{
int hour, minute;
double seconds;
struct tm tm;
time_t fix_time;
if (tok.p + 6 > tok.end)
return -1;
if (r->utc_year < 0) {
// no date yet, get current one
time_t now = time(NULL);
gmtime_r( &now, &tm );
r->utc_year = tm.tm_year + 1900;
r->utc_mon = tm.tm_mon + 1;
r->utc_day = tm.tm_mday;
}
hour = str2int(tok.p, tok.p+2);
minute = str2int(tok.p+2, tok.p+4);
seconds = str2float(tok.p+4, tok.end);
tm.tm_hour = hour;
tm.tm_min = minute;
tm.tm_sec = (int) seconds;
tm.tm_year = r->utc_year - 1900;
tm.tm_mon = r->utc_mon - 1;
tm.tm_mday = r->utc_day;
tm.tm_isdst = -1;
fix_time = mktime( &tm ) + r->utc_diff;
r->fix.timestamp = (long long)fix_time * 1000;
return 0;
}
static int
nmea_reader_update_date( NmeaReader* r, Token date, Token time )
{
Token tok = date;
int day, mon, year;
if (tok.p + 6 != tok.end) {
D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
return -1;
}
day = str2int(tok.p, tok.p+2);
mon = str2int(tok.p+2, tok.p+4);
year = str2int(tok.p+4, tok.p+6) + 2000;
if ((day|mon|year) < 0) {
D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
return -1;
}
r->utc_year = year;
r->utc_mon = mon;
r->utc_day = day;
return nmea_reader_update_time( r, time );
}
static double
convert_from_hhmm( Token tok )
{
double val = str2float(tok.p, tok.end);
int degrees = (int)(floor(val) / 100);
double minutes = val - degrees*100.;
double dcoord = degrees + minutes / 60.0;
return dcoord;
}
static int
nmea_reader_update_latlong( NmeaReader* r,
Token latitude,
char latitudeHemi,
Token longitude,
char longitudeHemi )
{
double lat, lon;
Token tok;
D("%s \n", __FUNCTION__);
tok = latitude;
if (tok.p + 6 > tok.end) {
D("latitude is too short: '%.*s'", tok.end-tok.p, tok.p);
return -1;
}
lat = convert_from_hhmm(tok);
if (latitudeHemi == 'S')
lat = -lat;
tok = longitude;
if (tok.p + 6 > tok.end) {
D("longitude is too short: '%.*s'", tok.end-tok.p, tok.p);
return -1;
}
lon = convert_from_hhmm(tok);
if (longitudeHemi == 'W')
lon = -lon;
//D("GPS_LOCATION_HAS_LAT_LONG \n");
r->fix.flags |= GPS_LOCATION_HAS_LAT_LONG;
r->fix.latitude = lat;
r->fix.longitude = lon;
return 0;
}
static int
nmea_reader_update_altitude( NmeaReader* r,
Token altitude,
Token units )
{
double alt;
Token tok = altitude;
D("%s \n", __FUNCTION__);
if (tok.p >= tok.end)
return -1;
//D("longitude is too short GPS_LOCATION_HAS_ALTITUDE \n");
r->fix.flags |= GPS_LOCATION_HAS_ALTITUDE;
r->fix.altitude = str2float(tok.p, tok.end);
return 0;
}
static int
nmea_reader_update_bearing( NmeaReader* r,
Token bearing )
{
double alt;
Token tok = bearing;
// D("%s \n", __FUNCTION__);
if (tok.p >= tok.end)
return -1;
//D("GPS_LOCATION_HAS_BEARING \n");
r->fix.flags |= GPS_LOCATION_HAS_BEARING;
r->fix.bearing = str2float(tok.p, tok.end);
return 0;
}
static int
nmea_reader_update_cdate( NmeaReader* r, Token tok_d, Token tok_m, Token tok_y )
{
if ( (tok_d.p + 2 > tok_d.end) ||
(tok_m.p + 2 > tok_m.end) ||
(tok_y.p + 4 > tok_y.end) )
return -1;
r->utc_day = str2int(tok_d.p, tok_d.p+2);
r->utc_mon = str2int(tok_m.p, tok_m.p+2);
r->utc_year = str2int(tok_y.p, tok_y.end+4);
return 0;
}
static int
nmea_reader_update_speed( NmeaReader* r,
Token speed )
{
double alt;
Token tok = speed;
//D("%s \n", __FUNCTION__);
if (tok.p >= tok.end)
return -1;
//D("GPS_LOCATION_HAS_SPEED \n");
r->fix.flags |= GPS_LOCATION_HAS_SPEED;
r->fix.speed = str2float(tok.p, tok.end);
return 0;
}
static int
nmea_reader_update_accuracy( NmeaReader* r,
Token accuracy )
{
double acc;
Token tok = accuracy;
if (tok.p >= tok.end)
return -1;
r->fix.accuracy = str2float(tok.p, tok.end);
if (r->fix.accuracy == 99.99){
return 0;
}
//frank
//LOGD("nmea_reader_update_accuracy");
r->fix.flags |= GPS_LOCATION_HAS_ACCURACY;
return 0;
}
static void
nmea_reader_parse( NmeaReader* r )
{
/* we received a complete sentence, now parse it to generate
* a new GPS fix...
*/
NmeaTokenizer tzer[1];
Token tok;
D("Received: '%.*s'", r->pos, r->in);
if (r->pos < 9) {
D("Too short. discarded.");
return;
}
nmea_tokenizer_init(tzer, r->in, r->in + r->pos);
//#if GPS_DEBUG
#if 0
{
int n;
D("Found %d tokens", tzer->count);
for (n = 0; n < tzer->count; n++) {
Token tok = nmea_tokenizer_get(tzer,n);
D("%2d: '%.*s'", n, tok.end-tok.p, tok.p);
}
}
#endif
tok = nmea_tokenizer_get(tzer, 0);
if (tok.p + 5 > tok.end) {
D("sentence id '%.*s' too short, ignored.", tok.end-tok.p, tok.p);
return;
}
// ignore first two characters.
tok.p += 2;
if ( !memcmp(tok.p, "GGA", 3) ) {
// GPS fix
Token tok_time = nmea_tokenizer_get(tzer,1);
Token tok_latitude = nmea_tokenizer_get(tzer,2);
Token tok_latitudeHemi = nmea_tokenizer_get(tzer,3);
Token tok_longitude = nmea_tokenizer_get(tzer,4);
Token tok_longitudeHemi = nmea_tokenizer_get(tzer,5);
Token tok_altitude = nmea_tokenizer_get(tzer,9);
Token tok_altitudeUnits = nmea_tokenizer_get(tzer,10);
nmea_reader_update_time(r, tok_time);
nmea_reader_update_latlong(r, tok_latitude,
tok_latitudeHemi.p[0],
tok_longitude,
tok_longitudeHemi.p[0]);
nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits);
} else if ( !memcmp(tok.p, "GLL", 3) ) {
LOGD(">>>>>>>> GLL string");
Token tok_fixstaus = nmea_tokenizer_get(tzer,6);
if (tok_fixstaus.p[0] == 'A') {
Token tok_latitude = nmea_tokenizer_get(tzer,1);
Token tok_latitudeHemi = nmea_tokenizer_get(tzer,2);
Token tok_longitude = nmea_tokenizer_get(tzer,3);
Token tok_longitudeHemi = nmea_tokenizer_get(tzer,4);
Token tok_time = nmea_tokenizer_get(tzer,5);
nmea_reader_update_time(r, tok_time);
nmea_reader_update_latlong(r, tok_latitude,
tok_latitudeHemi.p[0],
tok_longitude,
tok_longitudeHemi.p[0]);
}
}else if ( !memcmp(tok.p, "GSA", 3) ) {
// do something ?
LOGD(">>>>>>>> GSA string");
Token tok_fixStatus = nmea_tokenizer_get(tzer, 2);
int i;
if (tok_fixStatus.p[0] != '\0' && tok_fixStatus.p[0] != '1') {
Token tok_accuracy = nmea_tokenizer_get(tzer, 15);
nmea_reader_update_accuracy(r, tok_accuracy);
r->sv_status.used_in_fix_mask = 0ul;
for (i = 3; i <= 14; ++i){
Token tok_prn = nmea_tokenizer_get(tzer, i);
int prn = str2int(tok_prn.p, tok_prn.end);
if (prn > 0){
r->sv_status.used_in_fix_mask |= (1ul << (32 - prn));
r->sv_status_changed = 1;
// D("%s: fix mask is %d", __FUNCTION__, r->sv_status.used_in_fix_mask);
}
}
}
} else if ( !memcmp(tok.p, "GSV", 3) ) {
Token tok_noSatellites = nmea_tokenizer_get(tzer, 3);
int noSatellites = str2int(tok_noSatellites.p, tok_noSatellites.end);
if (noSatellites > 0) {
Token tok_noSentences = nmea_tokenizer_get(tzer, 1);
Token tok_sentence = nmea_tokenizer_get(tzer, 2);
int sentence = str2int(tok_sentence.p, tok_sentence.end);
int totalSentences = str2int(tok_noSentences.p, tok_noSentences.end);
int curr;
int i;
if (sentence == 1) {
r->sv_status_changed = 0;
r->sv_status.num_svs = 0;
}
curr = r->sv_status.num_svs;
i = 0;
while (i < 4 && r->sv_status.num_svs < noSatellites){
Token tok_prn = nmea_tokenizer_get(tzer, i * 4 + 4);
Token tok_elevation = nmea_tokenizer_get(tzer, i * 4 + 5);
Token tok_azimuth = nmea_tokenizer_get(tzer, i * 4 + 6);
Token tok_snr = nmea_tokenizer_get(tzer, i * 4 + 7);
r->sv_status.sv_list[curr].prn = str2int(tok_prn.p, tok_prn.end);
r->sv_status.sv_list[curr].elevation = str2float(tok_elevation.p, tok_elevation.end);
r->sv_status.sv_list[curr].azimuth = str2float(tok_azimuth.p, tok_azimuth.end);
r->sv_status.sv_list[curr].snr = str2float(tok_snr.p, tok_snr.end);
r->sv_status.num_svs += 1;
curr += 1;
i += 1;
}
if (sentence == totalSentences) {
r->sv_status_changed = 1;
}
// LOGD("%s: GSV message with total satellites %d", __FUNCTION__, noSatellites);
}
} else if ( !memcmp(tok.p, "RMC", 3) ) {
Token tok_time = nmea_tokenizer_get(tzer,1);
Token tok_fixStatus = nmea_tokenizer_get(tzer,2);
Token tok_latitude = nmea_tokenizer_get(tzer,3);
Token tok_latitudeHemi = nmea_tokenizer_get(tzer,4);
Token tok_longitude = nmea_tokenizer_get(tzer,5);
Token tok_longitudeHemi = nmea_tokenizer_get(tzer,6);
Token tok_speed = nmea_tokenizer_get(tzer,7);
Token tok_bearing = nmea_tokenizer_get(tzer,8);
Token tok_date = nmea_tokenizer_get(tzer,9);
D("in RMC, fixStatus=%c", tok_fixStatus.p[0]);
if (tok_fixStatus.p[0] == 'A')
{
nmea_reader_update_date( r, tok_date, tok_time );
nmea_reader_update_latlong( r, tok_latitude,
tok_latitudeHemi.p[0],
tok_longitude,
tok_longitudeHemi.p[0] );
nmea_reader_update_bearing( r, tok_bearing );
nmea_reader_update_speed ( r, tok_speed );
}
} else if ( !memcmp(tok.p, "VTG", 3) ) {
Token tok_fixStatus = nmea_tokenizer_get(tzer,9);
if (tok_fixStatus.p[0] != '\0' && tok_fixStatus.p[0] != 'N')
{
Token tok_bearing = nmea_tokenizer_get(tzer,1);
Token tok_speed = nmea_tokenizer_get(tzer,5);
nmea_reader_update_bearing( r, tok_bearing );
nmea_reader_update_speed ( r, tok_speed );
}
} else if ( !memcmp(tok.p, "ZDA", 3) ) {
Token tok_time;
Token tok_year = nmea_tokenizer_get(tzer,4);
if (tok_year.p[0] != '\0') {
Token tok_day = nmea_tokenizer_get(tzer,2);
Token tok_mon = nmea_tokenizer_get(tzer,3);
nmea_reader_update_cdate( r, tok_day, tok_mon, tok_year );
}
tok_time = nmea_tokenizer_get(tzer,1);
if (tok_time.p[0] != '\0') {
nmea_reader_update_time(r, tok_time);
}
}else {
tok.p -= 2;
D("unknown sentence '%.*s", tok.end-tok.p, tok.p);
}
if (r->fix.flags != 0) {
#if GPS_DEBUG
char temp[256];
char* p = temp;
char* end = p + sizeof(temp);
struct tm utc;
p += snprintf( p, end-p, "sending fix" );
if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
p += snprintf(p, end-p, " lat=%g lon=%g", r->fix.latitude, r->fix.longitude);
}
if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) {
p += snprintf(p, end-p, " altitude=%g", r->fix.altitude);
}
if (r->fix.flags & GPS_LOCATION_HAS_SPEED) {
p += snprintf(p, end-p, " speed=%g", r->fix.speed);
}
if (r->fix.flags & GPS_LOCATION_HAS_BEARING) {
p += snprintf(p, end-p, " bearing=%g", r->fix.bearing);
}
if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) {
p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy);
}
gmtime_r( (time_t*) &r->fix.timestamp, &utc );
p += snprintf(p, end-p, " time=%s", asctime( &utc ) );
D("%s",temp);
#endif
if (r->callback)
{
r->callback( &r->fix );
r->fix.flags = 0;
if (r->sv_status_changed != 0)
{
LOGD("gps sv status callback");
if (gps_state->callbacks.sv_status_cb)
{
//frank
LOGD("gps sv status callback---2");
gps_state->callbacks.sv_status_cb( &r->sv_status );
r->sv_status_changed = 0;
}
}
}
else {
D("no callback, keeping data until needed !");
}
}
}
static void
nmea_reader_addc( NmeaReader* r, int c )
{
if (r->overflow) {
r->overflow = (c != '\n');
return;
}
if (r->pos >= (int) sizeof(r->in)-1 ) {
r->overflow = 1;
r->pos = 0;
return;
}
r->in[r->pos] = (char)c;
r->pos += 1;
if (c == '\n') {
GPS_STATE_LOCK_FIX(gps_state);//--frank--2011-10-13
nmea_reader_parse( r );
GPS_STATE_UNLOCK_FIX(gps_state);//--frank--2011-10-13
r->pos = 0;
}
}
/*****************************************************************/
/*****************************************************************/
/***** *****/
/***** C O N N E C T I O N S T A T E *****/
/***** *****/
/*****************************************************************/
/*****************************************************************/
/* commands sent to the gps thread */
enum {
CMD_QUIT = 0,
CMD_START = 1,
CMD_STOP = 2
};
static void
gps_state_done( GpsState* s )
{
// tell the thread to quit, and wait for it
char cmd = CMD_QUIT;
int ret;
void* dummy;
D("gps send quit command");
do{
ret = write( s->control[0], &cmd, 1 );
}while(ret<0 && errno == EINTR);
pthread_join(s->thread, &dummy);
// close the control socket pair
close( s->control[0] ); s->control[0] = -1;
close( s->control[1] ); s->control[1] = -1;
// close connection to the QEMU GPS daemon
close( s->fd ); s->fd = -1;
sem_destroy(&s->fix_sem);
s->init = 0;
D("gps send quit end");
}
static void
gps_state_start( GpsState* s )
{
char cmd = CMD_START;
int ret;
do { ret=write( s->control[0], &cmd, 1 ); }
while (ret < 0 && errno == EINTR);
if (ret != 1)
D("%s: could not send CMD_START command: ret=%d: %s",
__FUNCTION__, ret, strerror(errno));
}
static void
gps_state_stop( GpsState* s )
{
char cmd = CMD_STOP;
int ret;
do { ret=write( s->control[0], &cmd, 1 ); }
while (ret < 0 && errno == EINTR);
if (ret != 1)
D("%s: could not send CMD_STOP command: ret=%d: %s",
__FUNCTION__, ret, strerror(errno));
}
static int
epoll_register( int epoll_fd, int fd )
{
struct epoll_event ev;
int ret, flags;
/* important: make the fd non-blocking */
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
ev.events = EPOLLIN;
ev.data.fd = fd;
do {
ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
} while (ret < 0 && errno == EINTR);
return ret;
}
static int
epoll_deregister( int epoll_fd, int fd )
{
int ret;
do {
ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
} while (ret < 0 && errno == EINTR);
return ret;
}
/* this is the main thread, it waits for commands from gps_state_start/stop and,
* when started, messages from the QEMU GPS daemon. these are simple NMEA sentences
* that must be parsed to be converted into GPS fixes sent to the framework
*/
static void*
gps_state_thread( void* arg )
{
GpsState* state = (GpsState*) arg;
NmeaReader reader[1];
int epoll_fd = epoll_create(2);
int started = 0;
int gps_fd = state->fd;
int control_fd = state->control[1];
nmea_reader_init( reader );
// register control file descriptors for polling
epoll_register( epoll_fd, control_fd );
epoll_register( epoll_fd, gps_fd );
D("gps thread running");
// now loop
for (;;) {
struct epoll_event events[2];
int ne, nevents;
nevents = epoll_wait( epoll_fd, events, 2, -1 );
if (nevents < 0) {
if (errno != EINTR)
LOGE("epoll_wait() unexpected error: %s", strerror(errno));
continue;
}
//D("gps thread received %d events", nevents);
for (ne = 0; ne < nevents; ne++) {
if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
LOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
goto Exit;
}
if ((events[ne].events & EPOLLIN) != 0) {
int fd = events[ne].data.fd;
if (fd == control_fd)
{
char cmd = 255;
int ret;
D("gps control fd event");
do {
ret = read( fd, &cmd, 1 );
} while (ret < 0 && errno == EINTR);
if (cmd == CMD_QUIT) {
D("gps thread quitting on demand");
goto Exit;
}
else if (cmd == CMD_START) {
if (!started) {
D("gps thread starting location_cb=%p", state->callbacks.location_cb);
started = 1;
nmea_reader_set_callback( reader, state->callbacks.location_cb );
if(state->callbacks.location_cb != NULL)
{
state->callbacks.location_cb(NULL);
}
/* char testbuf[100];
int nn;
sprintf(testbuf,"%s",GPS_TestData);
for (nn = 0; nn < strlen(testbuf); nn++)
{
// D("%02x ",buff[nn]);
nmea_reader_addc( reader, testbuf[nn] );
}*/
}
}
else if (cmd == CMD_STOP) {
if (started) {
D("gps thread stopping");
started = 0;
nmea_reader_set_callback( reader, NULL );
}
}
}
else if (fd == gps_fd)
{
char buff[32];
// D("gps fd event\n");
for (;;) {
int nn, ret;
ret = read( fd, buff, sizeof(buff) );
if (ret < 0) {
if (errno == EINTR)
continue;
if (errno != EWOULDBLOCK)
LOGE("error while reading from gps daemon socket: %s:", strerror(errno));
break;
}
//D("received %d bytes: %.*s", ret, ret, buff);
for (nn = 0; nn < ret; nn++)
{
// D("%02x ",buff[nn]);
nmea_reader_addc( reader, buff[nn] );
}
}
//D("\ngps fd event end\n");
}
else
{
LOGE("epoll_wait() returned unkown fd %d ?", fd);
}
}
}
}
Exit:
D("%s Exit\n", __FUNCTION__);
return NULL;
}
static void
gps_state_init( GpsState* state )
{
state->init = 1;
state->control[0] = -1;
state->control[1] = -1;
state->fd = -1;
D("%s: GPS hardware init !!\n", __FUNCTION__);
if (sem_init(&state->fix_sem, 0, 1) != 0) {
D("gps semaphore initialization failed! errno = %d", errno);
return;
}
//state->fd = qemud_channel_open(QEMU_CHANNEL_NAME);
state->fd = open(GPS_Serial_Name, O_RDONLY );//frank --2011.8.9
if (state->fd < 0) {
D("gps Open SerialPort fail\n");
state->init = 0;
return;
}
// disable echo on serial lines
if ( isatty( state->fd ) ) {
struct termios ios;
tcgetattr( state->fd, &ios );
ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */
ios.c_oflag &= (~ONLCR); /* Stop \n -> \r\n translation on output */
ios.c_iflag &= (~(ICRNL | INLCR)); /* Stop \r -> \n & \n -> \r translation on input */
ios.c_iflag |= (IGNCR | IXOFF); /* Ignore \r & XON/XOFF on input */
cfsetispeed(&ios, B38400);
cfsetospeed(&ios, B38400);
tcsetattr( state->fd, TCSANOW, &ios );
}
D("gps emulation will read from '%s' qemud channel", GPS_Serial_Name );
if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
LOGE("could not create thread control socket pair: %s", strerror(errno));
goto Fail;
}
/*
if ( pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0 ) {
LOGE("could not create gps thread: %s", strerror(errno));
goto Fail;
}
*/
state->thread = state->callbacks.create_thread_cb("qemu_gps", gps_state_thread, state);
D("gps state initialized");
return;
Fail:
gps_state_done( state );
}
/*****************************************************************/
/*****************************************************************/
/***** *****/
/***** I N T E R F A C E *****/
/***** *****/
/*****************************************************************/
/*****************************************************************/
static int
qemu_gps_init(GpsCallbacks* callbacks)
{
GpsState* s = _gps_state;
D("%s: GPS init !!\n", __FUNCTION__);
s->callbacks = *callbacks;
if (!s->init)
gps_state_init(s);
if (s->fd < 0)
return -1;
// s->callbacks = *callbacks;
return 0;
}
static void
qemu_gps_cleanup(void)
{
GpsState* s = _gps_state;
D("%s: GPS cleanup !!\n", __FUNCTION__);
if (s->init)
gps_state_done(s);
}
static int
qemu_gps_start()
{
GpsState* s = _gps_state;
D("%s: GPS start !!\n", __FUNCTION__);
if (!s->init) {
D("%s: called with uninitialized state %d!!", __FUNCTION__,s->init);
return -1;
}
D("%s: called", __FUNCTION__);
gps_state_start(s);
return 0;
}
static int
qemu_gps_stop()
{
GpsState* s = _gps_state;
D("%s: GPS stop !!\n", __FUNCTION__);
if (!s->init) {
D("%s: called with uninitialized state !!", __FUNCTION__);
return -1;
}
D("%s: called", __FUNCTION__);
gps_state_stop(s);
return 0;
}
static int
qemu_gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty)
{
D("%s \n", __FUNCTION__);
return 0;
}
static int
qemu_gps_inject_location(double latitude, double longitude, float accuracy)
{
D("%s \n", __FUNCTION__);
return 0;
}
static void
qemu_gps_delete_aiding_data(GpsAidingData flags)
{
D("%s \n", __FUNCTION__);
}
static int qemu_gps_set_position_mode(GpsPositionMode mode, int fix_frequency)
{
// FIXME - support fix_frequency
D("%s \n", __FUNCTION__);
return 0;
}
static const GpsInterface qemuGpsInterface = {
sizeof(GpsInterface),
qemu_gps_init,
qemu_gps_start,
qemu_gps_stop,
qemu_gps_cleanup,
qemu_gps_inject_time,
qemu_gps_inject_location,
qemu_gps_delete_aiding_data,
qemu_gps_set_position_mode,
qemu_gps_get_extension,
};
static const void*
qemu_gps_get_extension(const char* name)
{
// no extensions supported
D("%s : %s\n", __FUNCTION__,name);
return &qemuGpsInterface;
//return NULL;
}
const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev)
{
return &qemuGpsInterface;
}
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));
D("%s: GPS OPEN !!\n", __FUNCTION__);
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (struct hw_module_t*)module;
// dev->common.close = (int (*)(struct hw_device_t*))close_lights;
dev->get_gps_interface = gps__get_gps_interface;
*device = (struct hw_device_t*)dev;
return 0;
}
static struct hw_module_methods_t gps_module_methods = {
.open = open_gps
};
const struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = GPS_HARDWARE_MODULE_ID,
.name = "Goldfish GPS Module",
.author = "The Android Open Source Project",
.methods = &gps_module_methods,
};