Instrument Neutral Distributed Interface INDI  1.0.0
indicom.c
1 /*
2  INDI LIB
3  Common routines used by all drivers
4  Copyright (C) 2003 by Jason Harris (jharris@30doradus.org)
5  Elwood C. Downey
6 
7  This is the C version of the astronomical library in KStars
8  modified by Jasem Mutlaq (mutlaqja@ikarustech.com)
9 
10  This library is free software; you can redistribute it and/or
11  modify it under the terms of the GNU Lesser General Public
12  License as published by the Free Software Foundation; either
13  version 2.1 of the License, or (at your option) any later version.
14 
15  This library is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public
21  License along with this library; if not, write to the Free Software
22  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 
24 */
25 
26 #include <stdlib.h>
27 #include <math.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <stdarg.h>
34 #include <sys/param.h>
35 
36 #if defined(BSD) && !defined(__GNU__)
37 #include <IOKit/serial/ioss.h>
38 #include <sys/ioctl.h>
39 #endif
40 
41 #include <config.h>
42 
43 #ifdef HAVE_NOVA_H
44 #include <libnova.h>
45 #endif
46 
47 #include "indicom.h"
48 #ifdef _WIN32
49 #undef CX
50 #undef CY
51 #endif
52 
53 #ifndef _WIN32
54 #include <termios.h>
55 #define PARITY_NONE 0
56 #define PARITY_EVEN 1
57 #define PARITY_ODD 2
58 #endif
59 
60 #define MAXRBUF 2048
61 
62 #include "indidevapi.h"
63 
64 int tty_debug = 0;
65 
66 void getSexComponents(double value, int *d, int *m, int *s);
67 
68 int extractISOTime(char *timestr, struct ln_date *iso_date)
69 {
70  #ifdef HAVE_NOVA_H
71  struct tm utm;
72 
73  if (strptime(timestr, "%Y/%m/%dT%H:%M:%S", &utm))
74  {
75  ln_get_date_from_tm(&utm, iso_date);
76  return (0);
77  }
78 
79  if (strptime(timestr, "%Y-%m-%dT%H:%M:%S", &utm))
80  {
81  ln_get_date_from_tm(&utm, iso_date);
82  return (0);
83  }
84  #endif
85 
86  return (-1);
87 }
88 
89 
90 /* sprint the variable a in sexagesimal format into out[].
91  * w is the number of spaces for the whole part.
92  * fracbase is the number of pieces a whole is to broken into; valid options:
93  * 360000: <w>:mm:ss.ss
94  * 36000: <w>:mm:ss.s
95  * 3600: <w>:mm:ss
96  * 600: <w>:mm.m
97  * 60: <w>:mm
98  * return number of characters written to out, not counting finaif (NOVA_FOUND)
99  include_directories(${NOVA_INCLUDE_DIR})
100 endif (NOVA_FOUND)l '\0'.
101  */
102 int
103 fs_sexa (char *out, double a, int w, int fracbase)
104 {
105  char *out0 = out;
106  unsigned long n;
107  int d;
108  int f;
109  int m;
110  int s;
111  int isneg;
112 
113  /* save whether it's negative but do all the rest with a positive */
114  isneg = (a < 0);
115  if (isneg)
116  a = -a;
117 
118  /* convert to an integral number of whole portions */
119  n = (unsigned long)(a * fracbase + 0.5);
120  d = n/fracbase;
121  f = n%fracbase;
122 
123  /* form the whole part; "negative 0" is a special case */
124  if (isneg && d == 0)
125  out += sprintf (out, "%*s-0", w-2, "");
126  else
127  out += sprintf (out, "%*d", w, isneg ? -d : d);
128 
129  /* do the rest */
130  switch (fracbase) {
131  case 60: /* dd:mm */
132  m = f/(fracbase/60);
133  out += sprintf (out, ":%02d", m);
134  break;
135  case 600: /* dd:mm.m */
136  out += sprintf (out, ":%02d.%1d", f/10, f%10);
137  break;
138  case 3600: /* dd:mm:ss */
139  m = f/(fracbase/60);
140  s = f%(fracbase/60);
141  out += sprintf (out, ":%02d:%02d", m, s);
142  break;
143  case 36000: /* dd:mm:ss.s*/
144  m = f/(fracbase/60);
145  s = f%(fracbase/60);
146  out += sprintf (out, ":%02d:%02d.%1d", m, s/10, s%10);
147  break;
148  case 360000: /* dd:mm:ss.ss */
149  m = f/(fracbase/60);
150  s = f%(fracbase/60);
151  out += sprintf (out, ":%02d:%02d.%02d", m, s/100, s%100);
152  break;
153  default:
154  printf ("fs_sexa: unknown fracbase: %d\n", fracbase);
155  return -1;
156  }
157 
158  return (out - out0);
159 }
160 
161 /* convert sexagesimal string str AxBxC to double.
162  * x can be anything non-numeric. Any missing A, B or C will be assumed 0.
163  * optional - and + can be anywhere.
164  * return 0 if ok, -1 if can't find a thing.
165  */
166 int
168 const char *str0, /* input string */
169 double *dp) /* cracked value, if return 0 */
170 {
171  double a = 0, b = 0, c = 0;
172  char str[128];
173  char *neg;
174  int r;
175 
176  /* copy str0 so we can play with it */
177  strncpy (str, str0, sizeof(str)-1);
178  str[sizeof(str)-1] = '\0';
179 
180  neg = strchr(str, '-');
181  if (neg)
182  *neg = ' ';
183  r = sscanf (str, "%lf%*[^0-9]%lf%*[^0-9]%lf", &a, &b, &c);
184  if (r < 1)
185  return (-1);
186  *dp = a + b/60 + c/3600;
187  if (neg)
188  *dp *= -1;
189  return (0);
190 }
191 
192 void getSexComponents(double value, int *d, int *m, int *s)
193 {
194 
195  *d = (int) fabs(value);
196  *m = (int) ((fabs(value) - *d) * 60.0);
197  *s = (int) rint(((fabs(value) - *d) * 60.0 - *m) *60.0);
198 
199  if (value < 0)
200  *d *= -1;
201 }
202 
203 /* fill buf with properly formatted INumber string. return length */
204 int
205 numberFormat (char *buf, const char *format, double value)
206 {
207  int w, f, s;
208  char m;
209 
210  if (sscanf (format, "%%%d.%d%c", &w, &f, &m) == 3 && m == 'm') {
211  /* INDI sexi format */
212  switch (f) {
213  case 9: s = 360000; break;
214  case 8: s = 36000; break;
215  case 6: s = 3600; break;
216  case 5: s = 600; break;
217  default: s = 60; break;
218  }
219  return (fs_sexa (buf, value, w-f, s));
220  } else {
221  /* normal printf format */
222  return (sprintf (buf, format, value));
223  }
224 }
225 
226 /* log message locally.
227  * this has nothing to do with XML or any Clients.
228  */
229 void
230 IDLog (const char *fmt, ...)
231 {
232  va_list ap;
233  /* JM: Since all INDI's stderr are timestampped now, we don't need to time stamp ID Log */
234  /*fprintf (stderr, "%s ", timestamp());*/
235  va_start (ap, fmt);
236  vfprintf (stderr, fmt, ap);
237  va_end (ap);
238 }
239 
240 /* return current system time in message format */
241 const char *
243 {
244  static char ts[32];
245  struct tm *tp;
246  time_t t;
247 
248  time (&t);
249  tp = gmtime (&t);
250  strftime (ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", tp);
251  return (ts);
252 }
253 
254 void tty_set_debug(int debug)
255 {
256  tty_debug = debug;
257 }
258 
259 int tty_timeout(int fd, int timeout)
260 {
261  if (fd == -1)
262  return TTY_ERRNO;
263 
264  struct timeval tv;
265  fd_set readout;
266  int retval;
267 
268  FD_ZERO(&readout);
269  FD_SET(fd, &readout);
270 
271  /* wait for 'timeout' seconds */
272  tv.tv_sec = timeout;
273  tv.tv_usec = 0;
274 
275  /* Wait till we have a change in the fd status */
276  retval = select (fd+1, &readout, NULL, NULL, &tv);
277 
278  /* Return 0 on successful fd change */
279  if (retval > 0)
280  return TTY_OK;
281  /* Return -1 due to an error */
282  else if (retval == -1)
283  return TTY_SELECT_ERROR;
284  /* Return -2 if time expires before anything interesting happens */
285  else
286  return TTY_TIME_OUT;
287 
288 }
289 
290 int tty_write(int fd, const char * buf, int nbytes, int *nbytes_written)
291 {
292  if (fd == -1)
293  return TTY_ERRNO;
294 
295  int bytes_w = 0;
296  *nbytes_written = 0;
297 
298  while (nbytes > 0)
299  {
300 
301  bytes_w = write(fd, buf, nbytes);
302 
303  if (bytes_w < 0)
304  return TTY_WRITE_ERROR;
305 
306  *nbytes_written += bytes_w;
307  buf += bytes_w;
308  nbytes -= bytes_w;
309  }
310 
311  return TTY_OK;
312 }
313 
314 int tty_write_string(int fd, const char * buf, int *nbytes_written)
315 {
316  if (fd == -1)
317  return TTY_ERRNO;
318 
319  unsigned int nbytes;
320  int bytes_w = 0;
321  *nbytes_written = 0;
322 
323  nbytes = strlen(buf);
324 
325  while (nbytes > 0)
326  {
327 
328  bytes_w = write(fd, buf, nbytes);
329 
330  if (bytes_w < 0)
331  return TTY_WRITE_ERROR;
332 
333  *nbytes_written += bytes_w;
334  buf += bytes_w;
335  nbytes -= bytes_w;
336  }
337 
338  return TTY_OK;
339 }
340 
341 int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
342 {
343  if (fd == -1)
344  return TTY_ERRNO;
345 
346  int bytesRead = 0;
347  int err = 0;
348  *nbytes_read =0;
349 
350  if (nbytes <=0)
351  return TTY_PARAM_ERROR;
352 
353  if (tty_debug)
354  IDLog("%s: Request to read %d bytes with %d timeout for fd %d\n", __FUNCTION__, nbytes, timeout, fd);
355 
356  while (nbytes > 0)
357  {
358  if ( (err = tty_timeout(fd, timeout)) )
359  return err;
360 
361  bytesRead = read(fd, buf, ((unsigned) nbytes));
362 
363  if (bytesRead < 0 )
364  return TTY_READ_ERROR;
365 
366  buf += bytesRead;
367  *nbytes_read += bytesRead;
368  nbytes -= bytesRead;
369 
370  if (tty_debug)
371  {
372  int i=0;
373  for (i=0; i < (nbytes+*nbytes_read); i++)
374  IDLog("%s: buffer[%d]=%c\n", __FUNCTION__, i, buf[i]);
375  }
376  }
377 
378  return TTY_OK;
379 }
380 
381 int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
382 {
383  if (fd == -1)
384  return TTY_ERRNO;
385 
386  int bytesRead = 0;
387  int err = TTY_OK;
388  *nbytes_read = 0;
389 
390  if (tty_debug)
391  IDLog("%s: Request to read until stop char '%c' with %d timeout for fd %d\n", __FUNCTION__, stop_char, timeout, fd);
392 
393  for (;;)
394  {
395  if ( (err = tty_timeout(fd, timeout)) )
396  return err;
397 
398  bytesRead = read(fd, buf, 1);
399 
400  if (bytesRead < 0 )
401  return TTY_READ_ERROR;
402 
403  if (tty_debug)
404  IDLog("%s: buffer[%d]=%c\n", __FUNCTION__, (*nbytes_read), buf[(*nbytes_read)]);
405 
406  if (bytesRead)
407  (*nbytes_read)++;
408 
409  if (*buf == stop_char)
410  return TTY_OK;
411 
412  buf += bytesRead;
413 
414  }
415 
416  return TTY_TIME_OUT;
417 }
418 
419 #if defined(BSD) && !defined(__GNU__)
420 // BSD - OSX version
421 int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
422 {
423  int t_fd = -1;
424  int bps;
425  char msg[80];
426  int handshake;
427  struct termios tty_setting;
428 
429  // Open the serial port read/write, with no controlling terminal, and don't wait for a connection.
430  // The O_NONBLOCK flag also causes subsequent I/O on the device to be non-blocking.
431  // See open(2) ("man 2 open") for details.
432 
433  t_fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
434  if (t_fd == -1)
435  {
436  printf("Error opening serial port %s - %s(%d).\n",
437  device, strerror(errno), errno);
438  goto error;
439  }
440 
441  // Note that open() follows POSIX semantics: multiple open() calls to the same file will succeed
442  // unless the TIOCEXCL ioctl is issued. This will prevent additional opens except by root-owned
443  // processes.
444  // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
445 
446  if (ioctl(t_fd, TIOCEXCL) == -1)
447  {
448  printf("Error setting TIOCEXCL on %s - %s(%d).\n",
449  device, strerror(errno), errno);
450  goto error;
451  }
452 
453  // Now that the device is open, clear the O_NONBLOCK flag so subsequent I/O will block.
454  // See fcntl(2) ("man 2 fcntl") for details.
455 
456  if (fcntl(t_fd, F_SETFL, 0) == -1)
457  {
458  printf("Error clearing O_NONBLOCK %s - %s(%d).\n",
459  device, strerror(errno), errno);
460  goto error;
461  }
462 
463  // Get the current options and save them so we can restore the default settings later.
464  if (tcgetattr(t_fd, &tty_setting) == -1)
465  {
466  printf("Error getting tty attributes %s - %s(%d).\n",
467  device, strerror(errno), errno);
468  goto error;
469  }
470 
471  // Set raw input (non-canonical) mode, with reads blocking until either a single character
472  // has been received or a one second timeout expires.
473  // See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios") for details.
474 
475  cfmakeraw(&tty_setting);
476  tty_setting.c_cc[VMIN] = 1;
477  tty_setting.c_cc[VTIME] = 10;
478 
479  // The baud rate, word length, and handshake options can be set as follows:
480  switch (bit_rate) {
481  case 0:
482  bps = B0;
483  break;
484  case 50:
485  bps = B50;
486  break;
487  case 75:
488  bps = B75;
489  break;
490  case 110:
491  bps = B110;
492  break;
493  case 134:
494  bps = B134;
495  break;
496  case 150:
497  bps = B150;
498  break;
499  case 200:
500  bps = B200;
501  break;
502  case 300:
503  bps = B300;
504  break;
505  case 600:
506  bps = B600;
507  break;
508  case 1200:
509  bps = B1200;
510  break;
511  case 1800:
512  bps = B1800;
513  break;
514  case 2400:
515  bps = B2400;
516  break;
517  case 4800:
518  bps = B4800;
519  break;
520  case 9600:
521  bps = B9600;
522  break;
523  case 19200:
524  bps = B19200;
525  break;
526  case 38400:
527  bps = B38400;
528  break;
529  case 57600:
530  bps = B57600;
531  break;
532  case 115200:
533  bps = B115200;
534  break;
535  case 230400:
536  bps = B230400;
537  break;
538  default:
539  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid bit rate.", bit_rate) < 0)
540  perror(NULL);
541  else
542  perror(msg);
543  return TTY_PARAM_ERROR;
544  }
545 
546  cfsetspeed(&tty_setting, bps); // Set baud rate
547  /* word size */
548  switch (word_size) {
549  case 5:
550  tty_setting.c_cflag |= CS5;
551  break;
552  case 6:
553  tty_setting.c_cflag |= CS6;
554  break;
555  case 7:
556  tty_setting.c_cflag |= CS7;
557  break;
558  case 8:
559  tty_setting.c_cflag |= CS8;
560  break;
561  default:
562 
563  fprintf( stderr, "Default\n") ;
564  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid data bit count.", word_size) < 0)
565  perror(NULL);
566  else
567  perror(msg);
568 
569  return TTY_PARAM_ERROR;
570  }
571 
572  /* parity */
573  switch (parity) {
574  case PARITY_NONE:
575  break;
576  case PARITY_EVEN:
577  tty_setting.c_cflag |= PARENB;
578  break;
579  case PARITY_ODD:
580  tty_setting.c_cflag |= PARENB | PARODD;
581  break;
582  default:
583 
584  fprintf( stderr, "Default1\n") ;
585  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid parity selection value.", parity) < 0)
586  perror(NULL);
587  else
588  perror(msg);
589 
590  return TTY_PARAM_ERROR;
591  }
592 
593  /* stop_bits */
594  switch (stop_bits) {
595  case 1:
596  break;
597  case 2:
598  tty_setting.c_cflag |= CSTOPB;
599  break;
600  default:
601  fprintf( stderr, "Default2\n") ;
602  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid stop bit count.", stop_bits) < 0)
603  perror(NULL);
604  else
605  perror(msg);
606 
607  return TTY_PARAM_ERROR;
608  }
609 
610 #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
611  // Starting with Tiger, the IOSSIOSPEED ioctl can be used to set arbitrary baud rates
612  // other than those specified by POSIX. The driver for the underlying serial hardware
613  // ultimately determines which baud rates can be used. This ioctl sets both the input
614  // and output speed.
615 
616  speed_t speed = 14400; // Set 14400 baud
617  if (ioctl(t_fd, IOSSIOSPEED, &speed) == -1)
618  {
619  printf("Error calling ioctl(..., IOSSIOSPEED, ...) - %s(%d).\n",
620  strerror(errno), errno);
621  }
622 #endif
623 
624  // Cause the new options to take effect immediately.
625  if (tcsetattr(t_fd, TCSANOW, &tty_setting) == -1)
626  {
627  printf("Error setting tty attributes %s - %s(%d).\n",
628  device, strerror(errno), errno);
629  goto error;
630  }
631 
632  // To set the modem handshake lines, use the following ioctls.
633  // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
634 
635  if (ioctl(t_fd, TIOCSDTR) == -1) // Assert Data Terminal Ready (DTR)
636  {
637  printf("Error asserting DTR %s - %s(%d).\n",
638  device, strerror(errno), errno);
639  }
640 
641  if (ioctl(t_fd, TIOCCDTR) == -1) // Clear Data Terminal Ready (DTR)
642  {
643  printf("Error clearing DTR %s - %s(%d).\n",
644  device, strerror(errno), errno);
645  }
646 
647  handshake = TIOCM_DTR | TIOCM_RTS | TIOCM_CTS | TIOCM_DSR;
648  if (ioctl(t_fd, TIOCMSET, &handshake) == -1)
649  // Set the modem lines depending on the bits set in handshake
650  {
651  printf("Error setting handshake lines %s - %s(%d).\n",
652  device, strerror(errno), errno);
653  }
654 
655  // To read the state of the modem lines, use the following ioctl.
656  // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
657 
658  if (ioctl(t_fd, TIOCMGET, &handshake) == -1)
659  // Store the state of the modem lines in handshake
660  {
661  printf("Error getting handshake lines %s - %s(%d).\n",
662  device, strerror(errno), errno);
663  }
664 
665  printf("Handshake lines currently set to %d\n", handshake);
666 
667 #if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3)
668  unsigned long mics = 1UL;
669 
670  // Set the receive latency in microseconds. Serial drivers use this value to determine how often to
671  // dequeue characters received by the hardware. Most applications don't need to set this value: if an
672  // app reads lines of characters, the app can't do anything until the line termination character has been
673  // received anyway. The most common applications which are sensitive to read latency are MIDI and IrDA
674  // applications.
675 
676  if (ioctl(t_fd, IOSSDATALAT, &mics) == -1)
677  {
678  // set latency to 1 microsecond
679  printf("Error setting read latency %s - %s(%d).\n",
680  device, strerror(errno), errno);
681  goto error;
682  }
683 #endif
684 
685  *fd = t_fd;
686  /* return success */
687  return TTY_OK;
688 
689  // Failure path
690 error:
691  if (t_fd != -1)
692  {
693  close(t_fd);
694  *fd = -1;
695  }
696 
697  return TTY_PORT_FAILURE;
698 }
699 #else
700 int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
701 {
702 #ifdef _WIN32
703  return TTY_PORT_FAILURE;
704 
705 #else
706  int t_fd=-1;
707  char msg[80];
708  int bps;
709  struct termios tty_setting;
710 
711  if ( (t_fd = open(device, O_RDWR | O_NOCTTY )) == -1)
712  {
713  *fd = -1;
714 
715  return TTY_PORT_FAILURE;
716  }
717 
718  /* Control Modes
719  Set bps rate */
720  switch (bit_rate) {
721  case 0:
722  bps = B0;
723  break;
724  case 50:
725  bps = B50;
726  break;
727  case 75:
728  bps = B75;
729  break;
730  case 110:
731  bps = B110;
732  break;
733  case 134:
734  bps = B134;
735  break;
736  case 150:
737  bps = B150;
738  break;
739  case 200:
740  bps = B200;
741  break;
742  case 300:
743  bps = B300;
744  break;
745  case 600:
746  bps = B600;
747  break;
748  case 1200:
749  bps = B1200;
750  break;
751  case 1800:
752  bps = B1800;
753  break;
754  case 2400:
755  bps = B2400;
756  break;
757  case 4800:
758  bps = B4800;
759  break;
760  case 9600:
761  bps = B9600;
762  break;
763  case 19200:
764  bps = B19200;
765  break;
766  case 38400:
767  bps = B38400;
768  break;
769  case 57600:
770  bps = B57600;
771  break;
772  case 115200:
773  bps = B115200;
774  break;
775  case 230400:
776  bps = B230400;
777  break;
778  default:
779  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid bit rate.", bit_rate) < 0)
780  perror(NULL);
781  else
782  perror(msg);
783  return TTY_PARAM_ERROR;
784  }
785  if ((cfsetispeed(&tty_setting, bps) < 0) ||
786  (cfsetospeed(&tty_setting, bps) < 0))
787  {
788  perror("tty_connect: failed setting bit rate.");
789  return TTY_PORT_FAILURE;
790  }
791 
792  /* Control Modes
793  set no flow control word size, parity and stop bits.
794  Also don't hangup automatically and ignore modem status.
795  Finally enable receiving characters. */
796  tty_setting.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | HUPCL | CRTSCTS);
797  tty_setting.c_cflag |= (CLOCAL | CREAD);
798 
799  /* word size */
800  switch (word_size) {
801  case 5:
802  tty_setting.c_cflag |= CS5;
803  break;
804  case 6:
805  tty_setting.c_cflag |= CS6;
806  break;
807  case 7:
808  tty_setting.c_cflag |= CS7;
809  break;
810  case 8:
811  tty_setting.c_cflag |= CS8;
812  break;
813  default:
814 
815  fprintf( stderr, "Default\n") ;
816  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid data bit count.", word_size) < 0)
817  perror(NULL);
818  else
819  perror(msg);
820 
821  return TTY_PARAM_ERROR;
822  }
823 
824  /* parity */
825  switch (parity) {
826  case PARITY_NONE:
827  break;
828  case PARITY_EVEN:
829  tty_setting.c_cflag |= PARENB;
830  break;
831  case PARITY_ODD:
832  tty_setting.c_cflag |= PARENB | PARODD;
833  break;
834  default:
835 
836  fprintf( stderr, "Default1\n") ;
837  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid parity selection value.", parity) < 0)
838  perror(NULL);
839  else
840  perror(msg);
841 
842  return TTY_PARAM_ERROR;
843  }
844 
845  /* stop_bits */
846  switch (stop_bits) {
847  case 1:
848  break;
849  case 2:
850  tty_setting.c_cflag |= CSTOPB;
851  break;
852  default:
853  fprintf( stderr, "Default2\n") ;
854  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid stop bit count.", stop_bits) < 0)
855  perror(NULL);
856  else
857  perror(msg);
858 
859  return TTY_PARAM_ERROR;
860  }
861  /* Control Modes complete */
862 
863  /* Ignore bytes with parity errors and make terminal raw and dumb.*/
864  tty_setting.c_iflag &= ~(PARMRK | ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON | IXANY);
865  tty_setting.c_iflag |= INPCK | IGNPAR | IGNBRK;
866 
867  /* Raw output.*/
868  tty_setting.c_oflag &= ~(OPOST | ONLCR);
869 
870  /* Local Modes
871  Don't echo characters. Don't generate signals.
872  Don't process any characters.*/
873  tty_setting.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | IEXTEN | NOFLSH | TOSTOP);
874  tty_setting.c_lflag |= NOFLSH;
875 
876  /* blocking read until 1 char arrives */
877  tty_setting.c_cc[VMIN] = 1;
878  tty_setting.c_cc[VTIME] = 0;
879 
880  /* now clear input and output buffers and activate the new terminal settings */
881  tcflush(t_fd, TCIOFLUSH);
882  if (tcsetattr(t_fd, TCSANOW, &tty_setting))
883  {
884  perror("tty_connect: failed setting attributes on serial port.");
885  tty_disconnect(t_fd);
886  return TTY_PORT_FAILURE;
887  }
888 
889  *fd = t_fd;
890  /* return success */
891  return TTY_OK;
892 #endif
893 }
894 // Unix - Linux version
895 
896 #endif
897 
898 
899 int tty_disconnect(int fd)
900 {
901  if (fd == -1)
902  return TTY_ERRNO;
903 
904 #ifdef _WIN32
905  return TTY_ERRNO;
906 #else
907  int err;
908  tcflush(fd, TCIOFLUSH);
909  err = close(fd);
910 
911  if (err != 0)
912  return TTY_ERRNO;
913 
914  return TTY_OK;
915 #endif
916 }
917 
918 void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
919 {
920  char error_string[512];
921 
922  switch (err_code)
923  {
924  case TTY_OK:
925  strncpy(err_msg, "No Error", err_msg_len);
926  break;
927 
928  case TTY_READ_ERROR:
929  snprintf(error_string, 512, "Read Error: %s", strerror(errno));
930  strncpy(err_msg, error_string, err_msg_len);
931  break;
932 
933  case TTY_WRITE_ERROR:
934  snprintf(error_string, 512, "Write Error: %s", strerror(errno));
935  strncpy(err_msg, error_string, err_msg_len);
936  break;
937 
938  case TTY_SELECT_ERROR:
939  snprintf(error_string, 512, "Select Error: %s", strerror(errno));
940  strncpy(err_msg, error_string, err_msg_len);
941  break;
942 
943  case TTY_TIME_OUT:
944  strncpy(err_msg, "Timeout error", err_msg_len);
945  break;
946 
947  case TTY_PORT_FAILURE:
948  if (errno == EACCES)
949  snprintf(error_string, 512, "Port failure Error: %s. Try adding your user to the dialout group and restart (sudo adduser $USER dialout)", strerror(errno));
950  else
951  snprintf(error_string, 512, "Port failure Error: %s. Check if device is connected to this port.", strerror(errno));
952 
953  strncpy(err_msg, error_string, err_msg_len);
954  break;
955 
956  case TTY_PARAM_ERROR:
957  strncpy(err_msg, "Parameter error", err_msg_len);
958  break;
959 
960  case TTY_ERRNO:
961  snprintf(error_string, 512, "%s", strerror(errno));
962  strncpy(err_msg, error_string, err_msg_len);
963  break;
964 
965  default:
966  strncpy(err_msg, "Error: unrecognized error code", err_msg_len);
967  break;
968 
969 
970  }
971 }
972 
973 /* return static string corresponding to the given property or light state */
974 const char *
976 {
977  switch (s) {
978  case IPS_IDLE: return ("Idle");
979  case IPS_OK: return ("Ok");
980  case IPS_BUSY: return ("Busy");
981  case IPS_ALERT: return ("Alert");
982  default:
983  fprintf (stderr, "Impossible IPState %d\n", s);
984  return NULL;
985  }
986 }
987 
988 /* crack string into IPState.
989  * return 0 if ok, else -1
990  */
991 int
992 crackIPState (const char *str, IPState *ip)
993 {
994  if (!strcmp (str, "Idle")) *ip = IPS_IDLE;
995  else if (!strcmp (str, "Ok")) *ip = IPS_OK;
996  else if (!strcmp (str, "Busy")) *ip = IPS_BUSY;
997  else if (!strcmp (str, "Alert")) *ip = IPS_ALERT;
998  else return (-1);
999  return (0);
1000 }
1001 
1002 /* crack string into ISState.
1003  * return 0 if ok, else -1
1004  */
1005 int
1006 crackISState (const char *str, ISState *ip)
1007 {
1008  if (!strcmp (str, "On")) *ip = ISS_ON;
1009  else if (!strcmp (str, "Off")) *ip = ISS_OFF;
1010  else return (-1);
1011  return (0);
1012 }
1013 
1014 int
1015 crackIPerm (const char *str, IPerm *ip)
1016 {
1017  if (!strcmp (str, "rw")) *ip = IP_RW;
1018  else if (!strcmp (str, "ro")) *ip = IP_RO;
1019  else if (!strcmp (str, "wo")) *ip = IP_WO;
1020  else return (-1);
1021  return (0);
1022 }
1023 
1024 int crackISRule (const char *str, ISRule *ip)
1025 {
1026  if (!strcmp (str, "OneOfMany")) *ip = ISR_1OFMANY;
1027  else if (!strcmp (str, "AtMostOne")) *ip = ISR_ATMOST1;
1028  else if (!strcmp (str, "AnyOfMany")) *ip = ISR_NOFMANY;
1029  else return (-1);
1030  return (0);
1031 }
1032 
1033 /* return static string corresponding to the given switch state */
1034 const char *
1036 {
1037  switch (s) {
1038  case ISS_ON: return ("On");
1039  case ISS_OFF: return ("Off");
1040  default:
1041  fprintf (stderr, "Impossible ISState %d\n", s);
1042  return NULL;
1043  }
1044 }
1045 
1046 /* return static string corresponding to the given Rule */
1047 const char *
1049 {
1050  switch (r) {
1051  case ISR_1OFMANY: return ("OneOfMany");
1052  case ISR_ATMOST1: return ("AtMostOne");
1053  case ISR_NOFMANY: return ("AnyOfMany");
1054  default:
1055  fprintf (stderr, "Impossible ISRule %d\n", r);
1056  return NULL;
1057  }
1058 }
1059 
1060 /* return static string corresponding to the given IPerm */
1061 const char *
1063 {
1064  switch (p) {
1065  case IP_RO: return ("ro");
1066  case IP_WO: return ("wo");
1067  case IP_RW: return ("rw");
1068  default:
1069  fprintf (stderr, "Impossible IPerm %d\n", p);
1070  return NULL;
1071  }
1072 }
1073 
1074 /* print the boilerplate comment introducing xml */
1075 void
1076 xmlv1()
1077 {
1078  printf ("<?xml version='1.0'?>\n");
1079 }
1080 
1081 /* pull out device and name attributes from root.
1082  * return 0 if ok else -1 with reason in msg[].
1083  */
1084 int
1085 crackDN (XMLEle *root, char **dev, char **name, char msg[])
1086 {
1087  XMLAtt *ap;
1088 
1089  ap = findXMLAtt (root, "device");
1090  if (!ap) {
1091  sprintf (msg, "%s requires 'device' attribute", tagXMLEle(root));
1092  return (-1);
1093  }
1094  *dev = valuXMLAtt(ap);
1095 
1096  ap = findXMLAtt (root, "name");
1097  if (!ap) {
1098  sprintf (msg, "%s requires 'name' attribute", tagXMLEle(root));
1099  return (-1);
1100  }
1101  *name = valuXMLAtt(ap);
1102 
1103  return (0);
1104 }
1105 
1106 /* find a member of an IText vector, else NULL */
1107 IText *
1108 IUFindText (const ITextVectorProperty *tvp, const char *name)
1109 {
1110  int i;
1111 
1112  for (i = 0; i < tvp->ntp; i++)
1113  if (strcmp (tvp->tp[i].name, name) == 0)
1114  return (&tvp->tp[i]);
1115  fprintf (stderr, "No IText '%s' in %s.%s\n",name,tvp->device,tvp->name);
1116  return (NULL);
1117 }
1118 
1119 /* find a member of an INumber vector, else NULL */
1120 INumber *
1121 IUFindNumber(const INumberVectorProperty *nvp, const char *name)
1122 {
1123  int i;
1124 
1125  for (i = 0; i < nvp->nnp; i++)
1126  if (strcmp (nvp->np[i].name, name) == 0)
1127  return (&nvp->np[i]);
1128  fprintf(stderr,"No INumber '%s' in %s.%s\n",name,nvp->device,nvp->name);
1129  return (NULL);
1130 }
1131 
1132 /* find a member of an ISwitch vector, else NULL */
1133 ISwitch *
1134 IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
1135 {
1136  int i;
1137 
1138  for (i = 0; i < svp->nsp; i++)
1139  if (strcmp (svp->sp[i].name, name) == 0)
1140  return (&svp->sp[i]);
1141  fprintf(stderr,"No ISwitch '%s' in %s.%s\n",name,svp->device,svp->name);
1142  return (NULL);
1143 }
1144 
1145 /* find a member of an ILight vector, else NULL */
1146 ILight *
1147 IUFindLight(const ILightVectorProperty *lvp, const char *name)
1148 {
1149  int i;
1150 
1151  for (i = 0; i < lvp->nlp; i++)
1152  if (strcmp (lvp->lp[i].name, name) == 0)
1153  return (&lvp->lp[i]);
1154  fprintf(stderr,"No ILight '%s' in %s.%s\n",name,lvp->device,lvp->name);
1155  return (NULL);
1156 }
1157 
1158 /* find a member of an IBLOB vector, else NULL */
1159 IBLOB *
1160 IUFindBLOB(const IBLOBVectorProperty *bvp, const char *name)
1161 {
1162  int i;
1163 
1164  for (i = 0; i < bvp->nbp; i++)
1165  if (strcmp (bvp->bp[i].name, name) == 0)
1166  return (&bvp->bp[i]);
1167  fprintf(stderr,"No IBLOB '%s' in %s.%s\n",name,bvp->device,bvp->name);
1168  return (NULL);
1169 }
1170 
1171 /* find an ON member of an ISwitch vector, else NULL.
1172  * N.B. user must make sense of result with ISRule in mind.
1173  */
1174 ISwitch *
1176 {
1177  int i;
1178 
1179  for (i = 0; i < svp->nsp; i++)
1180  if (svp->sp[i].s == ISS_ON)
1181  return (&svp->sp[i]);
1182  /*fprintf(stderr, "No ISwitch On in %s.%s\n", svp->device, svp->name);*/
1183  return (NULL);
1184 }
1185 
1186 /* Find index of the ON member of an ISwitchVectorProperty */
1188 {
1189  int i;
1190 
1191  for (i = 0; i < svp->nsp; i++)
1192  if (svp->sp[i].s == ISS_ON)
1193  return i;
1194  return -1;
1195 }
1196 
1197 /* Set all switches to off */
1198 void
1200 {
1201  int i;
1202 
1203  for (i = 0; i < svp->nsp; i++)
1204  svp->sp[i].s = ISS_OFF;
1205 }
1206 
1207 /* save malloced copy of newtext in tp->text, reusing if not first time */
1208 void
1209 IUSaveText (IText *tp, const char *newtext)
1210 {
1211  /* seed for realloc */
1212  if (tp->text == NULL)
1213  tp->text = malloc (1);
1214 
1215  /* copy in fresh string */
1216  tp->text = strcpy (realloc (tp->text, strlen(newtext)+1), newtext);
1217 }
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indicom.c:1209
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indicom.c:1199
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double.
Definition: indicom.c:167
const char * pstateStr(IPState s)
Definition: indicom.c:975
Light vector property descriptor.
Definition: indiapi.h:332
char device[MAXINDIDEVICE]
Definition: indiapi.h:198
char * text
Definition: indiapi.h:184
int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
Establishes a tty connection to a terminal device.
Definition: indicom.c:700
ISState s
Definition: indiapi.h:283
ILight * IUFindLight(const ILightVectorProperty *lvp, const char *name)
Find an ILight member in a vector Light property.
Definition: indicom.c:1147
int crackISRule(const char *str, ISRule *ip)
Extract switch rule (OneOfMany, OnlyOne..etc) from the supplied string.
Definition: indicom.c:1024
IPState
Property state.
Definition: indiapi.h:115
One light descriptor.
Definition: indiapi.h:321
char device[MAXINDIDEVICE]
Definition: indiapi.h:381
One number descriptor.
Definition: indiapi.h:224
void tty_set_debug(int debug)
tty_set_debug Enable or disable debug which prints verbose information.
Definition: indicom.c:254
One Blob (Binary Large Object) descriptor.
Definition: indiapi.h:356
char device[MAXINDIDEVICE]
Definition: indiapi.h:334
int crackIPerm(const char *str, IPerm *ip)
Extract property permission state (RW, RO, WO) from the supplied string.
Definition: indicom.c:1015
Definition: indiapi.h:139
Definition: indiapi.h:138
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition: lilxml.c:415
Definition: indiapi.h:140
int tty_disconnect(int fd)
Closes a tty connection and flushes the bus.
Definition: indicom.c:899
Switch vector property descriptor.
Definition: indiapi.h:291
char name[MAXINDINAME]
Definition: indiapi.h:256
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:314
int numberFormat(char *buf, const char *format, double value)
Fill buffer with properly formatted INumber string.
Definition: indicom.c:205
const char * permStr(IPerm p)
Definition: indicom.c:1062
One switch descriptor.
Definition: indiapi.h:280
IText * IUFindText(const ITextVectorProperty *tvp, const char *name)
Find an IText member in a vector text property.
Definition: indicom.c:1108
int extractISOTime(char *timestr, struct ln_date *iso_date)
Extract ISO 8601 time and store it in a tm struct.
Definition: indicom.c:68
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string.
Definition: indicom.c:103
char name[MAXINDINAME]
Definition: indiapi.h:336
BLOB (Binary Large Object) vector property descriptor.
Definition: indiapi.h:379
const char * timestamp()
Create an ISO 8601 formatted time stamp. The format is YYYY-MM-DDTHH:MM:SS.
Definition: indicom.c:242
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:381
IBLOB * IUFindBLOB(const IBLOBVectorProperty *bvp, const char *name)
Find an IBLOB member in a vector BLOB property.
Definition: indicom.c:1160
int crackDN(XMLEle *root, char **dev, char **name, char msg[])
Extract dev and name attributes from an XML element.
Definition: indicom.c:1085
const char * ruleStr(ISRule r)
Definition: indicom.c:1048
int crackISState(const char *str, ISState *ip)
Extract switch state (On or Off) from the supplied string.
Definition: indicom.c:1006
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition: indicom.c:1121
char name[MAXINDINAME]
Definition: indiapi.h:358
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indicom.c:1187
Number vector property descriptor.
Definition: indiapi.h:252
int crackIPState(const char *str, IPState *ip)
Extract property state (Idle, OK, Busy, Alert) from the supplied string.
Definition: indicom.c:992
One text descriptor.
Definition: indiapi.h:178
char name[MAXINDINAME]
Definition: indiapi.h:295
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:290
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
Definition: lilxml.c:306
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:341
char name[MAXINDINAME]
Definition: indiapi.h:200
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:918
Interface to the reference INDI C API device implementation on the Device Driver side.
void IDLog(const char *fmt,...)
Function Drivers call to log a message locally.
Definition: indicom.c:230
IPerm
Permission hint, with respect to client.
Definition: indiapi.h:136
ISState
Switch state.
Definition: indiapi.h:106
char name[MAXINDINAME]
Definition: indiapi.h:180
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
Definition: indicom.c:1134
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
Definition: indicom.c:1175
const char * sstateStr(ISState s)
Definition: indicom.c:1035
Text vector property descriptor.
Definition: indiapi.h:196
char name[MAXINDINAME]
Definition: indiapi.h:383
char device[MAXINDIDEVICE]
Definition: indiapi.h:293
char * tagXMLEle(XMLEle *ep)
Return the tag of an XML element.
Definition: lilxml.c:387
ISRule
Switch vector rule hint.
Definition: indiapi.h:126
char device[MAXINDIDEVICE]
Definition: indiapi.h:254
Implementations for common driver routines.