/*
 * 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@
 */
/*
 *  IOFireWireLibUnitDirectory.cpp
 *  IOFireWireLib
 *
 *  Created by NWG on Thu Apr 27 2000.
 *  Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 */

#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/iokitmig.h>

#include "IOFireWireLib.h"
#include "IOFireWireLibUnitDirectory.h"

//#define __IOFireWireClientDebug__ 1

// ============================================================
//
// static interface table
//
// ============================================================

IOFireWireLocalUnitDirectoryInterface IOFireWireLocalUnitDirectoryImp::sInterface =
{
	INTERFACEIMP_INTERFACE,
	1, 0, // version/revision
	& IOFireWireLocalUnitDirectoryImp::SAddEntry_Ptr,
	& IOFireWireLocalUnitDirectoryImp::SAddEntry_UInt32,
	& IOFireWireLocalUnitDirectoryImp::SAddEntry_FWAddress,
	& IOFireWireLocalUnitDirectoryImp::SPublish,
	& IOFireWireLocalUnitDirectoryImp::SUnpublish
//	0,	// set device added callback
//	0,	// set device removed callback
//	0,	// command is complete?
//	0,	// turn on notification
//	0,	// turn off notification
} ;

IUnknownVTbl** 
IOFireWireLocalUnitDirectoryImp::Alloc(
	IOFireWireDeviceInterfaceImp& 	inUserClient)
{
    IOFireWireLocalUnitDirectoryImp*	me = new IOFireWireLocalUnitDirectoryImp(inUserClient) ;
	IUnknownVTbl** 	interface = NULL;

    if( me )
	{
//		me->AddRef();
        interface = (IUnknownVTbl**) &me->mInterface ;
    }
	
	return interface;
}

HRESULT STDMETHODCALLTYPE
IOFireWireLocalUnitDirectoryImp::QueryInterface(REFIID iid, LPVOID* ppv)
{
	HRESULT		result = S_OK ;
	*ppv = nil ;

	CFUUIDRef	interfaceID	= CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, iid) ;

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

// ============================================================
//
// static interface methods
//
// ============================================================

IOReturn 
IOFireWireLocalUnitDirectoryImp::SAddEntry_Ptr(
	IOFireWireLibLocalUnitDirectoryRef self,
	int				key, 
	void* 			inBuffer, 
	size_t 			inLen, 
	CFStringRef 	inDesc)
{ 
	return GetThis(self)->AddEntry(key, inBuffer, inLen, inDesc); 
}

IOReturn
IOFireWireLocalUnitDirectoryImp::SAddEntry_UInt32(
	IOFireWireLibLocalUnitDirectoryRef self,
	int 			key,
	UInt32 			value,
	CFStringRef 	inDesc)
{ 
	return GetThis(self)->AddEntry(key, value, inDesc); 
}

IOReturn
IOFireWireLocalUnitDirectoryImp::SAddEntry_FWAddress(
	IOFireWireLibLocalUnitDirectoryRef self,
	int 				key, 
	const FWAddress*	inAddr, 
	CFStringRef 		inDesc)
{ 
	return GetThis(self)->AddEntry(key, *inAddr, inDesc); 
}

IOReturn
IOFireWireLocalUnitDirectoryImp::SPublish(
	IOFireWireLibLocalUnitDirectoryRef self)
{ 
	return GetThis(self)->Publish();
}

IOReturn
IOFireWireLocalUnitDirectoryImp::SUnpublish(
	IOFireWireLibLocalUnitDirectoryRef self)
{ return GetThis(self)->Unpublish(); }


// ============================================================
// IOFireWireLocalUnitDirectoryImp implementation
// ============================================================

