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.cpp320
1 files changed, 194 insertions, 126 deletions
diff --git a/src/serialport/qserialportinfo_unix.cpp b/src/serialport/qserialportinfo_unix.cpp
index 12d7966a..83d832ba 100644
--- a/src/serialport/qserialportinfo_unix.cpp
+++ b/src/serialport/qserialportinfo_unix.cpp
@@ -42,6 +42,7 @@
#include <QtCore/qdir.h>
#include <QtCore/qscopedpointer.h>
+#include <private/qcore_unix_p.h>
#include <errno.h>
#include <sys/types.h> // kill
@@ -93,7 +94,7 @@ static QStringList filteredDeviceFilePaths()
return result;
}
-QList<QSerialPortInfo> availablePortsByFiltersOfDevices()
+QList<QSerialPortInfo> availablePortsByFiltersOfDevices(bool &ok)
{
QList<QSerialPortInfo> serialPortInfoList;
@@ -104,15 +105,122 @@ QList<QSerialPortInfo> availablePortsByFiltersOfDevices()
serialPortInfoList.append(priv);
}
+ ok = true;
return serialPortInfoList;
}
-QList<QSerialPortInfo> availablePortsBySysfs()
+static bool isSerial8250Driver(const QString &driverName)
+{
+ return (driverName == QStringLiteral("serial8250"));
+}
+
+static bool isValidSerial8250(const QString &systemLocation)
+{
+#ifdef Q_OS_LINUX
+ const mode_t flags = O_RDWR | O_NONBLOCK | O_NOCTTY;
+ const int fd = qt_safe_open(systemLocation.toLocal8Bit().constData(), flags);
+ if (fd != -1) {
+ struct serial_struct serinfo;
+ const int retval = ::ioctl(fd, TIOCGSERIAL, &serinfo);
+ qt_safe_close(fd);
+ if (retval != -1 && serinfo.type != PORT_UNKNOWN)
+ return true;
+ }
+#else
+ Q_UNUSED(systemLocation);
+#endif
+ return false;
+}
+
+static bool isRfcommDevice(const QString &portName)
+{
+ if (!portName.startsWith(QStringLiteral("rfcomm")))
+ return false;
+
+ bool ok;
+ const int portNumber = portName.mid(6).toInt(&ok);
+ if (!ok || (portNumber < 0) || (portNumber > 255))
+ return false;
+ return true;
+}
+
+static QString ueventProperty(const QDir &targetDir, const QByteArray &pattern)
+{
+ QFile f(QFileInfo(targetDir, QStringLiteral("uevent")).absoluteFilePath());
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
+ return QString();
+
+ const QByteArray content = f.readAll();
+
+ const int firstbound = content.indexOf(pattern);
+ if (firstbound == -1)
+ return QString();
+
+ const int lastbound = content.indexOf('\n', firstbound);
+ return QString::fromLatin1(
+ content.mid(firstbound + pattern.size(),
+ lastbound - firstbound - pattern.size()))
+ .simplified();
+}
+
+static QString deviceName(const QDir &targetDir)
+{
+ return ueventProperty(targetDir, "DEVNAME=");
+}
+
+static QString deviceDriver(const QDir &targetDir)
+{
+ const QDir deviceDir(targetDir.absolutePath() + "/device");
+ return ueventProperty(deviceDir, "DRIVER=");
+}
+
+static QString deviceProperty(const QString &targetFilePath)
+{
+ QFile f(targetFilePath);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
+ return QString();
+ return QString::fromLatin1(f.readAll()).simplified();
+}
+
+static QString deviceDescription(const QDir &targetDir)
+{
+ return deviceProperty(QFileInfo(targetDir, QStringLiteral("product")).absoluteFilePath());
+}
+
+static QString deviceManufacturer(const QDir &targetDir)
+{
+ return deviceProperty(QFileInfo(targetDir, QStringLiteral("manufacturer")).absoluteFilePath());
+}
+
+static quint16 deviceProductIdentifier(const QDir &targetDir, bool &hasIdentifier)
+{
+ QString result = deviceProperty(QFileInfo(targetDir, QStringLiteral("idProduct")).absoluteFilePath());
+ if (result.isEmpty())
+ result = deviceProperty(QFileInfo(targetDir, QStringLiteral("device")).absoluteFilePath());
+ return result.toInt(&hasIdentifier, 16);
+}
+
+static quint16 deviceVendorIdentifier(const QDir &targetDir, bool &hasIdentifier)
+{
+ QString result = deviceProperty(QFileInfo(targetDir, QStringLiteral("idVendor")).absoluteFilePath());
+ if (result.isEmpty())
+ result = deviceProperty(QFileInfo(targetDir, QStringLiteral("vendor")).absoluteFilePath());
+ return result.toInt(&hasIdentifier, 16);
+}
+
+static QString deviceSerialNumber(const QDir &targetDir)
+{
+ return deviceProperty(QFileInfo(targetDir, QStringLiteral("serial")).absoluteFilePath());
+}
+
+QList<QSerialPortInfo> availablePortsBySysfs(bool &ok)
{
QDir ttySysClassDir(QStringLiteral("/sys/class/tty"));
- if (!(ttySysClassDir.exists() && ttySysClassDir.isReadable()))
+ if (!(ttySysClassDir.exists() && ttySysClassDir.isReadable())) {
+ ok = false;
return QList<QSerialPortInfo>();
+ }
QList<QSerialPortInfo> serialPortInfoList;
ttySysClassDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
@@ -120,92 +228,53 @@ QList<QSerialPortInfo> availablePortsBySysfs()
if (!fileInfo.isSymLink())
continue;
- const QString targetPath = fileInfo.symLinkTarget();
- const int lastIndexOfSlash = targetPath.lastIndexOf(QLatin1Char('/'));
- if (lastIndexOfSlash == -1)
- continue;
+ QDir targetDir(fileInfo.symLinkTarget());
QSerialPortInfoPrivate priv;
- if (targetPath.contains(QStringLiteral("pnp"))) {
- // TODO: Obtain more information
-#ifndef Q_OS_ANDROID
- } else if (targetPath.contains(QStringLiteral("platform"))) {
-#else
- } else if (targetPath.contains(QStringLiteral("platform")) && !targetPath.contains(QStringLiteral("ttyUSB")) ) {
-#endif
- continue;
- } else if (targetPath.contains(QStringLiteral("usb"))) {
- QDir targetDir(targetPath);
- targetDir.setFilter(QDir::Files | QDir::Readable);
- targetDir.setNameFilters(QStringList(QStringLiteral("uevent")));
-
- do {
- const QFileInfoList entryInfoList = targetDir.entryInfoList();
- if (entryInfoList.isEmpty())
- continue;
-
- QFile uevent(entryInfoList.first().absoluteFilePath());
- if (!uevent.open(QIODevice::ReadOnly | QIODevice::Text))
- continue;
-
- const QString ueventContent = QString::fromLatin1(uevent.readAll());
-
- if (ueventContent.contains(QStringLiteral("DEVTYPE=usb_device"))
- && ueventContent.contains(QStringLiteral("DRIVER=usb"))) {
-
- QFile description(QFileInfo(targetDir, QStringLiteral("product")).absoluteFilePath());
- if (description.open(QIODevice::ReadOnly | QIODevice::Text))
- priv.description = QString::fromLatin1(description.readAll()).simplified();
-
- QFile manufacturer(QFileInfo(targetDir, QStringLiteral("manufacturer")).absoluteFilePath());
- if (manufacturer.open(QIODevice::ReadOnly | QIODevice::Text))
- priv.manufacturer = QString::fromLatin1(manufacturer.readAll()).simplified();
-
- QFile serialNumber(QFileInfo(targetDir, QStringLiteral("serial")).absoluteFilePath());
- if (serialNumber.open(QIODevice::ReadOnly | QIODevice::Text))
- priv.serialNumber = QString::fromLatin1(serialNumber.readAll()).simplified();
-
- QFile vendorIdentifier(QFileInfo(targetDir, QStringLiteral("idVendor")).absoluteFilePath());
- if (vendorIdentifier.open(QIODevice::ReadOnly | QIODevice::Text)) {
- priv.vendorIdentifier = QString::fromLatin1(vendorIdentifier.readAll())
- .toInt(&priv.hasVendorIdentifier, 16);
- }
-
- QFile productIdentifier(QFileInfo(targetDir, QStringLiteral("idProduct")).absoluteFilePath());
- if (productIdentifier.open(QIODevice::ReadOnly | QIODevice::Text)) {
- priv.productIdentifier = QString::fromLatin1(productIdentifier.readAll())
- .toInt(&priv.hasProductIdentifier, 16);
- }
-
- break;
- }
- } while (targetDir.cdUp());
-
- } else if (targetPath.contains(QStringLiteral("pci"))) {
- QDir targetDir(targetPath + QStringLiteral("/device"));
- QFile vendorIdentifier(QFileInfo(targetDir, QStringLiteral("vendor")).absoluteFilePath());
- if (vendorIdentifier.open(QIODevice::ReadOnly | QIODevice::Text)) {
- priv.vendorIdentifier = QString::fromLatin1(vendorIdentifier.readAll())
- .toInt(&priv.hasVendorIdentifier, 16);
- }
- QFile productIdentifier(QFileInfo(targetDir, QStringLiteral("device")).absoluteFilePath());
- if (productIdentifier.open(QIODevice::ReadOnly | QIODevice::Text)) {
- priv.productIdentifier = QString::fromLatin1(productIdentifier.readAll())
- .toInt(&priv.hasProductIdentifier, 16);
- }
- // TODO: Obtain more information about the device
- } else if (targetPath.contains(QStringLiteral(".serial/tty/tty"))) {
- // This condition matches onboard serial port on embedded devices.
- // Keep those devices in the list
- } else {
+
+ priv.portName = deviceName(targetDir);
+ if (priv.portName.isEmpty())
continue;
+
+ const QString driverName = deviceDriver(targetDir);
+ if (driverName.isEmpty()) {
+ if (!isRfcommDevice(priv.portName))
+ continue;
}
- priv.portName = targetPath.mid(lastIndexOfSlash + 1);
priv.device = QSerialPortInfoPrivate::portNameToSystemLocation(priv.portName);
+ if (isSerial8250Driver(driverName) && !isValidSerial8250(priv.device))
+ continue;
+
+ do {
+ if (priv.description.isEmpty())
+ priv.description = deviceDescription(targetDir);
+
+ if (priv.manufacturer.isEmpty())
+ priv.manufacturer = deviceManufacturer(targetDir);
+
+ if (priv.serialNumber.isEmpty())
+ priv.serialNumber = deviceSerialNumber(targetDir);
+
+ if (!priv.hasVendorIdentifier)
+ priv.vendorIdentifier = deviceVendorIdentifier(targetDir, priv.hasVendorIdentifier);
+
+ if (!priv.hasProductIdentifier)
+ priv.productIdentifier = deviceProductIdentifier(targetDir, priv.hasProductIdentifier);
+
+ if (!priv.description.isEmpty()
+ || !priv.manufacturer.isEmpty()
+ || !priv.serialNumber.isEmpty()
+ || priv.hasVendorIdentifier
+ || priv.hasProductIdentifier) {
+ break;
+ }
+ } while (targetDir.cdUp());
+
serialPortInfoList.append(priv);
}
+ ok = true;
return serialPortInfoList;
}
@@ -237,53 +306,55 @@ struct ScopedPointerUdevDeviceDeleter
Q_GLOBAL_STATIC(QLibrary, udevLibrary)
#endif
-static
-QString getUdevPropertyValue(struct ::udev_device *dev, const char *name)
+static QString deviceProperty(struct ::udev_device *dev, const char *name)
{
return QString::fromLatin1(::udev_device_get_property_value(dev, name));
}
-static
-bool checkUdevForSerial8250Driver(struct ::udev_device *dev)
+static QString deviceDriver(struct ::udev_device *dev)
{
- const QString driverName = QString::fromLatin1(::udev_device_get_driver(dev));
- return (driverName == QStringLiteral("serial8250"));
+ return QString::fromLatin1(::udev_device_get_driver(dev));
}
-static
-QString getUdevModelName(struct ::udev_device *dev)
+static QString deviceDescription(struct ::udev_device *dev)
{
- return getUdevPropertyValue(dev, "ID_MODEL")
- .replace(QLatin1Char('_'), QLatin1Char(' '));
+ return deviceProperty(dev, "ID_MODEL").replace(QLatin1Char('_'), QLatin1Char(' '));
}
-static
-QString getUdevVendorName(struct ::udev_device *dev)
+static QString deviceManufacturer(struct ::udev_device *dev)
{
- return getUdevPropertyValue(dev, "ID_VENDOR")
- .replace(QLatin1Char('_'), QLatin1Char(' '));
+ return deviceProperty(dev, "ID_VENDOR").replace(QLatin1Char('_'), QLatin1Char(' '));
}
-static
-quint16 getUdevModelIdentifier(struct ::udev_device *dev, bool &hasIdentifier)
+static quint16 deviceProductIdentifier(struct ::udev_device *dev, bool &hasIdentifier)
{
- return getUdevPropertyValue(dev, "ID_MODEL_ID").toInt(&hasIdentifier, 16);
+ return deviceProperty(dev, "ID_MODEL_ID").toInt(&hasIdentifier, 16);
}
-static
-quint16 getUdevVendorIdentifier(struct ::udev_device *dev, bool &hasIdentifier)
+static quint16 deviceVendorIdentifier(struct ::udev_device *dev, bool &hasIdentifier)
{
- return getUdevPropertyValue(dev, "ID_VENDOR_ID").toInt(&hasIdentifier, 16);
+ return deviceProperty(dev, "ID_VENDOR_ID").toInt(&hasIdentifier, 16);
}
-static
-QString getUdevSerialNumber(struct ::udev_device *dev)
+static QString deviceSerialNumber(struct ::udev_device *dev)
{
- return getUdevPropertyValue(dev,"ID_SERIAL_SHORT");
+ return deviceProperty(dev,"ID_SERIAL_SHORT");
}
-QList<QSerialPortInfo> availablePortsByUdev()
+static QString deviceName(struct ::udev_device *dev)
{
+ return QString::fromLatin1(::udev_device_get_sysname(dev));
+}
+
+static QString deviceLocation(struct ::udev_device *dev)
+{
+ return QString::fromLatin1(::udev_device_get_devnode(dev));
+}
+
+QList<QSerialPortInfo> availablePortsByUdev(bool &ok)
+{
+ ok = false;
+
#ifndef LINK_LIBUDEV
static bool symbolsResolved = resolveSymbols(udevLibrary());
if (!symbolsResolved)
@@ -311,6 +382,8 @@ QList<QSerialPortInfo> availablePortsByUdev()
udev_list_entry *dev_list_entry;
udev_list_entry_foreach(dev_list_entry, devices) {
+ ok = true;
+
QScopedPointer<udev_device, ScopedPointerUdevDeviceDeleter>
dev(::udev_device_new_from_syspath(
udev.data(), ::udev_list_entry_get_name(dev_list_entry)));
@@ -320,28 +393,23 @@ QList<QSerialPortInfo> availablePortsByUdev()
QSerialPortInfoPrivate priv;
- priv.device = QString::fromLatin1(::udev_device_get_devnode(dev.data()));
- priv.portName = QString::fromLatin1(::udev_device_get_sysname(dev.data()));
+ priv.device = deviceLocation(dev.data());
+ priv.portName = deviceName(dev.data());
udev_device *parentdev = ::udev_device_get_parent(dev.data());
if (parentdev) {
- if (checkUdevForSerial8250Driver(parentdev))
+ const QString driverName = deviceDriver(parentdev);
+ if (isSerial8250Driver(driverName) && !isValidSerial8250(priv.portName))
continue;
- priv.description = getUdevModelName(dev.data());
- priv.manufacturer = getUdevVendorName(dev.data());
- priv.serialNumber = getUdevSerialNumber(dev.data());
- priv.vendorIdentifier = getUdevVendorIdentifier(dev.data(), priv.hasVendorIdentifier);
- priv.productIdentifier = getUdevModelIdentifier(dev.data(), priv.hasProductIdentifier);
+ priv.description = deviceDescription(dev.data());
+ priv.manufacturer = deviceManufacturer(dev.data());
+ priv.serialNumber = deviceSerialNumber(dev.data());
+ priv.vendorIdentifier = deviceVendorIdentifier(dev.data(), priv.hasVendorIdentifier);
+ priv.productIdentifier = deviceProductIdentifier(dev.data(), priv.hasProductIdentifier);
} else {
- if (priv.portName.startsWith(rfcommDeviceName)) {
- bool ok;
- int portNumber = priv.portName.mid(rfcommDeviceName.length()).toInt(&ok);
- if (!ok || (portNumber < 0) || (portNumber > 255))
- continue;
- } else {
+ if (!isRfcommDevice(priv.portName))
continue;
- }
}
serialPortInfoList.append(priv);
@@ -352,17 +420,17 @@ QList<QSerialPortInfo> availablePortsByUdev()
QList<QSerialPortInfo> QSerialPortInfo::availablePorts()
{
- QList<QSerialPortInfo> serialPortInfoList = availablePortsByUdev();
+ bool ok;
+
+ QList<QSerialPortInfo> serialPortInfoList = availablePortsByUdev(ok);
#ifdef Q_OS_LINUX
- if (serialPortInfoList.isEmpty())
- serialPortInfoList = availablePortsBySysfs();
- else
- return serialPortInfoList;
+ if (!ok)
+ serialPortInfoList = availablePortsBySysfs(ok);
#endif
- if (serialPortInfoList.isEmpty())
- serialPortInfoList = availablePortsByFiltersOfDevices();
+ if (!ok)
+ serialPortInfoList = availablePortsByFiltersOfDevices(ok);
return serialPortInfoList;
}