1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/kernelhwsrv/bsptemplate/asspandvariant/template_assp/iic/iic_master.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,789 @@
1.4 +/*
1.5 +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
1.6 +* All rights reserved.
1.7 +* This component and the accompanying materials are made available
1.8 +* under the terms of the License "Eclipse Public License v1.0"
1.9 +* which accompanies this distribution, and is available
1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.11 +*
1.12 +* Initial Contributors:
1.13 +* Nokia Corporation - initial contribution.
1.14 +*
1.15 +* Contributors:
1.16 +*
1.17 +* Description:
1.18 +*
1.19 +*/
1.20 +
1.21 +
1.22 +#include <drivers/iic.h>
1.23 +// #include <gpio.h> // Include if using GPIO functionality
1.24 +#include "iic_psl.h"
1.25 +#include "iic_master.h"
1.26 +
1.27 +
1.28 +// Method called when transmission activity is ended. The method is used to indicate
1.29 +// the success or failure reported in the first parameter
1.30 +//
1.31 +// All timers are cancelled, relevant interrupts are disabled and the transaction
1.32 +// request is completed by calling the relevant PIL method.
1.33 +//
1.34 +void DIicBusChannelMasterPsl::ExitComplete(TInt aErr, TBool aComplete /*= ETrue*/)
1.35 + {
1.36 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::ExitComplete, aErr %d, aComplete %d", aErr, aComplete));
1.37 +
1.38 + // Disable interrupts for the channel
1.39 + // with something similar to the following lines:
1.40 + // Interrupt::Disable(iRxInterruptId);
1.41 + // Interrupt::Disable(iTxInterruptId);
1.42 +
1.43 + // Cancel timers and DFCs..
1.44 + CancelTimeOut();
1.45 + iHwGuardTimer.Cancel();
1.46 + iTransferEndDfc.Cancel();
1.47 +
1.48 + // Change the channel state to EIdle so that subsequent transaction requests can be accepted
1.49 + // once the current one has been completed
1.50 + iState = EIdle;
1.51 +
1.52 + // Call the PIL method to complete the request
1.53 + if(aComplete)
1.54 + {
1.55 + CompleteRequest(aErr);
1.56 + }
1.57 + }
1.58 +
1.59 +// Callback function for the iHwGuardTimer timer.
1.60 +//
1.61 +// Called in ISR context if the iHwGuardTimer expires. Sets iTransactionStatus to KErrTimedOut
1.62 +//
1.63 +void DIicBusChannelMasterPsl::TimeoutCallback(TAny* aPtr)
1.64 + {
1.65 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TimeoutCallback"));
1.66 + DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr;
1.67 + a->iTransactionStatus = KErrTimedOut;
1.68 + }
1.69 +
1.70 +// HandleSlaveTimeout
1.71 +//
1.72 +// This method is called by the PIL in the case of expiry of a timer started by the PSL. It is
1.73 +// specificaly intended to guard against the Slave not responding within an expected time
1.74 +//
1.75 +// The PIL method StartSlaveTimeoutTimer is available for the PSL to start the timer (this is
1.76 +// called from ProcessNextTransfers, below).
1.77 +// The PIL method CancelTimeOut is available for the PSL to cancel the same timer (this is called
1.78 +// from ExitComplete, above)
1.79 +//
1.80 +// The PIL will call CompleteRequest() after this function returns, so the PSL needs only to clean-up
1.81 +//
1.82 +TInt DIicBusChannelMasterPsl::HandleSlaveTimeout()
1.83 + {
1.84 + __KTRACE_OPT(KIIC, Kern::Printf("HandleSlaveTimeout"));
1.85 +
1.86 + // Ensure that the hardware has ceased transfers, with something similar to the following line:
1.87 + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusDisableBit);
1.88 + // GPIO::SetPinMode(aPinId, GPIO::EDisabled);
1.89 +
1.90 + // Stop the PSL's operation, and inform the PIL of the timeout
1.91 + ExitComplete(KErrTimedOut, EFalse);
1.92 +
1.93 + // Perform any further hardware manipulation necessary
1.94 + //
1.95 +
1.96 + return KErrTimedOut;
1.97 + }
1.98 +
1.99 +
1.100 +// DFC
1.101 +//
1.102 +// For execution when a Rx buffer has been filled or a Tx buffer has been emptied
1.103 +//
1.104 +void DIicBusChannelMasterPsl::TransferEndDfc(TAny* aPtr)
1.105 + {
1.106 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TransferEndDfc"));
1.107 + DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr;
1.108 +
1.109 + // Start of optional processing - not necessary for all implementations
1.110 + //
1.111 + // When operating full-duplex transfers, one of the Rx and Tx operations may have caused an interrupt
1.112 + // before the other has completed. For the example here, the Tx is assumed to have completed before the Rx
1.113 + // and a timer is used to ensure that the outstanding Rx operation completes within an expected time.
1.114 + //
1.115 + // If there has been no error so far, may want to check if we are still receiving the data
1.116 + if(a->iTransactionStatus == KErrNone)
1.117 + {
1.118 + // Use an active wait since this is likely to be a brief check.
1.119 + // Start the guard timer (which will update iTransactionStatus with KErrTimedOut if it expires)
1.120 + // while also polling the hardware to check if transmission has ceased.
1.121 + //
1.122 + // Polling the hardware would be something like the line below
1.123 + // (AsspRegister::Read32(a->aRegisterSetBaseAddress + KStatusRegisterOffset) & KTransmissionActive));
1.124 + // but for the template port will use a dummy condition (value of 1)
1.125 + //
1.126 + a->iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
1.127 + while((a->iTransactionStatus == KErrNone) && 1); // Replace 1 with a register read
1.128 + }
1.129 + //
1.130 + // Now the timer has expired, deactivate the slave select until the current state has been processed, but only
1.131 + // if this is not an extended transaction, in which case we want to leave the bus alone so that the multiple
1.132 + // transactions making up the extended transaction become one big transaction as far as the bus is concerned.
1.133 + // Do this with something similar to the following line:
1.134 + // if (!(a->iCurrTransaction->Flags() & KTransactionWithMultiTransc))
1.135 + // {
1.136 + // GPIO::SetPinMode(aPinId, GPIO::EDisabled);
1.137 + // }
1.138 + //
1.139 + // Disable the hardware from further transmissions
1.140 + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusDisableBit);
1.141 + //
1.142 + // Check if the guard timer expired
1.143 + if(a->iTransactionStatus != KErrNone)
1.144 + {
1.145 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TransferEndDfc(): error %d",a->iTransactionStatus));
1.146 + a->ExitComplete(a->iTransactionStatus); // report the error
1.147 + return;
1.148 + }
1.149 + else
1.150 + {
1.151 + // Transfer completed successfully, so just cancel the guard timer
1.152 + a->iHwGuardTimer.Cancel();
1.153 + }
1.154 + // End of optional processing - not necessary for all implementations
1.155 +
1.156 + // At this point, prepare for subsequent transfers by performing any necessary clean-up.
1.157 + // As stated above, for this example, it is assumed that any Rx or Tx transfer has completed -
1.158 + // the following just checks if an Rx, Tx operation was started, and assumes that they completed.
1.159 +
1.160 + if(a->iOperation.iOp.iIsReceiving)
1.161 + {
1.162 + // If the channel has been receiving data, may need to ensure that any FIFO used has been drained
1.163 + // The example here checks if one extra data item remains in the FIFO
1.164 + // To check if data remains in a FIFO, something similar to the following could be used:
1.165 + // TInt8 dataRemains = AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxFifoLevel);
1.166 + // Reading data from the FIFO would be achieved with something like the line below
1.167 + // value = AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxData);
1.168 + // but for the template port will just use a dummy values (data remains=1, value = 1)
1.169 + //
1.170 + TInt8 dataRemains = 1; // Would be a check of a Rx FIFO level
1.171 +
1.172 + // If data remains in the Rx FIFO and the Rx buffer is not full, copy the data to the buffer
1.173 + if(dataRemains && (a->iRxDataEnd - a->iRxData >= a->iWordSize) )
1.174 + {
1.175 + TUint8 value = 1; // Would be a read of the Rx FIFO data
1.176 + *a->iRxData = value; // For this example, assumes one byte of data has been read from the FIFO
1.177 + // but if operating in 16-bit mode, two "values" would be written the buffer
1.178 + a->iRxData += a->iWordSize; // In this example, 8-bit mode is assumed (iWordSize=1)
1.179 + // but if operating in 16-bit mode a->iRxData would be incremented by
1.180 + // the number of bytes specified in a->iWordSize
1.181 + }
1.182 + }
1.183 +
1.184 + if(a->iOperation.iOp.iIsTransmitting)
1.185 + {
1.186 + // If the channel has been transmitting data, may need to ensure that any FIFO used has been flushed
1.187 + // To check if data remains in a FIFO, something similar to the following could be used:
1.188 + // TInt8 dataRemains = AsspRegister::Read32(a->aRegisterSetBaseAddress + KTxFifoLevel);
1.189 + // The means to flush the FIFO will be platform specific, and so no example is given here.
1.190 + }
1.191 +
1.192 + // Start the next transfer for this transaction, if any remain
1.193 + if(a->iState == EBusy)
1.194 + {
1.195 + TInt err = a->ProcessNextTransfers();
1.196 + if(err != KErrNone)
1.197 + {
1.198 + // If the next transfer could not be started, complete the transaction with
1.199 + // the returned error code
1.200 + a->ExitComplete(err);
1.201 + }
1.202 + }
1.203 + }
1.204 +
1.205 +
1.206 +// ISR Handler
1.207 +//
1.208 +// If the channel is to be event driven, it will use interrupts that indicate the
1.209 +// hardware has received or transmitted. To support this an ISR is required.
1.210 +//
1.211 +void DIicBusChannelMasterPsl::IicIsr(TAny* aPtr)
1.212 + {
1.213 + DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr;
1.214 +
1.215 + // The processing for Rx and Tx will differ, so must determine the status of the interrupts.
1.216 + // This will be PSL-specific, but is likely to achieved by reading a status register, in a
1.217 + // way similar to this:
1.218 + // TUint32 status = AsspRegister::Read32(aRegisterSetBaseAddress + KStatusRegisterOffset);
1.219 + //
1.220 + // For the purposes of compiling the template port, just initialise status to zero.
1.221 + TUint32 status = 0;
1.222 +
1.223 + if(status & KIicPslTxInterrupt)
1.224 + {
1.225 + // Tx interrupt processing
1.226 +
1.227 + // Clear the interrupt source, with something similar to the following line:
1.228 + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KStatusRegisterOffset, KIicPslTxInterrupt);
1.229 +
1.230 + // Then check whether all the required data has been transmitted.
1.231 + if(a->iTxData == a->iTxDataEnd)
1.232 + {
1.233 + // All data sent, so disable the Tx interrupt and queue a DFC to handle the next steps.
1.234 + // Interrupt::Disable(a->iTxInterruptId);
1.235 + a->iTransferEndDfc.Add();
1.236 + }
1.237 + else
1.238 + {
1.239 + if(a->iOperation.iOp.iIsTransmitting)
1.240 + {
1.241 + // Data remaining - copy the next value to send to the Tx register
1.242 +
1.243 + // TUint8 nextTxValue = *a->iTxData; // For this example, assumes one byte of data is to be transmitted
1.244 + // but if operating in 16-bit mode, bytes may need arranging for
1.245 + // endianness
1.246 +
1.247 + // Write to the Tx register with something similar to the following:
1.248 + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KTxRegisterOffset, nextTxValue);
1.249 +
1.250 + a->iTxData += a->iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed
1.251 + // (iWordSize=1), but if operating in 16-bit mode a->iTxData would be incremented
1.252 + // by the number of bytes specified in a->iWordSize
1.253 + }
1.254 + }
1.255 + }
1.256 +
1.257 + if(status & KIicPslRxInterrupt)
1.258 + {
1.259 + // Rx interrupt processing
1.260 +
1.261 + // Copy the received data to the Rx buffer.
1.262 + // Do this in a loop in case there are more than one units of data to be handled. Data availability
1.263 + // will be indicated by a PSL-specific register, and so may be handled by code similar to the following:
1.264 + // while(AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxData))
1.265 + //
1.266 + // But, to allow compilation of the template port, just use a dummy condition (while(1)):
1.267 + while(1)
1.268 + {
1.269 + // While there is space in the buffer, copy received data to it
1.270 + if((a->iRxDataEnd - a->iRxData) >= a->iWordSize)
1.271 + {
1.272 + TUint8 nextRxValue = 0;
1.273 + // Read from the Rx register with something similar to the following:
1.274 + // TUint8 nextRxValue = AsspRegister::Read32(aRegisterSetBaseAddress + KRxRegisterOffset);
1.275 + *a->iRxData = nextRxValue;
1.276 + }
1.277 + else
1.278 + {
1.279 + // If there is no space left in the buffer an Overrun has occurred
1.280 + a->iTransactionStatus = KErrOverflow;
1.281 + break;
1.282 + }
1.283 +
1.284 + // Increment the pointer to the received data
1.285 + a->iRxData += a->iWordSize;
1.286 + }
1.287 +
1.288 + // If the Rx buffer is now full, finish the transmission.
1.289 + if(a->iRxDataEnd == a->iRxData)
1.290 + {
1.291 + // Disable the interrupt since it is no longer required
1.292 + // Interrupt::Disable(a->iRxInterruptId);
1.293 +
1.294 + // Then queue a DFC to perform the next steps
1.295 + a->iTransferEndDfc.Add();
1.296 + }
1.297 +
1.298 + // After processing the data, clear the interrupt source (do it last to prevent the ISR being
1.299 + // re-invoked before this ISR is finished), with something similar to the following line:
1.300 + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KStatusRegisterOffset, KIicPslRxInterrupt);
1.301 + }
1.302 + }
1.303 +
1.304 +// Constructor, first stage
1.305 +//
1.306 +// The PSL is responsible for setting the channel number - this is passed as the first parameter to
1.307 +// this overload of the base class constructor
1.308 +//
1.309 +DIicBusChannelMasterPsl::DIicBusChannelMasterPsl(TInt aChannelNumber, TBusType aBusType, TChannelDuplex aChanDuplex) :
1.310 + DIicBusChannelMaster(aBusType, aChanDuplex), // Base class constructor
1.311 + iTransferEndDfc(TransferEndDfc, this, KIicPslDfcPriority), // DFC to handle transfer completion
1.312 + iHwGuardTimer(TimeoutCallback, this) // Timer to guard against hardware timeout
1.313 + {
1.314 + iChannelNumber = aChannelNumber; // Set the iChannelNumber of the Base Class
1.315 + iState = EIdle; // Initialise channel state machine
1.316 +
1.317 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::DIicBusChannelMasterPsl: iChannelNumber = %d", iChannelNumber));
1.318 + }
1.319 +
1.320 +// Second stage construction
1.321 +//
1.322 +// Allocate and initialise objects required by the PSL channel implementation
1.323 +//
1.324 +TInt DIicBusChannelMasterPsl::DoCreate()
1.325 + {
1.326 + __KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelMasterPsl::DoCreate() ch: %d \n", iChannelNumber));
1.327 +
1.328 + TInt r = KErrNone;
1.329 +
1.330 + // Interrupt IDs (such as iRxInterruptId, iTxInterruptId, used in this file)would be initialised here.
1.331 + //
1.332 + // Also, information relevant to the channel number (such as the base register address,
1.333 + // aRegisterSetBaseAddress) would be initialised here.
1.334 +
1.335 + // Create the DFCQ to be used by the channel
1.336 + if(!iDfcQ)
1.337 + {
1.338 + TBuf8<KMaxName> threadName (KIicPslThreadName);
1.339 + threadName.AppendNum(iChannelNumber); // Optional: append the channel number to the name
1.340 + r = Kern::DfcQCreate(iDfcQ, KIicPslThreadPriority, &threadName);
1.341 + if(r != KErrNone)
1.342 + {
1.343 + __KTRACE_OPT(KIIC, Kern::Printf("DFC Queue creation failed, channel number: %d, r = %d\n", iChannelNumber, r));
1.344 + return r;
1.345 + }
1.346 + }
1.347 +
1.348 + // PIL Base class initialization - this must be called prior to SetDfcQ(iDfcQ)
1.349 + r = Init();
1.350 + if(r == KErrNone)
1.351 + {
1.352 + // Call base class function to set DFCQ pointers in the required objects
1.353 + // This also enables the channel to process transaction requests
1.354 + SetDfcQ(iDfcQ);
1.355 +
1.356 + // PSL DFCQ initialisation for local DFC
1.357 + iTransferEndDfc.SetDfcQ(iDfcQ);
1.358 +
1.359 +#ifdef CPU_AFFINITY_ANY
1.360 + NKern::ThreadSetCpuAffinity((NThread*)(iDfcQ->iThread), KCpuAffinityAny);
1.361 +#endif
1.362 + // Bind interrupts.
1.363 + // This would be with something similar to the following lines:
1.364 + // iMasterIntId = Interrupt::Bind(interruptIdToUse, IicPslIsr, this);
1.365 + //
1.366 + // Interrupt::Bind returns interruptId or an error code (negative value)
1.367 + if(iMasterIntId < KErrNone)
1.368 + {
1.369 + __KTRACE_OPT(KIIC, Kern::Printf("ERROR: InterruptBind error.. %d", r));
1.370 + r = iMasterIntId;
1.371 + }
1.372 + }
1.373 + return r;
1.374 + }
1.375 +
1.376 +// New
1.377 +//
1.378 +// A static method used to construct the DIicBusChannelMasterPsl object.
1.379 +DIicBusChannelMasterPsl* DIicBusChannelMasterPsl::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex)
1.380 + {
1.381 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::NewL(): ChannelNumber = %d, BusType =%d", aChannelNumber, aBusType));
1.382 + DIicBusChannelMasterPsl *pChan = new DIicBusChannelMasterPsl(aChannelNumber, aBusType, aChanDuplex);
1.383 +
1.384 + TInt r = KErrNoMemory;
1.385 + if(pChan)
1.386 + {
1.387 + r = pChan->DoCreate();
1.388 + }
1.389 + if(r != KErrNone)
1.390 + {
1.391 + delete pChan;
1.392 + pChan = NULL;
1.393 + }
1.394 + return pChan;
1.395 + }
1.396 +
1.397 +// Optional method - that determines if the previous transaction header is different to the current one.
1.398 +// If configuration is the same, the hardware may not need to be re-initialized.
1.399 +TBool DIicBusChannelMasterPsl::TransConfigDiffersFromPrev()
1.400 + {
1.401 + //
1.402 + // The header will be specific to a particular bus type. Using a fictional
1.403 + // bus type Abc, code similar to the following could be used to compare each
1.404 + // member of the previous header with the current one
1.405 + //
1.406 + // TConfigAbcBufV01* oldHdrBuf = (TConfigAbcBufV01*) iPrevHeader;
1.407 + // TConfigAbcV01 &oldHeader = (*oldHdrBuf)();
1.408 + // TConfigAbcBufV01* newHdrBuf = (TConfigAbcBufV01*) iCurrHeader;
1.409 + // TConfigAbcV01 &newHeader = (*newHdrBuf)();
1.410 + // if( (newHeader.iHeaderMember != oldHeader.iHeaderMember) ||
1.411 + // ... )
1.412 + // {
1.413 + // return EFalse;
1.414 + // }
1.415 + return ETrue;
1.416 + }
1.417 +
1.418 +
1.419 +// CheckHdr is called by the PIL when a transaction is queued, in function
1.420 +// QueueTransaction. This is done in the context of the Client's thread.
1.421 +//
1.422 +// The PSL is required to check that the transaction header is valid for
1.423 +// this channel.
1.424 +//
1.425 +// If the pointer to the header is NULL, return KErrArgument.
1.426 +// If the content of the header is not valid for this channel, return KErrNotSupported.
1.427 +//
1.428 +TInt DIicBusChannelMasterPsl::CheckHdr(TDes8* aHdrBuff)
1.429 + {
1.430 + TInt r = KErrNone;
1.431 +
1.432 + if(!aHdrBuff)
1.433 + {
1.434 + r = KErrArgument;
1.435 + }
1.436 + else
1.437 + {
1.438 + // Check that the contents of the header are valid
1.439 + //
1.440 + // The header will be specific to a particular bus type. Using a fictional
1.441 + // bus type Abc,code similar to the following could be used to validate each
1.442 + // member of the header:
1.443 + //
1.444 + // TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) aHdrBuff;
1.445 + // TConfigAbcV01 &abcHeader = (*headerBuf)();
1.446 + // if( (abcHeader.iHeaderMember < ESomeMinValue) ||
1.447 + // (abcHeader.iHeaderMember > ESomeMaxValue))
1.448 + // {
1.449 + // __KTRACE_OPT(KIIC, Kern::Printf("iHeaderMember %d not supported",abcHeader.iHeaderMember));
1.450 + // r = KErrNotSupported;
1.451 + // }
1.452 + }
1.453 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::CheckHdr() r %d", r));
1.454 + return r;
1.455 + }
1.456 +
1.457 +// Initialise the hardware with the data provided in the transaction and slave-address field
1.458 +//
1.459 +// If a specified configuration is not supported, return KErrNotSupported
1.460 +// If a configuration is supported, but the implementing configuration fails, return KErrGeneral
1.461 +//
1.462 +TInt DIicBusChannelMasterPsl::ConfigureInterface()
1.463 + {
1.464 + __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()"));
1.465 +
1.466 + // This method will be platform-specific and will configure the hardware as required to support
1.467 + // the current transacation. This will be supported with something similar to the following:
1.468 + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit);
1.469 + // GPIO::SetPinMode(aPinId, GPIO::EEnabled);
1.470 +
1.471 + // Depending on the platform, timers (such as iHwGuardTimer) may be used to check that the hardware
1.472 + // responds in the required way within an allowed timeout. Since this is configuring the channel for
1.473 + // an operation, it is acceptable to perform an active wait, with something similar to the following:
1.474 + // iTransactionStatus = KErrNone;
1.475 + // iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue));
1.476 + // while((iTransactionStatus == KErrNone) &&
1.477 + // AsspRegister::Read32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit);
1.478 + // if(iTransactionStatus != KErrNone)
1.479 + // {
1.480 + // return KErrGeneral;
1.481 + // }
1.482 + // else
1.483 + // {
1.484 + // iHwGuardTimer.Cancel();
1.485 + // }
1.486 + return KErrNone;
1.487 + }
1.488 +
1.489 +
1.490 +// Method called by StartTransfer to actually initiate the transfers. It manipulates the hardware to
1.491 +// perform the required tasks.
1.492 +//
1.493 +TInt DIicBusChannelMasterPsl::DoTransfer(TInt8 *aBuff, TUint aNumOfBytes, TUint8 aType)
1.494 + {
1.495 + __KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelMasterPsl::DoTransfer() - aBuff=0x%x, aNumOfBytes=0x%x\n",aBuff,aNumOfBytes));
1.496 +
1.497 + TInt r = KErrNone;
1.498 +
1.499 + // Validate the input arguments
1.500 + if((aBuff == NULL) || (aNumOfBytes == 0))
1.501 + {
1.502 + r = KErrArgument;
1.503 + }
1.504 + else
1.505 + {
1.506 + // This method will be platform-specific and will configure the hardware as required
1.507 + // This will likely be supported with calls similar to the following:
1.508 + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit);
1.509 + // GPIO::SetPinMode(aPinId, GPIO::EEnabled);
1.510 + // Interrupt::Enable(aInterruptId);
1.511 + //
1.512 + // Steps that may be typically required are described below
1.513 +
1.514 + switch(aType)
1.515 + {
1.516 + case TIicBusTransfer::EMasterWrite:
1.517 + {
1.518 + // If using a Tx FIFO, may wish to disable transmission until the FIFO has been filled to certain level
1.519 + // If using a Tx FIFO, may wish to flush it and re-initialise any counters or pointers used
1.520 +
1.521 + // If using a FIFO, copy data to it until either the FIFO is full or all data has been copied
1.522 + // Checking the FIFO is full will be reading a flag in a status register, by use of code similar the following
1.523 + // TUint32 status = AsspRegister::Read32(aRegisterSetBaseAddress + KStatusRegisterOffset);
1.524 + // if(status & KTxFifoFullBitMask)
1.525 + // ... FIFO is full
1.526 + //
1.527 + // For this example base port, represent the FIFO full status by a dummy value (zero).
1.528 + TUint8 txFifoFull = 0;
1.529 + while(!txFifoFull && (iTxData != iTxDataEnd))
1.530 + {
1.531 + // TUint8 nextTxValue = *iTxData; // For this example, assumes one byte of data is to be transmitted
1.532 + // but if operating in 16-bit mode, bytes may need arranging for
1.533 + // endianness
1.534 +
1.535 + // Write to the Tx FIFO register with something similar to the following:
1.536 + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KTxRegisterOffset, nextTxValue);
1.537 +
1.538 + iTxData += iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed
1.539 + // (iWordSize=1), but if operating in 16-bit mode a->iTxData would be incremented
1.540 + // by the number of bytes specified in a->iWordSize
1.541 + }
1.542 + // May wish to enable transmission now - or wait until after the Read transfer has been initialised
1.543 + break;
1.544 + }
1.545 + case TIicBusTransfer::EMasterRead:
1.546 + {
1.547 + // If using an Rx FIFO, it will already have been drained at the end of the last transfer by TransferEndDfc
1.548 + // so no need to do it again here.
1.549 +
1.550 + // May wish to enable reception now - or group with the code, below
1.551 + break;
1.552 + }
1.553 + default:
1.554 + {
1.555 + __KTRACE_OPT(KIIC, Kern::Printf("Unsupported TransactionType %x", aType));
1.556 + r = KErrArgument;
1.557 + break;
1.558 + }
1.559 + }
1.560 +
1.561 + // Final stages of hardware preparation
1.562 + //
1.563 + // Enable hardware interrupts
1.564 + // Finally, enable (start) transmission and reception
1.565 + }
1.566 +
1.567 + return r;
1.568 + }
1.569 +
1.570 +
1.571 +// This method performs the initialisation required for either a read or write transfer
1.572 +// and then invokes the next stage of the processing (DoTransfer)
1.573 +//
1.574 +TInt DIicBusChannelMasterPsl::StartTransfer(TIicBusTransfer* aTransferPtr, TUint8 aType)
1.575 + {
1.576 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::StartTransfer() - aTransferPtr=0x%x, aType=%d",aTransferPtr,aType));
1.577 +
1.578 + if(aTransferPtr == NULL)
1.579 + {
1.580 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::StartTransfer - NULL pointer\n"));
1.581 + return KErrArgument;
1.582 + }
1.583 +
1.584 + TInt r = KErrNone;
1.585 +
1.586 + switch(aType)
1.587 + {
1.588 + case TIicBusTransfer::EMasterWrite:
1.589 + {
1.590 + __KTRACE_OPT(KIIC, Kern::Printf("Starting EMasterWrite, duplex=%d", iFullDTransfer));
1.591 +
1.592 + // Get a pointer to the transfer object's buffer, to facilitate passing arguments to DoTransfer
1.593 + const TDes8* aBufPtr = GetTferBuffer(aTransferPtr);
1.594 +
1.595 + __KTRACE_OPT(KIIC, Kern::Printf("Length %d, iWordSize %d", aBufPtr->Length(), iWordSize));
1.596 +
1.597 + // Store the current address and ending address for Transmission - they are required by the ISR and DFC
1.598 + iTxData = (TInt8*) aBufPtr->Ptr();
1.599 + iTxDataEnd = (TInt8*) (iTxData + aBufPtr->Length());
1.600 +
1.601 + __KTRACE_OPT(KIIC, Kern::Printf("Tx: Start: %x, End %x, bytes %d\n\n", iTxData, iTxDataEnd, aBufPtr->Length()));
1.602 +
1.603 + // Set the flag to indicate that we'll be transmitting data
1.604 + iOperation.iOp.iIsTransmitting = ETrue;
1.605 +
1.606 + // initiate the transmission..
1.607 + r = DoTransfer((TInt8 *) aBufPtr->Ptr(), aBufPtr->Length(), aType);
1.608 + if(r != KErrNone)
1.609 + {
1.610 + __KTRACE_OPT(KIIC, Kern::Printf("Starting Write failed, r = %d", r));
1.611 + }
1.612 + break;
1.613 + }
1.614 +
1.615 + case TIicBusTransfer::EMasterRead:
1.616 + {
1.617 + __KTRACE_OPT(KIIC, Kern::Printf("Starting EMasterRead, duplex=%x", iFullDTransfer));
1.618 +
1.619 + // Get a pointer to the transfer object's buffer, to facilitate passing arguments to DoTransfer
1.620 + const TDes8* aBufPtr = GetTferBuffer(aTransferPtr);
1.621 +
1.622 + // Store the current address and ending address for Reception - they are required by the ISR and DFC
1.623 + iRxData = (TInt8*) aBufPtr->Ptr();
1.624 + iRxDataEnd = (TInt8*) (iRxData + aBufPtr->Length());
1.625 +
1.626 + __KTRACE_OPT(KIIC, Kern::Printf("Rx: Start: %x, End %x, bytes %d", iRxData, iRxDataEnd, aBufPtr->Length()));
1.627 +
1.628 + // Set the flag to indicate that we'll be receiving data
1.629 + iOperation.iOp.iIsReceiving = ETrue;
1.630 +
1.631 + // initiate the reception
1.632 + r = DoTransfer((TInt8 *) aBufPtr->Ptr(), aBufPtr->Length(), aType);
1.633 + if(r != KErrNone)
1.634 + {
1.635 + __KTRACE_OPT(KIIC, Kern::Printf("Starting Read failed, r = %d", r));
1.636 + }
1.637 + break;
1.638 + }
1.639 +
1.640 + default:
1.641 + {
1.642 + __KTRACE_OPT(KIIC, Kern::Printf("Unsupported TransactionType %x", aType));
1.643 + r = KErrArgument;
1.644 + break;
1.645 + }
1.646 + }
1.647 +
1.648 + return r;
1.649 + }
1.650 +
1.651 +// This method determines the next transfers to be processed, and passes them to the next stage
1.652 +// in the processing (StartTransfer).
1.653 +//
1.654 +// This is called from DoRequest (for the first transfer) and TransferEndDfc (after a transfer
1.655 +// has completed)
1.656 +//
1.657 +TInt DIicBusChannelMasterPsl::ProcessNextTransfers()
1.658 + {
1.659 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::ProcessNextTransfers(),BUSY=%d", iState));
1.660 +
1.661 + // Since new transfers are strating, clear exisiting flags
1.662 + iOperation.iValue = TIicOperationType::ENop;
1.663 +
1.664 + // Some hardware preparation may be required before starting the transfer using something similar
1.665 + // to the following line:
1.666 + // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit);
1.667 +
1.668 + // If this is the first transfer in the transaction the channel will be in state EIdle
1.669 + if(iState == EIdle)
1.670 + {
1.671 + // Get the pointer to half-duplex transfer object..
1.672 + iHalfDTransfer = GetTransHalfDuplexTferPtr(iCurrTransaction);
1.673 +
1.674 + // Get the pointer to full-duplex transfer object..
1.675 + iFullDTransfer = GetTransFullDuplexTferPtr(iCurrTransaction);
1.676 +
1.677 + // Update the channel state to EBusy and initialise the transaction status
1.678 + iState = EBusy;
1.679 + iTransactionStatus = KErrNone;
1.680 +
1.681 + // Use the PIL funcitonality to start a timer that will timeout if the transaction
1.682 + // is not completed within a specified time period (the client may have specified a period
1.683 + // to use in the transaction header - some something similar tot he following could be used)
1.684 + // StartSlaveTimeOutTimer(iCurrHeader->iTimeoutPeriod);
1.685 + //
1.686 + // If the timer expires, callback function HandleSlaveTimeout (implemented by the PSL, above)
1.687 + // will be called. This will ensure that the hardware ceases transfer activity, and calls ExitComplete
1.688 + // with KErrTImedOut, which will return the channel state to EIdle.
1.689 + }
1.690 + else
1.691 + // If not in state EIdle, process the next transfer in the linked-list held by the transaction
1.692 + {
1.693 + // Get the pointer the next half-duplex transfer object..
1.694 + iHalfDTransfer = GetTferNextTfer(iHalfDTransfer);
1.695 +
1.696 + // Get the pointer to the next half-duplex transfer object..
1.697 + if(iFullDTransfer)
1.698 + {
1.699 + iFullDTransfer = GetTferNextTfer(iFullDTransfer);
1.700 + }
1.701 + }
1.702 +
1.703 + TInt r = KErrNone;
1.704 + if(!iFullDTransfer && !iHalfDTransfer)
1.705 + {
1.706 + // If all of the transfers were completed, just notify the PIL and return.
1.707 + // (if either Rx or Tx has not finished properly ExitComplete() would have been called
1.708 + // from TransferEndDfc if there was an error during the transfer)
1.709 + __KTRACE_OPT(KIIC, Kern::Printf("All transfers completed successfully"));
1.710 +
1.711 + ExitComplete(KErrNone);
1.712 + }
1.713 + else
1.714 + {
1.715 + // Transfers remain to be processed
1.716 + //
1.717 + // For full-duplex transfers, the order in which read and write transfers are started may be significant.
1.718 + // Below is an example where the read transfer is explicitly started before the write transfer.
1.719 + TInt8 hDTrType = (TInt8) GetTferType(iHalfDTransfer);
1.720 +
1.721 + if(iFullDTransfer)
1.722 + {
1.723 + if(hDTrType == TIicBusTransfer::EMasterRead)
1.724 + {
1.725 + r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterRead);
1.726 + if(r != KErrNone)
1.727 + {
1.728 + return r;
1.729 + }
1.730 + r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterWrite);
1.731 + }
1.732 + else // hDTrType == TIicBusTransfer::EMasterWrite)
1.733 + {
1.734 + r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterRead);
1.735 + if(r != KErrNone)
1.736 + {
1.737 + return r;
1.738 + }
1.739 + r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterWrite);
1.740 + }
1.741 + }
1.742 + else
1.743 + // This is a HalfDuplex transfer - so just start it
1.744 + {
1.745 + r = StartTransfer(iHalfDTransfer, hDTrType);
1.746 + }
1.747 + }
1.748 + return r;
1.749 + }
1.750 +
1.751 +// The gateway function for PSL implementation
1.752 +//
1.753 +// This method is called by the PIL to initiate the transaction. After finishing it's processing,
1.754 +// the PSL calls the PIL function CompleteRequest to indicate the success (or otherwise) of the request
1.755 +//
1.756 +TInt DIicBusChannelMasterPsl::DoRequest(TIicBusTransaction* aTransaction)
1.757 + {
1.758 + __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::DoRequest (aTransaction=0x%x)\n", aTransaction));
1.759 +
1.760 + // If the pointer to the transaction passed in as a parameter, or its associated pointer to the
1.761 + // header information is NULL, return KErrArgument
1.762 + if(!aTransaction || !GetTransactionHeader(aTransaction))
1.763 + {
1.764 + return KErrArgument;
1.765 + }
1.766 +
1.767 + // The PSL operates a simple state machine to ensure that only one transaction is processed
1.768 + // at a time - if the channel is currently busy, reject the request with KErrInUse.
1.769 + if(iState != EIdle)
1.770 + {
1.771 + return KErrInUse;
1.772 + }
1.773 +
1.774 + // Make a copy of the pointer to the transaction
1.775 + iCurrTransaction = aTransaction;
1.776 +
1.777 + // Configure the hardware to support the transaction
1.778 + TInt r = KErrNone;
1.779 + if(TransConfigDiffersFromPrev()) // Optional: check if hardware needs reconfiguration
1.780 + {
1.781 + r = ConfigureInterface();
1.782 + if(r != KErrNone)
1.783 + {
1.784 + return r;
1.785 + }
1.786 + }
1.787 +
1.788 + // start processing transfers of this transaction.
1.789 + r = ProcessNextTransfers();
1.790 + return r;
1.791 + }
1.792 +