bsptemplate/asspandvariant/template_assp/iic/iic_master.cpp
changeset 43 96e5fb8b040d
equal deleted inserted replaced
-1:000000000000 43:96e5fb8b040d
       
     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 <gpio.h>	// Include if using GPIO functionality
       
    21 #include "iic_psl.h"
       
    22 #include "iic_master.h"
       
    23 
       
    24 
       
    25 // Method called when transmission activity is ended. The method is used to indicate
       
    26 // the success or failure reported in the first parameter
       
    27 //
       
    28 // All timers are cancelled, relevant interrupts are disabled and the transaction
       
    29 // request is completed by calling the relevant PIL method.
       
    30 //
       
    31 void DIicBusChannelMasterPsl::ExitComplete(TInt aErr, TBool aComplete /*= ETrue*/)
       
    32 	{
       
    33 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::ExitComplete, aErr %d, aComplete %d", aErr, aComplete));
       
    34 
       
    35 	// Disable interrupts for the channel
       
    36 	// with something similar to the following lines:
       
    37 	//		Interrupt::Disable(iRxInterruptId);
       
    38 	//		Interrupt::Disable(iTxInterruptId);
       
    39 
       
    40 	// Cancel timers and DFCs..
       
    41 	CancelTimeOut();
       
    42 	iHwGuardTimer.Cancel();
       
    43 	iTransferEndDfc.Cancel();
       
    44 
       
    45 	// Change the channel state to EIdle so that subsequent transaction requests can be accepted
       
    46 	// once the current one has been completed
       
    47 	iState = EIdle;
       
    48 
       
    49 	// Call the PIL method to complete the request
       
    50 	if(aComplete)
       
    51 		{
       
    52 		CompleteRequest(aErr);
       
    53 		}
       
    54 	}
       
    55 
       
    56 // Callback function for the iHwGuardTimer timer. 
       
    57 //
       
    58 // Called in ISR context if the iHwGuardTimer expires. Sets iTransactionStatus to KErrTimedOut
       
    59 //
       
    60 void DIicBusChannelMasterPsl::TimeoutCallback(TAny* aPtr)
       
    61 	{
       
    62 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TimeoutCallback"));
       
    63 	DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr;
       
    64 	a->iTransactionStatus = KErrTimedOut;
       
    65 	}
       
    66 
       
    67 // HandleSlaveTimeout
       
    68 //
       
    69 // This method is called by the PIL in the case of expiry of a timer started by the PSL. It is
       
    70 // specificaly intended to guard against the Slave not responding within an expected time
       
    71 //
       
    72 // The PIL method StartSlaveTimeoutTimer is available for the PSL to start the timer (this is 
       
    73 // called from ProcessNextTransfers, below).
       
    74 // The PIL method CancelTimeOut is available for the PSL to cancel the same timer (this is called
       
    75 // from ExitComplete, above)
       
    76 //
       
    77 // The PIL will call CompleteRequest() after this function returns, so the PSL needs only to clean-up
       
    78 //
       
    79 TInt DIicBusChannelMasterPsl::HandleSlaveTimeout()
       
    80 	{
       
    81 	__KTRACE_OPT(KIIC, Kern::Printf("HandleSlaveTimeout"));
       
    82 
       
    83 	// Ensure that the hardware has ceased transfers, with something similar to the following line:
       
    84 	//		AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusDisableBit);
       
    85 	//		GPIO::SetPinMode(aPinId, GPIO::EDisabled);
       
    86 
       
    87 	// Stop the PSL's operation, and inform the PIL of the timeout
       
    88 	ExitComplete(KErrTimedOut, EFalse);
       
    89 
       
    90 	// Perform any further hardware manipulation necessary
       
    91 	//
       
    92 
       
    93 	return KErrTimedOut;
       
    94 	}
       
    95 
       
    96 
       
    97 // DFC 
       
    98 //
       
    99 // For execution when a Rx buffer has been filled or a Tx buffer has been emptied
       
   100 //
       
   101 void DIicBusChannelMasterPsl::TransferEndDfc(TAny* aPtr)
       
   102 	{
       
   103 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TransferEndDfc"));
       
   104 	DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr;
       
   105 
       
   106 	//	Start of optional processing - not necessary for all implementations
       
   107 	//
       
   108 	// When operating full-duplex transfers, one of the Rx and Tx operations may have caused an interrupt
       
   109 	// before the other has completed. For the example here, the Tx is assumed to have completed before the Rx
       
   110 	// and a timer is used to ensure that the outstanding Rx operation completes within an expected time.
       
   111 	//
       
   112 	// If there has been no error so far, may want to check if we are still receiving the data
       
   113 	if(a->iTransactionStatus == KErrNone)
       
   114 		{
       
   115 		// Use an active wait since this is likely to be a brief check.
       
   116 		// Start the guard timer (which will update iTransactionStatus with KErrTimedOut if it expires)
       
   117 		// while also polling the hardware to check if transmission has ceased.
       
   118 		// 
       
   119 		// Polling the hardware would be something like the line below
       
   120 		//		(AsspRegister::Read32(a->aRegisterSetBaseAddress + KStatusRegisterOffset) & KTransmissionActive));
       
   121 		// but for the template port will use a dummy condition (value of 1)
       
   122 		//
       
   123 		a->iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
       
   124 		while((a->iTransactionStatus == KErrNone) && 1);	// Replace 1 with a register read
       
   125 		}
       
   126 	//
       
   127 	// Now the timer has expired, deactivate the slave select until the current state has been processed, but only
       
   128 	// if this is not an extended transaction, in which case we want to leave the bus alone so that the multiple
       
   129 	// transactions making up the extended transaction become one big transaction as far as the bus is concerned.
       
   130 	// Do this with something similar to the following line:
       
   131 	// if (!(a->iCurrTransaction->Flags() & KTransactionWithMultiTransc))
       
   132 	//	{
       
   133 	//	GPIO::SetPinMode(aPinId, GPIO::EDisabled);
       
   134 	//	}
       
   135 	//
       
   136 	// Disable the hardware from further transmissions
       
   137 	// AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusDisableBit);
       
   138 	//
       
   139 	// Check if the guard timer expired
       
   140 	if(a->iTransactionStatus != KErrNone)
       
   141 		{
       
   142 		__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TransferEndDfc(): error %d",a->iTransactionStatus));
       
   143 		a->ExitComplete(a->iTransactionStatus); // report the error
       
   144 		return;
       
   145 		}
       
   146 	else
       
   147 		{
       
   148 		// Transfer completed successfully, so just cancel the guard timer
       
   149 		a->iHwGuardTimer.Cancel();
       
   150 		}
       
   151 	//	End of optional processing - not necessary for all implementations
       
   152 
       
   153 	// At this point, prepare for subsequent transfers by performing any necessary clean-up.
       
   154 	// As stated above, for this example, it is assumed that any Rx or Tx transfer has completed - 
       
   155 	// the following just checks if an Rx, Tx operation was started, and assumes that they completed.
       
   156 
       
   157 	if(a->iOperation.iOp.iIsReceiving)
       
   158 		{
       
   159 		// If the channel has been receiving data, may need to ensure that any FIFO used has been drained
       
   160 		// The example here checks if one extra data item remains in the FIFO
       
   161 		// To check if data remains in a FIFO, something similar to the following could be used:
       
   162 		//		TInt8 dataRemains = AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxFifoLevel);
       
   163 		// Reading data from the FIFO would be achieved with something like the line below
       
   164 		//		value = AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxData);
       
   165 		// but for the template port will just use a dummy values (data remains=1, value = 1)
       
   166 		//
       
   167 		TInt8 dataRemains = 1;	// Would be a check of a Rx FIFO level
       
   168 
       
   169 		// If data remains in the Rx FIFO and the Rx buffer is not full, copy the data to the buffer
       
   170 		if(dataRemains && (a->iRxDataEnd - a->iRxData >= a->iWordSize) )
       
   171 			{
       
   172 			TUint8 value = 1;			// Would be a read of the Rx FIFO data
       
   173 			*a->iRxData = value;		// For this example, assumes one byte of data has been read from the FIFO
       
   174 										// but if operating in 16-bit mode, two "values" would be written the buffer
       
   175 			a->iRxData += a->iWordSize;	// In this example, 8-bit mode is assumed (iWordSize=1)
       
   176 										// but if operating in 16-bit mode a->iRxData would be incremented by 
       
   177 										// the number of bytes specified in a->iWordSize
       
   178 			}
       
   179 		}
       
   180 
       
   181 	if(a->iOperation.iOp.iIsTransmitting)
       
   182 		{
       
   183 		// If the channel has been transmitting data, may need to ensure that any FIFO used has been flushed
       
   184 		// To check if data remains in a FIFO, something similar to the following could be used:
       
   185 		//		TInt8 dataRemains = AsspRegister::Read32(a->aRegisterSetBaseAddress + KTxFifoLevel);
       
   186 		// The means to flush the FIFO will be platform specific, and so no example is given here.
       
   187 		}
       
   188 
       
   189 	// Start the next transfer for this transaction, if any remain
       
   190 	if(a->iState == EBusy)
       
   191 		{
       
   192 		TInt err = a->ProcessNextTransfers();
       
   193 		if(err != KErrNone)
       
   194 			{
       
   195 			// If the next transfer could not be started, complete the transaction with
       
   196 			// the returned error code
       
   197 			a->ExitComplete(err);
       
   198 			}
       
   199 		}
       
   200 	}
       
   201 
       
   202 
       
   203 // ISR Handler
       
   204 //
       
   205 // If the channel is to be event driven, it will use interrupts that indicate the 
       
   206 // hardware has received or transmitted. To support this an ISR is required.
       
   207 //
       
   208 void DIicBusChannelMasterPsl::IicIsr(TAny* aPtr)
       
   209 	{
       
   210 	DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr;
       
   211 
       
   212 	// The processing for Rx and Tx will differ, so must determine the status of the interrupts.
       
   213 	// This will be PSL-specific, but is likely to achieved by reading a status register, in a
       
   214 	// way similar to this:
       
   215 	//		TUint32 status = AsspRegister::Read32(aRegisterSetBaseAddress + KStatusRegisterOffset);
       
   216 	//
       
   217 	// For the purposes of compiling the template port, just initialise status to zero.
       
   218 	TUint32 status = 0;
       
   219 
       
   220 	if(status & KIicPslTxInterrupt)
       
   221 		{
       
   222 		// Tx interrupt processing
       
   223 
       
   224 		// Clear the interrupt source, with something similar to the following line:
       
   225 		//		AsspRegister::Write32(a->aRegisterSetBaseAddress + KStatusRegisterOffset, KIicPslTxInterrupt);
       
   226 
       
   227 		// Then check whether all the required data has been transmitted.
       
   228 		if(a->iTxData == a->iTxDataEnd)
       
   229 			{
       
   230 			// All data sent, so disable the Tx interrupt and queue a DFC to handle the next steps.
       
   231 			//		Interrupt::Disable(a->iTxInterruptId);
       
   232 			a->iTransferEndDfc.Add();
       
   233 			}
       
   234 		else
       
   235 			{
       
   236 			if(a->iOperation.iOp.iIsTransmitting)
       
   237 				{
       
   238 				// Data remaining - copy the next value to send to the Tx register
       
   239 
       
   240 				// TUint8 nextTxValue = *a->iTxData;	// For this example, assumes one byte of data is to be transmitted
       
   241 														// but if operating in 16-bit mode, bytes may need arranging for
       
   242 														// endianness
       
   243 
       
   244 				// Write to the Tx register with something similar to the following:
       
   245 				//		AsspRegister::Write32(a->aRegisterSetBaseAddress + KTxRegisterOffset, nextTxValue);
       
   246 
       
   247 				a->iTxData += a->iWordSize;	// Then increment the pointer to the data. In this example, 8-bit mode is assumed
       
   248 											// (iWordSize=1), but if operating in 16-bit mode a->iTxData would be incremented
       
   249 											// by the number of bytes specified in a->iWordSize
       
   250 				}
       
   251 			}
       
   252 		}
       
   253 
       
   254 	if(status & KIicPslRxInterrupt)
       
   255 		{
       
   256 		// Rx interrupt processing
       
   257 
       
   258 		// Copy the received data to the Rx buffer. 
       
   259 		// Do this in a loop in case there are more than one units of data to be handled. Data availability
       
   260 		// will be indicated by a PSL-specific register, and so may be handled by code similar to the following:
       
   261 		//		while(AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxData))
       
   262 		// 
       
   263 		// But, to allow compilation of the template port, just use a dummy condition (while(1)):
       
   264 		while(1)
       
   265 			{
       
   266 			// While there is space in the buffer, copy received data to it
       
   267 			if((a->iRxDataEnd - a->iRxData) >= a->iWordSize)
       
   268 				{
       
   269 				TUint8 nextRxValue = 0;
       
   270 				// Read from the Rx register with something similar to the following:
       
   271 				//		TUint8 nextRxValue = AsspRegister::Read32(aRegisterSetBaseAddress + KRxRegisterOffset);
       
   272 				*a->iRxData = nextRxValue;
       
   273 				}
       
   274 			else
       
   275 				{
       
   276 				// If there is no space left in the buffer an Overrun has occurred
       
   277 				a->iTransactionStatus = KErrOverflow;
       
   278 				break;
       
   279 				}
       
   280 
       
   281 			// Increment the pointer to the received data
       
   282 			a->iRxData += a->iWordSize;
       
   283 			}
       
   284 
       
   285 		// If the Rx buffer is now full, finish the transmission.
       
   286 		if(a->iRxDataEnd == a->iRxData)
       
   287 			{
       
   288 			// Disable the interrupt since it is no longer required
       
   289 			//		Interrupt::Disable(a->iRxInterruptId);
       
   290 
       
   291 			// Then queue a DFC to perform the next steps
       
   292 			a->iTransferEndDfc.Add();
       
   293 			}
       
   294 		
       
   295 		// After processing the data, clear the interrupt source (do it last to prevent the ISR being
       
   296 		// re-invoked before this ISR is finished), with something similar to the following line:
       
   297 		//		AsspRegister::Write32(a->aRegisterSetBaseAddress + KStatusRegisterOffset, KIicPslRxInterrupt);
       
   298 		}
       
   299 	}
       
   300 
       
   301 // Constructor, first stage
       
   302 //
       
   303 // The PSL is responsible for setting the channel number - this is passed as the first parameter to
       
   304 // this overload of the base class constructor
       
   305 //
       
   306 DIicBusChannelMasterPsl::DIicBusChannelMasterPsl(TInt aChannelNumber, TBusType aBusType, TChannelDuplex aChanDuplex) :
       
   307 	DIicBusChannelMaster(aBusType, aChanDuplex),				// Base class constructor
       
   308 	iTransferEndDfc(TransferEndDfc, this, KIicPslDfcPriority),	// DFC to handle transfer completion
       
   309 	iHwGuardTimer(TimeoutCallback, this)						// Timer to guard against hardware timeout
       
   310 	{
       
   311 	iChannelNumber = aChannelNumber;	// Set the iChannelNumber of the Base Class
       
   312 	iState = EIdle;						// Initialise channel state machine
       
   313 
       
   314 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::DIicBusChannelMasterPsl: iChannelNumber = %d", iChannelNumber));
       
   315 	}
       
   316 
       
   317 // Second stage construction
       
   318 //
       
   319 // Allocate and initialise objects required by the PSL channel implementation
       
   320 //
       
   321 TInt DIicBusChannelMasterPsl::DoCreate()
       
   322 	{
       
   323 	__KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelMasterPsl::DoCreate() ch: %d \n", iChannelNumber));
       
   324 
       
   325 	TInt r = KErrNone;
       
   326 
       
   327 	// Interrupt IDs (such as iRxInterruptId, iTxInterruptId, used in this file)would be initialised here.
       
   328 	//
       
   329 	// Also, information relevant to the channel number (such as the base register address, 
       
   330 	// aRegisterSetBaseAddress) would be initialised here.
       
   331 
       
   332 	// Create the DFCQ to be used by the channel
       
   333 	if(!iDfcQ)
       
   334 		{
       
   335 		TBuf8<KMaxName> threadName (KIicPslThreadName);
       
   336 		threadName.AppendNum(iChannelNumber);		// Optional: append the channel number to the name
       
   337 		r = Kern::DfcQCreate(iDfcQ, KIicPslThreadPriority, &threadName);
       
   338 		if(r != KErrNone)
       
   339 			{
       
   340 			__KTRACE_OPT(KIIC, Kern::Printf("DFC Queue creation failed, channel number: %d, r = %d\n", iChannelNumber, r));
       
   341 			return r;
       
   342 			}
       
   343 		}
       
   344 
       
   345 	// PIL Base class initialization - this must be called prior to SetDfcQ(iDfcQ)
       
   346 	r = Init();			
       
   347 	if(r == KErrNone)
       
   348 		{
       
   349 		// Call base class function to set DFCQ pointers in the required objects
       
   350 		// This also enables the channel to process transaction requests
       
   351 		SetDfcQ(iDfcQ); 
       
   352 
       
   353 		// PSL DFCQ initialisation for local DFC
       
   354 		iTransferEndDfc.SetDfcQ(iDfcQ); 
       
   355 
       
   356 #ifdef CPU_AFFINITY_ANY
       
   357 		NKern::ThreadSetCpuAffinity((NThread*)(iDfcQ->iThread), KCpuAffinityAny);
       
   358 #endif
       
   359 		// Bind interrupts.
       
   360 		// This would be with something similar to the following lines:
       
   361 		//		iMasterIntId = Interrupt::Bind(interruptIdToUse, IicPslIsr, this);
       
   362 		//
       
   363 		// Interrupt::Bind returns interruptId or an error code (negative value)
       
   364 		if(iMasterIntId < KErrNone)
       
   365 			{
       
   366 			__KTRACE_OPT(KIIC, Kern::Printf("ERROR: InterruptBind error.. %d", r));
       
   367 			r = iMasterIntId;
       
   368 			}
       
   369 		}
       
   370 	return r;
       
   371 	}
       
   372 
       
   373 // New
       
   374 //
       
   375 // A static method used to construct the DIicBusChannelMasterPsl object.
       
   376 DIicBusChannelMasterPsl* DIicBusChannelMasterPsl::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex)
       
   377 	{
       
   378 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::NewL(): ChannelNumber = %d, BusType =%d", aChannelNumber, aBusType));
       
   379 	DIicBusChannelMasterPsl *pChan = new DIicBusChannelMasterPsl(aChannelNumber, aBusType, aChanDuplex);
       
   380 
       
   381 	TInt r = KErrNoMemory;
       
   382 	if(pChan)
       
   383 		{
       
   384 		r = pChan->DoCreate();
       
   385 		}
       
   386 	if(r != KErrNone)
       
   387 		{
       
   388 		delete pChan;
       
   389 		pChan = NULL;
       
   390 		}
       
   391 	return pChan;
       
   392 	}
       
   393 
       
   394 // Optional method - that determines if the previous transaction header is different to the current one.
       
   395 // If configuration is the same, the hardware may not need to be re-initialized. 
       
   396 TBool DIicBusChannelMasterPsl::TransConfigDiffersFromPrev()
       
   397 	{
       
   398 	// 
       
   399 	// The header will be specific to a particular bus type. Using a fictional
       
   400 	// bus type Abc, code similar to the following could be used to compare each
       
   401 	// member of the previous header with the current one
       
   402 	// 
       
   403 	//		TConfigAbcBufV01* oldHdrBuf = (TConfigAbcBufV01*) iPrevHeader;
       
   404 	//		TConfigAbcV01 &oldHeader = (*oldHdrBuf)();
       
   405 	//		TConfigAbcBufV01* newHdrBuf = (TConfigAbcBufV01*) iCurrHeader;
       
   406 	//		TConfigAbcV01 &newHeader = (*newHdrBuf)();
       
   407 	//		if(	(newHeader.iHeaderMember != oldHeader.iHeaderMember)	||
       
   408 	//			... )
       
   409 	//			{
       
   410 	//			return EFalse;
       
   411 	//			}
       
   412 	return ETrue;
       
   413 	}
       
   414 
       
   415 
       
   416 // CheckHdr is called by the PIL when a transaction is queued, in function
       
   417 // QueueTransaction. This is done in the context of the Client's thread.
       
   418 //
       
   419 // The PSL is required to check that the transaction header is valid for
       
   420 // this channel.
       
   421 //
       
   422 // If the pointer to the header is NULL, return KErrArgument.
       
   423 // If the content of the header is not valid for this channel, return KErrNotSupported.
       
   424 //
       
   425 TInt DIicBusChannelMasterPsl::CheckHdr(TDes8* aHdrBuff)
       
   426 	{
       
   427 	TInt r = KErrNone;
       
   428 
       
   429 	if(!aHdrBuff)
       
   430 		{
       
   431 		r = KErrArgument;
       
   432 		}
       
   433 	else
       
   434 		{
       
   435 		// Check that the contents of the header are valid
       
   436 		//
       
   437 		// The header will be specific to a particular bus type. Using a fictional
       
   438 		// bus type Abc,code similar to the following could be used to validate each
       
   439 		// member of the header:
       
   440 		// 
       
   441 		//		TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) aHdrBuff;
       
   442 		//		TConfigAbcV01 &abcHeader = (*headerBuf)();
       
   443 		//		if(	(abcHeader.iHeaderMember < ESomeMinValue)	||
       
   444 		//			(abcHeader.iHeaderMember > ESomeMaxValue))
       
   445 		//			{
       
   446 		//			__KTRACE_OPT(KIIC, Kern::Printf("iHeaderMember %d not supported",abcHeader.iHeaderMember));
       
   447 		//			r = KErrNotSupported;
       
   448 		//			}
       
   449 		}
       
   450 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::CheckHdr() r %d", r));
       
   451 	return r;
       
   452 	}
       
   453 
       
   454 // Initialise the hardware with the data provided in the transaction and slave-address field
       
   455 //
       
   456 // If a specified configuration is not supported, return KErrNotSupported
       
   457 // If a configuration is supported, but the implementing configuration fails, return KErrGeneral
       
   458 //
       
   459 TInt DIicBusChannelMasterPsl::ConfigureInterface()
       
   460 	{
       
   461 	__KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()"));
       
   462 
       
   463 	// This method will be platform-specific and will configure the hardware as required to support
       
   464 	// the current transacation. This will be supported with something similar to the following:
       
   465 	//		AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit);
       
   466 	//		GPIO::SetPinMode(aPinId, GPIO::EEnabled);
       
   467 
       
   468 	// Depending on the platform, timers (such as iHwGuardTimer) may be used to check that the hardware
       
   469 	// responds in the required way within an allowed timeout. Since this is configuring the channel for
       
   470 	// an operation, it is acceptable to perform an active wait, with something similar to the following:
       
   471 	//		iTransactionStatus = KErrNone;
       
   472 	//		iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
       
   473 	//		while((iTransactionStatus == KErrNone) &&
       
   474 	//			AsspRegister::Read32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit);
       
   475 	//		if(iTransactionStatus != KErrNone)
       
   476 	//			{
       
   477 	//			return KErrGeneral;
       
   478 	//			}
       
   479 	//		else
       
   480 	//			{
       
   481 	//			iHwGuardTimer.Cancel();
       
   482 	//			}
       
   483 	return KErrNone;
       
   484 	}
       
   485 
       
   486 
       
   487 // Method called by StartTransfer to actually initiate the transfers. It manipulates the hardware to
       
   488 // perform the required tasks.
       
   489 //
       
   490 TInt DIicBusChannelMasterPsl::DoTransfer(TInt8 *aBuff, TUint aNumOfBytes, TUint8 aType)
       
   491 	{
       
   492 	__KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelMasterPsl::DoTransfer() - aBuff=0x%x, aNumOfBytes=0x%x\n",aBuff,aNumOfBytes));
       
   493 
       
   494 	TInt r = KErrNone;
       
   495 
       
   496 	// Validate the input arguments
       
   497 	if((aBuff == NULL) || (aNumOfBytes == 0))
       
   498 		{
       
   499 		r = KErrArgument;
       
   500 		}
       
   501 	else
       
   502 		{
       
   503 		// This method will be platform-specific and will configure the hardware as required
       
   504 		// This will likely be supported with calls similar to the following:
       
   505 		//		AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit);
       
   506 		//		GPIO::SetPinMode(aPinId, GPIO::EEnabled);
       
   507 		//		Interrupt::Enable(aInterruptId);
       
   508 		//
       
   509 		// Steps that may be typically required are described below
       
   510 
       
   511 		switch(aType)
       
   512 			{
       
   513 			case TIicBusTransfer::EMasterWrite:
       
   514 				{
       
   515 				// If using a Tx FIFO, may wish to disable transmission until the FIFO has been filled to certain level
       
   516 				// If using a Tx FIFO, may wish to flush it and re-initialise any counters or pointers used
       
   517 
       
   518 				// If using a FIFO, copy data to it until either the FIFO is full or all data has been copied
       
   519 				// Checking the FIFO is full will be reading a flag in a status register, by use of code similar the following
       
   520 				//		TUint32 status = AsspRegister::Read32(aRegisterSetBaseAddress + KStatusRegisterOffset);
       
   521 				//		if(status & KTxFifoFullBitMask)
       
   522 				//			... FIFO is full
       
   523 				//
       
   524 				// For this example base port, represent the FIFO full status by a dummy value (zero).
       
   525 				TUint8 txFifoFull = 0;
       
   526 				while(!txFifoFull && (iTxData != iTxDataEnd))
       
   527 					{
       
   528 					// TUint8 nextTxValue = *iTxData;	// For this example, assumes one byte of data is to be transmitted
       
   529 															// but if operating in 16-bit mode, bytes may need arranging for
       
   530 															// endianness
       
   531 
       
   532 					// Write to the Tx FIFO register with something similar to the following:
       
   533 					//		AsspRegister::Write32(a->aRegisterSetBaseAddress + KTxRegisterOffset, nextTxValue);
       
   534 
       
   535 					iTxData += iWordSize;	// Then increment the pointer to the data. In this example, 8-bit mode is assumed
       
   536 												// (iWordSize=1), but if operating in 16-bit mode a->iTxData would be incremented
       
   537 												// by the number of bytes specified in a->iWordSize
       
   538 					}
       
   539 				// May wish to enable transmission now - or wait until after the Read transfer has been initialised
       
   540 				break;
       
   541 				}
       
   542 			case TIicBusTransfer::EMasterRead:
       
   543 				{
       
   544 				// If using an Rx FIFO, it will already have been drained at the end of the last transfer by TransferEndDfc
       
   545 				// so no need to do it again here.
       
   546 
       
   547 				// May wish to enable reception now - or group with the code, below
       
   548 				break;
       
   549 				}
       
   550 			default:
       
   551 				{
       
   552 				__KTRACE_OPT(KIIC, Kern::Printf("Unsupported TransactionType %x", aType));
       
   553 				r = KErrArgument;
       
   554 				break;
       
   555 				}
       
   556 			}
       
   557 
       
   558 		// Final stages of hardware preparation
       
   559 		//
       
   560 		// Enable hardware interrupts
       
   561 		// Finally, enable (start) transmission and reception
       
   562 		}
       
   563 
       
   564 	return r;
       
   565 	}
       
   566 
       
   567 
       
   568 // This method performs the initialisation required for either a read or write transfer
       
   569 // and then invokes the next stage of the processing (DoTransfer)
       
   570 //
       
   571 TInt DIicBusChannelMasterPsl::StartTransfer(TIicBusTransfer* aTransferPtr, TUint8 aType)
       
   572 	{
       
   573 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::StartTransfer() - aTransferPtr=0x%x, aType=%d",aTransferPtr,aType));
       
   574 
       
   575 	if(aTransferPtr == NULL)
       
   576 		{
       
   577 		__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::StartTransfer - NULL pointer\n"));
       
   578 		return KErrArgument;
       
   579 		}
       
   580 
       
   581 	TInt r = KErrNone;
       
   582 
       
   583 	switch(aType)
       
   584 		{
       
   585 		case TIicBusTransfer::EMasterWrite:
       
   586 			{
       
   587 			__KTRACE_OPT(KIIC, Kern::Printf("Starting EMasterWrite, duplex=%d", iFullDTransfer));
       
   588 
       
   589 			// Get a pointer to the transfer object's buffer, to facilitate passing arguments to DoTransfer
       
   590 			const TDes8* aBufPtr = GetTferBuffer(aTransferPtr);
       
   591 
       
   592 			__KTRACE_OPT(KIIC, Kern::Printf("Length %d, iWordSize %d", aBufPtr->Length(), iWordSize));
       
   593 
       
   594 			// Store the current address and ending address for Transmission - they are required by the ISR and DFC
       
   595 			iTxData = (TInt8*) aBufPtr->Ptr();
       
   596 			iTxDataEnd = (TInt8*) (iTxData + aBufPtr->Length());
       
   597 
       
   598 			__KTRACE_OPT(KIIC, Kern::Printf("Tx: Start: %x, End %x, bytes %d\n\n", iTxData, iTxDataEnd, aBufPtr->Length()));
       
   599 
       
   600 			// Set the flag to indicate that we'll be transmitting data
       
   601 			iOperation.iOp.iIsTransmitting = ETrue;
       
   602 
       
   603 			// initiate the transmission..
       
   604 			r = DoTransfer((TInt8 *) aBufPtr->Ptr(), aBufPtr->Length(), aType);
       
   605 			if(r != KErrNone)
       
   606 				{
       
   607 				__KTRACE_OPT(KIIC, Kern::Printf("Starting Write failed, r = %d", r));
       
   608 				}
       
   609 			break;
       
   610 			}
       
   611 
       
   612 		case TIicBusTransfer::EMasterRead:
       
   613 			{
       
   614 			__KTRACE_OPT(KIIC, Kern::Printf("Starting EMasterRead, duplex=%x", iFullDTransfer));
       
   615 
       
   616 			// Get a pointer to the transfer object's buffer, to facilitate passing arguments to DoTransfer
       
   617 			const TDes8* aBufPtr = GetTferBuffer(aTransferPtr);
       
   618 
       
   619 			// Store the current address and ending address for Reception - they are required by the ISR and DFC
       
   620 			iRxData = (TInt8*) aBufPtr->Ptr();
       
   621 			iRxDataEnd = (TInt8*) (iRxData + aBufPtr->Length());
       
   622 
       
   623 			__KTRACE_OPT(KIIC, Kern::Printf("Rx: Start: %x, End %x, bytes %d", iRxData, iRxDataEnd, aBufPtr->Length()));
       
   624 
       
   625 			// Set the flag to indicate that we'll be receiving data
       
   626 			iOperation.iOp.iIsReceiving = ETrue;
       
   627 
       
   628 			// initiate the reception
       
   629 			r = DoTransfer((TInt8 *) aBufPtr->Ptr(), aBufPtr->Length(), aType);
       
   630 			if(r != KErrNone)
       
   631 				{
       
   632 				__KTRACE_OPT(KIIC, Kern::Printf("Starting Read failed, r = %d", r));
       
   633 				}
       
   634 			break;
       
   635 			}
       
   636 
       
   637 		default:
       
   638 			{
       
   639 			__KTRACE_OPT(KIIC, Kern::Printf("Unsupported TransactionType %x", aType));
       
   640 			r = KErrArgument;
       
   641 			break;
       
   642 			}
       
   643 		}
       
   644 
       
   645 	return r;
       
   646 	}
       
   647 
       
   648 // This method determines the next transfers to be processed, and passes them to the next stage
       
   649 // in the processing (StartTransfer).
       
   650 //
       
   651 // This is called from DoRequest (for the first transfer) and TransferEndDfc (after a transfer
       
   652 // has completed)
       
   653 //
       
   654 TInt DIicBusChannelMasterPsl::ProcessNextTransfers()
       
   655 	{
       
   656 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::ProcessNextTransfers(),BUSY=%d", iState));
       
   657 
       
   658 	// Since new transfers are strating, clear exisiting flags
       
   659 	iOperation.iValue = TIicOperationType::ENop;
       
   660 
       
   661 	// Some hardware preparation may be required before starting the transfer using something similar
       
   662 	// to the following line:
       
   663 	//		AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit);
       
   664 
       
   665 	// If this is the first transfer in the transaction the channel will be in state EIdle
       
   666 	if(iState == EIdle)
       
   667 		{
       
   668 		// Get the pointer to half-duplex transfer object..
       
   669 		iHalfDTransfer = GetTransHalfDuplexTferPtr(iCurrTransaction);
       
   670 
       
   671 		// Get the pointer to full-duplex transfer object..
       
   672 		iFullDTransfer = GetTransFullDuplexTferPtr(iCurrTransaction);
       
   673 
       
   674 		// Update the channel state to EBusy and initialise the transaction status
       
   675 		iState = EBusy;
       
   676 		iTransactionStatus = KErrNone;
       
   677 
       
   678 		// Use the PIL funcitonality to start a timer that will timeout if the transaction
       
   679 		// is not completed within a specified time period (the client may have specified a period
       
   680 		// to use in the transaction header - some something similar tot he following could be used)
       
   681 		//		StartSlaveTimeOutTimer(iCurrHeader->iTimeoutPeriod);
       
   682 		//
       
   683 		// If the timer expires, callback function HandleSlaveTimeout (implemented by the PSL, above)
       
   684 		// will be called. This will ensure that the hardware ceases transfer activity, and calls ExitComplete
       
   685 		// with KErrTImedOut, which will return the channel state to EIdle.
       
   686 		}
       
   687 	else
       
   688 	// If not in state EIdle, process the next transfer in the linked-list held by the transaction
       
   689 		{
       
   690 		// Get the pointer the next half-duplex transfer object..
       
   691 		iHalfDTransfer = GetTferNextTfer(iHalfDTransfer);
       
   692 
       
   693 		// Get the pointer to the next half-duplex transfer object..
       
   694 		if(iFullDTransfer)
       
   695 			{
       
   696 			iFullDTransfer = GetTferNextTfer(iFullDTransfer);
       
   697 			}
       
   698 		}
       
   699 
       
   700 	TInt r = KErrNone;
       
   701 	if(!iFullDTransfer && !iHalfDTransfer)
       
   702 		{
       
   703 		// If all of the transfers were completed, just notify the PIL and return.
       
   704 		// (if either Rx or Tx has not finished properly ExitComplete() would have been called
       
   705 		// from TransferEndDfc if there was an error during the transfer)
       
   706 		__KTRACE_OPT(KIIC, Kern::Printf("All transfers completed successfully"));
       
   707 
       
   708 		ExitComplete(KErrNone);
       
   709 		}
       
   710 	else
       
   711 		{
       
   712 		// Transfers remain to be processed
       
   713 		//
       
   714 		// For full-duplex transfers, the order in which read and write transfers are started may be significant.
       
   715 		// Below is an example where the read transfer is explicitly started before the write transfer.
       
   716 		TInt8 hDTrType = (TInt8) GetTferType(iHalfDTransfer);
       
   717 
       
   718 		if(iFullDTransfer)
       
   719 			{
       
   720 			if(hDTrType == TIicBusTransfer::EMasterRead)
       
   721 				{
       
   722 				r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterRead);
       
   723 				if(r != KErrNone)
       
   724 					{
       
   725 					return r;
       
   726 					}
       
   727 				r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterWrite);
       
   728 				}
       
   729 			else // hDTrType == TIicBusTransfer::EMasterWrite)
       
   730 				{
       
   731 				r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterRead);
       
   732 				if(r != KErrNone)
       
   733 					{
       
   734 					return r;
       
   735 					}
       
   736 				r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterWrite);
       
   737 				}
       
   738 			}
       
   739 		else
       
   740 		// This is a HalfDuplex transfer - so just start it
       
   741 			{
       
   742 			r = StartTransfer(iHalfDTransfer, hDTrType);
       
   743 			}
       
   744 		}
       
   745 	return r;
       
   746 	}
       
   747 
       
   748 // The gateway function for PSL implementation
       
   749 //
       
   750 // This method is called by the PIL to initiate the transaction. After finishing it's processing,
       
   751 // the PSL calls the PIL function CompleteRequest to indicate the success (or otherwise) of the request
       
   752 //
       
   753 TInt DIicBusChannelMasterPsl::DoRequest(TIicBusTransaction* aTransaction)
       
   754 	{
       
   755 	__KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::DoRequest (aTransaction=0x%x)\n", aTransaction));
       
   756 
       
   757 	// If the pointer to the transaction passed in as a parameter, or its associated pointer to the
       
   758 	// header information is NULL, return KErrArgument
       
   759 	if(!aTransaction || !GetTransactionHeader(aTransaction))
       
   760 		{
       
   761 		return KErrArgument;
       
   762 		}
       
   763 
       
   764 	// The PSL operates a simple state machine to ensure that only one transaction is processed
       
   765 	// at a time - if the channel is currently busy, reject the request with KErrInUse.
       
   766 	if(iState != EIdle)
       
   767 		{
       
   768 		return KErrInUse;
       
   769 		}
       
   770 
       
   771 	// Make a copy of the pointer to the transaction
       
   772 	iCurrTransaction = aTransaction;
       
   773 
       
   774 	// Configure the hardware to support the transaction
       
   775 	TInt r = KErrNone;
       
   776 	if(TransConfigDiffersFromPrev())	// Optional: check if hardware needs reconfiguration
       
   777 		{
       
   778 		r = ConfigureInterface();
       
   779 		if(r != KErrNone)
       
   780 			{
       
   781 			return r;
       
   782 			}
       
   783 		}
       
   784 
       
   785 	// start processing transfers of this transaction.
       
   786 	r = ProcessNextTransfers();
       
   787 	return r;
       
   788 	}
       
   789