diff -r 4dc88a4ac6f4 -r f6055a57ae18 obex/obexprotocol/obexusbtransport/src/usbconn.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/obex/obexprotocol/obexusbtransport/src/usbconn.cpp Tue Oct 19 11:00:12 2010 +0800 @@ -0,0 +1,1321 @@ +// 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 +#include "usbconn.h" +#include +#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 used +when 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(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(aConn)->SignalDown(KErrIrObexClientPeerDoesNotHaveObex); + return KErrNone; + } + + +/** +Cancel an active connection attempt. This is unlikely to succeed, as a client +connection 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 transport +down itself. It will only delete the transport object when an error +has occurred (such as the link being brought down) and hence the +transport 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 USB +host to initiate a CLEAR_FEATURE sequence, which notifies the server that the +Obex 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 two +reasons---it's called from a different class and it happens when an +error condition has been cleared. Currently it has no special +meaning, 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 phases +The constructor copies out most of the information it needs into member variables +apart 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(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(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 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(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 specified +interface. Used when setting up Obex interfaces. + +@param aInterface The interface to release +*/ +void CObexUsbHandler::CleanupReleaseInterface0(TAny* aInterface) + { + reinterpret_cast(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 requested +This 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 will +panic 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(); + } +