// Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// 

/**
@file
@internalComponent
*/

#include "fdchctloriginal.h"

#include <d32usbdescriptors.h>
#include <usbhost/internal/fdcpluginobserver.h>
#include <usbhosterrors.h>
#include <bluetooth/logger.h>

#include "fdchctloriginalserver.h"

#include <bluetooth/logger.h>

#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, "fdchctloriginal");
#endif


CFdcHctlOriginal* CFdcHctlOriginal::NewL(MFdcPluginObserver& aObserver)
	{
	LOG_STATIC_FUNC
	CFdcHctlOriginal* self = new(ELeave) CFdcHctlOriginal(aObserver);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();
	return self;
	}

CFdcHctlOriginal::CFdcHctlOriginal(MFdcPluginObserver& aObserver)
	: CFdcPlugin(aObserver)
	{
	LOG_FUNC
	}

CFdcHctlOriginal::~CFdcHctlOriginal()
	{
	LOG_FUNC
	iHctlSession.Close();
	delete iServer;
	}

void CFdcHctlOriginal::ConstructL()
	{
	LOG_FUNC
	iServer = CFdcHctlOriginalServer::NewL(*this);
	}

TAny* CFdcHctlOriginal::GetInterface(TUid aUid)
	{
	LOG_FUNC
	LOG1(_L8("\taUid = 0x%08x"), aUid);
	
	TAny* ret = NULL;
	if(aUid == TUid::Uid(KFdcInterfaceV1))
		{
		ret = reinterpret_cast<TAny*>(static_cast<MFdcInterfaceV1*>(this));
		}
	
	LOG1(_L8("\tret = [0x%08x]"), ret);
	return ret;
	}

