diff -r 000000000000 -r c9bc50fca66e usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/AcmReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/AcmReader.cpp Tue Feb 02 02:02:59 2010 +0200 @@ -0,0 +1,870 @@ +/* +* Copyright (c) 2003-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 +#include "AcmReader.h" +#include "AcmUtils.h" +#include "ActiveReader.h" +#include "ActiveReadOneOrMoreReader.h" +#include "CdcAcmClass.h" +#include "AcmPanic.h" +#include "AcmPort.h" +#include + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, "ECACM"); +#endif + +CAcmReader* CAcmReader::NewL(CAcmPort& aPort, + TUint aBufSize) +/** + * Factory function. + * + * @param aPort The CAcmPort parent. + * @param aBufSize The size of the buffer. + * @return Ownership of a new CAcmReader. + */ + { + LOG_STATIC_FUNC_ENTRY + + CAcmReader* self = new(ELeave) CAcmReader(aPort, aBufSize); + CleanupStack::PushL(self); + self->ConstructL(); + CLEANUPSTACK_POP(self); + return self; + } + +CAcmReader::~CAcmReader() +/** + * Destructor. + */ + { + LOG_FUNC + + ReadCancel(); + + delete iBuffer; + } + +CAcmReader::CAcmReader(CAcmPort& aPort, + TUint aBufSize) +/** + * Constructor. + * + * @param aPort The CPort parent. + * @param aBufSize The size of the buffer. + */ + : iBufSize(aBufSize), + iInBuf(NULL,0,0), + iPort(aPort) + { + } + +void CAcmReader::ConstructL() +/** + * 2nd-phase construction. + */ + { + // Create iBuffer. + LOGTEXT(_L8("\tabout to create iBuffer")); + LEAVEIFERRORL(SetBufSize(iBufSize)); + } + +void CAcmReader::Read(const TAny* aClientBuffer, TUint aMaxLen) +/** + * Read API. + * + * @param aClientBuffer Pointer to the client's memory space. + * @param aMaxLen Maximum length to read. + */ + { + LOG_FUNC + LOGTEXT3(_L8("\taClientBuffer=0x%08x, aMaxLen=%d"), + aClientBuffer, aMaxLen); + + // Check we're open to requests and make a note of interesting data. + // including iLengthToGo + CheckNewRequest(aClientBuffer, aMaxLen); + iCurrentRequest.iRequestType = ERead; + + // A read larger than our internal buffer limit will result in us doing + // multiple reads to the ports below. + // We used to just refuse the request with a KErrNoMemory. + if ( iTerminatorCount == 0 ) + { + ReadWithoutTerminators(); + } + else + { + ReadWithTerminators(); + } + } + +void CAcmReader::ReadWithoutTerminators() +/** + * Process a read request given that no terminator characters are set. + */ + { + LOG_FUNC + + // Can we complete immediately from the buffer? + const TUint bufLen = BufLen(); + LOGTEXT2(_L8("\thave %d bytes in buffer"), bufLen); + LOGTEXT2(_L8("\tLength to go is %d bytes"), iLengthToGo); + if ( iLengthToGo <= bufLen ) + { + LOGTEXT2(_L8("\tcompleting request immediately from buffer with %d " + "bytes"), iLengthToGo); + WriteBackData(iLengthToGo); + CompleteRequest(KErrNone); + return; + } + + // There isn't enough in the buffer to complete the request, so we write + // back as much as we have, and issue a Read for more. + if ( bufLen ) + { + LOGTEXT2(_L8("\twriting back %d bytes"), bufLen); + // Write back as much data as we've got already. + WriteBackData(bufLen); + } + + // Issue a read for the data we still need. + LOGTEXT2(_L8("\tRequesting read - require %d bytes"),iLengthToGo); + IssueRead(); + } + +void CAcmReader::ReadWithTerminators() +/** + * Process a read request given that terminator characters are set. + */ + { + LOG_FUNC + + // Can we complete immediately from the buffer? Search the buffer we have + // for any terminators. If found, complete back to the client. If not + // found, start issuing ReadOneOrMores until we either find one or run out + // of client buffer. + + const TUint bufLen = BufLen(); + LOGTEXT2(_L8("\tbufLen = %d"), bufLen); + if ( bufLen ) + { + CheckForBufferedTerminatorsAndProceed(); + return; + } + + // There's no buffered data. Get some. + IssueReadOneOrMore(); + } + +void CAcmReader::ReadOneOrMore(const TAny* aClientBuffer, TUint aMaxLen) +/** + * ReadOneOrMore API. Note that this is implemented to completely ignore + * terminator characters. + * + * @param aClientBuffer Pointer to the client's memory space. + * @param aMaxLen Maximum length to read. + */ + { + LOG_FUNC + LOGTEXT3(_L8("\taClientBuffer=0x%08x, aMaxLen=%d"), + aClientBuffer, aMaxLen); + + // Check we're open to requests and make a note of interesting data. + CheckNewRequest(aClientBuffer, aMaxLen); + + iCurrentRequest.iRequestType = EReadOneOrMore; + + // Check to see if there's anything in our buffer- if there is, we can + // complete immediately. + const TUint bufLen = BufLen(); + LOGTEXT2(_L8("\tbufLen = %d"), bufLen); + if ( bufLen ) + { + // Complete request with what's in the buffer + LOGTEXT2(_L8("\tcompleting request immediately from buffer with %d " + "bytes"), bufLen); + WriteBackData(bufLen); + CompleteRequest(KErrNone); + return; + } + + // Get some more data. + IssueReadOneOrMore(); + } + +void CAcmReader::NotifyDataAvailable() +/** + * NotifyDataAvailable API. If a request is pending completes the client with KErrInUse. + */ + { + LOG_FUNC + if(iCurrentRequest.iClientPtr) // a request is pending + { + iPort.NotifyDataAvailableCompleted(KErrInUse); + return; + } + iCurrentRequest.iClientPtr = iBuffer; + iCurrentRequest.iRequestType = ENotifyDataAvailable; + iPort.Acm()->NotifyDataAvailable(*this); + } + +void CAcmReader::NotifyDataAvailableCancel() +/** + * NotifyDataAvailableCancel API. Issues a ReadCancel() to abort pending requests on the LDD + * + */ + { + LOG_FUNC + if (ENotifyDataAvailable == iCurrentRequest.iRequestType) + { + // Cancel any outstanding request on the LDD. + if (iPort.Acm()) + { + LOGTEXT(_L8("\tiPort.Acm() exists- calling NotifyDataAvailableCancel() on it")); + iPort.Acm()->NotifyDataAvailableCancel(); + } + // Reset our flag to say there's no current outstanding request. What's + // already in our buffer can stay there. + iCurrentRequest.iClientPtr = NULL; + } + } + +void CAcmReader::ReadCancel() +/** + * Cancel API. Cancels any outstanding (Read or ReadOneOrMore) request. + */ + { + LOG_FUNC + + if (ENotifyDataAvailable != iCurrentRequest.iRequestType) + { + // Cancel any outstanding request on the LDD. + if (iPort.Acm()) + { + LOGTEXT(_L8("\tiPort.Acm() exists- calling ReadCancel on it")); + iPort.Acm()->ReadCancel(); + } + + // Reset our flag to say there's no current outstanding request. What's + // already in our buffer can stay there. + iCurrentRequest.iClientPtr = NULL; + } + } + +TUint CAcmReader::BufLen() const +/** + * This function returns the amount of data (in bytes) still remaining in the + * circular buffer. + * + * @return Length of data in buffer. + */ + { + LOGTEXT(_L8(">>CAcmReader::BufLen")); + + TUint len = 0; + if ( BufWrap() ) + { + LOGTEXT(_L8("\tbuf wrapped")); + len = iBufSize - ( iOutPtr - iInPtr ); + } + else + { + LOGTEXT(_L8("\tbuf not wrapped")); + len = iInPtr - iOutPtr; + } + + LOGTEXT2(_L8("<Des(); + iBufStart = const_cast(reinterpret_cast(ptr.Ptr())); + iBufSize = aSize; + CheckBufferEmptyAndResetPtrs(); + + return KErrNone; + } + +void CAcmReader::SetTerminators(const TCommConfigV01& aConfig) +/** + * API to change the terminator characters. + * + * @param aConfig The new configuration. + */ + { + LOG_FUNC + + // C32 protects the port against having config set while there's a request + // outstanding. + __ASSERT_DEBUG(!iCurrentRequest.iClientPtr, + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + + iTerminatorCount = aConfig.iTerminatorCount; + LOGTEXT2(_L8("\tnow %d terminators:"), iTerminatorCount); + for ( TUint ii = 0; ii < static_cast(KConfigMaxTerminators) ; ii++ ) + { + iTerminator[ii] = aConfig.iTerminator[ii]; + LOGTEXT2(_L8("\t\t%d"), iTerminator[ii]); + } + } + +void CAcmReader::ReadCompleted(TInt aError) +/** + * Called by lower classes when an LDD Read completes. + * + * @param aError Error. + */ + { + LOGTEXT2(_L8(">>CAcmReader::ReadCompleted aError=%d"), aError); + + const TUint justRead = static_cast(iInBuf.Length()); + LOGTEXT3(_L8("\tiInBuf length=%d, iLengthToGo=%d"), + justRead, + iLengthToGo); + + // This protects against a regression in the LDD- read requests shouldn't + // ever complete with zero bytes and KErrNone. + if ( justRead == 0 && aError == KErrNone ) + { + _USB_PANIC(KAcmPanicCat, EPanicInternalError); + } + + // The new data will have been added to our buffer. Move iInPtr up by the + // length just read. + iInPtr += justRead; + + if ( aError ) + { + // If the read failed, we complete back to the client and don't do + // anything more. (We don't want to get into retry strategies.) In a + // multi-stage Read the client will get any data already written back + // to them with IPCWrite. + CompleteRequest(aError); + return; + } + + // calling this will write the data to the clients address space + // it will also complete the request if we've given the client enough data + // or will reissue another read if not + ReadWithoutTerminators(); + + LOGTEXT(_L8("<>CAcmReader::ReadOneOrMoreCompleted aError=%d"), aError); + + const TUint justRead = static_cast(iInBuf.Length()); + LOGTEXT2(_L8("\tjustRead = %d"), justRead); + + // The new data will have been added to our buffer. Move iInPtr + // up by the length just read. + iInPtr += justRead; + + if ( aError ) + { + // If the ReadOneOrMore failed, we complete back to the client and + // don't do anything more. The client will get any data already + // written back to them with IPCWrite. + CompleteRequest(aError); + return; + } + + // TODO: may the LDD complete ROOM with zero bytes, eg if a ZLP comes in? + // NB The LDD is at liberty to complete a ReadPacket request with zero + // bytes and KErrNone, if the given packet was zero-length (a ZLP). In + // this case, we have to reissue the packet read until we do get some + // data. + if ( justRead == 0 ) + { + LOGTEXT(_L8("\twe appear to have a ZLP- reissuing ReadOneOrMore")); + IssueReadOneOrMore(); + return; + } + + if ( EReadOneOrMore == iCurrentRequest.iRequestType ) + { + // Complete the client's request with as much data as we can. NB + // Opinion may be divided over whether to do this, or complete with + // just 1 byte. We implement the more generous approach. + LOGTEXT2(_L8("\tcurrent request is ReadOneOrMore- completing with " + "%d bytes"), justRead); + WriteBackData(justRead); + CompleteRequest(KErrNone); + } + else + { + // Outstanding request is a Read with terminators. (Except for + // ReadOneOrMore, we only request LDD::ReadOneOrMore in this case.) + + // Process the buffer for terminators. + LOGTEXT(_L8("\tcurrent request is Read with terminators")); + CheckForBufferedTerminatorsAndProceed(); + } + + LOGTEXT(_L8("<>CAcmReader::NotifyDataAvailableCompleted aError=%d"), aError); + + // If the NotifyDataAvailable request failed, we complete back + // to the client and don't do anything more. + CompleteRequest(aError); + + LOGTEXT(_L8("< 0, _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + __ASSERT_DEBUG(aMaxLen <= static_cast(KMaxTInt), + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + + // Check we have no outstanding request already. + if ( iCurrentRequest.iClientPtr )// just panic in case of concurrent Read or ReadOneOrMore queries. + { // in case of NotifyDataAvailable queries, we already have completed the client with KErrInUse. + // This code is kept for legacy purpose. That justifies the existence of IsNotifyDataAvailableQueryPending + _USB_PANIC(KAcmPanicCat, EPanicInternalError); + } + // Sanity check on what C32 gave us. + __ASSERT_DEBUG(aClientBuffer, + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + + // Make a note of interesting data. + iCurrentRequest.iClientPtr = aClientBuffer; + iLengthToGo = aMaxLen; + iOffsetIntoClientsMemory = 0; + } + +void CAcmReader::CheckForBufferedTerminatorsAndProceed() +/** + * Checks for terminator characters in the buffer. Completes the client's + * request if possible, and issues further ReadOneOrMores for the appropriate + * amount if not. + */ + { + LOG_FUNC + + TInt ret = FindTerminator(); + LOGTEXT2(_L8("\tFindTerminator = %d"), ret); + if ( ret < KErrNone ) + { + LOGTEXT(_L8("\tno terminator found")); + const TUint bufLen = BufLen(); + LOGTEXT2(_L8("\tbufLen = %d"), bufLen); + // No terminator was found. Does the buffer already exceed the + // client's descriptor? + if ( bufLen >= iLengthToGo ) + { + // Yes- complete as much data to the client as their + // descriptor can handle. + LOGTEXT2(_L8("\tbuffer >= client descriptor- " + "writing back %d bytes"), iLengthToGo); + WriteBackData(iLengthToGo); + CompleteRequest(KErrNone); + } + else + { + // No- write back the data we've got and issue a request to get + // some more data. + WriteBackData(bufLen); + IssueReadOneOrMore(); + } + } + else + { + LOGTEXT(_L8("\tterminator found!")); + // Will the terminator position fit within the client's descriptor? + if ( static_cast(ret) <= iLengthToGo ) + { + // Yes- complete (up to the terminator) back to the client. + LOGTEXT2(_L8("\tterminator will fit in client's descriptor- " + "writing back %d bytes"), ret); + WriteBackData(static_cast(ret)); + CompleteRequest(KErrNone); + } + else + { + // No- complete as much data to the client as their descriptor can + // handle. + LOGTEXT2(_L8("\tterminator won't fit in client's descriptor- " + "writing back %d bytes"), iLengthToGo); + WriteBackData(iLengthToGo); + CompleteRequest(KErrNone); + } + } + } + +void CAcmReader::WriteBackData(TUint aLength) +/** + * This function writes back data to the client address space. The write will + * be performed in one go if the data to be written back is not wrapped across + * the end of the circular buffer. If the data is wrapped, the write is done + * in two stages. The read pointer is updated to reflect the data consumed, as + * is the counter for remaining data required by the client (iLengthToGo), and + * the pointer into the client's memory (iOffsetIntoClientsMemory). + * + * @param aLength Amount of data to write back. + */ + { + LOGTEXT2(_L8("CAcmReader::WriteBackData aLength = %d"), aLength); + LOGTEXT2(_L8("\tBufLen() = %d"), BufLen()); + + LOGTEXT2(_L8("\tBufLen() = %d"), BufLen()); + + __ASSERT_DEBUG(aLength <= BufLen(), + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + + const TUint lenBeforeWrap = iBufStart + iBufSize - iOutPtr; + + LOGTEXT2(_L8("\tiOutPtr=%d"), iOutPtr - iBufStart); + LOGTEXT2(_L8("\tiInPtr=%d"), iInPtr - iBufStart); + LOGTEXT2(_L8("\tiOffsetIntoClientsMemory=%d"), iOffsetIntoClientsMemory); + LOGTEXT2(_L8("\tlenBeforeWrap=%d"), lenBeforeWrap); + + if ( aLength > lenBeforeWrap ) + { + // We'll have to do this in two stages... + LOGTEXT(_L8("\twriting back in two stages")); + + __ASSERT_DEBUG(BufWrap(), + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + + // Stage 1... + TPtrC8 ptrBeforeWrap(iOutPtr, lenBeforeWrap); + TInt err = iPort.IPCWrite(iCurrentRequest.iClientPtr, + ptrBeforeWrap, + iOffsetIntoClientsMemory); + LOGTEXT2(_L8("\tIPCWrite = %d"), err); + __ASSERT_DEBUG(!err, _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + static_cast(err); + iOffsetIntoClientsMemory += lenBeforeWrap; + + // Stage 2... + TInt seg2Len = aLength - lenBeforeWrap; + TPtrC8 ptrAfterWrap(iBufStart, seg2Len); + err = iPort.IPCWrite(iCurrentRequest.iClientPtr, + ptrAfterWrap, + iOffsetIntoClientsMemory); + LOGTEXT2(_L8("\tIPCWrite = %d"), err); + __ASSERT_DEBUG(!err, _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + iOffsetIntoClientsMemory += seg2Len; + + // and set the pointers to show that we've consumed the data... + iOutPtr = iBufStart + seg2Len; + LOGTEXT(_L8("\twrite in two segments completed")); + } + else // We can do it in one go... + { + LOGTEXT(_L8("\twriting in one segment")); + + TPtrC8 ptr(iOutPtr, aLength); + TInt err = iPort.IPCWrite(iCurrentRequest.iClientPtr, + ptr, + iOffsetIntoClientsMemory); + LOGTEXT2(_L8("\tIPCWrite = %d"), err); + __ASSERT_DEBUG(!err, _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + static_cast(err); + iOffsetIntoClientsMemory += aLength; + + // Set the pointers to show that we've consumed the data... + iOutPtr += aLength; + LOGTEXT(_L8("\twrite in one segment completed")); + } + + LOGTEXT2(_L8("\tiOutPtr=%d"), iOutPtr - iBufStart); + LOGTEXT2(_L8("\tiOffsetIntoClientsMemory=%d"), iOffsetIntoClientsMemory); + + // Adjust iLengthToGo + __ASSERT_DEBUG(iLengthToGo >= aLength, + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + iLengthToGo -= aLength; + } + +void CAcmReader::CompleteRequest(TInt aError) +/** + * Utility to reset our 'outstanding request' flag and complete the client's + * request back to them. Does not actually write back data to the client's + * address space. + * + * @param aError The error code to complete with. + */ + { + LOGTEXT2(_L8("CAcmReader::CompleteRequest aError=%d"), aError); + + // Set our flag to say that we no longer have an outstanding request. + iCurrentRequest.iClientPtr = NULL; + + if(ENotifyDataAvailable==iCurrentRequest.iRequestType) + { + LOGTEXT2(_L8("\tcalling NotifyDataAvailableCompleted with error %d"), aError); + iPort.NotifyDataAvailableCompleted(aError); + } + else // read and readoneormore + { + LOGTEXT2(_L8("\tcalling ReadCompleted with error %d"), aError); + iPort.ReadCompleted(aError); + } + } + +void CAcmReader::IssueRead() +/** + * Issues a read request for N bytes, where N is the minimum of iLengthToGo, + * the LDD's limit on Read requests, and how far we are from the end of our + * buffer. Used when trying to satisfy an RComm Read without terminators. + * We enforce that the buffer is empty, so we don't have to worry about the + * buffer being wrapped and the consequent risk of overwriting. + */ + { + LOG_FUNC + + CheckBufferEmptyAndResetPtrs(); + + LOGTEXT2(_L8("\tiBufSize = %d"), iBufSize); + LOGTEXT2(_L8("\tiInPtr = %d"), iInPtr - iBufStart); + + const TUint lenBeforeWrap = iBufStart + iBufSize - iInPtr; + + LOGTEXT2(_L8("\tiLengthToGo = %d"), iLengthToGo); + LOGTEXT2(_L8("\tlenBeforeWrap = %d"), lenBeforeWrap); + + const TUint limit = Min(static_cast(iLengthToGo), + static_cast(lenBeforeWrap)); + LOGTEXT2(_L8("\tissuing read for %d bytes"), limit); + iInBuf.Set(iInPtr, 0, limit); + __ASSERT_DEBUG(iPort.Acm(), + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + iPort.Acm()->Read(*this, iInBuf, limit); + } + +void CAcmReader::IssueReadOneOrMore() +/** + * Issues a read request for N bytes, where N is the minimum of iLengthToGo, + * and how far we are from the end of our buffer. Used when trying to satisfy + * an RComm ReadOneOrMore. + * We enforce that the buffer is empty, so we don't have to worry about the + * buffer being wrapped and the consequent risk of overwriting. + */ + { + LOG_FUNC + + CheckBufferEmptyAndResetPtrs(); + + LOGTEXT2(_L8("\tiBufSize = %d"), iBufSize); + LOGTEXT2(_L8("\tiInPtr = %d"), iInPtr - iBufStart); + + const TUint lenBeforeWrap = iBufStart + iBufSize - iInPtr; + + LOGTEXT2(_L8("\tiLengthToGo = %d"), iLengthToGo); + LOGTEXT2(_L8("\tlenBeforeWrap = %d"), lenBeforeWrap); + + const TUint limit1 = Min(static_cast(lenBeforeWrap), iLengthToGo); + + LOGTEXT2(_L8("\tissuing read one or more for %d bytes"), limit1); + iInBuf.Set(iInPtr, 0, limit1); + __ASSERT_DEBUG(iPort.Acm(), + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + iPort.Acm()->ReadOneOrMore(*this, iInBuf, limit1); + } + +TInt CAcmReader::FindTerminator() const +/** + * This function searches the circular buffer for one of a number of + * termination characters. + * The search is conducted between the read and write pointers. The function + * copes with the wrapping of the buffer. + * + * @return If positive: the number of bytes between where iOutPtr is pointing + * at and where the terminator was found inclusive. Takes wrapping into + * account. If negative: error. + */ + { + LOGTEXT(_L8(">>CAcmReader::FindTerminator")); + + TInt pos = 0; + TInt ret = KErrNone; + if ( !BufWrap() ) + { + ret = PartialFindTerminator(iOutPtr, iInPtr, pos); + if ( !ret ) + { + // Buffer wasn't wrapped, terminator found. + ret = pos; + } + } + else + { + ret = PartialFindTerminator(iOutPtr, iBufStart+iBufSize, pos); + if ( !ret ) + { + // Buffer was wrapped, but terminator was found in the section + // before the wrap. + ret = pos; + } + else + { + ret = PartialFindTerminator(iBufStart, iInPtr, pos); + if ( !ret ) + { + // Buffer was wrapped, terminator was found in the wrapped + // section. + const TUint lenBeforeWrap = iBufStart + iBufSize - iOutPtr; + ret = pos + lenBeforeWrap; + } + } + } + + // Check we're returning what we said we would. + __ASSERT_DEBUG(ret != KErrNone, + _USB_PANIC(KAcmPanicCat, EPanicInternalError)); + + LOGTEXT2(_L8("<