kernel/eka/drivers/usbcc/chapter9.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
child 22 2f92ad2dc5db
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// e32/drivers/usbcc/chapter9.cpp
// Platform independent layer (PIL) of the USB Device controller driver:
// Processing of USB spec chapter 9 standard requests.
// 
//

/**
 @file chapter9.cpp
 @internalTechnology
*/

#include <drivers/usbc.h>


//#define ENABLE_EXCESSIVE_DEBUG_OUTPUT

//
// The way functions are called after an request has been completed by the PSL:
//
//                                         Ep0RequestComplete
//                                                 |
//                                        ------------------------------------------------
//                                       |                                                |
//                              ProcessEp0ReceiveDone                            ProcessEp0TransmitDone
//                                       |                                                |
//                   ---------------------------------------                              |
//                  |                                       |                             |
//        ProcessEp0SetupReceived                 ProcessEp0DataReceived        ProcessDataTransferDone
//                  |                                       |
//         ---------------------                      ---------------
//        |                     |                    |               |
//   ProcessXXX       ProcessDataTransferDone   ProceedXXX  ProcessDataTransferDone
//
//   XXX = Specific_Request
//

//
// === USB Controller member function implementation - PSL API (protected) ========================
//

/** Used to synchronize the Ep0 state machine between the PSL and PIL.
	Accepts a SETUP packet and returns the next Ep0 state.

	@param aSetupBuf The SETUP packet just received by the PSL.
	@return The next Ep0 state.

	@publishedPartner @released
*/
TUsbcEp0State DUsbClientController::EnquireEp0NextState(const TUint8* aSetupBuf) const
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::EnquireEp0NextState()"));

	// This function may be called by the PSL from within an ISR -- so we have
	// to take care what we do here (and also in all functions that get called
	// from here).

	if (SWAP_BYTES_16((reinterpret_cast<const TUint16*>(aSetupBuf)[3])) == 0) // iLength
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  --> EEp0StateStatusIn"));
		return EEp0StateStatusIn;							// No-data Control => Status_IN
		}
	else if ((aSetupBuf[0] & KUsbRequestType_DirMask) == KUsbRequestType_DirToDev)
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  --> EEp0StateDataOut"));
		return EEp0StateDataOut;							// Control Write => Data_OUT
		}
	else
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  --> EEp0StateDataIn"));
		return EEp0StateDataIn;								// Control Read => Data_IN
		}
	}


TInt DUsbClientController::ProcessEp0ReceiveDone(TInt aCount)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessEp0ReceiveDone()"));
	TInt r;
	if (iEp0DataReceiving == EFalse)
		{
		// It's obviously a Setup packet, so...
		r = ProcessEp0SetupReceived(aCount);
		}
	else
		{
		// If it isn't a Setup, it must be data...
		// (This is actually not quite true, as it could also be - in theory - a new Setup packet
		//  when the host has abandoned, for whatever reason, the previous one which was still
		//  in progress. However no such case is known to have occurred with this driver, or at
		//  least it didn't lead to problems.
		//  Some UDCs have a dedicated interrupt for Setup packets, but so far this driver hasn't
		//  made use of such a feature (as it would require a PSL/PIL API change).)
		r = ProcessEp0DataReceived(aCount);
		}
	return r;
	}


TInt DUsbClientController::ProcessEp0TransmitDone(TInt aCount, TInt aError)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessEp0TransmitDone()"));
	// In any case: there's now no longer a write pending
	iEp0WritePending = EFalse;
	// If it was a client who set up this transmission, we report to that client
	if (iEp0ClientDataTransmitting)
		{
		iEp0ClientDataTransmitting = EFalse;
		TUsbcRequestCallback* const p = iRequestCallbacks[KEp0_Tx];
		if (p)
			{
			__ASSERT_DEBUG((p->iTransferDir == EControllerWrite), Kern::Fault(KUsbPILPanicCat, __LINE__));
			p->iError = aError;
			p->iTxBytes = aCount;
			ProcessDataTransferDone(*p);
			return KErrNone;
			}
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: DUsbClientController::ProcessEpTransmitDone: Stalling Ep0"));
		StallEndpoint(KEp0_In);								// request not found
		return KErrNotFound;
		}
	// If _we_ sent the data, we simply do nothing here...
	return KErrNone;
	}


