mirror of
https://github.com/AxioDL/boo.git
synced 2025-12-08 21:17:50 +00:00
New code style refactor
This commit is contained in:
@@ -3,111 +3,93 @@
|
||||
#include "IHIDDevice.hpp"
|
||||
#include <cstdarg>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
DeviceBase::DeviceBase(uint64_t typeHash, DeviceToken* token)
|
||||
: m_typeHash(typeHash), m_token(token)
|
||||
{
|
||||
DeviceBase::DeviceBase(uint64_t typeHash, DeviceToken* token) : m_typeHash(typeHash), m_token(token) {}
|
||||
|
||||
void DeviceBase::_deviceDisconnected() {
|
||||
deviceDisconnected();
|
||||
m_token = nullptr;
|
||||
if (m_hidDev) {
|
||||
m_hidDev->_deviceDisconnected();
|
||||
m_hidDev.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceBase::_deviceDisconnected()
|
||||
{
|
||||
deviceDisconnected();
|
||||
m_token = nullptr;
|
||||
if (m_hidDev)
|
||||
{
|
||||
m_hidDev->_deviceDisconnected();
|
||||
m_hidDev.reset();
|
||||
}
|
||||
void DeviceBase::closeDevice() {
|
||||
if (m_token)
|
||||
m_token->_deviceClose();
|
||||
}
|
||||
|
||||
void DeviceBase::closeDevice()
|
||||
{
|
||||
if (m_token)
|
||||
m_token->_deviceClose();
|
||||
void DeviceBase::deviceError(const char* error, ...) {
|
||||
va_list vl;
|
||||
va_start(vl, error);
|
||||
vfprintf(stderr, error, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
void DeviceBase::deviceError(const char* error, ...)
|
||||
{
|
||||
va_list vl;
|
||||
va_start(vl, error);
|
||||
vfprintf(stderr, error, vl);
|
||||
va_end(vl);
|
||||
bool DeviceBase::sendUSBInterruptTransfer(const uint8_t* data, size_t length) {
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_sendUSBInterruptTransfer(data, length);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeviceBase::sendUSBInterruptTransfer(const uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_sendUSBInterruptTransfer(data, length);
|
||||
return false;
|
||||
size_t DeviceBase::receiveUSBInterruptTransfer(uint8_t* data, size_t length) {
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_receiveUSBInterruptTransfer(data, length);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t DeviceBase::receiveUSBInterruptTransfer(uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_receiveUSBInterruptTransfer(data, length);
|
||||
return false;
|
||||
unsigned DeviceBase::getVendorId() {
|
||||
if (m_token)
|
||||
return m_token->getVendorId();
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned DeviceBase::getVendorId()
|
||||
{
|
||||
if (m_token)
|
||||
return m_token->getVendorId();
|
||||
return -1;
|
||||
unsigned DeviceBase::getProductId() {
|
||||
if (m_token)
|
||||
return m_token->getProductId();
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned DeviceBase::getProductId()
|
||||
{
|
||||
if (m_token)
|
||||
return m_token->getProductId();
|
||||
return -1;
|
||||
std::string_view DeviceBase::getVendorName() {
|
||||
if (m_token)
|
||||
return m_token->getVendorName();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string_view DeviceBase::getVendorName()
|
||||
{
|
||||
if (m_token)
|
||||
return m_token->getVendorName();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string_view DeviceBase::getProductName()
|
||||
{
|
||||
if (m_token)
|
||||
return m_token->getProductName();
|
||||
return {};
|
||||
std::string_view DeviceBase::getProductName() {
|
||||
if (m_token)
|
||||
return m_token->getProductName();
|
||||
return {};
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
#if !WINDOWS_STORE
|
||||
const PHIDP_PREPARSED_DATA DeviceBase::getReportDescriptor()
|
||||
{
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_getReportDescriptor();
|
||||
return {};
|
||||
const PHIDP_PREPARSED_DATA DeviceBase::getReportDescriptor() {
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_getReportDescriptor();
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
std::vector<uint8_t> DeviceBase::getReportDescriptor()
|
||||
{
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_getReportDescriptor();
|
||||
return {};
|
||||
std::vector<uint8_t> DeviceBase::getReportDescriptor() {
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_getReportDescriptor();
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_sendHIDReport(data, length, tp, message);
|
||||
return false;
|
||||
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_sendHIDReport(data, length, tp, message);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t DeviceBase::receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_receiveHIDReport(data, length, tp, message);
|
||||
return 0;
|
||||
size_t DeviceBase::receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_receiveHIDReport(data, length, tp, message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -6,68 +6,59 @@
|
||||
#include <usbiodef.h>
|
||||
#endif
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
DeviceFinder* DeviceFinder::skDevFinder = nullptr;
|
||||
|
||||
#if _WIN32 && !WINDOWS_STORE
|
||||
/* Windows-specific WM_DEVICECHANGED handler */
|
||||
LRESULT DeviceFinder::winDevChangedHandler(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
PDEV_BROADCAST_HDR dbh = (PDEV_BROADCAST_HDR)lParam;
|
||||
PDEV_BROADCAST_DEVICEINTERFACE dbhi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
|
||||
DeviceFinder* finder = instance();
|
||||
if (!finder)
|
||||
return 0;
|
||||
|
||||
if (wParam == DBT_DEVICEARRIVAL)
|
||||
{
|
||||
if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
|
||||
{
|
||||
DeviceType type = DeviceType::None;
|
||||
if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
|
||||
type = DeviceType::USB;
|
||||
else if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_HID)
|
||||
type = DeviceType::HID;
|
||||
|
||||
if (type != DeviceType::None)
|
||||
{
|
||||
#ifdef UNICODE
|
||||
char devPath[1024];
|
||||
wcstombs(devPath, dbhi->dbcc_name, 1024);
|
||||
finder->m_listener->_extDevConnect(devPath);
|
||||
#else
|
||||
finder->m_listener->_extDevConnect(dbhi->dbcc_name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (wParam == DBT_DEVICEREMOVECOMPLETE)
|
||||
{
|
||||
if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
|
||||
{
|
||||
DeviceType type = DeviceType::None;
|
||||
if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
|
||||
type = DeviceType::USB;
|
||||
else if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_HID)
|
||||
type = DeviceType::HID;
|
||||
|
||||
if (type != DeviceType::None)
|
||||
{
|
||||
#ifdef UNICODE
|
||||
char devPath[1024];
|
||||
wcstombs(devPath, dbhi->dbcc_name, 1024);
|
||||
finder->m_listener->_extDevDisconnect(devPath);
|
||||
#else
|
||||
finder->m_listener->_extDevDisconnect(dbhi->dbcc_name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT DeviceFinder::winDevChangedHandler(WPARAM wParam, LPARAM lParam) {
|
||||
PDEV_BROADCAST_HDR dbh = (PDEV_BROADCAST_HDR)lParam;
|
||||
PDEV_BROADCAST_DEVICEINTERFACE dbhi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
|
||||
DeviceFinder* finder = instance();
|
||||
if (!finder)
|
||||
return 0;
|
||||
|
||||
if (wParam == DBT_DEVICEARRIVAL) {
|
||||
if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
|
||||
DeviceType type = DeviceType::None;
|
||||
if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
|
||||
type = DeviceType::USB;
|
||||
else if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_HID)
|
||||
type = DeviceType::HID;
|
||||
|
||||
if (type != DeviceType::None) {
|
||||
#ifdef UNICODE
|
||||
char devPath[1024];
|
||||
wcstombs(devPath, dbhi->dbcc_name, 1024);
|
||||
finder->m_listener->_extDevConnect(devPath);
|
||||
#else
|
||||
finder->m_listener->_extDevConnect(dbhi->dbcc_name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else if (wParam == DBT_DEVICEREMOVECOMPLETE) {
|
||||
if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
|
||||
DeviceType type = DeviceType::None;
|
||||
if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
|
||||
type = DeviceType::USB;
|
||||
else if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_HID)
|
||||
type = DeviceType::HID;
|
||||
|
||||
if (type != DeviceType::None) {
|
||||
#ifdef UNICODE
|
||||
char devPath[1024];
|
||||
wcstombs(devPath, dbhi->dbcc_name, 1024);
|
||||
finder->m_listener->_extDevDisconnect(devPath);
|
||||
#else
|
||||
finder->m_listener->_extDevDisconnect(dbhi->dbcc_name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -3,87 +3,75 @@
|
||||
#include "boo/inputdev/GenericPad.hpp"
|
||||
#include "IHIDDevice.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
extern const DeviceSignature BOO_DEVICE_SIGS[];
|
||||
|
||||
|
||||
bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSignatureSet& sigSet)
|
||||
{
|
||||
if (token.getDeviceType() == DeviceType::HID)
|
||||
{
|
||||
uint64_t genPadHash = dev_typeid(GenericPad);
|
||||
bool hasGenericPad = false;
|
||||
for (const DeviceSignature* sig : sigSet)
|
||||
{
|
||||
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId() &&
|
||||
sig->m_type != DeviceType::HID)
|
||||
return false;
|
||||
if (sig->m_typeHash == genPadHash)
|
||||
hasGenericPad = true;
|
||||
}
|
||||
return hasGenericPad;
|
||||
bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSignatureSet& sigSet) {
|
||||
if (token.getDeviceType() == DeviceType::HID) {
|
||||
uint64_t genPadHash = dev_typeid(GenericPad);
|
||||
bool hasGenericPad = false;
|
||||
for (const DeviceSignature* sig : sigSet) {
|
||||
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId() && sig->m_type != DeviceType::HID)
|
||||
return false;
|
||||
if (sig->m_typeHash == genPadHash)
|
||||
hasGenericPad = true;
|
||||
}
|
||||
for (const DeviceSignature* sig : sigSet)
|
||||
{
|
||||
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return hasGenericPad;
|
||||
}
|
||||
for (const DeviceSignature* sig : sigSet) {
|
||||
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp);
|
||||
std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token)
|
||||
{
|
||||
std::shared_ptr<DeviceBase> retval;
|
||||
std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token) {
|
||||
std::shared_ptr<DeviceBase> retval;
|
||||
|
||||
/* Perform signature-matching to find the appropriate device-factory */
|
||||
const DeviceSignature* foundSig = nullptr;
|
||||
const DeviceSignature* sigIter = BOO_DEVICE_SIGS;
|
||||
unsigned targetVid = token.getVendorId();
|
||||
unsigned targetPid = token.getProductId();
|
||||
while (sigIter->m_name)
|
||||
{
|
||||
if (sigIter->m_vid == targetVid && sigIter->m_pid == targetPid)
|
||||
{
|
||||
foundSig = sigIter;
|
||||
break;
|
||||
}
|
||||
++sigIter;
|
||||
/* Perform signature-matching to find the appropriate device-factory */
|
||||
const DeviceSignature* foundSig = nullptr;
|
||||
const DeviceSignature* sigIter = BOO_DEVICE_SIGS;
|
||||
unsigned targetVid = token.getVendorId();
|
||||
unsigned targetPid = token.getProductId();
|
||||
while (sigIter->m_name) {
|
||||
if (sigIter->m_vid == targetVid && sigIter->m_pid == targetPid) {
|
||||
foundSig = sigIter;
|
||||
break;
|
||||
}
|
||||
if (!foundSig)
|
||||
{
|
||||
/* Try Generic HID devices */
|
||||
if (token.getDeviceType() == DeviceType::HID)
|
||||
{
|
||||
retval = std::make_shared<GenericPad>(&token);
|
||||
if (!retval)
|
||||
return nullptr;
|
||||
|
||||
retval->m_hidDev = IHIDDeviceNew(token, retval);
|
||||
if (!retval->m_hidDev)
|
||||
return nullptr;
|
||||
retval->m_hidDev->_startThread();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
++sigIter;
|
||||
}
|
||||
if (!foundSig) {
|
||||
/* Try Generic HID devices */
|
||||
if (token.getDeviceType() == DeviceType::HID) {
|
||||
retval = std::make_shared<GenericPad>(&token);
|
||||
if (!retval)
|
||||
return nullptr;
|
||||
|
||||
retval->m_hidDev = IHIDDeviceNew(token, retval);
|
||||
if (!retval->m_hidDev)
|
||||
return nullptr;
|
||||
retval->m_hidDev->_startThread();
|
||||
|
||||
return retval;
|
||||
}
|
||||
if (foundSig->m_type != DeviceType::None && foundSig->m_type != token.getDeviceType())
|
||||
return nullptr;
|
||||
|
||||
retval = foundSig->m_factory(&token);
|
||||
if (!retval)
|
||||
return nullptr;
|
||||
|
||||
retval->m_hidDev = IHIDDeviceNew(token, retval);
|
||||
if (!retval->m_hidDev)
|
||||
return nullptr;
|
||||
retval->m_hidDev->_startThread();
|
||||
|
||||
return retval;
|
||||
return nullptr;
|
||||
}
|
||||
if (foundSig->m_type != DeviceType::None && foundSig->m_type != token.getDeviceType())
|
||||
return nullptr;
|
||||
|
||||
retval = foundSig->m_factory(&token);
|
||||
if (!retval)
|
||||
return nullptr;
|
||||
|
||||
retval->m_hidDev = IHIDDeviceNew(token, retval);
|
||||
if (!retval->m_hidDev)
|
||||
return nullptr;
|
||||
retval->m_hidDev->_startThread();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
/*
|
||||
* Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c
|
||||
*/
|
||||
@@ -14,134 +13,118 @@ DolphinSmashAdapter::DolphinSmashAdapter(DeviceToken* token)
|
||||
|
||||
DolphinSmashAdapter::~DolphinSmashAdapter() {}
|
||||
|
||||
static inline EDolphinControllerType parseType(unsigned char status)
|
||||
{
|
||||
EDolphinControllerType type =
|
||||
EDolphinControllerType(status) & (EDolphinControllerType::Normal | EDolphinControllerType::Wavebird);
|
||||
switch (type)
|
||||
{
|
||||
case EDolphinControllerType::Normal:
|
||||
case EDolphinControllerType::Wavebird:
|
||||
return type;
|
||||
default:
|
||||
return EDolphinControllerType::None;
|
||||
}
|
||||
}
|
||||
|
||||
static inline EDolphinControllerType parseState(DolphinControllerState* stateOut, uint8_t* payload, bool& rumble)
|
||||
{
|
||||
memset(stateOut, 0, sizeof(DolphinControllerState));
|
||||
unsigned char status = payload[0];
|
||||
EDolphinControllerType type = parseType(status);
|
||||
|
||||
rumble = ((status & 0x04) != 0) ? true : false;
|
||||
|
||||
stateOut->m_btns = (uint16_t)payload[1] << 8 | (uint16_t)payload[2];
|
||||
|
||||
stateOut->m_leftStick[0] = payload[3];
|
||||
stateOut->m_leftStick[1] = payload[4];
|
||||
stateOut->m_rightStick[0] = payload[5];
|
||||
stateOut->m_rightStick[1] = payload[6];
|
||||
stateOut->m_analogTriggers[0] = payload[7];
|
||||
stateOut->m_analogTriggers[1] = payload[8];
|
||||
|
||||
static inline EDolphinControllerType parseType(unsigned char status) {
|
||||
EDolphinControllerType type =
|
||||
EDolphinControllerType(status) & (EDolphinControllerType::Normal | EDolphinControllerType::Wavebird);
|
||||
switch (type) {
|
||||
case EDolphinControllerType::Normal:
|
||||
case EDolphinControllerType::Wavebird:
|
||||
return type;
|
||||
default:
|
||||
return EDolphinControllerType::None;
|
||||
}
|
||||
}
|
||||
|
||||
void DolphinSmashAdapter::initialCycle()
|
||||
{
|
||||
uint8_t handshakePayload[] = {0x13};
|
||||
sendUSBInterruptTransfer(handshakePayload, sizeof(handshakePayload));
|
||||
static inline EDolphinControllerType parseState(DolphinControllerState* stateOut, uint8_t* payload, bool& rumble) {
|
||||
memset(stateOut, 0, sizeof(DolphinControllerState));
|
||||
unsigned char status = payload[0];
|
||||
EDolphinControllerType type = parseType(status);
|
||||
|
||||
rumble = ((status & 0x04) != 0) ? true : false;
|
||||
|
||||
stateOut->m_btns = (uint16_t)payload[1] << 8 | (uint16_t)payload[2];
|
||||
|
||||
stateOut->m_leftStick[0] = payload[3];
|
||||
stateOut->m_leftStick[1] = payload[4];
|
||||
stateOut->m_rightStick[0] = payload[5];
|
||||
stateOut->m_rightStick[1] = payload[6];
|
||||
stateOut->m_analogTriggers[0] = payload[7];
|
||||
stateOut->m_analogTriggers[1] = payload[8];
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
void DolphinSmashAdapter::transferCycle()
|
||||
{
|
||||
uint8_t payload[37];
|
||||
size_t recvSz = receiveUSBInterruptTransfer(payload, sizeof(payload));
|
||||
if (recvSz != 37 || payload[0] != 0x21)
|
||||
return;
|
||||
void DolphinSmashAdapter::initialCycle() {
|
||||
uint8_t handshakePayload[] = {0x13};
|
||||
sendUSBInterruptTransfer(handshakePayload, sizeof(handshakePayload));
|
||||
}
|
||||
|
||||
// printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]);
|
||||
void DolphinSmashAdapter::transferCycle() {
|
||||
uint8_t payload[37];
|
||||
size_t recvSz = receiveUSBInterruptTransfer(payload, sizeof(payload));
|
||||
if (recvSz != 37 || payload[0] != 0x21)
|
||||
return;
|
||||
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (!m_callback)
|
||||
return;
|
||||
// printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]);
|
||||
|
||||
/* Parse controller states */
|
||||
uint8_t* controller = &payload[1];
|
||||
uint8_t rumbleMask = 0;
|
||||
for (uint32_t i = 0; i < 4; i++, controller += 9)
|
||||
{
|
||||
DolphinControllerState state;
|
||||
bool rumble = false;
|
||||
EDolphinControllerType type = parseState(&state, controller, rumble);
|
||||
if (type != EDolphinControllerType::None && !(m_knownControllers & 1 << i))
|
||||
{
|
||||
m_leftStickCal[0] = state.m_leftStick[0];
|
||||
m_leftStickCal[1] = state.m_leftStick[1];
|
||||
m_rightStickCal[0] = state.m_rightStick[0];
|
||||
m_rightStickCal[1] = state.m_rightStick[1];
|
||||
m_triggersCal[0] = state.m_analogTriggers[0];
|
||||
m_triggersCal[1] = state.m_analogTriggers[1];
|
||||
m_knownControllers |= 1 << i;
|
||||
m_callback->controllerConnected(i, type);
|
||||
}
|
||||
else if (type == EDolphinControllerType::None && (m_knownControllers & 1 << i))
|
||||
{
|
||||
m_knownControllers &= ~(1 << i);
|
||||
m_callback->controllerDisconnected(i);
|
||||
}
|
||||
if (m_knownControllers & 1 << i)
|
||||
{
|
||||
state.m_leftStick[0] = state.m_leftStick[0] - m_leftStickCal[0];
|
||||
state.m_leftStick[1] = state.m_leftStick[1] - m_leftStickCal[1];
|
||||
state.m_rightStick[0] = state.m_rightStick[0] - m_rightStickCal[0];
|
||||
state.m_rightStick[1] = state.m_rightStick[1] - m_rightStickCal[1];
|
||||
state.m_analogTriggers[0] = state.m_analogTriggers[0] - m_triggersCal[0];
|
||||
state.m_analogTriggers[1] = state.m_analogTriggers[1] - m_triggersCal[1];
|
||||
m_callback->controllerUpdate(i, type, state);
|
||||
}
|
||||
rumbleMask |= rumble ? 1 << i : 0;
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (!m_callback)
|
||||
return;
|
||||
|
||||
/* Parse controller states */
|
||||
uint8_t* controller = &payload[1];
|
||||
uint8_t rumbleMask = 0;
|
||||
for (uint32_t i = 0; i < 4; i++, controller += 9) {
|
||||
DolphinControllerState state;
|
||||
bool rumble = false;
|
||||
EDolphinControllerType type = parseState(&state, controller, rumble);
|
||||
if (type != EDolphinControllerType::None && !(m_knownControllers & 1 << i)) {
|
||||
m_leftStickCal[0] = state.m_leftStick[0];
|
||||
m_leftStickCal[1] = state.m_leftStick[1];
|
||||
m_rightStickCal[0] = state.m_rightStick[0];
|
||||
m_rightStickCal[1] = state.m_rightStick[1];
|
||||
m_triggersCal[0] = state.m_analogTriggers[0];
|
||||
m_triggersCal[1] = state.m_analogTriggers[1];
|
||||
m_knownControllers |= 1 << i;
|
||||
m_callback->controllerConnected(i, type);
|
||||
} else if (type == EDolphinControllerType::None && (m_knownControllers & 1 << i)) {
|
||||
m_knownControllers &= ~(1 << i);
|
||||
m_callback->controllerDisconnected(i);
|
||||
}
|
||||
if (m_knownControllers & 1 << i) {
|
||||
state.m_leftStick[0] = state.m_leftStick[0] - m_leftStickCal[0];
|
||||
state.m_leftStick[1] = state.m_leftStick[1] - m_leftStickCal[1];
|
||||
state.m_rightStick[0] = state.m_rightStick[0] - m_rightStickCal[0];
|
||||
state.m_rightStick[1] = state.m_rightStick[1] - m_rightStickCal[1];
|
||||
state.m_analogTriggers[0] = state.m_analogTriggers[0] - m_triggersCal[0];
|
||||
state.m_analogTriggers[1] = state.m_analogTriggers[1] - m_triggersCal[1];
|
||||
m_callback->controllerUpdate(i, type, state);
|
||||
}
|
||||
rumbleMask |= rumble ? 1 << i : 0;
|
||||
}
|
||||
|
||||
/* Send rumble message (if needed) */
|
||||
uint8_t rumbleReq = m_rumbleRequest & rumbleMask;
|
||||
if (rumbleReq != m_rumbleState) {
|
||||
uint8_t rumbleMessage[5] = {0x11};
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (rumbleReq & 1 << i)
|
||||
rumbleMessage[i + 1] = 1;
|
||||
else if (m_hardStop[i])
|
||||
rumbleMessage[i + 1] = 2;
|
||||
else
|
||||
rumbleMessage[i + 1] = 0;
|
||||
}
|
||||
|
||||
/* Send rumble message (if needed) */
|
||||
uint8_t rumbleReq = m_rumbleRequest & rumbleMask;
|
||||
if (rumbleReq != m_rumbleState)
|
||||
{
|
||||
uint8_t rumbleMessage[5] = {0x11};
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (rumbleReq & 1 << i)
|
||||
rumbleMessage[i + 1] = 1;
|
||||
else if (m_hardStop[i])
|
||||
rumbleMessage[i + 1] = 2;
|
||||
else
|
||||
rumbleMessage[i + 1] = 0;
|
||||
}
|
||||
|
||||
sendUSBInterruptTransfer(rumbleMessage, sizeof(rumbleMessage));
|
||||
m_rumbleState = rumbleReq;
|
||||
}
|
||||
}
|
||||
|
||||
void DolphinSmashAdapter::finalCycle()
|
||||
{
|
||||
uint8_t rumbleMessage[5] = {0x11, 0, 0, 0, 0};
|
||||
sendUSBInterruptTransfer(rumbleMessage, sizeof(rumbleMessage));
|
||||
m_rumbleState = rumbleReq;
|
||||
}
|
||||
}
|
||||
|
||||
void DolphinSmashAdapter::deviceDisconnected()
|
||||
{
|
||||
for (uint32_t i = 0; i < 4; i++)
|
||||
{
|
||||
if (m_knownControllers & 1 << i)
|
||||
{
|
||||
m_knownControllers &= ~(1 << i);
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerDisconnected(i);
|
||||
}
|
||||
void DolphinSmashAdapter::finalCycle() {
|
||||
uint8_t rumbleMessage[5] = {0x11, 0, 0, 0, 0};
|
||||
sendUSBInterruptTransfer(rumbleMessage, sizeof(rumbleMessage));
|
||||
}
|
||||
|
||||
void DolphinSmashAdapter::deviceDisconnected() {
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (m_knownControllers & 1 << i) {
|
||||
m_knownControllers &= ~(1 << i);
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerDisconnected(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The following code is derived from pad.c in libogc
|
||||
@@ -170,98 +153,79 @@ void DolphinSmashAdapter::deviceDisconnected()
|
||||
|
||||
static int16_t pad_clampregion[8] = {30, 180, 15, 72, 40, 15, 59, 31};
|
||||
|
||||
static void pad_clampstick(int16_t& px, int16_t& py, int16_t max, int16_t xy, int16_t min)
|
||||
{
|
||||
int x = px;
|
||||
int y = py;
|
||||
int signX;
|
||||
int signY;
|
||||
int d;
|
||||
static void pad_clampstick(int16_t& px, int16_t& py, int16_t max, int16_t xy, int16_t min) {
|
||||
int x = px;
|
||||
int y = py;
|
||||
int signX;
|
||||
int signY;
|
||||
int d;
|
||||
|
||||
if (x > 0)
|
||||
{
|
||||
signX = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
signX = -1;
|
||||
x = -x;
|
||||
}
|
||||
if (x > 0) {
|
||||
signX = 1;
|
||||
} else {
|
||||
signX = -1;
|
||||
x = -x;
|
||||
}
|
||||
|
||||
if (y > 0)
|
||||
{
|
||||
signY = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
signY = -1;
|
||||
y = -y;
|
||||
}
|
||||
if (y > 0) {
|
||||
signY = 1;
|
||||
} else {
|
||||
signY = -1;
|
||||
y = -y;
|
||||
}
|
||||
|
||||
if (x <= min)
|
||||
x = 0;
|
||||
else
|
||||
x -= min;
|
||||
if (x <= min)
|
||||
x = 0;
|
||||
else
|
||||
x -= min;
|
||||
|
||||
if (y <= min)
|
||||
{
|
||||
y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
y -= min;
|
||||
}
|
||||
if (y <= min) {
|
||||
y = 0;
|
||||
} else {
|
||||
y -= min;
|
||||
}
|
||||
|
||||
if (x == 0 && y == 0)
|
||||
{
|
||||
px = py = 0;
|
||||
return;
|
||||
}
|
||||
if (x == 0 && y == 0) {
|
||||
px = py = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (xy * y <= xy * x)
|
||||
{
|
||||
d = xy * x + (max - xy) * y;
|
||||
if (xy * max < d)
|
||||
{
|
||||
x = int16_t(xy * max * x / d);
|
||||
y = int16_t(xy * max * y / d);
|
||||
}
|
||||
if (xy * y <= xy * x) {
|
||||
d = xy * x + (max - xy) * y;
|
||||
if (xy * max < d) {
|
||||
x = int16_t(xy * max * x / d);
|
||||
y = int16_t(xy * max * y / d);
|
||||
}
|
||||
else
|
||||
{
|
||||
d = xy * y + (max - xy) * x;
|
||||
if (xy * max < d)
|
||||
{
|
||||
x = int16_t(xy * max * x / d);
|
||||
y = int16_t(xy * max * y / d);
|
||||
}
|
||||
} else {
|
||||
d = xy * y + (max - xy) * x;
|
||||
if (xy * max < d) {
|
||||
x = int16_t(xy * max * x / d);
|
||||
y = int16_t(xy * max * y / d);
|
||||
}
|
||||
}
|
||||
|
||||
px = int16_t(signX * x);
|
||||
py = int16_t(signY * y);
|
||||
px = int16_t(signX * x);
|
||||
py = int16_t(signY * y);
|
||||
}
|
||||
|
||||
static void pad_clamptrigger(int16_t& trigger)
|
||||
{
|
||||
int16_t min, max;
|
||||
static void pad_clamptrigger(int16_t& trigger) {
|
||||
int16_t min, max;
|
||||
|
||||
min = pad_clampregion[0];
|
||||
max = pad_clampregion[1];
|
||||
if (min > trigger)
|
||||
trigger = 0;
|
||||
else
|
||||
{
|
||||
if (max < trigger)
|
||||
trigger = max;
|
||||
trigger -= min;
|
||||
}
|
||||
min = pad_clampregion[0];
|
||||
max = pad_clampregion[1];
|
||||
if (min > trigger)
|
||||
trigger = 0;
|
||||
else {
|
||||
if (max < trigger)
|
||||
trigger = max;
|
||||
trigger -= min;
|
||||
}
|
||||
}
|
||||
|
||||
void DolphinControllerState::clamp()
|
||||
{
|
||||
pad_clampstick(m_leftStick[0], m_leftStick[1], pad_clampregion[3], pad_clampregion[4], pad_clampregion[2]);
|
||||
pad_clampstick(m_rightStick[0], m_rightStick[1], pad_clampregion[6], pad_clampregion[7], pad_clampregion[5]);
|
||||
pad_clamptrigger(m_analogTriggers[0]);
|
||||
pad_clamptrigger(m_analogTriggers[1]);
|
||||
}
|
||||
void DolphinControllerState::clamp() {
|
||||
pad_clampstick(m_leftStick[0], m_leftStick[1], pad_clampregion[3], pad_clampregion[4], pad_clampregion[2]);
|
||||
pad_clampstick(m_rightStick[0], m_rightStick[1], pad_clampregion[6], pad_clampregion[7], pad_clampregion[5]);
|
||||
pad_clamptrigger(m_analogTriggers[0]);
|
||||
pad_clamptrigger(m_analogTriggers[1]);
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -7,54 +7,40 @@
|
||||
#include <memory.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
static inline uint16_t bswap16(uint16_t val) {return _byteswap_ushort(val);}
|
||||
static inline uint16_t bswap16(uint16_t val) { return _byteswap_ushort(val); }
|
||||
#elif __GNUC__ && !defined(__FreeBSD__)
|
||||
static inline uint16_t bswap16(uint16_t val) {return __builtin_bswap16(val); }
|
||||
static inline uint16_t bswap16(uint16_t val) { return __builtin_bswap16(val); }
|
||||
#elif !defined(__FreeBSD__)
|
||||
static inline uint16_t bswap16(uint16_t val) {return __builtin_byteswap(val);}
|
||||
static inline uint16_t bswap16(uint16_t val) { return __builtin_byteswap(val); }
|
||||
#endif
|
||||
|
||||
#ifndef M_PIF
|
||||
#define M_PIF 3.14159265358979323846f /* pi */
|
||||
#endif
|
||||
|
||||
#define RAD_TO_DEG (180.f/M_PIF)
|
||||
#define RAD_TO_DEG (180.f / M_PIF)
|
||||
|
||||
namespace boo
|
||||
{
|
||||
static const uint8_t defaultReport[49] = {
|
||||
0x01,
|
||||
0x01, 0xff, 0x00, 0xff, 0x00,
|
||||
0xff, 0x80, 0x00, 0x00, 0x00,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
namespace boo {
|
||||
static const uint8_t defaultReport[49] = {0x01, 0x01, 0xff, 0x00, 0xff, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0xff,
|
||||
0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10,
|
||||
0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
DualshockPad::DualshockPad(DeviceToken* token)
|
||||
: TDeviceBase<IDualshockPadCallback>(dev_typeid(DualshockPad), token),
|
||||
m_rumbleRequest(EDualshockMotor::None),
|
||||
m_rumbleState(EDualshockMotor::None)
|
||||
{
|
||||
memcpy(m_report.buf, defaultReport, 49);
|
||||
: TDeviceBase<IDualshockPadCallback>(dev_typeid(DualshockPad), token)
|
||||
, m_rumbleRequest(EDualshockMotor::None)
|
||||
, m_rumbleState(EDualshockMotor::None) {
|
||||
memcpy(m_report.buf, defaultReport, 49);
|
||||
}
|
||||
|
||||
DualshockPad::~DualshockPad()
|
||||
{
|
||||
DualshockPad::~DualshockPad() {}
|
||||
|
||||
void DualshockPad::deviceDisconnected() {
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerDisconnected();
|
||||
}
|
||||
|
||||
void DualshockPad::deviceDisconnected()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerDisconnected();
|
||||
}
|
||||
|
||||
void DualshockPad::initialCycle()
|
||||
{
|
||||
void DualshockPad::initialCycle() {
|
||||
#if 0
|
||||
uint8_t setupCommand[5] = {0xF4, 0x42, 0x0c, 0x00, 0x00}; //Tells controller to start sending changes on in pipe
|
||||
if (!sendHIDReport(setupCommand, 5, HIDReportType::Feature, 0xF4))
|
||||
@@ -69,77 +55,64 @@ void DualshockPad::initialCycle()
|
||||
#endif
|
||||
}
|
||||
|
||||
void DualshockPad::transferCycle()
|
||||
{
|
||||
void DualshockPad::transferCycle() {}
|
||||
|
||||
void DualshockPad::finalCycle() {
|
||||
m_report.rumble.leftDuration = 0;
|
||||
m_report.rumble.leftForce = 0;
|
||||
m_report.rumble.rightDuration = 0;
|
||||
m_report.rumble.rightOn = false;
|
||||
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
|
||||
}
|
||||
|
||||
void DualshockPad::finalCycle()
|
||||
{
|
||||
m_report.rumble.leftDuration = 0;
|
||||
m_report.rumble.leftForce = 0;
|
||||
m_report.rumble.rightDuration = 0;
|
||||
m_report.rumble.rightOn = false;
|
||||
void DualshockPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
|
||||
if (message != 1 || length != 49 || tp != HIDReportType::Input)
|
||||
return;
|
||||
DualshockPadState state = *reinterpret_cast<const DualshockPadState*>(data);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
state.m_accelerometer[i] = bswap16(state.m_accelerometer[i]);
|
||||
|
||||
state.m_gyrometerZ = bswap16(state.m_gyrometerZ);
|
||||
const double zeroG = 511.5; // 1.65/3.3*1023 (1,65V);
|
||||
float accXval = -((double)state.m_accelerometer[0] - zeroG);
|
||||
float accYval = -((double)state.m_accelerometer[1] - zeroG);
|
||||
float accZval = -((double)state.m_accelerometer[2] - zeroG);
|
||||
state.accPitch = (atan2(accYval, accZval) + M_PIF) * RAD_TO_DEG;
|
||||
state.accYaw = (atan2(accXval, accZval) + M_PIF) * RAD_TO_DEG;
|
||||
state.gyroZ = (state.m_gyrometerZ / 1023.f);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerUpdate(*this, state);
|
||||
}
|
||||
|
||||
if (m_rumbleRequest != m_rumbleState) {
|
||||
if ((m_rumbleRequest & EDualshockMotor::Left) != EDualshockMotor::None) {
|
||||
m_report.rumble.leftDuration = m_rumbleDuration[0];
|
||||
m_report.rumble.leftForce = m_rumbleIntensity[0];
|
||||
} else {
|
||||
m_report.rumble.leftDuration = 0;
|
||||
m_report.rumble.leftForce = 0;
|
||||
}
|
||||
|
||||
if ((m_rumbleRequest & EDualshockMotor::Right) != EDualshockMotor::None) {
|
||||
m_report.rumble.rightDuration = m_rumbleDuration[1];
|
||||
m_report.rumble.rightOn = m_rumbleIntensity[1] > 0;
|
||||
} else {
|
||||
m_report.rumble.rightDuration = 0;
|
||||
m_report.rumble.rightOn = false;
|
||||
}
|
||||
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
|
||||
m_rumbleState = m_rumbleRequest;
|
||||
} else {
|
||||
if (state.m_reserved5[8] & 0x80)
|
||||
m_rumbleRequest &= ~EDualshockMotor::Right;
|
||||
if (state.m_reserved5[7] & 0x01)
|
||||
m_rumbleRequest &= ~EDualshockMotor::Left;
|
||||
m_rumbleState = m_rumbleRequest;
|
||||
}
|
||||
}
|
||||
|
||||
void DualshockPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
if (message != 1 || length != 49 || tp != HIDReportType::Input)
|
||||
return;
|
||||
DualshockPadState state = *reinterpret_cast<const DualshockPadState*>(data);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
state.m_accelerometer[i] = bswap16(state.m_accelerometer[i]);
|
||||
|
||||
state.m_gyrometerZ = bswap16(state.m_gyrometerZ);
|
||||
const double zeroG = 511.5; // 1.65/3.3*1023 (1,65V);
|
||||
float accXval = -((double)state.m_accelerometer[0] - zeroG);
|
||||
float accYval = -((double)state.m_accelerometer[1] - zeroG);
|
||||
float accZval = -((double)state.m_accelerometer[2] - zeroG);
|
||||
state.accPitch = (atan2(accYval, accZval) + M_PIF) * RAD_TO_DEG;
|
||||
state.accYaw = (atan2(accXval, accZval) + M_PIF) * RAD_TO_DEG;
|
||||
state.gyroZ = (state.m_gyrometerZ / 1023.f);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerUpdate(*this, state);
|
||||
}
|
||||
|
||||
if (m_rumbleRequest != m_rumbleState)
|
||||
{
|
||||
if ((m_rumbleRequest & EDualshockMotor::Left) != EDualshockMotor::None)
|
||||
{
|
||||
m_report.rumble.leftDuration = m_rumbleDuration[0];
|
||||
m_report.rumble.leftForce = m_rumbleIntensity[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_report.rumble.leftDuration = 0;
|
||||
m_report.rumble.leftForce = 0;
|
||||
}
|
||||
|
||||
if ((m_rumbleRequest & EDualshockMotor::Right) != EDualshockMotor::None)
|
||||
{
|
||||
m_report.rumble.rightDuration = m_rumbleDuration[1];
|
||||
m_report.rumble.rightOn = m_rumbleIntensity[1] > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_report.rumble.rightDuration = 0;
|
||||
m_report.rumble.rightOn = false;
|
||||
}
|
||||
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
|
||||
m_rumbleState = m_rumbleRequest;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state.m_reserved5[8] & 0x80)
|
||||
m_rumbleRequest &= ~EDualshockMotor::Right;
|
||||
if (state.m_reserved5[7] & 0x01)
|
||||
m_rumbleRequest &= ~EDualshockMotor::Left;
|
||||
m_rumbleState = m_rumbleRequest;
|
||||
}
|
||||
}
|
||||
|
||||
} // boo
|
||||
} // namespace boo
|
||||
|
||||
@@ -1,57 +1,46 @@
|
||||
#include "boo/inputdev/GenericPad.hpp"
|
||||
#include "boo/inputdev/DeviceToken.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
GenericPad::GenericPad(DeviceToken* token)
|
||||
: TDeviceBase<IGenericPadCallback>(dev_typeid(GenericPad), token)
|
||||
{
|
||||
|
||||
}
|
||||
GenericPad::GenericPad(DeviceToken* token) : TDeviceBase<IGenericPadCallback>(dev_typeid(GenericPad), token) {}
|
||||
|
||||
GenericPad::~GenericPad() {}
|
||||
|
||||
void GenericPad::deviceDisconnected()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerDisconnected();
|
||||
void GenericPad::deviceDisconnected() {
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerDisconnected();
|
||||
}
|
||||
|
||||
void GenericPad::initialCycle()
|
||||
{
|
||||
void GenericPad::initialCycle() {
|
||||
#if _WIN32
|
||||
#if !WINDOWS_STORE
|
||||
const PHIDP_PREPARSED_DATA reportDesc = getReportDescriptor();
|
||||
m_parser.Parse(reportDesc);
|
||||
const PHIDP_PREPARSED_DATA reportDesc = getReportDescriptor();
|
||||
m_parser.Parse(reportDesc);
|
||||
#endif
|
||||
#else
|
||||
std::vector<uint8_t> reportDesc = getReportDescriptor();
|
||||
m_parser.Parse(reportDesc.data(), reportDesc.size());
|
||||
std::vector<uint8_t> reportDesc = getReportDescriptor();
|
||||
m_parser.Parse(reportDesc.data(), reportDesc.size());
|
||||
#endif
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerConnected();
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerConnected();
|
||||
}
|
||||
|
||||
void GenericPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (length == 0 || tp != HIDReportType::Input || !m_callback)
|
||||
return;
|
||||
std::function<bool(const HIDMainItem&, int32_t)> func =
|
||||
[this](const HIDMainItem& item, int32_t value)
|
||||
{
|
||||
m_callback->valueUpdate(item, value);
|
||||
return true;
|
||||
};
|
||||
m_parser.ScanValues(func, data, length);
|
||||
void GenericPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (length == 0 || tp != HIDReportType::Input || !m_callback)
|
||||
return;
|
||||
std::function<bool(const HIDMainItem&, int32_t)> func = [this](const HIDMainItem& item, int32_t value) {
|
||||
m_callback->valueUpdate(item, value);
|
||||
return true;
|
||||
};
|
||||
m_parser.ScanValues(func, data, length);
|
||||
}
|
||||
|
||||
void GenericPad::enumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const
|
||||
{
|
||||
m_parser.EnumerateValues(valueCB);
|
||||
void GenericPad::enumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const {
|
||||
m_parser.EnumerateValues(valueCB);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -1,31 +1,21 @@
|
||||
#include "boo/inputdev/IHIDListener.hpp"
|
||||
#include "boo/inputdev/DeviceFinder.hpp"
|
||||
|
||||
namespace boo {
|
||||
|
||||
namespace boo
|
||||
{
|
||||
class HIDListenerBSD final : public IHIDListener {
|
||||
DeviceFinder& m_finder;
|
||||
|
||||
class HIDListenerBSD final : public IHIDListener
|
||||
{
|
||||
DeviceFinder& m_finder;
|
||||
public:
|
||||
HIDListenerBSD(DeviceFinder& finder)
|
||||
: m_finder(finder)
|
||||
{
|
||||
}
|
||||
HIDListenerBSD(DeviceFinder& finder) : m_finder(finder) {}
|
||||
|
||||
~HIDListenerBSD()
|
||||
{
|
||||
}
|
||||
~HIDListenerBSD() {}
|
||||
|
||||
bool startScanning() { return false; }
|
||||
bool stopScanning() { return false; }
|
||||
bool startScanning() { return false; }
|
||||
bool stopScanning() { return false; }
|
||||
|
||||
bool scanNow() { return false; }
|
||||
bool scanNow() { return false; }
|
||||
};
|
||||
|
||||
IHIDListener* IHIDListenerNew(DeviceFinder &finder)
|
||||
{
|
||||
return new HIDListenerBSD(finder);
|
||||
}
|
||||
}
|
||||
IHIDListener* IHIDListenerNew(DeviceFinder& finder) { return new HIDListenerBSD(finder); }
|
||||
} // namespace boo
|
||||
|
||||
@@ -5,380 +5,320 @@
|
||||
#include "IOKitPointer.hpp"
|
||||
#include <thread>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
class HIDDeviceIOKit : public IHIDDevice
|
||||
{
|
||||
DeviceToken& m_token;
|
||||
std::shared_ptr<DeviceBase> m_devImp;
|
||||
class HIDDeviceIOKit : public IHIDDevice {
|
||||
DeviceToken& m_token;
|
||||
std::shared_ptr<DeviceBase> m_devImp;
|
||||
|
||||
IUnknownPointer<IOUSBInterfaceInterface> m_usbIntf;
|
||||
uint8_t m_usbIntfInPipe = 0;
|
||||
uint8_t m_usbIntfOutPipe = 0;
|
||||
CFPointer<IOHIDDeviceRef> m_hidIntf;
|
||||
bool m_runningTransferLoop = false;
|
||||
bool m_isBt = false;
|
||||
IUnknownPointer<IOUSBInterfaceInterface> m_usbIntf;
|
||||
uint8_t m_usbIntfInPipe = 0;
|
||||
uint8_t m_usbIntfOutPipe = 0;
|
||||
CFPointer<IOHIDDeviceRef> m_hidIntf;
|
||||
bool m_runningTransferLoop = false;
|
||||
bool m_isBt = false;
|
||||
|
||||
std::string_view m_devPath;
|
||||
std::mutex m_initMutex;
|
||||
std::condition_variable m_initCond;
|
||||
std::thread m_thread;
|
||||
std::string_view m_devPath;
|
||||
std::mutex m_initMutex;
|
||||
std::condition_variable m_initCond;
|
||||
std::thread m_thread;
|
||||
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_usbIntf)
|
||||
{
|
||||
IOReturn res = m_usbIntf->WritePipe(m_usbIntf.storage(), m_usbIntfOutPipe, (void*)data, length);
|
||||
return res == kIOReturnSuccess;
|
||||
}
|
||||
return false;
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) {
|
||||
if (m_usbIntf) {
|
||||
IOReturn res = m_usbIntf->WritePipe(m_usbIntf.storage(), m_usbIntfOutPipe, (void*)data, length);
|
||||
return res == kIOReturnSuccess;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_usbIntf)
|
||||
{
|
||||
UInt32 readSize = length;
|
||||
IOReturn res = m_usbIntf->ReadPipe(m_usbIntf.storage(), m_usbIntfInPipe, data, &readSize);
|
||||
if (res != kIOReturnSuccess)
|
||||
return 0;
|
||||
return readSize;
|
||||
}
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) {
|
||||
if (m_usbIntf) {
|
||||
UInt32 readSize = length;
|
||||
IOReturn res = m_usbIntf->ReadPipe(m_usbIntf.storage(), m_usbIntfInPipe, data, &readSize);
|
||||
if (res != kIOReturnSuccess)
|
||||
return 0;
|
||||
return readSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> _getReportDescriptor()
|
||||
{
|
||||
if (m_hidIntf)
|
||||
{
|
||||
if (CFTypeRef desc = IOHIDDeviceGetProperty(m_hidIntf.get(), CFSTR(kIOHIDReportDescriptorKey)))
|
||||
{
|
||||
CFIndex len = CFDataGetLength(CFDataRef(desc));
|
||||
std::vector<uint8_t> ret(len, '\0');
|
||||
CFDataGetBytes(CFDataRef(desc), CFRangeMake(0, len), &ret[0]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
std::vector<uint8_t> _getReportDescriptor() {
|
||||
if (m_hidIntf) {
|
||||
if (CFTypeRef desc = IOHIDDeviceGetProperty(m_hidIntf.get(), CFSTR(kIOHIDReportDescriptorKey))) {
|
||||
CFIndex len = CFDataGetLength(CFDataRef(desc));
|
||||
std::vector<uint8_t> ret(len, '\0');
|
||||
CFDataGetBytes(CFDataRef(desc), CFRangeMake(0, len), &ret[0]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
/* HACK: A bug in IOBluetoothGamepadHIDDriver prevents raw output report transmission
|
||||
* USB driver appears to work correctly */
|
||||
if (m_hidIntf && !m_isBt)
|
||||
{
|
||||
IOReturn res = IOHIDDeviceSetReport(m_hidIntf.get(), IOHIDReportType(tp), message, data, length);
|
||||
return res == kIOReturnSuccess;
|
||||
}
|
||||
return false;
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
|
||||
/* HACK: A bug in IOBluetoothGamepadHIDDriver prevents raw output report transmission
|
||||
* USB driver appears to work correctly */
|
||||
if (m_hidIntf && !m_isBt) {
|
||||
IOReturn res = IOHIDDeviceSetReport(m_hidIntf.get(), IOHIDReportType(tp), message, data, length);
|
||||
return res == kIOReturnSuccess;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
if (m_hidIntf)
|
||||
{
|
||||
CFIndex readSize = length;
|
||||
IOReturn res = IOHIDDeviceGetReport(m_hidIntf.get(), IOHIDReportType(tp), message, data, &readSize);
|
||||
if (res != kIOReturnSuccess)
|
||||
return 0;
|
||||
return readSize;
|
||||
}
|
||||
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
|
||||
if (m_hidIntf) {
|
||||
CFIndex readSize = length;
|
||||
IOReturn res = IOHIDDeviceGetReport(m_hidIntf.get(), IOHIDReportType(tp), message, data, &readSize);
|
||||
if (res != kIOReturnSuccess)
|
||||
return 0;
|
||||
return readSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceIOKit> device) {
|
||||
char thrName[128];
|
||||
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().data());
|
||||
pthread_setname_np(thrName);
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* Get the HID element's parent (USB interrupt transfer-interface) */
|
||||
IOObjectPointer<io_iterator_t> devIter;
|
||||
IOObjectPointer<io_registry_entry_t> devEntry =
|
||||
IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.data());
|
||||
IOObjectPointer<io_object_t> interfaceEntry;
|
||||
IORegistryEntryGetChildIterator(devEntry.get(), kIOServicePlane, &devIter);
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(devIter.get())) {
|
||||
if (IOObjectConformsTo(obj.get(), kIOUSBInterfaceClassName)) {
|
||||
interfaceEntry = obj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!interfaceEntry) {
|
||||
snprintf(errStr, 256, "Unable to find interface for %s@%s\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceIOKit> device)
|
||||
{
|
||||
char thrName[128];
|
||||
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().data());
|
||||
pthread_setname_np(thrName);
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* Get the HID element's parent (USB interrupt transfer-interface) */
|
||||
IOObjectPointer<io_iterator_t> devIter;
|
||||
IOObjectPointer<io_registry_entry_t> devEntry = IORegistryEntryFromPath(kIOMasterPortDefault,
|
||||
device->m_devPath.data());
|
||||
IOObjectPointer<io_object_t> interfaceEntry;
|
||||
IORegistryEntryGetChildIterator(devEntry.get(), kIOServicePlane, &devIter);
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(devIter.get()))
|
||||
{
|
||||
if (IOObjectConformsTo(obj.get(), kIOUSBInterfaceClassName))
|
||||
{
|
||||
interfaceEntry = obj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!interfaceEntry)
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to find interface for %s@%s\n",
|
||||
device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
/* IOKit Plugin COM interface (WTF Apple???) */
|
||||
IOCFPluginPointer iodev;
|
||||
SInt32 score;
|
||||
IOReturn err;
|
||||
err = IOCreatePlugInInterfaceForService(interfaceEntry.get(),
|
||||
kIOUSBInterfaceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID,
|
||||
&iodev,
|
||||
&score);
|
||||
if (err)
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||
device->m_token.getProductName().data(), device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
/* USB interface function-pointer table */
|
||||
IUnknownPointer<IOUSBInterfaceInterface> intf;
|
||||
err = iodev.As(&intf, kIOUSBInterfaceInterfaceID);
|
||||
if (err)
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||
device->m_token.getProductName().data(), device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Obtain exclusive lock on device */
|
||||
device->m_usbIntf = intf;
|
||||
err = intf->USBInterfaceOpen(intf.storage());
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
if (err == kIOReturnExclusiveAccess)
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n",
|
||||
device->m_token.getProductName().data(), device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||
device->m_token.getProductName().data(), device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
}
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine pipe indices for interrupt I/O */
|
||||
UInt8 numEndpoints = 0;
|
||||
err = intf->GetNumEndpoints(intf.storage(), &numEndpoints);
|
||||
for (int i=1 ; i<numEndpoints+1 ; ++i)
|
||||
{
|
||||
UInt8 dir, num, tType, interval;
|
||||
UInt16 mPacketSz;
|
||||
err = intf->GetPipeProperties(intf.storage(), i, &dir, &num, &tType, &mPacketSz, &interval);
|
||||
if (tType == kUSBInterrupt)
|
||||
{
|
||||
if (dir == kUSBIn)
|
||||
device->m_usbIntfInPipe = num;
|
||||
else if (dir == kUSBOut)
|
||||
device->m_usbIntfOutPipe = num;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
intf->USBInterfaceClose(intf.storage());
|
||||
device->m_usbIntf = nullptr;
|
||||
/* IOKit Plugin COM interface (WTF Apple???) */
|
||||
IOCFPluginPointer iodev;
|
||||
SInt32 score;
|
||||
IOReturn err;
|
||||
err = IOCreatePlugInInterfaceForService(interfaceEntry.get(), kIOUSBInterfaceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID, &iodev, &score);
|
||||
if (err) {
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
static void _threadProcBTLL(std::shared_ptr<HIDDeviceIOKit> device)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
/* USB interface function-pointer table */
|
||||
IUnknownPointer<IOUSBInterfaceInterface> intf;
|
||||
err = iodev.As(&intf, kIOUSBInterfaceInterfaceID);
|
||||
if (err) {
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
static void _hidRemoveCb(void * _Nullable context,
|
||||
IOReturn result,
|
||||
void * _Nullable sender)
|
||||
{
|
||||
reinterpret_cast<HIDDeviceIOKit*>(context)->m_runningTransferLoop = false;
|
||||
/* Obtain exclusive lock on device */
|
||||
device->m_usbIntf = intf;
|
||||
err = intf->USBInterfaceOpen(intf.storage());
|
||||
if (err != kIOReturnSuccess) {
|
||||
if (err == kIOReturnExclusiveAccess) {
|
||||
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
} else {
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
}
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
static void _hidReportCb(void * _Nullable context,
|
||||
IOReturn,
|
||||
void * _Nullable,
|
||||
IOHIDReportType type,
|
||||
uint32_t reportID,
|
||||
uint8_t * report,
|
||||
CFIndex reportLength)
|
||||
{
|
||||
reinterpret_cast<DeviceBase*>(context)->receivedHIDReport(report, reportLength, HIDReportType(type), reportID);
|
||||
/* Determine pipe indices for interrupt I/O */
|
||||
UInt8 numEndpoints = 0;
|
||||
err = intf->GetNumEndpoints(intf.storage(), &numEndpoints);
|
||||
for (int i = 1; i < numEndpoints + 1; ++i) {
|
||||
UInt8 dir, num, tType, interval;
|
||||
UInt16 mPacketSz;
|
||||
err = intf->GetPipeProperties(intf.storage(), i, &dir, &num, &tType, &mPacketSz, &interval);
|
||||
if (tType == kUSBInterrupt) {
|
||||
if (dir == kUSBIn)
|
||||
device->m_usbIntfInPipe = num;
|
||||
else if (dir == kUSBOut)
|
||||
device->m_usbIntfOutPipe = num;
|
||||
}
|
||||
}
|
||||
|
||||
static void _threadProcHID(std::shared_ptr<HIDDeviceIOKit> device)
|
||||
{
|
||||
char thrName[128];
|
||||
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().data());
|
||||
pthread_setname_np(thrName);
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Get the HID element's object (HID device interface) */
|
||||
IOObjectPointer<io_service_t> interfaceEntry =
|
||||
IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.data());
|
||||
if (!IOObjectConformsTo(interfaceEntry.get(), "IOHIDDevice"))
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to find interface for %s@%s\n",
|
||||
device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
device->m_hidIntf = IOHIDDeviceCreate(nullptr, interfaceEntry.get());
|
||||
if (!device->m_hidIntf)
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||
device->m_token.getProductName().data(), device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
/* Cleanup */
|
||||
intf->USBInterfaceClose(intf.storage());
|
||||
device->m_usbIntf = nullptr;
|
||||
}
|
||||
|
||||
/* Open device */
|
||||
IOReturn err = IOHIDDeviceOpen(device->m_hidIntf.get(), kIOHIDOptionsTypeNone);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
if (err == kIOReturnExclusiveAccess)
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n",
|
||||
device->m_token.getProductName().data(), device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||
device->m_token.getProductName().data(), device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
}
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
static void _threadProcBTLL(std::shared_ptr<HIDDeviceIOKit> device) {
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* Register removal callback */
|
||||
IOHIDDeviceRegisterRemovalCallback(device->m_hidIntf.get(), _hidRemoveCb, device.get());
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Make note if device uses bluetooth driver */
|
||||
if (CFTypeRef transport = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDTransportKey)))
|
||||
device->m_isBt = CFStringCompare(CFStringRef(transport), CFSTR(kIOHIDTransportBluetoothValue), 0) == kCFCompareEqualTo;
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
}
|
||||
|
||||
/* Register input buffer */
|
||||
std::unique_ptr<uint8_t[]> buffer;
|
||||
int bufSize = 0;
|
||||
if (CFTypeRef maxSize = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDMaxInputReportSizeKey)))
|
||||
CFNumberGetValue(CFNumberRef(maxSize), kCFNumberIntType, &bufSize);
|
||||
if (bufSize)
|
||||
{
|
||||
buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufSize]);
|
||||
IOHIDDeviceRegisterInputReportCallback(device->m_hidIntf.get(), buffer.get(), bufSize,
|
||||
_hidReportCb, device->m_devImp.get());
|
||||
IOHIDDeviceScheduleWithRunLoop(device->m_hidIntf.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
}
|
||||
static void _hidRemoveCb(void* _Nullable context, IOReturn result, void* _Nullable sender) {
|
||||
reinterpret_cast<HIDDeviceIOKit*>(context)->m_runningTransferLoop = false;
|
||||
}
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
static void _hidReportCb(void* _Nullable context, IOReturn, void* _Nullable, IOHIDReportType type, uint32_t reportID,
|
||||
uint8_t* report, CFIndex reportLength) {
|
||||
reinterpret_cast<DeviceBase*>(context)->receivedHIDReport(report, reportLength, HIDReportType(type), reportID);
|
||||
}
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
{
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.010, true);
|
||||
if (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
}
|
||||
device->m_devImp->finalCycle();
|
||||
static void _threadProcHID(std::shared_ptr<HIDDeviceIOKit> device) {
|
||||
char thrName[128];
|
||||
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().data());
|
||||
pthread_setname_np(thrName);
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* Cleanup */
|
||||
IOHIDDeviceClose(device->m_hidIntf.get(), kIOHIDOptionsTypeNone);
|
||||
device->m_hidIntf.reset();
|
||||
/* Get the HID element's object (HID device interface) */
|
||||
IOObjectPointer<io_service_t> interfaceEntry =
|
||||
IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.data());
|
||||
if (!IOObjectConformsTo(interfaceEntry.get(), "IOHIDDevice")) {
|
||||
snprintf(errStr, 256, "Unable to find interface for %s@%s\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
void _deviceDisconnected()
|
||||
{
|
||||
m_runningTransferLoop = false;
|
||||
device->m_hidIntf = IOHIDDeviceCreate(nullptr, interfaceEntry.get());
|
||||
if (!device->m_hidIntf) {
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Open device */
|
||||
IOReturn err = IOHIDDeviceOpen(device->m_hidIntf.get(), kIOHIDOptionsTypeNone);
|
||||
if (err != kIOReturnSuccess) {
|
||||
if (err == kIOReturnExclusiveAccess) {
|
||||
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
} else {
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
}
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Register removal callback */
|
||||
IOHIDDeviceRegisterRemovalCallback(device->m_hidIntf.get(), _hidRemoveCb, device.get());
|
||||
|
||||
/* Make note if device uses bluetooth driver */
|
||||
if (CFTypeRef transport = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDTransportKey)))
|
||||
device->m_isBt =
|
||||
CFStringCompare(CFStringRef(transport), CFSTR(kIOHIDTransportBluetoothValue), 0) == kCFCompareEqualTo;
|
||||
|
||||
/* Register input buffer */
|
||||
std::unique_ptr<uint8_t[]> buffer;
|
||||
int bufSize = 0;
|
||||
if (CFTypeRef maxSize = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDMaxInputReportSizeKey)))
|
||||
CFNumberGetValue(CFNumberRef(maxSize), kCFNumberIntType, &bufSize);
|
||||
if (bufSize) {
|
||||
buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufSize]);
|
||||
IOHIDDeviceRegisterInputReportCallback(device->m_hidIntf.get(), buffer.get(), bufSize, _hidReportCb,
|
||||
device->m_devImp.get());
|
||||
IOHIDDeviceScheduleWithRunLoop(device->m_hidIntf.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
}
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop) {
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.010, true);
|
||||
if (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
}
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
IOHIDDeviceClose(device->m_hidIntf.get(), kIOHIDOptionsTypeNone);
|
||||
device->m_hidIntf.reset();
|
||||
}
|
||||
|
||||
void _deviceDisconnected() { m_runningTransferLoop = false; }
|
||||
|
||||
public:
|
||||
HIDDeviceIOKit(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
: m_token(token), m_devImp(devImp), m_devPath(token.getDevicePath()) {}
|
||||
|
||||
HIDDeviceIOKit(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
: m_token(token),
|
||||
m_devImp(devImp),
|
||||
m_devPath(token.getDevicePath())
|
||||
{
|
||||
void _startThread() {
|
||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||
DeviceType dType = m_token.getDeviceType();
|
||||
if (dType == DeviceType::USB)
|
||||
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
|
||||
else if (dType == DeviceType::Bluetooth)
|
||||
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
|
||||
else if (dType == DeviceType::HID)
|
||||
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
|
||||
else {
|
||||
fprintf(stderr, "invalid token supplied to device constructor\n");
|
||||
return;
|
||||
}
|
||||
m_initCond.wait(lk);
|
||||
}
|
||||
|
||||
void _startThread()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||
DeviceType dType = m_token.getDeviceType();
|
||||
if (dType == DeviceType::USB)
|
||||
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
|
||||
else if (dType == DeviceType::Bluetooth)
|
||||
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
|
||||
else if (dType == DeviceType::HID)
|
||||
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "invalid token supplied to device constructor\n");
|
||||
return;
|
||||
}
|
||||
m_initCond.wait(lk);
|
||||
}
|
||||
|
||||
~HIDDeviceIOKit()
|
||||
{
|
||||
m_runningTransferLoop = false;
|
||||
if (m_thread.joinable())
|
||||
m_thread.detach();
|
||||
}
|
||||
~HIDDeviceIOKit() {
|
||||
m_runningTransferLoop = false;
|
||||
if (m_thread.joinable())
|
||||
m_thread.detach();
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
return std::make_shared<HIDDeviceIOKit>(token, devImp);
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
|
||||
return std::make_shared<HIDDeviceIOKit>(token, devImp);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -1,34 +1,27 @@
|
||||
#include "IHIDDevice.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
class HIDDeviceNX : public IHIDDevice
|
||||
{
|
||||
DeviceToken& m_token;
|
||||
std::shared_ptr<DeviceBase> m_devImp;
|
||||
std::string_view m_devPath;
|
||||
class HIDDeviceNX : public IHIDDevice {
|
||||
DeviceToken& m_token;
|
||||
std::shared_ptr<DeviceBase> m_devImp;
|
||||
std::string_view m_devPath;
|
||||
|
||||
public:
|
||||
HIDDeviceNX(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
: m_token(token),
|
||||
m_devImp(devImp),
|
||||
m_devPath(token.getDevicePath())
|
||||
{
|
||||
}
|
||||
HIDDeviceNX(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
: m_token(token), m_devImp(devImp), m_devPath(token.getDevicePath()) {}
|
||||
|
||||
void _deviceDisconnected() {}
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; }
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; }
|
||||
std::vector<uint8_t> _getReportDescriptor() { return {}; }
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; }
|
||||
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return 0; }
|
||||
void _startThread() {}
|
||||
void _deviceDisconnected() {}
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; }
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; }
|
||||
std::vector<uint8_t> _getReportDescriptor() { return {}; }
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; }
|
||||
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return 0; }
|
||||
void _startThread() {}
|
||||
};
|
||||
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
return std::make_shared<HIDDeviceNX>(token, devImp);
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
|
||||
return std::make_shared<HIDDeviceNX>(token, devImp);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -3,28 +3,22 @@
|
||||
#include "boo/inputdev/DeviceToken.hpp"
|
||||
#include "boo/inputdev/DeviceBase.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
class HIDDeviceUWP : public IHIDDevice
|
||||
{
|
||||
class HIDDeviceUWP : public IHIDDevice {
|
||||
public:
|
||||
HIDDeviceUWP(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
HIDDeviceUWP(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {}
|
||||
|
||||
}
|
||||
|
||||
void _deviceDisconnected() {}
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; }
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; }
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; }
|
||||
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; }
|
||||
void _startThread() {}
|
||||
void _deviceDisconnected() {}
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; }
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; }
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; }
|
||||
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; }
|
||||
void _startThread() {}
|
||||
};
|
||||
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
return std::make_shared<HIDDeviceUWP>(token, devImp);
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
|
||||
return std::make_shared<HIDDeviceUWP>(token, devImp);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
#include <cstring>
|
||||
#include "boo/inputdev/HIDParser.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
udev* GetUdev();
|
||||
|
||||
@@ -27,331 +26,270 @@ udev* GetUdev();
|
||||
* Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html
|
||||
*/
|
||||
|
||||
class HIDDeviceUdev final : public IHIDDevice
|
||||
{
|
||||
DeviceToken& m_token;
|
||||
std::shared_ptr<DeviceBase> m_devImp;
|
||||
class HIDDeviceUdev final : public IHIDDevice {
|
||||
DeviceToken& m_token;
|
||||
std::shared_ptr<DeviceBase> m_devImp;
|
||||
|
||||
int m_devFd = 0;
|
||||
unsigned m_usbIntfInPipe = 0;
|
||||
unsigned m_usbIntfOutPipe = 0;
|
||||
bool m_runningTransferLoop = false;
|
||||
int m_devFd = 0;
|
||||
unsigned m_usbIntfInPipe = 0;
|
||||
unsigned m_usbIntfOutPipe = 0;
|
||||
bool m_runningTransferLoop = false;
|
||||
|
||||
std::string_view m_devPath;
|
||||
std::mutex m_initMutex;
|
||||
std::condition_variable m_initCond;
|
||||
std::thread m_thread;
|
||||
std::string_view m_devPath;
|
||||
std::mutex m_initMutex;
|
||||
std::condition_variable m_initCond;
|
||||
std::thread m_thread;
|
||||
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_devFd)
|
||||
{
|
||||
usbdevfs_bulktransfer xfer =
|
||||
{
|
||||
m_usbIntfOutPipe | USB_DIR_OUT,
|
||||
(unsigned)length,
|
||||
30,
|
||||
(void*)data
|
||||
};
|
||||
int ret = ioctl(m_devFd, USBDEVFS_BULK, &xfer);
|
||||
if (ret != (int)length)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) {
|
||||
if (m_devFd) {
|
||||
usbdevfs_bulktransfer xfer = {m_usbIntfOutPipe | USB_DIR_OUT, (unsigned)length, 30, (void*)data};
|
||||
int ret = ioctl(m_devFd, USBDEVFS_BULK, &xfer);
|
||||
if (ret != (int)length)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) {
|
||||
if (m_devFd) {
|
||||
usbdevfs_bulktransfer xfer = {m_usbIntfInPipe | USB_DIR_IN, (unsigned)length, 30, data};
|
||||
return ioctl(m_devFd, USBDEVFS_BULK, &xfer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceUdev> device) {
|
||||
int i;
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data());
|
||||
|
||||
/* Get device file */
|
||||
const char* dp = udev_device_get_devnode(udevDev);
|
||||
int fd = open(dp, O_RDWR);
|
||||
if (fd < 0) {
|
||||
snprintf(errStr, 256, "Unable to open %s@%s: %s\n", device->m_token.getProductName().data(), dp, strerror(errno));
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
udev_device_unref(udevDev);
|
||||
return;
|
||||
}
|
||||
device->m_devFd = fd;
|
||||
usb_device_descriptor devDesc = {};
|
||||
read(fd, &devDesc, 1);
|
||||
read(fd, &devDesc.bDescriptorType, devDesc.bLength - 1);
|
||||
if (devDesc.bNumConfigurations) {
|
||||
usb_config_descriptor confDesc = {};
|
||||
read(fd, &confDesc, 1);
|
||||
read(fd, &confDesc.bDescriptorType, confDesc.bLength - 1);
|
||||
if (confDesc.bNumInterfaces) {
|
||||
usb_interface_descriptor intfDesc = {};
|
||||
read(fd, &intfDesc, 1);
|
||||
read(fd, &intfDesc.bDescriptorType, intfDesc.bLength - 1);
|
||||
for (i = 0; i < intfDesc.bNumEndpoints + 1; ++i) {
|
||||
usb_endpoint_descriptor endpDesc = {};
|
||||
read(fd, &endpDesc, 1);
|
||||
read(fd, &endpDesc.bDescriptorType, endpDesc.bLength - 1);
|
||||
if ((endpDesc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
|
||||
if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
|
||||
device->m_usbIntfInPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
else if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
|
||||
device->m_usbIntfOutPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_devFd)
|
||||
{
|
||||
usbdevfs_bulktransfer xfer =
|
||||
{
|
||||
m_usbIntfInPipe | USB_DIR_IN,
|
||||
(unsigned)length,
|
||||
30,
|
||||
data
|
||||
};
|
||||
return ioctl(m_devFd, USBDEVFS_BULK, &xfer);
|
||||
}
|
||||
return 0;
|
||||
/* Request that kernel disconnects existing driver */
|
||||
usbdevfs_ioctl disconnectReq = {0, USBDEVFS_DISCONNECT, NULL};
|
||||
ioctl(fd, USBDEVFS_IOCTL, &disconnectReq);
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
close(fd);
|
||||
device->m_devFd = 0;
|
||||
udev_device_unref(udevDev);
|
||||
}
|
||||
|
||||
static void _threadProcBTLL(std::shared_ptr<HIDDeviceUdev> device) {
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data());
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
udev_device_unref(udevDev);
|
||||
}
|
||||
|
||||
static void _threadProcHID(std::shared_ptr<HIDDeviceUdev> device) {
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data());
|
||||
|
||||
/* Get device file */
|
||||
const char* dp = udev_device_get_devnode(udevDev);
|
||||
int fd = open(dp, O_RDWR | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
snprintf(errStr, 256, "Unable to open %s@%s: %s\n", device->m_token.getProductName().data(), dp, strerror(errno));
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
udev_device_unref(udevDev);
|
||||
return;
|
||||
}
|
||||
device->m_devFd = fd;
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Report descriptor size */
|
||||
int reportDescSize;
|
||||
if (ioctl(fd, HIDIOCGRDESCSIZE, &reportDescSize) == -1) {
|
||||
snprintf(errStr, 256, "Unable to ioctl(HIDIOCGRDESCSIZE) %s@%s: %s\n", device->m_token.getProductName().data(),
|
||||
dp, strerror(errno));
|
||||
device->m_devImp->deviceError(errStr);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceUdev> device)
|
||||
{
|
||||
int i;
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data());
|
||||
|
||||
/* Get device file */
|
||||
const char* dp = udev_device_get_devnode(udevDev);
|
||||
int fd = open(dp, O_RDWR);
|
||||
if (fd < 0)
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s: %s\n",
|
||||
device->m_token.getProductName().data(), dp, strerror(errno));
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
udev_device_unref(udevDev);
|
||||
return;
|
||||
}
|
||||
device->m_devFd = fd;
|
||||
usb_device_descriptor devDesc = {};
|
||||
read(fd, &devDesc, 1);
|
||||
read(fd, &devDesc.bDescriptorType, devDesc.bLength-1);
|
||||
if (devDesc.bNumConfigurations)
|
||||
{
|
||||
usb_config_descriptor confDesc = {};
|
||||
read(fd, &confDesc, 1);
|
||||
read(fd, &confDesc.bDescriptorType, confDesc.bLength-1);
|
||||
if (confDesc.bNumInterfaces)
|
||||
{
|
||||
usb_interface_descriptor intfDesc = {};
|
||||
read(fd, &intfDesc, 1);
|
||||
read(fd, &intfDesc.bDescriptorType, intfDesc.bLength-1);
|
||||
for (i=0 ; i<intfDesc.bNumEndpoints+1 ; ++i)
|
||||
{
|
||||
usb_endpoint_descriptor endpDesc = {};
|
||||
read(fd, &endpDesc, 1);
|
||||
read(fd, &endpDesc.bDescriptorType, endpDesc.bLength-1);
|
||||
if ((endpDesc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
|
||||
{
|
||||
if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
|
||||
device->m_usbIntfInPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
else if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
|
||||
device->m_usbIntfOutPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Request that kernel disconnects existing driver */
|
||||
usbdevfs_ioctl disconnectReq = {
|
||||
0,
|
||||
USBDEVFS_DISCONNECT,
|
||||
NULL
|
||||
};
|
||||
ioctl(fd, USBDEVFS_IOCTL, &disconnectReq);
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
close(fd);
|
||||
device->m_devFd = 0;
|
||||
udev_device_unref(udevDev);
|
||||
/* Get report descriptor */
|
||||
hidraw_report_descriptor reportDesc;
|
||||
reportDesc.size = reportDescSize;
|
||||
if (ioctl(fd, HIDIOCGRDESC, &reportDesc) == -1) {
|
||||
snprintf(errStr, 256, "Unable to ioctl(HIDIOCGRDESC) %s@%s: %s\n", device->m_token.getProductName().data(), dp,
|
||||
strerror(errno));
|
||||
device->m_devImp->deviceError(errStr);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
size_t readSz = HIDParser::CalculateMaxInputReportSize(reportDesc.value, reportDesc.size);
|
||||
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[readSz]);
|
||||
|
||||
static void _threadProcBTLL(std::shared_ptr<HIDDeviceUdev> device)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data());
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
udev_device_unref(udevDev);
|
||||
}
|
||||
|
||||
static void _threadProcHID(std::shared_ptr<HIDDeviceUdev> device)
|
||||
{
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data());
|
||||
|
||||
/* Get device file */
|
||||
const char* dp = udev_device_get_devnode(udevDev);
|
||||
int fd = open(dp, O_RDWR | O_NONBLOCK);
|
||||
if (fd < 0)
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s: %s\n",
|
||||
device->m_token.getProductName().data(), dp, strerror(errno));
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
udev_device_unref(udevDev);
|
||||
return;
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop) {
|
||||
fd_set readset;
|
||||
FD_ZERO(&readset);
|
||||
FD_SET(fd, &readset);
|
||||
struct timeval timeout = {0, 10000};
|
||||
if (select(fd + 1, &readset, nullptr, nullptr, &timeout) > 0) {
|
||||
while (true) {
|
||||
ssize_t sz = read(fd, readBuf.get(), readSz);
|
||||
if (sz < 0)
|
||||
break;
|
||||
device->m_devImp->receivedHIDReport(readBuf.get(), sz, HIDReportType::Input, readBuf[0]);
|
||||
}
|
||||
device->m_devFd = fd;
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Report descriptor size */
|
||||
int reportDescSize;
|
||||
if (ioctl(fd, HIDIOCGRDESCSIZE, &reportDescSize) == -1)
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to ioctl(HIDIOCGRDESCSIZE) %s@%s: %s\n",
|
||||
device->m_token.getProductName().data(), dp, strerror(errno));
|
||||
device->m_devImp->deviceError(errStr);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get report descriptor */
|
||||
hidraw_report_descriptor reportDesc;
|
||||
reportDesc.size = reportDescSize;
|
||||
if (ioctl(fd, HIDIOCGRDESC, &reportDesc) == -1)
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to ioctl(HIDIOCGRDESC) %s@%s: %s\n",
|
||||
device->m_token.getProductName().data(), dp, strerror(errno));
|
||||
device->m_devImp->deviceError(errStr);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
size_t readSz = HIDParser::CalculateMaxInputReportSize(reportDesc.value, reportDesc.size);
|
||||
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[readSz]);
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
{
|
||||
fd_set readset;
|
||||
FD_ZERO(&readset);
|
||||
FD_SET(fd, &readset);
|
||||
struct timeval timeout = {0, 10000};
|
||||
if (select(fd + 1, &readset, nullptr, nullptr, &timeout) > 0)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ssize_t sz = read(fd, readBuf.get(), readSz);
|
||||
if (sz < 0)
|
||||
break;
|
||||
device->m_devImp->receivedHIDReport(readBuf.get(), sz,
|
||||
HIDReportType::Input, readBuf[0]);
|
||||
}
|
||||
}
|
||||
if (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
}
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
close(fd);
|
||||
device->m_devFd = 0;
|
||||
udev_device_unref(udevDev);
|
||||
}
|
||||
if (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
}
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
void _deviceDisconnected()
|
||||
{
|
||||
m_runningTransferLoop = false;
|
||||
/* Cleanup */
|
||||
close(fd);
|
||||
device->m_devFd = 0;
|
||||
udev_device_unref(udevDev);
|
||||
}
|
||||
|
||||
void _deviceDisconnected() { m_runningTransferLoop = false; }
|
||||
|
||||
std::vector<uint8_t> _getReportDescriptor() {
|
||||
/* Report descriptor size */
|
||||
int reportDescSize;
|
||||
if (ioctl(m_devFd, HIDIOCGRDESCSIZE, &reportDescSize) == -1)
|
||||
return {};
|
||||
|
||||
/* Get report descriptor */
|
||||
hidraw_report_descriptor reportDesc;
|
||||
reportDesc.size = reportDescSize;
|
||||
if (ioctl(m_devFd, HIDIOCGRDESC, &reportDesc) == -1)
|
||||
return {};
|
||||
std::vector<uint8_t> ret(reportDesc.size, '\0');
|
||||
memmove(ret.data(), reportDesc.value, reportDesc.size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
|
||||
if (m_devFd) {
|
||||
if (tp == HIDReportType::Feature) {
|
||||
int ret = ioctl(m_devFd, HIDIOCSFEATURE(length), data);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
return true;
|
||||
} else if (tp == HIDReportType::Output) {
|
||||
ssize_t ret = write(m_devFd, data, length);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> _getReportDescriptor()
|
||||
{
|
||||
/* Report descriptor size */
|
||||
int reportDescSize;
|
||||
if (ioctl(m_devFd, HIDIOCGRDESCSIZE, &reportDescSize) == -1)
|
||||
return {};
|
||||
|
||||
/* Get report descriptor */
|
||||
hidraw_report_descriptor reportDesc;
|
||||
reportDesc.size = reportDescSize;
|
||||
if (ioctl(m_devFd, HIDIOCGRDESC, &reportDesc) == -1)
|
||||
return {};
|
||||
std::vector<uint8_t> ret(reportDesc.size, '\0');
|
||||
memmove(ret.data(), reportDesc.value, reportDesc.size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
if (m_devFd)
|
||||
{
|
||||
if (tp == HIDReportType::Feature)
|
||||
{
|
||||
int ret = ioctl(m_devFd, HIDIOCSFEATURE(length), data);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else if (tp == HIDReportType::Output)
|
||||
{
|
||||
ssize_t ret = write(m_devFd, data, length);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t _receiveHIDReport(uint8_t *data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
if (m_devFd)
|
||||
{
|
||||
if (tp == HIDReportType::Feature)
|
||||
{
|
||||
data[0] = message;
|
||||
int ret = ioctl(m_devFd, HIDIOCGFEATURE(length), data);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
return length;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
|
||||
if (m_devFd) {
|
||||
if (tp == HIDReportType::Feature) {
|
||||
data[0] = message;
|
||||
int ret = ioctl(m_devFd, HIDIOCGFEATURE(length), data);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
return length;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
HIDDeviceUdev(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
: m_token(token), m_devImp(devImp), m_devPath(token.getDevicePath()) {}
|
||||
|
||||
HIDDeviceUdev(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
: m_token(token),
|
||||
m_devImp(devImp),
|
||||
m_devPath(token.getDevicePath())
|
||||
{
|
||||
void _startThread() {
|
||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||
DeviceType dType = m_token.getDeviceType();
|
||||
if (dType == DeviceType::USB)
|
||||
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
|
||||
else if (dType == DeviceType::Bluetooth)
|
||||
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
|
||||
else if (dType == DeviceType::HID)
|
||||
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
|
||||
else {
|
||||
fprintf(stderr, "invalid token supplied to device constructor");
|
||||
abort();
|
||||
}
|
||||
m_initCond.wait(lk);
|
||||
}
|
||||
|
||||
void _startThread()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||
DeviceType dType = m_token.getDeviceType();
|
||||
if (dType == DeviceType::USB)
|
||||
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
|
||||
else if (dType == DeviceType::Bluetooth)
|
||||
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
|
||||
else if (dType == DeviceType::HID)
|
||||
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "invalid token supplied to device constructor");
|
||||
abort();
|
||||
}
|
||||
m_initCond.wait(lk);
|
||||
}
|
||||
|
||||
~HIDDeviceUdev()
|
||||
{
|
||||
m_runningTransferLoop = false;
|
||||
if (m_thread.joinable())
|
||||
m_thread.detach();
|
||||
}
|
||||
|
||||
|
||||
~HIDDeviceUdev() {
|
||||
m_runningTransferLoop = false;
|
||||
if (m_thread.joinable())
|
||||
m_thread.detach();
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
return std::make_shared<HIDDeviceUdev>(token, devImp);
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
|
||||
return std::make_shared<HIDDeviceUdev>(token, devImp);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -21,378 +21,311 @@
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
class HIDDeviceWinUSB final : public IHIDDevice
|
||||
{
|
||||
DeviceToken& m_token;
|
||||
std::shared_ptr<DeviceBase> m_devImp;
|
||||
class HIDDeviceWinUSB final : public IHIDDevice {
|
||||
DeviceToken& m_token;
|
||||
std::shared_ptr<DeviceBase> m_devImp;
|
||||
|
||||
HANDLE m_devHandle = 0;
|
||||
HANDLE m_hidHandle = 0;
|
||||
WINUSB_INTERFACE_HANDLE m_usbHandle = nullptr;
|
||||
unsigned m_usbIntfInPipe = 0;
|
||||
unsigned m_usbIntfOutPipe = 0;
|
||||
bool m_runningTransferLoop = false;
|
||||
HANDLE m_devHandle = 0;
|
||||
HANDLE m_hidHandle = 0;
|
||||
WINUSB_INTERFACE_HANDLE m_usbHandle = nullptr;
|
||||
unsigned m_usbIntfInPipe = 0;
|
||||
unsigned m_usbIntfOutPipe = 0;
|
||||
bool m_runningTransferLoop = false;
|
||||
|
||||
std::string_view m_devPath;
|
||||
std::mutex m_initMutex;
|
||||
std::condition_variable m_initCond;
|
||||
std::thread m_thread;
|
||||
std::string_view m_devPath;
|
||||
std::mutex m_initMutex;
|
||||
std::condition_variable m_initCond;
|
||||
std::thread m_thread;
|
||||
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_usbHandle)
|
||||
{
|
||||
ULONG lengthTransferred = 0;
|
||||
if (!WinUsb_WritePipe(m_usbHandle, m_usbIntfOutPipe, (PUCHAR)data,
|
||||
(ULONG)length, &lengthTransferred, NULL) ||
|
||||
lengthTransferred != length)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) {
|
||||
if (m_usbHandle) {
|
||||
ULONG lengthTransferred = 0;
|
||||
if (!WinUsb_WritePipe(m_usbHandle, m_usbIntfOutPipe, (PUCHAR)data, (ULONG)length, &lengthTransferred, NULL) ||
|
||||
lengthTransferred != length)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) {
|
||||
if (m_usbHandle) {
|
||||
ULONG lengthTransferred = 0;
|
||||
if (!WinUsb_ReadPipe(m_usbHandle, m_usbIntfInPipe, (PUCHAR)data, (ULONG)length, &lengthTransferred, NULL))
|
||||
return 0;
|
||||
return lengthTransferred;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceWinUSB> device) {
|
||||
unsigned i;
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* POSIX.. who needs it?? -MS */
|
||||
device->m_devHandle =
|
||||
CreateFileA(device->m_devPath.data(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (INVALID_HANDLE_VALUE == device->m_devHandle) {
|
||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data(), GetLastError());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_usbHandle)
|
||||
{
|
||||
ULONG lengthTransferred = 0;
|
||||
if (!WinUsb_ReadPipe(m_usbHandle, m_usbIntfInPipe, (PUCHAR)data,
|
||||
(ULONG)length, &lengthTransferred, NULL))
|
||||
return 0;
|
||||
return lengthTransferred;
|
||||
if (!WinUsb_Initialize(device->m_devHandle, &device->m_usbHandle)) {
|
||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data(), GetLastError());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
CloseHandle(device->m_devHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enumerate device pipes */
|
||||
USB_INTERFACE_DESCRIPTOR ifDesc = {0};
|
||||
if (!WinUsb_QueryInterfaceSettings(device->m_usbHandle, 0, &ifDesc)) {
|
||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data(), GetLastError());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
CloseHandle(device->m_devHandle);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < ifDesc.bNumEndpoints; ++i) {
|
||||
WINUSB_PIPE_INFORMATION pipeDesc;
|
||||
WinUsb_QueryPipe(device->m_usbHandle, 0, i, &pipeDesc);
|
||||
if (pipeDesc.PipeType == UsbdPipeTypeInterrupt) {
|
||||
if (USB_ENDPOINT_DIRECTION_IN(pipeDesc.PipeId))
|
||||
device->m_usbIntfInPipe = pipeDesc.PipeId;
|
||||
else
|
||||
device->m_usbIntfOutPipe = pipeDesc.PipeId;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
WinUsb_Free(device->m_usbHandle);
|
||||
CloseHandle(device->m_devHandle);
|
||||
device->m_devHandle = 0;
|
||||
}
|
||||
|
||||
static void _threadProcBTLL(std::shared_ptr<HIDDeviceWinUSB> device) {
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
}
|
||||
|
||||
size_t m_minFeatureSz = 0;
|
||||
size_t m_minInputSz = 0;
|
||||
size_t m_minOutputSz = 0;
|
||||
|
||||
PHIDP_PREPARSED_DATA m_preparsedData = nullptr;
|
||||
|
||||
static void _threadProcHID(std::shared_ptr<HIDDeviceWinUSB> device) {
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* POSIX.. who needs it?? -MS */
|
||||
device->m_overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
device->m_hidHandle =
|
||||
CreateFileA(device->m_devPath.data(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (INVALID_HANDLE_VALUE == device->m_hidHandle) {
|
||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data(), GetLastError());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HidD_GetPreparsedData(device->m_hidHandle, &device->m_preparsedData)) {
|
||||
_snprintf(errStr, 256, "Unable get preparsed data of %s@%s: %d\n", device->m_token.getProductName().data(),
|
||||
device->m_devPath.data(), GetLastError());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
HIDP_CAPS caps;
|
||||
HidP_GetCaps(device->m_preparsedData, &caps);
|
||||
device->m_minFeatureSz = caps.FeatureReportByteLength;
|
||||
device->m_minInputSz = caps.InputReportByteLength;
|
||||
device->m_minOutputSz = caps.OutputReportByteLength;
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Allocate read buffer */
|
||||
size_t inBufferSz = device->m_minInputSz;
|
||||
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[inBufferSz]);
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop) {
|
||||
device->ReadCycle(readBuf.get(), inBufferSz);
|
||||
if (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
}
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
CloseHandle(device->m_overlapped.hEvent);
|
||||
CloseHandle(device->m_hidHandle);
|
||||
HidD_FreePreparsedData(device->m_preparsedData);
|
||||
device->m_hidHandle = nullptr;
|
||||
}
|
||||
|
||||
void _deviceDisconnected() { m_runningTransferLoop = false; }
|
||||
|
||||
std::vector<uint8_t> m_sendBuf;
|
||||
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) {
|
||||
size_t maxOut = std::max(m_minFeatureSz, std::max(m_minOutputSz, length));
|
||||
if (m_sendBuf.size() < maxOut)
|
||||
m_sendBuf.resize(maxOut);
|
||||
if (maxOut > length)
|
||||
memset(m_sendBuf.data() + length, 0, maxOut - length);
|
||||
memmove(m_sendBuf.data(), data, length);
|
||||
|
||||
if (tp == HIDReportType::Output) {
|
||||
DWORD useLength = DWORD(std::max(length, m_minOutputSz));
|
||||
DWORD BytesWritten;
|
||||
OVERLAPPED Overlapped;
|
||||
ZeroMemory(&Overlapped, sizeof(Overlapped));
|
||||
BOOL Result = WriteFile(m_hidHandle, m_sendBuf.data(), useLength, &BytesWritten, &Overlapped);
|
||||
if (!Result) {
|
||||
DWORD Error = GetLastError();
|
||||
|
||||
if (Error == ERROR_INVALID_USER_BUFFER) {
|
||||
// std::cout << "Falling back to SetOutputReport" << std::endl;
|
||||
if (!HidD_SetOutputReport(m_hidHandle, (PVOID)m_sendBuf.data(), useLength))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Error != ERROR_IO_PENDING) {
|
||||
fprintf(stderr, "Write Failed %08X\n", int(Error));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetOverlappedResult(m_hidHandle, &Overlapped, &BytesWritten, TRUE)) {
|
||||
DWORD Error = GetLastError();
|
||||
fprintf(stderr, "Write Failed %08X\n", int(Error));
|
||||
return false;
|
||||
}
|
||||
} else if (tp == HIDReportType::Feature) {
|
||||
DWORD useLength = DWORD(std::max(length, m_minFeatureSz));
|
||||
if (!HidD_SetFeature(m_hidHandle, (PVOID)m_sendBuf.data(), useLength)) {
|
||||
// int error = GetLastError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
|
||||
size_t maxIn = std::max(m_minFeatureSz, std::max(m_minInputSz, length));
|
||||
if (m_recvBuf.size() < maxIn)
|
||||
m_recvBuf.resize(maxIn);
|
||||
memset(m_recvBuf.data(), 0, length);
|
||||
m_recvBuf[0] = message;
|
||||
|
||||
if (tp == HIDReportType::Input) {
|
||||
if (!HidD_GetInputReport(m_hidHandle, m_recvBuf.data(), ULONG(std::max(m_minInputSz, length))))
|
||||
return 0;
|
||||
} else if (tp == HIDReportType::Feature) {
|
||||
if (!HidD_GetFeature(m_hidHandle, m_recvBuf.data(), ULONG(std::max(m_minFeatureSz, length))))
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceWinUSB> device)
|
||||
{
|
||||
unsigned i;
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* POSIX.. who needs it?? -MS */
|
||||
device->m_devHandle = CreateFileA(device->m_devPath.data(),
|
||||
GENERIC_WRITE | GENERIC_READ,
|
||||
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||||
NULL);
|
||||
if (INVALID_HANDLE_VALUE == device->m_devHandle)
|
||||
{
|
||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||
device->m_token.getProductName().data(),
|
||||
device->m_devPath.data(), GetLastError());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WinUsb_Initialize(device->m_devHandle, &device->m_usbHandle))
|
||||
{
|
||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||
device->m_token.getProductName().data(),
|
||||
device->m_devPath.data(), GetLastError());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
CloseHandle(device->m_devHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enumerate device pipes */
|
||||
USB_INTERFACE_DESCRIPTOR ifDesc = {0};
|
||||
if (!WinUsb_QueryInterfaceSettings(device->m_usbHandle, 0, &ifDesc))
|
||||
{
|
||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||
device->m_token.getProductName().data(),
|
||||
device->m_devPath.data(), GetLastError());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
CloseHandle(device->m_devHandle);
|
||||
return;
|
||||
}
|
||||
for (i=0 ; i<ifDesc.bNumEndpoints ; ++i)
|
||||
{
|
||||
WINUSB_PIPE_INFORMATION pipeDesc;
|
||||
WinUsb_QueryPipe(device->m_usbHandle, 0, i, &pipeDesc);
|
||||
if (pipeDesc.PipeType == UsbdPipeTypeInterrupt)
|
||||
{
|
||||
if (USB_ENDPOINT_DIRECTION_IN(pipeDesc.PipeId))
|
||||
device->m_usbIntfInPipe = pipeDesc.PipeId;
|
||||
else
|
||||
device->m_usbIntfOutPipe = pipeDesc.PipeId;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
WinUsb_Free(device->m_usbHandle);
|
||||
CloseHandle(device->m_devHandle);
|
||||
device->m_devHandle = 0;
|
||||
}
|
||||
|
||||
static void _threadProcBTLL(std::shared_ptr<HIDDeviceWinUSB> device)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
}
|
||||
|
||||
size_t m_minFeatureSz = 0;
|
||||
size_t m_minInputSz = 0;
|
||||
size_t m_minOutputSz = 0;
|
||||
|
||||
PHIDP_PREPARSED_DATA m_preparsedData = nullptr;
|
||||
|
||||
static void _threadProcHID(std::shared_ptr<HIDDeviceWinUSB> device)
|
||||
{
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* POSIX.. who needs it?? -MS */
|
||||
device->m_overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
device->m_hidHandle = CreateFileA(device->m_devPath.data(),
|
||||
GENERIC_WRITE | GENERIC_READ,
|
||||
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||||
NULL);
|
||||
if (INVALID_HANDLE_VALUE == device->m_hidHandle)
|
||||
{
|
||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||
device->m_token.getProductName().data(),
|
||||
device->m_devPath.data(), GetLastError());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HidD_GetPreparsedData(device->m_hidHandle, &device->m_preparsedData))
|
||||
{
|
||||
_snprintf(errStr, 256, "Unable get preparsed data of %s@%s: %d\n",
|
||||
device->m_token.getProductName().data(),
|
||||
device->m_devPath.data(), GetLastError());
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
HIDP_CAPS caps;
|
||||
HidP_GetCaps(device->m_preparsedData, &caps);
|
||||
device->m_minFeatureSz = caps.FeatureReportByteLength;
|
||||
device->m_minInputSz = caps.InputReportByteLength;
|
||||
device->m_minOutputSz = caps.OutputReportByteLength;
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Allocate read buffer */
|
||||
size_t inBufferSz = device->m_minInputSz;
|
||||
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[inBufferSz]);
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
{
|
||||
device->ReadCycle(readBuf.get(), inBufferSz);
|
||||
if (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
}
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
CloseHandle(device->m_overlapped.hEvent);
|
||||
CloseHandle(device->m_hidHandle);
|
||||
HidD_FreePreparsedData(device->m_preparsedData);
|
||||
device->m_hidHandle = nullptr;
|
||||
}
|
||||
|
||||
void _deviceDisconnected()
|
||||
{
|
||||
m_runningTransferLoop = false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> m_sendBuf;
|
||||
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)
|
||||
{
|
||||
size_t maxOut = std::max(m_minFeatureSz, std::max(m_minOutputSz, length));
|
||||
if (m_sendBuf.size() < maxOut)
|
||||
m_sendBuf.resize(maxOut);
|
||||
if (maxOut > length)
|
||||
memset(m_sendBuf.data() + length, 0, maxOut - length);
|
||||
memmove(m_sendBuf.data(), data, length);
|
||||
|
||||
if (tp == HIDReportType::Output)
|
||||
{
|
||||
DWORD useLength = DWORD(std::max(length, m_minOutputSz));
|
||||
DWORD BytesWritten;
|
||||
OVERLAPPED Overlapped;
|
||||
ZeroMemory(&Overlapped, sizeof(Overlapped));
|
||||
BOOL Result = WriteFile(m_hidHandle, m_sendBuf.data(), useLength, &BytesWritten, &Overlapped);
|
||||
if (!Result)
|
||||
{
|
||||
DWORD Error = GetLastError();
|
||||
|
||||
if (Error == ERROR_INVALID_USER_BUFFER)
|
||||
{
|
||||
//std::cout << "Falling back to SetOutputReport" << std::endl;
|
||||
if (!HidD_SetOutputReport(m_hidHandle, (PVOID)m_sendBuf.data(), useLength))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Error != ERROR_IO_PENDING)
|
||||
{
|
||||
fprintf(stderr, "Write Failed %08X\n", int(Error));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetOverlappedResult(m_hidHandle, &Overlapped, &BytesWritten, TRUE))
|
||||
{
|
||||
DWORD Error = GetLastError();
|
||||
fprintf(stderr, "Write Failed %08X\n", int(Error));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (tp == HIDReportType::Feature)
|
||||
{
|
||||
DWORD useLength = DWORD(std::max(length, m_minFeatureSz));
|
||||
if (!HidD_SetFeature(m_hidHandle, (PVOID)m_sendBuf.data(), useLength))
|
||||
{
|
||||
//int error = GetLastError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
size_t maxIn = std::max(m_minFeatureSz, std::max(m_minInputSz, length));
|
||||
if (m_recvBuf.size() < maxIn)
|
||||
m_recvBuf.resize(maxIn);
|
||||
memset(m_recvBuf.data(), 0, length);
|
||||
m_recvBuf[0] = message;
|
||||
|
||||
if (tp == HIDReportType::Input)
|
||||
{
|
||||
if (!HidD_GetInputReport(m_hidHandle, m_recvBuf.data(), ULONG(std::max(m_minInputSz, length))))
|
||||
return 0;
|
||||
}
|
||||
else if (tp == HIDReportType::Feature)
|
||||
{
|
||||
if (!HidD_GetFeature(m_hidHandle, m_recvBuf.data(), ULONG(std::max(m_minFeatureSz, length))))
|
||||
return 0;
|
||||
}
|
||||
|
||||
memmove(data, m_recvBuf.data(), length);
|
||||
return length;
|
||||
}
|
||||
memmove(data, m_recvBuf.data(), length);
|
||||
return length;
|
||||
}
|
||||
|
||||
public:
|
||||
HIDDeviceWinUSB(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
: m_token(token), m_devImp(devImp), m_devPath(token.getDevicePath()) {}
|
||||
|
||||
HIDDeviceWinUSB(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
: m_token(token),
|
||||
m_devImp(devImp),
|
||||
m_devPath(token.getDevicePath())
|
||||
{
|
||||
}
|
||||
void _startThread() {
|
||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||
DeviceType dType = m_token.getDeviceType();
|
||||
if (dType == DeviceType::USB)
|
||||
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
|
||||
else if (dType == DeviceType::Bluetooth)
|
||||
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
|
||||
else if (dType == DeviceType::HID)
|
||||
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
|
||||
else
|
||||
throw std::runtime_error("invalid token supplied to device constructor");
|
||||
m_initCond.wait(lk);
|
||||
}
|
||||
|
||||
void _startThread()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||
DeviceType dType = m_token.getDeviceType();
|
||||
if (dType == DeviceType::USB)
|
||||
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
|
||||
else if (dType == DeviceType::Bluetooth)
|
||||
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
|
||||
else if (dType == DeviceType::HID)
|
||||
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
|
||||
else
|
||||
throw std::runtime_error("invalid token supplied to device constructor");
|
||||
m_initCond.wait(lk);
|
||||
}
|
||||
~HIDDeviceWinUSB() {
|
||||
m_runningTransferLoop = false;
|
||||
if (m_thread.joinable())
|
||||
m_thread.detach();
|
||||
}
|
||||
|
||||
~HIDDeviceWinUSB()
|
||||
{
|
||||
OVERLAPPED m_overlapped = {};
|
||||
|
||||
void ReadCycle(uint8_t* inBuffer, size_t inBufferSz) {
|
||||
ResetEvent(m_overlapped.hEvent);
|
||||
ZeroMemory(inBuffer, inBufferSz);
|
||||
DWORD BytesRead = 0;
|
||||
BOOL Result = ReadFile(m_hidHandle, inBuffer, DWORD(inBufferSz), &BytesRead, &m_overlapped);
|
||||
if (!Result) {
|
||||
DWORD Error = GetLastError();
|
||||
if (Error == ERROR_DEVICE_NOT_CONNECTED) {
|
||||
m_runningTransferLoop = false;
|
||||
if (m_thread.joinable())
|
||||
m_thread.detach();
|
||||
return;
|
||||
} else if (Error != ERROR_IO_PENDING) {
|
||||
fprintf(stderr, "Read Failed: %08X\n", int(Error));
|
||||
return;
|
||||
} else if (!GetOverlappedResultEx(m_hidHandle, &m_overlapped, &BytesRead, 10, TRUE)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
OVERLAPPED m_overlapped = {};
|
||||
|
||||
void ReadCycle(uint8_t* inBuffer, size_t inBufferSz)
|
||||
{
|
||||
ResetEvent(m_overlapped.hEvent);
|
||||
ZeroMemory(inBuffer, inBufferSz);
|
||||
DWORD BytesRead = 0;
|
||||
BOOL Result = ReadFile(m_hidHandle, inBuffer, DWORD(inBufferSz), &BytesRead, &m_overlapped);
|
||||
if (!Result)
|
||||
{
|
||||
DWORD Error = GetLastError();
|
||||
if (Error == ERROR_DEVICE_NOT_CONNECTED)
|
||||
{
|
||||
m_runningTransferLoop = false;
|
||||
return;
|
||||
}
|
||||
else if (Error != ERROR_IO_PENDING)
|
||||
{
|
||||
fprintf(stderr, "Read Failed: %08X\n", int(Error));
|
||||
return;
|
||||
}
|
||||
else if (!GetOverlappedResultEx(m_hidHandle, &m_overlapped, &BytesRead, 10, TRUE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_devImp->receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]);
|
||||
}
|
||||
m_devImp->receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]);
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
return std::make_shared<HIDDeviceWinUSB>(token, devImp);
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
|
||||
return std::make_shared<HIDDeviceWinUSB>(token, devImp);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -2,33 +2,25 @@
|
||||
#include "boo/inputdev/DeviceToken.hpp"
|
||||
#include "boo/inputdev/DeviceBase.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
class HIDDeviceBSD final : public IHIDDevice
|
||||
{
|
||||
DeviceToken& m_token;
|
||||
DeviceBase& m_devImp;
|
||||
class HIDDeviceBSD final : public IHIDDevice {
|
||||
DeviceToken& m_token;
|
||||
DeviceBase& m_devImp;
|
||||
|
||||
void _deviceDisconnected() {}
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; }
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; }
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message) { return false; }
|
||||
size_t _recieveReport(const uint8_t* data, size_t length, uint16_t message) { return 0; }
|
||||
|
||||
void _deviceDisconnected() {}
|
||||
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; }
|
||||
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; }
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message) { return false; }
|
||||
size_t _recieveReport(const uint8_t* data, size_t length, uint16_t message) {return 0; }
|
||||
public:
|
||||
HIDDeviceBSD(DeviceToken& token, DeviceBase& devImp)
|
||||
: m_token(token),
|
||||
m_devImp(devImp)
|
||||
{
|
||||
}
|
||||
HIDDeviceBSD(DeviceToken& token, DeviceBase& devImp) : m_token(token), m_devImp(devImp) {}
|
||||
|
||||
~HIDDeviceBSD()
|
||||
{
|
||||
}
|
||||
~HIDDeviceBSD() {}
|
||||
};
|
||||
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
return std::make_shared<HIDDeviceBSD>(token, devImp);
|
||||
}
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
|
||||
return std::make_shared<HIDDeviceBSD>(token, devImp);
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -11,326 +11,285 @@
|
||||
#include "IOKitPointer.hpp"
|
||||
#include "../CFPointer.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
/*
|
||||
* Reference: http://oroboro.com/usb-serial-number-osx/
|
||||
*/
|
||||
|
||||
static bool getUSBStringDescriptor(const IUnknownPointer<IOUSBDeviceInterface182>& usbDevice, UInt8 idx, char* out)
|
||||
{
|
||||
UInt16 buffer[128];
|
||||
|
||||
// wow... we're actually forced to make hard coded bus requests. Its like
|
||||
// hard disk programming in the 80's!
|
||||
IOUSBDevRequest request;
|
||||
|
||||
request.bmRequestType = USBmakebmRequestType(kUSBIn,
|
||||
kUSBStandard,
|
||||
kUSBDevice);
|
||||
request.bRequest = kUSBRqGetDescriptor;
|
||||
request.wValue = (kUSBStringDesc << 8) | idx;
|
||||
request.wIndex = 0x409; // english
|
||||
request.wLength = sizeof(buffer);
|
||||
request.pData = buffer;
|
||||
|
||||
kern_return_t err = usbDevice->DeviceRequest(usbDevice.storage(), &request);
|
||||
if (err != 0)
|
||||
{
|
||||
// the request failed... fairly uncommon for the USB disk driver, but not
|
||||
// so uncommon for other devices. This can also be less reliable if your
|
||||
// disk is mounted through an external USB hub. At this level we actually
|
||||
// have to worry about hardware issues like this.
|
||||
return false;
|
||||
}
|
||||
|
||||
// we're mallocing this string just as an example. But you probably will want
|
||||
// to do something smarter, like pre-allocated buffers in the info class, or
|
||||
// use a string class.
|
||||
if (request.wLenDone == 0)
|
||||
return false;
|
||||
static bool getUSBStringDescriptor(const IUnknownPointer<IOUSBDeviceInterface182>& usbDevice, UInt8 idx, char* out) {
|
||||
UInt16 buffer[128];
|
||||
|
||||
unsigned count = (request.wLenDone - 1) / 2;
|
||||
unsigned i;
|
||||
for (i=0 ; i<count ; ++i)
|
||||
out[i] = buffer[i+1];
|
||||
out[i] = '\0';
|
||||
|
||||
return true;
|
||||
// wow... we're actually forced to make hard coded bus requests. Its like
|
||||
// hard disk programming in the 80's!
|
||||
IOUSBDevRequest request;
|
||||
|
||||
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
|
||||
request.bRequest = kUSBRqGetDescriptor;
|
||||
request.wValue = (kUSBStringDesc << 8) | idx;
|
||||
request.wIndex = 0x409; // english
|
||||
request.wLength = sizeof(buffer);
|
||||
request.pData = buffer;
|
||||
|
||||
kern_return_t err = usbDevice->DeviceRequest(usbDevice.storage(), &request);
|
||||
if (err != 0) {
|
||||
// the request failed... fairly uncommon for the USB disk driver, but not
|
||||
// so uncommon for other devices. This can also be less reliable if your
|
||||
// disk is mounted through an external USB hub. At this level we actually
|
||||
// have to worry about hardware issues like this.
|
||||
return false;
|
||||
}
|
||||
|
||||
// we're mallocing this string just as an example. But you probably will want
|
||||
// to do something smarter, like pre-allocated buffers in the info class, or
|
||||
// use a string class.
|
||||
if (request.wLenDone == 0)
|
||||
return false;
|
||||
|
||||
unsigned count = (request.wLenDone - 1) / 2;
|
||||
unsigned i;
|
||||
for (i = 0; i < count; ++i)
|
||||
out[i] = buffer[i + 1];
|
||||
out[i] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class HIDListenerIOKit : public IHIDListener
|
||||
{
|
||||
DeviceFinder& m_finder;
|
||||
|
||||
CFRunLoopRef m_listenerRunLoop;
|
||||
IONotificationPortRef m_llPort;
|
||||
IOObjectPointer<io_iterator_t> m_llAddNotif, m_llRemoveNotif;
|
||||
IOObjectPointer<io_iterator_t> m_hidAddNotif, m_hidRemoveNotif;
|
||||
const char* m_usbClass;
|
||||
bool m_scanningEnabled;
|
||||
|
||||
static void devicesConnectedUSBLL(HIDListenerIOKit* listener,
|
||||
io_iterator_t iterator)
|
||||
{
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
|
||||
{
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
class HIDListenerIOKit : public IHIDListener {
|
||||
DeviceFinder& m_finder;
|
||||
|
||||
if (!listener->m_scanningEnabled ||
|
||||
listener->m_finder._hasToken(devPath))
|
||||
continue;
|
||||
|
||||
UInt16 vid, pid;
|
||||
char vstr[128] = {0};
|
||||
char pstr[128] = {0};
|
||||
{
|
||||
IOCFPluginPointer devServ;
|
||||
SInt32 score;
|
||||
IOReturn err;
|
||||
err = IOCreatePlugInInterfaceForService(obj.get(), kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
fprintf(stderr, "unable to open IOKit plugin interface\n");
|
||||
continue;
|
||||
}
|
||||
CFRunLoopRef m_listenerRunLoop;
|
||||
IONotificationPortRef m_llPort;
|
||||
IOObjectPointer<io_iterator_t> m_llAddNotif, m_llRemoveNotif;
|
||||
IOObjectPointer<io_iterator_t> m_hidAddNotif, m_hidRemoveNotif;
|
||||
const char* m_usbClass;
|
||||
bool m_scanningEnabled;
|
||||
|
||||
IUnknownPointer<IOUSBDeviceInterface182> dev;
|
||||
err = devServ.As(&dev, kIOUSBDeviceInterfaceID182);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
fprintf(stderr, "unable to open IOKit device interface\n");
|
||||
continue;
|
||||
}
|
||||
static void devicesConnectedUSBLL(HIDListenerIOKit* listener, io_iterator_t iterator) {
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator)) {
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
|
||||
dev->GetDeviceVendor(dev.storage(), &vid);
|
||||
dev->GetDeviceProduct(dev.storage(), &pid);
|
||||
if (!listener->m_scanningEnabled || listener->m_finder._hasToken(devPath))
|
||||
continue;
|
||||
|
||||
UInt8 vstridx, pstridx;
|
||||
dev->USBGetManufacturerStringIndex(dev.storage(), &vstridx);
|
||||
dev->USBGetProductStringIndex(dev.storage(), &pstridx);
|
||||
|
||||
getUSBStringDescriptor(dev, vstridx, vstr);
|
||||
getUSBStringDescriptor(dev, pstridx, pstr);
|
||||
}
|
||||
|
||||
listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::USB,
|
||||
vid, pid, vstr, pstr, devPath));
|
||||
|
||||
//printf("ADDED %08X %s\n", obj.get(), devPath);
|
||||
UInt16 vid, pid;
|
||||
char vstr[128] = {0};
|
||||
char pstr[128] = {0};
|
||||
{
|
||||
IOCFPluginPointer devServ;
|
||||
SInt32 score;
|
||||
IOReturn err;
|
||||
err = IOCreatePlugInInterfaceForService(obj.get(), kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
|
||||
&devServ, &score);
|
||||
if (err != kIOReturnSuccess) {
|
||||
fprintf(stderr, "unable to open IOKit plugin interface\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
IUnknownPointer<IOUSBDeviceInterface182> dev;
|
||||
err = devServ.As(&dev, kIOUSBDeviceInterfaceID182);
|
||||
if (err != kIOReturnSuccess) {
|
||||
fprintf(stderr, "unable to open IOKit device interface\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
dev->GetDeviceVendor(dev.storage(), &vid);
|
||||
dev->GetDeviceProduct(dev.storage(), &pid);
|
||||
|
||||
UInt8 vstridx, pstridx;
|
||||
dev->USBGetManufacturerStringIndex(dev.storage(), &vstridx);
|
||||
dev->USBGetProductStringIndex(dev.storage(), &pstridx);
|
||||
|
||||
getUSBStringDescriptor(dev, vstridx, vstr);
|
||||
getUSBStringDescriptor(dev, pstridx, pstr);
|
||||
}
|
||||
|
||||
listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::USB, vid, pid, vstr, pstr, devPath));
|
||||
|
||||
// printf("ADDED %08X %s\n", obj.get(), devPath);
|
||||
}
|
||||
|
||||
static void devicesDisconnectedUSBLL(HIDListenerIOKit* listener,
|
||||
io_iterator_t iterator)
|
||||
{
|
||||
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop)
|
||||
{
|
||||
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{
|
||||
devicesDisconnectedUSBLL(listener, iterator);
|
||||
});
|
||||
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
||||
return;
|
||||
}
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
|
||||
{
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
listener->m_finder._removeToken(devPath);
|
||||
//printf("REMOVED %08X %s\n", obj.get(), devPath);
|
||||
}
|
||||
}
|
||||
|
||||
static void devicesDisconnectedUSBLL(HIDListenerIOKit* listener, io_iterator_t iterator) {
|
||||
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop) {
|
||||
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode,
|
||||
^{ devicesDisconnectedUSBLL(listener, iterator); });
|
||||
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
||||
return;
|
||||
}
|
||||
|
||||
static void devicesConnectedHID(HIDListenerIOKit* listener,
|
||||
io_iterator_t iterator)
|
||||
{
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
|
||||
{
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
|
||||
if (!listener->m_scanningEnabled ||
|
||||
listener->m_finder._hasToken(devPath))
|
||||
continue;
|
||||
|
||||
unsigned vidv, pidv;
|
||||
char vstr[128] = {0};
|
||||
char pstr[128] = {0};
|
||||
{
|
||||
IOCFPluginPointer devServ;
|
||||
SInt32 score;
|
||||
IOReturn err;
|
||||
err = IOCreatePlugInInterfaceForService(obj.get(), kIOHIDDeviceTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
fprintf(stderr, "unable to open IOKit plugin interface\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
IUnknownPointer<IOHIDDeviceDeviceInterface> dev;
|
||||
err = devServ.As(&dev, kIOHIDDeviceDeviceInterfaceID);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
fprintf(stderr, "unable to open IOKit device interface\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Game controllers only */
|
||||
CFPointer<CFNumberRef> usagePage;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsagePageKey), (CFTypeRef*)&usagePage);
|
||||
CFPointer<CFNumberRef> usage;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsageKey), (CFTypeRef*)&usage);
|
||||
int usagePageV, usageV;
|
||||
CFNumberGetValue(usagePage.get(), kCFNumberIntType, &usagePageV);
|
||||
CFNumberGetValue(usage.get(), kCFNumberIntType, &usageV);
|
||||
if (usagePageV == kHIDPage_GenericDesktop)
|
||||
{
|
||||
if (usageV != kHIDUsage_GD_Joystick && usageV != kHIDUsage_GD_GamePad)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CFPointer<CFNumberRef> vid, pid;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDVendorIDKey), (CFTypeRef*)&vid);
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductIDKey), (CFTypeRef*)&pid);
|
||||
CFNumberGetValue(vid.get(), kCFNumberIntType, &vidv);
|
||||
CFNumberGetValue(pid.get(), kCFNumberIntType, &pidv);
|
||||
|
||||
CFPointer<CFStringRef> vstridx, pstridx;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDManufacturerKey), (CFTypeRef*)&vstridx);
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductKey), (CFTypeRef*)&pstridx);
|
||||
|
||||
if (vstridx)
|
||||
CFStringGetCString(vstridx.get(), vstr, 128, kCFStringEncodingUTF8);
|
||||
if (pstridx)
|
||||
CFStringGetCString(pstridx.get(), pstr, 128, kCFStringEncodingUTF8);
|
||||
}
|
||||
|
||||
listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::HID,
|
||||
vidv, pidv, vstr, pstr, devPath));
|
||||
|
||||
//printf("ADDED %08X %s\n", obj, devPath);
|
||||
}
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator)) {
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
listener->m_finder._removeToken(devPath);
|
||||
// printf("REMOVED %08X %s\n", obj.get(), devPath);
|
||||
}
|
||||
}
|
||||
|
||||
static void devicesDisconnectedHID(HIDListenerIOKit* listener,
|
||||
io_iterator_t iterator)
|
||||
{
|
||||
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop)
|
||||
{
|
||||
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{
|
||||
devicesDisconnectedHID(listener, iterator);
|
||||
});
|
||||
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
||||
return;
|
||||
static void devicesConnectedHID(HIDListenerIOKit* listener, io_iterator_t iterator) {
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator)) {
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
|
||||
if (!listener->m_scanningEnabled || listener->m_finder._hasToken(devPath))
|
||||
continue;
|
||||
|
||||
unsigned vidv, pidv;
|
||||
char vstr[128] = {0};
|
||||
char pstr[128] = {0};
|
||||
{
|
||||
IOCFPluginPointer devServ;
|
||||
SInt32 score;
|
||||
IOReturn err;
|
||||
err =
|
||||
IOCreatePlugInInterfaceForService(obj.get(), kIOHIDDeviceTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
|
||||
if (err != kIOReturnSuccess) {
|
||||
fprintf(stderr, "unable to open IOKit plugin interface\n");
|
||||
continue;
|
||||
}
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
|
||||
{
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
listener->m_finder._removeToken(devPath);
|
||||
//printf("REMOVED %08X %s\n", obj, devPath);
|
||||
|
||||
IUnknownPointer<IOHIDDeviceDeviceInterface> dev;
|
||||
err = devServ.As(&dev, kIOHIDDeviceDeviceInterfaceID);
|
||||
if (err != kIOReturnSuccess) {
|
||||
fprintf(stderr, "unable to open IOKit device interface\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Game controllers only */
|
||||
CFPointer<CFNumberRef> usagePage;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsagePageKey), (CFTypeRef*)&usagePage);
|
||||
CFPointer<CFNumberRef> usage;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsageKey), (CFTypeRef*)&usage);
|
||||
int usagePageV, usageV;
|
||||
CFNumberGetValue(usagePage.get(), kCFNumberIntType, &usagePageV);
|
||||
CFNumberGetValue(usage.get(), kCFNumberIntType, &usageV);
|
||||
if (usagePageV == kHIDPage_GenericDesktop) {
|
||||
if (usageV != kHIDUsage_GD_Joystick && usageV != kHIDUsage_GD_GamePad)
|
||||
continue;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFPointer<CFNumberRef> vid, pid;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDVendorIDKey), (CFTypeRef*)&vid);
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductIDKey), (CFTypeRef*)&pid);
|
||||
CFNumberGetValue(vid.get(), kCFNumberIntType, &vidv);
|
||||
CFNumberGetValue(pid.get(), kCFNumberIntType, &pidv);
|
||||
|
||||
CFPointer<CFStringRef> vstridx, pstridx;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDManufacturerKey), (CFTypeRef*)&vstridx);
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductKey), (CFTypeRef*)&pstridx);
|
||||
|
||||
if (vstridx)
|
||||
CFStringGetCString(vstridx.get(), vstr, 128, kCFStringEncodingUTF8);
|
||||
if (pstridx)
|
||||
CFStringGetCString(pstridx.get(), pstr, 128, kCFStringEncodingUTF8);
|
||||
}
|
||||
|
||||
listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::HID, vidv, pidv, vstr, pstr, devPath));
|
||||
|
||||
// printf("ADDED %08X %s\n", obj, devPath);
|
||||
}
|
||||
}
|
||||
|
||||
static void devicesDisconnectedHID(HIDListenerIOKit* listener, io_iterator_t iterator) {
|
||||
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop) {
|
||||
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode,
|
||||
^{ devicesDisconnectedHID(listener, iterator); });
|
||||
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
||||
return;
|
||||
}
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator)) {
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
listener->m_finder._removeToken(devPath);
|
||||
// printf("REMOVED %08X %s\n", obj, devPath);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
HIDListenerIOKit(DeviceFinder& finder)
|
||||
: m_finder(finder)
|
||||
HIDListenerIOKit(DeviceFinder& finder) : m_finder(finder) {
|
||||
struct utsname kernInfo;
|
||||
uname(&kernInfo);
|
||||
int release = atoi(kernInfo.release);
|
||||
m_usbClass = release >= 15 ? "IOUSBHostDevice" : kIOUSBDeviceClassName;
|
||||
|
||||
m_listenerRunLoop = CFRunLoopGetMain();
|
||||
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
|
||||
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
|
||||
m_scanningEnabled = true;
|
||||
|
||||
/* Register HID Matcher */
|
||||
{
|
||||
struct utsname kernInfo;
|
||||
uname(&kernInfo);
|
||||
int release = atoi(kernInfo.release);
|
||||
m_usbClass = release >= 15 ? "IOUSBHostDevice" : kIOUSBDeviceClassName;
|
||||
CFMutableDictionaryRef matchDict = IOServiceMatching("IOHIDDevice");
|
||||
CFRetain(matchDict);
|
||||
|
||||
m_listenerRunLoop = CFRunLoopGetMain();
|
||||
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
|
||||
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
|
||||
m_scanningEnabled = true;
|
||||
kern_return_t hidRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesConnectedHID, this, &m_hidAddNotif);
|
||||
if (hidRet == kIOReturnSuccess)
|
||||
devicesConnectedHID(this, m_hidAddNotif.get());
|
||||
|
||||
/* Register HID Matcher */
|
||||
{
|
||||
CFMutableDictionaryRef matchDict = IOServiceMatching("IOHIDDevice");
|
||||
CFRetain(matchDict);
|
||||
|
||||
kern_return_t hidRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesConnectedHID, this, &m_hidAddNotif);
|
||||
if (hidRet == kIOReturnSuccess)
|
||||
devicesConnectedHID(this, m_hidAddNotif.get());
|
||||
|
||||
hidRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesDisconnectedHID, this, &m_hidRemoveNotif);
|
||||
if (hidRet == kIOReturnSuccess)
|
||||
devicesDisconnectedHID(this, m_hidRemoveNotif.get());
|
||||
}
|
||||
|
||||
/* Register Low-Level USB Matcher */
|
||||
{
|
||||
CFMutableDictionaryRef matchDict = IOServiceMatching(m_usbClass);
|
||||
CFRetain(matchDict);
|
||||
|
||||
kern_return_t llRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif);
|
||||
if (llRet == kIOReturnSuccess)
|
||||
devicesConnectedUSBLL(this, m_llAddNotif.get());
|
||||
|
||||
llRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif);
|
||||
if (llRet == kIOReturnSuccess)
|
||||
devicesDisconnectedUSBLL(this, m_llRemoveNotif.get());
|
||||
}
|
||||
|
||||
m_scanningEnabled = false;
|
||||
hidRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesDisconnectedHID, this, &m_hidRemoveNotif);
|
||||
if (hidRet == kIOReturnSuccess)
|
||||
devicesDisconnectedHID(this, m_hidRemoveNotif.get());
|
||||
}
|
||||
|
||||
~HIDListenerIOKit()
|
||||
|
||||
/* Register Low-Level USB Matcher */
|
||||
{
|
||||
//CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
|
||||
IONotificationPortDestroy(m_llPort);
|
||||
CFMutableDictionaryRef matchDict = IOServiceMatching(m_usbClass);
|
||||
CFRetain(matchDict);
|
||||
|
||||
kern_return_t llRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif);
|
||||
if (llRet == kIOReturnSuccess)
|
||||
devicesConnectedUSBLL(this, m_llAddNotif.get());
|
||||
|
||||
llRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif);
|
||||
if (llRet == kIOReturnSuccess)
|
||||
devicesDisconnectedUSBLL(this, m_llRemoveNotif.get());
|
||||
}
|
||||
|
||||
/* Automatic device scanning */
|
||||
bool startScanning()
|
||||
{
|
||||
m_scanningEnabled = true;
|
||||
return true;
|
||||
|
||||
m_scanningEnabled = false;
|
||||
}
|
||||
|
||||
~HIDListenerIOKit() {
|
||||
// CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
|
||||
IONotificationPortDestroy(m_llPort);
|
||||
}
|
||||
|
||||
/* Automatic device scanning */
|
||||
bool startScanning() {
|
||||
m_scanningEnabled = true;
|
||||
return true;
|
||||
}
|
||||
bool stopScanning() {
|
||||
m_scanningEnabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Manual device scanning */
|
||||
bool scanNow() {
|
||||
IOObjectPointer<io_iterator_t> iter;
|
||||
if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(m_usbClass), &iter) == kIOReturnSuccess) {
|
||||
devicesConnectedUSBLL(this, iter.get());
|
||||
}
|
||||
bool stopScanning()
|
||||
{
|
||||
m_scanningEnabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Manual device scanning */
|
||||
bool scanNow()
|
||||
{
|
||||
IOObjectPointer<io_iterator_t> iter;
|
||||
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
|
||||
IOServiceMatching(m_usbClass), &iter) == kIOReturnSuccess)
|
||||
{
|
||||
devicesConnectedUSBLL(this, iter.get());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder)
|
||||
{
|
||||
return std::make_unique<HIDListenerIOKit>(finder);
|
||||
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) {
|
||||
return std::make_unique<HIDListenerIOKit>(finder);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
#include "boo/inputdev/IHIDListener.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
class HIDListenerNX : public IHIDListener
|
||||
{
|
||||
DeviceFinder& m_finder;
|
||||
class HIDListenerNX : public IHIDListener {
|
||||
DeviceFinder& m_finder;
|
||||
|
||||
public:
|
||||
HIDListenerNX(DeviceFinder& finder)
|
||||
: m_finder(finder)
|
||||
{}
|
||||
HIDListenerNX(DeviceFinder& finder) : m_finder(finder) {}
|
||||
|
||||
bool startScanning() { return false; }
|
||||
bool stopScanning() { return false; }
|
||||
bool scanNow() { return false; }
|
||||
bool startScanning() { return false; }
|
||||
bool stopScanning() { return false; }
|
||||
bool scanNow() { return false; }
|
||||
};
|
||||
|
||||
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder)
|
||||
{
|
||||
return std::make_unique<HIDListenerNX>(finder);
|
||||
}
|
||||
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) { return std::make_unique<HIDListenerNX>(finder); }
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -2,25 +2,20 @@
|
||||
#include "boo/inputdev/IHIDListener.hpp"
|
||||
#include "boo/inputdev/DeviceFinder.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
class HIDListenerUWP : public IHIDListener
|
||||
{
|
||||
class HIDListenerUWP : public IHIDListener {
|
||||
public:
|
||||
HIDListenerUWP(DeviceFinder& finder) {}
|
||||
HIDListenerUWP(DeviceFinder& finder) {}
|
||||
|
||||
/* Automatic device scanning */
|
||||
bool startScanning() { return false; }
|
||||
bool stopScanning() { return false; }
|
||||
/* Automatic device scanning */
|
||||
bool startScanning() { return false; }
|
||||
bool stopScanning() { return false; }
|
||||
|
||||
/* Manual device scanning */
|
||||
bool scanNow() { return false; }
|
||||
/* Manual device scanning */
|
||||
bool scanNow() { return false; }
|
||||
};
|
||||
|
||||
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder)
|
||||
{
|
||||
return std::make_unique<HIDListenerUWP>(finder);
|
||||
}
|
||||
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) { return std::make_unique<HIDListenerUWP>(finder); }
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -12,122 +12,110 @@
|
||||
#include <unistd.h>
|
||||
#include <thread>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
static udev* UDEV_INST = nullptr;
|
||||
udev* GetUdev()
|
||||
{
|
||||
if (!UDEV_INST)
|
||||
UDEV_INST = udev_new();
|
||||
return UDEV_INST;
|
||||
udev* GetUdev() {
|
||||
if (!UDEV_INST)
|
||||
UDEV_INST = udev_new();
|
||||
return UDEV_INST;
|
||||
}
|
||||
|
||||
class HIDListenerUdev final : public IHIDListener
|
||||
{
|
||||
DeviceFinder& m_finder;
|
||||
class HIDListenerUdev final : public IHIDListener {
|
||||
DeviceFinder& m_finder;
|
||||
|
||||
udev_monitor* m_udevMon;
|
||||
std::thread m_udevThread;
|
||||
bool m_scanningEnabled;
|
||||
udev_monitor* m_udevMon;
|
||||
std::thread m_udevThread;
|
||||
bool m_scanningEnabled;
|
||||
|
||||
void deviceConnected(udev_device* device)
|
||||
{
|
||||
if (!m_scanningEnabled)
|
||||
return;
|
||||
void deviceConnected(udev_device* device) {
|
||||
if (!m_scanningEnabled)
|
||||
return;
|
||||
|
||||
/* Prevent redundant registration */
|
||||
const char* devPath = udev_device_get_syspath(device);
|
||||
if (m_finder._hasToken(devPath))
|
||||
return;
|
||||
/* Prevent redundant registration */
|
||||
const char* devPath = udev_device_get_syspath(device);
|
||||
if (m_finder._hasToken(devPath))
|
||||
return;
|
||||
|
||||
/* Filter to USB/BT */
|
||||
const char* dt = udev_device_get_devtype(device);
|
||||
DeviceType type;
|
||||
int vid = 0, pid = 0;
|
||||
const char* manuf = nullptr;
|
||||
const char* product = nullptr;
|
||||
if (dt)
|
||||
{
|
||||
if (!strcmp(dt, "usb_device"))
|
||||
type = DeviceType::USB;
|
||||
else if (!strcmp(dt, "bluetooth_device"))
|
||||
type = DeviceType::Bluetooth;
|
||||
else
|
||||
return;
|
||||
/* Filter to USB/BT */
|
||||
const char* dt = udev_device_get_devtype(device);
|
||||
DeviceType type;
|
||||
int vid = 0, pid = 0;
|
||||
const char* manuf = nullptr;
|
||||
const char* product = nullptr;
|
||||
if (dt) {
|
||||
if (!strcmp(dt, "usb_device"))
|
||||
type = DeviceType::USB;
|
||||
else if (!strcmp(dt, "bluetooth_device"))
|
||||
type = DeviceType::Bluetooth;
|
||||
else
|
||||
return;
|
||||
|
||||
udev_list_entry* attrs = udev_device_get_properties_list_entry(device);
|
||||
udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID");
|
||||
if (vide)
|
||||
vid = strtol(udev_list_entry_get_value(vide), nullptr, 16);
|
||||
udev_list_entry* attrs = udev_device_get_properties_list_entry(device);
|
||||
udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID");
|
||||
if (vide)
|
||||
vid = strtol(udev_list_entry_get_value(vide), nullptr, 16);
|
||||
|
||||
udev_list_entry* pide = udev_list_entry_get_by_name(attrs, "ID_MODEL_ID");
|
||||
if (pide)
|
||||
pid = strtol(udev_list_entry_get_value(pide), nullptr, 16);
|
||||
udev_list_entry* pide = udev_list_entry_get_by_name(attrs, "ID_MODEL_ID");
|
||||
if (pide)
|
||||
pid = strtol(udev_list_entry_get_value(pide), nullptr, 16);
|
||||
|
||||
udev_list_entry* manufe = udev_list_entry_get_by_name(attrs, "ID_VENDOR");
|
||||
if (manufe)
|
||||
manuf = udev_list_entry_get_value(manufe);
|
||||
udev_list_entry* manufe = udev_list_entry_get_by_name(attrs, "ID_VENDOR");
|
||||
if (manufe)
|
||||
manuf = udev_list_entry_get_value(manufe);
|
||||
|
||||
udev_list_entry* producte = udev_list_entry_get_by_name(attrs, "ID_MODEL");
|
||||
if (producte)
|
||||
product = udev_list_entry_get_value(producte);
|
||||
}
|
||||
else if (!strcmp(udev_device_get_subsystem(device), "hidraw"))
|
||||
{
|
||||
type = DeviceType::HID;
|
||||
udev_device* parent = udev_device_get_parent(device);
|
||||
udev_list_entry* attrs = udev_device_get_properties_list_entry(parent);
|
||||
udev_list_entry* producte = udev_list_entry_get_by_name(attrs, "ID_MODEL");
|
||||
if (producte)
|
||||
product = udev_list_entry_get_value(producte);
|
||||
} else if (!strcmp(udev_device_get_subsystem(device), "hidraw")) {
|
||||
type = DeviceType::HID;
|
||||
udev_device* parent = udev_device_get_parent(device);
|
||||
udev_list_entry* attrs = udev_device_get_properties_list_entry(parent);
|
||||
|
||||
udev_list_entry* hidide = udev_list_entry_get_by_name(attrs, "HID_ID");
|
||||
if (hidide)
|
||||
{
|
||||
const char* hidid = udev_list_entry_get_value(hidide);
|
||||
const char* vids = strchr(hidid, ':') + 1;
|
||||
const char* pids = strchr(vids, ':') + 1;
|
||||
vid = strtol(vids, nullptr, 16);
|
||||
pid = strtol(pids, nullptr, 16);
|
||||
}
|
||||
udev_list_entry* hidide = udev_list_entry_get_by_name(attrs, "HID_ID");
|
||||
if (hidide) {
|
||||
const char* hidid = udev_list_entry_get_value(hidide);
|
||||
const char* vids = strchr(hidid, ':') + 1;
|
||||
const char* pids = strchr(vids, ':') + 1;
|
||||
vid = strtol(vids, nullptr, 16);
|
||||
pid = strtol(pids, nullptr, 16);
|
||||
}
|
||||
|
||||
udev_list_entry* hidnamee = udev_list_entry_get_by_name(attrs, "HID_NAME");
|
||||
if (hidnamee)
|
||||
{
|
||||
product = udev_list_entry_get_value(hidnamee);
|
||||
manuf = product;
|
||||
}
|
||||
udev_list_entry* hidnamee = udev_list_entry_get_by_name(attrs, "HID_NAME");
|
||||
if (hidnamee) {
|
||||
product = udev_list_entry_get_value(hidnamee);
|
||||
manuf = product;
|
||||
}
|
||||
|
||||
/* Get device file */
|
||||
const char* dp = udev_device_get_devnode(device);
|
||||
int fd = open(dp, O_RDWR);
|
||||
if (fd < 0)
|
||||
return;
|
||||
/* Get device file */
|
||||
const char* dp = udev_device_get_devnode(device);
|
||||
int fd = open(dp, O_RDWR);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
/* Report descriptor size */
|
||||
int reportDescSize;
|
||||
if (ioctl(fd, HIDIOCGRDESCSIZE, &reportDescSize) == -1)
|
||||
{
|
||||
//const char* err = strerror(errno);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
/* Report descriptor size */
|
||||
int reportDescSize;
|
||||
if (ioctl(fd, HIDIOCGRDESCSIZE, &reportDescSize) == -1) {
|
||||
// const char* err = strerror(errno);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get report descriptor */
|
||||
hidraw_report_descriptor reportDesc;
|
||||
reportDesc.size = reportDescSize;
|
||||
if (ioctl(fd, HIDIOCGRDESC, &reportDesc) == -1)
|
||||
{
|
||||
//const char* err = strerror(errno);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
close(fd);
|
||||
/* Get report descriptor */
|
||||
hidraw_report_descriptor reportDesc;
|
||||
reportDesc.size = reportDescSize;
|
||||
if (ioctl(fd, HIDIOCGRDESC, &reportDesc) == -1) {
|
||||
// const char* err = strerror(errno);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
std::pair<HIDUsagePage, HIDUsage> usage =
|
||||
HIDParser::GetApplicationUsage(reportDesc.value, reportDesc.size);
|
||||
if (usage.first != HIDUsagePage::GenericDesktop ||
|
||||
(usage.second != HIDUsage::Joystick && usage.second != HIDUsage::GamePad))
|
||||
return;
|
||||
}
|
||||
std::pair<HIDUsagePage, HIDUsage> usage = HIDParser::GetApplicationUsage(reportDesc.value, reportDesc.size);
|
||||
if (usage.first != HIDUsagePage::GenericDesktop ||
|
||||
(usage.second != HIDUsage::Joystick && usage.second != HIDUsage::GamePad))
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
udev_list_entry* att = nullptr;
|
||||
@@ -140,120 +128,105 @@ class HIDListenerUdev final : public IHIDListener
|
||||
fprintf(stderr, "\n\n");
|
||||
#endif
|
||||
|
||||
m_finder._insertToken(std::make_unique<DeviceToken>(type, vid, pid, manuf, product, devPath));
|
||||
}
|
||||
m_finder._insertToken(std::make_unique<DeviceToken>(type, vid, pid, manuf, product, devPath));
|
||||
}
|
||||
|
||||
void deviceDisconnected(udev_device* device)
|
||||
{
|
||||
const char* devPath = udev_device_get_syspath(device);
|
||||
m_finder._removeToken(devPath);
|
||||
}
|
||||
void deviceDisconnected(udev_device* device) {
|
||||
const char* devPath = udev_device_get_syspath(device);
|
||||
m_finder._removeToken(devPath);
|
||||
}
|
||||
|
||||
void _udevProc()
|
||||
{
|
||||
logvisor::RegisterThreadName("Boo udev");
|
||||
udev_monitor_enable_receiving(m_udevMon);
|
||||
int fd = udev_monitor_get_fd(m_udevMon);
|
||||
while (true)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
if (pselect(fd+1, &fds, nullptr, nullptr, nullptr, nullptr) < 0)
|
||||
{
|
||||
/* SIGTERM handled here */
|
||||
if (errno == EINTR)
|
||||
break;
|
||||
}
|
||||
int oldtype;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldtype);
|
||||
udev_device* dev = udev_monitor_receive_device(m_udevMon);
|
||||
if (dev)
|
||||
{
|
||||
const char* action = udev_device_get_action(dev);
|
||||
if (!strcmp(action, "add"))
|
||||
deviceConnected(dev);
|
||||
else if (!strcmp(action, "remove"))
|
||||
deviceDisconnected(dev);
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldtype);
|
||||
pthread_testcancel();
|
||||
}
|
||||
void _udevProc() {
|
||||
logvisor::RegisterThreadName("Boo udev");
|
||||
udev_monitor_enable_receiving(m_udevMon);
|
||||
int fd = udev_monitor_get_fd(m_udevMon);
|
||||
while (true) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
if (pselect(fd + 1, &fds, nullptr, nullptr, nullptr, nullptr) < 0) {
|
||||
/* SIGTERM handled here */
|
||||
if (errno == EINTR)
|
||||
break;
|
||||
}
|
||||
int oldtype;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldtype);
|
||||
udev_device* dev = udev_monitor_receive_device(m_udevMon);
|
||||
if (dev) {
|
||||
const char* action = udev_device_get_action(dev);
|
||||
if (!strcmp(action, "add"))
|
||||
deviceConnected(dev);
|
||||
else if (!strcmp(action, "remove"))
|
||||
deviceDisconnected(dev);
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldtype);
|
||||
pthread_testcancel();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
HIDListenerUdev(DeviceFinder& finder)
|
||||
: m_finder(finder)
|
||||
{
|
||||
/* Setup hotplug events */
|
||||
m_udevMon = udev_monitor_new_from_netlink(GetUdev(), "udev");
|
||||
if (!m_udevMon)
|
||||
{
|
||||
fprintf(stderr, "unable to init udev_monitor");
|
||||
abort();
|
||||
}
|
||||
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "usb", "usb_device");
|
||||
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "bluetooth", "bluetooth_device");
|
||||
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "hidraw", nullptr);
|
||||
udev_monitor_filter_update(m_udevMon);
|
||||
|
||||
/* Initial HID Device Add */
|
||||
m_scanningEnabled = true;
|
||||
scanNow();
|
||||
m_scanningEnabled = false;
|
||||
|
||||
/* Start hotplug thread */
|
||||
m_udevThread = std::thread(std::bind(&HIDListenerUdev::_udevProc, this), this);
|
||||
HIDListenerUdev(DeviceFinder& finder) : m_finder(finder) {
|
||||
/* Setup hotplug events */
|
||||
m_udevMon = udev_monitor_new_from_netlink(GetUdev(), "udev");
|
||||
if (!m_udevMon) {
|
||||
fprintf(stderr, "unable to init udev_monitor");
|
||||
abort();
|
||||
}
|
||||
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "usb", "usb_device");
|
||||
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "bluetooth", "bluetooth_device");
|
||||
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "hidraw", nullptr);
|
||||
udev_monitor_filter_update(m_udevMon);
|
||||
|
||||
~HIDListenerUdev()
|
||||
{
|
||||
pthread_cancel(m_udevThread.native_handle());
|
||||
if (m_udevThread.joinable())
|
||||
m_udevThread.join();
|
||||
udev_monitor_unref(m_udevMon);
|
||||
}
|
||||
/* Initial HID Device Add */
|
||||
m_scanningEnabled = true;
|
||||
scanNow();
|
||||
m_scanningEnabled = false;
|
||||
|
||||
/* Automatic device scanning */
|
||||
bool startScanning()
|
||||
{
|
||||
m_scanningEnabled = true;
|
||||
return true;
|
||||
}
|
||||
bool stopScanning()
|
||||
{
|
||||
m_scanningEnabled = false;
|
||||
return true;
|
||||
}
|
||||
/* Start hotplug thread */
|
||||
m_udevThread = std::thread(std::bind(&HIDListenerUdev::_udevProc, this), this);
|
||||
}
|
||||
|
||||
/* Manual device scanning */
|
||||
bool scanNow()
|
||||
{
|
||||
udev_enumerate* uenum = udev_enumerate_new(GetUdev());
|
||||
udev_enumerate_add_match_subsystem(uenum, "usb");
|
||||
udev_enumerate_add_match_subsystem(uenum, "bluetooth");
|
||||
udev_enumerate_add_match_subsystem(uenum, "hidraw");
|
||||
udev_enumerate_scan_devices(uenum);
|
||||
udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum);
|
||||
udev_list_entry* uenumItem;
|
||||
udev_list_entry_foreach(uenumItem, uenumList)
|
||||
{
|
||||
const char* devPath = udev_list_entry_get_name(uenumItem);
|
||||
udev_device* dev = udev_device_new_from_syspath(UDEV_INST, devPath);
|
||||
if (dev)
|
||||
deviceConnected(dev);
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
udev_enumerate_unref(uenum);
|
||||
return true;
|
||||
}
|
||||
~HIDListenerUdev() {
|
||||
pthread_cancel(m_udevThread.native_handle());
|
||||
if (m_udevThread.joinable())
|
||||
m_udevThread.join();
|
||||
udev_monitor_unref(m_udevMon);
|
||||
}
|
||||
|
||||
/* Automatic device scanning */
|
||||
bool startScanning() {
|
||||
m_scanningEnabled = true;
|
||||
return true;
|
||||
}
|
||||
bool stopScanning() {
|
||||
m_scanningEnabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Manual device scanning */
|
||||
bool scanNow() {
|
||||
udev_enumerate* uenum = udev_enumerate_new(GetUdev());
|
||||
udev_enumerate_add_match_subsystem(uenum, "usb");
|
||||
udev_enumerate_add_match_subsystem(uenum, "bluetooth");
|
||||
udev_enumerate_add_match_subsystem(uenum, "hidraw");
|
||||
udev_enumerate_scan_devices(uenum);
|
||||
udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum);
|
||||
udev_list_entry* uenumItem;
|
||||
udev_list_entry_foreach(uenumItem, uenumList) {
|
||||
const char* devPath = udev_list_entry_get_name(uenumItem);
|
||||
udev_device* dev = udev_device_new_from_syspath(UDEV_INST, devPath);
|
||||
if (dev)
|
||||
deviceConnected(dev);
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
udev_enumerate_unref(uenum);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder)
|
||||
{
|
||||
return std::make_unique<HIDListenerUdev>(finder);
|
||||
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) {
|
||||
return std::make_unique<HIDListenerUdev>(finder);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
@@ -18,306 +18,257 @@
|
||||
#include <hidclass.h>
|
||||
#include <Xinput.h>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
class HIDListenerWinUSB final : public IHIDListener
|
||||
{
|
||||
DeviceFinder& m_finder;
|
||||
class HIDListenerWinUSB final : public IHIDListener {
|
||||
DeviceFinder& m_finder;
|
||||
|
||||
bool m_scanningEnabled;
|
||||
bool m_scanningEnabled;
|
||||
|
||||
/*
|
||||
* Reference: https://github.com/pbatard/libwdi/blob/master/libwdi/libwdi.c
|
||||
*/
|
||||
/*
|
||||
* Reference: https://github.com/pbatard/libwdi/blob/master/libwdi/libwdi.c
|
||||
*/
|
||||
|
||||
void _enumerate(DeviceType type, CONST GUID* TypeGUID, const char* pathFilter)
|
||||
{
|
||||
/* Don't ask */
|
||||
static const LPCSTR arPrefix[3] = {"VID_", "PID_", "MI_"};
|
||||
unsigned i, j;
|
||||
CONFIGRET r;
|
||||
ULONG devpropType;
|
||||
DWORD reg_type;
|
||||
HDEVINFO hDevInfo = 0;
|
||||
SP_DEVINFO_DATA DeviceInfoData = {0};
|
||||
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
|
||||
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = {0};
|
||||
DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
|
||||
union {
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_A wtf;
|
||||
CHAR alloc[2048];
|
||||
} DeviceInterfaceDetailData; /* Stack allocation should be fine for this */
|
||||
DeviceInterfaceDetailData.wtf.cbSize = sizeof(DeviceInterfaceDetailData);
|
||||
CHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN];
|
||||
LPSTR pszToken, pszNextToken;
|
||||
CHAR szVid[MAX_DEVICE_ID_LEN], szPid[MAX_DEVICE_ID_LEN], szMi[MAX_DEVICE_ID_LEN];
|
||||
void _enumerate(DeviceType type, CONST GUID* TypeGUID, const char* pathFilter) {
|
||||
/* Don't ask */
|
||||
static const LPCSTR arPrefix[3] = {"VID_", "PID_", "MI_"};
|
||||
unsigned i, j;
|
||||
CONFIGRET r;
|
||||
ULONG devpropType;
|
||||
DWORD reg_type;
|
||||
HDEVINFO hDevInfo = 0;
|
||||
SP_DEVINFO_DATA DeviceInfoData = {0};
|
||||
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
|
||||
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = {0};
|
||||
DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
|
||||
union {
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_A wtf;
|
||||
CHAR alloc[2048];
|
||||
} DeviceInterfaceDetailData; /* Stack allocation should be fine for this */
|
||||
DeviceInterfaceDetailData.wtf.cbSize = sizeof(DeviceInterfaceDetailData);
|
||||
CHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN];
|
||||
LPSTR pszToken, pszNextToken;
|
||||
CHAR szVid[MAX_DEVICE_ID_LEN], szPid[MAX_DEVICE_ID_LEN], szMi[MAX_DEVICE_ID_LEN];
|
||||
|
||||
/* List all connected HID devices */
|
||||
hDevInfo = SetupDiGetClassDevs(NULL, 0, 0, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE);
|
||||
if (hDevInfo == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
/* List all connected HID devices */
|
||||
hDevInfo = SetupDiGetClassDevs(NULL, 0, 0, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE);
|
||||
if (hDevInfo == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
for (i=0 ; ; ++i)
|
||||
{
|
||||
if (!SetupDiEnumDeviceInterfaces(hDevInfo,
|
||||
NULL,
|
||||
TypeGUID,
|
||||
i,
|
||||
&DeviceInterfaceData))
|
||||
break;
|
||||
for (i = 0;; ++i) {
|
||||
if (!SetupDiEnumDeviceInterfaces(hDevInfo, NULL, TypeGUID, i, &DeviceInterfaceData))
|
||||
break;
|
||||
|
||||
DeviceInterfaceDetailData.wtf.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
if (!SetupDiGetDeviceInterfaceDetailA(hDevInfo,
|
||||
&DeviceInterfaceData,
|
||||
&DeviceInterfaceDetailData.wtf,
|
||||
sizeof(DeviceInterfaceDetailData),
|
||||
NULL,
|
||||
&DeviceInfoData))
|
||||
continue;
|
||||
DeviceInterfaceDetailData.wtf.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
if (!SetupDiGetDeviceInterfaceDetailA(hDevInfo, &DeviceInterfaceData, &DeviceInterfaceDetailData.wtf,
|
||||
sizeof(DeviceInterfaceDetailData), NULL, &DeviceInfoData))
|
||||
continue;
|
||||
|
||||
r = CM_Get_Device_IDA(DeviceInfoData.DevInst, szDeviceInstanceID, MAX_PATH, 0);
|
||||
if (r != CR_SUCCESS)
|
||||
continue;
|
||||
r = CM_Get_Device_IDA(DeviceInfoData.DevInst, szDeviceInstanceID, MAX_PATH, 0);
|
||||
if (r != CR_SUCCESS)
|
||||
continue;
|
||||
|
||||
/* Retreive the device description as reported by the device itself */
|
||||
pszToken = strtok_s(szDeviceInstanceID , "\\#&", &pszNextToken);
|
||||
szVid[0] = '\0';
|
||||
szPid[0] = '\0';
|
||||
szMi[0] = '\0';
|
||||
while (pszToken != NULL)
|
||||
{
|
||||
for (j=0 ; j<3 ; ++j)
|
||||
{
|
||||
if (strncmp(pszToken, arPrefix[j], 4) == 0)
|
||||
{
|
||||
switch (j)
|
||||
{
|
||||
case 0:
|
||||
strcpy_s(szVid, MAX_DEVICE_ID_LEN, pszToken);
|
||||
break;
|
||||
case 1:
|
||||
strcpy_s(szPid, MAX_DEVICE_ID_LEN, pszToken);
|
||||
break;
|
||||
case 2:
|
||||
strcpy_s(szMi, MAX_DEVICE_ID_LEN, pszToken);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pszToken = strtok_s(NULL, "\\#&", &pszNextToken);
|
||||
/* Retreive the device description as reported by the device itself */
|
||||
pszToken = strtok_s(szDeviceInstanceID, "\\#&", &pszNextToken);
|
||||
szVid[0] = '\0';
|
||||
szPid[0] = '\0';
|
||||
szMi[0] = '\0';
|
||||
while (pszToken != NULL) {
|
||||
for (j = 0; j < 3; ++j) {
|
||||
if (strncmp(pszToken, arPrefix[j], 4) == 0) {
|
||||
switch (j) {
|
||||
case 0:
|
||||
strcpy_s(szVid, MAX_DEVICE_ID_LEN, pszToken);
|
||||
break;
|
||||
case 1:
|
||||
strcpy_s(szPid, MAX_DEVICE_ID_LEN, pszToken);
|
||||
break;
|
||||
case 2:
|
||||
strcpy_s(szMi, MAX_DEVICE_ID_LEN, pszToken);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!szVid[0] || !szPid[0])
|
||||
continue;
|
||||
|
||||
unsigned vid = strtol(szVid+4, NULL, 16);
|
||||
unsigned pid = strtol(szPid+4, NULL, 16);
|
||||
|
||||
CHAR productW[1024] = {0};
|
||||
//CHAR product[1024] = {0};
|
||||
DWORD productSz = 0;
|
||||
if (!SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc,
|
||||
&devpropType, (BYTE*)productW, 1024, &productSz, 0)) {
|
||||
/* fallback to SPDRP_DEVICEDESC (USB hubs still use it) */
|
||||
SetupDiGetDeviceRegistryPropertyA(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC,
|
||||
®_type, (BYTE*)productW, 1024, &productSz);
|
||||
}
|
||||
/* DAFUQ??? Why isn't this really WCHAR??? */
|
||||
//WideCharToMultiByte(CP_UTF8, 0, productW, -1, product, 1024, nullptr, nullptr);
|
||||
|
||||
WCHAR manufW[1024] = L"Someone"; /* Windows Vista and earlier will use this as the vendor */
|
||||
CHAR manuf[1024] = {0};
|
||||
DWORD manufSz = 0;
|
||||
SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_Manufacturer,
|
||||
&devpropType, (BYTE*)manufW, 1024, &manufSz, 0);
|
||||
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) */
|
||||
CharUpperA(DeviceInterfaceDetailData.wtf.DevicePath);
|
||||
|
||||
/* Filter to specific device (provided by hotplug event) */
|
||||
if (pathFilter && strcmp(pathFilter, DeviceInterfaceDetailData.wtf.DevicePath))
|
||||
continue;
|
||||
|
||||
if (!m_scanningEnabled || m_finder._hasToken(DeviceInterfaceDetailData.wtf.DevicePath))
|
||||
continue;
|
||||
|
||||
/* Whew!! that's a single device enumerated!! */
|
||||
m_finder._insertToken(std::make_unique<DeviceToken>(
|
||||
type, vid, pid, manuf, productW,
|
||||
DeviceInterfaceDetailData.wtf.DevicePath));
|
||||
}
|
||||
}
|
||||
pszToken = strtok_s(NULL, "\\#&", &pszNextToken);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||
}
|
||||
if (!szVid[0] || !szPid[0])
|
||||
continue;
|
||||
|
||||
void _pollDevices(const char* pathFilter)
|
||||
{
|
||||
_enumerate(DeviceType::HID, &GUID_DEVINTERFACE_HID, pathFilter);
|
||||
_enumerate(DeviceType::USB, &GUID_DEVINTERFACE_USB_DEVICE, pathFilter);
|
||||
}
|
||||
unsigned vid = strtol(szVid + 4, NULL, 16);
|
||||
unsigned pid = strtol(szPid + 4, NULL, 16);
|
||||
|
||||
static XInputPadState ConvertXInputState(const XINPUT_GAMEPAD& pad)
|
||||
{
|
||||
return {pad.wButtons, pad.bLeftTrigger, pad.bRightTrigger,
|
||||
pad.sThumbLX, pad.sThumbLY, pad.sThumbLY, pad.sThumbRY};
|
||||
}
|
||||
CHAR productW[1024] = {0};
|
||||
// CHAR product[1024] = {0};
|
||||
DWORD productSz = 0;
|
||||
if (!SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devpropType,
|
||||
(BYTE*)productW, 1024, &productSz, 0)) {
|
||||
/* fallback to SPDRP_DEVICEDESC (USB hubs still use it) */
|
||||
SetupDiGetDeviceRegistryPropertyA(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC, ®_type, (BYTE*)productW, 1024,
|
||||
&productSz);
|
||||
}
|
||||
/* DAFUQ??? Why isn't this really WCHAR??? */
|
||||
// WideCharToMultiByte(CP_UTF8, 0, productW, -1, product, 1024, nullptr, nullptr);
|
||||
|
||||
std::thread m_xinputThread;
|
||||
bool m_xinputRunning = true;
|
||||
DWORD m_xinputPackets[4] = {DWORD(-1), DWORD(-1), DWORD(-1), DWORD(-1)};
|
||||
std::vector<DeviceToken> m_xinputTokens;
|
||||
void _xinputProc()
|
||||
{
|
||||
m_xinputTokens.reserve(4);
|
||||
for (int i=0 ; i<4 ; ++i)
|
||||
m_xinputTokens.emplace_back(DeviceType::XInput, 0, i, "", "", "");
|
||||
WCHAR manufW[1024] = L"Someone"; /* Windows Vista and earlier will use this as the vendor */
|
||||
CHAR manuf[1024] = {0};
|
||||
DWORD manufSz = 0;
|
||||
SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_Manufacturer, &devpropType, (BYTE*)manufW,
|
||||
1024, &manufSz, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, manufW, -1, manuf, 1024, nullptr, nullptr);
|
||||
|
||||
while (m_xinputRunning)
|
||||
{
|
||||
for (int i=0 ; i<4 ; ++i)
|
||||
{
|
||||
DeviceToken& tok = m_xinputTokens[i];
|
||||
XINPUT_STATE state;
|
||||
if (XInputGetState(i, &state) == ERROR_SUCCESS)
|
||||
{
|
||||
if (state.dwPacketNumber != m_xinputPackets[i])
|
||||
{
|
||||
if (m_xinputPackets[i] == -1)
|
||||
m_finder.deviceConnected(tok);
|
||||
m_xinputPackets[i] = state.dwPacketNumber;
|
||||
if (tok.m_connectedDev)
|
||||
{
|
||||
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
|
||||
std::lock_guard<std::mutex> lk(pad.m_callbackLock);
|
||||
if (pad.m_callback)
|
||||
pad.m_callback->controllerUpdate(pad, ConvertXInputState(state.Gamepad));
|
||||
}
|
||||
}
|
||||
if (tok.m_connectedDev)
|
||||
{
|
||||
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
|
||||
if (pad.m_rumbleRequest[0] != pad.m_rumbleState[0] ||
|
||||
pad.m_rumbleRequest[1] != pad.m_rumbleState[1])
|
||||
{
|
||||
pad.m_rumbleState[0] = pad.m_rumbleRequest[0];
|
||||
pad.m_rumbleState[1] = pad.m_rumbleRequest[1];
|
||||
XINPUT_VIBRATION vibe = {pad.m_rumbleRequest[0], pad.m_rumbleRequest[1]};
|
||||
XInputSetState(i, &vibe);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_xinputPackets[i] != -1)
|
||||
{
|
||||
m_xinputPackets[i] = -1;
|
||||
if (tok.m_connectedDev)
|
||||
{
|
||||
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
|
||||
pad.deviceDisconnected();
|
||||
}
|
||||
m_finder.deviceDisconnected(tok, tok.m_connectedDev.get());
|
||||
}
|
||||
}
|
||||
Sleep(10);
|
||||
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) */
|
||||
CharUpperA(DeviceInterfaceDetailData.wtf.DevicePath);
|
||||
|
||||
/* Filter to specific device (provided by hotplug event) */
|
||||
if (pathFilter && strcmp(pathFilter, DeviceInterfaceDetailData.wtf.DevicePath))
|
||||
continue;
|
||||
|
||||
if (!m_scanningEnabled || m_finder._hasToken(DeviceInterfaceDetailData.wtf.DevicePath))
|
||||
continue;
|
||||
|
||||
/* Whew!! that's a single device enumerated!! */
|
||||
m_finder._insertToken(
|
||||
std::make_unique<DeviceToken>(type, vid, pid, manuf, productW, DeviceInterfaceDetailData.wtf.DevicePath));
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||
}
|
||||
|
||||
void _pollDevices(const char* pathFilter) {
|
||||
_enumerate(DeviceType::HID, &GUID_DEVINTERFACE_HID, pathFilter);
|
||||
_enumerate(DeviceType::USB, &GUID_DEVINTERFACE_USB_DEVICE, pathFilter);
|
||||
}
|
||||
|
||||
static XInputPadState ConvertXInputState(const XINPUT_GAMEPAD& pad) {
|
||||
return {pad.wButtons, pad.bLeftTrigger, pad.bRightTrigger, pad.sThumbLX, pad.sThumbLY, pad.sThumbLY, pad.sThumbRY};
|
||||
}
|
||||
|
||||
std::thread m_xinputThread;
|
||||
bool m_xinputRunning = true;
|
||||
DWORD m_xinputPackets[4] = {DWORD(-1), DWORD(-1), DWORD(-1), DWORD(-1)};
|
||||
std::vector<DeviceToken> m_xinputTokens;
|
||||
void _xinputProc() {
|
||||
m_xinputTokens.reserve(4);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
m_xinputTokens.emplace_back(DeviceType::XInput, 0, i, "", "", "");
|
||||
|
||||
while (m_xinputRunning) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
DeviceToken& tok = m_xinputTokens[i];
|
||||
XINPUT_STATE state;
|
||||
if (XInputGetState(i, &state) == ERROR_SUCCESS) {
|
||||
if (state.dwPacketNumber != m_xinputPackets[i]) {
|
||||
if (m_xinputPackets[i] == -1)
|
||||
m_finder.deviceConnected(tok);
|
||||
m_xinputPackets[i] = state.dwPacketNumber;
|
||||
if (tok.m_connectedDev) {
|
||||
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
|
||||
std::lock_guard<std::mutex> lk(pad.m_callbackLock);
|
||||
if (pad.m_callback)
|
||||
pad.m_callback->controllerUpdate(pad, ConvertXInputState(state.Gamepad));
|
||||
}
|
||||
}
|
||||
if (tok.m_connectedDev) {
|
||||
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
|
||||
if (pad.m_rumbleRequest[0] != pad.m_rumbleState[0] || pad.m_rumbleRequest[1] != pad.m_rumbleState[1]) {
|
||||
pad.m_rumbleState[0] = pad.m_rumbleRequest[0];
|
||||
pad.m_rumbleState[1] = pad.m_rumbleRequest[1];
|
||||
XINPUT_VIBRATION vibe = {pad.m_rumbleRequest[0], pad.m_rumbleRequest[1]};
|
||||
XInputSetState(i, &vibe);
|
||||
}
|
||||
}
|
||||
} else if (m_xinputPackets[i] != -1) {
|
||||
m_xinputPackets[i] = -1;
|
||||
if (tok.m_connectedDev) {
|
||||
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
|
||||
pad.deviceDisconnected();
|
||||
}
|
||||
m_finder.deviceDisconnected(tok, tok.m_connectedDev.get());
|
||||
}
|
||||
}
|
||||
Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
HIDListenerWinUSB(DeviceFinder& finder)
|
||||
: m_finder(finder)
|
||||
{
|
||||
/* Initial HID Device Add */
|
||||
_pollDevices(nullptr);
|
||||
HIDListenerWinUSB(DeviceFinder& finder) : m_finder(finder) {
|
||||
/* Initial HID Device Add */
|
||||
_pollDevices(nullptr);
|
||||
|
||||
/* XInput arbitration thread */
|
||||
for (const DeviceSignature* sig : m_finder.getTypes())
|
||||
{
|
||||
if (sig->m_type == DeviceType::XInput)
|
||||
{
|
||||
m_xinputThread = std::thread(std::bind(&HIDListenerWinUSB::_xinputProc, this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* XInput arbitration thread */
|
||||
for (const DeviceSignature* sig : m_finder.getTypes()) {
|
||||
if (sig->m_type == DeviceType::XInput) {
|
||||
m_xinputThread = std::thread(std::bind(&HIDListenerWinUSB::_xinputProc, this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~HIDListenerWinUSB()
|
||||
{
|
||||
m_xinputRunning = false;
|
||||
if (m_xinputThread.joinable())
|
||||
m_xinputThread.join();
|
||||
}
|
||||
~HIDListenerWinUSB() {
|
||||
m_xinputRunning = false;
|
||||
if (m_xinputThread.joinable())
|
||||
m_xinputThread.join();
|
||||
}
|
||||
|
||||
/* Automatic device scanning */
|
||||
bool startScanning()
|
||||
{
|
||||
m_scanningEnabled = true;
|
||||
return true;
|
||||
}
|
||||
bool stopScanning()
|
||||
{
|
||||
m_scanningEnabled = false;
|
||||
return true;
|
||||
}
|
||||
/* Automatic device scanning */
|
||||
bool startScanning() {
|
||||
m_scanningEnabled = true;
|
||||
return true;
|
||||
}
|
||||
bool stopScanning() {
|
||||
m_scanningEnabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Manual device scanning */
|
||||
bool scanNow()
|
||||
{
|
||||
_pollDevices(nullptr);
|
||||
return true;
|
||||
}
|
||||
/* Manual device scanning */
|
||||
bool scanNow() {
|
||||
_pollDevices(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _extDevConnect(const char* path)
|
||||
{
|
||||
char upperPath[1024];
|
||||
strcpy_s(upperPath, 1024, path);
|
||||
CharUpperA(upperPath);
|
||||
if (m_scanningEnabled && !m_finder._hasToken(upperPath))
|
||||
_pollDevices(upperPath);
|
||||
return true;
|
||||
}
|
||||
bool _extDevConnect(const char* path) {
|
||||
char upperPath[1024];
|
||||
strcpy_s(upperPath, 1024, path);
|
||||
CharUpperA(upperPath);
|
||||
if (m_scanningEnabled && !m_finder._hasToken(upperPath))
|
||||
_pollDevices(upperPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _extDevDisconnect(const char* path)
|
||||
{
|
||||
char upperPath[1024];
|
||||
strcpy_s(upperPath, 1024, path);
|
||||
CharUpperA(upperPath);
|
||||
m_finder._removeToken(upperPath);
|
||||
return true;
|
||||
}
|
||||
bool _extDevDisconnect(const char* path) {
|
||||
char upperPath[1024];
|
||||
strcpy_s(upperPath, 1024, path);
|
||||
CharUpperA(upperPath);
|
||||
m_finder._removeToken(upperPath);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder)
|
||||
{
|
||||
return std::make_unique<HIDListenerWinUSB>(finder);
|
||||
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) {
|
||||
return std::make_unique<HIDListenerWinUSB>(finder);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,29 +8,27 @@
|
||||
#include <hidsdi.h>
|
||||
#endif
|
||||
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
|
||||
class IHIDDevice : public std::enable_shared_from_this<IHIDDevice>
|
||||
{
|
||||
friend class DeviceBase;
|
||||
friend struct DeviceSignature;
|
||||
virtual void _deviceDisconnected()=0;
|
||||
virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0;
|
||||
virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)=0;
|
||||
class IHIDDevice : public std::enable_shared_from_this<IHIDDevice> {
|
||||
friend class DeviceBase;
|
||||
friend struct DeviceSignature;
|
||||
virtual void _deviceDisconnected() = 0;
|
||||
virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) = 0;
|
||||
virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) = 0;
|
||||
#if _WIN32
|
||||
#if !WINDOWS_STORE
|
||||
virtual const PHIDP_PREPARSED_DATA _getReportDescriptor()=0;
|
||||
virtual const PHIDP_PREPARSED_DATA _getReportDescriptor() = 0;
|
||||
#endif
|
||||
#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 size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
|
||||
virtual void _startThread()=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 void _startThread() = 0;
|
||||
|
||||
public:
|
||||
virtual ~IHIDDevice() = default;
|
||||
virtual ~IHIDDevice() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
} // namespace boo
|
||||
|
||||
@@ -7,113 +7,97 @@
|
||||
#include <utility>
|
||||
|
||||
/// A smart pointer that can manage the lifecycle of IOKit objects.
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
class IOObjectPointer {
|
||||
public:
|
||||
IOObjectPointer() : storage(0) { }
|
||||
IOObjectPointer() : storage(0) {}
|
||||
|
||||
IOObjectPointer(T pointer) : storage(toStorageType(pointer)) {
|
||||
if (storage) {
|
||||
IOObjectRetain(storage);
|
||||
}
|
||||
IOObjectPointer(T pointer) : storage(toStorageType(pointer)) {
|
||||
if (storage) {
|
||||
IOObjectRetain(storage);
|
||||
}
|
||||
}
|
||||
|
||||
IOObjectPointer(const IOObjectPointer & other) : storage(other.storage) {
|
||||
if (io_object_t ptr = storage) {
|
||||
IOObjectRetain(ptr);
|
||||
}
|
||||
IOObjectPointer(const IOObjectPointer& other) : storage(other.storage) {
|
||||
if (io_object_t ptr = storage) {
|
||||
IOObjectRetain(ptr);
|
||||
}
|
||||
IOObjectPointer& operator=(const IOObjectPointer & other) {
|
||||
if (io_object_t pointer = storage) {
|
||||
IOObjectRelease(pointer);
|
||||
}
|
||||
storage = other.storage;
|
||||
if (io_object_t ptr = storage) {
|
||||
IOObjectRetain(ptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
IOObjectPointer& operator=(const IOObjectPointer& other) {
|
||||
if (io_object_t pointer = storage) {
|
||||
IOObjectRelease(pointer);
|
||||
}
|
||||
storage = other.storage;
|
||||
if (io_object_t ptr = storage) {
|
||||
IOObjectRetain(ptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
IOObjectPointer(IOObjectPointer && other) : storage(std::exchange(other.storage, 0)) { }
|
||||
IOObjectPointer(IOObjectPointer&& other) : storage(std::exchange(other.storage, 0)) {}
|
||||
|
||||
~IOObjectPointer() {
|
||||
if (io_object_t pointer = storage) {
|
||||
IOObjectRelease(pointer);
|
||||
}
|
||||
~IOObjectPointer() {
|
||||
if (io_object_t pointer = storage) {
|
||||
IOObjectRelease(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
static inline IOObjectPointer<T> adopt(T ptr) {
|
||||
return IOObjectPointer<T>(ptr, IOObjectPointer<T>::Adopt);
|
||||
}
|
||||
static inline IOObjectPointer<T> adopt(T ptr) { return IOObjectPointer<T>(ptr, IOObjectPointer<T>::Adopt); }
|
||||
|
||||
T get() const {
|
||||
return fromStorageType(storage);
|
||||
T get() const { return fromStorageType(storage); }
|
||||
io_object_t* operator&() {
|
||||
if (io_object_t pointer = storage) {
|
||||
IOObjectRelease(pointer);
|
||||
}
|
||||
io_object_t* operator&()
|
||||
{
|
||||
if (io_object_t pointer = storage) {
|
||||
IOObjectRelease(pointer);
|
||||
}
|
||||
return &storage;
|
||||
}
|
||||
operator bool() const { return storage != 0; }
|
||||
return &storage;
|
||||
}
|
||||
operator bool() const { return storage != 0; }
|
||||
|
||||
private:
|
||||
io_object_t storage;
|
||||
io_object_t storage;
|
||||
|
||||
enum AdoptTag { Adopt };
|
||||
IOObjectPointer(T ptr, AdoptTag) : storage(toStorageType(ptr)) { }
|
||||
enum AdoptTag { Adopt };
|
||||
IOObjectPointer(T ptr, AdoptTag) : storage(toStorageType(ptr)) {}
|
||||
|
||||
inline io_object_t toStorageType(io_object_t ptr) const {
|
||||
return (io_object_t)ptr;
|
||||
}
|
||||
inline io_object_t toStorageType(io_object_t ptr) const { return (io_object_t)ptr; }
|
||||
|
||||
inline T fromStorageType(io_object_t pointer) const {
|
||||
return (T)pointer;
|
||||
}
|
||||
inline T fromStorageType(io_object_t pointer) const { return (T)pointer; }
|
||||
|
||||
void swap(IOObjectPointer &other) {
|
||||
std::swap(storage, other.storage);
|
||||
}
|
||||
void swap(IOObjectPointer& other) { std::swap(storage, other.storage); }
|
||||
};
|
||||
|
||||
/// A smart pointer that can manage the lifecycle of IOKit plugin objects.
|
||||
class IOCFPluginPointer {
|
||||
public:
|
||||
IOCFPluginPointer() : _storage(nullptr) { }
|
||||
IOCFPluginPointer() : _storage(nullptr) {}
|
||||
|
||||
IOCFPluginPointer(const IOCFPluginPointer & other) = delete;
|
||||
IOCFPluginPointer(const IOCFPluginPointer& other) = delete;
|
||||
|
||||
IOCFPluginPointer(IOCFPluginPointer && other) : _storage(std::exchange(other._storage, nullptr)) { }
|
||||
IOCFPluginPointer(IOCFPluginPointer&& other) : _storage(std::exchange(other._storage, nullptr)) {}
|
||||
|
||||
~IOCFPluginPointer() {
|
||||
if (IOCFPlugInInterface** pointer = _storage) {
|
||||
IODestroyPlugInInterface(pointer);
|
||||
}
|
||||
~IOCFPluginPointer() {
|
||||
if (IOCFPlugInInterface** pointer = _storage) {
|
||||
IODestroyPlugInInterface(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
IOCFPlugInInterface*** operator&()
|
||||
{
|
||||
if (IOCFPlugInInterface** pointer = _storage) {
|
||||
IODestroyPlugInInterface(pointer);
|
||||
}
|
||||
return &_storage;
|
||||
IOCFPlugInInterface*** operator&() {
|
||||
if (IOCFPlugInInterface** pointer = _storage) {
|
||||
IODestroyPlugInInterface(pointer);
|
||||
}
|
||||
return &_storage;
|
||||
}
|
||||
|
||||
HRESULT As(LPVOID* p, CFUUIDRef uuid) const
|
||||
{
|
||||
(*_storage)->AddRef(_storage); // Needed for some reason
|
||||
return (*_storage)->QueryInterface(_storage, CFUUIDGetUUIDBytes(uuid), p);
|
||||
}
|
||||
HRESULT As(LPVOID* p, CFUUIDRef uuid) const {
|
||||
(*_storage)->AddRef(_storage); // Needed for some reason
|
||||
return (*_storage)->QueryInterface(_storage, CFUUIDGetUUIDBytes(uuid), p);
|
||||
}
|
||||
|
||||
operator bool() const { return _storage != nullptr; }
|
||||
operator bool() const { return _storage != nullptr; }
|
||||
|
||||
IOCFPlugInInterface** storage() const { return _storage; }
|
||||
IOCFPlugInInterface** storage() const { return _storage; }
|
||||
|
||||
private:
|
||||
IOCFPlugInInterface** _storage;
|
||||
void swap(IOCFPluginPointer &other) {
|
||||
std::swap(_storage, other._storage);
|
||||
}
|
||||
IOCFPlugInInterface** _storage;
|
||||
void swap(IOCFPluginPointer& other) { std::swap(_storage, other._storage); }
|
||||
};
|
||||
|
||||
|
||||
@@ -1,56 +1,44 @@
|
||||
#include "boo/inputdev/NintendoPowerA.hpp"
|
||||
#include "boo/inputdev/DeviceSignature.hpp"
|
||||
#include <memory.h>
|
||||
namespace boo
|
||||
{
|
||||
namespace boo {
|
||||
NintendoPowerA::NintendoPowerA(DeviceToken* token)
|
||||
: TDeviceBase<INintendoPowerACallback>(dev_typeid(NintendoPowerA), token)
|
||||
{
|
||||
: TDeviceBase<INintendoPowerACallback>(dev_typeid(NintendoPowerA), token) {}
|
||||
|
||||
}
|
||||
NintendoPowerA::~NintendoPowerA() {}
|
||||
|
||||
NintendoPowerA::~NintendoPowerA()
|
||||
{
|
||||
}
|
||||
|
||||
void NintendoPowerA::deviceDisconnected()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerDisconnected();
|
||||
void NintendoPowerA::deviceDisconnected() {
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (m_callback)
|
||||
m_callback->controllerDisconnected();
|
||||
}
|
||||
|
||||
void NintendoPowerA::initialCycle() {}
|
||||
|
||||
void NintendoPowerA::transferCycle()
|
||||
{
|
||||
uint8_t payload[8];
|
||||
size_t recvSz = receiveUSBInterruptTransfer(payload, sizeof(payload));
|
||||
if (recvSz != 8)
|
||||
return;
|
||||
void NintendoPowerA::transferCycle() {
|
||||
uint8_t payload[8];
|
||||
size_t recvSz = receiveUSBInterruptTransfer(payload, sizeof(payload));
|
||||
if (recvSz != 8)
|
||||
return;
|
||||
|
||||
NintendoPowerAState state = *reinterpret_cast<NintendoPowerAState*>(&payload);
|
||||
NintendoPowerAState state = *reinterpret_cast<NintendoPowerAState*>(&payload);
|
||||
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (state != m_last && m_callback)
|
||||
m_callback->controllerUpdate(state);
|
||||
m_last = state;
|
||||
std::lock_guard<std::mutex> lk(m_callbackLock);
|
||||
if (state != m_last && m_callback)
|
||||
m_callback->controllerUpdate(state);
|
||||
m_last = state;
|
||||
}
|
||||
|
||||
void NintendoPowerA::finalCycle() {}
|
||||
|
||||
void NintendoPowerA::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
void NintendoPowerA::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {}
|
||||
|
||||
bool NintendoPowerAState::operator==(const NintendoPowerAState& other) {
|
||||
return !memcmp(this, &other, sizeof(NintendoPowerAState));
|
||||
}
|
||||
|
||||
bool NintendoPowerAState::operator==(const NintendoPowerAState &other)
|
||||
{
|
||||
return !memcmp(this, &other, sizeof(NintendoPowerAState));
|
||||
bool NintendoPowerAState::operator!=(const NintendoPowerAState& other) {
|
||||
return memcmp(this, &other, sizeof(NintendoPowerAState));
|
||||
}
|
||||
|
||||
bool NintendoPowerAState::operator!=(const NintendoPowerAState &other)
|
||||
{
|
||||
return memcmp(this, &other, sizeof(NintendoPowerAState));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace boo
|
||||
|
||||
Reference in New Issue
Block a user