nyholku / purejavacomm

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

Fails on Apple Silicon #142

Open bobjacobsen opened 1 year ago

bobjacobsen commented 1 year ago

JMRI is an application that works cross-platform with PureJavaComm just fine across multiple platforms, including macOS Intel and macOS Rosetta2 on Apple Silicon.

When running on Apple Silicon, attempting to set a control line on a port gives:

     [java] purejavacomm.PureJavaIllegalStateException: ioctl(m_FD, TIOCMGET, m_ioctl) == -1
     [java]     at purejavacomm.PureJavaSerialPort.setControlLineState(PureJavaSerialPort.java:1277)
     [java]     at purejavacomm.PureJavaSerialPort.setRTS(PureJavaSerialPort.java:313)
     [java]     at jmri.jmrix.AbstractSerialPortController.configureLeadsAndFlowControl(AbstractSerialPortController.java:121)
     [java]     at jmri.jmrix.AbstractSerialPortController.configureLeadsAndFlowControl(AbstractSerialPortController.java:142)

The JMRI code line where it occurs:

        serialPort.setRTS(rts);

The port seems to have been successfully opened. At least, there was no exception at that point.

If I bypass setting the control lines, I get an I/O exception the first time I attempt to access the port's input stream:

     [java] java.io.IOException
     [java]     at purejavacomm.PureJavaSerialPort$2.available(PureJavaSerialPort.java:721)
     [java]     at jmri.jmrix.AbstractPortController.purgeStream(AbstractPortController.java:566)

Happens with Azul JDKs from Java 11 (earliest supported by application) through JDK 20. Observed on macOS Ventura with both M1 and M2 chips.

Working on debugging this, but would greatly appreciate any suggestions on how to go about it.

nyholku commented 1 year ago

On 28. Jun 2023, at 15.44, Bob Jacobsen @.***> wrote:

JMRI is an application that works cross-platform with PureJavaComm just fine across multiple platforms, including macOS Intel and macOS Rosetta2 on Apple Silicon.

When running on Apple Silicon, attempting to set a control line on a port gives:

 [java] purejavacomm.PureJavaIllegalStateException: ioctl(m_FD, TIOCMGET, m_ioctl) == -1
 [java]   at purejavacomm.PureJavaSerialPort.setControlLineState(PureJavaSerialPort.java:1277)
 [java]   at purejavacomm.PureJavaSerialPort.setRTS(PureJavaSerialPort.java:313)
 [java]   at jmri.jmrix.AbstractSerialPortController.configureLeadsAndFlowControl(AbstractSerialPortController.java:121)
 [java]   at jmri.jmrix.AbstractSerialPortController.configureLeadsAndFlowControl(AbstractSerialPortController.java:142)

The JMRI code line where it occurs:

    serialPort.setRTS(rts);

The port seems to have been successfully opened. At least, there was no exception at that point.

If I bypass setting the control lines, I get an I/O exception the first time I attempt to access the port's input stream:

 [java] java.io.IOException
 [java]   at purejavacomm.PureJavaSerialPort$2.available(PureJavaSerialPort.java:721)
 [java]   at jmri.jmrix.AbstractPortController.purgeStream(AbstractPortController.java:566)

Happens with Azul JDKs from Java 11 (earliest supported by application) through JDK 20. Observed on macOS Ventura with both M1 and M2 chips.

Working on debugging this, but would greatly appreciate any suggestions on how to go about it.

— Reply to this email directly, view it on GitHub https://github.com/nyholku/purejavacomm/issues/142, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFP7LNDYFNXPJAH5S6DGVTXNQRMNANCNFSM6AAAAAAZXCNWLE. You are receiving this because you are subscribed to this thread.

On 28. Jun 2023, at 15.44, Bob Jacobsen @.***> wrote:

JMRI is an application that works cross-platform with PureJavaComm just fine across multiple platforms, including macOS Intel and macOS Rosetta2 on Apple Silicon.

When running on Apple Silicon, attempting to set a control line on a port gives:

 [java] purejavacomm.PureJavaIllegalStateException: ioctl(m_FD, TIOCMGET, m_ioctl) == -1
 [java]   at purejavacomm.PureJavaSerialPort.setControlLineState(PureJavaSerialPort.java:1277)
 [java]   at purejavacomm.PureJavaSerialPort.setRTS(PureJavaSerialPort.java:313)
 [java]   at jmri.jmrix.AbstractSerialPortController.configureLeadsAndFlowControl(AbstractSerialPortController.java:121)
 [java]   at jmri.jmrix.AbstractSerialPortController.configureLeadsAndFlowControl(AbstractSerialPortController.java:142)

The JMRI code line where it occurs:

    serialPort.setRTS(rts);

The port seems to have been successfully opened. At least, there was no exception at that point.

If I bypass setting the control lines, I get an I/O exception the first time I attempt to access the port's input stream:

 [java] java.io.IOException
 [java]   at purejavacomm.PureJavaSerialPort$2.available(PureJavaSerialPort.java:721)
 [java]   at jmri.jmrix.AbstractPortController.purgeStream(AbstractPortController.java:566)