#pragma mark -
#pragma mark [constructors/destructors]
IOFireWireLocalUnitDirectoryImp::IOFireWireLocalUnitDirectoryImp(
	IOFireWireDeviceInterfaceImp& inUserClient): IOFireWireIUnknown(),
												 mUserClient(inUserClient), 
												 mPublished(false)
{
	#if __IOFireWireClientDebug__
	fprintf(stderr, "mUserClient.IsInited=%08lX, mKernUnitDirRef=%08lX\n", mUserClient.IsInited(), mKernUnitDirRef) ;
	#endif
	
	// ask IOFireWireFamily to create a unit directory in the kernel
	if (mUserClient.IsInited())
	{
		io_connect_t	connection ;

//		if (!mKernUnitDirRef)
			if ( nil != (connection = mUserClient.GetUserClientConnection()) )
			{
				mach_msg_type_number_t	size = 1 ;
				
				io_connect_method_scalarI_scalarO(
										connection,
										kFWUnitDirCreate,
										nil,
										0,
										(int*) & mKernUnitDirRef,
										& size) ;
				#if __IOFireWireClientDebug__
				fprintf(stderr,
						"IOFireWireLocalUnitDirectoryImp::IOFireWireLocalUnitDirectoryImp(): mKernUnitDir created @ %08lX\n",
						(UInt32) mKernUnitDirRef) ;
				#endif
			}
	}
	
	// COM support
	mInterface.pseudoVTable = (IUnknownVTbl*) & sInterface ;
	mInterface.obj = this ;
	
}

IOFireWireLocalUnitDirectoryImp::~IOFireWireLocalUnitDirectoryImp()
{
	if (mPublished)
		Unpublish() ;
}

#pragma mark -
#pragma mark [
IOReturn
IOFireWireLocalUnitDirectoryImp::AddEntry(
	int 								key,
	void*								inBuffer,
	size_t								inLen,
	CFStringRef	/*inDesc = NULL*/)	// zzz don't know what to do with this yet...
									// zzz should probably make it go into the kernel
{
	#if __IOFireWireClientDebug__
	fprintf(
			stderr, 
			"IOFireWireLib: + IOFireWireLocalUnitDirectoryImp::AddEntry() (inLen = %08lX, inKey = %08lX)\n",
			(UInt32)inLen, 
			(UInt32)key) ;
	#endif

	IOReturn kr = kIOReturnNoDevice ;

	if (inLen > 0xC00)
	{
		#if (__IOFireWireClientDebug__)
			fprintf(stderr, "IOFireWireLib: IOFireWireLocalUnitDirectoryImp::AddEntry(): input too large\n") ;
		#endif
			return kr = kIOReturnBadArgument ;
	}
	
	io_connect_t	connection ;

	if ( nil != (connection = mUserClient.GetUserClientConnection()) )
	{				
		io_scalar_inband_t 	params ;

		params[0] = (UInt32) mKernUnitDirRef ;
		params[1] = key ;
		
		kr = io_connect_method_scalarI_structureI(
								connection,
								kFWUnitDirAddEntry_Buffer,
								params,
								2,
								(io_struct_inband_t) inBuffer,
								inLen ) ;
								
		#if __IOFireWireClientDebug__
		fprintf(
				stderr, 
				"AddEntry() got %08lX\n", 
				(UInt32) kr) ;
		#endif
	}

	#if __IOFireWireClientDebug__
	fprintf(
			stderr, 
			"IOFireWireLocalUnitDirectoryImp::AddEntry(): result = %08lX\n", 
			(UInt32) kr) ;
	#endif

	return kr ;
}

