From 02e4a66a8bd9ce4d2f7f8c89cd1bddf4b6385b5f Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 10 Feb 2016 16:24:41 +0000 Subject: Move the FreeBSD code to own implementation file ... that simplified a code and its maintenance. Change-Id: Ic4a7aa14a461bd99c745665c56300c13ab84691d Reviewed-by: Sergey Belyashov Reviewed-by: Denis Shienkov --- src/serialport/qserialportinfo.h | 3 - src/serialport/qserialportinfo_freebsd.cpp | 363 +++++++++++++++++++++++++++++ src/serialport/qserialportinfo_unix.cpp | 288 ----------------------- src/serialport/serialport-lib.pri | 11 +- 4 files changed, 370 insertions(+), 295 deletions(-) create mode 100644 src/serialport/qserialportinfo_freebsd.cpp diff --git a/src/serialport/qserialportinfo.h b/src/serialport/qserialportinfo.h index 2b757f9..9f0352b 100644 --- a/src/serialport/qserialportinfo.h +++ b/src/serialport/qserialportinfo.h @@ -84,9 +84,6 @@ private: QSerialPortInfo(const QSerialPortInfoPrivate &dd); friend QList availablePortsByUdev(bool &ok); friend QList availablePortsBySysfs(bool &ok); -#ifdef Q_OS_FREEBSD - friend QList availablePortsBySysctl(bool &ok); -#endif friend QList availablePortsByFiltersOfDevices(bool &ok); QScopedPointer d_ptr; }; diff --git a/src/serialport/qserialportinfo_freebsd.cpp b/src/serialport/qserialportinfo_freebsd.cpp new file mode 100644 index 0000000..e65f09c --- /dev/null +++ b/src/serialport/qserialportinfo_freebsd.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Denis Shienkov +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialportinfo.h" +#include "qserialportinfo_p.h" +#include "qserialport_p.h" + +#include +#include +#include + +#include +#include // kill +#include // kill + +#include // sysctl, sysctlnametomib + +QT_BEGIN_NAMESPACE + +static QString deviceProperty(const QString &pnpinfo, const QByteArray &pattern) +{ + const int firstbound = pnpinfo.indexOf(QLatin1String(pattern)); + if (firstbound == -1) + return QString(); + const int lastbound = pnpinfo.indexOf(QLatin1Char(' '), firstbound); + return pnpinfo.mid(firstbound + pattern.size(), lastbound - firstbound - pattern.size()); +} + +static QString deviceName(const QString &pnpinfo) +{ + return deviceProperty(pnpinfo, "ttyname="); +} + +static QString deviceCount(const QString &pnpinfo) +{ + return deviceProperty(pnpinfo, "ttyports="); +} + +static quint16 deviceProductIdentifier(const QString &pnpinfo, bool &hasIdentifier) +{ + QString result = deviceProperty(pnpinfo, "product="); + return result.toInt(&hasIdentifier, 16); +} + +static quint16 deviceVendorIdentifier(const QString &pnpinfo, bool &hasIdentifier) +{ + QString result = deviceProperty(pnpinfo, "vendor="); + return result.toInt(&hasIdentifier, 16); +} + +static QString deviceSerialNumber(const QString &pnpinfo) +{ + QString serialNumber = deviceProperty(pnpinfo, "sernum="); + serialNumber.remove(QLatin1Char('"')); + return serialNumber; +} + +// A 'desc' string contains the both description and manufacturer +// properties, which are not possible to extract from the source +// string. Besides, this string can contains an other information, +// which should be excluded from the result. +static QString deviceDescriptionAndManufacturer(const QString &desc) +{ + const int classindex = desc.indexOf(QLatin1String(", class ")); + if (classindex == -1) + return desc; + return desc.mid(0, classindex); +} + +struct NodeInfo +{ + QString name; + QString value; +}; + +static QVector mibFromName(const QString &name) +{ + size_t mibsize = 0; + if (::sysctlnametomib(name.toLocal8Bit().constData(), Q_NULLPTR, &mibsize) < 0 + || mibsize == 0) { + return QVector(); + } + QVector mib(mibsize); + if (::sysctlnametomib(name.toLocal8Bit().constData(), &mib[0], &mibsize) < 0) + return QVector(); + + return mib; +} + +static QVector nextOid(const QVector &previousOid) +{ + QVector mib; + mib.append(0); // Magic undocumented code (CTL_UNSPEC ?) + mib.append(2); // Magic undocumented code + foreach (int code, previousOid) + mib.append(code); + + size_t requiredLength = 0; + if (::sysctl(&mib[0], mib.count(), Q_NULLPTR, &requiredLength, Q_NULLPTR, 0) < 0) + return QVector(); + const size_t oidLength = requiredLength / sizeof(int); + QVector oid(oidLength, 0); + if (::sysctl(&mib[0], mib.count(), &oid[0], &requiredLength, Q_NULLPTR, 0) < 0) + return QVector(); + + if (previousOid.first() != oid.first()) + return QVector(); + + return oid; +} + +static NodeInfo nodeForOid(const QVector &oid) +{ + QVector mib; + mib.append(0); // Magic undocumented code (CTL_UNSPEC ?) + mib.append(1); // Magic undocumented code + foreach (int code, oid) + mib.append(code); + + // query node name + size_t requiredLength = 0; + if (::sysctl(&mib[0], mib.count(), Q_NULLPTR, &requiredLength, Q_NULLPTR, 0) < 0) + return NodeInfo(); + QByteArray name(requiredLength, 0); + if (::sysctl(&mib[0], mib.count(), name.data(), &requiredLength, Q_NULLPTR, 0) < 0) + return NodeInfo(); + + // query node value + requiredLength = 0; + if (::sysctl(&oid[0], oid.count(), Q_NULLPTR, &requiredLength, Q_NULLPTR, 0) < 0) + return NodeInfo(); + QByteArray value(requiredLength, 0); + if (::sysctl(&oid[0], oid.count(), value.data(), &requiredLength, Q_NULLPTR, 0) < 0) + return NodeInfo(); + + // query value format + mib[1] = 4; // Magic undocumented code + requiredLength = 0; + if (::sysctl(&mib[0], mib.count(), Q_NULLPTR, &requiredLength, Q_NULLPTR, 0) < 0) + return NodeInfo(); + QByteArray buf(requiredLength, 0); + if (::sysctl(&mib[0], mib.count(), buf.data(), &requiredLength, Q_NULLPTR, 0) < 0) + return NodeInfo(); + + QDataStream in(buf); + in.setByteOrder(QDataStream::LittleEndian); + quint32 kind = 0; + qint8 format = 0; + in >> kind >> format; + + NodeInfo result; + + // we need only the string-type value + if (format == 'A') { + result.name = QString::fromLocal8Bit(name.constData()); + result.value = QString::fromLocal8Bit(value.constData()); + } + + return result; +} + +static QList enumerateDesiredNodes(const QVector &mib) +{ + QList nodes; + + QVector oid = mib; + + forever { + const QVector nextoid = nextOid(oid); + if (nextoid.isEmpty()) + break; + + const NodeInfo node = nodeForOid(nextoid); + if (!node.name.isEmpty()) { + if (node.name.endsWith("\%desc") + || node.name.endsWith("\%pnpinfo")) { + nodes.append(node); + } + } + + oid = nextoid; + } + + return nodes; +} + +QList QSerialPortInfo::availablePorts() +{ + const QVector mib = mibFromName(QLatin1String("dev")); + if (mib.isEmpty()) + return QList(); + + const QList nodes = enumerateDesiredNodes(mib); + if (nodes.isEmpty()) + return QList(); + + QDir deviceDir(QLatin1String("/dev")); + if (!(deviceDir.exists() && deviceDir.isReadable())) + return QList(); + + deviceDir.setNameFilters(QStringList() << QLatin1String("cua*")); + deviceDir.setFilter(QDir::Files | QDir::System | QDir::NoSymLinks); + + QList serialPortInfoList; + + foreach (const QString &portName, deviceDir.entryList()) { + if (portName.endsWith(QLatin1String(".init")) + || portName.endsWith(QLatin1String(".lock"))) { + continue; + } + + QSerialPortInfoPrivate priv; + priv.portName = portName; + priv.device = QSerialPortInfoPrivate::portNameToSystemLocation(portName); + + foreach (const NodeInfo &node, nodes) { + const int pnpinfoindex = node.name.indexOf(QLatin1String("\%pnpinfo")); + if (pnpinfoindex == -1) + continue; + + if (node.value.isEmpty()) + continue; + + QString ttyname = deviceName(node.value); + if (ttyname.isEmpty()) + continue; + + const QString ttyportscount = deviceCount(node.value); + if (ttyportscount.isEmpty()) + continue; + + const int count = ttyportscount.toInt(); + if (count == 0) + continue; + if (count > 1) { + bool matched = false; + for (int i = 0; i < count; ++i) { + const QString ends = QString(QLatin1String("%1.%2")).arg(ttyname).arg(i); + if (portName.endsWith(ends)) { + matched = true; + break; + } + } + + if (!matched) + continue; + } else { + if (!portName.endsWith(ttyname)) + continue; + } + + priv.serialNumber = deviceSerialNumber(node.value); + priv.vendorIdentifier = deviceVendorIdentifier(node.value, priv.hasVendorIdentifier); + priv.productIdentifier = deviceProductIdentifier(node.value, priv.hasProductIdentifier); + + const QString nodebase = node.name.mid(0, pnpinfoindex); + const QString descnode = QString(QLatin1String("%1\%desc")).arg(nodebase); + + // search for description and manufacturer properties + foreach (const NodeInfo &node, nodes) { + if (node.name != descnode) + continue; + + if (node.value.isEmpty()) + continue; + + // We can not separate the description and the manufacturer + // properties from the node value, so lets just duplicate it. + priv.description = deviceDescriptionAndManufacturer(node.value); + priv.manufacturer = priv.description; + break; + } + + break; + } + + serialPortInfoList.append(priv); + } + + return serialPortInfoList; +} + +QList QSerialPortInfo::standardBaudRates() +{ + return QSerialPortPrivate::standardBaudRates(); +} + +bool QSerialPortInfo::isBusy() const +{ + QString lockFilePath = serialPortLockFilePath(portName()); + if (lockFilePath.isEmpty()) + return false; + + QFile reader(lockFilePath); + if (!reader.open(QIODevice::ReadOnly)) + return false; + + QByteArray pidLine = reader.readLine(); + pidLine.chop(1); + if (pidLine.isEmpty()) + return false; + + qint64 pid = pidLine.toLongLong(); + + if (pid && (::kill(pid, 0) == -1) && (errno == ESRCH)) + return false; // PID doesn't exist anymore + + return true; +} + +#if QT_DEPRECATED_SINCE(5, 2) +bool QSerialPortInfo::isValid() const +{ + QFile f(systemLocation()); + return f.exists(); +} +#endif // QT_DEPRECATED_SINCE(5, 2) + +QString QSerialPortInfoPrivate::portNameToSystemLocation(const QString &source) +{ + return (source.startsWith(QLatin1Char('/')) + || source.startsWith(QLatin1String("./")) + || source.startsWith(QLatin1String("../"))) + ? source : (QLatin1String("/dev/") + source); +} + +QString QSerialPortInfoPrivate::portNameFromSystemLocation(const QString &source) +{ + return source.startsWith(QLatin1String("/dev/")) + ? source.mid(5) : source; +} + +QT_END_NAMESPACE diff --git a/src/serialport/qserialportinfo_unix.cpp b/src/serialport/qserialportinfo_unix.cpp index f5bbfc6..2a66635 100644 --- a/src/serialport/qserialportinfo_unix.cpp +++ b/src/serialport/qserialportinfo_unix.cpp @@ -42,21 +42,12 @@ #include #include -#ifdef Q_OS_FREEBSD -#include -#include -#endif - #include #include #include // kill #include // kill -#ifdef Q_OS_FREEBSD -#include // sysctl, sysctlnametomib -#endif - #include "qtudev_p.h" QT_BEGIN_NAMESPACE @@ -152,280 +143,6 @@ static bool isValidSerial8250(const QString &systemLocation) return false; } -#ifdef Q_OS_FREEBSD - -static QString deviceProperty(const QString &pnpinfo, const QByteArray &pattern) -{ - const int firstbound = pnpinfo.indexOf(QLatin1String(pattern)); - if (firstbound == -1) - return QString(); - const int lastbound = pnpinfo.indexOf(QLatin1Char(' '), firstbound); - return pnpinfo.mid(firstbound + pattern.size(), lastbound - firstbound - pattern.size()); -} - -static QString deviceName(const QString &pnpinfo) -{ - return deviceProperty(pnpinfo, "ttyname="); -} - -static QString deviceCount(const QString &pnpinfo) -{ - return deviceProperty(pnpinfo, "ttyports="); -} - -static quint16 deviceProductIdentifier(const QString &pnpinfo, bool &hasIdentifier) -{ - QString result = deviceProperty(pnpinfo, "product="); - return result.toInt(&hasIdentifier, 16); -} - -static quint16 deviceVendorIdentifier(const QString &pnpinfo, bool &hasIdentifier) -{ - QString result = deviceProperty(pnpinfo, "vendor="); - return result.toInt(&hasIdentifier, 16); -} - -static QString deviceSerialNumber(const QString &pnpinfo) -{ - QString serialNumber = deviceProperty(pnpinfo, "sernum="); - serialNumber.remove(QLatin1Char('"')); - return serialNumber; -} - -// A 'desc' string contains the both description and manufacturer -// properties, which are not possible to extract from the source -// string. Besides, this string can contains an other information, -// which should be excluded from the result. -static QString deviceDescriptionAndManufacturer(const QString &desc) -{ - const int classindex = desc.indexOf(QLatin1String(", class ")); - if (classindex == -1) - return desc; - return desc.mid(0, classindex); -} - -struct NodeInfo -{ - QString name; - QString value; -}; - -static QVector mibFromName(const QString &name) -{ - size_t mibsize = 0; - if (::sysctlnametomib(name.toLocal8Bit().constData(), Q_NULLPTR, &mibsize) < 0 - || mibsize == 0) { - return QVector(); - } - QVector mib(mibsize); - if (::sysctlnametomib(name.toLocal8Bit().constData(), &mib[0], &mibsize) < 0) - return QVector(); - - return mib; -} - -static QVector nextOid(const QVector &previousOid) -{ - QVector mib; - mib.append(0); // Magic undocumented code (CTL_UNSPEC ?) - mib.append(2); // Magic undocumented code - foreach (int code, previousOid) - mib.append(code); - - size_t requiredLength = 0; - if (::sysctl(&mib[0], mib.count(), Q_NULLPTR, &requiredLength, Q_NULLPTR, 0) < 0) - return QVector(); - const size_t oidLength = requiredLength / sizeof(int); - QVector oid(oidLength, 0); - if (::sysctl(&mib[0], mib.count(), &oid[0], &requiredLength, Q_NULLPTR, 0) < 0) - return QVector(); - - if (previousOid.first() != oid.first()) - return QVector(); - - return oid; -} - -static NodeInfo nodeForOid(const QVector &oid) -{ - QVector mib; - mib.append(0); // Magic undocumented code (CTL_UNSPEC ?) - mib.append(1); // Magic undocumented code - foreach (int code, oid) - mib.append(code); - - // query node name - size_t requiredLength = 0; - if (::sysctl(&mib[0], mib.count(), Q_NULLPTR, &requiredLength, Q_NULLPTR, 0) < 0) - return NodeInfo(); - QByteArray name(requiredLength, 0); - if (::sysctl(&mib[0], mib.count(), name.data(), &requiredLength, Q_NULLPTR, 0) < 0) - return NodeInfo(); - - // query node value - requiredLength = 0; - if (::sysctl(&oid[0], oid.count(), Q_NULLPTR, &requiredLength, Q_NULLPTR, 0) < 0) - return NodeInfo(); - QByteArray value(requiredLength, 0); - if (::sysctl(&oid[0], oid.count(), value.data(), &requiredLength, Q_NULLPTR, 0) < 0) - return NodeInfo(); - - // query value format - mib[1] = 4; // Magic undocumented code - requiredLength = 0; - if (::sysctl(&mib[0], mib.count(), Q_NULLPTR, &requiredLength, Q_NULLPTR, 0) < 0) - return NodeInfo(); - QByteArray buf(requiredLength, 0); - if (::sysctl(&mib[0], mib.count(), buf.data(), &requiredLength, Q_NULLPTR, 0) < 0) - return NodeInfo(); - - QDataStream in(buf); - in.setByteOrder(QDataStream::LittleEndian); - quint32 kind = 0; - qint8 format = 0; - in >> kind >> format; - - NodeInfo result; - - // we need only the string-type value - if (format == 'A') { - result.name = QString::fromLocal8Bit(name.constData()); - result.value = QString::fromLocal8Bit(value.constData()); - } - - return result; -} - -static QList enumerateDesiredNodes(const QVector &mib) -{ - QList nodes; - - QVector oid = mib; - - forever { - const QVector nextoid = nextOid(oid); - if (nextoid.isEmpty()) - break; - - const NodeInfo node = nodeForOid(nextoid); - if (!node.name.isEmpty()) { - if (node.name.endsWith("\%desc") - || node.name.endsWith("\%pnpinfo")) { - nodes.append(node); - } - } - - oid = nextoid; - } - - return nodes; -} - -QList availablePortsBySysctl(bool &ok) -{ - const QVector mib = mibFromName(QLatin1String("dev")); - if (mib.isEmpty()) { - ok = false; - return QList(); - } - - const QList nodes = enumerateDesiredNodes(mib); - if (nodes.isEmpty()) { - ok = false; - return QList(); - } - - QDir deviceDir(QLatin1String("/dev")); - if (!(deviceDir.exists() && deviceDir.isReadable())) { - ok = false; - return QList(); - } - - deviceDir.setNameFilters(QStringList() << QLatin1String("cua*")); - deviceDir.setFilter(QDir::Files | QDir::System | QDir::NoSymLinks); - - QList serialPortInfoList; - - foreach (const QString &portName, deviceDir.entryList()) { - if (portName.endsWith(QLatin1String(".init")) - || portName.endsWith(QLatin1String(".lock"))) { - continue; - } - - QSerialPortInfoPrivate priv; - priv.portName = portName; - priv.device = QSerialPortInfoPrivate::portNameToSystemLocation(portName); - - foreach (const NodeInfo &node, nodes) { - const int pnpinfoindex = node.name.indexOf(QLatin1String("\%pnpinfo")); - if (pnpinfoindex == -1) - continue; - - if (node.value.isEmpty()) - continue; - - QString ttyname = deviceName(node.value); - if (ttyname.isEmpty()) - continue; - - const QString ttyportscount = deviceCount(node.value); - if (ttyportscount.isEmpty()) - continue; - - const int count = ttyportscount.toInt(); - if (count == 0) - continue; - if (count > 1) { - bool matched = false; - for (int i = 0; i < count; ++i) { - const QString ends = QString(QLatin1String("%1.%2")).arg(ttyname).arg(i); - if (portName.endsWith(ends)) { - matched = true; - break; - } - } - - if (!matched) - continue; - } else { - if (!portName.endsWith(ttyname)) - continue; - } - - priv.serialNumber = deviceSerialNumber(node.value); - priv.vendorIdentifier = deviceVendorIdentifier(node.value, priv.hasVendorIdentifier); - priv.productIdentifier = deviceProductIdentifier(node.value, priv.hasProductIdentifier); - - const QString nodebase = node.name.mid(0, pnpinfoindex); - const QString descnode = QString(QLatin1String("%1\%desc")).arg(nodebase); - - // search for description and manufacturer properties - foreach (const NodeInfo &node, nodes) { - if (node.name != descnode) - continue; - - if (node.value.isEmpty()) - continue; - - // We can not separate the description and the manufacturer - // properties from the node value, so lets just duplicate it. - priv.description = deviceDescriptionAndManufacturer(node.value); - priv.manufacturer = priv.description; - break; - } - - break; - } - - serialPortInfoList.append(priv); - } - - ok = true; - return serialPortInfoList; -} - -#endif // Q_OS_FREEBSD - static bool isRfcommDevice(const QString &portName) { if (!portName.startsWith(QLatin1String("rfcomm"))) @@ -736,11 +453,6 @@ QList QSerialPortInfo::availablePorts() serialPortInfoList = availablePortsBySysfs(ok); #endif -#ifdef Q_OS_FREEBSD - if (!ok) - serialPortInfoList = availablePortsBySysctl(ok); -#endif - if (!ok) serialPortInfoList = availablePortsByFiltersOfDevices(ok); diff --git a/src/serialport/serialport-lib.pri b/src/serialport/serialport-lib.pri index c3b00ea..7179bea 100644 --- a/src/serialport/serialport-lib.pri +++ b/src/serialport/serialport-lib.pri @@ -37,14 +37,17 @@ unix { SOURCES += \ $$PWD/qserialport_unix.cpp - !osx { - SOURCES += \ - $$PWD/qserialportinfo_unix.cpp - } else { + osx { SOURCES += \ $$PWD/qserialportinfo_osx.cpp LIBS_PRIVATE += -framework IOKit -framework CoreFoundation + } else:freebsd { + SOURCES += \ + $$PWD/qserialportinfo_freebsd.cpp + } else { + SOURCES += \ + $$PWD/qserialportinfo_unix.cpp } } -- cgit v1.2.1