10 #define LOG_TAG "gps_qemu"
11 #include <cutils/log.h>
12 #include <cutils/sockets.h>
13 #include <hardware_legacy/gps.h>
15 /* the name of the qemud-controlled socket */
16 #define QEMU_CHANNEL_NAME "gps"
21 # define D(...) LOGD(__VA_ARGS__)
23 # define D(...) ((void)0)
27 /*****************************************************************/
28 /*****************************************************************/
30 /***** N M E A T O K E N I Z E R *****/
32 /*****************************************************************/
33 /*****************************************************************/
40 #define MAX_NMEA_TOKENS 16
44 Token tokens[ MAX_NMEA_TOKENS ];
48 nmea_tokenizer_init( NmeaTokenizer* t, const char* p, const char* end )
53 // the initial '$' is optional
54 if (p < end && p[0] == '$')
57 // remove trailing newline
58 if (end > p && end[-1] == '\n') {
60 if (end > p && end[-1] == '\r')
64 // get rid of checksum at the end of the sentecne
65 if (end >= p+3 && end[-3] == '*') {
72 q = memchr(p, ',', end-p);
77 if (count < MAX_NMEA_TOKENS) {
78 t->tokens[count].p = p;
79 t->tokens[count].end = q;
94 nmea_tokenizer_get( NmeaTokenizer* t, int index )
97 static const char* dummy = "";
99 if (index < 0 || index >= t->count) {
100 tok.p = tok.end = dummy;
102 tok = t->tokens[index];
109 str2int( const char* p, const char* end )
114 for ( ; len > 0; len--, p++ )
122 if ((unsigned)c >= 10)
125 result = result*10 + c;
134 str2float( const char* p, const char* end )
140 if (len >= (int)sizeof(temp))
143 memcpy( temp, p, len );
145 return strtod( temp, NULL );
148 /*****************************************************************/
149 /*****************************************************************/
151 /***** N M E A P A R S E R *****/
153 /*****************************************************************/
154 /*****************************************************************/
156 #define NMEA_MAX_SIZE 83
166 gps_location_callback callback;
167 char in[ NMEA_MAX_SIZE+1 ];
172 nmea_reader_update_utc_diff( NmeaReader* r )
174 time_t now = time(NULL);
177 long time_local, time_utc;
179 gmtime_r( &now, &tm_utc );
180 localtime_r( &now, &tm_local );
182 time_local = tm_local.tm_sec +
183 60*(tm_local.tm_min +
184 60*(tm_local.tm_hour +
185 24*(tm_local.tm_yday +
186 365*tm_local.tm_year)));
188 time_utc = tm_utc.tm_sec +
192 365*tm_utc.tm_year)));
194 r->utc_diff = time_utc - time_local;
199 nmea_reader_init( NmeaReader* r )
201 memset( r, 0, sizeof(*r) );
210 nmea_reader_update_utc_diff( r );
215 nmea_reader_set_callback( NmeaReader* r, gps_location_callback cb )
218 if (cb != NULL && r->fix.flags != 0) {
219 D("%s: sending latest fix to new callback", __FUNCTION__);
220 r->callback( &r->fix );
227 nmea_reader_update_time( NmeaReader* r, Token tok )
234 if (tok.p + 6 > tok.end)
237 if (r->utc_year < 0) {
238 // no date yet, get current one
239 time_t now = time(NULL);
240 gmtime_r( &now, &tm );
241 r->utc_year = tm.tm_year + 1900;
242 r->utc_mon = tm.tm_mon + 1;
243 r->utc_day = tm.tm_mday;
246 hour = str2int(tok.p, tok.p+2);
247 minute = str2int(tok.p+2, tok.p+4);
248 seconds = str2float(tok.p+4, tok.end);
252 tm.tm_sec = (int) seconds;
253 tm.tm_year = r->utc_year - 1900;
254 tm.tm_mon = r->utc_mon - 1;
255 tm.tm_mday = r->utc_day;
257 fix_time = mktime( &tm ) + r->utc_diff;
258 r->fix.timestamp = (long long)fix_time * 1000;
263 nmea_reader_update_date( NmeaReader* r, Token date, Token time )
268 if (tok.p + 6 != tok.end) {
269 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
272 day = str2int(tok.p, tok.p+2);
273 mon = str2int(tok.p+2, tok.p+4);
274 year = str2int(tok.p+4, tok.p+6) + 2000;
276 if ((day|mon|year) < 0) {
277 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
285 return nmea_reader_update_time( r, time );
290 convert_from_hhmm( Token tok )
292 double val = str2float(tok.p, tok.end);
293 int degrees = (int)(floor(val) / 100);
294 double minutes = val - degrees*100.;
295 double dcoord = degrees + minutes / 60.0;
301 nmea_reader_update_latlong( NmeaReader* r,
311 if (tok.p + 6 > tok.end) {
312 D("latitude is too short: '%.*s'", tok.end-tok.p, tok.p);
315 lat = convert_from_hhmm(tok);
316 if (latitudeHemi == 'S')
320 if (tok.p + 6 > tok.end) {
321 D("longitude is too short: '%.*s'", tok.end-tok.p, tok.p);
324 lon = convert_from_hhmm(tok);
325 if (longitudeHemi == 'W')
328 r->fix.flags |= GPS_LOCATION_HAS_LAT_LONG;
329 r->fix.latitude = lat;
330 r->fix.longitude = lon;
336 nmea_reader_update_altitude( NmeaReader* r,
341 Token tok = altitude;
343 if (tok.p >= tok.end)
346 r->fix.flags |= GPS_LOCATION_HAS_ALTITUDE;
347 r->fix.altitude = str2float(tok.p, tok.end);
353 nmea_reader_update_bearing( NmeaReader* r,
359 if (tok.p >= tok.end)
362 r->fix.flags |= GPS_LOCATION_HAS_BEARING;
363 r->fix.bearing = str2float(tok.p, tok.end);
369 nmea_reader_update_speed( NmeaReader* r,
375 if (tok.p >= tok.end)
378 r->fix.flags |= GPS_LOCATION_HAS_SPEED;
379 r->fix.speed = str2float(tok.p, tok.end);
385 nmea_reader_parse( NmeaReader* r )
387 /* we received a complete sentence, now parse it to generate
390 NmeaTokenizer tzer[1];
393 D("Received: '%.*s'", r->pos, r->in);
395 D("Too short. discarded.");
399 nmea_tokenizer_init(tzer, r->in, r->in + r->pos);
403 D("Found %d tokens", tzer->count);
404 for (n = 0; n < tzer->count; n++) {
405 Token tok = nmea_tokenizer_get(tzer,n);
406 D("%2d: '%.*s'", n, tok.end-tok.p, tok.p);
411 tok = nmea_tokenizer_get(tzer, 0);
412 if (tok.p + 5 > tok.end) {
413 D("sentence id '%.*s' too short, ignored.", tok.end-tok.p, tok.p);
417 // ignore first two characters.
419 if ( !memcmp(tok.p, "GGA", 3) ) {
421 Token tok_time = nmea_tokenizer_get(tzer,1);
422 Token tok_latitude = nmea_tokenizer_get(tzer,2);
423 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,3);
424 Token tok_longitude = nmea_tokenizer_get(tzer,4);
425 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,5);
426 Token tok_altitude = nmea_tokenizer_get(tzer,9);
427 Token tok_altitudeUnits = nmea_tokenizer_get(tzer,10);
429 nmea_reader_update_time(r, tok_time);
430 nmea_reader_update_latlong(r, tok_latitude,
431 tok_latitudeHemi.p[0],
433 tok_longitudeHemi.p[0]);
434 nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits);
436 } else if ( !memcmp(tok.p, "GSA", 3) ) {
438 } else if ( !memcmp(tok.p, "RMC", 3) ) {
439 Token tok_time = nmea_tokenizer_get(tzer,1);
440 Token tok_fixStatus = nmea_tokenizer_get(tzer,2);
441 Token tok_latitude = nmea_tokenizer_get(tzer,3);
442 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,4);
443 Token tok_longitude = nmea_tokenizer_get(tzer,5);
444 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,6);
445 Token tok_speed = nmea_tokenizer_get(tzer,7);
446 Token tok_bearing = nmea_tokenizer_get(tzer,8);
447 Token tok_date = nmea_tokenizer_get(tzer,9);
449 D("in RMC, fixStatus=%c", tok_fixStatus.p[0]);
450 if (tok_fixStatus.p[0] == 'A')
452 nmea_reader_update_date( r, tok_date, tok_time );
454 nmea_reader_update_latlong( r, tok_latitude,
455 tok_latitudeHemi.p[0],
457 tok_longitudeHemi.p[0] );
459 nmea_reader_update_bearing( r, tok_bearing );
460 nmea_reader_update_speed ( r, tok_speed );
464 D("unknown sentence '%.*s", tok.end-tok.p, tok.p);
466 if (r->fix.flags != 0) {
470 char* end = p + sizeof(temp);
473 p += snprintf( p, end-p, "sending fix" );
474 if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
475 p += snprintf(p, end-p, " lat=%g lon=%g", r->fix.latitude, r->fix.longitude);
477 if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) {
478 p += snprintf(p, end-p, " altitude=%g", r->fix.altitude);
480 if (r->fix.flags & GPS_LOCATION_HAS_SPEED) {
481 p += snprintf(p, end-p, " speed=%g", r->fix.speed);
483 if (r->fix.flags & GPS_LOCATION_HAS_BEARING) {
484 p += snprintf(p, end-p, " bearing=%g", r->fix.bearing);
486 if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) {
487 p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy);
489 gmtime_r( (time_t*) &r->fix.timestamp, &utc );
490 p += snprintf(p, end-p, " time=%s", asctime( &utc ) );
494 r->callback( &r->fix );
498 D("no callback, keeping data until needed !");
505 nmea_reader_addc( NmeaReader* r, int c )
508 r->overflow = (c != '\n');
512 if (r->pos >= (int) sizeof(r->in)-1 ) {
518 r->in[r->pos] = (char)c;
522 nmea_reader_parse( r );
528 /*****************************************************************/
529 /*****************************************************************/
531 /***** C O N N E C T I O N S T A T E *****/
533 /*****************************************************************/
534 /*****************************************************************/
536 /* commands sent to the gps thread */
544 /* this is the state of our connection to the qemu_gpsd daemon */
548 GpsCallbacks callbacks;
554 static GpsState _gps_state[1];
558 gps_state_done( GpsState* s )
560 // tell the thread to quit, and wait for it
563 write( s->control[0], &cmd, 1 );
564 pthread_join(s->thread, &dummy);
566 // close the control socket pair
567 close( s->control[0] ); s->control[0] = -1;
568 close( s->control[1] ); s->control[1] = -1;
570 // close connection to the QEMU GPS daemon
571 close( s->fd ); s->fd = -1;
577 gps_state_start( GpsState* s )
579 char cmd = CMD_START;
582 do { ret=write( s->control[0], &cmd, 1 ); }
583 while (ret < 0 && errno == EINTR);
586 D("%s: could not send CMD_START command: ret=%d: %s",
587 __FUNCTION__, ret, strerror(errno));
592 gps_state_stop( GpsState* s )
597 do { ret=write( s->control[0], &cmd, 1 ); }
598 while (ret < 0 && errno == EINTR);
601 D("%s: could not send CMD_STOP command: ret=%d: %s",
602 __FUNCTION__, ret, strerror(errno));
607 epoll_register( int epoll_fd, int fd )
609 struct epoll_event ev;
612 /* important: make the fd non-blocking */
613 flags = fcntl(fd, F_GETFL);
614 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
619 ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
620 } while (ret < 0 && errno == EINTR);
626 epoll_deregister( int epoll_fd, int fd )
630 ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
631 } while (ret < 0 && errno == EINTR);
635 /* this is the main thread, it waits for commands from gps_state_start/stop and,
636 * when started, messages from the QEMU GPS daemon. these are simple NMEA sentences
637 * that must be parsed to be converted into GPS fixes sent to the framework
640 gps_state_thread( void* arg )
642 GpsState* state = (GpsState*) arg;
643 NmeaReader reader[1];
644 int epoll_fd = epoll_create(2);
646 int gps_fd = state->fd;
647 int control_fd = state->control[1];
649 nmea_reader_init( reader );
651 // register control file descriptors for polling
652 epoll_register( epoll_fd, control_fd );
653 epoll_register( epoll_fd, gps_fd );
655 D("gps thread running");
659 struct epoll_event events[2];
662 nevents = epoll_wait( epoll_fd, events, 2, -1 );
665 LOGE("epoll_wait() unexpected error: %s", strerror(errno));
668 D("gps thread received %d events", nevents);
669 for (ne = 0; ne < nevents; ne++) {
670 if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
671 LOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
674 if ((events[ne].events & EPOLLIN) != 0) {
675 int fd = events[ne].data.fd;
677 if (fd == control_fd)
681 D("gps control fd event");
683 ret = read( fd, &cmd, 1 );
684 } while (ret < 0 && errno == EINTR);
686 if (cmd == CMD_QUIT) {
687 D("gps thread quitting on demand");
690 else if (cmd == CMD_START) {
692 D("gps thread starting location_cb=%p", state->callbacks.location_cb);
694 nmea_reader_set_callback( reader, state->callbacks.location_cb );
697 else if (cmd == CMD_STOP) {
699 D("gps thread stopping");
701 nmea_reader_set_callback( reader, NULL );
705 else if (fd == gps_fd)
712 ret = read( fd, buff, sizeof(buff) );
716 if (errno != EWOULDBLOCK)
717 LOGE("error while reading from gps daemon socket: %s:", strerror(errno));
720 D("received %d bytes: %.*s", ret, ret, buff);
721 for (nn = 0; nn < ret; nn++)
722 nmea_reader_addc( reader, buff[nn] );
724 D("gps fd event end");
728 LOGE("epoll_wait() returned unkown fd %d ?", fd);
739 gps_state_init( GpsState* state )
742 state->control[0] = -1;
743 state->control[1] = -1;
746 state->fd = qemu_channel_open( &state->channel,
751 D("no gps emulation detected");
755 D("gps emulation will read from '%s' qemud channel", QEMU_CHANNEL_NAME );
757 if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
758 LOGE("could not create thread control socket pair: %s", strerror(errno));
762 if ( pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0 ) {
763 LOGE("could not create gps thread: %s", strerror(errno));
767 D("gps state initialized");
771 gps_state_done( state );
775 /*****************************************************************/
776 /*****************************************************************/
778 /***** I N T E R F A C E *****/
780 /*****************************************************************/
781 /*****************************************************************/
785 qemu_gps_init(GpsCallbacks* callbacks)
787 GpsState* s = _gps_state;
795 s->callbacks = *callbacks;
801 qemu_gps_cleanup(void)
803 GpsState* s = _gps_state;
813 GpsState* s = _gps_state;
816 D("%s: called with uninitialized state !!", __FUNCTION__);
820 D("%s: called", __FUNCTION__);
829 GpsState* s = _gps_state;
832 D("%s: called with uninitialized state !!", __FUNCTION__);
836 D("%s: called", __FUNCTION__);
843 qemu_gps_set_fix_frequency()
845 GpsState* s = _gps_state;
848 D("%s: called with uninitialized state !!", __FUNCTION__);
852 D("%s: called", __FUNCTION__);
853 // FIXME - support fix_frequency
857 qemu_gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty)
863 qemu_gps_delete_aiding_data(GpsAidingData flags)
867 static int qemu_gps_set_position_mode(GpsPositionMode mode, int fix_frequency)
869 // FIXME - support fix_frequency
874 qemu_gps_get_extension(const char* name)
879 static const GpsInterface qemuGpsInterface = {
883 qemu_gps_set_fix_frequency,
885 qemu_gps_inject_time,
886 qemu_gps_delete_aiding_data,
887 qemu_gps_set_position_mode,
888 qemu_gps_get_extension,
891 const GpsInterface* gps_get_qemu_interface()
893 return &qemuGpsInterface;