mirror of https://github.com/AxioDL/boo.git
full IOKit support for smash adapter
This commit is contained in:
parent
92e7c3fb04
commit
4377109346
|
@ -1,20 +1,35 @@
|
||||||
#ifndef CDEVICEBASE
|
#ifndef CDEVICEBASE
|
||||||
#define CDEVICEBASE
|
#define CDEVICEBASE
|
||||||
|
|
||||||
class CDeviceToken;
|
#include <stdint.h>
|
||||||
class IHIDDevice;
|
#include <stdlib.h>
|
||||||
|
|
||||||
class CDeviceBase
|
class CDeviceBase
|
||||||
{
|
{
|
||||||
CDeviceToken* m_token;
|
friend class CDeviceToken;
|
||||||
IHIDDevice* m_hidDev;
|
friend class CHIDDeviceIOKit;
|
||||||
friend CDeviceToken;
|
friend class CHIDDeviceUdev;
|
||||||
|
friend class CHIDDeviceWin32;
|
||||||
|
|
||||||
|
class CDeviceToken* m_token;
|
||||||
|
class IHIDDevice* m_hidDev;
|
||||||
void _deviceDisconnected();
|
void _deviceDisconnected();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev);
|
CDeviceBase(CDeviceToken* token);
|
||||||
virtual ~CDeviceBase();
|
virtual ~CDeviceBase();
|
||||||
void closeDevice();
|
void closeDevice();
|
||||||
virtual void deviceDisconnected()=0;
|
virtual void deviceDisconnected()=0;
|
||||||
|
|
||||||
|
/* Low-Level API */
|
||||||
|
bool sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length);
|
||||||
|
size_t receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length);
|
||||||
|
virtual void transferCycle() {};
|
||||||
|
|
||||||
|
/* High-Level API */
|
||||||
|
bool sendReport(const uint8_t* data, size_t length);
|
||||||
|
virtual size_t receiveReport(uint8_t* data, size_t length) {};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CDEVICEBASE
|
#endif // CDEVICEBASE
|
||||||
|
|
|
@ -33,9 +33,9 @@ private:
|
||||||
|
|
||||||
/* Friend methods for platform-listener to find/insert/remove
|
/* Friend methods for platform-listener to find/insert/remove
|
||||||
* tokens with type-filtering */
|
* tokens with type-filtering */
|
||||||
inline bool _hasToken(TDeviceHandle handle)
|
inline bool _hasToken(const std::string& path)
|
||||||
{
|
{
|
||||||
auto preCheck = m_tokens.find(handle);
|
auto preCheck = m_tokens.find(path);
|
||||||
if (preCheck != m_tokens.end())
|
if (preCheck != m_tokens.end())
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
|
@ -44,14 +44,14 @@ private:
|
||||||
{
|
{
|
||||||
if (BooDeviceMatchToken(token, m_types)) {
|
if (BooDeviceMatchToken(token, m_types)) {
|
||||||
m_tokensLock.lock();
|
m_tokensLock.lock();
|
||||||
TInsertedDeviceToken inseredTok = m_tokens.insert(std::make_pair(token.getDeviceHandle(), std::move(token)));
|
TInsertedDeviceToken inseredTok = m_tokens.insert(std::make_pair(token.getDevicePath(), std::move(token)));
|
||||||
m_tokensLock.unlock();
|
m_tokensLock.unlock();
|
||||||
deviceConnected(inseredTok.first->second);
|
deviceConnected(inseredTok.first->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inline void _removeToken(TDeviceHandle handle)
|
inline void _removeToken(const std::string& path)
|
||||||
{
|
{
|
||||||
auto preCheck = m_tokens.find(handle);
|
auto preCheck = m_tokens.find(path);
|
||||||
if (preCheck != m_tokens.end())
|
if (preCheck != m_tokens.end())
|
||||||
{
|
{
|
||||||
CDeviceToken& tok = preCheck->second;
|
CDeviceToken& tok = preCheck->second;
|
||||||
|
|
|
@ -5,20 +5,13 @@
|
||||||
#include "CDeviceBase.hpp"
|
#include "CDeviceBase.hpp"
|
||||||
#include "DeviceClasses.hpp"
|
#include "DeviceClasses.hpp"
|
||||||
|
|
||||||
#if __APPLE__
|
|
||||||
#include <IOKit/hid/IOHIDLib.h>
|
|
||||||
typedef IOHIDDeviceRef TDeviceHandle;
|
|
||||||
#elif _WIN32
|
|
||||||
#elif __linux__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CDeviceToken
|
class CDeviceToken
|
||||||
{
|
{
|
||||||
unsigned m_vendorId;
|
unsigned m_vendorId;
|
||||||
unsigned m_productId;
|
unsigned m_productId;
|
||||||
std::string m_vendorName;
|
std::string m_vendorName;
|
||||||
std::string m_productName;
|
std::string m_productName;
|
||||||
TDeviceHandle m_devHandle;
|
std::string m_devPath;
|
||||||
|
|
||||||
friend class CDeviceBase;
|
friend class CDeviceBase;
|
||||||
CDeviceBase* m_connectedDev;
|
CDeviceBase* m_connectedDev;
|
||||||
|
@ -34,8 +27,8 @@ class CDeviceToken
|
||||||
public:
|
public:
|
||||||
CDeviceToken(const CDeviceToken&) = delete;
|
CDeviceToken(const CDeviceToken&) = delete;
|
||||||
CDeviceToken(CDeviceToken&&) = default;
|
CDeviceToken(CDeviceToken&&) = default;
|
||||||
inline CDeviceToken(unsigned vid, unsigned pid, const char* vname, const char* pname, TDeviceHandle handle)
|
inline CDeviceToken(unsigned vid, unsigned pid, const char* vname, const char* pname, const char* path)
|
||||||
: m_vendorId(vid), m_productId(pid), m_devHandle(handle), m_connectedDev(NULL)
|
: m_vendorId(vid), m_productId(pid), m_devPath(path), m_connectedDev(NULL)
|
||||||
{
|
{
|
||||||
if (vname)
|
if (vname)
|
||||||
m_vendorName = vname;
|
m_vendorName = vname;
|
||||||
|
@ -47,19 +40,19 @@ public:
|
||||||
inline unsigned getProductId() const {return m_productId;}
|
inline unsigned getProductId() const {return m_productId;}
|
||||||
inline const std::string& getVendorName() const {return m_vendorName;}
|
inline const std::string& getVendorName() const {return m_vendorName;}
|
||||||
inline const std::string& getProductName() const {return m_productName;}
|
inline const std::string& getProductName() const {return m_productName;}
|
||||||
inline TDeviceHandle getDeviceHandle() const {return m_devHandle;}
|
inline const std::string& getDevicePath() const {return m_devPath;}
|
||||||
inline bool isDeviceOpen() const {return m_connectedDev;}
|
inline bool isDeviceOpen() const {return m_connectedDev;}
|
||||||
inline CDeviceBase* openAndGetDevice()
|
inline CDeviceBase* openAndGetDevice()
|
||||||
{
|
{
|
||||||
if (!m_connectedDev)
|
if (!m_connectedDev)
|
||||||
m_connectedDev = BooDeviceNew(this);
|
m_connectedDev = BooDeviceNew(*this);
|
||||||
return m_connectedDev;
|
return m_connectedDev;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator ==(const CDeviceToken& rhs) const
|
inline bool operator ==(const CDeviceToken& rhs) const
|
||||||
{return m_devHandle == rhs.m_devHandle;}
|
{return m_devPath == rhs.m_devPath;}
|
||||||
inline bool operator <(const CDeviceToken& rhs) const
|
inline bool operator <(const CDeviceToken& rhs) const
|
||||||
{return m_devHandle < rhs.m_devHandle;}
|
{return m_devPath < rhs.m_devPath;}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CDEVICETOKEN
|
#endif // CDEVICETOKEN
|
||||||
|
|
|
@ -1,14 +1,63 @@
|
||||||
#ifndef CDOLPHINSMASHADAPTER_HPP
|
#ifndef CDOLPHINSMASHADAPTER_HPP
|
||||||
#define CDOLPHINSMASHADAPTER_HPP
|
#define CDOLPHINSMASHADAPTER_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include "CDeviceBase.hpp"
|
#include "CDeviceBase.hpp"
|
||||||
|
|
||||||
|
struct IDolphinSmashAdapterCallback
|
||||||
|
{
|
||||||
|
enum EDolphinControllerType
|
||||||
|
{
|
||||||
|
DOL_TYPE_NONE = 0,
|
||||||
|
DOL_TYPE_NORMAL = 0x10,
|
||||||
|
DOL_TYPE_WAVEBIRD = 0x20,
|
||||||
|
DOL_TYPE_RUMBLE = 0xF0
|
||||||
|
};
|
||||||
|
enum EDolphinControllerButtons
|
||||||
|
{
|
||||||
|
DOL_START = 1<<0,
|
||||||
|
DOL_Z = 1<<1,
|
||||||
|
DOL_L = 1<<2,
|
||||||
|
DOL_R = 1<<3,
|
||||||
|
DOL_A = 1<<8,
|
||||||
|
DOL_B = 1<<9,
|
||||||
|
DOL_X = 1<<10,
|
||||||
|
DOL_Y = 1<<11,
|
||||||
|
DOL_LEFT = 1<<12,
|
||||||
|
DOL_RIGHT = 1<<13,
|
||||||
|
DOL_DOWN = 1<<14,
|
||||||
|
DOL_UP = 1<<15
|
||||||
|
};
|
||||||
|
struct SDolphinControllerState
|
||||||
|
{
|
||||||
|
uint8_t m_leftStick[2];
|
||||||
|
uint8_t m_rightStick[2];
|
||||||
|
uint8_t m_analogTriggers[2];
|
||||||
|
uint16_t m_btns;
|
||||||
|
};
|
||||||
|
virtual void controllerConnected(unsigned idx, EDolphinControllerType type) {}
|
||||||
|
virtual void controllerDisconnected(unsigned idx, EDolphinControllerType type) {}
|
||||||
|
virtual void controllerUpdate(unsigned idx, EDolphinControllerType type,
|
||||||
|
const SDolphinControllerState& state) {}
|
||||||
|
};
|
||||||
|
|
||||||
class CDolphinSmashAdapter final : public CDeviceBase
|
class CDolphinSmashAdapter final : public CDeviceBase
|
||||||
{
|
{
|
||||||
|
IDolphinSmashAdapterCallback* m_callback;
|
||||||
|
uint8_t m_knownControllers;
|
||||||
|
uint8_t m_rumbleRequest;
|
||||||
|
uint8_t m_rumbleState;
|
||||||
|
bool m_didHandshake;
|
||||||
void deviceDisconnected();
|
void deviceDisconnected();
|
||||||
|
void transferCycle();
|
||||||
public:
|
public:
|
||||||
CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev);
|
CDolphinSmashAdapter(CDeviceToken* token);
|
||||||
~CDolphinSmashAdapter();
|
~CDolphinSmashAdapter();
|
||||||
|
|
||||||
|
inline void setCallback(IDolphinSmashAdapterCallback* cb)
|
||||||
|
{m_callback = cb; m_knownControllers = 0;}
|
||||||
|
inline void startRumble(unsigned idx) {if (idx >= 4) return; m_rumbleRequest |= 1<<idx;}
|
||||||
|
inline void stopRumble(unsigned idx) {if (idx >= 4) return; m_rumbleRequest &= ~(1<<idx);}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CDOLPHINSMASHADAPTER_HPP
|
#endif // CDOLPHINSMASHADAPTER_HPP
|
||||||
|
|
|
@ -22,6 +22,6 @@ enum EDeviceMask
|
||||||
};
|
};
|
||||||
|
|
||||||
bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask);
|
bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask);
|
||||||
CDeviceBase* BooDeviceNew(CDeviceToken* token);
|
CDeviceBase* BooDeviceNew(CDeviceToken& token);
|
||||||
|
|
||||||
#endif // CDEVICECLASSES_HPP
|
#endif // CDEVICECLASSES_HPP
|
||||||
|
|
|
@ -2,10 +2,9 @@
|
||||||
#include "inputdev/CDeviceToken.hpp"
|
#include "inputdev/CDeviceToken.hpp"
|
||||||
#include "IHIDDevice.hpp"
|
#include "IHIDDevice.hpp"
|
||||||
|
|
||||||
CDeviceBase::CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev)
|
CDeviceBase::CDeviceBase(CDeviceToken* token)
|
||||||
: m_token(token), m_hidDev(hidDev)
|
: m_token(token), m_hidDev(NULL)
|
||||||
{
|
{
|
||||||
hidDev->_setDeviceImp(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CDeviceBase::~CDeviceBase()
|
CDeviceBase::~CDeviceBase()
|
||||||
|
@ -25,11 +24,31 @@ void CDeviceBase::_deviceDisconnected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CDeviceBase::closeDevice()
|
void CDeviceBase::closeDevice()
|
||||||
{
|
{
|
||||||
if (m_token)
|
if (m_token)
|
||||||
m_token->_deviceClose();
|
m_token->_deviceClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CDeviceBase::sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_hidDev)
|
||||||
|
return m_hidDev->_sendInterruptTransfer(pipe, data, length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CDeviceBase::receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_hidDev)
|
||||||
|
return m_hidDev->_receiveInterruptTransfer(pipe, data, length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeviceBase::sendReport(const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_hidDev)
|
||||||
|
return m_hidDev->_sendReport(data, length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,143 @@
|
||||||
#include "inputdev/CDolphinSmashAdapter.hpp"
|
#include "inputdev/CDolphinSmashAdapter.hpp"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev)
|
/* Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c
|
||||||
: CDeviceBase(token, hidDev)
|
*/
|
||||||
|
|
||||||
|
static const bool BUTTON_MASK[] =
|
||||||
{
|
{
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token)
|
||||||
|
: CDeviceBase(token),
|
||||||
|
m_callback(NULL),
|
||||||
|
m_knownControllers(0),
|
||||||
|
m_rumbleRequest(0),
|
||||||
|
m_rumbleState(0),
|
||||||
|
m_didHandshake(false)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CDolphinSmashAdapter::~CDolphinSmashAdapter()
|
CDolphinSmashAdapter::~CDolphinSmashAdapter()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const uint8_t HANDSHAKE_PAYLOAD[] = {0x13};
|
||||||
|
|
||||||
|
static inline IDolphinSmashAdapterCallback::EDolphinControllerType
|
||||||
|
parseType(unsigned char status)
|
||||||
|
{
|
||||||
|
unsigned char type = status & (IDolphinSmashAdapterCallback::DOL_TYPE_NORMAL |
|
||||||
|
IDolphinSmashAdapterCallback::DOL_TYPE_WAVEBIRD);
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IDolphinSmashAdapterCallback::DOL_TYPE_NORMAL:
|
||||||
|
case IDolphinSmashAdapterCallback::DOL_TYPE_WAVEBIRD:
|
||||||
|
return (IDolphinSmashAdapterCallback::EDolphinControllerType)type;
|
||||||
|
default:
|
||||||
|
return IDolphinSmashAdapterCallback::DOL_TYPE_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline IDolphinSmashAdapterCallback::EDolphinControllerType
|
||||||
|
parseState(IDolphinSmashAdapterCallback::SDolphinControllerState* stateOut, uint8_t* payload)
|
||||||
|
{
|
||||||
|
memset(stateOut, 0, sizeof(IDolphinSmashAdapterCallback::SDolphinControllerState));
|
||||||
|
unsigned char status = payload[0];
|
||||||
|
IDolphinSmashAdapterCallback::EDolphinControllerType type = parseType(status);
|
||||||
|
|
||||||
|
IDolphinSmashAdapterCallback::EDolphinControllerType extra =
|
||||||
|
((status & 0x04) != 0) ? IDolphinSmashAdapterCallback::DOL_TYPE_RUMBLE :
|
||||||
|
IDolphinSmashAdapterCallback::DOL_TYPE_NONE;
|
||||||
|
|
||||||
|
stateOut->m_btns = (uint16_t)payload[1] << 8 | (uint16_t)payload[2];
|
||||||
|
|
||||||
|
stateOut->m_leftStick[0] = payload[3];
|
||||||
|
stateOut->m_leftStick[1] = payload[4] ^ 0xFF;
|
||||||
|
stateOut->m_rightStick[0] = payload[5];
|
||||||
|
stateOut->m_rightStick[1] = payload[6] ^ 0xFF;
|
||||||
|
stateOut->m_analogTriggers[0] = payload[7];
|
||||||
|
stateOut->m_analogTriggers[1] = payload[8];
|
||||||
|
|
||||||
|
return static_cast<IDolphinSmashAdapterCallback::EDolphinControllerType>(type|extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDolphinSmashAdapter::transferCycle()
|
||||||
|
{
|
||||||
|
if (!m_didHandshake)
|
||||||
|
{
|
||||||
|
if (!sendInterruptTransfer(0, HANDSHAKE_PAYLOAD, sizeof(HANDSHAKE_PAYLOAD)))
|
||||||
|
return;
|
||||||
|
//printf("SENT HANDSHAKE %d\n", res);
|
||||||
|
m_didHandshake = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint8_t payload[37];
|
||||||
|
size_t recvSz = receiveInterruptTransfer(0, payload, sizeof(payload));
|
||||||
|
if (recvSz != 37 || payload[0] != 0x21)
|
||||||
|
return;
|
||||||
|
//printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]);
|
||||||
|
|
||||||
|
if (!m_callback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Parse controller states */
|
||||||
|
uint8_t* controller = &payload[1];
|
||||||
|
bool rumbleMask[4] = {false};
|
||||||
|
for (int i=0 ; i<4 ; i++, controller += 9)
|
||||||
|
{
|
||||||
|
IDolphinSmashAdapterCallback::SDolphinControllerState state;
|
||||||
|
IDolphinSmashAdapterCallback::EDolphinControllerType type = parseState(&state, controller);
|
||||||
|
if (type && !(m_knownControllers & 1<<i))
|
||||||
|
{
|
||||||
|
m_knownControllers |= 1<<i;
|
||||||
|
m_callback->controllerConnected(i, type);
|
||||||
|
}
|
||||||
|
else if (!type && (m_knownControllers & 1<<i))
|
||||||
|
{
|
||||||
|
m_knownControllers &= ~(1<<i);
|
||||||
|
m_callback->controllerDisconnected(i, type);
|
||||||
|
}
|
||||||
|
m_callback->controllerUpdate(i, type, state);
|
||||||
|
rumbleMask[i] = type & IDolphinSmashAdapterCallback::DOL_TYPE_RUMBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send rumble message (if needed) */
|
||||||
|
uint8_t rumbleReq = m_rumbleRequest;
|
||||||
|
if (rumbleReq != m_rumbleState)
|
||||||
|
{
|
||||||
|
uint8_t rumbleMessage[5] = {0x11};
|
||||||
|
for (int i=0 ; i<4 ; ++i)
|
||||||
|
{
|
||||||
|
if (rumbleReq & 1<<i && rumbleMask[i])
|
||||||
|
rumbleMessage[i+1] = 1;
|
||||||
|
else
|
||||||
|
rumbleMessage[i+1] = 0;
|
||||||
|
}
|
||||||
|
sendInterruptTransfer(0, rumbleMessage, sizeof(rumbleMessage));
|
||||||
|
m_rumbleState = rumbleReq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void CDolphinSmashAdapter::deviceDisconnected()
|
void CDolphinSmashAdapter::deviceDisconnected()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,162 @@
|
||||||
#include "inputdev/CDeviceToken.hpp"
|
#include "inputdev/CDeviceToken.hpp"
|
||||||
#include "inputdev/CDeviceBase.hpp"
|
#include "inputdev/CDeviceBase.hpp"
|
||||||
#include <IOKit/hid/IOHIDLib.h>
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
#include <IOKit/usb/IOUSBLib.h>
|
||||||
|
#include <IOKit/IOCFPlugIn.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#define MAX_REPORT_SIZE 65536
|
#define MAX_REPORT_SIZE 65536
|
||||||
|
|
||||||
class CHIDDeviceIOKit final : public IHIDDevice
|
class CHIDDeviceIOKit final : public IHIDDevice
|
||||||
{
|
{
|
||||||
CDeviceToken* m_token;
|
CDeviceToken& m_token;
|
||||||
IOHIDDeviceRef m_dev;
|
CDeviceBase& m_devImp;
|
||||||
|
|
||||||
|
IOUSBInterfaceInterface** m_usbIntf = NULL;
|
||||||
|
uint8_t m_usbIntfInPipe = 0;
|
||||||
|
uint8_t m_usbIntfOutPipe = 0;
|
||||||
|
bool m_runningTransferLoop = false;
|
||||||
|
|
||||||
|
const std::string& m_devPath;
|
||||||
std::mutex m_initMutex;
|
std::mutex m_initMutex;
|
||||||
std::condition_variable m_initCond;
|
std::condition_variable m_initCond;
|
||||||
std::thread* m_thread;
|
std::thread* m_thread;
|
||||||
CFRunLoopRef m_runLoop;
|
CFRunLoopRef m_runLoop = NULL;
|
||||||
CDeviceBase* m_devImp;
|
|
||||||
|
bool _sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_usbIntf)
|
||||||
|
{
|
||||||
|
IOReturn res = (*m_usbIntf)->WritePipe(m_usbIntf, m_usbIntfOutPipe, (void*)data, length);
|
||||||
|
return res == kIOReturnSuccess;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_usbIntf)
|
||||||
|
{
|
||||||
|
UInt32 readSize = length;
|
||||||
|
IOReturn res = (*m_usbIntf)->ReadPipe(m_usbIntf, m_usbIntfInPipe, data, &readSize);
|
||||||
|
if (res != kIOReturnSuccess)
|
||||||
|
return 0;
|
||||||
|
return readSize;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _threadProcLL(CHIDDeviceIOKit* device)
|
||||||
|
{
|
||||||
|
char thrName[128];
|
||||||
|
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str());
|
||||||
|
pthread_setname_np(thrName);
|
||||||
|
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());
|
||||||
|
IORegistryEntryGetChildIterator(devEntry, kIOServicePlane, &devIter);
|
||||||
|
io_object_t obj, interfaceEntry = 0;
|
||||||
|
while ((obj = IOIteratorNext(devIter)))
|
||||||
|
{
|
||||||
|
if (IOObjectConformsTo(obj, kIOUSBInterfaceClassName))
|
||||||
|
interfaceEntry = obj;
|
||||||
|
else
|
||||||
|
IOObjectRelease(obj);
|
||||||
|
}
|
||||||
|
if (!interfaceEntry)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unable to find device interface");
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IOKit Plugin COM interface (WTF Apple???) */
|
||||||
|
IOCFPlugInInterface **iodev;
|
||||||
|
SInt32 score;
|
||||||
|
IOReturn err;
|
||||||
|
err = IOCreatePlugInInterfaceForService(interfaceEntry,
|
||||||
|
kIOUSBInterfaceUserClientTypeID,
|
||||||
|
kIOCFPlugInInterfaceID,
|
||||||
|
&iodev,
|
||||||
|
&score);
|
||||||
|
IOObjectRelease(interfaceEntry);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unable to obtain IOKit plugin service");
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* USB interface function-pointer table */
|
||||||
|
IOUSBInterfaceInterface** intf = NULL;
|
||||||
|
err = (*iodev)->QueryInterface(iodev,
|
||||||
|
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
|
||||||
|
(LPVOID*)&intf);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unable to query IOKit USB interface");
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
IODestroyPlugInInterface(iodev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Obtain exclusive lock on device */
|
||||||
|
device->m_usbIntf = intf;
|
||||||
|
err = (*intf)->USBInterfaceOpen(intf);
|
||||||
|
if (err != kIOReturnSuccess)
|
||||||
|
{
|
||||||
|
if (err == kIOReturnExclusiveAccess)
|
||||||
|
throw std::runtime_error("unable to open IOKit USB interface; someone else using it");
|
||||||
|
else
|
||||||
|
throw std::runtime_error("unable to open IOKit USB interface");
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
if (tType == kUSBInterrupt)
|
||||||
|
{
|
||||||
|
if (dir == kUSBIn)
|
||||||
|
device->m_usbIntfInPipe = num;
|
||||||
|
else if (dir == kUSBOut)
|
||||||
|
device->m_usbIntfOutPipe = num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return control to main thread */
|
||||||
|
device->m_runningTransferLoop = true;
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
|
/* Start transfer loop */
|
||||||
|
while (device->m_runningTransferLoop)
|
||||||
|
{
|
||||||
|
device->m_devImp.transferCycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
(*intf)->USBInterfaceClose(intf);
|
||||||
|
(*intf)->Release(intf);
|
||||||
|
IODestroyPlugInInterface(iodev);
|
||||||
|
device->m_usbIntf = NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _inputReport(CHIDDeviceIOKit* device,
|
static void _inputReport(CHIDDeviceIOKit* device,
|
||||||
IOReturn result,
|
IOReturn result,
|
||||||
|
@ -32,10 +175,11 @@ class CHIDDeviceIOKit final : public IHIDDevice
|
||||||
{
|
{
|
||||||
device->_deviceDisconnected();
|
device->_deviceDisconnected();
|
||||||
}
|
}
|
||||||
static void _threadProc(CHIDDeviceIOKit* device)
|
|
||||||
|
static void _threadProcHL(CHIDDeviceIOKit* device)
|
||||||
{
|
{
|
||||||
char thrName[128];
|
char thrName[128];
|
||||||
snprintf(thrName, 128, "%s Device Thread", device->m_token->getProductName().c_str());
|
snprintf(thrName, 128, "%s HID Thread", device->m_token.getProductName().c_str());
|
||||||
pthread_setname_np(thrName);
|
pthread_setname_np(thrName);
|
||||||
__block std::unique_lock<std::mutex> lk(device->m_initMutex);
|
__block std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
device->m_runLoop = CFRunLoopGetCurrent();
|
device->m_runLoop = CFRunLoopGetCurrent();
|
||||||
|
@ -47,14 +191,17 @@ class CHIDDeviceIOKit final : public IHIDDevice
|
||||||
}), kCFRunLoopCommonModes);
|
}), kCFRunLoopCommonModes);
|
||||||
|
|
||||||
uint8_t* inputBuf = new uint8_t[MAX_REPORT_SIZE];
|
uint8_t* inputBuf = new uint8_t[MAX_REPORT_SIZE];
|
||||||
IOHIDDeviceRegisterInputReportCallback(device->m_dev, inputBuf, MAX_REPORT_SIZE,
|
io_registry_entry_t devServ = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str());
|
||||||
|
IOHIDDeviceRef dev = IOHIDDeviceCreate(kCFAllocatorDefault, devServ);
|
||||||
|
IOHIDDeviceRegisterInputReportCallback(dev, inputBuf, MAX_REPORT_SIZE,
|
||||||
(IOHIDReportCallback)_inputReport, device);
|
(IOHIDReportCallback)_inputReport, device);
|
||||||
IOHIDDeviceRegisterRemovalCallback(device->m_dev, (IOHIDCallback)_disconnect, device);
|
IOHIDDeviceRegisterRemovalCallback(dev, (IOHIDCallback)_disconnect, device);
|
||||||
IOHIDDeviceScheduleWithRunLoop(device->m_dev, device->m_runLoop, kCFRunLoopDefaultMode);
|
IOHIDDeviceScheduleWithRunLoop(dev, device->m_runLoop, kCFRunLoopDefaultMode);
|
||||||
IOHIDDeviceOpen(device->m_dev, kIOHIDOptionsTypeNone);
|
IOHIDDeviceOpen(dev, kIOHIDOptionsTypeNone);
|
||||||
CFRunLoopRun();
|
CFRunLoopRun();
|
||||||
if (device->m_runLoop)
|
if (device->m_runLoop)
|
||||||
IOHIDDeviceClose(device->m_dev, kIOHIDOptionsTypeNone);
|
IOHIDDeviceClose(dev, kIOHIDOptionsTypeNone);
|
||||||
|
CFRelease(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deviceDisconnected()
|
void _deviceDisconnected()
|
||||||
|
@ -63,21 +210,31 @@ class CHIDDeviceIOKit final : public IHIDDevice
|
||||||
m_runLoop = NULL;
|
m_runLoop = NULL;
|
||||||
if (rl)
|
if (rl)
|
||||||
CFRunLoopStop(rl);
|
CFRunLoopStop(rl);
|
||||||
|
m_runningTransferLoop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setDeviceImp(CDeviceBase* dev)
|
bool _sendReport(const uint8_t* data, size_t length)
|
||||||
{
|
{
|
||||||
m_devImp = dev;
|
if (m_runLoop)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CHIDDeviceIOKit(CDeviceToken* token)
|
CHIDDeviceIOKit(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel)
|
||||||
: m_token(token),
|
: m_token(token),
|
||||||
m_dev(token->getDeviceHandle())
|
m_devImp(devImp),
|
||||||
|
m_devPath(token.getDevicePath())
|
||||||
{
|
{
|
||||||
|
devImp.m_hidDev = this;
|
||||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||||
m_thread = new std::thread(_threadProc, this);
|
if (lowLevel)
|
||||||
|
m_thread = new std::thread(_threadProcLL, this);
|
||||||
|
else
|
||||||
|
m_thread = new std::thread(_threadProcHL, this);
|
||||||
m_initCond.wait(lk);
|
m_initCond.wait(lk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +242,7 @@ public:
|
||||||
{
|
{
|
||||||
if (m_runLoop)
|
if (m_runLoop)
|
||||||
CFRunLoopStop(m_runLoop);
|
CFRunLoopStop(m_runLoop);
|
||||||
|
m_runningTransferLoop = false;
|
||||||
m_thread->detach();
|
m_thread->detach();
|
||||||
delete m_thread;
|
delete m_thread;
|
||||||
}
|
}
|
||||||
|
@ -92,7 +250,7 @@ public:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
IHIDDevice* IHIDDeviceNew(CDeviceToken* token)
|
IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel)
|
||||||
{
|
{
|
||||||
return new CHIDDeviceIOKit(token);
|
return new CHIDDeviceIOKit(token, devImp, lowLevel);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,51 @@
|
||||||
#include "inputdev/CDeviceFinder.hpp"
|
#include "inputdev/CDeviceFinder.hpp"
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include <IOKit/hid/IOHIDLib.h>
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
#include <IOKit/IOKitLib.h>
|
||||||
|
#include <IOKit/usb/IOUSBLib.h>
|
||||||
|
#include <IOKit/IOCFPlugIn.h>
|
||||||
|
|
||||||
|
/* Reference: http://oroboro.com/usb-serial-number-osx/
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool getUSBStringDescriptor(IOUSBDeviceInterface182** usbDevice, UInt8 idx, char* out)
|
||||||
|
{
|
||||||
|
UInt16 buffer[128];
|
||||||
|
|
||||||
|
// wow... we're actually forced to make hard coded bus requests. Its like
|
||||||
|
// hard disk programming in the 80's!
|
||||||
|
IOUSBDevRequest request;
|
||||||
|
|
||||||
|
request.bmRequestType = USBmakebmRequestType(kUSBIn,
|
||||||
|
kUSBStandard,
|
||||||
|
kUSBDevice);
|
||||||
|
request.bRequest = kUSBRqGetDescriptor;
|
||||||
|
request.wValue = (kUSBStringDesc << 8) | idx;
|
||||||
|
request.wIndex = 0x409; // english
|
||||||
|
request.wLength = sizeof(buffer);
|
||||||
|
request.pData = buffer;
|
||||||
|
|
||||||
|
kern_return_t err = (*usbDevice)->DeviceRequest(usbDevice, &request);
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
// the request failed... fairly uncommon for the USB disk driver, but not
|
||||||
|
// so uncommon for other devices. This can also be less reliable if your
|
||||||
|
// disk is mounted through an external USB hub. At this level we actually
|
||||||
|
// have to worry about hardware issues like this.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're mallocing this string just as an example. But you probably will want
|
||||||
|
// to do something smarter, like pre-allocated buffers in the info class, or
|
||||||
|
// use a string class.
|
||||||
|
unsigned count = (request.wLenDone - 1) / 2;
|
||||||
|
unsigned i;
|
||||||
|
for (i=0 ; i<count ; ++i)
|
||||||
|
out[i] = buffer[i+1];
|
||||||
|
out[i] = '\0';
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
class CHIDListenerIOKit final : public IHIDListener
|
class CHIDListenerIOKit final : public IHIDListener
|
||||||
{
|
{
|
||||||
|
@ -9,6 +54,7 @@ class CHIDListenerIOKit final : public IHIDListener
|
||||||
|
|
||||||
CFRunLoopRef m_listenerRunLoop;
|
CFRunLoopRef m_listenerRunLoop;
|
||||||
IOHIDManagerRef m_hidManager;
|
IOHIDManagerRef m_hidManager;
|
||||||
|
IONotificationPortRef m_llPort;
|
||||||
bool m_scanningEnabled;
|
bool m_scanningEnabled;
|
||||||
|
|
||||||
static void deviceConnected(CHIDListenerIOKit* listener,
|
static void deviceConnected(CHIDListenerIOKit* listener,
|
||||||
|
@ -18,7 +64,10 @@ class CHIDListenerIOKit final : public IHIDListener
|
||||||
{
|
{
|
||||||
if (!listener->m_scanningEnabled)
|
if (!listener->m_scanningEnabled)
|
||||||
return;
|
return;
|
||||||
if (listener->m_finder._hasToken(device))
|
io_string_t devPath;
|
||||||
|
if (IORegistryEntryGetPath(IOHIDDeviceGetService(device), kIOServicePlane, devPath) != 0)
|
||||||
|
return;
|
||||||
|
if (listener->m_finder._hasToken(devPath))
|
||||||
return;
|
return;
|
||||||
CFIndex vid, pid;
|
CFIndex vid, pid;
|
||||||
CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid);
|
CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid);
|
||||||
|
@ -28,7 +77,7 @@ class CHIDListenerIOKit final : public IHIDListener
|
||||||
listener->m_finder._insertToken(CDeviceToken(vid, pid,
|
listener->m_finder._insertToken(CDeviceToken(vid, pid,
|
||||||
CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8),
|
CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8),
|
||||||
CFStringGetCStringPtr(product, kCFStringEncodingUTF8),
|
CFStringGetCStringPtr(product, kCFStringEncodingUTF8),
|
||||||
device));
|
devPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deviceDisconnected(CHIDListenerIOKit* listener,
|
static void deviceDisconnected(CHIDListenerIOKit* listener,
|
||||||
|
@ -44,12 +93,18 @@ class CHIDListenerIOKit final : public IHIDListener
|
||||||
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
listener->m_finder._removeToken(device);
|
io_string_t devPath;
|
||||||
|
if (IORegistryEntryGetPath(IOHIDDeviceGetService(device), kIOServicePlane, devPath) != 0)
|
||||||
|
return;
|
||||||
|
listener->m_finder._removeToken(devPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void applyDevice(IOHIDDeviceRef device, CHIDListenerIOKit* listener)
|
static void applyDevice(IOHIDDeviceRef device, CHIDListenerIOKit* listener)
|
||||||
{
|
{
|
||||||
if (listener->m_finder._hasToken(device))
|
io_string_t devPath;
|
||||||
|
if (IORegistryEntryGetPath(IOHIDDeviceGetService(device), kIOServicePlane, devPath) != 0)
|
||||||
|
return;
|
||||||
|
if (listener->m_finder._hasToken(devPath))
|
||||||
return;
|
return;
|
||||||
CFIndex vid, pid;
|
CFIndex vid, pid;
|
||||||
CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid);
|
CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid);
|
||||||
|
@ -59,7 +114,84 @@ class CHIDListenerIOKit final : public IHIDListener
|
||||||
listener->m_finder._insertToken(CDeviceToken(vid, pid,
|
listener->m_finder._insertToken(CDeviceToken(vid, pid,
|
||||||
CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8),
|
CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8),
|
||||||
CFStringGetCStringPtr(product, kCFStringEncodingUTF8),
|
CFStringGetCStringPtr(product, kCFStringEncodingUTF8),
|
||||||
device));
|
devPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void devicesConnectedLL(CHIDListenerIOKit* listener,
|
||||||
|
io_iterator_t iterator)
|
||||||
|
{
|
||||||
|
io_object_t obj;
|
||||||
|
while ((obj = IOIteratorNext(iterator)))
|
||||||
|
{
|
||||||
|
io_string_t devPath;
|
||||||
|
if (IORegistryEntryGetPath(obj, 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)
|
||||||
|
throw std::runtime_error("unable to open IOKit plugin interface");
|
||||||
|
|
||||||
|
IOUSBDeviceInterface182 **dev;
|
||||||
|
err = (*devServ)->QueryInterface(devServ,
|
||||||
|
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182),
|
||||||
|
(LPVOID*)&dev);
|
||||||
|
if (err != kIOReturnSuccess)
|
||||||
|
throw std::runtime_error("unable to open IOKit device interface");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
listener->m_finder._insertToken(CDeviceToken(vid, pid, vstr, pstr, devPath));
|
||||||
|
|
||||||
|
//printf("ADDED %08X %s\n", obj, devPath);
|
||||||
|
(*dev)->Release(dev);
|
||||||
|
IODestroyPlugInInterface(devServ);
|
||||||
|
IOObjectRelease(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void devicesDisconnectedLL(CHIDListenerIOKit* listener,
|
||||||
|
io_iterator_t iterator)
|
||||||
|
{
|
||||||
|
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop)
|
||||||
|
{
|
||||||
|
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{
|
||||||
|
devicesDisconnectedLL(listener, iterator);
|
||||||
|
});
|
||||||
|
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
io_object_t obj;
|
||||||
|
while ((obj = IOIteratorNext(iterator)))
|
||||||
|
{
|
||||||
|
io_string_t devPath;
|
||||||
|
if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0)
|
||||||
|
continue;
|
||||||
|
listener->m_finder._removeToken(devPath);
|
||||||
|
//printf("REMOVED %08X %s\n", obj, devPath);
|
||||||
|
IOObjectRelease(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -78,9 +210,38 @@ public:
|
||||||
if (ret != kIOReturnSuccess)
|
if (ret != kIOReturnSuccess)
|
||||||
throw std::runtime_error("error establishing IOHIDManager");
|
throw std::runtime_error("error establishing IOHIDManager");
|
||||||
|
|
||||||
/* Initial Device Add */
|
/* Initial HID Device Add */
|
||||||
m_scanningEnabled = true;
|
m_scanningEnabled = true;
|
||||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
|
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
|
||||||
|
|
||||||
|
/* Register Low-Level Matcher */
|
||||||
|
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||||
|
CFRunLoopAddSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
|
||||||
|
|
||||||
|
CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName);
|
||||||
|
CFIndex nintendoVid = VID_NINTENDO;
|
||||||
|
CFIndex smashPid = PID_SMASH_ADAPTER;
|
||||||
|
CFNumberRef nintendoVidNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &nintendoVid);
|
||||||
|
CFNumberRef smashPidNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &smashPid);
|
||||||
|
CFDictionaryAddValue(matchDict, CFSTR(kUSBVendorID), nintendoVidNum);
|
||||||
|
CFDictionaryAddValue(matchDict, CFSTR(kUSBProductID), smashPidNum);
|
||||||
|
CFRelease(nintendoVidNum);
|
||||||
|
CFRelease(smashPidNum);
|
||||||
|
CFRetain(matchDict);
|
||||||
|
|
||||||
|
io_iterator_t initialIt;
|
||||||
|
kern_return_t llRet =
|
||||||
|
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
||||||
|
(IOServiceMatchingCallback)devicesConnectedLL, this, &initialIt);
|
||||||
|
if (llRet == 0)
|
||||||
|
devicesConnectedLL(this, initialIt);
|
||||||
|
|
||||||
|
llRet =
|
||||||
|
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
|
||||||
|
(IOServiceMatchingCallback)devicesDisconnectedLL, this, &initialIt);
|
||||||
|
if (llRet == 0)
|
||||||
|
devicesDisconnectedLL(this, initialIt);
|
||||||
|
|
||||||
m_scanningEnabled = false;
|
m_scanningEnabled = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -90,6 +251,8 @@ public:
|
||||||
IOHIDManagerUnscheduleFromRunLoop(m_hidManager, m_listenerRunLoop, kCFRunLoopDefaultMode);
|
IOHIDManagerUnscheduleFromRunLoop(m_hidManager, m_listenerRunLoop, kCFRunLoopDefaultMode);
|
||||||
IOHIDManagerClose(m_hidManager, kIOHIDManagerOptionNone);
|
IOHIDManagerClose(m_hidManager, kIOHIDManagerOptionNone);
|
||||||
CFRelease(m_hidManager);
|
CFRelease(m_hidManager);
|
||||||
|
CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
|
||||||
|
IONotificationPortDestroy(m_llPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Automatic device scanning */
|
/* Automatic device scanning */
|
||||||
|
|
|
@ -10,17 +10,29 @@ bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IHIDDevice* IHIDDeviceNew(CDeviceToken* token);
|
IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel);
|
||||||
CDeviceBase* BooDeviceNew(CDeviceToken* token)
|
CDeviceBase* BooDeviceNew(CDeviceToken& token)
|
||||||
{
|
{
|
||||||
IHIDDevice* newDev = IHIDDeviceNew(token);
|
|
||||||
|
CDeviceBase* retval = NULL;
|
||||||
|
bool lowLevel = false;
|
||||||
|
|
||||||
|
if (token.getVendorId() == VID_NINTENDO && token.getProductId() == PID_SMASH_ADAPTER)
|
||||||
|
{
|
||||||
|
retval = new CDolphinSmashAdapter(&token);
|
||||||
|
lowLevel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!retval)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
IHIDDevice* newDev = IHIDDeviceNew(token, *retval, lowLevel);
|
||||||
if (!newDev)
|
if (!newDev)
|
||||||
|
{
|
||||||
|
delete retval;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (token->getVendorId() == VID_NINTENDO && token->getProductId() == PID_SMASH_ADAPTER)
|
return retval;
|
||||||
return new CDolphinSmashAdapter(token, newDev);
|
|
||||||
else
|
|
||||||
delete newDev;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +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;
|
||||||
|
virtual bool _sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)=0;
|
||||||
|
virtual size_t _receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)=0;
|
||||||
|
virtual bool _sendReport(const uint8_t* data, size_t length)=0;
|
||||||
public:
|
public:
|
||||||
inline virtual ~IHIDDevice() {};
|
inline virtual ~IHIDDevice() {};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#ifndef IHIDLISTENER_HPP
|
#ifndef IHIDLISTENER_HPP
|
||||||
#define IHIDLISTENER_HPP
|
#define IHIDLISTENER_HPP
|
||||||
|
|
||||||
#include <map>
|
#include <unordered_map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include "CDeviceToken.hpp"
|
#include "CDeviceToken.hpp"
|
||||||
typedef std::map<TDeviceHandle, CDeviceToken> TDeviceTokens;
|
typedef std::unordered_map<std::string, CDeviceToken> TDeviceTokens;
|
||||||
typedef std::pair<TDeviceTokens::iterator, bool> TInsertedDeviceToken;
|
typedef std::pair<TDeviceTokens::iterator, bool> TInsertedDeviceToken;
|
||||||
class CDeviceFinder;
|
class CDeviceFinder;
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,27 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <boo.hpp>
|
#include <boo.hpp>
|
||||||
|
|
||||||
|
class CDolphinSmashAdapterCallback : public IDolphinSmashAdapterCallback
|
||||||
|
{
|
||||||
|
void controllerConnected(unsigned idx, EDolphinControllerType type)
|
||||||
|
{
|
||||||
|
printf("CONTROLLER %u CONNECTED\n", idx);
|
||||||
|
}
|
||||||
|
void controllerDisconnected(unsigned idx, EDolphinControllerType type)
|
||||||
|
{
|
||||||
|
printf("CONTROLLER %u DISCONNECTED\n", idx);
|
||||||
|
}
|
||||||
|
void controllerUpdate(unsigned idx, EDolphinControllerType type,
|
||||||
|
const SDolphinControllerState& state)
|
||||||
|
{
|
||||||
|
printf("CONTROLLER %u UPDATE %d %d\n", idx, state.m_leftStick[0], state.m_leftStick[1]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class CTestDeviceFinder : public CDeviceFinder
|
class CTestDeviceFinder : public CDeviceFinder
|
||||||
{
|
{
|
||||||
CDolphinSmashAdapter* smashAdapter = NULL;
|
CDolphinSmashAdapter* smashAdapter = NULL;
|
||||||
|
CDolphinSmashAdapterCallback m_cb;
|
||||||
public:
|
public:
|
||||||
CTestDeviceFinder()
|
CTestDeviceFinder()
|
||||||
: CDeviceFinder(DEV_DOL_SMASH_ADAPTER)
|
: CDeviceFinder(DEV_DOL_SMASH_ADAPTER)
|
||||||
|
@ -13,6 +31,7 @@ public:
|
||||||
void deviceConnected(CDeviceToken& tok)
|
void deviceConnected(CDeviceToken& tok)
|
||||||
{
|
{
|
||||||
smashAdapter = dynamic_cast<CDolphinSmashAdapter*>(tok.openAndGetDevice());
|
smashAdapter = dynamic_cast<CDolphinSmashAdapter*>(tok.openAndGetDevice());
|
||||||
|
smashAdapter->setCallback(&m_cb);
|
||||||
}
|
}
|
||||||
void deviceDisconnected(CDeviceToken&, CDeviceBase* device)
|
void deviceDisconnected(CDeviceToken&, CDeviceBase* device)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue