direcs  2012-09-30
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
direcsSerial.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  * Copyright (C) Markus Knapp *
3  * www.direcs.de *
4  * *
5  * This file is part of direcs. *
6  * *
7  * direcs is free software: you can redistribute it and/or modify it *
8  * under the terms of the GNU General Public License as published *
9  * by the Free Software Foundation, version 3 of the License. *
10  * *
11  * direcs is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License *
17  * along with direcs. If not, see <http://www.gnu.org/licenses/>. *
18  * *
19  *************************************************************************/
20 
21 #include "direcsSerial.h"
22 
23 
25 {
26  // get the name of this class (this is for debugging messages)
27  className = this->staticMetaObject.className();
28 
29  mDev_fd = -1; // the file descriptor !!
30 }
31 
32 
34 {
35 }
36 
37 
38 int DirecsSerial::openPort(char *dev_name, int baudrate)
39 {
40  struct termios options;
41  int spd = -1;
42  int newbaud = 0;
43 
44 
45  // mDev_fd = open(dev_name, O_RDWR | O_NOCTTY, 0); // 2010-05-23: not needed for Atmel and SICK laser S300!
46  mDev_fd = open(dev_name, O_RDWR | O_NOCTTY | O_NONBLOCK);
47 
48  // Note that open() follows POSIX semantics: multiple open() calls to
49  // the same file will succeed unless the TIOCEXCL ioctl is issued.
50  // This will prevent additional opens except by root-owned processes.
51  // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
52  if (ioctl(mDev_fd, TIOCEXCL) == -1)
53  {
54  emit message(QString("<font color=\"#FF0000\">ERROR %1 setting TIOCEXCL on serial device:<br>%2 in %3.</font>").arg(errno).arg(strerror(errno)).arg(className));
55  emit message(QString("<font color=\"#FF0000\">Serial port already opened?</font>"));
56  return -1;
57  }
58 
59  // Now that the device is open, clear the O_NONBLOCK flag so subsequent I/O will block.
60  // See fcntl(2) ("man 2 fcntl") for details.
61  if (fcntl(mDev_fd, F_SETFL, 0) == -1)
62  {
63  emit message(QString("<font color=\"#FF0000\">ERROR %1 clearing O_NONBLOCK on serial device:<br>%2 in %3.</font>").arg(errno).arg(strerror(errno)).arg(className));
64  return -1;
65  }
66 
67  if (mDev_fd < 0)
68  {
69  emit message(QString("<font color=\"#FF0000\">ERROR %1 opening serial device:<br>%2 in %3.</font>").arg(errno).arg(strerror(errno)).arg(className));
70  return errno;
71  }
72 
73 
74  // Get current port settings
75  tcgetattr(mDev_fd, &options);
76 
77  // this setting is needed for Mac OS! But works under Linux, too!
78  options.c_cflag |= CLOCAL;
79 
80  // 8N1
81  options.c_cflag &= ~PARENB; // no parity bit
82  options.c_cflag &= ~CSTOPB; // 1 stop bit
83  options.c_cflag &= ~CSIZE; //
84  options.c_cflag |= CS8; // 8 data bit
85 
86  // Disable hardware flow control:
87  options.c_cflag &= ~CRTSCTS;
88 
89  // options.c_lflag = 0; // DISABLED! Using cfmakeraw instead below! Now we are fine under Linux with the laser scanner S300 which uses this method, too! (2.4.2011)
90 
91  options.c_cc[VTIME] = 0; // inter-character timer unused
92  options.c_cc[VMIN] = 0; // blocking read until 0 chars received
93 
94  // this part is originally from setparms:
95  newbaud = (baudrate/100);
96 
97  switch(newbaud)
98  {
99  case 0:
100  #ifdef B0
101  spd = B0; break;
102  #else
103  spd = 0; break;
104  #endif
105  case 3:
106  spd = B300; break;
107  case 6:
108  spd = B600; break;
109  case 12:
110  spd = B1200; break;
111  case 24:
112  spd = B2400; break;
113  case 48:
114  spd = B4800; break;
115  case 96:
116  spd = B9600; break;
117  #ifdef B19200
118  case 192:
119  spd = B19200; break;
120  #else
121  #ifdef EXTA
122  case 192:
123  spd = EXTA; break;
124  #else
125  case 192:
126  spd = B9600; break;
127  #endif
128  #endif
129  #ifdef B38400
130  case 384:
131  spd = B38400; break;
132  #else
133  #ifdef EXTB
134  case 384:
135  spd = EXTB; break;
136  #else
137  case 384:
138  spd = B9600; break;
139  #endif
140  #endif
141  #ifdef B57600
142  case 576:
143  spd = B57600; break;
144  #endif
145  #ifdef B115200
146  case 1152:
147  spd = B115200; break;
148  #endif
149  #ifdef B500000
150  case 5000:
151  spd = B500000; break;
152  #endif
153  }
154 
155  // set speed (input and output)
156  if(spd != -1)
157  {
158  if (cfsetispeed(&options, (speed_t) spd) != 0)
159  {
160  emit message(QString("<font color=\"#FF0000\">ERROR setting serial port input speed at DirecsSerial::openPort!</font>"));
161  return -1;
162  }
163 
164  if (cfsetospeed(&options, (speed_t) spd) != 0)
165  {
166  emit message(QString("<font color=\"#FF0000\">ERROR setting serial port output speed at DirecsSerial::openPort!</font>"));
167  return -1;
168  }
169  }
170  else
171  {
172  emit message(QString("<font color=\"#FF0000\">ERROR: Wrong value for speed parameter at DirecsSerial::openPort!</font>"));
173  return -1;
174  }
175 
176  // Flushes all pending I/O to the serial port. This clears only the read buffer!
177  if (tcflush(mDev_fd, TCIFLUSH) != 0)
178  {
179  emit message(QString("<font color=\"#FF0000\">ERROR fluhsing serial input buffer at DirecsSerial::openPort!</font>"));
180  return -1;
181  }
182 
184  // Added on 2.4.2011 for the usage of the laser scanner S300 under Linux. Resolved the "resource unavailable" error and works under Mac OS X 10.6, too.
185  cfmakeraw(&options);
186 
187  // Cause the new options to take effect immediately.
188  if (tcsetattr(mDev_fd, TCSANOW, &options) != 0)
189  {
190  emit message(QString("<font color=\"#FF0000\">ERROR setting serial port attributes at DirecsSerial::openPort!</font>"));
191  return -1;
192  }
193 
194  emit message("Serial device openend.");
195  return (mDev_fd);
196 }
197 
198 
200 {
205  return tcflush(mDev_fd, TCIFLUSH);
206 }
207 
208 
209 int DirecsSerial::writeData(unsigned char *c, QString callingClassName)
210 {
211  int n = write(mDev_fd, c, 1);
212 
213  if (n < 0)
214  {
215  emit message(QString("<font color=\"#FF0000\">ERROR '%1=%2' when writing to serial device at DirecsSerial::writeData called from %3.</font>").arg(errno).arg(strerror(errno)).arg(callingClassName));
216 // qDebug("Error %d writing to serial device: %s\n", errno, strerror(errno));
217  return errno;
218  }
219  else
220  {
221  //qDebug("%1 byte(s) written.", n);
222  }
223 
224  return n;
225 }
226 
227 
228 int DirecsSerial::readData(unsigned char *buf, int nChars, QString callingClassName)
229 {
230  //
231  // Original code from method readPort
232  // Only using the local member dev_fd, instead of serial ports from laser scanner struct
233  //
234  int amountRead = 0, bytes_read = 0;
235  struct timeval t;
236  fd_set set;
237  int returnValue;
238 
239 
240  while (nChars > 0)
241  {
242  // wait up to 0,25 seconds (250000 microseconds)
243  // Timeout is not changed by select(), and may be reused on subsequent calls, however it is good style to re-initialize it before each invocation of select().
244  t.tv_sec = 0;
245  t.tv_usec = READ_TIMEOUT_ATMEL; // 0,25 seconds
246 
247  // watch serial port to see when it has input
248  FD_ZERO(&set);
249  FD_SET(mDev_fd, &set);
250 
251  // is the serial port ready for reading?
252  returnValue = select(mDev_fd + 1, &set, NULL, NULL, &t);
253 
254  // check if timeout or an error occured
255  if (returnValue == -1)
256  {
257  emit message(QString("<font color=\"#FF0000\">ERROR '%1=%2' <br>when selecting serial device at DirecsSerial::readData called from %3.</font>").arg(errno).arg(strerror(errno)).arg(callingClassName));
258  return errno;
259  }
260  else
261  {
262  if (returnValue)
263  {
264  // data available now
265  }
266  else
267  {
268  // timeout
269  emit message(QString("<font color=\"#FF0000\">ERROR No data available within %1 ms when using select() on serial device at DirecsSerial::readData called from %2.</font>").arg(READ_TIMEOUT_ATMEL).arg(callingClassName));
270  emit message(QString("<font color=\"#FF0000\">ERROR %1: %2.</font>").arg(errno).arg(strerror(errno)));
271 
272  return errno;
273  }
274  }
275 
276  // read from the serial device
277  amountRead = read(mDev_fd, buf, nChars);
278 
279  if (amountRead < 0 && errno != EWOULDBLOCK)
280  {
281  emit message(QString("<font color=\"#FF0000\">ERROR '%1=%2' when using read() on serial device at DirecsSerial::readData called from %3.</font>").arg(errno).arg(strerror(errno)).arg(callingClassName));
282 // FIXME: was, wenn return 0 ?!?!?
283  return errno;
284  }
285  else
286  {
287  if(amountRead > 0)
288  {
289  bytes_read += amountRead;
290  nChars -= amountRead;
291  buf += amountRead;
292  }
293  }
294  }
295  return bytes_read;
296 }
297 
298 
300 {
301  return close(mDev_fd);
302 }