#define USB_PROCESS_REQUEST(request) \
	if (Process ## request(packet) != KErrNone) \
		{ \
		__KTRACE_OPT(KUSB, \
					 Kern::Printf("  ProcessEp0SetupReceived: Stalling Ep0")); \
		StallEndpoint(KEp0_In); \
		}

TInt DUsbClientController::ProcessEp0SetupReceived(TInt aCount)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessEp0SetupReceived()"));

	if (aCount > iEp0MaxPacketSize)
		{
		// Fatal error: too much data!
		aCount = iEp0MaxPacketSize;
		}

	// first we split the data into meaningful units:
	TUsbcSetup packet;
	Buffer2Setup(iEp0_RxBuf, packet);

#if defined(_DEBUG) && defined(ENABLE_EXCESSIVE_DEBUG_OUTPUT)
	// let's see what we've got:
	__KTRACE_OPT(KUSB, Kern::Printf("  bmRequestType = 0x%02x", packet.iRequestType));
	if ((packet.iRequestType & KUsbRequestType_TypeMask) == KUsbRequestType_TypeStd)
		{
		switch (packet.iRequest)
			{
		case KUsbRequest_GetStatus:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (GET_STATUS)",
											KUsbRequest_GetStatus));
			break;
		case KUsbRequest_ClearFeature:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (CLEAR_FEATURE)",
											KUsbRequest_ClearFeature));
			break;
		case KUsbRequest_SetFeature:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (SET_FEATURE)",
											KUsbRequest_SetFeature));
			break;
		case KUsbRequest_SetAddress:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (SET_ADDRESS)",
											KUsbRequest_SetAddress));
			break;
		case KUsbRequest_GetDescriptor:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (GET_DESCRIPTOR)",
											KUsbRequest_GetDescriptor));
			break;
		case KUsbRequest_SetDescriptor:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (SET_DESCRIPTOR)",
											KUsbRequest_SetDescriptor));
			break;
		case KUsbRequest_GetConfig:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (GET_CONFIGURATION)",
											KUsbRequest_GetConfig));
			break;
		case KUsbRequest_SetConfig:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (SET_CONFIGURATION)",
											KUsbRequest_SetConfig));
			break;
		case KUsbRequest_GetInterface:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (GET_INTERFACE)",
											KUsbRequest_GetInterface));
			break;
		case KUsbRequest_SetInterface:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (SET_INTERFACE)",
											KUsbRequest_SetInterface));
			break;
		case KUsbRequest_SynchFrame:
			__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (SYNCH_FRAME)",
											KUsbRequest_SynchFrame));
			break;
		default:
			__KTRACE_OPT(KPANIC, Kern::Printf("  Error: bRequest = 0x%02x (UNKNWON STANDARD REQUEST)",
											  packet.iRequest));
			break;
			}
		}
	else
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  bRequest      = 0x%02x (NON-STANDARD REQUEST)",
										packet.iRequest));
		}
	__KTRACE_OPT(KUSB, Kern::Printf("  wValue        = 0x%04x", packet.iValue));
	__KTRACE_OPT(KUSB, Kern::Printf("  wIndex        = 0x%04x", packet.iIndex));
	__KTRACE_OPT(KUSB, Kern::Printf("  wLength       = 0x%04x", packet.iLength));
#endif // defined(_DEBUG) && defined(ENABLE_EXCESSIVE_DEBUG_OUTPUT)

	// now the actual analysis
	if ((packet.iRequestType & KUsbRequestType_TypeMask) == KUsbRequestType_TypeStd)
		{
		iEp0ReceivedNonStdRequest = EFalse;
		switch (packet.iRequest)
			{
		case KUsbRequest_GetStatus:
			switch (packet.iRequestType & KUsbRequestType_DestMask)
				{ // Recipient
			case KUsbRequestType_DestDevice:
				USB_PROCESS_REQUEST(GetDeviceStatus);
				break;
			case KUsbRequestType_DestIfc:
				USB_PROCESS_REQUEST(GetInterfaceStatus);
				break;
			case KUsbRequestType_DestEp:
				USB_PROCESS_REQUEST(GetEndpointStatus);
				break;
			default:
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: GET STATUS - Other or Unknown recipient"));
				__KTRACE_OPT(KPANIC, Kern::Printf("  -> DUsbClientController::ProcessEp0SetupReceived: "
												  "Stalling Ep0"));
				StallEndpoint(KEp0_In);
				break;
				}
			break;
		case KUsbRequest_ClearFeature:
		case KUsbRequest_SetFeature:
			switch (packet.iRequestType & KUsbRequestType_DestMask)
				{ // Recipient
			case KUsbRequestType_DestDevice:
				USB_PROCESS_REQUEST(SetClearDevFeature);
				break;
			case KUsbRequestType_DestIfc:
				USB_PROCESS_REQUEST(SetClearIfcFeature);
				break;
			case KUsbRequestType_DestEp:
				USB_PROCESS_REQUEST(SetClearEpFeature);
				break;
			default:
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: SET/CLEAR FEATURE - "
												  "Other or Unknown recipient"));
				__KTRACE_OPT(KPANIC, Kern::Printf("  -> Stalling Ep0"));
				StallEndpoint(KEp0_In);
				break;
				}
			break;
		case KUsbRequest_SetAddress:
			USB_PROCESS_REQUEST(SetAddress);
			break;
		case KUsbRequest_GetDescriptor:
			USB_PROCESS_REQUEST(GetDescriptor);
			break;
		case KUsbRequest_SetDescriptor:
			USB_PROCESS_REQUEST(SetDescriptor);
			break;
		case KUsbRequest_GetConfig:
			USB_PROCESS_REQUEST(GetConfiguration);
			break;
		case KUsbRequest_SetConfig:
			USB_PROCESS_REQUEST(SetConfiguration);
			break;
		case KUsbRequest_GetInterface:
			USB_PROCESS_REQUEST(GetInterface);
			break;
		case KUsbRequest_SetInterface:
			USB_PROCESS_REQUEST(SetInterface);
			break;
		case KUsbRequest_SynchFrame:
			USB_PROCESS_REQUEST(SynchFrame);
			break;
		default:
			__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Unknown/unsupported Std Setup Request"));
			__KTRACE_OPT(KPANIC, Kern::Printf("  -> Stalling Ep0"));
			StallEndpoint(KEp0_In);
			break;
			}
		}
	else
		{
		// Type mask != KUsbRequestType_TypeStd => class- or vendor-specific request
		iEp0ReceivedNonStdRequest = ETrue;
		const DBase* client = NULL;
		switch (packet.iRequestType & KUsbRequestType_DestMask)
			{ // Recipient
		case KUsbRequestType_DestDevice:
			client = iEp0DeviceControl;
			break;
		case KUsbRequestType_DestIfc:
			if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateConfigured)
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
				}
			else
				{
				const TUsbcInterfaceSet* const ifcset_ptr =
					InterfaceNumber2InterfacePointer(packet.iIndex);
				if (ifcset_ptr)
					{
					if (ifcset_ptr->CurrentInterface()->iNoEp0Requests)
						{
						__KTRACE_OPT(KUSB, Kern::Printf("  Recipient says: NoEp0RequestsPlease"));
						}
					else
						{
						client = ifcset_ptr->iClientId;
						}
					}
				else
					{
					__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Interface 0x%02x does not exist",
													  packet.iIndex));
					}
				}
			break;
		case KUsbRequestType_DestEp:
			if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateConfigured)
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
				}
			else if (EndpointExists(packet.iIndex) == EFalse)
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Endpoint 0x%02x does not exist",
												  packet.iIndex));
				}
			else
				{
				const TInt idx = EpAddr2Idx(packet.iIndex);
				const TUsbcInterfaceSet* const ifcset_ptr =
					iRealEndpoints[idx].iLEndpoint->iInterface->iInterfaceSet;
				if (ifcset_ptr->CurrentInterface()->iNoEp0Requests)
					{
					__KTRACE_OPT(KUSB, Kern::Printf("  Recipient says: NoEp0RequestsPlease"));
					}
				else
					{
					client = ifcset_ptr->iClientId;
					}
				}
			break;
		default:
			__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Other or Unknown recipient"));
			break;
			}
		if (client != NULL)
			{
			// Try to relay packet to the appropriate recipient
			TSglQueIter<TUsbcRequestCallback> iter(iEp0ReadRequestCallbacks);
			TUsbcRequestCallback* p;
			while ((p = iter++) != NULL)
				{
				if (p->Owner() == client)
					{
					__ASSERT_DEBUG((p->iEndpointNum == 0), Kern::Fault(KUsbPILPanicCat, __LINE__));
					__ASSERT_DEBUG((p->iTransferDir == EControllerRead), Kern::Fault(KUsbPILPanicCat, __LINE__));
					__KTRACE_OPT(KUSB, Kern::Printf("  Found Ep0 read request"));
					if (packet.iLength != 0)
						{
						if ((packet.iRequestType & KUsbRequestType_DirMask) == KUsbRequestType_DirToDev)
							{
							// Data transfer & direction OUT => there'll be a DATA_OUT stage
							__KTRACE_OPT(KUSB, Kern::Printf("  Next is DATA_OUT: setting up DataOutVars"));
							SetEp0DataOutVars(packet, client);
							}
						else if ((packet.iRequestType & KUsbRequestType_DirMask) == KUsbRequestType_DirToHost)
							{
							// For possible later use (ZLP).
							iEp0_TxNonStdCount = packet.iLength;
							}
						}
					memcpy(p->iBufferStart, iEp0_RxBuf, aCount);
					p->iError = KErrNone;					// if it wasn't 'KErrNone' we wouldn't be here
					*(p->iPacketSize) = aCount;
					p->iRxPackets = 1;
					*(p->iPacketIndex) = 0;
					ProcessDataTransferDone(*p);
					return KErrNone;
					}
				}
			__KTRACE_OPT(KUSB, Kern::Printf("  Ep0 read request not found: setting RxExtra vars (Setup)"));
			iEp0_RxExtraCount = aCount;
			iEp0_RxExtraData = ETrue;
			return KErrNotFound;
			}
		else // if (client == NULL)
			{
			__KTRACE_OPT(KPANIC, Kern::Printf("  Ep0 request error: Stalling Ep0"));
			StallEndpoint(KEp0_In);
			return KErrGeneral;
			}
		}
	return KErrNone;
	}

