Browse Source

initial commit

lennlouis 3 months ago
commit
e40aec514c
51 changed files with 6034 additions and 0 deletions
  1. 47 0
      .gitignore
  2. 267 0
      ChangeLog
  3. 94 0
      LICENSE
  4. 64 0
      README
  5. 12 0
      doc/doc.pri
  6. 16 0
      doc/examples/enumerator.qdoc
  7. BIN
      doc/examples/images/uartassistant.png
  8. 7 0
      doc/examples/qespta.qdoc
  9. 24 0
      doc/examples/uartassistant.qdoc
  10. 199 0
      doc/index.qdoc
  11. 53 0
      doc/qextserialport.qdocconf
  12. 137 0
      doc/style/style.css
  13. 6 0
      examples/enumerator/enumerator.pro
  14. 31 0
      examples/enumerator/main.cpp
  15. 43 0
      examples/event/PortListener.cpp
  16. 26 0
      examples/event/PortListener.h
  17. 7 0
      examples/event/event.pro
  18. 19 0
      examples/event/main.cpp
  19. 5 0
      examples/examples.pro
  20. 61 0
      examples/qespta/MainWindow.cpp
  21. 38 0
      examples/qespta/MainWindow.h
  22. 102 0
      examples/qespta/MessageWindow.cpp
  23. 84 0
      examples/qespta/MessageWindow.h
  24. 128 0
      examples/qespta/QespTest.cpp
  25. 36 0
      examples/qespta/QespTest.h
  26. 4 0
      examples/qespta/README
  27. 30 0
      examples/qespta/main.cpp
  28. 14 0
      examples/qespta/qespta.pro
  29. 179 0
      examples/uartassistant/dialog.cpp
  30. 45 0
      examples/uartassistant/dialog.h
  31. 191 0
      examples/uartassistant/dialog.ui
  32. 133 0
      examples/uartassistant/hled.cpp
  33. 34 0
      examples/uartassistant/hled.h
  34. 11 0
      examples/uartassistant/main.cpp
  35. 22 0
      examples/uartassistant/uartassistant.pro
  36. 24 0
      extserialport.prf.in
  37. 96 0
      qextserialport.pro
  38. 163 0
      src/qextserialenumerator.cpp
  39. 72 0
      src/qextserialenumerator.h
  40. 210 0
      src/qextserialenumerator_linux.cpp
  41. 307 0
      src/qextserialenumerator_osx.cpp
  42. 120 0
      src/qextserialenumerator_p.h
  43. 56 0
      src/qextserialenumerator_unix.cpp
  44. 318 0
      src/qextserialenumerator_win.cpp
  45. 1013 0
      src/qextserialport.cpp
  46. 248 0
      src/qextserialport.h
  47. 36 0
      src/qextserialport.pri
  48. 72 0
      src/qextserialport_global.h
  49. 253 0
      src/qextserialport_p.h
  50. 469 0
      src/qextserialport_unix.cpp
  51. 408 0
      src/qextserialport_win.cpp

+ 47 - 0
.gitignore

@@ -0,0 +1,47 @@
+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.*

+ 267 - 0
ChangeLog

@@ -0,0 +1,267 @@
+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.

+ 94 - 0
LICENSE

@@ -0,0 +1,94 @@
+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.

+ 64 - 0
README

@@ -0,0 +1,64 @@
+= 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)
+

+ 12 - 0
doc/doc.pri

@@ -0,0 +1,12 @@
+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"
+

+ 16 - 0
doc/examples/enumerator.qdoc

@@ -0,0 +1,16 @@
+/*!
+    \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
+*/
+

BIN
doc/examples/images/uartassistant.png


+ 7 - 0
doc/examples/qespta.qdoc

@@ -0,0 +1,7 @@
+/*!
+    \example examples/qespta
+    \title qespta Demo
+
+    The example demonstrates how to use QextSerialPort.
+*/
+

+ 24 - 0
doc/examples/uartassistant.qdoc

@@ -0,0 +1,24 @@
+/*!
+    \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
+*/
+

+ 199 - 0
doc/index.qdoc

