/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This 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 OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 *  IOFireWireLibPhysicalAddressSpace.cpp
 *  IOFireWireLib
 *
 *  Created by NWG on Tue Dec 12 2000.
 *  Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 */


#include "IOFireWireLibPriv.h"
#include "IOFireWireLibPhysAddrSpace.h"

#define MIN(a,b) ((a < b) ? a : b)

// COM

IOFireWirePhysicalAddressSpaceInterface IOFireWirePhysicalAddressSpaceImp::sInterface =
{
	INTERFACEIMP_INTERFACE,
	1, 0,	//vers, rev
	& IOFireWirePhysicalAddressSpaceImp::SGetPhysicalSegments,
	& IOFireWirePhysicalAddressSpaceImp::SGetPhysicalSegment,
	& IOFireWirePhysicalAddressSpaceImp::SGetPhysicalAddress,
				
	& IOFireWirePhysicalAddressSpaceImp::SGetFWAddress,
	& IOFireWirePhysicalAddressSpaceImp::SGetBuffer,
	& IOFireWirePhysicalAddressSpaceImp::SGetBufferSize
} ;

HRESULT
IOFireWirePhysicalAddressSpaceImp::QueryInterface(REFIID iid, void **ppv)
{
	HRESULT		result = S_OK ;
	*ppv = nil ;

	CFUUIDRef	interfaceID	= CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, iid) ;

	if ( CFEqual(interfaceID, IUnknownUUID) ||  CFEqual(interfaceID, kIOFireWirePhysicalAddressSpaceInterfaceID) )
	{
		*ppv = & mInterface ;
		AddRef() ;
	}
	else
	{
		*ppv = nil ;
		result = E_NOINTERFACE ;
	}	
	
	CFRelease(interfaceID) ;
	return result ;
}

IUnknownVTbl**
IOFireWirePhysicalAddressSpaceImp::Alloc(
	IOFireWireDeviceInterfaceImp& 	inUserClient,
	FWKernPhysicalAddrSpaceRef		inAddrSpaceRef,
	UInt32		 					inSize, 
	void* 							inBackingStore, 
	UInt32 							inFlags)
{
	IUnknownVTbl**	interface = nil ;
	IOFireWirePhysicalAddressSpaceImp*	me = new IOFireWirePhysicalAddressSpaceImp(inUserClient, inAddrSpaceRef, inSize, inBackingStore, inFlags) ;

	IOReturn result = me->Init() ;
//	printf("IOFireWirePhysicalAddressSpaceImp::Alloc: init returned %08lX\n", result) ;

	if (kIOReturnSuccess != result)
	{
		delete me ;
		me = nil ;
		interface = nil ;
	}
	
	if (me)
	{
//		me->AddRef();
        interface = (IUnknownVTbl**) &me->mInterface.pseudoVTable;
	}
	
	return interface ;
}

#pragma mark -
#pragma mark --static methods
//
//	static methods
//

void
IOFireWirePhysicalAddressSpaceImp::SGetPhysicalSegments(
	IOFireWireLibPhysicalAddressSpaceRef self,
	UInt32*				ioSegmentCount,
	IOByteCount			outSegments[],
	IOPhysicalAddress	outAddresses[])
{
	GetThis(self)->GetPhysicalSegments(ioSegmentCount, outSegments, outAddresses) ;
}

IOPhysicalAddress
IOFireWirePhysicalAddressSpaceImp::SGetPhysicalSegment(
	IOFireWireLibPhysicalAddressSpaceRef self,
	IOByteCount 		offset,
	IOByteCount*		length)
{
	return GetThis(self)->GetPhysicalSegment(offset, length) ;
}

IOPhysicalAddress
IOFireWirePhysicalAddressSpaceImp::SGetPhysicalAddress(
	IOFireWireLibPhysicalAddressSpaceRef self)
{
	return GetThis(self)->GetPhysicalAddress() ;
}

void
IOFireWirePhysicalAddressSpaceImp::SGetFWAddress(IOFireWireLibPhysicalAddressSpaceRef self, FWAddress* outAddr )
{
	bcopy(& GetThis(self)->GetFWAddress(), outAddr, sizeof(*outAddr)) ;
}

void*
IOFireWirePhysicalAddressSpaceImp::SGetBuffer(IOFireWireLibPhysicalAddressSpaceRef self)
{
	return GetThis(self)->GetBuffer() ;
}

const UInt32
IOFireWirePhysicalAddressSpaceImp::SGetBufferSize(IOFireWireLibPhysicalAddressSpaceRef self)
{
	return GetThis(self)->GetBufferSize() ;
}

#pragma mark -
#pragma mark --ctor/dtor

//
// === ctor/dtor ==============================
//

