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>
45 # define D(...) ALOGD(__VA_ARGS__)
47 # define D(...) ((void)0)
50 typedef void (*start_t)(void*);
52 /* Nmea Parser stuff */
53 #define NMEA_MAX_SIZE 200
69 GpsSvStatus sv_status;
70 int sv_status_changed;
71 char in[ NMEA_MAX_SIZE+1 ];
79 GpsCallbacks callbacks;
80 AGpsCallbacks a_callbacks;
81 GpsXtraCallbacks xtra_callbacks;
89 int min_interval; // in ms
95 /* Since NMEA parser requires lcoks */
96 #define GPS_STATE_LOCK_FIX(_s) \
100 ret = sem_wait(&(_s)->fix_sem); \
101 } while (ret < 0 && errno == EINTR); \
104 #define GPS_STATE_UNLOCK_FIX(_s) \
105 sem_post(&(_s)->fix_sem)
107 static GpsState _gps_state[1];
108 static GpsState *gps_state = _gps_state;
110 #define GPS_POWER_IF "/sys/bus/platform/devices/shuttle-pm-gps/power_on"
112 #define GPS_DEV_SLOW_UPDATE_RATE (10)
113 #define GPS_DEV_HIGH_UPDATE_RATE (1)
115 static void dev_start(int fd);
116 static void dev_stop(int fd);
117 static void *gps_timer_thread( void* arg );
120 static void serial_write(int fd, char *msg)
130 ret = write(fd, msg + n, i - n);
132 if (ret < 0 && errno == EINTR) {
143 static unsigned char calc_nmea_csum(char *msg)
145 unsigned char csum = 0;
148 for (i = 1; msg[i] != '*'; ++i) {
156 /*****************************************************************/
157 /*****************************************************************/
159 /***** D E V I C E *****/
161 /*****************************************************************/
162 /*****************************************************************/
164 static void dev_power(int state)
166 char prop[PROPERTY_VALUE_MAX];
172 if (property_get("gps.power_on",prop,GPS_POWER_IF) == 0) {
173 ALOGE("no gps power interface name");
178 fd = open( prop, O_RDWR );
179 } while (fd < 0 && errno == EINTR);
182 ALOGE("could not open gps power interface: %s", prop );
193 ret = write( fd, &cmd, 1 );
194 } while (ret < 0 && errno == EINTR);
198 D("gps power state = %c", cmd);
207 static void dev_set_nmea_message_rate(int fd,const char *msg, int rate)
213 sprintf(buff, "$PUBX,40,%s,%d,%d,%d,0*", msg, rate, rate, rate);
217 sprintf((buff + i), "%02x\r\n", calc_nmea_csum(buff));
219 serial_write(fd, buff);
221 D("gps sent to device: %s", buff);
227 static void dev_set_message_rate(int fd, int rate)
231 static const char *msg[] = {
237 for (i = 0; i < sizeof(msg)/sizeof(msg[0]); ++i) {
238 dev_set_nmea_message_rate(fd, msg[i], rate);
245 static void dev_start(int fd)
247 D("gps dev start initiated");
248 // Set full message rate
249 //RvdB dev_set_message_rate(fd, GPS_DEV_HIGH_UPDATE_RATE);
252 static void dev_stop(int fd)
256 // Set slow message rate
257 //RvdB dev_set_message_rate(fd, GPS_DEV_SLOW_UPDATE_RATE);
260 /*****************************************************************/
261 /*****************************************************************/
263 /***** N M E A T O K E N I Z E R *****/
265 /*****************************************************************/
266 /*****************************************************************/
273 #define MAX_NMEA_TOKENS 32
277 Token tokens[ MAX_NMEA_TOKENS ];
280 static int nmea_tokenizer_init( NmeaTokenizer* t, const char* p, const char* end )
285 // the initial '$' is optional
286 if (p < end && p[0] == '$')
289 // remove trailing newline
290 if (end > p && end[-1] == '\n') {
292 if (end > p && end[-1] == '\r')
296 // get rid of checksum at the end of the sentecne
297 if (end >= p+3 && end[-3] == '*') {
304 q = memchr(p, ',', end-p);
308 if (count < MAX_NMEA_TOKENS) {
309 t->tokens[count].p = p;
310 t->tokens[count].end = q;
324 static Token nmea_tokenizer_get( NmeaTokenizer* t, int index )
327 static const char* dummy = "";
329 if (index < 0 || index >= t->count) {
330 tok.p = tok.end = dummy;
332 tok = t->tokens[index];
338 static int str2int( const char* p, const char* end )
347 for ( ; len > 0; len--, p++ )
355 if ((unsigned)c >= 10)
358 result = result*10 + c;
366 static double str2float( const char* p, const char* end )
376 if (len >= (int)sizeof(temp))
379 memcpy( temp, p, len );
381 return strtod( temp, NULL );
384 /** @desc Convert struct tm to time_t (time zone neutral).
386 * The one missing function in libc: It works basically like mktime, with the main difference that
387 * it does no time zone-related processing but interprets the members of the struct tm as UTC.
388 * Unlike mktime, it will not modify any fields of the tm structure; if you need this behavior, call
389 * mktime before this function.
391 * @param t Pointer to a struct tm containing date and time. Only the tm_year, tm_mon, tm_mday,
392 * tm_hour, tm_min and tm_sec members will be evaluated, all others will be ignored.
394 * @return The epoch time (seconds since 1970-01-01 00:00:00 UTC) which corresponds to t.
396 * @author Originally written by Philippe De Muyter <phdm@macqel.be> for Lynx.
397 * http://lynx.isc.org/current/lynx2-8-8/src/mktime.c
400 static time_t mkgmtime(struct tm *t)
404 static const int m_to_d[12] =
405 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
408 year = t->tm_year + month / 12 + 1900;
414 result = (year - 1970) * 365 + m_to_d[month];
417 result += (year - 1968) / 4;
418 result -= (year - 1900) / 100;
419 result += (year - 1600) / 400;
420 result += t->tm_mday;
423 result += t->tm_hour;
431 /*****************************************************************/
432 /*****************************************************************/
434 /***** N M E A P A R S E R *****/
436 /*****************************************************************/
437 /*****************************************************************/
439 static void nmea_reader_update_utc_diff( NmeaReader* r )
441 time_t now = time(NULL);
444 long time_local, time_utc;
446 gmtime_r( &now, &tm_utc );
447 localtime_r( &now, &tm_local );
449 time_local = mktime(&tm_local);
450 time_utc = mktime(&tm_utc);
452 r->utc_diff = time_local - time_utc;
456 static void nmea_reader_init( NmeaReader* r )
459 memset( r, 0, sizeof(*r) );
461 // Initialize the sizes of all the structs we use
462 r->fix.size = sizeof(GpsLocation);
463 r->sv_status.size = sizeof(GpsSvStatus);
464 for (i = 0; i < GPS_MAX_SVS; i++) {
465 r->sv_status.sv_list[i].size = sizeof(GpsSvInfo);
474 // not sure if we still need this (this module doesn't use utc_diff)
475 nmea_reader_update_utc_diff( r );
478 static int nmea_reader_update_time( NmeaReader* r, Token tok )
480 int hour, minute, seconds, milliseconds;
484 if (tok.p + 6 > tok.end)
487 if (r->utc_year < 0) {
488 // no date, can't return valid timestamp (never ever make up a date, this could wreak havoc)
493 tm.tm_year = r->utc_year - 1900;
494 tm.tm_mon = r->utc_mon - 1;
495 tm.tm_mday = r->utc_day;
498 hour = str2int(tok.p, tok.p+2);
499 minute = str2int(tok.p+2, tok.p+4);
500 seconds = str2int(tok.p+4, tok.p+6);
502 // parse also milliseconds (if present) for better precision
504 if (tok.end - (tok.p+7) == 2) {
505 milliseconds = str2int(tok.p+7, tok.end) * 10;
507 else if (tok.end - (tok.p+7) == 1) {
508 milliseconds = str2int(tok.p+7, tok.end) * 100;
510 else if (tok.end - (tok.p+7) >= 3) {
511 milliseconds = str2int(tok.p+7, tok.p+10);
514 // the following is only guaranteed to work if we have previously set a correct date, so be sure
515 // to always do that before
521 fix_time = mkgmtime( &tm );
523 r->fix.timestamp = (long long)fix_time * 1000 + milliseconds;
527 static int nmea_reader_update_cdate( NmeaReader* r, Token tok_d, Token tok_m, Token tok_y )
530 if ( (tok_d.p + 2 > tok_d.end) ||
531 (tok_m.p + 2 > tok_m.end) ||
532 (tok_y.p + 4 > tok_y.end) )
535 r->utc_day = str2int(tok_d.p, tok_d.p+2);
536 r->utc_mon = str2int(tok_m.p, tok_m.p+2);
537 r->utc_year = str2int(tok_y.p, tok_y.p+4);
542 static int nmea_reader_update_date( NmeaReader* r, Token date, Token time )
547 if (tok.p + 6 != tok.end) {
548 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
551 day = str2int(tok.p, tok.p+2);
552 mon = str2int(tok.p+2, tok.p+4);
553 year = str2int(tok.p+4, tok.p+6) + 2000;
555 if ((day|mon|year) < 0) {
556 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
564 return nmea_reader_update_time( r, time );
568 static double convert_from_hhmm( Token tok )
570 double val = str2float(tok.p, tok.end);
571 int degrees = (int)(floor(val) / 100);
572 double minutes = val - degrees*100.;
573 double dcoord = degrees + minutes / 60.0;
578 static int nmea_reader_update_latlong( NmeaReader* r,
588 if (tok.p + 6 > tok.end) {
589 D("latitude is too short: '%.*s'", tok.end-tok.p, tok.p);
592 lat = convert_from_hhmm(tok);
593 if (latitudeHemi == 'S')
597 if (tok.p + 6 > tok.end) {
598 D("longitude is too short: '%.*s'", tok.end-tok.p, tok.p);
601 lon = convert_from_hhmm(tok);
602 if (longitudeHemi == 'W')
605 r->fix.flags |= GPS_LOCATION_HAS_LAT_LONG;
606 r->fix.latitude = lat;
607 r->fix.longitude = lon;
612 static int nmea_reader_update_altitude( NmeaReader* r,
617 Token tok = altitude;
619 if (tok.p >= tok.end)
622 r->fix.flags |= GPS_LOCATION_HAS_ALTITUDE;
623 r->fix.altitude = str2float(tok.p, tok.end);
627 static int nmea_reader_update_accuracy( NmeaReader* r,
631 Token tok = accuracy;
633 if (tok.p >= tok.end)
636 r->fix.accuracy = str2float(tok.p, tok.end);
638 if (r->fix.accuracy == 99.99){
642 r->fix.flags |= GPS_LOCATION_HAS_ACCURACY;
646 static int nmea_reader_update_bearing( NmeaReader* r,
652 if (tok.p >= tok.end)
655 r->fix.flags |= GPS_LOCATION_HAS_BEARING;
656 r->fix.bearing = str2float(tok.p, tok.end);
661 static int nmea_reader_update_speed( NmeaReader* r,
667 if (tok.p >= tok.end)
670 r->fix.flags |= GPS_LOCATION_HAS_SPEED;
671 // convert knots into m/sec (1 knot equals 1.852 km/h, 1 km/h equals 3.6 m/s)
672 // since 1.852 / 3.6 is an odd value (periodic), we're calculating the quotient on the fly
673 // to obtain maximum precision (we don't want 1.9999 instead of 2)
674 r->fix.speed = str2float(tok.p, tok.end) * 1.852 / 3.6;
679 static void nmea_reader_parse( NmeaReader* r )
681 /* we received a complete sentence, now parse it to generate
684 NmeaTokenizer tzer[1];
687 D("Received: '%.*s'", r->pos, r->in);
690 D("Too short. discarded.");
694 nmea_tokenizer_init(tzer, r->in, r->in + r->pos);
698 D("Found %d tokens", tzer->count);
699 for (n = 0; n < tzer->count; n++) {
700 Token tok = nmea_tokenizer_get(tzer,n);
701 D("%2d: '%.*s'", n, tok.end-tok.p, tok.p);
706 tok = nmea_tokenizer_get(tzer, 0);
708 if (tok.p + 5 > tok.end) {
709 D("sentence id '%.*s' too short, ignored.", tok.end-tok.p, tok.p);
713 // ignore first two characters.
716 if ( !memcmp(tok.p, "GGA", 3) ) {
718 Token tok_fixstaus = nmea_tokenizer_get(tzer,6);
720 if ((tok_fixstaus.p[0] > '0') && (r->utc_year >= 0)) {
721 // ignore this until we have a valid timestamp
723 Token tok_time = nmea_tokenizer_get(tzer,1);
724 Token tok_latitude = nmea_tokenizer_get(tzer,2);
725 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,3);
726 Token tok_longitude = nmea_tokenizer_get(tzer,4);
727 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,5);
728 Token tok_altitude = nmea_tokenizer_get(tzer,9);
729 Token tok_altitudeUnits = nmea_tokenizer_get(tzer,10);
731 // don't use this as we have no fractional seconds and no date; there are better ways to
732 // get a good timestamp from GPS
733 //nmea_reader_update_time(r, tok_time);
734 nmea_reader_update_latlong(r, tok_latitude,
735 tok_latitudeHemi.p[0],
737 tok_longitudeHemi.p[0]);
738 nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits);
741 } else if ( !memcmp(tok.p, "GLL", 3) ) {
743 Token tok_fixstaus = nmea_tokenizer_get(tzer,6);
745 if ((tok_fixstaus.p[0] == 'A') && (r->utc_year >= 0)) {
746 // ignore this until we have a valid timestamp
748 Token tok_latitude = nmea_tokenizer_get(tzer,1);
749 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,2);
750 Token tok_longitude = nmea_tokenizer_get(tzer,3);
751 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,4);
752 Token tok_time = nmea_tokenizer_get(tzer,5);
754 // don't use this as we have no fractional seconds and no date; there are better ways to
755 // get a good timestamp from GPS
756 //nmea_reader_update_time(r, tok_time);
757 nmea_reader_update_latlong(r, tok_latitude,
758 tok_latitudeHemi.p[0],
760 tok_longitudeHemi.p[0]);
763 } else if ( !memcmp(tok.p, "GSA", 3) ) {
765 Token tok_fixStatus = nmea_tokenizer_get(tzer, 2);
768 if (tok_fixStatus.p[0] != '\0' && tok_fixStatus.p[0] != '1') {
770 Token tok_accuracy = nmea_tokenizer_get(tzer, 15);
772 nmea_reader_update_accuracy(r, tok_accuracy);
774 r->sv_status.used_in_fix_mask = 0ul;
776 for (i = 3; i <= 14; ++i){
778 Token tok_prn = nmea_tokenizer_get(tzer, i);
779 int prn = str2int(tok_prn.p, tok_prn.end);
782 r->sv_status.used_in_fix_mask |= (1ul << (32 - prn));
783 r->sv_status_changed = 1;
784 D("%s: fix mask is %d", __FUNCTION__, r->sv_status.used_in_fix_mask);
791 } else if ( !memcmp(tok.p, "GSV", 3) ) {
793 Token tok_noSatellites = nmea_tokenizer_get(tzer, 3);
794 int noSatellites = str2int(tok_noSatellites.p, tok_noSatellites.end);
796 if (noSatellites > 0) {
798 Token tok_noSentences = nmea_tokenizer_get(tzer, 1);
799 Token tok_sentence = nmea_tokenizer_get(tzer, 2);
801 int sentence = str2int(tok_sentence.p, tok_sentence.end);
802 int totalSentences = str2int(tok_noSentences.p, tok_noSentences.end);
807 r->sv_status_changed = 0;
808 r->sv_status.num_svs = 0;
811 curr = r->sv_status.num_svs;
815 while (i < 4 && r->sv_status.num_svs < noSatellites){
817 Token tok_prn = nmea_tokenizer_get(tzer, i * 4 + 4);
818 Token tok_elevation = nmea_tokenizer_get(tzer, i * 4 + 5);
819 Token tok_azimuth = nmea_tokenizer_get(tzer, i * 4 + 6);
820 Token tok_snr = nmea_tokenizer_get(tzer, i * 4 + 7);
822 r->sv_status.sv_list[curr].prn = str2int(tok_prn.p, tok_prn.end);
823 r->sv_status.sv_list[curr].elevation = str2float(tok_elevation.p, tok_elevation.end);
824 r->sv_status.sv_list[curr].azimuth = str2float(tok_azimuth.p, tok_azimuth.end);
825 r->sv_status.sv_list[curr].snr = str2float(tok_snr.p, tok_snr.end);
827 r->sv_status.num_svs += 1;
834 if (sentence == totalSentences) {
835 r->sv_status_changed = 1;
838 D("%s: GSV message with total satellites %d", __FUNCTION__, noSatellites);
842 } else if ( !memcmp(tok.p, "RMC", 3) ) {
844 Token tok_fixStatus = nmea_tokenizer_get(tzer,2);
846 if (tok_fixStatus.p[0] == 'A')
848 Token tok_time = nmea_tokenizer_get(tzer,1);
849 Token tok_latitude = nmea_tokenizer_get(tzer,3);
850 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,4);
851 Token tok_longitude = nmea_tokenizer_get(tzer,5);
852 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,6);
853 Token tok_speed = nmea_tokenizer_get(tzer,7);
854 Token tok_bearing = nmea_tokenizer_get(tzer,8);
855 Token tok_date = nmea_tokenizer_get(tzer,9);
857 nmea_reader_update_date( r, tok_date, tok_time );
859 nmea_reader_update_latlong( r, tok_latitude,
860 tok_latitudeHemi.p[0],
862 tok_longitudeHemi.p[0] );
864 nmea_reader_update_bearing( r, tok_bearing );
865 nmea_reader_update_speed ( r, tok_speed );
868 } else if ( !memcmp(tok.p, "VTG", 3) ) {
870 Token tok_fixStatus = nmea_tokenizer_get(tzer,9);
872 if (tok_fixStatus.p[0] != '\0' && tok_fixStatus.p[0] != 'N')
874 Token tok_bearing = nmea_tokenizer_get(tzer,1);
875 Token tok_speed = nmea_tokenizer_get(tzer,5);
877 nmea_reader_update_bearing( r, tok_bearing );
878 nmea_reader_update_speed ( r, tok_speed );
881 } else if ( !memcmp(tok.p, "ZDA", 3) ) {
884 Token tok_year = nmea_tokenizer_get(tzer,4);
885 tok_time = nmea_tokenizer_get(tzer,1);
887 if ((tok_year.p[0] != '\0') && (tok_time.p[0] != '\0')) {
889 // make sure to always set date and time together, lest bad things happen
890 Token tok_day = nmea_tokenizer_get(tzer,2);
891 Token tok_mon = nmea_tokenizer_get(tzer,3);
893 nmea_reader_update_cdate( r, tok_day, tok_mon, tok_year );
894 nmea_reader_update_time(r, tok_time);
900 D("unknown sentence '%.*s", tok.end-tok.p, tok.p);
904 if (r->fix.flags != 0) {
908 char* end = p + sizeof(temp);
911 p += snprintf( p, end-p, "sending fix" );
912 if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
913 p += snprintf(p, end-p, " lat=%g lon=%g", r->fix.latitude, r->fix.longitude);
915 if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) {
916 p += snprintf(p, end-p, " altitude=%g", r->fix.altitude);
918 if (r->fix.flags & GPS_LOCATION_HAS_SPEED) {
919 p += snprintf(p, end-p, " speed=%g", r->fix.speed);
921 if (r->fix.flags & GPS_LOCATION_HAS_BEARING) {
922 p += snprintf(p, end-p, " bearing=%g", r->fix.bearing);
924 if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) {
925 p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy);
927 gmtime_r( (time_t*) &r->fix.timestamp, &utc );
928 p += snprintf(p, end-p, " time=%s", asctime( &utc ) );
935 static void nmea_reader_addc( NmeaReader* r, int c )
938 r->overflow = (c != '\n');
942 if (r->pos >= (int) sizeof(r->in)-1 ) {
948 r->in[r->pos] = (char)c;
952 nmea_reader_parse( r );
957 /*****************************************************************/
958 /*****************************************************************/
960 /***** C O N N E C T I O N S T A T E *****/
962 /*****************************************************************/
963 /*****************************************************************/
965 /* commands sent to the gps thread */
973 static void gps_state_start( GpsState* s )
975 char cmd = CMD_START;
978 do { ret=write( s->control[0], &cmd, 1 ); }
979 while (ret < 0 && errno == EINTR);
982 D("%s: could not send CMD_START command: ret=%d: %s",
983 __FUNCTION__, ret, strerror(errno));
987 static void gps_state_stop( GpsState* s )
992 do { ret=write( s->control[0], &cmd, 1 ); }
993 while (ret < 0 && errno == EINTR);
996 D("%s: could not send CMD_STOP command: ret=%d: %s",
997 __FUNCTION__, ret, strerror(errno));
1001 static int epoll_register( int epoll_fd, int fd )
1003 struct epoll_event ev;
1006 /* important: make the fd non-blocking */
1007 flags = fcntl(fd, F_GETFL);
1008 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
1010 ev.events = EPOLLIN;
1013 ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
1014 } while (ret < 0 && errno == EINTR);
1019 static int epoll_deregister( int epoll_fd, int fd )
1023 ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
1024 } while (ret < 0 && errno == EINTR);
1028 static void gps_nmea_thread_cb( GpsState* state )
1030 D("%s()", __FUNCTION__ );
1031 state->callbacks.nmea_cb(state->reader.fix.timestamp,&state->nmea_buf[0],state->nmea_len);
1032 GPS_STATE_UNLOCK_FIX(state);
1035 static void gps_nmea_cb( GpsState* state , const char* buf, int len)
1037 D("%s()", __FUNCTION__ );
1038 // Forward NMEA sentences ....
1039 if (state->callbacks.nmea_cb) {
1041 GPS_STATE_LOCK_FIX(state);
1042 memcpy(&state->nmea_buf[0],buf,len);
1043 state->nmea_buf[len] = 0;
1044 state->nmea_len = len;
1045 state->callbacks.create_thread_cb("nmea",(start_t)gps_nmea_thread_cb,(void*)state);
1049 static void gps_status_thread_cb( GpsState* state )
1051 D("%s()", __FUNCTION__ );
1052 state->callbacks.status_cb(&state->gps_status);
1053 GPS_STATE_UNLOCK_FIX(state);
1056 static void gps_status_cb( GpsState* state , GpsStatusValue status)
1058 D("%s()", __FUNCTION__ );
1059 if (state->callbacks.status_cb) {
1060 GPS_STATE_LOCK_FIX(state);
1062 state->gps_status.size = sizeof(GpsStatus);
1063 state->gps_status.status = status;
1064 state->callbacks.create_thread_cb("status",(start_t)gps_status_thread_cb,(void*)state);
1066 D("gps status callback: 0x%x", status);
1070 static void gps_set_capabilities_cb( GpsState* state, unsigned long caps)
1072 D("%s()", __FUNCTION__ );
1073 if (state->callbacks.set_capabilities_cb) {
1074 state->callbacks.create_thread_cb("caps",(start_t)state->callbacks.set_capabilities_cb,(void*)caps);
1078 static void gps_location_thread_cb( GpsState* state )
1080 D("%s()", __FUNCTION__ );
1081 state->callbacks.location_cb( &state->reader.fix );
1082 state->reader.fix.flags = 0;
1083 GPS_STATE_UNLOCK_FIX(state);
1087 static void gps_location_cb( GpsState* state )
1089 D("%s()", __FUNCTION__ );
1090 if (state->callbacks.location_cb) {
1091 GPS_STATE_LOCK_FIX(state);
1092 state->callbacks.create_thread_cb("fix",(start_t)gps_location_thread_cb,(void*)state);
1096 static void gps_sv_status_thread_cb( GpsState* state )
1098 D("%s()", __FUNCTION__ );
1099 state->callbacks.sv_status_cb( &state->reader.sv_status );
1100 state->reader.sv_status_changed = 0;
1101 GPS_STATE_UNLOCK_FIX(state);
1105 static void gps_sv_status_cb( GpsState* state )
1107 D("%s()", __FUNCTION__ );
1108 if (state->callbacks.sv_status_cb) {
1109 GPS_STATE_LOCK_FIX(state);
1110 state->callbacks.create_thread_cb("sv-status",(start_t)gps_sv_status_thread_cb,(void*)state);
1115 /* this is the main thread, it waits for commands from gps_state_start/stop and,
1116 * when started, messages from the QEMU GPS daemon. these are simple NMEA sentences
1117 * that must be parsed to be converted into GPS fixes sent to the framework
1119 static void* gps_state_thread( void* arg )
1121 GpsState* state = (GpsState*) arg;
1123 int epoll_fd = epoll_create(2);
1125 int gps_fd = state->fd;
1126 int control_fd = state->control[1];
1128 reader = &state->reader;
1130 nmea_reader_init( reader );
1132 // register control file descriptors for polling
1133 epoll_register( epoll_fd, control_fd );
1134 epoll_register( epoll_fd, gps_fd );
1136 D("gps thread running");
1138 gps_set_capabilities_cb( state , GPS_CAPABILITY_MSA | GPS_CAPABILITY_MSB );
1140 D("after set capabilities");
1142 gps_status_cb( state , GPS_STATUS_ENGINE_ON);
1144 D("after set status");
1148 struct epoll_event events[2];
1151 nevents = epoll_wait( epoll_fd, events, 2, -1 );
1154 ALOGE("epoll_wait() unexpected error: %s", strerror(errno));
1157 D("gps thread received %d events", nevents);
1158 for (ne = 0; ne < nevents; ne++) {
1159 if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
1160 ALOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
1163 if ((events[ne].events & EPOLLIN) != 0) {
1164 int fd = events[ne].data.fd;
1166 if (fd == control_fd)
1170 D("gps control fd event");
1172 ret = read( fd, &cmd, 1 );
1173 } while (ret < 0 && errno == EINTR);
1175 if (cmd == CMD_QUIT) {
1176 D("gps thread quitting on demand");
1179 else if (cmd == CMD_START) {
1181 D("gps thread starting location_cb=%p", state->callbacks.location_cb);
1186 gps_status_cb( state , GPS_STATUS_SESSION_BEGIN);
1188 state->init = STATE_START;
1190 if ( pthread_create( &state->tmr_thread, NULL, gps_timer_thread, state ) != 0 ) {
1191 ALOGE("could not create gps timer thread: %s", strerror(errno));
1193 state->init = STATE_INIT;
1199 else if (cmd == CMD_STOP) {
1202 D("gps thread stopping");
1207 state->init = STATE_INIT;
1209 pthread_join(state->tmr_thread, &dummy);
1211 gps_status_cb( state , GPS_STATUS_SESSION_END);
1216 else if (fd == gps_fd)
1222 ret = read( fd, buf, sizeof(buf) );
1223 } while (ret < 0 && errno == EINTR);
1227 gps_nmea_cb( state , &buf[0], ret);
1229 GPS_STATE_LOCK_FIX(state);
1230 for (nn = 0; nn < ret; nn++)
1231 nmea_reader_addc( reader, buf[nn] );
1232 GPS_STATE_UNLOCK_FIX(state);
1235 D("gps fd event end");
1239 ALOGE("epoll_wait() returned unkown fd %d ?", fd);
1246 gps_status_cb( state , GPS_STATUS_ENGINE_OFF);
1251 static void* gps_timer_thread( void* arg )
1254 GpsState *state = (GpsState *)arg;
1256 D("gps entered timer thread");
1260 D ("gps timer exp");
1262 if (state->reader.fix.flags != 0) {
1264 D("gps fix cb: 0x%x", state->reader.fix.flags);
1266 gps_location_cb( state );
1269 if (state->reader.sv_status_changed != 0) {
1271 D("gps sv status callback");
1273 gps_sv_status_cb( state );
1277 if (state->min_interval == 0) {
1278 state->min_interval = 1000;
1281 usleep(state->min_interval*1000);
1283 } while(state->init == STATE_START);
1285 D("gps timer thread destroyed");
1291 static int open_serialport( const char* name )
1295 fd = open( name, O_RDWR );
1296 } while (fd < 0 && errno == EINTR);
1299 ALOGE("could not open serial device %s: %s", name, strerror(errno) );
1303 // disable echo on serial lines
1304 if ( isatty( fd ) ) {
1306 tcgetattr( fd, &ios );
1307 ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */
1308 ios.c_oflag &= (~ONLCR); /* Stop \n -> \r\n translation on output */
1309 ios.c_iflag &= (~(ICRNL | INLCR)); /* Stop \r -> \n & \n -> \r translation on input */
1310 ios.c_iflag |= (IGNCR | IXOFF); /* Ignore \r & XON/XOFF on input */
1311 tcsetattr( fd, TCSANOW, &ios );
1317 static void gps_state_done( GpsState* s )
1319 // tell the thread to quit, and wait for it
1320 char cmd = CMD_QUIT;
1324 D("gps send quit command");
1326 do { ret=write( s->control[0], &cmd, 1 ); }
1327 while (ret < 0 && errno == EINTR);
1329 D("gps waiting for command thread to stop");
1331 pthread_join(s->thread, &dummy);
1333 /* Timer thread depends on this state check */
1334 s->init = STATE_QUIT;
1335 s->min_interval = 1000;
1337 // close the control socket pair
1338 close( s->control[0] ); s->control[0] = -1;
1339 close( s->control[1] ); s->control[1] = -1;
1341 // Turn off GPS function
1342 serial_write(s->ctrl_fd,"AT^WPEND\r\n");
1344 // close connection to the GPS
1345 close( s->fd ); s->fd = -1;
1346 close( s->ctrl_fd ); s->ctrl_fd = -1;
1348 // Power down the GPS interface
1351 sem_destroy(&s->fix_sem);
1353 memset(s, 0, sizeof(*s));
1355 D("gps deinit complete");
1360 static void gps_state_init( GpsState* state )
1362 char gps_data[PROPERTY_VALUE_MAX];
1363 char gps_ctrl[PROPERTY_VALUE_MAX];
1364 char gps_supl[PROPERTY_VALUE_MAX];
1365 char buf[PROPERTY_VALUE_MAX+20];
1369 struct sigevent tmr_event;
1371 state->init = STATE_INIT;
1372 state->control[0] = -1;
1373 state->control[1] = -1;
1375 state->ctrl_fd = -1;
1376 state->min_interval = 1000;
1378 if (sem_init(&state->fix_sem, 0, 1) != 0) {
1379 D("gps semaphore initialization failed! errno = %d", errno);
1383 // Power up the GPS interface
1386 // look for a kernel-provided gps_data port
1387 strcpy(gps_data,"/dev/ttyUSB3") ; //RvdB
1389 if (property_get("ro.kernel.gps.data",gps_data,"/dev/ttyUSB3") == 0) {
1390 D("no kernel-provided gps device data name");
1395 state->fd = open_serialport( gps_data );
1396 if (state->fd < 0) {
1397 ALOGE("could not open gps serial device %s: %s", gps_data, strerror(errno) );
1402 D("gps will read from %s", gps_data);
1404 // look for a kernel-provided gps_ctrl port
1405 strcpy(gps_ctrl,"/dev/ttyUSB4") ;//RvdB
1408 if (property_get("ro.kernel.gps.ctl",gps_ctrl,"/dev/ttyUSB4") == 0) {
1409 D("no kernel-provided gps device ctrl name");
1414 state->ctrl_fd = open_serialport( gps_ctrl );
1415 if (state->ctrl_fd < 0) {
1416 ALOGE("could not open gps serial device %s: %s", gps_ctrl, strerror(errno) );
1423 D("gps will be controlled by %s", gps_ctrl);
1425 // Turn on GPS function
1426 serial_write(state->ctrl_fd,"AT^WPEND\r\n");
1428 // look for a kernel-provided supl
1429 strcpy(gps_supl,"http://supl.nokia.com") ; //RvdB
1431 if (property_get("ro.kernel.gps.supl",gps_supl,"http://supl.nokia.com") == 0) {
1432 D("no kernel-provided supl");
1435 close(state->ctrl_fd);
1436 state->ctrl_fd = -1;
1441 sprintf(buf,"AT^WPURL=%s\r\n",gps_supl);
1442 serial_write(state->ctrl_fd,buf);
1444 // configure AGPS to work speed optimal
1445 serial_write(state->ctrl_fd,"AT^WPDOM=2\r\n");
1448 serial_write(state->ctrl_fd,"AT^WPDGP\r\n");
1450 if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
1451 ALOGE("could not create thread control socket pair: %s", strerror(errno));
1456 if ( pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0 ) {
1457 ALOGE("could not create gps thread: %s", strerror(errno));
1462 D("gps state initialized");
1467 gps_state_done( state );
1472 /*****************************************************************/
1473 /*****************************************************************/
1475 /***** I N T E R F A C E *****/
1477 /*****************************************************************/
1478 /*****************************************************************/
1480 static int gps_init(GpsCallbacks* callbacks)
1482 GpsState* s = _gps_state;
1490 s->callbacks = *callbacks;
1495 static void gps_cleanup(void)
1497 GpsState* s = _gps_state;
1504 static int gps_start(void)
1506 GpsState* s = _gps_state;
1509 D("%s: called with uninitialized state !!", __FUNCTION__);
1513 D("%s: called", __FUNCTION__);
1519 static int gps_stop(void)
1521 GpsState* s = _gps_state;
1524 D("%s: called with uninitialized state !!", __FUNCTION__);
1528 D("%s: called", __FUNCTION__);
1534 static int gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty)
1539 /** Injects current location from another location provider
1540 * (typically cell ID).
1541 * latitude and longitude are measured in degrees
1542 * expected accuracy is measured in meters
1544 static int gps_inject_location(double latitude, double longitude, float accuracy)
1549 static void gps_delete_aiding_data(GpsAidingData flags)
1553 static int gps_set_position_mode(GpsPositionMode mode, GpsPositionRecurrence recurrence,
1554 uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time)
1556 GpsState* s = _gps_state;
1559 D("%s: called with uninitialized state !!", __FUNCTION__);
1563 // only standalone supported for now.
1564 if (mode == GPS_POSITION_MODE_STANDALONE) {
1566 if (mode == GPS_POSITION_MODE_MS_BASED ||
1567 mode == GPS_POSITION_MODE_MS_ASSISTED) {
1571 s->min_interval = min_interval;
1573 D("gps fix frquency set to %d secs", min_interval);
1578 /***** AGpsInterface *****/
1581 * Opens the AGPS interface and provides the callback routines
1582 * to the implemenation of this interface.
1584 static void agps_init( AGpsCallbacks* callbacks )
1586 D("%s() is called", __FUNCTION__);
1588 GpsState* s = _gps_state;
1591 D("%s: called with uninitialized state !!", __FUNCTION__);
1595 s->a_callbacks = *callbacks;
1599 * Notifies that a data connection is available and sets
1600 * the name of the APN to be used for SUPL.
1602 static int agps_conn_open( const char* apn )
1604 D("%s() is called", __FUNCTION__);
1607 GpsState* s = _gps_state;
1610 D("%s: called with uninitialized state !!", __FUNCTION__);
1618 * Notifies that the AGPS data connection has been closed.
1620 static int agps_conn_closed( void )
1622 D("%s() is called", __FUNCTION__);
1624 GpsState* s = _gps_state;
1627 D("%s: called with uninitialized state !!", __FUNCTION__);
1635 * Notifies that a data connection is not available for AGPS.
1637 static int agps_conn_failed( void )
1639 D("%s() is called", __FUNCTION__);
1641 GpsState* s = _gps_state;
1644 D("%s: called with uninitialized state !!", __FUNCTION__);
1652 * Sets the hostname and port for the AGPS server.
1654 static int agps_set_server( AGpsType type, const char* hostname, int port )
1658 D("%s() is called", __FUNCTION__);
1659 D("type=%d, hostname=%s, port=%d", type, hostname, port);
1661 GpsState* s = _gps_state;
1664 D("%s: called with uninitialized state !!", __FUNCTION__);
1669 sprintf(buf,"AT^WPURL=%s\r\n",hostname);
1670 serial_write(s->ctrl_fd,buf);
1676 static const AGpsInterface sAGpsInterface = {
1677 sizeof(AGpsInterface),
1686 /***** GpsXtraInterface *****/
1687 static int xtra_init(GpsXtraCallbacks* callbacks)
1689 D("%s() is called", __FUNCTION__);
1690 GpsState* s = _gps_state;
1692 s->xtra_callbacks = *callbacks;
1697 static int xtra_inject_xtra_data(char* data, int length)
1699 D("%s() is called", __FUNCTION__);
1700 D("xtra size = %d, data ptr = 0x%x\n", length, (int) data);
1702 GpsState* s = _gps_state;
1709 static const GpsXtraInterface sGpsXtraInterface = {
1710 sizeof(GpsXtraInterface),
1712 xtra_inject_xtra_data,
1715 static const void* gps_get_extension(const char* name)
1717 D("%s('%s') is called", __FUNCTION__, name);
1719 if (!strcmp(name, GPS_XTRA_INTERFACE)) {
1720 return &sGpsXtraInterface;
1721 } else if (!strcmp(name, AGPS_INTERFACE)) {
1722 return &sAGpsInterface;
1727 static const GpsInterface sGpsInterface = {
1728 sizeof(GpsInterface),
1734 gps_inject_location,
1735 gps_delete_aiding_data,
1736 gps_set_position_mode,
1740 // As under Android, there are no exceptions in C++ base system, we will hide all exported symbols
1741 // except the required ones. This will generate better code!
1744 #define DLL_PUBLIC __attribute__ ((visibility ("default")))
1745 #define DLL_LOCAL __attribute__ ((visibility ("hidden")))
1752 static const GpsInterface* gps_get_hardware_interface(struct gps_device_t* dev)
1754 ALOGV("get_interface was called");
1755 return &sGpsInterface;
1758 static int open_gps(const struct hw_module_t* module, char const* name,
1759 struct hw_device_t** device)
1761 struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));
1762 memset(dev, 0, sizeof(*dev));
1764 dev->common.tag = HARDWARE_DEVICE_TAG;
1765 dev->common.version = 0;
1766 dev->common.module = (struct hw_module_t*)module;
1767 dev->get_gps_interface = gps_get_hardware_interface;
1769 *device = (struct hw_device_t*)dev;
1774 static struct hw_module_methods_t gps_module_methods = {
1778 DLL_PUBLIC struct hw_module_t HAL_MODULE_INFO_SYM = {
1779 .tag = HARDWARE_MODULE_TAG,
1782 .id = GPS_HARDWARE_MODULE_ID,
1783 .name = "GPS Module",
1784 .author = "Eduardo José Tagle",
1785 .methods = &gps_module_methods,