diff -r 59aa7d6e3e0f -r 089413cdde3c usbdrv/peripheral/pdd/pil/src/ps_usbc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbdrv/peripheral/pdd/pil/src/ps_usbc.cpp Fri Jul 23 15:54:47 2010 +0800 @@ -0,0 +1,4832 @@ +// 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/ps_usbc.cpp +// Platform independent layer (PIL) of the USB Device controller driver (PDD). +// Interface to the USB LDD. +// +// + +/** + @file ps_usbc.cpp + @internalTechnology +*/ +//#include +#include + +#include "controltransfersm.h" +/** + TUsbcInterfaceSet and TUsbcInterface + ==================================== + + TUsbcInterfaceSet represents a 'USB Interface' and TUsbcInterface + represents an 'Alternate Setting of a USB Interface'. + + Since every LDD governs exactly one interface, the above distinction is + made only within the USB implementation. At the LDD API, there is/are + simply one or more settings for this single interface, numbered from '0' + (the default) to 'n', and specified by the parameter 'TInt aInterfaceNum'. + + Within the PDD implementation, for a TUsbcInterfaceSet number the parameter + 'TInt aIfcSet' is used (local variable ifcset); for a TUsbcInterface number + the parameter 'TInt aIfc' is used (local variable ifc). + + + iConfigs[0] and CurrentConfig() + =============================== + + One problem with this file is that it always uses iConfigs[0] and not + CurrentConfig(). This is mainly because the API to the LDD doesn't know + about the concept of multiple configurations, and thus always assumes one + single configuration (which is also always active: a further problem). + + In the file chapter9.cpp this issue doesn't exist, since there we always + have to obey the USB protocol, and in this way will use the configuration + which is selected by the host (which will then again currently always be + iConfigs[0].) + + iEp0ClientId + ================================== + + The purpose of these two members of class DUsbClientController is the + following. + + They are used only during Ep0 control transactions which have an OUT (Rx) + data stage. We cannot deduce from the received data itself to whom + it is addressed (that's because of the shared nature of Ep0). + + In order to be able to tell whether received Ep0 data is to be processed by + the PIL or a LDD, we use iEp0ClientId. iEp0ClientId is usually NULL, which + means it is our data. However it is set to the client ID of an LDD in case + 2) above. That way we can subsequently hand over received data to the + correct client LDD. + + iEp0DataReceived tracks the amount of data already received - it is used to + determine the end of the DATA_OUT phase, irrespective of the owner of the + data. The total amount that is to be received can be obtained via + iConTransferMgr->PktParser().Length(). (iConTransferMgr->PktParser() holds in + that case the Setup packet of the current Control transfer.) + + iEp0ClientDataTransmitting is only set to TRUE if a client sets up an Ep0 + write. After that transmission has completed we use this value to decide + whether we have to report the completion to a client or not. (If this + variable is FALSE, we did set up the write and thus no client notification + is necessary.) + +*/ + +// +// === Global and Local Variables ================================================================== +// + +// Currently we support at most 2 peripheral stack +GLDEF_D DUsbClientController* DUsbClientController::UsbClientController[] = {NULL, NULL}; + +static const TInt KUsbReconnectDelay = 500; // milliseconds + +// Charger detector is the guy(PSL) who can detect the charger type and report +// it via a charger type observer +static UsbShai::MChargerDetectorIf* gChargerDetector = NULL; + +// Charger observer is the guy who want to monitor the chager type event. +static UsbShai::MChargerDetectorObserverIf* gChargerObsever = NULL; + + +// Those const variables are used to construct the default +// Usb descriptors, Upper layer can change them later. +/** Default vendor ID to set in device descriptor */ +static const TUint16 KUsbVendorId = KUsbVendorId_Symbian; +/** Default product ID to set in device descriptor */ +static const TUint16 KUsbProductId = 0x1111; +/** Default language ID (US English) to set in device descriptor */ +static const TUint16 KUsbLangId = 0x0409; +static const TUint8 KUsbNumberOfConfiguration = 0x01; +/** Default manufacturer string */ +static const wchar_t KStringManufacturer[] = L"Nokia Corporation"; +/** Default product name string */ +static const wchar_t KStringProduct[] = L"Nokia USB Driver"; +/** Default configuration name string */ +static const wchar_t KStringConfig[] = L"Bulk transfer method configuration"; +/** Default configuration name string */ +static const wchar_t KStringSerial[] = L"Serial Not defined"; + + +// +// === USB Controller member function implementations - LDD API (public) =========================== +// + +DECLARE_STANDARD_EXTENSION()//lint !e1717 !e960 defined by symbian + { + __KTRACE_OPT(KUSB, Kern::Printf("> Peripheral PIL Extension entry")); + // Don't need to do anything here, using extension just to make sure + // we're loaded when peripheral PSL trying to register itself to us. + __KTRACE_OPT(KUSB, Kern::Printf("< Peripheral PIL Extension exit")); + return KErrNone; + } + +/** The class destructor. + + This rarely gets called, except, for example when something goes + wrong during construction. + + It's not exported because it is virtual. +*/ +DUsbClientController::~DUsbClientController() + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::~DUsbClientController()")); + if (iPowerHandler) + { + iPowerHandler->Remove(); + delete iPowerHandler; + } + + // ResetAndDestroy() will call for every array element the destructor of the pointed-to object, + // before deleting the element itself, and closing the array. + iConfigs.ResetAndDestroy(); + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::~DUsbClientController(): Done.")); + } + +EXPORT_C DUsbClientController* DUsbClientController::Create(UsbShai::MPeripheralControllerIf& aPeripheralControllerIf, + const UsbShai::TPeripheralControllerProperties& aProperties, + TBool aIsOtgPort) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::Create")); + // Attempt to create the object + DUsbClientController* usbcc = new DUsbClientController(aPeripheralControllerIf, + aProperties, + aIsOtgPort); + + __ASSERT_DEBUG( (usbcc != NULL), Kern::Fault( " USB PSL Out of memory, DUsbClientController", __LINE__ ) ); + + if (usbcc != NULL) + { + // Second phase constructor + TInt err = usbcc->Construct(); + __ASSERT_DEBUG( (err == KErrNone), Kern::Fault( "DUsbClientController::Construct failed", err) ); + + if (err != KErrNone) + { + delete usbcc; + usbcc = NULL; + } + } + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::Create instance = %d",usbcc)); + + return usbcc; + } + +/** + * FIXME: This function used to be called by the dummy DCD to disable + * the peripheral stack. It has been deprecated and we currently use + * DisablePeripheralStack() to achieve the same effect. + */ +EXPORT_C void DUsbClientController::DisableClientStack() + { + __KTRACE_OPT(KUSB, Kern::Printf("CALL TO OBSOLETE FUNCTION: DUsbClientController::DisableClientStack()")); + } + + +/** + * FIXME: This function used to be called by the dummy DCD to enable + * the peripheral stack. It has been deprecated and we currently use + * EnablePeripheralStack() to achieve the same effect. + */ +EXPORT_C void DUsbClientController::EnableClientStack() + { + __KTRACE_OPT(KUSB, Kern::Printf("CALL TO OBSOLETE FUNCTION: DUsbClientController::EnableClientStack()")); + } + + +/** Called by LDD to see if controller is usable. + + @return ETrue if controller is in normal state, EFalse if it is disabled. +*/ +EXPORT_C TBool DUsbClientController::IsActive() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::IsActive = %d",iStackIsActive)); + return iStackIsActive; + } + + +/** Called by LDD to register client callbacks. + + @return KErrNone if successful, KErrAlreadyExists callback exists. +*/ +EXPORT_C TInt DUsbClientController::RegisterClientCallback(TUsbcClientCallback& aCallback) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::RegisterClientCallback()")); + if (iClientCallbacks.Elements() == KUsbcMaxListLength) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Maximum list length reached: %d", + KUsbcMaxListLength)); + return KErrGeneral; + } + TSglQueIter iter(iClientCallbacks); + TUsbcClientCallback* p; + while ((p = iter++) != NULL) + if (p == &aCallback) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Error: ClientCallback @ 0x%x already registered", &aCallback)); + return KErrAlreadyExists; + } + iClientCallbacks.AddLast(aCallback); + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::RegisterClientCallback()")); + return KErrNone; + } + + +/** Returns a pointer to the USB client controller object. + + This function is static. + + @param aUdc The number of the UDC (0..n) for which the pointer is to be returned. + + @return A pointer to the USB client controller object. +*/ +EXPORT_C DUsbClientController* DUsbClientController::UsbcControllerPointer(TInt aUdc) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::UsbcControllerPointer()")); + + if (aUdc < 0 || aUdc > 1) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: aUdc out of range (%d)", aUdc)); + return NULL; + } + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::UsbcControllerPointer()")); + + return UsbClientController[aUdc]; + } + + +/** Fills the buffer passed in as an argument with endpoint capability information. + + @see DUsbClientController::DeviceCaps() + @see TUsbcEndpointData + @see TUsbDeviceCaps + + @param aClientId A pointer to the LDD making the enquiry. + @param aCapsBuf A reference to a descriptor buffer, which, on return, contains an array of + TUsbcEndpointData elements; there are TUsbDeviceCaps::iTotalEndpoints elements in the array; + call DeviceCaps() to get the number of elements required. +*/ +EXPORT_C void DUsbClientController::EndpointCaps(const DBase* aClientId, TDes8& aCapsBuf) const + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::EndpointCaps()")); + // Here we do not simply call DUsbClientController::DeviceEndpointCaps(), + // because that function fills an array which comprises of _all_ endpoints, + // whereas this function omits ep0 and all unusable endpoints. + // Apart from that, we have to fill an array of TUsbcEndpointData, not TUsbcEndpointCaps. + TUsbcEndpointData data[KUsbcMaxEndpoints]; + const TInt ifcset_num = ClientId2InterfaceNumber(aClientId); + for (TInt i = 2, j = 0; i < iDeviceTotalEndpoints; ++i) + { + __KTRACE_OPT(KUSB, Kern::Printf(" DUsbClientController::Caps: RealEndpoint #%d", i)); + if (iRealEndpoints[i].iCaps.iTypesAndDir != UsbShai::KUsbEpNotAvailable) + { + __KTRACE_OPT(KUSB, Kern::Printf(" DUsbClientController::Caps: --> UsableEndpoint #%d", j)); + + data[j].iCaps.iSizes = iRealEndpoints[i].iCaps.iSizes; + data[j].iCaps.iTypesAndDir = iRealEndpoints[i].iCaps.iTypesAndDir; + data[j].iCaps.iHighBandwidth = iRealEndpoints[i].iCaps.iHighBandwidth; + data[j].iCaps.iReserved[0] = iRealEndpoints[i].iCaps.iReserved[0]; + data[j].iCaps.iReserved[1] = iRealEndpoints[i].iCaps.iReserved[1]; + + if (ifcset_num < 0) + { + // If this LDD doesn't own an interface, but the Ep points to one, + // then that must be the interface of a different LDD. Hence the Ep + // is not available for this LDD. + data[j].iInUse = (iRealEndpoints[i].iIfcNumber != NULL); + } + else + { + // If this LDD does already own an interface, and the Ep also points to one, + // then the Ep is not available for this LDD only if that interface is owned + // by a different LDD (i.e. if the interface number is different). + // Reason: Even though the endpoint might already be part of an interface setting, + // it is still available for a different alternate setting of the same interface. + data[j].iInUse = ((iRealEndpoints[i].iIfcNumber != NULL) && + (*(iRealEndpoints[i].iIfcNumber) != ifcset_num)); + } + + j++; + } + } + // aCapsBuf resides in userland + TPtrC8 des((TUint8*)data, sizeof(data)); + const TInt r = Kern::ThreadDesWrite((reinterpret_cast(aClientId))->Client(), + &aCapsBuf, des, 0, KChunkShiftBy0, NULL); + if (r != KErrNone) + { + Kern::ThreadKill((reinterpret_cast(aClientId))->Client(), + EExitPanic, r, KUsbPILKillCat); + } + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::EndpointCaps()")); + } + + +/** Fills the buffer passed in as an argument with device capability information. + + @see TUsbDeviceCaps + @see TUsbDeviceCapsV01 + + @param aClientId A pointer to the LDD making the enquiry. + @param aCapsBuf A reference to a descriptor buffer which, on return, contains + a TUsbDeviceCaps structure. +*/ +EXPORT_C void DUsbClientController::DeviceCaps(const DBase* aClientId, TDes8& aCapsBuf) const + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::DeviceCaps()")); + TUsbDeviceCaps caps; + caps().iTotalEndpoints = iDeviceUsableEndpoints; // not DeviceTotalEndpoints()! + + caps().iSelfPowered = iSelfPowered; + caps().iRemoteWakeup = iRemoteWakeup; + caps().iHighSpeed = (iControllerProperties.iControllerCaps & UsbShai::KDevCapHighSpeed)?ETrue:EFalse; + + // PIL always assume controller support this caps, see explaination in peripheral shai header + caps().iFeatureWord1 = caps().iFeatureWord1 | KUsbDevCapsFeatureWord1_CableDetectWithoutPower; + + caps().iFeatureWord1 = caps().iFeatureWord1 | KUsbDevCapsFeatureWord1_EndpointResourceAllocV2; + caps().iReserved = 0; + + // aCapsBuf resides in userland + const TInt r = Kern::ThreadDesWrite((reinterpret_cast(aClientId))->Client(), + &aCapsBuf, caps, 0, KChunkShiftBy0, NULL); + if (r != KErrNone) + { + Kern::ThreadKill((reinterpret_cast(aClientId))->Client(), + EExitPanic, r, KUsbPILKillCat); + } + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::DeviceCaps()")); + } + + +TUsbcEndpointInfoArray::TUsbcEndpointInfoArray(const TUsbcEndpointInfo* aData, TInt aDataSize) + { + iType = EUsbcEndpointInfo; + iData = (TUint8*) aData; + if (aDataSize > 0) + iDataSize = aDataSize; + else + iDataSize = sizeof(TUsbcEndpointInfo); + } + + +inline TUsbcEndpointInfo& TUsbcEndpointInfoArray::operator[](TInt aIndex) const + { + return *(TUsbcEndpointInfo*) &iData[aIndex * iDataSize]; + } + + +EXPORT_C TInt DUsbClientController::SetInterface(const DBase* aClientId, DThread* aThread, + TInt aInterfaceNum, TUsbcClassInfo& aClass, + TDesC8* aString, TInt aTotalEndpointsUsed, + const TUsbcEndpointInfo aEndpointData[], + TInt (*aRealEpNumbers)[6], TUint32 aFeatureWord) + { + TUsbcEndpointInfoArray endpointData = TUsbcEndpointInfoArray(aEndpointData); + return SetInterface(aClientId, aThread, aInterfaceNum, aClass, aString, aTotalEndpointsUsed, + endpointData, (TInt*) aRealEpNumbers, aFeatureWord); + } + + +/** Creates a new USB interface (one setting), complete with endpoints, descriptors, etc., + and chains it into the internal device configuration tree. + + @param aClientId A pointer to the LDD owning the new interface. + @param aThread A pointer to the thread the owning LDD is running in. + @param aInterfaceNum The interface setting number of the new interface setting. This must be 0 + if it is the first setting of the interface that gets created, or 1 more than the last setting + that was created for this interface. + @param aClass Contains information about the device class this interface might belong to. + @param aString A pointer to a string that is used for the string descriptor of this interface. + @param aTotalEndpointsUsed The number of endpoints used by this interface (and also the number of + elements of the following array). + @param aEndpointData An array with aTotalEndpointsUsed elements, containing information about the + endpoints of this interface. + + @return KErrNotSupported if Control endpoints are requested by the LDD but aren't supported by the PIL, + KErrInUse if at least one requested endpoint is - temporarily or permanently - not available for use, + KErrNoMemory if (endpoint, interface, string) descriptor allocation fails, KErrGeneral if something else + goes wrong during endpoint or interface or descriptor creation, KErrNone if interface successfully set up. +*/ +EXPORT_C TInt DUsbClientController::SetInterface(const DBase* aClientId, DThread* aThread, + TInt aInterfaceNum, TUsbcClassInfo& aClass, + TDesC8* aString, TInt aTotalEndpointsUsed, + const TUsbcEndpointInfoArray aEndpointData, + TInt aRealEpNumbers[], TUint32 aFeatureWord) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::SetInterface()")); + if (aInterfaceNum != 0) + { + __KTRACE_OPT(KUSB, Kern::Printf(" alternate interface setting request: #%d", aInterfaceNum)); + } + +#ifndef USB_SUPPORTS_CONTROLENDPOINTS + for (TInt i = 0; i < aTotalEndpointsUsed; ++i) + { + if (aEndpointData[i].iType == UsbShai::KUsbEpTypeControl) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: control endpoints not supported")); + return KErrNotSupported; + } + } +#endif + + // Check for endpoint availability & check those endpoint's capabilities + const TInt ifcset_num = ClientId2InterfaceNumber(aClientId); + + // The passed-in ifcset_num may be -1 now, but that's intended. + if (!CheckEpAvailability(aTotalEndpointsUsed, aEndpointData, ifcset_num)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: endpoints not (all) available")); + return KErrInUse; + } + + // Create & setup new interface + TUsbcInterface* ifc = CreateInterface(aClientId, aInterfaceNum, aFeatureWord); + if (ifc == NULL) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: ifc == NULL")); + return KErrGeneral; + } + + // Create logical endpoints + TInt r = CreateEndpoints(ifc, aTotalEndpointsUsed, aEndpointData, aRealEpNumbers); + if (r != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: CreateEndpoints() err = %d ",r)); + DeleteInterface(ifc->iInterfaceSet->iInterfaceNumber, aInterfaceNum); + return r; + } + + // Create & setup interface, string, and endpoint descriptors + r = SetupIfcDescriptor(ifc, aClass, aThread, aString, aEndpointData); + if (r != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: SetupIfcDescriptor() err = %d",r)); + return r; + } + + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::SetInterface()")); + return KErrNone; + } + + +/** Releases an existing USB interface (one setting), complete with endpoints, descriptors, etc., + and removes it from the internal device configuration tree. + + @param aClientId A pointer to the LDD owning the interface. + @param aInterfaceNum The setting number of the interface setting to be deleted. This must be + the highest numbered (or 'last') setting for this interface. + + @return KErrNotFound if interface (not setting) for some reason cannot be found, KErrArgument if an + invalid interface setting number is specified (not existing or existing but too small), KErrNone if + interface successfully released or if this client doesn't own any interface. +*/ +EXPORT_C TInt DUsbClientController::ReleaseInterface(const DBase* aClientId, TInt aInterfaceNum) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::ReleaseInterface(..., %d)", aInterfaceNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KUSB, Kern::Printf(" interface not found")); // no error + return KErrNone; + } + TUsbcInterfaceSet* const ifcset_ptr = InterfaceNumber2InterfacePointer(ifcset); + if (!ifcset_ptr) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Error: interface number %d doesn't exist", ifcset)); + return KErrNotFound; + } + const TInt setting_count = ifcset_ptr->iInterfaces.Count(); + if ((setting_count - 1) != aInterfaceNum) + { + __KTRACE_OPT(KUSB, + Kern::Printf(" Error: interface settings must be released in descending order:\n\r" + " %d setting(s) exist, #%d was requested to be released.\n\r" + " (#%d has to be released first)", + setting_count, aInterfaceNum, setting_count - 1)); + return KErrArgument; + } + // Tear down current setting (invalidate configured state) + __KTRACE_OPT(KUSB, Kern::Printf(" tearing down InterfaceSet %d", ifcset)); + // Cancel all transfers on the current setting of this interface and deconfigure all its endpoints. + InterfaceSetTeardown(ifcset_ptr); + // 'Setting 0' means: delete all existing settings. + if (aInterfaceNum == 0) + { + TInt m = ifcset_ptr->iInterfaces.Count(); + while (m > 0) + { + m--; + // Ground the physical endpoints' logical_endpoint_pointers + const TInt n = ifcset_ptr->iInterfaces[m]->iEndpoints.Count(); + for (TInt i = 0; i < n; ++i) + { + TUsbcPhysicalEndpoint* ptr = const_cast + (ifcset_ptr->iInterfaces[m]->iEndpoints[i]->iPEndpoint); + ptr->iLEndpoint = NULL; + } + // Delete the setting itself + its ifc & ep descriptors + DeleteInterface(ifcset, m); + iDescriptors.DeleteIfcDescriptor(ifcset, m); + } + } + else + { + // Ground the physical endpoints' logical_endpoint_pointers + const TInt n = ifcset_ptr->iInterfaces[aInterfaceNum]->iEndpoints.Count(); + for (TInt i = 0; i < n; ++i) + { + TUsbcPhysicalEndpoint* ptr = const_cast + (ifcset_ptr->iInterfaces[aInterfaceNum]->iEndpoints[i]->iPEndpoint); + ptr->iLEndpoint = NULL; + } + // Delete the setting itself + its ifc & ep descriptors + DeleteInterface(ifcset, aInterfaceNum); + iDescriptors.DeleteIfcDescriptor(ifcset, aInterfaceNum); + } + // Delete the whole interface if all settings are gone + if (ifcset_ptr->iInterfaces.Count() == 0) + { + DeleteInterfaceSet(ifcset); + } + // We now no longer have a valid current configuration + iCurrentConfig = 0; + if (iDeviceState == UsbShai::EUsbPeripheralStateConfigured) + { + NextDeviceState(UsbShai::EUsbPeripheralStateAddress); + } + // If it was the last interface(set)... + if (iConfigs[0]->iInterfaceSets.Count() == 0) + { + __KTRACE_OPT(KUSB, Kern::Printf(" No ifc left -> turning off UDC")); + // First disconnect the device from the bus + UsbDisconnect(); + DeActivateHardwareController(); + // (this also disables endpoint zero; we cannot have a USB device w/o interface, see 9.6.3) + } + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::ReleaseInterface")); + return KErrNone; + } + + +/** Enforces a USB re-enumeration by disconnecting the UDC from the bus (if it is currently connected) and + re-connecting it. + + This only works if the PSL supports it, i.e. if SoftConnectCaps() returns ETrue. +*/ +EXPORT_C TInt DUsbClientController::ReEnumerate() + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::ReEnumerate()")); + + // ReEnumerate is possible only when stack is enabled + if (!iStackIsActive) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Client stack disabled -> returning here")); + return KErrNotReady; + } + + // If no interfaces setup, there is no point to reenumerate + if (iConfigs[0]->iInterfaceSets.Count() == 0) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > No interface registered -> no need to re-enumerate")); + return KErrNone;; + } + + if (!iHardwareActivated) + { + // If the UDC is still off, we switch it on here. + const TInt r = ActivateHardwareController(); + if (r != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: ActivateHardwareController() failed: %d", r)); + return r; + } + + // Finally connect the device to the bus + UsbConnect(); + } + else + { + UsbDisconnect(); + + // Now we have to wait a certain amount of time, in order to give the host the opportunity + // to come to terms with the new situation. + // (The ETrue parameter makes the callback get called in DFC instead of in ISR context.) + iReconnectTimer.OneShot(KUsbReconnectDelay, ETrue); + } + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::ReEnumerate()")); + return KErrNone;; + } + + +/** Powers up the UDC if one or more interfaces exist. + + @return KErrNone if UDC successfully powered up, KErrNotReady if no + interfaces have been registered yet, KErrHardwareNotAvailable if UDC + couldn't be activated. +*/ +EXPORT_C TInt DUsbClientController::PowerUpUdc() + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::PowerUpUdc()")); + + // we need to check whether Stack is activate or not(can be done by otg sw in otg setup + // or by vBus risen/fallen event in a non-otg env) + if (!iStackIsActive) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Client stack disabled -> returning here")); + return KErrNotReady; + } + + if (iConfigs[0]->iInterfaceSets.Count() == 0) + { + __KTRACE_OPT(KUSB, Kern::Printf(" No interface registered -> won't power up UDC")); + return KErrNotReady; + } + + // If the UDC is still off, we switch it on here. + const TInt r = ActivateHardwareController(); + if (r != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: ActivateHardwareController() failed: %d", r)); + } + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::PowerUpUdc() returns %d",r)); + return r; + } + + +/** Connects the UDC to the bus. + + @return KErrNone if UDC successfully connected, + KErrNotSupported if KDevCapSoftConnect not supported + KErrGeneral if there was an error. + +*/ +EXPORT_C TInt DUsbClientController::UsbConnect() + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::UsbConnect()")); + iClientSupportReady = ETrue; + + // If a deferred reset is pending, service it now + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + if (iUsbResetDeferred) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Resetting USB Reset 'defer' flag")); + iUsbResetDeferred = EFalse; + (void) ProcessResetEvent(EFalse); + } + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + + // Indicate readiness to connect to the PSL + const TInt r = iController.PeripheralConnect(); + + // Check whether Stack is activated by OTG controller + // or Vbus Risen had been detected. + // If either of them is true and HW is not activated yet, do it here. + if (iStackIsActive && !iHardwareActivated ) + { + // PowerUpUdc only do Activating Hardware when there are at least 1 + // Iterface registered + PowerUpUdc(); + } + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::UsbConnect()")); + return r; + } + + +/** Disconnects the UDC from the bus. + + This only works if the PSL supports it, i.e. if SoftConnectCaps() returns ETrue. + + @return KErrNone if UDC successfully disconnected, KErrGeneral if there was an error. +*/ +EXPORT_C TInt DUsbClientController::UsbDisconnect() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::UsbDisconnect()")); + iClientSupportReady = EFalse; + + // Indicate to PSL that we are no longer ready to connect + const TInt r = iController.PeripheralDisconnect(); + + // There won't be any notification by the PSL about this, + // so we have to notify the LDD/user ourselves: + if ((r == KErrNone) && (iDeviceState != UsbShai::EUsbPeripheralStateUndefined)) + { + // Not being in state UNDEFINED implies that the cable is inserted. + if (iHardwareActivated) + { + NextDeviceState(UsbShai::EUsbPeripheralStatePowered); + } + // (If the hardware is NOT activated at this point, we can only be in + // state UsbShai::EUsbPeripheralStateAttached, so we don't have to move to it.) + } + return r; + } + + +/** Registers a notification callback for changes of the USB device state. + + In the event of a device state change, the callback's state member gets updated (using SetState) with a + new UsbShai::TUsbPeripheralState value, and then the callback is executed (DoCallback). 'USB device state' here refers + to the Visible Device States as defined in chapter 9 of the USB specification. + + @param aCallback A reference to a properly filled in status callback structure. + + @return KErrNone if callback successfully registered, KErrGeneral if this callback is already registered + (it won't be registered twice). +*/ +EXPORT_C TInt DUsbClientController::RegisterForStatusChange(TUsbcStatusCallback& aCallback) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::RegisterForStatusChange()")); + if (iStatusCallbacks.Elements() == KUsbcMaxListLength) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Maximum list length reached: %d", + KUsbcMaxListLength)); + return KErrGeneral; + } + if (IsInTheStatusList(aCallback)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Error: StatusCallback @ 0x%x already registered", &aCallback)); + return KErrGeneral; + } + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + iStatusCallbacks.AddLast(aCallback); + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + return KErrNone; + } + + +/** De-registers (removes from the list of pending requests) a notification callback for the USB device + status. + + @param aClientId A pointer to the LDD owning the status change callback. + + @return KErrNone if callback successfully unregistered, KErrNotFound if the callback couldn't be found. +*/ +EXPORT_C TInt DUsbClientController::DeRegisterForStatusChange(const DBase* aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeRegisterForStatusChange()")); + __ASSERT_DEBUG((aClientId != NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + TSglQueIter iter(iStatusCallbacks); + TUsbcStatusCallback* p; + while ((p = iter++) != NULL) + { + if (p->Owner() == aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf(" removing StatusCallback @ 0x%x", p)); + iStatusCallbacks.Remove(*p); + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + return KErrNone; + } + } + __KTRACE_OPT(KUSB, Kern::Printf(" client not found")); + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + return KErrNotFound; + } + + +/** Registers a notification callback for changes of the state of endpoints. + + In the event of a state change of an endpoint that is spart of an interface which is owned by the LDD + specified in the callback structure, the callback's state member gets updated (using SetState) with a new + value, and the callback is executed (DoCallback). 'Endpoint state' here refers to the state of the + ENDPOINT_HALT feature of an endpoint as described in chapter 9 of the USB specification. The contents of + the state variable reflects the state of the halt features for all endpoints of the current interface + setting: bit 0 represents endpoint 1, bit 1 endpoint 2, etc. A set bit means 'endpoint halted', a cleared + bit 'endpoint not halted'. + + @param aCallback A reference to a properly filled in endpoint status callback structure. + + @return KErrNone if callback successfully registered, KErrGeneral if this callback is already registered + (it won't be registered twice). +*/ +EXPORT_C TInt DUsbClientController::RegisterForEndpointStatusChange(TUsbcEndpointStatusCallback& aCallback) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::RegisterForEndpointStatusChange()")); + if (iEpStatusCallbacks.Elements() == KUsbcMaxListLength) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Maximum list length reached: %d", + KUsbcMaxListLength)); + return KErrGeneral; + } + if (IsInTheEpStatusList(aCallback)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Error: EpStatusCallback @ 0x%x already registered", &aCallback)); + return KErrGeneral; + } + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + iEpStatusCallbacks.AddLast(aCallback); + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + return KErrNone; + } + + +/** De-registers (removes from the list of pending requests) a notification callback for changes of the state + of endpoints. + + @param aClientId A pointer to the LDD owning the endpoint status change callback. + + @return KErrNone if callback successfully unregistered, KErrNotFound if the callback couldn't be found. +*/ +EXPORT_C TInt DUsbClientController::DeRegisterForEndpointStatusChange(const DBase* aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeRegisterForEndpointStatusChange()")); + __ASSERT_DEBUG((aClientId != NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + TSglQueIter iter(iEpStatusCallbacks); + TUsbcEndpointStatusCallback* p; + while ((p = iter++) != NULL) + { + if (p->Owner() == aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf(" removing EpStatusCallback @ 0x%x", p)); + iEpStatusCallbacks.Remove(*p); + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + return KErrNone; + } + } + __KTRACE_OPT(KUSB, Kern::Printf(" client not found")); + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + return KErrNotFound; + } + + +/** Returns the number of the currently active alternate interface setting for this interface. + + @param aClientId A pointer to the LDD owning the interface. + @param aInterfaceNum Here the interface gets written to. + + @return KErrNotFound if an interface for this client couldn't be found, KErrNone if setting value was + successfully written. +*/ +EXPORT_C TInt DUsbClientController::GetInterfaceNumber(const DBase* aClientId, TInt& aInterfaceNum) const + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetInterfaceNumber()")); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error (ifc < 0)")); + return KErrNotFound; + } + const TUsbcInterfaceSet* const ifcset_ptr = InterfaceNumber2InterfacePointer(ifcset); + if (!ifcset_ptr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: interface number %d doesn't exist", ifcset)); + return KErrNotFound; + } + aInterfaceNum = ifcset_ptr->iCurrentInterface; + return KErrNone; + } + + +/** This is normally called once by an LDD's destructor, either after a Close() on the user side, + or during general cleanup. + + It might also be called by the LDD when some internal unrecoverable error occurs. + + This function + - de-registers a possibly pending device state change notification request, + - de-registers a possibly pending endpoint state change notification request, + - releases all interfaces + settings owned by this LDD, + - cancels all remaining (if any) read/write requests. + + @param aClientId A pointer to the LDD to be unregistered. + + @return KErrNone. +*/ +EXPORT_C TInt DUsbClientController::DeRegisterClient(const DBase* aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeRegisterClient(0x%x)", aClientId)); + // Cancel all device state notification requests + DeRegisterForStatusChange(aClientId); + // Cancel all endpoint state notification requests + DeRegisterForEndpointStatusChange(aClientId); + DeRegisterForOtgFeatureChange(aClientId); + DeRegisterClientCallback(aClientId); + // Delete the interface including all its alternate settings which might exist. + // (If we release the default setting (0), all alternate settings are deleted as well.) + const TInt r = ReleaseInterface(aClientId, 0); + // Cancel all remaining (if any) read/write requests + DeleteRequestCallbacks(aClientId); + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeRegisterClient: Done.")); + return r; + } + + +/** Returns the currently used Ep0 max packet size. + + @return The currently used Ep0 max packet size. +*/ +EXPORT_C TInt DUsbClientController::Ep0PacketSize() const + { + const TUsbcLogicalEndpoint* const ep = iRealEndpoints[0].iLEndpoint; + if (iHighSpeed) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Ep0 size = %d (HS)", ep->iEpSize_Hs)); + return ep->iEpSize_Hs; + } + else + { + __KTRACE_OPT(KUSB, Kern::Printf(" Ep0 size = %d (FS)", ep->iEpSize_Fs)); + return ep->iEpSize_Fs; + } + } + + +/** Stalls Ep0. + + @param aClientId A pointer to the LDD wishing to stall Ep0 (this is for PIL internal purposes only). + + @return KErrNone if endpoint zero successfully stalled, KErrGeneral otherwise. +*/ +EXPORT_C TInt DUsbClientController::Ep0Stall(const DBase* aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::Ep0Stall()")); + if (aClientId == iEp0ClientId) + { + ResetEp0DataOutVars(); + } + const TInt err = iConTransferMgr->StallEndpoint(KEp0_Out); + if (err < 0) + { + return err; + } + else + return iConTransferMgr->StallEndpoint(KEp0_In); + } + + +/** Sends a zero-byte status packet on Ep0. + + @param aClientId A pointer to the LDD wishing to send the status packet (not used at present). +*/ +EXPORT_C void DUsbClientController::SendEp0StatusPacket(const DBase* /* aClientId */) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SendEp0StatusPacket()")); + iConTransferMgr->SendEp0ZeroByteStatusPacket(); + } + + +/** Returns the current USB device state. + + 'USB device state' here refers to the Visible Device States as defined in chapter 9 of the USB + specification. + + @return The current USB device state, or UsbShai::EUsbPeripheralStateUndefined +*/ +EXPORT_C UsbShai::TUsbPeripheralState DUsbClientController::GetDeviceStatus() const + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetDeviceStatus()")); + return iDeviceState; + } + + +/** Returns the state of an endpoint. + + 'Endpoint state' here refers to the state of the ENDPOINT_HALT feature of + an endpoint as described in chapter 9 of the USB specification. + + @param aClientId A pointer to the LDD owning the interface which contains the endpoint to be queried. + @param aEndpointNum The number of the endpoint to be queried. + + @return The current endpoint state, or EEndpointStateUnknown if the endpoint couldn't be found. +*/ +EXPORT_C TEndpointState DUsbClientController::GetEndpointStatus(const DBase* aClientId, TInt aEndpointNum) const + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::GetEndpointStatus()")); + + TEndpointState ret = (iRealEndpoints[aEndpointNum].iHalt)?EEndpointStateStalled : EEndpointStateNotStalled; + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::GetEndpointStatus() %d",ret)); + + return ret; + } + + +/** Sets up a data read request for an endpoint. + + @param aCallback A reference to a properly filled in data transfer request callback structure. + + @return KErrNone if callback successfully registered or if this callback is already registered + (but it won't be registered twice), KErrNotFound if the endpoint couldn't be found, KErrArgument if + endpoint number invalid (PSL), KErrGeneral if something else goes wrong. +*/ +EXPORT_C TInt DUsbClientController::SetupReadBuffer(TUsbcRequestCallback& aCallback) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetupReadBuffer()")); + const TInt ep = aCallback.iRealEpNum; + __KTRACE_OPT(KUSB, Kern::Printf(" logical ep: #%d", aCallback.iEndpointNum)); + __KTRACE_OPT(KUSB, Kern::Printf(" real ep: #%d", ep)); + TInt err = KErrGeneral; + if (ep != 0) + { + if (iRequestCallbacks[ep]) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Warning: RequestCallback already registered for that ep")); + if (iRequestCallbacks[ep] == &aCallback) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" (this same RequestCallback @ 0x%x)", &aCallback)); + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" (a different RequestCallback @ 0x%x)", &aCallback)); + } + return KErrNone; + } + // This may seem awkward: + // First we add a callback, and then, in case of an error, we remove it again. + // However this is necessary because the transfer request might complete (through + // an ISR) _before_ the SetupEndpointRead function returns. Since we don't know the + // outcome, we have to provide the callback before making the setup call. + // + __KTRACE_OPT(KUSB, Kern::Printf(" adding RequestCallback[%d] @ 0x%x", ep, &aCallback)); + iRequestCallbacks[ep] = &aCallback; + if ((err = iController.SetupEndpointRead(ep, aCallback)) != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" removing RequestCallback @ 0x%x (due to error)", + &aCallback)); + iRequestCallbacks[ep] = NULL; + } + } + else // (ep == 0) + { + if (iEp0ReadRequestCallbacks.Elements() == KUsbcMaxListLength) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Maximum list length reached: %d", + KUsbcMaxListLength)); + return KErrGeneral; + } + if (IsInTheRequestList(aCallback)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" RequestCallback @ 0x%x already registered", &aCallback)); + return KErrNone; + } + // Ep0 reads don't need to be prepared - there's always one pending + __KTRACE_OPT(KUSB, Kern::Printf(" adding RequestCallback @ 0x%x (ep0)", &aCallback)); + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + iEp0ReadRequestCallbacks.AddLast(aCallback); + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + err = KErrNone; + if (iEp0_RxExtraCount != 0) + { + __KTRACE_OPT(KUSB, Kern::Printf(" iEp0_RxExtraData: trying again...")); + + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + + // Extra data is either a setup packet or a data packet + // They are not possible to be both + // And, the error code must be KErrNone, otherwise, we can not arrive here + if( iSetupPacketPending ) + { + ProcessSetupPacket(iEp0_RxExtraCount,KErrNone); + } + else + { + ProcessDataOutPacket(iEp0_RxExtraCount,KErrNone); + } + + // clear it since already completed to client + iEp0_RxExtraCount = 0; + + err = iLastError; + + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + if (err == KErrNone) + { + //iEp0_RxExtraData = EFalse; + // Queue a new Ep0 read (because xxxProceed only re-enables the interrupt) + iConTransferMgr->SetupEndpointZeroRead(); + if (iSetupPacketPending) + { + iConTransferMgr->Ep0SetupPacketProceed(); + iSetupPacketPending = EFalse; + } + else + { + iConTransferMgr->Ep0DataPacketProceed(); + } + + __KTRACE_OPT(KUSB, Kern::Printf(" :-)")); + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: :-(")); + err = KErrGeneral; + } + return err; + } + } + return err; + } + + +/** Sets up a data write request for an endpoint. + + @param aCallback A reference to a properly filled in data transfer request callback structure. + + @return KErrNone if callback successfully registered or if this callback is already registered + (but it won't be registered twice), KErrNotFound if the endpoint couldn't be found, KErrArgument if + endpoint number invalid (PSL), KErrGeneral if something else goes wrong. +*/ +EXPORT_C TInt DUsbClientController::SetupWriteBuffer(TUsbcRequestCallback& aCallback) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetupWriteBuffer()")); + TInt ep = aCallback.iRealEpNum; + __KTRACE_OPT(KUSB, Kern::Printf(" logical ep: #%d", aCallback.iEndpointNum)); + __KTRACE_OPT(KUSB, Kern::Printf(" real ep: #%d", ep)); + if (iRequestCallbacks[ep]) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Warning: RequestCallback already registered for that ep")); + if (iRequestCallbacks[ep] == &aCallback) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" (this same RequestCallback @ 0x%x)", &aCallback)); + return KErrNone; + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" (a different RequestCallback @ 0x%x - poss. error)", + &aCallback)); + return KErrGeneral; + } + } + if (ep == 0) + { + if (iEp0_TxNonStdCount) + { + if (iEp0_TxNonStdCount > aCallback.iLength) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Warning: Ep0 is sending less data than requested")); + if ((aCallback.iLength % iEp0MaxPacketSize == 0) && !aCallback.iZlpReqd) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Warning: Zlp should probably be requested")); + } + } + else if (iEp0_TxNonStdCount < aCallback.iLength) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Warning: Ep0 is sending more data than requested")); + } + iEp0_TxNonStdCount = 0; + } + // Ep0 IN needs to be adjusted: the LDD uses 0 for both Ep0 directions. + ep = KEp0_Tx; + } + // This may seem awkward: + // First we add a callback, and then, in case of an error, we remove it again. + // However this is necessary because the transfer request might complete (through + // an ISR) _before_ the SetupEndpointWrite function returns. Since we don't know the + // outcome, we have to provide the callback before making the setup call. + // + __KTRACE_OPT(KUSB, Kern::Printf(" adding RequestCallback[%d] @ 0x%x", ep, &aCallback)); + iRequestCallbacks[ep] = &aCallback; + if (ep == KEp0_Tx) + { + iEp0ClientDataTransmitting = ETrue; // this must be set before calling SetupEndpointZeroWrite + TInt ret = iConTransferMgr->SetupEndpointZeroWrite(aCallback.iBufferStart, aCallback.iLength, aCallback.iZlpReqd); + if (ret != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" removing RequestCallback @ 0x%x (due to error)", &aCallback)); + iRequestCallbacks[ep] = NULL; + iEp0ClientDataTransmitting = EFalse; + return ret; + } + } + else if (iController.SetupEndpointWrite(ep, aCallback) != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" removing RequestCallback @ 0x%x (due to error)", &aCallback)); + iRequestCallbacks[ep] = NULL; + } + return KErrNone; + } + + +/** Cancels a data read request for an endpoint. + + The request callback will be removed from the queue and the + callback function won't be executed. + + @param aClientId A pointer to the LDD owning the interface which contains the endpoint. + @param aRealEndpoint The number of the endpoint for which the transfer request is to be cancelled. +*/ +EXPORT_C void DUsbClientController::CancelReadBuffer(const DBase* aClientId, TInt aRealEndpoint) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::CancelReadBuffer(%d)", aRealEndpoint)); + if (aRealEndpoint < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: ep # < 0: %d", aRealEndpoint)); + return; + } + // Note that we here don't cancel Ep0 read requests at the PSL level! + if (aRealEndpoint > 0) + { + iController.CancelEndpointRead(aRealEndpoint); + } + DeleteRequestCallback(aClientId, aRealEndpoint, UsbShai::EControllerRead); + } + + +/** Cancels a data write request for an endpoint. + + It cannot be guaranteed that the data is not sent nonetheless, as some UDCs don't permit a flushing of a + TX FIFO once it has been filled. The request callback will be removed from the queue in any case and the + callback function won't be executed. + + @param aClientId A pointer to the LDD owning the interface which contains the endpoint. + @param aRealEndpoint The number of the endpoint for which the transfer request is to be cancelled. +*/ +EXPORT_C void DUsbClientController::CancelWriteBuffer(const DBase* aClientId, TInt aRealEndpoint) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::CancelWriteBuffer(%d)", aRealEndpoint)); + if (aRealEndpoint < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: ep # < 0: %d", aRealEndpoint)); + return; + } + if (aRealEndpoint == 0) + { + // Ep0 IN needs to be adjusted: the LDD uses 0 for both Ep0 directions. + aRealEndpoint = KEp0_Tx; + } + iController.CancelEndpointWrite(aRealEndpoint); + if (aRealEndpoint == KEp0_Tx) + { + // Since Ep0 is shared among clients, we don't have to care about the client id. + iEp0WritePending = EFalse; + } + DeleteRequestCallback(aClientId, aRealEndpoint, UsbShai::EControllerWrite); + } + + +/** Halts (stalls) an endpoint (but not Ep0). + + @param aClientId A pointer to the LDD owning the interface which contains the endpoint to be stalled. + @param aEndpointNum The number of the endpoint. + + @return KErrNotFound if endpoint couldn't be found (includes Ep0), KErrNone if endpoint successfully + stalled, KErrGeneral otherwise. +*/ +EXPORT_C TInt DUsbClientController::HaltEndpoint(const DBase* aClientId, TInt aEndpointNum) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::HaltEndpoint(%d)", aEndpointNum)); + const TInt r = iController.StallEndpoint(aEndpointNum); + if (r == KErrNone) + { + iRealEndpoints[aEndpointNum].iHalt = ETrue; + } + else if (r == KErrArgument) + { + return KErrNotFound; + } + return r; + } + + +/** Clears the halt condition of an endpoint (but not Ep0). + + @param aClientId A pointer to the LDD owning the interface which contains the endpoint to be un-stalled. + @param aEndpointNum The number of the endpoint. + + @return KErrNotFound if endpoint couldn't be found (includes Ep0), KErrNone if endpoint successfully + stalled, KErrGeneral otherwise. +*/ +EXPORT_C TInt DUsbClientController::ClearHaltEndpoint(const DBase* aClientId, TInt aEndpointNum) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ClearHaltEndpoint(%d)", aEndpointNum)); + const TInt r = iController.ClearStallEndpoint(aEndpointNum); + if (r == KErrNone) + { + iRealEndpoints[aEndpointNum].iHalt = EFalse; + } + else if (r == KErrArgument) + { + return KErrNotFound; + } + return r; + } + + +/** This function requests 'device control' for an LDD. + + Class or vendor specific Ep0 requests addressed to the USB device as a whole (Recipient field in + bmRequestType byte of a Setup packet set to zero) are delivered to the LDD that owns device control. For + obvious reasons only one USB LDD can have device control at any given time. + + @param aClientId A pointer to the LDD requesting device control. + + @return KErrNone if device control successfully claimed or if this LDD already owns it, KErrGeneral if + device control already owned by a different client. +*/ +EXPORT_C TInt DUsbClientController::SetDeviceControl(const DBase* aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetDeviceControl()")); + if (iEp0DeviceControl) + { + if (iEp0DeviceControl == aClientId) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Warning: Device Control already owned by this client")); + return KErrNone; + } + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Device Control already claimed by a different client")); + return KErrGeneral; + } + iEp0DeviceControl = aClientId; + return KErrNone; + } + + +/** This function releases device control for an LDD. + + @see DUsbClientController::SetDeviceControl() + + @param aClientId A pointer to the LDD releasing device control. + + @return KErrNone if device control successfully released, KErrGeneral if device control owned by a + different client or by no client at all. +*/ +EXPORT_C TInt DUsbClientController::ReleaseDeviceControl(const DBase* aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ReleaseDeviceControl()")); + if (iEp0DeviceControl) + { + if (iEp0DeviceControl == aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Releasing Device Control")); + iEp0DeviceControl = NULL; + return KErrNone; + } + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Device Control owned by a different client")); + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Device Control not owned by any client")); + } + return KErrGeneral; + } + + +/** Returns all available (configurable) max packet sizes for Ep0. + + The information is coded as bitwise OR'ed values of KUsbEpSizeXXX constants (the bitmap format used for + TUsbcEndpointCaps.iSupportedSizes). + + @return All available (configurable) max packet sizes for Ep0. +*/ +EXPORT_C TUint DUsbClientController::EndpointZeroMaxPacketSizes() const + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::EndpointZeroMaxPacketSizes()")); + return iRealEndpoints[0].iCaps.iSizes; + } + + +/** Sets (configures) the max packet size for Ep0. + + For available sizes as returned by DUsbClientController::EndpointZeroMaxPacketSizes() + + Note that for HS operation the Ep0 size cannot be chosen, but is fixed at 64 bytes. + + @return KErrNotSupported if invalid size specified, KErrNone if new max packet size successfully set or + requested size was already set. +*/ +EXPORT_C TInt DUsbClientController::SetEndpointZeroMaxPacketSize(TInt aMaxPacketSize) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetEndpointZeroMaxPacketSize(%d)", + aMaxPacketSize)); + + if (iControllerProperties.iControllerCaps & UsbShai::KDevCapHighSpeed) + { + // We're not going to mess with this on a HS device. + return KErrNone; + } + + if (!(iRealEndpoints[0].iCaps.iSizes & PacketSize2Mask(aMaxPacketSize))) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: invalid size")); + return KErrNotSupported; + } + if (iRealEndpoints[0].iLEndpoint->iEpSize_Fs == aMaxPacketSize) + { + __KTRACE_OPT(KUSB, Kern::Printf(" this packet size already set -> returning")); + return KErrNone; + } + const TUsbcLogicalEndpoint* const ep0_0 = iRealEndpoints[0].iLEndpoint; + const TUsbcLogicalEndpoint* const ep0_1 = iRealEndpoints[1].iLEndpoint; + const_cast(ep0_0)->iEpSize_Fs = aMaxPacketSize; + const_cast(ep0_1)->iEpSize_Fs = aMaxPacketSize; + + // @@@ We should probably modify the device descriptor here as well... + + if (iHardwareActivated) + { + // De-configure endpoint zero + iController.DeConfigureEndpoint(KEp0_Out); + iController.DeConfigureEndpoint(KEp0_In); + // Re-configure endpoint zero + const_cast(ep0_0)->iInfo.iSize = ep0_0->iEpSize_Fs; + const_cast(ep0_1)->iInfo.iSize = ep0_1->iEpSize_Fs; + iController.ConfigureEndpoint(0, ep0_0->iInfo); + iController.ConfigureEndpoint(1, ep0_1->iInfo); + iEp0MaxPacketSize = ep0_0->iInfo.iSize; + } + return KErrNone; + } + + +/** Returns the current USB Device descriptor. + + @param aThread A pointer to the thread the LDD requesting the descriptor is running in. + @param aDeviceDescriptor A reference to a buffer into which the requested descriptor should be written + (most likely located user-side). + + @return The return value of the thread write operation, Kern::ThreadWrite(), when writing to the target + buffer. +*/ +EXPORT_C TInt DUsbClientController::GetDeviceDescriptor(DThread* aThread, TDes8& aDeviceDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetDeviceDescriptor()")); + return iDescriptors.GetDeviceDescriptorTC(aThread, aDeviceDescriptor); + } + + +/** Sets a new USB Device descriptor. + + @param aThread A pointer to the thread the LDD requesting the setting of the descriptor is running in. + @param aDeviceDescriptor A reference to a buffer which contains the descriptor to be set (most likely + located user-side). + + @return The return value of the thread read operation, Kern::ThreadRead(), when reading from the source + buffer in case of a failure, KErrNone if the new descriptor was successfully set. +*/ +EXPORT_C TInt DUsbClientController::SetDeviceDescriptor(DThread* aThread, const TDes8& aDeviceDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetDeviceDescriptor()")); + return iDescriptors.SetDeviceDescriptorTC(aThread, aDeviceDescriptor); + } + + +/** Returns the current USB Device descriptor size. + + @param aThread A pointer to the thread the LDD requesting the descriptor size is running in. + @param aSize A reference to a buffer into which the requested descriptor size should be written + (most likely located user-side). + + @return The return value of the thread write operation, Kern::ThreadWrite(), when writing to the target + buffer. +*/ +EXPORT_C TInt DUsbClientController::GetDeviceDescriptorSize(DThread* aThread, TDes8& aSize) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetDeviceDescriptorSize()")); + // We do not really enquire here.... + const TPtrC8 size(reinterpret_cast(&KUsbDescSize_Device), sizeof(KUsbDescSize_Device)); + return Kern::ThreadDesWrite(aThread, &aSize, size, 0); + } + + +/** Returns the current USB configuration descriptor. + + @param aThread A pointer to the thread the LDD requesting the descriptor is running in. + @param aConfigurationDescriptor A reference to a buffer into which the requested descriptor should be + written (most likely located user-side). + + @return The return value of the thread write operation, Kern::ThreadWrite(), when writing to the target + buffer. +*/ +EXPORT_C TInt DUsbClientController::GetConfigurationDescriptor(DThread* aThread, TDes8& aConfigurationDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetConfigurationDescriptor()")); + return iDescriptors.GetConfigurationDescriptorTC(aThread, aConfigurationDescriptor); + } + + +/** Sets a new USB configuration descriptor. + + @param aThread A pointer to the thread the LDD requesting the setting of the descriptor is running in. + @param aConfigurationDescriptor A reference to a buffer which contains the descriptor to be set (most + likely located user-side). + + @return The return value of the thread read operation, Kern::ThreadRead() when reading from the source + buffer in case of a failure, KErrNone if the new descriptor was successfully set. +*/ +EXPORT_C TInt DUsbClientController::SetConfigurationDescriptor(DThread* aThread, + const TDes8& aConfigurationDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetConfigurationDescriptor()")); + return iDescriptors.SetConfigurationDescriptorTC(aThread, aConfigurationDescriptor); + } + + +/** Returns the current USB configuration descriptor size. + + @param aThread A pointer to the thread the LDD requesting the descriptor size is running in. + @param aSize A reference to a buffer into which the requested descriptor size should be written + (most likely located user-side). + + @return The return value of the thread write operation, Kern::ThreadWrite(), when writing to the target + buffer. +*/ +EXPORT_C TInt DUsbClientController::GetConfigurationDescriptorSize(DThread* aThread, TDes8& aSize) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetConfigurationDescriptorSize()")); + // We do not really enquire here.... + const TPtrC8 size(reinterpret_cast(&KUsbDescSize_Config), sizeof(KUsbDescSize_Config)); + return Kern::ThreadDesWrite(aThread, &aSize, size, 0); + } + + +/** Returns the current USB OTG descriptor. + + @param aThread A pointer to the thread the LDD requesting the descriptor size is running in. + @param aOtgDesc A reference to a buffer into which the requested descriptor should be + written (most likely located user-side). + + @return KErrNotSupported or the return value of the thread write operation, Kern::ThreadDesWrite(), + when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetOtgDescriptor(DThread* aThread, TDes8& aOtgDesc) const + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetOtgDescriptor()")); + if (!iOtgSupport) + { + return KErrNotSupported; + } + return iDescriptors.GetOtgDescriptorTC(aThread, aOtgDesc); + } + + +/** Sets a new OTG descriptor. + + @param aThread A pointer to the thread the LDD requesting the descriptor size is running in. + @param aOtgDesc A reference to a buffer which contains new OTG descriptor. + + @return KErrNotSupported or the return value of the thread read operation, Kern::ThreadDesRead(). +*/ +EXPORT_C TInt DUsbClientController::SetOtgDescriptor(DThread* aThread, const TDesC8& aOtgDesc) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetOtgDescriptor()")); + if (!iOtgSupport) + { + return KErrNotSupported; + } + TBuf8 otg; + const TInt r = Kern::ThreadDesRead(aThread, &aOtgDesc, otg, 0); + if (r != KErrNone) + { + return r; + } + // Check descriptor validity + if (otg[0] != KUsbDescSize_Otg || otg[1] != KUsbDescType_Otg || otg[2] > 3) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Invalid OTG descriptor")); + return KErrGeneral; + } + __KTRACE_OPT(KUSB, Kern::Printf(" iOtgFuncMap before: 0x%x", iOtgFuncMap)); + // Update value in controller as well + const TUint8 hnp = otg[2] & KUsbOtgAttr_HnpSupp; + const TUint8 srp = otg[2] & KUsbOtgAttr_SrpSupp; + if (hnp && !srp) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Warning: Invalid OTG attribute combination (HNP && !SRP")); + } + if (hnp && !(iOtgFuncMap & KUsbOtgAttr_HnpSupp)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Setting attribute KUsbOtgAttr_HnpSupp")); + iOtgFuncMap |= KUsbOtgAttr_HnpSupp; + } + else if (!hnp && (iOtgFuncMap & KUsbOtgAttr_HnpSupp)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Removing attribute KUsbOtgAttr_HnpSupp")); + iOtgFuncMap &= ~KUsbOtgAttr_HnpSupp; + } + if (srp && !(iOtgFuncMap & KUsbOtgAttr_SrpSupp)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Setting attribute KUsbOtgAttr_SrpSupp")); + iOtgFuncMap |= KUsbOtgAttr_SrpSupp; + } + else if (!srp && (iOtgFuncMap & KUsbOtgAttr_SrpSupp)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Removing attribute KUsbOtgAttr_SrpSupp")); + iOtgFuncMap &= ~KUsbOtgAttr_SrpSupp; + } + __KTRACE_OPT(KUSB, Kern::Printf(" iOtgFuncMap after: 0x%x", iOtgFuncMap)); + return iDescriptors.SetOtgDescriptor(otg); + } + + +/** Returns current OTG features of USB device. + + @param aThread A pointer to the thread the LDD requesting the descriptor size is running in. + @param aFeatures A reference to a buffer into which the requested OTG features should be written. + + @return KErrNotSupported or the return value of the thread write operation, Kern::ThreadDesWrite(). +*/ +EXPORT_C TInt DUsbClientController::GetOtgFeatures(DThread* aThread, TDes8& aFeatures) const + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetOtgFeatures()")); + if (!iOtgSupport) + { + return KErrNotSupported; + } + TBuf8<1> features(1); + features[0] = iOtgFuncMap & 0x1C; + return Kern::ThreadDesWrite(aThread, &aFeatures, features, 0); + } + + +/** Returns current OTG features of USB device. This function is intended to be + called only from kernel side. + + @param aFeatures The reference to which the current features should be set at. + @return KErrNone if successful, KErrNotSupported if OTG is unavailable. +*/ +EXPORT_C TInt DUsbClientController::GetCurrentOtgFeatures(TUint8& aFeatures) const + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetCurrentOtgFeatures()")); + if (!iOtgSupport) + { + return KErrNotSupported; + } + aFeatures = iOtgFuncMap & 0x1C; + return KErrNone; + } + + +/** Registers client request for OTG feature change. Client is notified when any OTG + feature is changed. + + @see KUsbOtgAttr_B_HnpEnable, KUsbOtgAttr_A_HnpSupport, KUsbOtgAttr_A_AltHnpSupport + + @param aCallback Callback function. Gets called when OTG features change + + @return KErrNone if successful, KErrAlreadyExists if aCallback is already in the queue. +*/ +EXPORT_C TInt DUsbClientController::RegisterForOtgFeatureChange(TUsbcOtgFeatureCallback& aCallback) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::RegisterForOtgFeatureChange()")); + if (iOtgCallbacks.Elements() == KUsbcMaxListLength) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Maximum list length reached: %d", + KUsbcMaxListLength)); + return KErrGeneral; + } + if (IsInTheOtgFeatureList(aCallback)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Error: OtgFeatureCallback @ 0x%x already registered", &aCallback)); + return KErrAlreadyExists; + } + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + iOtgCallbacks.AddLast(aCallback); + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + return KErrNone; + } + + +/** De-registers (removes from the list of pending requests) a notification callback for + OTG feature change. + + @param aClientId A pointer to the LDD owning the endpoint status change callback. + + @return KErrNone if callback successfully unregistered, KErrNotFound if the callback couldn't be found. +*/ +EXPORT_C TInt DUsbClientController::DeRegisterForOtgFeatureChange(const DBase* aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeRegisterForOtgFeatureChange()")); + __ASSERT_DEBUG((aClientId != NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + TSglQueIter iter(iOtgCallbacks); + TUsbcOtgFeatureCallback* p; + while ((p = iter++) != NULL) + { + if (!aClientId || p->Owner() == aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf(" removing OtgFeatureCallback @ 0x%x", p)); + iOtgCallbacks.Remove(*p); + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + return KErrNone; + } + } + __KTRACE_OPT(KUSB, Kern::Printf(" client not found")); + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + return KErrNotFound; + } + + +/** Returns a specific standard USB interface descriptor. + + @param aThread A pointer to the thread the LDD requesting the descriptor is running in. + @param aClientId A pointer to the LDD requesting the descriptor. + @param aSettingNum The setting number of the interface for which the descriptor is requested. + @param aInterfaceDescriptor A reference to a buffer into which the requested descriptor should be written + (most likely located user-side). + + @return KErrNotFound if the specified interface couldn't be found, otherwise the return value of the thread + write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetInterfaceDescriptor(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, TDes8& aInterfaceDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetInterfaceDescriptor(x, 0x%08x, %d, y)", + aClientId, aSettingNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + return iDescriptors.GetInterfaceDescriptorTC(aThread, aInterfaceDescriptor, ifcset, aSettingNum); + } + + +/** Sets a new standard USB interface descriptor. + + This function can also be used, by the user, and under certain conditions, to change an interface's number + (reported as bInterfaceNumber in the descriptor). The conditions are: 1) We cannot accept a number that is + already used by another interface, 2) We allow the interface number to be changed only when it's still the + only setting, and 3) We allow the interface number to be changed only for the default setting (0). (All + alternate settings created for that interface thereafter will inherit the new, changed number.) + + @param aThread A pointer to the thread the LDD requesting the setting of the descriptor is running in. + @param aClientId A pointer to the LDD requesting the setting of the descriptor. + @param aSettingNum The setting number of the interface for which the descriptor is to be set. + @param aInterfaceDescriptor A reference to a buffer which contains the descriptor to be set (most + likely located user-side). + + @return KErrNotFound if the specified interface couldn't be found, the return value of the thread read + operation, Kern::ThreadRead(), when reading from the source buffer in case of a failure, KErrArgument if the + interface number is to be changed (via bInterfaceNumber in the descriptor) and either the requested + interface number is already used by another interface or the interface has more than one setting. KErrNone + if the new descriptor was successfully set. +*/ +EXPORT_C TInt DUsbClientController::SetInterfaceDescriptor(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, const TDes8& aInterfaceDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetInterfaceDescriptor(x, 0x%08x, %d, y)", + aClientId, aSettingNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + TBuf8 new_ifc; + TInt r = Kern::ThreadDesRead(aThread, &aInterfaceDescriptor, new_ifc, 0); + if (r != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Copying interface descriptor buffer failed (%d)", r)); + return r; + } + const TInt ifcset_new = new_ifc[2]; + const TBool ifc_num_changes = (ifcset != ifcset_new); + TUsbcInterfaceSet* const ifcset_ptr = InterfaceNumber2InterfacePointer(ifcset); + if (!ifcset_ptr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: interface number %d doesn't exist", ifcset)); + return KErrNotFound; + } + if (ifc_num_changes) + { + // If the user wants to change the interface number, we need to do some sanity checks: + if (InterfaceExists(ifcset_new)) + { + // Obviously we cannot accept a number that is already used by another interface. + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: interface number %d already in use", ifcset_new)); + return KErrArgument; + } + if (ifcset_ptr->iInterfaces.Count() > 1) + { + // We allow the interface number to be changed only when it's the only setting. + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: interface has more than one alternate setting")); + return KErrArgument; + } + if (aSettingNum != 0) + { + // We allow the interface number to be changed only when it's the default setting. + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: interface number can only be changed for setting 0")); + return KErrArgument; + } + } + if ((r = iDescriptors.SetInterfaceDescriptor(new_ifc, ifcset, aSettingNum)) != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: iDescriptors.SetInterfaceDescriptorfailed")); + return r; + } + if (ifc_num_changes) + { + // Alright then, let's do it... + __KTRACE_OPT(KUSB, Kern::Printf(" about to change interface number from %d to %d", + ifcset, ifcset_new)); + ifcset_ptr->iInterfaceNumber = ifcset_new; + } + return KErrNone; + } + + +/** Returns the size of a specific standard USB interface descriptor. + + @param aThread A pointer to the thread the LDD requesting the descriptor size is running in. + @param aClientId A pointer to the LDD requesting the descriptor size. + @param aSettingNum The setting number of the interface for which the descriptor size is requested. + @param aSize A reference to a buffer into which the requested descriptor size should be written (most + likely located user-side). + + @return KErrNotFound if the specified interface couldn't be found, otherwise the return value of the thread + write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetInterfaceDescriptorSize(DThread* aThread, const DBase* aClientId, + TInt /*aSettingNum*/, TDes8& aSize) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetInterfaceDescriptorSize()")); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + // Actually, we do not really enquire here.... + const TPtrC8 size(reinterpret_cast(&KUsbDescSize_Interface), sizeof(KUsbDescSize_Interface)); + Kern::ThreadDesWrite(aThread, &aSize, size, 0); + return KErrNone; + } + + +/** Returns a specific standard USB endpoint descriptor. + + @param aThread A pointer to the thread the LDD requesting the descriptor is running in. + @param aClientId A pointer to the LDD requesting the descriptor. + @param aSettingNum The setting number of the interface that contains the endpoint for which the + descriptor is requested. + @param aEndpointNum The endpoint for which the descriptor is requested. + @param aEndpointDescriptor A reference to a buffer into which the requested descriptor should be written + (most likely located user-side). + + @return KErrNotFound if the specified interface or endpoint couldn't be found, otherwise the return value + of the thread write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetEndpointDescriptor(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, TInt aEndpointNum, + TDes8& aEndpointDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetEndpointDescriptor(x, 0x%08x, %d, %d, y)", + aClientId, aSettingNum, aEndpointNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + return iDescriptors.GetEndpointDescriptorTC(aThread, aEndpointDescriptor, ifcset, + aSettingNum, EpIdx2Addr(aEndpointNum)); + } + + +/** Sets a new standard USB endpoint descriptor. + + @param aThread A pointer to the thread the LDD requesting the setting of the descriptor is running in. + @param aClientId A pointer to the LDD requesting the setting of the descriptor. + @param aSettingNum The setting number of the interface that contains the endpoint for which the + descriptor is to be set. + @param aEndpointNum The endpoint for which the descriptor is to be set. + @param aEndpointDescriptor A reference to a buffer which contains the descriptor to be set (most + likely located user-side). + + @return KErrNotFound if the specified interface or endpoint couldn't be found, the return value of the + thread read operation, Kern::ThreadRead(), when reading from the source buffer in case of a read failure, + KErrNone if the new descriptor was successfully set. +*/ +EXPORT_C TInt DUsbClientController::SetEndpointDescriptor(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, TInt aEndpointNum, + const TDes8& aEndpointDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetEndpointDescriptor(x, 0x%08x, %d, %d, y)", + aClientId, aSettingNum, aEndpointNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + return iDescriptors.SetEndpointDescriptorTC(aThread, aEndpointDescriptor, ifcset, + aSettingNum, EpIdx2Addr(aEndpointNum)); + } + + +/** Returns the size of a specific standard USB endpoint descriptor. + + @param aThread A pointer to the thread the LDD requesting the descriptor size is running in. + @param aClientId A pointer to the LDD requesting the descriptor size. + @param aSettingNum The setting number of the interface that contains the endpoint for which the + descriptor size is requested. + @param aEndpointNum The endpoint for which the descriptor size is requested. + @param aEndpointDescriptor A reference to a buffer into which the requested descriptor size should be + written (most likely located user-side). + + @return KErrNotFound if the specified interface or endpoint couldn't be found, otherwise the return value + of the thread write operation, kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetEndpointDescriptorSize(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, TInt aEndpointNum, + TDes8& aSize) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetEndpointDescriptorSize(x, 0x%08x, %d, %d, y)", + aClientId, aSettingNum, aEndpointNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + TInt s; + TInt r = iDescriptors.GetEndpointDescriptorSize(ifcset, aSettingNum, + EpIdx2Addr(aEndpointNum), s); + if (r == KErrNone) + { + TPtrC8 size(reinterpret_cast(&s), sizeof(s)); + r = Kern::ThreadDesWrite(aThread, &aSize, size, 0); + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: endpoint descriptor not found")); + } + return r; + } + + +/** Returns the current Device_Qualifier descriptor. On a USB device which doesn't support high-speed + operation this function will return an error. Note that the contents of the descriptor depend on + the current device speed (full-speed or high-speed). + + @param aThread A pointer to the thread the LDD requesting the descriptor is running in. + @param aDeviceQualifierDescriptor A reference to a buffer into which the requested descriptor + should be written (most likely located user-side). + + @return KErrNotSupported if this descriptor is not supported, otherwise the return value of the thread + write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetDeviceQualifierDescriptor(DThread* aThread, + TDes8& aDeviceQualifierDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetDeviceQualifierDescriptor()")); + return iDescriptors.GetDeviceQualifierDescriptorTC(aThread, aDeviceQualifierDescriptor); + } + + +/** Sets a new Device_Qualifier descriptor. On a USB device which doesn't support high-speed + operation this function will return an error. Note that the contents of the descriptor should take + into account the current device speed (full-speed or high-speed) as it is dependent on it. + + @param aThread A pointer to the thread the LDD requesting the setting of the descriptor is running in. + @param aDeviceQualifierDescriptor A reference to a buffer which contains the descriptor to be set (most + likely located user-side). + + @return KErrNotSupported if this descriptor is not supported, otherwise the return value of the thread + read operation, Kern::ThreadRead(), when reading from the source buffer in case of a failure, KErrNone if + the new descriptor was successfully set. +*/ +EXPORT_C TInt DUsbClientController::SetDeviceQualifierDescriptor(DThread* aThread, + const TDes8& aDeviceQualifierDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetDeviceQualifierDescriptor()")); + return iDescriptors.SetDeviceQualifierDescriptorTC(aThread, aDeviceQualifierDescriptor); + } + + +/** Returns the current Other_Speed_Configuration descriptor. On a USB device which doesn't support high-speed + operation this function will return an error. Note that the contents of the descriptor depend on the + current device speed (full-speed or high-speed). + + @param aThread A pointer to the thread the LDD requesting the descriptor is running in. + @param aConfigurationDescriptor A reference to a buffer into which the requested descriptor + should be written (most likely located user-side). + + @return KErrNotSupported if this descriptor is not supported, otherwise the return value of the thread + write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetOtherSpeedConfigurationDescriptor(DThread* aThread, + TDes8& aConfigurationDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetOtherSpeedConfigurationDescriptor()")); + return iDescriptors.GetOtherSpeedConfigurationDescriptorTC(aThread, aConfigurationDescriptor); + } + + +/** Sets a new Other_Speed_Configuration descriptor. On a USB device which doesn't support high-speed + operation this function will return an error. Note that the contents of the descriptor should take + into account the current device speed (full-speed or high-speed) as it is dependent on it. + + @param aThread A pointer to the thread the LDD requesting the setting of the descriptor is running in. + @param aConfigurationDescriptor A reference to a buffer which contains the descriptor to be set (most + likely located user-side). + + @return KErrNotSupported if this descriptor is not supported, otherwise the return value of the thread + read operation, Kern::ThreadRead(), when reading from the source buffer in case of a failure, KErrNone if + the new descriptor was successfully set. +*/ +EXPORT_C TInt DUsbClientController::SetOtherSpeedConfigurationDescriptor(DThread* aThread, + const TDes8& aConfigurationDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetOtherSpeedConfigurationDescriptor()")); + return iDescriptors.SetOtherSpeedConfigurationDescriptorTC(aThread, aConfigurationDescriptor); + } + + +/** Returns a block of all available non-standard (class-specific) interface descriptors for a specific + interface. + + @param aThread A pointer to the thread the LDD requesting the descriptor block is running in. + @param aClientId A pointer to the LDD requesting the descriptor block. + @param aSettingNum The setting number of the interface for which the descriptor block is requested. + @param aInterfaceDescriptor A reference to a buffer into which the requested descriptor(s) should be + written (most likely located user-side). + + @return KErrNotFound if the specified interface couldn't be found, otherwise the return value of the thread + write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetCSInterfaceDescriptorBlock(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, + TDes8& aInterfaceDescriptor) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetCSInterfaceDescriptorBlock(x, 0x%08x, %d, y)", + aClientId, aSettingNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + return iDescriptors.GetCSInterfaceDescriptorTC(aThread, aInterfaceDescriptor, ifcset, aSettingNum); + } + + +/** Sets a block of (i.e. one or more) non-standard (class-specific) interface descriptors for a specific + interface. + + @param aThread A pointer to the thread the LDD requesting the setting of the descriptor block is running + in. + @param aClientId A pointer to the LDD requesting the setting of the descriptor block. + @param aSettingNum The setting number of the interface for which the setting of the descriptor block is + requested. + @param aInterfaceDescriptor A reference to a buffer which contains the descriptor block to be set (most + likely located user-side). + @param aSize The size of the descriptor block to be set. + + @return KErrNotFound if the specified interface couldn't be found, KErrArgument if aSize is less than 2, + KErrNoMemory if enough memory for the new descriptor(s) couldn't be allocated, otherwise the return value + of the thread read operation, Kern::ThreadRead(), when reading from the source buffer. +*/ +EXPORT_C TInt DUsbClientController::SetCSInterfaceDescriptorBlock(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, + const TDes8& aInterfaceDescriptor, TInt aSize) + { + __KTRACE_OPT(KUSB, + Kern::Printf("DUsbClientController::SetCSInterfaceDescriptorBlock(x, 0x%08x, %d, y, %d)", + aClientId, aSettingNum, aSize)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + if (aSize < 2) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: aSize < 2 (%d)", aSize)); + return KErrArgument; + } + return iDescriptors.SetCSInterfaceDescriptorTC(aThread, aInterfaceDescriptor, ifcset, aSettingNum, aSize); + } + + +/** Returns the total size all non-standard (class-specific) interface descriptors for a specific interface. + + @param aThread A pointer to the thread the LDD requesting the descriptor block size is running in. + @param aClientId A pointer to the LDD requesting the descriptor block size. + @param aSettingNum The setting number of the interface for which the descriptor block size is + requested. + @param aSize A reference to a buffer into which the requested descriptor block size should be written (most + likely located user-side). + + @return KErrNotFound if the specified interface couldn't be found, otherwise the return value of the thread + write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetCSInterfaceDescriptorBlockSize(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, TDes8& aSize) + { + __KTRACE_OPT(KUSB, + Kern::Printf("DUsbClientController::GetCSInterfaceDescriptorBlockSize(x, 0x%08x, %d, y)", + aClientId, aSettingNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + TInt s; + const TInt r = iDescriptors.GetCSInterfaceDescriptorSize(ifcset, aSettingNum, s); + if (r == KErrNone) + { + const TPtrC8 size(reinterpret_cast(&s), sizeof(s)); + Kern::ThreadDesWrite(aThread, &aSize, size, 0); + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: cs interface descriptor not found")); + } + return r; + } + + +/** Returns a block of all available non-standard (class-specific) endpoint descriptors for a specific endpoint. + + @param aThread A pointer to the thread the LDD requesting the descriptor block is running in. + @param aClientId A pointer to the LDD requesting the descriptor block. + @param aSettingNum The setting number of the interface that contains the endpoint for which the + descriptor block is requested. + @param aEndpointNum The endpoint for which the descriptor block is requested. + @param aEndpointDescriptor A reference to a buffer into which the requested descriptor(s) should be written + (most likely located user-side). + + @return KErrNotFound if the specified interface or endpoint couldn't be found, otherwise the return value + of the thread write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetCSEndpointDescriptorBlock(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, TInt aEndpointNum, + TDes8& aEndpointDescriptor) + { + __KTRACE_OPT(KUSB, + Kern::Printf("DUsbClientController::GetCSEndpointDescriptorBlock(x, 0x%08x, %d, %d, y)", + aClientId, aSettingNum, aEndpointNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + return iDescriptors.GetCSEndpointDescriptorTC(aThread, aEndpointDescriptor, ifcset, + aSettingNum, EpIdx2Addr(aEndpointNum)); + } + + +/** Sets a block of (i.e. one or more) non-standard (class-specific) endpoint descriptors for a specific + endpoint. + + @param aThread A pointer to the thread the LDD requesting the setting of the descriptor block is running + in. + @param aClientId A pointer to the LDD requesting the setting of the descriptor block. + @param aSettingNum The setting number of the interface that contains the endpoint for which the + descriptor block is to be set. + @param aEndpointNum The endpoint for which the descriptor block is to be set. + @param aEndpointDescriptor A reference to a buffer which contains the descriptor block to be set (most + likely located user-side). + @param aSize The size of the descriptor block to be set. + + @return KErrNotFound if the specified interface or endpoint couldn't be found, KErrArgument if aSize is + less than 2, KErrNoMemory if enough memory for the new descriptor(s) couldn't be allocated, otherwise the + return value of the thread read operation, Kern::ThreadRead(), when reading from the source buffer. +*/ +EXPORT_C TInt DUsbClientController::SetCSEndpointDescriptorBlock(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, TInt aEndpointNum, + const TDes8& aEndpointDescriptor, TInt aSize) + { + __KTRACE_OPT(KUSB, + Kern::Printf("DUsbClientController::SetCSEndpointDescriptorBlock(x, 0x%08x, %d, %d, y)", + aClientId, aSettingNum, aEndpointNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + if (aSize < 2) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: aSize < 2 (%d)", aSize)); + return KErrArgument; + } + return iDescriptors.SetCSEndpointDescriptorTC(aThread, aEndpointDescriptor, ifcset, + aSettingNum, EpIdx2Addr(aEndpointNum), aSize); + } + + +/** Returns the total size all non-standard (class-specific) endpoint descriptors for a specific endpoint. + + @param aThread A pointer to the thread the LDD requesting the descriptor block size is running in. + @param aClientId A pointer to the LDD requesting the descriptor block size. + @param aSettingNum The setting number of the interface for which the descriptor block size is + requested. + @param aEndpointNum The endpoint for which the descriptor block size is requested. + @param aSize A reference to a buffer into which the requested descriptor block size should be written (most + likely located user-side). + + @return KErrNotFound if the specified interface or endpoint couldn't be found, otherwise the return value + of the thread write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetCSEndpointDescriptorBlockSize(DThread* aThread, const DBase* aClientId, + TInt aSettingNum, TInt aEndpointNum, + TDes8& aSize) + { + __KTRACE_OPT(KUSB, + Kern::Printf("DUsbClientController::GetCSEndpointDescriptorBlockSize(x, 0x%08x, %d, %d, y)", + aClientId, aSettingNum, aEndpointNum)); + const TInt ifcset = ClientId2InterfaceNumber(aClientId); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Interface not found from client ID")); + return KErrNotFound; + } + TInt s; + const TInt r = iDescriptors.GetCSEndpointDescriptorSize(ifcset, aSettingNum, + EpIdx2Addr(aEndpointNum), s); + if (r == KErrNone) + { + const TPtrC8 size(reinterpret_cast(&s), sizeof(s)); + Kern::ThreadDesWrite(aThread, &aSize, size, 0); + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: cs endpoint descriptor not found")); + } + return r; + } + + +/** Returns the currently set string descriptor language ID (LANGID) code. + + @param aThread A pointer to the thread the LDD requesting the LANGID is running in. + @param aLangId A reference to a buffer into which the requested code should be written (most likely + located user-side). + + @return The return value of the thread write operation, Kern::ThreadDesWrite(), + when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetStringDescriptorLangId(DThread* aThread, TDes8& aLangId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetStringDescriptorLangId()")); + return iDescriptors.GetStringDescriptorLangIdTC(aThread, aLangId); + } + + +/** Sets the string descriptor language ID (LANGID) code. + + @param aLangId The langauge ID code to be written. + + @return KErrNone. +*/ +EXPORT_C TInt DUsbClientController::SetStringDescriptorLangId(TUint16 aLangId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetStringDescriptorLangId()")); + return iDescriptors.SetStringDescriptorLangId(aLangId); + } + + +/** Returns the currently set Manufacturer string (which is referenced by the iManufacturer field in the device + descriptor). + + (Thus, the function should actually be called either 'GetManufacturerString' + or 'GetManufacturerStringDescriptorString'.) + + @param aThread A pointer to the thread the LDD requesting the string is running in. + @param aString A reference to a buffer into which the requested string should be written (most likely + located user-side). + + @return KErrNotFound if the string descriptor couldn't be found (PIL internal error), otherwise the return + value of the thread write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetManufacturerStringDescriptor(DThread* aThread, TDes8& aString) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetManufacturerStringDescriptor()")); + return iDescriptors.GetManufacturerStringDescriptorTC(aThread, aString); + } + + +/** Sets a new Manufacturer string in the Manufacturer string descriptor (which is referenced by the + iManufacturer field in the device descriptor). + + (Thus, the function should actually be called either + 'SetManufacturerString' or 'SetManufacturerStringDescriptorString'.) + + @param aThread A pointer to the thread the LDD requesting the setting of the string is running in. + @param aString A reference to a buffer which contains the string to be set (most likely located + user-side). + + @return KErrNoMemory if not enough memory for the new descriptor or the string could be allocated, the + return value of the thread read operation, Kern::ThreadRead(), if reading from the source buffer goes wrong, + KErrNone if new string descriptor successfully set. +*/ +EXPORT_C TInt DUsbClientController::SetManufacturerStringDescriptor(DThread* aThread, const TDes8& aString) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetManufacturerStringDescriptor()")); + return iDescriptors.SetManufacturerStringDescriptorTC(aThread, aString); + } + + +/** Removes (deletes) the Manufacturer string descriptor (which is referenced by the + iManufacturer field in the device descriptor). + + @return KErrNone if successful, KErrNotFound if the string descriptor couldn't be found +*/ +EXPORT_C TInt DUsbClientController::RemoveManufacturerStringDescriptor() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::RemoveManufacturerStringDescriptor()")); + return iDescriptors.RemoveManufacturerStringDescriptor(); + } + + +/** Returns the currently set Product string (which is referenced by the iProduct field in the device + descriptor). + + (Thus, the function should actually be called either 'GetProductString' or + 'GetProductStringDescriptorString'.) + + @param aThread A pointer to the thread the LDD requesting the string is running in. + @param aString A reference to a buffer into which the requested string should be written (most likely + located user-side). + + @return KErrNotFound if the string descriptor couldn't be found (PIL internal error), otherwise the return + value of the thread write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetProductStringDescriptor(DThread* aThread, TDes8& aString) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetProductStringDescriptor()")); + return iDescriptors.GetProductStringDescriptorTC(aThread, aString); + } + + +/** Sets a new Product string in the Product string descriptor (which is referenced by the iProduct field in + the device descriptor). + + (Thus, the function should actually be called either 'SetProductString' or + 'SetProductStringDescriptorString'.) + + @param aThread A pointer to the thread the LDD requesting the setting of the string is running in. + @param aString A reference to a buffer which contains the string to be set (most likely located + user-side). + + @return KErrNoMemory if not enough memory for the new descriptor or the string could be allocated, the + return value of the thread read operation, Kern::ThreadRead(), if reading from the source buffer goes wrong, + KErrNone if new string descriptor successfully set. +*/ +EXPORT_C TInt DUsbClientController::SetProductStringDescriptor(DThread* aThread, const TDes8& aString) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetProductStringDescriptor()")); + return iDescriptors.SetProductStringDescriptorTC(aThread, aString); + } + + +/** Removes (deletes) the Product string descriptor (which is referenced by the + iProduct field in the device descriptor). + + @return KErrNone if successful, KErrNotFound if the string descriptor couldn't be found +*/ +EXPORT_C TInt DUsbClientController::RemoveProductStringDescriptor() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::RemoveProductStringDescriptor()")); + return iDescriptors.RemoveProductStringDescriptor(); + } + + +/** Returns the currently set SerialNumber string (which is referenced by the iSerialNumber field in the device + descriptor). + + (Thus, the function should actually be called either 'GetSerialNumberString' or + 'GetSerialNumberStringDescriptorString'.) + + @param aThread A pointer to the thread the LDD requesting the string is running in. + @param aString A reference to a buffer into which the requested string should be written (most likely + located user-side). + + @return KErrNotFound if the string descriptor couldn't be found (PIL internal error), otherwise the return + value of the thread write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetSerialNumberStringDescriptor(DThread* aThread, TDes8& aString) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetSerialNumberStringDescriptor()")); + return iDescriptors.GetSerialNumberStringDescriptorTC(aThread, aString); + } + + +/** Sets a new SerialNumber string in the SerialNumber string descriptor (which is referenced by the + iSerialNumber field in the device descriptor). + + (Thus, the function should actually be called either + 'SetSerialNumberString' or 'SetSerialNumberStringDescriptorString'.) + + @param aThread A pointer to the thread the LDD requesting the setting of the string is running in. + @param aString A reference to a buffer which contains the string to be set (most likely located + user-side). + + @return KErrNoMemory if not enough memory for the new descriptor or the string could be allocated, the + return value of the thread read operation, Kern::ThreadRead(), if reading from the source buffer goes wrong, + KErrNone if new string descriptor successfully set. +*/ +EXPORT_C TInt DUsbClientController::SetSerialNumberStringDescriptor(DThread* aThread, const TDes8& aString) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetSerialNumberStringDescriptor()")); + return iDescriptors.SetSerialNumberStringDescriptorTC(aThread, aString); + } + + +/** Removes (deletes) the Serial Number string descriptor (which is referenced by the + iSerialNumber field in the device descriptor). + + @return KErrNone if successful, KErrNotFound if the string descriptor couldn't be found +*/ +EXPORT_C TInt DUsbClientController::RemoveSerialNumberStringDescriptor() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::RemoveSerialNumberStringDescriptor()")); + return iDescriptors.RemoveSerialNumberStringDescriptor(); + } + + +/** Returns the currently set Configuration string (which is referenced by the iConfiguration field in the + configuration descriptor). + + (Thus, the function should actually be called either 'GetConfigurationString' or + 'GetConfigurationStringDescriptorString'.) + + @param aThread A pointer to the thread the LDD requesting the string is running in. + @param aString A reference to a buffer into which the requested string should be written (most likely + located user-side). + + @return KErrNotFound if the string descriptor couldn't be found (PIL internal error), otherwise the return + value of the thread write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetConfigurationStringDescriptor(DThread* aThread, TDes8& aString) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetConfigurationStringDescriptor()")); + return iDescriptors.GetConfigurationStringDescriptorTC(aThread, aString); + } + + +/** Sets a new Configuration string in the Configuration string descriptor (which is referenced by the + iConfiguration field in the configuration descriptor). + + (Thus, the function should actually be called either + 'SetConfigurationString' or 'SetConfigurationStringDescriptorString'.) + + @param aThread A pointer to the thread the LDD requesting the setting of the string is running in. + @param aString A reference to a buffer which contains the string to be set (most likely located + user-side). + + @return KErrNoMemory if not enough memory for the new descriptor or the string could be allocated, the + return value of the thread read operation, Kern::ThreadRead(), if reading from the source buffer goes wrong, + KErrNone if new string descriptor successfully set. +*/ +EXPORT_C TInt DUsbClientController::SetConfigurationStringDescriptor(DThread* aThread, const TDes8& aString) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetConfigurationStringDescriptor()")); + return iDescriptors.SetConfigurationStringDescriptorTC(aThread, aString); + } + + +/** Removes (deletes) the Configuration string descriptor (which is referenced by the + iConfiguration field in the configuration descriptor). + + @return KErrNone if successful, KErrNotFound if the string descriptor couldn't be found. +*/ +EXPORT_C TInt DUsbClientController::RemoveConfigurationStringDescriptor() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::RemoveConfigurationStringDescriptor()")); + return iDescriptors.RemoveConfigurationStringDescriptor(); + } + + +/** Copies the string descriptor at the specified index in the string descriptor array into + the aString argument. + + @param aIndex The position of the string descriptor in the string descriptor array. + @param aThread A pointer to the thread the LDD requesting the string is running in. + @param aString A reference to a buffer into which the requested string should be written (most likely + located user-side). + + @return KErrNone if successful, KErrNotFound if no string descriptor exists at the specified index, or the + return value of the thread write operation, Kern::ThreadWrite(), when writing to the target buffer. +*/ +EXPORT_C TInt DUsbClientController::GetStringDescriptor(DThread* aThread, TUint8 aIndex, TDes8& aString) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::GetStringDescriptor(%d)", aIndex)); + return iDescriptors.GetStringDescriptorTC(aThread, aIndex, aString); + } + + +/** Sets the aString argument to be a string descriptor at the specified index in the string + descriptor array. If a string descriptor already exists at that position then it will be replaced. + + @param aIndex The position of the string descriptor in the string descriptor array. + @param aThread A pointer to the thread the LDD requesting the setting of the string is running in. + @param aString A reference to a buffer which contains the string to be set (most likely located + user-side). + + @return KErrNone if successful, KErrArgument if aIndex is invalid, KErrNoMemory if no memory is available + to store the new string (an existing descriptor at that index will be preserved), or the return value of + the thread read operation, Kern::ThreadRead(), if reading from the source buffer goes wrong. +*/ +EXPORT_C TInt DUsbClientController::SetStringDescriptor(DThread* aThread, TUint8 aIndex, const TDes8& aString) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetStringDescriptor(%d)", aIndex)); + return iDescriptors.SetStringDescriptorTC(aThread, aIndex, aString); + } + + +/** Removes (deletes) the string descriptor at the specified index in the string descriptor array. + + @param aIndex The position of the string descriptor in the string descriptor array. + + @return KErrNone if successful, KErrNotFound if no string descriptor exists at the specified index. +*/ +EXPORT_C TInt DUsbClientController::RemoveStringDescriptor(TUint8 aIndex) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::RemoveStringDescriptor(%d)", aIndex)); + return iDescriptors.RemoveStringDescriptor(aIndex); + } + + +/** Queries the use of and endpoint resource. + + If the resource gets successfully allocated, it will be used from when the current bus transfer + has been completed. + + @param aClientId A pointer to the LDD querying the endpoint resource. + @param aEndpointNum The number of the endpoint. + @param aResource The endpoint resource to be queried. + + @return ETrue if the specified resource is in use at the endpoint, EFalse if not or if there was any error + during the execution of the function. +*/ +EXPORT_C TBool DUsbClientController::QueryEndpointResource(const DBase* /*aClientId*/, TInt aEndpointNum, + TUsbcEndpointResource aResource) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::QueryEndpointResource()")); + return iController.QueryEndpointResource(aEndpointNum, aResource); + } + + +EXPORT_C TInt DUsbClientController::EndpointPacketSize(const DBase* aClientId, TInt aEndpointNum) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::EndpointPacketSize(0x%08x, %d)", + aClientId, aEndpointNum)); + + const TUsbcInterfaceSet* const ifcset_ptr = ClientId2InterfacePointer(aClientId); + if (!ifcset_ptr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: interface or clientid not found")); + return -1; + } + const TUsbcInterface* const ifc_ptr = ifcset_ptr->iInterfaces[ifcset_ptr->iCurrentInterface]; + const RPointerArray& ep_array = ifc_ptr->iEndpoints; + const TInt n = ep_array.Count(); + for (TInt i = 0; i < n; i++) + { + const TUsbcLogicalEndpoint* const ep = ep_array[i]; + if (EpAddr2Idx(ep->iPEndpoint->iEndpointAddr) == static_cast(aEndpointNum)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Endpoint packet sizes: FS = %d HS = %d", + ep->iEpSize_Fs, ep->iEpSize_Hs)); + const TInt size = iHighSpeed ? ep->iEpSize_Hs : ep->iEpSize_Fs; + __KTRACE_OPT(KUSB, Kern::Printf(" Returning %d", size)); + return size; + } + } + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: endpoint not found")); + return -1; + } + +EXPORT_C TDfcQue* DUsbClientController::DfcQ(TInt /*aIndex*/) + { + return iControllerProperties.iDfcQueue; + } + +EXPORT_C void DUsbClientController::DumpRegisters() + { + return; + } + +EXPORT_C TInt DUsbClientController::SignalRemoteWakeup() + { + return iController.SignalRemoteWakeup(); + } + +EXPORT_C TBool DUsbClientController::CurrentlyUsingHighSpeed() + { + UsbShai::TSpeed speed = iController.DeviceOperatingSpeed(); + + return (speed == UsbShai::EHighSpeed)?ETrue:EFalse; + } + +// +// === USB Controller member function implementations - PSL API (public) =========================== +// + +/** Gets called by the PSL to register a newly created derived class controller object. + + @param aUdc The number of the new UDC. It should be 0 for the first (or only) UDC in the system, 1 for the + second one, and so forth. KUsbcMaxUdcs determines how many UDCs are supported. + + @return A pointer to the controller if successfully registered, NULL if aUdc out of (static) range. + + @publishedPartner @released +*/ +TInt DUsbClientController::RegisterUdc(TInt aUdc) + { + TInt err = KErrNone; + + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::RegisterUdc()")); + + if (aUdc < 0 || aUdc > (KUsbcMaxUdcs - 1)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: aUdc out of range (%d)", aUdc)); + return KErrInUse; + } + else + { + UsbClientController[aUdc] = this; + } + + return err; + } + + +// +// === USB Controller member function implementations - PSL API (protected) ======================== +// + +/** Initialises an instance of this class, which is the base class of the derived class (= PSL, which is + supposed to call this function). + + It does the following things: + + - disconnects the UDC from the bus, + - initialises the USB descriptor pool, uses data from the PSL (see function argument list) + - creates and initialises the basic USB device configuration + - initialises the array of physical endpoints + - initialises Ep0 structures (but doesn't configure & enable Ep0 yet) + - creates and installs the USB power handler + + @param aDeviceDesc A pointer to a valid standard USB device descriptor or NULL. The values initially + required in the descriptor follow from its constructor. The descriptor is not copied over, but rather this + pointer is queued directly into the descriptor pool. Must be writable memory. + + @param aConfigDesc A pointer to a valid standard USB configuration descriptor or NULL. The values + initially required in the descriptor follow from its constructor. The descriptor is not copied over, but + rather this pointer is queued directly into the descriptor pool. Must be writable memory. + + @param aLangId A pointer to a valid USB language ID (string) descriptor. The values initially required in + the descriptor follow from its constructor. The descriptor is not copied over, but rather this pointer is + queued directly into the descriptor pool. Must be writable memory. Other than the remaining four string + descriptors, this one is not optional. The reason is that the USB spec mandates a LangId descriptor as + soon as a single string descriptor gets returned by the device. So, even though the device might omit the + Manufacturer, Product, SerialNumber, and Configuration string descriptors, it is at this point not known + whether there will be any Interface string descriptors. Since any USB API user can create an interface + with an Interface string descriptor, we have to insist here on the provision of a LangId string + descriptor. (The PIL decides at run-time whether or not to return the LangId string descriptor to the + host, depending on whether there exist any string descriptors at that time.) + + @param aManufacturer A pointer to a valid USB string descriptor or NULL. The values initially required in + the descriptor follow from its constructor. The descriptor is not copied over, but rather this pointer is + queued directly into the descriptor pool. Must be writable memory. This descriptor will be referenced by + the iManufacturer field in the device descriptor. + + @param aProduct A pointer to a valid USB string descriptor or NULL. The values initially required in the + descriptor follow from its constructor. The descriptor is not copied over, but rather this pointer is + queued directly into the descriptor pool. Must be writable memory. This descriptor will be referenced by + the iProduct field in the device descriptor. + + @param aSerialNum A pointer to a valid USB string descriptor or NULL. The values initially required in the + descriptor follow from its constructor. The descriptor is not copied over, but rather this pointer is + queued directly into the descriptor pool. Must be writable memory. This descriptor will be referenced by + the iSerialNumber field in the device descriptor. + + @param aConfig A pointer to a valid USB string descriptor or NULL. The values initially required in the + descriptor follow from its constructor. The descriptor is not copied over, but rather this pointer is + queued directly into the descriptor pool. Must be writable memory. This descriptor will be referenced by + the iConfiguration field in the configuration descriptor. + + @param aOtgDesc A pointer to a valid USB OTG descriptor (if OTG is supported by this device and is to be + supported by the driver) or NULL. The values initially required in the descriptor follow from its + constructor. The descriptor is not copied over, but rather this pointer is queued directly into the + descriptor pool. Must be writable memory. + + @return EFalse, if USB descriptor pool initialisation fails, or if configuration creation fails, or if the + PSL reports more endpoints than the constant KUsbcMaxEndpoints permits, or if the Ep0 logical endpoint + creation fails, or if the creation of the power handler fails; ETrue, if base class object successfully + initialised. + + @publishedPartner @released +*/ +TBool DUsbClientController::Initialise(TUsbPeripheralDescriptorPool& aDescPool, + const UsbShai::TUsbPeripheralEndpointCaps* aEndpointCaps, + TInt aTotalEndpoints) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::InitialiseBaseClass()")); + + // We don't want the host to see us (at least not yet): + UsbDisconnect(); + + iDeviceTotalEndpoints = aTotalEndpoints; + + // Initialise USB descriptor pool + if (iDescriptors.Init(aDescPool.iDeviceDesc, + aDescPool.iConfigDesc, + aDescPool.iLangId, + aDescPool.iManufacturer, + aDescPool.iProduct, + aDescPool.iSerialNum, + aDescPool.iConfig, + aDescPool.iOtgDesc) != KErrNone) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Error: Descriptor initialization failed")); + return EFalse; + } + + if (aDescPool.iOtgDesc) + { + iOtgSupport = ETrue; + iOtgFuncMap = aDescPool.iOtgDesc->DescriptorData()[2]; + } + + // Some member variables + iSelfPowered = aDescPool.iConfigDesc->Byte(7) & (1 << 6); // Byte 7: bmAttributes + iRemoteWakeup = aDescPool.iConfigDesc->Byte(7) & (1 << 5); + + if (iControllerProperties.iControllerCaps & UsbShai::KDevCapHighSpeed) + { + if (iDescriptors.InitHs() != KErrNone) + { + return EFalse; + } + } + + // Create and initialise our first (and only) configuration + TUsbcConfiguration* config = new TUsbcConfiguration(1); + if (!config) + { + return EFalse; + } + iConfigs.Append(config); + + // Initialise the array of physical endpoints + __KTRACE_OPT(KUSB, Kern::Printf(" DeviceTotalEndpoints: %d", aTotalEndpoints)); + + // KUsbcMaxEndpoints doesn't include ep 0 + if ((aTotalEndpoints > (KUsbcMaxEndpoints + 2)) || + ((aTotalEndpoints * sizeof(TUsbcPhysicalEndpoint)) > sizeof(iRealEndpoints))) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: too many endpoints! (change KUsbcMaxEndpoints: %d)", + KUsbcMaxEndpoints)); + return EFalse; + } + + for (TInt i = 0; i < aTotalEndpoints; ++i) + { + iRealEndpoints[i].iEndpointAddr = EpIdx2Addr(i); + + __KTRACE_OPT(KUSB, Kern::Printf(" aEndpointCaps[%02d] - iTypes: 0x%08x iSizes: 0x%08x", + i, aEndpointCaps[i].iTypesAndDir, aEndpointCaps[i].iSizes)); + + iRealEndpoints[i].iCaps = aEndpointCaps[i]; + + // Reset revered bytes to zero + iRealEndpoints[i].iCaps.iReserved[0] = 0; + iRealEndpoints[i].iCaps.iReserved[1] = 0; + + if ((i > 1) && (aEndpointCaps[i].iTypesAndDir != UsbShai::KUsbEpNotAvailable)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" --> UsableEndpoint: #%d", i)); + iDeviceUsableEndpoints++; + } + } + + // Initialise Ep0 structures (logical endpoints are numbered 1..KMaxEndpointsPerClient, + // and virtual 0 is real 0): + // -- Ep0 OUT + iEp0MaxPacketSize = MaxEndpointPacketSize(aEndpointCaps[0].iSizes); + __KTRACE_OPT(KUSB, Kern::Printf(" using Ep0 maxpacketsize of %d bytes", iEp0MaxPacketSize)); + + TUsbcEndpointInfo info(UsbShai::KUsbEpTypeControl, UsbShai::KUsbEpDirOut, 0); + TUsbcLogicalEndpoint* ep = NULL; + + info.iSize = iEp0MaxPacketSize; + ep = new TUsbcLogicalEndpoint(this, 0, info, NULL, &iRealEndpoints[KEp0_Out]); + if (!ep) + { + return EFalse; + } + + __KTRACE_OPT(KUSB, Kern::Printf(" creating ep: mapping real ep %d --> logical ep 0", KEp0_Out)); + iRealEndpoints[KEp0_Out].iLEndpoint = ep; + + // -- Ep0 IN + info.iDir = UsbShai::KUsbEpDirIn; + ep = new TUsbcLogicalEndpoint(this, 0, info, NULL, &iRealEndpoints[KEp0_In]); + if (!ep) + { + delete iRealEndpoints[KEp0_Out].iLEndpoint; + iRealEndpoints[KEp0_Out].iLEndpoint = NULL; + return EFalse; + } + __KTRACE_OPT(KUSB, Kern::Printf(" creating ep: mapping real ep %d --> logical ep 0", KEp0_In)); + iRealEndpoints[KEp0_In].iLEndpoint = ep; + + iPowerHandler = new DUsbcPowerHandler(this); + if (!iPowerHandler) + { + delete iRealEndpoints[KEp0_Out].iLEndpoint; + iRealEndpoints[KEp0_Out].iLEndpoint = NULL; + delete iRealEndpoints[KEp0_In].iLEndpoint; + iRealEndpoints[KEp0_In].iLEndpoint = NULL; + return EFalse; + } + iPowerHandler->Add(); + + return ETrue; + } + +/** The standard constructor for this class. + + @publishedPartner @released + */ +DUsbClientController::DUsbClientController(UsbShai::MPeripheralControllerIf& aPeripheralControllerIf, + const UsbShai::TPeripheralControllerProperties& aProperties, + TBool aIsOtgPort) + : iEp0ReceivedNonStdRequest(EFalse), + iRmWakeupStatus_Enabled(EFalse), + iEp0_RxBuf(), + iDeviceTotalEndpoints(0), + iDeviceUsableEndpoints(0), + iDeviceState(UsbShai::EUsbPeripheralStateUndefined), + iDeviceStateB4Suspend(UsbShai::EUsbPeripheralStateUndefined), + iSelfPowered(EFalse), + iRemoteWakeup(EFalse), + iHardwareActivated(EFalse), + iOtgSupport(EFalse), + iOtgFuncMap(0), + iHighSpeed(EFalse), + iEp0MaxPacketSize(0), + iEp0ClientId(NULL), + iEp0DataReceived(0), + iEp0WritePending(EFalse), + iEp0ClientDataTransmitting(EFalse), + iEp0DeviceControl(NULL), + iDescriptors(iEp0_TxBuf), + iCurrentConfig(0), + iConfigs(1), + iRealEndpoints(), + iEp0_TxBuf(), + iEp0_RxExtraCount(0), + iEp0_TxNonStdCount(0), + iEp0ReadRequestCallbacks(_FOFF(TUsbcRequestCallback, iLink)), + iClientCallbacks(_FOFF(TUsbcClientCallback, iLink)), + iStatusCallbacks(_FOFF(TUsbcStatusCallback, iLink)), + iEpStatusCallbacks(_FOFF(TUsbcEndpointStatusCallback, iLink)), + iOtgCallbacks(_FOFF(TUsbcOtgFeatureCallback, iLink)), + iReconnectTimer(ReconnectTimerCallback, this), + iUsbLock(TSpinLock::EOrderGenericIrqLow3), + iController(aPeripheralControllerIf), + iControllerProperties(aProperties), + iIsOtgPort(aIsOtgPort), + iOtgObserver(NULL), + iConTransferMgr(NULL), + iLastError(EFalse), + iSetupPacketPending(EFalse), + iCommonDfcQThread(NULL), + iPowerUpDfc(PowerUpDfc, this, 3), + iPowerDownDfc(PowerDownDfc, this, 3), + iDeviceEventNotifyDfc(DeviceEventNotifyDfc,this,3), + iThreadContextFinder(ThreadContextFinderDfc,this,3), + iStandby(EFalse), + iStackIsActive(EFalse), + iClientSupportReady(EFalse), + iUsbResetDeferred(EFalse), + iEnablePullUpOnDPlus(NULL), + iDisablePullUpOnDPlus(NULL), + iOtgContext(NULL) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DUsbClientController()")); + + iLastError = KErrNone; + +#ifndef SEPARATE_USB_DFC_QUEUE + iPowerUpDfc.SetDfcQ(Kern::DfcQue0()); + iPowerDownDfc.SetDfcQ(Kern::DfcQue0()); +#endif // SEPARATE_USB_DFC_QUEUE + + for (TInt i = 0; i < KUsbcEpArraySize; i++) + iRequestCallbacks[i] = NULL; + } + +TInt DUsbClientController::Construct() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::Construct")); + + // Setup the state machines of ep0 + TInt err = SetupEp0StateMachine(); + if( err != KErrNone) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Can not setup state machines, exit")); + return err; + } + +#ifdef SEPARATE_USB_DFC_QUEUE + iPowerUpDfc.SetDfcQ(iControllerProperties.iDfcQueue); + iPowerDownDfc.SetDfcQ(iControllerProperties.iDfcQueue); +#endif // SEPARATE_USB_DFC_QUEUE + + iDeviceEventNotifyDfc.SetDfcQ(iControllerProperties.iDfcQueue); + iThreadContextFinder.SetDfcQ(iControllerProperties.iDfcQueue); + + // Register + if( RegisterUdc(0) != KErrNone) + { + // This is the only reason. + return KErrInUse; + } + + __KTRACE_OPT(KUSB, Kern::Printf(" peripheral controller registered")); + TUsbPeripheralDescriptorPool descPool; + + if( CreateDescriptors(descPool) == KErrNone) + { + __KTRACE_OPT(KUSB, Kern::Printf(" descriptors created")); + + // Initialise the array of physical endpoints + __KTRACE_OPT(KUSB, Kern::Printf(" initialising PIL ")); + TBool initOk = Initialise(descPool, + iControllerProperties.iDeviceEndpointCaps, + iControllerProperties.iDeviceTotalEndpoints); + + // Let UDC has a changes to know the callback interface is ready. + // Any further initialization/startup/preparation etc can be performed now. + if ( initOk ) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Initializing PSL ")); + + // Set Rx buffer for endpoint zero + iController.SetEp0RxBuffer(iEp0_RxBuf,KUsbcBufSzControl); + + // Set pil callback interface for PSL. + iController.SetPilCallbackInterface(*this); + + } + else + { + return KErrNoMemory; + } + } + + // Register ourself as the ONLY one client of charger detection observer + gChargerObsever = this; + + // In case the charger detector already registered, start monitor + // Charger type notifications + if( gChargerDetector != NULL ) + { + gChargerDetector->SetChargerDetectorObserver(*gChargerObsever); + } + + iThreadContextFinder.Enque(); + + return KErrNone; + } + +// This function doesn't consider the situation of OOM. +// Because, this function will be call during extension's entry point, +// There is no way to start up phone successfully if anything failed anyway... +TInt DUsbClientController::SetupEp0StateMachine() + { + // Create the state machine first + __ASSERT_DEBUG((iConTransferMgr == NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + iConTransferMgr = new DControlTransferManager(*this); + if(iConTransferMgr == 0) + { + return KErrNoMemory; + } + + // Add UsbShai::EControlTransferStageSetup stage machine + TControlStageSm* stageSm = new DSetupStageSm(*iConTransferMgr); + __ASSERT_DEBUG((stageSm != NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + if(stageSm != NULL) + { + iConTransferMgr->AddState(UsbShai::EControlTransferStageSetup,*stageSm); + } + else + { + return KErrNoMemory; + } + + // Add EControlTransferStageDataOut stage state machine + stageSm = new DDataOutStageSm(*iConTransferMgr); + __ASSERT_DEBUG((stageSm != NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + if(stageSm != NULL) + { + iConTransferMgr->AddState(UsbShai::EControlTransferStageDataOut,*stageSm); + } + else + { + // we don't need bother to delete the previous allocated memory + // system can not bootup if we return error + return KErrNoMemory; + } + + // Add EControlTransferStageStatusIn stage state machine + stageSm = new DStatusInStageSm(*iConTransferMgr); + __ASSERT_DEBUG((stageSm != NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + if(stageSm != NULL) + { + iConTransferMgr->AddState(UsbShai::EControlTransferStageStatusIn,*stageSm); + } + else + { + return KErrNoMemory; + } + + // Add EControlTransferStageDataIn stage state machine + stageSm = new DDataInStageSm(*iConTransferMgr); + __ASSERT_DEBUG((stageSm != NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + if(stageSm != NULL) + { + iConTransferMgr->AddState(UsbShai::EControlTransferStageDataIn,*stageSm); + } + else + { + return KErrNoMemory; + } + + // Add EControlTransferStageStatusOut stage state machine + stageSm = new DStatusOutStageSm(*iConTransferMgr); + __ASSERT_DEBUG((stageSm != NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + if(stageSm != NULL) + { + iConTransferMgr->AddState(UsbShai::EControlTransferStageStatusOut,*stageSm); + } + else + { + return KErrNoMemory; + } + + return KErrNone; + } + +// --------------------------------------------------------------------------- +// From MUsbPeripheralPilCallbackIf. +// Enable the peripheral stack +// --------------------------------------------------------------------------- +// +void DUsbClientController::EnablePeripheralStack() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::EnablePeripheralStack")); + + if (iStackIsActive) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Already enabled - returning")); + return; + } + + // Mark stack is enabled, Waiting upper application to power controller + // Anyway, this will lead us to attached state + iStackIsActive = ETrue; + NextDeviceState(UsbShai::EUsbPeripheralStateAttached); + + // If hardware is not activated yet, do it here. + if(iClientSupportReady && !iHardwareActivated) + { + // PowerUpUdc only do Activating Hardware when there are at least 1 + // Iterface registered. + PowerUpUdc(); + } + + } + + +// --------------------------------------------------------------------------- +// From MUsbPeripheralPilCallbackIf. +// Disable the peripheral stack +// --------------------------------------------------------------------------- +// +void DUsbClientController::DisablePeripheralStack() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DisablePeripheralStack")); + + if (!iStackIsActive) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Already disabled - returning")); + return; + } + + // Reset OTG features, leave attributes as is (just as in USB Reset case) + // (OTG spec 1.3 sections 6.5.x all say "... on a bus reset or at the end + // of a session." VBus drop is the end of a session.) + iOtgFuncMap &= KUsbOtgAttr_SrpSupp | KUsbOtgAttr_HnpSupp; + OtgFeaturesNotify(); + // Tear down the current configuration (if any) + ChangeConfiguration(0); + + if (iDeviceState != UsbShai::EUsbPeripheralStateUndefined) + { + // Not being in state UNDEFINED implies that the cable is inserted. + if (iHardwareActivated) + { + NextDeviceState(UsbShai::EUsbPeripheralStatePowered); + } + // (If the hardware is NOT activated at this point, we can only be in + // state UsbShai::EUsbPeripheralStateAttached, so we don't have to move to it.) + } + DeActivateHardwareController(); // turn off UDC altogether + iStackIsActive = EFalse; + // Notify registered clients on the user side about a USB device state + // change event and a transition to the "Undefined" state. + // Note: the state should be changed to "Undefined" before calling RunClientCallbacks(), + // otherwise the "Undefined" state will probably be lost. + NextDeviceState(UsbShai::EUsbPeripheralStateUndefined); + // Complete all pending requests, returning KErrDisconnected + RunClientCallbacks(); + } + + +// --------------------------------------------------------------------------- +// From MUsbPeripheralPilCallbackIf. +// Set the OTG Observer +// --------------------------------------------------------------------------- +// +void DUsbClientController::SetOtgObserver(MUsbOtgPeripheralObserverIf* aObserver) + { + iOtgObserver = aObserver; + } + + +/** This function gets called by the PSL upon detection of either of the following events: + - USB Reset, + - USB Suspend event, + - USB Resume signalling, + - The USB cable has been attached (inserted) or detached (removed). + + @param anEvent An enum denoting the event that has occured. + + @return KErrArgument if the event is not recognized, otherwise KErrNone. + + @publishedPartner @released +*/ +TInt DUsbClientController::DeviceEventNotification(UsbShai::TUsbPeripheralEvent anEvent) + { + TInt err = KErrNone; + + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeviceEventNotification(anEvent=%d)", anEvent)); + + switch (anEvent) + { + case UsbShai::EUsbEventSuspend: + case UsbShai::EUsbEventResume: + case UsbShai::EUsbEventReset: + case UsbShai::EUsbEventVbusRisen: + case UsbShai::EUsbEventVbusFallen: + { + TInt nkern_curr_ctx= NKern::CurrentContext(); + + if( (nkern_curr_ctx != NKern::EInterrupt) && (nkern_curr_ctx != NKern::EIDFC)) + { + // Normal context + __ASSERT_DEBUG((iCommonDfcQThread != NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + if(iCommonDfcQThread == &(Kern::CurrentThread().iNThread)) + { + // we already in the correct context, just run processes here directly. + __KTRACE_OPT(KUSB, Kern::Printf(" Correct thread context")); + ProcessDeviceEventNotification(anEvent); + } + else + { + // we're in a normal thread, but it is not the same as the DfcQ context + // passed by PSL, queue it + __KTRACE_OPT(KUSB, Kern::Printf(" Incorrect thread context")); + iDevEventQueue.FifoAdd(anEvent); + iDeviceEventNotifyDfc.Enque(); + } + } + else + { + // We're in a ISR or IDFC context + __KTRACE_OPT(KUSB, Kern::Printf(" ISR|IDFC context")); + iDevEventQueue.FifoAdd(anEvent); + iDeviceEventNotifyDfc.Add(); + } + } + break; + + default: + err = KErrArgument; + } + + return err; + } + + +/** This function gets called by the PSL upon completion of a pending data transfer request. + + This function is not to be used for endpoint zero completions (use Ep0RequestComplete instead). + + @param aCallback A pointer to a data transfer request callback structure which was previously passed to + the PSL in a SetupReadBuffer() or SetupWriteBuffer() call. + + @publishedPartner @released +*/ +void DUsbClientController::EndpointRequestComplete(UsbShai::TUsbPeripheralRequest* aCallback) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::EndpointRequestComplete(%p)", aCallback)); + + TUsbcRequestCallback* cb = static_cast(aCallback); + // 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). + + // We don't test aCallback for NULL here (and therefore risk a crash) + // because the PSL should never give us a NULL argument. If it does it + // means the PSL is buggy and ought to be fixed. + ProcessDataTransferDone(*cb); + } + + +/** This function should be called by the PSL after reception of an Ep0 + SET_FEATURE request with a feature selector of either {b_hnp_enable, + a_hnp_support, a_alt_hnp_support}, but only when that Setup packet is not + handed up to the PIL (for instance because it is auto-decoded and + 'swallowed' by the UDC hardware). + + @param aHnpState A bitmask indicating the present state of the three OTG + feature selectors as follows: + + bit.0 == a_alt_hnp_support + bit.1 == a_hnp_support + bit.2 == b_hnp_enable + + @see DUsbClientController::ProcessSetClearDevFeature() + + @publishedPartner @released +*/ +void DUsbClientController::HandleHnpRequest(TInt aHnpState) +// This function is 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). + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::HandleHnpRequest(%d)", aHnpState)); + + if (!iOtgSupport) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Request only supported on a OTG device")); + return; + } + if (!(iOtgFuncMap & KUsbOtgAttr_HnpSupp)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Request only valid if OTG device supports HNP")); + return; + } + // (case KUsbFeature_B_HnpEnable:) + if (aHnpState & 0x04) + { + iOtgFuncMap |= KUsbOtgAttr_B_HnpEnable; + } + // (case KUsbFeature_A_HnpSupport:) + if (aHnpState & 0x02) + { + iOtgFuncMap |= KUsbOtgAttr_A_HnpSupport; + } + // (case KUsbFeature_A_AltHnpSupport:) + if (aHnpState & 0x01) + { + iOtgFuncMap |= KUsbOtgAttr_A_AltHnpSupport; + } + OtgFeaturesNotify(); + } + +void DUsbClientController::GetEp0RxBufferInfo(TUint8*& aBuffer, TInt& aBufferLen) + { + aBuffer = iEp0_RxBuf; + aBufferLen = KUsbcBufSzControl; + } + +UsbShai::TUsbPeripheralState DUsbClientController::DeviceStatus() const + { + return iDeviceState; + } + +TBool DUsbClientController::Ep0ReceivedNonStdRequest() + { + return iEp0ReceivedNonStdRequest; + } + +/** This function gets called by the PSL upon completion of a pending endpoint zero data transfer request. + + @param aRealEndpoint Either 0 for Ep0 OUT (= Read), or 1 for Ep0 IN (= Write). + @param aCount The number of bytes received or transmitted, respectively. + @param aError The error status of the completed transfer request. Can be KErrNone if no error, KErrCancel + if transfer was cancelled, or KErrPrematureEnd if a premature status end was encountered. + + @return KErrNone if no error during transfer completion processing, KErrGeneral if the request was a read & + a Setup packet was received & the recipient for that packet couldn't be found (invalid packet: Ep0 has been + stalled), KErrNotFound if the request was a read & the recipient for that packet (Setup or data) _was_ + found - however no read had been set up by that recipient (this case should be used by the PSL to disable + the Ep0 interrupt at that point and give the LDD time to set up a new Ep0 read; once the 'missing' read + was set up either Ep0ReceiveProceed or Ep0ReadSetupPktProceed will be called by the PIL). + + @publishedPartner @released +*/ +TInt DUsbClientController::Ep0RequestComplete(TInt aRealEndpoint, + TInt aCount, + TInt aError, + UsbShai::TControlPacketType aPktType) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::Ep0RequestComplete(%d)", aRealEndpoint)); + + iLastError = KErrNone; + + iConTransferMgr->Ep0RequestComplete(iEp0_RxBuf,aCount,aError,aPktType); + + __KTRACE_OPT(KUSB, Kern::Printf(" iLastError(%d)", iLastError)); + + if(iEp0WritePending == EFalse) + { + iConTransferMgr->SetupEndpointZeroRead(); + } + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::Ep0RequestComplete")); + return iLastError; + } + +/** This function should be called by the PSL once the UDC (and thus the USB device) is in the Address state. + + @publishedPartner @released +*/ +void DUsbClientController::MoveToAddressState() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::MoveToAddressState()")); + + // 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). + + NextDeviceState(UsbShai::EUsbPeripheralStateAddress); + } + + +TBool DUsbClientController::CreateDescriptors(TUsbPeripheralDescriptorPool& aOutput) + { + TInt errCode = KErrNone; + + // Create all the string descriptors + TUsbcDeviceDescriptor* deviceDesc = TUsbcDeviceDescriptor::New( + 0, // aDeviceClass, will be changed later by upper app + 0, // aDeviceSubClass, will be changed later by upper app + 0, // aDeviceProtocol, will be changed later by upper app + iControllerProperties.iMaxEp0Size , // aMaxPacketSize0 + KUsbVendorId, // aVendorId + KUsbProductId , // aProductId + iControllerProperties.iDeviceRelease, // aDeviceRelease + KUsbNumberOfConfiguration);// aNumConfigurations + __ASSERT_DEBUG( (deviceDesc != NULL), Kern::Fault( "USB PSL Out of memory, deviceDesc", __LINE__ )); + + TUsbcConfigDescriptor* configDesc = TUsbcConfigDescriptor::New( + 1, // Only one configruation is supported current. + EFalse, // at here, we always mark it as bus powered. + (iControllerProperties.iControllerCaps & UsbShai::KDevCapRemoteWakeupSupport)?ETrue:EFalse, // remote wakeup + 100); // 100 is a default value, thise value will be changed by + + __ASSERT_DEBUG( (configDesc != NULL), Kern::Fault( "USB PSL Out of memory, configDesc", __LINE__ )); + + TUsbcLangIdDescriptor* stringDescLang = TUsbcLangIdDescriptor::New(KUsbLangId); + __ASSERT_DEBUG( (stringDescLang != NULL), Kern::Fault( "USB PSL Out of memory, stringDescLang", __LINE__ )); + + // Default manufacturer string + TPtrC8 aString; + aString.Set(reinterpret_cast(KStringManufacturer), sizeof(KStringManufacturer) - 2); + TUsbcStringDescriptor* stringDescManu = TUsbcStringDescriptor::New(aString); + __ASSERT_DEBUG( (stringDescManu != NULL), Kern::Fault( "USB PSL Out of memory, stringDescManu", __LINE__ )); + + // Default product name string + aString.Set(reinterpret_cast(KStringProduct), sizeof(KStringProduct) - 2); + TUsbcStringDescriptor* stringDescProd = TUsbcStringDescriptor::New(aString); + __ASSERT_DEBUG( (stringDescProd != NULL), Kern::Fault( "USB PSL Out of memory, stringDescProd", __LINE__ )); + + // Default configuration name string + aString.Set(reinterpret_cast(KStringConfig), sizeof(KStringConfig) - 2); + TUsbcStringDescriptor* stringDescConf = TUsbcStringDescriptor::New(aString); + __ASSERT_DEBUG( (stringDescConf != NULL), Kern::Fault( "USB PSL Out of memory, stringDescConf", __LINE__ )); + + // Default serial bumber string + aString.Set(reinterpret_cast(KStringSerial), sizeof(KStringSerial) - 2); + TUsbcStringDescriptor* stringSerial = TUsbcStringDescriptor::New(aString); + __ASSERT_DEBUG( (stringSerial != NULL), Kern::Fault( "USB PSL Out of memory, stringDescConf", __LINE__ )); + + TUsbcOtgDescriptor* otgDesc = NULL; + + // In an OTG-environment, we also need to create the OTG + // descriptor. The PSL supports both HNP and SRP and hence we + // report support for them. Upper layers will override the + // descriptors anyway. + if (iIsOtgPort) + { + TBool srpSupported = (iControllerProperties.iControllerCaps & UsbShai::KDevCapSrpSupport)?ETrue:EFalse; + TBool hnpSupported = (iControllerProperties.iControllerCaps & UsbShai::KDevCapHnpSupport)?ETrue:EFalse; + + otgDesc = TUsbcOtgDescriptor::New(srpSupported, + hnpSupported); + + __ASSERT_DEBUG( (otgDesc != NULL), Kern::Fault( "USB PSL Out of memory, otgDesc", __LINE__ )); + } + + if( (deviceDesc != NULL) && + (configDesc != NULL) && + (stringDescLang != NULL) && + (stringDescManu != NULL) && + (stringDescProd != NULL) && + (stringDescConf != NULL) && + ((!iIsOtgPort) || (iIsOtgPort && (otgDesc != NULL)))) + { + aOutput.iDeviceDesc = deviceDesc; + aOutput.iConfigDesc = configDesc; + aOutput.iLangId = stringDescLang; + aOutput.iManufacturer = stringDescManu; + aOutput.iProduct = stringDescProd; + aOutput.iConfig = stringDescConf; + aOutput.iSerialNum = stringSerial; + aOutput.iOtgDesc = otgDesc; + } + else + { + if( deviceDesc != NULL ) + { + delete deviceDesc; + } + + if( configDesc != NULL ) + { + delete configDesc; + } + + if( stringDescLang != NULL ) + { + delete stringDescLang; + } + + if( stringDescManu != NULL ) + { + delete stringDescManu; + } + + if( stringDescProd != NULL ) + { + delete stringDescProd; + } + + if( stringDescConf != NULL ) + { + delete stringDescConf; + } + + if( stringSerial != NULL ) + { + delete stringSerial; + } + + if( otgDesc != NULL ) + { + delete otgDesc; + } + + errCode = KErrNoMemory; + } + + // We don't support serial number + aOutput.iSerialNum = NULL; + + return errCode; + } + +// +// === USB Controller member function implementations - Internal utility functions (private) ======= +// + +TInt DUsbClientController::DeRegisterClientCallback(const DBase* aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeRegisterClientCallback()")); + __ASSERT_DEBUG((aClientId != NULL), Kern::Fault(KUsbPILPanicCat, __LINE__)); + TSglQueIter iter(iClientCallbacks); + TUsbcClientCallback* p; + while ((p = iter++) != NULL) + if (p->Owner() == aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf(" removing ClientCallback @ 0x%x", p)); + iClientCallbacks.Remove(*p); + return KErrNone; + } + __KTRACE_OPT(KUSB, Kern::Printf(" Client not found")); + return KErrNotFound; + } + + +TBool DUsbClientController::CheckEpAvailability(TInt aEndpointsUsed, + const TUsbcEndpointInfoArray& aEndpointData, + TInt aIfcNumber) const + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::CheckEpAvailability()")); + if (aEndpointsUsed > KMaxEndpointsPerClient) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: too many endpoints claimed (%d)", aEndpointsUsed)); + return EFalse; + } + TBool reserve[KUsbcEpArraySize]; // iDeviceTotalEndpoints can be equal to 32 + memset(reserve, EFalse, sizeof(reserve)); // reset the array + for (TInt i = 0; i < aEndpointsUsed; ++i) + { + __KTRACE_OPT(KUSB, Kern::Printf(" checking for (user) endpoint #%d availability...", i + 1)); + TInt j = 2; + while (j < iDeviceTotalEndpoints) + { + if ((iRealEndpoints[j].EndpointSuitable(&aEndpointData[i], aIfcNumber)) && + (reserve[j] == EFalse)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" ---> found suitable endpoint: RealEndpoint #%d", j)); + reserve[j] = ETrue; // found one: mark this ep as reserved + break; + } + __KTRACE_OPT(KUSB, Kern::Printf(" -> endpoint not suitable: RealEndpoint #%d", j)); + j++; + } + if (j == iDeviceTotalEndpoints) + { + return EFalse; + } + } + return ETrue; + } + + +TUsbcInterface* DUsbClientController::CreateInterface(const DBase* aClientId, TInt aIfc, TUint32 aFeatureWord) +// We know that 9.2.3 says: "Interfaces are numbered from zero to one less than the number of +// concurrent interfaces supported by the configuration." But since we permit the user to +// change interface numbers, we can neither assume nor enforce anything about them here. + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::CreateInterface(x, aIfc=%d)", aIfc)); + TUsbcInterfaceSet* ifcset_ptr = NULL; + TInt ifcset = ClientId2InterfaceNumber(aClientId); + TBool new_ifc; + if (ifcset < 0) + { + // New interface(set), so we need to find a number for it. + new_ifc = ETrue; + const TInt num_ifcsets = iConfigs[0]->iInterfaceSets.Count(); + if (num_ifcsets == 255) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Too many interfaces already exist: 255")); + return NULL; + } + // Find the smallest interface number that has not yet been used. + for (ifcset = 0; ifcset < 256; ++ifcset) + { + TBool n_used = EFalse; + for (TInt i = 0; i < num_ifcsets; ++i) + { + if ((iConfigs[0]->iInterfaceSets[i]->iInterfaceNumber) == ifcset) + { + __KTRACE_OPT(KUSB, Kern::Printf(" interface number %d already used", ifcset)); + n_used = ETrue; + break; + } + } + if (!n_used) + { + break; + } + } + if (ifcset == 256) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: no available interface number found")); + return NULL; + } + // append the ifcset + __KTRACE_OPT(KUSB, Kern::Printf(" creating new InterfaceSet %d first", ifcset)); + if (aIfc != 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: invalid interface setting number (1): %d", aIfc)); + return NULL; + } + if ((ifcset_ptr = new TUsbcInterfaceSet(aClientId, ifcset)) == NULL) + { + __KTRACE_OPT(KPANIC, + Kern::Printf(" Error: new TUsbcInterfaceSet(aClientId, ifcset_num) failed")); + return NULL; + } + iConfigs[0]->iInterfaceSets.Append(ifcset_ptr); + } + else /* if (ifcset_num >= 0) */ + { + // use an existent ifcset + new_ifc = EFalse; + __KTRACE_OPT(KUSB, Kern::Printf(" using existing InterfaceSet %d", ifcset)); + ifcset_ptr = InterfaceNumber2InterfacePointer(ifcset); + if (aIfc != ifcset_ptr->iInterfaces.Count()) + { + // 9.2.3: "Alternate settings range from zero to one less than the number of alternate + // settings for a specific interface." (Thus we can here only append a setting.) + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: invalid interface setting number (2): %d", aIfc)); + return NULL; + } + // Check whether the existing interface belongs indeed to this client + if (ifcset_ptr->iClientId != aClientId) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: iClientId (%p) != aClientId (%p)", + ifcset_ptr->iClientId, aClientId)); + return NULL; + } + } + const TBool no_ep0_requests = aFeatureWord & KUsbcInterfaceInfo_NoEp0RequestsPlease; + TUsbcInterface* const ifc_ptr = new TUsbcInterface(ifcset_ptr, aIfc, no_ep0_requests); + if (!ifc_ptr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: new TUsbcInterface(ifcset, aIfc) failed")); + if (new_ifc) + { + DeleteInterfaceSet(ifcset); + } + return NULL; + } + ifcset_ptr->iInterfaces.Append(ifc_ptr); + return ifc_ptr; + } + + +#define RESET_SETTINGRESERVE \ + for (TInt i = start_ep; i < iDeviceTotalEndpoints; i++) \ + { \ + if (iRealEndpoints[i].iSettingReserve) \ + iRealEndpoints[i].iSettingReserve = EFalse; \ + } \ + +TInt DUsbClientController::CreateEndpoints(TUsbcInterface* aIfc, TInt aEndpointsUsed, + const TUsbcEndpointInfoArray& aEndpointData, + TInt aRealEpNumbers[]) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::CreateEndpoints()")); + const TInt ifc_num = aIfc->iInterfaceSet->iInterfaceNumber; + const TInt start_ep = 2; + for (TInt i = 0; i < aEndpointsUsed; ++i) + { + for (TInt j = start_ep; j < iDeviceTotalEndpoints; ++j) + { + if (iRealEndpoints[j].EndpointSuitable(&aEndpointData[i], ifc_num)) + { + // Logical endpoints are numbered 1..KMaxEndpointsPerClient (virtual 0 is real 0 and 1) + TUsbcLogicalEndpoint* const ep = new TUsbcLogicalEndpoint(this, i + 1, aEndpointData[i], + aIfc, &iRealEndpoints[j]); + if (!ep) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: new TUsbcLogicalEndpoint() failed")); + aIfc->iEndpoints.ResetAndDestroy(); + RESET_SETTINGRESERVE; + return KErrNoMemory; + } + aIfc->iEndpoints.Append(ep); + // Check on logical endpoint's sizes for compliance with special restrictions. + if (aIfc->iSettingCode == 0) + { + // For details see last paragraph of 5.7.3 "Interrupt Transfer Packet Size Constraints". + if ((ep->iInfo.iType == UsbShai::KUsbEpTypeInterrupt) && (ep->iEpSize_Hs > 64)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Warning: INT ep HS size = %d on default ifc setting", + ep->iEpSize_Hs)); + __KTRACE_OPT(KPANIC, Kern::Printf(" (should be <= 64)")); + } + // For details see last paragraph of 5.6.3 "Isochronous Transfer Packet Size Constraints". + else if ((ep->iInfo.iType == UsbShai::KUsbEpTypeIsochronous) && (ep->iInfo.iSize > 0)) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Warning: ISO ep size = %d on default ifc setting", + ep->iInfo.iSize)); + __KTRACE_OPT(KPANIC, Kern::Printf(" (should be zero or ep non-existent)")); + } + } + // If the endpoint doesn't support DMA (now or never) the next operation + // will be a successful no-op. + /* + const TInt r = OpenDmaChannel(j); + if (r != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Opening of DMA channel failed")); + aIfc->iEndpoints.ResetAndDestroy(); + RESET_SETTINGRESERVE; + return r; + } + */ + __KTRACE_OPT(KUSB, Kern::Printf(" creating ep: mapping real ep %d -> logical ep %d", + j, i + 1)); + iRealEndpoints[j].iIfcNumber = &aIfc->iInterfaceSet->iInterfaceNumber; + iRealEndpoints[j].iSettingReserve = ETrue; + __KTRACE_OPT(KUSB, + Kern::Printf(" ep->iInfo: iType=0x%x iDir=0x%x iSize=%d iInterval=%d", + ep->iInfo.iType, ep->iInfo.iDir, ep->iInfo.iSize, + ep->iInfo.iInterval)); + __KTRACE_OPT(KUSB, + Kern::Printf(" ep->iInfo: iInterval_Hs=%d iTransactions=%d iExtra=%d", + ep->iInfo.iInterval_Hs, ep->iInfo.iTransactions, + ep->iInfo.iExtra)); + // Store real endpoint numbers: + // array[x] holds the number for logical ep x. + aRealEpNumbers[i + 1] = j; + break; + } + } + } + aRealEpNumbers[0] = 0; // ep0: 0. + __KTRACE_OPT(KUSB,{ + Kern::Printf(" Endpoint Mapping for Interface %d / Setting %d:", ifc_num, aIfc->iSettingCode); + Kern::Printf("Logical | Real"); + Kern::Printf("Endpoint | Endpoint"); + for (TInt ep = 0; ep <= aEndpointsUsed; ++ep) Kern::Printf(" %2d %3d",ep, aRealEpNumbers[ep]); + }); + RESET_SETTINGRESERVE; + return KErrNone; + } + + +TInt DUsbClientController::SetupIfcDescriptor(TUsbcInterface* aIfc, TUsbcClassInfo& aClass, DThread* aThread, + TDesC8* aString, const TUsbcEndpointInfoArray& aEndpointData) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::SetupIfcDescriptor()")); + + // Interface descriptor + TUsbcDescriptorBase* d = TUsbcInterfaceDescriptor::New(aIfc->iInterfaceSet->iInterfaceNumber, + aIfc->iSettingCode, + aIfc->iEndpoints.Count(), + aClass); + if (!d) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for ifc desc failed.")); + return KErrNoMemory; + } + iDescriptors.InsertDescriptor(d); + + // Interface string descriptor + if (aString) + { + // we don't know the length of the string, so we have to allocate memory dynamically + TUint strlen = Kern::ThreadGetDesLength(aThread, aString); + if (strlen > KUsbStringDescStringMaxSize) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Warning: $ descriptor too long - string will be truncated")); + strlen = KUsbStringDescStringMaxSize; + } + HBuf8* const stringbuf = HBuf8::New(strlen); + if (!stringbuf) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for ifc $ desc string failed.")); + iDescriptors.DeleteIfcDescriptor(aIfc->iInterfaceSet->iInterfaceNumber, + aIfc->iSettingCode); + return KErrNoMemory; + } + stringbuf->SetMax(); + // the aString points to data that lives in user memory, so we have to copy it: + TInt r = Kern::ThreadDesRead(aThread, aString, *stringbuf, 0); + if (r != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Thread read error")); + iDescriptors.DeleteIfcDescriptor(aIfc->iInterfaceSet->iInterfaceNumber, + aIfc->iSettingCode); + delete stringbuf; + return r; + } + TUsbcStringDescriptor* const sd = TUsbcStringDescriptor::New(*stringbuf); + if (!sd) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for ifc $ desc failed.")); + iDescriptors.DeleteIfcDescriptor(aIfc->iInterfaceSet->iInterfaceNumber, + aIfc->iSettingCode); + delete stringbuf; + return KErrNoMemory; + } + iDescriptors.SetIfcStringDescriptor(sd, aIfc->iInterfaceSet->iInterfaceNumber, aIfc->iSettingCode); + delete stringbuf; // the (EPOC) descriptor was copied by New() + } + + // Endpoint descriptors + for (TInt i = 0; i < aIfc->iEndpoints.Count(); ++i) + { + // The reason for using another function argument for Endpoint Info + // (and not possibly - similar to the Endpoint Address - + // "aIfc->iEndpoints[i]->iPEndpoint->iLEndpoint->iInfo") is that this time + // there are no logical endpoints associated with our real endpoints, + // i.e. iLEndpoint is NULL!. + if (aEndpointData[i].iExtra) + { + // if a non-standard endpoint descriptor is requested... + if (aEndpointData[i].iExtra != 2) + { + // ...then it must be a Audio Class endpoint descriptor. Else... + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: EP desc extension > 2 bytes (%d)", + aEndpointData[i].iExtra)); + iDescriptors.DeleteIfcDescriptor(aIfc->iInterfaceSet->iInterfaceNumber, + aIfc->iSettingCode); + return KErrArgument; + } + d = TUsbcAudioEndpointDescriptor::New(aIfc->iEndpoints[i]->iPEndpoint->iEndpointAddr, + aEndpointData[i]); + } + else + { + d = TUsbcEndpointDescriptor::New(aIfc->iEndpoints[i]->iPEndpoint->iEndpointAddr, + aEndpointData[i]); + } + if (!d) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Memory allocation for ep desc #%d failed.", i)); + iDescriptors.DeleteIfcDescriptor(aIfc->iInterfaceSet->iInterfaceNumber, + aIfc->iSettingCode); + return KErrNoMemory; + } + iDescriptors.InsertDescriptor(d); + } + + return KErrNone; + } + + +TInt DUsbClientController::ClientId2InterfaceNumber(const DBase* aClientId) const + { + const TInt num_ifcsets = iConfigs[0]->iInterfaceSets.Count(); + for (TInt i = 0; i < num_ifcsets; ++i) + { + if (iConfigs[0]->iInterfaceSets[i]->iClientId == aClientId) + { + return iConfigs[0]->iInterfaceSets[i]->iInterfaceNumber; + } + } + return -1; + } + + +TUsbcInterfaceSet* DUsbClientController::ClientId2InterfacePointer(const DBase* aClientId) const + { + const TInt num_ifcsets = iConfigs[0]->iInterfaceSets.Count(); + for (TInt i = 0; i < num_ifcsets; ++i) + { + if (iConfigs[0]->iInterfaceSets[i]->iClientId == aClientId) + { + return iConfigs[0]->iInterfaceSets[i]; + } + } + return NULL; + } + + +const DBase* DUsbClientController::InterfaceNumber2ClientId(TInt aIfcSet) const + { + if (!InterfaceExists(aIfcSet)) + { + return NULL; + } + return InterfaceNumber2InterfacePointer(aIfcSet)->iClientId; + } + + +TUsbcInterfaceSet* DUsbClientController::InterfaceNumber2InterfacePointer(TInt aIfcSet) const + { + const TInt num_ifcsets = iConfigs[0]->iInterfaceSets.Count(); + for (TInt i = 0; i < num_ifcsets; ++i) + { + if ((iConfigs[0]->iInterfaceSets[i]->iInterfaceNumber) == aIfcSet) + { + return iConfigs[0]->iInterfaceSets[i]; + } + } + return NULL; + } + + +TInt DUsbClientController::ActivateHardwareController() + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::ActivateHardwareController()")); + + // iStackIsActive must be ETrue at this time + // the caller of this function shall checked that iStackIsActive is ETrue + __ASSERT_DEBUG(iStackIsActive, Kern::Fault(KUsbPILPanicCat, __LINE__)); + + if (iHardwareActivated) + { + __KTRACE_OPT(KUSB, Kern::Printf(" already active -> returning")); + return KErrNone; + } + // Initialise HW + TInt r = iController.StartPeripheralController(); + if (r != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: StartPeripheralController() failed")); + return KErrHardwareNotAvailable; + } + + iHardwareActivated = ETrue; + + // Configure & enable endpoint zero + const TUsbcLogicalEndpoint* const ep0_0 = iRealEndpoints[0].iLEndpoint; + const TUsbcLogicalEndpoint* const ep0_1 = iRealEndpoints[1].iLEndpoint; + if (iHighSpeed) + { + const_cast(ep0_0)->iInfo.iSize = ep0_0->iEpSize_Hs; + const_cast(ep0_1)->iInfo.iSize = ep0_1->iEpSize_Hs; + } + else + { + const_cast(ep0_0)->iInfo.iSize = ep0_0->iEpSize_Fs; + const_cast(ep0_1)->iInfo.iSize = ep0_1->iEpSize_Fs; + } + iController.ConfigureEndpoint(0, ep0_0->iInfo); + iController.ConfigureEndpoint(1, ep0_1->iInfo); + iEp0MaxPacketSize = ep0_0->iInfo.iSize; + + __KTRACE_OPT(KUSB, Kern::Printf(" Controller activated.")); + + // Controller is powered up, changes device state to powered + NextDeviceState(UsbShai::EUsbPeripheralStatePowered); + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::ActivateHardwareController()")); + return KErrNone;; + } + + +void DUsbClientController::DeActivateHardwareController() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeActivateHardwareController()")); + if (!iHardwareActivated) + { + __KTRACE_OPT(KUSB, Kern::Printf(" not active -> returning")); + return; + } + // Deconfigure & disable endpoint zero + iController.DeConfigureEndpoint(KEp0_Out); + iController.DeConfigureEndpoint(KEp0_In); + // Stop HW + iController.StopPeripheralController(); + iHardwareActivated = EFalse; + __KTRACE_OPT(KUSB, Kern::Printf(" Controller deactivated.")); + + // Always go to attached state, until statck is disabled + NextDeviceState(UsbShai::EUsbPeripheralStateAttached); + + return; + } + + +void DUsbClientController::DeleteInterfaceSet(TInt aIfcSet) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeleteInterfaceSet(%d)", aIfcSet)); + TUsbcInterfaceSet* const ifcset_ptr = InterfaceNumber2InterfacePointer(aIfcSet); + if (!ifcset_ptr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: invalid interface number: %d", aIfcSet)); + return; + } + const TInt idx = iConfigs[0]->iInterfaceSets.Find(ifcset_ptr); + if (idx == KErrNotFound) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: interface not found in array")); + return; + } + //Add this mutex to protect the interface set data structure + if (NKern::CurrentContext() == EThread) + { + NKern::FMWait(&iMutex); + } + + iConfigs[0]->iInterfaceSets.Remove(idx); + if (NKern::CurrentContext() == EThread) + { + NKern::FMSignal(&iMutex); + } + delete ifcset_ptr; + } + + +void DUsbClientController::DeleteInterface(TInt aIfcSet, TInt aIfc) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeleteInterface(%d, %d)", aIfcSet, aIfc)); + TUsbcInterfaceSet* const ifcset_ptr = InterfaceNumber2InterfacePointer(aIfcSet); + if (!ifcset_ptr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: invalid interface number: %d", aIfcSet)); + return; + } + if (ifcset_ptr->iInterfaces.Count() <= aIfc) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: invalid interface setting: %d", aIfc)); + return; + } + //Add this mutex to protect the interface set data structure + if (NKern::CurrentContext() == EThread) + { + NKern::FMWait(&iMutex); + } + TUsbcInterface* const ifc_ptr = ifcset_ptr->iInterfaces[aIfc]; + // Always first remove, then delete (see ~TUsbcLogicalEndpoint() for the reason why) + ifcset_ptr->iInterfaces.Remove(aIfc); + + if (aIfc == ifcset_ptr->iCurrentInterface) + { + __KTRACE_OPT(KUSB, Kern::Printf(" > Warning: deleting current interface setting")); + ifcset_ptr->iCurrentInterface = 0; + } + if (NKern::CurrentContext() == EThread) + { + NKern::FMSignal(&iMutex); + } + delete ifc_ptr; + } + + +void DUsbClientController::CancelTransferRequests(TInt aRealEndpoint) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::CancelTransferRequests(aRealEndpoint=%d)", + aRealEndpoint)); + const DBase* const clientId = PEndpoint2ClientId(aRealEndpoint); + if (EpIdx2Addr(aRealEndpoint) & KUsbEpAddress_In) + { + CancelWriteBuffer(clientId, aRealEndpoint); + } + else + { + CancelReadBuffer(clientId, aRealEndpoint); + } + } + + +void DUsbClientController::DeleteRequestCallback(const DBase* aClientId, TInt aEndpointNum, + UsbShai::TTransferDirection aTransferDir) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeleteRequestCallback()")); + // Ep0 OUT + if (aEndpointNum == 0) + { + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + TSglQueIter iter(iEp0ReadRequestCallbacks); + TUsbcRequestCallback* p; + while ((p = iter++) != NULL) + { + if (p->Owner() == aClientId) + { + __ASSERT_DEBUG((p->iRealEpNum == 0), Kern::Fault(KUsbPILPanicCat, __LINE__)); + __ASSERT_DEBUG((p->iTransferDir == UsbShai::EControllerRead), Kern::Fault(KUsbPILPanicCat, __LINE__)); + __KTRACE_OPT(KUSB, Kern::Printf(" removing RequestCallback @ 0x%x (ep0)", p)); + iEp0ReadRequestCallbacks.Remove(*p); + } + } + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + return; + } + // Other endpoints + TUsbcRequestCallback* const p = iRequestCallbacks[aEndpointNum]; + if (p) + { + __ASSERT_DEBUG((p->Owner() == aClientId), Kern::Fault(KUsbPILPanicCat, __LINE__)); + __ASSERT_DEBUG((p->iTransferDir == aTransferDir), Kern::Fault(KUsbPILPanicCat, __LINE__)); + __KTRACE_OPT(KUSB, Kern::Printf(" removing RequestCallback @ 0x%x", p)); + iRequestCallbacks[aEndpointNum] = NULL; + } + } + + +void DUsbClientController::DeleteRequestCallbacks(const DBase* aClientId) + { + // aClientId being NULL means: delete all requests for *all* clients. + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::DeleteRequestCallbacks()")); + // Ep0 OUT + const TInt irq = __SPIN_LOCK_IRQSAVE(iUsbLock); + TSglQueIter iter(iEp0ReadRequestCallbacks); + TUsbcRequestCallback* p; + while ((p = iter++) != NULL) + { + if (!aClientId || p->Owner() == aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf(" removing RequestCallback @ 0x%x (ep0)", p)); + iEp0ReadRequestCallbacks.Remove(*p); + } + } + __SPIN_UNLOCK_IRQRESTORE(iUsbLock, irq); + // Other endpoints + for (TInt i = 1; i < KUsbcEpArraySize; i++) + { + TUsbcRequestCallback* const p = iRequestCallbacks[i]; + if (p && (!aClientId || p->Owner() == aClientId)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" removing RequestCallback @ 0x%x", p)); + iRequestCallbacks[i] = NULL; + } + } + } + + +void DUsbClientController::StatusNotify(UsbShai::TUsbPeripheralState aState, const DBase* aClientId) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::StatusNotify()")); + + // This function may be called by the PSL (via chapter9.cpp) from within an + // ISR -- so we have to take care what we do here (and also in all + // functions that get called from here). + + TSglQueIter iter(iStatusCallbacks); + TUsbcStatusCallback* p; + while ((p = iter++) != NULL) + { + if (!aClientId || aClientId == p->Owner()) + { + __KTRACE_OPT(KUSB, Kern::Printf(" notifying LDD @ 0x%x about %d", p->Owner(), aState)); + p->SetState(aState); + p->DoCallback(); + } + } + } + + +void DUsbClientController::EpStatusNotify(TInt aRealEndpoint) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::EpStatusNotify()")); + + // This function may be called by the PSL (via chapter9.cpp) from within an + // ISR -- so we have to take care what we do here (and also in all + // functions that get called from here). + + const DBase* const client_id = PEndpoint2ClientId(aRealEndpoint); + if (!client_id) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Client not found for real ep %d", aRealEndpoint)); + return; + } + // Check if there is a notification request queued for that client (if not, we can return here). + TSglQueIter iter(iEpStatusCallbacks); + TUsbcEndpointStatusCallback* p; + while ((p = iter++) != NULL) + { + if (p->Owner() == client_id) + { + break; + } + } + if (!p) + { + __KTRACE_OPT(KUSB, Kern::Printf(" No notification request for that client, returning")); + return; + } + const TInt ifcset = ClientId2InterfaceNumber(client_id); + if (ifcset < 0) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Ifcset not found for clientid %d", client_id)); + return; + } + const TUsbcInterfaceSet* const ifcset_ptr = InterfaceNumber2InterfacePointer(ifcset); + if (!ifcset_ptr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Ifcset pointer not found for ifcset %d", ifcset)); + return; + } + const TUsbcInterface* const ifc_ptr = ifcset_ptr->CurrentInterface(); + if (!ifc_ptr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Current ifc pointer not found for ifcset %d", ifcset)); + return; + } + TUint state = 0; + const TInt eps = ifc_ptr->iEndpoints.Count(); + for (TInt i = 0; i < eps; i++) + { + const TUsbcLogicalEndpoint* const ep_ptr = ifc_ptr->iEndpoints[i]; + __KTRACE_OPT(KUSB, Kern::Printf(" checking logical ep #%d for stall state...", + ep_ptr->iLEndpointNum)); + if (ep_ptr->iPEndpoint->iHalt) + { + __KTRACE_OPT(KUSB, Kern::Printf(" -- stalled")); + // set the bit n to 1, where n is the logical endpoint number minus one + state |= (1 << (ep_ptr->iLEndpointNum - 1)); + } + else + { + __KTRACE_OPT(KUSB, Kern::Printf(" -- not stalled")); + } + } + __KTRACE_OPT(KUSB, Kern::Printf(" passing ep state 0x%x on to LDD @ 0x%x", state, client_id)); + p->SetState(state); + p->DoCallback(); + } + + +void DUsbClientController::OtgFeaturesNotify() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::OtgFeaturesNotify()")); + + // This function may be called from the PSL (via PIL's chapter9.cpp) from + // within an ISR -- so we have to take care what we do here (and also in + // all functions that get called from here). + + TSglQueIter iter(iOtgCallbacks); + TUsbcOtgFeatureCallback* p; + while ((p = iter++) != NULL) + { + p->SetFeatures(iOtgFuncMap & 0x1C); + p->DoCallback(); + } + } + + +void DUsbClientController::RunClientCallbacks() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::RunClientCallbacks()")); + TSglQueIter iter(iClientCallbacks); + TUsbcClientCallback* p; + while ((p = iter++) != NULL) + { + __KTRACE_OPT(KUSB, Kern::Printf("Callback 0x%x", p)); + p->DoCallback(); + } + } + + +void DUsbClientController::ProcessDataTransferDone(TUsbcRequestCallback& aRcb) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessDataTransferDone()")); + // This piece can only be called in thread context from ProcessEp0DataReceived() / + // ProcessEp0SetupReceived() via the call to ProcessEp0ReceiveDone() in + // SetupReadBuffer(), which is guarded by an interrupt lock. + TInt ep = aRcb.iRealEpNum; + if (ep == 0) + { + if (aRcb.iTransferDir == UsbShai::EControllerRead) + { + // Ep0 OUT is special + iEp0ReadRequestCallbacks.Remove(aRcb); + } + else // UsbShai::EControllerWrite + { + // Ep0 IN needs to be adjusted: it's '1' within the PIL. + ep = KEp0_Tx; + } + } + if (ep > 0) // not 'else'! + { + __ASSERT_DEBUG((iRequestCallbacks[ep] == &aRcb), Kern::Fault(KUsbPILPanicCat, __LINE__)); + __KTRACE_OPT(KUSB, Kern::Printf(" > removing RequestCallback[%d] @ 0x%x", ep, &aRcb)); + iRequestCallbacks[ep] = NULL; + } + aRcb.DoCallback(); + } + + +void DUsbClientController::NextDeviceState(UsbShai::TUsbPeripheralState aNextState) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::NextDeviceState()")); +#ifdef _DEBUG + const char* const states[] = {"Undefined", "Attached", "Powered", "Default", + "Address", "Configured", "Suspended"}; + if ((aNextState >= UsbShai::EUsbPeripheralStateUndefined) && + (aNextState <= UsbShai::EUsbPeripheralStateSuspended)) + { + __KTRACE_OPT(KUSB, Kern::Printf(" next device state: %s", states[aNextState])); + } + else + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Unknown next device state: %d", aNextState)); + } + // Print a warning when an invalid state transition is detected + // 'Undefined' is not a state that is mentioned in the USB spec, but + // that's what we're in once the cable gets pulled (for instance). + switch (iDeviceState) + { + case UsbShai::EUsbPeripheralStateUndefined: + // valid: Undefined -> Attached + if (aNextState != UsbShai::EUsbPeripheralStateAttached) + break; + goto OK; + case UsbShai::EUsbPeripheralStateAttached: + // valid: Attached -> {Undefined, Powered} + if ((aNextState != UsbShai::EUsbPeripheralStateUndefined) && + (aNextState != UsbShai::EUsbPeripheralStatePowered)) + break; + goto OK; + case UsbShai::EUsbPeripheralStatePowered: + // valid: Powered -> {Undefined, Attached, Default, Suspended} + if ((aNextState != UsbShai::EUsbPeripheralStateUndefined) && + (aNextState != UsbShai::EUsbPeripheralStateAttached) && + (aNextState != UsbShai::EUsbPeripheralStateDefault) && + (aNextState != UsbShai::EUsbPeripheralStateSuspended)) + break; + goto OK; + case UsbShai::EUsbPeripheralStateDefault: + // valid: Default -> {Undefined, Powered, Default, Address, Suspended} + if ((aNextState != UsbShai::EUsbPeripheralStateUndefined) && + (aNextState != UsbShai::EUsbPeripheralStatePowered) && + (aNextState != UsbShai::EUsbPeripheralStateDefault) && + (aNextState != UsbShai::EUsbPeripheralStateAddress) && + (aNextState != UsbShai::EUsbPeripheralStateSuspended)) + break; + goto OK; + case UsbShai::EUsbPeripheralStateAddress: + // valid: Address -> {Undefined, Powered, Default, Configured, Suspended} + if ((aNextState != UsbShai::EUsbPeripheralStateUndefined) && + (aNextState != UsbShai::EUsbPeripheralStatePowered) && + (aNextState != UsbShai::EUsbPeripheralStateDefault) && + (aNextState != UsbShai::EUsbPeripheralStateConfigured) && + (aNextState != UsbShai::EUsbPeripheralStateSuspended)) + break; + goto OK; + case UsbShai::EUsbPeripheralStateConfigured: + // valid: Configured -> {Undefined, Powered, Default, Address, Suspended} + if ((aNextState != UsbShai::EUsbPeripheralStateUndefined) && + (aNextState != UsbShai::EUsbPeripheralStatePowered) && + (aNextState != UsbShai::EUsbPeripheralStateDefault) && + (aNextState != UsbShai::EUsbPeripheralStateAddress) && + (aNextState != UsbShai::EUsbPeripheralStateSuspended)) + break; + goto OK; + case UsbShai::EUsbPeripheralStateSuspended: + // valid: Suspended -> {Undefined, Powered, Default, Address, Configured} + if ((aNextState != UsbShai::EUsbPeripheralStateUndefined) && + (aNextState != UsbShai::EUsbPeripheralStatePowered) && + (aNextState != UsbShai::EUsbPeripheralStateDefault) && + (aNextState != UsbShai::EUsbPeripheralStateAddress) && + (aNextState != UsbShai::EUsbPeripheralStateConfigured)) + break; + goto OK; + default: + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Unknown current device state: %d", iDeviceState)); + goto OK; + } + // KUSB only (instead of KPANIC) so as not to worry people too much where + // a particular h/w regularly enforces invalid (but harmless) transitions + __KTRACE_OPT(KUSB, Kern::Printf(" Warning: Invalid next state from %s", states[iDeviceState])); +OK: +#endif // _DEBUG + + iDeviceState = aNextState; + StatusNotify(iDeviceState); + } + + +TInt DUsbClientController::ProcessSuspendEvent() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessSuspendEvent()")); + // A suspend interrupt has been received and needs attention. + iDeviceStateB4Suspend = iDeviceState; + + // We have to move to the Suspend state immediately (in case it's a genuine Suspend) + // because 7.1.7.6 says: "The device must actually be suspended, [...] after no more + // than 10ms of bus inactivity [...]." Assuming we got the interrupt 3ms after the + // Suspend condition arose, we have now 7ms left. + NextDeviceState(UsbShai::EUsbPeripheralStateSuspended); + iController.Suspend(); + + return KErrNone; + } + +TInt DUsbClientController::ProcessResumeEvent() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessResumeEvent()")); + if (iDeviceState == UsbShai::EUsbPeripheralStateSuspended) + { + NextDeviceState(iDeviceStateB4Suspend); + } + iController.Resume(); + return KErrNone; + } + + +TInt DUsbClientController::ProcessResetEvent(TBool aPslUpcall) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessResetEvent()")); + + if (aPslUpcall) + { + // Call back into PSL if we're coming from there. + // Also, do it always, even when PIL processing will be deferred. + iController.Reset(); + } + + // In an OTG-environment, some OTG controllers have implemented + // the OTG state machine in HW and connect to the bus + // automatically already before the SW has indicated + // readiness. Peripheral PSL operating on top of such controller + // may report a reset event already before the upper layers are + // ready. In this case we defer the reset processing to after the + // upper layers have indicated their readiness. + if (iUsbResetDeferred) + { + __KTRACE_OPT(KUSB, Kern::Printf(" User-side (still) not ready -> returning")); + return KErrNone; + } + else if (!iClientSupportReady) + { + // Wait with the PIL Reset processing until user-side is ready + __KTRACE_OPT(KUSB, Kern::Printf(" User-side not ready -> deferring")); + iUsbResetDeferred = ETrue; + return KErrNone; + } + + if (iDeviceState == UsbShai::EUsbPeripheralStateAttached) + { + NextDeviceState(UsbShai::EUsbPeripheralStatePowered); + } + // Notify the world. (This will just queue a DFC, so users won't actually be + // notified before we return. But we change the device state already here so + // ChangeConfiguration will see the correct one.) + NextDeviceState(UsbShai::EUsbPeripheralStateDefault); + // Tear down the current configuration (never called from thread) + ChangeConfiguration(0); + // Reset essential vars + ResetEp0DataOutVars(); + //iEp0_RxExtraData = EFalse; + iEp0WritePending = EFalse; + iEp0ClientDataTransmitting = EFalse; + + iLastError = KErrNone; + iSetupPacketPending = EFalse; + //iEp0_RxExtraError = 0; + iConTransferMgr->Reset(); + + // Reset OTG features, leave attributes as is + iOtgFuncMap &= KUsbOtgAttr_SrpSupp | KUsbOtgAttr_HnpSupp; + if (iOtgSupport) + { + OtgFeaturesNotify(); + } + + // Check whether there's a speed change + const TBool was_hs = iHighSpeed; + iHighSpeed = CurrentlyUsingHighSpeed(); + if (!was_hs && iHighSpeed) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Moving to High-speed")); + EnterHighSpeed(); + } + else if (was_hs && !iHighSpeed) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Moving to Full-speed")); + EnterFullSpeed(); + } + + // Setup initial Ep0 read (SetupEndpointZeroRead never called from thread) + if (iConTransferMgr->SetupEndpointZeroRead() != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: while setting up Ep0 read")); + return KErrGeneral; + } + + return KErrNone; + } + + +TInt DUsbClientController::ProcessVbusRisenEvent() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessVbusRisenEvent()")); + if (iIsOtgPort) + { + // In an OTG environment, this notification is not expected + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: EUsbEventVbusRisen shouldn't be sent by an OTG Client PSL")); + return KErrArgument; + } + else + { + // In a non-OTG environment, seeing VBUS rising causes the + // peripheral stack to be enabled + EnablePeripheralStack(); + return KErrNone; + } + } + + +TInt DUsbClientController::ProcessVbusFallenEvent() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ProcessVbusFallenEvent()")); + if (iIsOtgPort) + { + // In an OTG environment, this notification is not expected + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: EUsbEventVbusFallen shouldn't be sent by an OTG Client PSL")); + return KErrArgument; + } + else + { + // In a non-OTG environment, seeing VBUS falling causes the + // peripheral stack to be disabled + DisablePeripheralStack(); + return KErrNone; + } + } + + +void DUsbClientController::EnterFullSpeed() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::EnterFullSpeed()")); + iDescriptors.UpdateDescriptorsFs(); + } + + +void DUsbClientController::EnterHighSpeed() + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::EnterHighSpeed()")); + iDescriptors.UpdateDescriptorsHs(); + } + + +// +// DFC (static), we are runing in a DFC context. +// +void DUsbClientController::ReconnectTimerCallback(TAny *aPtr) + { + __KTRACE_OPT(KUSB, Kern::Printf("DUsbClientController::ReconnectTimerCallback()")); + if (!aPtr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: !aPtr")); + return; + } + + DUsbClientController* const ptr = static_cast(aPtr); + + ptr->UsbConnect(); + + } + +// +// static Dfc function +// +void DUsbClientController::PowerUpDfc(TAny* aPtr) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::PowerUpDfc")); + if (!aPtr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: !aPtr")); + return; + } + + DUsbClientController* const ptr = static_cast(aPtr); + + __PM_ASSERT(ptr->iStandby); + + ptr->iStandby = EFalse; + + // We have nothing to do when powerup + ptr->iPowerHandler->PowerUpDone(); + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::PowerUpDfc")); + return ; + } + +// +// static Dfc function +// +void DUsbClientController::PowerDownDfc(TAny* aPtr) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::PowerDownDfc")); + if (!aPtr) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: !aPtr")); + return; + } + + DUsbClientController* const ptr = static_cast(aPtr); + __PM_ASSERT(!ptr->iStandby); + + ptr->iStandby = ETrue; + + // Disable stack(stop controller inside) + ptr->DisablePeripheralStack(); + + ptr->iPowerHandler->PowerDownDone(); + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::PowerDownDfc")); + return ; + } + +// +// Static Dfc function +// +void DUsbClientController::DeviceEventNotifyDfc(TAny* aPtr) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::DeviceEventNotifyDfc")); + DUsbClientController* const ptr = static_cast(aPtr); + + UsbShai::TUsbPeripheralEvent event = ptr->iDevEventQueue.FifoGet(); + + if( event == -1) + { + // No event queued + __KTRACE_OPT(KUSB, Kern::Printf(" Warning, No event found, exit !!!")); + return; + } + + ptr->ProcessDeviceEventNotification(event); + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::DeviceEventNotifyDfc")); + } + +// +// Static Dfc function +// +void DUsbClientController::ThreadContextFinderDfc(TAny* aPtr) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::ThreadContextFinderDfc")); + + DUsbClientController* const ptr = static_cast(aPtr); + + ptr->iCommonDfcQThread = NKern::CurrentThread(); + __KTRACE_OPT(KUSB, Kern::Printf(" iCommonDfcQThread = %d",ptr->iCommonDfcQThread)); + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::ThreadContextFinderDfc")); + } +// +// +// +void DUsbClientController::ProcessDeviceEventNotification(UsbShai::TUsbPeripheralEvent aEvent) + { + __KTRACE_OPT(KUSB, Kern::Printf("> DUsbClientController::ProcessDeviceEventNotification %d",aEvent)); + // If an OTG Observer has registered, forward the event + // notification to it as well + if (iOtgObserver != NULL) + { + __KTRACE_OPT(KUSB, Kern::Printf(" Forwarding to OTG Observer")); + iOtgObserver->NotifyPeripheralEvent(aEvent); + } + + // 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). + + switch (aEvent) + { + case UsbShai::EUsbEventSuspend: + ProcessSuspendEvent(); + break; + case UsbShai::EUsbEventResume: + ProcessResumeEvent(); + break; + case UsbShai::EUsbEventReset: + ProcessResetEvent(); + break; + case UsbShai::EUsbEventVbusRisen: + ProcessVbusRisenEvent(); + break; + case UsbShai::EUsbEventVbusFallen: + ProcessVbusFallenEvent(); + break; + + default: + break; + } + + __KTRACE_OPT(KUSB, Kern::Printf("< DUsbClientController::ProcessDeviceEventNotification")); + } + +// Functions from UsbShai::MChargerDetectorObserverIf +void DUsbClientController::NotifyPortType(UsbShai::TPortType aPortType) + { + // FIXME: Not yet implemented! + } + +void DUsbClientController::Buffer2Setup(const TAny* aBuf, TUsbcSetup& aSetup) const + { + // TUint8 index + aSetup.iRequestType = static_cast(aBuf)[0]; + aSetup.iRequest = static_cast(aBuf)[1]; + // TUint16 index from here! + aSetup.iValue = SWAP_BYTES_16((static_cast(aBuf))[1]); + aSetup.iIndex = SWAP_BYTES_16((static_cast(aBuf))[2]); + aSetup.iLength = SWAP_BYTES_16((static_cast(aBuf))[3]); + } + +TUsbPeriDeviceEventQueue::TUsbPeriDeviceEventQueue() + { + iDeviceQueueHead = 0; + iDeviceQueueTail = 0; + } + +// FifoAdd will be called from Isr or IDfc context +// be careful when processing +void TUsbPeriDeviceEventQueue::FifoAdd(UsbShai::TUsbPeripheralEvent aDeviceEvent) + { + __KTRACE_OPT(KUSB, Kern::Printf("> TUsbPeriDeviceEventQueue::FifoAdd %d , %d",iDeviceQueueHead,iDeviceQueueTail)); + iDeviceEventQueue[iDeviceQueueTail] = aDeviceEvent; + + iDeviceQueueTail ++; + iDeviceQueueTail %= KUsbDeviceEventQueueDepth; + if(iDeviceQueueTail == iDeviceQueueHead) + { + __KTRACE_OPT(KUSB, Kern::Printf("TUsbPeriDeviceEventQueue::FifoAdd overflow, oldest event missed")); + iDeviceQueueHead ++; + iDeviceQueueHead %= KUsbDeviceEventQueueDepth; + } + __KTRACE_OPT(KUSB, Kern::Printf("< TUsbPeriDeviceEventQueue::FifoAdd %d , %d",iDeviceQueueHead,iDeviceQueueTail)); + } + +// FifoAdd will be called from thread context +UsbShai::TUsbPeripheralEvent TUsbPeriDeviceEventQueue::FifoGet() + { + __KTRACE_OPT(KUSB, Kern::Printf("> TUsbPeriDeviceEventQueue::FifoGet %d , %d",iDeviceQueueHead,iDeviceQueueTail)); + UsbShai::TUsbPeripheralEvent ret = (UsbShai::TUsbPeripheralEvent)-1; + + if(iDeviceQueueHead != iDeviceQueueTail) + { + ret = iDeviceEventQueue[iDeviceQueueHead]; + iDeviceQueueHead++; + iDeviceQueueHead %= KUsbDeviceEventQueueDepth; + } + __KTRACE_OPT(KUSB, Kern::Printf("< TUsbPeriDeviceEventQueue::FifoGet %d , %d",iDeviceQueueHead,iDeviceQueueTail)); + return ret; + } + +/** Sets some data members of this request for a read request. + + @param aBufferStart The start of the data buffer to be filled. + @param aBufferAddr The physical address of the buffer (used for DMA). + @param aPacketIndex A pointer to the packet index values array. + @param aPacketSize A pointer to the packet size values array. + @param aLength The number of bytes to be received. +*/ +EXPORT_C void TUsbcRequestCallback::SetRxBufferInfo(TUint8* aBufferStart, TUintPtr aBufferAddr, + TUint32* aPacketIndex, + TUint32* aPacketSize, + TInt aLength) + { + iTransferDir = UsbShai::EControllerRead; + iBufferStart = aBufferStart; + iBufferAddr = aBufferAddr; + iPacketIndex = aPacketIndex; + iPacketSize = aPacketSize; + iLength = aLength; + } + + +/** Sets some data members of this request for a write request. + + @param aBufferStart The start of the buffer that contains the data to be sent. + @param aBufferAddr The physical address of the buffer (used for DMA). + @param aLength The number of bytes to be transmitted. +*/ +EXPORT_C void TUsbcRequestCallback::SetTxBufferInfo(TUint8* aBufferStart, TUintPtr aBufferAddr, TInt aLength) + { + iTransferDir = UsbShai::EControllerWrite; + iBufferStart = aBufferStart; + iBufferAddr = aBufferAddr; + iLength = aLength; + } + +EXPORT_C UsbShai::TUsbPeripheralRequest::TUsbPeripheralRequest(TInt aRealEpNum): + iRealEpNum(aRealEpNum), + iBufferStart(NULL), + iBufferAddr(0), + iLength(0), + iTxBytes(0), + iRxPackets(0), + iPacketIndex(NULL), // actually TUint16 (*)[] + iPacketSize(NULL), // actually TUint16 (*)[] + iTransferDir(EControllerNone), + iZlpReqd(EFalse), + iError(KErrNone) + { + } + +// Peripheral PSL use this interface to register itself to PIL layer, in our case, +// DUsbClientController instance +// +// param aPeripheralControllerIf point to an implementation of +// UsbShai::MPeripheralControllerIf which represent a peripheral controller hardware. +EXPORT_C void UsbShai::UsbPeripheralPil::RegisterPeripheralController( UsbShai::MPeripheralControllerIf& aPeripheralControllerIf, + const UsbShai::TPeripheralControllerProperties& aProperties) + { + __KTRACE_OPT(KUSB, Kern::Printf("> UsbPeripheralPil::RegisterPeripheralController enter.")); + + DUsbClientController* usbcc = DUsbClientController::Create(aPeripheralControllerIf, + aProperties, + /*aIsOtgPort=*/EFalse); + __ASSERT_DEBUG( (usbcc != NULL), + Kern::Fault( "DUsbClientController extension entry point: USB PSL Out of memory", __LINE__ ) ); + } + +// The way for charger detector(PSL) to make it known to us. +EXPORT_C void UsbShai::UsbChargerDetectionPil::RegisterChargerDetector(UsbShai::MChargerDetectorIf& aChargerDetector, + UsbShai::TChargerDetectorProperties& aProperties) + { + // Only on charger detector is allowed per system. + if( gChargerDetector != NULL) + { + return ; + } + + gChargerDetector = &aChargerDetector; + + if(gChargerObsever != NULL) + { + // Register to charger detector to listen to any charger type change + // events. + gChargerDetector->SetChargerDetectorObserver(*gChargerObsever); + } + } + +// -EOF-