IOFireWirePhysicalAddressSpaceImp::IOFireWirePhysicalAddressSpaceImp(
	IOFireWireDeviceInterfaceImp &	inUserClient,
	FWKernPhysicalAddrSpaceRef		inKernPhysicalAddrSpaceRef,
	UInt32 							inSize, 
	void* 							inBackingStore, 
	UInt32 							inFlags): IOFireWireIUnknown(),
											  mUserClient(inUserClient),
											  mKernPhysicalAddrSpaceRef(inKernPhysicalAddrSpaceRef),
											  mSize(inSize),
											  mBackingStore(inBackingStore),
											  mSegments(0),
											  mSegmentLengths(0),
											  mSegmentCount(0)
{
	inUserClient.AddRef() ;

	// COM bits
	mInterface.pseudoVTable = (IUnknownVTbl*) & sInterface ;
	mInterface.obj = this ;
}

IOFireWirePhysicalAddressSpaceImp::~IOFireWirePhysicalAddressSpaceImp()
{
	// call user client to delete our addr space ref here (if not yet released)
	IOConnectMethodScalarIScalarO(
		mUserClient.GetUserClientConnection(),
		kFWPhysicalAddrSpace_Release,
		1,
		0,
		mKernPhysicalAddrSpaceRef) ;
	
	delete[] mSegments ;
	delete[] mSegmentLengths ;

	mUserClient.Release() ;
}

//
// === virtual members ========================
//

IOReturn
IOFireWirePhysicalAddressSpaceImp::Init()
{
	IOReturn	result = kIOReturnSuccess ;
	
	if (!mKernPhysicalAddrSpaceRef)
		result = kIOReturnError ;
	
//	fprintf(stderr, "mKernPhysicalAddrSpaceRef = %08lX\n", mKernPhysicalAddrSpaceRef) ;
	
	if (kIOReturnSuccess == result)
		result = IOConnectMethodScalarIScalarO(
				mUserClient.GetUserClientConnection(),
				kFWPhysicalAddrSpace_GetSegmentCount,
				1,
				1,
				mKernPhysicalAddrSpaceRef,
				& mSegmentCount) ;
	
	
//	fprintf(stderr, "kFWPhysicalAddrSpace_GetSegmentCount returned %08lX, count=%i\n", result, mSegmentCount) ;
	
	if (mSegmentCount > 0)
	{
		mSegments = new IOPhysicalAddress[mSegmentCount] ;
		mSegmentLengths = new IOByteCount[mSegmentCount] ;
	
		if (!mSegments || !mSegmentLengths)
		{
			delete mSegments ;
			delete mSegmentLengths ;
			result = kIOReturnNoMemory ;
		}
	}

	if (kIOReturnSuccess == result)
	{
		UInt32 newSegmentCount ;
		
		result = IOConnectMethodScalarIScalarO(
				mUserClient.GetUserClientConnection(),
				kFWPhysicalAddrSpace_GetSegments,
				4,
				1,
				mKernPhysicalAddrSpaceRef,
				mSegmentCount,
				mSegments,
				mSegmentLengths,
				& newSegmentCount) ;
		
		mSegmentCount = newSegmentCount ;

//	fprintf(stderr, "kFWPhysicalAddrSpace_GetSegments returned %08lX, count=%08lX\n", result, newSegmentCount) ;
//	for(UInt32 debugIndex=0; debugIndex<mSegmentCount; debugIndex++)
//		fprintf(stderr, "segment %08lX: %08lX, len %08lX\n", debugIndex, mSegments[debugIndex], mSegmentLengths[debugIndex]) ;

	}
	mFWAddress = FWAddress(0, mSegments[0], 0) ;
	
	return result ;
}

#pragma mark --

void
IOFireWirePhysicalAddressSpaceImp::GetPhysicalSegments(
	UInt32*				ioSegmentCount,
	IOByteCount			outSegmentLengths[],
	IOPhysicalAddress	outSegments[])
{
	*ioSegmentCount = MIN(*ioSegmentCount, mSegmentCount) ;
	
	bcopy(mSegmentLengths, outSegmentLengths, (*ioSegmentCount)*sizeof(UInt32)) ;
	bcopy(mSegments, outSegments, (*ioSegmentCount) * sizeof(IOPhysicalAddress)) ;
}

IOPhysicalAddress
IOFireWirePhysicalAddressSpaceImp::GetPhysicalSegment(
	IOByteCount 		offset,
	IOByteCount*		length)
{
	IOPhysicalAddress result = 0 ;

	if (mSegmentCount > 0)
	{		
		IOByteCount traversed = mSegmentLengths[0] ;
		UInt32		currentSegment = 0 ;
		
		while((traversed < offset) && (currentSegment < mSegmentCount))
		{
			traversed += mSegmentLengths[currentSegment] ;
			currentSegment++ ;
		}	
		
		if (currentSegment <= mSegmentCount)
		{
			*length = mSegmentLengths[currentSegment] ;
			result =  mSegments[currentSegment] ;
		}
	}
	
	return result ;
}

IOPhysicalAddress
IOFireWirePhysicalAddressSpaceImp::GetPhysicalAddress()
{
	return mSegments[0] ;
}

const FWAddress&
IOFireWirePhysicalAddressSpaceImp::GetFWAddress()
{
	return mFWAddress ;
}

void*
IOFireWirePhysicalAddressSpaceImp::GetBuffer()
{
	return mBackingStore ;
}

const UInt32
IOFireWirePhysicalAddressSpaceImp::GetBufferSize()
{
	return mSize ;
}
