All pastes #1518467 Raw Edit

Untitled

public text v1 · immutable
#1518467 ·published 2009-08-04 22:21 UTC
rendered paste body
/*

iPodSCSICodePages

This project shows how to access the hidden XML data from the iPod's SCSI CodePages
This XML contains, amongst other things, the iPod's serial number and audio / video capabilities

version 1.0
30 Jan 2007
Dave Addey
http://www.thismuchiknow.co.uk/

Adapted from Apple's SCSIOldAndNew sample code v1.1, found at:
http://developer.apple.com/samplecode/SCSIOldAndNew/
    
*/

#include <IOKit/scsi-commands/SCSICommandOperationCodes.h>
#include <Carbon/Carbon.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/scsi-commands/SCSITaskLib.h>

// IO_OBJECT_NULL was added in Mac OS X 10.4. If it's not defined in the headers, define it ourselves.
// This allows the same code to build using the 10.4u SDK for Intel-based systems and earlier SDKs for backwards compatibility
// with PowerPC-based systems.
#ifndef IO_OBJECT_NULL
#define    IO_OBJECT_NULL    ((io_object_t) 0)
#endif

// define a struct to contain the CodePage data we will be retrieving
typedef struct SCSICmd_INQUIRY_CodePageData
{
    UInt8        Padding1;
    UInt8        CodePage;
    UInt8        Padding2;
    UInt8        DataSize;
    char        Data[251];
} SCSICmd_INQUIRY_CodePageData;

bool ExecuteVPDInquiryUsingSTUC(SCSITaskDeviceInterface **interface, UInt32 inCodePage, SCSICmd_INQUIRY_CodePageData *inqBuffer)
{
    SCSITaskStatus                    taskStatus;
    SCSI_Sense_Data                    senseData;
    SCSICommandDescriptorBlock        cdb;
    SCSITaskInterface                **task = NULL;
    IOReturn                        kr = kIOReturnSuccess;
    IOVirtualRange                    *range = NULL;
    UInt64                            transferCount = 0;
    UInt32                            transferCountHi = 0;
    UInt32                            transferCountLo = 0;            

    assert(interface != NULL);

    // Create a task now that we have exclusive access.
    task = (*interface)->CreateSCSITask(interface);

    if (task == NULL) {
        fprintf(stderr, "xxxxxxxx CreateSCSITask returned NULL.\n");
        return false;
    }
    else {
    
        // Zero the buffer.  We set it to be the max data size, even though we may not receive this much back in inqBuffer->Data.
        bzero(inqBuffer, sizeof(SCSICmd_INQUIRY_CodePageData));

        // Allocate a virtual range for the buffer. If we had more than 1 scatter-gather entry,
        // we would allocate more than 1 IOVirtualRange.
        range = (IOVirtualRange *) malloc(sizeof(IOVirtualRange));
        if (range == NULL) {
            fprintf(stderr, "xxxxxxxx Could not malloc an IOVirtualRange\n" );
        }
    
        // Zero the senseData and CDB.
        bzero(&senseData, sizeof(senseData));
        bzero(cdb, sizeof(cdb));
    
        // Set up the range. The address is just the buffer's address. The length is our request size.
        range->address = (IOVirtualAddress) inqBuffer;
        range->length = sizeof(SCSICmd_INQUIRY_CodePageData);
    
        // We're going to execute an INQUIRY to the device.
        // see http://en.wikipedia.org/wiki/SCSI_Inquiry_Command for more info on the format of the SCSI INQUIRY command
        cdb[0] = kSCSICmd_INQUIRY;
    
        cdb[1] = 1; // set the EVPD flag to 1 so that the target will return Vital Product Data
        cdb[2] = inCodePage; // set byte 2 to be the codepage we want to retrieve
    
        cdb[4] = sizeof(SCSICmd_INQUIRY_CodePageData); // the size of the data we want to retrieve
                    
        // Set the actual cdb in the task.
        kr = (*task)->SetCommandDescriptorBlock(task, cdb, kSCSICDBSize_6Byte);
        if (kr != kIOReturnSuccess) {
            fprintf(stderr, "xxxxxxxx Error setting CDB. (0x%08x)\n", kr);        
        }
    
        // Set the scatter-gather entry in the task.
        kr = (*task)->SetScatterGatherEntries(task, range, 1, sizeof(SCSICmd_INQUIRY_CodePageData),
                                                   kSCSIDataTransfer_FromTargetToInitiator);
        if (kr != kIOReturnSuccess) {
            fprintf(stderr, "xxxxxxxx Error setting scatter-gather entries. (0x%08x)\n", kr);        
        }

        // Set the timeout in the task
        kr = (*task)->SetTimeoutDuration(task, 10000);
        if (kr != kIOReturnSuccess) {
            fprintf(stderr, "xxxxxxxx Error setting timeout. (0x%08x)\n", kr);        
        }
    
        // Send it!
        kr = (*task)->ExecuteTaskSync(task, &senseData, &taskStatus, &transferCount);
        if (kr != kIOReturnSuccess) {
            fprintf(stderr, "xxxxxxxx Error executing task. (0x%08x)\n", kr);        
        }
    
        // Get the transfer counts
        transferCountHi = ((transferCount >> 32) & 0xFFFFFFFF);
        transferCountLo = (transferCount & 0xFFFFFFFF);
    
        // Be a good citizen and clean up.
        free(range);
    
        // Release the task interface.
        (*task)->Release(task);

        // return a boolean to indicate if we got the data or not
        return (taskStatus == kSCSITaskStatus_GOOD);

    }
}

void CreateDeviceInterfaceUsingSTUC(io_object_t scsiDevice,
                                    IOCFPlugInInterface ***thePlugInInterface,   
                                    SCSITaskDeviceInterface ***theInterface)
{


    kern_return_t            kr = kIOReturnSuccess;
    IOCFPlugInInterface        **plugInInterface = NULL;
    SCSITaskDeviceInterface    **interface = NULL;
    HRESULT                    plugInResult = S_OK;
    SInt32                    score = 0;

    assert(scsiDevice != IO_OBJECT_NULL);

    // Create the base interface of type IOCFPlugInInterface.
    // This object will be used to create the SCSI device interface object.
    kr = IOCreatePlugInInterfaceForService(scsiDevice,
                                           kIOSCSITaskDeviceUserClientTypeID,
                                           kIOCFPlugInInterfaceID,
                                           &plugInInterface,
                                           &score);

    if (kr != kIOReturnSuccess) {
        fprintf(stderr, "xxxxxxxx Couldn't create a plugin interface for the io_service_t. (0x%08x)\n", kr);
    }
    else {
        // Query the base plugin interface for an instance of the specific SCSI device interface
        // object.
        plugInResult = (*plugInInterface)->QueryInterface(plugInInterface, 
                                            CFUUIDGetUUIDBytes(kIOSCSITaskDeviceInterfaceID),
                                            (LPVOID *) &interface);
    
        if (plugInResult != S_OK) {
            fprintf(stderr, "xxxxxxxx Couldn't create SCSI device interface. (%ld)\n", plugInResult);
        }
    }

    // Set the return values.
    *thePlugInInterface = plugInInterface;
    *theInterface = interface;
}

void GetiPodXML(io_service_t scsiDevice) {

    IOCFPlugInInterface        **plugInInterface = NULL;
    SCSITaskDeviceInterface    **interface = NULL;

    // Create an interface to this iPod, so that we can send it a SCSI Inquiry command
    CreateDeviceInterfaceUsingSTUC(scsiDevice,
                                   &plugInInterface,
                                   &interface);

    // if we got an interface, then try and get the xml
    if (interface != NULL) {

        // get the XML
        kern_return_t    kr = kIOReturnSuccess;

        assert(interface != NULL);
    
        // Get exclusive access for the device if we can. This must be done
        // before any SCSITasks can be created and sent to the device.
        kr = (*interface)->ObtainExclusiveAccess(interface);

        if (kr != kIOReturnSuccess) {
            fprintf(stderr, "xxxxxxxx ObtainExclusiveAccess failed. (0x%08x)\n", kr);        
        } else {

            // Execute a VPD INQUIRY command on the device to get the codepages of the hidden XML document

            // allocate a buffer for the codepage list output
            SCSICmd_INQUIRY_CodePageData CodePagesList;
            bool GotCodePagesList = false;
        
            // request the data on the codepages of the XML
            // this list of codepages is stored in codepage 0xC0
            GotCodePagesList = ExecuteVPDInquiryUsingSTUC(interface, 0xC0, &CodePagesList);

            // check we got the codepage and that we got the same codepage number back - this is a sign that we have some data to retrieve
            if (GotCodePagesList && CodePagesList.CodePage == 0xC0) {

                // allocate a codepage data structure for retrieving each page (will be re-used)
                SCSICmd_INQUIRY_CodePageData ThisCodePage;

                // a tracker bool for each attempt
                bool GotThisCodePage = false;

                // get each data codepage
                UInt32 i;
                for (i = 0; i < CodePagesList.DataSize; i++) {

                    // request the XML string data for this codepage (the number of which is in CodePagesList.Data[i])
                    GotThisCodePage = ExecuteVPDInquiryUsingSTUC(interface, CodePagesList.Data[i], &ThisCodePage);
                
                    if (GotThisCodePage) {
                
                        // print this chunk of XML to the standard output
                        // we will have received ThisCodePage.DataSize chars in ThisCodePage.Data,
                        // although we don't use ThisCodePage.DataSize for this simple call to fprintf
                        fprintf(stderr, "%s", ThisCodePage.Data);
                
                    } else {
                
                        // end the loop
                        i = CodePagesList.DataSize;
                
                    }

                }
            
            }

            // done with the interface now, so release our exclusive access to it
            kr = (*interface)->ReleaseExclusiveAccess(interface);
        
            if (kr != kIOReturnSuccess) {
                fprintf(stderr, "xxxxxxxx ReleaseExclusiveAccess failed. (0x%08x)\n", kr);        
            }

        }

        (*interface)->Release(interface);
    }

    // if we have a plugin interface, then release it
    if (plugInInterface != NULL) {
        IODestroyPlugInInterface(plugInInterface);
    }
}

