cleanup specfile for packaging
[profile/ivi/gpsd.git] / gpsctl.c
1 /* gpsctl.c -- tweak the control settings on a GPS
2  *
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.
5  *
6  */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <sys/types.h>
10 #include "gpsd_config.h"
11 #if HAVE_SYS_IOCTL_H
12  #include <sys/ioctl.h>
13 #endif /* HAVE_SYS_IOCTL_H */
14 #ifndef S_SPLINT_S
15 #include <unistd.h>
16 #endif /* S_SPLINT_S */
17 #include <stdbool.h>
18 #include <string.h>
19 #include <stdarg.h>
20 #include <errno.h>
21 #include <assert.h>
22 #include <signal.h>
23
24 #include "gpsd.h"
25 #include "revision.h"
26
27 static int debuglevel;
28
29 /*
30  * Set this as high or higher than the maximum number of subtype 
31  * probes in drivers.c.
32  */
33 #define REDIRECT_SNIFF  15
34
35 void gpsd_report(int errlevel UNUSED, const char *fmt, ... )
36 /* our version of the logger */
37 {
38     if (errlevel <= debuglevel) {
39         va_list ap;
40         va_start(ap, fmt);
41         (void)fputs("gpsctl: ", stderr);
42         (void)vfprintf(stderr, fmt, ap);
43         va_end(ap);
44     }
45 }
46
47 /*@ -noret @*/
48 static gps_mask_t get_packet(struct gps_device_t *session)
49 /* try to get a well-formed packet from the GPS */
50 {
51     gps_mask_t fieldmask;
52
53     for (;;) {
54         int waiting = 0;
55         (void)ioctl(session->gpsdata.gps_fd, FIONREAD, &waiting);
56         if (waiting == 0) {
57             (void)usleep(300);
58             continue;
59         }
60         fieldmask = gpsd_poll(session);
61         if ((fieldmask &~ ONLINE_IS)!=0)
62             return fieldmask;
63     }
64 }
65 /*@ +noret @*/
66
67 static int gps_query(struct gps_data_t *gpsdata, const char *fmt, ... )
68 /* query a gpsd instance for new data */
69 {
70     char buf[BUFSIZ];
71     va_list ap;
72     int ret;
73
74     va_start(ap, fmt);
75     (void)vsnprintf(buf, sizeof(buf)-2, fmt, ap);
76     va_end(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");
81         return -1;
82     }
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);
87     }
88     return ret;
89
90 }
91
92 static void onsig(int sig)
93 {
94     if (sig == SIGALRM) {
95         gpsd_report(LOG_ERROR, "packet recognition timed out.\n");
96         exit(1);
97     } else {
98         gpsd_report(LOG_ERROR, "killed by signal %d\n", sig);
99         exit(0);
100     }
101 }
102
103 int main(int argc, char **argv)
104 {
105     int option, status;
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
115     char cooked[BUFSIZ];
116     ssize_t cooklen = 0;
117 #endif /* ALLOW_RECONFIGURE */
118
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) {
121         switch (option) {
122         case 'b':               /* switch to vendor binary mode */
123             to_binary = true;
124             break;
125         case 'c':
126 #ifdef ALLOW_RECONFIGURE
127             rate = optarg;
128 #else
129             gpsd_report(LOG_ERROR, "cycle-change capability has been conditioned out.\n");
130 #endif /* ALLOW_RECONFIGURE */
131             break;
132         case 'x':               /* ship specified control string */
133 #ifdef ALLOW_CONTROLSEND
134             control = optarg;
135             lowlevel = true;
136             if ((cooklen = hex_escapes(cooked, control)) <= 0) {
137                 gpsd_report(LOG_ERROR, 
138                             "invalid escape string (error %d)\n", (int)cooklen);
139                 exit(1);
140             }
141 #else
142             gpsd_report(LOG_ERROR, "control_send capability has been conditioned out.\n");          
143 #endif /* ALLOW_CONTROLSEND */
144             break;
145         case 'e':               /* echo specified control string with wrapper */
146             lowlevel = true;
147             echo = true;
148             break;
149         case 'f':               /* force direct access to the device */
150             lowlevel = true;
151             break;
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);
157                 else
158                     (void)fputc('\t', stdout);
159                 if ((*dp)->speed_switcher != NULL)
160                     (void)fputs("-s\t", stdout);
161                 else
162                     (void)fputc('\t', stdout);
163                 if ((*dp)->rate_switcher != NULL)
164                     (void)fputs("-c\t", stdout);
165                 else
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);
171                 else
172                     (void)fputc('\t', stdout);
173 #endif /* ALLOW_CONTROLSEND */
174                 (void)puts((*dp)->type_name);
175             }
176             exit(0);
177         case 'n':               /* switch to NMEA mode */
178 #ifdef ALLOW_RECONFIGURE
179             to_nmea = true;
180 #else
181             gpsd_report(LOG_ERROR, "speed-change capability has been conditioned out.\n");
182 #endif /* ALLOW_RECONFIGURE */
183             break;
184         case 'r':               /* force-switch to default mode */
185 #ifdef ALLOW_RECONFIGURE
186             reset = true;
187             lowlevel = false;   /* so we'll abort if the daemon is running */
188 #else
189             gpsd_report(LOG_ERROR, "reset capability has been conditioned out.\n");
190 #endif /* ALLOW_RECONFIGURE */
191             break;
192         case 's':               /* change output baud rate */
193 #ifdef ALLOW_RECONFIGURE
194             speed = optarg;
195 #else
196             gpsd_report(LOG_ERROR, "speed-change capability has been conditioned out.\n");
197 #endif /* ALLOW_RECONFIGURE */
198             break;
199         case 't':               /* force the device type */
200             devtype = optarg;
201             break;
202         case 'T':               /* set the timeout on packet recognition */
203             timeout = (unsigned)atoi(optarg);
204             break;
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 */
211             break;
212         case 'V':
213             (void)fprintf(stderr, "gpsctl: version %s (revision %s)\n",
214                           VERSION, REVISION);
215             break;
216         case 'h':
217         default:
218             fprintf(stderr, USAGE);
219             break;
220         }
221     }
222
223     if (optind < argc)
224         device = argv[optind];
225
226     if (devtype != NULL) {
227         int matchcount = 0;
228         for (dp = gpsd_drivers; *dp; dp++) {
229             if (strstr((*dp)->type_name, devtype) != NULL) {
230                 forcetype = *dp;
231                 matchcount++;
232             }
233         }
234         if (matchcount == 0)
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);
239         } else {
240             forcetype = NULL;
241             gpsd_report(LOG_ERROR, "%d driver type names match '%s'.\n",
242                         matchcount, devtype);
243         }
244     }    
245
246     if ((int)to_nmea + (int)to_binary + (int)reset > 1) {
247         gpsd_report(LOG_ERROR, "make up your mind, would you?\n");
248         exit(0);
249     }
250
251     (void) signal(SIGINT, onsig);
252     (void) signal(SIGTERM, onsig);
253     (void) signal(SIGQUIT, onsig);
254
255     /*@-nullpass@*/ /* someday, add null annotation to the gpsopen_r() params */
256     if (!lowlevel) {
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));
261             lowlevel = true;
262         }
263     }
264     /*@-nullpass@*/
265
266     /* ^ someday, add out annotation to the gpspoll() param  and remove */
267     if (!lowlevel) {
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);
274             exit(1);
275         }
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);
280             exit(1);
281         }
282
283         if (gpsdata.devices.ndevices == 0) {
284             gpsd_report(LOG_ERROR, "no devices connected.\n"); 
285             (void)gps_close(&gpsdata);
286             exit(1);
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);
291             exit(1);
292         }
293         gpsd_report(LOG_PROG,"%d device(s) found.\n",gpsdata.devices.ndevices);
294
295         if (gpsdata.devices.ndevices == 1) {
296             devlistp = &gpsdata.devices.list[0];
297             device = devlistp->path;
298         } else {
299             int i;
300             assert(device != NULL);
301             for (i = 0; i < gpsdata.devices.ndevices; i++)
302                 if (strcmp(device, gpsdata.devices.list[i].path) == 0)
303                     goto foundit;
304             gpsd_report(LOG_ERROR, "specified device not found.\n");
305             (void)gps_close(&gpsdata);
306             exit(1);
307         foundit:
308             devlistp = &gpsdata.devices.list[i];
309         }
310
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);
315             exit(0);
316         }
317
318         status = 0;
319 #ifdef ALLOW_RECONFIGURE
320         if (reset)
321         {
322             gpsd_report(LOG_PROG, "cannot reset with gpsd running.\n");
323             exit(0);
324         }
325
326         /*@-boolops@*/
327         if (to_nmea) {
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);
331                 status = 1;
332             } else
333                 gpsd_report(LOG_PROG, "%s mode change succeeded\n", gpsdata.dev.path);
334         }
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);
339                 status = 1;
340             } else
341                 gpsd_report(LOG_PROG, "%s mode change succeeded\n", gpsdata.dev.path);
342         }
343         /*@+boolops@*/
344         if (speed != NULL) {
345             char parity = 'N';
346             char stopbits = '1';
347             if (strchr(speed, ':') == NULL)
348                 (void)gps_query(&gpsdata,
349                                 "?DEVICE={\"path\":\"%s\",\"bps\":%s}\r\n", 
350                                 device, speed);
351             else {
352                 char *modespec = strchr(speed, ':');
353                 /*@ +charint @*/
354                 status = 0;
355                 if (modespec!=NULL) {
356                     *modespec = '\0';
357                     if (strchr("78", *++modespec) == NULL) {
358                         gpsd_report(LOG_ERROR, "No support for that word lengths.\n");
359                         status = 1;
360                     }
361                     parity = *++modespec;
362                     if (strchr("NOE", parity) == NULL) {
363                         gpsd_report(LOG_ERROR, "What parity is '%c'?\n", parity);
364                         status = 1;
365                     }
366                     stopbits = *++modespec;
367                     if (strchr("12", stopbits) == NULL) {
368                         gpsd_report(LOG_ERROR, "Stop bits must be 1 or 2.\n");
369                         status = 1;
370                     }
371                 }
372                 if (status == 0)
373                     (void)gps_query(&gpsdata, 
374                                     "?DEVICE={\"path\":\"%s\",\"bps\":%s,\"parity\":\"%c\",\"stopbits\":%c}\r\n", 
375                                     device, speed, parity, stopbits);
376             }
377             if (atoi(speed) != (int)gpsdata.dev.baudrate) {
378                 gpsd_report(LOG_ERROR, "%s driver won't support %s%c%c\n", 
379                             gpsdata.dev.path,
380                             speed, parity, stopbits);
381                 status = 1;
382             } else
383                 gpsd_report(LOG_PROG, "%s change to %s%c%c succeeded\n", 
384                             gpsdata.dev.path,
385                             speed, parity, stopbits);
386         }
387         if (rate != NULL) {
388             (void)gps_query(&gpsdata, 
389                             "?DEVICE={\"path\":\"%s\",\"cycle\":%s}\n", 
390                             device, rate);
391         }
392 #endif /* ALLOW_RECONFIGURE */
393         (void)gps_close(&gpsdata);
394         exit(status);
395 #ifdef ALLOW_RECONFIGURE
396     } else if (reset) {
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 */
401         int i;
402
403         if (device == NULL || forcetype == NULL) {
404                 gpsd_report(LOG_ERROR, "device and type must be specified for the reset operation.\n");
405                 exit(1);
406             }
407
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);
421         }
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);
426         gpsd_wrap(&session);
427         exit(0);
428         /*@ +mustfreeonly +immediatetrans @*/
429 #endif /* ALLOW_RECONFIGURE */
430     } else {
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 */
436
437         if (echo)
438             context.readonly = true;
439
440         (void) alarm(timeout);
441         (void) signal(SIGALRM, onsig);
442         /*
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.
446          */
447         if (!(forcetype != NULL && echo)) {
448             int seq;
449
450             if (device == NULL) {
451                 gpsd_report(LOG_ERROR, "device must be specified for low-level access.\n");
452                 exit(1);
453             }
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",
459                               device, errno);
460                 exit(2);
461             }
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");
467                     exit(2);
468                 } else {
469                     gpsd_report(LOG_IO,
470                                 "autodetection after %d reads.\n", seq);
471                     (void) alarm(0);
472                     break;
473                 }
474             }
475             gpsd_report(LOG_PROG, "%s looks like a %s at %d.\n",
476                         device, gpsd_id(&session), session.gpsdata.dev.baudrate);
477
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);
480             }
481
482             /* 
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.
488              */
489             if (strcmp(session.device_type->type_name, "Generic NMEA") == 0) {
490                 int dummy;
491                 for (dummy = 0; dummy < REDIRECT_SNIFF; dummy++) {
492                     if ((get_packet(&session) & DEVICEID_SET)!=0)
493                         break;
494                 }
495             }
496             gpsd_report(LOG_SHOUT, "%s identified as a %s at %d.\n",
497                         device, gpsd_id(&session), session.gpsdata.dev.baudrate);
498         }
499
500         /* if no control operation was specified, we're done */
501         if (speed==NULL && !to_nmea && !to_binary && control==NULL)
502             exit(0);
503
504         /* maybe user wants to see the packet rather than send it */
505         if (echo)
506             session.gpsdata.gps_fd = fileno(stdout);
507
508         /* control op specified; maybe we forced the type */
509         if (forcetype != NULL)
510             (void)gpsd_switch_driver(&session, forcetype->type_name);
511
512         /* now perform the actual control function */
513         status = 0;
514 #ifdef ALLOW_RECONFIGURE
515         /*@ -nullderef @*/
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);
521                 status = 1;
522             } else {
523                 int target_mode = to_nmea ? MODE_NMEA : MODE_BINARY;
524                 int target_type = to_nmea ? NMEA_PACKET : session.device_type->packet_type;
525
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);
530
531
532                 /* 
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.
540                  */
541                 if (!echo) {
542                     /* suppresses probing for subtypes */
543                     context.readonly = true;
544                     (void)sleep(1);
545                     (void) alarm(timeout);
546                     for (;;) {
547                         if (get_packet(&session) == ERROR_SET) {
548                             continue;
549                         } else if (session.packet.type == target_type) {
550                             (void)alarm(0);
551                             break;
552                         }
553                     }
554                     context.readonly = false;
555                 }
556                 /*@ -nullpass @*/
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);
559                 /*@ +nullpass @*/
560             }
561         }
562         if (speed) {
563             char parity = echo ? 'N': session.gpsdata.dev.parity;
564             int stopbits = echo ? 1 : session.gpsdata.dev.stopbits;
565             char *modespec;
566
567             modespec = strchr(speed, ':');
568             /*@ +charint @*/
569             status = 0;
570             if (modespec!=NULL) {
571                 *modespec = '\0';
572                 if (strchr("78", *++modespec) == NULL) {
573                     gpsd_report(LOG_ERROR, "No support for that word lengths.\n");
574                     status = 1;
575                 }
576                 parity = *++modespec;
577                 if (strchr("NOE", parity) == NULL) {
578                     gpsd_report(LOG_ERROR, "What parity is '%c'?\n", parity);
579                     status = 1;
580                 }
581                 stopbits = *++modespec;
582                 if (strchr("12", parity) == NULL) {
583                     gpsd_report(LOG_ERROR, "Stop bits must be 1 or 2.\n");
584                     status = 1;
585                 }
586                 stopbits = (int)(stopbits-'0');
587             }
588             if (status == 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);
593                     status = 1;
594                 }
595                 else if (session.device_type->speed_switcher(&session, 
596                                                              (speed_t)atoi(speed),
597                                                              parity, 
598                                                              stopbits)) {
599                     /*
600                      * See the 'deep black magic' comment in
601                      * gpsd.c:set_serial() Probably not needed here,
602                      * but it can't hurt.
603                      */
604                     (void)tcdrain(session.gpsdata.gps_fd);
605                     (void)usleep(50000);
606                     gpsd_report(LOG_PROG, "%s change to %s%c%d succeeded\n", 
607                             session.gpsdata.dev.path,
608                             speed, parity, stopbits);
609                 } else {
610                     gpsd_report(LOG_ERROR, "%s driver won't support %s%c%d.\n",
611                                 session.gpsdata.dev.path,
612                                 speed, parity, stopbits);
613                     status = 1;
614                 }
615             }
616         }
617         if (rate) {
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);
624                 status = 1;
625             } else {
626                 double rate_dbl = strtod(rate, NULL);
627
628                 if (!session.device_type->rate_switcher(&session, rate_dbl)) {
629                     gpsd_report(LOG_ERROR, "rate switch failed.\n");
630                     status = 1;
631                 }
632             }
633             context.readonly = write_enable;
634         }
635 #endif /* ALLOW_RECONFIGURE */
636 #ifdef ALLOW_CONTROLSEND
637         /*@ -compdef @*/
638         if (control) {
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);
645                 status = 1;
646             } else {
647                 if (session.device_type->control_send(&session, 
648                                                       cooked, 
649                                                       (size_t)cooklen) == -1) {
650                     gpsd_report(LOG_ERROR, "control transmission failed.\n");
651                     status = 1;
652                 }
653             }
654             context.readonly = write_enable;
655         }
656         /*@ +compdef @*/
657 #endif /* ALLOW_CONTROLSEND */
658
659         if (forcetype == NULL || !echo) {
660             /*
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.
664              */
665             (void)usleep(300000);
666
667             gpsd_wrap(&session);
668         }
669         exit(status);
670         /*@ +nullderef @*/
671         /*@ +mustfreeonly +immediatetrans @*/
672     }
673 }