nyholku / purejavacomm

Pure Java implementation of JavaComm SerialPort
http://www.sparetimelabs.com/purejavacomm/index.html
BSD 3-Clause "New" or "Revised" License
366 stars 146 forks source link

Error 87 on Windows #122

Open innot opened 5 years ago

innot commented 5 years ago

This issue is probably related to issue #104 PureJavaSerialPort initializing fails for STM32 Virtual USB COM Port Driver

I have been experimenting with PureJavaCom on Windows 10 using virtual Com Ports, but it would not work for me. I have been using the SimpleWrite.java example from Sun

Debugging showed, that the problem was in the WinAPI.java class in the static public boolean SetCommState(HANDLE hFile, DCB dcb) method at line 779: _res = mK32lib.SetCommState(hFile, dcb);

This always threw a LastErrorException with Code 87 Invalid Parameter.

This was caused by the DCB fields XonChar and XoffChar both being 0x00. But the Windows API documentation for the SetCommState Function states:

The SetCommState function fails if the XonChar member of the DCB structure is equal to the XoffChar member.

A quick fix for this issue is to set the XonChar and XoffChar fields in the DCB class:

diff --git a/src/jtermios/windows/WinAPI.java b/src/jtermios/windows/WinAPI.java
index 101aabc..1bd9007 100644
--- a/src/jtermios/windows/WinAPI.java
+++ b/src/jtermios/windows/WinAPI.java
@@ -505,6 +505,6 @@
        public byte Parity;
        public byte StopBits;
-       public byte XonChar;
-       public byte XoffChar;
+       public byte XonChar = 0x11;
+       public byte XoffChar = 0x13;
        public byte ErrorChar;
        public byte EofChar;

The bug probably depends on how purejavacomm is used, i.e. in which order the purejavacomm functions are called. There seem to be multiple execution paths to the SetCommState() method, and at least the one thru CommPortIdentifier.getPortIdentifier(String) does not instantiate the PureJavaSerialPort() class where XonChar and XoffChar would be initialized. But I do not have the expertise (and time) to really check this. My small hack should work okay and should not have any side effects.

Testing my quick fix I found another small issue in the jtermios.windows.testsuite.TestSuite.java class which causes the same Error 87 (for the same reason) - a typo where XonChar is set twice. The following patch fixes this and the test now works for me

diff --git a/src/jtermios/windows/testsuite/TestSuite.java b/src/jtermios/windows/testsuite/TestSuite.java
old mode 100755
new mode 100644
index 76e1c1c..14f2787
--- a/src/jtermios/windows/testsuite/TestSuite.java
+++ b/src/jtermios/windows/testsuite/TestSuite.java
@@ -87,5 +87,5 @@
        dcb.XonChar = 0x11;
        dcb.StopBits = ONESTOPBIT;
-       dcb.XonChar = 0x13;
+       dcb.XoffChar = 0x13;

        check(SetCommState(hComm, dcb), "SetCommState ");

Best regards,

Thomas

innot commented 5 years ago

My little hack works for my simple test app and also for the jtermios.windows.testsuite.TestSuite.java tests, but still fails with the same problem running the full purejavacomm.testsuite. The DCB.XonChar and XoffChar get overwritten with 0x00 on a different path to SetCommState().

Back to the drawing board.

innot commented 5 years ago

New hack to fix the issue:

diff --git a/src/jtermios/windows/JTermiosImpl.java b/src/jtermios/windows/JTermiosImpl.java
index 28fe18c..a57d0f2 100644
--- a/src/jtermios/windows/JTermiosImpl.java
+++ b/src/jtermios/windows/JTermiosImpl.java
@@ -155,6 +155,8 @@
                        cfsetospeed(m_Termios, B9600);
                        m_Termios.c_cc[VTIME] = 0;
                        m_Termios.c_cc[VMIN] = 0;
+                       m_Termios.c_cc[VSTART] = 0x11;
+                       m_Termios.c_cc[VSTOP] = 0x13;
                        updateFromTermios(this);

                        WaitCommEventCancelObject = CreateEvent(null, false, false, null);

As this is in the open() Method I would guess that all execution paths should run through this method.

With this fix the purejavacomm TestSuite runs almost completly, just Test9 is failing but I guess that this is a different issue (Windows Error 995 ERROR_OPERATION_ABORTED in the select() method)