macOS IOKit interface fixes; callback-change mutex for controllers

This commit is contained in:
Jack Andersen 2017-12-15 13:35:54 -10:00
parent cb99c05284
commit e0aa15610b
15 changed files with 87 additions and 52 deletions

View File

@ -6,6 +6,7 @@
#include <stdio.h> #include <stdio.h>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <mutex>
#include "boo/System.hpp" #include "boo/System.hpp"
#if _WIN32 #if _WIN32
@ -36,7 +37,7 @@ class DeviceBase : public std::enable_shared_from_this<DeviceBase>
public: public:
DeviceBase(DeviceToken* token); DeviceBase(DeviceToken* token);
virtual ~DeviceBase(); virtual ~DeviceBase() = default;
void closeDevice(); void closeDevice();
/* Callbacks */ /* Callbacks */
@ -63,6 +64,21 @@ public:
virtual void receivedHIDReport(const uint8_t* /*data*/, size_t /*length*/, HIDReportType /*tp*/, uint32_t /*message*/) {} virtual void receivedHIDReport(const uint8_t* /*data*/, size_t /*length*/, HIDReportType /*tp*/, uint32_t /*message*/) {}
}; };
template <class CB>
class TDeviceBase : public DeviceBase
{
protected:
std::mutex m_callbackLock;
CB* m_callback = nullptr;
public:
TDeviceBase(DeviceToken* token) : DeviceBase(token) {}
void setCallback(CB* cb)
{
std::lock_guard<std::mutex> lk(m_callbackLock);
m_callback = cb;
}
};
} }
#endif // CDEVICEBASE #endif // CDEVICEBASE

View File

@ -112,7 +112,7 @@ public:
} }
} }
} }
~DeviceFinder() virtual ~DeviceFinder()
{ {
if (m_listener) if (m_listener)
m_listener->stopScanning(); m_listener->stopScanning();

View File

@ -60,9 +60,10 @@ struct IDolphinSmashAdapterCallback
const DolphinControllerState& state) {(void)idx;(void)type;(void)state;} const DolphinControllerState& state) {(void)idx;(void)type;(void)state;}
}; };
class DolphinSmashAdapter final : public DeviceBase class DolphinSmashAdapter final : public TDeviceBase<IDolphinSmashAdapterCallback>
{ {
IDolphinSmashAdapterCallback* m_callback = nullptr; int16_t m_leftStickCal[2] = {0x7f};
int16_t m_rightStickCal[2] = {0x7f};
uint8_t m_knownControllers = 0; uint8_t m_knownControllers = 0;
uint8_t m_rumbleRequest = 0; uint8_t m_rumbleRequest = 0;
bool m_hardStop[4] = {false}; bool m_hardStop[4] = {false};
@ -76,7 +77,10 @@ public:
~DolphinSmashAdapter(); ~DolphinSmashAdapter();
void setCallback(IDolphinSmashAdapterCallback* cb) void setCallback(IDolphinSmashAdapterCallback* cb)
{m_callback = cb; m_knownControllers = 0;} {
TDeviceBase<IDolphinSmashAdapterCallback>::setCallback(cb);
m_knownControllers = 0;
}
void startRumble(unsigned idx) void startRumble(unsigned idx)
{if (idx >= 4) return; m_rumbleRequest |= 1<<idx;} {if (idx >= 4) return; m_rumbleRequest |= 1<<idx;}
void stopRumble(unsigned idx, bool hard=false) void stopRumble(unsigned idx, bool hard=false)

View File

@ -122,9 +122,8 @@ struct IDualshockPadCallback
virtual void controllerUpdate(DualshockPad&, const DualshockPadState&) {} virtual void controllerUpdate(DualshockPad&, const DualshockPadState&) {}
}; };
class DualshockPad final : public DeviceBase class DualshockPad final : public TDeviceBase<IDualshockPadCallback>
{ {
IDualshockPadCallback* m_callback;
EDualshockMotor m_rumbleRequest; EDualshockMotor m_rumbleRequest;
EDualshockMotor m_rumbleState; EDualshockMotor m_rumbleState;
uint8_t m_rumbleDuration[2]; uint8_t m_rumbleDuration[2];
@ -140,8 +139,6 @@ public:
DualshockPad(DeviceToken* token); DualshockPad(DeviceToken* token);
~DualshockPad(); ~DualshockPad();
void setCallback(IDualshockPadCallback* cb) { m_callback = cb; }
void startRumble(EDualshockMotor motor, uint8_t duration = 254, uint8_t intensity=255) void startRumble(EDualshockMotor motor, uint8_t duration = 254, uint8_t intensity=255)
{ {
m_rumbleRequest |= motor; m_rumbleRequest |= motor;

View File

@ -16,15 +16,13 @@ struct IGenericPadCallback
virtual void valueUpdate(const HIDMainItem& item, int32_t value) {} virtual void valueUpdate(const HIDMainItem& item, int32_t value) {}
}; };
class GenericPad final : public DeviceBase class GenericPad final : public TDeviceBase<IGenericPadCallback>
{ {
HIDParser m_parser; HIDParser m_parser;
IGenericPadCallback* m_cb = nullptr;
public: public:
GenericPad(DeviceToken* token); GenericPad(DeviceToken* token);
~GenericPad(); ~GenericPad();
void setCallback(IGenericPadCallback* cb) { m_cb = cb; }
void deviceDisconnected(); void deviceDisconnected();
void initialCycle(); void initialCycle();
void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message); void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);

View File

@ -37,9 +37,8 @@ struct INintendoPowerACallback
virtual void controllerUpdate(const NintendoPowerAState& state) {} virtual void controllerUpdate(const NintendoPowerAState& state) {}
}; };
class NintendoPowerA final : public DeviceBase class NintendoPowerA final : public TDeviceBase<INintendoPowerACallback>
{ {
INintendoPowerACallback* m_callback = nullptr;
NintendoPowerAState m_last; NintendoPowerAState m_last;
void deviceDisconnected(); void deviceDisconnected();
void initialCycle(); void initialCycle();
@ -49,8 +48,6 @@ class NintendoPowerA final : public DeviceBase
public: public:
NintendoPowerA(DeviceToken*); NintendoPowerA(DeviceToken*);
~NintendoPowerA(); ~NintendoPowerA();
void setCallback(INintendoPowerACallback* cb) { m_callback = cb; }
}; };
} }

