diff options
author | Denis Shienkov <denis.shienkov@gmail.com> | 2015-08-11 19:17:14 +0300 |
---|---|---|
committer | Denis Shienkov <denis.shienkov@gmail.com> | 2015-08-25 12:20:31 +0000 |
commit | 3e6308409dd679389e6231745e4d919d68d6487c (patch) | |
tree | fba0fd80850770ff15541cea30fdd4a14d85b608 | |
parent | c63af1a307bdde0ba12906550aa7167d8c3be006 (diff) |
Obtain detail information in FreeBSD
Implementation based on the sysctl(3) system call,
in which undocumented features are used.
This implementation tested only with the USB devices,
because there is no opportunity to check it as well with
an other stuff.
Also still it is impossible to extract the description
and the manufacturer properties from the source, because
they are merged inside of one string.
Tested with the USB devices (FTDI, Prolific, ZTE, Samsung)
using FreeBSD v10.
Change-Id: I0091e4db70bfdfd4da199dd9d89dc78cf8f632b0
Reviewed-by: Denis Shienkov <denis.shienkov@gmail.com>
-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 9f0352b7..2b757f9c 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 69c39dcb..f3f5a27d 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); |