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) {
141 static unsigned char calc_nmea_csum(char *msg)
143 unsigned char csum = 0;
146 for (i = 1; msg[i] != '*'; ++i) {
154 /*****************************************************************/
155 /*****************************************************************/
157 /***** D E V I C E *****/
159 /*****************************************************************/
160 /*****************************************************************/
162 static void dev_power(int state)
164 char prop[PROPERTY_VALUE_MAX];
170 if (property_get("gps.power_on",prop,GPS_POWER_IF) == 0) {
171 ALOGE("no gps power interface name");
176 fd = open( prop, O_RDWR );
177 } while (fd < 0 && errno == EINTR);
180 ALOGE("could not open gps power interface: %s", prop );
191 ret = write( fd, &cmd, 1 );
192 } while (ret < 0 && errno == EINTR);
196 D("gps power state = %c", cmd);
205 static void dev_set_nmea_message_rate(int fd,const char *msg, int rate)
211 sprintf(buff, "$PUBX,40,%s,%d,%d,%d,0*", msg, rate, rate, rate);
215 sprintf((buff + i), "%02x\r\n", calc_nmea_csum(buff));
217 serial_write(fd, buff);
219 D("gps sent to device: %s", buff);
225 static void dev_set_message_rate(int fd, int rate)
229 static const char *msg[] = {
235 for (i = 0; i < sizeof(msg)/sizeof(msg[0]); ++i) {
236 dev_set_nmea_message_rate(fd, msg[i], rate);
243 static void dev_start(int fd)
245 D("gps dev start initiated");
246 // Set full message rate
247 //RvdB dev_set_message_rate(fd, GPS_DEV_HIGH_UPDATE_RATE);
250 static void dev_stop(int fd)
254 // Set slow message rate
255 //RvdB dev_set_message_rate(fd, GPS_DEV_SLOW_UPDATE_RATE);
258 /*****************************************************************/
259 /*****************************************************************/
261 /***** N M E A T O K E N I Z E R *****/
263 /*****************************************************************/
264 /*****************************************************************/
271 #define MAX_NMEA_TOKENS 32
275 Token tokens[ MAX_NMEA_TOKENS ];
278 static int nmea_tokenizer_init( NmeaTokenizer* t, const char* p, const char* end )
283 // the initial '$' is optional
284 if (p < end && p[0] == '$')
287 // remove trailing newline
288 if (end > p && end[-1] == '\n') {
290 if (end > p && end[-1] == '\r')
294 // get rid of checksum at the end of the sentecne
295 if (end >= p+3 && end[-3] == '*') {
302 q = memchr(p, ',', end-p);
306 if (count < MAX_NMEA_TOKENS) {
307 t->tokens[count].p = p;
308 t->tokens[count].end = q;
322 static Token nmea_tokenizer_get( NmeaTokenizer* t, int index )
325 static const char* dummy = "";
327 if (index < 0 || index >= t->count) {
328 tok.p = tok.end = dummy;
330 tok = t->tokens[index];
336 static int str2int( const char* p, const char* end )
345 for ( ; len > 0; len--, p++ )
353 if ((unsigned)c >= 10)
356 result = result*10 + c;
364 static double str2float( const char* p, const char* end )
374 if (len >= (int)sizeof(temp))
377 memcpy( temp, p, len );
379 return strtod( temp, NULL );
382 /** @desc Convert struct tm to time_t (time zone neutral).
384 * The one missing function in libc: It works basically like mktime, with the main difference that
385 * it does no time zone-related processing but interprets the members of the struct tm as UTC.
386 * Unlike mktime, it will not modify any fields of the tm structure; if you need this behavior, call
387 * mktime before this function.
389 * @param t Pointer to a struct tm containing date and time. Only the tm_year, tm_mon, tm_mday,
390 * tm_hour, tm_min and tm_sec members will be evaluated, all others will be ignored.
392 * @return The epoch time (seconds since 1970-01-01 00:00:00 UTC) which corresponds to t.
394 * @author Originally written by Philippe De Muyter <phdm@macqel.be> for Lynx.
395 * http://lynx.isc.org/current/lynx2-8-8/src/mktime.c
398 static time_t mkgmtime(struct tm *t)
402 static const int m_to_d[12] =
403 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
406 year = t->tm_year + month / 12 + 1900;
412 result = (year - 1970) * 365 + m_to_d[month];
415 result += (year - 1968) / 4;
416 result -= (year - 1900) / 100;
417 result += (year - 1600) / 400;
418 result += t->tm_mday;
421 result += t->tm_hour;
429 /*****************************************************************/
430 /*****************************************************************/
432 /***** N M E A P A R S E R *****/
434 /*****************************************************************/
435 /*****************************************************************/
437 static void nmea_reader_update_utc_diff( NmeaReader* r )
439 time_t now = time(NULL);
442 long time_local, time_utc;
444 gmtime_r( &now, &tm_utc );
445 localtime_r( &now, &tm_local );
447 time_local = mktime(&tm_local);
448 time_utc = mktime(&tm_utc);
450 r->utc_diff = time_local - time_utc;
454 static void nmea_reader_init( NmeaReader* r )
457 memset( r, 0, sizeof(*r) );
459 // Initialize the sizes of all the structs we use
460 r->fix.size = sizeof(GpsLocation);
461 r->sv_status.size = sizeof(GpsSvStatus);
462 for (i = 0; i < GPS_MAX_SVS; i++) {
463 r->sv_status.sv_list[i].size = sizeof(GpsSvInfo);
472 // not sure if we still need this (this module doesn't use utc_diff)
473 nmea_reader_update_utc_diff( r );
476 static int nmea_reader_update_time( NmeaReader* r, Token tok )
478 int hour, minute, seconds, milliseconds;
482 if (tok.p + 6 > tok.end)
485 if (r->utc_year < 0) {
486 // no date, can't return valid timestamp (never ever make up a date, this could wreak havoc)
491 tm.tm_year = r->utc_year - 1900;
492 tm.tm_mon = r->utc_mon - 1;
493 tm.tm_mday = r->utc_day;
496 hour = str2int(tok.p, tok.p+2);
497 minute = str2int(tok.p+2, tok.p+4);
498 seconds = str2int(tok.p+4, tok.p+6);
500 // parse also milliseconds (if present) for better precision
502 if (tok.end - (tok.p+7) == 2) {
503 milliseconds = str2int(tok.p+7, tok.end) * 10;
505 else if (tok.end - (tok.p+7) == 1) {
506 milliseconds = str2int(tok.p+7, tok.end) * 100;
508 else if (tok.end - (tok.p+7) >= 3) {
509 milliseconds = str2int(tok.p+7, tok.p+10);
512 // the following is only guaranteed to work if we have previously set a correct date, so be sure
513 // to always do that before
519 fix_time = mkgmtime( &tm );
521 r->fix.timestamp = (long long)fix_time * 1000 + milliseconds;
525 static int nmea_reader_update_cdate( NmeaReader* r, Token tok_d, Token tok_m, Token tok_y )
528 if ( (tok_d.p + 2 > tok_d.end) ||
529 (tok_m.p + 2 > tok_m.end) ||
530 (tok_y.p + 4 > tok_y.end) )
533 r->utc_day = str2int(tok_d.p, tok_d.p+2);
534 r->utc_mon = str2int(tok_m.p, tok_m.p+2);
535 r->utc_year = str2int(tok_y.p, tok_y.p+4);
540 static int nmea_reader_update_date( NmeaReader* r, Token date, Token time )
545 if (tok.p + 6 != tok.end) {
546 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
549 day = str2int(tok.p, tok.p+2);
550 mon = str2int(tok.p+2, tok.p+4);
551 year = str2int(tok.p+4, tok.p+6) + 2000;
553 if ((day|mon|year) < 0) {
554 D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
562 return nmea_reader_update_time( r, time );
566 static double convert_from_hhmm( Token tok )
568 double val = str2float(tok.p, tok.end);
569 int degrees = (int)(floor(val) / 100);
570 double minutes = val - degrees*100.;
571 double dcoord = degrees + minutes / 60.0;
576 static int nmea_reader_update_latlong( NmeaReader* r,
586 if (tok.p + 6 > tok.end) {
587 D("latitude is too short: '%.*s'", tok.end-tok.p, tok.p);
590 lat = convert_from_hhmm(tok);
591 if (latitudeHemi == 'S')
595 if (tok.p + 6 > tok.end) {
596 D("longitude is too short: '%.*s'", tok.end-tok.p, tok.p);
599 lon = convert_from_hhmm(tok);
600 if (longitudeHemi == 'W')
603 r->fix.flags |= GPS_LOCATION_HAS_LAT_LONG;
604 r->fix.latitude = lat;
605 r->fix.longitude = lon;
610 static int nmea_reader_update_altitude( NmeaReader* r,
615 Token tok = altitude;
617 if (tok.p >= tok.end)
620 r->fix.flags |= GPS_LOCATION_HAS_ALTITUDE;
621 r->fix.altitude = str2float(tok.p, tok.end);
625 static int nmea_reader_update_accuracy( NmeaReader* r,
629 Token tok = accuracy;
631 if (tok.p >= tok.end)
634 r->fix.accuracy = str2float(tok.p, tok.end);
636 if (r->fix.accuracy == 99.99){
640 r->fix.flags |= GPS_LOCATION_HAS_ACCURACY;
644 static int nmea_reader_update_bearing( NmeaReader* r,
650 if (tok.p >= tok.end)
653 r->fix.flags |= GPS_LOCATION_HAS_BEARING;
654 r->fix.bearing = str2float(tok.p, tok.end);
659 static int nmea_reader_update_speed( NmeaReader* r,
665 if (tok.p >= tok.end)
668 r->fix.flags |= GPS_LOCATION_HAS_SPEED;
669 // convert knots into m/sec (1 knot equals 1.852 km/h, 1 km/h equals 3.6 m/s)
670 // since 1.852 / 3.6 is an odd value (periodic), we're calculating the quotient on the fly
671 // to obtain maximum precision (we don't want 1.9999 instead of 2)
672 r->fix.speed = str2float(tok.p, tok.end) * 1.852 / 3.6;
677 static void nmea_reader_parse( NmeaReader* r )
679 /* we received a complete sentence, now parse it to generate
682 NmeaTokenizer tzer[1];
685 D("Received: '%.*s'", r->pos, r->in);
688 D("Too short. discarded.");
692 nmea_tokenizer_init(tzer, r->in, r->in + r->pos);
696 D("Found %d tokens", tzer->count);
697 for (n = 0; n < tzer->count; n++) {
698 Token tok = nmea_tokenizer_get(tzer,n);
699 D("%2d: '%.*s'", n, tok.end-tok.p, tok.p);
704 tok = nmea_tokenizer_get(tzer, 0);
706 if (tok.p + 5 > tok.end) {
707 D("sentence id '%.*s' too short, ignored.", tok.end-tok.p, tok.p);
711 // ignore first two characters.
714 if ( !memcmp(tok.p, "GGA", 3) ) {
716 Token tok_fixstaus = nmea_tokenizer_get(tzer,6);
718 if ((tok_fixstaus.p[0] > '0') && (r->utc_year >= 0)) {
719 // ignore this until we have a valid timestamp
721 Token tok_time = nmea_tokenizer_get(tzer,1);
722 Token tok_latitude = nmea_tokenizer_get(tzer,2);
723 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,3);
724 Token tok_longitude = nmea_tokenizer_get(tzer,4);
725 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,5);
726 Token tok_altitude = nmea_tokenizer_get(tzer,9);
727 Token tok_altitudeUnits = nmea_tokenizer_get(tzer,10);
729 // don't use this as we have no fractional seconds and no date; there are better ways to
730 // get a good timestamp from GPS
731 //nmea_reader_update_time(r, tok_time);
732 nmea_reader_update_latlong(r, tok_latitude,
733 tok_latitudeHemi.p[0],
735 tok_longitudeHemi.p[0]);
736 nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits);
739 } else if ( !memcmp(tok.p, "GLL", 3) ) {
741 Token tok_fixstaus = nmea_tokenizer_get(tzer,6);
743 if ((tok_fixstaus.p[0] == 'A') && (r->utc_year >= 0)) {
744 // ignore this until we have a valid timestamp
746 Token tok_latitude = nmea_tokenizer_get(tzer,1);
747 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,2);
748 Token tok_longitude = nmea_tokenizer_get(tzer,3);
749 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,4);
750 Token tok_time = nmea_tokenizer_get(tzer,5);
752 // don't use this as we have no fractional seconds and no date; there are better ways to
753 // get a good timestamp from GPS
754 //nmea_reader_update_time(r, tok_time);
755 nmea_reader_update_latlong(r, tok_latitude,
756 tok_latitudeHemi.p[0],
758 tok_longitudeHemi.p[0]);
761 } else if ( !memcmp(tok.p, "GSA", 3) ) {
763 Token tok_fixStatus = nmea_tokenizer_get(tzer, 2);
766 if (tok_fixStatus.p[0] != '\0' && tok_fixStatus.p[0] != '1') {
768 Token tok_accuracy = nmea_tokenizer_get(tzer, 15);
770 nmea_reader_update_accuracy(r, tok_accuracy);
772 r->sv_status.used_in_fix_mask = 0ul;
774 for (i = 3; i <= 14; ++i){
776 Token tok_prn = nmea_tokenizer_get(tzer, i);
777 int prn = str2int(tok_prn.p, tok_prn.end);
780 r->sv_status.used_in_fix_mask |= (1ul << (32 - prn));
781 r->sv_status_changed = 1;
782 D("%s: fix mask is %d", __FUNCTION__, r->sv_status.used_in_fix_mask);
789 } else if ( !memcmp(tok.p, "GSV", 3) ) {
791 Token tok_noSatellites = nmea_tokenizer_get(tzer, 3);
792 int noSatellites = str2int(tok_noSatellites.p, tok_noSatellites.end);
794 if (noSatellites > 0) {
796 Token tok_noSentences = nmea_tokenizer_get(tzer, 1);
797 Token tok_sentence = nmea_tokenizer_get(tzer, 2);
799 int sentence = str2int(tok_sentence.p, tok_sentence.end);
800 int totalSentences = str2int(tok_noSentences.p, tok_noSentences.end);
805 r->sv_status_changed = 0;
806 r->sv_status.num_svs = 0;
809 curr = r->sv_status.num_svs;
813 while (i < 4 && r->sv_status.num_svs < noSatellites){
815 Token tok_prn = nmea_tokenizer_get(tzer, i * 4 + 4);
816 Token tok_elevation = nmea_tokenizer_get(tzer, i * 4 + 5);
817 Token tok_azimuth = nmea_tokenizer_get(tzer, i * 4 + 6);
818 Token tok_snr = nmea_tokenizer_get(tzer, i * 4 + 7);
820 r->sv_status.sv_list[curr].prn = str2int(tok_prn.p, tok_prn.end);
821 r->sv_status.sv_list[curr].elevation = str2float(tok_elevation.p, tok_elevation.end);
822 r->sv_status.sv_list[curr].azimuth = str2float(tok_azimuth.p, tok_azimuth.end);
823 r->sv_status.sv_list[curr].snr = str2float(tok_snr.p, tok_snr.end);
825 r->sv_status.num_svs += 1;
832 if (sentence == totalSentences) {
833 r->sv_status_changed = 1;
836 D("%s: GSV message with total satellites %d", __FUNCTION__, noSatellites);
840 } else if ( !memcmp(tok.p, "RMC", 3) ) {
842 Token tok_fixStatus = nmea_tokenizer_get(tzer,2);
844 if (tok_fixStatus.p[0] == 'A')
846 Token tok_time = nmea_tokenizer_get(tzer,1);
847 Token tok_latitude = nmea_tokenizer_get(tzer,3);
848 Token tok_latitudeHemi = nmea_tokenizer_get(tzer,4);
849 Token tok_longitude = nmea_tokenizer_get(tzer,5);
850 Token tok_longitudeHemi = nmea_tokenizer_get(tzer,6);
851 Token tok_speed = nmea_tokenizer_get(tzer,7);
852 Token tok_bearing = nmea_tokenizer_get(tzer,8);
853 Token tok_date = nmea_tokenizer_get(tzer,9);
855 nmea_reader_update_date( r, tok_date, tok_time );
857 nmea_reader_update_latlong( r, tok_latitude,
858 tok_latitudeHemi.p[0],
860 tok_longitudeHemi.p[0] );
862 nmea_reader_update_bearing( r, tok_bearing );
863 nmea_reader_update_speed ( r, tok_speed );
866 } else if ( !memcmp(tok.p, "VTG", 3) ) {
868 Token tok_fixStatus = nmea_tokenizer_get(tzer,9);
870 if (tok_fixStatus.p[0] != '\0' && tok_fixStatus.p[0] != 'N')
872 Token tok_bearing = nmea_tokenizer_get(tzer,1);
873 Token tok_speed = nmea_tokenizer_get(tzer,5);
875 nmea_reader_update_bearing( r, tok_bearing );
876 nmea_reader_update_speed ( r, tok_speed );
879 } else if ( !memcmp(tok.p, "ZDA", 3) ) {
882 Token tok_year = nmea_tokenizer_get(tzer,4);
883 tok_time = nmea_tokenizer_get(tzer,1);
885 if ((tok_year.p[0] != '\0') && (tok_time.p[0] != '\0')) {
887 // make sure to always set date and time together, lest bad things happen
888 Token tok_day = nmea_tokenizer_get(tzer,2);
889 Token tok_mon = nmea_tokenizer_get(tzer,3);
891 nmea_reader_update_cdate( r, tok_day, tok_mon, tok_year );
892 nmea_reader_update_time(r, tok_time);
898 D("unknown sentence '%.*s", tok.end-tok.p, tok.p);
902 if (r->fix.flags != 0) {
906 char* end = p + sizeof(temp);
909 p += snprintf( p, end-p, "sending fix" );
910 if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
911 p += snprintf(p, end-p, " lat=%g lon=%g", r->fix.latitude, r->fix.longitude);
913 if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) {
914 p += snprintf(p, end-p, " altitude=%g", r->fix.altitude);
916 if (r->fix.flags & GPS_LOCATION_HAS_SPEED) {
917 p += snprintf(p, end-p, " speed=%g", r->fix.speed);
919 if (r->fix.flags & GPS_LOCATION_HAS_BEARING) {
920 p += snprintf(p, end-p, " bearing=%g", r->fix.bearing);
922 if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) {
923 p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy);
925 gmtime_r( (time_t*) &r->fix.timestamp, &utc );
926 p += snprintf(p, end-p, " time=%s", asctime( &utc ) );
933 static void nmea_reader_addc( NmeaReader* r, int c )
936 r->overflow = (c != '\n');
940 if (r->pos >= (int) sizeof(r->in)-1 ) {
946 r->in[r->pos] = (char)c;
950 nmea_reader_parse( r );
955 /*****************************************************************/
956 /*****************************************************************/
958 /***** C O N N E C T I O N S T A T E *****/
960 /*****************************************************************/
961 /*****************************************************************/
963 /* commands sent to the gps thread */
971 static void gps_state_start( GpsState* s )
973 char cmd = CMD_START;
976 do { ret=write( s->control[0], &cmd, 1 ); }
977 while (ret < 0 && errno == EINTR);
980 D("%s: could not send CMD_START command: ret=%d: %s",
981 __FUNCTION__, ret, strerror(errno));
985 static void gps_state_stop( GpsState* s )
990 do { ret=write( s->control[0], &cmd, 1 ); }
991 while (ret < 0 && errno == EINTR);
994 D("%s: could not send CMD_STOP command: ret=%d: %s",
995 __FUNCTION__, ret, strerror(errno));
999 static int epoll_register( int epoll_fd, int fd )
1001 struct epoll_event ev;
1004 /* important: make the fd non-blocking */
1005 flags = fcntl(fd, F_GETFL);
1006 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
1008 ev.events = EPOLLIN;
1011 ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
1012 } while (ret < 0 && errno == EINTR);
1017 static int epoll_deregister( int epoll_fd, int fd )
1021 ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
1022 } while (ret < 0 && errno == EINTR);
1026 static void gps_nmea_thread_cb( GpsState* state )
1028 D("%s()", __FUNCTION__ );
1029 state->callbacks.nmea_cb(state->reader.fix.timestamp,&state->nmea_buf[0],state->nmea_len);
1030 GPS_STATE_UNLOCK_FIX(state);
1033 static void gps_nmea_cb( GpsState* state , const char* buf, int len)
1035 D("%s()", __FUNCTION__ );
1036 // Forward NMEA sentences ....
1037 if (state->callbacks.nmea_cb) {
1039 GPS_STATE_LOCK_FIX(state);
1040 memcpy(&state->nmea_buf[0],buf,len);
1041 state->nmea_buf[len] = 0;
1042 state->nmea_len = len;
1043 state->callbacks.create_thread_cb("nmea",(start_t)gps_nmea_thread_cb,(void*)state);
1047 static void gps_status_thread_cb( GpsState* state )
1049 D("%s()", __FUNCTION__ );
1050 state->callbacks.status_cb(&state->gps_status);
1051 GPS_STATE_UNLOCK_FIX(state);
1054 static void gps_status_cb( GpsState* state , GpsStatusValue status)
1056 D("%s()", __FUNCTION__ );
1057 if (state->callbacks.status_cb) {
1058 GPS_STATE_LOCK_FIX(state);
1060 state->gps_status.size = sizeof(GpsStatus);
1061 state->gps_status.status = status;
1062 state->callbacks.create_thread_cb("status",(start_t)gps_status_thread_cb,(void*)state);
1064 D("gps status callback: 0x%x", status);
1068 static void gps_set_capabilities_cb( GpsState* state, unsigned long caps)
1070 D("%s()", __FUNCTION__ );
1071 if (state->callbacks.set_capabilities_cb) {
1072 state->callbacks.create_thread_cb("caps",(start_t)state->callbacks.set_capabilities_cb,(void*)caps);
1076 static void gps_location_thread_cb( GpsState* state )
1078 D("%s()", __FUNCTION__ );
1079 state->callbacks.location_cb( &state->reader.fix );
1080 state->reader.fix.flags = 0;
1081 GPS_STATE_UNLOCK_FIX(state);
1085 static void gps_location_cb( GpsState* state )
1087 D("%s()", __FUNCTION__ );
1088 if (state->callbacks.location_cb) {
1089 GPS_STATE_LOCK_FIX(state);
1090 state->callbacks.create_thread_cb("fix",(start_t)gps_location_thread_cb,(void*)state);
1094 static void gps_sv_status_thread_cb( GpsState* state )
1096 D("%s()", __FUNCTION__ );
1097 state->callbacks.sv_status_cb( &state->reader.sv_status );
1098 state->reader.sv_status_changed = 0;
1099 GPS_STATE_UNLOCK_FIX(state);
1103 static void gps_sv_status_cb( GpsState* state )
1105 D("%s()", __FUNCTION__ );
1106 if (state->callbacks.sv_status_cb) {
1107 GPS_STATE_LOCK_FIX(state);
1108 state->callbacks.create_thread_cb("sv-status",(start_t)gps_sv_status_thread_cb,(void*)state);
1113 /* this is the main thread, it waits for commands from gps_state_start/stop and,
1114 * when started, messages from the QEMU GPS daemon. these are simple NMEA sentences
1115 * that must be parsed to be converted into GPS fixes sent to the framework
1117 static void* gps_state_thread( void* arg )
1119 GpsState* state = (GpsState*) arg;
1121 int epoll_fd = epoll_create(2);
1123 int gps_fd = state->fd;
1124 int control_fd = state->control[1];
1126 reader = &state->reader;
1128 nmea_reader_init( reader );
1130 // register control file descriptors for polling
1131 epoll_register( epoll_fd, control_fd );
1132 epoll_register( epoll_fd, gps_fd );
1134 D("gps thread running");
1136 gps_set_capabilities_cb( state , GPS_CAPABILITY_MSA | GPS_CAPABILITY_MSB );
1138 D("after set capabilities");
1140 gps_status_cb( state , GPS_STATUS_ENGINE_ON);
1142 D("after set status");
1146 struct epoll_event events[2];
1149 nevents = epoll_wait( epoll_fd, events, 2, -1 );
1152 ALOGE("epoll_wait() unexpected error: %s", strerror(errno));
1155 D("gps thread received %d events", nevents);
1156 for (ne = 0; ne < nevents; ne++) {
1157 if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
1158 ALOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
1161 if ((events[ne].events & EPOLLIN) != 0) {
1162 int fd = events[ne].data.fd;
1164 if (fd == control_fd)
1168 D("gps control fd event");
1170 ret = read( fd, &cmd, 1 );
1171 } while (ret < 0 && errno == EINTR);
1173 if (cmd == CMD_QUIT) {
1174 D("gps thread quitting on demand");
1177 else if (cmd == CMD_START) {
1179 D("gps thread starting location_cb=%p", state->callbacks.location_cb);
1184 gps_status_cb( state , GPS_STATUS_SESSION_BEGIN);
1186 state->init = STATE_START;
1188 if ( pthread_create( &state->tmr_thread, NULL, gps_timer_thread, state ) != 0 ) {
1189 ALOGE("could not create gps timer thread: %s", strerror(errno));
1191 state->init = STATE_INIT;
1197 else if (cmd == CMD_STOP) {
1200 D("gps thread stopping");
1205 state->init = STATE_INIT;
1207 pthread_join(state->tmr_thread, &dummy);
1209 gps_status_cb( state , GPS_STATUS_SESSION_END);
1214 else if (fd == gps_fd)
1220 ret = read( fd, buf, sizeof(buf) );
1221 } while (ret < 0 && errno == EINTR);
1225 gps_nmea_cb( state , &buf[0], ret);
1227 GPS_STATE_LOCK_FIX(state);
1228 for (nn = 0; nn < ret; nn++)
1229 nmea_reader_addc( reader, buf[nn] );
1230 GPS_STATE_UNLOCK_FIX(state);
1233 D("gps fd event end");
1237 ALOGE("epoll_wait() returned unkown fd %d ?", fd);
1244 gps_status_cb( state , GPS_STATUS_ENGINE_OFF);
1249 static void* gps_timer_thread( void* arg )
1252 GpsState *state = (GpsState *)arg;
1254 D("gps entered timer thread");
1258 D ("gps timer exp");
1260 if (state->reader.fix.flags != 0) {
1262 D("gps fix cb: 0x%x", state->reader.fix.flags);
1264 gps_location_cb( state );
1267 if (state->reader.sv_status_changed != 0) {
1269 D("gps sv status callback");
1271 gps_sv_status_cb( state );
1275 if (state->min_interval == 0) {
1276 state->min_interval = 1000;
1279 usleep(state->min_interval*1000);
1281 } while(state->init == STATE_START);
1283 D("gps timer thread destroyed");
1289 static int open_serialport( const char* name )
1293 fd = open( name, O_RDWR );
1294 } while (fd < 0 && errno == EINTR);
1297 ALOGE("could not open serial device %s: %s", name, strerror(errno) );
1301 // disable echo on serial lines
1302 if ( isatty( fd ) ) {
1304 tcgetattr( fd, &ios );
1305 ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */
1306 ios.c_oflag &= (~ONLCR); /* Stop \n -> \r\n translation on output */
1307 ios.c_iflag &= (~(ICRNL | INLCR)); /* Stop \r -> \n & \n -> \r translation on input */
1308 ios.c_iflag |= (IGNCR | IXOFF); /* Ignore \r & XON/XOFF on input */
1309 tcsetattr( fd, TCSANOW, &ios );
1315 static void gps_state_done( GpsState* s )
1317 // tell the thread to quit, and wait for it
1318 char cmd = CMD_QUIT;
1322 D("gps send quit command");
1324 do { ret=write( s->control[0], &cmd, 1 ); }
1325 while (ret < 0 && errno == EINTR);
1327 D("gps waiting for command thread to stop");
1329 pthread_join(s->thread, &dummy);
1331 /* Timer thread depends on this state check */
1332 s->init = STATE_QUIT;
1333 s->min_interval = 1000;
1335 // close the control socket pair
1336 close( s->control[0] ); s->control[0] = -1;
1337 close( s->control[1] ); s->control[1] = -1;
1339 // Turn off GPS function
1340 serial_write(s->ctrl_fd,"AT^WPEND\r\n");
1342 // close connection to the GPS
1343 close( s->fd ); s->fd = -1;
1344 close( s->ctrl_fd ); s->ctrl_fd = -1;
1346 // Power down the GPS interface
1349 sem_destroy(&s->fix_sem);
1351 memset(s, 0, sizeof(*s));
1353 D("gps deinit complete");
1358 static void gps_state_init( GpsState* state )
1360 char gps_data[PROPERTY_VALUE_MAX];
1361 char gps_ctrl[PROPERTY_VALUE_MAX];
1362 char gps_supl[PROPERTY_VALUE_MAX];
1363 char buf[PROPERTY_VALUE_MAX+20];
1367 struct sigevent tmr_event;
1369 state->init = STATE_INIT;
1370 state->control[0] = -1;
1371 state->control[1] = -1;
1373 state->ctrl_fd = -1;
1374 state->min_interval = 1000;
1376 if (sem_init(&state->fix_sem, 0, 1) != 0) {
1377 D("gps semaphore initialization failed! errno = %d", errno);
1381 // Power up the GPS interface
1384 // look for a kernel-provided gps_data port
1385 strcpy(gps_data,"/dev/ttyUSB3") ; //RvdB
1387 if (property_get("ro.kernel.gps.data",gps_data,"/dev/ttyUSB3") == 0) {
1388 D("no kernel-provided gps device data name");
1393 state->fd = open_serialport( gps_data );
1394 if (state->fd < 0) {
1395 ALOGE("could not open gps serial device %s: %s", gps_data, strerror(errno) );
1400 D("gps will read from %s", gps_data);
1402 // look for a kernel-provided gps_ctrl port
1403 strcpy(gps_ctrl,"/dev/ttyUSB4") ;//RvdB
1406 if (property_get("ro.kernel.gps.ctl",gps_ctrl,"/dev/ttyUSB4") == 0) {
1407 D("no kernel-provided gps device ctrl name");
1412 state->ctrl_fd = open_serialport( gps_ctrl );
1413 if (state->ctrl_fd < 0) {
1414 ALOGE("could not open gps serial device %s: %s", gps_ctrl, strerror(errno) );
1421 D("gps will be controlled by %s", gps_ctrl);
1423 // Turn on GPS function
1424 serial_write(state->ctrl_fd,"AT^WPEND\r\n");
1426 // look for a kernel-provided supl
1427 strcpy(gps_supl,"http://supl.nokia.com") ; //RvdB
1429 if (property_get("ro.kernel.gps.supl",gps_supl,"http://supl.nokia.com") == 0) {
1430 D("no kernel-provided supl");
1433 close(state->ctrl_fd);
1434 state->ctrl_fd = -1;
1439 sprintf(buf,"AT^WPURL=%s\r\n",gps_supl);
1440 serial_write(state->ctrl_fd,buf);
1442 // configure AGPS to work speed optimal
1443 serial_write(state->ctrl_fd,"AT^WPDOM=2\r\n");
1446 serial_write(state->ctrl_fd,"AT^WPDGP\r\n");
1448 if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
1449 ALOGE("could not create thread control socket pair: %s", strerror(errno));
1454 if ( pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0 ) {
1455 ALOGE("could not create gps thread: %s", strerror(errno));
1460 D("gps state initialized");
1465 gps_state_done( state );
1470 /*****************************************************************/
1471 /*****************************************************************/
1473 /***** I N T E R F A C E *****/
1475 /*****************************************************************/
1476 /*****************************************************************/
1478 static int gps_init(GpsCallbacks* callbacks)
1480 GpsState* s = _gps_state;
1488 s->callbacks = *callbacks;
1493 static void gps_cleanup(void)
1495 GpsState* s = _gps_state;
1502 static int gps_start(void)
1504 GpsState* s = _gps_state;
1507 D("%s: called with uninitialized state !!", __FUNCTION__);
1511 D("%s: called", __FUNCTION__);
1517 static int gps_stop(void)
1519 GpsState* s = _gps_state;
1522 D("%s: called with uninitialized state !!", __FUNCTION__);
1526 D("%s: called", __FUNCTION__);
1532 static int gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty)
1537 /** Injects current location from another location provider
1538 * (typically cell ID).
1539 * latitude and longitude are measured in degrees
1540 * expected accuracy is measured in meters
1542 static int gps_inject_location(double latitude, double longitude, float accuracy)
1547 static void gps_delete_aiding_data(GpsAidingData flags)
1551 static int gps_set_position_mode(GpsPositionMode mode, GpsPositionRecurrence recurrence,
1552 uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time)
1554 GpsState* s = _gps_state;
1557 D("%s: called with uninitialized state !!", __FUNCTION__);
1561 // only standalone supported for now.
1562 if (mode == GPS_POSITION_MODE_STANDALONE) {
1564 if (mode == GPS_POSITION_MODE_MS_BASED ||
1565 mode == GPS_POSITION_MODE_MS_ASSISTED) {
1569 s->min_interval = min_interval;
1571 D("gps fix frquency set to %d secs", min_interval);
1576 /***** AGpsInterface *****/
1579 * Opens the AGPS interface and provides the callback routines
1580 * to the implemenation of this interface.
1582 static void agps_init( AGpsCallbacks* callbacks )
1584 D("%s() is called", __FUNCTION__);
1586 GpsState* s = _gps_state;
1589 D("%s: called with uninitialized state !!", __FUNCTION__);
1593 s->a_callbacks = *callbacks;
1597 * Notifies that a data connection is available and sets
1598 * the name of the APN to be used for SUPL.
1600 static int agps_conn_open( const char* apn )
1602 D("%s() is called", __FUNCTION__);
1605 GpsState* s = _gps_state;
1608 D("%s: called with uninitialized state !!", __FUNCTION__);
1616 * Notifies that the AGPS data connection has been closed.
1618 static int agps_conn_closed( void )
1620 D("%s() is called", __FUNCTION__);
1622 GpsState* s = _gps_state;
1625 D("%s: called with uninitialized state !!", __FUNCTION__);
1633 * Notifies that a data connection is not available for AGPS.
1635 static int agps_conn_failed( void )
1637 D("%s() is called", __FUNCTION__);
1639 GpsState* s = _gps_state;
1642 D("%s: called with uninitialized state !!", __FUNCTION__);
1650 * Sets the hostname and port for the AGPS server.
1652 static int agps_set_server( AGpsType type, const char* hostname, int port )
1656 D("%s() is called", __FUNCTION__);
1657 D("type=%d, hostname=%s, port=%d", type, hostname, port);
1659 GpsState* s = _gps_state;
1662 D("%s: called with uninitialized state !!", __FUNCTION__);
1667 sprintf(buf,"AT^WPURL=%s\r\n",hostname);
1668 serial_write(s->ctrl_fd,buf);
1674 static const AGpsInterface sAGpsInterface = {
1675 sizeof(AGpsInterface),
1684 /***** GpsXtraInterface *****/
1685 static int xtra_init(GpsXtraCallbacks* callbacks)
1687 D("%s() is called", __FUNCTION__);
1688 GpsState* s = _gps_state;
1690 s->xtra_callbacks = *callbacks;
1695 static int xtra_inject_xtra_data(char* data, int length)
1697 D("%s() is called", __FUNCTION__);
1698 D("xtra size = %d, data ptr = 0x%x\n", length, (int) data);
1700 GpsState* s = _gps_state;
1707 static const GpsXtraInterface sGpsXtraInterface = {
1708 sizeof(GpsXtraInterface),
1710 xtra_inject_xtra_data,
1713 static const void* gps_get_extension(const char* name)
1715 D("%s('%s') is called", __FUNCTION__, name);
1717 if (!strcmp(name, GPS_XTRA_INTERFACE)) {
1718 return &sGpsXtraInterface;
1719 } else if (!strcmp(name, AGPS_INTERFACE)) {
1720 return &sAGpsInterface;
1725 static const GpsInterface sGpsInterface = {
1726 sizeof(GpsInterface),
1732 gps_inject_location,
1733 gps_delete_aiding_data,
1734 gps_set_position_mode,
1738 // As under Android, there are no exceptions in C++ base system, we will hide all exported symbols
1739 // except the required ones. This will generate better code!
1742 #define DLL_PUBLIC __attribute__ ((visibility ("default")))
1743 #define DLL_LOCAL __attribute__ ((visibility ("hidden")))
1750 static const GpsInterface* gps_get_hardware_interface(struct gps_device_t* dev)
1752 ALOGV("get_interface was called");
1753 return &sGpsInterface;
1756 static int open_gps(const struct hw_module_t* module, char const* name,
1757 struct hw_device_t** device)
1759 struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));
1760 memset(dev, 0, sizeof(*dev));
1762 dev->common.tag = HARDWARE_DEVICE_TAG;
1763 dev->common.version = 0;
1764 dev->common.module = (struct hw_module_t*)module;
1765 dev->get_gps_interface = gps_get_hardware_interface;
1767 *device = (struct hw_device_t*)dev;
1772 static struct hw_module_methods_t gps_module_methods = {
1776 DLL_PUBLIC struct hw_module_t HAL_MODULE_INFO_SYM = {
1777 .tag = HARDWARE_MODULE_TAG,
1780 .id = GPS_HARDWARE_MODULE_ID,
1781 .name = "GPS Module",
1782 .author = "Eduardo José Tagle",
1783 .methods = &gps_module_methods,