title: How to correlate PCI card bus/function or VENDEV and the /dev/ttySn installed from Linux C/C++
body: <p>I am writing a test program to verify serial port operation on any our extensive line of PCI, PCI Express, M.2 (and related busses) single- and multi-port cards.</p>
  <p>How can I determine which /dev/ttyS{n} are created by which PCI card, programmatically (CPP), on Linux.</p>
  <p>For example, 8-port RS232 Card is installed in a Linux PC, Linux kernel drivers install 8 /dev/ttyS devices successfully, user runs my program, my program must send/receive serial data at several baud rates through each of the 8 ports.</p>
  <p>Which 8 ports are from the Unit Under Test (&quot;UUT&quot;)?</p>
  <p>Well, one Linux box has /dev/ttyS0 through /dev/ttyS31, and /dev/ttyS0 through /dev/ttyS3 are from the motherboard. On this system the UUT's 8 ports get installed as /dev/ttyS4 through /dev/ttyS11 -- but it can/will be different ttyS numbers on different systems/linux images.</p>
  <p>I can <code>lspci -n |grep $VEND:</code> to find the card (&quot;04:00.0 0700: VEND:DEVI&quot;), take the first 7 bytes (04:00.0) manually and <code>dmesg | grep 04:00.0 | grep ttyS</code> to get the list of ports for that card ... but I want to do this from inside a &quot;CPP&quot; program.</p>
  <p>I can</p>
  <pre><code>for (const auto &amp; entry : fs::directory_iterator(&quot;/dev/&quot;))
      if (entry.path().filename().string().compare(0,4, &quot;ttyS&quot;) == 0 )
          list.push_back( entry.path() );
  </code></pre>
  <p>to get a list of all 32 /dev/ttyS devices on this system, but can't tell which are from the UUT.</p>
  <p>Worse, some cards are 1, 2, 4, or 8 port.
  Worse, multiple multiport cards could be installed simultaneously.</p>
  <p>So: How can I determine the VENDEV by which any given /dev/ttyS was created, programmatically? (C/CPP; gcc C++17)</p>
  <p>Something involving libpci? (<code>#include &lt;pci/pci.h&gt;</code>)</p>
  <pre><code>`root@systemname:/proc/tty/driver# cat serial
  serinfo:1.0 driver revision:
  0: uart:16550A port:000003F8 irq:4 tx:0 rx:0
  ...
  4: uart:ST16650 port:00003000 irq:18 tx:0 rx:0
  5: uart:ST16650 port:00003008 irq:18 tx:0 rx:0
  6: uart:ST16650 port:00003010 irq:18 tx:0 rx:0
  7: uart:ST16650 port:00003018 irq:18 tx:0 rx:0
  8: uart:ST16650 port:00003020 irq:18 tx:0 rx:0
  9: uart:ST16650 port:00003028 irq:18 tx:0 rx:0
  10: uart:ST16650 port:00003030 irq:18 tx:0 rx:0
  11: uart:ST16650 port:00003038 irq:18 tx:0 rx:0
  12: uart:unknown port:00000000 irq:0
  ...
  31: uart:unknown port:00000000 irq:0`
  </code></pre>
  <p>I can</p>
  <pre><code>struct pci_access *pciAccess;
  struct pci_dev *dev;
  pciAccess = pci_alloc(); 
  pci_init(pciAccess);                                 
  pci_scan_bus(pciAccess);                             
  for (dev = pciAccess-&gt;devices; dev; dev = dev-&gt;next) 
  {
      pci_fill_info(dev, PCI_FILL_IDENT); 
      if (dev-&gt;vendor_id == VENDOR_ID_UUT)
      {
          printf(&quot;%04x:%02x:%02x.%d vendor=%04x device=%04x  base0=%lx\n&quot;,
              dev-&gt;domain, dev-&gt;bus, dev-&gt;dev, dev-&gt;func, dev-&gt;vendor_id, dev-&gt;device_id,
              (long)dev-&gt;base_addr[0]&amp;0xFFF0);
      }
  }
  pci_cleanup(pciAccess);
  </code></pre>
  <p>but it still doesn't give me a way to <em>programmatically</em> select the 8 ttyS devices associated with the card(s) detected.</p>
  <p>Do I really need to shell <code>setserial</code> and parse the output??</p>