mirror of
https://github.com/AxioDL/boo.git
synced 2025-12-14 15:46:19 +00:00
IOKit input refactor
This commit is contained in:
@@ -2,9 +2,13 @@
|
||||
#include "boo/inputdev/DeviceFinder.hpp"
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/hid/IOHIDLib.h>
|
||||
#include <IOKit/hid/IOHIDDevicePlugin.h>
|
||||
#include <IOKit/hid/IOHIDKeys.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include "IOKitPointer.hpp"
|
||||
#include "CFPointer.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
@@ -13,7 +17,7 @@ namespace boo
|
||||
* Reference: http://oroboro.com/usb-serial-number-osx/
|
||||
*/
|
||||
|
||||
static bool getUSBStringDescriptor(IOUSBDeviceInterface182** usbDevice, UInt8 idx, char* out)
|
||||
static bool getUSBStringDescriptor(const IUnknownPointer<IOUSBDeviceInterface182>& usbDevice, UInt8 idx, char* out)
|
||||
{
|
||||
UInt16 buffer[128];
|
||||
|
||||
@@ -30,7 +34,7 @@ static bool getUSBStringDescriptor(IOUSBDeviceInterface182** usbDevice, UInt8 id
|
||||
request.wLength = sizeof(buffer);
|
||||
request.pData = buffer;
|
||||
|
||||
kern_return_t err = (*usbDevice)->DeviceRequest(usbDevice, &request);
|
||||
kern_return_t err = usbDevice->DeviceRequest(usbDevice.storage(), &request);
|
||||
if (err != 0)
|
||||
{
|
||||
// the request failed... fairly uncommon for the USB disk driver, but not
|
||||
@@ -61,72 +65,65 @@ class HIDListenerIOKit : public IHIDListener
|
||||
|
||||
CFRunLoopRef m_listenerRunLoop;
|
||||
IONotificationPortRef m_llPort;
|
||||
io_iterator_t m_llAddNotif, m_llRemoveNotif;
|
||||
IOObjectPointer<io_iterator_t> m_llAddNotif, m_llRemoveNotif;
|
||||
IOObjectPointer<io_iterator_t> m_hidAddNotif, m_hidRemoveNotif;
|
||||
bool m_scanningEnabled;
|
||||
|
||||
static void devicesConnectedUSBLL(HIDListenerIOKit* listener,
|
||||
io_iterator_t iterator)
|
||||
{
|
||||
io_object_t obj;
|
||||
while ((obj = IOIteratorNext(iterator)))
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
|
||||
{
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0)
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
|
||||
if (!listener->m_scanningEnabled ||
|
||||
listener->m_finder._hasToken(devPath))
|
||||
{
|
||||
IOObjectRelease(obj);
|
||||
continue;
|
||||
}
|
||||
|
||||
IOCFPlugInInterface** devServ;
|
||||
SInt32 score;
|
||||
IOReturn err;
|
||||
err = IOCreatePlugInInterfaceForService(obj, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
fprintf(stderr, "unable to open IOKit plugin interface\n");
|
||||
return;
|
||||
}
|
||||
|
||||
IOUSBDeviceInterface182 **dev;
|
||||
err = (*devServ)->QueryInterface(devServ,
|
||||
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182),
|
||||
(LPVOID*)&dev);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
fprintf(stderr, "unable to open IOKit device interface\n");
|
||||
return;
|
||||
}
|
||||
|
||||
UInt16 vid, pid;
|
||||
(*dev)->GetDeviceVendor(dev, &vid);
|
||||
(*dev)->GetDeviceProduct(dev, &pid);
|
||||
|
||||
UInt8 vstridx, pstridx;
|
||||
(*dev)->USBGetManufacturerStringIndex(dev, &vstridx);
|
||||
(*dev)->USBGetProductStringIndex(dev, &pstridx);
|
||||
|
||||
char vstr[128] = {0};
|
||||
char pstr[128] = {0};
|
||||
getUSBStringDescriptor(dev, vstridx, vstr);
|
||||
getUSBStringDescriptor(dev, pstridx, pstr);
|
||||
{
|
||||
IOCFPluginPointer devServ;
|
||||
SInt32 score;
|
||||
IOReturn err;
|
||||
err = IOCreatePlugInInterfaceForService(obj.get(), kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
fprintf(stderr, "unable to open IOKit plugin interface\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!listener->m_finder._insertToken(DeviceToken(DeviceToken::DeviceType::USB,
|
||||
vid, pid, vstr, pstr, devPath)))
|
||||
IUnknownPointer<IOUSBDeviceInterface182> dev;
|
||||
err = devServ.As(&dev, kIOUSBDeviceInterfaceID182);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
fprintf(stderr, "unable to open IOKit device interface\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
dev->GetDeviceVendor(dev.storage(), &vid);
|
||||
dev->GetDeviceProduct(dev.storage(), &pid);
|
||||
|
||||
UInt8 vstridx, pstridx;
|
||||
dev->USBGetManufacturerStringIndex(dev.storage(), &vstridx);
|
||||
dev->USBGetProductStringIndex(dev.storage(), &pstridx);
|
||||
|
||||
getUSBStringDescriptor(dev, vstridx, vstr);
|
||||
getUSBStringDescriptor(dev, pstridx, pstr);
|
||||
}
|
||||
|
||||
if (!listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::USB,
|
||||
vid, pid, vstr, pstr, devPath)))
|
||||
{
|
||||
/* Matched-insertion failed; see if generic HID interface is available */
|
||||
/* TODO: Do */
|
||||
}
|
||||
|
||||
//printf("ADDED %08X %s\n", obj, devPath);
|
||||
(*dev)->Release(dev);
|
||||
IODestroyPlugInInterface(devServ);
|
||||
IOObjectRelease(obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void devicesDisconnectedUSBLL(HIDListenerIOKit* listener,
|
||||
@@ -140,15 +137,114 @@ class HIDListenerIOKit : public IHIDListener
|
||||
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
||||
return;
|
||||
}
|
||||
io_object_t obj;
|
||||
while ((obj = IOIteratorNext(iterator)))
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
|
||||
{
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0)
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
listener->m_finder._removeToken(devPath);
|
||||
//printf("REMOVED %08X %s\n", obj, devPath);
|
||||
}
|
||||
}
|
||||
|
||||
static void devicesConnectedHID(HIDListenerIOKit* listener,
|
||||
io_iterator_t iterator)
|
||||
{
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
|
||||
{
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
|
||||
if (!listener->m_scanningEnabled ||
|
||||
listener->m_finder._hasToken(devPath))
|
||||
continue;
|
||||
|
||||
unsigned vidv, pidv;
|
||||
char vstr[128] = {0};
|
||||
char pstr[128] = {0};
|
||||
{
|
||||
IOCFPluginPointer devServ;
|
||||
SInt32 score;
|
||||
IOReturn err;
|
||||
err = IOCreatePlugInInterfaceForService(obj.get(), kIOHIDDeviceTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
fprintf(stderr, "unable to open IOKit plugin interface\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
IUnknownPointer<IOHIDDeviceDeviceInterface> dev;
|
||||
err = devServ.As(&dev, kIOHIDDeviceDeviceInterfaceID);
|
||||
if (err != kIOReturnSuccess)
|
||||
{
|
||||
fprintf(stderr, "unable to open IOKit device interface\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Game controllers only */
|
||||
CFPointer<CFNumberRef> usagePage;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsagePageKey), &usagePage);
|
||||
CFPointer<CFNumberRef> usage;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsageKey), &usage);
|
||||
int usagePageV, usageV;
|
||||
CFNumberGetValue(usagePage.get(), kCFNumberIntType, &usagePageV);
|
||||
CFNumberGetValue(usage.get(), kCFNumberIntType, &usageV);
|
||||
if (usagePageV == kHIDPage_GenericDesktop)
|
||||
{
|
||||
if (usageV != kHIDUsage_GD_Joystick && usageV != kHIDUsage_GD_GamePad)
|
||||
continue;
|
||||
}
|
||||
else if (usagePageV != kHIDPage_Game)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CFPointer<CFNumberRef> vid, pid;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDVendorIDKey), &vid);
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductIDKey), &pid);
|
||||
CFNumberGetValue(vid.get(), kCFNumberIntType, &vidv);
|
||||
CFNumberGetValue(pid.get(), kCFNumberIntType, &pidv);
|
||||
|
||||
CFPointer<CFStringRef> vstridx, pstridx;
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDManufacturerKey), &vstridx);
|
||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductKey), &pstridx);
|
||||
|
||||
if (vstridx)
|
||||
CFStringGetCString(vstridx.get(), vstr, 128, kCFStringEncodingUTF8);
|
||||
if (pstridx)
|
||||
CFStringGetCString(pstridx.get(), pstr, 128, kCFStringEncodingUTF8);
|
||||
}
|
||||
|
||||
if (!listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::HID,
|
||||
vidv, pidv, vstr, pstr, devPath)))
|
||||
{
|
||||
/* Matched-insertion failed; see if generic HID interface is available */
|
||||
/* TODO: Do */
|
||||
}
|
||||
|
||||
//printf("ADDED %08X %s\n", obj, devPath);
|
||||
}
|
||||
}
|
||||
|
||||
static void devicesDisconnectedHID(HIDListenerIOKit* listener,
|
||||
io_iterator_t iterator)
|
||||
{
|
||||
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop)
|
||||
{
|
||||
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{
|
||||
devicesDisconnectedHID(listener, iterator);
|
||||
});
|
||||
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
||||
return;
|
||||
}
|
||||
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
|
||||
{
|
||||
io_string_t devPath;
|
||||
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
|
||||
continue;
|
||||
listener->m_finder._removeToken(devPath);
|
||||
//printf("REMOVED %08X %s\n", obj, devPath);
|
||||
IOObjectRelease(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,38 +252,54 @@ public:
|
||||
HIDListenerIOKit(DeviceFinder& finder)
|
||||
: m_finder(finder)
|
||||
{
|
||||
|
||||
/* Register Low-Level USB Matcher */
|
||||
m_listenerRunLoop = CFRunLoopGetCurrent();
|
||||
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
|
||||
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
|
||||
|
||||
CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName);
|
||||
CFRetain(matchDict);
|
||||
|
||||
m_scanningEnabled = true;
|
||||
kern_return_t llRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif);
|
||||
if (llRet == kIOReturnSuccess)
|
||||
devicesConnectedUSBLL(this, m_llAddNotif);
|
||||
|
||||
llRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif);
|
||||
if (llRet == kIOReturnSuccess)
|
||||
devicesDisconnectedUSBLL(this, m_llRemoveNotif);
|
||||
|
||||
/* Register HID Matcher */
|
||||
{
|
||||
CFMutableDictionaryRef matchDict = IOServiceMatching("IOHIDDevice");
|
||||
CFRetain(matchDict);
|
||||
|
||||
kern_return_t hidRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesConnectedHID, this, &m_hidAddNotif);
|
||||
if (hidRet == kIOReturnSuccess)
|
||||
devicesConnectedHID(this, m_hidAddNotif.get());
|
||||
|
||||
hidRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesDisconnectedHID, this, &m_hidRemoveNotif);
|
||||
if (hidRet == kIOReturnSuccess)
|
||||
devicesDisconnectedHID(this, m_hidRemoveNotif.get());
|
||||
}
|
||||
|
||||
/* Register Low-Level USB Matcher */
|
||||
{
|
||||
CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName);
|
||||
CFRetain(matchDict);
|
||||
|
||||
kern_return_t llRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif);
|
||||
if (llRet == kIOReturnSuccess)
|
||||
devicesConnectedUSBLL(this, m_llAddNotif.get());
|
||||
|
||||
llRet =
|
||||
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
|
||||
(IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif);
|
||||
if (llRet == kIOReturnSuccess)
|
||||
devicesDisconnectedUSBLL(this, m_llRemoveNotif.get());
|
||||
}
|
||||
|
||||
m_scanningEnabled = false;
|
||||
|
||||
}
|
||||
|
||||
~HIDListenerIOKit()
|
||||
{
|
||||
//CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
|
||||
IOObjectRelease(m_llAddNotif);
|
||||
IOObjectRelease(m_llRemoveNotif);
|
||||
IONotificationPortDestroy(m_llPort);
|
||||
}
|
||||
|
||||
@@ -206,12 +318,11 @@ public:
|
||||
/* Manual device scanning */
|
||||
bool scanNow()
|
||||
{
|
||||
io_iterator_t iter;
|
||||
IOObjectPointer<io_iterator_t> iter;
|
||||
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
|
||||
IOServiceMatching(kIOUSBDeviceClassName), &iter) == kIOReturnSuccess)
|
||||
{
|
||||
devicesConnectedUSBLL(this, iter);
|
||||
IOObjectRelease(iter);
|
||||
devicesConnectedUSBLL(this, iter.get());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user