// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).// All rights reserved.// This component and the accompanying materials are made available// under the terms of "Eclipse Public License v1.0"// which accompanies this distribution, and is available// at the URL "http://www.eclipse.org/legal/epl-v10.html".//// Initial Contributors:// Nokia Corporation - initial contribution.//// Contributors://// Description:///** @file @internalComponent */#include <obexusbtransportinfo.h>#include "usbconn.h"#include <obex/transport/obexconnector.h>#include "logger.h"#include "obexusbfaults.h"#ifdef __FLOG_ACTIVE_LIT8(KLogComponent, "USB");#endif#ifdef _DEBUG_LIT(KPanicCat, "ObexUsbHandler");#endif///**CObexUsbActiveWriter factory function@param aTransport Reference to a CObexUsbTransportController object. Required for a call to the CObexUsbActiveReader@param aOwner Reference to a MObexTransportNotify object@param aUsb Reference to a RDevUsbcClient object@param aInfo Reference to a TObexConnectionInfo object@param aPacketSize The size of packets used on the underlying link, dependent on High Speed mode.@return Ownership of a new CObexUsbActiveWriter.*/CObexUsbActiveWriter* CObexUsbActiveWriter::NewL(MObexTransportNotify& aOwner, RDevUsbcClient& aUsb, TObexConnectionInfo& aInfo, TInt aPacketSize) { CObexUsbActiveWriter* self = new(ELeave) CObexUsbActiveWriter(aOwner, aUsb, aInfo, aPacketSize); CleanupStack::PushL(self); self->BaseConstructL(); CleanupStack::Pop(self); return self; }/**CObexUsbActiveWriter constructor@param aTransport Reference to a CObexUsbTransportController object. Required for a call to the CObexUsbActiveReader@param aOwner Reference to a MObexTransportNotify object@param aUsb Reference to a RDevUsbcClient object@param aInfo Reference to a TObexConnectionInfo object@param aPacketSize The size of packets used on the underlying link, dependent on High Speed mode.*/CObexUsbActiveWriter::CObexUsbActiveWriter(MObexTransportNotify& aOwner, RDevUsbcClient& aUsb, TObexConnectionInfo& aInfo, TInt aPacketSize): CObexWriterBase(EPriorityHigh, aOwner, aInfo), iUsb(aUsb), iPacketSize(aPacketSize) { }/**CObexUsbActiveWriter destructor.*/CObexUsbActiveWriter::~CObexUsbActiveWriter() { LOG_FUNC Cancel(); }/**Start actual transfer. May be called several times by CObexActiveRW::RunL.Queues a write on USB endpoint.*/void CObexUsbActiveWriter::DoTransfer() { LOG_FUNC LOG1(_L("CObexUsbActiveWriter::DoTransfer [Length=%d]"),iLocation.Length()); iLocation.SetMax(); iUsb.Write(iStatus, KTransmitEndpoint, iLocation, iLocation.Length(), ETrue); SetActive(); }/**Cancels an outstanding write.*/void CObexUsbActiveWriter::DoCancel() { LOG_FUNC //Only cancel the write if there's more than 1 packet left to write. //This restriction is imposed to prevent the SUCCESS response being //cancelled when an ObexServer is handling a disconnect and bringing //the transport down. if(Remaining() > iPacketSize) { iUsb.WriteCancel(KTransmitEndpoint); } }///**CObexUsbActiveReader factory function@param aOwner Reference to a MObexTransportNotify object@param aUsb Reference to a RDevUsbcClient object@param aInfo Reference to a TObexConnectionInfo object@return Ownership of a new CObexUsbActiveWriter.*/CObexUsbActiveReader* CObexUsbActiveReader::NewL(MObexTransportNotify& aOwner, RDevUsbcClient& aUsb, TObexConnectionInfo& aInfo) { CObexUsbActiveReader* self = new(ELeave) CObexUsbActiveReader(aOwner, aUsb, aInfo); CleanupStack::PushL(self); self->BaseConstructL(); CleanupStack::Pop(self); return self; }/**CObexUsbActiveReader constructor@param aOwner Reference to a MObexTransportNotify object@param aUsb Reference to a RDevUsbcClient object@param aInfo Reference to a TObexConnectionInfo object*/CObexUsbActiveReader::CObexUsbActiveReader(MObexTransportNotify& aOwner, RDevUsbcClient& aUsb, TObexConnectionInfo& aInfo): CObexReaderBase(EPriorityStandard, aOwner, aInfo), iUsb(aUsb) { }/**CObexUsbActiveReader destructor.*/CObexUsbActiveReader::~CObexUsbActiveReader() { LOG_FUNC Cancel(); }/**Start actual transfer. May be called several times by CObexActiveRW::RunL.Queues a read on USB endpoint.*/void CObexUsbActiveReader::DoTransfer() { LOG_FUNC LOG1(_L("CObexUsbActiveReader::DoTransfer [maxLength=%d]"),iLocation.MaxLength()); iUsb.ReadUntilShort(iStatus, KReceiveEndpoint, iLocation); SetActive(); }/**Cancels an outstanding read, */void CObexUsbActiveReader::DoCancel() { FLOG(_L("CObexUsbActiveReader::DoCancel")); iUsb.ReadCancel(KReceiveEndpoint); }/**Return the maximum packet size@return TInt the maximum packet size for this transport*/TInt CObexUsbActiveReader::GetMaxPacketSize() { LOG_FUNC return GetObexPacketDataLimit(); }/**Returns a initial packet size when the packet size of iPacket is not know. This is usedwhen determining the remaining bytes to be read. @return TInt the initial packet size*/TInt CObexUsbActiveReader::GetInitialPacketSize () { LOG_FUNC return GetObexPacketBufferSize(); }///**CObexUsbConnector constructor.*/CObexUsbConnector::CObexUsbConnector(MObexTransportNotify& aObserver, TObexUsbTransportInfo& aUsbTransportInfo) : CObexConnector(aObserver), iObexTransportInfo(aUsbTransportInfo), iMode(EDefaultClient) { LOG_FUNC // Note iObexTransportInfo reference is only kept so that it can be used in the ConstructL // It may not be safe to use this variables outside of the construction phases }/*Returns an instance of CObexUsbConnector@param aController Reference to a MObexTransportNotify object (the CObexUsbTransportController that owns this)@param aTransportInfo Reference to a TObexUsbTransportInfo object containing the transport information@return A pointer to a CObexUsbConnector object*/CObexUsbConnector* CObexUsbConnector::NewL(MObexTransportNotify& aController, TObexUsbTransportInfo& aTransportInfo) { CObexUsbConnector* self = new(ELeave) CObexUsbConnector(aController, aTransportInfo); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(self); return(self); }/**Second phase construction.*/void CObexUsbConnector::ConstructL() { LOG_FUNC OpenUsbL(0); FLOG(_L("USB opened")); iHandler = CObexUsbHandler::NewL(iObexTransportInfo, *this, iUsb); iCallback = new(ELeave) CAsyncCallBack(EPriorityHigh); iStaller = CObexUsbStaller::NewL(iUsb, *this); iHandler->Start(); BaseConstructL(); }/**CObexUsbConnector destructor. Releases endpoint and closes USB object if we have an open connection.*/CObexUsbConnector::~CObexUsbConnector() { LOG_FUNC delete iCallback; delete iHandler; delete iStaller; if (iUsb.Handle()) { iUsb.Close(); } }/**Attempt to open a client-type connection.This will succeed if the USB host has already opened a connection to us*/void CObexUsbConnector::ConnectL() { LOG_FUNC if (iMode == EServer) { // Already performed a server-type connect, so can't act as a // client as well. FLOG(_L(" -- In server mode, give up")); LEAVEIFERRORL(KErrNotSupported); } FLOG(_L(" -- Not in server mode")); iMode = EClient; if (iTransportLinkAvailable) { FLOG(_L(" -- Link available, set a callback")); TCallBack callback(LinkUp, this); iCallback->Set(callback); iCallback->CallBack(); iConnectRequested = ETrue; } else { FLOG(_L(" -- No link available, set a callback")); TCallBack callback(NoDevice, this); iCallback->Set(callback); iCallback->CallBack(); } }/*@param aConn A pointer to a connector object. Will be cast to CObexUsbConnector@return KErrNone*/TInt CObexUsbConnector::LinkUp(TAny* aConn) { FLOG(_L("CObexUsbConnector::LinkUp")); reinterpret_cast<CObexUsbConnector*>(aConn)->SignalUp(); return KErrNone; }/*@param aConn A pointer to a connector object. Will be cast to CObexUsbConnector@return KErrNone*/TInt CObexUsbConnector::NoDevice(TAny* aConn) { FLOG(_L("CObexUsbConnector::NoDevice")); reinterpret_cast<CObexUsbConnector*>(aConn)->SignalDown(KErrIrObexClientPeerDoesNotHaveObex); return KErrNone; }/**Cancel an active connection attempt. This is unlikely to succeed, as a clientconnection over USB will complete almost immediately.*/void CObexUsbConnector::CancelConnect() { LOG_FUNC if (iMode == EClient) { iCallback->Cancel(); } iConnectRequested = EFalse; }/**Start listening for a connection to us.*/void CObexUsbConnector::AcceptL() { LOG_FUNC if (iMode == EClient) { // Already carried out a client side connection. Therefore we // can't be a server as well. FLOG(_L(" -- In client mode, give up")); LEAVEIFERRORL(KErrNotSupported); } FLOG(_L(" -- Not in client mode")); iMode = EServer; if (iTransportLinkAvailable) { FLOG(_L(" -- Transport link currently available, set a callback")); TCallBack callback(LinkUp, this); iCallback->Set(callback); iCallback->CallBack(); } else { FLOG(_L(" -- No link available, wait for one")); iWaitingForLink = ETrue; } }/**Stop listening for connections.*/void CObexUsbConnector::CancelAccept() { LOG_FUNC if (iMode == EServer) { iWaitingForLink = EFalse; iCallback->Cancel(); } }/**Called to indicate transport layer has come up.*/void CObexUsbConnector::TransportUp() { LOG_FUNC SetTransportPacketSize(); iTransportLinkAvailable = ETrue; if ((iMode == EServer) && (iWaitingForLink)) { FLOG(_L(" -- Signalling link available")); iWaitingForLink = EFalse; SignalUp(); } }/**Called to indicate transport layer has gone down.@param aErr Any error code associated with disconnection. Defaults to KErrDisconnected.*/void CObexUsbConnector::TransportDown(TInt aErr) { LOG_FUNC ResetTransportPacketSize(); iStaller->Cancel(); iTransportLinkAvailable = EFalse; if (iMode == EServer) { FLOG(_L(" -- In server mode, signal link down")); SignalDown(aErr); } else { FLOG(_L(" -- In client mode")); // Client or default client state if (iConnectRequested) { FLOG(_L(" -- Outstanding client connection. Signal link down")); iConnectRequested = EFalse; SignalDown(aErr); } } }/**Signals to the transport controller that the transport is up*/void CObexUsbConnector::SignalUp() { LOG_FUNC // Indicate transport now up TObexConnectionInfo sockinfo; sockinfo.iMaxRecvSize = 0; // set these to zero as we can't find them out sockinfo.iMaxSendSize = 0; // and they are not used sockinfo.iSocketType = TObexConnectionInfo::EUsbStream; Observer().TransportUp(sockinfo); }/**Signals (with an error code) to the transport controller that the transport has gone down */void CObexUsbConnector::SignalDown(TInt aErr) { FLOG(_L("CObexUsbConnector::SignalDown")); Observer().Error(aErr); }/**The Obex server running over USB is not able to bring the transportdown itself. It will only delete the transport object when an errorhas occurred (such as the link being brought down) and hence thetransport is no longer valid.@return ETrue*/TBool CObexUsbConnector::BringTransportDown(){ // This cancel function needs to be called here because it // doesn't get called anywhere else. Bluetooth and IrDA rely // on the socket shutdown to cancel the accept. CancelAccept(); return ETrue;}/**Return the transport layer object for use by USB transport objects@return Pointer to the USB transport layer object*/RDevUsbcClient* CObexUsbConnector::TransportObject() { return &iUsb; }/**Sets a bus stall condition on the IN endpoint. This will cause the USBhost to initiate a CLEAR_FEATURE sequence, which notifies the server that theObex link has been reset (equivalent to dropping the Obex level connection)*/void CObexUsbConnector::SignalTransportError() { LOG_FUNC iStaller->Start(); iTransportLinkAvailable = EFalse; iConnectRequested = EFalse; }/**Stall cleared => transport available.This upcall has been separated from the TransportUp upcall for tworeasons---it's called from a different class and it happens when anerror condition has been cleared. Currently it has no specialmeaning, but this could change in future.*/void CObexUsbConnector::StallCleared() { LOG_FUNC TransportUp(); }/**Opens USB and prepares it for use.@param aUnit According to the RDevUsbcClient documentation, this should be 0. */void CObexUsbConnector::OpenUsbL(TInt aUnit) { LOG1(_L("CObexUsbConnector::OpenUsbL called for unit=%d"), aUnit); // Load ldd TInt err = User::LoadLogicalDevice(KUsbLddName); if (err != KErrNone && err != KErrAlreadyExists) { LEAVEIFERRORL(err); } // Open the requested unit and initialise local state LEAVEIFERRORL(iUsb.Open(aUnit)); }void CObexUsbConnector::ResetTransportPacketSize() { iPacketSize = 0; }void CObexUsbConnector::SetTransportPacketSize() { if (iUsb.CurrentlyUsingHighSpeed()) { iPacketSize = KMaxPacketTypeBulkHS; } else { iPacketSize = KMaxPacketTypeBulkFS; } }TInt CObexUsbConnector::GetTransportPacketSize() { return iPacketSize; }///**Constructs a CObexUsbHandler object@param aUsbTransportInfo Reference to a TObexTransportInfo object. Will be cast to TObexUsbTransportInfo.@param aOwner Reference to a MObexUsbConnector that owns this handler@param aUsb Reference to a RDevUsbcClient object.@return A new CObexUsbHandler object*/CObexUsbHandler* CObexUsbHandler::NewL(TObexUsbTransportInfo& aUsbTransportInfo, MObexUsbConnector& aOwner, RDevUsbcClient& aUsb) { FLOG(_L("CObexUsbHandler::NewL")); CObexUsbHandler* self = new (ELeave) CObexUsbHandler(aUsbTransportInfo, aOwner, aUsb); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(self); return self; }/**CObexUsbHandler constructor.Note that the iObexTransportInfo may not be valid outside of the construction phasesThe constructor copies out most of the information it needs into member variablesapart from the interface string descriptor, which is used from RegInterfacesL@param aUsbTransportInfo Reference to a TObexTransportInfo object. Will be cast to TObexUsbTransportInfo.@param aOwner Reference to a MObexUsbConnector that owns this handler@param aUsb Reference to a RDevUsbcClient object.*/CObexUsbHandler::CObexUsbHandler(TObexUsbTransportInfo& aUsbTransportInfo, MObexUsbConnector& aOwner, RDevUsbcClient& aUsb) : CActive(EPriorityStandard), iObexTransportInfo(aUsbTransportInfo), iOwner(aOwner), iUsb(aUsb) { LOG_FUNC CActiveScheduler::Add(this); // Set interface performance configuration parameters if (aUsbTransportInfo.iTransportName == KObexUsbProtocol) // Default values if transport is normal USB - no extra bandwidth, no DMA { FLOG(_L("CObexUsbHandler - Using default USB protocol info")); iBandwidthPriority = EUsbcBandwidthOUTDefault | EUsbcBandwidthINDefault; iRequestDmaOnOutEndpoint = EFalse; iRequestDmaOnInEndpoint = EFalse; } else if (aUsbTransportInfo.iTransportName == KObexUsbProtocolV2) // If transport is extended USB take values from the protocol info { FLOG(_L("CObexUsbHandler - Using USB protocol extended info")); TObexUsbV2TransportInfo& infoV2 = static_cast<TObexUsbV2TransportInfo&>(aUsbTransportInfo); iBandwidthPriority = infoV2.iBandwidthPriority; iRequestDmaOnOutEndpoint = infoV2.iDmaOnOutEndpoint; iRequestDmaOnInEndpoint = infoV2.iDmaOnInEndpoint; } else { //aUsbTransportInfo.iTransportName is used to load the correct plug-in, so if this code //is being executed it must have contained one of the options above. Else something //has gone badly wrong. __ASSERT_DEBUG(false, PANIC(KPanicCat, EUnknownUsbTransport)); } LOG3(_L("CObexUsbHandler - iBandwidthPriority 0x%X, iDmaOnOutEndpoint, %d iDmaOnInEndpoint %d"), iBandwidthPriority, iRequestDmaOnOutEndpoint, iRequestDmaOnInEndpoint); }/**Second phase construction.*/void CObexUsbHandler::ConstructL() { // Check bandwidth priority to make sure it makes sense // Reasoning is that the bitwise OR of the maximum values of IN and OUT priorities // covers all the bits that can be set in iBandwidthPriority so, using this as a mask, // anything set outside of this indicates that iBandwidthPriority is corrupt if ( ( iBandwidthPriority & ~( EUsbcBandwidthOUTMaximum | EUsbcBandwidthINMaximum ) ) != 0 ) { LEAVEIFERRORL(KErrArgument); } FLOG(_L("About to open USB comms interface connection")); LEAVEIFERRORL(iUsbComms.Open(0)); FLOG(_L("Registering interfaces")); RegInterfacesL(); }/**CObexUsbHandler destructor.*/CObexUsbHandler::~CObexUsbHandler() { LOG_FUNC Cancel(); // Must release interfaces from highest alternate setting downwards if (iUsb.Handle()) {#ifndef NO_ALTERNATE_USB_INTERFACE_SUPPORT iUsb.ReleaseInterface(1 /*alternate setting*/);#endif iUsb.ReleaseInterface(0 /*alternate setting*/); } if (iUsbComms.Handle()) { iUsbComms.ReleaseInterface(0 /*alternate setting*/); } iUsbComms.Close(); }/**Standard active object error function.@return KErrNone because currently nothing should cause this to be called.*/TInt CObexUsbHandler::RunError(TInt /*aError*/) { return KErrNone; }/**This function will be called upon a change in the state of the device(as set up in AcceptL).*/void CObexUsbHandler::RunL() { LOG1(_L("CObexUsbHandler::RunL called state=0x%X"), iUsbState); if (iStatus != KErrNone) { LOG1(_L("CObexUsbHandler::RunL() - Error = %d"),iStatus.Int()); iTransportUp = EFalse; iOwner.TransportDown(iStatus.Int()); return; } // Check whether the alternate setting to use or the device state has changed. // We have to check the device state to handle the unplugging of the cable. We can't // rely on the always-outstanding read to complete with an error, because the driver // doesn't have a well-defined error code for this. if (iUsbState & KUsbAlternateSetting) { iUsbState &= ~KUsbAlternateSetting; LOG1(_L("switched to alternate setting %d"), iUsbState); if (iUsbState == KObexAlt0) { // alternate setting 0 - function inactive if (iTransportUp) { FLOG(_L("CObexUsbHandler::RunL - transport layer going down...")); iTransportUp = EFalse; iOwner.TransportDown(); } } else if (iUsbState == KObexAlt1) { // alternate setting 1 - function active if (!iTransportUp) { FLOG(_L("CObexUsbHandler::RunL - transport layer coming up...")); // Now the transport is selected, DMA can be allocated to the endpoints, if required AllocateDma(); iTransportUp = ETrue; iOwner.TransportUp(); } } else { FLOG(_L("WARNING: unknown setting!")); } } else { TUsbcDeviceState deviceState = static_cast<TUsbcDeviceState>(iUsbState); switch(deviceState) { case EUsbcDeviceStateUndefined: case EUsbcDeviceStateAttached: case EUsbcDeviceStatePowered: case EUsbcDeviceStateDefault: case EUsbcDeviceStateAddress:#ifdef ERRONEOUS_SUSPEND_INDICATIONS // On Lubbock, we sometimes get "suspended" when the cable is unplugged. case EUsbcDeviceStateSuspended:#endif // ERRONEOUS_SUSPEND_INDICATIONS if (iTransportUp) { FLOG(_L("CObexUsbHandler::RunL - transport layer going down...")); iTransportUp = EFalse; iOwner.TransportDown(); } break;#ifndef ERRONEOUS_SUSPEND_INDICATIONS case EUsbcDeviceStateSuspended: break;#endif // !ERRONEOUS_SUSPEND_INDICATIONS case EUsbcDeviceStateConfigured: // Normally, we don't do anything here, because the transport only goes up when // we actually receive the alternate interface change.#ifdef NO_ALTERNATE_USB_INTERFACE_SUPPORT if (!iTransportUp) { FLOG(_L("Device now configured: transport layer coming up...")); iTransportUp = ETrue; // DMA can be allocated to the endpoints, if required // This has to be done here for no alternate interface devices // as they will not receive interface change notification AllocateDma(); iOwner.TransportUp(); }#endif // NO_ALTERNATE_USB_INTERFACE_SUPPORT break; default: __ASSERT_DEBUG(false, PANIC(KPanicCat, EUnknownUsbState)); break; } } // Await further notification of a state change. We may have called Error(), which // would already have set this notification request. if (!IsActive()) { iUsb.AlternateDeviceStatusNotify(iStatus, iUsbState); SetActive(); } FLOG(_L("CObexUsbHandler::RunL finished")); }/**Standard active object cancellation function.*/void CObexUsbHandler::DoCancel() { LOG_FUNC iUsb.AlternateDeviceStatusNotifyCancel(); }/**Registers the required comms and data interfaces.*/void CObexUsbHandler::RegInterfacesL() { LOG_FUNC // Setup a CDC Communication Class interface TUsbcInterfaceInfoBuf ifc; // Extract the interface string from the transport info structure we were // passed on construction. This isn't const because the SetInterface API // requires it not to be. // This is the only point where iObexTransportInfo is used directly // Do not use iObexTransportInfo outside of construction phases TPtrC string(iObexTransportInfo.iInterfaceStringDescriptor); LOG1(_L("Using interface string \"%S\""), &string); ifc().iString = &string; ifc().iClass.iClassNum = KObexClassNumber; ifc().iClass.iSubClassNum = KObexSubClassNumber; ifc().iClass.iProtocolNum = KObexProtocolNumber; ifc().iTotalEndpointsUsed = 0; // Indicate that this interface does not expect any control transfers // from EP0. ifc().iFeatureWord |= KUsbcInterfaceInfo_NoEp0RequestsPlease; FLOG(_L("CObexUsbListeningConnector::RegInterfacesL - setting comms interface")); TInt err = iUsbComms.SetInterface(0, ifc); if (err != KErrNone) { LOG1(_L("SetInterface failed with error %d"), err); LEAVEIFERRORL(err); } // Get the interface number for later TInt intDescSize; err = iUsbComms.GetInterfaceDescriptorSize(0,intDescSize); if(err != KErrNone) { LOG1(_L("Failed to get interface descriptor size. Err = %d"),err); intDescSize = KObexDefaultInterfaceDescriptorLength; //Default is 100 } HBufC8* interfaceDescriptor; interfaceDescriptor = HBufC8::NewLC(intDescSize); TPtr8 pIntDesc = interfaceDescriptor->Des(); iUsbComms.GetInterfaceDescriptor(0, pIntDesc); TUint8 obexIntNo = interfaceDescriptor->Ptr()[2]; CleanupStack::PopAndDestroy(); TBuf8<KObexInterfaceDescriptorBlockLength> desc; // Comms Class Header Functional Descriptor desc.Append(KObexFunctionalDescriptorLength); desc.Append(KUsbDescType_CS_Interface); desc.Append(KHeaderFunctionalDescriptor); desc.Append(KCdcVersionNumber[0]); desc.Append(KCdcVersionNumber[1]); // Obex Functional Descriptor desc.Append(KObexFunctionalDescriptorLength); desc.Append(KUsbDescType_CS_Interface); desc.Append(KObexFunctionalDescriptor); desc.Append(KWmcdcVersionNumber[0]); desc.Append(KWmcdcVersionNumber[1]); // Union Functional Descriptor desc.Append(KObexFunctionalDescriptorLength); desc.Append(KUsbDescType_CS_Interface); desc.Append(KUnionFunctionalDescriptor); desc.Append(obexIntNo); TInt dataInt = obexIntNo + 1; desc.Append(static_cast<TUint8>(dataInt)); err = iUsbComms.SetCSInterfaceDescriptorBlock(0, desc); if (err != KErrNone) { LOG1(_L("SetCSInterfaceDescriptorBlock failed with error %d"), err); LEAVEIFERRORL(err); } // Setup CDC Data Class interfaces // Only set the "function inactive" interface (alternate setting 0) // if the device can support alternate interfaces#ifndef NO_ALTERNATE_USB_INTERFACE_SUPPORT TUsbcInterfaceInfoBuf dataifc; dataifc().iString = NULL; dataifc().iClass.iClassNum = KObexDataClass; dataifc().iClass.iSubClassNum = KObexDataSubClass; dataifc().iClass.iProtocolNum = 0; dataifc().iTotalEndpointsUsed = 0; // Indicate that this interface does not expect any control transfers // from EP0. dataifc().iFeatureWord |= KUsbcInterfaceInfo_NoEp0RequestsPlease; FLOG(_L("Setting data class interface (alt setting 0")); err = iUsb.SetInterface(0 /*alternate setting*/, dataifc, iBandwidthPriority); if (err != KErrNone) { LOG1(_L("Cannot set data interface (alternate setting 0): error %d"), err); LEAVEIFERRORL(err); } // If anything below this point leaves, make sure this alternate setting // gets released. CleanupStack::PushL(TCleanupItem(CleanupReleaseInterface0, &iUsb));#endif // NO_ALTERNATE_USB_INTERFACE_SUPPORT // Check the device has enough endpoints for the "function active" data interface TUsbDeviceCaps dCaps; LEAVEIFERRORL(iUsb.DeviceCaps(dCaps)); TInt n = dCaps().iTotalEndpoints; if (n < KObexMinNumEndpoints) { LEAVEIFERRORL(KErrOverflow); } // Use the hardware's HS capability to determine maximum bulk transfer packet size TInt maxPacketSize = (dCaps().iHighSpeed) ? KMaxPacketTypeBulkHS : KMaxPacketTypeBulkFS; // Get information on the available endpoints from the driver TUsbcEndpointData data[KUsbcMaxEndpoints]; TPtr8 dataptr(REINTERPRET_CAST(TUint8*, data), sizeof(data), sizeof(data)); LEAVEIFERRORL(iUsb.EndpointCaps(dataptr)); // Check to see if there are suitably capable IN and OUT endpoints available // and fill dataifc2 structure accordingly. // // NOTE: The order the iEndpointData array is filled matches the order used for the // virtual endpoint numbers KTransmitEndpoint and KReceiveEndpoint - so change with caution! // // The virtual endpoint numbers are 1-based, whereas the iEndpointData array is 0 based // hence the subtraction in the array indices // // NOTE: IN and OUT refer to the host so in this case: // IN => device to PC = KTransmitEndpoint // OUT => PC to device = KReceiveEndpoint TUsbcInterfaceInfoBuf dataifc2; TBool foundIn = EFalse; TBool foundOut = EFalse; for (TInt i = 0; !(foundIn && foundOut) && i < n; i++) { const TUsbcEndpointCaps* caps = &data[i].iCaps; if (data[i].iInUse) { continue; } const TUint KBulkInFlags = KUsbEpTypeBulk | KUsbEpDirIn; const TUint KBulkOutFlags = KUsbEpTypeBulk | KUsbEpDirOut; if (!foundIn && (caps->iTypesAndDir & KBulkInFlags) == KBulkInFlags) { dataifc2().iEndpointData[KTransmitEndpoint - 1].iType = KUsbEpTypeBulk; dataifc2().iEndpointData[KTransmitEndpoint - 1].iDir = KUsbEpDirIn; TInt maxSize = caps->MaxPacketSize(); if (maxSize > maxPacketSize) { maxSize = maxPacketSize; } dataifc2().iEndpointData[KTransmitEndpoint - 1].iSize = maxSize; // Allocate dma if requested and the device support resource allocation scheme version 2 // for resource allocation scheme version1, refer to AllocateDma() if (iRequestDmaOnInEndpoint && ((dCaps().iFeatureWord1 & KUsbDevCapsFeatureWord1_EndpointResourceAllocV2) != 0)) { dataifc2().iEndpointData[KTransmitEndpoint - 1].iFeatureWord1 |= KUsbcEndpointInfoFeatureWord1_DMA; } foundIn = ETrue; } else if (!foundOut && (caps->iTypesAndDir & KBulkOutFlags) == KBulkOutFlags) { dataifc2().iEndpointData[KReceiveEndpoint - 1].iType = KUsbEpTypeBulk; dataifc2().iEndpointData[KReceiveEndpoint - 1].iDir = KUsbEpDirOut; TInt maxSize = caps->MaxPacketSize(); if (maxSize > maxPacketSize) { maxSize = maxPacketSize; } dataifc2().iEndpointData[KReceiveEndpoint - 1].iSize = maxSize; //Allocate dma here if requested and the device support resource allocation scheme version 2 // for resource allocation scheme version1, refer to AllocateDma() if (iRequestDmaOnOutEndpoint && ((dCaps().iFeatureWord1 & KUsbDevCapsFeatureWord1_EndpointResourceAllocV2) != 0)) { dataifc2().iEndpointData[KReceiveEndpoint - 1].iFeatureWord1 |= KUsbcEndpointInfoFeatureWord1_DMA; } foundOut = ETrue; } } if (!(foundIn && foundOut)) { LEAVEIFERRORL(KErrHardwareNotAvailable); } // Set the active interface. dataifc2().iString = NULL; dataifc2().iClass.iClassNum = KObexDataClass; dataifc2().iClass.iSubClassNum = KObexDataSubClass; dataifc2().iClass.iProtocolNum = 0; dataifc2().iTotalEndpointsUsed = KObexTotalEndpoints; // Indicate that this interface does not expect any control transfers // from EP0. dataifc2().iFeatureWord |= KUsbcInterfaceInfo_NoEp0RequestsPlease;#ifdef NO_ALTERNATE_USB_INTERFACE_SUPPORT // For devices that don't suport alternate interfaces, have to place the "function active" // interface at alternate setting 0, although the CDC specification says if should be alternate setting 1 FLOG(_L("Setting data class interface (no alternate interface support)")); err = iUsb.SetInterface(0 /*alternate setting*/, dataifc2, iBandwidthPriority); if (err != KErrNone) { //FTRACE(FPrint(_L("Cannot set data interface (no alternate interface support): error %d"), err)); LOG1(_L("Cannot set data interface (no alternate interface support): error %d"), err); LEAVEIFERRORL(err); }#else FLOG(_L("Setting data class interface (alternate setting 1)")); err = iUsb.SetInterface(1 /*alternate setting*/, dataifc2, iBandwidthPriority); if (err != KErrNone) { //FTRACE(FPrint(_L("Cannot set data interface (alternate setting 1): error %d"), err)); LOG1(_L("Cannot set data interface (alternate setting 1): error %d"), err); LEAVEIFERRORL(err); } CleanupStack::Pop(); // ReleaseInterface0#endif FLOG(_L("CObexUsbHandler::RegInterfacesL - finished.")); }/**Utility function which releases the first alternate setting of the specifiedinterface. Used when setting up Obex interfaces.@param aInterface The interface to release*/void CObexUsbHandler::CleanupReleaseInterface0(TAny* aInterface) { reinterpret_cast<RDevUsbcClient*>(aInterface)->ReleaseInterface(0 /*alternate setting*/); }/**Accept an incoming connection.*/void CObexUsbHandler::Start() { LOG_FUNC // Await notification of a state change (this is like waiting for a connect...). iUsb.AlternateDeviceStatusNotify(iStatus, iUsbState); SetActive(); }/**Utility function to allocate DMA to the bulk endpoints - if they have been requestedThis function is called during a connect, once the device is configured,so if the allocations fail it wont complain in release builds but debug builds willpanic with either EDmaAllocationFailedEndpointIn or EDmaAllocationFailedEndpointOut. */void CObexUsbHandler::AllocateDma() { LOG_FUNC TUsbDeviceCaps dCaps; iUsb.DeviceCaps(dCaps); if ((dCaps().iFeatureWord1 & KUsbDevCapsFeatureWord1_EndpointResourceAllocV2) != 0) { // for resource allocation version2, refer to CObexUsbHandler::RegInterfacesL() return; } if (iRequestDmaOnInEndpoint) { TInt err = iUsb.AllocateEndpointResource(KTransmitEndpoint, EUsbcEndpointResourceDMA); // The following log message is checked for in the test code - change with caution! LOG1(_L("IN Endpoint DMA resource allocation result %d"), err); (void)err; // to stop compilers complaining about unused variables } if (iRequestDmaOnOutEndpoint) { TInt err = iUsb.AllocateEndpointResource(KReceiveEndpoint, EUsbcEndpointResourceDMA); // The following log message is checked for in the test code - change with caution! LOG1(_L("OUT Endpoint DMA resource allocation result %d"), err); (void)err; // to stop compilers complaining about unused variables } }///**Constructs a CObexUsbStaller object@param aUsb Reference to a RDevUsbcClient object.@param aOwner Reference to a MObexUsbConnector that owns this handler@return A new CObexUsbStaller object*/CObexUsbStaller* CObexUsbStaller::NewL(RDevUsbcClient& aUsb, MObexUsbConnector& aOwner) { CObexUsbStaller* self = new(ELeave) CObexUsbStaller(aUsb, aOwner); return self; }/**CObexUsbStaller constructor.@param aUsb Reference to a RDevUsbcClient object.@param aOwner Reference to a MObexUsbConnector that owns this handler*/CObexUsbStaller::CObexUsbStaller(RDevUsbcClient& aUsb, MObexUsbConnector& aOwner) : CActive(EPriorityStandard), iUsb(aUsb), iOwner(aOwner) { CActiveScheduler::Add(this); }/**CObexUsbStaller destructor.*/CObexUsbStaller::~CObexUsbStaller() { Cancel(); }/**Starts the staller*/void CObexUsbStaller::Start() { LOG_FUNC if (!IsActive()) { FLOG(_L("Halting transmit endpoint...")); iUsb.HaltEndpoint(KTransmitEndpoint); iUsb.EndpointStatusNotify(iStatus, iEndpointStatus); SetActive(); } }/**Called when the transmit endpoint has changed state*/void CObexUsbStaller::RunL() { LOG_FUNC TEndpointState endpointState; iUsb.EndpointStatus(KTransmitEndpoint, endpointState); #ifdef __FLOG_ACTIVE switch (endpointState) { case EEndpointStateNotStalled: { FLOG(_L("Receive endpoint not stalled")); break; } case EEndpointStateStalled: { FLOG(_L("Receive endpoint stalled")); break; } case EEndpointStateUnknown: { FLOG(_L("Receive endpoint unknown state")); break; } }#endif if (endpointState == EEndpointStateNotStalled) { LOG1(_L("CObexUsbStallWatcher::RunL -- endpoint no longer stalled (0x%08x)"), iEndpointStatus); iOwner.StallCleared(); } else { LOG1(_L("CObexUsbStallWatcher::RunL -- endpoint still stalled (0x%08x)"), iEndpointStatus); iUsb.EndpointStatusNotify(iStatus, iEndpointStatus); SetActive(); } }/**Standard active object error function.@return KErrNone because currently nothing should cause this to be called.*/TInt CObexUsbStaller::RunError(TInt /*aError*/) { LOG_FUNC return KErrNone; }/**Standard active object cancellation function.*/ void CObexUsbStaller::DoCancel() { FLOG(_L("CObexUsbStaller -- Cancelling status notification")); iUsb.EndpointStatusNotifyCancel(); }