rs232-cygwin.cpp
Go to the documentation of this file.
1 //======================================================================
28 //======================================================================
29 
30 #include "sdhlibrary_settings.h"
31 
32 //----------------------------------------------------------------------
33 // System Includes - include with <>
34 //----------------------------------------------------------------------
35 
36 #include <fcntl.h>
37 #include <termios.h>
38 #include <stdio.h>
39 #if ! SDH_USE_VCC
40 # include <unistd.h>
41 #endif
42 #include <errno.h>
43 #include <sys/select.h>
44 #include <sys/ioctl.h>
45 
46 #include <iostream>
47 #include <exception>
48 #include <string>
49 #include <stdarg.h>
50 #include <cstring> // needed in gcc-4.3 for prototypes like strcmp according to http://gcc.gnu.org/gcc-4.3/porting_to.html
51 
52 //----------------------------------------------------------------------
53 // Project Includes - include with ""
54 //----------------------------------------------------------------------
55 
56 #include "rs232-cygwin.h"
57 #include "simpletime.h"
58 
59 //----------------------------------------------------------------------
60 // Defines, enums, unions, structs,
61 //----------------------------------------------------------------------
62 
72 #define SDH_RS232_CYGWIN_DEBUG 1
73 
78 #if SDH_RS232_CYGWIN_DEBUG
79 # define DBG( ... ) \
80  do { \
81  __VA_ARGS__; \
82  } while (0)
83 #else
84 # define DBG( ... )
85 #endif
86 
87 //----------------------------------------------------------------------
88 // Global variables
89 //----------------------------------------------------------------------
90 
91 
92 //----------------------------------------------------------------------
93 // Function and class member implementation (function definitions)
94 //----------------------------------------------------------------------
95 
96 using namespace std;
98 
100 char* StrDupNew( char* const s )
101 {
102  char* dup = new char[strlen( s ) + 1];
103  return strcpy( dup, s );
104 }
105 //----------------------------------------------------------------------
106 
107 cRS232::cRS232( int _port, unsigned long _baudrate, double _timeout, char const* _device_format_string ) :
108  // init base class and members
109  cSerialBase(),
110  port( _port ),
111  device_format_string( _device_format_string ),
112  baudrate( _baudrate ),
113  fd( -1 ),
114  status( 0 )
115  // io_set_old cannot be initialized until we have an fd
116 {
117  SetTimeout( _timeout );
118 }
119 //----------------------------------------------------------------------
120 
121 void cRS232::Open( void )
122 {
123  char device[ device_format_string.size()+5 ]; // initializer just to get just enough space
124 
125  sprintf( device, device_format_string.c_str(), port );
126  DBG( dbg << "Opening RS232 device '" << std::string(device) << "', baudrate: " << baudrate << "\n" );
127 
128  fd = open( device, O_RDWR | O_NOCTTY | O_NDELAY );
129 
130  if (fd <0)
131  throw new cRS232Exception( cMsg( "Could not open device \"%s\": %s",
132  device, GetLastErrorMessage() ) );
133 
134  termios io_set_new;
135  // get device-settings
136  if (tcgetattr( fd, &io_set_old ) < 0)
137  {
138  status = errno;
139  throw new cRS232Exception( cMsg( "Could not get attributes of device \"%s\": %s",
140  device, GetLastErrorMessage() ) );
141  }
142  else
143  status = 0;
144 
145  // copy settings from old settings
146  io_set_new=io_set_old;
147 
148  // declare new settings
149  // (according to http://www.easysw.com/~mike/serial/serial.html
150  // the bits should be modified from current settings, not just set
151  // to the desired values)
152 
153  // The c_cflag member controls the baud rate, number of data bits,
154  // parity, stop bits, and hardware flow control.
155  io_set_new.c_cflag |= CLOCAL; // set Local line - do not change "owner" of port
156  io_set_new.c_cflag |= HUPCL; // Hangup (drop DTR) on last close
157  io_set_new.c_cflag |= CREAD; // enable reader
158  io_set_new.c_cflag &= ~PARENB; // set 8N1
159  io_set_new.c_cflag &= ~CSTOPB;
160  io_set_new.c_cflag &= ~CSIZE;
161  io_set_new.c_cflag |= CS8;
162  io_set_new.c_cflag &= ~CRTSCTS; // disable hardware flow control
163  io_set_new.c_cflag &= ~CBAUD; // clear baudrate bits first
164  io_set_new.c_cflag |= BaudrateToBaudrateCode( baudrate ); // set requested baudrate bits
165 
166  // The c_oflag member contains output filtering options.
167  // Like the input modes, you can select processed or raw data output
168  io_set_new.c_oflag &= ~OPOST; // disable output processing
169 
170  // The input modes member c_iflag controls any input processing that is
171  // done to characters received on the port.
172  io_set_new.c_iflag &= ~INPCK; // disable parity checking
173  io_set_new.c_iflag |= IGNPAR; // ignore parity errors
174  io_set_new.c_iflag &= ~ISTRIP; // do NOT strip off parity bits
175  //io_set_new.c_iflag |= ISTRIP; // strip off parity bits (On Linux this will set the most significant bit to 0! On Cygwin this has no effect)
176  io_set_new.c_iflag &= ~(IXON | IXOFF | IXANY); // disable software flow control
177  io_set_new.c_iflag |= IGNBRK; // do Ignore break condition
178  io_set_new.c_iflag &= ~BRKINT; // do not send a SIGINT when a break condition is detected
179  io_set_new.c_iflag &= ~INLCR; // do not Map NL to CR
180  io_set_new.c_iflag &= ~IGNCR; // do not Ignore CR
181  io_set_new.c_iflag &= ~ICRNL; // do not Map CR to NL
182  io_set_new.c_iflag &= ~IUCLC; // do not Map uppercase to lowercase
183  io_set_new.c_iflag &= ~IMAXBEL; // do not Echo BEL on input line too long
184 
185  // The local modes member c_lflag controls how input characters are managed
186  // by the serial driver. In general you will configure the c_lflag member
187  // for canonical or raw input.
188  io_set_new.c_lflag &= ~ICANON; // disable canonical input, enable raw input
189  io_set_new.c_lflag &= ~ECHO; // disable echoing of input characters
190  io_set_new.c_lflag &= ~ECHOE; // disable echo erase character as BS-SP-BS
191  io_set_new.c_lflag &= ~ISIG; // disable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
192 
193  /*
194  * source: http://www.easysw.com/~mike/serial/serial.html
195  *
196  * VMIN specifies the minimum number of characters to read. If it is set
197  * to 0, then the VTIME value specifies the time to wait for every
198  * character read. Note that this does not mean that a read call for N
199  * bytes will wait for N characters to come in. Rather, the timeout will
200  * apply to the first character and the read call will return the number
201  * of characters immediately available (up to the number you request).
202  *
203  * If VMIN is non-zero, VTIME specifies the time to wait for the first
204  * character read. If a character is read within the time given, any read
205  * will block (wait) until all VMIN characters are read. That is, once the
206  * first character is read, the serial interface driver expects to receive
207  * an entire packet of characters (VMIN bytes total). If no character is
208  * read within the time allowed, then the call to read returns 0. This
209  * method allows you to tell the serial driver you need exactly N bytes
210  * and any read call will return 0 or N bytes. However, the timeout only
211  * applies to the first character read, so if for some reason the driver
212  * misses one character inside the N byte packet then the read call could
213  * block forever waiting for additional input characters.
214  *
215  * VTIME specifies the amount of time to wait for incoming characters in
216  * tenths of seconds. If VTIME is set to 0 (the default), reads will
217  * block (wait) indefinitely unless the NDELAY option is set on the port
218  * with open or fcntl.
219  */
220  io_set_new.c_cc[VMIN] = 1;
221  io_set_new.c_cc[VTIME]= 0;
222 
223  cfsetispeed(&io_set_new, BaudrateToBaudrateCode( baudrate ));
224  cfsetospeed(&io_set_new, BaudrateToBaudrateCode( baudrate ));
225 
226  // set new settings
227  if (tcsetattr(fd,TCSANOW,&io_set_new) < 0)
228  {
229  status=errno;
230  throw new cRS232Exception( cMsg( "Could not set attributes of device \"%s\": %s",
231  device, GetLastErrorMessage() ) );
232  }
233  else
234  status=0;
235 
236 }
237 //----------------------------------------------------------------------
238 
239 
240 bool cRS232::IsOpen( void )
241 throw()
242 {
243  return (fd>=0);
244 }
245 //----------------------------------------------------------------------
246 
247 
248 void cRS232::Close( void )
249 {
250  if ( fd < 0 )
251  throw new cRS232Exception( cMsg( "Could not close un-opened device" ) );
252 
253  close( fd );
254  fd = -1;
255 }
256 //----------------------------------------------------------------------
257 
258 tcflag_t cRS232::BaudrateToBaudrateCode( unsigned long baudrate )
259 {
260  switch (baudrate)
261  {
262 #ifdef B3000000
263  case 3000000: return B3000000;
264 #endif
265 #ifdef B2500000
266  case 2500000: return B2500000;
267 #endif
268 #ifdef B2000000
269  case 2000000: return B2000000;
270 #endif
271 #ifdef B1500000
272  case 1500000: return B1500000;
273 #endif
274 #ifdef B1152000
275  case 1152000: return B1152000;
276 #endif
277 #ifdef B1000000
278  case 1000000: return B1000000;
279 #endif
280 #ifdef B921600
281  case 921600: return B921600;
282 #endif
283 #ifdef B576000
284  case 576000: return B576000;
285 #endif
286 #ifdef B500000
287  case 500000: return B500000;
288 #endif
289 #ifdef B460800
290  case 460800: return B460800;
291 #endif
292 #ifdef B256000
293  case 256000: return B256000;
294 #endif
295 #ifdef B230400
296  case 230400: return B230400;
297 #endif
298  case 115200: return B115200;
299  case 57600: return B57600;
300  case 38400: return B38400;
301  case 19200: return B19200;
302  case 9600: return B9600;
303  case 4800: return B4800;
304  case 2400: return B2400;
305  case 1800: return B1800;
306  case 1200: return B1200;
307  case 600: return B600;
308  case 300: return B300;
309  case 200: return B200;
310  case 150: return B150;
311  case 134: return B134;
312  case 110: return B110;
313  case 75: return B75;
314  case 50: return B50;
315  }
316 
317  throw new cRS232Exception( cMsg( "Invalid baudrate %ld", baudrate ) );
318 }
319 //----------------------------------------------------------------------
320 
321 int cRS232::write( char const *ptr, int len )
322 {
323  if ( len == 0 )
324  len = strlen( ptr );
325 
326  int written = ::write( fd, ptr, len );
327  if (written < 0)
328  {
329  // error from write
332  throw new cRS232Exception( cMsg( "Error calling write(): %s", GetLastErrorMessage() ) );
333  }
334  DBG( dbg << "cRS232::write wrote " << len << "/" << written << " bytes (hex):" << cHexByteString( ptr, written ) << "\n" );
335 
336  return written;
337 }
338 //----------------------------------------------------------------------
339 
340 
341 ssize_t cRS232::Read( void *data, ssize_t size, long timeout_us, bool return_on_less_data )
342 {
343  if (fd < 0)
344  return status;
345 
346  fd_set fds;
347  int bytes_read = 0;
348  int bytes_read_inc;
349  int select_return;
350  char* buffer =(char*) data;
351  long max_time_us = timeout_us;
352  if ( max_time_us <= 0 )
353  max_time_us = 1;
354  cSimpleTime start_time;
355  timeval time_left;
356  long us_left;
357  timeval* timeout_p;
358 
359  status = 0;
360  // We wait max max_time_us
361  do
362  {
363  //---------------------
364  // Look for received data with select()
365 
366  // calculate time left (min 1 us, otherwise we will read nothing at all)
367  if ( max_time_us >= 0)
368  {
369  us_left = max_time_us - start_time.Elapsed_us();
370  time_left.tv_sec = us_left / 1000000;
371  time_left.tv_usec = us_left % 1000000;
372 
373  if (time_left.tv_sec <= 0 && time_left.tv_usec < 1 )
374  {
375  time_left.tv_sec = 0;
376  time_left.tv_usec = 1;
377  }
378 
379  timeout_p = &time_left;
380  }
381  else
382  timeout_p = NULL; // wait indefinitely
383 
384 
385 
386  // prepare select call:
387  FD_ZERO(&fds);
388  FD_SET(fd, &fds);
389 
390  // Watchout:
391  // The manpage states: "...On Linux, select() modifies timeout..."
392  // So create a copy before calling select():
393  struct timeval timeout_maybe_overwritten = *timeout_p;
394  select_return = select( fd+1, &fds, NULL, NULL, &timeout_maybe_overwritten );
395 
397  if ( select_return < 0 )
398  {
399  // error from select
402  throw new cRS232Exception( cMsg( "Error calling select(): %s", GetLastErrorMessage() ) );
403  }
404  else if (select_return > 0)
405  {
406  // select says something is available for reading
407  if (return_on_less_data)
408  {
409  // we can return on less, so just read what is available
410  bytes_read_inc = read( fd, buffer + bytes_read, size - bytes_read );
411 
412 
413  if (bytes_read_inc < 0)
414  {
415  // error from read
418  throw new cRS232Exception( cMsg( "Error calling read(): %s", GetLastErrorMessage() ) );
419  }
420  DBG( dbg << "cRS232::Read: Read " << bytes_read_inc << "/" << (size-bytes_read) << " bytes (hex): " << cHexByteString( buffer+bytes_read, bytes_read_inc ) << "\n" );
421  // Any bytes read ?
422  if (bytes_read_inc>0)
423  {
424  bytes_read+=bytes_read_inc;
425  if (bytes_read==size)
426  return bytes_read;
427  //printf("data read:%i\n",bytes_read);
428  }
429  }
430  else
431  {
432  // we can NOT return on less, so check if enough is available
433 
434  //printf("serial:time left %lu\n",Time2Long(tz));
435  // Are there already enough bytes received ?
436 
437  // from sys/termios.h on cygwin:
438  /* TIOCINQ is utilized instead of FIONREAD which has been
439  accupied for other purposes under CYGWIN.
440  Other UNIX ioctl requests has been omited because
441  effects of their work one can achive by standard
442  POSIX commands */
443 
444  errno = 0;
445  int irc = ioctl( fd, TIOCINQ, &bytes_read_inc );
446  if ( irc < 0)
447  {
448  // error from ioctl
451  throw new cRS232Exception( cMsg( "Error calling ioctl(): %s", GetLastErrorMessage() ) );
452  }
453  else
454  {
455  //if ( bytes_read_inc > 200 ) //size )
456  // cerr << "ioctl ok, bytes_read_inc=" << bytes_read_inc << "\n"; // FIX ME: remove me
457  // Yes? then read data
458  if (bytes_read_inc>=size)
459  {
460  if ((bytes_read = read( fd, data, size )) < 0)
461  {
462  // error from read
465  throw new cRS232Exception( cMsg( "Error calling read(): %s", GetLastErrorMessage() ) );
466  }
467  else
468  {
469  DBG( dbg << "cRS232::Read: Read " << bytes_read << "/" << size << " bytes (hex): " << cHexByteString( (char const*) data, bytes_read ) << "\n" );
470  return bytes_read;
471  }
472  }
473  // No ? do nothing
474  }
475  }
476  }
477  else
478  {
479  // (select_return == 0) select returned, but nothing is available
480 
481  if (return_on_less_data)
482  return bytes_read;
483 
484  // else keep trying...
485  }
486  }
487  // Look again for data, if any time left
488  while ( timeout_us < 0 || start_time.Elapsed_us() < (long) max_time_us );
489 
490  return bytes_read;
491 }
492 //----------------------------------------------------------------------
493 
494 
495 //======================================================================
496 /*
497  Here are some settings for the emacs/xemacs editor (and can be safely ignored):
498  (e.g. to explicitely set C++ mode for *.h header files)
499 
500  Local Variables:
501  mode:C++
502  mode:ELSE
503  End:
504  */
505 //======================================================================
ssize_t Read(void *data, ssize_t size, long timeout_us, bool return_on_less_data)
USING_NAMESPACE_SDH char * StrDupNew(char *const s)
helper function, duplicate string s into a char array allocated with new[]
Interface of auxilliary utility functions for SDHLibrary-CPP.
#define NULL
Definition: getopt1.c:56
int status
Definition: rs232-cygwin.h:103
std::string device_format_string
the sprintf format string to generate the device name from the port, see Constructor ...
Definition: rs232-cygwin.h:92
dummy class for (debug) stream output of bytes as list of hex values
Definition: dbg.h:329
void Open(void)
void Close(void)
Close the previously opened rs232 port.
#define DBG(...)
Low-level communication class to access a serial port.
Definition: serialbase.h:105
virtual void SetTimeout(double _timeout)
set the timeout for next readline() calls (negative value means: no timeout, wait for ever) ...
Definition: rs232-vcc.cpp:183
char const * GetLastErrorMessage(void)
return the last error message as string. The string returned will be overwritten by the next call to ...
Definition: serialbase.h:260
int write(char const *ptr, int len=0)
Write data to a previously opened port.
int fd
the file descriptor of the RS232 port
Definition: rs232-cygwin.h:98
cRS232(int _port, unsigned long _baudrate, double _timeout, char const *_device_format_string="/dev/ttyS%d")
#define USING_NAMESPACE_SDH
UInt16 size
Definition: dsa.h:269
bool IsOpen(void)
Return true if port to RS232 is open.
int port
the RS232 portnumber to use
Definition: rs232-cygwin.h:89
Interface of class #SDH::cRS232, a class to access serial RS232 port on cygwin/linux.
tcflag_t BaudrateToBaudrateCode(unsigned long baudrate)
Translate a baudrate given as unsigned long into a baudrate code for struct termios.
This file contains settings to make the SDHLibrary compile on differen systems:
termios io_set_old
Definition: rs232-cygwin.h:105
Very simple class to measure elapsed time.
Definition: simpletime.h:84
unsigned long baudrate
the baudrate in bit/s
Definition: rs232-cygwin.h:95
Derived exception class for low-level RS232 related exceptions.
Definition: rs232-cygwin.h:71
Class for short, fixed maximum length text messages.
Definition: sdhexception.h:77
long Elapsed_us(void) const
Return time in micro seconds elapsed between the time stored in the object and now.
Definition: simpletime.h:125
cDBG dbg
A stream object to print colored debug messages.
Definition: serialbase.h:227


sdhlibrary_cpp
Author(s): Dirk Osswald
autogenerated on Mon Feb 28 2022 23:41:51