spicetools/external/stepmaniax-sdk/sdk/Windows/SMXDeviceSearch.cpp

180 lines
5.7 KiB
C++

#include "SMXDeviceSearch.h"
#include "SMXDeviceConnection.h"
#include "Helpers.h"
#include <string>
#include <memory>
#include <set>
using namespace std;
using namespace SMX;
extern "C"
{
#include <hidsdi.h>
}
#include <setupapi.h>
// Return all USB HID device paths. This doesn't open the device to filter just our devices.
static set<wstring> GetAllHIDDevicePaths(wstring &error)
{
HDEVINFO DeviceInfoSet = NULL;
GUID HidGuid;
HidD_GetHidGuid(&HidGuid);
DeviceInfoSet = SetupDiGetClassDevs(&HidGuid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
if(DeviceInfoSet == NULL)
return {};
set<wstring> paths;
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
for(DWORD iIndex = 0;
SetupDiEnumDeviceInterfaces(DeviceInfoSet, NULL, &HidGuid, iIndex, &DeviceInterfaceData);
iIndex++)
{
DWORD iSize;
if(!SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet, &DeviceInterfaceData, NULL, 0, &iSize, NULL))
{
// This call normally fails with ERROR_INSUFFICIENT_BUFFER.
int iError = GetLastError();
if(iError != ERROR_INSUFFICIENT_BUFFER)
{
Log(wssprintf(L"SetupDiGetDeviceInterfaceDetail failed: %ls", GetErrorString(iError).c_str()));
continue;
}
}
PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W) new uint8_t[iSize];
DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
SP_DEVINFO_DATA DeviceInfoData;
ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if(!SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet, &DeviceInterfaceData, DeviceInterfaceDetailData, iSize, NULL, &DeviceInfoData))
{
Log(wssprintf(L"SetupDiGetDeviceInterfaceDetail failed: %ls", GetErrorString(GetLastError()).c_str()));
delete[] (uint8_t *)DeviceInterfaceDetailData;
continue;
}
paths.insert(DeviceInterfaceDetailData->DevicePath);
delete[] (uint8_t *)DeviceInterfaceDetailData;
}
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
return paths;
}
static shared_ptr<AutoCloseHandle> OpenUSBDevice(LPCWSTR DevicePath, wstring &error)
{
// Log(ssprintf("Opening device: %ls", DevicePath));
HANDLE OpenDevice = CreateFileW(
DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL
);
if(OpenDevice == INVALID_HANDLE_VALUE)
{
// Many unrelated devices will fail to open, so don't return this as an error.
Log(wssprintf(L"Error opening device %ls: %ls", DevicePath, GetErrorString(GetLastError()).c_str()));
return nullptr;
}
auto result = make_shared<AutoCloseHandle>(OpenDevice);
// Get the HID attributes to check the IDs.
HIDD_ATTRIBUTES HidAttributes;
HidAttributes.Size = sizeof(HidAttributes);
if(!HidD_GetAttributes(result->value(), &HidAttributes))
{
Log(ssprintf("Error opening device %ls: HidD_GetAttributes failed", DevicePath));
error = L"HidD_GetAttributes failed";
return nullptr;
}
if(HidAttributes.VendorID != 0x2341 || HidAttributes.ProductID != 0x8037)
{
Log(ssprintf("Device %ls: not our device (ID %04x:%04x)", DevicePath, HidAttributes.VendorID, HidAttributes.ProductID));
return nullptr;
}
// Since we're using the default Arduino IDs, check the product name to make sure
// this isn't some other Arduino device.
WCHAR ProductName[255];
ZeroMemory(ProductName, sizeof(ProductName));
if(!HidD_GetProductString(result->value(), ProductName, 255))
{
Log(ssprintf("Error opening device %ls: HidD_GetProductString failed", DevicePath));
return nullptr;
}
if(wstring(ProductName) != L"StepManiaX")
{
Log(ssprintf("Device %ls: not our device (%ls)", DevicePath, ProductName));
return nullptr;
}
return result;
}
vector<shared_ptr<AutoCloseHandle>> SMX::SMXDeviceSearch::GetDevices(wstring &error)
{
set<wstring> aDevicePaths = GetAllHIDDevicePaths(error);
// Remove any entries in m_Devices that are no longer in the list.
for(wstring sPath: m_setLastDevicePaths)
{
if(aDevicePaths.find(sPath) != aDevicePaths.end())
continue;
Log(ssprintf("Device removed: %ls", sPath.c_str()));
m_Devices.erase(sPath);
}
// Check for new entries.
for(wstring sPath: aDevicePaths)
{
// Only look at devices that weren't in the list last time. OpenUSBDevice has
// to open the device and causes requests to be sent to it.
if(m_setLastDevicePaths.find(sPath) != m_setLastDevicePaths.end())
continue;
// This will return NULL if this isn't our device.
shared_ptr<AutoCloseHandle> hDevice = OpenUSBDevice(sPath.c_str(), error);
if(hDevice == nullptr)
continue;
Log(ssprintf("Device added: %ls", sPath.c_str()));
m_Devices[sPath] = hDevice;
}
m_setLastDevicePaths = aDevicePaths;
vector<shared_ptr<AutoCloseHandle>> aDevices;
for(auto it: m_Devices)
aDevices.push_back(it.second);
return aDevices;
}
void SMX::SMXDeviceSearch::DeviceWasClosed(shared_ptr<AutoCloseHandle> pDevice)
{
map<wstring, shared_ptr<AutoCloseHandle>> aDevices;
for(auto it: m_Devices)
{
if(it.second == pDevice)
{
m_setLastDevicePaths.erase(it.first);
}
else
{
aDevices[it.first] = it.second;
}
}
m_Devices = aDevices;
}