1 /* gpsctl.c -- tweak the control settings on a GPS
3 * This file is Copyright (c) 2010 by the GPSD project
4 * BSD terms apply: see the file COPYING in the distribution root for details.
10 #include "gpsd_config.h"
12 #include <sys/ioctl.h>
13 #endif /* HAVE_SYS_IOCTL_H */
16 #endif /* S_SPLINT_S */
27 static int debuglevel;
30 * Set this as high or higher than the maximum number of subtype
31 * probes in drivers.c.
33 #define REDIRECT_SNIFF 15
35 void gpsd_report(int errlevel UNUSED, const char *fmt, ... )
36 /* our version of the logger */
38 if (errlevel <= debuglevel) {
41 (void)fputs("gpsctl: ", stderr);
42 (void)vfprintf(stderr, fmt, ap);
48 static gps_mask_t get_packet(struct gps_device_t *session)
49 /* try to get a well-formed packet from the GPS */
55 (void)ioctl(session->gpsdata.gps_fd, FIONREAD, &waiting);
60 fieldmask = gpsd_poll(session);
61 if ((fieldmask &~ ONLINE_IS)!=0)
67 static int gps_query(struct gps_data_t *gpsdata, const char *fmt, ... )
68 /* query a gpsd instance for new data */
75 (void)vsnprintf(buf, sizeof(buf)-2, fmt, ap);
77 if (buf[strlen(buf)-1] != '\n')
78 (void)strlcat(buf, "\n", BUFSIZ);
79 if (write(gpsdata->gps_fd, buf, strlen(buf)) <= 0) {
80 gpsd_report(LOG_ERROR, "gps_query(), write failed\n");
83 gpsd_report(LOG_PROG, "gps_query(), wrote, %s\n", buf);
84 ret = gps_read(gpsdata);
85 if (ERROR_IS & gpsdata->set) {
86 gpsd_report(LOG_ERROR, "gps_query() error '%s'\n", gpsdata->error);
92 static void onsig(int sig)
95 gpsd_report(LOG_ERROR, "packet recognition timed out.\n");
98 gpsd_report(LOG_ERROR, "killed by signal %d\n", sig);
103 int main(int argc, char **argv)
106 char *device = NULL, *devtype = NULL;
107 char *speed = NULL, *control = NULL, *rate = NULL;
108 bool to_binary = false, to_nmea = false, reset = false;
109 bool lowlevel=false, echo=false;
110 struct gps_data_t gpsdata;
111 const struct gps_type_t *forcetype = NULL;
112 const struct gps_type_t **dp;
113 unsigned int timeout = 4;
114 #ifdef ALLOW_CONTROLSEND
117 #endif /* ALLOW_RECONFIGURE */
119 #define USAGE "usage: gpsctl [-l] [-b | -n | -r] [-D n] [-s speed] [-c rate] [-T timeout] [-V] [-t devtype] [-x control] [-e] <device>\n"
120 while ((option = getopt(argc, argv, "bec:fhlnrs:t:x:D:T:V")) != -1) {
122 case 'b': /* switch to vendor binary mode */
126 #ifdef ALLOW_RECONFIGURE
129 gpsd_report(LOG_ERROR, "cycle-change capability has been conditioned out.\n");
130 #endif /* ALLOW_RECONFIGURE */
132 case 'x': /* ship specified control string */
133 #ifdef ALLOW_CONTROLSEND
136 if ((cooklen = hex_escapes(cooked, control)) <= 0) {
137 gpsd_report(LOG_ERROR,
138 "invalid escape string (error %d)\n", (int)cooklen);
142 gpsd_report(LOG_ERROR, "control_send capability has been conditioned out.\n");
143 #endif /* ALLOW_CONTROLSEND */
145 case 'e': /* echo specified control string with wrapper */
149 case 'f': /* force direct access to the device */
152 case 'l': /* list known device types */
153 for (dp = gpsd_drivers; *dp; dp++) {
154 #ifdef ALLOW_RECONFIGURE
155 if ((*dp)->mode_switcher != NULL)
156 (void)fputs("-[bn]\t", stdout);
158 (void)fputc('\t', stdout);
159 if ((*dp)->speed_switcher != NULL)
160 (void)fputs("-s\t", stdout);
162 (void)fputc('\t', stdout);
163 if ((*dp)->rate_switcher != NULL)
164 (void)fputs("-c\t", stdout);
166 (void)fputc('\t', stdout);
167 #endif /* ALLOW_RECONFIGURE */
168 #ifdef ALLOW_CONTROLSEND
169 if ((*dp)->control_send != NULL)
170 (void)fputs("-x\t", stdout);
172 (void)fputc('\t', stdout);
173 #endif /* ALLOW_CONTROLSEND */
174 (void)puts((*dp)->type_name);
177 case 'n': /* switch to NMEA mode */
178 #ifdef ALLOW_RECONFIGURE
181 gpsd_report(LOG_ERROR, "speed-change capability has been conditioned out.\n");
182 #endif /* ALLOW_RECONFIGURE */
184 case 'r': /* force-switch to default mode */
185 #ifdef ALLOW_RECONFIGURE
187 lowlevel = false; /* so we'll abort if the daemon is running */
189 gpsd_report(LOG_ERROR, "reset capability has been conditioned out.\n");
190 #endif /* ALLOW_RECONFIGURE */
192 case 's': /* change output baud rate */
193 #ifdef ALLOW_RECONFIGURE
196 gpsd_report(LOG_ERROR, "speed-change capability has been conditioned out.\n");
197 #endif /* ALLOW_RECONFIGURE */
199 case 't': /* force the device type */
202 case 'T': /* set the timeout on packet recognition */
203 timeout = (unsigned)atoi(optarg);
205 case 'D': /* set debugging level */
206 debuglevel = atoi(optarg);
207 gpsd_hexdump_level = debuglevel;
208 #ifdef CLIENTDEBUG_ENABLE
209 gps_enable_debug(debuglevel, stderr);
210 #endif /* CLIENTDEBUG_ENABLE */
213 (void)fprintf(stderr, "gpsctl: version %s (revision %s)\n",
218 fprintf(stderr, USAGE);
224 device = argv[optind];
226 if (devtype != NULL) {
228 for (dp = gpsd_drivers; *dp; dp++) {
229 if (strstr((*dp)->type_name, devtype) != NULL) {
235 gpsd_report(LOG_ERROR, "no driver type name matches '%s'.\n", devtype);
236 else if (matchcount == 1) {
237 assert(forcetype != NULL);
238 gpsd_report(LOG_PROG, "%s driver selected.\n", forcetype->type_name);
241 gpsd_report(LOG_ERROR, "%d driver type names match '%s'.\n",
242 matchcount, devtype);
246 if ((int)to_nmea + (int)to_binary + (int)reset > 1) {
247 gpsd_report(LOG_ERROR, "make up your mind, would you?\n");
251 (void) signal(SIGINT, onsig);
252 (void) signal(SIGTERM, onsig);
253 (void) signal(SIGQUIT, onsig);
255 /*@-nullpass@*/ /* someday, add null annotation to the gpsopen_r() params */
257 /* Try to open the stream to gpsd. */
258 if (gps_open_r(NULL, NULL, &gpsdata) != 0) {
259 gpsd_report(LOG_ERROR, "no gpsd running or network error: %s.\n",
260 netlib_errstr(errno));
266 /* ^ someday, add out annotation to the gpspoll() param and remove */
268 /* OK, there's a daemon instance running. Do things the easy way */
269 struct devconfig_t *devlistp;
270 (void)gps_read(&gpsdata);
271 if ((gpsdata.set & DEVICELIST_SET) != 0) {
272 gpsd_report(LOG_ERROR, "no VERSION response received; update your gpsd.\n");
273 (void)gps_close(&gpsdata);
276 (void)gps_query(&gpsdata, "?DEVICES;\n");
277 if ((gpsdata.set & DEVICELIST_SET) == 0) {
278 gpsd_report(LOG_ERROR, "no DEVICES response received.\n");
279 (void)gps_close(&gpsdata);
283 if (gpsdata.devices.ndevices == 0) {
284 gpsd_report(LOG_ERROR, "no devices connected.\n");
285 (void)gps_close(&gpsdata);
287 } else if (gpsdata.devices.ndevices > 1 && device == NULL) {
288 gpsd_report(LOG_ERROR,
289 "multiple devices and no device specified.\n");
290 (void)gps_close(&gpsdata);
293 gpsd_report(LOG_PROG,"%d device(s) found.\n",gpsdata.devices.ndevices);
295 if (gpsdata.devices.ndevices == 1) {
296 devlistp = &gpsdata.devices.list[0];
297 device = devlistp->path;
300 assert(device != NULL);
301 for (i = 0; i < gpsdata.devices.ndevices; i++)
302 if (strcmp(device, gpsdata.devices.list[i].path) == 0)
304 gpsd_report(LOG_ERROR, "specified device not found.\n");
305 (void)gps_close(&gpsdata);
308 devlistp = &gpsdata.devices.list[i];
311 /* if no control operation was specified, just ID the device */
312 if (speed==NULL && rate == NULL && !to_nmea && !to_binary && !reset) {
313 gpsd_report(LOG_SHOUT, "%s identified as %s at %d\n",
314 devlistp->path, devlistp->driver, devlistp->baudrate);
319 #ifdef ALLOW_RECONFIGURE
322 gpsd_report(LOG_PROG, "cannot reset with gpsd running.\n");
328 (void)gps_query(&gpsdata, "?DEVICE={\"path\":\"%s\",\"native\":0}\r\n", device);
329 if ((gpsdata.set & ERROR_SET) || (gpsdata.dev.driver_mode != MODE_NMEA)) {
330 gpsd_report(LOG_ERROR, "%s mode change to NMEA failed\n", gpsdata.dev.path);
333 gpsd_report(LOG_PROG, "%s mode change succeeded\n", gpsdata.dev.path);
335 else if (to_binary) {
336 (void)gps_query(&gpsdata, "?DEVICE={\"path\":\"%s\",\"native\":1}\r\n", device);
337 if ((gpsdata.set & ERROR_SET) || (gpsdata.dev.driver_mode != MODE_BINARY)) {
338 gpsd_report(LOG_ERROR, "%s mode change to native mode failed\n", gpsdata.dev.path);
341 gpsd_report(LOG_PROG, "%s mode change succeeded\n", gpsdata.dev.path);
347 if (strchr(speed, ':') == NULL)
348 (void)gps_query(&gpsdata,
349 "?DEVICE={\"path\":\"%s\",\"bps\":%s}\r\n",
352 char *modespec = strchr(speed, ':');
355 if (modespec!=NULL) {
357 if (strchr("78", *++modespec) == NULL) {
358 gpsd_report(LOG_ERROR, "No support for that word lengths.\n");
361 parity = *++modespec;
362 if (strchr("NOE", parity) == NULL) {
363 gpsd_report(LOG_ERROR, "What parity is '%c'?\n", parity);
366 stopbits = *++modespec;
367 if (strchr("12", stopbits) == NULL) {
368 gpsd_report(LOG_ERROR, "Stop bits must be 1 or 2.\n");
373 (void)gps_query(&gpsdata,
374 "?DEVICE={\"path\":\"%s\",\"bps\":%s,\"parity\":\"%c\",\"stopbits\":%c}\r\n",
375 device, speed, parity, stopbits);
377 if (atoi(speed) != (int)gpsdata.dev.baudrate) {
378 gpsd_report(LOG_ERROR, "%s driver won't support %s%c%c\n",
380 speed, parity, stopbits);
383 gpsd_report(LOG_PROG, "%s change to %s%c%c succeeded\n",
385 speed, parity, stopbits);
388 (void)gps_query(&gpsdata,
389 "?DEVICE={\"path\":\"%s\",\"cycle\":%s}\n",
392 #endif /* ALLOW_RECONFIGURE */
393 (void)gps_close(&gpsdata);
395 #ifdef ALLOW_RECONFIGURE
397 /* hard reset will go through lower-level operations */
398 const int speeds[] = {2400, 4800, 9600, 19200, 38400, 57600, 115200};
399 static struct gps_context_t context; /* start it zeroed */
400 static struct gps_device_t session; /* zero this too */
403 if (device == NULL || forcetype == NULL) {
404 gpsd_report(LOG_ERROR, "device and type must be specified for the reset operation.\n");
408 /*@ -mustfreeonly -immediatetrans @*/
409 session.context = &context;
410 gpsd_tty_init(&session);
411 (void)strlcpy(session.gpsdata.dev.path, device, sizeof(session.gpsdata.dev.path));
412 session.device_type = forcetype;
413 (void)gpsd_open(&session);
414 (void)gpsd_set_raw(&session);
415 (void)session.device_type->speed_switcher(&session, 4800, 'N', 1);
416 (void)tcdrain(session.gpsdata.gps_fd);
417 for(i = 0; i < (int)(sizeof(speeds) / sizeof(speeds[0])); i++) {
418 (void)gpsd_set_speed(&session, speeds[i], 'N', 1);
419 (void)session.device_type->speed_switcher(&session, 4800, 'N', 1);
420 (void)tcdrain(session.gpsdata.gps_fd);
422 gpsd_set_speed(&session, 4800, 'N', 1);
423 for (i = 0; i < 3; i++)
424 if (session.device_type->mode_switcher)
425 session.device_type->mode_switcher(&session, MODE_NMEA);
428 /*@ +mustfreeonly +immediatetrans @*/
429 #endif /* ALLOW_RECONFIGURE */
431 /* access to the daemon failed, use the low-level facilities */
432 static struct gps_context_t context; /* start it zeroed */
433 static struct gps_device_t session; /* zero this too */
434 /*@ -mustfreeonly -immediatetrans @*/
435 session.context = &context; /* in case gps_init isn't called */
438 context.readonly = true;
440 (void) alarm(timeout);
441 (void) signal(SIGALRM, onsig);
443 * Unless the user has forced a type and only wants to see the
444 * string (not send it) we now need to try to open the device
445 * and find out what is actually there.
447 if (!(forcetype != NULL && echo)) {
450 if (device == NULL) {
451 gpsd_report(LOG_ERROR, "device must be specified for low-level access.\n");
454 gpsd_init(&session, &context, device);
455 gpsd_report(LOG_PROG, "initialization passed.\n");
456 if (gpsd_activate(&session) == -1) {
457 gpsd_report(LOG_ERROR,
458 "activation of device %s failed, errno=%d\n",
462 /* hunt for packet type and serial parameters */
463 for (seq = 0; session.device_type == NULL; seq++) {
464 if (get_packet(&session) == ERROR_SET) {
465 gpsd_report(LOG_ERROR,
466 "autodetection failed.\n");
470 "autodetection after %d reads.\n", seq);
475 gpsd_report(LOG_PROG, "%s looks like a %s at %d.\n",
476 device, gpsd_id(&session), session.gpsdata.dev.baudrate);
478 if (forcetype!=NULL && strcmp("Generic NMEA", session.device_type->type_name) !=0 && strcmp(forcetype->type_name, session.device_type->type_name)!=0) {
479 gpsd_report(LOG_ERROR, "'%s' doesn't match non-generic type '%s' of selected device.\n", forcetype->type_name, session.device_type->type_name);
483 * If we've identified this as an NMEA device, we have to eat
484 * packets for a while to see if one of our probes elicits an
485 * ID response telling us that it's really a SiRF or
486 * something. If so, the libgpsd(3) layer will automatically
487 * redispatch to the correct driver type.
489 if (strcmp(session.device_type->type_name, "Generic NMEA") == 0) {
491 for (dummy = 0; dummy < REDIRECT_SNIFF; dummy++) {
492 if ((get_packet(&session) & DEVICEID_SET)!=0)
496 gpsd_report(LOG_SHOUT, "%s identified as a %s at %d.\n",
497 device, gpsd_id(&session), session.gpsdata.dev.baudrate);
500 /* if no control operation was specified, we're done */
501 if (speed==NULL && !to_nmea && !to_binary && control==NULL)
504 /* maybe user wants to see the packet rather than send it */
506 session.gpsdata.gps_fd = fileno(stdout);
508 /* control op specified; maybe we forced the type */
509 if (forcetype != NULL)
510 (void)gpsd_switch_driver(&session, forcetype->type_name);
512 /* now perform the actual control function */
514 #ifdef ALLOW_RECONFIGURE
516 if (to_nmea || to_binary) {
517 if (session.device_type->mode_switcher == NULL) {
518 gpsd_report(LOG_SHOUT,
519 "%s devices have no mode switch.\n",
520 session.device_type->type_name);
523 int target_mode = to_nmea ? MODE_NMEA : MODE_BINARY;
524 int target_type = to_nmea ? NMEA_PACKET : session.device_type->packet_type;
526 gpsd_report(LOG_SHOUT,
527 "switching to mode %s.\n",
528 to_nmea ? "NMEA" : "BINARY");
529 session.device_type->mode_switcher(&session, target_mode);
533 * Hunt for packet type again (mode might have
534 * changed). We've found by experiment that you can't
535 * close the connection to the device after a mode
536 * change but before you see a packet of the right
537 * type come back from it - otherwise you can hit a
538 * timing window where the mode-change control message
539 * gets ignored or flushed.
542 /* suppresses probing for subtypes */
543 context.readonly = true;
545 (void) alarm(timeout);
547 if (get_packet(&session) == ERROR_SET) {
549 } else if (session.packet.type == target_type) {
554 context.readonly = false;
557 gpsd_report(LOG_SHOUT, "after mode change, %s looks like a %s at %d.\n",
558 device, gpsd_id(&session), session.gpsdata.dev.baudrate);
563 char parity = echo ? 'N': session.gpsdata.dev.parity;
564 int stopbits = echo ? 1 : session.gpsdata.dev.stopbits;
567 modespec = strchr(speed, ':');
570 if (modespec!=NULL) {
572 if (strchr("78", *++modespec) == NULL) {
573 gpsd_report(LOG_ERROR, "No support for that word lengths.\n");
576 parity = *++modespec;
577 if (strchr("NOE", parity) == NULL) {
578 gpsd_report(LOG_ERROR, "What parity is '%c'?\n", parity);
581 stopbits = *++modespec;
582 if (strchr("12", parity) == NULL) {
583 gpsd_report(LOG_ERROR, "Stop bits must be 1 or 2.\n");
586 stopbits = (int)(stopbits-'0');
589 if (session.device_type->speed_switcher == NULL) {
590 gpsd_report(LOG_ERROR,
591 "%s devices have no speed switch.\n",
592 session.device_type->type_name);
595 else if (session.device_type->speed_switcher(&session,
596 (speed_t)atoi(speed),
600 * See the 'deep black magic' comment in
601 * gpsd.c:set_serial() Probably not needed here,
604 (void)tcdrain(session.gpsdata.gps_fd);
606 gpsd_report(LOG_PROG, "%s change to %s%c%d succeeded\n",
607 session.gpsdata.dev.path,
608 speed, parity, stopbits);
610 gpsd_report(LOG_ERROR, "%s driver won't support %s%c%d.\n",
611 session.gpsdata.dev.path,
612 speed, parity, stopbits);
618 bool write_enable = context.readonly;
619 context.readonly = false;
620 if (session.device_type->rate_switcher == NULL) {
621 gpsd_report(LOG_ERROR,
622 "%s devices have no rate switcher.\n",
623 session.device_type->type_name);
626 double rate_dbl = strtod(rate, NULL);
628 if (!session.device_type->rate_switcher(&session, rate_dbl)) {
629 gpsd_report(LOG_ERROR, "rate switch failed.\n");
633 context.readonly = write_enable;
635 #endif /* ALLOW_RECONFIGURE */
636 #ifdef ALLOW_CONTROLSEND
639 bool write_enable = context.readonly;
640 context.readonly = false;
641 if (session.device_type->control_send == NULL) {
642 gpsd_report(LOG_ERROR,
643 "%s devices have no control sender.\n",
644 session.device_type->type_name);
647 if (session.device_type->control_send(&session,
649 (size_t)cooklen) == -1) {
650 gpsd_report(LOG_ERROR, "control transmission failed.\n");
654 context.readonly = write_enable;
657 #endif /* ALLOW_CONTROLSEND */
659 if (forcetype == NULL || !echo) {
661 * Give the device time to settle before closing it. Alas, this is
662 * voodoo programming; we don't know it will have any effect, but
663 * GPSes are notoriously prone to timing-dependent errors.
665 (void)usleep(300000);
671 /*@ +mustfreeonly +immediatetrans @*/