From 1757f32fd0c60c293331fd35d35255aa55236c58 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 19 Jun 2013 10:18:48 -0700 Subject: [PATCH] Added source files for UVCCameraControl. --- .../UVCCameraControl/src/UVCCameraControl.h | 83 ++++ .../UVCCameraControl/src/UVCCameraControl.m | 383 ++++++++++++++++++ 2 files changed, 466 insertions(+) create mode 100644 interface/external/UVCCameraControl/src/UVCCameraControl.h create mode 100644 interface/external/UVCCameraControl/src/UVCCameraControl.m diff --git a/interface/external/UVCCameraControl/src/UVCCameraControl.h b/interface/external/UVCCameraControl/src/UVCCameraControl.h new file mode 100644 index 0000000000..a57f85edef --- /dev/null +++ b/interface/external/UVCCameraControl/src/UVCCameraControl.h @@ -0,0 +1,83 @@ +#import +#include + +#include +#include +#include +#include + + +#define UVC_INPUT_TERMINAL_ID 0x01 +#define UVC_PROCESSING_UNIT_ID 0x03 + +#define UVC_CONTROL_INTERFACE_CLASS 14 +#define UVC_CONTROL_INTERFACE_SUBCLASS 1 + +#define UVC_SET_CUR 0x01 +#define UVC_GET_CUR 0x81 +#define UVC_GET_MIN 0x82 +#define UVC_GET_MAX 0x83 + +typedef struct { + int min, max; +} uvc_range_t; + +typedef struct { + int unit; + int selector; + int size; +} uvc_control_info_t; + +typedef struct { + uvc_control_info_t autoExposure; + uvc_control_info_t exposure; + uvc_control_info_t brightness; + uvc_control_info_t contrast; + uvc_control_info_t gain; + uvc_control_info_t saturation; + uvc_control_info_t sharpness; + uvc_control_info_t whiteBalance; + uvc_control_info_t autoWhiteBalance; +} uvc_controls_t ; + + +@interface UVCCameraControl : NSObject { + long dataBuffer; + IOUSBInterfaceInterface190 **interface; +} + + +- (id)initWithLocationID:(UInt32)locationID; +- (id)initWithVendorID:(long)vendorID productID:(long)productID; +- (IOUSBInterfaceInterface190 **)getControlInferaceWithDeviceInterface:(IOUSBDeviceInterface **)deviceInterface; + +- (BOOL)sendControlRequest:(IOUSBDevRequest)controlRequest; +- (BOOL)setData:(long)value withLength:(int)length forSelector:(int)selector at:(int)unitID; +- (long)getDataFor:(int)type withLength:(int)length fromSelector:(int)selector at:(int)unitID; + +- (uvc_range_t)getRangeForControl:(const uvc_control_info_t *)control; +- (float)mapValue:(float)value fromMin:(float)fromMin max:(float)fromMax toMin:(float)toMin max:(float)toMax; +- (float)getValueForControl:(const uvc_control_info_t *)control; +- (BOOL)setValue:(float)value forControl:(const uvc_control_info_t *)control; + + +- (BOOL)setAutoExposure:(BOOL)enabled; +- (BOOL)getAutoExposure; +- (BOOL)setExposure:(float)value; +- (float)getExposure; +- (BOOL)setGain:(float)value; +- (float)getGain; +- (BOOL)setBrightness:(float)value; +- (float)getBrightness; +- (BOOL)setContrast:(float)value; +- (float)getContrast; +- (BOOL)setSaturation:(float)value; +- (float)getSaturation; +- (BOOL)setSharpness:(float)value; +- (float)getSharpness; +- (BOOL)setAutoWhiteBalance:(BOOL)enabled; +- (BOOL)getAutoWhiteBalance; +- (BOOL)setWhiteBalance:(float)value; +- (float)getWhiteBalance; + +@end diff --git a/interface/external/UVCCameraControl/src/UVCCameraControl.m b/interface/external/UVCCameraControl/src/UVCCameraControl.m new file mode 100644 index 0000000000..3f2ccbab37 --- /dev/null +++ b/interface/external/UVCCameraControl/src/UVCCameraControl.m @@ -0,0 +1,383 @@ +#import "UVCCameraControl.h" +#import "UVCCameraControl.hpp" + +void configureCamera(int vendorID, int productID, int autoExposure, float exposure, float contrast, float saturation, float sharpness, int autoWhiteBalance, float whiteBalance) { + UVCCameraControl* cameraControl = [[UVCCameraControl alloc] initWithVendorID:0x05ac productID:0x8510]; + [cameraControl setAutoExposure:autoExposure]; + [cameraControl setExposure:exposure]; + [cameraControl setContrast:contrast]; + [cameraControl setSaturation:saturation]; + [cameraControl setSharpness:sharpness]; + [cameraControl setAutoWhiteBalance:autoWhiteBalance]; + [cameraControl setWhiteBalance:whiteBalance]; +} + +const uvc_controls_t uvc_controls = { + .autoExposure = { + .unit = UVC_INPUT_TERMINAL_ID, + .selector = 0x02, + .size = 1, + }, + .exposure = { + .unit = UVC_INPUT_TERMINAL_ID, + .selector = 0x04, + .size = 4, + }, + .brightness = { + .unit = UVC_PROCESSING_UNIT_ID, + .selector = 0x02, + .size = 2, + }, + .contrast = { + .unit = UVC_PROCESSING_UNIT_ID, + .selector = 0x03, + .size = 2, + }, + .gain = { + .unit = UVC_PROCESSING_UNIT_ID, + .selector = 0x04, + .size = 2, + }, + .saturation = { + .unit = UVC_PROCESSING_UNIT_ID, + .selector = 0x07, + .size = 2, + }, + .sharpness = { + .unit = UVC_PROCESSING_UNIT_ID, + .selector = 0x08, + .size = 2, + }, + .whiteBalance = { + .unit = UVC_PROCESSING_UNIT_ID, + .selector = 0x0A, + .size = 2, + }, + .autoWhiteBalance = { + .unit = UVC_PROCESSING_UNIT_ID, + .selector = 0x0B, + .size = 1, + }, +}; + + +@implementation UVCCameraControl + + +- (id)initWithLocationID:(UInt32)locationID { + if( self = [super init] ) { + interface = NULL; + + // Find All USB Devices, get their locationId and check if it matches the requested one + CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); + io_iterator_t serviceIterator; + IOServiceGetMatchingServices( kIOMasterPortDefault, matchingDict, &serviceIterator ); + + io_service_t camera; + while( camera = IOIteratorNext(serviceIterator) ) { + // Get DeviceInterface + IOUSBDeviceInterface **deviceInterface = NULL; + IOCFPlugInInterface **plugInInterface = NULL; + SInt32 score; + kern_return_t kr = IOCreatePlugInInterfaceForService( camera, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score ); + if( (kIOReturnSuccess != kr) || !plugInInterface ) { + NSLog( @"CameraControl Error: IOCreatePlugInInterfaceForService returned 0x%08x.", kr ); + continue; + } + + HRESULT res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceInterface ); + (*plugInInterface)->Release(plugInInterface); + if( res || deviceInterface == NULL ) { + NSLog( @"CameraControl Error: QueryInterface returned %d.\n", (int)res ); + continue; + } + + UInt32 currentLocationID = 0; + (*deviceInterface)->GetLocationID(deviceInterface, ¤tLocationID); + + if( currentLocationID == locationID ) { + // Yep, this is the USB Device that was requested! + interface = [self getControlInferaceWithDeviceInterface:deviceInterface]; + return self; + } + } // end while + + } + return self; +} + + +- (id)initWithVendorID:(long)vendorID productID:(long)productID { + if( self = [super init] ) { + interface = NULL; + + // Find USB Device + CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); + CFNumberRef numberRef; + + numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendorID); + CFDictionarySetValue( matchingDict, CFSTR(kUSBVendorID), numberRef ); + CFRelease(numberRef); + + numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &productID); + CFDictionarySetValue( matchingDict, CFSTR(kUSBProductID), numberRef ); + CFRelease(numberRef); + io_service_t camera = IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict ); + + + // Get DeviceInterface + IOUSBDeviceInterface **deviceInterface = NULL; + IOCFPlugInInterface **plugInInterface = NULL; + SInt32 score; + kern_return_t kr = IOCreatePlugInInterfaceForService( camera, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score ); + if( (kIOReturnSuccess != kr) || !plugInInterface ) { + NSLog( @"CameraControl Error: IOCreatePlugInInterfaceForService returned 0x%08x.", kr ); + return self; + } + + HRESULT res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceInterface ); + (*plugInInterface)->Release(plugInInterface); + if( res || deviceInterface == NULL ) { + NSLog( @"CameraControl Error: QueryInterface returned %d.\n", (int)res ); + return self; + } + + interface = [self getControlInferaceWithDeviceInterface:deviceInterface]; + } + return self; +} + + +- (IOUSBInterfaceInterface190 **)getControlInferaceWithDeviceInterface:(IOUSBDeviceInterface **)deviceInterface { + IOUSBInterfaceInterface190 **controlInterface; + + io_iterator_t interfaceIterator; + IOUSBFindInterfaceRequest interfaceRequest; + interfaceRequest.bInterfaceClass = UVC_CONTROL_INTERFACE_CLASS; + interfaceRequest.bInterfaceSubClass = UVC_CONTROL_INTERFACE_SUBCLASS; + interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + IOReturn success = (*deviceInterface)->CreateInterfaceIterator( deviceInterface, &interfaceRequest, &interfaceIterator ); + if( success != kIOReturnSuccess ) { + return NULL; + } + + io_service_t usbInterface; + HRESULT result; + + + if( usbInterface = IOIteratorNext(interfaceIterator) ) { + IOCFPlugInInterface **plugInInterface = NULL; + + //Create an intermediate plug-in + SInt32 score; + kern_return_t kr = IOCreatePlugInInterfaceForService( usbInterface, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score ); + + //Release the usbInterface object after getting the plug-in + kr = IOObjectRelease(usbInterface); + if( (kr != kIOReturnSuccess) || !plugInInterface ) { + NSLog( @"CameraControl Error: Unable to create a plug-in (%08x)\n", kr ); + return NULL; + } + + //Now create the device interface for the interface + result = (*plugInInterface)->QueryInterface( plugInInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID *) &controlInterface ); + + //No longer need the intermediate plug-in + (*plugInInterface)->Release(plugInInterface); + + if( result || !controlInterface ) { + NSLog( @"CameraControl Error: Couldn’t create a device interface for the interface (%08x)", (int) result ); + return NULL; + } + + return controlInterface; + } + + return NULL; +} + +- (BOOL)sendControlRequest:(IOUSBDevRequest)controlRequest { + if( !interface ){ + NSLog( @"CameraControl Error: No interface to send request" ); + return NO; + } + + //Now open the interface. This will cause the pipes associated with + //the endpoints in the interface descriptor to be instantiated + kern_return_t kr = (*interface)->ControlRequest( interface, 0, &controlRequest ); + if( kr != kIOReturnSuccess ) { + NSLog( @"CameraControl Error: Control request failed: %08x", kr ); + return NO; + } + + return YES; +} + + +- (BOOL)setData:(long)value withLength:(int)length forSelector:(int)selector at:(int)unitId { + IOUSBDevRequest controlRequest; + controlRequest.bmRequestType = USBmakebmRequestType( kUSBOut, kUSBClass, kUSBInterface ); + controlRequest.bRequest = UVC_SET_CUR; + controlRequest.wValue = (selector << 8) | 0x00; + controlRequest.wIndex = (unitId << 8) | 0x00; + controlRequest.wLength = length; + controlRequest.wLenDone = 0; + controlRequest.pData = &value; + return [self sendControlRequest:controlRequest]; +} + + +- (long)getDataFor:(int)type withLength:(int)length fromSelector:(int)selector at:(int)unitId { + long value = 0; + IOUSBDevRequest controlRequest; + controlRequest.bmRequestType = USBmakebmRequestType( kUSBIn, kUSBClass, kUSBInterface ); + controlRequest.bRequest = type; + controlRequest.wValue = (selector << 8) | 0x00; + controlRequest.wIndex = (unitId << 8) | 0x00; + controlRequest.wLength = length; + controlRequest.wLenDone = 0; + controlRequest.pData = &value; + BOOL success = [self sendControlRequest:controlRequest]; + return ( success ? value : 0 ); +} + + +// Get Range (min, max) +- (uvc_range_t)getRangeForControl:(uvc_control_info_t *)control { + uvc_range_t range = { 0, 0 }; + range.min = [self getDataFor:UVC_GET_MIN withLength:control->size fromSelector:control->selector at:control->unit]; + range.max = [self getDataFor:UVC_GET_MAX withLength:control->size fromSelector:control->selector at:control->unit]; + return range; +} + + +// Used to de-/normalize values +- (float)mapValue:(float)value fromMin:(float)fromMin max:(float)fromMax toMin:(float)toMin max:(float)toMax { + return toMin + (toMax - toMin) * ((value - fromMin) / (fromMax - fromMin)); +} + + +// Get a normalized value +- (float)getValueForControl:(uvc_control_info_t *)control { + // TODO: Cache the range somewhere? + uvc_range_t range = [self getRangeForControl:control]; + + int intval = [self getDataFor:UVC_GET_CUR withLength:control->size fromSelector:control->selector at:control->unit]; + return [self mapValue:intval fromMin:range.min max:range.max toMin:0 max:1]; +} + + +// Set a normalized value +- (BOOL)setValue:(float)value forControl:(uvc_control_info_t *)control { + // TODO: Cache the range somewhere? + uvc_range_t range = [self getRangeForControl:control]; + + int intval = [self mapValue:value fromMin:0 max:1 toMin:range.min max:range.max]; + return [self setData:intval withLength:control->size forSelector:control->selector at:control->unit]; +} + + + + + +// ================================================================ + +// Set/Get the actual values for the camera +// + +- (BOOL)setAutoExposure:(BOOL)enabled { + int intval = (enabled ? 0x08 : 0x01); // "auto exposure modes" ar NOT boolean (on|off) as it seems + return [self setData:intval + withLength:uvc_controls.autoExposure.size + forSelector:uvc_controls.autoExposure.selector + at:uvc_controls.autoExposure.unit]; +} + +- (BOOL)getAutoExposure { + int intval = [self getDataFor:UVC_GET_CUR + withLength:uvc_controls.autoExposure.size + fromSelector:uvc_controls.autoExposure.selector + at:uvc_controls.autoExposure.unit]; + + return ( intval == 0x08 ? YES : NO ); +} + +- (BOOL)setExposure:(float)value { + value = 1 - value; + return [self setValue:value forControl:&uvc_controls.exposure]; +} + +- (float)getExposure { + float value = [self getValueForControl:&uvc_controls.exposure]; + return 1 - value; +} + +- (BOOL)setGain:(float)value { + return [self setValue:value forControl:&uvc_controls.gain]; +} + +- (float)getGain { + return [self getValueForControl:&uvc_controls.gain]; +} + +- (BOOL)setBrightness:(float)value { + return [self setValue:value forControl:&uvc_controls.brightness]; +} + +- (float)getBrightness { + return [self getValueForControl:&uvc_controls.brightness]; +} + +- (BOOL)setContrast:(float)value { + return [self setValue:value forControl:&uvc_controls.contrast]; +} + +- (float)getContrast { + return [self getValueForControl:&uvc_controls.contrast]; +} + +- (BOOL)setSaturation:(float)value { + return [self setValue:value forControl:&uvc_controls.saturation]; +} + +- (float)getSaturation { + return [self getValueForControl:&uvc_controls.saturation]; +} + +- (BOOL)setSharpness:(float)value { + return [self setValue:value forControl:&uvc_controls.sharpness]; +} + +- (float)getSharpness { + return [self getValueForControl:&uvc_controls.sharpness]; +} + +- (BOOL)setAutoWhiteBalance:(BOOL)enabled { + int intval = (enabled ? 0x01 : 0x00); + return [self setData:intval + withLength:uvc_controls.autoWhiteBalance.size + forSelector:uvc_controls.autoWhiteBalance.selector + at:uvc_controls.autoWhiteBalance.unit]; +} + +- (BOOL)getAutoWhiteBalance { + int intval = [self getDataFor:UVC_GET_CUR + withLength:uvc_controls.autoWhiteBalance.size + fromSelector:uvc_controls.autoWhiteBalance.selector + at:uvc_controls.autoWhiteBalance.unit]; + + return ( intval ? YES : NO ); +} + +- (BOOL)setWhiteBalance:(float)value { + return [self setValue:value forControl:&uvc_controls.whiteBalance]; +} + +- (float)getWhiteBalance { + return [self getValueForControl:&uvc_controls.whiteBalance]; +} + + +@end