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 SerialIoHandlerPosix::SerialIoHandlerPosix(
200 scoped_refptr
<base::MessageLoopProxy
> file_thread_message_loop
,
201 scoped_refptr
<base::MessageLoopProxy
> ui_thread_message_loop
)
202 : SerialIoHandler(file_thread_message_loop
, ui_thread_message_loop
),
203 is_watching_reads_(false),
204 is_watching_writes_(false) {
207 SerialIoHandlerPosix::~SerialIoHandlerPosix() {
210 void SerialIoHandlerPosix::OnFileCanReadWithoutBlocking(int fd
) {
211 DCHECK(CalledOnValidThread());
212 DCHECK_EQ(fd
, file().GetPlatformFile());
214 if (pending_read_buffer()) {
215 int bytes_read
= HANDLE_EINTR(read(file().GetPlatformFile(),
216 pending_read_buffer(),
217 pending_read_buffer_len()));
218 if (bytes_read
< 0) {
219 if (errno
== ENXIO
) {
220 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST
);
222 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR
);
224 } else if (bytes_read
== 0) {
225 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST
);
227 ReadCompleted(bytes_read
, serial::RECEIVE_ERROR_NONE
);
230 // Stop watching the fd if we get notifications with no pending
231 // reads or writes to avoid starving the message loop.
232 is_watching_reads_
= false;
233 file_read_watcher_
.StopWatchingFileDescriptor();
237 void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd
) {
238 DCHECK(CalledOnValidThread());
239 DCHECK_EQ(fd
, file().GetPlatformFile());
241 if (pending_write_buffer()) {
242 int bytes_written
= HANDLE_EINTR(write(file().GetPlatformFile(),
243 pending_write_buffer(),
244 pending_write_buffer_len()));
245 if (bytes_written
< 0) {
246 WriteCompleted(0, serial::SEND_ERROR_SYSTEM_ERROR
);
248 WriteCompleted(bytes_written
, serial::SEND_ERROR_NONE
);
251 // Stop watching the fd if we get notifications with no pending
252 // writes to avoid starving the message loop.
253 is_watching_writes_
= false;
254 file_write_watcher_
.StopWatchingFileDescriptor();
258 void SerialIoHandlerPosix::EnsureWatchingReads() {
259 DCHECK(CalledOnValidThread());
260 DCHECK(file().IsValid());
261 if (!is_watching_reads_
) {
262 is_watching_reads_
= base::MessageLoopForIO::current()->WatchFileDescriptor(
263 file().GetPlatformFile(),
265 base::MessageLoopForIO::WATCH_READ
,
271 void SerialIoHandlerPosix::EnsureWatchingWrites() {
272 DCHECK(CalledOnValidThread());
273 DCHECK(file().IsValid());
274 if (!is_watching_writes_
) {
275 is_watching_writes_
=
276 base::MessageLoopForIO::current()->WatchFileDescriptor(
277 file().GetPlatformFile(),
279 base::MessageLoopForIO::WATCH_WRITE
,
280 &file_write_watcher_
,
285 bool SerialIoHandlerPosix::ConfigurePort(
286 const serial::ConnectionOptions
& options
) {
287 struct termios config
;
288 tcgetattr(file().GetPlatformFile(), &config
);
289 if (options
.bitrate
) {
290 speed_t bitrate_opt
= B0
;
291 if (BitrateToSpeedConstant(options
.bitrate
, &bitrate_opt
)) {
292 cfsetispeed(&config
, bitrate_opt
);
293 cfsetospeed(&config
, bitrate_opt
);
295 // Attempt to set a custom speed.
296 if (!SetCustomBitrate(
297 file().GetPlatformFile(), &config
, options
.bitrate
)) {
302 if (options
.data_bits
!= serial::DATA_BITS_NONE
) {
303 config
.c_cflag
&= ~CSIZE
;
304 switch (options
.data_bits
) {
305 case serial::DATA_BITS_SEVEN
:
306 config
.c_cflag
|= CS7
;
308 case serial::DATA_BITS_EIGHT
:
310 config
.c_cflag
|= CS8
;
314 if (options
.parity_bit
!= serial::PARITY_BIT_NONE
) {
315 switch (options
.parity_bit
) {
316 case serial::PARITY_BIT_EVEN
:
317 config
.c_cflag
|= PARENB
;
318 config
.c_cflag
&= ~PARODD
;
320 case serial::PARITY_BIT_ODD
:
321 config
.c_cflag
|= (PARODD
| PARENB
);
323 case serial::PARITY_BIT_NO
:
325 config
.c_cflag
&= ~(PARODD
| PARENB
);
329 if (options
.stop_bits
!= serial::STOP_BITS_NONE
) {
330 switch (options
.stop_bits
) {
331 case serial::STOP_BITS_TWO
:
332 config
.c_cflag
|= CSTOPB
;
334 case serial::STOP_BITS_ONE
:
336 config
.c_cflag
&= ~CSTOPB
;
340 if (options
.has_cts_flow_control
) {
341 if (options
.cts_flow_control
) {
342 config
.c_cflag
|= CRTSCTS
;
344 config
.c_cflag
&= ~CRTSCTS
;
347 return tcsetattr(file().GetPlatformFile(), TCSANOW
, &config
) == 0;
350 bool SerialIoHandlerPosix::PostOpen() {
351 struct termios config
;
352 tcgetattr(file().GetPlatformFile(), &config
);
354 // Set flags for 'raw' operation
355 config
.c_lflag
&= ~(ICANON
| ECHO
| ECHOE
| ECHONL
| ISIG
);
357 ~(IGNBRK
| BRKINT
| PARMRK
| ISTRIP
| INLCR
| IGNCR
| ICRNL
| IXON
);
358 config
.c_oflag
&= ~OPOST
;
360 // CLOCAL causes the system to disregard the DCD signal state.
361 // CREAD enables reading from the port.
362 config
.c_cflag
|= (CLOCAL
| CREAD
);
364 return tcsetattr(file().GetPlatformFile(), TCSANOW
, &config
) == 0;
367 bool SerialIoHandlerPosix::Flush() const {
368 return tcflush(file().GetPlatformFile(), TCIOFLUSH
) == 0;
371 serial::DeviceControlSignalsPtr
SerialIoHandlerPosix::GetControlSignals()
374 if (ioctl(file().GetPlatformFile(), TIOCMGET
, &status
) == -1) {
375 return serial::DeviceControlSignalsPtr();
378 serial::DeviceControlSignalsPtr
signals(serial::DeviceControlSignals::New());
379 signals
->dcd
= (status
& TIOCM_CAR
) != 0;
380 signals
->cts
= (status
& TIOCM_CTS
) != 0;
381 signals
->dsr
= (status
& TIOCM_DSR
) != 0;
382 signals
->ri
= (status
& TIOCM_RI
) != 0;
383 return signals
.Pass();
386 bool SerialIoHandlerPosix::SetControlSignals(
387 const serial::HostControlSignals
& signals
) {
390 if (ioctl(file().GetPlatformFile(), TIOCMGET
, &status
) == -1) {
394 if (signals
.has_dtr
) {
398 status
&= ~TIOCM_DTR
;
402 if (signals
.has_rts
) {
406 status
&= ~TIOCM_RTS
;
410 return ioctl(file().GetPlatformFile(), TIOCMSET
, &status
) == 0;
413 serial::ConnectionInfoPtr
SerialIoHandlerPosix::GetPortInfo() const {
414 struct termios config
;
415 if (tcgetattr(file().GetPlatformFile(), &config
) == -1) {
416 return serial::ConnectionInfoPtr();
418 serial::ConnectionInfoPtr
info(serial::ConnectionInfo::New());
419 speed_t ispeed
= cfgetispeed(&config
);
420 speed_t ospeed
= cfgetospeed(&config
);
421 if (ispeed
== ospeed
) {
423 if (SpeedConstantToBitrate(ispeed
, &bitrate
)) {
424 info
->bitrate
= bitrate
;
425 } else if (ispeed
> 0) {
426 info
->bitrate
= static_cast<int>(ispeed
);
429 if ((config
.c_cflag
& CSIZE
) == CS7
) {
430 info
->data_bits
= serial::DATA_BITS_SEVEN
;
431 } else if ((config
.c_cflag
& CSIZE
) == CS8
) {
432 info
->data_bits
= serial::DATA_BITS_EIGHT
;
434 info
->data_bits
= serial::DATA_BITS_NONE
;
436 if (config
.c_cflag
& PARENB
) {
437 info
->parity_bit
= (config
.c_cflag
& PARODD
) ? serial::PARITY_BIT_ODD
438 : serial::PARITY_BIT_EVEN
;
440 info
->parity_bit
= serial::PARITY_BIT_NO
;
443 (config
.c_cflag
& CSTOPB
) ? serial::STOP_BITS_TWO
: serial::STOP_BITS_ONE
;
444 info
->cts_flow_control
= (config
.c_cflag
& CRTSCTS
) != 0;
448 std::string
SerialIoHandler::MaybeFixUpPortName(const std::string
& port_name
) {
452 } // namespace device