#undef USB_PROCESS_REQUEST


TInt DUsbClientController::ProcessEp0DataReceived(TInt aCount)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessEp0DataReceived()"));

	__KTRACE_OPT(KUSB, Kern::Printf("  : %d bytes", aCount));

	if (aCount > iEp0MaxPacketSize)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Too much data"));
		aCount = iEp0MaxPacketSize;
		}
	iEp0DataReceived += aCount;
	if (iEp0ClientId == NULL)
		{
		// it is us (not an app), who owns this transaction
		switch (iSetup.iRequest)
			{
#ifdef USB_SUPPORTS_SET_DESCRIPTOR_REQUEST
		case KUsbRequest_SetDescriptor:
			memcpy(iEp0_RxCollectionBuf + iEp0DataReceived, iEp0_RxBuf, aCount);
			ProceedSetDescriptor();
			break;
#endif
		default:
			__KTRACE_OPT(KPANIC, Kern::Printf("  Error: invalid request in iSetup"));
			__KTRACE_OPT(KPANIC, Kern::Printf("  -> DUsbClientController::ProcessEp0DataReceived: Stalling Ep0"));
			StallEndpoint(KEp0_In);
			ResetEp0DataOutVars();
			break;
			}
		}
	else
		{
		// pass the data on to a client
		TSglQueIter<TUsbcRequestCallback> iter(iEp0ReadRequestCallbacks);
		TUsbcRequestCallback* p;
		while ((p = iter++) != NULL)
			{
			if (p->Owner() == iEp0ClientId)
				{
				__ASSERT_DEBUG((p->iEndpointNum == 0), Kern::Fault(KUsbPILPanicCat, __LINE__));
				__ASSERT_DEBUG((p->iTransferDir == EControllerRead), Kern::Fault(KUsbPILPanicCat, __LINE__));
				__KTRACE_OPT(KUSB, Kern::Printf("  Found Ep0 read request"));
				memcpy(p->iBufferStart, iEp0_RxBuf, aCount);
				p->iError = KErrNone;						// if it wasn't 'KErrNone' we wouldn't be here
				*(p->iPacketSize) = aCount;
				p->iRxPackets = 1;
				*(p->iPacketIndex) = 0;
				ProcessDataTransferDone(*p);
				goto found;
				}
			}
		__KTRACE_OPT(KUSB, Kern::Printf("  Ep0 read request not found: setting RxExtra vars (Data)"));
		iEp0_RxExtraCount = aCount;
		iEp0_RxExtraData = ETrue;
		iEp0DataReceived -= aCount;
		return KErrNotFound;
		}
 found:
	if (iEp0DataReceived >= iSetup.iLength)
		{
		// all data seems now to be here
		ResetEp0DataOutVars();
		}
	return KErrNone;
	}


