/* * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-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 "BusProbeClass.h" #import #import #import #import #import #import #import static Node *busprobeRootNode = nil; static NSMutableDictionary *vendorNamesDictionary = nil; static NSMutableDictionary *videoClassDevicesDictionary = nil; // add this line @implementation BusProbeClass +(Node *)busprobeRootNode { return busprobeRootNode; } // ________________________________________________________________________________________________ // USBProbe // // Scan all USB devices +(void)USBProbe { NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init]; CFDictionaryRef matchingDict = NULL; mach_port_t mMasterDevicePort = NULL; io_iterator_t devIter = NULL; io_service_t ioDeviceObj = NULL; int outlineViewDeviceNumber = 0; //used to iterate through devices [busprobeRootNode clearNode]; [busprobeRootNode setItemName: @"USB Bus Devices"]; [busprobeRootNode setItemValue: NULL]; outlineViewDeviceNumber = 0; // This gets the master device mach port through which all messages // to the kernel go, and initiates communication with IOKit. require_noerr(IOMasterPort(MACH_PORT_NULL, &mMasterDevicePort), errexit); // Create matching dictionary for IOUSBDevice's matchingDict = IOServiceMatching(kIOUSBDeviceClassName); require(matchingDict != NULL, errexit); // Get device iterator require_noerr( IOServiceGetMatchingServices( mMasterDevicePort, matchingDict, // reference consumed &devIter), errexit); // Walk through devices while ((ioDeviceObj = IOIteratorNext(devIter)) != NULL) { IOCFPlugInInterface **ioPlugin; IOUSBDeviceInterface **deviceIntf = NULL; IOReturn kr; SInt32 score; UInt32 locationID; // Get self pointer to device require_noerr(IOCreatePlugInInterfaceForService( ioDeviceObj, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &ioPlugin, &score), nextDevice); kr = (*ioPlugin)->QueryInterface(ioPlugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID *)&deviceIntf); (*ioPlugin)->Release(ioPlugin); ioPlugin = NULL; require_string(kr == kIOReturnSuccess, nextDevice, "QueryInterface failed"); verify_noerr((*deviceIntf)->GetLocationID(deviceIntf, &locationID)); [self outputDevice:deviceIntf locationID:locationID deviceNumber:outlineViewDeviceNumber]; outlineViewDeviceNumber++; nextDevice: if (deviceIntf != NULL) (*deviceIntf)->Release(deviceIntf); IOObjectRelease(ioDeviceObj); } errexit: if (devIter != NULL) IOObjectRelease(devIter); if (mMasterDevicePort) mach_port_deallocate(mach_task_self(), mMasterDevicePort); [pool release]; } // ________________________________________________________________________________________________ // outputDevice // // Output all of a device's descriptors +(void)outputDevice:(IOUSBDeviceInterface **)deviceIntf locationID:(UInt32)locationID deviceNumber:(int)deviceNumber { Node *newNode; char str[500]; int len; USBClass *deviceClass = NULL; USBClass *interfaceClass = NULL; NSString *tempString1, *tempString2, *tempString3, *tempString4; UInt8 lastInterfaceClass = 0; int currentInterfaceNum = 0; UInt8 lastInterfaceSubClass = 0; IOUSBDeviceDescriptor dev; UInt8 speed = 0; USBDeviceAddress address = 0; int iconfig; NSString *aTempString; char tempCString[500]; // Get the connection Speed for this device GetDeviceSpeed( deviceIntf, &speed ); // Get the connection Speed for this device GetDeviceAddress( deviceIntf, &address ); len = GetDescriptor(deviceIntf, kUSBDeviceDesc, 0, &dev, sizeof(dev)); if (len > 0) { Swap16(&dev.bcdUSB); Swap16(&dev.idVendor); Swap16(&dev.idProduct); Swap16(&dev.bcdDevice); // Create a new child node for this device newNode = [[Node alloc] init]; // Set the node's name to be the device's location [newNode setItemName:[NSString stringWithFormat:@"%s Speed device @ %d (0x%08lX): .............................................",(speed == kUSBDeviceSpeedHigh ? "High" : (speed == kUSBDeviceSpeedLow ? "Low" : "Full")), address, locationID]]; [newNode setItemValue:NULL]; // since I'm not actually gathering anything yet [busprobeRootNode addChild:newNode]; [newNode release]; [self PrintKeyVal:"Device Descriptor" val:"" forDevice:deviceNumber atDepth:DEVICE_DESCRIPTOR_LEVEL-1 forNode:busprobeRootNode]; NUM(dev, "Descriptor Version Number:", bcdUSB, deviceNumber, DEVICE_DESCRIPTOR_LEVEL, 0); deviceClass = [self DeviceClassAndSubClass:"Device" devDescriptor:&dev forDevice:deviceNumber atDepth:1]; NUM(dev, "Device Protocol", bDeviceProtocol, deviceNumber, DEVICE_DESCRIPTOR_LEVEL, 1); NUM(dev, "Device MaxPacketSize:", bMaxPacketSize0, deviceNumber, DEVICE_DESCRIPTOR_LEVEL, 1); aTempString = RETURNNUM(dev, idVendor, 0); sprintf(tempCString,"%s/",[aTempString cString]); aTempString = RETURNNUM(dev, idProduct, 0); strcat(tempCString,[aTempString cString]); tempString1 = RETURNNUM(dev, idVendor, 0); tempString2 = RETURNNUM(dev, idProduct, 0); tempString3 = RETURNNUM(dev, idVendor, 1); aTempString = [NSString stringWithFormat:@"%@/%@ (%@)",tempString1,tempString2,[self vendorNameFromVendorID:tempString3]]; [self PrintKeyVal:"Device VendorID/ProductID:" val:(char *)[aTempString cString] forDevice:deviceNumber atDepth:1 forNode:busprobeRootNode]; //[self PrintKeyVal:"Device VendorID/ProductID:" val:tempCString forDevice:deviceNumber atDepth:1 forNode:busprobeRootNode]; NUM(dev, "Device Version Number:", bcdDevice, deviceNumber, DEVICE_DESCRIPTOR_LEVEL, 0); NUM(dev, "Number of Configurations:", bNumConfigurations, deviceNumber, DEVICE_DESCRIPTOR_LEVEL, 1); STR(dev, "Manufacturer String:", iManufacturer, deviceNumber, 1); STR(dev, "Product String:", iProduct, deviceNumber, 1); STR(dev, "Serial Number String:", iSerialNumber, deviceNumber, 1); // Add the string for the kind of device that it is. We look at the class of the device // and then add the product name. If the product name is blank (iProduct is 0), then we // put the vendor name from the database in the string // // Examples: // // Composite Device: "Apple Extended USB Keyboard" // Hub device from Atmel Corporation // Vendor-specific device from unknown vendor // tempString1 = [deviceClass className]; // If our subclass name is different than our class name, then add the sub class to the description // following a "/" // if( ! [[deviceClass subClassName] isEqualToString:@""] && ! [[deviceClass subClassName] isEqualToString:[deviceClass className]] ) { tempString1 = [[tempString1 stringByAppendingString:@"/"] stringByAppendingString:[deviceClass subClassName]]; } tempString2 = RETURNSTR(dev, iProduct); tempString3 = [tempString1 stringByAppendingString:@" device: "]; tempString4 = RETURNNUM(dev, idVendor, 1); if ([tempString2 isEqualToString:@"0x00"]) { tempString2 = [NSString stringWithFormat:@"%@",[self vendorNameFromVendorID:tempString4]]; if ([tempString2 isEqualToString:@"0x00"]) tempString2 = @"(unnamed)"; else tempString3 = [tempString1 stringByAppendingString:@" device from "]; } [[busprobeRootNode childAtIndex:deviceNumber] setItemValue:[tempString3 stringByAppendingString:tempString2]]; for (iconfig = 0; iconfig < dev.bNumConfigurations; ++iconfig) { IOUSBConfigurationDescriptor cfg; len = GetDescriptor(deviceIntf, kUSBConfDesc, iconfig, &cfg, sizeof(cfg)); if (len > 0) { /* struct IOUSBConfigurationDescriptor { UInt8 bLength; UInt8 bDescriptorType; UInt16 wTotalLength; UInt8 bNumInterfaces; UInt8 bConfigurationValue; UInt8 iConfiguration; UInt8 bmAttributes; UInt8 MaxPower; }; */ Byte *configBuf; Byte *p, *pend; char configHeading[500]; aTempString = RETURNSTR(cfg, iConfiguration); if (strcmp([aTempString cString], "0x00") != 0) { sprintf(configHeading, "Configuration Descriptor: ......................................."); sprintf(tempCString, [aTempString cString]); [self PrintKeyVal:configHeading val:tempCString forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL-1 forNode:busprobeRootNode]; } else { sprintf(configHeading, "Configuration Descriptor"); [self PrintKeyVal:configHeading val:"" forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL-1 forNode:busprobeRootNode]; } Swap16(&cfg.wTotalLength); NUM(cfg, "Total Length of Descriptor:", wTotalLength, deviceNumber, CONFIGURATION_DESCRIPTOR_LEVEL, 1); NUM(cfg, "Number of Interfaces:", bNumInterfaces, deviceNumber, CONFIGURATION_DESCRIPTOR_LEVEL, 1); NUM(cfg, "Configuration Value:", bConfigurationValue, deviceNumber, CONFIGURATION_DESCRIPTOR_LEVEL, 1); sprintf(str, "0x%02X", cfg.bmAttributes); if (cfg.bmAttributes & 0x40) { strcat(str, " (self-powered"); } else { strcat(str, " (bus-powered"); } if (cfg.bmAttributes & 0x20) { strcat(str, ", remote wakeup"); } strcat(str, ")"); [self PrintKeyVal:"Attributes:" val:str forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; aTempString = RETURNNUM(cfg, MaxPower, 1); sprintf(tempCString, "%d ma", [aTempString intValue]*2); [self PrintKeyVal:"MaxPower:" val:tempCString forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; configBuf = malloc(cfg.wTotalLength*sizeof(Byte)); if ( GetDescriptor(deviceIntf, kUSBConfDesc, iconfig, configBuf, cfg.wTotalLength) < 0 ) continue; p = configBuf; pend = p + cfg.wTotalLength; p += cfg.bLength; // Dump the descriptors in the Configuration Descriptor // while (p < pend) { UInt8 descLen = p[0]; UInt8 descType = p[1]; // If this is an interface descriptor, save the interface class and subclass // if ( descType == kUSBInterfaceDesc ) { lastInterfaceClass = ((IOUSBInterfaceDescriptor *)p)->bInterfaceClass; lastInterfaceSubClass = ((IOUSBInterfaceDescriptor *)p)->bInterfaceSubClass; currentInterfaceNum = (int) ((IOUSBInterfaceDescriptor *)p)->bInterfaceNumber; } [self DumpDescriptor:deviceIntf dev:dev p:p forDevice:deviceNumber lastInterfaceClass:lastInterfaceClass lastInterfaceSubClass:lastInterfaceSubClass currentInterfaceNum:currentInterfaceNum]; p += descLen; } } } } else { // Create a new child node for this device newNode = [[Node alloc] init]; //[newNode setItemName:[NSString stringWithFormat:@"USB device @ 0x%08lX: .............................................",locationID]]; // Set the node's name to be the device's location [newNode setItemName:[NSString stringWithFormat:@"%s Speed device @ %d (0x%08lX): .............................................",(speed == kUSBDeviceSpeedHigh ? "High" : (speed == kUSBDeviceSpeedLow ? "Low" : "Full")), address, locationID]]; [newNode setItemValue: @"Unknown device (did not respond do inquiry)"]; [busprobeRootNode addChild:newNode]; [newNode release]; } // If the device is a hub, then dump the Hub descriptor // if ( dev.bDeviceClass == kUSBHubClass ) { IOUSBHubDescriptor cfg; len = GetClassDescriptor(deviceIntf, kUSBHUBDesc, 0, &cfg, sizeof(cfg)); if (len > 0) { [self DumpDescriptor:deviceIntf dev:dev p:(Byte *)&cfg forDevice:deviceNumber lastInterfaceClass:lastInterfaceClass lastInterfaceSubClass:lastInterfaceSubClass currentInterfaceNum:currentInterfaceNum]; } } // Check to see if the device has the "Device Qualifier" descriptor // IOUSBDeviceQualifierDescriptor desc; if ( dev.bcdUSB >= 0x0200 ) { len = GetDescriptor(deviceIntf, kUSBDeviceQualifierDesc, 0, &desc, sizeof(desc)); if ( len > 0) { [self DumpDescriptor:deviceIntf dev:dev p:(Byte *)&desc forDevice:deviceNumber lastInterfaceClass:lastInterfaceClass lastInterfaceSubClass:lastInterfaceSubClass currentInterfaceNum:currentInterfaceNum]; // Since we have a Device Qualifier Descriptor, we can get a "Other Speed Configuration Descriptor" (It's the same as a // regular configuration descriptor) // for (iconfig = 0; iconfig < desc.bNumConfigurations; ++iconfig) { IOUSBConfigurationDescriptor cfg; len = GetDescriptor(deviceIntf, kUSBOtherSpeedConfDesc, iconfig, &cfg, sizeof(cfg)); if (len > 0) { /* struct IOUSBConfigurationDescriptor { UInt8 bLength; UInt8 bDescriptorType; UInt16 wTotalLength; UInt8 bNumInterfaces; UInt8 bConfigurationValue; UInt8 iConfiguration; UInt8 bmAttributes; UInt8 MaxPower; }; */ Byte *configBuf; Byte *p, *pend; char configHeading[500]; aTempString = RETURNSTR(cfg, iConfiguration); if (strcmp([aTempString cString], "0x00") != 0) { sprintf(configHeading, "Other Speed Configuration Descriptor: ......................................."); sprintf(tempCString, [aTempString cString]); [self PrintKeyVal:configHeading val:tempCString forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL-1 forNode:busprobeRootNode]; } else { sprintf(configHeading, "Other Speed Configuration Descriptor"); [self PrintKeyVal:configHeading val:"" forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL-1 forNode:busprobeRootNode]; } Swap16(&cfg.wTotalLength); NUM(cfg, "Total Length of Descriptor:", wTotalLength, deviceNumber, CONFIGURATION_DESCRIPTOR_LEVEL, 1); NUM(cfg, "Number of Interfaces:", bNumInterfaces, deviceNumber, CONFIGURATION_DESCRIPTOR_LEVEL, 1); NUM(cfg, "Configuration Value:", bConfigurationValue, deviceNumber, CONFIGURATION_DESCRIPTOR_LEVEL, 1); sprintf(str, "0x%02X", cfg.bmAttributes); if (cfg.bmAttributes & 0x40) { strcat(str, " (self-powered"); } else { strcat(str, " (bus-powered"); } if (cfg.bmAttributes & 0x20) { strcat(str, ", remote wakeup"); } strcat(str, ")"); [self PrintKeyVal:"Attributes:" val:str forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; aTempString = RETURNNUM(cfg, MaxPower, 1); sprintf(tempCString, "%d ma", [aTempString intValue]*2); [self PrintKeyVal:"MaxPower:" val:tempCString forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; configBuf = malloc(cfg.wTotalLength*sizeof(Byte)); if ( GetDescriptor(deviceIntf, kUSBOtherSpeedConfDesc, iconfig, configBuf, cfg.wTotalLength) < 0 ) continue; p = configBuf; pend = p + cfg.wTotalLength; p += cfg.bLength; // Dump the descriptors in the Configuration Descriptor // while (p < pend) { UInt8 descLen = p[0]; UInt8 descType = p[1]; // If this is an interface descriptor, save the interface class and subclass // if ( descType == kUSBInterfaceDesc ) { lastInterfaceClass = ((IOUSBInterfaceDescriptor *)p)->bInterfaceClass; lastInterfaceSubClass = ((IOUSBInterfaceDescriptor *)p)->bInterfaceSubClass; currentInterfaceNum = (int) ((IOUSBInterfaceDescriptor *)p)->bInterfaceNumber; } [self DumpDescriptor:deviceIntf dev:dev p:p forDevice:deviceNumber lastInterfaceClass:lastInterfaceClass lastInterfaceSubClass:lastInterfaceSubClass currentInterfaceNum:currentInterfaceNum]; p += descLen; } } } } } [deviceClass release]; [interfaceClass release]; } int GetClassDescriptor(IOUSBDeviceInterface **deviceIntf, UInt8 descType, UInt8 descIndex, void *buf, UInt16 len) { IOUSBDevRequest req; IOReturn err; req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBDevice); req.bRequest = kUSBRqGetDescriptor; req.wValue = (descType << 8) | descIndex; req.wIndex = 0; req.wLength = len; req.pData = buf; verify_noerr(err = (*deviceIntf)->DeviceRequest(deviceIntf, &req)); if (err) return -1; return req.wLenDone; } int GetDescriptor(IOUSBDeviceInterface **deviceIntf, UInt8 descType, UInt8 descIndex, void *buf, UInt16 len) { IOUSBDevRequest req; IOReturn err; req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); req.bRequest = kUSBRqGetDescriptor; req.wValue = (descType << 8) | descIndex; req.wIndex = 0; req.wLength = len; req.pData = buf; verify_noerr(err = (*deviceIntf)->DeviceRequest(deviceIntf, &req)); if (err) return -1; return req.wLenDone; } int GetDescriptorFromInterface(IOUSBDeviceInterface **deviceIntf, UInt8 descType, UInt8 descIndex, UInt16 wIndex, void *buf, UInt16 len) { IOUSBDevRequest req; IOReturn err; req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBInterface); req.bRequest = kUSBRqGetDescriptor; req.wValue = (descType << 8) | descIndex; req.wIndex = wIndex; req.wLength = len; req.pData = buf; verify_noerr(err = (*deviceIntf)->DeviceRequest(deviceIntf, &req)); if (err) return -1; return req.wLenDone; } int GetDeviceSpeed( IOUSBDeviceInterface **deviceIntf, UInt8 * speed ) { IOReturn err; verify_noerr(err = (*deviceIntf)->GetDeviceSpeed(deviceIntf, speed)); if (err) return -1; return 0; } int GetDeviceAddress( IOUSBDeviceInterface **deviceIntf, USBDeviceAddress * address ) { IOReturn err; verify_noerr(err = (*deviceIntf)->GetDeviceAddress(deviceIntf, address)); if (err) return -1; return 0; } // ________________________________________________________________________________________________ // GetStringDescriptor // // Get a string descriptor from the device. First, we get the length by getting 2 bytes, and then // we use that information to get the actual string. // int GetStringDescriptor(IOUSBDeviceInterface **deviceIntf, UInt8 descIndex, void *buf, UInt16 len, UInt16 lang) { IOUSBDevRequest req; UInt8 desc[256]; // Max possible descriptor length int stringLen; IOReturn err; if (lang == NULL) // set default langID lang=0x0409; req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); req.bRequest = kUSBRqGetDescriptor; req.wValue = (kUSBStringDesc << 8) | descIndex; req.wIndex = lang; // English req.wLength = 2; req.pData = &desc; verify_noerr(err = (*deviceIntf)->DeviceRequest(deviceIntf, &req)); if ( (err != kIOReturnSuccess) && (err != kIOReturnOverrun) ) return -1; // If the string is 0 (it happens), then just return 0 as the length // stringLen = desc[0]; if(stringLen == 0) { return 0; } // OK, now that we have the string length, make a request for the full length // req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); req.bRequest = kUSBRqGetDescriptor; req.wValue = (kUSBStringDesc << 8) | descIndex; req.wIndex = lang; // English req.wLength = stringLen; req.pData = buf; verify_noerr(err = (*deviceIntf)->DeviceRequest(deviceIntf, &req)); return req.wLenDone; } +(USBClass *)DeviceClassAndSubClass:(const char *)scope devDescriptor:(IOUSBDeviceDescriptor *)devDescriptor forDevice:(int)deviceNumber atDepth:(int)depth { USBClass *usbc = NULL; char *cls = "", *sub = ""; char name[500]; switch (devDescriptor->bDeviceClass) { case kUSBCompositeClass: cls = "Composite"; break; case kUSBAudioClass: cls = "Audio"; switch (devDescriptor->bDeviceSubClass) { case 0x01: sub = "Audio Control"; break; case 0x02: sub = "Audio Streaming"; break; case 0x03: sub = "MIDI Streaming"; break; default: sub = "Unknown"; break; } break; case kUSBCommClass: cls = "Comm"; break; case kUSBHIDClass: cls = "HID"; switch (devDescriptor->bDeviceSubClass) { case kUSBHIDBootInterfaceSubClass: sub = "Boot Interface"; break; default: sub = ""; break; } break; case kUSBDisplayClass: cls = "Display"; break; case kUSBPrintingClass: cls = "Printing"; break; case kUSBMassStorageClass: cls = "Mass Storage"; switch (devDescriptor->bDeviceSubClass) { case kUSBMassStorageRBCSubClass: sub = "Reduced Block Commands"; break; case kUSBMassStorageATAPISubClass: sub = "ATAPI"; break; case kUSBMassStorageQIC157SubClass: sub = "QIC-157"; break; case kUSBMassStorageUFISubClass: sub = "UFI"; break; case kUSBMassStorageSFF8070iSubClass: sub = "SFF-8070i"; break; case kUSBMassStorageSCSISubClass: sub = "SCSI"; break; default: sub = "Unknown"; break; } break; case kUSBHubClass: cls = "Hub"; break; case kUSBDataClass: cls = "Data"; break; case 0xE0: cls = "Bluetooth Wireless Controller"; break; case kUSBApplicationSpecificClass: cls = "Application Specific"; switch (devDescriptor->bDeviceSubClass) { case kUSBDFUSubClass: sub = "Device Firmware Upgrade"; break; case kUSBIrDABridgeSubClass: sub = "IrDA Bridge"; break; default: sub = "Unknown"; break; } break; case kUSBVendorSpecificClass: cls = sub = "Vendor-specific"; break; default: cls = "Unknown"; break; } sprintf(name, "%s Class:", scope); [self PrintNumStr:name value:devDescriptor->bDeviceClass size:1 interpret:cls forDevice:deviceNumber atDepth:depth asInt:1]; sprintf(name, "%s Subclass:", scope); [self PrintNumStr:name value:devDescriptor->bDeviceSubClass size:1 interpret:sub forDevice:deviceNumber atDepth:depth asInt:1]; if (usbc == NULL) { usbc = [[USBClass alloc] init]; [usbc setClassName:[NSString stringWithCString:cls]]; [usbc setSubClassName:[NSString stringWithCString:sub]]; } return usbc; } +(USBClass *)InterfaceClassAndSubClass:(const char *)scope devDescriptor:(IOUSBDeviceDescriptor *)devDescriptor intfceDescriptor:(IOUSBInterfaceDescriptor *)intfceDescriptor forDevice:(int)deviceNumber atDepth:(int)depth { USBClass *usbc = NULL; char *cls = "", *sub = ""; char name[500]; UInt32 productID = devDescriptor->idProduct; UInt32 vendorID = devDescriptor->idVendor; switch (intfceDescriptor->bInterfaceClass) { case kUSBCompositeClass: cls = "Composite"; break; case kUSBAudioClass: cls = "Audio"; switch (intfceDescriptor->bInterfaceSubClass) { case 0x01: sub = "Audio Control"; break; case 0x02: sub = "Audio Streaming"; break; case 0x03: sub = "MIDI Streaming"; break; default: sub = "Unknown"; break; } break; case kUSBCommClass: cls = "Comm"; break; case kUSBHIDClass: cls = "HID"; switch (intfceDescriptor->bInterfaceSubClass) { case kUSBHIDBootInterfaceSubClass: sub = "Boot Interface"; break; default: sub = ""; break; } break; case kUSBDisplayClass: cls = "Display"; break; case kUSBPrintingClass: cls = "Printing"; break; case kUSBMassStorageClass: cls = "Mass Storage"; switch (intfceDescriptor->bInterfaceSubClass) { case kUSBMassStorageRBCSubClass: sub = "Reduced Block Commands"; break; case kUSBMassStorageATAPISubClass: sub = "ATAPI"; break; case kUSBMassStorageQIC157SubClass: sub = "QIC-157"; break; case kUSBMassStorageUFISubClass: sub = "UFI"; break; case kUSBMassStorageSFF8070iSubClass: sub = "SFF-8070i"; break; case kUSBMassStorageSCSISubClass: sub = "SCSI"; break; default: sub = "Unknown"; break; } break; case kUSBHubClass: cls = "Hub"; break; case kUSBDataClass: cls = "Data"; break; case 0xE0: cls = "Bluetooth Wireless Controller"; break; case kUSBApplicationSpecificClass: cls = "Application Specific"; switch (intfceDescriptor->bInterfaceSubClass) { case kUSBDFUSubClass: sub = "Device Firmware Upgrade"; break; case kUSBIrDABridgeSubClass: sub = "IrDA Bridge"; break; default: sub = "Unknown"; break; } break; case kUSBVendorSpecificClass: if ( [self isInterfaceVDC:vendorID idProduct:productID ] ) { cls = "Vendor-specific"; switch (intfceDescriptor->bInterfaceSubClass) { case SC_VIDEOCONTROL: sub = "Video Control"; break; case SC_VIDEOSTREAMING: sub = "Video Streaming"; break; default: sub = "unknown"; break; } } else cls = sub = "Vendor-specific"; break; default: cls = "Unknown"; break; } sprintf(name, "%s Class:", scope); [self PrintNumStr:name value:intfceDescriptor->bInterfaceClass size:1 interpret:cls forDevice:deviceNumber atDepth:depth asInt:1]; sprintf(name, "%s Subclass:", scope); [self PrintNumStr:name value:intfceDescriptor->bInterfaceSubClass size:1 interpret:sub forDevice:deviceNumber atDepth:depth asInt:1]; if (usbc == NULL) { usbc = [[USBClass alloc] init]; [usbc setClassName:[NSString stringWithCString:cls]]; [usbc setSubClassName:[NSString stringWithCString:sub]]; } return usbc; } // ________________________________________________________________________________________________ // PrintDescLenAndType // // Print the length and type fields of a USB descriptor. +(void)PrintDescLenAndType:(void *)desc forDevice:(int)deviceNumber atDepth:(int)depth { Byte *p = (Byte *)desc; char *str; [self printNum:"bLength" value:p[0] size:1 forDevice:(int)deviceNumber atDepth:depth asInt:0]; switch (p[1]) { case kUSBDeviceDesc: str = "Device"; break; case kUSBConfDesc: str = "Configuration"; break; case kUSBStringDesc: str = "String"; break; case kUSBInterfaceDesc: str = "Interface"; break; case kUSBEndpointDesc: str = "Endpoint"; break; case kUSBHIDDesc: str = "HID"; break; case kUSBReportDesc: str = "Report"; break; case kUSBPhysicalDesc: str = "Physical"; break; case kUSBHUBDesc: str = "Hub"; break; case CS_INTERFACE: str = "CS Interface"; break; case CS_ENDPOINT: str = "CS Endpoint"; break; default: str = "(unknown)"; break; } [self PrintNumStr:"bDescriptorType" value:p[1] size:1 interpret:str forDevice:(int)deviceNumber atDepth:depth asInt:0]; } // ________________________________________________________________________________________________ // printNum // // Print a numeric field's name and value +(void)printNum:(char *)name value:(UInt32)value size:(int)sizeInBytes forDevice:(int)deviceNumber atDepth:(int)depth asInt:(int)asInt { char format[32], valstr[32]; if (asInt==1){ sprintf(valstr, "%d",(int)value); [self PrintKeyVal:name val:valstr forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; return; } else { sprintf(format, "0x%%0%dlX", sizeInBytes*2); sprintf(valstr, format, value); [self PrintKeyVal:name val:valstr forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; return; } } // ________________________________________________________________________________________________ // returnNum // // Print a numeric field's name and value +(NSString *)returnNum:(UInt32)value size:(int)sizeInBytes asInt:(int)asInt { char format[32], valstr[32]; if (asInt==1){ sprintf(valstr, "%d",(int)value); return [NSString stringWithCString:valstr]; } else { sprintf(format, "0x%%0%dlX", sizeInBytes*2); sprintf(valstr, format, value); return [NSString stringWithCString:valstr]; } } // ________________________________________________________________________________________________ // PrintNumStr // // Print a numeric field's name, value, and interpretation +(void)PrintNumStr:(char *)name value:(UInt32)value size:(int)sizeInBytes interpret:(char *)interpret forDevice:(int)deviceNumber atDepth:(int)depth asInt:(int)asInt { char format[32], valstr[256]; if (asInt==1) { if (strcmp(interpret,"")==0) sprintf(valstr, "%d", (int)value); else sprintf(valstr, "%d (%s)", (int)value, interpret); } else { if (strcmp(interpret,"")==0) sprintf(format, "0x%%0%dlX %%s", sizeInBytes*2); else sprintf(format, "0x%%0%dlX (%%s)", sizeInBytes*2); sprintf(valstr, format, value, interpret); } [self PrintKeyVal:name val:valstr forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; } UInt16 Swap16(void *p) { * (UInt16 *) p = CFSwapInt16LittleToHost(*(UInt16 *)p); return * (UInt16 *) p; } UInt32 Swap32(void *p) { * (UInt32 *) p = CFSwapInt32LittleToHost(*(UInt32 *)p); return * (UInt32 *) p; } UInt64 Swap64(void *p) { UInt32 hiDW = (UInt32) (( *(UInt64 *)p) >> 32); UInt32 loDW = (UInt32) ( ( *(UInt64 *)p) & 0xffffffff); // Swap the 2 32 bit quantities // hiDW = Swap32(&hiDW); loDW = Swap32(&loDW); * (UInt64 *) p = (( (UInt64)hiDW ) << 32) | ( (UInt64) loDW); return * (UInt64 *) p; } // ________________________________________________________________________________________________ // PrintStr // // For a descriptor field which is a string reference, fetch the string from // device and print the field. +(void)PrintStr:(IOUSBDeviceInterface **)deviceIntf name:(char *)name strIndex:(UInt8)strIndex forDevice:(int)deviceNumber atDepth:(int)depth { Byte buf[256]; char str2[500]; if (strIndex > 0) { int len; buf[0] = 0; len = GetStringDescriptor(deviceIntf, strIndex, buf, sizeof(buf),NULL); if (len > 2) { Byte *p; CFStringRef str; for (p = buf + 2; p < buf + len; p += 2) { Swap16(p); } str = CFStringCreateWithCharacters(NULL, (const UniChar *)(buf+2), (len-2)/2); CFStringGetCString(str, (char *)buf, 256, kCFStringEncodingNonLossyASCII); CFRelease(str); sprintf(str2, "%d \"%s\"", strIndex, buf); [self PrintKeyVal:name val:str2 forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; } else { char str[20]; buf[0] = 0; sprintf(str,"%d (none)",strIndex); [self PrintKeyVal:name val:str forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; } } else { char str[20]; sprintf(str,"%d (none)",strIndex); [self PrintKeyVal:name val:str forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; } } +(NSString *)ReturnStr:(IOUSBDeviceInterface **)deviceIntf strIndex:(UInt8)strIndex { Byte buf[256]; char str2[500]; if (strIndex > 0) { int len; buf[0] = 0; len = GetStringDescriptor(deviceIntf, strIndex, buf, sizeof(buf),NULL); if (len > 2) { Byte *p; CFStringRef str; for (p = buf + 2; p < buf + len; p += 2) { Swap16(p); } str = CFStringCreateWithCharacters(NULL, (const UniChar *)(buf+2), (len-2)/2); CFStringGetCString(str, (char *)buf, 256, kCFStringEncodingNonLossyASCII); CFRelease(str); sprintf(str2, "\"%s\"", buf); return [NSString stringWithCString:str2]; } else { buf[0] = 0; return @"0x00"; } } else return @"0x00"; } // ________________________________________________________________________________________________ // dump +(void)dump:(int)n byte:(Byte *)p forDevice:(int)deviceNumber atDepth:(int)depth { #define BYTESPERLINE 16 int lineCount = 0; int runningCount = 0; int lastLine = 0; char str1[BYTESPERLINE * 6] = ""; // 0xXX + 2 spaces char str2[10]; char descriptor[40]; strcat( str1, "0000: "); sprintf(descriptor, "Raw Descriptor (hex) "); while (--n >= 0) { sprintf(str2, "%02X ", *p++); strcat(str1, str2); lineCount++; runningCount++; // Add a space in between BYTESPERLINE / 2 and the next one // if ( (runningCount % (BYTESPERLINE>>1)) == 0 ) strcat(str1, " "); // Add the index to the bytes (should they be in hex?) to the text // // Split the descriptor into BYTESPERLINE bytes each line so that it's more readabale // if ( lineCount == BYTESPERLINE ) { [self PrintKeyVal:descriptor val:str1 forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; lastLine = runningCount; lineCount = 0; sprintf(str1, "%4.4x: ",runningCount); strcpy(descriptor, ""); // strcpy(str1,""); } } if ( lineCount != 0 ) { // Don't add an index for descriptors that only occupy one line // if ( lastLine == 0 ) strcpy(descriptor,"Raw Descriptor (hex)"); [self PrintKeyVal:descriptor val:str1 forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; } else [self PrintKeyVal:"Unknown Descriptor" val:str1 forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; return; } // ________________________________________________________________________________________________ // DumpDescriptor // +(void)DumpDescriptor:(IOUSBDeviceInterface **)deviceIntf dev:(IOUSBDeviceDescriptor)dev p:(Byte *)p forDevice:(int)deviceNumber lastInterfaceClass:(UInt8)lastInterfaceClass lastInterfaceSubClass:(UInt8)lastInterfaceSubClass currentInterfaceNum:(int)currentInterfaceNum { UInt8 descType = p[1]; char * xferTypes[] = { "Control", "Isochronous", "Bulk", "Interrupt" }; int xferTypes2[] = { 0, 1, 2, 3 }; USBClass * interfaceClass = NULL; int tempInt1, tempInt2; NSString * tempString1; char str[500]; char temporaryString[500]; switch (descType) { case kUSBInterfaceDesc: { /* struct IOUSBInterfaceDescriptor { UInt8 bLength; UInt8 bDescriptorType; UInt8 bInterfaceNumber; UInt8 bAlternateSetting; UInt8 bNumEndpoints; UInt8 bInterfaceClass; UInt8 bInterfaceSubClass; UInt8 bInterfaceProtocol; UInt8 iInterface; }; */ IOUSBInterfaceDescriptor interfaceDescriptor; char interfaceHeading[500]; interfaceDescriptor = *(IOUSBInterfaceDescriptor *)p; sprintf(interfaceHeading, "Interface #%d", (int)interfaceDescriptor.bInterfaceNumber); [self PrintKeyVal:interfaceHeading val:"" forDevice:deviceNumber atDepth:INTERFACE_LEVEL-1 forNode:busprobeRootNode]; NUM(interfaceDescriptor, "Alternate Setting", bAlternateSetting, deviceNumber, INTERFACE_LEVEL, 1); NUM(interfaceDescriptor, "Number of Endpoints", bNumEndpoints, deviceNumber, INTERFACE_LEVEL, 1); interfaceClass = [self InterfaceClassAndSubClass:"Interface" devDescriptor:&dev intfceDescriptor:&interfaceDescriptor forDevice:deviceNumber atDepth:INTERFACE_LEVEL]; tempInt1 = [[busprobeRootNode childAtIndex:deviceNumber] childrenCount]; tempInt2 = [[[busprobeRootNode childAtIndex:deviceNumber] childAtIndex:tempInt1-1] childrenCount]; tempString1 = [interfaceClass className]; // If our subclass name is different than our class name, then add the sub class to the description // following a "/" // if( ! [[interfaceClass subClassName] isEqualToString:@""] && ! [[interfaceClass subClassName] isEqualToString:[interfaceClass className]] ) { tempString1 = [[tempString1 stringByAppendingString:@"/"] stringByAppendingString:[interfaceClass subClassName]]; } if ( interfaceDescriptor.bAlternateSetting != 0 ) [[[[busprobeRootNode childAtIndex:deviceNumber] childAtIndex:tempInt1-1] childAtIndex:tempInt2-1] setItemName:[NSString stringWithFormat:@"Interface #%d - %s (#%d)", (int)interfaceDescriptor.bInterfaceNumber, [tempString1 cString], (int)interfaceDescriptor.bAlternateSetting]]; else [[[[busprobeRootNode childAtIndex:deviceNumber] childAtIndex:tempInt1-1] childAtIndex:tempInt2-1] setItemName:[NSString stringWithFormat:@"Interface #%d - %s", (int)interfaceDescriptor.bInterfaceNumber, [tempString1 cString]]]; NUM(interfaceDescriptor, "Interface Protocol", bInterfaceProtocol, deviceNumber, INTERFACE_LEVEL, 1); lastInterfaceClass = interfaceDescriptor.bInterfaceClass; lastInterfaceSubClass = interfaceDescriptor.bInterfaceSubClass; } break; case kUSBEndpointDesc: { /* struct IOUSBEndpointDescriptor { UInt8 bLength; UInt8 bDescriptorType; UInt8 bEndpointAddress; UInt8 bmAttributes; UInt16 wMaxPacketSize; UInt8 bInterval; }; */ IOUSBEndpointDescriptor endpointDescriptor; char endpointHeading[500]; endpointDescriptor = *(IOUSBEndpointDescriptor *)p; Swap16(&endpointDescriptor.wMaxPacketSize); switch (xferTypes2[endpointDescriptor.bmAttributes & 3]) { case 0: sprintf(endpointHeading, "Endpoint 0x%02X - Control Endpoint", endpointDescriptor.bEndpointAddress); [self PrintKeyVal:endpointHeading val:"" forDevice:deviceNumber atDepth:ENDPOINT_LEVEL-1 forNode:busprobeRootNode]; break; case 1: if ( (endpointDescriptor.bEndpointAddress & kEndpointAddressMask ) == 0 ) sprintf(endpointHeading, "Endpoint 0x%02X - Isochronous Output", endpointDescriptor.bEndpointAddress); else sprintf(endpointHeading, "Endpoint 0x%02X - Isochronous Input", endpointDescriptor.bEndpointAddress); [self PrintKeyVal:endpointHeading val:"" forDevice:deviceNumber atDepth:ENDPOINT_LEVEL-1 forNode:busprobeRootNode]; break; case 2: if ( (endpointDescriptor.bEndpointAddress & kEndpointAddressMask ) == 0 ) sprintf(endpointHeading, "Endpoint 0x%02X - Bulk Output", endpointDescriptor.bEndpointAddress); else sprintf(endpointHeading, "Endpoint 0x%02X - Bulk Input", endpointDescriptor.bEndpointAddress); [self PrintKeyVal:endpointHeading val:"" forDevice:deviceNumber atDepth:ENDPOINT_LEVEL-1 forNode:busprobeRootNode]; break; case 3: if ( (endpointDescriptor.bEndpointAddress & kEndpointAddressMask ) == 0 ) sprintf(endpointHeading, "Endpoint 0x%02X - Interrupt Output", endpointDescriptor.bEndpointAddress); else sprintf(endpointHeading, "Endpoint 0x%02X - Interrupt Input", endpointDescriptor.bEndpointAddress); [self PrintKeyVal:endpointHeading val:"" forDevice:deviceNumber atDepth:ENDPOINT_LEVEL-1 forNode:busprobeRootNode]; break; default: sprintf(endpointHeading, "Endpoint 0x%02X", endpointDescriptor.bEndpointAddress); [self PrintKeyVal:endpointHeading val:"" forDevice:deviceNumber atDepth:ENDPOINT_LEVEL-1 forNode:busprobeRootNode]; break; } if (!(xferTypes2[endpointDescriptor.bmAttributes & 3] == 0)) { // we dont need to show direction for Control Endpoints // if ( (endpointDescriptor.bEndpointAddress & kEndpointAddressMask ) == 0 ) sprintf(temporaryString, "0x%02X (OUT)", endpointDescriptor.bEndpointAddress); else sprintf(temporaryString, "0x%02X (IN)", endpointDescriptor.bEndpointAddress); [self PrintKeyVal:"Attributes:" val:temporaryString forDevice:deviceNumber atDepth:ENDPOINT_LEVEL forNode:busprobeRootNode]; } sprintf(str, "0x%02X (%s)", endpointDescriptor.bmAttributes, xferTypes[endpointDescriptor.bmAttributes & 3]); [self PrintKeyVal:"Attributes:" val:str forDevice:deviceNumber atDepth:ENDPOINT_LEVEL forNode:busprobeRootNode]; sprintf(temporaryString, "%d", endpointDescriptor.wMaxPacketSize); [self PrintKeyVal:"Max Packet Size:" val:temporaryString forDevice:deviceNumber atDepth:ENDPOINT_LEVEL forNode:busprobeRootNode]; sprintf(temporaryString, "%d ms", endpointDescriptor.bInterval); [self PrintKeyVal:"Polling Interval:" val:temporaryString forDevice:deviceNumber atDepth:ENDPOINT_LEVEL forNode:busprobeRootNode]; } break; /* case CS_INTERFACE: switch (lastInterfaceClass) { case 1: // audio class if( AC_CONTROL_SUBCLASS == lastInterfaceSubClass ) { switch ( ((GenericAudioDescriptorPtr)p)->descSubType ) { case ACS_HEADER: sprintf((char *)temporaryString, "Audio Control Class Specific Header Descriptor"); break; case ACS_INPUT_TERMINAL: sprintf((char *)temporaryString, "Audio Class Specific Input Terminal Descriptor"); break; case ACS_OUTPUT_TERMINAL: sprintf((char *)temporaryString, "Audio Class Specific Ouput Terminal Descriptor"); break; case ACS_MIXER_UNIT: sprintf((char *)temporaryString, "Audio Class Specific Mixer Unit Descriptor"); break; case ACS_SELECTOR_UNIT: sprintf((char *)temporaryString, "Audio Class Specific Selector Unit Descriptor"); break; case ACS_FEATURE_UNIT: sprintf((char *)temporaryString, "Audio Class Specific Feature Descriptor"); break; case ACS_PROCESSING_UNIT: sprintf((char *)temporaryString, "Audio Class Specific Processing Unit Descriptor"); break; case ACS_EXTENSION_UNIT: sprintf((char *)temporaryString, "Audio Class Specific Extension Descriptor"); break; } } else if( AC_STREAM_SUBCLASS == lastInterfaceSubClass ) { switch ( ((GenericAudioDescriptorPtr)p)->descSubType ) { case ACS_HEADER: sprintf((char *)temporaryString, "Audio Control Class Specific Header Descriptor"); break; case ACS_FORMAT_TYPE: sprintf((char *)temporaryString, "Audio Class Specific Audio Data Format Descriptor"); break; } } break; default: sprintf((char *)temporaryString, "Type 0x%02x Descriptor",((GenericAudioDescriptorPtr)p)->descSubType); break; } [self PrintKeyVal:temporaryString val:"" forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL+2 forNode:busprobeRootNode]; break; case CS_ENDPOINT: [self DoRegularCSEndpoint:p deviceClass:interfaceClass forDevice:deviceNumber atDepth:ENDPOINT_LEVEL-1]; break; */ case HID_DESCRIPTOR: // case DFU_FUNCTIONAL_DESCRIPTOR: - same value, compiler complains { if (lastInterfaceClass == kUSBApplicationSpecificClass && lastInterfaceSubClass == kUSBDFUSubClass) { IOUSBDFUDescriptor dfuDescriptor; dfuDescriptor = *(IOUSBDFUDescriptor *)p; [self PrintKeyVal:"DFU Functional Descriptor" val:"" forDevice:deviceNumber atDepth:DFU_DESCRIPTOR_LEVEL-1 forNode:busprobeRootNode]; sprintf(temporaryString, "0x%02x (%sDownload, %sUpload, %sManifestation Tolerant, " "Reserved bits: 0x%02x)", dfuDescriptor.bmAttributes, dfuDescriptor.bmAttributes & (1 << kUSBDFUCanDownloadBit) ? "" : "No ", dfuDescriptor.bmAttributes & ( 1 << kUSBDFUCanUploadBit) ? "" : "No ", dfuDescriptor.bmAttributes & ( 1 << kUSBDFUManifestationTolerantBit) ? "" : "Not ", dfuDescriptor.bmAttributes & ~kUSBDFUAttributesMask); [self PrintKeyVal:"bmAttributes:" val:temporaryString forDevice:deviceNumber atDepth:DFU_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; sprintf(temporaryString, "%d ms", Swap16(&dfuDescriptor.wDetachTimeout) ); [self PrintKeyVal:"wDetachTimeout:" val:temporaryString forDevice:deviceNumber atDepth:DFU_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; sprintf(temporaryString, "%d bytes", Swap16(&dfuDescriptor.wTransferSize)); [self PrintKeyVal:"wTransferSize:" val:temporaryString forDevice:deviceNumber atDepth:DFU_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; } else if (lastInterfaceClass == kUSBHIDClass) { IOUSBHIDDescriptor hidDescriptor; int descriptorIncrement=0; hidDescriptor = *(IOUSBHIDDescriptor *)p; [self PrintKeyVal:"HID Descriptor" val:"" forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL-1 forNode:busprobeRootNode]; Swap16(&hidDescriptor.descVersNum); NUM(hidDescriptor, "Descriptor Version Number:", descVersNum, deviceNumber, HID_DESCRIPTOR_LEVEL, 0); NUM(hidDescriptor, "Country Code:", hidCountryCode, deviceNumber, HID_DESCRIPTOR_LEVEL, 1); NUM(hidDescriptor, "Descriptor Count:", hidNumDescriptors, deviceNumber, HID_DESCRIPTOR_LEVEL, 1); for(descriptorIncrement=1; descriptorIncrement <= hidDescriptor.hidNumDescriptors; descriptorIncrement++) { char tempCString[20], descriptorHeading[20]; NSString *tempString; sprintf(descriptorHeading, "Descriptor %d", descriptorIncrement); [self PrintKeyVal:descriptorHeading val:"" forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; tempString = RETURNNUM(hidDescriptor, hidDescriptorType, 0); if ( hidDescriptor.hidDescriptorType == kUSBHIDDesc) { UInt16 hidDescriptorLength = ( hidDescriptor.hidDescriptorLengthHi << 8 ) | hidDescriptor.hidDescriptorLengthLo; sprintf(tempCString, "%s (HID Descriptor)", [tempString cString]); [self PrintKeyVal:"Type:" val:tempCString forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode]; sprintf(tempCString, "%d", hidDescriptorLength); [self PrintKeyVal:"Length:" val:tempCString forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode]; } else if (hidDescriptor.hidDescriptorType == kUSBReportDesc) { unsigned char *reportdesc; UInt16 hidlen, hidDescriptorLength = ( hidDescriptor.hidDescriptorLengthHi << 8 ) | hidDescriptor.hidDescriptorLengthLo; sprintf(tempCString, "%s (Report Descriptor)", [tempString cString]); [self PrintKeyVal:"Type:" val:tempCString forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode]; sprintf(tempCString, "%d", hidDescriptorLength); [self PrintKeyVal:"Length (and contents):" val:tempCString forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode]; reportdesc = malloc(hidDescriptorLength); if (reportdesc) { hidlen = GetDescriptorFromInterface(deviceIntf, kUSBReportDesc, 0 /*desc index*/, currentInterfaceNum, reportdesc, hidDescriptorLength); if (hidlen == hidDescriptorLength) { [self dump:hidlen byte:reportdesc forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL+2]; [DecodeHIDReport DecodeHIDReport:reportdesc forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL+1 reportLen:hidlen forNode:busprobeRootNode]; } free(reportdesc); } } else if (hidDescriptor.hidDescriptorType == kUSBPhysicalDesc) { UInt16 hidDescriptorLength = ( hidDescriptor.hidDescriptorLengthHi << 8 ) | hidDescriptor.hidDescriptorLengthLo; sprintf(tempCString, "%s (Physical Descriptor)", [tempString cString]); [self PrintKeyVal:"Type:" val:tempCString forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode]; sprintf(tempCString, "%d", hidDescriptorLength); [self PrintKeyVal:"Length:" val:tempCString forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode]; } else { UInt16 hidDescriptorLength = ( hidDescriptor.hidDescriptorLengthHi << 8 ) | hidDescriptor.hidDescriptorLengthLo; sprintf(tempCString, "%s", [tempString cString]); [self PrintKeyVal:"Type:" val:tempCString forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode]; sprintf(tempCString, "%d", hidDescriptorLength); [self PrintKeyVal:"Length:" val:tempCString forDevice:deviceNumber atDepth:HID_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode]; } } } else { // Descriptor 21 for an unknown class. Just dump it out // [self DumpRawDescriptor:p forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL+1]; } } break; case kUSBHUBDesc: { IOUSBHubDescriptor hubDescriptor; UInt16 hubChar; hubDescriptor = *(IOUSBHubDescriptor *)p; [self PrintKeyVal:"Hub Descriptor" val:"" forDevice:deviceNumber atDepth:HUB_DESCRIPTOR_LEVEL-1 forNode:busprobeRootNode]; NUM(hubDescriptor, "Number of Ports:", numPorts, deviceNumber, HUB_DESCRIPTOR_LEVEL, 0); hubChar = Swap16(&hubDescriptor.characteristics); sprintf(temporaryString, "0x%x (%sswitched %s hub with %s overcurrent protection)", hubChar, (((hubChar & 3) == 0) ? "Gang " : ((hubChar & 3) == 1) ? "Individually " : "Non-"), ((hubChar & 4) == 4) ? "compound" : "standalone", ((hubChar & 0x18) == 0) ? "global" : ((hubChar & 0x18) == 0x8) ? "individual port" : "no"); [self PrintKeyVal:"Hub Characteristics:" val:temporaryString forDevice:deviceNumber atDepth:HUB_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; sprintf(temporaryString, "%d ms", hubDescriptor.powerOnToGood*2); [self PrintKeyVal:"PowerOnToGood time:" val:temporaryString forDevice:deviceNumber atDepth:HUB_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; sprintf(temporaryString, "%d mA", hubDescriptor.hubCurrent); [self PrintKeyVal:"Controller current:" val:temporaryString forDevice:deviceNumber atDepth:HUB_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; if (hubDescriptor.numPorts < 8) { sprintf(temporaryString, "0x%x", hubDescriptor.removablePortFlags[0]); [self PrintKeyVal:"Device Removeable (byte):" val:temporaryString forDevice:deviceNumber atDepth:HUB_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; sprintf(temporaryString, "0x%x", hubDescriptor.removablePortFlags[1]); [self PrintKeyVal:"Port Power Control Mask (byte):" val:temporaryString forDevice:deviceNumber atDepth:HUB_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; } else if (hubDescriptor.numPorts < 16) { sprintf(temporaryString, "0x%lx", (UInt32)Swap16( &( (UInt16 *)hubDescriptor.removablePortFlags)[0])); [self PrintKeyVal:"Device Removeable (byte):" val:temporaryString forDevice:deviceNumber atDepth:HUB_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; sprintf(temporaryString, "0x%lx", (UInt32)Swap16(&((UInt16 *)hubDescriptor.removablePortFlags)[1])); [self PrintKeyVal:"Port Power Control Mask (byte):" val:temporaryString forDevice:deviceNumber atDepth:HUB_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; } } break; case kUSBDeviceQualifierDesc: { IOUSBDeviceQualifierDescriptor devQualDescriptor; USBClass * deviceClass = NULL; devQualDescriptor = *(IOUSBDeviceQualifierDescriptor *)p; Swap16(&devQualDescriptor.bcdUSB); [self PrintKeyVal:"Device Qualifier Descriptor" val:"" forDevice:deviceNumber atDepth:DEVICE_QUAL_DESCRIPTOR_LEVEL-1 forNode:busprobeRootNode]; NUM(devQualDescriptor, "Descriptor Version Number:", bcdUSB, deviceNumber, DEVICE_QUAL_DESCRIPTOR_LEVEL, 0); deviceClass = [self DeviceClassAndSubClass:"Device" devDescriptor:(IOUSBDeviceDescriptor *)&devQualDescriptor forDevice:deviceNumber atDepth:1]; NUM(devQualDescriptor, "Device Protocol", bDeviceProtocol, deviceNumber, DEVICE_QUAL_DESCRIPTOR_LEVEL, 1); NUM(devQualDescriptor, "Device MaxPacketSize:", bMaxPacketSize0, deviceNumber, DEVICE_QUAL_DESCRIPTOR_LEVEL, 1); NUM(devQualDescriptor, "Number of Configurations:", bNumConfigurations, deviceNumber, DEVICE_DESCRIPTOR_LEVEL, 1); NUM(devQualDescriptor, "bReserved:", bReserved, deviceNumber, DEVICE_DESCRIPTOR_LEVEL, 1); [deviceClass release]; } break; case kUSBInterfaceAssociationDesc: { IOUSBInterfaceAssociationDescriptor interfaceAssocDescriptor; interfaceAssocDescriptor = *(IOUSBInterfaceAssociationDescriptor *)p; [self PrintKeyVal:"Interface Association Descriptor" val:"" forDevice:deviceNumber atDepth:DEVICE_QUAL_DESCRIPTOR_LEVEL forNode:busprobeRootNode]; NUM(interfaceAssocDescriptor, "First Interface:", bFirstInterface, deviceNumber, DEVICE_QUAL_DESCRIPTOR_LEVEL+1, 0); NUM(interfaceAssocDescriptor, "Interface Count:", bInterfaceCount, deviceNumber, DEVICE_QUAL_DESCRIPTOR_LEVEL+1, 0); NUM(interfaceAssocDescriptor, "Function Class:", bFunctionClass, deviceNumber, DEVICE_QUAL_DESCRIPTOR_LEVEL+1, 0); NUM(interfaceAssocDescriptor, "Function SubClass:", bFunctionSubClass, deviceNumber, DEVICE_QUAL_DESCRIPTOR_LEVEL+1, 0); NUM(interfaceAssocDescriptor, "Function Protocol:", bFunctionProtocol, deviceNumber, DEVICE_QUAL_DESCRIPTOR_LEVEL+1, 0); } break; default: switch(lastInterfaceClass) { case 1: /* audio class */ [DecodeAudioInterfaceDescriptor DecodeAudioInterfaceDescriptor:p forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode subClass:lastInterfaceSubClass ]; break; case 2: /* communication class */ // DecodeCommClassDescriptor( desc, myItem, curSubClass, curProtocol); switch ( ((GenericAudioDescriptorPtr)p)->descSubType ) { case 0: sprintf((char *)temporaryString, "Comm Class Header Functional Descriptor"); break; case 1: sprintf((char *)temporaryString, "Comm Class Call Management Functional Descriptor"); break; case 2: sprintf((char *)temporaryString, "Comm Class Abstract Control Management Functional Descriptor"); break; case 3: sprintf((char *)temporaryString, "Comm Class Direct Line Management Functional Descriptor"); break; case 4: sprintf((char *)temporaryString, "Comm Class Telephone Ringer Functional Descriptor"); break; case 5: sprintf((char *)temporaryString, "Comm Class Call and LIne State Reporting Functional Descriptor"); break; case 6: sprintf((char *)temporaryString, "Comm Class Union Functional Descriptor"); break; case 7: sprintf((char *)temporaryString, "Comm Class Country Selection Functional Descriptor"); break; case 8: sprintf((char *)temporaryString, "Comm Class Telephone Operational Modes Functional Descriptor"); break; case 9: sprintf((char *)temporaryString, "Comm Class USB Terminal Functional Descriptor"); break; case 10: sprintf((char *)temporaryString, "Comm Class Network Channel Terminal Functional Descriptor"); break; case 11: sprintf((char *)temporaryString, "Comm Class Protocol Unit Functional Descriptor"); break; case 12: sprintf((char *)temporaryString, "Comm Class Extension Unit Functional Descriptor"); break; case 13: sprintf((char *)temporaryString, "Comm Class Multi-Channel Management Functional Descriptor"); break; case 14: sprintf((char *)temporaryString, "Comm Class CAPI Control Management Functional Descriptor"); break; case 15: sprintf((char *)temporaryString, "Comm Class Ethernet Networking Functional Descriptor"); break; case 16: sprintf((char *)temporaryString, "Comm Class ATM Networking Functional Descriptor"); break; default: sprintf((char *)temporaryString, "Comm Class Reserved Functional Descriptor (%d)",((GenericAudioDescriptorPtr)p)->descSubType); break; } [self PrintKeyVal:temporaryString val:"" forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode]; [self DumpRawDescriptor:p forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL+2]; break; case 0xff: if ( [self isInterfaceVDC:dev.idVendor idProduct:dev.idProduct]) { [DecodeVideoInterfaceDescriptor DecodeVideoInterfaceDescriptor:p deviceIntf:deviceIntf forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL+1 forNode:busprobeRootNode subClass:lastInterfaceSubClass ]; break; } // Fall thru. default: [self DumpRawDescriptor:p forDevice:deviceNumber atDepth:CONFIGURATION_DESCRIPTOR_LEVEL+1]; break; } break; } [interfaceClass release]; } // ________________________________________________________________________________________________ // DumpRawDescriptor // // When we don't know any better... +(void)DumpRawDescriptor:(Byte *)p forDevice:(int)deviceNumber atDepth:(int)depth { [self dump:p[0] byte:p forDevice:deviceNumber atDepth:depth]; } +(void)DoRegularCSInterface:(Byte *)p deviceClass:(USBClass *)interfaceClass forDevice:(int)deviceNumber atDepth:(int)depth { NSString *compositedString; char compositedCString[500]; if ([[interfaceClass subClassName] isEqualToString:@""]) compositedString = [NSString stringWithFormat:@"%s Class-Specific Interface", [[interfaceClass className] cString], [[interfaceClass subClassName] cString]]; else compositedString = [NSString stringWithFormat:@"%s/%s Class-Specific Interface", [[interfaceClass className] cString], [[interfaceClass subClassName] cString]]; sprintf(compositedCString,"%s",[compositedString cString]); [self PrintKeyVal:compositedCString val:"" forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; [self DumpRawDescriptor:p forDevice:deviceNumber atDepth:depth+1]; } +(void)DoRegularCSEndpoint:(Byte *)p deviceClass:(USBClass *)interfaceClass forDevice:(int)deviceNumber atDepth:(int)depth; { NSString *compositedString; char compositedCString[500]; if ([[interfaceClass subClassName] isEqualToString:@""]) compositedString = [NSString stringWithFormat:@"%s Class-Specific Endpoint", [[interfaceClass className] cString], [[interfaceClass subClassName] cString]]; else compositedString = [NSString stringWithFormat:@"%s/%s Class-Specific Endpoint", [[interfaceClass className] cString], [[interfaceClass subClassName] cString]]; sprintf(compositedCString,"%s",[compositedString cString]); [self PrintKeyVal:compositedCString val:"" forDevice:deviceNumber atDepth:depth forNode:busprobeRootNode]; [self DumpRawDescriptor:p forDevice:deviceNumber atDepth:depth+1]; } - (void)loadVendorNamesFromFile { NSString *vendorListString = [NSString stringWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"USBVendors.txt"]]; if (vendorListString == nil) { return; } else { NSArray *vendorsAndIDs = [vendorListString componentsSeparatedByString:@"\n"]; if (vendorsAndIDs == nil) { return; } else { NSEnumerator *enumerator = [vendorsAndIDs objectEnumerator]; NSString *vendorIDCombo; NSArray *aVendor; while ((vendorIDCombo = [enumerator nextObject])) { aVendor = [vendorIDCombo componentsSeparatedByString:@"|"]; if (aVendor == nil || [aVendor count] < 2) { continue; } [vendorNamesDictionary setObject:[aVendor objectAtIndex:1] forKey:[aVendor objectAtIndex:0]]; } } } } - (void)loadVDCListFromFile { // read the text file into a string NSString *vdcListString = [NSString stringWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"USBVDCList.txt"]]; // make sure we actually found and read the file properly if (vdcListString == nil) { return; } else { // create an array that is filled with the lines of the file NSArray *vendorsAndProducts = [vdcListString componentsSeparatedByString:@"\n"]; // make sure the file actually had some lines that could be separated if (vendorsAndProducts == nil) { return; } else { NSEnumerator *enumerator = [vendorsAndProducts objectEnumerator]; NSString *thisLine; NSArray *lineComponents; while ((thisLine = [enumerator nextObject])) { // split the line up into idVendor/idProduct lineComponents = [thisLine componentsSeparatedByString:@"|"]; // just to make sure the line actually had 2 items in it (idVendor/idProduct) if (lineComponents == nil || [lineComponents count] < 2) { continue; } // basically, we will put the entire line from the file, into the dictionary as a key. as the // corresponding value, we just use a dummy NSObject. That way, we just need to check for a non-nil // value to see if the idVendor/idProduct combo was in the file. [videoClassDevicesDictionary setObject:[[NSObject new] autorelease] forKey:thisLine]; } } } } + (NSString *)vendorNameFromVendorID:(NSString *)intValueAsString { NSString *vendorName = [vendorNamesDictionary objectForKey:intValueAsString]; if (vendorName != nil) return vendorName; else return @"unknown vendor"; } + (BOOL)isInterfaceVDC:(UInt32)vendorID idProduct:(UInt32)productID { NSString *hashString = [NSString stringWithFormat:@"%U|%U",vendorID,productID]; return ([videoClassDevicesDictionary objectForKey:hashString] != nil); } // init is like a constructor - init { self = [super init]; busprobeRootNode = [[Node alloc] init]; vendorNamesDictionary = [[NSMutableDictionary alloc] init]; videoClassDevicesDictionary = [[NSMutableDictionary alloc] init]; // add this line [self loadVendorNamesFromFile]; [self loadVDCListFromFile]; // this line return self; } // Data source methods get called automatically // This method is called repeatedly when the table view is displaying it self. - (id)outlineView:(NSOutlineView *)ov child:(int)index ofItem:(id)item { // is the parent non-nil? if (item) // Return the child return [item childAtIndex:index]; else // Else return the root return busprobeRootNode; } // Called repeatedly to find out if there should be an // "expand triangle" next to the label - (BOOL)outlineView:(NSOutlineView *)ov isItemExpandable:(id)item { // Returns YES if the node has children return [item expandable]; } // Called repeatedly when the table view is displaying itself - (int)outlineView:(NSOutlineView *)ov numberOfChildrenOfItem:(id)item { if (item == nil) { // The root object; return 1; } return [item childrenCount]; } // This method gets called repeatedly when the outline view is trying // to display it self. - (id)outlineView:(NSOutlineView *)ov objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { // What is returned depends upon which column it is // going to appear. if ([[tableColumn identifier] isEqual:@"itemValue"]){ // NSLog([item itemValue]); return [item itemValue]; } else { // NSLog([item itemName]); return [item itemName]; } } - (void)dealloc { [busprobeRootNode release]; [super dealloc]; } @end