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 } 244} 245 246 247static int 248nmea_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 291static int 292nmea_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 318static double 319convert_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 329static int 330nmea_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 364static int 365nmea_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 381static int 382nmea_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 397static int 398nmea_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 412static int 413nmea_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 423static void 424nmea_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 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 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 806static void 807gps_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 850Fail: 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 864static int 865qemu_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 878static void 879qemu_gps_cleanup(void) 880{ 881 GpsState* s = _gps_state; 882 883 if (s->init) 884 gps_state_done(s); 885} 886 887 888static int 889qemu_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 904static int 905qemu_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 920static int 921qemu_gps_inject_time(GpsUtcTime __unused time, 922 int64_t __unused timeReference, 923 int __unused uncertainty) 924{ 925 return 0; 926} 927 928static int 929qemu_gps_inject_location(double __unused latitude, 930 double __unused longitude, 931 float __unused accuracy) 932{ 933 return 0; 934} 935 936static void 937qemu_gps_delete_aiding_data(GpsAidingData __unused flags) 938{ 939} 940 941static 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 951static const void* 952qemu_gps_get_extension(const char* __unused name) 953{ 954 // no extensions supported 955 return NULL; 956} 957 958static 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 971const GpsInterface* gps__get_gps_interface(struct gps_device_t* __unused dev) 972{ 973 return &qemuGpsInterface; 974} 975 976static 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 994static struct hw_module_methods_t gps_module_methods = { 995 .open = open_gps 996}; 997 998struct 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