cortex-debug icon indicating copy to clipboard operation
cortex-debug copied to clipboard

Possible to auto select BMPGDBSerialPort?

Open siliconwitch opened this issue 1 year ago • 9 comments

We tend to share a whole bunch of Black magic probes across our team who use Mac. It's become a bit of a ritual to always need to change the BMPGDBSerialPort value whenever someone picks up different hardware.

Would it be possible to add some kind of auto port selection in case BMPGDBSerialPort isn't explicitly given? For instance, if Mac is used, choose the first /dev/cu.usbmodem*1, or on Linux simply choose /dev/ttyACM0.

siliconwitch avatar Apr 01 '24 12:04 siliconwitch

@ssimek When you get a chance, can you take a look at this? Close it if we cannot do it.

It sounds dangerous to auto-select unless you can query it. But also talking to random serial ports is not a good idea. Also, not sure you can do this for all OSes

haneefdm avatar Jan 17 '25 20:01 haneefdm

I was actually thinking about sidestepping the OS and using libusb to directly talk to the peripheral, now that we have usb capabilities integrated in the extension. It would be especially useful for Windows folks, as the OS tends to assign port numbers depending on the physical port used.

ssimek avatar Jan 17 '25 20:01 ssimek

It would be especially useful for Windows folks, as the OS tends to assign port numbers depending on the physical port used

But, aren't Windows serial port names also pretty stable even across reboots? So, it is a one-time thing for the user to figure out the port name. Note that I could have multiple boards connected to my machine and some boards have multiple serial ports.

If you are detecting things, I would do it in the main extension and not in the debug adapter. We want the adapter free of strange dependencies... and error reporting or user interaction is also difficult from there

haneefdm avatar Jan 17 '25 21:01 haneefdm

How about allowing the wildcard option? /dev/cu.usbmodem*1 or COM* for instance?

siliconwitch avatar Jan 17 '25 21:01 siliconwitch

@haneefdm Maybe something has changed, I haven't used Windows on bare metal for more than ten years but I remember that some drivers used to create new COM port instances for every physical USB port. If I'm doing this I'll definitely test it on all platforms.

@siliconwitch Sorry, but I don't see much value in the wildcard approach, especially compared to the confusion it can bring when things randomly stop working because the wrong port is picked up. On macOS, the name is pretty stable because it's based on device serial number. I understand your specific scenario of using multiple probes but I'd hazard a guess that's pretty rare. For linux, unless I'm mistaken, the numbers are assigned in order of connection so it shouldn't really be an issue, if there is just one probe (and no other CDC devices connected before it) it's always ttyACM0. On Windows... well, I guess no one knows how it works exactly, but COM* just presumes there is literally a single connected device. Plus, as far as I can remember, there is no simple way to enumerate COM ports on Windows (as they are not FS mapped)

That said, I really think the correct approach is to try to locate the probe using USB enumeration and see if we can either derive the serial port from that (doubtful) or create a bridge between a socket (for GDB to connect to) and the USB endpoint.

ssimek avatar Jan 18 '25 14:01 ssimek

I get a bad feeling that this will lead to a rabbit hole. Generate issues that I have to deal with. Especially when things can be very OS-specific. I still don't see the big benefit compared to the kinds of issues I am expecting later on.

Even with libusb, I just don't like the idea of connecting to a random serial port. You don't see a serial terminal emulator (Putty, Terraterm, etc.) connecting to a random serial port.

In the template for a BMP default launch.json, we could add reasonable defaults for all the platforms.

haneefdm avatar Jan 18 '25 23:01 haneefdm

The USB VID:PID for a Blackmagic Probe is 1D50:6018

See also https://github.com/platformio/platformio-core/issues/4023

You can see it is not that simple. VID:PID seems stable across BMP versions (FW and HW). But not sure what OSes do with it. I am far from a USB expert for any OS

I have other things to attend to, so this is low priority for me. Just be careful.

haneefdm avatar Jan 19 '25 00:01 haneefdm

PlatformIO did the exact mistake I want to avoid - trying to guess a serial port name via USB enumeration. I want to use the USB endpoint directly to avoid this kind of issue.

If anything, it will be strictly opt-in (by not providing a device name), explicitly specifying a device has to work as it did until now.

FWIW, STM actually uses this exact same approach of talking to its probes via libusb, even though they are actually full CDC/ACM devices.

Let's discuss once I have something tangible.

ssimek avatar Jan 20 '25 08:01 ssimek

Some updates

  • the libusb approach won't work, because CDC devices are typically already captured by the OS on USB level, and Windows doesn't support them at all

but

The serialport package we use already has great port detection capability via SerialPort.list(). This is the output for the various OSes (Windows obviously being an outlier once again), but I'm pretty sure we'll be able to use it

macOS

Very clean info, we can use vid/pid or manufacturer, and even select the right one just by serial number. Unfortunately, the info doesn't differentiate between GDB and AUX ports, but I'm pretty sure we can assume the first one to be GDB

