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

167
lib/inputdev/CFPointer.hpp Normal file
View File

@@ -0,0 +1,167 @@
#ifndef __CFPOINTER_HPP__
#define __CFPOINTER_HPP__
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFPlugInCOM.h>
#include <utility>
/// A smart pointer that can manage the lifecycle of Core Foundation objects.
template<typename T>
class CFPointer {
public:
CFPointer() : storage(nullptr) { }
CFPointer(T pointer) : storage(toStorageType(pointer)) {
if (storage) {
CFRetain(storage);
}
}
CFPointer(const CFPointer & other) : storage(other.storage) {
if (CFTypeRef ptr = storage) {
CFRetain(ptr);
}
}
CFPointer(CFPointer && other) : storage(std::exchange(other.storage, nullptr)) { }
~CFPointer() {
if (CFTypeRef pointer = storage) {
CFRelease(pointer);
}
}
static inline CFPointer<T> adopt(T CF_RELEASES_ARGUMENT ptr);
T get() const;
CFPointer &operator=(CFPointer);
CFTypeRef* operator&()
{
if (CFTypeRef pointer = storage) {
CFRelease(pointer);
}
return &storage;
}
operator bool() const { return storage != nullptr; }
private:
CFTypeRef storage;
enum AdoptTag { Adopt };
CFPointer(T ptr, AdoptTag) : storage(toStorageType(ptr)) { }
inline CFTypeRef toStorageType(CFTypeRef ptr) const {
return (CFTypeRef)ptr;
}
inline T fromStorageType(CFTypeRef pointer) const {
return (T)pointer;
}
void swap(CFPointer &);
};
template<typename T>
CFPointer<T> CFPointer<T>::adopt(T CF_RELEASES_ARGUMENT ptr) {
return CFPointer<T>(ptr, CFPointer<T>::Adopt);
}
template<typename T>
T CFPointer<T>::get() const {
return fromStorageType(storage);
}
template<typename T>
inline CFPointer<T>& CFPointer<T>::operator=(CFPointer other) {
swap(other);
return *this;
}
template<typename T>
inline void CFPointer<T>::swap(CFPointer &other) {
std::swap(storage, other.storage);
}
/// A smart pointer that can manage the lifecycle of CoreFoundation IUnknown objects.
template<typename T>
class IUnknownPointer {
public:
IUnknownPointer() : _storage(nullptr) { }
IUnknownPointer(T** pointer) : _storage(toStorageType(pointer)) {
if (_storage) {
(*pointer)->AddRef(pointer);
}
}
IUnknownPointer(const IUnknownPointer & other) : _storage(other._storage) {
if (IUnknownVTbl** ptr = _storage) {
(*ptr)->AddRef(ptr);
}
}
IUnknownPointer& operator=(const IUnknownPointer & other) {
if (IUnknownVTbl** pointer = _storage) {
(*pointer)->Release(pointer);
}
_storage = other._storage;
if (IUnknownVTbl** ptr = _storage) {
(*ptr)->AddRef(ptr);
}
return *this;
}
IUnknownPointer(IUnknownPointer && other) : _storage(std::exchange(other._storage, nullptr)) { }
~IUnknownPointer() {
if (IUnknownVTbl** pointer = _storage) {
(*pointer)->Release(pointer);
}
}
static inline IUnknownPointer<T> adopt(T** ptr);
T* get() const;
T* operator->() const { return get(); }
T** storage() const { return (T**)_storage; }
LPVOID* operator&()
{
if (IUnknownVTbl** pointer = _storage) {
printf("%p RELEASE %d\n", pointer, (*pointer)->Release(pointer));
}
return (LPVOID*)&_storage;
}
operator bool() const { return _storage != nullptr; }
private:
IUnknownVTbl** _storage;
enum AdoptTag { Adopt };
IUnknownPointer(T** ptr, AdoptTag) : _storage(toStorageType(ptr)) { }
inline IUnknownVTbl** toStorageType(T** ptr) const {
return (IUnknownVTbl**)ptr;
}
inline T* fromStorageType(IUnknownVTbl** pointer) const {
return *(T**)pointer;
}
void swap(IUnknownPointer &);
};
template<typename T>
IUnknownPointer<T> IUnknownPointer<T>::adopt(T** ptr) {
return IUnknownPointer<T>(ptr, IUnknownPointer<T>::Adopt);
}
template<typename T>
T* IUnknownPointer<T>::get() const {
return fromStorageType(_storage);
}
template<typename T>
inline void IUnknownPointer<T>::swap(IUnknownPointer &other) {
std::swap(_storage, other._storage);
}
#endif // __CFPOINTER_HPP__

