Merge branch 'master' of https://github.com/RetroView/libBoo into ds3

This commit is contained in:
2015-08-18 16:32:19 -07:00
73 changed files with 3748 additions and 943 deletions

View File

@@ -0,0 +1 @@
#include "boo/inputdev/CafeProPad.hpp"

View File

@@ -0,0 +1,73 @@
#include "boo/inputdev/DeviceBase.hpp"
#include "boo/inputdev/DeviceToken.hpp"
#include "IHIDDevice.hpp"
#include <cstdarg>
namespace boo
{
DeviceBase::DeviceBase(DeviceToken* token)
: m_token(token), m_hidDev(NULL)
{
}
DeviceBase::~DeviceBase()
{
delete m_hidDev;
}
void DeviceBase::_deviceDisconnected()
{
deviceDisconnected();
m_token = NULL;
if (m_hidDev)
{
m_hidDev->_deviceDisconnected();
delete m_hidDev;
m_hidDev = NULL;
}
}
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);
}
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;
}
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, uint16_t message)
{
if (m_hidDev)
return m_hidDev->_sendHIDReport(data, length, message);
return false;
}
size_t DeviceBase::receiveReport(uint8_t* data, size_t length, uint16_t message)
{
if (m_hidDev)
return m_hidDev->_recieveReport(data, length, message);
return false;
}
}

View File

@@ -0,0 +1,78 @@
#include "boo/inputdev/DeviceSignature.hpp"
#include "boo/inputdev/DeviceToken.hpp"
#include "boo/inputdev/GenericPad.hpp"
#include "IHIDDevice.hpp"
namespace boo
{
extern const DeviceSignature BOO_DEVICE_SIGS[];
bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSignatureSet& sigSet)
{
if (token.getDeviceType() == DeviceToken::DEVTYPE_GENERICHID)
return true;
for (const DeviceSignature* sig : sigSet)
{
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId())
return true;
}
return false;
}
IHIDDevice* IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp);
DeviceBase* DeviceSignature::DeviceNew(DeviceToken& token)
{
DeviceBase* retval = NULL;
/* Early-return for generic HID devices */
if (token.getDeviceType() == DeviceToken::DEVTYPE_GENERICHID)
{
retval = new GenericPad(&token);
if (!retval)
return NULL;
IHIDDevice* newDev = IHIDDeviceNew(token, *retval);
if (!newDev)
{
delete retval;
return NULL;
}
return retval;
}
/* Otherwise perform signature-matching to find the appropriate device-factory */
const DeviceSignature* foundSig = NULL;
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;
}
if (!foundSig)
return NULL;
retval = foundSig->m_factory(&token);
if (!retval)
return NULL;
IHIDDevice* newDev = IHIDDeviceNew(token, *retval);
if (!newDev)
{
delete retval;
return NULL;
}
return retval;
}
}

View File

@@ -0,0 +1,123 @@
#include "boo/inputdev/DolphinSmashAdapter.hpp"
#include <stdio.h>
#include <string.h>
namespace boo
{
/*
* Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c
*/
DolphinSmashAdapter::DolphinSmashAdapter(DeviceToken* token)
: DeviceBase(token),
m_callback(NULL),
m_knownControllers(0),
m_rumbleRequest(0),
m_rumbleState(0)
{
}
DolphinSmashAdapter::~DolphinSmashAdapter()
{
}
static inline EDolphinControllerType parseType(unsigned char status)
{
unsigned char type = status & (DOL_TYPE_NORMAL | DOL_TYPE_WAVEBIRD);
switch (type)
{
case DOL_TYPE_NORMAL:
case DOL_TYPE_WAVEBIRD:
return (EDolphinControllerType)type;
default:
return DOL_TYPE_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];
return type;
}
void DolphinSmashAdapter::initialCycle()
{
uint8_t handshakePayload[] = {0x13};
sendUSBInterruptTransfer(handshakePayload, sizeof(handshakePayload));
}
void DolphinSmashAdapter::transferCycle()
{
uint8_t payload[37];
size_t recvSz = receiveUSBInterruptTransfer(payload, sizeof(payload));
if (recvSz != 37 || payload[0] != 0x21)
return;
//printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]);
if (!m_callback)
return;
/* Parse controller states */
uint8_t* controller = &payload[1];
uint8_t rumbleMask = 0;
for (int i=0 ; i<4 ; i++, controller += 9)
{
DolphinControllerState state;
bool rumble = false;
EDolphinControllerType type = parseState(&state, controller, rumble);
if (type && !(m_knownControllers & 1<<i))
{
m_knownControllers |= 1<<i;
m_callback->controllerConnected(i, type);
}
else if (!type && (m_knownControllers & 1<<i))
{
m_knownControllers &= ~(1<<i);
m_callback->controllerDisconnected(i, type);
}
if (m_knownControllers & 1<<i)
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)
rumbleMessage[i+1] = (rumbleReq & 1<<i);
sendUSBInterruptTransfer(rumbleMessage, sizeof(rumbleMessage));
m_rumbleState = rumbleReq;
}
}
void DolphinSmashAdapter::finalCycle()
{
uint8_t rumbleMessage[5] = {0x11, 0, 0, 0, 0};
sendUSBInterruptTransfer(rumbleMessage, sizeof(rumbleMessage));
}
void DolphinSmashAdapter::deviceDisconnected()
{
}
}

