File indexing completed on 2025-04-20 08:13:27
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