summaryrefslogtreecommitdiffstats
path: root/src/serialport/qserialportinfo_unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/serialport/qserialportinfo_unix.cpp')
-rw-r--r--src/serialport/qserialportinfo_unix.cpp288
1 files changed, 288 insertions, 0 deletions
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);