[
  {
    "path": "/dev/tty.usbmodemBFC5C1E81",
    "manufacturer": "Black Magic Debug",
    "serialNumber": "BFC5C1E8",
    "locationId": "03100000",
    "vendorId": "1d50",
    "productId": "6018"
  },
  {
    "path": "/dev/tty.usbmodemBFC5C1E83",
    "manufacturer": "Black Magic Debug",
    "serialNumber": "BFC5C1E8",
    "locationId": "03100000",
    "vendorId": "1d50",
    "productId": "6018"
  },
]

Linux

Pretty much the same as MacOS, with the addition of seeing the firmware build as well as interface number at the end of pnpId

[
  {
    "path": "/dev/ttyACM0",
    "manufacturer": "Black Magic Debug",
    "serialNumber": "BFC5C1E8",
    "pnpId": "usb-Black_Magic_Debug_Black_Magic_Probe_v2.0.0-rc1-42-gd16b211e-dirty_BFC5C1E8-if00",
    "vendorId": "1d50",
    "productId": "6018"
  },
  {
    "path": "/dev/ttyACM1",
    "manufacturer": "Black Magic Debug",
    "serialNumber": "BFC5C1E8",
    "pnpId": "usb-Black_Magic_Debug_Black_Magic_Probe_v2.0.0-rc1-42-gd16b211e-dirty_BFC5C1E8-if02",
    "vendorId": "1d50",
    "productId": "6018"
  }
]

Windows

Well... better than nothing. Manufacturer is gone, Serial number is weird for some reason. But we should be able to match it.

[
  {
    "path": "COM3",
    "manufacturer": "Microsoft",
    "serialNumber": "7&378167A1&0&0002",
    "pnpId": "USB\\VID_1D50&PID_6018&MI_02\\7&378167A1&0&0002",
    "locationId": "0009.0000.0000.007.001.000.000.000.000",
    "friendlyName": "USB Serial Device (COM3)",
    "vendorId": "1D50",
    "productId": "6018"
  },
  {
    "path": "COM4",
    "manufacturer": "Microsoft",
    "serialNumber": "7&378167A1&0&0000",
    "pnpId": "USB\\VID_1D50&PID_6018&MI_00\\7&378167A1&0&0000",
    "locationId": "0009.0000.0000.007.001.000.000.000.000",
    "friendlyName": "USB Serial Device (COM4)",
    "vendorId": "1D50",
    "productId": "6018"
  }
]

ssimek avatar Jan 23 '25 14:01 ssimek

or create a bridge between a socket (for GDB to connect to) and the USB endpoint.

This only moves the problem of selecting the interface to that proxy/bridge application (socat or ser2net work).

STM actually uses this exact same approach of talking to its probes via libusb, even though they are actually full CDC/ACM devices.

How come? ST-Link/V2, V3 debugger API (HLA or dapdirect) is raw bulk endpoints, not standard CDC-ACM -- which they also implement for the purpose of VCP (UART passthrough) just like BMP does. But BMP is a standard CDC-ACM GDB RSP extended-remote and does not require libusb to work for debugging. This reminds me that SWO capture is a raw EP 3 IN (which may need libusb1 to extract streaming data) but that's tracked in a different issue.

Unfortunately, the info doesn't differentiate between GDB and AUX ports

@ssimek Yes it does. On MacOS (and FreeBSD?) the device node is composed as /dev/tty.usbmodem + serialno + ifno, the interface number of ... CDC Data Interface, so /dev/tty.usbmodemBFC5C1E81 is reliably GDB port (ifno 1) and /dev/tty.usbmodemBFC5C1E83 is reliably UART port (ifno 3). Linux uses the other number, of CDC Control Interface, so -if00 is GDB, -if02 is UART. Windows, likewise, exposes this as &MI_00 for GDB (COM4) and &MI_02 for UART (COM3) and we see here a perfect example of why you can't trust ntoskernel to reliably enumerate ports in ascending order of interfaces. The setup INF also tries to match this to assign user-readable names to those ports. On Linux with udev rules this is simpler and is the reason why /dev/ttyBmpGdbBFC5C1E8 & /dev/ttyBmpTargBFC5C1E8 aliases work.

Related: https://github.com/serialport/serialport-rs/issues/214

ALTracer avatar Mar 16 '25 18:03 ALTracer

@ALTracer you are correct regarding the STLink, my mistake. I've had my mind on too many projects and confused the libusb situation with something else, can't even remember where I got it from.

Regarding the bridge - I meant to create a bridge inside the extension, not use an external app, just for the sake of being able to give GDB a port to connect to. But as I've noted in my last comment and as you noted yourself - that approach is nonsense (relying on libusb for CDC comms).

So yes, the serial port enumeration outlined in my previous comment works just fine, it's just unfortunate that every OS sticks the interface number into a different place (path on macOS, pnpID on Linux and both pnpID and serial on Windows). I have actually used this approach in another extension and it works flawlessly.

ssimek avatar Mar 17 '25 08:03 ssimek