mirror of
https://github.com/AxioDL/boo.git
synced 2025-05-15 03:41:23 +00:00
HID Parser for Win32
This commit is contained in:
parent
0f2a838bfb
commit
1f14cc09a1
@ -5,6 +5,12 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include "boo/System.hpp"
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include <hidsdi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
@ -45,7 +51,11 @@ public:
|
|||||||
size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length);
|
size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length);
|
||||||
|
|
||||||
/* High-Level API */
|
/* High-Level API */
|
||||||
|
#if _WIN32
|
||||||
|
const PHIDP_PREPARSED_DATA getReportDescriptor();
|
||||||
|
#else
|
||||||
std::vector<uint8_t> getReportDescriptor();
|
std::vector<uint8_t> getReportDescriptor();
|
||||||
|
#endif
|
||||||
bool sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0);
|
bool sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0);
|
||||||
size_t receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0); // Prefer callback version
|
size_t receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0); // Prefer callback version
|
||||||
virtual void receivedHIDReport(const uint8_t* /*data*/, size_t /*length*/, HIDReportType /*tp*/, uint32_t /*message*/) {}
|
virtual void receivedHIDReport(const uint8_t* /*data*/, size_t /*length*/, HIDReportType /*tp*/, uint32_t /*message*/) {}
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
|
||||||
static class DeviceFinder* skDevFinder = NULL;
|
|
||||||
|
|
||||||
class DeviceFinder
|
class DeviceFinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -31,6 +29,7 @@ public:
|
|||||||
static inline DeviceFinder* instance() {return skDevFinder;}
|
static inline DeviceFinder* instance() {return skDevFinder;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static class DeviceFinder* skDevFinder;
|
||||||
|
|
||||||
/* Types this finder is interested in (immutable) */
|
/* Types this finder is interested in (immutable) */
|
||||||
DeviceSignature::TDeviceSignatureSet m_types;
|
DeviceSignature::TDeviceSignatureSet m_types;
|
||||||
|
@ -29,7 +29,7 @@ public:
|
|||||||
void initialCycle();
|
void initialCycle();
|
||||||
void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
|
void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
|
||||||
|
|
||||||
void enumerateValues(std::function<bool(const HIDMainItem& item)>& valueCB) const;
|
void enumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include <hidsdi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
@ -157,6 +162,8 @@ struct HIDMainItem
|
|||||||
|
|
||||||
HIDMainItem() = default;
|
HIDMainItem() = default;
|
||||||
HIDMainItem(uint32_t flags, const HIDItemState& state, uint32_t reportIdx);
|
HIDMainItem(uint32_t flags, const HIDItemState& state, uint32_t reportIdx);
|
||||||
|
HIDMainItem(uint32_t flags, HIDUsagePage usagePage, HIDUsage usage,
|
||||||
|
HIDRange logicalRange, int32_t reportSize);
|
||||||
const char* GetUsagePageName() const;
|
const char* GetUsagePageName() const;
|
||||||
const char* GetUsageName() const;
|
const char* GetUsageName() const;
|
||||||
};
|
};
|
||||||
@ -171,22 +178,35 @@ public:
|
|||||||
Error
|
Error
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
ParserStatus m_status = ParserStatus::OK;
|
||||||
|
#if _WIN32
|
||||||
|
std::vector<HIDMainItem> m_itemPool;
|
||||||
|
mutable std::vector<HIDP_DATA> m_dataList;
|
||||||
|
PHIDP_PREPARSED_DATA m_descriptorData = nullptr;
|
||||||
|
#else
|
||||||
std::unique_ptr<HIDMainItem[]> m_itemPool;
|
std::unique_ptr<HIDMainItem[]> m_itemPool;
|
||||||
using Report = std::pair<uint32_t, std::pair<uint32_t, uint32_t>>;
|
using Report = std::pair<uint32_t, std::pair<uint32_t, uint32_t>>;
|
||||||
std::unique_ptr<Report[]> m_reportPool;
|
std::unique_ptr<Report[]> m_reportPool;
|
||||||
std::pair<uint32_t, uint32_t> m_inputReports = {};
|
std::pair<uint32_t, uint32_t> m_inputReports = {};
|
||||||
std::pair<uint32_t, uint32_t> m_outputReports = {};
|
std::pair<uint32_t, uint32_t> m_outputReports = {};
|
||||||
std::pair<uint32_t, uint32_t> m_featureReports = {};
|
std::pair<uint32_t, uint32_t> m_featureReports = {};
|
||||||
ParserStatus m_status = ParserStatus::OK;
|
|
||||||
bool m_multipleReports = false;
|
bool m_multipleReports = false;
|
||||||
ParserStatus ParseItem(HIDReports& reportsOut,
|
ParserStatus ParseItem(HIDReports& reportsOut,
|
||||||
std::stack<HIDItemState>& stateStack, std::stack<HIDCollectionItem>& collectionStack,
|
std::stack<HIDItemState>& stateStack,
|
||||||
|
std::stack<HIDCollectionItem>& collectionStack,
|
||||||
const uint8_t*& it, const uint8_t* end);
|
const uint8_t*& it, const uint8_t* end);
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#if _WIN32
|
||||||
|
ParserStatus Parse(const PHIDP_PREPARSED_DATA descriptorData);
|
||||||
|
#else
|
||||||
ParserStatus Parse(const uint8_t* descriptorData, size_t len);
|
ParserStatus Parse(const uint8_t* descriptorData, size_t len);
|
||||||
|
#endif
|
||||||
operator bool() const { return m_status == ParserStatus::Done; }
|
operator bool() const { return m_status == ParserStatus::Done; }
|
||||||
void EnumerateValues(std::function<bool(uint32_t rep, const HIDMainItem& item)>& valueCB) const;
|
void EnumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const;
|
||||||
void ScanValues(std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB,
|
void ScanValues(const std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB,
|
||||||
const uint8_t* data, size_t len) const;
|
const uint8_t* data, size_t len) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,12 +54,21 @@ size_t DeviceBase::receiveUSBInterruptTransfer(uint8_t* data, size_t length)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
const PHIDP_PREPARSED_DATA DeviceBase::getReportDescriptor()
|
||||||
|
{
|
||||||
|
if (m_hidDev)
|
||||||
|
return m_hidDev->_getReportDescriptor();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
#else
|
||||||
std::vector<uint8_t> DeviceBase::getReportDescriptor()
|
std::vector<uint8_t> DeviceBase::getReportDescriptor()
|
||||||
{
|
{
|
||||||
if (m_hidDev)
|
if (m_hidDev)
|
||||||
return m_hidDev->_getReportDescriptor();
|
return m_hidDev->_getReportDescriptor();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
|
||||||
|
DeviceFinder* DeviceFinder::skDevFinder = nullptr;
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
/* Windows-specific WM_DEVICECHANGED handler */
|
/* Windows-specific WM_DEVICECHANGED handler */
|
||||||
LRESULT DeviceFinder::winDevChangedHandler(WPARAM wParam, LPARAM lParam)
|
LRESULT DeviceFinder::winDevChangedHandler(WPARAM wParam, LPARAM lParam)
|
||||||
|
@ -20,8 +20,13 @@ void GenericPad::deviceDisconnected()
|
|||||||
|
|
||||||
void GenericPad::initialCycle()
|
void GenericPad::initialCycle()
|
||||||
{
|
{
|
||||||
|
#if _WIN32
|
||||||
|
const PHIDP_PREPARSED_DATA reportDesc = getReportDescriptor();
|
||||||
|
m_parser.Parse(reportDesc);
|
||||||
|
#else
|
||||||
std::vector<uint8_t> reportDesc = getReportDescriptor();
|
std::vector<uint8_t> reportDesc = getReportDescriptor();
|
||||||
m_parser.Parse(reportDesc.data(), reportDesc.size());
|
m_parser.Parse(reportDesc.data(), reportDesc.size());
|
||||||
|
#endif
|
||||||
if (m_cb)
|
if (m_cb)
|
||||||
m_cb->controllerConnected();
|
m_cb->controllerConnected();
|
||||||
}
|
}
|
||||||
@ -39,11 +44,9 @@ void GenericPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReport
|
|||||||
m_parser.ScanValues(func, data, length);
|
m_parser.ScanValues(func, data, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericPad::enumerateValues(std::function<bool(const HIDMainItem& item)>& valueCB) const
|
void GenericPad::enumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const
|
||||||
{
|
{
|
||||||
std::function<bool(uint32_t, const HIDMainItem&)> func =
|
m_parser.EnumerateValues(valueCB);
|
||||||
[&](uint32_t rep, const HIDMainItem& item) { return valueCB(item); };
|
|
||||||
m_parser.EnumerateValues(func);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ namespace boo
|
|||||||
class HIDDeviceWinUSB final : public IHIDDevice
|
class HIDDeviceWinUSB final : public IHIDDevice
|
||||||
{
|
{
|
||||||
DeviceToken& m_token;
|
DeviceToken& m_token;
|
||||||
DeviceBase& m_devImp;
|
std::shared_ptr<DeviceBase> m_devImp;
|
||||||
|
|
||||||
HANDLE m_devHandle = 0;
|
HANDLE m_devHandle = 0;
|
||||||
HANDLE m_hidHandle = 0;
|
HANDLE m_hidHandle = 0;
|
||||||
@ -68,7 +68,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _threadProcUSBLL(HIDDeviceWinUSB* device)
|
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceWinUSB> device)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
char errStr[256];
|
char errStr[256];
|
||||||
@ -87,7 +87,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||||
device->m_token.getProductName().c_str(),
|
device->m_token.getProductName().c_str(),
|
||||||
device->m_devPath.c_str(), GetLastError());
|
device->m_devPath.c_str(), GetLastError());
|
||||||
device->m_devImp.deviceError(errStr);
|
device->m_devImp->deviceError(errStr);
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
return;
|
return;
|
||||||
@ -98,7 +98,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||||
device->m_token.getProductName().c_str(),
|
device->m_token.getProductName().c_str(),
|
||||||
device->m_devPath.c_str(), GetLastError());
|
device->m_devPath.c_str(), GetLastError());
|
||||||
device->m_devImp.deviceError(errStr);
|
device->m_devImp->deviceError(errStr);
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
CloseHandle(device->m_devHandle);
|
CloseHandle(device->m_devHandle);
|
||||||
@ -112,7 +112,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||||
device->m_token.getProductName().c_str(),
|
device->m_token.getProductName().c_str(),
|
||||||
device->m_devPath.c_str(), GetLastError());
|
device->m_devPath.c_str(), GetLastError());
|
||||||
device->m_devImp.deviceError(errStr);
|
device->m_devImp->deviceError(errStr);
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
CloseHandle(device->m_devHandle);
|
CloseHandle(device->m_devHandle);
|
||||||
@ -137,10 +137,10 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
/* Start transfer loop */
|
/* Start transfer loop */
|
||||||
device->m_devImp.initialCycle();
|
device->m_devImp->initialCycle();
|
||||||
while (device->m_runningTransferLoop)
|
while (device->m_runningTransferLoop)
|
||||||
device->m_devImp.transferCycle();
|
device->m_devImp->transferCycle();
|
||||||
device->m_devImp.finalCycle();
|
device->m_devImp->finalCycle();
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
WinUsb_Free(device->m_usbHandle);
|
WinUsb_Free(device->m_usbHandle);
|
||||||
@ -148,7 +148,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
device->m_devHandle = 0;
|
device->m_devHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _threadProcBTLL(HIDDeviceWinUSB* device)
|
static void _threadProcBTLL(std::shared_ptr<HIDDeviceWinUSB> device)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
|
||||||
@ -158,17 +158,19 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
/* Start transfer loop */
|
/* Start transfer loop */
|
||||||
device->m_devImp.initialCycle();
|
device->m_devImp->initialCycle();
|
||||||
while (device->m_runningTransferLoop)
|
while (device->m_runningTransferLoop)
|
||||||
device->m_devImp.transferCycle();
|
device->m_devImp->transferCycle();
|
||||||
device->m_devImp.finalCycle();
|
device->m_devImp->finalCycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t m_minFeatureSz = 0;
|
size_t m_minFeatureSz = 0;
|
||||||
size_t m_minInputSz = 0;
|
size_t m_minInputSz = 0;
|
||||||
size_t m_minOutputSz = 0;
|
size_t m_minOutputSz = 0;
|
||||||
|
|
||||||
static void _threadProcHID(HIDDeviceWinUSB* device)
|
PHIDP_PREPARSED_DATA m_preparsedData = nullptr;
|
||||||
|
|
||||||
|
static void _threadProcHID(std::shared_ptr<HIDDeviceWinUSB> device)
|
||||||
{
|
{
|
||||||
char errStr[256];
|
char errStr[256];
|
||||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
@ -187,30 +189,28 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||||
device->m_token.getProductName().c_str(),
|
device->m_token.getProductName().c_str(),
|
||||||
device->m_devPath.c_str(), GetLastError());
|
device->m_devPath.c_str(), GetLastError());
|
||||||
device->m_devImp.deviceError(errStr);
|
device->m_devImp->deviceError(errStr);
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PHIDP_PREPARSED_DATA preparsedData;
|
if (!HidD_GetPreparsedData(device->m_hidHandle, &device->m_preparsedData))
|
||||||
if (!HidD_GetPreparsedData(device->m_hidHandle, &preparsedData))
|
|
||||||
{
|
{
|
||||||
_snprintf(errStr, 256, "Unable get preparsed data of %s@%s: %d\n",
|
_snprintf(errStr, 256, "Unable get preparsed data of %s@%s: %d\n",
|
||||||
device->m_token.getProductName().c_str(),
|
device->m_token.getProductName().c_str(),
|
||||||
device->m_devPath.c_str(), GetLastError());
|
device->m_devPath.c_str(), GetLastError());
|
||||||
device->m_devImp.deviceError(errStr);
|
device->m_devImp->deviceError(errStr);
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HIDP_CAPS caps;
|
HIDP_CAPS caps;
|
||||||
HidP_GetCaps(preparsedData, &caps);
|
HidP_GetCaps(device->m_preparsedData, &caps);
|
||||||
device->m_minFeatureSz = caps.FeatureReportByteLength;
|
device->m_minFeatureSz = caps.FeatureReportByteLength;
|
||||||
device->m_minInputSz = caps.InputReportByteLength;
|
device->m_minInputSz = caps.InputReportByteLength;
|
||||||
device->m_minOutputSz = caps.OutputReportByteLength;
|
device->m_minOutputSz = caps.OutputReportByteLength;
|
||||||
HidD_FreePreparsedData(preparsedData);
|
|
||||||
|
|
||||||
/* Return control to main thread */
|
/* Return control to main thread */
|
||||||
device->m_runningTransferLoop = true;
|
device->m_runningTransferLoop = true;
|
||||||
@ -218,21 +218,23 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
/* Allocate read buffer */
|
/* Allocate read buffer */
|
||||||
size_t inBufferSz = std::max(device->m_minInputSz, device->m_devImp.getInputBufferSize());
|
size_t inBufferSz = device->m_minInputSz;
|
||||||
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[inBufferSz]);
|
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[inBufferSz]);
|
||||||
|
|
||||||
/* Start transfer loop */
|
/* Start transfer loop */
|
||||||
device->m_devImp.initialCycle();
|
device->m_devImp->initialCycle();
|
||||||
while (device->m_runningTransferLoop)
|
while (device->m_runningTransferLoop)
|
||||||
{
|
{
|
||||||
device->ReadCycle(readBuf.get(), inBufferSz);
|
device->ReadCycle(readBuf.get(), inBufferSz);
|
||||||
device->m_devImp.transferCycle();
|
if (device->m_runningTransferLoop)
|
||||||
|
device->m_devImp->transferCycle();
|
||||||
}
|
}
|
||||||
device->m_devImp.finalCycle();
|
device->m_devImp->finalCycle();
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
CloseHandle(device->m_overlapped.hEvent);
|
CloseHandle(device->m_overlapped.hEvent);
|
||||||
CloseHandle(device->m_hidHandle);
|
CloseHandle(device->m_hidHandle);
|
||||||
|
HidD_FreePreparsedData(device->m_preparsedData);
|
||||||
device->m_hidHandle = nullptr;
|
device->m_hidHandle = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,6 +246,11 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
std::vector<uint8_t> m_sendBuf;
|
std::vector<uint8_t> m_sendBuf;
|
||||||
std::vector<uint8_t> m_recvBuf;
|
std::vector<uint8_t> m_recvBuf;
|
||||||
|
|
||||||
|
const PHIDP_PREPARSED_DATA _getReportDescriptor()
|
||||||
|
{
|
||||||
|
return m_preparsedData;
|
||||||
|
}
|
||||||
|
|
||||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||||
{
|
{
|
||||||
size_t maxOut = std::max(m_minFeatureSz, std::max(m_minOutputSz, length));
|
size_t maxOut = std::max(m_minFeatureSz, std::max(m_minOutputSz, length));
|
||||||
@ -323,7 +330,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HIDDeviceWinUSB(DeviceToken& token, DeviceBase& devImp)
|
HIDDeviceWinUSB(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||||
: m_token(token),
|
: m_token(token),
|
||||||
m_devImp(devImp),
|
m_devImp(devImp),
|
||||||
m_devPath(token.getDevicePath())
|
m_devPath(token.getDevicePath())
|
||||||
@ -335,11 +342,11 @@ public:
|
|||||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||||
DeviceType dType = m_token.getDeviceType();
|
DeviceType dType = m_token.getDeviceType();
|
||||||
if (dType == DeviceType::USB)
|
if (dType == DeviceType::USB)
|
||||||
m_thread = std::thread(_threadProcUSBLL, this);
|
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
|
||||||
else if (dType == DeviceType::Bluetooth)
|
else if (dType == DeviceType::Bluetooth)
|
||||||
m_thread = std::thread(_threadProcBTLL, this);
|
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
|
||||||
else if (dType == DeviceType::HID)
|
else if (dType == DeviceType::HID)
|
||||||
m_thread = std::thread(_threadProcHID, this);
|
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
|
||||||
else
|
else
|
||||||
throw std::runtime_error("invalid token supplied to device constructor");
|
throw std::runtime_error("invalid token supplied to device constructor");
|
||||||
m_initCond.wait(lk);
|
m_initCond.wait(lk);
|
||||||
@ -348,7 +355,8 @@ public:
|
|||||||
~HIDDeviceWinUSB()
|
~HIDDeviceWinUSB()
|
||||||
{
|
{
|
||||||
m_runningTransferLoop = false;
|
m_runningTransferLoop = false;
|
||||||
m_thread.join();
|
if (m_thread.joinable())
|
||||||
|
m_thread.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
OVERLAPPED m_overlapped = {};
|
OVERLAPPED m_overlapped = {};
|
||||||
@ -362,7 +370,12 @@ public:
|
|||||||
if (!Result)
|
if (!Result)
|
||||||
{
|
{
|
||||||
DWORD Error = GetLastError();
|
DWORD Error = GetLastError();
|
||||||
if (Error != ERROR_IO_PENDING)
|
if (Error == ERROR_DEVICE_NOT_CONNECTED)
|
||||||
|
{
|
||||||
|
m_runningTransferLoop = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (Error != ERROR_IO_PENDING)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Read Failed: %08X\n", Error);
|
fprintf(stderr, "Read Failed: %08X\n", Error);
|
||||||
return;
|
return;
|
||||||
@ -373,7 +386,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_devImp.receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]);
|
m_devImp->receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,6 +135,32 @@ class HIDListenerWinUSB final : public IHIDListener
|
|||||||
&devpropType, (BYTE*)manufW, 1024, &manufSz, 0);
|
&devpropType, (BYTE*)manufW, 1024, &manufSz, 0);
|
||||||
WideCharToMultiByte(CP_UTF8, 0, manufW, -1, manuf, 1024, nullptr, nullptr);
|
WideCharToMultiByte(CP_UTF8, 0, manufW, -1, manuf, 1024, nullptr, nullptr);
|
||||||
|
|
||||||
|
if (type == DeviceType::HID)
|
||||||
|
{
|
||||||
|
HANDLE devHnd = CreateFileA(DeviceInterfaceDetailData.wtf.DevicePath,
|
||||||
|
GENERIC_WRITE | GENERIC_READ,
|
||||||
|
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||||||
|
NULL);
|
||||||
|
if (INVALID_HANDLE_VALUE == devHnd)
|
||||||
|
continue;
|
||||||
|
PHIDP_PREPARSED_DATA preparsedData;
|
||||||
|
if (!HidD_GetPreparsedData(devHnd, &preparsedData))
|
||||||
|
{
|
||||||
|
CloseHandle(devHnd);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
HIDP_CAPS caps;
|
||||||
|
HidP_GetCaps(preparsedData, &caps);
|
||||||
|
HidD_FreePreparsedData(preparsedData);
|
||||||
|
CloseHandle(devHnd);
|
||||||
|
/* Filter non joysticks and gamepads */
|
||||||
|
if (caps.UsagePage != 1 || (caps.Usage != 4 && caps.Usage != 5))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Store as a shouting string (to keep hash-lookups consistent) */
|
/* Store as a shouting string (to keep hash-lookups consistent) */
|
||||||
CharUpperA(DeviceInterfaceDetailData.wtf.DevicePath);
|
CharUpperA(DeviceInterfaceDetailData.wtf.DevicePath);
|
||||||
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
#include "boo/inputdev/HIDParser.hpp"
|
#include "boo/inputdev/HIDParser.hpp"
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#undef min
|
||||||
|
#undef max
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
@ -289,6 +293,12 @@ HIDMainItem::HIDMainItem(uint32_t flags, const HIDItemState& state, uint32_t rep
|
|||||||
m_reportSize = state.m_reportSize;
|
m_reportSize = state.m_reportSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HIDMainItem::HIDMainItem(uint32_t flags, HIDUsagePage usagePage, HIDUsage usage,
|
||||||
|
HIDRange logicalRange, int32_t reportSize)
|
||||||
|
: m_flags(uint16_t(flags)), m_usagePage(usagePage), m_usage(usage),
|
||||||
|
m_logicalRange(logicalRange), m_reportSize(reportSize)
|
||||||
|
{}
|
||||||
|
|
||||||
const char* HIDMainItem::GetUsagePageName() const
|
const char* HIDMainItem::GetUsagePageName() const
|
||||||
{
|
{
|
||||||
if (int(m_usagePage) >= std::extent<decltype(UsagePageNames)>::value)
|
if (int(m_usagePage) >= std::extent<decltype(UsagePageNames)>::value)
|
||||||
@ -385,9 +395,92 @@ struct HIDReports
|
|||||||
void AddFeatureItem(uint32_t flags, const HIDItemState& state) { _AddItem(m_featureReports, flags, state); }
|
void AddFeatureItem(uint32_t flags, const HIDItemState& state) { _AddItem(m_featureReports, flags, state); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
HIDParser::ParserStatus HIDParser::Parse(const PHIDP_PREPARSED_DATA descriptorData)
|
||||||
|
{
|
||||||
|
/* User mode HID report descriptor isn't available on Win32.
|
||||||
|
* Opaque preparsed data must be enumerated and sorted into
|
||||||
|
* iterable items.
|
||||||
|
*
|
||||||
|
* Wine's implementation has a good illustration of what's
|
||||||
|
* going on here:
|
||||||
|
* https://github.com/wine-mirror/wine/blob/master/dlls/hidclass.sys/descriptor.c
|
||||||
|
*
|
||||||
|
* (Thanks for this pointless pain-in-the-ass Microsoft)
|
||||||
|
*/
|
||||||
|
|
||||||
|
m_descriptorData = descriptorData;
|
||||||
|
HIDP_CAPS caps;
|
||||||
|
HidP_GetCaps(descriptorData, &caps);
|
||||||
|
m_dataList.resize(HidP_MaxDataListLength(HidP_Input, descriptorData));
|
||||||
|
|
||||||
|
std::map<uint32_t, HIDMainItem> inputItems;
|
||||||
|
|
||||||
|
{
|
||||||
|
/* First enumerate buttons */
|
||||||
|
USHORT length = caps.NumberInputButtonCaps;
|
||||||
|
std::vector<HIDP_BUTTON_CAPS> bCaps(caps.NumberInputButtonCaps, HIDP_BUTTON_CAPS());
|
||||||
|
HidP_GetButtonCaps(HidP_Input, bCaps.data(), &length, descriptorData);
|
||||||
|
for (const HIDP_BUTTON_CAPS& caps : bCaps)
|
||||||
|
{
|
||||||
|
if (caps.IsRange)
|
||||||
|
{
|
||||||
|
int usage = caps.Range.UsageMin;
|
||||||
|
for (int i=caps.Range.DataIndexMin ; i<=caps.Range.DataIndexMax ; ++i, ++usage)
|
||||||
|
{
|
||||||
|
inputItems.insert(std::make_pair(i,
|
||||||
|
HIDMainItem(caps.BitField, HIDUsagePage(caps.UsagePage),
|
||||||
|
HIDUsage(usage), std::make_pair(0, 1), 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inputItems.insert(std::make_pair(caps.NotRange.DataIndex,
|
||||||
|
HIDMainItem(caps.BitField, HIDUsagePage(caps.UsagePage),
|
||||||
|
HIDUsage(caps.NotRange.Usage), std::make_pair(0, 1), 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
/* Now enumerate values */
|
||||||
|
USHORT length = caps.NumberInputValueCaps;
|
||||||
|
std::vector<HIDP_VALUE_CAPS> vCaps(caps.NumberInputValueCaps, HIDP_VALUE_CAPS());
|
||||||
|
HidP_GetValueCaps(HidP_Input, vCaps.data(), &length, descriptorData);
|
||||||
|
for (const HIDP_VALUE_CAPS& caps : vCaps)
|
||||||
|
{
|
||||||
|
if (caps.IsRange)
|
||||||
|
{
|
||||||
|
int usage = caps.Range.UsageMin;
|
||||||
|
for (int i=caps.Range.DataIndexMin ; i<=caps.Range.DataIndexMax ; ++i, ++usage)
|
||||||
|
{
|
||||||
|
inputItems.insert(std::make_pair(i,
|
||||||
|
HIDMainItem(caps.BitField, HIDUsagePage(caps.UsagePage), HIDUsage(usage),
|
||||||
|
std::make_pair(caps.LogicalMin, caps.LogicalMax), caps.BitSize)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inputItems.insert(std::make_pair(caps.NotRange.DataIndex,
|
||||||
|
HIDMainItem(caps.BitField, HIDUsagePage(caps.UsagePage), HIDUsage(caps.NotRange.Usage),
|
||||||
|
HIDRange(caps.LogicalMin, caps.LogicalMax), caps.BitSize)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_itemPool.reserve(inputItems.size());
|
||||||
|
for (const auto& item : inputItems)
|
||||||
|
m_itemPool.push_back(item.second);
|
||||||
|
|
||||||
|
m_status = ParserStatus::Done;
|
||||||
|
return ParserStatus::Done;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
HIDParser::ParserStatus
|
HIDParser::ParserStatus
|
||||||
HIDParser::ParseItem(HIDReports& reportsOut,
|
HIDParser::ParseItem(HIDReports& reportsOut,
|
||||||
std::stack<HIDItemState>& stateStack, std::stack<HIDCollectionItem>& collectionStack,
|
std::stack<HIDItemState>& stateStack,
|
||||||
|
std::stack<HIDCollectionItem>& collectionStack,
|
||||||
const uint8_t*& it, const uint8_t* end)
|
const uint8_t*& it, const uint8_t* end)
|
||||||
{
|
{
|
||||||
ParserStatus status = ParserStatus::OK;
|
ParserStatus status = ParserStatus::OK;
|
||||||
@ -567,8 +660,24 @@ HIDParser::ParserStatus HIDParser::Parse(const uint8_t* descriptorData, size_t l
|
|||||||
|
|
||||||
return m_status;
|
return m_status;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void HIDParser::EnumerateValues(std::function<bool(uint32_t rep, const HIDMainItem& item)>& valueCB) const
|
#if _WIN32
|
||||||
|
void HIDParser::EnumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const
|
||||||
|
{
|
||||||
|
if (m_status != ParserStatus::Done)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const HIDMainItem& item : m_itemPool)
|
||||||
|
{
|
||||||
|
if (item.IsConstant())
|
||||||
|
continue;
|
||||||
|
if (!valueCB(item))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void HIDParser::EnumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const
|
||||||
{
|
{
|
||||||
if (m_status != ParserStatus::Done)
|
if (m_status != ParserStatus::Done)
|
||||||
return;
|
return;
|
||||||
@ -579,11 +688,50 @@ void HIDParser::EnumerateValues(std::function<bool(uint32_t rep, const HIDMainIt
|
|||||||
for (uint32_t j=rep.second.first ; j<rep.second.second ; ++j)
|
for (uint32_t j=rep.second.first ; j<rep.second.second ; ++j)
|
||||||
{
|
{
|
||||||
const HIDMainItem& item = m_itemPool[j];
|
const HIDMainItem& item = m_itemPool[j];
|
||||||
if (!valueCB(rep.first, item))
|
if (item.IsConstant())
|
||||||
|
continue;
|
||||||
|
if (!valueCB(item))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
void HIDParser::ScanValues(const std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB,
|
||||||
|
const uint8_t* data, size_t len) const
|
||||||
|
{
|
||||||
|
if (m_status != ParserStatus::Done)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ULONG dataLen = m_dataList.size();
|
||||||
|
if (HidP_GetData(HidP_Input, m_dataList.data(), &dataLen,
|
||||||
|
m_descriptorData, PCHAR(data), len) != HIDP_STATUS_SUCCESS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
auto it = m_dataList.begin();
|
||||||
|
auto end = m_dataList.begin() + dataLen;
|
||||||
|
for (const HIDMainItem& item : m_itemPool)
|
||||||
|
{
|
||||||
|
if (item.IsConstant())
|
||||||
|
continue;
|
||||||
|
int32_t value = 0;
|
||||||
|
if (it != end)
|
||||||
|
{
|
||||||
|
const HIDP_DATA& data = *it;
|
||||||
|
if (data.DataIndex == idx)
|
||||||
|
{
|
||||||
|
value = data.RawValue;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!valueCB(item, value))
|
||||||
|
return;
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
class BitwiseIterator
|
class BitwiseIterator
|
||||||
{
|
{
|
||||||
@ -618,7 +766,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void HIDParser::ScanValues(std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB,
|
void HIDParser::ScanValues(const std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB,
|
||||||
const uint8_t* data, size_t len) const
|
const uint8_t* data, size_t len) const
|
||||||
{
|
{
|
||||||
if (m_status != ParserStatus::Done)
|
if (m_status != ParserStatus::Done)
|
||||||
@ -646,11 +794,14 @@ void HIDParser::ScanValues(std::function<bool(const HIDMainItem& item, int32_t v
|
|||||||
int32_t val = bitIt.GetUnsignedValue(item.m_reportSize, status);
|
int32_t val = bitIt.GetUnsignedValue(item.m_reportSize, status);
|
||||||
if (status == ParserStatus::Error)
|
if (status == ParserStatus::Error)
|
||||||
return;
|
return;
|
||||||
|
if (item.IsConstant())
|
||||||
|
continue;
|
||||||
if (!valueCB(item, val))
|
if (!valueCB(item, val))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
#include "boo/inputdev/DeviceBase.hpp"
|
#include "boo/inputdev/DeviceBase.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include <hidsdi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -15,7 +19,11 @@ class IHIDDevice : public std::enable_shared_from_this<IHIDDevice>
|
|||||||
virtual void _deviceDisconnected()=0;
|
virtual void _deviceDisconnected()=0;
|
||||||
virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0;
|
virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0;
|
||||||
virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)=0;
|
virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)=0;
|
||||||
|
#if _WIN32
|
||||||
|
virtual const PHIDP_PREPARSED_DATA _getReportDescriptor()=0;
|
||||||
|
#else
|
||||||
virtual std::vector<uint8_t> _getReportDescriptor()=0;
|
virtual std::vector<uint8_t> _getReportDescriptor()=0;
|
||||||
|
#endif
|
||||||
virtual bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
|
virtual bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
|
||||||
virtual size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
|
virtual size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
|
||||||
virtual void _startThread()=0;
|
virtual void _startThread()=0;
|
||||||
|
@ -316,11 +316,11 @@ public:
|
|||||||
/* Lookup boo window instance */
|
/* Lookup boo window instance */
|
||||||
auto search = m_allWindows.find(hwnd);
|
auto search = m_allWindows.find(hwnd);
|
||||||
if (search == m_allWindows.end())
|
if (search == m_allWindows.end())
|
||||||
return 0;
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);;
|
||||||
|
|
||||||
std::shared_ptr<IWindow> window = search->second.lock();
|
std::shared_ptr<IWindow> window = search->second.lock();
|
||||||
if (!window)
|
if (!window)
|
||||||
return 0;
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||||
|
|
||||||
switch (uMsg)
|
switch (uMsg)
|
||||||
{
|
{
|
||||||
@ -527,7 +527,7 @@ int ApplicationRun(IApplication::EPlatformType platform,
|
|||||||
};
|
};
|
||||||
wndClass.hIcon = LoadIconW(wndClass.hInstance, MAKEINTRESOURCEW(101));
|
wndClass.hIcon = LoadIconW(wndClass.hInstance, MAKEINTRESOURCEW(101));
|
||||||
wndClass.hCursor = WIN32_CURSORS.m_arrow;
|
wndClass.hCursor = WIN32_CURSORS.m_arrow;
|
||||||
RegisterClassW(&wndClass);
|
ATOM a = RegisterClassW(&wndClass);
|
||||||
|
|
||||||
APP = new ApplicationWin32(cb, uniqueName, friendlyName, pname, args, singleInstance);
|
APP = new ApplicationWin32(cb, uniqueName, friendlyName, pname, args, singleInstance);
|
||||||
return APP->run();
|
return APP->run();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user