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