/*
 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	Includes
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

#include "IOSCSITargetDeviceHashTable.h"
#include <IOKit/storage/IOStorageProtocolCharacteristics.h>


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	Macros
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

#define DEBUG 												0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING					"SCSITargetHashTable"

#if DEBUG
#define SCSI_TARGET_HASH_TABLE_DEBUGGING_LEVEL				0
#endif


#include "IOSCSIArchitectureModelFamilyDebugging.h"


#if ( SCSI_TARGET_HASH_TABLE_DEBUGGING_LEVEL >= 1 )
#define PANIC_NOW(x)		IOPanic x
#else
#define PANIC_NOW(x)
#endif

#if ( SCSI_TARGET_HASH_TABLE_DEBUGGING_LEVEL >= 2 )
#define ERROR_LOG(x)		IOLog x
#else
#define ERROR_LOG(x)
#endif

#if ( SCSI_TARGET_HASH_TABLE_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x)		IOLog x; IOSleep(1)
#else
#define STATUS_LOG(x)
#endif


#define super __OSHashTable


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	Globals
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

static IOSCSITargetDeviceHashTable gSCSITargetDeviceHashTable;


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	GetSharedInstance - Gets pointer to global hash table	   [PUBLIC][STATIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOSCSITargetDeviceHashTable *
IOSCSITargetDeviceHashTable::GetSharedInstance ( void )
{
	return &gSCSITargetDeviceHashTable;
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	IsProviderPathToExistingTarget -
//	
//	Checks if a node is undiscovered yet. If so, adds it to the global hash
//	table, else it adds another path to the target. Returns true if node is a
//	new PATH, false if it is a new target device.					   [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

bool
IOSCSITargetDeviceHashTable::IsProviderPathToExistingTarget (
									IOSCSITargetDevice *		newTarget,
									IOSCSIProtocolServices *	provider,
									UInt32						hashValue )
{
	
	bool						isNewPath		= false;
	__OSHashEntry *				newEntry		= NULL;
	__OSHashEntryBucket *		bucket			= NULL;
	__OSHashEntry *				existingEntry	= NULL;
	IOSCSITargetDevice *		existingTarget	= NULL;
	
	STATUS_LOG ( ( "+IOSCSITargetDeviceHashTable::IsProviderPathToExistingTarget\n" ) );
	
	// Allocate the OSHashEntry here. You don't want to allocate while holding
	// the table lock, as allocations may block.
	newEntry = IONew ( __OSHashEntry, 1 );
	require_nonzero ( newEntry, ErrorExit );
	
	// Initialize the OSHashEntry structure.
	newEntry->hashValue 	= hashValue;
	newEntry->next			= NULL;
	newEntry->prev			= NULL;
	newEntry->object		= newTarget;
	hashValue 				= hashValue % fSize;
	
	STATUS_LOG ( ( "hashValue = 0x%08x\n", ( unsigned int ) hashValue ) );
	
	// Get the table lock.
	Lock ( );
	
	// Find the hash bucket.
	bucket			= &fTable[hashValue];
	existingEntry	= bucket->firstEntry;
	
	// Walk the list of hash entries in this bucket (if any). Check if any entries
	// match our entry. If so, add a path to the existing entry and bail. Else,
	// we insert the new entry into the table and associate the hash entry with the
	// target device.
	if ( existingEntry != NULL )
	{
		
		// Get the object for this entry.
		existingTarget = ( IOSCSITargetDevice * ) existingEntry->object;
		
		// Check the target object for existence.
		while ( existingTarget != NULL )
		{
			
			STATUS_LOG ( ( "Looking for target device\n" ) );
			
			OSObject *	id1 = NULL;
			OSObject *	id2 = NULL;
			
			id1 = existingTarget->GetNodeUniqueIdentifier ( );
			id2 = newTarget->GetNodeUniqueIdentifier ( );
			
			// See if the node unique identifiers are the same. We make the assumption that any
			// path to the same target device should get the same INQUIRY page 83h and page 80h
			// information from that target. So, the node unique identifiers would be equal for
			// any two paths to the same node.
			if ( ( id1 != NULL ) && ( id2 != NULL ) && ( id1->isEqualTo ( id2 ) ) )
			{
				
				// They match. Add the path.
				STATUS_LOG ( ( "Adding path to target\n" ) );
				existingTarget->AddPath ( provider );
				isNewPath = true;
				break;
				
			}
			
			// They didn't match. See if we should keep traversing this bucket.
			if ( existingEntry->next == NULL )
				break;
			
			// Advance to next entry.
			existingEntry 	= existingEntry->next;
			existingTarget	= ( IOSCSITargetDevice * ) existingEntry->object;
			
		}
		
	}
	
	// Is this a new target device?
	if ( isNewPath == false )
	{
		
		// Yes, this is a new target device. Insert it into the table.
		STATUS_LOG ( ( "Adding new target to hash table\n" ) );
		newTarget->SetHashEntry ( newEntry );
		InsertHashEntry ( newEntry );
		
	}
	
	Unlock ( );
	
	// Did we add a path to an existing target?
	if ( isNewPath == true )
	{
		
		// Yes. Free the memory for the hash entry, since we'll use the
		// one in existence already.
		IODelete ( newEntry, __OSHashEntry, 1 );
		
	}
	
	
ErrorExit:
	
	
	STATUS_LOG ( ( "-IOSCSITargetDeviceHashTable::IsProviderPathToExistingTarget: isNewPath = %s\n", isNewPath ? "true" : "false" ) );
	
	return isNewPath;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	DestroyHashReference -  Removes a hash entry from the table when a target
//					  		device is freed.						   [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOSCSITargetDeviceHashTable::DestroyHashReference ( void * oldEntry )
{
	
	STATUS_LOG ( ( "+IOSCSITargetDeviceHashTable::DestroyHashReference\n" ) );
	
	super::RemoveHashEntry ( ( __OSHashEntry * ) oldEntry );
	IODelete ( oldEntry, __OSHashEntry, 1 );
	
	STATUS_LOG ( ( "-IOSCSITargetDeviceHashTable::DestroyHashReference\n" ) );
	
}