diff -r 000000000000 -r 96e5fb8b040d bsptemplate/asspandvariant/template_assp/iic/iic_master.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bsptemplate/asspandvariant/template_assp/iic/iic_master.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,789 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + + +#include +// #include // Include if using GPIO functionality +#include "iic_psl.h" +#include "iic_master.h" + + +// Method called when transmission activity is ended. The method is used to indicate +// the success or failure reported in the first parameter +// +// All timers are cancelled, relevant interrupts are disabled and the transaction +// request is completed by calling the relevant PIL method. +// +void DIicBusChannelMasterPsl::ExitComplete(TInt aErr, TBool aComplete /*= ETrue*/) + { + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::ExitComplete, aErr %d, aComplete %d", aErr, aComplete)); + + // Disable interrupts for the channel + // with something similar to the following lines: + // Interrupt::Disable(iRxInterruptId); + // Interrupt::Disable(iTxInterruptId); + + // Cancel timers and DFCs.. + CancelTimeOut(); + iHwGuardTimer.Cancel(); + iTransferEndDfc.Cancel(); + + // Change the channel state to EIdle so that subsequent transaction requests can be accepted + // once the current one has been completed + iState = EIdle; + + // Call the PIL method to complete the request + if(aComplete) + { + CompleteRequest(aErr); + } + } + +// Callback function for the iHwGuardTimer timer. +// +// Called in ISR context if the iHwGuardTimer expires. Sets iTransactionStatus to KErrTimedOut +// +void DIicBusChannelMasterPsl::TimeoutCallback(TAny* aPtr) + { + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TimeoutCallback")); + DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr; + a->iTransactionStatus = KErrTimedOut; + } + +// HandleSlaveTimeout +// +// This method is called by the PIL in the case of expiry of a timer started by the PSL. It is +// specificaly intended to guard against the Slave not responding within an expected time +// +// The PIL method StartSlaveTimeoutTimer is available for the PSL to start the timer (this is +// called from ProcessNextTransfers, below). +// The PIL method CancelTimeOut is available for the PSL to cancel the same timer (this is called +// from ExitComplete, above) +// +// The PIL will call CompleteRequest() after this function returns, so the PSL needs only to clean-up +// +TInt DIicBusChannelMasterPsl::HandleSlaveTimeout() + { + __KTRACE_OPT(KIIC, Kern::Printf("HandleSlaveTimeout")); + + // Ensure that the hardware has ceased transfers, with something similar to the following line: + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusDisableBit); + // GPIO::SetPinMode(aPinId, GPIO::EDisabled); + + // Stop the PSL's operation, and inform the PIL of the timeout + ExitComplete(KErrTimedOut, EFalse); + + // Perform any further hardware manipulation necessary + // + + return KErrTimedOut; + } + + +// DFC +// +// For execution when a Rx buffer has been filled or a Tx buffer has been emptied +// +void DIicBusChannelMasterPsl::TransferEndDfc(TAny* aPtr) + { + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TransferEndDfc")); + DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr; + + // Start of optional processing - not necessary for all implementations + // + // When operating full-duplex transfers, one of the Rx and Tx operations may have caused an interrupt + // before the other has completed. For the example here, the Tx is assumed to have completed before the Rx + // and a timer is used to ensure that the outstanding Rx operation completes within an expected time. + // + // If there has been no error so far, may want to check if we are still receiving the data + if(a->iTransactionStatus == KErrNone) + { + // Use an active wait since this is likely to be a brief check. + // Start the guard timer (which will update iTransactionStatus with KErrTimedOut if it expires) + // while also polling the hardware to check if transmission has ceased. + // + // Polling the hardware would be something like the line below + // (AsspRegister::Read32(a->aRegisterSetBaseAddress + KStatusRegisterOffset) & KTransmissionActive)); + // but for the template port will use a dummy condition (value of 1) + // + a->iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue)); + while((a->iTransactionStatus == KErrNone) && 1); // Replace 1 with a register read + } + // + // Now the timer has expired, deactivate the slave select until the current state has been processed, but only + // if this is not an extended transaction, in which case we want to leave the bus alone so that the multiple + // transactions making up the extended transaction become one big transaction as far as the bus is concerned. + // Do this with something similar to the following line: + // if (!(a->iCurrTransaction->Flags() & KTransactionWithMultiTransc)) + // { + // GPIO::SetPinMode(aPinId, GPIO::EDisabled); + // } + // + // Disable the hardware from further transmissions + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusDisableBit); + // + // Check if the guard timer expired + if(a->iTransactionStatus != KErrNone) + { + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TransferEndDfc(): error %d",a->iTransactionStatus)); + a->ExitComplete(a->iTransactionStatus); // report the error + return; + } + else + { + // Transfer completed successfully, so just cancel the guard timer + a->iHwGuardTimer.Cancel(); + } + // End of optional processing - not necessary for all implementations + + // At this point, prepare for subsequent transfers by performing any necessary clean-up. + // As stated above, for this example, it is assumed that any Rx or Tx transfer has completed - + // the following just checks if an Rx, Tx operation was started, and assumes that they completed. + + if(a->iOperation.iOp.iIsReceiving) + { + // If the channel has been receiving data, may need to ensure that any FIFO used has been drained + // The example here checks if one extra data item remains in the FIFO + // To check if data remains in a FIFO, something similar to the following could be used: + // TInt8 dataRemains = AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxFifoLevel); + // Reading data from the FIFO would be achieved with something like the line below + // value = AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxData); + // but for the template port will just use a dummy values (data remains=1, value = 1) + // + TInt8 dataRemains = 1; // Would be a check of a Rx FIFO level + + // If data remains in the Rx FIFO and the Rx buffer is not full, copy the data to the buffer + if(dataRemains && (a->iRxDataEnd - a->iRxData >= a->iWordSize) ) + { + TUint8 value = 1; // Would be a read of the Rx FIFO data + *a->iRxData = value; // For this example, assumes one byte of data has been read from the FIFO + // but if operating in 16-bit mode, two "values" would be written the buffer + a->iRxData += a->iWordSize; // In this example, 8-bit mode is assumed (iWordSize=1) + // but if operating in 16-bit mode a->iRxData would be incremented by + // the number of bytes specified in a->iWordSize + } + } + + if(a->iOperation.iOp.iIsTransmitting) + { + // If the channel has been transmitting data, may need to ensure that any FIFO used has been flushed + // To check if data remains in a FIFO, something similar to the following could be used: + // TInt8 dataRemains = AsspRegister::Read32(a->aRegisterSetBaseAddress + KTxFifoLevel); + // The means to flush the FIFO will be platform specific, and so no example is given here. + } + + // Start the next transfer for this transaction, if any remain + if(a->iState == EBusy) + { + TInt err = a->ProcessNextTransfers(); + if(err != KErrNone) + { + // If the next transfer could not be started, complete the transaction with + // the returned error code + a->ExitComplete(err); + } + } + } + + +// ISR Handler +// +// If the channel is to be event driven, it will use interrupts that indicate the +// hardware has received or transmitted. To support this an ISR is required. +// +void DIicBusChannelMasterPsl::IicIsr(TAny* aPtr) + { + DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr; + + // The processing for Rx and Tx will differ, so must determine the status of the interrupts. + // This will be PSL-specific, but is likely to achieved by reading a status register, in a + // way similar to this: + // TUint32 status = AsspRegister::Read32(aRegisterSetBaseAddress + KStatusRegisterOffset); + // + // For the purposes of compiling the template port, just initialise status to zero. + TUint32 status = 0; + + if(status & KIicPslTxInterrupt) + { + // Tx interrupt processing + + // Clear the interrupt source, with something similar to the following line: + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KStatusRegisterOffset, KIicPslTxInterrupt); + + // Then check whether all the required data has been transmitted. + if(a->iTxData == a->iTxDataEnd) + { + // All data sent, so disable the Tx interrupt and queue a DFC to handle the next steps. + // Interrupt::Disable(a->iTxInterruptId); + a->iTransferEndDfc.Add(); + } + else + { + if(a->iOperation.iOp.iIsTransmitting) + { + // Data remaining - copy the next value to send to the Tx register + + // TUint8 nextTxValue = *a->iTxData; // For this example, assumes one byte of data is to be transmitted + // but if operating in 16-bit mode, bytes may need arranging for + // endianness + + // Write to the Tx register with something similar to the following: + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KTxRegisterOffset, nextTxValue); + + a->iTxData += a->iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed + // (iWordSize=1), but if operating in 16-bit mode a->iTxData would be incremented + // by the number of bytes specified in a->iWordSize + } + } + } + + if(status & KIicPslRxInterrupt) + { + // Rx interrupt processing + + // Copy the received data to the Rx buffer. + // Do this in a loop in case there are more than one units of data to be handled. Data availability + // will be indicated by a PSL-specific register, and so may be handled by code similar to the following: + // while(AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxData)) + // + // But, to allow compilation of the template port, just use a dummy condition (while(1)): + while(1) + { + // While there is space in the buffer, copy received data to it + if((a->iRxDataEnd - a->iRxData) >= a->iWordSize) + { + TUint8 nextRxValue = 0; + // Read from the Rx register with something similar to the following: + // TUint8 nextRxValue = AsspRegister::Read32(aRegisterSetBaseAddress + KRxRegisterOffset); + *a->iRxData = nextRxValue; + } + else + { + // If there is no space left in the buffer an Overrun has occurred + a->iTransactionStatus = KErrOverflow; + break; + } + + // Increment the pointer to the received data + a->iRxData += a->iWordSize; + } + + // If the Rx buffer is now full, finish the transmission. + if(a->iRxDataEnd == a->iRxData) + { + // Disable the interrupt since it is no longer required + // Interrupt::Disable(a->iRxInterruptId); + + // Then queue a DFC to perform the next steps + a->iTransferEndDfc.Add(); + } + + // After processing the data, clear the interrupt source (do it last to prevent the ISR being + // re-invoked before this ISR is finished), with something similar to the following line: + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KStatusRegisterOffset, KIicPslRxInterrupt); + } + } + +// Constructor, first stage +// +// The PSL is responsible for setting the channel number - this is passed as the first parameter to +// this overload of the base class constructor +// +DIicBusChannelMasterPsl::DIicBusChannelMasterPsl(TInt aChannelNumber, TBusType aBusType, TChannelDuplex aChanDuplex) : + DIicBusChannelMaster(aBusType, aChanDuplex), // Base class constructor + iTransferEndDfc(TransferEndDfc, this, KIicPslDfcPriority), // DFC to handle transfer completion + iHwGuardTimer(TimeoutCallback, this) // Timer to guard against hardware timeout + { + iChannelNumber = aChannelNumber; // Set the iChannelNumber of the Base Class + iState = EIdle; // Initialise channel state machine + + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::DIicBusChannelMasterPsl: iChannelNumber = %d", iChannelNumber)); + } + +// Second stage construction +// +// Allocate and initialise objects required by the PSL channel implementation +// +TInt DIicBusChannelMasterPsl::DoCreate() + { + __KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelMasterPsl::DoCreate() ch: %d \n", iChannelNumber)); + + TInt r = KErrNone; + + // Interrupt IDs (such as iRxInterruptId, iTxInterruptId, used in this file)would be initialised here. + // + // Also, information relevant to the channel number (such as the base register address, + // aRegisterSetBaseAddress) would be initialised here. + + // Create the DFCQ to be used by the channel + if(!iDfcQ) + { + TBuf8 threadName (KIicPslThreadName); + threadName.AppendNum(iChannelNumber); // Optional: append the channel number to the name + r = Kern::DfcQCreate(iDfcQ, KIicPslThreadPriority, &threadName); + if(r != KErrNone) + { + __KTRACE_OPT(KIIC, Kern::Printf("DFC Queue creation failed, channel number: %d, r = %d\n", iChannelNumber, r)); + return r; + } + } + + // PIL Base class initialization - this must be called prior to SetDfcQ(iDfcQ) + r = Init(); + if(r == KErrNone) + { + // Call base class function to set DFCQ pointers in the required objects + // This also enables the channel to process transaction requests + SetDfcQ(iDfcQ); + + // PSL DFCQ initialisation for local DFC + iTransferEndDfc.SetDfcQ(iDfcQ); + +#ifdef CPU_AFFINITY_ANY + NKern::ThreadSetCpuAffinity((NThread*)(iDfcQ->iThread), KCpuAffinityAny); +#endif + // Bind interrupts. + // This would be with something similar to the following lines: + // iMasterIntId = Interrupt::Bind(interruptIdToUse, IicPslIsr, this); + // + // Interrupt::Bind returns interruptId or an error code (negative value) + if(iMasterIntId < KErrNone) + { + __KTRACE_OPT(KIIC, Kern::Printf("ERROR: InterruptBind error.. %d", r)); + r = iMasterIntId; + } + } + return r; + } + +// New +// +// A static method used to construct the DIicBusChannelMasterPsl object. +DIicBusChannelMasterPsl* DIicBusChannelMasterPsl::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex) + { + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::NewL(): ChannelNumber = %d, BusType =%d", aChannelNumber, aBusType)); + DIicBusChannelMasterPsl *pChan = new DIicBusChannelMasterPsl(aChannelNumber, aBusType, aChanDuplex); + + TInt r = KErrNoMemory; + if(pChan) + { + r = pChan->DoCreate(); + } + if(r != KErrNone) + { + delete pChan; + pChan = NULL; + } + return pChan; + } + +// Optional method - that determines if the previous transaction header is different to the current one. +// If configuration is the same, the hardware may not need to be re-initialized. +TBool DIicBusChannelMasterPsl::TransConfigDiffersFromPrev() + { + // + // The header will be specific to a particular bus type. Using a fictional + // bus type Abc, code similar to the following could be used to compare each + // member of the previous header with the current one + // + // TConfigAbcBufV01* oldHdrBuf = (TConfigAbcBufV01*) iPrevHeader; + // TConfigAbcV01 &oldHeader = (*oldHdrBuf)(); + // TConfigAbcBufV01* newHdrBuf = (TConfigAbcBufV01*) iCurrHeader; + // TConfigAbcV01 &newHeader = (*newHdrBuf)(); + // if( (newHeader.iHeaderMember != oldHeader.iHeaderMember) || + // ... ) + // { + // return EFalse; + // } + return ETrue; + } + + +// CheckHdr is called by the PIL when a transaction is queued, in function +// QueueTransaction. This is done in the context of the Client's thread. +// +// The PSL is required to check that the transaction header is valid for +// this channel. +// +// If the pointer to the header is NULL, return KErrArgument. +// If the content of the header is not valid for this channel, return KErrNotSupported. +// +TInt DIicBusChannelMasterPsl::CheckHdr(TDes8* aHdrBuff) + { + TInt r = KErrNone; + + if(!aHdrBuff) + { + r = KErrArgument; + } + else + { + // Check that the contents of the header are valid + // + // The header will be specific to a particular bus type. Using a fictional + // bus type Abc,code similar to the following could be used to validate each + // member of the header: + // + // TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) aHdrBuff; + // TConfigAbcV01 &abcHeader = (*headerBuf)(); + // if( (abcHeader.iHeaderMember < ESomeMinValue) || + // (abcHeader.iHeaderMember > ESomeMaxValue)) + // { + // __KTRACE_OPT(KIIC, Kern::Printf("iHeaderMember %d not supported",abcHeader.iHeaderMember)); + // r = KErrNotSupported; + // } + } + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::CheckHdr() r %d", r)); + return r; + } + +// Initialise the hardware with the data provided in the transaction and slave-address field +// +// If a specified configuration is not supported, return KErrNotSupported +// If a configuration is supported, but the implementing configuration fails, return KErrGeneral +// +TInt DIicBusChannelMasterPsl::ConfigureInterface() + { + __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()")); + + // This method will be platform-specific and will configure the hardware as required to support + // the current transacation. This will be supported with something similar to the following: + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit); + // GPIO::SetPinMode(aPinId, GPIO::EEnabled); + + // Depending on the platform, timers (such as iHwGuardTimer) may be used to check that the hardware + // responds in the required way within an allowed timeout. Since this is configuring the channel for + // an operation, it is acceptable to perform an active wait, with something similar to the following: + // iTransactionStatus = KErrNone; + // iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue)); + // while((iTransactionStatus == KErrNone) && + // AsspRegister::Read32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit); + // if(iTransactionStatus != KErrNone) + // { + // return KErrGeneral; + // } + // else + // { + // iHwGuardTimer.Cancel(); + // } + return KErrNone; + } + + +// Method called by StartTransfer to actually initiate the transfers. It manipulates the hardware to +// perform the required tasks. +// +TInt DIicBusChannelMasterPsl::DoTransfer(TInt8 *aBuff, TUint aNumOfBytes, TUint8 aType) + { + __KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelMasterPsl::DoTransfer() - aBuff=0x%x, aNumOfBytes=0x%x\n",aBuff,aNumOfBytes)); + + TInt r = KErrNone; + + // Validate the input arguments + if((aBuff == NULL) || (aNumOfBytes == 0)) + { + r = KErrArgument; + } + else + { + // This method will be platform-specific and will configure the hardware as required + // This will likely be supported with calls similar to the following: + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit); + // GPIO::SetPinMode(aPinId, GPIO::EEnabled); + // Interrupt::Enable(aInterruptId); + // + // Steps that may be typically required are described below + + switch(aType) + { + case TIicBusTransfer::EMasterWrite: + { + // If using a Tx FIFO, may wish to disable transmission until the FIFO has been filled to certain level + // If using a Tx FIFO, may wish to flush it and re-initialise any counters or pointers used + + // If using a FIFO, copy data to it until either the FIFO is full or all data has been copied + // Checking the FIFO is full will be reading a flag in a status register, by use of code similar the following + // TUint32 status = AsspRegister::Read32(aRegisterSetBaseAddress + KStatusRegisterOffset); + // if(status & KTxFifoFullBitMask) + // ... FIFO is full + // + // For this example base port, represent the FIFO full status by a dummy value (zero). + TUint8 txFifoFull = 0; + while(!txFifoFull && (iTxData != iTxDataEnd)) + { + // TUint8 nextTxValue = *iTxData; // For this example, assumes one byte of data is to be transmitted + // but if operating in 16-bit mode, bytes may need arranging for + // endianness + + // Write to the Tx FIFO register with something similar to the following: + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KTxRegisterOffset, nextTxValue); + + iTxData += iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed + // (iWordSize=1), but if operating in 16-bit mode a->iTxData would be incremented + // by the number of bytes specified in a->iWordSize + } + // May wish to enable transmission now - or wait until after the Read transfer has been initialised + break; + } + case TIicBusTransfer::EMasterRead: + { + // If using an Rx FIFO, it will already have been drained at the end of the last transfer by TransferEndDfc + // so no need to do it again here. + + // May wish to enable reception now - or group with the code, below + break; + } + default: + { + __KTRACE_OPT(KIIC, Kern::Printf("Unsupported TransactionType %x", aType)); + r = KErrArgument; + break; + } + } + + // Final stages of hardware preparation + // + // Enable hardware interrupts + // Finally, enable (start) transmission and reception + } + + return r; + } + + +// This method performs the initialisation required for either a read or write transfer +// and then invokes the next stage of the processing (DoTransfer) +// +TInt DIicBusChannelMasterPsl::StartTransfer(TIicBusTransfer* aTransferPtr, TUint8 aType) + { + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::StartTransfer() - aTransferPtr=0x%x, aType=%d",aTransferPtr,aType)); + + if(aTransferPtr == NULL) + { + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::StartTransfer - NULL pointer\n")); + return KErrArgument; + } + + TInt r = KErrNone; + + switch(aType) + { + case TIicBusTransfer::EMasterWrite: + { + __KTRACE_OPT(KIIC, Kern::Printf("Starting EMasterWrite, duplex=%d", iFullDTransfer)); + + // Get a pointer to the transfer object's buffer, to facilitate passing arguments to DoTransfer + const TDes8* aBufPtr = GetTferBuffer(aTransferPtr); + + __KTRACE_OPT(KIIC, Kern::Printf("Length %d, iWordSize %d", aBufPtr->Length(), iWordSize)); + + // Store the current address and ending address for Transmission - they are required by the ISR and DFC + iTxData = (TInt8*) aBufPtr->Ptr(); + iTxDataEnd = (TInt8*) (iTxData + aBufPtr->Length()); + + __KTRACE_OPT(KIIC, Kern::Printf("Tx: Start: %x, End %x, bytes %d\n\n", iTxData, iTxDataEnd, aBufPtr->Length())); + + // Set the flag to indicate that we'll be transmitting data + iOperation.iOp.iIsTransmitting = ETrue; + + // initiate the transmission.. + r = DoTransfer((TInt8 *) aBufPtr->Ptr(), aBufPtr->Length(), aType); + if(r != KErrNone) + { + __KTRACE_OPT(KIIC, Kern::Printf("Starting Write failed, r = %d", r)); + } + break; + } + + case TIicBusTransfer::EMasterRead: + { + __KTRACE_OPT(KIIC, Kern::Printf("Starting EMasterRead, duplex=%x", iFullDTransfer)); + + // Get a pointer to the transfer object's buffer, to facilitate passing arguments to DoTransfer + const TDes8* aBufPtr = GetTferBuffer(aTransferPtr); + + // Store the current address and ending address for Reception - they are required by the ISR and DFC + iRxData = (TInt8*) aBufPtr->Ptr(); + iRxDataEnd = (TInt8*) (iRxData + aBufPtr->Length()); + + __KTRACE_OPT(KIIC, Kern::Printf("Rx: Start: %x, End %x, bytes %d", iRxData, iRxDataEnd, aBufPtr->Length())); + + // Set the flag to indicate that we'll be receiving data + iOperation.iOp.iIsReceiving = ETrue; + + // initiate the reception + r = DoTransfer((TInt8 *) aBufPtr->Ptr(), aBufPtr->Length(), aType); + if(r != KErrNone) + { + __KTRACE_OPT(KIIC, Kern::Printf("Starting Read failed, r = %d", r)); + } + break; + } + + default: + { + __KTRACE_OPT(KIIC, Kern::Printf("Unsupported TransactionType %x", aType)); + r = KErrArgument; + break; + } + } + + return r; + } + +// This method determines the next transfers to be processed, and passes them to the next stage +// in the processing (StartTransfer). +// +// This is called from DoRequest (for the first transfer) and TransferEndDfc (after a transfer +// has completed) +// +TInt DIicBusChannelMasterPsl::ProcessNextTransfers() + { + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::ProcessNextTransfers(),BUSY=%d", iState)); + + // Since new transfers are strating, clear exisiting flags + iOperation.iValue = TIicOperationType::ENop; + + // Some hardware preparation may be required before starting the transfer using something similar + // to the following line: + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit); + + // If this is the first transfer in the transaction the channel will be in state EIdle + if(iState == EIdle) + { + // Get the pointer to half-duplex transfer object.. + iHalfDTransfer = GetTransHalfDuplexTferPtr(iCurrTransaction); + + // Get the pointer to full-duplex transfer object.. + iFullDTransfer = GetTransFullDuplexTferPtr(iCurrTransaction); + + // Update the channel state to EBusy and initialise the transaction status + iState = EBusy; + iTransactionStatus = KErrNone; + + // Use the PIL funcitonality to start a timer that will timeout if the transaction + // is not completed within a specified time period (the client may have specified a period + // to use in the transaction header - some something similar tot he following could be used) + // StartSlaveTimeOutTimer(iCurrHeader->iTimeoutPeriod); + // + // If the timer expires, callback function HandleSlaveTimeout (implemented by the PSL, above) + // will be called. This will ensure that the hardware ceases transfer activity, and calls ExitComplete + // with KErrTImedOut, which will return the channel state to EIdle. + } + else + // If not in state EIdle, process the next transfer in the linked-list held by the transaction + { + // Get the pointer the next half-duplex transfer object.. + iHalfDTransfer = GetTferNextTfer(iHalfDTransfer); + + // Get the pointer to the next half-duplex transfer object.. + if(iFullDTransfer) + { + iFullDTransfer = GetTferNextTfer(iFullDTransfer); + } + } + + TInt r = KErrNone; + if(!iFullDTransfer && !iHalfDTransfer) + { + // If all of the transfers were completed, just notify the PIL and return. + // (if either Rx or Tx has not finished properly ExitComplete() would have been called + // from TransferEndDfc if there was an error during the transfer) + __KTRACE_OPT(KIIC, Kern::Printf("All transfers completed successfully")); + + ExitComplete(KErrNone); + } + else + { + // Transfers remain to be processed + // + // For full-duplex transfers, the order in which read and write transfers are started may be significant. + // Below is an example where the read transfer is explicitly started before the write transfer. + TInt8 hDTrType = (TInt8) GetTferType(iHalfDTransfer); + + if(iFullDTransfer) + { + if(hDTrType == TIicBusTransfer::EMasterRead) + { + r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterRead); + if(r != KErrNone) + { + return r; + } + r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterWrite); + } + else // hDTrType == TIicBusTransfer::EMasterWrite) + { + r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterRead); + if(r != KErrNone) + { + return r; + } + r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterWrite); + } + } + else + // This is a HalfDuplex transfer - so just start it + { + r = StartTransfer(iHalfDTransfer, hDTrType); + } + } + return r; + } + +// The gateway function for PSL implementation +// +// This method is called by the PIL to initiate the transaction. After finishing it's processing, +// the PSL calls the PIL function CompleteRequest to indicate the success (or otherwise) of the request +// +TInt DIicBusChannelMasterPsl::DoRequest(TIicBusTransaction* aTransaction) + { + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::DoRequest (aTransaction=0x%x)\n", aTransaction)); + + // If the pointer to the transaction passed in as a parameter, or its associated pointer to the + // header information is NULL, return KErrArgument + if(!aTransaction || !GetTransactionHeader(aTransaction)) + { + return KErrArgument; + } + + // The PSL operates a simple state machine to ensure that only one transaction is processed + // at a time - if the channel is currently busy, reject the request with KErrInUse. + if(iState != EIdle) + { + return KErrInUse; + } + + // Make a copy of the pointer to the transaction + iCurrTransaction = aTransaction; + + // Configure the hardware to support the transaction + TInt r = KErrNone; + if(TransConfigDiffersFromPrev()) // Optional: check if hardware needs reconfiguration + { + r = ConfigureInterface(); + if(r != KErrNone) + { + return r; + } + } + + // start processing transfers of this transaction. + r = ProcessNextTransfers(); + return r; + } +