gpsctl.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. /* gpsctl.c -- tweak the control settings on a GPS
  2. *
  3. * This file is Copyright 2010 by the GPSD project
  4. * SPDX-License-Identifier: BSD-2-clause
  5. *
  6. */
  7. #include "include/gpsd_config.h" /* must be before all includes */
  8. #include <assert.h>
  9. #include <errno.h>
  10. #ifdef HAVE_GETOPT_LONG
  11. #include <getopt.h>
  12. #endif
  13. #include <signal.h>
  14. #include <stdarg.h>
  15. #include <stdbool.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h> /* for strlcat() and strlcpy() */
  19. #include <sys/select.h>
  20. #include <sys/time.h>
  21. #include <time.h>
  22. #include <unistd.h>
  23. #include "include/gpsd.h"
  24. #ifdef SHM_EXPORT_ENABLE
  25. #include <sys/ipc.h>
  26. #include <sys/shm.h>
  27. #endif /* SHM_EXPORT_ENABLE */
  28. #define HIGH_LEVEL_TIMEOUT 8
  29. static int debuglevel;
  30. static bool explicit_timeout = false;
  31. static unsigned int timeout = 0; /* no timeout */
  32. static struct gps_context_t context;
  33. static bool hunting = true;
  34. /*
  35. * Set this as high or higher than the maximum number of subtype
  36. * probes in drivers.c.
  37. */
  38. #define REDIRECT_SNIFF 15
  39. /* allow the device to settle after a control operation */
  40. static void settle(struct gps_device_t *session)
  41. {
  42. struct timespec delay;
  43. /*
  44. * See the 'deep black magic' comment in serial.c:set_serial().
  45. */
  46. (void)tcdrain(session->gpsdata.gps_fd);
  47. /* wait 50,000 uSec */
  48. delay.tv_sec = 0;
  49. delay.tv_nsec = 50000000L;
  50. nanosleep(&delay, NULL);
  51. (void)tcdrain(session->gpsdata.gps_fd);
  52. }
  53. /*
  54. * Allows any response other than ERROR. Use it for queries where a
  55. * failure return (due to, for example, a missing driver method) is
  56. * immediate, but successful responses have unpredictable lag.
  57. */
  58. #define NON_ERROR 0 /* must be distinct from any gps_mask_t value */
  59. /* ship a command and wait on an expected response type */
  60. static bool gps_query(struct gps_data_t *gpsdata,
  61. gps_mask_t expect,
  62. const int timeout,
  63. const char *fmt, ... )
  64. {
  65. static fd_set rfds;
  66. char buf[BUFSIZ];
  67. va_list ap;
  68. time_t starttime;
  69. struct timespec tv;
  70. sigset_t oldset, blockset;
  71. (void)sigemptyset(&blockset);
  72. (void)sigaddset(&blockset, SIGHUP);
  73. (void)sigaddset(&blockset, SIGINT);
  74. (void)sigaddset(&blockset, SIGTERM);
  75. (void)sigaddset(&blockset, SIGQUIT);
  76. (void)sigprocmask(SIG_BLOCK, &blockset, &oldset);
  77. va_start(ap, fmt);
  78. (void)vsnprintf(buf, sizeof(buf)-2, fmt, ap);
  79. va_end(ap);
  80. if (buf[strlen(buf)-1] != '\n')
  81. (void)strlcat(buf, "\n", sizeof(buf));
  82. if (write(gpsdata->gps_fd, buf, strlen(buf)) <= 0) {
  83. GPSD_LOG(LOG_ERROR, &context.errout, "gps_query(), write failed\n");
  84. return false;
  85. }
  86. GPSD_LOG(LOG_PROG, &context.errout, "gps_query(), wrote, %s\n", buf);
  87. FD_ZERO(&rfds);
  88. starttime = time(NULL);
  89. for (;;) {
  90. FD_CLR(gpsdata->gps_fd, &rfds);
  91. GPSD_LOG(LOG_PROG, &context.errout, "waiting...\n");
  92. tv.tv_sec = 2;
  93. tv.tv_nsec = 0;
  94. // (socket_t) to pacify codacy.
  95. if (pselect((socket_t)gpsdata->gps_fd + 1, &rfds, NULL, NULL,
  96. &tv, &oldset) == -1) {
  97. if (errno == EINTR || !FD_ISSET(gpsdata->gps_fd, &rfds))
  98. continue;
  99. GPSD_LOG(LOG_ERROR, &context.errout, "select %s\n",
  100. strerror(errno));
  101. exit(EXIT_FAILURE);
  102. }
  103. GPSD_LOG(LOG_PROG, &context.errout, "reading...\n");
  104. (void)gps_read(gpsdata, NULL, 0);
  105. if (ERROR_SET & gpsdata->set) {
  106. GPSD_LOG(LOG_ERROR, &context.errout, "error '%s'\n",
  107. gpsdata->error);
  108. return false;
  109. }
  110. if ((expect == NON_ERROR) || (expect & gpsdata->set) != 0)
  111. return true;
  112. else if (timeout > 0 && (time(NULL) - starttime > timeout)) {
  113. GPSD_LOG(LOG_ERROR, &context.errout,
  114. "timed out after %d seconds\n",
  115. timeout);
  116. return false;
  117. }
  118. }
  119. return false;
  120. }
  121. static void onsig(int sig)
  122. {
  123. if (sig == SIGALRM) {
  124. GPSD_LOG(LOG_ERROR, &context.errout, "packet recognition timed out.\n");
  125. exit(EXIT_FAILURE);
  126. } else {
  127. GPSD_LOG(LOG_ERROR, &context.errout, "killed by signal %d\n", sig);
  128. exit(EXIT_SUCCESS);
  129. }
  130. }
  131. static char *gpsd_id(struct gps_device_t *session)
  132. /* full ID of the device for reports, including subtype */
  133. {
  134. static char buf[128];
  135. if ((session == NULL) || (session->device_type == NULL) ||
  136. (session->device_type->type_name == NULL))
  137. return "unknown,";
  138. (void)strlcpy(buf, session->device_type->type_name, sizeof(buf));
  139. if (session->subtype[0] != '\0') {
  140. (void)strlcat(buf, " ", sizeof(buf));
  141. (void)strlcat(buf, session->subtype, sizeof(buf));
  142. }
  143. return (buf);
  144. }
  145. /* recognize when we've achieved sync */
  146. static void ctlhook(struct gps_device_t *device UNUSED,
  147. gps_mask_t changed UNUSED)
  148. {
  149. static int packet_counter = 0;
  150. /*
  151. * If it's NMEA, go back around enough times for the type probes to
  152. * reveal any secret identity (like SiRF or UBX) the chip might have.
  153. * If it's not, getting more packets might fetch subtype information.
  154. */
  155. if (packet_counter++ >= REDIRECT_SNIFF)
  156. {
  157. hunting = false;
  158. (void) alarm(0);
  159. }
  160. }
  161. static void usage(void)
  162. {
  163. (void)printf("usage: gpsctl [OPTIONS] [device]\n\n"
  164. #ifdef HAVE_GETOPT_LONG
  165. " --binary Switch device to native binary mode.\n"
  166. " --debug DEBUGLEVEL Set debug level to DEBUGLEVEL.\n"
  167. " --direct Force direct access to the device.\n"
  168. " --echo Echo specified control string with wrapper.\n"
  169. " --help Show this help, then exit\n"
  170. " --list List known device types and exit.\n"
  171. " --nmea Switch device to NMEA mode.\n"
  172. " --rate RATE Change receiver cyclte time to RATE.\n"
  173. " --reset Force reset to default mode.\n"
  174. #ifdef SHM_EXPORT_ENABLE
  175. " --rmshm Remove the SHM export segment and exit.\n"
  176. #endif // SHM_EXPORT_ENABLE
  177. " --ship CONTROL Ship specified control string.\n"
  178. " --speed SPEED Set device speed to SPEED.\n"
  179. " --timeout TIMEOUT Set the timeout on packet recognition.\n"
  180. " --type DEVTYPE Force the device type.\n"
  181. " --version Show version, then exit\n"
  182. #endif // HAVE_GETOPT_LONG
  183. " -? Show this help, then exit\n"
  184. " -b Switch device to native binary mode.\n"
  185. " -c RATE Change receiver cyclte time to RATE.\n"
  186. " -D DEBUGLEVEL Set debug level to DEBUGLEVEL.\n"
  187. " -e Echo specified control string with wrapper.\n"
  188. " -f Force direct access to the device.\n"
  189. " -h Show this help, then exit\n"
  190. " -l List known device types and exit.\n"
  191. " -n Switch device to NMEA mode.\n"
  192. #ifdef SHM_EXPORT_ENABLE
  193. " -R Remove the SHM export segment and exit.\n"
  194. #endif // SHM_EXPORT_ENABLE
  195. " -r Force reset to default mode.\n"
  196. " -s SPEED Set device speed to SPEED.\n"
  197. " -t DEVTYPE Force the device type.\n"
  198. " -T TIMEOUT Set the timeout on packet recognition.\n"
  199. " -V Show version, then exit\n"
  200. " -x CONTROL Ship specified control string.\n");
  201. }
  202. int main(int argc, char **argv)
  203. {
  204. int ch, status;
  205. char *device = NULL, *devtype = NULL;
  206. char *speed = NULL, *control = NULL, *rate = NULL;
  207. bool to_binary = false, to_nmea = false, reset = false;
  208. bool control_stdout = false;
  209. bool lowlevel=false, echo=false;
  210. struct gps_data_t gpsdata;
  211. const struct gps_type_t *forcetype = NULL;
  212. const struct gps_type_t **dp;
  213. char cooked[BUFSIZ];
  214. ssize_t cooklen = 0;
  215. const char *optstring = "bec:fhlnrs:t:x:D:RT:V";
  216. #ifdef HAVE_GETOPT_LONG
  217. int option_index = 0;
  218. static struct option long_options[] = {
  219. {"binary", no_argument, NULL, 'b'},
  220. {"debug", required_argument, NULL, 'D'},
  221. {"direct", no_argument, NULL, 'f'},
  222. {"echo", no_argument, NULL, 'e'},
  223. {"help", no_argument, NULL, 'h'},
  224. {"list", no_argument, NULL, 'l'},
  225. {"nmea", no_argument, NULL, 'n'},
  226. {"rate", required_argument, NULL, 'c'},
  227. #ifdef SHM_EXPORT_ENABLE
  228. {"rmshm", required_argument, NULL, 'R'},
  229. #endif
  230. {"ship", required_argument, NULL, 'x'},
  231. {"speed", required_argument, NULL, 's'},
  232. {"timeout", required_argument, NULL, 'T'},
  233. {"type", required_argument, NULL, 't'},
  234. {"version", no_argument, NULL, 'V' },
  235. {NULL, 0, NULL, 0},
  236. };
  237. #endif
  238. /* We need this before any logging happens (for report_mutex) */
  239. gps_context_init(&context, "gpsctl");
  240. while (1) {
  241. #ifdef HAVE_GETOPT_LONG
  242. ch = getopt_long(argc, argv, optstring, long_options, &option_index);
  243. #else
  244. ch = getopt(argc, argv, optstring);
  245. #endif
  246. if (ch == -1) {
  247. break;
  248. }
  249. switch (ch) {
  250. case 'b': /* switch to vendor binary mode */
  251. to_binary = true;
  252. break;
  253. case 'c':
  254. rate = optarg;
  255. break;
  256. case 'D': /* set debugging level */
  257. debuglevel = atoi(optarg);
  258. gps_enable_debug(debuglevel, stderr);
  259. break;
  260. case 'e': /* echo specified control string with wrapper */
  261. lowlevel = true;
  262. control_stdout = true; /* Prevent message going to stdout */
  263. echo = true;
  264. break;
  265. case 'f': /* force direct access to the device */
  266. lowlevel = true;
  267. break;
  268. case 'l': /* list known device types */
  269. for (dp = gpsd_drivers; *dp; dp++) {
  270. if ((*dp)->mode_switcher != NULL)
  271. (void)fputs("-[bn]\t", stdout);
  272. else
  273. (void)fputc('\t', stdout);
  274. if ((*dp)->speed_switcher != NULL)
  275. (void)fputs("-s\t", stdout);
  276. else
  277. (void)fputc('\t', stdout);
  278. if ((*dp)->rate_switcher != NULL)
  279. (void)fputs("-c\t", stdout);
  280. else
  281. (void)fputc('\t', stdout);
  282. if ((*dp)->control_send != NULL)
  283. (void)fputs("-x\t", stdout);
  284. else
  285. (void)fputc('\t', stdout);
  286. (void)puts((*dp)->type_name);
  287. }
  288. exit(EXIT_SUCCESS);
  289. case 'n': /* switch to NMEA mode */
  290. to_nmea = true;
  291. break;
  292. #ifdef SHM_EXPORT_ENABLE
  293. case 'R': /* remove the SHM export segment */
  294. status = shmget(getenv("GPSD_SHM_KEY") ?
  295. (key_t)strtol(getenv("GPSD_SHM_KEY"), NULL, 0) :
  296. (key_t)GPSD_SHM_KEY, 0, 0);
  297. if (status == -1) {
  298. GPSD_LOG(LOG_WARN, &context.errout,
  299. "GPSD SHM segment does not exist.\n");
  300. exit(1);
  301. } else {
  302. status = shmctl(status, IPC_RMID, NULL);
  303. if (status == -1) {
  304. GPSD_LOG(LOG_ERROR, &context.errout,
  305. "shmctl failed, errno = %d (%s)\n",
  306. errno, strerror(errno));
  307. exit(1);
  308. }
  309. }
  310. exit(0);
  311. #endif /* SHM_EXPORT_ENABLE */
  312. case 'r': /* force-switch to default mode */
  313. reset = true;
  314. lowlevel = false; /* so we'll abort if the daemon is running */
  315. break;
  316. case 's': /* change output baud rate */
  317. speed = optarg;
  318. break;
  319. case 'T': /* set the timeout on packet recognition */
  320. timeout = (unsigned)atoi(optarg);
  321. explicit_timeout = true;
  322. break;
  323. case 't': /* force the device type */
  324. devtype = optarg;
  325. /* experimental kluge */
  326. if (strcmp(devtype, "u-blox") == 0)
  327. timeout = 2;
  328. break;
  329. case 'x': /* ship specified control string */
  330. control = optarg;
  331. lowlevel = true;
  332. if ((cooklen = hex_escapes(cooked, control)) <= 0) {
  333. GPSD_LOG(LOG_ERROR, &context.errout,
  334. "invalid escape string (error %d)\n", (int)cooklen);
  335. exit(EXIT_FAILURE);
  336. }
  337. break;
  338. case 'V':
  339. (void)fprintf(stderr, "%s: version %s (revision %s)\n",
  340. argv[0], VERSION, REVISION);
  341. exit(EXIT_SUCCESS);
  342. case '?':
  343. FALLTHROUGH
  344. case 'h':
  345. usage();
  346. exit(EXIT_SUCCESS);
  347. default:
  348. usage();
  349. exit(EXIT_FAILURE);
  350. }
  351. }
  352. if (optind < argc)
  353. device = argv[optind];
  354. if (devtype != NULL) {
  355. int matchcount = 0;
  356. for (dp = gpsd_drivers; *dp; dp++) {
  357. if (strstr((*dp)->type_name, devtype) != NULL) {
  358. forcetype = *dp;
  359. matchcount++;
  360. }
  361. }
  362. if (matchcount == 0)
  363. GPSD_LOG(LOG_ERROR, &context.errout,
  364. "no driver type name matches '%s'.\n", devtype);
  365. else if (matchcount == 1) {
  366. assert(forcetype != NULL);
  367. GPSD_LOG( LOG_PROG,&context.errout,
  368. "%s driver selected.\n", forcetype->type_name);
  369. } else {
  370. forcetype = NULL;
  371. GPSD_LOG(LOG_ERROR, &context.errout,
  372. "%d driver type names match '%s'.\n",
  373. matchcount, devtype);
  374. }
  375. }
  376. if (((int)to_nmea + (int)to_binary + (int)reset) > 1) {
  377. GPSD_LOG(LOG_ERROR, &context.errout, "make up your mind, would you?\n");
  378. exit(EXIT_SUCCESS);
  379. }
  380. (void) signal(SIGINT, onsig);
  381. (void) signal(SIGTERM, onsig);
  382. (void) signal(SIGQUIT, onsig);
  383. if (!lowlevel) {
  384. /* Try to open the stream to gpsd. */
  385. if (gps_open(NULL, NULL, &gpsdata) != 0) {
  386. GPSD_LOG(LOG_ERROR, &context.errout,
  387. "no gpsd running or network error: %s.\n",
  388. gps_errstr(errno));
  389. lowlevel = true;
  390. }
  391. }
  392. if (!lowlevel) {
  393. int i, devcount;
  394. if (!explicit_timeout)
  395. timeout = HIGH_LEVEL_TIMEOUT;
  396. /* what devices have we available? */
  397. if (!gps_query(&gpsdata, DEVICELIST_SET, (int)timeout,
  398. "?DEVICES;\r\n")) {
  399. GPSD_LOG(LOG_ERROR, &context.errout,
  400. "no DEVICES response received.\n");
  401. (void)gps_close(&gpsdata);
  402. exit(EXIT_FAILURE);
  403. }
  404. if (gpsdata.devices.ndevices == 0) {
  405. GPSD_LOG(LOG_ERROR, &context.errout, "no devices connected.\n");
  406. (void)gps_close(&gpsdata);
  407. exit(EXIT_FAILURE);
  408. } else if (gpsdata.devices.ndevices > 1 && device == NULL) {
  409. GPSD_LOG(LOG_ERROR, &context.errout,
  410. "multiple devices and no device specified.\n");
  411. (void)gps_close(&gpsdata);
  412. exit(EXIT_FAILURE);
  413. }
  414. GPSD_LOG(LOG_PROG, &context.errout,
  415. "%d device(s) found.\n",gpsdata.devices.ndevices);
  416. /* try to mine the devicelist return for the data we want */
  417. if (gpsdata.devices.ndevices == 1 && device == NULL) {
  418. device = gpsdata.dev.path;
  419. i = 0;
  420. } else {
  421. assert(device != NULL);
  422. for (i = 0; i < gpsdata.devices.ndevices; i++)
  423. if (strcmp(device, gpsdata.devices.list[i].path) == 0) {
  424. goto devicelist_entry_matches;
  425. }
  426. GPSD_LOG(LOG_ERROR, &context.errout,
  427. "specified device not found in device list.\n");
  428. (void)gps_close(&gpsdata);
  429. exit(EXIT_FAILURE);
  430. devicelist_entry_matches:;
  431. }
  432. gpsdata.dev = gpsdata.devices.list[i];
  433. devcount = gpsdata.devices.ndevices;
  434. /* if the device has not identified, watch it until it does so */
  435. if (gpsdata.dev.driver[0] == '\0') {
  436. if (gps_stream(&gpsdata, WATCH_ENABLE|WATCH_JSON, NULL) == -1) {
  437. GPSD_LOG(LOG_ERROR, &context.errout, "stream set failed.\n");
  438. (void)gps_close(&gpsdata);
  439. exit(EXIT_FAILURE);
  440. }
  441. while (devcount > 0) {
  442. /* Wait for input data */
  443. if (!gps_waiting(&gpsdata, timeout * 1000000)) {
  444. GPSD_LOG(LOG_ERROR, &context.errout,
  445. "timed out waiting for device\n");
  446. (void)gps_close(&gpsdata);
  447. exit(EXIT_FAILURE);
  448. }
  449. errno = 0;
  450. if (gps_read(&gpsdata, NULL, 0) == -1) {
  451. GPSD_LOG(LOG_ERROR, &context.errout, "data read failed.\n");
  452. (void)gps_close(&gpsdata);
  453. exit(EXIT_FAILURE);
  454. }
  455. if (gpsdata.set & DEVICE_SET) {
  456. --devcount;
  457. assert(gpsdata.dev.path[0]!='\0' &&
  458. gpsdata.dev.driver[0]!='\0');
  459. if (strcmp(gpsdata.dev.path, device) == 0) {
  460. goto matching_device_seen;
  461. }
  462. }
  463. }
  464. GPSD_LOG(LOG_ERROR, &context.errout, "data read failed.\n");
  465. (void)gps_close(&gpsdata);
  466. exit(EXIT_FAILURE);
  467. matching_device_seen:;
  468. }
  469. /* sanity check */
  470. if (gpsdata.dev.driver[0] == '\0') {
  471. GPSD_LOG(LOG_SHOUT, &context.errout,
  472. "%s can't be identified.\n",
  473. gpsdata.dev.path);
  474. (void)gps_close(&gpsdata);
  475. exit(EXIT_SUCCESS);
  476. }
  477. /* if no control operation was specified, just ID the device */
  478. if (speed==NULL && rate == NULL && !to_nmea && !to_binary && !reset) {
  479. (void)printf("%s identified as a %s",
  480. gpsdata.dev.path, gpsdata.dev.driver);
  481. if (gpsdata.dev.subtype[0] != '\0') {
  482. (void)fputc(' ', stdout);
  483. (void)fputs(gpsdata.dev.subtype, stdout);
  484. }
  485. if (gpsdata.dev.baudrate > 0)
  486. (void)printf(" at %u baud", gpsdata.dev.baudrate);
  487. (void)fputc('.', stdout);
  488. (void)fputc('\n', stdout);
  489. }
  490. status = 0;
  491. if (reset)
  492. {
  493. GPSD_LOG(LOG_PROG, &context.errout,
  494. "cannot reset with gpsd running.\n");
  495. exit(EXIT_SUCCESS);
  496. }
  497. /*
  498. * We used to wait on DEVICE_SET here. That doesn't work
  499. * anymore because when the demon generates its response it
  500. * sets the mode bit in the response from the current packet
  501. * type, which may not have changed (probably will not have
  502. * changed) even though the command to switch modes has been
  503. * sent and will shortly take effect.
  504. */
  505. if (to_nmea) {
  506. if (!gps_query(&gpsdata, NON_ERROR, (int)timeout,
  507. "?DEVICE={\"path\":\"%s\",\"native\":0}\r\n",
  508. device)) {
  509. GPSD_LOG(LOG_ERROR, &context.errout,
  510. "%s mode change to NMEA failed\n",
  511. gpsdata.dev.path);
  512. status = 1;
  513. } else
  514. GPSD_LOG(LOG_PROG, &context.errout,
  515. "%s mode change succeeded\n", gpsdata.dev.path);
  516. }
  517. else if (to_binary) {
  518. if (!gps_query(&gpsdata, NON_ERROR, (int)timeout,
  519. "?DEVICE={\"path\":\"%s\",\"native\":1}\r\n",
  520. device)) {
  521. GPSD_LOG(LOG_ERROR, &context.errout,
  522. "%s mode change to native mode failed\n",
  523. gpsdata.dev.path);
  524. status = 1;
  525. } else
  526. GPSD_LOG(LOG_PROG, &context.errout,
  527. "%s mode change succeeded\n",
  528. gpsdata.dev.path);
  529. }
  530. if (speed != NULL) {
  531. char parity = 'N';
  532. char stopbits = '1';
  533. if (strchr(speed, ':') == NULL)
  534. (void)gps_query(&gpsdata,
  535. DEVICE_SET, (int)timeout,
  536. "?DEVICE={\"path\":\"%s\",\"bps\":%s}\r\n",
  537. device, speed);
  538. else {
  539. char *modespec = strchr(speed, ':');
  540. status = 0;
  541. if (modespec!=NULL) {
  542. *modespec = '\0';
  543. if (strchr("78", *++modespec) == NULL) {
  544. GPSD_LOG(LOG_ERROR, &context.errout,
  545. "No support for that word length.\n");
  546. status = 1;
  547. }
  548. parity = *++modespec;
  549. if (strchr("NOE", parity) == NULL) {
  550. GPSD_LOG(LOG_ERROR, &context.errout,
  551. "What parity is '%c'?\n", parity);
  552. status = 1;
  553. }
  554. stopbits = *++modespec;
  555. if (strchr("12", stopbits) == NULL) {
  556. GPSD_LOG(LOG_ERROR, &context.errout,
  557. "Stop bits must be 1 or 2.\n");
  558. status = 1;
  559. }
  560. }
  561. if (status == 0)
  562. (void)gps_query(&gpsdata,
  563. DEVICE_SET, (int)timeout,
  564. "?DEVICE={\"path\":\"%s\",\"bps\":%s,"
  565. "\"parity\":\"%c\",\"stopbits\":%c}\r\n",
  566. device, speed, parity, stopbits);
  567. }
  568. if (atoi(speed) != (int)gpsdata.dev.baudrate) {
  569. GPSD_LOG(LOG_ERROR, &context.errout,
  570. "%s driver won't support %s%c%c\n",
  571. gpsdata.dev.path,
  572. speed, parity, stopbits);
  573. status = 1;
  574. } else
  575. GPSD_LOG(LOG_PROG, &context.errout,
  576. "%s change to %s%c%c succeeded\n",
  577. gpsdata.dev.path,
  578. speed, parity, stopbits);
  579. }
  580. if (rate != NULL) {
  581. (void)gps_query(&gpsdata,
  582. DEVICE_SET, (int)timeout,
  583. "?DEVICE={\"path\":\"%s\",\"cycle\":%s}\r\n",
  584. device, rate);
  585. }
  586. (void)gps_close(&gpsdata);
  587. exit(status);
  588. } else if (reset) {
  589. /* hard reset will go through lower-level operations */
  590. const int speeds[] = {4800, 9600, 19200, 38400, 57600, 115200, 230400,
  591. 460800};
  592. static struct gps_device_t session; /* zero this too */
  593. int i;
  594. if (device == NULL || forcetype == NULL) {
  595. GPSD_LOG(LOG_ERROR, &context.errout,
  596. "device and type must be specified for the "
  597. "reset operation.\n");
  598. exit(EXIT_FAILURE);
  599. }
  600. context.errout.debug = debuglevel;
  601. session.context = &context;
  602. gpsd_tty_init(&session);
  603. (void)strlcpy(session.gpsdata.dev.path, device,
  604. sizeof(session.gpsdata.dev.path));
  605. session.device_type = forcetype;
  606. (void)gpsd_open(&session);
  607. (void)gpsd_set_raw(&session);
  608. (void)session.device_type->speed_switcher(&session, 4800, 'N', 1);
  609. (void)tcdrain(session.gpsdata.gps_fd);
  610. for(i = 0; i < (int)(sizeof(speeds) / sizeof(speeds[0])); i++) {
  611. (void)gpsd_set_speed(&session, speeds[i], 'N', 1);
  612. (void)session.device_type->speed_switcher(&session, 4800, 'N', 1);
  613. (void)tcdrain(session.gpsdata.gps_fd);
  614. }
  615. gpsd_set_speed(&session, 4800, 'N', 1);
  616. for (i = 0; i < 3; i++)
  617. if (session.device_type->mode_switcher)
  618. session.device_type->mode_switcher(&session, MODE_NMEA);
  619. gpsd_wrap(&session);
  620. exit(EXIT_SUCCESS);
  621. } else {
  622. /* access to the daemon failed, use the low-level facilities */
  623. static struct gps_device_t session; /* zero this too */
  624. fd_set all_fds;
  625. fd_set rfds;
  626. /*
  627. * Unless the user explicitly requested it, always run to end of
  628. * hunt rather than timing out. Otherwise we can easily get messages
  629. * that spuriously look like failure at high baud rates.
  630. */
  631. gps_context_init(&context, "gpsctl");
  632. context.errout.debug = debuglevel;
  633. session.context = &context; /* in case gps_init isn't called */
  634. if (echo)
  635. context.readonly = true;
  636. if (timeout > 0) {
  637. (void) alarm(timeout);
  638. (void) signal(SIGALRM, onsig);
  639. }
  640. /*
  641. * Unless the user has forced a type and only wants to see the
  642. * string (not send it) we now need to try to open the device
  643. * and find out what is actually there.
  644. */
  645. if (!(forcetype != NULL && echo)) {
  646. socket_t maxfd = 0;
  647. int activated = -1;
  648. if (device == NULL) {
  649. GPSD_LOG(LOG_ERROR, &context.errout,
  650. "device must be specified for low-level access.\n");
  651. exit(EXIT_FAILURE);
  652. }
  653. gpsd_init(&session, &context, device);
  654. activated = gpsd_activate(&session, O_PROBEONLY);
  655. if ( 0 > activated ) {
  656. if ( PLACEHOLDING_FD == activated ) {
  657. (void)printf("%s identified as a %s.\n",
  658. device, gpsd_id(&session));
  659. exit(EXIT_SUCCESS);
  660. }
  661. GPSD_LOG(LOG_ERROR, &context.errout,
  662. "initial GPS device %s open failed\n",
  663. device);
  664. exit(EXIT_FAILURE);
  665. }
  666. GPSD_LOG(LOG_INF, &context.errout,
  667. "device %s activated\n", session.gpsdata.dev.path);
  668. FD_SET(session.gpsdata.gps_fd, &all_fds);
  669. if (session.gpsdata.gps_fd > maxfd)
  670. maxfd = session.gpsdata.gps_fd;
  671. /* initialize the GPS context's time fields */
  672. gpsd_time_init(&context, time(NULL));
  673. /* grab packets until we time out, get sync, or fail sync */
  674. for (hunting = true; hunting; )
  675. {
  676. fd_set efds;
  677. switch(gpsd_await_data(&rfds, &efds, maxfd, &all_fds,
  678. &context.errout))
  679. {
  680. case AWAIT_GOT_INPUT:
  681. break;
  682. case AWAIT_NOT_READY:
  683. /* no recovery from bad fd is possible */
  684. if (FD_ISSET(session.gpsdata.gps_fd, &efds))
  685. exit(EXIT_FAILURE);
  686. continue;
  687. case AWAIT_FAILED:
  688. exit(EXIT_FAILURE);
  689. }
  690. switch(gpsd_multipoll(FD_ISSET(session.gpsdata.gps_fd, &rfds),
  691. &session, ctlhook, 0))
  692. {
  693. case DEVICE_READY:
  694. FD_SET(session.gpsdata.gps_fd, &all_fds);
  695. break;
  696. case DEVICE_UNREADY:
  697. FD_CLR(session.gpsdata.gps_fd, &all_fds);
  698. break;
  699. case DEVICE_ERROR:
  700. /* this is where a failure to sync lands */
  701. GPSD_LOG(LOG_WARN, &context.errout,
  702. "device error, bailing out.\n");
  703. exit(EXIT_FAILURE);
  704. case DEVICE_EOF:
  705. GPSD_LOG(LOG_WARN, &context.errout,
  706. "device signed off, bailing out.\n");
  707. exit(EXIT_SUCCESS);
  708. default:
  709. break;
  710. }
  711. }
  712. GPSD_LOG(LOG_PROG, &context.errout,
  713. "%s looks like a %s at %d.\n",
  714. device, gpsd_id(&session),
  715. session.gpsdata.dev.baudrate);
  716. if (forcetype!=NULL &&
  717. strcmp("NMEA0183", session.device_type->type_name) != 0 &&
  718. strcmp(forcetype->type_name,
  719. session.device_type->type_name) != 0) {
  720. GPSD_LOG(LOG_ERROR, &context.errout,
  721. "'%s' doesn't match non-generic type '%s' "
  722. "of selected device.\n",
  723. forcetype->type_name,
  724. session.device_type->type_name);
  725. }
  726. }
  727. if(!control_stdout)
  728. (void)printf("%s identified as a %s at %u baud.\n",
  729. device, gpsd_id(&session),
  730. session.gpsdata.dev.baudrate);
  731. /* if no control operation was specified, we're done */
  732. if (speed==NULL && !to_nmea && !to_binary && control==NULL)
  733. exit(EXIT_SUCCESS);
  734. /* maybe user wants to see the packet rather than send it */
  735. if (echo)
  736. session.gpsdata.gps_fd = fileno(stdout);
  737. /* control op specified; maybe we forced the type */
  738. if (forcetype != NULL)
  739. (void)gpsd_switch_driver(&session, forcetype->type_name);
  740. /* now perform the actual control function */
  741. status = 0;
  742. if (to_nmea || to_binary) {
  743. bool write_enable = context.readonly;
  744. context.readonly = false;
  745. if (session.device_type->mode_switcher == NULL) {
  746. GPSD_LOG(LOG_SHOUT, &context.errout,
  747. "%s devices have no mode switch.\n",
  748. session.device_type->type_name);
  749. status = 1;
  750. } else {
  751. int target_mode = to_nmea ? MODE_NMEA : MODE_BINARY;
  752. GPSD_LOG(LOG_SHOUT, &context.errout,
  753. "switching to mode %s.\n",
  754. to_nmea ? "NMEA" : "BINARY");
  755. session.device_type->mode_switcher(&session, target_mode);
  756. settle(&session);
  757. }
  758. context.readonly = write_enable;
  759. }
  760. if (speed) {
  761. char parity = echo ? 'N': session.gpsdata.dev.parity;
  762. int stopbits = echo ? 1 : session.gpsdata.dev.stopbits;
  763. char *modespec;
  764. modespec = strchr(speed, ':');
  765. status = 0;
  766. if (modespec!=NULL) {
  767. *modespec = '\0';
  768. if (strchr("78", *++modespec) == NULL) {
  769. GPSD_LOG(LOG_ERROR, &context.errout,
  770. "No support for that word lengths.\n");
  771. status = 1;
  772. }
  773. parity = *++modespec;
  774. if (strchr("NOE", parity) == NULL) {
  775. GPSD_LOG(LOG_ERROR, &context.errout,
  776. "What parity is '%c'?\n", parity);
  777. status = 1;
  778. }
  779. stopbits = *++modespec;
  780. if (strchr("12", parity) == NULL) {
  781. GPSD_LOG(LOG_ERROR, &context.errout,
  782. "Stop bits must be 1 or 2.\n");
  783. status = 1;
  784. }
  785. stopbits = (int)(stopbits-'0');
  786. }
  787. if (status == 0) {
  788. if (session.device_type->speed_switcher == NULL) {
  789. GPSD_LOG(LOG_ERROR, &context.errout,
  790. "%s devices have no speed switch.\n",
  791. session.device_type->type_name);
  792. status = 1;
  793. }
  794. else if (session.device_type->speed_switcher(&session,
  795. (speed_t)atoi(speed),
  796. parity,
  797. stopbits)) {
  798. settle(&session);
  799. GPSD_LOG(LOG_PROG, &context.errout,
  800. "%s change to %s%c%d succeeded\n",
  801. session.gpsdata.dev.path,
  802. speed, parity, stopbits);
  803. } else {
  804. GPSD_LOG(LOG_ERROR, &context.errout,
  805. "%s driver won't support %s%c%d.\n",
  806. session.gpsdata.dev.path,
  807. speed, parity, stopbits);
  808. status = 1;
  809. }
  810. }
  811. }
  812. if (rate) {
  813. bool write_enable = context.readonly;
  814. context.readonly = false;
  815. if (session.device_type->rate_switcher == NULL) {
  816. GPSD_LOG(LOG_ERROR, &context.errout,
  817. "%s devices have no rate switcher.\n",
  818. session.device_type->type_name);
  819. status = 1;
  820. } else {
  821. double rate_dbl = strtod(rate, NULL);
  822. if (!session.device_type->rate_switcher(&session, rate_dbl)) {
  823. GPSD_LOG(LOG_ERROR, &context.errout,
  824. "rate switch failed.\n");
  825. status = 1;
  826. }
  827. settle(&session);
  828. }
  829. context.readonly = write_enable;
  830. }
  831. if (control) {
  832. bool write_enable = context.readonly;
  833. context.readonly = false;
  834. if (session.device_type->control_send == NULL) {
  835. GPSD_LOG(LOG_ERROR, &context.errout,
  836. "%s devices have no control sender.\n",
  837. session.device_type->type_name);
  838. status = 1;
  839. } else {
  840. if (session.device_type->control_send(&session,
  841. cooked,
  842. (size_t)cooklen) == -1) {
  843. GPSD_LOG(LOG_ERROR, &context.errout,
  844. "control transmission failed.\n");
  845. status = 1;
  846. }
  847. settle(&session);
  848. }
  849. context.readonly = write_enable;
  850. }
  851. exit(status);
  852. }
  853. }
  854. /* end */
  855. // vim: set expandtab shiftwidth=4