diff options
-rw-r--r-- | src/serialport/qserialportinfo.h | 3 | ||||
-rw-r--r-- | src/serialport/qserialportinfo_unix.cpp | 288 |
2 files changed, 291 insertions, 0 deletions
diff --git a/src/serialport/qserialportinfo.h b/src/serialport/qserialportinfo.h index 9f0352b..2b757f9 100644 --- a/src/serialport/qserialportinfo.h +++ b/src/serialport/qserialportinfo.h @@ -84,6 +84,9 @@ private: QSerialPortInfo(const QSerialPortInfoPrivate &dd); friend QList<QSerialPortInfo> availablePortsByUdev(bool &ok); friend QList<QSerialPortInfo> availablePortsBySysfs(bool &ok); +#ifdef Q_OS_FREEBSD + friend QList<QSerialPortInfo> availablePortsBySysctl(bool &ok); +#endif friend QList<QSerialPortInfo> availablePortsByFiltersOfDevices(bool &ok); QScopedPointer<QSerialPortInfoPrivate, QSerialPortInfoPrivateDeleter> d_ptr; }; diff --git a/src/serialport/qserialportinfo_unix.cpp b/src/serialport/qserialportinfo_unix.cpp index 69c39dc..f3f5a27 100644 --- a/src/serialport/qserialportinfo_unix.cpp +++ b/src/serialport/qserialportinfo_unix.cpp @@ -42,12 +42,21 @@ #include <QtCore/qdir.h> #include <QtCore/qscopedpointer.h> +#ifdef Q_OS_FREEBSD +#include <QtCore/qdatastream.h> +#include <QtCore/qvector.h> +#endif + #include <private/qcore_unix_p.h> #include <errno.h> #include <sys/types.h> // kill #include <signal.h> // kill +#ifdef Q_OS_FREEBSD +#include <sys/sysctl.h> // sysctl, sysctlnametomib +#endif + #include "qtudev_p.h" QT_BEGIN_NAMESPACE @@ -141,6 +150,280 @@ 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<int> mibFromName(const QString &name) +{ + size_t mibsize = 0; + if (::sysctlnametomib(name.toLocal8Bit().constData(), Q_NULLPTR, &mibsize) < 0 + || mibsize == 0) { + return QVector<int>(); + } + QVector<int> mib(mibsize); + if (::sysctlnametomib(name.toLocal8Bit().constData(), &mib[0], &mibsize) < 0) + return QVector<int>(); + + return mib; +} + +static QVector<int> nextOid(const QVector<int> &previousOid) +{ + QVector<int> 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<int>(); + const size_t oidLength = requiredLength / sizeof(int); + QVector<int> oid(oidLength, 0); + if (::sysctl(&mib[0], mib.count(), &oid[0], &requiredLength, Q_NULLPTR, 0) < 0) + return QVector<int>(); + + if (previousOid.first() != oid.first()) + return QVector<int>(); + + return oid; +} + +static NodeInfo nodeForOid(const QVector<int> &oid) +{ + QVector<int> 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<NodeInfo> enumerateDesiredNodes(const QVector<int> &mib) +{ + QList<NodeInfo> nodes; + + QVector<int> oid = mib; + + forever { + const QVector<int> 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> availablePortsBySysctl(bool &ok) +{ + const QVector<int> mib = mibFromName(QLatin1String("dev")); + if (mib.isEmpty()) { + ok = false; + return QList<QSerialPortInfo>(); + } + + const QList<NodeInfo> nodes = enumerateDesiredNodes(mib); + if (nodes.isEmpty()) { + ok = false; + return QList<QSerialPortInfo>(); + } + + QDir deviceDir(QLatin1String("/dev")); + if (!(deviceDir.exists() && deviceDir.isReadable())) { + ok = false; + return QList<QSerialPortInfo>(); + } + + deviceDir.setNameFilters(QStringList() << QLatin1String("cua*")); + deviceDir.setFilter(QDir::Files | QDir::System | QDir::NoSymLinks); + + QList<QSerialPortInfo> 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"))) @@ -438,6 +721,11 @@ QList<QSerialPortInfo> QSerialPortInfo::availablePorts() serialPortInfoList = availablePortsBySysfs(ok); #endif +#ifdef Q_OS_FREEBSD + if (!ok) + serialPortInfoList = availablePortsBySysctl(ok); +#endif + if (!ok) serialPortInfoList = availablePortsByFiltersOfDevices(ok); |