summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenis Shienkov <denis.shienkov@gmail.com>2015-08-11 19:17:14 +0300
committerDenis Shienkov <denis.shienkov@gmail.com>2015-08-26 07:49:07 +0000
commit9b016e0e28a6ff41d62172ac85c4cccce5477444 (patch)
tree229bc35ce1f12fb44d63fcbbf65cad453a56b658
parent0d476c7099432ae0a93eac691f93e4ee950ee0bd (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. (cherry-picked from 3e6308409dd679389e6231745e4d919d68d6487c) Change-Id: I0091e4db70bfdfd4da199dd9d89dc78cf8f632b0 Reviewed-by: Denis Shienkov <denis.shienkov@gmail.com>
-rw-r--r--src/serialport/qserialportinfo.h3
-rw-r--r--src/serialport/qserialportinfo_unix.cpp288
2 files changed, 291 insertions, 0 deletions
diff --git a/src/serialport/qserialportinfo.h b/src/serialport/qserialportinfo.h
index 7aa98d10..f7e719fa 100644
--- a/src/serialport/qserialportinfo.h
+++ b/src/serialport/qserialportinfo.h
@@ -92,6 +92,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 b522c3dd..7e64bfc5 100644
--- a/src/serialport/qserialportinfo_unix.cpp
+++ b/src/serialport/qserialportinfo_unix.cpp
@@ -50,12 +50,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
@@ -149,6 +158,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")))
@@ -446,6 +729,11 @@ QList<QSerialPortInfo> QSerialPortInfo::availablePorts()
serialPortInfoList = availablePortsBySysfs(ok);
#endif
+#ifdef Q_OS_FREEBSD
+ if (!ok)
+ serialPortInfoList = availablePortsBySysctl(ok);
+#endif
+
if (!ok)
serialPortInfoList = availablePortsByFiltersOfDevices(ok);