3fb6073c0f48bc7f413d4b3d0ffbc063b8cd2195
[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 #ifndef _WIN32
57 memset(&m_originalSettings, 0, sizeof(m_originalSettings));
58 #endif
59 }
60
61 /**
62 * Destructor
63 */
64 Serial::~Serial()
65 {
66 close();
67 safe_free(m_pszPort);
68 }
69
70 /**
71 * Open serial device
72 */
73 bool Serial::open(const TCHAR *pszPort)
74 {
75 bool bRet = false;
76
77 close(); // In case if port already open
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
87 m_hPort = ::_topen(pszPort, O_RDWR | O_NOCTTY | O_NDELAY);
88 if (m_hPort != -1)
89 {
90 tcgetattr(m_hPort, &m_originalSettings);
91 #endif
92 set(38400, 8, NOPARITY, ONESTOPBIT, FLOW_NONE);
93 bRet = true;
94 }
95 return bRet;
96 }
97
98 /**
99 * Set communication parameters
100 */
101 bool Serial::set(int nSpeed, int nDataBits, int nParity, int nStopBits, int nFlowControl)
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;
150 newTio.c_cc[VTIME] = m_nTimeout / 100; // convert to deciseconds (VTIME in 1/10sec)
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;
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
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
259 bRet = (tcsetattr(m_hPort, TCSANOW, &newTio) == 0);
260
261 #endif // _WIN32
262 return bRet;
263 }
264
265 /**
266 * Close serial port
267 */
268 void Serial::close()
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);
277 ::close(m_hPort);
278 #endif // _WIN32s
279 m_hPort = INVALID_HANDLE_VALUE;
280 }
281 }
282
283 /**
284 * Set receive timeout (in milliseconds)
285 */
286 void Serial::setTimeout(int nTimeout)
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);
301
302 m_nTimeout = nTimeout;
303 tio.c_cc[VTIME] = nTimeout / 100; // convert to deciseconds (VTIME in 1/10sec)
304
305 tcsetattr(m_hPort, TCSANOW, &tio);
306 #endif // WIN32
307 }
308
309 /**
310 * Restart port
311 */
312 bool Serial::restart()
313 {
314 if (m_pszPort == NULL)
315 return false;
316
317 close();
318 ThreadSleepMs(500);
319
320 TCHAR *temp = m_pszPort;
321 m_pszPort = NULL; // to prevent desctruction by open()
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;
327 if (open(temp))
328 {
329 if (set(speed, dataBits, parity, stopBits, flowControl))
330 {
331 setTimeout(m_nTimeout);
332 free(temp);
333 return true;
334 }
335 }
336 free(temp);
337 return false;
338 }
339
340 /**
341 * Read character(s) from port
342 */
343 int Serial::read(char *pBuff, int nSize)
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);
369 tv.tv_sec = m_nTimeout / 1000; // Timeout is in milliseconds
370 tv.tv_usec = 0;
371 nRet = select(m_hPort + 1, &rdfs, NULL, NULL, &tv);
372 if (nRet > 0)
373 nRet = ::read(m_hPort, pBuff, nSize);
374 else
375 nRet = -1; // Timeout is an error
376
377 #endif // _WIN32
378
379 return nRet;
380 }
381
382 /**
383 * Read character(s) from port
384 */
385 int Serial::readAll(char *pBuff, int nSize)
386 {
387 memset(pBuff, 0, nSize);
388 if (m_hPort == INVALID_HANDLE_VALUE)
389 return -1;
390
391 int nRet = -1;
392
393 #ifdef _WIN32
394 DWORD dwBytes;
395
396 if (ReadFile(m_hPort, pBuff, nSize, &dwBytes, NULL))
397 {
398 nRet = (int)dwBytes;
399 }
400 #else // UNIX
401 fd_set rdfs;
402 struct timeval tv;
403 int got, offset;
404
405 offset = 0;
406
407 while (offset < nSize)
408 {
409 FD_ZERO(&rdfs);
410 FD_SET(m_hPort, &rdfs);
411 tv.tv_sec = m_nTimeout / 1000; // timeout is in milliseconds
412 tv.tv_usec = 0;
413 nRet = select(m_hPort + 1, &rdfs, NULL, NULL, &tv);
414 if (nRet > 0)
415 {
416 got = ::read(m_hPort, pBuff + offset, nSize - offset);
417 if (got >= 0)
418 {
419 offset += got;
420 nRet = offset;
421 }
422 else
423 {
424 nRet = -1;
425 break;
426 }
427 }
428 else
429 {
430 if (offset == 0) // got no data
431 {
432 nRet = -1;
433 }
434 break;
435 }
436 }
437
438 #endif // _WIN32
439
440 return nRet;
441 }
442
443 /**
444 * Read data up to one of given marks
445 */
446 int Serial::readToMark(char *buffer, int size, const char **marks, char **occurence)
447 {
448 char *curr = buffer;
449 int sizeLeft = size - 1;
450 int totalBytesRead = 0;
451 *occurence = NULL;
452
453 while(sizeLeft > 0)
454 {
455 int bytesRead = read(curr, sizeLeft);
456 if (bytesRead <= 0)
457 return bytesRead;
458
459 totalBytesRead += bytesRead;
460 curr += bytesRead;
461 sizeLeft -= bytesRead;
462 *curr = 0;
463 for(int i = 0; marks[i] != NULL; i++)
464 {
465 char *mark = strstr(buffer, marks[i]);
466 if (mark != NULL)
467 {
468 *occurence = mark;
469 return totalBytesRead;
470 }
471 }
472 }
473 return totalBytesRead;
474 }
475
476 /**
477 * Write character(s) to port
478 */
479 bool Serial::write(const char *pBuff, int nSize)
480 {
481 bool bRet = false;
482
483 if (m_hPort == INVALID_HANDLE_VALUE)
484 return false;
485
486 ThreadSleepMs(100);
487
488 #ifdef _WIN32
489
490 DWORD nDone;
491 if (WriteFile(m_hPort, pBuff, nSize, &nDone, NULL) != 0)
492 {
493 if (nDone == (DWORD)nSize)
494 {
495 bRet = true;
496 }
497 }
498 else
499 {
500 restart();
501 }
502
503 #else // UNIX
504
505 if (::write(m_hPort, (char *)pBuff, nSize) == nSize)
506 {
507 bRet = true;
508 }
509 else
510 {
511 restart();
512 }
513
514 #endif // _WIN32
515
516 return bRet;
517 }
518
519 /**
520 * Flush buffer
521 */
522 void Serial::flush()
523 {
524 #ifdef _WIN32
525
526 FlushFileBuffers(m_hPort);
527
528 #else // UNIX
529
530 #ifndef _NETWARE
531 tcflush(m_hPort, TCIOFLUSH);
532 #endif
533
534 #endif // _WIN32
535 }