usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/AcmReader.cpp
changeset 0 c9bc50fca66e
child 15 f92a4f87e424
--- /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 <cs_port.h>
+#include "AcmReader.h"
+#include "AcmUtils.h"
+#include "ActiveReader.h"
+#include "ActiveReadOneOrMoreReader.h"
+#include "CdcAcmClass.h"
+#include "AcmPanic.h"
+#include "AcmPort.h"
+#include <usb/usblogger.h>
+
+#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("<<CAcmReader::BufLen len=%d"), len);
+	return len;
+	}
+
+void CAcmReader::ResetBuffer()
+/**
+ * Called by the port to clear the buffer.
+ */
+	{
+	LOG_FUNC
+
+	// A request is outstanding- C32 should protect against this.
+	__ASSERT_DEBUG(!iCurrentRequest.iClientPtr, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+
+	// Reset the pointers. All data is 'lost'.
+	iOutPtr = iInPtr = iBufStart;
+	}
+
+TInt CAcmReader::SetBufSize(TUint aSize)
+/**
+ * Called by the port to set the buffer size. Also used as a utility by us to 
+ * create the buffer at instantiation. Note that this causes what was in the 
+ * buffer at the time to be lost.
+ *
+ * @param aSize The required size of the buffer.
+ */
+	{
+	LOG_FUNC
+	LOGTEXT2(_L8("\taSize=%d"), aSize);
+
+	if ( iCurrentRequest.iClientPtr )
+		{
+		// A request is outstanding. C32 does not protect us against this.
+		LOGTEXT(_L8("\t***a request is outstanding- returning KErrInUse"));
+		return KErrInUse;
+		}
+
+	// Create the new buffer.
+	HBufC8* newBuf = HBufC8::New(aSize);
+	if ( !newBuf )
+		{
+		LOGTEXT(_L8("\tfailed to create new buffer- returning KErrNoMemory"));
+		return KErrNoMemory;
+		}
+	delete iBuffer;
+	iBuffer = newBuf;
+
+	// Update pointers etc.
+	TPtr8 ptr = iBuffer->Des();
+	iBufStart = const_cast<TUint8*>(reinterpret_cast<const TUint8*>(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<TUint>(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<TUint>(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::ReadCompleted"));
+	}
+
+void CAcmReader::ReadOneOrMoreCompleted(TInt aError)
+/**
+ * Called by lower classes when an LDD ReadOneOrMore completes. 
+ *
+ * @param aError Error.
+ */
+	{
+	LOGTEXT2(_L8(">>CAcmReader::ReadOneOrMoreCompleted aError=%d"), aError);						   	
+
+	const TUint justRead = static_cast<TUint>(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::ReadOneOrMoreCompleted"));
+	}
+
+void CAcmReader::NotifyDataAvailableCompleted(TInt aError)
+	{
+/**
+ * Called by lower classes when data has arrived at the LDD after a 
+ * NotifyDataAvailable request has been posted on the port. 
+ *
+ * @param aError Error.
+ */
+	LOGTEXT2(_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("<<CAcmReader::NotifyDataAvailableCompleted"));	
+	}
+
+void CAcmReader::CheckBufferEmptyAndResetPtrs()
+/**
+ * Utility to assert that the buffer is empty and to reset the read and write 
+ * pointers to the beginning of the buffer. We reset them there to cut down on 
+ * fiddling around wrapping at the end of the buffer.
+ */
+	{
+	LOGTEXT(_L8("CAcmReader::CheckBufferEmptyAndResetPtrs"));
+
+	if ( BufLen() != 0 )
+		{
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError);
+		}
+
+	iOutPtr = iInPtr = iBufStart;
+	}
+
+void CAcmReader::CheckNewRequest(const TAny* aClientBuffer, TUint aMaxLen)
+/**
+ * Utility function to check a Read or ReadOneOrMore request from the port. 
+ * Also checks that there isn't a request already outstanding. Makes a note of 
+ * the relevant parameters and sets up internal counters.
+ *
+ * @param aClientBuffer Pointer to the client's memory space.
+ * @param aMaxLen Maximum length to read.
+ */
+	{
+	// The port should handle zero-length reads, not us.
+	__ASSERT_DEBUG(aMaxLen > 0, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	__ASSERT_DEBUG(aMaxLen <= static_cast<TUint>(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<TUint>(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<TUint>(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<void>(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<void>(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<TInt>(iLengthToGo), 
+		static_cast<TInt>(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<TInt>(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("<<CAcmReader::FindTerminator ret=%d"), ret);
+	return ret;
+	}
+
+TInt CAcmReader::PartialFindTerminator(TUint8* aFrom, 
+									   TUint8* aTo, 
+									   TInt& aPos) const
+/**
+ * This function searches the buffer for one of a number of termination 
+ * characters. The search is conducted between the pointers given. The 
+ * function only searches a continuous buffer space, and does not respect the 
+ * circular buffer wrap.
+ *
+ * @param aFrom The pointer at which to start searching.
+ * @param aTo The pointer one beyond where to stop searching.
+ * @param aPos The number of bytes beyond (and including) aFrom the terminator 
+ * was found. That is, if the terminator was found adjacent to aFrom, aPos 
+ * will be 2- the client can take this as meaning that 2 bytes are to be 
+ * written back to the RComm client (aFrom and the one containing the 
+ * terminator).
+ * @return KErrNone- a terminator was found. KErrNotFound- no terminator was 
+ * found.
+ */
+	{
+	LOG_FUNC
+
+	aPos = 1;
+	LOGTEXT3(_L8("\taFrom=%d, aTo=%d"), aFrom-iBufStart, aTo-iBufStart);
+
+	for ( TUint8* p = aFrom ; p < aTo ; p++, aPos++ )
+		{
+		for ( TUint i = 0 ; i < iTerminatorCount ; i++ )
+			{
+			__ASSERT_DEBUG(p, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+			if ( *p == iTerminator[i] )
+				{
+				LOGTEXT3(_L8("\tterminator %d found at aPos %d"), 
+					iTerminator[i], aPos);
+				return KErrNone;
+				}
+			}
+		}
+	
+	LOGTEXT(_L8("\tno terminator found"));
+	return KErrNotFound;
+	}
+
+//
+// End of file