Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / device / serial / serial_io_handler_posix.cc
blob01b6a275bbc3b60ace2fbb2bb46e45ac99423c5e
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"
7 #include <sys/ioctl.h>
8 #include <termios.h>
10 #include "base/posix/eintr_wrapper.h"
12 #if defined(OS_LINUX)
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.
17 extern "C" {
18 struct termios2 {
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>
34 #endif
36 namespace {
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) \
42 case x: \
43 *speed = B##x; \
44 return true;
45 switch (bitrate) {
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)
69 #endif
70 default:
71 return false;
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) \
81 case B##x: \
82 *bitrate = x; \
83 return true;
84 switch (speed) {
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)
101 default:
102 return false;
104 #undef SPEED_TO_BITRATE_CASE
106 #endif
108 } // namespace
110 namespace device {
112 // static
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) {
154 #else
155 struct termios config;
156 if (tcgetattr(file().GetPlatformFile(), &config) != 0) {
157 #endif
158 VPLOG(1) << "Failed to get port configuration";
159 return false;
162 // Set flags for 'raw' operation
163 config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
164 config.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
165 config.c_iflag |= PARMRK;
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;
176 #endif
177 if (BitrateToSpeedConstant(options().bitrate, &bitrate_opt)) {
178 #if defined(OS_LINUX)
179 config.c_cflag &= ~CBAUD;
180 config.c_cflag |= bitrate_opt;
181 #else
182 cfsetispeed(&config, bitrate_opt);
183 cfsetospeed(&config, bitrate_opt);
184 #endif
185 } else {
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;
196 #else
197 return false;
198 #endif
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;
206 break;
207 case serial::DATA_BITS_EIGHT:
208 default:
209 config.c_cflag |= CS8;
210 break;
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;
218 break;
219 case serial::PARITY_BIT_ODD:
220 config.c_cflag |= (PARODD | PARENB);
221 break;
222 case serial::PARITY_BIT_NO:
223 default:
224 config.c_cflag &= ~(PARODD | PARENB);
225 break;
228 error_detect_state_ = ErrorDetectState::NO_ERROR;
229 num_chars_stashed_ = 0;
231 if (config.c_cflag & PARENB) {
232 config.c_iflag &= ~IGNPAR;
233 config.c_iflag |= INPCK;
234 parity_check_enabled_ = true;
235 } else {
236 config.c_iflag |= IGNPAR;
237 config.c_iflag &= ~INPCK;
238 parity_check_enabled_ = false;
241 DCHECK(options().stop_bits != serial::STOP_BITS_NONE);
242 switch (options().stop_bits) {
243 case serial::STOP_BITS_TWO:
244 config.c_cflag |= CSTOPB;
245 break;
246 case serial::STOP_BITS_ONE:
247 default:
248 config.c_cflag &= ~CSTOPB;
249 break;
252 DCHECK(options().has_cts_flow_control);
253 if (options().cts_flow_control) {
254 config.c_cflag |= CRTSCTS;
255 } else {
256 config.c_cflag &= ~CRTSCTS;
259 #if defined(OS_LINUX)
260 if (ioctl(file().GetPlatformFile(), TCSETS2, &config) < 0) {
261 #else
262 if (tcsetattr(file().GetPlatformFile(), TCSANOW, &config) != 0) {
263 #endif
264 VPLOG(1) << "Failed to set port attributes";
265 return false;
268 #if defined(OS_MACOSX)
269 if (need_iossiospeed) {
270 speed_t bitrate = options().bitrate;
271 if (ioctl(file().GetPlatformFile(), IOSSIOSPEED, &bitrate) == -1) {
272 VPLOG(1) << "Failed to set custom baud rate";
273 return false;
276 #endif
278 return true;
281 SerialIoHandlerPosix::SerialIoHandlerPosix(
282 scoped_refptr<base::SingleThreadTaskRunner> file_thread_task_runner,
283 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner)
284 : SerialIoHandler(file_thread_task_runner, ui_thread_task_runner),
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);
303 } else {
304 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR);
306 } else if (bytes_read == 0) {
307 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST);
308 } else {
309 bool break_detected = false;
310 bool parity_error_detected = false;
311 int new_bytes_read =
312 CheckReceiveError(pending_read_buffer(), pending_read_buffer_len(),
313 bytes_read, break_detected, parity_error_detected);
315 if (break_detected) {
316 ReadCompleted(new_bytes_read, serial::RECEIVE_ERROR_BREAK);
317 } else if (parity_error_detected) {
318 ReadCompleted(new_bytes_read, serial::RECEIVE_ERROR_PARITY_ERROR);
319 } else {
320 ReadCompleted(new_bytes_read, serial::RECEIVE_ERROR_NONE);
323 } else {
324 // Stop watching the fd if we get notifications with no pending
325 // reads or writes to avoid starving the message loop.
326 is_watching_reads_ = false;
327 file_read_watcher_.StopWatchingFileDescriptor();
331 void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd) {
332 DCHECK(CalledOnValidThread());
333 DCHECK_EQ(fd, file().GetPlatformFile());
335 if (pending_write_buffer()) {
336 int bytes_written = HANDLE_EINTR(write(file().GetPlatformFile(),
337 pending_write_buffer(),
338 pending_write_buffer_len()));
339 if (bytes_written < 0) {
340 WriteCompleted(0, serial::SEND_ERROR_SYSTEM_ERROR);
341 } else {
342 WriteCompleted(bytes_written, serial::SEND_ERROR_NONE);
344 } else {
345 // Stop watching the fd if we get notifications with no pending
346 // writes to avoid starving the message loop.
347 is_watching_writes_ = false;
348 file_write_watcher_.StopWatchingFileDescriptor();
352 void SerialIoHandlerPosix::EnsureWatchingReads() {
353 DCHECK(CalledOnValidThread());
354 DCHECK(file().IsValid());
355 if (!is_watching_reads_) {
356 is_watching_reads_ = base::MessageLoopForIO::current()->WatchFileDescriptor(
357 file().GetPlatformFile(),
358 true,
359 base::MessageLoopForIO::WATCH_READ,
360 &file_read_watcher_,
361 this);
365 void SerialIoHandlerPosix::EnsureWatchingWrites() {
366 DCHECK(CalledOnValidThread());
367 DCHECK(file().IsValid());
368 if (!is_watching_writes_) {
369 is_watching_writes_ =
370 base::MessageLoopForIO::current()->WatchFileDescriptor(
371 file().GetPlatformFile(),
372 true,
373 base::MessageLoopForIO::WATCH_WRITE,
374 &file_write_watcher_,
375 this);
379 bool SerialIoHandlerPosix::Flush() const {
380 if (tcflush(file().GetPlatformFile(), TCIOFLUSH) != 0) {
381 VPLOG(1) << "Failed to flush port";
382 return false;
384 return true;
387 serial::DeviceControlSignalsPtr SerialIoHandlerPosix::GetControlSignals()
388 const {
389 int status;
390 if (ioctl(file().GetPlatformFile(), TIOCMGET, &status) == -1) {
391 VPLOG(1) << "Failed to get port control signals";
392 return serial::DeviceControlSignalsPtr();
395 serial::DeviceControlSignalsPtr signals(serial::DeviceControlSignals::New());
396 signals->dcd = (status & TIOCM_CAR) != 0;
397 signals->cts = (status & TIOCM_CTS) != 0;
398 signals->dsr = (status & TIOCM_DSR) != 0;
399 signals->ri = (status & TIOCM_RI) != 0;
400 return signals.Pass();
403 bool SerialIoHandlerPosix::SetControlSignals(
404 const serial::HostControlSignals& signals) {
405 int status;
407 if (ioctl(file().GetPlatformFile(), TIOCMGET, &status) == -1) {
408 VPLOG(1) << "Failed to get port control signals";
409 return false;
412 if (signals.has_dtr) {
413 if (signals.dtr) {
414 status |= TIOCM_DTR;
415 } else {
416 status &= ~TIOCM_DTR;
420 if (signals.has_rts) {
421 if (signals.rts) {
422 status |= TIOCM_RTS;
423 } else {
424 status &= ~TIOCM_RTS;
428 if (ioctl(file().GetPlatformFile(), TIOCMSET, &status) != 0) {
429 VPLOG(1) << "Failed to set port control signals";
430 return false;
432 return true;
435 serial::ConnectionInfoPtr SerialIoHandlerPosix::GetPortInfo() const {
436 #if defined(OS_LINUX)
437 struct termios2 config;
438 if (ioctl(file().GetPlatformFile(), TCGETS2, &config) < 0) {
439 #else
440 struct termios config;
441 if (tcgetattr(file().GetPlatformFile(), &config) == -1) {
442 #endif
443 VPLOG(1) << "Failed to get port info";
444 return serial::ConnectionInfoPtr();
447 serial::ConnectionInfoPtr info(serial::ConnectionInfo::New());
448 #if defined(OS_LINUX)
449 // Linux forces c_ospeed to contain the correct value, which is nice.
450 info->bitrate = config.c_ospeed;
451 #else
452 speed_t ispeed = cfgetispeed(&config);
453 speed_t ospeed = cfgetospeed(&config);
454 if (ispeed == ospeed) {
455 int bitrate = 0;
456 if (SpeedConstantToBitrate(ispeed, &bitrate)) {
457 info->bitrate = bitrate;
458 } else if (ispeed > 0) {
459 info->bitrate = static_cast<int>(ispeed);
462 #endif
464 if ((config.c_cflag & CSIZE) == CS7) {
465 info->data_bits = serial::DATA_BITS_SEVEN;
466 } else if ((config.c_cflag & CSIZE) == CS8) {
467 info->data_bits = serial::DATA_BITS_EIGHT;
468 } else {
469 info->data_bits = serial::DATA_BITS_NONE;
471 if (config.c_cflag & PARENB) {
472 info->parity_bit = (config.c_cflag & PARODD) ? serial::PARITY_BIT_ODD
473 : serial::PARITY_BIT_EVEN;
474 } else {
475 info->parity_bit = serial::PARITY_BIT_NO;
477 info->stop_bits =
478 (config.c_cflag & CSTOPB) ? serial::STOP_BITS_TWO : serial::STOP_BITS_ONE;
479 info->cts_flow_control = (config.c_cflag & CRTSCTS) != 0;
480 return info.Pass();
483 bool SerialIoHandlerPosix::SetBreak() {
484 if (ioctl(file().GetPlatformFile(), TIOCSBRK, 0) != 0) {
485 VPLOG(1) << "Failed to set break";
486 return false;
489 return true;
492 bool SerialIoHandlerPosix::ClearBreak() {
493 if (ioctl(file().GetPlatformFile(), TIOCCBRK, 0) != 0) {
494 VPLOG(1) << "Failed to clear break";
495 return false;
497 return true;
500 // break sequence:
501 // '\377' --> ErrorDetectState::MARK_377_SEEN
502 // '\0' --> ErrorDetectState::MARK_0_SEEN
503 // '\0' --> break detected
505 // parity error sequence:
506 // '\377' --> ErrorDetectState::MARK_377_SEEN
507 // '\0' --> ErrorDetectState::MARK_0_SEEN
508 // character with parity error --> parity error detected
510 // break/parity error sequences are removed from the byte stream
511 // '\377' '\377' sequence is replaced with '\377'
512 int SerialIoHandlerPosix::CheckReceiveError(char* buffer,
513 int buffer_len,
514 int bytes_read,
515 bool& break_detected,
516 bool& parity_error_detected) {
517 int new_bytes_read = num_chars_stashed_;
518 DCHECK_LE(new_bytes_read, 2);
520 for (int i = 0; i < bytes_read; ++i) {
521 char ch = buffer[i];
522 if (new_bytes_read == 0) {
523 chars_stashed_[0] = ch;
524 } else if (new_bytes_read == 1) {
525 chars_stashed_[1] = ch;
526 } else {
527 buffer[new_bytes_read - 2] = ch;
529 ++new_bytes_read;
530 switch (error_detect_state_) {
531 case ErrorDetectState::NO_ERROR:
532 if (ch == '\377') {
533 error_detect_state_ = ErrorDetectState::MARK_377_SEEN;
535 break;
536 case ErrorDetectState::MARK_377_SEEN:
537 DCHECK_GE(new_bytes_read, 2);
538 if (ch == '\0') {
539 error_detect_state_ = ErrorDetectState::MARK_0_SEEN;
540 } else {
541 if (ch == '\377') {
542 // receive two bytes '\377' '\377', since ISTRIP is not set and
543 // PARMRK is set, a valid byte '\377' is passed to the program as
544 // two bytes, '\377' '\377'. Replace these two bytes with one byte
545 // of '\377', and set error_detect_state_ back to
546 // ErrorDetectState::NO_ERROR.
547 --new_bytes_read;
549 error_detect_state_ = ErrorDetectState::NO_ERROR;
551 break;
552 case ErrorDetectState::MARK_0_SEEN:
553 DCHECK_GE(new_bytes_read, 3);
554 if (ch == '\0') {
555 break_detected = true;
556 new_bytes_read -= 3;
557 error_detect_state_ = ErrorDetectState::NO_ERROR;
558 } else {
559 if (parity_check_enabled_) {
560 parity_error_detected = true;
561 new_bytes_read -= 3;
562 error_detect_state_ = ErrorDetectState::NO_ERROR;
563 } else if (ch == '\377') {
564 error_detect_state_ = ErrorDetectState::MARK_377_SEEN;
565 } else {
566 error_detect_state_ = ErrorDetectState::NO_ERROR;
569 break;
572 // Now new_bytes_read bytes should be returned to the caller (including the
573 // previously stashed characters that were stored at chars_stashed_[]) and are
574 // now stored at: chars_stashed_[0], chars_stashed_[1], buffer[...].
576 // Stash up to 2 characters that are potentially part of a break/parity error
577 // sequence. The buffer may also not be large enough to store all the bytes.
578 // tmp[] stores the characters that need to be stashed for this read.
579 char tmp[2];
580 num_chars_stashed_ = 0;
581 if (error_detect_state_ == ErrorDetectState::MARK_0_SEEN ||
582 new_bytes_read - buffer_len == 2) {
583 // need to stash the last two characters
584 if (new_bytes_read == 2) {
585 memcpy(tmp, chars_stashed_, new_bytes_read);
586 } else {
587 if (new_bytes_read == 3) {
588 tmp[0] = chars_stashed_[1];
589 } else {
590 tmp[0] = buffer[new_bytes_read - 4];
592 tmp[1] = buffer[new_bytes_read - 3];
594 num_chars_stashed_ = 2;
595 } else if (error_detect_state_ == ErrorDetectState::MARK_377_SEEN ||
596 new_bytes_read - buffer_len == 1) {
597 // need to stash the last character
598 if (new_bytes_read <= 2) {
599 tmp[0] = chars_stashed_[new_bytes_read - 1];
600 } else {
601 tmp[0] = buffer[new_bytes_read - 3];
603 num_chars_stashed_ = 1;
606 new_bytes_read -= num_chars_stashed_;
607 if (new_bytes_read > 2) {
608 // right shift two bytes to store bytes from chars_stashed_[]
609 memmove(buffer + 2, buffer, new_bytes_read - 2);
611 memcpy(buffer, chars_stashed_, std::min(new_bytes_read, 2));
612 memcpy(chars_stashed_, tmp, num_chars_stashed_);
613 return new_bytes_read;
616 std::string SerialIoHandler::MaybeFixUpPortName(const std::string& port_name) {
617 return port_name;
620 } // namespace device