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