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