@@ -0,0 +1,199 @@
+/*!
+    \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
+*/

+ 53 - 0
doc/qextserialport.qdocconf

@@ -0,0 +1,53 @@
+# 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>"

+ 137 - 0
doc/style/style.css

@@ -0,0 +1,137 @@
+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;
+}

+ 6 - 0
examples/enumerator/enumerator.pro

@@ -0,0 +1,6 @@
+TEMPLATE = app
+DEPENDPATH += .
+CONFIG += console
+include(../../src/qextserialport.pri)
+SOURCES += main.cpp
+

+ 31 - 0
examples/enumerator/main.cpp

@@ -0,0 +1,31 @@
+/**
+ * @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;
+}
+

+ 43 - 0
examples/event/PortListener.cpp

@@ -0,0 +1,43 @@
+
+#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";
+}

+ 26 - 0
examples/event/PortListener.h

@@ -0,0 +1,26 @@
+
+
+
+#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_*/

+ 7 - 0
examples/event/event.pro

@@ -0,0 +1,7 @@
+TEMPLATE = app
+DEPENDPATH += .
+CONFIG += console
+include(../../src/qextserialport.pri)
+
+SOURCES += main.cpp PortListener.cpp
+HEADERS += PortListener.h

+ 19 - 0
examples/event/main.cpp

@@ -0,0 +1,19 @@
+/**
+ * @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();
+}

+ 5 - 0
examples/examples.pro

@@ -0,0 +1,5 @@
+TEMPLATE = subdirs
+SUBDIRS = qespta enumerator \
+    uartassistant
+win32:SUBDIRS += event
+

+ 61 - 0
examples/qespta/MainWindow.cpp

@@ -0,0 +1,61 @@
+/**
+ * @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);
+}
+

+ 38 - 0
examples/qespta/MainWindow.h

@@ -0,0 +1,38 @@
+/**
+ * @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_*/
+

+ 102 - 0
examples/qespta/MessageWindow.cpp

@@ -0,0 +1,102 @@
+/**
+ * @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;
+}

+ 84 - 0
examples/qespta/MessageWindow.h

@@ -0,0 +1,84 @@
+/**
+ * @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_*/

+ 128 - 0
examples/qespta/QespTest.cpp

@@ -0,0 +1,128 @@
+/* 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());
+}
+

+ 36 - 0
examples/qespta/QespTest.h

@@ -0,0 +1,36 @@
+/* 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

+ 4 - 0
examples/qespta/README

@@ -0,0 +1,4 @@
+This is simple application using QextSerialPort library.
+
+Port settings are in QespTest constructor (QespTest.cpp)
+

+ 30 - 0
examples/qespta/main.cpp

@@ -0,0 +1,30 @@
+/**
+ * @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();
+}
+
+

+ 14 - 0
examples/qespta/qespta.pro

@@ -0,0 +1,14 @@
+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)

+ 179 - 0
examples/uartassistant/dialog.cpp

@@ -0,0 +1,179 @@
+#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]

+ 45 - 0
examples/uartassistant/dialog.h

@@ -0,0 +1,45 @@
+#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

+ 191 - 0
examples/uartassistant/dialog.ui

@@ -0,0 +1,191 @@
+<?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>

+ 133 - 0
examples/uartassistant/hled.cpp

@@ -0,0 +1,133 @@
+#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;
+}
+

+ 34 - 0
examples/uartassistant/hled.h

@@ -0,0 +1,34 @@
+#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

+ 11 - 0
examples/uartassistant/main.cpp

@@ -0,0 +1,11 @@
+#include <QApplication>
+#include "dialog.h"
+
+int main(int argc, char *argv[])
+{
+    QApplication a(argc, argv);
+    Dialog w;
+    w.show();
+
+    return a.exec();
+}

+ 22 - 0
examples/uartassistant/uartassistant.pro

@@ -0,0 +1,22 @@
+#-------------------------------------------------
+#
+# 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

+ 24 - 0
extserialport.prf.in

@@ -0,0 +1,24 @@
+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

+ 96 - 0
qextserialport.pro

@@ -0,0 +1,96 @@
+############################### *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

+ 163 - 0
src/qextserialenumerator.cpp

@@ -0,0 +1,163 @@
+/****************************************************************************
+** Copyright (c) 2000-2003 Wayne Roth
+** Copyright (c) 2004-2007 Stefan Sander
+** Copyright (c) 2007 Michal Policht
+** Copyright (c) 2008 Brandon Fosdick
+** Copyright (c) 2009-2010 Liam Staskawicz
+** Copyright (c) 2011 Debao Zhang
+** All right reserved.
+** Web: http://code.google.com/p/qextserialport/
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+****************************************************************************/
+
+#include "qextserialenumerator.h"
+#include "qextserialenumerator_p.h"
+
+#include <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"

