IOReturn XboxOneControllerDriver::sendHello()
{
	IOReturn ior = kIOReturnSuccess;
	IOMemoryDescriptor* hello = nullptr;
	IOByteCount bytesWritten = 0;
	constexpr size_t helloSize = sizeof XboxOneControllerHelloMessage;
	
	if (_interruptPipe == nullptr) // paranoid check
	{
		IO_LOG_DEBUG("_interruptPipe is null");
		ior = kIOReturnInternalError;
		goto cleanup;
	}
	
	// Create the hello message that we're about to send to the controller.
	hello = IOBufferMemoryDescriptor::inTaskWithOptions(kernel_task, 0, helloSize);
	if (hello == nullptr)
	{
		IO_LOG_DEBUG("Could not allocate buffer for hello message.");
		ior = kIOReturnNoMemory;
		goto cleanup;
	}
	
	bytesWritten = hello->writeBytes(0, XboxOneControllerHelloMessage, helloSize);
	if (bytesWritten != helloSize) // paranoid check
	{
		ior = kIOReturnOverrun;
		goto cleanup;
	}
	
	// Now send the message
	ior = _interruptPipe->Write(hello, 0, 0, hello->getLength());
	if (ior != kIOReturnSuccess)
	{
		IO_LOG_DEBUG("Couldn't send hello message to controller: %08x\n", ior);
		goto cleanup;
	}
	
cleanup:
	if (hello != nullptr)
	{
		hello->release();
	}
	
	return ior;
}
bool AppleSamplePCI::start( IOService * provider )
{
    IOMemoryDescriptor *	mem;
    IOMemoryMap *		map;

    IOLog("AppleSamplePCI::start\n");

    if( !super::start( provider ))
        return( false );

    /*
     * Our provider class is specified in the driver property table
     * as IOPCIDevice, so the provider must be of that class.
     * The assert is just to make absolutely sure for debugging.
     */

    assert( OSDynamicCast( IOPCIDevice, provider ));
    fPCIDevice = (IOPCIDevice *) provider;

    /*
     * Enable memory response from the card
     */
    fPCIDevice->setMemoryEnable( true );


    /*
     * Log some info about the device
     */

    /* print all the device's memory ranges */
    for( UInt32 index = 0;
         index < fPCIDevice->getDeviceMemoryCount();
         index++ ) {

        mem = fPCIDevice->getDeviceMemoryWithIndex( index );
        assert( mem );
        IOLog("Range[%ld] %08lx:%08lx\n", index,
              mem->getPhysicalAddress(), mem->getLength());
    }

    /* look up a range based on its config space base address register */
    mem = fPCIDevice->getDeviceMemoryWithRegister(
                                  kIOPCIConfigBaseAddress0 );
    if( mem )
        IOLog("Range@0x%x %08lx:%08lx\n", kIOPCIConfigBaseAddress0,
                mem->getPhysicalAddress(), mem->getLength());

    /* map a range based on its config space base address register,
     * this is how the driver gets access to its memory mapped registers
     * the getVirtualAddress() method returns a kernel virtual address
     * for the register mapping */
    
    map = fPCIDevice->mapDeviceMemoryWithRegister(
                                  kIOPCIConfigBaseAddress0 );
    if( map ) {
        IOLog("Range@0x%x (%08lx) mapped to kernel virtual address %08x\n",
                kIOPCIConfigBaseAddress0,
                map->getPhysicalAddress(),
                map->getVirtualAddress());
        /* release the map object, and the mapping itself */
        map->release();
    }

    /* read a config space register */
    IOLog("Config register@0x%x = %08lx\n", kIOPCIConfigCommand,
          fPCIDevice->configRead32(kIOPCIConfigCommand) );

    // construct a memory descriptor for a buffer below the 4Gb line &
    // so addressable by 32 bit DMA. This could be used for a 
    // DMA program buffer for example

    IOBufferMemoryDescriptor * bmd = 
	IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
				// task to hold the memory
				kernel_task, 
				// options
				kIOMemoryPhysicallyContiguous, 
				// size
				64*1024, 
				// physicalMask - 32 bit addressable and page aligned
				0x00000000FFFFF000ULL);

    if (bmd) {
	generateDMAAddresses(bmd);
    } else {
	IOLog("IOBufferMemoryDescriptor::inTaskWithPhysicalMask failed\n");
    }
    fLowMemory = bmd;
    
    /* publish ourselves so clients can find us */
    registerService();

    return( true );
}
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::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::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;
    
}