// 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 <drivers/usbc.h>
#include <usb/usbc.h>
#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<TUsbcClientCallback> 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<const DLddUsbcChannel*>(aClientId))->Client(),
&aCapsBuf, des, 0, KChunkShiftBy0, NULL);
if (r != KErrNone)
{
Kern::ThreadKill((reinterpret_cast<const DLddUsbcChannel*>(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<const DLddUsbcChannel*>(aClientId))->Client(),
&aCapsBuf, caps, 0, KChunkShiftBy0, NULL);
if (r != KErrNone)
{
Kern::ThreadKill((reinterpret_cast<const DLddUsbcChannel*>(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<TUsbcPhysicalEndpoint*>
(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<TUsbcPhysicalEndpoint*>
(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<TUsbcStatusCallback> 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<TUsbcEndpointStatusCallback> 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<TUsbcLogicalEndpoint*>(ep0_0)->iEpSize_Fs = aMaxPacketSize;
const_cast<TUsbcLogicalEndpoint*>(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<TUsbcLogicalEndpoint*>(ep0_0)->iInfo.iSize = ep0_0->iEpSize_Fs;
const_cast<TUsbcLogicalEndpoint*>(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<const TUint8*>(&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<const TUint8*>(&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<KUsbDescSize_Otg> 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<TUsbcOtgFeatureCallback> 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<KUsbDescSize_Interface> 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<const TUint8*>(&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<const TUint8*>(&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<const TUint8*>(&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<const TUint8*>(&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<TUsbcLogicalEndpoint>& 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<TUint>(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<TUsbcRequestCallback*>(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<const TUint8*>(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<const TUint8*>(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<const TUint8*>(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<const TUint8*>(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<TUsbcClientCallback> 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<TUsbcLogicalEndpoint*>(ep0_0)->iInfo.iSize = ep0_0->iEpSize_Hs;
const_cast<TUsbcLogicalEndpoint*>(ep0_1)->iInfo.iSize = ep0_1->iEpSize_Hs;
}
else
{
const_cast<TUsbcLogicalEndpoint*>(ep0_0)->iInfo.iSize = ep0_0->iEpSize_Fs;
const_cast<TUsbcLogicalEndpoint*>(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<TUsbcRequestCallback> 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<TUsbcRequestCallback> 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<TUsbcStatusCallback> 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<TUsbcEndpointStatusCallback> 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<TUsbcOtgFeatureCallback> 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<TUsbcClientCallback> 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<DUsbClientController*>(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<DUsbClientController*>(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<DUsbClientController*>(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<DUsbClientController*>(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<DUsbClientController*>(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<const TUint8*>(aBuf)[0];
aSetup.iRequest = static_cast<const TUint8*>(aBuf)[1];
// TUint16 index from here!
aSetup.iValue = SWAP_BYTES_16((static_cast<const TUint16*>(aBuf))[1]);
aSetup.iIndex = SWAP_BYTES_16((static_cast<const TUint16*>(aBuf))[2]);
aSetup.iLength = SWAP_BYTES_16((static_cast<const TUint16*>(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-