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

@ -81,7 +81,9 @@ elseif(APPLE)
PROPERTIES COMPILE_FLAGS -fobjc-arc)
list(APPEND PLAT_HDRS
include/boo/graphicsdev/Metal.hpp)
include/boo/graphicsdev/Metal.hpp
lib/inputdev/CFPointer.hpp
lib/inputdev/IOKitPointer.hpp)
find_library(APPKIT_LIBRARY AppKit)
find_library(IOKIT_LIBRARY IOKit)

View File

@ -7,8 +7,8 @@ namespace boo
const DeviceSignature BOO_DEVICE_SIGS[] =
{
DEVICE_SIG(DolphinSmashAdapter, 0x57e, 0x337),
DEVICE_SIG(DualshockPad, 0x54c, 0x268),
DEVICE_SIG(DolphinSmashAdapter, 0x57e, 0x337, DeviceType::USB),
DEVICE_SIG(DualshockPad, 0x54c, 0x268, DeviceType::HID),
DEVICE_SIG_SENTINEL()
};

View File

@ -4,39 +4,50 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "../System.hpp"
#include <memory>
namespace boo
{
class DeviceToken;
class IHIDDevice;
class DeviceBase
enum class HIDReportType
{
Input,
Output,
Feature
};
class DeviceBase : public std::enable_shared_from_this<DeviceBase>
{
friend class DeviceToken;
friend class HIDDeviceIOKit;
friend class HIDDeviceUdev;
friend class HIDDeviceWinUSB;
friend struct DeviceSignature;
class DeviceToken* m_token;
class IHIDDevice* m_hidDev;
std::unique_ptr<IHIDDevice> m_hidDev;
void _deviceDisconnected();
public:
DeviceBase(DeviceToken* token);
virtual ~DeviceBase();
void closeDevice();
/* Callbacks */
virtual void deviceDisconnected()=0;
virtual void deviceError(const char* error, ...);
/* Low-Level API */
bool sendUSBInterruptTransfer(const uint8_t* data, size_t length);
size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length);
virtual void initialCycle() {}
virtual void transferCycle() {}
virtual void finalCycle() {}
virtual size_t getInputBufferSize() const { return 0; }
/* Low-Level API */
bool sendUSBInterruptTransfer(const uint8_t* data, size_t length);
size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length);
/* High-Level API */
bool sendHIDReport(const uint8_t* data, size_t length, uint16_t message=0);
virtual size_t receiveReport(uint8_t* data, size_t length, uint16_t message=0);
bool sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0);
size_t receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0); // Prefer callback version
virtual void receivedHIDReport(const uint8_t* /*data*/, size_t /*length*/, HIDReportType /*tp*/, uint32_t /*message*/) {}
};
}

View File

