blob: 7296f2297502fe325e46d00989529e1bd96027aa [file] [log] [blame]
The Android Open Source Projectd6054a32008-10-21 07:00:00 -07001
2#include <errno.h>
3#include <pthread.h>
The Android Open Source Project51704be2008-12-17 18:05:50 -08004#include "qemu.h"
The Android Open Source Projectd6054a32008-10-21 07:00:00 -07005#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>
The Android Open Source Projectd6054a32008-10-21 07:00:00 -070013#include <hardware/gps.h>
14
15/* the name of the qemud-controlled socket */
The Android Open Source Project51704be2008-12-17 18:05:50 -080016#define QEMU_CHANNEL_NAME "gps"
The Android Open Source Projectd6054a32008-10-21 07:00:00 -070017
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
35typedef struct {
36 const char* p;
37 const char* end;
38} Token;
39
40#define MAX_NMEA_TOKENS 16
41
42typedef struct {
43 int count;
44 Token tokens[ MAX_NMEA_TOKENS ];
45} NmeaTokenizer;
46
47static int
48nmea_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
93static Token
94nmea_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
108static int
109str2int( 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
129Fail:
130 return -1;
131}
132
133static double
134str2float( 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
158typedef 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
171static void
172nmea_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
198static void
199nmea_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
214static void
215nmea_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
226static int
227nmea_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
257 fix_time = mktime( &tm ) + r->utc_diff;
258 r->fix.timestamp = (long long)fix_time * 1000;
259 return 0;
260}
261
262static int
263nmea_reader_update_date( NmeaReader* r, Token date, Token time )
264{
265 Token tok = date;
266 int day, mon, year;
267
268 if (tok.p + 6 != tok.end) {
269 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
270 return -1;
271 }
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;
275
276 if ((day|mon|year) < 0) {
277 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
278 return -1;
279 }
280
281 r->utc_year = year;
282 r->utc_mon = mon;
283 r->utc_day = day;
284
285 return nmea_reader_update_time( r, time );
286}
287
288
289static double
290convert_from_hhmm( Token tok )
291{
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;
296 return dcoord;
297}
298
299
300static int
301nmea_reader_update_latlong( NmeaReader* r,
302 Token latitude,
303 char latitudeHemi,
304 Token longitude,
305 char longitudeHemi )
306{
307 double lat, lon;
308 Token tok;
309
310 tok = latitude;
311 if (tok.p + 6 > tok.end) {
312 D("latitude is too short: '%.*s'", tok.end-tok.p, tok.p);
313 return -1;
314 }
315 lat = convert_from_hhmm(tok);
316 if (latitudeHemi == 'S')
317 lat = -lat;
318
319 tok = longitude;
320 if (tok.p + 6 > tok.end) {
321 D("longitude is too short: '%.*s'", tok.end-tok.p, tok.p);
322 return -1;
323 }
324 lon = convert_from_hhmm(tok);
325 if (longitudeHemi == 'W')
326 lon = -lon;
327
328 r->fix.flags |= GPS_LOCATION_HAS_LAT_LONG;
329 r->fix.latitude = lat;
330 r->fix.longitude = lon;
331 return 0;
332}
333
334
335static int
336nmea_reader_update_altitude( NmeaReader* r,
337 Token altitude,
338 Token units )
339{
340 double alt;
341 Token tok = altitude;
342
343 if (tok.p >= tok.end)
344 return -1;
345
346 r->fix.flags |= GPS_LOCATION_HAS_ALTITUDE;
347 r->fix.altitude = str2float(tok.p, tok.end);
348 return 0;
349}
350
351
352static int
353nmea_reader_update_bearing( NmeaReader* r,
354 Token bearing )
355{
356 double alt;
357 Token tok = bearing;
358
359 if (tok.p >= tok.end)
360 return -1;
361
362 r->fix.flags |= GPS_LOCATION_HAS_BEARING;
363 r->fix.bearing = str2float(tok.p, tok.end);
364 return 0;
365}
366
367
368static int
369nmea_reader_update_speed( NmeaReader* r,
370 Token speed )
371{
372 double alt;
373 Token tok = speed;
374
375 if (tok.p >= tok.end)
376 return -1;
377
378 r->fix.flags |= GPS_LOCATION_HAS_SPEED;
379 r->fix.speed = str2float(tok.p, tok.end);
380 return 0;
381}
382
383
384static void
385nmea_reader_parse( NmeaReader* r )
386{
387 /* we received a complete sentence, now parse it to generate
388 * a new GPS fix...
389 */
390 NmeaTokenizer tzer[1];
391 Token tok;
392
393 D("Received: '%.*s'", r->pos, r->in);
394 if (r->pos < 9) {
395 D("Too short. discarded.");
396 return;
397 }
398
399 nmea_tokenizer_init(tzer, r->in, r->in + r->pos);
400#if GPS_DEBUG
401 {
402 int n;
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);
407 }
408 }
409#endif
410
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);
414 return;
415 }
416
417 // ignore first two characters.
418 tok.p += 2;
419 if ( !memcmp(tok.p, "GGA", 3) ) {
420 // GPS fix
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);
428
429 nmea_reader_update_time(r, tok_time);
430 nmea_reader_update_latlong(r, tok_latitude,
431 tok_latitudeHemi.p[0],
432 tok_longitude,
433 tok_longitudeHemi.p[0]);
434 nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits);
435
436 } else if ( !memcmp(tok.p, "GSA", 3) ) {
437 // do something ?
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);
448
449 D("in RMC, fixStatus=%c", tok_fixStatus.p[0]);
450 if (tok_fixStatus.p[0] == 'A')
451 {
452 nmea_reader_update_date( r, tok_date, tok_time );
453
454 nmea_reader_update_latlong( r, tok_latitude,
455 tok_latitudeHemi.p[0],
456 tok_longitude,
457 tok_longitudeHemi.p[0] );
458
459 nmea_reader_update_bearing( r, tok_bearing );
460 nmea_reader_update_speed ( r, tok_speed );
461 }
462 } else {
463 tok.p -= 2;
464 D("unknown sentence '%.*s", tok.end-tok.p, tok.p);
465 }
466 if (r->fix.flags != 0) {
467#if GPS_DEBUG
468 char temp[256];
469 char* p = temp;
470 char* end = p + sizeof(temp);
471 struct tm utc;
472
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);
476 }
477 if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) {
478 p += snprintf(p, end-p, " altitude=%g", r->fix.altitude);
479 }
480 if (r->fix.flags & GPS_LOCATION_HAS_SPEED) {
481 p += snprintf(p, end-p, " speed=%g", r->fix.speed);
482 }
483 if (r->fix.flags & GPS_LOCATION_HAS_BEARING) {
484 p += snprintf(p, end-p, " bearing=%g", r->fix.bearing);
485 }
486 if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) {
487 p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy);
488 }
489 gmtime_r( (time_t*) &r->fix.timestamp, &utc );
490 p += snprintf(p, end-p, " time=%s", asctime( &utc ) );
491 D(temp);
492#endif
493 if (r->callback) {
494 r->callback( &r->fix );
495 r->fix.flags = 0;
496 }
497 else {
498 D("no callback, keeping data until needed !");
499 }
500 }
501}
502
503
504static void
505nmea_reader_addc( NmeaReader* r, int c )
506{
507 if (r->overflow) {
508 r->overflow = (c != '\n');
509 return;
510 }
511
512 if (r->pos >= (int) sizeof(r->in)-1 ) {
513 r->overflow = 1;
514 r->pos = 0;
515 return;
516 }
517
518 r->in[r->pos] = (char)c;
519 r->pos += 1;
520
521 if (c == '\n') {
522 nmea_reader_parse( r );
523 r->pos = 0;
524 }
525}
526
527
528/*****************************************************************/
529/*****************************************************************/
530/***** *****/
531/***** C O N N E C T I O N S T A T E *****/
532/***** *****/
533/*****************************************************************/
534/*****************************************************************/
535
536/* commands sent to the gps thread */
537enum {
538 CMD_QUIT = 0,
539 CMD_START = 1,
540 CMD_STOP = 2
541};
542
543
544/* this is the state of our connection to the qemu_gpsd daemon */
545typedef struct {
546 int init;
547 int fd;
548 GpsCallbacks callbacks;
549 pthread_t thread;
550 int control[2];
The Android Open Source Project51704be2008-12-17 18:05:50 -0800551 QemuChannel channel;
The Android Open Source Projectd6054a32008-10-21 07:00:00 -0700552} GpsState;
553
554static GpsState _gps_state[1];
555
556
557static void
558gps_state_done( GpsState* s )
559{
560 // tell the thread to quit, and wait for it
561 char cmd = CMD_QUIT;
562 void* dummy;
563 write( s->control[0], &cmd, 1 );
564 pthread_join(s->thread, &dummy);
565
566 // close the control socket pair
567 close( s->control[0] ); s->control[0] = -1;
568 close( s->control[1] ); s->control[1] = -1;
569
570 // close connection to the QEMU GPS daemon
571 close( s->fd ); s->fd = -1;
572 s->init = 0;
573}
574
575
576static void
577gps_state_start( GpsState* s )
578{
579 char cmd = CMD_START;
580 int ret;
581
582 do { ret=write( s->control[0], &cmd, 1 ); }
583 while (ret < 0 && errno == EINTR);
584
585 if (ret != 1)
586 D("%s: could not send CMD_START command: ret=%d: %s",
587 __FUNCTION__, ret, strerror(errno));
588}
589
590
591static void
592gps_state_stop( GpsState* s )
593{
594 char cmd = CMD_STOP;
595 int ret;
596
597 do { ret=write( s->control[0], &cmd, 1 ); }
598 while (ret < 0 && errno == EINTR);
599
600 if (ret != 1)
601 D("%s: could not send CMD_STOP command: ret=%d: %s",
602 __FUNCTION__, ret, strerror(errno));
603}
604
605
606static int
607epoll_register( int epoll_fd, int fd )
608{
609 struct epoll_event ev;
610 int ret, flags;
611
612 /* important: make the fd non-blocking */
613 flags = fcntl(fd, F_GETFL);
614 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
615
616 ev.events = EPOLLIN;
617 ev.data.fd = fd;
618 do {
619 ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
620 } while (ret < 0 && errno == EINTR);
621 return ret;
622}
623
624
625static int
626epoll_deregister( int epoll_fd, int fd )
627{
628 int ret;
629 do {
630 ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
631 } while (ret < 0 && errno == EINTR);
632 return ret;
633}
634
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
638 */
639static void*
640gps_state_thread( void* arg )
641{
642 GpsState* state = (GpsState*) arg;
643 NmeaReader reader[1];
644 int epoll_fd = epoll_create(2);
645 int started = 0;
646 int gps_fd = state->fd;
647 int control_fd = state->control[1];
648
649 nmea_reader_init( reader );
650
651 // register control file descriptors for polling
652 epoll_register( epoll_fd, control_fd );
653 epoll_register( epoll_fd, gps_fd );
654
655 D("gps thread running");
656
657 // now loop
658 for (;;) {
659 struct epoll_event events[2];
660 int ne, nevents;
661
662 nevents = epoll_wait( epoll_fd, events, 2, -1 );
663 if (nevents < 0) {
664 if (errno != EINTR)
665 LOGE("epoll_wait() unexpected error: %s", strerror(errno));
666 continue;
667 }
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() !?");
672 goto Exit;
673 }
674 if ((events[ne].events & EPOLLIN) != 0) {
675 int fd = events[ne].data.fd;
676
677 if (fd == control_fd)
678 {
679 char cmd = 255;
680 int ret;
681 D("gps control fd event");
682 do {
683 ret = read( fd, &cmd, 1 );
684 } while (ret < 0 && errno == EINTR);
685
686 if (cmd == CMD_QUIT) {
687 D("gps thread quitting on demand");
688 goto Exit;
689 }
690 else if (cmd == CMD_START) {
691 if (!started) {
692 D("gps thread starting location_cb=%p", state->callbacks.location_cb);
693 started = 1;
694 nmea_reader_set_callback( reader, state->callbacks.location_cb );
695 }
696 }
697 else if (cmd == CMD_STOP) {
698 if (started) {
699 D("gps thread stopping");
700 started = 0;
701 nmea_reader_set_callback( reader, NULL );
702 }
703 }
704 }
705 else if (fd == gps_fd)
706 {
707 char buff[32];
708 D("gps fd event");
709 for (;;) {
710 int nn, ret;
711
712 ret = read( fd, buff, sizeof(buff) );
713 if (ret < 0) {
714 if (errno == EINTR)
715 continue;
716 if (errno != EWOULDBLOCK)
717 LOGE("error while reading from gps daemon socket: %s:", strerror(errno));
718 break;
719 }
720 D("received %d bytes: %.*s", ret, ret, buff);
721 for (nn = 0; nn < ret; nn++)
722 nmea_reader_addc( reader, buff[nn] );
723 }
724 D("gps fd event end");
725 }
726 else
727 {
728 LOGE("epoll_wait() returned unkown fd %d ?", fd);
729 }
730 }
731 }
732 }
733Exit:
734 return NULL;
735}
736
737
738static void
739gps_state_init( GpsState* state )
740{
The Android Open Source Projectd6054a32008-10-21 07:00:00 -0700741 state->init = 1;
742 state->control[0] = -1;
743 state->control[1] = -1;
744 state->fd = -1;
745
The Android Open Source Project51704be2008-12-17 18:05:50 -0800746 state->fd = qemu_channel_open( &state->channel,
747 QEMU_CHANNEL_NAME,
748 O_RDONLY );
The Android Open Source Projectd6054a32008-10-21 07:00:00 -0700749
The Android Open Source Project51704be2008-12-17 18:05:50 -0800750 if (state->fd < 0) {
The Android Open Source Projectd6054a32008-10-21 07:00:00 -0700751 D("no gps emulation detected");
752 return;
753 }
754
755 D("gps emulation will read from %s", device);
756
The Android Open Source Projectd6054a32008-10-21 07:00:00 -0700757 if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
758 LOGE("could not create thread control socket pair: %s", strerror(errno));
759 goto Fail;
760 }
761
762 if ( pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0 ) {
763 LOGE("could not create gps thread: %s", strerror(errno));
764 goto Fail;
765 }
766
767 D("gps state initialized");
768 return;
769
770Fail:
771 gps_state_done( state );
772}
773
774
775/*****************************************************************/
776/*****************************************************************/
777/***** *****/
778/***** I N T E R F A C E *****/
779/***** *****/
780/*****************************************************************/
781/*****************************************************************/
782
783
784static int
785qemu_gps_init(GpsCallbacks* callbacks)
786{
787 GpsState* s = _gps_state;
788
789 if (!s->init)
790 gps_state_init(s);
791
792 if (s->fd < 0)
793 return -1;
794
795 s->callbacks = *callbacks;
796
797 return 0;
798}
799
800static void
801qemu_gps_cleanup(void)
802{
803 GpsState* s = _gps_state;
804
805 if (s->init)
806 gps_state_done(s);
807}
808
809
810static int
811qemu_gps_start()
812{
813 GpsState* s = _gps_state;
814
815 if (!s->init) {
816 D("%s: called with uninitialized state !!", __FUNCTION__);
817 return -1;
818 }
819
820 D("%s: called", __FUNCTION__);
821 gps_state_start(s);
822 return 0;
823}
824
825
826static int
827qemu_gps_stop()
828{
829 GpsState* s = _gps_state;
830
831 if (!s->init) {
832 D("%s: called with uninitialized state !!", __FUNCTION__);
833 return -1;
834 }
835
836 D("%s: called", __FUNCTION__);
837 gps_state_stop(s);
838 return 0;
839}
840
841
842static void
843qemu_gps_set_fix_frequency()
844{
845 GpsState* s = _gps_state;
846
847 if (!s->init) {
848 D("%s: called with uninitialized state !!", __FUNCTION__);
The Android Open Source Project51704be2008-12-17 18:05:50 -0800849 return;
The Android Open Source Projectd6054a32008-10-21 07:00:00 -0700850 }
851
852 D("%s: called", __FUNCTION__);
853 // FIXME - support fix_frequency
854}
855
856static int
857qemu_gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty)
858{
859 return 0;
860}
861
862static void
863qemu_gps_delete_aiding_data(GpsAidingData flags)
864{
865}
866
867static int qemu_gps_set_position_mode(GpsPositionMode mode, int fix_frequency)
868{
869 // FIXME - support fix_frequency
870 // only standalone supported for now.
871 if (mode != GPS_POSITION_MODE_STANDALONE)
872 return -1;
873 return 0;
874}
875
876static const void*
877qemu_gps_get_extension(const char* name)
878{
879 return NULL;
880}
881
882static const GpsInterface qemuGpsInterface = {
883 qemu_gps_init,
884 qemu_gps_start,
885 qemu_gps_stop,
886 qemu_gps_set_fix_frequency,
887 qemu_gps_cleanup,
888 qemu_gps_inject_time,
889 qemu_gps_delete_aiding_data,
890 qemu_gps_set_position_mode,
891 qemu_gps_get_extension,
892};
893
894const GpsInterface* gps_get_qemu_interface()
895{
896 return &qemuGpsInterface;
897}
898