Main Page | Modules | Class Hierarchy | Class List | File List | Class Members

di_serial_io.cpp

00001 /***************************************************************************
00002               di_serial_io.cpp  -  Functions that deal with input/output
00003                                    to many serial devices.
00004                              -------------------
00005     begin                : Wed Jun 23 2004
00006     author               : Ioan S. Popescu
00007 
00008 Copyright (C) 2004 DATAQ Instruments, Inc. <develop@dataq.com>
00009 
00010 This program is free software; you can redistribute it and/or 
00011 modify it under the terms of the GNU General Public License 
00012 as published by the Free Software Foundation; either 
00013 version 2 of the License, or (at your option) any later 
00014 version.
00015 
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 GNU General Public License for more details.
00020 
00021 You should have received a copy of the GNU General Public License
00022 along with this program; if not, write to the Free Software
00023 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00024  ***************************************************************************/
00025 
00026 #include "di_serial_io.h"
00027 
00028 #include <unistd.h>   // UNIX standard function definitions
00029 #include <sys/ioctl.h>// IO port control function definitions
00030 #include <time.h>     // Time function definitions
00031 #include <fcntl.h>    // File control definitions
00032 
00033 di_serial_io::di_serial_io()
00034 {
00035   m_comm_fd = -1;
00036   m_baudrate = B4800;
00037   m_old_tiocm = 0;
00038   m_timeout = 2;
00039 }
00040 
00041 di_serial_io::~di_serial_io()
00042 {
00043   if(m_comm_fd != -1)
00044     disconnect();
00045 }
00046 
00064 const u_int16_t di_serial_io::connect(const char *const dev_file, const u_int8_t device)
00065 {
00066   my_errno = 0;
00067   // open the serial port
00068   m_comm_fd = open(dev_file, O_RDWR | O_NOCTTY | O_NDELAY);
00069   if (m_comm_fd == -1)
00070   {
00071     my_errno = errno;
00072     return my_errno; // same error codes as open()
00073   }
00074   else
00075     fcntl(m_comm_fd, F_SETFL, FNDELAY); // set to non-blocking
00076 
00077   struct termios serial_opts; // will hold new serial port settings
00078 
00079   // flush send/receive buffers so settings will be set properly
00080   flush_receive();
00081   tcflush(m_comm_fd, TCOFLUSH);
00082 
00083   // get copy of current serial port settings to restore later
00084   if(tcgetattr(m_comm_fd, &m_old_termios) == -1)
00085   {
00086     my_errno = errno;
00087     return my_errno; // same error codes as tcgetattr()
00088   }
00089   // get copy of other current serial port settings to restore later
00090   if(ioctl(m_comm_fd, TIOCMGET, &m_old_tiocm) == -1)
00091   {
00092     my_errno = errno;
00093     return my_errno;
00094   }
00095 
00096   // get current serial port settings (must modify existing settings)
00097   if(tcgetattr(m_comm_fd, &serial_opts) == -1)
00098   {
00099     my_errno = errno;
00100     return my_errno; // same error codes as tcgetattr()
00101   }
00102 
00103   // clear out settings
00104   serial_opts.c_cflag = 0;
00105   serial_opts.c_iflag = 0;
00106   serial_opts.c_lflag = 0;
00107   serial_opts.c_oflag = 0;
00108 
00109   // set baud rate
00110   if(cfsetispeed(&serial_opts, m_baudrate) == -1)
00111   {
00112     my_errno = errno;
00113     return my_errno; // same error codes as cfsetispeed()
00114   }
00115   if(cfsetospeed(&serial_opts, m_baudrate) == -1)
00116   {
00117     my_errno = errno;
00118     return my_errno; // same error codes as cfsetospeed()
00119   }
00120 
00121   // no parity
00122   serial_opts.c_cflag &= ~PARENB;
00123   serial_opts.c_iflag &= ~INPCK;
00124 
00125   // apply settings and check for error
00126   // this is done because tcsetattr() would return success if *any*
00127   // settings were set correctly; this way, more error checking is done
00128   if(tcsetattr(m_comm_fd, TCSANOW, &serial_opts) == -1)
00129   {
00130     my_errno = errno;
00131     return my_errno; // same error codes as tcsetattr()
00132   }
00133 
00134   serial_opts.c_cflag &= ~CSTOPB; // 1 stop bit
00135   serial_opts.c_cflag &= ~CSIZE;  // reset byte size
00136   serial_opts.c_cflag |= CS8;     // 8 bits
00137 
00138   // apply settings and check for error
00139   if(tcsetattr(m_comm_fd, TCSANOW, &serial_opts) == -1)
00140   {
00141     my_errno = errno;
00142     return my_errno; // same error codes as tcsetattr()
00143   }
00144 
00145   // disable hardware flow control
00146   serial_opts.c_cflag &= ~CRTSCTS;
00147 
00148   // apply settings and check for error
00149   if(tcsetattr(m_comm_fd, TCSANOW, &serial_opts) == -1)
00150   {
00151     my_errno = errno;
00152     return my_errno; // same error codes as tcsetattr()
00153   }
00154 
00155   // disable software flow control
00156   serial_opts.c_iflag &= ~(IXON | IXOFF | IXANY);
00157 
00158   // apply settings and check for error
00159   if(tcsetattr(m_comm_fd, TCSANOW, &serial_opts) == -1)
00160   {
00161     my_errno = errno;
00162     return my_errno; // same error codes as tcsetattr()
00163   }
00164 
00165   // raw I/O
00166   serial_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
00167   serial_opts.c_oflag &= ~OPOST;
00168 
00169   // apply settings and check for error
00170   if(tcsetattr(m_comm_fd, TCSANOW, &serial_opts) == -1)
00171   {
00172     my_errno = errno;
00173     return my_errno; // same error codes as tcsetattr()
00174   }
00175 
00176   // timeouts
00177   serial_opts.c_cc[VMIN] = 0;
00178   serial_opts.c_cc[VTIME] = 10;
00179 
00180   // apply settings and check for error
00181   if(tcsetattr(m_comm_fd, TCSANOW, &serial_opts) == -1)
00182   {
00183     my_errno = errno;
00184     return my_errno; // same error codes as tcsetattr()
00185   }
00186 
00187   // misc. settings
00188   serial_opts.c_cflag |= HUPCL;
00189   serial_opts.c_cflag |= (CLOCAL | CREAD);
00190 
00191   // apply settings and check for error
00192   if(tcsetattr(m_comm_fd, TCSANOW, &serial_opts) == -1)
00193   {
00194     my_errno = errno;
00195     return my_errno; // same error codes as tcsetattr()
00196   }
00197 
00198   int status = m_old_tiocm; // get settings
00199   switch(device)
00200   {
00201     default:
00202       status |= TIOCM_DTR;  // turn on DTR and...
00203       status |= TIOCM_RTS;  // ...RTS lines to power up device
00204       // apply settings
00205       if(ioctl(m_comm_fd, TIOCMSET, &status) == -1)
00206       {
00207         my_errno = errno;
00208         return my_errno;
00209       }
00210 
00211       // wait for device to power up
00212       usleep(100000);
00213   }
00214 
00215   // flush send/receive buffers so settings will be set properly
00216   flush_receive();
00217   tcflush(m_comm_fd, TCOFLUSH);
00218 
00219   return 0;
00220 }
00221 
00232 const u_int16_t di_serial_io::disconnect()
00233 {
00234   my_errno = 0;
00235   // flush send/receive buffers so settings will be set properly
00236   flush_receive();
00237   tcflush(m_comm_fd, TCOFLUSH);
00238 
00239   // try to reset serial port settings back to original
00240   tcsetattr(m_comm_fd, TCSANOW, &m_old_termios);
00241   // try to reset other serial port settings back to original
00242   ioctl(m_comm_fd, TIOCMSET, &m_old_tiocm);
00243 
00244   // try to close serial port
00245   if(close(m_comm_fd) == -1)
00246   {
00247     my_errno = errno;
00248     return my_errno;
00249   }
00250   m_comm_fd = -1;
00251   return 0;
00252 }
00253 
00258 const bool di_serial_io::is_comm_open()
00259 {
00260   if(m_comm_fd == -1)
00261     return false;
00262   else
00263     return true;
00264 }
00265 
00285 const bool di_serial_io::di_read(u_int8_t *data,
00286                                  u_int16_t &amount,
00287                                  const u_int8_t packet_len)
00288 {
00289   my_errno = 0;
00290   int16_t available = 0;
00291   u_int8_t retries = 4;
00292   time_t time_start = time(NULL);
00293 
00294   ioctl(m_comm_fd, FIONREAD, &available);
00295   // wait for data
00296   while((time(NULL) - time_start < m_timeout) &&  // stop when timeout reached
00297         (available < amount))                      // or when enough data
00298   {
00299     usleep(50);
00300     ioctl(m_comm_fd, FIONREAD, &available);       // is available
00301   }
00302 
00303   // timed out without any data
00304   if(available == 0)
00305   {
00306     my_errno = ENODATA;
00307     return true;
00308   }
00309   // timed out and not enough data
00310   if(time(NULL) - time_start >= m_timeout && available < amount)
00311   {
00312     // read as much as is available
00313     if(read(m_comm_fd, data, available) == -1)
00314     {
00315       my_errno = errno;
00316       return true;
00317     }
00318 
00319     // let caller know how much was read
00320     amount = available;
00321     my_errno = ETIMEDOUT;
00322     return true;
00323   }
00324 
00325   // read the amount requested
00326   if(read(m_comm_fd, data, amount) == -1)
00327   {
00328     my_errno = errno;
00329     return true;
00330   }
00331 
00332   // validate if requested
00333   if(packet_len > 0)
00334   {
00335     time_start = time(NULL);
00336     while(retries-- > 0 && !di_valid(data, packet_len))
00337     {
00338       // inside this loop means previously read data was invalid
00339       if(!di_synchronize(data, amount, packet_len))
00340       {
00341         return true;
00342       }
00343     }
00344   }
00345 
00346   return false;  // success
00347 }
00348 
00367 const bool di_serial_io::di_send(const u_int8_t *const data,
00368                                  u_int16_t &amount,
00369                                  const u_int8_t echo,
00370                                  const u_int8_t retry)
00371 {
00372   my_errno = 0;
00373   u_int8_t tries = retry;
00374   int write_return = 0;
00375 
00376   while(tries-- > 0)
00377   {
00378     // flush send buffer to make sure command gets sent
00379     tcflush(m_comm_fd, TCOFLUSH);
00380     if(echo > 0)
00381       // clear receive buffer if checking echo
00382       flush_receive();
00383 
00384     // try to send all of it at once
00385     write_return = write(m_comm_fd, data, amount);
00386     if(write_return <= 0)
00387     {
00388       my_errno = errno;
00389       return true;  // error in send
00390     }
00391 
00392     // send whatever's left, if need to (one byte at a time)
00393     for(u_int16_t i = static_cast<u_int16_t>(write_return); i < amount; i++)
00394     {
00395       if(write(m_comm_fd, data + i, 1) <= 0)
00396       {
00397         my_errno = errno;
00398         return true;  // error in send
00399       }
00400     }
00401 
00402     if(echo > 0)  // check for at least 1 echo
00403     {
00404       // check if echo matches
00405       my_errno = 0;
00406       if(!di_echo(data, echo))
00407       {
00408         my_errno = EBADRSVP;
00409         // wait a little
00410         usleep(500000);
00411         continue;  // didn't match, try again
00412       }
00413       else if(my_errno != 0)
00414       {
00415         return true; // error in read
00416       }
00417     }
00418 
00419     my_errno = 0;
00420     return false; // data sent successfully
00421   }
00422 
00423   return true;
00424 }
00425 
00430 const int16_t di_serial_io::bytes_in_receive()
00431 {
00432   int bytes = 0;
00433   
00434   ioctl(m_comm_fd, FIONREAD, &bytes);
00435   return bytes;
00436 }
00437 
00442 void di_serial_io::flush_receive()
00443 {
00444   time_t time_start = time(NULL);
00445 
00446   while(bytes_in_receive() > 0 && (time(NULL) - time_start < 2))
00447   {
00448     // flush receive buffers to clear them
00449     tcflush(m_comm_fd, TCIFLUSH);
00450     usleep(50);
00451   }
00452 
00453   return;
00454 }
00455 
00466 const bool di_serial_io::di_valid(const u_int8_t *const data,
00467                                   const u_int8_t packet_len)
00468 {
00469   // byte 1 has zero at end
00470   if((data[0] & 0x1) != 0)
00471     return false;
00472 
00473   // remaining bytes have one at end
00474   for(int i = 1; i < packet_len; i++)
00475   {
00476     if((data[i] & 0x1) != 1)
00477       return false;
00478   }
00479 
00480   return true;
00481 }
00482 
00495 const bool di_serial_io::di_synchronize(u_int8_t *data,
00496                                              const u_int8_t data_len,
00497                                              const u_int8_t packet_len)
00498 {
00499   u_int8_t i = 0, j = 0;
00500 
00501   // look for the trailing zero bit in current data
00502   for(i = 0; i < data_len && (data[i] & 0x1) != 0; i++);
00503 
00504   // copy that portion to beginning of data
00505   for(j = 0; i < data_len; j++, i++)
00506     data[j] = data[i];
00507 
00508   // read what is assumed to be the remainder of the packet
00509   u_int16_t amount = packet_len - j;
00510   if(di_read(data + j, amount, 0))
00511     return true;
00512 
00513   return false; // Success.
00514 }
00515 
00527 const bool di_serial_io::di_echo(const u_int8_t *const expected,
00528                                     const u_int8_t amount)
00529 {
00530   u_int16_t bytes = amount;
00531   u_int8_t *buf = new u_int8_t[bytes];
00532 
00533   if(di_read(buf, bytes, 0)) // read data
00534   {
00535     delete [] buf;
00536     return false;
00537   }
00538   for(unsigned int i = 1; i <= bytes; i++)  // try to match data
00539   {
00540   // need to shift expected because NULL isn't echoed
00541     if(expected[i] != buf[i-1])
00542     {
00543       delete [] buf;
00544       return false; // mismatch found
00545     }
00546   }
00547   delete [] buf;
00548   return true;  // match succeeded
00549 }
00550 

Generated on Mon Aug 2 09:44:50 2004 for DataqSDK by doxygen 1.3.6