3 ** Copyright 2006, The Android Open Source Project
4 ** Copyright 2009, Michael Trimarchi <michael@panicking.kicks-ass.org>
5 ** Copyright 2011, Eduardo José Tagle <ejtagle@tutopia.com>
7 ** This program is free software; you can redistribute it and/or modify it under
8 ** the terms of the GNU General Public License as published by the Free
9 ** Software Foundation; either version 2, or (at your option) any later
12 ** This program is distributed in the hope that it will be useful, but WITHOUT
13 ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 ** You should have received a copy of the GNU General Public License along with
18 ** this program; if not, write to the Free Software Foundation, Inc., 59
19 ** Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include <sys/epoll.h>
29 #include <semaphore.h>
32 #include <linux/socket.h>
33 #include <sys/socket.h>
35 #define LOG_TAG "gps_huawei"
37 #include <cutils/log.h>
38 #include <cutils/sockets.h>
39 #include <cutils/properties.h>
40 #include <hardware/gps.h>
43 # define D(...) ALOGD(__VA_ARGS__)
45 # define D(...) ((void)0)
48 typedef void (*start_t)(void*);
50 /* Nmea Parser stuff */
51 #define NMEA_MAX_SIZE 200
67 GpsSvStatus sv_status;
68 int sv_status_changed;
69 char in[ NMEA_MAX_SIZE+1 ];
77 GpsCallbacks callbacks;
78 AGpsCallbacks a_callbacks;
79 GpsXtraCallbacks xtra_callbacks;
87 int min_interval; // in ms
93 /* Since NMEA parser requires lcoks */
94 #define GPS_STATE_LOCK_FIX(_s) \
98 ret = sem_wait(&(_s)->fix_sem); \
99 } while (ret < 0 && errno == EINTR); \
102 #define GPS_STATE_UNLOCK_FIX(_s) \
103 sem_post(&(_s)->fix_sem)
105 static GpsState _gps_state[1];
106 static GpsState *gps_state = _gps_state;
108 #define GPS_POWER_IF "/sys/bus/platform/devices/shuttle-pm-gps/power_on"
110 #define GPS_DEV_SLOW_UPDATE_RATE (10)
111 #define GPS_DEV_HIGH_UPDATE_RATE (1)
113 static void dev_start(int fd);
114 static void dev_stop(int fd);
115 static void *gps_timer_thread( void* arg );
118 static void serial_write(int fd, char *msg)
128 ret = write(fd, msg + n, i - n);
130 if (ret < 0 && errno == EINTR) {
142 /*****************************************************************/
143 /*****************************************************************/
145 /***** D E V I C E *****/
147 /*****************************************************************/
148 /*****************************************************************/
150 static void dev_power(int state)
152 char prop[PROPERTY_VALUE_MAX];
158 if (property_get("gps.power_on",prop,GPS_POWER_IF) == 0) {
159 ALOGE("no gps power interface name");
164 fd = open( prop, O_RDWR );
165 } while (fd < 0 && errno == EINTR);
168 ALOGE("could not open gps power interface: %s", prop );
179 ret = write( fd, &cmd, 1 );
180 } while (ret < 0 && errno == EINTR);
184 D("gps power state = %c", cmd);
194 static void dev_start(int fd)
196 D("gps dev start initiated");
197 // Set full message rate
198 //RvdB dev_set_message_rate(fd, GPS_DEV_HIGH_UPDATE_RATE);
201 static void dev_stop(int fd)
205 // Set slow message rate
206 //RvdB dev_set_message_rate(fd, GPS_DEV_SLOW_UPDATE_RATE);
209 /*****************************************************************/
210 /*****************************************************************/
212 /***** N M E A T O K E N I Z E R *****/
214 /*****************************************************************/
215 /*****************************************************************/
222 #define MAX_NMEA_TOKENS 32
226 Token tokens[ MAX_NMEA_TOKENS ];
229 static int nmea_tokenizer_init( NmeaTokenizer* t, const char* p, const char* end )
234 // the initial '$' is optional
235 if (p < end && p[0] == '$')
238 // remove trailing newline
239 if (end > p && end[-1] == '\n') {
241 if (end > p && end[-1] == '\r')
245 // get rid of checksum at the end of the sentecne
246 if (end >= p+3 && end[-3] == '*') {
253 q = memchr(p, ',', end-p);
257 if (count < MAX_NMEA_TOKENS) {
258 t->tokens[count].p = p;
259 t->tokens[count].end = q;
273 static Token nmea_tokenizer_get( NmeaTokenizer* t, int index )
276 static const char* dummy = "";
278 if (index < 0 || index >= t->count) {
279 tok.p = tok.end = dummy;
281 tok = t->tokens[index];
287 static int str2int( const char* p, const char* end )
296 for ( ; len > 0; len--, p++ )
304 if ((unsigned)c >= 10)
307 result = result*10 + c;
315 static double str2float( const char* p, const char* end )
325 if (len >= (int)sizeof(temp))
328 memcpy( temp, p, len );
330 return strtod( temp, NULL );
333 /** @desc Convert struct tm to time_t (time zone neutral).
335 * The one missing function in libc: It works basically like mktime, with the main difference that
336 * it does no time zone-related processing but interprets the members of the struct tm as UTC.
337 * Unlike mktime, it will not modify any fields of the tm structure; if you need this behavior, call
338 * mktime before this function.
340 * @param t Pointer to a struct tm containing date and time. Only the tm_year, tm_mon, tm_mday,
341 * tm_hour, tm_min and tm_sec members will be evaluated, all others will be ignored.
343 * @return The epoch time (seconds since 1970-01-01 00:00:00 UTC) which corresponds to t.
345 * @author Originally written by Philippe De Muyter <phdm@macqel.be> for Lynx.
346 * http://lynx.isc.org/current/lynx2-8-8/src/mktime.c
349 static time_t mkgmtime(struct tm *t)
353 static const int m_to_d[12] =
354 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
357 year = t->tm_year + month / 12 + 1900;
363 result = (year - 1970) * 365 + m_to_d[month];
366 result += (year - 1968) / 4;
367 result -= (year - 1900) / 100;
368 result += (year - 1600) / 400;
369 result += t->tm_mday;
372 result += t->tm_hour;
380 /*****************************************************************/
381 /*****************************************************************/
383 /***** N M E A P A R S E R *****/
385 /*****************************************************************/
386 /*****************************************************************/
388 static void nmea_reader_update_utc_diff( NmeaReader* r )
390 time_t now = time(NULL);
393 long time_local, time_utc;
395 gmtime_r( &now, &tm_utc );
396 localtime_r( &now, &tm_local );
398 time_local = mktime(&tm_local);
399 time_utc = mktime(&tm_utc);
401 r->utc_diff = time_local - time_utc;
405 static void nmea_reader_init( NmeaReader* r )
408 memset( r, 0, sizeof(*r) );
410 // Initialize the sizes of all the structs we use
411 r->fix.size = sizeof(GpsLocation);
412 r->sv_status.size = sizeof(GpsSvStatus);
413 for (i = 0; i < GPS_MAX_SVS; i++) {
414 r->sv_status.sv_list[i].size = sizeof(GpsSvInfo);
423 // not sure if we still need this (this module doesn't use utc_diff)
424 nmea_reader_update_utc_diff( r );
427 static int nmea_reader_update_time( NmeaReader* r, Token tok )
429 int hour, minute, seconds, milliseconds;
433 if (tok.p + 6 > tok.end)
436 if (r->utc_year < 0) {
437 // no date, can't return valid timestamp (never ever make up a date, this could wreak havoc)
442 tm.tm_year = r->utc_year - 1900;
443 tm.tm_mon = r->utc_mon - 1;
444 tm.tm_mday = r->utc_day;
447 hour = str2int(tok.p, tok.p+2);
448 minute = str2int(tok.p+2, tok.p+4);
449 seconds = str2int(tok.p+4, tok.p+6);
451 // parse also milliseconds (if present) for better precision
453 if (tok.end - (tok.p+7) == 2) {
454 milliseconds = str2int(tok.p+7, tok.end) * 10;
456 else if (tok.end - (tok.p+7) == 1) {
457 milliseconds = str2int(tok.p+7, tok.end) * 100;
459 else if (tok.end - (tok.p+7) >= 3) {
460 milliseconds = str2int(tok.p+7, tok.p+10);
463 // the following is only guaranteed to work if we have previously set a correct date, so be sure
464 // to always do that before
470 fix_time = mkgmtime( &tm );
472 r->fix.timestamp = (long long)fix_time * 1000 + milliseconds;
476 static int nmea_reader_update_cdate( NmeaReader* r, Token tok_d, Token tok_m, Token tok_y )
479 if ( (tok_d.p + 2 > tok_d.end) ||
480 (tok_m.p + 2 > tok_m.end) ||
481 (tok_y.p + 4 > tok_y.end) )
484 r->utc_day = str2int(tok_d.p, tok_d.p+2);
485 r->utc_mon = str2int(tok_m.p, tok_m.p+2);
486 r->utc_year = str2int(tok_y.p, tok_y.p+4);
491 static int nmea_reader_update_date( NmeaReader* r, Token date, Token time )
496 if (tok.p + 6 != tok.end) {
497 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
500 day = str2int(tok.p, tok.p+2);
501 mon = str2int(tok.p+2, tok.p+4);
502 year = str2int(tok.p+4, tok.p+6) + 2000;
504 if ((day|mon|year) < 0) {
505 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
513 return nmea_reader_update_time( r, time );
517 static double convert_from_hhmm( Token tok )
519 double val = str2float(tok.p, tok.end);
520 int degrees = (int)(floor(val) / 100);
521 double minutes = val - degrees*100.;
522 double dcoord = degrees + minutes / 60.0;
527 static int nmea_reader_update_latlong( NmeaReader* r,
537 if (tok.p + 6 > tok.end) {
538 D("latitude is too short: '%.*s'", tok.end-tok.p, tok.p);
541 lat = convert_from_hhmm(tok);
542 if (latitudeHemi == 'S')
546 if (tok.p + 6 > tok.end) {
547 D("longitude is too short: '%.*s'", tok.end-tok.p, tok.p);
550 lon = convert_from_hhmm(tok);
551 if (longitudeHemi == 'W')
554 r->fix.flags |= GPS_LOCATION_HAS_LAT_LONG;
555 r->fix.latitude = lat;
556 r->fix.longitude = lon;
561 static int nmea_reader_update_altitude( NmeaReader* r,
566 Token tok = altitude;
568 if (tok.p >= tok.end)
571 r->fix.flags |= GPS_LOCATION_HAS_ALTITUDE;
572 r->fix.altitude = str2float(tok.p, tok.end);
576 static int nmea_reader_update_accuracy( NmeaReader* r,
580 Token tok = accuracy;
582 if (tok.p >= tok.end)
585 r->fix.accuracy = str2float(tok.p, tok.end);
587 if (r->fix.accuracy == 99.99){
591 r->fix.flags |= GPS_LOCATION_HAS_ACCURACY;
595 static int nmea_reader_update_bearing( NmeaReader* r,
601 if (tok.p >= tok.end)
604 r->fix.flags |= GPS_LOCATION_HAS_BEARING;
605 r->fix.bearing = str2float(tok.p, tok.end);
610 static int nmea_reader_update_speed( NmeaReader* r,
616 if (tok.p >= tok.end)
619 r->fix.flags |= GPS_LOCATION_HAS_SPEED;
620 // convert knots into m/sec (1 knot equals 1.852 km/h, 1 km/h equals 3.6 m/s)
621 // since 1.852 / 3.6 is an odd value (periodic), we're calculating the quotient on the fly
622 // to obtain maximum precision (we don't want 1.9999 instead of 2)
623 r->fix.speed = str2float(tok.p, tok.end) * 1.852 / 3.6;
628 static void nmea_reader_parse( NmeaReader* r )
630 /* we received a complete sentence, now parse it to generate
633 NmeaTokenizer tzer[1];
636 D("Received: '%.*s'", r->pos, r->in);
639 D("Too short. discarded.");
643 nmea_tokenizer_init(tzer, r->in, r->in + r->pos);
647 D("Found %d tokens", tzer->count);
648 for (n = 0; n < tzer->count; n++) {
649 Token tok = nmea_tokenizer_get(tzer,n);
650 D("%2d: '%.*s'", n, tok.end-tok.p, tok.p);
655 tok = nmea_tokenizer_get(tzer, 0);
657 if (tok.p + 5 > tok.end) {
658 D("sentence id '%.*s' too short, ignored.", tok.end-tok.p, tok.p);
662 // ignore first two characters.
665 if ( !memcmp(tok.p, "GGA", 3) ) {
667 Token tok_fixstaus = nmea_tokenizer_get(tzer,6);
669 if ((tok_fixstaus.p[0] > '0') && (r->utc_year >= 0)) {
670 // ignore this until we have a valid timestamp
672 Token tok_time = nmea_tokenizer_get(tzer,1);
673 Token tok_latitude = nmea_tokenizer_get(tzer,2);
674 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,3);
675 Token tok_longitude = nmea_tokenizer_get(tzer,4);
676 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,5);
677 Token tok_altitude = nmea_tokenizer_get(tzer,9);
678 Token tok_altitudeUnits = nmea_tokenizer_get(tzer,10);
680 // don't use this as we have no fractional seconds and no date; there are better ways to
681 // get a good timestamp from GPS
682 //nmea_reader_update_time(r, tok_time);
683 nmea_reader_update_latlong(r, tok_latitude,
684 tok_latitudeHemi.p[0],
686 tok_longitudeHemi.p[0]);
687 nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits);
690 } else if ( !memcmp(tok.p, "GLL", 3) ) {
692 Token tok_fixstaus = nmea_tokenizer_get(tzer,6);
694 if ((tok_fixstaus.p[0] == 'A') && (r->utc_year >= 0)) {
695 // ignore this until we have a valid timestamp
697 Token tok_latitude = nmea_tokenizer_get(tzer,1);
698 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,2);
699 Token tok_longitude = nmea_tokenizer_get(tzer,3);
700 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,4);
701 Token tok_time = nmea_tokenizer_get(tzer,5);
703 // don't use this as we have no fractional seconds and no date; there are better ways to
704 // get a good timestamp from GPS
705 //nmea_reader_update_time(r, tok_time);
706 nmea_reader_update_latlong(r, tok_latitude,
707 tok_latitudeHemi.p[0],
709 tok_longitudeHemi.p[0]);
712 } else if ( !memcmp(tok.p, "GSA", 3) ) {
714 Token tok_fixStatus = nmea_tokenizer_get(tzer, 2);
717 if (tok_fixStatus.p[0] != '\0' && tok_fixStatus.p[0] != '1') {
719 Token tok_accuracy = nmea_tokenizer_get(tzer, 15);
721 nmea_reader_update_accuracy(r, tok_accuracy);
723 r->sv_status.used_in_fix_mask = 0ul;
725 for (i = 3; i <= 14; ++i){
727 Token tok_prn = nmea_tokenizer_get(tzer, i);
728 int prn = str2int(tok_prn.p, tok_prn.end);
731 r->sv_status.used_in_fix_mask |= (1ul << (32 - prn));
732 r->sv_status_changed = 1;
733 D("%s: fix mask is %d", __FUNCTION__, r->sv_status.used_in_fix_mask);
740 } else if ( !memcmp(tok.p, "GSV", 3) ) {
742 Token tok_noSatellites = nmea_tokenizer_get(tzer, 3);
743 int noSatellites = str2int(tok_noSatellites.p, tok_noSatellites.end);
745 if (noSatellites > 0) {
747 Token tok_noSentences = nmea_tokenizer_get(tzer, 1);
748 Token tok_sentence = nmea_tokenizer_get(tzer, 2);
750 int sentence = str2int(tok_sentence.p, tok_sentence.end);
751 int totalSentences = str2int(tok_noSentences.p, tok_noSentences.end);
756 r->sv_status_changed = 0;
757 r->sv_status.num_svs = 0;
760 curr = r->sv_status.num_svs;
764 while (i < 4 && r->sv_status.num_svs < noSatellites){
766 Token tok_prn = nmea_tokenizer_get(tzer, i * 4 + 4);
767 Token tok_elevation = nmea_tokenizer_get(tzer, i * 4 + 5);
768 Token tok_azimuth = nmea_tokenizer_get(tzer, i * 4 + 6);
769 Token tok_snr = nmea_tokenizer_get(tzer, i * 4 + 7);
771 r->sv_status.sv_list[curr].prn = str2int(tok_prn.p, tok_prn.end);
772 r->sv_status.sv_list[curr].elevation = str2float(tok_elevation.p, tok_elevation.end);
773 r->sv_status.sv_list[curr].azimuth = str2float(tok_azimuth.p, tok_azimuth.end);
774 r->sv_status.sv_list[curr].snr = str2float(tok_snr.p, tok_snr.end);
776 r->sv_status.num_svs += 1;
783 if (sentence == totalSentences) {
784 r->sv_status_changed = 1;
787 D("%s: GSV message with total satellites %d", __FUNCTION__, noSatellites);
791 } else if ( !memcmp(tok.p, "RMC", 3) ) {
793 Token tok_fixStatus = nmea_tokenizer_get(tzer,2);
795 if (tok_fixStatus.p[0] == 'A')
797 Token tok_time = nmea_tokenizer_get(tzer,1);
798 Token tok_latitude = nmea_tokenizer_get(tzer,3);
799 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,4);
800 Token tok_longitude = nmea_tokenizer_get(tzer,5);
801 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,6);
802 Token tok_speed = nmea_tokenizer_get(tzer,7);
803 Token tok_bearing = nmea_tokenizer_get(tzer,8);
804 Token tok_date = nmea_tokenizer_get(tzer,9);
806 nmea_reader_update_date( r, tok_date, tok_time );
808 nmea_reader_update_latlong( r, tok_latitude,
809 tok_latitudeHemi.p[0],
811 tok_longitudeHemi.p[0] );
813 nmea_reader_update_bearing( r, tok_bearing );
814 nmea_reader_update_speed ( r, tok_speed );
817 } else if ( !memcmp(tok.p, "VTG", 3) ) {
819 Token tok_fixStatus = nmea_tokenizer_get(tzer,9);
821 if (tok_fixStatus.p[0] != '\0' && tok_fixStatus.p[0] != 'N')
823 Token tok_bearing = nmea_tokenizer_get(tzer,1);
824 Token tok_speed = nmea_tokenizer_get(tzer,5);
826 nmea_reader_update_bearing( r, tok_bearing );
827 nmea_reader_update_speed ( r, tok_speed );
830 } else if ( !memcmp(tok.p, "ZDA", 3) ) {
833 Token tok_year = nmea_tokenizer_get(tzer,4);
834 tok_time = nmea_tokenizer_get(tzer,1);
836 if ((tok_year.p[0] != '\0') && (tok_time.p[0] != '\0')) {
838 // make sure to always set date and time together, lest bad things happen
839 Token tok_day = nmea_tokenizer_get(tzer,2);
840 Token tok_mon = nmea_tokenizer_get(tzer,3);
842 nmea_reader_update_cdate( r, tok_day, tok_mon, tok_year );
843 nmea_reader_update_time(r, tok_time);
849 D("unknown sentence '%.*s", tok.end-tok.p, tok.p);
853 if (r->fix.flags != 0) {
857 char* end = p + sizeof(temp);
860 p += snprintf( p, end-p, "sending fix" );
861 if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
862 p += snprintf(p, end-p, " lat=%g lon=%g", r->fix.latitude, r->fix.longitude);
864 if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) {
865 p += snprintf(p, end-p, " altitude=%g", r->fix.altitude);
867 if (r->fix.flags & GPS_LOCATION_HAS_SPEED) {
868 p += snprintf(p, end-p, " speed=%g", r->fix.speed);
870 if (r->fix.flags & GPS_LOCATION_HAS_BEARING) {
871 p += snprintf(p, end-p, " bearing=%g", r->fix.bearing);
873 if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) {
874 p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy);
876 gmtime_r( (time_t*) &r->fix.timestamp, &utc );
877 p += snprintf(p, end-p, " time=%s", asctime( &utc ) );
884 static void nmea_reader_addc( NmeaReader* r, int c )
887 r->overflow = (c != '\n');
891 if (r->pos >= (int) sizeof(r->in)-1 ) {
897 r->in[r->pos] = (char)c;
901 nmea_reader_parse( r );
906 /*****************************************************************/
907 /*****************************************************************/
909 /***** C O N N E C T I O N S T A T E *****/
911 /*****************************************************************/
912 /*****************************************************************/
914 /* commands sent to the gps thread */
922 static void gps_state_start( GpsState* s )
924 char cmd = CMD_START;
927 do { ret=write( s->control[0], &cmd, 1 ); }
928 while (ret < 0 && errno == EINTR);
931 D("%s: could not send CMD_START command: ret=%d: %s",
932 __FUNCTION__, ret, strerror(errno));
936 static void gps_state_stop( GpsState* s )
941 do { ret=write( s->control[0], &cmd, 1 ); }
942 while (ret < 0 && errno == EINTR);
945 D("%s: could not send CMD_STOP command: ret=%d: %s",
946 __FUNCTION__, ret, strerror(errno));
950 static int epoll_register( int epoll_fd, int fd )
952 struct epoll_event ev;
955 /* important: make the fd non-blocking */
956 flags = fcntl(fd, F_GETFL);
957 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
962 ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
963 } while (ret < 0 && errno == EINTR);
968 static void gps_nmea_thread_cb( GpsState* state )
970 D("%s()", __FUNCTION__ );
971 state->callbacks.nmea_cb(state->reader.fix.timestamp,&state->nmea_buf[0],state->nmea_len);
972 GPS_STATE_UNLOCK_FIX(state);
975 static void gps_nmea_cb( GpsState* state , const char* buf, int len)
977 D("%s()", __FUNCTION__ );
978 // Forward NMEA sentences ....
979 if (state->callbacks.nmea_cb) {
981 GPS_STATE_LOCK_FIX(state);
982 memcpy(&state->nmea_buf[0],buf,len);
983 state->nmea_buf[len] = 0;
984 state->nmea_len = len;
985 state->callbacks.create_thread_cb("nmea",(start_t)gps_nmea_thread_cb,(void*)state);
989 static void gps_status_thread_cb( GpsState* state )
991 D("%s()", __FUNCTION__ );
992 state->callbacks.status_cb(&state->gps_status);
993 GPS_STATE_UNLOCK_FIX(state);
996 static void gps_status_cb( GpsState* state , GpsStatusValue status)
998 D("%s()", __FUNCTION__ );
999 if (state->callbacks.status_cb) {
1000 GPS_STATE_LOCK_FIX(state);
1002 state->gps_status.size = sizeof(GpsStatus);
1003 state->gps_status.status = status;
1004 state->callbacks.create_thread_cb("status",(start_t)gps_status_thread_cb,(void*)state);
1006 D("gps status callback: 0x%x", status);
1010 static void gps_set_capabilities_cb( GpsState* state, unsigned long caps)
1012 D("%s()", __FUNCTION__ );
1013 if (state->callbacks.set_capabilities_cb) {
1014 state->callbacks.create_thread_cb("caps",(start_t)state->callbacks.set_capabilities_cb,(void*)caps);
1018 static void gps_location_thread_cb( GpsState* state )
1020 D("%s()", __FUNCTION__ );
1021 state->callbacks.location_cb( &state->reader.fix );
1022 state->reader.fix.flags = 0;
1023 GPS_STATE_UNLOCK_FIX(state);
1027 static void gps_location_cb( GpsState* state )
1029 D("%s()", __FUNCTION__ );
1030 if (state->callbacks.location_cb) {
1031 GPS_STATE_LOCK_FIX(state);
1032 state->callbacks.create_thread_cb("fix",(start_t)gps_location_thread_cb,(void*)state);
1036 static void gps_sv_status_thread_cb( GpsState* state )
1038 D("%s()", __FUNCTION__ );
1039 state->callbacks.sv_status_cb( &state->reader.sv_status );
1040 state->reader.sv_status_changed = 0;
1041 GPS_STATE_UNLOCK_FIX(state);
1045 static void gps_sv_status_cb( GpsState* state )
1047 D("%s()", __FUNCTION__ );
1048 if (state->callbacks.sv_status_cb) {
1049 GPS_STATE_LOCK_FIX(state);
1050 state->callbacks.create_thread_cb("sv-status",(start_t)gps_sv_status_thread_cb,(void*)state);
1055 /* this is the main thread, it waits for commands from gps_state_start/stop and,
1056 * when started, messages from the QEMU GPS daemon. these are simple NMEA sentences
1057 * that must be parsed to be converted into GPS fixes sent to the framework
1059 static void* gps_state_thread( void* arg )
1061 GpsState* state = (GpsState*) arg;
1063 int epoll_fd = epoll_create(2);
1065 int gps_fd = state->fd;
1066 int control_fd = state->control[1];
1068 reader = &state->reader;
1070 nmea_reader_init( reader );
1072 // register control file descriptors for polling
1073 epoll_register( epoll_fd, control_fd );
1074 epoll_register( epoll_fd, gps_fd );
1076 D("gps thread running");
1078 gps_set_capabilities_cb( state , GPS_CAPABILITY_MSA | GPS_CAPABILITY_MSB );
1080 D("after set capabilities");
1082 gps_status_cb( state , GPS_STATUS_ENGINE_ON);
1084 D("after set status");
1088 struct epoll_event events[2];
1091 nevents = epoll_wait( epoll_fd, events, 2, -1 );
1094 ALOGE("epoll_wait() unexpected error: %s", strerror(errno));
1097 D("gps thread received %d events", nevents);
1098 for (ne = 0; ne < nevents; ne++) {
1099 if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
1100 ALOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
1103 if ((events[ne].events & EPOLLIN) != 0) {
1104 int fd = events[ne].data.fd;
1106 if (fd == control_fd)
1110 D("gps control fd event");
1112 ret = read( fd, &cmd, 1 );
1113 } while (ret < 0 && errno == EINTR);
1115 if (cmd == CMD_QUIT) {
1116 D("gps thread quitting on demand");
1119 else if (cmd == CMD_START) {
1121 D("gps thread starting location_cb=%p", state->callbacks.location_cb);
1126 gps_status_cb( state , GPS_STATUS_SESSION_BEGIN);
1128 state->init = STATE_START;
1130 if ( pthread_create( &state->tmr_thread, NULL, gps_timer_thread, state ) != 0 ) {
1131 ALOGE("could not create gps timer thread: %s", strerror(errno));
1133 state->init = STATE_INIT;
1139 else if (cmd == CMD_STOP) {
1142 D("gps thread stopping");
1147 state->init = STATE_INIT;
1149 pthread_join(state->tmr_thread, &dummy);
1151 gps_status_cb( state , GPS_STATUS_SESSION_END);
1156 else if (fd == gps_fd)
1162 ret = read( fd, buf, sizeof(buf) );
1163 } while (ret < 0 && errno == EINTR);
1167 gps_nmea_cb( state , &buf[0], ret);
1169 GPS_STATE_LOCK_FIX(state);
1170 for (nn = 0; nn < ret; nn++)
1171 nmea_reader_addc( reader, buf[nn] );
1172 GPS_STATE_UNLOCK_FIX(state);
1175 D("gps fd event end");
1179 ALOGE("epoll_wait() returned unkown fd %d ?", fd);
1186 gps_status_cb( state , GPS_STATUS_ENGINE_OFF);
1191 static void* gps_timer_thread( void* arg )
1194 GpsState *state = (GpsState *)arg;
1196 D("gps entered timer thread");
1200 D ("gps timer exp");
1202 if (state->reader.fix.flags != 0) {
1204 D("gps fix cb: 0x%x", state->reader.fix.flags);
1206 gps_location_cb( state );
1209 if (state->reader.sv_status_changed != 0) {
1211 D("gps sv status callback");
1213 gps_sv_status_cb( state );
1217 if (state->min_interval == 0) {
1218 state->min_interval = 1000;
1221 usleep(state->min_interval*1000);
1223 } while(state->init == STATE_START);
1225 D("gps timer thread destroyed");
1231 static int open_serialport( const char* name )
1235 fd = open( name, O_RDWR );
1236 } while (fd < 0 && errno == EINTR);
1239 ALOGE("could not open serial device %s: %s", name, strerror(errno) );
1243 // disable echo on serial lines
1244 if ( isatty( fd ) ) {
1246 tcgetattr( fd, &ios );
1247 ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */
1248 ios.c_oflag &= (~ONLCR); /* Stop \n -> \r\n translation on output */
1249 ios.c_iflag &= (~(ICRNL | INLCR)); /* Stop \r -> \n & \n -> \r translation on input */
1250 ios.c_iflag |= (IGNCR | IXOFF); /* Ignore \r & XON/XOFF on input */
1251 tcsetattr( fd, TCSANOW, &ios );
1257 static void gps_state_done( GpsState* s )
1259 // tell the thread to quit, and wait for it
1260 char cmd = CMD_QUIT;
1264 D("gps send quit command");
1266 do { ret=write( s->control[0], &cmd, 1 ); }
1267 while (ret < 0 && errno == EINTR);
1269 D("gps waiting for command thread to stop");
1271 pthread_join(s->thread, &dummy);
1273 /* Timer thread depends on this state check */
1274 s->init = STATE_QUIT;
1275 s->min_interval = 1000;
1277 // close the control socket pair
1278 close( s->control[0] ); s->control[0] = -1;
1279 close( s->control[1] ); s->control[1] = -1;
1281 // Turn off GPS function
1282 serial_write(s->ctrl_fd,"AT^WPEND\r\n");
1284 // close connection to the GPS
1285 close( s->fd ); s->fd = -1;
1286 close( s->ctrl_fd ); s->ctrl_fd = -1;
1288 // Power down the GPS interface
1291 sem_destroy(&s->fix_sem);
1293 memset(s, 0, sizeof(*s));
1295 D("gps deinit complete");
1300 static void gps_state_init( GpsState* state )
1302 char gps_data[PROPERTY_VALUE_MAX];
1303 char gps_ctrl[PROPERTY_VALUE_MAX];
1304 char gps_supl[PROPERTY_VALUE_MAX];
1305 char buf[PROPERTY_VALUE_MAX+20];
1309 struct sigevent tmr_event;
1311 state->init = STATE_INIT;
1312 state->control[0] = -1;
1313 state->control[1] = -1;
1315 state->ctrl_fd = -1;
1316 state->min_interval = 1000;
1318 if (sem_init(&state->fix_sem, 0, 1) != 0) {
1319 D("gps semaphore initialization failed! errno = %d", errno);
1323 // Power up the GPS interface
1326 // look for a kernel-provided gps_data port
1327 strcpy(gps_data,"/dev/ttyUSB3") ; //RvdB
1329 if (property_get("ro.kernel.gps.data",gps_data,"/dev/ttyUSB3") == 0) {
1330 D("no kernel-provided gps device data name");
1335 state->fd = open_serialport( gps_data );
1336 if (state->fd < 0) {
1337 ALOGE("could not open gps serial device %s: %s", gps_data, strerror(errno) );
1342 D("gps will read from %s", gps_data);
1344 // look for a kernel-provided gps_ctrl port
1345 strcpy(gps_ctrl,"/dev/ttyUSB4") ;//RvdB
1348 if (property_get("ro.kernel.gps.ctl",gps_ctrl,"/dev/ttyUSB4") == 0) {
1349 D("no kernel-provided gps device ctrl name");
1354 state->ctrl_fd = open_serialport( gps_ctrl );
1355 if (state->ctrl_fd < 0) {
1356 ALOGE("could not open gps serial device %s: %s", gps_ctrl, strerror(errno) );
1363 D("gps will be controlled by %s", gps_ctrl);
1365 // Turn on GPS function
1366 serial_write(state->ctrl_fd,"AT^WPEND\r\n");
1368 // look for a kernel-provided supl
1369 strcpy(gps_supl,"http://supl.nokia.com") ; //RvdB
1371 if (property_get("ro.kernel.gps.supl",gps_supl,"http://supl.nokia.com") == 0) {
1372 D("no kernel-provided supl");
1375 close(state->ctrl_fd);
1376 state->ctrl_fd = -1;
1381 sprintf(buf,"AT^WPURL=%s\r\n",gps_supl);
1382 serial_write(state->ctrl_fd,buf);
1384 // configure AGPS to work speed optimal
1385 serial_write(state->ctrl_fd,"AT^WPDOM=2\r\n");
1388 serial_write(state->ctrl_fd,"AT^WPDGP\r\n");
1390 if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
1391 ALOGE("could not create thread control socket pair: %s", strerror(errno));
1396 if ( pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0 ) {
1397 ALOGE("could not create gps thread: %s", strerror(errno));
1402 D("gps state initialized");
1407 gps_state_done( state );
1412 /*****************************************************************/
1413 /*****************************************************************/
1415 /***** I N T E R F A C E *****/
1417 /*****************************************************************/
1418 /*****************************************************************/
1420 static int gps_init(GpsCallbacks* callbacks)
1422 GpsState* s = _gps_state;
1430 s->callbacks = *callbacks;
1435 static void gps_cleanup(void)
1437 GpsState* s = _gps_state;
1444 static int gps_start(void)
1446 GpsState* s = _gps_state;
1449 D("%s: called with uninitialized state !!", __FUNCTION__);
1453 D("%s: called", __FUNCTION__);
1459 static int gps_stop(void)
1461 GpsState* s = _gps_state;
1464 D("%s: called with uninitialized state !!", __FUNCTION__);
1468 D("%s: called", __FUNCTION__);
1474 static int gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty)
1479 /** Injects current location from another location provider
1480 * (typically cell ID).
1481 * latitude and longitude are measured in degrees
1482 * expected accuracy is measured in meters
1484 static int gps_inject_location(double latitude, double longitude, float accuracy)
1489 static void gps_delete_aiding_data(GpsAidingData flags)
1493 static int gps_set_position_mode(GpsPositionMode mode, GpsPositionRecurrence recurrence,
1494 uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time)
1496 GpsState* s = _gps_state;
1499 D("%s: called with uninitialized state !!", __FUNCTION__);
1503 // only standalone supported for now.
1504 if (mode == GPS_POSITION_MODE_STANDALONE) {
1506 if (mode == GPS_POSITION_MODE_MS_BASED ||
1507 mode == GPS_POSITION_MODE_MS_ASSISTED) {
1511 s->min_interval = min_interval;
1513 D("gps fix frquency set to %d secs", min_interval);
1518 /***** AGpsInterface *****/
1521 * Opens the AGPS interface and provides the callback routines
1522 * to the implemenation of this interface.
1524 static void agps_init( AGpsCallbacks* callbacks )
1526 D("%s() is called", __FUNCTION__);
1528 GpsState* s = _gps_state;
1531 D("%s: called with uninitialized state !!", __FUNCTION__);
1535 s->a_callbacks = *callbacks;
1539 * Notifies that a data connection is available and sets
1540 * the name of the APN to be used for SUPL.
1542 static int agps_conn_open( const char* apn )
1544 D("%s() is called", __FUNCTION__);
1547 GpsState* s = _gps_state;
1550 D("%s: called with uninitialized state !!", __FUNCTION__);
1558 * Notifies that the AGPS data connection has been closed.
1560 static int agps_conn_closed( void )
1562 D("%s() is called", __FUNCTION__);
1564 GpsState* s = _gps_state;
1567 D("%s: called with uninitialized state !!", __FUNCTION__);
1575 * Notifies that a data connection is not available for AGPS.
1577 static int agps_conn_failed( void )
1579 D("%s() is called", __FUNCTION__);
1581 GpsState* s = _gps_state;
1584 D("%s: called with uninitialized state !!", __FUNCTION__);
1592 * Sets the hostname and port for the AGPS server.
1594 static int agps_set_server( AGpsType type, const char* hostname, int port )
1598 D("%s() is called", __FUNCTION__);
1599 D("type=%d, hostname=%s, port=%d", type, hostname, port);
1601 GpsState* s = _gps_state;
1604 D("%s: called with uninitialized state !!", __FUNCTION__);
1609 sprintf(buf,"AT^WPURL=%s\r\n",hostname);
1610 serial_write(s->ctrl_fd,buf);
1616 static const AGpsInterface sAGpsInterface = {
1617 sizeof(AGpsInterface),
1627 /***** GpsXtraInterface *****/
1628 static int xtra_init(GpsXtraCallbacks* callbacks)
1630 D("%s() is called", __FUNCTION__);
1631 GpsState* s = _gps_state;
1633 s->xtra_callbacks = *callbacks;
1638 static int xtra_inject_xtra_data(char* data, int length)
1640 D("%s() is called", __FUNCTION__);
1641 D("xtra size = %d, data ptr = 0x%x\n", length, (int) data);
1643 GpsState* s = _gps_state;
1650 static const GpsXtraInterface sGpsXtraInterface = {
1651 sizeof(GpsXtraInterface),
1653 xtra_inject_xtra_data,
1656 static const void* gps_get_extension(const char* name)
1658 D("%s('%s') is called", __FUNCTION__, name);
1660 if (!strcmp(name, GPS_XTRA_INTERFACE)) {
1661 return &sGpsXtraInterface;
1662 } else if (!strcmp(name, AGPS_INTERFACE)) {
1663 return &sAGpsInterface;
1668 static const GpsInterface sGpsInterface = {
1669 sizeof(GpsInterface),
1675 gps_inject_location,
1676 gps_delete_aiding_data,
1677 gps_set_position_mode,
1681 // As under Android, there are no exceptions in C++ base system, we will hide all exported symbols
1682 // except the required ones. This will generate better code!
1685 #define DLL_PUBLIC __attribute__ ((visibility ("default")))
1686 #define DLL_LOCAL __attribute__ ((visibility ("hidden")))
1693 static const GpsInterface* gps_get_hardware_interface(struct gps_device_t* dev)
1695 ALOGV("get_interface was called");
1696 return &sGpsInterface;
1699 static int open_gps(const struct hw_module_t* module, char const* name,
1700 struct hw_device_t** device)
1702 struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));
1703 memset(dev, 0, sizeof(*dev));
1705 dev->common.tag = HARDWARE_DEVICE_TAG;
1706 dev->common.version = 0;
1707 dev->common.module = (struct hw_module_t*)module;
1708 dev->get_gps_interface = gps_get_hardware_interface;
1710 *device = (struct hw_device_t*)dev;
1715 static struct hw_module_methods_t gps_module_methods = {
1719 DLL_PUBLIC struct hw_module_t HAL_MODULE_INFO_SYM = {
1720 .tag = HARDWARE_MODULE_TAG,
1723 .id = GPS_HARDWARE_MODULE_ID,
1724 .name = "GPS Module",
1725 .author = "Eduardo José Tagle",
1726 .methods = &gps_module_methods,