mgorny created this revision.
mgorny added reviewers: labath, teemperor, krytarowski, emaste.
mgorny requested review of this revision.
Add setters for common teletype properties to the Terminal class:
- SetRaw() to enable common raw mode options
- SetBaudRate() to set the baud rate
- SetStopBits() to select the number of stop bits
- SetParity() to control parity bit in the output
- SetHardwareControlFlow() to enable or disable hardware control flow (if
supported)
https://reviews.llvm.org/D111030
Files:
lldb/include/lldb/Host/Terminal.h
lldb/source/Host/common/Terminal.cpp
lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp
lldb/unittests/Host/TerminalTest.cpp
Index: lldb/unittests/Host/TerminalTest.cpp
===================================================================
--- lldb/unittests/Host/TerminalTest.cpp
+++ lldb/unittests/Host/TerminalTest.cpp
@@ -78,6 +78,134 @@
EXPECT_EQ(terminfo.c_lflag & ICANON, 0U);
}
+TEST_F(TerminalTest, SetRaw) {
+ struct termios terminfo;
+ Terminal term{m_slave};
+
+ ASSERT_EQ(term.SetRaw(), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ // NB: cfmakeraw() on glibc disables IGNBRK, on FreeBSD sets it
+ EXPECT_EQ(terminfo.c_iflag &
+ (BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON),
+ 0U);
+ EXPECT_EQ(terminfo.c_oflag & OPOST, 0U);
+ EXPECT_EQ(terminfo.c_lflag & (ICANON | ECHO | ISIG | IEXTEN), 0U);
+ EXPECT_EQ(terminfo.c_cflag & (CSIZE | PARENB), 0U | CS8);
+ EXPECT_EQ(terminfo.c_cc[VMIN], 1);
+ EXPECT_EQ(terminfo.c_cc[VTIME], 0);
+}
+
+TEST_F(TerminalTest, SetBaudRate) {
+ struct termios terminfo;
+ Terminal term{m_slave};
+
+ ASSERT_EQ(term.SetBaudRate(38400), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B38400));
+ EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B38400));
+
+ ASSERT_EQ(term.SetBaudRate(115200), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B115200));
+ EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B115200));
+
+ // uncommon value
+#if defined(B153600)
+ ASSERT_EQ(term.SetBaudRate(153600), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B153600));
+ EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B153600));
+#else
+ EXPECT_EQ(term.SetBaudRate(153600), false);
+#endif
+}
+
+TEST_F(TerminalTest, SetStopBits) {
+ struct termios terminfo;
+ Terminal term{m_slave};
+
+ ASSERT_EQ(term.SetStopBits(TerminalStopBits::One), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ EXPECT_EQ(terminfo.c_cflag & CSTOPB, 0U);
+
+ ASSERT_EQ(term.SetStopBits(TerminalStopBits::OneAndAHalf), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ EXPECT_NE(terminfo.c_cflag & CSTOPB, 0U);
+
+ ASSERT_EQ(term.SetStopBits(TerminalStopBits::Two), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ EXPECT_NE(terminfo.c_cflag & CSTOPB, 0U);
+}
+
+TEST_F(TerminalTest, SetParity) {
+ struct termios terminfo;
+ Terminal term{m_slave};
+
+ ASSERT_EQ(term.SetParity(TerminalParity::No), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ EXPECT_EQ(terminfo.c_cflag & PARENB, 0U);
+
+ ASSERT_EQ(term.SetParity(TerminalParity::Even), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ printf("cflag: %08x\n", terminfo.c_cflag);
+#if !defined(__linux__) // Linux pty devices strip PARENB
+ EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
+#endif
+ EXPECT_EQ(terminfo.c_cflag & PARODD, 0U);
+#if defined(CMSPAR)
+ EXPECT_EQ(terminfo.c_cflag & CMSPAR, 0U);
+#endif
+
+ ASSERT_EQ(term.SetParity(TerminalParity::Odd), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+#if !defined(__linux__) // Linux pty devices strip PARENB
+ EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
+#endif
+ EXPECT_NE(terminfo.c_cflag & PARODD, 0U);
+#if defined(CMSPAR)
+ EXPECT_EQ(terminfo.c_cflag & CMSPAR, 0U);
+#endif
+
+#if defined(CMSPAR)
+ ASSERT_EQ(term.SetParity(TerminalParity::Space), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+#if !defined(__linux__) // Linux pty devices strip PARENB
+ EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
+#endif
+ EXPECT_EQ(terminfo.c_cflag & PARODD, 0U);
+ EXPECT_NE(terminfo.c_cflag & CMSPAR, 0U);
+
+ ASSERT_EQ(term.SetParity(TerminalParity::Mark), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+#if !defined(__linux__) // Linux pty devices strip PARENB
+ EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
+#endif
+ EXPECT_NE(terminfo.c_cflag & PARODD, 0U);
+ EXPECT_NE(terminfo.c_cflag & CMSPAR, 0U);
+#else
+ EXPECT_EQ(term.SetParity(TerminalParity::Space), false);
+ EXPECT_EQ(term.SetParity(TerminalParity::Mark), false);
+#endif
+}
+
+TEST_F(TerminalTest, SetHardwareFlowControl) {
+ struct termios terminfo;
+ Terminal term{m_slave};
+
+#if defined(CRTSCTS)
+ ASSERT_EQ(term.SetHardwareFlowControl(true), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ EXPECT_NE(terminfo.c_cflag & CRTSCTS, 0U);
+
+ ASSERT_EQ(term.SetHardwareFlowControl(false), true);
+ ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0);
+ EXPECT_EQ(terminfo.c_cflag & CRTSCTS, 0U);
+#else
+ ASSERT_EQ(term.SetHardwareFlowControl(true), false);
+ ASSERT_EQ(term.SetHardwareFlowControl(false), true);
+#endif
+}
+
TEST_F(TerminalTest, SaveRestoreRAII) {
struct termios orig_terminfo;
struct termios terminfo;
Index: lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp
===================================================================
--- lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp
+++ lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp
@@ -250,22 +250,8 @@
Terminal term{fd};
if (term.IsATerminal()) {
m_saved_term_state.Save(term, false);
- // Set up serial terminal emulation
- struct termios options;
- ::tcgetattr(fd, &options);
-
- // Set port speed to maximum
- ::cfsetospeed(&options, B115200);
- ::cfsetispeed(&options, B115200);
-
- // Raw input, disable echo and signals
- options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
-
- // Make sure only one character is needed to return from a read
- options.c_cc[VMIN] = 1;
- options.c_cc[VTIME] = 0;
-
- llvm::sys::RetryAfterSignal(-1, ::tcsetattr, fd, TCSANOW, &options);
+ term.SetRaw();
+ term.SetBaudRate(115200);
}
int flags = ::fcntl(fd, F_GETFL, 0);
Index: lldb/source/Host/common/Terminal.cpp
===================================================================
--- lldb/source/Host/common/Terminal.cpp
+++ lldb/source/Host/common/Terminal.cpp
@@ -81,6 +81,302 @@
return false;
}
+bool Terminal::SetRaw() {
+#if LLDB_ENABLE_TERMIOS
+ if (IsATerminal()) {
+ struct termios fd_termios;
+ if (::tcgetattr(m_fd, &fd_termios) == 0) {
+ ::cfmakeraw(&fd_termios);
+
+ // Make sure only one character is needed to return from a read
+ // (cfmakeraw() doesn't do this on NetBSD)
+ fd_termios.c_cc[VMIN] = 1;
+ fd_termios.c_cc[VTIME] = 0;
+
+ return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0;
+ }
+ }
+#endif // #if LLDB_ENABLE_TERMIOS
+ return false;
+}
+
+static llvm::Optional<speed_t> baudRateToConst(unsigned int baud_rate) {
+ switch (baud_rate) {
+#if defined(B0)
+ case 0:
+ return B0;
+#endif
+#if defined(B50)
+ case 50:
+ return B50;
+#endif
+#if defined(B75)
+ case 75:
+ return B75;
+#endif
+#if defined(B110)
+ case 110:
+ return B110;
+#endif
+#if defined(B134)
+ case 134:
+ return B134;
+#endif
+#if defined(B150)
+ case 150:
+ return B150;
+#endif
+#if defined(B200)
+ case 200:
+ return B200;
+#endif
+#if defined(B300)
+ case 300:
+ return B300;
+#endif
+#if defined(B600)
+ case 600:
+ return B600;
+#endif
+#if defined(B1200)
+ case 1200:
+ return B1200;
+#endif
+#if defined(B1800)
+ case 1800:
+ return B1800;
+#endif
+#if defined(B2400)
+ case 2400:
+ return B2400;
+#endif
+#if defined(B4800)
+ case 4800:
+ return B4800;
+#endif
+#if defined(B9600)
+ case 9600:
+ return B9600;
+#endif
+#if defined(B19200)
+ case 19200:
+ return B19200;
+#endif
+#if defined(B38400)
+ case 38400:
+ return B38400;
+#endif
+#if defined(B57600)
+ case 57600:
+ return B57600;
+#endif
+#if defined(B115200)
+ case 115200:
+ return B115200;
+#endif
+#if defined(B230400)
+ case 230400:
+ return B230400;
+#endif
+#if defined(B460800)
+ case 460800:
+ return B460800;
+#endif
+#if defined(B500000)
+ case 500000:
+ return B500000;
+#endif
+#if defined(B576000)
+ case 576000:
+ return B576000;
+#endif
+#if defined(B921600)
+ case 921600:
+ return B921600;
+#endif
+#if defined(B1000000)
+ case 1000000:
+ return B1000000;
+#endif
+#if defined(B1152000)
+ case 1152000:
+ return B1152000;
+#endif
+#if defined(B1500000)
+ case 1500000:
+ return B1500000;
+#endif
+#if defined(B2000000)
+ case 2000000:
+ return B2000000;
+#endif
+#if defined(B76800)
+ case 76800:
+ return B76800;
+#endif
+#if defined(B153600)
+ case 153600:
+ return B153600;
+#endif
+#if defined(B307200)
+ case 307200:
+ return B307200;
+#endif
+#if defined(B614400)
+ case 614400:
+ return B614400;
+#endif
+#if defined(B2500000)
+ case 2500000:
+ return B2500000;
+#endif
+#if defined(B3000000)
+ case 3000000:
+ return B3000000;
+#endif
+#if defined(B3500000)
+ case 3500000:
+ return B3500000;
+#endif
+#if defined(B4000000)
+ case 4000000:
+ return B4000000;
+#endif
+ default:
+ return llvm::None;
+ }
+}
+
+bool Terminal::SetBaudRate(unsigned int baud_rate) {
+#if LLDB_ENABLE_TERMIOS
+ if (IsATerminal()) {
+ struct termios fd_termios;
+ if (::tcgetattr(m_fd, &fd_termios) == 0) {
+ llvm::Optional<speed_t> val = baudRateToConst(baud_rate);
+ if (!val) // invalid value
+ return false;
+ if (::cfsetispeed(&fd_termios, val.getValue()) != 0)
+ return false;
+ if (::cfsetospeed(&fd_termios, val.getValue()) != 0)
+ return false;
+ return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0;
+ }
+ }
+#endif // #if LLDB_ENABLE_TERMIOS
+ return false;
+}
+
+bool Terminal::SetStopBits(TerminalStopBits stop_bits) {
+#if LLDB_ENABLE_TERMIOS
+ if (IsATerminal()) {
+ struct termios fd_termios;
+ if (::tcgetattr(m_fd, &fd_termios) == 0) {
+ switch (stop_bits) {
+ case TerminalStopBits::One:
+ fd_termios.c_cflag &= ~CSTOPB;
+ break;
+ case TerminalStopBits::OneAndAHalf:
+ // 1.5 stop bits are not supported by POSIX, round it up to 2
+ case TerminalStopBits::Two:
+ fd_termios.c_cflag |= CSTOPB;
+ break;
+ }
+ return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0;
+ }
+ }
+#endif // #if LLDB_ENABLE_TERMIOS
+ return false;
+}
+
+bool Terminal::SetParity(TerminalParity parity) {
+#if LLDB_ENABLE_TERMIOS
+ if (IsATerminal()) {
+ struct termios fd_termios;
+ if (::tcgetattr(m_fd, &fd_termios) == 0) {
+ bool parity_bit = true;
+ bool odd_bit = false;
+ bool stick_bit = false;
+
+ switch (parity) {
+ case TerminalParity::No:
+ parity_bit = false;
+ break;
+ case TerminalParity::Even:
+ break;
+ case TerminalParity::Odd:
+ odd_bit = true;
+ break;
+ case TerminalParity::Space:
+ stick_bit = true;
+ break;
+ case TerminalParity::Mark:
+ stick_bit = true;
+ odd_bit = true;
+ break;
+ }
+
+ printf("before: %08x\n", fd_termios.c_cflag);
+ printf("PARENB %08x\n", PARENB);
+ if (!parity_bit)
+ fd_termios.c_cflag &= ~PARENB;
+ else {
+ fd_termios.c_cflag |= PARENB;
+ if (odd_bit)
+ fd_termios.c_cflag |= PARODD;
+ else
+ fd_termios.c_cflag &= ~PARODD;
+#if defined(CMSPAR)
+ if (stick_bit)
+ fd_termios.c_cflag |= CMSPAR;
+ else
+ fd_termios.c_cflag &= ~CMSPAR;
+#else
+ if (stick_bit)
+ return false;
+#endif
+ }
+ printf("after: %08x\n", fd_termios.c_cflag);
+
+ return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0;
+ }
+ }
+#endif // #if LLDB_ENABLE_TERMIOS
+ return false;
+}
+
+bool Terminal::SetHardwareFlowControl(bool enabled) {
+ if (FileDescriptorIsValid()) {
+#if LLDB_ENABLE_TERMIOS
+ if (IsATerminal()) {
+#if defined(CRTSCTS)
+ struct termios fd_termios;
+ if (::tcgetattr(m_fd, &fd_termios) == 0) {
+ bool set_corectly = false;
+ if (enabled) {
+ if (fd_termios.c_cflag & CRTSCTS)
+ set_corectly = true;
+ else
+ fd_termios.c_cflag |= CRTSCTS;
+ } else {
+ if (fd_termios.c_cflag & CRTSCTS)
+ fd_termios.c_cflag &= ~CRTSCTS;
+ else
+ set_corectly = true;
+ }
+
+ if (set_corectly)
+ return true;
+ return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0;
+ }
+#else // !defined(CRTSCTS)
+ return !enabled;
+#endif
+ }
+#endif // #if LLDB_ENABLE_TERMIOS
+ }
+ return false;
+}
+
struct TerminalState::Data {
#if LLDB_ENABLE_TERMIOS
struct termios m_termios; ///< Cached terminal state information.
Index: lldb/include/lldb/Host/Terminal.h
===================================================================
--- lldb/include/lldb/Host/Terminal.h
+++ lldb/include/lldb/Host/Terminal.h
@@ -15,6 +15,20 @@
namespace lldb_private {
+enum class TerminalStopBits {
+ One,
+ OneAndAHalf,
+ Two,
+};
+
+enum class TerminalParity {
+ No,
+ Even,
+ Odd,
+ Space,
+ Mark,
+};
+
class Terminal {
public:
Terminal(int fd = -1) : m_fd(fd) {}
@@ -35,6 +49,16 @@
bool SetCanonical(bool enabled);
+ bool SetRaw();
+
+ bool SetBaudRate(unsigned int baud_rate);
+
+ bool SetStopBits(TerminalStopBits stop_bits);
+
+ bool SetParity(TerminalParity parity);
+
+ bool SetHardwareFlowControl(bool enabled);
+
protected:
int m_fd; // This may or may not be a terminal file descriptor
};
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits