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 |
unsigned 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(unsigned 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 |
} ; |