From 944b0f59877b1c4b8e86d2719c54ef2c6fcc4639 Mon Sep 17 00:00:00 2001 From: Phil Taylor Date: Tue, 11 May 2021 09:38:05 +0100 Subject: [PATCH] Initial commit of pty rewrite --- pttyhandler.cpp | 197 ++++++++++++++++++++++++------------------------ pttyhandler.h | 6 +- wfmain.cpp | 26 +++++-- wfview.pro | 1 - 4 files changed, 123 insertions(+), 107 deletions(-) diff --git a/pttyhandler.cpp b/pttyhandler.cpp index 25f77aa..8457021 100644 --- a/pttyhandler.cpp +++ b/pttyhandler.cpp @@ -2,94 +2,54 @@ #include "logcategories.h" #include +#include + +#include +#include +#include +#include // Copyright 2017-2021 Elliott H. Liggett & Phil Taylor pttyHandler::pttyHandler(QString pty) { //constructor - // grab baud rate and other comm port details - // if they need to be changed later, please - // destroy this and create a new one. - port = new QSerialPort(); + portName = pty; +#ifdef Q_OS_WIN + port = new QSerialPort(); // TODO: The following should become arguments and/or functions // Add signal/slot everywhere for comm port setup. // Consider how to "re-setup" and how to save the state for next time. baudRate = 115200; stopBits = 1; - -#ifdef Q_OS_WIN portName = pty; -#else - Q_UNUSED(pty); - portName = "/dev/ptmx"; #endif - if (portName != "" && portName != "None") { - setupPtty(); // basic parameters - openPort(); - //qDebug(logSerial()) << "Serial buffer size: " << port->readBufferSize(); - //port->setReadBufferSize(1024); // manually. 256 never saw any return from the radio. why... - //qDebug(logSerial()) << "Serial buffer size: " << port->readBufferSize(); - connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn())); - } -} - -pttyHandler::pttyHandler(QString portName, quint32 baudRate) -{ - //constructor - // grab baud rate and other comm port details - // if they need to be changed later, please - // destroy this and create a new one. - - port = new QSerialPort(); - - this->portName = portName; - this->baudRate = baudRate; - - setupPtty(); // basic parameters openPort(); - // TODO: The following should become arguments and/or functions - // Add signal/slot everywhere for comm port setup. - // Consider how to "re-setup" and how to save the state for next time. - // qDebug(logSerial()) << "Serial buffer size: " << port->readBufferSize(); - //port->setReadBufferSize(1024); // manually. 256 never saw any return from the radio. why... - //qDebug(logSerial()) << "Serial buffer size: " << port->readBufferSize(); - - connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn())); -} - -void pttyHandler::setupPtty() -{ - qDebug(logSerial()) << "Setting up Pseudo Term: " << portName; - serialError = false; - port->setPortName(portName); -#ifdef Q_OS_WIN - port->setBaudRate(baudRate); - port->setStopBits(QSerialPort::OneStop);// OneStop is other option -#endif } void pttyHandler::openPort() { - // qDebug(logSerial()) << "opening pt port"; - bool success; -#ifndef Q_OS_WIN - char ptname[128]; - int sysResult = 0; - QString ptLinkCmd = "ln -s "; -#endif - success = port->open(QIODevice::ReadWrite); - if (success) - { -#ifndef Q_OS_WIN + serialError = false; + bool success=false; + +#ifdef Q_OS_WIN + port->setPortName(portName); + port->setBaudRate(baudRate); + port->setStopBits(QSerialPort::OneStop);// OneStop is other option + success = port->open(QIODevice::ReadWrite); + connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn(0))); +#else + // Generic method in Linux/MacOS to find a pty + ptfd = ::posix_openpt(O_RDWR | O_NOCTTY); + + if (ptfd >=0) + { + qDebug(logSerial()) << "Opened pt device: " << ptfd << ", attempting to grant pt status"; - qDebug(logSerial()) << "Opened pt device, attempting to grant pt status"; - ptfd = port->handle(); - qDebug(logSerial()) << "ptfd: " << ptfd; if (grantpt(ptfd)) { qDebug(logSerial()) << "Failed to grantpt"; @@ -101,32 +61,42 @@ void pttyHandler::openPort() return; } // we're good! - qDebug(logSerial()) << "Opened pseudoterminal."; - qDebug(logSerial()) << "Slave name: " << ptsname(ptfd); + qDebug(logSerial()) << "Opened pseudoterminal, slave name :" << ptsname(ptfd); - ptsname_r(ptfd, ptname, 128); - ptDevSlave = QString::fromLocal8Bit(ptname); - ptLinkCmd.append(ptDevSlave); - ptLinkCmd.append(" /tmp/rig"); - sysResult = system("rm /tmp/rig"); - sysResult = system(ptLinkCmd.toStdString().c_str()); - if (sysResult) - { - qDebug(logSerial()) << "Received error from pseudo-terminal symlink command: code: [" << sysResult << "]" << " command: [" << ptLinkCmd << "]"; - } + ptReader = new QSocketNotifier(ptfd, QSocketNotifier::Read, this); + connect(ptReader, &QSocketNotifier::activated, + this, &pttyHandler::receiveDataIn); - isConnected = true; + success=true; + } #endif - } - else { + if (!success) + { ptfd = 0; - qDebug(logSerial()) << "Could not open pseudo terminal port " << portName << " , please restart."; + qDebug(logSerial()) << "Could not open pseudo terminal port, please restart."; isConnected = false; serialError = true; emit haveSerialPortError(portName, "Could not open pseudo terminal port. Please restart."); return; } + + +#ifndef Q_OS_WIN + ptDevSlave = QString::fromLocal8Bit(ptsname(ptfd)); + + if (portName != "" && portName != "none") + { + if (!QFile::link(ptDevSlave, portName)) + { + qDebug(logSerial()) << "Error creating link to" << ptDevSlave << "from" << portName; + } else { + qDebug(logSerial()) << "Created link to" << ptDevSlave << "from" << portName; + } + } +#endif + + isConnected = true; } pttyHandler::~pttyHandler() @@ -157,8 +127,11 @@ void pttyHandler::sendDataOut(const QByteArray& writeData) //printHex(writeData, false, true); mutex.lock(); - +#ifdef Q_OS_WIN bytesWritten = port->write(writeData); +#else + bytesWritten = ::write(ptfd, writeData.constData(), writeData.size()); +#endif if (bytesWritten != writeData.length()) { qDebug(logSerial()) << "bytesWritten: " << bytesWritten << " length of byte array: " << writeData.length()\ << " size of byte array: " << writeData.size()\ @@ -167,23 +140,44 @@ void pttyHandler::sendDataOut(const QByteArray& writeData) mutex.unlock(); } +void pttyHandler::receiveDataIn(int fd) { -void pttyHandler::receiveDataIn() -{ - // connected to comm port data signal +#ifndef Q_OS_WIN + ssize_t available = 255; // Read up to 'available' bytes +#else + Q_UNUSED(fd); +#endif - // Here we get a little specific to CIV radios - // because we know what constitutes a valid "frame" of data. + // Linux will correctly return the number of available bytes with the FIONREAD ioctl + // Sadly MacOS always returns zero! +#ifdef Q_OS_LINUX + int ret = ::ioctl(fd, FIONREAD, (char *) &available); + if (ret != 0) + return; +#endif - // new code: +#ifdef Q_OS_WIN port->startTransaction(); inPortData = port->readAll(); +#else + inPortData.resize(available); + ssize_t got = ::read(fd, inPortData.data(), available); + int err = errno; + if (got < 0) { + qDebug(logSerial()) << tr("Read failed: %1").arg(QString::fromLatin1(strerror(err))); + return; + } + inPortData.resize(got); +#endif + if (inPortData.startsWith("\xFE\xFE")) { if (inPortData.endsWith("\xFD")) { // good! +#ifdef Q_OS_WIN port->commitTransaction(); +#endif // filter 1A 05 01 12/27 = C-IV transceive command before forwarding on. if (inPortData.contains(QByteArrayLiteral("\x1a\x05\x01\x12")) || inPortData.contains(QByteArrayLiteral("\x1a\x05\x01\x27"))) @@ -199,8 +193,8 @@ void pttyHandler::receiveDataIn() else { emit haveDataFromPort(inPortData); - //qDebug(logSerial()) << "Data from pseudo term:"; - //printHex(inPortData, false, true); + qDebug(logSerial()) << "Data from pseudo term:"; + printHex(inPortData, false, true); } if (rolledBack) @@ -214,8 +208,9 @@ void pttyHandler::receiveDataIn() // did not receive the entire thing so roll back: // qDebug(logSerial()) << "Rolling back transaction. End not detected. Lenth: " << inPortData.length(); //printHex(inPortData, false, true); - port->rollbackTransaction(); rolledBack = true; +#ifdef Q_OS_WIN + port->rollbackTransaction(); } } else { @@ -223,23 +218,29 @@ void pttyHandler::receiveDataIn() //qDebug(logSerial()) << "Warning: received data with invalid start. Dropping data."; //qDebug(logSerial()) << "THIS SHOULD ONLY HAPPEN ONCE!!"; // THIS SHOULD ONLY HAPPEN ONCE! - - // unrecoverable. We did not receive the start and must - // have missed it earlier because we did not roll back to - // preserve the beginning. - - //printHex(inPortData, false, true); - } +#else + } + } +#endif } + + void pttyHandler::closePort() { +#ifdef Q_OS_WIN if (port) { port->close(); delete port; } +#else + if (portName != "" && portName != "none") + { + QFile::remove(portName); + } +#endif isConnected = false; } diff --git a/pttyhandler.h b/pttyhandler.h index d8661c3..ccc7640 100644 --- a/pttyhandler.h +++ b/pttyhandler.h @@ -5,6 +5,8 @@ #include #include +#include +#include #include // This class abstracts the comm port in a useful way and connects to @@ -22,7 +24,7 @@ public: ~pttyHandler(); private slots: - void receiveDataIn(); // from physical port + void receiveDataIn(int fd); // from physical port void receiveDataFromRigToPtty(const QByteArray& data); void debugThis(); @@ -64,6 +66,8 @@ private: mutable QMutex mutex; void printHex(const QByteArray& pdata, bool printVert, bool printHoriz); + QSocketNotifier *ptReader = nullptr; + }; #endif // PTTYHANDLER_H diff --git a/wfmain.cpp b/wfmain.cpp index 4cc5ebc..ed23f5d 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -182,21 +182,33 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, QWidget *parent ui->serialDeviceListCombo->addItem("Manual...", 256); ui->serialDeviceListCombo->blockSignals(false); -#ifndef Q_OS_WIN - ui->vspCombo->setVisible(false); - ui->vspLabel->setVisible(false); -#else - i = 0; ui->vspCombo->blockSignals(true); + +#ifdef Q_OS_WIN ui->vspCombo->addItem(QString("None"), i++); + foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) { - ui->vspCombo->addItem(serialPortInfo.portName(), i++); + ui->vspCombo->addItem(serialPortInfo.portName()); } - ui->vspCombo->blockSignals(false); +#else + // Provide reasonable names for the symbolic link to the pty device + QString vspName=QDir::homePath()+"/rig-pty"; + for (i=1;i<8;i++) { + ui->vspCombo->addItem(vspName+QString::number(i)); + + if (QFile::exists(vspName+QString::number(i))) { + auto * model = qobject_cast(ui->vspCombo->model()); + auto *item = model->item(ui->vspCombo->count()-1); + item->setEnabled(false); + } + } + ui->vspCombo->addItem(vspName+QString::number(i)); + ui->vspCombo->addItem(QString("None"), i++); #endif + ui->vspCombo->blockSignals(false); setDefaultColors(); // set of UI colors with defaults populated diff --git a/wfview.pro b/wfview.pro index 7bfe8e8..a52a131 100644 --- a/wfview.pro +++ b/wfview.pro @@ -39,7 +39,6 @@ macx:LIBS += -L/usr/local/lib macx:ICON = wfview.icns QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.13 - !win32:DEFINES += HOST=\\\"`hostname`\\\" UNAME=\\\"`whoami`\\\" !win32:DEFINES += GITSHORT="\\\"$(shell git -C $$PWD rev-parse --short HEAD)\\\""