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>
14 #if defined(OS_CHROMEOS)
15 #include "base/bind.h"
16 #include "base/sys_info.h"
17 #include "chromeos/dbus/dbus_thread_manager.h"
18 #include "chromeos/dbus/permission_broker_client.h"
19 #endif // defined(OS_CHROMEOS)
20 #endif // defined(OS_LINUX)
24 // Convert an integral bit rate to a nominal one. Returns |true|
25 // if the conversion was successful and |false| otherwise.
26 bool BitrateToSpeedConstant(int bitrate
, speed_t
* speed
) {
27 #define BITRATE_TO_SPEED_CASE(x) \
32 BITRATE_TO_SPEED_CASE(0)
33 BITRATE_TO_SPEED_CASE(50)
34 BITRATE_TO_SPEED_CASE(75)
35 BITRATE_TO_SPEED_CASE(110)
36 BITRATE_TO_SPEED_CASE(134)
37 BITRATE_TO_SPEED_CASE(150)
38 BITRATE_TO_SPEED_CASE(200)
39 BITRATE_TO_SPEED_CASE(300)
40 BITRATE_TO_SPEED_CASE(600)
41 BITRATE_TO_SPEED_CASE(1200)
42 BITRATE_TO_SPEED_CASE(1800)
43 BITRATE_TO_SPEED_CASE(2400)
44 BITRATE_TO_SPEED_CASE(4800)
45 BITRATE_TO_SPEED_CASE(9600)
46 BITRATE_TO_SPEED_CASE(19200)
47 BITRATE_TO_SPEED_CASE(38400)
48 #if defined(OS_POSIX) && !defined(OS_MACOSX)
49 BITRATE_TO_SPEED_CASE(57600)
50 BITRATE_TO_SPEED_CASE(115200)
51 BITRATE_TO_SPEED_CASE(230400)
52 BITRATE_TO_SPEED_CASE(460800)
53 BITRATE_TO_SPEED_CASE(576000)
54 BITRATE_TO_SPEED_CASE(921600)
59 #undef BITRATE_TO_SPEED_CASE
62 // Convert a known nominal speed into an integral bitrate. Returns |true|
63 // if the conversion was successful and |false| otherwise.
64 bool SpeedConstantToBitrate(speed_t speed
, int* bitrate
) {
65 #define SPEED_TO_BITRATE_CASE(x) \
70 SPEED_TO_BITRATE_CASE(0)
71 SPEED_TO_BITRATE_CASE(50)
72 SPEED_TO_BITRATE_CASE(75)
73 SPEED_TO_BITRATE_CASE(110)
74 SPEED_TO_BITRATE_CASE(134)
75 SPEED_TO_BITRATE_CASE(150)
76 SPEED_TO_BITRATE_CASE(200)
77 SPEED_TO_BITRATE_CASE(300)
78 SPEED_TO_BITRATE_CASE(600)
79 SPEED_TO_BITRATE_CASE(1200)
80 SPEED_TO_BITRATE_CASE(1800)
81 SPEED_TO_BITRATE_CASE(2400)
82 SPEED_TO_BITRATE_CASE(4800)
83 SPEED_TO_BITRATE_CASE(9600)
84 SPEED_TO_BITRATE_CASE(19200)
85 SPEED_TO_BITRATE_CASE(38400)
86 #if defined(OS_POSIX) && !defined(OS_MACOSX)
87 SPEED_TO_BITRATE_CASE(57600)
88 SPEED_TO_BITRATE_CASE(115200)
89 SPEED_TO_BITRATE_CASE(230400)
90 SPEED_TO_BITRATE_CASE(460800)
91 SPEED_TO_BITRATE_CASE(576000)
92 SPEED_TO_BITRATE_CASE(921600)
97 #undef SPEED_TO_BITRATE_CASE
100 bool SetCustomBitrate(base::PlatformFile file
,
101 struct termios
* config
,
103 #if defined(OS_LINUX)
104 struct serial_struct serial
;
105 if (ioctl(file
, TIOCGSERIAL
, &serial
) < 0) {
108 serial
.flags
= (serial
.flags
& ~ASYNC_SPD_MASK
) | ASYNC_SPD_CUST
;
109 serial
.custom_divisor
= serial
.baud_base
/ bitrate
;
110 if (serial
.custom_divisor
< 1) {
111 serial
.custom_divisor
= 1;
113 cfsetispeed(config
, B38400
);
114 cfsetospeed(config
, B38400
);
115 return ioctl(file
, TIOCSSERIAL
, &serial
) >= 0;
116 #elif defined(OS_MACOSX)
117 speed_t speed
= static_cast<speed_t
>(bitrate
);
118 cfsetispeed(config
, speed
);
119 cfsetospeed(config
, speed
);
131 scoped_refptr
<SerialIoHandler
> SerialIoHandler::Create(
132 scoped_refptr
<base::MessageLoopProxy
> file_thread_message_loop
,
133 scoped_refptr
<base::MessageLoopProxy
> ui_thread_message_loop
) {
134 return new SerialIoHandlerPosix(file_thread_message_loop
,
135 ui_thread_message_loop
);
138 void SerialIoHandlerPosix::RequestAccess(
139 const std::string
& port
,
140 scoped_refptr
<base::MessageLoopProxy
> file_message_loop
,
141 scoped_refptr
<base::MessageLoopProxy
> ui_message_loop
) {
142 #if defined(OS_LINUX) && defined(OS_CHROMEOS)
143 if (base::SysInfo::IsRunningOnChromeOS()) {
144 chromeos::PermissionBrokerClient
* client
=
145 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
147 DVLOG(1) << "Could not get permission_broker client.";
148 OnRequestAccessComplete(port
, false /* failure */);
151 // PermissionBrokerClient should be called on the UI thread.
152 ui_message_loop
->PostTask(
155 &chromeos::PermissionBrokerClient::RequestPathAccess
,
156 base::Unretained(client
),
159 base::Bind(&SerialIoHandler::OnRequestAccessComplete
, this, port
)));
161 OnRequestAccessComplete(port
, true /* success */);
165 OnRequestAccessComplete(port
, true /* success */);
166 #endif // defined(OS_LINUX) && defined(OS_CHROMEOS)
169 void SerialIoHandlerPosix::ReadImpl() {
170 DCHECK(CalledOnValidThread());
171 DCHECK(pending_read_buffer());
172 DCHECK(file().IsValid());
174 EnsureWatchingReads();
177 void SerialIoHandlerPosix::WriteImpl() {
178 DCHECK(CalledOnValidThread());
179 DCHECK(pending_write_buffer());
180 DCHECK(file().IsValid());
182 EnsureWatchingWrites();
185 void SerialIoHandlerPosix::CancelReadImpl() {
186 DCHECK(CalledOnValidThread());
187 is_watching_reads_
= false;
188 file_read_watcher_
.StopWatchingFileDescriptor();
189 QueueReadCompleted(0, read_cancel_reason());
192 void SerialIoHandlerPosix::CancelWriteImpl() {
193 DCHECK(CalledOnValidThread());
194 is_watching_writes_
= false;
195 file_write_watcher_
.StopWatchingFileDescriptor();
196 QueueWriteCompleted(0, write_cancel_reason());
199 bool SerialIoHandlerPosix::ConfigurePortImpl() {
200 struct termios config
;
201 if (tcgetattr(file().GetPlatformFile(), &config
) != 0) {
202 VPLOG(1) << "Failed to get port attributes";
206 // Set flags for 'raw' operation
207 config
.c_lflag
&= ~(ICANON
| ECHO
| ECHOE
| ECHONL
| ISIG
);
209 ~(IGNBRK
| BRKINT
| PARMRK
| ISTRIP
| INLCR
| IGNCR
| ICRNL
| IXON
);
210 config
.c_oflag
&= ~OPOST
;
212 // CLOCAL causes the system to disregard the DCD signal state.
213 // CREAD enables reading from the port.
214 config
.c_cflag
|= (CLOCAL
| CREAD
);
216 DCHECK(options().bitrate
);
217 speed_t bitrate_opt
= B0
;
218 if (BitrateToSpeedConstant(options().bitrate
, &bitrate_opt
)) {
219 cfsetispeed(&config
, bitrate_opt
);
220 cfsetospeed(&config
, bitrate_opt
);
222 // Attempt to set a custom speed.
223 if (!SetCustomBitrate(file().GetPlatformFile(), &config
,
224 options().bitrate
)) {
229 DCHECK(options().data_bits
!= serial::DATA_BITS_NONE
);
230 config
.c_cflag
&= ~CSIZE
;
231 switch (options().data_bits
) {
232 case serial::DATA_BITS_SEVEN
:
233 config
.c_cflag
|= CS7
;
235 case serial::DATA_BITS_EIGHT
:
237 config
.c_cflag
|= CS8
;
241 DCHECK(options().parity_bit
!= serial::PARITY_BIT_NONE
);
242 switch (options().parity_bit
) {
243 case serial::PARITY_BIT_EVEN
:
244 config
.c_cflag
|= PARENB
;
245 config
.c_cflag
&= ~PARODD
;
247 case serial::PARITY_BIT_ODD
:
248 config
.c_cflag
|= (PARODD
| PARENB
);
250 case serial::PARITY_BIT_NO
:
252 config
.c_cflag
&= ~(PARODD
| PARENB
);
256 DCHECK(options().stop_bits
!= serial::STOP_BITS_NONE
);
257 switch (options().stop_bits
) {
258 case serial::STOP_BITS_TWO
:
259 config
.c_cflag
|= CSTOPB
;
261 case serial::STOP_BITS_ONE
:
263 config
.c_cflag
&= ~CSTOPB
;
267 DCHECK(options().has_cts_flow_control
);
268 if (options().cts_flow_control
) {
269 config
.c_cflag
|= CRTSCTS
;
271 config
.c_cflag
&= ~CRTSCTS
;
274 if (tcsetattr(file().GetPlatformFile(), TCSANOW
, &config
) != 0) {
275 VPLOG(1) << "Failed to set port attributes";
281 SerialIoHandlerPosix::SerialIoHandlerPosix(
282 scoped_refptr
<base::MessageLoopProxy
> file_thread_message_loop
,
283 scoped_refptr
<base::MessageLoopProxy
> ui_thread_message_loop
)
284 : SerialIoHandler(file_thread_message_loop
, ui_thread_message_loop
),
285 is_watching_reads_(false),
286 is_watching_writes_(false) {
289 SerialIoHandlerPosix::~SerialIoHandlerPosix() {
292 void SerialIoHandlerPosix::OnFileCanReadWithoutBlocking(int fd
) {
293 DCHECK(CalledOnValidThread());
294 DCHECK_EQ(fd
, file().GetPlatformFile());
296 if (pending_read_buffer()) {
297 int bytes_read
= HANDLE_EINTR(read(file().GetPlatformFile(),
298 pending_read_buffer(),
299 pending_read_buffer_len()));
300 if (bytes_read
< 0) {
301 if (errno
== ENXIO
) {
302 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST
);
304 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR
);
306 } else if (bytes_read
== 0) {
307 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST
);
309 ReadCompleted(bytes_read
, serial::RECEIVE_ERROR_NONE
);
312 // Stop watching the fd if we get notifications with no pending
313 // reads or writes to avoid starving the message loop.
314 is_watching_reads_
= false;
315 file_read_watcher_
.StopWatchingFileDescriptor();
319 void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd
) {
320 DCHECK(CalledOnValidThread());
321 DCHECK_EQ(fd
, file().GetPlatformFile());
323 if (pending_write_buffer()) {
324 int bytes_written
= HANDLE_EINTR(write(file().GetPlatformFile(),
325 pending_write_buffer(),
326 pending_write_buffer_len()));
327 if (bytes_written
< 0) {
328 WriteCompleted(0, serial::SEND_ERROR_SYSTEM_ERROR
);
330 WriteCompleted(bytes_written
, serial::SEND_ERROR_NONE
);
333 // Stop watching the fd if we get notifications with no pending
334 // writes to avoid starving the message loop.
335 is_watching_writes_
= false;
336 file_write_watcher_
.StopWatchingFileDescriptor();
340 void SerialIoHandlerPosix::EnsureWatchingReads() {
341 DCHECK(CalledOnValidThread());
342 DCHECK(file().IsValid());
343 if (!is_watching_reads_
) {
344 is_watching_reads_
= base::MessageLoopForIO::current()->WatchFileDescriptor(
345 file().GetPlatformFile(),
347 base::MessageLoopForIO::WATCH_READ
,
353 void SerialIoHandlerPosix::EnsureWatchingWrites() {
354 DCHECK(CalledOnValidThread());
355 DCHECK(file().IsValid());
356 if (!is_watching_writes_
) {
357 is_watching_writes_
=
358 base::MessageLoopForIO::current()->WatchFileDescriptor(
359 file().GetPlatformFile(),
361 base::MessageLoopForIO::WATCH_WRITE
,
362 &file_write_watcher_
,
367 bool SerialIoHandlerPosix::Flush() const {
368 if (tcflush(file().GetPlatformFile(), TCIOFLUSH
) != 0) {
369 VPLOG(1) << "Failed to flush port";
375 serial::DeviceControlSignalsPtr
SerialIoHandlerPosix::GetControlSignals()
378 if (ioctl(file().GetPlatformFile(), TIOCMGET
, &status
) == -1) {
379 VPLOG(1) << "Failed to get port control signals";
380 return serial::DeviceControlSignalsPtr();
383 serial::DeviceControlSignalsPtr
signals(serial::DeviceControlSignals::New());
384 signals
->dcd
= (status
& TIOCM_CAR
) != 0;
385 signals
->cts
= (status
& TIOCM_CTS
) != 0;
386 signals
->dsr
= (status
& TIOCM_DSR
) != 0;
387 signals
->ri
= (status
& TIOCM_RI
) != 0;
388 return signals
.Pass();
391 bool SerialIoHandlerPosix::SetControlSignals(
392 const serial::HostControlSignals
& signals
) {
395 if (ioctl(file().GetPlatformFile(), TIOCMGET
, &status
) == -1) {
396 VPLOG(1) << "Failed to get port control signals";
400 if (signals
.has_dtr
) {
404 status
&= ~TIOCM_DTR
;
408 if (signals
.has_rts
) {
412 status
&= ~TIOCM_RTS
;
416 if (ioctl(file().GetPlatformFile(), TIOCMSET
, &status
) != 0) {
417 VPLOG(1) << "Failed to set port control signals";
423 serial::ConnectionInfoPtr
SerialIoHandlerPosix::GetPortInfo() const {
424 struct termios config
;
425 if (tcgetattr(file().GetPlatformFile(), &config
) == -1) {
426 VPLOG(1) << "Failed to get port info";
427 return serial::ConnectionInfoPtr();
429 serial::ConnectionInfoPtr
info(serial::ConnectionInfo::New());
430 speed_t ispeed
= cfgetispeed(&config
);
431 speed_t ospeed
= cfgetospeed(&config
);
432 if (ispeed
== ospeed
) {
434 if (SpeedConstantToBitrate(ispeed
, &bitrate
)) {
435 info
->bitrate
= bitrate
;
436 } else if (ispeed
> 0) {
437 info
->bitrate
= static_cast<int>(ispeed
);
440 if ((config
.c_cflag
& CSIZE
) == CS7
) {
441 info
->data_bits
= serial::DATA_BITS_SEVEN
;
442 } else if ((config
.c_cflag
& CSIZE
) == CS8
) {
443 info
->data_bits
= serial::DATA_BITS_EIGHT
;
445 info
->data_bits
= serial::DATA_BITS_NONE
;
447 if (config
.c_cflag
& PARENB
) {
448 info
->parity_bit
= (config
.c_cflag
& PARODD
) ? serial::PARITY_BIT_ODD
449 : serial::PARITY_BIT_EVEN
;
451 info
->parity_bit
= serial::PARITY_BIT_NO
;
454 (config
.c_cflag
& CSTOPB
) ? serial::STOP_BITS_TWO
: serial::STOP_BITS_ONE
;
455 info
->cts_flow_control
= (config
.c_cflag
& CRTSCTS
) != 0;
459 std::string
SerialIoHandler::MaybeFixUpPortName(const std::string
& port_name
) {
463 } // namespace device