void IOVideoSampleStream::postFreeInputBuffer(UInt64 vbiTime, UInt64 outputTime, UInt64 totalFrameCount, UInt64 droppedFrameCount,UInt64 lastDisplayedSequenceNumber)
{
	IOStreamBuffer* buf = OSDynamicCast(IOStreamBuffer, _freeBuffers->getObject(0));
	if (NULL == buf)
	{
		USBLog(1, "IOVideoSampleStream::postFreeInputBuffer - no free buffers\n");
		if ((getOutputQueue())->entryCount > 0)
		{
			//all the free buffers are in the queue already so just alert the host
			(void) sendOutputNotification();
		}
		return;
	}
	
	_freeBuffers->removeObject(0);

	IOMemoryDescriptor *ctrlDescriptor = OSDynamicCast(IOMemoryDescriptor, buf->getControlBuffer());
	if (NULL != ctrlDescriptor)
	{
		USBLog(1, "IOVideoSampleStream got control buffer descriptor\n");
		SampleVideoDeviceControlBuffer theBuffControl, readBackControl;
		USBLog(1, "IOVideoSampleStream::postFreeInputBuffer - passed in vbiTime = %lld outputTime = %lld framecount = %lld droppedframecount = %lld lastDisplayedSequenceNumber = %lld  \n", vbiTime, outputTime, totalFrameCount, droppedFrameCount, lastDisplayedSequenceNumber);

		
		theBuffControl.vbiTime = vbiTime; 
		theBuffControl.outputTime = outputTime; 
		theBuffControl.totalFrameCount = totalFrameCount; 
		theBuffControl.droppedFrameCount = droppedFrameCount; 
		theBuffControl.firstVBITime = 0;
		theBuffControl.sequenceNumber = lastDisplayedSequenceNumber;
		theBuffControl.discontinuityFlags = 0;
		
		(void) ctrlDescriptor->prepare();
		
        ctrlDescriptor->writeBytes(0, &theBuffControl, buf->getControlBuffer()->getLength());
		ctrlDescriptor->readBytes(0, &readBackControl, buf->getControlBuffer()->getLength());
		USBLog(1, "IOVideoSampleStream::postFreeInputBuffer - control buffer info vbiTime = %lld outputTime = %lld framecount = %lld droppedframecount = %lld  sequencenumber = %lld\n", readBackControl.vbiTime, readBackControl.outputTime, readBackControl.totalFrameCount, readBackControl.droppedFrameCount,readBackControl.sequenceNumber);
		(void) ctrlDescriptor->complete();
	}
		
	IOReturn result = enqueueOutputBuffer(buf, 0, buf->getDataBuffer()->getLength(), 0, buf->getControlBuffer()->getLength()); 
	if (result != kIOReturnSuccess)
	{
		USBLog(1, "IOVideoSampleStream::postFreeInputBuffer help enqueueOutputBuffer failed! (%x)\n", result);
		return;
	}
	
	result = sendOutputNotification();
	if (result != kIOReturnSuccess)
	{
		USBLog(1, "IOVideoSampleStream::postFreeInputBuffer help sendOutputNotification failed! (%x)\n", result);
		return;
	}
}
Exemple #2
0
int st_readwrite(dev_t dev, struct uio *uio, int ioflag)
{
	IOSCSITape			*st			= IOSCSITape::devices[minor(dev)];
	IOMemoryDescriptor	*dataBuffer	= IOMemoryDescriptorFromUIO(uio);
	int					status		= ENOSYS;
	IOReturn			opStatus	= kIOReturnError;
	int					lastRealizedBytes = 0;
	
	if (dataBuffer == 0)
		return ENOMEM;
	
	dataBuffer->prepare();
	
	opStatus = st->ReadWrite(dataBuffer, &lastRealizedBytes);
	
	dataBuffer->complete();
	dataBuffer->release();
	
	if (opStatus == kIOReturnSuccess)
	{
		uio_setresid(uio, uio_resid(uio) - lastRealizedBytes);
		
		if (st->blkno != -1)
		{
			if (st->IsFixedBlockSize())
				st->blkno += (lastRealizedBytes / st->blksize);
			else
				st->blkno++;
		}

		status = KERN_SUCCESS;
	}
	else if (st->sense_flags & SENSE_FILEMARK)
	{
		if (st->fileno != -1)
		{
			st->fileno++;
			st->blkno = 0;
		}
		
		status = KERN_SUCCESS;
	}
	
	return status;
}
bool
IOFWUserLocalIsochPort::initWithUserDCLProgram ( 
		AllocateParams * 			params,
		IOFireWireUserClient & 		userclient,
		IOFireWireController &		controller )
{
	// sanity checking
	if ( params->programExportBytes == 0 )
	{
		ErrorLog ( "No program!" ) ;
		return false ;
	}
	
	fLock = IORecursiveLockAlloc () ;
	if ( ! fLock )
	{
		ErrorLog ( "Couldn't allocate recursive lock\n" ) ;
		return false ;
	}

// init easy params

	fUserObj = params->userObj ;
	fUserClient = & userclient ;
	fDCLPool = NULL ;
	fProgramCount = 0;
	fStarted = false ;

	IOReturn error = kIOReturnSuccess ;
	
// get user program ranges:

	IOAddressRange * bufferRanges = new IOAddressRange[ params->bufferRangeCount ] ;
	if ( !bufferRanges )
	{
		error = kIOReturnNoMemory ;
	}
	
	if ( !error )
	{
		error = fUserClient->copyUserData(params->bufferRanges,(mach_vm_address_t)bufferRanges, sizeof ( IOAddressRange ) * params->bufferRangeCount ) ;
	}

// create descriptor for program buffers

	IOMemoryDescriptor * bufferDesc = NULL ;
	if ( ! error )
	{
		IOByteCount length = 0 ;
		for ( unsigned index = 0; index < params->bufferRangeCount; ++index )
		{
			length += bufferRanges[ index ].length ;
		}			
	
		bufferDesc = IOMemoryDescriptor::withAddressRanges (	bufferRanges, params->bufferRangeCount, kIODirectionOutIn, 
															fUserClient->getOwningTask() ) ;
		if ( ! bufferDesc )
		{
			error = kIOReturnNoMemory ;
		}
		else
		{
		
			// IOLog( "IOFWUserLocalIsochPort::initWithUserDCLProgram - checkMemoryInRange status 0x%08lx\n", checkMemoryInRange( bufferDesc, 0x000000001FFFFFFF ) );
		
			error = bufferDesc->prepare( kIODirectionPrepareToPhys32 ) ;
			
			FWTrace( kFWTIsoch, kTPIsochPortUserInitWithUserDCLProgram, (uintptr_t)(fUserClient->getOwner()->getController()->getLink()), error, length, 0 );
			
			// IOLog( "IOFWUserLocalIsochPort::initWithUserDCLProgram - prep 32 checkMemoryInRange status 0x%08lx\n", checkMemoryInRange( bufferDesc, 0x000000001FFFFFFF ) );
			
		}
	}
	
// create map for buffers; we will need to get a virtual address for them

	IOMemoryMap * bufferMap = NULL ;
	if ( !error )
	{
		bufferMap = bufferDesc->map() ;
		if ( !bufferMap )
		{
			DebugLog( "Couldn't map program buffers\n" ) ;
			error = kIOReturnVMError ;
		}
		
		bufferDesc->release() ;
	}
	
	IOMemoryDescriptor * userProgramExportDesc = NULL ;
	if ( !error )
	{
		userProgramExportDesc = IOMemoryDescriptor::withAddressRange( 
																	 params->programData, 
																	 params->programExportBytes, 
																	 kIODirectionOut, 
																	 fUserClient->getOwningTask() ) ;
	
	}

	// get map of program export data
	if ( userProgramExportDesc )
	{
		error = userProgramExportDesc->prepare() ;
	}
	
	if ( !error )	
	{
		DCLCommand * opcodes = NULL ;
		switch ( params->version )
		{
			case kDCLExportDataLegacyVersion :

				error = importUserProgram( userProgramExportDesc, params->bufferRangeCount, bufferRanges, bufferMap ) ;
				ErrorLogCond( error, "importUserProgram returned %x\n", error ) ;

				if ( ! error )
				{
					opcodes = (DCLCommand*)fProgramBuffer ;
				}
				
				break ;

			case kDCLExportDataNuDCLRosettaVersion :

				fDCLPool = fUserClient->getOwner()->getBus()->createDCLPool() ;
				
				if ( ! fDCLPool )
				{
					error = kIOReturnNoMemory ;
				}

				if ( !error )
				{
					error = fDCLPool->importUserProgram( userProgramExportDesc, params->bufferRangeCount, bufferRanges, bufferMap ) ;
				}
				
				fProgramBuffer = new UInt8[ sizeof( DCLNuDCLLeader ) ] ;
				{
					DCLNuDCLLeader * leader = (DCLNuDCLLeader*)fProgramBuffer ;
					{
						leader->pNextDCLCommand = NULL ;	// unused - always NULL
						leader->opcode = kDCLNuDCLLeaderOp ;
						leader->program = fDCLPool ;
					}
					
					opcodes = (DCLCommand*)leader ;
				}
				
				break ;
			
			default :
			
				ErrorLog ( "unsupported DCL program type\n" ) ;
				error = kIOReturnBadArgument ;
				
				break ;
		}
		
		ErrorLogCond( !opcodes, "Couldn't get opcodes\n" ) ;
		
		IODCLProgram * program = NULL ;
		
		if ( opcodes )
		{
//			IOFWLocalIsochPort::printDCLProgram( opcodes ) ;
		
			IOFireWireBus::DCLTaskInfoAux	infoAux ;
			{
				infoAux.version = 2 ;

				infoAux.u.v2.bufferMemoryMap = bufferMap ;
				infoAux.u.v2.workloop = params->options & kFWIsochPortUseSeparateKernelThread ? createRealtimeThread() : NULL ;
				infoAux.u.v2.options = (IOFWIsochPortOptions)params->options ;
			}
						
			IOFireWireBus::DCLTaskInfo info = { 0, 0, 0, 0, 0, 0, & infoAux } ;
			
			program = fUserClient->getOwner()->getController()->getLink()->createDCLProgram(	params->talking,
																								opcodes,
																								& info,
																								params->startEvent, 
																								params->startState,
																								params->startMask ) ;

			bufferMap->release() ;		// retained by DCL program
			bufferMap = NULL ;
			
			if (  infoAux.u.v2.workloop )
			{
				// If we created a custom workloop, it will be retained by the program...
				// We can release our reference...
				infoAux.u.v2.workloop->release() ;
			}
			
			DebugLogCond( !program, "createDCLProgram returned nil\n" ) ;
		}

		if ( program )
		{
			if ( ! super::init( program, & controller ) )
			{
				ErrorLog ( "IOFWUserIsochPort::init failed\n" ) ;
				error = kIOReturnError ;
			}
		}
		else
		{
			DebugLog ( "Couldn't create DCL program\n" ) ;
			error = kIOReturnNoMemory ;
		}
		
		userProgramExportDesc->complete() ;
		userProgramExportDesc->release() ;
		userProgramExportDesc = NULL ;
	}
	
	delete [] bufferRanges ;
	
	InfoLog( "-IOFWUserLocalIsochPort::initWithUserDCLProgram error=%x (build date "__TIME__" "__DATE__")\n", error ) ;

	return ( ! error ) ;
}
//
// start
// when this method is called, I have been selected as the driver for this device.
// I can still return false to allow a different driver to load
//
bool 
local_IOath3kfrmwr::start(IOService *provider)
{
    IOReturn 				err;
    const IOUSBConfigurationDescriptor *cd;
    
    // Do all the work here, on an IOKit matching thread.
    
    // 0.1 Get my USB Device
    DEBUG_LOG("%s(%p)::start!\n", getName(), this);
    pUsbDev = OSDynamicCast(IOUSBDevice, provider);
    if(!pUsbDev) 
    {
        DEBUG_LOG("%s(%p)::start - Provider isn't a USB device!!!\n", getName(), this);
        return false;
    }

    // 0.2 Reset the device
    err = pUsbDev->ResetDevice();
    if (err)
    {
        DEBUG_LOG("%s(%p)::start - failed to reset the device\n", getName(), this);
        pUsbDev->close(this);
        return false;
    } else IOLog("%s(%p)::start: device reset\n", getName(), this);
    
    // 0.3 Find the first config/interface
    int numconf = 0;
    if ((numconf = pUsbDev->GetNumConfigurations()) < 1)
    {
        DEBUG_LOG("%s(%p)::start - no composite configurations\n", getName(), this);
        return false;
    } else DEBUG_LOG("%s(%p)::start: num configurations %d\n", getName(), this, numconf);
        
    cd = pUsbDev->GetFullConfigurationDescriptor(0);
    
    // Set the configuration to the first config
    if (!cd)
    {
        DEBUG_LOG("%s(%p)::start - no config descriptor\n", getName(), this);
        return false;
    }
	
    // 1.0 Open the USB device
    if (!pUsbDev->open(this))
    {
        DEBUG_LOG("%s(%p)::start - unable to open device for configuration\n", getName(), this);
        return false;
    }
    
    // 1.1 Set the configuration to the first config
    err = pUsbDev->SetConfiguration(this, cd->bConfigurationValue, true);
    if (err)
    {
        DEBUG_LOG("%s(%p)::start - unable to set the configuration\n", getName(), this);
        pUsbDev->close(this);
        return false;
    }
    
    DEBUG_LOG("%s(%p)::start - UPLOADED - %c\n", getName(), this,uploaded);
    //Uploading only one time
    if (!uploaded) {
        
    
    // 1.2 Get the status of the USB device (optional, for diag.)
    USBStatus status;
    err = pUsbDev->GetDeviceStatus(&status);
    if (err)
    {
        DEBUG_LOG("%s(%p)::start - unable to get device status\n", getName(), this);
        pUsbDev->close(this);
        return false;
    } else IOLog("%s(%p)::start: device status %d\n", getName(), this, (int)status);

    // 2.0 Find the interface for bulk endpoint transfers
    IOUSBFindInterfaceRequest request;
    request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
    request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
    request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
    request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
    
    IOUSBInterface * intf = pUsbDev->FindNextInterface(NULL, &request);
    if (!intf) {
        DEBUG_LOG("%s(%p)::start - unable to find interface\n", getName(), this);
        pUsbDev->close(this);
        return false;
    }

    // 2.1 Open the interface
    if (!intf->open(this))
    {
        DEBUG_LOG("%s(%p)::start - unable to open interface\n", getName(), this);
        pUsbDev->close(this);
        return false;
    }

    // 2.2 Get info on endpoints (optional, for diag.)
    int numep = intf->GetNumEndpoints();
    DEBUG_LOG("%s(%p)::start: interface has %d endpoints\n", getName(), this, numep);
    
    UInt8 transferType = 0;
    UInt16 maxPacketSize = 0;
    UInt8 interval = 0;
    err = intf->GetEndpointProperties(0, 0x02, kUSBOut, &transferType, &maxPacketSize, &interval);
    if (err) {
        DEBUG_LOG("%s(%p)::start - failed to get endpoint 2 properties\n", getName(), this);
        intf->close(this);
        pUsbDev->close(this);
        return false;    
    } else DEBUG_LOG("%s(%p)::start: EP2 %d %d %d\n", getName(), this, transferType, maxPacketSize, interval);
    
    err = intf->GetEndpointProperties(0, 0x01, kUSBIn, &transferType, &maxPacketSize, &interval);
    if (err) {
        DEBUG_LOG("%s(%p)::start - failed to get endpoint 1 properties\n", getName(), this);
        intf->close(this);
        pUsbDev->close(this);
        return false;    
    } else {DEBUG_LOG("%s(%p)::start: EP1 %d %d %d\n", getName(), this, transferType, maxPacketSize, interval);
        }

    // 2.3 Get the pipe for bulk endpoint 2 Out
    IOUSBPipe * pipe = intf->GetPipeObj(0x02);
    if (!pipe) {
        DEBUG_LOG("%s(%p)::start - failed to find bulk out pipe\n", getName(), this);
        intf->close(this);
        pUsbDev->close(this);
        return false;    
    }
    /*  // TODO: Test the alternative way to do it:
     IOUSBFindEndpointRequest pipereq;
     pipereq.type = kUSBBulk;
     pipereq.direction = kUSBOut;
     pipereq.maxPacketSize = BULK_SIZE;
     pipereq.interval = 0;
     IOUSBPipe *pipe = intf->FindNextPipe(NULL, &pipereq);
     pipe = intf->FindNextPipe(pipe, &pipereq);
     if (!pipe) {
     DEBUG_LOG("%s(%p)::start - failed to find bulk out pipe 2\n", getName(), this);
     intf->close(this);
     pUsbDev->close(this);
     return false;    
     }
     */

    
    // 3.0 Send request to Control Endpoint to initiate the firmware transfer
    IOUSBDevRequest ctlreq;
    ctlreq.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice);
    ctlreq.bRequest = USB_REQ_DFU_DNLOAD;
    ctlreq.wValue = 0;
    ctlreq.wIndex = 0;
    ctlreq.wLength = 20;
    ctlreq.pData = firmware_buf;

#if 0  // Trying to troubleshoot the problem after Restart (with OSBundleRequired Root)
    for (int irep = 0; irep < 5; irep++) { // retry on error
        err = pUsbDev->DeviceRequest(&ctlreq); // (synchronous, will block)
        if (err)
            DEBUG_LOG("%s(%p)::start - failed to initiate firmware transfer (%d), retrying (%d)\n", getName(), this, err, irep+1);
        else
            break;
    }
#else
    err = pUsbDev->DeviceRequest(&ctlreq); // (synchronous, will block)
#endif
    if (err) {
        DEBUG_LOG("%s(%p)::start - failed to initiate firmware transfer (%d)\n", getName(), this, err);
        intf->close(this);
        pUsbDev->close(this);
        return false;
    }

    // 3.1 Create IOMemoryDescriptor for bulk transfers
    char buftmp[BULK_SIZE];
    IOMemoryDescriptor * membuf = IOMemoryDescriptor::withAddress(&buftmp, BULK_SIZE, kIODirectionNone);
    if (!membuf) {
        DEBUG_LOG("%s(%p)::start - failed to map memory descriptor\n", getName(), this);
        intf->close(this);
        pUsbDev->close(this);
        return false; 
    }
    err = membuf->prepare();
    if (err) {
        DEBUG_LOG("%s(%p)::start - failed to prepare memory descriptor\n", getName(), this);
        intf->close(this);
        pUsbDev->close(this);
        return false; 
    }
    
    // 3.2 Send the rest of firmware to the bulk pipe
    char * buf = firmware_buf;
    int size = sizeof(firmware_buf); 
    buf += 20;
    size -= 20;
    int ii = 1;
    while (size) {
        int to_send = size < BULK_SIZE ? size : BULK_SIZE; 
        
        memcpy(buftmp, buf, to_send);
        err = pipe->Write(membuf, 10000, 10000, to_send);
        if (err) {
            DEBUG_LOG("%s(%p)::start - failed to write firmware to bulk pipe (%d)\n", getName(), this, ii);
            intf->close(this);
            pUsbDev->close(this);
            return false; 
        }
        buf += to_send;
        size -= to_send;
        ii++;
    }
    IOLog("%s(%p)::start: firmware was sent to bulk pipe\n", getName(), this);
    
    err = membuf->complete();
    if (err) {
        DEBUG_LOG("%s(%p)::start - failed to complete memory descriptor\n", getName(), this);
        intf->close(this);
        pUsbDev->close(this);
        uploaded = false;
        return false; 
    }

    /*  // TODO: Test the alternative way to do it:
     IOMemoryDescriptor * membuf = IOMemoryDescriptor::withAddress(&firmware_buf[20], 246804-20, kIODirectionNone); // sizeof(firmware_buf)
     if (!membuf) {
     DEBUG_LOG("%s(%p)::start - failed to map memory descriptor\n", getName(), this);
     intf->close(this);
     pUsbDev->close(this);
     return false; 
     }
     err = membuf->prepare();
     if (err) {
     DEBUG_LOG("%s(%p)::start - failed to prepare memory descriptor\n", getName(), this);
     intf->close(this);
     pUsbDev->close(this);
     return false; 
     }
     
     //err = pipe->Write(membuf);
     err = pipe->Write(membuf, 10000, 10000, 246804-20, NULL);
     if (err) {
     DEBUG_LOG("%s(%p)::start - failed to write firmware to bulk pipe\n", getName(), this);
     intf->close(this);
     pUsbDev->close(this);
     return false; 
     }
     DEBUG_LOG("%s(%p)::start: firmware was sent to bulk pipe\n", getName(), this);
     */
    
    
    // 4.0 Get device status (it fails, but somehow is important for operational device)
    err = pUsbDev->GetDeviceStatus(&status);
    if (err)
    {
        DEBUG_LOG("%s(%p)::start - unable to get device status\n", getName(), this);
        intf->close(this);
        pUsbDev->close(this);
        
        DEBUG_LOG("%s(%p)::start - Firmware Uploaded State Changed to - %c\n", getName(), this,uploaded);
        // Set the Upload State
        uploaded = true;
        
        return false;
    } else {DEBUG_LOG("%s(%p)::start: device status %d\n", getName(), this, (int)status);
    
    }
    
    // Close the interface
    intf->close(this);
        
    DEBUG_LOG("%s(%p)::start - Firmware Uploaded State Changed to - %c\n", getName(), this,uploaded);
    // Set the Upload State
        uploaded = true;
    }
    else {
        IOLog("%s(%p)::Firmware already uploaded\n", getName(), this);
    }
    
    // Close the USB device
    pUsbDev->close(this);
    return false;  // return false to allow a different driver to load
}
IOReturn SamplePCIUserClientClassName::method2( SampleStructForMethod2 * structIn,
        SampleResultsForMethod2 * structOut,
        IOByteCount inputSize, IOByteCount * outputSize )

