eliminated some hid-thread race conditions

This commit is contained in:
Jack Andersen 2015-04-22 14:46:32 -10:00
parent c1dc218bd1
commit 92e7c3fb04
12 changed files with 131 additions and 26 deletions

View File

@ -11,8 +11,8 @@ class CDeviceBase
friend CDeviceToken;
void _deviceDisconnected();
public:
inline CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev)
: m_token(token), m_hidDev(hidDev) {}
CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev);
virtual ~CDeviceBase();
void closeDevice();
virtual void deviceDisconnected()=0;
};

View File

@ -55,8 +55,9 @@ private:
if (preCheck != m_tokens.end())
{
CDeviceToken& tok = preCheck->second;
CDeviceBase* dev = tok.m_connectedDev;
tok._deviceClose();
deviceDisconnected(tok);
deviceDisconnected(tok, dev);
m_tokensLock.lock();
m_tokens.erase(preCheck);
m_tokensLock.unlock();
@ -127,7 +128,7 @@ public:
}
virtual void deviceConnected(CDeviceToken&) {}
virtual void deviceDisconnected(CDeviceToken&) {}
virtual void deviceDisconnected(CDeviceToken&, CDeviceBase*) {}
};

View File

@ -26,7 +26,6 @@ class CDeviceToken
friend class CDeviceFinder;
inline void _deviceClose()
{
printf("CLOSE %p\n", this);
if (m_connectedDev)
m_connectedDev->_deviceDisconnected();
m_connectedDev = NULL;
@ -52,7 +51,6 @@ public:
inline bool isDeviceOpen() const {return m_connectedDev;}
inline CDeviceBase* openAndGetDevice()
{
printf("OPEN %p\n", this);
if (!m_connectedDev)
m_connectedDev = BooDeviceNew(this);
return m_connectedDev;

View File

@ -8,6 +8,7 @@ class CDolphinSmashAdapter final : public CDeviceBase
void deviceDisconnected();
public:
CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev);
~CDolphinSmashAdapter();
};
#endif // CDOLPHINSMASHADAPTER_HPP

View File

@ -7,6 +7,9 @@
#include "CDualshockPad.hpp"
#include "CGenericPad.hpp"
#define VID_NINTENDO 0x57e
#define PID_SMASH_ADAPTER 0x337
enum EDeviceMask
{
DEV_NONE = 0,

View File

@ -2,17 +2,30 @@
#include "inputdev/CDeviceToken.hpp"
#include "IHIDDevice.hpp"
CDeviceBase::CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev)
: m_token(token), m_hidDev(hidDev)
{
hidDev->_setDeviceImp(this);
}
CDeviceBase::~CDeviceBase()
{
delete m_hidDev;
}
void CDeviceBase::_deviceDisconnected()
{
deviceDisconnected();
m_token = NULL;
if (m_hidDev)
{
m_hidDev->_deviceDisconnected();
delete m_hidDev;
m_hidDev = NULL;
}
}
void CDeviceBase::closeDevice()
{
if (m_token)

View File

@ -4,10 +4,15 @@
CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev)
: CDeviceBase(token, hidDev)
{
printf("I've been plugged!!\n");
}
CDolphinSmashAdapter::~CDolphinSmashAdapter()
{
}
void CDolphinSmashAdapter::deviceDisconnected()
{
printf("I've been unplugged!!\n");
}

View File

@ -1,27 +1,92 @@
#include "IHIDDevice.hpp"
#include "inputdev/CDeviceToken.hpp"
#include "inputdev/CDeviceBase.hpp"
#include <IOKit/hid/IOHIDLib.h>
#include <thread>
#define MAX_REPORT_SIZE 65536
class CHIDDeviceIOKit final : public IHIDDevice
{
CDeviceToken* m_token;
IOHIDDeviceRef m_dev;
std::mutex m_initMutex;
std::condition_variable m_initCond;
std::thread* m_thread;
CFRunLoopRef m_runLoop;
CDeviceBase* m_devImp;
static void _inputReport(CHIDDeviceIOKit* device,
IOReturn result,
void* sender,
IOHIDReportType type,
uint32_t reportID,
uint8_t* report,
CFIndex reportLength)
{
}
static void _disconnect(CHIDDeviceIOKit* device,
IOReturn result,
IOHIDDeviceRef sender)
{
device->_deviceDisconnected();
}
static void _threadProc(CHIDDeviceIOKit* device)
{
char thrName[128];
snprintf(thrName, 128, "%s Device Thread", device->m_token->getProductName().c_str());
pthread_setname_np(thrName);
__block std::unique_lock<std::mutex> lk(device->m_initMutex);
device->m_runLoop = CFRunLoopGetCurrent();
CFRunLoopAddObserver(device->m_runLoop,
CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopEntry, false, 0,
^(CFRunLoopObserverRef, CFRunLoopActivity) {
lk.unlock();
device->m_initCond.notify_one();
}), kCFRunLoopCommonModes);
uint8_t* inputBuf = new uint8_t[MAX_REPORT_SIZE];
IOHIDDeviceRegisterInputReportCallback(device->m_dev, inputBuf, MAX_REPORT_SIZE,
(IOHIDReportCallback)_inputReport, device);
IOHIDDeviceRegisterRemovalCallback(device->m_dev, (IOHIDCallback)_disconnect, device);
IOHIDDeviceScheduleWithRunLoop(device->m_dev, device->m_runLoop, kCFRunLoopDefaultMode);
IOHIDDeviceOpen(device->m_dev, kIOHIDOptionsTypeNone);
CFRunLoopRun();
if (device->m_runLoop)
IOHIDDeviceClose(device->m_dev, kIOHIDOptionsTypeNone);
}
void _deviceDisconnected()
{
IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone);
CFRunLoopRef rl = m_runLoop;
m_runLoop = NULL;
if (rl)
CFRunLoopStop(rl);
}
void _setDeviceImp(CDeviceBase* dev)
{
m_devImp = dev;
}
public:
CHIDDeviceIOKit(CDeviceToken* token)
: m_dev(token->getDeviceHandle())
: m_token(token),
m_dev(token->getDeviceHandle())
{
IOHIDDeviceOpen(m_dev, kIOHIDOptionsTypeNone);
std::unique_lock<std::mutex> lk(m_initMutex);
m_thread = new std::thread(_threadProc, this);
m_initCond.wait(lk);
}
~CHIDDeviceIOKit()
{
IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone);
if (m_runLoop)
CFRunLoopStop(m_runLoop);
m_thread->detach();
delete m_thread;
}

