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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 937 qemu_gps_delete_aiding_data(GpsAidingData __unused flags) 938 { 939 } 940 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* 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 971 const GpsInterface* gps__get_gps_interface(struct gps_device_t* __unused dev) 972 { 973 return &qemuGpsInterface; 974 } 975 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