@ -52,15 +52,15 @@ private:
return true;
return false;
}
inline bool _insertToken(DeviceToken&& token)
inline bool _insertToken(std::unique_ptr<DeviceToken>&& token)
{
if (DeviceSignature::DeviceMatchToken(token, m_types))
if (DeviceSignature::DeviceMatchToken(*token, m_types))
{
m_tokensLock.lock();
TInsertedDeviceToken inseredTok =
m_tokens.insert(std::make_pair(token.getDevicePath(), std::move(token)));
m_tokens.insert(std::make_pair(token->getDevicePath(), std::move(token)));
m_tokensLock.unlock();
deviceConnected(inseredTok.first->second);
deviceConnected(*inseredTok.first->second);
return true;
}
return false;
@ -70,10 +70,10 @@ private:
auto preCheck = m_tokens.find(path);
if (preCheck != m_tokens.end())
{
DeviceToken& tok = preCheck->second;
DeviceBase* dev = tok.m_connectedDev;
DeviceToken& tok = *preCheck->second;
std::shared_ptr<DeviceBase> dev = tok.m_connectedDev;
tok._deviceClose();
deviceDisconnected(tok, dev);
deviceDisconnected(tok, dev.get());
m_tokensLock.lock();
m_tokens.erase(preCheck);
m_tokensLock.unlock();

View File

@ -8,26 +8,38 @@
namespace boo
{
enum class DeviceType
{
None = 0,
USB = 1,
Bluetooth = 2,
HID = 3
};
class DeviceToken;
class DeviceBase;
struct DeviceSignature
{
typedef std::vector<const DeviceSignature*> TDeviceSignatureSet;
typedef std::function<DeviceBase*(DeviceToken*)> TFactoryLambda;
typedef std::function<std::shared_ptr<DeviceBase>(DeviceToken*)> TFactoryLambda;
const char* m_name;
std::type_index m_typeIdx;
unsigned m_vid, m_pid;
TFactoryLambda m_factory;
DeviceType m_type;
DeviceSignature() : m_name(NULL), m_typeIdx(typeid(DeviceSignature)) {} /* Sentinel constructor */
DeviceSignature(const char* name, std::type_index&& typeIdx, unsigned vid, unsigned pid, TFactoryLambda&& factory)
: m_name(name), m_typeIdx(typeIdx), m_vid(vid), m_pid(pid), m_factory(factory) {}
DeviceSignature(const char* name, std::type_index&& typeIdx, unsigned vid, unsigned pid,
TFactoryLambda&& factory, DeviceType type=DeviceType::None)
: m_name(name), m_typeIdx(typeIdx), m_vid(vid), m_pid(pid),
m_factory(factory), m_type(type) {}
static bool DeviceMatchToken(const DeviceToken& token, const TDeviceSignatureSet& sigSet);
static DeviceBase* DeviceNew(DeviceToken& token);
static std::shared_ptr<DeviceBase> DeviceNew(DeviceToken& token);
};
#define DEVICE_SIG(name, vid, pid) \
DeviceSignature(#name, typeid(name), vid, pid, [](DeviceToken* tok) -> DeviceBase* {return new name(tok);})
#define DEVICE_SIG(name, vid, pid, type) \
DeviceSignature(#name, typeid(name), vid, pid,\
[](DeviceToken* tok) -> std::shared_ptr<DeviceBase> {return std::make_shared<name>(tok);}, type)
#define DEVICE_SIG_SENTINEL() DeviceSignature()
extern const DeviceSignature BOO_DEVICE_SIGS[];

View File

@ -10,16 +10,7 @@ namespace boo
class DeviceToken
{
public:
enum class DeviceType
{
None = 0,
USB = 1,
Bluetooth = 2,
GenericHID = 3
};
private:
friend struct DeviceSignature;
DeviceType m_devType;
unsigned m_vendorId;
unsigned m_productId;
@ -28,7 +19,7 @@ private:
std::string m_devPath;
friend class DeviceBase;
DeviceBase* m_connectedDev;
std::shared_ptr<DeviceBase> m_connectedDev;
friend class DeviceFinder;
inline void _deviceClose()
@ -70,7 +61,7 @@ public:
inline const std::string& getProductName() const {return m_productName;}
inline const std::string& getDevicePath() const {return m_devPath;}
inline bool isDeviceOpen() const {return (m_connectedDev != NULL);}
inline DeviceBase* openAndGetDevice()
inline std::shared_ptr<DeviceBase> openAndGetDevice()
{
if (!m_connectedDev)
m_connectedDev = DeviceSignature::DeviceNew(*this);

View File

@ -3,6 +3,7 @@
#include <stdint.h>
#include "DeviceBase.hpp"
#include "../System.hpp"
namespace boo
{

View File

@ -1,8 +1,40 @@
#ifndef CDUALSHOCKPAD_HPP
#define CDUALSHOCKPAD_HPP
#include <stdint.h>
#include <type_traits>
#include "DeviceBase.hpp"
#ifndef ENABLE_BITWISE_ENUM
#define ENABLE_BITWISE_ENUM(type)\
constexpr type operator|(type a, type b)\
{\
using T = std::underlying_type_t<type>;\
return type(static_cast<T>(a) | static_cast<T>(b));\
}\
constexpr type operator&(type a, type b)\
{\
using T = std::underlying_type_t<type>;\
return type(static_cast<T>(a) & static_cast<T>(b));\
}\
inline type& operator|=(type& a, const type& b)\
{\
using T = std::underlying_type_t<type>;\
a = type(static_cast<T>(a) | static_cast<T>(b));\
return a;\
}\
inline type& operator&=(type& a, const type& b)\
{\
using T = std::underlying_type_t<type>;\
a = type(static_cast<T>(a) & static_cast<T>(b));\
return a;\
}\
inline type operator~(const type& key)\
{\
using T = std::underlying_type_t<type>;\
return type(~static_cast<T>(key));\
}
#endif
namespace boo
{
@ -134,6 +166,7 @@ class DualshockPad final : public DeviceBase
void initialCycle();
void transferCycle();
void finalCycle();
void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
public:
DualshockPad(DeviceToken* token);
~DualshockPad();
@ -179,8 +212,10 @@ public:
void setRawLED(int led)
{
m_report.leds = led;
sendHIDReport(m_report.buf, sizeof(m_report), 0x0201);
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x0201);
}
size_t getInputBufferSize() const { return 49; }
};
}

View File

@ -8,7 +8,7 @@
namespace boo
{
typedef std::unordered_map<std::string, DeviceToken> TDeviceTokens;
typedef std::unordered_map<std::string, std::unique_ptr<DeviceToken>> TDeviceTokens;
typedef std::pair<TDeviceTokens::iterator, bool> TInsertedDeviceToken;
class DeviceFinder;

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__

View File

@ -76,9 +76,8 @@ class DualshockPadCallback : public IDualshockPadCallback
class TestDeviceFinder : public DeviceFinder
{
DolphinSmashAdapter* smashAdapter = NULL;
DualshockPad* ds3 = nullptr;
std::shared_ptr<DolphinSmashAdapter> smashAdapter;
std::shared_ptr<DualshockPad> ds3;
DolphinSmashAdapterCallback m_cb;
DualshockPadCallback m_ds3CB;
public:
@ -87,14 +86,14 @@ public:
{}
void deviceConnected(DeviceToken& tok)
{
smashAdapter = dynamic_cast<DolphinSmashAdapter*>(tok.openAndGetDevice());
smashAdapter = std::dynamic_pointer_cast<DolphinSmashAdapter>(tok.openAndGetDevice());
if (smashAdapter)
{
smashAdapter->setCallback(&m_cb);
smashAdapter->startRumble(0);
return;
}
ds3 = dynamic_cast<DualshockPad*>(tok.openAndGetDevice());
ds3 = std::dynamic_pointer_cast<DualshockPad>(tok.openAndGetDevice());
if (ds3)
{
ds3->setCallback(&m_ds3CB);
@ -103,16 +102,10 @@ public:
}
void deviceDisconnected(DeviceToken&, DeviceBase* device)
{
if (smashAdapter == device)
{
delete smashAdapter;
smashAdapter = NULL;
}
if (ds3 == device)
{
delete ds3;
ds3 = nullptr;
}
if (smashAdapter.get() == device)
smashAdapter.reset();
if (ds3.get() == device)
ds3.reset();
}
};