void TestiPodsUsingSTUC(io_iterator_t iterator)
{
    io_service_t            scsiDevice = IO_OBJECT_NULL;

    while ((scsiDevice = IOIteratorNext(iterator))) {
        GetiPodXML(scsiDevice);
    }
}

void CreateMatchingDictionaryForSTUC(CFMutableDictionaryRef *matchingDict)
{
    CFMutableDictionaryRef     subDict;

    assert(matchingDict != NULL);

    // Create the dictionaries
    *matchingDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, 
                                               &kCFTypeDictionaryValueCallBacks);
    if (*matchingDict != NULL) {
        subDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, 
                                               &kCFTypeDictionaryValueCallBacks);

        if (subDict != NULL) {
            // Create a dictionary with the "SCSITaskDeviceCategory" key with the appropriate value
            // for the device type we're interested in. 
            // for iPods, this key is "iPodUserClientDevice"
            CFDictionarySetValue(subDict, CFSTR(kIOPropertySCSITaskDeviceCategory),
                                 CFSTR("iPodUserClientDevice"));
        }
    
        // Add the dictionary to the main dictionary with the key "IOPropertyMatch" to
        // narrow the search to the above dictionary.
        CFDictionarySetValue(*matchingDict, CFSTR(kIOPropertyMatchKey), subDict);
                        
        CFRelease(subDict);
    }
}

boolean_t FindiPodsUsingSTUC(mach_port_t masterPort,
                               io_iterator_t *iterator)
{
    CFMutableDictionaryRef    matchingDict = NULL;
    boolean_t                result = false;

    // Set up a matching dictionary to search the I/O Registry for SCSI devices
    // we are interested in
    CreateMatchingDictionaryForSTUC(&matchingDict);

    if (matchingDict == NULL) {
        fprintf(stderr, "xxxxxxxx Couldn't create a matching dictionary.\n");
    }
    else {
        kern_return_t    kr;
    
        // Now search I/O Registry for matching devices.
        kr = IOServiceGetMatchingServices(masterPort, matchingDict, iterator);

        if (*iterator && kr == kIOReturnSuccess) {
            result = true;
        }
    }

    // IOServiceGetMatchingServices consumes a reference to the
    // matching dictionary, so we don't need to release the dictionary ref.

    return result;
}

int main(int argc, char* argv[])
{
  CFMutableDictionaryRef  matchingDict;
  io_service_t      service;
    
  matchingDict = IOBSDNameMatching(kIOMasterPortDefault, 0, "disk2");
  if (NULL == matchingDict) {
    fprintf(stderr, "IOBSDNameMatching returned a NULL dictionary.\n");
    return 1;
  }

    // Fetch the object with the matching BSD node name. 
    // Note that there should only be one match, so IOServiceGetMatchingService is used instead of
    // IOServiceGetMatchingServices to simplify the code.
    service = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);    
    
    if (IO_OBJECT_NULL == service) {
      fprintf(stderr, "IOServiceGetMatchingService returned IO_OBJECT_NULL.\n");
      return 1;
    }


    GetiPodXML(service);

    return 0;
}