diff --git a/lldb/include/lldb/Host/File.h b/lldb/include/lldb/Host/File.h --- a/lldb/include/lldb/Host/File.h +++ b/lldb/include/lldb/Host/File.h @@ -439,6 +439,7 @@ struct Options { llvm::Optional BaudRate = llvm::None; llvm::Optional Parity = llvm::None; + llvm::Optional ParityCheck = llvm::None; llvm::Optional StopBits = llvm::None; }; diff --git a/lldb/include/lldb/Host/Terminal.h b/lldb/include/lldb/Host/Terminal.h --- a/lldb/include/lldb/Host/Terminal.h +++ b/lldb/include/lldb/Host/Terminal.h @@ -28,6 +28,18 @@ Mark, }; + enum class ParityCheck { + // No parity checking + No, + // Replace erraneous bytes with NUL + ReplaceWithNUL, + // Ignore erraneous bytes + Ignore, + // Mark erraneous bytes by prepending them with \xFF\x00; real \xFF + // is escaped to \xFF\xFF + Mark, + }; + Terminal(int fd = -1) : m_fd(fd) {} ~Terminal() = default; @@ -54,6 +66,8 @@ llvm::Error SetParity(Parity parity); + llvm::Error SetParityCheck(ParityCheck parity_check); + llvm::Error SetHardwareFlowControl(bool enabled); protected: diff --git a/lldb/source/Host/common/File.cpp b/lldb/source/Host/common/File.cpp --- a/lldb/source/Host/common/File.cpp +++ b/lldb/source/Host/common/File.cpp @@ -794,6 +794,21 @@ llvm::inconvertibleErrorCode(), "Invalid parity (must be no, even, odd, mark or space): %s", x.str().c_str()); + } else if (x.consume_front("parity-check=")) { + serial_options.ParityCheck = + llvm::StringSwitch>(x) + .Case("no", Terminal::ParityCheck::No) + .Case("replace", Terminal::ParityCheck::ReplaceWithNUL) + .Case("ignore", Terminal::ParityCheck::Ignore) + // "mark" mode is not currently supported as it requires special + // input processing + // .Case("mark", Terminal::ParityCheck::Mark) + .Default(llvm::None); + if (!serial_options.ParityCheck) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Invalid parity-check (must be no, replace, ignore or mark): %s", + x.str().c_str()); } else if (x.consume_front("stop-bits=")) { unsigned int stop_bits; if (!llvm::to_integer(x, stop_bits, 10) || @@ -831,6 +846,11 @@ if (llvm::Error error = term.SetParity(serial_options.Parity.getValue())) return std::move(error); } + if (serial_options.ParityCheck) { + if (llvm::Error error = + term.SetParityCheck(serial_options.ParityCheck.getValue())) + return std::move(error); + } if (serial_options.StopBits) { if (llvm::Error error = term.SetStopBits(serial_options.StopBits.getValue())) diff --git a/lldb/source/Host/common/Terminal.cpp b/lldb/source/Host/common/Terminal.cpp --- a/lldb/source/Host/common/Terminal.cpp +++ b/lldb/source/Host/common/Terminal.cpp @@ -335,6 +335,26 @@ #endif // #if LLDB_ENABLE_TERMIOS } +llvm::Error Terminal::SetParityCheck(Terminal::ParityCheck parity_check) { + llvm::Expected data = GetData(); + if (!data) + return data.takeError(); + +#if LLDB_ENABLE_TERMIOS + struct termios &fd_termios = data->m_termios; + fd_termios.c_iflag &= ~(IGNPAR | PARMRK | INPCK); + + if (parity_check != ParityCheck::No) { + fd_termios.c_iflag |= INPCK; + if (parity_check == ParityCheck::Ignore) + fd_termios.c_iflag |= IGNPAR; + else if (parity_check == ParityCheck::Mark) + fd_termios.c_iflag |= PARMRK; + } + return SetData(data.get()); +#endif // #if LLDB_ENABLE_TERMIOS +} + llvm::Error Terminal::SetHardwareFlowControl(bool enabled) { llvm::Expected data = GetData(); if (!data) diff --git a/lldb/unittests/Host/posix/TerminalTest.cpp b/lldb/unittests/Host/posix/TerminalTest.cpp --- a/lldb/unittests/Host/posix/TerminalTest.cpp +++ b/lldb/unittests/Host/posix/TerminalTest.cpp @@ -190,6 +190,36 @@ #endif } +TEST_F(TerminalTest, SetParityCheck) { + struct termios terminfo; + + ASSERT_THAT_ERROR(m_term.SetParityCheck(Terminal::ParityCheck::No), + llvm::Succeeded()); + ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); + EXPECT_EQ(terminfo.c_iflag & (IGNPAR | PARMRK | INPCK), 0U); + + ASSERT_THAT_ERROR( + m_term.SetParityCheck(Terminal::ParityCheck::ReplaceWithNUL), + llvm::Succeeded()); + ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); + EXPECT_NE(terminfo.c_iflag & INPCK, 0U); + EXPECT_EQ(terminfo.c_iflag & (IGNPAR | PARMRK), 0U); + + ASSERT_THAT_ERROR(m_term.SetParityCheck(Terminal::ParityCheck::Ignore), + llvm::Succeeded()); + ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); + EXPECT_NE(terminfo.c_iflag & IGNPAR, 0U); + EXPECT_EQ(terminfo.c_iflag & PARMRK, 0U); + EXPECT_NE(terminfo.c_iflag & INPCK, 0U); + + ASSERT_THAT_ERROR(m_term.SetParityCheck(Terminal::ParityCheck::Mark), + llvm::Succeeded()); + ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0); + EXPECT_EQ(terminfo.c_iflag & IGNPAR, 0U); + EXPECT_NE(terminfo.c_iflag & PARMRK, 0U); + EXPECT_NE(terminfo.c_iflag & INPCK, 0U); +} + TEST_F(TerminalTest, SetHardwareFlowControl) { #if defined(CRTSCTS) struct termios terminfo;