View File

@ -7,6 +7,7 @@ class CHIDListenerIOKit final : public IHIDListener
{
CDeviceFinder& m_finder;
CFRunLoopRef m_listenerRunLoop;
IOHIDManagerRef m_hidManager;
bool m_scanningEnabled;
@ -31,10 +32,18 @@ class CHIDListenerIOKit final : public IHIDListener
}
static void deviceDisconnected(CHIDListenerIOKit* listener,
IOReturn,
void*,
IOReturn ret,
void* sender,
IOHIDDeviceRef device)
{
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop)
{
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{
deviceDisconnected(listener, ret, sender, device);
});
CFRunLoopWakeUp(listener->m_listenerRunLoop);
return;
}
listener->m_finder._removeToken(device);
}
@ -63,7 +72,8 @@ public:
IOHIDManagerSetDeviceMatching(m_hidManager, NULL);
IOHIDManagerRegisterDeviceMatchingCallback(m_hidManager, (IOHIDDeviceCallback)deviceConnected, this);
IOHIDManagerRegisterDeviceRemovalCallback(m_hidManager, (IOHIDDeviceCallback)deviceDisconnected, this);
IOHIDManagerScheduleWithRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
m_listenerRunLoop = CFRunLoopGetCurrent();
IOHIDManagerScheduleWithRunLoop(m_hidManager, m_listenerRunLoop, kCFRunLoopDefaultMode);
IOReturn ret = IOHIDManagerOpen(m_hidManager, kIOHIDManagerOptionNone);
if (ret != kIOReturnSuccess)
throw std::runtime_error("error establishing IOHIDManager");
@ -77,7 +87,7 @@ public:
~CHIDListenerIOKit()
{
IOHIDManagerUnscheduleFromRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
IOHIDManagerUnscheduleFromRunLoop(m_hidManager, m_listenerRunLoop, kCFRunLoopDefaultMode);
IOHIDManagerClose(m_hidManager, kIOHIDManagerOptionNone);
CFRelease(m_hidManager);
}

View File

@ -1,10 +1,11 @@
#include "inputdev/DeviceClasses.hpp"
#include "inputdev/CDeviceToken.hpp"
#include "IHIDDevice.hpp"
bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask)
{
if (mask & DEV_DOL_SMASH_ADAPTER &&
token.getVendorId() == 0x57e && token.getProductId() == 0x337)
token.getVendorId() == VID_NINTENDO && token.getProductId() == PID_SMASH_ADAPTER)
return true;
return false;
}
@ -13,9 +14,13 @@ IHIDDevice* IHIDDeviceNew(CDeviceToken* token);
CDeviceBase* BooDeviceNew(CDeviceToken* token)
{
IHIDDevice* newDev = IHIDDeviceNew(token);
if (!newDev)
return NULL;
if (token->getVendorId() == 0x57e && token->getProductId() == 0x337)
if (token->getVendorId() == VID_NINTENDO && token->getProductId() == PID_SMASH_ADAPTER)
return new CDolphinSmashAdapter(token, newDev);
else
delete newDev;
return NULL;
}

View File

@ -7,7 +7,10 @@ class CDeviceBase;
class IHIDDevice
{
friend CDeviceBase;
virtual void _setDeviceImp(CDeviceBase* dev)=0;
virtual void _deviceDisconnected()=0;
public:
inline virtual ~IHIDDevice() {};
};
#endif // IHIDDEVICE_HPP

View File

@ -12,15 +12,16 @@ public:
{}
void deviceConnected(CDeviceToken& tok)
{
printf("CONNECTED %s %s\n", tok.getVendorName().c_str(), tok.getProductName().c_str());
smashAdapter = dynamic_cast<CDolphinSmashAdapter*>(tok.openAndGetDevice());
}
void deviceDisconnected(CDeviceToken& tok)
void deviceDisconnected(CDeviceToken&, CDeviceBase* device)
{
if (smashAdapter == device)
{
printf("DISCONNECTED %s %s\n", tok.getVendorName().c_str(), tok.getProductName().c_str());
delete smashAdapter;
smashAdapter = NULL;
}
}
};
int main(int argc, char** argv)