// --- The USB Spec Chapter 9 Standard Endpoint Zero Device Requests ---

TInt DUsbClientController::ProcessGetDeviceStatus(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessGetDeviceStatus()"));
	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateAddress)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	const TUint16 status = ((DeviceSelfPowered() ? KUsbDevStat_SelfPowered : 0) |
					  (iRmWakeupStatus_Enabled ? KUsbDevStat_RemoteWakeup : 0));
	__KTRACE_OPT(KUSB, Kern::Printf("  Reporting device status: 0x%02x", status));
	*reinterpret_cast<TUint16*>(iEp0_TxBuf) = SWAP_BYTES_16(status);
	if (SetupEndpointZeroWrite(iEp0_TxBuf, sizeof(status)) == KErrNone)
		{
		iEp0WritePending = ETrue;
		}
	return KErrNone;
	}


TInt DUsbClientController::ProcessGetInterfaceStatus(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessGetInterfaceStatus()"));
	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateConfigured)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	if (InterfaceExists(aPacket.iIndex) == EFalse)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Interface does not exist"));
		return KErrGeneral;
		}
	const TUint16 status = 0x0000;							// as of USB Spec 2.0
	__KTRACE_OPT(KUSB, Kern::Printf("  Reporting interface status: 0x%02x", status));
	*reinterpret_cast<TUint16*>(iEp0_TxBuf) = SWAP_BYTES_16(status);
	if (SetupEndpointZeroWrite(iEp0_TxBuf, sizeof(status)) == KErrNone)
		{
		iEp0WritePending = ETrue;
		}
	return KErrNone;
	}


TInt DUsbClientController::ProcessGetEndpointStatus(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessGetEndpointStatus()"));
	if (iTrackDeviceState &&
		((iDeviceState < EUsbcDeviceStateAddress) ||
		 (iDeviceState == EUsbcDeviceStateAddress && (aPacket.iIndex & KUsbEpAddress_Portmask) != 0)))
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	if (EndpointExists(aPacket.iIndex) == EFalse)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Endpoint does not exist"));
		return KErrGeneral;
		}
	const TInt ep = EpAddr2Idx(aPacket.iIndex);
	const TUint16 status = (iRealEndpoints[ep].iHalt) ?	 KUsbEpStat_Halt : 0;
	__KTRACE_OPT(KUSB, Kern::Printf("  Reporting endpoint status 0x%02x for real endpoint %d",
									status, ep));
	*reinterpret_cast<TUint16*>(iEp0_TxBuf) = SWAP_BYTES_16(status);
	if (SetupEndpointZeroWrite(iEp0_TxBuf, 2) == KErrNone)
		{
		iEp0WritePending = ETrue;
		}
	return KErrNone;
	}


TInt DUsbClientController::ProcessSetClearDevFeature(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessSetClearDevFeature()"));
	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateDefault)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}

	TUint test_sel = 0;

	if (aPacket.iRequest == KUsbRequest_SetFeature)
		{
		switch (aPacket.iValue)
			{
		case KUsbFeature_RemoteWakeup:
			if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateAddress)
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
				return KErrGeneral;
				}
			iRmWakeupStatus_Enabled = ETrue;
			break;
		case KUsbFeature_TestMode:
			if (!iHighSpeed)
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Request only supported in High-Speed mode"));
				return KErrGeneral;
				}
			if (LowByte(aPacket.iIndex) != 0)
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Lower byte of wIndex must be zero"));
				return KErrGeneral;
				}
			test_sel = HighByte(aPacket.iIndex);
			if ((test_sel < KUsbTestSelector_Test_J) || (test_sel > KUsbTestSelector_Test_Force_Enable))
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid test selector: %d", test_sel));
				return KErrGeneral;
				}
			break;
		case KUsbFeature_B_HnpEnable:
			if (!iOtgSupport)
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Request only supported on a OTG device"));
				return KErrGeneral;
				}
			if (!(iOtgFuncMap & KUsbOtgAttr_HnpSupp))
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Request only valid if OTG device supports HNP"));
				return KErrGeneral;
				}
			iOtgFuncMap |= KUsbOtgAttr_B_HnpEnable;
			OtgFeaturesNotify();
			break;
		case KUsbFeature_A_HnpSupport:
			if (!iOtgSupport)
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Request only supported on a OTG device"));
				return KErrGeneral;
				}
			if (!(iOtgFuncMap & KUsbOtgAttr_HnpSupp))
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Request only valid if OTG device supports HNP"));
				return KErrGeneral;
				}
			iOtgFuncMap |= KUsbOtgAttr_A_HnpSupport;
			OtgFeaturesNotify();
			break;
		case KUsbFeature_A_AltHnpSupport:
			if (!iOtgSupport)
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Request only supported on a OTG device"));
				return KErrGeneral;
				}
			if (!(iOtgFuncMap & KUsbOtgAttr_HnpSupp))
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Request only valid if OTG device supports HNP"));
				return KErrGeneral;
				}
			iOtgFuncMap |= KUsbOtgAttr_A_AltHnpSupport;
			OtgFeaturesNotify();
			break;
		default:
			__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Unknown feature requested"));
			return KErrGeneral;
			}
		}
	else // KUsbRequest_ClearFeature
		{
		switch (aPacket.iValue)
			{
		case KUsbFeature_RemoteWakeup:
			if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateAddress)
				{
				__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
				return KErrGeneral;
				}
			iRmWakeupStatus_Enabled = EFalse;
			break;
		default:
			__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Unknown feature requested"));
			return KErrGeneral;
			}
		}

	SendEp0ZeroByteStatusPacket();							// success: zero bytes data during status stage

	// 9.4.9: "The transition to test mode of an upstream facing port must not happen until
	// after the status stage of the request."
	if (test_sel)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Entering HS Test Mode %d", test_sel));
		EnterTestMode(test_sel);
		}

	return KErrNone;
	}


