node-serialport icon indicating copy to clipboard operation
node-serialport copied to clipboard

Apple M1 Mac OS X Big Sur FT232R can't control DTR / CTS

Open brandonros opened this issue 3 years ago • 6 comments

image

image

https://www.amazon.com/gp/product/B00IJXZQ7C/

I made this test case to see what is happening at the ioctl level

#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

int main ()
{
    char fd, ret, flags;

    // open device
    if ((fd = open("/dev/tty.usbserial-A50285BI", O_RDWR | O_NDELAY)) < 0)
    {
        fprintf(stderr, "failed to open device");
        return -1;
    }

    // Get the current state of the bits
    ioctl(fd, TIOCMGET, &flags);
    fprintf(stderr, "Flags are %x.\n", flags);

    flags &= ~TIOCM_CTS;  // Disable the CTS bit
    ret = ioctl(fd, TIOCMSET, &flags);

    if (ret == -1)
        fprintf(stderr, "TIOCMSET failed %d\n", errno);
    else
        fprintf(stderr, "TIOCMSET succeeded. flags: %x.\n", flags);

    return 0;
}
Flags are 6.
TIOCMSET failed 25
const SerialPort = require('serialport')

const initConnection = async (baudRate) => {
  const connection = new SerialPort('/dev/tty.usbserial-A50285BI', { baudRate })
  await new Promise((resolve, reject) => connection.on('open', resolve))
  return connection
}

const getControlFlags = (connection) => {
  return new Promise((resolve, reject) => {
    connection.get((err, data) => {
      if (err) {
        return reject(err)
      }
      resolve(data)
    })
  })
}

const setControlFlags = (connection, options) => {
  return new Promise((resolve, reject) => {
    connection.set(options, (err) => {
      if (err) {
        return reject(err)
      }
      resolve()
    })
  })
}

const run = async () => {
  const connection = await initConnection(250000)
  console.log(await getControlFlags(connection))
  await setControlFlags(connection, { cts: true })
  console.log(await getControlFlags(connection))
  await setControlFlags(connection, { cts: false })
  console.log(await getControlFlags(connection))
}

run()
  serialport/bindings loading DarwinBinding +0ms
  serialport/stream opening path: /dev/tty.usbserial-A50285BI +0ms
  serialport/binding-abstract open +0ms
  serialport/bindings/poller Creating poller +0ms
  serialport/stream opened path: /dev/tty.usbserial-A50285BI +6ms
  serialport/stream #get +0ms
  serialport/binding-abstract get +6ms
  serialport/stream binding.get finished +1ms
{ cts: true, dsr: false, dcd: false, lowLatency: false }
  serialport/stream #set { brk: false, cts: true, dtr: true, dts: false, rts: true } +3ms
  serialport/binding-abstract set +4ms
  serialport/stream binding.set finished +1ms
  serialport/stream #get +0ms
  serialport/binding-abstract get +1ms
  serialport/stream binding.get finished +0ms
{ cts: true, dsr: false, dcd: false, lowLatency: false }
  serialport/stream #set { brk: false, cts: false, dtr: true, dts: false, rts: true } +0ms
  serialport/binding-abstract set +0ms
  serialport/stream binding.set finished +1ms
  serialport/stream #get +0ms
  serialport/binding-abstract get +1ms
  serialport/stream binding.get finished +1ms
{ cts: true, dsr: false, dcd: false, lowLatency: false }

Notice how CTS never changes but an exception/error is never thrown.

Any other kind of debug logs I can provide?

brandonros avatar May 01 '21 16:05 brandonros

Hi @brandonros it might be useful for you to tag or highlight if this issue is only occuring on the new M1 chip or is present on intel based macs too?

I may be wrong but it may be that explicit changes need to be made to enable the USB control on the new chips. After all, USB is listed as one of the aspects of the Linux M1 updates which is still to be completed, therefore it sounds like we may need to make some changes to support the new chip.

In particular, at present serialport uses IOKit and it looks like this needs to be migrated to DriverKit per the porting guide on the apple dev documentation.

GazHank avatar May 05 '21 10:05 GazHank

One easy fix might be to at least catch if ioctl is failing (as per the C test case I provided)

