Home | History | Annotate | Download | only in serial
      1 // Copyright (c) 2012 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 "chrome/browser/extensions/api/serial/serial_connection.h"
      6 
      7 #include <sys/ioctl.h>
      8 #include <termios.h>
      9 
     10 #if defined(OS_LINUX)
     11 #include <linux/serial.h>
     12 #endif
     13 
     14 namespace extensions {
     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) case x: *speed = B ## x; return true;
     22   switch (bitrate) {
     23     BITRATE_TO_SPEED_CASE(0)
     24     BITRATE_TO_SPEED_CASE(50)
     25     BITRATE_TO_SPEED_CASE(75)
     26     BITRATE_TO_SPEED_CASE(110)
     27     BITRATE_TO_SPEED_CASE(134)
     28     BITRATE_TO_SPEED_CASE(150)
     29     BITRATE_TO_SPEED_CASE(200)
     30     BITRATE_TO_SPEED_CASE(300)
     31     BITRATE_TO_SPEED_CASE(600)
     32     BITRATE_TO_SPEED_CASE(1200)
     33     BITRATE_TO_SPEED_CASE(1800)
     34     BITRATE_TO_SPEED_CASE(2400)
     35     BITRATE_TO_SPEED_CASE(4800)
     36     BITRATE_TO_SPEED_CASE(9600)
     37     BITRATE_TO_SPEED_CASE(19200)
     38     BITRATE_TO_SPEED_CASE(38400)
     39 #if defined(OS_POSIX) && !defined(OS_MACOSX)
     40     BITRATE_TO_SPEED_CASE(57600)
     41     BITRATE_TO_SPEED_CASE(115200)
     42     BITRATE_TO_SPEED_CASE(230400)
     43     BITRATE_TO_SPEED_CASE(460800)
     44     BITRATE_TO_SPEED_CASE(576000)
     45     BITRATE_TO_SPEED_CASE(921600)
     46 #endif
     47     default:
     48       return false;
     49   }
     50 #undef BITRATE_TO_SPEED_CASE
     51 }
     52 
     53 // Convert a known nominal speed into an integral bitrate. Returns |true|
     54 // if the conversion was successful and |false| otherwise.
     55 bool SpeedConstantToBitrate(speed_t speed, int* bitrate) {
     56 #define SPEED_TO_BITRATE_CASE(x) case B ## x: *bitrate = x; return true;
     57   switch (speed) {
     58     SPEED_TO_BITRATE_CASE(0)
     59     SPEED_TO_BITRATE_CASE(50)
     60     SPEED_TO_BITRATE_CASE(75)
     61     SPEED_TO_BITRATE_CASE(110)
     62     SPEED_TO_BITRATE_CASE(134)
     63     SPEED_TO_BITRATE_CASE(150)
     64     SPEED_TO_BITRATE_CASE(200)
     65     SPEED_TO_BITRATE_CASE(300)
     66     SPEED_TO_BITRATE_CASE(600)
     67     SPEED_TO_BITRATE_CASE(1200)
     68     SPEED_TO_BITRATE_CASE(1800)
     69     SPEED_TO_BITRATE_CASE(2400)
     70     SPEED_TO_BITRATE_CASE(4800)
     71     SPEED_TO_BITRATE_CASE(9600)
     72     SPEED_TO_BITRATE_CASE(19200)
     73     SPEED_TO_BITRATE_CASE(38400)
     74 #if defined(OS_POSIX) && !defined(OS_MACOSX)
     75     SPEED_TO_BITRATE_CASE(57600)
     76     SPEED_TO_BITRATE_CASE(115200)
     77     SPEED_TO_BITRATE_CASE(230400)
     78     SPEED_TO_BITRATE_CASE(460800)
     79     SPEED_TO_BITRATE_CASE(576000)
     80     SPEED_TO_BITRATE_CASE(921600)
     81 #endif
     82     default:
     83       return false;
     84   }
     85 #undef SPEED_TO_BITRATE_CASE
     86 }
     87 
     88 bool SetCustomBitrate(base::PlatformFile file,
     89                       struct termios* config,
     90                       int bitrate) {
     91 #if defined(OS_LINUX)
     92   struct serial_struct serial;
     93   if (ioctl(file, TIOCGSERIAL, &serial) < 0) {
     94     return false;
     95   }
     96   serial.flags = (serial.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
     97   serial.custom_divisor = serial.baud_base / bitrate;
     98   if (serial.custom_divisor < 1) {
     99     serial.custom_divisor = 1;
    100   }
    101   cfsetispeed(config, B38400);
    102   cfsetospeed(config, B38400);
    103   return ioctl(file, TIOCSSERIAL, &serial) >= 0;
    104 #elif defined(OS_MACOSX)
    105   speed_t speed = static_cast<speed_t>(bitrate);
    106   cfsetispeed(config, speed);
    107   cfsetospeed(config, speed);
    108   return true;
    109 #else
    110   return false;
    111 #endif
    112 }
    113 
    114 }  // namespace
    115 
    116 bool SerialConnection::ConfigurePort(
    117     const api::serial::ConnectionOptions& options) {
    118   struct termios config;
    119   tcgetattr(file_, &config);
    120   if (options.bitrate.get()) {
    121     if (*options.bitrate >= 0) {
    122       speed_t bitrate_opt = B0;
    123       if (BitrateToSpeedConstant(*options.bitrate, &bitrate_opt)) {
    124         cfsetispeed(&config, bitrate_opt);
    125         cfsetospeed(&config, bitrate_opt);
    126       } else {
    127         // Attempt to set a custom speed.
    128         if (!SetCustomBitrate(file_, &config, *options.bitrate)) {
    129           return false;
    130         }
    131       }
    132     }
    133   }
    134   if (options.data_bits != api::serial::DATA_BITS_NONE) {
    135     config.c_cflag &= ~CSIZE;
    136     switch (options.data_bits) {
    137       case api::serial::DATA_BITS_SEVEN:
    138         config.c_cflag |= CS7;
    139         break;
    140       case api::serial::DATA_BITS_EIGHT:
    141       default:
    142         config.c_cflag |= CS8;
    143         break;
    144     }
    145   }
    146   if (options.parity_bit != api::serial::PARITY_BIT_NONE) {
    147     switch (options.parity_bit) {
    148       case api::serial::PARITY_BIT_EVEN:
    149         config.c_cflag |= PARENB;
    150         config.c_cflag &= ~PARODD;
    151         break;
    152       case api::serial::PARITY_BIT_ODD:
    153         config.c_cflag |= (PARODD | PARENB);
    154         break;
    155       case api::serial::PARITY_BIT_NO:
    156       default:
    157         config.c_cflag &= ~(PARODD | PARENB);
    158         break;
    159     }
    160   }
    161   if (options.stop_bits != api::serial::STOP_BITS_NONE) {
    162     switch (options.stop_bits) {
    163       case api::serial::STOP_BITS_TWO:
    164         config.c_cflag |= CSTOPB;
    165         break;
    166       case api::serial::STOP_BITS_ONE:
    167       default:
    168         config.c_cflag &= ~CSTOPB;
    169         break;
    170     }
    171   }
    172   if (options.cts_flow_control.get()) {
    173     if (*options.cts_flow_control){
    174       config.c_cflag |= CRTSCTS;
    175     } else {
    176       config.c_cflag &= ~CRTSCTS;
    177     }
    178   }
    179   return tcsetattr(file_, TCSANOW, &config) == 0;
    180 }
    181 
    182 bool SerialConnection::PostOpen() {
    183   struct termios config;
    184   tcgetattr(file_, &config);
    185 
    186   // Set flags for 'raw' operation
    187   config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
    188   config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
    189                       ICRNL | IXON);
    190   config.c_oflag &= ~OPOST;
    191 
    192   // CLOCAL causes the system to disregard the DCD signal state.
    193   // CREAD enables reading from the port.
    194   config.c_cflag |= (CLOCAL | CREAD);
    195 
    196   return tcsetattr(file_, TCSANOW, &config) == 0;
    197 }
    198 
    199 bool SerialConnection::Flush() const {
    200   return tcflush(file_, TCIOFLUSH) == 0;
    201 }
    202 
    203 bool SerialConnection::GetControlSignals(
    204     api::serial::DeviceControlSignals* signals) const {
    205   int status;
    206   if (ioctl(file_, TIOCMGET, &status) == -1) {
    207     return false;
    208   }
    209 
    210   signals->dcd = (status & TIOCM_CAR) != 0;
    211   signals->cts = (status & TIOCM_CTS) != 0;
    212   signals->dsr = (status & TIOCM_DSR) != 0;
    213   signals->ri = (status & TIOCM_RI) != 0;
    214   return true;
    215 }
    216 
    217 bool SerialConnection::SetControlSignals(
    218     const api::serial::HostControlSignals& signals) {
    219   int status;
    220 
    221   if (ioctl(file_, TIOCMGET, &status) == -1) {
    222     return false;
    223   }
    224 
    225   if (signals.dtr.get()) {
    226     if (*signals.dtr) {
    227       status |= TIOCM_DTR;
    228     } else {
    229       status &= ~TIOCM_DTR;
    230     }
    231   }
    232 
    233   if (signals.rts.get()) {
    234     if (*signals.rts){
    235       status |= TIOCM_RTS;
    236     } else{
    237       status &= ~TIOCM_RTS;
    238     }
    239   }
    240 
    241   return ioctl(file_, TIOCMSET, &status) == 0;
    242 }
    243 
    244 bool SerialConnection::GetPortInfo(api::serial::ConnectionInfo* info) const {
    245   struct termios config;
    246   if (tcgetattr(file_, &config) == -1) {
    247     return false;
    248   }
    249   speed_t ispeed = cfgetispeed(&config);
    250   speed_t ospeed = cfgetospeed(&config);
    251   if (ispeed == ospeed) {
    252     int bitrate = 0;
    253     if (SpeedConstantToBitrate(ispeed, &bitrate)) {
    254       info->bitrate.reset(new int(bitrate));
    255     } else if (ispeed > 0) {
    256       info->bitrate.reset(new int(static_cast<int>(ispeed)));
    257     }
    258   }
    259   if ((config.c_cflag & CSIZE) == CS7) {
    260     info->data_bits = api::serial::DATA_BITS_SEVEN;
    261   } else if ((config.c_cflag & CSIZE) == CS8) {
    262     info->data_bits = api::serial::DATA_BITS_EIGHT;
    263   } else {
    264     info->data_bits = api::serial::DATA_BITS_NONE;
    265   }
    266   if (config.c_cflag & PARENB) {
    267     info->parity_bit = (config.c_cflag & PARODD) ? api::serial::PARITY_BIT_ODD
    268                                                  : api::serial::PARITY_BIT_EVEN;
    269   } else {
    270     info->parity_bit = api::serial::PARITY_BIT_NO;
    271   }
    272   info->stop_bits = (config.c_cflag & CSTOPB) ? api::serial::STOP_BITS_TWO
    273                                               : api::serial::STOP_BITS_ONE;
    274   info->cts_flow_control.reset(new bool((config.c_cflag & CRTSCTS) != 0));
    275   return true;
    276 }
    277 
    278 std::string SerialConnection::MaybeFixUpPortName(
    279     const std::string &port_name) {
    280   return port_name;
    281 }
    282 
    283 }  // namespace extensions
    284