diff options
author | Denis Shienkov <denis.shienkov@gmail.com> | 2013-08-27 15:53:29 +0400 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-22 19:44:52 +0200 |
commit | 0973537c5471288726b929250199a41435729bee (patch) | |
tree | 0fcdef3ff7418d1f4d8349c54b6419935e1e2d66 | |
parent | eea6754a5f907cf66bdc497b587e3a4a43fadc99 (diff) |
Use sysfs on Linux when available
This change is the continuation of the previous:
* sha1: 4bdbbe4c87676ca94daae947ac5908d77966319b
The previous operation was to use the nodes in the "/dev" hierarchy.
The "sysfs" was only used for extending the information that cannot be
obtained from "/dev", like manufacturer, description of the device, etc.
This decision had a shortcoming of expansion of the list of filters in
case of appearance the new device with the new name. Besides, the Linux
kernel has a lot drivers, each of which provide a various names for an
serial devices, so we can't know all these names.
Now the sysfs is used directly, without filters. To search for devices
we use an appropriate "/sys/class/tty" directory that contains all
necessary info.
Notes:
* There are possible some regressions relating to enumeration of rfcomm,
ircomm devices. Because this devices can belong to other subsystems
which we do not process and skip at present. It needs to be checked and
submitted the set of next patches to fix it.
* If sysfs is not available, the fallback algorithm uses the "/dev"
hierarchy by having a predefined device entry list.
Tested on:
* Fedora 19 Desktop Edition (64-bit)
* Ubuntu 12.04.2 LTS (32-bit)
with on-board (PnP) and pluggable (USB) devices using Qt4 and then Qt5.
Change-Id: Ia242bb2ed81d17b6f826a187454bb9d2a12d40a9
Reviewed-by: Laszlo Papp <lpapp@kde.org>
Reviewed-by: Sergey Belyashov <Sergey.Belyashov@gmail.com>
-rw-r--r-- | src/serialport/qserialportinfo_unix.cpp | 230 |
1 files changed, 128 insertions, 102 deletions
diff --git a/src/serialport/qserialportinfo_unix.cpp b/src/serialport/qserialportinfo_unix.cpp index 1856c0e0..ea5a1b38 100644 --- a/src/serialport/qserialportinfo_unix.cpp +++ b/src/serialport/qserialportinfo_unix.cpp @@ -49,7 +49,7 @@ #ifndef Q_OS_MAC -#if defined (Q_OS_LINUX) && defined (HAVE_LIBUDEV) +#ifdef HAVE_LIBUDEV extern "C" { #include <libudev.h> @@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE #ifndef Q_OS_MAC -#if !(defined (Q_OS_LINUX) && defined (HAVE_LIBUDEV)) +#ifndef HAVE_LIBUDEV static inline const QStringList& filtersOfDevices() { @@ -90,13 +90,135 @@ static inline const QStringList& filtersOfDevices() return deviceFileNameFilterList; } -#endif +static QStringList filteredDeviceFilePaths() +{ + QStringList result; + + QDir deviceDir(QLatin1String("/dev")); + if (deviceDir.exists()) { + deviceDir.setNameFilters(filtersOfDevices()); + deviceDir.setFilter(QDir::Files | QDir::System | QDir::NoSymLinks); + QStringList deviceFilePaths; + foreach (const QFileInfo &deviceFileInfo, deviceDir.entryInfoList()) { + const QString deviceAbsoluteFilePath = deviceFileInfo.absoluteFilePath(); + if (!deviceFilePaths.contains(deviceAbsoluteFilePath)) { + deviceFilePaths.append(deviceAbsoluteFilePath); + result.append(deviceAbsoluteFilePath); + } + } + } + + return result; +} QList<QSerialPortInfo> QSerialPortInfo::availablePorts() { QList<QSerialPortInfo> serialPortInfoList; -#if defined (Q_OS_LINUX) && defined (HAVE_LIBUDEV) + bool sysfsEnabled = false; + +#ifdef Q_OS_LINUX + + QDir ttySysClassDir(QLatin1String("/sys/class/tty")); + sysfsEnabled = ttySysClassDir.exists() && ttySysClassDir.isReadable(); + + if (sysfsEnabled) { + ttySysClassDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (const QFileInfo &fileInfo, ttySysClassDir.entryInfoList()) { + if (!fileInfo.isSymLink()) + continue; + + const QString targetPath = fileInfo.symLinkTarget(); + const int lastIndexOfSlash = targetPath.lastIndexOf(QLatin1Char('/')); + if (lastIndexOfSlash == -1) + continue; + + bool canAppendToList = true; + QSerialPortInfo serialPortInfo; + + if (targetPath.contains(QLatin1String("pnp"))) { + // TODO: Implement me. + } else if (targetPath.contains(QLatin1String("platform"))) { + // Platform 'pseudo' bus for legacy device. + // Skip this devices because this type of subsystem does + // not include a real physical serial device. + canAppendToList = false; + } else if (targetPath.contains(QLatin1String("usb"))) { + + QDir targetDir(targetPath); + targetDir.setFilter(QDir::Files | QDir::Readable); + targetDir.setNameFilters(QStringList(QLatin1String("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(QLatin1String("DEVTYPE=usb_device")) + && ueventContent.contains(QLatin1String("DRIVER=usb"))) { + + QFile description(QFileInfo(targetDir, QLatin1String("product")).absoluteFilePath()); + if (description.open(QIODevice::ReadOnly | QIODevice::Text)) + serialPortInfo.d_ptr->description = QString::fromLatin1(description.readAll()).simplified(); + + QFile manufacturer(QFileInfo(targetDir, QLatin1String("manufacturer")).absoluteFilePath()); + if (manufacturer.open(QIODevice::ReadOnly | QIODevice::Text)) + serialPortInfo.d_ptr->manufacturer = QString::fromLatin1(manufacturer.readAll()).simplified(); + + QFile vendorIdentifier(QFileInfo(targetDir, QLatin1String("idVendor")).absoluteFilePath()); + if (vendorIdentifier.open(QIODevice::ReadOnly | QIODevice::Text)) { + serialPortInfo.d_ptr->vendorIdentifier = QString::fromLatin1(vendorIdentifier.readAll()) + .toInt(&serialPortInfo.d_ptr->hasVendorIdentifier, 16); + } + + QFile productIdentifier(QFileInfo(targetDir, QLatin1String("idProduct")).absoluteFilePath()); + if (productIdentifier.open(QIODevice::ReadOnly | QIODevice::Text)) { + serialPortInfo.d_ptr->productIdentifier = QString::fromLatin1(productIdentifier.readAll()) + .toInt(&serialPortInfo.d_ptr->hasProductIdentifier, 16); + } + + break; + } + } while (targetDir.cdUp()); + + } else { + // unknown types of devices + canAppendToList = false; + } + + if (canAppendToList) { + serialPortInfo.d_ptr->portName = targetPath.mid(lastIndexOfSlash + 1); + serialPortInfo.d_ptr->device = QSerialPortPrivate::portNameToSystemLocation(serialPortInfo.d_ptr->portName); + serialPortInfoList.append(serialPortInfo); + } + } + } + +#endif + + if (!sysfsEnabled) { + foreach (const QString &deviceFilePath, filteredDeviceFilePaths()) { + QSerialPortInfo serialPortInfo; + serialPortInfo.d_ptr->device = deviceFilePath; + serialPortInfo.d_ptr->portName = QSerialPortPrivate::portNameFromSystemLocation(deviceFilePath); + serialPortInfoList.append(serialPortInfo); + } + } + + return serialPortInfoList; +} + +#else + +QList<QSerialPortInfo> QSerialPortInfo::availablePorts() +{ + QList<QSerialPortInfo> serialPortInfoList; // White list for devices without a parent static const QString rfcommDeviceName(QLatin1String("rfcomm")); @@ -202,107 +324,11 @@ QList<QSerialPortInfo> QSerialPortInfo::availablePorts() ::udev_unref(udev); } -#elif defined (Q_OS_FREEBSD) && defined (HAVE_LIBUSB) - // TODO: Implement me. -#else - - QDir devDir(QLatin1String("/dev")); - if (devDir.exists()) { - - devDir.setNameFilters(filtersOfDevices()); - devDir.setFilter(QDir::Files | QDir::System | QDir::NoSymLinks); - - QStringList foundDevices; // Found devices list. - - foreach (const QFileInfo &deviceFileInfo, devDir.entryInfoList()) { - QString deviceFilePath = deviceFileInfo.absoluteFilePath(); - if (!foundDevices.contains(deviceFilePath)) { - foundDevices.append(deviceFilePath); - - QSerialPortInfo serialPortInfo; - - serialPortInfo.d_ptr->device = deviceFilePath; - serialPortInfo.d_ptr->portName = QSerialPortPrivate::portNameFromSystemLocation(deviceFilePath); - - bool canAppendToList = true; - -#if defined (Q_OS_LINUX) - - const QFileInfo ttySysClassFileInfo(QDir(QLatin1String("/sys/class/tty")) - .absoluteFilePath(serialPortInfo.d_ptr->portName)); - - const QString targetPath = ttySysClassFileInfo.isSymLink() - ? ttySysClassFileInfo.symLinkTarget() : ttySysClassFileInfo.canonicalPath(); - - if (targetPath.contains(QLatin1String("pnp"))) { - // TODO: Implement me. - } else if (targetPath.contains(QLatin1String("platform"))) { - // Platform 'pseudo' bus for legacy device. - // Skip this devices because this type of subsystem does - // not include a real physical serial device. - canAppendToList = false; - } else if (targetPath.contains(QLatin1String("usb"))) { - - QDir targetDir(targetPath); - targetDir.setFilter(QDir::Files | QDir::Readable); - targetDir.setNameFilters(QStringList(QLatin1String("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(uevent.readAll()); - - if (ueventContent.contains(QLatin1String("DEVTYPE=usb_device")) - && ueventContent.contains(QLatin1String("DRIVER=usb"))) { - - QFile description(QFileInfo(targetDir, QLatin1String("product")).absoluteFilePath()); - if (description.open(QIODevice::ReadOnly | QIODevice::Text)) - serialPortInfo.d_ptr->description = QString(description.readAll()).simplified(); - - QFile manufacturer(QFileInfo(targetDir, QLatin1String("manufacturer")).absoluteFilePath()); - if (manufacturer.open(QIODevice::ReadOnly | QIODevice::Text)) - serialPortInfo.d_ptr->manufacturer = QString(manufacturer.readAll()).simplified(); - - QFile vendorIdentifier(QFileInfo(targetDir, QLatin1String("idVendor")).absoluteFilePath()); - if (vendorIdentifier.open(QIODevice::ReadOnly | QIODevice::Text)) { - serialPortInfo.d_ptr->vendorIdentifier = QString::fromLatin1(vendorIdentifier.readAll()) - .toInt(&serialPortInfo.d_ptr->hasVendorIdentifier, 16); - } - - QFile productIdentifier(QFileInfo(targetDir, QLatin1String("idProduct")).absoluteFilePath()); - if (productIdentifier.open(QIODevice::ReadOnly | QIODevice::Text)) { - serialPortInfo.d_ptr->productIdentifier = QString::fromLatin1(productIdentifier.readAll()) - .toInt(&serialPortInfo.d_ptr->hasProductIdentifier, 16); - } - - break; - } - } while (targetDir.cdUp()); - - } else { - // Unknown types of devices - canAppendToList = false; - } - -#endif - if (canAppendToList) - serialPortInfoList.append(serialPortInfo); - - } - } - } - -#endif - return serialPortInfoList; } +#endif + #endif // Q_OS_MAC // common part |