+ 72 - 0
src/qextserialenumerator.h

@@ -0,0 +1,72 @@
+/****************************************************************************
+** Copyright (c) 2000-2003 Wayne Roth
+** Copyright (c) 2004-2007 Stefan Sander
+** Copyright (c) 2007 Michal Policht
+** Copyright (c) 2008 Brandon Fosdick
+** Copyright (c) 2009-2010 Liam Staskawicz
+** Copyright (c) 2011 Debao Zhang
+** All right reserved.
+** Web: http://code.google.com/p/qextserialport/
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+****************************************************************************/
+
+#ifndef _QEXTSERIALENUMERATOR_H_
+#define _QEXTSERIALENUMERATOR_H_
+
+#include <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_*/

+ 210 - 0
src/qextserialenumerator_linux.cpp

@@ -0,0 +1,210 @@
+/****************************************************************************
+** Copyright (c) 2000-2003 Wayne Roth
+** Copyright (c) 2004-2007 Stefan Sander
+** Copyright (c) 2007 Michal Policht
+** Copyright (c) 2008 Brandon Fosdick
+** Copyright (c) 2009-2010 Liam Staskawicz
+** Copyright (c) 2011 Debao Zhang
+** Copyright (c) 2012 Doug Brown
+** All right reserved.
+** Web: http://code.google.com/p/qextserialport/
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+****************************************************************************/
+
+#include "qextserialenumerator.h"
+#include "qextserialenumerator_p.h"
+#include <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

+ 307 - 0
src/qextserialenumerator_osx.cpp

@@ -0,0 +1,307 @@
+/****************************************************************************
+** Copyright (c) 2000-2003 Wayne Roth
+** Copyright (c) 2004-2007 Stefan Sander
+** Copyright (c) 2007 Michal Policht
+** Copyright (c) 2008 Brandon Fosdick
+** Copyright (c) 2009-2010 Liam Staskawicz
+** Copyright (c) 2011 Debao Zhang
+** All right reserved.
+** Web: http://code.google.com/p/qextserialport/
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+****************************************************************************/
+
+#include "qextserialenumerator.h"
+#include "qextserialenumerator_p.h"
+#include <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;
+}
+

+ 120 - 0
src/qextserialenumerator_p.h

@@ -0,0 +1,120 @@
+/****************************************************************************
+** 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_

+ 56 - 0
src/qextserialenumerator_unix.cpp

@@ -0,0 +1,56 @@
+/****************************************************************************
+** Copyright (c) 2000-2003 Wayne Roth
+** Copyright (c) 2004-2007 Stefan Sander
+** Copyright (c) 2007 Michal Policht
+** Copyright (c) 2008 Brandon Fosdick
+** Copyright (c) 2009-2010 Liam Staskawicz
+** Copyright (c) 2011 Debao Zhang
+** All right reserved.
+** Web: http://code.google.com/p/qextserialport/
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+****************************************************************************/
+
+#include "qextserialenumerator.h"
+#include "qextserialenumerator_p.h"
+#include <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;
+}

+ 318 - 0
src/qextserialenumerator_win.cpp