TInt DUsbClientController::ProcessSetClearIfcFeature(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessSetClearIfcFeature()"));
	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateConfigured)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	// No interface features defined in USB spec, thus
	return KErrGeneral;
	}


TInt DUsbClientController::ProcessSetClearEpFeature(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessSetClearEpFeature()"));
	if (iTrackDeviceState &&
		((iDeviceState < EUsbcDeviceStateAddress) ||
		 (iDeviceState == EUsbcDeviceStateAddress && (aPacket.iIndex & KUsbEpAddress_Portmask) != 0)))
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	if (aPacket.iValue != KUsbFeature_EndpointHalt)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Unknown feature requested"));
		return KErrGeneral;
		}
	if (EndpointExists(aPacket.iIndex) == EFalse)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Endpoint does not exist"));
		return KErrGeneral;
		}
	const TInt ep = EpAddr2Idx(aPacket.iIndex);
	if (iRealEndpoints[ep].iLEndpoint->iInfo.iType == KUsbEpTypeControl ||
		iRealEndpoints[ep].iLEndpoint->iInfo.iType == KUsbEpTypeIsochronous)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Endpoint is Control or Isochronous"));
		return KErrGeneral;
		}
	SetClearHaltFeature(ep, aPacket.iRequest);
	SendEp0ZeroByteStatusPacket();							// success: zero bytes data during status stage
	return KErrNone;
	}


TInt DUsbClientController::ProcessSetAddress(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessSetAddress()"));
	if (iTrackDeviceState && iDeviceState > EUsbcDeviceStateAddress)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	const TUint16 addr = aPacket.iValue;
	if (addr > 127)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Bad address value: %d (>127)", addr));
		return KErrGeneral;
		}
	if (addr == 0)
		{
		// Enter Default state (from Default or Address)
		NextDeviceState(EUsbcDeviceStateDefault);
		}
	__KTRACE_OPT(KUSB, Kern::Printf("  USB address: %d", addr));
	// The spec says, under section 9.4.6:
	// "Stages after the initial Setup packet assume the same device address as the Setup packet. The USB
	// device does not change its device address until after the Status stage of this request is completed
	// successfully. Note that this is a difference between this request and all other requests. For all other
	// requests, the operation indicated must be completed before the Status stage."
	// Therefore, here we first send the status packet and only then actually execute the request.
	SendEp0ZeroByteStatusPacket();
	SetDeviceAddress(addr);
	return KErrNone;
	}


TInt DUsbClientController::ProcessGetDescriptor(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessGetDescriptor()"));
	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateDefault)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}

	// Make sure we assume the correct speed
	__ASSERT_DEBUG((iHighSpeed == CurrentlyUsingHighSpeed()), Kern::Fault(KUsbPILPanicCat, __LINE__));

	TInt size = 0;
	const TInt result = iDescriptors.FindDescriptor(HighByte(aPacket.iValue), // Type
													LowByte(aPacket.iValue), // Index
													aPacket.iIndex, // Language ID
													size);

	if ((result != KErrNone) || (size == 0))
		{
		// This doesn't have to be an error - protocol-wise it's OK.
		__KTRACE_OPT(KUSB, Kern::Printf("  Couldn't retrieve descriptor"));
		return KErrGeneral;
		}

	__KTRACE_OPT(KUSB, Kern::Printf("  Descriptor found, size: %d (requested: %d)",
									size, aPacket.iLength));
	if (size > KUsbcBufSz_Ep0Tx)
		{
		// This should actually not be possible (i.e. we should never get here).
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Ep0_Tx buffer too small"));
		}
	if (size > aPacket.iLength)
		{
		// Send only as much data as requested by the host
		size = aPacket.iLength;
		}

#ifdef ENABLE_EXCESSIVE_DEBUG_OUTPUT
	__KTRACE_OPT(KUSB,
				 Kern::Printf("  Data: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x ...",
							  iEp0_TxBuf[0], iEp0_TxBuf[1], iEp0_TxBuf[2], iEp0_TxBuf[3],
							  iEp0_TxBuf[4], iEp0_TxBuf[5], iEp0_TxBuf[6], iEp0_TxBuf[7]));
#endif
	// If we're about to send less bytes than expected by the host AND our number is a
	// multiple of the packet size, in order to indicate the end of the control transfer,
	// we must finally send a zero length data packet (ZLP):
	const TBool zlp = ((size < aPacket.iLength) && (size % iEp0MaxPacketSize == 0));
	if (SetupEndpointZeroWrite(iEp0_TxBuf, size, zlp) == KErrNone)
		{
		iEp0WritePending = ETrue;
		}

	return KErrNone;
	}


