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.
5 #include "device/serial/serial_io_handler_posix.h"
10 #include "base/posix/eintr_wrapper.h"
13 #include <linux/serial.h>
15 // The definition of struct termios2 is copied from asm-generic/termbits.h
16 // because including that header directly conflicts with termios.h.
19 tcflag_t c_iflag
; // input mode flags
20 tcflag_t c_oflag
; // output mode flags
21 tcflag_t c_cflag
; // control mode flags
22 tcflag_t c_lflag
; // local mode flags
23 cc_t c_line
; // line discipline
24 cc_t c_cc
[19]; // control characters
25 speed_t c_ispeed
; // input speed
26 speed_t c_ospeed
; // output speed
30 #endif // defined(OS_LINUX)
32 #if defined(OS_MACOSX)
33 #include <IOKit/serial/ioss.h>
38 // Convert an integral bit rate to a nominal one. Returns |true|
39 // if the conversion was successful and |false| otherwise.
40 bool BitrateToSpeedConstant(int bitrate
, speed_t
* speed
) {
41 #define BITRATE_TO_SPEED_CASE(x) \
46 BITRATE_TO_SPEED_CASE(0)
47 BITRATE_TO_SPEED_CASE(50)
48 BITRATE_TO_SPEED_CASE(75)
49 BITRATE_TO_SPEED_CASE(110)
50 BITRATE_TO_SPEED_CASE(134)
51 BITRATE_TO_SPEED_CASE(150)
52 BITRATE_TO_SPEED_CASE(200)
53 BITRATE_TO_SPEED_CASE(300)
54 BITRATE_TO_SPEED_CASE(600)
55 BITRATE_TO_SPEED_CASE(1200)
56 BITRATE_TO_SPEED_CASE(1800)
57 BITRATE_TO_SPEED_CASE(2400)
58 BITRATE_TO_SPEED_CASE(4800)
59 BITRATE_TO_SPEED_CASE(9600)
60 BITRATE_TO_SPEED_CASE(19200)
61 BITRATE_TO_SPEED_CASE(38400)
62 #if !defined(OS_MACOSX)
63 BITRATE_TO_SPEED_CASE(57600)
64 BITRATE_TO_SPEED_CASE(115200)
65 BITRATE_TO_SPEED_CASE(230400)
66 BITRATE_TO_SPEED_CASE(460800)
67 BITRATE_TO_SPEED_CASE(576000)
68 BITRATE_TO_SPEED_CASE(921600)
73 #undef BITRATE_TO_SPEED_CASE
76 #if !defined(OS_LINUX)
77 // Convert a known nominal speed into an integral bitrate. Returns |true|
78 // if the conversion was successful and |false| otherwise.
79 bool SpeedConstantToBitrate(speed_t speed
, int* bitrate
) {
80 #define SPEED_TO_BITRATE_CASE(x) \
85 SPEED_TO_BITRATE_CASE(0)
86 SPEED_TO_BITRATE_CASE(50)
87 SPEED_TO_BITRATE_CASE(75)
88 SPEED_TO_BITRATE_CASE(110)
89 SPEED_TO_BITRATE_CASE(134)
90 SPEED_TO_BITRATE_CASE(150)
91 SPEED_TO_BITRATE_CASE(200)
92 SPEED_TO_BITRATE_CASE(300)
93 SPEED_TO_BITRATE_CASE(600)
94 SPEED_TO_BITRATE_CASE(1200)
95 SPEED_TO_BITRATE_CASE(1800)
96 SPEED_TO_BITRATE_CASE(2400)
97 SPEED_TO_BITRATE_CASE(4800)
98 SPEED_TO_BITRATE_CASE(9600)
99 SPEED_TO_BITRATE_CASE(19200)
100 SPEED_TO_BITRATE_CASE(38400)
104 #undef SPEED_TO_BITRATE_CASE
113 scoped_refptr
<SerialIoHandler
> SerialIoHandler::Create(
114 scoped_refptr
<base::SingleThreadTaskRunner
> file_thread_task_runner
,
115 scoped_refptr
<base::SingleThreadTaskRunner
> ui_thread_task_runner
) {
116 return new SerialIoHandlerPosix(file_thread_task_runner
,
117 ui_thread_task_runner
);
120 void SerialIoHandlerPosix::ReadImpl() {
121 DCHECK(CalledOnValidThread());
122 DCHECK(pending_read_buffer());
123 DCHECK(file().IsValid());
125 EnsureWatchingReads();
128 void SerialIoHandlerPosix::WriteImpl() {
129 DCHECK(CalledOnValidThread());
130 DCHECK(pending_write_buffer());
131 DCHECK(file().IsValid());
133 EnsureWatchingWrites();
136 void SerialIoHandlerPosix::CancelReadImpl() {
137 DCHECK(CalledOnValidThread());
138 is_watching_reads_
= false;
139 file_read_watcher_
.StopWatchingFileDescriptor();
140 QueueReadCompleted(0, read_cancel_reason());
143 void SerialIoHandlerPosix::CancelWriteImpl() {
144 DCHECK(CalledOnValidThread());
145 is_watching_writes_
= false;
146 file_write_watcher_
.StopWatchingFileDescriptor();
147 QueueWriteCompleted(0, write_cancel_reason());
150 bool SerialIoHandlerPosix::ConfigurePortImpl() {
151 #if defined(OS_LINUX)
152 struct termios2 config
;
153 if (ioctl(file().GetPlatformFile(), TCGETS2
, &config
) < 0) {
155 struct termios config
;
156 if (tcgetattr(file().GetPlatformFile(), &config
) != 0) {
158 VPLOG(1) << "Failed to get port configuration";
162 // Set flags for 'raw' operation
163 config
.c_lflag
&= ~(ICANON
| ECHO
| ECHOE
| ECHONL
| ISIG
);
165 ~(IGNBRK
| BRKINT
| PARMRK
| ISTRIP
| INLCR
| IGNCR
| ICRNL
| IXON
);
166 config
.c_oflag
&= ~OPOST
;
168 // CLOCAL causes the system to disregard the DCD signal state.
169 // CREAD enables reading from the port.
170 config
.c_cflag
|= (CLOCAL
| CREAD
);
172 DCHECK(options().bitrate
);
173 speed_t bitrate_opt
= B0
;
174 #if defined(OS_MACOSX)
175 bool need_iossiospeed
= false;
177 if (BitrateToSpeedConstant(options().bitrate
, &bitrate_opt
)) {
178 #if defined(OS_LINUX)
179 config
.c_cflag
&= ~CBAUD
;
180 config
.c_cflag
|= bitrate_opt
;
182 cfsetispeed(&config
, bitrate_opt
);
183 cfsetospeed(&config
, bitrate_opt
);
186 // Attempt to set a custom speed.
187 #if defined(OS_LINUX)
188 config
.c_cflag
&= ~CBAUD
;
189 config
.c_cflag
|= CBAUDEX
;
190 config
.c_ispeed
= config
.c_ospeed
= options().bitrate
;
191 #elif defined(OS_MACOSX)
192 // cfsetispeed and cfsetospeed sometimes work for custom baud rates on OS
193 // X but the IOSSIOSPEED ioctl is more reliable but has to be done after
194 // the rest of the port parameters are set or else it will be overwritten.
195 need_iossiospeed
= true;
201 DCHECK(options().data_bits
!= serial::DATA_BITS_NONE
);
202 config
.c_cflag
&= ~CSIZE
;
203 switch (options().data_bits
) {
204 case serial::DATA_BITS_SEVEN
:
205 config
.c_cflag
|= CS7
;
207 case serial::DATA_BITS_EIGHT
:
209 config
.c_cflag
|= CS8
;
213 DCHECK(options().parity_bit
!= serial::PARITY_BIT_NONE
);
214 switch (options().parity_bit
) {
215 case serial::PARITY_BIT_EVEN
:
216 config
.c_cflag
|= PARENB
;
217 config
.c_cflag
&= ~PARODD
;
219 case serial::PARITY_BIT_ODD
:
220 config
.c_cflag
|= (PARODD
| PARENB
);
222 case serial::PARITY_BIT_NO
:
224 config
.c_cflag
&= ~(PARODD
| PARENB
);
228 DCHECK(options().stop_bits
!= serial::STOP_BITS_NONE
);
229 switch (options().stop_bits
) {
230 case serial::STOP_BITS_TWO
:
231 config
.c_cflag
|= CSTOPB
;
233 case serial::STOP_BITS_ONE
:
235 config
.c_cflag
&= ~CSTOPB
;
239 DCHECK(options().has_cts_flow_control
);
240 if (options().cts_flow_control
) {
241 config
.c_cflag
|= CRTSCTS
;
243 config
.c_cflag
&= ~CRTSCTS
;
246 #if defined(OS_LINUX)
247 if (ioctl(file().GetPlatformFile(), TCSETS2
, &config
) < 0) {
249 if (tcsetattr(file().GetPlatformFile(), TCSANOW
, &config
) != 0) {
251 VPLOG(1) << "Failed to set port attributes";
255 #if defined(OS_MACOSX)
256 if (need_iossiospeed
) {
257 speed_t bitrate
= options().bitrate
;
258 if (ioctl(file().GetPlatformFile(), IOSSIOSPEED
, &bitrate
) == -1) {
259 VPLOG(1) << "Failed to set custom baud rate";
268 SerialIoHandlerPosix::SerialIoHandlerPosix(
269 scoped_refptr
<base::SingleThreadTaskRunner
> file_thread_task_runner
,
270 scoped_refptr
<base::SingleThreadTaskRunner
> ui_thread_task_runner
)
271 : SerialIoHandler(file_thread_task_runner
, ui_thread_task_runner
),
272 is_watching_reads_(false),
273 is_watching_writes_(false) {
276 SerialIoHandlerPosix::~SerialIoHandlerPosix() {
279 void SerialIoHandlerPosix::OnFileCanReadWithoutBlocking(int fd
) {
280 DCHECK(CalledOnValidThread());
281 DCHECK_EQ(fd
, file().GetPlatformFile());
283 if (pending_read_buffer()) {
284 int bytes_read
= HANDLE_EINTR(read(file().GetPlatformFile(),
285 pending_read_buffer(),
286 pending_read_buffer_len()));
287 if (bytes_read
< 0) {
288 if (errno
== ENXIO
) {
289 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST
);
291 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR
);
293 } else if (bytes_read
== 0) {
294 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST
);
296 ReadCompleted(bytes_read
, serial::RECEIVE_ERROR_NONE
);
299 // Stop watching the fd if we get notifications with no pending
300 // reads or writes to avoid starving the message loop.
301 is_watching_reads_
= false;
302 file_read_watcher_
.StopWatchingFileDescriptor();
306 void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd
) {
307 DCHECK(CalledOnValidThread());
308 DCHECK_EQ(fd
, file().GetPlatformFile());
310 if (pending_write_buffer()) {
311 int bytes_written
= HANDLE_EINTR(write(file().GetPlatformFile(),
312 pending_write_buffer(),
313 pending_write_buffer_len()));
314 if (bytes_written
< 0) {
315 WriteCompleted(0, serial::SEND_ERROR_SYSTEM_ERROR
);
317 WriteCompleted(bytes_written
, serial::SEND_ERROR_NONE
);
320 // Stop watching the fd if we get notifications with no pending
321 // writes to avoid starving the message loop.
322 is_watching_writes_
= false;
323 file_write_watcher_
.StopWatchingFileDescriptor();
327 void SerialIoHandlerPosix::EnsureWatchingReads() {
328 DCHECK(CalledOnValidThread());
329 DCHECK(file().IsValid());
330 if (!is_watching_reads_
) {
331 is_watching_reads_
= base::MessageLoopForIO::current()->WatchFileDescriptor(
332 file().GetPlatformFile(),
334 base::MessageLoopForIO::WATCH_READ
,
340 void SerialIoHandlerPosix::EnsureWatchingWrites() {
341 DCHECK(CalledOnValidThread());
342 DCHECK(file().IsValid());
343 if (!is_watching_writes_
) {
344 is_watching_writes_
=
345 base::MessageLoopForIO::current()->WatchFileDescriptor(
346 file().GetPlatformFile(),
348 base::MessageLoopForIO::WATCH_WRITE
,
349 &file_write_watcher_
,
354 bool SerialIoHandlerPosix::Flush() const {
355 if (tcflush(file().GetPlatformFile(), TCIOFLUSH
) != 0) {
356 VPLOG(1) << "Failed to flush port";
362 serial::DeviceControlSignalsPtr
SerialIoHandlerPosix::GetControlSignals()
365 if (ioctl(file().GetPlatformFile(), TIOCMGET
, &status
) == -1) {
366 VPLOG(1) << "Failed to get port control signals";
367 return serial::DeviceControlSignalsPtr();
370 serial::DeviceControlSignalsPtr
signals(serial::DeviceControlSignals::New());
371 signals
->dcd
= (status
& TIOCM_CAR
) != 0;
372 signals
->cts
= (status
& TIOCM_CTS
) != 0;
373 signals
->dsr
= (status
& TIOCM_DSR
) != 0;
374 signals
->ri
= (status
& TIOCM_RI
) != 0;
375 return signals
.Pass();
378 bool SerialIoHandlerPosix::SetControlSignals(
379 const serial::HostControlSignals
& signals
) {
382 if (ioctl(file().GetPlatformFile(), TIOCMGET
, &status
) == -1) {
383 VPLOG(1) << "Failed to get port control signals";
387 if (signals
.has_dtr
) {
391 status
&= ~TIOCM_DTR
;
395 if (signals
.has_rts
) {
399 status
&= ~TIOCM_RTS
;
403 if (ioctl(file().GetPlatformFile(), TIOCMSET
, &status
) != 0) {
404 VPLOG(1) << "Failed to set port control signals";
410 serial::ConnectionInfoPtr
SerialIoHandlerPosix::GetPortInfo() const {
411 #if defined(OS_LINUX)
412 struct termios2 config
;
413 if (ioctl(file().GetPlatformFile(), TCGETS2
, &config
) < 0) {
415 struct termios config
;
416 if (tcgetattr(file().GetPlatformFile(), &config
) == -1) {
418 VPLOG(1) << "Failed to get port info";
419 return serial::ConnectionInfoPtr();
422 serial::ConnectionInfoPtr
info(serial::ConnectionInfo::New());
423 #if defined(OS_LINUX)
424 // Linux forces c_ospeed to contain the correct value, which is nice.
425 info
->bitrate
= config
.c_ospeed
;
427 speed_t ispeed
= cfgetispeed(&config
);
428 speed_t ospeed
= cfgetospeed(&config
);
429 if (ispeed
== ospeed
) {
431 if (SpeedConstantToBitrate(ispeed
, &bitrate
)) {
432 info
->bitrate
= bitrate
;
433 } else if (ispeed
> 0) {
434 info
->bitrate
= static_cast<int>(ispeed
);
439 if ((config
.c_cflag
& CSIZE
) == CS7
) {
440 info
->data_bits
= serial::DATA_BITS_SEVEN
;
441 } else if ((config
.c_cflag
& CSIZE
) == CS8
) {
442 info
->data_bits
= serial::DATA_BITS_EIGHT
;
444 info
->data_bits
= serial::DATA_BITS_NONE
;
446 if (config
.c_cflag
& PARENB
) {
447 info
->parity_bit
= (config
.c_cflag
& PARODD
) ? serial::PARITY_BIT_ODD
448 : serial::PARITY_BIT_EVEN
;
450 info
->parity_bit
= serial::PARITY_BIT_NO
;
453 (config
.c_cflag
& CSTOPB
) ? serial::STOP_BITS_TWO
: serial::STOP_BITS_ONE
;
454 info
->cts_flow_control
= (config
.c_cflag
& CRTSCTS
) != 0;
458 bool SerialIoHandlerPosix::SetBreak() {
459 if (ioctl(file().GetPlatformFile(), TIOCSBRK
, 0) != 0) {
460 VPLOG(1) << "Failed to set break";
467 bool SerialIoHandlerPosix::ClearBreak() {
468 if (ioctl(file().GetPlatformFile(), TIOCCBRK
, 0) != 0) {
469 VPLOG(1) << "Failed to clear break";
475 std::string
SerialIoHandler::MaybeFixUpPortName(const std::string
& port_name
) {
479 } // namespace device