Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

SerialCommPort.cc

Go to the documentation of this file.
00001 #include "SerialCommPort.h"
00002 #include <sys/ioctl.h>
00003 #include <errno.h>
00004 #ifdef __APPLE__
00005 #  include <IOKit/serial/ioss.h>
00006 #endif
00007 #ifdef __linux__
00008 #  include <linux/serial.h>
00009 #endif
00010 
00011 #ifdef __CYGWIN__
00012 // Cygwin doesn't provide cfmakeraw...
00013 // this definition found in port of unix 'script' utility
00014 // by Alan Evans (Alan_Evans AT iwv com), 2002-09-27
00015 // http://marc.info/?l=cygwin&m=103314951904556&w=2
00016 void cfmakeraw(struct termios *termios_p) {
00017   termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
00018         |INLCR|IGNCR|ICRNL|IXON);
00019   termios_p->c_oflag &= ~OPOST;
00020   termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
00021   termios_p->c_cflag &= ~(CSIZE|PARENB);
00022   termios_p->c_cflag |= CS8;
00023 }
00024 #endif
00025 
00026 using namespace std;
00027 
00028 const char * const SerialCommPort::parityNames[] = { "EVEN", "ODD", "NONE", NULL };
00029 INSTANTIATE_NAMEDENUMERATION_STATICS(SerialCommPort::parity_t);
00030 
00031 std::map<int,speed_t> SerialCommPort::bauds;
00032 
00033 const std::string SerialCommPort::autoRegisterSerialCommPort = CommPort::getRegistry().registerType<SerialCommPort>("SerialCommPort");
00034 
00035 void SerialCommPort::setupSerial() {
00036   if(fd<0)
00037     return;
00038   
00039   // ** first do a basic setup to initialize our access to the serial port **
00040   
00041   // get current termios structure (sys/termios.h)
00042   struct termios  theTermios;
00043   int ret = tcgetattr(fd, &theTermios);
00044   cfmakeraw(&theTermios);
00045   
00046   // We'll handle Darwin and Linux after we're done with termios settings
00047   // these platforms use a separate ioctl call to get non-standard baud rates
00048 #ifndef __APPLE__
00049   std::map<int,speed_t>::const_iterator baudIt = bauds.find(baudRate);
00050 #  ifdef __linux__
00051   // For linux, custom/non-standard speed requires B38400 baud be set here (wtf...)
00052   const speed_t TGTBAUD = baudIt==bauds.end() ? B38400 : baudIt->second;
00053 #  else
00054   // Unknown platform: we'll just have to stick to cfsetspeed, which probably
00055   // doesn't handle non-standard rates, but worth a shot :(
00056   const speed_t TGTBAUD = baudIt==bauds.end() ? baudRate : baudIt->second;
00057 #  endif
00058   ret = cfsetispeed(&theTermios, TGTBAUD);
00059   if(ret)
00060     dispError("cfsetispeed",ret,errno);
00061   ret = cfsetospeed(&theTermios, TGTBAUD);
00062   if(ret)
00063     dispError("cfsetospeed",ret,errno);
00064 #endif
00065   
00066   // turn on READ and ignore modem control lines
00067   theTermios.c_cflag |= CREAD | CLOCAL;
00068   //theTermios.c_cflag = CIGNORE;
00069   
00070   switch (dataBits) {
00071     case 5:   theTermios.c_cflag |= CS5;    break;
00072     case 6:   theTermios.c_cflag |= CS6;    break;
00073     case 7:   theTermios.c_cflag |= CS7;    break;
00074     case 8:   theTermios.c_cflag |= CS8;    break;
00075     default: std::cerr << "SerialCommPort: bad DataBits value " << dataBits << std::endl; 
00076   }
00077   
00078   // stop bit(s)?
00079   if(stopBits==1u)
00080     theTermios.c_cflag &= ~CSTOPB;
00081   else if(stopBits==2u)
00082     theTermios.c_cflag |= CSTOPB;
00083   else
00084     std::cerr << "SerialCommPort: bad StopBits value " << stopBits << std::endl; 
00085   
00086   // parity?
00087   switch(parity) {
00088     case EVEN:
00089       theTermios.c_cflag |= PARENB; theTermios.c_cflag &= ~PARODD; break;
00090     case ODD:
00091       theTermios.c_cflag |= PARENB; theTermios.c_cflag |= PARODD; break;
00092     case NONE:
00093       theTermios.c_cflag &= ~PARENB; break;
00094   }
00095   // default no flow control:
00096   theTermios.c_iflag &= ~(IXON | IXOFF);
00097   theTermios.c_cflag &= ~CRTSCTS;
00098   
00099   // apply our settings
00100   ret = tcsetattr(fd, TCSAFLUSH, &theTermios);
00101   //ret = ioctl(fd, TIOCSETA, &theTermios); // alternative ?
00102   if (ret)
00103     dispError("tcsetattr(TCSAFLUSH)",ret,errno);
00104   
00105 #ifdef __APPLE__
00106   // this allows higher (non-standard) baud rates, apparently not supported (on darwin) via termios
00107   const int TGTBAUD = baudRate;
00108   ret = ioctl(fd, IOSSIOSPEED, &TGTBAUD); // as opposed to setting it in theTermios ?
00109   if (ret)
00110     dispError("ioctl(IOSSIOSPEED)",ret,errno);
00111 #endif
00112 
00113 #ifdef __linux__
00114   if(baudIt==bauds.end()) {
00115     serial_struct sserial;
00116     ret = ioctl(fd, TIOCGSERIAL, &sserial);
00117     if(ret)
00118       dispError("ioctl(TIOCGSERIAL)",ret,errno);
00119     else {
00120       // if we're using a custom speed, set the bit, otherwise clear it
00121       if(baudIt==bauds.end())
00122         sserial.flags |= ASYNC_SPD_CUST;
00123       else
00124         sserial.flags &= ~ASYNC_SPD_MASK;
00125       // always request low-latency though (appears to be default anyway)
00126       sserial.flags |= ASYNC_LOW_LATENCY;
00127       sserial.custom_divisor = (int)(sserial.baud_base / (float)baudRate + .5);
00128       ret = ioctl(fd, TIOCSSERIAL, &sserial);
00129       if(ret)
00130         dispError("ioctl(TIOCSSERIAL)",ret,errno);
00131     }
00132   }
00133 #endif
00134   
00135   // this part doesn't seem to be necessary, but better safe than sorry...
00136   int modem;
00137   ret = ioctl(fd, TIOCMGET, &modem);
00138   if (ret!=0) {
00139     //int err = errno;
00140     //std::cerr << "Warning: ioctl(TIOCMGET) returned errno " << err << " (" << strerror(err) << ")" << endl;
00141   } else {
00142     modem |= TIOCM_DTR;
00143     ret = ioctl(fd, TIOCMSET, &modem);
00144     if (ret)
00145       dispError("ioctl(TIOCMSET)",ret,errno);
00146   }
00147   
00148   // this works on linux, but won't let you set custom speeds on OS X
00149   // best to leave blank unless you really need it
00150   if(sttyConfig.size()>0) {
00151     // ** do additional setup by calling out to stty **
00152   #ifdef __linux__
00153     std::string cmd="stty -F "+path+" "+sttyConfig;
00154   #else /* assume BSD style... use a -f instead of -F (...sigh...) */
00155     std::string cmd="stty -f "+path+" "+sttyConfig;
00156   #endif
00157     switch(::system(cmd.c_str())) {
00158       case 0:
00159         break; // good!
00160       case -1:
00161         perror("Warning: SerialCommPort could not make system call to stty"); break;
00162       case 127:
00163         std::cerr << "Warning: SerialCommPort could not make system call to stty: no shell found" << std::endl; break;
00164       default:
00165         std::cerr << "Warning: SerialCommPort stty reported error on configuration string: " << sttyConfig << std::endl; break;
00166     }
00167   }
00168 }
00169 
00170 void SerialCommPort::dispError(const char* where, int ret, int err) {
00171   std::cerr << where << " returned " << ret << " (errno " << err << ": " << strerror(err) << ")" << std::endl;
00172 }
00173 
00174 void SerialCommPort::initBauds() {
00175 #ifdef B0
00176   bauds[0]=B0;
00177 #endif
00178 #ifdef B50
00179   bauds[50]=B50;
00180 #endif
00181 #ifdef B75
00182   bauds[75]=B75;
00183 #endif
00184 #ifdef B110
00185   bauds[110]=B110;
00186 #endif
00187 #ifdef B134
00188   bauds[134]=B134;
00189 #endif
00190 #ifdef B150
00191   bauds[150]=B150;
00192 #endif
00193 #ifdef B200
00194   bauds[200]=B200;
00195 #endif
00196 #ifdef B300
00197   bauds[300]=B300;
00198 #endif
00199 #ifdef B600
00200   bauds[600]=B600;
00201 #endif
00202 #ifdef B1200
00203   bauds[1200]=B1200;
00204 #endif
00205 #ifdef B1200
00206   bauds[1200]=B1200;
00207 #endif
00208 #ifdef B1800
00209   bauds[1800]=B1800;
00210 #endif
00211 #ifdef B2400
00212   bauds[2400]=B2400;
00213 #endif
00214 #ifdef B4800
00215   bauds[4800]=B4800;
00216 #endif
00217 #ifdef B9600
00218   bauds[9600]=B9600;
00219 #endif
00220 #ifdef B19200
00221   bauds[19200]=B19200;
00222 #endif
00223 #ifdef B38400
00224   bauds[38400]=B38400;
00225 #endif
00226 #ifdef B57600
00227   bauds[57600]=B57600;
00228 #endif
00229 #ifdef B76800
00230   bauds[76800]=B76800;
00231 #endif
00232 #ifdef B115200
00233   bauds[115200]=B115200;
00234 #endif
00235 #ifdef B153600
00236   bauds[153600]=B153600;
00237 #endif
00238 #ifdef B230400
00239   bauds[230400]=B230400;
00240 #endif
00241 #ifdef B307200
00242   bauds[307200]=B307200;
00243 #endif
00244 #ifdef B460800
00245   bauds[460800]=B460800;
00246 #endif
00247 #ifdef B500000
00248   bauds[500000]=B500000;
00249 #endif
00250 #ifdef B576000
00251   bauds[576000]=B576000;
00252 #endif
00253 #ifdef B921600
00254   bauds[921600]=B921600;
00255 #endif
00256 #ifdef B1000000
00257   bauds[1000000]=B1000000;
00258 #endif
00259 #ifdef B1152000
00260   bauds[1152000]=B1152000;
00261 #endif
00262 #ifdef B1500000
00263   bauds[1500000]=B1500000;
00264 #endif
00265 #ifdef B2000000
00266   bauds[2000000]=B2000000;
00267 #endif
00268 #ifdef B2500000
00269   bauds[2500000]=B2500000;
00270 #endif
00271 #ifdef B3000000
00272   bauds[3000000]=B3000000;
00273 #endif
00274 #ifdef B3500000
00275   bauds[3500000]=B3500000;
00276 #endif
00277 #ifdef B4000000
00278   bauds[4000000]=B4000000;
00279 #endif
00280 }
00281 
00282 /*! @file
00283  * @brief 
00284  * @author Ethan Tira-Thompson (ejt) (Creator)
00285  */

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:39 2016 by Doxygen 1.6.3