我正在尝试使用iokit与游戏控制器进行通信,特别是我想通过IOHIDDeviceRegisterInputValueCallback
通知价值更改。我的代码有效,除了自Mac被启动并且从未插入的情况下已插入设备。在这种情况下,IOHIDDeviceOpen
仍然成功,但是价值回调从未被调用。而且,如果我尝试使用IOHIDDeviceGetValue
获取值,它没有报告任何错误,但是我得到的整数值都是零,这是不正确的。
如果我拔下设备并将其插入,则程序开始获取值回调。
我可以做些什么来解决这个问题,还是应该责怪硬件?
也许有某种方法可以在软件上进行拔下插头。内核框架参考列出了听起来像这样做的函数 ReEnumerateDevice
,但是如果从非内核代码可以使用,我将需要很多帮助。
static void ValueCallback(
void *context,
IOReturn result,
void *sender,
IOHIDValueRef value )
{
IOHIDElementRef theElement = IOHIDValueGetElement( value );
uint32_t usagePage = IOHIDElementGetUsagePage( theElement );
uint32_t usage = IOHIDElementGetUsage( theElement );
IOHIDElementCookie cookie = IOHIDElementGetCookie( theElement );
IOHIDElementType typeCode = IOHIDElementGetType( theElement );
CFIndex intValue = IOHIDValueGetIntegerValue( value );
double physValue = IOHIDValueGetScaledValue( value,
kIOHIDValueScaleTypePhysical );
double calibratedValue = IOHIDValueGetScaledValue( value,
kIOHIDValueScaleTypeCalibrated );
NSLog(@"Element %@ (0x%X, 0x%X, %p, type %d) changed to %d (%f, %f)",
theElement, (int)usagePage, (int)usage, cookie, (int) typeCode,
(int)intValue, physValue, calibratedValue );
}
static void DeviceMatchingCallback(
void *context,
IOReturn result,
void *sender,
IOHIDDeviceRef device )
{
NSLog( @"Added device %@", device );
IOHIDDeviceScheduleWithRunLoop( device, CFRunLoopGetMain(),
kCFRunLoopDefaultMode );
IOReturn err = IOHIDDeviceOpen( device, kIOHIDOptionsTypeNone );
NSLog(@"IOHIDDeviceOpen result 0x%08X", err );
IOHIDDeviceRegisterInputValueCallback( device, ValueCallback, context );
// Let's see if I can get elements and values.
CFArrayRef elementArray = IOHIDDeviceCopyMatchingElements( device,
NULL, 0 );
if ( elementArray != NULL )
{
NSArray* elArray = (NSArray*)elementArray;
for (id oneEl in elArray)
{
IOHIDElementRef anElement = (IOHIDElementRef) oneEl;
IOHIDElementType elType = IOHIDElementGetType( anElement );
NSLog(@"Element type %d", (int)elType);
if ( (elType == 1) || (elType == 2) || (elType == 3) )
{
IOHIDElementCookie theCookie =
IOHIDElementGetCookie( anElement );
CFIndex val = -1;
IOHIDValueRef valueRef = NULL;
err = IOHIDDeviceGetValue( device, anElement, &valueRef );
if (err == kIOReturnSuccess)
{
val = IOHIDValueGetIntegerValue( valueRef );
NSLog(@" cookie %p, value %ld", theCookie, val );
}
else
{
NSLog(@" cookie %p, error getting value 0x%08X",
theCookie, err );
}
}
}
}
}
static void DeviceRemovalCallback(
void *context,
IOReturn result,
void *sender,
IOHIDDeviceRef device )
{
NSLog( @"Removed device %@", device );
IOHIDDeviceUnscheduleFromRunLoop( device, CFRunLoopGetMain(),
kCFRunLoopDefaultMode );
}
@implementation AppDelegate
@synthesize window = _window;
- (void)dealloc
{
if (_hidManager != NULL)
{
CFRelease( _hidManager );
}
[super dealloc];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
_hidManager = IOHIDManagerCreate( NULL, 0 );
IOHIDManagerSetDeviceMatchingMultiple( _hidManager, (CFArrayRef) @[
@{ @(kIOHIDDeviceUsagePageKey): @(kHIDPage_GenericDesktop),
@(kIOHIDDeviceUsageKey): @(kHIDUsage_GD_Joystick)
},
@{ @(kIOHIDDeviceUsagePageKey): @(kHIDPage_GenericDesktop),
@(kIOHIDDeviceUsageKey): @(kHIDUsage_GD_GamePad)
}
] );
IOHIDManagerRegisterDeviceMatchingCallback( _hidManager,
DeviceMatchingCallback, self );
IOHIDManagerRegisterDeviceRemovalCallback( _hidManager,
DeviceRemovalCallback, self );
IOHIDManagerScheduleWithRunLoop( _hidManager, CFRunLoopGetMain(),
kCFRunLoopDefaultMode );
}
@end
启动过程中的操作系统看起来会自动卸载设备(因为未使用)
当您断开(并再次将其插入)设备时,操作系统将加载设备...
从文档中,根据驱动程序开发中使用的命令行工具,您可以使用:
kextload
加载内核扩展名(例如设备驱动程序)或生成一个 远程调试的静态链接符号文件。
和
kextunload
卸载内核扩展名(如果可能的话)。
可能对您有帮助的另一件事:
驱动程序加载
所有驱动程序都探测了该设备后,将附加了最高探头分数的设备,并且必须调用所有驱动程序的起始功能。启动功能将设备硬件初始化并为操作准备。如果驾驶员成功启动,它将返回真实;剩下的候选驾驶员实例被丢弃,开始成功运行的驾驶员。如果驱动程序无法初始化硬件,则必须将硬件留在启动时所在状态下并返回false。探测分数有机会开始。
发生在此之后的一段时间,当前未使用的所有加载驱动程序均已卸载。
结论一下此页面也可能对您有所帮助