1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "device/serial/serial_io_handler_posix.h" 6 7 #include <sys/ioctl.h> 8 #include <termios.h> 9 10 #include "base/posix/eintr_wrapper.h" 11 12 #if defined(OS_LINUX) 13 #include <linux/serial.h> 14 #endif 15 16 namespace { 17 18 // Convert an integral bit rate to a nominal one. Returns |true| 19 // if the conversion was successful and |false| otherwise. 20 bool BitrateToSpeedConstant(int bitrate, speed_t* speed) { 21 #define BITRATE_TO_SPEED_CASE(x) \ 22 case x: \ 23 *speed = B##x; \ 24 return true; 25 switch (bitrate) { 26 BITRATE_TO_SPEED_CASE(0) 27 BITRATE_TO_SPEED_CASE(50) 28 BITRATE_TO_SPEED_CASE(75) 29 BITRATE_TO_SPEED_CASE(110) 30 BITRATE_TO_SPEED_CASE(134) 31 BITRATE_TO_SPEED_CASE(150) 32 BITRATE_TO_SPEED_CASE(200) 33 BITRATE_TO_SPEED_CASE(300) 34 BITRATE_TO_SPEED_CASE(600) 35 BITRATE_TO_SPEED_CASE(1200) 36 BITRATE_TO_SPEED_CASE(1800) 37 BITRATE_TO_SPEED_CASE(2400) 38 BITRATE_TO_SPEED_CASE(4800) 39 BITRATE_TO_SPEED_CASE(9600) 40 BITRATE_TO_SPEED_CASE(19200) 41 BITRATE_TO_SPEED_CASE(38400) 42 #if defined(OS_POSIX) && !defined(OS_MACOSX) 43 BITRATE_TO_SPEED_CASE(57600) 44 BITRATE_TO_SPEED_CASE(115200) 45 BITRATE_TO_SPEED_CASE(230400) 46 BITRATE_TO_SPEED_CASE(460800) 47 BITRATE_TO_SPEED_CASE(576000) 48 BITRATE_TO_SPEED_CASE(921600) 49 #endif 50 default: 51 return false; 52 } 53 #undef BITRATE_TO_SPEED_CASE 54 } 55 56 // Convert a known nominal speed into an integral bitrate. Returns |true| 57 // if the conversion was successful and |false| otherwise. 58 bool SpeedConstantToBitrate(speed_t speed, int* bitrate) { 59 #define SPEED_TO_BITRATE_CASE(x) \ 60 case B##x: \ 61 *bitrate = x; \ 62 return true; 63 switch (speed) { 64 SPEED_TO_BITRATE_CASE(0) 65 SPEED_TO_BITRATE_CASE(50) 66 SPEED_TO_BITRATE_CASE(75) 67 SPEED_TO_BITRATE_CASE(110) 68 SPEED_TO_BITRATE_CASE(134) 69 SPEED_TO_BITRATE_CASE(150) 70 SPEED_TO_BITRATE_CASE(200) 71 SPEED_TO_BITRATE_CASE(300) 72 SPEED_TO_BITRATE_CASE(600) 73 SPEED_TO_BITRATE_CASE(1200) 74 SPEED_TO_BITRATE_CASE(1800) 75 SPEED_TO_BITRATE_CASE(2400) 76 SPEED_TO_BITRATE_CASE(4800) 77 SPEED_TO_BITRATE_CASE(9600) 78 SPEED_TO_BITRATE_CASE(19200) 79 SPEED_TO_BITRATE_CASE(38400) 80 #if defined(OS_POSIX) && !defined(OS_MACOSX) 81 SPEED_TO_BITRATE_CASE(57600) 82 SPEED_TO_BITRATE_CASE(115200) 83 SPEED_TO_BITRATE_CASE(230400) 84 SPEED_TO_BITRATE_CASE(460800) 85 SPEED_TO_BITRATE_CASE(576000) 86 SPEED_TO_BITRATE_CASE(921600) 87 #endif 88 default: 89 return false; 90 } 91 #undef SPEED_TO_BITRATE_CASE 92 } 93 94 bool SetCustomBitrate(base::PlatformFile file, 95 struct termios* config, 96 int bitrate) { 97 #if defined(OS_LINUX) 98 struct serial_struct serial; 99 if (ioctl(file, TIOCGSERIAL, &serial) < 0) { 100 return false; 101 } 102 serial.flags = (serial.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST; 103 serial.custom_divisor = serial.baud_base / bitrate; 104 if (serial.custom_divisor < 1) { 105 serial.custom_divisor = 1; 106 } 107 cfsetispeed(config, B38400); 108 cfsetospeed(config, B38400); 109 return ioctl(file, TIOCSSERIAL, &serial) >= 0; 110 #elif defined(OS_MACOSX) 111 speed_t speed = static_cast<speed_t>(bitrate); 112 cfsetispeed(config, speed); 113 cfsetospeed(config, speed); 114 return true; 115 #else 116 return false; 117 #endif 118 } 119 120 } // namespace 121 122 namespace device { 123 124 // static 125 scoped_refptr<SerialIoHandler> SerialIoHandler::Create( 126 scoped_refptr<base::MessageLoopProxy> file_thread_message_loop) { 127 return new SerialIoHandlerPosix(file_thread_message_loop); 128 } 129 130 void SerialIoHandlerPosix::ReadImpl() { 131 DCHECK(CalledOnValidThread()); 132 DCHECK(pending_read_buffer()); 133 DCHECK(file().IsValid()); 134 135 EnsureWatchingReads(); 136 } 137 138 void SerialIoHandlerPosix::WriteImpl() { 139 DCHECK(CalledOnValidThread()); 140 DCHECK(pending_write_buffer()); 141 DCHECK(file().IsValid()); 142 143 EnsureWatchingWrites(); 144 } 145 146 void SerialIoHandlerPosix::CancelReadImpl() { 147 DCHECK(CalledOnValidThread()); 148 is_watching_reads_ = false; 149 file_read_watcher_.StopWatchingFileDescriptor(); 150 QueueReadCompleted(0, read_cancel_reason()); 151 } 152 153 void SerialIoHandlerPosix::CancelWriteImpl() { 154 DCHECK(CalledOnValidThread()); 155 is_watching_writes_ = false; 156 file_write_watcher_.StopWatchingFileDescriptor(); 157 QueueWriteCompleted(0, write_cancel_reason()); 158 } 159 160 SerialIoHandlerPosix::SerialIoHandlerPosix( 161 scoped_refptr<base::MessageLoopProxy> file_thread_message_loop) 162 : SerialIoHandler(file_thread_message_loop), 163 is_watching_reads_(false), 164 is_watching_writes_(false) { 165 } 166 167 SerialIoHandlerPosix::~SerialIoHandlerPosix() { 168 } 169 170 void SerialIoHandlerPosix::OnFileCanReadWithoutBlocking(int fd) { 171 DCHECK(CalledOnValidThread()); 172 DCHECK_EQ(fd, file().GetPlatformFile()); 173 174 if (pending_read_buffer()) { 175 int bytes_read = HANDLE_EINTR(read(file().GetPlatformFile(), 176 pending_read_buffer(), 177 pending_read_buffer_len())); 178 if (bytes_read < 0) { 179 if (errno == ENXIO) { 180 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST); 181 } else { 182 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR); 183 } 184 } else if (bytes_read == 0) { 185 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST); 186 } else { 187 ReadCompleted(bytes_read, serial::RECEIVE_ERROR_NONE); 188 } 189 } else { 190 // Stop watching the fd if we get notifications with no pending 191 // reads or writes to avoid starving the message loop. 192 is_watching_reads_ = false; 193 file_read_watcher_.StopWatchingFileDescriptor(); 194 } 195 } 196 197 void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd) { 198 DCHECK(CalledOnValidThread()); 199 DCHECK_EQ(fd, file().GetPlatformFile()); 200 201 if (pending_write_buffer()) { 202 int bytes_written = HANDLE_EINTR(write(file().GetPlatformFile(), 203 pending_write_buffer(), 204 pending_write_buffer_len())); 205 if (bytes_written < 0) { 206 WriteCompleted(0, serial::SEND_ERROR_SYSTEM_ERROR); 207 } else { 208 WriteCompleted(bytes_written, serial::SEND_ERROR_NONE); 209 } 210 } else { 211 // Stop watching the fd if we get notifications with no pending 212 // writes to avoid starving the message loop. 213 is_watching_writes_ = false; 214 file_write_watcher_.StopWatchingFileDescriptor(); 215 } 216 } 217 218 void SerialIoHandlerPosix::EnsureWatchingReads() { 219 DCHECK(CalledOnValidThread()); 220 DCHECK(file().IsValid()); 221 if (!is_watching_reads_) { 222 is_watching_reads_ = base::MessageLoopForIO::current()->WatchFileDescriptor( 223 file().GetPlatformFile(), 224 true, 225 base::MessageLoopForIO::WATCH_READ, 226 &file_read_watcher_, 227 this); 228 } 229 } 230 231 void SerialIoHandlerPosix::EnsureWatchingWrites() { 232 DCHECK(CalledOnValidThread()); 233 DCHECK(file().IsValid()); 234 if (!is_watching_writes_) { 235 is_watching_writes_ = 236 base::MessageLoopForIO::current()->WatchFileDescriptor( 237 file().GetPlatformFile(), 238 true, 239 base::MessageLoopForIO::WATCH_WRITE, 240 &file_write_watcher_, 241 this); 242 } 243 } 244 245 bool SerialIoHandlerPosix::ConfigurePort( 246 const serial::ConnectionOptions& options) { 247 struct termios config; 248 tcgetattr(file().GetPlatformFile(), &config); 249 if (options.bitrate) { 250 speed_t bitrate_opt = B0; 251 if (BitrateToSpeedConstant(options.bitrate, &bitrate_opt)) { 252 cfsetispeed(&config, bitrate_opt); 253 cfsetospeed(&config, bitrate_opt); 254 } else { 255 // Attempt to set a custom speed. 256 if (!SetCustomBitrate( 257 file().GetPlatformFile(), &config, options.bitrate)) { 258 return false; 259 } 260 } 261 } 262 if (options.data_bits != serial::DATA_BITS_NONE) { 263 config.c_cflag &= ~CSIZE; 264 switch (options.data_bits) { 265 case serial::DATA_BITS_SEVEN: 266 config.c_cflag |= CS7; 267 break; 268 case serial::DATA_BITS_EIGHT: 269 default: 270 config.c_cflag |= CS8; 271 break; 272 } 273 } 274 if (options.parity_bit != serial::PARITY_BIT_NONE) { 275 switch (options.parity_bit) { 276 case serial::PARITY_BIT_EVEN: 277 config.c_cflag |= PARENB; 278 config.c_cflag &= ~PARODD; 279 break; 280 case serial::PARITY_BIT_ODD: 281 config.c_cflag |= (PARODD | PARENB); 282 break; 283 case serial::PARITY_BIT_NO: 284 default: 285 config.c_cflag &= ~(PARODD | PARENB); 286 break; 287 } 288 } 289 if (options.stop_bits != serial::STOP_BITS_NONE) { 290 switch (options.stop_bits) { 291 case serial::STOP_BITS_TWO: 292 config.c_cflag |= CSTOPB; 293 break; 294 case serial::STOP_BITS_ONE: 295 default: 296 config.c_cflag &= ~CSTOPB; 297 break; 298 } 299 } 300 if (options.has_cts_flow_control) { 301 if (options.cts_flow_control) { 302 config.c_cflag |= CRTSCTS; 303 } else { 304 config.c_cflag &= ~CRTSCTS; 305 } 306 } 307 return tcsetattr(file().GetPlatformFile(), TCSANOW, &config) == 0; 308 } 309 310 bool SerialIoHandlerPosix::PostOpen() { 311 struct termios config; 312 tcgetattr(file().GetPlatformFile(), &config); 313 314 // Set flags for 'raw' operation 315 config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG); 316 config.c_iflag &= 317 ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); 318 config.c_oflag &= ~OPOST; 319 320 // CLOCAL causes the system to disregard the DCD signal state. 321 // CREAD enables reading from the port. 322 config.c_cflag |= (CLOCAL | CREAD); 323 324 return tcsetattr(file().GetPlatformFile(), TCSANOW, &config) == 0; 325 } 326 327 bool SerialIoHandlerPosix::Flush() const { 328 return tcflush(file().GetPlatformFile(), TCIOFLUSH) == 0; 329 } 330 331 serial::DeviceControlSignalsPtr SerialIoHandlerPosix::GetControlSignals() 332 const { 333 int status; 334 if (ioctl(file().GetPlatformFile(), TIOCMGET, &status) == -1) { 335 return serial::DeviceControlSignalsPtr(); 336 } 337 338 serial::DeviceControlSignalsPtr signals(serial::DeviceControlSignals::New()); 339 signals->dcd = (status & TIOCM_CAR) != 0; 340 signals->cts = (status & TIOCM_CTS) != 0; 341 signals->dsr = (status & TIOCM_DSR) != 0; 342 signals->ri = (status & TIOCM_RI) != 0; 343 return signals.Pass(); 344 } 345 346 bool SerialIoHandlerPosix::SetControlSignals( 347 const serial::HostControlSignals& signals) { 348 int status; 349 350 if (ioctl(file().GetPlatformFile(), TIOCMGET, &status) == -1) { 351 return false; 352 } 353 354 if (signals.has_dtr) { 355 if (signals.dtr) { 356 status |= TIOCM_DTR; 357 } else { 358 status &= ~TIOCM_DTR; 359 } 360 } 361 362 if (signals.has_rts) { 363 if (signals.rts) { 364 status |= TIOCM_RTS; 365 } else { 366 status &= ~TIOCM_RTS; 367 } 368 } 369 370 return ioctl(file().GetPlatformFile(), TIOCMSET, &status) == 0; 371 } 372 373 serial::ConnectionInfoPtr SerialIoHandlerPosix::GetPortInfo() const { 374 struct termios config; 375 if (tcgetattr(file().GetPlatformFile(), &config) == -1) { 376 return serial::ConnectionInfoPtr(); 377 } 378 serial::ConnectionInfoPtr info(serial::ConnectionInfo::New()); 379 speed_t ispeed = cfgetispeed(&config); 380 speed_t ospeed = cfgetospeed(&config); 381 if (ispeed == ospeed) { 382 int bitrate = 0; 383 if (SpeedConstantToBitrate(ispeed, &bitrate)) { 384 info->bitrate = bitrate; 385 } else if (ispeed > 0) { 386 info->bitrate = static_cast<int>(ispeed); 387 } 388 } 389 if ((config.c_cflag & CSIZE) == CS7) { 390 info->data_bits = serial::DATA_BITS_SEVEN; 391 } else if ((config.c_cflag & CSIZE) == CS8) { 392 info->data_bits = serial::DATA_BITS_EIGHT; 393 } else { 394 info->data_bits = serial::DATA_BITS_NONE; 395 } 396 if (config.c_cflag & PARENB) { 397 info->parity_bit = (config.c_cflag & PARODD) ? serial::PARITY_BIT_ODD 398 : serial::PARITY_BIT_EVEN; 399 } else { 400 info->parity_bit = serial::PARITY_BIT_NO; 401 } 402 info->stop_bits = 403 (config.c_cflag & CSTOPB) ? serial::STOP_BITS_TWO : serial::STOP_BITS_ONE; 404 info->cts_flow_control = (config.c_cflag & CRTSCTS) != 0; 405 return info.Pass(); 406 } 407 408 std::string SerialIoHandler::MaybeFixUpPortName(const std::string& port_name) { 409 return port_name; 410 } 411 412 } // namespace device 413