I do not know if C level ioctl calls are supposed to fail because it is an M1 chip as well? lol

brandonros avatar May 06 '21 22:05 brandonros

@GazHank thanks for that link, that's going to be some work....

reconbot avatar May 07 '21 03:05 reconbot

Hi @brandonros sorry I'd not responded to your point about the error handling before, but I think the code is trying to trap those errors in the same way as you suggested : https://github.com/serialport/node-serialport/blob/2cd5648c88e85d69533803ad99053c9c0e7defda/packages/bindings/src/serialport_unix.cpp#L347-L349

The error codes just don't seem to be getting thrown for some reason :-(

I've been looking through the SDK for IOKit and while the Apple dev docs seem to be pushing everyone to use DriverKit for most purposes, it doesn't seem to be a blanket rule. They have also made some accomodations for the new ARM chips within the IOKit, so perhaps it's not dead just yet...

I've compared SDK 10.15 to 11.3, and while there are a few bits of code they have deprecated it all seems to be tied to things which shouldnt affect us. For example they have deprecated, some highlevel stuff (like keyboard or other device specific logic which is moved to DriverKit instead), and removed access to some items outside of the Kernel mode for security:

#if !defined(KERNEL_PRIVATE)
extern const OSSymbol *gIOSerialBSDServiceValue __deprecated_msg("Use DriverKit");
extern const OSSymbol *gIOSerialBSDTypeKey __deprecated_msg("Use DriverKit");
extern const OSSymbol *gIOSerialBSDAllTypes __deprecated_msg("Use DriverKit");
extern const OSSymbol *gIOSerialBSDModemType __deprecated_msg("Use DriverKit");
extern const OSSymbol *gIOSerialBSDRS232Type __deprecated_msg("Use DriverKit");
extern const OSSymbol *gIOTTYDeviceKey __deprecated_msg("Use DriverKit");
extern const OSSymbol *gIOTTYBaseNameKey __deprecated_msg("Use DriverKit");
extern const OSSymbol *gIOTTYSuffixKey __deprecated_msg("Use DriverKit");
extern const OSSymbol *gIOCalloutDeviceKey __deprecated_msg("Use DriverKit");
extern const OSSymbol *gIODialinDeviceKey __deprecated_msg("Use DriverKit");
#else

But the things we use seem unchanged: https://github.com/serialport/node-serialport/blob/2cd5648c88e85d69533803ad99053c9c0e7defda/packages/bindings/src/darwin_list.cpp#L54-L58

Given the dependencies on other areas of the SDK that may exist, I fear I could be chasing my tail a little trying to get to the bottom of this.

Since the current build process is only configured to produce a Mac version for the intel machines (x64), I can't check it's build log for any interesting info, and don't have access to a mac myself (M1 or otherwise). I can't help think that the compiler may be thowing out some warnings or some clues when compiling for the new processor, that may help identify what needs to be fixed.

@reconbot would it be possible to build for the M1 (arm64) too? even if only in an experimental branch for now until we bottom this out? (while access to ios11 machines is not possible at the moment I'd assume the fact that the machines are running SDK 1.2+ would make this feasible?)

@brandonros would you be able to share any logs from when you (re)compiled SerialPort for the M1? or even just the Bindings package initially that should cut things down a bit, and it seems to be where the current errors you mentioned are getting thrown from

Many thanks

Gaz

GazHank avatar May 30 '21 15:05 GazHank

I've heard rumors that an m1 Mac is being tested for GitHub actions in a private beta but I don't have access to the beta or a physical machine at the moment either.

I'm not aware of any way to cross compile to the m1 arch either.

reconbot avatar May 30 '21 15:05 reconbot

Hi @brandonros I probably should have asked this before, but can you please confirm if the errors you are seeing are when running the precompiled binaries, or after a local recompile of Serialport?

if possible could you also confirm the versions of the Node and SerialPort that you are using, along with any other packages (especially anything like electron). Given how many packages may need to be reworked or recompiled for the new chips, I'd just like to make sure we arent accidentally troubleshooting an issue with Nodejs etc

cheers

Gaz

GazHank avatar May 31 '21 13:05 GazHank