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.
BitrateToSpeedConstant(int bitrate,speed_t * speed)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.
SpeedConstantToBitrate(speed_t speed,int * bitrate)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
SetCustomBitrate(base::PlatformFile file,struct termios * config,int bitrate)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
ConfigurePort(const api::serial::ConnectionOptions & options)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
PostOpen()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
Flush() const200 bool SerialConnection::Flush() const {
201 return tcflush(file_.GetPlatformFile(), TCIOFLUSH) == 0;
202 }
203
GetControlSignals(api::serial::DeviceControlSignals * signals) const204 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
SetControlSignals(const api::serial::HostControlSignals & signals)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
GetPortInfo(api::serial::ConnectionInfo * info) const245 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
MaybeFixUpPortName(const std::string & port_name)279 std::string SerialConnection::MaybeFixUpPortName(
280 const std::string &port_name) {
281 return port_name;
282 }
283
284 } // namespace extensions
285