View File

@ -33,17 +33,16 @@ struct IXInputPadCallback
virtual void controllerUpdate(XInputPad& pad, const XInputPadState&) {} virtual void controllerUpdate(XInputPad& pad, const XInputPadState&) {}
}; };
class XInputPad final : public DeviceBase class XInputPad final : public TDeviceBase<IXInputPadCallback>
{ {
friend class HIDListenerWinUSB; friend class HIDListenerWinUSB;
IXInputPadCallback* m_callback;
uint16_t m_rumbleRequest[2] = {}; uint16_t m_rumbleRequest[2] = {};
uint16_t m_rumbleState[2] = {}; uint16_t m_rumbleState[2] = {};
public: public:
XInputPad(DeviceToken* token) : DeviceBase(token) {} XInputPad(DeviceToken* token) : TDeviceBase<IXInputPadCallback>(token) {}
void setCallback(IXInputPadCallback* cb) { m_callback = cb; }
void deviceDisconnected() void deviceDisconnected()
{ {
std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback) if (m_callback)
m_callback->controllerDisconnected(); m_callback->controllerDisconnected();
} }

View File

@ -11,10 +11,6 @@ DeviceBase::DeviceBase(DeviceToken* token)
{ {
} }
DeviceBase::~DeviceBase()
{
}
void DeviceBase::_deviceDisconnected() void DeviceBase::_deviceDisconnected()
{ {
deviceDisconnected(); deviceDisconnected();

View File

@ -8,7 +8,7 @@ namespace boo
* Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c * Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c
*/ */
DolphinSmashAdapter::DolphinSmashAdapter(DeviceToken* token) : DeviceBase(token) {} DolphinSmashAdapter::DolphinSmashAdapter(DeviceToken* token) : TDeviceBase<IDolphinSmashAdapterCallback>(token) {}
DolphinSmashAdapter::~DolphinSmashAdapter() {} DolphinSmashAdapter::~DolphinSmashAdapter() {}
@ -61,6 +61,7 @@ void DolphinSmashAdapter::transferCycle()
// printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]); // printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]);
std::lock_guard<std::mutex> lk(m_callbackLock);
if (!m_callback) if (!m_callback)
return; return;
@ -74,6 +75,10 @@ void DolphinSmashAdapter::transferCycle()
EDolphinControllerType type = parseState(&state, controller, rumble); EDolphinControllerType type = parseState(&state, controller, rumble);
if (type != EDolphinControllerType::None && !(m_knownControllers & 1 << i)) if (type != EDolphinControllerType::None && !(m_knownControllers & 1 << i))
{ {
m_leftStickCal[0] = reinterpret_cast<uint8_t&>(state.m_leftStick[0]);
m_leftStickCal[1] = reinterpret_cast<uint8_t&>(state.m_leftStick[1]);
m_rightStickCal[0] = reinterpret_cast<uint8_t&>(state.m_rightStick[0]);
m_rightStickCal[1] = reinterpret_cast<uint8_t&>(state.m_rightStick[1]);
m_knownControllers |= 1 << i; m_knownControllers |= 1 << i;
m_callback->controllerConnected(i, type); m_callback->controllerConnected(i, type);
} }
@ -83,7 +88,13 @@ void DolphinSmashAdapter::transferCycle()
m_callback->controllerDisconnected(i); m_callback->controllerDisconnected(i);
} }
if (m_knownControllers & 1 << i) if (m_knownControllers & 1 << i)
{
state.m_leftStick[0] = int8_t(reinterpret_cast<uint8_t&>(state.m_leftStick[0]) - m_leftStickCal[0]);
state.m_leftStick[1] = int8_t(reinterpret_cast<uint8_t&>(state.m_leftStick[1]) - m_leftStickCal[1]);
state.m_rightStick[0] = int8_t(reinterpret_cast<uint8_t&>(state.m_rightStick[0]) - m_rightStickCal[0]);
state.m_rightStick[1] = int8_t(reinterpret_cast<uint8_t&>(state.m_rightStick[1]) - m_rightStickCal[1]);
m_callback->controllerUpdate(i, type, state); m_callback->controllerUpdate(i, type, state);
}
rumbleMask |= rumble ? 1 << i : 0; rumbleMask |= rumble ? 1 << i : 0;
} }
@ -115,13 +126,13 @@ void DolphinSmashAdapter::finalCycle()
void DolphinSmashAdapter::deviceDisconnected() void DolphinSmashAdapter::deviceDisconnected()
{ {
if (!m_callback)
return;
for (uint32_t i = 0; i < 4; i++) for (uint32_t i = 0; i < 4; i++)
{ {
if (m_knownControllers & 1 << i) if (m_knownControllers & 1 << i)
{ {
m_knownControllers &= ~(1 << i); m_knownControllers &= ~(1 << i);
std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback)
m_callback->controllerDisconnected(i); m_callback->controllerDisconnected(i);
} }
} }

View File

@ -29,8 +29,7 @@ static const uint8_t defaultReport[49] = {
}; };
DualshockPad::DualshockPad(DeviceToken* token) DualshockPad::DualshockPad(DeviceToken* token)
: DeviceBase(token), : TDeviceBase<IDualshockPadCallback>(token),
m_callback(nullptr),
m_rumbleRequest(EDualshockMotor::None), m_rumbleRequest(EDualshockMotor::None),
m_rumbleState(EDualshockMotor::None) m_rumbleState(EDualshockMotor::None)
{ {
@ -44,6 +43,7 @@ DualshockPad::~DualshockPad()
void DualshockPad::deviceDisconnected() void DualshockPad::deviceDisconnected()
{ {
std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback) if (m_callback)
m_callback->controllerDisconnected(); m_callback->controllerDisconnected();
} }
@ -95,8 +95,11 @@ void DualshockPad::receivedHIDReport(const uint8_t* data, size_t length, HIDRepo
state.accYaw = (atan2(accXval, accZval) + M_PI) * RAD_TO_DEG; state.accYaw = (atan2(accXval, accZval) + M_PI) * RAD_TO_DEG;
state.gyroZ = (state.m_gyrometerZ / 1023.f); state.gyroZ = (state.m_gyrometerZ / 1023.f);
{
std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback) if (m_callback)
m_callback->controllerUpdate(*this, state); m_callback->controllerUpdate(*this, state);
}
if (m_rumbleRequest != m_rumbleState) if (m_rumbleRequest != m_rumbleState)
{ {

View File

@ -5,7 +5,7 @@ namespace boo
{ {
GenericPad::GenericPad(DeviceToken* token) GenericPad::GenericPad(DeviceToken* token)
: DeviceBase(token) : TDeviceBase<IGenericPadCallback>(token)
{ {
} }
@ -14,8 +14,9 @@ GenericPad::~GenericPad() {}
void GenericPad::deviceDisconnected() void GenericPad::deviceDisconnected()
{ {
if (m_cb) std::lock_guard<std::mutex> lk(m_callbackLock);
m_cb->controllerDisconnected(); if (m_callback)
m_callback->controllerDisconnected();
} }
void GenericPad::initialCycle() void GenericPad::initialCycle()
@ -29,18 +30,20 @@ void GenericPad::initialCycle()
std::vector<uint8_t> reportDesc = getReportDescriptor(); std::vector<uint8_t> reportDesc = getReportDescriptor();
m_parser.Parse(reportDesc.data(), reportDesc.size()); m_parser.Parse(reportDesc.data(), reportDesc.size());
#endif #endif
if (m_cb) std::lock_guard<std::mutex> lk(m_callbackLock);
m_cb->controllerConnected(); if (m_callback)
m_callback->controllerConnected();
} }
void GenericPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) void GenericPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{ {
if (length == 0 || tp != HIDReportType::Input || !m_cb) std::lock_guard<std::mutex> lk(m_callbackLock);
if (length == 0 || tp != HIDReportType::Input || !m_callback)
return; return;
std::function<bool(const HIDMainItem&, int32_t)> func = std::function<bool(const HIDMainItem&, int32_t)> func =
[this](const HIDMainItem& item, int32_t value) [this](const HIDMainItem& item, int32_t value)
{ {
m_cb->valueUpdate(item, value); m_callback->valueUpdate(item, value);
return true; return true;
}; };
m_parser.ScanValues(func, data, length); m_parser.ScanValues(func, data, length);

View File

@ -100,14 +100,15 @@ class HIDDeviceIOKit : public IHIDDevice
IOObjectPointer<io_iterator_t> devIter; IOObjectPointer<io_iterator_t> devIter;
IOObjectPointer<io_registry_entry_t> devEntry = IORegistryEntryFromPath(kIOMasterPortDefault, IOObjectPointer<io_registry_entry_t> devEntry = IORegistryEntryFromPath(kIOMasterPortDefault,
device->m_devPath.data()); device->m_devPath.data());
IORegistryEntryGetChildIterator(devEntry, kIOServicePlane, &devIter);
IOObjectPointer<io_object_t> interfaceEntry; IOObjectPointer<io_object_t> interfaceEntry;
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(devIter)) IORegistryEntryGetChildIterator(devEntry.get(), kIOServicePlane, &devIter);
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(devIter.get()))
{
if (IOObjectConformsTo(obj.get(), kIOUSBInterfaceClassName))
{ {
if (IOObjectConformsTo(obj, kIOUSBInterfaceClassName))
interfaceEntry = obj; interfaceEntry = obj;
else break;
IOObjectRelease(obj); }
} }
if (!interfaceEntry) if (!interfaceEntry)
{ {
@ -124,7 +125,7 @@ class HIDDeviceIOKit : public IHIDDevice
IOCFPluginPointer iodev; IOCFPluginPointer iodev;
SInt32 score; SInt32 score;
IOReturn err; IOReturn err;
err = IOCreatePlugInInterfaceForService(interfaceEntry, err = IOCreatePlugInInterfaceForService(interfaceEntry.get(),
kIOUSBInterfaceUserClientTypeID, kIOUSBInterfaceUserClientTypeID,
kIOCFPlugInInterfaceID, kIOCFPlugInInterfaceID,
&iodev, &iodev,

View File

@ -7,6 +7,7 @@
#include <IOKit/IOKitLib.h> #include <IOKit/IOKitLib.h>
#include <IOKit/usb/IOUSBLib.h> #include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOCFPlugIn.h> #include <IOKit/IOCFPlugIn.h>
#include <sys/utsname.h>
#include "IOKitPointer.hpp" #include "IOKitPointer.hpp"
#include "CFPointer.hpp" #include "CFPointer.hpp"
@ -67,6 +68,7 @@ class HIDListenerIOKit : public IHIDListener
IONotificationPortRef m_llPort; IONotificationPortRef m_llPort;
IOObjectPointer<io_iterator_t> m_llAddNotif, m_llRemoveNotif; IOObjectPointer<io_iterator_t> m_llAddNotif, m_llRemoveNotif;
IOObjectPointer<io_iterator_t> m_hidAddNotif, m_hidRemoveNotif; IOObjectPointer<io_iterator_t> m_hidAddNotif, m_hidRemoveNotif;
const char* m_usbClass;
bool m_scanningEnabled; bool m_scanningEnabled;
static void devicesConnectedUSBLL(HIDListenerIOKit* listener, static void devicesConnectedUSBLL(HIDListenerIOKit* listener,
@ -118,7 +120,7 @@ class HIDListenerIOKit : public IHIDListener
listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::USB, listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::USB,
vid, pid, vstr, pstr, devPath)); vid, pid, vstr, pstr, devPath));
//printf("ADDED %08X %s\n", obj, devPath); //printf("ADDED %08X %s\n", obj.get(), devPath);
} }
} }
@ -139,7 +141,7 @@ class HIDListenerIOKit : public IHIDListener
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0) if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
continue; continue;
listener->m_finder._removeToken(devPath); listener->m_finder._removeToken(devPath);
//printf("REMOVED %08X %s\n", obj, devPath); //printf("REMOVED %08X %s\n", obj.get(), devPath);
} }
} }
@ -244,6 +246,11 @@ public:
HIDListenerIOKit(DeviceFinder& finder) HIDListenerIOKit(DeviceFinder& finder)
: m_finder(finder) : m_finder(finder)
{ {
struct utsname kernInfo;
uname(&kernInfo);
int release = atoi(kernInfo.release);
m_usbClass = release >= 15 ? "IOUSBHostDevice" : kIOUSBDeviceClassName;
m_listenerRunLoop = CFRunLoopGetMain(); m_listenerRunLoop = CFRunLoopGetMain();
m_llPort = IONotificationPortCreate(kIOMasterPortDefault); m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort); CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
@ -270,7 +277,7 @@ public:
/* Register Low-Level USB Matcher */ /* Register Low-Level USB Matcher */
{ {
CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName); CFMutableDictionaryRef matchDict = IOServiceMatching(m_usbClass);
CFRetain(matchDict); CFRetain(matchDict);
kern_return_t llRet = kern_return_t llRet =
@ -312,7 +319,7 @@ public:
{ {
IOObjectPointer<io_iterator_t> iter; IOObjectPointer<io_iterator_t> iter;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, if (IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching(kIOUSBDeviceClassName), &iter) == kIOReturnSuccess) IOServiceMatching(m_usbClass), &iter) == kIOReturnSuccess)
{ {
devicesConnectedUSBLL(this, iter.get()); devicesConnectedUSBLL(this, iter.get());
} }

View File

@ -218,6 +218,7 @@ class HIDListenerWinUSB final : public IHIDListener
if (tok.m_connectedDev) if (tok.m_connectedDev)
{ {
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev); XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
std::lock_guard<std::mutex> lk(pad.m_callbackLock);
if (pad.m_callback) if (pad.m_callback)
pad.m_callback->controllerUpdate(pad, ConvertXInputState(state.Gamepad)); pad.m_callback->controllerUpdate(pad, ConvertXInputState(state.Gamepad));
} }

View File

@ -3,7 +3,7 @@
namespace boo namespace boo
{ {
NintendoPowerA::NintendoPowerA(DeviceToken* token) NintendoPowerA::NintendoPowerA(DeviceToken* token)
: DeviceBase(token) : TDeviceBase<INintendoPowerACallback>(token)
{ {
} }
@ -14,6 +14,7 @@ NintendoPowerA::~NintendoPowerA()
void NintendoPowerA::deviceDisconnected() void NintendoPowerA::deviceDisconnected()
{ {
std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback) if (m_callback)
m_callback->controllerDisconnected(); m_callback->controllerDisconnected();
} }
@ -29,6 +30,7 @@ void NintendoPowerA::transferCycle()
NintendoPowerAState state = *reinterpret_cast<NintendoPowerAState*>(&payload); NintendoPowerAState state = *reinterpret_cast<NintendoPowerAState*>(&payload);
std::lock_guard<std::mutex> lk(m_callbackLock);
if (state != m_last && m_callback) if (state != m_last && m_callback)
m_callback->controllerUpdate(state); m_callback->controllerUpdate(state);
m_last = state; m_last = state;