1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
|
#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;
}
#ifdef FIXED_PORT_SPEED
session->saved_baud = FIXED_PORT_SPEED;
#endif
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 */
{
#ifdef FIXED_PORT_SPEED
/* just the one fixed port speed... */
static unsigned int rates[] = {FIXED_PORT_SPEED};
#else /* FIXED_PORT_SPEED not defined */
/* every rate we're likely to see on a GPS */
static unsigned int rates[] = {0, 4800, 9600, 19200, 38400, 57600};
#endif /* FIXED_PORT_SPEED defined */
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;
}
}
|