View File

@@ -7,24 +7,22 @@ namespace boo
{
DeviceBase::DeviceBase(DeviceToken* token)
: m_token(token), m_hidDev(NULL)
: m_token(token)
{
}
DeviceBase::~DeviceBase()
{
delete m_hidDev;
}
void DeviceBase::_deviceDisconnected()
{
deviceDisconnected();
m_token = NULL;
m_token = nullptr;
if (m_hidDev)
{
m_hidDev->_deviceDisconnected();
delete m_hidDev;
m_hidDev = NULL;
m_hidDev.reset();
}
}
@@ -56,18 +54,18 @@ size_t DeviceBase::receiveUSBInterruptTransfer(uint8_t* data, size_t length)
return false;
}
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, uint16_t message)
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{
if (m_hidDev)
return m_hidDev->_sendHIDReport(data, length, message);
return m_hidDev->_sendHIDReport(data, length, tp, message);
return false;
}
size_t DeviceBase::receiveReport(uint8_t* data, size_t length, uint16_t message)
size_t DeviceBase::receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{
if (m_hidDev)
return m_hidDev->_recieveReport(data, length, message);
return false;
return m_hidDev->_receiveHIDReport(data, length, tp, message);
return 0;
}
}

View File

@@ -11,8 +11,16 @@ extern const DeviceSignature BOO_DEVICE_SIGS[];
bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSignatureSet& sigSet)
{
if (token.getDeviceType() == DeviceToken::DeviceType::GenericHID)
if (token.getDeviceType() == DeviceType::HID)
{
for (const DeviceSignature* sig : sigSet)
{
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId() &&
sig->m_type != DeviceType::HID)
return false;
}
return true;
}
for (const DeviceSignature* sig : sigSet)
{
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId())
@@ -21,30 +29,13 @@ bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSi
return false;
}
IHIDDevice* IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp);
DeviceBase* DeviceSignature::DeviceNew(DeviceToken& token)
std::unique_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp);
std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token)
{
DeviceBase* retval = NULL;
std::shared_ptr<DeviceBase> retval;
/* Early-return for generic HID devices */
if (token.getDeviceType() == DeviceToken::DeviceType::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;
/* Perform signature-matching to find the appropriate device-factory */
const DeviceSignature* foundSig = nullptr;
const DeviceSignature* sigIter = BOO_DEVICE_SIGS;
unsigned targetVid = token.getVendorId();
unsigned targetPid = token.getProductId();
@@ -58,21 +49,35 @@ DeviceBase* DeviceSignature::DeviceNew(DeviceToken& token)
++sigIter;
}
if (!foundSig)
return NULL;
{
/* Try Generic HID devices */
if (token.getDeviceType() == DeviceType::HID)
{
retval = std::make_shared<GenericPad>(&token);
if (!retval)
return nullptr;
retval->m_hidDev = IHIDDeviceNew(token, *retval);
if (!retval->m_hidDev)
return nullptr;
return retval;
}
return nullptr;
}
if (foundSig->m_type != DeviceType::None && foundSig->m_type != token.getDeviceType())
return nullptr;
retval = foundSig->m_factory(&token);
if (!retval)
return NULL;
return nullptr;
IHIDDevice* newDev = IHIDDeviceNew(token, *retval);
if (!newDev)
{
delete retval;
return NULL;
}
retval->m_hidDev = IHIDDeviceNew(token, *retval);
if (!retval->m_hidDev)
return nullptr;
return retval;
}
}

View File

@@ -50,23 +50,31 @@ void DualshockPad::deviceDisconnected()
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))
if (!sendHIDReport(setupCommand, sizeof(setupCommand), HIDReportType::Feature, 0xF4))
{
deviceError("Unable to send complete packet! Request size %x\n", sizeof(setupCommand));
return;
}
uint8_t btAddr[8];
receiveReport(btAddr, sizeof(btAddr), 0x03F5);
receiveHIDReport(btAddr, sizeof(btAddr), HIDReportType::Feature, 0xF5);
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()
{
DualshockPadState state;
size_t recvSz = receiveUSBInterruptTransfer((uint8_t*)&state, 49);
if (recvSz != 49)
}
void DualshockPad::finalCycle()
{
}
void DualshockPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{
if (message != 1 || length != 49 || tp != HIDReportType::Input)
return;
DualshockPadState state = *reinterpret_cast<const DualshockPadState*>(data);
for (int i = 0; i < 3; i++)
state.m_accelerometer[i] = bswap16(state.m_accelerometer[i]);
@@ -98,7 +106,7 @@ void DualshockPad::transferCycle()
m_report.rumble.rightDuration = 0;
m_report.rumble.rightOn = false;
}
sendHIDReport(m_report.buf, sizeof(m_report), 0x0201);
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
m_rumbleState = m_rumbleRequest;
}
else
@@ -116,12 +124,6 @@ void DualshockPad::transferCycle()
state.accYaw = (atan2(accXval, accZval) + M_PI) * RAD_TO_DEG;
state.gyroZ = (state.m_gyrometerZ / 1023.f);
}
}
void DualshockPad::finalCycle()
{
}
} // boo

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);
}
}

