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