mirror of https://github.com/AxioDL/boo.git
initial WinUSB implementation
This commit is contained in:
parent
570396b76c
commit
e2d7030bb4
|
@ -13,7 +13,7 @@ class CDeviceBase
|
||||||
friend class CDeviceToken;
|
friend class CDeviceToken;
|
||||||
friend class CHIDDeviceIOKit;
|
friend class CHIDDeviceIOKit;
|
||||||
friend class CHIDDeviceUdev;
|
friend class CHIDDeviceUdev;
|
||||||
friend class CHIDDeviceWin32;
|
friend class CHIDDeviceWinUSB;
|
||||||
|
|
||||||
class CDeviceToken* m_token;
|
class CDeviceToken* m_token;
|
||||||
class IHIDDevice* m_hidDev;
|
class IHIDDevice* m_hidDev;
|
||||||
|
|
|
@ -19,7 +19,7 @@ class CDeviceFinder
|
||||||
public:
|
public:
|
||||||
friend class CHIDListenerIOKit;
|
friend class CHIDListenerIOKit;
|
||||||
friend class CHIDListenerUdev;
|
friend class CHIDListenerUdev;
|
||||||
friend class CHIDListenerWin32;
|
friend class CHIDListenerWinUSB;
|
||||||
static inline CDeviceFinder* instance() {return skDevFinder;}
|
static inline CDeviceFinder* instance() {return skDevFinder;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -69,7 +69,7 @@ public:
|
||||||
inline const std::string& getVendorName() const {return m_vendorName;}
|
inline const std::string& getVendorName() const {return m_vendorName;}
|
||||||
inline const std::string& getProductName() const {return m_productName;}
|
inline const std::string& getProductName() const {return m_productName;}
|
||||||
inline const std::string& getDevicePath() const {return m_devPath;}
|
inline const std::string& getDevicePath() const {return m_devPath;}
|
||||||
inline bool isDeviceOpen() const {return m_connectedDev;}
|
inline bool isDeviceOpen() const {return (m_connectedDev != NULL);}
|
||||||
inline CDeviceBase* openAndGetDevice()
|
inline CDeviceBase* openAndGetDevice()
|
||||||
{
|
{
|
||||||
if (!m_connectedDev)
|
if (!m_connectedDev)
|
||||||
|
|
|
@ -31,7 +31,9 @@ SOURCES += \
|
||||||
$$PWD/src/inputdev/CDualshockPad.cpp \
|
$$PWD/src/inputdev/CDualshockPad.cpp \
|
||||||
$$PWD/src/inputdev/CGenericPad.cpp \
|
$$PWD/src/inputdev/CGenericPad.cpp \
|
||||||
$$PWD/src/inputdev/CDeviceBase.cpp \
|
$$PWD/src/inputdev/CDeviceBase.cpp \
|
||||||
$$PWD/src/inputdev/SDeviceSignature.cpp
|
$$PWD/src/inputdev/SDeviceSignature.cpp \
|
||||||
|
$$PWD/src/inputdev/CHIDListenerWinUSB.cpp \
|
||||||
|
$$PWD/src/inputdev/CHIDDeviceWinUSB.cpp
|
||||||
|
|
||||||
unix:!macx {
|
unix:!macx {
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
@ -66,9 +68,7 @@ win32 {
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
$$PWD/include/win/CWGLContext.hpp
|
$$PWD/include/win/CWGLContext.hpp
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/src/win/CWGLContext.cpp \
|
$$PWD/src/win/CWGLContext.cpp
|
||||||
$$PWD/src/inputdev/CHIDDeviceWin32.cpp \
|
|
||||||
$$PWD/src/inputdev/CHIDListenerWin32.cpp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INCLUDEPATH += $$PWD/include
|
INCLUDEPATH += $$PWD/include
|
||||||
|
|
|
@ -5,14 +5,7 @@ CONFIG += console
|
||||||
unix:QMAKE_CXXFLAGS += -stdlib=libc++
|
unix:QMAKE_CXXFLAGS += -stdlib=libc++
|
||||||
unix:LIBS += -std=c++11 -stdlib=libc++ -lc++abi
|
unix:LIBS += -std=c++11 -stdlib=libc++ -lc++abi
|
||||||
|
|
||||||
win32:INCLUDEPATH += $$PWD/extern/libwdi
|
win32:LIBS += Setupapi.lib winusb.lib User32.lib
|
||||||
win32:LIBS += \
|
|
||||||
Shell32.lib \
|
|
||||||
Ole32.lib \
|
|
||||||
Setupapi.lib \
|
|
||||||
Advapi32.lib \
|
|
||||||
User32.lib \
|
|
||||||
$$PWD/extern/libwdi/x64/Debug/lib/libwdi.lib
|
|
||||||
|
|
||||||
#unix:!macx:CONFIG += link_pkgconfig
|
#unix:!macx:CONFIG += link_pkgconfig
|
||||||
#unix:!macx:PKGCONFIG += x11
|
#unix:!macx:PKGCONFIG += x11
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
#include <IOKit/IOCFPlugIn.h>
|
#include <IOKit/IOCFPlugIn.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#define MAX_REPORT_SIZE 65536
|
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -54,6 +52,7 @@ class CHIDDeviceIOKit final : public IHIDDevice
|
||||||
char thrName[128];
|
char thrName[128];
|
||||||
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str());
|
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str());
|
||||||
pthread_setname_np(thrName);
|
pthread_setname_np(thrName);
|
||||||
|
char errStr[256];
|
||||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
|
||||||
/* Get the HID element's parent (USB interrupt transfer-interface) */
|
/* Get the HID element's parent (USB interrupt transfer-interface) */
|
||||||
|
@ -70,7 +69,10 @@ class CHIDDeviceIOKit final : public IHIDDevice
|
||||||
}
|
}
|
||||||
if (!interfaceEntry)
|
if (!interfaceEntry)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("unable to find device interface");
|
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();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
return;
|
return;
|
||||||
|
@ -88,7 +90,9 @@ class CHIDDeviceIOKit final : public IHIDDevice
|
||||||
IOObjectRelease(interfaceEntry);
|
IOObjectRelease(interfaceEntry);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("unable to obtain IOKit plugin service");
|
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();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
return;
|
return;
|
||||||
|
@ -101,7 +105,9 @@ class CHIDDeviceIOKit final : public IHIDDevice
|
||||||
(LPVOID*)&intf);
|
(LPVOID*)&intf);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("unable to query IOKit USB interface");
|
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();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
IODestroyPlugInInterface(iodev);
|
IODestroyPlugInInterface(iodev);
|
||||||
|
@ -114,9 +120,17 @@ class CHIDDeviceIOKit final : public IHIDDevice
|
||||||
if (err != kIOReturnSuccess)
|
if (err != kIOReturnSuccess)
|
||||||
{
|
{
|
||||||
if (err == kIOReturnExclusiveAccess)
|
if (err == kIOReturnExclusiveAccess)
|
||||||
throw std::runtime_error("unable to open IOKit USB interface; someone else using it");
|
{
|
||||||
|
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
|
else
|
||||||
throw std::runtime_error("unable to open IOKit USB interface");
|
{
|
||||||
|
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();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
(*intf)->Release(intf);
|
(*intf)->Release(intf);
|
||||||
|
|
|
@ -19,8 +19,6 @@ namespace boo
|
||||||
|
|
||||||
udev* GetUdev();
|
udev* GetUdev();
|
||||||
|
|
||||||
#define MAX_REPORT_SIZE 65536
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html
|
* Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html
|
||||||
*/
|
*/
|
||||||
|
@ -78,15 +76,15 @@ class CHIDDeviceUdev final : public IHIDDevice
|
||||||
static void _threadProcUSBLL(CHIDDeviceUdev* device)
|
static void _threadProcUSBLL(CHIDDeviceUdev* device)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
char errStr[256];
|
||||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str());
|
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str());
|
||||||
|
|
||||||
/* Get the HID element's parent (USB interrupt transfer-interface) */
|
/* Get device file */
|
||||||
const char* dp = udev_device_get_devnode(udevDev);
|
const char* dp = udev_device_get_devnode(udevDev);
|
||||||
device->m_devFd = open(dp, O_RDWR);
|
device->m_devFd = open(dp, O_RDWR);
|
||||||
if (device->m_devFd < 0)
|
if (device->m_devFd < 0)
|
||||||
{
|
{
|
||||||
char errStr[256];
|
|
||||||
snprintf(errStr, 256, "Unable to open %s@%s: %s\n",
|
snprintf(errStr, 256, "Unable to open %s@%s: %s\n",
|
||||||
device->m_token.getProductName().c_str(), dp, strerror(errno));
|
device->m_token.getProductName().c_str(), dp, strerror(errno));
|
||||||
device->m_devImp.deviceError(errStr);
|
device->m_devImp.deviceError(errStr);
|
||||||
|
|
|
@ -1,234 +0,0 @@
|
||||||
#include "IHIDDevice.hpp"
|
|
||||||
#include "inputdev/CDeviceToken.hpp"
|
|
||||||
#include "inputdev/CDeviceBase.hpp"
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
namespace boo
|
|
||||||
{
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
udev* GetUdev();
|
|
||||||
|
|
||||||
#define MAX_REPORT_SIZE 65536
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CHIDDeviceUdev final : public IHIDDevice
|
|
||||||
{
|
|
||||||
CDeviceToken& m_token;
|
|
||||||
CDeviceBase& 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(uint8_t pipe, const uint8_t* data, size_t length)
|
|
||||||
{
|
|
||||||
if (m_devFd)
|
|
||||||
{
|
|
||||||
usbdevfs_bulktransfer xfer =
|
|
||||||
{
|
|
||||||
m_usbIntfOutPipe | USB_DIR_OUT,
|
|
||||||
(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 pipe, 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(CHIDDeviceUdev* device)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
|
||||||
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str());
|
|
||||||
|
|
||||||
/* Get the HID element's parent (USB interrupt transfer-interface) */
|
|
||||||
const char* dp = udev_device_get_devnode(udevDev);
|
|
||||||
device->m_devFd = open(dp, O_RDWR);
|
|
||||||
if (device->m_devFd < 0)
|
|
||||||
{
|
|
||||||
char errStr[256];
|
|
||||||
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 = {0};
|
|
||||||
read(device->m_devFd, &devDesc, 1);
|
|
||||||
read(device->m_devFd, &devDesc.bDescriptorType, devDesc.bLength-1);
|
|
||||||
if (devDesc.bNumConfigurations)
|
|
||||||
{
|
|
||||||
usb_config_descriptor confDesc = {0};
|
|
||||||
read(device->m_devFd, &confDesc, 1);
|
|
||||||
read(device->m_devFd, &confDesc.bDescriptorType, confDesc.bLength-1);
|
|
||||||
if (confDesc.bNumInterfaces)
|
|
||||||
{
|
|
||||||
usb_interface_descriptor intfDesc = {0};
|
|
||||||
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 = {0};
|
|
||||||
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(CHIDDeviceUdev* 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(CHIDDeviceUdev* 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)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
CHIDDeviceUdev(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
~CHIDDeviceUdev()
|
|
||||||
{
|
|
||||||
m_runningTransferLoop = false;
|
|
||||||
m_thread->join();
|
|
||||||
delete m_thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp)
|
|
||||||
{
|
|
||||||
//return new CHIDDeviceUdev(token, devImp);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
#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>
|
||||||
|
|
||||||
|
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(uint8_t pipe, 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 pipe, 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, 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, GetLastError());
|
||||||
|
device->m_devImp.deviceError(errStr);
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
CloseHandle(device->m_devHandle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,222 +0,0 @@
|
||||||
#include "inputdev/IHIDListener.hpp"
|
|
||||||
#include "inputdev/CDeviceFinder.hpp"
|
|
||||||
#include <string.h>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#define _WIN32_LEAN_AND_MEAN 1
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
namespace boo
|
|
||||||
{
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
static udev* UDEV_INST = NULL;
|
|
||||||
udev* GetUdev()
|
|
||||||
{
|
|
||||||
if (!UDEV_INST)
|
|
||||||
UDEV_INST = udev_new();
|
|
||||||
return UDEV_INST;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CHIDListenerWin32 final : public IHIDListener
|
|
||||||
{
|
|
||||||
CDeviceFinder& m_finder;
|
|
||||||
|
|
||||||
udev_monitor* m_udevMon;
|
|
||||||
std::thread* m_udevThread;
|
|
||||||
bool m_udevRunning;
|
|
||||||
bool m_scanningEnabled;
|
|
||||||
|
|
||||||
static void deviceConnected(CHIDListenerWin32* listener,
|
|
||||||
udev_device* device)
|
|
||||||
{
|
|
||||||
if (!listener->m_scanningEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Filter to USB/BT */
|
|
||||||
const char* dt = udev_device_get_devtype(device);
|
|
||||||
CDeviceToken::TDeviceType type;
|
|
||||||
if (!strcmp(dt, "usb_device"))
|
|
||||||
type = CDeviceToken::DEVTYPE_USB;
|
|
||||||
else if (!strcmp(dt, "bluetooth_device"))
|
|
||||||
type = CDeviceToken::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(CDeviceToken(type, vid, pid, manuf, product, devPath)))
|
|
||||||
{
|
|
||||||
/* Matched-insertion failed; see if generic HID interface is available */
|
|
||||||
udev_list_entry* devInterfaces = NULL;
|
|
||||||
if (type == CDeviceToken::DEVTYPE_USB)
|
|
||||||
devInterfaces = udev_list_entry_get_by_name(attrs, "ID_USB_INTERFACES");
|
|
||||||
else if (type == CDeviceToken::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 */
|
|
||||||
{
|
|
||||||
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(CDeviceToken(CDeviceToken::DEVTYPE_GENERICHID,
|
|
||||||
vid, pid, manuf, product, hidPath));
|
|
||||||
}
|
|
||||||
udev_enumerate_unref(hidEnum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void deviceDisconnected(CHIDListenerWin32* listener,
|
|
||||||
udev_device* device)
|
|
||||||
{
|
|
||||||
const char* devPath = udev_device_get_syspath(device);
|
|
||||||
listener->m_finder._removeToken(devPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _udevProc(CHIDListenerWin32* 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:
|
|
||||||
CHIDListenerWin32(CDeviceFinder& finder)
|
|
||||||
: m_finder(finder)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Setup hotplug events */
|
|
||||||
m_udevMon = udev_monitor_new_from_netlink(GetUdev(), "udev");
|
|
||||||
if (!m_udevMon)
|
|
||||||
throw std::runtime_error("unable to init udev_monitor");
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
~CHIDListenerWin32()
|
|
||||||
{
|
|
||||||
m_udevRunning = false;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
IHIDListener* IHIDListenerNew(CDeviceFinder& finder)
|
|
||||||
{
|
|
||||||
// return new CHIDListenerWin32(finder);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
#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;
|
||||||
|
|
||||||
|
std::thread* m_setupThread;
|
||||||
|
bool m_setupRunning;
|
||||||
|
bool m_scanningEnabled;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reference: https://github.com/pbatard/libwdi/blob/master/libwdi/libwdi.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
void _pollDevices()
|
||||||
|
{
|
||||||
|
|
||||||
|
/* 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,
|
||||||
|
®_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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _setupProc(CHIDListenerWinUSB* listener)
|
||||||
|
{
|
||||||
|
while (listener->m_setupRunning)
|
||||||
|
{
|
||||||
|
if (listener->m_scanningEnabled)
|
||||||
|
listener->_pollDevices();
|
||||||
|
|
||||||
|
/* Due to NT derpiness, this needs to be a periodic poll */
|
||||||
|
Sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CHIDListenerWinUSB(CDeviceFinder& finder)
|
||||||
|
: m_finder(finder)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Initial HID Device Add */
|
||||||
|
_pollDevices();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~CHIDListenerWinUSB()
|
||||||
|
{
|
||||||
|
m_setupRunning = false;
|
||||||
|
m_setupThread->join();
|
||||||
|
delete m_setupThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Automatic device scanning */
|
||||||
|
bool startScanning()
|
||||||
|
{
|
||||||
|
m_scanningEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool stopScanning()
|
||||||
|
{
|
||||||
|
m_scanningEnabled = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manual device scanning */
|
||||||
|
bool scanNow()
|
||||||
|
{
|
||||||
|
_pollDevices();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IHIDListener* IHIDListenerNew(CDeviceFinder& finder)
|
||||||
|
{
|
||||||
|
return new CHIDListenerWinUSB(finder);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,37 +0,0 @@
|
||||||
|
|
||||||
#define _WIN32_LEAN_AND_MEAN 1
|
|
||||||
#include <windows.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
int genWin32ShellExecute(const wchar_t* AppFullPath,
|
|
||||||
const wchar_t* Verb,
|
|
||||||
const wchar_t* Params,
|
|
||||||
bool ShowAppWindow,
|
|
||||||
bool WaitToFinish)
|
|
||||||
{
|
|
||||||
int Result = 0;
|
|
||||||
|
|
||||||
// Setup the required structure
|
|
||||||
SHELLEXECUTEINFO ShExecInfo;
|
|
||||||
memset(&ShExecInfo, 0, sizeof(SHELLEXECUTEINFO));
|
|
||||||
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
||||||
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
||||||
ShExecInfo.hwnd = NULL;
|
|
||||||
ShExecInfo.lpVerb = Verb;
|
|
||||||
ShExecInfo.lpFile = AppFullPath;
|
|
||||||
ShExecInfo.lpParameters = Params;
|
|
||||||
ShExecInfo.lpDirectory = NULL;
|
|
||||||
ShExecInfo.nShow = (ShowAppWindow ? SW_SHOW : SW_HIDE);
|
|
||||||
ShExecInfo.hInstApp = NULL;
|
|
||||||
|
|
||||||
// Spawn the process
|
|
||||||
if (ShellExecuteEx(&ShExecInfo) == FALSE)
|
|
||||||
{
|
|
||||||
Result = -1; // Failed to execute process
|
|
||||||
} else if (WaitToFinish)
|
|
||||||
{
|
|
||||||
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
|
@ -8,6 +8,9 @@
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#define _WIN32_LEAN_AND_MEAN 1
|
#define _WIN32_LEAN_AND_MEAN 1
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <Usbiodef.h>
|
||||||
|
#include <Dbt.h>
|
||||||
#else
|
#else
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -58,70 +61,8 @@ public:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _WIN32
|
|
||||||
static int genWin32ShellExecute(const wchar_t* AppFullPath,
|
|
||||||
const wchar_t* Verb,
|
|
||||||
const wchar_t* Params,
|
|
||||||
bool ShowAppWindow,
|
|
||||||
bool WaitToFinish)
|
|
||||||
{
|
|
||||||
int Result = 0;
|
|
||||||
|
|
||||||
// Setup the required structure
|
|
||||||
SHELLEXECUTEINFO ShExecInfo;
|
|
||||||
memset(&ShExecInfo, 0, sizeof(SHELLEXECUTEINFO));
|
|
||||||
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
||||||
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
||||||
ShExecInfo.hwnd = NULL;
|
|
||||||
ShExecInfo.lpVerb = Verb;
|
|
||||||
ShExecInfo.lpFile = AppFullPath;
|
|
||||||
ShExecInfo.lpParameters = Params;
|
|
||||||
ShExecInfo.lpDirectory = NULL;
|
|
||||||
ShExecInfo.nShow = (ShowAppWindow ? SW_SHOW : SW_HIDE);
|
|
||||||
ShExecInfo.hInstApp = NULL;
|
|
||||||
|
|
||||||
// Spawn the process
|
|
||||||
if (ShellExecuteEx(&ShExecInfo) == FALSE)
|
|
||||||
{
|
|
||||||
Result = -1; // Failed to execute process
|
|
||||||
} else if (WaitToFinish)
|
|
||||||
{
|
|
||||||
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <libwdi/libwdi.h>
|
|
||||||
static void scanWinUSB()
|
|
||||||
{
|
|
||||||
struct wdi_device_info *device, *list;
|
|
||||||
struct wdi_options_create_list WDI_LIST_OPTS =
|
|
||||||
{
|
|
||||||
true, false, true
|
|
||||||
};
|
|
||||||
int err = wdi_create_list(&list, &WDI_LIST_OPTS);
|
|
||||||
if (err == WDI_SUCCESS)
|
|
||||||
{
|
|
||||||
for (device = list; device != NULL; device = device->next)
|
|
||||||
{
|
|
||||||
if (device->vid == 0x57E && device->pid == 0x337 &&
|
|
||||||
!strcmp(device->driver, "HidUsb"))
|
|
||||||
{
|
|
||||||
printf("GC adapter detected; installing driver\n");
|
|
||||||
genWin32ShellExecute(L"WinUsbInstaller.exe", L"", L"", false, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wdi_destroy_list(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
#if _WIN32
|
|
||||||
scanWinUSB();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
boo::CTestDeviceFinder finder;
|
boo::CTestDeviceFinder finder;
|
||||||
finder.startScanning();
|
finder.startScanning();
|
||||||
|
@ -137,7 +78,35 @@ int main(int argc, char** argv)
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
CFRunLoopRun();
|
CFRunLoopRun();
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
while (true) {Sleep(1000);}
|
|
||||||
|
/* Register hotplug notification with windows */
|
||||||
|
DEV_BROADCAST_DEVICEINTERFACE_A hotplugConf =
|
||||||
|
{
|
||||||
|
sizeof(DEV_BROADCAST_DEVICEINTERFACE_A),
|
||||||
|
DBT_DEVTYP_DEVICEINTERFACE,
|
||||||
|
0,
|
||||||
|
GUID_DEVINTERFACE_USB_DEVICE
|
||||||
|
};
|
||||||
|
HWND consoleWnd = GetConsoleWindow();
|
||||||
|
HDEVNOTIFY notHandle = RegisterDeviceNotificationA(consoleWnd, &hotplugConf, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||||
|
|
||||||
|
MSG recvMsg;
|
||||||
|
while (GetMessage(&recvMsg, consoleWnd, 0, 0))
|
||||||
|
{
|
||||||
|
printf("MSG: %d\n", recvMsg.message);
|
||||||
|
switch (recvMsg.message)
|
||||||
|
{
|
||||||
|
case WM_DEVICECHANGE:
|
||||||
|
printf("DEVICECHANGE!!\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TranslateMessage(&recvMsg);
|
||||||
|
DispatchMessage(&recvMsg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
while (true) {sleep(1);}
|
while (true) {sleep(1);}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/main.cpp \
|
$$PWD/main.cpp
|
||||||
$$PWD/Win32Elevatedlauncher.c
|
|
||||||
|
|
||||||
win32:SOURCES +=
|
win32:SOURCES +=
|
||||||
|
|
Loading…
Reference in New Issue