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