2
votes

I use setup API functions to find a USB device then use createfile to communicate with it. i.e. using SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces, SetupDiGetDeviceInterfaceDetail, etc.

I would like to be able to determine if the device is connected at USB2 speeds or USB3 speeds ie. SuperSpeed or not

How can I do this through the Windows APIs?

2

2 Answers

0
votes

I guess you will have to try WinUSB in the link there's a sample code of detecting the speed of USB. If you want the description of WinUSB you will find it here.

0
votes

This is what I ended up going with. It's convoluted. Cant believe there isn't an easier way:

#include "stdafx.h"

#include <Windows.h>
#include <Setupapi.h>
#include <winusb.h>

#undef LowSpeed
#include <Usbioctl.h>

#include <iostream>
#include <string>
#include <memory>
#include <vector>

class Usb_Device
{
private:
    std::wstring _driverKey;
    char _speed;

public:
    Usb_Device(int adapterNumber, std::wstring devicePath, char speed);

    virtual char GetSpeed(std::wstring driverKey);
};

class Usb_Hub : public Usb_Device
{
private:
    bool _isRootHub;
    std::wstring _deviceDescription;
    std::wstring _devicePath;

    std::vector<std::unique_ptr<Usb_Device>> _devices;

public:
    Usb_Hub(std::wstring devicePath, char speed);

    virtual char GetSpeed(std::wstring driverKey) override;
};

class Usb_Controller
{
private:

    GUID _interfaceClassGuid;
    std::wstring _devicePath;
    std::wstring _deviceDescription;
    std::wstring _driverKey;

    std::vector<std::unique_ptr<Usb_Device>> _devices;

public:
    Usb_Controller();

    char GetSpeed(std::wstring driverKey);
};

static std::unique_ptr<Usb_Device> BuildDevice(int portCount, std::wstring devicePath)
{
    std::unique_ptr<Usb_Device> ret;

    HANDLE handle = INVALID_HANDLE_VALUE;

    DWORD bytes = -1;
    DWORD bytesReturned = -1;
    BOOL isConnected = FALSE;
    char speed;

    // Open a handle to the Hub device
    handle = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
    if (handle != INVALID_HANDLE_VALUE)
    {
        bytes = sizeof(USB_NODE_CONNECTION_INFORMATION_EX);
        PUSB_NODE_CONNECTION_INFORMATION_EX nodeConnection = (PUSB_NODE_CONNECTION_INFORMATION_EX)(new char[bytes]);

        nodeConnection->ConnectionIndex = portCount;

        if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, nodeConnection, bytes, nodeConnection, bytes, &bytesReturned, 0))
        {
            isConnected = nodeConnection->ConnectionStatus == USB_CONNECTION_STATUS::DeviceConnected;
            speed = nodeConnection->Speed;
        }

        if (isConnected)
        {
            if (nodeConnection->DeviceDescriptor.bDeviceClass == 0x09 /*HubDevice*/)
            {
                bytes = sizeof(USB_NODE_CONNECTION_NAME);
                PUSB_NODE_CONNECTION_NAME nodeConnectionName = (PUSB_NODE_CONNECTION_NAME)(new char[bytes]);
                nodeConnectionName->ConnectionIndex = portCount;

                if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_NAME, nodeConnectionName, bytes, nodeConnectionName, bytes, &bytesReturned, 0))
                {
                    bytes = nodeConnectionName->ActualLength;
                    delete[] nodeConnectionName;

                    nodeConnectionName = (PUSB_NODE_CONNECTION_NAME)(new char[bytes]);
                    nodeConnectionName->ConnectionIndex = portCount;

                    if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_NAME, nodeConnectionName, bytes, nodeConnectionName, bytes, &bytesReturned, 0))
                    {
                        std::wstring name = std::wstring(L"\\\\?\\") + std::wstring(nodeConnectionName->NodeName);
                        ret = std::unique_ptr<Usb_Device>(new Usb_Hub(name, speed));
                    }
                }

                delete[] nodeConnectionName;
            }
            else
            {
                ret = std::unique_ptr<Usb_Device>(new Usb_Device(portCount, devicePath, speed));
            }
        }
        else
        {
            // Chuck this device
        }

        delete[] nodeConnection;
        CloseHandle(handle);
    }

    return ret;
}

