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