blob: 67737c18c838e74b7cc9314c76ba67c7a12f0700 [file] [log] [blame]
The Android Open Source Projectd6054a32008-10-21 07:00:00 -07001
2#include <errno.h>
3#include <pthread.h>
4#include <termios.h>
5#include <fcntl.h>
6#include <sys/epoll.h>
7#include <math.h>
8#include <time.h>
9
10#define LOG_TAG "gps_qemu"
11#include <cutils/log.h>
12#include <cutils/sockets.h>
13#include <cutils/properties.h>
14#include <hardware/gps.h>
15
16/* the name of the qemud-controlled socket */
17#define QEMUD_SOCKET_NAME "qemud_gps"
18
19#define GPS_DEBUG 0
20
21#if GPS_DEBUG
22# define D(...) LOGD(__VA_ARGS__)
23#else
24# define D(...) ((void)0)
25#endif
26
27
28/*****************************************************************/
29/*****************************************************************/
30/***** *****/
31/***** N M E A T O K E N I Z E R *****/
32/***** *****/
33/*****************************************************************/
34/*****************************************************************/
35
36typedef struct {
37 const char* p;
38 const char* end;
39} Token;
40
41#define MAX_NMEA_TOKENS 16
42
43typedef struct {
44 int count;
45 Token tokens[ MAX_NMEA_TOKENS ];
46} NmeaTokenizer;
47
48static int
49nmea_tokenizer_init( NmeaTokenizer* t, const char* p, const char* end )
50{
51 int count = 0;
52 char* q;
53
54 // the initial '$' is optional
55 if (p < end && p[0] == '$')
56 p += 1;
57
58 // remove trailing newline
59 if (end > p && end[-1] == '\n') {
60 end -= 1;
61 if (end > p && end[-1] == '\r')
62 end -= 1;
63 }
64
65 // get rid of checksum at the end of the sentecne
66 if (end >= p+3 && end[-3] == '*') {
67 end -= 3;
68 }
69
70 while (p < end) {
71 const char* q = p;
72
73 q = memchr(p, ',', end-p);
74 if (q == NULL)
75 q = end;
76
77 if (q > p) {
78 if (count < MAX_NMEA_TOKENS) {
79 t->tokens[count].p = p;
80 t->tokens[count].end = q;
81 count += 1;
82 }
83 }
84 if (q < end)
85 q += 1;
86
87 p = q;
88 }
89
90 t->count = count;
91 return count;
92}
93
94static Token
95nmea_tokenizer_get( NmeaTokenizer* t, int index )
96{
97 Token tok;
98 static const char* dummy = "";
99
100 if (index < 0 || index >= t->count) {
101 tok.p = tok.end = dummy;
102 } else
103 tok = t->tokens[index];
104
105 return tok;
106}
107
108
109static int
110str2int( const char* p, const char* end )
111{
112 int result = 0;
113 int len = end - p;
114
115 for ( ; len > 0; len--, p++ )
116 {
117 int c;
118
119 if (p >= end)
120 goto Fail;
121
122 c = *p - '0';
123 if ((unsigned)c >= 10)
124 goto Fail;
125
126 result = result*10 + c;
127 }
128 return result;
129
130Fail:
131 return -1;
132}
133
134static double
135str2float( const char* p, const char* end )
136{
137 int result = 0;
138 int len = end - p;
139 char temp[16];
140
141 if (len >= (int)sizeof(temp))
142 return 0.;
143
144 memcpy( temp, p, len );
145 temp[len] = 0;
146 return strtod( temp, NULL );
147}
148
149/*****************************************************************/
150/*****************************************************************/
151/***** *****/
152/***** N M E A P A R S E R *****/
153/***** *****/
154/*****************************************************************/
155/*****************************************************************/
156
157#define NMEA_MAX_SIZE 83
158
159typedef struct {
160 int pos;
161 int overflow;
162 int utc_year;
163 int utc_mon;
164 int utc_day;
165 int utc_diff;
166 GpsLocation fix;
167 gps_location_callback callback;
168 char in[ NMEA_MAX_SIZE+1 ];
169} NmeaReader;
170
171
172static void
173nmea_reader_update_utc_diff( NmeaReader* r )
174{
175 time_t now = time(NULL);
176 struct tm tm_local;
177 struct tm tm_utc;
178 long time_local, time_utc;
179
180 gmtime_r( &now, &tm_utc );
181 localtime_r( &now, &tm_local );
182
183 time_local = tm_local.tm_sec +
184 60*(tm_local.tm_min +
185 60*(tm_local.tm_hour +
186 24*(tm_local.tm_yday +
187 365*tm_local.tm_year)));
188
189 time_utc = tm_utc.tm_sec +
190 60*(tm_utc.tm_min +
191 60*(tm_utc.tm_hour +
192 24*(tm_utc.tm_yday +
193 365*tm_utc.tm_year)));
194
195 r->utc_diff = time_utc - time_local;
196}
197
198
199static void
200nmea_reader_init( NmeaReader* r )
201{
202 memset( r, 0, sizeof(*r) );
203
204 r->pos = 0;
205 r->overflow = 0;
206 r->utc_year = -1;
207 r->utc_mon = -1;
208 r->utc_day = -1;
209 r->callback = NULL;
210
211 nmea_reader_update_utc_diff( r );
212}
213
214
215static void
216nmea_reader_set_callback( NmeaReader* r, gps_location_callback cb )
217{
218 r->callback = cb;
219 if (cb != NULL && r->fix.flags != 0) {
220 D("%s: sending latest fix to new callback", __FUNCTION__);
221 r->callback( &r->fix );
222 r->fix.flags = 0;
223 }
224}
225
226
227static int
228nmea_reader_update_time( NmeaReader* r, Token tok )
229{
230 int hour, minute;
231 double seconds;
232 struct tm tm;
233 time_t fix_time;
234
235 if (tok.p + 6 > tok.end)
236 return -1;
237
238 if (r->utc_year < 0) {
239 // no date yet, get current one
240 time_t now = time(NULL);
241 gmtime_r( &now, &tm );
242 r->utc_year = tm.tm_year + 1900;
243 r->utc_mon = tm.tm_mon + 1;
244 r->utc_day = tm.tm_mday;
245 }
246
247 hour = str2int(tok.p, tok.p+2);
248 minute = str2int(tok.p+2, tok.p+4);
249 seconds = str2float(tok.p+4, tok.end);
250
251 tm.tm_hour = hour;
252 tm.tm_min = minute;
253 tm.tm_sec = (int) seconds;
254 tm.tm_year = r->utc_year - 1900;
255 tm.tm_mon = r->utc_mon - 1;
256 tm.tm_mday = r->utc_day;
257
258 fix_time = mktime( &tm ) + r->utc_diff;
259 r->fix.timestamp = (long long)fix_time * 1000;
260 return 0;
261}
262
263static int
264nmea_reader_update_date( NmeaReader* r, Token date, Token time )
265{
266 Token tok = date;
267 int day, mon, year;
268
269 if (tok.p + 6 != tok.end) {
270 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
271 return -1;
272 }
273 day = str2int(tok.p, tok.p+2);
274 mon = str2int(tok.p+2, tok.p+4);
275 year = str2int(tok.p+4, tok.p+6) + 2000;
276
277 if ((day|mon|year) < 0) {
278 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
279 return -1;
280 }
281
282 r->utc_year = year;
283 r->utc_mon = mon;
284 r->utc_day = day;
285
286 return nmea_reader_update_time( r, time );
287}
288
289
290static double
291convert_from_hhmm( Token tok )
292{
293 double val = str2float(tok.p, tok.end);
294 int degrees = (int)(floor(val) / 100);
295 double minutes = val - degrees*100.;
296 double dcoord = degrees + minutes / 60.0;
297 return dcoord;
298}
299
300
301static int
302nmea_reader_update_latlong( NmeaReader* r,
303 Token latitude,
304 char latitudeHemi,
305 Token longitude,
306 char longitudeHemi )
307{
308 double lat, lon;
309 Token tok;
310
311 tok = latitude;
312 if (tok.p + 6 > tok.end) {
313 D("latitude is too short: '%.*s'", tok.end-tok.p, tok.p);
314 return -1;
315 }
316 lat = convert_from_hhmm(tok);
317 if (latitudeHemi == 'S')
318 lat = -lat;
319
320 tok = longitude;
321 if (tok.p + 6 > tok.end) {
322 D("longitude is too short: '%.*s'", tok.end-tok.p, tok.p);
323 return -1;
324 }
325 lon = convert_from_hhmm(tok);
326 if (longitudeHemi == 'W')
327 lon = -lon;
328
329 r->fix.flags |= GPS_LOCATION_HAS_LAT_LONG;
330 r->fix.latitude = lat;
331 r->fix.longitude = lon;
332 return 0;
333}
334
335
336static int
337nmea_reader_update_altitude( NmeaReader* r,
338 Token altitude,
339 Token units )
340{
341 double alt;
342 Token tok = altitude;
343
344 if (tok.p >= tok.end)
345 return -1;
346
347 r->fix.flags |= GPS_LOCATION_HAS_ALTITUDE;
348 r->fix.altitude = str2float(tok.p, tok.end);
349 return 0;
350}
351
352
353static int
354nmea_reader_update_bearing( NmeaReader* r,
355 Token bearing )
356{
357 double alt;
358 Token tok = bearing;
359
360 if (tok.p >= tok.end)
361 return -1;
362
363 r->fix.flags |= GPS_LOCATION_HAS_BEARING;
364 r->fix.bearing = str2float(tok.p, tok.end);
365 return 0;
366}
367
368
369static int
370nmea_reader_update_speed( NmeaReader* r,
371 Token speed )
372{
373 double alt;
374 Token tok = speed;
375
376 if (tok.p >= tok.end)
377 return -1;
378
379 r->fix.flags |= GPS_LOCATION_HAS_SPEED;
380 r->fix.speed = str2float(tok.p, tok.end);
381 return 0;
382}
383
384
385static void
386nmea_reader_parse( NmeaReader* r )
387{
388 /* we received a complete sentence, now parse it to generate
389 * a new GPS fix...
390 */
391 NmeaTokenizer tzer[1];
392 Token tok;
393
394 D("Received: '%.*s'", r->pos, r->in);
395 if (r->pos < 9) {
396 D("Too short. discarded.");
397 return;
398 }
399
400 nmea_tokenizer_init(tzer, r->in, r->in + r->pos);
401#if GPS_DEBUG
402 {
403 int n;
404 D("Found %d tokens", tzer->count);
405 for (n = 0; n < tzer->count; n++) {
406 Token tok = nmea_tokenizer_get(tzer,n);
407 D("%2d: '%.*s'", n, tok.end-tok.p, tok.p);
408 }
409 }
410#endif
411
412 tok = nmea_tokenizer_get(tzer, 0);
413 if (tok.p + 5 > tok.end) {
414 D("sentence id '%.*s' too short, ignored.", tok.end-tok.p, tok.p);
415 return;
416 }
417
418 // ignore first two characters.
419 tok.p += 2;
420 if ( !memcmp(tok.p, "GGA", 3) ) {
421 // GPS fix
422 Token tok_time = nmea_tokenizer_get(tzer,1);
423 Token tok_latitude = nmea_tokenizer_get(tzer,2);
424 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,3);
425 Token tok_longitude = nmea_tokenizer_get(tzer,4);
426 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,5);
427 Token tok_altitude = nmea_tokenizer_get(tzer,9);
428 Token tok_altitudeUnits = nmea_tokenizer_get(tzer,10);
429
430 nmea_reader_update_time(r, tok_time);
431 nmea_reader_update_latlong(r, tok_latitude,
432 tok_latitudeHemi.p[0],
433 tok_longitude,
434 tok_longitudeHemi.p[0]);
435 nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits);
436
437 } else if ( !memcmp(tok.p, "GSA", 3) ) {
438 // do something ?
439 } else if ( !memcmp(tok.p, "RMC", 3) ) {
440 Token tok_time = nmea_tokenizer_get(tzer,1);
441 Token tok_fixStatus = nmea_tokenizer_get(tzer,2);
442 Token tok_latitude = nmea_tokenizer_get(tzer,3);
443 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,4);
444 Token tok_longitude = nmea_tokenizer_get(tzer,5);
445 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,6);
446 Token tok_speed = nmea_tokenizer_get(tzer,7);
447 Token tok_bearing = nmea_tokenizer_get(tzer,8);
448 Token tok_date = nmea_tokenizer_get(tzer,9);
449
450 D("in RMC, fixStatus=%c", tok_fixStatus.p[0]);
451 if (tok_fixStatus.p[0] == 'A')
452 {
453 nmea_reader_update_date( r, tok_date, tok_time );
454
455 nmea_reader_update_latlong( r, tok_latitude,
456 tok_latitudeHemi.p[0],
457 tok_longitude,
458 tok_longitudeHemi.p[0] );
459
460 nmea_reader_update_bearing( r, tok_bearing );
461 nmea_reader_update_speed ( r, tok_speed );
462 }
463 } else {
464 tok.p -= 2;
465 D("unknown sentence '%.*s", tok.end-tok.p, tok.p);
466 }
467 if (r->fix.flags != 0) {
468#if GPS_DEBUG
469 char temp[256];
470 char* p = temp;
471 char* end = p + sizeof(temp);
472 struct tm utc;
473
474 p += snprintf( p, end-p, "sending fix" );
475 if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
476 p += snprintf(p, end-p, " lat=%g lon=%g", r->fix.latitude, r->fix.longitude);
477 }
478 if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) {
479 p += snprintf(p, end-p, " altitude=%g", r->fix.altitude);
480 }
481 if (r->fix.flags & GPS_LOCATION_HAS_SPEED) {
482 p += snprintf(p, end-p, " speed=%g", r->fix.speed);
483 }
484 if (r->fix.flags & GPS_LOCATION_HAS_BEARING) {
485 p += snprintf(p, end-p, " bearing=%g", r->fix.bearing);
486 }
487 if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) {
488 p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy);
489 }
490 gmtime_r( (time_t*) &r->fix.timestamp, &utc );
491 p += snprintf(p, end-p, " time=%s", asctime( &utc ) );
492 D(temp);
493#endif
494 if (r->callback) {
495 r->callback( &r->fix );
496 r->fix.flags = 0;
497 }
498 else {
499 D("no callback, keeping data until needed !");
500 }
501 }
502}
503
504
505static void
506nmea_reader_addc( NmeaReader* r, int c )
507{
508 if (r->overflow) {
509 r->overflow = (c != '\n');
510 return;
511 }
512
513 if (r->pos >= (int) sizeof(r->in)-1 ) {
514 r->overflow = 1;
515 r->pos = 0;
516 return;
517 }
518
519 r->in[r->pos] = (char)c;
520 r->pos += 1;
521
522 if (c == '\n') {
523 nmea_reader_parse( r );
524 r->pos = 0;
525 }
526}
527
528
529/*****************************************************************/
530/*****************************************************************/
531/***** *****/
532/***** C O N N E C T I O N S T A T E *****/
533/***** *****/
534/*****************************************************************/
535/*****************************************************************/
536
537/* commands sent to the gps thread */
538enum {
539 CMD_QUIT = 0,
540 CMD_START = 1,
541 CMD_STOP = 2
542};
543
544
545/* this is the state of our connection to the qemu_gpsd daemon */
546typedef struct {
547 int init;
548 int fd;
549 GpsCallbacks callbacks;
550 pthread_t thread;
551 int control[2];
552
553} GpsState;
554
555static GpsState _gps_state[1];
556
557
558static void
559gps_state_done( GpsState* s )
560{
561 // tell the thread to quit, and wait for it
562 char cmd = CMD_QUIT;
563 void* dummy;
564 write( s->control[0], &cmd, 1 );
565 pthread_join(s->thread, &dummy);
566
567 // close the control socket pair
568 close( s->control[0] ); s->control[0] = -1;
569 close( s->control[1] ); s->control[1] = -1;
570
571 // close connection to the QEMU GPS daemon
572 close( s->fd ); s->fd = -1;
573 s->init = 0;
574}
575
576
577static void
578gps_state_start( GpsState* s )
579{
580 char cmd = CMD_START;
581 int ret;
582
583 do { ret=write( s->control[0], &cmd, 1 ); }
584 while (ret < 0 && errno == EINTR);
585
586 if (ret != 1)
587 D("%s: could not send CMD_START command: ret=%d: %s",
588 __FUNCTION__, ret, strerror(errno));
589}
590
591
592static void
593gps_state_stop( GpsState* s )
594{
595 char cmd = CMD_STOP;
596 int ret;
597
598 do { ret=write( s->control[0], &cmd, 1 ); }
599 while (ret < 0 && errno == EINTR);
600
601 if (ret != 1)
602 D("%s: could not send CMD_STOP command: ret=%d: %s",
603 __FUNCTION__, ret, strerror(errno));
604}
605
606
607static int
608epoll_register( int epoll_fd, int fd )
609{
610 struct epoll_event ev;
611 int ret, flags;
612
613 /* important: make the fd non-blocking */
614 flags = fcntl(fd, F_GETFL);
615 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
616
617 ev.events = EPOLLIN;
618 ev.data.fd = fd;
619 do {
620 ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
621 } while (ret < 0 && errno == EINTR);
622 return ret;
623}
624
625
626static int
627epoll_deregister( int epoll_fd, int fd )
628{
629 int ret;
630 do {
631 ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
632 } while (ret < 0 && errno == EINTR);
633 return ret;
634}
635
636/* this is the main thread, it waits for commands from gps_state_start/stop and,
637 * when started, messages from the QEMU GPS daemon. these are simple NMEA sentences
638 * that must be parsed to be converted into GPS fixes sent to the framework
639 */
640static void*
641gps_state_thread( void* arg )
642{
643 GpsState* state = (GpsState*) arg;
644 NmeaReader reader[1];
645 int epoll_fd = epoll_create(2);
646 int started = 0;
647 int gps_fd = state->fd;
648 int control_fd = state->control[1];
649
650 nmea_reader_init( reader );
651
652 // register control file descriptors for polling
653 epoll_register( epoll_fd, control_fd );
654 epoll_register( epoll_fd, gps_fd );
655
656 D("gps thread running");
657
658 // now loop
659 for (;;) {
660 struct epoll_event events[2];
661 int ne, nevents;
662
663 nevents = epoll_wait( epoll_fd, events, 2, -1 );
664 if (nevents < 0) {
665 if (errno != EINTR)
666 LOGE("epoll_wait() unexpected error: %s", strerror(errno));
667 continue;
668 }
669 D("gps thread received %d events", nevents);
670 for (ne = 0; ne < nevents; ne++) {
671 if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
672 LOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
673 goto Exit;
674 }
675 if ((events[ne].events & EPOLLIN) != 0) {
676 int fd = events[ne].data.fd;
677
678 if (fd == control_fd)
679 {
680 char cmd = 255;
681 int ret;
682 D("gps control fd event");
683 do {
684 ret = read( fd, &cmd, 1 );
685 } while (ret < 0 && errno == EINTR);
686
687 if (cmd == CMD_QUIT) {
688 D("gps thread quitting on demand");
689 goto Exit;
690 }
691 else if (cmd == CMD_START) {
692 if (!started) {
693 D("gps thread starting location_cb=%p", state->callbacks.location_cb);
694 started = 1;
695 nmea_reader_set_callback( reader, state->callbacks.location_cb );
696 }
697 }
698 else if (cmd == CMD_STOP) {
699 if (started) {
700 D("gps thread stopping");
701 started = 0;
702 nmea_reader_set_callback( reader, NULL );
703 }
704 }
705 }
706 else if (fd == gps_fd)
707 {
708 char buff[32];
709 D("gps fd event");
710 for (;;) {
711 int nn, ret;
712
713 ret = read( fd, buff, sizeof(buff) );
714 if (ret < 0) {
715 if (errno == EINTR)
716 continue;
717 if (errno != EWOULDBLOCK)
718 LOGE("error while reading from gps daemon socket: %s:", strerror(errno));
719 break;
720 }
721 D("received %d bytes: %.*s", ret, ret, buff);
722 for (nn = 0; nn < ret; nn++)
723 nmea_reader_addc( reader, buff[nn] );
724 }
725 D("gps fd event end");
726 }
727 else
728 {
729 LOGE("epoll_wait() returned unkown fd %d ?", fd);
730 }
731 }
732 }
733 }
734Exit:
735 return NULL;
736}
737
738
739static void
740gps_state_init( GpsState* state )
741{
742 char prop[PROPERTY_VALUE_MAX];
743 char device[256];
744 int ret;
745 int done = 0;
746
747 state->init = 1;
748 state->control[0] = -1;
749 state->control[1] = -1;
750 state->fd = -1;
751
752 // try to connect to the qemud socket
753 do {
754 state->fd = socket_local_client( QEMUD_SOCKET_NAME,
755 ANDROID_SOCKET_NAMESPACE_RESERVED,
756 SOCK_STREAM );
757 if (state->fd < 0) {
758 D("no '%s' control socket available: %s", QEMUD_SOCKET_NAME, strerror(errno));
759 break;
760 }
761 snprintf( device, sizeof(device), "/dev/socket/%s", QEMUD_SOCKET_NAME );
762 done = 1;
763
764 } while (0);
765
766 // otherwise, look for a kernel-provided device name
767 if (!done) do {
768 if (property_get("ro.kernel.android.gps",prop,"") == 0) {
769 D("no kernel-provided gps device name");
770 break;
771 }
772 if ( snprintf(device, sizeof(device), "/dev/%s", prop) >= (int)sizeof(device) ) {
773 LOGE("gps serial device name too long: '%s'", prop);
774 break;
775 }
776
777 do {
778 state->fd = open( device, O_RDWR );
779 } while (state->fd < 0 && errno == EINTR);
780
781 if (state->fd < 0) {
782 LOGE("could not open gps serial device %s: %s", device, strerror(errno) );
783 break;
784 }
785 done = 1;
786
787 } while (0);
788
789 if (!done) {
790 D("no gps emulation detected");
791 return;
792 }
793
794 D("gps emulation will read from %s", device);
795
796 // disable echo on serial lines
797 if ( !memcmp( device, "/dev/ttyS", 9 ) ) {
798 struct termios ios;
799 tcgetattr( state->fd, &ios );
800 ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */
801 tcsetattr( state->fd, TCSANOW, &ios );
802 }
803
804 if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
805 LOGE("could not create thread control socket pair: %s", strerror(errno));
806 goto Fail;
807 }
808
809 if ( pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0 ) {
810 LOGE("could not create gps thread: %s", strerror(errno));
811 goto Fail;
812 }
813
814 D("gps state initialized");
815 return;
816
817Fail:
818 gps_state_done( state );
819}
820
821
822/*****************************************************************/
823/*****************************************************************/
824/***** *****/
825/***** I N T E R F A C E *****/
826/***** *****/
827/*****************************************************************/
828/*****************************************************************/
829
830
831static int
832qemu_gps_init(GpsCallbacks* callbacks)
833{
834 GpsState* s = _gps_state;
835
836 if (!s->init)
837 gps_state_init(s);
838
839 if (s->fd < 0)
840 return -1;
841
842 s->callbacks = *callbacks;
843
844 return 0;
845}
846
847static void
848qemu_gps_cleanup(void)
849{
850 GpsState* s = _gps_state;
851
852 if (s->init)
853 gps_state_done(s);
854}
855
856
857static int
858qemu_gps_start()
859{
860 GpsState* s = _gps_state;
861
862 if (!s->init) {
863 D("%s: called with uninitialized state !!", __FUNCTION__);
864 return -1;
865 }
866
867 D("%s: called", __FUNCTION__);
868 gps_state_start(s);
869 return 0;
870}
871
872
873static int
874qemu_gps_stop()
875{
876 GpsState* s = _gps_state;
877
878 if (!s->init) {
879 D("%s: called with uninitialized state !!", __FUNCTION__);
880 return -1;
881 }
882
883 D("%s: called", __FUNCTION__);
884 gps_state_stop(s);
885 return 0;
886}
887
888
889static void
890qemu_gps_set_fix_frequency()
891{
892 GpsState* s = _gps_state;
893
894 if (!s->init) {
895 D("%s: called with uninitialized state !!", __FUNCTION__);
896 return -1;
897 }
898
899 D("%s: called", __FUNCTION__);
900 // FIXME - support fix_frequency
901}
902
903static int
904qemu_gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty)
905{
906 return 0;
907}
908
909static void
910qemu_gps_delete_aiding_data(GpsAidingData flags)
911{
912}
913
914static int qemu_gps_set_position_mode(GpsPositionMode mode, int fix_frequency)
915{
916 // FIXME - support fix_frequency
917 // only standalone supported for now.
918 if (mode != GPS_POSITION_MODE_STANDALONE)
919 return -1;
920 return 0;
921}
922
923static const void*
924qemu_gps_get_extension(const char* name)
925{
926 return NULL;
927}
928
929static const GpsInterface qemuGpsInterface = {
930 qemu_gps_init,
931 qemu_gps_start,
932 qemu_gps_stop,
933 qemu_gps_set_fix_frequency,
934 qemu_gps_cleanup,
935 qemu_gps_inject_time,
936 qemu_gps_delete_aiding_data,
937 qemu_gps_set_position_mode,
938 qemu_gps_get_extension,
939};
940
941const GpsInterface* gps_get_qemu_interface()
942{
943 return &qemuGpsInterface;
944}
945