Usb_Controller::Usb_Controller()
{
    BOOL success = TRUE;
    for (int index = 0; success; index++)
    {
        GUID guid;
        HRESULT hr = CLSIDFromString(L"{3abf6f2d-71c4-462a-8a92-1e6861e6af27}", (LPCLSID)&guid);

        unsigned char* ptr = new unsigned char[2048]; // Should really do two calls, but that's more effort

        HDEVINFO deviceInfoHandle = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

        // Create a device interface data structure
        SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { 0 };
        deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

        // Start the enumeration.
        success = SetupDiEnumDeviceInterfaces(deviceInfoHandle, 0, &guid, index, &deviceInterfaceData);
        if (success)
        {
            _interfaceClassGuid = deviceInterfaceData.InterfaceClassGuid;

            // Build a DevInfo data structure.
            SP_DEVINFO_DATA deviceInfoData = { 0 };
            deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

            // Now we can get some more detailed informations.
            DWORD nRequiredSize = 0;

            SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterfaceData, 0, 0, &nRequiredSize, 0);
            if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
            {
                PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new char[nRequiredSize]);
                memset(deviceInterfaceDetailData, 0, nRequiredSize);
                deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

                if (SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterfaceData, deviceInterfaceDetailData, nRequiredSize, &nRequiredSize, &deviceInfoData))
                {
                    _devicePath = deviceInterfaceDetailData->DevicePath;

                    // Get the device description and driver key name.
                    DWORD requiredSize = 0;
                    DWORD regType = REG_SZ;

                    if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInfoData, SPDRP_DEVICEDESC, &regType, ptr, 2048, &requiredSize))
                    {
                        _deviceDescription = reinterpret_cast<wchar_t*>(ptr);
                    }

                    if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInfoData, SPDRP_DRIVER, &regType, ptr, 2048, &requiredSize))
                    {
                        _driverKey = reinterpret_cast<wchar_t*>(ptr);
                    }
                }

                delete[] deviceInterfaceDetailData;
            }

            SetupDiDestroyDeviceInfoList(deviceInfoHandle);

            std::unique_ptr<Usb_Device> hub(new Usb_Hub(_devicePath, -1));
            _devices.push_back(std::move(hub));
        }
        else
        {
            success = false;
        }

        delete[] ptr;
    }
}

char Usb_Controller::GetSpeed(std::wstring driverKey)
{
    char speed = -1;

    for (auto it = _devices.begin(); it != _devices.end() && speed == -1; ++it)
    {
        if (*it != nullptr)
        {
            speed = (*it)->GetSpeed(driverKey);
        }
    }

    return speed;
}

Usb_Hub::Usb_Hub(std::wstring devicePath, char speed) :
    Usb_Device(-1, devicePath, speed)
{
    HANDLE handle1 = INVALID_HANDLE_VALUE;
    HANDLE handle2 = INVALID_HANDLE_VALUE;
    _deviceDescription = L"Standard-USB-Hub";
    _devicePath = devicePath;

    DWORD bytesReturned = -1;
    DWORD bytes = -1;

    BOOL success = TRUE;

    // Open a handle to the host controller.
    handle1 = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
    if (handle1 != INVALID_HANDLE_VALUE)
    {
        USB_ROOT_HUB_NAME rootHubName;
        memset(&rootHubName, 0, sizeof(USB_ROOT_HUB_NAME));

        // Get the root hub name.
        if (DeviceIoControl(handle1, IOCTL_USB_GET_ROOT_HUB_NAME, nullptr, 0, &rootHubName, sizeof(USB_ROOT_HUB_NAME), &bytesReturned, 0))
        {
            if (rootHubName.ActualLength > 0)
            {
                PUSB_ROOT_HUB_NAME actualRootHubName = (PUSB_ROOT_HUB_NAME)(new char[rootHubName.ActualLength]);

                if (DeviceIoControl(handle1, IOCTL_USB_GET_ROOT_HUB_NAME, nullptr, 0, actualRootHubName, rootHubName.ActualLength, &bytesReturned, 0))
                {
                    _isRootHub = true;
                    _deviceDescription = L"RootHub";
                    _devicePath = std::wstring(L"\\\\?\\") + std::wstring(actualRootHubName->RootHubName);
                }

                delete[] actualRootHubName;
            }
        }

        // Now let's open the hub (based upon the hub name we got above).
        int PortCount = 0;
        handle2 = CreateFile(_devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
        if (handle2 != INVALID_HANDLE_VALUE)
        {
            bytes = sizeof(USB_NODE_INFORMATION);
            PUSB_NODE_INFORMATION nodeInfo = (PUSB_NODE_INFORMATION)(new char[bytes]);
            memset(nodeInfo, 0, sizeof(USB_NODE_INFORMATION));

            nodeInfo->NodeType = USB_HUB_NODE::UsbHub;

            // Get the hub information.
            if (DeviceIoControl(handle2, IOCTL_USB_GET_NODE_INFORMATION, nodeInfo, bytes, nodeInfo, bytes, &bytesReturned, 0))
            {
                DWORD d = GetLastError();

                PortCount = nodeInfo->u.HubInformation.HubDescriptor.bNumberOfPorts;
            }

            delete[] nodeInfo;

            CloseHandle(handle2);
        }

        CloseHandle(handle1);

        for (int index = 1; index <= PortCount; index++)
        {
            std::unique_ptr<Usb_Device> device = BuildDevice(index, _devicePath);
            _devices.push_back(std::move(device));
        }
    }
    else
    {
        success = FALSE;
    }
}

char Usb_Hub::GetSpeed(std::wstring driverKey)
{
    char speed = Usb_Device::GetSpeed(driverKey);

    if (speed == -1)
    {
        for (auto it = _devices.begin(); it != _devices.end() && speed == -1; ++it)
        {
            if (*it != nullptr)
            {
                speed = (*it)->GetSpeed(driverKey);
            }
        }
    }

    return speed;
}

Usb_Device::Usb_Device(int adapterNumber, std::wstring devicePath, char speed)
{
    _speed = speed;

    HANDLE handle = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
    if (handle != INVALID_HANDLE_VALUE)
    {
        // Get the Driver Key Name (usefull in locating a device)
        DWORD bytesReturned = -1;
        DWORD bytes = sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME);
        PUSB_NODE_CONNECTION_DRIVERKEY_NAME driverKey = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)(new char[bytes]);

        driverKey->ConnectionIndex = adapterNumber;

        // Use an IOCTL call to request the Driver Key Name
        if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, driverKey, bytes, driverKey, bytes, &bytesReturned, 0))
        {
            bytes = driverKey->ActualLength;
            delete[] driverKey;

            driverKey = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)(new char[bytes]);
            driverKey->ConnectionIndex = adapterNumber;

            if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, driverKey, bytes, driverKey, bytes, &bytesReturned, 0))
            {
                _driverKey = driverKey->DriverKeyName;
            }
        }

        delete[] driverKey;

        CloseHandle(handle);
    }
}

