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