View File

@@ -0,0 +1,138 @@
#include "boo/inputdev/DualshockPad.hpp"
#include <math.h>
#include <iostream>
#include <stdio.h>
#include <endian.h>
#include <memory.h>
#define RAD_TO_DEG (180.0/M_PI)
void hexdump(void *ptr, int buflen) {
unsigned char *buf = (unsigned char*)ptr;
int i, j;
for (i=0; i<buflen; i+=16) {
printf("%06x: ", i);
for (j=0; j<16; j++)
if (i+j < buflen)
printf("%02x ", buf[i+j]);
else
printf(" ");
printf(" ");
for (j=0; j<16; j++)
if (i+j < buflen)
printf("%c", isprint(buf[i+j]) ? buf[i+j] : '.');
printf("\n");
}
}
namespace boo
{
static const uint8_t defaultReport[35] = {
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)
: DeviceBase(token),
m_callback(nullptr),
m_rumbleRequest(0),
m_rumbleState(0)
{
memcpy(m_report.buf, defaultReport, 35);
}
DualshockPad::~DualshockPad()
{
}
void DualshockPad::deviceDisconnected()
{
if (m_callback)
m_callback->controllerDisconnected();
}
void DualshockPad::initialCycle()
{
uint8_t setupCommand[4] = {0x42, 0x0c, 0x00, 0x00}; //Tells controller to start sending changes on in pipe
if (!sendHIDReport(setupCommand, sizeof(setupCommand), 0x03F4))
{
deviceError("Unable to send complete packet! Request size %x\n", sizeof(setupCommand));
return;
}
uint8_t btAddr[8];
receiveReport(btAddr, sizeof(btAddr), 0x03F5);
for (int i = 0; i < 6; i++)
m_btAddress[5 - i] = btAddr[i + 2]; // Copy into buffer reversed, so it is LSB first
}
void DualshockPad::transferCycle()
{
DualshockControllerState state;
size_t recvSz = receiveUSBInterruptTransfer((uint8_t*)&state, 49);
if (recvSz != 49)
return;
for (int i = 0; i < 3; i++)
state.m_accelerometer[i] = be16toh(state.m_accelerometer[i]);
state.m_gyrometerZ = be16toh(state.m_gyrometerZ);
if (m_callback)
m_callback->controllerUpdate(state);
if (m_rumbleRequest != m_rumbleState)
{
if (m_rumbleRequest & DS3_MOTOR_LEFT)
{
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 & DS3_MOTOR_RIGHT)
{
m_report.rumble.rightDuration = m_rumbleDuration[0];
m_report.rumble.rightOn = true;
}
else
{
m_report.rumble.rightDuration = 0;
m_report.rumble.rightOn = false;
}
sendHIDReport(m_report.buf, sizeof(m_report), 0x0201);
m_rumbleState = m_rumbleRequest;
}
else
{
if (state.m_reserved5[8] & 0x80)
m_rumbleRequest &= ~DS3_MOTOR_RIGHT;
if (state.m_reserved5[7] & 0x01)
m_rumbleRequest &= ~DS3_MOTOR_LEFT;
m_rumbleState = m_rumbleRequest;
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_PI) * RAD_TO_DEG;
state.accYaw = (atan2(accXval, accZval) + M_PI) * RAD_TO_DEG;
state.gyroZ = (state.m_gyrometerZ / 1023.f);
}
}
void DualshockPad::finalCycle()
{
}
} // boo

