diff options
Diffstat (limited to 'src/serialport/qserialportinfo_unix.cpp')
-rw-r--r-- | src/serialport/qserialportinfo_unix.cpp | 320 |
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; } |