Index: lldb/include/lldb/Host/File.h =================================================================== --- lldb/include/lldb/Host/File.h +++ lldb/include/lldb/Host/File.h @@ -10,6 +10,7 @@ #define LLDB_HOST_FILE_H #include "lldb/Host/PosixApi.h" +#include "lldb/Host/Terminal.h" #include "lldb/Utility/IOObject.h" #include "lldb/Utility/Status.h" #include "lldb/lldb-private.h" @@ -433,6 +434,37 @@ const NativeFile &operator=(const NativeFile &) = delete; }; +class SerialPort : public NativeFile { +public: + struct Options { + llvm::Optional BaudRate = llvm::None; + llvm::Optional Parity = llvm::None; + llvm::Optional StopBits = llvm::None; + }; + + SerialPort() {} + SerialPort(int fd, OpenOptions options, Options serial_options, + bool transfer_ownership); + + bool IsValid() const override { + return NativeFile::IsValid() && m_is_interactive == eLazyBoolYes; + } + + Status Close() override; + + static char ID; + virtual bool isA(const void *classID) const override { + return classID == &ID || File::isA(classID); + } + static bool classof(const File *file) { return file->isA(&ID); } + +private: + SerialPort(const SerialPort &) = delete; + const SerialPort &operator=(const SerialPort &) = delete; + + TerminalState m_state; +}; + } // namespace lldb_private #endif // LLDB_HOST_FILE_H Index: lldb/include/lldb/Host/posix/ConnectionFileDescriptorPosix.h =================================================================== --- lldb/include/lldb/Host/posix/ConnectionFileDescriptorPosix.h +++ lldb/include/lldb/Host/posix/ConnectionFileDescriptorPosix.h @@ -88,6 +88,9 @@ lldb::ConnectionStatus ConnectFile(llvm::StringRef args, Status *error_ptr); + lldb::ConnectionStatus ConnectSerialPort(llvm::StringRef args, + Status *error_ptr); + lldb::IOObjectSP m_read_sp; lldb::IOObjectSP m_write_sp; Index: lldb/source/Host/common/File.cpp =================================================================== --- lldb/source/Host/common/File.cpp +++ lldb/source/Host/common/File.cpp @@ -769,5 +769,28 @@ return mode; } +SerialPort::SerialPort(int fd, OpenOptions options, + SerialPort::Options serial_options, + bool transfer_ownership) + : NativeFile(fd, options, transfer_ownership), m_state(fd) { + if (GetIsInteractive()) { + Terminal term{fd}; + // TODO: error handling? + term.SetRaw(); + if (serial_options.BaudRate) + term.SetBaudRate(serial_options.BaudRate.getValue()); + if (serial_options.Parity) + term.SetParity(serial_options.Parity.getValue()); + if (serial_options.StopBits) + term.SetStopBits(serial_options.StopBits.getValue()); + } +} + +Status SerialPort::Close() { + m_state.Restore(); + return NativeFile::Close(); +} + char File::ID = 0; char NativeFile::ID = 0; +char SerialPort::ID = 0; Index: lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp =================================================================== --- lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -154,6 +154,7 @@ #if LLDB_ENABLE_POSIX .Case("fd", &ConnectionFileDescriptor::ConnectFD) .Case("file", &ConnectionFileDescriptor::ConnectFile) + .Case("serial", &ConnectionFileDescriptor::ConnectSerialPort) #endif .Default(nullptr); @@ -753,6 +754,89 @@ llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); } +ConnectionStatus +ConnectionFileDescriptor::ConnectSerialPort(llvm::StringRef s, + Status *error_ptr) { +#if LLDB_ENABLE_POSIX + llvm::StringRef path, params; + // serial:///PATH?k1=v1&k2=v2... + std::tie(path, params) = s.split('?'); + int fd = llvm::sys::RetryAfterSignal(-1, ::open, path.str().c_str(), O_RDWR); + if (fd == -1) { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + int flags = ::fcntl(fd, F_GETFL, 0); + if (flags >= 0) { + if ((flags & O_NONBLOCK) == 0) { + flags |= O_NONBLOCK; + ::fcntl(fd, F_SETFL, flags); + } + } + + SerialPort::Options serial_options; + for (auto x : llvm::Split(params, '&')) { + if (x.consume_front("baud=")) { + unsigned int baud_rate; + if (!llvm::to_integer(x, baud_rate, 10)) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid baud rate: \"%s\"", + x.str().c_str()); + return eConnectionStatusError; + } + serial_options.BaudRate = baud_rate; + } else if (x.consume_front("parity=")) { + serial_options.Parity = + llvm::StringSwitch>(x) + .Case("no", TerminalParity::No) + .Case("even", TerminalParity::Even) + .Case("odd", TerminalParity::Odd) + .Case("mark", TerminalParity::Mark) + .Case("space", TerminalParity::Space) + .Default(llvm::None); + if (!serial_options.Parity) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "invalid parity (must be no, even, odd, mark or space): \"%s\"", + x.str().c_str()); + return eConnectionStatusError; + } + } else if (x.consume_front("stop-bits=")) { + unsigned int stop_bits; + if (!llvm::to_integer(x, stop_bits, 10) || + (stop_bits != 1 && stop_bits != 2)) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "invalid stop bits (must be 1 or 2): \"%s\"", x.str().c_str()); + return eConnectionStatusError; + } + serial_options.StopBits = stop_bits; + } else { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("unknown parameter: \"%s\"", + x.str().c_str()); + return eConnectionStatusError; + } + } + + m_read_sp = std::make_shared(fd, File::eOpenOptionReadWrite, + serial_options, true); + if (!m_read_sp->IsValid()) { + m_read_sp.reset(); + if (error_ptr) + error_ptr->SetErrorStringWithFormat("path is not a tty: \"%s\"", + path.str().c_str()); + return eConnectionStatusError; + } + m_write_sp = m_read_sp; + + return eConnectionStatusSuccess; +#endif // LLDB_ENABLE_POSIX + llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); +} + uint16_t ConnectionFileDescriptor::GetListeningPort(const Timeout &timeout) { auto Result = m_port_predicate.WaitForValueNotEqualTo(0, timeout);