View File

@@ -0,0 +1,23 @@
#include "boo/inputdev/GenericPad.hpp"
#include "boo/inputdev/DeviceToken.hpp"
namespace boo
{
GenericPad::GenericPad(DeviceToken* token)
: DeviceBase(token)
{
}
GenericPad::~GenericPad()
{
}
void GenericPad::deviceDisconnected()
{
}
}

View File

@@ -0,0 +1,256 @@
#include "IHIDDevice.hpp"
#include "inputdev/CDeviceToken.hpp"
#include "inputdev/CDeviceBase.hpp"
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <thread>
namespace boo
{
class CHIDDeviceIOKit final : public IHIDDevice
{
CDeviceToken& m_token;
CDeviceBase& m_devImp;
IOUSBInterfaceInterface** m_usbIntf = NULL;
uint8_t m_usbIntfInPipe = 0;
uint8_t m_usbIntfOutPipe = 0;
bool m_runningTransferLoop = false;
const std::string& 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, 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, m_usbIntfInPipe, data, &readSize);
if (res != kIOReturnSuccess)
return 0;
return readSize;
}
return 0;
}
static void _threadProcUSBLL(CHIDDeviceIOKit* device)
{
char thrName[128];
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str());
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) */
io_iterator_t devIter;
io_registry_entry_t devEntry = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str());
IORegistryEntryGetChildIterator(devEntry, kIOServicePlane, &devIter);
io_object_t obj, interfaceEntry = 0;
while ((obj = IOIteratorNext(devIter)))
{
if (IOObjectConformsTo(obj, kIOUSBInterfaceClassName))
interfaceEntry = obj;
else
IOObjectRelease(obj);
}
if (!interfaceEntry)
{
snprintf(errStr, 256, "Unable to find interface for %s@%s\n",
device->m_token.getProductName().c_str(),
device->m_devPath.c_str());
device->m_devImp.deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
/* IOKit Plugin COM interface (WTF Apple???) */
IOCFPlugInInterface **iodev;
SInt32 score;
IOReturn err;
err = IOCreatePlugInInterfaceForService(interfaceEntry,
kIOUSBInterfaceUserClientTypeID,
kIOCFPlugInInterfaceID,
&iodev,
&score);
IOObjectRelease(interfaceEntry);
if (err)
{
snprintf(errStr, 256, "Unable to open %s@%s\n",
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
device->m_devImp.deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
/* USB interface function-pointer table */
IOUSBInterfaceInterface** intf = NULL;
err = (*iodev)->QueryInterface(iodev,
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
(LPVOID*)&intf);
if (err)
{
snprintf(errStr, 256, "Unable to open %s@%s\n",
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
device->m_devImp.deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
IODestroyPlugInInterface(iodev);
return;
}
/* Obtain exclusive lock on device */
device->m_usbIntf = intf;
err = (*intf)->USBInterfaceOpen(intf);
if (err != kIOReturnSuccess)
{
if (err == kIOReturnExclusiveAccess)
{
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n",
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
device->m_devImp.deviceError(errStr);
}
else
{
snprintf(errStr, 256, "Unable to open %s@%s\n",
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
device->m_devImp.deviceError(errStr);
}
lk.unlock();
device->m_initCond.notify_one();
(*intf)->Release(intf);
IODestroyPlugInInterface(iodev);
return;
}
/* Determine pipe indices for interrupt I/O */
UInt8 numEndpoints = 0;
err = (*intf)->GetNumEndpoints(intf, &numEndpoints);
for (int i=1 ; i<numEndpoints+1 ; ++i)
{
UInt8 dir, num, tType, interval;
UInt16 mPacketSz;
err = (*intf)->GetPipeProperties(intf, 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);
(*intf)->Release(intf);
IODestroyPlugInInterface(iodev);
device->m_usbIntf = NULL;
}
static void _threadProcBTLL(CHIDDeviceIOKit* 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();
}
static void _threadProcHID(CHIDDeviceIOKit* 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();
}
void _deviceDisconnected()
{
m_runningTransferLoop = false;
}
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message)
{
return false;
}
public:
CHIDDeviceIOKit(CDeviceToken& token, CDeviceBase& devImp)
: m_token(token),
m_devImp(devImp),
m_devPath(token.getDevicePath())
{
devImp.m_hidDev = this;
std::unique_lock<std::mutex> lk(m_initMutex);
CDeviceToken::TDeviceType dType = token.getDeviceType();
if (dType == CDeviceToken::DEVTYPE_USB)
m_thread = new std::thread(_threadProcUSBLL, this);
else if (dType == CDeviceToken::DEVTYPE_BLUETOOTH)
m_thread = new std::thread(_threadProcBTLL, this);
else if (dType == CDeviceToken::DEVTYPE_GENERICHID)
m_thread = new std::thread(_threadProcHID, this);
else
throw std::runtime_error("invalid token supplied to device constructor");
m_initCond.wait(lk);
}
~CHIDDeviceIOKit()
{
m_runningTransferLoop = false;
m_thread->join();
delete m_thread;
}
};
IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp)
{
return new CHIDDeviceIOKit(token, devImp);
}
}

View File

@@ -0,0 +1,275 @@
#include "IHIDDevice.hpp"
#include "boo/inputdev/DeviceToken.hpp"
#include "boo/inputdev/DeviceBase.hpp"
#include <thread>
#include <mutex>
#include <condition_variable>
#include <stdio.h>
#include <libudev.h>
#include <stropts.h>
#include <linux/usb/ch9.h>
#include <linux/usbdevice_fs.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
namespace boo
{
udev* GetUdev();
/*
* Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html
*/
class HIDDeviceUdev final : public IHIDDevice
{
DeviceToken& m_token;
DeviceBase& m_devImp;
int m_devFd = 0;
unsigned m_usbIntfInPipe = 0;
unsigned m_usbIntfOutPipe = 0;
bool m_runningTransferLoop = false;
const std::string& 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 | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
(unsigned)length,
0,
(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,
0,
data
};
return ioctl(m_devFd, USBDEVFS_BULK, &xfer);
}
return 0;
}
static void _threadProcUSBLL(HIDDeviceUdev* device)
{
unsigned 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.c_str());
/* Get device file */
const char* dp = udev_device_get_devnode(udevDev);
device->m_devFd = open(dp, O_RDWR);
if (device->m_devFd < 0)
{
snprintf(errStr, 256, "Unable to open %s@%s: %s\n",
device->m_token.getProductName().c_str(), dp, strerror(errno));
device->m_devImp.deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
udev_device_unref(udevDev);
return;
}
usb_device_descriptor devDesc = {};
read(device->m_devFd, &devDesc, 1);
read(device->m_devFd, &devDesc.bDescriptorType, devDesc.bLength-1);
if (devDesc.bNumConfigurations)
{
usb_config_descriptor confDesc = {};
read(device->m_devFd, &confDesc, 1);
read(device->m_devFd, &confDesc.bDescriptorType, confDesc.bLength-1);
if (confDesc.bNumInterfaces)
{
usb_interface_descriptor intfDesc = {};
read(device->m_devFd, &intfDesc, 1);
read(device->m_devFd, &intfDesc.bDescriptorType, intfDesc.bLength-1);
for (i=0 ; i<intfDesc.bNumEndpoints+1 ; ++i)
{
usb_endpoint_descriptor endpDesc = {};
read(device->m_devFd, &endpDesc, 1);
read(device->m_devFd, &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(device->m_devFd, 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(device->m_devFd);
device->m_devFd = 0;
udev_device_unref(udevDev);
}
static void _threadProcBTLL(HIDDeviceUdev* device)
{
std::unique_lock<std::mutex> lk(device->m_initMutex);
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str());
/* 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(HIDDeviceUdev* device)
{
std::unique_lock<std::mutex> lk(device->m_initMutex);
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str());
/* 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);
}
void _deviceDisconnected()
{
m_runningTransferLoop = false;
}
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message)
{
if (m_devFd)
{
usbdevfs_ctrltransfer xfer =
{
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0x09, // HID_SET_REPORT
message,
0,
(uint16_t)length,
0,
(void*)data
};
int ret = ioctl(m_devFd, USBDEVFS_CONTROL, &xfer);
if (ret != (int)length)
return false;
return true;
}
return false;
}
size_t _recieveReport(const uint8_t *data, size_t length, uint16_t message)
{
if (m_devFd)
{
usbdevfs_ctrltransfer xfer =
{
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0x01, // HID_GET_REPORT
message,
0,
(uint16_t)length,
0,
(void*)data
};
return ioctl(m_devFd, USBDEVFS_CONTROL, &xfer);
}
return 0;
}
public:
HIDDeviceUdev(DeviceToken& token, DeviceBase& devImp)
: m_token(token),
m_devImp(devImp),
m_devPath(token.getDevicePath())
{
devImp.m_hidDev = this;
std::unique_lock<std::mutex> lk(m_initMutex);
DeviceToken::TDeviceType dType = token.getDeviceType();
if (dType == DeviceToken::DEVTYPE_USB)
m_thread = new std::thread(_threadProcUSBLL, this);
else if (dType == DeviceToken::DEVTYPE_BLUETOOTH)
m_thread = new std::thread(_threadProcBTLL, this);
else if (dType == DeviceToken::DEVTYPE_GENERICHID)
m_thread = new std::thread(_threadProcHID, this);
else
{
fprintf(stderr, "invalid token supplied to device constructor");
abort();
}
m_initCond.wait(lk);
}
~HIDDeviceUdev()
{
m_runningTransferLoop = false;
m_thread->join();
delete m_thread;
}
};
IHIDDevice* IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
{
return new HIDDeviceUdev(token, devImp);
}
}

View File

@@ -0,0 +1,224 @@
#define _CRT_SECURE_NO_WARNINGS 1 /* STFU MSVC */
#include "IHIDDevice.hpp"
#include "inputdev/CDeviceToken.hpp"
#include "inputdev/CDeviceBase.hpp"
#include <thread>
#include <mutex>
#include <condition_variable>
#include <string.h>
#include <stdio.h>
#define _WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <winusb.h>
#include <usb100.h>
#include <Winusbio.h>
namespace boo
{
class CHIDDeviceWinUSB final : public IHIDDevice
{
CDeviceToken& m_token;
CDeviceBase& m_devImp;
HANDLE m_devHandle = 0;
WINUSB_INTERFACE_HANDLE m_usbHandle = NULL;
unsigned m_usbIntfInPipe = 0;
unsigned m_usbIntfOutPipe = 0;
bool m_runningTransferLoop = false;
const std::string& 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;
}
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(CHIDDeviceWinUSB* 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.c_str(),
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().c_str(),
device->m_devPath.c_str(), 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().c_str(),
device->m_devPath.c_str(), 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().c_str(),
device->m_devPath.c_str(), 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(CHIDDeviceWinUSB* 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();
}
static void _threadProcHID(CHIDDeviceWinUSB* 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();
}
void _deviceDisconnected()
{
m_runningTransferLoop = false;
}
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message)
{
return false;
}
public:
CHIDDeviceWinUSB(CDeviceToken& token, CDeviceBase& devImp)
: m_token(token),
m_devImp(devImp),
m_devPath(token.getDevicePath())
{
devImp.m_hidDev = this;
std::unique_lock<std::mutex> lk(m_initMutex);
CDeviceToken::TDeviceType dType = token.getDeviceType();
if (dType == CDeviceToken::DEVTYPE_USB)
m_thread = new std::thread(_threadProcUSBLL, this);
else if (dType == CDeviceToken::DEVTYPE_BLUETOOTH)
m_thread = new std::thread(_threadProcBTLL, this);
else if (dType == CDeviceToken::DEVTYPE_GENERICHID)
m_thread = new std::thread(_threadProcHID, this);
else
throw std::runtime_error("invalid token supplied to device constructor");
m_initCond.wait(lk);
}
~CHIDDeviceWinUSB()
{
m_runningTransferLoop = false;
m_thread->join();
delete m_thread;
}
};
IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp)
{
return new CHIDDeviceWinUSB(token, devImp);
}
}

View File

@@ -0,0 +1,217 @@
#include "IHIDListener.hpp"
#include "inputdev/CDeviceFinder.hpp"
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOCFPlugIn.h>
namespace boo
{
/*
* Reference: http://oroboro.com/usb-serial-number-osx/
*/
static bool getUSBStringDescriptor(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, &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.
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 CHIDListenerIOKit final : public IHIDListener
{
CDeviceFinder& m_finder;
CFRunLoopRef m_listenerRunLoop;
IONotificationPortRef m_llPort;
io_iterator_t m_llAddNotif, m_llRemoveNotif;
bool m_scanningEnabled;
static void devicesConnectedUSBLL(CHIDListenerIOKit* listener,
io_iterator_t iterator)
{
io_object_t obj;
while ((obj = IOIteratorNext(iterator)))
{
io_string_t devPath;
if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0)
continue;
if (!listener->m_scanningEnabled ||
listener->m_finder._hasToken(devPath))
{
IOObjectRelease(obj);
continue;
}
IOCFPlugInInterface** devServ;
SInt32 score;
IOReturn err;
err = IOCreatePlugInInterfaceForService(obj, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
if (err != kIOReturnSuccess)
throw std::runtime_error("unable to open IOKit plugin interface");
IOUSBDeviceInterface182 **dev;
err = (*devServ)->QueryInterface(devServ,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182),
(LPVOID*)&dev);
if (err != kIOReturnSuccess)
throw std::runtime_error("unable to open IOKit device interface");
UInt16 vid, pid;
(*dev)->GetDeviceVendor(dev, &vid);
(*dev)->GetDeviceProduct(dev, &pid);
UInt8 vstridx, pstridx;
(*dev)->USBGetManufacturerStringIndex(dev, &vstridx);
(*dev)->USBGetProductStringIndex(dev, &pstridx);
char vstr[128] = {0};
char pstr[128] = {0};
getUSBStringDescriptor(dev, vstridx, vstr);
getUSBStringDescriptor(dev, pstridx, pstr);
if (!listener->m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_USB,
vid, pid, vstr, pstr, devPath)))
{
/* Matched-insertion failed; see if generic HID interface is available */
/* TODO: Do */
}
//printf("ADDED %08X %s\n", obj, devPath);
(*dev)->Release(dev);
IODestroyPlugInInterface(devServ);
IOObjectRelease(obj);
}
}
static void devicesDisconnectedUSBLL(CHIDListenerIOKit* listener,
io_iterator_t iterator)
{
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop)
{
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{
devicesDisconnectedUSBLL(listener, iterator);
});
CFRunLoopWakeUp(listener->m_listenerRunLoop);
return;
}
io_object_t obj;
while ((obj = IOIteratorNext(iterator)))
{
io_string_t devPath;
if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0)
continue;
listener->m_finder._removeToken(devPath);
//printf("REMOVED %08X %s\n", obj, devPath);
IOObjectRelease(obj);
}
}
public:
CHIDListenerIOKit(CDeviceFinder& finder)
: m_finder(finder)
{
/* Register Low-Level USB Matcher */
m_listenerRunLoop = CFRunLoopGetCurrent();
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName);
CFRetain(matchDict);
m_scanningEnabled = true;
kern_return_t llRet =
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
(IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif);
if (llRet == kIOReturnSuccess)
devicesConnectedUSBLL(this, m_llAddNotif);
llRet =
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
(IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif);
if (llRet == kIOReturnSuccess)
devicesDisconnectedUSBLL(this, m_llRemoveNotif);
m_scanningEnabled = false;
}
~CHIDListenerIOKit()
{
CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
IOObjectRelease(m_llAddNotif);
IOObjectRelease(m_llRemoveNotif);
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()
{
io_iterator_t iter;
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching(kIOUSBDeviceClassName), &iter) == kIOReturnSuccess)
{
devicesConnectedUSBLL(this, iter);
IOObjectRelease(iter);
}
return true;
}
};
IHIDListener* IHIDListenerNew(CDeviceFinder& finder)
{
return new CHIDListenerIOKit(finder);
}
}

