通用串口库加入
parent
2e9a1b69db
commit
c5e5f10f87
24
Qss.cpp
24
Qss.cpp
|
@ -103,6 +103,8 @@ QssTtitleBar::QssTtitleBar(QWidget *parent ,
|
||||||
|
|
||||||
QssTtitleBar::~QssTtitleBar()
|
QssTtitleBar::~QssTtitleBar()
|
||||||
{
|
{
|
||||||
|
delete(this->m_maxBtn);
|
||||||
|
delete(this->m_restoreBtn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QssTtitleBar::setTitle( QString title )
|
void QssTtitleBar::setTitle( QString title )
|
||||||
|
@ -118,7 +120,6 @@ void QssTtitleBar::setIcon( QIcon icon)
|
||||||
void QssTtitleBar::setMaxOrRestore( bool val)
|
void QssTtitleBar::setMaxOrRestore( bool val)
|
||||||
{
|
{
|
||||||
m_maxOrRestore = val;//true
|
m_maxOrRestore = val;//true
|
||||||
|
|
||||||
if ((m_type & QTitleBar_Button_Restore) && (m_type & QTitleBar_Button_Max))
|
if ((m_type & QTitleBar_Button_Restore) && (m_type & QTitleBar_Button_Max))
|
||||||
{
|
{
|
||||||
m_restoreBtn->setVisible(m_maxOrRestore);
|
m_restoreBtn->setVisible(m_maxOrRestore);
|
||||||
|
@ -187,11 +188,6 @@ bool QssTtitleBar::eventFilter( QObject * obj, QEvent * ev )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (obj == m_iconBtn)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return QWidget::eventFilter(obj, ev);
|
return QWidget::eventFilter(obj, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,9 +249,7 @@ QssMainWindow::QssMainWindow(QWidget *parent/* = 0*/, Qt::WindowFlags flags/* =
|
||||||
m_bLeftPress(false)
|
m_bLeftPress(false)
|
||||||
{
|
{
|
||||||
m_rcValid = QApplication::desktop()->availableGeometry();
|
m_rcValid = QApplication::desktop()->availableGeometry();
|
||||||
|
|
||||||
m_frame = new QFrame(parent, flags);
|
m_frame = new QFrame(parent, flags);
|
||||||
//css
|
|
||||||
m_frame->setObjectName("window");
|
m_frame->setObjectName("window");
|
||||||
|
|
||||||
m_frame->setWindowFlags(Qt::Window |
|
m_frame->setWindowFlags(Qt::Window |
|
||||||
|
@ -287,8 +281,6 @@ QssMainWindow::QssMainWindow(QWidget *parent/* = 0*/, Qt::WindowFlags flags/* =
|
||||||
QTextStream in(&file);
|
QTextStream in(&file);
|
||||||
QString css = in.readAll();
|
QString css = in.readAll();
|
||||||
//this->setStyleSheet(css);
|
//this->setStyleSheet(css);
|
||||||
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,9 +631,12 @@ void QssMainWindow::ScaleChanged(float scale)
|
||||||
|
|
||||||
WId QssMainWindow::GetWID() const
|
WId QssMainWindow::GetWID() const
|
||||||
{
|
{
|
||||||
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void QssMainWindow::SetScale(float scale)
|
void QssMainWindow::SetScale(float scale)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -649,8 +644,8 @@ void QssMainWindow::SetScale(float scale)
|
||||||
|
|
||||||
QssDialog::QssDialog(QWidget *parent)
|
QssDialog::QssDialog(QWidget *parent)
|
||||||
: QDialog(0),
|
: QDialog(0),
|
||||||
m_mousePressedInBorder(false),
|
m_parent(parent),
|
||||||
m_parent(parent)
|
m_mousePressedInBorder(false)
|
||||||
{
|
{
|
||||||
m_rcValid = QApplication::desktop()->availableGeometry();
|
m_rcValid = QApplication::desktop()->availableGeometry();
|
||||||
|
|
||||||
|
@ -696,13 +691,11 @@ void QssDockWidget::paintEvent(QPaintEvent *){
|
||||||
|
|
||||||
void QssDockWidget::show()
|
void QssDockWidget::show()
|
||||||
{
|
{
|
||||||
/** resize m_framem_framesizehint */
|
|
||||||
int offset = (QSSDIALOG_SHADOW_WIDTH + QSSDIALOG_BODER_WIDTH)*2;//rect()<29><><EFBFBD><EFBFBD>padding<6E><67>paddingframe
|
int offset = (QSSDIALOG_SHADOW_WIDTH + QSSDIALOG_BODER_WIDTH)*2;//rect()<29><><EFBFBD><EFBFBD>padding<6E><67>paddingframe
|
||||||
m_frame->resize(rect().width() + offset, rect().height() + m_titleBar->rect().height() + offset);
|
m_frame->resize(rect().width() + offset, rect().height() + m_titleBar->rect().height() + offset);
|
||||||
|
|
||||||
QDockWidget::show();
|
QDockWidget::show();
|
||||||
m_frame->show();
|
m_frame->show();
|
||||||
//m_titleBar->show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QssDialog::raise()
|
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->resize(rect().width() + offset, rect().height() + m_titleBar->rect().height() + offset);
|
||||||
|
|
||||||
m_frame->setWindowModality(Qt::ApplicationModal);//Qt::ApplicationModal
|
m_frame->setWindowModality(Qt::ApplicationModal);//Qt::ApplicationModal
|
||||||
//m_frame->setWindowFlags(m_frame->windowFlags() | Qt::Tool);//Qt::Tool
|
|
||||||
|
|
||||||
m_frame->show();
|
m_frame->show();
|
||||||
m_frame->raise();
|
m_frame->raise();
|
||||||
|
@ -884,13 +876,11 @@ void QssDockWidget::onMousePressEvent( QMouseEvent * ev )
|
||||||
void QssDialog::onMouseReleaseEvent( QMouseEvent * )
|
void QssDialog::onMouseReleaseEvent( QMouseEvent * )
|
||||||
{
|
{
|
||||||
m_mousePressedInBorder = false;
|
m_mousePressedInBorder = false;
|
||||||
//qDebug() << "mousePressed release in border";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QssDockWidget::onMouseReleaseEvent( QMouseEvent * ev )
|
void QssDockWidget::onMouseReleaseEvent( QMouseEvent * ev )
|
||||||
{
|
{
|
||||||
m_mousePressedInBorder = false;
|
m_mousePressedInBorder = false;
|
||||||
//qDebug() << "mousePressed release in border";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QssDialog::eventFilter( QObject * obj, QEvent * ev )
|
bool QssDialog::eventFilter( QObject * obj, QEvent * ev )
|
||||||
|
|
7
Qss.h
7
Qss.h
|
@ -187,7 +187,7 @@ class QssEventFilter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) Q_DECL_OVERRIDE
|
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) Q_DECL_OVERRIDE
|
||||||
{
|
{/*
|
||||||
MSG* pMsg = reinterpret_cast<MSG*>(message);
|
MSG* pMsg = reinterpret_cast<MSG*>(message);
|
||||||
if(nullptr != pMsg){
|
if(nullptr != pMsg){
|
||||||
switch (pMsg->message)
|
switch (pMsg->message)
|
||||||
|
@ -198,7 +198,7 @@ public:
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
// TODO: filter out or modify msg struct here
|
// TODO: filter out or modify msg struct here
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -292,11 +292,12 @@ private:
|
||||||
void onMouseReleaseEvent(QMouseEvent * ev);
|
void onMouseReleaseEvent(QMouseEvent * ev);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QWidget* m_parent;
|
||||||
|
|
||||||
QFrame* m_frame;
|
QFrame* m_frame;
|
||||||
QssTtitleBar* m_titleBar;
|
QssTtitleBar* m_titleBar;
|
||||||
|
|
||||||
QRect m_rcValid;
|
QRect m_rcValid;
|
||||||
QWidget* m_parent;
|
|
||||||
|
|
||||||
/** 边框调整大小相关 */
|
/** 边框调整大小相关 */
|
||||||
QRect mFrameRect;
|
QRect mFrameRect;
|
||||||
|
|
|
@ -44,6 +44,7 @@ QWidget#qssTitleBar >QPushButton#titlebarrestorebtn{
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height:30px;
|
height:30px;
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
QWidget#qssTitleBar >QPushButton#titlebarrestorebtn:enabled:hover{
|
QWidget#qssTitleBar >QPushButton#titlebarrestorebtn:enabled:hover{
|
||||||
background: rgb(187, 212, 238);
|
background: rgb(187, 212, 238);
|
||||||
|
|
10
css/qss.css
10
css/qss.css
|
@ -884,11 +884,11 @@ QToolButton#transferButton:hover {
|
||||||
|
|
||||||
/**********按钮**********/
|
/**********按钮**********/
|
||||||
QPushButton{
|
QPushButton{
|
||||||
border: none;
|
border: none;
|
||||||
width: 75px;
|
width: 75px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
QPushButton:enabled {
|
QPushButton:enabled {
|
||||||
background: rgb(120, 170, 220);
|
background: rgb(120, 170, 220);
|
||||||
|
|
|
@ -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 <QtCore/QDebug>
|
||||||
|
#include <QtCore/QMetaType>
|
||||||
|
#include <QtCore/QRegExp>
|
||||||
|
|
||||||
|
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<QextPortInfo> 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>("QextPortInfo");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Destructs the QextSerialEnumerator object.
|
||||||
|
*/
|
||||||
|
QextSerialEnumerator::~QextSerialEnumerator()
|
||||||
|
{
|
||||||
|
delete d_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Get list of ports.
|
||||||
|
|
||||||
|
return list of ports currently available in the system.
|
||||||
|
*/
|
||||||
|
QList<QextPortInfo> 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"
|
|
@ -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 <QtCore/QList>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#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<QextPortInfo> 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_*/
|
|
@ -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 <QtCore/QDebug>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
|
||||||
|
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<QextPortInfo> QextSerialEnumeratorPrivate::getPorts_sys()
|
||||||
|
{
|
||||||
|
QList<QextPortInfo> 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
|
|
@ -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 <QtCore/QDebug>
|
||||||
|
#include <IOKit/serial/IOSerialKeys.h>
|
||||||
|
#include <CoreFoundation/CFNumber.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
|
||||||
|
void QextSerialEnumeratorPrivate::init_sys()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QextSerialEnumeratorPrivate::destroy_sys()
|
||||||
|
{
|
||||||
|
IONotificationPortDestroy(notificationPortRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
QList<QextPortInfo> QextSerialEnumeratorPrivate::getPorts_sys()
|
||||||
|
{
|
||||||
|
QList<QextPortInfo> 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<QextPortInfo> &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;
|
||||||
|
}
|
||||||
|
|
|
@ -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 <QtCore/qt_windows.h>
|
||||||
|
#endif /*Q_OS_WIN*/
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
# include <IOKit/usb/IOUSBLib.h>
|
||||||
|
#endif /*Q_OS_MAC*/
|
||||||
|
|
||||||
|
#if defined(Q_OS_LINUX) && !defined(QESP_NO_UDEV)
|
||||||
|
# include <QSocketNotifier>
|
||||||
|
extern "C" {
|
||||||
|
# include <libudev.h>
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class QextSerialRegistrationWidget;
|
||||||
|
class QextSerialEnumeratorPrivate
|
||||||
|
{
|
||||||
|
Q_DECLARE_PUBLIC(QextSerialEnumerator)
|
||||||
|
public:
|
||||||
|
QextSerialEnumeratorPrivate(QextSerialEnumerator *enumrator);
|
||||||
|
~QextSerialEnumeratorPrivate();
|
||||||
|
void init_sys();
|
||||||
|
void destroy_sys();
|
||||||
|
|
||||||
|
static QList<QextPortInfo> 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<QextPortInfo> &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_
|
|
@ -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 <QtCore/QDebug>
|
||||||
|
|
||||||
|
void QextSerialEnumeratorPrivate::init_sys()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QextSerialEnumeratorPrivate::destroy_sys()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QextPortInfo> QextSerialEnumeratorPrivate::getPorts_sys()
|
||||||
|
{
|
||||||
|
QList<QextPortInfo> 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;
|
||||||
|
}
|
|
@ -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 <QtCore/QDebug>
|
||||||
|
#include <QtCore/QMetaType>
|
||||||
|
#include <QtCore/QRegExp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <objbase.h>
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <setupapi.h>
|
||||||
|
#include <dbt.h>
|
||||||
|
|
||||||
|
#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 <QtGui/QWidget>
|
||||||
|
class QextSerialRegistrationWidget : public QWidget
|
||||||
|
#else
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
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 *>(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<ushort *>(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<ushort *>(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<QextPortInfo> *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()<s2.portName.mid(3).toInt();
|
||||||
|
}
|
||||||
|
return s1.portName < s2.portName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Get list of ports.
|
||||||
|
|
||||||
|
return list of ports currently available in the system.
|
||||||
|
*/
|
||||||
|
QList<QextPortInfo> QextSerialEnumeratorPrivate::getPorts_sys()
|
||||||
|
{
|
||||||
|
QList<QextPortInfo> 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<ushort *>(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<ushort *>(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
|
File diff suppressed because it is too large
Load Diff
|
@ -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 <QtCore/QIODevice>
|
||||||
|
#include "qextserialport_global.h"
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
#include <termios.h>
|
||||||
|
#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
|
|
@ -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__
|
|
@ -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 <QtCore/QtGlobal>
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
|
@ -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 <QtCore/QReadWriteLock>
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
# include <termios.h>
|
||||||
|
#elif (defined Q_OS_WIN)
|
||||||
|
# include <QtCore/qt_windows.h>
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// 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<char *>(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<char *>(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<OVERLAPPED *> 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_
|
|
@ -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 <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <QtCore/QMutexLocker>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QSocketNotifier>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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 <QtCore/QThread>
|
||||||
|
#include <QtCore/QReadWriteLock>
|
||||||
|
#include <QtCore/QMutexLocker>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QRegExp>
|
||||||
|
#include <QtCore/QMetaType>
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
# include <QtCore/QWinEventNotifier>
|
||||||
|
#else
|
||||||
|
# include <QtCore/private/qwineventnotifier_p.h>
|
||||||
|
#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:"<<GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
winEventNotifier = new QWinEventNotifier(overlap.hEvent, q);
|
||||||
|
qRegisterMetaType<HANDLE>("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:"<<GetLastError();
|
||||||
|
failed = true;
|
||||||
|
if (!CancelIo(newOverlapWrite->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<OVERLAPPED *> 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;
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
INCLUDEPATH += $$PWD
|
INCLUDEPATH += $$PWD
|
||||||
DEPENDPATH += $$PWD
|
DEPENDPATH += $$PWD
|
||||||
|
|
||||||
|
|
||||||
|
include($$PWD/extserial/src/qextserialport.pri)
|
||||||
|
|
||||||
HEADERS += $$PWD/Qss.h
|
HEADERS += $$PWD/Qss.h
|
||||||
|
|
||||||
SOURCES += $$PWD/Qss.cpp
|
SOURCES += $$PWD/Qss.cpp
|
||||||
|
|
Loading…
Reference in New Issue