Possible to auto select BMPGDBSerialPort?
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.
@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
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.
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
How about allowing the wildcard option? /dev/cu.usbmodem*1 or COM* for instance?
@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.
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.
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.
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.
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"
}
]
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 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.