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