File indexing completed on 2024-06-16 11:34:24

0001 /***************************************************************************
0002  *   Copyright (C) 2005 by David Saxton                                    *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "port.h"
0012 
0013 #include <QScopedPointer>
0014 #include <QSerialPort>
0015 #include <QSerialPortInfo>
0016 
0017 #ifdef Q_OS_UNIX
0018 #include <errno.h>
0019 #include <fcntl.h>
0020 #include <sys/ioctl.h>
0021 #include <unistd.h>
0022 #endif
0023 
0024 #ifdef Q_OS_LINUX
0025 #include <linux/ppdev.h>
0026 #endif
0027 
0028 #include <ktechlab_debug.h>
0029 
0030 // BEGIN class Port
0031 Port::Port()
0032 {
0033 }
0034 
0035 Port::~Port()
0036 {
0037 }
0038 
0039 QStringList Port::ports()
0040 {
0041     return SerialPort::ports() + ParallelPort::ports();
0042 }
0043 // END class Port
0044 
0045 // BEGIN class SerialPort
0046 SerialPort::SerialPort()
0047 {
0048     m_port = nullptr;
0049 }
0050 
0051 SerialPort::~SerialPort()
0052 {
0053     closePort();
0054 }
0055 
0056 void SerialPort::setBreakEnabled(bool state)
0057 {
0058     if (!m_port)
0059         return;
0060 
0061     m_port->setBreakEnabled(state);
0062 }
0063 
0064 void SerialPort::setDataTerminalReady(bool state)
0065 {
0066     if (!m_port)
0067         return;
0068 
0069     m_port->setDataTerminalReady(state);
0070 }
0071 
0072 void SerialPort::setDataSetReady(bool state)
0073 {
0074     if (!m_port)
0075         return;
0076 
0077 #ifdef Q_OS_UNIX
0078     const int flags = TIOCM_DSR;
0079     if (ioctl(m_port->handle(), state ? TIOCMBIS : TIOCMBIC, &flags) == -1)
0080         qCCritical(KTL_LOG) << "Could not set DSR, errno = " << errno;
0081 #else
0082     Q_UNUSED(state);
0083     qCWarning(KTL_LOG) << "Cannot set DSR on non-Unix OS";
0084 #endif
0085 }
0086 
0087 void SerialPort::setRequestToSend(bool state)
0088 {
0089     if (!m_port)
0090         return;
0091 
0092     m_port->setRequestToSend(state);
0093 }
0094 
0095 bool SerialPort::getDataCarrierDetectSignal()
0096 {
0097     if (!m_port)
0098         return false;
0099 
0100     return m_port->pinoutSignals() & QSerialPort::DataCarrierDetectSignal;
0101 }
0102 
0103 bool SerialPort::getSecondaryReceivedDataSignal()
0104 {
0105     if (!m_port)
0106         return false;
0107 
0108     return m_port->pinoutSignals() & QSerialPort::SecondaryReceivedDataSignal;
0109 }
0110 
0111 bool SerialPort::getClearToSendSignal()
0112 {
0113     if (!m_port)
0114         return false;
0115 
0116     return m_port->pinoutSignals() & QSerialPort::ClearToSendSignal;
0117 }
0118 
0119 bool SerialPort::getRingIndicatorSignal()
0120 {
0121     if (!m_port)
0122         return false;
0123 
0124     return m_port->pinoutSignals() & QSerialPort::RingIndicatorSignal;
0125 }
0126 
0127 bool SerialPort::openPort(const QString &port, qint32 baudRate)
0128 {
0129     closePort();
0130 
0131     QScopedPointer<QSerialPort> newPort(new QSerialPort(port));
0132     newPort->setDataBits(QSerialPort::Data8);
0133     newPort->setBaudRate(baudRate);
0134     newPort->setStopBits(QSerialPort::OneStop);
0135     newPort->setFlowControl(QSerialPort::NoFlowControl);
0136     if (!newPort->open(QIODevice::ReadWrite)) {
0137         qCCritical(KTL_LOG) << "Could not open port " << port;
0138         return false;
0139     }
0140 
0141     m_port = newPort.take();
0142 
0143     return true;
0144 }
0145 
0146 void SerialPort::closePort()
0147 {
0148     if (!m_port)
0149         return;
0150 
0151     m_port->close();
0152     delete m_port;
0153     m_port = nullptr;
0154 }
0155 
0156 QStringList SerialPort::ports()
0157 {
0158     QStringList list;
0159 
0160     const auto serialPortInfos = QSerialPortInfo::availablePorts();
0161     for (const QSerialPortInfo &serialPortInfo : serialPortInfos) {
0162         list << serialPortInfo.systemLocation();
0163     }
0164 
0165     return list;
0166 }
0167 
0168 bool SerialPort::isAvailable()
0169 {
0170     return true;
0171 }
0172 // END class SerialPort
0173 
0174 // BEGIN class ParallelPort
0175 // I wasn't able to find any documentation on programming the parallel port
0176 // in Darwin, so I've just functionally neutered this section.  Apparently
0177 // parallel output is handled on a case by case basis (???) by the
0178 // manufacturer of whatever USB dongle is, unless they build it as a
0179 // Comms class device, in which case it is treated as a serial device.
0180 // ( Info from Garth Cummings, Apple Developer Technical Support )
0181 
0182 #ifdef Q_OS_LINUX
0183 
0184 const int IRQ_MODE_BIT = 1 << 20;   // Controls if pin 10 (Ack) causes interrupts
0185 const int INPUT_MODE_BIT = 1 << 21; // Controls if the data pins are input or output
0186 
0187 const unsigned long IOCTL_REG_READ[3] = {
0188     PPRDATA,
0189     PPRSTATUS,
0190     PPRCONTROL,
0191 };
0192 
0193 const unsigned long IOCTL_REG_WRITE[3] = {
0194     PPWDATA,
0195     0,
0196     PPWCONTROL,
0197 };
0198 
0199 // const int INVERT_MASK[3] = { // 2017.10.01 - comment unused code
0200 //  0x0,
0201 //  0x80, // 10000000
0202 //  0x0b, // 00001011
0203 // };
0204 
0205 #endif
0206 
0207 ParallelPort::ParallelPort()
0208 {
0209     reset();
0210 }
0211 
0212 ParallelPort::~ParallelPort()
0213 {
0214 }
0215 
0216 void ParallelPort::reset()
0217 {
0218 #ifdef Q_OS_LINUX
0219     m_file = -1;
0220     m_reg[Data] = 0;
0221     m_reg[Status] = 0;
0222     m_reg[Control] = 0;
0223     m_outputPins = INPUT_MODE_BIT | IRQ_MODE_BIT;
0224     m_inputPins = STATUS_PINS | INPUT_MODE_BIT | IRQ_MODE_BIT;
0225 #endif
0226 }
0227 
0228 // BEGIN Pin-oriented operations
0229 void ParallelPort::setPinState(int pins, bool state)
0230 {
0231 #ifdef Q_OS_LINUX
0232     // only allow writing to output pins
0233     pins &= m_outputPins;
0234 
0235     if (pins & DATA_PINS)
0236         setDataState((pins & DATA_PINS) >> 0, state);
0237 
0238     if (pins & CONTROL_PINS)
0239         setControlState((pins & CONTROL_PINS) >> 16, state);
0240 #else
0241     Q_UNUSED(pins);
0242     Q_UNUSED(state);
0243 #endif
0244 }
0245 
0246 int ParallelPort::pinState(int pins)
0247 {
0248     int value = 0;
0249 
0250 #ifdef Q_OS_LINUX
0251     // only allow reading from input pins
0252     pins &= m_inputPins;
0253 
0254     if (pins & DATA_PINS)
0255         value |= ((readFromRegister(Data) & ((pins & DATA_PINS) >> 0)) << 0);
0256 
0257     if (pins & STATUS_PINS)
0258         value |= ((readFromRegister(Status) & ((pins & STATUS_PINS) >> 8)) << 8);
0259 
0260     if (pins & CONTROL_PINS)
0261         value |= ((readFromRegister(Control) & ((pins & CONTROL_PINS) >> 16)) << 16);
0262 #else
0263     Q_UNUSED(pins);
0264 #endif
0265 
0266     return value;
0267 }
0268 
0269 void ParallelPort::setDataState(uchar pins, bool state)
0270 {
0271     uchar value = readFromRegister(Data);
0272 
0273     if (state)
0274         value |= pins;
0275     else
0276         value &= ~pins;
0277 
0278     writeToData(value);
0279 }
0280 
0281 void ParallelPort::setControlState(uchar pins, bool state)
0282 {
0283     uchar value = readFromRegister(Control);
0284 
0285     if (state)
0286         value |= pins;
0287     else
0288         value &= ~pins;
0289 
0290     writeToControl(value);
0291 }
0292 // END Pin-oriented operations
0293 
0294 // BEGIN Register-oriented operations
0295 uchar ParallelPort::readFromRegister(Register reg)
0296 {
0297 #ifdef Q_OS_LINUX
0298     if (m_file == -1)
0299         return 0;
0300 
0301     //  uchar value = inb( m_lpBase + reg ) ^ INVERT_MASK[reg];
0302     uchar value = 0;
0303     if (ioctl(m_file, IOCTL_REG_READ[reg], &value))
0304         qCCritical(KTL_LOG) << "errno=" << errno;
0305     else
0306         m_reg[reg] = value;
0307     return value;
0308 #else
0309     Q_UNUSED(reg);
0310     return 0;
0311 #endif
0312 }
0313 
0314 void ParallelPort::writeToRegister(Register reg, uchar value)
0315 {
0316 #ifdef Q_OS_LINUX
0317     if (m_file == -1)
0318         return;
0319 
0320     //  outb( value ^ INVERT_MASK[reg], m_lpBase + reg );
0321     if (ioctl(m_file, IOCTL_REG_WRITE[reg], &value))
0322         qCCritical(KTL_LOG) << "errno=" << errno;
0323     else
0324         m_reg[reg] = value;
0325 #else
0326     Q_UNUSED(reg);
0327     Q_UNUSED(value);
0328 #endif
0329 }
0330 
0331 void ParallelPort::writeToData(uchar value)
0332 {
0333     writeToRegister(Data, value);
0334 }
0335 
0336 void ParallelPort::writeToControl(uchar value)
0337 {
0338 #ifdef Q_OS_LINUX
0339     // Set all inputs to ones
0340     value |= ((m_inputPins & CONTROL_PINS) >> 16);
0341 #endif
0342 
0343     writeToRegister(Control, value);
0344 }
0345 // END Register-oriented operations
0346 
0347 // BEGIN Changing pin directions
0348 void ParallelPort::setDataDirection(Direction dir)
0349 {
0350 #ifdef Q_OS_LINUX
0351     if (dir == Input) {
0352         m_inputPins |= DATA_PINS;
0353         m_outputPins &= ~DATA_PINS;
0354     } else {
0355         m_inputPins &= DATA_PINS;
0356         m_outputPins |= ~DATA_PINS;
0357     }
0358 
0359     setPinState(INPUT_MODE_BIT, dir == Input);
0360 #else
0361     Q_UNUSED(dir);
0362 #endif
0363 }
0364 
0365 void ParallelPort::setControlDirection(int pins, Direction dir)
0366 {
0367 #ifdef Q_OS_LINUX
0368     pins &= CONTROL_PINS;
0369 
0370     if (dir == Input) {
0371         m_inputPins |= pins;
0372         m_outputPins &= ~pins;
0373     } else {
0374         m_inputPins &= pins;
0375         m_outputPins |= ~pins;
0376     }
0377 
0378     setControlState(0, true);
0379 #else
0380     Q_UNUSED(pins);
0381     Q_UNUSED(dir);
0382 #endif
0383 }
0384 // END Changing pin directions
0385 
0386 Port::ProbeResult ParallelPort::probe(const QString &port)
0387 {
0388 #ifdef Q_OS_LINUX
0389     int file = open(port.toLatin1(), O_RDWR);
0390     if (file == -1)
0391         return Port::DoesntExist;
0392 
0393     if (ioctl(file, PPCLAIM) != 0) {
0394         close(file);
0395         return Port::ExistsButNotRW;
0396     }
0397 
0398     ioctl(file, PPRELEASE);
0399     close(file);
0400     return Port::ExistsAndRW;
0401 #else
0402     Q_UNUSED(port);
0403     return Port::DoesntExist;
0404 #endif
0405 }
0406 
0407 QStringList ParallelPort::ports()
0408 {
0409     QStringList list;
0410 
0411 #ifdef Q_OS_LINUX
0412     for (unsigned i = 0; i < 8; ++i) {
0413         QString dev = QString("/dev/parport%1").arg(i);
0414         if (probe(dev) & ExistsAndRW)
0415             list << dev;
0416     }
0417 
0418     for (unsigned i = 0; i < 8; ++i) {
0419         QString dev = QString("/dev/parports/%1").arg(i);
0420         if (probe(dev) & ExistsAndRW)
0421             list << dev;
0422     }
0423 #endif
0424 
0425     return list;
0426 }
0427 
0428 bool ParallelPort::openPort(const QString &port)
0429 {
0430 #ifdef Q_OS_LINUX
0431     if (m_file != -1) {
0432         qCWarning(KTL_LOG) << "Port already open";
0433         return false;
0434     }
0435 
0436     m_file = open(port.toLatin1(), O_RDWR);
0437 
0438     if (m_file == -1) {
0439         qCCritical(KTL_LOG) << "Could not open port \"" << port << "\": errno=" << errno;
0440         return false;
0441     }
0442 
0443     if (ioctl(m_file, PPCLAIM)) {
0444         qCCritical(KTL_LOG) << "Port " << port << " must be RW";
0445         close(m_file);
0446         m_file = -1;
0447         return false;
0448     }
0449 
0450     return true;
0451 #else
0452     Q_UNUSED(port);
0453     return false;
0454 #endif
0455 }
0456 
0457 void ParallelPort::closePort()
0458 {
0459 #ifdef Q_OS_LINUX
0460     if (m_file == -1)
0461         return;
0462 
0463     int res = ioctl(m_file, PPRELEASE);
0464     close(m_file);
0465 
0466     if (res)
0467         qCCritical(KTL_LOG) << "res=" << res;
0468 
0469     m_file = -1;
0470 #endif
0471 }
0472 
0473 bool ParallelPort::isAvailable()
0474 {
0475 #ifdef Q_OS_LINUX
0476     return true;
0477 #else
0478     return false;
0479 #endif
0480 }
0481 // END class ParallelPort