TInt DUsbClientController::ProcessSetDescriptor(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessSetDescriptor()"));
#ifndef USB_SUPPORTS_SET_DESCRIPTOR_REQUEST
	return KErrGeneral;
#else
	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateAddress)
		{
		// Error: Invalid device state!
		return KErrGeneral;
		}
	if (aPacket.iLength > KUsbcBufSz_Ep0Rx)
		{
		// Error: Our Rx buffer is too small! (Raise a defect to make it larger)
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Ep0_Rx buffer too small"));
		return KErrGeneral;
		}
	SetEp0DataOutVars(aPacket);
	SetupEndpointZeroRead();
	return KErrNone;
#endif
	}


TInt DUsbClientController::ProcessGetConfiguration(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessGetConfiguration()"));
	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateAddress)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	if (iTrackDeviceState && iDeviceState == EUsbcDeviceStateAddress && iCurrentConfig != 0)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: DeviceState Address && Config != 0"));
		return KErrGeneral;
		}
	if (iTrackDeviceState && iDeviceState == EUsbcDeviceStateConfigured && iCurrentConfig == 0)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: DeviceState Configured && Config == 0"));
		return KErrGeneral;
		}
	if (aPacket.iLength != 1)								// "unspecified behavior"
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  Warning: wLength != 1 (= %d)", aPacket.iLength));
		}
	__KTRACE_OPT(KUSB, Kern::Printf("  Reporting configuration value %d", iCurrentConfig));
	if (SetupEndpointZeroWrite(&iCurrentConfig, sizeof(iCurrentConfig)) == KErrNone)
		{
		iEp0WritePending = ETrue;
		}
	return KErrNone;
	}


/** Changes the device's configuration value, including interface setup and/or
	teardown and state change notification of higher-layer clients.
	May also be called by the PSL in special cases - therefore publishedPartner.

	@param aPacket The received Ep0 SET_CONFIGURATION setup request packet.
	@return KErrGeneral in case of a protocol error, KErrNone otherwise.

	@publishedPartner @released
*/
TInt DUsbClientController::ProcessSetConfiguration(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessSetConfiguration()"));

	// This function may be called by the PSL from within an ISR -- so we have
	// to take care what we do here (and also in all functions that get called
	// from here).

	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateAddress)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	const TUint16 value = aPacket.iValue;
	if (value > 1)											// we support only one configuration
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Configuration value too large: %d", value));
		return KErrGeneral;
		}

	__KTRACE_OPT(KUSB, Kern::Printf("  Configuration value: %d", value));
	ChangeConfiguration(value);

	// In 9.4.5 under GET_STATUS we read, that after SET_CONFIGURATION the HALT feature
	// for all endpoints is reset to zero.
	TInt num = 0;
	(TAny) DoForEveryEndpointInUse(&DUsbClientController::ClearHaltFeature, num);
	__KTRACE_OPT(KUSB, Kern::Printf("  Called ClearHaltFeature() for %d endpoints", num));
	SendEp0ZeroByteStatusPacket();							// success: zero bytes data during status stage
	return KErrNone;
	}


TInt DUsbClientController::ProcessGetInterface(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessGetInterface()"));
	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateConfigured)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	if (iCurrentConfig == 0)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Device not configured"));
		return KErrGeneral;
		}
	const TInt number = aPacket.iIndex;
	if (!InterfaceExists(number))
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Bad interface index: %d", number));
		return KErrGeneral;
		}
	// Send alternate setting code of iCurrentInterface of Interface(set) <number> of the current
	// config (iCurrentConfig).
	const TUint8 setting = InterfaceNumber2InterfacePointer(number)->iCurrentInterface;
	__KTRACE_OPT(KUSB, Kern::Printf("  Reporting interface setting %d", setting));
	if (SetupEndpointZeroWrite(&setting, 1) == KErrNone)
		{
		iEp0WritePending = ETrue;
		}
	return KErrNone;
	}


TInt DUsbClientController::ProcessSetInterface(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessSetInterface()"));
	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateConfigured)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	if (iCurrentConfig == 0)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Device not configured"));
		return KErrGeneral;
		}
	const TInt number = aPacket.iIndex;
	if (!InterfaceExists(number))
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Bad interface index: %d", number));
		return KErrGeneral;
		}
	const TInt setting = aPacket.iValue;
	TUsbcInterfaceSet* const ifcset_ptr = InterfaceNumber2InterfacePointer(number);
	RPointerArray<TUsbcInterface>& ifcs = ifcset_ptr->iInterfaces;
	if (setting >= ifcs.Count())
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Alt Setting >= bNumAltSettings: %d", setting));
		return KErrGeneral;
		}
	__KTRACE_OPT(KUSB, Kern::Printf("  Interface setting:: %d", setting));
	// Set iCurrentInterface of Interface(set) <number> of the current config
	// (iCurrentConfig) to alternate setting <setting>.
	ChangeInterface(ifcs[setting]);
	// In 9.4.5 under GET_STATUS we read, that after SET_INTERFACE the HALT feature
	// for all endpoints (of the now current interface setting) is reset to zero.
	RPointerArray<TUsbcLogicalEndpoint>& eps = ifcset_ptr->CurrentInterface()->iEndpoints;
	const TInt num_eps = eps.Count();
	for (TInt i = 0; i < num_eps; i++)
		{
		const TInt ep_num = EpAddr2Idx(eps[i]->iPEndpoint->iEndpointAddr);
		(TAny) ClearHaltFeature(ep_num);
		}
	SendEp0ZeroByteStatusPacket();							// success: zero bytes data during status stage
	return KErrNone;
	}


