Wiselib
|
00001 // vim: set noexpandtab ts=4 sw=4: 00002 00003 #ifndef PC_COM_UART_H 00004 #define PC_COM_UART_H 00005 00006 #include "util/base_classes/uart_base.h" 00007 00008 #include <stdlib.h> 00009 #include <stdio.h> 00010 #include <signal.h> 00011 #include <pthread.h> 00012 #include <unistd.h> 00013 #include <fcntl.h> 00014 #include <termios.h> 00015 #include <err.h> 00016 #include <errno.h> 00017 #include <sys/ioctl.h> 00018 00019 /* 00020 * PC_COM_UART_DEBUG 00021 * 00022 * undefined or 0 -> No debugging 00023 * >= 1 -> Basic state changes like successful connection will be 00024 * reported 00025 * >= 100 -> Hardcore debug output 00026 */ 00027 00028 #if PC_COM_UART_DEBUG 00029 #include <iostream> 00030 #include <iomanip> 00031 #endif 00032 00033 namespace wiselib { 00034 00035 namespace { 00036 enum { BUFFER_SIZE = 256 }; // should be enough for 19200 Baud (technically 20 should suffice) 00037 } 00038 00049 template< 00050 typename OsModel_P, 00051 const char* address_, 00052 const bool isense_reset_ = false, 00053 typename Timer_P = typename OsModel_P::Timer 00054 > 00055 class PCComUartModel 00056 : public UartBase<OsModel_P, typename OsModel_P::size_t, typename OsModel_P::block_data_t> 00057 { 00058 public: 00059 typedef OsModel_P OsModel; 00060 typedef Timer_P Timer; 00061 typedef typename OsModel::size_t size_t; 00062 typedef typename OsModel::block_data_t block_data_t; 00063 typedef PCComUartModel<OsModel_P, address_, isense_reset_, Timer_P> self_type; 00064 typedef self_type* self_pointer_t; 00065 00066 enum ErrorCodes 00067 { 00068 SUCCESS = OsModel::SUCCESS, 00069 ERR_UNSPEC = OsModel::ERR_UNSPEC 00070 }; 00071 00072 PCComUartModel(); 00073 PCComUartModel(PCOs& os); 00074 00075 void set_baudrate(uint32_t baudrate) { 00076 switch(baudrate) { 00077 case 9600: baudrate_ = B9600; break; 00078 case 19200: baudrate_ = B19200; break; 00079 case 38400: baudrate_ = B38400; break; 00080 case 57600: baudrate_ = B57600; break; 00081 case 115200: baudrate_ = B115200; break; 00082 default: 00083 assert(false); 00084 } 00085 } 00086 00087 int enable_serial_comm(); 00088 int disable_serial_comm(); 00089 00090 int write(size_t len, char* buf); 00091 void try_read(void* userdata); 00092 00093 const char* address() { return address_; } 00094 00095 private: 00096 Timer timer_; 00097 ::speed_t baudrate_; 00098 00099 int port_fd_; 00100 00101 void try_read(); 00102 }; // class PCComUartModel 00103 00104 template<typename OsModel_P, const char* address_, const bool isense_reset_, typename Timer_P> 00105 PCComUartModel<OsModel_P, address_, isense_reset_, Timer_P>:: 00106 PCComUartModel(PCOs& os) : timer_(os), baudrate_(B9600) { 00107 } 00108 00109 template<typename OsModel_P, const char* address_, const bool isense_reset_, typename Timer_P> 00110 PCComUartModel<OsModel_P, address_, isense_reset_, Timer_P>:: 00111 PCComUartModel() : baudrate_(B9600) { 00112 } 00113 00114 template<typename OsModel_P, const char* address_, const bool isense_reset_, typename Timer_P> 00115 int PCComUartModel<OsModel_P, address_, isense_reset_, Timer_P>::enable_serial_comm() { 00116 // source: http://en.wikibooks.org/wiki/Serial_Programming/Serial_Linux 00117 00118 struct termios attr; 00119 //memset(&attr, 0, sizeof(attr)); 00120 bzero(&attr, sizeof(attr)); 00121 //tcgetattr(port_fd_, &attr); 00122 attr.c_cflag = baudrate_|CS8|CREAD|CLOCAL; // 8N1 00123 attr.c_iflag = 0; 00124 attr.c_oflag = 0; 00125 attr.c_lflag = 0; 00126 attr.c_cc[VMIN] = 1; // try to read at least 1 char 00127 attr.c_cc[VTIME] = 0; // time out after 800ms 00128 00129 #if PC_COM_UART_DEBUG 00130 std::cout << "[pc_com_uart] Opening: " << address_ << "\n"; 00131 #endif 00132 00133 port_fd_ = open(address_, O_RDWR | O_NONBLOCK | O_NOCTTY); 00134 if(port_fd_ < 0) { 00135 err(1, "Error opening UART %s", address_); 00136 } 00137 00138 if( ( cfsetospeed(&attr, baudrate_) == -1 ) || 00139 ( cfsetispeed(&attr, baudrate_) == -1 ) ) 00140 { 00141 perror( "Could not set baudrate:" ); 00142 } 00143 00144 tcflush(port_fd_, TCOFLUSH); 00145 tcflush(port_fd_, TCIFLUSH); 00146 if(tcsetattr(port_fd_, TCSANOW, &attr) == -1) { 00147 err(1, "Error during tcsetattr() on %s", address_); 00148 } 00149 00150 if(isense_reset_) { 00151 #if PC_COM_UART_DEBUG 00152 std::cout << "[pc_com_uart] Executing isense reset" << std::endl; 00153 #endif 00154 00155 int status = TIOCM_RTS | TIOCM_DTR; 00156 ioctl(port_fd_, TIOCMSET, &status); 00157 timer_.sleep(100); 00158 status = 0; 00159 ioctl(port_fd_, TIOCMSET, &status); 00160 timer_.sleep(100); 00161 } 00162 00163 timer_.template set_timer<self_type, &self_type::try_read>(100, this, 0); 00164 00165 return SUCCESS; 00166 } 00167 00168 template<typename OsModel_P, const char* address_, const bool isense_reset_, typename Timer_P> 00169 int PCComUartModel<OsModel_P, address_, isense_reset_, Timer_P>::disable_serial_comm() { 00170 //close(port_fd_); 00171 //port_fd_ = -1; 00172 return SUCCESS; 00173 } 00174 00175 template<typename OsModel_P, const char* address_, const bool isense_reset_, typename Timer_P> 00176 int PCComUartModel<OsModel_P, address_, isense_reset_, Timer_P>:: 00177 write(size_t len, char* buf) { 00178 // Block SIGALRM to avoid interrupting call of timer_handler. 00179 sigset_t signal_set, old_signal_set; 00180 if ( ( sigemptyset( &signal_set ) == -1 ) || 00181 ( sigaddset( &signal_set, SIGALRM ) == -1 ) || 00182 pthread_sigmask( SIG_BLOCK, &signal_set, &old_signal_set ) ) 00183 { 00184 perror( "Failed to block SIGALRM" ); 00185 } 00186 00187 static const int max_retries = 100; 00188 int retries = max_retries, r; 00189 size_t written = 0; 00190 00191 do { 00192 r = ::write(port_fd_, reinterpret_cast<void*>(buf+written), len-written); 00193 if(r < 0) { 00194 if( ( errno != EAGAIN ) && ( errno != EWOULDBLOCK ) && ( errno != EINTR ) ) { 00195 warn("Error writing to UART %s (%d more retries)", address_, retries); 00196 if(retries == 0) { 00197 err(1, "Couldnt write to UART %s", address_); 00198 } 00199 else { 00200 retries--; 00201 } 00202 } 00203 } else { 00204 written += r; 00205 retries = max_retries; 00206 } 00207 } while(written < len); 00208 00209 #if PC_COM_UART_DEBUG >= 100 00210 std::cout << "[pc_com_uart] wrote: " << len << " bytes." << std::endl; 00211 #endif 00212 00213 // Unblock SIGALRM. 00214 if( sigismember( &old_signal_set, SIGALRM ) == 0 ) 00215 { 00216 if ( ( sigemptyset( &signal_set ) == -1 ) || 00217 ( sigaddset( &signal_set, SIGALRM ) == -1 ) || 00218 pthread_sigmask( SIG_UNBLOCK, &signal_set, 0 ) ) 00219 { 00220 perror( "Failed to unblock SIGALRM" ); 00221 } 00222 } 00223 00224 return SUCCESS; 00225 } // write 00226 00227 template<typename OsModel_P, const char* address_, const bool isense_reset_, typename Timer_P> 00228 void PCComUartModel<OsModel_P, address_, isense_reset_, Timer_P>:: 00229 try_read(void* userdata) { 00230 00231 // Block SIGALRM to avoid interrupting call of timer_handler. 00232 sigset_t signal_set, old_signal_set; 00233 if ( ( sigemptyset( &signal_set ) == -1 ) || 00234 ( sigaddset( &signal_set, SIGALRM ) == -1 ) || 00235 pthread_sigmask( SIG_BLOCK, &signal_set, &old_signal_set ) ) 00236 { 00237 perror( "Failed to block SIGALRM" ); 00238 } 00239 00240 uint8_t buffer[BUFFER_SIZE]; 00241 int bytes = ::read(port_fd_, static_cast<void*>(buffer), BUFFER_SIZE); 00242 00243 if(bytes == -1) { 00244 if(errno != EAGAIN && errno != EWOULDBLOCK) { 00245 err(1, "Couldnt read from UART %s", address_); 00246 } 00247 } 00248 else if(bytes > 0) { 00249 // <debug> 00250 /* 00251 for(size_t i=0; i<bytes; i++) { 00252 std::cout << (char)buffer[i] << " (" << std::hex << (int)buffer[i] << ") "; 00253 } 00254 std::cout << "\n"; 00255 std::cout << std::dec; 00256 */ 00257 // </debug> 00258 00259 self_type::notify_receivers(bytes, buffer); 00260 00261 #if PC_COM_UART_DEBUG >= 100 00262 std::cout << "[pc_com_uart] try_read read " << bytes << " bytes.\n"; 00263 #endif 00264 00265 } 00266 00267 // Unblock SIGALRM. 00268 if( sigismember( &old_signal_set, SIGALRM ) == 0 ) 00269 { 00270 if ( ( sigemptyset( &signal_set ) == -1 ) || 00271 ( sigaddset( &signal_set, SIGALRM ) == -1 ) || 00272 pthread_sigmask( SIG_UNBLOCK, &signal_set, 0 ) ) 00273 { 00274 perror( "Failed to unblock SIGALRM" ); 00275 } 00276 } 00277 00278 timer_.template set_timer<self_type, &self_type::try_read>(10, this, 0); 00279 } // try_read 00280 00281 } // ns wiselib 00282 00283 #endif // PC_COM_UART_H 00284