boo/lib/inputdev/HIDDeviceIOKit.cpp

260 lines
8.2 KiB
C++
Raw Normal View History

2015-04-19 20:16:50 +00:00
#include "IHIDDevice.hpp"
2015-09-02 19:09:13 +00:00
#include "boo/inputdev/DeviceToken.hpp"
#include "boo/inputdev/DeviceBase.hpp"
#include <IOKit/hid/IOHIDLib.h>
2015-04-24 00:24:15 +00:00
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <thread>
namespace boo
{
2015-09-02 19:09:13 +00:00
class HIDDeviceIOKit : public IHIDDevice
{
2015-09-02 19:09:13 +00:00
DeviceToken& m_token;
DeviceBase& m_devImp;
2015-04-24 00:24:15 +00:00
IOUSBInterfaceInterface** m_usbIntf = NULL;
uint8_t m_usbIntfInPipe = 0;
uint8_t m_usbIntfOutPipe = 0;
bool m_runningTransferLoop = false;
2015-04-24 00:24:15 +00:00
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-04-24 00:24:15 +00:00
{
if (m_usbIntf)
{
IOReturn res = (*m_usbIntf)->WritePipe(m_usbIntf, m_usbIntfOutPipe, (void*)data, length);
return res == kIOReturnSuccess;
}
return false;
}
2015-05-10 07:02:18 +00:00
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)
2015-04-24 00:24:15 +00:00
{
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;
}
2015-09-02 19:09:13 +00:00
static void _threadProcUSBLL(HIDDeviceIOKit* device)
2015-04-24 00:24:15 +00:00
{
char thrName[128];
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str());
pthread_setname_np(thrName);
2015-05-03 06:40:20 +00:00
char errStr[256];
2015-04-24 00:24:15 +00:00
std::unique_lock<std::mutex> lk(device->m_initMutex);
2015-04-24 00:24:15 +00:00
/* 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)
{
2015-05-03 06:40:20 +00:00
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);
2015-04-24 00:24:15 +00:00
lk.unlock();
device->m_initCond.notify_one();
return;
}
2015-04-24 00:24:15 +00:00
/* IOKit Plugin COM interface (WTF Apple???) */
IOCFPlugInInterface **iodev;
SInt32 score;
IOReturn err;
err = IOCreatePlugInInterfaceForService(interfaceEntry,
kIOUSBInterfaceUserClientTypeID,
kIOCFPlugInInterfaceID,
&iodev,
&score);
IOObjectRelease(interfaceEntry);
if (err)
{
2015-05-03 06:40:20 +00:00
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);
2015-04-24 00:24:15 +00:00
lk.unlock();
device->m_initCond.notify_one();
return;
}
2015-04-24 00:24:15 +00:00
/* USB interface function-pointer table */
IOUSBInterfaceInterface** intf = NULL;
err = (*iodev)->QueryInterface(iodev,
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
(LPVOID*)&intf);
if (err)
{
2015-05-03 06:40:20 +00:00
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);
2015-04-24 00:24:15 +00:00
lk.unlock();
device->m_initCond.notify_one();
IODestroyPlugInInterface(iodev);
return;
}
2015-04-24 00:24:15 +00:00
/* Obtain exclusive lock on device */
device->m_usbIntf = intf;
err = (*intf)->USBInterfaceOpen(intf);
if (err != kIOReturnSuccess)
{
if (err == kIOReturnExclusiveAccess)
2015-05-03 06:40:20 +00:00
{
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);
}
2015-04-24 00:24:15 +00:00
else
2015-05-03 06:40:20 +00:00
{
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);
}
2015-04-24 00:24:15 +00:00
lk.unlock();
device->m_initCond.notify_one();
(*intf)->Release(intf);
IODestroyPlugInInterface(iodev);
return;
}
2015-04-24 00:24:15 +00:00
/* 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;
}
}
2015-04-24 00:24:15 +00:00
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
2015-04-24 00:24:15 +00:00
/* Start transfer loop */
2015-05-01 01:11:25 +00:00
device->m_devImp.initialCycle();
2015-04-24 00:24:15 +00:00
while (device->m_runningTransferLoop)
device->m_devImp.transferCycle();
2015-04-26 08:25:44 +00:00
device->m_devImp.finalCycle();
2015-04-24 00:24:15 +00:00
/* Cleanup */
(*intf)->USBInterfaceClose(intf);
(*intf)->Release(intf);
IODestroyPlugInInterface(iodev);
device->m_usbIntf = NULL;
2015-04-24 00:24:15 +00:00
}
2015-09-02 19:09:13 +00:00
static void _threadProcBTLL(HIDDeviceIOKit* device)
2015-05-01 01:11:25 +00:00
{
std::unique_lock<std::mutex> lk(device->m_initMutex);
2015-05-01 01:11:25 +00:00
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
2015-05-01 01:11:25 +00:00
/* Start transfer loop */
device->m_devImp.initialCycle();
while (device->m_runningTransferLoop)
device->m_devImp.transferCycle();
device->m_devImp.finalCycle();
2015-05-01 01:11:25 +00:00
}
2015-09-02 19:09:13 +00:00
static void _threadProcHID(HIDDeviceIOKit* device)
{
2015-05-01 01:11:25 +00:00
std::unique_lock<std::mutex> lk(device->m_initMutex);
2015-05-01 01:11:25 +00:00
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
2015-05-01 01:11:25 +00:00
/* Start transfer loop */
device->m_devImp.initialCycle();
while (device->m_runningTransferLoop)
device->m_devImp.transferCycle();
device->m_devImp.finalCycle();
}
2015-04-22 21:48:23 +00:00
void _deviceDisconnected()
{
2015-04-24 00:24:15 +00:00
m_runningTransferLoop = false;
}
2015-05-15 01:16:36 +00:00
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message)
{
2015-04-24 00:24:15 +00:00
return false;
2015-04-22 21:48:23 +00:00
}
public:
2015-09-02 19:09:13 +00:00
HIDDeviceIOKit(DeviceToken& token, DeviceBase& devImp)
: m_token(token),
2015-04-24 00:24:15 +00:00
m_devImp(devImp),
m_devPath(token.getDevicePath())
{
2015-04-24 00:24:15 +00:00
devImp.m_hidDev = this;
std::unique_lock<std::mutex> lk(m_initMutex);
2015-11-21 02:16:15 +00:00
DeviceToken::DeviceType dType = token.getDeviceType();
if (dType == DeviceToken::DeviceType::USB)
m_thread = std::thread(_threadProcUSBLL, this);
2015-11-21 02:16:15 +00:00
else if (dType == DeviceToken::DeviceType::Bluetooth)
m_thread = std::thread(_threadProcBTLL, this);
2015-11-21 02:16:15 +00:00
else if (dType == DeviceToken::DeviceType::GenericHID)
m_thread = std::thread(_threadProcHID, this);
2015-04-24 00:24:15 +00:00
else
2015-09-02 19:09:13 +00:00
{
fprintf(stderr, "invalid token supplied to device constructor\n");
return;
}
m_initCond.wait(lk);
}
2015-09-02 19:09:13 +00:00
~HIDDeviceIOKit()
{
2015-04-24 00:24:15 +00:00
m_runningTransferLoop = false;
if (m_thread.joinable())
m_thread.join();
}
2015-04-22 21:48:23 +00:00
};
2015-09-02 19:09:13 +00:00
IHIDDevice* IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
{
2015-09-02 19:09:13 +00:00
return new HIDDeviceIOKit(token, devImp);
}
}