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
58 typedef struct {
59 const char* p;
60 const char* end;
61 } Token;
62
63 #define MAX_NMEA_TOKENS 16
64
65 typedef struct {
66 int count;
67 Token tokens[ MAX_NMEA_TOKENS ];
68 } NmeaTokenizer;
69
70 static int
nmea_tokenizer_init(NmeaTokenizer * t,const char * p,const char * end)71 nmea_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
114 static Token
nmea_tokenizer_get(NmeaTokenizer * t,int index)115 nmea_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
129 static int
str2int(const char * p,const char * end)130 str2int( 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
150 Fail:
151 return -1;
152 }
153
154 static double
str2float(const char * p,const char * end)155 str2float( 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
179 typedef 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
192 static void
nmea_reader_update_utc_diff(NmeaReader * r)193 nmea_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
219 static void
nmea_reader_init(NmeaReader * r)220 nmea_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
236 static void
nmea_reader_set_callback(NmeaReader * r,gps_location_callback cb)237 nmea_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 }
244 }
245
246
247 static int
nmea_reader_update_time(NmeaReader * r,Token tok)248 nmea_reader_update_time( NmeaReader* r, Token tok )
249 {
250 int hour, minute;
251 double seconds;
252 struct tm tm;
253 time_t fix_time;
254
255 if (tok.p + 6 > tok.end)
256 return -1;
257
258 if (r->utc_year < 0) {
259 // no date yet, get current one
260 time_t now = time(NULL);
261 gmtime_r( &now, &tm );
262 r->utc_year = tm.tm_year + 1900;
263 r->utc_mon = tm.tm_mon + 1;
264 r->utc_day = tm.tm_mday;
265 }
266
267 hour = str2int(tok.p, tok.p+2);
268 minute = str2int(tok.p+2, tok.p+4);
269 seconds = str2float(tok.p+4, tok.end);
270
271 tm.tm_hour = hour;
272 tm.tm_min = minute;
273 tm.tm_sec = (int) seconds;
274 tm.tm_year = r->utc_year - 1900;
275 tm.tm_mon = r->utc_mon - 1;
276 tm.tm_mday = r->utc_day;
277 tm.tm_isdst = -1;
278
279 // This is a little confusing, let's use an example:
280 // Suppose now it's 1970-1-1 01:00 GMT, local time is 1970-1-1 00:00 GMT-1
281 // Then the utc_diff is 3600.
282 // The time string from GPS is 01:00:00, mktime assumes it's a local
283 // time. So we are doing mktime for 1970-1-1 01:00 GMT-1. The result of
284 // mktime is 7200 (1970-1-1 02:00 GMT) actually. To get the correct
285 // timestamp, we have to subtract utc_diff here.
286 fix_time = mktime( &tm ) - r->utc_diff;
287 r->fix.timestamp = (long long)fix_time * 1000;
288 return 0;
289 }
290
291 static int
nmea_reader_update_date(NmeaReader * r,Token date,Token time)292 nmea_reader_update_date( NmeaReader* r, Token date, Token time )
293 {
294 Token tok = date;
295 int day, mon, year;
296
297 if (tok.p + 6 != tok.end) {
298 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
299 return -1;
300 }
301 day = str2int(tok.p, tok.p+2);
302 mon = str2int(tok.p+2, tok.p+4);
303 year = str2int(tok.p+4, tok.p+6) + 2000;
304
305 if ((day|mon|year) < 0) {
306 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
307 return -1;
308 }
309
310 r->utc_year = year;
311 r->utc_mon = mon;
312 r->utc_day = day;
313
314 return nmea_reader_update_time( r, time );
315 }
316
317
318 static double
convert_from_hhmm(Token tok)319 convert_from_hhmm( Token tok )
320 {
321 double val = str2float(tok.p, tok.end);
322 int degrees = (int)(floor(val) / 100);
323 double minutes = val - degrees*100.;
324 double dcoord = degrees + minutes / 60.0;
325 return dcoord;
326 }
327
328
329 static int
nmea_reader_update_latlong(NmeaReader * r,Token latitude,char latitudeHemi,Token longitude,char longitudeHemi)330 nmea_reader_update_latlong( NmeaReader* r,
331 Token latitude,
332 char latitudeHemi,
333 Token longitude,
334 char longitudeHemi )
335 {
336 double lat, lon;
337 Token tok;
338
339 tok = latitude;
340 if (tok.p + 6 > tok.end) {
341 D("latitude is too short: '%.*s'", tok.end-tok.p, tok.p);
342 return -1;
343 }
344 lat = convert_from_hhmm(tok);
345 if (latitudeHemi == 'S')
346 lat = -lat;
347
348 tok = longitude;
349 if (tok.p + 6 > tok.end) {
350 D("longitude is too short: '%.*s'", tok.end-tok.p, tok.p);
351 return -1;
352 }
353 lon = convert_from_hhmm(tok);
354 if (longitudeHemi == 'W')
355 lon = -lon;
356
357 r->fix.flags |= GPS_LOCATION_HAS_LAT_LONG;
358 r->fix.latitude = lat;
359 r->fix.longitude = lon;
360 return 0;
361 }
362
363
364 static int
nmea_reader_update_altitude(NmeaReader * r,Token altitude,Token __unused units)365 nmea_reader_update_altitude( NmeaReader* r,
366 Token altitude,
367 Token __unused units )
368 {
369 double alt;
370 Token tok = altitude;
371
372 if (tok.p >= tok.end)
373 return -1;
374
375 r->fix.flags |= GPS_LOCATION_HAS_ALTITUDE;
376 r->fix.altitude = str2float(tok.p, tok.end);
377 return 0;
378 }
379
380
381 static int
nmea_reader_update_bearing(NmeaReader * r,Token bearing)382 nmea_reader_update_bearing( NmeaReader* r,
383 Token bearing )
384 {
385 double alt;
386 Token tok = bearing;
387
388 if (tok.p >= tok.end)
389 return -1;
390
391 r->fix.flags |= GPS_LOCATION_HAS_BEARING;
392 r->fix.bearing = str2float(tok.p, tok.end);
393 return 0;
394 }
395
396
397 static int
nmea_reader_update_speed(NmeaReader * r,Token speed)398 nmea_reader_update_speed( NmeaReader* r,
399 Token speed )
400 {
401 double alt;
402 Token tok = speed;
403
404 if (tok.p >= tok.end)
405 return -1;
406
407 r->fix.flags |= GPS_LOCATION_HAS_SPEED;
408 r->fix.speed = str2float(tok.p, tok.end);
409 return 0;
410 }
411
412 static int
nmea_reader_update_accuracy(NmeaReader * r)413 nmea_reader_update_accuracy( NmeaReader* r )
414 {
415 // Always return 20m accuracy.
416 // Possibly parse it from the NMEA sentence in the future.
417 r->fix.flags |= GPS_LOCATION_HAS_ACCURACY;
418 r->fix.accuracy = 20;
419 return 0;
420 }
421
422
423 static void
nmea_reader_parse(NmeaReader * r)424 nmea_reader_parse( NmeaReader* r )
425 {
426 /* we received a complete sentence, now parse it to generate
427 * a new GPS fix...
428 */
429 NmeaTokenizer tzer[1];
430 Token tok;
431
432 D("Received: '%.*s'", r->pos, r->in);
433 if (r->pos < 9) {
434 D("Too short. discarded.");
435 return;
436 }
437
438 nmea_tokenizer_init(tzer, r->in, r->in + r->pos);
439 #if GPS_DEBUG
440 {
441 int n;
442 D("Found %d tokens", tzer->count);
443 for (n = 0; n < tzer->count; n++) {
444 Token tok = nmea_tokenizer_get(tzer,n);
445 D("%2d: '%.*s'", n, tok.end-tok.p, tok.p);
446 }
447 }
448 #endif
449
450 tok = nmea_tokenizer_get(tzer, 0);
451 if (tok.p + 5 > tok.end) {
452 D("sentence id '%.*s' too short, ignored.", tok.end-tok.p, tok.p);
453 return;
454 }
455
456 // ignore first two characters.
457 tok.p += 2;
458 if ( !memcmp(tok.p, "GGA", 3) ) {
459 // GPS fix
460 Token tok_time = nmea_tokenizer_get(tzer,1);
461 Token tok_latitude = nmea_tokenizer_get(tzer,2);
462 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,3);
463 Token tok_longitude = nmea_tokenizer_get(tzer,4);
464 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,5);
465 Token tok_altitude = nmea_tokenizer_get(tzer,9);
466 Token tok_altitudeUnits = nmea_tokenizer_get(tzer,10);
467
468 r->fix.flags = 0;
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 r->fix.flags = 0;
493 nmea_reader_update_date( r, tok_date, tok_time );
494
495 nmea_reader_update_latlong( r, tok_latitude,
496 tok_latitudeHemi.p[0],
497 tok_longitude,
498 tok_longitudeHemi.p[0] );
499
500 nmea_reader_update_bearing( r, tok_bearing );
501 nmea_reader_update_speed ( r, tok_speed );
502 }
503 } else {
504 tok.p -= 2;
505 D("unknown sentence '%.*s", tok.end-tok.p, tok.p);
506 }
507
508 // Always update accuracy
509 nmea_reader_update_accuracy( r );
510
511 if (r->fix.flags != 0) {
512 #if GPS_DEBUG
513 char temp[256];
514 char* p = temp;
515 char* end = p + sizeof(temp);
516 struct tm utc;
517
518 p += snprintf( p, end-p, "sending fix" );
519 if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
520 p += snprintf(p, end-p, " lat=%g lon=%g", r->fix.latitude, r->fix.longitude);
521 }
522 if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) {
523 p += snprintf(p, end-p, " altitude=%g", r->fix.altitude);
524 }
525 if (r->fix.flags & GPS_LOCATION_HAS_SPEED) {
526 p += snprintf(p, end-p, " speed=%g", r->fix.speed);
527 }
528 if (r->fix.flags & GPS_LOCATION_HAS_BEARING) {
529 p += snprintf(p, end-p, " bearing=%g", r->fix.bearing);
530 }
531 if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) {
532 p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy);
533 }
534 gmtime_r( (time_t*) &r->fix.timestamp, &utc );
535 p += snprintf(p, end-p, " time=%s", asctime( &utc ) );
536 D(temp);
537 #endif
538 if (r->callback) {
539 r->callback( &r->fix );
540 }
541 else {
542 D("no callback, keeping data until needed !");
543 }
544 }
545 }
546
547
548 static void
nmea_reader_addc(NmeaReader * r,int c)549 nmea_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 */
581 enum {
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 */
589 typedef struct {
590 int init;
591 int fd;
592 GpsCallbacks callbacks;
593 pthread_t thread;
594 int control[2];
595 } GpsState;
596
597 static GpsState _gps_state[1];
598
599
600 static void
gps_state_done(GpsState * s)601 gps_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
619 static void
gps_state_start(GpsState * s)620 gps_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
634 static void
gps_state_stop(GpsState * s)635 gps_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
649 static int
epoll_register(int epoll_fd,int fd)650 epoll_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
668 static int
epoll_deregister(int epoll_fd,int fd)669 epoll_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 */
682 static void
gps_state_thread(void * arg)683 gps_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 GpsStatus gps_status;
692 gps_status.size = sizeof(gps_status);
693 GpsSvStatus gps_sv_status;
694 memset(&gps_sv_status, 0, sizeof(gps_sv_status));
695 gps_sv_status.size = sizeof(gps_sv_status);
696 gps_sv_status.num_svs = 1;
697 gps_sv_status.sv_list[0].size = sizeof(gps_sv_status.sv_list[0]);
698 gps_sv_status.sv_list[0].prn = 17;
699 gps_sv_status.sv_list[0].snr = 60.0;
700 gps_sv_status.sv_list[0].elevation = 30.0;
701 gps_sv_status.sv_list[0].azimuth = 30.0;
702
703 nmea_reader_init( reader );
704
705 // register control file descriptors for polling
706 epoll_register( epoll_fd, control_fd );
707 epoll_register( epoll_fd, gps_fd );
708
709 D("gps thread running");
710
711 // now loop
712 for (;;) {
713 struct epoll_event events[2];
714 int ne, nevents;
715
716 int timeout = -1;
717 if (gps_status.status == GPS_STATUS_SESSION_BEGIN) {
718 timeout = 10 * 1000; // 10 seconds
719 }
720 nevents = epoll_wait( epoll_fd, events, 2, timeout );
721 if (state->callbacks.sv_status_cb) {
722 state->callbacks.sv_status_cb(&gps_sv_status);
723 }
724 // update satilite info
725 if (nevents < 0) {
726 if (errno != EINTR)
727 ALOGE("epoll_wait() unexpected error: %s", strerror(errno));
728 continue;
729 }
730 D("gps thread received %d events", nevents);
731 for (ne = 0; ne < nevents; ne++) {
732 if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
733 ALOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
734 return;
735 }
736 if ((events[ne].events & EPOLLIN) != 0) {
737 int fd = events[ne].data.fd;
738
739 if (fd == control_fd)
740 {
741 char cmd = 255;
742 int ret;
743 D("gps control fd event");
744 do {
745 ret = read( fd, &cmd, 1 );
746 } while (ret < 0 && errno == EINTR);
747
748 if (cmd == CMD_QUIT) {
749 D("gps thread quitting on demand");
750 return;
751 }
752 else if (cmd == CMD_START) {
753 if (!started) {
754 D("gps thread starting location_cb=%p", state->callbacks.location_cb);
755 started = 1;
756 nmea_reader_set_callback( reader, state->callbacks.location_cb );
757 gps_status.status = GPS_STATUS_SESSION_BEGIN;
758 if (state->callbacks.status_cb) {
759 state->callbacks.status_cb(&gps_status);
760 }
761 }
762 }
763 else if (cmd == CMD_STOP) {
764 if (started) {
765 D("gps thread stopping");
766 started = 0;
767 nmea_reader_set_callback( reader, NULL );
768 gps_status.status = GPS_STATUS_SESSION_END;
769 if (state->callbacks.status_cb) {
770 state->callbacks.status_cb(&gps_status);
771 }
772 }
773 }
774 }
775 else if (fd == gps_fd)
776 {
777 char buff[32];
778 D("gps fd event");
779 for (;;) {
780 int nn, ret;
781
782 ret = read( fd, buff, sizeof(buff) );
783 if (ret < 0) {
784 if (errno == EINTR)
785 continue;
786 if (errno != EWOULDBLOCK)
787 ALOGE("error while reading from gps daemon socket: %s:", strerror(errno));
788 break;
789 }
790 D("received %d bytes: %.*s", ret, ret, buff);
791 for (nn = 0; nn < ret; nn++)
792 nmea_reader_addc( reader, buff[nn] );
793 }
794 D("gps fd event end");
795 }
796 else
797 {
798 ALOGE("epoll_wait() returned unkown fd %d ?", fd);
799 }
800 }
801 }
802 }
803 }
804
805
806 static void
gps_state_init(GpsState * state,GpsCallbacks * callbacks)807 gps_state_init( GpsState* state, GpsCallbacks* callbacks )
808 {
809 state->init = 1;
810 state->control[0] = -1;
811 state->control[1] = -1;
812 state->fd = -1;
813
814 state->fd = qemu_pipe_open(QEMU_CHANNEL_NAME);
815
816 if (state->fd < 0) {
817 D("no gps emulation detected");
818 return;
819 }
820
821 D("gps emulation will read from '%s' qemu pipe", QEMU_CHANNEL_NAME );
822
823 if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
824 ALOGE("could not create thread control socket pair: %s", strerror(errno));
825 goto Fail;
826 }
827
828 state->thread = callbacks->create_thread_cb( "gps_state_thread", gps_state_thread, state );
829
830 if ( !state->thread ) {
831 ALOGE("could not create gps thread: %s", strerror(errno));
832 goto Fail;
833 }
834
835 state->callbacks = *callbacks;
836
837 // Explicitly initialize capabilities
838 state->callbacks.set_capabilities_cb(0);
839
840
841 // Setup system info, we are pre 2016 hardware.
842 GnssSystemInfo sysinfo;
843 sysinfo.size = sizeof(GnssSystemInfo);
844 sysinfo.year_of_hw = 2015;
845 state->callbacks.set_system_info_cb(&sysinfo);
846
847 D("gps state initialized");
848 return;
849
850 Fail:
851 gps_state_done( state );
852 }
853
854
855 /*****************************************************************/
856 /*****************************************************************/
857 /***** *****/
858 /***** I N T E R F A C E *****/
859 /***** *****/
860 /*****************************************************************/
861 /*****************************************************************/
862
863
864 static int
qemu_gps_init(GpsCallbacks * callbacks)865 qemu_gps_init(GpsCallbacks* callbacks)
866 {
867 GpsState* s = _gps_state;
868
869 if (!s->init)
870 gps_state_init(s, callbacks);
871
872 if (s->fd < 0)
873 return -1;
874
875 return 0;
876 }
877
878 static void
qemu_gps_cleanup(void)879 qemu_gps_cleanup(void)
880 {
881 GpsState* s = _gps_state;
882
883 if (s->init)
884 gps_state_done(s);
885 }
886
887
888 static int
qemu_gps_start()889 qemu_gps_start()
890 {
891 GpsState* s = _gps_state;
892
893 if (!s->init) {
894 D("%s: called with uninitialized state !!", __FUNCTION__);
895 return -1;
896 }
897
898 D("%s: called", __FUNCTION__);
899 gps_state_start(s);
900 return 0;
901 }
902
903
904 static int
qemu_gps_stop()905 qemu_gps_stop()
906 {
907 GpsState* s = _gps_state;
908
909 if (!s->init) {
910 D("%s: called with uninitialized state !!", __FUNCTION__);
911 return -1;
912 }
913
914 D("%s: called", __FUNCTION__);
915 gps_state_stop(s);
916 return 0;
917 }
918
919
920 static int
qemu_gps_inject_time(GpsUtcTime __unused time,int64_t __unused timeReference,int __unused uncertainty)921 qemu_gps_inject_time(GpsUtcTime __unused time,
922 int64_t __unused timeReference,
923 int __unused uncertainty)
924 {
925 return 0;
926 }
927
928 static int
qemu_gps_inject_location(double __unused latitude,double __unused longitude,float __unused accuracy)929 qemu_gps_inject_location(double __unused latitude,
930 double __unused longitude,
931 float __unused accuracy)
932 {
933 return 0;
934 }
935
936 static void
qemu_gps_delete_aiding_data(GpsAidingData __unused flags)937 qemu_gps_delete_aiding_data(GpsAidingData __unused flags)
938 {
939 }
940
qemu_gps_set_position_mode(GpsPositionMode __unused mode,GpsPositionRecurrence __unused recurrence,uint32_t __unused min_interval,uint32_t __unused preferred_accuracy,uint32_t __unused preferred_time)941 static int qemu_gps_set_position_mode(GpsPositionMode __unused mode,
942 GpsPositionRecurrence __unused recurrence,
943 uint32_t __unused min_interval,
944 uint32_t __unused preferred_accuracy,
945 uint32_t __unused preferred_time)
946 {
947 // FIXME - support fix_frequency
948 return 0;
949 }
950
951 static const void*
qemu_gps_get_extension(const char * __unused name)952 qemu_gps_get_extension(const char* __unused name)
953 {
954 // no extensions supported
955 return NULL;
956 }
957
958 static const GpsInterface qemuGpsInterface = {
959 sizeof(GpsInterface),
960 qemu_gps_init,
961 qemu_gps_start,
962 qemu_gps_stop,
963 qemu_gps_cleanup,
964 qemu_gps_inject_time,
965 qemu_gps_inject_location,
966 qemu_gps_delete_aiding_data,
967 qemu_gps_set_position_mode,
968 qemu_gps_get_extension,
969 };
970
gps__get_gps_interface(struct gps_device_t * __unused dev)971 const GpsInterface* gps__get_gps_interface(struct gps_device_t* __unused dev)
972 {
973 return &qemuGpsInterface;
974 }
975
open_gps(const struct hw_module_t * module,char const * __unused name,struct hw_device_t ** device)976 static int open_gps(const struct hw_module_t* module,
977 char const* __unused name,
978 struct hw_device_t** device)
979 {
980 struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));
981 memset(dev, 0, sizeof(*dev));
982
983 dev->common.tag = HARDWARE_DEVICE_TAG;
984 dev->common.version = 0;
985 dev->common.module = (struct hw_module_t*)module;
986 // dev->common.close = (int (*)(struct hw_device_t*))close_lights;
987 dev->get_gps_interface = gps__get_gps_interface;
988
989 *device = (struct hw_device_t*)dev;
990 return 0;
991 }
992
993
994 static struct hw_module_methods_t gps_module_methods = {
995 .open = open_gps
996 };
997
998 struct hw_module_t HAL_MODULE_INFO_SYM = {
999 .tag = HARDWARE_MODULE_TAG,
1000 .version_major = 1,
1001 .version_minor = 0,
1002 .id = GPS_HARDWARE_MODULE_ID,
1003 .name = "Goldfish GPS Module",
1004 .author = "The Android Open Source Project",
1005 .methods = &gps_module_methods,
1006 };
1007