mirror of https://github.com/AxioDL/boo.git
IOKit input refactor
This commit is contained in:
parent
12bbf540fd
commit
62443b59e7
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
|
||||
|
|
|
@ -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*/) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include "DeviceBase.hpp"
|
||||
#include "../System.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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__
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
};
|
||||
|
|
|
@ -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__
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue