IOKit input refactor

This commit is contained in:
Jack Andersen
2017-05-07 11:24:00 -10:00
parent 12bbf540fd
commit 62443b59e7
18 changed files with 798 additions and 220 deletions

View File

@@ -2,8 +2,11 @@
#include "boo/inputdev/DeviceToken.hpp"
#include "boo/inputdev/DeviceBase.hpp"
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDDevicePlugin.h>
#include <IOKit/hid/IOHIDDevice.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOCFPlugIn.h>
#include "IOKitPointer.hpp"
#include <thread>
namespace boo
@@ -14,9 +17,10 @@ class HIDDeviceIOKit : public IHIDDevice
DeviceToken& m_token;
DeviceBase& m_devImp;
IOUSBInterfaceInterface** m_usbIntf = NULL;
IUnknownPointer<IOUSBInterfaceInterface> m_usbIntf;
uint8_t m_usbIntfInPipe = 0;
uint8_t m_usbIntfOutPipe = 0;
IUnknownPointer<IOHIDDeviceDeviceInterface> m_hidIntf;
bool m_runningTransferLoop = false;
const std::string& m_devPath;
@@ -28,7 +32,7 @@ class HIDDeviceIOKit : public IHIDDevice
{
if (m_usbIntf)
{
IOReturn res = (*m_usbIntf)->WritePipe(m_usbIntf, m_usbIntfOutPipe, (void*)data, length);
IOReturn res = m_usbIntf->WritePipe(m_usbIntf.storage(), m_usbIntfOutPipe, (void*)data, length);
return res == kIOReturnSuccess;
}
return false;
@@ -39,7 +43,32 @@ class HIDDeviceIOKit : public IHIDDevice
if (m_usbIntf)
{
UInt32 readSize = length;
IOReturn res = (*m_usbIntf)->ReadPipe(m_usbIntf, m_usbIntfInPipe, data, &readSize);
IOReturn res = m_usbIntf->ReadPipe(m_usbIntf.storage(), m_usbIntfInPipe, data, &readSize);
if (res != kIOReturnSuccess)
return 0;
return readSize;
}
return 0;
}
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{
if (m_hidIntf)
{
IOReturn res = m_hidIntf->setReport(m_hidIntf.storage(), IOHIDReportType(tp), message, data, length,
1000, nullptr, nullptr, 0);
return res == kIOReturnSuccess;
}
return false;
}
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{
if (m_hidIntf)
{
CFIndex readSize = length;
IOReturn res = m_hidIntf->getReport(m_hidIntf.storage(), IOHIDReportType(tp), message, data, &readSize,
1000, nullptr, nullptr, 0);
if (res != kIOReturnSuccess)
return 0;
return readSize;
@@ -56,11 +85,11 @@ class HIDDeviceIOKit : public IHIDDevice
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());
IOObjectPointer<io_iterator_t> devIter;
IOObjectPointer<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)))
IOObjectPointer<io_object_t> interfaceEntry;
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(devIter))
{
if (IOObjectConformsTo(obj, kIOUSBInterfaceClassName))
interfaceEntry = obj;
@@ -79,7 +108,7 @@ class HIDDeviceIOKit : public IHIDDevice
}
/* IOKit Plugin COM interface (WTF Apple???) */
IOCFPlugInInterface **iodev;
IOCFPluginPointer iodev;
SInt32 score;
IOReturn err;
err = IOCreatePlugInInterfaceForService(interfaceEntry,
@@ -87,7 +116,6 @@ class HIDDeviceIOKit : public IHIDDevice
kIOCFPlugInInterfaceID,
&iodev,
&score);
IOObjectRelease(interfaceEntry);
if (err)
{
snprintf(errStr, 256, "Unable to open %s@%s\n",
@@ -99,10 +127,8 @@ class HIDDeviceIOKit : public IHIDDevice
}
/* USB interface function-pointer table */
IOUSBInterfaceInterface** intf = NULL;
err = (*iodev)->QueryInterface(iodev,
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
(LPVOID*)&intf);
IUnknownPointer<IOUSBInterfaceInterface> intf;
err = iodev.As(&intf, kIOUSBInterfaceInterfaceID);
if (err)
{
snprintf(errStr, 256, "Unable to open %s@%s\n",
@@ -110,13 +136,12 @@ class HIDDeviceIOKit : public IHIDDevice
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);
err = intf->USBInterfaceOpen(intf.storage());
if (err != kIOReturnSuccess)
{
if (err == kIOReturnExclusiveAccess)
@@ -133,19 +158,17 @@ class HIDDeviceIOKit : public IHIDDevice
}
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);
err = intf->GetNumEndpoints(intf.storage(), &numEndpoints);
for (int i=1 ; i<numEndpoints+1 ; ++i)
{
UInt8 dir, num, tType, interval;
UInt16 mPacketSz;
err = (*intf)->GetPipeProperties(intf, i, &dir, &num, &tType, &mPacketSz, &interval);
err = intf->GetPipeProperties(intf.storage(), i, &dir, &num, &tType, &mPacketSz, &interval);
if (tType == kUSBInterrupt)
{
if (dir == kUSBIn)
@@ -167,11 +190,8 @@ class HIDDeviceIOKit : public IHIDDevice
device->m_devImp.finalCycle();
/* Cleanup */
(*intf)->USBInterfaceClose(intf);
(*intf)->Release(intf);
IODestroyPlugInInterface(iodev);
device->m_usbIntf = NULL;
intf->USBInterfaceClose(intf.storage());
device->m_usbIntf = nullptr;
}
static void _threadProcBTLL(HIDDeviceIOKit* device)
@@ -191,10 +211,106 @@ class HIDDeviceIOKit : public IHIDDevice
}
static void _hidReportCb(void * _Nullable context,
IOReturn,
void * _Nullable,
IOHIDReportType type,
uint32_t reportID,
uint8_t * report,
CFIndex reportLength)
{
reinterpret_cast<DeviceBase*>(context)->receivedHIDReport(report, reportLength, HIDReportType(type), reportID);
}
static void _threadProcHID(HIDDeviceIOKit* 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 object (HID device interface) */
IOObjectPointer<io_service_t> interfaceEntry = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str());
if (!IOObjectConformsTo(interfaceEntry.get(), "IOHIDDevice"))
{
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???) */
IOCFPluginPointer iodev;
SInt32 score;
IOReturn err;
err = IOCreatePlugInInterfaceForService(interfaceEntry.get(),
kIOHIDDeviceTypeID,
kIOCFPlugInInterfaceID,
&iodev,
&score);
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;
}
/* HID interface function-pointer table */
IUnknownPointer<IOHIDDeviceDeviceInterface> intf;
err = iodev.As(&intf, kIOHIDDeviceDeviceInterfaceID);
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;
}
/* Open device */
device->m_hidIntf = intf;
err = intf->open(intf.storage(), kIOHIDOptionsTypeNone);
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();
return;
}
/* Register input buffer */
std::unique_ptr<uint8_t[]> buffer;
if (size_t bufSize = device->m_devImp.getInputBufferSize())
{
buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufSize]);
CFTypeRef source;
device->m_hidIntf->getAsyncEventSource(device->m_hidIntf.storage(), &source);
device->m_hidIntf->setInputReportCallback(device->m_hidIntf.storage(), buffer.get(),
bufSize, _hidReportCb, &device->m_devImp, 0);
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFRunLoopAddSource(rl, CFRunLoopSourceRef(source), kCFRunLoopDefaultMode);
CFRunLoopWakeUp(rl);
}
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
@@ -203,8 +319,15 @@ class HIDDeviceIOKit : public IHIDDevice
/* Start transfer loop */
device->m_devImp.initialCycle();
while (device->m_runningTransferLoop)
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
device->m_devImp.transferCycle();
}
device->m_devImp.finalCycle();
/* Cleanup */
intf->close(intf.storage(), kIOHIDOptionsTypeNone);
device->m_hidIntf = nullptr;
}
void _deviceDisconnected()
@@ -212,11 +335,6 @@ class HIDDeviceIOKit : public IHIDDevice
m_runningTransferLoop = false;
}
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message)
{
return false;
}
public:
HIDDeviceIOKit(DeviceToken& token, DeviceBase& devImp)
@@ -224,14 +342,13 @@ public:
m_devImp(devImp),
m_devPath(token.getDevicePath())
{
devImp.m_hidDev = this;
std::unique_lock<std::mutex> lk(m_initMutex);
DeviceToken::DeviceType dType = token.getDeviceType();
if (dType == DeviceToken::DeviceType::USB)
DeviceType dType = token.getDeviceType();
if (dType == DeviceType::USB)
m_thread = std::thread(_threadProcUSBLL, this);
else if (dType == DeviceToken::DeviceType::Bluetooth)
else if (dType == DeviceType::Bluetooth)
m_thread = std::thread(_threadProcBTLL, this);
else if (dType == DeviceToken::DeviceType::GenericHID)
else if (dType == DeviceType::HID)
m_thread = std::thread(_threadProcHID, this);
else
{
@@ -251,9 +368,9 @@ public:
};
IHIDDevice* IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
std::unique_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
{
return new HIDDeviceIOKit(token, devImp);
return std::make_unique<HIDDeviceIOKit>(token, devImp);
}
}