blob: ac72405be0f8fc27ecf6430948569a2b2821c2c5 [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 1999-2005 Open Source Telecom Corporation.
2// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17//
18// As a special exception, you may use this file as part of a free software
19// library without restriction. Specifically, if other files instantiate
20// templates or use macros or inline functions from this file, or you compile
21// this file and link it with other files to produce an executable, this
22// file does not by itself cause the resulting executable to be covered by
23// the GNU General Public License. This exception does not however
24// invalidate any other reasons why the executable file might be covered by
25// the GNU General Public License.
26//
27// This exception applies only to the code released under the name GNU
28// Common C++. If you copy code from other releases into a copy of GNU
29// Common C++, as the General Public License permits, the exception does
30// not apply to the code that you add in this way. To avoid misleading
31// anyone as to the status of such modified files, you must delete
32// this exception notice from them.
33//
34// If you write modifications of your own for GNU Common C++, it is your choice
35// whether to permit this exception to apply to your modifications.
36// If you do not wish that, delete this exception notice.
37//
38
39#include <cc++/config.h>
40#ifdef CCXX_WITHOUT_EXTRAS
41#include <cc++/export.h>
42#endif
43#include <cc++/exception.h>
44#include <cc++/thread.h>
45#include <cc++/file.h>
46#ifndef CCXX_WITHOUT_EXTRAS
47#include <cc++/export.h>
48#endif
49#include <cc++/serial.h>
50#include "private.h"
51#include <cstdlib>
52#include <climits>
53
54#ifdef WIN32
55
56#define B256000 CBR_256000
57#define B128000 CBR_128000
58#define B115200 CBR_115200
59#define B57600 CBR_57600
60#define B56000 CBR_56000
61#define B38400 CBR_38400
62#define B19200 CBR_19200
63#define B14400 CBR_14400
64#define B9600 CBR_9600
65#define B4800 CBR_4800
66#define B2400 CBR_2400
67#define B1200 CBR_1200
68#define B600 CBR_600
69#define B300 CBR_300
70#define B110 CBR_110
71
72#include <io.h>
73#include <fcntl.h>
74#else
75#include <sys/ioctl.h>
76#include <termios.h>
77#endif
78
79#include <cerrno>
80#include <iostream>
81
82#ifdef CCXX_NAMESPACES
83namespace ost {
84using std::streambuf;
85using std::iostream;
86using std::ios;
87#endif
88
89#ifndef MAX_INPUT
90#define MAX_INPUT 255
91#endif
92
93#ifndef MAX_CANON
94#define MAX_CANON MAX_INPUT
95#endif
96
97#ifdef __FreeBSD__
98#undef _PC_MAX_INPUT
99#undef _PC_MAX_CANON
100#endif
101
102#if defined(__QNX__)
103#define CRTSCTS (IHFLOW | OHFLOW)
104#endif
105
106#if defined(_THR_UNIXWARE) || defined(__hpux) || defined(_AIX)
107#include <sys/termiox.h>
108#define CRTSCTS (CTSXON | RTSXOFF)
109#endif
110
111// IRIX
112
113#ifndef CRTSCTS
114#ifdef CNEW_RTSCTS
115#define CRTSCTS (CNEW_RTSCTS)
116#endif
117#endif
118
119#if defined(CTSXON) && defined(RTSXOFF) && !defined(CRTSCTS)
120#define CRTSCTS (CTSXON | RTSXOFF)
121#endif
122
123#ifndef CRTSCTS
124#define CRTSCTS 0
125#endif
126
127Serial::Serial(const char *fname)
128{
129 initSerial();
130
131 open(fname);
132
133#ifdef WIN32
134 if(dev == INVALID_HANDLE_VALUE)
135#else
136 if(dev < 0)
137#endif
138 {
139 error(errOpenFailed);
140 return;
141 }
142
143#ifdef WIN32
144 COMMTIMEOUTS CommTimeOuts ;
145
146 GetCommTimeouts(dev, &CommTimeOuts);
147
148// CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
149 CommTimeOuts.ReadIntervalTimeout = 0;
150
151 CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;
152 CommTimeOuts.ReadTotalTimeoutConstant = 0;
153 CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ;
154 CommTimeOuts.WriteTotalTimeoutConstant = 1000;
155
156 SetCommTimeouts(dev, &CommTimeOuts) ;
157
158#else
159
160 if(!isatty(dev)) {
161 Serial::close();
162 error(errOpenNoTty);
163 return;
164 }
165#endif
166}
167
168Serial::~Serial()
169{
170 endSerial();
171}
172
173void Serial::initConfig(void)
174{
175#ifdef WIN32
176
177#define ASCII_XON 0x11
178#define ASCII_XOFF 0x13
179
180 DCB * attr = (DCB *)current;
181 DCB * orig = (DCB *)original;
182
183 attr->DCBlength = sizeof(DCB);
184 orig->DCBlength = sizeof(DCB);
185
186 GetCommState(dev, orig);
187 GetCommState(dev, attr);
188
189 attr->DCBlength = sizeof(DCB);
190 attr->BaudRate = 1200;
191 attr->Parity = NOPARITY;
192 attr->ByteSize = 8;
193
194 attr->XonChar = ASCII_XON;
195 attr->XoffChar = ASCII_XOFF;
196 attr->XonLim = 100;
197 attr->XoffLim = 100;
198 attr->fOutxDsrFlow = 0;
199 attr->fDtrControl = DTR_CONTROL_ENABLE;
200 attr->fOutxCtsFlow = 1;
201 attr->fRtsControl = RTS_CONTROL_ENABLE;
202 attr->fInX = attr->fOutX = 0;
203
204 attr->fBinary = true;
205 attr->fParity = true;
206
207 SetCommState(dev, attr);
208
209#else
210 struct termios *attr = (struct termios *)current;
211 struct termios *orig = (struct termios *)original;
212 long ioflags = fcntl(dev, F_GETFL);
213
214 tcgetattr(dev, (struct termios *)original);
215 tcgetattr(dev, (struct termios *)current);
216
217 attr->c_oflag = attr->c_lflag = 0;
218 attr->c_cflag = CLOCAL | CREAD | HUPCL;
219 attr->c_iflag = IGNBRK;
220
221 memset(attr->c_cc, 0, sizeof(attr->c_cc));
222 attr->c_cc[VMIN] = 1;
223
224 // inherit original settings, maybe we should keep more??
225 cfsetispeed(attr, cfgetispeed(orig));
226 cfsetospeed(attr, cfgetospeed(orig));
227 attr->c_cflag |= orig->c_cflag & (CRTSCTS | CSIZE | PARENB | PARODD | CSTOPB);
228 attr->c_iflag |= orig->c_iflag & (IXON | IXANY | IXOFF);
229
230 tcsetattr(dev, TCSANOW, attr);
231 fcntl(dev, F_SETFL, ioflags & ~O_NDELAY);
232
233#if defined(TIOCM_RTS) && defined(TIOCMODG)
234 int mcs = 0;
235 ioctl(dev, TIOCMODG, &mcs);
236 mcs |= TIOCM_RTS;
237 ioctl(dev, TIOCMODS, &mcs);
238#endif
239
240#ifdef _COHERENT
241 ioctl(dev, TIOCSRTS, 0);
242#endif
243
244#endif // WIN32
245 }
246
247void Serial::restore(void)
248{
249#ifdef WIN32
250 memcpy(current, original, sizeof(DCB));
251 SetCommState(dev, (DCB *)current);
252#else
253 memcpy(current, original, sizeof(struct termios));
254 tcsetattr(dev, TCSANOW, (struct termios *)current);
255#endif
256}
257
258void Serial::initSerial(void)
259{
260 flags.thrown = false;
261 flags.linebuf = false;
262 errid = errSuccess;
263 errstr = NULL;
264
265 dev = INVALID_HANDLE_VALUE;
266#ifdef WIN32
267 current = new DCB;
268 original = new DCB;
269#else
270 current = new struct termios;
271 original = new struct termios;
272#endif
273}
274
275void Serial::endSerial(void)
276{
277#ifdef WIN32
278 if(dev == INVALID_HANDLE_VALUE && original)
279 SetCommState(dev, (DCB *)original);
280
281 if(current)
282 delete (DCB *)current;
283
284 if(original)
285 delete (DCB *)original;
286#else
287 if(dev < 0 && original)
288 tcsetattr(dev, TCSANOW, (struct termios *)original);
289
290 if(current)
291 delete (struct termios *)current;
292
293 if(original)
294 delete (struct termios *)original;
295#endif
296 Serial::close();
297
298 current = NULL;
299 original = NULL;
300}
301
302Serial::Error Serial::error(Error err, char *errs)
303{
304 errid = err;
305 errstr = errs;
306 if(!err)
307 return err;
308
309 if(flags.thrown)
310 return err;
311
312 // prevents recursive throws
313
314 flags.thrown = true;
315#ifdef CCXX_EXCEPTIONS
316 if(Thread::getException() == Thread::throwObject)
317 throw((Serial *)this);
318#ifdef COMMON_STD_EXCEPTION
319 else if(Thread::getException() == Thread::throwException) {
320 if(!errs)
321 errs = (char *)"";
322 throw SerException(String(errs));
323 }
324#endif
325#endif
326 return err;
327}
328
329int Serial::setPacketInput(int size, unsigned char btimer)
330{
331#ifdef WIN32
332 // Still to be done......
333 return 0;
334#else
335
336#ifdef _PC_MAX_INPUT
337 int max = fpathconf(dev, _PC_MAX_INPUT);
338#else
339 int max = MAX_INPUT;
340#endif
341 struct termios *attr = (struct termios *)current;
342
343 if(size > max)
344 size = max;
345
346 attr->c_cc[VEOL] = attr->c_cc[VEOL2] = 0;
347 attr->c_cc[VMIN] = (unsigned char)size;
348 attr->c_cc[VTIME] = btimer;
349 attr->c_lflag &= ~ICANON;
350 tcsetattr(dev, TCSANOW, attr);
351 bufsize = size;
352 return size;
353#endif
354}
355
356int Serial::setLineInput(char newline, char nl1)
357{
358#ifdef WIN32
359 // Still to be done......
360 return 0;
361#else
362
363 struct termios *attr = (struct termios *)current;
364 attr->c_cc[VMIN] = attr->c_cc[VTIME] = 0;
365 attr->c_cc[VEOL] = newline;
366 attr->c_cc[VEOL2] = nl1;
367 attr->c_lflag |= ICANON;
368 tcsetattr(dev, TCSANOW, attr);
369#ifdef _PC_MAX_CANON
370 bufsize = fpathconf(dev, _PC_MAX_CANON);
371#else
372 bufsize = MAX_CANON;
373#endif
374 return bufsize;
375#endif
376}
377
378void Serial::flushInput(void)
379{
380#ifdef WIN32
381 PurgeComm(dev, PURGE_RXABORT | PURGE_RXCLEAR);
382#else
383 tcflush(dev, TCIFLUSH);
384#endif
385}
386
387void Serial::flushOutput(void)
388{
389#ifdef WIN32
390 PurgeComm(dev, PURGE_TXABORT | PURGE_TXCLEAR);
391#else
392 tcflush(dev, TCOFLUSH);
393#endif
394}
395
396void Serial::waitOutput(void)
397{
398#ifdef WIN32
399
400#else
401 tcdrain(dev);
402#endif
403}
404
405Serial &Serial::operator=(const Serial &ser)
406{
407 Serial::close();
408
409 if(ser.dev < 0)
410 return *this;
411
412#ifdef WIN32
413 HANDLE process = GetCurrentProcess();
414
415 int result = DuplicateHandle(process, ser.dev, process, &dev, DUPLICATE_SAME_ACCESS, 0, 0);
416
417 if (0 == result) {
418 memcpy(current, ser.current, sizeof(DCB));
419 memcpy(original, ser.original, sizeof(DCB));
420 }
421#else
422 dev = dup(ser.dev);
423
424 memcpy(current, ser.current, sizeof(struct termios));
425 memcpy(original, ser.original, sizeof(struct termios));
426#endif
427 return *this;
428}
429
430
431void Serial::open(const char * fname)
432{
433
434#ifndef WIN32
435 int cflags = O_RDWR | O_NDELAY;
436 dev = ::open(fname, cflags);
437 if(dev > -1)
438 initConfig();
439#else
440 // open COMM device
441 dev = CreateFile(fname,
442 GENERIC_READ | GENERIC_WRITE,
443 0, // exclusive access
444 NULL, // no security attrs
445 OPEN_EXISTING,
446 FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
447 NULL);
448 if(dev != INVALID_HANDLE_VALUE)
449 initConfig();
450#endif
451}
452
453#ifdef WIN32
454int Serial::aRead(char * Data, const int Length)
455{
456
457 unsigned long dwLength = 0, dwError, dwReadLength;
458 COMSTAT cs;
459 OVERLAPPED ol;
460
461 // Return zero if handle is invalid
462 if(dev == INVALID_HANDLE_VALUE)
463 return 0;
464
465 // Read max length or only what is available
466 ClearCommError(dev, &dwError, &cs);
467
468 // If not requiring an exact byte count, get whatever is available
469 if(Length > (int)cs.cbInQue)
470 dwReadLength = cs.cbInQue;
471 else
472 dwReadLength = Length;
473
474 memset(&ol, 0, sizeof(OVERLAPPED));
475 ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
476
477 if(dwReadLength > 0) {
478 if(ReadFile(dev, Data, dwReadLength, &dwLength, &ol) == FALSE) {
479 if(GetLastError() == ERROR_IO_PENDING) {
480 WaitForSingleObject(ol.hEvent, INFINITE);
481 GetOverlappedResult(dev, &ol, &dwLength, TRUE);
482 }
483 else
484 ClearCommError(dev, &dwError, &cs);
485 }
486 }
487
488 if(ol.hEvent != INVALID_HANDLE_VALUE)
489 CloseHandle(ol.hEvent);
490
491 return dwLength;
492}
493
494int Serial::aWrite(const char * Data, const int Length)
495{
496 COMSTAT cs;
497 unsigned long dwError = 0;
498 OVERLAPPED ol;
499
500 // Clear the com port of any error condition prior to read
501 ClearCommError(dev, &dwError, &cs);
502
503 unsigned long retSize = 0;
504
505 memset(&ol, 0, sizeof(OVERLAPPED));
506 ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
507
508 if(WriteFile(dev, Data, Length, &retSize, &ol) == FALSE) {
509 if(GetLastError() == ERROR_IO_PENDING) {
510 WaitForSingleObject(ol.hEvent, INFINITE);
511 GetOverlappedResult(dev, &ol, &retSize, TRUE);
512 }
513 else
514 ClearCommError(dev, &dwError, &cs);
515 }
516
517 if(ol.hEvent != INVALID_HANDLE_VALUE)
518 CloseHandle(ol.hEvent);
519
520 return retSize;
521}
522#else
523int Serial::aRead(char *Data, const int Length)
524{
525 return ::read(dev, Data, Length);
526}
527
528int Serial::aWrite(const char *Data, const int Length)
529{
530 return ::write(dev, Data, Length);
531}
532#endif
533
534void Serial::close()
535{
536#ifdef WIN32
537 CloseHandle(dev);
538#else
539 ::close(dev);
540#endif
541
542 dev = INVALID_HANDLE_VALUE;
543}
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568/*
569const int iAsync::getTimeOuts(unsigned long & readTimeout, unsigned long & writeTimeout)
570{
571 return GetCommTimeouts(_TheHandle, &CommTimeOuts);
572 }
573
574const int iAsync::setTimeOuts(unsigned long readTimeout, unsigned long writeTimeout)
575{
576 COMMTIMEOUTS CommTimeOuts ;
577
578 getTimeOuts(CommTimeOuts);
579
580 CommTimeOuts.ReadIntervalTimeout = readTimeout;
581 CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;
582 CommTimeOuts.ReadTotalTimeoutConstant = 0;
583 CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ;
584 CommTimeOuts.WriteTotalTimeoutConstant = 1000;
585
586 return GetCommTimeouts(_TheHandle, &CommTimeOuts);
587 }
588 return SetCommTimeouts(_TheHandle, &CommTimeOuts) ;
589{
590 DCB dcb ;
591
592 dcb.DCBlength = sizeof(DCB) ;
593
594 GetCommState(_TheHandle, &dcb) ;
595
596 // hardcode this stuff for now.....
597 dcb.BaudRate = _TheBaudrate;
598 dcb.ByteSize = 8;
599 dcb.Parity = NOPARITY;
600 dcb.StopBits = ONESTOPBIT;
601 dcb.fOutxDsrFlow = 0;
602 dcb.fDtrControl = DTR_CONTROL_ENABLE ;
603 dcb.fOutxCtsFlow = 1;
604 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE ;
605 dcb.fInX = dcb.fOutX = 0;
606
607 dcb.XonChar = ASCII_XON;
608 dcb.XoffChar = ASCII_XOFF;
609 dcb.XonLim = 100;
610 dcb.XoffLim = 100;
611
612 // other various settings
613
614 dcb.fBinary = TRUE;
615 dcb.fParity = TRUE;
616
617 GetCommState(_TheHandle, &dcb) ;
618 dcb.DCBlength = sizeof(DCB) ;
619 dcb.BaudRate = _TheBaudrate;
620 SetCommState(_TheHandle, &dcb) ;
621
622 }
623*/
624
625Serial::Error Serial::setSpeed(unsigned long speed)
626{
627 unsigned long rate;
628
629 switch(speed) {
630#ifdef B115200
631 case 115200:
632 rate = B115200;
633 break;
634#endif
635#ifdef B57600
636 case 57600:
637 rate = B57600;
638 break;
639#endif
640#ifdef B38400
641 case 38400:
642 rate = B38400;
643 break;
644#endif
645 case 19200:
646 rate = B19200;
647 break;
648 case 9600:
649 rate = B9600;
650 break;
651 case 4800:
652 rate = B4800;
653 break;
654 case 2400:
655 rate = B2400;
656 break;
657 case 1200:
658 rate = B1200;
659 break;
660 case 600:
661 rate = B600;
662 break;
663 case 300:
664 rate = B300;
665 break;
666 case 110:
667 rate = B110;
668 break;
669#ifdef B0
670 case 0:
671 rate = B0;
672 break;
673#endif
674 default:
675 return error(errSpeedInvalid);
676 }
677
678#ifdef WIN32
679
680 DCB * dcb = (DCB *)current;
681 dcb->DCBlength = sizeof(DCB);
682 GetCommState(dev, dcb);
683
684 dcb->BaudRate = rate;
685 SetCommState(dev, dcb) ;
686
687#else
688 struct termios *attr = (struct termios *)current;
689 cfsetispeed(attr, rate);
690 cfsetospeed(attr, rate);
691 tcsetattr(dev, TCSANOW, attr);
692#endif
693 return errSuccess;
694 }
695
696Serial::Error Serial::setFlowControl(Flow flow)
697{
698#ifdef WIN32
699
700 DCB * attr = (DCB *)current;
701 attr->XonChar = ASCII_XON;
702 attr->XoffChar = ASCII_XOFF;
703 attr->XonLim = 100;
704 attr->XoffLim = 100;
705
706 switch(flow) {
707 case flowSoft:
708 attr->fInX = attr->fOutX = 1;
709 break;
710 case flowBoth:
711 attr->fInX = attr->fOutX = 1;
712 case flowHard:
713 attr->fOutxCtsFlow = 1;
714 attr->fRtsControl = RTS_CONTROL_HANDSHAKE;
715 break;
716 case flowNone:
717 break;
718 default:
719 return error(errFlowInvalid);
720 }
721
722 SetCommState(dev, attr);
723#else
724
725 struct termios *attr = (struct termios *)current;
726
727 attr->c_cflag &= ~CRTSCTS;
728 attr->c_iflag &= ~(IXON | IXANY | IXOFF);
729
730 switch(flow) {
731 case flowSoft:
732 attr->c_iflag |= (IXON | IXANY | IXOFF);
733 break;
734 case flowBoth:
735 attr->c_iflag |= (IXON | IXANY | IXOFF);
736 case flowHard:
737 attr->c_cflag |= CRTSCTS;
738 break;
739 case flowNone:
740 break;
741 default:
742 return error(errFlowInvalid);
743 }
744
745 tcsetattr(dev, TCSANOW, attr);
746
747#endif
748 return errSuccess;
749 }
750
751Serial::Error Serial::setStopBits(int bits)
752{
753#ifdef WIN32
754
755 DCB * attr = (DCB *)current;
756 switch(bits) {
757 case 1:
758 attr->StopBits = ONESTOPBIT;
759 break;
760 case 2:
761 attr->StopBits = TWOSTOPBITS;
762 break;
763 default:
764 return error(errStopbitsInvalid);
765 }
766
767 SetCommState(dev, attr);
768#else
769 struct termios *attr = (struct termios *)current;
770 attr->c_cflag &= ~CSTOPB;
771
772 switch(bits) {
773 case 1:
774 break;
775 case 2:
776 attr->c_cflag |= CSTOPB;
777 break;
778 default:
779 return error(errStopbitsInvalid);
780 }
781 tcsetattr(dev, TCSANOW, attr);
782#endif
783 return errSuccess;
784 }
785
786Serial::Error Serial::setCharBits(int bits)
787{
788#ifdef WIN32
789
790 DCB * attr = (DCB *)current;
791 switch(bits) {
792 case 5:
793 case 6:
794 case 7:
795 case 8:
796 attr->ByteSize = bits;
797 break;
798 default:
799 return error(errCharsizeInvalid);
800 }
801 SetCommState(dev, attr);
802#else
803 struct termios *attr = (struct termios *)current;
804 attr->c_cflag &= ~CSIZE;
805
806 switch(bits) {
807 case 5:
808 attr->c_cflag |= CS5;
809 break;
810 case 6:
811 attr->c_cflag |= CS6;
812 break;
813 case 7:
814 attr->c_cflag |= CS7;
815 break;
816 case 8:
817 attr->c_cflag |= CS8;
818 break;
819 default:
820 return error(errCharsizeInvalid);
821 }
822 tcsetattr(dev, TCSANOW, attr);
823#endif
824 return errSuccess;
825 }
826
827Serial::Error Serial::setParity(Parity parity)
828{
829#ifdef WIN32
830
831 DCB * attr = (DCB *)current;
832 switch(parity) {
833 case parityEven:
834 attr->Parity = EVENPARITY;
835 break;
836 case parityOdd:
837 attr->Parity = ODDPARITY;
838 break;
839 case parityNone:
840 attr->Parity = NOPARITY;
841 break;
842 default:
843 return error(errParityInvalid);
844 }
845 SetCommState(dev, attr);
846#else
847 struct termios *attr = (struct termios *)current;
848 attr->c_cflag &= ~(PARENB | PARODD);
849
850 switch(parity) {
851 case parityEven:
852 attr->c_cflag |= PARENB;
853 break;
854 case parityOdd:
855 attr->c_cflag |= (PARENB | PARODD);
856 break;
857 case parityNone:
858 break;
859 default:
860 return error(errParityInvalid);
861 }
862 tcsetattr(dev, TCSANOW, attr);
863#endif
864 return errSuccess;
865 }
866
867void Serial::sendBreak(void)
868{
869#ifdef WIN32
870 SetCommBreak(dev);
871 Thread::sleep(100L);
872 ClearCommBreak(dev);
873#else
874 tcsendbreak(dev, 0);
875#endif
876 }
877
878void Serial::toggleDTR(timeout_t millisec)
879{
880#ifdef WIN32
881 EscapeCommFunction(dev, CLRDTR);
882 if(millisec) {
883 Thread::sleep(millisec);
884 EscapeCommFunction(dev, SETDTR);
885 }
886
887#else
888 struct termios tty, old;
889 tcgetattr(dev, &tty);
890 tcgetattr(dev, &old);
891 cfsetospeed(&tty, B0);
892 cfsetispeed(&tty, B0);
893 tcsetattr(dev, TCSANOW, &tty);
894
895 if(millisec) {
896 Thread::sleep(millisec);
897 tcsetattr(dev, TCSANOW, &old);
898 }
899#endif
900 }
901
902bool Serial::isPending(Pending pending, timeout_t timeout)
903{
904#ifdef WIN32
905 unsigned long dwError;
906 COMSTAT cs;
907
908 ClearCommError(dev, &dwError, &cs);
909
910 if(timeout == 0 || ((pending == pendingInput) && (0 != cs.cbInQue)) ||
911 ((pending == pendingOutput) && (0 != cs.cbOutQue)) || (pending == pendingError))
912 {
913 switch(pending) {
914 case pendingInput:
915 return (0 != cs.cbInQue);
916 case pendingOutput:
917 return (0 != cs.cbOutQue);
918 case pendingError:
919 return false;
920 }
921 }
922 else {
923 Thread::Cancel save = Thread::enterCancel();
924 OVERLAPPED ol;
925 DWORD dwMask;
926 DWORD dwEvents = 0;
927 BOOL suc;
928
929 memset(&ol, 0, sizeof(OVERLAPPED));
930 ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
931
932 if(pending == pendingInput)
933 dwMask = EV_RXCHAR;
934 else if(pending == pendingOutput)
935 dwMask = EV_TXEMPTY;
936 else // on error
937 dwMask = EV_ERR;
938
939 SetCommMask(dev, dwMask);
940 // let's wait for event or timeout
941 if((suc = WaitCommEvent(dev, &dwEvents, &ol)) == FALSE) {
942 if(GetLastError() == ERROR_IO_PENDING) {
943 DWORD transferred;
944
945 dwError = WaitForSingleObject(ol.hEvent, timeout);
946 if (dwError != WAIT_OBJECT_0)
947 SetCommMask(dev, 0);
948
949 suc = GetOverlappedResult(dev, &ol, &transferred, TRUE);
950 if (suc)
951 suc = (dwEvents & dwMask) ? TRUE : FALSE;
952 }
953 else
954 ClearCommError(dev, &dwError, &cs);
955 }
956
957 if(ol.hEvent != INVALID_HANDLE_VALUE)
958 CloseHandle(ol.hEvent);
959
960 Thread::exitCancel(save);
961 if(suc == FALSE)
962 return false;
963 return true;
964 }
965#else
966
967
968 int status;
969#ifdef HAVE_POLL
970 struct pollfd pfd;
971
972 pfd.fd = dev;
973 pfd.revents = 0;
974 switch(pending) {
975 case pendingInput:
976 pfd.events = POLLIN;
977 break;
978 case pendingOutput:
979 pfd.events = POLLOUT;
980 break;
981 case pendingError:
982 pfd.events = POLLERR | POLLHUP;
983 break;
984 }
985
986 status = 0;
987 while(status < 1) {
988 if(timeout == TIMEOUT_INF)
989 status = poll(&pfd, 1, -1);
990 else
991 status = poll(&pfd, 1, timeout);
992
993 if(status < 1) {
994 if(status == -1 && errno == EINTR)
995 continue;
996 return false;
997 }
998 }
999
1000 if(pfd.revents & pfd.events)
1001 return true;
1002
1003#else
1004 struct timeval tv;
1005 fd_set grp;
1006 struct timeval *tvp = &tv;
1007
1008 if(timeout == TIMEOUT_INF)
1009 tvp = NULL;
1010 else {
1011 tv.tv_usec = (timeout % 1000) * 1000;
1012 tv.tv_sec = timeout / 1000;
1013 }
1014
1015 FD_ZERO(&grp);
1016 FD_SET(dev, &grp);
1017 switch(pending) {
1018 case pendingInput:
1019 status = select(dev + 1, &grp, NULL, NULL, tvp);
1020 break;
1021 case pendingOutput:
1022 status = select(dev + 1, NULL, &grp, NULL, tvp);
1023 break;
1024 case pendingError:
1025 status = select(dev + 1, NULL, NULL, &grp, tvp);
1026 break;
1027 }
1028 if(status < 1)
1029 return false;
1030
1031 if(FD_ISSET(dev, &grp))
1032 return true;
1033
1034#endif
1035
1036#endif // WIN32
1037
1038 return false;
1039 }
1040
1041
1042
1043
1044
1045TTYStream::TTYStream(const char *filename, timeout_t to)
1046 : streambuf(),
1047 Serial(filename),
1048#ifdef HAVE_OLD_IOSTREAM
1049 iostream()
1050#else
1051 iostream((streambuf *)this)
1052#endif
1053{
1054#ifdef HAVE_OLD_IOSTREAM
1055 init((streambuf *)this);
1056#endif
1057 gbuf = pbuf = NULL;
1058 timeout = to;
1059
1060 if(INVALID_HANDLE_VALUE != dev)
1061 allocate();
1062 }
1063
1064TTYStream::TTYStream()
1065 : streambuf(),
1066 Serial(),
1067#ifdef HAVE_OLD_IOSTREAM
1068 iostream()
1069#else
1070 iostream((streambuf *)this)
1071#endif
1072{
1073#ifdef HAVE_OLD_IOSTREAM
1074 init((streambuf *)this);
1075#endif
1076 timeout = 0;
1077 gbuf = pbuf = NULL;
1078 }
1079
1080TTYStream::~TTYStream()
1081{
1082 endStream();
1083 endSerial();
1084 }
1085
1086void TTYStream::endStream(void)
1087{
1088 if(bufsize)
1089 sync();
1090
1091 if(gbuf) {
1092 delete[] gbuf;
1093 gbuf = NULL;
1094 }
1095 if(pbuf) {
1096 delete[] pbuf;
1097 pbuf = NULL;
1098 }
1099 bufsize = 0;
1100 clear();
1101 }
1102
1103void TTYStream::allocate(void)
1104{
1105 if(INVALID_HANDLE_VALUE == dev)
1106 return;
1107
1108#ifdef _PC_MAX_INPUT
1109 bufsize = fpathconf(dev, _PC_MAX_INPUT);
1110#else
1111 bufsize = MAX_INPUT;
1112#endif
1113
1114 gbuf = new char[bufsize];
1115 pbuf = new char[bufsize];
1116
1117 if(!pbuf || !gbuf) {
1118 error(errResourceFailure);
1119 return;
1120 }
1121
1122 clear();
1123
1124#if !(defined(STLPORT) || defined(__KCC))
1125 setg(gbuf, gbuf + bufsize, 0);
1126#endif
1127
1128 setg(gbuf, gbuf + bufsize, gbuf + bufsize);
1129 setp(pbuf, pbuf + bufsize);
1130 }
1131
1132int TTYStream::doallocate()
1133{
1134 if(bufsize)
1135 return 0;
1136
1137 allocate();
1138 return 1;
1139 }
1140
1141void TTYStream::interactive(bool iflag)
1142{
1143#ifdef WIN32
1144 if(dev == INVALID_HANDLE_VALUE)
1145#else
1146 if(dev < 0)
1147#endif
1148 return;
1149
1150 if(bufsize >= 1)
1151 endStream();
1152
1153 if(iflag) {
1154 // setting to unbuffered mode
1155
1156 bufsize = 1;
1157 gbuf = new char[bufsize];
1158
1159#if !(defined(STLPORT) || defined(__KCC))
1160#if defined(__GNUC__) && (__GNUC__ < 3)
1161 setb(0,0);
1162#endif
1163#endif
1164 setg(gbuf, gbuf+bufsize, gbuf+bufsize);
1165 setp(pbuf, pbuf);
1166 return;
1167 }
1168
1169 if(bufsize < 2)
1170 allocate();
1171 }
1172
1173int TTYStream::uflow(void)
1174{
1175 int rlen;
1176 unsigned char ch;
1177
1178 if(bufsize < 2) {
1179 if(timeout) {
1180 if(Serial::isPending(pendingInput, timeout))
1181 rlen = aRead((char *)&ch, 1);
1182 else
1183 rlen = -1;
1184 }
1185 else
1186 rlen = aRead((char *)&ch, 1);
1187 if(rlen < 1) {
1188 if(rlen < 0)
1189 clear(ios::failbit | rdstate());
1190 return EOF;
1191 }
1192 return ch;
1193 }
1194 else {
1195 ch = underflow();
1196 gbump(1);
1197 return ch;
1198 }
1199 }
1200
1201int TTYStream::underflow(void)
1202{
1203 ssize_t rlen = 1;
1204
1205 if(!gptr())
1206 return EOF;
1207
1208 if(gptr() < egptr())
1209 return (unsigned char)*gptr();
1210
1211 rlen = (ssize_t)((gbuf + bufsize) - eback());
1212 if(timeout && !Serial::isPending(pendingInput, timeout))
1213 rlen = -1;
1214 else
1215 rlen = aRead((char *)eback(), rlen);
1216
1217 if(rlen < 1) {
1218 if(rlen < 0) {
1219 clear(ios::failbit | rdstate());
1220 error(errInput);
1221 }
1222 return EOF;
1223 }
1224
1225 setg(eback(), eback(), eback() + rlen);
1226 return (unsigned char) *gptr();
1227 }
1228
1229int TTYStream::sync(void)
1230{
1231 if(bufsize > 1 && pbase() && ((pptr() - pbase()) > 0)) {
1232 overflow(0);
1233 waitOutput();
1234 setp(pbuf, pbuf + bufsize);
1235 }
1236 setg(gbuf, gbuf + bufsize, gbuf + bufsize);
1237 return 0;
1238 }
1239
1240int TTYStream::overflow(int c)
1241{
1242 unsigned char ch;
1243 ssize_t rlen, req;
1244
1245 if(bufsize < 2) {
1246 if(c == EOF)
1247 return 0;
1248
1249 ch = (unsigned char)(c);
1250 rlen = aWrite((char *)&ch, 1);
1251 if(rlen < 1) {
1252 if(rlen < 0)
1253 clear(ios::failbit | rdstate());
1254 return EOF;
1255 }
1256 else
1257 return c;
1258 }
1259
1260 if(!pbase())
1261 return EOF;
1262
1263 req = (ssize_t)(pptr() - pbase());
1264 if(req) {
1265 rlen = aWrite((char *)pbase(), req);
1266 if(rlen < 1) {
1267 if(rlen < 0)
1268 clear(ios::failbit | rdstate());
1269 return EOF;
1270 }
1271 req -= rlen;
1272 }
1273
1274 if(req)
1275// memmove(pptr(), pptr() + rlen, req);
1276 memmove(pbuf, pbuf + rlen, req);
1277 setp(pbuf + req, pbuf + bufsize);
1278
1279 if(c != EOF) {
1280 *pptr() = (unsigned char)c;
1281 pbump(1);
1282 }
1283 return c;
1284 }
1285
1286bool TTYStream::isPending(Pending pending, timeout_t timer)
1287{
1288// if(pending == pendingInput && in_avail())
1289// return true;
1290// else if(pending == pendingOutput)
1291// flush();
1292
1293 return Serial::isPending(pending, timer);
1294 }
1295
1296
1297
1298
1299
1300
1301ttystream::ttystream() :
1302TTYStream()
1303{
1304 setError(false);
1305 }
1306
1307ttystream::ttystream(const char *name) :
1308TTYStream()
1309{
1310 setError(false);
1311 open(name);
1312 }
1313
1314void ttystream::close(void)
1315{
1316#ifdef WIN32
1317 if (INVALID_HANDLE_VALUE == dev)
1318#else
1319 if(dev < 0)
1320#endif
1321 return;
1322
1323 endStream();
1324 restore();
1325 TTYStream::close();
1326 }
1327
1328void ttystream::open(const char *name)
1329{
1330 const char *cpp;
1331 char *cp;
1332 char pathname[256];
1333 size_t namelen;
1334 long opt;
1335
1336 if (INVALID_HANDLE_VALUE != dev) {
1337 restore();
1338 close();
1339 }
1340
1341 cpp = strrchr(name, ':');
1342 if(cpp)
1343 namelen = cpp - name;
1344 else
1345 namelen = strlen(name);
1346
1347 cp = pathname;
1348
1349#ifndef WIN32
1350 if(*name != '/') {
1351 strcpy(pathname, "/dev/");
1352 cp += 5;
1353 }
1354
1355 if((cp - pathname) + namelen > 255) {
1356 error(errResourceFailure);
1357 return;
1358 }
1359#endif
1360 setString(cp, pathname - cp + sizeof(pathname), name);
1361 cp += namelen;
1362#ifdef WIN32
1363 *cp++ = ':';
1364#endif
1365 *cp = 0;
1366
1367 Serial::open(pathname);
1368
1369 if(INVALID_HANDLE_VALUE == dev) {
1370 error(errOpenFailed);
1371 return;
1372 }
1373
1374 allocate();
1375
1376 setString(pathname, sizeof(pathname), name + namelen);
1377 cp = pathname + 1;
1378
1379 if(*pathname == ':')
1380 cp = strtok(cp, ",");
1381 else
1382 cp = NULL;
1383
1384 while(cp) {
1385 switch(*cp) {
1386 case 'h':
1387 case 'H':
1388 setFlowControl(flowHard);
1389 break;
1390 case 's':
1391 case 'S':
1392 setFlowControl(flowSoft);
1393 break;
1394 case 'b':
1395 case 'B':
1396 setFlowControl(flowBoth);
1397 break;
1398 case 'n':
1399 case 'N':
1400 setParity(parityNone);
1401 break;
1402 case 'O':
1403 case 'o':
1404 setParity(parityOdd);
1405 break;
1406 case 'e':
1407 case 'E':
1408 setParity(parityEven);
1409 break;
1410 case '0':
1411 case '1':
1412 case '2':
1413 case '3':
1414 case '4':
1415 case '5':
1416 case '6':
1417 case '7':
1418 case '8':
1419 case '9':
1420 opt = atol(cp);
1421 if(opt == 1 || opt == 2) {
1422 setStopBits((int)opt);
1423 break;
1424 }
1425 if(opt > 4 && opt < 9) {
1426 setCharBits((int)opt);
1427 break;
1428 }
1429 setSpeed(opt);
1430 break;
1431 default:
1432 error(errOptionInvalid);
1433 }
1434 cp = strtok(NULL, ",");
1435 }
1436 }
1437
1438TTYSession::TTYSession(const char *filename, int pri, int stack) :
1439Thread(pri, stack), TTYStream(filename)
1440{
1441 setError(false);
1442 }
1443
1444
1445TTYSession::~TTYSession()
1446{
1447 terminate();
1448 endSerial();
1449 }
1450
1451#ifndef WIN32
1452// Not supporting this right now........
1453//
1454
1455SerialPort::SerialPort(SerialService *svc, const char *name) :
1456Serial(name),
1457detect_pending(true),
1458detect_output(false),
1459detect_disconnect(true)
1460{
1461 next = prev = NULL;
1462 service = NULL;
1463
1464#ifdef WIN32
1465 if(INVALID_HANDLE_VALUE != dev)
1466#else
1467 if(dev > -1)
1468#endif
1469 {
1470 setError(false);
1471 service = svc;
1472 svc->attach(this);
1473 }
1474 }
1475
1476SerialPort::~SerialPort()
1477{
1478 if(service)
1479 service->detach(this);
1480
1481 endSerial();
1482 }
1483
1484void SerialPort::expired(void)
1485{
1486 }
1487
1488void SerialPort::pending(void)
1489{
1490 }
1491
1492void SerialPort::disconnect(void)
1493{
1494 }
1495
1496void SerialPort::output(void)
1497{
1498 }
1499
1500void SerialPort::setTimer(timeout_t ptimer)
1501{
1502 TimerPort::setTimer(ptimer);
1503 service->update();
1504 }
1505
1506void SerialPort::incTimer(timeout_t ptimer)
1507{
1508 TimerPort::incTimer(ptimer);
1509 service->update();
1510 }
1511
1512
1513void SerialPort::setDetectPending( bool val )
1514{
1515 if ( detect_pending != val ) {
1516 detect_pending = val;
1517#ifdef USE_POLL
1518 if ( ufd ) {
1519 if ( val ) {
1520 ufd->events |= POLLIN;
1521 } else {
1522 ufd->events &= ~POLLIN;
1523 }
1524 }
1525#endif
1526 service->update();
1527 }
1528 }
1529
1530void SerialPort::setDetectOutput( bool val )
1531{
1532 if ( detect_output != val ) {
1533 detect_output = val;
1534#ifdef USE_POLL
1535 if ( ufd ) {
1536 if ( val ) {
1537 ufd->events |= POLLOUT;
1538 } else {
1539 ufd->events &= ~POLLOUT;
1540 }
1541 }
1542#endif
1543 service->update();
1544 }
1545 }
1546
1547
1548SerialService::SerialService(int pri, size_t stack, const char *id) :
1549Thread(pri, stack), Mutex(id)
1550{
1551 long opt;
1552
1553 first = last = NULL;
1554 count = 0;
1555 FD_ZERO(&connect);
1556 if(::pipe(iosync)) {
1557#ifdef CCXX_EXCEPTIONS
1558 switch(Thread::getException()) {
1559 case throwObject:
1560 throw(this);
1561 return;
1562#ifdef COMMON_STD_EXCEPTION
1563 case throwException:
1564 throw(ThrException("no service pipe"));
1565 return;
1566#endif
1567 default:
1568 return;
1569 }
1570#else
1571 return;
1572#endif
1573 }
1574 hiwater = iosync[0] + 1;
1575 FD_SET(iosync[0], &connect);
1576
1577 opt = fcntl(iosync[0], F_GETFL);
1578 fcntl(iosync[0], F_SETFL, opt | O_NDELAY);
1579 }
1580
1581SerialService::~SerialService()
1582{
1583 update(0);
1584 terminate();
1585
1586 while(first)
1587 delete first;
1588 }
1589
1590void SerialService::onUpdate(unsigned char flag)
1591{
1592 }
1593
1594void SerialService::onEvent(void)
1595{
1596 }
1597
1598void SerialService::onCallback(SerialPort *port)
1599{
1600 }
1601
1602void SerialService::attach(SerialPort *port)
1603{
1604 enterMutex();
1605#ifdef USE_POLL
1606 port->ufd = 0;
1607#endif
1608 if(last)
1609 last->next = port;
1610
1611 port->prev = last;
1612 last = port;
1613 FD_SET(port->dev, &connect);
1614 if(port->dev >= hiwater)
1615 hiwater = port->dev + 1;
1616
1617 if(!first) {
1618 first = port;
1619 leaveMutex();
1620 ++count;
1621 start();
1622 }
1623 else {
1624 leaveMutex();
1625 update();
1626 ++count;
1627 }
1628 }
1629
1630void SerialService::detach(SerialPort *port)
1631{
1632 enterMutex();
1633
1634#ifndef USE_POLL
1635 FD_CLR(port->dev, &connect);
1636#endif
1637
1638 if(port->prev)
1639 port->prev->next = port->next;
1640 else
1641 first = port->next;
1642
1643 if(port->next)
1644 port->next->prev = port->prev;
1645 else
1646 last = port->prev;
1647
1648 --count;
1649 leaveMutex();
1650 update();
1651 }
1652
1653void SerialService::update(unsigned char flag)
1654{
1655 if(::write(iosync[1], (char *)&flag, 1) < 1) {
1656
1657#ifdef CCXX_EXCEPTIONS
1658 switch(Thread::getException()) {
1659 case throwObject:
1660 throw(this);
1661 return;
1662#ifdef COMMON_STD_EXCEPTION
1663 case throwException:
1664 throw(ThrException("update failed"));
1665 return;
1666#endif
1667 default:
1668 return;
1669 }
1670#else
1671 return;
1672#endif
1673 }
1674 }
1675
1676
1677void SerialService::run(void)
1678{
1679 timeout_t timer, expires;
1680 SerialPort *port;
1681 unsigned char buf;
1682
1683#ifdef USE_POLL
1684
1685 Poller mfd;
1686 pollfd *p_ufd;
1687 int lastcount = 0;
1688
1689 // initialize ufd in all attached ports :
1690 // probably don't need this but it can't hurt.
1691 enterMutex();
1692 port = first;
1693 while(port) {
1694 port->ufd = 0;
1695 port = port->next;
1696 }
1697 leaveMutex();
1698
1699#else
1700 struct timeval timeout, *tvp;
1701 fd_set inp, out, err;
1702 int dev;
1703 FD_ZERO(&inp);
1704 FD_ZERO(&out);
1705 FD_ZERO(&err);
1706#endif
1707
1708 setCancel(cancelDeferred);
1709 for(;;) {
1710 timer = TIMEOUT_INF;
1711 while(1 == ::read(iosync[0], (char *)&buf, 1)) {
1712 if(buf) {
1713 onUpdate(buf);
1714 continue;
1715 }
1716
1717 setCancel(cancelImmediate);
1718 sleep(TIMEOUT_INF);
1719 exit();
1720 }
1721
1722#ifdef USE_POLL
1723
1724 bool reallocate = false;
1725
1726 enterMutex();
1727 onEvent();
1728 port = first;
1729 while(port) {
1730 onCallback(port);
1731 if ( ( p_ufd = port->ufd ) ) {
1732
1733 if ( ( POLLHUP | POLLNVAL ) & p_ufd->revents ) {
1734 // Avoid infinite loop from disconnected sockets
1735 port->detect_disconnect = false;
1736 p_ufd->events &= ~POLLHUP;
1737 port->disconnect();
1738 }
1739
1740 if ( ( POLLIN | POLLPRI ) & p_ufd->revents )
1741 port->pending();
1742
1743 if ( POLLOUT & p_ufd->revents )
1744 port->output();
1745
1746 } else {
1747 reallocate = true;
1748 }
1749
1750retry:
1751 expires = port->getTimer();
1752 if(expires > 0)
1753 if(expires < timer)
1754 timer = expires;
1755
1756 if(!expires) {
1757 port->endTimer();
1758 port->expired();
1759 goto retry;
1760 }
1761
1762 port = port->next;
1763 }
1764
1765 //
1766 // reallocate things if we saw a ServerPort without
1767 // ufd set !
1768 if ( reallocate || ( ( count + 1 ) != lastcount ) ) {
1769 lastcount = count + 1;
1770 p_ufd = mfd.getList( count + 1 );
1771
1772 // Set up iosync polling
1773 p_ufd->fd = iosync[0];
1774 p_ufd->events = POLLIN | POLLHUP;
1775 p_ufd ++;
1776
1777 port = first;
1778 while(port) {
1779 p_ufd->fd = port->dev;
1780 p_ufd->events =
1781 ( port->detect_disconnect ? POLLHUP : 0 )
1782 | ( port->detect_output ? POLLOUT : 0 )
1783 | ( port->detect_pending ? POLLIN : 0 )
1784 ;
1785 port->ufd = p_ufd;
1786 p_ufd ++;
1787 port = port->next;
1788 }
1789 }
1790 leaveMutex();
1791
1792 poll( mfd.getList(), count + 1, timer );
1793
1794#else
1795 enterMutex();
1796 onEvent();
1797 port = first;
1798
1799 while(port) {
1800 onCallback(port);
1801 dev = port->dev;
1802 if(FD_ISSET(dev, &err)) {
1803 port->detect_disconnect = false;
1804 port->disconnect();
1805 }
1806
1807 if(FD_ISSET(dev, &inp))
1808 port->pending();
1809
1810 if(FD_ISSET(dev, &out))
1811 port->output();
1812
1813retry:
1814 expires = port->getTimer();
1815 if(expires > 0)
1816 if(expires < timer)
1817 timer = expires;
1818
1819 if(!expires) {
1820 port->endTimer();
1821 port->expired();
1822 goto retry;
1823 }
1824
1825 port = port->next;
1826 }
1827
1828 FD_ZERO(&inp);
1829 FD_ZERO(&out);
1830 FD_ZERO(&err);
1831 int so;
1832 port = first;
1833 while(port) {
1834 so = port->dev;
1835
1836 if(port->detect_pending)
1837 FD_SET(so, &inp);
1838
1839 if(port->detect_output)
1840 FD_SET(so, &out);
1841
1842 if(port->detect_disconnect)
1843 FD_SET(so, &err);
1844
1845 port = port->next;
1846 }
1847
1848 leaveMutex();
1849 if(timer == TIMEOUT_INF)
1850 tvp = NULL;
1851 else {
1852 tvp = &timeout;
1853 timeout.tv_sec = timer / 1000;
1854 timeout.tv_usec = (timer % 1000) * 1000;
1855 }
1856 select(hiwater, &inp, &out, &err, tvp);
1857#endif
1858 }
1859 }
1860
1861#endif
1862
1863#ifdef CCXX_NAMESPACES
1864 }
1865#endif
1866
1867/** EMACS **
1868 * Local variables:
1869 * mode: c++
1870 * c-basic-offset: 8
1871 * End:
1872 */