#include "config.h"
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#if defined(HAVE_SYS_MODEM_H)
#include <sys/modem.h>
#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 */
#if defined(__CYGWIN__)
/* Workaround for Cygwin, which is missing cfmakeraw */
/* Pasted from man page; added in serial.c arbitrarily */
void cfmakeraw(struct termios *termios_p)
{
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios_p->c_cflag &= ~(CSIZE|PARENB);
termios_p->c_cflag |= CS8;
}
#endif /* defined(__CYGWIN__) */
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_NONBLOCK|O_NOCTTY)) < 0) {
gpsd_report(1, "device open failed: %s\n", strerror(errno));
return -1;
}
if (session->saved_baud != -1) {
/*@i@*/(void)cfsetispeed(&session->ttyset, (speed_t)session->saved_baud);
/*@i@*/(void)cfsetospeed(&session->ttyset, (speed_t)session->saved_baud);
(void)tcsetattr(session->gpsdata.gps_fd, TCSANOW, &session->ttyset);
(void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);
}
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->baudindex = 0;
gpsd_set_speed(session,
gpsd_get_speed(&session->ttyset_old), 'N', 1);
}
return session->gpsdata.gps_fd;
}
bool gpsd_write(struct gps_device_t *session, void const *buf, size_t len)
{
ssize_t status;
bool ok;
status = write(session->gpsdata.gps_fd, buf, len);
ok = (status == (ssize_t)len);
(void)tcdrain(session->gpsdata.gps_fd);
/* code that will check for data could be add here to print buffer as text or hex */
/* no test here now, always print as hex */
gpsd_report(5, "=> GPS: %s%s\n", gpsd_hexdump(buf, len), ok?"":" FAILED");
return ok;
}
/*
* 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 256
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->retry_counter++ >= SNIFF_RETRIES) {
session->retry_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;
}
}
syntax highlighted by Code2HTML, v. 0.9.1