mgorny created this revision. mgorny added reviewers: labath, teemperor, krytarowski, emaste. mgorny requested review of this revision.
Add a new serial:// protocol along with SerialPort that provides a new API to open serial ports. The URL consists of serial device path followed by URL-style options, e.g.: serial:///dev/ttyS0?baud=115200&parity=even If no options are provided, the serial port is only set to raw mode and the other attributes remain unchanged. Attributes provided via options are modified to the specified values. Upon closing the serial port, its original attributes are restored. https://reviews.llvm.org/D111355 Files: lldb/include/lldb/Host/File.h lldb/include/lldb/Host/posix/ConnectionFileDescriptorPosix.h lldb/source/Host/common/File.cpp lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp
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<llvm::Optional<TerminalParity>>(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<SerialPort>(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<std::micro> &timeout) { auto Result = m_port_predicate.WaitForValueNotEqualTo(0, timeout); 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/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/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<unsigned int> BaudRate = llvm::None; + llvm::Optional<TerminalParity> Parity = llvm::None; + llvm::Optional<unsigned int> 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
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits