author | mikek |
Sun, 27 Jun 2010 21:43:55 +0100 | |
branch | GCC_SURGE |
changeset 181 | bd8f1e65581b |
parent 0 | a41df078684a |
permissions | -rw-r--r-- |
/* * 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 <drivers/iic.h> // #include <gpio.h> // 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<KMaxName> 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; }