f35e466211e560ef9d40a27d8a42bf2b100da990
[public/netxms.git] / src / libnetxms / serial.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2005-2014 Raden Solutions
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU Lesser General Public License as published
7 ** by the Free Software Foundation; either version 3 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 Lesser General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: serial.cpp
20 **
21 **/
22
23 #include "libnetxms.h"
24
25 #ifndef CRTSCTS
26 # ifdef CNEW_RTSCTS
27 # define CRTSCTS CNEW_RTSCTS
28 # else
29 # define CRTSCTS 0
30 # endif
31 #endif
32
33 #ifdef _NETWARE
34 #ifndef ECHOKE
35 #define ECHOKE 0
36 #endif
37 #ifndef ECHOCTL
38 #define ECHOCTL 0
39 #endif
40 #ifndef OPOST
41 #define OPOST 0
42 #endif
43 #ifndef ONLCR
44 #define ONLCR 0
45 #endif
46 #endif
47
48 /**
49 * Constructor
50 */
51 Serial::Serial()
52 {
53 m_nTimeout = 5000;
54 m_hPort = INVALID_HANDLE_VALUE;
55 m_pszPort = NULL;
56 m_nSpeed = 9600;
57 m_nDataBits = 8;
58 m_nParity = NOPARITY;
59 m_nStopBits = ONESTOPBIT;
60 m_nFlowControl = FLOW_NONE;
61 #ifndef _WIN32
62 memset(&m_originalSettings, 0, sizeof(m_originalSettings));
63 #endif
64 }
65
66 /**
67 * Destructor
68 */
69 Serial::~Serial()
70 {
71 close();
72 safe_free(m_pszPort);
73 }
74
75 /**
76 * Open serial device
77 */
78 bool Serial::open(const TCHAR *pszPort)
79 {
80 bool bRet = false;
81
82 close(); // In case if port already open
83 safe_free(m_pszPort);
84 m_pszPort = _tcsdup(pszPort);
85
86 #ifdef _WIN32
87 m_hPort = CreateFile(pszPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,
88 OPEN_EXISTING, 0, NULL);
89 if (m_hPort != INVALID_HANDLE_VALUE)
90 {
91 #else // UNIX
92 m_hPort = ::_topen(pszPort, O_RDWR | O_NOCTTY | O_NDELAY);
93 if (m_hPort != -1)
94 {
95 tcgetattr(m_hPort, &m_originalSettings);
96 #endif
97 set(38400, 8, NOPARITY, ONESTOPBIT, FLOW_NONE);
98 bRet = true;
99 }
100 return bRet;
101 }
102
103 /**
104 * Set communication parameters
105 */
106 bool Serial::set(int nSpeed, int nDataBits, int nParity, int nStopBits, int nFlowControl)
107 {
108 bool bRet = false;
109
110 m_nDataBits = nDataBits;
111 m_nSpeed = nSpeed;
112 m_nParity = nParity;
113 m_nStopBits = nStopBits;
114 m_nFlowControl = nFlowControl;
115
116 #ifdef _WIN32
117 DCB dcb;
118
119 dcb.DCBlength = sizeof(DCB);
120 if (GetCommState(m_hPort, &dcb))
121 {
122 dcb.BaudRate = nSpeed;
123 dcb.ByteSize = nDataBits;
124 dcb.Parity = nParity;
125 dcb.StopBits = nStopBits;
126 dcb.fDtrControl = DTR_CONTROL_ENABLE;
127 dcb.fBinary = TRUE;
128 dcb.fParity = FALSE;
129 dcb.fOutxCtsFlow = FALSE;
130 dcb.fOutxDsrFlow = FALSE;
131 dcb.fDsrSensitivity = FALSE;
132 dcb.fOutX = FALSE;
133 dcb.fInX = FALSE;
134 dcb.fRtsControl = RTS_CONTROL_ENABLE;
135 dcb.fAbortOnError = FALSE;
136
137 if (SetCommState(m_hPort, &dcb))
138 {
139 COMMTIMEOUTS ct;
140
141 memset(&ct, 0, sizeof(COMMTIMEOUTS));
142 ct.ReadTotalTimeoutConstant = m_nTimeout;
143 ct.WriteTotalTimeoutConstant = m_nTimeout;
144 SetCommTimeouts(m_hPort, &ct);
145 bRet = true;
146 }
147 }
148
149 #else /* UNIX */
150 struct termios newTio;
151
152 tcgetattr(m_hPort, &newTio);
153
154 newTio.c_cc[VMIN] = 1;
155 newTio.c_cc[VTIME] = m_nTimeout / 100; // convert to deciseconds (VTIME in 1/10sec)
156
157 newTio.c_cflag |= CLOCAL | CREAD;
158
159 int baud;
160 switch(nSpeed)
161 {
162 case 50: baud = B50; break;
163 case 75: baud = B75; break;
164 case 110: baud = B110; break;
165 case 134: baud = B134; break;
166 case 150: baud = B150; break;
167 case 200: baud = B200; break;
168 case 300: baud = B300; break;
169 case 600: baud = B600; break;
170 case 1200: baud = B1200; break;
171 case 1800: baud = B1800; break;
172 case 2400: baud = B2400; break;
173 case 4800: baud = B4800; break;
174 case 9600: baud = B9600; break;
175 case 19200: baud = B19200; break;
176 case 38400: baud = B38400; break;
177 #ifdef B57600
178 case 57600: baud = B57600; break;
179 #endif
180 #ifdef B115200
181 case 115200: baud = B115200; break;
182 #endif
183 #ifdef B230400
184 case 230400: baud = B230400; break;
185 #endif
186 #ifdef B460800
187 case 460800: baud = B460800; break;
188 #endif
189 #ifdef B500000
190 case 500000: baud = B500000; break;
191 #endif
192 #ifdef B576000
193 case 576000: baud = B576000; break;
194 #endif
195 #ifdef B921600
196 case 921600: baud = B921600; break;
197 #endif
198 default:
199 return false; // wrong/unsupported speed
200 }
201 #ifdef _NETWARE
202 cfsetispeed(&newTio, (speed_t)baud);
203 cfsetospeed(&newTio, (speed_t)baud);
204 #else
205 cfsetispeed(&newTio, baud);
206 cfsetospeed(&newTio, baud);
207 #endif
208
209 newTio.c_cflag &= ~(CSIZE);
210 switch(nDataBits)
211 {
212 case 5:
213 newTio.c_cflag |= CS5;
214 break;
215 case 6:
216 newTio.c_cflag |= CS6;
217 break;
218 case 7:
219 newTio.c_cflag |= CS7;
220 break;
221 case 8:
222 default:
223 newTio.c_cflag |= CS8;
224 break;
225 }
226
227 newTio.c_cflag &= ~(PARODD | PARENB);
228 switch(nParity)
229 {
230 case ODDPARITY:
231 newTio.c_cflag |= PARODD | PARENB;
232 break;
233 case EVENPARITY:
234 newTio.c_cflag |= PARENB;
235 break;
236 default:
237 break;
238 }
239
240 newTio.c_cflag &= ~(CSTOPB);
241 if (nStopBits != ONESTOPBIT)
242 {
243 newTio.c_cflag |= CSTOPB;
244 }
245
246 //newTio.c_cflag &= ~(CRTSCTS | IXON | IXOFF);
247 newTio.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL | ISIG | IEXTEN);
248 newTio.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);
249 newTio.c_iflag |= IGNBRK;
250 newTio.c_oflag &= ~(OPOST | ONLCR);
251
252 switch(nFlowControl)
253 {
254 case FLOW_HARDWARE:
255 newTio.c_cflag |= CRTSCTS;
256 break;
257 case FLOW_SOFTWARE:
258 newTio.c_iflag |= IXON | IXOFF;
259 break;
260 default:
261 break;
262 }
263
264 bRet = (tcsetattr(m_hPort, TCSANOW, &newTio) == 0);
265
266 #endif // _WIN32
267 return bRet;
268 }
269
270 /**
271 * Close serial port
272 */
273 void Serial::close()
274 {
275 if (m_hPort != INVALID_HANDLE_VALUE)
276 {
277 #ifdef _WIN32
278 //PurgeComm(m_hPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
279 CloseHandle(m_hPort);
280 #else // UNIX
281 tcsetattr(m_hPort, TCSANOW, &m_originalSettings);
282 ::close(m_hPort);
283 #endif // _WIN32s
284 m_hPort = INVALID_HANDLE_VALUE;
285 }
286 }
287
288 /**
289 * Set receive timeout (in milliseconds)
290 */
291 void Serial::setTimeout(int nTimeout)
292 {
293 #ifdef _WIN32
294 COMMTIMEOUTS ct;
295
296 m_nTimeout = nTimeout;
297 memset(&ct, 0, sizeof(COMMTIMEOUTS));
298 ct.ReadTotalTimeoutConstant = m_nTimeout;
299 ct.WriteTotalTimeoutConstant = m_nTimeout;
300
301 SetCommTimeouts(m_hPort, &ct);
302 #else
303 struct termios tio;
304
305 tcgetattr(m_hPort, &tio);
306
307 m_nTimeout = nTimeout;
308 tio.c_cc[VTIME] = nTimeout / 100; // convert to deciseconds (VTIME in 1/10sec)
309
310 tcsetattr(m_hPort, TCSANOW, &tio);
311 #endif // WIN32
312 }
313
314 /**
315 * Restart port
316 */
317 bool Serial::restart()
318 {
319 if (m_pszPort == NULL)
320 return false;
321
322 close();
323 ThreadSleepMs(500);
324
325 TCHAR *temp = m_pszPort;
326 m_pszPort = NULL; // to prevent desctruction by open()
327 int speed = m_nSpeed;
328 int dataBits = m_nDataBits;
329 int parity = m_nParity;
330 int stopBits = m_nStopBits;
331 int flowControl = m_nFlowControl;
332 if (open(temp))
333 {
334 if (set(speed, dataBits, parity, stopBits, flowControl))
335 {
336 setTimeout(m_nTimeout);
337 free(temp);
338 return true;
339 }
340 }
341 free(temp);
342 return false;
343 }
344
345 /**
346 * Read character(s) from port
347 */
348 int Serial::read(char *pBuff, int nSize)
349 {
350 int nRet;
351
352 memset(pBuff, 0, nSize);
353 if (m_hPort == INVALID_HANDLE_VALUE)
354 return -1;
355
356 #ifdef _WIN32
357 DWORD nDone;
358
359 if (ReadFile(m_hPort, pBuff, nSize, &nDone, NULL) != 0)
360 {
361 nRet = (int)nDone;
362 }
363 else
364 {
365 nRet = -1;
366 }
367
368 #else // UNIX
369 fd_set rdfs;
370 struct timeval tv;
371
372 FD_ZERO(&rdfs);
373 FD_SET(m_hPort, &rdfs);
374 tv.tv_sec = m_nTimeout / 1000; // Timeout is in milliseconds
375 tv.tv_usec = 0;
376 nRet = select(m_hPort + 1, &rdfs, NULL, NULL, &tv);
377 if (nRet > 0)
378 {
379 do
380 {
381 nRet = ::read(m_hPort, pBuff, nSize);
382 }
383 while((nRet == -1) && (errno == EAGAIN));
384 }
385 else
386 {
387 nRet = -1; // Timeout is an error
388 }
389
390 #endif // _WIN32
391
392 return nRet;
393 }
394
395 /**
396 * Read character(s) from port
397 */
398 int Serial::readAll(char *pBuff, int nSize)
399 {
400 memset(pBuff, 0, nSize);
401 if (m_hPort == INVALID_HANDLE_VALUE)
402 return -1;
403
404 int nRet = -1;
405
406 #ifdef _WIN32
407 DWORD dwBytes;
408
409 if (ReadFile(m_hPort, pBuff, nSize, &dwBytes, NULL))
410 {
411 nRet = (int)dwBytes;
412 }
413 #else // UNIX
414 fd_set rdfs;
415 struct timeval tv;
416 int got, offset;
417
418 offset = 0;
419
420 while (offset < nSize)
421 {
422 FD_ZERO(&rdfs);
423 FD_SET(m_hPort, &rdfs);
424 tv.tv_sec = m_nTimeout / 1000; // timeout is in milliseconds
425 tv.tv_usec = 0;
426 nRet = select(m_hPort + 1, &rdfs, NULL, NULL, &tv);
427 if (nRet > 0)
428 {
429 got = ::read(m_hPort, pBuff + offset, nSize - offset);
430 if (got >= 0)
431 {
432 offset += got;
433 nRet = offset;
434 }
435 else
436 {
437 nRet = -1;
438 break;
439 }
440 }
441 else
442 {
443 if (offset == 0) // got no data
444 {
445 nRet = -1;
446 }
447 break;
448 }
449 }
450
451 #endif // _WIN32
452
453 return nRet;
454 }
455
456 /**
457 * Read data up to one of given marks
458 */
459 int Serial::readToMark(char *buffer, int size, const char **marks, char **occurence)
460 {
461 char *curr = buffer;
462 int sizeLeft = size - 1;
463 int totalBytesRead = 0;
464 *occurence = NULL;
465
466 while(sizeLeft > 0)
467 {
468 int bytesRead = read(curr, sizeLeft);
469 if (bytesRead <= 0)
470 return bytesRead;
471
472 totalBytesRead += bytesRead;
473 curr += bytesRead;
474 sizeLeft -= bytesRead;
475 *curr = 0;
476 for(int i = 0; marks[i] != NULL; i++)
477 {
478 char *mark = strstr(buffer, marks[i]);
479 if (mark != NULL)
480 {
481 *occurence = mark;
482 return totalBytesRead;
483 }
484 }
485 }
486 return totalBytesRead;
487 }
488
489 /**
490 * Write character(s) to port
491 */
492 bool Serial::write(const char *pBuff, int nSize)
493 {
494 bool bRet = false;
495
496 if (m_hPort == INVALID_HANDLE_VALUE)
497 return false;
498
499 ThreadSleepMs(100);
500
501 #ifdef _WIN32
502
503 DWORD nDone;
504 if (WriteFile(m_hPort, pBuff, nSize, &nDone, NULL) != 0)
505 {
506 if (nDone == (DWORD)nSize)
507 {
508 bRet = true;
509 }
510 }
511 else
512 {
513 restart();
514 }
515
516 #else // UNIX
517
518 if (::write(m_hPort, (char *)pBuff, nSize) == nSize)
519 {
520 bRet = true;
521 }
522 else
523 {
524 restart();
525 }
526
527 #endif // _WIN32
528
529 return bRet;
530 }
531
532 /**
533 * Flush buffer
534 */
535 void Serial::flush()
536 {
537 #ifdef _WIN32
538
539 FlushFileBuffers(m_hPort);
540
541 #else // UNIX
542
543 #ifndef _NETWARE
544 tcflush(m_hPort, TCIOFLUSH);
545 #endif
546
547 #endif // _WIN32
548 }