TInt DUsbClientController::ProcessSynchFrame(const TUsbcSetup& aPacket)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessSynchFrame()"));
	if (iTrackDeviceState && iDeviceState < EUsbcDeviceStateConfigured)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Invalid device state"));
		return KErrGeneral;
		}
	const TInt ep = aPacket.iIndex;
	if (EndpointExists(ep) == EFalse)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Endpoint does not exist"));
		return KErrGeneral;
		}
	if (iRealEndpoints[EpAddr2Idx(ep)].iLEndpoint->iInfo.iType != KUsbEpTypeIsochronous)
		{
		__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Endpoint is not isochronous"));
		return KErrGeneral;
		}
	// We always send 0:
	*reinterpret_cast<TUint16*>(iEp0_TxBuf) = 0x00;
	if (SetupEndpointZeroWrite(iEp0_TxBuf, 2) == KErrNone)
		{
		iEp0WritePending = ETrue;
		}
	return KErrNone;
	}


#ifdef USB_SUPPORTS_SET_DESCRIPTOR_REQUEST
void DUsbClientController::ProceedSetDescriptor()
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProceedSetDescriptor()"));
	// iEp0DataReceived already reflects the current buffer state
	if (iEp0DataReceived < iSetup.iLength)
		{
		// Not yet all data received => proceed
		return;
		}
	if (iEp0DataReceived > iSetup.iLength)
		{
		// Error: more data received than expected
		// but we don't care...
		}
	// at this point: iEp0DataReceived == iSetup.iLength
	const TUint8 type = HighByte(iSetup.iValue);
	if (type == KUsbDescType_String)
		{
		// set/add new string descriptor
		}
	else
		{
		// set/add new ordinary descriptor
		}
	TUint8 index = LowByte(iSetup.iValue);
	TUint16 langid = iSetup.iIndex;
	TUint16 length_total = iSetup.iLength;
	}
#endif


// --- Secondary (Helper) Functions

void DUsbClientController::SetClearHaltFeature(TInt aRealEndpoint, TUint8 aRequest)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetClearHaltFeature()"));
	if (aRequest == KUsbRequest_SetFeature)
		{
		if (iRealEndpoints[aRealEndpoint].iHalt)
			{
			// (This condition is not really an error)
			__KTRACE_OPT(KUSB, Kern::Printf("  Warning: HALT feature already set"));
			return;
			}
		__KTRACE_OPT(KUSB, Kern::Printf("  setting HALT feature for real endpoint %d",
										aRealEndpoint));
		StallEndpoint(aRealEndpoint);
		iRealEndpoints[aRealEndpoint].iHalt = ETrue;
		}
	else													// KUsbRequest_ClearFeature
		{
		if (iRealEndpoints[aRealEndpoint].iHalt == EFalse)
			{
			// In this case, before we return, the data toggles are reset to DATA0.
			__KTRACE_OPT(KUSB, Kern::Printf("  Warning: HALT feature already cleared"));
			ResetDataToggle(aRealEndpoint);
			return;
			}
		__KTRACE_OPT(KUSB, Kern::Printf("  clearing HALT feature for real endpoint %d",
										aRealEndpoint));
		ResetDataToggle(aRealEndpoint);
		ClearStallEndpoint(aRealEndpoint);
		iRealEndpoints[aRealEndpoint].iHalt = EFalse;
		}
	EpStatusNotify(aRealEndpoint);							// only called if actually something changed
	}


TInt DUsbClientController::ClearHaltFeature(TInt aRealEndpoint)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ClearHaltFeature()"));
	if (iRealEndpoints[aRealEndpoint].iHalt != EFalse)
		{
		ClearStallEndpoint(aRealEndpoint);
		iRealEndpoints[aRealEndpoint].iHalt = EFalse;
		}
	return KErrNone;
	}


void DUsbClientController::ChangeConfiguration(TUint16 aValue)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ChangeConfiguration()"));
	// New configuration is the same as the old one: 0
	if (iCurrentConfig == 0 && aValue == 0)
		{
		// no-op
		__KTRACE_OPT(KUSB, Kern::Printf("  Configuration: New == Old == 0 --> exiting"));
		return;
		}
	// New configuration is the same as the old one (but not 0)
	if (iCurrentConfig == aValue)
		{
		// no-op
		__KTRACE_OPT(KUSB, Kern::Printf("  Configuration: New == Old == %d --> exiting", aValue));
		return;
		}
	// Device is already configured
	if (iCurrentConfig != 0)
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  Device was configured: %d", iCurrentConfig));
		// Tear down all interface(set)s of the old configuration
		RPointerArray<TUsbcInterfaceSet>& ifcsets = CurrentConfig()->iInterfaceSets;
		for (TInt i = 0; i < ifcsets.Count(); ++i)
			{
			__KTRACE_OPT(KUSB, Kern::Printf("  Tearing down InterfaceSet %d", i));
			InterfaceSetTeardown(ifcsets[i]);
			}
		iCurrentConfig = 0;
		// Enter Address state (from Configured)
		if (iDeviceState == EUsbcDeviceStateConfigured)
			NextDeviceState(EUsbcDeviceStateAddress);
		}
	// Device gets a new configuration
	if (aValue != 0)
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  Device gets new configuration..."));
		// Setup all alternate settings 0 of all interfaces
		// (Don't separate the next two lines of code.)
		iCurrentConfig = aValue;
		RPointerArray<TUsbcInterfaceSet>& ifcsets = CurrentConfig()->iInterfaceSets;
		const TInt n = ifcsets.Count();
		for (TInt i = 0; i < n; ++i)
			{
			__KTRACE_OPT(KUSB, Kern::Printf("  Setting up InterfaceSet %d", i));
			InterfaceSetup(ifcsets[i]->iInterfaces[0]);
			}
		// Enter Configured state (from Address or Configured)
		NextDeviceState(EUsbcDeviceStateConfigured);
		}
	__KTRACE_OPT(KUSB, Kern::Printf("  New configuration: %d", iCurrentConfig));
	return;
	}


