Show JRUSBInterface.m syntax highlighted
#import "JRUSBInterface.h"
#include <IOKit/IOKitLib.h>
#if JRUSBInterfaceDEBUG
#include "debugHexStr.h"
#endif
errstr_t JRUSBInterface_DeviceNotFound = "JRUSBInterface_DeviceNotFound";
#define kReadBufferSize 8*1024
@implementation JRUSBInterface
+ (id)interfaceWithVendorID:(uint16_t)vendorID_ productID:(uint16_t)productID_ error:(NSError**)error_ {
return [[[self alloc] initWithVendorID:vendorID_ productID:productID_ error:error_] autorelease];
}
// High level:
// * You start with a collection of Services. You are given an iterator to the collection.
// * Given a Service, you can create a Plugin. Plugins are not useful in their own right.
// * Given a Plugin, you can create an Interface. Interfaces are useful (even if they lead to another Service collection (which IOUSBDeviceInterface does)).
//
// So this code basically traverses this object graph:
// * Device Service[0] -> IOUSBDevice Plugin -> IOUSBDeviceInterface -> Interface Service[0] -> IOUSBInterface Plugin -> IOUSBInterfaceInterface.
//
// Of course every single call can fail, and it's your responsiblity to unwind correctly and not leak. Hence NSX...Error() macros.
- (id)initWithVendorID:(uint16_t)vendorID_ productID:(uint16_t)productID_ error:(NSError**)error_ {
self = [super init];
if (self) {
NSError *error = nil;
mach_port_t masterPort = 0;
NSXReturnError(IOMasterPort(MACH_PORT_NULL, &masterPort)); //dtr0
// Get Device Service[] iterator (deviceServiceIterator).
NSMutableDictionary *matchingDictionary = nil;
if (!error && masterPort) {
matchingDictionary = (NSMutableDictionary*)IOServiceMatching(kIOUSBDeviceClassName);
NSXReturnError(matchingDictionary);
}
io_iterator_t deviceServiceIterator = 0; // dtr1
if (!error && matchingDictionary) {
[matchingDictionary setObject:[NSNumber numberWithShort:vendorID_] forKey:@kUSBVendorID];
[matchingDictionary setObject:[NSNumber numberWithShort:productID_] forKey:@kUSBProductID];
NSXReturnError(IOServiceGetMatchingServices(masterPort, (CFMutableDictionaryRef)matchingDictionary, &deviceServiceIterator));
matchingDictionary = nil; // Consumed by IOServiceGetMatchingServices.
if (!error && !deviceServiceIterator)
error = NSXMakeErrorWithErrstr_t(JRUSBInterface_DeviceNotFound);
}
// Get IOUSBDevice Plugin.
devicePlugin = NULL; //dtr2
if (!error && deviceServiceIterator) {
io_service_t deviceService = IOIteratorNext(deviceServiceIterator);
NSXReturnError(deviceService);
if (deviceService) {
SInt32 score;
NSXReturnError(IOCreatePlugInInterfaceForService(deviceService, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devicePlugin, &score));
IOObjectRelease(deviceService);
deviceService = 0;
}
}
// Get IOUSBDeviceInterface.
deviceInterface = NULL; //dtr3
if (!error && devicePlugin) {
NSXReturnError((*devicePlugin)->QueryInterface(devicePlugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&deviceInterface));
if (!error)
NSXReturnError(deviceInterface);
}
// Interface Service[] iterator.
io_iterator_t interfaceServiceIterator = 0; //dtr4
if (!error && deviceInterface) {
NSXReturnError((*deviceInterface)->USBDeviceOpen(deviceInterface));
UInt8 numConf = 0;
if (!error) {
NSXReturnError((*deviceInterface)->GetNumberOfConfigurations(deviceInterface, &numConf));
}
IOUSBConfigurationDescriptorPtr confDesc = NULL;
if (!error && numConf) {
NSXReturnError((*deviceInterface)->GetConfigurationDescriptorPtr(deviceInterface, 0, &confDesc));
}
if (!error) {
NSXReturnError(confDesc);
}
if (!error && confDesc) {
NSXReturnError((*deviceInterface)->SetConfiguration(deviceInterface, confDesc->bConfigurationValue));
}
if (!error) {
IOUSBFindInterfaceRequest interfaceRequest = { kIOUSBFindInterfaceDontCare, kIOUSBFindInterfaceDontCare, kIOUSBFindInterfaceDontCare, kIOUSBFindInterfaceDontCare };
NSXReturnError((*deviceInterface)->CreateInterfaceIterator(deviceInterface, &interfaceRequest, &interfaceServiceIterator));
}
}
// Get IOUSBInterface Plugin.
interfacePlugin = NULL; //dtr5
if (!error && interfaceServiceIterator) {
io_service_t interfaceService = IOIteratorNext(interfaceServiceIterator);
NSXReturnError(interfaceService);
if (interfaceService) {
SInt32 score;
NSXReturnError(IOCreatePlugInInterfaceForService(interfaceService, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &interfacePlugin, &score));
IOObjectRelease(interfaceService);
interfaceService = 0;
}
}
// Get IOUSBInterfaceInterface.
interfaceInterface = NULL; //dtr6 (handled in -dealloc)
if (!error && interfacePlugin) {
NSXReturnError((*interfacePlugin)->QueryInterface(interfacePlugin, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID182), (LPVOID)&interfaceInterface));
if (!error)
NSXReturnError(interfaceInterface);
}
// Open the interface.
if (!error && interfaceInterface) {
NSXReturnError((*interfaceInterface)->USBInterfaceOpen(interfaceInterface));
}
// Discover the bulk input pipe and bulk output pipe reference numbers.
UInt8 pipeCount = 0;
if (!error && interfaceInterface) {
NSXReturnError((*interfaceInterface)->GetNumEndpoints(interfaceInterface, &pipeCount));
if (!error)
NSXAssertToError(pipeCount);
}
inputPipeRef = outputPipeRef = 0;
UInt8 pipeIndex;
for (pipeIndex = 1; !error && (!inputPipeRef || !outputPipeRef) && pipeIndex <= pipeCount; ++pipeIndex) { // 1-based indexing since pipe 0 is the default control pipe.
UInt8 direction, number, transferType, interval;
UInt16 maxPacketSize;
NSXReturnError((*interfaceInterface)->GetPipeProperties(interfaceInterface, pipeIndex, &direction, &number, &transferType, &maxPacketSize, &interval));
if (!error && kUSBBulk == transferType) {
if ((kUSBIn == direction) && !inputPipeRef)
inputPipeRef = pipeIndex; // Shouldn't this be `number`?
else if((kUSBOut == direction) && !outputPipeRef)
outputPipeRef = pipeIndex; // Shouldn't this be `number`?
}
}
if (!error) {
NSXAssertToError(inputPipeRef);
}
if (!error) {
NSXAssertToError(outputPipeRef);
}
if (!error) {
readBufferData = [[NSMutableData alloc] initWithLength:kReadBufferSize]; // dtr7
CircularBufferInit(&readBuffer, [readBufferData mutableBytes], kReadBufferSize);
}
//
// Clean up.
//
if (interfaceServiceIterator) { //dtr4
IOObjectRelease(interfaceServiceIterator);
interfaceServiceIterator = 0;
}
if (deviceServiceIterator) { //dtr1
IOObjectRelease(deviceServiceIterator);
deviceServiceIterator = 0;
}
if (masterPort) { //dtr0
mach_port_deallocate(mach_task_self(), masterPort);
masterPort = 0;
}
if (error) {
[self release];
self = nil;
}
if (error_) *error_ = error;
}
return self;
}
- (void)dealloc {
if (readBufferData) { // dtr7
[readBufferData release];
readBufferData = nil;
}
if (interfaceInterface) { // dtr6
(*interfaceInterface)->USBInterfaceClose(interfaceInterface);
(*interfaceInterface)->Release(interfaceInterface);
interfaceInterface = NULL;
}
if (interfacePlugin) { //dtr5
IODestroyPlugInInterface(interfacePlugin);
interfacePlugin = NULL;
}
if (deviceInterface) { //dtr3
(*deviceInterface)->USBDeviceClose(deviceInterface);
(*deviceInterface)->Release(deviceInterface);
deviceInterface = NULL;
}
if (devicePlugin) { //dtr2
IODestroyPlugInInterface(devicePlugin);
devicePlugin = NULL;
}
[super dealloc];
}
- (void)write:(const void*)buffer_ size:(size_t)size_ error:(NSError**)error_ {
NSParameterAssert(buffer_);
NSParameterAssert(size_);
NSError *error = nil;
if (!error) {
#if JRUSBInterfaceDEBUG
printf("\n>> %s\n", debugHexStr(buffer_, size_));
#endif
NSXReturnError((*interfaceInterface)->WritePipeTO(interfaceInterface, outputPipeRef, (void*)buffer_, size_, 10000, 10000));
}
if (error_) *error_ = error;
}
- (void)read:(void*)buffer_ size:(size_t)size_ error:(NSError**)error_ {
NSParameterAssert(buffer_);
NSError *error = nil;
if (CircularBufferDataSize(&readBuffer) < size_) {
// Not enough data in readBuffer, perform an actual read.
char internalBuffer[kReadBufferSize];
UInt32 actualSize = sizeof(internalBuffer);
NSXReturnError((*interfaceInterface)->ReadPipeTO(interfaceInterface, inputPipeRef, internalBuffer, &actualSize, 10000, 10000));
if (!error)
NSXAssertToError(actualSize <= kReadBufferSize);
if (!error)
NSXAssertToError(CircularBufferWrite(&readBuffer, internalBuffer, actualSize));
#if JRUSBInterfaceDEBUG
if (!error)
printf("<< %s\n", debugHexStr(internalBuffer, actualSize));
#endif
}
if (!error)
NSXAssertToError(CircularBufferDataSize(&readBuffer) >= size_);
if (!error) {
size_t actualSize = CircularBufferRead(&readBuffer, buffer_, size_);
NSXAssertToError(actualSize == size_);
}
#if JRUSBInterfaceDEBUG
if (!error)
;//printf("<< %s %zu %zu\n", debugHexStr(buffer_, size_), size_, CircularBufferDataSize(&readBuffer));
#endif
if (error_) *error_ = error;
}
@end
See more files for this project here