DecodeVideoInterfaceDescriptor.m   [plain text]


/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1998-2003 Apple Computer, Inc.  All Rights Reserved.
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */


#import "DecodeVideoInterfaceDescriptor.h"


@implementation DecodeVideoInterfaceDescriptor

+(void)decodeBytes:(UInt8 *)descriptor forDevice:(BusProbeDevice *)thisDevice withDeviceInterface:(IOUSBDeviceRef)deviceIntf
{

    static  char			buf[256];
    static  char			buf2[256];
    auto IOUSBVCInterfaceDescriptor *		pVideoControlHeader = NULL;
    auto IOUSBVCInputTerminalDescriptor *	pVideoInTermDesc = NULL;
    auto IOUSBVCCameraTerminalDescriptor *	pCameraTermDesc = NULL;
    auto IOUSBVCOutputTerminalDescriptor *	pVideoOutTermDesc = NULL;
    auto IOUSBVCSelectorUnitDescriptor *	pSelectorUnitDesc = NULL;
    auto IOUSBVCSelectorUnit2Descriptor *	pSelectorUnit2Desc = NULL;
    auto IOUSBVCProcessingUnitDescriptor *	pProcessingUnitDesc = NULL;
    auto IOUSBVCProcessingUnit2Descriptor *	pProcessingUnit2Desc = NULL;
    auto IOUSBVCExtensionUnitDescriptor *	pExtensionUnitDesc = NULL;
    auto IOUSBVCExtensionUnit2Descriptor *	pExtensionUnit2Desc = NULL;
    auto IOUSBVCExtensionUnit3Descriptor *	pExtensionUnit3Desc = NULL;
    auto IOUSBVCInterruptEndpointDescriptor *	pVCInterruptEndpintDesc = NULL;
    auto IOUSBVSInputHeaderDescriptor *		pVSInputHeaderDesc = NULL;
    auto IOUSBVSOutputHeaderDescriptor *	pVSOutputHeaderDesc = NULL;
    auto IOUSBVDC_MJPEGFormatDescriptor *	pMJPEGFormatDesc = NULL;
    auto IOUSBVDC_MJPEGFrameDescriptor *	pMJPEGFrameDesc = NULL;
    auto IOUSBVDC_MJPEGDiscreteFrameDescriptor *	pMJPEGDiscreteFrameDesc = NULL;
    auto IOUSBVDC_ColorFormatDescriptor *       pColorFormatDesc = NULL;
    auto IOUSBVDC_UncompressedFormatDescriptor * pUncompressedFormatDesc = NULL;
    auto IOUSBVDC_UncompressedFrameDescriptor * pUncompressedFrameDesc = NULL;
    auto IOUSBVDC_UncompressedDiscreteFrameDescriptor * pUncompressedDiscreteFrameDesc = NULL;
	auto IOUSBVDC_StillImageFrameDescriptor * pVSStillImageFrameDesc = NULL;

    UInt16					i, j;
    UInt8					*p;
    UInt32					*t;
    char					*s = NULL;
    IOUSBVCInterfaceDescriptor *                desc = (IOUSBVCInterfaceDescriptor *) descriptor;
    UInt64					uuidLO;
    UInt32                                      data1;
    UInt16                                      data2, data3;
    char					str[256];

    if ( desc->bDescriptorType == CS_ENDPOINT )
    {
        pVCInterruptEndpintDesc = (IOUSBVCInterruptEndpointDescriptor *)desc;

        switch ( pVCInterruptEndpintDesc->bDescriptorSubType )
        {
            case EP_INTERRUPT:
                sprintf((char *)buf, "VDC Specific Interrupt Endpoint");
                break;
            default:
                sprintf((char *)buf, "Unknown Endpoint SubType Descriptor");
        }
        [thisDevice addProperty:buf withValue:"" atDepth:INTERFACE_LEVEL];

        // Print the Length and contents of this class-specific descriptor
        //
        sprintf(str, "%u", pVCInterruptEndpintDesc->bLength);
        [thisDevice addProperty:"Length (and contents):" withValue:str atDepth:INTERFACE_LEVEL+1];
        [DescriptorDecoder dumpRawDescriptor:(Byte*)desc forDevice:thisDevice atDepth:INTERFACE_LEVEL+2];

        sprintf((char *)buf, "%u", Swap16(&pVCInterruptEndpintDesc->wMaxTransferSize) );
        [thisDevice addProperty:"Max Transfer Size:" withValue:buf atDepth:INTERFACE_LEVEL+1];
        return;
    }
    
    if ( desc->bDescriptorType != CS_INTERFACE )
        return;

    if( SC_VIDEOCONTROL == [[thisDevice lastInterfaceClassInfo] subclassNum] )
    {
        switch ( desc->bDescriptorSubType )
        {
            case VC_DESCRIPTOR_UNDEFINED:
                sprintf((char *)buf, "Video Control Class Unknown Header");
                break;
            case VC_HEADER:
                sprintf((char *)buf, "VDC (Control) Header");
                break;
            case VC_INPUT_TERMINAL:
                sprintf((char *)buf, "VDC (Control) Input Terminal");
                break;
            case VC_OUTPUT_TERMINAL:
                sprintf((char *)buf, "VDC (Control) Output Terminal");
                break;
            case VC_SELECTOR_UNIT:
                sprintf((char *)buf, "VDC (Control) Selector Unit");
                break;
            case VC_PROCESSING_UNIT:
                sprintf((char *)buf, "VDC (Control) Processing Unit");
                break;
            case VC_EXTENSION_UNIT:
                sprintf((char *)buf, "VDC (Control) Extension Unit");
                break;
            default:
                sprintf((char *)buf, "Unknown SC_VIDEOCONTROL SubType Descriptor");
        }
    }
    else if( SC_VIDEOSTREAMING == [[thisDevice lastInterfaceClassInfo] subclassNum] )
    {
        switch ( desc->bDescriptorSubType )
        {
            case VS_UNDEFINED:
                sprintf((char *)buf, "VDC (Streaming) Unknown Header");
                break;
            case VS_INPUT_HEADER:
                sprintf((char *)buf, "VDC (Streaming) Input Header");
                break;
            case VS_OUTPUT_HEADER:
                sprintf((char *)buf, "VDC (Streaming) Output Header");
                break;
            case VS_STILL_IMAGE_FRAME:
                sprintf((char *)buf, "VDC (Streaming) Still Image Frame Descriptor");
                break;
            case VS_FORMAT_UNCOMPRESSED:
                sprintf((char *)buf, "VDC (Streaming) Uncompressed Format Descriptor");
                break;
            case VS_FRAME_UNCOMPRESSED:
                sprintf((char *)buf, "VDC (Streaming) Uncompressed Frame Descriptor");
                break;
            case VS_FORMAT_MJPEG:
                sprintf((char *)buf, "VDC (Streaming) MJPEG Format Descriptor");
                break;
            case VS_FRAME_MJPEG:
                sprintf((char *)buf, "VDC (Streaming) MJPEG Frame Descriptor");
                break;
            case VS_FORMAT_MPEG1:
                sprintf((char *)buf, "VDC (Streaming) MPEG1 Format Descriptor");
                break;
            case VS_FORMAT_MPEG2PS:
                sprintf((char *)buf, "VDC (Streaming) MPEG2-PS Format Descriptor");
                break;
            case VS_FORMAT_MPEG2TS:
                sprintf((char *)buf, "VDC (Streaming) MPEG2-TS Format Descriptor");
                break;
            case VS_FORMAT_MPEG4SL:
                sprintf((char *)buf, "VDC (Streaming) MPEG4-SL Format Descriptor");
                break;
            case VS_FORMAT_DV:
                sprintf((char *)buf, "VDC (Streaming) DV Format Descriptor");
                break;
            case VS_COLORFORMAT:
                sprintf((char *)buf, "VDC (Streaming) Color Format Descriptor");
                break;
            case VS_FORMAT_VENDOR:
                sprintf((char *)buf, "VDC (Streaming) Vendor-specific Format Descriptor");
                break;
            case VS_FRAME_VENDOR:
                sprintf((char *)buf, "VDC (Streaming) Vendor-specific Frame Descriptor");
                break;
            default:
                sprintf((char *)buf, "Unknown SC_VIDEOSTREAMING SubType Descriptor");
        }
    }
    else
        sprintf((char *)buf, "Unknown VDC Interface Subclass Type");

    [thisDevice addProperty:buf withValue:"" atDepth:INTERFACE_LEVEL];

    // Print the Length and contents of this class-specific descriptor
    //
    sprintf(str, "%u", desc->bLength);
    [thisDevice addProperty:"Length (and contents):" withValue:str atDepth:INTERFACE_LEVEL+1];
    [DescriptorDecoder dumpRawDescriptor:(Byte*)desc forDevice:thisDevice atDepth:INTERFACE_LEVEL+2];
    sprintf((char *)buf, 	"0x%x", desc->bDescriptorType );
    [thisDevice addProperty:"bDescriptorType:" withValue:buf atDepth:INTERFACE_LEVEL+1];
    sprintf((char *)buf, 	"0x%x", desc->bDescriptorSubType );
    [thisDevice addProperty:"bDescriptorSubType:" withValue:buf atDepth:INTERFACE_LEVEL+1];
    
    
    
    if( SC_VIDEOCONTROL == [[thisDevice lastInterfaceClassInfo] subclassNum] ) // Video Control Subclass
    {
        switch ( desc->bDescriptorSubType )
        {
            case VC_HEADER:
                pVideoControlHeader = (IOUSBVCInterfaceDescriptor *)desc;
                i = Swap16(&pVideoControlHeader->bcdVDC);
                
                // If release is less than 0x0100, then we need to display the release version
                if ( (pVideoControlHeader->bcdVDC & 0xFF00) == 0 )
                {
                    sprintf((char *)buf, "%1x%1x.%1x%1c", (i>>12)&0x000f, (i>>8)&0x000f, (i>>4)&0x000f, MapNumberToVersion((i>>0)&0x000f));
                }
                else
                {
                    sprintf((char *)buf, "%1x%1x.%1x%1c", (i>>12)&0x000f, (i>>8)&0x000f, (i>>4)&0x000f, (i>>0)&0x000f);
                }
                    
                [thisDevice addProperty:"Specification Version Number:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                sprintf((char *)buf, "%u", Swap16(&pVideoControlHeader->wTotalLength) );

                sprintf((char *)buf, "%lu", Swap32(&pVideoControlHeader->dwClockFrequency) );
                [thisDevice addProperty:"Device Clock Frequency (Hz):" withValue:buf atDepth:INTERFACE_LEVEL+1];

                sprintf((char *)buf, "%u", pVideoControlHeader->bInCollection );
                [thisDevice addProperty:"Number of Video Streaming Interfaces:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                // Haven't seen this array filled with more than 1 yet.
                //
                for (i = 0, p = &pVideoControlHeader->baInterfaceNr[0]; i < pVideoControlHeader->bInCollection; i++, p++ )
                {
                    sprintf((char *)buf, "%u", *p );
                    [thisDevice addProperty:"Video Interface Number:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                }
                break;

            case VC_INPUT_TERMINAL:
                pVideoInTermDesc = (IOUSBVCInputTerminalDescriptor *)desc;

                sprintf((char *)buf, "%u", pVideoInTermDesc->bTerminalID );
                [thisDevice addProperty:"Terminal ID" withValue:buf atDepth:INTERFACE_LEVEL+1];

                switch ( Swap16(&pVideoInTermDesc->wTerminalType) )
                {
                    case TT_VENDOR_SPECIFIC: 	s="USB vendor specific"; break;
                    case TT_STREAMING: 		s="USB streaming"; break;
                    case ITT_VENDOR_SPECIFIC: 	s="Vendor Specific Input Terminal"; break;
                    case ITT_CAMERA: 		s="Camera Sensor"; break;
                    case ITT_MEDIA_TRANSPORT_UNIT: 	s="Sequential Media"; break;
                    default: 				s="Invalid Input Terminal Type";
                }

                sprintf((char *)buf, 	"0x%x (%s)", pVideoInTermDesc->wTerminalType, s );
                [thisDevice addProperty:"Input Terminal Type:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                if( !pVideoInTermDesc->bAssocTerminal )
                    sprintf((char *)buf, "%u [NONE]", pVideoInTermDesc->bAssocTerminal );
                else
                    sprintf((char *)buf, "%u", pVideoInTermDesc->bAssocTerminal );

                [thisDevice addProperty:"Input Terminal ID:" withValue:buf atDepth:INTERFACE_LEVEL+1];


                if( !pVideoInTermDesc->iTerminal )
                {
                    sprintf((char *)buf, "%u [NONE]", pVideoInTermDesc->iTerminal );
                    [thisDevice addProperty:"Input Terminal String Index:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                }
                    else
                    {
                        [thisDevice addStringProperty:"Input Terminal String:" fromStringIndex: (UInt8)pVideoInTermDesc->iTerminal fromDeviceInterface:deviceIntf atDepth:INTERFACE_LEVEL+1];
                    }
                    
                if ( ITT_CAMERA == pVideoInTermDesc->wTerminalType )
                {
                    pCameraTermDesc = (IOUSBVCCameraTerminalDescriptor *)desc;

                    sprintf((char *)buf, "%u", Swap16(&pCameraTermDesc->wObjectiveFocalLengthMin) );
                    [thisDevice addProperty:"Minimum Focal Length" withValue:buf atDepth:INTERFACE_LEVEL+1];

                    sprintf((char *)buf, "%u", Swap16(&pCameraTermDesc->wObjectiveFocalLengthMax) );
                    [thisDevice addProperty:"Maximum Focal Length" withValue:buf atDepth:INTERFACE_LEVEL+1];

                    sprintf((char *)buf, "%u", Swap16(&pCameraTermDesc->wOcularFocalLength) );
                    [thisDevice addProperty:"Ocular Focal Length" withValue:buf atDepth:INTERFACE_LEVEL+1];

                    if ( pCameraTermDesc->bControlSize != 0 )
                    {
                        sprintf((char *)buf, "Description");
                        [thisDevice addProperty:"Controls Supported" withValue:buf atDepth:INTERFACE_LEVEL+1];
                    }

                    strcpy((char *)buf, "");

                    /*
                    // Need to swap the following bmControls field
                    //
                    switch ( pCameraTermDesc->bControlSize)
                    {
                        case 1: break;
                        case 2: Swap16(&pCameraTermDesc->bmControls); break;
                        //case 3: Swap24(&pCameraTermDesc->bmControls); break;
                        case 3:  break;
                        case 4: Swap32(&pCameraTermDesc->bmControls); break;
                    }
                    */
                    
                    for (i = 0, p = &pCameraTermDesc->bmControls[0]; i < pCameraTermDesc->bControlSize; i++, p++ )
                    {
                        // For 1.0, only 19 bits are defined....
                        // p is pointing to the first byte of a little endian field.
                        //
                        // i = 0, p -> D0-D7
                        // i = 1, p -> D8-D15
                        // i = 2, p -> D16-D23
                        // i = 4, p -> D24-D31
                        //
                        if ( i > 2 )
                        {
                            sprintf((char *)buf, "Unknown");
                            [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                        }

                        if ( (*p) & (1 << 0) )
                        {
                            if ( i == 0 ) 	sprintf((char *)buf, "Scanning Mode");
                            else if ( i == 1 ) 	sprintf((char *)buf, "Iris (Relative)");
                            else if ( i == 2 ) 	sprintf((char *)buf, "Iris (Relative)");
                            if ( strcmp(buf,"") )
                                [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                        }

                        if ( (*p) & (1 << 1) )
                        {
                            if ( i == 0 ) 	sprintf((char *)buf, "Auto Exposure Mode");
                            else if ( i == 1 ) 	sprintf((char *)buf, "Zoom (Absolute)");
                            else if ( i == 2 ) 	sprintf((char *)buf, "Tilt (Relative)");
                            if ( strcmp(buf,"") )
                                [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                        }

                        if ( (*p) & (1 << 2) )
                        {
                            if ( i == 0 ) 	sprintf((char *)buf, "Auto Exposure Priority");
                            else if ( i == 1 ) 	sprintf((char *)buf, "Zoom (Relative)");
                            else if ( i == 2 ) 	sprintf((char *)buf, "Focus, Auto");
                            if ( strcmp(buf,"") )
                                [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                        }

                        if ( (*p) & (1 << 3) )
                        {
                            if ( i == 0 ) 	sprintf((char *)buf, "Exposure Time (Absolute)");
                            else if ( i == 1 ) 	sprintf((char *)buf, "Pan (Absolute)");
                            else if ( i == 2 ) 	sprintf((char *)buf, "Unknown");
                            if ( strcmp(buf,"") )
                                [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                        }

                        if ( (*p) & (1 << 4) )
                        {
                            if ( i == 0 ) 	sprintf((char *)buf, "Exposure Time (Relative)");
                            else if ( i == 1 ) 	sprintf((char *)buf, "Pan (Relative)");
                            else if ( i == 2 ) 	sprintf((char *)buf, "Unknown");
                            if ( strcmp(buf,"") )
                                [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                        }

                        if ( (*p) & (1 << 5) )
                        {
                            if ( i == 0 ) 	sprintf((char *)buf, "Focus (Absolute)");
                            else if ( i == 1 ) 	sprintf((char *)buf, "Roll (Absolute)");
                            else if ( i == 2 ) 	sprintf((char *)buf, "Unknown");
                            if ( strcmp(buf,"") )
                                [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                        }

                        if ( (*p) & (1 << 6) )
                        {
                            if ( i == 0 ) 	sprintf((char *)buf, "Focus (Relative)");
                            else if ( i == 1 ) 	sprintf((char *)buf, "Roll (Relative)");
                            else if ( i == 2 ) 	sprintf((char *)buf, "Unknown");
                            if ( strcmp(buf,"") )
                                [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                        }

                        if ( (*p) & (1 << 7) )
                        {
                            if ( i == 0 ) 	sprintf((char *)buf, "Iris (Absolute)");
                            else if ( i == 1 ) 	sprintf((char *)buf, "Tilt (Absolute)");
                            else if ( i == 2 ) 	sprintf((char *)buf, "Unknown");
                            if ( strcmp(buf,"") )
                                [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                        }

                    }
                }
                break;

            case VC_OUTPUT_TERMINAL:
                pVideoOutTermDesc = (IOUSBVCOutputTerminalDescriptor *)desc;

                sprintf((char *)buf, "%u", pVideoOutTermDesc->bTerminalID );
                [thisDevice addProperty:"Terminal ID:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                switch ( Swap16(&pVideoOutTermDesc->wTerminalType) )
                {
                    case TT_VENDOR_SPECIFIC: 	s="USB vendor specific"; break;
                    case TT_STREAMING: 		s="USB streaming"; break;
                    case OTT_VENDOR_SPECIFIC: 	s="USB vendor specific"; break;
                    case OTT_DISPLAY: 		s="Generic Display"; break;
                    case OTT_MEDIA_TRANSPORT_OUTPUT: 	s="Sequential Media Output Terminal"; break;
                    default: 				s="Invalid Output Terminal Type";
                }

                sprintf((char *)buf, 	"0x%x (%s)", pVideoOutTermDesc->wTerminalType, s );
                [thisDevice addProperty:"Output Terminal Type:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                if( !pVideoOutTermDesc->bAssocTerminal )
                    sprintf((char *)buf, "%u [NONE]", pVideoOutTermDesc->bAssocTerminal );
                else
                    sprintf((char *)buf, "%u", pVideoOutTermDesc->bAssocTerminal );

                [thisDevice addProperty:"Output Terminal ID:" withValue:buf atDepth:INTERFACE_LEVEL+1];


                if( !pVideoOutTermDesc->iTerminal )
                {
                    sprintf((char *)buf, "%u [NONE]", pVideoOutTermDesc->iTerminal );
                    [thisDevice addProperty:"Output Terminal String Index:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                }
                    else
                    {
                        [thisDevice addStringProperty:"Output Terminal String:" fromStringIndex: (UInt8)pVideoOutTermDesc->iTerminal fromDeviceInterface:deviceIntf atDepth:INTERFACE_LEVEL+1];
                    }
                break;

            case VC_SELECTOR_UNIT:
                pSelectorUnitDesc = (IOUSBVCSelectorUnitDescriptor *)desc;
                sprintf((char *)buf, 	"%u", pSelectorUnitDesc->bUnitID );
                [thisDevice addProperty:"Unit ID:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                sprintf((char *)buf, 	"%u", pSelectorUnitDesc->bNrInPins );
                [thisDevice addProperty:"Number of pins:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                //
                for (i = 0, p = &pSelectorUnitDesc->baSourceID[0]; i < pSelectorUnitDesc->bNrInPins; i++, p++ )
                {
                    sprintf((char *)buf2,	"Source ID Pin[%d]:", i);
                    sprintf((char *)buf, "%u", *p );
                    [thisDevice addProperty:buf2 withValue:buf atDepth:INTERFACE_LEVEL+1];
                }

                // Now, p will point to the IOUSBVCSelectorUnit2Descriptor
                //
                pSelectorUnit2Desc = (IOUSBVCSelectorUnit2Descriptor *) p;
                if( !pSelectorUnit2Desc->iSelector )
                {
                    sprintf((char *)buf, "%u [NONE]", pSelectorUnit2Desc->iSelector );
                    [thisDevice addProperty:"Selector Unit String Index:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                }
                else
                {
                    [thisDevice addStringProperty:"Selector Unit String:" fromStringIndex: (UInt8)pSelectorUnit2Desc->iSelector fromDeviceInterface:deviceIntf atDepth:INTERFACE_LEVEL+1];
                }
                    
                break;

            case VC_PROCESSING_UNIT:
                pProcessingUnitDesc = ( IOUSBVCProcessingUnitDescriptor *) desc;
                sprintf((char *)buf, 	"%u", pProcessingUnitDesc->bUnitID );
                [thisDevice addProperty:"Unit ID:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                sprintf((char *)buf, 	"%u", pProcessingUnitDesc->bSourceID );
                [thisDevice addProperty:"Source ID:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                sprintf((char *)buf, 	"%u", Swap16(&pProcessingUnitDesc->wMaxMultiplier) );
                [thisDevice addProperty:"Digital Multiplier (100X):" withValue:buf atDepth:INTERFACE_LEVEL+1];

                if ( pProcessingUnitDesc->bControlSize != 0 )
                {
                    sprintf((char *)buf, "Description");
                    [thisDevice addProperty:"Controls Supported" withValue:buf atDepth:INTERFACE_LEVEL+1];
                }

                strcpy((char *)buf, "");

                /*
                // Need to swap the following bmControls field
                //
                switch ( pProcessingUnitDesc->bControlSize)
                {
                    case 1: break;
                    case 2: Swap16(&pProcessingUnitDesc->bmControls); break;
                    case 3: Swap24(&pCameraTermDesc->bmControls); break;
                    case 4: Swap32(&pProcessingUnitDesc->bmControls); break;
                }
                */
                
                for (i = 0, p = &pProcessingUnitDesc->bmControls[0]; i < pProcessingUnitDesc->bControlSize; i++, p++ )
                {
                    // For 1.0, only 16 bits are defined:
                    //
                    // p is pointing to the first byte of a little endian field.
                    //
                    // i = 0, p -> D0-D7
                    // i = 1, p -> D8-D15
                    // i = 2, p -> D16-D23
                    // i = 4, p -> D24-D31
                    //

                    if ( (*p) & (1 << 0) )
                    {
                        if ( i == 0 ) 		sprintf((char *)buf, "Brightness");
                        else if ( i == 1 ) 	sprintf((char *)buf, "Backlight Compensation");
                        if ( strcmp(buf,"") )
                            [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                    }

                    if ( (*p) & (1 << 1) )
                    {
                        if ( i == 0 ) 		sprintf((char *)buf, "Contrast");
                        else if ( i == 1 ) 	sprintf((char *)buf, "Gain");
                        if ( strcmp(buf,"") )
                            [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                    }

                    if ( (*p) & (1 << 2) )
                    {
                        if ( i == 0 ) 		sprintf((char *)buf, "Hue");
                        else if ( i == 1 ) 	sprintf((char *)buf, "Power Line Frequency");
                        if ( strcmp(buf,"") )
                            [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                    }

                    if ( (*p) & (1 << 3) )
                    {
                        if ( i == 0 ) 		sprintf((char *)buf, "Saturation");
                        else if ( i == 1 ) 	sprintf((char *)buf, "Hue, Auto");
                        if ( strcmp(buf,"") )
                            [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                    }

                    if ( (*p) & (1 << 4) )
                    {
                        if ( i == 0 ) 		sprintf((char *)buf, "Sharpness");
                        else if ( i == 1 ) 	sprintf((char *)buf, "White Balance Temperature, Auto");
                        if ( strcmp(buf,"") )
                            [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                    }

                    if ( (*p) & (1 << 5) )
                    {
                        if ( i == 0 ) 		sprintf((char *)buf, "Gamma");
                        else if ( i == 1 ) 	sprintf((char *)buf, "White Balance Component, Auto");
                        if ( strcmp(buf,"") )
                            [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                    }

                    if ( (*p) & (1 << 6) )
                    {
                        if ( i == 0 ) 		sprintf((char *)buf, "White Balance Temperature");
                        else if ( i == 1 ) 	sprintf((char *)buf, "Digital Multiplier");
                        if ( strcmp(buf,"") )
                            [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                    }

                    if ( (*p) & (1 << 7) )
                    {
                        if ( i == 0 ) 		sprintf((char *)buf, "White Balance Component");
                        else if ( i == 1 ) 	sprintf((char *)buf, "Digital Multiplier Limit");
                        if ( strcmp(buf,"") )
                            [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                    }

                }

                // At this point, p should be pointing to the iProcessing field:
                pProcessingUnit2Desc = (IOUSBVCProcessingUnit2Descriptor *) p;
                if( !pProcessingUnit2Desc->iProcessing )
                {
                    sprintf((char *)buf, "%u [NONE]", pProcessingUnit2Desc->iProcessing );
                    [thisDevice addProperty:"Processing Unit String Index:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                }
                    else
                    {
                        [thisDevice addStringProperty:"Selector Unit String:" fromStringIndex: (UInt8)pProcessingUnit2Desc->iProcessing fromDeviceInterface:deviceIntf atDepth:INTERFACE_LEVEL+1];
                    }
                    break;

            case VC_EXTENSION_UNIT:
                sprintf((char *)buf, 	"%u", desc->bLength );
                [thisDevice addProperty:"bLength:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                sprintf((char *)buf, 	"%u", desc->bDescriptorType );
                [thisDevice addProperty:"bDescriptorType:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                sprintf((char *)buf, 	"%u", desc->bDescriptorSubType );
                [thisDevice addProperty:"bDescriptorSubType:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                pExtensionUnitDesc = (IOUSBVCExtensionUnitDescriptor *)desc;

                sprintf((char *)buf, 	"%u", pExtensionUnitDesc->bUnitID );
                [thisDevice addProperty:"Unit ID:" withValue:buf atDepth:INTERFACE_LEVEL+1];

				data1 = USBToHostLong(* (UInt32 *) &pExtensionUnitDesc->guidFormat[0]);
				data2 = USBToHostWord(* (UInt16 *) &pExtensionUnitDesc->guidFormat[4]);
				data3 = USBToHostWord(* (UInt16 *) &pExtensionUnitDesc->guidFormat[6]);
				uuidLO = NXSwapBigLongLongToHost(* (UInt64 *) &pExtensionUnitDesc->guidFormat[8]);
				
				
				sprintf((char *)buf, 	"%8.8lx-%4.4x-%4.4x-%4.4lx-%12.12qx", data1, data2, data3, 
						(UInt32) ( (uuidLO & 0xffff000000000000ULL)>>48), (uuidLO & 0x0000FFFFFFFFFFFFULL) );
                [thisDevice addProperty:"Vendor UUID:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                sprintf((char *)buf, 	"%u", pExtensionUnitDesc->bNumControls );
                [thisDevice addProperty:"Number of Controls:" withValue:buf atDepth:INTERFACE_LEVEL+1];


                sprintf((char *)buf, 	"%u", pExtensionUnitDesc->bNrInPins );
                [thisDevice addProperty:"Number of In pins:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                for (i = 0, p = &pExtensionUnitDesc->baSourceID[0]; i < pExtensionUnitDesc->bNrInPins; i++, p++ )
                {
                    sprintf((char *)buf2,	"Source ID Pin[%d]:", i);
                    sprintf((char *)buf, "%u", *p );
                    [thisDevice addProperty:buf2 withValue:buf atDepth:INTERFACE_LEVEL+1];
                }

                // Now, p points to the rest of the Extension Unit descriptor:
                //
                pExtensionUnit2Desc = ( IOUSBVCExtensionUnit2Descriptor *) p;

                if ( pExtensionUnit2Desc->bControlSize != 0 )
                {
                    sprintf((char *)buf, "Description");
                    [thisDevice addProperty:"Controls Supported" withValue:buf atDepth:INTERFACE_LEVEL+1];
                }

                strcpy((char *)buf, "");
                for (i = 0, p = &pExtensionUnit2Desc->bmControls[0]; i < pExtensionUnit2Desc->bControlSize; i++, p++ )
                {
                    // For 1.0, all bits are vendor specific:
                    //
                    sprintf((char *)buf, "Vendor Specific Byte[i] = 0x%x", (*p));
                    [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];

                }

                // At this point, p should be pointing to the iProcessing field:
                pExtensionUnit3Desc = ( IOUSBVCExtensionUnit3Descriptor *) p;
                if( !pExtensionUnit3Desc->iExtension )
                {
                    sprintf((char *)buf, "%u [NONE]", pExtensionUnit3Desc->iExtension );
                    [thisDevice addProperty:"Processing Unit String Index:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                }
                    else
                    {
                        [thisDevice addStringProperty:"Selector Unit String:" fromStringIndex: (UInt8)pExtensionUnit3Desc->iExtension fromDeviceInterface:deviceIntf atDepth:INTERFACE_LEVEL+1];
                    }
                    break;
            case VC_DESCRIPTOR_UNDEFINED:
            default:
                [thisDevice addProperty:"Undefined Descriptor:" withValue:"" atDepth:INTERFACE_LEVEL+1];
    

        }
    }
    else if( SC_VIDEOSTREAMING == [[thisDevice lastInterfaceClassInfo] subclassNum] ) // Video Streaming Subclass
    {
        switch ( desc->bDescriptorSubType )
                {
                    case VS_INPUT_HEADER:
                        pVSInputHeaderDesc = (IOUSBVSInputHeaderDescriptor *)desc;

                        sprintf((char *)buf, "%u", pVSInputHeaderDesc->bNumFormats );
                        [thisDevice addProperty:"bNumFormats:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "0x%x (%u)", Swap16(&pVSInputHeaderDesc->wTotalLength), pVSInputHeaderDesc->wTotalLength );
                        [thisDevice addProperty:"wTotalLength:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "0x%x", pVSInputHeaderDesc->bEndpointAddress );
                        [thisDevice addProperty:"bEndpointAddress:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "Capabilities (0x%x)", pVSInputHeaderDesc->bmInfo );
                        [thisDevice addProperty:buf withValue:"bmInfo" atDepth:INTERFACE_LEVEL+1];

                        if (pVSInputHeaderDesc->bmInfo & 0x01)
                            [thisDevice addProperty:"" withValue:"Dynamic Format Change supported" atDepth:INTERFACE_LEVEL+2];

                        if ( pVSInputHeaderDesc->bmInfo & 0xfe)
                            [thisDevice addProperty:"" withValue:"Unknown capabilities" atDepth:INTERFACE_LEVEL+2];

                        sprintf((char *)buf, "%u", pVSInputHeaderDesc->bTerminalLink );
                        [thisDevice addProperty:"bTerminalLink:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        switch (pVSInputHeaderDesc->bStillCaptureMethod)
                        {
                            case 0: s = "None"; break;
                            case 1: s = "Method 1"; break;
                            case 2: s = "Method 2"; break;
                            case 3: s = "Method 3"; break;
                            default: s = "Unknown Method";
                        }
                        sprintf((char *)buf, 	"%u (%s)", pVSInputHeaderDesc->bStillCaptureMethod, s );
                        [thisDevice addProperty:"bStillCaptureMethod:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
				sprintf((char *)buf, "%s", pVSInputHeaderDesc->bTriggerSupport ? "1 (Supported)" : "0 (Not Supported)");
                        [thisDevice addProperty:"bTriggerSupport" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
				if ( pVSInputHeaderDesc->bTriggerSupport )
				{
					sprintf((char *)buf, "%s", pVSInputHeaderDesc->bTriggerUsage ? "1 (General Purpose)" : "(0) Initiate Still Image Capture");
				}
					else
					{
						sprintf((char *)buf, "%s", "Ignored because bTriggerSupport is 0");
					}
                        [thisDevice addProperty:"bTriggerUsage" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%x", pVSInputHeaderDesc->bControlSize );
				
                        [thisDevice addProperty:"bControlSize:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        for (j = 0; j < pVSInputHeaderDesc->bNumFormats; j++ )
                        {
                            strcpy((char *)buf, "");
                            strcpy((char *)buf2, "");
                            for (i = 0, p = &pVSInputHeaderDesc->bmControls[0]; i < pVSInputHeaderDesc->bControlSize; i++, p++ )
                            {
                                sprintf((char *)buf, "0x%x", *p );
                                strcat( buf2, buf);
                            }
                            sprintf((char *)buf, "bmaControls( Format %d):", j+1 );
                            
                            [thisDevice addProperty:buf withValue:buf2 atDepth:INTERFACE_LEVEL+1];
                            
                            strcpy((char *)buf, "");
                            for (i = 0, p = &pVSInputHeaderDesc->bmControls[0]; i < pVSInputHeaderDesc->bControlSize; i++, p++ )
                            {
                                if ( (*p) & (1 << 0) )
                                {
                                    if ( i == 0 )       sprintf((char *)buf, "Key Frame Rate");
                                    if ( strcmp(buf,"") )
                                        [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                                }
                                
                                if ( (*p) & (1 << 1) )
                                {
                                    if ( i == 0 ) 		sprintf((char *)buf, "P frame Rate");
                                    if ( strcmp(buf,"") )
                                        [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                                }
                                
                                if ( (*p) & (1 << 2) )
                                {
                                    if ( i == 0 ) 		sprintf((char *)buf, "Compression quality");
                                    if ( strcmp(buf,"") )
                                        [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                                }
                                
                                if ( (*p) & (1 << 3) )
                                {
                                    if ( i == 0 ) 		sprintf((char *)buf, "Compression window size");
                                    if ( strcmp(buf,"") )
                                        [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                                }
                                
                                if ( (*p) & (1 << 4) )
                                {
                                    if ( i == 0 ) 		sprintf((char *)buf, "Generate Key Frame");
                                    if ( strcmp(buf,"") )
                                        [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                                }
                                
                                if ( (*p) & (1 << 5) )
                                {
                                    if ( i == 0 ) 		sprintf((char *)buf, "Update Frame Segment");
                                    if ( strcmp(buf,"") )
                                        [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                                }
                                
                                if ( (*p) & (1 << 6) )
                                {
                                     sprintf((char *)buf, "Unknown");
                                    if ( strcmp(buf,"") )
                                        [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                                }
                                
                                if ( (*p) & (1 << 7) )
                                {
                                    sprintf((char *)buf, "Unknown");
                                    if ( strcmp(buf,"") )
                                        [thisDevice addProperty:"" withValue:buf atDepth:INTERFACE_LEVEL+2];
                                }
                                
                            }
                        }
                            break;
                    case VS_OUTPUT_HEADER:
                        pVSOutputHeaderDesc = (IOUSBVSOutputHeaderDescriptor *)desc;

                        sprintf((char *)buf, "%u", pVSOutputHeaderDesc->bNumFormats);
                        [thisDevice addProperty:"Number of Formats:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "%u", Swap16(&pVSOutputHeaderDesc->wTotalLength) );
                        [thisDevice addProperty:"Total Length of Descriptor:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "0x%x", pVSOutputHeaderDesc->bEndpointAddress );
                        [thisDevice addProperty:"Endpoint Address:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "%u", pVSOutputHeaderDesc->bTerminalLink );
                        [thisDevice addProperty:"Terminal ID of Output Terminal:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        break;
                    case VS_FORMAT_MJPEG:
                        pMJPEGFormatDesc = (IOUSBVDC_MJPEGFormatDescriptor *)desc;

                        sprintf((char *)buf, "0x%x", pMJPEGFormatDesc->bFormatIndex);
                        [thisDevice addProperty:"bFormatIndex:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "0x%x", pMJPEGFormatDesc->bNumFrameDescriptors);
                        [thisDevice addProperty:"bNumFrameDescriptors:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "(0x%x)", pMJPEGFormatDesc->bmFlags );
                        [thisDevice addProperty:"bmFlags" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        if (pMJPEGFormatDesc->bmFlags & 0x01)
                            [thisDevice addProperty:"" withValue:"Fixed Sample Sizes Supported" atDepth:INTERFACE_LEVEL+2];

                        if ( pMJPEGFormatDesc->bmFlags & 0xfe)
                            [thisDevice addProperty:"" withValue:"Unknown characteristics" atDepth:INTERFACE_LEVEL+2];

                        sprintf((char *)buf, "0x%x", pMJPEGFormatDesc->bDefaultFrameIndex);
                        [thisDevice addProperty:"bDefaultFrameIndex:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "0x%x", pMJPEGFormatDesc->bAspectRatioX);
                        [thisDevice addProperty:"bAspectRatioX:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "0x%x", pMJPEGFormatDesc->bAspectRatioY);
                        [thisDevice addProperty:"bAspectRatioY:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "(0x%x)", pMJPEGFormatDesc->bmInterlaceFlags );
                        [thisDevice addProperty:"bmInterlaceFlags" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        if (pMJPEGFormatDesc->bmInterlaceFlags & 0x01)
                            [thisDevice addProperty:"Interlaced Stream or Variable" withValue:"Yes" atDepth:INTERFACE_LEVEL+2];
                            else
                                [thisDevice addProperty:"Interlaced Stream or Variable" withValue:"No" atDepth:INTERFACE_LEVEL+2];

                        if (pMJPEGFormatDesc->bmInterlaceFlags & 0x02)
                            [thisDevice addProperty:"Fields per frame" withValue:"2" atDepth:INTERFACE_LEVEL+2];
                        else
                            [thisDevice addProperty:"Fields per frame" withValue:"1" atDepth:INTERFACE_LEVEL+2];

                        if (pMJPEGFormatDesc->bmInterlaceFlags & 0x04)
                            [thisDevice addProperty:"Field 1 first" withValue:"Yes" atDepth:INTERFACE_LEVEL+2];
                        else
                            [thisDevice addProperty:"Field 1 first" withValue:"No" atDepth:INTERFACE_LEVEL+2];

                        if (pMJPEGFormatDesc->bmInterlaceFlags & 0x08)
                            [thisDevice addProperty:"" withValue:"Reserved field used!" atDepth:INTERFACE_LEVEL+2];

                        i = (pMJPEGFormatDesc->bmInterlaceFlags & 0x30) >> 4;
                        switch (i)
                        {
                            case 0: s = "Field 1 only"; break;
                            case 1: s = "Field 2 only"; break;
                            case 2: s = "Regular pattern of fields 1 and 2"; break;
                            case 3: s = "Random pattern of fields 1 and 2"; break;
                        }
                            sprintf((char *)buf, 	"%s", s );
                        [thisDevice addProperty:"Field Pattern" withValue:buf atDepth:INTERFACE_LEVEL+2];

                        i = (pMJPEGFormatDesc->bmInterlaceFlags & 0xc0) >> 6;
                        switch (i)
                        {
                            case 0: s = "Bob only"; break;
                            case 1: s = "Weave only"; break;
                            case 2: s = "Bob or weave"; break;
                            case 3: s = "Unknown"; break;
                        }
                        sprintf((char *)buf, 	"%s", s );
                        [thisDevice addProperty:"Display Mode" withValue:buf atDepth:INTERFACE_LEVEL+2];

                        if (pMJPEGFormatDesc->bCopyProtect == 1)
                            [thisDevice addProperty:"bCopyProtect:" withValue:"Restricted" atDepth:INTERFACE_LEVEL+1];
                        else
                            [thisDevice addProperty:"bCopyProtect" withValue:"No Restriction" atDepth:INTERFACE_LEVEL+1];
                        break;

                    case VS_FRAME_MJPEG:
                        pMJPEGFrameDesc = (IOUSBVDC_MJPEGFrameDescriptor *)desc;

                        sprintf((char *)buf, "%u", pMJPEGFrameDesc->bFrameIndex);
                        [thisDevice addProperty:"bFrameIndex:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "(0x%x)", pMJPEGFrameDesc->bmCapabilities );
                        [thisDevice addProperty:"bmCapabilities " withValue:buf atDepth:INTERFACE_LEVEL+1];

                        if (pMJPEGFrameDesc->bmCapabilities & 0x01)
                            [thisDevice addProperty:"" withValue:"Still Image supported" atDepth:INTERFACE_LEVEL+2];

                        if ( pMJPEGFrameDesc->bmCapabilities & 0xfe)
                            [thisDevice addProperty:"" withValue:"Unknown capabilities" atDepth:INTERFACE_LEVEL+2];

                            sprintf((char *)buf, "0x%x (%u)", Swap16(&pMJPEGFrameDesc->wWidth), pMJPEGFrameDesc->wWidth);
                        [thisDevice addProperty:"wWidth:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "0x%x (%u)", Swap16(&pMJPEGFrameDesc->wHeight), pMJPEGFrameDesc->wHeight);
                        [thisDevice addProperty:"wHeight:" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "0x%lx (%lu)", Swap32(&pMJPEGFrameDesc->dwMinBitRate), pMJPEGFrameDesc->dwMinBitRate);
                        [thisDevice addProperty:"dwMinBitRate (bps):" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "0x%lx (%lu)", Swap32(&pMJPEGFrameDesc->dwMaxBitRate), pMJPEGFrameDesc->dwMaxBitRate);
                        [thisDevice addProperty:"dwMaxBitRate (bps):" withValue:buf atDepth:INTERFACE_LEVEL+1];

                        sprintf((char *)buf, "0x%lx (%lu)", Swap32(&pMJPEGFrameDesc->dwMaxVideoFrameBufferSize), pMJPEGFrameDesc->dwMaxVideoFrameBufferSize);
                        [thisDevice addProperty:"dwMaxVideoFrameBufferSize (bytes):" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%lx (%8.3f ms)", Swap32(&pMJPEGFrameDesc->dwDefaultFrameInterval), (double) ( pMJPEGFrameDesc->dwDefaultFrameInterval / 10000) );
                        [thisDevice addProperty:"dwDefaultFrameInterval:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        if (pMJPEGFrameDesc->bFrameIntervalType == 0)
                        {
                            [thisDevice addProperty:"bFrameIntervalType:" withValue:"Continuous" atDepth:INTERFACE_LEVEL+1];

                            sprintf((char *)buf, "0x%lx (%8.3f ms)", Swap32(&pMJPEGFrameDesc->dwMinFrameInterval), (double) ( pMJPEGFrameDesc->dwMinFrameInterval / 10000) );
                            [thisDevice addProperty:"dwMinFrameInterval:" withValue:buf atDepth:INTERFACE_LEVEL+2];

                            sprintf((char *)buf, "0x%lx (%8.3f ms)", Swap32(&pMJPEGFrameDesc->dwMaxFrameInterval), (double) ( pMJPEGFrameDesc->dwMaxFrameInterval / 10000) );
                            [thisDevice addProperty:"dwMaxFrameInterval:" withValue:buf atDepth:INTERFACE_LEVEL+2];

                            sprintf((char *)buf, "0x%lx (%8.3f ms)", Swap32(&pMJPEGFrameDesc->dwFrameIntervalStep), (double) ( pMJPEGFrameDesc->dwFrameIntervalStep / 10000) );
                            [thisDevice addProperty:"dwFrameIntervalStep:" withValue:buf atDepth:INTERFACE_LEVEL+2];

                        }
                        else
                        {
                            // Need to recast as a IOUSBVDC_MJPEGDiscreteFrameDescriptor
                            //
                            pMJPEGDiscreteFrameDesc = (IOUSBVDC_MJPEGDiscreteFrameDescriptor *) pMJPEGFrameDesc;
                            sprintf((char *)buf, "%u", (pMJPEGDiscreteFrameDesc->bFrameIntervalType));
                            [thisDevice addProperty:"Discrete Frame Intervals supported" withValue:buf atDepth:INTERFACE_LEVEL+1];
                            for (i = 0, t = &pMJPEGDiscreteFrameDesc->dwFrameInterval[0]; i < pMJPEGDiscreteFrameDesc->bFrameIntervalType; i++, t++ )
                            {
                                UInt32 interval = *t;
                                sprintf((char *)buf, "0x%lx (%8.3f ms)", Swap32(&interval), (double) (interval / 10000));
                                sprintf((char *)buf2, "dwFrameInterval[%u] (100 ns)", i+1 );
                                [thisDevice addProperty:buf2 withValue:buf atDepth:INTERFACE_LEVEL+2];
                            }
                        }
                        break;
                        
                    case VS_COLORFORMAT:
                        pColorFormatDesc = (IOUSBVDC_ColorFormatDescriptor *)desc;
                        
                        switch (pColorFormatDesc->bColorPrimaries)
                        {
                            case 0:  s = "Unspecified (Image characteristic unknown)"; break;
                            case 1:  s = "BT.709, sRGB (default)"; break;
                            case 2:  s = "BT.470-2(M)"; break;
                            case 3:  s = "BT.470-2(B,G)"; break;
                            case 4:  s = "SMPTE 170M"; break;
                            case 5:  s = "SMPTE 240M"; break;
                            default: s = "reserved"; break;
                        }
                            
                            sprintf((char *) buf, "%u ( %s )", pColorFormatDesc->bColorPrimaries, s);
                        [thisDevice addProperty:"Color Primaries:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        switch (pColorFormatDesc->bTransferCharacteristics)
                        {
                            case 0:  s = "Unspecified (Image characteristic unknown)"; break;
                            case 1:  s = "BT.709 (default)"; break;
                            case 2:  s = "BT.470-2(M)"; break;
                            case 3:  s = "BT.470-2(B,G)"; break;
                            case 4:  s = "SMPTE 170M"; break;
                            case 5:  s = "SMPTE 240M"; break;
                            case 6:  s = "Linear ( V = Lc)"; break;
                            case 7:  s = "sRGB (very similar to BT.709)"; break;
                            default: s = "reserved"; break;
                        }
                            
                        sprintf((char *) buf, "%u ( %s )", pColorFormatDesc->bTransferCharacteristics, s);
                        [thisDevice addProperty:"Transfer Characteristics:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        switch (pColorFormatDesc->bMatrixCoefficients)
                        {
                            case 0:  s = "Unspecified (Image characteristic unknown)"; break;
                            case 1:  s = "BT.709"; break;
                            case 2:  s = "FCC"; break;
                            case 3:  s = "BT.470-2(B,G)"; break;
                            case 4:  s = "SMPTE 170M (BT.601, default)"; break;
                            case 5:  s = "SMPTE 240M"; break;
                            default: s = "reserved"; break;
                        }
                            
                        sprintf((char *) buf, "%u ( %s )", pColorFormatDesc->bMatrixCoefficients, s);
                        [thisDevice addProperty:"Matrix Coefficients:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        
                        break;
                        
                    case VS_FORMAT_UNCOMPRESSED:
                        pUncompressedFormatDesc = (IOUSBVDC_UncompressedFormatDescriptor *)desc;
                        
                        sprintf((char *)buf, "0x%x", pUncompressedFormatDesc->bFormatIndex);
                        [thisDevice addProperty:"bFormatIndex:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%x", pUncompressedFormatDesc->bNumFrameDescriptors);
                        [thisDevice addProperty:"bNumFrameDescriptors:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        // Decode the GUID per section 2.9 of the FAQ
                        //
                        data1 = USBToHostLong(* (UInt32 *) &pUncompressedFormatDesc->guidFormat[0]);
                        data2 = USBToHostWord(* (UInt16 *) &pUncompressedFormatDesc->guidFormat[4]);
                        data3 = USBToHostWord(* (UInt16 *) &pUncompressedFormatDesc->guidFormat[6]);
						uuidLO = NXSwapBigLongLongToHost(* (UInt64 *) &pUncompressedFormatDesc->guidFormat[8]);

                        
                        sprintf((char *)buf, 	"%8.8lx-%4.4x-%4.4x-%4.4lx-%12.12qx", data1, data2, data3, 
                                (UInt32) ( (uuidLO & 0xffff000000000000ULL)>>48), (uuidLO & 0x0000FFFFFFFFFFFFULL) );
                        [thisDevice addProperty:"Format GUID:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%x (%u)", pUncompressedFormatDesc->bBitsPerPixel,pUncompressedFormatDesc->bBitsPerPixel);
                        [thisDevice addProperty:"bBitsPerPixel:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%x", pUncompressedFormatDesc->bDefaultFrameIndex);
                        [thisDevice addProperty:"bDefaultFrameIndex:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%x", pUncompressedFormatDesc->bAspectRatioX);
                        [thisDevice addProperty:"bAspectRatioX:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%x", pUncompressedFormatDesc->bAspectRatioY);
                        [thisDevice addProperty:"bAspectRatioY:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "(0x%x)", pUncompressedFormatDesc->bmInterlaceFlags );
                        [thisDevice addProperty:"bmInterlaceFlags" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        if (pUncompressedFormatDesc->bmInterlaceFlags & 0x01)
                            [thisDevice addProperty:"Interlace Stream or variable" withValue:"YES" atDepth:INTERFACE_LEVEL+2];
                        else
                            [thisDevice addProperty:"Interlace Stream or variable" withValue:"NO" atDepth:INTERFACE_LEVEL+2];
                        
                        if (pUncompressedFormatDesc->bmInterlaceFlags & 0x02)
                            [thisDevice addProperty:"Fields per Frame" withValue:"1" atDepth:INTERFACE_LEVEL+2];
                        else
                            [thisDevice addProperty:"Fields per Frame" withValue:"2" atDepth:INTERFACE_LEVEL+2];
                        
                        if (pUncompressedFormatDesc->bmInterlaceFlags & 0x04)
                            [thisDevice addProperty:"Field 1 First" withValue:"Yes" atDepth:INTERFACE_LEVEL+2];
                        else
                            [thisDevice addProperty:"Field 1 First" withValue:"No" atDepth:INTERFACE_LEVEL+2];
                        
                        
                        if (pUncompressedFormatDesc->bmInterlaceFlags & 0x08)
                            [thisDevice addProperty:"" withValue:"Reserved field used" atDepth:INTERFACE_LEVEL+2];
                            
                            switch ( pUncompressedFormatDesc->bmInterlaceFlags & 0x30 )
                            {
                                case 0: [thisDevice addProperty:"Field Pattern" withValue:"Field 1 only" atDepth:INTERFACE_LEVEL+2]; break;
                                case 1: [thisDevice addProperty:"Field Pattern" withValue:"Field 2 only" atDepth:INTERFACE_LEVEL+2]; break;
                                case 2: [thisDevice addProperty:"Field Pattern" withValue:"Regular pattern of fields 1 and 2" atDepth:INTERFACE_LEVEL+2]; break;
                                case 3: [thisDevice addProperty:"Field Pattern" withValue:"Random pattern of fields 1 and 2" atDepth:INTERFACE_LEVEL+2]; break;
                            }
                                
                                switch ( pUncompressedFormatDesc->bmInterlaceFlags & 0xc0 )
                                {
                                    case 0: [thisDevice addProperty:"Display Mode" withValue:"Bob only" atDepth:INTERFACE_LEVEL+2]; break;
                                    case 1: [thisDevice addProperty:"Display Mode" withValue:"Weave only" atDepth:INTERFACE_LEVEL+2]; break;
                                    case 2: [thisDevice addProperty:"Display Mode" withValue:"Bob or Weave" atDepth:INTERFACE_LEVEL+2]; break;
                                    case 3: [thisDevice addProperty:"Display Mode" withValue:"Undefined" atDepth:INTERFACE_LEVEL+2]; break;
                                }
                        
                        sprintf((char *)buf, "0x%x", pUncompressedFormatDesc->bCopyProtect);
                        if (pUncompressedFormatDesc->bCopyProtect)
                            [thisDevice addProperty:"Copy Protection:" withValue:"Restrict Duplication" atDepth:INTERFACE_LEVEL+1];
                        else
                            [thisDevice addProperty:"Copy Protection:" withValue:"No Restrictions" atDepth:INTERFACE_LEVEL+1];
                        break;
                        
                    case VS_FRAME_UNCOMPRESSED:
                        pUncompressedFrameDesc = (IOUSBVDC_UncompressedFrameDescriptor *)desc;
                        
                        sprintf((char *)buf, "0x%x", pUncompressedFrameDesc->bFrameIndex);
                        [thisDevice addProperty:"bFrameIndex:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "(0x%x)", pUncompressedFrameDesc->bmCapabilities );
                        [thisDevice addProperty:"bmCapabilities " withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        if (pUncompressedFrameDesc->bmCapabilities & 0x01)
                            [thisDevice addProperty:"" withValue:"Still Image supported" atDepth:INTERFACE_LEVEL+2];
                            
                            if ( pUncompressedFrameDesc->bmCapabilities & 0xfe)
                                [thisDevice addProperty:"" withValue:"Unknown capabilities" atDepth:INTERFACE_LEVEL+2];
                                
                        sprintf((char *)buf, "0x%x (%u)", Swap16(&pUncompressedFrameDesc->wWidth), pUncompressedFrameDesc->wWidth);
                        [thisDevice addProperty:"wWidth:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%x (%u)", Swap16(&pUncompressedFrameDesc->wHeight), pUncompressedFrameDesc->wHeight);
                        [thisDevice addProperty:"wHeight:" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%lx (%lu)", Swap32(&pUncompressedFrameDesc->dwMinBitRate), pUncompressedFrameDesc->dwMinBitRate);
                        [thisDevice addProperty:"dwMinBitRate (bps):" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%lx (%lu)", Swap32(&pUncompressedFrameDesc->dwMaxBitRate), pUncompressedFrameDesc->dwMaxBitRate);
                        [thisDevice addProperty:"dwMaxBitRate (bps):" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%lx (%lu)", Swap32(&pUncompressedFrameDesc->dwMaxVideoFrameBufferSize), pUncompressedFrameDesc->dwMaxVideoFrameBufferSize);
                        [thisDevice addProperty:"dwMaxVideoFrameBufferSize (bytes):" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        sprintf((char *)buf, "0x%lx (%8.3f ms)", Swap32(&pUncompressedFrameDesc->dwDefaultFrameInterval), (double) (pUncompressedFrameDesc->dwDefaultFrameInterval / 10000));
                        [thisDevice addProperty:"dwDefaultFrameInterval (100 ns):" withValue:buf atDepth:INTERFACE_LEVEL+1];
                        
                        if (pUncompressedFrameDesc->bFrameIntervalType == 0)
                        {
                            [thisDevice addProperty:"bFrameIntervalType:" withValue:"Continuous" atDepth:INTERFACE_LEVEL+1];
                            
                            sprintf((char *)buf, "0x%lx (%8.3f ms)", Swap32(&pUncompressedFrameDesc->dwMinFrameInterval), (double ) (pUncompressedFrameDesc->dwMinFrameInterval / 100000) );
                            [thisDevice addProperty:"dwMinFrameInterval (100 ns):" withValue:buf atDepth:INTERFACE_LEVEL+2];
                            
                            sprintf((char *)buf, "0x%lx (%8.3f ms)", Swap32(&pUncompressedFrameDesc->dwMaxFrameInterval),  (double ) (pUncompressedFrameDesc->dwMaxFrameInterval / 100000) );
                            [thisDevice addProperty:"dwMaxFrameInterval (100 ns):" withValue:buf atDepth:INTERFACE_LEVEL+2];
                            
                            sprintf((char *)buf, "0x%lx (%8.3f ms)", Swap32(&pUncompressedFrameDesc->dwFrameIntervalStep),  (double ) (pUncompressedFrameDesc->dwFrameIntervalStep / 100000) );
                            [thisDevice addProperty:"dwFrameIntervalStep (100 ns):" withValue:buf atDepth:INTERFACE_LEVEL+2];
                            
                        }
                            else
                            {
                                // Need to recast as a IOUSBVDC_UncompressedDiscreteFrameDescriptor
                                //
                                pUncompressedDiscreteFrameDesc = (IOUSBVDC_UncompressedDiscreteFrameDescriptor *) pUncompressedFrameDesc;
                                
                                sprintf((char *)buf, "0x%x", (pUncompressedDiscreteFrameDesc->bFrameIntervalType));
                                [thisDevice addProperty:"Discrete Frame Intervals supported" withValue:buf atDepth:INTERFACE_LEVEL+1];
                                
                                for (i = 0, t = &pUncompressedDiscreteFrameDesc->dwFrameInterval[0]; i < pUncompressedDiscreteFrameDesc->bFrameIntervalType; i++, t++ )
                                {
                                    UInt32 interval = *t;
                                    sprintf((char *)buf, "0x%lx (%8.3f ms)", Swap32(&interval), (double) (interval / 10000));
                                    sprintf((char *)buf2, "dwFrameInterval[%u] (100 ns)", i+1 );
                                    [thisDevice addProperty:buf2 withValue:buf atDepth:INTERFACE_LEVEL+2];
                                }
                            }
                            break;
                        
                        
			case VS_STILL_IMAGE_FRAME:
				pVSStillImageFrameDesc = (IOUSBVDC_StillImageFrameDescriptor *)desc;
				
				sprintf((char *)buf, "0x%x", pVSStillImageFrameDesc->bEndpointAddress );
				[thisDevice addProperty:"bEndpointAddress:" withValue:buf atDepth:INTERFACE_LEVEL+1];
				
				sprintf((char *)buf, "%d", pVSStillImageFrameDesc->bNumImageSizePatterns );
				[thisDevice addProperty:"bNumImageSizePatterns" withValue:buf atDepth:INTERFACE_LEVEL+1];
						
				IOSUBVDC_StillImageSize	*	dimensions;
				
				for (j = 0; j < pVSStillImageFrameDesc->bNumImageSizePatterns; j++ )
				{
					dimensions = (IOSUBVDC_StillImageSize *)&pVSStillImageFrameDesc->dwSize[j];
					
					sprintf((char *)buf, "Width: %d, Height: %d", OSSwapLittleToHostInt16( dimensions->wWidth), OSSwapLittleToHostInt16(dimensions->wHeight) );
					sprintf((char *)buf2, "wWidth[%d], wHeight[%d]", j+1, j+1 );

					[thisDevice addProperty:buf2 withValue:buf atDepth:INTERFACE_LEVEL+2];
				}
					
				IOSUBVDC_StillImageCompressionPattern *	pattern = (IOSUBVDC_StillImageCompressionPattern *) &pVSStillImageFrameDesc->dwSize[j];
				sprintf((char *)buf, "%d", pattern->bNumCompressionPattern );
				[thisDevice addProperty:"bCompressionPatterns" withValue:buf atDepth:INTERFACE_LEVEL+2];

				for (j = 0; j < pattern->bNumCompressionPattern; j++ )
				{
					sprintf((char *)buf, "%d", pattern->bCompression[j] );
					sprintf((char *)buf2, "bCompression[%d]", j+1);
					
					[thisDevice addProperty:buf2 withValue:buf atDepth:INTERFACE_LEVEL+3];
				}
					
					break;
                    // default:
                }
     }       
}

char MapNumberToVersion( int i )
{
    char rev;
    
    switch (i)
    {
        case 1:
            rev = 'a';
            break;
        case 2:
            rev = 'b';
            break;
        case 3:
            rev = 'c';
            break;
        case 4:
            rev = 'd';
            break;
        case 5:
            rev = 'e';
            break;
        case 6:
            rev = 'f';
            break;
        case 7:
            rev = 'g';
            break;
        case 8:
            rev = 'h';
            break;
        case 9:
            rev = 'i';
            break;
        default:
            rev = 'a';
    }
    return rev;
}
@end