/[projects]/smsdaemon/SerialPort.cpp
ViewVC logotype

Contents of /smsdaemon/SerialPort.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 26 - (show annotations) (download)
Mon Jun 9 18:15:53 2008 UTC (15 years, 11 months ago) by torben
File size: 41515 byte(s)
Added first basic edition of smsdaemon.

So far sending & receiving sms works and a basic sample plugin is implemented.

1 /***************************************************************************
2 * Copyright (C) 2004 by Manish Pagey *
3 * crayzeewulf@users.sourceforge.net
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21 #include "SerialPort.h"
22 #include "PosixSignalDispatcher.h"
23 #include "PosixSignalHandler.h"
24 #include <queue>
25 #include <map>
26 #include <cerrno>
27 #include <cassert>
28 #include <termios.h>
29 #include <fcntl.h>
30 #include <sys/ioctl.h>
31 #include <sys/time.h>
32 #include <signal.h>
33 #include <strings.h>
34
35 namespace
36 {
37 //
38 // Various error messages used in this file while throwing
39 // exceptions.
40 //
41 const std::string ERR_MSG_PORT_NOT_OPEN = "Serial port not open." ;
42 const std::string ERR_MSG_PORT_ALREADY_OPEN = "Serial port already open." ;
43 const std::string ERR_MSG_UNSUPPORTED_BAUD = "Unsupported baud rate." ;
44 const std::string ERR_MSG_UNKNOWN_BAUD = "Unknown baud rate." ;
45 const std::string ERR_MSG_INVALID_PARITY = "Invalid parity setting." ;
46 const std::string ERR_MSG_INVALID_STOP_BITS = "Invalid number of stop bits." ;
47 const std::string ERR_MSG_INVALID_FLOW_CONTROL = "Invalid flow control." ;
48
49 /*
50 * Return the difference between the two specified timeval values.
51 * This method subtracts secondOperand from firstOperand and returns
52 * the result as a timeval. The time represented by firstOperand must
53 * be later than the time represented by secondOperand. Otherwise,
54 * the result of this operator may be undefined.
55 */
56 const struct timeval
57 operator-( const struct timeval& firstOperand,
58 const struct timeval& secondOperand ) ;
59 } ;
60
61 class SerialPort::SerialPortImpl : public PosixSignalHandler
62 {
63 public:
64 /**
65 * Constructor.
66 */
67 SerialPortImpl( const std::string& serialPortName ) ;
68
69 /**
70 * Destructor.
71 */
72 ~SerialPortImpl() ;
73
74 /**
75 * Open the serial port.
76 */
77 void Open()
78 throw( SerialPort::OpenFailed,
79 SerialPort::AlreadyOpen ) ;
80
81 /**
82 * Check if the serial port is currently open.
83 */
84 bool
85 IsOpen() const ;
86
87 /**
88 * Close the serial port.
89 */
90 void
91 Close()
92 throw(SerialPort::NotOpen) ;
93
94 /**
95 * Set the baud rate of the serial port.
96 */
97 void
98 SetBaudRate( const SerialPort::BaudRate baudRate )
99 throw( SerialPort::NotOpen,
100 SerialPort::UnsupportedBaudRate,
101 std::invalid_argument,
102 std::runtime_error ) ;
103
104 /**
105 * Get the current baud rate.
106 */
107 SerialPort::BaudRate
108 GetBaudRate() const
109 throw( SerialPort::NotOpen,
110 std::runtime_error ) ;
111
112 /**
113 * Set the character size.
114 */
115 void
116 SetCharSize( const SerialPort::CharacterSize charSize )
117 throw( SerialPort::NotOpen,
118 std::invalid_argument,
119 std::runtime_error ) ;
120
121 /**
122 * Get the current character size.
123 */
124 SerialPort::CharacterSize
125 GetCharSize() const
126 throw( SerialPort::NotOpen,
127 std::runtime_error ) ;
128
129 void
130 SetParity( const SerialPort::Parity parityType )
131 throw( SerialPort::NotOpen,
132 std::invalid_argument,
133 std::runtime_error ) ;
134
135 SerialPort::Parity
136 GetParity() const
137 throw(SerialPort::NotOpen) ;
138
139 void
140 SetNumOfStopBits( const SerialPort::StopBits numOfStopBits )
141 throw( SerialPort::NotOpen,
142 std::invalid_argument ) ;
143
144 SerialPort::StopBits
145 GetNumOfStopBits() const
146 throw(SerialPort::NotOpen) ;
147
148 void
149 SetFlowControl( const SerialPort::FlowControl flowControl )
150 throw( SerialPort::NotOpen,
151 std::invalid_argument ) ;
152
153 SerialPort::FlowControl
154 GetFlowControl() const
155 throw( SerialPort::NotOpen ) ;
156
157 bool
158 IsDataAvailable() const
159 throw( SerialPort::NotOpen,
160 std::runtime_error ) ;
161
162 unsigned char
163 ReadByte(const unsigned int msTimeout = 0 )
164 throw( SerialPort::NotOpen,
165 SerialPort::ReadTimeout,
166 std::runtime_error ) ;
167
168 void
169 Read( SerialPort::DataBuffer& dataBuffer,
170 const unsigned int numOfBytes,
171 const unsigned int msTimeout )
172 throw( SerialPort::NotOpen,
173 SerialPort::ReadTimeout,
174 std::runtime_error ) ;
175
176 const std::string
177 ReadLine( const unsigned int msTimeout = 0,
178 const char lineTerminator = '\n' )
179 throw( SerialPort::NotOpen,
180 SerialPort::ReadTimeout,
181 std::runtime_error ) ;
182
183 void
184 WriteByte( const unsigned char dataByte )
185 throw( SerialPort::NotOpen,
186 std::runtime_error ) ;
187
188 void
189 Write(const SerialPort::DataBuffer& dataBuffer)
190 throw( SerialPort::NotOpen,
191 std::runtime_error ) ;
192
193 void
194 Write( const unsigned char* dataBuffer,
195 const unsigned int bufferSize )
196 throw( SerialPort::NotOpen,
197 std::runtime_error ) ;
198
199 void
200 SetDtr( const bool dtrState )
201 throw( SerialPort::NotOpen,
202 std::runtime_error ) ;
203
204 bool
205 GetDtr() const
206 throw( SerialPort::NotOpen,
207 std::runtime_error ) ;
208
209 void
210 SetRts( const bool rtsState )
211 throw( SerialPort::NotOpen,
212 std::runtime_error ) ;
213
214 bool
215 GetRts() const
216 throw( SerialPort::NotOpen,
217 std::runtime_error ) ;
218
219 bool
220 GetCts() const
221 throw( SerialPort::NotOpen,
222 std::runtime_error ) ;
223
224
225 bool
226 GetDsr() const
227 throw( SerialPort::NotOpen,
228 std::runtime_error ) ;
229 /*
230 * This method must be defined by all subclasses of
231 * PosixSignalHandler.
232 */
233 void
234 HandlePosixSignal(int signalNumber) ;
235 private:
236 /**
237 * Name of the serial port. On POSIX systems this is the name of
238 * the device file.
239 */
240 std::string mSerialPortName ;
241
242 /**
243 * Flag that indicates whether the serial port is currently open.
244 */
245 bool mIsOpen ;
246
247 /**
248 * The file descriptor corresponding to the serial port.
249 */
250 int mFileDescriptor ;
251
252 /**
253 * Serial port settings are saved into this variable immediately
254 * after the port is opened. These settings are restored when the
255 * serial port is closed.
256 */
257 termios mOldPortSettings ;
258
259 /**
260 * Circular buffer used to store the received data. This is done
261 * asynchronously and helps prevent overflow of the corresponding
262 * tty's input buffer.
263 *
264 * :TODO: The size of this buffer is allowed to increase indefinitely. If
265 * data keeps arriving at the serial port and is never read then this
266 * buffer will continue occupying more and more memory. We need to put a
267 * cap on the size of this buffer. It might even be worth providing a
268 * method to set the size of this buffer.
269 */
270 std::queue<unsigned char> mInputBuffer ;
271
272 /**
273 * Set the specified modem control line to the specified value.
274 *
275 * @param modemLine One of the following four values: TIOCM_DTR,
276 * TIOCM_RTS, TIOCM_CTS, or TIOCM_DSR.
277 *
278 * @param lineState State of the modem line after successful
279 * call to this method.
280 */
281 void
282 SetModemControlLine( const int modemLine,
283 const bool lineState )
284 throw( SerialPort::NotOpen,
285 std::runtime_error ) ;
286
287 /**
288 * Get the current state of the specified modem control line.
289 *
290 * @param modemLine One of the following four values: TIOCM_DTR,
291 * TIOCM_RTS, TIOCM_CTS, or TIOCM_DSR.
292 *
293 * @return True if the specified line is currently set and false
294 * otherwise.
295 */
296 bool
297 GetModemControlLine( const int modemLine ) const
298 throw( SerialPort::NotOpen,
299 std::runtime_error ) ;
300 } ;
301
302 SerialPort::SerialPort( const std::string& serialPortName ) :
303 mSerialPortImpl(new SerialPortImpl(serialPortName) )
304 {
305 /* empty */
306 }
307
308 SerialPort::~SerialPort()
309 throw()
310 {
311 /*
312 * Close the serial port if it is open.
313 */
314 if ( this->IsOpen() )
315 {
316 this->Close() ;
317 }
318 /*
319 * Free the memory allocated to the implementation instance.
320 */
321 if ( mSerialPortImpl )
322 {
323 delete mSerialPortImpl ;
324 }
325 return ;
326 }
327
328 void
329 SerialPort::Open( const BaudRate baudRate,
330 const CharacterSize charSize,
331 const Parity parityType,
332 const StopBits stopBits,
333 const FlowControl flowControl )
334 throw( OpenFailed,
335 AlreadyOpen,
336 UnsupportedBaudRate,
337 std::invalid_argument )
338 {
339 //
340 // Open the serial port.
341 mSerialPortImpl->Open() ;
342 //
343 // Set the various parameters of the serial port if it is open.
344 //
345 this->SetBaudRate(baudRate) ;
346 this->SetCharSize(charSize) ;
347 this->SetParity(parityType) ;
348 this->SetNumOfStopBits(stopBits) ;
349 this->SetFlowControl(flowControl) ;
350 //
351 // All done.
352 //
353 return ;
354 }
355
356 bool
357 SerialPort::IsOpen() const
358 {
359 return mSerialPortImpl->IsOpen() ;
360 }
361
362 void
363 SerialPort::Close()
364 throw(NotOpen)
365 {
366 mSerialPortImpl->Close() ;
367 return ;
368 }
369
370 void
371 SerialPort::SetBaudRate( const BaudRate baudRate )
372 throw( UnsupportedBaudRate,
373 NotOpen,
374 std::invalid_argument )
375 {
376 mSerialPortImpl->SetBaudRate( baudRate ) ;
377 return ;
378 }
379
380 SerialPort::BaudRate
381 SerialPort::GetBaudRate() const
382 throw( NotOpen,
383 std::runtime_error )
384 {
385 return mSerialPortImpl->GetBaudRate() ;
386 }
387
388
389 void
390 SerialPort::SetCharSize( const CharacterSize charSize )
391 throw( NotOpen,
392 std::invalid_argument )
393 {
394 mSerialPortImpl->SetCharSize(charSize) ;
395 }
396
397 SerialPort::CharacterSize
398 SerialPort::GetCharSize() const
399 throw(NotOpen)
400 {
401 return mSerialPortImpl->GetCharSize() ;
402 }
403
404 void
405 SerialPort::SetParity( const Parity parityType )
406 throw( NotOpen,
407 std::invalid_argument )
408 {
409 mSerialPortImpl->SetParity( parityType ) ;
410 return ;
411 }
412
413 SerialPort::Parity
414 SerialPort::GetParity() const
415 throw(NotOpen)
416 {
417 return mSerialPortImpl->GetParity() ;
418 }
419
420 void
421 SerialPort::SetNumOfStopBits( const StopBits numOfStopBits )
422 throw( NotOpen,
423 std::invalid_argument )
424 {
425 mSerialPortImpl->SetNumOfStopBits(numOfStopBits) ;
426 return ;
427 }
428
429 SerialPort::StopBits
430 SerialPort::GetNumOfStopBits() const
431 throw(NotOpen)
432 {
433 return mSerialPortImpl->GetNumOfStopBits() ;
434 }
435
436
437 void
438 SerialPort::SetFlowControl( const FlowControl flowControl )
439 throw( NotOpen,
440 std::invalid_argument )
441 {
442 mSerialPortImpl->SetFlowControl( flowControl ) ;
443 return ;
444 }
445
446 SerialPort::FlowControl
447 SerialPort::GetFlowControl() const
448 throw( NotOpen )
449 {
450 return mSerialPortImpl->GetFlowControl() ;
451 }
452
453 bool
454 SerialPort::IsDataAvailable() const
455 throw(NotOpen)
456 {
457 return mSerialPortImpl->IsDataAvailable() ;
458 }
459
460 unsigned char
461 SerialPort::ReadByte( const unsigned int msTimeout )
462 throw( NotOpen,
463 ReadTimeout,
464 std::runtime_error )
465 {
466 return mSerialPortImpl->ReadByte(msTimeout) ;
467 }
468
469 void
470 SerialPort::Read( SerialPort::DataBuffer& dataBuffer,
471 const unsigned int numOfBytes,
472 const unsigned int msTimeout )
473 throw( NotOpen,
474 ReadTimeout,
475 std::runtime_error )
476 {
477 return mSerialPortImpl->Read( dataBuffer,
478 numOfBytes,
479 msTimeout ) ;
480 }
481
482
483 const std::string
484 SerialPort::ReadLine( const unsigned int msTimeout,
485 const char lineTerminator )
486 throw( NotOpen,
487 ReadTimeout,
488 std::runtime_error )
489 {
490 return mSerialPortImpl->ReadLine( msTimeout,
491 lineTerminator ) ;
492 }
493
494
495 void
496 SerialPort::WriteByte( const unsigned char dataByte )
497 throw( SerialPort::NotOpen,
498 std::runtime_error )
499 {
500 mSerialPortImpl->WriteByte( dataByte ) ;
501 return ;
502 }
503
504
505 void
506 SerialPort::Write(const DataBuffer& dataBuffer)
507 throw( NotOpen,
508 std::runtime_error )
509 {
510 mSerialPortImpl->Write( dataBuffer ) ;
511 return ;
512 }
513
514 void
515 SerialPort::Write(const std::string& dataString)
516 throw( NotOpen,
517 std::runtime_error )
518 {
519 mSerialPortImpl->Write( reinterpret_cast<const unsigned char*>(dataString.c_str()),
520 dataString.length() ) ;
521 return ;
522 }
523
524 void
525 SerialPort::SetDtr( const bool dtrState )
526 throw( SerialPort::NotOpen,
527 std::runtime_error )
528 {
529 mSerialPortImpl->SetDtr( dtrState ) ;
530 return ;
531 }
532
533 bool
534 SerialPort::GetDtr() const
535 throw( SerialPort::NotOpen,
536 std::runtime_error )
537 {
538 return mSerialPortImpl->GetDtr() ;
539 }
540
541 void
542 SerialPort::SetRts( const bool rtsState )
543 throw( SerialPort::NotOpen,
544 std::runtime_error )
545 {
546 mSerialPortImpl->SetRts( rtsState ) ;
547 return ;
548 }
549
550 bool
551 SerialPort::GetRts() const
552 throw( SerialPort::NotOpen,
553 std::runtime_error )
554 {
555 return mSerialPortImpl->GetRts() ;
556 }
557
558
559 bool
560 SerialPort::GetCts() const
561 throw( SerialPort::NotOpen,
562 std::runtime_error )
563 {
564 return mSerialPortImpl->GetCts() ;
565 }
566
567 bool
568 SerialPort::GetDsr() const
569 throw( SerialPort::NotOpen,
570 std::runtime_error )
571 {
572 return mSerialPortImpl->GetDsr() ;
573 }
574
575 /* ------------------------------------------------------------ */
576 inline
577 SerialPort::SerialPortImpl::SerialPortImpl( const std::string& serialPortName ) :
578 mSerialPortName(serialPortName),
579 mIsOpen(false),
580 mFileDescriptor(-1),
581 mOldPortSettings(),
582 mInputBuffer()
583 {
584 /* empty */
585 }
586
587 inline
588 SerialPort::SerialPortImpl::~SerialPortImpl()
589 {
590 //
591 // Close the serial port if it is open.
592 //
593 if ( this->IsOpen() )
594 {
595 this->Close() ;
596 }
597 return ;
598 }
599
600 inline
601 void
602 SerialPort::SerialPortImpl::Open()
603 throw( SerialPort::OpenFailed,
604 SerialPort::AlreadyOpen )
605 {
606 /*
607 * Throw an exception if the port is already open.
608 */
609 if ( this->IsOpen() )
610 {
611 throw SerialPort::AlreadyOpen( ERR_MSG_PORT_ALREADY_OPEN ) ;
612 }
613 /*
614 * Try to open the serial port and throw an exception if we are
615 * not able to open it.
616 *
617 * :FIXME: Exception thrown by this method after opening the
618 * serial port might leave the port open even though mIsOpen
619 * is false. We need to close the port before throwing an
620 * exception or close it next time this method is called before
621 * calling open() again.
622 */
623 mFileDescriptor = open( mSerialPortName.c_str(),
624 O_RDWR | O_NOCTTY | O_NONBLOCK ) ;
625 if ( mFileDescriptor < 0 )
626 {
627 throw SerialPort::OpenFailed( strerror(errno) ) ;
628 }
629
630
631 PosixSignalDispatcher& signal_dispatcher = PosixSignalDispatcher::Instance() ;
632 signal_dispatcher.AttachHandler( SIGIO,
633 *this ) ;
634
635 /*
636 * Direct all SIGIO and SIGURG signals for the port to the current
637 * process.
638 */
639 if ( fcntl( mFileDescriptor,
640 F_SETOWN,
641 getpid() ) < 0 )
642 {
643 throw SerialPort::OpenFailed( strerror(errno) ) ;
644 }
645
646 /*
647 * Enable asynchronous I/O with the serial port.
648 */
649 if ( fcntl( mFileDescriptor,
650 F_SETFL,
651 FASYNC ) < 0 )
652 {
653 throw SerialPort::OpenFailed( strerror(errno) ) ;
654 }
655
656 /*
657 * Save the current settings of the serial port so they can be
658 * restored when the serial port is closed.
659 */
660 if ( tcgetattr( mFileDescriptor,
661 &mOldPortSettings ) < 0 )
662 {
663 throw SerialPort::OpenFailed( strerror(errno) ) ;
664 }
665
666 //
667 // Start assembling the new port settings.
668 //
669 termios port_settings ;
670 bzero( &port_settings,
671 sizeof( port_settings ) ) ;
672
673 //
674 // Enable the receiver (CREAD) and ignore modem control lines
675 // (CLOCAL).
676 //
677 port_settings.c_cflag |= CREAD | CLOCAL ;
678
679 //
680 // Set the VMIN and VTIME parameters to zero by default. VMIN is
681 // the minimum number of characters for non-canonical read and
682 // VTIME is the timeout in deciseconds for non-canonical
683 // read. Setting both of these parameters to zero implies that a
684 // read will return immediately only giving the currently
685 // available characters.
686 //
687 port_settings.c_cc[ VMIN ] = 0 ;
688 port_settings.c_cc[ VTIME ] = 0 ;
689 /*
690 * Flush the input buffer associated with the port.
691 */
692 if ( tcflush( mFileDescriptor,
693 TCIFLUSH ) < 0 )
694 {
695 throw SerialPort::OpenFailed( strerror(errno) ) ;
696 }
697 /*
698 * Write the new settings to the port.
699 */
700 if ( tcsetattr( mFileDescriptor,
701 TCSANOW,
702 &port_settings ) < 0 )
703 {
704 throw SerialPort::OpenFailed( strerror(errno) ) ;
705 }
706
707 /*
708 * The serial port is open at this point.
709 */
710 mIsOpen = true ;
711 return ;
712 }
713
714 inline
715 bool
716 SerialPort::SerialPortImpl::IsOpen() const
717 {
718 return mIsOpen ;
719 }
720
721 inline
722 void
723 SerialPort::SerialPortImpl::Close()
724 throw( SerialPort::NotOpen )
725 {
726 //
727 // Throw an exception if the serial port is not open.
728 //
729 if ( ! this->IsOpen() )
730 {
731 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
732 }
733 //
734 PosixSignalDispatcher& signal_dispatcher = PosixSignalDispatcher::Instance() ;
735 signal_dispatcher.DetachHandler( SIGIO,
736 *this ) ;
737 //
738 // Restore the old settings of the port.
739 //
740 tcsetattr( mFileDescriptor,
741 TCSANOW,
742 &mOldPortSettings ) ;
743 //
744 // Close the serial port file descriptor.
745 //
746 close(mFileDescriptor) ;
747 //
748 // The port is not open anymore.
749 //
750 mIsOpen = false ;
751 //
752 return ;
753 }
754
755 inline
756 void
757 SerialPort::SerialPortImpl::SetBaudRate( const SerialPort::BaudRate baudRate )
758 throw( SerialPort::NotOpen,
759 SerialPort::UnsupportedBaudRate,
760 std::invalid_argument,
761 std::runtime_error )
762 {
763 //
764 // Throw an exception if the serial port is not open.
765 //
766 if ( ! this->IsOpen() )
767 {
768 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
769 }
770 //
771 // Get the current settings of the serial port.
772 //
773 termios port_settings ;
774 if ( tcgetattr( mFileDescriptor,
775 &port_settings ) < 0 )
776 {
777 throw std::runtime_error( strerror(errno) ) ;
778 }
779 //
780 // Set the baud rate for both input and output.
781 //
782 if ( ( cfsetispeed( &port_settings,
783 baudRate ) < 0 ) ||
784 ( cfsetospeed( &port_settings,
785 baudRate ) < 0 ) )
786 {
787 //
788 // If any of the settings fail, we abandon this method.
789 //
790 throw SerialPort::UnsupportedBaudRate( ERR_MSG_UNSUPPORTED_BAUD ) ;
791 }
792 //
793 // Set the new attributes of the serial port.
794 //
795 if ( tcsetattr( mFileDescriptor,
796 TCSANOW,
797 &port_settings ) < 0 )
798 {
799 throw SerialPort::UnsupportedBaudRate( strerror(errno) ) ;
800 }
801 return ;
802 }
803
804 inline
805 SerialPort::BaudRate
806 SerialPort::SerialPortImpl::GetBaudRate() const
807 throw( SerialPort::NotOpen,
808 std::runtime_error )
809 {
810 //
811 // Make sure that the serial port is open.
812 //
813 if ( ! this->IsOpen() )
814 {
815 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
816 }
817 //
818 // Read the current serial port settings.
819 //
820 termios port_settings ;
821 if ( tcgetattr( mFileDescriptor,
822 &port_settings ) < 0 )
823 {
824 throw std::runtime_error( strerror(errno) ) ;
825 }
826 //
827 // Obtain the input baud rate from the current settings.
828 //
829 return SerialPort::BaudRate(cfgetispeed( &port_settings )) ;
830 }
831
832 inline
833 void
834 SerialPort::SerialPortImpl::SetCharSize( const SerialPort::CharacterSize charSize )
835 throw( SerialPort::NotOpen,
836 std::invalid_argument,
837 std::runtime_error )
838 {
839 //
840 // Make sure that the serial port is open.
841 //
842 if ( ! this->IsOpen() )
843 {
844 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
845 }
846 //
847 // Get the current settings of the serial port.
848 //
849 termios port_settings ;
850 if ( tcgetattr( mFileDescriptor,
851 &port_settings ) < 0 )
852 {
853 throw std::runtime_error( strerror(errno) ) ;
854 }
855 //
856 // Set the character size.
857 //
858 port_settings.c_cflag &= ~CSIZE ;
859 port_settings.c_cflag |= charSize ;
860 //
861 // Apply the modified settings.
862 //
863 if ( tcsetattr( mFileDescriptor,
864 TCSANOW,
865 &port_settings ) < 0 )
866 {
867 throw std::invalid_argument( strerror(errno) ) ;
868 }
869 return ;
870 }
871
872 inline
873 SerialPort::CharacterSize
874 SerialPort::SerialPortImpl::GetCharSize() const
875 throw( SerialPort::NotOpen,
876 std::runtime_error )
877 {
878 //
879 // Make sure that the serial port is open.
880 //
881 if ( ! this->IsOpen() )
882 {
883 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
884 }
885 //
886 // Get the current port settings.
887 //
888 termios port_settings ;
889 if ( tcgetattr( mFileDescriptor,
890 &port_settings ) < 0 )
891 {
892 throw std::runtime_error( strerror(errno) ) ;
893 }
894 //
895 // Read the character size from the setttings.
896 //
897 return SerialPort::CharacterSize( port_settings.c_cflag & CSIZE ) ;
898 }
899
900 inline
901 void
902 SerialPort::SerialPortImpl::SetParity( const SerialPort::Parity parityType )
903 throw( SerialPort::NotOpen,
904 std::invalid_argument,
905 std::runtime_error )
906 {
907 //
908 // Make sure that the serial port is open.
909 //
910 if ( ! this->IsOpen() )
911 {
912 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
913 }
914 //
915 // Get the current port settings.
916 //
917 termios port_settings ;
918 if ( tcgetattr( mFileDescriptor,
919 &port_settings ) < 0 )
920 {
921 throw std::runtime_error( strerror(errno) ) ;
922 }
923 //
924 // Set the parity type depending on the specified parameter.
925 //
926 switch( parityType )
927 {
928 case SerialPort::PARITY_EVEN:
929 port_settings.c_cflag |= PARENB ;
930 port_settings.c_cflag &= ~PARODD ;
931 port_settings.c_iflag |= INPCK ;
932 break ;
933 case SerialPort::PARITY_ODD:
934 port_settings.c_cflag |= ( PARENB | PARODD ) ;
935 port_settings.c_iflag |= INPCK ;
936 break ;
937 case SerialPort::PARITY_NONE:
938 port_settings.c_cflag &= ~(PARENB) ;
939 port_settings.c_iflag |= IGNPAR ;
940 break ;
941 default:
942 throw std::invalid_argument( ERR_MSG_INVALID_PARITY ) ;
943 break ;
944 }
945 //
946 // Apply the modified port settings.
947 //
948 if ( tcsetattr( mFileDescriptor,
949 TCSANOW,
950 &port_settings ) < 0 )
951 {
952 throw std::invalid_argument( strerror(errno) ) ;
953 }
954 return ;
955 }
956
957 inline
958 SerialPort::Parity
959 SerialPort::SerialPortImpl::GetParity() const
960 throw(SerialPort::NotOpen)
961 {
962 //
963 // Make sure that the serial port is open.
964 //
965 if ( ! this->IsOpen() )
966 {
967 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
968 }
969 //
970 // Get the current port settings.
971 //
972 termios port_settings ;
973 if ( tcgetattr( mFileDescriptor,
974 &port_settings ) < 0 )
975 {
976 throw std::runtime_error( strerror(errno) ) ;
977 }
978 //
979 // Get the parity type from the current settings.
980 //
981 if ( port_settings.c_cflag & PARENB )
982 {
983 //
984 // Parity is enabled. Lets check if it is odd or even.
985 //
986 if ( port_settings.c_cflag & PARODD )
987 {
988 return SerialPort::PARITY_ODD ;
989 }
990 else
991 {
992 return SerialPort::PARITY_EVEN ;
993 }
994 }
995 //
996 // Parity is disabled.
997 //
998 return SerialPort::PARITY_NONE ;
999 }
1000
1001 inline
1002 void
1003 SerialPort::SerialPortImpl::SetNumOfStopBits( const SerialPort::StopBits numOfStopBits )
1004 throw( SerialPort::NotOpen,
1005 std::invalid_argument )
1006 {
1007 //
1008 // Make sure that the serial port is open.
1009 //
1010 if ( ! this->IsOpen() )
1011 {
1012 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1013 }
1014 //
1015 // Get the current port settings.
1016 //
1017 termios port_settings ;
1018 if ( tcgetattr( mFileDescriptor,
1019 &port_settings ) < 0 )
1020 {
1021 throw std::runtime_error( strerror(errno) ) ;
1022 }
1023 //
1024 // Set the number of stop bits.
1025 //
1026 switch( numOfStopBits )
1027 {
1028 case SerialPort::STOP_BITS_1:
1029 port_settings.c_cflag &= ~(CSTOPB) ;
1030 break ;
1031 case SerialPort::STOP_BITS_2:
1032 port_settings.c_cflag |= CSTOPB ;
1033 break ;
1034 default:
1035 throw std::invalid_argument( ERR_MSG_INVALID_STOP_BITS ) ;
1036 break ;
1037 }
1038 //
1039 // Apply the modified settings.
1040 //
1041 if ( tcsetattr( mFileDescriptor,
1042 TCSANOW,
1043 &port_settings ) < 0 )
1044 {
1045 throw std::invalid_argument( strerror(errno) ) ;
1046 }
1047 return ;
1048 }
1049
1050 inline
1051 SerialPort::StopBits
1052 SerialPort::SerialPortImpl::GetNumOfStopBits() const
1053 throw(SerialPort::NotOpen)
1054 {
1055 //
1056 // Make sure that the serial port is open.
1057 //
1058 if ( ! this->IsOpen() )
1059 {
1060 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1061 }
1062 //
1063 // Get the current port settings.
1064 //
1065 termios port_settings ;
1066 if ( tcgetattr( mFileDescriptor,
1067 &port_settings ) < 0 )
1068 {
1069 throw std::runtime_error( strerror(errno) ) ;
1070 }
1071 //
1072 // If CSTOPB is set then we are using two stop bits, otherwise we
1073 // are using 1 stop bit.
1074 //
1075 if ( port_settings.c_cflag & CSTOPB )
1076 {
1077 return SerialPort::STOP_BITS_2 ;
1078 }
1079 return SerialPort::STOP_BITS_1 ;
1080 }
1081
1082 inline
1083 void
1084 SerialPort::SerialPortImpl::SetFlowControl( const SerialPort::FlowControl flowControl )
1085 throw( SerialPort::NotOpen,
1086 std::invalid_argument )
1087 {
1088 //
1089 // Make sure that the serial port is open.
1090 //
1091 if ( ! this->IsOpen() )
1092 {
1093 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1094 }
1095 //
1096 // Get the current port settings.
1097 //
1098 termios port_settings ;
1099 if ( tcgetattr( mFileDescriptor,
1100 &port_settings ) < 0 )
1101 {
1102 throw std::runtime_error( strerror(errno) ) ;
1103 }
1104 //
1105 // Set the flow control.
1106 //
1107 switch( flowControl )
1108 {
1109 case SerialPort::FLOW_CONTROL_HARD:
1110 port_settings.c_cflag |= CRTSCTS ;
1111 break ;
1112 case SerialPort::FLOW_CONTROL_NONE:
1113 port_settings.c_cflag &= ~(CRTSCTS) ;
1114 break ;
1115 default:
1116 throw std::invalid_argument( ERR_MSG_INVALID_FLOW_CONTROL ) ;
1117 break ;
1118 }
1119 //
1120 // Apply the modified settings.
1121 //
1122 if ( tcsetattr( mFileDescriptor,
1123 TCSANOW,
1124 &port_settings ) < 0 )
1125 {
1126 throw std::invalid_argument( strerror(errno) ) ;
1127 }
1128 return ;
1129 }
1130
1131 inline
1132 SerialPort::FlowControl
1133 SerialPort::SerialPortImpl::GetFlowControl() const
1134 throw( SerialPort::NotOpen )
1135 {
1136 //
1137 // Make sure that the serial port is open.
1138 //
1139 if ( ! this->IsOpen() )
1140 {
1141 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1142 }
1143 //
1144 // Get the current port settings.
1145 //
1146 termios port_settings ;
1147 if ( tcgetattr( mFileDescriptor,
1148 &port_settings ) < 0 )
1149 {
1150 throw std::runtime_error( strerror(errno) ) ;
1151 }
1152 //
1153 // If CRTSCTS is set then we are using hardware flow
1154 // control. Otherwise, we are not using any flow control.
1155 //
1156 if ( port_settings.c_cflag & CRTSCTS )
1157 {
1158 return SerialPort::FLOW_CONTROL_HARD ;
1159 }
1160 return SerialPort::FLOW_CONTROL_NONE ;
1161 }
1162
1163 inline
1164 bool
1165 SerialPort::SerialPortImpl::IsDataAvailable() const
1166 throw( SerialPort::NotOpen,
1167 std::runtime_error )
1168 {
1169 //
1170 // Make sure that the serial port is open.
1171 //
1172 if ( ! this->IsOpen() )
1173 {
1174 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1175 }
1176 //
1177 // Check if any data is available in the input buffer.
1178 //
1179 return ( mInputBuffer.size() > 0 ? true : false ) ;
1180 }
1181
1182 inline
1183 unsigned char
1184 SerialPort::SerialPortImpl::ReadByte(const unsigned int msTimeout)
1185 throw( SerialPort::NotOpen,
1186 SerialPort::ReadTimeout,
1187 std::runtime_error )
1188 {
1189 //
1190 // Make sure that the serial port is open.
1191 //
1192 if ( ! this->IsOpen() )
1193 {
1194 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1195 }
1196 //
1197 // Get the current time. Throw an exception if we are unable
1198 // to read the current time.
1199 //
1200 struct timeval entry_time ;
1201 if ( gettimeofday( &entry_time,
1202 NULL ) < 0 )
1203 {
1204 throw std::runtime_error( strerror(errno) ) ;
1205 }
1206 //
1207 // Wait for data to be available.
1208 //
1209 const int MICROSECONDS_PER_MS = 1000 ;
1210 const int MILLISECONDS_PER_SEC = 1000 ;
1211 //
1212 while( 0 == mInputBuffer.size() )
1213 {
1214 //
1215 // Read the current time.
1216 //
1217 struct timeval curr_time ;
1218 if ( gettimeofday( &curr_time,
1219 NULL ) < 0 )
1220 {
1221 throw std::runtime_error( strerror(errno) ) ;
1222 }
1223 //
1224 // Obtain the elapsed time.
1225 //
1226 struct timeval elapsed_time = curr_time - entry_time ;
1227 //
1228 // Increase the elapsed number of milliseconds.
1229 //
1230 int elapsed_ms = ( elapsed_time.tv_sec * MILLISECONDS_PER_SEC +
1231 elapsed_time.tv_usec / MICROSECONDS_PER_MS ) ;
1232 //
1233 // If more than msTimeout milliseconds have elapsed while
1234 // waiting for data, then we throw a ReadTimeout exception.
1235 //
1236 if ( ( msTimeout > 0 ) &&
1237 ( elapsed_ms > msTimeout ) )
1238 {
1239 throw SerialPort::ReadTimeout() ;
1240 }
1241 //
1242 // Wait for 1ms (1000us) for data to arrive.
1243 //
1244 usleep( MICROSECONDS_PER_MS ) ;
1245 }
1246 //
1247 // Return the first byte and remove it from the queue.
1248 //
1249 unsigned char next_char = mInputBuffer.front() ;
1250 mInputBuffer.pop() ;
1251 return next_char ;
1252 }
1253
1254 inline
1255 void
1256 SerialPort::SerialPortImpl::Read( SerialPort::DataBuffer& dataBuffer,
1257 const unsigned int numOfBytes,
1258 const unsigned int msTimeout )
1259 throw( SerialPort::NotOpen,
1260 SerialPort::ReadTimeout,
1261 std::runtime_error )
1262 {
1263 //
1264 // Make sure that the serial port is open.
1265 //
1266 if ( ! this->IsOpen() )
1267 {
1268 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1269 }
1270 //
1271 // Empty the data buffer.
1272 //
1273 dataBuffer.resize(0) ;
1274 //
1275 if ( 0 == numOfBytes )
1276 {
1277 //
1278 // Read all available data if numOfBytes is zero.
1279 //
1280 while( this->IsDataAvailable() )
1281 {
1282 dataBuffer.push_back( ReadByte(msTimeout) ) ;
1283 }
1284 }
1285 else
1286 {
1287 //
1288 // Reserve enough space in the buffer to store the incoming
1289 // data.
1290 //
1291 dataBuffer.reserve( numOfBytes ) ;
1292 //
1293 for(int i=0; i<numOfBytes; ++i)
1294 {
1295 dataBuffer.push_back( ReadByte(msTimeout) ) ;
1296 }
1297 }
1298 return ;
1299 }
1300
1301 inline
1302 const std::string
1303 SerialPort::SerialPortImpl::ReadLine( const unsigned int msTimeout,
1304 const char lineTerminator )
1305 throw( SerialPort::NotOpen,
1306 SerialPort::ReadTimeout,
1307 std::runtime_error )
1308 {
1309 std::string result ;
1310 char next_char = 0 ;
1311 do
1312 {
1313 next_char = this->ReadByte( msTimeout ) ;
1314 result += next_char ;
1315 }
1316 while( next_char != lineTerminator ) ;
1317 return result ;
1318 }
1319
1320 inline
1321 void
1322 SerialPort::SerialPortImpl::WriteByte( const unsigned char dataByte )
1323 throw( SerialPort::NotOpen,
1324 std::runtime_error )
1325 {
1326 //
1327 // Make sure that the serial port is open.
1328 //
1329 if ( ! this->IsOpen() )
1330 {
1331 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1332 }
1333 //
1334 // Write the byte to the serial port.
1335 //
1336 this->Write( &dataByte,
1337 1 ) ;
1338 return ;
1339 }
1340
1341 inline
1342 void
1343 SerialPort::SerialPortImpl::Write(const SerialPort::DataBuffer& dataBuffer)
1344 throw( SerialPort::NotOpen,
1345 std::runtime_error )
1346 {
1347 //
1348 // Make sure that the serial port is open.
1349 //
1350 if ( ! this->IsOpen() )
1351 {
1352 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1353 }
1354 //
1355 // Nothing needs to be done if there is no data in the buffer.
1356 //
1357 if ( 0 == dataBuffer.size() )
1358 {
1359 return ;
1360 }
1361 //
1362 // Allocate memory for storing the contents of the
1363 // dataBuffer. This allows us to write all the data using a single
1364 // call to write() instead of writing one byte at a time.
1365 //
1366 unsigned char* local_buffer = new unsigned char[dataBuffer.size()] ;
1367 if ( 0 == local_buffer )
1368 {
1369 throw std::runtime_error( std::string(__FUNCTION__) +
1370 ": Cannot allocate memory while writing"
1371 "data to the serial port." ) ;
1372 }
1373 //
1374 // Copy the data into local_buffer.
1375 //
1376 std::copy( dataBuffer.begin(),
1377 dataBuffer.end(),
1378 local_buffer ) ;
1379 //
1380 // Write data to the serial port.
1381 //
1382 try
1383 {
1384 this->Write( local_buffer,
1385 dataBuffer.size() ) ;
1386 }
1387 catch( ... )
1388 {
1389 //
1390 // Free the allocated memory.
1391 //
1392 delete [] local_buffer ;
1393 throw ;
1394 }
1395 //
1396 // Free the allocated memory.
1397 //
1398 delete [] local_buffer ;
1399 return ;
1400 }
1401
1402 inline
1403 void
1404 SerialPort::SerialPortImpl::Write( const unsigned char* dataBuffer,
1405 const unsigned int bufferSize )
1406 throw( SerialPort::NotOpen,
1407 std::runtime_error )
1408 {
1409 //
1410 // Make sure that the serial port is open.
1411 //
1412 if ( ! this->IsOpen() )
1413 {
1414 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1415 }
1416 //
1417 // Write the data to the serial port. Keep retrying if EAGAIN
1418 // error is received.
1419 //
1420 int num_of_bytes_written = -1 ;
1421 do
1422 {
1423 num_of_bytes_written = write( mFileDescriptor,
1424 dataBuffer,
1425 bufferSize ) ;
1426 }
1427 while ( ( num_of_bytes_written < 0 ) &&
1428 ( EAGAIN == errno ) ) ;
1429 //
1430 if ( num_of_bytes_written < 0 )
1431 {
1432 throw std::runtime_error( strerror(errno) ) ;
1433 }
1434 //
1435 // :FIXME: What happens if num_of_bytes_written < bufferSize ?
1436 //
1437 return ;
1438 }
1439
1440 inline
1441 void
1442 SerialPort::SerialPortImpl::SetDtr( const bool dtrState )
1443 throw( SerialPort::NotOpen,
1444 std::runtime_error )
1445 {
1446 this->SetModemControlLine( TIOCM_DTR,
1447 dtrState ) ;
1448 return ;
1449 }
1450
1451 inline
1452 bool
1453 SerialPort::SerialPortImpl::GetDtr() const
1454 throw( SerialPort::NotOpen,
1455 std::runtime_error )
1456 {
1457 return this->GetModemControlLine( TIOCM_DTR ) ;
1458 }
1459
1460 inline
1461 void
1462 SerialPort::SerialPortImpl::SetRts( const bool rtsState )
1463 throw( SerialPort::NotOpen,
1464 std::runtime_error )
1465 {
1466 this->SetModemControlLine( TIOCM_RTS,
1467 rtsState ) ;
1468 return ;
1469 }
1470
1471 inline
1472 bool
1473 SerialPort::SerialPortImpl::GetRts() const
1474 throw( SerialPort::NotOpen,
1475 std::runtime_error )
1476 {
1477 return this->GetModemControlLine( TIOCM_RTS ) ;
1478 }
1479
1480
1481 inline
1482 bool
1483 SerialPort::SerialPortImpl::GetCts() const
1484 throw( SerialPort::NotOpen,
1485 std::runtime_error )
1486 {
1487 return this->GetModemControlLine( TIOCM_CTS ) ;
1488 }
1489
1490
1491 inline
1492 bool
1493 SerialPort::SerialPortImpl::GetDsr() const
1494 throw( SerialPort::NotOpen,
1495 std::runtime_error )
1496 {
1497 return this->GetModemControlLine( TIOCM_DSR ) ;
1498 }
1499
1500 inline
1501 void
1502 SerialPort::SerialPortImpl::HandlePosixSignal( int signalNumber )
1503 {
1504 //
1505 // We only want to deal with SIGIO signals here.
1506 //
1507 if ( SIGIO != signalNumber )
1508 {
1509 return ;
1510 }
1511 //
1512 // Check if any data is available at the specified file
1513 // descriptor.
1514 //
1515 int num_of_bytes_available = 0 ;
1516 if ( ioctl( mFileDescriptor,
1517 FIONREAD,
1518 &num_of_bytes_available ) < 0 )
1519 {
1520 /*
1521 * Ignore any errors and return immediately.
1522 */
1523 return ;
1524 }
1525 //
1526 // If data is available, read all available data and shove
1527 // it into the corresponding input buffer.
1528 //
1529 for(int i=0; i<num_of_bytes_available; ++i)
1530 {
1531 unsigned char next_byte ;
1532 if ( read( mFileDescriptor,
1533 &next_byte,
1534 1 ) > 0 )
1535 {
1536 mInputBuffer.push( next_byte ) ;
1537 }
1538 else
1539 {
1540 break ;
1541 }
1542 }
1543 return ;
1544 }
1545
1546 inline
1547 void
1548 SerialPort::SerialPortImpl::SetModemControlLine( const int modemLine,
1549 const bool lineState )
1550 throw( SerialPort::NotOpen,
1551 std::runtime_error )
1552 {
1553 //
1554 // Make sure that the serial port is open.
1555 //
1556 if ( ! this->IsOpen() )
1557 {
1558 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1559 }
1560 //
1561 // :TODO: Check to make sure that modemLine is a valid value.
1562 //
1563 // Set or unset the specified bit according to the value of
1564 // lineState.
1565 //
1566 int ioctl_result = -1 ;
1567 if ( true == lineState )
1568 {
1569 int set_line_mask = modemLine ;
1570 ioctl_result = ioctl( mFileDescriptor,
1571 TIOCMBIS,
1572 &set_line_mask ) ;
1573 }
1574 else
1575 {
1576 int reset_line_mask = modemLine ;
1577 ioctl_result = ioctl( mFileDescriptor,
1578 TIOCMBIC,
1579 &reset_line_mask ) ;
1580 }
1581 //
1582 // Check for errors.
1583 //
1584 if ( -1 == ioctl_result )
1585 {
1586 throw std::runtime_error( strerror(errno) ) ;
1587 }
1588 return ;
1589 }
1590
1591 inline
1592 bool
1593 SerialPort::SerialPortImpl::GetModemControlLine( const int modemLine ) const
1594 throw( SerialPort::NotOpen,
1595 std::runtime_error )
1596 {
1597 //
1598 // Make sure that the serial port is open.
1599 //
1600 if ( ! this->IsOpen() )
1601 {
1602 throw SerialPort::NotOpen( ERR_MSG_PORT_NOT_OPEN ) ;
1603 }
1604 //
1605 // Use an ioctl() call to get the state of the line.
1606 //
1607 int serial_port_state = 0 ;
1608 if ( -1 == ioctl( mFileDescriptor,
1609 TIOCMGET,
1610 &serial_port_state ) )
1611 {
1612 throw std::runtime_error( strerror(errno) ) ;
1613 }
1614 //
1615 // :TODO: Verify that modemLine is a valid value.
1616 //
1617 return ( serial_port_state & modemLine ) ;
1618 }
1619
1620 namespace
1621 {
1622 const struct timeval
1623 operator-( const struct timeval& firstOperand,
1624 const struct timeval& secondOperand )
1625 {
1626 /*
1627 * This implementation may result in undefined behavior if the
1628 * platform uses unsigned values for storing tv_sec and tv_usec
1629 * members of struct timeval.
1630 */
1631 //
1632 // Number of microseconds in a second.
1633 //
1634 const int MICROSECONDS_PER_SECOND = 1000000 ;
1635 struct timeval result ;
1636 //
1637 // Take the difference of individual members of the two operands.
1638 //
1639 result.tv_sec = firstOperand.tv_sec - secondOperand.tv_sec ;
1640 result.tv_usec = firstOperand.tv_usec - secondOperand.tv_usec ;
1641 //
1642 // If abs(result.tv_usec) is larger than MICROSECONDS_PER_SECOND,
1643 // then increment/decrement result.tv_sec accordingly.
1644 //
1645 if ( abs( result.tv_usec ) > MICROSECONDS_PER_SECOND )
1646 {
1647 int num_of_seconds = (result.tv_usec / MICROSECONDS_PER_SECOND ) ;
1648 result.tv_sec += num_of_seconds ;
1649 result.tv_usec -= ( MICROSECONDS_PER_SECOND * num_of_seconds ) ;
1650 }
1651 return result ;
1652 }
1653 } ;

  ViewVC Help
Powered by ViewVC 1.1.20