Skip to main content

Discovering USB Paths

When multiple readers share the same VID/PID, you need a way to target a specific physical USB port. RIK supports connecting by topological USB path -- a stable identifier for a physical port that doesn't change when you unplug and replug a device.

This page covers how to discover USB paths on each platform.

Path Formats

PlatformFormatExample
LinuxB-P or B-P.P.P (bus-port)1-7, 1-7.2
WindowsLocation path stringPCIROOT(0)#PCI(1400)#USBROOT(0)#USB(7)

The path identifies a physical port, not a device. Moving a reader to a different port changes the path. Reconnecting the same reader to the same port keeps the path stable.


C++ UsbDeviceEnumerator

The RIK SDK ships with UsbDeviceEnumerator, a C++ utility class for discovering connected readers and their topological paths. Include the header from the installed SDK:

#include "UsbDeviceEnumerator.h"

Enumerate All Known Readers

enumerate_readers.cpp
#include <iostream>
#include <iomanip>
#include "UsbDeviceEnumerator.h"

int main() {
using namespace RikTransport;

// Get the predefined VID/PID filters for all known rf IDEAS readers
auto filters = UsbDeviceEnumerator::GetKnownReaderFilters();

// Enumerate connected devices matching those filters
auto devices = UsbDeviceEnumerator::EnumerateDevices(filters);

std::cout << "Found " << devices.size() << " reader(s):\n\n";

for (const auto& device : devices) {
std::cout << " Path: " << device.TopologicalPath << "\n"
<< " VID:PID: " << std::hex << std::setfill('0')
<< "0x" << std::setw(4) << device.VendorId << ":"
<< "0x" << std::setw(4) << device.ProductId << "\n"
<< " Name: " << device.DeviceName << "\n"
<< " HID: " << device.HidDevicePath << "\n\n";
}

return 0;
}

Enumerate a Specific VID/PID

auto devices = UsbDeviceEnumerator::EnumerateDevices(0x0C27, 0x3BFA);

for (const auto& device : devices) {
std::cout << "Reader at path: " << device.TopologicalPath << "\n";
}

Enumerate Then Connect

enumerate_and_connect.cpp
#include <iostream>
#include <cwchar>
#include "UsbDeviceEnumerator.h"
#include "Reader/AbstractReader.h"
#include "Reader/Reader.h"

int main() {
using namespace RikTransport;
using namespace Rik;
using namespace RikCommon;

// 1. Discover readers
auto devices = UsbDeviceEnumerator::EnumerateDevices(0x0C27, 0x3BFA);

if (devices.empty()) {
std::cerr << "No readers found.\n";
return 1;
}

// 2. Pick the first one and get its path
const auto& target = devices[0];
std::cout << "Connecting to reader at path: " << target.TopologicalPath << "\n";

// 3. Convert path to wide string and set on ReaderDefinition
std::wstring widePath(target.TopologicalPath.begin(), target.TopologicalPath.end());

ReaderDefinition readerDef{};
readerDef.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
// UsbPath takes full precedence -- VID/PID are not required in this mode
std::wcsncpy(readerDef.DeviceId.UsbPath, widePath.c_str(),
sizeof(readerDef.DeviceId.UsbPath) / sizeof(wchar_t) - 1);

// 4. Connect
auto handle = AbstractReader::CreateReaderInstance(readerDef, 3);
auto* app = AbstractReader::GetInstance(handle);
app->Init();

auto metadata = app->GetMetadataStruct();
std::cout << "Connected: " << metadata.PartNumber << "\n";

app->Close();
AbstractReader::DestroyInstance(handle);

return 0;
}

CMake Setup

Link to the Application library as normal -- UsbDeviceEnumerator symbols are included:

CMakeLists.txt
find_package(ReaderIntegrationKit REQUIRED)

add_executable(enumerate_readers enumerate_readers.cpp)
target_link_libraries(enumerate_readers
PRIVATE ReaderIntegrationKit::ReaderIntegrationKit)

UsbDeviceInfo Fields

FieldTypeDescription
TopologicalPathstd::stringStable USB path (use as DeviceId.UsbPath)
VendorIduint16_tUSB Vendor ID
ProductIduint16_tUSB Product ID
DeviceNamestd::stringDevice name from USB descriptor
HidDevicePathstd::stringOS-level HID path (e.g., /dev/hidraw1)
note

UsbDeviceEnumerator is a C++ API only. It is not available through the C API, C#, or Python facades. For non-C++ workflows, use the platform-specific methods described below.


Linux -- Shell Script

RIK includes a shell script that enumerates USB devices and displays their topological paths by reading from Linux sysfs.

Usage

# Show all known rf IDEAS readers
./enum_usb_linux.sh

# Show all USB devices (not just known ones)
./enum_usb_linux.sh --all

# Search for a specific VID:PID
./enum_usb_linux.sh 0c27:3bfa

Example Output

