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