Commit ae8ffd0c authored by Don Gagne's avatar Don Gagne

Switch Firmware Upgrade to qextserialport

QSerialPort buffering too slow on Windows.
parent ef726502
syntax: glob
*.pro.user*
*.app
*.moc
*.prl
Makefile*
doc/html/
*.framework/
*.xcodeproj/
debug/
release/
qtc-gdbmacros/
*.rej
*.orig
*.obj
*.swp
*.dll
*.exp
*.ilk
*.pdb
*.lib
Thumbs.db
moc_*.cpp
qrc_*.cpp
*.o
*.so.*
*.so
*.pdb
ui_*.h
*~
.qmake.cache
extserialport.prf
lib/*
*.orig
*.exe
*.vcproj
*.vcproj.*.user
*_resource.rc
*.sln
*.idb
*.ncb
*.suo
examples/enumerator/enumerator
examples/event/event
examples/qespta/qespta
examples/uartassistant/uartassistant
object_script.*
Change history for QextSerialPort (formerly QwSerialPort):
(Lines beginning with + represent new functionality, * represent changed or
fixed functionality, - represent removed or deprecated functionality)
Version 1.2 rc (2012 Debao Zhang)
* Build-system refactor
* Issue 145 : Custom baud support for MacOS
* Issue 36 : Fix Devices Notification for Vista
* Issue 54 and Issue 108 : Try detect more ports for windows
* Issue 139 : Adjust the name of generated library
- QextWinEventNotifier has been removed
Version 1.2 beta2 (2012 Debao Zhang)
* Issue 124 : implement canReadLine
* Issue 122 : make Dtr default to TRUE under Windows.
* Issue 127 : fix QObject::MoveToThread brokes SerialPort on Windows
* Issue 129 : Add custom baud support for Windows.
* Issue 131 : Make sure portName returned by QextSerialEnumerator can be used by QextSerialPort
* Issue 134 : Make "make install" really works
* Issue 2: device discovery / removal notification on linux (read config_example.pri to figure out how to enable it.)
Version 1.2 beta1 (2012 Debao Zhang)
* D-pointer and Q_PRIVATE_SLOT are used to moving private members from QextSerialPort to QextSerialPortPrivate
* qdoc3 instead of doxygen is used for generating documents
* MIT license header add to all sources files
+ add a helper class QextWinEventNotifier for windows user, when user's SDK doesnot contain Qt's private files, this class will be auto selected.
Version 1.2win-alpha (2007 Michal Policht)
+ Added QextSerialEnumerator pre-alpha. Works under W2k and later versions of Windows.
+ Event driven mechanism (alternative to polling) is now available on Windows.
- Removed default (=0) parameter from open() functions.
* Fixed bug #1714917 in Win_QextSerialPort::close() method (by Kurt).
* Fixed problem with lack of proper blocking in readData() on win32 (by Brandon Fosdick).
* Removed QT_THREAD_SUPPORT option. Now QextSerialPort must be always compiled with threads support.
* Mutexes are not static.
* setTimeout() now accepts only one parameter.
* bytesAvailable() on POSIX now shows 0 bytes instead of -1 when no bytes are available.
* bytesAvailable() is const.
* native POSIX file descriptors instead of QFile->handle() calls
+ POSIX: Save and restore original termios when opening and closing the device
* POSIX: Only disable special characters on systems that support it
* POSIX: Use cfmakeraw(3) to get a non-canonical termios
+ POSIX: Call close(2) in close() to actually close the device
Version 1.1 (official release)
Version 1.0.1
* Minor changes (mostly in test application)
Version 1.0.0e (by Micha? Policht)
* Fixed bytesAvailable(). Includes buffered bytes to the result.
+ Added isSequential() method.
+ Provided test application
Version 1.0.0d ( changes by Micha? Policht )
- Removed isOpen() overriden declaration/implementation from qextserialport's classes. isOpen() relies on QIODevice now.
- Removed bool portOpen variable. Replaced by internal QIODevice.openMode.
- Removed getChar(), putChar() overriden declaration/implementation. QIODevice can handle this.
* Calling open() with specified OpenMode invokes QIODevice::open() which result in proper openMode setting.
* readData(), writeData() are protected as in QIODevice declaration.
* QIODevice:: read() and write() function are working now (use them instead of readData() writeData()).
* readData(), writeData() don't check if port is open any more (read() and write() assures that). The same behaviour can be found in QFile for example.
* Fixed readLine().
* Fixed randomly crash on deletion bug on Windows ( by Stuart Nixon )
http://lists.trolltech.com/qt-interest/2007-02/thread00340-0.html#msg00351
Version 0.9 (March 3, 2005) Stefan Sander <stefan-sander@users.sf.net>:
+ Added a new precompiler constant, _TTY_FREEBSD_
to support FreeBSD port names.
+ Added _TTY_WIN_ constant in qextserialport.pro win32:DEFINES
to have Windows port names as default when compiling on it.
- Removed construct() call from QextSerialBase constructors,
it is called indirectly through Win_QextSerialPort::construct()
and Posix_QextSerialPort::construct().
+ Added construct() call to Win_QextSerialPort constructors.
+ Added setTimeout(0, 500) call to Win_QextSerialPort::construct().
- Removed setTimeout(0, 500) call from Win_QextSerialPort(const char* name).
* Fixed Posix_QextSerialPort::open(int) control flow, now the port settings
are only applied if the associated file could be opened.
* Fixed masking CR to NL, in Posix_CommConfig.c_iflag
Version 0.8 (, 2003) (Alpha release):
* Added code to set the port timeouts in Win_QextSerialPort's default
constructor.
* Fixed Posix_QextSerialPort::construct() to set up the port correctly.
* Fixed syntax errors in 2 ioctl() calls in posix_QextSerialPort.
* lastError is now initialized to E_NO_ERROR in the QextSerialBase
constructor.
* The select() call in posix_QextSerialPort::bytesWaiting() is now
properly coded. Previously it would always time out.
* Fixed runtime errors in the ioctl() calls for
Posix_QextSerialPort::setDtr() and Posix_QextSerialPort::setRts().
Thanks to Marc Pignat.
Version 0.7 (June 15, 2002) <Bugfix release>:
(0.61 - unofficial release)
* Fixed a small bug in the initializations of the static members when
QT_THREAD_SUPPORT was defined.
* Fixed a bug that caused Borland's compiler to choke on Windows platforms
(which perversely actually stemmed from a shortcoming of Visual C++ that
Borland doesn't have).
(0.62 - unofficial release)
* Fixed a bug that gave Q_LONG the wrong typedef for QT versions prior to
3.0.
(0.63 - unofficial release)
* Fixed 2 incorrect references to Posix_Comm_Config.
* Fixed scoping of Posix_QextSerialPort::operator=().
* Posix_QextSerialPort::construct should now be coded correctly.
* Fixed return type for Posix_QextSerialPort::size().
(0.64 - unofficial release)
* Fixed all the port settings functions to work properly when opening the
port for the first time - previously none of the settings were being
applied when the port was opened.
* Fixed an oversight in Win_QextSerialPort::open() that caused the setting
of port parameters to fail on NT and 2000 systems.
(0.7 - official release)
* Fixed some calls to QextSerialBase constructors that no longer exist on
the POSIX side.
* Fixed the bad memcpy()'s in the POSIX copy constructor.
* Fixed the Offset scoping problem under gcc 2.95.
* The CBAUD flag has been deprecated on some POSIX systems. Fixed
Posix_QextSerialPort::setBaudRate() to reflect this.
* Added construct() calls to all of the Posix_QextSerialPort constructors.
* Fixed double (and conflicting) typedefs of Offset when using QT versions
prior to 3.0
* Changed the call to CreateFile() to CreateFileA() in
Win_QextSerialPort.cpp. This should get rid of problems for those using
Unicode or other multibyte character sets for their string literals.
* A few tweaks to the documentation.
- Removed the protected Posix_Handle variable from Posix_QextSerialPort.
Version 0.6 (March 11, 2002) <Bugfix release>:
+ Added a new precompiler constant, QTVER_PRE_30. QT3 changed the return
types of some QIODevice functions. Therefore, if compiling on versions
of QT prior to 3.0, you should always define QTVER_PRE_30 in your project.
Also had to add some preprocessor blocks to support both 3.0 and earlier
versions of QT.
+ Added implementations of 2 of the new constructors added in 0.5 to both
Win_QextSerialPort and Posix_QextSerialPort.
* The scoping of the enums used in the PortSettings struct has been fixed.
* QObject inheritance has been removed. This should not affect the
functionality of the classes.
* Replaced a few stray references to mutex->unlock() with UNLOCK_MUTEX() in
the Windows code.
* Fixed several runtime errors caused by calling nonexistent members of
QextSerialBase.
* Fixed a whole bunch of little things that were causing MSVC to choke when
compiling for Windows.
Version 0.5 (February 15, 2002):
+ There are 4 new macros (LOCK_MUTEX, UNLOCK_MUTEX, TTY_WARNING, and
TTY_PORTABILITY_WARNING) that replace most of those ugly #ifdef blocks in
the code.
+ In place of the old namingConvention stuff, there is a new function,
setName(). It is used to set the name of the device to be associated with
the object. The new name() function can be used to retrieve the device
name, which is stored in the new member variable portName.
+ There is a new version of open() that takes a const char* as a parameter.
It can be used to specify the name of the device when it is opened rather
than at construction time.
* 3 constructors have been removed and 3 more added. There is now a copy
constructor (and operator=()) as well as a constructor that takes a
PortSettings structure as a parameter, and another that takes both a
device name and a PortSettings structure. As a result of these changes
the PortSettings structure declaration is no longer local to the
QextSerialBase class. All of the removed constructors had to do with
the setNamingConvention() system.
* The static mutex member should now be reference-counted and only deleted
when it is no longer referenced.
* Most of the object construction duties have been pushed back into
QextSerialBase
* Fixed a couple resource leaks, mostly to do with unlocking the mutex
properly
- Removed the setNamingConvention() nonsense.
- Removed all QStrings and calls to sprintf() for thread compatibility.
- Removed setNumber() functions as well as the portNumber member variable,
as they were only necessary under the setNamingConvention() system.
I am grateful to Jorg Preiss (Preisz? Sorry, American keyboards don't have
an ess-tset character ;)) for his invaluable input on most of the changes
that went into this version.
Version 0.4 (March 20, 2001):
+ All of the classes now derive from QObject as well as QIODevice. This
is pretty much useless at the moment - signals and slots may be used
to implement asynchronous communications in a future version
+ Added configurable timeouts via the setTimeout() function. The default
timeout for read and write operations is now 500 milliseconds
+ There is now a functional .pro file for the library (thanks to
Gunnstein Lye)
+ The prefixes for all of the classes have changed from Qw to Qext, in
compliance with the qt-addons project standard
* Fixed a bug that caused port settings to be restored incorrectly when
switching ports with setNumber()
* Minor changes to QextSerialBase::setNumber(). Functionality should now
reflect the documentation, which has also been updated to reflect the
changes that went in on version 0.3.
* Some fixes to the documentation. The Posix_QextSerialPort and
Win_QextSerialPort classes should no longer have any unnecessary
references to inapplicable platforms, and the documentation for open() has
been updated.
* Should now compile without QT_THREAD_SUPPORT defined (ie, in single-
threaded environments), although it will require slight changes to the
makefile (tmake "CONFIG-=thread" should work)
* Fixed a few compilation issues, especially on the POSIX side (should
compile under Linux now :))
* POSIX code is a little cleaner and more efficient
* Various small fixes to the documentation
* Constants now follow a consistent naming convention, with underscores at
the beginning and end of each. For example TTY_POSIX has become
_TTY_POSIX_
Version 0.3 (Feb. 14, 2001):
+ Added a warning that appears when QwSerialPort is compiled on a POSIX
platform that does not implement 76800 baud operation. In this situation
QwSerialPort will also switch to 57600 baud.
+ Major code reorganization - there are now 4 classes instead of 1. This
should remove a lot of the #ifdef...#else...#endif constructs and
hopefully make the code easier to read. Including the class in your
project is still done by including QwSerialPort.h and instantiating a
QwSerialPort object.
* The serial port associated with a QwSerialPort object is no longer
opened on construction, or upon calling the setNumber() function. You
must now explicitly call open() to open the port.
Version 0.2 (Jan. 3, 2001):
+ Added lastError() function with rudimentary error codes
+ Better documentation
+ Added ability to examine the empty/not empty state of a port's input
buffer with atEnd()
+ Added ability to retrieve the number of bytes in a port's input buffer
with size() (thanks to Olivier Tubach)
+ Added ability to turn off portability warnings by defining
TTY_NOWARN_PORT in your project
+ Added ability to turn off all warning messages by defining TTY_NOWARN
in your project
+ Added ability to select POSIX serial functions in Windows NT/2000 by
defining TTY_POSIX in your project (untested)
+ Added control over RTS and DTR lines with setRts() and setDtr()
respectively
+ Added ability to query line status using lineStatus().
+ Added readLine() functionality (thanks to Olivier Tubach)
+ Added bytesWaiting(), a non-const/thread-safe version of size()
+ The class should now be thread-safe through the use of a recursive
QMutex (untested)
* Fixed a bug that could cause hardware flow control not to work on some
POSIX systems
* Put in a few missing fileno() calls in the POSIX code
* Fixed a few syntax errors that caused compilation to fail on POSIX systems
- BAUD0 is no longer a valid baud rate setting - to drop the DTR line,
call setDtr(FALSE)
Version 0.1 (Dec. 11, 2000):
Initial public release.
From QextSerialPort 1.2-beta on, we use MIT license for QextSerialPort project.
== License ==
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
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.
== Why license needed? ==
Many users complains that, without a proper licence they can not use this library.
* http://groups.google.com/group/qextserialport/browse_thread/thread/0e8756920b01da82
Hi,
we are considering using a modified version of QExtSerialPort in one of our
projects (Qt Creator, http://qt.gitorious.org/qt-creator).
Would it be possible to add license header information or a license file to the
QExtSerialPort code base? - This would make re-use of the code base easier.
If that is not possible, could we redistribute the source code with BSD-
license headers manually added?
And
I am also considering packaging the software for Debian, but I
couldn't do it yet just because of the license.
* http://code.google.com/p/qextserialport/issues/detail?id=8
Questions:
Can I use qextserialport in a commercial product?
If yes, how?
Compile it in? I guess no.
If I can use it as a library, how should the README be formulated?
Is the "MIT license" from 2008 appropriate?
== Why can we use MIT? ==
Form the history of [http://lists.trolltech.com/qt-interest/2004-12/msg01022.html qt-interest mail list]
* Wayne Roth, the original author of the project, had said that:
the code is in the public domain. Do whatever you like with it. Right
now I have too many other things to do to put any serious time into
fixing it. Trolltech should be aware of this already; they asked
about a license when they offered to host the tarball.
* Stefan Sander, the maintainer of qextserialport on sourceforge, said that
Hello,
My project registration at !SourceForge have been approved.
http://www.sf.net/projects/qextserialport
I thought an initial licence of Public Domain would be best solution.
Someone wrote: - Because its public domain, some could fork it under different licenses -
And from [http://groups.google.com/group/qextserialport/browse_thread/thread/fbcddbfb4a0b5a51?pli=1 this thread] on qesp mail list, we can see that, current maintainers and users agree with a MIT licence.
* Brandon Fosdick,
I would vote for BSD or MIT :)
* Liam Staskawicz,
That works for me - let's call it MIT and go for it :)
And from [[https://groups.google.com/forum/?fromgroups#!topic/qextserialport/P_5TrNHBICE this other thread]] on the same mailing list:
* Michal Policht,
I agree to license.
= About QextSerialPort =
QextSerialPort provides an interface to old fashioned serial ports for Qt-based applications. It currently supports Mac OS X, Windows, Linux, FreeBSD.
http://code.google.com/p/qextserialport/
== How to use (1) ==
* Download the source code.
* Put the source code in any directory you like. For example, 3rdparty:
|-- project.pro
|-- ....
|-- 3rdparty\
| |-- qextserialport\
| |
* Add following line to your qmake project file:
include(3rdparty/qextserialport/src/qextserialport.pri)
* Using QextSerialPort in your code. Enjoy it!
#include "qextserialport.h"
....
QextSerialPort * port = new QextSerialPort();
....
== How to use (2) ==
It's very easy to compile QextSerialPort directly into your application
(see above section), however, we would prefer to use it as a shared library.
* Download the source code, and put it in any location you like.
* Run following command to generate library.
qmake
make (or nmake)
sudo make install (or nmake install)
* Add following line to your project's file
CONFIG += extserialport
* Using QextSerialPort in your code. Enjoy it!
#include "qextserialport.h"
....
QextSerialPort * port = new QextSerialPort();
....
== Build Documents ==
* Run
qmake
make docs
* Note: More information can be found in doc/readme.txt
== Build examples ==
* Goto examples directory, then run
qmake (or qmake -r)
make (or nmake)
OTHER_FILES += $$PWD/qextserialport.qdocconf
#name of qdoc3 has been changed to qdoc under Qt5
QESP_QDOC = qdoc
lessThan(QT_MAJOR_VERSION, 5):QESP_QDOC = qdoc3
docs_target.target = docs
docs_target.commands = $$QESP_QDOC $$PWD/qextserialport.qdocconf
QMAKE_EXTRA_TARGETS = docs_target
QMAKE_CLEAN += "-r $$PWD/html"
/*!
\example examples/enumerator
\title enumerator Demo
The example demonstrates how to use QextSerialEnumerator.
Include the proper header file
\snippet examples/enumerator/main.cpp 0
Get available ports in the system.
\snippet examples/enumerator/main.cpp 1
Output
\snippet examples/enumerator/main.cpp 2
*/
/*!
\example examples/qespta
\title qespta Demo
The example demonstrates how to use QextSerialPort.
*/
/*!
\example examples/uartassistant
\title UartAssistant Demo
The example demonstrates how to use QextSerialPort.
Initialze UI element.
\snippet examples/uartassistant/dialog.cpp 0
Initialize serial port
\snippet examples/uartassistant/dialog.cpp 1
port Settings
\snippet examples/uartassistant/dialog.cpp 2
Open or Close the port.
\snippet examples/uartassistant/dialog.cpp 3
Read from or Write to the port
\snippet examples/uartassistant/dialog.cpp 4
\image uartassistant.png
*/
/*!
\page index.html
\title QextSerialPort Manual
\section1 Overview
QextSerialPort provides an interface to old fashioned serial ports for
Qt-based applications. It currently supports Mac OS X, Windows, Linux, FreeBSD.
From QextSerialPort 1.2-beta on, license of the project has been changed to MIT.
\list
\o Revision 0.9.x is Qt 2 & 3 compatible.
\o Revision 1.x.x is Qt 4 compatible.
\o From revision 1.2beta1 on, Qt 5 support is added.
\endlist
\section1 Classes
\list
\o \l QextSerialPort encapsulates a serial port on both POSIX and Windows systems.
\o \l QextSerialEnumerator enumerates ports currently available in the system.
\endlist
\section1 Getting Started
\section2 Usage(1): Source Code Only
The package contains a qextserialport.pri file that allows you to integrate the component into programs that use qmake for the build step.
Download the source code.
Put the source code in any directory you like. For example, 3rdparty:
\code
|-- project.pro
|-- ....
|-- 3rdparty\
| |-- qextserialport\
| |
\endcode
Add following line to your qmake project file:
\code
include(pathToPri/qextserialport.pri)
\endcode
Then, using QextSerialPort in your code
\code
#include "qextserialport.h"
...
MyClass::MyClass()
{
port = new QextSerialPort("COM1");
connect(port, SIGNAL(readyRead()), this, SLOT(onDataAvailable()));
port->open();
}
void MyClass::onDataAvailable()
{
QByteArray data = port->readAll();
processNewData(usbdata);
}
\endcode
\section2 Usage(2): shared library
Although QextSerialPort can be directly compiled into your application, You may prefer
to use QextSerailPort as an library, which is very easy too.
1. Download the source code, and put it in any location you like.
2. Goto the top level directory ,run following command to generate library.
\code
qmake
sudo make install (or nmake install)
\endcode
3. Add following line to your project's file
\code
CONFIG += extserialport
\endcode
4. Using QextSerialPort in your code. Enjoy it!
\code
#include "qextserialport.h"
....
QextSerialPort * port = new QextSerialPort();
....
\endcode
\section2 Usage(3): Static library
Someone prefer to use QextSerailPort as static library.
Open the project file: qextserialport.pro, add uncomment follow line
\code
# CONFIG += qesp_static
\endcode
Then follow the same steps as shared library
\code
qmake
sudo make install
\endcode
The static library, the header files, and the feature file will be installed to your system.
Add following line to your qmake's project file:
\code
CONFIG += extserialport
\endcode
\section1 Platform Special
\section2 For MacX: Build as framework
Open the project file: *qextserialport.pro*, and uncomment follow line
\code
# CONFIG += qesp_mac_framework
\endcode
Then follow the same steps as shared library, Goto the top level directory , and run
\code
qmake
sudo make install
\endcode
The framework which includes libraries and the header files, and the feature file will be installed to your system.
Add following line to your qmake's project file:
\code
CONFIG += extserialport
\endcode
\section2 For Linux: Enable udev
Open the project file: *qextserialport.pro*, uncomment follow line
\code
#linux*:CONFIG += qesp_linux_udev
\endcode
Note, If you are using the usage(1), Add following line before include the qextserialport.pri file.
\code
CONFIG += qesp_linux_udev
\endcode
\section2 Build documents
\code
make docs
\endcode
\section1 Examples
\list
\o \l examples/enumerator
\o \l examples/qespta
\o \l examples/uartassistant
\endlist
\section1 Resources
\section2 Nokia(Trolltech)
\list
\o \l {http://doc.trolltech.com/qq/qq12-iodevice.html} {Writing a Custom I/O Device}
\o \l {http://doc.trolltech.com/3.3/qiodevice.html} {Qt 3.3: QIODevice Class Reference}
\o \l {http://doc.trolltech.com/4.7/qiodevice.html} {Qt 4.7: QIODevice Class Reference}
\endlist
\section2 MSDN
\list
\o \l {http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/communications_resources.asp} {Communications Resources}
\o \l {http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/about_communications_resources.asp} {About Communications Resources}
\o \l {http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/using_communications_resources.asp}{Using Communications Resources}
\o \l {http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/communications_functions.asp} {Communications Functions}
\o \l {http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/communications_structures.asp} {Communications Structures}
\endlist
\section2 TLDP
\list
\o \l {http://www.tldp.org/HOWTO/Serial-HOWTO.html}{Serial HOWTO}
\o \l {http://www.tldp.org/HOWTO/Serial-Programming-HOWTO/}{Serial Programming HOWTO}
\endlist
\section2 Other
\list
\o \l {http://www.easysw.com/~mike/serial/serial.html} {Serial Programming Guide for POSIX Operating Systems}
\endlist
*/
/*!
\page classes.html
\generatelist annotatedclasses
*/
# Run qdoc3 from the directory that contains this file.
project = qesp
description = QextSerialPort Reference Documentation
url = http://code.google.com/p/qextserialport
outputencoding = UTF-8
language = Cpp
#Paths are relative to the location of this file
headerdirs = . ../src
sourcedirs = . ../src
exampledirs = ../examples ..
imagedirs = ./examples/images images
Cpp.ignoretokens = QEXTSERIALPORT_EXPORT
indexes = $QTDIR/doc/html/qt.index
qhp.projects = qesp
qhp.qesp.file = qesp.qhp
qhp.qesp.namespace = com.google.code.qextserialport.120
qhp.qesp.virtualFolder = qdoc
qhp.qesp.indexTitle = QextSerialPort Reference Documentation
qhp.qesp.indexRoot =
qhp.qesp.extraFiles = style/style.css
#------------------------------------------------------------------
outputdir = html
outputformats = HTML
headers.fileextensions = "*.h"
sources.fileextensions = "*.cpp *.qdoc"
HTML.templatedir = .
HTML.stylesheets = style/style.css
HTML.headerstyles = " <link rel=\"stylesheet\" type=\"text/css\" href=\"style/style.css\" />\n"
HTML.endheader = "</head>\n"
HTML.postheader = "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n" \
"<tr>\n" \
"<td class=\"postheader\" valign=\"center\">" \
"<a href=\"index.html\">Home</a> &middot;" \
" <a href=\"classes.html\">All Classes</a> &middot;" \
"</td></tr></table>"
HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \
"<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \
"<td width=\"40%\" align=\"left\">Copyright &copy; 2000-2012</td>\n" \
"<td width=\"20%\" align=\"center\"><a href=\"http://code.google.com/p/qextserialport\">QextSerialPort Project</a></td>\n" \
"<td width=\"40%\" align=\"right\"><div align=\"right\">QextSerialPort Manual</div></td>\n" \
"</tr></table></div></address>"
Note:
If you are using qextserialport-XXX.tar.gz, the qesp.qch and
html files have been provided.
Open the file "html/index.html" using your web browser.
Or integrated the "html/qesp.qch" into your QtCreator.
== How to generate help files? ==
Simply run following commands at toplevel directory.
qmake
make docs
Or run the following command at this directory
qdoc3 qextserialport.qdocconf
Then a folder called "html" will be generated.
Open the file "html/index.html" using your web browser.
== How to integrated into Qt Creator or Qt Assistant? ==
Once the html files are generated. run following commands
cd doc/html
qhelpgenerator qesp.qhp
A file called "qesp.qch" will be generated.
For Qt Assistant:
Edit ==> Preferences ==> Documentations ==> Add...
For Qt Creator
Tools ==> Options ==> Help ==> Documentations ==> Add...
a:link, a:visited {
color: #00732F;
text-decoration: none;
font-weight: bold;
}
body {
font: normal 400 14px/1.2 Arial;
margin-top: 85px;
}
h1 {
margin: 0;
}
h2 {
font: 500 20px/1.2 Arial;
}
h3.fn, span.fn {
-moz-border-radius: 7px 7px 7px 7px;
-webkit-border-radius: 7px 7px 7px 7px;
border-radius: 7px 7px 7px 7px;
background-color: #F6F6F6;
border-width: 1px;
border-style: solid;
border-color: #E6E6E6;
word-spacing: 3px;
padding: 3px 5px;
}
table, pre {
-moz-border-radius: 7px 7px 7px 7px;
-webkit-border-radius: 7px 7px 7px 7px;
border-radius: 7px 7px 7px 7px;
background-color: #F6F6F6;
border: 1px solid #E6E6E6;
border-collapse: separate;
font-size: 12px;
line-height: 1.2;
margin-bottom: 25px;
margin-left: 15px;
}
table td {
padding: 3px 15px 3px 20px;
}
table tr.even {
background-color: white;
color: #66666E;
}
table tr.odd {
background-color: #F6F6F6;
color: #66666E;
}
li {
margin-bottom: 10px;
padding-left: 12px;
}
.cpp {
display: block;
margin: 10;
overflow: hidden;
overflow-x: hidden;
overflow-y: hidden;
padding: 20px 0 20px 0;
}
.footer {
margin-top: 50px;
}
.memItemLeft {
padding-right: 3px;
}
.memItemRight {
padding: 3px 15px 3px 0;
}
.qml {
display: block;
margin: 10;
overflow: hidden;
overflow-x: hidden;
overflow-y: hidden;
padding: 20px 0 20px 0;
}
.qmldefault {
padding-left: 5px;
float: right;
color: red;
}
.qmlreadonly {
padding-left: 5px;
float: right;
color: #254117;
}
.rightAlign {
padding: 3px 5px 3px 10px;
text-align: right;
}
.title {
background-color: white;
color: #44A51C;
font-family: Verdana;
font-size: 35px;
font-weight: normal;
left: 0;
padding-bottom: 5px;
padding-left: 16px;
padding-top: 20px;
position: absolute;
right: 0;
top: 0;
}
.toc {
float: right;
-moz-border-radius: 7px 7px 7px 7px;
-webkit-border-radius: 7px 7px 7px 7px;
border-radius: 7px 7px 7px 7px;
background-color: #F6F6F6;
border: 1px solid #DDD;
margin: 0 20px 10px 10px;
padding: 20px 15px 20px 20px;
height: auto;
width: 200px;
}
TEMPLATE = app
DEPENDPATH += .
CONFIG += console
include(../../src/qextserialport.pri)
SOURCES += main.cpp
/**
* @file main.cpp
* @brief Main file.
* @author Micha? Policht
*/
//! [0]
#include "qextserialenumerator.h"
//! [0]
#include <QtCore/QList>
#include <QtCore/QDebug>
int main()
{
//! [1]
QList<QextPortInfo> ports = QextSerialEnumerator::getPorts();
//! [1]
qDebug() << "List of ports:";
//! [2]
foreach (QextPortInfo info, ports) {
qDebug() << "port name:" << info.portName;
qDebug() << "friendly name:" << info.friendName;
qDebug() << "physical name:" << info.physName;
qDebug() << "enumerator name:" << info.enumName;
qDebug() << "vendor ID:" << info.vendorID;
qDebug() << "product ID:" << info.productID;
qDebug() << "===================================";
}
//! [2]
return 0;
}
#include "PortListener.h"
#include <QtDebug>
PortListener::PortListener(const QString &portName)
{
qDebug() << "hi there";
this->port = new QextSerialPort(portName, QextSerialPort::EventDriven);
port->setBaudRate(BAUD9600);
port->setFlowControl(FLOW_OFF);
port->setParity(PAR_NONE);
port->setDataBits(DATA_8);
port->setStopBits(STOP_2);
if (port->open(QIODevice::ReadWrite) == true) {
connect(port, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(port, SIGNAL(dsrChanged(bool)), this, SLOT(onDsrChanged(bool)));
if (!(port->lineStatus() & LS_DSR))
qDebug() << "warning: device is not turned on";
qDebug() << "listening for data on" << port->portName();
}
else {
qDebug() << "device failed to open:" << port->errorString();
}
}
void PortListener::onReadyRead()
{
QByteArray bytes;
int a = port->bytesAvailable();
bytes.resize(a);
port->read(bytes.data(), bytes.size());
qDebug() << "bytes read:" << bytes.size();
qDebug() << "bytes:" << bytes;
}
void PortListener::onDsrChanged(bool status)
{
if (status)
qDebug() << "device was turned on";
else
qDebug() << "device was turned off";
}
#ifndef PORTLISTENER_H_
#define PORTLISTENER_H_
#include <QObject>
#include "qextserialport.h"
class PortListener : public QObject
{
Q_OBJECT
public:
PortListener(const QString &portName);
private:
QextSerialPort *port;
private slots:
void onReadyRead();
void onDsrChanged(bool status);
};
#endif /*PORTLISTENER_H_*/
TEMPLATE = app
DEPENDPATH += .
CONFIG += console
include(../../src/qextserialport.pri)
SOURCES += main.cpp PortListener.cpp
HEADERS += PortListener.h
/**
* @file main.cpp
* @brief Main file.
* @author Michal Policht
*/
#include <QCoreApplication>
#include "PortListener.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QString portName = QLatin1String("COM1"); // update this to use your port of choice
PortListener listener(portName); // signals get hooked up internally
// start the event loop and wait for signals
return app.exec();
}
TEMPLATE = subdirs
SUBDIRS = qespta enumerator \
uartassistant
win32:SUBDIRS += event
/**
* @file MainWindow.cpp
* @brief MainWindow Implementation.
* @see MainWindow.h
* @author Micha? Policht
*/
#include <QMessageBox>
#include <QMenuBar>
#include "MainWindow.h"
#include "MessageWindow.h"
#include "QespTest.h"
MainWindow::MainWindow()
{
//central widget
QespTest *qespTest = new QespTest();
setCentralWidget(qespTest);
//bottom dock widget
MessageWindow *msgWindow = new MessageWindow();
addDockWidget(Qt::BottomDockWidgetArea, msgWindow);
createActions();
createMenus();
setWindowTitle(tr("QextSerialPort Test Application"));
}
void MainWindow::about()
{
QMessageBox::about(this, tr("About "),
tr("<B>""</B><BR>"
"author: Michal Policht<br>"
"<a href='mailto:xpolik@users.sourceforge.net'>xpolik@users.sourceforge.net</a>"));
}
void MainWindow::createActions()
{
//File actions
exitAct = new QAction(tr("E&xit"), this);
exitAct->setShortcut(tr("CTRL+D"));
exitAct->setStatusTip(tr("Exit the application"));
connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
//Help actions
aboutAct = new QAction(tr("&About"), this);
aboutAct->setShortcut(tr("CTRL+A"));
aboutAct->setStatusTip(tr("About application"));
connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
}
void MainWindow::createMenus()
{
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(exitAct);
helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(aboutAct);
}
/**
* @file MainWindow.h
* @brief Application's Main Window.
* @see MainWindow
* @author Micha? Policht
*/
#ifndef MAINWINDOW_H_
#define MAINWINDOW_H_
#include <QMainWindow>
class QMenu;
class QAction;
class MainWindow : public QMainWindow
{
Q_OBJECT
QMenu *fileMenu;
QAction *exitAct;
QMenu *helpMenu;
QAction *aboutAct;
private:
void createMenus();
void createActions();
private slots:
void about();
public:
MainWindow();
};
#endif /*MAINWINDOW_H_*/
/**
* @file MessageWindow.cpp
* @brief MessageWindow Implementation.
* @see MessageWindow.h
* @author Micha? Policht
*/
#include <stdio.h>
#include "MessageWindow.h"
#include <QMessageBox>
#include <QCoreApplication>
#include <QMutexLocker>
const char *MessageWindow::WINDOW_TITLE = "Message Window";
MessageWindow *MessageWindow::MsgHandler = NULL;
MessageWindow::MessageWindow(QWidget *parent, Qt::WindowFlags flags)
: QDockWidget(parent, flags),
msgTextEdit(this)
{
setWindowTitle(tr(WINDOW_TITLE));
msgTextEdit.setReadOnly(true);
setWidget(&msgTextEdit);
MessageWindow::MsgHandler = this;
}
//static
QString MessageWindow::QtMsgToQString(QtMsgType type, const char *msg)
{
switch (type) {
case QtDebugMsg:
return QLatin1String("Debug: ")+QLatin1String(msg);
case QtWarningMsg:
return QLatin1String("Warning: ")+QLatin1String(msg);
case QtCriticalMsg:
return QLatin1String("Critical: ")+QLatin1String(msg);
case QtFatalMsg:
return QLatin1String("Fatal: ")+QLatin1String(msg);
default:
return QLatin1String("Unrecognized message type: ")+QLatin1String(msg);
}
}
//static
void MessageWindow::AppendMsgWrapper(QtMsgType type, const char *msg)
{
static QMutex mutex;
QMutexLocker locker(&mutex);
if (MessageWindow::MsgHandler != NULL)
return MessageWindow::MsgHandler->postMsgEvent(type, msg);
else
fprintf(stderr, "%s", MessageWindow::QtMsgToQString(type, msg).toLatin1().data());
}
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
void MessageWindow::AppendMsgWrapper(QtMsgType type, const QMessageLogContext & /*context*/, const QString &msg)
{
AppendMsgWrapper(type, msg.toLatin1().data());
}
#endif
void MessageWindow::customEvent(QEvent *event)
{
if (static_cast<MessageWindow::EventType>(event->type()) == MessageWindow::MessageEventType)
msgTextEdit.append(dynamic_cast<MessageEvent *>(event)->msg);
}
void MessageWindow::postMsgEvent(QtMsgType type, const char *msg)
{
QString qmsg = MessageWindow::QtMsgToQString(type, msg);
switch (type) {
case QtDebugMsg:
break;
case QtWarningMsg:
qmsg.prepend(QLatin1String("<FONT color=\"#FF0000\">"));
qmsg.append(QLatin1String("</FONT>"));
break;
case QtCriticalMsg:
if (QMessageBox::critical(this, QLatin1String("Critical Error"), qmsg,
QMessageBox::Ignore,
QMessageBox::Abort,
QMessageBox::NoButton) == QMessageBox::Abort)
abort(); // core dump
qmsg.prepend(QLatin1String("<B><FONT color=\"#FF0000\">"));
qmsg.append(QLatin1String("</FONT></B>"));
break;
case QtFatalMsg:
QMessageBox::critical(this, QLatin1String("Fatal Error"), qmsg, QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton);
abort(); // deliberately core dump
}
//it's impossible to change GUI directly from thread other than the main thread
//so post message encapsulated by MessageEvent to the main thread's event queue
QCoreApplication::postEvent(this, new MessageEvent(qmsg));
}
MessageEvent::MessageEvent(QString &msg):
QEvent(static_cast<QEvent::Type>(MessageWindow::MessageEventType))
{
this->msg = msg;
}
/**
* @file MessageWindow.h
* @brief Message Window.
* @see MessageWindow
* @author Micha? Policht
*/
#ifndef MESSAGEWINDOW_H_
#define MESSAGEWINDOW_H_
#include <QDockWidget>
#include <QTextEdit>
#include <QEvent>
/**
* Message Window. Handling errors and other messages.
*/
class MessageWindow: public QDockWidget
{
Q_OBJECT
QTextEdit msgTextEdit; ///< Main widget.
static MessageWindow *MsgHandler; ///< Set in constructor.
static const char *WINDOW_TITLE; ///< Window title.
private:
static QString QtMsgToQString(QtMsgType type, const char *msg);
protected:
/**
* Handle custom events. MessageWindow hadles custom events listed in
* EventType enum.
*/
virtual void customEvent(QEvent* event);
public:
enum EventType {MessageEventType = QEvent::User}; ///< Custom event types.
/**
* Default constructor.
* @param parent parent widget.
* @param flags widget flags.
*/
MessageWindow(QWidget* parent = 0, Qt::WindowFlags flags = 0);
/**
* Append message wrapper. Since ISO forbids casting member functions
* to C functions, wrapper is needed to use this class as QtMsgHandler.
* This method is thread-safe but not reentrant.
* @param type message type.
* @param msg message string.
*/
static void AppendMsgWrapper(QtMsgType type, const char *msg);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
static void AppendMsgWrapper(QtMsgType type, const QMessageLogContext &context, const QString &msg);
#endif
/**
* Post message event to the main event loop. This function encapsulates
* message into MessageEvent object and passes it to the main event loop.
* @param type message type.
* @param msg message string.
*/
void postMsgEvent(QtMsgType type, const char *msg);
};
/**
* Message Event. Custom event used by @ref MessageWindow to provide multi-threaded
* access. Encapsulates message inside @a msg variable.
*/
class MessageEvent: public QEvent
{
public:
QString msg; ///< Message string.
/**
* Contructor.
* @param msg message to post.
*/
MessageEvent(QString &msg);
};
#endif /*MESSAGEWINDOW_H_*/
/* QespTest.cpp
**************************************/
#include "QespTest.h"
#include "qextserialport.h"
#include <QLayout>
#include <QLineEdit>
#include <QTextEdit>
#include <QPushButton>
#include <QSpinBox>
QespTest::QespTest(QWidget *parent)
: QWidget(parent)
{
//modify the port settings on your own
#ifdef Q_OS_UNIX
port = new QextSerialPort(QLatin1String("/dev/ttyS0"), QextSerialPort::Polling);
#else
port = new QextSerialPort(QLatin1String("COM1"), QextSerialPort::Polling);
#endif /*Q_OS_UNIX*/
port->setBaudRate(BAUD19200);
port->setFlowControl(FLOW_OFF);
port->setParity(PAR_NONE);
port->setDataBits(DATA_8);
port->setStopBits(STOP_2);
//set timeouts to 500 ms
port->setTimeout(500);
message = new QLineEdit(this);
// transmit receive
QPushButton *transmitButton = new QPushButton(tr("Transmit"));
connect(transmitButton, SIGNAL(clicked()), SLOT(transmitMsg()));
QPushButton *receiveButton = new QPushButton(tr("Receive"));
connect(receiveButton, SIGNAL(clicked()), SLOT(receiveMsg()));
QHBoxLayout *trLayout = new QHBoxLayout;
trLayout->addWidget(transmitButton);
trLayout->addWidget(receiveButton);
//CR LF
QPushButton *CRButton = new QPushButton(tr("CR"));
connect(CRButton, SIGNAL(clicked()), SLOT(appendCR()));
QPushButton *LFButton = new QPushButton(tr("LF"));
connect(LFButton, SIGNAL(clicked()), SLOT(appendLF()));
QHBoxLayout *crlfLayout = new QHBoxLayout;
crlfLayout->addWidget(CRButton);
crlfLayout->addWidget(LFButton);
//open close
QPushButton *openButton = new QPushButton(tr("Open"));
connect(openButton, SIGNAL(clicked()), SLOT(openPort()));
QPushButton *closeButton = new QPushButton(tr("Close"));
connect(closeButton, SIGNAL(clicked()), SLOT(closePort()));
QHBoxLayout *ocLayout = new QHBoxLayout;
ocLayout->addWidget(openButton);
ocLayout->addWidget(closeButton);
received_msg = new QTextEdit();
QVBoxLayout *myVBox = new QVBoxLayout;
myVBox->addWidget(message);
myVBox->addLayout(crlfLayout);
myVBox->addLayout(trLayout);
myVBox->addLayout(ocLayout);
myVBox->addWidget(received_msg);
setLayout(myVBox);
qDebug("isOpen : %d", port->isOpen());
}
QespTest::~QespTest()
{
delete port;
port = NULL;
}
void QespTest::transmitMsg()
{
int i = port->write(message->text().toLatin1());
qDebug("trasmitted : %d", i);
}
void QespTest::receiveMsg()
{
char buff[1024];
int numBytes;
numBytes = port->bytesAvailable();
if(numBytes > 1024)
numBytes = 1024;
int i = port->read(buff, numBytes);
if (i != -1)
buff[i] = '\0';
else
buff[0] = '\0';
QString msg = QLatin1String(buff);
received_msg->append(msg);
received_msg->ensureCursorVisible();
qDebug("bytes available: %d", numBytes);
qDebug("received: %d", i);
}
void QespTest::appendCR()
{
message->insert(QLatin1String("\x0D"));
}
void QespTest::appendLF()
{
message->insert(QLatin1String("\x0A"));
}
void QespTest::closePort()
{
port->close();
qDebug("is open: %d", port->isOpen());
}
void QespTest::openPort()
{
port->open(QIODevice::ReadWrite | QIODevice::Unbuffered);
qDebug("is open: %d", port->isOpen());
}
/* qesptest.h
**************************************/
#ifndef _QESPTEST_H_
#define _QESPTEST_H_
#include <QWidget>
class QLineEdit;
class QTextEdit;
class QextSerialPort;
class QSpinBox;
class QespTest : public QWidget
{
Q_OBJECT
public:
QespTest(QWidget *parent=0);
virtual ~QespTest();
private:
QLineEdit *message;
QSpinBox *delaySpinBox;
QTextEdit *received_msg;
QextSerialPort *port;
private slots:
void transmitMsg();
void receiveMsg();
void appendCR();
void appendLF();
void closePort();
void openPort();
};
#endif
This is simple application using QextSerialPort library.
Port settings are in QespTest constructor (QespTest.cpp)
/**
* @file main.cpp
* @brief Main file.
* @author Micha? Policht
*/
#include <QApplication>
#include "MainWindow.h"
#include "MessageWindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
//! [0]
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
//redirect debug messages to the MessageWindow dialog
qInstallMsgHandler(MessageWindow::AppendMsgWrapper);
#else
qInstallMessageHandler(MessageWindow::AppendMsgWrapper);
#endif
//! [0]
MainWindow mainWindow;
mainWindow.show();
return app.exec();
}
TEMPLATE = app
DEPENDPATH += .
QT += core gui
contains(QT_VERSION, ^5\\..*\\..*): QT += widgets
HEADERS += MainWindow.h \
MessageWindow.h \
QespTest.h
SOURCES += main.cpp \
MainWindow.cpp \
MessageWindow.cpp \
QespTest.cpp
include(../../src/qextserialport.pri)
#include "qextserialport.h"
#include "qextserialenumerator.h"
#include "dialog.h"
#include "ui_dialog.h"
#include <QtCore>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
//! [0]
foreach (QextPortInfo info, QextSerialEnumerator::getPorts())
ui->portBox->addItem(info.portName);
//make sure user can input their own port name!
ui->portBox->setEditable(true);
ui->baudRateBox->addItem("1200", BAUD1200);
ui->baudRateBox->addItem("2400", BAUD2400);
ui->baudRateBox->addItem("4800", BAUD4800);
ui->baudRateBox->addItem("9600", BAUD9600);
ui->baudRateBox->addItem("19200", BAUD19200);
ui->baudRateBox->setCurrentIndex(3);
ui->parityBox->addItem("NONE", PAR_NONE);
ui->parityBox->addItem("ODD", PAR_ODD);
ui->parityBox->addItem("EVEN", PAR_EVEN);
ui->dataBitsBox->addItem("5", DATA_5);
ui->dataBitsBox->addItem("6", DATA_6);
ui->dataBitsBox->addItem("7", DATA_7);
ui->dataBitsBox->addItem("8", DATA_8);
ui->dataBitsBox->setCurrentIndex(3);
ui->stopBitsBox->addItem("1", STOP_1);
ui->stopBitsBox->addItem("2", STOP_2);
ui->queryModeBox->addItem("Polling", QextSerialPort::Polling);
ui->queryModeBox->addItem("EventDriven", QextSerialPort::EventDriven);
//! [0]
ui->led->turnOff();
timer = new QTimer(this);
timer->setInterval(40);
//! [1]
PortSettings settings = {BAUD9600, DATA_8, PAR_NONE, STOP_1, FLOW_OFF, 10};
port = new QextSerialPort(ui->portBox->currentText(), settings, QextSerialPort::Polling);
//! [1]
enumerator = new QextSerialEnumerator(this);
enumerator->setUpNotifications();
connect(ui->baudRateBox, SIGNAL(currentIndexChanged(int)), SLOT(onBaudRateChanged(int)));
connect(ui->parityBox, SIGNAL(currentIndexChanged(int)), SLOT(onParityChanged(int)));
connect(ui->dataBitsBox, SIGNAL(currentIndexChanged(int)), SLOT(onDataBitsChanged(int)));
connect(ui->stopBitsBox, SIGNAL(currentIndexChanged(int)), SLOT(onStopBitsChanged(int)));
connect(ui->queryModeBox, SIGNAL(currentIndexChanged(int)), SLOT(onQueryModeChanged(int)));
connect(ui->timeoutBox, SIGNAL(valueChanged(int)), SLOT(onTimeoutChanged(int)));
connect(ui->portBox, SIGNAL(editTextChanged(QString)), SLOT(onPortNameChanged(QString)));
connect(ui->openCloseButton, SIGNAL(clicked()), SLOT(onOpenCloseButtonClicked()));
connect(ui->sendButton, SIGNAL(clicked()), SLOT(onSendButtonClicked()));
connect(timer, SIGNAL(timeout()), SLOT(onReadyRead()));
connect(port, SIGNAL(readyRead()), SLOT(onReadyRead()));
connect(enumerator, SIGNAL(deviceDiscovered(QextPortInfo)), SLOT(onPortAddedOrRemoved()));
connect(enumerator, SIGNAL(deviceRemoved(QextPortInfo)), SLOT(onPortAddedOrRemoved()));
setWindowTitle(tr("QextSerialPort Demo"));
}
Dialog::~Dialog()
{
delete ui;
delete port;
}
void Dialog::changeEvent(QEvent *e)
{
QDialog::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
void Dialog::onPortNameChanged(const QString & /*name*/)
{
if (port->isOpen()) {
port->close();
ui->led->turnOff();
}
}
//! [2]
void Dialog::onBaudRateChanged(int idx)
{
port->setBaudRate((BaudRateType)ui->baudRateBox->itemData(idx).toInt());
}
void Dialog::onParityChanged(int idx)
{
port->setParity((ParityType)ui->parityBox->itemData(idx).toInt());
}
void Dialog::onDataBitsChanged(int idx)
{
port->setDataBits((DataBitsType)ui->dataBitsBox->itemData(idx).toInt());
}
void Dialog::onStopBitsChanged(int idx)
{
port->setStopBits((StopBitsType)ui->stopBitsBox->itemData(idx).toInt());
}
void Dialog::onQueryModeChanged(int idx)
{
port->setQueryMode((QextSerialPort::QueryMode)ui->queryModeBox->itemData(idx).toInt());
}
void Dialog::onTimeoutChanged(int val)
{
port->setTimeout(val);
}
//! [2]
//! [3]
void Dialog::onOpenCloseButtonClicked()
{
if (!port->isOpen()) {
port->setPortName(ui->portBox->currentText());
port->open(QIODevice::ReadWrite);
}
else {
port->close();
}
//If using polling mode, we need a QTimer
if (port->isOpen() && port->queryMode() == QextSerialPort::Polling)
timer->start();
else
timer->stop();
//update led's status
ui->led->turnOn(port->isOpen());
}
//! [3]
//! [4]
void Dialog::onSendButtonClicked()
{
if (port->isOpen() && !ui->sendEdit->toPlainText().isEmpty())
port->write(ui->sendEdit->toPlainText().toLatin1());
}
void Dialog::onReadyRead()
{
if (port->bytesAvailable()) {
ui->recvEdit->moveCursor(QTextCursor::End);
ui->recvEdit->insertPlainText(QString::fromLatin1(port->readAll()));
}
}
void Dialog::onPortAddedOrRemoved()
{
QString current = ui->portBox->currentText();
ui->portBox->blockSignals(true);
ui->portBox->clear();
foreach (QextPortInfo info, QextSerialEnumerator::getPorts())
ui->portBox->addItem(info.portName);
ui->portBox->setCurrentIndex(ui->portBox->findText(current));
ui->portBox->blockSignals(false);
}
//! [4]
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
namespace Ui {
class Dialog;
}
class QTimer;
class QextSerialPort;
class QextSerialEnumerator;
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
protected:
void changeEvent(QEvent *e);
private Q_SLOTS:
void onPortNameChanged(const QString &name);
void onBaudRateChanged(int idx);
void onParityChanged(int idx);
void onDataBitsChanged(int idx);
void onStopBitsChanged(int idx);
void onQueryModeChanged(int idx);
void onTimeoutChanged(int val);
void onOpenCloseButtonClicked();
void onSendButtonClicked();
void onReadyRead();
void onPortAddedOrRemoved();
private:
Ui::Dialog *ui;
QTimer *timer;
QextSerialPort *port;
QextSerialEnumerator *enumerator;
};
#endif // DIALOG_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>604</width>
<height>485</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="3,1">
<item>
<widget class="QPlainTextEdit" name="recvEdit">
<property name="maximumBlockCount">
<number>800</number>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="sendEdit"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="portBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>BaudRate:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="baudRateBox"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>DataBits:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="dataBitsBox"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Parity:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="parityBox"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>StopBits:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="stopBitsBox"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>QueryMode:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="queryModeBox"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Timeout:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="timeoutBox">
<property name="suffix">
<string> ms</string>
</property>
<property name="minimum">
<number>-1</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="HLed" name="led" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="openCloseButton">
<property name="text">
<string>Open/Close</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="sendButton">
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>HLed</class>
<extends>QWidget</extends>
<header>hled.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
#include <QtGui>
#include "hled.h"
struct HLed::Private
{
public:
Private()
: darkerFactor(300), color(Qt::green), isOn(true)
{ }
int darkerFactor;
QColor color;
bool isOn;
};
HLed::HLed(QWidget *parent)
:QWidget(parent), m_d(new Private)
{
}
HLed::~HLed()
{
delete m_d;
}
QColor HLed::color() const
{
return m_d->color;
}
void HLed::setColor(const QColor &color)
{
if (m_d->color == color)
return;
update();
}
QSize HLed::sizeHint() const
{
return QSize(20, 20);
}
QSize HLed::minimumSizeHint() const
{
return QSize(16, 16);
}
void HLed::toggle()
{
m_d->isOn = !m_d->isOn;
update();
}
void HLed::turnOn(bool on)
{
m_d->isOn = on;
update();
}
void HLed::turnOff(bool off)
{
turnOn(!off);
}
void HLed::paintEvent(QPaintEvent * /*event*/)
{
int width = ledWidth();
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QColor color = m_d->isOn ? m_d->color
: m_d->color.darker(m_d->darkerFactor);
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(color);
painter.setBrush(brush);
// draw plain
painter.drawEllipse(1, 1, width-1, width-1);
QPen pen;
pen.setWidth(2);
int pos = width / 5 + 1;
int lightWidth = width * 2 / 3;
int lightQuote = 130 * 2 / (lightWidth ? lightWidth : 1) + 100;
// draw bright spot
while (lightWidth) {
color = color.lighter(lightQuote);
pen.setColor(color);
painter.setPen(pen);
painter.drawEllipse(pos, pos, lightWidth, lightWidth);
lightWidth--;
if (!lightWidth)
break;
painter.drawEllipse(pos, pos, lightWidth, lightWidth);
lightWidth--;
if (!lightWidth)
break;
painter.drawEllipse(pos, pos, lightWidth, lightWidth);
pos++;
lightWidth--;
}
//draw border
painter.setBrush(Qt::NoBrush);
int angle = -720;
color = palette().color(QPalette::Light);
for (int arc=120; arc<2880; arc+=240) {
pen.setColor(color);
painter.setPen(pen);
int w = width - pen.width()/2;
painter.drawArc(pen.width()/2, pen.width()/2, w, w, angle+arc, 240);
painter.drawArc(pen.width()/2, pen.width()/2, w, w, angle-arc, 240);
color = color.darker(110);
}
}
int HLed::ledWidth() const
{
int width = qMin(this->width(), this->height());
width -= 2;
return width > 0 ? width : 0;
}
#ifndef HLED_H
#define HLED_H
#include <QWidget>
class QColor;
class HLed : public QWidget
{
Q_OBJECT
public:
HLed(QWidget *parent = 0);
~HLed();
QColor color() const;
QSize sizeHint() const;
QSize minimumSizeHint() const;
public slots:
void setColor(const QColor &color);
void toggle();
void turnOn(bool on=true);
void turnOff(bool off=true);
protected:
void paintEvent(QPaintEvent *);
int ledWidth() const;
private:
struct Private;
Private * const m_d;
};
#endif // HLED_H
#include <QApplication>
#include "dialog.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
#-------------------------------------------------
#
# Project created by QtCreator 2011-11-06T21:37:41
#
#-------------------------------------------------
QT += core gui
contains(QT_VERSION, ^5\\..*\\..*): QT += widgets
TARGET = uartassistant
TEMPLATE = app
include(../../src/qextserialport.pri)
SOURCES += main.cpp\
dialog.cpp\
hled.cpp
HEADERS += dialog.h \
hled.h
FORMS += dialog.ui
defineReplace(qextLibraryName) {
unset(LIBRARY_NAME)
LIBRARY_NAME = \$\$1
greaterThan(QT_MAJOR_VERSION, 4):LIBRARY_NAME ~= s,^Qt,Qt\$\$QT_MAJOR_VERSION,
CONFIG(debug, debug|release) {
!debug_and_release|build_pass {
mac:LIBRARY_NAME = \$\${LIBRARY_NAME}_debug
else:win32:LIBRARY_NAME = \$\${LIBRARY_NAME}d
}
}
return(\$\$LIBRARY_NAME)
}
!!IF qesp_mac_framework
LIBS += -framework $$QESP_LIB_BASENAME
INCLUDEPATH += $$[QT_INSTALL_LIBS]/$${QESP_LIB_BASENAME}.framework/Headers
!!ELSE
LIBS += -l\$\$qextLibraryName($$QESP_LIB_BASENAME)
INCLUDEPATH += $$[QT_INSTALL_HEADERS]/QtExtSerialPort
!!ENDIF
!!IF !qesp_static
DEFINES += QEXTSERIALPORT_USING_SHARED
!!ENDIF
############################### *User Config* ###############################
# Uncomment following line if you want to build a static library
# CONFIG += qesp_static
# Uncomment following line if you want to build framework for mac
# macx:CONFIG += qesp_mac_framework
# Uncomment following line if you want to enable udev for linux
# linux*:CONFIG += qesp_linux_udev
# Note: you can create a ".qmake.cache" file, then copy these lines to it.
# If so, you can avoid to change this project file.
############################### *User Config* ###############################
defineReplace(qextLibraryName) {
unset(LIBRARY_NAME)
LIBRARY_NAME = $$1
macx:qesp_mac_framework {
QMAKE_FRAMEWORK_BUNDLE_NAME = $$LIBRARY_NAME
export(QMAKE_FRAMEWORK_BUNDLE_NAME)
} else {
greaterThan(QT_MAJOR_VERSION, 4):LIBRARY_NAME ~= s,^Qt,Qt$$QT_MAJOR_VERSION,
}
CONFIG(debug, debug|release) {
!debug_and_release|build_pass {
mac:LIBRARY_NAME = $${LIBRARY_NAME}_debug
else:win32:LIBRARY_NAME = $${LIBRARY_NAME}d
}
}
return($$LIBRARY_NAME)
}
TEMPLATE=lib
include(src/qextserialport.pri)
#create_prl is needed, otherwise, MinGW can't found libqextserialport1.a
CONFIG += create_prl
#mac framework is designed for shared library
macx:qesp_mac_framework:qesp_static: CONFIG -= qesp_static
!macx:qesp_mac_framework:CONFIG -= qesp_mac_framework
qesp_static {
CONFIG += static
} else {
CONFIG += shared
macx:!qesp_mac_framework:CONFIG += absolute_library_soname
DEFINES += QEXTSERIALPORT_BUILD_SHARED
}
#Creare lib bundle for mac
macx:qesp_mac_framework {
CONFIG += lib_bundle
FRAMEWORK_HEADERS.files = $$PUBLIC_HEADERS
FRAMEWORK_HEADERS.path = Headers
QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS
}
win32|mac:!wince*:!win32-msvc:!macx-xcode:CONFIG += debug_and_release build_all
#For non-windows system, only depends on QtCore module
unix:QT = core
else:QT = core gui
#generate proper library name
greaterThan(QT_MAJOR_VERSION, 4) {
QESP_LIB_BASENAME = QtExtSerialPort
} else {
QESP_LIB_BASENAME = qextserialport
}
TARGET = $$qextLibraryName($$QESP_LIB_BASENAME)
VERSION = 1.2.0
# generate feature file by qmake based on this *.in file.
QMAKE_SUBSTITUTES += extserialport.prf.in
OTHER_FILES += extserialport.prf.in
# for make docs
include(doc/doc.pri)
# for make install
win32:!qesp_static {
dlltarget.path = $$[QT_INSTALL_BINS]
INSTALLS += dlltarget
}
!macx|!qesp_mac_framework {
headers.files = $$PUBLIC_HEADERS
headers.path = $$[QT_INSTALL_HEADERS]/QtExtSerialPort
INSTALLS += headers
}
target.path = $$[QT_INSTALL_LIBS]
features.files = extserialport.prf
features.path = $$[QMAKE_MKSPECS]/features
INSTALLS += target features
/****************************************************************************
** 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)
{
platformSpecificInit();
}
QextSerialEnumeratorPrivate::~QextSerialEnumeratorPrivate()
{
platformSpecificDestruct();
}
/*!
\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"
/****************************************************************************
** 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_*/
/****************************************************************************
** 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::platformSpecificInit()
{
#ifndef QESP_NO_UDEV
monitor = NULL;
notifierFd = -1;
notifier = NULL;
udev = udev_new();
if (!udev)
qCritical() << "Unable to initialize udev notifications";
#endif
}
void QextSerialEnumeratorPrivate::platformSpecificDestruct()
{
#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));
udev_device_unref(dev);
if (action == QLatin1String("add"))
Q_EMIT q->deviceDiscovered(pi);
else if (action == QLatin1String("remove"))
Q_EMIT q->deviceRemoved(pi);
}
}
#endif
/****************************************************************************
** 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::platformSpecificInit()
{
}
void QextSerialEnumeratorPrivate::platformSpecificDestruct()
{
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;
}
/****************************************************************************
** 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_OS_WIN
// 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
# ifdef __MINGW32__
# define _WIN32_WINNT 0x0500
# define _WIN32_WINDOWS 0x0500
# define WINVER 0x0500
# endif
# 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 platformSpecificInit();
void platformSpecificDestruct();
static QList<QextPortInfo> getPorts_sys();
bool setUpNotifications_sys(bool setup);
#ifdef Q_OS_WIN
LRESULT onDeviceChanged(WPARAM wParam, LPARAM lParam);
bool matchAndDispatchChangedDevice(const QString &deviceID, const GUID &guid, WPARAM wParam);
# ifdef QT_GUI_LIB
QextSerialRegistrationWidget *notificationWidget;
# endif
#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_
/****************************************************************************
** 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::platformSpecificInit()
{
}
void QextSerialEnumeratorPrivate::platformSpecificDestruct()
{
}
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;
}
/****************************************************************************
** 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 <objbase.h>
#include <initguid.h>
#include <setupapi.h>
#include <dbt.h>
#include "qextserialport.h"
#ifdef QT_GUI_LIB
#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::platformSpecificInit()
{
#ifdef QT_GUI_LIB
notificationWidget = 0;
#endif // QT_GUI_LIB
}
/*!
default
*/
void QextSerialEnumeratorPrivate::platformSpecificDestruct()
{
#ifdef QT_GUI_LIB
if (notificationWidget)
delete notificationWidget;
#endif
}
// see http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426(v=vs.85).aspx
// for list of GUID classes
const GUID deviceClassGuids[] =
{
// Ports (COM & LPT ports), Class = Ports
{0x4D36E978, 0xE325, 0x11CE, {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18}},
// Modem, Class = Modem
{0x4D36E96D, 0xE325, 0x11CE, {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18}},
// Bluetooth Devices, Class = Bluetooth
{0xE0CBF06C, 0xCD8B, 0x4647, {0xBB, 0x8A, 0x26, 0x3B, 0x43, 0xF0, 0xF9, 0x74}},
// Added by Arne Kristian Jansen, for use with com0com virtual ports (See Issue 54)
{0xDF799E12, 0x3C56, 0x421B, {0xB2, 0x98, 0xB6, 0xD3, 0x64, 0x2B, 0xC8, 0x78}}
};
/* Gordon Schumacher's macros for TCHAR -> QString conversions and vice versa */
#ifdef UNICODE
#define QStringToTCHAR(x) (wchar_t *) x.utf16()
#define PQStringToTCHAR(x) (wchar_t *) x->utf16()
#define TCHARToQString(x) QString::fromUtf16((ushort *)(x))
#define TCHARToQStringN(x,y) QString::fromUtf16((ushort *)(x),(y))
#else
#define QStringToTCHAR(x) x.local8Bit().constData()
#define PQStringToTCHAR(x) x->local8Bit().constData()
#define TCHARToQString(x) QString::fromLocal8Bit((char *)(x))
#define TCHARToQStringN(x,y) QString::fromLocal8Bit((char *)(x),(y))
#endif /*UNICODE*/
/*!
\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;
::RegQueryValueEx(key, property, NULL, NULL, NULL, &size);
BYTE *buff = new BYTE[size];
QString result;
if (::RegQueryValueEx(key, property, NULL, &type, buff, &size) == ERROR_SUCCESS)
result = TCHARToQString(buff);
::RegCloseKey(key);
delete [] buff;
return result;
}
/*!
\internal
Get specific property from registry.
\a devInfo pointer to the device information set that contains the interface
and its underlying device. Returned by SetupDiGetClassDevs() function.
\a devData 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 getDeviceProperty(HDEVINFO devInfo, PSP_DEVINFO_DATA devData, DWORD property)
{
DWORD buffSize = 0;
::SetupDiGetDeviceRegistryProperty(devInfo, devData, property, NULL, NULL, 0, &buffSize);
BYTE *buff = new BYTE[buffSize];
::SetupDiGetDeviceRegistryProperty(devInfo, devData, property, NULL, buff, buffSize, NULL);
QString result = TCHARToQString(buff);
delete [] buff;
return result;
}
/*!
\internal
*/
static bool getDeviceDetailsWin(QextPortInfo *portInfo, HDEVINFO devInfo, PSP_DEVINFO_DATA devData
, WPARAM wParam = DBT_DEVICEARRIVAL)
{
portInfo->friendName = getDeviceProperty(devInfo, devData, SPDRP_FRIENDLYNAME);
if (wParam == DBT_DEVICEARRIVAL)
portInfo->physName = getDeviceProperty(devInfo, devData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME);
portInfo->enumName = getDeviceProperty(devInfo, devData, SPDRP_ENUMERATOR_NAME);
QString hardwareIDs = getDeviceProperty(devInfo, devData, SPDRP_HARDWAREID);
HKEY devKey = ::SetupDiOpenDevRegKey(devInfo, devData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
portInfo->portName = getRegKeyValue(devKey, TEXT("PortName"));
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 enumerateDevicesWin(const GUID &guid, QList<QextPortInfo> *infoList)
{
HDEVINFO devInfo;
if ((devInfo = ::SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT)) != INVALID_HANDLE_VALUE) {
SP_DEVINFO_DATA devInfoData;
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for(int i = 0; ::SetupDiEnumDeviceInfo(devInfo, i, &devInfoData); i++) {
QextPortInfo info;
info.productID = info.vendorID = 0;
getDeviceDetailsWin(&info, devInfo, &devInfoData);
if (!info.portName.startsWith(QLatin1String("LPT"), Qt::CaseInsensitive))
infoList->append(info);
}
::SetupDiDestroyDeviceInfoList(devInfo);
}
}
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;
const int count = sizeof(deviceClassGuids)/sizeof(deviceClassGuids[0]);
for (int i=0; i<count; ++i)
enumerateDevicesWin(deviceClassGuids[i], &ports);
qSort(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
}
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 = TCHARToQString(pDevInf->dbcc_name).toUpper().replace(QLatin1String("#"), QLatin1String("\\"));
const int count = sizeof(deviceClassGuids)/sizeof(deviceClassGuids[0]);
for (int i=0; i<count; ++i) {
if (matchAndDispatchChangedDevice(deviceID, deviceClassGuids[i], wParam))
break;
}
}
}
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_ALLCLASSES;
HDEVINFO devInfo;
if ((devInfo = SetupDiGetClassDevs(&guid,NULL,NULL,dwFlag)) != INVALID_HANDLE_VALUE) {
SP_DEVINFO_DATA spDevInfoData;
spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for(int i=0; SetupDiEnumDeviceInfo(devInfo, i, &spDevInfoData); i++) {
DWORD nSize = 0;
TCHAR buf[MAX_PATH];
if (SetupDiGetDeviceInstanceId(devInfo, &spDevInfoData, buf, MAX_PATH, &nSize) &&
deviceID.contains(TCHARToQString(buf))) { // we found a match
rv = true;
QextPortInfo info;
info.productID = info.vendorID = 0;
getDeviceDetailsWin(&info, devInfo, &spDevInfoData, wParam);
if (wParam == DBT_DEVICEARRIVAL)
Q_EMIT q->deviceDiscovered(info);
else if (wParam == DBT_DEVICEREMOVECOMPLETE)
Q_EMIT q->deviceRemoved(info);
break;
}
}
SetupDiDestroyDeviceInfoList(devInfo);
}
return rv;
}
/****************************************************************************
** 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 <stdio.h>
#include <QtCore/QDebug>
#include <QtCore/QReadLocker>
#include <QtCore/QWriteLocker>
/*!
\class PortSettings
\brief The PortSettings class contain port settings
Structure to contain port settings.
\code
BaudRateType BaudRate;
DataBitsType DataBits;
ParityType Parity;
StopBitsType StopBits;
FlowType FlowControl;
long Timeout_Millisec;
\endcode
*/
QextSerialPortPrivate::QextSerialPortPrivate(QextSerialPort *q)
:lock(QReadWriteLock::Recursive), q_ptr(q)
{
lastErr = E_NO_ERROR;
settings.BaudRate = BAUD9600;
settings.Parity = PAR_NONE;
settings.FlowControl = FLOW_OFF;
settings.DataBits = DATA_8;
settings.StopBits = STOP_1;
settings.Timeout_Millisec = 10;
settingsDirtyFlags = DFE_ALL;
platformSpecificInit();
}
QextSerialPortPrivate::~QextSerialPortPrivate()
{
platformSpecificDestruct();
}
void QextSerialPortPrivate::setBaudRate(BaudRateType baudRate, bool update)
{
switch (baudRate) {
#ifdef Q_OS_WIN
//Windows Special
case BAUD14400:
case BAUD56000:
case BAUD128000:
case BAUD256000:
QESP_PORTABILITY_WARNING()<<"QextSerialPort Portability Warning: POSIX does not support baudRate:"<<baudRate;
#elif defined(Q_OS_UNIX)
//Unix Special
case BAUD50:
case BAUD75:
case BAUD134:
case BAUD150:
case BAUD200:
case BAUD1800:
# ifdef B76800
case BAUD76800:
# endif
# if defined(B230400) && defined(B4000000)
case BAUD230400:
case BAUD460800:
case BAUD500000:
case BAUD576000:
case BAUD921600:
case BAUD1000000:
case BAUD1152000:
case BAUD1500000:
case BAUD2000000:
case BAUD2500000:
case BAUD3000000:
case BAUD3500000:
case BAUD4000000:
# endif
QESP_PORTABILITY_WARNING()<<"QextSerialPort Portability Warning: Windows does not support baudRate:"<<baudRate;
#endif
case BAUD110:
case BAUD300:
case BAUD600:
case BAUD1200:
case BAUD2400:
case BAUD4800:
case BAUD9600:
case BAUD19200:
case BAUD38400:
case BAUD57600:
case BAUD115200:
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
default:
#endif
settings.BaudRate = baudRate;
settingsDirtyFlags |= DFE_BaudRate;
if (update && q_func()->isOpen())
updatePortSettings();
break;
#if !(defined(Q_OS_WIN) || defined(Q_OS_MAC))
default:
QESP_WARNING()<<"QextSerialPort does not support baudRate:"<<baudRate;
#endif
}
}
void QextSerialPortPrivate::setParity(ParityType parity, bool update)
{
switch (parity) {
case PAR_SPACE:
if (settings.DataBits == DATA_8) {
#ifdef Q_OS_WIN
QESP_PORTABILITY_WARNING("QextSerialPort Portability Warning: Space parity with 8 data bits is not supported by POSIX systems.");
#else
QESP_WARNING("Space parity with 8 data bits is not supported by POSIX systems.");
#endif
}
break;
#ifdef Q_OS_WIN
/*mark parity - WINDOWS ONLY*/
case PAR_MARK:
QESP_PORTABILITY_WARNING("QextSerialPort Portability Warning: Mark parity is not supported by POSIX systems");
break;
#endif
case PAR_NONE:
case PAR_EVEN:
case PAR_ODD:
break;
default:
QESP_WARNING()<<"QextSerialPort does not support Parity:" << parity;
}
settings.Parity = parity;
settingsDirtyFlags |= DFE_Parity;
if (update && q_func()->isOpen())
updatePortSettings();
}
void QextSerialPortPrivate::setDataBits(DataBitsType dataBits, bool update)
{
switch(dataBits) {
case DATA_5:
if (settings.StopBits == STOP_2) {
QESP_WARNING("QextSerialPort: 5 Data bits cannot be used with 2 stop bits.");
}
else {
settings.DataBits = dataBits;
settingsDirtyFlags |= DFE_DataBits;
}
break;
case DATA_6:
#ifdef Q_OS_WIN
if (settings.StopBits == STOP_1_5) {
QESP_WARNING("QextSerialPort: 6 Data bits cannot be used with 1.5 stop bits.");
}
else
#endif
{
settings.DataBits = dataBits;
settingsDirtyFlags |= DFE_DataBits;
}
break;
case DATA_7:
#ifdef Q_OS_WIN
if (settings.StopBits == STOP_1_5) {
QESP_WARNING("QextSerialPort: 7 Data bits cannot be used with 1.5 stop bits.");
}
else
#endif
{
settings.DataBits = dataBits;
settingsDirtyFlags |= DFE_DataBits;
}
break;
case DATA_8:
#ifdef Q_OS_WIN
if (settings.StopBits == STOP_1_5) {
QESP_WARNING("QextSerialPort: 8 Data bits cannot be used with 1.5 stop bits.");
}
else
#endif
{
settings.DataBits = dataBits;
settingsDirtyFlags |= DFE_DataBits;
}
break;
default:
QESP_WARNING()<<"QextSerialPort does not support Data bits:"<<dataBits;
}
if (update && q_func()->isOpen())
updatePortSettings();
}
void QextSerialPortPrivate::setStopBits(StopBitsType stopBits, bool update)
{
switch (stopBits) {
/*one stop bit*/
case STOP_1:
settings.StopBits = stopBits;
settingsDirtyFlags |= DFE_StopBits;
break;
#ifdef Q_OS_WIN
/*1.5 stop bits*/
case STOP_1_5:
QESP_PORTABILITY_WARNING("QextSerialPort Portability Warning: 1.5 stop bit operation is not supported by POSIX.");
if (settings.DataBits != DATA_5) {
QESP_WARNING("QextSerialPort: 1.5 stop bits can only be used with 5 data bits");
}
else {
settings.StopBits = stopBits;
settingsDirtyFlags |= DFE_StopBits;
}
break;
#endif
/*two stop bits*/
case STOP_2:
if (settings.DataBits == DATA_5) {
QESP_WARNING("QextSerialPort: 2 stop bits cannot be used with 5 data bits");
}
else {
settings.StopBits = stopBits;
settingsDirtyFlags |= DFE_StopBits;
}
break;
default:
QESP_WARNING()<<"QextSerialPort does not support stop bits: "<<stopBits;
}
if (update && q_func()->isOpen())
updatePortSettings();
}
void QextSerialPortPrivate::setFlowControl(FlowType flow, bool update)
{
settings.FlowControl = flow;
settingsDirtyFlags |= DFE_Flow;
if (update && q_func()->isOpen())
updatePortSettings();
}
void QextSerialPortPrivate::setTimeout(long millisec, bool update)
{
settings.Timeout_Millisec = millisec;
settingsDirtyFlags |= DFE_TimeOut;
if (update && q_func()->isOpen())
updatePortSettings();
}
void QextSerialPortPrivate::setPortSettings(const PortSettings &settings, bool update)
{
setBaudRate(settings.BaudRate, false);
setDataBits(settings.DataBits, false);
setStopBits(settings.StopBits, false);
setParity(settings.Parity, false);
setFlowControl(settings.FlowControl, false);
setTimeout(settings.Timeout_Millisec, false);
settingsDirtyFlags = DFE_ALL;
if (update && q_func()->isOpen())
updatePortSettings();
}
void QextSerialPortPrivate::_q_canRead()
{
qint64 maxSize = bytesAvailable_sys();
if (maxSize > 0) {
char *writePtr = readBuffer.reserve(size_t(maxSize));
qint64 bytesRead = readData_sys(writePtr, maxSize);
if (bytesRead < maxSize)
readBuffer.chop(maxSize - bytesRead);
Q_Q(QextSerialPort);
Q_EMIT q->readyRead();
}
}
/*! \class QextSerialPort
\brief The QextSerialPort class encapsulates a serial port on both POSIX and Windows systems.
\section1 Usage
QextSerialPort offers both a polling and event driven API. Event driven
is typically easier to use, since you never have to worry about checking
for new data.
\bold Example
\code
QextSerialPort *port = new QextSerialPort("COM1");
connect(port, SIGNAL(readyRead()), myClass, SLOT(onDataAvailable()));
port->open();
void MyClass::onDataAvailable()
{
QByteArray data = port->readAll();
processNewData(usbdata);
}
\endcode
\section1 Compatibility
The user will be notified of errors and possible portability conflicts at run-time
by default.
For example, if a application has used BAUD1800, when it is runing under unix, you
will get following message.
\code
QextSerialPort Portability Warning: Windows does not support baudRate:1800
\endcode
This behavior can be turned off by defining macro QESP_NO_WARN (to turn off all warnings)
or QESP_NO_PORTABILITY_WARN (to turn off portability warnings) in the project.
\bold Author: Stefan Sander, Michal Policht, Brandon Fosdick, Liam Staskawicz, Debao Zhang
*/
/*!
\enum QextSerialPort::QueryMode
This enum type specifies query mode used in a serial port:
\value Polling
asynchronously read and write
\value EventDriven
synchronously read and write
*/
/*!
\fn void QextSerialPort::dsrChanged(bool status)
This signal is emitted whenever dsr line has changed its state. You may
use this signal to check if device is connected.
\a status true when DSR signal is on, false otherwise.
*/
/*!
\fn QueryMode QextSerialPort::queryMode() const
Get query mode.
*/
/*!
Default constructor. Note that the name of the device used by a QextSerialPort is dependent on
your OS. Possible naming conventions and their associated OS are:
\code
OS Constant Used By Naming Convention
------------- ------------- ------------------------
Q_OS_WIN Windows COM1, COM2
Q_OS_IRIX SGI/IRIX /dev/ttyf1, /dev/ttyf2
Q_OS_HPUX HP-UX /dev/tty1p0, /dev/tty2p0
Q_OS_SOLARIS SunOS/Slaris /dev/ttya, /dev/ttyb
Q_OS_OSF Digital UNIX /dev/tty01, /dev/tty02
Q_OS_FREEBSD FreeBSD /dev/ttyd0, /dev/ttyd1
Q_OS_OPENBSD OpenBSD /dev/tty00, /dev/tty01
Q_OS_LINUX Linux /dev/ttyS0, /dev/ttyS1
<none> /dev/ttyS0, /dev/ttyS1
\endcode
This constructor assigns the device name to the name of the first port on the specified system.
See the other constructors if you need to open a different port. Default \a mode is EventDriven.
As a subclass of QObject, \a parent can be specified.
*/
QextSerialPort::QextSerialPort(QextSerialPort::QueryMode mode, QObject *parent)
: QIODevice(parent), d_ptr(new QextSerialPortPrivate(this))
{
#ifdef Q_OS_WIN
setPortName(QLatin1String("COM1"));
#elif defined(Q_OS_IRIX)
setPortName(QLatin1String("/dev/ttyf1"));
#elif defined(Q_OS_HPUX)
setPortName(QLatin1String("/dev/tty1p0"));
#elif defined(Q_OS_SOLARIS)
setPortName(QLatin1String("/dev/ttya"));
#elif defined(Q_OS_OSF) //formally DIGITAL UNIX
setPortName(QLatin1String("/dev/tty01"));
#elif defined(Q_OS_FREEBSD)
setPortName(QLatin1String("/dev/ttyd1"));
#elif defined(Q_OS_OPENBSD)
setPortName(QLatin1String("/dev/tty00"));
#else
setPortName(QLatin1String("/dev/ttyS0"));
#endif
setQueryMode(mode);
}
/*!
Constructs a serial port attached to the port specified by name.
\a name is the name of the device, which is windowsystem-specific,
e.g."COM1" or "/dev/ttyS0". \a mode
*/
QextSerialPort::QextSerialPort(const QString &name, QextSerialPort::QueryMode mode, QObject *parent)
: QIODevice(parent), d_ptr(new QextSerialPortPrivate(this))
{
setQueryMode(mode);
setPortName(name);
}
/*!
Constructs a port with default name and specified \a settings.
*/
QextSerialPort::QextSerialPort(const PortSettings &settings, QextSerialPort::QueryMode mode, QObject *parent)
: QIODevice(parent), d_ptr(new QextSerialPortPrivate(this))
{
Q_D(QextSerialPort);
setQueryMode(mode);
d->setPortSettings(settings);
}
/*!
Constructs a port with specified \a name , \a mode and \a settings.
*/
QextSerialPort::QextSerialPort(const QString &name, const PortSettings &settings, QextSerialPort::QueryMode mode, QObject *parent)
: QIODevice(parent), d_ptr(new QextSerialPortPrivate(this))
{
Q_D(QextSerialPort);
setPortName(name);
setQueryMode(mode);
d->setPortSettings(settings);
}
/*!
Opens a serial port and sets its OpenMode to \a mode.
Note that this function does not specify which device to open.
Returns true if successful; otherwise returns false.This function has no effect
if the port associated with the class is already open. The port is also
configured to the current settings, as stored in the settings structure.
*/
bool QextSerialPort::open(OpenMode mode)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (mode != QIODevice::NotOpen && !isOpen())
d->open_sys(mode);
return isOpen();
}
/*! \reimp
Closes a serial port. This function has no effect if the serial port associated with the class
is not currently open.
*/
void QextSerialPort::close()
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (isOpen()) {
// Be a good QIODevice and call QIODevice::close() before really close()
// so the aboutToClose() signal is emitted at the proper time
QIODevice::close(); // mark ourselves as closed
d->close_sys();
d->readBuffer.clear();
}
}
/*!
Flushes all pending I/O to the serial port. This function has no effect if the serial port
associated with the class is not currently open.
*/
void QextSerialPort::flush()
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (isOpen())
d->flush_sys();
}
/*! \reimp
Returns the number of bytes waiting in the port's receive queue. This function will return 0 if
the port is not currently open, or -1 on error.
*/
qint64 QextSerialPort::bytesAvailable() const
{
QWriteLocker locker(&d_func()->lock);
if (isOpen()) {
qint64 bytes = d_func()->bytesAvailable_sys();
if (bytes != -1) {
return bytes + d_func()->readBuffer.size()
+ QIODevice::bytesAvailable();
} else {
return -1;
}
}
return 0;
}
/*! \reimp
*/
bool QextSerialPort::canReadLine() const
{
QReadLocker locker(&d_func()->lock);
return QIODevice::canReadLine() || d_func()->readBuffer.canReadLine();
}
/*!
* Set desired serial communication handling style. You may choose from polling
* or event driven approach. This function does nothing when port is open; to
* apply changes port must be reopened.
*
* In event driven approach read() and write() functions are acting
* asynchronously. They return immediately and the operation is performed in
* the background, so they doesn't freeze the calling thread.
* To determine when operation is finished, QextSerialPort runs separate thread
* and monitors serial port events. Whenever the event occurs, adequate signal
* is emitted.
*
* When polling is set, read() and write() are acting synchronously. Signals are
* not working in this mode and some functions may not be available. The advantage
* of polling is that it generates less overhead due to lack of signals emissions
* and it doesn't start separate thread to monitor events.
*
* Generally event driven approach is more capable and friendly, although some
* applications may need as low overhead as possible and then polling comes.
*
* \a mode query mode.
*/
void QextSerialPort::setQueryMode(QueryMode mode)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (mode != d->queryMode) {
d->queryMode = mode;
}
}
/*!
Sets the \a name of the device associated with the object, e.g. "COM1", or "/dev/ttyS0".
*/
void QextSerialPort::setPortName(const QString &name)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
d->port = name;
}
/*!
Returns the name set by setPortName().
*/
QString QextSerialPort::portName() const
{
QReadLocker locker(&d_func()->lock);
return d_func()->port;
}
QextSerialPort::QueryMode QextSerialPort::queryMode() const
{
QReadLocker locker(&d_func()->lock);
return d_func()->queryMode;
}
/*!
Reads all available data from the device, and returns it as a QByteArray.
This function has no way of reporting errors; returning an empty QByteArray()
can mean either that no data was currently available for reading, or that an error occurred.
*/
QByteArray QextSerialPort::readAll()
{
int avail = this->bytesAvailable();
return (avail > 0) ? this->read(avail) : QByteArray();
}
/*!
Returns the baud rate of the serial port. For a list of possible return values see
the definition of the enum BaudRateType.
*/
BaudRateType QextSerialPort::baudRate() const
{
QReadLocker locker(&d_func()->lock);
return d_func()->settings.BaudRate;
}
/*!
Returns the number of data bits used by the port. For a list of possible values returned by
this function, see the definition of the enum DataBitsType.
*/
DataBitsType QextSerialPort::dataBits() const
{
QReadLocker locker(&d_func()->lock);
return d_func()->settings.DataBits;
}
/*!
Returns the type of parity used by the port. For a list of possible values returned by
this function, see the definition of the enum ParityType.
*/
ParityType QextSerialPort::parity() const
{
QReadLocker locker(&d_func()->lock);
return d_func()->settings.Parity;
}
/*!
Returns the number of stop bits used by the port. For a list of possible return values, see
the definition of the enum StopBitsType.
*/
StopBitsType QextSerialPort::stopBits() const
{
QReadLocker locker(&d_func()->lock);
return d_func()->settings.StopBits;
}
/*!
Returns the type of flow control used by the port. For a list of possible values returned
by this function, see the definition of the enum FlowType.
*/
FlowType QextSerialPort::flowControl() const
{
QReadLocker locker(&d_func()->lock);
return d_func()->settings.FlowControl;
}
/*!
\reimp
Returns true if device is sequential, otherwise returns false. Serial port is sequential device
so this function always returns true. Check QIODevice::isSequential() documentation for more
information.
*/
bool QextSerialPort::isSequential() const
{
return true;
}
/*!
Return the error number, or 0 if no error occurred.
*/
ulong QextSerialPort::lastError() const
{
QReadLocker locker(&d_func()->lock);
return d_func()->lastErr;
}
/*!
Returns the line status as stored by the port function. This function will retrieve the states
of the following lines: DCD, CTS, DSR, and RI. On POSIX systems, the following additional lines
can be monitored: DTR, RTS, Secondary TXD, and Secondary RXD. The value returned is an unsigned
long with specific bits indicating which lines are high. The following constants should be used
to examine the states of individual lines:
\code
Mask Line
------ ----
LS_CTS CTS
LS_DSR DSR
LS_DCD DCD
LS_RI RI
LS_RTS RTS (POSIX only)
LS_DTR DTR (POSIX only)
LS_ST Secondary TXD (POSIX only)
LS_SR Secondary RXD (POSIX only)
\endcode
This function will return 0 if the port associated with the class is not currently open.
*/
unsigned long QextSerialPort::lineStatus()
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (isOpen())
return d->lineStatus_sys();
return 0;
}
/*!
Returns a human-readable description of the last device error that occurred.
*/
QString QextSerialPort::errorString()
{
Q_D(QextSerialPort);
QReadLocker locker(&d->lock);
switch(d->lastErr) {
case E_NO_ERROR:
return tr("No Error has occurred");
case E_INVALID_FD:
return tr("Invalid file descriptor (port was not opened correctly)");
case E_NO_MEMORY:
return tr("Unable to allocate memory tables (POSIX)");
case E_CAUGHT_NON_BLOCKED_SIGNAL:
return tr("Caught a non-blocked signal (POSIX)");
case E_PORT_TIMEOUT:
return tr("Operation timed out (POSIX)");
case E_INVALID_DEVICE:
return tr("The file opened by the port is not a valid device");
case E_BREAK_CONDITION:
return tr("The port detected a break condition");
case E_FRAMING_ERROR:
return tr("The port detected a framing error (usually caused by incorrect baud rate settings)");
case E_IO_ERROR:
return tr("There was an I/O error while communicating with the port");
case E_BUFFER_OVERRUN:
return tr("Character buffer overrun");
case E_RECEIVE_OVERFLOW:
return tr("Receive buffer overflow");
case E_RECEIVE_PARITY_ERROR:
return tr("The port detected a parity error in the received data");
case E_TRANSMIT_OVERFLOW:
return tr("Transmit buffer overflow");
case E_READ_FAILED:
return tr("General read operation failure");
case E_WRITE_FAILED:
return tr("General write operation failure");
case E_FILE_NOT_FOUND:
return tr("The %1 file doesn't exists").arg(this->portName());
case E_PERMISSION_DENIED:
return tr("Permission denied");
case E_AGAIN:
return tr("Device is already locked");
default:
return tr("Unknown error: %1").arg(d->lastErr);
}
}
/*!
Destructs the QextSerialPort object.
*/
QextSerialPort::~QextSerialPort()
{
if (isOpen()) {
close();
}
delete d_ptr;
}
/*!
Sets the flow control used by the port to \a flow. Possible values of flow are:
\code
FLOW_OFF No flow control
FLOW_HARDWARE Hardware (RTS/CTS) flow control
FLOW_XONXOFF Software (XON/XOFF) flow control
\endcode
*/
void QextSerialPort::setFlowControl(FlowType flow)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (d->settings.FlowControl != flow)
d->setFlowControl(flow, true);
}
/*!
Sets the parity associated with the serial port to \a parity. The possible values of parity are:
\code
PAR_SPACE Space Parity
PAR_MARK Mark Parity
PAR_NONE No Parity
PAR_EVEN Even Parity
PAR_ODD Odd Parity
\endcode
*/
void QextSerialPort::setParity(ParityType parity)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (d->settings.Parity != parity)
d->setParity(parity, true);
}
/*!
Sets the number of data bits used by the serial port to \a dataBits. Possible values of dataBits are:
\code
DATA_5 5 data bits
DATA_6 6 data bits
DATA_7 7 data bits
DATA_8 8 data bits
\endcode
\bold note:
This function is subject to the following restrictions:
\list
\o 5 data bits cannot be used with 2 stop bits.
\o 1.5 stop bits can only be used with 5 data bits.
\o 8 data bits cannot be used with space parity on POSIX systems.
\endlist
*/
void QextSerialPort::setDataBits(DataBitsType dataBits)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (d->settings.DataBits != dataBits)
d->setDataBits(dataBits, true);
}
/*!
Sets the number of stop bits used by the serial port to \a stopBits. Possible values of stopBits are:
\code
STOP_1 1 stop bit
STOP_1_5 1.5 stop bits
STOP_2 2 stop bits
\endcode
\bold note:
This function is subject to the following restrictions:
\list
\o 2 stop bits cannot be used with 5 data bits.
\o 1.5 stop bits cannot be used with 6 or more data bits.
\o POSIX does not support 1.5 stop bits.
\endlist
*/
void QextSerialPort::setStopBits(StopBitsType stopBits)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (d->settings.StopBits != stopBits)
d->setStopBits(stopBits, true);
}
/*!
Sets the baud rate of the serial port to \a baudRate. Note that not all rates are applicable on
all platforms. The following table shows translations of the various baud rate
constants on Windows(including NT/2000) and POSIX platforms. Speeds marked with an *
are speeds that are usable on both Windows and POSIX.
\code
RATE Windows Speed POSIX Speed
----------- ------------- -----------
BAUD50 X 50
BAUD75 X 75
*BAUD110 110 110
BAUD134 X 134.5
BAUD150 X 150
BAUD200 X 200
*BAUD300 300 300
*BAUD600 600 600
*BAUD1200 1200 1200
BAUD1800 X 1800
*BAUD2400 2400 2400
*BAUD4800 4800 4800
*BAUD9600 9600 9600
BAUD14400 14400 X
*BAUD19200 19200 19200
*BAUD38400 38400 38400
BAUD56000 56000 X
*BAUD57600 57600 57600
BAUD76800 X 76800
*BAUD115200 115200 115200
BAUD128000 128000 X
BAUD230400 X 230400
BAUD256000 256000 X
BAUD460800 X 460800
BAUD500000 X 500000
BAUD576000 X 576000
BAUD921600 X 921600
BAUD1000000 X 1000000
BAUD1152000 X 1152000
BAUD1500000 X 1500000
BAUD2000000 X 2000000
BAUD2500000 X 2500000
BAUD3000000 X 3000000
BAUD3500000 X 3500000
BAUD4000000 X 4000000
\endcode
*/
void QextSerialPort::setBaudRate(BaudRateType baudRate)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (d->settings.BaudRate != baudRate)
d->setBaudRate(baudRate, true);
}
/*!
For Unix:
Sets the read and write timeouts for the port to \a millisec milliseconds.
Note that this is a per-character timeout, i.e. the port will wait this long for each
individual character, not for the whole read operation. This timeout also applies to the
bytesWaiting() function.
\bold note:
POSIX does not support millisecond-level control for I/O timeout values. Any
timeout set using this function will be set to the next lowest tenth of a second for
the purposes of detecting read or write timeouts. For example a timeout of 550 milliseconds
will be seen by the class as a timeout of 500 milliseconds for the purposes of reading and
writing the port. However millisecond-level control is allowed by the select() system call,
so for example a 550-millisecond timeout will be seen as 550 milliseconds on POSIX systems for
the purpose of detecting available bytes in the read buffer.
For Windows:
Sets the read and write timeouts for the port to \a millisec milliseconds.
Setting 0 indicates that timeouts are not used for read nor write operations;
however read() and write() functions will still block. Set -1 to provide
non-blocking behaviour (read() and write() will return immediately).
\bold note: this function does nothing in event driven mode.
*/
void QextSerialPort::setTimeout(long millisec)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (d->settings.Timeout_Millisec != millisec)
d->setTimeout(millisec, true);
}
/*!
Sets DTR line to the requested state (\a set default to high). This function will have no effect if
the port associated with the class is not currently open.
*/
void QextSerialPort::setDtr(bool set)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (isOpen())
d->setDtr_sys(set);
}
/*!
Sets RTS line to the requested state \a set (high by default).
This function will have no effect if
the port associated with the class is not currently open.
*/
void QextSerialPort::setRts(bool set)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
if (isOpen())
d->setRts_sys(set);
}
/*! \reimp
Reads a block of data from the serial port. This function will read at most maxlen bytes from
the serial port and place them in the buffer pointed to by data. Return value is the number of
bytes actually read, or -1 on error.
\warning before calling this function ensure that serial port associated with this class
is currently open (use isOpen() function to check if port is open).
*/
qint64 QextSerialPort::readData(char *data, qint64 maxSize)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
qint64 bytesFromBuffer = 0;
if (!d->readBuffer.isEmpty()) {
bytesFromBuffer = d->readBuffer.read(data, maxSize);
if (bytesFromBuffer == maxSize)
return bytesFromBuffer;
}
qint64 bytesFromDevice = d->readData_sys(data+bytesFromBuffer, maxSize-bytesFromBuffer);
if (bytesFromDevice < 0) {
return -1;
}
return bytesFromBuffer + bytesFromDevice;
}
/*! \reimp
Writes a block of data to the serial port. This function will write len bytes
from the buffer pointed to by data to the serial port. Return value is the number
of bytes actually written, or -1 on error.
\warning before calling this function ensure that serial port associated with this class
is currently open (use isOpen() function to check if port is open).
*/
qint64 QextSerialPort::writeData(const char *data, qint64 maxSize)
{
Q_D(QextSerialPort);
QWriteLocker locker(&d->lock);
return d->writeData_sys(data, maxSize);
}
#include "moc_qextserialport.cpp"
/****************************************************************************
** 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
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__
/****************************************************************************
** 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
/****************************************************************************
** 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_
/****************************************************************************
** 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(&currentTermios); // 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(&currentTermios, B50);
break;
case BAUD75:
setBaudRate2Termios(&currentTermios, B75);
break;
case BAUD110:
setBaudRate2Termios(&currentTermios, B110);
break;
case BAUD134:
setBaudRate2Termios(&currentTermios, B134);
break;
case BAUD150:
setBaudRate2Termios(&currentTermios, B150);
break;
case BAUD200:
setBaudRate2Termios(&currentTermios, B200);
break;
case BAUD300:
setBaudRate2Termios(&currentTermios, B300);
break;
case BAUD600:
setBaudRate2Termios(&currentTermios, B600);
break;
case BAUD1200:
setBaudRate2Termios(&currentTermios, B1200);
break;
case BAUD1800:
setBaudRate2Termios(&currentTermios, B1800);
break;
case BAUD2400:
setBaudRate2Termios(&currentTermios, B2400);
break;
case BAUD4800:
setBaudRate2Termios(&currentTermios, B4800);
break;
case BAUD9600:
setBaudRate2Termios(&currentTermios, B9600);
break;
case BAUD19200:
setBaudRate2Termios(&currentTermios, B19200);
break;
case BAUD38400:
setBaudRate2Termios(&currentTermios, B38400);
break;
case BAUD57600:
setBaudRate2Termios(&currentTermios, B57600);
break;
#ifdef B76800
case BAUD76800:
setBaudRate2Termios(&currentTermios, B76800);
break;
#endif
case BAUD115200:
setBaudRate2Termios(&currentTermios, B115200);
break;
#if defined(B230400) && defined(B4000000)
case BAUD230400:
setBaudRate2Termios(&currentTermios, B230400);
break;
case BAUD460800:
setBaudRate2Termios(&currentTermios, B460800);
break;
case BAUD500000:
setBaudRate2Termios(&currentTermios, B500000);
break;
case BAUD576000:
setBaudRate2Termios(&currentTermios, B576000);
break;
case BAUD921600:
setBaudRate2Termios(&currentTermios, B921600);
break;
case BAUD1000000:
setBaudRate2Termios(&currentTermios, B1000000);
break;
case BAUD1152000:
setBaudRate2Termios(&currentTermios, B1152000);
break;
case BAUD1500000:
setBaudRate2Termios(&currentTermios, B1500000);
break;
case BAUD2000000:
setBaudRate2Termios(&currentTermios, B2000000);
break;
case BAUD2500000:
setBaudRate2Termios(&currentTermios, B2500000);
break;
case BAUD3000000:
setBaudRate2Termios(&currentTermios, B3000000);
break;
case BAUD3500000:
setBaudRate2Termios(&currentTermios, B3500000);
break;
case BAUD4000000:
setBaudRate2Termios(&currentTermios, B4000000);
break;
#endif
#ifdef Q_OS_MAC
default:
setBaudRate2Termios(&currentTermios, 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, &currentTermios);
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, &currentTermios);
currentTermios.c_cc[VTIME] = millisec/100;
::tcsetattr(fd, TCSAFLUSH, &currentTermios);
}
settingsDirtyFlags = 0;
}
/****************************************************************************
** 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;
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) {
QWriteLocker writelocker(bytesToWriteLock);
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;
}
...@@ -194,6 +194,8 @@ ReleaseBuild { ...@@ -194,6 +194,8 @@ ReleaseBuild {
} }
} }
include(libs/qextserialport/src/qextserialport.pri)
# #
# External library configuration # External library configuration
# #
......
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
#include <QDebug> #include <QDebug>
#include <QTime> #include <QTime>
#include "QGC.h"
static const quint32 crctab[] = static const quint32 crctab[] =
{ {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
...@@ -76,22 +78,24 @@ static quint32 crc32(const uint8_t *src, unsigned len, unsigned state) ...@@ -76,22 +78,24 @@ static quint32 crc32(const uint8_t *src, unsigned len, unsigned state)
return state; return state;
} }
#if 0
const struct PX4Bootloader::serialPortErrorString PX4Bootloader::_rgSerialPortErrors[14] = { const struct PX4Bootloader::serialPortErrorString PX4Bootloader::_rgSerialPortErrors[14] = {
{ QSerialPort::NoError, "No error occurred." }, { QextSerialPort::NoError, "No error occurred." },
{ QSerialPort::DeviceNotFoundError, "An error occurred while attempting to open a non-existing device." }, { QextSerialPort::DeviceNotFoundError, "An error occurred while attempting to open a non-existing device." },
{ QSerialPort::PermissionError, "An error occurred while attempting to open an already opened device by another process or a user not having enough permission and credentials to open." }, { QextSerialPort::PermissionError, "An error occurred while attempting to open an already opened device by another process or a user not having enough permission and credentials to open." },
{ QSerialPort::OpenError, "An error occurred while attempting to open an already opened device in this object." }, { QextSerialPort::OpenError, "An error occurred while attempting to open an already opened device in this object." },
{ QSerialPort::NotOpenError, "This error occurs when an operation is executed that can only be successfully performed if the device is open." }, { QextSerialPort::NotOpenError, "This error occurs when an operation is executed that can only be successfully performed if the device is open." },
{ QSerialPort::ParityError, "Parity error detected by the hardware while reading data." }, { QextSerialPort::ParityError, "Parity error detected by the hardware while reading data." },
{ QSerialPort::FramingError, "Framing error detected by the hardware while reading data." }, { QextSerialPort::FramingError, "Framing error detected by the hardware while reading data." },
{ QSerialPort::BreakConditionError, "Break condition detected by the hardware on the input line." }, { QextSerialPort::BreakConditionError, "Break condition detected by the hardware on the input line." },
{ QSerialPort::WriteError, "An I/O error occurred while writing the data." }, { QextSerialPort::WriteError, "An I/O error occurred while writing the data." },
{ QSerialPort::ReadError, "An I/O error occurred while reading the data." }, { QextSerialPort::ReadError, "An I/O error occurred while reading the data." },
{ QSerialPort::ResourceError, "An I/O error occurred when a resource becomes unavailable, e.g. when the device is unexpectedly removed from the system." }, { QextSerialPort::ResourceError, "An I/O error occurred when a resource becomes unavailable, e.g. when the device is unexpectedly removed from the system." },
{ QSerialPort::UnsupportedOperationError, "The requested device operation is not supported or prohibited by the running operating system." }, { QextSerialPort::UnsupportedOperationError, "The requested device operation is not supported or prohibited by the running operating system." },
{ QSerialPort::TimeoutError, "A timeout error occurred." }, { QextSerialPort::TimeoutError, "A timeout error occurred." },
{ QSerialPort::UnknownError, "An unidentified error occurred." } { QextSerialPort::UnknownError, "An unidentified error occurred." }
}; };
#endif
PX4Bootloader::PX4Bootloader(QObject *parent) : PX4Bootloader::PX4Bootloader(QObject *parent) :
QObject(parent) QObject(parent)
...@@ -99,9 +103,11 @@ PX4Bootloader::PX4Bootloader(QObject *parent) : ...@@ -99,9 +103,11 @@ PX4Bootloader::PX4Bootloader(QObject *parent) :
} }
/// @brief Translate a QSerialPort::SerialPortError code into a string. /// @brief Translate a QextSerialPort::SerialPortError code into a string.
const char* PX4Bootloader::_serialPortErrorString(int error) const char* PX4Bootloader::_serialPortErrorString(int error)
{ {
Q_UNUSED(error);
#if 0
Again: Again:
for (size_t i=0; i<sizeof(_rgSerialPortErrors)/sizeof(_rgSerialPortErrors[0]); i++) { for (size_t i=0; i<sizeof(_rgSerialPortErrors)/sizeof(_rgSerialPortErrors[0]); i++) {
if (error == _rgSerialPortErrors[i].error) { if (error == _rgSerialPortErrors[i].error) {
...@@ -109,22 +115,19 @@ Again: ...@@ -109,22 +115,19 @@ Again:
} }
} }
error = QSerialPort::UnknownError; error = QextSerialPort::UnknownError;
goto Again; goto Again;
Q_ASSERT(false); Q_ASSERT(false);
return NULL; return NULL;
#else
return "NYI error";
#endif
} }
bool PX4Bootloader::write(QSerialPort* port, const uint8_t* data, qint64 maxSize) bool PX4Bootloader::write(QextSerialPort* port, const uint8_t* data, qint64 maxSize)
{ {
// Make sure we don't overflow output buffer
while (port->bytesToWrite() > 50) {
int bump = 0;
bump++;
}
qint64 bytesWritten = port->write((const char*)data, maxSize); qint64 bytesWritten = port->write((const char*)data, maxSize);
if (bytesWritten == -1) { if (bytesWritten == -1) {
_errorString = tr("Write failed: %1").arg(port->errorString()); _errorString = tr("Write failed: %1").arg(port->errorString());
...@@ -136,39 +139,36 @@ bool PX4Bootloader::write(QSerialPort* port, const uint8_t* data, qint64 maxSize ...@@ -136,39 +139,36 @@ bool PX4Bootloader::write(QSerialPort* port, const uint8_t* data, qint64 maxSize
qWarning() << _errorString; qWarning() << _errorString;
return false; return false;
} }
if (!port->waitForBytesWritten(1000)) {
_errorString = tr("Timeout waiting for write");
qWarning() << _errorString;
return false;
}
return true; return true;
} }
bool PX4Bootloader::write(QSerialPort* port, const uint8_t byte) bool PX4Bootloader::write(QextSerialPort* port, const uint8_t byte)
{ {
uint8_t buf[1] = { byte }; uint8_t buf[1] = { byte };
return write(port, buf, 1); return write(port, buf, 1);
} }
bool PX4Bootloader::read(QSerialPort* port, uint8_t* data, qint64 maxSize, bool warnOnError, int readTimeout) bool PX4Bootloader::read(QextSerialPort* port, uint8_t* data, qint64 maxSize, bool warnOnError, int readTimeout)
{ {
qint64 bytesToRead = 0; qint64 bytesAlreadyRead = 0;
while (bytesToRead < maxSize) { while (bytesAlreadyRead < maxSize) {
if (port->bytesAvailable() == 0) { QTime timeout;
if (!port->waitForReadyRead(readTimeout)) { timeout.start();
_errorString = tr("Timeout waiting for byte to be available"); while (port->bytesAvailable() < 1) {
if (timeout.elapsed() > readTimeout) {
_errorString = tr("Timeout waiting for bytes to be available");
if (warnOnError) { if (warnOnError) {
qWarning() << _errorString; qWarning() << _errorString;
} }
return false; return false;
} }
Q_ASSERT(port->bytesAvailable() != 0); QGC::SLEEP::usleep(100);
} }
qint64 bytesRead; qint64 bytesRead;
bytesRead = port->read((char*)&data[bytesToRead], maxSize); bytesRead = port->read((char*)&data[bytesAlreadyRead], maxSize);
if (bytesRead == -1) { if (bytesRead == -1) {
_errorString = tr("Read failed: Could not read 1 byte, error: %1").arg(port->errorString()); _errorString = tr("Read failed: Could not read 1 byte, error: %1").arg(port->errorString());
...@@ -178,14 +178,14 @@ bool PX4Bootloader::read(QSerialPort* port, uint8_t* data, qint64 maxSize, bool ...@@ -178,14 +178,14 @@ bool PX4Bootloader::read(QSerialPort* port, uint8_t* data, qint64 maxSize, bool
return false; return false;
} else { } else {
Q_ASSERT(bytesRead != 0); Q_ASSERT(bytesRead != 0);
bytesToRead += bytesRead; bytesAlreadyRead += bytesRead;
} }
} }
return true; return true;
} }
bool PX4Bootloader::getCommandResponse(QSerialPort* port, bool warnOnError, int responseTimeout) bool PX4Bootloader::getCommandResponse(QextSerialPort* port, bool warnOnError, int responseTimeout)
{ {
uint8_t response[2]; uint8_t response[2];
...@@ -217,30 +217,32 @@ bool PX4Bootloader::getCommandResponse(QSerialPort* port, bool warnOnError, int ...@@ -217,30 +217,32 @@ bool PX4Bootloader::getCommandResponse(QSerialPort* port, bool warnOnError, int
return true; return true;
} }
bool PX4Bootloader::getBoardInfo(QSerialPort* port, uint8_t param, uint32_t& value) bool PX4Bootloader::getBoardInfo(QextSerialPort* port, uint8_t param, uint32_t& value)
{ {
uint8_t buf[3] = { PROTO_GET_DEVICE, param, PROTO_EOC }; uint8_t buf[3] = { PROTO_GET_DEVICE, param, PROTO_EOC };
if (!write(port, buf, sizeof(buf))) { if (!write(port, buf, sizeof(buf))) {
return false; return false;
} }
port->flush();
if (!read(port, (uint8_t*)&value, sizeof(value), warnOnError)) { if (!read(port, (uint8_t*)&value, sizeof(value), warnOnError)) {
return false; return false;
} }
return getCommandResponse(port, warnOnError); return getCommandResponse(port, warnOnError);
} }
bool PX4Bootloader::sendCommand(QSerialPort* port, const uint8_t cmd, bool warnOnError, int responseTimeout) bool PX4Bootloader::sendCommand(QextSerialPort* port, const uint8_t cmd, bool warnOnError, int responseTimeout)
{ {
uint8_t buf[2] = { cmd, PROTO_EOC }; uint8_t buf[2] = { cmd, PROTO_EOC };
if (!write(port, buf, 2)) { if (!write(port, buf, 2)) {
return false; return false;
} }
port->flush();
return getCommandResponse(port, warnOnError, responseTimeout); return getCommandResponse(port, warnOnError, responseTimeout);
} }
bool PX4Bootloader::erase(QSerialPort* port) bool PX4Bootloader::erase(QextSerialPort* port)
{ {
// Erase is slow, need larger timeout // Erase is slow, need larger timeout
if (!sendCommand(port, PROTO_CHIP_ERASE, warnOnError, _eraseTimeout)) { if (!sendCommand(port, PROTO_CHIP_ERASE, warnOnError, _eraseTimeout)) {
...@@ -252,7 +254,7 @@ bool PX4Bootloader::erase(QSerialPort* port) ...@@ -252,7 +254,7 @@ bool PX4Bootloader::erase(QSerialPort* port)
return true; return true;
} }
bool PX4Bootloader::program(QSerialPort* port, const QString& firmwareFilename) bool PX4Bootloader::program(QextSerialPort* port, const QString& firmwareFilename)
{ {
QFile firmwareFile(firmwareFilename); QFile firmwareFile(firmwareFilename);
if (!firmwareFile.open(QIODevice::ReadOnly)) { if (!firmwareFile.open(QIODevice::ReadOnly)) {
...@@ -285,11 +287,20 @@ bool PX4Bootloader::program(QSerialPort* port, const QString& firmwareFilename) ...@@ -285,11 +287,20 @@ bool PX4Bootloader::program(QSerialPort* port, const QString& firmwareFilename)
Q_ASSERT(bytesToSend <= 0x8F); Q_ASSERT(bytesToSend <= 0x8F);
if (!write(port, PROTO_PROG_MULTI) || bool failed = true;
!write(port, (uint8_t)bytesToSend) || if (write(port, PROTO_PROG_MULTI)) {
!write(port, imageBuf, bytesToSend) || if (write(port, (uint8_t)bytesToSend)) {
!write(port, PROTO_EOC) || if (write(port, imageBuf, bytesToSend)) {
!getCommandResponse(port, warnOnError)) { if (write(port, PROTO_EOC)) {
port->flush();
if (getCommandResponse(port, warnOnError)) {
failed = false;
}
}
}
}
}
if (failed) {
_errorString = tr("Flash failed: %1").arg(_errorString); _errorString = tr("Flash failed: %1").arg(_errorString);
qWarning() << _errorString; qWarning() << _errorString;
return false; return false;
...@@ -314,7 +325,7 @@ bool PX4Bootloader::program(QSerialPort* port, const QString& firmwareFilename) ...@@ -314,7 +325,7 @@ bool PX4Bootloader::program(QSerialPort* port, const QString& firmwareFilename)
return true; return true;
} }
bool PX4Bootloader::verify(QSerialPort* port, const QString firmwareFilename) bool PX4Bootloader::verify(QextSerialPort* port, const QString firmwareFilename)
{ {
bool ret; bool ret;
...@@ -331,7 +342,7 @@ bool PX4Bootloader::verify(QSerialPort* port, const QString firmwareFilename) ...@@ -331,7 +342,7 @@ bool PX4Bootloader::verify(QSerialPort* port, const QString firmwareFilename)
/// @brief Verify the flash on bootloader version 2 by reading it back and comparing it against /// @brief Verify the flash on bootloader version 2 by reading it back and comparing it against
/// the original firmware file. /// the original firmware file.
bool PX4Bootloader::_bootloaderVerifyRev2(QSerialPort* port, const QString firmwareFilename) bool PX4Bootloader::_bootloaderVerifyRev2(QextSerialPort* port, const QString firmwareFilename)
{ {
QFile firmwareFile(firmwareFilename); QFile firmwareFile(firmwareFilename);
if (!firmwareFile.open(QIODevice::ReadOnly)) { if (!firmwareFile.open(QIODevice::ReadOnly)) {
...@@ -368,11 +379,20 @@ bool PX4Bootloader::_bootloaderVerifyRev2(QSerialPort* port, const QString firmw ...@@ -368,11 +379,20 @@ bool PX4Bootloader::_bootloaderVerifyRev2(QSerialPort* port, const QString firmw
Q_ASSERT(bytesToRead <= 0x8F); Q_ASSERT(bytesToRead <= 0x8F);
if (!write(port, PROTO_READ_MULTI) || bool failed = true;
!write(port, (uint8_t)bytesToRead) || if (write(port, PROTO_READ_MULTI)) {
!write(port, PROTO_EOC) || if (write(port, (uint8_t)bytesToRead)) {
!read(port, flashBuf, sizeof(flashBuf), warnOnError) || if (write(port, PROTO_EOC)) {
!getCommandResponse(port, warnOnError)) { port->flush();
if (read(port, flashBuf, sizeof(flashBuf), warnOnError)) {
if (getCommandResponse(port, warnOnError)) {
failed = false;
}
}
}
}
}
if (failed) {
return false; return false;
} }
...@@ -392,16 +412,23 @@ bool PX4Bootloader::_bootloaderVerifyRev2(QSerialPort* port, const QString firmw ...@@ -392,16 +412,23 @@ bool PX4Bootloader::_bootloaderVerifyRev2(QSerialPort* port, const QString firmw
} }
/// @Brief Verify the flash on a version 3 or higher bootloader board by comparing CRCs. /// @Brief Verify the flash on a version 3 or higher bootloader board by comparing CRCs.
bool PX4Bootloader::_bootloaderVerifyRev3(QSerialPort* port) bool PX4Bootloader::_bootloaderVerifyRev3(QextSerialPort* port)
{ {
return true; return true;
uint8_t buf[2] = { PROTO_GET_CRC, PROTO_EOC }; uint8_t buf[2] = { PROTO_GET_CRC, PROTO_EOC };
quint32 flashCRC; quint32 flashCRC;
if (!write(port, buf, 2) || bool failed = true;
!read(port, (uint8_t*)&flashCRC, sizeof(flashCRC), warnOnError, _verifyTimeout) || if (write(port, buf, 2)) {
!getCommandResponse(port, warnOnError)) { port->flush();
if (read(port, (uint8_t*)&flashCRC, sizeof(flashCRC), warnOnError, _verifyTimeout)) {
if (getCommandResponse(port, warnOnError)) {
failed = false;
}
}
}
if (failed) {
return false; return false;
} }
...@@ -413,19 +440,19 @@ bool PX4Bootloader::_bootloaderVerifyRev3(QSerialPort* port) ...@@ -413,19 +440,19 @@ bool PX4Bootloader::_bootloaderVerifyRev3(QSerialPort* port)
return true; return true;
} }
bool PX4Bootloader::open(QSerialPort* port, const QString portName) bool PX4Bootloader::open(QextSerialPort* port, const QString portName)
{ {
Q_ASSERT(!port->isOpen()); Q_ASSERT(!port->isOpen());
port->setPortName(portName); port->setPortName(portName);
port->setBaudRate(QSerialPort::Baud115200); port->setBaudRate(BAUD115200);
port->setDataBits(QSerialPort::Data8); port->setDataBits(DATA_8);
port->setParity(QSerialPort::NoParity); port->setParity(PAR_NONE);
port->setStopBits(QSerialPort::OneStop); port->setStopBits(STOP_1);
port->setFlowControl(QSerialPort::NoFlowControl); port->setFlowControl(FLOW_OFF);
if (!port->open(QIODevice::ReadWrite)) { if (!port->open(QIODevice::ReadWrite | QIODevice::Unbuffered)) {
_errorString = tr("Open failed on port %1: %2").arg(portName).arg(_serialPortErrorString(port->error())); _errorString = tr("Open failed on port %1: %2").arg(portName).arg(port->errorString());
qWarning() << _errorString; qWarning() << _errorString;
return false; return false;
} }
...@@ -433,20 +460,13 @@ bool PX4Bootloader::open(QSerialPort* port, const QString portName) ...@@ -433,20 +460,13 @@ bool PX4Bootloader::open(QSerialPort* port, const QString portName)
return true; return true;
} }
bool PX4Bootloader::sync(QSerialPort* port) bool PX4Bootloader::sync(QextSerialPort* port)
{ {
// Drain out any remaining input or output from the port
if (!port->clear()) {
_errorString = tr("Unable to clear port");
qWarning() << _errorString;
return false;
}
// Send sync command // Send sync command
return sendCommand(port, PROTO_GET_SYNC, noWarnOnError); return sendCommand(port, PROTO_GET_SYNC, noWarnOnError);
} }
bool PX4Bootloader::getBoardInfo(QSerialPort* port, uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize) bool PX4Bootloader::getBoardInfo(QextSerialPort* port, uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize)
{ {
if (!getBoardInfo(port, INFO_BL_REV, _bootloaderVersion)) { if (!getBoardInfo(port, INFO_BL_REV, _bootloaderVersion)) {
...@@ -482,7 +502,7 @@ Error: ...@@ -482,7 +502,7 @@ Error:
return false; return false;
} }
bool PX4Bootloader::sendBootloaderReboot(QSerialPort* port) bool PX4Bootloader::sendBootloaderReboot(QextSerialPort* port)
{ {
return write(port, PROTO_BOOT) && write(port, PROTO_EOC); return write(port, PROTO_BOOT) && write(port, PROTO_EOC);
} }
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#ifndef PX4Bootloader_H #ifndef PX4Bootloader_H
#define PX4Bootloader_H #define PX4Bootloader_H
#include <QSerialPort> #include "qextserialport.h"
#include <stdint.h> #include <stdint.h>
...@@ -52,56 +52,56 @@ public: ...@@ -52,56 +52,56 @@ public:
/// @param data Bytes to write /// @param data Bytes to write
/// @param maxSize Number of bytes to write /// @param maxSize Number of bytes to write
/// @return true: success /// @return true: success
bool write(QSerialPort* port, const uint8_t* data, qint64 maxSize); bool write(QextSerialPort* port, const uint8_t* data, qint64 maxSize);
bool write(QSerialPort* port, const uint8_t byte); bool write(QextSerialPort* port, const uint8_t byte);
/// @brief Read a set of bytes from the port /// @brief Read a set of bytes from the port
/// @param data Read bytes into this buffer /// @param data Read bytes into this buffer
/// @param maxSize Number of bytes to read /// @param maxSize Number of bytes to read
/// @param warnOnError true: Log error using qWarning /// @param warnOnError true: Log error using qWarning
/// @param readTimeout Msecs to wait for bytes to become available on port /// @param readTimeout Msecs to wait for bytes to become available on port
bool read(QSerialPort* port, uint8_t* data, qint64 maxSize, bool warnOnError, int readTimeout = _readTimout); bool read(QextSerialPort* port, uint8_t* data, qint64 maxSize, bool warnOnError, int readTimeout = _readTimout);
/// @brief Read a PROTO_SYNC command response from the bootloader /// @brief Read a PROTO_SYNC command response from the bootloader
/// @param responseTimeout Msecs to wait for response bytes to become available on port /// @param responseTimeout Msecs to wait for response bytes to become available on port
bool getCommandResponse(QSerialPort* port, bool warnOnError, const int responseTimeout = _responseTimeout); bool getCommandResponse(QextSerialPort* port, bool warnOnError, const int responseTimeout = _responseTimeout);
/// @brief Send a PROTO_GET_DEVICE command to retrieve a value from the bootloader /// @brief Send a PROTO_GET_DEVICE command to retrieve a value from the bootloader
/// @param param Value to retrieve using INFO_BOARD_* enums /// @param param Value to retrieve using INFO_BOARD_* enums
/// @param value Returned value /// @param value Returned value
bool getBoardInfo(QSerialPort* port, uint8_t param, uint32_t& value); bool getBoardInfo(QextSerialPort* port, uint8_t param, uint32_t& value);
/// @brief Send a command to the bootloader /// @brief Send a command to the bootloader
/// @param cmd Command to send using PROTO_* enums /// @param cmd Command to send using PROTO_* enums
/// @return true: Command sent and valid sync response returned /// @return true: Command sent and valid sync response returned
bool sendCommand(QSerialPort* port, uint8_t cmd, bool warnOnError, int responseTimeout = _responseTimeout); bool sendCommand(QextSerialPort* port, uint8_t cmd, bool warnOnError, int responseTimeout = _responseTimeout);
/// @brief Program the board with the specified firmware /// @brief Program the board with the specified firmware
bool program(QSerialPort* port, const QString& firmwareFilename); bool program(QextSerialPort* port, const QString& firmwareFilename);
/// @brief Verify the board flash. How it works depend on bootloader rev /// @brief Verify the board flash. How it works depend on bootloader rev
/// Rev 2: Read the flash back and compare it against the firmware file /// Rev 2: Read the flash back and compare it against the firmware file
/// Rev 3: Compare CRCs for flash and file /// Rev 3: Compare CRCs for flash and file
bool verify(QSerialPort* port, const QString firmwareFilename); bool verify(QextSerialPort* port, const QString firmwareFilename);
/// @brief Read a PROTO_SYNC response from the bootloader /// @brief Read a PROTO_SYNC response from the bootloader
/// @return true: Valid sync response was received /// @return true: Valid sync response was received
bool sync(QSerialPort* port); bool sync(QextSerialPort* port);
/// @brief Retrieve a set of board info from the bootloader /// @brief Retrieve a set of board info from the bootloader
/// @param bootloaderVersion Returned INFO_BL_REV /// @param bootloaderVersion Returned INFO_BL_REV
/// @param boardID Returned INFO_BOARD_ID /// @param boardID Returned INFO_BOARD_ID
/// @param flashSize Returned INFO_FLASH_SIZE /// @param flashSize Returned INFO_FLASH_SIZE
bool getBoardInfo(QSerialPort* port, uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize); bool getBoardInfo(QextSerialPort* port, uint32_t& bootloaderVersion, uint32_t& boardID, uint32_t& flashSize);
/// @brief Opens a port to the bootloader /// @brief Opens a port to the bootloader
bool open(QSerialPort* port, const QString portName); bool open(QextSerialPort* port, const QString portName);
/// @brief Sends a PROTO_REBOOT command to the bootloader /// @brief Sends a PROTO_REBOOT command to the bootloader
bool sendBootloaderReboot(QSerialPort* port); bool sendBootloaderReboot(QextSerialPort* port);
/// @brief Sends a PROTO_ERASE command to the bootlader /// @brief Sends a PROTO_ERASE command to the bootlader
bool erase(QSerialPort* port); bool erase(QextSerialPort* port);
signals: signals:
/// @brief Signals progress indicator for long running bootloader utility routines /// @brief Signals progress indicator for long running bootloader utility routines
...@@ -148,8 +148,8 @@ private: ...@@ -148,8 +148,8 @@ private:
bool _findBootloader(void); bool _findBootloader(void);
bool _downloadFirmware(void); bool _downloadFirmware(void);
bool _bootloaderVerifyRev2(QSerialPort* port, const QString firmwareFilename); bool _bootloaderVerifyRev2(QextSerialPort* port, const QString firmwareFilename);
bool _bootloaderVerifyRev3(QSerialPort* port); bool _bootloaderVerifyRev3(QextSerialPort* port);
const char* _serialPortErrorString(int error); const char* _serialPortErrorString(int error);
...@@ -166,7 +166,7 @@ private: ...@@ -166,7 +166,7 @@ private:
QString _errorString; ///< Last error QString _errorString; ///< Last error
static const struct serialPortErrorString _rgSerialPortErrors[14]; ///< Translation of QSerialPort::SerialPortError into string static const struct serialPortErrorString _rgSerialPortErrors[14]; ///< Translation of QextSerialPort::SerialPortError into string
static const int _eraseTimeout = 20000; ///< Msecs to wait for response from erase command static const int _eraseTimeout = 20000; ///< Msecs to wait for response from erase command
static const int _rebootTimeout = 10000; ///< Msecs to wait for reboot command to cause serial port to disconnect static const int _rebootTimeout = 10000; ///< Msecs to wait for reboot command to cause serial port to disconnect
......
...@@ -91,7 +91,8 @@ const PX4FirmwareUpgrade::stateMachineEntry* PX4FirmwareUpgrade::_getStateMachin ...@@ -91,7 +91,8 @@ const PX4FirmwareUpgrade::stateMachineEntry* PX4FirmwareUpgrade::_getStateMachin
static const char* msgFirmwareBoardEraseFailed = "Board erase failed."; static const char* msgFirmwareBoardEraseFailed = "Board erase failed.";
static const char* msgFirmwareBoardFlashing = "Flashing new firmware onto board..."; static const char* msgFirmwareBoardFlashing = "Flashing new firmware onto board...";
static const char* msgFirmwareBoardFlashError = "A failure has occured while flashing the new firmware to your board. " static const char* msgFirmwareBoardFlashError = "A failure has occured while flashing the new firmware to your board. "
"This has left the board in an inconsistent state. " "This has left the board in an inconsistent state.\n\n"
"There currently is an known issue which does not yet have a fix which may cause this.\n\n"
"You should click 'Try Again' to attempt the upgrade process again."; "You should click 'Try Again' to attempt the upgrade process again.";
static const char* msgFirmwareBoardVerifying = "Verifying firmware on board..."; static const char* msgFirmwareBoardVerifying = "Verifying firmware on board...";
static const char* msgFirmwareBoardVerifyError = "Verification of flash memory on board failed. " static const char* msgFirmwareBoardVerifyError = "Verification of flash memory on board failed. "
......
...@@ -30,11 +30,12 @@ ...@@ -30,11 +30,12 @@
#include <QWidget> #include <QWidget>
#include <QUrl> #include <QUrl>
#include <QSerialPort>
#include <QTimer> #include <QTimer>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include "qextserialport.h"
#include <stdint.h> #include <stdint.h>
#include "PX4FirmwareUpgradeThread.h" #include "PX4FirmwareUpgradeThread.h"
......
...@@ -99,7 +99,7 @@ void PX4FirmwareUpgradeThreadWorker::_findBoardOnce(void) ...@@ -99,7 +99,7 @@ void PX4FirmwareUpgradeThreadWorker::_findBoardOnce(void)
qDebug() << "\tvendor ID:" << info.vendorIdentifier(); qDebug() << "\tvendor ID:" << info.vendorIdentifier();
qDebug() << "\tproduct ID:" << info.productIdentifier(); qDebug() << "\tproduct ID:" << info.productIdentifier();
portName = info.portName(); portName = info.systemLocation();
portDescription = info.description(); portDescription = info.description();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
...@@ -134,7 +134,7 @@ void PX4FirmwareUpgradeThreadWorker::_findBootloaderOnce(void) ...@@ -134,7 +134,7 @@ void PX4FirmwareUpgradeThreadWorker::_findBootloaderOnce(void)
uint32_t bootloaderVersion, boardID, flashSize; uint32_t bootloaderVersion, boardID, flashSize;
_bootloaderPort = new QSerialPort; _bootloaderPort = new QextSerialPort(QextSerialPort::Polling);
Q_CHECK_PTR(_bootloaderPort); Q_CHECK_PTR(_bootloaderPort);
if (_bootloader->open(_bootloaderPort, _portName)) { if (_bootloader->open(_bootloaderPort, _portName)) {
...@@ -149,7 +149,7 @@ void PX4FirmwareUpgradeThreadWorker::_findBootloaderOnce(void) ...@@ -149,7 +149,7 @@ void PX4FirmwareUpgradeThreadWorker::_findBootloaderOnce(void)
_bootloaderPort->close(); _bootloaderPort->close();
_bootloaderPort->deleteLater(); _bootloaderPort->deleteLater();
_bootloaderPort = NULL; _bootloaderPort = NULL;
qDebug() << "Bootloader error:" << _bootloader->errorString(); qDebug() << "Bootloader Get Board Info error:" << _bootloader->errorString();
emit error(commandBootloader, _bootloader->errorString()); emit error(commandBootloader, _bootloader->errorString());
return; return;
} }
......
...@@ -29,11 +29,12 @@ ...@@ -29,11 +29,12 @@
#define PX4FirmwareUpgradeThread_H #define PX4FirmwareUpgradeThread_H
#include <QObject> #include <QObject>
#include <QSerialPort>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <QTime> #include <QTime>
#include "qextserialport.h"
#include <stdint.h> #include <stdint.h>
#include "PX4Bootloader.h" #include "PX4Bootloader.h"
...@@ -85,7 +86,7 @@ private slots: ...@@ -85,7 +86,7 @@ private slots:
private: private:
PX4Bootloader* _bootloader; PX4Bootloader* _bootloader;
QSerialPort* _bootloaderPort; QextSerialPort* _bootloaderPort;
QTimer* _timerTimeout; QTimer* _timerTimeout;
QTimer* _timerRetry; QTimer* _timerRetry;
QTime _elapsed; QTime _elapsed;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment