diff -r 000000000000 -r a41df078684a bsptemplate/asspandvariant/template_assp/pa_usbc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bsptemplate/asspandvariant/template_assp/pa_usbc.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1998 @@ +// Copyright (c) 2004-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: +// template\template_assp\pa_usbc.cpp +// Platform-dependent USB client controller layer (USB PSL). +// +// + + +#include // /assp/template_assp/ +#include // /assp/template_assp/ + +#include + +#include "pa_usbc.h" // . + +// Debug support +#ifdef _DEBUG +static const char KUsbPanicCat[] = "USB PSL"; +#endif + + +// Define USB_SUPPORTS_PREMATURE_STATUS_IN to enable proper handling of a premature STATUS_IN stage, i.e. a +// situation where the host sends less data than first announced and instead of more data (OUT) will send an +// IN token to start the status stage. What we do in order to implement this here is to prime the TX fifo with +// a ZLP immediately when we find out that we're dealing with a DATA_OUT request. This way, as soon as the +// premature IN token is received, we complete the transaction by sending off the ZLP. If we don't prime the +// TX fifo then there is no way for us to recognise a premature status because the IN token itself doesn't +// raise an interrupt. We would simply wait forever for more data, or rather we would time out and the host +// would move on and send the next Setup packet. +// The reason why we would not want to implement the proper behaviour is this: After having primed the TX fifo +// with a ZLP, it is impossible for a user to reject such a (class/vendor specific) Setup request, basically +// because the successful status stage happens automatically. At the time the user has received and decoded +// the Setup request there's for her no way to stall Ep0 in order to show to the host that this Setup packet +// is invalid or inappropriate or whatever, because she cannot prevent the status stage from happening. +// (All this is strictly true only if the amount of data in the data stage is less than or equal to Ep0's max +// packet size. However this is almost always the case.) +//#define USB_SUPPORTS_PREMATURE_STATUS_IN + + +static const TUsbcEndpointCaps DeviceEndpoints[KUsbTotalEndpoints] = + { + // Hardware # iEndpoints index + {KEp0MaxPktSzMask, (KUsbEpTypeControl | KUsbEpDirOut)}, // 0 - 0 + {KEp0MaxPktSzMask, (KUsbEpTypeControl | KUsbEpDirIn )}, // 0 - 1 + {KUsbEpNotAvailable, KUsbEpNotAvailable}, // --- Not present + {KBlkMaxPktSzMask, (KUsbEpTypeBulk | KUsbEpDirIn )}, // 1 - 3 + {KBlkMaxPktSzMask, (KUsbEpTypeBulk | KUsbEpDirOut)}, // 2 - 4 + {KUsbEpNotAvailable, KUsbEpNotAvailable}, // --- Not present + {KUsbEpNotAvailable, KUsbEpNotAvailable}, // --- Not present + {KIsoMaxPktSzMask, (KUsbEpTypeIsochronous | KUsbEpDirIn )}, // 3 - 7 + {KIsoMaxPktSzMask, (KUsbEpTypeIsochronous | KUsbEpDirOut)}, // 4 - 8 + {KUsbEpNotAvailable, KUsbEpNotAvailable}, // --- Not present + {KUsbEpNotAvailable, KUsbEpNotAvailable}, // --- Not present + {KIntMaxPktSzMask, (KUsbEpTypeInterrupt | KUsbEpDirIn )}, // 5 - 11 + }; + + +// --- TEndpoint -------------------------------------------------------------- + +TEndpoint::TEndpoint() +// +// Constructor +// + : iRxBuf(NULL), iReceived(0), iLength(0), iZlpReqd(EFalse), iNoBuffer(EFalse), iDisabled(EFalse), + iPackets(0), iLastError(KErrNone), iRequest(NULL), iRxTimer(RxTimerCallback, this), + iRxTimerSet(EFalse), iRxMoreDataRcvd(EFalse), iPacketIndex(NULL), iPacketSize(NULL) + { + __KTRACE_OPT(KUSB, Kern::Printf("TEndpoint::TEndpoint")); + } + + +void TEndpoint::RxTimerCallback(TAny* aPtr) +// +// (This function is static.) +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TEndpoint::RxTimerCallback")); + + TEndpoint* const ep = static_cast(aPtr); + if (!ep) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: !ep")); + } + else if (!ep->iRxTimerSet) + { + // Timer 'stop' substitute (instead of stopping it, + // we just let it expire after clearing iRxTimerSet) + __KTRACE_OPT(KUSB, Kern::Printf("!ep->iRxTimerSet - returning")); + } + else if (!ep->iRxBuf) + { + // Request already completed + __KTRACE_OPT(KUSB, Kern::Printf("!ep->iRxBuf - returning")); + } + else if (ep->iRxMoreDataRcvd) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > rx timer cb: not yet completing...")); + ep->iRxMoreDataRcvd = EFalse; + ep->iRxTimer.Again(KRxTimerTimeout); + } + else + { + __KTRACE_OPT(KUSB, Kern::Printf(" > rx timer cb: completing now...")); + *ep->iPacketSize = ep->iReceived; + ep->iController->RxComplete(ep); + } + } + + +// --- TTemplateAsspUsbcc public --------------------------------------------------- + +TTemplateAsspUsbcc::TTemplateAsspUsbcc() +// +// Constructor. +// + : iCableConnected(ETrue), iBusIsPowered(EFalse), + iInitialized(EFalse), iUsbClientConnectorCallback(UsbClientConnectorCallback), + iEp0Configured(EFalse) + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::TTemplateAsspUsbcc")); + + iAssp = static_cast(Arch::TheAsic()); + + iSoftwareConnectable = iAssp->UsbSoftwareConnectable(); + + iCableDetectable = iAssp->UsbClientConnectorDetectable(); + + if (iCableDetectable) + { + // Register our callback for detecting USB cable insertion/removal. + // We ignore the error code: if the registration fails, we just won't get any events. + // (Which of course is bad enough...) + (void) iAssp->RegisterUsbClientConnectorCallback(iUsbClientConnectorCallback, this); + // Call the callback straight away so we get the proper PIL state from the beginning. + (void) UsbClientConnectorCallback(this); + } + + for (TInt i = 0; i < KUsbTotalEndpoints; i++) + { + iEndpoints[i].iController = this; + } + } + + +TInt TTemplateAsspUsbcc::Construct() +// +// Construct. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Construct")); + + TUsbcDeviceDescriptor* DeviceDesc = TUsbcDeviceDescriptor::New( + 0x00, // aDeviceClass + 0x00, // aDeviceSubClass + 0x00, // aDeviceProtocol + KEp0MaxPktSz, // aMaxPacketSize0 + KUsbVendorId, // aVendorId + KUsbProductId, // aProductId + KUsbDevRelease, // aDeviceRelease + 1); // aNumConfigurations + if (!DeviceDesc) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for dev desc failed.")); + return KErrGeneral; + } + + TUsbcConfigDescriptor* ConfigDesc = TUsbcConfigDescriptor::New( + 1, // aConfigurationValue + ETrue, // aSelfPowered (see 12.4.2 "Bus-Powered Devices") + ETrue, // aRemoteWakeup + 0); // aMaxPower (mA) + if (!ConfigDesc) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for config desc failed.")); + return KErrGeneral; + } + + TUsbcLangIdDescriptor* StringDescLang = TUsbcLangIdDescriptor::New(KUsbLangId); + if (!StringDescLang) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for lang id $ desc failed.")); + return KErrGeneral; + } + + // ('sizeof(x) - 2' because 'wchar_t KStringXyz' created a wide string that ends in '\0\0'.) + + TUsbcStringDescriptor* StringDescManu = + TUsbcStringDescriptor::New(TPtr8( + const_cast(reinterpret_cast(KStringManufacturer)), + sizeof(KStringManufacturer) - 2, sizeof(KStringManufacturer) - 2)); + if (!StringDescManu) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for manufacturer $ desc failed.")); + return KErrGeneral; + } + + TUsbcStringDescriptor* StringDescProd = + TUsbcStringDescriptor::New(TPtr8( + const_cast(reinterpret_cast(KStringProduct)), + sizeof(KStringProduct) - 2, sizeof(KStringProduct) - 2)); + if (!StringDescProd) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for product $ desc failed.")); + return KErrGeneral; + } + + TUsbcStringDescriptor* StringDescSer = + TUsbcStringDescriptor::New(TPtr8( + const_cast(reinterpret_cast(KStringSerialNo)), + sizeof(KStringSerialNo) - 2, sizeof(KStringSerialNo) - 2)); + if (!StringDescSer) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for serial no $ desc failed.")); + return KErrGeneral; + } + + TUsbcStringDescriptor* StringDescConf = + TUsbcStringDescriptor::New(TPtr8( + const_cast(reinterpret_cast(KStringConfig)), + sizeof(KStringConfig) - 2, sizeof(KStringConfig) - 2)); + if (!StringDescConf) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for config $ desc failed.")); + return KErrGeneral; + } + + const TBool r = InitialiseBaseClass(DeviceDesc, + ConfigDesc, + StringDescLang, + StringDescManu, + StringDescProd, + StringDescSer, + StringDescConf); + if (!r) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: UsbClientController::InitialiseBaseClass failed.")); + return KErrGeneral; + } + + return KErrNone; + } + + +TTemplateAsspUsbcc::~TTemplateAsspUsbcc() +// +// Destructor. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::~TTemplateAsspUsbcc")); + + // Unregister our callback for detecting USB cable insertion/removal + if (iCableDetectable) + { + iAssp->UnregisterUsbClientConnectorCallback(); + } + if (iInitialized) + { + // (The explicit scope operator is used against Lint warning #1506.) + TTemplateAsspUsbcc::StopUdc(); + } + } + + +TBool TTemplateAsspUsbcc::DeviceStateChangeCaps() const +// +// Returns capability of hardware to accurately track the device state (Chapter 9 state). +// + { + // TO DO: Return EFalse or ETrue here, depending on whether the UDC supports exact device state tracking + // (most don't). + return EFalse; + } + + +TInt TTemplateAsspUsbcc::SignalRemoteWakeup() +// +// Forces the UDC into a non-idle state to perform a remote wakeup operation. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SignalRemoteWakeup")); + + // TO DO: Do here whatever is necessary for the UDC to signal remote wakeup. + + return KErrNone; + } + + +void TTemplateAsspUsbcc::DumpRegisters() +// +// Dumps the contents of a number of UDC registers to the screen (using Kern::Printf()). +// Rarely used, but might prove helpful when needed. +// + { + Kern::Printf("TCotullaUsbcc::DumpRegisters:"); + + // TO DO: Print the contents of some (or all) UDC registers here. + } + + +TDfcQue* TTemplateAsspUsbcc::DfcQ(TInt /* aUnit */) +// +// Returns a pointer to the kernel DFC queue to be used buy the USB LDD. +// + { + return Kern::DfcQue0(); + } + + +// --- TTemplateAsspUsbcc private virtual ------------------------------------------ + +TInt TTemplateAsspUsbcc::SetDeviceAddress(TInt aAddress) +// +// Sets the PIL-provided device address manually (if possible - otherwise do nothing). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SetDeviceAddress: %d", aAddress)); + + // TO DO (optional): Set device address here. + + if (aAddress) + { + // Address can be zero. + MoveToAddressState(); + } + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::ConfigureEndpoint(TInt aRealEndpoint, const TUsbcEndpointInfo& aEndpointInfo) +// +// Prepares (enables) an endpoint (incl. Ep0) for data transmission or reception. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::ConfigureEndpoint(%d)", aRealEndpoint)); + + const TInt n = ArrayIdx2TemplateEp(aRealEndpoint); + if (n < 0) + return KErrArgument; + + TEndpoint* const ep = &iEndpoints[aRealEndpoint]; + if (ep->iDisabled == EFalse) + { + EnableEndpointInterrupt(n); + } + ep->iNoBuffer = EFalse; + if (n == 0) + iEp0Configured = ETrue; + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::DeConfigureEndpoint(TInt aRealEndpoint) +// +// Disables an endpoint (incl. Ep0). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::DeConfigureEndpoint(%d)", aRealEndpoint)); + + const TInt n = ArrayIdx2TemplateEp(aRealEndpoint); + if (n < 0) + return KErrArgument; + + DisableEndpointInterrupt(n); + if (n == 0) + iEp0Configured = EFalse; + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::AllocateEndpointResource(TInt aRealEndpoint, TUsbcEndpointResource aResource) +// +// Puts the requested endpoint resource to use, if possible. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::AllocateEndpointResource(%d): %d", + aRealEndpoint, aResource)); + + // TO DO: Allocate endpoint resource here. + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::DeAllocateEndpointResource(TInt aRealEndpoint, TUsbcEndpointResource aResource) +// +// Stops the use of the indicated endpoint resource, if beneficial. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::DeAllocateEndpointResource(%d): %d", + aRealEndpoint, aResource)); + + // TO DO: Deallocate endpoint resource here. + + return KErrNone; + } + + +TBool TTemplateAsspUsbcc::QueryEndpointResource(TInt aRealEndpoint, TUsbcEndpointResource aResource) const +// +// Returns the status of the indicated resource and endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::QueryEndpointResource(%d): %d", + aRealEndpoint, aResource)); + + // TO DO: Query endpoint resource here. The return value should reflect the actual state. + return ETrue; + } + + +TInt TTemplateAsspUsbcc::OpenDmaChannel(TInt aRealEndpoint) +// +// Opens a DMA channel for this endpoint. This function is always called during the creation of an endpoint +// in the PIL. If DMA channels are a scarce resource, it's possible to do nothing here and wait for an +// AllocateEndpointResource call instead. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::OpenDmaChannel(%d)", aRealEndpoint)); + + // TO DO (optional): Open DMA channel here. + + // An error should only be returned in case of an actual DMA problem. + return KErrNone; + } + + +void TTemplateAsspUsbcc::CloseDmaChannel(TInt aRealEndpoint) +// +// Closes a DMA channel for this endpoint. This function is always called during the destruction of an +// endpoint in the PIL. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::CloseDmaChannel(%d)", aRealEndpoint)); + + // TO DO (optional): Close DMA channel here (only if it was opened via OpenDmaChannel). + } + + +TInt TTemplateAsspUsbcc::SetupEndpointRead(TInt aRealEndpoint, TUsbcRequestCallback& aCallback) +// +// Sets up a read request for an endpoint on behalf of the LDD. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SetupEndpointRead(%d)", aRealEndpoint)); + + if (!IS_OUT_ENDPOINT(aRealEndpoint)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: !IS_OUT_ENDPOINT(%d)", aRealEndpoint)); + return KErrArgument; + } + TEndpoint* const ep = &iEndpoints[aRealEndpoint]; + if (ep->iRxBuf != NULL) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > WARNING: iEndpoints[%d].iRxBuf != NULL", aRealEndpoint)); + return KErrGeneral; + } + ep->iRxBuf = aCallback.iBufferStart; + ep->iReceived = 0; + ep->iLength = aCallback.iLength; + // For Bulk reads we start out with the assumption of 1 packet (see BulkReceive for why): + ep->iPackets = IS_BULK_OUT_ENDPOINT(aRealEndpoint) ? 1 : 0; + ep->iRequest = &aCallback; + ep->iPacketIndex = aCallback.iPacketIndex; + if (IS_BULK_OUT_ENDPOINT(aRealEndpoint)) + *ep->iPacketIndex = 0; // a one-off optimization + ep->iPacketSize = aCallback.iPacketSize; + + const TInt n = ArrayIdx2TemplateEp(aRealEndpoint); + if (ep->iDisabled) + { + ep->iDisabled = EFalse; + EnableEndpointInterrupt(n); + } + else if (ep->iNoBuffer) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > There had been no Rx buffer available: reading Rx FIFO now")); + ep->iNoBuffer = EFalse; + if (IS_BULK_OUT_ENDPOINT(aRealEndpoint)) + { + BulkReadRxFifo(n); + } + else if (IS_ISO_OUT_ENDPOINT(aRealEndpoint)) + { + IsoReadRxFifo(n); + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Endpoint not found")); + } + } + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::SetupEndpointWrite(TInt aRealEndpoint, TUsbcRequestCallback& aCallback) +// +// Sets up a write request for an endpoint on behalf of the LDD. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SetupEndpointWrite(%d)", aRealEndpoint)); + + if (!IS_IN_ENDPOINT(aRealEndpoint)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: !IS_IN_ENDPOINT(%d)", aRealEndpoint)); + return KErrArgument; + } + TEndpoint* const ep = &iEndpoints[aRealEndpoint]; + if (ep->iTxBuf != NULL) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: iEndpoints[%d].iTxBuf != NULL", aRealEndpoint)); + return KErrGeneral; + } + ep->iTxBuf = aCallback.iBufferStart; + ep->iTransmitted = 0; + ep->iLength = aCallback.iLength; + ep->iPackets = 0; + ep->iZlpReqd = aCallback.iZlpReqd; + ep->iRequest = &aCallback; + + const TInt n = ArrayIdx2TemplateEp(aRealEndpoint); + if (IS_BULK_IN_ENDPOINT(aRealEndpoint)) + { + if (ep->iDisabled) + { + ep->iDisabled = EFalse; + EnableEndpointInterrupt(n); + } + BulkTransmit(n); + } + else if (IS_ISO_IN_ENDPOINT(aRealEndpoint)) + { + IsoTransmit(n); + } + else if (IS_INT_IN_ENDPOINT(aRealEndpoint)) + { + IntTransmit(n); + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Endpoint not found")); + } + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::CancelEndpointRead(TInt aRealEndpoint) +// +// Cancels a read request for an endpoint on behalf of the LDD. +// No completion to the PIL occurs. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::CancelEndpointRead(%d)", aRealEndpoint)); + + if (!IS_OUT_ENDPOINT(aRealEndpoint)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: !IS_OUT_ENDPOINT(%d)", aRealEndpoint)); + return KErrArgument; + } + TEndpoint* const ep = &iEndpoints[aRealEndpoint]; + if (ep->iRxBuf == NULL) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > WARNING: iEndpoints[%d].iRxBuf == NULL", aRealEndpoint)); + return KErrNone; + } + ep->iRxBuf = NULL; + ep->iReceived = 0; + ep->iNoBuffer = EFalse; + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::CancelEndpointWrite(TInt aRealEndpoint) +// +// Cancels a write request for an endpoint on behalf of the LDD. +// No completion to the PIL occurs. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::CancelEndpointWrite(%d)", aRealEndpoint)); + + if (!IS_IN_ENDPOINT(aRealEndpoint)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: !IS_IN_ENDPOINT(%d)", aRealEndpoint)); + return KErrArgument; + } + TEndpoint* const ep = &iEndpoints[aRealEndpoint]; + if (ep->iTxBuf == NULL) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > WARNING: iEndpoints[%d].iTxBuf == NULL", aRealEndpoint)); + return KErrNone; + } + + // TO DO (optional): Flush the Ep's Tx FIFO here, if possible. + + ep->iTxBuf = NULL; + ep->iTransmitted = 0; + ep->iNoBuffer = EFalse; + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::SetupEndpointZeroRead() +// +// Sets up an Ep0 read request (own function due to Ep0's special status). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SetupEndpointZeroRead")); + + TEndpoint* const ep = &iEndpoints[KEp0_Out]; + if (ep->iRxBuf != NULL) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > WARNING: iEndpoints[%d].iRxBuf != NULL", KEp0_Out)); + return KErrGeneral; + } + ep->iRxBuf = iEp0_RxBuf; + ep->iReceived = 0; + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::SetupEndpointZeroWrite(const TUint8* aBuffer, TInt aLength, TBool aZlpReqd) +// +// Sets up an Ep0 write request (own function due to Ep0's special status). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SetupEndpointZeroWrite")); + + TEndpoint* const ep = &iEndpoints[KEp0_In]; + if (ep->iTxBuf != NULL) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: iEndpoints[%d].iTxBuf != NULL", KEp0_In)); + return KErrGeneral; + } + ep->iTxBuf = aBuffer; + ep->iTransmitted = 0; + ep->iLength = aLength; + ep->iZlpReqd = aZlpReqd; + ep->iRequest = NULL; + Ep0Transmit(); + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::SendEp0ZeroByteStatusPacket() +// +// Sets up an Ep0 write request for zero bytes. +// This is a separate function because no data transfer is involved here. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SendEp0ZeroByteStatusPacket")); + + // This is possibly a bit tricky. When this function is called it just means that the higher layer wants a + // ZLP to be sent. Whether we actually send one manually here depends on a number of factors, as the + // current Ep0 state (i.e. the stage of the Ep0 Control transfer), and, in case the hardware handles some + // ZLPs itself, whether it might already handle this one. + + // Here is an example of what the checking of the conditions might look like: + +#ifndef USB_SUPPORTS_SET_DESCRIPTOR_REQUEST + if ((!iEp0ReceivedNonStdRequest && iEp0State == EP0_IN_DATA_PHASE) || +#else + if ((!iEp0ReceivedNonStdRequest && iEp0State != EP0_IDLE) || +#endif +#ifdef USB_SUPPORTS_PREMATURE_STATUS_IN + (iEp0ReceivedNonStdRequest && iEp0State != EP0_OUT_DATA_PHASE)) +#else + (iEp0ReceivedNonStdRequest)) +#endif + { + // TO DO: Arrange for the sending of a ZLP here. + } + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::StallEndpoint(TInt aRealEndpoint) +// +// Stalls an endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::StallEndpoint(%d)", aRealEndpoint)); + + if (IS_ISO_ENDPOINT(aRealEndpoint)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Iso endpoint cannot be stalled")); + return KErrArgument; + } + + // TO DO: Stall the endpoint here. + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::ClearStallEndpoint(TInt aRealEndpoint) +// +// Clears the stall condition of an endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::ClearStallEndpoint(%d)", aRealEndpoint)); + + if (IS_ISO_ENDPOINT(aRealEndpoint)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Iso endpoint cannot be unstalled")); + return KErrArgument; + } + + // TO DO: De-stall the endpoint here. + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::EndpointStallStatus(TInt aRealEndpoint) const +// +// Reports the stall status of an endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::EndpointStallStatus(%d)", aRealEndpoint)); + + if (IS_ISO_ENDPOINT(aRealEndpoint)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Iso endpoint has no stall status")); + return KErrArgument; + } + + // TO DO: Query endpoint stall status here. The return value should reflect the actual state. + return ETrue; + } + + +TInt TTemplateAsspUsbcc::EndpointErrorStatus(TInt aRealEndpoint) const +// +// Reports the error status of an endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::EndpointErrorStatus(%d)", aRealEndpoint)); + + if (!IS_VALID_ENDPOINT(aRealEndpoint)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: !IS_VALID_ENDPOINT(%d)", aRealEndpoint)); + return KErrArgument; + } + + // TO DO: Query endpoint error status here. The return value should reflect the actual state. + // With some UDCs there is no way of inquiring the endpoint error status; say 'ETrue' in that case. + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::ResetDataToggle(TInt aRealEndpoint) +// +// Resets to zero the data toggle bit of an endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::ResetDataToggle(%d)", aRealEndpoint)); + + // TO DO: Reset the endpoint's data toggle bit here. + // With some UDCs there is no way to individually reset the endpoint's toggle bits; just return KErrNone + // in that case. + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::SynchFrameNumber() const +// +// For use with isochronous endpoints only. Causes the SOF frame number to be returned. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SynchFrameNumber")); + + // TO DO: Query and return the SOF frame number here. + return 0; + } + + +void TTemplateAsspUsbcc::SetSynchFrameNumber(TInt aFrameNumber) +// +// For use with isochronous endpoints only. Causes the SOF frame number to be stored. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SetSynchFrameNumber(%d)", aFrameNumber)); + + // We should actually store this number somewhere. But the PIL always sends '0x00' + // in response to a SYNCH_FRAME request... + // TO DO: Store the frame number. Alternatively (until SYNCH_FRAME request specification changes): Do + // nothing. + } + + +TInt TTemplateAsspUsbcc::StartUdc() +// +// Called to initialize the device controller hardware before any operation can be performed. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::StartUdc")); + + if (iInitialized) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: UDC already initialised")); + return KErrNone; + } + + // Disable UDC (might also reset the entire design): + UdcDisable(); + + // Enable UDC's clock: + // TO DO: Enable UDC's clock here. + + // Even if only one USB feature has been enabled, we later need to undo it: + iInitialized = ETrue; + + // Bind & enable the UDC interrupt + if (SetupUdcInterrupt() != KErrNone) + { + return KErrGeneral; + } + + // Write meaningful values to some registers: + InitialiseUdcRegisters(); + + // Finally, turn on the UDC: + UdcEnable(); + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::StopUdc() +// +// Basically, makes undone what happened in StartUdc. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::StopUdc")); + + if (!iInitialized) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: UDC not initialized")); + return KErrNone; + } + + // Disable UDC: + UdcDisable(); + + // Mask (disable) Reset interrupt: + // TO DO: Mask (disable) the USB Reset interrupt here. + + // Disable & unbind the UDC interrupt: + ReleaseUdcInterrupt(); + + // Finally turn off UDC's clock: + // TO DO: Disable UDC's clock here. + + // Only when all USB features have been disabled we'll call it a day: + iInitialized = EFalse; + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::UdcConnect() +// +// Connects the UDC to the bus under software control. How this is achieved depends on the UDC; the +// functionality might also be part of the Variant component (instead of the ASSP). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::UdcConnect")); + + // Here: A call into the Variant-provided function. + return iAssp->UsbConnect(); + } + + +TInt TTemplateAsspUsbcc::UdcDisconnect() +// +// Disconnects the UDC from the bus under software control. How this is achieved depends on the UDC; the +// functionality might also be part of the Variant component (instead of the ASSP). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::UdcDisconnect")); + + // Here: A call into the Variant-provided function. + return iAssp->UsbDisconnect(); + } + + +TBool TTemplateAsspUsbcc::UsbConnectionStatus() const +// +// Returns a value showing the USB cable connection status of the device. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::UsbConnectionStatus")); + + return iCableConnected; + } + + +TBool TTemplateAsspUsbcc::UsbPowerStatus() const +// +// Returns a truth value showing whether VBUS is currently powered or not. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::UsbPowerStatus")); + + return iBusIsPowered; + } + + +TBool TTemplateAsspUsbcc::DeviceSelfPowered() const +// +// Returns a truth value showing whether the device is currently self-powered or not. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::DeviceSelfPowered")); + + // TO DO: Query and return self powered status here. The return value should reflect the actual state. + // (This can be always 'ETrue' if the UDC does not support bus-powered devices.) + return ETrue; + } + + +const TUsbcEndpointCaps* TTemplateAsspUsbcc::DeviceEndpointCaps() const +// +// Returns a pointer to an array of elements, each of which describes the capabilities of one endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::DeviceEndpointCaps")); + __KTRACE_OPT(KUSB, Kern::Printf(" > Ep: Sizes Mask, Types Mask")); + __KTRACE_OPT(KUSB, Kern::Printf(" > --------------------------")); + for (TInt i = 0; i < KUsbTotalEndpoints; ++i) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > %02d: 0x%08x, 0x%08x", + i, DeviceEndpoints[i].iSizes, DeviceEndpoints[i].iTypesAndDir)); + } + return DeviceEndpoints; + } + + +TInt TTemplateAsspUsbcc::DeviceTotalEndpoints() const +// +// Returns the element number of the endpoints array a pointer to which is returned by DeviceEndpointCaps. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::DeviceTotalEndpoints")); + + return KUsbTotalEndpoints; + } + + +TBool TTemplateAsspUsbcc::SoftConnectCaps() const +// +// Returns a truth value showing whether or not there is the capability to disconnect and re-connect the D+ +// line under software control. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SoftConnectCaps")); + + return iSoftwareConnectable; + } + + +void TTemplateAsspUsbcc::Suspend() +// +// Called by the PIL after a Suspend event has been reported (by us). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Suspend")); + + // TO DO (optional): Implement here anything the device might require after bus SUSPEND signalling. + } + + +void TTemplateAsspUsbcc::Resume() +// +// Called by the PIL after a Resume event has been reported (by us). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Resume")); + + // TO DO (optional): Implement here anything the device might require after bus RESUME signalling. + } + + +void TTemplateAsspUsbcc::Reset() +// +// Called by the PIL after a Reset event has been reported (by us). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Reset")); + + // This does not really belong here, but has to do with the way the PIL sets + // up Ep0 reads and writes. + TEndpoint* ep = &iEndpoints[0]; + ep->iRxBuf = NULL; + ++ep; + ep->iTxBuf = NULL; + // Idle + Ep0NextState(EP0_IDLE); + + // TO DO (optional): Implement here anything the device might require after bus RESET signalling. + + // Write meaningful values to some registers + InitialiseUdcRegisters(); + UdcEnable(); + if (iEp0Configured) + EnableEndpointInterrupt(0); + } + + +// --- TTemplateAsspUsbcc private -------------------------------------------------- + +void TTemplateAsspUsbcc::InitialiseUdcRegisters() +// +// Called after every USB Reset etc. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::InitialiseUdcRegisters")); + + // Unmask Suspend interrupt + // TO DO: Unmask Suspend interrupt here. + + // Unmask Resume interrupt + // TO DO: Unmask Resume interrupt here. + + // Unmask Start-of-Frame (SOF) interrupt + // TO DO (optional): Unmask SOF interrupt here. + + // Disable interrupt requests for all endpoints + // TO DO: Disable interrupt requests for all endpoints here. + } + + +void TTemplateAsspUsbcc::UdcEnable() +// +// Enables the UDC for USB transmission or reception. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::UdcEnable")); + + // TO DO: Do whatever is necessary to enable the UDC here. This might include enabling (unmasking) + // the USB Reset interrupt, setting a UDC enable bit, etc. + } + + +void TTemplateAsspUsbcc::UdcDisable() +// +// Disables the UDC. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::UdcDisable")); + + // TO DO: Do whatever is necessary to disable the UDC here. This might include disabling (masking) + // the USB Reset interrupt, clearing a UDC enable bit, etc. + } + + +void TTemplateAsspUsbcc::EnableEndpointInterrupt(TInt aEndpoint) +// +// Enables interrupt requests for an endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::EnableEndpointInterrupt(%d)", aEndpoint)); + + // Enable (unmask) interrupt requests for this endpoint: + // TO DO: Enable interrupt requests for aEndpoint here. + } + + +void TTemplateAsspUsbcc::DisableEndpointInterrupt(TInt aEndpoint) +// +// Disables interrupt requests for an endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::DisableEndpointInterrupt(%d)", aEndpoint)); + + // Disable (mask) interrupt requests for this endpoint: + // TO DO: Disable interrupt requests for aEndpoint here. + } + + +void TTemplateAsspUsbcc::ClearEndpointInterrupt(TInt aEndpoint) +// +// Clears a pending interrupt request for an endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::ClearEndpointInterrupt(%d)", aEndpoint)); + + // Clear (reset) pending interrupt request for this endpoint: + // TO DO: Clear interrupt request for aEndpoint here. + } + + +void TTemplateAsspUsbcc::Ep0IntService() +// +// ISR for endpoint zero interrupt. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Ep0IntService")); + + // TO DO: Enquire about Ep0 status & the interrupt cause here. Depending on the event and the Ep0 state, + // one or more of the following functions might then be called: + Ep0Cancel(); + Ep0ReadSetupPkt(); + Ep0EndXfer(); + Ep0PrematureStatusOut(); + Ep0Transmit(); + Ep0StatusIn(); + Ep0Receive(); + ClearStallEndpoint(0); + + ClearEndpointInterrupt(0); + return; + } + + +void TTemplateAsspUsbcc::Ep0ReadSetupPkt() +// +// Called from the Ep0 ISR when a new Setup packet has been received. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Ep0ReadSetupPkt")); + + TEndpoint* const ep = &iEndpoints[KEp0_Out]; + TUint8* buf = ep->iRxBuf; + if (!buf) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: No Ep0 Rx buffer available (1)")); + StallEndpoint(KEp0_Out); + return; + } + + // TO DO: Read Setup packet data from Rx FIFO into 'buf' here. + // (In this function we don't need to use "ep->iReceived" since Setup packets + // are always 8 bytes long.) + + // Upcall into PIL to determine next Ep0 state: + TUsbcEp0State state = EnquireEp0NextState(ep->iRxBuf); + + if (state == EEp0StateStatusIn) + { + Ep0NextState(EP0_IDLE); // Ep0 No Data + } + else if (state == EEp0StateDataIn) + { + Ep0NextState(EP0_IN_DATA_PHASE); // Ep0 Control Read + } + else + { + Ep0NextState(EP0_OUT_DATA_PHASE); // Ep0 Control Write + } + + ep->iRxBuf = NULL; + const TInt r = Ep0RequestComplete(KEp0_Out, 8, KErrNone); + + // Don't finish (proceed) if request completion returned 'KErrNotFound'! + if (!(r == KErrNone || r == KErrGeneral)) + { + DisableEndpointInterrupt(0); + } + + // TO DO (optional): Clear Ep0 Setup condition flags here. + +#ifdef USB_SUPPORTS_PREMATURE_STATUS_IN + if (iEp0State == EP0_OUT_DATA_PHASE) + { + // Allow for a premature STATUS IN + // TO DO: Arrange for the sending of a ZLP here. + } +#endif + } + + +void TTemplateAsspUsbcc::Ep0ReadSetupPktProceed() +// +// Called by the PIL to signal that it has finished processing a received Setup packet and that the PSL can +// now prepare itself for the next Ep0 reception (for instance by re-enabling the Ep0 interrupt). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Ep0ReadSetupPktProceed")); + + EnableEndpointInterrupt(0); + } + + +void TTemplateAsspUsbcc::Ep0Receive() +// +// Called from the Ep0 ISR when a data packet has been received. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Ep0Receive")); + + TEndpoint* const ep = &iEndpoints[KEp0_Out]; + TUint8* buf = ep->iRxBuf; + if (!buf) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: No Ep0 Rx buffer available (2)")); + StallEndpoint(KEp0_Out); + return; + } + + TInt n = 0; + // TO DO: Read packet data from Rx FIFO into 'buf' and update 'n' (# of received bytes) here. + + ep->iReceived = n; + ep->iRxBuf = NULL; + const TInt r = Ep0RequestComplete(KEp0_Out, n, KErrNone); + + // Don't finish (proceed) if request was 'KErrNotFound'! + if (!(r == KErrNone || r == KErrGeneral)) + { + DisableEndpointInterrupt(0); + } + + // TO DO (optional): Clear Ep0 Rx condition flags here. + +#ifdef USB_SUPPORTS_PREMATURE_STATUS_IN + // Allow for a premature STATUS IN + // TO DO: Arrange for the sending of a ZLP here. +#endif + } + + +void TTemplateAsspUsbcc::Ep0ReceiveProceed() +// +// Called by the PIL to signal that it has finished processing a received Ep0 data packet and that the PSL can +// now prepare itself for the next Ep0 reception (for instance by re-enabling the Ep0 interrupt). +// + { + Ep0ReadSetupPktProceed(); + } + + +void TTemplateAsspUsbcc::Ep0Transmit() +// +// Called from either the Ep0 ISR or the PIL when a data packet has been or is to be transmitted. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Ep0Transmit")); + + if (iEp0State != EP0_IN_DATA_PHASE) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > WARNING: Invalid Ep0 state when trying to handle EP0 IN")); + // TO DO (optional): Do something about this warning. + } + + TEndpoint* const ep = &iEndpoints[KEp0_In]; + const TUint8* buf = ep->iTxBuf; + if (!buf) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > No Tx buffer available: returning")); + return; + } + const TInt t = ep->iTransmitted; // already transmitted + buf += t; + TInt n = 0; // now transmitted + + // TO DO: Write packet data (if any) into Tx FIFO from 'buf' and update 'n' (# of tx'ed bytes) here. + + ep->iTransmitted += n; + + // coverity[dead_error_condition] + // The next line should be reachable when this template file is edited for use + if (n == KEp0MaxPktSz) + { + if (ep->iTransmitted == ep->iLength && !(ep->iZlpReqd)) + Ep0NextState(EP0_END_XFER); + } + else if (n && n != KEp0MaxPktSz) + { + // Send off the data + __ASSERT_DEBUG((ep->iTransmitted == ep->iLength), + Kern::Printf(" > ERROR: Short packet in mid-transfer")); + Ep0NextState(EP0_END_XFER); + // TO DO: Send off the data here. + } + else // if (n == 0) + { + __ASSERT_DEBUG((ep->iTransmitted == ep->iLength), + Kern::Printf(" > ERROR: Nothing transmitted but still not finished")); + if (ep->iZlpReqd) + { + // Send a zero length packet + ep->iZlpReqd = EFalse; + Ep0NextState(EP0_END_XFER); + // TO DO: Arrange for the sending of a ZLP here. + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: nothing transmitted & no ZLP req'd")); + } + } + } + + +void TTemplateAsspUsbcc::Ep0EndXfer() +// +// Called at the end of a Ep0 Control transfer. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Ep0EndXfer")); + + // TO DO (optional): Clear Ep0 Rx condition flags here. + + Ep0NextState(EP0_IDLE); + TEndpoint* const ep = &iEndpoints[KEp0_In]; + ep->iTxBuf = NULL; + (void) Ep0RequestComplete(KEp0_In, ep->iTransmitted, KErrNone); + } + + +void TTemplateAsspUsbcc::Ep0Cancel() +// +// Called when an ongoing Ep0 Control transfer has to be aborted prematurely (for instance when receiving a +// new Setup packet before the processing of the old one has completed). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Ep0Cancel")); + + Ep0NextState(EP0_IDLE); + TEndpoint* const ep = &iEndpoints[KEp0_In]; + if (ep->iTxBuf) + { + ep->iTxBuf = NULL; + const TInt err = (ep->iTransmitted == ep->iLength) ? KErrNone : KErrCancel; + (void) Ep0RequestComplete(KEp0_In, ep->iTransmitted, err); + } + } + + +void TTemplateAsspUsbcc::Ep0PrematureStatusOut() +// +// Called when an ongoing Ep0 Control transfer encounters a premature Status OUT condition. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Ep0PrematureStatusOut")); + + // TO DO (optional): Clear Ep0 Rx condition flags here. + + Ep0NextState(EP0_IDLE); + + // TO DO (optional): Flush the Ep0 Tx FIFO here, if possible. + + TEndpoint* const ep = &iEndpoints[KEp0_In]; + if (ep->iTxBuf) + { + ep->iTxBuf = NULL; + (void) Ep0RequestComplete(KEp0_In, ep->iTransmitted, KErrPrematureEnd); + } + } + + +void TTemplateAsspUsbcc::Ep0StatusIn() +// +// Called when an ongoing Ep0 Control transfer moves to a Status IN stage. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Ep0StatusIn")); + + Ep0NextState(EP0_IDLE); + } + + +void TTemplateAsspUsbcc::BulkTransmit(TInt aEndpoint) +// +// Endpoint 1 (BULK IN). +// Called from either the Ep ISR or the PIL when a data packet has been or is to be transmitted. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::BulkTransmit(%d)", aEndpoint)); + + // TO DO: Enquire about Ep status here. + + const TInt idx = 3; // only in our special case of course! + TEndpoint* const ep = &iEndpoints[idx]; + const TUint8* buf = ep->iTxBuf; + if (!buf) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: No Tx buffer has been set up")); + DisableEndpointInterrupt(aEndpoint); + ep->iDisabled = ETrue; + ClearEndpointInterrupt(aEndpoint); + return; + } + const TInt t = ep->iTransmitted; // already transmitted + const TInt len = ep->iLength; // to be sent in total + // (len || ep->iPackets): Don't complete for a zero bytes request straight away. + if (t >= len && (len || ep->iPackets)) + { + if (ep->iZlpReqd) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > 'Transmit Short Packet' explicitly")); + // TO DO: Arrange for the sending of a ZLP here. + ep->iZlpReqd = EFalse; + } + else + { + __KTRACE_OPT(KUSB, Kern::Printf(" > All data sent: %d --> completing", len)); + ep->iTxBuf = NULL; + ep->iRequest->iTxBytes = ep->iTransmitted; + ep->iRequest->iError = KErrNone; + EndpointRequestComplete(ep->iRequest); + ep->iRequest = NULL; + } + } + else + { + buf += t; + TInt left = len - t; // left in total + TInt n = (left >= KBlkMaxPktSz) ? KBlkMaxPktSz : left; // now to be transmitted + __KTRACE_OPT(KUSB, Kern::Printf(" > About to send %d bytes (%d bytes left in total)", n, left)); + + // TO DO: Write data into Tx FIFO from 'buf' here. + + ep->iTransmitted += n; + ep->iPackets++; // only used for (len == 0) case + left -= n; // (still) left in total + if (n < KBlkMaxPktSz) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > 'Transmit Short Packet' implicitly")); + // TO DO: Arrange for the sending of a ZLP here. + ep->iZlpReqd = EFalse; + } + // If double-buffering is available, it might be possible to stick a second packet + // into the FIFO here. + + // TO DO (optional): Send another packet if possible (& available) here. + } + + ClearEndpointInterrupt(aEndpoint); + } + + + +void TTemplateAsspUsbcc::BulkReceive(TInt aEndpoint) +// +// Endpoint 2 (BULK OUT) (This one is called in an ISR.) +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::BulkReceive(%d)", aEndpoint)); + + // TO DO: Enquire about Ep status here. + const TUint32 status = *(TUint32*)0xdefaced; // bogus + + const TInt idx = 4; // only in our special case of course! + TEndpoint* const ep = &iEndpoints[idx]; + TUint8* buf = ep->iRxBuf; + if (!buf) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > No Rx buffer available: setting iNoBuffer")); + ep->iNoBuffer = ETrue; + DisableEndpointInterrupt(aEndpoint); + ep->iDisabled = ETrue; + ClearEndpointInterrupt(aEndpoint); + return; + } + TInt bytes = 0; + const TInt r = ep->iReceived; // already received + // TO DO: Check whether a ZLP was received here: + if (status & 1) // some condition + { + __KTRACE_OPT(KUSB, Kern::Printf(" > received zero-length packet")); + } + else if (status & 2) // some other condition + { + // TO DO: Get number of bytes received here. + bytes = *(TUint32*)0xdadadada; // bogus + __KTRACE_OPT(KUSB, Kern::Printf(" > Bulk received: %d bytes", bytes)); + if (r + bytes > ep->iLength) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > not enough space in rx buffer: setting iNoBuffer")); + ep->iNoBuffer = ETrue; + StopRxTimer(ep); + *ep->iPacketSize = ep->iReceived; + RxComplete(ep); + + // TO DO (optional): Clear Ep Rx condition flags here. + + ClearEndpointInterrupt(aEndpoint); + return; + } + buf += r; // set buffer pointer + + // TO DO: Read 'bytes' bytes from Rx FIFO into 'buf' here. + + ep->iReceived += bytes; + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Inconsistent Ep%d state", aEndpoint)); + + // TO DO (optional): Clear Ep Rx condition flags here. + + ClearEndpointInterrupt(aEndpoint); + return; + } + + if (bytes == 0) + { + // ZLPs must be recorded separately + const TInt i = ep->iReceived ? 1 : 0; + ep->iPacketIndex[i] = r; + ep->iPacketSize[i] = 0; + // If there were data packets before: total packets reported 1 -> 2 + ep->iPackets += i; + } + + if ((bytes < KBlkMaxPktSz) || + (ep->iReceived == ep->iLength)) + { + StopRxTimer(ep); + *ep->iPacketSize = ep->iReceived; + RxComplete(ep); + // since we have no buffer any longer we disable interrupts: + DisableEndpointInterrupt(aEndpoint); + ep->iDisabled = ETrue; + } + else + { + if (!ep->iRxTimerSet) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > setting rx timer")); + ep->iRxTimerSet = ETrue; + ep->iRxTimer.OneShot(KRxTimerTimeout); + } + else + { + ep->iRxMoreDataRcvd = ETrue; + } + } + + // TO DO (optional): Clear Ep Rx condition flags here. + + ClearEndpointInterrupt(aEndpoint); + } + + +void TTemplateAsspUsbcc::BulkReadRxFifo(TInt aEndpoint) +// +// Endpoint 2 (BULK OUT) (This one is called w/o interrupt to be served.) +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::BulkReadRxFifo(%d)", aEndpoint)); + + // TO DO: Enquire about Ep status here. + const TUint32 status = *(TUint32*)0xdefaced; // bogus + + const TInt idx = 4; // only in our special case of course! + TEndpoint* const ep = &iEndpoints[idx]; + TUint8* buf = ep->iRxBuf; + if (!buf) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: No Rx buffer has been set up")); + return; + } + TInt bytes = 0; + const TInt r = ep->iReceived; // already received + // TO DO: Check whether a ZLP was received here: + if (status & 1) // some condition + { + __KTRACE_OPT(KUSB, Kern::Printf(" > received zero-length packet")); + } + else if (status & 2) // some other condition + { + // TO DO: Get number of bytes received here. + bytes = *(TUint32*)0xdadadada; // bogus + __KTRACE_OPT(KUSB, Kern::Printf(" > Bulk received: %d bytes", bytes)); + if (r + bytes > ep->iLength) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > not enough space in rx buffer: setting iNoBuffer")); + ep->iNoBuffer = ETrue; + *ep->iPacketSize = ep->iReceived; + RxComplete(ep); + return; + } + buf += r; // set buffer pointer + + // TO DO: Read 'bytes' bytes from Rx FIFO into 'buf' here. + + ep->iReceived += bytes; + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Inconsistent Ep%d state", aEndpoint)); + return; + } + + if (bytes == 0) + { + // ZLPs must be recorded separately + const TInt i = ep->iReceived ? 1 : 0; + ep->iPacketIndex[i] = r; + ep->iPacketSize[i] = 0; + // If there were data packets before: total packets reported 1 -> 2 + ep->iPackets += i; + } + + if ((bytes < KBlkMaxPktSz) || + (ep->iReceived == ep->iLength)) + { + *ep->iPacketSize = ep->iReceived; + RxComplete(ep); + } + else + { + if (!ep->iRxTimerSet) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > setting rx timer")); + ep->iRxTimerSet = ETrue; + ep->iRxTimer.OneShot(KRxTimerTimeout); + } + else + { + ep->iRxMoreDataRcvd = ETrue; + } + } + + // TO DO (optional): Clear Ep Rx condition flags here. + + } + + +void TTemplateAsspUsbcc::IsoTransmit(TInt aEndpoint) +// +// Endpoint 3 (ISOCHRONOUS IN). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::IsoTransmit(%d)", aEndpoint)); + + // TO DO: Write data to endpoint FIFO. Might be similar to BulkTransmit. + + } + + +void TTemplateAsspUsbcc::IsoReceive(TInt aEndpoint) +// +// Endpoint 4 (ISOCHRONOUS OUT) (This one is called in an ISR.) +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::IsoReceive(%d)", aEndpoint)); + + // TO DO: Read data from endpoint FIFO. Might be similar to BulkReceive. + } + + +void TTemplateAsspUsbcc::IsoReadRxFifo(TInt aEndpoint) +// +// Endpoint 4 (ISOCHRONOUS OUT) (This one is called w/o interrupt to be served.) +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::IsoReadRxFifo(%d)", aEndpoint)); + + // TO DO: Read data from endpoint FIFO. Might be similar to BulkReadRxFifo. + } + + +void TTemplateAsspUsbcc::IntTransmit(TInt aEndpoint) +// +// Endpoint 5 (INTERRUPT IN). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::IntTransmit(%d)", aEndpoint)); + + // TO DO: Write data to endpoint FIFO. Might be similar to BulkTransmit. + } + + +void TTemplateAsspUsbcc::RxComplete(TEndpoint* aEndpoint) +// +// Called at the end of an Rx (OUT) transfer to complete to the PIL. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::RxComplete")); + + TUsbcRequestCallback* const req = aEndpoint->iRequest; + + __ASSERT_DEBUG((req != NULL), Kern::Fault(KUsbPanicCat, __LINE__)); + + aEndpoint->iRxBuf = NULL; + aEndpoint->iRxTimerSet = EFalse; + aEndpoint->iRxMoreDataRcvd = EFalse; + req->iRxPackets = aEndpoint->iPackets; + req->iError = aEndpoint->iLastError; + EndpointRequestComplete(req); + aEndpoint->iRequest = NULL; + } + + +void TTemplateAsspUsbcc::StopRxTimer(TEndpoint* aEndpoint) +// +// Stops (cancels) the Rx timer for an endpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::StopRxTimer")); + + if (aEndpoint->iRxTimerSet) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > stopping rx timer")); + aEndpoint->iRxTimer.Cancel(); + aEndpoint->iRxTimerSet = EFalse; + } + } + + +void TTemplateAsspUsbcc::EndpointIntService(TInt aEndpoint) +// +// ISR for endpoint interrupts. +// Note: the aEndpoint here is a "hardware endpoint", not a aRealEndpoint. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::EndpointIntService(%d)", aEndpoint)); + + switch (aEndpoint) + { + case 0: + Ep0IntService(); + break; + case 1: + BulkTransmit(aEndpoint); + break; + case 2: + BulkReceive(aEndpoint); + break; + case 3: + IsoTransmit(aEndpoint); + break; + case 4: + IsoReceive(aEndpoint); + break; + case 5: + IntTransmit(aEndpoint); + break; + default: + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Endpoint not found")); + break; + } + } + + +TInt TTemplateAsspUsbcc::ResetIntService() +// +// ISR for a USB Reset event interrupt. +// This function returns a value which can be used on the calling end to decide how to proceed. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::ResetIntService")); + + // Clear an interrupt: + // TO DO: Clear reset interrupt flag here. + + // TO DO (optional): Enquire about special conditions and possibly return here. + + DeviceEventNotification(EUsbEventReset); + + return KErrNone; + } + + +void TTemplateAsspUsbcc::SuspendIntService() +// +// ISR for a USB Suspend event interrupt. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SuspendIntService")); + + // Clear an interrupt: + // TO DO: Clear suspend interrupt flag here. + + DeviceEventNotification(EUsbEventSuspend); + } + + +void TTemplateAsspUsbcc::ResumeIntService() +// +// ISR for a USB Resume event interrupt. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::ResumeIntService")); + + // Clear an interrupt: + // TO DO: Clear resume interrupt flag here. + + DeviceEventNotification(EUsbEventResume); + } + + +void TTemplateAsspUsbcc::SofIntService() +// +// ISR for a USB Start-of-Frame event interrupt. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SofIntService")); + + // Clear an interrupt: + // TO DO: Clear SOF interrupt flag here. + + // TO DO (optional): Do something about the SOF condition. + } + + +void TTemplateAsspUsbcc::UdcInterruptService() +// +// Main UDC ISR - determines the cause of the interrupt, clears the condition, dispatches further for service. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::InterruptService")); + + // TO DO: Find the cause of the interrupt (possibly querying a number of status registers) here. + + // Determine the type of UDC interrupt & then serve it: + // (The following operations are of course EXAMPLES only.) + volatile const TUint32* const status_reg = (TUint32*) 0xdefaced; + const TUint32 status = *status_reg; + enum {reset_interrupt, suspend_interrupt, resume_interrupt, sof_interrupt, ep_interrupt}; + + // Reset interrupt + if (status & reset_interrupt) + { + ResetIntService(); + } + + // Suspend interrupt + if (status & suspend_interrupt) + { + SuspendIntService(); + } + + // Resume interrupt + if (status & resume_interrupt) + { + ResumeIntService(); + } + + // Start-of-Frame interrupt + if (status & sof_interrupt) + { + SofIntService(); + } + + // Endpoint interrupt + if (status & ep_interrupt) + { + const TInt ep = status & 0xffff0000; + { + EndpointIntService(ep); + } + } + } + + +void TTemplateAsspUsbcc::Ep0NextState(TEp0State aNextState) +// +// Moves the Ep0 state to aNextState. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::Ep0NextState")); + + iEp0State = aNextState; + } + + +void TTemplateAsspUsbcc::UdcIsr(TAny* aPtr) +// +// This is the static ASSP first-level UDC interrupt service routine. It dispatches the call to the +// actual controller's ISR. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::UdcIsr")); + + static_cast(aPtr)->UdcInterruptService(); + } + + +TInt TTemplateAsspUsbcc::UsbClientConnectorCallback(TAny* aPtr) +// +// This function is called in ISR context by the Variant's UsbClientConnectorInterruptService. +// (This function is static.) +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::UsbClientConnectorCallback")); + + TTemplateAsspUsbcc* const ptr = static_cast(aPtr); + ptr->iCableConnected = ptr->iAssp->UsbClientConnectorInserted(); +#ifdef _DEBUG + _LIT(KIns, "inserted"); + _LIT(KRem, "removed"); + __KTRACE_OPT(KUSB, Kern::Printf(" > USB cable now %lS", ptr->iCableConnected ? &KIns : &KRem)); +#endif + if (ptr->iCableConnected) + { + ptr->DeviceEventNotification(EUsbEventCableInserted); + } + else + { + ptr->DeviceEventNotification(EUsbEventCableRemoved); + } + + return KErrNone; + } + + +TInt TTemplateAsspUsbcc::SetupUdcInterrupt() +// +// Registers and enables the UDC interrupt (ASSP first level interrupt). +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::SetupUdcInterrupt")); + + // Register UDC interrupt: + const TInt error = Interrupt::Bind(EAsspIntIdUsb, UdcIsr, this); + if (error != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Binding UDC interrupt failed")); + return error; + } + + // Enable UDC interrupt: + Interrupt::Enable(EAsspIntIdUsb); + + return KErrNone; + } + + +void TTemplateAsspUsbcc::ReleaseUdcInterrupt() +// +// Disables and unbinds the UDC interrupt. +// + { + __KTRACE_OPT(KUSB, Kern::Printf("TTemplateAsspUsbcc::ReleaseUdcInterrupt")); + + // Disable UDC interrupt: + Interrupt::Disable(EAsspIntIdUsb); + + // Unregister UDC interrupt: + Interrupt::Unbind(EAsspIntIdUsb); + } + + +// +// --- DLL Exported Function -------------------------------------------------- +// + +DECLARE_STANDARD_EXTENSION() +// +// Creates and initializes a new USB client controller object on the kernel heap. +// + { + __KTRACE_OPT(KUSB, Kern::Printf(" > Initializing USB client support (Udcc)...")); + + TTemplateAsspUsbcc* const usbcc = new TTemplateAsspUsbcc(); + if (!usbcc) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for TTemplateAsspUsbcc failed")); + return KErrNoMemory; + } + + TInt r; + if ((r = usbcc->Construct()) != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Construction of TTemplateAsspUsbcc failed (%d)", r)); + delete usbcc; + return r; + } + + if (usbcc->RegisterUdc(0) == NULL) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: PIL registration of PSL failed")); + delete usbcc; + return KErrGeneral; + } + + __KTRACE_OPT(KUSB, Kern::Printf(" > Initializing USB client support: Done")); + + return KErrNone; + } + + +// --- EOF --------------------------------------------------------------------