Home | History | Annotate | Download | only in serial
      1 // Copyright 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_.GetPlatformFile(), &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_.GetPlatformFile(), &config,
    129                               *options.bitrate)) {
    130           return false;
    131         }
    132       }
    133     }
    134   }
    135   if (options.data_bits != api::serial::DATA_BITS_NONE) {
    136     config.c_cflag &= ~CSIZE;
    137     switch (options.data_bits) {
    138       case api::serial::DATA_BITS_SEVEN:
    139         config.c_cflag |= CS7;
    140         break;
    141       case api::serial::DATA_BITS_EIGHT:
    142       default:
    143         config.c_cflag |= CS8;
    144         break;
    145     }
    146   }
    147   if (options.parity_bit != api::serial::PARITY_BIT_NONE) {
    148     switch (options.parity_bit) {
    149       case api::serial::PARITY_BIT_EVEN:
    150         config.c_cflag |= PARENB;
    151         config.c_cflag &= ~PARODD;
    152         break;
    153       case api::serial::PARITY_BIT_ODD:
    154         config.c_cflag |= (PARODD | PARENB);
    155         break;
    156       case api::serial::PARITY_BIT_NO:
    157       default:
    158         config.c_cflag &= ~(PARODD | PARENB);
    159         break;
    160     }
    161   }
    162   if (options.stop_bits != api::serial::STOP_BITS_NONE) {
    163     switch (options.stop_bits) {
    164       case api::serial::STOP_BITS_TWO:
    165         config.c_cflag |= CSTOPB;
    166         break;
    167       case api::serial::STOP_BITS_ONE:
    168       default:
    169         config.c_cflag &= ~CSTOPB;
    170         break;
    171     }
    172   }
    173   if (options.cts_flow_control.get()) {
    174     if (*options.cts_flow_control){
    175       config.c_cflag |= CRTSCTS;
    176     } else {
    177       config.c_cflag &= ~CRTSCTS;
    178     }
    179   }
    180   return tcsetattr(file_.GetPlatformFile(), TCSANOW, &config) == 0;
    181 }
    182 
    183 bool SerialConnection::PostOpen() {
    184   struct termios config;
    185   tcgetattr(file_.GetPlatformFile(), &config);
    186 
    187   // Set flags for 'raw' operation
    188   config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
    189   config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
    190                       ICRNL | IXON);
    191   config.c_oflag &= ~OPOST;
    192 
    193   // CLOCAL causes the system to disregard the DCD signal state.
    194   // CREAD enables reading from the port.
    195   config.c_cflag |= (CLOCAL | CREAD);
    196 
    197   return tcsetattr(file_.GetPlatformFile(), TCSANOW, &config) == 0;
    198 }
    199 
    200 bool SerialConnection::Flush() const {
    201   return tcflush(file_.GetPlatformFile(), TCIOFLUSH) == 0;
    202 }
    203 
    204 bool SerialConnection::GetControlSignals(
    205     api::serial::DeviceControlSignals* signals) const {
    206   int status;
    207   if (ioctl(file_.GetPlatformFile(), TIOCMGET, &status) == -1) {
    208     return false;
    209   }
    210 
    211   signals->dcd = (status & TIOCM_CAR) != 0;
    212   signals->cts = (status & TIOCM_CTS) != 0;
    213   signals->dsr = (status & TIOCM_DSR) != 0;
    214   signals->ri = (status & TIOCM_RI) != 0;
    215   return true;
    216 }
    217 
    218 bool SerialConnection::SetControlSignals(
    219     const api::serial::HostControlSignals& signals) {
    220   int status;
    221 
    222   if (ioctl(file_.GetPlatformFile(), TIOCMGET, &status) == -1) {
    223     return false;
    224   }
    225 
    226   if (signals.dtr.get()) {
    227     if (*signals.dtr) {
    228       status |= TIOCM_DTR;
    229     } else {
    230       status &= ~TIOCM_DTR;
    231     }
    232   }
    233 
    234   if (signals.rts.get()) {
    235     if (*signals.rts){
    236       status |= TIOCM_RTS;
    237     } else{
    238       status &= ~TIOCM_RTS;
    239     }
    240   }
    241 
    242   return ioctl(file_.GetPlatformFile(), TIOCMSET, &status) == 0;
    243 }
    244 
    245 bool SerialConnection::GetPortInfo(api::serial::ConnectionInfo* info) const {
    246   struct termios config;
    247   if (tcgetattr(file_.GetPlatformFile(), &config) == -1) {
    248     return false;
    249   }
    250   speed_t ispeed = cfgetispeed(&config);
    251   speed_t ospeed = cfgetospeed(&config);
    252   if (ispeed == ospeed) {
    253     int bitrate = 0;
    254     if (SpeedConstantToBitrate(ispeed, &bitrate)) {
    255       info->bitrate.reset(new int(bitrate));
    256     } else if (ispeed > 0) {
    257       info->bitrate.reset(new int(static_cast<int>(ispeed)));
    258     }
    259   }
    260   if ((config.c_cflag & CSIZE) == CS7) {
    261     info->data_bits = api::serial::DATA_BITS_SEVEN;
    262   } else if ((config.c_cflag & CSIZE) == CS8) {
    263     info->data_bits = api::serial::DATA_BITS_EIGHT;
    264   } else {
    265     info->data_bits = api::serial::DATA_BITS_NONE;
    266   }
    267   if (config.c_cflag & PARENB) {
    268     info->parity_bit = (config.c_cflag & PARODD) ? api::serial::PARITY_BIT_ODD
    269                                                  : api::serial::PARITY_BIT_EVEN;
    270   } else {
    271     info->parity_bit = api::serial::PARITY_BIT_NO;
    272   }
    273   info->stop_bits = (config.c_cflag & CSTOPB) ? api::serial::STOP_BITS_TWO
    274                                               : api::serial::STOP_BITS_ONE;
    275   info->cts_flow_control.reset(new bool((config.c_cflag & CRTSCTS) != 0));
    276   return true;
    277 }
    278 
    279 std::string SerialConnection::MaybeFixUpPortName(
    280     const std::string &port_name) {
    281   return port_name;
    282 }
    283 
    284 }  // namespace extensions
    285