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