[Lldb-commits] [lldb] 92fb574 - [lldb] [Host] Add setters for common teletype properties to Terminal

Michał Górny via lldb-commits lldb-commits at lists.llvm.org
Thu Oct 21 01:33:45 PDT 2021


Author: Michał Górny
Date: 2021-10-21T10:33:38+02:00
New Revision: 92fb574c9f20aef56948c51f4921a92efbb72a5b

URL: https://github.com/llvm/llvm-project/commit/92fb574c9f20aef56948c51f4921a92efbb72a5b
DIFF: https://github.com/llvm/llvm-project/commit/92fb574c9f20aef56948c51f4921a92efbb72a5b.diff

LOG: [lldb] [Host] Add setters for common teletype properties to Terminal

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)

Differential Revision: https://reviews.llvm.org/D111030

Added: 
    

Modified: 
    lldb/include/lldb/Host/Terminal.h
    lldb/source/Host/common/Terminal.cpp
    lldb/unittests/Host/posix/TerminalTest.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Host/Terminal.h b/lldb/include/lldb/Host/Terminal.h
index f8f5d5d16ef90..ff5db4c6b9b05 100644
--- a/lldb/include/lldb/Host/Terminal.h
+++ b/lldb/include/lldb/Host/Terminal.h
@@ -20,6 +20,14 @@ class TerminalState;
 
 class Terminal {
 public:
+  enum class Parity {
+    No,
+    Even,
+    Odd,
+    Space,
+    Mark,
+  };
+
   Terminal(int fd = -1) : m_fd(fd) {}
 
   ~Terminal() = default;
@@ -38,6 +46,16 @@ class Terminal {
 
   llvm::Error SetCanonical(bool enabled);
 
+  llvm::Error SetRaw();
+
+  llvm::Error SetBaudRate(unsigned int baud_rate);
+
+  llvm::Error SetStopBits(unsigned int stop_bits);
+
+  llvm::Error SetParity(Parity parity);
+
+  llvm::Error SetHardwareFlowControl(bool enabled);
+
 protected:
   struct Data;
 

diff  --git a/lldb/source/Host/common/Terminal.cpp b/lldb/source/Host/common/Terminal.cpp
index 7050bae0c3c1c..6b7c2821d3783 100644
--- a/lldb/source/Host/common/Terminal.cpp
+++ b/lldb/source/Host/common/Terminal.cpp
@@ -94,6 +94,267 @@ llvm::Error Terminal::SetCanonical(bool enabled) {
 #endif // LLDB_ENABLE_TERMIOS
 }
 
+llvm::Error Terminal::SetRaw() {
+  llvm::Expected<Data> data = GetData();
+  if (!data)
+    return data.takeError();
+
+#if LLDB_ENABLE_TERMIOS
+  struct termios &fd_termios = data->m_termios;
+  ::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 SetData(data.get());
+#endif // #if LLDB_ENABLE_TERMIOS
+}
+
+static llvm::Optional<speed_t> baudRateToConst(unsigned int baud_rate) {
+  switch (baud_rate) {
+#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;
+  }
+}
+
+llvm::Error Terminal::SetBaudRate(unsigned int baud_rate) {
+  llvm::Expected<Data> data = GetData();
+  if (!data)
+    return data.takeError();
+
+#if LLDB_ENABLE_TERMIOS
+  struct termios &fd_termios = data->m_termios;
+  llvm::Optional<speed_t> val = baudRateToConst(baud_rate);
+  if (!val) // invalid value
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "baud rate %d unsupported by the platform",
+                                   baud_rate);
+  if (::cfsetispeed(&fd_termios, val.getValue()) != 0)
+    return llvm::createStringError(
+        std::error_code(errno, std::generic_category()),
+        "setting input baud rate failed");
+  if (::cfsetospeed(&fd_termios, val.getValue()) != 0)
+    return llvm::createStringError(
+        std::error_code(errno, std::generic_category()),
+        "setting output baud rate failed");
+  return SetData(data.get());
+#endif // #if LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetStopBits(unsigned int stop_bits) {
+  llvm::Expected<Data> data = GetData();
+  if (!data)
+    return data.takeError();
+
+#if LLDB_ENABLE_TERMIOS
+  struct termios &fd_termios = data->m_termios;
+  switch (stop_bits) {
+  case 1:
+    fd_termios.c_cflag &= ~CSTOPB;
+    break;
+  case 2:
+    fd_termios.c_cflag |= CSTOPB;
+    break;
+  default:
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "invalid stop bit count: %d (must be 1 or 2)", stop_bits);
+  }
+  return SetData(data.get());
+#endif // #if LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetParity(Terminal::Parity parity) {
+  llvm::Expected<Data> data = GetData();
+  if (!data)
+    return data.takeError();
+
+#if LLDB_ENABLE_TERMIOS
+  struct termios &fd_termios = data->m_termios;
+  fd_termios.c_cflag &= ~(
+#if defined(CMSPAR)
+      CMSPAR |
+#endif
+      PARENB | PARODD);
+
+  if (parity != Parity::No) {
+    fd_termios.c_cflag |= PARENB;
+    if (parity == Parity::Odd || parity == Parity::Mark)
+      fd_termios.c_cflag |= PARODD;
+    if (parity == Parity::Mark || parity == Parity::Space) {
+#if defined(CMSPAR)
+      fd_termios.c_cflag |= CMSPAR;
+#else
+      return llvm::createStringError(
+          llvm::inconvertibleErrorCode(),
+          "space/mark parity is not supported by the platform");
+#endif
+    }
+  }
+  return SetData(data.get());
+#endif // #if LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetHardwareFlowControl(bool enabled) {
+  llvm::Expected<Data> data = GetData();
+  if (!data)
+    return data.takeError();
+
+#if LLDB_ENABLE_TERMIOS
+#if defined(CRTSCTS)
+  struct termios &fd_termios = data->m_termios;
+  fd_termios.c_cflag &= ~CRTSCTS;
+  if (enabled)
+    fd_termios.c_cflag |= CRTSCTS;
+  return SetData(data.get());
+#else  // !defined(CRTSCTS)
+  if (enabled)
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "hardware flow control is not supported by the platform");
+  return llvm::Error::success();
+#endif // defined(CRTSCTS)
+#endif // #if LLDB_ENABLE_TERMIOS
+}
+
 TerminalState::TerminalState(Terminal term, bool save_process_group)
     : m_tty(term) {
   Save(term, save_process_group);

diff  --git a/lldb/unittests/Host/posix/TerminalTest.cpp b/lldb/unittests/Host/posix/TerminalTest.cpp
index cdc80bea95679..4a81bea88d9eb 100644
--- a/lldb/unittests/Host/posix/TerminalTest.cpp
+++ b/lldb/unittests/Host/posix/TerminalTest.cpp
@@ -72,6 +72,149 @@ TEST_F(TerminalTest, SetCanonical) {
   EXPECT_EQ(terminfo.c_lflag & ICANON, 0U);
 }
 
+TEST_F(TerminalTest, SetRaw) {
+  struct termios terminfo;
+
+  ASSERT_THAT_ERROR(m_term.SetRaw(), llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &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;
+
+  ASSERT_THAT_ERROR(m_term.SetBaudRate(38400), llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+  EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B38400));
+  EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B38400));
+
+  ASSERT_THAT_ERROR(m_term.SetBaudRate(115200), llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &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_THAT_ERROR(m_term.SetBaudRate(153600), llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+  EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B153600));
+  EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B153600));
+#else
+  ASSERT_THAT_ERROR(m_term.SetBaudRate(153600),
+                    llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+                        &llvm::ErrorInfoBase::message,
+                        "baud rate 153600 unsupported by the platform")));
+#endif
+}
+
+TEST_F(TerminalTest, SetStopBits) {
+  struct termios terminfo;
+
+  ASSERT_THAT_ERROR(m_term.SetStopBits(1), llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+  EXPECT_EQ(terminfo.c_cflag & CSTOPB, 0U);
+
+  ASSERT_THAT_ERROR(m_term.SetStopBits(2), llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+  EXPECT_NE(terminfo.c_cflag & CSTOPB, 0U);
+
+  ASSERT_THAT_ERROR(m_term.SetStopBits(0),
+                    llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+                        &llvm::ErrorInfoBase::message,
+                        "invalid stop bit count: 0 (must be 1 or 2)")));
+  ASSERT_THAT_ERROR(m_term.SetStopBits(3),
+                    llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+                        &llvm::ErrorInfoBase::message,
+                        "invalid stop bit count: 3 (must be 1 or 2)")));
+}
+
+TEST_F(TerminalTest, SetParity) {
+  struct termios terminfo;
+
+  ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::No), llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+  EXPECT_EQ(terminfo.c_cflag & PARENB, 0U);
+
+  ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Even),
+                    llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &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);
+#if defined(CMSPAR)
+  EXPECT_EQ(terminfo.c_cflag & CMSPAR, 0U);
+#endif
+
+  ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Odd), llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &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_THAT_ERROR(m_term.SetParity(Terminal::Parity::Space),
+                    llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &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_THAT_ERROR(m_term.SetParity(Terminal::Parity::Mark),
+                    llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &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
+  ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Space),
+                    llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+                        &llvm::ErrorInfoBase::message,
+                        "space/mark parity is not supported by the platform")));
+  ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Mark),
+                    llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+                        &llvm::ErrorInfoBase::message,
+                        "space/mark parity is not supported by the platform")));
+#endif
+}
+
+TEST_F(TerminalTest, SetHardwareFlowControl) {
+#if defined(CRTSCTS)
+  struct termios terminfo;
+
+  ASSERT_THAT_ERROR(m_term.SetHardwareFlowControl(true), llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+  EXPECT_NE(terminfo.c_cflag & CRTSCTS, 0U);
+
+  ASSERT_THAT_ERROR(m_term.SetHardwareFlowControl(false), llvm::Succeeded());
+  ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+  EXPECT_EQ(terminfo.c_cflag & CRTSCTS, 0U);
+#else
+  ASSERT_THAT_ERROR(
+      m_term.SetHardwareFlowControl(true),
+      llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+          &llvm::ErrorInfoBase::message,
+          "hardware flow control is not supported by the platform")));
+  ASSERT_THAT_ERROR(m_term.SetHardwareFlowControl(false), llvm::Succeeded());
+#endif
+}
+
 TEST_F(TerminalTest, SaveRestoreRAII) {
   struct termios orig_terminfo;
   struct termios terminfo;


        


More information about the lldb-commits mailing list