Devuan fork of gpsd
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

927 lines
34 KiB

/* gpsctl.c -- tweak the control settings on a GPS
*
* This file is Copyright 2010 by the GPSD project
* SPDX-License-Identifier: BSD-2-clause
*
*/
#include "include/gpsd_config.h" /* must be before all includes */
#include <assert.h>
#include <errno.h>
#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#endif
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* for strlcat() and strlcpy() */
#include <sys/select.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include "include/gpsd.h"
#ifdef SHM_EXPORT_ENABLE
#include <sys/ipc.h>
#include <sys/shm.h>
#endif /* SHM_EXPORT_ENABLE */
#define HIGH_LEVEL_TIMEOUT 8
static int debuglevel;
static bool explicit_timeout = false;
static unsigned int timeout = 0; /* no timeout */
static struct gps_context_t context;
static bool hunting = true;
/*
* Set this as high or higher than the maximum number of subtype
* probes in drivers.c.
*/
#define REDIRECT_SNIFF 15
/* allow the device to settle after a control operation */
static void settle(struct gps_device_t *session)
{
struct timespec delay;
/*
* See the 'deep black magic' comment in serial.c:set_serial().
*/
(void)tcdrain(session->gpsdata.gps_fd);
/* wait 50,000 uSec */
delay.tv_sec = 0;
delay.tv_nsec = 50000000L;
nanosleep(&delay, NULL);
(void)tcdrain(session->gpsdata.gps_fd);
}
/*
* Allows any response other than ERROR. Use it for queries where a
* failure return (due to, for example, a missing driver method) is
* immediate, but successful responses have unpredictable lag.
*/
#define NON_ERROR 0 /* must be distinct from any gps_mask_t value */
/* ship a command and wait on an expected response type */
static bool gps_query(struct gps_data_t *gpsdata,
gps_mask_t expect,
const int timeout,
const char *fmt, ... )
{
static fd_set rfds;
char buf[BUFSIZ];
va_list ap;
time_t starttime;
struct timespec tv;
sigset_t oldset, blockset;
(void)sigemptyset(&blockset);
(void)sigaddset(&blockset, SIGHUP);
(void)sigaddset(&blockset, SIGINT);
(void)sigaddset(&blockset, SIGTERM);
(void)sigaddset(&blockset, SIGQUIT);
(void)sigprocmask(SIG_BLOCK, &blockset, &oldset);
va_start(ap, fmt);
(void)vsnprintf(buf, sizeof(buf)-2, fmt, ap);
va_end(ap);
if (buf[strlen(buf)-1] != '\n')
(void)strlcat(buf, "\n", sizeof(buf));
if (write(gpsdata->gps_fd, buf, strlen(buf)) <= 0) {
GPSD_LOG(LOG_ERROR, &context.errout, "gps_query(), write failed\n");
return false;
}
GPSD_LOG(LOG_PROG, &context.errout, "gps_query(), wrote, %s\n", buf);
FD_ZERO(&rfds);
starttime = time(NULL);
for (;;) {
FD_CLR(gpsdata->gps_fd, &rfds);
GPSD_LOG(LOG_PROG, &context.errout, "waiting...\n");
tv.tv_sec = 2;
tv.tv_nsec = 0;
// (socket_t) to pacify codacy.
if (pselect((socket_t)gpsdata->gps_fd + 1, &rfds, NULL, NULL,
&tv, &oldset) == -1) {
if (errno == EINTR || !FD_ISSET(gpsdata->gps_fd, &rfds))
continue;
GPSD_LOG(LOG_ERROR, &context.errout, "select %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
GPSD_LOG(LOG_PROG, &context.errout, "reading...\n");
(void)gps_read(gpsdata, NULL, 0);
if (ERROR_SET & gpsdata->set) {
GPSD_LOG(LOG_ERROR, &context.errout, "error '%s'\n",
gpsdata->error);
return false;
}
if ((expect == NON_ERROR) || (expect & gpsdata->set) != 0)
return true;
else if (timeout > 0 && (time(NULL) - starttime > timeout)) {
GPSD_LOG(LOG_ERROR, &context.errout,
"timed out after %d seconds\n",
timeout);
return false;
}
}
return false;
}
static void onsig(int sig)
{
if (sig == SIGALRM) {
GPSD_LOG(LOG_ERROR, &context.errout, "packet recognition timed out.\n");
exit(EXIT_FAILURE);
} else {
GPSD_LOG(LOG_ERROR, &context.errout, "killed by signal %d\n", sig);
exit(EXIT_SUCCESS);
}
}
static char *gpsd_id(struct gps_device_t *session)
/* full ID of the device for reports, including subtype */
{
static char buf[128];
if ((session == NULL) || (session->device_type == NULL) ||
(session->device_type->type_name == NULL))
return "unknown,";
(void)strlcpy(buf, session->device_type->type_name, sizeof(buf));
if (session->subtype[0] != '\0') {
(void)strlcat(buf, " ", sizeof(buf));
(void)strlcat(buf, session->subtype, sizeof(buf));
}
return (buf);
}
/* recognize when we've achieved sync */
static void ctlhook(struct gps_device_t *device UNUSED,
gps_mask_t changed UNUSED)
{
static int packet_counter = 0;
/*
* If it's NMEA, go back around enough times for the type probes to
* reveal any secret identity (like SiRF or UBX) the chip might have.
* If it's not, getting more packets might fetch subtype information.
*/
if (packet_counter++ >= REDIRECT_SNIFF)
{
hunting = false;
(void) alarm(0);
}
}
static void usage(void)
{
(void)printf("usage: gpsctl [OPTIONS] [device]\n\n"
#ifdef HAVE_GETOPT_LONG
" --binary Switch device to native binary mode.\n"
" --debug DEBUGLEVEL Set debug level to DEBUGLEVEL.\n"
" --direct Force direct access to the device.\n"
" --echo Echo specified control string with wrapper.\n"
" --help Show this help, then exit\n"
" --list List known device types and exit.\n"
" --nmea Switch device to NMEA mode.\n"
" --rate RATE Change receiver cyclte time to RATE.\n"
" --reset Force reset to default mode.\n"
#ifdef SHM_EXPORT_ENABLE
" --rmshm Remove the SHM export segment and exit.\n"
#endif // SHM_EXPORT_ENABLE
" --ship CONTROL Ship specified control string.\n"
" --speed SPEED Set device speed to SPEED.\n"
" --timeout TIMEOUT Set the timeout on packet recognition.\n"
" --type DEVTYPE Force the device type.\n"
" --version Show version, then exit\n"
#endif // HAVE_GETOPT_LONG
" -? Show this help, then exit\n"
" -b Switch device to native binary mode.\n"
" -c RATE Change receiver cyclte time to RATE.\n"
" -D DEBUGLEVEL Set debug level to DEBUGLEVEL.\n"
" -e Echo specified control string with wrapper.\n"
" -f Force direct access to the device.\n"
" -h Show this help, then exit\n"
" -l List known device types and exit.\n"
" -n Switch device to NMEA mode.\n"
#ifdef SHM_EXPORT_ENABLE
" -R Remove the SHM export segment and exit.\n"
#endif // SHM_EXPORT_ENABLE
" -r Force reset to default mode.\n"
" -s SPEED Set device speed to SPEED.\n"
" -t DEVTYPE Force the device type.\n"
" -T TIMEOUT Set the timeout on packet recognition.\n"
" -V Show version, then exit\n"
" -x CONTROL Ship specified control string.\n");
}
int main(int argc, char **argv)
{
int ch, status;
char *device = NULL, *devtype = NULL;
char *speed = NULL, *control = NULL, *rate = NULL;
bool to_binary = false, to_nmea = false, reset = false;
bool control_stdout = false;
bool lowlevel=false, echo=false;
struct gps_data_t gpsdata;
const struct gps_type_t *forcetype = NULL;
const struct gps_type_t **dp;
char cooked[BUFSIZ];
ssize_t cooklen = 0;
const char *optstring = "?bec:fhlnrs:t:x:D:RT:V";
#ifdef HAVE_GETOPT_LONG
int option_index = 0;
static struct option long_options[] = {
{"binary", no_argument, NULL, 'b'},
{"debug", required_argument, NULL, 'D'},
{"direct", no_argument, NULL, 'f'},
{"echo", no_argument, NULL, 'e'},
{"help", no_argument, NULL, 'h'},
{"list", no_argument, NULL, 'l'},
{"nmea", no_argument, NULL, 'n'},
{"rate", required_argument, NULL, 'c'},
#ifdef SHM_EXPORT_ENABLE
{"rmshm", required_argument, NULL, 'R'},
#endif
{"ship", required_argument, NULL, 'x'},
{"speed", required_argument, NULL, 's'},
{"timeout", required_argument, NULL, 'T'},
{"type", required_argument, NULL, 't'},
{"version", no_argument, NULL, 'V' },
{NULL, 0, NULL, 0},
};
#endif
/* We need this before any logging happens (for report_mutex) */
gps_context_init(&context, "gpsctl");
while (1) {
#ifdef HAVE_GETOPT_LONG
ch = getopt_long(argc, argv, optstring, long_options, &option_index);
#else
ch = getopt(argc, argv, optstring);
#endif
if (ch == -1) {
break;
}
switch (ch) {
case 'b': /* switch to vendor binary mode */
to_binary = true;
break;
case 'c':
rate = optarg;
break;
case 'D': /* set debugging level */
debuglevel = atoi(optarg);
gps_enable_debug(debuglevel, stderr);
break;
case 'e': /* echo specified control string with wrapper */
lowlevel = true;
control_stdout = true; /* Prevent message going to stdout */
echo = true;
break;
case 'f': /* force direct access to the device */
lowlevel = true;
break;
case 'l': /* list known device types */
for (dp = gpsd_drivers; *dp; dp++) {
if ((*dp)->mode_switcher != NULL)
(void)fputs("-[bn]\t", stdout);
else
(void)fputc('\t', stdout);
if ((*dp)->speed_switcher != NULL)
(void)fputs("-s\t", stdout);
else
(void)fputc('\t', stdout);
if ((*dp)->rate_switcher != NULL)
(void)fputs("-c\t", stdout);
else
(void)fputc('\t', stdout);
if ((*dp)->control_send != NULL)
(void)fputs("-x\t", stdout);
else
(void)fputc('\t', stdout);
(void)puts((*dp)->type_name);
}
exit(EXIT_SUCCESS);
case 'n': /* switch to NMEA mode */
to_nmea = true;
break;
#ifdef SHM_EXPORT_ENABLE
case 'R': /* remove the SHM export segment */
status = shmget(getenv("GPSD_SHM_KEY") ?
(key_t)strtol(getenv("GPSD_SHM_KEY"), NULL, 0) :
(key_t)GPSD_SHM_KEY, 0, 0);
if (status == -1) {
GPSD_LOG(LOG_WARN, &context.errout,
"GPSD SHM segment does not exist.\n");
exit(1);
} else {
status = shmctl(status, IPC_RMID, NULL);
if (status == -1) {
GPSD_LOG(LOG_ERROR, &context.errout,
"shmctl failed, errno = %d (%s)\n",
errno, strerror(errno));
exit(1);
}
}
exit(0);
#endif /* SHM_EXPORT_ENABLE */
case 'r': /* force-switch to default mode */
reset = true;
lowlevel = false; /* so we'll abort if the daemon is running */
break;
case 's': /* change output baud rate */
speed = optarg;
break;
case 'T': /* set the timeout on packet recognition */
timeout = (unsigned)atoi(optarg);
explicit_timeout = true;
break;
case 't': /* force the device type */
devtype = optarg;
/* experimental kluge */
if (strcmp(devtype, "u-blox") == 0)
timeout = 2;
break;
case 'x': /* ship specified control string */
control = optarg;
lowlevel = true;
if ((cooklen = hex_escapes(cooked, control)) <= 0) {
GPSD_LOG(LOG_ERROR, &context.errout,
"invalid escape string (error %d)\n", (int)cooklen);
exit(EXIT_FAILURE);
}
break;
case 'V':
(void)fprintf(stderr, "%s: version %s (revision %s)\n",
argv[0], VERSION, REVISION);
exit(EXIT_SUCCESS);
case '?':
FALLTHROUGH
case 'h':
usage();
exit(EXIT_SUCCESS);
default:
usage();
exit(EXIT_FAILURE);
}
}
if (optind < argc)
device = argv[optind];
if (devtype != NULL) {
int matchcount = 0;
for (dp = gpsd_drivers; *dp; dp++) {
if (strstr((*dp)->type_name, devtype) != NULL) {
forcetype = *dp;
matchcount++;
}
}
if (matchcount == 0)
GPSD_LOG(LOG_ERROR, &context.errout,
"no driver type name matches '%s'.\n", devtype);
else if (matchcount == 1) {
assert(forcetype != NULL);
GPSD_LOG( LOG_PROG,&context.errout,
"%s driver selected.\n", forcetype->type_name);
} else {
forcetype = NULL;
GPSD_LOG(LOG_ERROR, &context.errout,
"%d driver type names match '%s'.\n",
matchcount, devtype);
}
}
if (((int)to_nmea + (int)to_binary + (int)reset) > 1) {
GPSD_LOG(LOG_ERROR, &context.errout, "make up your mind, would you?\n");
exit(EXIT_SUCCESS);
}
(void) signal(SIGINT, onsig);
(void) signal(SIGTERM, onsig);
(void) signal(SIGQUIT, onsig);
if (!lowlevel) {
/* Try to open the stream to gpsd. */
if (gps_open(NULL, NULL, &gpsdata) != 0) {
GPSD_LOG(LOG_ERROR, &context.errout,
"no gpsd running or network error: %s.\n",
gps_errstr(errno));
lowlevel = true;
}
}
if (!lowlevel) {
int i, devcount;
if (!explicit_timeout)
timeout = HIGH_LEVEL_TIMEOUT;
/* what devices have we available? */
if (!gps_query(&gpsdata, DEVICELIST_SET, (int)timeout,
"?DEVICES;\r\n")) {
GPSD_LOG(LOG_ERROR, &context.errout,
"no DEVICES response received.\n");
(void)gps_close(&gpsdata);
exit(EXIT_FAILURE);
}
if (gpsdata.devices.ndevices == 0) {
GPSD_LOG(LOG_ERROR, &context.errout, "no devices connected.\n");
(void)gps_close(&gpsdata);
exit(EXIT_FAILURE);
} else if (gpsdata.devices.ndevices > 1 && device == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"multiple devices and no device specified.\n");
(void)gps_close(&gpsdata);
exit(EXIT_FAILURE);
}
GPSD_LOG(LOG_PROG, &context.errout,
"%d device(s) found.\n",gpsdata.devices.ndevices);
/* try to mine the devicelist return for the data we want */
if (gpsdata.devices.ndevices == 1 && device == NULL) {
device = gpsdata.dev.path;
i = 0;
} else {
assert(device != NULL);
for (i = 0; i < gpsdata.devices.ndevices; i++)
if (strcmp(device, gpsdata.devices.list[i].path) == 0) {
goto devicelist_entry_matches;
}
GPSD_LOG(LOG_ERROR, &context.errout,
"specified device not found in device list.\n");
(void)gps_close(&gpsdata);
exit(EXIT_FAILURE);
devicelist_entry_matches:;
}
gpsdata.dev = gpsdata.devices.list[i];
devcount = gpsdata.devices.ndevices;
/* if the device has not identified, watch it until it does so */
if (gpsdata.dev.driver[0] == '\0') {
if (gps_stream(&gpsdata, WATCH_ENABLE|WATCH_JSON, NULL) == -1) {
GPSD_LOG(LOG_ERROR, &context.errout, "stream set failed.\n");
(void)gps_close(&gpsdata);
exit(EXIT_FAILURE);
}
while (devcount > 0) {
/* Wait for input data */
if (!gps_waiting(&gpsdata, timeout * 1000000)) {
GPSD_LOG(LOG_ERROR, &context.errout,
"timed out waiting for device\n");
(void)gps_close(&gpsdata);
exit(EXIT_FAILURE);
}
errno = 0;
if (gps_read(&gpsdata, NULL, 0) == -1) {
GPSD_LOG(LOG_ERROR, &context.errout, "data read failed.\n");
(void)gps_close(&gpsdata);
exit(EXIT_FAILURE);
}
if (gpsdata.set & DEVICE_SET) {
--devcount;
assert(gpsdata.dev.path[0]!='\0' &&
gpsdata.dev.driver[0]!='\0');
if (strcmp(gpsdata.dev.path, device) == 0) {
goto matching_device_seen;
}
}
}
GPSD_LOG(LOG_ERROR, &context.errout, "data read failed.\n");
(void)gps_close(&gpsdata);
exit(EXIT_FAILURE);
matching_device_seen:;
}
/* sanity check */
if (gpsdata.dev.driver[0] == '\0') {
GPSD_LOG(LOG_SHOUT, &context.errout,
"%s can't be identified.\n",
gpsdata.dev.path);
(void)gps_close(&gpsdata);
exit(EXIT_SUCCESS);
}
/* if no control operation was specified, just ID the device */
if (speed==NULL && rate == NULL && !to_nmea && !to_binary && !reset) {
(void)printf("%s identified as a %s",
gpsdata.dev.path, gpsdata.dev.driver);
if (gpsdata.dev.subtype[0] != '\0') {
(void)fputc(' ', stdout);
(void)fputs(gpsdata.dev.subtype, stdout);
}
if (gpsdata.dev.baudrate > 0)
(void)printf(" at %u baud", gpsdata.dev.baudrate);
(void)fputc('.', stdout);
(void)fputc('\n', stdout);
}
status = 0;
if (reset)
{
GPSD_LOG(LOG_PROG, &context.errout,
"cannot reset with gpsd running.\n");
exit(EXIT_SUCCESS);
}
/*
* We used to wait on DEVICE_SET here. That doesn't work
* anymore because when the demon generates its response it
* sets the mode bit in the response from the current packet
* type, which may not have changed (probably will not have
* changed) even though the command to switch modes has been
* sent and will shortly take effect.
*/
if (to_nmea) {
if (!gps_query(&gpsdata, NON_ERROR, (int)timeout,
"?DEVICE={\"path\":\"%s\",\"native\":0}\r\n",
device)) {
GPSD_LOG(LOG_ERROR, &context.errout,
"%s mode change to NMEA failed\n",
gpsdata.dev.path);
status = 1;
} else
GPSD_LOG(LOG_PROG, &context.errout,
"%s mode change succeeded\n", gpsdata.dev.path);
}
else if (to_binary) {
if (!gps_query(&gpsdata, NON_ERROR, (int)timeout,
"?DEVICE={\"path\":\"%s\",\"native\":1}\r\n",
device)) {
GPSD_LOG(LOG_ERROR, &context.errout,
"%s mode change to native mode failed\n",
gpsdata.dev.path);
status = 1;
} else
GPSD_LOG(LOG_PROG, &context.errout,
"%s mode change succeeded\n",
gpsdata.dev.path);
}
if (speed != NULL) {
char parity = 'N';
char stopbits = '1';
if (strchr(speed, ':') == NULL)
(void)gps_query(&gpsdata,
DEVICE_SET, (int)timeout,
"?DEVICE={\"path\":\"%s\",\"bps\":%s}\r\n",
device, speed);
else {
char *modespec = strchr(speed, ':');
status = 0;
if (modespec!=NULL) {
*modespec = '\0';
if (strchr("78", *++modespec) == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"No support for that word length.\n");
status = 1;
}
parity = *++modespec;
if (strchr("NOE", parity) == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"What parity is '%c'?\n", parity);
status = 1;
}
stopbits = *++modespec;
if (strchr("12", stopbits) == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"Stop bits must be 1 or 2.\n");
status = 1;
}
}
if (status == 0)
(void)gps_query(&gpsdata,
DEVICE_SET, (int)timeout,
"?DEVICE={\"path\":\"%s\",\"bps\":%s,"
"\"parity\":\"%c\",\"stopbits\":%c}\r\n",
device, speed, parity, stopbits);
}
if (atoi(speed) != (int)gpsdata.dev.baudrate) {
GPSD_LOG(LOG_ERROR, &context.errout,
"%s driver won't support %s%c%c\n",
gpsdata.dev.path,
speed, parity, stopbits);
status = 1;
} else
GPSD_LOG(LOG_PROG, &context.errout,
"%s change to %s%c%c succeeded\n",
gpsdata.dev.path,
speed, parity, stopbits);
}
if (rate != NULL) {
(void)gps_query(&gpsdata,
DEVICE_SET, (int)timeout,
"?DEVICE={\"path\":\"%s\",\"cycle\":%s}\r\n",
device, rate);
}
(void)gps_close(&gpsdata);
exit(status);
} else if (reset) {
/* hard reset will go through lower-level operations */
const int speeds[] = {4800, 9600, 19200, 38400, 57600, 115200, 230400,
460800};
static struct gps_device_t session; /* zero this too */
int i;
if (device == NULL || forcetype == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"device and type must be specified for the "
"reset operation.\n");
exit(EXIT_FAILURE);
}
context.errout.debug = debuglevel;
session.context = &context;
gpsd_tty_init(&session);
(void)strlcpy(session.gpsdata.dev.path, device,
sizeof(session.gpsdata.dev.path));
session.device_type = forcetype;
(void)gpsd_open(&session);
(void)gpsd_set_raw(&session);
(void)session.device_type->speed_switcher(&session, 4800, 'N', 1);
(void)tcdrain(session.gpsdata.gps_fd);
for(i = 0; i < (int)(sizeof(speeds) / sizeof(speeds[0])); i++) {
(void)gpsd_set_speed(&session, speeds[i], 'N', 1);
(void)session.device_type->speed_switcher(&session, 4800, 'N', 1);
(void)tcdrain(session.gpsdata.gps_fd);
}
gpsd_set_speed(&session, 4800, 'N', 1);
for (i = 0; i < 3; i++)
if (session.device_type->mode_switcher)
session.device_type->mode_switcher(&session, MODE_NMEA);
gpsd_wrap(&session);
exit(EXIT_SUCCESS);
} else {
/* access to the daemon failed, use the low-level facilities */
static struct gps_device_t session; /* zero this too */
fd_set all_fds;
fd_set rfds;
/*
* Unless the user explicitly requested it, always run to end of
* hunt rather than timing out. Otherwise we can easily get messages
* that spuriously look like failure at high baud rates.
*/
gps_context_init(&context, "gpsctl");
context.errout.debug = debuglevel;
session.context = &context; /* in case gps_init isn't called */
if (echo)
context.readonly = true;
if (timeout > 0) {
(void) alarm(timeout);
(void) signal(SIGALRM, onsig);
}
/*
* Unless the user has forced a type and only wants to see the
* string (not send it) we now need to try to open the device
* and find out what is actually there.
*/
if (!(forcetype != NULL && echo)) {
socket_t maxfd = 0;
int activated = -1;
if (device == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"device must be specified for low-level access.\n");
exit(EXIT_FAILURE);
}
gpsd_init(&session, &context, device);
activated = gpsd_activate(&session, O_PROBEONLY);
if ( 0 > activated ) {
if ( PLACEHOLDING_FD == activated ) {
(void)printf("%s identified as a %s.\n",
device, gpsd_id(&session));
exit(EXIT_SUCCESS);
}
GPSD_LOG(LOG_ERROR, &context.errout,
"initial GPS device %s open failed\n",
device);
exit(EXIT_FAILURE);
}
GPSD_LOG(LOG_INF, &context.errout,
"device %s activated\n", session.gpsdata.dev.path);
FD_SET(session.gpsdata.gps_fd, &all_fds);
if (session.gpsdata.gps_fd > maxfd)
maxfd = session.gpsdata.gps_fd;
/* initialize the GPS context's time fields */
gpsd_time_init(&context, time(NULL));
/* grab packets until we time out, get sync, or fail sync */
for (hunting = true; hunting; )
{
fd_set efds;
switch(gpsd_await_data(&rfds, &efds, maxfd, &all_fds,
&context.errout))
{
case AWAIT_GOT_INPUT:
break;
case AWAIT_NOT_READY:
/* no recovery from bad fd is possible */
if (FD_ISSET(session.gpsdata.gps_fd, &efds))
exit(EXIT_FAILURE);
continue;
case AWAIT_FAILED:
exit(EXIT_FAILURE);
}
switch(gpsd_multipoll(FD_ISSET(session.gpsdata.gps_fd, &rfds),
&session, ctlhook, 0))
{
case DEVICE_READY:
FD_SET(session.gpsdata.gps_fd, &all_fds);
break;
case DEVICE_UNREADY:
FD_CLR(session.gpsdata.gps_fd, &all_fds);
break;
case DEVICE_ERROR:
/* this is where a failure to sync lands */
GPSD_LOG(LOG_WARN, &context.errout,
"device error, bailing out.\n");
exit(EXIT_FAILURE);
case DEVICE_EOF:
GPSD_LOG(LOG_WARN, &context.errout,
"device signed off, bailing out.\n");
exit(EXIT_SUCCESS);
default:
break;
}
}
GPSD_LOG(LOG_PROG, &context.errout,
"%s looks like a %s at %d.\n",
device, gpsd_id(&session),
session.gpsdata.dev.baudrate);
if (forcetype!=NULL &&
strcmp("NMEA0183", session.device_type->type_name) != 0 &&
strcmp(forcetype->type_name,
session.device_type->type_name) != 0) {
GPSD_LOG(LOG_ERROR, &context.errout,
"'%s' doesn't match non-generic type '%s' "
"of selected device.\n",
forcetype->type_name,
session.device_type->type_name);
}
}
if(!control_stdout)
(void)printf("%s identified as a %s at %u baud.\n",
device, gpsd_id(&session),
session.gpsdata.dev.baudrate);
/* if no control operation was specified, we're done */
if (speed==NULL && !to_nmea && !to_binary && control==NULL)
exit(EXIT_SUCCESS);
/* maybe user wants to see the packet rather than send it */
if (echo)
session.gpsdata.gps_fd = fileno(stdout);
/* control op specified; maybe we forced the type */
if (forcetype != NULL)
(void)gpsd_switch_driver(&session, forcetype->type_name);
/* now perform the actual control function */
status = 0;
if (to_nmea || to_binary) {
bool write_enable = context.readonly;
context.readonly = false;
if (session.device_type->mode_switcher == NULL) {
GPSD_LOG(LOG_SHOUT, &context.errout,
"%s devices have no mode switch.\n",
session.device_type->type_name);
status = 1;
} else {
int target_mode = to_nmea ? MODE_NMEA : MODE_BINARY;
GPSD_LOG(LOG_SHOUT, &context.errout,
"switching to mode %s.\n",
to_nmea ? "NMEA" : "BINARY");
session.device_type->mode_switcher(&session, target_mode);
settle(&session);
}
context.readonly = write_enable;
}
if (speed) {
char parity = echo ? 'N': session.gpsdata.dev.parity;
int stopbits = echo ? 1 : session.gpsdata.dev.stopbits;
char *modespec;
modespec = strchr(speed, ':');
status = 0;
if (modespec!=NULL) {
*modespec = '\0';
if (strchr("78", *++modespec) == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"No support for that word lengths.\n");
status = 1;
}
parity = *++modespec;
if (strchr("NOE", parity) == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"What parity is '%c'?\n", parity);
status = 1;
}
stopbits = *++modespec;
if (strchr("12", parity) == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"Stop bits must be 1 or 2.\n");
status = 1;
}
stopbits = (int)(stopbits-'0');
}
if (status == 0) {
if (session.device_type->speed_switcher == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"%s devices have no speed switch.\n",
session.device_type->type_name);
status = 1;
}
else if (session.device_type->speed_switcher(&session,
(speed_t)atoi(speed),
parity,
stopbits)) {
settle(&session);
GPSD_LOG(LOG_PROG, &context.errout,
"%s change to %s%c%d succeeded\n",
session.gpsdata.dev.path,
speed, parity, stopbits);
} else {
GPSD_LOG(LOG_ERROR, &context.errout,
"%s driver won't support %s%c%d.\n",
session.gpsdata.dev.path,
speed, parity, stopbits);
status = 1;
}
}
}
if (rate) {
bool write_enable = context.readonly;
context.readonly = false;
if (session.device_type->rate_switcher == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"%s devices have no rate switcher.\n",
session.device_type->type_name);
status = 1;
} else {
double rate_dbl = strtod(rate, NULL);
if (!session.device_type->rate_switcher(&session, rate_dbl)) {
GPSD_LOG(LOG_ERROR, &context.errout,
"rate switch failed.\n");
status = 1;
}
settle(&session);
}
context.readonly = write_enable;
}
if (control) {
bool write_enable = context.readonly;
context.readonly = false;
if (session.device_type->control_send == NULL) {
GPSD_LOG(LOG_ERROR, &context.errout,
"%s devices have no control sender.\n",
session.device_type->type_name);
status = 1;
} else {
if (session.device_type->control_send(&session,
cooked,
(size_t)cooklen) == -1) {
GPSD_LOG(LOG_ERROR, &context.errout,
"control transmission failed.\n");
status = 1;
}
settle(&session);
}
context.readonly = write_enable;
}
exit(status);
}
}
/* end */
// vim: set expandtab shiftwidth=4