Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macos and Windows disagree on interface numbering for Black Magic Probe #214

Open
xobs opened this issue Aug 31, 2024 · 12 comments
Open

macos and Windows disagree on interface numbering for Black Magic Probe #214

xobs opened this issue Aug 31, 2024 · 12 comments

Comments

@xobs
Copy link
Contributor

xobs commented Aug 31, 2024

The Black Magic Probe has two serial ports, and the interface should be 0 and 2. For example, this is how it shows up under Windows:

   Compiling serialport v4.5.1-alpha.0 (E:\Code\serialport-rs)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.71s
     Running `target\debug\examples\list_ports.exe`
Found 2 ports:
  COM8
    Type: USB
    VID:1d50 PID:6018
     Serial Number: 97B6DC14
      Manufacturer: Microsoft
           Product: USB Serial Device (COM8)
         Interface: 02
  COM9
    Type: USB
    VID:1d50 PID:6018
     Serial Number: 97B6DC14
      Manufacturer: Microsoft
           Product: USB Serial Device (COM9)
         Interface: 00

However under Mac, it lists each port twice, and the interface is offset by one:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/examples/list_ports`
Found 4 ports:
  /dev/cu.usbmodem97B6DC141
    Type: USB
    VID:1d50 PID:6018
     Serial Number: 97B6DC14
      Manufacturer: Black Magic Debug
           Product: Black Magic Probe v1.10.0-634-gfa79ab6b
         Interface: 01
  /dev/tty.usbmodem97B6DC141
    Type: USB
    VID:1d50 PID:6018
     Serial Number: 97B6DC14
      Manufacturer: Black Magic Debug
           Product: Black Magic Probe v1.10.0-634-gfa79ab6b
         Interface: 01
  /dev/cu.usbmodem97B6DC143
    Type: USB
    VID:1d50 PID:6018
     Serial Number: 97B6DC14
      Manufacturer: Black Magic Debug
           Product: Black Magic Probe v1.10.0-634-gfa79ab6b
         Interface: 03
  /dev/tty.usbmodem97B6DC143
    Type: USB
    VID:1d50 PID:6018
     Serial Number: 97B6DC14
      Manufacturer: Black Magic Debug
           Product: Black Magic Probe v1.10.0-634-gfa79ab6b
         Interface: 03

There are two issues:

  1. Serial ports are doubled, with both the "cu" and "tty" variant presented, and
  2. The Interface is offset by one

I'm uncertain what this would show on Linux, so I'm not sure which would be considered more "canonical". I can work around it in software by accepting either 0 or 1, and I can ignore "/dev/tty*", but it would be nice if it were the same across platforms.

@xobs
Copy link
Contributor Author

xobs commented Aug 31, 2024

(The above comment appears to be spam, and I am unable to submit a report to Github because their captcha isn't working.)

@RossSmyth
Copy link
Contributor

RossSmyth commented Aug 31, 2024

Ahhh those spammers are all over github right now.

Anyways, thanks for the report. I have one thing I am interested in seeing, and that is the HWID for the Black Magic ports on Windows. To see these open up Device Manager, click on the ports, go to the "Details" tab, then go to the "Hardware Ids" tab.

@xobs
Copy link
Contributor Author

xobs commented Aug 31, 2024

Here's the HWID for COM9, which shows up as Interface 0:

image

Similarly, here's COM8, which shows up as Interface 2:

image

On the Mac, they show up as Interface 1 and 3 respectively.

@RossSmyth
Copy link
Contributor

Well we are just reporting what it says then, unfortunately. Not much we can do about it here. You could ask the Black Magic folks about it. It is defined here:
https://github.com/blackmagic-debug/blackmagic/blob/545f69d35d58d9d15ed692c69f54411683487d03/driver/blackmagic.inf#L29-L30

I'm not sure where they are defined for Darwin.

@xobs
Copy link
Contributor Author

xobs commented Aug 31, 2024

I think what's going on is that the Mac is reporting the CDC Data interface, whereas Linux and Windows are reporting the Communcations interface.

So it's fundamentally different information. It might be useful to point out this difference, and ultimately the proper thing to do is for application writers to be aware of the difference and to have different checks when the operating system is from Apple.

You might want to note this in the documentation, but if not please feel free to close this issue.

@xobs
Copy link
Contributor Author

xobs commented Aug 31, 2024

For the record, the issue is in fact that the serial port is listed under the AppleUSBACMData node rather than the lowest-numbered descriptor, which is what the spec says it should be:

    | |   | | |   +-o Black Magic UART Port@2  <class IOUSBHostInterface, id 0x100a253d4, registered, matched, active, busy 0 (40 ms), retain 9>
    | |   | | |   | | {
    | |   | | |   | |   "USBSpeed" = 1
    | |   | | |   | |   "iInterface" = 5
    | |   | | |   | |   "bInterfaceProtocol" = 0
    | |   | | |   | |   "bAlternateSetting" = 0
    | |   | | |   | |   "idProduct" = 24600
    | |   | | |   | |   "bcdDevice" = 265
    | |   | | |   | |   "USB Product Name" = "Black Magic Probe v1.10.0-634-gfa79ab6b"
    | |   | | |   | |   "locationID" = 1048576
    | |   | | |   | |   "bInterfaceClass" = 2
    | |   | | |   | |   "bInterfaceSubClass" = 2
    | |   | | |   | |   "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
    | |   | | |   | |   "UsbExclusiveOwner" = "AppleUSBACMControl"
    | |   | | |   | |   "USBPortType" = 0
    | |   | | |   | |   "kUSBString" = "Black Magic UART Port"
    | |   | | |   | |   "bInterfaceNumber" = 2
    | |   | | |   | |   "bConfigurationValue" = 1
    | |   | | |   | |   "USB Vendor Name" = "Black Magic Debug"
    | |   | | |   | |   "idVendor" = 7504
    | |   | | |   | |   "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
    | |   | | |   | |   "bNumEndpoints" = 1
    | |   | | |   | |   "IODEXTMatchCount" = 1
    | |   | | |   | |   "USB Serial Number" = "97B6DC14"
    | |   | | |   | | }
    | |   | | |   | |
    | |   | | |   | +-o AppleUSBACMControl  <class AppleUSBACMControl, id 0x100a253df, registered, matched, active, busy 0 (1 ms), retain 7>
    | |   | | |   |     {
    | |   | | |   |       "IOClass" = "AppleUSBACMControl"
    | |   | | |   |       "CFBundleIdentifier" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |       "IOProviderClass" = "IOUSBHostInterface"
    | |   | | |   |       "IOProbeScore" = 60000
    | |   | | |   |       "bInterfaceSubClass" = 2
    | |   | | |   |       "IOMatchedAtBoot" = Yes
    | |   | | |   |       "IOMatchCategory" = "IODefaultMatchCategory"
    | |   | | |   |       "IOMatchDefer" = Yes
    | |   | | |   |       "InterruptPipeReturn" = 0
    | |   | | |   |       "IOPersonalityPublisher" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |       "bInterfaceProtocol" = 0
    | |   | | |   |       "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |       "bInterfaceClass" = 2
    | |   | | |   |     }
    | |   | | |   |
    | |   | | |   +-o IOUSBHostInterface@3  <class IOUSBHostInterface, id 0x100a253d5, registered, matched, active, busy 0 (282 ms), retain 6>
    | |   | | |   | | {
    | |   | | |   | |   "USBSpeed" = 1
    | |   | | |   | |   "iInterface" = 0
    | |   | | |   | |   "bInterfaceProtocol" = 0
    | |   | | |   | |   "bAlternateSetting" = 0
    | |   | | |   | |   "idProduct" = 24600
    | |   | | |   | |   "bcdDevice" = 265
    | |   | | |   | |   "USB Product Name" = "Black Magic Probe v1.10.0-634-gfa79ab6b"
    | |   | | |   | |   "locationID" = 1048576
    | |   | | |   | |   "Product Name" = "USB ACM"
    | |   | | |   | |   "bInterfaceClass" = 10
    | |   | | |   | |   "bInterfaceSubClass" = 0
    | |   | | |   | |   "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
    | |   | | |   | |   "USBPortType" = 0
    | |   | | |   | |   "bConfigurationValue" = 1
    | |   | | |   | |   "bInterfaceNumber" = 3
    | |   | | |   | |   "USB Vendor Name" = "Black Magic Debug"
    | |   | | |   | |   "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
    | |   | | |   | |   "idVendor" = 7504
    | |   | | |   | |   "IODEXTMatchCount" = 2
    | |   | | |   | |   "bNumEndpoints" = 2
    | |   | | |   | |   "USB Serial Number" = "97B6DC14"
    | |   | | |   | | }
    | |   | | |   | |
    | |   | | |   | +-o AppleUSBACMData  <class AppleUSBACMData, id 0x100a253dc, registered, matched, active, busy 0 (2 ms), retain 7>
    | |   | | |   |   | {
    | |   | | |   |   |   "IOClass" = "AppleUSBACMData"
    | |   | | |   |   |   "CFBundleIdentifier" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |   |   "IOProviderClass" = "IOUSBHostInterface"
    | |   | | |   |   |   "IOTTYBaseName" = "usbmodem"
    | |   | | |   |   |   "idProduct" = 24600
    | |   | | |   |   |   "IOProbeScore" = 49998
    | |   | | |   |   |   "bInterfaceSubClass" = 0
    | |   | | |   |   |   "IOMatchedAtBoot" = Yes
    | |   | | |   |   |   "IOMatchCategory" = "IODefaultMatchCategory"
    | |   | | |   |   |   "IOMatchDefer" = Yes
    | |   | | |   |   |   "HiddenPort" = Yes
    | |   | | |   |   |   "IOPersonalityPublisher" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |   |   "idVendor" = 7504
    | |   | | |   |   |   "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |   |   "IOTTYSuffix" = "97B6DC143"
    | |   | | |   |   |   "bInterfaceClass" = 10
    | |   | | |   |   | }
    | |   | | |   |   |
    | |   | | |   |   +-o IOSerialBSDClient  <class IOSerialBSDClient, id 0x100a253eb, registered, matched, active, busy 0 (1 ms), retain 5>
    | |   | | |   |       {
    | |   | | |   |         "IOClass" = "IOSerialBSDClient"
    | |   | | |   |         "CFBundleIdentifier" = "com.apple.iokit.IOSerialFamily"
    | |   | | |   |         "IOProviderClass" = "IOSerialStreamSync"
    | |   | | |   |         "IOTTYBaseName" = "usbmodem"
    | |   | | |   |         "IOSerialBSDClientType" = "IOSerialStream"
    | |   | | |   |         "IOProbeScore" = 1000
    | |   | | |   |         "IOResourceMatch" = "IOBSD"
    | |   | | |   |         "IOMatchedAtBoot" = Yes
    | |   | | |   |         "IOMatchCategory" = "IODefaultMatchCategory"
    | |   | | |   |         "IOTTYDevice" = "usbmodem97B6DC143"
    | |   | | |   |         "IOCalloutDevice" = "/dev/cu.usbmodem97B6DC143"
    | |   | | |   |         "IODialinDevice" = "/dev/tty.usbmodem97B6DC143"
    | |   | | |   |         "IOPersonalityPublisher" = "com.apple.iokit.IOSerialFamily"
    | |   | | |   |         "CFBundleIdentifierKernel" = "com.apple.iokit.IOSerialFamily"
    | |   | | |   |         "IOTTYSuffix" = "97B6DC143"
    | |   | | |   |       }

Notice how the actual interface is Black Magic UART Port@2 which has a bInterfaceNumber of 2, but serialport-rs picks up the second descriptor which has a bInterfaceNumber of 3.

Apple is not doing the correct thing in this case, and I'm not sure how to get the correct thing out of the system.

@sirhcel
Copy link
Contributor

sirhcel commented Aug 31, 2024

Thank you very much for spotting this and your extensive research @xobs! Having two device files for a single serial devices on macOS is how macOS reports it: as a callout and a TTY device. It is how it is.

But the interface number does not match my expectation: I was assuming that bInterfaceNumber is always the value from the USB device descriptor. Looking at the output from ioreg above, it looks to me, that there are different interface numbers assigned along the "device path":

  • "bInterfaceNumber" = 2 at Black Magic UART Port@2 <class IOUSBHostInterface, ...>
  • "bInterfaceNumber" = 3 at IOUSBHostInterface@3 <class IOUSBHostInterface, ...>

So may be we just have to put some more fairy dust at it for picking the "right" interface number. Could you please post the whole tree from ioreg for the Black Magic probe? How are the interfaces presented at the USB device descriptor according to lsusb -v on Linux?

@xobs
Copy link
Contributor Author

xobs commented Aug 31, 2024

Here's the entire output from the Black Magic device:

    | |   | +-o AppleT8122USBXHCI@00000000  <class AppleT8122USBXHCI, id 0x10000062d, registered, matched, active, busy 0 (3200 ms), retain 205>
    | |   | | | {
    | |   | | |   "IOClass" = "AppleT8122USBXHCI"
    | |   | | |   "kUSBSleepPortCurrentLimit" = 3000
    | |   | | |   "UsbHostControllerSoftRetryPolicy" = 0
    | |   | | |   "IOPersonalityPublisher" = "com.apple.driver.usb.AppleSynopsysUSB40XHCI"
    | |   | | |   "IOMatchedAtBoot" = Yes
    | |   | | |   "IOPowerManagement" = {"ChildrenPowerState"=3,"DevicePowerState"=0,"CurrentPowerState"=3,"CapabilityFlags"=32768,"MaxPowerState"=3,"DriverPowerState"=0}
    | |   | | |   "IOProviderClass" = "AppleARMIODevice"
    | |   | | |   "IOProbeScore" = 10001
    | |   | | |   "locationID" = 0
    | |   | | |   "kUSBWakePortCurrentLimit" = 3000
    | |   | | |   "IONameMatch" = "usb-drd,t8122"
    | |   | | |   "CFBundleIdentifierKernel" = "com.apple.driver.usb.AppleSynopsysUSB40XHCI"
    | |   | | |   "UsbHostControllerDeferRegisterService" = Yes
    | |   | | |   "IOMatchCategory" = "usb-host"
    | |   | | |   "CFBundleIdentifier" = "com.apple.driver.usb.AppleSynopsysUSB40XHCI"
    | |   | | |   "Revision" = <0103>
    | |   | | |   "IONameMatched" = "usb-drd,t8122"
    | |   | | |   "UsbHostControllerUSB4LPMPolicy" = 1
    | |   | | |   "UsbHostControllerTierLimit" = 6
    | |   | | |   "controller-statistics" = {"kControllerStatIOCount"=707435,"kControllerStatPowerStateTime"={"kPowerStateOff"="1162607842ms (90%)","kPowerStateSleep"="337ms (0%)","kPowerStateOn"="117358349ms (9%)","kPowerStateSuspended"="761ms (0%)"},"kControllerStatSpuriousInterruptCount"=0}
    | |   | | |   "kUSBSleepSupported" = Yes
    | |   | | | }
    | |   | | | 
    | |   | | +-o usb-drd0-port-hs@00100000  <class AppleUSB20XHCITypeCPort, id 0x1000009d1, registered, matched, active, busy 0 (1328 ms), retain 17>
    | |   | | | | {
    | |   | | | |   "usb-c-port-number" = <01000000>
    | |   | | | |   "port-statistics" = {"kPortStatEOF2ViolationCurrentConnectCount"=0,"kPortStatEOF2ViolationDuringResetCount"=0,"kPortStatPowerStateTime"={"kPowerStateOff"="1162608844ms (90%)","kPowerStateSleep"="516ms (0%)","kPowerStateOn"="117356525ms (9%)","kPowerStateSuspended"="762ms (0%)"},"kPortStatConnectCount"=6,"kPortStatRemoteWakeCount"=0,"kPortStatEOF2ViolationRecoveryDuringResetCount"=0,"kPortStatAddressFailureCount"=0,"kPortStatEOF2ViolationCount"=0,"kPortStatEnumerationFailureCount"=1,"kPortStatOverCurrentCount"=0,"kPortStatEOF2ViolationDuringResumeCount"=0}
    | |   | | | |   "kUSBSleepPortCurrentLimit" = 3000
    | |   | | | |   "AAPL,phandle" = <ee000000>
    | |   | | | |   "kUSBWakePortCurrentLimit" = 3000
    | |   | | | |   "IOPowerManagement" = {"ChildrenPowerState"=3,"DevicePowerState"=2,"CurrentPowerState"=3,"CapabilityFlags"=32768,"MaxPowerState"=3,"DriverPowerState"=0}
    | |   | | | |   "IOGeneralInterest" = "IOCommand is not serializable"
    | |   | | | |   "locationID" = 1048576
    | |   | | | |   "device_type" = <"usb-drd0-port-hs">
    | |   | | | |   "kUSBBusCurrentSleepAllocation" = 0
    | |   | | | |   "port-type" = <04000000>
    | |   | | | |   "port-status" = 4352
    | |   | | | |   "kUSBBusCurrentAllocation" = 500
    | |   | | | |   "UsbCPortNumber" = 1
    | |   | | | |   "dock-remote-wake" = <>
    | |   | | | |   "name" = <"usb-drd0-port-hs">
    | |   | | | |   "port" = <01000000>
    | |   | | | | }
    | |   | | | | 
    | |   | | | +-o Black Magic Probe v1.10.0-634-gfa79ab6b@00100000  <class IOUSBHostDevice, id 0x100a253cc, registered, matched, active, busy 0 (333 ms), retain 62>
    | |   | | |   | {
    | |   | | |   |   "sessionID" = 30130759895879
    | |   | | |   |   "USBSpeed" = 1
    | |   | | |   |   "idProduct" = 24600
    | |   | | |   |   "iManufacturer" = 1
    | |   | | |   |   "bDeviceClass" = 239
    | |   | | |   |   "IOPowerManagement" = {"PowerOverrideOn"=Yes,"DevicePowerState"=2,"CurrentPowerState"=2,"CapabilityFlags"=32768,"MaxPowerState"=2,"DriverPowerState"=0}
    | |   | | |   |   "bcdDevice" = 265
    | |   | | |   |   "bMaxPacketSize0" = 32
    | |   | | |   |   "iProduct" = 2
    | |   | | |   |   "iSerialNumber" = 3
    | |   | | |   |   "bNumConfigurations" = 1
    | |   | | |   |   "UsbDeviceSignature" = <501d186009013937423644433134ef02010202000a0000fe0101ffffff>
    | |   | | |   |   "USB Product Name" = "Black Magic Probe v1.10.0-634-g"
    | |   | | |   |   "locationID" = 1048576
    | |   | | |   |   "bDeviceSubClass" = 2
    | |   | | |   |   "bcdUSB" = 513
    | |   | | |   |   "kUSBSerialNumberString" = "97B6DC14"
    | |   | | |   |   "USB Address" = 1
    | |   | | |   |   "IOCFPlugInTypes" = {"9dc7b780-9ec0-11d4-a54f-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
    | |   | | |   |   "kUSBCurrentConfiguration" = 1
    | |   | | |   |   "bDeviceProtocol" = 1
    | |   | | |   |   "USBPortType" = 0
    | |   | | |   |   "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
    | |   | | |   |   "USB Vendor Name" = "Black Magic Debug"
    | |   | | |   |   "Device Speed" = 1
    | |   | | |   |   "idVendor" = 7504
    | |   | | |   |   "kUSBProductString" = "Black Magic Probe v1.10.0-634-gfa79ab6b"
    | |   | | |   |   "USB Serial Number" = "97B6DC14"
    | |   | | |   |   "IOGeneralInterest" = "IOCommand is not serializable"
    | |   | | |   |   "kUSBAddress" = 1
    | |   | | |   |   "kUSBVendorString" = "Black Magic Debug"
    | |   | | |   | }
    | |   | | |   | 
    | |   | | |   +-o Black Magic GDB Server@0  <class IOUSBHostInterface, id 0x100a253d2, registered, matched, active, busy 0 (37 ms), retain 10>
    | |   | | |   | | {
    | |   | | |   | |   "USBSpeed" = 1
    | |   | | |   | |   "iInterface" = 4
    | |   | | |   | |   "bInterfaceProtocol" = 0
    | |   | | |   | |   "bAlternateSetting" = 0
    | |   | | |   | |   "idProduct" = 24600
    | |   | | |   | |   "bcdDevice" = 265
    | |   | | |   | |   "USB Product Name" = "Black Magic Probe v1.10.0-634-gfa79ab6b"
    | |   | | |   | |   "locationID" = 1048576
    | |   | | |   | |   "bInterfaceClass" = 2
    | |   | | |   | |   "bInterfaceSubClass" = 2
    | |   | | |   | |   "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
    | |   | | |   | |   "UsbExclusiveOwner" = "AppleUSBACMControl"
    | |   | | |   | |   "USBPortType" = 0
    | |   | | |   | |   "kUSBString" = "Black Magic GDB Server"
    | |   | | |   | |   "bInterfaceNumber" = 0
    | |   | | |   | |   "bConfigurationValue" = 1
    | |   | | |   | |   "USB Vendor Name" = "Black Magic Debug"
    | |   | | |   | |   "idVendor" = 7504
    | |   | | |   | |   "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
    | |   | | |   | |   "bNumEndpoints" = 1
    | |   | | |   | |   "IODEXTMatchCount" = 1
    | |   | | |   | |   "USB Serial Number" = "97B6DC14"
    | |   | | |   | | }
    | |   | | |   | | 
    | |   | | |   | +-o AppleUSBACMControl  <class AppleUSBACMControl, id 0x100a253d9, registered, matched, active, busy 0 (1 ms), retain 7>
    | |   | | |   |     {
    | |   | | |   |       "IOClass" = "AppleUSBACMControl"
    | |   | | |   |       "CFBundleIdentifier" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |       "IOProviderClass" = "IOUSBHostInterface"
    | |   | | |   |       "IOProbeScore" = 60000
    | |   | | |   |       "bInterfaceSubClass" = 2
    | |   | | |   |       "IOMatchedAtBoot" = Yes
    | |   | | |   |       "IOMatchCategory" = "IODefaultMatchCategory"
    | |   | | |   |       "IOMatchDefer" = Yes
    | |   | | |   |       "InterruptPipeReturn" = 0
    | |   | | |   |       "IOPersonalityPublisher" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |       "bInterfaceProtocol" = 0
    | |   | | |   |       "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |       "bInterfaceClass" = 2
    | |   | | |   |     }
    | |   | | |   |     
    | |   | | |   +-o IOUSBHostInterface@1  <class IOUSBHostInterface, id 0x100a253d3, registered, matched, active, busy 0 (296 ms), retain 8>
    | |   | | |   | | {
    | |   | | |   | |   "USBSpeed" = 1
    | |   | | |   | |   "iInterface" = 0
    | |   | | |   | |   "bInterfaceProtocol" = 0
    | |   | | |   | |   "bAlternateSetting" = 0
    | |   | | |   | |   "idProduct" = 24600
    | |   | | |   | |   "bcdDevice" = 265
    | |   | | |   | |   "USB Product Name" = "Black Magic Probe v1.10.0-634-gfa79ab6b"
    | |   | | |   | |   "locationID" = 1048576
    | |   | | |   | |   "Product Name" = "USB ACM"
    | |   | | |   | |   "bInterfaceClass" = 10
    | |   | | |   | |   "bInterfaceSubClass" = 0
    | |   | | |   | |   "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
    | |   | | |   | |   "USBPortType" = 0
    | |   | | |   | |   "bConfigurationValue" = 1
    | |   | | |   | |   "bInterfaceNumber" = 1
    | |   | | |   | |   "USB Vendor Name" = "Black Magic Debug"
    | |   | | |   | |   "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
    | |   | | |   | |   "idVendor" = 7504
    | |   | | |   | |   "IODEXTMatchCount" = 2
    | |   | | |   | |   "bNumEndpoints" = 2
    | |   | | |   | |   "USB Serial Number" = "97B6DC14"
    | |   | | |   | | }
    | |   | | |   | | 
    | |   | | |   | +-o AppleUSBACMData  <class AppleUSBACMData, id 0x100a253e4, registered, matched, active, busy 0 (1 ms), retain 7>
    | |   | | |   |   | {
    | |   | | |   |   |   "IOClass" = "AppleUSBACMData"
    | |   | | |   |   |   "CFBundleIdentifier" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |   |   "IOProviderClass" = "IOUSBHostInterface"
    | |   | | |   |   |   "IOTTYBaseName" = "usbmodem"
    | |   | | |   |   |   "idProduct" = 24600
    | |   | | |   |   |   "IOProbeScore" = 49998
    | |   | | |   |   |   "bInterfaceSubClass" = 0
    | |   | | |   |   |   "IOMatchedAtBoot" = Yes
    | |   | | |   |   |   "IOMatchCategory" = "IODefaultMatchCategory"
    | |   | | |   |   |   "IOMatchDefer" = Yes
    | |   | | |   |   |   "HiddenPort" = Yes
    | |   | | |   |   |   "IOPersonalityPublisher" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |   |   "idVendor" = 7504
    | |   | | |   |   |   "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |   |   "IOTTYSuffix" = "97B6DC141"
    | |   | | |   |   |   "bInterfaceClass" = 10
    | |   | | |   |   | }
    | |   | | |   |   | 
    | |   | | |   |   +-o IOSerialBSDClient  <class IOSerialBSDClient, id 0x100a253ec, registered, matched, active, busy 0 (1 ms), retain 5>
    | |   | | |   |       {
    | |   | | |   |         "IOClass" = "IOSerialBSDClient"
    | |   | | |   |         "CFBundleIdentifier" = "com.apple.iokit.IOSerialFamily"
    | |   | | |   |         "IOProviderClass" = "IOSerialStreamSync"
    | |   | | |   |         "IOTTYBaseName" = "usbmodem"
    | |   | | |   |         "IOSerialBSDClientType" = "IOSerialStream"
    | |   | | |   |         "IOProbeScore" = 1000
    | |   | | |   |         "IOResourceMatch" = "IOBSD"
    | |   | | |   |         "IOMatchedAtBoot" = Yes
    | |   | | |   |         "IOMatchCategory" = "IODefaultMatchCategory"
    | |   | | |   |         "IOTTYDevice" = "usbmodem97B6DC141"
    | |   | | |   |         "IOCalloutDevice" = "/dev/cu.usbmodem97B6DC141"
    | |   | | |   |         "IODialinDevice" = "/dev/tty.usbmodem97B6DC141"
    | |   | | |   |         "IOPersonalityPublisher" = "com.apple.iokit.IOSerialFamily"
    | |   | | |   |         "CFBundleIdentifierKernel" = "com.apple.iokit.IOSerialFamily"
    | |   | | |   |         "IOTTYSuffix" = "97B6DC141"
    | |   | | |   |       }
    | |   | | |   |       
    | |   | | |   +-o Black Magic UART Port@2  <class IOUSBHostInterface, id 0x100a253d4, registered, matched, active, busy 0 (40 ms), retain 9>
    | |   | | |   | | {
    | |   | | |   | |   "USBSpeed" = 1
    | |   | | |   | |   "iInterface" = 5
    | |   | | |   | |   "bInterfaceProtocol" = 0
    | |   | | |   | |   "bAlternateSetting" = 0
    | |   | | |   | |   "idProduct" = 24600
    | |   | | |   | |   "bcdDevice" = 265
    | |   | | |   | |   "USB Product Name" = "Black Magic Probe v1.10.0-634-gfa79ab6b"
    | |   | | |   | |   "locationID" = 1048576
    | |   | | |   | |   "bInterfaceClass" = 2
    | |   | | |   | |   "bInterfaceSubClass" = 2
    | |   | | |   | |   "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
    | |   | | |   | |   "UsbExclusiveOwner" = "AppleUSBACMControl"
    | |   | | |   | |   "USBPortType" = 0
    | |   | | |   | |   "kUSBString" = "Black Magic UART Port"
    | |   | | |   | |   "bInterfaceNumber" = 2
    | |   | | |   | |   "bConfigurationValue" = 1
    | |   | | |   | |   "USB Vendor Name" = "Black Magic Debug"
    | |   | | |   | |   "idVendor" = 7504
    | |   | | |   | |   "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
    | |   | | |   | |   "bNumEndpoints" = 1
    | |   | | |   | |   "IODEXTMatchCount" = 1
    | |   | | |   | |   "USB Serial Number" = "97B6DC14"
    | |   | | |   | | }
    | |   | | |   | | 
    | |   | | |   | +-o AppleUSBACMControl  <class AppleUSBACMControl, id 0x100a253df, registered, matched, active, busy 0 (1 ms), retain 7>
    | |   | | |   |     {
    | |   | | |   |       "IOClass" = "AppleUSBACMControl"
    | |   | | |   |       "CFBundleIdentifier" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |       "IOProviderClass" = "IOUSBHostInterface"
    | |   | | |   |       "IOProbeScore" = 60000
    | |   | | |   |       "bInterfaceSubClass" = 2
    | |   | | |   |       "IOMatchedAtBoot" = Yes
    | |   | | |   |       "IOMatchCategory" = "IODefaultMatchCategory"
    | |   | | |   |       "IOMatchDefer" = Yes
    | |   | | |   |       "InterruptPipeReturn" = 0
    | |   | | |   |       "IOPersonalityPublisher" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |       "bInterfaceProtocol" = 0
    | |   | | |   |       "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |       "bInterfaceClass" = 2
    | |   | | |   |     }
    | |   | | |   |     
    | |   | | |   +-o IOUSBHostInterface@3  <class IOUSBHostInterface, id 0x100a253d5, registered, matched, active, busy 0 (282 ms), retain 6>
    | |   | | |   | | {
    | |   | | |   | |   "USBSpeed" = 1
    | |   | | |   | |   "iInterface" = 0
    | |   | | |   | |   "bInterfaceProtocol" = 0
    | |   | | |   | |   "bAlternateSetting" = 0
    | |   | | |   | |   "idProduct" = 24600
    | |   | | |   | |   "bcdDevice" = 265
    | |   | | |   | |   "USB Product Name" = "Black Magic Probe v1.10.0-634-gfa79ab6b"
    | |   | | |   | |   "locationID" = 1048576
    | |   | | |   | |   "Product Name" = "USB ACM"
    | |   | | |   | |   "bInterfaceClass" = 10
    | |   | | |   | |   "bInterfaceSubClass" = 0
    | |   | | |   | |   "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
    | |   | | |   | |   "USBPortType" = 0
    | |   | | |   | |   "bConfigurationValue" = 1
    | |   | | |   | |   "bInterfaceNumber" = 3
    | |   | | |   | |   "USB Vendor Name" = "Black Magic Debug"
    | |   | | |   | |   "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
    | |   | | |   | |   "idVendor" = 7504
    | |   | | |   | |   "IODEXTMatchCount" = 2
    | |   | | |   | |   "bNumEndpoints" = 2
    | |   | | |   | |   "USB Serial Number" = "97B6DC14"
    | |   | | |   | | }
    | |   | | |   | | 
    | |   | | |   | +-o AppleUSBACMData  <class AppleUSBACMData, id 0x100a253dc, registered, matched, active, busy 0 (2 ms), retain 7>
    | |   | | |   |   | {
    | |   | | |   |   |   "IOClass" = "AppleUSBACMData"
    | |   | | |   |   |   "CFBundleIdentifier" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |   |   "IOProviderClass" = "IOUSBHostInterface"
    | |   | | |   |   |   "IOTTYBaseName" = "usbmodem"
    | |   | | |   |   |   "idProduct" = 24600
    | |   | | |   |   |   "IOProbeScore" = 49998
    | |   | | |   |   |   "bInterfaceSubClass" = 0
    | |   | | |   |   |   "IOMatchedAtBoot" = Yes
    | |   | | |   |   |   "IOMatchCategory" = "IODefaultMatchCategory"
    | |   | | |   |   |   "IOMatchDefer" = Yes
    | |   | | |   |   |   "HiddenPort" = Yes
    | |   | | |   |   |   "IOPersonalityPublisher" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |   |   "idVendor" = 7504
    | |   | | |   |   |   "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc.acm"
    | |   | | |   |   |   "IOTTYSuffix" = "97B6DC143"
    | |   | | |   |   |   "bInterfaceClass" = 10
    | |   | | |   |   | }
    | |   | | |   |   | 
    | |   | | |   |   +-o IOSerialBSDClient  <class IOSerialBSDClient, id 0x100a253eb, registered, matched, active, busy 0 (1 ms), retain 5>
    | |   | | |   |       {
    | |   | | |   |         "IOClass" = "IOSerialBSDClient"
    | |   | | |   |         "CFBundleIdentifier" = "com.apple.iokit.IOSerialFamily"
    | |   | | |   |         "IOProviderClass" = "IOSerialStreamSync"
    | |   | | |   |         "IOTTYBaseName" = "usbmodem"
    | |   | | |   |         "IOSerialBSDClientType" = "IOSerialStream"
    | |   | | |   |         "IOProbeScore" = 1000
    | |   | | |   |         "IOResourceMatch" = "IOBSD"
    | |   | | |   |         "IOMatchedAtBoot" = Yes
    | |   | | |   |         "IOMatchCategory" = "IODefaultMatchCategory"
    | |   | | |   |         "IOTTYDevice" = "usbmodem97B6DC143"
    | |   | | |   |         "IOCalloutDevice" = "/dev/cu.usbmodem97B6DC143"
    | |   | | |   |         "IODialinDevice" = "/dev/tty.usbmodem97B6DC143"
    | |   | | |   |         "IOPersonalityPublisher" = "com.apple.iokit.IOSerialFamily"
    | |   | | |   |         "CFBundleIdentifierKernel" = "com.apple.iokit.IOSerialFamily"
    | |   | | |   |         "IOTTYSuffix" = "97B6DC143"
    | |   | | |   |       }
    | |   | | |   |       
    | |   | | |   +-o Black Magic DFU@4  <class IOUSBHostInterface, id 0x100a253d6, registered, matched, active, busy 0 (8 ms), retain 5>
    | |   | | |   |   {
    | |   | | |   |     "USBSpeed" = 1
    | |   | | |   |     "iInterface" = 6
    | |   | | |   |     "bInterfaceProtocol" = 1
    | |   | | |   |     "bAlternateSetting" = 0
    | |   | | |   |     "idProduct" = 24600
    | |   | | |   |     "bcdDevice" = 265
    | |   | | |   |     "USB Product Name" = "Black Magic Probe v1.10.0-634-gfa79ab6b"
    | |   | | |   |     "locationID" = 1048576
    | |   | | |   |     "bInterfaceClass" = 254
    | |   | | |   |     "bInterfaceSubClass" = 1
    | |   | | |   |     "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
    | |   | | |   |     "USBPortType" = 0
    | |   | | |   |     "kUSBString" = "Black Magic DFU"
    | |   | | |   |     "bInterfaceNumber" = 4
    | |   | | |   |     "bConfigurationValue" = 1
    | |   | | |   |     "USB Vendor Name" = "Black Magic Debug"
    | |   | | |   |     "idVendor" = 7504
    | |   | | |   |     "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
    | |   | | |   |     "bNumEndpoints" = 0
    | |   | | |   |     "USB Serial Number" = "97B6DC14"
    | |   | | |   |   }
    | |   | | |   |   
    | |   | | |   +-o Black Magic Trace Capture@5  <class IOUSBHostInterface, id 0x100a253d7, registered, matched, active, busy 0 (7 ms), retain 5>
    | |   | | |   |   {
    | |   | | |   |     "USBSpeed" = 1
    | |   | | |   |     "iInterface" = 7
    | |   | | |   |     "bInterfaceProtocol" = 255
    | |   | | |   |     "bAlternateSetting" = 0
    | |   | | |   |     "idProduct" = 24600
    | |   | | |   |     "bcdDevice" = 265
    | |   | | |   |     "USB Product Name" = "Black Magic Probe v1.10.0-634-gfa79ab6b"
    | |   | | |   |     "locationID" = 1048576
    | |   | | |   |     "bInterfaceClass" = 255
    | |   | | |   |     "bInterfaceSubClass" = 255
    | |   | | |   |     "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
    | |   | | |   |     "USBPortType" = 0
    | |   | | |   |     "kUSBString" = "Black Magic Trace Capture"
    | |   | | |   |     "bInterfaceNumber" = 5
    | |   | | |   |     "bConfigurationValue" = 1
    | |   | | |   |     "USB Vendor Name" = "Black Magic Debug"
    | |   | | |   |     "idVendor" = 7504
    | |   | | |   |     "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
    | |   | | |   |     "bNumEndpoints" = 1
    | |   | | |   |     "USB Serial Number" = "97B6DC14"
    | |   | | |   |   }
    | |   | | |   |   
    | |   | | |   +-o AppleUSBHostCompositeDevice  <class AppleUSBHostCompositeDevice, id 0x100a253d1, !registered, !matched, active, busy 0, retain 4>
    | |   | | |       {
    | |   | | |         "IOClass" = "AppleUSBHostCompositeDevice"
    | |   | | |         "CFBundleIdentifier" = "com.apple.driver.usb.AppleUSBHostCompositeDevice"
    | |   | | |         "IOProviderClass" = "IOUSBHostDevice"
    | |   | | |         "kUSBPreferredConfiguration" = 1
    | |   | | |         "IOProbeScore" = 60000
    | |   | | |         "IOMatchedAtBoot" = Yes
    | |   | | |         "IOMatchCategory" = "IODefaultMatchCategory"
    | |   | | |         "bDeviceSubClass" = 2
    | |   | | |         "IOPersonalityPublisher" = "com.apple.driver.usb.AppleUSBHostCompositeDevice"
    | |   | | |         "bDeviceProtocol" = 1
    | |   | | |         "CFBundleIdentifierKernel" = "com.apple.driver.usb.AppleUSBHostCompositeDevice"
    | |   | | |         "IOPrimaryDriverTerminateOptions" = Yes
    | |   | | |         "bDeviceClass" = 239
    | |   | | |       }

It has six interfaces.

One interface is for SWO capture, and is not a specific interface. It's designed to be read with libusb directly.

One interface is for talking with DFU. Again, this is designed to be read with libusb.

Two interfaces are the serial output to communicate with the onboard UART.

Two interfaces are the GDB server, designed to interact directly with GDB.

Recall that with USB ACM, a single serial port needs to have two interfaces:

  1. A data interface through which bytes flow to the communications port
  2. A control interface where you send packets like "hangup" and "DCD Ready"

Here's an example I found of the various interfaces: https://gist.github.com/lupyuen/073984471bc064216995d0a6b18c0b95

So yes, there are two interfaces for the one serial port. Unfortunately, Macos (and therefore serialport-rs) is picking the wrong one. It apparently should pick the lower-numbered one, but I can't find a doc that describes that.

@sirhcel
Copy link
Contributor

sirhcel commented Sep 5, 2024

Recall that with USB ACM, a single serial port needs to have two interfaces:

  1. A data interface through which bytes flow to the communications port
  2. A control interface where you send packets like "hangup" and "DCD Ready"

Thank you for pointing this out! I was not aware of this when writing my last reply.

So yes, there are two interfaces for the one serial port. Unfortunately, Macos (and therefore serialport-rs) is picking the wrong one. It apparently should pick the lower-numbered one, but I can't find a doc that describes that.

So may be this is open to drivers and OS' and the choices were just made out of convenience. So it's likely up to us to look for both CDC ACM interfaces and report the lower one. Speaking about consistency: Have you looked at other libraries (like pySerial) how they report interface numbers for CDC ACM devices?

@xobs
Copy link
Contributor Author

xobs commented Sep 5, 2024

pySerial doesn't report interface numbers:

from serial.tools import list_ports
if __name__ == '__main__':
    for port in  list_ports.comports(include_links=False):
        print(f"Port: {port.name}  Interface: {port.interface}")

Windows:

Port: COM9  Interface: None
Port: COM4  Interface: None
Port: COM3  Interface: None
Port: COM8  Interface: None

Mac:

Port: cu.usbmodem97B6DC143  Interface: None
Port: cu.usbmodem97B6DC141  Interface: None
Port: cu.SOUNDPEATSCapsule3Pro  Interface: None
Port: cu.Bluetooth-Incoming-Port  Interface: None

I'm trying to find the location in the USB spec that defines which interface number to use when referring to the device.

@xobs
Copy link
Contributor Author

xobs commented Sep 5, 2024

Ah okay, I found it. From https://www.usb.org/sites/default/files/iadclasscode_r10.pdf on the very last page:

The value in the bInterfaceCount field must include all of the interfaces in the intended set. For example, assume a multi-interface function with interfaces numbered N through M. The bFirstInterface field gets the value of N and bInterfaceCount gets the value of (M-N)+1. 1 Note that each interface can have zero or more alternate settings, but alternate settings don’t figure into the calculation for bInterfaceCount.

For device functions that use an IAD (like the 1 st and 3 rd functions in the example), USB system software shouldconstruct ‘hardware identifiers’ used to locate and load a device driver using the idVendor and idProduct from the Device Descriptor and the bFirstInterface field from the IAD. Further, system software should construct ‘compatibility identifiers’ using the class code fields (bFunctionClass, bFunctionSubClass, bFunctionProtocol) from the IAD.

So it's defined that the Interface number should be the lowest value when there are multiple descriptors.

@xobs
Copy link
Contributor Author

xobs commented Sep 5, 2024

After thinking about it a bit more, it may just be that the answer is that the Interface can be either one of them, so users of the serialport-rs library might want to be aware of it.

However, it's probably safe to say that, regardless of the platform, it'll be one of the two, so either should be acceptable when searching for a particular device.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants
@xobs @sirhcel @RossSmyth and others