Happens with Azul JDKs from Java 11 (earliest supported by application) through JDK 20. Observed on macOS Ventura with both M1 and M2 chips.

Working on debugging this, but would greatly appreciate any suggestions on how to go about it.

— Reply to this email directly, view it on GitHub https://github.com/nyholku/purejavacomm/issues/142, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFP7LNDYFNXPJAH5S6DGVTXNQRMNANCNFSM6AAAAAAZXCNWLE. You are receiving this because you are subscribed to this thread.Hi Bob,

Thanks for reaching out to me.

It so happens that I have a M2 Mac so I might have a look at this in during coming vacation.

Meanwhile, my hunch is that there is something wrong with the ioctl().

Maybe the signature parameter mapping from Java type to the actual C type is wrong and has so far worked by fluke.

The actual mapping is here:

https://github.com/nyholku/purejavacomm/blob/d3b6903ec2e220d63e55a06ae6bf6ced929d961e/src/jtermios/macosx/JTermiosImpl.java#L117C12-L117C12

Ref doc for mappings is here:

https://java-native-access.github.io/jna/4.2.1/overview-summary.html#pointers

If you google for "man ioctl' you will see the ioctl call C signature, which is kind of not too helpful.

I suspect the third parameter but this is pure conjecture.

You might ask on the the JNA list if the Java

    public int ioctl(int fd, NativeLong cmd, int[] arg);

matches the C:

   int ioctl(int fd, unsigned long request, ...);

You could/should also check the errno to see why the ioctl fails.

In any case you should work the ioctl failure because if that fails, nothing is going to work.

wbr Kusti

bobjacobsen commented 1 year ago

Thank you for the quick reply!

This is the minimal test program I'm using:

import purejavacomm.*;
import java.io.*;

class JavaCommTest {

    static String portName = "cu.Bluetooth-Incoming-Port";
    // static String portName = "cu.usbmodem2100412E1";

    public static void main(String[] args) {
        System.out.println("starting");

        SerialPort activeSerialPort = null;

        try {
            // get and open the primary port
            CommPortIdentifier portID = CommPortIdentifier.getPortIdentifier(portName);
            try {
                activeSerialPort = (SerialPort) portID.open("JMRI", 2000);  // name of program, msec to wait
            } catch (PortInUseException p) {
                System.err.println("PortInUseException");
            }

            // These operations pass
            try {
                activeSerialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
            } catch (UnsupportedCommOperationException e) {
                System.err.println("Setting serial params failed");
            }

            // These operations fail
            activeSerialPort.setRTS(true);
            activeSerialPort.setDTR(true);

        } catch (NoSuchPortException e1) {
            System.err.println("NoSuchPortException "+e1);
            e1.printStackTrace();
        } catch (RuntimeException e2) {
            System.err.println("RuntimeException "+e2);
            e2.printStackTrace();
        }

        System.out.println("ending");
    }
}

It gives:

starting
ending
RuntimeException purejavacomm.PureJavaIllegalStateException: ioctl(m_FD, TIOCMGET, m_ioctl) == -1
purejavacomm.PureJavaIllegalStateException: ioctl(m_FD, TIOCMGET, m_ioctl) == -1
    at purejavacomm.PureJavaSerialPort.setControlLineState(PureJavaSerialPort.java:1277)
    at purejavacomm.PureJavaSerialPort.setRTS(PureJavaSerialPort.java:313)
    at JavaCommTest.main(JavaCommTest.java:31)

Thanks again.

bobjacobsen commented 1 year ago

Using com.sun.jna.Native.getLastError() I find that the errno is 14:

 14 EFAULT Bad address. The system detected an invalid address in attempting to use an argument of a call.
nyholku commented 1 year ago

On 28. Jun 2023, at 18.31, Bob Jacobsen @.***> wrote:

Using com.sun.jna.Native.getLastError() I find that the errno is 14:

14 EFAULT Bad address. The system detected an invalid address in attempting to use an argument of a call. — Reply to this email directly, view it on GitHub https://github.com/nyholku/purejavacomm/issues/142#issuecomment-1611655183, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFP7LIMUM7CWUEZP6O7TLLXNRE4DANCNFSM6AAAAAAZXCNWLE. You are receiving this because you commented.

I think you should take this up on the JNA list . Something wrong with the 3rd argument I'm guessing.

wbr Kusti

bobjacobsen commented 1 year ago

See a possible solution descend on the JNA list. There's an odd dependence on debugging output, unfortunately.

nyholku commented 1 year ago

On 1. Jul 2023, at 18.03, Bob Jacobsen @.***> wrote:

See a possible solution descend on the JNA list https://groups.google.com/g/jna-users/c/wF4aJ5ViQJE/m/u1b9dl4rAAAJ. There's an odd dependence on debugging output, unfortunately.

— Reply to this email directly, view it on GitHub https://github.com/nyholku/purejavacomm/issues/142#issuecomment-1615951899, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFP7LI56WIIFIM6TW6IYADXOA35XANCNFSM6AAAAAAZXCNWLE. You are receiving this because you commented.

Thanks, I saw it. Yeah, that looks suspicious.