Bug 1698 - On Mac, USB ports are showing up as native
Summary: On Mac, USB ports are showing up as native
Status: IN_PROGRESS
Alias: None
Product: libserialport
Classification: Unclassified
Component: Port enumeration (show other bugs)
Version: 0.1.1
Hardware: All Mac OS X
: Normal normal
Target Milestone: ---
Assignee: Nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-06-30 16:29 CEST by Dan Sandberg
Modified: 2021-07-03 17:08 CEST (History)
2 users (show)



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Dan Sandberg 2021-06-30 16:29:13 CEST
I've got two USB <-> UART dongles plugged in, and the "transport" is showing as native.  Here's the output of port_info

$ ./port_info /dev/cu.SLAB_USBtoUART

Looking for port /dev/cu.SLAB_USBtoUART.
Port name: /dev/cu.SLAB_USBtoUART
Description: CP210x USB to UART Bridge Controller
Type: Native
Freeing port.

Because the type is "Native", I'm not able to get USB specific information even though the device is clearly a USB device.


If I run "ioreg -p IOUSB -l -w 0" to get the USB information, it shows this information about the USB dongle:

  |   | +-o CP2102 USB to UART Bridge Controller@14333000  <class AppleUSBDevice, id 0x1000f58f5, registered, matched, active, busy 0 (12 ms), retain 14>
  |   | |   {
  |   | |     "sessionID" = 4915452761161400
  |   | |     "iManufacturer" = 1
  |   | |     "bNumConfigurations" = 1
  |   | |     "idProduct" = 60000
  |   | |     "bcdDevice" = 256
  |   | |     "Bus Power Available" = 250
  |   | |     "USB Address" = 7
  |   | |     "bMaxPacketSize0" = 64
  |   | |     "iProduct" = 2
  |   | |     "iSerialNumber" = 3
  |   | |     "bDeviceClass" = 0
  |   | |     "Built-In" = No
  |   | |     "locationID" = 338898944
  |   | |     "bDeviceSubClass" = 0
  |   | |     "bcdUSB" = 272
  |   | |     "USB Product Name" = "CP2102 USB to UART Bridge Controller"
  |   | |     "PortNum" = 3
  |   | |     "non-removable" = "no"
  |   | |     "IOCFPlugInTypes" = {"9dc7b780-9ec0-11d4-a54f-000a27052861"="IOUSBFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
  |   | |     "bDeviceProtocol" = 0
  |   | |     "IOUserClientClass" = "IOUSBDeviceUserClientV2"
  |   | |     "IOPowerManagement" = {"DevicePowerState"=2,"CurrentPowerState"=3,"CapabilityFlags"=65536,"MaxPowerState"=4,"DriverPowerState"=3}
  |   | |     "kUSBCurrentConfiguration" = 1
  |   | |     "Device Speed" = 1
  |   | |     "USB Vendor Name" = "Silicon Labs"
  |   | |     "idVendor" = 4292
  |   | |     "IOGeneralInterest" = "IOCommand is not serializable"
  |   | |     "USB Serial Number" = "0001"
  |   | |     "IOClassNameOverride" = "IOUSBDevice"
  |   | |   }


The MAC version is 10.14.6 (Mojave).  Running as root doesn't change the output.
Comment 1 Martin Ling 2021-07-01 12:14:44 CEST
I guess Apple have changed their APIs again. The current OSX enumeration code was written in 2014, and updated once in 2015 for El Capitan.
Comment 2 Dan Sandberg 2021-07-01 12:28:39 CEST
So they break compatibility with the newer OS released?  Why would they do that -- bizarre?!

So the options are to support multiple different APIs or to just disallow support on older versions?

I'm really surprised they would do this, could there be some other explanation?
Comment 3 Martin Ling 2021-07-01 12:44:11 CEST
I'm afraid Apple's mindset does often seem to be that application code will need to be updated for newer versions of their operating systems, both desktop and mobile. As opposed to Windows and Linux where one can usually reasonably expect the same code to work for 20 years or so.

But yes, there could be other explanations for this specific issue. Do you get the same result for other USB serial devices?

I don't have the device you're using, and the newest OSX system I have ready access to is running High Sierra (10.13), so I can't really investigate myself.

The OSX enumeration code in libserialport was written by Aurelien Jacobs <aurel@gnuage.org>, but I don't think he's contributed to the project for several years.

The most recent fixes came from the Arduino team, who use libserialport specifically for enumeration of USB serial devices.

Actually if there's an issue with this on later OS versions, I would expect them to have run into it. So it may be something weird about this particular device.
Comment 4 Dan Sandberg 2021-07-01 13:38:11 CEST
Thanks Martin -- very helpful.

This USB <-> Serial chipset I'm using is extremely common, so I would guess that the Arduino team will see the same problem on other chipsets.

I think the reason they haven't posted a patch is that this problem would affect few people -- the port is properly enumerated, it's just the transport/USB information that is incorrect/missing.  Most users don't need those I suspect.

I will go through the change log to find the contact information for the last person who changed it and see if I can start a dialog with them.

I looked at the enumeration code and because I haven't done Mac programming before it's quite inscrutable...
Comment 5 Martin Ling 2021-07-01 14:11:08 CEST
The Arduino IDE does use USB information, but they're primarily interested in identifying official Arduino devices: which use an ATmega8u2 emulating a generic USB CDC serial device, with their own VID and PIDs.

So they may not have noticed a change affecting CP2102 devices.
Comment 6 Dan Sandberg 2021-07-01 14:32:18 CEST
Interesting, thank you.  

I assumed that serial devices were implemented as generic HID devices but apparently they are not since HID is limited to 64 kb/s.  Listing kernel extensions shows the "com.silabs.driver.CP210xVCPDriver" driver.

We'll see if Martino has any ideas.  If not, I can see what pyserial does, since it provides the correct information.
Comment 7 Martin Ling 2021-07-01 14:42:28 CEST
No, serial devices have generally been separate from HID.

There are some exceptions out there - e.g. there's a CH9325 chipset which implements serial-over-HID for low speed devices. It's used in a lot of multimeters for data logging output. The key advantage is that you don't need any driver support on the OS side because the device is accessible through the HID APIs.

But these don't emulate control signals etc, and they don't show up as serial ports in the OS, so aren't supported through libserialport. You have to access them through device-specific software. In libsigrok we support them via libusb.
Comment 8 Dan Sandberg 2021-07-03 08:34:23 CEST
I did a deep-dive and compared to pyserial.  Pyserial seems to have a cleaner approach -- for each found device, it sees if there's a parent device with the type IOUSBHostDevice or IOUSBDevice (for pre macOS 10.11 compatibility).

If it finds such a parent, the device is considered a USB device and the USB information is taken from the parent.

libserialport, in contrast, determines if the device is a USB device by searching the parents for "IOClass" or "IOProviderClass", which apparently isn't needed for a device to be a USB device.  Then, regardless of the outcome, it searches the devices parents for things like USB Manufacturer, USB VID, etc.  So it is searching all parents repeatedly for different attributes even though all the desired attributes are contained in the parent IOUSBHostDevice or IOUSBDevice.

Anyway, I made a simple pull-request that simplified the transport determination.  It says that the device is a USB device if it has a Vendor ID and Product ID.  

PR here:


https://github.com/sigrokproject/libserialport/pull/9/files
Comment 9 Dan Sandberg 2021-07-03 08:39:19 CEST
With the patch ./port_info /dev/cu.SLAB_USBtoUART173 yields:

Looking for port /dev/cu.SLAB_USBtoUART173.
Port name: /dev/cu.SLAB_USBtoUART173
Description: CP210x USB to UART Bridge Controller
Type: USB
Manufacturer: Silicon Labs
Product: CP210x USB to UART Bridge Controller
Serial: 0001
VID: 10C4 PID: EA60
Bus: 0 Address: 0

--

Seems like Apple's not to blame for changing their API in this case -- the determination of "USBness" was being made in a way that depended on internals.
Comment 10 Dan Sandberg 2021-07-03 08:41:00 CEST
BTW, I have two CP2102 adapters plugged in, with the same VID/PID/Serial, and the location shown by libserialport is the same for both.  pyserial correctly shows that they have separate locations.  This isn't an issue for me, but thought I'd mention it.
Comment 11 Dan Sandberg 2021-07-03 08:58:42 CEST
One thing I wanted to make clear -- the existing code was gathering all the USB information even if it determined the device was not a USB device.

So the existing code had the correct information (USB VID/PID/Serial/etc) even though the transport was set to native.  Because the device was incorrectly understood to be native, the gathered information wasn't shown.
Comment 12 Martin Ling 2021-07-03 16:06:21 CEST
Thanks for investigating and working on this.

I'm a little surprised by the idea that the removed code was completely redundant.

If the existing code was using unreliable internal details to distinguish USB devices, is there a correct alternative that we can use?

It seems wise to have a step that specifically identifies a device as USB, rather than deciding that based on attributes that might exist for other hardware too, either now or in the future.

Can we be sure, for instance, that the vendor & product IDs will only be populated if the device is connected via USB?

Other bus types have vendor and product IDs too - consider the case of a serial port on a PCIe expansion card.
Comment 13 Martin Ling 2021-07-03 16:14:42 CEST
Actually looking your earlier comments again, you answered that already.

I think we should replace the check for IOClass/IOProviderClass with one that checks for a parent IOUSBHostDevice/IOUSBDevice, as per the approach in pyserial.
Comment 14 Dan Sandberg 2021-07-03 16:28:20 CEST
PCI uses device ID rather than product ID, no?  Regardless I agree that using the pyserial approach (also the https://github.com/wjwwood/serial approach I believe) is cleaner.  It'll fix the location problem I mentioned also.

However, it'll probably take me a significant part of a day to get it working and I can't afford to spend that kind of time on it right now.

I'd suggest that fixing a definite problem is worth the risk of maybe introducing a new problem (in the worst case, a port is mis-identified as USB), but I understand if you want to wait until a better fix is available.
Comment 15 Martin Ling 2021-07-03 17:08:17 CEST
Given that the previous code turned out to be subtly incorrect, despite several years of use, I want to be sure that we replace it with something that we are sure is correct, and that we test this on some different devices and OS versions.

Your change gets you the results you want on your OS with your device, but that alone does not confirm that the faulty code is properly fixed.

On other operating systems port enumeration and metadata has proven tricky to get right in several ways, especially in edge cases like composite USB devices, and with subtle changes from one OS version to another.

I understand if you don't have the time to work further on it, and I appreciate the work you've already put in, but I'm not prepared to merge this as-is yet.