@@ -0,0 +1,318 @@
+/****************************************************************************
+** 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;
+}

+ 1013 - 0
src/qextserialport.cpp

@@ -0,0 +1,1013 @@
+/****************************************************************************
+** 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");
+    case E_OS_SPECIFIC:
+        return tr("OS error: %1").arg(d->lastOSErrString);
+    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"

+ 248 - 0
src/qextserialport.h

@@ -0,0 +1,248 @@
+/****************************************************************************
+** 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
+#define E_OS_SPECIFIC               18  // Error did not translate, os error code in lastOSErr
+
+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,
+
+    BAUD460800 = 460800,
+    BAUD230400 = 230400,
+
+    BAUD921600 = 921600,
+    BAUD1500000 = 1500000,
+    BAUD2000000 = 2000000
+};
+
+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 lastOpenOSError() 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

+ 36 - 0
src/qextserialport.pri

@@ -0,0 +1,36 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+
+PUBLIC_HEADERS         += $$PWD/qextserialport.h \
+                          $$PWD/qextserialenumerator.h \
+                          $$PWD/qextserialport_global.h
+
+HEADERS                += $$PUBLIC_HEADERS \
+                          $$PWD/qextserialport_p.h \
+                          $$PWD/qextserialenumerator_p.h \
+
+SOURCES                += $$PWD/qextserialport.cpp \
+                          $$PWD/qextserialenumerator.cpp
+unix {
+    SOURCES            += $$PWD/qextserialport_unix.cpp
+    linux* {
+        SOURCES        += $$PWD/qextserialenumerator_linux.cpp
+    } else:macx {
+        SOURCES        += $$PWD/qextserialenumerator_osx.cpp
+    } else {
+        SOURCES        += $$PWD/qextserialenumerator_unix.cpp
+    }
+}
+win32:SOURCES          += $$PWD/qextserialport_win.cpp \
+                          $$PWD/qextserialenumerator_win.cpp
+
+linux*{
+    !qesp_linux_udev:DEFINES += QESP_NO_UDEV
+    qesp_linux_udev: LIBS += -ludev
+}
+
+macx:LIBS              += -framework IOKit -framework CoreFoundation
+win32:LIBS             += -lsetupapi -ladvapi32 -luser32
+
+# moc doesn't detect Q_OS_LINUX correctly, so add this to make it work
+linux*:DEFINES += __linux__

+ 72 - 0
src/qextserialport_global.h

@@ -0,0 +1,72 @@
+/****************************************************************************
+** Copyright (c) 2000-2003 Wayne Roth
+** Copyright (c) 2004-2007 Stefan Sander
+** Copyright (c) 2007 Michal Policht
+** Copyright (c) 2008 Brandon Fosdick
+** Copyright (c) 2009-2010 Liam Staskawicz
+** Copyright (c) 2011 Debao Zhang
+** All right reserved.
+** Web: http://code.google.com/p/qextserialport/
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+****************************************************************************/
+
+#ifndef QEXTSERIALPORT_GLOBAL_H
+#define QEXTSERIALPORT_GLOBAL_H
+
+#include <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
+

+ 253 - 0
src/qextserialport_p.h

@@ -0,0 +1,253 @@
+/****************************************************************************
+** 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;
+    ulong lastOSErr;
+    QString lastOSErrString;
+    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_

+ 469 - 0
src/qextserialport_unix.cpp

@@ -0,0 +1,469 @@
+/****************************************************************************
+** 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()
+{
+#ifdef __android__
+    ::ioctl(fd, TCSADRAIN, &currentTermios);
+#else
+    ::tcdrain(fd);
+#endif
+    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;
+    default:
+        lastOSErr = error;
+        lastOSErrString = strerror(error);
+        lastErr = E_OS_SPECIFIC;
+        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;
+}

+ 408 - 0
src/qextserialport_win.cpp

@@ -0,0 +1,408 @@
+/****************************************************************************
+** 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();
+                translateError(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;
+    }
+    translateError(GetLastError());
+    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)
+{
+    lastOSErr = error;
+    lastErr = E_OS_SPECIFIC;
+    
+    char buf[256];
+    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
+                  FORMAT_MESSAGE_IGNORE_INSERTS,
+                  NULL,
+                  error,
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                  buf, sizeof(buf), 
+				  NULL);
+    lastOSErrString = buf;
+}
+
+/*
+    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;
+
+	translateError(GetLastError());
+	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;
+
+	translateError(GetLastError());
+    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;
+}