title: Get callback using IOKit using an Interrupt Input Endpoint
body: <p>I'm trying to get a grasp on IOKit and I feel I am close, but not there yet. So excuse my confusion.</p>
  
  <p>I have managed to write code which detects my USB Device (a simple Button on the end of a USB cable which has a Windows Driver, but no Mac driver).</p>
  
  <p>I'm trying to get some sort of callback when the button is pressed.</p>
  
  <p>I manage to get a callback when the device is connected to USB or removed. Now, I am trying to find out how to get informed when the button is pressed, but I cannot figure it out. The documentation is very confusing to me, as IOKit seems to be available both a c++ and c depending on how you access it (Kernel Extension or user-space driver, or something similar. Not sure I have the right terminology.</p>
  
  <p>I've tried adding a couple methods to get a callback when any Interrupt value changes as you will see in the code. But nothing happens.</p>
  
  <p>Here is my current AppDelegate.m file as well as the USB Probe information on the device. </p>
  
  <pre><code>Low Speed device @ 5 (0x14100000): .............................................   Composite device: "DL100B Dream Cheeky Generic Controller"
  Port Information:   0x101a
  Number Of Endpoints (includes EP0):   
  Device Descriptor   
  Configuration Descriptor (current config)   
      Length (and contents):   34
      Number of Interfaces:   1
      Configuration Value:   1
      Attributes:   0x80 (bus-powered)
      MaxPower:   500 mA
      Interface #0 - HID   
          Alternate Setting   0
          Number of Endpoints   1
          Interface Class:   3   (HID)
          Interface Subclass;   0
          Interface Protocol:   0
          HID Descriptor   
          Endpoint 0x81 - Interrupt Input   
              Address:   0x81  (IN)
              Attributes:   0x03  (Interrupt)
              Max Packet Size:   8
              Polling Interval:   10 ms
  </code></pre>
  
  <p>The App Delegate.m file:</p>
  
  <pre><code>    //
  //  USBHIDAppDelegate.m
  //  USBHID
  //
  //  Created by Michael Dolinar on 12-05-02.
  //  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
  //
  
  #import "USBHIDAppDelegate.h"      
  #import "IOKit/hid/IOHIDManager.h" 
  #include &lt;IOKit/IOKitLib.h&gt;
  #include &lt;IOKit/IOCFPlugIn.h&gt;
  #include &lt;IOKit/usb/IOUSBLib.h&gt;
  #include &lt;IOKit/usb/USBSpec.h&gt;
  
  @implementation USBHIDAppDelegate
  
  @synthesize window = _window;
  
  // New USB device specified in the matching dictionary has been added (callback function)
  static void Handle_DeviceMatchingCallback(void *inContext,
                                            IOReturn inResult,
                                            void *inSender,
                                            IOHIDDeviceRef inIOHIDDeviceRef){
  
      // Retrieve the device name &amp; serial number
      NSString *devName = [NSString stringWithUTF8String:
                           CFStringGetCStringPtr(
                                                 IOHIDDeviceGetProperty(inIOHIDDeviceRef,
                                                                        CFSTR("Product")),
                                                 kCFStringEncodingMacRoman)];
  
     UInt32 serialString = CFStringGetCStringPtr(
                            IOHIDDeviceGetProperty(inIOHIDDeviceRef,
                                                   CFSTR("SerialNumber")),
                            kCFStringEncodingMacRoman);
      NSString *devSerialNumber;
      if (serialString == 0) {
          devSerialNumber = @"No Serial Number";
  
      } else {
          devSerialNumber = [NSString stringWithUTF8String:serialString];
  
      }
      // Log the device reference, Name, Serial Number &amp; device count
      NSLog(@"\nDevice added: %p\nModel: %@\nSerial Number:%@\nDevice count: %ld",
            inIOHIDDeviceRef,
            devName,
            devSerialNumber,
            USBDeviceCount(inSender));
  
  //Open the device (Was missing)
  IOReturn err = IOHIDDeviceOpen(inIOHIDDeviceRef, 0);
  </code></pre>
  
  <p>//Should check for error here...</p>
  
  <pre><code>IOHIDDeviceRegisterInputValueCallback(inIOHIDDeviceRef, Handle_IOHIDDeviceInputValueCallback, NULL);
  </code></pre>
  
  <p>//Must also register for RunLoop again here... was also missing
      IOHIDDeviceScheduleWithRunLoop(inIOHIDDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);</p>
  
  <pre><code>}
  
  static void Handle_IOHIDDeviceInputValueCallback(void *inContext,
                                                   IOReturn inResult,
                                                    void *inSender,
                                                   IOHIDValueRef inIOHIDValueRef
                                                   )
  {
      NSLog(@"Value changed");
  }
  
  
  // USB device specified in the matching dictionary has been removed (callback function)
  static void Handle_DeviceRemovalCallback(void *inContext,
                                           IOReturn inResult,
                                           void *inSender,
                                           IOHIDDeviceRef inIOHIDDeviceRef){
  
      // Log the device ID &amp; device count
      NSLog(@"\nDevice removed: %p\nDevice count: %ld",
            (void *)inIOHIDDeviceRef,
            USBDeviceCount(inSender));
       IOHIDDeviceRegisterInputValueCallback(inIOHIDDeviceRef, NULL, NULL); //Remove callback
  
  }
  
  
  
  
  
  // Counts the number of devices in the device set (incudes all USB devices that match our dictionary)
  static long USBDeviceCount(IOHIDManagerRef HIDManager){
  
      // The device set includes all USB devices that match our matching dictionary. Fetch it.
      CFSetRef devSet = IOHIDManagerCopyDevices(HIDManager);
  
      // The devSet will be NULL if there are 0 devices, so only try to count the devices if devSet exists
      if(devSet) return CFSetGetCount(devSet);
  
      // There were no matching devices (devSet was NULL), so return a count of 0
      return 0;
  }
  
  
  - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
  {
      // Insert code here to initialize your application
      SInt32 idVendor = 0x1D34;//0x062A;//0x1d34; //0x1AAD; //// set vendor id
      SInt32 idProduct = 0x000D;//0x0000;//0x000d; //0x000F; //// set product id
  
      // Create an HID Manager
      IOHIDManagerRef HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, 
                                                      kIOHIDOptionsTypeNone);
  
      // Create a Matching Dictionary
      CFMutableDictionaryRef matchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 
                                                                   2, 
                                                                   &amp;kCFTypeDictionaryKeyCallBacks, 
                                                                   &amp;kCFTypeDictionaryValueCallBacks); 
  
      // Specify a device manufacturer in the Matching Dictionary
  
      CFDictionarySetValue(matchDict,
                           CFSTR(kIOHIDVendorIDKey),
                           CFNumberCreate(kCFAllocatorDefault,
                                          kCFNumberSInt32Type, &amp;idVendor));
      CFDictionarySetValue(matchDict,
                           CFSTR(kIOHIDProductKey),
                           CFNumberCreate(kCFAllocatorDefault,
                                          kCFNumberSInt32Type, &amp;idProduct));
  
  
      // Register the Matching Dictionary to the HID Manager
      IOHIDManagerSetDeviceMatching(HIDManager, matchDict); 
  
      // Register a callback for USB device detection with the HID Manager
      IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, &amp;Handle_DeviceMatchingCallback, NULL);
      // Register a callback fro USB device removal with the HID Manager
      IOHIDManagerRegisterDeviceRemovalCallback(HIDManager, &amp;Handle_DeviceRemovalCallback, NULL);
  
      // Register the HID Manager on our app’s run loop
      IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
  
      // Open the HID Manager
      IOReturn IOReturn = IOHIDManagerOpen(HIDManager, kIOHIDOptionsTypeNone);
      if(IOReturn) NSLog(@"IOHIDManagerOpen failed.");  //  Couldn't open the HID manager! TODO: proper error handling
  }
  @end
  </code></pre>
  
  <p>I'm not even sure the device is sending anything... I try logging Using USB Logger, in Level 7, but only pressing the button doesn't seem to display anything... how can I make sure it is actually working?</p>
  
  <p>UPDATE: Was able to detect button presses using <a href="https://github.com/derrick/dream_cheeky" rel="nofollow">this Ruby Open Source project</a> for the BigRedButton I'm using to learn this. So I know it actually works.
  I also reworked my code to register for value changes only when the device is actually detected and removing it when the device is removed. Still nothing at this point.</p>
  
  <p>UPDATE 2: Got this to work! Two issues... I was not opening the device for reading, and also not registering the device itself on the Current RunLoop which is also required. A great WWDC video on Userland device access from WWDC 2011 greatly helped. I also have to say that the BigRedBUtton I was using perhaps is not quite a standard device and that never worked as planed, but the actual device I wanted to work with works like a charm and is giving me input! So it is all good now, thanks to the WWDC videos!</p>