IOHIDUserDeviceTestController.m [plain text]
//
// NSObject+IOHIDTestController.m
// IOHIDFamily
//
// Created by YG on 10/24/16.
//
//
#import "IOHIDUserDeviceTestController.h"
#include <IOKit/IOKitLib.h>
#include <dispatch/private.h>
#include "IOHIDUnitTestUtility.h"
static NSString * deviceDescription =
@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
" <plist version=\"1.0\"> "
" <dict> "
" <key>VendorID</key> "
" <integer>555</integer> "
" <key>ProductID</key> "
" <integer>555</integer> "
" <key>ReportInterval</key> "
" <integer>10000</integer> "
" <key>RequestTimeout</key> "
" <integer>5000000</integer> "
" <key>UnitTestService</key> "
" <true/> "
" </dict> "
" </plist> ";
static IOReturn UserDeviceSetReportCallbackStatic(void * _Nullable refcon, IOHIDReportType type, uint32_t reportID, uint8_t * report, CFIndex reportLength);
static IOReturn UserDeviceGetReportWithReturnLengthCallbackStatic(void * _Nullable refcon, IOHIDReportType type, uint32_t reportID, uint8_t * report, CFIndex * reportLength);
@implementation IOHIDUserDeviceTestController
-(nullable instancetype) initWithDescriptor: (nonnull NSData *) descriptor DeviceUniqueID: (NSString*) uniqueID andQueue:(nullable dispatch_queue_t) queue {
NSMutableDictionary* deviceConfig = [NSPropertyListSerialization propertyListWithData:[deviceDescription dataUsingEncoding:NSUTF8StringEncoding] options:NSPropertyListMutableContainers format:NULL error:NULL];
deviceConfig [@kIOHIDReportDescriptorKey] = descriptor;
deviceConfig [@kIOHIDPhysicalDeviceUniqueIDKey] = uniqueID;
self->_userDeviceUniqueID = uniqueID;
return [self initWithDeviceConfiguration:deviceConfig andQueue:queue];
}
-(nullable instancetype) initWithDeviceConfiguration: (nonnull NSDictionary *) deviceConfig andQueue:(nullable dispatch_queue_t) queue {
self = [super init];
if (!self) {
return self;
}
self->_userDevice = IOHIDUserDeviceCreate(kCFAllocatorDefault, (CFDictionaryRef)deviceConfig);
if (self.userDevice == NULL) {
return nil;
}
self.userDeviceSetReports = [[NSMutableArray alloc] init];
if (!self.userDeviceSetReports) {
return nil;
}
self->_userDeviceQueue = queue;
if (queue) {
IOHIDUserDeviceScheduleWithDispatchQueue (self.userDevice, self.userDeviceQueue);
}
IOHIDUserDeviceRegisterSetReportCallback(self.userDevice, UserDeviceSetReportCallbackStatic, (__bridge void * _Nullable)(self));
IOHIDUserDeviceRegisterGetReportWithReturnLengthCallback (self.userDevice, UserDeviceGetReportWithReturnLengthCallbackStatic, (__bridge void * _Nullable)(self));
return self;
}
-(IOReturn) handleReport: (nonnull NSData*) report withInterval: (NSInteger) interval {
TestLog("handleReport: report: if (interval) {
usleep ((useconds_t)interval);
}
return IOHIDUserDeviceHandleReport(self.userDevice, (uint8_t *)report.bytes , report.length);
}
-(IOReturn) handleReport: (nonnull uint8_t*) report Length: (NSUInteger) length andInterval: (NSInteger) interval {
NSData *reportData = [NSData dataWithBytes:report length:length];
return [self handleReport:reportData withInterval:interval];
}
-(IOReturn) handleReport: (nonnull NSData*) report withTimestamp: (uint64_t) timestamp {
TestLog("handleReport: report: return IOHIDUserDeviceHandleReportWithTimeStamp(self.userDevice, timestamp, (uint8_t *)report.bytes , report.length);
}
-(IOReturn) handleReportAsync: (nonnull NSData*) report withTimestamp: (uint64_t) timestamp Callback: (IOHIDUserDeviceHandleReportAsyncCallback) callback Context: (void *) context {
TestLog("handleReportAsync: report: return IOHIDUserDeviceHandleReportAsyncWithTimeStamp(self.userDevice, timestamp, (uint8_t *)report.bytes , report.length, callback, context);
}
-(IOReturn) handleReportAsync: (nonnull NSData*) report Callback: (IOHIDUserDeviceHandleReportAsyncCallback) callback Context: (void *) context {
TestLog("handleReportAsync: report: return IOHIDUserDeviceHandleReportAsync(self.userDevice, (uint8_t *)report.bytes , report.length, callback, context);
}
-(void)dealloc {
[self invalidate];
if (self.userDevice) {
CFRelease (self.userDevice);
}
}
-(IOReturn) handleReports: (nonnull NSArray *) reports withInterval: (NSInteger) interval {
IOReturn result = kIOReturnBadArgument;
for (id report in reports) {
usleep ((useconds_t)interval);
result = [self handleReport:report withInterval: interval];
if (result) {
break;
}
}
return result;
}
-(IOReturn) handleReportsWithIntervals: (nonnull NSArray *) reports {
IOReturn result = kIOReturnBadArgument;
for (id reportDict in reports) {
if (![reportDict isKindOfClass: [NSDictionary class]]) {
return kIOReturnBadArgument;
}
NSData* report = reportDict[@"report"];
NSNumber* interval= reportDict[@"interval"];
if (!report || [report isKindOfClass: [NSData class]] || !interval || [report isKindOfClass: [NSNumber class]]) {
return kIOReturnBadArgument;
}
result = [self handleReport:report withInterval: interval.unsignedIntValue];
if (result) {
break;
}
}
return result;
}
-(IOReturn) UserDeviceSetReportCallback: (IOHIDReportType) type Id: (uint32_t) reportID Bytes: (uint8_t*) report Length: (CFIndex) length {
TestLog("UserDeviceSetReport: type: NSData* reportData = [NSData dataWithBytes: report length:length];
NSDictionary * reportDict = @{@"report":reportData};
[self.userDeviceSetReports addObject: reportDict];
return kIOReturnSuccess;
}
-(void)invalidate {
if (self->_userDeviceQueue) {
IOHIDUserDeviceUnscheduleFromDispatchQueue(self.userDevice, self->_userDeviceQueue);
self->_userDeviceQueue = nil;
}
}
@end
IOReturn UserDeviceSetReportCallbackStatic(void * _Nullable refcon, IOHIDReportType type, uint32_t reportID, uint8_t * report, CFIndex reportLength) {
IOHIDUserDeviceTestController *self = (__bridge IOHIDUserDeviceTestController *)refcon;
if (self.userDeviceObserver && [self.userDeviceObserver respondsToSelector:@selector(SetReportCallback::::)]) {
return [self.userDeviceObserver SetReportCallback: type :reportID : report : reportLength];
}
return kIOReturnUnsupported;
}
IOReturn UserDeviceGetReportWithReturnLengthCallbackStatic(void * _Nullable refcon, IOHIDReportType type, uint32_t reportID, uint8_t * report, CFIndex * reportLength) {
IOHIDUserDeviceTestController *self = (__bridge IOHIDUserDeviceTestController *)refcon;
if (self.userDeviceObserver && [self.userDeviceObserver respondsToSelector:@selector(GetReportCallback::::)]) {
return [self.userDeviceObserver GetReportCallback: type :reportID : report : reportLength];
}
return kIOReturnUnsupported;
}