#include "config.h" #include #include #include #include #include #if defined(HAVE_SYS_MODEM_H) #include #endif /* HAVE_SYS_MODEM_H */ #include "gpsd.h" /* Workaround for HP-UX 11.23, which is missing CRTSCTS */ #ifndef CRTSCTS # ifdef CNEW_RTSCTS # define CRTSCTS CNEW_RTSCTS # else # define CRTSCTS 0 # endif /* CNEW_RTSCTS */ #endif /* !CRTSCTS */ speed_t gpsd_get_speed(struct termios* ttyctl) { speed_t code = cfgetospeed(ttyctl); switch (code) { case B0: return(0); case B300: return(300); case B1200: return(1200); case B2400: return(2400); case B4800: return(4800); case B9600: return(9600); case B19200: return(19200); case B38400: return(38400); case B57600: return(57600); default: return(115200); } } void gpsd_set_speed(struct gps_device_t *session, speed_t speed, unsigned char parity, unsigned int stopbits) { speed_t rate; if (speed < 300) rate = B0; else if (speed < 1200) rate = B300; else if (speed < 2400) rate = B1200; else if (speed < 4800) rate = B2400; else if (speed < 9600) rate = B4800; else if (speed < 19200) rate = B9600; else if (speed < 38400) rate = B19200; else if (speed < 57600) rate = B38400; else if (speed < 115200) rate = B57600; else rate = B115200; if (rate!=cfgetispeed(&session->ttyset) || (unsigned int)parity!=session->gpsdata.parity || stopbits!=session->gpsdata.stopbits) { /*@ignore@*/ (void)cfsetispeed(&session->ttyset, rate); (void)cfsetospeed(&session->ttyset, rate); /*@end@*/ session->ttyset.c_iflag &=~ (PARMRK | INPCK); session->ttyset.c_cflag &=~ (CSIZE | CSTOPB | PARENB | PARODD); session->ttyset.c_cflag |= (stopbits==2 ? CS7|CSTOPB : CS8); switch (parity) { case 'E': session->ttyset.c_iflag |= INPCK; session->ttyset.c_cflag |= PARENB; break; case 'O': session->ttyset.c_iflag |= INPCK; session->ttyset.c_cflag |= PARENB | PARODD; break; } session->ttyset.c_cflag &=~ CSIZE; session->ttyset.c_cflag |= (CSIZE & (stopbits==2 ? CS7 : CS8)); if (tcsetattr(session->gpsdata.gps_fd, TCSANOW, &session->ttyset) != 0) return; (void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH); } gpsd_report(1, "speed %d, %d%c%d\n", speed, 9-stopbits, parity, stopbits); session->gpsdata.baudrate = (unsigned int)speed; session->gpsdata.parity = (unsigned int)parity; session->gpsdata.stopbits = stopbits; packet_reset(session); } int gpsd_open(struct gps_device_t *session) { gpsd_report(1, "opening GPS data source at '%s'\n", session->gpsdata.gps_device); if ((session->gpsdata.gps_fd = open(session->gpsdata.gps_device, O_RDWR|O_NOCTTY)) < 0) { gpsd_report(1, "device open failed: %s\n", strerror(errno)); return -1; } session->packet_type = BAD_PACKET; if (isatty(session->gpsdata.gps_fd)!=0) { #ifdef NON_NMEA_ENABLE struct gps_type_t **dp; for (dp = gpsd_drivers; *dp; dp++) { (void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH); /* toss stale data */ if ((*dp)->probe!=NULL && (*dp)->probe(session)!=0) { gpsd_report(3, "probe found %s driver...\n", (*dp)->typename); /*@i1@*/session->device_type = *dp; if (session->device_type->initializer) session->device_type->initializer(session); /*@i1@*/return session->gpsdata.gps_fd; } } gpsd_report(3, "no probe matched...\n"); #endif /* NON_NMEA_ENABLE */ /* Save original terminal parameters */ if (tcgetattr(session->gpsdata.gps_fd,&session->ttyset_old) != 0) return -1; (void)memcpy(&session->ttyset, &session->ttyset_old, sizeof(session->ttyset)); /* * Only block until we get at least one character, whatever the * third arg of read(2) says. */ /*@ ignore @*/ memset(session->ttyset.c_cc,0,sizeof(session->ttyset.c_cc)); session->ttyset.c_cc[VMIN] = 1; /*@ end @*/ /* * Tip from Chris Kuethe: the FIDI chip used in the Trip-Nav * 200 (and possibly other USB GPSes) gets completely hosed * in the presence of flow control. Thus, turn off CRTSCTS. */ session->ttyset.c_cflag &= ~(PARENB | PARODD | CRTSCTS); session->ttyset.c_cflag |= CREAD | CLOCAL; session->ttyset.c_iflag = session->ttyset.c_oflag = session->ttyset.c_lflag = (tcflag_t) 0; session->counter = session->baudindex = 0; gpsd_set_speed(session, gpsd_get_speed(&session->ttyset_old), 'N', 1); } return session->gpsdata.gps_fd; } /* * This constant controls how long the packet sniffer will spend looking * for a packet leader before it gives up. It *must* be larger than * MAX_PACKET_LENGTH or we risk never syncing up at all. Large values * will produce annoying startup lag. */ #define SNIFF_RETRIES 600 bool gpsd_next_hunt_setting(struct gps_device_t *session) /* advance to the next hunt setting */ { /* every rate we're likely to see on a GPS */ static unsigned int rates[] = {0, 4800, 9600, 19200, 38400, 57600}; if (session->counter++ >= SNIFF_RETRIES) { session->counter = 0; if (session->baudindex++ >= (unsigned int)(sizeof(rates)/sizeof(rates[0]))) { session->baudindex = 0; if (session->gpsdata.stopbits++ >= 2) return false; /* hunt is over, no sync */ } gpsd_set_speed(session, rates[session->baudindex], 'N', session->gpsdata.stopbits); } return true; /* keep hunting */ } void gpsd_close(struct gps_device_t *session) { if (session->gpsdata.gps_fd != -1) { if (isatty(session->gpsdata.gps_fd)!=0) { /* force hangup on close on systems that don't do HUPCL properly */ /*@ ignore @*/ (void)cfsetispeed(&session->ttyset, (speed_t)B0); (void)cfsetospeed(&session->ttyset, (speed_t)B0); /*@ end @*/ (void)tcsetattr(session->gpsdata.gps_fd,TCSANOW, &session->ttyset); } /* this is the clean way to do it */ session->ttyset_old.c_cflag |= HUPCL; (void)tcsetattr(session->gpsdata.gps_fd,TCSANOW,&session->ttyset_old); (void)close(session->gpsdata.gps_fd); session->gpsdata.gps_fd = -1; } }