Home | History | Annotate | Download | only in serial
      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