TInt CFdcHctlOriginal::Mfi1NewFunction(TUint aDeviceId,
		const TArray<TUint>& aInterfaces,
		const TUsbDeviceDescriptor& aDeviceDescriptor,
		const TUsbConfigurationDescriptor& aConfigurationDescriptor)
	{
	LOG_FUNC
	
	// Normally an FDC is required to claim it's interfaces first.  In this
	// case we need to parse the descriptor tree to determine if the device
	// firmware update interface is present. (We don't do firmware updates but
	// we claim it until to provide successful device attachments. If a
	// firmware update FDC is provided this should be removed.)
#ifdef SYMBIAN_FDC_HCTL_ORIGINAL_ACCEPT_FIRMWARE_UPDATE
	TBool firmwareIntFound = EFalse;
	TUint8 firmwareIntNum = 0xff;
	
	TUint8 KDfuInterfaceClass = 0xfe;
	TUint8 KDfuInterfaceSubClass = 0x01;
	
	// Drop down a level from the configuration descriptor.
	TUsbGenericDescriptor* descriptor = aConfigurationDescriptor.iFirstChild;
	// Search across the interface tier (note doesn't handle DFU in IAD).
	while(descriptor)
		{
		TUsbInterfaceDescriptor* interface;
		if (interface = TUsbInterfaceDescriptor::Cast(descriptor), interface)
			{
			if(	interface->InterfaceClass() == KDfuInterfaceClass &&
				interface->InterfaceSubClass() == KDfuInterfaceSubClass)
				{
				firmwareIntNum = interface->InterfaceNumber();
				firmwareIntFound = ETrue;
				break;
				}
			}
		descriptor = descriptor->iNextPeer;
		}
#endif // SYMBIAN_FDC_HCTL_ORIGINAL_ACCEPT_FIRMWARE_UPDATE
	
	// We claim the interfaces we are to represent, we must claim the
	// first interface given to us as FDF has already determined that
	// we are to use it.
	const TUint KNumOfHctlInterfaces = 2;
	const TUint8 KAclInterfaceNum = 0x00, KScoInterfaceNum = 0x01;
	TBool gotAcl = EFalse, gotSco = EFalse, fatalError = EFalse;
	
	for(TInt i=0; i<aInterfaces.Count(); ++i)
		{
		TUint intNum = aInterfaces[i];
		if(intNum == KAclInterfaceNum)
			{
			fatalError = (fatalError || gotAcl); // The USB device should only report one ACL interface
			iAclToken = Observer().TokenForInterface(intNum);
			gotAcl = ETrue;
			}
		else if(intNum == KScoInterfaceNum)
			{
			fatalError = (fatalError || gotSco); // The USB device should only report one ACL interface
			iScoToken = Observer().TokenForInterface(intNum);
			gotSco = ETrue;
			}
		else if(i == 0)
			{
			// We always need to claim the first interface, this should have
			// been claimed already, but if we have a funny device then this
			// might not be the case.  We will need to error.
			TUint32 unknownToken = Observer().TokenForInterface(intNum);
			return KErrCorrupt;
			}
#ifdef SYMBIAN_FDC_HCTL_ORIGINAL_ACCEPT_FIRMWARE_UPDATE
		else if(firmwareIntFound && intNum == firmwareIntNum)
			{
			TUint32 dfuToken = Observer().TokenForInterface(intNum);
			}
#endif // SYMBIAN_FDC_HCTL_ORIGINAL_ACCEPT_FIRMWARE_UPDATE
		}
	// At this point we will have claimed to the interface mandated by FDF and
	// so we are at liberty to return an error.
	
	// firstly, check to see if a fatal error occured.
	if(fatalError)
		{
		LOG(_L8("\tFatal error when retrieving interfaces for driver instance..."));
		return KErrGeneral;
		}
	
	// Now we perform some validation that the function is what we expect.
	// There should be two interfaces as part of the function. One is
	// the standard data and control planes.  The other for the sync-
	// chronous connections.
	if(aConfigurationDescriptor.NumInterfaces() < KNumOfHctlInterfaces)
		{
		LOG(_L8("\tInsufficent interfaces in USB config. descriptor..."));
		return KErrUsbBadDescriptor;
		}
	if(aInterfaces.Count() < KNumOfHctlInterfaces)
		{
		LOG(_L8("\tInsufficient interfaces provided to FDC..."));
		return KErrUnderflow;
		}
	
	// Ensure that we got both interfaces, otherwise the device is malformed.
	if(!gotAcl || !gotSco)
		{
		LOG2(_L8("\tMissing Token [ACL=%d] [SCO=%d]"), gotAcl, gotSco);
		return KErrNotFound;
		}
	
	// At this point we are set-up to use the device.
	iReady = ETrue;
	
	// We try our best; the Bluetooth stack may not be running, it may not be even using
	// the USB HCTL.  So we accept the tokens and try the best to set-up the HCTL.
	// If we fail now the HCTL should later inform us to try again.
	RequestConnection();
	
	return KErrNone;
	}

void CFdcHctlOriginal::Mfi1DeviceDetached(TUint aDeviceId)
	{
	LOG_FUNC
	
	iReady = EFalse;
	
	if(iHctlSession.Handle())
		{
		// Inform of disconnection.
		iHctlSession.DeviceDetached();
		}
	// Close Hctl Handle
	iHctlSession.Close();
	}

void CFdcHctlOriginal::RequestConnection()
	{
	LOG_FUNC
	// Trigger the attempt to connect to the USB HCTL Server.
	
	if(!iReady)
		{
		// For whatever reason, we have not got the tokens for the USB interfaces.
		LOG(_L8("\tFDC is not ready"));
		return;
		}
	
	// Note that this will error if, the bt thread or usb hctl is not running;
	// or if there is another FDC already connected to it.
	TInt err = iHctlSession.Connect();
	if(err != KErrNone)
		{
		LOG1(_L8("\tRHCTLUsbOriginal::Connect error = %d"), err);
		return;
		}
	
	// Now inform the stack that we have a connected device.
	err = iHctlSession.DeviceAttached(iAclToken, iScoToken);
	if(err != KErrNone)
		{
		LOG1(_L8("\tRHCTLUsbOriginal::DeviceAttached error = %d"), err);
		iHctlSession.Close();
		return;
		}
	}
	



