os/kernelhwsrv/bsptemplate/asspandvariant/template_assp/iic/iic_slave.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /*
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     4 * This component and the accompanying materials are made available
     5 * under the terms of the License "Eclipse Public License v1.0"
     6 * which accompanies this distribution, and is available
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
     8 *
     9 * Initial Contributors:
    10 * Nokia Corporation - initial contribution.
    11 *
    12 * Contributors:
    13 *
    14 * Description:
    15 *
    16 */
    17 
    18 
    19 #include <drivers/iic.h>
    20 #include <drivers/iic_channel.h>
    21 // #include <gpio.h>	// Include if using GPIO functionality
    22 #include "iic_psl.h"
    23 #include "iic_slave.h"
    24 
    25 // The timeout period to wait for a response from the client, expressed in milliseconds
    26 // This is converted to timer ticks by the PIL, so the maximum value is 2147483.
    27 // The value should be selected to allow for the longest, slowest transfer
    28 // const TInt KClientWaitTime = 2; // 2mS, when debugging might set up to KMaxWaitTime
    29 
    30 
    31 // In an SMP system, use a spin lock to guard access to member variables iTrigger and iInProgress
    32 #ifdef __SMP__
    33 static TSpinLock IicPslSpinLock = TSpinLock(TSpinLock::EOrderGenericIrqLow3);
    34 #endif
    35 
    36 // Callback function for the iHwGuardTimer timer. 
    37 //
    38 // Called in ISR context if the iHwGuardTimer expires. Sets iTransactionStatus to KErrTimedOut
    39 //
    40 void DIicBusChannelSlavePsl::TimeoutCallback(TAny* aPtr)
    41 	{
    42 	__KTRACE_OPT(KIIC, Kern::Printf("DCsiChannelMaster::TimeoutCallback"));
    43 	DIicBusChannelSlavePsl *a = (DIicBusChannelSlavePsl*) aPtr;
    44 	a->iTransactionStatus = KErrTimedOut;
    45 	}
    46 
    47 
    48 // Static method called by the ISR when the Master has ended a transfer
    49 //
    50 // The method checks and reports the Rx and Tx status to the PIL by calling NotifyClient with a bitmask described as follows:.
    51 // - If a Tx transfer has ended before all the data was transmitted, bitmask = (ETxAllBytes | ETxOverrun)
    52 // - If a Tx transfer has ended and all the data was transmitted, bitmask = ETxAllBytes
    53 // - If a Rx transfer has ended before the expected amount of data was received, bitmask = (ERxAllBytes | ERxUnderrun)
    54 // - If a Rx transfer has ended and the expected amount of data was received, bitmask = ERxAllBytes
    55 //
    56 void DIicBusChannelSlavePsl::NotifyClientEnd(DIicBusChannelSlavePsl* aPtr)
    57 	{
    58 	__KTRACE_OPT(KIIC, Kern::Printf("NotifyClientEnd, iTrigger %x", aPtr->iTrigger));
    59 
    60 	// Since a transfer has ended, may wish to disable interrupts at this point
    61 	// This will likely be supported with calls similar to the following:
    62 	//		AsspRegister::Write32(aPtr->iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask);
    63 	//		Interrupt::Disable(aPtr->iRxInterruptId);
    64 	//		Interrupt::Disable(aPtr->iTxInterruptId);
    65 
    66 	// iTrigger will have bits ETransmit and EReceive set according to the operation requested in the call to DoRequest
    67 	// Use variable flag for the bitmask to pass into the PIL method NotifyClient
    68 	TInt flag = 0;
    69 	if(aPtr->iTrigger & EReceive)
    70 		{
    71 		// Requested Rx operation has ended - check for RxUnderrun
    72 		flag = ERxAllBytes;
    73 		if(aPtr->iRxDataEnd != aPtr->iRxData)
    74 			{
    75 			flag |= ERxUnderrun;
    76 			}
    77 		}
    78 	if(aPtr->iTrigger & ETransmit)
    79 		{
    80 		// Requested Tx operation has ended - check for RxOverrun
    81 		flag |= ETxAllBytes;
    82 		if(aPtr->iTxDataEnd != aPtr->iTxData)
    83 			{
    84 			flag |= ETxOverrun;
    85 			}
    86 		}
    87 	aPtr->NotifyClient(flag);
    88 	}
    89 
    90 
    91 // ISR Handler
    92 //
    93 // The ISR handler identifies the cause of the interrupt that lead to its invocation:
    94 // if the cause was transfer-related, it calls the PIL function NotifyClient to report a summary of the transfer status;
    95 // if the cause was completion of asynchronous channel capture, PIL function ChanCaptureCallback is called 
    96 //
    97 // The ISR also clears the source of the interrupt, and (for transfer-related interrupts) transfers the next data 
    98 // between buffers and the hardware and updates the member variable iInProgress to indicate if a transfer has started or
    99 // ended. If a transfer has ended before the expected amount of data has been transfered it calls function NotifyClientEnd.
   100 //
   101 void DIicBusChannelSlavePsl::IicPslIsr(TAny* /*aPtr*/)
   102 	{
   103 	//		DIicBusChannelSlavePsl *a = (DIicBusChannelSlavePsl*) aPtr;
   104 
   105 	//		TInt intState = 0;	// Variable to support use of spin lock
   106 
   107 	//		TInt trigger = 0; // Record the Rx and Tx transfers
   108 
   109 	//		TUint32 intStatus = 0; // Record of the interrupts that are being reported
   110 
   111 	// Identify the cause of the interrupt. If this can be achieved by reading a single register,
   112 	// code similar to the following could be used:
   113 	//		intStatus = AsspRegister::Read32(a->iChannelBase + KIntStatusOffset);
   114 
   115 	// Optional (not required if asynchronous channel capture is not supported)
   116 	// If the cause of the interrupt is completion of asynchronous channel capture, the ISR will check the appropriate
   117 	// indicator for confirmation of success - for a real PSL, this may be by querying a bitmask in a register. For the template PSL,
   118 	// however, a dummy member variable (iAsyncConfig) has been used to represent the asynchronous operation instead.
   119 	//
   120 	//		if(iAsyncConfig == 1)	// Replace with a check of the indicator that the interrupt was due to asynchrous channel capture
   121 	//			{
   122 	//			// The PIL function ChanCaptureCallback is now to be invoked. It takes as an argument either KErrNone or a
   123 	//			// system-wide error code to indicate the cause of failure. For a real PSL, the argument would likely be determined
   124 	//			// by reading a bitmask in a status register - but for the template port, just use KErrNone.
   125 	//			//
   126 	//			a->ChanCaptureCallback(KErrNone);
   127 	//			return;
   128 	//			}
   129 
   130 	// If an interrupt indicates that a transfer has started, or that it has now ended, (such as a chip select 
   131 	// line transition for a SPI bus the member variable iInProgress should be modified accordingly. This should
   132 	// be done under the guard of spin lock macros since iInProgress can be accessed in the context of the Client
   133 	// thread (in DoRequest, ProcessData and InitTransfer). The following structure should be adopted:
   134 	//		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   135 	//		<access a->iInProgress>
   136 	//		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   137 	//
   138 	// If a transfer has ended before the expected amount of data has been transfered, function NotifyClientEnd
   139 	// should be called, as follows:
   140 	//		a->NotifyClientEnd(a);
   141 	//		return;	// Return now - the interrupt indicated transfer end, not receipt or transmission of data.
   142 
   143 	// The transfers that had been started are indicated by the bitmask held in member variable iTrigger.
   144 	// This must be accessed under the guard of a spin lock since it can be accessed in the context of the
   145 	// Client thread (in DoRequest, ProcessData and InitTransfer). The following structure should be adopted:
   146 	//		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   147 	//		trigger = a->iTrigger;
   148 	//		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   149 
   150 	// If the interrupt was raised for a Tx event, and a Tx transfer had been started (so the interrupt was not spurious)
   151 	// then either prepare the next data to send, or, if all the data has been sent, call the PIL function NotifyClient
   152 	// with bitmask (ETxAllBytes | ETxUnderrun) so that, if the Client specified a ETxUnderrun notification, it will be alerted
   153 	// and can determine whether another buffer of data should be provide for transmission.
   154 	// Code similar to the following could be used:
   155 	//		if(intStatus & KTxInterruptBitMask)
   156 	//			{
   157 	//			if(trigger & ETransmit)
   158 	//				{
   159 	//				// Interrupt was not spurious
   160 	//				if(a->iTxData == a->iTxDataEnd)
   161 	//					{
   162 	//					// All the data to be transmitted has been sent, so call the PIL method NotifyClient
   163 	//					a->NotifyClient(ETxAllBytes | ETxUnderrun);
   164 	//					}
   165 	//				else
   166 	//					{
   167 	//					// There is more data to be sent
   168 	//					// TUint8 nextTxValue = *iTxData;	// For this example, assumes one byte of data is to be transmitted
   169 	//														// but if operating in 16-bit mode, bytes may need arranging for
   170 	//														// endianness
   171 	//
   172 	//					// Write to the Tx register with something similar to the following:
   173 	//					//		AsspRegister::Write32(iChannelBase + KTxFifoOffset, nextTxValue);
   174 	//
   175 	//					iTxData += iWordSize;	// Then increment the pointer to the data. In this example, 8-bit mode is assumed
   176 	//											// (iWordSize=1), but if operating in 16-bit mode iTxData would be incremented
   177 	//											// by the number of bytes specified in iWordSize
   178 	//					}
   179 	//				}
   180 	//			}
   181 
   182 	// If the interrupt was raised for a Rx event, and a Rx transfer had been started (so the interrupt was not spurious)
   183 	// read the received data from the hardware to the buffer. If a Rx FIFO is being used, use a loop to drain it - until
   184 	// the FIFO is empty or the buffer is full. If data remains after the buffer is full, an RxOverrun condition has occurred
   185 	// - so the PIL function NotifyClient should be called with bitmask (ERxAllBytes | ERxOverrun) so that, if the Client specified
   186 	// a ERxOverrun notification, it will be alerted and can determine whether another buffer should be provided to continue reception.
   187 	// Code similar to the following could be used:
   188 	//		if(intStatus & KRxInterruptBitMask)
   189 	//			{
   190 	//			if(trigger & EReceive)
   191 	//				{
   192 	//				// Interrupt was not spurious
   193 	//				while(AsspRegister::Read32(a->iChannelBase + KRxFifoLevelOffset))
   194 	//					{
   195 	//					if((a->iRxData - a->iRxDataEnd) >= a->iWordSize)
   196 	//						{
   197 	//						// Space remains in the buffer, so copy the received data to it
   198 	//						TUint8 nextRxValue = AsspRegister::Read32(a->iChannelBase + KRxFifoOffset);
   199 	//						*a->iRxData = nextRxValue;	// For this example, assumes one byte of data is to be transmitted
   200 	//													// but if operating in 16-bit mode, bytes may need arranging for
   201 	//													// endianness
   202 	//
   203 	//						a->iRxData += a->iWordSize;	// Then increment the pointer to the data. In this example, 8-bit mode is assumed
   204 	//													// (iWordSize=1), but if operating in 16-bit mode iRxData would be incremented
   205 	//													// by the number of bytes specified in iWordSize
   206 	//						}
   207 	//					else
   208 	//						{
   209 	//						// The buffer is full but more data has been received - so there is an RxOverrun condition
   210 	//						// Disable the hardware from receiving any more data and call the PIL function NotifyClient
   211 	//						// with bitmask (ERxAllBytes | ERxOverrun).
   212 	//						AsspRegister::Write32(a->iChannelBase + KRxFifoControl, KRxFifoDisableBitMask);
   213 	//						a->NotifyClient(ERxAllBytes | ERxOverrun);
   214 	//						break;
   215 	//						}
   216 	//					}
   217 	//				}
   218 	//			else
   219 	//				{
   220 	//				// If the interrupt was spurious, ignore the data, and reset the FIFO
   221 	//				AsspRegister::Write32(a->iChannelBase + KRxFifoControl, KRxFifoClearBitMask);
   222 	//				}
   223 
   224 	// Once the interrupts have been processed, clear the source. If this can be achieve by writing to
   225 	// a single register, code similar to the following could be used:
   226 	//		AsspRegister::Write32(a->iChannelBase + KIntStatusOffset, KAIntBitMask);
   227 
   228 	}
   229 
   230 
   231 // Constructor, first stage
   232 //
   233 // The PSL is responsible for setting the channel number - this is passed as the first parameter to
   234 // this overload of the base class constructor
   235 //
   236 DIicBusChannelSlavePsl::DIicBusChannelSlavePsl(TInt aChannelNumber, TBusType aBusType, TChannelDuplex aChanDuplex) :
   237 	DIicBusChannelSlave(aBusType, aChanDuplex, 0),	// Base class constructor. Initalise channel ID to zero.
   238 	iHwGuardTimer(TimeoutCallback, this)			// Timer to guard against hardware timeout
   239 	{
   240 	iChannelNumber = aChannelNumber;
   241 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::DIicBusChannelSlavePsl, iChannelNumber = %d\n", iChannelNumber));
   242 	}
   243 
   244 
   245 // Second stage construction
   246 //
   247 // Allocate and initialise objects required by the PSL channel implementation
   248 //
   249 TInt DIicBusChannelSlavePsl::DoCreate()
   250 	{
   251 	__KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelSlavePsl::DoCreate, ch: %d \n", iChannelNumber));
   252 
   253 	TInt r = KErrNone;
   254 
   255 	// PIL Base class initialization.
   256 	r = Init();
   257 	if(r == KErrNone)
   258 		{
   259 		// At a minimum, this function must set the channel's unique channel ID.
   260 		// When the channel is captured, this value will be combined with an instance count
   261 		// provided by the PIL to generate a value that will be used by a client as a unique
   262 		// identifer in subsequent calls to the Slave API.
   263 		//
   264 		// There is no set format for the ID, it just needs to be unique.
   265 		// Un-comment and complete the following line:
   266 //		iChannelId = 
   267 		
   268 		// This method may also be concerned with setting the base register address iChannelBase), and allocating
   269 		// any objects that will be required to support operaton until the channel is deleted.
   270 		//
   271 		// Un-comment and complete the following line:
   272 //		iChannelBase = 
   273 		}
   274 	return r;
   275 	}
   276 
   277 // static method used to construct the DIicBusChannelSlavePsl object.
   278 DIicBusChannelSlavePsl* DIicBusChannelSlavePsl::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex)
   279 	{
   280 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::NewL(): aChannelNumber = %d, BusType =%d", aChannelNumber, aBusType));
   281 	DIicBusChannelSlavePsl *pChan = new DIicBusChannelSlavePsl(aChannelNumber, aBusType, aChanDuplex);
   282 
   283 	TInt r = KErrNoMemory;
   284 	if (pChan)
   285 		{
   286 		r = pChan->DoCreate();
   287 		}
   288 	if (r != KErrNone)
   289 		{
   290 		delete pChan;
   291 		pChan = NULL;
   292 		}
   293 
   294 	return pChan;
   295 	}
   296 
   297 
   298 // Validates the configuration information specified by the client when capturing the channel
   299 //
   300 // Called by the PIL as part of the Slave CaptureChannel processing
   301 //
   302 // If the pointer to the header is NULL, return KErrArgument.
   303 // If the content of the header is not valid for this channel, return KErrNotSupported.
   304 // 
   305 TInt DIicBusChannelSlavePsl::CheckHdr(TDes8* aHdrBuff)
   306 	{
   307 	TInt r = KErrNone;
   308 
   309 	if(!aHdrBuff)
   310 		{
   311 		r = KErrArgument;
   312 		}
   313 	else
   314 		{
   315 		// Check that the contents of the header are valid
   316 		//
   317 		// The header will be specific to a particular bus type. Using a fictional
   318 		// bus type Abc,code similar to the following could be used to validate each
   319 		// member of the header:
   320 		// 
   321 		//		TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) aHdrBuff;
   322 		//		TConfigAbcV01 &abcHeader = (*headerBuf)();
   323 		//		if(	(abcHeader.iHeaderMember < ESomeMinValue)	||
   324 		//			(abcHeader.iHeaderMember > ESomeMaxValue))
   325 		//			{
   326 		//			__KTRACE_OPT(KIIC, Kern::Printf("iHeaderMember %d not supported",abcHeader.iHeaderMember));
   327 		//			r = KErrNotSupported;
   328 		//			}
   329 
   330 		}
   331 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::CheckHdr() r %d", r));
   332 
   333 	return r;
   334 	}
   335 
   336 
   337 // Method called in the context of the client thread, as a consequence of the PSL invocation of the 
   338 // PIL method NotifyClient when a bus event occurs.
   339 //
   340 // This method updates the bitmask of requested operations (held in member variable iTrigger) and the
   341 // PIL counts of data received and transmitted. If the event was a bus error, the bitmask of requested operations
   342 // is cleared.
   343 //
   344 void DIicBusChannelSlavePsl::ProcessData(TInt aTrigger, TIicBusSlaveCallback* aCb)
   345 	{
   346 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::ProcessData(), trigger: %x\n", aTrigger));
   347 
   348 	TInt intState;
   349 
   350 	// If using the iInProgress member variable to indicate transitions on a chip-select line, and an interrupt
   351 	// occurred as a transfer was to end, then must ensure the transmission of data has ceased.
   352 	//
   353 	// Must use spin lock to guard access since iInProgress is accessed by the ISR
   354 	//
   355 	TInt inProgress;
   356 	intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   357 	inProgress = iInProgress;
   358 	__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   359 	//
   360 	if(!inProgress &&								// Transfer has now ended
   361 	   (aTrigger & (ERxAllBytes | ETxAllBytes)))	// Master has not yet finished transferring data
   362 		{
   363 		// Use the guard timer to make sure that transfer ends with an expected time - if this does not cease 
   364 		// before the timer expires, iTransactionStatus will be set to KErrTimedOut by the callback function TimeoutCallback
   365 		//
   366 		// Poll the relevant register to check for transfer activity, using code similar to the following:
   367 		//		TInt8 transferring = AsspRegister::Read32(iChannelBase + KStatusRegisterOffset) & KTransferringBitMask);
   368 		// For the template port, use a dummy variable instead of the register access (transferring = 1)
   369 		//
   370 		TInt8 transferring = 1;
   371 		iTransactionStatus = KErrNone;
   372 		iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
   373 
   374 		while((iTransactionStatus == KErrNone) &&
   375 		      transferring);		// Replace transferring with a register read, as described above
   376 
   377 		// At this point, either the transfer has ceased, or the timer expired - in either case, may disable the interrupt
   378 		// for the transfer now, using code similar to the following:
   379 		//		AsspRegister::Write32(iChannelBase + KIntEnableRegisterOffset, KIntDisableBitMask);
   380 
   381 		// Check for guard timer expiry
   382 		if(iTransactionStatus != KErrNone)
   383 			{
   384 			__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::ProcessData - Transaction timed-out"));
   385 			return;
   386 			}
   387 		else
   388 			{
   389 			iHwGuardTimer.Cancel();
   390 			}
   391 
   392 		// If all transfer activity has now ceased, clear iTrigger
   393 		// Must use spin lock to guard access since iInProgress is accessed by the ISR
   394 		//
   395 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   396 		iTrigger = 0;
   397 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   398 		}
   399 
   400 	// If the PSL called the PIL function NotifyClient to indicate transfer activity (or error), the reason
   401 	// will be specified as a bitmask in aTrigger
   402 	//  - if a Rx event occurred, the ERxAllBytes flag will be set
   403 	//  - if a Tx event occurred, the ETxAllBytes flag will be set
   404 	//  - if a bus error occurred, the EGeneralBusError flag will be set
   405 	//
   406 	if(aTrigger & ERxAllBytes)
   407 		{
   408 		__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Rx Buf:    %x\n", iRxData));
   409 		__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Rx Bufend: %x\n", iRxDataEnd));
   410 
   411 		// Clear the internal EReceive flag
   412 		// This must be done under guard of a spin lock since iTrigger is accessed by the ISR
   413 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   414 		iTrigger &= ~EReceive;
   415 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   416 
   417 		// Update the PIL count of Rx data (in the Callback object)
   418 		aCb->SetRxWords(iNumRxWords - ((iRxDataEnd - iRxData) / iWordSize));
   419 		}
   420 	// 
   421 	if(aTrigger & ETxAllBytes)
   422 		{
   423 		__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Tx Buf:    %x\n", iTxData));
   424 		__KTRACE_OPT(KIIC, Kern::Printf("ProcessData - Tx Bufend: %x\n", iTxDataEnd));
   425 
   426 		// Clear the internal ETransmit flag..
   427 		// This must be done under guard of a spin lock since iTrigger is accessed by the ISR
   428 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   429 		iTrigger &= ~ETransmit;
   430 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   431 
   432 		// Update the PIL count of Tx data (in the Callback object)
   433 		aCb->SetTxWords(iNumTxWords - ((iTxDataEnd - iTxData) / iWordSize));
   434 		}
   435 	//
   436 	if(aTrigger & EGeneralBusError)
   437 		{
   438 		__KTRACE_OPT(KIIC, Kern::Printf("BusError.."));
   439 
   440 		// Clear and disable relevant interrupts, possibly using code similar to the following:
   441 		//		AsspRegister::Write32(iChannelBase + KIntEnableRegisterOffset, KIntDisableBitMask);
   442 
   443 		// Clear internal flags
   444 		// This must be done under guard of a spin lock since iTrigger is accessed by the ISR
   445 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   446 		iTrigger = 0;
   447 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   448 		}
   449 
   450 	// Set the callback's trigger, for use by the PIL
   451 	aCb->SetTrigger(aTrigger | aCb->GetTrigger());
   452 	}
   453 
   454 
   455 
   456 // Method to initialise the hardware in accordance with the data provided by the Client
   457 // in the configuration header when capturing the channel
   458 //
   459 // This method is called from DoRequest and is expected to return a value to indicate success
   460 // or a system wide error code to inform of the failure
   461 //
   462 TInt DIicBusChannelSlavePsl::ConfigureInterface()
   463 	{
   464 	__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()"));
   465 
   466 	TInt r = KErrNone;
   467 
   468 	// The header is stored in member variable iConfigHeader, and will be specific to a particular bus type.
   469 	// Using a fictional bus type Abc, code similar to the following could be used to access each
   470 	// member of the header:
   471 	// 
   472 	//		TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) iConfigHeader;
   473 	//		TConfigAbcV01 &abcHeader = (*headerBuf)();
   474 	//		TInt value = abcHeader.iTintMember;
   475 
   476 	// Initialising the hardware may be achieved with calls similar to the following:
   477 	//		AsspRegister::Write32(a->iChannelBase + KBusModeControlOffset, KIicPslModeControlBitMask);
   478 	//		GPIO::SetPinMode(aPinId, GPIO::EEnabled);
   479 
   480 	// Binding an ISR may be achieved with calls similar to the following:
   481 	//		r = Interrupt::Bind(iRxInterruptId, DIicBusChannelSlavePsl::IicPslIsr, this);
   482 	//		r = Interrupt::Bind(iTxInterruptId, DIicBusChannelSlavePsl::IicPslIsr, this);
   483 	// Enabling interrupts may be achieved with calls similar to the following:
   484 	//		r = Interrupt::Enable(iRxInterruptId);
   485 	//		r = Interrupt::Enable(iTxInterruptId);
   486 
   487 	// Modifying a hardware register may not be a zero-delay operation. The member variable iHwGuardTimer could be used to guard a
   488 	// continuous poll of the hardware register that checks for the required change in the setting; TimeoutCallback is already 
   489 	// assigned as the callback function for iHwGaurdTimer, and it modifies member variable iTransactionStatus to indicate a timeout
   490 	// - so the two could be used together as follows:
   491 	//		iTransactionStatus = KErrNone;
   492 	//		iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
   493 	//		while((iTransactionStatus == KErrNone) &&
   494 	//		       AsspRegister::Read32(iChannelBase + KRegisterOffset) & KRegisterFlagBitMask);
   495 	//		if(iTransactionStatus != KErrNone)
   496 	//			{
   497 	//			r = KErrGeneral;
   498 	//			}
   499 	//		else
   500 	//			{
   501 	//			iHwGuardTimer.Cancel();
   502 	//			}
   503 
   504 	// DoRequest checks the return value so the variable r should be modified in the event of failure with a system-wide error code
   505 	// for example, if a register could not be modified,
   506 	//			r = KErrGeneral;
   507 	//			__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface failed with error %d\n",r));
   508 	return r;
   509 	}
   510 
   511 
   512 // Method to start asynchronous initialisation of the hardware, in accordance with the data provided by the Client
   513 // in the configuration header when capturing the channel. This differs from ConfigureInterface in that it
   514 // merely starts the initialisation, then returns immediately; 
   515 //
   516 // The PSL is expected to be implemented as an asynchronous state machine, where events (for example hardware 
   517 // interrupts, or timer expiry) invoke callback functions that advance the state machine to the next state. Once
   518 // all the required states have been transitioned, so that the PSL part of the CaptureChannel processing is 
   519 // complete, the ISR should be invoked, which will then call PIL method ChanCaptureCallback
   520 //
   521 // This method is called from DoRequest and is expected to return a value to indicate success
   522 // or a system wide error code to inform of the failure
   523 //
   524 TInt DIicBusChannelSlavePsl::AsynchConfigureInterface()
   525 	{
   526 	__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()"));
   527 
   528 //	TInt r = KErrNone;	// A real implementation would use this as the return value to indicate success / failure
   529 
   530 	// Precisely what processing is done to 'start' the asynchronous processing is entirely platform-specific;
   531 	// it may be the set-up and activation of a long-running operation that completes asynchronously. Regardless of what
   532 	// is done, its completion is expected to result in the ISR being run.
   533 	//
   534 	// Whatever the operation, there must be some means of the ISR recognising that an asynchronous initialisation has
   535 	// been performed
   536 	// In a real PSL, this may be be checking a bitmask in a status register. For the template PSL, however,
   537 	// a dummy class member will be used (iAsyncConfig)
   538 	// Since this member will be accessed by the ISR, it should, strictly speaking, be accessed under the guard of a spin lock
   539 	TInt intState;
   540 	intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   541 	iAsyncConfig = 1;
   542 	__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   543 
   544 	return KErrNone;	// A real implementation would return an indication of success / failure
   545 	}
   546 
   547 // Method called from DoRequest to start Tx and-or Rx transfer.
   548 //
   549 // The method will initialise the hardware and pointers used to manage transfers, before returning a value to report success
   550 // (KErrNone) or a system-wide error code that indicates the cause of failure.
   551 //
   552 TInt DIicBusChannelSlavePsl::InitTransfer()
   553 	{
   554 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelSlavePsl::InitTransfer()"));
   555 
   556 	TInt r = KErrNone;
   557 
   558 	// Local copies of member variables that must be accessed in a synchronised manner
   559 	TInt inProgress;
   560 	TInt trigger;
   561 
   562 	TInt intState;
   563 
   564 	// Check if a transfer is already in progress. 
   565 	// If variable iInProgress is being used, this must be determined in a synchronised manner because the ISR modifies it.
   566 	// Bus types that do not rely on chip-select transitions may use an alternative method to indicate if a transfer is in
   567 	// progress
   568 	intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   569 	inProgress = iInProgress;
   570 	__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   571 
   572 	if(!inProgress)
   573 		{
   574 		// If no transfers are in progress, it may be necessary to initialise the hardware to support those that
   575 		// are being requested. This may include FIFO and interrupt initialisation, 
   576 		//
   577 		// Initialising the hardware may be achieved with calls similar to the following:
   578 		//		AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslModeControlBitMask);
   579 		//		GPIO::SetPinMode(aPinId, GPIO::EEnabled);
   580 		}
   581 
   582 	// Check the current operations. This must be determined in a synchronised manner because ProcessData
   583 	// runs in the context of the Client thread and it modifies the value of iTrigger
   584 	intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   585 	trigger = iTrigger;
   586 	__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   587 
   588 	if(trigger & ETransmit)
   589 		{
   590 		// If Tx transfers were not previously active, it may be necessary to initialise the Tx hardware here, e.g.
   591 		//		AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslTxModeBitMask);
   592 		
   593 		// Initialise the Tx pointers
   594 		iTxData = iTxBuf + (iWordSize * iTxOffset);
   595 		iTxDataEnd = iTxData + (iWordSize * iNumTxWords);
   596 
   597 		__KTRACE_OPT(KIIC, Kern::Printf("Tx Buf:    %x", iTxData));
   598 		__KTRACE_OPT(KIIC, Kern::Printf("Tx Bufend: %x", iTxDataEnd));
   599 
   600 		// If using a FIFO, copy the data to it until either the FIFO is full or all the data has been copied
   601 		// This could be achieved with something similar to the following lines:
   602 		//		while(AsspRegister::Read32(iChannelBase + KFifoLevelOffset) <= (KFifoMaxLevel - iWordSize) &&
   603 		//		      iTxData != iTxDataEnd)
   604 		// For the template port, will just use a dummy variable (dummyFifoLvlChk )in place of the register read
   605 		TInt dummyFifoLvlChk = 0;
   606 		while((dummyFifoLvlChk)	&&	// Replace this dummy variable with a read of the hardware
   607 			(iTxData != iTxDataEnd))
   608 			{
   609 			// TUint8 nextTxValue = *iTxData;	// For this example, assumes one byte of data is to be transmitted
   610 												// but if operating in 16-bit mode, bytes may need arranging for
   611 												// endianness
   612 
   613 			// Write to the Tx register with something similar to the following:
   614 			//		AsspRegister::Write32(iChannelBase + KTxFifoOffset, nextTxValue);
   615 
   616 			iTxData += iWordSize;	// Then increment the pointer to the data. In this example, 8-bit mode is assumed
   617 									// (iWordSize=1), but if operating in 16-bit mode iTxData would be incremented
   618 									// by the number of bytes specified in iWordSize
   619 			}
   620 		// If a Tx FIFO is not being used, a single Tx value would be written - in which case the above loop would be replaced
   621 	
   622 		__KTRACE_OPT(KIIC, Kern::Printf("After adding:\n\rTx Buf:    %x", iTxData));
   623 		__KTRACE_OPT(KIIC, Kern::Printf("Tx Bufend: %x", iTxDataEnd));
   624 		}
   625 
   626 	if(trigger & EReceive) 
   627 		{
   628 		// Initialise the Rx pointers
   629 		iRxData = iRxBuf + (iWordSize * iRxOffset);
   630 		iRxDataEnd = iRxData + (iWordSize * iNumRxWords);
   631 
   632 		__KTRACE_OPT(KIIC, Kern::Printf("Rx Buffer:  %x", iRxData));
   633 		__KTRACE_OPT(KIIC, Kern::Printf("Rx Bufend: %x", iRxDataEnd));
   634 
   635 		// If Rx transfers were not previously active, it may be necessary to initialise the Rx hardware here, e.g.
   636 		//		AsspRegister::Write32(iChannelBase + KBusModeControlOffset, KIicPslRxModeBitMask);
   637 		}
   638 
   639 	// If there is some common configuration required to support Rx, Tx transfers, may do it here
   640 
   641 	return r;
   642 	}
   643 
   644 
   645 // The gateway function for PSL implementation
   646 //
   647 // This method is called by the PIL to perform one or more operations indicated in the bitmask aOperation,
   648 // which corresponds to members of the TPslOperation enumeration.
   649 //
   650 TInt DIicBusChannelSlavePsl::DoRequest(TInt aOperation)
   651 	{
   652 	__KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelSlavePsl::DoRequest, Operation 0x%x\n", aOperation));
   653 
   654 	TInt r = KErrNone;
   655 	TInt intState;
   656 
   657 	if (aOperation & EAsyncConfigPwrUp)
   658 		{
   659 		// The PIL has requested asynchronous operation of CaptureChannel.
   660 		// The PSL should start the processing required for a channel to be captured, and then return immediately with
   661 		// error code KErrNone (if the processing was started without error), so that the client thread will be unblocked. 
   662 		// The PSL is expected to be implemented as an asynchronous state machine, where events (for example hardware 
   663 		// interrupts, or timer expiry) invoke callback functions that advance the state machine to the next state. Once
   664 		// all the required states have been transitioned, so that the PSL part of the CaptureChannel processing is 
   665 		// complete, the PSL should call the PIL function ChanCaptureCallback - this will lead to the Client-provided
   666 		// callback being executed in the context of the client thread
   667 		// 
   668 		__KTRACE_OPT(KIIC, Kern::Printf("EAsyncConfigPwrUp"));
   669 		r = AsynchConfigureInterface();
   670 		if (r != KErrNone)
   671 			{
   672 			__KTRACE_OPT(KIIC, Kern::Printf("AsynchConfigureInterface returned %d\n", r));
   673 			}
   674 		return r;
   675 		}
   676 
   677 	if (aOperation & ESyncConfigPwrUp)
   678 		{
   679 		// The PIL has requested synchronous operation of CaptureChannel.
   680 		// The PSL should perform the processing required for a channel to be captured, and return a system-wide error
   681 		// code when this is complete to indicate the status of the capture.
   682 		// Capturing a channel is expected to include initialisation of the hardware to enable operation in accordance
   683 		// with the configuration specified in the PIL member variable iConfigHeader, which holds the configuration
   684 		// specified by the Client.
   685 		//
   686 		__KTRACE_OPT(KIIC, Kern::Printf("ESyncConfigPwrUp"));
   687 		r = ConfigureInterface();
   688 		if (r != KErrNone)
   689 			{
   690 			__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface returned %d\n", r));
   691 			return r;
   692 			}
   693 		}
   694 
   695 	if (aOperation & ETransmit)
   696 		{
   697 		// The PIL has requested that a Tx operation be started. 
   698 		// Since the SPL may support simultaneous Rx and Tx operations, just set the flag in the iTrigger bitmask to 
   699 		// indicate what has been requested. If both Rx and Tx operations are requested, and one completes ahead of the other,
   700 		// requiring the Client to provide a new buffer and associated call to DoRequest (as is the case if the Master wishes
   701 		// to transfer more data than the Slave buffer supported), it is possible that the other transfer could complete while
   702 		// this function is running; consequently, it may attempt to access iTrigger, and so cause data corruption. To cater for
   703 		// such situations, use a spin lock to guard access to iTrigger.
   704 		// When the same check has been performed for Rx, call the InitTransfer function to start the required transfers.
   705 		//
   706 		__KTRACE_OPT(KIIC, Kern::Printf("ETransmit"));
   707 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   708 		iTrigger |= ETransmit;
   709 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   710 		}
   711 
   712 	if (aOperation & EReceive)
   713 		{
   714 		// The PIL has requested that a Rx operation be started. 
   715 		// Since the SPL may support simultaneous Rx and Tx operations, just set the flag in the iTrigger bitmask to 
   716 		// indicate what has been requested. If both Rx and Tx operations are requested, and one completes ahead of the other,
   717 		// requiring the Client to provide a new buffer and associated call to DoRequest (as is the case if the Master wishes
   718 		// to transfer more data than the Slave buffer supported), it is possible that the other transfer could complete while
   719 		// this function is running; consequently, it may attempt to access iTrigger, and so cause data corruption. To cater for
   720 		// such situations, use a spin lock to guard access to iTrigger.
   721 		// When the same check has been performed for Tx, call the InitTransfer function to start the required transfers.
   722 		//
   723 		__KTRACE_OPT(KIIC, Kern::Printf("EReceive"));
   724 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   725 		iTrigger |= EReceive;
   726 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   727 		}
   728 
   729 	if (aOperation & (EReceive | ETransmit))
   730 		{
   731 		// This code should only be executed once it has been checked whether Rx and Tx operations are required.
   732 		r = InitTransfer();
   733 		}
   734 
   735 	if (aOperation & EAbort)
   736 		{
   737 		// The PIL has requested that the current transaction be aborted.
   738 		// This is the case if the Client has not responded within an expected time to specify the next steps in
   739 		// the transaction processing. The time allowed is specified by calling PIL function SetClientWaitTime, otherwise 
   740 		// the time defaults to KSlaveDefCWaitTime.
   741 		// If the PSL is able to satisfy this request it should, at a minimum, disable interrupts and update the member 
   742 		// variables that indicate a transaction is in progress. If the PSL is unable to satisfy the request then the same
   743 		// behaviour will follow as if this request had not been made, so there is no point in modifying the state variables.
   744 		// If both Rx and Tx operations had been requested, and one completes ahead of the other, it is possible that the other 
   745 		// transfer could complete while this function is running; consequently, it may attempt to access iTrigger and iInProgress,
   746 		// and so cause data corruption. To cater for such situations, use a spin lock to guard access to iTrigger.
   747 		// The PIL makes no assumptions of whether the PSL can support this request or not, and does not check the return 
   748 		// value - so there is no need to set one.
   749 		//
   750 		TUint8 dummyCanAbort = 1;	// Dummy variable to represent a check of if it is possible to abort the current transaction
   751 		__KTRACE_OPT(KIIC, Kern::Printf("EAbort"));
   752 		intState = __SPIN_LOCK_IRQSAVE(IicPslSpinLock);
   753 		if(dummyCanAbort)
   754 			{
   755 			// The spin lock has been acquired, so it is safe to modify data and hardware registers that may be accessed as part of 
   756 			// interrupt processing performed by an ISR - this is assuming that the ISR has been written to acquire the same spin lock.
   757 			// Limit the processing to only that which is necessary to be processed under spin lock control, so as to not delay other
   758 			// threads of execution that are waiting for the spin lock to be freed.
   759 			// Hardware may be configured using code similar to the following:
   760 			//		AsspRegister::Write32(iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask);
   761 			iInProgress = EFalse;
   762 			iTrigger = 0;
   763 			}
   764 		__SPIN_UNLOCK_IRQRESTORE(IicPslSpinLock, intState);
   765 		// Having released the spin lock, now perform any actions that are not affected by pre-emption by an ISR, this may include code
   766 		// such as the following
   767 		//		Interrupt::Disable(iRxInterruptId);
   768 		//		Interrupt::Disable(iTxInterruptId);
   769 		}
   770 
   771 	if (aOperation & EPowerDown)
   772 		{
   773 		// The PIL has requested that the channel be released.
   774 		// If this channel is not part of a MasterSlave channel, the next Client will operate in Slave mode. In this case, it may only
   775 		// be necessary to disable interrupts, and reset the channel hardware.
   776 		// If this channel represents the Slave of a MasterSlave channel, it is possible that some of the hardware is shared between the
   777 		// Master and Slave sub-channels. Since it may not be known whether the next Client of the parent channel will require operation
   778 		// in either Master or Slave mode, some additional processing may be required to allow for subsequent Master operation (for example.
   779 		// unbinding an interrupt).
   780 		//
   781 		__KTRACE_OPT(KIIC, Kern::Printf("EPowerDown"));
   782 
   783 		// Resetting the hardware may be achieved with calls similar to the following:
   784 		//		AsspRegister::Write32(iChannelBase + KBusInterruptEnableOffset, KIicPslBusDisableBitMask);
   785 		//		GPIO::SetPinMode(aPinId, GPIO::EDisabled);
   786 
   787 		// Disable interrupts may be achieved with calls similar to the following:
   788 		//		Interrupt::Disable(iRxInterruptId);
   789 		//		Interrupt::Disable(iTxInterruptId);
   790 
   791 		// Unbinding an ISR may be achieved with calls similar to the following:
   792 		//		Interrupt::Unbind(iRxInterruptId);
   793 		//		Interrupt::Unbind(iTxInterruptId);
   794 
   795 		// The PIL checks the return value so the variable r should be modified in the event of failure with a system-wide error code
   796 		// for example, if a register could not be modified,
   797 		//		r = KErrGeneral;
   798 		//		__KTRACE_OPT(KIIC, Kern::Printf("EPowerDown failed with error %d\n",r));
   799 
   800 		}
   801 	return r;
   802 	}
   803