serial.cpp
1 /***************************************************************************
2  Copyright (C) 2002-2015 Kentaro Kitagawa
3  kitagawa@phys.s.u-tokyo.ac.jp
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  You should have received a copy of the GNU Library General
11  Public License and a list of authors along with this program;
12  see the files COPYING and AUTHORS.
13 ***************************************************************************/
14 #include "serial.h"
15 
16 #define TTY_WAIT 1 //ms
17 #define MIN_BUFFER_SIZE 256
18 
19 #ifdef SERIAL_POSIX
20 #include <termios.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 
25 XSerialPort::XSerialPort(XCharInterface *interface)
26  : XPort(interface), m_scifd(-1) {
27 
28 }
29 XSerialPort::~XSerialPort() {
30  if(m_scifd >= 0) close(m_scifd);
31 }
32 #endif /*SERIAL_POSIX*/
33 
34 
35 #ifdef SERIAL_WIN32
36 #include <windows.h>
37 #undef PARITY_EVEN
38 #undef PARITY_ODD
39 #undef PARITY_NONE
40 
41 XSerialPort::XSerialPort(XCharInterface *intf)
42  : XPort(intf), m_handle(INVALID_HANDLE_VALUE) {
43  C_ASSERT(sizeof(void *) == sizeof(HANDLE));
44 }
45 XSerialPort::~XSerialPort() {
46  if(m_handle != INVALID_HANDLE_VALUE)
47  CloseHandle(m_handle);
48 }
49 #endif /*SERIAL_WIN32*/
50 
51 void
52 XSerialPort::open(const XCharInterface *pInterface) throw (XInterface::XCommError &) {
53  Snapshot shot( *pInterface);
54 
55 #ifdef SERIAL_POSIX
56  struct termios ttyios;
57  speed_t baudrate;
58  if((m_scifd = ::open(QString(shot[ *pInterface->port()].to_str()).toLocal8Bit().data(),
59  O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK)) == -1) {
60  throw XInterface::XCommError(i18n("tty open failed"), __FILE__, __LINE__);
61  }
62 
63  tcsetpgrp(m_scifd, getpgrp());
64 
65  bzero( &ttyios, sizeof(ttyios));
66 // tcgetattr(m_scifd, &ttyios);
67 
68  switch(static_cast<int>(pInterface->serialBaudRate())) {
69  case 2400: baudrate = B2400; break;
70  case 4800: baudrate = B4800; break;
71  case 9600: baudrate = B9600; break;
72  case 19200: baudrate = B19200; break;
73  case 38400: baudrate = B38400; break;
74  case 57600: baudrate = B57600; break;
75  case 115200: baudrate = B115200; break;
76  case 230400: baudrate = B230400; break;
77  default:
78  throw XInterface::XCommError(i18n("Invalid Baudrate"), __FILE__, __LINE__);
79  }
80 
81  cfsetispeed( &ttyios, baudrate);
82  cfsetospeed( &ttyios, baudrate);
83  cfmakeraw( &ttyios);
84  ttyios.c_cflag &= ~(PARENB | CSIZE);
85  if(pInterface->serialParity() == XCharInterface::PARITY_EVEN)
86  ttyios.c_cflag |= PARENB;
87  if(pInterface->serialParity() == XCharInterface::PARITY_ODD)
88  ttyios.c_cflag |= PARENB | PARODD;
89  if(pInterface->serial7Bits())
90  ttyios.c_cflag |= CS7;
91  else
92  ttyios.c_cflag |= CS8;
93  ttyios.c_cflag |= HUPCL | CLOCAL | CREAD;
94  if(pInterface->serialStopBits() == 2)
95  ttyios.c_cflag |= CSTOPB;
96  ttyios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //non-canonical mode
97  ttyios.c_iflag |= IGNBRK;
98  if(pInterface->serialParity() == XCharInterface::PARITY_NONE)
99  ttyios.c_iflag |= IGNPAR;
100  ttyios.c_cc[VMIN] = 0; //no min. size
101  ttyios.c_cc[VTIME] = 30; //3sec time-out
102  if(tcsetattr(m_scifd, TCSAFLUSH, &ttyios ) < 0)
103  throw XInterface::XCommError(i18n("stty failed"), __FILE__, __LINE__);
104 
105  if(fcntl(m_scifd, F_SETFL, (~O_NONBLOCK) & fcntl(m_scifd, F_GETFL)) == - 1) {
106  throw XInterface::XCommError(i18n("tty open failed"), __FILE__, __LINE__);
107  }
108 #endif /*SERIAL_POSIX*/
109 
110 #ifdef SERIAL_WIN32
111  m_handle = CreateFileA( QString(shot[ *pInterface->port()].to_str()).toLocal8Bit().data(),
112  GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
113  if (m_handle == INVALID_HANDLE_VALUE)
114  throw XInterface::XCommError(i18n("tty open failed"), __FILE__, __LINE__);
115 
116  DCB dcb;
117  GetCommState(m_handle, &dcb); //loading the original state
118  dcb.BaudRate = static_cast<int>(pInterface->serialBaudRate());
119  dcb.ByteSize = pInterface->serial7Bits() ? 7 : 8;
120  switch((int)pInterface->serialParity()) {
121  case XCharInterface::PARITY_EVEN:
122  dcb.fParity = TRUE;
123  dcb.Parity = EVENPARITY;
124  break;
125  case XCharInterface::PARITY_ODD:
126  dcb.fParity = TRUE;
127  dcb.Parity = ODDPARITY;
128  break;
129  default:
130  case XCharInterface::PARITY_NONE:
131  dcb.fParity = FALSE;
132  dcb.Parity = NOPARITY;
133  break;
134  }
135  dcb.StopBits = (pInterface->serialStopBits() == 2) ? TWOSTOPBITS : ONESTOPBIT;
136  if( !SetCommState(m_handle, &dcb))
137  throw XInterface::XCommError(i18n("tty SetCommState failed"), __FILE__, __LINE__);
138 
139  COMMTIMEOUTS cto;
140  GetCommTimeouts(m_handle, &cto); //loading the original timeout settings.
141  cto.ReadIntervalTimeout = 0;
142  cto.ReadTotalTimeoutMultiplier = 0;
143  cto.ReadTotalTimeoutConstant = 3000;
144  cto.WriteTotalTimeoutMultiplier = 0;
145  cto.WriteTotalTimeoutConstant = 3000;
146  if( !SetCommTimeouts(m_handle, &cto))
147  throw XInterface::XCommError(i18n("tty SetCommTimeouts failed"), __FILE__, __LINE__);
148 #endif /*SERIAL_WIN32*/
149 
150  m_serialFlushBeforeWrite = pInterface->serialFlushBeforeWrite();
151  m_serialHasEchoBack = pInterface->serialHasEchoBack();
152 
153  fprintf(stderr, "Serial port opened w/ baudrate=%d\n", (int)pInterface->serialBaudRate());
154 }
155 void
156 XSerialPort::send(const char *str) throw (XInterface::XCommError &) {
157  XString buf(str);
158  buf += eos();
159  if(m_serialHasEchoBack) {
160  this->write(str, strlen(str)); //every char should wait for echo back.
161  this->write(buf.c_str() + strlen(str), buf.length() - strlen(str)); //EOS
162  this->receive(); //wait for EOS.
163  }
164  else {
165  this->write(buf.c_str(), buf.length());
166  }
167 }
168 void
169 XSerialPort::write(const char *sendbuf, int size) throw (XInterface::XCommError &) {
170  if(m_serialHasEchoBack && (size >= 2) && isprint(sendbuf[0])) {
171  for(int cnt = 0; cnt < size; ++cnt) {
172  //sends 1 char.
173 #ifdef SERIAL_POSIX
174  write(sendbuf + cnt, 1);
175 #endif
176 #ifdef SERIAL_WIN32
177  DWORD wcnt;
178  WriteFile(m_handle, sendbuf + cnt, 1, &wcnt, NULL);
179 #endif
180  //waits for echo back.
181  for(;;) {
182  receive(1);
183  if(buffer()[0] == sendbuf[cnt])
184  break;
185  if(isspace(buffer()[0]))
186  continue; //ignores spaces.
188  formatString("inconsistent echo back %c against %c", buffer()[0], sendbuf[cnt]).c_str(),
189  __FILE__, __LINE__);
190  }
191  }
192  return;
193  }
194 
195  if(m_serialFlushBeforeWrite) {
196 #ifdef SERIAL_POSIX
197  for (;;) {
198  int ret = tcflush(m_scifd, TCIFLUSH);
199  if(ret < 0) {
200  if(errno == EINTR) {
201  dbgPrint("Serial, EINTR, try to continue.");
202  continue;
203  }
204  throw XInterface::XCommError(i18n("tciflush error."), __FILE__, __LINE__);
205  }
206  break;
207  }
208 #endif
209 #ifdef SERIAL_WIN32
210  if( !PurgeComm(m_handle, PURGE_RXCLEAR)) {
211  throw XInterface::XCommError(i18n("Serial PurgeComm error"), __FILE__, __LINE__);
212  }
213 #endif
214  }
215 
216  msecsleep(TTY_WAIT);
217 
218  int wlen = 0;
219  do {
220 #ifdef SERIAL_POSIX
221  int ret = ::write(m_scifd, sendbuf, size - wlen);
222  if(ret < 0) {
223  if(errno == EINTR) {
224  dbgPrint("Serial, EINTR, try to continue.");
225  continue;
226  }
227  else {
228  throw XInterface::XCommError(i18n("write error"), __FILE__, __LINE__);
229  }
230  }
231 #endif
232 #ifdef SERIAL_WIN32
233  DWORD ret;
234  WriteFile(m_handle, sendbuf, size - wlen, &ret, NULL);
235  if( !ret)
236  throw XInterface::XCommError(i18n("write error"), __FILE__, __LINE__);
237 #endif
238  wlen += ret;
239  sendbuf += ret;
240  } while (wlen < size);
241 }
242 void
243 XSerialPort::receive() throw (XInterface::XCommError &) {
244 // for(;;) {
245 // if(tcdrain(m_scifd) < 0) {
246 // dbgPrint("tcdrain failed, continue.");
247 // continue;
248 // }
249 // break;
250 // }
251 
252  msecsleep(TTY_WAIT);
253 
254  buffer().resize(MIN_BUFFER_SIZE);
255 
256  const char *ceos = eos().c_str();
257  unsigned int eos_len = strlen(ceos);
258  unsigned int len = 0;
259  for(;;) {
260  if(buffer().size() <= len + 1)
261  buffer().resize(len + MIN_BUFFER_SIZE);
262 #ifdef SERIAL_POSIX
263  int rlen = ::read(m_scifd, &buffer().at(len), 1);
264  if(rlen < 0) {
265  if(errno == EINTR) {
266  dbgPrint("Serial, EINTR, try to continue.");
267  continue;
268  }
269  else
270  throw XInterface::XCommError(i18n("read error"), __FILE__, __LINE__);
271  }
272 #endif
273 #ifdef SERIAL_WIN32
274  DWORD rlen;
275  ReadFile(m_handle, &buffer().at(len), 1, &rlen, NULL);
276 #endif
277  if(rlen == 0) {
278  buffer().at(len) = '\0';
279  throw XInterface::XCommError(i18n("read time-out, buf=;") + &buffer().at(0), __FILE__, __LINE__);
280  }
281  len += rlen;
282  if(len >= eos_len) {
283  if( !strncmp(&buffer().at(len - eos_len), ceos, eos_len)) {
284  break;
285  }
286  }
287  }
288 
289  buffer().resize(len + 1);
290  buffer().at(len) = '\0';
291 }
292 void
293 XSerialPort::receive(unsigned int length) throw (XInterface::XCommError &) {
294 // for(;;) {
295 // if(tcdrain(m_scifd) < 0) {
296 // dbgPrint("tcdrain failed, continue.");
297 // continue;
298 // }
299 // break;
300 // }
301 
302  msecsleep(TTY_WAIT);
303 
304  buffer().resize(length);
305  unsigned int len = 0;
306 
307  while(len < length) {
308 #ifdef SERIAL_POSIX
309  int rlen = ::read(m_scifd, &buffer().at(len), 1);
310  if(rlen < 0) {
311  if(errno == EINTR) {
312  dbgPrint("Serial, EINTR, try to continue.");
313  continue;
314  }
315  else
316  throw XInterface::XCommError(i18n("read error"), __FILE__, __LINE__);
317  }
318 #endif
319 #ifdef SERIAL_WIN32
320  DWORD rlen;
321  ReadFile(m_handle, &buffer().at(len), 1, &rlen, NULL);
322 #endif
323  if(rlen == 0)
324  throw XInterface::XCommError(i18n("read time-out"), __FILE__, __LINE__);
325  len += rlen;
326  }
327 }
328 
329 

Generated for KAME4 by  doxygen 1.8.3