View File

@@ -2,9 +2,13 @@
#include "boo/inputdev/DeviceFinder.hpp"
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDDevicePlugin.h>
#include <IOKit/hid/IOHIDKeys.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOCFPlugIn.h>
#include "IOKitPointer.hpp"
#include "CFPointer.hpp"
namespace boo
{
@@ -13,7 +17,7 @@ namespace boo
* Reference: http://oroboro.com/usb-serial-number-osx/
*/
static bool getUSBStringDescriptor(IOUSBDeviceInterface182** usbDevice, UInt8 idx, char* out)
static bool getUSBStringDescriptor(const IUnknownPointer<IOUSBDeviceInterface182>& usbDevice, UInt8 idx, char* out)
{
UInt16 buffer[128];
@@ -30,7 +34,7 @@ static bool getUSBStringDescriptor(IOUSBDeviceInterface182** usbDevice, UInt8 id
request.wLength = sizeof(buffer);
request.pData = buffer;
kern_return_t err = (*usbDevice)->DeviceRequest(usbDevice, &request);
kern_return_t err = usbDevice->DeviceRequest(usbDevice.storage(), &request);
if (err != 0)
{
// the request failed... fairly uncommon for the USB disk driver, but not
@@ -61,72 +65,65 @@ class HIDListenerIOKit : public IHIDListener
CFRunLoopRef m_listenerRunLoop;
IONotificationPortRef m_llPort;
io_iterator_t m_llAddNotif, m_llRemoveNotif;
IOObjectPointer<io_iterator_t> m_llAddNotif, m_llRemoveNotif;
IOObjectPointer<io_iterator_t> m_hidAddNotif, m_hidRemoveNotif;
bool m_scanningEnabled;
static void devicesConnectedUSBLL(HIDListenerIOKit* listener,
io_iterator_t iterator)
{
io_object_t obj;
while ((obj = IOIteratorNext(iterator)))
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
{
io_string_t devPath;
if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0)
if (IORegistryEntryGetPath(obj.get(), 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)
{
fprintf(stderr, "unable to open IOKit plugin interface\n");
return;
}
IOUSBDeviceInterface182 **dev;
err = (*devServ)->QueryInterface(devServ,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182),
(LPVOID*)&dev);
if (err != kIOReturnSuccess)
{
fprintf(stderr, "unable to open IOKit device interface\n");
return;
}
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);
{
IOCFPluginPointer devServ;
SInt32 score;
IOReturn err;
err = IOCreatePlugInInterfaceForService(obj.get(), kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
if (err != kIOReturnSuccess)
{
fprintf(stderr, "unable to open IOKit plugin interface\n");
continue;
}
if (!listener->m_finder._insertToken(DeviceToken(DeviceToken::DeviceType::USB,
vid, pid, vstr, pstr, devPath)))
IUnknownPointer<IOUSBDeviceInterface182> dev;
err = devServ.As(&dev, kIOUSBDeviceInterfaceID182);
if (err != kIOReturnSuccess)
{
fprintf(stderr, "unable to open IOKit device interface\n");
continue;
}
dev->GetDeviceVendor(dev.storage(), &vid);
dev->GetDeviceProduct(dev.storage(), &pid);
UInt8 vstridx, pstridx;
dev->USBGetManufacturerStringIndex(dev.storage(), &vstridx);
dev->USBGetProductStringIndex(dev.storage(), &pstridx);
getUSBStringDescriptor(dev, vstridx, vstr);
getUSBStringDescriptor(dev, pstridx, pstr);
}
if (!listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::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(HIDListenerIOKit* listener,
@@ -140,15 +137,114 @@ class HIDListenerIOKit : public IHIDListener
CFRunLoopWakeUp(listener->m_listenerRunLoop);
return;
}
io_object_t obj;
while ((obj = IOIteratorNext(iterator)))
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
{
io_string_t devPath;
if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0)
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
continue;
listener->m_finder._removeToken(devPath);
//printf("REMOVED %08X %s\n", obj, devPath);
}
}
static void devicesConnectedHID(HIDListenerIOKit* listener,
io_iterator_t iterator)
{
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
{
io_string_t devPath;
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
continue;
if (!listener->m_scanningEnabled ||
listener->m_finder._hasToken(devPath))
continue;
unsigned vidv, pidv;
char vstr[128] = {0};
char pstr[128] = {0};
{
IOCFPluginPointer devServ;
SInt32 score;
IOReturn err;
err = IOCreatePlugInInterfaceForService(obj.get(), kIOHIDDeviceTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
if (err != kIOReturnSuccess)
{
fprintf(stderr, "unable to open IOKit plugin interface\n");
continue;
}
IUnknownPointer<IOHIDDeviceDeviceInterface> dev;
err = devServ.As(&dev, kIOHIDDeviceDeviceInterfaceID);
if (err != kIOReturnSuccess)
{
fprintf(stderr, "unable to open IOKit device interface\n");
continue;
}
/* Game controllers only */
CFPointer<CFNumberRef> usagePage;
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsagePageKey), &usagePage);
CFPointer<CFNumberRef> usage;
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsageKey), &usage);
int usagePageV, usageV;
CFNumberGetValue(usagePage.get(), kCFNumberIntType, &usagePageV);
CFNumberGetValue(usage.get(), kCFNumberIntType, &usageV);
if (usagePageV == kHIDPage_GenericDesktop)
{
if (usageV != kHIDUsage_GD_Joystick && usageV != kHIDUsage_GD_GamePad)
continue;
}
else if (usagePageV != kHIDPage_Game)
{
continue;
}
CFPointer<CFNumberRef> vid, pid;
dev->getProperty(dev.storage(), CFSTR(kIOHIDVendorIDKey), &vid);
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductIDKey), &pid);
CFNumberGetValue(vid.get(), kCFNumberIntType, &vidv);
CFNumberGetValue(pid.get(), kCFNumberIntType, &pidv);
CFPointer<CFStringRef> vstridx, pstridx;
dev->getProperty(dev.storage(), CFSTR(kIOHIDManufacturerKey), &vstridx);
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductKey), &pstridx);
if (vstridx)
CFStringGetCString(vstridx.get(), vstr, 128, kCFStringEncodingUTF8);
if (pstridx)
CFStringGetCString(pstridx.get(), pstr, 128, kCFStringEncodingUTF8);
}
if (!listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::HID,
vidv, pidv, vstr, pstr, devPath)))
{
/* Matched-insertion failed; see if generic HID interface is available */
/* TODO: Do */
}
//printf("ADDED %08X %s\n", obj, devPath);
}
}
static void devicesDisconnectedHID(HIDListenerIOKit* listener,
io_iterator_t iterator)
{
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop)
{
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{
devicesDisconnectedHID(listener, iterator);
});
CFRunLoopWakeUp(listener->m_listenerRunLoop);
return;
}
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
{
io_string_t devPath;
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
continue;
listener->m_finder._removeToken(devPath);
//printf("REMOVED %08X %s\n", obj, devPath);
IOObjectRelease(obj);
}
}
@@ -156,38 +252,54 @@ public:
HIDListenerIOKit(DeviceFinder& 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);
/* Register HID Matcher */
{
CFMutableDictionaryRef matchDict = IOServiceMatching("IOHIDDevice");
CFRetain(matchDict);
kern_return_t hidRet =
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
(IOServiceMatchingCallback)devicesConnectedHID, this, &m_hidAddNotif);
if (hidRet == kIOReturnSuccess)
devicesConnectedHID(this, m_hidAddNotif.get());
hidRet =
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
(IOServiceMatchingCallback)devicesDisconnectedHID, this, &m_hidRemoveNotif);
if (hidRet == kIOReturnSuccess)
devicesDisconnectedHID(this, m_hidRemoveNotif.get());
}
/* Register Low-Level USB Matcher */
{
CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName);
CFRetain(matchDict);
kern_return_t llRet =
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
(IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif);
if (llRet == kIOReturnSuccess)
devicesConnectedUSBLL(this, m_llAddNotif.get());
llRet =
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
(IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif);
if (llRet == kIOReturnSuccess)
devicesDisconnectedUSBLL(this, m_llRemoveNotif.get());
}
m_scanningEnabled = false;
}
~HIDListenerIOKit()
{
//CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
IOObjectRelease(m_llAddNotif);
IOObjectRelease(m_llRemoveNotif);
IONotificationPortDestroy(m_llPort);
}
@@ -206,12 +318,11 @@ public:
/* Manual device scanning */
bool scanNow()
{
io_iterator_t iter;
IOObjectPointer<io_iterator_t> iter;
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching(kIOUSBDeviceClassName), &iter) == kIOReturnSuccess)
{
devicesConnectedUSBLL(this, iter);
IOObjectRelease(iter);
devicesConnectedUSBLL(this, iter.get());
}
return true;
}

