From edaebc5e508c79d01f7649d9a69c3cb432f593ed Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Mon, 20 Apr 2015 18:02:43 -1000 Subject: [PATCH 01/17] lots of HID implementation (OS X only for now) --- include/boo.hpp | 9 +- include/inputdev/CDeviceBase.hpp | 11 +- include/inputdev/CDeviceFinder.hpp | 126 ++++++++++++++++++++++ include/inputdev/CDeviceToken.hpp | 60 +++++++++-- include/inputdev/CDolphinSmashAdapter.hpp | 12 +++ include/inputdev/DeviceClasses.hpp | 24 +++++ libBoo.pri | 5 +- src/inputdev/CDeviceBase.cpp | 12 +++ src/inputdev/CDeviceFinder.cpp | 1 - src/inputdev/CDolphinSmashAdapter.cpp | 5 + src/inputdev/CHIDDeviceIOKit.mm | 29 +++++ src/inputdev/CHIDListenerIOKit.cpp | 114 ++++++++++++++++++++ src/inputdev/CHIDListenerIOKit.mm | 1 - src/inputdev/DeviceClasses.cpp | 21 ++++ src/inputdev/IHIDDevice.hpp | 6 +- src/inputdev/IHIDListener.hpp | 18 ++++ test/main.cpp | 7 ++ 17 files changed, 443 insertions(+), 18 deletions(-) create mode 100644 include/inputdev/DeviceClasses.hpp delete mode 100644 src/inputdev/CDeviceFinder.cpp create mode 100644 src/inputdev/CHIDListenerIOKit.cpp delete mode 100644 src/inputdev/CHIDListenerIOKit.mm create mode 100644 src/inputdev/DeviceClasses.cpp diff --git a/include/boo.hpp b/include/boo.hpp index 8eba4dc..7821f5c 100644 --- a/include/boo.hpp +++ b/include/boo.hpp @@ -1,16 +1,21 @@ #ifndef BOO_HPP #define BOO_HPP -#include "IGraphicsContext.hpp" - #if defined(_WIN32) #error "No support for WGL" + #elif defined(__APPLE__) #include "mac/CCGLContext.hpp" typedef CCGLContext CGraphicsContext; + + #elif defined(__GNUC__) || defined(__clang__) #include "x11/CGLXContext.hpp" typedef CGLXContext CGraphicsContext; + #endif +#include "IGraphicsContext.hpp" +#include "inputdev/CDeviceFinder.hpp" + #endif // BOO_HPP diff --git a/include/inputdev/CDeviceBase.hpp b/include/inputdev/CDeviceBase.hpp index 0fa5876..22656c3 100644 --- a/include/inputdev/CDeviceBase.hpp +++ b/include/inputdev/CDeviceBase.hpp @@ -1,13 +1,18 @@ #ifndef CDEVICEBASE #define CDEVICEBASE -#include "CDeviceToken.hpp" +class CDeviceToken; +class IHIDDevice; class CDeviceBase { - + CDeviceToken* m_token; + IHIDDevice* m_hidDev; public: - + inline CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev) + : m_token(token), m_hidDev(hidDev) {} + void _deviceDisconnected(); + virtual void deviceDisconnected()=0; }; #endif // CDEVICEBASE diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index f096170..54678f9 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -1,5 +1,131 @@ #ifndef CDEVICEFINDER_HPP #define CDEVICEFINDER_HPP +#include +#include +#include +#include "CDeviceToken.hpp" +#include "IHIDListener.hpp" +#include "DeviceClasses.hpp" + +static class CDeviceFinder* skDevFinder = NULL; + +class CDeviceFinder +{ +public: + friend class CHIDListenerIOKit; + friend class CHIDListenerUdev; + friend class CHIDListenerWin32; + static inline CDeviceFinder* instance() {return skDevFinder;} + +private: + + /* Types this finder is interested in (immutable) */ + EDeviceMask m_types; + + /* Platform-specific USB event registration + * (for auto-scanning, NULL if not registered) */ + IHIDListener* m_listener; + + /* Set of presently-connected device tokens */ + TDeviceTokens m_tokens; + std::mutex m_tokensLock; + + /* Friend methods for platform-listener to find/insert/remove + * tokens with type-filtering */ + inline bool _hasToken(TDeviceHandle handle) + { + auto preCheck = m_tokens.find(handle); + if (preCheck != m_tokens.end()) + return true; + return false; + } + inline void _insertToken(CDeviceToken&& token) + { + if (BooDeviceMatchToken(token, m_types)) { + m_tokensLock.lock(); + m_tokens.insert(std::make_pair(token.getDeviceHandle(), token)); + m_tokensLock.unlock(); + } + } + inline void _removeToken(TDeviceHandle handle) + { + auto preCheck = m_tokens.find(handle); + if (preCheck != m_tokens.end()) + { + CDeviceToken& tok = preCheck->second; + tok._deviceClose(); + m_tokensLock.lock(); + m_tokens.erase(preCheck); + m_tokensLock.unlock(); + } + } + +public: + + class CDeviceTokensHandle + { + CDeviceFinder& m_finder; + public: + inline CDeviceTokensHandle(CDeviceFinder& finder) : m_finder(finder) + {m_finder.m_tokensLock.lock();} + inline ~CDeviceTokensHandle() {m_finder.m_tokensLock.unlock();} + inline TDeviceTokens::iterator begin() {return m_finder.m_tokens.begin();} + inline TDeviceTokens::iterator end() {return m_finder.m_tokens.end();} + }; + + /* Application must specify its interested device-types */ + CDeviceFinder(EDeviceMask types, bool autoScan=true) + : m_types(types), m_listener(NULL) + { + if (skDevFinder) + throw std::runtime_error("only one instance of CDeviceFinder may be constructed"); + skDevFinder = this; + if (autoScan) + startScanning(); + } + ~CDeviceFinder() + { + if (m_listener) + m_listener->stopScanning(); + delete m_listener; + skDevFinder = NULL; + } + + /* Get interested device-type mask */ + inline EDeviceMask getTypes() const {return m_types;} + + /* Iterable set of tokens */ + inline CDeviceTokensHandle getTokens() {return CDeviceTokensHandle(*this);} + + /* Automatic device scanning */ + inline bool startScanning() + { + if (!m_listener) + m_listener = IHIDListenerNew(*this); + if (m_listener) + return m_listener->startScanning(); + return false; + } + inline bool stopScanning() + { + if (!m_listener) + m_listener = IHIDListenerNew(*this); + if (m_listener) + return m_listener->stopScanning(); + return false; + } + + /* Manual device scanning */ + inline bool scanNow() + { + if (!m_listener) + m_listener = IHIDListenerNew(*this); + if (m_listener) + return m_listener->scanNow(); + return false; + } + +}; #endif // CDEVICEFINDER_HPP diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index 253e4fe..b6069c3 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -2,21 +2,65 @@ #define CDEVICETOKEN #include -#include +#include "CDeviceBase.hpp" +#include "DeviceClasses.hpp" + +#if __APPLE__ +#include +typedef IOHIDDeviceRef TDeviceHandle; +#elif _WIN32 +#elif __linux__ +#endif class CDeviceBase; class CDeviceToken { - std::string m_name; -#if __APPLE__ + unsigned m_vendorId; + unsigned m_productId; + std::string m_vendorName; + std::string m_productName; + TDeviceHandle m_devHandle; -#elif _WIN32 -#elif __linux__ -#endif + friend CDeviceBase; + CDeviceBase* m_connectedDev; + public: - const std::string& getName() const; - CDeviceBase* getDevice() const; + inline CDeviceToken(unsigned vid, unsigned pid, const char* vname, const char* pname, TDeviceHandle handle) + : m_vendorId(vid), m_productId(pid), m_devHandle(handle), m_connectedDev(NULL) + { + if (vname) + m_vendorName = vname; + if (pname) + m_productName = pname; + } + + inline unsigned getVendorId() const {return m_vendorId;} + inline unsigned getProductId() const {return m_productId;} + inline const std::string& getVendorName() const {return m_vendorName;} + inline const std::string& getProductName() const {return m_productName;} + inline TDeviceHandle getDeviceHandle() const {return m_devHandle;} + inline bool isDeviceOpen() const {return m_connectedDev;} + inline CDeviceBase* openAndGetDevice() + { + if (!m_connectedDev) + m_connectedDev = BooDeviceNew(this); + return m_connectedDev; + } + + inline void _deviceClose() + { + if (m_connectedDev) + m_connectedDev->deviceDisconnected(); + m_connectedDev = NULL; + } + + inline CDeviceToken operator =(const CDeviceToken& other) + {return CDeviceToken(other);} + inline bool operator ==(const CDeviceToken& rhs) const + {return m_devHandle == rhs.m_devHandle;} + inline bool operator <(const CDeviceToken& rhs) const + {return m_devHandle < rhs.m_devHandle;} }; #endif // CDEVICETOKEN diff --git a/include/inputdev/CDolphinSmashAdapter.hpp b/include/inputdev/CDolphinSmashAdapter.hpp index 94fd679..68c7ae9 100644 --- a/include/inputdev/CDolphinSmashAdapter.hpp +++ b/include/inputdev/CDolphinSmashAdapter.hpp @@ -1,5 +1,17 @@ #ifndef CDOLPHINSMASHADAPTER_HPP #define CDOLPHINSMASHADAPTER_HPP +#include "CDeviceBase.hpp" + +class CDolphinSmashAdapter final : public CDeviceBase +{ + void deviceDisconnected(); +public: + CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev) + : CDeviceBase(token, hidDev) + { + + } +}; #endif // CDOLPHINSMASHADAPTER_HPP diff --git a/include/inputdev/DeviceClasses.hpp b/include/inputdev/DeviceClasses.hpp new file mode 100644 index 0000000..5b5320c --- /dev/null +++ b/include/inputdev/DeviceClasses.hpp @@ -0,0 +1,24 @@ +#ifndef CDEVICECLASSES_HPP +#define CDEVICECLASSES_HPP + +#include "CDolphinSmashAdapter.hpp" +#include "CRevolutionPad.hpp" +#include "CCafeProPad.hpp" +#include "CDualshockPad.hpp" +#include "CGenericPad.hpp" + +enum EDeviceMask +{ + DEV_NONE = 0, + DEV_DOL_SMASH_ADAPTER = 1<<0, + DEV_RVL_PAD = 1<<1, + DEV_CAFE_PRO_PAD = 1<<2, + DEV_DUALSHOCK_PAD = 1<<3, + DEV_GENERIC_PAD = 1<<4, + DEV_ALL = 0xff +}; + +bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask); +CDeviceBase* BooDeviceNew(CDeviceToken* token); + +#endif // CDEVICECLASSES_HPP diff --git a/libBoo.pri b/libBoo.pri index 5561ba9..0f36c46 100644 --- a/libBoo.pri +++ b/libBoo.pri @@ -17,6 +17,7 @@ HEADERS += \ $$PWD/include/inputdev/CDeviceFinder.hpp \ $$PWD/include/inputdev/CDeviceToken.hpp \ $$PWD/include/inputdev/CDeviceBase.hpp \ + $$PWD/include/inputdev/DeviceClasses.hpp \ $$PWD/src/inputdev/IHIDDevice.hpp \ $$PWD/src/inputdev/IHIDListener.hpp @@ -39,8 +40,8 @@ SOURCES += \ $$PWD/src/inputdev/CCafeProPad.cpp \ $$PWD/src/inputdev/CDualshockPad.cpp \ $$PWD/src/inputdev/CGenericPad.cpp \ - $$PWD/src/inputdev/CDeviceFinder.cpp \ - $$PWD/src/inputdev/CDeviceBase.cpp + $$PWD/src/inputdev/CDeviceBase.cpp \ + $$PWD/src/inputdev/DeviceClasses.cpp unix:!macx:SOURCES += \ $$PWD/src/x11/CGLXContext.cpp \ diff --git a/src/inputdev/CDeviceBase.cpp b/src/inputdev/CDeviceBase.cpp index 9c5d1fa..2311700 100644 --- a/src/inputdev/CDeviceBase.cpp +++ b/src/inputdev/CDeviceBase.cpp @@ -1,3 +1,15 @@ #include "inputdev/CDeviceBase.hpp" +#include "inputdev/CDeviceToken.hpp" +#include "IHIDDevice.hpp" + + +void CDeviceBase::_deviceDisconnected() +{ + m_token->m_connectedDev = NULL; + m_token = NULL; + m_hidDev->deviceDisconnected(); + m_hidDev = NULL; +} + diff --git a/src/inputdev/CDeviceFinder.cpp b/src/inputdev/CDeviceFinder.cpp deleted file mode 100644 index 5d0b47b..0000000 --- a/src/inputdev/CDeviceFinder.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "inputdev/CDeviceFinder.hpp" diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index 4f67300..b25f0b4 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -1 +1,6 @@ #include "inputdev/CDolphinSmashAdapter.hpp" + +void CDolphinSmashAdapter::deviceDisconnected() +{ + +} diff --git a/src/inputdev/CHIDDeviceIOKit.mm b/src/inputdev/CHIDDeviceIOKit.mm index 2888b5b..e91faf8 100644 --- a/src/inputdev/CHIDDeviceIOKit.mm +++ b/src/inputdev/CHIDDeviceIOKit.mm @@ -1 +1,30 @@ #include "IHIDDevice.hpp" +#include "inputdev/CDeviceToken.hpp" +#include + +class CHIDDeviceIOKit final : public IHIDDevice +{ + IOHIDDeviceRef m_dev; +public: + + CHIDDeviceIOKit(CDeviceToken* token) + : m_dev(token->getDeviceHandle()) + { + IOHIDDeviceOpen(m_dev, kIOHIDOptionsTypeNone); + } + + CHIDDeviceIOKit() + { + IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone); + } + + void deviceDisconnected() + { + IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone); + } +}; + +IHIDDevice* IHIDDeviceNew(CDeviceToken* token) +{ + return new CHIDDeviceIOKit(token); +} diff --git a/src/inputdev/CHIDListenerIOKit.cpp b/src/inputdev/CHIDListenerIOKit.cpp new file mode 100644 index 0000000..7ee41df --- /dev/null +++ b/src/inputdev/CHIDListenerIOKit.cpp @@ -0,0 +1,114 @@ +#include "IHIDListener.hpp" +#include "inputdev/CDeviceFinder.hpp" +#include +#include + +class CHIDListenerIOKit final : public IHIDListener +{ + CDeviceFinder& m_finder; + + IOHIDManagerRef m_hidManager; + bool m_scanningEnabled; + + static void deviceConnected(CHIDListenerIOKit* listener, + IOReturn, + void*, + IOHIDDeviceRef device) + { + if (!listener->m_scanningEnabled) + return; + if (listener->m_finder._hasToken(device)) + return; + CFIndex vid, pid; + CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid); + CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)), kCFNumberCFIndexType, &pid); + CFStringRef manuf = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey)); + CFStringRef product = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + listener->m_finder._insertToken(CDeviceToken(vid, pid, + CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8), + CFStringGetCStringPtr(product, kCFStringEncodingUTF8), + device)); + } + + static void deviceDisconnected(CHIDListenerIOKit* listener, + IOReturn, + void*, + IOHIDDeviceRef device) + {listener->m_finder._removeToken(device);} + + static void applyDevice(IOHIDDeviceRef device, CHIDListenerIOKit* listener) + { + auto preCheck = listener->m_finder.m_tokens.find(device); + if (preCheck != listener->m_finder.m_tokens.end()) + return; + CFIndex vid, pid; + CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid); + CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)), kCFNumberCFIndexType, &pid); + CFStringRef manuf = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey)); + CFStringRef product = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + listener->m_finder.m_tokens.insert(std::make_pair(device, + CDeviceToken(vid, pid, + CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8), + CFStringGetCStringPtr(product, kCFStringEncodingUTF8), + device))); + } + +public: + CHIDListenerIOKit(CDeviceFinder& finder) + : m_finder(finder) + { + + /* Register HID Manager */ + m_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone); + IOHIDManagerSetDeviceMatching(m_hidManager, NULL); + IOHIDManagerRegisterDeviceMatchingCallback(m_hidManager, (IOHIDDeviceCallback)deviceConnected, this); + IOHIDManagerRegisterDeviceRemovalCallback(m_hidManager, (IOHIDDeviceCallback)deviceDisconnected, this); + IOHIDManagerScheduleWithRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOReturn ret = IOHIDManagerOpen(m_hidManager, kIOHIDManagerOptionNone); + if (ret != kIOReturnSuccess) + throw std::runtime_error("error establishing IOHIDManager"); + + /* Initial Device Add */ + m_scanningEnabled = true; + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); + m_scanningEnabled = false; + + } + + ~CHIDListenerIOKit() + { + IOHIDManagerUnscheduleFromRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDManagerClose(m_hidManager, kIOHIDManagerOptionNone); + CFRelease(m_hidManager); + } + + /* Automatic device scanning */ + bool startScanning() + { + m_scanningEnabled = true; + return true; + } + bool stopScanning() + { + m_scanningEnabled = false; + return true; + } + + /* Manual device scanning */ + bool scanNow() + { + CFSetRef devs = IOHIDManagerCopyDevices(m_hidManager); + m_finder.m_tokensLock.lock(); + CFSetApplyFunction(devs, (CFSetApplierFunction)applyDevice, this); + m_finder.m_tokensLock.unlock(); + CFRelease(devs); + return true; + } + +}; + +IHIDListener* IHIDListenerNew(CDeviceFinder& finder) +{ + return new CHIDListenerIOKit(finder); +} + diff --git a/src/inputdev/CHIDListenerIOKit.mm b/src/inputdev/CHIDListenerIOKit.mm deleted file mode 100644 index 90f36b2..0000000 --- a/src/inputdev/CHIDListenerIOKit.mm +++ /dev/null @@ -1 +0,0 @@ -#include "IHIDListener.hpp" diff --git a/src/inputdev/DeviceClasses.cpp b/src/inputdev/DeviceClasses.cpp new file mode 100644 index 0000000..be5e530 --- /dev/null +++ b/src/inputdev/DeviceClasses.cpp @@ -0,0 +1,21 @@ +#include "inputdev/DeviceClasses.hpp" +#include "inputdev/CDeviceToken.hpp" + +bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask) +{ + if (mask & DEV_DOL_SMASH_ADAPTER && + token.getVendorId() == 0x57e && token.getProductId() == 0x337) + return true; + return false; +} + +IHIDDevice* IHIDDeviceNew(CDeviceToken* token); +CDeviceBase* BooDeviceNew(CDeviceToken* token) +{ + IHIDDevice* newDev = IHIDDeviceNew(token); + + if (token->getVendorId() == 0x57e && token->getProductId() == 0x337) + return new CDolphinSmashAdapter(token, newDev); + + return NULL; +} diff --git a/src/inputdev/IHIDDevice.hpp b/src/inputdev/IHIDDevice.hpp index 869b179..9a1da5c 100644 --- a/src/inputdev/IHIDDevice.hpp +++ b/src/inputdev/IHIDDevice.hpp @@ -1,9 +1,13 @@ #ifndef IHIDDEVICE_HPP #define IHIDDEVICE_HPP +#include "inputdev/CDeviceToken.hpp" +class CDeviceBase; + class IHIDDevice { - + friend CDeviceBase; + virtual void deviceDisconnected()=0; }; #endif // IHIDDEVICE_HPP diff --git a/src/inputdev/IHIDListener.hpp b/src/inputdev/IHIDListener.hpp index a1118b1..8fbb46a 100644 --- a/src/inputdev/IHIDListener.hpp +++ b/src/inputdev/IHIDListener.hpp @@ -1,9 +1,27 @@ #ifndef IHIDLISTENER_HPP #define IHIDLISTENER_HPP +#include +#include +#include "CDeviceToken.hpp" +typedef std::map TDeviceTokens; +class CDeviceFinder; + class IHIDListener { +public: + virtual ~IHIDListener() {}; + + /* Automatic device scanning */ + virtual bool startScanning()=0; + virtual bool stopScanning()=0; + + /* Manual device scanning */ + virtual bool scanNow()=0; }; +/* Platform-specific constructor */ +IHIDListener* IHIDListenerNew(CDeviceFinder& finder); + #endif // IHIDLISTENER_HPP diff --git a/test/main.cpp b/test/main.cpp index f31a297..d3d3cef 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,14 +1,21 @@ +#include #include #include int main(int argc, char** argv) { + CDeviceFinder finder(DEV_DOL_SMASH_ADAPTER); + CDeviceToken& smashToken = finder.getTokens().begin()->second; + CDolphinSmashAdapter* smashAdapter = dynamic_cast(smashToken.openAndGetDevice()); + IGraphicsContext* ctx = new CGraphicsContext; if (ctx->create()) { } + + CFRunLoopRun(); delete ctx; return 0; From c1dc218bd1231c0a67946ebf76d4e01d69de8cb0 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Wed, 22 Apr 2015 11:48:23 -1000 Subject: [PATCH 02/17] better event handling --- include/inputdev/CDeviceBase.hpp | 4 ++- include/inputdev/CDeviceFinder.hpp | 11 +++++--- include/inputdev/CDeviceToken.hpp | 25 ++++++++++--------- include/inputdev/CDolphinSmashAdapter.hpp | 6 +---- libBoo.pri | 7 +++--- src/inputdev/CDeviceBase.cpp | 11 ++++++-- src/inputdev/CDolphinSmashAdapter.cpp | 9 ++++++- ...CHIDDeviceIOKit.mm => CHIDDeviceIOKit.cpp} | 13 ++++++---- src/inputdev/CHIDListenerIOKit.cpp | 16 ++++++------ src/inputdev/IHIDDevice.hpp | 2 +- src/inputdev/IHIDListener.hpp | 1 + test/main.cpp | 25 ++++++++++++++++--- 12 files changed, 85 insertions(+), 45 deletions(-) rename src/inputdev/{CHIDDeviceIOKit.mm => CHIDDeviceIOKit.cpp} (89%) diff --git a/include/inputdev/CDeviceBase.hpp b/include/inputdev/CDeviceBase.hpp index 22656c3..20a4636 100644 --- a/include/inputdev/CDeviceBase.hpp +++ b/include/inputdev/CDeviceBase.hpp @@ -8,10 +8,12 @@ class CDeviceBase { CDeviceToken* m_token; IHIDDevice* m_hidDev; + friend CDeviceToken; + void _deviceDisconnected(); public: inline CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev) : m_token(token), m_hidDev(hidDev) {} - void _deviceDisconnected(); + void closeDevice(); virtual void deviceDisconnected()=0; }; diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index 54678f9..7f6ef05 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -44,8 +44,9 @@ private: { if (BooDeviceMatchToken(token, m_types)) { m_tokensLock.lock(); - m_tokens.insert(std::make_pair(token.getDeviceHandle(), token)); + TInsertedDeviceToken inseredTok = m_tokens.insert(std::make_pair(token.getDeviceHandle(), std::move(token))); m_tokensLock.unlock(); + deviceConnected(inseredTok.first->second); } } inline void _removeToken(TDeviceHandle handle) @@ -55,6 +56,7 @@ private: { CDeviceToken& tok = preCheck->second; tok._deviceClose(); + deviceDisconnected(tok); m_tokensLock.lock(); m_tokens.erase(preCheck); m_tokensLock.unlock(); @@ -75,14 +77,12 @@ public: }; /* Application must specify its interested device-types */ - CDeviceFinder(EDeviceMask types, bool autoScan=true) + CDeviceFinder(EDeviceMask types) : m_types(types), m_listener(NULL) { if (skDevFinder) throw std::runtime_error("only one instance of CDeviceFinder may be constructed"); skDevFinder = this; - if (autoScan) - startScanning(); } ~CDeviceFinder() { @@ -126,6 +126,9 @@ public: return false; } + virtual void deviceConnected(CDeviceToken&) {} + virtual void deviceDisconnected(CDeviceToken&) {} + }; #endif // CDEVICEFINDER_HPP diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index b6069c3..1f4fb26 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -12,8 +12,6 @@ typedef IOHIDDeviceRef TDeviceHandle; #elif __linux__ #endif -class CDeviceBase; - class CDeviceToken { unsigned m_vendorId; @@ -22,10 +20,21 @@ class CDeviceToken std::string m_productName; TDeviceHandle m_devHandle; - friend CDeviceBase; + friend class CDeviceBase; CDeviceBase* m_connectedDev; + + friend class CDeviceFinder; + inline void _deviceClose() + { + printf("CLOSE %p\n", this); + if (m_connectedDev) + m_connectedDev->_deviceDisconnected(); + m_connectedDev = NULL; + } public: + CDeviceToken(const CDeviceToken&) = delete; + CDeviceToken(CDeviceToken&&) = default; inline CDeviceToken(unsigned vid, unsigned pid, const char* vname, const char* pname, TDeviceHandle handle) : m_vendorId(vid), m_productId(pid), m_devHandle(handle), m_connectedDev(NULL) { @@ -43,20 +52,12 @@ public: inline bool isDeviceOpen() const {return m_connectedDev;} inline CDeviceBase* openAndGetDevice() { + printf("OPEN %p\n", this); if (!m_connectedDev) m_connectedDev = BooDeviceNew(this); return m_connectedDev; } - inline void _deviceClose() - { - if (m_connectedDev) - m_connectedDev->deviceDisconnected(); - m_connectedDev = NULL; - } - - inline CDeviceToken operator =(const CDeviceToken& other) - {return CDeviceToken(other);} inline bool operator ==(const CDeviceToken& rhs) const {return m_devHandle == rhs.m_devHandle;} inline bool operator <(const CDeviceToken& rhs) const diff --git a/include/inputdev/CDolphinSmashAdapter.hpp b/include/inputdev/CDolphinSmashAdapter.hpp index 68c7ae9..6ea8ff3 100644 --- a/include/inputdev/CDolphinSmashAdapter.hpp +++ b/include/inputdev/CDolphinSmashAdapter.hpp @@ -7,11 +7,7 @@ class CDolphinSmashAdapter final : public CDeviceBase { void deviceDisconnected(); public: - CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev) - : CDeviceBase(token, hidDev) - { - - } + CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev); }; #endif // CDOLPHINSMASHADAPTER_HPP diff --git a/libBoo.pri b/libBoo.pri index 0f36c46..f108921 100644 --- a/libBoo.pri +++ b/libBoo.pri @@ -49,12 +49,13 @@ unix:!macx:SOURCES += \ $$PWD/src/inputdev/CHIDListenerUdev.cpp macx:SOURCES += \ - $$PWD/src/mac/CCGLContext.cpp + $$PWD/src/mac/CCGLContext.cpp \ + $$PWD/src/inputdev/CHIDDeviceIOKit.cpp \ + $$PWD/src/inputdev/CHIDListenerIOKit.cpp macx:OBJECTIVE_SOURCES += \ $$PWD/src/mac/CCGLCocoaView.mm \ - $$PWD/src/inputdev/CHIDDeviceIOKit.mm \ - $$PWD/src/inputdev/CHIDListenerIOKit.mm + win32:SOURCES += \ $$PWD/src/win/CWGLContext.cpp \ diff --git a/src/inputdev/CDeviceBase.cpp b/src/inputdev/CDeviceBase.cpp index 2311700..aa3eff7 100644 --- a/src/inputdev/CDeviceBase.cpp +++ b/src/inputdev/CDeviceBase.cpp @@ -5,11 +5,18 @@ void CDeviceBase::_deviceDisconnected() { - m_token->m_connectedDev = NULL; + deviceDisconnected(); m_token = NULL; - m_hidDev->deviceDisconnected(); + m_hidDev->_deviceDisconnected(); m_hidDev = NULL; } +void CDeviceBase::closeDevice() +{ + if (m_token) + m_token->_deviceClose(); +} + + diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index b25f0b4..8489379 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -1,6 +1,13 @@ #include "inputdev/CDolphinSmashAdapter.hpp" +#include + +CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev) +: CDeviceBase(token, hidDev) +{ + printf("I've been plugged!!\n"); +} void CDolphinSmashAdapter::deviceDisconnected() { - + printf("I've been unplugged!!\n"); } diff --git a/src/inputdev/CHIDDeviceIOKit.mm b/src/inputdev/CHIDDeviceIOKit.cpp similarity index 89% rename from src/inputdev/CHIDDeviceIOKit.mm rename to src/inputdev/CHIDDeviceIOKit.cpp index e91faf8..990a298 100644 --- a/src/inputdev/CHIDDeviceIOKit.mm +++ b/src/inputdev/CHIDDeviceIOKit.cpp @@ -5,6 +5,12 @@ class CHIDDeviceIOKit final : public IHIDDevice { IOHIDDeviceRef m_dev; + + void _deviceDisconnected() + { + IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone); + } + public: CHIDDeviceIOKit(CDeviceToken* token) @@ -13,15 +19,12 @@ public: IOHIDDeviceOpen(m_dev, kIOHIDOptionsTypeNone); } - CHIDDeviceIOKit() + ~CHIDDeviceIOKit() { IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone); } - void deviceDisconnected() - { - IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone); - } + }; IHIDDevice* IHIDDeviceNew(CDeviceToken* token) diff --git a/src/inputdev/CHIDListenerIOKit.cpp b/src/inputdev/CHIDListenerIOKit.cpp index 7ee41df..6aa964d 100644 --- a/src/inputdev/CHIDListenerIOKit.cpp +++ b/src/inputdev/CHIDListenerIOKit.cpp @@ -34,23 +34,23 @@ class CHIDListenerIOKit final : public IHIDListener IOReturn, void*, IOHIDDeviceRef device) - {listener->m_finder._removeToken(device);} + { + listener->m_finder._removeToken(device); + } static void applyDevice(IOHIDDeviceRef device, CHIDListenerIOKit* listener) { - auto preCheck = listener->m_finder.m_tokens.find(device); - if (preCheck != listener->m_finder.m_tokens.end()) + if (listener->m_finder._hasToken(device)) return; CFIndex vid, pid; CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid); CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)), kCFNumberCFIndexType, &pid); CFStringRef manuf = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey)); CFStringRef product = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); - listener->m_finder.m_tokens.insert(std::make_pair(device, - CDeviceToken(vid, pid, - CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8), - CFStringGetCStringPtr(product, kCFStringEncodingUTF8), - device))); + listener->m_finder._insertToken(CDeviceToken(vid, pid, + CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8), + CFStringGetCStringPtr(product, kCFStringEncodingUTF8), + device)); } public: diff --git a/src/inputdev/IHIDDevice.hpp b/src/inputdev/IHIDDevice.hpp index 9a1da5c..58174f7 100644 --- a/src/inputdev/IHIDDevice.hpp +++ b/src/inputdev/IHIDDevice.hpp @@ -7,7 +7,7 @@ class CDeviceBase; class IHIDDevice { friend CDeviceBase; - virtual void deviceDisconnected()=0; + virtual void _deviceDisconnected()=0; }; #endif // IHIDDEVICE_HPP diff --git a/src/inputdev/IHIDListener.hpp b/src/inputdev/IHIDListener.hpp index 8fbb46a..e6230a9 100644 --- a/src/inputdev/IHIDListener.hpp +++ b/src/inputdev/IHIDListener.hpp @@ -5,6 +5,7 @@ #include #include "CDeviceToken.hpp" typedef std::map TDeviceTokens; +typedef std::pair TInsertedDeviceToken; class CDeviceFinder; class IHIDListener diff --git a/test/main.cpp b/test/main.cpp index d3d3cef..c0e221d 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -3,11 +3,30 @@ #include #include +class CTestDeviceFinder : public CDeviceFinder +{ + CDolphinSmashAdapter* smashAdapter = NULL; +public: + CTestDeviceFinder() + : CDeviceFinder(DEV_DOL_SMASH_ADAPTER) + {} + void deviceConnected(CDeviceToken& tok) + { + printf("CONNECTED %s %s\n", tok.getVendorName().c_str(), tok.getProductName().c_str()); + smashAdapter = dynamic_cast(tok.openAndGetDevice()); + } + void deviceDisconnected(CDeviceToken& tok) + { + printf("DISCONNECTED %s %s\n", tok.getVendorName().c_str(), tok.getProductName().c_str()); + delete smashAdapter; + smashAdapter = NULL; + } +}; + int main(int argc, char** argv) { - CDeviceFinder finder(DEV_DOL_SMASH_ADAPTER); - CDeviceToken& smashToken = finder.getTokens().begin()->second; - CDolphinSmashAdapter* smashAdapter = dynamic_cast(smashToken.openAndGetDevice()); + CTestDeviceFinder finder; + finder.startScanning(); IGraphicsContext* ctx = new CGraphicsContext; From 92e7c3fb0471fc2aadd3f5286459373c6f099ecf Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Wed, 22 Apr 2015 14:46:32 -1000 Subject: [PATCH 03/17] eliminated some hid-thread race conditions --- include/inputdev/CDeviceBase.hpp | 4 +- include/inputdev/CDeviceFinder.hpp | 5 +- include/inputdev/CDeviceToken.hpp | 2 - include/inputdev/CDolphinSmashAdapter.hpp | 1 + include/inputdev/DeviceClasses.hpp | 3 + src/inputdev/CDeviceBase.cpp | 19 +++++- src/inputdev/CDolphinSmashAdapter.cpp | 9 ++- src/inputdev/CHIDDeviceIOKit.cpp | 73 +++++++++++++++++++++-- src/inputdev/CHIDListenerIOKit.cpp | 18 ++++-- src/inputdev/DeviceClasses.cpp | 9 ++- src/inputdev/IHIDDevice.hpp | 3 + test/main.cpp | 11 ++-- 12 files changed, 131 insertions(+), 26 deletions(-) diff --git a/include/inputdev/CDeviceBase.hpp b/include/inputdev/CDeviceBase.hpp index 20a4636..b2c068a 100644 --- a/include/inputdev/CDeviceBase.hpp +++ b/include/inputdev/CDeviceBase.hpp @@ -11,8 +11,8 @@ class CDeviceBase friend CDeviceToken; void _deviceDisconnected(); public: - inline CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev) - : m_token(token), m_hidDev(hidDev) {} + CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev); + virtual ~CDeviceBase(); void closeDevice(); virtual void deviceDisconnected()=0; }; diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index 7f6ef05..30da74f 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -55,8 +55,9 @@ private: if (preCheck != m_tokens.end()) { CDeviceToken& tok = preCheck->second; + CDeviceBase* dev = tok.m_connectedDev; tok._deviceClose(); - deviceDisconnected(tok); + deviceDisconnected(tok, dev); m_tokensLock.lock(); m_tokens.erase(preCheck); m_tokensLock.unlock(); @@ -127,7 +128,7 @@ public: } virtual void deviceConnected(CDeviceToken&) {} - virtual void deviceDisconnected(CDeviceToken&) {} + virtual void deviceDisconnected(CDeviceToken&, CDeviceBase*) {} }; diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index 1f4fb26..9109dbd 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -26,7 +26,6 @@ class CDeviceToken friend class CDeviceFinder; inline void _deviceClose() { - printf("CLOSE %p\n", this); if (m_connectedDev) m_connectedDev->_deviceDisconnected(); m_connectedDev = NULL; @@ -52,7 +51,6 @@ public: inline bool isDeviceOpen() const {return m_connectedDev;} inline CDeviceBase* openAndGetDevice() { - printf("OPEN %p\n", this); if (!m_connectedDev) m_connectedDev = BooDeviceNew(this); return m_connectedDev; diff --git a/include/inputdev/CDolphinSmashAdapter.hpp b/include/inputdev/CDolphinSmashAdapter.hpp index 6ea8ff3..50fb7e0 100644 --- a/include/inputdev/CDolphinSmashAdapter.hpp +++ b/include/inputdev/CDolphinSmashAdapter.hpp @@ -8,6 +8,7 @@ class CDolphinSmashAdapter final : public CDeviceBase void deviceDisconnected(); public: CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev); + ~CDolphinSmashAdapter(); }; #endif // CDOLPHINSMASHADAPTER_HPP diff --git a/include/inputdev/DeviceClasses.hpp b/include/inputdev/DeviceClasses.hpp index 5b5320c..42c3c4e 100644 --- a/include/inputdev/DeviceClasses.hpp +++ b/include/inputdev/DeviceClasses.hpp @@ -7,6 +7,9 @@ #include "CDualshockPad.hpp" #include "CGenericPad.hpp" +#define VID_NINTENDO 0x57e +#define PID_SMASH_ADAPTER 0x337 + enum EDeviceMask { DEV_NONE = 0, diff --git a/src/inputdev/CDeviceBase.cpp b/src/inputdev/CDeviceBase.cpp index aa3eff7..d748986 100644 --- a/src/inputdev/CDeviceBase.cpp +++ b/src/inputdev/CDeviceBase.cpp @@ -2,17 +2,30 @@ #include "inputdev/CDeviceToken.hpp" #include "IHIDDevice.hpp" +CDeviceBase::CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev) +: m_token(token), m_hidDev(hidDev) +{ + hidDev->_setDeviceImp(this); +} + +CDeviceBase::~CDeviceBase() +{ + delete m_hidDev; +} void CDeviceBase::_deviceDisconnected() { deviceDisconnected(); m_token = NULL; - m_hidDev->_deviceDisconnected(); - m_hidDev = NULL; + if (m_hidDev) + { + m_hidDev->_deviceDisconnected(); + delete m_hidDev; + m_hidDev = NULL; + } } - void CDeviceBase::closeDevice() { if (m_token) diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index 8489379..0dbd6b1 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -4,10 +4,15 @@ CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev) : CDeviceBase(token, hidDev) { - printf("I've been plugged!!\n"); + +} + +CDolphinSmashAdapter::~CDolphinSmashAdapter() +{ + } void CDolphinSmashAdapter::deviceDisconnected() { - printf("I've been unplugged!!\n"); + } diff --git a/src/inputdev/CHIDDeviceIOKit.cpp b/src/inputdev/CHIDDeviceIOKit.cpp index 990a298..81675a9 100644 --- a/src/inputdev/CHIDDeviceIOKit.cpp +++ b/src/inputdev/CHIDDeviceIOKit.cpp @@ -1,27 +1,92 @@ #include "IHIDDevice.hpp" #include "inputdev/CDeviceToken.hpp" +#include "inputdev/CDeviceBase.hpp" #include +#include + +#define MAX_REPORT_SIZE 65536 class CHIDDeviceIOKit final : public IHIDDevice { + CDeviceToken* m_token; IOHIDDeviceRef m_dev; + std::mutex m_initMutex; + std::condition_variable m_initCond; + std::thread* m_thread; + CFRunLoopRef m_runLoop; + CDeviceBase* m_devImp; + + static void _inputReport(CHIDDeviceIOKit* device, + IOReturn result, + void* sender, + IOHIDReportType type, + uint32_t reportID, + uint8_t* report, + CFIndex reportLength) + { + + } + static void _disconnect(CHIDDeviceIOKit* device, + IOReturn result, + IOHIDDeviceRef sender) + { + device->_deviceDisconnected(); + } + static void _threadProc(CHIDDeviceIOKit* device) + { + char thrName[128]; + snprintf(thrName, 128, "%s Device Thread", device->m_token->getProductName().c_str()); + pthread_setname_np(thrName); + __block std::unique_lock lk(device->m_initMutex); + device->m_runLoop = CFRunLoopGetCurrent(); + CFRunLoopAddObserver(device->m_runLoop, + CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopEntry, false, 0, + ^(CFRunLoopObserverRef, CFRunLoopActivity) { + lk.unlock(); + device->m_initCond.notify_one(); + }), kCFRunLoopCommonModes); + + uint8_t* inputBuf = new uint8_t[MAX_REPORT_SIZE]; + IOHIDDeviceRegisterInputReportCallback(device->m_dev, inputBuf, MAX_REPORT_SIZE, + (IOHIDReportCallback)_inputReport, device); + IOHIDDeviceRegisterRemovalCallback(device->m_dev, (IOHIDCallback)_disconnect, device); + IOHIDDeviceScheduleWithRunLoop(device->m_dev, device->m_runLoop, kCFRunLoopDefaultMode); + IOHIDDeviceOpen(device->m_dev, kIOHIDOptionsTypeNone); + CFRunLoopRun(); + if (device->m_runLoop) + IOHIDDeviceClose(device->m_dev, kIOHIDOptionsTypeNone); + } void _deviceDisconnected() { - IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone); + CFRunLoopRef rl = m_runLoop; + m_runLoop = NULL; + if (rl) + CFRunLoopStop(rl); + } + + void _setDeviceImp(CDeviceBase* dev) + { + m_devImp = dev; } public: CHIDDeviceIOKit(CDeviceToken* token) - : m_dev(token->getDeviceHandle()) + : m_token(token), + m_dev(token->getDeviceHandle()) { - IOHIDDeviceOpen(m_dev, kIOHIDOptionsTypeNone); + std::unique_lock lk(m_initMutex); + m_thread = new std::thread(_threadProc, this); + m_initCond.wait(lk); } ~CHIDDeviceIOKit() { - IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone); + if (m_runLoop) + CFRunLoopStop(m_runLoop); + m_thread->detach(); + delete m_thread; } diff --git a/src/inputdev/CHIDListenerIOKit.cpp b/src/inputdev/CHIDListenerIOKit.cpp index 6aa964d..09a9325 100644 --- a/src/inputdev/CHIDListenerIOKit.cpp +++ b/src/inputdev/CHIDListenerIOKit.cpp @@ -7,6 +7,7 @@ class CHIDListenerIOKit final : public IHIDListener { CDeviceFinder& m_finder; + CFRunLoopRef m_listenerRunLoop; IOHIDManagerRef m_hidManager; bool m_scanningEnabled; @@ -31,10 +32,18 @@ class CHIDListenerIOKit final : public IHIDListener } static void deviceDisconnected(CHIDListenerIOKit* listener, - IOReturn, - void*, + IOReturn ret, + void* sender, IOHIDDeviceRef device) { + if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop) + { + CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{ + deviceDisconnected(listener, ret, sender, device); + }); + CFRunLoopWakeUp(listener->m_listenerRunLoop); + return; + } listener->m_finder._removeToken(device); } @@ -63,7 +72,8 @@ public: IOHIDManagerSetDeviceMatching(m_hidManager, NULL); IOHIDManagerRegisterDeviceMatchingCallback(m_hidManager, (IOHIDDeviceCallback)deviceConnected, this); IOHIDManagerRegisterDeviceRemovalCallback(m_hidManager, (IOHIDDeviceCallback)deviceDisconnected, this); - IOHIDManagerScheduleWithRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + m_listenerRunLoop = CFRunLoopGetCurrent(); + IOHIDManagerScheduleWithRunLoop(m_hidManager, m_listenerRunLoop, kCFRunLoopDefaultMode); IOReturn ret = IOHIDManagerOpen(m_hidManager, kIOHIDManagerOptionNone); if (ret != kIOReturnSuccess) throw std::runtime_error("error establishing IOHIDManager"); @@ -77,7 +87,7 @@ public: ~CHIDListenerIOKit() { - IOHIDManagerUnscheduleFromRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDManagerUnscheduleFromRunLoop(m_hidManager, m_listenerRunLoop, kCFRunLoopDefaultMode); IOHIDManagerClose(m_hidManager, kIOHIDManagerOptionNone); CFRelease(m_hidManager); } diff --git a/src/inputdev/DeviceClasses.cpp b/src/inputdev/DeviceClasses.cpp index be5e530..8c6c0e3 100644 --- a/src/inputdev/DeviceClasses.cpp +++ b/src/inputdev/DeviceClasses.cpp @@ -1,10 +1,11 @@ #include "inputdev/DeviceClasses.hpp" #include "inputdev/CDeviceToken.hpp" +#include "IHIDDevice.hpp" bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask) { if (mask & DEV_DOL_SMASH_ADAPTER && - token.getVendorId() == 0x57e && token.getProductId() == 0x337) + token.getVendorId() == VID_NINTENDO && token.getProductId() == PID_SMASH_ADAPTER) return true; return false; } @@ -13,9 +14,13 @@ IHIDDevice* IHIDDeviceNew(CDeviceToken* token); CDeviceBase* BooDeviceNew(CDeviceToken* token) { IHIDDevice* newDev = IHIDDeviceNew(token); + if (!newDev) + return NULL; - if (token->getVendorId() == 0x57e && token->getProductId() == 0x337) + if (token->getVendorId() == VID_NINTENDO && token->getProductId() == PID_SMASH_ADAPTER) return new CDolphinSmashAdapter(token, newDev); + else + delete newDev; return NULL; } diff --git a/src/inputdev/IHIDDevice.hpp b/src/inputdev/IHIDDevice.hpp index 58174f7..7f3da47 100644 --- a/src/inputdev/IHIDDevice.hpp +++ b/src/inputdev/IHIDDevice.hpp @@ -7,7 +7,10 @@ class CDeviceBase; class IHIDDevice { friend CDeviceBase; + virtual void _setDeviceImp(CDeviceBase* dev)=0; virtual void _deviceDisconnected()=0; +public: + inline virtual ~IHIDDevice() {}; }; #endif // IHIDDEVICE_HPP diff --git a/test/main.cpp b/test/main.cpp index c0e221d..1ee6aa1 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -12,14 +12,15 @@ public: {} void deviceConnected(CDeviceToken& tok) { - printf("CONNECTED %s %s\n", tok.getVendorName().c_str(), tok.getProductName().c_str()); smashAdapter = dynamic_cast(tok.openAndGetDevice()); } - void deviceDisconnected(CDeviceToken& tok) + void deviceDisconnected(CDeviceToken&, CDeviceBase* device) { - printf("DISCONNECTED %s %s\n", tok.getVendorName().c_str(), tok.getProductName().c_str()); - delete smashAdapter; - smashAdapter = NULL; + if (smashAdapter == device) + { + delete smashAdapter; + smashAdapter = NULL; + } } }; From 437710934642039f1ff501a63b50ef1750c2ea6e Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Thu, 23 Apr 2015 14:24:15 -1000 Subject: [PATCH 04/17] full IOKit support for smash adapter --- include/inputdev/CDeviceBase.hpp | 27 ++- include/inputdev/CDeviceFinder.hpp | 10 +- include/inputdev/CDeviceToken.hpp | 21 +-- include/inputdev/CDolphinSmashAdapter.hpp | 51 +++++- include/inputdev/DeviceClasses.hpp | 2 +- src/inputdev/CDeviceBase.cpp | 27 ++- src/inputdev/CDolphinSmashAdapter.cpp | 134 ++++++++++++++- src/inputdev/CHIDDeviceIOKit.cpp | 194 ++++++++++++++++++++-- src/inputdev/CHIDListenerIOKit.cpp | 175 ++++++++++++++++++- src/inputdev/DeviceClasses.cpp | 30 +++- src/inputdev/IHIDDevice.hpp | 4 +- src/inputdev/IHIDListener.hpp | 4 +- test/main.cpp | 19 +++ 13 files changed, 627 insertions(+), 71 deletions(-) diff --git a/include/inputdev/CDeviceBase.hpp b/include/inputdev/CDeviceBase.hpp index b2c068a..1d6d3a4 100644 --- a/include/inputdev/CDeviceBase.hpp +++ b/include/inputdev/CDeviceBase.hpp @@ -1,20 +1,35 @@ #ifndef CDEVICEBASE #define CDEVICEBASE -class CDeviceToken; -class IHIDDevice; +#include +#include class CDeviceBase { - CDeviceToken* m_token; - IHIDDevice* m_hidDev; - friend CDeviceToken; + friend class CDeviceToken; + friend class CHIDDeviceIOKit; + friend class CHIDDeviceUdev; + friend class CHIDDeviceWin32; + + class CDeviceToken* m_token; + class IHIDDevice* m_hidDev; void _deviceDisconnected(); + public: - CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev); + CDeviceBase(CDeviceToken* token); virtual ~CDeviceBase(); void closeDevice(); virtual void deviceDisconnected()=0; + + /* Low-Level API */ + bool sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length); + size_t receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length); + virtual void transferCycle() {}; + + /* High-Level API */ + bool sendReport(const uint8_t* data, size_t length); + virtual size_t receiveReport(uint8_t* data, size_t length) {}; + }; #endif // CDEVICEBASE diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index 30da74f..1704541 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -33,9 +33,9 @@ private: /* Friend methods for platform-listener to find/insert/remove * tokens with type-filtering */ - inline bool _hasToken(TDeviceHandle handle) + inline bool _hasToken(const std::string& path) { - auto preCheck = m_tokens.find(handle); + auto preCheck = m_tokens.find(path); if (preCheck != m_tokens.end()) return true; return false; @@ -44,14 +44,14 @@ private: { if (BooDeviceMatchToken(token, m_types)) { m_tokensLock.lock(); - TInsertedDeviceToken inseredTok = m_tokens.insert(std::make_pair(token.getDeviceHandle(), std::move(token))); + TInsertedDeviceToken inseredTok = m_tokens.insert(std::make_pair(token.getDevicePath(), std::move(token))); m_tokensLock.unlock(); deviceConnected(inseredTok.first->second); } } - inline void _removeToken(TDeviceHandle handle) + inline void _removeToken(const std::string& path) { - auto preCheck = m_tokens.find(handle); + auto preCheck = m_tokens.find(path); if (preCheck != m_tokens.end()) { CDeviceToken& tok = preCheck->second; diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index 9109dbd..68bc89b 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -5,20 +5,13 @@ #include "CDeviceBase.hpp" #include "DeviceClasses.hpp" -#if __APPLE__ -#include -typedef IOHIDDeviceRef TDeviceHandle; -#elif _WIN32 -#elif __linux__ -#endif - class CDeviceToken { unsigned m_vendorId; unsigned m_productId; std::string m_vendorName; std::string m_productName; - TDeviceHandle m_devHandle; + std::string m_devPath; friend class CDeviceBase; CDeviceBase* m_connectedDev; @@ -34,8 +27,8 @@ class CDeviceToken public: CDeviceToken(const CDeviceToken&) = delete; CDeviceToken(CDeviceToken&&) = default; - inline CDeviceToken(unsigned vid, unsigned pid, const char* vname, const char* pname, TDeviceHandle handle) - : m_vendorId(vid), m_productId(pid), m_devHandle(handle), m_connectedDev(NULL) + inline CDeviceToken(unsigned vid, unsigned pid, const char* vname, const char* pname, const char* path) + : m_vendorId(vid), m_productId(pid), m_devPath(path), m_connectedDev(NULL) { if (vname) m_vendorName = vname; @@ -47,19 +40,19 @@ public: inline unsigned getProductId() const {return m_productId;} inline const std::string& getVendorName() const {return m_vendorName;} inline const std::string& getProductName() const {return m_productName;} - inline TDeviceHandle getDeviceHandle() const {return m_devHandle;} + inline const std::string& getDevicePath() const {return m_devPath;} inline bool isDeviceOpen() const {return m_connectedDev;} inline CDeviceBase* openAndGetDevice() { if (!m_connectedDev) - m_connectedDev = BooDeviceNew(this); + m_connectedDev = BooDeviceNew(*this); return m_connectedDev; } inline bool operator ==(const CDeviceToken& rhs) const - {return m_devHandle == rhs.m_devHandle;} + {return m_devPath == rhs.m_devPath;} inline bool operator <(const CDeviceToken& rhs) const - {return m_devHandle < rhs.m_devHandle;} + {return m_devPath < rhs.m_devPath;} }; #endif // CDEVICETOKEN diff --git a/include/inputdev/CDolphinSmashAdapter.hpp b/include/inputdev/CDolphinSmashAdapter.hpp index 50fb7e0..7786b29 100644 --- a/include/inputdev/CDolphinSmashAdapter.hpp +++ b/include/inputdev/CDolphinSmashAdapter.hpp @@ -1,14 +1,63 @@ #ifndef CDOLPHINSMASHADAPTER_HPP #define CDOLPHINSMASHADAPTER_HPP +#include #include "CDeviceBase.hpp" +struct IDolphinSmashAdapterCallback +{ + enum EDolphinControllerType + { + DOL_TYPE_NONE = 0, + DOL_TYPE_NORMAL = 0x10, + DOL_TYPE_WAVEBIRD = 0x20, + DOL_TYPE_RUMBLE = 0xF0 + }; + enum EDolphinControllerButtons + { + DOL_START = 1<<0, + DOL_Z = 1<<1, + DOL_L = 1<<2, + DOL_R = 1<<3, + DOL_A = 1<<8, + DOL_B = 1<<9, + DOL_X = 1<<10, + DOL_Y = 1<<11, + DOL_LEFT = 1<<12, + DOL_RIGHT = 1<<13, + DOL_DOWN = 1<<14, + DOL_UP = 1<<15 + }; + struct SDolphinControllerState + { + uint8_t m_leftStick[2]; + uint8_t m_rightStick[2]; + uint8_t m_analogTriggers[2]; + uint16_t m_btns; + }; + virtual void controllerConnected(unsigned idx, EDolphinControllerType type) {} + virtual void controllerDisconnected(unsigned idx, EDolphinControllerType type) {} + virtual void controllerUpdate(unsigned idx, EDolphinControllerType type, + const SDolphinControllerState& state) {} +}; + class CDolphinSmashAdapter final : public CDeviceBase { + IDolphinSmashAdapterCallback* m_callback; + uint8_t m_knownControllers; + uint8_t m_rumbleRequest; + uint8_t m_rumbleState; + bool m_didHandshake; void deviceDisconnected(); + void transferCycle(); public: - CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev); + CDolphinSmashAdapter(CDeviceToken* token); ~CDolphinSmashAdapter(); + + inline void setCallback(IDolphinSmashAdapterCallback* cb) + {m_callback = cb; m_knownControllers = 0;} + inline void startRumble(unsigned idx) {if (idx >= 4) return; m_rumbleRequest |= 1<= 4) return; m_rumbleRequest &= ~(1<_setDeviceImp(this); } CDeviceBase::~CDeviceBase() @@ -25,11 +24,31 @@ void CDeviceBase::_deviceDisconnected() } } - void CDeviceBase::closeDevice() { if (m_token) m_token->_deviceClose(); } +bool CDeviceBase::sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length) +{ + if (m_hidDev) + return m_hidDev->_sendInterruptTransfer(pipe, data, length); + return false; +} + +size_t CDeviceBase::receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length) +{ + if (m_hidDev) + return m_hidDev->_receiveInterruptTransfer(pipe, data, length); + return false; +} + +bool CDeviceBase::sendReport(const uint8_t* data, size_t length) +{ + if (m_hidDev) + return m_hidDev->_sendReport(data, length); + return false; +} + diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index 0dbd6b1..41ac550 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -1,17 +1,143 @@ #include "inputdev/CDolphinSmashAdapter.hpp" #include +#include -CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev) -: CDeviceBase(token, hidDev) +/* Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c + */ + +static const bool BUTTON_MASK[] = +{ + true, + true, + true, + true, + false, + false, + false, + false, + true, + true, + true, + true, + true, + true, + true, + true +}; + +CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token) +: CDeviceBase(token), + m_callback(NULL), + m_knownControllers(0), + m_rumbleRequest(0), + m_rumbleState(0), + m_didHandshake(false) { - } CDolphinSmashAdapter::~CDolphinSmashAdapter() { - } +static const uint8_t HANDSHAKE_PAYLOAD[] = {0x13}; + +static inline IDolphinSmashAdapterCallback::EDolphinControllerType +parseType(unsigned char status) +{ + unsigned char type = status & (IDolphinSmashAdapterCallback::DOL_TYPE_NORMAL | + IDolphinSmashAdapterCallback::DOL_TYPE_WAVEBIRD); + switch (type) + { + case IDolphinSmashAdapterCallback::DOL_TYPE_NORMAL: + case IDolphinSmashAdapterCallback::DOL_TYPE_WAVEBIRD: + return (IDolphinSmashAdapterCallback::EDolphinControllerType)type; + default: + return IDolphinSmashAdapterCallback::DOL_TYPE_NONE; + } +} + +static inline IDolphinSmashAdapterCallback::EDolphinControllerType +parseState(IDolphinSmashAdapterCallback::SDolphinControllerState* stateOut, uint8_t* payload) +{ + memset(stateOut, 0, sizeof(IDolphinSmashAdapterCallback::SDolphinControllerState)); + unsigned char status = payload[0]; + IDolphinSmashAdapterCallback::EDolphinControllerType type = parseType(status); + + IDolphinSmashAdapterCallback::EDolphinControllerType extra = + ((status & 0x04) != 0) ? IDolphinSmashAdapterCallback::DOL_TYPE_RUMBLE : + IDolphinSmashAdapterCallback::DOL_TYPE_NONE; + + stateOut->m_btns = (uint16_t)payload[1] << 8 | (uint16_t)payload[2]; + + stateOut->m_leftStick[0] = payload[3]; + stateOut->m_leftStick[1] = payload[4] ^ 0xFF; + stateOut->m_rightStick[0] = payload[5]; + stateOut->m_rightStick[1] = payload[6] ^ 0xFF; + stateOut->m_analogTriggers[0] = payload[7]; + stateOut->m_analogTriggers[1] = payload[8]; + + return static_cast(type|extra); +} + +void CDolphinSmashAdapter::transferCycle() +{ + if (!m_didHandshake) + { + if (!sendInterruptTransfer(0, HANDSHAKE_PAYLOAD, sizeof(HANDSHAKE_PAYLOAD))) + return; + //printf("SENT HANDSHAKE %d\n", res); + m_didHandshake = true; + } + else + { + uint8_t payload[37]; + size_t recvSz = receiveInterruptTransfer(0, payload, sizeof(payload)); + if (recvSz != 37 || payload[0] != 0x21) + return; + //printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]); + + if (!m_callback) + return; + + /* Parse controller states */ + uint8_t* controller = &payload[1]; + bool rumbleMask[4] = {false}; + for (int i=0 ; i<4 ; i++, controller += 9) + { + IDolphinSmashAdapterCallback::SDolphinControllerState state; + IDolphinSmashAdapterCallback::EDolphinControllerType type = parseState(&state, controller); + if (type && !(m_knownControllers & 1<controllerConnected(i, type); + } + else if (!type && (m_knownControllers & 1<controllerDisconnected(i, type); + } + m_callback->controllerUpdate(i, type, state); + rumbleMask[i] = type & IDolphinSmashAdapterCallback::DOL_TYPE_RUMBLE; + } + + /* Send rumble message (if needed) */ + uint8_t rumbleReq = m_rumbleRequest; + if (rumbleReq != m_rumbleState) + { + uint8_t rumbleMessage[5] = {0x11}; + for (int i=0 ; i<4 ; ++i) + { + if (rumbleReq & 1< +#include +#include #include #define MAX_REPORT_SIZE 65536 class CHIDDeviceIOKit final : public IHIDDevice { - CDeviceToken* m_token; - IOHIDDeviceRef m_dev; + CDeviceToken& m_token; + CDeviceBase& m_devImp; + + IOUSBInterfaceInterface** m_usbIntf = NULL; + uint8_t m_usbIntfInPipe = 0; + uint8_t m_usbIntfOutPipe = 0; + bool m_runningTransferLoop = false; + + const std::string& m_devPath; std::mutex m_initMutex; std::condition_variable m_initCond; std::thread* m_thread; - CFRunLoopRef m_runLoop; - CDeviceBase* m_devImp; + CFRunLoopRef m_runLoop = NULL; + + bool _sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length) + { + if (m_usbIntf) + { + IOReturn res = (*m_usbIntf)->WritePipe(m_usbIntf, m_usbIntfOutPipe, (void*)data, length); + return res == kIOReturnSuccess; + } + return false; + } + + size_t _receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length) + { + if (m_usbIntf) + { + UInt32 readSize = length; + IOReturn res = (*m_usbIntf)->ReadPipe(m_usbIntf, m_usbIntfInPipe, data, &readSize); + if (res != kIOReturnSuccess) + return 0; + return readSize; + } + return 0; + } + + static void _threadProcLL(CHIDDeviceIOKit* device) + { + char thrName[128]; + snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str()); + pthread_setname_np(thrName); + std::unique_lock lk(device->m_initMutex); + + /* Get the HID element's parent (USB interrupt transfer-interface) */ + io_iterator_t devIter; + io_registry_entry_t devEntry = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str()); + IORegistryEntryGetChildIterator(devEntry, kIOServicePlane, &devIter); + io_object_t obj, interfaceEntry = 0; + while ((obj = IOIteratorNext(devIter))) + { + if (IOObjectConformsTo(obj, kIOUSBInterfaceClassName)) + interfaceEntry = obj; + else + IOObjectRelease(obj); + } + if (!interfaceEntry) + { + throw std::runtime_error("unable to find device interface"); + lk.unlock(); + device->m_initCond.notify_one(); + return; + } + + /* IOKit Plugin COM interface (WTF Apple???) */ + IOCFPlugInInterface **iodev; + SInt32 score; + IOReturn err; + err = IOCreatePlugInInterfaceForService(interfaceEntry, + kIOUSBInterfaceUserClientTypeID, + kIOCFPlugInInterfaceID, + &iodev, + &score); + IOObjectRelease(interfaceEntry); + if (err) + { + throw std::runtime_error("unable to obtain IOKit plugin service"); + lk.unlock(); + device->m_initCond.notify_one(); + return; + } + + /* USB interface function-pointer table */ + IOUSBInterfaceInterface** intf = NULL; + err = (*iodev)->QueryInterface(iodev, + CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), + (LPVOID*)&intf); + if (err) + { + throw std::runtime_error("unable to query IOKit USB interface"); + lk.unlock(); + device->m_initCond.notify_one(); + IODestroyPlugInInterface(iodev); + return; + } + + /* Obtain exclusive lock on device */ + device->m_usbIntf = intf; + err = (*intf)->USBInterfaceOpen(intf); + if (err != kIOReturnSuccess) + { + if (err == kIOReturnExclusiveAccess) + throw std::runtime_error("unable to open IOKit USB interface; someone else using it"); + else + throw std::runtime_error("unable to open IOKit USB interface"); + lk.unlock(); + device->m_initCond.notify_one(); + (*intf)->Release(intf); + IODestroyPlugInInterface(iodev); + return; + } + + /* Determine pipe indices for interrupt I/O */ + UInt8 numEndpoints = 0; + err = (*intf)->GetNumEndpoints(intf, &numEndpoints); + for (int i=1 ; iGetPipeProperties(intf, i, &dir, &num, &tType, &mPacketSz, &interval); + if (tType == kUSBInterrupt) + { + if (dir == kUSBIn) + device->m_usbIntfInPipe = num; + else if (dir == kUSBOut) + device->m_usbIntfOutPipe = num; + } + } + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + while (device->m_runningTransferLoop) + { + device->m_devImp.transferCycle(); + } + + /* Cleanup */ + (*intf)->USBInterfaceClose(intf); + (*intf)->Release(intf); + IODestroyPlugInInterface(iodev); + device->m_usbIntf = NULL; + + } + static void _inputReport(CHIDDeviceIOKit* device, IOReturn result, @@ -32,10 +175,11 @@ class CHIDDeviceIOKit final : public IHIDDevice { device->_deviceDisconnected(); } - static void _threadProc(CHIDDeviceIOKit* device) + + static void _threadProcHL(CHIDDeviceIOKit* device) { char thrName[128]; - snprintf(thrName, 128, "%s Device Thread", device->m_token->getProductName().c_str()); + snprintf(thrName, 128, "%s HID Thread", device->m_token.getProductName().c_str()); pthread_setname_np(thrName); __block std::unique_lock lk(device->m_initMutex); device->m_runLoop = CFRunLoopGetCurrent(); @@ -47,14 +191,17 @@ class CHIDDeviceIOKit final : public IHIDDevice }), kCFRunLoopCommonModes); uint8_t* inputBuf = new uint8_t[MAX_REPORT_SIZE]; - IOHIDDeviceRegisterInputReportCallback(device->m_dev, inputBuf, MAX_REPORT_SIZE, + io_registry_entry_t devServ = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str()); + IOHIDDeviceRef dev = IOHIDDeviceCreate(kCFAllocatorDefault, devServ); + IOHIDDeviceRegisterInputReportCallback(dev, inputBuf, MAX_REPORT_SIZE, (IOHIDReportCallback)_inputReport, device); - IOHIDDeviceRegisterRemovalCallback(device->m_dev, (IOHIDCallback)_disconnect, device); - IOHIDDeviceScheduleWithRunLoop(device->m_dev, device->m_runLoop, kCFRunLoopDefaultMode); - IOHIDDeviceOpen(device->m_dev, kIOHIDOptionsTypeNone); + IOHIDDeviceRegisterRemovalCallback(dev, (IOHIDCallback)_disconnect, device); + IOHIDDeviceScheduleWithRunLoop(dev, device->m_runLoop, kCFRunLoopDefaultMode); + IOHIDDeviceOpen(dev, kIOHIDOptionsTypeNone); CFRunLoopRun(); if (device->m_runLoop) - IOHIDDeviceClose(device->m_dev, kIOHIDOptionsTypeNone); + IOHIDDeviceClose(dev, kIOHIDOptionsTypeNone); + CFRelease(dev); } void _deviceDisconnected() @@ -63,21 +210,31 @@ class CHIDDeviceIOKit final : public IHIDDevice m_runLoop = NULL; if (rl) CFRunLoopStop(rl); + m_runningTransferLoop = false; } - void _setDeviceImp(CDeviceBase* dev) + bool _sendReport(const uint8_t* data, size_t length) { - m_devImp = dev; + if (m_runLoop) + { + + } + return false; } public: - CHIDDeviceIOKit(CDeviceToken* token) + CHIDDeviceIOKit(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel) : m_token(token), - m_dev(token->getDeviceHandle()) + m_devImp(devImp), + m_devPath(token.getDevicePath()) { + devImp.m_hidDev = this; std::unique_lock lk(m_initMutex); - m_thread = new std::thread(_threadProc, this); + if (lowLevel) + m_thread = new std::thread(_threadProcLL, this); + else + m_thread = new std::thread(_threadProcHL, this); m_initCond.wait(lk); } @@ -85,6 +242,7 @@ public: { if (m_runLoop) CFRunLoopStop(m_runLoop); + m_runningTransferLoop = false; m_thread->detach(); delete m_thread; } @@ -92,7 +250,7 @@ public: }; -IHIDDevice* IHIDDeviceNew(CDeviceToken* token) +IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel) { - return new CHIDDeviceIOKit(token); + return new CHIDDeviceIOKit(token, devImp, lowLevel); } diff --git a/src/inputdev/CHIDListenerIOKit.cpp b/src/inputdev/CHIDListenerIOKit.cpp index 09a9325..1a413ad 100644 --- a/src/inputdev/CHIDListenerIOKit.cpp +++ b/src/inputdev/CHIDListenerIOKit.cpp @@ -2,6 +2,51 @@ #include "inputdev/CDeviceFinder.hpp" #include #include +#include +#include +#include + +/* Reference: http://oroboro.com/usb-serial-number-osx/ + */ + +static bool getUSBStringDescriptor(IOUSBDeviceInterface182** usbDevice, UInt8 idx, char* out) +{ + UInt16 buffer[128]; + + // wow... we're actually forced to make hard coded bus requests. Its like + // hard disk programming in the 80's! + IOUSBDevRequest request; + + request.bmRequestType = USBmakebmRequestType(kUSBIn, + kUSBStandard, + kUSBDevice); + request.bRequest = kUSBRqGetDescriptor; + request.wValue = (kUSBStringDesc << 8) | idx; + request.wIndex = 0x409; // english + request.wLength = sizeof(buffer); + request.pData = buffer; + + kern_return_t err = (*usbDevice)->DeviceRequest(usbDevice, &request); + if (err != 0) + { + // the request failed... fairly uncommon for the USB disk driver, but not + // so uncommon for other devices. This can also be less reliable if your + // disk is mounted through an external USB hub. At this level we actually + // have to worry about hardware issues like this. + return false; + } + + // we're mallocing this string just as an example. But you probably will want + // to do something smarter, like pre-allocated buffers in the info class, or + // use a string class. + unsigned count = (request.wLenDone - 1) / 2; + unsigned i; + for (i=0 ; im_scanningEnabled) return; - if (listener->m_finder._hasToken(device)) + io_string_t devPath; + if (IORegistryEntryGetPath(IOHIDDeviceGetService(device), kIOServicePlane, devPath) != 0) + return; + if (listener->m_finder._hasToken(devPath)) return; CFIndex vid, pid; CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid); @@ -28,7 +77,7 @@ class CHIDListenerIOKit final : public IHIDListener listener->m_finder._insertToken(CDeviceToken(vid, pid, CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8), CFStringGetCStringPtr(product, kCFStringEncodingUTF8), - device)); + devPath)); } static void deviceDisconnected(CHIDListenerIOKit* listener, @@ -44,12 +93,18 @@ class CHIDListenerIOKit final : public IHIDListener CFRunLoopWakeUp(listener->m_listenerRunLoop); return; } - listener->m_finder._removeToken(device); + io_string_t devPath; + if (IORegistryEntryGetPath(IOHIDDeviceGetService(device), kIOServicePlane, devPath) != 0) + return; + listener->m_finder._removeToken(devPath); } static void applyDevice(IOHIDDeviceRef device, CHIDListenerIOKit* listener) { - if (listener->m_finder._hasToken(device)) + io_string_t devPath; + if (IORegistryEntryGetPath(IOHIDDeviceGetService(device), kIOServicePlane, devPath) != 0) + return; + if (listener->m_finder._hasToken(devPath)) return; CFIndex vid, pid; CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid); @@ -59,7 +114,84 @@ class CHIDListenerIOKit final : public IHIDListener listener->m_finder._insertToken(CDeviceToken(vid, pid, CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8), CFStringGetCStringPtr(product, kCFStringEncodingUTF8), - device)); + devPath)); + } + + static void devicesConnectedLL(CHIDListenerIOKit* listener, + io_iterator_t iterator) + { + io_object_t obj; + while ((obj = IOIteratorNext(iterator))) + { + io_string_t devPath; + if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0) + continue; + + if (!listener->m_scanningEnabled || + listener->m_finder._hasToken(devPath)) + { + IOObjectRelease(obj); + continue; + } + + IOCFPlugInInterface** devServ; + SInt32 score; + IOReturn err; + err = IOCreatePlugInInterfaceForService(obj, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devServ, &score); + if (err != kIOReturnSuccess) + throw std::runtime_error("unable to open IOKit plugin interface"); + + IOUSBDeviceInterface182 **dev; + err = (*devServ)->QueryInterface(devServ, + CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182), + (LPVOID*)&dev); + if (err != kIOReturnSuccess) + throw std::runtime_error("unable to open IOKit device interface"); + + UInt16 vid, pid; + (*dev)->GetDeviceVendor(dev, &vid); + (*dev)->GetDeviceProduct(dev, &pid); + + UInt8 vstridx, pstridx; + (*dev)->USBGetManufacturerStringIndex(dev, &vstridx); + (*dev)->USBGetProductStringIndex(dev, &pstridx); + + char vstr[128] = {0}; + char pstr[128] = {0}; + getUSBStringDescriptor(dev, vstridx, vstr); + getUSBStringDescriptor(dev, pstridx, pstr); + + listener->m_finder._insertToken(CDeviceToken(vid, pid, vstr, pstr, devPath)); + + //printf("ADDED %08X %s\n", obj, devPath); + (*dev)->Release(dev); + IODestroyPlugInInterface(devServ); + IOObjectRelease(obj); + } + + } + + static void devicesDisconnectedLL(CHIDListenerIOKit* listener, + io_iterator_t iterator) + { + if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop) + { + CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{ + devicesDisconnectedLL(listener, iterator); + }); + CFRunLoopWakeUp(listener->m_listenerRunLoop); + return; + } + io_object_t obj; + while ((obj = IOIteratorNext(iterator))) + { + io_string_t devPath; + if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0) + continue; + listener->m_finder._removeToken(devPath); + //printf("REMOVED %08X %s\n", obj, devPath); + IOObjectRelease(obj); + } } public: @@ -78,9 +210,38 @@ public: if (ret != kIOReturnSuccess) throw std::runtime_error("error establishing IOHIDManager"); - /* Initial Device Add */ + /* Initial HID Device Add */ m_scanningEnabled = true; CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); + + /* Register Low-Level Matcher */ + m_llPort = IONotificationPortCreate(kIOMasterPortDefault); + CFRunLoopAddSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode); + + CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName); + CFIndex nintendoVid = VID_NINTENDO; + CFIndex smashPid = PID_SMASH_ADAPTER; + CFNumberRef nintendoVidNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &nintendoVid); + CFNumberRef smashPidNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &smashPid); + CFDictionaryAddValue(matchDict, CFSTR(kUSBVendorID), nintendoVidNum); + CFDictionaryAddValue(matchDict, CFSTR(kUSBProductID), smashPidNum); + CFRelease(nintendoVidNum); + CFRelease(smashPidNum); + CFRetain(matchDict); + + io_iterator_t initialIt; + kern_return_t llRet = + IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict, + (IOServiceMatchingCallback)devicesConnectedLL, this, &initialIt); + if (llRet == 0) + devicesConnectedLL(this, initialIt); + + llRet = + IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict, + (IOServiceMatchingCallback)devicesDisconnectedLL, this, &initialIt); + if (llRet == 0) + devicesDisconnectedLL(this, initialIt); + m_scanningEnabled = false; } @@ -90,6 +251,8 @@ public: IOHIDManagerUnscheduleFromRunLoop(m_hidManager, m_listenerRunLoop, kCFRunLoopDefaultMode); IOHIDManagerClose(m_hidManager, kIOHIDManagerOptionNone); CFRelease(m_hidManager); + CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode); + IONotificationPortDestroy(m_llPort); } /* Automatic device scanning */ diff --git a/src/inputdev/DeviceClasses.cpp b/src/inputdev/DeviceClasses.cpp index 8c6c0e3..e38c037 100644 --- a/src/inputdev/DeviceClasses.cpp +++ b/src/inputdev/DeviceClasses.cpp @@ -10,17 +10,29 @@ bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask) return false; } -IHIDDevice* IHIDDeviceNew(CDeviceToken* token); -CDeviceBase* BooDeviceNew(CDeviceToken* token) +IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel); +CDeviceBase* BooDeviceNew(CDeviceToken& token) { - IHIDDevice* newDev = IHIDDeviceNew(token); - if (!newDev) + + CDeviceBase* retval = NULL; + bool lowLevel = false; + + if (token.getVendorId() == VID_NINTENDO && token.getProductId() == PID_SMASH_ADAPTER) + { + retval = new CDolphinSmashAdapter(&token); + lowLevel = true; + } + + if (!retval) return NULL; - if (token->getVendorId() == VID_NINTENDO && token->getProductId() == PID_SMASH_ADAPTER) - return new CDolphinSmashAdapter(token, newDev); - else - delete newDev; + IHIDDevice* newDev = IHIDDeviceNew(token, *retval, lowLevel); + if (!newDev) + { + delete retval; + return NULL; + } + + return retval; - return NULL; } diff --git a/src/inputdev/IHIDDevice.hpp b/src/inputdev/IHIDDevice.hpp index 7f3da47..474d755 100644 --- a/src/inputdev/IHIDDevice.hpp +++ b/src/inputdev/IHIDDevice.hpp @@ -7,8 +7,10 @@ class CDeviceBase; class IHIDDevice { friend CDeviceBase; - virtual void _setDeviceImp(CDeviceBase* dev)=0; virtual void _deviceDisconnected()=0; + virtual bool _sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)=0; + virtual size_t _receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)=0; + virtual bool _sendReport(const uint8_t* data, size_t length)=0; public: inline virtual ~IHIDDevice() {}; }; diff --git a/src/inputdev/IHIDListener.hpp b/src/inputdev/IHIDListener.hpp index e6230a9..e1bf41d 100644 --- a/src/inputdev/IHIDListener.hpp +++ b/src/inputdev/IHIDListener.hpp @@ -1,10 +1,10 @@ #ifndef IHIDLISTENER_HPP #define IHIDLISTENER_HPP -#include +#include #include #include "CDeviceToken.hpp" -typedef std::map TDeviceTokens; +typedef std::unordered_map TDeviceTokens; typedef std::pair TInsertedDeviceToken; class CDeviceFinder; diff --git a/test/main.cpp b/test/main.cpp index 1ee6aa1..ddb42c6 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -3,9 +3,27 @@ #include #include +class CDolphinSmashAdapterCallback : public IDolphinSmashAdapterCallback +{ + void controllerConnected(unsigned idx, EDolphinControllerType type) + { + printf("CONTROLLER %u CONNECTED\n", idx); + } + void controllerDisconnected(unsigned idx, EDolphinControllerType type) + { + printf("CONTROLLER %u DISCONNECTED\n", idx); + } + void controllerUpdate(unsigned idx, EDolphinControllerType type, + const SDolphinControllerState& state) + { + printf("CONTROLLER %u UPDATE %d %d\n", idx, state.m_leftStick[0], state.m_leftStick[1]); + } +}; + class CTestDeviceFinder : public CDeviceFinder { CDolphinSmashAdapter* smashAdapter = NULL; + CDolphinSmashAdapterCallback m_cb; public: CTestDeviceFinder() : CDeviceFinder(DEV_DOL_SMASH_ADAPTER) @@ -13,6 +31,7 @@ public: void deviceConnected(CDeviceToken& tok) { smashAdapter = dynamic_cast(tok.openAndGetDevice()); + smashAdapter->setCallback(&m_cb); } void deviceDisconnected(CDeviceToken&, CDeviceBase* device) { From 79b9e0fd9ba9834adef9734785be857b2a0dde8a Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Thu, 23 Apr 2015 19:50:08 -1000 Subject: [PATCH 05/17] better rumble/update logic for smash adapter --- include/inputdev/CDolphinSmashAdapter.hpp | 1 - src/inputdev/CDolphinSmashAdapter.cpp | 42 ++++++----------------- test/main.cpp | 1 + 3 files changed, 12 insertions(+), 32 deletions(-) diff --git a/include/inputdev/CDolphinSmashAdapter.hpp b/include/inputdev/CDolphinSmashAdapter.hpp index 7786b29..0516135 100644 --- a/include/inputdev/CDolphinSmashAdapter.hpp +++ b/include/inputdev/CDolphinSmashAdapter.hpp @@ -11,7 +11,6 @@ struct IDolphinSmashAdapterCallback DOL_TYPE_NONE = 0, DOL_TYPE_NORMAL = 0x10, DOL_TYPE_WAVEBIRD = 0x20, - DOL_TYPE_RUMBLE = 0xF0 }; enum EDolphinControllerButtons { diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index 41ac550..acc4778 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -5,26 +5,6 @@ /* Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c */ -static const bool BUTTON_MASK[] = -{ - true, - true, - true, - true, - false, - false, - false, - false, - true, - true, - true, - true, - true, - true, - true, - true -}; - CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token) : CDeviceBase(token), m_callback(NULL), @@ -57,15 +37,13 @@ parseType(unsigned char status) } static inline IDolphinSmashAdapterCallback::EDolphinControllerType -parseState(IDolphinSmashAdapterCallback::SDolphinControllerState* stateOut, uint8_t* payload) +parseState(IDolphinSmashAdapterCallback::SDolphinControllerState* stateOut, uint8_t* payload, bool& rumble) { memset(stateOut, 0, sizeof(IDolphinSmashAdapterCallback::SDolphinControllerState)); unsigned char status = payload[0]; IDolphinSmashAdapterCallback::EDolphinControllerType type = parseType(status); - IDolphinSmashAdapterCallback::EDolphinControllerType extra = - ((status & 0x04) != 0) ? IDolphinSmashAdapterCallback::DOL_TYPE_RUMBLE : - IDolphinSmashAdapterCallback::DOL_TYPE_NONE; + rumble = ((status & 0x04) != 0) ? true : false; stateOut->m_btns = (uint16_t)payload[1] << 8 | (uint16_t)payload[2]; @@ -76,7 +54,7 @@ parseState(IDolphinSmashAdapterCallback::SDolphinControllerState* stateOut, uint stateOut->m_analogTriggers[0] = payload[7]; stateOut->m_analogTriggers[1] = payload[8]; - return static_cast(type|extra); + return type; } void CDolphinSmashAdapter::transferCycle() @@ -101,11 +79,12 @@ void CDolphinSmashAdapter::transferCycle() /* Parse controller states */ uint8_t* controller = &payload[1]; - bool rumbleMask[4] = {false}; + uint8_t rumbleMask = 0; for (int i=0 ; i<4 ; i++, controller += 9) { IDolphinSmashAdapterCallback::SDolphinControllerState state; - IDolphinSmashAdapterCallback::EDolphinControllerType type = parseState(&state, controller); + bool rumble = false; + IDolphinSmashAdapterCallback::EDolphinControllerType type = parseState(&state, controller, rumble); if (type && !(m_knownControllers & 1<controllerDisconnected(i, type); } - m_callback->controllerUpdate(i, type, state); - rumbleMask[i] = type & IDolphinSmashAdapterCallback::DOL_TYPE_RUMBLE; + if (m_knownControllers & 1<controllerUpdate(i, type, state); + rumbleMask |= rumble ? 1<(tok.openAndGetDevice()); smashAdapter->setCallback(&m_cb); + smashAdapter->startRumble(0); } void deviceDisconnected(CDeviceToken&, CDeviceBase* device) { From cf0eaad38856177e070a6d3517a25c97ccc30d76 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 25 Apr 2015 22:25:44 -1000 Subject: [PATCH 06/17] final messsage added --- include/inputdev/CDeviceBase.hpp | 1 + include/inputdev/CDolphinSmashAdapter.hpp | 1 + src/inputdev/CDolphinSmashAdapter.cpp | 13 +++++++++++-- src/inputdev/CHIDDeviceIOKit.cpp | 5 ++--- test/main.cpp | 6 ++++++ 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/include/inputdev/CDeviceBase.hpp b/include/inputdev/CDeviceBase.hpp index 1d6d3a4..f299429 100644 --- a/include/inputdev/CDeviceBase.hpp +++ b/include/inputdev/CDeviceBase.hpp @@ -25,6 +25,7 @@ public: bool sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length); size_t receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length); virtual void transferCycle() {}; + virtual void finalCycle() {}; /* High-Level API */ bool sendReport(const uint8_t* data, size_t length); diff --git a/include/inputdev/CDolphinSmashAdapter.hpp b/include/inputdev/CDolphinSmashAdapter.hpp index 0516135..f79428a 100644 --- a/include/inputdev/CDolphinSmashAdapter.hpp +++ b/include/inputdev/CDolphinSmashAdapter.hpp @@ -49,6 +49,7 @@ class CDolphinSmashAdapter final : public CDeviceBase bool m_didHandshake; void deviceDisconnected(); void transferCycle(); + void finalCycle(); public: CDolphinSmashAdapter(CDeviceToken* token); ~CDolphinSmashAdapter(); diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index acc4778..9f9c8b3 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -48,9 +48,9 @@ parseState(IDolphinSmashAdapterCallback::SDolphinControllerState* stateOut, uint stateOut->m_btns = (uint16_t)payload[1] << 8 | (uint16_t)payload[2]; stateOut->m_leftStick[0] = payload[3]; - stateOut->m_leftStick[1] = payload[4] ^ 0xFF; + stateOut->m_leftStick[1] = payload[4]; stateOut->m_rightStick[0] = payload[5]; - stateOut->m_rightStick[1] = payload[6] ^ 0xFF; + stateOut->m_rightStick[1] = payload[6]; stateOut->m_analogTriggers[0] = payload[7]; stateOut->m_analogTriggers[1] = payload[8]; @@ -118,6 +118,15 @@ void CDolphinSmashAdapter::transferCycle() } }; +void CDolphinSmashAdapter::finalCycle() +{ + if (m_didHandshake) + { + uint8_t rumbleMessage[5] = {0x11, 0, 0, 0, 0}; + sendInterruptTransfer(0, rumbleMessage, sizeof(rumbleMessage)); + } +}; + void CDolphinSmashAdapter::deviceDisconnected() { diff --git a/src/inputdev/CHIDDeviceIOKit.cpp b/src/inputdev/CHIDDeviceIOKit.cpp index 6a5001b..215d71e 100644 --- a/src/inputdev/CHIDDeviceIOKit.cpp +++ b/src/inputdev/CHIDDeviceIOKit.cpp @@ -146,10 +146,9 @@ class CHIDDeviceIOKit final : public IHIDDevice /* Start transfer loop */ while (device->m_runningTransferLoop) - { device->m_devImp.transferCycle(); - } - + device->m_devImp.finalCycle(); + /* Cleanup */ (*intf)->USBInterfaceClose(intf); (*intf)->Release(intf); diff --git a/test/main.cpp b/test/main.cpp index b58cf30..6261a8c 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,5 +1,8 @@ +#if __APPLE__ #include +#else +#endif #include #include @@ -55,7 +58,10 @@ int main(int argc, char** argv) { } +#if __APPLE__ CFRunLoopRun(); +#else +#endif delete ctx; return 0; From 8442c492d48c727e9d8ef77b6f23bcf21e12a74c Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Tue, 28 Apr 2015 21:56:16 -1000 Subject: [PATCH 07/17] initial udev implementation for linux-hosted hid devices --- {src => include}/inputdev/IHIDListener.hpp | 0 libBoo.pri | 16 +- libBoo.pro | 7 +- src/inputdev/CHIDDeviceUdev.cpp | 181 +++++++++++++++++++++ src/inputdev/CHIDListenerUdev.cpp | 167 ++++++++++++++++++- test/main.cpp | 2 + test/test.pri | 4 +- 7 files changed, 364 insertions(+), 13 deletions(-) rename {src => include}/inputdev/IHIDListener.hpp (100%) diff --git a/src/inputdev/IHIDListener.hpp b/include/inputdev/IHIDListener.hpp similarity index 100% rename from src/inputdev/IHIDListener.hpp rename to include/inputdev/IHIDListener.hpp diff --git a/libBoo.pri b/libBoo.pri index f108921..aef1e8b 100644 --- a/libBoo.pri +++ b/libBoo.pri @@ -1,5 +1,3 @@ -!contains(CONFIG,c++11):CONFIG += C++11 - HEADERS += \ $$PWD/include/boo.hpp \ $$PWD/include/IGraphicsContext.hpp \ @@ -18,8 +16,8 @@ HEADERS += \ $$PWD/include/inputdev/CDeviceToken.hpp \ $$PWD/include/inputdev/CDeviceBase.hpp \ $$PWD/include/inputdev/DeviceClasses.hpp \ - $$PWD/src/inputdev/IHIDDevice.hpp \ - $$PWD/src/inputdev/IHIDListener.hpp + $$PWD/include/inputdev/IHIDListener.hpp \ + $$PWD/src/inputdev/IHIDDevice.hpp unix:!macx:HEADERS += \ $$PWD/include/x11/CGLXContext.hpp @@ -41,12 +39,12 @@ SOURCES += \ $$PWD/src/inputdev/CDualshockPad.cpp \ $$PWD/src/inputdev/CGenericPad.cpp \ $$PWD/src/inputdev/CDeviceBase.cpp \ - $$PWD/src/inputdev/DeviceClasses.cpp + $$PWD/src/inputdev/DeviceClasses.cpp \ + $$PWD/src/inputdev/CHIDListenerUdev.cpp \ + $$PWD/src/inputdev/CHIDDeviceUdev.cpp unix:!macx:SOURCES += \ - $$PWD/src/x11/CGLXContext.cpp \ - $$PWD/src/inputdev/CHIDDeviceUdev.cpp \ - $$PWD/src/inputdev/CHIDListenerUdev.cpp + $$PWD/src/x11/CGLXContext.cpp macx:SOURCES += \ $$PWD/src/mac/CCGLContext.cpp \ @@ -63,3 +61,5 @@ win32:SOURCES += \ $$PWD/src/inputdev/CHIDListenerWin32.cpp INCLUDEPATH += $$PWD/include + +unix:!macx:LIBS += -ludev diff --git a/libBoo.pro b/libBoo.pro index 3c9e72e..339b233 100644 --- a/libBoo.pro +++ b/libBoo.pro @@ -1,8 +1,9 @@ CONFIG -= Qt -CONFIG += app c++11 +#QMAKE_CXXFLAGS -= -std=c++0x +QMAKE_CXXFLAGS += -std=c++11 -unix:!macx:CONFIG += link_pkgconfig -unix:!macx:PKGCONFIG += x11 +#unix:!macx:CONFIG += link_pkgconfig +#unix:!macx:PKGCONFIG += x11 include(libBoo.pri) include(test/test.pri) diff --git a/src/inputdev/CHIDDeviceUdev.cpp b/src/inputdev/CHIDDeviceUdev.cpp index 2888b5b..2bc4ac0 100644 --- a/src/inputdev/CHIDDeviceUdev.cpp +++ b/src/inputdev/CHIDDeviceUdev.cpp @@ -1 +1,182 @@ #include "IHIDDevice.hpp" +#include "inputdev/CDeviceToken.hpp" +#include "inputdev/CDeviceBase.hpp" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +udev* BooGetUdev(); + +#define MAX_REPORT_SIZE 65536 + +/* Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html + */ + +class CHIDDeviceUdev final : public IHIDDevice +{ + CDeviceToken& m_token; + CDeviceBase& m_devImp; + + int m_devFd = 0; + unsigned m_usbIntfInPipe = 0; + unsigned m_usbIntfOutPipe = 0; + bool m_runningTransferLoop = false; + + const std::string& m_devPath; + std::mutex m_initMutex; + std::condition_variable m_initCond; + std::thread* m_thread; + + bool _sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length) + { + if (m_devFd) + { + usbdevfs_bulktransfer xfer = + { + m_usbIntfOutPipe | USB_DIR_OUT, + (unsigned)length, + 0, + (void*)data + }; + int ret = ioctl(m_devFd, USBDEVFS_BULK, &xfer); + if (ret != length) + return false; + return true; + } + return false; + } + + size_t _receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length) + { + if (m_devFd) + { + usbdevfs_bulktransfer xfer = + { + m_usbIntfInPipe | USB_DIR_IN, + (unsigned)length, + 0, + data + }; + return ioctl(m_devFd, USBDEVFS_BULK, &xfer); + } + return 0; + } + + static void _threadProcLL(CHIDDeviceUdev* device) + { + unsigned i; + std::unique_lock lk(device->m_initMutex); + udev_device* hidDev = udev_device_new_from_syspath(BooGetUdev(), device->m_devPath.c_str()); + + /* Get the HID element's parent (USB interrupt transfer-interface) */ + udev_device* usbDev = udev_device_get_parent_with_subsystem_devtype(hidDev, "usb", "usb_device"); + const char* dp = udev_device_get_devnode(usbDev); + device->m_devFd = open(dp, O_RDONLY); + usb_device_descriptor devDesc = {0}; + read(device->m_devFd, &devDesc, 1); + read(device->m_devFd, &devDesc.bDescriptorType, devDesc.bLength-1); + if (devDesc.bNumConfigurations) + { + usb_config_descriptor confDesc = {0}; + read(device->m_devFd, &confDesc, 1); + read(device->m_devFd, &confDesc.bDescriptorType, confDesc.bLength-1); + if (confDesc.bNumInterfaces) + { + usb_interface_descriptor intfDesc = {0}; + read(device->m_devFd, &intfDesc, 1); + read(device->m_devFd, &intfDesc.bDescriptorType, intfDesc.bLength-1); + for (i=0 ; im_devFd, &endpDesc, 1); + read(device->m_devFd, &endpDesc.bDescriptorType, endpDesc.bLength-1); + if ((endpDesc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + { + if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + device->m_usbIntfInPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + else if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) + device->m_usbIntfOutPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + } + } + } + } + + /* Request that kernel disconnects existing driver */ + usbdevfs_ioctl disconnectReq = { + 0, + USBDEVFS_DISCONNECT, + NULL + }; + ioctl(device->m_devFd, USBDEVFS_IOCTL, &disconnectReq); + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); + + /* Cleanup */ + close(device->m_devFd); + device->m_devFd = NULL; + udev_device_unref(hidDev); + + } + + static void _threadProcHL(CHIDDeviceUdev* device) + { + + } + + void _deviceDisconnected() + { + m_runningTransferLoop = false; + } + + bool _sendReport(const uint8_t* data, size_t length) + { + return false; + } + +public: + + CHIDDeviceUdev(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel) + : m_token(token), + m_devImp(devImp), + m_devPath(token.getDevicePath()) + { + devImp.m_hidDev = this; + std::unique_lock lk(m_initMutex); + if (lowLevel) + m_thread = new std::thread(_threadProcLL, this); + else + m_thread = new std::thread(_threadProcHL, this); + m_initCond.wait(lk); + } + + ~CHIDDeviceUdev() + { + m_runningTransferLoop = false; + m_thread->detach(); + delete m_thread; + } + + +}; + +IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel) +{ + return new CHIDDeviceUdev(token, devImp, lowLevel); +} diff --git a/src/inputdev/CHIDListenerUdev.cpp b/src/inputdev/CHIDListenerUdev.cpp index 90f36b2..2fd3894 100644 --- a/src/inputdev/CHIDListenerUdev.cpp +++ b/src/inputdev/CHIDListenerUdev.cpp @@ -1 +1,166 @@ -#include "IHIDListener.hpp" +#include "inputdev/IHIDListener.hpp" +#include "inputdev/CDeviceFinder.hpp" +#include +#include +#include + +static udev* UDEV_INST = NULL; +udev* BooGetUdev() +{ + if (!UDEV_INST) + UDEV_INST = udev_new(); + return UDEV_INST; +} + +class CHIDListenerUdev final : public IHIDListener +{ + CDeviceFinder& m_finder; + + udev_monitor* m_udevMon; + std::thread* m_udevThread; + bool m_udevRunning; + bool m_scanningEnabled; + + static void deviceConnected(CHIDListenerUdev* listener, + udev_device* device) + { + if (!listener->m_scanningEnabled) + return; + + const char* devPath = udev_device_get_syspath(device); + if (listener->m_finder._hasToken(devPath)) + return; + + udev_device* devCast = udev_device_get_parent_with_subsystem_devtype(device, "bluetooth", "bluetooth_device"); + if (!devCast) + devCast = udev_device_get_parent_with_subsystem_devtype(device, "usb", "usb_device"); + if (!devCast) + return; + + int vid = 0, pid = 0; + udev_list_entry* attrs = udev_device_get_properties_list_entry(devCast); + + udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID"); + if (vide) + vid = strtol(udev_list_entry_get_value(vide), NULL, 16); + + udev_list_entry* pide = udev_list_entry_get_by_name(attrs, "ID_MODEL_ID"); + if (pide) + pid = strtol(udev_list_entry_get_value(pide), NULL, 16); + + const char* manuf = NULL; + udev_list_entry* manufe = udev_list_entry_get_by_name(attrs, "ID_VENDOR"); + if (manufe) + manuf = udev_list_entry_get_value(manufe); + + const char* product = NULL; + udev_list_entry* producte = udev_list_entry_get_by_name(attrs, "ID_MODEL"); + if (producte) + product = udev_list_entry_get_value(producte); + + listener->m_finder._insertToken(CDeviceToken(vid, pid, manuf, product, devPath)); + } + + static void deviceDisconnected(CHIDListenerUdev* listener, + udev_device* device) + { + const char* devPath = udev_device_get_syspath(device); + listener->m_finder._removeToken(devPath); + } + + static void _udevProc(CHIDListenerUdev* listener) + { + udev_monitor_enable_receiving(listener->m_udevMon); + int fd = udev_monitor_get_fd(listener->m_udevMon); + while (listener->m_udevRunning) + { + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + select(fd+1, &fds, NULL, NULL, NULL); + udev_device* dev = udev_monitor_receive_device(listener->m_udevMon); + if (dev) + { + const char* action = udev_device_get_action(dev); + if (!strcmp(action, "add")) + { + deviceConnected(listener, dev); + } + else if (!strcmp(action, "remove")) + { + deviceDisconnected(listener, dev); + } + udev_device_unref(dev); + } + } + } + +public: + CHIDListenerUdev(CDeviceFinder& finder) + : m_finder(finder) + { + + /* Setup hiddev and hotplug events */ + m_udevMon = udev_monitor_new_from_netlink(BooGetUdev(), "udev"); + if (!m_udevMon) + throw std::runtime_error("unable to init udev_monitor"); + udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "hid", NULL); + udev_monitor_filter_update(m_udevMon); + + /* Initial HID Device Add */ + m_scanningEnabled = true; + scanNow(); + m_scanningEnabled = false; + + /* Start hotplug thread */ + m_udevRunning = true; + m_udevThread = new std::thread(_udevProc, this); + + } + + ~CHIDListenerUdev() + { + m_udevRunning = false; + m_udevThread->join(); + udev_monitor_unref(m_udevMon); + } + + /* Automatic device scanning */ + bool startScanning() + { + m_scanningEnabled = true; + return true; + } + bool stopScanning() + { + m_scanningEnabled = false; + return true; + } + + /* Manual device scanning */ + bool scanNow() + { + udev_enumerate* uenum = udev_enumerate_new(BooGetUdev()); + udev_enumerate_add_match_subsystem(uenum, "hid"); + udev_enumerate_scan_devices(uenum); + udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum); + udev_list_entry* uenumItem; + udev_list_entry_foreach(uenumItem, uenumList) + { + const char* devPath = udev_list_entry_get_name(uenumItem); + udev_device* dev = udev_device_new_from_syspath(UDEV_INST, devPath); + if (dev) + deviceConnected(this, dev); + udev_device_unref(dev); + } + udev_enumerate_unref(uenum); + return true; + } + +}; + +IHIDListener* IHIDListenerNew(CDeviceFinder& finder) +{ + return new CHIDListenerUdev(finder); +} + diff --git a/test/main.cpp b/test/main.cpp index 6261a8c..7dfb51e 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -4,6 +4,7 @@ #else #endif #include +#include #include class CDolphinSmashAdapterCallback : public IDolphinSmashAdapterCallback @@ -61,6 +62,7 @@ int main(int argc, char** argv) #if __APPLE__ CFRunLoopRun(); #else + while (true) {sleep(1);} #endif delete ctx; diff --git a/test/test.pri b/test/test.pri index bf76c86..a0eae95 100644 --- a/test/test.pri +++ b/test/test.pri @@ -1,2 +1,4 @@ SOURCES += \ - $$PWD/main.cpp \ No newline at end of file + $$PWD/main.cpp + +CONFIG += c++11 \ No newline at end of file From d4b1211c24382111673baa71efa6f99d17908423 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Wed, 29 Apr 2015 00:24:39 -1000 Subject: [PATCH 08/17] added boo namespace --- include/CInputDeferredRelay.hpp | 5 ++ include/CInputRelay.hpp | 5 ++ include/CSurface.hpp | 5 ++ include/IGraphicsContext.hpp | 5 ++ include/IInputWaiter.hpp | 5 ++ include/IRetraceWaiter.hpp | 7 ++- include/ISurface.hpp | 7 ++- include/boo.hpp | 4 +- include/inputdev/CCafeProPad.hpp | 4 ++ include/inputdev/CDeviceBase.hpp | 5 ++ include/inputdev/CDeviceFinder.hpp | 5 ++ include/inputdev/CDeviceToken.hpp | 5 ++ include/inputdev/CDolphinSmashAdapter.hpp | 64 +++++++++++++---------- include/inputdev/CDualshockPad.hpp | 4 ++ include/inputdev/CGenericPad.hpp | 4 ++ include/inputdev/CRevolutionPad.hpp | 4 ++ include/inputdev/DeviceClasses.hpp | 5 ++ include/inputdev/IHIDListener.hpp | 6 +++ include/mac/CCGLContext.hpp | 5 ++ include/win/CWGLContext.hpp | 3 ++ include/x11/CGLXContext.hpp | 5 +- src/CSurface.cpp | 5 ++ src/inputdev/CDeviceBase.cpp | 5 +- src/inputdev/CDolphinSmashAdapter.cpp | 34 ++++++------ src/inputdev/CHIDDeviceUdev.cpp | 8 ++- src/inputdev/CHIDListenerIOKit.cpp | 3 +- src/inputdev/CHIDListenerUdev.cpp | 10 +++- src/inputdev/DeviceClasses.cpp | 5 ++ src/inputdev/IHIDDevice.hpp | 8 ++- src/x11/CGLXContext.cpp | 5 ++ test/main.cpp | 9 +++- 31 files changed, 197 insertions(+), 57 deletions(-) diff --git a/include/CInputDeferredRelay.hpp b/include/CInputDeferredRelay.hpp index d4a0982..524261c 100644 --- a/include/CInputDeferredRelay.hpp +++ b/include/CInputDeferredRelay.hpp @@ -3,4 +3,9 @@ #include "IInputWaiter.hpp" +namespace boo +{ + +} + #endif // CINPUTDEFERREDRELAY_HPP diff --git a/include/CInputRelay.hpp b/include/CInputRelay.hpp index eb126ac..03eacbe 100644 --- a/include/CInputRelay.hpp +++ b/include/CInputRelay.hpp @@ -3,9 +3,14 @@ #include "IInputWaiter.hpp" +namespace boo +{ + class CInputRelay : IInputWaiter { }; +} + #endif // CINPUTRELAY_HPP diff --git a/include/CSurface.hpp b/include/CSurface.hpp index 7360716..a1916a8 100644 --- a/include/CSurface.hpp +++ b/include/CSurface.hpp @@ -3,7 +3,12 @@ #include "ISurface.hpp" +namespace boo +{ + ISurface* CSurfaceNewWindow(); ISurface* CSurfaceNewQWidget(); +} + #endif // CSURFACE_HPP diff --git a/include/IGraphicsContext.hpp b/include/IGraphicsContext.hpp index 7ca8d60..6764d3c 100644 --- a/include/IGraphicsContext.hpp +++ b/include/IGraphicsContext.hpp @@ -3,6 +3,9 @@ #include +namespace boo +{ + class IGraphicsContext { public: @@ -19,4 +22,6 @@ public: virtual int blueDepth() const=0; }; +} + #endif // IGRAPHICSCONTEXT_HPP diff --git a/include/IInputWaiter.hpp b/include/IInputWaiter.hpp index e7f0048..552a2d8 100644 --- a/include/IInputWaiter.hpp +++ b/include/IInputWaiter.hpp @@ -1,9 +1,14 @@ #ifndef IINPUTWAITER_HPP #define IINPUTWAITER_HPP +namespace boo +{ + class IInputWaiter { }; +} + #endif // IINPUTWAITER_HPP diff --git a/include/IRetraceWaiter.hpp b/include/IRetraceWaiter.hpp index e1cf336..aacd34f 100644 --- a/include/IRetraceWaiter.hpp +++ b/include/IRetraceWaiter.hpp @@ -1,9 +1,14 @@ #ifndef IRETRACEWAITER_HPP #define IRETRACEWAITER_HPP +namespace boo +{ + class IRetraceWaiter { }; -#endif // IRETRACEWAITER_HPP \ No newline at end of file +} + +#endif // IRETRACEWAITER_HPP diff --git a/include/ISurface.hpp b/include/ISurface.hpp index cdcdc5e..6f2f13d 100644 --- a/include/ISurface.hpp +++ b/include/ISurface.hpp @@ -1,10 +1,15 @@ #ifndef ISURFACE_HPP #define ISURFACE_HPP +namespace boo +{ + class ISurface { public: }; -#endif // CSURFACE_HPP \ No newline at end of file +} + +#endif // CSURFACE_HPP diff --git a/include/boo.hpp b/include/boo.hpp index 7821f5c..f295a9c 100644 --- a/include/boo.hpp +++ b/include/boo.hpp @@ -6,12 +6,12 @@ #elif defined(__APPLE__) #include "mac/CCGLContext.hpp" -typedef CCGLContext CGraphicsContext; +namespace boo {typedef CCGLContext CGraphicsContext;} #elif defined(__GNUC__) || defined(__clang__) #include "x11/CGLXContext.hpp" -typedef CGLXContext CGraphicsContext; +namespace boo {typedef boo::CGLXContext CGraphicsContext;} #endif diff --git a/include/inputdev/CCafeProPad.hpp b/include/inputdev/CCafeProPad.hpp index b82a55b..6249cc1 100644 --- a/include/inputdev/CCafeProPad.hpp +++ b/include/inputdev/CCafeProPad.hpp @@ -1,5 +1,9 @@ #ifndef CCAFEPROPAD_HPP #define CCAFEPROPAD_HPP +namespace boo +{ + +} #endif // CCAFEPROPAD_HPP diff --git a/include/inputdev/CDeviceBase.hpp b/include/inputdev/CDeviceBase.hpp index f299429..8e9d5c1 100644 --- a/include/inputdev/CDeviceBase.hpp +++ b/include/inputdev/CDeviceBase.hpp @@ -4,6 +4,9 @@ #include #include +namespace boo +{ + class CDeviceBase { friend class CDeviceToken; @@ -33,4 +36,6 @@ public: }; +} + #endif // CDEVICEBASE diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index 1704541..bb93fda 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -8,6 +8,9 @@ #include "IHIDListener.hpp" #include "DeviceClasses.hpp" +namespace boo +{ + static class CDeviceFinder* skDevFinder = NULL; class CDeviceFinder @@ -132,4 +135,6 @@ public: }; +} + #endif // CDEVICEFINDER_HPP diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index 68bc89b..814b484 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -5,6 +5,9 @@ #include "CDeviceBase.hpp" #include "DeviceClasses.hpp" +namespace boo +{ + class CDeviceToken { unsigned m_vendorId; @@ -55,4 +58,6 @@ public: {return m_devPath < rhs.m_devPath;} }; +} + #endif // CDEVICETOKEN diff --git a/include/inputdev/CDolphinSmashAdapter.hpp b/include/inputdev/CDolphinSmashAdapter.hpp index f79428a..cf0f780 100644 --- a/include/inputdev/CDolphinSmashAdapter.hpp +++ b/include/inputdev/CDolphinSmashAdapter.hpp @@ -4,36 +4,42 @@ #include #include "CDeviceBase.hpp" +namespace boo +{ + +enum EDolphinControllerType +{ + DOL_TYPE_NONE = 0, + DOL_TYPE_NORMAL = 0x10, + DOL_TYPE_WAVEBIRD = 0x20, +}; + +enum EDolphinControllerButtons +{ + DOL_START = 1<<0, + DOL_Z = 1<<1, + DOL_L = 1<<2, + DOL_R = 1<<3, + DOL_A = 1<<8, + DOL_B = 1<<9, + DOL_X = 1<<10, + DOL_Y = 1<<11, + DOL_LEFT = 1<<12, + DOL_RIGHT = 1<<13, + DOL_DOWN = 1<<14, + DOL_UP = 1<<15 +}; + +struct SDolphinControllerState +{ + uint8_t m_leftStick[2]; + uint8_t m_rightStick[2]; + uint8_t m_analogTriggers[2]; + uint16_t m_btns; +}; + struct IDolphinSmashAdapterCallback { - enum EDolphinControllerType - { - DOL_TYPE_NONE = 0, - DOL_TYPE_NORMAL = 0x10, - DOL_TYPE_WAVEBIRD = 0x20, - }; - enum EDolphinControllerButtons - { - DOL_START = 1<<0, - DOL_Z = 1<<1, - DOL_L = 1<<2, - DOL_R = 1<<3, - DOL_A = 1<<8, - DOL_B = 1<<9, - DOL_X = 1<<10, - DOL_Y = 1<<11, - DOL_LEFT = 1<<12, - DOL_RIGHT = 1<<13, - DOL_DOWN = 1<<14, - DOL_UP = 1<<15 - }; - struct SDolphinControllerState - { - uint8_t m_leftStick[2]; - uint8_t m_rightStick[2]; - uint8_t m_analogTriggers[2]; - uint16_t m_btns; - }; virtual void controllerConnected(unsigned idx, EDolphinControllerType type) {} virtual void controllerDisconnected(unsigned idx, EDolphinControllerType type) {} virtual void controllerUpdate(unsigned idx, EDolphinControllerType type, @@ -60,4 +66,6 @@ public: inline void stopRumble(unsigned idx) {if (idx >= 4) return; m_rumbleRequest &= ~(1< #include #include "CDeviceToken.hpp" + +namespace boo +{ + typedef std::unordered_map TDeviceTokens; typedef std::pair TInsertedDeviceToken; class CDeviceFinder; @@ -25,4 +29,6 @@ public: /* Platform-specific constructor */ IHIDListener* IHIDListenerNew(CDeviceFinder& finder); +} + #endif // IHIDLISTENER_HPP diff --git a/include/mac/CCGLContext.hpp b/include/mac/CCGLContext.hpp index 26244ad..5a66f40 100644 --- a/include/mac/CCGLContext.hpp +++ b/include/mac/CCGLContext.hpp @@ -5,6 +5,9 @@ #include "IGraphicsContext.hpp" #include +namespace boo +{ + class CCGLContext final : public IGraphicsContext { public: @@ -25,5 +28,7 @@ private: int m_majVersion; }; +} + #endif // __APPLE__ #endif // CCGLCONTEXT_HPP diff --git a/include/win/CWGLContext.hpp b/include/win/CWGLContext.hpp index 77bfac5..3c1e57d 100644 --- a/include/win/CWGLContext.hpp +++ b/include/win/CWGLContext.hpp @@ -3,6 +3,9 @@ #ifdef _WIN32 +namespace boo +{ +} #endif // _WIN32 #endif // CWGLCONTEXT_HPP diff --git a/include/x11/CGLXContext.hpp b/include/x11/CGLXContext.hpp index 421e093..809101a 100644 --- a/include/x11/CGLXContext.hpp +++ b/include/x11/CGLXContext.hpp @@ -3,8 +3,10 @@ #if !defined(__APPLE__) && (defined(__linux__) || defined(BSD)) #include +#include "IGraphicsContext.hpp" -#include +namespace boo +{ class CGLXContext final : public IGraphicsContext { @@ -28,6 +30,7 @@ private: Display* m_display; }; +} #endif // !defined(__APPLE__) && (defined(__linux__) || defined(BSD)) #endif // CGLXCONTEXT_HPP diff --git a/src/CSurface.cpp b/src/CSurface.cpp index c522c4d..4605050 100644 --- a/src/CSurface.cpp +++ b/src/CSurface.cpp @@ -1,5 +1,8 @@ #include "CSurface.hpp" +namespace boo +{ + ISurface* CSurfaceNewWindow() { @@ -9,3 +12,5 @@ ISurface* CSurfaceNewQWidget() { } + +} diff --git a/src/inputdev/CDeviceBase.cpp b/src/inputdev/CDeviceBase.cpp index 2d1e805..ea5a199 100644 --- a/src/inputdev/CDeviceBase.cpp +++ b/src/inputdev/CDeviceBase.cpp @@ -2,6 +2,9 @@ #include "inputdev/CDeviceToken.hpp" #include "IHIDDevice.hpp" +namespace boo +{ + CDeviceBase::CDeviceBase(CDeviceToken* token) : m_token(token), m_hidDev(NULL) { @@ -51,4 +54,4 @@ bool CDeviceBase::sendReport(const uint8_t* data, size_t length) return false; } - +} diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index 9f9c8b3..5c9962f 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -2,7 +2,11 @@ #include #include -/* Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c +namespace boo +{ + +/* + * Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c */ CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token) @@ -21,27 +25,25 @@ CDolphinSmashAdapter::~CDolphinSmashAdapter() static const uint8_t HANDSHAKE_PAYLOAD[] = {0x13}; -static inline IDolphinSmashAdapterCallback::EDolphinControllerType -parseType(unsigned char status) +static inline EDolphinControllerType parseType(unsigned char status) { - unsigned char type = status & (IDolphinSmashAdapterCallback::DOL_TYPE_NORMAL | - IDolphinSmashAdapterCallback::DOL_TYPE_WAVEBIRD); + unsigned char type = status & (DOL_TYPE_NORMAL | DOL_TYPE_WAVEBIRD); switch (type) { - case IDolphinSmashAdapterCallback::DOL_TYPE_NORMAL: - case IDolphinSmashAdapterCallback::DOL_TYPE_WAVEBIRD: - return (IDolphinSmashAdapterCallback::EDolphinControllerType)type; + case DOL_TYPE_NORMAL: + case DOL_TYPE_WAVEBIRD: + return (EDolphinControllerType)type; default: - return IDolphinSmashAdapterCallback::DOL_TYPE_NONE; + return DOL_TYPE_NONE; } } -static inline IDolphinSmashAdapterCallback::EDolphinControllerType -parseState(IDolphinSmashAdapterCallback::SDolphinControllerState* stateOut, uint8_t* payload, bool& rumble) +static inline EDolphinControllerType +parseState(SDolphinControllerState* stateOut, uint8_t* payload, bool& rumble) { - memset(stateOut, 0, sizeof(IDolphinSmashAdapterCallback::SDolphinControllerState)); + memset(stateOut, 0, sizeof(SDolphinControllerState)); unsigned char status = payload[0]; - IDolphinSmashAdapterCallback::EDolphinControllerType type = parseType(status); + EDolphinControllerType type = parseType(status); rumble = ((status & 0x04) != 0) ? true : false; @@ -82,9 +84,9 @@ void CDolphinSmashAdapter::transferCycle() uint8_t rumbleMask = 0; for (int i=0 ; i<4 ; i++, controller += 9) { - IDolphinSmashAdapterCallback::SDolphinControllerState state; + SDolphinControllerState state; bool rumble = false; - IDolphinSmashAdapterCallback::EDolphinControllerType type = parseState(&state, controller, rumble); + EDolphinControllerType type = parseState(&state, controller, rumble); if (type && !(m_knownControllers & 1< #include +namespace boo +{ + udev* BooGetUdev(); #define MAX_REPORT_SIZE 65536 -/* Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html +/* + * Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html */ class CHIDDeviceUdev final : public IHIDDevice @@ -180,3 +184,5 @@ IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLeve { return new CHIDDeviceUdev(token, devImp, lowLevel); } + +} diff --git a/src/inputdev/CHIDListenerIOKit.cpp b/src/inputdev/CHIDListenerIOKit.cpp index 1a413ad..8826113 100644 --- a/src/inputdev/CHIDListenerIOKit.cpp +++ b/src/inputdev/CHIDListenerIOKit.cpp @@ -6,7 +6,8 @@ #include #include -/* 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) diff --git a/src/inputdev/CHIDListenerUdev.cpp b/src/inputdev/CHIDListenerUdev.cpp index 2fd3894..2601c17 100644 --- a/src/inputdev/CHIDListenerUdev.cpp +++ b/src/inputdev/CHIDListenerUdev.cpp @@ -4,6 +4,9 @@ #include #include +namespace boo +{ + static udev* UDEV_INST = NULL; udev* BooGetUdev() { @@ -31,7 +34,8 @@ class CHIDListenerUdev final : public IHIDListener if (listener->m_finder._hasToken(devPath)) return; - udev_device* devCast = udev_device_get_parent_with_subsystem_devtype(device, "bluetooth", "bluetooth_device"); + udev_device* devCast = + udev_device_get_parent_with_subsystem_devtype(device, "bluetooth", "bluetooth_device"); if (!devCast) devCast = udev_device_get_parent_with_subsystem_devtype(device, "usb", "usb_device"); if (!devCast) @@ -100,7 +104,7 @@ public: : m_finder(finder) { - /* Setup hiddev and hotplug events */ + /* Setup hotplug events */ m_udevMon = udev_monitor_new_from_netlink(BooGetUdev(), "udev"); if (!m_udevMon) throw std::runtime_error("unable to init udev_monitor"); @@ -122,6 +126,7 @@ public: { m_udevRunning = false; m_udevThread->join(); + delete m_udevThread; udev_monitor_unref(m_udevMon); } @@ -164,3 +169,4 @@ IHIDListener* IHIDListenerNew(CDeviceFinder& finder) return new CHIDListenerUdev(finder); } +} diff --git a/src/inputdev/DeviceClasses.cpp b/src/inputdev/DeviceClasses.cpp index e38c037..3c70a57 100644 --- a/src/inputdev/DeviceClasses.cpp +++ b/src/inputdev/DeviceClasses.cpp @@ -2,6 +2,9 @@ #include "inputdev/CDeviceToken.hpp" #include "IHIDDevice.hpp" +namespace boo +{ + bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask) { if (mask & DEV_DOL_SMASH_ADAPTER && @@ -36,3 +39,5 @@ CDeviceBase* BooDeviceNew(CDeviceToken& token) return retval; } + +} diff --git a/src/inputdev/IHIDDevice.hpp b/src/inputdev/IHIDDevice.hpp index 474d755..0eec0f1 100644 --- a/src/inputdev/IHIDDevice.hpp +++ b/src/inputdev/IHIDDevice.hpp @@ -2,11 +2,13 @@ #define IHIDDEVICE_HPP #include "inputdev/CDeviceToken.hpp" -class CDeviceBase; + +namespace boo +{ class IHIDDevice { - friend CDeviceBase; + friend class CDeviceBase; virtual void _deviceDisconnected()=0; virtual bool _sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)=0; virtual size_t _receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)=0; @@ -15,4 +17,6 @@ public: inline virtual ~IHIDDevice() {}; }; +} + #endif // IHIDDEVICE_HPP diff --git a/src/x11/CGLXContext.cpp b/src/x11/CGLXContext.cpp index 6c42417..f1e42e2 100644 --- a/src/x11/CGLXContext.cpp +++ b/src/x11/CGLXContext.cpp @@ -2,6 +2,9 @@ #include "x11/CGLXContext.hpp" #include +namespace boo +{ + CGLXContext::CGLXContext() : m_majVersion(3), m_minVersion(3), @@ -55,4 +58,6 @@ int CGLXContext::blueDepth() const return -1; } +} + #endif // !defined(__APPLE__) && (defined(__linux__) || defined(BSD)) diff --git a/test/main.cpp b/test/main.cpp index 7dfb51e..fdce46d 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -7,6 +7,9 @@ #include #include +namespace boo +{ + class CDolphinSmashAdapterCallback : public IDolphinSmashAdapterCallback { void controllerConnected(unsigned idx, EDolphinControllerType type) @@ -48,12 +51,14 @@ public: } }; +} + int main(int argc, char** argv) { - CTestDeviceFinder finder; + boo::CTestDeviceFinder finder; finder.startScanning(); - IGraphicsContext* ctx = new CGraphicsContext; + boo::IGraphicsContext* ctx = new boo::CGraphicsContext; if (ctx->create()) { From 4b8651b844bff3086ab603607c921fe6248c56f8 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Wed, 29 Apr 2015 21:01:55 -1000 Subject: [PATCH 09/17] much better input-device class registration --- InputDeviceClasses.cpp | 13 ++++++ include/boo.hpp | 3 +- include/inputdev/CDeviceBase.hpp | 9 ++-- include/inputdev/CDeviceFinder.hpp | 26 ++++++++--- include/inputdev/CDeviceToken.hpp | 4 +- include/inputdev/CDolphinSmashAdapter.hpp | 1 + include/inputdev/DeviceClasses.hpp | 32 ------------- include/inputdev/IHIDListener.hpp | 2 +- include/inputdev/SDeviceSignature.hpp | 37 +++++++++++++++ libBoo.pri | 9 ++-- libBoo.pro | 4 +- src/inputdev/CDolphinSmashAdapter.cpp | 9 +++- src/inputdev/CHIDDeviceIOKit.cpp | 5 ++ src/inputdev/CHIDDeviceUdev.cpp | 24 +++++++--- src/inputdev/CHIDListenerIOKit.cpp | 4 ++ src/inputdev/CHIDListenerUdev.cpp | 6 +-- src/inputdev/DeviceClasses.cpp | 43 ----------------- src/inputdev/SDeviceSignature.cpp | 57 +++++++++++++++++++++++ test/main.cpp | 2 +- 19 files changed, 184 insertions(+), 106 deletions(-) create mode 100644 InputDeviceClasses.cpp delete mode 100644 include/inputdev/DeviceClasses.hpp create mode 100644 include/inputdev/SDeviceSignature.hpp delete mode 100644 src/inputdev/DeviceClasses.cpp create mode 100644 src/inputdev/SDeviceSignature.cpp diff --git a/InputDeviceClasses.cpp b/InputDeviceClasses.cpp new file mode 100644 index 0000000..0156671 --- /dev/null +++ b/InputDeviceClasses.cpp @@ -0,0 +1,13 @@ +#include "inputdev/SDeviceSignature.hpp" +#include "inputdev/CDolphinSmashAdapter.hpp" + +namespace boo +{ + +const SDeviceSignature BOO_DEVICE_SIGS[] = +{ + DEVICE_SIG(CDolphinSmashAdapter, 0x57e, 0x337, true), + DEVICE_SIG_SENTINEL() +}; + +} diff --git a/include/boo.hpp b/include/boo.hpp index f295a9c..74883e6 100644 --- a/include/boo.hpp +++ b/include/boo.hpp @@ -11,11 +11,12 @@ namespace boo {typedef CCGLContext CGraphicsContext;} #elif defined(__GNUC__) || defined(__clang__) #include "x11/CGLXContext.hpp" -namespace boo {typedef boo::CGLXContext CGraphicsContext;} +namespace boo {typedef CGLXContext CGraphicsContext;} #endif #include "IGraphicsContext.hpp" #include "inputdev/CDeviceFinder.hpp" +#include "inputdev/CDolphinSmashAdapter.hpp" #endif // BOO_HPP diff --git a/include/inputdev/CDeviceBase.hpp b/include/inputdev/CDeviceBase.hpp index 8e9d5c1..1d4df7f 100644 --- a/include/inputdev/CDeviceBase.hpp +++ b/include/inputdev/CDeviceBase.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace boo { @@ -23,16 +24,18 @@ public: virtual ~CDeviceBase(); void closeDevice(); virtual void deviceDisconnected()=0; + virtual void deviceError(const char* error) {fprintf(stderr, "%s\n", error);} /* Low-Level API */ bool sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length); size_t receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length); - virtual void transferCycle() {}; - virtual void finalCycle() {}; + virtual void initialCycle() {} + virtual void transferCycle() {} + virtual void finalCycle() {} /* High-Level API */ bool sendReport(const uint8_t* data, size_t length); - virtual size_t receiveReport(uint8_t* data, size_t length) {}; + virtual size_t receiveReport(uint8_t* data, size_t length) {return 0;} }; diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index bb93fda..c6cb3de 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -6,7 +6,8 @@ #include #include "CDeviceToken.hpp" #include "IHIDListener.hpp" -#include "DeviceClasses.hpp" +#include "SDeviceSignature.hpp" +#include namespace boo { @@ -24,7 +25,7 @@ public: private: /* Types this finder is interested in (immutable) */ - EDeviceMask m_types; + SDeviceSignature::TDeviceSignatureSet m_types; /* Platform-specific USB event registration * (for auto-scanning, NULL if not registered) */ @@ -45,9 +46,10 @@ private: } inline void _insertToken(CDeviceToken&& token) { - if (BooDeviceMatchToken(token, m_types)) { + if (SDeviceSignature::DeviceMatchToken(token, m_types)) { m_tokensLock.lock(); - TInsertedDeviceToken inseredTok = m_tokens.insert(std::make_pair(token.getDevicePath(), std::move(token))); + TInsertedDeviceToken inseredTok = + m_tokens.insert(std::make_pair(token.getDevicePath(), std::move(token))); m_tokensLock.unlock(); deviceConnected(inseredTok.first->second); } @@ -81,12 +83,22 @@ public: }; /* Application must specify its interested device-types */ - CDeviceFinder(EDeviceMask types) - : m_types(types), m_listener(NULL) + CDeviceFinder(std::vector types) + : m_listener(NULL) { if (skDevFinder) throw std::runtime_error("only one instance of CDeviceFinder may be constructed"); skDevFinder = this; + for (const char* typeName : types) + { + const SDeviceSignature* sigIter = BOO_DEVICE_SIGS; + while (sigIter->m_name) + { + if (!strcmp(sigIter->m_name, typeName)) + m_types.push_back(sigIter); + ++sigIter; + } + } } ~CDeviceFinder() { @@ -97,7 +109,7 @@ public: } /* Get interested device-type mask */ - inline EDeviceMask getTypes() const {return m_types;} + inline const SDeviceSignature::TDeviceSignatureSet& getTypes() const {return m_types;} /* Iterable set of tokens */ inline CDeviceTokensHandle getTokens() {return CDeviceTokensHandle(*this);} diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index 814b484..cab3367 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -3,7 +3,7 @@ #include #include "CDeviceBase.hpp" -#include "DeviceClasses.hpp" +#include "SDeviceSignature.hpp" namespace boo { @@ -48,7 +48,7 @@ public: inline CDeviceBase* openAndGetDevice() { if (!m_connectedDev) - m_connectedDev = BooDeviceNew(*this); + m_connectedDev = SDeviceSignature::DeviceNew(*this); return m_connectedDev; } diff --git a/include/inputdev/CDolphinSmashAdapter.hpp b/include/inputdev/CDolphinSmashAdapter.hpp index cf0f780..5954752 100644 --- a/include/inputdev/CDolphinSmashAdapter.hpp +++ b/include/inputdev/CDolphinSmashAdapter.hpp @@ -54,6 +54,7 @@ class CDolphinSmashAdapter final : public CDeviceBase uint8_t m_rumbleState; bool m_didHandshake; void deviceDisconnected(); + void initialCycle(); void transferCycle(); void finalCycle(); public: diff --git a/include/inputdev/DeviceClasses.hpp b/include/inputdev/DeviceClasses.hpp deleted file mode 100644 index 6bafdd6..0000000 --- a/include/inputdev/DeviceClasses.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef CDEVICECLASSES_HPP -#define CDEVICECLASSES_HPP - -#include "CDolphinSmashAdapter.hpp" -#include "CRevolutionPad.hpp" -#include "CCafeProPad.hpp" -#include "CDualshockPad.hpp" -#include "CGenericPad.hpp" - -namespace boo -{ - -#define VID_NINTENDO 0x57e -#define PID_SMASH_ADAPTER 0x337 - -enum EDeviceMask -{ - DEV_NONE = 0, - DEV_DOL_SMASH_ADAPTER = 1<<0, - DEV_RVL_PAD = 1<<1, - DEV_CAFE_PRO_PAD = 1<<2, - DEV_DUALSHOCK_PAD = 1<<3, - DEV_GENERIC_PAD = 1<<4, - DEV_ALL = 0xff -}; - -bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask); -CDeviceBase* BooDeviceNew(CDeviceToken& token); - -} - -#endif // CDEVICECLASSES_HPP diff --git a/include/inputdev/IHIDListener.hpp b/include/inputdev/IHIDListener.hpp index 0139d5e..edf1549 100644 --- a/include/inputdev/IHIDListener.hpp +++ b/include/inputdev/IHIDListener.hpp @@ -15,7 +15,7 @@ class CDeviceFinder; class IHIDListener { public: - virtual ~IHIDListener() {}; + virtual ~IHIDListener() {} /* Automatic device scanning */ virtual bool startScanning()=0; diff --git a/include/inputdev/SDeviceSignature.hpp b/include/inputdev/SDeviceSignature.hpp new file mode 100644 index 0000000..cef1681 --- /dev/null +++ b/include/inputdev/SDeviceSignature.hpp @@ -0,0 +1,37 @@ +#ifndef SDeviceSignature_HPP +#define SDeviceSignature_HPP + +#include +#include + +namespace boo +{ + +class CDeviceToken; +class CDeviceBase; + +struct SDeviceSignature +{ + typedef std::vector TDeviceSignatureSet; + typedef std::function TFactoryLambda; + const char* m_name; + unsigned m_vid, m_pid; + bool m_lowLevel; + TFactoryLambda m_factory; + SDeviceSignature() : m_name(NULL) {} /* Sentinel constructor */ + SDeviceSignature(const char* name, unsigned vid, unsigned pid, bool lowLevel, TFactoryLambda&& factory) + : m_name(name), m_vid(vid), m_pid(pid), m_lowLevel(lowLevel), m_factory(factory) {} + static bool DeviceMatchToken(const CDeviceToken& token, const TDeviceSignatureSet& sigSet); + static CDeviceBase* DeviceNew(CDeviceToken& token); +}; + +#define DEVICE_SIG(name, vid, pid, lowLevel) \ + SDeviceSignature(#name, vid, pid, lowLevel, [](CDeviceToken* tok) -> CDeviceBase* {return new name(tok);}) +#define DEVICE_SIG_SENTINEL() SDeviceSignature() + +extern const SDeviceSignature BOO_DEVICE_SIGS[]; + +} + +#endif // SDeviceSignature_HPP + diff --git a/libBoo.pri b/libBoo.pri index aef1e8b..acbd12b 100644 --- a/libBoo.pri +++ b/libBoo.pri @@ -15,9 +15,9 @@ HEADERS += \ $$PWD/include/inputdev/CDeviceFinder.hpp \ $$PWD/include/inputdev/CDeviceToken.hpp \ $$PWD/include/inputdev/CDeviceBase.hpp \ - $$PWD/include/inputdev/DeviceClasses.hpp \ $$PWD/include/inputdev/IHIDListener.hpp \ - $$PWD/src/inputdev/IHIDDevice.hpp + $$PWD/src/inputdev/IHIDDevice.hpp \ + $$PWD/include/inputdev/SDeviceSignature.hpp unix:!macx:HEADERS += \ $$PWD/include/x11/CGLXContext.hpp @@ -29,6 +29,7 @@ win32:HEADERS += \ $$PWD/include/win/CWGLContext.hpp SOURCES += \ + $$PWD/InputDeviceClasses.cpp \ $$PWD/src/CSurface.cpp \ $$PWD/src/CRetraceWaiter.cpp \ $$PWD/src/CInputRelay.cpp \ @@ -39,9 +40,9 @@ SOURCES += \ $$PWD/src/inputdev/CDualshockPad.cpp \ $$PWD/src/inputdev/CGenericPad.cpp \ $$PWD/src/inputdev/CDeviceBase.cpp \ - $$PWD/src/inputdev/DeviceClasses.cpp \ $$PWD/src/inputdev/CHIDListenerUdev.cpp \ - $$PWD/src/inputdev/CHIDDeviceUdev.cpp + $$PWD/src/inputdev/CHIDDeviceUdev.cpp \ + $$PWD/src/inputdev/SDeviceSignature.cpp unix:!macx:SOURCES += \ $$PWD/src/x11/CGLXContext.cpp diff --git a/libBoo.pro b/libBoo.pro index 339b233..38c4c31 100644 --- a/libBoo.pro +++ b/libBoo.pro @@ -1,6 +1,8 @@ CONFIG -= Qt #QMAKE_CXXFLAGS -= -std=c++0x -QMAKE_CXXFLAGS += -std=c++11 +CONFIG += c++11 +QMAKE_CXXFLAGS += -stdlib=libc++ +LIBS += -std=c++11 -stdlib=libc++ -lc++abi #unix:!macx:CONFIG += link_pkgconfig #unix:!macx:PKGCONFIG += x11 diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index 5c9962f..a0371ad 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -118,7 +118,12 @@ void CDolphinSmashAdapter::transferCycle() m_rumbleState = rumbleReq; } } -}; +} + +void CDolphinSmashAdapter::initialCycle() +{ + +} void CDolphinSmashAdapter::finalCycle() { @@ -127,7 +132,7 @@ void CDolphinSmashAdapter::finalCycle() uint8_t rumbleMessage[5] = {0x11, 0, 0, 0, 0}; sendInterruptTransfer(0, rumbleMessage, sizeof(rumbleMessage)); } -}; +} void CDolphinSmashAdapter::deviceDisconnected() { diff --git a/src/inputdev/CHIDDeviceIOKit.cpp b/src/inputdev/CHIDDeviceIOKit.cpp index 215d71e..b2199c4 100644 --- a/src/inputdev/CHIDDeviceIOKit.cpp +++ b/src/inputdev/CHIDDeviceIOKit.cpp @@ -8,6 +8,9 @@ #define MAX_REPORT_SIZE 65536 +namespace boo +{ + class CHIDDeviceIOKit final : public IHIDDevice { CDeviceToken& m_token; @@ -253,3 +256,5 @@ IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLeve { return new CHIDDeviceIOKit(token, devImp, lowLevel); } + +} diff --git a/src/inputdev/CHIDDeviceUdev.cpp b/src/inputdev/CHIDDeviceUdev.cpp index a8ddd18..ea7c1c8 100644 --- a/src/inputdev/CHIDDeviceUdev.cpp +++ b/src/inputdev/CHIDDeviceUdev.cpp @@ -17,7 +17,7 @@ namespace boo { -udev* BooGetUdev(); +udev* GetUdev(); #define MAX_REPORT_SIZE 65536 @@ -52,7 +52,7 @@ class CHIDDeviceUdev final : public IHIDDevice (void*)data }; int ret = ioctl(m_devFd, USBDEVFS_BULK, &xfer); - if (ret != length) + if (ret != (int)length) return false; return true; } @@ -79,12 +79,23 @@ class CHIDDeviceUdev final : public IHIDDevice { unsigned i; std::unique_lock lk(device->m_initMutex); - udev_device* hidDev = udev_device_new_from_syspath(BooGetUdev(), device->m_devPath.c_str()); + udev_device* hidDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); /* Get the HID element's parent (USB interrupt transfer-interface) */ udev_device* usbDev = udev_device_get_parent_with_subsystem_devtype(hidDev, "usb", "usb_device"); const char* dp = udev_device_get_devnode(usbDev); - device->m_devFd = open(dp, O_RDONLY); + device->m_devFd = open(dp, O_RDWR); + if (device->m_devFd < 0) + { + char errStr[256]; + snprintf(errStr, 256, "Unable to open %s@%s: %s\n", + device->m_token.getProductName().c_str(), dp, strerror(errno)); + device->m_devImp.deviceError(errStr); + lk.unlock(); + device->m_initCond.notify_one(); + udev_device_unref(hidDev); + return; + } usb_device_descriptor devDesc = {0}; read(device->m_devFd, &devDesc, 1); read(device->m_devFd, &devDesc.bDescriptorType, devDesc.bLength-1); @@ -128,13 +139,14 @@ class CHIDDeviceUdev final : public IHIDDevice device->m_initCond.notify_one(); /* Start transfer loop */ + device->m_devImp.initialCycle(); while (device->m_runningTransferLoop) device->m_devImp.transferCycle(); device->m_devImp.finalCycle(); /* Cleanup */ close(device->m_devFd); - device->m_devFd = NULL; + device->m_devFd = 0; udev_device_unref(hidDev); } @@ -173,7 +185,7 @@ public: ~CHIDDeviceUdev() { m_runningTransferLoop = false; - m_thread->detach(); + m_thread->join(); delete m_thread; } diff --git a/src/inputdev/CHIDListenerIOKit.cpp b/src/inputdev/CHIDListenerIOKit.cpp index 8826113..ee7604c 100644 --- a/src/inputdev/CHIDListenerIOKit.cpp +++ b/src/inputdev/CHIDListenerIOKit.cpp @@ -6,6 +6,9 @@ #include #include +namespace boo +{ + /* * Reference: http://oroboro.com/usb-serial-number-osx/ */ @@ -286,3 +289,4 @@ IHIDListener* IHIDListenerNew(CDeviceFinder& finder) return new CHIDListenerIOKit(finder); } +} diff --git a/src/inputdev/CHIDListenerUdev.cpp b/src/inputdev/CHIDListenerUdev.cpp index 2601c17..94abbdf 100644 --- a/src/inputdev/CHIDListenerUdev.cpp +++ b/src/inputdev/CHIDListenerUdev.cpp @@ -8,7 +8,7 @@ namespace boo { static udev* UDEV_INST = NULL; -udev* BooGetUdev() +udev* GetUdev() { if (!UDEV_INST) UDEV_INST = udev_new(); @@ -105,7 +105,7 @@ public: { /* Setup hotplug events */ - m_udevMon = udev_monitor_new_from_netlink(BooGetUdev(), "udev"); + m_udevMon = udev_monitor_new_from_netlink(GetUdev(), "udev"); if (!m_udevMon) throw std::runtime_error("unable to init udev_monitor"); udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "hid", NULL); @@ -145,7 +145,7 @@ public: /* Manual device scanning */ bool scanNow() { - udev_enumerate* uenum = udev_enumerate_new(BooGetUdev()); + udev_enumerate* uenum = udev_enumerate_new(GetUdev()); udev_enumerate_add_match_subsystem(uenum, "hid"); udev_enumerate_scan_devices(uenum); udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum); diff --git a/src/inputdev/DeviceClasses.cpp b/src/inputdev/DeviceClasses.cpp deleted file mode 100644 index 3c70a57..0000000 --- a/src/inputdev/DeviceClasses.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "inputdev/DeviceClasses.hpp" -#include "inputdev/CDeviceToken.hpp" -#include "IHIDDevice.hpp" - -namespace boo -{ - -bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask) -{ - if (mask & DEV_DOL_SMASH_ADAPTER && - token.getVendorId() == VID_NINTENDO && token.getProductId() == PID_SMASH_ADAPTER) - return true; - return false; -} - -IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel); -CDeviceBase* BooDeviceNew(CDeviceToken& token) -{ - - CDeviceBase* retval = NULL; - bool lowLevel = false; - - if (token.getVendorId() == VID_NINTENDO && token.getProductId() == PID_SMASH_ADAPTER) - { - retval = new CDolphinSmashAdapter(&token); - lowLevel = true; - } - - if (!retval) - return NULL; - - IHIDDevice* newDev = IHIDDeviceNew(token, *retval, lowLevel); - if (!newDev) - { - delete retval; - return NULL; - } - - return retval; - -} - -} diff --git a/src/inputdev/SDeviceSignature.cpp b/src/inputdev/SDeviceSignature.cpp new file mode 100644 index 0000000..bb18161 --- /dev/null +++ b/src/inputdev/SDeviceSignature.cpp @@ -0,0 +1,57 @@ +#include "inputdev/SDeviceSignature.hpp" +#include "inputdev/CDeviceToken.hpp" +#include "IHIDDevice.hpp" + +namespace boo +{ + +extern const SDeviceSignature BOO_DEVICE_SIGS[]; + + +bool SDeviceSignature::DeviceMatchToken(const CDeviceToken& token, const TDeviceSignatureSet& sigSet) +{ + for (const SDeviceSignature* sig : sigSet) + { + if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId()) + return true; + } + return false; +} + +IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel); +CDeviceBase* SDeviceSignature::DeviceNew(CDeviceToken& token) +{ + + CDeviceBase* retval = NULL; + const SDeviceSignature* foundSig = NULL; + const SDeviceSignature* sigIter = BOO_DEVICE_SIGS; + unsigned targetVid = token.getVendorId(); + unsigned targetPid = token.getProductId(); + while (sigIter->m_name) + { + if (sigIter->m_vid == targetVid && sigIter->m_pid == targetPid) + { + foundSig = sigIter; + break; + } + ++sigIter; + } + if (!foundSig) + return NULL; + + retval = foundSig->m_factory(&token); + if (!retval) + return NULL; + + IHIDDevice* newDev = IHIDDeviceNew(token, *retval, foundSig->m_lowLevel); + if (!newDev) + { + delete retval; + return NULL; + } + + return retval; + +} + +} diff --git a/test/main.cpp b/test/main.cpp index fdce46d..3e3902a 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -33,7 +33,7 @@ class CTestDeviceFinder : public CDeviceFinder CDolphinSmashAdapterCallback m_cb; public: CTestDeviceFinder() - : CDeviceFinder(DEV_DOL_SMASH_ADAPTER) + : CDeviceFinder({"CDolphinSmashAdapter"}) {} void deviceConnected(CDeviceToken& tok) { From 1125e20b6e11172f62d1e085c028392dd2b8d765 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Thu, 30 Apr 2015 13:17:46 -1000 Subject: [PATCH 10/17] better generic HID differentiation; smash udev working --- InputDeviceClasses.cpp | 2 +- include/inputdev/CDeviceBase.hpp | 6 +- include/inputdev/CDeviceFinder.hpp | 4 +- include/inputdev/CDeviceToken.hpp | 17 +++- include/inputdev/CDolphinSmashAdapter.hpp | 1 - include/inputdev/CGenericPad.hpp | 11 +++ include/inputdev/SDeviceSignature.hpp | 9 +- src/inputdev/CDeviceBase.cpp | 12 +-- src/inputdev/CDolphinSmashAdapter.cpp | 107 ++++++++++------------ src/inputdev/CGenericPad.cpp | 22 +++++ src/inputdev/CHIDDeviceIOKit.cpp | 12 +-- src/inputdev/CHIDDeviceUdev.cpp | 73 +++++++++++---- src/inputdev/CHIDListenerUdev.cpp | 73 ++++++++++++--- src/inputdev/IHIDDevice.hpp | 6 +- src/inputdev/SDeviceSignature.cpp | 27 +++++- 15 files changed, 257 insertions(+), 125 deletions(-) diff --git a/InputDeviceClasses.cpp b/InputDeviceClasses.cpp index 0156671..3557116 100644 --- a/InputDeviceClasses.cpp +++ b/InputDeviceClasses.cpp @@ -6,7 +6,7 @@ namespace boo const SDeviceSignature BOO_DEVICE_SIGS[] = { - DEVICE_SIG(CDolphinSmashAdapter, 0x57e, 0x337, true), + DEVICE_SIG(CDolphinSmashAdapter, 0x57e, 0x337), DEVICE_SIG_SENTINEL() }; diff --git a/include/inputdev/CDeviceBase.hpp b/include/inputdev/CDeviceBase.hpp index 1d4df7f..fc84027 100644 --- a/include/inputdev/CDeviceBase.hpp +++ b/include/inputdev/CDeviceBase.hpp @@ -27,14 +27,14 @@ public: virtual void deviceError(const char* error) {fprintf(stderr, "%s\n", error);} /* Low-Level API */ - bool sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length); - size_t receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length); + bool sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length); + size_t receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length); virtual void initialCycle() {} virtual void transferCycle() {} virtual void finalCycle() {} /* High-Level API */ - bool sendReport(const uint8_t* data, size_t length); + bool sendHIDReport(const uint8_t* data, size_t length); virtual size_t receiveReport(uint8_t* data, size_t length) {return 0;} }; diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index c6cb3de..cec1660 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -44,7 +44,7 @@ private: return true; return false; } - inline void _insertToken(CDeviceToken&& token) + inline bool _insertToken(CDeviceToken&& token) { if (SDeviceSignature::DeviceMatchToken(token, m_types)) { m_tokensLock.lock(); @@ -52,7 +52,9 @@ private: m_tokens.insert(std::make_pair(token.getDevicePath(), std::move(token))); m_tokensLock.unlock(); deviceConnected(inseredTok.first->second); + return true; } + return false; } inline void _removeToken(const std::string& path) { diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index cab3367..7efc3b4 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -10,6 +10,17 @@ namespace boo class CDeviceToken { +public: + enum TDeviceType + { + DEVTYPE_NONE = 0, + DEVTYPE_USB = 1, + DEVTYPE_BLUETOOTH = 2, + DEVTYPE_GENERICHID = 3 + }; + +private: + TDeviceType m_devType; unsigned m_vendorId; unsigned m_productId; std::string m_vendorName; @@ -28,10 +39,11 @@ class CDeviceToken } public: + CDeviceToken(const CDeviceToken&) = delete; CDeviceToken(CDeviceToken&&) = default; - inline CDeviceToken(unsigned vid, unsigned pid, const char* vname, const char* pname, const char* path) - : m_vendorId(vid), m_productId(pid), m_devPath(path), m_connectedDev(NULL) + inline CDeviceToken(enum TDeviceType devType, unsigned vid, unsigned pid, const char* vname, const char* pname, const char* path) + : m_devType(devType), m_vendorId(vid), m_productId(pid), m_devPath(path), m_connectedDev(NULL) { if (vname) m_vendorName = vname; @@ -39,6 +51,7 @@ public: m_productName = pname; } + inline TDeviceType getDeviceType() const {return m_devType;} inline unsigned getVendorId() const {return m_vendorId;} inline unsigned getProductId() const {return m_productId;} inline const std::string& getVendorName() const {return m_vendorName;} diff --git a/include/inputdev/CDolphinSmashAdapter.hpp b/include/inputdev/CDolphinSmashAdapter.hpp index 5954752..ceb0795 100644 --- a/include/inputdev/CDolphinSmashAdapter.hpp +++ b/include/inputdev/CDolphinSmashAdapter.hpp @@ -52,7 +52,6 @@ class CDolphinSmashAdapter final : public CDeviceBase uint8_t m_knownControllers; uint8_t m_rumbleRequest; uint8_t m_rumbleState; - bool m_didHandshake; void deviceDisconnected(); void initialCycle(); void transferCycle(); diff --git a/include/inputdev/CGenericPad.hpp b/include/inputdev/CGenericPad.hpp index 234a092..2444c3b 100644 --- a/include/inputdev/CGenericPad.hpp +++ b/include/inputdev/CGenericPad.hpp @@ -1,9 +1,20 @@ #ifndef CGENERICPAD_HPP #define CGENERICPAD_HPP +#include "CDeviceBase.hpp" + namespace boo { +class CGenericPad final : public CDeviceBase +{ +public: + CGenericPad(CDeviceToken* token); + ~CGenericPad(); + + void deviceDisconnected(); +}; + } #endif // CGENERICPAD_HPP diff --git a/include/inputdev/SDeviceSignature.hpp b/include/inputdev/SDeviceSignature.hpp index cef1681..80708b9 100644 --- a/include/inputdev/SDeviceSignature.hpp +++ b/include/inputdev/SDeviceSignature.hpp @@ -16,17 +16,16 @@ struct SDeviceSignature typedef std::function TFactoryLambda; const char* m_name; unsigned m_vid, m_pid; - bool m_lowLevel; TFactoryLambda m_factory; SDeviceSignature() : m_name(NULL) {} /* Sentinel constructor */ - SDeviceSignature(const char* name, unsigned vid, unsigned pid, bool lowLevel, TFactoryLambda&& factory) - : m_name(name), m_vid(vid), m_pid(pid), m_lowLevel(lowLevel), m_factory(factory) {} + SDeviceSignature(const char* name, unsigned vid, unsigned pid, TFactoryLambda&& factory) + : m_name(name), m_vid(vid), m_pid(pid), m_factory(factory) {} static bool DeviceMatchToken(const CDeviceToken& token, const TDeviceSignatureSet& sigSet); static CDeviceBase* DeviceNew(CDeviceToken& token); }; -#define DEVICE_SIG(name, vid, pid, lowLevel) \ - SDeviceSignature(#name, vid, pid, lowLevel, [](CDeviceToken* tok) -> CDeviceBase* {return new name(tok);}) +#define DEVICE_SIG(name, vid, pid) \ + SDeviceSignature(#name, vid, pid, [](CDeviceToken* tok) -> CDeviceBase* {return new name(tok);}) #define DEVICE_SIG_SENTINEL() SDeviceSignature() extern const SDeviceSignature BOO_DEVICE_SIGS[]; diff --git a/src/inputdev/CDeviceBase.cpp b/src/inputdev/CDeviceBase.cpp index ea5a199..c48c719 100644 --- a/src/inputdev/CDeviceBase.cpp +++ b/src/inputdev/CDeviceBase.cpp @@ -33,24 +33,24 @@ void CDeviceBase::closeDevice() m_token->_deviceClose(); } -bool CDeviceBase::sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length) +bool CDeviceBase::sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length) { if (m_hidDev) - return m_hidDev->_sendInterruptTransfer(pipe, data, length); + return m_hidDev->_sendUSBInterruptTransfer(pipe, data, length); return false; } -size_t CDeviceBase::receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length) +size_t CDeviceBase::receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length) { if (m_hidDev) - return m_hidDev->_receiveInterruptTransfer(pipe, data, length); + return m_hidDev->_receiveUSBInterruptTransfer(pipe, data, length); return false; } -bool CDeviceBase::sendReport(const uint8_t* data, size_t length) +bool CDeviceBase::sendHIDReport(const uint8_t* data, size_t length) { if (m_hidDev) - return m_hidDev->_sendReport(data, length); + return m_hidDev->_sendHIDReport(data, length); return false; } diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index a0371ad..4e5e840 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -14,8 +14,7 @@ CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token) m_callback(NULL), m_knownControllers(0), m_rumbleRequest(0), - m_rumbleState(0), - m_didHandshake(false) + m_rumbleState(0) { } @@ -23,8 +22,6 @@ CDolphinSmashAdapter::~CDolphinSmashAdapter() { } -static const uint8_t HANDSHAKE_PAYLOAD[] = {0x13}; - static inline EDolphinControllerType parseType(unsigned char status) { unsigned char type = status & (DOL_TYPE_NORMAL | DOL_TYPE_WAVEBIRD); @@ -61,77 +58,65 @@ parseState(SDolphinControllerState* stateOut, uint8_t* payload, bool& rumble) void CDolphinSmashAdapter::transferCycle() { - if (!m_didHandshake) + uint8_t payload[37]; + size_t recvSz = receiveUSBInterruptTransfer(0, payload, sizeof(payload)); + if (recvSz != 37 || payload[0] != 0x21) + return; + //printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]); + + if (!m_callback) + return; + + /* Parse controller states */ + uint8_t* controller = &payload[1]; + uint8_t rumbleMask = 0; + for (int i=0 ; i<4 ; i++, controller += 9) { - if (!sendInterruptTransfer(0, HANDSHAKE_PAYLOAD, sizeof(HANDSHAKE_PAYLOAD))) - return; - //printf("SENT HANDSHAKE %d\n", res); - m_didHandshake = true; + SDolphinControllerState state; + bool rumble = false; + EDolphinControllerType type = parseState(&state, controller, rumble); + if (type && !(m_knownControllers & 1<controllerConnected(i, type); + } + else if (!type && (m_knownControllers & 1<controllerDisconnected(i, type); + } + if (m_knownControllers & 1<controllerUpdate(i, type, state); + rumbleMask |= rumble ? 1<controllerConnected(i, type); - } - else if (!type && (m_knownControllers & 1<controllerDisconnected(i, type); - } - if (m_knownControllers & 1<controllerUpdate(i, type, state); - rumbleMask |= rumble ? 1< lk(device->m_initMutex); - udev_device* hidDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); + udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); /* Get the HID element's parent (USB interrupt transfer-interface) */ - udev_device* usbDev = udev_device_get_parent_with_subsystem_devtype(hidDev, "usb", "usb_device"); - const char* dp = udev_device_get_devnode(usbDev); + const char* dp = udev_device_get_devnode(udevDev); device->m_devFd = open(dp, O_RDWR); if (device->m_devFd < 0) { @@ -93,7 +92,7 @@ class CHIDDeviceUdev final : public IHIDDevice device->m_devImp.deviceError(errStr); lk.unlock(); device->m_initCond.notify_one(); - udev_device_unref(hidDev); + udev_device_unref(udevDev); return; } usb_device_descriptor devDesc = {0}; @@ -147,13 +146,46 @@ class CHIDDeviceUdev final : public IHIDDevice /* Cleanup */ close(device->m_devFd); device->m_devFd = 0; - udev_device_unref(hidDev); + udev_device_unref(udevDev); } - - static void _threadProcHL(CHIDDeviceUdev* device) - { + static void _threadProcBTLL(CHIDDeviceUdev* device) + { + std::unique_lock lk(device->m_initMutex); + udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + device->m_devImp.initialCycle(); + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); + + udev_device_unref(udevDev); + } + + static void _threadProcHID(CHIDDeviceUdev* device) + { + std::unique_lock lk(device->m_initMutex); + udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + device->m_devImp.initialCycle(); + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); + + udev_device_unref(udevDev); } void _deviceDisconnected() @@ -161,24 +193,29 @@ class CHIDDeviceUdev final : public IHIDDevice m_runningTransferLoop = false; } - bool _sendReport(const uint8_t* data, size_t length) + bool _sendHIDReport(const uint8_t* data, size_t length) { return false; } public: - CHIDDeviceUdev(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel) + CHIDDeviceUdev(CDeviceToken& token, CDeviceBase& devImp) : m_token(token), m_devImp(devImp), m_devPath(token.getDevicePath()) { devImp.m_hidDev = this; std::unique_lock lk(m_initMutex); - if (lowLevel) - m_thread = new std::thread(_threadProcLL, this); + CDeviceToken::TDeviceType dType = token.getDeviceType(); + if (dType == CDeviceToken::DEVTYPE_USB) + m_thread = new std::thread(_threadProcUSBLL, this); + else if (dType == CDeviceToken::DEVTYPE_BLUETOOTH) + m_thread = new std::thread(_threadProcBTLL, this); + else if (dType == CDeviceToken::DEVTYPE_GENERICHID) + m_thread = new std::thread(_threadProcHID, this); else - m_thread = new std::thread(_threadProcHL, this); + throw std::runtime_error("invalid token supplied to device constructor"); m_initCond.wait(lk); } @@ -192,9 +229,9 @@ public: }; -IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel) +IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp) { - return new CHIDDeviceUdev(token, devImp, lowLevel); + return new CHIDDeviceUdev(token, devImp); } } diff --git a/src/inputdev/CHIDListenerUdev.cpp b/src/inputdev/CHIDListenerUdev.cpp index 94abbdf..eba7e61 100644 --- a/src/inputdev/CHIDListenerUdev.cpp +++ b/src/inputdev/CHIDListenerUdev.cpp @@ -30,19 +30,33 @@ class CHIDListenerUdev final : public IHIDListener if (!listener->m_scanningEnabled) return; + /* Filter to USB/BT */ + const char* dt = udev_device_get_devtype(device); + CDeviceToken::TDeviceType type; + if (!strcmp(dt, "usb_device")) + type = CDeviceToken::DEVTYPE_USB; + else if (!strcmp(dt, "bluetooth_device")) + type = CDeviceToken::DEVTYPE_BLUETOOTH; + else + return; + + /* Prevent redundant registration */ const char* devPath = udev_device_get_syspath(device); if (listener->m_finder._hasToken(devPath)) return; - udev_device* devCast = - udev_device_get_parent_with_subsystem_devtype(device, "bluetooth", "bluetooth_device"); - if (!devCast) - devCast = udev_device_get_parent_with_subsystem_devtype(device, "usb", "usb_device"); - if (!devCast) - return; - int vid = 0, pid = 0; - udev_list_entry* attrs = udev_device_get_properties_list_entry(devCast); + udev_list_entry* attrs = udev_device_get_properties_list_entry(device); +#if 0 + udev_list_entry* att = NULL; + udev_list_entry_foreach(att, attrs) + { + const char* name = udev_list_entry_get_name(att); + const char* val = udev_list_entry_get_value(att); + fprintf(stderr, "%s %s\n", name, val); + } + fprintf(stderr, "\n\n"); +#endif udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID"); if (vide) @@ -62,7 +76,36 @@ class CHIDListenerUdev final : public IHIDListener if (producte) product = udev_list_entry_get_value(producte); - listener->m_finder._insertToken(CDeviceToken(vid, pid, manuf, product, devPath)); + if (!listener->m_finder._insertToken(CDeviceToken(type, vid, pid, manuf, product, devPath))) + { + /* Matched-insertion failed; see if generic HID interface is available */ + udev_list_entry* devInterfaces = NULL; + if (type == CDeviceToken::DEVTYPE_USB) + devInterfaces = udev_list_entry_get_by_name(attrs, "ID_USB_INTERFACES"); + else if (type == CDeviceToken::DEVTYPE_BLUETOOTH) + devInterfaces = udev_list_entry_get_by_name(attrs, "ID_BLUETOOTH_INTERFACES"); + if (devInterfaces) + { + const char* interfacesStr = udev_list_entry_get_value(devInterfaces); + if (strstr(interfacesStr, ":030104") || /* HID / GenericDesktop / Joystick */ + strstr(interfacesStr, ":030105")) /* HID / GenericDesktop / Gamepad */ + { + udev_enumerate* hidEnum = udev_enumerate_new(UDEV_INST); + udev_enumerate_add_match_parent(hidEnum, device); + udev_enumerate_add_match_subsystem(hidEnum, "hid"); + udev_enumerate_scan_devices(hidEnum); + udev_list_entry* hidEnt = udev_enumerate_get_list_entry(hidEnum); + if (hidEnt) + { + const char* hidPath = udev_list_entry_get_name(hidEnt); + if (!listener->m_finder._hasToken(hidPath)) + listener->m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_GENERICHID, + vid, pid, manuf, product, hidPath)); + } + udev_enumerate_unref(hidEnum); + } + } + } } static void deviceDisconnected(CHIDListenerUdev* listener, @@ -87,13 +130,9 @@ class CHIDListenerUdev final : public IHIDListener { const char* action = udev_device_get_action(dev); if (!strcmp(action, "add")) - { deviceConnected(listener, dev); - } else if (!strcmp(action, "remove")) - { deviceDisconnected(listener, dev); - } udev_device_unref(dev); } } @@ -108,7 +147,8 @@ public: m_udevMon = udev_monitor_new_from_netlink(GetUdev(), "udev"); if (!m_udevMon) throw std::runtime_error("unable to init udev_monitor"); - udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "hid", NULL); + udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "usb", "usb_device"); + udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "bluetooth", "bluetooth_device"); udev_monitor_filter_update(m_udevMon); /* Initial HID Device Add */ @@ -146,7 +186,10 @@ public: bool scanNow() { udev_enumerate* uenum = udev_enumerate_new(GetUdev()); - udev_enumerate_add_match_subsystem(uenum, "hid"); + udev_enumerate_add_match_subsystem(uenum, "usb"); + udev_enumerate_add_match_property(uenum, "DEVTYPE", "usb_device"); + udev_enumerate_add_match_subsystem(uenum, "bluetooth"); + udev_enumerate_add_match_property(uenum, "DEVTYPE", "bluetooth_device"); udev_enumerate_scan_devices(uenum); udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum); udev_list_entry* uenumItem; diff --git a/src/inputdev/IHIDDevice.hpp b/src/inputdev/IHIDDevice.hpp index 0eec0f1..e65d311 100644 --- a/src/inputdev/IHIDDevice.hpp +++ b/src/inputdev/IHIDDevice.hpp @@ -10,9 +10,9 @@ class IHIDDevice { friend class CDeviceBase; virtual void _deviceDisconnected()=0; - virtual bool _sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)=0; - virtual size_t _receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)=0; - virtual bool _sendReport(const uint8_t* data, size_t length)=0; + virtual bool _sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)=0; + virtual size_t _receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)=0; + virtual bool _sendHIDReport(const uint8_t* data, size_t length)=0; public: inline virtual ~IHIDDevice() {}; }; diff --git a/src/inputdev/SDeviceSignature.cpp b/src/inputdev/SDeviceSignature.cpp index bb18161..ced5ffe 100644 --- a/src/inputdev/SDeviceSignature.cpp +++ b/src/inputdev/SDeviceSignature.cpp @@ -1,5 +1,6 @@ #include "inputdev/SDeviceSignature.hpp" #include "inputdev/CDeviceToken.hpp" +#include "inputdev/CGenericPad.hpp" #include "IHIDDevice.hpp" namespace boo @@ -10,6 +11,8 @@ extern const SDeviceSignature BOO_DEVICE_SIGS[]; bool SDeviceSignature::DeviceMatchToken(const CDeviceToken& token, const TDeviceSignatureSet& sigSet) { + if (token.getDeviceType() == CDeviceToken::DEVTYPE_GENERICHID) + return true; for (const SDeviceSignature* sig : sigSet) { if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId()) @@ -18,11 +21,29 @@ bool SDeviceSignature::DeviceMatchToken(const CDeviceToken& token, const TDevice return false; } -IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel); +IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp); CDeviceBase* SDeviceSignature::DeviceNew(CDeviceToken& token) { - CDeviceBase* retval = NULL; + + /* Early-return for generic HID devices */ + if (token.getDeviceType() == CDeviceToken::DEVTYPE_GENERICHID) + { + retval = new CGenericPad(&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 SDeviceSignature* foundSig = NULL; const SDeviceSignature* sigIter = BOO_DEVICE_SIGS; unsigned targetVid = token.getVendorId(); @@ -43,7 +64,7 @@ CDeviceBase* SDeviceSignature::DeviceNew(CDeviceToken& token) if (!retval) return NULL; - IHIDDevice* newDev = IHIDDeviceNew(token, *retval, foundSig->m_lowLevel); + IHIDDevice* newDev = IHIDDeviceNew(token, *retval); if (!newDev) { delete retval; From d32dbbd6942d8f59e90cae009b8de8b5657a438c Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Thu, 30 Apr 2015 15:11:25 -1000 Subject: [PATCH 11/17] brought mac in sync from udev edits --- libBoo.pri | 57 +++++++----- src/inputdev/CHIDDeviceIOKit.cpp | 80 ++++++++--------- src/inputdev/CHIDListenerIOKit.cpp | 137 +++++++---------------------- src/mac/CCGLContext.cpp | 6 ++ 4 files changed, 110 insertions(+), 170 deletions(-) diff --git a/libBoo.pri b/libBoo.pri index acbd12b..752282a 100644 --- a/libBoo.pri +++ b/libBoo.pri @@ -19,15 +19,6 @@ HEADERS += \ $$PWD/src/inputdev/IHIDDevice.hpp \ $$PWD/include/inputdev/SDeviceSignature.hpp -unix:!macx:HEADERS += \ - $$PWD/include/x11/CGLXContext.hpp - -macx:HEADERS += \ - $$PWD/include/mac/CCGLContext.hpp - -win32:HEADERS += \ - $$PWD/include/win/CWGLContext.hpp - SOURCES += \ $$PWD/InputDeviceClasses.cpp \ $$PWD/src/CSurface.cpp \ @@ -40,27 +31,45 @@ SOURCES += \ $$PWD/src/inputdev/CDualshockPad.cpp \ $$PWD/src/inputdev/CGenericPad.cpp \ $$PWD/src/inputdev/CDeviceBase.cpp \ - $$PWD/src/inputdev/CHIDListenerUdev.cpp \ - $$PWD/src/inputdev/CHIDDeviceUdev.cpp \ $$PWD/src/inputdev/SDeviceSignature.cpp -unix:!macx:SOURCES += \ - $$PWD/src/x11/CGLXContext.cpp +unix:!macx { + HEADERS += \ + $$PWD/include/x11/CGLXContext.hpp -macx:SOURCES += \ - $$PWD/src/mac/CCGLContext.cpp \ - $$PWD/src/inputdev/CHIDDeviceIOKit.cpp \ - $$PWD/src/inputdev/CHIDListenerIOKit.cpp + SOURCES += \ + $$PWD/src/x11/CGLXContext.cpp +} -macx:OBJECTIVE_SOURCES += \ - $$PWD/src/mac/CCGLCocoaView.mm \ +linux { + SOURCES += \ + $$PWD/src/inputdev/CHIDListenerUdev.cpp \ + $$PWD/src/inputdev/CHIDDeviceUdev.cpp + LIBS += -ludev +} -win32:SOURCES += \ - $$PWD/src/win/CWGLContext.cpp \ - $$PWD/src/inputdev/CHIDDeviceWin32.cpp \ - $$PWD/src/inputdev/CHIDListenerWin32.cpp +macx { + HEADERS += \ + $$PWD/include/mac/CCGLContext.hpp + + SOURCES += \ + $$PWD/src/mac/CCGLContext.cpp \ + $$PWD/src/inputdev/CHIDDeviceIOKit.cpp \ + $$PWD/src/inputdev/CHIDListenerIOKit.cpp + + OBJECTIVE_SOURCES += \ + $$PWD/src/mac/CCGLCocoaView.mm +} + +win32 { + HEADERS += \ + $$PWD/include/win/CWGLContext.hpp + SOURCES += \ + $$PWD/src/win/CWGLContext.cpp \ + $$PWD/src/inputdev/CHIDDeviceWin32.cpp \ + $$PWD/src/inputdev/CHIDListenerWin32.cpp +} INCLUDEPATH += $$PWD/include -unix:!macx:LIBS += -ludev diff --git a/src/inputdev/CHIDDeviceIOKit.cpp b/src/inputdev/CHIDDeviceIOKit.cpp index c4ee698..7e84f8c 100644 --- a/src/inputdev/CHIDDeviceIOKit.cpp +++ b/src/inputdev/CHIDDeviceIOKit.cpp @@ -25,7 +25,6 @@ class CHIDDeviceIOKit final : public IHIDDevice std::mutex m_initMutex; std::condition_variable m_initCond; std::thread* m_thread; - CFRunLoopRef m_runLoop = NULL; bool _sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length) { @@ -50,7 +49,7 @@ class CHIDDeviceIOKit final : public IHIDDevice return 0; } - static void _threadProcLL(CHIDDeviceIOKit* device) + static void _threadProcUSBLL(CHIDDeviceIOKit* device) { char thrName[128]; snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str()); @@ -148,6 +147,7 @@ class CHIDDeviceIOKit final : public IHIDDevice device->m_initCond.notify_one(); /* Start transfer loop */ + device->m_devImp.initialCycle(); while (device->m_runningTransferLoop) device->m_devImp.transferCycle(); device->m_devImp.finalCycle(); @@ -160,6 +160,23 @@ class CHIDDeviceIOKit final : public IHIDDevice } + static void _threadProcBTLL(CHIDDeviceIOKit* device) + { + std::unique_lock lk(device->m_initMutex); + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + device->m_devImp.initialCycle(); + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); + + } + static void _inputReport(CHIDDeviceIOKit* device, IOReturn result, @@ -178,49 +195,29 @@ class CHIDDeviceIOKit final : public IHIDDevice device->_deviceDisconnected(); } - static void _threadProcHL(CHIDDeviceIOKit* device) + static void _threadProcHID(CHIDDeviceIOKit* device) { - char thrName[128]; - snprintf(thrName, 128, "%s HID Thread", device->m_token.getProductName().c_str()); - pthread_setname_np(thrName); - __block std::unique_lock lk(device->m_initMutex); - device->m_runLoop = CFRunLoopGetCurrent(); - CFRunLoopAddObserver(device->m_runLoop, - CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopEntry, false, 0, - ^(CFRunLoopObserverRef, CFRunLoopActivity) { - lk.unlock(); - device->m_initCond.notify_one(); - }), kCFRunLoopCommonModes); + std::unique_lock lk(device->m_initMutex); - uint8_t* inputBuf = new uint8_t[MAX_REPORT_SIZE]; - io_registry_entry_t devServ = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str()); - IOHIDDeviceRef dev = IOHIDDeviceCreate(kCFAllocatorDefault, devServ); - IOHIDDeviceRegisterInputReportCallback(dev, inputBuf, MAX_REPORT_SIZE, - (IOHIDReportCallback)_inputReport, device); - IOHIDDeviceRegisterRemovalCallback(dev, (IOHIDCallback)_disconnect, device); - IOHIDDeviceScheduleWithRunLoop(dev, device->m_runLoop, kCFRunLoopDefaultMode); - IOHIDDeviceOpen(dev, kIOHIDOptionsTypeNone); - CFRunLoopRun(); - if (device->m_runLoop) - IOHIDDeviceClose(dev, kIOHIDOptionsTypeNone); - CFRelease(dev); + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + device->m_devImp.initialCycle(); + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); } void _deviceDisconnected() { - CFRunLoopRef rl = m_runLoop; - m_runLoop = NULL; - if (rl) - CFRunLoopStop(rl); m_runningTransferLoop = false; } bool _sendHIDReport(const uint8_t* data, size_t length) { - if (m_runLoop) - { - - } return false; } @@ -233,19 +230,22 @@ public: { devImp.m_hidDev = this; std::unique_lock lk(m_initMutex); - if (lowLevel) - m_thread = new std::thread(_threadProcLL, this); + CDeviceToken::TDeviceType dType = token.getDeviceType(); + if (dType == CDeviceToken::DEVTYPE_USB) + m_thread = new std::thread(_threadProcUSBLL, this); + else if (dType == CDeviceToken::DEVTYPE_BLUETOOTH) + m_thread = new std::thread(_threadProcBTLL, this); + else if (dType == CDeviceToken::DEVTYPE_GENERICHID) + m_thread = new std::thread(_threadProcHID, this); else - m_thread = new std::thread(_threadProcHL, this); + throw std::runtime_error("invalid token supplied to device constructor"); m_initCond.wait(lk); } ~CHIDDeviceIOKit() { - if (m_runLoop) - CFRunLoopStop(m_runLoop); m_runningTransferLoop = false; - m_thread->detach(); + m_thread->join(); delete m_thread; } diff --git a/src/inputdev/CHIDListenerIOKit.cpp b/src/inputdev/CHIDListenerIOKit.cpp index ee7604c..7be2019 100644 --- a/src/inputdev/CHIDListenerIOKit.cpp +++ b/src/inputdev/CHIDListenerIOKit.cpp @@ -57,72 +57,12 @@ class CHIDListenerIOKit final : public IHIDListener CDeviceFinder& m_finder; CFRunLoopRef m_listenerRunLoop; - IOHIDManagerRef m_hidManager; IONotificationPortRef m_llPort; + io_iterator_t m_llAddNotif, m_llRemoveNotif; bool m_scanningEnabled; - static void deviceConnected(CHIDListenerIOKit* listener, - IOReturn, - void*, - IOHIDDeviceRef device) - { - if (!listener->m_scanningEnabled) - return; - io_string_t devPath; - if (IORegistryEntryGetPath(IOHIDDeviceGetService(device), kIOServicePlane, devPath) != 0) - return; - if (listener->m_finder._hasToken(devPath)) - return; - CFIndex vid, pid; - CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid); - CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)), kCFNumberCFIndexType, &pid); - CFStringRef manuf = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey)); - CFStringRef product = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); - listener->m_finder._insertToken(CDeviceToken(vid, pid, - CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8), - CFStringGetCStringPtr(product, kCFStringEncodingUTF8), - devPath)); - } - - static void deviceDisconnected(CHIDListenerIOKit* listener, - IOReturn ret, - void* sender, - IOHIDDeviceRef device) - { - if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop) - { - CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{ - deviceDisconnected(listener, ret, sender, device); - }); - CFRunLoopWakeUp(listener->m_listenerRunLoop); - return; - } - io_string_t devPath; - if (IORegistryEntryGetPath(IOHIDDeviceGetService(device), kIOServicePlane, devPath) != 0) - return; - listener->m_finder._removeToken(devPath); - } - - static void applyDevice(IOHIDDeviceRef device, CHIDListenerIOKit* listener) - { - io_string_t devPath; - if (IORegistryEntryGetPath(IOHIDDeviceGetService(device), kIOServicePlane, devPath) != 0) - return; - if (listener->m_finder._hasToken(devPath)) - return; - CFIndex vid, pid; - CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid); - CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)), kCFNumberCFIndexType, &pid); - CFStringRef manuf = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey)); - CFStringRef product = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); - listener->m_finder._insertToken(CDeviceToken(vid, pid, - CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8), - CFStringGetCStringPtr(product, kCFStringEncodingUTF8), - devPath)); - } - - static void devicesConnectedLL(CHIDListenerIOKit* listener, - io_iterator_t iterator) + static void devicesConnectedUSBLL(CHIDListenerIOKit* listener, + io_iterator_t iterator) { io_object_t obj; while ((obj = IOIteratorNext(iterator))) @@ -165,7 +105,12 @@ class CHIDListenerIOKit final : public IHIDListener getUSBStringDescriptor(dev, vstridx, vstr); getUSBStringDescriptor(dev, pstridx, pstr); - listener->m_finder._insertToken(CDeviceToken(vid, pid, vstr, pstr, devPath)); + if (!listener->m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_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); @@ -175,13 +120,13 @@ class CHIDListenerIOKit final : public IHIDListener } - static void devicesDisconnectedLL(CHIDListenerIOKit* listener, - io_iterator_t iterator) + static void devicesDisconnectedUSBLL(CHIDListenerIOKit* listener, + io_iterator_t iterator) { if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop) { CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{ - devicesDisconnectedLL(listener, iterator); + devicesDisconnectedUSBLL(listener, iterator); }); CFRunLoopWakeUp(listener->m_listenerRunLoop); return; @@ -203,48 +148,27 @@ public: : m_finder(finder) { - /* Register HID Manager */ - m_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone); - IOHIDManagerSetDeviceMatching(m_hidManager, NULL); - IOHIDManagerRegisterDeviceMatchingCallback(m_hidManager, (IOHIDDeviceCallback)deviceConnected, this); - IOHIDManagerRegisterDeviceRemovalCallback(m_hidManager, (IOHIDDeviceCallback)deviceDisconnected, this); + /* Register Low-Level USB Matcher */ m_listenerRunLoop = CFRunLoopGetCurrent(); - IOHIDManagerScheduleWithRunLoop(m_hidManager, m_listenerRunLoop, kCFRunLoopDefaultMode); - IOReturn ret = IOHIDManagerOpen(m_hidManager, kIOHIDManagerOptionNone); - if (ret != kIOReturnSuccess) - throw std::runtime_error("error establishing IOHIDManager"); - - /* Initial HID Device Add */ - m_scanningEnabled = true; - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); - - /* Register Low-Level Matcher */ m_llPort = IONotificationPortCreate(kIOMasterPortDefault); - CFRunLoopAddSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode); + CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort); + CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode); CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName); - CFIndex nintendoVid = VID_NINTENDO; - CFIndex smashPid = PID_SMASH_ADAPTER; - CFNumberRef nintendoVidNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &nintendoVid); - CFNumberRef smashPidNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &smashPid); - CFDictionaryAddValue(matchDict, CFSTR(kUSBVendorID), nintendoVidNum); - CFDictionaryAddValue(matchDict, CFSTR(kUSBProductID), smashPidNum); - CFRelease(nintendoVidNum); - CFRelease(smashPidNum); CFRetain(matchDict); - io_iterator_t initialIt; + m_scanningEnabled = true; kern_return_t llRet = IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict, - (IOServiceMatchingCallback)devicesConnectedLL, this, &initialIt); - if (llRet == 0) - devicesConnectedLL(this, initialIt); + (IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif); + if (llRet == kIOReturnSuccess) + devicesConnectedUSBLL(this, m_llAddNotif); llRet = IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict, - (IOServiceMatchingCallback)devicesDisconnectedLL, this, &initialIt); - if (llRet == 0) - devicesDisconnectedLL(this, initialIt); + (IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif); + if (llRet == kIOReturnSuccess) + devicesDisconnectedUSBLL(this, m_llRemoveNotif); m_scanningEnabled = false; @@ -252,10 +176,9 @@ public: ~CHIDListenerIOKit() { - IOHIDManagerUnscheduleFromRunLoop(m_hidManager, m_listenerRunLoop, kCFRunLoopDefaultMode); - IOHIDManagerClose(m_hidManager, kIOHIDManagerOptionNone); - CFRelease(m_hidManager); CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode); + IOObjectRelease(m_llAddNotif); + IOObjectRelease(m_llRemoveNotif); IONotificationPortDestroy(m_llPort); } @@ -274,11 +197,13 @@ public: /* Manual device scanning */ bool scanNow() { - CFSetRef devs = IOHIDManagerCopyDevices(m_hidManager); - m_finder.m_tokensLock.lock(); - CFSetApplyFunction(devs, (CFSetApplierFunction)applyDevice, this); - m_finder.m_tokensLock.unlock(); - CFRelease(devs); + io_iterator_t iter; + if (IOServiceGetMatchingServices(kIOMasterPortDefault, + IOServiceMatching(kIOUSBDeviceClassName), &iter) == kIOReturnSuccess) + { + devicesConnectedUSBLL(this, iter); + IOObjectRelease(iter); + } return true; } diff --git a/src/mac/CCGLContext.cpp b/src/mac/CCGLContext.cpp index a7eeaf6..7e3217c 100644 --- a/src/mac/CCGLContext.cpp +++ b/src/mac/CCGLContext.cpp @@ -2,6 +2,9 @@ #include "CCGLContext.hpp" #include +namespace boo +{ + CCGLContext::CCGLContext() : m_minVersion(3), m_majVersion(3) @@ -58,4 +61,7 @@ int CCGLContext::blueDepth() const { return -1; } + +} + #endif \ No newline at end of file From e01920ffe93966922e3ce31c7d243d289522a6e6 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Thu, 30 Apr 2015 15:13:48 -1000 Subject: [PATCH 12/17] unnecessary method removal --- src/inputdev/CHIDDeviceIOKit.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/inputdev/CHIDDeviceIOKit.cpp b/src/inputdev/CHIDDeviceIOKit.cpp index 7e84f8c..a7bd0dd 100644 --- a/src/inputdev/CHIDDeviceIOKit.cpp +++ b/src/inputdev/CHIDDeviceIOKit.cpp @@ -177,24 +177,6 @@ class CHIDDeviceIOKit final : public IHIDDevice } - - static void _inputReport(CHIDDeviceIOKit* device, - IOReturn result, - void* sender, - IOHIDReportType type, - uint32_t reportID, - uint8_t* report, - CFIndex reportLength) - { - - } - static void _disconnect(CHIDDeviceIOKit* device, - IOReturn result, - IOHIDDeviceRef sender) - { - device->_deviceDisconnected(); - } - static void _threadProcHID(CHIDDeviceIOKit* device) { std::unique_lock lk(device->m_initMutex); From f0dc0451fd962ae86e6c8a4ba000fe40bce8daa2 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 1 May 2015 14:20:30 -1000 Subject: [PATCH 13/17] initial round of MSVC porting of libBoo also added libwdi submodule --- .gitmodules | 3 + WinUSBInstaller/WinUSBInstaller.manifest | 10 + WinUSBInstaller/WinUSBInstaller.pro | 20 ++ WinUSBInstaller/main.c | 39 ++++ extern/libwdi | 1 + include/boo.hpp | 4 +- include/inputdev/CDeviceToken.hpp | 16 +- include/win/CWGLContext.hpp | 8 + libBoo.pro | 16 +- src/CSurface.cpp | 4 +- src/inputdev/CHIDDeviceWin32.cpp | 233 +++++++++++++++++++++++ src/inputdev/CHIDListenerWin32.cpp | 223 +++++++++++++++++++++- test/Win32Elevatedlauncher.c | 37 ++++ test/main.cpp | 48 ++++- test/test.pri | 5 +- 15 files changed, 653 insertions(+), 14 deletions(-) create mode 100644 .gitmodules create mode 100644 WinUSBInstaller/WinUSBInstaller.manifest create mode 100644 WinUSBInstaller/WinUSBInstaller.pro create mode 100644 WinUSBInstaller/main.c create mode 160000 extern/libwdi create mode 100644 test/Win32Elevatedlauncher.c diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4c252a7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "extern/libwdi"] + path = extern/libwdi + url = https://github.com/jackoalan/libwdi.git diff --git a/WinUSBInstaller/WinUSBInstaller.manifest b/WinUSBInstaller/WinUSBInstaller.manifest new file mode 100644 index 0000000..c3b4160 --- /dev/null +++ b/WinUSBInstaller/WinUSBInstaller.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/WinUSBInstaller/WinUSBInstaller.pro b/WinUSBInstaller/WinUSBInstaller.pro new file mode 100644 index 0000000..3a122e2 --- /dev/null +++ b/WinUSBInstaller/WinUSBInstaller.pro @@ -0,0 +1,20 @@ +CONFIG += console +CONFIG -= app_bundle +CONFIG -= qt + +!win32 { + error(this project is designed for windows only) +} + +INCLUDEPATH += $$PWD/../extern/libwdi +LIBS += \ + Shell32.lib \ + Ole32.lib \ + Setupapi.lib \ + Advapi32.lib \ + User32.lib \ + $$PWD/../extern/libwdi/x64/Debug/lib/libwdi.lib + +SOURCES += main.c + + diff --git a/WinUSBInstaller/main.c b/WinUSBInstaller/main.c new file mode 100644 index 0000000..27e3364 --- /dev/null +++ b/WinUSBInstaller/main.c @@ -0,0 +1,39 @@ +#include +#include +#include + +int main(void) +{ + printf("Hello World!\n"); + + struct wdi_device_info *device, *list; + struct wdi_options_create_list WDI_LIST_OPTS = + { + true, false, true + }; + int err = wdi_create_list(&list, &WDI_LIST_OPTS); + if (err == WDI_SUCCESS) + { + for (device = list; device != NULL; device = device->next) + { + if (device->vid == 0x57E && device->pid == 0x337 && + !strcmp(device->driver, "HidUsb")) + { + printf("GC adapter detected; installing driver\n"); + char tempDir[128]; + GetTempPathA(128, tempDir); + err = wdi_prepare_driver(device, tempDir, "winusb_smash.inf", NULL); + if (err == WDI_SUCCESS) + { + err = wdi_install_driver(device, tempDir, "winusb_smash.inf", NULL); + printf(""); + } + break; + } + } + wdi_destroy_list(list); + } + + return 0; +} + diff --git a/extern/libwdi b/extern/libwdi new file mode 160000 index 0000000..aeacb8b --- /dev/null +++ b/extern/libwdi @@ -0,0 +1 @@ +Subproject commit aeacb8b85c8143d0e59b0f6c4206008a017ebde0 diff --git a/include/boo.hpp b/include/boo.hpp index 74883e6..c407f9e 100644 --- a/include/boo.hpp +++ b/include/boo.hpp @@ -2,13 +2,13 @@ #define BOO_HPP #if defined(_WIN32) -#error "No support for WGL" +#include "win/CWGLContext.hpp" +namespace boo {typedef CWGLContext CGraphicsContext;} #elif defined(__APPLE__) #include "mac/CCGLContext.hpp" namespace boo {typedef CCGLContext CGraphicsContext;} - #elif defined(__GNUC__) || defined(__clang__) #include "x11/CGLXContext.hpp" namespace boo {typedef CGLXContext CGraphicsContext;} diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index 7efc3b4..2753081 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -41,9 +41,21 @@ private: public: CDeviceToken(const CDeviceToken&) = delete; - CDeviceToken(CDeviceToken&&) = default; + CDeviceToken(const CDeviceToken&& other) + : m_devType(other.m_devType), + m_vendorId(other.m_vendorId), + m_productId(other.m_productId), + m_vendorName(other.m_vendorName), + m_productName(other.m_productName), + m_devPath(other.m_devPath), + m_connectedDev(other.m_connectedDev) + {} inline CDeviceToken(enum TDeviceType devType, unsigned vid, unsigned pid, const char* vname, const char* pname, const char* path) - : m_devType(devType), m_vendorId(vid), m_productId(pid), m_devPath(path), m_connectedDev(NULL) + : m_devType(devType), + m_vendorId(vid), + m_productId(pid), + m_devPath(path), + m_connectedDev(NULL) { if (vname) m_vendorName = vname; diff --git a/include/win/CWGLContext.hpp b/include/win/CWGLContext.hpp index 3c1e57d..ea40ff7 100644 --- a/include/win/CWGLContext.hpp +++ b/include/win/CWGLContext.hpp @@ -3,8 +3,16 @@ #ifdef _WIN32 +#include "IGraphicsContext.hpp" + namespace boo { + +class CWGLContext : public IGraphicsContext +{ + +}; + } #endif // _WIN32 diff --git a/libBoo.pro b/libBoo.pro index 38c4c31..d1460c1 100644 --- a/libBoo.pro +++ b/libBoo.pro @@ -1,8 +1,18 @@ CONFIG -= Qt +CONFIG += console #QMAKE_CXXFLAGS -= -std=c++0x -CONFIG += c++11 -QMAKE_CXXFLAGS += -stdlib=libc++ -LIBS += -std=c++11 -stdlib=libc++ -lc++abi +#CONFIG += c++11 +unix:QMAKE_CXXFLAGS += -stdlib=libc++ +unix:LIBS += -std=c++11 -stdlib=libc++ -lc++abi + +win32:INCLUDEPATH += $$PWD/extern/libwdi +win32:LIBS += \ + Shell32.lib \ + Ole32.lib \ + Setupapi.lib \ + Advapi32.lib \ + User32.lib \ + $$PWD/extern/libwdi/x64/Debug/lib/libwdi.lib #unix:!macx:CONFIG += link_pkgconfig #unix:!macx:PKGCONFIG += x11 diff --git a/src/CSurface.cpp b/src/CSurface.cpp index 4605050..e23af70 100644 --- a/src/CSurface.cpp +++ b/src/CSurface.cpp @@ -5,12 +5,12 @@ namespace boo ISurface* CSurfaceNewWindow() { - + return nullptr; } ISurface* CSurfaceNewQWidget() { - + return nullptr; } } diff --git a/src/inputdev/CHIDDeviceWin32.cpp b/src/inputdev/CHIDDeviceWin32.cpp index 2888b5b..4984fa0 100644 --- a/src/inputdev/CHIDDeviceWin32.cpp +++ b/src/inputdev/CHIDDeviceWin32.cpp @@ -1 +1,234 @@ #include "IHIDDevice.hpp" +#include "inputdev/CDeviceToken.hpp" +#include "inputdev/CDeviceBase.hpp" +#include +#include +#include +#include + +namespace boo +{ + +#if 0 + +udev* GetUdev(); + +#define MAX_REPORT_SIZE 65536 + +/* + * Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html + */ + +class CHIDDeviceUdev final : public IHIDDevice +{ + CDeviceToken& m_token; + CDeviceBase& m_devImp; + + int m_devFd = 0; + unsigned m_usbIntfInPipe = 0; + unsigned m_usbIntfOutPipe = 0; + bool m_runningTransferLoop = false; + + const std::string& m_devPath; + std::mutex m_initMutex; + std::condition_variable m_initCond; + std::thread* m_thread; + + bool _sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length) + { + if (m_devFd) + { + usbdevfs_bulktransfer xfer = + { + m_usbIntfOutPipe | USB_DIR_OUT, + (unsigned)length, + 0, + (void*)data + }; + int ret = ioctl(m_devFd, USBDEVFS_BULK, &xfer); + if (ret != (int)length) + return false; + return true; + } + return false; + } + + size_t _receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length) + { + if (m_devFd) + { + usbdevfs_bulktransfer xfer = + { + m_usbIntfInPipe | USB_DIR_IN, + (unsigned)length, + 0, + data + }; + return ioctl(m_devFd, USBDEVFS_BULK, &xfer); + } + return 0; + } + + static void _threadProcUSBLL(CHIDDeviceUdev* device) + { + unsigned i; + std::unique_lock lk(device->m_initMutex); + udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); + + /* Get the HID element's parent (USB interrupt transfer-interface) */ + const char* dp = udev_device_get_devnode(udevDev); + device->m_devFd = open(dp, O_RDWR); + if (device->m_devFd < 0) + { + char errStr[256]; + snprintf(errStr, 256, "Unable to open %s@%s: %s\n", + device->m_token.getProductName().c_str(), dp, strerror(errno)); + device->m_devImp.deviceError(errStr); + lk.unlock(); + device->m_initCond.notify_one(); + udev_device_unref(udevDev); + return; + } + usb_device_descriptor devDesc = {0}; + read(device->m_devFd, &devDesc, 1); + read(device->m_devFd, &devDesc.bDescriptorType, devDesc.bLength-1); + if (devDesc.bNumConfigurations) + { + usb_config_descriptor confDesc = {0}; + read(device->m_devFd, &confDesc, 1); + read(device->m_devFd, &confDesc.bDescriptorType, confDesc.bLength-1); + if (confDesc.bNumInterfaces) + { + usb_interface_descriptor intfDesc = {0}; + read(device->m_devFd, &intfDesc, 1); + read(device->m_devFd, &intfDesc.bDescriptorType, intfDesc.bLength-1); + for (i=0 ; im_devFd, &endpDesc, 1); + read(device->m_devFd, &endpDesc.bDescriptorType, endpDesc.bLength-1); + if ((endpDesc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + { + if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + device->m_usbIntfInPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + else if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) + device->m_usbIntfOutPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + } + } + } + } + + /* Request that kernel disconnects existing driver */ + usbdevfs_ioctl disconnectReq = { + 0, + USBDEVFS_DISCONNECT, + NULL + }; + ioctl(device->m_devFd, USBDEVFS_IOCTL, &disconnectReq); + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + device->m_devImp.initialCycle(); + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); + + /* Cleanup */ + close(device->m_devFd); + device->m_devFd = 0; + udev_device_unref(udevDev); + + } + + static void _threadProcBTLL(CHIDDeviceUdev* device) + { + std::unique_lock lk(device->m_initMutex); + udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + device->m_devImp.initialCycle(); + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); + + udev_device_unref(udevDev); + } + + static void _threadProcHID(CHIDDeviceUdev* device) + { + std::unique_lock lk(device->m_initMutex); + udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + device->m_devImp.initialCycle(); + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); + + udev_device_unref(udevDev); + } + + void _deviceDisconnected() + { + m_runningTransferLoop = false; + } + + bool _sendHIDReport(const uint8_t* data, size_t length) + { + return false; + } + +public: + + CHIDDeviceUdev(CDeviceToken& token, CDeviceBase& devImp) + : m_token(token), + m_devImp(devImp), + m_devPath(token.getDevicePath()) + { + devImp.m_hidDev = this; + std::unique_lock lk(m_initMutex); + CDeviceToken::TDeviceType dType = token.getDeviceType(); + if (dType == CDeviceToken::DEVTYPE_USB) + m_thread = new std::thread(_threadProcUSBLL, this); + else if (dType == CDeviceToken::DEVTYPE_BLUETOOTH) + m_thread = new std::thread(_threadProcBTLL, this); + else if (dType == CDeviceToken::DEVTYPE_GENERICHID) + m_thread = new std::thread(_threadProcHID, this); + else + throw std::runtime_error("invalid token supplied to device constructor"); + m_initCond.wait(lk); + } + + ~CHIDDeviceUdev() + { + m_runningTransferLoop = false; + m_thread->join(); + delete m_thread; + } + + +}; + +#endif + +IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp) +{ + //return new CHIDDeviceUdev(token, devImp); + return nullptr; +} + +} diff --git a/src/inputdev/CHIDListenerWin32.cpp b/src/inputdev/CHIDListenerWin32.cpp index 90f36b2..7398249 100644 --- a/src/inputdev/CHIDListenerWin32.cpp +++ b/src/inputdev/CHIDListenerWin32.cpp @@ -1 +1,222 @@ -#include "IHIDListener.hpp" +#include "inputdev/IHIDListener.hpp" +#include "inputdev/CDeviceFinder.hpp" +#include +#include + +#define _WIN32_LEAN_AND_MEAN 1 +#include + +namespace boo +{ + +#if 0 + +static udev* UDEV_INST = NULL; +udev* GetUdev() +{ + if (!UDEV_INST) + UDEV_INST = udev_new(); + return UDEV_INST; +} + +class CHIDListenerWin32 final : public IHIDListener +{ + CDeviceFinder& m_finder; + + udev_monitor* m_udevMon; + std::thread* m_udevThread; + bool m_udevRunning; + bool m_scanningEnabled; + + static void deviceConnected(CHIDListenerWin32* listener, + udev_device* device) + { + if (!listener->m_scanningEnabled) + return; + + /* Filter to USB/BT */ + const char* dt = udev_device_get_devtype(device); + CDeviceToken::TDeviceType type; + if (!strcmp(dt, "usb_device")) + type = CDeviceToken::DEVTYPE_USB; + else if (!strcmp(dt, "bluetooth_device")) + type = CDeviceToken::DEVTYPE_BLUETOOTH; + else + return; + + /* Prevent redundant registration */ + const char* devPath = udev_device_get_syspath(device); + if (listener->m_finder._hasToken(devPath)) + return; + + int vid = 0, pid = 0; + udev_list_entry* attrs = udev_device_get_properties_list_entry(device); +#if 0 + udev_list_entry* att = NULL; + udev_list_entry_foreach(att, attrs) + { + const char* name = udev_list_entry_get_name(att); + const char* val = udev_list_entry_get_value(att); + fprintf(stderr, "%s %s\n", name, val); + } + fprintf(stderr, "\n\n"); +#endif + + udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID"); + if (vide) + vid = strtol(udev_list_entry_get_value(vide), NULL, 16); + + udev_list_entry* pide = udev_list_entry_get_by_name(attrs, "ID_MODEL_ID"); + if (pide) + pid = strtol(udev_list_entry_get_value(pide), NULL, 16); + + const char* manuf = NULL; + udev_list_entry* manufe = udev_list_entry_get_by_name(attrs, "ID_VENDOR"); + if (manufe) + manuf = udev_list_entry_get_value(manufe); + + const char* product = NULL; + udev_list_entry* producte = udev_list_entry_get_by_name(attrs, "ID_MODEL"); + if (producte) + product = udev_list_entry_get_value(producte); + + if (!listener->m_finder._insertToken(CDeviceToken(type, vid, pid, manuf, product, devPath))) + { + /* Matched-insertion failed; see if generic HID interface is available */ + udev_list_entry* devInterfaces = NULL; + if (type == CDeviceToken::DEVTYPE_USB) + devInterfaces = udev_list_entry_get_by_name(attrs, "ID_USB_INTERFACES"); + else if (type == CDeviceToken::DEVTYPE_BLUETOOTH) + devInterfaces = udev_list_entry_get_by_name(attrs, "ID_BLUETOOTH_INTERFACES"); + if (devInterfaces) + { + const char* interfacesStr = udev_list_entry_get_value(devInterfaces); + if (strstr(interfacesStr, ":030104") || /* HID / GenericDesktop / Joystick */ + strstr(interfacesStr, ":030105")) /* HID / GenericDesktop / Gamepad */ + { + udev_enumerate* hidEnum = udev_enumerate_new(UDEV_INST); + udev_enumerate_add_match_parent(hidEnum, device); + udev_enumerate_add_match_subsystem(hidEnum, "hid"); + udev_enumerate_scan_devices(hidEnum); + udev_list_entry* hidEnt = udev_enumerate_get_list_entry(hidEnum); + if (hidEnt) + { + const char* hidPath = udev_list_entry_get_name(hidEnt); + if (!listener->m_finder._hasToken(hidPath)) + listener->m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_GENERICHID, + vid, pid, manuf, product, hidPath)); + } + udev_enumerate_unref(hidEnum); + } + } + } + } + + static void deviceDisconnected(CHIDListenerWin32* listener, + udev_device* device) + { + const char* devPath = udev_device_get_syspath(device); + listener->m_finder._removeToken(devPath); + } + + static void _udevProc(CHIDListenerWin32* listener) + { + udev_monitor_enable_receiving(listener->m_udevMon); + int fd = udev_monitor_get_fd(listener->m_udevMon); + while (listener->m_udevRunning) + { + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + select(fd+1, &fds, NULL, NULL, NULL); + udev_device* dev = udev_monitor_receive_device(listener->m_udevMon); + if (dev) + { + const char* action = udev_device_get_action(dev); + if (!strcmp(action, "add")) + deviceConnected(listener, dev); + else if (!strcmp(action, "remove")) + deviceDisconnected(listener, dev); + udev_device_unref(dev); + } + } + } + +public: + CHIDListenerWin32(CDeviceFinder& finder) + : m_finder(finder) + { + + /* Setup hotplug events */ + m_udevMon = udev_monitor_new_from_netlink(GetUdev(), "udev"); + if (!m_udevMon) + throw std::runtime_error("unable to init udev_monitor"); + udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "usb", "usb_device"); + udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "bluetooth", "bluetooth_device"); + udev_monitor_filter_update(m_udevMon); + + /* Initial HID Device Add */ + m_scanningEnabled = true; + scanNow(); + m_scanningEnabled = false; + + /* Start hotplug thread */ + m_udevRunning = true; + m_udevThread = new std::thread(_udevProc, this); + + } + + ~CHIDListenerWin32() + { + m_udevRunning = false; + m_udevThread->join(); + delete m_udevThread; + udev_monitor_unref(m_udevMon); + } + + /* Automatic device scanning */ + bool startScanning() + { + m_scanningEnabled = true; + return true; + } + bool stopScanning() + { + m_scanningEnabled = false; + return true; + } + + /* Manual device scanning */ + bool scanNow() + { + udev_enumerate* uenum = udev_enumerate_new(GetUdev()); + udev_enumerate_add_match_subsystem(uenum, "usb"); + udev_enumerate_add_match_property(uenum, "DEVTYPE", "usb_device"); + udev_enumerate_add_match_subsystem(uenum, "bluetooth"); + udev_enumerate_add_match_property(uenum, "DEVTYPE", "bluetooth_device"); + udev_enumerate_scan_devices(uenum); + udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum); + udev_list_entry* uenumItem; + udev_list_entry_foreach(uenumItem, uenumList) + { + const char* devPath = udev_list_entry_get_name(uenumItem); + udev_device* dev = udev_device_new_from_syspath(UDEV_INST, devPath); + if (dev) + deviceConnected(this, dev); + udev_device_unref(dev); + } + udev_enumerate_unref(uenum); + return true; + } + +}; + +#endif + +IHIDListener* IHIDListenerNew(CDeviceFinder& finder) +{ + // return new CHIDListenerWin32(finder); + return nullptr; +} + +} diff --git a/test/Win32Elevatedlauncher.c b/test/Win32Elevatedlauncher.c new file mode 100644 index 0000000..3f4445d --- /dev/null +++ b/test/Win32Elevatedlauncher.c @@ -0,0 +1,37 @@ + +#define _WIN32_LEAN_AND_MEAN 1 +#include +#include + +int genWin32ShellExecute(const wchar_t* AppFullPath, + const wchar_t* Verb, + const wchar_t* Params, + bool ShowAppWindow, + bool WaitToFinish) +{ + int Result = 0; + + // Setup the required structure + SHELLEXECUTEINFO ShExecInfo; + memset(&ShExecInfo, 0, sizeof(SHELLEXECUTEINFO)); + ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); + ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + ShExecInfo.hwnd = NULL; + ShExecInfo.lpVerb = Verb; + ShExecInfo.lpFile = AppFullPath; + ShExecInfo.lpParameters = Params; + ShExecInfo.lpDirectory = NULL; + ShExecInfo.nShow = (ShowAppWindow ? SW_SHOW : SW_HIDE); + ShExecInfo.hInstApp = NULL; + + // Spawn the process + if (ShellExecuteEx(&ShExecInfo) == FALSE) + { + Result = -1; // Failed to execute process + } else if (WaitToFinish) + { + WaitForSingleObject(ShExecInfo.hProcess, INFINITE); + } + + return Result; +} diff --git a/test/main.cpp b/test/main.cpp index 3e3902a..0133f1c 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -4,8 +4,13 @@ #else #endif #include -#include #include +#if _WIN32 +#define _WIN32_LEAN_AND_MEAN 1 +#include +#else +#include +#endif namespace boo { @@ -53,23 +58,62 @@ public: } +#if _WIN32 +extern "C" int genWin32ShellExecute(const wchar_t* AppFullPath, + const wchar_t* Verb, + const wchar_t* Params, + bool ShowAppWindow, + bool WaitToFinish); +#include +static void scanWinUSB() +{ + struct wdi_device_info *device, *list; + struct wdi_options_create_list WDI_LIST_OPTS = + { + true, false, true + }; + int err = wdi_create_list(&list, &WDI_LIST_OPTS); + if (err == WDI_SUCCESS) + { + for (device = list; device != NULL; device = device->next) + { + if (device->vid == 0x57E && device->pid == 0x337 && + !strcmp(device->driver, "HidUsb")) + { + printf("GC adapter detected; installing driver\n"); + + } + } + wdi_destroy_list(list); + } +} +#endif + int main(int argc, char** argv) { +#if _WIN32 + scanWinUSB(); +#endif + boo::CTestDeviceFinder finder; finder.startScanning(); +#if 0 boo::IGraphicsContext* ctx = new boo::CGraphicsContext; if (ctx->create()) { } +#endif #if __APPLE__ CFRunLoopRun(); +#elif _WIN32 + while (true) {Sleep(1000);} #else while (true) {sleep(1);} #endif - delete ctx; + //delete ctx; return 0; } diff --git a/test/test.pri b/test/test.pri index a0eae95..07e0ac2 100644 --- a/test/test.pri +++ b/test/test.pri @@ -1,4 +1,5 @@ SOURCES += \ - $$PWD/main.cpp + $$PWD/main.cpp \ + $$PWD/Win32Elevatedlauncher.c -CONFIG += c++11 \ No newline at end of file +win32:SOURCES += From 570396b76cfa896e15ab5fd143e6b425075c4ef4 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 1 May 2015 20:14:36 -1000 Subject: [PATCH 14/17] removed libwdi.. too much UAC headaches.. users will use zadig just like with dolphin --- .gitmodules | 3 -- WinUSBInstaller/WinUSBInstaller.manifest | 10 ------ WinUSBInstaller/WinUSBInstaller.pro | 20 ------------ WinUSBInstaller/main.c | 39 ----------------------- extern/libwdi | 1 - test/main.cpp | 40 ++++++++++++++++++++---- 6 files changed, 34 insertions(+), 79 deletions(-) delete mode 100644 WinUSBInstaller/WinUSBInstaller.manifest delete mode 100644 WinUSBInstaller/WinUSBInstaller.pro delete mode 100644 WinUSBInstaller/main.c delete mode 160000 extern/libwdi diff --git a/.gitmodules b/.gitmodules index 4c252a7..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "extern/libwdi"] - path = extern/libwdi - url = https://github.com/jackoalan/libwdi.git diff --git a/WinUSBInstaller/WinUSBInstaller.manifest b/WinUSBInstaller/WinUSBInstaller.manifest deleted file mode 100644 index c3b4160..0000000 --- a/WinUSBInstaller/WinUSBInstaller.manifest +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/WinUSBInstaller/WinUSBInstaller.pro b/WinUSBInstaller/WinUSBInstaller.pro deleted file mode 100644 index 3a122e2..0000000 --- a/WinUSBInstaller/WinUSBInstaller.pro +++ /dev/null @@ -1,20 +0,0 @@ -CONFIG += console -CONFIG -= app_bundle -CONFIG -= qt - -!win32 { - error(this project is designed for windows only) -} - -INCLUDEPATH += $$PWD/../extern/libwdi -LIBS += \ - Shell32.lib \ - Ole32.lib \ - Setupapi.lib \ - Advapi32.lib \ - User32.lib \ - $$PWD/../extern/libwdi/x64/Debug/lib/libwdi.lib - -SOURCES += main.c - - diff --git a/WinUSBInstaller/main.c b/WinUSBInstaller/main.c deleted file mode 100644 index 27e3364..0000000 --- a/WinUSBInstaller/main.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include - -int main(void) -{ - printf("Hello World!\n"); - - struct wdi_device_info *device, *list; - struct wdi_options_create_list WDI_LIST_OPTS = - { - true, false, true - }; - int err = wdi_create_list(&list, &WDI_LIST_OPTS); - if (err == WDI_SUCCESS) - { - for (device = list; device != NULL; device = device->next) - { - if (device->vid == 0x57E && device->pid == 0x337 && - !strcmp(device->driver, "HidUsb")) - { - printf("GC adapter detected; installing driver\n"); - char tempDir[128]; - GetTempPathA(128, tempDir); - err = wdi_prepare_driver(device, tempDir, "winusb_smash.inf", NULL); - if (err == WDI_SUCCESS) - { - err = wdi_install_driver(device, tempDir, "winusb_smash.inf", NULL); - printf(""); - } - break; - } - } - wdi_destroy_list(list); - } - - return 0; -} - diff --git a/extern/libwdi b/extern/libwdi deleted file mode 160000 index aeacb8b..0000000 --- a/extern/libwdi +++ /dev/null @@ -1 +0,0 @@ -Subproject commit aeacb8b85c8143d0e59b0f6c4206008a017ebde0 diff --git a/test/main.cpp b/test/main.cpp index 0133f1c..faa1202 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -59,11 +59,39 @@ public: } #if _WIN32 -extern "C" int genWin32ShellExecute(const wchar_t* AppFullPath, - const wchar_t* Verb, - const wchar_t* Params, - bool ShowAppWindow, - bool WaitToFinish); +static int genWin32ShellExecute(const wchar_t* AppFullPath, + const wchar_t* Verb, + const wchar_t* Params, + bool ShowAppWindow, + bool WaitToFinish) +{ + int Result = 0; + + // Setup the required structure + SHELLEXECUTEINFO ShExecInfo; + memset(&ShExecInfo, 0, sizeof(SHELLEXECUTEINFO)); + ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); + ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + ShExecInfo.hwnd = NULL; + ShExecInfo.lpVerb = Verb; + ShExecInfo.lpFile = AppFullPath; + ShExecInfo.lpParameters = Params; + ShExecInfo.lpDirectory = NULL; + ShExecInfo.nShow = (ShowAppWindow ? SW_SHOW : SW_HIDE); + ShExecInfo.hInstApp = NULL; + + // Spawn the process + if (ShellExecuteEx(&ShExecInfo) == FALSE) + { + Result = -1; // Failed to execute process + } else if (WaitToFinish) + { + WaitForSingleObject(ShExecInfo.hProcess, INFINITE); + } + + return Result; +} + #include static void scanWinUSB() { @@ -81,7 +109,7 @@ static void scanWinUSB() !strcmp(device->driver, "HidUsb")) { printf("GC adapter detected; installing driver\n"); - + genWin32ShellExecute(L"WinUsbInstaller.exe", L"", L"", false, true); } } wdi_destroy_list(list); From e2d7030bb4d69549fa8a740592c8bfc99653a1c5 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 2 May 2015 20:40:20 -1000 Subject: [PATCH 15/17] initial WinUSB implementation --- include/inputdev/CDeviceBase.hpp | 2 +- include/inputdev/CDeviceFinder.hpp | 2 +- include/inputdev/CDeviceToken.hpp | 2 +- libBoo.pri | 8 +- libBoo.pro | 9 +- src/inputdev/CHIDDeviceIOKit.cpp | 28 +++- src/inputdev/CHIDDeviceUdev.cpp | 6 +- src/inputdev/CHIDDeviceWin32.cpp | 234 ---------------------------- src/inputdev/CHIDDeviceWinUSB.cpp | 194 +++++++++++++++++++++++ src/inputdev/CHIDListenerWin32.cpp | 222 -------------------------- src/inputdev/CHIDListenerWinUSB.cpp | 203 ++++++++++++++++++++++++ test/Win32Elevatedlauncher.c | 37 ----- test/main.cpp | 95 ++++------- test/test.pri | 3 +- 14 files changed, 461 insertions(+), 584 deletions(-) delete mode 100644 src/inputdev/CHIDDeviceWin32.cpp create mode 100644 src/inputdev/CHIDDeviceWinUSB.cpp delete mode 100644 src/inputdev/CHIDListenerWin32.cpp create mode 100644 src/inputdev/CHIDListenerWinUSB.cpp delete mode 100644 test/Win32Elevatedlauncher.c diff --git a/include/inputdev/CDeviceBase.hpp b/include/inputdev/CDeviceBase.hpp index fc84027..392802f 100644 --- a/include/inputdev/CDeviceBase.hpp +++ b/include/inputdev/CDeviceBase.hpp @@ -13,7 +13,7 @@ class CDeviceBase friend class CDeviceToken; friend class CHIDDeviceIOKit; friend class CHIDDeviceUdev; - friend class CHIDDeviceWin32; + friend class CHIDDeviceWinUSB; class CDeviceToken* m_token; class IHIDDevice* m_hidDev; diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index cec1660..7878d01 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -19,7 +19,7 @@ class CDeviceFinder public: friend class CHIDListenerIOKit; friend class CHIDListenerUdev; - friend class CHIDListenerWin32; + friend class CHIDListenerWinUSB; static inline CDeviceFinder* instance() {return skDevFinder;} private: diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index 2753081..2908130 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -69,7 +69,7 @@ public: inline const std::string& getVendorName() const {return m_vendorName;} 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;} + inline bool isDeviceOpen() const {return (m_connectedDev != NULL);} inline CDeviceBase* openAndGetDevice() { if (!m_connectedDev) diff --git a/libBoo.pri b/libBoo.pri index 752282a..66e92a2 100644 --- a/libBoo.pri +++ b/libBoo.pri @@ -31,7 +31,9 @@ SOURCES += \ $$PWD/src/inputdev/CDualshockPad.cpp \ $$PWD/src/inputdev/CGenericPad.cpp \ $$PWD/src/inputdev/CDeviceBase.cpp \ - $$PWD/src/inputdev/SDeviceSignature.cpp + $$PWD/src/inputdev/SDeviceSignature.cpp \ + $$PWD/src/inputdev/CHIDListenerWinUSB.cpp \ + $$PWD/src/inputdev/CHIDDeviceWinUSB.cpp unix:!macx { HEADERS += \ @@ -66,9 +68,7 @@ win32 { HEADERS += \ $$PWD/include/win/CWGLContext.hpp SOURCES += \ - $$PWD/src/win/CWGLContext.cpp \ - $$PWD/src/inputdev/CHIDDeviceWin32.cpp \ - $$PWD/src/inputdev/CHIDListenerWin32.cpp + $$PWD/src/win/CWGLContext.cpp } INCLUDEPATH += $$PWD/include diff --git a/libBoo.pro b/libBoo.pro index d1460c1..496ad73 100644 --- a/libBoo.pro +++ b/libBoo.pro @@ -5,14 +5,7 @@ CONFIG += console unix:QMAKE_CXXFLAGS += -stdlib=libc++ unix:LIBS += -std=c++11 -stdlib=libc++ -lc++abi -win32:INCLUDEPATH += $$PWD/extern/libwdi -win32:LIBS += \ - Shell32.lib \ - Ole32.lib \ - Setupapi.lib \ - Advapi32.lib \ - User32.lib \ - $$PWD/extern/libwdi/x64/Debug/lib/libwdi.lib +win32:LIBS += Setupapi.lib winusb.lib User32.lib #unix:!macx:CONFIG += link_pkgconfig #unix:!macx:PKGCONFIG += x11 diff --git a/src/inputdev/CHIDDeviceIOKit.cpp b/src/inputdev/CHIDDeviceIOKit.cpp index a7bd0dd..95b115a 100644 --- a/src/inputdev/CHIDDeviceIOKit.cpp +++ b/src/inputdev/CHIDDeviceIOKit.cpp @@ -6,8 +6,6 @@ #include #include -#define MAX_REPORT_SIZE 65536 - namespace boo { @@ -54,6 +52,7 @@ class CHIDDeviceIOKit final : public IHIDDevice 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 lk(device->m_initMutex); /* Get the HID element's parent (USB interrupt transfer-interface) */ @@ -70,7 +69,10 @@ class CHIDDeviceIOKit final : public IHIDDevice } if (!interfaceEntry) { - throw std::runtime_error("unable to find device interface"); + 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; @@ -88,7 +90,9 @@ class CHIDDeviceIOKit final : public IHIDDevice IOObjectRelease(interfaceEntry); if (err) { - throw std::runtime_error("unable to obtain IOKit plugin service"); + 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; @@ -101,7 +105,9 @@ class CHIDDeviceIOKit final : public IHIDDevice (LPVOID*)&intf); if (err) { - throw std::runtime_error("unable to query IOKit USB interface"); + 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(); IODestroyPlugInInterface(iodev); @@ -114,9 +120,17 @@ class CHIDDeviceIOKit final : public IHIDDevice if (err != kIOReturnSuccess) { if (err == kIOReturnExclusiveAccess) - throw std::runtime_error("unable to open IOKit USB interface; someone else using it"); + { + 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 - throw std::runtime_error("unable to open IOKit USB interface"); + { + 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(); (*intf)->Release(intf); diff --git a/src/inputdev/CHIDDeviceUdev.cpp b/src/inputdev/CHIDDeviceUdev.cpp index c4a8111..48aaa11 100644 --- a/src/inputdev/CHIDDeviceUdev.cpp +++ b/src/inputdev/CHIDDeviceUdev.cpp @@ -19,8 +19,6 @@ namespace boo udev* GetUdev(); -#define MAX_REPORT_SIZE 65536 - /* * Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html */ @@ -78,15 +76,15 @@ class CHIDDeviceUdev final : public IHIDDevice static void _threadProcUSBLL(CHIDDeviceUdev* device) { unsigned i; + char errStr[256]; std::unique_lock lk(device->m_initMutex); udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); - /* Get the HID element's parent (USB interrupt transfer-interface) */ + /* Get device file */ const char* dp = udev_device_get_devnode(udevDev); device->m_devFd = open(dp, O_RDWR); if (device->m_devFd < 0) { - char errStr[256]; snprintf(errStr, 256, "Unable to open %s@%s: %s\n", device->m_token.getProductName().c_str(), dp, strerror(errno)); device->m_devImp.deviceError(errStr); diff --git a/src/inputdev/CHIDDeviceWin32.cpp b/src/inputdev/CHIDDeviceWin32.cpp deleted file mode 100644 index 4984fa0..0000000 --- a/src/inputdev/CHIDDeviceWin32.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include "IHIDDevice.hpp" -#include "inputdev/CDeviceToken.hpp" -#include "inputdev/CDeviceBase.hpp" -#include -#include -#include -#include - -namespace boo -{ - -#if 0 - -udev* GetUdev(); - -#define MAX_REPORT_SIZE 65536 - -/* - * Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html - */ - -class CHIDDeviceUdev final : public IHIDDevice -{ - CDeviceToken& m_token; - CDeviceBase& m_devImp; - - int m_devFd = 0; - unsigned m_usbIntfInPipe = 0; - unsigned m_usbIntfOutPipe = 0; - bool m_runningTransferLoop = false; - - const std::string& m_devPath; - std::mutex m_initMutex; - std::condition_variable m_initCond; - std::thread* m_thread; - - bool _sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length) - { - if (m_devFd) - { - usbdevfs_bulktransfer xfer = - { - m_usbIntfOutPipe | USB_DIR_OUT, - (unsigned)length, - 0, - (void*)data - }; - int ret = ioctl(m_devFd, USBDEVFS_BULK, &xfer); - if (ret != (int)length) - return false; - return true; - } - return false; - } - - size_t _receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length) - { - if (m_devFd) - { - usbdevfs_bulktransfer xfer = - { - m_usbIntfInPipe | USB_DIR_IN, - (unsigned)length, - 0, - data - }; - return ioctl(m_devFd, USBDEVFS_BULK, &xfer); - } - return 0; - } - - static void _threadProcUSBLL(CHIDDeviceUdev* device) - { - unsigned i; - std::unique_lock lk(device->m_initMutex); - udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); - - /* Get the HID element's parent (USB interrupt transfer-interface) */ - const char* dp = udev_device_get_devnode(udevDev); - device->m_devFd = open(dp, O_RDWR); - if (device->m_devFd < 0) - { - char errStr[256]; - snprintf(errStr, 256, "Unable to open %s@%s: %s\n", - device->m_token.getProductName().c_str(), dp, strerror(errno)); - device->m_devImp.deviceError(errStr); - lk.unlock(); - device->m_initCond.notify_one(); - udev_device_unref(udevDev); - return; - } - usb_device_descriptor devDesc = {0}; - read(device->m_devFd, &devDesc, 1); - read(device->m_devFd, &devDesc.bDescriptorType, devDesc.bLength-1); - if (devDesc.bNumConfigurations) - { - usb_config_descriptor confDesc = {0}; - read(device->m_devFd, &confDesc, 1); - read(device->m_devFd, &confDesc.bDescriptorType, confDesc.bLength-1); - if (confDesc.bNumInterfaces) - { - usb_interface_descriptor intfDesc = {0}; - read(device->m_devFd, &intfDesc, 1); - read(device->m_devFd, &intfDesc.bDescriptorType, intfDesc.bLength-1); - for (i=0 ; im_devFd, &endpDesc, 1); - read(device->m_devFd, &endpDesc.bDescriptorType, endpDesc.bLength-1); - if ((endpDesc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) - { - if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) - device->m_usbIntfInPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - else if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) - device->m_usbIntfOutPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - } - } - } - } - - /* Request that kernel disconnects existing driver */ - usbdevfs_ioctl disconnectReq = { - 0, - USBDEVFS_DISCONNECT, - NULL - }; - ioctl(device->m_devFd, USBDEVFS_IOCTL, &disconnectReq); - - /* Return control to main thread */ - device->m_runningTransferLoop = true; - lk.unlock(); - device->m_initCond.notify_one(); - - /* Start transfer loop */ - device->m_devImp.initialCycle(); - while (device->m_runningTransferLoop) - device->m_devImp.transferCycle(); - device->m_devImp.finalCycle(); - - /* Cleanup */ - close(device->m_devFd); - device->m_devFd = 0; - udev_device_unref(udevDev); - - } - - static void _threadProcBTLL(CHIDDeviceUdev* device) - { - std::unique_lock lk(device->m_initMutex); - udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); - - /* Return control to main thread */ - device->m_runningTransferLoop = true; - lk.unlock(); - device->m_initCond.notify_one(); - - /* Start transfer loop */ - device->m_devImp.initialCycle(); - while (device->m_runningTransferLoop) - device->m_devImp.transferCycle(); - device->m_devImp.finalCycle(); - - udev_device_unref(udevDev); - } - - static void _threadProcHID(CHIDDeviceUdev* device) - { - std::unique_lock lk(device->m_initMutex); - udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); - - /* Return control to main thread */ - device->m_runningTransferLoop = true; - lk.unlock(); - device->m_initCond.notify_one(); - - /* Start transfer loop */ - device->m_devImp.initialCycle(); - while (device->m_runningTransferLoop) - device->m_devImp.transferCycle(); - device->m_devImp.finalCycle(); - - udev_device_unref(udevDev); - } - - void _deviceDisconnected() - { - m_runningTransferLoop = false; - } - - bool _sendHIDReport(const uint8_t* data, size_t length) - { - return false; - } - -public: - - CHIDDeviceUdev(CDeviceToken& token, CDeviceBase& devImp) - : m_token(token), - m_devImp(devImp), - m_devPath(token.getDevicePath()) - { - devImp.m_hidDev = this; - std::unique_lock lk(m_initMutex); - CDeviceToken::TDeviceType dType = token.getDeviceType(); - if (dType == CDeviceToken::DEVTYPE_USB) - m_thread = new std::thread(_threadProcUSBLL, this); - else if (dType == CDeviceToken::DEVTYPE_BLUETOOTH) - m_thread = new std::thread(_threadProcBTLL, this); - else if (dType == CDeviceToken::DEVTYPE_GENERICHID) - m_thread = new std::thread(_threadProcHID, this); - else - throw std::runtime_error("invalid token supplied to device constructor"); - m_initCond.wait(lk); - } - - ~CHIDDeviceUdev() - { - m_runningTransferLoop = false; - m_thread->join(); - delete m_thread; - } - - -}; - -#endif - -IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp) -{ - //return new CHIDDeviceUdev(token, devImp); - return nullptr; -} - -} diff --git a/src/inputdev/CHIDDeviceWinUSB.cpp b/src/inputdev/CHIDDeviceWinUSB.cpp new file mode 100644 index 0000000..f1811ed --- /dev/null +++ b/src/inputdev/CHIDDeviceWinUSB.cpp @@ -0,0 +1,194 @@ +#define _CRT_SECURE_NO_WARNINGS 1 /* STFU MSVC */ +#include "IHIDDevice.hpp" +#include "inputdev/CDeviceToken.hpp" +#include "inputdev/CDeviceBase.hpp" +#include +#include +#include +#include +#include + +#define _WIN32_LEAN_AND_MEAN 1 +#include +#include + +namespace boo +{ + +class CHIDDeviceWinUSB final : public IHIDDevice +{ + CDeviceToken& m_token; + CDeviceBase& m_devImp; + + HANDLE m_devHandle = 0; + WINUSB_INTERFACE_HANDLE m_usbHandle = NULL; + unsigned m_usbIntfInPipe = 0; + unsigned m_usbIntfOutPipe = 0; + bool m_runningTransferLoop = false; + + const std::string& m_devPath; + std::mutex m_initMutex; + std::condition_variable m_initCond; + std::thread* m_thread; + + bool _sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length) + { + if (m_usbHandle) + { + ULONG lengthTransferred = 0; + if (!WinUsb_WritePipe(m_usbHandle, m_usbIntfOutPipe, (PUCHAR)data, + (ULONG)length, &lengthTransferred, NULL) + || lengthTransferred != length) + return false; + return true; + } + return false; + } + + size_t _receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length) + { + if (m_usbHandle) + { + ULONG lengthTransferred = 0; + if (!WinUsb_ReadPipe(m_usbHandle, m_usbIntfInPipe, (PUCHAR)data, + (ULONG)length, &lengthTransferred, NULL)) + return 0; + return lengthTransferred; + } + return 0; + } + + static void _threadProcUSBLL(CHIDDeviceWinUSB* device) + { + unsigned i; + char errStr[256]; + std::unique_lock lk(device->m_initMutex); + + /* POSIX.. who needs it?? -MS */ + device->m_devHandle = CreateFileA(device->m_devPath.c_str(), + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + if (INVALID_HANDLE_VALUE == device->m_devHandle) { + _snprintf(errStr, 256, "Unable to open %s@%s: %d\n", + device->m_token.getProductName().c_str(), + device->m_devPath, GetLastError()); + device->m_devImp.deviceError(errStr); + lk.unlock(); + device->m_initCond.notify_one(); + return; + } + + if (!WinUsb_Initialize(device->m_devHandle, &device->m_usbHandle)) { + _snprintf(errStr, 256, "Unable to open %s@%s: %d\n", + device->m_token.getProductName().c_str(), + device->m_devPath, GetLastError()); + device->m_devImp.deviceError(errStr); + lk.unlock(); + device->m_initCond.notify_one(); + CloseHandle(device->m_devHandle); + return; + } + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + device->m_devImp.initialCycle(); + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); + + /* Cleanup */ + WinUsb_Free(device->m_usbHandle); + CloseHandle(device->m_devHandle); + device->m_devHandle = 0; + + } + + static void _threadProcBTLL(CHIDDeviceWinUSB* device) + { + std::unique_lock lk(device->m_initMutex); + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + device->m_devImp.initialCycle(); + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); + + } + + static void _threadProcHID(CHIDDeviceWinUSB* device) + { + std::unique_lock lk(device->m_initMutex); + + /* Return control to main thread */ + device->m_runningTransferLoop = true; + lk.unlock(); + device->m_initCond.notify_one(); + + /* Start transfer loop */ + device->m_devImp.initialCycle(); + while (device->m_runningTransferLoop) + device->m_devImp.transferCycle(); + device->m_devImp.finalCycle(); + + } + + void _deviceDisconnected() + { + m_runningTransferLoop = false; + } + + bool _sendHIDReport(const uint8_t* data, size_t length) + { + return false; + } + +public: + + CHIDDeviceWinUSB(CDeviceToken& token, CDeviceBase& devImp) + : m_token(token), + m_devImp(devImp), + m_devPath(token.getDevicePath()) + { + devImp.m_hidDev = this; + std::unique_lock lk(m_initMutex); + CDeviceToken::TDeviceType dType = token.getDeviceType(); + if (dType == CDeviceToken::DEVTYPE_USB) + m_thread = new std::thread(_threadProcUSBLL, this); + else if (dType == CDeviceToken::DEVTYPE_BLUETOOTH) + m_thread = new std::thread(_threadProcBTLL, this); + else if (dType == CDeviceToken::DEVTYPE_GENERICHID) + m_thread = new std::thread(_threadProcHID, this); + else + throw std::runtime_error("invalid token supplied to device constructor"); + m_initCond.wait(lk); + } + + ~CHIDDeviceWinUSB() + { + m_runningTransferLoop = false; + m_thread->join(); + delete m_thread; + } + + +}; + +IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp) +{ + return new CHIDDeviceWinUSB(token, devImp); +} + +} diff --git a/src/inputdev/CHIDListenerWin32.cpp b/src/inputdev/CHIDListenerWin32.cpp deleted file mode 100644 index 7398249..0000000 --- a/src/inputdev/CHIDListenerWin32.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "inputdev/IHIDListener.hpp" -#include "inputdev/CDeviceFinder.hpp" -#include -#include - -#define _WIN32_LEAN_AND_MEAN 1 -#include - -namespace boo -{ - -#if 0 - -static udev* UDEV_INST = NULL; -udev* GetUdev() -{ - if (!UDEV_INST) - UDEV_INST = udev_new(); - return UDEV_INST; -} - -class CHIDListenerWin32 final : public IHIDListener -{ - CDeviceFinder& m_finder; - - udev_monitor* m_udevMon; - std::thread* m_udevThread; - bool m_udevRunning; - bool m_scanningEnabled; - - static void deviceConnected(CHIDListenerWin32* listener, - udev_device* device) - { - if (!listener->m_scanningEnabled) - return; - - /* Filter to USB/BT */ - const char* dt = udev_device_get_devtype(device); - CDeviceToken::TDeviceType type; - if (!strcmp(dt, "usb_device")) - type = CDeviceToken::DEVTYPE_USB; - else if (!strcmp(dt, "bluetooth_device")) - type = CDeviceToken::DEVTYPE_BLUETOOTH; - else - return; - - /* Prevent redundant registration */ - const char* devPath = udev_device_get_syspath(device); - if (listener->m_finder._hasToken(devPath)) - return; - - int vid = 0, pid = 0; - udev_list_entry* attrs = udev_device_get_properties_list_entry(device); -#if 0 - udev_list_entry* att = NULL; - udev_list_entry_foreach(att, attrs) - { - const char* name = udev_list_entry_get_name(att); - const char* val = udev_list_entry_get_value(att); - fprintf(stderr, "%s %s\n", name, val); - } - fprintf(stderr, "\n\n"); -#endif - - udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID"); - if (vide) - vid = strtol(udev_list_entry_get_value(vide), NULL, 16); - - udev_list_entry* pide = udev_list_entry_get_by_name(attrs, "ID_MODEL_ID"); - if (pide) - pid = strtol(udev_list_entry_get_value(pide), NULL, 16); - - const char* manuf = NULL; - udev_list_entry* manufe = udev_list_entry_get_by_name(attrs, "ID_VENDOR"); - if (manufe) - manuf = udev_list_entry_get_value(manufe); - - const char* product = NULL; - udev_list_entry* producte = udev_list_entry_get_by_name(attrs, "ID_MODEL"); - if (producte) - product = udev_list_entry_get_value(producte); - - if (!listener->m_finder._insertToken(CDeviceToken(type, vid, pid, manuf, product, devPath))) - { - /* Matched-insertion failed; see if generic HID interface is available */ - udev_list_entry* devInterfaces = NULL; - if (type == CDeviceToken::DEVTYPE_USB) - devInterfaces = udev_list_entry_get_by_name(attrs, "ID_USB_INTERFACES"); - else if (type == CDeviceToken::DEVTYPE_BLUETOOTH) - devInterfaces = udev_list_entry_get_by_name(attrs, "ID_BLUETOOTH_INTERFACES"); - if (devInterfaces) - { - const char* interfacesStr = udev_list_entry_get_value(devInterfaces); - if (strstr(interfacesStr, ":030104") || /* HID / GenericDesktop / Joystick */ - strstr(interfacesStr, ":030105")) /* HID / GenericDesktop / Gamepad */ - { - udev_enumerate* hidEnum = udev_enumerate_new(UDEV_INST); - udev_enumerate_add_match_parent(hidEnum, device); - udev_enumerate_add_match_subsystem(hidEnum, "hid"); - udev_enumerate_scan_devices(hidEnum); - udev_list_entry* hidEnt = udev_enumerate_get_list_entry(hidEnum); - if (hidEnt) - { - const char* hidPath = udev_list_entry_get_name(hidEnt); - if (!listener->m_finder._hasToken(hidPath)) - listener->m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_GENERICHID, - vid, pid, manuf, product, hidPath)); - } - udev_enumerate_unref(hidEnum); - } - } - } - } - - static void deviceDisconnected(CHIDListenerWin32* listener, - udev_device* device) - { - const char* devPath = udev_device_get_syspath(device); - listener->m_finder._removeToken(devPath); - } - - static void _udevProc(CHIDListenerWin32* listener) - { - udev_monitor_enable_receiving(listener->m_udevMon); - int fd = udev_monitor_get_fd(listener->m_udevMon); - while (listener->m_udevRunning) - { - fd_set fds; - FD_ZERO(&fds); - FD_SET(fd, &fds); - select(fd+1, &fds, NULL, NULL, NULL); - udev_device* dev = udev_monitor_receive_device(listener->m_udevMon); - if (dev) - { - const char* action = udev_device_get_action(dev); - if (!strcmp(action, "add")) - deviceConnected(listener, dev); - else if (!strcmp(action, "remove")) - deviceDisconnected(listener, dev); - udev_device_unref(dev); - } - } - } - -public: - CHIDListenerWin32(CDeviceFinder& finder) - : m_finder(finder) - { - - /* Setup hotplug events */ - m_udevMon = udev_monitor_new_from_netlink(GetUdev(), "udev"); - if (!m_udevMon) - throw std::runtime_error("unable to init udev_monitor"); - udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "usb", "usb_device"); - udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "bluetooth", "bluetooth_device"); - udev_monitor_filter_update(m_udevMon); - - /* Initial HID Device Add */ - m_scanningEnabled = true; - scanNow(); - m_scanningEnabled = false; - - /* Start hotplug thread */ - m_udevRunning = true; - m_udevThread = new std::thread(_udevProc, this); - - } - - ~CHIDListenerWin32() - { - m_udevRunning = false; - m_udevThread->join(); - delete m_udevThread; - udev_monitor_unref(m_udevMon); - } - - /* Automatic device scanning */ - bool startScanning() - { - m_scanningEnabled = true; - return true; - } - bool stopScanning() - { - m_scanningEnabled = false; - return true; - } - - /* Manual device scanning */ - bool scanNow() - { - udev_enumerate* uenum = udev_enumerate_new(GetUdev()); - udev_enumerate_add_match_subsystem(uenum, "usb"); - udev_enumerate_add_match_property(uenum, "DEVTYPE", "usb_device"); - udev_enumerate_add_match_subsystem(uenum, "bluetooth"); - udev_enumerate_add_match_property(uenum, "DEVTYPE", "bluetooth_device"); - udev_enumerate_scan_devices(uenum); - udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum); - udev_list_entry* uenumItem; - udev_list_entry_foreach(uenumItem, uenumList) - { - const char* devPath = udev_list_entry_get_name(uenumItem); - udev_device* dev = udev_device_new_from_syspath(UDEV_INST, devPath); - if (dev) - deviceConnected(this, dev); - udev_device_unref(dev); - } - udev_enumerate_unref(uenum); - return true; - } - -}; - -#endif - -IHIDListener* IHIDListenerNew(CDeviceFinder& finder) -{ - // return new CHIDListenerWin32(finder); - return nullptr; -} - -} diff --git a/src/inputdev/CHIDListenerWinUSB.cpp b/src/inputdev/CHIDListenerWinUSB.cpp new file mode 100644 index 0000000..667b81c --- /dev/null +++ b/src/inputdev/CHIDListenerWinUSB.cpp @@ -0,0 +1,203 @@ +#define _CRT_SECURE_NO_WARNINGS 1 /* STFU MSVC */ +#include "inputdev/IHIDListener.hpp" +#include "inputdev/CDeviceFinder.hpp" +#include +#include + +#define _WIN32_LEAN_AND_MEAN 1 +#include + +#include +#include +#include +#include +#include + +namespace boo +{ + +class CHIDListenerWinUSB final : public IHIDListener +{ + CDeviceFinder& m_finder; + + std::thread* m_setupThread; + bool m_setupRunning; + bool m_scanningEnabled; + + /* + * Reference: https://github.com/pbatard/libwdi/blob/master/libwdi/libwdi.c + */ + + void _pollDevices() + { + + /* Don't ask */ + static const LPCSTR arPrefix[3] = {"VID_", "PID_", "MI_"}; + unsigned i, j; + CONFIGRET r; + ULONG devpropType; + DWORD reg_type; + HDEVINFO hDevInfo = 0; + SP_DEVINFO_DATA DeviceInfoData = {0}; + DeviceInfoData.cbSize = sizeof(DeviceInfoData); + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = {0}; + DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData); + union { + SP_DEVICE_INTERFACE_DETAIL_DATA_A wtf; + CHAR alloc[2048]; + } DeviceInterfaceDetailData; /* Stack allocation should be fine for this */ + DeviceInterfaceDetailData.wtf.cbSize = sizeof(DeviceInterfaceDetailData); + CHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN]; + LPSTR pszToken, pszNextToken; + CHAR szVid[MAX_DEVICE_ID_LEN], szPid[MAX_DEVICE_ID_LEN], szMi[MAX_DEVICE_ID_LEN]; + + /* List all connected USB devices */ + hDevInfo = SetupDiGetClassDevs(NULL, 0, 0, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE); + if (hDevInfo == INVALID_HANDLE_VALUE) + return; + + for (i=0 ; ; ++i) + { + + if (!SetupDiEnumDeviceInterfaces(hDevInfo, + NULL, + &GUID_DEVINTERFACE_USB_DEVICE, + i, + &DeviceInterfaceData)) + break; + + DeviceInterfaceDetailData.wtf.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + if (!SetupDiGetDeviceInterfaceDetailA(hDevInfo, + &DeviceInterfaceData, + &DeviceInterfaceDetailData.wtf, + sizeof(DeviceInterfaceDetailData), + NULL, + &DeviceInfoData)) + continue; + + r = CM_Get_Device_IDA(DeviceInfoData.DevInst, szDeviceInstanceID , MAX_PATH, 0); + if (r != CR_SUCCESS) + continue; + + /* Retreive the device description as reported by the device itself */ + pszToken = strtok_s(szDeviceInstanceID , "\\#&", &pszNextToken); + szVid[0] = '\0'; + szPid[0] = '\0'; + szMi[0] = '\0'; + while (pszToken != NULL) + { + for (j=0 ; j<3 ; ++j) + { + if (strncmp(pszToken, arPrefix[j], 4) == 0) + { + switch (j) + { + case 0: + strcpy_s(szVid, MAX_DEVICE_ID_LEN, pszToken); + break; + case 1: + strcpy_s(szPid, MAX_DEVICE_ID_LEN, pszToken); + break; + case 2: + strcpy_s(szMi, MAX_DEVICE_ID_LEN, pszToken); + break; + default: + break; + } + } + } + pszToken = strtok_s(NULL, "\\#&", &pszNextToken); + } + + if (!szVid[0] || !szPid[0]) + continue; + + unsigned vid = strtol(szVid+4, NULL, 16); + unsigned pid = strtol(szPid+4, NULL, 16); + + WCHAR productW[1024] = {0}; + CHAR product[1024] = {0}; + DWORD productSz = 0; + if (!SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, + &devpropType, (BYTE*)productW, 1024, &productSz, 0)) { + /* fallback to SPDRP_DEVICEDESC (USB hubs still use it) */ + SetupDiGetDeviceRegistryPropertyA(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC, + ®_type, (BYTE*)productW, 1024, &productSz); + } + wcstombs(product, productW, productSz / 2); + + WCHAR manufW[1024] = L"Someone"; /* Windows Vista and earlier will use this as the vendor */ + CHAR manuf[1024] = {0}; + DWORD manufSz = 0; + SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_Manufacturer, + &devpropType, (BYTE*)manufW, 1024, &manufSz, 0); + wcstombs(manuf, manufW, manufSz / 2); + + /* Whew!! that's a single device enumerated!! */ + if (!m_finder._hasToken(DeviceInterfaceDetailData.wtf.DevicePath)) + m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_USB, + vid, pid, manuf, product, + DeviceInterfaceDetailData.wtf.DevicePath)); + + } + + SetupDiDestroyDeviceInfoList(hDevInfo); + + } + + static void _setupProc(CHIDListenerWinUSB* listener) + { + while (listener->m_setupRunning) + { + if (listener->m_scanningEnabled) + listener->_pollDevices(); + + /* Due to NT derpiness, this needs to be a periodic poll */ + Sleep(1000); + } + } + +public: + CHIDListenerWinUSB(CDeviceFinder& finder) + : m_finder(finder) + { + + /* Initial HID Device Add */ + _pollDevices(); + + } + + ~CHIDListenerWinUSB() + { + m_setupRunning = false; + m_setupThread->join(); + delete m_setupThread; + } + + /* Automatic device scanning */ + bool startScanning() + { + m_scanningEnabled = true; + return true; + } + bool stopScanning() + { + m_scanningEnabled = false; + return true; + } + + /* Manual device scanning */ + bool scanNow() + { + _pollDevices(); + return true; + } + +}; + +IHIDListener* IHIDListenerNew(CDeviceFinder& finder) +{ + return new CHIDListenerWinUSB(finder); +} + +} diff --git a/test/Win32Elevatedlauncher.c b/test/Win32Elevatedlauncher.c deleted file mode 100644 index 3f4445d..0000000 --- a/test/Win32Elevatedlauncher.c +++ /dev/null @@ -1,37 +0,0 @@ - -#define _WIN32_LEAN_AND_MEAN 1 -#include -#include - -int genWin32ShellExecute(const wchar_t* AppFullPath, - const wchar_t* Verb, - const wchar_t* Params, - bool ShowAppWindow, - bool WaitToFinish) -{ - int Result = 0; - - // Setup the required structure - SHELLEXECUTEINFO ShExecInfo; - memset(&ShExecInfo, 0, sizeof(SHELLEXECUTEINFO)); - ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); - ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; - ShExecInfo.hwnd = NULL; - ShExecInfo.lpVerb = Verb; - ShExecInfo.lpFile = AppFullPath; - ShExecInfo.lpParameters = Params; - ShExecInfo.lpDirectory = NULL; - ShExecInfo.nShow = (ShowAppWindow ? SW_SHOW : SW_HIDE); - ShExecInfo.hInstApp = NULL; - - // Spawn the process - if (ShellExecuteEx(&ShExecInfo) == FALSE) - { - Result = -1; // Failed to execute process - } else if (WaitToFinish) - { - WaitForSingleObject(ShExecInfo.hProcess, INFINITE); - } - - return Result; -} diff --git a/test/main.cpp b/test/main.cpp index faa1202..ba16599 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -8,6 +8,9 @@ #if _WIN32 #define _WIN32_LEAN_AND_MEAN 1 #include +#include +#include +#include #else #include #endif @@ -58,70 +61,8 @@ public: } -#if _WIN32 -static int genWin32ShellExecute(const wchar_t* AppFullPath, - const wchar_t* Verb, - const wchar_t* Params, - bool ShowAppWindow, - bool WaitToFinish) -{ - int Result = 0; - - // Setup the required structure - SHELLEXECUTEINFO ShExecInfo; - memset(&ShExecInfo, 0, sizeof(SHELLEXECUTEINFO)); - ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); - ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; - ShExecInfo.hwnd = NULL; - ShExecInfo.lpVerb = Verb; - ShExecInfo.lpFile = AppFullPath; - ShExecInfo.lpParameters = Params; - ShExecInfo.lpDirectory = NULL; - ShExecInfo.nShow = (ShowAppWindow ? SW_SHOW : SW_HIDE); - ShExecInfo.hInstApp = NULL; - - // Spawn the process - if (ShellExecuteEx(&ShExecInfo) == FALSE) - { - Result = -1; // Failed to execute process - } else if (WaitToFinish) - { - WaitForSingleObject(ShExecInfo.hProcess, INFINITE); - } - - return Result; -} - -#include -static void scanWinUSB() -{ - struct wdi_device_info *device, *list; - struct wdi_options_create_list WDI_LIST_OPTS = - { - true, false, true - }; - int err = wdi_create_list(&list, &WDI_LIST_OPTS); - if (err == WDI_SUCCESS) - { - for (device = list; device != NULL; device = device->next) - { - if (device->vid == 0x57E && device->pid == 0x337 && - !strcmp(device->driver, "HidUsb")) - { - printf("GC adapter detected; installing driver\n"); - genWin32ShellExecute(L"WinUsbInstaller.exe", L"", L"", false, true); - } - } - wdi_destroy_list(list); - } -} -#endif - int main(int argc, char** argv) { -#if _WIN32 - scanWinUSB(); -#endif boo::CTestDeviceFinder finder; finder.startScanning(); @@ -137,7 +78,35 @@ int main(int argc, char** argv) #if __APPLE__ CFRunLoopRun(); #elif _WIN32 - while (true) {Sleep(1000);} + + /* Register hotplug notification with windows */ + DEV_BROADCAST_DEVICEINTERFACE_A hotplugConf = + { + sizeof(DEV_BROADCAST_DEVICEINTERFACE_A), + DBT_DEVTYP_DEVICEINTERFACE, + 0, + GUID_DEVINTERFACE_USB_DEVICE + }; + HWND consoleWnd = GetConsoleWindow(); + HDEVNOTIFY notHandle = RegisterDeviceNotificationA(consoleWnd, &hotplugConf, DEVICE_NOTIFY_WINDOW_HANDLE); + + MSG recvMsg; + while (GetMessage(&recvMsg, consoleWnd, 0, 0)) + { + printf("MSG: %d\n", recvMsg.message); + switch (recvMsg.message) + { + case WM_DEVICECHANGE: + printf("DEVICECHANGE!!\n"); + break; + + default: + TranslateMessage(&recvMsg); + DispatchMessage(&recvMsg); + break; + } + } + #else while (true) {sleep(1);} #endif diff --git a/test/test.pri b/test/test.pri index 07e0ac2..872e5fb 100644 --- a/test/test.pri +++ b/test/test.pri @@ -1,5 +1,4 @@ SOURCES += \ - $$PWD/main.cpp \ - $$PWD/Win32Elevatedlauncher.c + $$PWD/main.cpp win32:SOURCES += From 4d1ec9f94b9e0308e16536ca90b847f0a5ab4f7f Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sun, 3 May 2015 18:28:07 -1000 Subject: [PATCH 16/17] WinUSB API implemented!! --- include/inputdev/CDeviceFinder.hpp | 47 +++++++++++ include/inputdev/IHIDListener.hpp | 6 ++ libBoo.pro | 2 +- src/inputdev/CDolphinSmashAdapter.cpp | 12 +-- src/inputdev/CHIDDeviceWinUSB.cpp | 42 ++++++++-- src/inputdev/CHIDListenerWinUSB.cpp | 54 ++++++------ src/inputdev/IHIDDevice.hpp | 2 +- test/main.cpp | 116 ++++++++++++++++++-------- 8 files changed, 210 insertions(+), 71 deletions(-) diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index 7878d01..ec68a78 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -9,6 +9,12 @@ #include "SDeviceSignature.hpp" #include +#ifdef _WIN32 +#define _WIN32_LEAN_AND_MEAN 1 +#include +#include +#endif + namespace boo { @@ -146,6 +152,47 @@ public: virtual void deviceConnected(CDeviceToken&) {} virtual void deviceDisconnected(CDeviceToken&, CDeviceBase*) {} + +#if _WIN32 + /* Windows-specific WM_DEVICECHANGED handler */ + static LRESULT winDevChangedHandler(WPARAM wParam, LPARAM lParam) + { + PDEV_BROADCAST_HDR dbh = (PDEV_BROADCAST_HDR)lParam; + PDEV_BROADCAST_DEVICEINTERFACE dbhi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam; + CDeviceFinder* finder = instance(); + if (!finder) + return 0; + + if (wParam == DBT_DEVICEARRIVAL) + { + if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + { +#ifdef UNICODE + char devPath[1024]; + wcstombs(devPath, dbhi->dbcc_name, 1024); + finder->m_listener->_extDevConnect(devPath); +#else + finder->m_listener->_extDevConnect(dbhi->dbcc_name); +#endif + } + } + else if (wParam == DBT_DEVICEREMOVECOMPLETE) + { + if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + { +#ifdef UNICODE + char devPath[1024]; + wcstombs(devPath, dbhi->dbcc_name, 1024); + finder->m_listener->_extDevDisconnect(devPath); +#else + finder->m_listener->_extDevDisconnect(dbhi->dbcc_name); +#endif + } + } + + return 0; + } +#endif }; diff --git a/include/inputdev/IHIDListener.hpp b/include/inputdev/IHIDListener.hpp index edf1549..a1a26ce 100644 --- a/include/inputdev/IHIDListener.hpp +++ b/include/inputdev/IHIDListener.hpp @@ -23,6 +23,12 @@ public: /* Manual device scanning */ virtual bool scanNow()=0; + +#if _WIN32 + /* External listener implementation (for Windows) */ + virtual bool _extDevConnect(const char* path)=0; + virtual bool _extDevDisconnect(const char* path)=0; +#endif }; diff --git a/libBoo.pro b/libBoo.pro index 496ad73..849f483 100644 --- a/libBoo.pro +++ b/libBoo.pro @@ -5,7 +5,7 @@ CONFIG += console unix:QMAKE_CXXFLAGS += -stdlib=libc++ unix:LIBS += -std=c++11 -stdlib=libc++ -lc++abi -win32:LIBS += Setupapi.lib winusb.lib User32.lib +win32:LIBS += Setupapi.lib winusb.lib User32.lib /SUBSYSTEM:Windows #unix:!macx:CONFIG += link_pkgconfig #unix:!macx:PKGCONFIG += x11 diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index 4e5e840..ab5106e 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -56,6 +56,12 @@ parseState(SDolphinControllerState* stateOut, uint8_t* payload, bool& rumble) return type; } +void CDolphinSmashAdapter::initialCycle() +{ + uint8_t handshakePayload[] = {0x13}; + sendUSBInterruptTransfer(0, handshakePayload, sizeof(handshakePayload)); +} + void CDolphinSmashAdapter::transferCycle() { uint8_t payload[37]; @@ -107,12 +113,6 @@ void CDolphinSmashAdapter::transferCycle() } } -void CDolphinSmashAdapter::initialCycle() -{ - uint8_t handshakePayload[] = {0x13}; - sendUSBInterruptTransfer(0, handshakePayload, sizeof(handshakePayload)); -} - void CDolphinSmashAdapter::finalCycle() { uint8_t rumbleMessage[5] = {0x11, 0, 0, 0, 0}; diff --git a/src/inputdev/CHIDDeviceWinUSB.cpp b/src/inputdev/CHIDDeviceWinUSB.cpp index f1811ed..d419140 100644 --- a/src/inputdev/CHIDDeviceWinUSB.cpp +++ b/src/inputdev/CHIDDeviceWinUSB.cpp @@ -11,6 +11,8 @@ #define _WIN32_LEAN_AND_MEAN 1 #include #include +#include +#include namespace boo { @@ -37,8 +39,8 @@ class CHIDDeviceWinUSB final : public IHIDDevice { ULONG lengthTransferred = 0; if (!WinUsb_WritePipe(m_usbHandle, m_usbIntfOutPipe, (PUCHAR)data, - (ULONG)length, &lengthTransferred, NULL) - || lengthTransferred != length) + (ULONG)length, &lengthTransferred, NULL) || + lengthTransferred != length) return false; return true; } @@ -72,20 +74,22 @@ class CHIDDeviceWinUSB final : public IHIDDevice OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); - if (INVALID_HANDLE_VALUE == device->m_devHandle) { + if (INVALID_HANDLE_VALUE == device->m_devHandle) + { _snprintf(errStr, 256, "Unable to open %s@%s: %d\n", device->m_token.getProductName().c_str(), - device->m_devPath, GetLastError()); + device->m_devPath.c_str(), GetLastError()); device->m_devImp.deviceError(errStr); lk.unlock(); device->m_initCond.notify_one(); return; } - if (!WinUsb_Initialize(device->m_devHandle, &device->m_usbHandle)) { + if (!WinUsb_Initialize(device->m_devHandle, &device->m_usbHandle)) + { _snprintf(errStr, 256, "Unable to open %s@%s: %d\n", device->m_token.getProductName().c_str(), - device->m_devPath, GetLastError()); + device->m_devPath.c_str(), GetLastError()); device->m_devImp.deviceError(errStr); lk.unlock(); device->m_initCond.notify_one(); @@ -93,6 +97,32 @@ class CHIDDeviceWinUSB final : public IHIDDevice return; } + /* Enumerate device pipes */ + USB_INTERFACE_DESCRIPTOR ifDesc = {0}; + if (!WinUsb_QueryInterfaceSettings(device->m_usbHandle, 0, &ifDesc)) + { + _snprintf(errStr, 256, "Unable to open %s@%s: %d\n", + device->m_token.getProductName().c_str(), + device->m_devPath.c_str(), GetLastError()); + device->m_devImp.deviceError(errStr); + lk.unlock(); + device->m_initCond.notify_one(); + CloseHandle(device->m_devHandle); + return; + } + for (i=0 ; im_usbHandle, 0, i, &pipeDesc); + if (pipeDesc.PipeType == UsbdPipeTypeInterrupt) + { + if (USB_ENDPOINT_DIRECTION_IN(pipeDesc.PipeId)) + device->m_usbIntfInPipe = pipeDesc.PipeId; + else + device->m_usbIntfOutPipe = pipeDesc.PipeId; + } + } + /* Return control to main thread */ device->m_runningTransferLoop = true; lk.unlock(); diff --git a/src/inputdev/CHIDListenerWinUSB.cpp b/src/inputdev/CHIDListenerWinUSB.cpp index 667b81c..e12084d 100644 --- a/src/inputdev/CHIDListenerWinUSB.cpp +++ b/src/inputdev/CHIDListenerWinUSB.cpp @@ -20,15 +20,13 @@ class CHIDListenerWinUSB final : public IHIDListener { CDeviceFinder& m_finder; - std::thread* m_setupThread; - bool m_setupRunning; bool m_scanningEnabled; /* * Reference: https://github.com/pbatard/libwdi/blob/master/libwdi/libwdi.c */ - void _pollDevices() + void _pollDevices(const char* pathFilter) { /* Don't ask */ @@ -133,6 +131,13 @@ class CHIDListenerWinUSB final : public IHIDListener &devpropType, (BYTE*)manufW, 1024, &manufSz, 0); wcstombs(manuf, manufW, manufSz / 2); + /* Store as a shouting string (to keep hash-lookups consistent) */ + CharUpperA(DeviceInterfaceDetailData.wtf.DevicePath); + + /* Filter to specific device (provided by hotplug event) */ + if (pathFilter && strcmp(pathFilter, DeviceInterfaceDetailData.wtf.DevicePath)) + continue; + /* Whew!! that's a single device enumerated!! */ if (!m_finder._hasToken(DeviceInterfaceDetailData.wtf.DevicePath)) m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_USB, @@ -145,34 +150,16 @@ class CHIDListenerWinUSB final : public IHIDListener } - static void _setupProc(CHIDListenerWinUSB* listener) - { - while (listener->m_setupRunning) - { - if (listener->m_scanningEnabled) - listener->_pollDevices(); - - /* Due to NT derpiness, this needs to be a periodic poll */ - Sleep(1000); - } - } - public: CHIDListenerWinUSB(CDeviceFinder& finder) : m_finder(finder) { - /* Initial HID Device Add */ - _pollDevices(); - + _pollDevices(NULL); } ~CHIDListenerWinUSB() - { - m_setupRunning = false; - m_setupThread->join(); - delete m_setupThread; - } + {} /* Automatic device scanning */ bool startScanning() @@ -189,7 +176,26 @@ public: /* Manual device scanning */ bool scanNow() { - _pollDevices(); + _pollDevices(NULL); + return true; + } + + bool _extDevConnect(const char* path) + { + char upperPath[1024]; + strcpy_s(upperPath, 1024, path); + CharUpperA(upperPath); + if (m_scanningEnabled && !m_finder._hasToken(upperPath)) + _pollDevices(upperPath); + return true; + } + + bool _extDevDisconnect(const char* path) + { + char upperPath[1024]; + strcpy_s(upperPath, 1024, path); + CharUpperA(upperPath); + m_finder._removeToken(upperPath); return true; } diff --git a/src/inputdev/IHIDDevice.hpp b/src/inputdev/IHIDDevice.hpp index e65d311..99ca399 100644 --- a/src/inputdev/IHIDDevice.hpp +++ b/src/inputdev/IHIDDevice.hpp @@ -14,7 +14,7 @@ class IHIDDevice virtual size_t _receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)=0; virtual bool _sendHIDReport(const uint8_t* data, size_t length)=0; public: - inline virtual ~IHIDDevice() {}; + inline virtual ~IHIDDevice() {} }; } diff --git a/test/main.cpp b/test/main.cpp index ba16599..79e4b27 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,3 +1,4 @@ +#define _CRT_SECURE_NO_WARNINGS 1 #if __APPLE__ #include @@ -10,7 +11,6 @@ #include #include #include -#include #else #include #endif @@ -61,6 +61,86 @@ public: } +#if _WIN32 + +/* This simple 'test' console app needs a full windows + * message loop for receiving device connection events + */ +static const DEV_BROADCAST_DEVICEINTERFACE_A HOTPLUG_CONF = +{ + sizeof(DEV_BROADCAST_DEVICEINTERFACE_A), + DBT_DEVTYP_DEVICEINTERFACE, + 0, + GUID_DEVINTERFACE_USB_DEVICE +}; + +LRESULT CALLBACK WindowProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam +) +{ + switch (uMsg) + { + case WM_CREATE: + /* Register hotplug notification with windows */ + RegisterDeviceNotificationA(hwnd, (LPVOID)&HOTPLUG_CONF, DEVICE_NOTIFY_WINDOW_HANDLE); + return 0; + + case WM_DEVICECHANGE: + return boo::CDeviceFinder::winDevChangedHandler(wParam, lParam); + + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } +} + +int APIENTRY wWinMain( + _In_ HINSTANCE hInstance, + _In_ HINSTANCE, + _In_ LPTSTR, + _In_ int +) +{ + AllocConsole(); + freopen("CONOUT$", "w", stdout); + + WNDCLASS wndClass = + { + 0, + WindowProc, + 0, + 0, + hInstance, + 0, + 0, + 0, + 0, + L"BooTestWindow" + }; + + RegisterClassW(&wndClass); + + boo::CTestDeviceFinder finder; + finder.startScanning(); + + HWND hwnd = CreateWindowW(L"BooTestWindow", L"BooTest", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, hInstance, NULL); + + /* Pump messages */ + MSG msg = {0}; + while (GetMessage(&msg, hwnd, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + +} + +#else + int main(int argc, char** argv) { @@ -77,40 +157,10 @@ int main(int argc, char** argv) #if __APPLE__ CFRunLoopRun(); -#elif _WIN32 - - /* Register hotplug notification with windows */ - DEV_BROADCAST_DEVICEINTERFACE_A hotplugConf = - { - sizeof(DEV_BROADCAST_DEVICEINTERFACE_A), - DBT_DEVTYP_DEVICEINTERFACE, - 0, - GUID_DEVINTERFACE_USB_DEVICE - }; - HWND consoleWnd = GetConsoleWindow(); - HDEVNOTIFY notHandle = RegisterDeviceNotificationA(consoleWnd, &hotplugConf, DEVICE_NOTIFY_WINDOW_HANDLE); - - MSG recvMsg; - while (GetMessage(&recvMsg, consoleWnd, 0, 0)) - { - printf("MSG: %d\n", recvMsg.message); - switch (recvMsg.message) - { - case WM_DEVICECHANGE: - printf("DEVICECHANGE!!\n"); - break; - - default: - TranslateMessage(&recvMsg); - DispatchMessage(&recvMsg); - break; - } - } - -#else - while (true) {sleep(1);} #endif //delete ctx; return 0; } + +#endif From ff04fbc5ce5b51ed5696ea842fffeca9673877c9 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sun, 3 May 2015 19:18:52 -1000 Subject: [PATCH 17/17] brought linux in sync from windows implementation --- libBoo.pri | 8 ++++---- libBoo.pro | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libBoo.pri b/libBoo.pri index 66e92a2..f589cd3 100644 --- a/libBoo.pri +++ b/libBoo.pri @@ -31,9 +31,7 @@ SOURCES += \ $$PWD/src/inputdev/CDualshockPad.cpp \ $$PWD/src/inputdev/CGenericPad.cpp \ $$PWD/src/inputdev/CDeviceBase.cpp \ - $$PWD/src/inputdev/SDeviceSignature.cpp \ - $$PWD/src/inputdev/CHIDListenerWinUSB.cpp \ - $$PWD/src/inputdev/CHIDDeviceWinUSB.cpp + $$PWD/src/inputdev/SDeviceSignature.cpp unix:!macx { HEADERS += \ @@ -68,7 +66,9 @@ win32 { HEADERS += \ $$PWD/include/win/CWGLContext.hpp SOURCES += \ - $$PWD/src/win/CWGLContext.cpp + $$PWD/src/win/CWGLContext.cpp \ + $$PWD/src/inputdev/CHIDListenerWinUSB.cpp \ + $$PWD/src/inputdev/CHIDDeviceWinUSB.cpp } INCLUDEPATH += $$PWD/include diff --git a/libBoo.pro b/libBoo.pro index 849f483..f26f9eb 100644 --- a/libBoo.pro +++ b/libBoo.pro @@ -2,7 +2,7 @@ CONFIG -= Qt CONFIG += console #QMAKE_CXXFLAGS -= -std=c++0x #CONFIG += c++11 -unix:QMAKE_CXXFLAGS += -stdlib=libc++ +unix:QMAKE_CXXFLAGS += -std=c++11 -stdlib=libc++ unix:LIBS += -std=c++11 -stdlib=libc++ -lc++abi win32:LIBS += Setupapi.lib winusb.lib User32.lib /SUBSYSTEM:Windows