vrpn  07.33
Virtual Reality Peripheral Network
vrpn_Serial.C
Go to the documentation of this file.
1 #include <fcntl.h> // for open, O_NDELAY, O_NOCTTY, etc
2 #include <stdio.h> // for perror, fprintf, NULL, etc
3 #include <string.h> // for memcpy
4 
5 #ifndef _WIN32
6 #include <errno.h> // for EINTR, errno
7 #include <sys/ioctl.h> // for ioctl, TIOCMGET, TIOCMSET, etc
8 #include <termios.h> // for termios, tcflush, CSIZE, etc
9 #endif
10 
11 #if !defined(_WIN32) || defined(__GNUC__) && !defined(__MINGW32__)
12 #include <unistd.h> // for close, read, write
13 #endif
14 
15 #ifdef _AIX
16 #include <sys/termios.h>
17 #endif
18 
19 #include "vrpn_Shared.h" // for timeval, vrpn_gettimeofday, and making it safe to include windows headers
20 
21 #if defined(_WIN32) && !defined(_WIN32_WCE)
22 #include <io.h>
23 #if defined(__GNUC__) && !defined(__MINGW32__)
24 #include <netinet/in.h>
25 #include <sys/socket.h>
26 #else
27 //#include <afxcoll.h>
28 #endif
29 #endif
30 
31 #include "vrpn_Serial.h"
32 
33 //#define VERBOSE
34 
35 #define time_add(t1, t2, tr) \
36  { \
37  (tr).tv_sec = (t1).tv_sec + (t2).tv_sec; \
38  (tr).tv_usec = (t1).tv_usec + (t2).tv_usec; \
39  while ((tr).tv_usec >= 1000000L) { \
40  (tr).tv_sec++; \
41  (tr).tv_usec -= 1000000L; \
42  } \
43  }
44 #define time_greater(t1, t2) \
45  ((t1.tv_sec > t2.tv_sec) || \
46  ((t1.tv_sec == t2.tv_sec) && (t1.tv_usec > t2.tv_usec)))
47 
48 #if defined(_WIN32) && !defined(__CYGWIN__)
49 const int maxCom = 50;
50 static HANDLE commConnections[maxCom];
51 static int curCom = -1;
52 #endif
53 
54 int vrpn_open_commport(const char *portname, long baud, int charsize,
55  vrpn_SER_PARITY parity, bool rts_flow)
56 {
57 #ifdef VERBOSE
58  printf("vrpn_open_commport(): Entering\n");
59 #endif
60 #if defined(hpux) || defined(__hpux) || defined(ultrix) || defined(FreeBSD) || \
61  defined(__CYGWIN__)
62  fprintf(
63  stderr,
64  "vrpn_open_commport(): Not yet implemented in this operating system\n");
65  return -1;
66 #else
67 
68 #if defined(_WIN32)
69  DCB dcb;
70  COMMTIMEOUTS cto;
71  HANDLE hCom;
72  BOOL fSuccess;
73 #else
74  int fileDescriptor;
75  struct termios sttyArgs;
76 #endif
77 
78 #if defined(_WIN32)
79  if (curCom + 1 >= maxCom) {
80  fprintf(stderr, "VRPN: To many communication connections open, edit "
81  "vrpn_Serial.C and recompile\n");
82  return -1;
83  }
84 
85  hCom = CreateFile(portname, GENERIC_READ | GENERIC_WRITE,
86  0, // comm devices must be opened w/exclusive-access
87  NULL, // no security attributes
88  OPEN_EXISTING, // comm devices must use OPEN_EXISTING
89  0, // not overlapped I/O
90  NULL); // hTemplate must be NULL for comm devices );
91 
92  if (hCom == INVALID_HANDLE_VALUE) {
93  perror("vrpn_open_commport: cannot open serial port");
94  fprintf(stderr, " (Make sure port name is valid and it has not been "
95  "opened by another program)\n");
96  return -1;
97  }
98 
99  if ((fSuccess = GetCommState(hCom, &dcb)) == 0) {
100  perror("vrpn_open_commport: cannot get serial port configuration "
101  "settings");
102  CloseHandle(hCom);
103  return -1;
104  }
105 
106  // Enable the hardware data-ready line (the TNG-3 uses this for power).
107  dcb.fDtrControl = DTR_CONTROL_ENABLE;
108 
109  // Set the baud rate
110  switch (baud) {
111  case 300:
112  dcb.BaudRate = CBR_300;
113  break;
114  case 1200:
115  dcb.BaudRate = CBR_1200;
116  break;
117  case 2400:
118  dcb.BaudRate = CBR_2400;
119  break;
120  case 4800:
121  dcb.BaudRate = CBR_4800;
122  break;
123  case 9600:
124  dcb.BaudRate = CBR_9600;
125  break;
126  case 19200:
127  dcb.BaudRate = CBR_19200;
128  break;
129  case 38400:
130  dcb.BaudRate = CBR_38400;
131  break;
132  case 57600:
133  dcb.BaudRate = CBR_57600;
134  break;
135  case 115200:
136  dcb.BaudRate = CBR_115200;
137  break;
138  default:
139  fprintf(stderr, "vrpn_open_commport: unknown baud rate %ld\n", baud);
140  CloseHandle(hCom);
141  return -1;
142  }
143 
144  switch (parity) {
146  dcb.fParity = FALSE;
147  dcb.Parity = NOPARITY;
148  break;
149  case vrpn_SER_PARITY_ODD:
150  dcb.fParity = TRUE;
151  dcb.Parity = 1;
152  break;
154  dcb.fParity = TRUE;
155  dcb.Parity = 2;
156  break;
158  dcb.fParity = TRUE;
159  dcb.Parity = 3;
160  break;
162  dcb.fParity = TRUE;
163  dcb.Parity = 3;
164  break;
165  default:
166  fprintf(stderr, "vrpn_open_commport: unknown parity setting\n");
167  CloseHandle(hCom);
168  return -1;
169  }
170 
171  dcb.StopBits = ONESTOPBIT;
172  if (charsize == 8)
173  dcb.ByteSize = 8;
174  else if (charsize == 7)
175  dcb.ByteSize = 7;
176  else {
177  fprintf(stderr,
178  "vrpn_open_commport: unknown character size (charsize = %d)\n",
179  charsize);
180  CloseHandle(hCom);
181  return -1;
182  }
183 
184  // Turn on RTS hardware flow control if we've been asked to.
185  if (rts_flow) {
186  dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
187  }
188 
189  if ((fSuccess = SetCommState(hCom, &dcb)) == 0) {
190  perror("vrpn_open_commport: cannot set serial port configuration "
191  "settings");
192  CloseHandle(hCom);
193  return -1;
194  }
195 
196  cto.ReadIntervalTimeout = MAXDWORD;
197  cto.ReadTotalTimeoutMultiplier = MAXDWORD;
198  cto.ReadTotalTimeoutConstant = 1;
199  cto.WriteTotalTimeoutConstant = 0;
200  cto.WriteTotalTimeoutMultiplier = 0;
201 
202  if ((fSuccess = SetCommTimeouts(hCom, &cto)) == 0) {
203  perror("vrpn_open_commport: cannot set serial port timeouts");
204  CloseHandle(hCom);
205  return -1;
206  }
207 
208  curCom++;
209  commConnections[curCom] = hCom;
210 
211  return curCom;
212 
213 // -- This section is "Win32"
214 #else
215 // -- This section is "Not win32"
216 
217 // Open the serial port for r/w
218 #if defined(sol) || defined(__APPLE__) || defined(linux)
219  if ((fileDescriptor = open(portname, O_RDWR | O_NDELAY | O_NOCTTY)) == -1) {
220 #else
221  if ((fileDescriptor = open(portname, O_RDWR)) == -1) {
222 #endif
223  perror("vrpn_open_commport: cannot open serial port");
224  return -1;
225  }
226 
227 #ifdef VERBOSE
228  printf("vrpn_open_commport(): Getting settings\n");
229 #endif
230  /* get current settings */
231  if (tcgetattr(fileDescriptor, &sttyArgs) == -1) {
232  perror("vrpn_open_commport: tcgetattr failed");
233  return (-1);
234  }
235 
236  /* override old values */
237  speed_t speed;
238  switch (baud) {
239  case 300:
240  speed = B300;
241  break;
242  case 1200:
243  speed = B1200;
244  break;
245  case 2400:
246  speed = B2400;
247  break;
248  case 4800:
249  speed = B4800;
250  break;
251  case 9600:
252  speed = B9600;
253  break;
254  case 19200:
255  speed = B19200;
256  break;
257  case 38400:
258  speed = B38400;
259  break;
260 #ifdef B57600
261  case 57600:
262  speed = B57600;
263  break;
264 #endif // End B57600
265 #ifdef B115200
266  case 115200:
267  speed = B115200;
268  break;
269 #endif // End B115200
270  default:
271  fprintf(stderr, "vrpn_open_commport: unknown baud rate %ld\n", baud);
272  return -1;
273  }
274  cfsetispeed(&sttyArgs, speed);
275  cfsetospeed(&sttyArgs, speed);
276 
277  sttyArgs.c_iflag = (IGNBRK | IGNPAR); /* Ignore break & parity errs */
278  sttyArgs.c_oflag = 0; /* Raw output, leave tabs alone */
279  sttyArgs.c_lflag = 0; /* Raw input (no KILL, etc.), no echo */
280 
281  sttyArgs.c_cflag &= ~CSIZE;
282  if (charsize == 8)
283  sttyArgs.c_cflag |= CSIZE & CS8; /* 8 bits */
284  else if (charsize == 7)
285  sttyArgs.c_cflag |= CSIZE & CS7; /* 7 bits */
286  else {
287  fprintf(stderr,
288  "vrpn_open_commport: unknown character size (charsize = %d)\n",
289  charsize);
290  return -1;
291  }
292  sttyArgs.c_cflag &= ~CSTOPB; /* One stop bit */
293 
294  switch (parity) {
296  sttyArgs.c_cflag &= ~PARENB; /* No parity */
297  break;
298  case vrpn_SER_PARITY_ODD:
299  sttyArgs.c_cflag |= PARENB | PARODD;
300  break;
302  sttyArgs.c_cflag |= PARENB;
303  sttyArgs.c_cflag &= ~PARODD;
304  break;
307  default:
308  fprintf(stderr, "vrpn_open_commport: unsupported parity setting (only "
309  "none, odd and even)\n");
310  return -1;
311  }
312 
313  sttyArgs.c_cflag |= CREAD; /* Allow reading */
314  sttyArgs.c_cflag |= CLOCAL; /* No modem between us and device */
315 
316  sttyArgs.c_cc[VMIN] = 0; /* Return read even if no chars */
317  sttyArgs.c_cc[VTIME] = 0; /* Return without waiting */
318 
319  // Enable RTS hardware flow control if we've been asked for it.
320  if (rts_flow) {
321  sttyArgs.c_cflag |= CRTSCTS;
322  }
323 
324 #ifdef VERBOSE
325  printf("vrpn_open_commport(): Setting settings\n");
326 #endif
327  /* pass the new settings back to the driver */
328  if (tcsetattr(fileDescriptor, TCSANOW, &sttyArgs) == -1) {
329  perror("vrpn_open_commport: tcsetattr failed");
330  close(fileDescriptor);
331  return (-1);
332  }
333 
334 #ifdef VERBOSE
335  printf("vrpn_open_commport(): Exiting\n");
336 #endif
337  return (fileDescriptor);
338 // -- This section is "Not win32"
339 #endif // _WIN32
340 
341 #endif // !defined(...lots of stuff...)
342 }
343 
344 // When finished close the commport.
345 int vrpn_close_commport(int comm)
346 {
347 #ifdef VERBOSE
348  printf("vrpn_close_commport(): Entering\n");
349 #endif
350 #if defined(_WIN32) && !defined(__CYGWIN__)
351  int ret = CloseHandle(commConnections[comm]);
352 
353  for (int i = comm; i < curCom - 1; i++)
354  commConnections[i] = commConnections[i + 1];
355 
356  commConnections[curCom--] = NULL;
357 
358  return ret;
359 #else
360  return close(comm);
361 #endif
362 }
363 
364 // Set the RTS ("ready to send") bit on an open commport.
365 int vrpn_set_rts(int comm)
366 {
367 #ifdef VERBOSE
368  printf("vrpn_set_rts(): Entering\n");
369 #endif
370 #if defined(_WIN32)
371  // Determine the handle for this serial port by looking it
372  // up in our list. Then make the system call that Kyle from
373  // Ascension told us about. Return 0 on success; the Windows
374  // function returns nonzero on success
375  return EscapeCommFunction(commConnections[comm], SETRTS) != 0;
376 
377 #else
378  // XXX There are termios methods to enable/disable RTSCTS. but
379  // they do not actually seem to set the line. We need to use
380  // an ioctl interface, which has been shown to work, to do this
381  // function. We are doing this now by going directly to the
382  // modem bits.
383  int flags;
384  if (ioctl(comm, TIOCMGET, &flags) == -1) {
385  perror("vrpn_set_rts: Failed to get modem status bits");
386  return (-1);
387  }
388  flags |= TIOCM_RTS;
389  if (ioctl(comm, TIOCMSET, &flags) == -1) {
390  perror("vrpn_set_rts: Failed to set modem status bits");
391  return (-1);
392  }
393 
394  return 0;
395 #endif
396 }
397 
398 // Clear the RTS ("ready to send") bit on an open commport.
399 int vrpn_clear_rts(int comm)
400 {
401 #ifdef VERBOSE
402  printf("vrpn_clear_rts(): Entering\n");
403 #endif
404 #if defined(_WIN32)
405  // Determine the handle for this serial port by looking it
406  // up in our list. Then make the system call that Kyle from
407  // Ascension told us about. Return 0 on success; the Windows
408  // function returns nonzero on success
409  return EscapeCommFunction(commConnections[comm], CLRRTS) != 0;
410 
411 #else
412  // XXX There are termios methods to enable/disable RTSCTS. but
413  // they do not actually seem to set the line. We need to use
414  // an ioctl interface, which has been shown to work, to do this
415  // function. We are doing this now by going directly to the
416  // modem bits.
417  int flags;
418  if (ioctl(comm, TIOCMGET, &flags) == -1) {
419  perror("vrpn_set_rts: Failed to get modem status bits");
420  return (-1);
421  }
422  flags &= ~TIOCM_RTS;
423  if (ioctl(comm, TIOCMSET, &flags) == -1) {
424  perror("vrpn_set_rts: Failed to set modem status bits");
425  return (-1);
426  }
427 
428  return 0;
429 #endif
430 }
431 
432 // empty the input buffer (discarding the chars)
433 // Return 0 on success, -1 on failure.
434 // NOT CALLED! OBSOLETE? -- no ... used by vrpn_Flock and others
436 {
437 #ifdef VERBOSE
438  printf("vrpn_flush_input_buffer(): Entering\n");
439 #endif
440 #if defined(hpux) || defined(__hpux) || defined(ultrix) || defined(__CYGWIN__)
441  fprintf(
442  stderr,
443  "vrpn_flush_input_buffer: Not impemented on cygwin, ultrix, or HP\n");
444  return -1;
445 #else
446 #if defined(_WIN32)
447  if (PurgeComm(commConnections[comm], PURGE_RXCLEAR) != 0) {
448  return 0;
449  }
450  else {
451  return -1;
452  }
453 #else
454  return tcflush(comm, TCIFLUSH);
455 #endif
456 #endif
457 }
458 
459 // empty the output buffer, discarding all of the chars
460 // Return 0 on success, tc err codes other on failure.
461 // NOT CALLED! OBSOLETE? -- no ... used by vrpn_Flock
463 {
464 #ifdef VERBOSE
465  printf("vrpn_flush_output_buffer(): Entering\n");
466 #endif
467 #if defined(hpux) || defined(__hpux) || defined(ultrix) || defined(__CYGWIN__)
468  fprintf(stderr, "vrpn_flush_output_buffer: Not impemented on NT, ultrix, "
469  "HP, or cygwin\n");
470  return -1;
471 #else
472 
473 #if defined(_WIN32)
474  return PurgeComm(commConnections[comm], PURGE_TXCLEAR);
475 #else
476  return tcflush(comm, TCOFLUSH);
477 #endif
478 
479 #endif
480 }
481 
482 // empty the output buffer, waiting for all of the chars to be delivered
483 // Return 0 on success, nonzero on failure.
484 // NOT CALLED! OBSOLETE? -- no ... used by vrpn_Flock
486 {
487 #ifdef VERBOSE
488  printf("vrpn_drain_output_buffer(): Entering\n");
489 #endif
490 #if defined(hpux) || defined(__hpux) || defined(ultrix) || \
491  defined(__CYGWIN__) || defined(__ANDROID__)
492 
493  fprintf(stderr, "vrpn_drain_output_buffer: Not impemented on NT, ultrix, "
494  "android, or HP\n");
495  return -1;
496 #else
497 
498 #if defined(_WIN32)
499  return FlushFileBuffers(commConnections[comm]) == 0;
500 #else
501  return tcdrain(comm);
502 #endif
503 
504 #endif
505 }
506 
507 // This routine will read any available characters from the handle into
508 // the buffer specified, up to the number of bytes requested. It returns
509 // the number of characters read or -1 on failure. Note that it only
510 // reads characters that are available at the time of the read, so less
511 // than the requested number may be returned.
512 int vrpn_read_available_characters(int comm, unsigned char *buffer,
513  size_t bytes)
514 {
515 #ifdef VERBOSE
516  printf("vrpn_read_available_characters(): Entering\n");
517 #endif
518 #if defined(_WIN32) && !defined(__CYGWIN__)
519  BOOL fSuccess;
520  DWORD numRead;
521  COMSTAT cstat;
522  DWORD errors;
523  OVERLAPPED Overlapped;
524 
525  Overlapped.Offset = 0;
526  Overlapped.OffsetHigh = 0;
527  Overlapped.hEvent = NULL;
528 
529  if ((fSuccess = ClearCommError(commConnections[comm], &errors, &cstat)) ==
530  0) {
531  perror("vrpn_read_available_characters: can't get current status");
532  return (-1);
533  }
534 
535  if (cstat.cbInQue > 0) {
536  if ((fSuccess = ReadFile(commConnections[comm], buffer,
537  static_cast<DWORD>(bytes), &numRead,
538  &Overlapped)) == 0) {
539  perror(
540  "vrpn_read_available_characters: can't read from serial port");
541  return (-1);
542  }
543 
544  return numRead;
545  }
546 
547  return 0;
548 #else
549  int bRead;
550 
551  // on sgi's (and possibly other architectures) the folks from
552  // ascension have noticed that a read command will not necessarily
553  // read everything available in the read buffer (see the following file:
554  // /afs/cs.unc.edu/project/hmd/src/libs/tracker/apps/ascension/FLOCK232/C/unix.txt
555  // For this reason, we loop and try to fill the buffer, stopping our
556  // loop whenever no chars are available.
557  int cReadThisTime = 0;
558  int cSpaceLeft = bytes;
559  unsigned char *pch = buffer;
560 
561  do {
562  if ((cReadThisTime = read(comm, (char *)pch, cSpaceLeft)) == -1) {
563  // If the read stopped because of an interrupt, don't cause an error
564  // but rather behave as if no characters were available.
565  if (errno == EINTR) {
566  cReadThisTime = 0;
567  }
568  else {
569  perror("vrpn_read_available_characters: cannot read from "
570  "serial port");
571  fprintf(stderr, "buffer = %p, %d\n", pch,
572  static_cast<int>(bytes));
573  return -1;
574  }
575  }
576  cSpaceLeft -= cReadThisTime;
577  pch += cReadThisTime;
578  } while ((cReadThisTime != 0) && (cSpaceLeft > 0));
579  bRead = pch - buffer;
580 
581 #ifdef VERBOSE
582  printf("vrpn_read_available_characters(): Exiting\n");
583 #endif
584  return bRead;
585 #endif
586 }
587 
589 // If there is a NULL timeout pointer, block indefinitely. Return the number
590 // of characters read.
591 
592 int vrpn_read_available_characters(int comm, unsigned char *buffer,
593  size_t bytes, struct timeval *timeout)
594 {
595 #ifdef VERBOSE
596  printf("vrpn_read_available_characters(timeout): Entering\n");
597 #endif
598  struct timeval start, finish, now;
599  int sofar = 0, ret; // How many characters we have read so far
600  unsigned char *where = buffer;
601 
602  // Find out what time it is at the start, and when we should end
603  // (unless the timeout is NULL)
604  if (timeout == NULL) {
605  // Set up so that it will never be that now > finish
606  // This prevents the while() loop below from stopping looping
607  vrpn_gettimeofday(&now, NULL);
608  memcpy(&finish, &now, sizeof(finish));
609  vrpn_gettimeofday(&finish, NULL);
610  }
611  else {
612  vrpn_gettimeofday(&start, NULL);
613  memcpy(&now, &start, sizeof(now));
614  time_add(start, *timeout, finish);
615  }
616 
617  // Keep reading until we timeout. Exit from the middle of this by
618  // returning if we complete or find an error so that the loop only has
619  // to check for timeout, not other terminating conditions.
620  do {
621  ret = vrpn_read_available_characters(comm, where, bytes - sofar);
622  if (ret == -1) {
623  return -1;
624  }
625  sofar += ret;
626  if (sofar == bytes) {
627  break;
628  }
629  where += ret;
630  if (timeout != NULL) { // Update the time if we are checking timeout
631  vrpn_gettimeofday(&now, NULL);
632  }
633  } while (!(time_greater(now, finish)));
634 
635 #ifdef VERBOSE
636  printf("vrpn_read_available_characters(timeout): Exiting\n");
637 #endif
638  return sofar;
639 }
640 
642 
643 int vrpn_write_characters(int comm, const unsigned char *buffer, size_t bytes)
644 {
645 #ifdef VERBOSE
646  printf("vrpn_write_characters(): Entering\n");
647 #endif
648 #if defined(_WIN32) && !defined(__CYGWIN__)
649  BOOL fSuccess;
650  DWORD numWritten;
651  OVERLAPPED Overlapped;
652 
653  Overlapped.Offset = 0;
654  Overlapped.OffsetHigh = 0;
655  Overlapped.hEvent = NULL;
656 
657  if ((fSuccess =
658  WriteFile(commConnections[comm], buffer, static_cast<DWORD>(bytes),
659  &numWritten, &Overlapped)) == 0) {
660  perror("vrpn_write_characters: can't write to serial port");
661  return (-1);
662  }
663 
664  return numWritten;
665 #else
666  return write(comm, buffer, bytes);
667 #endif
668 }
669 
670 int vrpn_write_slowly(int comm, const unsigned char *buffer, size_t bytes,
671  int millisec_delay)
672 {
673  size_t i;
674 
675  for (i = 0; i < bytes; i++) {
676  vrpn_SleepMsecs(millisec_delay);
677  if (vrpn_write_characters(comm, &buffer[i], 1) != 1) {
678  return -1;
679  }
680  }
681  return static_cast<int>(bytes);
682 }
int vrpn_write_characters(int comm, const unsigned char *buffer, size_t bytes)
Write the buffer to the serial port.
Definition: vrpn_Serial.C:643
int vrpn_close_commport(int comm)
Definition: vrpn_Serial.C:345
void vrpn_SleepMsecs(double dMsecs)
Definition: vrpn_Shared.C:157
int vrpn_open_commport(const char *portname, long baud, int charsize, vrpn_SER_PARITY parity, bool rts_flow)
Open a serial port, given its name and baud rate.
Definition: vrpn_Serial.C:54
vrpn_SER_PARITY
Definition: vrpn_Serial.h:15
int vrpn_flush_input_buffer(int comm)
Throw out any characters within the input buffer.
Definition: vrpn_Serial.C:435
vrpn_Serial: Pulls all the serial port routines into one file to make porting to new operating system...
#define time_greater(t1, t2)
Definition: vrpn_Serial.C:44
int vrpn_drain_output_buffer(int comm)
Wait until all of the characters in the output buffer are sent, then return.
Definition: vrpn_Serial.C:485
int vrpn_set_rts(int comm)
Definition: vrpn_Serial.C:365
int vrpn_write_slowly(int comm, const unsigned char *buffer, size_t bytes, int millisec_delay)
Definition: vrpn_Serial.C:670
int vrpn_read_available_characters(int comm, unsigned char *buffer, size_t bytes)
Definition: vrpn_Serial.C:512
int vrpn_clear_rts(int comm)
Definition: vrpn_Serial.C:399
int vrpn_flush_output_buffer(int comm)
Throw out any characters (do not send) within the output buffer.
Definition: vrpn_Serial.C:462
#define vrpn_gettimeofday
Definition: vrpn_Shared.h:89
#define time_add(t1, t2, tr)
Definition: vrpn_Serial.C:35