diff -r 000000000000 -r c9bc50fca66e usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/CdcAcmClass.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/CdcAcmClass.cpp Tue Feb 02 02:02:59 2010 +0200 @@ -0,0 +1,1077 @@ +/* +* 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: +* +*/ + +#include "CdcAcmClass.h" +#include "CdcControlInterface.h" +#include "CdcDataInterface.h" +#include "AcmPanic.h" +#include "AcmUtils.h" +#include "HostPushedChangeObserver.h" +#include "BreakController.h" +#include + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, "ECACM"); +#endif + +CCdcAcmClass::CCdcAcmClass() +/** + * Constructor. + */ + { + SetDefaultAcm(); + } + +CCdcAcmClass* CCdcAcmClass::NewL(const TUint8 aProtocolNum, const TDesC16& aAcmControlIfcName, const TDesC16& aAcmDataIfcName) +/** + * Create a new CCdcAcmClass object + * + * @return Ownership of a new CCdcAcmClass object + */ + { + LOG_STATIC_FUNC_ENTRY + + CCdcAcmClass* self = new(ELeave) CCdcAcmClass; + CleanupStack::PushL(self); + self->ConstructL(aProtocolNum, aAcmControlIfcName, aAcmDataIfcName); + CLEANUPSTACK_POP(self); + return self; + } + +void CCdcAcmClass::ConstructL(const TUint8 aProtocolNum, const TDesC16& aControlIfcName, const TDesC16& aDataIfcName) +/** + * 2nd-phase construction. Creates both the control and data objects. + * @param aProtocolNum contains the Table 17 protocol number. + * @param aControlIfcName contains the interface name + * @param aDataIfcName contains the interface name + */ + { + TUint8 interfaceNumber; + TInt res; + + LOGTEXT(_L8("\tabout to create control interface with name")); + iControl = CCdcControlInterface::NewL(*this, aProtocolNum, aControlIfcName); + + LOGTEXT(_L8("\tabout to create data interface with name")); + iData = CCdcDataInterface::NewL(aDataIfcName); + + iBreakController = CBreakController::NewL(*this); + + LOGTEXT(_L8("\tabout to call GetInterfaceNumber")); + res = iData->GetInterfaceNumber(interfaceNumber); + if ( res ) + { + LOGTEXT2(_L8("\tGetInterfaceNumber=%d"), res); + LEAVEIFERRORL(res); + } + + LOGTEXT(_L8("\tabout to call SetupClassSpecificDescriptor")); + res = iControl->SetupClassSpecificDescriptor(interfaceNumber); + if ( res ) + { + LOGTEXT2(_L8("\tSetupClassSpecificDescriptor=%d"), res); + LEAVEIFERRORL(res); + } + } + +CCdcAcmClass::~CCdcAcmClass() +/** + * Destructor. + */ + { + LOG_FUNC + + delete iControl; + delete iData; + delete iBreakController; + } + +void CCdcAcmClass::SetCallback(MHostPushedChangeObserver* aCallback) +/** + * Set the ACM class callback object. This cannot be done at construction + * because the ACM class and the port have different lifetimes. + */ + { + LOGTEXT2(_L8(">>CCdcAcmClass::SetCallback aCallback=0x%08x"), aCallback); + + iCallback = aCallback; + + // remember that this function can also be called to + // unset the callback when it is given a NULL pointer + if ( iCallback ) + { + // send off whatever has been seen from Host + // requests to change the line coding + TCommConfigV01 epocConfig; + ConvertUsbConfigCodingToEpoc(iUsbConfig,epocConfig); + iCallback->HostConfigChange(epocConfig); + + // send off whatever has been seen from Host + // requests to set the control line state + iCallback->HostSignalChange(iDtrState,iRtsState); + } + + LOGTEXT(_L8("<>CCdcAcmClass::SetBreakCallback aBreakCallback=0x%08x"), + aBreakCallback); + + iBreakCallback = aBreakCallback; + + LOGTEXT(_L8("<>CCdcAcmClass::ReadOneOrMore aObserver=0x%08x"), + &aObserver); + + ReadOneOrMore(aObserver, aDes, aDes.Length()); + + LOGTEXT(_L8("<>CCdcAcmClass::ReadOneOrMore aObserver=0x%08x, " + "aLength=%d"), + &aObserver, aLength); + + __ASSERT_DEBUG(iData, _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + iData->ReadOneOrMore(aObserver, aDes, aLength); + + LOGTEXT(_L8("<>CCdcAcmClass::Read aObserver=0x%08x"), &aObserver); + + Read(aObserver, aDes, aDes.Length()); + + LOGTEXT(_L8("<>CCdcAcmClass::Read aObserver=0x%08x, aLength=%d"), + &aObserver, aLength); + + __ASSERT_DEBUG(iData, _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + iData->Read(aObserver, aDes, aLength); + + LOGTEXT(_L8("<CancelRead(); + } + +void CCdcAcmClass::Write(MWriteObserver& aObserver, const TDesC8& aDes) +/** + * Write to the bus. + * + * @param aObserver The observer to notify completion to. + * @param aDes Descriptor containing the data to be written. + */ + { + LOGTEXT2(_L8(">>CCdcAcmClass::Write aObserver=0x%08x"), + &aObserver); + + Write(aObserver, aDes, aDes.Length()); + + LOGTEXT(_L8("<>CCdcAcmClass::Write aObserver=0x%08x, aLength=%d"), + &aObserver, aLength); + + __ASSERT_DEBUG(iData, _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + iData->Write(aObserver, aDes, aLength); + + LOGTEXT(_L8("<CancelWrite(); + } + +TInt CCdcAcmClass::HandleGetCommFeature(const TUint16 aSelector, + TDes8& aReturnData) +/** + * Callback for Get Comm Feature requests. + * + * @param aSelector Multiplex control for the feature is held in wValue field + * @param aReturnData Descriptor containing the multiplexed and idle state of + * the ACM device or the country code from ISO 3166. + */ + { + LOGTEXT2(_L8(">>CCdcAcmClass::HandleGetCommFeature aSelector=%d"), + aSelector); + + TInt ret = KErrNone; + + // check the feature selector from the header and reject if invalid, + // otherwise deal with it. + switch ( aSelector ) + { + case EUsbCommFeatureSelectAbstractState: + { + aReturnData.SetLength(2); + + TUint8* pbuffer; + TUint8** ppbuffer; + pbuffer = &aReturnData[0]; + ppbuffer = &pbuffer; + + CCdcControlInterface::PutU16(ppbuffer,iAcmState); + + LOGTEXT2(_L8("\tAbstract State [0x%04X]"), iAcmState); + } + break; + + case EUsbCommFeatureSelectCountryCode: + { +#if defined(DISABLE_ACM_CF_COUNTRY_SETTING) + + aReturnData.SetLength(0); + LOGTEXT(_L8("\tCountry Code Not Supported")); + ret = KErrNotSupported; + +#elif defined(ENABLE_ACM_CF_COUNTRY_SETTING) + + aReturnData.SetLength(2); + + TUint8* pbuffer; + TUint8** ppbuffer; + pbuffer = &aReturnData[0]; + ppbuffer = &pbuffer; + + CCdcControlInterface::PutU16(ppbuffer,iCountryCode); + + LOGTEXT2(_L8("\tCountry Code [0x%04X]"), iCountryCode); + +#endif + } + break; + + default: + aReturnData.SetLength(0); + LOGTEXT(_L8("\tBad Selector")); + ret = KErrUnknown; + } + + LOGTEXT2(_L8("<>CCdcAcmClass::HandleClearCommFeature aSelector=%d"), + aSelector); + + TInt ret = KErrNone; + + // check the feature selector from the header and reject if invalid, + // otherwise deal with it. + switch ( aSelector ) + { + case EUsbCommFeatureSelectAbstractState: + { + // reset to guaranteed-success default, so ignore return value + static_cast(HandleNewAbstractState(EUsbAbstractStateDataMultiplex)); + LOGTEXT2(_L8("\tAbstract State [0x%04X]"), iAcmState); + } + break; + + case EUsbCommFeatureSelectCountryCode: + { +#if defined(DISABLE_ACM_CF_COUNTRY_SETTING) + + LOGTEXT(_L8("\tCountry Code Not Supported")); + ret = KErrNotSupported; + +#elif defined(ENABLE_ACM_CF_COUNTRY_SETTING) + + HandleNewCountryCode(KUsbCommCountryCode0); + LOGTEXT2(_L8("\tCountry Code [0x%04X]"), iCountryCode); + +#endif + } + break; + + default: + { + LOGTEXT(_L8("\tBad Selector")); + ret = KErrUnknown; + } + } + + LOGTEXT2(_L8("<(&aData[0]); + ppbuffer = &pbuffer; + + iUsbConfig.iRate = CCdcControlInterface::GetU32(ppbuffer); + iUsbConfig.iStopBits = static_cast(CCdcControlInterface::GetU08(ppbuffer)); + iUsbConfig.iParity = static_cast(CCdcControlInterface::GetU08(ppbuffer)); + iUsbConfig.iDataBits = static_cast(CCdcControlInterface::GetU08(ppbuffer)); + + if ( iCallback ) + { + TCommConfigV01 epocConfig; + + ConvertUsbConfigCodingToEpoc(iUsbConfig, epocConfig); + + iCallback->HostConfigChange(epocConfig); + } + + return KErrNone; + } + +void CCdcAcmClass::SetDefaultAcm() +/** + * Reset the data fields to a set of known default values. Will be used when + * we implement restart, which involves a host re-enumeration without + * destroying the ACM class, so we need a function to reset the ACM to + * defaults. + */ + { + iUsbConfig.iRate = 115200; + iUsbConfig.iStopBits = EUsbStopBitOne; + iUsbConfig.iParity = EUsbParityNone; + iUsbConfig.iDataBits = EUsbDataBitsEight; + + iAcmState = EUsbAbstractStateDataMultiplex; + iCountryCode = 0x2A2A; // 0x2A2A is "**"- just an invalid code + + iAcmDataMultiplex = (iAcmState & EUsbAbstractStateDataMultiplex) + ? ETrue : EFalse; + iAcmIdleSetting = (iAcmState & EUsbAbstractStateIdleSetting) + ? ETrue : EFalse; + + iRtsState = EFalse; + iDtrState = EFalse; + + iRingState = EFalse; + iDsrState = EFalse; + iDcdState = EFalse; + + iBreakActive = EFalse; + } + +void CCdcAcmClass::ConvertUsbConfigCodingToEpoc(const TUsbConfig& aUsbConfig, + TCommConfigV01& aEpocConfig) +/** + * Convert configuration information received from a Host in "USB" format to + * the native "EPOC" configuration format. + * + * @param aUsbConfig The USB configuration to be converted from. + * @param aEpocConfig The EPOC configuration to be converted to. + */ + { + switch ( aUsbConfig.iDataBits ) + { + case EUsbDataBitsFive: + aEpocConfig.iDataBits = EData5; + break; + + case EUsbDataBitsSix: + aEpocConfig.iDataBits = EData6; + break; + + case EUsbDataBitsSeven: + aEpocConfig.iDataBits = EData7; + break; + + case EUsbDataBitsEight: + aEpocConfig.iDataBits = EData8; + break; + + // EPOC doesn't support this, so map it to "8" for the time being... + case EUsbDataBitsSixteen: + aEpocConfig.iDataBits = EData8; + break; + + // Map any other signals to "8"... + default: + aEpocConfig.iDataBits = EData8; + break; + } + + switch ( aUsbConfig.iStopBits ) + { + case EUsbStopBitOne: + aEpocConfig.iStopBits = EStop1; + break; + + // EPOC doesn't have any "1.5" setting, so go for "1". + case EUsbStopBitOnePtFive: + aEpocConfig.iStopBits = EStop1; + break; + + case EUsbStopBitTwo: + aEpocConfig.iStopBits = EStop2; + break; + + // Map any other signals to "1"... + default: + aEpocConfig.iStopBits = EStop1; + break; + } + + switch ( aUsbConfig.iParity ) + { + case EUsbParityNone: + aEpocConfig.iParity = EParityNone; + break; + + case EUsbParityOdd: + aEpocConfig.iParity = EParityOdd; + break; + + case EUsbParityEven: + aEpocConfig.iParity = EParityEven; + break; + + case EUsbParityMark: + aEpocConfig.iParity = EParityMark; + break; + + case EUsbParitySpace: + aEpocConfig.iParity = EParitySpace; + break; + + // Map any other signals to "No Parity"... + default: + aEpocConfig.iParity = EParityNone; + break; + } + + static const TUint32 KUsbDataRate[] = { 50, 75, 110, + 134, 150, 300, + 600, 1200, 1800, + 2000, 2400, 3600, + 4800, 7200, 9600, + 19200, 38400, 57600, + 115200,230400, 460800, + 576000,1152000,4000000}; + + static const TBps KEpocDataRate[] = { EBps50, EBps75, EBps110, + EBps134, EBps150, EBps300, + EBps600, EBps1200, EBps1800, + EBps2000, EBps2400, EBps3600, + EBps4800, EBps7200, EBps9600, + EBps19200, EBps38400, EBps57600, + EBps115200,EBps230400, EBps460800, + EBps576000,EBps1152000,EBps4000000}; + + aEpocConfig.iRate = EBpsSpecial; // Default to "Special" + TInt arraySize = sizeof(KUsbDataRate)/sizeof(TUint32); + for ( TInt i = 0 ; i < arraySize ; i++ ) + { + if ( aUsbConfig.iRate == KUsbDataRate[i] ) + { + aEpocConfig.iRate = KEpocDataRate[i]; + break; + } + } + } + +TInt CCdcAcmClass::HandleNewAbstractState(const TUint16 aAbstractState) +/** + * Handle new Abstract State as received from Host, check values + * and inform client watcher (if registered) + * @param aAbstractState The new Abstract State which contains + * significant bits D0 and D1 where: + * + * D0 controls 'idle setting' -> iAcmIdleSetting + * D1 controls 'data multiplexed state' -> iAcmDataMultiplex + */ + { + LOGTEXT2(_L8(">>CCdcAcmClass::HandleNewAbstractState aAbstractState=%d"), + aAbstractState); + + TBool multiplex; + TBool idle; + + TUint16 newstate; + + TInt ret; + + // collect local booleans from incoming combo ready to do local + // discrepancy check + multiplex = (aAbstractState & EUsbAbstractStateDataMultiplex) + ? ETrue : EFalse; + idle = (aAbstractState & EUsbAbstractStateIdleSetting) + ? ETrue : EFalse; + + // apply any necessary overrides due to incomplete ACM class + // support (this may change the local booleans and also may be + // the reason to inform caller of a failure) + ret = KErrNone; + + if ( multiplex != iAcmDataMultiplex ) + { +#if defined(DISABLE_ACM_CF_DATA_MULTIPLEX) + // if this selector is disabled then the attempt to change the + // bit must fail + ret = KErrNotSupported; + + multiplex = iAcmDataMultiplex; +#endif + } + + if ( idle != iAcmIdleSetting ) + { +#if defined(DISABLE_ACM_CF_IDLE_SETTING) + // if this selector is disabled then the attempt to change the + // bit must fail + ret = KErrNotSupported; + + idle = iAcmIdleSetting; +#endif + } + + // save the new booleans into the private store + iAcmDataMultiplex = multiplex; + iAcmIdleSetting = idle; + + // recreate the private combo from these booleans + newstate = static_cast( + ( iAcmIdleSetting ? EUsbAbstractStateIdleSetting : EUsbAbstractStateNoBits ) + | ( iAcmDataMultiplex ? EUsbAbstractStateDataMultiplex : EUsbAbstractStateNoBits) + ); + + // discrepancy check to see if the client application needs to be + // informed, note that the callback may not have been placed. + if( iAcmState != newstate ) + { + LOGTEXT2(_L8("\tNew Combo [0x%04X]"), newstate); + + if ( iCallback ) + { + // If there was such a function in class + // MHostPushedChangeObserver, notify + // via iCallback->HostAbstractStateChange(newstate); + LOGTEXT(_L8("\tHas No Notification Method in class MHostPushedChangeObserver")); + } + } + + // and save the new state ready for next check + iAcmState = newstate; + + LOGTEXT2(_L8("<>CCdcAcmClass::HandleNewCountryCode aCountryCode=%d"), + aCountryCode); + + TInt ret; + +#if defined(DISABLE_ACM_CF_COUNTRY_SETTING) + + // cite the incoming parameter to suppress 'unreferenced formal parameter' + // warning and then set the return value to show this code does not handle + // the country code. + (void)aCountryCode; + + ret = KErrNotSupported; + +#else + + // check the received code against the array in the descriptor + // and confirm it is one of those present, otherwise return + // 'unknown' : for now, just pretend it always works + iCountryCode = aCountryCode; + + ret = KErrNone; + +#endif + + LOGTEXT2(_L8("<>CCdcAcmClass::HandleSetCommFeature aSelector=%d"), + aSelector); + + // reject any message that has malformed data + if ( aData.Length() != 2 ) + { + LOGTEXT2(_L8("\t***aData.Length (%d) incorrect"), aData.Length()); + LOGTEXT2(_L8("<(&aData[0]); + ppbuffer = &pbuffer; + + newstate = CCdcControlInterface::GetU16(ppbuffer); + + if ( newstate != iAcmState ) + { + ret = HandleNewAbstractState(newstate); + + LOGTEXT4(_L8("\tHandleNewAbstractState=%d [0x%04X]->[0x%04X]"), + ret, iAcmState, newstate); + } + } + break; + + case EUsbCommFeatureSelectCountryCode: + { +#if defined(DISABLE_ACM_CF_COUNTRY_SETTING) + + LOGTEXT(_L8("Country Code Not Supported")); + ret = KErrNotSupported; + +#elif defined(ENABLE_ACM_CF_COUNTRY_SETTING) + + TUint16 newcountry; + + TUint8* pbuffer; + TUint8** ppbuffer; + pbuffer = &aData[0]; + ppbuffer = &pbuffer; + + newcountry = CCdcControlInterface::GetU16(ppbuffer); + + if( newcountry != iCountryCode ) + { + ret = HandleNewCountryCode(newcountry); + + LOGTEXT4(_L8("\tHandleNewCountryCode=%d [0x%04X]->[0x%04X]"), + ret, iCountryCode, newcountry); + } + +#endif + break; + } + + default: + { + LOGTEXT(_L8("\tBad Selector")); + ret = KErrUnknown; + } + } + + LOGTEXT2(_L8("<(iUsbConfig.iStopBits)); + CCdcControlInterface::PutU08(ppbuffer, static_cast(iUsbConfig.iParity )); + CCdcControlInterface::PutU08(ppbuffer, static_cast(iUsbConfig.iDataBits)); + + return KErrNone; + } + +TInt CCdcAcmClass::HandleSetControlLineState(TBool aRtsState, TBool aDtrState) +/** + * Callback for Set Control Line State requests + * + * @param aRtsState RTS state. + * @param aDtrState DTR state. + */ + { + LOGTEXT3(_L8(">>CCdcAcmClass::HandleSetControlLineState aRtsState=%d, " + "aDtrState=%d"), + aRtsState, aDtrState); + + iRtsState = aRtsState; + iDtrState = aDtrState; + if ( iCallback ) + { + iCallback->HostSignalChange(iDtrState,iRtsState); + } + + LOGTEXT(_L8("<BreakRequest(CBreakController::EHost, + CBreakController::EInactive); + } + else if ( aDuration == 0xFFFF ) + { + ret = iBreakController->BreakRequest(CBreakController::EHost, + CBreakController::ELocked); // i.e. locked ON, until further + // notice. + } + else + { + ret = iBreakController->BreakRequest(CBreakController::EHost, + CBreakController::ETiming, + // Convert from host-supplied milliseconds to microseconds. + aDuration*1000); + } + + LOGTEXT2(_L8("\tret = %d"), ret); + return ret; + } + +TInt CCdcAcmClass::SendSerialState(TBool aRing, TBool aDsr, TBool aDcd) +/** + * Send a change in serial state to the host. + * + * Note that Overrun, Parity and Framing errors are not currently supported, + * so are stubbed out with EFalse. + * + * Note that the BREAK signal is managed locally in the break controller + * Active Object. + */ + { + LOGTEXT4(_L8(">>CCdcAcmClass::SendSerialState aRing=%d, " + "aDsr=%d, aDcd=%d"), + aRing, aDsr, aDcd); + + // stub non-supported flags + TBool overrun = EFalse; + TBool parity = EFalse; + TBool framing = EFalse; + + // Save incoming state flags for possible use when notifying break + // changes. + iRingState = aRing; + iDsrState = aDsr; + iDcdState = aDcd; + + // send new state off to the host (this will use an interrupt transfer, + // and will handle all discrepancy checking to suppress same-state + // notifications) + TInt ret = iControl->SendSerialState(overrun, + parity, + framing, + aRing, + BreakActive(), + aDsr, + aDcd); + + LOGTEXT2(_L8("<BreakRequest(aRequester, aState, aDelay); + LOGTEXT2(_L8("\tBreakRequest = %d"), err); + return err; + } + +void CCdcAcmClass::NotifyDataAvailable(MNotifyDataAvailableObserver& aObserver) +/** + * Notify the caller when data is available to be read from the LDD. + * + * @param aObserver The observer to notify completion to. + */ + { + LOGTEXT2(_L8(">>CCdcAcmClass::NotifyDataAvailable aObserver=0x%08x"), &aObserver); + + __ASSERT_DEBUG(iData, _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + iData->NotifyDataAvailable(aObserver); + + LOGTEXT(_L8("<CancelNotifyDataAvailable(); + } + +// +// End of file