
By Michael Dolinar - Wednesday, 2 May, 2012
There are several classes of USB device. One of these is the HID (Human
Interface Device) class. The HID class of device includes things like
keyboards, mice, joysticks and some data acquisition hardware!
In this article, we will be looking at Apple’s supplied interface for
accessing the HID class devices attached to your system. Particularly,
we’ll be using the HID Manager to detect I/O devices from ONTRAK Control
Systems. If you don’t have any of ONTRAK’s USB I/O devices, don’t worry
- the code will work for detecting HID devices from any manufacturer.
Note: retrieving and sending data to the USB device will be covered in
part 2.
Apple’s HID Manager provides a very neat way to work with HID devices.
Since the HID Manager interfaces with all of the system’s HID devices,
we’ll use a matching dictionary to tell the HID Manager which devices
we’re interested in. Then we’ll register some callback functions do
detect the addition or removal of USB devices. Finally, we’ll query the
HID Manager to get a device set that we’ll use to count the devices that
match our dictionary.
Pseudocode for initialization of our app:
Create an HID
Manager
Create a Matching Dictionary
Specify a device manufacturer in the
Matching Dictionary
Register the Matching Dictionary to the
HID Manager
Register a callback for USB device
detection with the HID Manager
Register a callback fro USB device
removal with the HID Manager
Register the HID Manager on our app’s
run loop
Open the
HID Manager
Let’s dive right into the implementation!
Create a new Cocoa application
- Crack open Xcode 4 & select Create a new Xcode project
-Under Mac OS X / Application select Cocoa Application
-Click Next
-For simplicity, use the same project name as me: USBHID
-Use the same value for class prefix too: USBHID
-Click Next
-Select a folder where you’d like to save your project
-Click Create
Ah, there’s nothing like a shiny new Cocoa app!
Before we begin coding, we need to add the IOKit framework:
-Click on the project root USBHID
-In the Linked Frameworks and Libraries section, click the +
-In the search field, type IOKit
-Select the IOKit.framework
-Click Add
Well done! Let’s start coding!
Click on the USBHIDAppDelegate.m file to begin coding. We start by
importing IOHIDManager.h header file. Insert this line after the other
#import statements:
#import "IOKit/hid/IOHIDManager.h"
Next, move your cursor down to the function
named:
applicationDidFinishLaunching:
This is where you’ll write your initialization code. We’ll start coding
right after the Apple-supplied comment:
// Insert code here to initialize your application
Enter the following code to accomplish the tasks listed in our
pseudocode:
// Create an HID Manager
IOHIDManagerRef HIDManager =
IOHIDManagerCreate(kCFAllocatorDefault,
kIOHIDOptionsTypeNone);
// Create a Matching Dictionary
CFMutableDictionaryRef
matchDict = CFDictionaryCreateMutable(
kCFAllocatorDefault,
2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
// Specify a device manufacturer in the Matching
Dictionary
CFDictionarySetValue(matchDict,
CFSTR(kIOHIDManufacturerKey),
CFSTR("www.ontrak.net"));
// Register the Matching Dictionary to the HID
Manager
IOHIDManagerSetDeviceMatching(HIDManager,
matchDict);
No doubt you’ve noticed that we stopped short of
registering the callback functions. Why you ask? Simply because we
haven’t declared them yet, and Xcode will complain when we try to
register a callback to a function that doesn’t exist.
At this point, it’s worth noting that we’ve specified a manufacturer
name of “www.ontrak.net”. This tells the HID Manager to react only to
devices with this manufacturer name. If you want to detect another
manufacturer’s devices, just change this string to the device
manufacturer’s name. For example, you can substitute “Apple Inc.” for
“www.ontrak.net” and the HID Manager will detect the Apple USB HID
devices connected to your system.
Editors Note: An
alternative way to detect a particular manufacturers device is to use
the Manufacturer ID , which is HEX 0A07 for Ontrak Control Systems
devices. All manufacturers of USB based products are assigned a 4 digit
Hex code by usb.org
Let’s take a minute to declare our callback functions in the our
application’s header file. Open the USBHIDAppDelegate.h by clicking on
it. Start by importing the all important HIDManager.h file by entering
the following line of code after the other #import statements:
|
|
#import
"IOKit/hid/IOHIDManager.h"
|
|
|
Now we can declare our callback functions. Enter the following code just
above the line that reads @end:
// USB device added
callback function
static void
Handle_DeviceMatchingCallback(void
*inContext,
IOReturn inResult,
void *inSender,
IOHIDDeviceRef inIOHIDDeviceRef);
// USB device removed
callback function
static void
Handle_DeviceRemovalCallback(void
*inContext,
IOReturn inResult,
void *inSender,
IOHIDDeviceRef inIOHIDDeviceRef);
// Counts the number of
devices in the device set (includes all USB devices
that match our dictionary)
static long
USBDeviceCount(IOHIDManagerRef
HIDManager);
The first line of code declares the function that
shall be called when a USB device is added. The second
is for USB device removal.
The third line declares a counter function. We’re going
to implement this counter function to query the HID
Manager and return the number of matching devices. This
function is not a callback function.
Let’s jump back to the implementation file and code
these babies! Click on the USBHIDAppDelegate.m file and
enter the following code just above the line that reads
@end:
// 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){
// Log the device ID &
device count
NSLog(@"\nONTRAK
device added: %p\nONTRAK device count: %ld",
(void *)inIOHIDDeviceRef,
USBDeviceCount(inSender));
}
// 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 &
device count
NSLog(@"\nONTRAK
device removed: %p\nONTRAK device count: %ld",
(void *)inIOHIDDeviceRef,
USBDeviceCount(inSender));
}
// Counts the number of
devices in the device set (includes 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;
}
The first two functions look much the same: When a
device is attached or removed from the system, the
device’s identifier and the device count are printed to
the debug window.
The USBDeviceCount function requests the device set from
the HID Manager, counts the devices in the set and
returns the result. This is where the device count is
generated for the callback functions.
With the callback functions declared and implemented, we
can continue with our application initialization. Scroll
back up to the function:
applicationDidFinishLaunching:
Append our previous code in this function with the
following:
// Register a callback
for USB device detection with the HID Manager
IOHIDManagerRegisterDeviceMatchingCallback(HIDManager,
&Handle_DeviceMatchingCallback,
NULL);
// Register a callback
fro USB device removal with the HID Manager
IOHIDManagerRegisterDeviceRemovalCallback(HIDManager,
&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!
That does it! You can now click Xcode’s Run button to
compile & execute your project.
When an ONTRAK Control Systems USB device is attached,
the debug window will show you the device ID and device
count. The code can manage multiple devices. Finally,
when disconnecting a device, another message will be
posted to the debug window.
Complete Code Listing
Complete code listing for the project’s header file:
HIDManager.h
#import <Cocoa/Cocoa.h>
#import <IOKit/hid/IOHIDManager.h>
@interface USBHIDAppDelegate :
NSObject <NSApplicationDelegate>
@property (assign)
IBOutlet
NSWindow *window;
// USB device added
callback function
static void
Handle_DeviceMatchingCallback(void
*inContext,
IOReturn inResult,
void *inSender,
IOHIDDeviceRef inIOHIDDeviceRef);
// USB device removed
callback function
static void
Handle_DeviceRemovalCallback(void
*inContext,
IOReturn inResult,
void *inSender,
IOHIDDeviceRef inIOHIDDeviceRef);
// Counts the number of
devices in the device set (includes all USB devices
that match our dictionary)
static long
USBDeviceCount(IOHIDManagerRef
HIDManager);
@end
Complete code listing for the project’s
implementation file:
HIDManager.m
#import "USBHIDAppDelegate.h"
#import "IOKit/hid/IOHIDManager.h"
@implementation USBHIDAppDelegate
@synthesize window =
_window;
- (void)applicationDidFinishLaunching:(NSNotification
*)aNotification
{
// Insert code here to
initialize your application
// Create an HID
Manager
IOHIDManagerRef
HIDManager =
IOHIDManagerCreate(kCFAllocatorDefault,
kIOHIDOptionsTypeNone);
// Create a Matching
Dictionary
CFMutableDictionaryRef matchDict =
CFDictionaryCreateMutable(kCFAllocatorDefault,
2,
&kCFTypeDictionaryKeyCallBacks/span>,,
&kCFTypeDictionaryValueCallBacks/span>);
// Specify a device
manufacturer in the Matching Dictionary
CCFDictionarySetValue(matchDict,
CFSTR(span class="style_3">kIOHIDManufacturerKey)),
CFSTR(span class="style_9">"www.ontrak.net"));
// Register the
Matching Dictionary to the HID Manager
IOHIDManagerSetDeviceMatching(HIDManager,
matchDict);
// Register a callback
for USB device detection with the HID Manager
IOHIDManagerRegisterDeviceMatchingCallback(HIDManager,
&Handle_DeviceMatchingCallback,
NULL);
// Register a callback
fro USB device removal with the HID Manager
IOHIDManagerRegisterDeviceRemovalCallback(HIDManager,
&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
}
// 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){
// Log the device ID &
device countbr />
NSLog((@"\nONTRAK device added: %p\nONTRAK
device count: %ld",
(void/span>
*)inIOHIDDeviceRef,
USBDeviceCount(inSender));
}br />
// 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 &
device countbr />
NSLog((@"\nONTRAK device removed: %p\nONTRAK
device count: %ld",
(void/span>
*)inIOHIDDeviceRef,
USBDeviceCount(inSender));
// TODO: make sure your
application doesn't try to do anything with the
removed devicebr />
}
// 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;
}
@@end
Download the Xcode 4 project file
USBHID.zip.
View USB HID Device Access From
XCODE Part 2
This project was created on OS X 10.7.3 using Xcode
4.2.1.
Thanks to Tom Fortin from ONTRAK Control Systems for
providing the ADU208
and
ADU200
USB I/O devices used in
the development of this series of articles.
Back to Programming Page> |