de-vri-es/serial2-rs

hope to be able to read the vid and pid information of the port

Opened this issue · 3 comments

I need vid and pid to determine if the port is what I need, but serial2 doesn't do that

By VID/PID you are talking about the USB descriptors, meaning you're trying to determine which USB device corresponds to a given port ?

Unfortunately there is no single API on any operating system that I'm aware of that provides this. This is also made slightly more complex by the fact that a single USB device can provide several different CDC endpoints (which are what looks like a "serial port" to your OS).

This crate is focused on serial port handling only, so it makes sense to me that this extra functionality (which is not trivial) isn't handled there.

As a hint, see the following three C# classes to do the Serial Port <-> USB VID/PID mapping on Windows (only)

public class PnPSerialQuery {
  private const int QUERY_SLEEP_IN_MS = 100;
  
  public struct Result {
	  public Result(string port, string location) {
		  comPort = port;
		  usbLocation = location;
	  }
  
	  public string comPort { get; }
	  public string usbLocation { get; }
  }
  
  public PnPSerialDevice? PnPIdentityForSerialPort(string name) {
	  // No searching, directly creating the object.
	  // This makes the search waaaaay faster
	  string nameTrimmed = name.Trim();
	  var entity = new ManagementObject($"Win32_SerialPort.DeviceId='{nameTrimmed}'");
  
	  string deviceId;
	  string pnpDeviceId;
  
	  try {
		  deviceId = (string)entity.GetPropertyValue("DeviceId");
		  pnpDeviceId = (string)entity.GetPropertyValue("PNPDeviceID");
	  } catch (ManagementException ex) when (ex.ErrorCode == ManagementStatus.NotFound) {
		  return null;
	  }
  
	  // Recover the path
	  return new PnPSerialDevice(deviceId, pnpDeviceId);
  }
}

public enum SerialDeviceKind {
  Static,
  USB,
  BTH
}

public class PnPSerialDevice {
  public SerialDeviceKind Kind {
	  get {
		  // The beginning of the instance ID indicates the type
		  if (InstanceID.StartsWith("USB")) {
			  return SerialDeviceKind.USB;
		  } else {
			  return SerialDeviceKind.BTH;
		  }
	  }
  }
  
  public readonly string PortName;
  
  // looks like USB\VID_2E8A&PID_000A&MI_00\6&1E5028C0&0&0000
  public readonly string InstanceID;
  
  public PnPSerialDevice(string portName, string instanceId) {
	  this.PortName = portName;
	  this.InstanceID = instanceId;
  }
  
  public USBPath ToUSBPath() {
	  if (Kind != SerialDeviceKind.USB) {
		  throw new Exception("Incorrect type for a USB path");
	  }
  
	  string vidHex = InstanceID.Substring(8, 4);
	  short vid = Convert.ToInt16(vidHex, 16);
  
	  string pidHex = InstanceID.Substring(17, 4);
	  short pid = Convert.ToInt16(pidHex, 16);
  
	  string interfaceNumberHex = InstanceID.Substring(25, 2);
	  short interfaceNumber = Convert.ToInt16(interfaceNumberHex, 16);
  
	  string parentId = InstanceID.Substring(28, 12);
  
	  return new USBPath(new USBIdentity(vid, pid), interfaceNumber, parentId);
  }
}
public readonly struct USBIdentity {
  public readonly short VendorID;
  public readonly short ProductID;
  
  public USBIdentity(short vendorID, short productID) {
	  VendorID = vendorID;
	  ProductID = productID;
  }
  
  public string ToWindowsString() {
	  string vidHex = VendorID.ToString("X4");
	  string pidHex = VendorID.ToString("X4");
	  return $"VID_{vidHex}&PID_{pidHex}";
  }
  
  public override string ToString() {
	  return ToWindowsString();
  }
}

public readonly struct USBPath {
  public readonly USBIdentity Identity;
  public readonly int InterfaceNumber;
  public readonly string ParentID;
  
  public USBPath(USBIdentity identity, int interfaceNumber, string parentID) {
	  Identity = identity;
	  InterfaceNumber = interfaceNumber;
	  ParentID = parentID;
  }
}

Indeed, getting this information is highly platform specific and not that trivial. I'm not opposed to exposing some useful OS specific information about serial ports in a new enumeration interface though.

However, I do want to avoid adding new dependencies for this. And if it increases compile time too much then I would want it hidden behind a default-off feature flag.

However, such an interface also needs to take into account that not all ports are USB ports. Some are PCI(e) devices, some are motherboard peripherals, some are virtual devices, etc...

The complexity of doing this correctly cross platform is why I never started on it yet.

This is indeed very platform-specific, and in my mind wouldn't even be in scope in a serial port handling library.

In general this is the problem of "PnP" mapping in Windows parlance, where a virtual device (webcam, keyboard, drive, or indeed a serial port) has to be mapped to a physical device.

I am not sure if there is any elegant way to do so in a cross-platform way, as my experience is solely Windows, Linux & bare-metal embedded. And if so, it would probably be quite an endeavour (and it would probably warrant providing C bindings for other languages).