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