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; friend CDeviceToken;
void _deviceDisconnected(); void _deviceDisconnected();
public: public:
inline CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev) CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev);
: m_token(token), m_hidDev(hidDev) {} virtual ~CDeviceBase();
void closeDevice(); void closeDevice();
virtual void deviceDisconnected()=0; virtual void deviceDisconnected()=0;
}; };

View File

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

View File

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

View File

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

View File

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

View File

@ -2,17 +2,30 @@
#include "inputdev/CDeviceToken.hpp" #include "inputdev/CDeviceToken.hpp"
#include "IHIDDevice.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() void CDeviceBase::_deviceDisconnected()
{ {
deviceDisconnected(); deviceDisconnected();
m_token = NULL; m_token = NULL;
m_hidDev->_deviceDisconnected(); if (m_hidDev)
m_hidDev = NULL; {
m_hidDev->_deviceDisconnected();
delete m_hidDev;
m_hidDev = NULL;
}
} }
void CDeviceBase::closeDevice() void CDeviceBase::closeDevice()
{ {
if (m_token) if (m_token)

View File

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

View File

@ -1,27 +1,92 @@
#include "IHIDDevice.hpp" #include "IHIDDevice.hpp"
#include "inputdev/CDeviceToken.hpp" #include "inputdev/CDeviceToken.hpp"
#include "inputdev/CDeviceBase.hpp"
#include <IOKit/hid/IOHIDLib.h> #include <IOKit/hid/IOHIDLib.h>
#include <thread>
#define MAX_REPORT_SIZE 65536
class CHIDDeviceIOKit final : public IHIDDevice class CHIDDeviceIOKit final : public IHIDDevice
{ {
CDeviceToken* m_token;
IOHIDDeviceRef m_dev; 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() 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: public:
CHIDDeviceIOKit(CDeviceToken* token) 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() ~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; CDeviceFinder& m_finder;
CFRunLoopRef m_listenerRunLoop;
IOHIDManagerRef m_hidManager; IOHIDManagerRef m_hidManager;
bool m_scanningEnabled; bool m_scanningEnabled;
@ -31,10 +32,18 @@ class CHIDListenerIOKit final : public IHIDListener
} }
static void deviceDisconnected(CHIDListenerIOKit* listener, static void deviceDisconnected(CHIDListenerIOKit* listener,
IOReturn, IOReturn ret,
void*, void* sender,
IOHIDDeviceRef device) 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); listener->m_finder._removeToken(device);
} }
@ -63,7 +72,8 @@ public:
IOHIDManagerSetDeviceMatching(m_hidManager, NULL); IOHIDManagerSetDeviceMatching(m_hidManager, NULL);
IOHIDManagerRegisterDeviceMatchingCallback(m_hidManager, (IOHIDDeviceCallback)deviceConnected, this); IOHIDManagerRegisterDeviceMatchingCallback(m_hidManager, (IOHIDDeviceCallback)deviceConnected, this);
IOHIDManagerRegisterDeviceRemovalCallback(m_hidManager, (IOHIDDeviceCallback)deviceDisconnected, 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); IOReturn ret = IOHIDManagerOpen(m_hidManager, kIOHIDManagerOptionNone);
if (ret != kIOReturnSuccess) if (ret != kIOReturnSuccess)
throw std::runtime_error("error establishing IOHIDManager"); throw std::runtime_error("error establishing IOHIDManager");
@ -77,7 +87,7 @@ public:
~CHIDListenerIOKit() ~CHIDListenerIOKit()
{ {
IOHIDManagerUnscheduleFromRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); IOHIDManagerUnscheduleFromRunLoop(m_hidManager, m_listenerRunLoop, kCFRunLoopDefaultMode);
IOHIDManagerClose(m_hidManager, kIOHIDManagerOptionNone); IOHIDManagerClose(m_hidManager, kIOHIDManagerOptionNone);
CFRelease(m_hidManager); CFRelease(m_hidManager);
} }

View File

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

View File

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

View File

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