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