1
2 #include <errno.h>
3 #include <pthread.h>
4 #include "qemu.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 <hardware_legacy/gps.h>
14
15 /* the name of the qemud-controlled socket */
16 #define QEMU_CHANNEL_NAME "gps"
17
18 #define GPS_DEBUG 0
19
20 #if GPS_DEBUG
21 # define D(...) LOGD(__VA_ARGS__)
22 #else
23 # define D(...) ((void)0)
24 #endif
25
26
27 /*****************************************************************/
28 /*****************************************************************/
29 /***** *****/
30 /***** N M E A T O K E N I Z E R *****/
31 /***** *****/
32 /*****************************************************************/
33 /*****************************************************************/
34
35 typedef struct {
36 const char* p;
37 const char* end;
38 } Token;
39
40 #define MAX_NMEA_TOKENS 16
41
42 typedef struct {
43 int count;
44 Token tokens[ MAX_NMEA_TOKENS ];
45 } NmeaTokenizer;
46
47 static int
nmea_tokenizer_init(NmeaTokenizer * t,const char * p,const char * end)48 nmea_tokenizer_init( NmeaTokenizer* t, const char* p, const char* end )
49 {
50 int count = 0;
51 char* q;
52
53 // the initial '$' is optional
54 if (p < end && p[0] == '$')
55 p += 1;
56
57 // remove trailing newline
58 if (end > p && end[-1] == '\n') {
59 end -= 1;
60 if (end > p && end[-1] == '\r')
61 end -= 1;
62 }
63
64 // get rid of checksum at the end of the sentecne
65 if (end >= p+3 && end[-3] == '*') {
66 end -= 3;
67 }
68
69 while (p < end) {
70 const char* q = p;
71
72 q = memchr(p, ',', end-p);
73 if (q == NULL)
74 q = end;
75
76 if (q > p) {
77 if (count < MAX_NMEA_TOKENS) {
78 t->tokens[count].p = p;
79 t->tokens[count].end = q;
80 count += 1;
81 }
82 }
83 if (q < end)
84 q += 1;
85
86 p = q;
87 }
88
89 t->count = count;
90 return count;
91 }
92
93 static Token
nmea_tokenizer_get(NmeaTokenizer * t,int index)94 nmea_tokenizer_get( NmeaTokenizer* t, int index )
95 {
96 Token tok;
97 static const char* dummy = "";
98
99 if (index < 0 || index >= t->count) {
100 tok.p = tok.end = dummy;
101 } else
102 tok = t->tokens[index];
103
104 return tok;
105 }
106
107
108 static int
str2int(const char * p,const char * end)109 str2int( const char* p, const char* end )
110 {
111 int result = 0;
112 int len = end - p;
113
114 for ( ; len > 0; len--, p++ )
115 {
116 int c;
117
118 if (p >= end)
119 goto Fail;
120
121 c = *p - '0';
122 if ((unsigned)c >= 10)
123 goto Fail;
124
125 result = result*10 + c;
126 }
127 return result;
128
129 Fail:
130 return -1;
131 }
132
133 static double
str2float(const char * p,const char * end)134 str2float( const char* p, const char* end )
135 {
136 int result = 0;
137 int len = end - p;
138 char temp[16];
139
140 if (len >= (int)sizeof(temp))
141 return 0.;
142
143 memcpy( temp, p, len );
144 temp[len] = 0;
145 return strtod( temp, NULL );
146 }
147
148 /*****************************************************************/
149 /*****************************************************************/
150 /***** *****/
151 /***** N M E A P A R S E R *****/
152 /***** *****/
153 /*****************************************************************/
154 /*****************************************************************/
155
156 #define NMEA_MAX_SIZE 83
157
158 typedef struct {
159 int pos;
160 int overflow;
161 int utc_year;
162 int utc_mon;
163 int utc_day;
164 int utc_diff;
165 GpsLocation fix;
166 gps_location_callback callback;
167 char in[ NMEA_MAX_SIZE+1 ];
168 } NmeaReader;
169
170
171 static void
nmea_reader_update_utc_diff(NmeaReader * r)172 nmea_reader_update_utc_diff( NmeaReader* r )
173 {
174 time_t now = time(NULL);
175 struct tm tm_local;
176 struct tm tm_utc;
177 long time_local, time_utc;
178
179 gmtime_r( &now, &tm_utc );
180 localtime_r( &now, &tm_local );
181
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)));
187
188 time_utc = tm_utc.tm_sec +
189 60*(tm_utc.tm_min +
190 60*(tm_utc.tm_hour +
191 24*(tm_utc.tm_yday +
192 365*tm_utc.tm_year)));
193
194 r->utc_diff = time_utc - time_local;
195 }
196
197
198 static void
nmea_reader_init(NmeaReader * r)199 nmea_reader_init( NmeaReader* r )
200 {
201 memset( r, 0, sizeof(*r) );
202
203 r->pos = 0;
204 r->overflow = 0;
205 r->utc_year = -1;
206 r->utc_mon = -1;
207 r->utc_day = -1;
208 r->callback = NULL;
209
210 nmea_reader_update_utc_diff( r );
211 }
212
213
214 static void
nmea_reader_set_callback(NmeaReader * r,gps_location_callback cb)215 nmea_reader_set_callback( NmeaReader* r, gps_location_callback cb )
216 {
217 r->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 );
221 r->fix.flags = 0;
222 }
223 }
224
225
226 static int
nmea_reader_update_time(NmeaReader * r,Token tok)227 nmea_reader_update_time( NmeaReader* r, Token tok )
228 {
229 int hour, minute;
230 double seconds;
231 struct tm tm;
232 time_t fix_time;
233
234 if (tok.p + 6 > tok.end)
235 return -1;
236
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;
244 }
245
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);
249
250 tm.tm_hour = hour;
251 tm.tm_min = minute;
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;
256 tm.tm_isdst = -1;
257
258 fix_time = mktime( &tm ) + r->utc_diff;
259 r->fix.timestamp = (long long)fix_time * 1000;
260 return 0;
261 }
262
263 static int
nmea_reader_update_date(NmeaReader * r,Token date,Token time)264 nmea_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
290 static double
convert_from_hhmm(Token tok)291 convert_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
301 static int
nmea_reader_update_latlong(NmeaReader * r,Token latitude,char latitudeHemi,Token longitude,char longitudeHemi)302 nmea_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
336 static int
nmea_reader_update_altitude(NmeaReader * r,Token altitude,Token units)337 nmea_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
353 static int
nmea_reader_update_bearing(NmeaReader * r,Token bearing)354 nmea_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
369 static int
nmea_reader_update_speed(NmeaReader * r,Token speed)370 nmea_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
385 static void
nmea_reader_parse(NmeaReader * r)386 nmea_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
505 static void
nmea_reader_addc(NmeaReader * r,int c)506 nmea_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 */
538 enum {
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 */
546 typedef struct {
547 int init;
548 int fd;
549 GpsCallbacks callbacks;
550 pthread_t thread;
551 int control[2];
552 QemuChannel channel;
553 } GpsState;
554
555 static GpsState _gps_state[1];
556
557
558 static void
gps_state_done(GpsState * s)559 gps_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
577 static void
gps_state_start(GpsState * s)578 gps_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
592 static void
gps_state_stop(GpsState * s)593 gps_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
607 static int
epoll_register(int epoll_fd,int fd)608 epoll_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
626 static int
epoll_deregister(int epoll_fd,int fd)627 epoll_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 */
640 static void*
gps_state_thread(void * arg)641 gps_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 }
734 Exit:
735 return NULL;
736 }
737
738
739 static void
gps_state_init(GpsState * state)740 gps_state_init( GpsState* state )
741 {
742 state->init = 1;
743 state->control[0] = -1;
744 state->control[1] = -1;
745 state->fd = -1;
746
747 state->fd = qemu_channel_open( &state->channel,
748 QEMU_CHANNEL_NAME,
749 O_RDONLY );
750
751 if (state->fd < 0) {
752 D("no gps emulation detected");
753 return;
754 }
755
756 D("gps emulation will read from '%s' qemud channel", QEMU_CHANNEL_NAME );
757
758 if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
759 LOGE("could not create thread control socket pair: %s", strerror(errno));
760 goto Fail;
761 }
762
763 if ( pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0 ) {
764 LOGE("could not create gps thread: %s", strerror(errno));
765 goto Fail;
766 }
767
768 D("gps state initialized");
769 return;
770
771 Fail:
772 gps_state_done( state );
773 }
774
775
776 /*****************************************************************/
777 /*****************************************************************/
778 /***** *****/
779 /***** I N T E R F A C E *****/
780 /***** *****/
781 /*****************************************************************/
782 /*****************************************************************/
783
784
785 static int
qemu_gps_init(GpsCallbacks * callbacks)786 qemu_gps_init(GpsCallbacks* callbacks)
787 {
788 GpsState* s = _gps_state;
789
790 if (!s->init)
791 gps_state_init(s);
792
793 if (s->fd < 0)
794 return -1;
795
796 s->callbacks = *callbacks;
797
798 return 0;
799 }
800
801 static void
qemu_gps_cleanup(void)802 qemu_gps_cleanup(void)
803 {
804 GpsState* s = _gps_state;
805
806 if (s->init)
807 gps_state_done(s);
808 }
809
810
811 static int
qemu_gps_start()812 qemu_gps_start()
813 {
814 GpsState* s = _gps_state;
815
816 if (!s->init) {
817 D("%s: called with uninitialized state !!", __FUNCTION__);
818 return -1;
819 }
820
821 D("%s: called", __FUNCTION__);
822 gps_state_start(s);
823 return 0;
824 }
825
826
827 static int
qemu_gps_stop()828 qemu_gps_stop()
829 {
830 GpsState* s = _gps_state;
831
832 if (!s->init) {
833 D("%s: called with uninitialized state !!", __FUNCTION__);
834 return -1;
835 }
836
837 D("%s: called", __FUNCTION__);
838 gps_state_stop(s);
839 return 0;
840 }
841
842
843 static int
qemu_gps_inject_time(GpsUtcTime time,int64_t timeReference,int uncertainty)844 qemu_gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty)
845 {
846 return 0;
847 }
848
849 static int
qemu_gps_inject_location(double latitude,double longitude,float accuracy)850 qemu_gps_inject_location(double latitude, double longitude, float accuracy)
851 {
852 return 0;
853 }
854
855 static void
qemu_gps_delete_aiding_data(GpsAidingData flags)856 qemu_gps_delete_aiding_data(GpsAidingData flags)
857 {
858 }
859
qemu_gps_set_position_mode(GpsPositionMode mode,int fix_frequency)860 static int qemu_gps_set_position_mode(GpsPositionMode mode, int fix_frequency)
861 {
862 // FIXME - support fix_frequency
863 return 0;
864 }
865
866 static const void*
qemu_gps_get_extension(const char * name)867 qemu_gps_get_extension(const char* name)
868 {
869 return NULL;
870 }
871
872 static const GpsInterface qemuGpsInterface = {
873 qemu_gps_init,
874 qemu_gps_start,
875 qemu_gps_stop,
876 qemu_gps_cleanup,
877 qemu_gps_inject_time,
878 qemu_gps_inject_location,
879 qemu_gps_delete_aiding_data,
880 qemu_gps_set_position_mode,
881 qemu_gps_get_extension,
882 };
883
gps_get_qemu_interface()884 const GpsInterface* gps_get_qemu_interface()
885 {
886 return &qemuGpsInterface;
887 }
888
889