View File

@@ -2,6 +2,7 @@
#define IHIDDEVICE_HPP
#include "boo/inputdev/DeviceToken.hpp"
#include "boo/inputdev/DeviceBase.hpp"
namespace boo
{
@@ -12,8 +13,8 @@ class IHIDDevice
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) {return 0;}
virtual bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
virtual size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
public:
inline virtual ~IHIDDevice() {}
};

View File

@@ -0,0 +1,132 @@
#ifndef __IOKITPOINTER_HPP__
#define __IOKITPOINTER_HPP__
#include "CFPointer.hpp"
#include <IOKit/IOTypes.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <utility>
/// A smart pointer that can manage the lifecycle of IOKit objects.
template<typename T>
class IOObjectPointer {
public:
IOObjectPointer() : storage(0) { }
IOObjectPointer(T pointer) : storage(toStorageType(pointer)) {
if (storage) {
IOObjectRetain(storage);
}
}
IOObjectPointer(const IOObjectPointer & other) : storage(other.storage) {
if (io_object_t ptr = storage) {
IOObjectRetain(ptr);
}
}
IOObjectPointer& operator=(const IOObjectPointer & other) {
if (io_object_t pointer = storage) {
IOObjectRelease(pointer);
}
storage = other.storage;
if (io_object_t ptr = storage) {
IOObjectRetain(ptr);
}
return *this;
}
IOObjectPointer(IOObjectPointer && other) : storage(std::exchange(other.storage, 0)) { }
~IOObjectPointer() {
if (io_object_t pointer = storage) {
IOObjectRelease(pointer);
}
}
static inline IOObjectPointer<T> adopt(T ptr);
T get() const;
io_object_t* operator&()
{
if (io_object_t pointer = storage) {
IOObjectRelease(pointer);
}
return &storage;
}
operator bool() const { return storage != 0; }
private:
io_object_t storage;
enum AdoptTag { Adopt };
IOObjectPointer(T ptr, AdoptTag) : storage(toStorageType(ptr)) { }
inline io_object_t toStorageType(io_object_t ptr) const {
return (io_object_t)ptr;
}
inline T fromStorageType(io_object_t pointer) const {
return (T)pointer;
}
void swap(IOObjectPointer &);
};
template<typename T>
IOObjectPointer<T> IOObjectPointer<T>::adopt(T ptr) {
return IOObjectPointer<T>(ptr, IOObjectPointer<T>::Adopt);
}
template<typename T>
T IOObjectPointer<T>::get() const {
return fromStorageType(storage);
}
template<typename T>
inline void IOObjectPointer<T>::swap(IOObjectPointer &other) {
std::swap(storage, other.storage);
}
/// A smart pointer that can manage the lifecycle of IOKit plugin objects.
class IOCFPluginPointer {
public:
IOCFPluginPointer() : _storage(nullptr) { }
IOCFPluginPointer(const IOCFPluginPointer & other) = delete;
IOCFPluginPointer(IOCFPluginPointer && other) : _storage(std::exchange(other._storage, nullptr)) { }
~IOCFPluginPointer() {
if (IOCFPlugInInterface** pointer = _storage) {
IODestroyPlugInInterface(pointer);
}
}
IOCFPlugInInterface*** operator&()
{
if (IOCFPlugInInterface** pointer = _storage) {
IODestroyPlugInInterface(pointer);
}
return &_storage;
}
HRESULT As(LPVOID* p, CFUUIDRef uuid) const
{
(*_storage)->AddRef(_storage); // Needed for some reason
return (*_storage)->QueryInterface(_storage, CFUUIDGetUUIDBytes(uuid), p);
}
operator bool() const { return _storage != nullptr; }
IOCFPlugInInterface** storage() const { return _storage; }
private:
IOCFPlugInInterface** _storage;
void swap(IOCFPluginPointer &);
};
inline void IOCFPluginPointer::swap(IOCFPluginPointer &other) {
std::swap(_storage, other._storage);
}
#endif // __IOKITPOINTER_HPP__