如果设备自引导后插入,IOHIDDeviceRegisterInputValueCallback将失败

时间:2017-12-01 18:07:23

标签: macos cocoa usb hid iokit

我尝试使用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

1 个答案:

答案 0 :(得分:-1)

在启动过程中看起来操作系统会自动卸载设备(因为未使用)

当您断开连接(并再次插入)设备时,操作系统将加载设备......

documentation开始,根据驱动程序开发中使用的命令行工具,您可以使用:

kextload
  

加载内核扩展(例如设备驱动程序)或生成   用于远程调试的静态链接符号文件。

kextunload
  

卸载内核扩展(如果可能)。

可能有助于你的另一件事:

  

驱动程序加载

     

在所有驱动程序探测到设备后,将附加具有最高探测分数的驱动程序,并调用其必须由所有驱动程序实现的startfunction。 start函数初始化设备硬件并准备运行。如果驱动程序成功启动,则返回true;丢弃剩余的候选驱动程序实例,并且成功启动的驱动程序继续运行。 如果驱动程序无法初始化硬件,则必须使硬件处于调用start时所处的状态并返回false。然后分离并丢弃失败的驱动程序,并使用下一个最高的候选驱动程序探究得分有机会开始。

     

发生这种情况后的一段时间,将卸载当前未使用的所有已加载驱动程序。

总结this page也可能对您有所帮助