void DUsbClientController::InterfaceSetup(TUsbcInterface* aIfc)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::InterfaceSetup()"));
	const TInt num_eps = aIfc->iEndpoints.Count();
	for (TInt i = 0; i < num_eps; i++)
		{
		// Prepare this endpoint for I/O
		TUsbcLogicalEndpoint* const ep = aIfc->iEndpoints[i];
		// (TUsbcLogicalEndpoint's FS/HS endpoint sizes and interval values got
		//  adjusted in its constructor.)
		if (iHighSpeed)
			{
			__KTRACE_OPT(KUSB, Kern::Printf("  Setting Ep info size to %d (HS)", ep->iEpSize_Hs));
			ep->iInfo.iSize = ep->iEpSize_Hs;
			}
		else
			{
			__KTRACE_OPT(KUSB, Kern::Printf("  Setting Ep info size to %d (FS)", ep->iEpSize_Fs));
			ep->iInfo.iSize = ep->iEpSize_Fs;
			}
		const TInt idx = EpAddr2Idx(ep->iPEndpoint->iEndpointAddr);
		if (ConfigureEndpoint(idx, ep->iInfo) != KErrNone)
			{
			__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Endpoint %d configuration failed", idx));
			continue;
			}
		// Should there be a problem with it then we could try resetting the ep
		// data toggle at this point (or before the Configure) as well.
		__KTRACE_OPT(KUSB, Kern::Printf("  Connecting real ep addr 0x%02x & logical ep #%d",
										ep->iPEndpoint->iEndpointAddr, ep->iLEndpointNum));
		ep->iPEndpoint->iLEndpoint = ep;
		}
	aIfc->iInterfaceSet->iCurrentInterface = aIfc->iSettingCode;
	return;
	}


void DUsbClientController::InterfaceSetTeardown(TUsbcInterfaceSet* aIfcSet)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::InterfaceSetTeardown()"));
	if (aIfcSet->iInterfaces.Count() == 0)
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  No interfaces exist - returning"));
		return;
		}
	RPointerArray<TUsbcLogicalEndpoint>& eps = aIfcSet->CurrentInterface()->iEndpoints;
	const TInt num_eps = eps.Count();
	for (TInt i = 0; i < num_eps; i++)
		{
		TUsbcLogicalEndpoint* const ep = eps[i];
		const TInt idx = EpAddr2Idx(ep->iPEndpoint->iEndpointAddr);

		CancelTransferRequests(idx);

		if (!ep->iPEndpoint->iLEndpoint)
			{
			__KTRACE_OPT(KUSB, Kern::Printf("  real ep %d not configured: skipping", idx));
			continue;
			}
		if (ResetDataToggle(idx) != KErrNone)
			{
			__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Endpoint %d data toggle reset failed", idx));
			}
		if (DeConfigureEndpoint(idx) != KErrNone)
			{
			__KTRACE_OPT(KPANIC, Kern::Printf("  Error: Endpoint %d de-configuration failed", idx));
			}

		__KTRACE_OPT(KUSB, Kern::Printf("  disconnecting real ep & logical ep"));
		ep->iPEndpoint->iLEndpoint = NULL;
		}
	if (aIfcSet->CurrentInterface() != 0)
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  Resetting alternate interface setting to 0"));
		aIfcSet->iCurrentInterface = 0;
		}
	return;
	}


void DUsbClientController::ChangeInterface(TUsbcInterface* aIfc)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ChangeInterface()"));
	TUsbcInterfaceSet* ifcset = aIfc->iInterfaceSet;
	const TUint8 setting = aIfc->iSettingCode;
	if (ifcset->iCurrentInterface == setting)
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  New Ifc == old Ifc: nothing to do"));
		return;
		}
	__KTRACE_OPT(KUSB, Kern::Printf("  Setting new interface setting #%d", setting));
	InterfaceSetTeardown(ifcset);
	InterfaceSetup(aIfc);
	StatusNotify(static_cast<TUsbcDeviceState>(KUsbAlternateSetting | setting), ifcset->iClientId);
	}


// aFunction gets called, successively, with the endpoint index of every ep in-use as its argument.
// (BTW: The declaration "type (class::*name)(params)" makes <name> a "pointer to element function".)
//
TInt DUsbClientController::DoForEveryEndpointInUse(TInt (DUsbClientController::*aFunction)(TInt), TInt& aCount)
	{
	__KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DoForEveryEndpointInUse()"));
	aCount = 0;
	TUsbcConfiguration* const config = CurrentConfig();
	if (!config)
		{
		__KTRACE_OPT(KUSB, Kern::Printf("  Device is not configured - returning"));
		return KErrNone;
		}
	RPointerArray<TUsbcInterfaceSet>& ifcsets = config->iInterfaceSets;
	const TInt num_ifcsets = ifcsets.Count();
	for (TInt i = 0; i < num_ifcsets; i++)
		{
		RPointerArray<TUsbcLogicalEndpoint>& eps = ifcsets[i]->CurrentInterface()->iEndpoints;
		const TInt num_eps = eps.Count();
		for (TInt j = 0; j < num_eps; j++)
			{
			const TInt ep_num = EpAddr2Idx(eps[j]->iPEndpoint->iEndpointAddr);
			const TInt result = (this->*aFunction)(ep_num);
			++aCount;
			if (result != KErrNone)
				{
				return result;
				}
			}
		}
	return KErrNone;
	}


// -eof-