char Usb_Device::GetSpeed(std::wstring driverKey)
{
    return _speed;
}

int main()
{
    Usb_Controller controller;

    GUID guid;
    HRESULT hr = CLSIDFromString(L"{AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA}", (LPCLSID)&guid);

    HDEVINFO deviceInfoHandle = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

    if (deviceInfoHandle != INVALID_HANDLE_VALUE)
    {
        int deviceIndex = 0;
        while (true)
        {
            SP_DEVICE_INTERFACE_DATA deviceInterface = { 0 };
            deviceInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

            if (SetupDiEnumDeviceInterfaces(deviceInfoHandle, 0, &guid, deviceIndex, &deviceInterface))
            {
                DWORD cbRequired = 0;

                SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, 0, 0, &cbRequired, 0);
                if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
                {
                    PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new char[cbRequired]);
                    memset(deviceInterfaceDetail, 0, cbRequired);
                    deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

                    if (!SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, deviceInterfaceDetail, cbRequired, &cbRequired, 0))
                    {
                        deviceIndex++;
                        continue;
                    }

                    // Initialize the structure before using it.
                    memset(deviceInterfaceDetail, 0, cbRequired);
                    deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

                    // Call the API a second time to retrieve the actual
                    // device path string.
                    BOOL status = SetupDiGetDeviceInterfaceDetail(
                        deviceInfoHandle,  // Handle to device information set
                        &deviceInterface,     // Pointer to current node in devinfo set
                        deviceInterfaceDetail,   // Pointer to buffer to receive device path
                        cbRequired,   // Length of user-allocated buffer
                        &cbRequired,  // Pointer to arg to receive required buffer length
                        NULL);        // Not interested in additional data

                    BOOL success = TRUE;
                    for (int i = 0; success; i++)
                    {
                        SP_DEVINFO_DATA deviceInterfaceData = { 0 };
                        deviceInterfaceData.cbSize = sizeof(SP_DEVINFO_DATA);

                        // Start the enumeration.
                        success = SetupDiEnumDeviceInfo(deviceInfoHandle, i, &deviceInterfaceData);

                        DWORD RequiredSize = 0;
                        DWORD regType = REG_SZ;
                        unsigned char* ptr = new unsigned char[2048];
                        if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInterfaceData, SPDRP_DRIVER, &regType, ptr, 2048, &RequiredSize))
                        {
                            char speed = controller.GetSpeed(reinterpret_cast<wchar_t*>(ptr));
                            std::wcout << std::wstring(reinterpret_cast<wchar_t*>(ptr)) << std::endl;
                            std::wcout << L"Speed: " << (int)speed << std::endl;
                        }

                        delete[] ptr;
                    }

                    auto hDeviceHandle = CreateFile(
                        deviceInterfaceDetail->DevicePath,
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        FILE_FLAG_OVERLAPPED,
                        NULL);

                    CloseHandle(hDeviceHandle);

                    delete[] deviceInterfaceDetail;
                }
            }
            else
            {
                break;
            }

            ++deviceIndex;
        }

        SetupDiDestroyDeviceInfoList(deviceInfoHandle);
    }

    return 0;
}