在mac osx 下使用IOHidInterface 与 USB Hid进行通信
USBHid.h
#ifndef USBHid_h
#define USBHid_h
#import <Foundation/Foundation.h>
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDKeys.h>
@protocol UsbHIDDelegate <NSObject>
@optional
- (void)usbhidDidRecvData:(uint8_t*)recvData length:(CFIndex)reportLength;
- (void)usbhidDidMatch;
- (void)usbhidDidRemove;
@end
@interface UsbHID : NSObject {
IOHIDManagerRef managerRef;
IOHIDDeviceRef deviceRef;
}
@property(nonatomic,strong)id<UsbHIDDelegate> delegate;
@property(nonatomic) BOOL connected;
+ (UsbHID *)sharedManager;
- (BOOL)connectHID;
- (BOOL)disconnectHID;
- (BOOL)senddata:(Byte *)outbuffer size:(int)size;
- (int)readdata:(Byte *)readbuffer size:(int)size;
- (IOHIDManagerRef)getManageRef;
- (void)setManageRef:(IOHIDManagerRef)ref;
- (IOHIDDeviceRef)getDeviceRef;
- (void)setDeviceRef:(IOHIDDeviceRef)ref;
@end
#endif /* USBHid_h */
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import "UsbHID.h"
@implementation UsbHID
static UsbHID *_sharedManager = nil;
@synthesize delegate;
@synthesize connected;
static void MyInputCallback(void* context, IOReturn result, void* sender, IOHIDReportType type, uint32_t reportID, uint8_t *report,CFIndex reportLength) {
id<UsbHIDDelegate> dele = [[UsbHID sharedManager] delegate];
[dele usbhidDidRecvData:report length:reportLength];
}
static void Handle_DeviceMatchingCallback(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) {
[[UsbHID sharedManager] setDeviceRef:inIOHIDDeviceRef];
char *inputbuffer = malloc(64);
IOHIDDeviceRegisterInputReportCallback([[UsbHID sharedManager]getDeviceRef], (uint8_t*)inputbuffer, 64, MyInputCallback, NULL);
[UsbHID sharedManager].connected = true;
NSLog(@"%p设备插入,现在usb设备数量:%ld",(void *)inIOHIDDeviceRef,USBDeviceCount(inSender));
[[[UsbHID sharedManager] delegate] usbhidDidMatch];
}
static void Handle_DeviceRemovalCallback(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) {
[[UsbHID sharedManager] setDeviceRef:nil];
[UsbHID sharedManager].connected = false;
NSLog(@"%p设备拔出,现在usb设备数量:%ld",(void *)inIOHIDDeviceRef,USBDeviceCount(inSender));
[[[UsbHID sharedManager] delegate] usbhidDidRemove];
}
static long USBDeviceCount(IOHIDManagerRef HIDManager){
CFSetRef devSet = IOHIDManagerCopyDevices(HIDManager);
if(devSet)
return CFSetGetCount(devSet);
return 0;
}
+(UsbHID *)sharedManager {
@synchronized( [UsbHID class] ){
if(!_sharedManager)
_sharedManager = [[self alloc] init];
return _sharedManager;
}
return nil;
}
+(id)alloc {
@synchronized ([UsbHID class]){
NSAssert(_sharedManager == nil,
@"Attempted to allocated a second instance");
_sharedManager = [super alloc];
return _sharedManager;
}
return nil;
}
- (id)init {
self = [super init];
if (self) {
managerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
IOHIDManagerScheduleWithRunLoop(managerRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
IOReturn ret = IOHIDManagerOpen(managerRef, 0L);
if (ret != kIOReturnSuccess) {
NSAlert *alert = [NSAlert alertWithMessageText:@"error" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"打开设备失败!"];
[alert runModal];
return self;
}
const long vendorID = 0x0483;
const long productID = 0x5750;
NSMutableDictionary* dict= [NSMutableDictionary dictionary];
[dict setValue:[NSNumber numberWithLong:productID] forKey:[NSString stringWithCString:kIOHIDProductIDKey encoding:NSUTF8StringEncoding]];
[dict setValue:[NSNumber numberWithLong:vendorID] forKey:[NSString stringWithCString:kIOHIDVendorIDKey encoding:NSUTF8StringEncoding]];
IOHIDManagerSetDeviceMatching(managerRef, (__bridge CFMutableDictionaryRef)dict);
IOHIDManagerRegisterDeviceMatchingCallback(managerRef, &Handle_DeviceMatchingCallback, NULL);
IOHIDManagerRegisterDeviceRemovalCallback(managerRef, &Handle_DeviceRemovalCallback, NULL);
NSSet* allDevices = (__bridge NSSet*)(IOHIDManagerCopyDevices(managerRef));
NSArray* deviceRefs = [allDevices allObjects];
if (deviceRefs.count==0) {
}
}
return self;
}
- (void)dealloc {
IOReturn ret = IOHIDDeviceClose(deviceRef, 0L);
if (ret == kIOReturnSuccess) {
deviceRef = nil;
}
ret = IOHIDManagerClose(managerRef, 0L);
if (ret == kIOReturnSuccess) {
managerRef = nil;
}
}
- (BOOL)connectHID {
NSSet* allDevices = (__bridge NSSet*)(IOHIDManagerCopyDevices(managerRef));
NSArray* deviceRefs = [allDevices allObjects];
deviceRef = (deviceRefs.count)?(__bridge IOHIDDeviceRef)[deviceRefs objectAtIndex:0]:nil;
if (deviceRef){
IOReturn ret = IOHIDDeviceOpen(deviceRef, 0L);
if (ret != kIOReturnSuccess)
return false;
self.connected = true;
return true;
}
return false;
}
-(BOOL)disconnectHID{
if (deviceRef){
IOReturn ret = IOHIDDeviceClose(deviceRef, 0L);
if (ret != kIOReturnSuccess)
return false;
self.connected = false;
return true;
}
return false;
}
- (BOOL)senddata:(Byte*)outbuffer size:(int)size {
if (!deviceRef) {
return false;
}
IOReturn ret = IOHIDDeviceSetReport(deviceRef, kIOHIDReportTypeOutput, 0, (uint8_t*)outbuffer, size);
if (ret != kIOReturnSuccess) {
NSAlert* alert = [NSAlert alertWithMessageText:@"error" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"发送数据失败!"];
[alert runModal];
return false;
}
return true;
}
- (int)readdata:(Byte*)readbuffer size:(int)size {
if (!deviceRef) {
return 0;
}
CFIndex sizecf = (CFIndex)size;
IOReturn ret = IOHIDDeviceGetReport(deviceRef, kIOHIDReportTypeFeature, 0, (uint8_t *)readbuffer, (CFIndex *) &sizecf );
if (ret != kIOReturnSuccess) {
NSAlert* alert = [NSAlert alertWithMessageText:@"error" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"接收数据失败!"];
[alert runModal];
return 0;
}
return (int)sizecf;
}
- (IOHIDManagerRef)getManageRef {
return managerRef;
}
- (void)setManageRef:(IOHIDManagerRef)ref {
managerRef = ref;
}
- (IOHIDDeviceRef)getDeviceRef {
return deviceRef;
}
- (void)setDeviceRef:(IOHIDDeviceRef)ref {
deviceRef = ref;
}
@end
可以看到usb hid 的接收数据是使用回调的方式触发的。
之后发现在使用runModalWindow弹出模态窗口NSWindow之后,会突然无法接收到usb hid 的数据,在回调处打上断点之后一直没有进来,猜想可能是这个回调线程是基于当前的NSWindow,在runModal另一个NSWindow之后,将原来的NSWindow的线程阻塞了,
解决方法就是不要使用runModal方法,使用beginSheet方法。
例如对于NSOpenlPanel,使用
let panel : NSOpenPanel = NSOpenPanel()
let fileTypes : NSArray = ["PNG"]
panel.message = "Select a PNG file"
panel.prompt = "OK"
panel.canChooseFiles = true
panel.allowsMultipleSelection = false
panel.allowedFileTypes = (fileTypes as! [String])
// let result : Int = panel.runModal()
panel.beginWithCompletionHandler { (result) -> Void in
if (result == NSFileHandlingPanelOKButton){
}
else{
}
}
使用beginsheet方法后,就可以不影响usb hid的接收数据了