IOReturn
IOFireWireLocalUnitDirectoryImp::AddEntry(
	int			key,
	UInt32		value,
	CFStringRef	/*inDesc = NULL*/)	// zzz don't know what to do with this yet...
									// zzz should probably go into kernel
{
	#if __IOFireWireClientDebug__
	fprintf(
			stderr,
			"IOFireWireLib: + IOFireWireLocalUnitDirectoryImp::AddEntry[UInt32]() (inKey = %08lX)\n", 
			(UInt32)key) ;
	#endif

	IOReturn kr = kIOReturnNoDevice ;
	
	io_connect_t	connection ;

	if ( nil != (connection = mUserClient.GetUserClientConnection()) )
	{				

		mach_msg_type_number_t	size = 0 ;
		io_scalar_inband_t 		params ;

		params[0] = (UInt32) mKernUnitDirRef ;
		params[1] = key ;
		params[2] = value ;
		
		fprintf(stderr, "add entry mKernUnitDirRef=%08lX\n", mKernUnitDirRef) ;
		kr = io_connect_method_scalarI_scalarO(
								connection,
								kFWUnitDirAddEntry_UInt32,
								params,
								3,
								params,
								& size ) ;
		#if __IOFireWireClientDebug__
		fprintf(
				stderr, 
				"AddEntry() got %08lX\n", 
				(UInt32) kr) ;
		#endif
	}

	#if __IOFireWireClientDebug__
	fprintf(
			stderr, 
			"IOFireWireLocalUnitDirectoryImp::AddEntry[UInt32](): result = %08lX\n", 
			(UInt32) kr) ;
	#endif

	return kr ;
}

IOReturn
IOFireWireLocalUnitDirectoryImp::AddEntry(
	int					key,
	const FWAddress &	value,
	CFStringRef	/*inDesc = NULL*/)	// zzz don't know what to do with this yet...
									// zzz should probably go into kernel
{
	#if __IOFireWireClientDebug__
	fprintf(
			stderr, 
			"IOFireWireLib: + IOFireWireLocalUnitDirectoryImp::AddEntry() (inKey = %08lX)\n",
			(UInt32)key) ;
	#endif

	IOReturn kr = kIOReturnNoDevice ;

	io_connect_t	connection ;

	if ( nil != (connection = mUserClient.GetUserClientConnection()) )
	{				
		io_scalar_inband_t 	params ;

		params[0] = (UInt32) mKernUnitDirRef ;
		params[1] = key ;
		
		kr = io_connect_method_scalarI_structureI(
								connection,
								kFWUnitDirAddEntry_FWAddr,
								params,
								2,
								(char*) & value,
								sizeof(value) ) ;
	}

	return kr ;
}

IOReturn
IOFireWireLocalUnitDirectoryImp::Publish()
{
	#if __IOFireWireClientDebug__
		fprintf(stderr, "IOFireWireLocalUnitDirectoryImp::Publish()\n") ;
	#endif
	
	io_connect_t	connection ;
	IOReturn		kr			= kIOReturnSuccess ;
	
	if (!mPublished)
	{
		if (nil == (connection = mUserClient.GetUserClientConnection()) )
			kr = kIOReturnNoDevice ;

		if ( kIOReturnSuccess == kr )
		{
			io_scalar_inband_t 		params ;
			io_scalar_inband_t		output ;
			mach_msg_type_number_t	size = 0 ;

			params[0] = (UInt32) mKernUnitDirRef ;
			

			kr = io_connect_method_scalarI_scalarO(
									connection,
									kFWUnitDirPublish,
									params,
									1,
									& output[0],
									& size ) ;
		}
		
		mPublished = (kr == kIOReturnSuccess) ;
	}
	return kr ;
}

IOReturn
IOFireWireLocalUnitDirectoryImp::Unpublish()
{
	io_connect_t	connection ;
	IOReturn		kr = kIOReturnSuccess ;
	
	
	if (mPublished)
	{
		if (nil == (connection = mUserClient.GetUserClientConnection()) )
			kr = kIOReturnNoDevice ;

		if ( kIOReturnSuccess == kr )
		{
			io_scalar_inband_t 		params ;
			io_scalar_inband_t		output ;
			mach_msg_type_number_t	size = 0 ;

			params[0] = (UInt32) mKernUnitDirRef ;

			kr = io_connect_method_scalarI_scalarO(
									connection,
									kFWUnitDirUnpublish,
									params,
									1,
									& output[0],
									& size ) ;
		}
		
		if (kIOReturnSuccess == kr)
			mPublished = false ;
	}
	
	return kr ;
}
