Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "device/serial/serial_io_handler_posix.h" | 5 #include "device/serial/serial_io_handler_posix.h" |
| 6 | 6 |
| 7 #include <sys/ioctl.h> | 7 #include <sys/ioctl.h> |
| 8 #include <termios.h> | 8 #include <termios.h> |
| 9 | 9 |
| 10 #include "base/posix/eintr_wrapper.h" | 10 #include "base/posix/eintr_wrapper.h" |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 179 | 179 |
| 180 bool SerialIoHandlerPosix::ConfigurePortImpl() { | 180 bool SerialIoHandlerPosix::ConfigurePortImpl() { |
| 181 struct termios config; | 181 struct termios config; |
| 182 if (tcgetattr(file().GetPlatformFile(), &config) != 0) { | 182 if (tcgetattr(file().GetPlatformFile(), &config) != 0) { |
| 183 VPLOG(1) << "Failed to get port attributes"; | 183 VPLOG(1) << "Failed to get port attributes"; |
| 184 return false; | 184 return false; |
| 185 } | 185 } |
| 186 | 186 |
| 187 // Set flags for 'raw' operation | 187 // Set flags for 'raw' operation |
| 188 config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG); | 188 config.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG); |
| 189 config.c_iflag &= | 189 config.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | ICRNL | IXON); |
| 190 ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); | 190 config.c_iflag |= PARMRK; |
| 191 config.c_oflag &= ~OPOST; | 191 config.c_oflag &= ~OPOST; |
| 192 | 192 |
| 193 // CLOCAL causes the system to disregard the DCD signal state. | 193 // CLOCAL causes the system to disregard the DCD signal state. |
| 194 // CREAD enables reading from the port. | 194 // CREAD enables reading from the port. |
| 195 config.c_cflag |= (CLOCAL | CREAD); | 195 config.c_cflag |= (CLOCAL | CREAD); |
| 196 | 196 |
| 197 DCHECK(options().bitrate); | 197 DCHECK(options().bitrate); |
| 198 speed_t bitrate_opt = B0; | 198 speed_t bitrate_opt = B0; |
| 199 if (BitrateToSpeedConstant(options().bitrate, &bitrate_opt)) { | 199 if (BitrateToSpeedConstant(options().bitrate, &bitrate_opt)) { |
| 200 cfsetispeed(&config, bitrate_opt); | 200 cfsetispeed(&config, bitrate_opt); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 227 break; | 227 break; |
| 228 case serial::PARITY_BIT_ODD: | 228 case serial::PARITY_BIT_ODD: |
| 229 config.c_cflag |= (PARODD | PARENB); | 229 config.c_cflag |= (PARODD | PARENB); |
| 230 break; | 230 break; |
| 231 case serial::PARITY_BIT_NO: | 231 case serial::PARITY_BIT_NO: |
| 232 default: | 232 default: |
| 233 config.c_cflag &= ~(PARODD | PARENB); | 233 config.c_cflag &= ~(PARODD | PARENB); |
| 234 break; | 234 break; |
| 235 } | 235 } |
| 236 | 236 |
| 237 error_detect_state_ = ErrorDetectState::NO_ERROR; | |
| 238 num_chars_ignored_ = 0; | |
| 239 | |
| 240 if (config.c_cflag & PARENB) { | |
| 241 config.c_iflag &= ~IGNPAR; | |
| 242 config.c_iflag |= INPCK; | |
| 243 parity_check_enabled_ = true; | |
| 244 } else { | |
| 245 config.c_iflag |= IGNPAR; | |
| 246 config.c_iflag &= ~INPCK; | |
| 247 parity_check_enabled_ = false; | |
| 248 } | |
| 249 | |
| 237 DCHECK(options().stop_bits != serial::STOP_BITS_NONE); | 250 DCHECK(options().stop_bits != serial::STOP_BITS_NONE); |
| 238 switch (options().stop_bits) { | 251 switch (options().stop_bits) { |
| 239 case serial::STOP_BITS_TWO: | 252 case serial::STOP_BITS_TWO: |
| 240 config.c_cflag |= CSTOPB; | 253 config.c_cflag |= CSTOPB; |
| 241 break; | 254 break; |
| 242 case serial::STOP_BITS_ONE: | 255 case serial::STOP_BITS_ONE: |
| 243 default: | 256 default: |
| 244 config.c_cflag &= ~CSTOPB; | 257 config.c_cflag &= ~CSTOPB; |
| 245 break; | 258 break; |
| 246 } | 259 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 280 pending_read_buffer_len())); | 293 pending_read_buffer_len())); |
| 281 if (bytes_read < 0) { | 294 if (bytes_read < 0) { |
| 282 if (errno == ENXIO) { | 295 if (errno == ENXIO) { |
| 283 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST); | 296 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST); |
| 284 } else { | 297 } else { |
| 285 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR); | 298 ReadCompleted(0, serial::RECEIVE_ERROR_SYSTEM_ERROR); |
| 286 } | 299 } |
| 287 } else if (bytes_read == 0) { | 300 } else if (bytes_read == 0) { |
| 288 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST); | 301 ReadCompleted(0, serial::RECEIVE_ERROR_DEVICE_LOST); |
| 289 } else { | 302 } else { |
| 290 ReadCompleted(bytes_read, serial::RECEIVE_ERROR_NONE); | 303 bool break_detected = false; |
| 304 bool parity_error_detected = false; | |
| 305 int new_bytes_read = | |
| 306 CheckReceiveError(pending_read_buffer(), pending_read_buffer_len(), | |
| 307 bytes_read, break_detected, parity_error_detected); | |
| 308 | |
| 309 if (break_detected) { | |
| 310 ReadCompleted(new_bytes_read, serial::RECEIVE_ERROR_BREAK); | |
| 311 } else if (parity_error_detected) { | |
| 312 ReadCompleted(new_bytes_read, serial::RECEIVE_ERROR_PARITY_ERROR); | |
| 313 } else { | |
| 314 ReadCompleted(new_bytes_read, serial::RECEIVE_ERROR_NONE); | |
| 315 } | |
| 291 } | 316 } |
| 292 } else { | 317 } else { |
| 293 // Stop watching the fd if we get notifications with no pending | 318 // Stop watching the fd if we get notifications with no pending |
| 294 // reads or writes to avoid starving the message loop. | 319 // reads or writes to avoid starving the message loop. |
| 295 is_watching_reads_ = false; | 320 is_watching_reads_ = false; |
| 296 file_read_watcher_.StopWatchingFileDescriptor(); | 321 file_read_watcher_.StopWatchingFileDescriptor(); |
| 297 } | 322 } |
| 298 } | 323 } |
| 299 | 324 |
| 300 void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd) { | 325 void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking(int fd) { |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 447 } | 472 } |
| 448 | 473 |
| 449 bool SerialIoHandlerPosix::ClearBreak() { | 474 bool SerialIoHandlerPosix::ClearBreak() { |
| 450 if (ioctl(file().GetPlatformFile(), TIOCCBRK, 0) != 0) { | 475 if (ioctl(file().GetPlatformFile(), TIOCCBRK, 0) != 0) { |
| 451 VPLOG(1) << "Failed to clear break"; | 476 VPLOG(1) << "Failed to clear break"; |
| 452 return false; | 477 return false; |
| 453 } | 478 } |
| 454 return true; | 479 return true; |
| 455 } | 480 } |
| 456 | 481 |
| 482 // break sequence: | |
| 483 // '\377' --> ErrorDetectState::MARK_377_SEEN | |
| 484 // '\0' --> ErrorDetectState::MARK_0_SEEN | |
| 485 // '\0' --> break detected | |
| 486 // | |
| 487 // parity error sequence: | |
| 488 // '\377' --> ErrorDetectState::MARK_377_SEEN | |
| 489 // '\0' --> ErrorDetectState::MARK_0_SEEN | |
| 490 // character with parity error --> parity error detected | |
| 491 // | |
| 492 // break/parity error sequences are removed from the byte stream | |
| 493 // '\377' '\377' sequence is replaced with '\377' | |
| 494 int SerialIoHandlerPosix::CheckReceiveError(char* buffer, | |
| 495 int buffer_len, | |
| 496 int bytes_read, | |
| 497 bool& break_detected, | |
| 498 bool& parity_error_detected) { | |
| 499 int new_bytes_read = num_chars_ignored_; | |
| 500 | |
|
Reilly Grant (use Gerrit)
2015/07/30 17:38:34
Some DCHECKs may be helpful for documenting the in
juncai
2015/07/30 19:51:49
Done.
| |
| 501 for (int i = 0; i < bytes_read; ++i) { | |
| 502 char ch = buffer[i]; | |
| 503 if (new_bytes_read == 0) { | |
| 504 chars_ignored_[0] = ch; | |
| 505 } else if (new_bytes_read == 1) { | |
| 506 chars_ignored_[1] = ch; | |
| 507 } else { | |
| 508 buffer[new_bytes_read - 2] = ch; | |
| 509 } | |
| 510 ++new_bytes_read; | |
| 511 switch (error_detect_state_) { | |
| 512 case ErrorDetectState::NO_ERROR: | |
| 513 if (ch == '\377') { | |
| 514 error_detect_state_ = ErrorDetectState::MARK_377_SEEN; | |
| 515 } | |
| 516 break; | |
| 517 case ErrorDetectState::MARK_377_SEEN: | |
| 518 if (ch == '\0') { | |
| 519 error_detect_state_ = ErrorDetectState::MARK_0_SEEN; | |
| 520 } else { | |
| 521 if (ch == '\377') { | |
| 522 // receive two bytes '\377' '\377', since ISTRIP is not set and | |
| 523 // PARMRK is set, a valid byte '\377' is passed to the program as | |
| 524 // two bytes, '\377' '\377'. Replace these two bytes with one byte | |
| 525 // of '\377', and set error_detect_state_ back to | |
| 526 // ErrorDetectState::NO_ERROR. | |
| 527 --new_bytes_read; | |
|
Reilly Grant (use Gerrit)
2015/07/30 17:38:34
To document the assumption that this is valid I su
juncai
2015/07/30 19:51:49
Done.
| |
| 528 } | |
| 529 error_detect_state_ = ErrorDetectState::NO_ERROR; | |
| 530 } | |
| 531 break; | |
| 532 case ErrorDetectState::MARK_0_SEEN: | |
| 533 if (ch == '\0') { | |
| 534 break_detected = true; | |
| 535 new_bytes_read -= 3; | |
|
Reilly Grant (use Gerrit)
2015/07/30 17:38:34
Similarly,
DCHECK_GE(new_bytes_read, 3);
at the
juncai
2015/07/30 19:51:49
Done.
| |
| 536 error_detect_state_ = ErrorDetectState::NO_ERROR; | |
| 537 } else { | |
| 538 if (parity_check_enabled_) { | |
| 539 parity_error_detected = true; | |
| 540 new_bytes_read -= 3; | |
| 541 error_detect_state_ = ErrorDetectState::NO_ERROR; | |
| 542 } else if (ch == '\377') { | |
| 543 error_detect_state_ = ErrorDetectState::MARK_377_SEEN; | |
| 544 } else { | |
| 545 error_detect_state_ = ErrorDetectState::NO_ERROR; | |
| 546 } | |
| 547 } | |
| 548 break; | |
| 549 } | |
| 550 } | |
| 551 // Now number of new_bytes_read bytes are read (including the previously | |
| 552 // ignored characters that were stored at chars_ignored_[]) and are stored at: | |
| 553 // chars_ignored_[0], chars_ignored_[1], buffer[...]. | |
| 554 | |
| 555 // Ignore some characters that are potentially part of the break/parity error | |
| 556 // sequence or buffer is not large enough to store all the bytes. | |
| 557 // tmp[] stores the characters that need to be ignored for this read. | |
| 558 char tmp[2]; | |
| 559 num_chars_ignored_ = 0; | |
| 560 if (error_detect_state_ == ErrorDetectState::MARK_0_SEEN || | |
| 561 new_bytes_read - buffer_len == 2) { | |
| 562 // need to ignore the last two characters | |
| 563 if (new_bytes_read == 2) { | |
| 564 memcpy(tmp, chars_ignored_, new_bytes_read); | |
| 565 } else { | |
| 566 if (new_bytes_read == 3) { | |
| 567 tmp[0] = chars_ignored_[1]; | |
| 568 } else { | |
| 569 tmp[0] = buffer[new_bytes_read - 4]; | |
| 570 } | |
| 571 tmp[1] = buffer[new_bytes_read - 3]; | |
| 572 } | |
| 573 num_chars_ignored_ = 2; | |
| 574 } else if (error_detect_state_ == ErrorDetectState::MARK_377_SEEN || | |
| 575 new_bytes_read - buffer_len == 1) { | |
| 576 // need to ignore the last character | |
| 577 if (new_bytes_read <= 2) { | |
| 578 tmp[0] = chars_ignored_[new_bytes_read - 1]; | |
| 579 } else { | |
| 580 tmp[0] = buffer[new_bytes_read - 3]; | |
| 581 } | |
| 582 num_chars_ignored_ = 1; | |
| 583 } | |
| 584 | |
| 585 new_bytes_read -= num_chars_ignored_; | |
| 586 if (new_bytes_read > 2) { | |
| 587 // right shift two bytes to store bytes from chars_ignored_[] | |
| 588 memmove(buffer + 2, buffer, new_bytes_read - 2); | |
| 589 } | |
| 590 memcpy(buffer, chars_ignored_, std::min(new_bytes_read, 2)); | |
| 591 memcpy(chars_ignored_, tmp, num_chars_ignored_); | |
| 592 return new_bytes_read; | |
| 593 } | |
| 594 | |
| 457 std::string SerialIoHandler::MaybeFixUpPortName(const std::string& port_name) { | 595 std::string SerialIoHandler::MaybeFixUpPortName(const std::string& port_name) { |
| 458 return port_name; | 596 return port_name; |
| 459 } | 597 } |
| 460 | 598 |
| 461 } // namespace device | 599 } // namespace device |
| OLD | NEW |