2015-05-03 06:40:20 +00:00
|
|
|
#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>
|
2015-05-04 04:28:07 +00:00
|
|
|
#include <usb100.h>
|
|
|
|
#include <Winusbio.h>
|
2015-05-03 06:40:20 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2015-05-10 07:02:18 +00:00
|
|
|
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)
|
2015-05-03 06:40:20 +00:00
|
|
|
{
|
|
|
|
if (m_usbHandle)
|
|
|
|
{
|
|
|
|
ULONG lengthTransferred = 0;
|
|
|
|
if (!WinUsb_WritePipe(m_usbHandle, m_usbIntfOutPipe, (PUCHAR)data,
|
2015-05-04 04:28:07 +00:00
|
|
|
(ULONG)length, &lengthTransferred, NULL) ||
|
|
|
|
lengthTransferred != length)
|
2015-05-03 06:40:20 +00:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-05-10 07:02:18 +00:00
|
|
|
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)
|
2015-05-03 06:40:20 +00:00
|
|
|
{
|
|
|
|
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);
|
2015-05-04 04:28:07 +00:00
|
|
|
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))
|
|
|
|
{
|
2015-05-03 06:40:20 +00:00
|
|
|
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
|
|
|
device->m_token.getProductName().c_str(),
|
2015-05-04 04:28:07 +00:00
|
|
|
device->m_devPath.c_str(), GetLastError());
|
2015-05-03 06:40:20 +00:00
|
|
|
device->m_devImp.deviceError(errStr);
|
|
|
|
lk.unlock();
|
|
|
|
device->m_initCond.notify_one();
|
2015-05-04 04:28:07 +00:00
|
|
|
CloseHandle(device->m_devHandle);
|
2015-05-03 06:40:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-04 04:28:07 +00:00
|
|
|
/* Enumerate device pipes */
|
|
|
|
USB_INTERFACE_DESCRIPTOR ifDesc = {0};
|
|
|
|
if (!WinUsb_QueryInterfaceSettings(device->m_usbHandle, 0, &ifDesc))
|
|
|
|
{
|
2015-05-03 06:40:20 +00:00
|
|
|
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
|
|
|
device->m_token.getProductName().c_str(),
|
2015-05-04 04:28:07 +00:00
|
|
|
device->m_devPath.c_str(), GetLastError());
|
2015-05-03 06:40:20 +00:00
|
|
|
device->m_devImp.deviceError(errStr);
|
|
|
|
lk.unlock();
|
|
|
|
device->m_initCond.notify_one();
|
|
|
|
CloseHandle(device->m_devHandle);
|
|
|
|
return;
|
|
|
|
}
|
2015-05-04 04:28:07 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2015-05-03 06:40:20 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2015-05-15 01:16:36 +00:00
|
|
|
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message)
|
2015-05-03 06:40:20 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|