mirror of
https://github.com/AxioDL/boo.git
synced 2025-06-06 14:43:33 +00:00
IOKit input refactor
This commit is contained in:
parent
12bbf540fd
commit
62443b59e7
@ -81,7 +81,9 @@ elseif(APPLE)
|
|||||||
PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
|
|
||||||
list(APPEND PLAT_HDRS
|
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(APPKIT_LIBRARY AppKit)
|
||||||
find_library(IOKIT_LIBRARY IOKit)
|
find_library(IOKIT_LIBRARY IOKit)
|
||||||
|
@ -7,8 +7,8 @@ namespace boo
|
|||||||
|
|
||||||
const DeviceSignature BOO_DEVICE_SIGS[] =
|
const DeviceSignature BOO_DEVICE_SIGS[] =
|
||||||
{
|
{
|
||||||
DEVICE_SIG(DolphinSmashAdapter, 0x57e, 0x337),
|
DEVICE_SIG(DolphinSmashAdapter, 0x57e, 0x337, DeviceType::USB),
|
||||||
DEVICE_SIG(DualshockPad, 0x54c, 0x268),
|
DEVICE_SIG(DualshockPad, 0x54c, 0x268, DeviceType::HID),
|
||||||
DEVICE_SIG_SENTINEL()
|
DEVICE_SIG_SENTINEL()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,39 +4,50 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "../System.hpp"
|
#include <memory>
|
||||||
|
|
||||||
namespace boo
|
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 DeviceToken;
|
||||||
friend class HIDDeviceIOKit;
|
friend struct DeviceSignature;
|
||||||
friend class HIDDeviceUdev;
|
|
||||||
friend class HIDDeviceWinUSB;
|
|
||||||
|
|
||||||
class DeviceToken* m_token;
|
class DeviceToken* m_token;
|
||||||
class IHIDDevice* m_hidDev;
|
std::unique_ptr<IHIDDevice> m_hidDev;
|
||||||
void _deviceDisconnected();
|
void _deviceDisconnected();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DeviceBase(DeviceToken* token);
|
DeviceBase(DeviceToken* token);
|
||||||
virtual ~DeviceBase();
|
virtual ~DeviceBase();
|
||||||
void closeDevice();
|
void closeDevice();
|
||||||
|
|
||||||
|
/* Callbacks */
|
||||||
virtual void deviceDisconnected()=0;
|
virtual void deviceDisconnected()=0;
|
||||||
virtual void deviceError(const char* error, ...);
|
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 initialCycle() {}
|
||||||
virtual void transferCycle() {}
|
virtual void transferCycle() {}
|
||||||
virtual void finalCycle() {}
|
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 */
|
/* High-Level API */
|
||||||
bool sendHIDReport(const 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);
|
||||||
virtual size_t receiveReport(uint8_t* data, size_t length, uint16_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 true;
|
||||||
return false;
|
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();
|
m_tokensLock.lock();
|
||||||
TInsertedDeviceToken inseredTok =
|
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();
|
m_tokensLock.unlock();
|
||||||
deviceConnected(inseredTok.first->second);
|
deviceConnected(*inseredTok.first->second);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -70,10 +70,10 @@ private:
|
|||||||
auto preCheck = m_tokens.find(path);
|
auto preCheck = m_tokens.find(path);
|
||||||
if (preCheck != m_tokens.end())
|
if (preCheck != m_tokens.end())
|
||||||
{
|
{
|
||||||
DeviceToken& tok = preCheck->second;
|
DeviceToken& tok = *preCheck->second;
|
||||||
DeviceBase* dev = tok.m_connectedDev;
|
std::shared_ptr<DeviceBase> dev = tok.m_connectedDev;
|
||||||
tok._deviceClose();
|
tok._deviceClose();
|
||||||
deviceDisconnected(tok, dev);
|
deviceDisconnected(tok, dev.get());
|
||||||
m_tokensLock.lock();
|
m_tokensLock.lock();
|
||||||
m_tokens.erase(preCheck);
|
m_tokens.erase(preCheck);
|
||||||
m_tokensLock.unlock();
|
m_tokensLock.unlock();
|
||||||
|
@ -8,26 +8,38 @@
|
|||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
|
||||||
|
enum class DeviceType
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
USB = 1,
|
||||||
|
Bluetooth = 2,
|
||||||
|
HID = 3
|
||||||
|
};
|
||||||
|
|
||||||
class DeviceToken;
|
class DeviceToken;
|
||||||
class DeviceBase;
|
class DeviceBase;
|
||||||
|
|
||||||
struct DeviceSignature
|
struct DeviceSignature
|
||||||
{
|
{
|
||||||
typedef std::vector<const DeviceSignature*> TDeviceSignatureSet;
|
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;
|
const char* m_name;
|
||||||
std::type_index m_typeIdx;
|
std::type_index m_typeIdx;
|
||||||
unsigned m_vid, m_pid;
|
unsigned m_vid, m_pid;
|
||||||
TFactoryLambda m_factory;
|
TFactoryLambda m_factory;
|
||||||
|
DeviceType m_type;
|
||||||
DeviceSignature() : m_name(NULL), m_typeIdx(typeid(DeviceSignature)) {} /* Sentinel constructor */
|
DeviceSignature() : m_name(NULL), m_typeIdx(typeid(DeviceSignature)) {} /* Sentinel constructor */
|
||||||
DeviceSignature(const char* name, std::type_index&& typeIdx, unsigned vid, unsigned pid, TFactoryLambda&& factory)
|
DeviceSignature(const char* name, std::type_index&& typeIdx, unsigned vid, unsigned pid,
|
||||||
: m_name(name), m_typeIdx(typeIdx), m_vid(vid), m_pid(pid), m_factory(factory) {}
|
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 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) \
|
#define DEVICE_SIG(name, vid, pid, type) \
|
||||||
DeviceSignature(#name, typeid(name), vid, pid, [](DeviceToken* tok) -> DeviceBase* {return new name(tok);})
|
DeviceSignature(#name, typeid(name), vid, pid,\
|
||||||
|
[](DeviceToken* tok) -> std::shared_ptr<DeviceBase> {return std::make_shared<name>(tok);}, type)
|
||||||
#define DEVICE_SIG_SENTINEL() DeviceSignature()
|
#define DEVICE_SIG_SENTINEL() DeviceSignature()
|
||||||
|
|
||||||
extern const DeviceSignature BOO_DEVICE_SIGS[];
|
extern const DeviceSignature BOO_DEVICE_SIGS[];
|
||||||
|
@ -10,16 +10,7 @@ namespace boo
|
|||||||
|
|
||||||
class DeviceToken
|
class DeviceToken
|
||||||
{
|
{
|
||||||
public:
|
friend struct DeviceSignature;
|
||||||
enum class DeviceType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
USB = 1,
|
|
||||||
Bluetooth = 2,
|
|
||||||
GenericHID = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
DeviceType m_devType;
|
DeviceType m_devType;
|
||||||
unsigned m_vendorId;
|
unsigned m_vendorId;
|
||||||
unsigned m_productId;
|
unsigned m_productId;
|
||||||
@ -28,7 +19,7 @@ private:
|
|||||||
std::string m_devPath;
|
std::string m_devPath;
|
||||||
|
|
||||||
friend class DeviceBase;
|
friend class DeviceBase;
|
||||||
DeviceBase* m_connectedDev;
|
std::shared_ptr<DeviceBase> m_connectedDev;
|
||||||
|
|
||||||
friend class DeviceFinder;
|
friend class DeviceFinder;
|
||||||
inline void _deviceClose()
|
inline void _deviceClose()
|
||||||
@ -70,7 +61,7 @@ public:
|
|||||||
inline const std::string& getProductName() const {return m_productName;}
|
inline const std::string& getProductName() const {return m_productName;}
|
||||||
inline const std::string& getDevicePath() const {return m_devPath;}
|
inline const std::string& getDevicePath() const {return m_devPath;}
|
||||||
inline bool isDeviceOpen() const {return (m_connectedDev != NULL);}
|
inline bool isDeviceOpen() const {return (m_connectedDev != NULL);}
|
||||||
inline DeviceBase* openAndGetDevice()
|
inline std::shared_ptr<DeviceBase> openAndGetDevice()
|
||||||
{
|
{
|
||||||
if (!m_connectedDev)
|
if (!m_connectedDev)
|
||||||
m_connectedDev = DeviceSignature::DeviceNew(*this);
|
m_connectedDev = DeviceSignature::DeviceNew(*this);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "DeviceBase.hpp"
|
#include "DeviceBase.hpp"
|
||||||
|
#include "../System.hpp"
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,40 @@
|
|||||||
#ifndef CDUALSHOCKPAD_HPP
|
#ifndef CDUALSHOCKPAD_HPP
|
||||||
#define CDUALSHOCKPAD_HPP
|
#define CDUALSHOCKPAD_HPP
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <type_traits>
|
||||||
#include "DeviceBase.hpp"
|
#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
|
namespace boo
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -134,6 +166,7 @@ class DualshockPad final : public DeviceBase
|
|||||||
void initialCycle();
|
void initialCycle();
|
||||||
void transferCycle();
|
void transferCycle();
|
||||||
void finalCycle();
|
void finalCycle();
|
||||||
|
void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
|
||||||
public:
|
public:
|
||||||
DualshockPad(DeviceToken* token);
|
DualshockPad(DeviceToken* token);
|
||||||
~DualshockPad();
|
~DualshockPad();
|
||||||
@ -179,8 +212,10 @@ public:
|
|||||||
void setRawLED(int led)
|
void setRawLED(int led)
|
||||||
{
|
{
|
||||||
m_report.leds = 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
|
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;
|
typedef std::pair<TDeviceTokens::iterator, bool> TInsertedDeviceToken;
|
||||||
class DeviceFinder;
|
class DeviceFinder;
|
||||||
|
|
||||||
|
167
lib/inputdev/CFPointer.hpp
Normal file
167
lib/inputdev/CFPointer.hpp
Normal file
@ -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)
|
DeviceBase::DeviceBase(DeviceToken* token)
|
||||||
: m_token(token), m_hidDev(NULL)
|
: m_token(token)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceBase::~DeviceBase()
|
DeviceBase::~DeviceBase()
|
||||||
{
|
{
|
||||||
delete m_hidDev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceBase::_deviceDisconnected()
|
void DeviceBase::_deviceDisconnected()
|
||||||
{
|
{
|
||||||
deviceDisconnected();
|
deviceDisconnected();
|
||||||
m_token = NULL;
|
m_token = nullptr;
|
||||||
if (m_hidDev)
|
if (m_hidDev)
|
||||||
{
|
{
|
||||||
m_hidDev->_deviceDisconnected();
|
m_hidDev->_deviceDisconnected();
|
||||||
delete m_hidDev;
|
m_hidDev.reset();
|
||||||
m_hidDev = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,18 +54,18 @@ size_t DeviceBase::receiveUSBInterruptTransfer(uint8_t* data, size_t length)
|
|||||||
return false;
|
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)
|
if (m_hidDev)
|
||||||
return m_hidDev->_sendHIDReport(data, length, message);
|
return m_hidDev->_sendHIDReport(data, length, tp, message);
|
||||||
return false;
|
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)
|
if (m_hidDev)
|
||||||
return m_hidDev->_recieveReport(data, length, message);
|
return m_hidDev->_receiveHIDReport(data, length, tp, message);
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,16 @@ extern const DeviceSignature BOO_DEVICE_SIGS[];
|
|||||||
|
|
||||||
bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSignatureSet& sigSet)
|
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;
|
return true;
|
||||||
|
}
|
||||||
for (const DeviceSignature* sig : sigSet)
|
for (const DeviceSignature* sig : sigSet)
|
||||||
{
|
{
|
||||||
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId())
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IHIDDevice* IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp);
|
std::unique_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp);
|
||||||
DeviceBase* DeviceSignature::DeviceNew(DeviceToken& token)
|
std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token)
|
||||||
{
|
{
|
||||||
DeviceBase* retval = NULL;
|
std::shared_ptr<DeviceBase> retval;
|
||||||
|
|
||||||
/* Early-return for generic HID devices */
|
/* Perform signature-matching to find the appropriate device-factory */
|
||||||
if (token.getDeviceType() == DeviceToken::DeviceType::GenericHID)
|
const DeviceSignature* foundSig = nullptr;
|
||||||
{
|
|
||||||
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;
|
|
||||||
const DeviceSignature* sigIter = BOO_DEVICE_SIGS;
|
const DeviceSignature* sigIter = BOO_DEVICE_SIGS;
|
||||||
unsigned targetVid = token.getVendorId();
|
unsigned targetVid = token.getVendorId();
|
||||||
unsigned targetPid = token.getProductId();
|
unsigned targetPid = token.getProductId();
|
||||||
@ -58,21 +49,35 @@ DeviceBase* DeviceSignature::DeviceNew(DeviceToken& token)
|
|||||||
++sigIter;
|
++sigIter;
|
||||||
}
|
}
|
||||||
if (!foundSig)
|
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);
|
retval = foundSig->m_factory(&token);
|
||||||
if (!retval)
|
if (!retval)
|
||||||
return NULL;
|
return nullptr;
|
||||||
|
|
||||||
IHIDDevice* newDev = IHIDDeviceNew(token, *retval);
|
retval->m_hidDev = IHIDDeviceNew(token, *retval);
|
||||||
if (!newDev)
|
if (!retval->m_hidDev)
|
||||||
{
|
return nullptr;
|
||||||
delete retval;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -50,23 +50,31 @@ void DualshockPad::deviceDisconnected()
|
|||||||
void DualshockPad::initialCycle()
|
void DualshockPad::initialCycle()
|
||||||
{
|
{
|
||||||
uint8_t setupCommand[4] = {0x42, 0x0c, 0x00, 0x00}; //Tells controller to start sending changes on in pipe
|
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));
|
deviceError("Unable to send complete packet! Request size %x\n", sizeof(setupCommand));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint8_t btAddr[8];
|
uint8_t btAddr[8];
|
||||||
receiveReport(btAddr, sizeof(btAddr), 0x03F5);
|
receiveHIDReport(btAddr, sizeof(btAddr), HIDReportType::Feature, 0xF5);
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
m_btAddress[5 - i] = btAddr[i + 2]; // Copy into buffer reversed, so it is LSB first
|
m_btAddress[5 - i] = btAddr[i + 2]; // Copy into buffer reversed, so it is LSB first
|
||||||
}
|
}
|
||||||
|
|
||||||
void DualshockPad::transferCycle()
|
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;
|
return;
|
||||||
|
DualshockPadState state = *reinterpret_cast<const DualshockPadState*>(data);
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
state.m_accelerometer[i] = bswap16(state.m_accelerometer[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.rightDuration = 0;
|
||||||
m_report.rumble.rightOn = false;
|
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;
|
m_rumbleState = m_rumbleRequest;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -116,12 +124,6 @@ void DualshockPad::transferCycle()
|
|||||||
state.accYaw = (atan2(accXval, accZval) + M_PI) * RAD_TO_DEG;
|
state.accYaw = (atan2(accXval, accZval) + M_PI) * RAD_TO_DEG;
|
||||||
state.gyroZ = (state.m_gyrometerZ / 1023.f);
|
state.gyroZ = (state.m_gyrometerZ / 1023.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void DualshockPad::finalCycle()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // boo
|
} // boo
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
#include "boo/inputdev/DeviceToken.hpp"
|
#include "boo/inputdev/DeviceToken.hpp"
|
||||||
#include "boo/inputdev/DeviceBase.hpp"
|
#include "boo/inputdev/DeviceBase.hpp"
|
||||||
#include <IOKit/hid/IOHIDLib.h>
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
#include <IOKit/hid/IOHIDDevicePlugin.h>
|
||||||
|
#include <IOKit/hid/IOHIDDevice.h>
|
||||||
#include <IOKit/usb/IOUSBLib.h>
|
#include <IOKit/usb/IOUSBLib.h>
|
||||||
#include <IOKit/IOCFPlugIn.h>
|
#include <IOKit/IOCFPlugIn.h>
|
||||||
|
#include "IOKitPointer.hpp"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
@ -14,9 +17,10 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
DeviceToken& m_token;
|
DeviceToken& m_token;
|
||||||
DeviceBase& m_devImp;
|
DeviceBase& m_devImp;
|
||||||
|
|
||||||
IOUSBInterfaceInterface** m_usbIntf = NULL;
|
IUnknownPointer<IOUSBInterfaceInterface> m_usbIntf;
|
||||||
uint8_t m_usbIntfInPipe = 0;
|
uint8_t m_usbIntfInPipe = 0;
|
||||||
uint8_t m_usbIntfOutPipe = 0;
|
uint8_t m_usbIntfOutPipe = 0;
|
||||||
|
IUnknownPointer<IOHIDDeviceDeviceInterface> m_hidIntf;
|
||||||
bool m_runningTransferLoop = false;
|
bool m_runningTransferLoop = false;
|
||||||
|
|
||||||
const std::string& m_devPath;
|
const std::string& m_devPath;
|
||||||
@ -28,7 +32,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
{
|
{
|
||||||
if (m_usbIntf)
|
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 res == kIOReturnSuccess;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -39,7 +43,32 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
if (m_usbIntf)
|
if (m_usbIntf)
|
||||||
{
|
{
|
||||||
UInt32 readSize = length;
|
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)
|
if (res != kIOReturnSuccess)
|
||||||
return 0;
|
return 0;
|
||||||
return readSize;
|
return readSize;
|
||||||
@ -56,11 +85,11 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
|
||||||
/* Get the HID element's parent (USB interrupt transfer-interface) */
|
/* Get the HID element's parent (USB interrupt transfer-interface) */
|
||||||
io_iterator_t devIter;
|
IOObjectPointer<io_iterator_t> devIter;
|
||||||
io_registry_entry_t devEntry = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str());
|
IOObjectPointer<io_registry_entry_t> devEntry = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str());
|
||||||
IORegistryEntryGetChildIterator(devEntry, kIOServicePlane, &devIter);
|
IORegistryEntryGetChildIterator(devEntry, kIOServicePlane, &devIter);
|
||||||
io_object_t obj, interfaceEntry = 0;
|
IOObjectPointer<io_object_t> interfaceEntry;
|
||||||
while ((obj = IOIteratorNext(devIter)))
|
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(devIter))
|
||||||
{
|
{
|
||||||
if (IOObjectConformsTo(obj, kIOUSBInterfaceClassName))
|
if (IOObjectConformsTo(obj, kIOUSBInterfaceClassName))
|
||||||
interfaceEntry = obj;
|
interfaceEntry = obj;
|
||||||
@ -79,7 +108,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* IOKit Plugin COM interface (WTF Apple???) */
|
/* IOKit Plugin COM interface (WTF Apple???) */
|
||||||
IOCFPlugInInterface **iodev;
|
IOCFPluginPointer iodev;
|
||||||
SInt32 score;
|
SInt32 score;
|
||||||
IOReturn err;
|
IOReturn err;
|
||||||
err = IOCreatePlugInInterfaceForService(interfaceEntry,
|
err = IOCreatePlugInInterfaceForService(interfaceEntry,
|
||||||
@ -87,7 +116,6 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
kIOCFPlugInInterfaceID,
|
kIOCFPlugInInterfaceID,
|
||||||
&iodev,
|
&iodev,
|
||||||
&score);
|
&score);
|
||||||
IOObjectRelease(interfaceEntry);
|
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||||
@ -99,10 +127,8 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* USB interface function-pointer table */
|
/* USB interface function-pointer table */
|
||||||
IOUSBInterfaceInterface** intf = NULL;
|
IUnknownPointer<IOUSBInterfaceInterface> intf;
|
||||||
err = (*iodev)->QueryInterface(iodev,
|
err = iodev.As(&intf, kIOUSBInterfaceInterfaceID);
|
||||||
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
|
|
||||||
(LPVOID*)&intf);
|
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||||
@ -110,13 +136,12 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
device->m_devImp.deviceError(errStr);
|
device->m_devImp.deviceError(errStr);
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
IODestroyPlugInInterface(iodev);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Obtain exclusive lock on device */
|
/* Obtain exclusive lock on device */
|
||||||
device->m_usbIntf = intf;
|
device->m_usbIntf = intf;
|
||||||
err = (*intf)->USBInterfaceOpen(intf);
|
err = intf->USBInterfaceOpen(intf.storage());
|
||||||
if (err != kIOReturnSuccess)
|
if (err != kIOReturnSuccess)
|
||||||
{
|
{
|
||||||
if (err == kIOReturnExclusiveAccess)
|
if (err == kIOReturnExclusiveAccess)
|
||||||
@ -133,19 +158,17 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
}
|
}
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
(*intf)->Release(intf);
|
|
||||||
IODestroyPlugInInterface(iodev);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine pipe indices for interrupt I/O */
|
/* Determine pipe indices for interrupt I/O */
|
||||||
UInt8 numEndpoints = 0;
|
UInt8 numEndpoints = 0;
|
||||||
err = (*intf)->GetNumEndpoints(intf, &numEndpoints);
|
err = intf->GetNumEndpoints(intf.storage(), &numEndpoints);
|
||||||
for (int i=1 ; i<numEndpoints+1 ; ++i)
|
for (int i=1 ; i<numEndpoints+1 ; ++i)
|
||||||
{
|
{
|
||||||
UInt8 dir, num, tType, interval;
|
UInt8 dir, num, tType, interval;
|
||||||
UInt16 mPacketSz;
|
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 (tType == kUSBInterrupt)
|
||||||
{
|
{
|
||||||
if (dir == kUSBIn)
|
if (dir == kUSBIn)
|
||||||
@ -167,11 +190,8 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
device->m_devImp.finalCycle();
|
device->m_devImp.finalCycle();
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
(*intf)->USBInterfaceClose(intf);
|
intf->USBInterfaceClose(intf.storage());
|
||||||
(*intf)->Release(intf);
|
device->m_usbIntf = nullptr;
|
||||||
IODestroyPlugInInterface(iodev);
|
|
||||||
device->m_usbIntf = NULL;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _threadProcBTLL(HIDDeviceIOKit* device)
|
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)
|
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);
|
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 */
|
/* Return control to main thread */
|
||||||
device->m_runningTransferLoop = true;
|
device->m_runningTransferLoop = true;
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
@ -203,8 +319,15 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
/* Start transfer loop */
|
/* Start transfer loop */
|
||||||
device->m_devImp.initialCycle();
|
device->m_devImp.initialCycle();
|
||||||
while (device->m_runningTransferLoop)
|
while (device->m_runningTransferLoop)
|
||||||
|
{
|
||||||
|
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
|
||||||
device->m_devImp.transferCycle();
|
device->m_devImp.transferCycle();
|
||||||
|
}
|
||||||
device->m_devImp.finalCycle();
|
device->m_devImp.finalCycle();
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
intf->close(intf.storage(), kIOHIDOptionsTypeNone);
|
||||||
|
device->m_hidIntf = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deviceDisconnected()
|
void _deviceDisconnected()
|
||||||
@ -212,11 +335,6 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
m_runningTransferLoop = false;
|
m_runningTransferLoop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HIDDeviceIOKit(DeviceToken& token, DeviceBase& devImp)
|
HIDDeviceIOKit(DeviceToken& token, DeviceBase& devImp)
|
||||||
@ -224,14 +342,13 @@ public:
|
|||||||
m_devImp(devImp),
|
m_devImp(devImp),
|
||||||
m_devPath(token.getDevicePath())
|
m_devPath(token.getDevicePath())
|
||||||
{
|
{
|
||||||
devImp.m_hidDev = this;
|
|
||||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||||
DeviceToken::DeviceType dType = token.getDeviceType();
|
DeviceType dType = token.getDeviceType();
|
||||||
if (dType == DeviceToken::DeviceType::USB)
|
if (dType == DeviceType::USB)
|
||||||
m_thread = std::thread(_threadProcUSBLL, this);
|
m_thread = std::thread(_threadProcUSBLL, this);
|
||||||
else if (dType == DeviceToken::DeviceType::Bluetooth)
|
else if (dType == DeviceType::Bluetooth)
|
||||||
m_thread = std::thread(_threadProcBTLL, this);
|
m_thread = std::thread(_threadProcBTLL, this);
|
||||||
else if (dType == DeviceToken::DeviceType::GenericHID)
|
else if (dType == DeviceType::HID)
|
||||||
m_thread = std::thread(_threadProcHID, this);
|
m_thread = std::thread(_threadProcHID, this);
|
||||||
else
|
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 "boo/inputdev/DeviceFinder.hpp"
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include <IOKit/hid/IOHIDLib.h>
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
#include <IOKit/hid/IOHIDDevicePlugin.h>
|
||||||
|
#include <IOKit/hid/IOHIDKeys.h>
|
||||||
#include <IOKit/IOKitLib.h>
|
#include <IOKit/IOKitLib.h>
|
||||||
#include <IOKit/usb/IOUSBLib.h>
|
#include <IOKit/usb/IOUSBLib.h>
|
||||||
#include <IOKit/IOCFPlugIn.h>
|
#include <IOKit/IOCFPlugIn.h>
|
||||||
|
#include "IOKitPointer.hpp"
|
||||||
|
#include "CFPointer.hpp"
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
@ -13,7 +17,7 @@ namespace boo
|
|||||||
* Reference: http://oroboro.com/usb-serial-number-osx/
|
* 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];
|
UInt16 buffer[128];
|
||||||
|
|
||||||
@ -30,7 +34,7 @@ static bool getUSBStringDescriptor(IOUSBDeviceInterface182** usbDevice, UInt8 id
|
|||||||
request.wLength = sizeof(buffer);
|
request.wLength = sizeof(buffer);
|
||||||
request.pData = buffer;
|
request.pData = buffer;
|
||||||
|
|
||||||
kern_return_t err = (*usbDevice)->DeviceRequest(usbDevice, &request);
|
kern_return_t err = usbDevice->DeviceRequest(usbDevice.storage(), &request);
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
{
|
{
|
||||||
// the request failed... fairly uncommon for the USB disk driver, but not
|
// the request failed... fairly uncommon for the USB disk driver, but not
|
||||||
@ -61,72 +65,65 @@ class HIDListenerIOKit : public IHIDListener
|
|||||||
|
|
||||||
CFRunLoopRef m_listenerRunLoop;
|
CFRunLoopRef m_listenerRunLoop;
|
||||||
IONotificationPortRef m_llPort;
|
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;
|
bool m_scanningEnabled;
|
||||||
|
|
||||||
static void devicesConnectedUSBLL(HIDListenerIOKit* listener,
|
static void devicesConnectedUSBLL(HIDListenerIOKit* listener,
|
||||||
io_iterator_t iterator)
|
io_iterator_t iterator)
|
||||||
{
|
{
|
||||||
io_object_t obj;
|
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
|
||||||
while ((obj = IOIteratorNext(iterator)))
|
|
||||||
{
|
{
|
||||||
io_string_t devPath;
|
io_string_t devPath;
|
||||||
if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0)
|
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!listener->m_scanningEnabled ||
|
if (!listener->m_scanningEnabled ||
|
||||||
listener->m_finder._hasToken(devPath))
|
listener->m_finder._hasToken(devPath))
|
||||||
{
|
|
||||||
IOObjectRelease(obj);
|
|
||||||
continue;
|
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;
|
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 vstr[128] = {0};
|
||||||
char pstr[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,
|
IUnknownPointer<IOUSBDeviceInterface182> dev;
|
||||||
vid, pid, vstr, pstr, devPath)))
|
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 */
|
/* Matched-insertion failed; see if generic HID interface is available */
|
||||||
/* TODO: Do */
|
/* TODO: Do */
|
||||||
}
|
}
|
||||||
|
|
||||||
//printf("ADDED %08X %s\n", obj, devPath);
|
//printf("ADDED %08X %s\n", obj, devPath);
|
||||||
(*dev)->Release(dev);
|
|
||||||
IODestroyPlugInInterface(devServ);
|
|
||||||
IOObjectRelease(obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void devicesDisconnectedUSBLL(HIDListenerIOKit* listener,
|
static void devicesDisconnectedUSBLL(HIDListenerIOKit* listener,
|
||||||
@ -140,15 +137,114 @@ class HIDListenerIOKit : public IHIDListener
|
|||||||
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
io_object_t obj;
|
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
|
||||||
while ((obj = IOIteratorNext(iterator)))
|
|
||||||
{
|
{
|
||||||
io_string_t devPath;
|
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;
|
continue;
|
||||||
listener->m_finder._removeToken(devPath);
|
listener->m_finder._removeToken(devPath);
|
||||||
//printf("REMOVED %08X %s\n", obj, devPath);
|
//printf("REMOVED %08X %s\n", obj, devPath);
|
||||||
IOObjectRelease(obj);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,38 +252,54 @@ public:
|
|||||||
HIDListenerIOKit(DeviceFinder& finder)
|
HIDListenerIOKit(DeviceFinder& finder)
|
||||||
: m_finder(finder)
|
: m_finder(finder)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* Register Low-Level USB Matcher */
|
|
||||||
m_listenerRunLoop = CFRunLoopGetCurrent();
|
m_listenerRunLoop = CFRunLoopGetCurrent();
|
||||||
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
|
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||||
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
|
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
|
||||||
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
|
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
|
||||||
|
|
||||||
CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName);
|
|
||||||
CFRetain(matchDict);
|
|
||||||
|
|
||||||
m_scanningEnabled = true;
|
m_scanningEnabled = true;
|
||||||
kern_return_t llRet =
|
|
||||||
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
/* Register HID Matcher */
|
||||||
(IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif);
|
{
|
||||||
if (llRet == kIOReturnSuccess)
|
CFMutableDictionaryRef matchDict = IOServiceMatching("IOHIDDevice");
|
||||||
devicesConnectedUSBLL(this, m_llAddNotif);
|
CFRetain(matchDict);
|
||||||
|
|
||||||
llRet =
|
kern_return_t hidRet =
|
||||||
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
|
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
||||||
(IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif);
|
(IOServiceMatchingCallback)devicesConnectedHID, this, &m_hidAddNotif);
|
||||||
if (llRet == kIOReturnSuccess)
|
if (hidRet == kIOReturnSuccess)
|
||||||
devicesDisconnectedUSBLL(this, m_llRemoveNotif);
|
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;
|
m_scanningEnabled = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~HIDListenerIOKit()
|
~HIDListenerIOKit()
|
||||||
{
|
{
|
||||||
//CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
|
//CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
|
||||||
IOObjectRelease(m_llAddNotif);
|
|
||||||
IOObjectRelease(m_llRemoveNotif);
|
|
||||||
IONotificationPortDestroy(m_llPort);
|
IONotificationPortDestroy(m_llPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,12 +318,11 @@ public:
|
|||||||
/* Manual device scanning */
|
/* Manual device scanning */
|
||||||
bool scanNow()
|
bool scanNow()
|
||||||
{
|
{
|
||||||
io_iterator_t iter;
|
IOObjectPointer<io_iterator_t> iter;
|
||||||
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
|
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
|
||||||
IOServiceMatching(kIOUSBDeviceClassName), &iter) == kIOReturnSuccess)
|
IOServiceMatching(kIOUSBDeviceClassName), &iter) == kIOReturnSuccess)
|
||||||
{
|
{
|
||||||
devicesConnectedUSBLL(this, iter);
|
devicesConnectedUSBLL(this, iter.get());
|
||||||
IOObjectRelease(iter);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define IHIDDEVICE_HPP
|
#define IHIDDEVICE_HPP
|
||||||
|
|
||||||
#include "boo/inputdev/DeviceToken.hpp"
|
#include "boo/inputdev/DeviceToken.hpp"
|
||||||
|
#include "boo/inputdev/DeviceBase.hpp"
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
@ -12,8 +13,8 @@ class IHIDDevice
|
|||||||
virtual void _deviceDisconnected()=0;
|
virtual void _deviceDisconnected()=0;
|
||||||
virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0;
|
virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0;
|
||||||
virtual size_t _receiveUSBInterruptTransfer(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 bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
|
||||||
virtual size_t _recieveReport(const uint8_t* data, size_t length, uint16_t message) {return 0;}
|
virtual size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
|
||||||
public:
|
public:
|
||||||
inline virtual ~IHIDDevice() {}
|
inline virtual ~IHIDDevice() {}
|
||||||
};
|
};
|
||||||
|
132
lib/inputdev/IOKitPointer.hpp
Normal file
132
lib/inputdev/IOKitPointer.hpp
Normal file
@ -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
|
class TestDeviceFinder : public DeviceFinder
|
||||||
{
|
{
|
||||||
|
std::shared_ptr<DolphinSmashAdapter> smashAdapter;
|
||||||
DolphinSmashAdapter* smashAdapter = NULL;
|
std::shared_ptr<DualshockPad> ds3;
|
||||||
DualshockPad* ds3 = nullptr;
|
|
||||||
DolphinSmashAdapterCallback m_cb;
|
DolphinSmashAdapterCallback m_cb;
|
||||||
DualshockPadCallback m_ds3CB;
|
DualshockPadCallback m_ds3CB;
|
||||||
public:
|
public:
|
||||||
@ -87,14 +86,14 @@ public:
|
|||||||
{}
|
{}
|
||||||
void deviceConnected(DeviceToken& tok)
|
void deviceConnected(DeviceToken& tok)
|
||||||
{
|
{
|
||||||
smashAdapter = dynamic_cast<DolphinSmashAdapter*>(tok.openAndGetDevice());
|
smashAdapter = std::dynamic_pointer_cast<DolphinSmashAdapter>(tok.openAndGetDevice());
|
||||||
if (smashAdapter)
|
if (smashAdapter)
|
||||||
{
|
{
|
||||||
smashAdapter->setCallback(&m_cb);
|
smashAdapter->setCallback(&m_cb);
|
||||||
smashAdapter->startRumble(0);
|
smashAdapter->startRumble(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ds3 = dynamic_cast<DualshockPad*>(tok.openAndGetDevice());
|
ds3 = std::dynamic_pointer_cast<DualshockPad>(tok.openAndGetDevice());
|
||||||
if (ds3)
|
if (ds3)
|
||||||
{
|
{
|
||||||
ds3->setCallback(&m_ds3CB);
|
ds3->setCallback(&m_ds3CB);
|
||||||
@ -103,16 +102,10 @@ public:
|
|||||||
}
|
}
|
||||||
void deviceDisconnected(DeviceToken&, DeviceBase* device)
|
void deviceDisconnected(DeviceToken&, DeviceBase* device)
|
||||||
{
|
{
|
||||||
if (smashAdapter == device)
|
if (smashAdapter.get() == device)
|
||||||
{
|
smashAdapter.reset();
|
||||||
delete smashAdapter;
|
if (ds3.get() == device)
|
||||||
smashAdapter = NULL;
|
ds3.reset();
|
||||||
}
|
|
||||||
if (ds3 == device)
|
|
||||||
{
|
|
||||||
delete ds3;
|
|
||||||
ds3 = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user