View File

@@ -0,0 +1,222 @@
#include "boo/inputdev/IHIDListener.hpp"
#include "boo/inputdev/DeviceFinder.hpp"
#include <libudev.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <thread>
namespace boo
{
static udev* UDEV_INST = NULL;
udev* GetUdev()
{
if (!UDEV_INST)
UDEV_INST = udev_new();
return UDEV_INST;
}
class HIDListenerUdev final : public IHIDListener
{
DeviceFinder& m_finder;
udev_monitor* m_udevMon;
std::thread* m_udevThread;
bool m_udevRunning;
bool m_scanningEnabled;
static void deviceConnected(HIDListenerUdev* listener,
udev_device* device)
{
if (!listener->m_scanningEnabled)
return;
/* Filter to USB/BT */
const char* dt = udev_device_get_devtype(device);
DeviceToken::TDeviceType type;
if (!strcmp(dt, "usb_device"))
type = DeviceToken::DEVTYPE_USB;
else if (!strcmp(dt, "bluetooth_device"))
type = DeviceToken::DEVTYPE_BLUETOOTH;
else
return;
/* Prevent redundant registration */
const char* devPath = udev_device_get_syspath(device);
if (listener->m_finder._hasToken(devPath))
return;
int vid = 0, pid = 0;
udev_list_entry* attrs = udev_device_get_properties_list_entry(device);
#if 0
udev_list_entry* att = NULL;
udev_list_entry_foreach(att, attrs)
{
const char* name = udev_list_entry_get_name(att);
const char* val = udev_list_entry_get_value(att);
fprintf(stderr, "%s %s\n", name, val);
}
fprintf(stderr, "\n\n");
#endif
udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID");
if (vide)
vid = strtol(udev_list_entry_get_value(vide), NULL, 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), NULL, 16);
const char* manuf = NULL;
udev_list_entry* manufe = udev_list_entry_get_by_name(attrs, "ID_VENDOR");
if (manufe)
manuf = udev_list_entry_get_value(manufe);
const char* product = NULL;
udev_list_entry* producte = udev_list_entry_get_by_name(attrs, "ID_MODEL");
if (producte)
product = udev_list_entry_get_value(producte);
if (!listener->m_finder._insertToken(DeviceToken(type, vid, pid, manuf, product, devPath)))
{
/* Matched-insertion failed; see if generic HID interface is available */
udev_list_entry* devInterfaces = NULL;
if (type == DeviceToken::DEVTYPE_USB)
devInterfaces = udev_list_entry_get_by_name(attrs, "ID_USB_INTERFACES");
else if (type == DeviceToken::DEVTYPE_BLUETOOTH)
devInterfaces = udev_list_entry_get_by_name(attrs, "ID_BLUETOOTH_INTERFACES");
if (devInterfaces)
{
const char* interfacesStr = udev_list_entry_get_value(devInterfaces);
if (strstr(interfacesStr, ":030104") || /* HID / GenericDesktop / Joystick */
strstr(interfacesStr, ":030105") || /* HID / GenericDesktop / Gamepad */
strstr(interfacesStr, ":090000")) /* HID / Sony / Dualshock */
{
udev_enumerate* hidEnum = udev_enumerate_new(UDEV_INST);
udev_enumerate_add_match_parent(hidEnum, device);
udev_enumerate_add_match_subsystem(hidEnum, "hid");
udev_enumerate_scan_devices(hidEnum);
udev_list_entry* hidEnt = udev_enumerate_get_list_entry(hidEnum);
if (hidEnt)
{
const char* hidPath = udev_list_entry_get_name(hidEnt);
if (!listener->m_finder._hasToken(hidPath))
listener->m_finder._insertToken(DeviceToken(DeviceToken::DEVTYPE_GENERICHID,
vid, pid, manuf, product, hidPath));
}
udev_enumerate_unref(hidEnum);
}
}
}
}
static void deviceDisconnected(HIDListenerUdev* listener,
udev_device* device)
{
const char* devPath = udev_device_get_syspath(device);
listener->m_finder._removeToken(devPath);
}
static void _udevProc(HIDListenerUdev* listener)
{
udev_monitor_enable_receiving(listener->m_udevMon);
int fd = udev_monitor_get_fd(listener->m_udevMon);
while (listener->m_udevRunning)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
select(fd+1, &fds, NULL, NULL, NULL);
udev_device* dev = udev_monitor_receive_device(listener->m_udevMon);
if (dev)
{
const char* action = udev_device_get_action(dev);
if (!strcmp(action, "add"))
deviceConnected(listener, dev);
else if (!strcmp(action, "remove"))
deviceDisconnected(listener, dev);
udev_device_unref(dev);
}
}
}
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_update(m_udevMon);
/* Initial HID Device Add */
m_scanningEnabled = true;
scanNow();
m_scanningEnabled = false;
/* Start hotplug thread */
m_udevRunning = true;
m_udevThread = new std::thread(_udevProc, this);
}
~HIDListenerUdev()
{
m_udevRunning = false;
pthread_kill(m_udevThread->native_handle(), SIGINT);
m_udevThread->join();
delete m_udevThread;
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_property(uenum, "DEVTYPE", "usb_device");
udev_enumerate_add_match_subsystem(uenum, "bluetooth");
udev_enumerate_add_match_property(uenum, "DEVTYPE", "bluetooth_device");
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(this, dev);
udev_device_unref(dev);
}
udev_enumerate_unref(uenum);
return true;
}
};
IHIDListener* IHIDListenerNew(DeviceFinder& finder)
{
return new HIDListenerUdev(finder);
}
}

