diff --git a/Qss.cpp b/Qss.cpp index 842bceb..2836d2f 100644 --- a/Qss.cpp +++ b/Qss.cpp @@ -103,6 +103,8 @@ QssTtitleBar::QssTtitleBar(QWidget *parent , QssTtitleBar::~QssTtitleBar() { + delete(this->m_maxBtn); + delete(this->m_restoreBtn); } void QssTtitleBar::setTitle( QString title ) @@ -118,7 +120,6 @@ void QssTtitleBar::setIcon( QIcon icon) void QssTtitleBar::setMaxOrRestore( bool val) { m_maxOrRestore = val;//true - if ((m_type & QTitleBar_Button_Restore) && (m_type & QTitleBar_Button_Max)) { m_restoreBtn->setVisible(m_maxOrRestore); @@ -187,11 +188,6 @@ bool QssTtitleBar::eventFilter( QObject * obj, QEvent * ev ) return true; } } - else if (obj == m_iconBtn) - { - - } - return QWidget::eventFilter(obj, ev); } @@ -253,9 +249,7 @@ QssMainWindow::QssMainWindow(QWidget *parent/* = 0*/, Qt::WindowFlags flags/* = m_bLeftPress(false) { m_rcValid = QApplication::desktop()->availableGeometry(); - m_frame = new QFrame(parent, flags); - //css m_frame->setObjectName("window"); m_frame->setWindowFlags(Qt::Window | @@ -287,8 +281,6 @@ QssMainWindow::QssMainWindow(QWidget *parent/* = 0*/, Qt::WindowFlags flags/* = QTextStream in(&file); QString css = in.readAll(); //this->setStyleSheet(css); - - return; } @@ -639,9 +631,12 @@ void QssMainWindow::ScaleChanged(float scale) WId QssMainWindow::GetWID() const { + return 0; } + + void QssMainWindow::SetScale(float scale) { @@ -649,8 +644,8 @@ void QssMainWindow::SetScale(float scale) QssDialog::QssDialog(QWidget *parent) : QDialog(0), - m_mousePressedInBorder(false), - m_parent(parent) + m_parent(parent), + m_mousePressedInBorder(false) { m_rcValid = QApplication::desktop()->availableGeometry(); @@ -696,13 +691,11 @@ void QssDockWidget::paintEvent(QPaintEvent *){ void QssDockWidget::show() { - /** resize m_framem_framesizehint */ int offset = (QSSDIALOG_SHADOW_WIDTH + QSSDIALOG_BODER_WIDTH)*2;//rect()����padding��paddingframe m_frame->resize(rect().width() + offset, rect().height() + m_titleBar->rect().height() + offset); QDockWidget::show(); m_frame->show(); - //m_titleBar->show(); } void QssDialog::raise() @@ -722,7 +715,6 @@ int QssDialog::exec() m_frame->resize(rect().width() + offset, rect().height() + m_titleBar->rect().height() + offset); m_frame->setWindowModality(Qt::ApplicationModal);//Qt::ApplicationModal - //m_frame->setWindowFlags(m_frame->windowFlags() | Qt::Tool);//Qt::Tool m_frame->show(); m_frame->raise(); @@ -884,13 +876,11 @@ void QssDockWidget::onMousePressEvent( QMouseEvent * ev ) void QssDialog::onMouseReleaseEvent( QMouseEvent * ) { m_mousePressedInBorder = false; - //qDebug() << "mousePressed release in border"; } void QssDockWidget::onMouseReleaseEvent( QMouseEvent * ev ) { m_mousePressedInBorder = false; - //qDebug() << "mousePressed release in border"; } bool QssDialog::eventFilter( QObject * obj, QEvent * ev ) diff --git a/Qss.h b/Qss.h index 83051d7..ed2ee2a 100644 --- a/Qss.h +++ b/Qss.h @@ -187,7 +187,7 @@ class QssEventFilter { public: virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) Q_DECL_OVERRIDE - { + {/* MSG* pMsg = reinterpret_cast(message); if(nullptr != pMsg){ switch (pMsg->message) @@ -198,7 +198,7 @@ public: } } - } + }*/ // TODO: filter out or modify msg struct here return false; } @@ -292,11 +292,12 @@ private: void onMouseReleaseEvent(QMouseEvent * ev); private: + QWidget* m_parent; + QFrame* m_frame; QssTtitleBar* m_titleBar; QRect m_rcValid; - QWidget* m_parent; /** 边框调整大小相关 */ QRect mFrameRect; diff --git a/css/QssTitleBar.css b/css/QssTitleBar.css index 4035a68..315be89 100644 --- a/css/QssTitleBar.css +++ b/css/QssTitleBar.css @@ -44,6 +44,7 @@ QWidget#qssTitleBar >QPushButton#titlebarrestorebtn{ width: 40px; height:30px; margin-left: 1px; + margin-right: 2px; } QWidget#qssTitleBar >QPushButton#titlebarrestorebtn:enabled:hover{ background: rgb(187, 212, 238); diff --git a/css/qss.css b/css/qss.css index c2449f3..8b808ca 100644 --- a/css/qss.css +++ b/css/qss.css @@ -884,11 +884,11 @@ QToolButton#transferButton:hover { /**********按钮**********/ QPushButton{ - border: none; - width: 75px; - height: 25px; - font-size: 18px; - color: black; + border: none; + width: 75px; + height: 25px; + font-size: 18px; + color: black; } QPushButton:enabled { background: rgb(120, 170, 220); diff --git a/extserial/src/qextserialenumerator.cpp b/extserial/src/qextserialenumerator.cpp new file mode 100644 index 0000000..9ffd098 --- /dev/null +++ b/extserial/src/qextserialenumerator.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "qextserialenumerator.h" +#include "qextserialenumerator_p.h" + +#include +#include +#include + +QextSerialEnumeratorPrivate::QextSerialEnumeratorPrivate(QextSerialEnumerator *enumrator) + :q_ptr(enumrator) +{ + init_sys(); +} + +QextSerialEnumeratorPrivate::~QextSerialEnumeratorPrivate() +{ + destroy_sys(); +} + +/*! + \class QextPortInfo + + \brief The QextPortInfo class containing port information. + + Structure containing port information. + + \code + QString portName; ///< Port name. + QString physName; ///< Physical name. + QString friendName; ///< Friendly name. + QString enumName; ///< Enumerator name. + int vendorID; ///< Vendor ID. + int productID; ///< Product ID + \endcode + */ + +/*! \class QextSerialEnumerator + + \brief The QextSerialEnumerator class provides list of ports available in the system. + + \section1 Usage + To poll the system for a list of connected devices, simply use getPorts(). Each + QextPortInfo structure will populated with information about the corresponding device. + + \bold Example + \code + QList ports = QextSerialEnumerator::getPorts(); + foreach (QextPortInfo port, ports) { + // inspect port... + } + \endcode + + To enable event-driven notification of device connection events, first call + setUpNotifications() and then connect to the deviceDiscovered() and deviceRemoved() + signals. Event-driven behavior is currently available only on Windows and OS X. + + \bold Example + \code + QextSerialEnumerator *enumerator = new QextSerialEnumerator(); + connect(enumerator, SIGNAL(deviceDiscovered(const QextPortInfo &)), + myClass, SLOT(onDeviceDiscovered(const QextPortInfo &))); + connect(enumerator, SIGNAL(deviceRemoved(const QextPortInfo &)), + myClass, SLOT(onDeviceRemoved(const QextPortInfo &))); + \endcode + + \section1 Credits + Windows implementation is based on Zach Gorman's work from + \l {http://www.codeproject.com}{The Code Project} (\l http://www.codeproject.com/system/setupdi.asp). + + OS X implementation, see \l http://developer.apple.com/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Finding_Devices/chapter_4_section_2.html + + \bold author Michal Policht, Liam Staskawicz +*/ + +/*! + \fn void QextSerialEnumerator::deviceDiscovered(const QextPortInfo &info) + A new device has been connected to the system. + + setUpNotifications() must be called first to enable event-driven device notifications. + Currently only implemented on Windows and OS X. + + \a info The device that has been discovered. +*/ + +/*! + \fn void QextSerialEnumerator::deviceRemoved(const QextPortInfo &info); + A device has been disconnected from the system. + + setUpNotifications() must be called first to enable event-driven device notifications. + Currently only implemented on Windows and OS X. + + \a info The device that was disconnected. +*/ + +/*! + Constructs a QextSerialEnumerator object with the given \a parent. +*/ +QextSerialEnumerator::QextSerialEnumerator(QObject *parent) + :QObject(parent), d_ptr(new QextSerialEnumeratorPrivate(this)) +{ + if (!QMetaType::isRegistered(QMetaType::type("QextPortInfo"))) + qRegisterMetaType("QextPortInfo"); +} + +/*! + Destructs the QextSerialEnumerator object. +*/ +QextSerialEnumerator::~QextSerialEnumerator() +{ + delete d_ptr; +} + +/*! + Get list of ports. + + return list of ports currently available in the system. +*/ +QList QextSerialEnumerator::getPorts() +{ + return QextSerialEnumeratorPrivate::getPorts_sys(); +} + +/*! + Enable event-driven notifications of board discovery/removal. +*/ +void QextSerialEnumerator::setUpNotifications() +{ + Q_D(QextSerialEnumerator); + if (!d->setUpNotifications_sys(true)) + QESP_WARNING("Setup Notification Failed..."); +} + +#include "moc_qextserialenumerator.cpp" diff --git a/extserial/src/qextserialenumerator.h b/extserial/src/qextserialenumerator.h new file mode 100644 index 0000000..f207d0d --- /dev/null +++ b/extserial/src/qextserialenumerator.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef _QEXTSERIALENUMERATOR_H_ +#define _QEXTSERIALENUMERATOR_H_ + +#include +#include +#include "qextserialport_global.h" + +struct QextPortInfo { + QString portName; ///< Port name. + QString physName; ///< Physical name. + QString friendName; ///< Friendly name. + QString enumName; ///< Enumerator name. + int vendorID; ///< Vendor ID. + int productID; ///< Product ID +}; + +class QextSerialEnumeratorPrivate; +class QEXTSERIALPORT_EXPORT QextSerialEnumerator : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QextSerialEnumerator) +public: + QextSerialEnumerator(QObject *parent=0); + ~QextSerialEnumerator(); + + static QList getPorts(); + void setUpNotifications(); + +Q_SIGNALS: + void deviceDiscovered(const QextPortInfo &info); + void deviceRemoved(const QextPortInfo &info); + +private: + Q_DISABLE_COPY(QextSerialEnumerator) +#if defined(Q_OS_LINUX) && !defined(QESP_NO_UDEV) + Q_PRIVATE_SLOT(d_func(), void _q_deviceEvent()) +#endif + QextSerialEnumeratorPrivate *d_ptr; +}; + +#endif /*_QEXTSERIALENUMERATOR_H_*/ diff --git a/extserial/src/qextserialenumerator_linux.cpp b/extserial/src/qextserialenumerator_linux.cpp new file mode 100644 index 0000000..450d883 --- /dev/null +++ b/extserial/src/qextserialenumerator_linux.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** Copyright (c) 2012 Doug Brown +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "qextserialenumerator.h" +#include "qextserialenumerator_p.h" +#include +#include +#include + +void QextSerialEnumeratorPrivate::init_sys() +{ +#ifndef QESP_NO_UDEV + monitor = NULL; + notifierFd = -1; + notifier = NULL; + + udev = udev_new(); + if (!udev) + qCritical() << "Unable to initialize udev notifications"; +#endif +} + +void QextSerialEnumeratorPrivate::destroy_sys() +{ +#ifndef QESP_NO_UDEV + if (notifier) { + notifier->setEnabled(false); + delete notifier; + } + + if (monitor) + udev_monitor_unref(monitor); + + if (udev) + udev_unref(udev); +#endif +} + +#ifndef QESP_NO_UDEV +static QextPortInfo portInfoFromDevice(struct udev_device *dev) +{ + QString vendor = QString::fromLatin1(udev_device_get_property_value(dev, "ID_VENDOR_ID")); + QString product = QString::fromLatin1(udev_device_get_property_value(dev, "ID_MODEL_ID")); + + QextPortInfo pi; + pi.vendorID = vendor.toInt(0, 16); + pi.productID = product.toInt(0, 16); + pi.portName = QString::fromLatin1(udev_device_get_devnode(dev)); + pi.physName = pi.portName; + + return pi; +} +#endif + +QList QextSerialEnumeratorPrivate::getPorts_sys() +{ + QList infoList; +#ifndef QESP_NO_UDEV + struct udev *ud = udev_new(); + if (!ud) { + qCritical() << "Unable to enumerate ports because udev is not initialized."; + return infoList; + } + + struct udev_enumerate *enumerate = udev_enumerate_new(ud); + udev_enumerate_add_match_subsystem(enumerate, "tty"); + udev_enumerate_scan_devices(enumerate); + struct udev_list_entry *list = udev_enumerate_get_list_entry(enumerate); + struct udev_list_entry *entry; + udev_list_entry_foreach(entry, list) { + const char *path; + struct udev_device *dev; + + // Have to grab the actual udev device here... + path = udev_list_entry_get_name(entry); + dev = udev_device_new_from_syspath(ud, path); + + infoList.append(portInfoFromDevice(dev)); + + // Done with this device + udev_device_unref(dev); + } + // Done with the list and this udev + udev_enumerate_unref(enumerate); + udev_unref(ud); +#else + QStringList portNamePrefixes, portNameList; + portNamePrefixes << QLatin1String("ttyS*"); // list normal serial ports first + + QDir dir(QLatin1String("/dev")); + portNameList = dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name); + + // remove the values which are not serial ports for e.g. /dev/ttysa + for (int i = 0; i < portNameList.size(); i++) { + bool ok; + QString current = portNameList.at(i); + // remove the ttyS part, and check, if the other part is a number + current.remove(0,4).toInt(&ok, 10); + if (!ok) { + portNameList.removeAt(i); + i--; + } + } + + // get the non standard serial ports names + // (USB-serial, bluetooth-serial, 18F PICs, and so on) + // if you know an other name prefix for serial ports please let us know + portNamePrefixes.clear(); + portNamePrefixes << QLatin1String("ttyACM*") << QLatin1String("ttyUSB*") << QLatin1String("rfcomm*"); + portNameList += dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name); + + foreach (QString str , portNameList) { + QextPortInfo inf; + inf.physName = QLatin1String("/dev/")+str; + inf.portName = str; + + if (str.contains(QLatin1String("ttyS"))) { + inf.friendName = QLatin1String("Serial port ")+str.remove(0, 4); + } + else if (str.contains(QLatin1String("ttyUSB"))) { + inf.friendName = QLatin1String("USB-serial adapter ")+str.remove(0, 6); + } + else if (str.contains(QLatin1String("rfcomm"))) { + inf.friendName = QLatin1String("Bluetooth-serial adapter ")+str.remove(0, 6); + } + inf.enumName = QLatin1String("/dev"); // is there a more helpful name for this? + infoList.append(inf); + } +#endif + + return infoList; +} + +bool QextSerialEnumeratorPrivate::setUpNotifications_sys(bool setup) +{ + Q_UNUSED(setup); +#ifndef QESP_NO_UDEV + Q_Q(QextSerialEnumerator); + if (!udev) { + qCritical() << "Unable to initialize notifications because udev is not initialized."; + return false; + } + + // Emit signals immediately for devices already connected (Windows version seems to behave + // this way) + foreach (QextPortInfo i, getPorts_sys()) + Q_EMIT q->deviceDiscovered(i); + + // Look for tty devices from udev. + monitor = udev_monitor_new_from_netlink(udev, "udev"); + udev_monitor_filter_add_match_subsystem_devtype(monitor, "tty", NULL); + udev_monitor_enable_receiving(monitor); + notifierFd = udev_monitor_get_fd(monitor); + notifier = new QSocketNotifier(notifierFd, QSocketNotifier::Read); + q->connect(notifier, SIGNAL(activated(int)), q, SLOT(_q_deviceEvent())); + notifier->setEnabled(true); + + return true; +#else + return false; +#endif +} + +#ifndef QESP_NO_UDEV +void QextSerialEnumeratorPrivate::_q_deviceEvent() +{ + Q_Q(QextSerialEnumerator); + struct udev_device *dev = udev_monitor_receive_device(monitor); + if (dev) { + QextPortInfo pi = portInfoFromDevice(dev); + QLatin1String action(udev_device_get_action(dev)); + + if (action == QLatin1String("add")) + Q_EMIT q->deviceDiscovered(pi); + else if (action == QLatin1String("remove")) + Q_EMIT q->deviceRemoved(pi); + + udev_device_unref(dev); + } +} +#endif diff --git a/extserial/src/qextserialenumerator_osx.cpp b/extserial/src/qextserialenumerator_osx.cpp new file mode 100644 index 0000000..ad33e9a --- /dev/null +++ b/extserial/src/qextserialenumerator_osx.cpp @@ -0,0 +1,307 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "qextserialenumerator.h" +#include "qextserialenumerator_p.h" +#include +#include +#include +#include + +void QextSerialEnumeratorPrivate::init_sys() +{ +} + +void QextSerialEnumeratorPrivate::destroy_sys() +{ + IONotificationPortDestroy(notificationPortRef); +} + +// static +QList QextSerialEnumeratorPrivate::getPorts_sys() +{ + QList infoList; + io_iterator_t serialPortIterator = 0; + kern_return_t kernResult = KERN_FAILURE; + CFMutableDictionaryRef matchingDictionary; + + // first try to get any serialbsd devices, then try any USBCDC devices + if (!(matchingDictionary = IOServiceMatching(kIOSerialBSDServiceValue))) { + QESP_WARNING("IOServiceMatching returned a NULL dictionary."); + return infoList; + } + CFDictionaryAddValue(matchingDictionary, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); + + // then create the iterator with all the matching devices + if (IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &serialPortIterator) != KERN_SUCCESS) { + qCritical() << "IOServiceGetMatchingServices failed, returned" << kernResult; + return infoList; + } + iterateServicesOSX(serialPortIterator, infoList); + IOObjectRelease(serialPortIterator); + serialPortIterator = 0; + + if (!(matchingDictionary = IOServiceNameMatching("AppleUSBCDC"))) { + QESP_WARNING("IOServiceNameMatching returned a NULL dictionary."); + return infoList; + } + + if (IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &serialPortIterator) != KERN_SUCCESS) { + qCritical() << "IOServiceGetMatchingServices failed, returned" << kernResult; + return infoList; + } + iterateServicesOSX(serialPortIterator, infoList); + IOObjectRelease(serialPortIterator); + + return infoList; +} + +void QextSerialEnumeratorPrivate::iterateServicesOSX(io_object_t service, QList &infoList) +{ + // Iterate through all modems found. + io_object_t usbService; + while ((usbService = IOIteratorNext(service))) { + QextPortInfo info; + info.vendorID = 0; + info.productID = 0; + getServiceDetailsOSX(usbService, &info); + infoList.append(info); + } +} + +bool QextSerialEnumeratorPrivate::getServiceDetailsOSX(io_object_t service, QextPortInfo *portInfo) +{ + bool retval = true; + CFTypeRef bsdPathAsCFString = NULL; + CFTypeRef productNameAsCFString = NULL; + CFTypeRef vendorIdAsCFNumber = NULL; + CFTypeRef productIdAsCFNumber = NULL; + // check the name of the modem's callout device + bsdPathAsCFString = IORegistryEntryCreateCFProperty(service, CFSTR(kIOCalloutDeviceKey), + kCFAllocatorDefault, 0); + + // wander up the hierarchy until we find the level that can give us the + // vendor/product IDs and the product name, if available + io_registry_entry_t parent; + kern_return_t kernResult = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); + while (kernResult == KERN_SUCCESS && !vendorIdAsCFNumber && !productIdAsCFNumber) { + if (!productNameAsCFString) + productNameAsCFString = IORegistryEntrySearchCFProperty(parent, + kIOServicePlane, + CFSTR("Product Name"), + kCFAllocatorDefault, 0); + vendorIdAsCFNumber = IORegistryEntrySearchCFProperty(parent, + kIOServicePlane, + CFSTR(kUSBVendorID), + kCFAllocatorDefault, 0); + productIdAsCFNumber = IORegistryEntrySearchCFProperty(parent, + kIOServicePlane, + CFSTR(kUSBProductID), + kCFAllocatorDefault, 0); + io_registry_entry_t oldparent = parent; + kernResult = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent); + IOObjectRelease(oldparent); + } + + io_string_t ioPathName; + IORegistryEntryGetPath(service, kIOServicePlane, ioPathName); + portInfo->physName = ioPathName; + + if (bsdPathAsCFString) { + char path[MAXPATHLEN]; + if (CFStringGetCString((CFStringRef)bsdPathAsCFString, path, + PATH_MAX, kCFStringEncodingUTF8)) + portInfo->portName = path; + CFRelease(bsdPathAsCFString); + } + + if (productNameAsCFString) { + char productName[MAXPATHLEN]; + if (CFStringGetCString((CFStringRef)productNameAsCFString, productName, + PATH_MAX, kCFStringEncodingUTF8)) + portInfo->friendName = productName; + CFRelease(productNameAsCFString); + } + + if (vendorIdAsCFNumber) { + SInt32 vID; + if (CFNumberGetValue((CFNumberRef)vendorIdAsCFNumber, kCFNumberSInt32Type, &vID)) + portInfo->vendorID = vID; + CFRelease(vendorIdAsCFNumber); + } + + if (productIdAsCFNumber) { + SInt32 pID; + if (CFNumberGetValue((CFNumberRef)productIdAsCFNumber, kCFNumberSInt32Type, &pID)) + portInfo->productID = pID; + CFRelease(productIdAsCFNumber); + } + IOObjectRelease(service); + return retval; +} + +// IOKit callbacks registered via setupNotifications() +void deviceDiscoveredCallbackOSX(void *ctxt, io_iterator_t serialPortIterator) +{ + QextSerialEnumeratorPrivate *d = (QextSerialEnumeratorPrivate *)ctxt; + io_object_t serialService; + while ((serialService = IOIteratorNext(serialPortIterator))) + d->onDeviceDiscoveredOSX(serialService); +} + +void deviceTerminatedCallbackOSX(void *ctxt, io_iterator_t serialPortIterator) +{ + QextSerialEnumeratorPrivate *d = (QextSerialEnumeratorPrivate *)ctxt; + io_object_t serialService; + while ((serialService = IOIteratorNext(serialPortIterator))) + d->onDeviceTerminatedOSX(serialService); +} + +/* + A device has been discovered via IOKit. + Create a QextPortInfo if possible, and emit the signal indicating that we've found it. +*/ +void QextSerialEnumeratorPrivate::onDeviceDiscoveredOSX(io_object_t service) +{ + Q_Q(QextSerialEnumerator); + QextPortInfo info; + info.vendorID = 0; + info.productID = 0; + if (getServiceDetailsOSX(service, &info)) + Q_EMIT q->deviceDiscovered(info); +} + +/* + Notification via IOKit that a device has been removed. + Create a QextPortInfo if possible, and emit the signal indicating that it's gone. +*/ +void QextSerialEnumeratorPrivate::onDeviceTerminatedOSX(io_object_t service) +{ + Q_Q(QextSerialEnumerator); + QextPortInfo info; + info.vendorID = 0; + info.productID = 0; + if (getServiceDetailsOSX(service, &info)) + Q_EMIT q->deviceRemoved(info); +} + +/* + Create matching dictionaries for the devices we want to get notifications for, + and add them to the current run loop. Invoke the callbacks that will be responding + to these notifications once to arm them, and discover any devices that + are currently connected at the time notifications are setup. +*/ +bool QextSerialEnumeratorPrivate::setUpNotifications_sys(bool /*setup*/) +{ + kern_return_t kernResult; + mach_port_t masterPort; + CFRunLoopSourceRef notificationRunLoopSource; + CFMutableDictionaryRef classesToMatch; + CFMutableDictionaryRef cdcClassesToMatch; + io_iterator_t portIterator; + + kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort); + if (KERN_SUCCESS != kernResult) { + qDebug() << "IOMasterPort returned:" << kernResult; + return false; + } + + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (classesToMatch == NULL) + qDebug("IOServiceMatching returned a NULL dictionary."); + else + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); + + if (!(cdcClassesToMatch = IOServiceNameMatching("AppleUSBCDC"))) { + QESP_WARNING("couldn't create cdc matching dict"); + return false; + } + + // Retain an additional reference since each call to IOServiceAddMatchingNotification consumes one. + classesToMatch = (CFMutableDictionaryRef) CFRetain(classesToMatch); + cdcClassesToMatch = (CFMutableDictionaryRef) CFRetain(cdcClassesToMatch); + + notificationPortRef = IONotificationPortCreate(masterPort); + if (notificationPortRef == NULL) { + qDebug("IONotificationPortCreate return a NULL IONotificationPortRef."); + return false; + } + + notificationRunLoopSource = IONotificationPortGetRunLoopSource(notificationPortRef); + if (notificationRunLoopSource == NULL) { + qDebug("IONotificationPortGetRunLoopSource returned NULL CFRunLoopSourceRef."); + return false; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), notificationRunLoopSource, kCFRunLoopDefaultMode); + + kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOMatchedNotification, classesToMatch, + deviceDiscoveredCallbackOSX, this, &portIterator); + if (kernResult != KERN_SUCCESS) { + qDebug() << "IOServiceAddMatchingNotification return:" << kernResult; + return false; + } + + // arm the callback, and grab any devices that are already connected + deviceDiscoveredCallbackOSX(this, portIterator); + + kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOMatchedNotification, cdcClassesToMatch, + deviceDiscoveredCallbackOSX, this, &portIterator); + if (kernResult != KERN_SUCCESS) { + qDebug() << "IOServiceAddMatchingNotification return:" << kernResult; + return false; + } + + // arm the callback, and grab any devices that are already connected + deviceDiscoveredCallbackOSX(this, portIterator); + + kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOTerminatedNotification, classesToMatch, + deviceTerminatedCallbackOSX, this, &portIterator); + if (kernResult != KERN_SUCCESS) { + qDebug() << "IOServiceAddMatchingNotification return:" << kernResult; + return false; + } + + // arm the callback, and clear any devices that are terminated + deviceTerminatedCallbackOSX(this, portIterator); + + kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOTerminatedNotification, cdcClassesToMatch, + deviceTerminatedCallbackOSX, this, &portIterator); + if (kernResult != KERN_SUCCESS) { + qDebug() << "IOServiceAddMatchingNotification return:" << kernResult; + return false; + } + + // arm the callback, and clear any devices that are terminated + deviceTerminatedCallbackOSX(this, portIterator); + return true; +} + diff --git a/extserial/src/qextserialenumerator_p.h b/extserial/src/qextserialenumerator_p.h new file mode 100644 index 0000000..74bbeb2 --- /dev/null +++ b/extserial/src/qextserialenumerator_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** Copyright (c) 2012 Doug Brown +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef _QEXTSERIALENUMERATOR_P_H_ +#define _QEXTSERIALENUMERATOR_P_H_ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QESP API. It exists for the convenience +// of other QESP classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qextserialenumerator.h" + +#ifdef Q_CC_MINGW +// needed for mingw to pull in appropriate dbt business... +// probably a better way to do this +// http://mingw-users.1079350.n2.nabble.com/DEV-BROADCAST-DEVICEINTERFACE-was-not-declared-in-this-scope-td3552762.html +// http://msdn.microsoft.com/en-us/library/6sehtctf.aspx +# ifndef WINVER +# define WINVER 0x0501 +# endif +# ifndef _WIN32_WINNT +# define _WIN32_WINNT WINVER +# endif +#endif + +#ifdef Q_OS_WIN +# include +#endif /*Q_OS_WIN*/ + +#ifdef Q_OS_MAC +# include +#endif /*Q_OS_MAC*/ + +#if defined(Q_OS_LINUX) && !defined(QESP_NO_UDEV) +# include +extern "C" { +# include +} +#endif + +class QextSerialRegistrationWidget; +class QextSerialEnumeratorPrivate +{ + Q_DECLARE_PUBLIC(QextSerialEnumerator) +public: + QextSerialEnumeratorPrivate(QextSerialEnumerator *enumrator); + ~QextSerialEnumeratorPrivate(); + void init_sys(); + void destroy_sys(); + + static QList getPorts_sys(); + bool setUpNotifications_sys(bool setup); + +#if defined(Q_OS_WIN) && defined(QT_GUI_LIB) + LRESULT onDeviceChanged(WPARAM wParam, LPARAM lParam); + bool matchAndDispatchChangedDevice(const QString &deviceID, const GUID &guid, WPARAM wParam); + QextSerialRegistrationWidget *notificationWidget; +#endif /*Q_OS_WIN*/ + +#ifdef Q_OS_MAC + /*! + * Search for serial ports using IOKit. + * \param infoList list with result. + */ + static void iterateServicesOSX(io_object_t service, QList &infoList); + static bool getServiceDetailsOSX(io_object_t service, QextPortInfo *portInfo); + void onDeviceDiscoveredOSX(io_object_t service); + void onDeviceTerminatedOSX(io_object_t service); + friend void deviceDiscoveredCallbackOSX(void *ctxt, io_iterator_t serialPortIterator); + friend void deviceTerminatedCallbackOSX(void *ctxt, io_iterator_t serialPortIterator); + + IONotificationPortRef notificationPortRef; +#endif // Q_OS_MAC + +#if defined(Q_OS_LINUX) && !defined(QESP_NO_UDEV) + QSocketNotifier *notifier; + int notifierFd; + struct udev *udev; + struct udev_monitor *monitor; + + void _q_deviceEvent(); +#endif + +private: + QextSerialEnumerator *q_ptr; +}; + +#endif //_QEXTSERIALENUMERATOR_P_H_ diff --git a/extserial/src/qextserialenumerator_unix.cpp b/extserial/src/qextserialenumerator_unix.cpp new file mode 100644 index 0000000..dfc12a8 --- /dev/null +++ b/extserial/src/qextserialenumerator_unix.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "qextserialenumerator.h" +#include "qextserialenumerator_p.h" +#include + +void QextSerialEnumeratorPrivate::init_sys() +{ +} + +void QextSerialEnumeratorPrivate::destroy_sys() +{ +} + +QList QextSerialEnumeratorPrivate::getPorts_sys() +{ + QList infoList; + QESP_WARNING("Enumeration for POSIX systems (except Linux) is not implemented yet."); + return infoList; +} + +bool QextSerialEnumeratorPrivate::setUpNotifications_sys(bool setup) +{ + Q_UNUSED(setup) + QESP_WARNING("Notifications for *Nix/FreeBSD are not implemented yet"); + return false; +} diff --git a/extserial/src/qextserialenumerator_win.cpp b/extserial/src/qextserialenumerator_win.cpp new file mode 100644 index 0000000..d9ed833 --- /dev/null +++ b/extserial/src/qextserialenumerator_win.cpp @@ -0,0 +1,303 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "qextserialenumerator.h" +#include "qextserialenumerator_p.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef QT_GUI_LIB +/*! + \internal + \class QextSerialRegistrationWidget + + Internal window which is used to receive device arrvial and removal message. +*/ + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +#include +class QextSerialRegistrationWidget : public QWidget +#else +#include +class QextSerialRegistrationWidget : public QWindow +#endif +{ +public: + QextSerialRegistrationWidget(QextSerialEnumeratorPrivate *qese) { + this->qese = qese; + } + ~QextSerialRegistrationWidget() {} + +protected: + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + bool winEvent(MSG *message, long *result) { +#else + bool nativeEvent(const QByteArray & /*eventType*/, void *msg, long *result) { + MSG *message = static_cast(msg); +#endif + if (message->message == WM_DEVICECHANGE) { + qese->onDeviceChanged(message->wParam, message->lParam); + *result = 1; + return true; + } + return false; + } +private: + QextSerialEnumeratorPrivate *qese; +}; +#endif // QT_GUI_LIB + +void QextSerialEnumeratorPrivate::init_sys() +{ +#ifdef QT_GUI_LIB + notificationWidget = 0; +#endif // QT_GUI_LIB +} + +/*! + default +*/ +void QextSerialEnumeratorPrivate::destroy_sys() +{ +#ifdef QT_GUI_LIB + if (notificationWidget) + delete notificationWidget; +#endif +} + +#ifndef GUID_DEVINTERFACE_COMPORT +DEFINE_GUID(GUID_DEVINTERFACE_COMPORT, 0x86e0d1e0L, 0x8089, 0x11d0, 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73); +#endif + + +/*! + \internal + Get value of specified property from the registry. + \a key handle to an open key. + \a property property name. + + return property value. +*/ +static QString getRegKeyValue(HKEY key, LPCTSTR property) +{ + DWORD size = 0; + DWORD type; + if (::RegQueryValueEx(key, property, NULL, NULL, NULL, &size) != ERROR_SUCCESS) + return QString(); + BYTE *buff = new BYTE[size]; + QString result; + if (::RegQueryValueEx(key, property, NULL, &type, buff, &size) == ERROR_SUCCESS) + result = QString::fromUtf16(reinterpret_cast(buff)); + delete [] buff; + return result; +} + +/*! + \internal + Get specific property from registry. + \a devInfoSet pointer to the device information set that contains the interface + and its underlying device. Returned by SetupDiGetClassDevs() function. + \a devInfoData pointer to an SP_DEVINFO_DATA structure that defines the device instance. + this is returned by SetupDiGetDeviceInterfaceDetail() function. + \a property registry property. One of defined SPDRP_* constants. + + return property string. + */ +static QString getDeviceRegistryProperty(HDEVINFO devInfoSet, PSP_DEVINFO_DATA devInfoData, DWORD property) +{ + DWORD buffSize = 0; + ::SetupDiGetDeviceRegistryProperty(devInfoSet, devInfoData, property, NULL, NULL, 0, &buffSize); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return QString(); + BYTE *buff = new BYTE[buffSize]; + ::SetupDiGetDeviceRegistryProperty(devInfoSet, devInfoData, property, NULL, buff, buffSize, NULL); + QString result = QString::fromUtf16(reinterpret_cast(buff)); + delete [] buff; + return result; +} + +/*! + \internal +*/ +static bool getDeviceDetailsInformation(QextPortInfo *portInfo, HDEVINFO devInfoSet, PSP_DEVINFO_DATA devInfoData + , WPARAM wParam = DBT_DEVICEARRIVAL) +{ + portInfo->friendName = getDeviceRegistryProperty(devInfoSet, devInfoData, SPDRP_FRIENDLYNAME); + if (wParam == DBT_DEVICEARRIVAL) + portInfo->physName = getDeviceRegistryProperty(devInfoSet, devInfoData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME); + portInfo->enumName = getDeviceRegistryProperty(devInfoSet, devInfoData, SPDRP_ENUMERATOR_NAME); + + HKEY devKey = ::SetupDiOpenDevRegKey(devInfoSet, devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE); + portInfo->portName = getRegKeyValue(devKey, TEXT("PortName")); + ::RegCloseKey(devKey); + + QString hardwareIDs = getDeviceRegistryProperty(devInfoSet, devInfoData, SPDRP_HARDWAREID); + QRegExp idRx(QLatin1String("VID_(\\w+)&PID_(\\w+)")); + if (hardwareIDs.toUpper().contains(idRx)) { + bool dummy; + portInfo->vendorID = idRx.cap(1).toInt(&dummy, 16); + portInfo->productID = idRx.cap(2).toInt(&dummy, 16); + //qDebug() << "got vid:" << vid << "pid:" << pid; + } + return true; +} + +/*! + \internal +*/ +static void enumerateDevices(const GUID &guid, QList *infoList) +{ + HDEVINFO devInfoSet = ::SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (devInfoSet != INVALID_HANDLE_VALUE) { + SP_DEVINFO_DATA devInfoData; + devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + for(int i = 0; ::SetupDiEnumDeviceInfo(devInfoSet, i, &devInfoData); i++) { + QextPortInfo info; + info.productID = info.vendorID = 0; + getDeviceDetailsInformation(&info, devInfoSet, &devInfoData); + infoList->append(info); + } + ::SetupDiDestroyDeviceInfoList(devInfoSet); + } +} + + +static bool lessThan(const QextPortInfo &s1, const QextPortInfo &s2) +{ + if (s1.portName.startsWith(QLatin1String("COM")) + && s2.portName.startsWith(QLatin1String("COM"))) { + return s1.portName.mid(3).toInt() QextSerialEnumeratorPrivate::getPorts_sys() +{ + QList ports; + enumerateDevices(GUID_DEVINTERFACE_COMPORT, &ports); + std::sort(ports.begin(), ports.end(), lessThan); + return ports; +} + + +/* + Enable event-driven notifications of board discovery/removal. +*/ +bool QextSerialEnumeratorPrivate::setUpNotifications_sys(bool setup) +{ +#ifndef QT_GUI_LIB + Q_UNUSED(setup) + QESP_WARNING("QextSerialEnumerator: GUI not enabled - can't register for device notifications."); + return false; +#else + Q_Q(QextSerialEnumerator); + if (setup && notificationWidget) //already setup + return true; + notificationWidget = new QextSerialRegistrationWidget(this); + + DEV_BROADCAST_DEVICEINTERFACE dbh; + ::ZeroMemory(&dbh, sizeof(dbh)); + dbh.dbcc_size = sizeof(dbh); + dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + // dbh.dbcc_classguid = GUID_DEVCLASS_PORTS; //Ignored in such case + DWORD flags = DEVICE_NOTIFY_WINDOW_HANDLE|DEVICE_NOTIFY_ALL_INTERFACE_CLASSES; + if (::RegisterDeviceNotification((HWND)notificationWidget->winId(), &dbh, flags) == NULL) { + QESP_WARNING() << "RegisterDeviceNotification failed:" << GetLastError(); + return false; + } + // setting up notifications doesn't tell us about devices already connected + // so get those manually + foreach (QextPortInfo port, getPorts_sys()) + Q_EMIT q->deviceDiscovered(port); + return true; +#endif // QT_GUI_LIB +} + +#ifdef QT_GUI_LIB +LRESULT QextSerialEnumeratorPrivate::onDeviceChanged(WPARAM wParam, LPARAM lParam) +{ + if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam) { + PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam; + if (pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; + // delimiters are different across APIs...change to backslash. ugh. + QString deviceID = QString::fromUtf16(reinterpret_cast(pDevInf->dbcc_name)); + deviceID = deviceID.toUpper().replace(QLatin1String("#"), QLatin1String("\\")); + + matchAndDispatchChangedDevice(deviceID, GUID_DEVINTERFACE_COMPORT, wParam); + } + } + return 0; +} + +bool QextSerialEnumeratorPrivate::matchAndDispatchChangedDevice(const QString &deviceID, const GUID &guid, WPARAM wParam) +{ + Q_Q(QextSerialEnumerator); + bool rv = false; + DWORD dwFlag = (DBT_DEVICEARRIVAL == wParam) ? DIGCF_PRESENT : DIGCF_PROFILE; + HDEVINFO devInfoSet = SetupDiGetClassDevs(&guid, NULL, NULL, dwFlag | DIGCF_DEVICEINTERFACE); + if (devInfoSet != INVALID_HANDLE_VALUE) { + SP_DEVINFO_DATA spDevInfoData; + spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + for(int i=0; SetupDiEnumDeviceInfo(devInfoSet, i, &spDevInfoData); i++) { + DWORD nSize = 0; + TCHAR buf[MAX_PATH]; + if (SetupDiGetDeviceInstanceId(devInfoSet, &spDevInfoData, buf, MAX_PATH, &nSize) + && deviceID.contains(QString::fromUtf16(reinterpret_cast(buf)))) { // we found a match + rv = true; + QextPortInfo info; + info.productID = info.vendorID = 0; + getDeviceDetailsInformation(&info, devInfoSet, &spDevInfoData, wParam); + if (wParam == DBT_DEVICEARRIVAL) + Q_EMIT q->deviceDiscovered(info); + else if (wParam == DBT_DEVICEREMOVECOMPLETE) + Q_EMIT q->deviceRemoved(info); + break; + } + } + SetupDiDestroyDeviceInfoList(devInfoSet); + } + return rv; +} +#endif //QT_GUI_LIB diff --git a/extserial/src/qextserialport.cpp b/extserial/src/qextserialport.cpp new file mode 100644 index 0000000..d1089e2 --- /dev/null +++ b/extserial/src/qextserialport.cpp @@ -0,0 +1,1005 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "qextserialport.h" +#include "qextserialport_p.h" +#include +#include +#include +#include + +/*! + \class PortSettings + + \brief The PortSettings class contain port settings + + Structure to contain port settings. + + \code + BaudRateType BaudRate; + DataBitsType DataBits; + ParityType Parity; + StopBitsType StopBits; + FlowType FlowControl; + long Timeout_Millisec; + \endcode +*/ + +QextSerialPortPrivate::QextSerialPortPrivate(QextSerialPort *q) + :lock(QReadWriteLock::Recursive), q_ptr(q) +{ + lastErr = E_NO_ERROR; + settings.BaudRate = BAUD9600; + settings.Parity = PAR_NONE; + settings.FlowControl = FLOW_OFF; + settings.DataBits = DATA_8; + settings.StopBits = STOP_1; + settings.Timeout_Millisec = 10; + settingsDirtyFlags = DFE_ALL; + + platformSpecificInit(); +} + +QextSerialPortPrivate::~QextSerialPortPrivate() +{ + platformSpecificDestruct(); +} + +void QextSerialPortPrivate::setBaudRate(BaudRateType baudRate, bool update) +{ + switch (baudRate) { +#ifdef Q_OS_WIN + //Windows Special + case BAUD14400: + case BAUD56000: + case BAUD128000: + case BAUD256000: + QESP_PORTABILITY_WARNING()<<"QextSerialPort Portability Warning: POSIX does not support baudRate:"<isOpen()) + updatePortSettings(); + break; +#if !(defined(Q_OS_WIN) || defined(Q_OS_MAC)) + default: + QESP_WARNING()<<"QextSerialPort does not support baudRate:"<isOpen()) + updatePortSettings(); +} + +void QextSerialPortPrivate::setDataBits(DataBitsType dataBits, bool update) +{ + switch(dataBits) { + + case DATA_5: + if (settings.StopBits == STOP_2) { + QESP_WARNING("QextSerialPort: 5 Data bits cannot be used with 2 stop bits."); + } else { + settings.DataBits = dataBits; + settingsDirtyFlags |= DFE_DataBits; + } + break; + + case DATA_6: +#ifdef Q_OS_WIN + if (settings.StopBits == STOP_1_5) { + QESP_WARNING("QextSerialPort: 6 Data bits cannot be used with 1.5 stop bits."); + } + else +#endif + { + settings.DataBits = dataBits; + settingsDirtyFlags |= DFE_DataBits; + } + break; + + case DATA_7: +#ifdef Q_OS_WIN + if (settings.StopBits == STOP_1_5) { + QESP_WARNING("QextSerialPort: 7 Data bits cannot be used with 1.5 stop bits."); + } + else +#endif + { + settings.DataBits = dataBits; + settingsDirtyFlags |= DFE_DataBits; + } + break; + + case DATA_8: +#ifdef Q_OS_WIN + if (settings.StopBits == STOP_1_5) { + QESP_WARNING("QextSerialPort: 8 Data bits cannot be used with 1.5 stop bits."); + } + else +#endif + { + settings.DataBits = dataBits; + settingsDirtyFlags |= DFE_DataBits; + } + break; + default: + QESP_WARNING()<<"QextSerialPort does not support Data bits:"<isOpen()) + updatePortSettings(); +} + +void QextSerialPortPrivate::setStopBits(StopBitsType stopBits, bool update) +{ + switch (stopBits) { + + /*one stop bit*/ + case STOP_1: + settings.StopBits = stopBits; + settingsDirtyFlags |= DFE_StopBits; + break; + +#ifdef Q_OS_WIN + /*1.5 stop bits*/ + case STOP_1_5: + QESP_PORTABILITY_WARNING("QextSerialPort Portability Warning: 1.5 stop bit operation is not supported by POSIX."); + if (settings.DataBits != DATA_5) { + QESP_WARNING("QextSerialPort: 1.5 stop bits can only be used with 5 data bits"); + } else { + settings.StopBits = stopBits; + settingsDirtyFlags |= DFE_StopBits; + } + break; +#endif + + /*two stop bits*/ + case STOP_2: + if (settings.DataBits == DATA_5) { + QESP_WARNING("QextSerialPort: 2 stop bits cannot be used with 5 data bits"); + } else { + settings.StopBits = stopBits; + settingsDirtyFlags |= DFE_StopBits; + } + break; + default: + QESP_WARNING()<<"QextSerialPort does not support stop bits: "<isOpen()) + updatePortSettings(); +} + +void QextSerialPortPrivate::setFlowControl(FlowType flow, bool update) +{ + settings.FlowControl = flow; + settingsDirtyFlags |= DFE_Flow; + if (update && q_func()->isOpen()) + updatePortSettings(); +} + +void QextSerialPortPrivate::setTimeout(long millisec, bool update) +{ + settings.Timeout_Millisec = millisec; + settingsDirtyFlags |= DFE_TimeOut; + if (update && q_func()->isOpen()) + updatePortSettings(); +} + +void QextSerialPortPrivate::setPortSettings(const PortSettings &settings, bool update) +{ + setBaudRate(settings.BaudRate, false); + setDataBits(settings.DataBits, false); + setStopBits(settings.StopBits, false); + setParity(settings.Parity, false); + setFlowControl(settings.FlowControl, false); + setTimeout(settings.Timeout_Millisec, false); + settingsDirtyFlags = DFE_ALL; + if (update && q_func()->isOpen()) + updatePortSettings(); +} + + +void QextSerialPortPrivate::_q_canRead() +{ + qint64 maxSize = bytesAvailable_sys(); + if (maxSize > 0) { + char *writePtr = readBuffer.reserve(size_t(maxSize)); + qint64 bytesRead = readData_sys(writePtr, maxSize); + if (bytesRead < maxSize) + readBuffer.chop(maxSize - bytesRead); + Q_Q(QextSerialPort); + Q_EMIT q->readyRead(); + } +} + +/*! \class QextSerialPort + + \brief The QextSerialPort class encapsulates a serial port on both POSIX and Windows systems. + + \section1 Usage + QextSerialPort offers both a polling and event driven API. Event driven + is typically easier to use, since you never have to worry about checking + for new data. + + \bold Example + \code + QextSerialPort *port = new QextSerialPort("COM1"); + connect(port, SIGNAL(readyRead()), myClass, SLOT(onDataAvailable())); + port->open(); + + void MyClass::onDataAvailable() + { + QByteArray data = port->readAll(); + processNewData(usbdata); + } + \endcode + + \section1 Compatibility + The user will be notified of errors and possible portability conflicts at run-time + by default. + + For example, if a application has used BAUD1800, when it is runing under unix, you + will get following message. + + \code + QextSerialPort Portability Warning: Windows does not support baudRate:1800 + \endcode + + This behavior can be turned off by defining macro QESP_NO_WARN (to turn off all warnings) + or QESP_NO_PORTABILITY_WARN (to turn off portability warnings) in the project. + + + \bold Author: Stefan Sander, Michal Policht, Brandon Fosdick, Liam Staskawicz, Debao Zhang +*/ + +/*! + \enum QextSerialPort::QueryMode + + This enum type specifies query mode used in a serial port: + + \value Polling + asynchronously read and write + \value EventDriven + synchronously read and write +*/ + +/*! + \fn void QextSerialPort::dsrChanged(bool status) + This signal is emitted whenever dsr line has changed its state. You may + use this signal to check if device is connected. + + \a status true when DSR signal is on, false otherwise. + */ + + +/*! + \fn QueryMode QextSerialPort::queryMode() const + Get query mode. + */ + +/*! + Default constructor. Note that the name of the device used by a QextSerialPort is dependent on + your OS. Possible naming conventions and their associated OS are: + + \code + + OS Constant Used By Naming Convention + ------------- ------------- ------------------------ + Q_OS_WIN Windows COM1, COM2 + Q_OS_IRIX SGI/IRIX /dev/ttyf1, /dev/ttyf2 + Q_OS_HPUX HP-UX /dev/tty1p0, /dev/tty2p0 + Q_OS_SOLARIS SunOS/Slaris /dev/ttya, /dev/ttyb + Q_OS_OSF Digital UNIX /dev/tty01, /dev/tty02 + Q_OS_FREEBSD FreeBSD /dev/ttyd0, /dev/ttyd1 + Q_OS_OPENBSD OpenBSD /dev/tty00, /dev/tty01 + Q_OS_LINUX Linux /dev/ttyS0, /dev/ttyS1 + /dev/ttyS0, /dev/ttyS1 + \endcode + + This constructor assigns the device name to the name of the first port on the specified system. + See the other constructors if you need to open a different port. Default \a mode is EventDriven. + As a subclass of QObject, \a parent can be specified. +*/ + +QextSerialPort::QextSerialPort(QextSerialPort::QueryMode mode, QObject *parent) + : QIODevice(parent), d_ptr(new QextSerialPortPrivate(this)) +{ +#ifdef Q_OS_WIN + setPortName(QLatin1String("COM1")); + +#elif defined(Q_OS_IRIX) + setPortName(QLatin1String("/dev/ttyf1")); + +#elif defined(Q_OS_HPUX) + setPortName(QLatin1String("/dev/tty1p0")); + +#elif defined(Q_OS_SOLARIS) + setPortName(QLatin1String("/dev/ttya")); + +#elif defined(Q_OS_OSF) //formally DIGITAL UNIX + setPortName(QLatin1String("/dev/tty01")); + +#elif defined(Q_OS_FREEBSD) + setPortName(QLatin1String("/dev/ttyd1")); + +#elif defined(Q_OS_OPENBSD) + setPortName(QLatin1String("/dev/tty00")); + +#else + setPortName(QLatin1String("/dev/ttyS0")); +#endif + setQueryMode(mode); +} + +/*! + Constructs a serial port attached to the port specified by name. + \a name is the name of the device, which is windowsystem-specific, + e.g."COM1" or "/dev/ttyS0". \a mode +*/ +QextSerialPort::QextSerialPort(const QString &name, QextSerialPort::QueryMode mode, QObject *parent) + : QIODevice(parent), d_ptr(new QextSerialPortPrivate(this)) +{ + setQueryMode(mode); + setPortName(name); +} + +/*! + Constructs a port with default name and specified \a settings. +*/ +QextSerialPort::QextSerialPort(const PortSettings &settings, QextSerialPort::QueryMode mode, QObject *parent) + : QIODevice(parent), d_ptr(new QextSerialPortPrivate(this)) +{ + Q_D(QextSerialPort); + setQueryMode(mode); + d->setPortSettings(settings); +} + +/*! + Constructs a port with specified \a name , \a mode and \a settings. +*/ +QextSerialPort::QextSerialPort(const QString &name, const PortSettings &settings, QextSerialPort::QueryMode mode, QObject *parent) + : QIODevice(parent), d_ptr(new QextSerialPortPrivate(this)) +{ + Q_D(QextSerialPort); + setPortName(name); + setQueryMode(mode); + d->setPortSettings(settings); +} + +/*! + Opens a serial port and sets its OpenMode to \a mode. + Note that this function does not specify which device to open. + Returns true if successful; otherwise returns false.This function has no effect + if the port associated with the class is already open. The port is also + configured to the current settings, as stored in the settings structure. +*/ +bool QextSerialPort::open(OpenMode mode) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (mode != QIODevice::NotOpen && !isOpen()) + d->open_sys(mode); + + return isOpen(); +} + + +/*! \reimp + Closes a serial port. This function has no effect if the serial port associated with the class + is not currently open. +*/ +void QextSerialPort::close() +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (isOpen()) { + // Be a good QIODevice and call QIODevice::close() before really close() + // so the aboutToClose() signal is emitted at the proper time + QIODevice::close(); // mark ourselves as closed + d->close_sys(); + d->readBuffer.clear(); + } +} + +/*! + Flushes all pending I/O to the serial port. This function has no effect if the serial port + associated with the class is not currently open. +*/ +void QextSerialPort::flush() +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (isOpen()) + d->flush_sys(); +} + +/*! \reimp + Returns the number of bytes waiting in the port's receive queue. This function will return 0 if + the port is not currently open, or -1 on error. +*/ +qint64 QextSerialPort::bytesAvailable() const +{ + QWriteLocker locker(&d_func()->lock); + if (isOpen()) { + qint64 bytes = d_func()->bytesAvailable_sys(); + if (bytes != -1) { + return bytes + d_func()->readBuffer.size() + + QIODevice::bytesAvailable(); + } + return -1; + } + return 0; +} + +/*! \reimp + +*/ +bool QextSerialPort::canReadLine() const +{ + QReadLocker locker(&d_func()->lock); + return QIODevice::canReadLine() || d_func()->readBuffer.canReadLine(); +} + +/*! + * Set desired serial communication handling style. You may choose from polling + * or event driven approach. This function does nothing when port is open; to + * apply changes port must be reopened. + * + * In event driven approach read() and write() functions are acting + * asynchronously. They return immediately and the operation is performed in + * the background, so they doesn't freeze the calling thread. + * To determine when operation is finished, QextSerialPort runs separate thread + * and monitors serial port events. Whenever the event occurs, adequate signal + * is emitted. + * + * When polling is set, read() and write() are acting synchronously. Signals are + * not working in this mode and some functions may not be available. The advantage + * of polling is that it generates less overhead due to lack of signals emissions + * and it doesn't start separate thread to monitor events. + * + * Generally event driven approach is more capable and friendly, although some + * applications may need as low overhead as possible and then polling comes. + * + * \a mode query mode. + */ +void QextSerialPort::setQueryMode(QueryMode mode) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (mode != d->queryMode) + d->queryMode = mode; +} + +/*! + Sets the \a name of the device associated with the object, e.g. "COM1", or "/dev/ttyS0". +*/ +void QextSerialPort::setPortName(const QString &name) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + d->port = name; +} + +/*! + Returns the name set by setPortName(). +*/ +QString QextSerialPort::portName() const +{ + QReadLocker locker(&d_func()->lock); + return d_func()->port; +} + +QextSerialPort::QueryMode QextSerialPort::queryMode() const +{ + QReadLocker locker(&d_func()->lock); + return d_func()->queryMode; +} + +/*! + Reads all available data from the device, and returns it as a QByteArray. + This function has no way of reporting errors; returning an empty QByteArray() + can mean either that no data was currently available for reading, or that an error occurred. +*/ +QByteArray QextSerialPort::readAll() +{ + int avail = this->bytesAvailable(); + return (avail > 0) ? this->read(avail) : QByteArray(); +} + +/*! + Returns the baud rate of the serial port. For a list of possible return values see + the definition of the enum BaudRateType. +*/ +BaudRateType QextSerialPort::baudRate() const +{ + QReadLocker locker(&d_func()->lock); + return d_func()->settings.BaudRate; +} + +/*! + Returns the number of data bits used by the port. For a list of possible values returned by + this function, see the definition of the enum DataBitsType. +*/ +DataBitsType QextSerialPort::dataBits() const +{ + QReadLocker locker(&d_func()->lock); + return d_func()->settings.DataBits; +} + +/*! + Returns the type of parity used by the port. For a list of possible values returned by + this function, see the definition of the enum ParityType. +*/ +ParityType QextSerialPort::parity() const +{ + QReadLocker locker(&d_func()->lock); + return d_func()->settings.Parity; +} + +/*! + Returns the number of stop bits used by the port. For a list of possible return values, see + the definition of the enum StopBitsType. +*/ +StopBitsType QextSerialPort::stopBits() const +{ + QReadLocker locker(&d_func()->lock); + return d_func()->settings.StopBits; +} + +/*! + Returns the type of flow control used by the port. For a list of possible values returned + by this function, see the definition of the enum FlowType. +*/ +FlowType QextSerialPort::flowControl() const +{ + QReadLocker locker(&d_func()->lock); + return d_func()->settings.FlowControl; +} + +/*! + \reimp + Returns true if device is sequential, otherwise returns false. Serial port is sequential device + so this function always returns true. Check QIODevice::isSequential() documentation for more + information. +*/ +bool QextSerialPort::isSequential() const +{ + return true; +} + +/*! + Return the error number, or 0 if no error occurred. +*/ +ulong QextSerialPort::lastError() const +{ + QReadLocker locker(&d_func()->lock); + return d_func()->lastErr; +} + +/*! + Returns the line status as stored by the port function. This function will retrieve the states + of the following lines: DCD, CTS, DSR, and RI. On POSIX systems, the following additional lines + can be monitored: DTR, RTS, Secondary TXD, and Secondary RXD. The value returned is an unsigned + long with specific bits indicating which lines are high. The following constants should be used + to examine the states of individual lines: + + \code + Mask Line + ------ ---- + LS_CTS CTS + LS_DSR DSR + LS_DCD DCD + LS_RI RI + LS_RTS RTS (POSIX only) + LS_DTR DTR (POSIX only) + LS_ST Secondary TXD (POSIX only) + LS_SR Secondary RXD (POSIX only) + \endcode + + This function will return 0 if the port associated with the class is not currently open. +*/ +unsigned long QextSerialPort::lineStatus() +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (isOpen()) + return d->lineStatus_sys(); + return 0; +} + +/*! + Returns a human-readable description of the last device error that occurred. +*/ +QString QextSerialPort::errorString() +{ + Q_D(QextSerialPort); + QReadLocker locker(&d->lock); + switch(d->lastErr) { + case E_NO_ERROR: + return tr("No Error has occurred"); + case E_INVALID_FD: + return tr("Invalid file descriptor (port was not opened correctly)"); + case E_NO_MEMORY: + return tr("Unable to allocate memory tables (POSIX)"); + case E_CAUGHT_NON_BLOCKED_SIGNAL: + return tr("Caught a non-blocked signal (POSIX)"); + case E_PORT_TIMEOUT: + return tr("Operation timed out (POSIX)"); + case E_INVALID_DEVICE: + return tr("The file opened by the port is not a valid device"); + case E_BREAK_CONDITION: + return tr("The port detected a break condition"); + case E_FRAMING_ERROR: + return tr("The port detected a framing error (usually caused by incorrect baud rate settings)"); + case E_IO_ERROR: + return tr("There was an I/O error while communicating with the port"); + case E_BUFFER_OVERRUN: + return tr("Character buffer overrun"); + case E_RECEIVE_OVERFLOW: + return tr("Receive buffer overflow"); + case E_RECEIVE_PARITY_ERROR: + return tr("The port detected a parity error in the received data"); + case E_TRANSMIT_OVERFLOW: + return tr("Transmit buffer overflow"); + case E_READ_FAILED: + return tr("General read operation failure"); + case E_WRITE_FAILED: + return tr("General write operation failure"); + case E_FILE_NOT_FOUND: + return tr("The %1 file doesn't exists").arg(this->portName()); + case E_PERMISSION_DENIED: + return tr("Permission denied"); + case E_AGAIN: + return tr("Device is already locked"); + default: + return tr("Unknown error: %1").arg(d->lastErr); + } +} + +/*! + Destructs the QextSerialPort object. +*/ +QextSerialPort::~QextSerialPort() +{ + if (isOpen()) + close(); + + delete d_ptr; +} + +/*! + Sets the flow control used by the port to \a flow. Possible values of flow are: + \code + FLOW_OFF No flow control + FLOW_HARDWARE Hardware (RTS/CTS) flow control + FLOW_XONXOFF Software (XON/XOFF) flow control + \endcode +*/ +void QextSerialPort::setFlowControl(FlowType flow) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (d->settings.FlowControl != flow) + d->setFlowControl(flow, true); +} + +/*! + Sets the parity associated with the serial port to \a parity. The possible values of parity are: + \code + PAR_SPACE Space Parity + PAR_MARK Mark Parity + PAR_NONE No Parity + PAR_EVEN Even Parity + PAR_ODD Odd Parity + \endcode +*/ +void QextSerialPort::setParity(ParityType parity) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (d->settings.Parity != parity) + d->setParity(parity, true); +} + +/*! + Sets the number of data bits used by the serial port to \a dataBits. Possible values of dataBits are: + \code + DATA_5 5 data bits + DATA_6 6 data bits + DATA_7 7 data bits + DATA_8 8 data bits + \endcode + + \bold note: + This function is subject to the following restrictions: + \list + \o 5 data bits cannot be used with 2 stop bits. + \o 1.5 stop bits can only be used with 5 data bits. + \o 8 data bits cannot be used with space parity on POSIX systems. + \endlist + */ +void QextSerialPort::setDataBits(DataBitsType dataBits) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (d->settings.DataBits != dataBits) + d->setDataBits(dataBits, true); +} + +/*! + Sets the number of stop bits used by the serial port to \a stopBits. Possible values of stopBits are: + \code + STOP_1 1 stop bit + STOP_1_5 1.5 stop bits + STOP_2 2 stop bits + \endcode + + \bold note: + This function is subject to the following restrictions: + \list + \o 2 stop bits cannot be used with 5 data bits. + \o 1.5 stop bits cannot be used with 6 or more data bits. + \o POSIX does not support 1.5 stop bits. + \endlist +*/ +void QextSerialPort::setStopBits(StopBitsType stopBits) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (d->settings.StopBits != stopBits) + d->setStopBits(stopBits, true); +} + +/*! + Sets the baud rate of the serial port to \a baudRate. Note that not all rates are applicable on + all platforms. The following table shows translations of the various baud rate + constants on Windows(including NT/2000) and POSIX platforms. Speeds marked with an * + are speeds that are usable on both Windows and POSIX. + \code + + RATE Windows Speed POSIX Speed + ----------- ------------- ----------- + BAUD50 X 50 + BAUD75 X 75 + *BAUD110 110 110 + BAUD134 X 134.5 + BAUD150 X 150 + BAUD200 X 200 + *BAUD300 300 300 + *BAUD600 600 600 + *BAUD1200 1200 1200 + BAUD1800 X 1800 + *BAUD2400 2400 2400 + *BAUD4800 4800 4800 + *BAUD9600 9600 9600 + BAUD14400 14400 X + *BAUD19200 19200 19200 + *BAUD38400 38400 38400 + BAUD56000 56000 X + *BAUD57600 57600 57600 + BAUD76800 X 76800 + *BAUD115200 115200 115200 + BAUD128000 128000 X + BAUD230400 X 230400 + BAUD256000 256000 X + BAUD460800 X 460800 + BAUD500000 X 500000 + BAUD576000 X 576000 + BAUD921600 X 921600 + BAUD1000000 X 1000000 + BAUD1152000 X 1152000 + BAUD1500000 X 1500000 + BAUD2000000 X 2000000 + BAUD2500000 X 2500000 + BAUD3000000 X 3000000 + BAUD3500000 X 3500000 + BAUD4000000 X 4000000 + \endcode +*/ + +void QextSerialPort::setBaudRate(BaudRateType baudRate) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (d->settings.BaudRate != baudRate) + d->setBaudRate(baudRate, true); +} + +/*! + For Unix: + + Sets the read and write timeouts for the port to \a millisec milliseconds. + Note that this is a per-character timeout, i.e. the port will wait this long for each + individual character, not for the whole read operation. This timeout also applies to the + bytesWaiting() function. + + \bold note: + POSIX does not support millisecond-level control for I/O timeout values. Any + timeout set using this function will be set to the next lowest tenth of a second for + the purposes of detecting read or write timeouts. For example a timeout of 550 milliseconds + will be seen by the class as a timeout of 500 milliseconds for the purposes of reading and + writing the port. However millisecond-level control is allowed by the select() system call, + so for example a 550-millisecond timeout will be seen as 550 milliseconds on POSIX systems for + the purpose of detecting available bytes in the read buffer. + + For Windows: + + Sets the read and write timeouts for the port to \a millisec milliseconds. + Setting 0 indicates that timeouts are not used for read nor write operations; + however read() and write() functions will still block. Set -1 to provide + non-blocking behaviour (read() and write() will return immediately). + + \bold note: this function does nothing in event driven mode. +*/ +void QextSerialPort::setTimeout(long millisec) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (d->settings.Timeout_Millisec != millisec) + d->setTimeout(millisec, true); +} + +/*! + Sets DTR line to the requested state (\a set default to high). This function will have no effect if + the port associated with the class is not currently open. +*/ +void QextSerialPort::setDtr(bool set) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (isOpen()) + d->setDtr_sys(set); +} + +/*! + Sets RTS line to the requested state \a set (high by default). + This function will have no effect if + the port associated with the class is not currently open. +*/ +void QextSerialPort::setRts(bool set) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + if (isOpen()) + d->setRts_sys(set); +} + +/*! \reimp + Reads a block of data from the serial port. This function will read at most maxlen bytes from + the serial port and place them in the buffer pointed to by data. Return value is the number of + bytes actually read, or -1 on error. + + \warning before calling this function ensure that serial port associated with this class + is currently open (use isOpen() function to check if port is open). +*/ +qint64 QextSerialPort::readData(char *data, qint64 maxSize) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + qint64 bytesFromBuffer = 0; + if (!d->readBuffer.isEmpty()) { + bytesFromBuffer = d->readBuffer.read(data, maxSize); + if (bytesFromBuffer == maxSize) + return bytesFromBuffer; + } + qint64 bytesFromDevice = d->readData_sys(data+bytesFromBuffer, maxSize-bytesFromBuffer); + if (bytesFromDevice < 0) + return -1; + return bytesFromBuffer + bytesFromDevice; +} + +/*! \reimp + Writes a block of data to the serial port. This function will write len bytes + from the buffer pointed to by data to the serial port. Return value is the number + of bytes actually written, or -1 on error. + + \warning before calling this function ensure that serial port associated with this class + is currently open (use isOpen() function to check if port is open). +*/ +qint64 QextSerialPort::writeData(const char *data, qint64 maxSize) +{ + Q_D(QextSerialPort); + QWriteLocker locker(&d->lock); + return d->writeData_sys(data, maxSize); +} + +#include "moc_qextserialport.cpp" diff --git a/extserial/src/qextserialport.h b/extserial/src/qextserialport.h new file mode 100644 index 0000000..a0bc822 --- /dev/null +++ b/extserial/src/qextserialport.h @@ -0,0 +1,240 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef _QEXTSERIALPORT_H_ +#define _QEXTSERIALPORT_H_ + +#include +#include "qextserialport_global.h" +#ifdef Q_OS_UNIX +#include +#endif +/*line status constants*/ +// ### QESP2.0 move to enum +#define LS_CTS 0x01 +#define LS_DSR 0x02 +#define LS_DCD 0x04 +#define LS_RI 0x08 +#define LS_RTS 0x10 +#define LS_DTR 0x20 +#define LS_ST 0x40 +#define LS_SR 0x80 + +/*error constants*/ +// ### QESP2.0 move to enum +#define E_NO_ERROR 0 +#define E_INVALID_FD 1 +#define E_NO_MEMORY 2 +#define E_CAUGHT_NON_BLOCKED_SIGNAL 3 +#define E_PORT_TIMEOUT 4 +#define E_INVALID_DEVICE 5 +#define E_BREAK_CONDITION 6 +#define E_FRAMING_ERROR 7 +#define E_IO_ERROR 8 +#define E_BUFFER_OVERRUN 9 +#define E_RECEIVE_OVERFLOW 10 +#define E_RECEIVE_PARITY_ERROR 11 +#define E_TRANSMIT_OVERFLOW 12 +#define E_READ_FAILED 13 +#define E_WRITE_FAILED 14 +#define E_FILE_NOT_FOUND 15 +#define E_PERMISSION_DENIED 16 +#define E_AGAIN 17 + +enum BaudRateType +{ +#if defined(Q_OS_UNIX) || defined(qdoc) + BAUD50 = 50, //POSIX ONLY + BAUD75 = 75, //POSIX ONLY + BAUD134 = 134, //POSIX ONLY + BAUD150 = 150, //POSIX ONLY + BAUD200 = 200, //POSIX ONLY + BAUD1800 = 1800, //POSIX ONLY +# if defined(B76800) || defined(qdoc) + BAUD76800 = 76800, //POSIX ONLY +# endif +# if (defined(B230400) && defined(B4000000)) || defined(qdoc) + BAUD230400 = 230400, //POSIX ONLY + BAUD460800 = 460800, //POSIX ONLY + BAUD500000 = 500000, //POSIX ONLY + BAUD576000 = 576000, //POSIX ONLY + BAUD921600 = 921600, //POSIX ONLY + BAUD1000000 = 1000000, //POSIX ONLY + BAUD1152000 = 1152000, //POSIX ONLY + BAUD1500000 = 1500000, //POSIX ONLY + BAUD2000000 = 2000000, //POSIX ONLY + BAUD2500000 = 2500000, //POSIX ONLY + BAUD3000000 = 3000000, //POSIX ONLY + BAUD3500000 = 3500000, //POSIX ONLY + BAUD4000000 = 4000000, //POSIX ONLY +# endif +#endif //Q_OS_UNIX +#if defined(Q_OS_WIN) || defined(qdoc) + BAUD14400 = 14400, //WINDOWS ONLY + BAUD56000 = 56000, //WINDOWS ONLY + BAUD128000 = 128000, //WINDOWS ONLY + BAUD256000 = 256000, //WINDOWS ONLY +#endif //Q_OS_WIN + BAUD110 = 110, + BAUD300 = 300, + BAUD600 = 600, + BAUD1200 = 1200, + BAUD2400 = 2400, + BAUD4800 = 4800, + BAUD9600 = 9600, + BAUD19200 = 19200, + BAUD38400 = 38400, + BAUD57600 = 57600, + BAUD115200 = 115200 +}; + +enum DataBitsType +{ + DATA_5 = 5, + DATA_6 = 6, + DATA_7 = 7, + DATA_8 = 8 +}; + +enum ParityType +{ + PAR_NONE, + PAR_ODD, + PAR_EVEN, +#if defined(Q_OS_WIN) || defined(qdoc) + PAR_MARK, //WINDOWS ONLY +#endif + PAR_SPACE +}; + +enum StopBitsType +{ + STOP_1, +#if defined(Q_OS_WIN) || defined(qdoc) + STOP_1_5, //WINDOWS ONLY +#endif + STOP_2 +}; + +enum FlowType +{ + FLOW_OFF, + FLOW_HARDWARE, + FLOW_XONXOFF +}; + +/** + * structure to contain port settings + */ +struct PortSettings +{ + BaudRateType BaudRate; + DataBitsType DataBits; + ParityType Parity; + StopBitsType StopBits; + FlowType FlowControl; + long Timeout_Millisec; +}; + +class QextSerialPortPrivate; +class QEXTSERIALPORT_EXPORT QextSerialPort: public QIODevice +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QextSerialPort) + Q_ENUMS(QueryMode) + Q_PROPERTY(QString portName READ portName WRITE setPortName) + Q_PROPERTY(QueryMode queryMode READ queryMode WRITE setQueryMode) +public: + enum QueryMode { + Polling, + EventDriven + }; + + explicit QextSerialPort(QueryMode mode = EventDriven, QObject *parent = 0); + explicit QextSerialPort(const QString &name, QueryMode mode = EventDriven, QObject *parent = 0); + explicit QextSerialPort(const PortSettings &s, QueryMode mode = EventDriven, QObject *parent = 0); + QextSerialPort(const QString &name, const PortSettings &s, QueryMode mode = EventDriven, QObject *parent=0); + + ~QextSerialPort(); + + QString portName() const; + QueryMode queryMode() const; + BaudRateType baudRate() const; + DataBitsType dataBits() const; + ParityType parity() const; + StopBitsType stopBits() const; + FlowType flowControl() const; + + bool open(OpenMode mode); + bool isSequential() const; + void close(); + void flush(); + qint64 bytesAvailable() const; + bool canReadLine() const; + QByteArray readAll(); + + ulong lastError() const; + + ulong lineStatus(); + QString errorString(); + +public Q_SLOTS: + void setPortName(const QString &name); + void setQueryMode(QueryMode mode); + void setBaudRate(BaudRateType); + void setDataBits(DataBitsType); + void setParity(ParityType); + void setStopBits(StopBitsType); + void setFlowControl(FlowType); + void setTimeout(long); + + void setDtr(bool set=true); + void setRts(bool set=true); + +Q_SIGNALS: + void dsrChanged(bool status); + +protected: + qint64 readData(char *data, qint64 maxSize); + qint64 writeData(const char *data, qint64 maxSize); + +private: + Q_DISABLE_COPY(QextSerialPort) + +#ifdef Q_OS_WIN + Q_PRIVATE_SLOT(d_func(), void _q_onWinEvent(HANDLE)) +#endif + Q_PRIVATE_SLOT(d_func(), void _q_canRead()) + + QextSerialPortPrivate * const d_ptr; +}; + +#endif diff --git a/extserial/src/qextserialport.pri b/extserial/src/qextserialport.pri new file mode 100644 index 0000000..27b05ef --- /dev/null +++ b/extserial/src/qextserialport.pri @@ -0,0 +1,36 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +PUBLIC_HEADERS += $$PWD/qextserialport.h \ + $$PWD/qextserialenumerator.h \ + $$PWD/qextserialport_global.h + +HEADERS += $$PUBLIC_HEADERS \ + $$PWD/qextserialport_p.h \ + $$PWD/qextserialenumerator_p.h \ + +SOURCES += $$PWD/qextserialport.cpp \ + $$PWD/qextserialenumerator.cpp +unix { + SOURCES += $$PWD/qextserialport_unix.cpp + linux* { + SOURCES += $$PWD/qextserialenumerator_linux.cpp + } else:macx { + SOURCES += $$PWD/qextserialenumerator_osx.cpp + } else { + SOURCES += $$PWD/qextserialenumerator_unix.cpp + } +} +win32:SOURCES += $$PWD/qextserialport_win.cpp \ + $$PWD/qextserialenumerator_win.cpp + +linux*{ + !qesp_linux_udev:DEFINES += QESP_NO_UDEV + qesp_linux_udev: LIBS += -ludev +} + +macx:LIBS += -framework IOKit -framework CoreFoundation +win32:LIBS += -lsetupapi -ladvapi32 -luser32 + +# moc doesn't detect Q_OS_LINUX correctly, so add this to make it work +linux*:DEFINES += __linux__ diff --git a/extserial/src/qextserialport_global.h b/extserial/src/qextserialport_global.h new file mode 100644 index 0000000..507d2cf --- /dev/null +++ b/extserial/src/qextserialport_global.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef QEXTSERIALPORT_GLOBAL_H +#define QEXTSERIALPORT_GLOBAL_H + +#include + +#ifdef QEXTSERIALPORT_BUILD_SHARED +# define QEXTSERIALPORT_EXPORT Q_DECL_EXPORT +#elif defined(QEXTSERIALPORT_USING_SHARED) +# define QEXTSERIALPORT_EXPORT Q_DECL_IMPORT +#else +# define QEXTSERIALPORT_EXPORT +#endif + +// ### for compatible with old version. should be removed in QESP 2.0 +#ifdef _TTY_NOWARN_ +# define QESP_NO_WARN +#endif +#ifdef _TTY_NOWARN_PORT_ +# define QESP_NO_PORTABILITY_WARN +#endif + +/*if all warning messages are turned off, flag portability warnings to be turned off as well*/ +#ifdef QESP_NO_WARN +# define QESP_NO_PORTABILITY_WARN +#endif + +/*macros for warning and debug messages*/ +#ifdef QESP_NO_PORTABILITY_WARN +# define QESP_PORTABILITY_WARNING while (false)qWarning +#else +# define QESP_PORTABILITY_WARNING qWarning +#endif /*QESP_NOWARN_PORT*/ + +#ifdef QESP_NO_WARN +# define QESP_WARNING while (false)qWarning +#else +# define QESP_WARNING qWarning +#endif /*QESP_NOWARN*/ + +#endif // QEXTSERIALPORT_GLOBAL_H + diff --git a/extserial/src/qextserialport_p.h b/extserial/src/qextserialport_p.h new file mode 100644 index 0000000..31cea71 --- /dev/null +++ b/extserial/src/qextserialport_p.h @@ -0,0 +1,250 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef _QEXTSERIALPORT_P_H_ +#define _QEXTSERIALPORT_P_H_ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QESP API. It exists for the convenience +// of other QESP classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qextserialport.h" +#include +#ifdef Q_OS_UNIX +# include +#elif (defined Q_OS_WIN) +# include +#endif +#include + +// This is QextSerialPort's read buffer, needed by posix system. +// ref: QRingBuffer & QIODevicePrivateLinearBuffer +class QextReadBuffer +{ +public: + inline QextReadBuffer(size_t growth=4096) + : len(0), first(0), buf(0), capacity(0), basicBlockSize(growth) { + } + + ~QextReadBuffer() { + delete [] buf; + } + + inline void clear() { + first = buf; + len = 0; + } + + inline int size() const { + return len; + } + + inline bool isEmpty() const { + return len == 0; + } + + inline int read(char *target, int size) { + int r = qMin(size, len); + if (r == 1) { + *target = *first; + --len; + ++first; + } else { + memcpy(target, first, r); + len -= r; + first += r; + } + return r; + } + + inline char *reserve(size_t size) { + if ((first - buf) + len + size > capacity) { + size_t newCapacity = qMax(capacity, basicBlockSize); + while (newCapacity < len + size) + newCapacity *= 2; + if (newCapacity > capacity) { + // allocate more space + char *newBuf = new char[newCapacity]; + memmove(newBuf, first, len); + delete [] buf; + buf = newBuf; + capacity = newCapacity; + } else { + // shift any existing data to make space + memmove(buf, first, len); + } + first = buf; + } + char *writePtr = first + len; + len += (int)size; + return writePtr; + } + + inline void chop(int size) { + if (size >= len) + clear(); + else + len -= size; + } + + inline void squeeze() { + if (first != buf) { + memmove(buf, first, len); + first = buf; + } + size_t newCapacity = basicBlockSize; + while (newCapacity < size_t(len)) + newCapacity *= 2; + if (newCapacity < capacity) { + char *tmp = static_cast(realloc(buf, newCapacity)); + if (tmp) { + buf = tmp; + capacity = newCapacity; + } + } + } + + inline QByteArray readAll() { + char *f = first; + int l = len; + clear(); + return QByteArray(f, l); + } + + inline int readLine(char *target, int size) { + int r = qMin(size, len); + char *eol = static_cast(memchr(first, '\n', r)); + if (eol) + r = 1+(eol-first); + memcpy(target, first, r); + len -= r; + first += r; + return int(r); + } + + inline bool canReadLine() const { + return memchr(first, '\n', len); + } + +private: + int len; + char *first; + char *buf; + size_t capacity; + size_t basicBlockSize; +}; + +class QWinEventNotifier; +class QReadWriteLock; +class QSocketNotifier; + +class QextSerialPortPrivate +{ + Q_DECLARE_PUBLIC(QextSerialPort) +public: + QextSerialPortPrivate(QextSerialPort *q); + ~QextSerialPortPrivate(); + enum DirtyFlagEnum + { + DFE_BaudRate = 0x0001, + DFE_Parity = 0x0002, + DFE_StopBits = 0x0004, + DFE_DataBits = 0x0008, + DFE_Flow = 0x0010, + DFE_TimeOut = 0x0100, + DFE_ALL = 0x0fff, + DFE_Settings_Mask = 0x00ff //without TimeOut + }; + mutable QReadWriteLock lock; + QString port; + PortSettings settings; + QextReadBuffer readBuffer; + int settingsDirtyFlags; + ulong lastErr; + QextSerialPort::QueryMode queryMode; + + // platform specific members +#ifdef Q_OS_UNIX + int fd; + QSocketNotifier *readNotifier; + struct termios currentTermios; + struct termios oldTermios; +#elif (defined Q_OS_WIN) + HANDLE handle; + OVERLAPPED overlap; + COMMCONFIG commConfig; + COMMTIMEOUTS commTimeouts; + QWinEventNotifier *winEventNotifier; + DWORD eventMask; + QList pendingWrites; + QReadWriteLock *bytesToWriteLock; +#endif + + /*fill PortSettings*/ + void setBaudRate(BaudRateType baudRate, bool update=true); + void setDataBits(DataBitsType dataBits, bool update=true); + void setParity(ParityType parity, bool update=true); + void setStopBits(StopBitsType stopbits, bool update=true); + void setFlowControl(FlowType flow, bool update=true); + void setTimeout(long millisec, bool update=true); + void setPortSettings(const PortSettings &settings, bool update=true); + + void platformSpecificDestruct(); + void platformSpecificInit(); + void translateError(ulong error); + void updatePortSettings(); + + qint64 readData_sys(char *data, qint64 maxSize); + qint64 writeData_sys(const char *data, qint64 maxSize); + void setDtr_sys(bool set=true); + void setRts_sys(bool set=true); + bool open_sys(QIODevice::OpenMode mode); + bool close_sys(); + bool flush_sys(); + ulong lineStatus_sys(); + qint64 bytesAvailable_sys() const; + +#ifdef Q_OS_WIN + void _q_onWinEvent(HANDLE h); +#endif + void _q_canRead(); + + QextSerialPort *q_ptr; +}; + +#endif //_QEXTSERIALPORT_P_H_ diff --git a/extserial/src/qextserialport_unix.cpp b/extserial/src/qextserialport_unix.cpp new file mode 100644 index 0000000..09c6568 --- /dev/null +++ b/extserial/src/qextserialport_unix.cpp @@ -0,0 +1,458 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "qextserialport.h" +#include "qextserialport_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void QextSerialPortPrivate::platformSpecificInit() +{ + fd = 0; + readNotifier = 0; +} + +/*! + Standard destructor. +*/ +void QextSerialPortPrivate::platformSpecificDestruct() +{ +} + +static QString fullPortName(const QString &name) +{ + if (name.startsWith(QLatin1Char('/'))) + return name; + return QLatin1String("/dev/")+name; +} + +bool QextSerialPortPrivate::open_sys(QIODevice::OpenMode mode) +{ + Q_Q(QextSerialPort); + //note: linux 2.6.21 seems to ignore O_NDELAY flag + if ((fd = ::open(fullPortName(port).toLatin1() ,O_RDWR | O_NOCTTY | O_NDELAY)) != -1) { + + /*In the Private class, We can not call QIODevice::open()*/ + q->setOpenMode(mode); // Flag the port as opened + ::tcgetattr(fd, &oldTermios); // Save the old termios + currentTermios = oldTermios; // Make a working copy + ::cfmakeraw(¤tTermios); // Enable raw access + + /*set up other port settings*/ + currentTermios.c_cflag |= CREAD|CLOCAL; + currentTermios.c_lflag &= (~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|ISIG)); + currentTermios.c_iflag &= (~(INPCK|IGNPAR|PARMRK|ISTRIP|ICRNL|IXANY)); + currentTermios.c_oflag &= (~OPOST); + currentTermios.c_cc[VMIN] = 0; +#ifdef _POSIX_VDISABLE // Is a disable character available on this system? + // Some systems allow for per-device disable-characters, so get the + // proper value for the configured device + const long vdisable = ::fpathconf(fd, _PC_VDISABLE); + currentTermios.c_cc[VINTR] = vdisable; + currentTermios.c_cc[VQUIT] = vdisable; + currentTermios.c_cc[VSTART] = vdisable; + currentTermios.c_cc[VSTOP] = vdisable; + currentTermios.c_cc[VSUSP] = vdisable; +#endif //_POSIX_VDISABLE + settingsDirtyFlags = DFE_ALL; + updatePortSettings(); + + if (queryMode == QextSerialPort::EventDriven) { + readNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, q); + q->connect(readNotifier, SIGNAL(activated(int)), q, SLOT(_q_canRead())); + } + return true; + } else { + translateError(errno); + return false; + } +} + +bool QextSerialPortPrivate::close_sys() +{ + // Force a flush and then restore the original termios + flush_sys(); + // Using both TCSAFLUSH and TCSANOW here discards any pending input + ::tcsetattr(fd, TCSAFLUSH | TCSANOW, &oldTermios); // Restore termios + ::close(fd); + if (readNotifier) { + delete readNotifier; + readNotifier = 0; + } + return true; +} + +bool QextSerialPortPrivate::flush_sys() +{ + ::tcdrain(fd); + return true; +} + +qint64 QextSerialPortPrivate::bytesAvailable_sys() const +{ + int bytesQueued; + if (::ioctl(fd, FIONREAD, &bytesQueued) == -1) + return (qint64)-1; + return bytesQueued; +} + +/*! + Translates a system-specific error code to a QextSerialPort error code. Used internally. +*/ +void QextSerialPortPrivate::translateError(ulong error) +{ + switch (error) { + case EBADF: + case ENOTTY: + lastErr = E_INVALID_FD; + break; + case EINTR: + lastErr = E_CAUGHT_NON_BLOCKED_SIGNAL; + break; + case ENOMEM: + lastErr = E_NO_MEMORY; + break; + case EACCES: + lastErr = E_PERMISSION_DENIED; + break; + case EAGAIN: + lastErr = E_AGAIN; + break; + } +} + +void QextSerialPortPrivate::setDtr_sys(bool set) +{ + int status; + ::ioctl(fd, TIOCMGET, &status); + if (set) + status |= TIOCM_DTR; + else + status &= ~TIOCM_DTR; + ::ioctl(fd, TIOCMSET, &status); +} + +void QextSerialPortPrivate::setRts_sys(bool set) +{ + int status; + ::ioctl(fd, TIOCMGET, &status); + if (set) + status |= TIOCM_RTS; + else + status &= ~TIOCM_RTS; + ::ioctl(fd, TIOCMSET, &status); +} + +unsigned long QextSerialPortPrivate::lineStatus_sys() +{ + unsigned long Status=0, Temp=0; + ::ioctl(fd, TIOCMGET, &Temp); + if (Temp & TIOCM_CTS) Status |= LS_CTS; + if (Temp & TIOCM_DSR) Status |= LS_DSR; + if (Temp & TIOCM_RI) Status |= LS_RI; + if (Temp & TIOCM_CD) Status |= LS_DCD; + if (Temp & TIOCM_DTR) Status |= LS_DTR; + if (Temp & TIOCM_RTS) Status |= LS_RTS; + if (Temp & TIOCM_ST) Status |= LS_ST; + if (Temp & TIOCM_SR) Status |= LS_SR; + return Status; +} + +/*! + Reads a block of data from the serial port. This function will read at most maxSize bytes from + the serial port and place them in the buffer pointed to by data. Return value is the number of + bytes actually read, or -1 on error. + + \warning before calling this function ensure that serial port associated with this class + is currently open (use isOpen() function to check if port is open). +*/ +qint64 QextSerialPortPrivate::readData_sys(char *data, qint64 maxSize) +{ + int retVal = ::read(fd, data, maxSize); + if (retVal == -1) + lastErr = E_READ_FAILED; + + return retVal; +} + +/*! + Writes a block of data to the serial port. This function will write maxSize bytes + from the buffer pointed to by data to the serial port. Return value is the number + of bytes actually written, or -1 on error. + + \warning before calling this function ensure that serial port associated with this class + is currently open (use isOpen() function to check if port is open). +*/ +qint64 QextSerialPortPrivate::writeData_sys(const char *data, qint64 maxSize) +{ + int retVal = ::write(fd, data, maxSize); + if (retVal == -1) + lastErr = E_WRITE_FAILED; + + return (qint64)retVal; +} + +static void setBaudRate2Termios(termios *config, int baudRate) +{ +#ifdef CBAUD + config->c_cflag &= (~CBAUD); + config->c_cflag |= baudRate; +#else + ::cfsetispeed(config, baudRate); + ::cfsetospeed(config, baudRate); +#endif +} + +/* + All the platform settings was performed in this function. +*/ +void QextSerialPortPrivate::updatePortSettings() +{ + if (!q_func()->isOpen() || !settingsDirtyFlags) + return; + + if (settingsDirtyFlags & DFE_BaudRate) { + switch (settings.BaudRate) { + case BAUD50: + setBaudRate2Termios(¤tTermios, B50); + break; + case BAUD75: + setBaudRate2Termios(¤tTermios, B75); + break; + case BAUD110: + setBaudRate2Termios(¤tTermios, B110); + break; + case BAUD134: + setBaudRate2Termios(¤tTermios, B134); + break; + case BAUD150: + setBaudRate2Termios(¤tTermios, B150); + break; + case BAUD200: + setBaudRate2Termios(¤tTermios, B200); + break; + case BAUD300: + setBaudRate2Termios(¤tTermios, B300); + break; + case BAUD600: + setBaudRate2Termios(¤tTermios, B600); + break; + case BAUD1200: + setBaudRate2Termios(¤tTermios, B1200); + break; + case BAUD1800: + setBaudRate2Termios(¤tTermios, B1800); + break; + case BAUD2400: + setBaudRate2Termios(¤tTermios, B2400); + break; + case BAUD4800: + setBaudRate2Termios(¤tTermios, B4800); + break; + case BAUD9600: + setBaudRate2Termios(¤tTermios, B9600); + break; + case BAUD19200: + setBaudRate2Termios(¤tTermios, B19200); + break; + case BAUD38400: + setBaudRate2Termios(¤tTermios, B38400); + break; + case BAUD57600: + setBaudRate2Termios(¤tTermios, B57600); + break; +#ifdef B76800 + case BAUD76800: + setBaudRate2Termios(¤tTermios, B76800); + break; +#endif + case BAUD115200: + setBaudRate2Termios(¤tTermios, B115200); + break; +#if defined(B230400) && defined(B4000000) + case BAUD230400: + setBaudRate2Termios(¤tTermios, B230400); + break; + case BAUD460800: + setBaudRate2Termios(¤tTermios, B460800); + break; + case BAUD500000: + setBaudRate2Termios(¤tTermios, B500000); + break; + case BAUD576000: + setBaudRate2Termios(¤tTermios, B576000); + break; + case BAUD921600: + setBaudRate2Termios(¤tTermios, B921600); + break; + case BAUD1000000: + setBaudRate2Termios(¤tTermios, B1000000); + break; + case BAUD1152000: + setBaudRate2Termios(¤tTermios, B1152000); + break; + case BAUD1500000: + setBaudRate2Termios(¤tTermios, B1500000); + break; + case BAUD2000000: + setBaudRate2Termios(¤tTermios, B2000000); + break; + case BAUD2500000: + setBaudRate2Termios(¤tTermios, B2500000); + break; + case BAUD3000000: + setBaudRate2Termios(¤tTermios, B3000000); + break; + case BAUD3500000: + setBaudRate2Termios(¤tTermios, B3500000); + break; + case BAUD4000000: + setBaudRate2Termios(¤tTermios, B4000000); + break; +#endif +#ifdef Q_OS_MAC + default: + setBaudRate2Termios(¤tTermios, settings.BaudRate); + break; +#endif + } + } + if (settingsDirtyFlags & DFE_Parity) { + switch (settings.Parity) { + case PAR_SPACE: + /*space parity not directly supported - add an extra data bit to simulate it*/ + settingsDirtyFlags |= DFE_DataBits; + break; + case PAR_NONE: + currentTermios.c_cflag &= (~PARENB); + break; + case PAR_EVEN: + currentTermios.c_cflag &= (~PARODD); + currentTermios.c_cflag |= PARENB; + break; + case PAR_ODD: + currentTermios.c_cflag |= (PARENB|PARODD); + break; + } + } + /*must after Parity settings*/ + if (settingsDirtyFlags & DFE_DataBits) { + if (settings.Parity != PAR_SPACE) { + currentTermios.c_cflag &= (~CSIZE); + switch(settings.DataBits) { + case DATA_5: + currentTermios.c_cflag |= CS5; + break; + case DATA_6: + currentTermios.c_cflag |= CS6; + break; + case DATA_7: + currentTermios.c_cflag |= CS7; + break; + case DATA_8: + currentTermios.c_cflag |= CS8; + break; + } + } else { + /*space parity not directly supported - add an extra data bit to simulate it*/ + currentTermios.c_cflag &= ~(PARENB|CSIZE); + switch(settings.DataBits) { + case DATA_5: + currentTermios.c_cflag |= CS6; + break; + case DATA_6: + currentTermios.c_cflag |= CS7; + break; + case DATA_7: + currentTermios.c_cflag |= CS8; + break; + case DATA_8: + /*this will never happen, put here to Suppress an warning*/ + break; + } + } + } + if (settingsDirtyFlags & DFE_StopBits) { + switch (settings.StopBits) { + case STOP_1: + currentTermios.c_cflag &= (~CSTOPB); + break; + case STOP_2: + currentTermios.c_cflag |= CSTOPB; + break; + } + } + if (settingsDirtyFlags & DFE_Flow) { + switch(settings.FlowControl) { + case FLOW_OFF: + currentTermios.c_cflag &= (~CRTSCTS); + currentTermios.c_iflag &= (~(IXON|IXOFF|IXANY)); + break; + case FLOW_XONXOFF: + /*software (XON/XOFF) flow control*/ + currentTermios.c_cflag &= (~CRTSCTS); + currentTermios.c_iflag |= (IXON|IXOFF|IXANY); + break; + case FLOW_HARDWARE: + currentTermios.c_cflag |= CRTSCTS; + currentTermios.c_iflag &= (~(IXON|IXOFF|IXANY)); + break; + } + } + + /*if any thing in currentTermios changed, flush*/ + if (settingsDirtyFlags & DFE_Settings_Mask) + ::tcsetattr(fd, TCSAFLUSH, ¤tTermios); + + if (settingsDirtyFlags & DFE_TimeOut) { + int millisec = settings.Timeout_Millisec; + if (millisec == -1) { + ::fcntl(fd, F_SETFL, O_NDELAY); + } else { + //O_SYNC should enable blocking ::write() + //however this seems not working on Linux 2.6.21 (works on OpenBSD 4.2) + ::fcntl(fd, F_SETFL, O_SYNC); + } + ::tcgetattr(fd, ¤tTermios); + currentTermios.c_cc[VTIME] = millisec/100; + ::tcsetattr(fd, TCSAFLUSH, ¤tTermios); + } + + settingsDirtyFlags = 0; +} diff --git a/extserial/src/qextserialport_win.cpp b/extserial/src/qextserialport_win.cpp new file mode 100644 index 0000000..1d25e0e --- /dev/null +++ b/extserial/src/qextserialport_win.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** Copyright (c) 2000-2003 Wayne Roth +** Copyright (c) 2004-2007 Stefan Sander +** Copyright (c) 2007 Michal Policht +** Copyright (c) 2008 Brandon Fosdick +** Copyright (c) 2009-2010 Liam Staskawicz +** Copyright (c) 2011 Debao Zhang +** All right reserved. +** Web: http://code.google.com/p/qextserialport/ +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "qextserialport.h" +#include "qextserialport_p.h" +#include +#include +#include +#include +#include +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +# include +#else +# include +#endif +void QextSerialPortPrivate::platformSpecificInit() +{ + handle = INVALID_HANDLE_VALUE; + ZeroMemory(&overlap, sizeof(OVERLAPPED)); + overlap.hEvent = CreateEvent(NULL, true, false, NULL); + winEventNotifier = 0; + bytesToWriteLock = new QReadWriteLock; +} + +void QextSerialPortPrivate::platformSpecificDestruct() { + CloseHandle(overlap.hEvent); + delete bytesToWriteLock; +} + + +/*! + \internal + COM ports greater than 9 need \\.\ prepended + + This is only need when open the port. +*/ +static QString fullPortNameWin(const QString &name) +{ + QRegExp rx(QLatin1String("^COM(\\d+)")); + QString fullName(name); + if (fullName.contains(rx)) + fullName.prepend(QLatin1String("\\\\.\\")); + return fullName; +} + +bool QextSerialPortPrivate::open_sys(QIODevice::OpenMode mode) +{ + Q_Q(QextSerialPort); + DWORD confSize = sizeof(COMMCONFIG); + commConfig.dwSize = confSize; + DWORD dwFlagsAndAttributes = 0; + if (queryMode == QextSerialPort::EventDriven) + dwFlagsAndAttributes += FILE_FLAG_OVERLAPPED; + + /*open the port*/ + handle = CreateFileW((wchar_t *)fullPortNameWin(port).utf16(), GENERIC_READ|GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL); + if (handle != INVALID_HANDLE_VALUE) { + q->setOpenMode(mode); + /*configure port settings*/ + GetCommConfig(handle, &commConfig, &confSize); + GetCommState(handle, &(commConfig.dcb)); + + /*set up parameters*/ + commConfig.dcb.fBinary = TRUE; + commConfig.dcb.fInX = FALSE; + commConfig.dcb.fOutX = FALSE; + commConfig.dcb.fAbortOnError = FALSE; + commConfig.dcb.fNull = FALSE; + /* Dtr default to true. See Issue 122*/ + commConfig.dcb.fDtrControl = TRUE; + /*flush all settings*/ + settingsDirtyFlags = DFE_ALL; + updatePortSettings(); + + //init event driven approach + if (queryMode == QextSerialPort::EventDriven) { + if (!SetCommMask(handle, EV_TXEMPTY | EV_RXCHAR | EV_DSR)) { + QESP_WARNING()<<"failed to set Comm Mask. Error code:"<("HANDLE"); + q->connect(winEventNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_onWinEvent(HANDLE)), Qt::DirectConnection); + WaitCommEvent(handle, &eventMask, &overlap); + } + return true; + } + return false; +} + +bool QextSerialPortPrivate::close_sys() +{ + flush_sys(); + CancelIo(handle); + if (CloseHandle(handle)) + handle = INVALID_HANDLE_VALUE; + if (winEventNotifier) { + winEventNotifier->setEnabled(false); + winEventNotifier->deleteLater(); + winEventNotifier = 0; + } + + foreach (OVERLAPPED *o, pendingWrites) { + CloseHandle(o->hEvent); + delete o; + } + pendingWrites.clear(); + return true; +} + +bool QextSerialPortPrivate::flush_sys() +{ + FlushFileBuffers(handle); + return true; +} + +qint64 QextSerialPortPrivate::bytesAvailable_sys() const +{ + DWORD Errors; + COMSTAT Status; + if (ClearCommError(handle, &Errors, &Status)) + return Status.cbInQue; + + return (qint64)-1; +} + +/* + Translates a system-specific error code to a QextSerialPort error code. Used internally. +*/ +void QextSerialPortPrivate::translateError(ulong error) +{ + if (error & CE_BREAK) { + lastErr = E_BREAK_CONDITION; + } else if (error & CE_FRAME) { + lastErr = E_FRAMING_ERROR; + } else if (error & CE_IOE) { + lastErr = E_IO_ERROR; + } else if (error & CE_MODE) { + lastErr = E_INVALID_FD; + } else if (error & CE_OVERRUN) { + lastErr = E_BUFFER_OVERRUN; + } else if (error & CE_RXPARITY) { + lastErr = E_RECEIVE_PARITY_ERROR; + } else if (error & CE_RXOVER) { + lastErr = E_RECEIVE_OVERFLOW; + } else if (error & CE_TXFULL) { + lastErr = E_TRANSMIT_OVERFLOW; + } +} + +/* + Reads a block of data from the serial port. This function will read at most maxlen bytes from + the serial port and place them in the buffer pointed to by data. Return value is the number of + bytes actually read, or -1 on error. + + \warning before calling this function ensure that serial port associated with this class + is currently open (use isOpen() function to check if port is open). +*/ +qint64 QextSerialPortPrivate::readData_sys(char *data, qint64 maxSize) +{ + DWORD bytesRead = 0; + bool failed = false; + if (queryMode == QextSerialPort::EventDriven) { + OVERLAPPED overlapRead; + ZeroMemory(&overlapRead, sizeof(OVERLAPPED)); + if (!ReadFile(handle, (void *)data, (DWORD)maxSize, &bytesRead, &overlapRead)) { + if (GetLastError() == ERROR_IO_PENDING) + GetOverlappedResult(handle, &overlapRead, &bytesRead, true); + else + failed = true; + } + } else if (!ReadFile(handle, (void *)data, (DWORD)maxSize, &bytesRead, NULL)) { + failed = true; + } + if (!failed) + return (qint64)bytesRead; + + lastErr = E_READ_FAILED; + return -1; +} + +/* + Writes a block of data to the serial port. This function will write len bytes + from the buffer pointed to by data to the serial port. Return value is the number + of bytes actually written, or -1 on error. + + \warning before calling this function ensure that serial port associated with this class + is currently open (use isOpen() function to check if port is open). +*/ +qint64 QextSerialPortPrivate::writeData_sys(const char *data, qint64 maxSize) +{ + DWORD bytesWritten = 0; + bool failed = false; + if (queryMode == QextSerialPort::EventDriven) { + OVERLAPPED *newOverlapWrite = new OVERLAPPED; + ZeroMemory(newOverlapWrite, sizeof(OVERLAPPED)); + newOverlapWrite->hEvent = CreateEvent(NULL, true, false, NULL); + if (WriteFile(handle, (void *)data, (DWORD)maxSize, &bytesWritten, newOverlapWrite)) { + CloseHandle(newOverlapWrite->hEvent); + delete newOverlapWrite; + } else if (GetLastError() == ERROR_IO_PENDING) { + // writing asynchronously...not an error + QWriteLocker writelocker(bytesToWriteLock); + pendingWrites.append(newOverlapWrite); + } else { + QESP_WARNING()<<"QextSerialPort write error:"<hEvent)) + QESP_WARNING("QextSerialPort: couldn't cancel IO"); + if (!CloseHandle(newOverlapWrite->hEvent)) + QESP_WARNING("QextSerialPort: couldn't close OVERLAPPED handle"); + delete newOverlapWrite; + } + } else if (!WriteFile(handle, (void *)data, (DWORD)maxSize, &bytesWritten, NULL)) { + failed = true; + } + + if (!failed) + return (qint64)bytesWritten; + + lastErr = E_WRITE_FAILED; + return -1; +} + +void QextSerialPortPrivate::setDtr_sys(bool set) { + EscapeCommFunction(handle, set ? SETDTR : CLRDTR); +} + +void QextSerialPortPrivate::setRts_sys(bool set) { + EscapeCommFunction(handle, set ? SETRTS : CLRRTS); +} + +ulong QextSerialPortPrivate::lineStatus_sys(void) { + unsigned long Status = 0, Temp = 0; + GetCommModemStatus(handle, &Temp); + if (Temp & MS_CTS_ON) Status |= LS_CTS; + if (Temp & MS_DSR_ON) Status |= LS_DSR; + if (Temp & MS_RING_ON) Status |= LS_RI; + if (Temp & MS_RLSD_ON) Status |= LS_DCD; + return Status; +} + +/* + Triggered when there's activity on our HANDLE. +*/ +void QextSerialPortPrivate::_q_onWinEvent(HANDLE h) +{ + Q_Q(QextSerialPort); + if (h == overlap.hEvent) { + if (eventMask & EV_RXCHAR) { + if (q->sender() != q && bytesAvailable_sys() > 0) + _q_canRead(); + } + if (eventMask & EV_TXEMPTY) { + /* + A write completed. Run through the list of OVERLAPPED writes, and if + they completed successfully, take them off the list and delete them. + Otherwise, leave them on there so they can finish. + */ + qint64 totalBytesWritten = 0; + QList overlapsToDelete; + QWriteLocker writelocker(bytesToWriteLock); + foreach (OVERLAPPED *o, pendingWrites) { + DWORD numBytes = 0; + if (GetOverlappedResult(handle, o, &numBytes, false)) { + overlapsToDelete.append(o); + totalBytesWritten += numBytes; + } else if (GetLastError() != ERROR_IO_INCOMPLETE) { + overlapsToDelete.append(o); + QESP_WARNING()<<"CommEvent overlapped write error:" << GetLastError(); + } + } + + if (q->sender() != q && totalBytesWritten > 0) + Q_EMIT q->bytesWritten(totalBytesWritten); + + foreach (OVERLAPPED *o, overlapsToDelete) { + OVERLAPPED *toDelete = pendingWrites.takeAt(pendingWrites.indexOf(o)); + CloseHandle(toDelete->hEvent); + delete toDelete; + } + } + if (eventMask & EV_DSR) { + if (lineStatus_sys() & LS_DSR) + Q_EMIT q->dsrChanged(true); + else + Q_EMIT q->dsrChanged(false); + } + } + WaitCommEvent(handle, &eventMask, &overlap); +} + +void QextSerialPortPrivate::updatePortSettings() +{ + if (!q_ptr->isOpen() || !settingsDirtyFlags) + return; + + //fill struct : COMMCONFIG + if (settingsDirtyFlags & DFE_BaudRate) + commConfig.dcb.BaudRate = settings.BaudRate; + if (settingsDirtyFlags & DFE_Parity) { + commConfig.dcb.Parity = (BYTE)settings.Parity; + commConfig.dcb.fParity = (settings.Parity == PAR_NONE) ? FALSE : TRUE; + } + if (settingsDirtyFlags & DFE_DataBits) + commConfig.dcb.ByteSize = (BYTE)settings.DataBits; + if (settingsDirtyFlags & DFE_StopBits) { + switch (settings.StopBits) { + case STOP_1: + commConfig.dcb.StopBits = ONESTOPBIT; + break; + case STOP_1_5: + commConfig.dcb.StopBits = ONE5STOPBITS; + break; + case STOP_2: + commConfig.dcb.StopBits = TWOSTOPBITS; + break; + } + } + if (settingsDirtyFlags & DFE_Flow) { + switch(settings.FlowControl) { + /*no flow control*/ + case FLOW_OFF: + commConfig.dcb.fOutxCtsFlow = FALSE; + commConfig.dcb.fRtsControl = RTS_CONTROL_DISABLE; + commConfig.dcb.fInX = FALSE; + commConfig.dcb.fOutX = FALSE; + break; + /*software (XON/XOFF) flow control*/ + case FLOW_XONXOFF: + commConfig.dcb.fOutxCtsFlow = FALSE; + commConfig.dcb.fRtsControl = RTS_CONTROL_DISABLE; + commConfig.dcb.fInX = TRUE; + commConfig.dcb.fOutX = TRUE; + break; + /*hardware flow control*/ + case FLOW_HARDWARE: + commConfig.dcb.fOutxCtsFlow = TRUE; + commConfig.dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; + commConfig.dcb.fInX = FALSE; + commConfig.dcb.fOutX = FALSE; + break; + } + } + + //fill struct : COMMTIMEOUTS + if (settingsDirtyFlags & DFE_TimeOut) { + if (queryMode != QextSerialPort::EventDriven) { + int millisec = settings.Timeout_Millisec; + if (millisec == -1) { + commTimeouts.ReadIntervalTimeout = MAXDWORD; + commTimeouts.ReadTotalTimeoutConstant = 0; + } else { + commTimeouts.ReadIntervalTimeout = millisec; + commTimeouts.ReadTotalTimeoutConstant = millisec; + } + commTimeouts.ReadTotalTimeoutMultiplier = 0; + commTimeouts.WriteTotalTimeoutMultiplier = millisec; + commTimeouts.WriteTotalTimeoutConstant = 0; + } else { + commTimeouts.ReadIntervalTimeout = MAXDWORD; + commTimeouts.ReadTotalTimeoutMultiplier = 0; + commTimeouts.ReadTotalTimeoutConstant = 0; + commTimeouts.WriteTotalTimeoutMultiplier = 0; + commTimeouts.WriteTotalTimeoutConstant = 0; + } + } + + + if (settingsDirtyFlags & DFE_Settings_Mask) + SetCommConfig(handle, &commConfig, sizeof(COMMCONFIG)); + if ((settingsDirtyFlags & DFE_TimeOut)) + SetCommTimeouts(handle, &commTimeouts); + settingsDirtyFlags = 0; +} diff --git a/qsswraper.pri b/qsswraper.pri index a8b4977..4f4aa74 100644 --- a/qsswraper.pri +++ b/qsswraper.pri @@ -1,6 +1,9 @@ INCLUDEPATH += $$PWD DEPENDPATH += $$PWD + +include($$PWD/extserial/src/qextserialport.pri) + HEADERS += $$PWD/Qss.h SOURCES += $$PWD/Qss.cpp