TOPO PATH   VID:PID    DEVICE NAME                              HIDRAW
--------------------------------------------------------------------------------
1-7 0c27:3bfa RFIDeas reader /dev/hidraw1
1-8 0c27:3bfa RFIDeas reader /dev/hidraw2

The TOPO PATH column is the value to pass as DeviceId.UsbPath. In the example above, the reader on port 1-7 would be targeted with UsbPath = "1-7".

Linux -- Manual Discovery

You can also discover paths directly from sysfs without the script:

# List all USB devices with their topological paths
for dev in /sys/bus/usb/devices/*/; do
# Skip interfaces and root hubs
[[ "$(basename "$dev")" == *:* ]] && continue
[[ "$(basename "$dev")" == usb* ]] && continue

[ -f "$dev/idVendor" ] || continue

vid=$(cat "$dev/idVendor" 2>/dev/null)
pid=$(cat "$dev/idProduct" 2>/dev/null)
bus=$(cat "$dev/busnum" 2>/dev/null)
port=$(cat "$dev/devpath" 2>/dev/null)
product=$(cat "$dev/product" 2>/dev/null)

echo "${bus}-${port} ${vid}:${pid} ${product}"
done

Filter for rf IDEAS devices:

# Show only rf IDEAS readers (VID 0c27)
for dev in /sys/bus/usb/devices/*/; do
[[ "$(basename "$dev")" == *:* ]] && continue
[ -f "$dev/idVendor" ] || continue
vid=$(cat "$dev/idVendor" 2>/dev/null)
[ "$vid" = "0c27" ] || continue

pid=$(cat "$dev/idProduct" 2>/dev/null)
bus=$(cat "$dev/busnum" 2>/dev/null)
port=$(cat "$dev/devpath" 2>/dev/null)
product=$(cat "$dev/product" 2>/dev/null)

echo "Path: ${bus}-${port} VID:PID: ${vid}:${pid} Name: ${product}"
done

Windows -- PowerShell

On Windows, use PowerShell to discover USB device location paths:

enum_usb_windows.ps1
# List all USB devices with location paths
Get-PnpDevice -Class USB -Status OK |
ForEach-Object {
$device = $_
$props = Get-PnpDeviceProperty -InstanceId $device.InstanceId

$locationPaths = ($props | Where-Object KeyName -eq 'DEVPKEY_Device_LocationPaths').Data
$hardwareIds = ($props | Where-Object KeyName -eq 'DEVPKEY_Device_HardwareIds').Data

if ($locationPaths -and $hardwareIds) {
# Extract VID/PID from hardware ID string
$vidMatch = [regex]::Match($hardwareIds[0], 'VID_([0-9A-Fa-f]{4})')
$pidMatch = [regex]::Match($hardwareIds[0], 'PID_([0-9A-Fa-f]{4})')

if ($vidMatch.Success) {
[PSCustomObject]@{
LocationPath = $locationPaths[0]
VID = $vidMatch.Groups[1].Value
PID = if ($pidMatch.Success) { $pidMatch.Groups[1].Value } else { "????" }
Name = $device.FriendlyName
}
}
}
} |
Format-Table -AutoSize

Filter for rf IDEAS Devices

# Show only rf IDEAS readers (VID 0C27)
Get-PnpDevice -Class USB -Status OK |
ForEach-Object {
$props = Get-PnpDeviceProperty -InstanceId $_.InstanceId
$locationPaths = ($props | Where-Object KeyName -eq 'DEVPKEY_Device_LocationPaths').Data
$hardwareIds = ($props | Where-Object KeyName -eq 'DEVPKEY_Device_HardwareIds').Data

if ($locationPaths -and $hardwareIds -and $hardwareIds[0] -match 'VID_0C27') {
Write-Host "Path: $($locationPaths[0])"
Write-Host "Name: $($_.FriendlyName)"
Write-Host ""
}
}

Windows -- Device Manager

You can also find the location path through Device Manager:

  1. Open Device Manager
  2. Find your reader under Human Interface Devices or Universal Serial Bus devices
  3. Right-click > Properties > Details tab
  4. Select Location paths from the property dropdown
  5. The value (e.g., PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(7)) is the USB path to use

Using the Discovered Path

Once you have a topological path, pass it as DeviceId.UsbPath when creating a ReaderDefinition. See Connect by USB Path for full examples in all languages.

// Linux path from enum_usb_linux.sh or UsbDeviceEnumerator
std::wstring usbPath = L"1-7";

// Windows path from PowerShell or Device Manager
// std::wstring usbPath = L"PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(7)";

std::wcsncpy(readerDef.DeviceId.UsbPath, usbPath.c_str(),
sizeof(readerDef.DeviceId.UsbPath) / sizeof(wchar_t) - 1);
tip

USB paths are stable across device reconnections and system reboots as long as the reader stays in the same physical port. They are ideal for fixed installations (kiosks, access control panels, multi-reader deployments).