OSDN Git Service

AI 149496: Backport emulator-related donut fixes into cupcake.
[android-x86/hardware-libhardware_legacy.git] / gps / gps_qemu.c
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
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
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
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
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
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
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
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
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
257     fix_time = mktime( &tm ) + r->utc_diff;
258     r->fix.timestamp = (long long)fix_time * 1000;
259     return 0;
260 }
261
262 static int
263 nmea_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
289 static double
290 convert_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
300 static int
301 nmea_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
335 static int
336 nmea_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
352 static int
353 nmea_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
368 static int
369 nmea_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
384 static void
385 nmea_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
504 static void
505 nmea_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 */
537 enum {
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 */
545 typedef struct {
546     int                     init;
547     int                     fd;
548     GpsCallbacks            callbacks;
549     pthread_t               thread;
550     int                     control[2];
551     QemuChannel             channel;
552 } GpsState;
553
554 static GpsState  _gps_state[1];
555
556
557 static void
558 gps_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
576 static void
577 gps_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
591 static void
592 gps_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
606 static int
607 epoll_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
625 static int
626 epoll_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  */
639 static void*
640 gps_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     }
733 Exit:
734     return NULL;
735 }
736
737
738 static void
739 gps_state_init( GpsState*  state )
740 {
741     state->init       = 1;
742     state->control[0] = -1;
743     state->control[1] = -1;
744     state->fd         = -1;
745
746     state->fd = qemu_channel_open( &state->channel,
747                                    QEMU_CHANNEL_NAME,
748                                    O_RDONLY );
749
750     if (state->fd < 0) {
751         D("no gps emulation detected");
752         return;
753     }
754
755     D("gps emulation will read from '%s' qemud channel", QEMU_CHANNEL_NAME );
756
757     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
770 Fail:
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
784 static int
785 qemu_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
800 static void
801 qemu_gps_cleanup(void)
802 {
803     GpsState*  s = _gps_state;
804
805     if (s->init)
806         gps_state_done(s);
807 }
808
809
810 static int
811 qemu_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
826 static int
827 qemu_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
842 static void
843 qemu_gps_set_fix_frequency()
844 {
845     GpsState*  s = _gps_state;
846
847     if (!s->init) {
848         D("%s: called with uninitialized state !!", __FUNCTION__);
849         return;
850     }
851
852     D("%s: called", __FUNCTION__);
853     // FIXME - support fix_frequency
854 }
855
856 static int
857 qemu_gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty)
858 {
859     return 0;
860 }
861
862 static void
863 qemu_gps_delete_aiding_data(GpsAidingData flags)
864 {
865 }
866
867 static int qemu_gps_set_position_mode(GpsPositionMode mode, int fix_frequency)
868 {
869     // FIXME - support fix_frequency
870     return 0;
871 }
872
873 static const void*
874 qemu_gps_get_extension(const char* name)
875 {
876     return NULL;
877 }
878
879 static const GpsInterface  qemuGpsInterface = {
880     qemu_gps_init,
881     qemu_gps_start,
882     qemu_gps_stop,
883     qemu_gps_set_fix_frequency,
884     qemu_gps_cleanup,
885     qemu_gps_inject_time,
886     qemu_gps_delete_aiding_data,
887     qemu_gps_set_position_mode,
888     qemu_gps_get_extension,
889 };
890
891 const GpsInterface* gps_get_qemu_interface()
892 {
893     return &qemuGpsInterface;
894 }
895