gpsctl.c 31 KB

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