{
    IOReturn err;
    IOMemoryDescriptor * memDesc = 0;
    UInt32 param1 = structIn->parameter1;

    uint64_t clientAddr = structIn->data_pointer;
    uint64_t size = structIn->data_length;

    // Rosetta
    if (fCrossEndian) {
        param1 = OSSwapInt32(param1);
    }

    IOLog("SamplePCIUserClient::method2(" UInt32_x_FORMAT ")\n", param1);
    IOLog( "fClientShared->string == \"%s\"\n", fClientShared->string );

    structOut->results1 = 0x87654321;
    // Rosetta
    if (fCrossEndian) {
        structOut->results1 = OSSwapInt64(structOut->results1);
        clientAddr = OSSwapInt64(clientAddr);
        size = OSSwapInt64(size);
    }

    do
    {

#if defined(__ppc__) && (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4)
        // construct a memory descriptor for the out of line client memory
        // old 32 bit API - this will fail and log a backtrace if the task is 64 bit
        IOLog("The Pre-Leopard way to construct a memory descriptor\n");
        memDesc = IOMemoryDescriptor::withAddress( (vm_address_t) clientAddr, (IOByteCount) size, kIODirectionNone, fTask );
        if (memDesc == NULL) {
            IOLog("IOMemoryDescriptor::withAddress failed\n");
            err = kIOReturnVMError;
            continue;
        }
#else
        // 64 bit API - works on all tasks, whether 64 bit or 32 bit
        IOLog("The Leopard and later way to construct a memory descriptor\n");
        memDesc = IOMemoryDescriptor::withAddressRange( clientAddr, size, kIODirectionNone, fTask );
        if (memDesc == NULL) {
            IOLog("IOMemoryDescriptor::withAddresswithAddressRange failed\n");
            err = kIOReturnVMError;
            continue;
        }
#endif
        // Wire it and make sure we can write it
        err = memDesc->prepare( kIODirectionOutIn );
        if (kIOReturnSuccess != err) {
            IOLog("IOMemoryDescriptor::prepare failed(0x%08x)\n", err);
            continue;
        }

        // Generate a DMA list for the client memory
        err = fDriver->generateDMAAddresses(memDesc);

        // Other methods to access client memory:

        // readBytes/writeBytes allow programmed I/O to/from an offset in the buffer
        char pioBuffer[ 200 ];
        memDesc->readBytes(32, &pioBuffer, sizeof(pioBuffer));
        IOLog("readBytes: \"%s\"\n", pioBuffer);

        // map() will create a mapping in the kernel address space.
        IOMemoryMap* memMap = memDesc->map();
        if (memMap) {
            char* address = (char *) memMap->getVirtualAddress();
            IOLog("kernel mapped: \"%s\"\n", address + 32);
            memMap->release();
        } else {
            IOLog("memDesc map(kernel) failed\n");
        }

        // this map() will create a mapping in the users (the client of this IOUserClient) address space.
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
        memMap = memDesc->map(fTask, 0, kIOMapAnywhere);
#else
        memMap = memDesc->createMappingInTask(fTask, 0, kIOMapAnywhere);
#endif
        if (memMap) {
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
            IOLog("The pre-Leopard way to construct a memory descriptor\n");
            // old 32 bit API - this will truncate and log a backtrace if the task is 64 bit
            IOVirtualAddress address32 = memMap->getVirtualAddress();
            IOLog("user32 mapped: " VirtAddr_FORMAT "\n", address32);
#else
            IOLog("The Leopard and later way to construct a memory descriptor\n");
            // new 64 bit API - same for 32 bit and 64 bit client tasks
            mach_vm_address_t address64 = memMap->getAddress();
            IOLog("user64 mapped: 0x%016llx\n", address64);
            memMap->release();
#endif
        } else {
            IOLog("memDesc map(user) failed\n");
        }

        // Done with the I/O now.
        memDesc->complete( kIODirectionOutIn );

    } while ( false );

    if (memDesc)
        memDesc->release();

    return err;
}
bool RadeonController::start( IOService * provider )
{
	if (!super::start(provider)) return false;
	
	device = OSDynamicCast(IOPCIDevice, provider);
	if (device == NULL) return false;
	
	//get user options
	OSBoolean *prop;
	
	OSDictionary *dict = OSDynamicCast(OSDictionary, getProperty("UserOptions"));
	
	bzero(&options, sizeof(UserOptions));
	options.HWCursorSupport = FALSE;
	options.enableGammaTable = FALSE;
	options.enableOSXI2C = FALSE;
	
	options.lowPowerMode = FALSE;
	if (dict) {
		prop = OSDynamicCast(OSBoolean, dict->getObject("enableHWCursor"));
		if (prop) options.HWCursorSupport = prop->getValue();
		prop = OSDynamicCast(OSBoolean, dict->getObject("debugMode"));
		if (prop) options.debugMode = prop->getValue();
		if (options.debugMode) options.HWCursorSupport = FALSE;
		prop = OSDynamicCast(OSBoolean, dict->getObject("enableGammaTable"));
		if (prop) options.enableGammaTable = prop->getValue();
		prop = OSDynamicCast(OSBoolean, dict->getObject("lowPowerMode"));
		if (prop) options.lowPowerMode = prop->getValue();
	}
	options.verbosity = 1;
#ifdef DEBUG
	if (0 == getRegistryRoot()->getProperty("RadeonDumpReady")) {
		getRegistryRoot()->setProperty("RadeonDumpReady", kOSBooleanTrue);
		DumpMsg.mVerbose = 1;
		DumpMsg.client = 1;
		DumpMsg.mMsgBufferSize = 65535;
		if (dict) {
			OSNumber *optionNum;
			optionNum = OSDynamicCast(OSNumber, dict->getObject("verboseLevel"));
			if (optionNum) DumpMsg.mVerbose = optionNum->unsigned32BitValue();
			optionNum = OSDynamicCast(OSNumber, dict->getObject("MsgBufferSize"));
			if (optionNum) DumpMsg.mMsgBufferSize = max(65535, optionNum->unsigned32BitValue());
		}	
		DumpMsg.mMsgBufferEnabled = false;
		DumpMsg.mMsgBufferPos = 0;
		DumpMsg.mMessageLock = IOLockAlloc();
		DumpMsg.mMsgBuffer = (char *) IOMalloc(DumpMsg.mMsgBufferSize);
		if (!DumpMsg.mMsgBuffer) {
			IOLog("error: couldn't allocate message buffer (%ld bytes)\n", DumpMsg.mMsgBufferSize);
			return false;
		}
		enableMsgBuffer(true);
	} else DumpMsg.client += 1;
	options.verbosity = DumpMsg.mVerbose;
#endif
	
	device->setMemoryEnable(true);
	IOMap = device->mapDeviceMemoryWithRegister( kIOPCIConfigBaseAddress2 );
	if (IOMap == NULL) return false;
	FBMap = device->mapDeviceMemoryWithRegister( kIOPCIConfigBaseAddress0 );
	if (FBMap == NULL) return false;
	memoryMap.MMIOBase = (pointer) IOMap->getVirtualAddress();
	memoryMap.MMIOMapSize = IOMap->getLength();
	memoryMap.FbBase = (pointer) FBMap->getVirtualAddress();
	memoryMap.FbMapSize = FBMap->getLength();
	memoryMap.FbPhysBase = (unsigned long)FBMap->getPhysicalAddress();
	memoryMap.bitsPerPixel = 32;
	memoryMap.bitsPerComponent = 8;
	memoryMap.colorFormat = 0;	//0 for non-64 bit
	
	memoryMap.BIOSCopy = NULL;
	memoryMap.BIOSLength = 0;
	
	IOMemoryDescriptor * mem;
	mem = IOMemoryDescriptor::withPhysicalAddress((IOPhysicalAddress) RHD_VBIOS_BASE, RHD_VBIOS_SIZE, kIODirectionOut);
	if (mem) {
		memoryMap.BIOSCopy = (unsigned char *)IOMalloc(RHD_VBIOS_SIZE);
		if (memoryMap.BIOSCopy) {
			mem->prepare(kIODirectionOut);
			if (!(memoryMap.BIOSLength = mem->readBytes(0, memoryMap.BIOSCopy, RHD_VBIOS_SIZE))) {
				LOG("Cannot read BIOS image\n");
				memoryMap.BIOSLength = 0;
			}
			if ((unsigned int)memoryMap.BIOSLength != RHD_VBIOS_SIZE)
				LOG("Read only %d of %d bytes of BIOS image\n", memoryMap.BIOSLength, RHD_VBIOS_SIZE);
			mem->complete(kIODirectionOut);
		}
	}

	if (dict) {
		const char typeKey[2][8] = {"@0,TYPE", "@1,TYPE"};
		const char EDIDKey[2][8] = {"@0,EDID", "@1,EDID"};
		const char fixedModesKey[2][17] = {"@0,UseFixedModes", "@1,UseFixedModes"};
		OSString *type;
		OSData *edidData;
		OSBoolean *boolData;
		int i;
		for (i = 0;i < 2;i++) {
			type = OSDynamicCast(OSString, dict->getObject(typeKey[i]));
			if (!type) continue;
			edidData = OSDynamicCast(OSData, dict->getObject(EDIDKey[i]));
			if (edidData == NULL) continue;
			options.EDID_Block[i] = (unsigned char *)IOMalloc(edidData->getLength());
			if (options.EDID_Block[i] == NULL) continue;
			strncpy(options.outputTypes[i], type->getCStringNoCopy(), outputTypeLength);
			bcopy(edidData->getBytesNoCopy(), options.EDID_Block[i], edidData->getLength());
			options.EDID_Length[i] = edidData->getLength();
			
			boolData = OSDynamicCast(OSBoolean, dict->getObject(fixedModesKey[i]));
			if (boolData) options.UseFixedModes[i] = boolData->getValue();
		}
	}
		
	xf86Screens[0] = IONew(ScrnInfoRec, 1);	//using global variable, will change it later
	ScrnInfoPtr pScrn = xf86Screens[0];
	if (pScrn == NULL) return false;
	bzero(pScrn, sizeof(ScrnInfoRec));
	MAKE_REG_ENTRY(&nub, device);
	pciRec.chipType = device->configRead16(kIOPCIConfigDeviceID);
	pciRec.subsysVendor = device->configRead16(kIOPCIConfigSubSystemVendorID);
	pciRec.subsysCard = device->configRead16(kIOPCIConfigSubSystemID);
	pciRec.biosSize = 16;	//RHD_VBIOS_SIZE = 1 << 16
	pScrn->PciTag = &nub;
	pScrn->PciInfo = &pciRec;
	pScrn->options = &options;
	pScrn->memPhysBase = (unsigned long)FBMap->getPhysicalAddress();
	pScrn->fbOffset = 0;	//scanout offset
	pScrn->bitsPerPixel = 32;
	pScrn->bitsPerComponent = 8;
	pScrn->colorFormat = 0;
	pScrn->depth = pScrn->bitsPerPixel;
	pScrn->memoryMap = &memoryMap;
	
	
	createNubs(provider);
	
	setModel(device);
	
	return true;
}
IOReturn
org_pqrs_driver_KeyRemap4MacBook_UserClient_kext::callback_synchronized_communication(const BridgeUserClientStruct* inputdata, uint64_t* outputdata)
{
  IOReturn result = kIOReturnSuccess;
  IOMemoryDescriptor* memorydescriptor = NULL;

  if (! inputdata || ! outputdata) {
    result = kIOReturnBadArgument;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnBadArgument\n");
    goto finish;
  }

  if (provider_ == NULL || isInactive()) {
    // Return an error if we don't have a provider. This could happen if the user process
    // called callback_synchronized_communication without calling IOServiceOpen first.
    // Or, the user client could be in the process of being terminated and is thus inactive.
    result = kIOReturnNotAttached;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnNotAttached\n");
    goto finish;
  }

  if (! provider_->isOpen(this)) {
    // Return an error if we do not have the driver open. This could happen if the user process
    // did not call callback_open before calling this function.
    result = kIOReturnNotOpen;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnNotOpen\n");
    goto finish;
  }

  memorydescriptor = IOMemoryDescriptor::withAddressRange(inputdata->data, inputdata->size, kIODirectionNone, task_);
  if (! memorydescriptor) {
    result = kIOReturnVMError;
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication kIOReturnVMError\n");
    goto finish;
  }

  // wire it and make sure we can write it
  result = memorydescriptor->prepare(kIODirectionOutIn);
  if (kIOReturnSuccess != result) {
    IOLOG_ERROR("UserClient_kext::callback_synchronized_communication IOMemoryDescriptor::prepare failed(0x%x)\n", result);
    goto finish;
  }

  {
    // this map() will create a mapping in the users (the client of this IOUserClient) address space.
    IOMemoryMap* memorymap = memorydescriptor->map();
    if (! memorymap) {
      result = kIOReturnVMError;
      IOLOG_ERROR("UserClient_kext::callback_synchronized_communication IOMemoryDescriptor::map failed\n");

    } else {
      mach_vm_address_t address = memorymap->getAddress();
      handle_synchronized_communication(inputdata->type, inputdata->option, address, inputdata->size, outputdata);
      memorymap->release();
    }
  }

  // Done with the I/O now.
  memorydescriptor->complete(kIODirectionOutIn);

finish:
  if (memorydescriptor) {
    memorydescriptor->release();
  }

  return result;
}
Exemple #8
0
IOReturn
IrDAUserClient::getIrDALog(void *pIn, void *pOut, IOByteCount inputSize, IOByteCount *outPutSize)
{
#if (hasTracing > 0)

    IOMemoryDescriptor *md;         // make a memory descriptor for the client's big buffer
    unsigned char *input = (unsigned char *)pIn;
    mach_vm_address_t bigaddr;
    IOByteCount   biglen;
    IrDALogInfo *info;

    require(inputSize == 9, Fail);
    require(outPutSize, Fail);
    require(*outPutSize == sizeof(IrDALogInfo), Fail);
	
    //bigaddr = input[1] << 24 | input[2] << 16 | input[3] << 8 | input[4];
    //biglen  = input[5] << 24 | input[6] << 16 | input[7] << 8 | input[8];
    bcopy(&input[1], &bigaddr, sizeof(bigaddr));
    bcopy(&input[5], &biglen, sizeof(biglen));
    
    //IOLog("biglen is %d\n", biglen);
    
    // create and init the memory descriptor
    //md = IOMemoryDescriptor::withAddress(bigaddr, biglen, kIODirectionOutIn, fTask);        // REVIEW direction
    //use withAddressRange() and prepare() instead
    md = IOMemoryDescriptor::withAddressRange(bigaddr, biglen, kIODirectionOutIn, fTask);        // REVIEW direction
    md->prepare(kIODirectionOutIn);

    require(md, Fail);
    
    info = IrDALogGetInfo();        // get the info block
		    
    //ELG(info->hdr,       info->hdrSize,       'irda', "info hdr");
    //ELG(info->eventLog,  info->eventLogSize,  'irda', "info events");
    //ELG(info->msgBuffer, info->msgBufferSize, 'irda', "info msg buf");
		    
    bcopy(info, pOut, sizeof(*info));       // copy the info record back to the client
    *outPutSize = sizeof(*info);            // set the output size (nop, it already is)
    
    // copy the buffer over now if there is room
    if (biglen >= info->hdrSize + info->eventLogSize + info->msgBufferSize) {
	IOByteCount ct;
	IOReturn rc;
	
	rc = md->prepare(kIODirectionNone);
	if (rc)  {ELG(-1, rc, 'irda', "prepare failed"); }
	
	ct = md->writeBytes(0,                              info->hdr,       info->hdrSize);
	if (ct != info->hdrSize) ELG(-1, rc, 'irda', "write of hdr failed");
	
	ct = md->writeBytes(info->hdrSize,                   info->eventLog,  info->eventLogSize);
	if (ct != info->eventLogSize) ELG(-1, rc, 'irda', "write of events failed");
	
	ct = md->writeBytes(info->hdrSize+info->eventLogSize, info->msgBuffer, info->msgBufferSize);
	if (ct != info->msgBufferSize) ELG(-1, rc, 'irda', "write of msgs failed");
	
	ELG(0, info->hdrSize+info->eventLogSize, 'irda', "wrote msgs at offset");
	
	rc = md->complete(kIODirectionNone);
	if (!rc) { ELG(0, 0, 'irda', "complete worked"); }
	else    { ELG(-1, rc, 'irda', "complete failed"); }

	// todo check return code of above before resetting the buffer
	IrDALogReset();     // reset the buffer now
    }
    md->release();  // free it

    return kIOReturnSuccess;


Fail:

#endif          // hasTracing > 0

    return kIOReturnBadArgument;
}
IOReturn
SATSMARTUserClient::ReadDataThresholds (UInt32 * dataOut,
                                        IOByteCount * outputSize)
{
    
    IOReturn status  = kIOReturnSuccess;
    IOSATCommand *                  command = NULL;
    IOMemoryDescriptor *    buffer  = NULL;
    DEBUG_LOG("%s[%p]::%s\n", getClassName(), this, __FUNCTION__);
    
    if (!dataOut || !outputSize || *outputSize != sizeof ( ATASMARTDataThresholds ) ) {
        return kIOReturnBadArgument;
    }
    
    fOutstandingCommands++;
    
    if ( isInactive ( ) )
    {
        
        status = kIOReturnNoDevice;
        goto ErrorExit;
        
    }
    
    fProvider->retain ( );
    
    command = AllocateCommand ( );
    if ( command == NULL )
    {
        
        status = kIOReturnNoResources;
        goto ReleaseProvider;
        
    }
    
    buffer = IOMemoryDescriptor::withAddress(dataOut, sizeof ( ATASMARTDataThresholds ), kIODirectionIn);
    
    if ( buffer == NULL )
    {
        
        status = kIOReturnNoResources;
        goto ReleaseCommand;
        
    }
    
    status = buffer->prepare ( );
    if ( status != kIOReturnSuccess )
    {
        
        goto ReleaseBuffer;
        
    }
    
    command->setBuffer                      ( buffer );
    command->setByteCount           ( sizeof ( ATASMARTDataThresholds ) );
    command->setFeatures            ( kFeaturesRegisterReadDataThresholds );
    command->setOpcode                      ( kATAFnExecIO );
    command->setTimeoutMS           ( kATAThirtySecondTimeoutInMS );
    command->setCylLo                       ( kSMARTMagicCylinderLoValue );
    command->setCylHi                       ( kSMARTMagicCylinderHiValue );
    command->setCommand                     ( kATAcmdSMART );
    command->setFlags                       ( mATAFlagIORead );
    
    status = SendSMARTCommand ( command );
    if ( status == kIOReturnIOError )
    {
        
        if ( command->getEndErrorReg ( ) & 0x04 )
        {
            
            ERROR_LOG ( "ReadDataThresholds unsupported\n" );
            status = kIOReturnUnsupported;
            
        }
        
        if ( command->getEndErrorReg ( ) & 0x10 )
        {
            
            ERROR_LOG ( "ReadDataThresholds Not readable\n" );
            status = kIOReturnNotReadable;
            
        }
        
    }
    
    *outputSize = buffer->getLength();
    
    buffer->complete ( );
    
    
ReleaseBuffer:
    
    
    buffer->release ( );
    buffer = NULL;
    
    
ReleaseCommand:
    
    
    DeallocateCommand ( command );
    command = NULL;
    
    
ReleaseProvider:
    
    
    fProvider->release ( );
    
    
ErrorExit:
    
    
    fOutstandingCommands--;
    
    DEBUG_LOG("%s[%p]::%s result %d\n", getClassName(), this,  __FUNCTION__, status);
    return status;
    
}
IOReturn
SATSMARTUserClient::GetIdentifyData (UInt32 * dataOut,
				     IOByteCount * outputSize)
{
    
    IOReturn status                  = kIOReturnSuccess;
    IOSATCommand *                          command                 = NULL;
    IOMemoryDescriptor *      buffer                  = NULL;
    DEBUG_LOG("%s[%p]::%s %p(%ld)\n", getClassName(), this, __FUNCTION__, dataOut, (long)(outputSize));
    
    if (!dataOut || !outputSize || *outputSize < kATADefaultSectorSize ) {
        return kIOReturnBadArgument;
    }
    
    fOutstandingCommands++;
    
    if ( isInactive ( ) )
    {
        
        status = kIOReturnNoDevice;
        goto ErrorExit;
        
    }
    
    fProvider->retain ( );
    
    command = AllocateCommand ( );
    if ( command == NULL )
    {
        
        status = kIOReturnNoResources;
        goto ReleaseProvider;
        
    }
    
    buffer = IOMemoryDescriptor::withAddress(dataOut, kATADefaultSectorSize, kIODirectionIn);
    if ( buffer == NULL )
    {
        
        status = kIOReturnNoResources;
        goto ReleaseCommand;
        
    }
    
    status = buffer->prepare ( );
    if ( status != kIOReturnSuccess )
    {
        
        goto ReleaseBuffer;
        
    }
    
    command->setBuffer                              ( buffer );
    command->setByteCount                   ( kATADefaultSectorSize );
    command->setTransferChunkSize   ( kATADefaultSectorSize );
    command->setOpcode                              ( kATAFnExecIO );
    command->setTimeoutMS                   ( kATAThirtySecondTimeoutInMS );
    command->setCommand                             ( kATAcmdDriveIdentify );
    command->setFlags                               ( mATAFlagIORead );
    command->setRegMask                             ( ( ataRegMask ) ( mATAErrFeaturesValid | mATAStatusCmdValid ) );
    
    status = SendSMARTCommand ( command );
    if ( status == kIOReturnSuccess )
    {
        
#if defined(__BIG_ENDIAN__)
        UInt8 *         bufferToCopy = identifyDataPtr;
        
        // The identify device info needs to be byte-swapped on big-endian (ppc)
        // systems becuase it is data that is produced by the drive, read across a
        // 16-bit little-endian PCI interface, directly into a big-endian system.
        // Regular data doesn't need to be byte-swapped because it is written and
        // read from the host and is intrinsically byte-order correct.
        
        IOByteCount index;
        UInt8 temp;
        UInt8 *                 firstBytePtr;
        UInt8 *                 identifyDataPtr = ( UInt8 * )dataOut;
        
        for ( index = 0; index < buffer->getLength ( ); index += 2 )
        {
            
            firstBytePtr            = identifyDataPtr;                          // save pointer
            temp                            = *identifyDataPtr++;               // Save Byte0, point to Byte1
            *firstBytePtr           = *identifyDataPtr;                         // Byte0 = Byte1
            *identifyDataPtr++      = temp;                                             // Byte1 = Byte0
            
        }
        
#endif
        
        *outputSize = buffer->getLength ( );
        DEBUG_LOG("%s[%p]::%s cpy %p %p\n", getClassName(), this,  __FUNCTION__, (void*)*outputSize, (void*)buffer->getLength());
    }
    
    
ReleaseBufferPrepared:
    
    buffer->complete ( );
    
    
ReleaseBuffer:
    
    
    buffer->release ( );
    buffer = NULL;
    
    
ReleaseCommand:
    
    
    DeallocateCommand ( command );
    command = NULL;
    
    
ReleaseProvider:
    
    
    fProvider->release ( );
    
    
ErrorExit:
    
    
    fOutstandingCommands--;
    
    DEBUG_LOG("%s[%p]::%s result %x\n", getClassName(), this,  __FUNCTION__, status);
    return status;
    
}
IOReturn
SATSMARTUserClient::WriteLogAtAddress ( ATASMARTWriteLogStruct *        writeLogData,
                                       UInt32 inStructSize )
{
    
    IOReturn status                  = kIOReturnSuccess;
    IOSATCommand *                  command                 = NULL;
    IOMemoryDescriptor *    buffer                  = NULL;
    DEBUG_LOG("%s[%p]::%s\n", getClassName(), this, __FUNCTION__);
    
    if ( inStructSize != sizeof ( ATASMARTWriteLogStruct ) || writeLogData->numSectors > 16 || writeLogData->data_length > kSATMaxDataSize) {
        return kIOReturnBadArgument;
    }
    
    fOutstandingCommands++;
    
    if ( isInactive ( ) )
    {
        
        status = kIOReturnNoDevice;
        goto ErrorExit;
        
    }
    
    fProvider->retain ( );
    
    command = AllocateCommand ( );
    if ( command == NULL )
    {
        status = kIOReturnNoResources;
        goto ReleaseProvider;
    }
    
    //buffer = IOMemoryDescriptor::withAddress(writeLogData->buffer, writeLogData->bufferSize, kIODirectionOut);
    buffer = IOMemoryDescriptor::withAddressRange(writeLogData->data_pointer, writeLogData->data_length, kIODirectionOut, fTask);
    
    if ( buffer == NULL )
    {
        status = kIOReturnVMError;
        goto ReleaseCommand;
    }
    
    status = buffer->prepare ( );
    if ( status != kIOReturnSuccess )
    {
        goto ReleaseBuffer;
    }
    
    command->setBuffer                      ( buffer );
    command->setByteCount           ( writeLogData->data_length );
    command->setFeatures            ( kFeaturesRegisterWriteLogAtAddress );
    command->setOpcode                      ( kATAFnExecIO );
    command->setTimeoutMS           ( kATAThirtySecondTimeoutInMS );
    command->setSectorCount         ( writeLogData->numSectors );
    command->setSectorNumber        ( writeLogData->logAddress );
    command->setCylLo                       ( kSMARTMagicCylinderLoValue );
    command->setCylHi                       ( kSMARTMagicCylinderHiValue );
    command->setCommand                     ( kATAcmdSMART );
    command->setFlags                       ( mATAFlagIOWrite );
    
    status = SendSMARTCommand ( command );
    if ( status == kIOReturnIOError )
    {
        
        if ( command->getEndErrorReg ( ) & 0x04 )
        {
            
            ERROR_LOG ( "WriteLogAtAddress %d unsupported\n", writeLogData->logAddress );
            status = kIOReturnUnsupported;
            
        }
        
        if ( command->getEndErrorReg ( ) & 0x10 )
        {
            
            ERROR_LOG ( "WriteLogAtAddress %d unwriteable\n", writeLogData->logAddress );
            status = kIOReturnNotWritable;
            
        }
        
    }
    
    buffer->complete ( );
    
    
ReleaseBuffer:
    
    
    buffer->release ( );
    buffer = NULL;
    
    
ReleaseCommand:
    
    
    DeallocateCommand ( command );
    command = NULL;
    
    
ReleaseProvider:
    
    
    fProvider->release ( );
    
    
ErrorExit:
    
    
    fOutstandingCommands--;
    
    DEBUG_LOG("%s[%p]::%s result %d\n", getClassName(), this,  __FUNCTION__, status);
    return status;
    
}
IOReturn
SATSMARTUserClient::ReadLogAtAddress ( ATASMARTReadLogStruct * structIn,
                                      void * structOut,
                                      IOByteCount inStructSize,
                                      IOByteCount *outStructSize)
{
    
    IOReturn status                  = kIOReturnSuccess;
    IOSATCommand *                  command                 = NULL;
    IOMemoryDescriptor *    buffer                  = NULL;
    DEBUG_LOG("%s[%p]::%s %p(%ld) %p(%ld)\n", getClassName(), this, __FUNCTION__, structIn, (long)inStructSize, structOut, (long)(outStructSize));
    
    if ( inStructSize != sizeof ( ATASMARTReadLogStruct )  || !outStructSize || *outStructSize < 1) {
        return kIOReturnBadArgument;
    }
    
    fOutstandingCommands++;
    
    if ( isInactive ( ) )
    {
        
        status = kIOReturnNoDevice;
        goto ErrorExit;
        
    }
    
    fProvider->retain ( );
    
    command = AllocateCommand ( );
    if ( command == NULL )
    {
        
        status = kIOReturnNoResources;
        goto ReleaseProvider;
        
    }
    
    buffer = IOMemoryDescriptor::withAddress (structOut,  *outStructSize, kIODirectionIn);
    if ( buffer == NULL )
    {
        
        status = kIOReturnNoResources;
        goto ReleaseCommand;
        
    }
    
    status = buffer->prepare ( );
    DEBUG_LOG("%s[%p]::%s status %x\n", getClassName(), this, __FUNCTION__, status);
    if ( status != kIOReturnSuccess )
    {
        
        goto ReleaseBuffer;
        
    }
    
    command->setBuffer                      ( buffer );
    command->setByteCount           ( buffer->getLength());
    command->setFeatures            ( kFeaturesRegisterReadLogAtAddress );
    command->setOpcode                      ( kATAFnExecIO );
    command->setTimeoutMS           ( kATAThirtySecondTimeoutInMS );
    command->setSectorCount         ( structIn->numSectors );
    command->setSectorNumber        ( structIn->logAddress );
    command->setCylLo                       ( kSMARTMagicCylinderLoValue );
    command->setCylHi                       ( kSMARTMagicCylinderHiValue );
    command->setCommand                     ( kATAcmdSMART );
    command->setFlags                       ( mATAFlagIORead );
    
    status = SendSMARTCommand ( command );
    if ( status == kIOReturnIOError )
    {
        
        if ( command->getEndErrorReg ( ) & 0x04 )
        {
            
            ERROR_LOG ( "ReadLogAtAddress %d unsupported\n", structIn->logAddress );
            status = kIOReturnUnsupported;
            
        }
        
        if ( command->getEndErrorReg ( ) & 0x10 )
        {
            
            ERROR_LOG ( "ReadLogAtAddress %d unreadable\n", structIn->logAddress );
            status = kIOReturnNotReadable;
            
        }
        
    }
    
    *outStructSize = buffer->getLength();
    
    buffer->complete ( );
    
    
    
ReleaseBuffer:
    
    
    buffer->release ( );
    buffer = NULL;
    
    
ReleaseCommand:
    
    
    DeallocateCommand ( command );
    command = NULL;
    
    
ReleaseProvider:
    
    
    fProvider->release ( );
    
    
ErrorExit:
    
    
    fOutstandingCommands--;
    
    DEBUG_LOG("%s[%p]::%s result %d\n", getClassName(), this,  __FUNCTION__, status);
    return status;
    
}