View File

@@ -0,0 +1,209 @@
#define _CRT_SECURE_NO_WARNINGS 1 /* STFU MSVC */
#include "inputdev/IHIDListener.hpp"
#include "inputdev/CDeviceFinder.hpp"
#include <string.h>
#include <thread>
#define _WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <initguid.h>
#include <SetupAPI.h>
#include <Cfgmgr32.h>
#include <Usbiodef.h>
#include <Devpkey.h>
namespace boo
{
class CHIDListenerWinUSB final : public IHIDListener
{
CDeviceFinder& m_finder;
bool m_scanningEnabled;
/*
* Reference: https://github.com/pbatard/libwdi/blob/master/libwdi/libwdi.c
*/
void _pollDevices(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 USB 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,
&GUID_DEVINTERFACE_USB_DEVICE,
i,
&DeviceInterfaceData))
break;
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;
/* 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);
}
if (!szVid[0] || !szPid[0])
continue;
unsigned vid = strtol(szVid+4, NULL, 16);
unsigned pid = strtol(szPid+4, NULL, 16);
WCHAR 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,
&reg_type, (BYTE*)productW, 1024, &productSz);
}
wcstombs(product, productW, productSz / 2);
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);
wcstombs(manuf, manufW, manufSz / 2);
/* 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;
/* Whew!! that's a single device enumerated!! */
if (!m_finder._hasToken(DeviceInterfaceDetailData.wtf.DevicePath))
m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_USB,
vid, pid, manuf, product,
DeviceInterfaceDetailData.wtf.DevicePath));
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
public:
CHIDListenerWinUSB(CDeviceFinder& finder)
: m_finder(finder)
{
/* Initial HID Device Add */
_pollDevices(NULL);
}
~CHIDListenerWinUSB()
{}
/* Automatic device scanning */
bool startScanning()
{
m_scanningEnabled = true;
return true;
}
bool stopScanning()
{
m_scanningEnabled = false;
return true;
}
/* Manual device scanning */
bool scanNow()
{
_pollDevices(NULL);
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;
}
};
IHIDListener* IHIDListenerNew(CDeviceFinder& finder)
{
return new CHIDListenerWinUSB(finder);
}
}

View File

@@ -0,0 +1,23 @@
#ifndef IHIDDEVICE_HPP
#define IHIDDEVICE_HPP
#include "boo/inputdev/DeviceToken.hpp"
namespace boo
{
class IHIDDevice
{
friend class DeviceBase;
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;
virtual bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message)=0;
virtual size_t _recieveReport(const uint8_t* data, size_t length, uint16_t message){}
public:
inline virtual ~IHIDDevice() {}
};
}
#endif // IHIDDEVICE_HPP

View File

@@ -0,0 +1 @@
#include "boo/inputdev/RevolutionPad.hpp"