SPI update: fixed pin and zero-lengh transfer bugs, added asynchronous trasaction example (test)
--- a/omap3530/beagle_drivers/byd_touch/common/controller.h Sat Nov 06 15:14:41 2010 +0000
+++ b/omap3530/beagle_drivers/byd_touch/common/controller.h Fri Nov 19 00:43:42 2010 +0000
@@ -44,9 +44,7 @@
const TUint KDataReadyPin = 133; // DAV connected to the GPIO133 (Expansion header pin 15)
const TUint KSpiModule = 2; // McSPI3
-const TUint KSpiSlaveAddr0 = 2; // for data18-data23 pin functions slave address 2=>CS0..
-const TUint KSpiSlaveAddr1 = 3; // ..slave address 3=>CS1 (pref!)
-const TUint KSpiSlaveAddr = KSpiSlaveAddr1;
+const TUint KSpiSlaveAddr = 3; // ..slave address 3=> spi taken to 'dvi_data18-dvi_data_23' pins
const TConfigSpiV01 KHeader =
--- a/omap3530/beagleboard/rom/kernel.iby Sat Nov 06 15:14:41 2010 +0000
+++ b/omap3530/beagleboard/rom/kernel.iby Fri Nov 19 00:43:42 2010 +0000
@@ -35,7 +35,7 @@
extension[VARID]=\epoc32\release\ARMV5\##BUILD##\_##VARIANT##_LED.DLL \sys\bin\led.dll
extension[VARID]=\Epoc32\Release\ARMV5\##BUILD##\_omap3530_I2C.DLL \sys\bin\i2c.DLL
-// Uncommnet to include West Bridge Astoria Symbian Storage driver
+// Uncommnet to include West Bridge Astoria Symbian Storage driver
//extension[VARID]=\epoc32\release\ARMV5\##BUILD##\_##VARIANT##_WB.DLL \sys\bin\wb.dll
//extension[VARID]=\epoc32\release\ARMV5\##BUILD##\wb.dll \sys\bin\wb.dll
@@ -45,7 +45,9 @@
#include <rom/omapshared/tps65950.iby>
#include <rom/omap3530/spi.iby>
+#ifdef BYD_LCD
extension[VARID]=\Epoc32\Release\ARMV5\##BUILD##\_##VARIANT##_byd_xyin.dll \sys\bin\byd_xyin.dll
+#endif
device[VARID]=\Epoc32\Release\ARMV5\##BUILD##\_omap3530_EUART.PDD \sys\bin\euart.pdd
device[VARID]=\Epoc32\Release\ARMV5\##BUILD##\ECOMM.LDD \sys\bin\ecomm.ldd
@@ -78,7 +80,7 @@
// Estart
data= \epoc32\rom\beagle\estart.txt \sys\data\estart.txt
-// Uncommnet to include West Bridge Astoria Symbian Storage driver
+// Uncommnet to include West Bridge Astoria Symbian Storage driver
//extension[VARID]=\epoc32\release\ARMV5\##BUILD##\_##VARIANT##_MEDWB.PDD \sys\bin\medwb.pdd
device[VARID]=\epoc32\release\##KMAIN##\##BUILD##\PIPELIB.LDD \sys\bin\pipelib.ldd
extension[VARID]=\epoc32\release\##KMAIN##\##BUILD##\EXSTART.DLL \sys\bin\exstart.dll
--- a/omap3530/omap3530_drivers/spi/master.cpp Sat Nov 06 15:14:41 2010 +0000
+++ b/omap3530/omap3530_drivers/spi/master.cpp Fri Nov 19 00:43:42 2010 +0000
@@ -15,14 +15,16 @@
// Implementation of IIC master channel for a SPI bus.
//
-#define DBGPRINT(x)
-#define DBG_ERR(x) x
#ifdef _DEBUG
+#define DBGPRINT(x) //x
#define DEBUG_ONLY(x) //x
+#define DBG_ERR(x) x
#else
#define DEBUG_ONLY(x)
+#define DBGPRINT(x)
+#define DBG_ERR(x)
#endif
@@ -188,8 +190,9 @@
// reconfigure pins if needed..
if(slavePinSet != iCurrSlavePinSet)
{
+ DeactivateSpiPins(iChannelNumber, iCurrSlavePinSet);
+ SetupSpiPins(iChannelNumber, slavePinSet);
iCurrSlavePinSet = slavePinSet;
- SetupSpiPins(iChannelNumber, iCurrSlavePinSet);
}
// store configuration parameters
@@ -410,9 +413,12 @@
// Store the current address and ending address for Transmission - they are required by the ISR and DFC
iTxData = (TInt8*) desBufPtr->Ptr();
iTxDataEnd = (TInt8*) (iTxData + desBufPtr->Length());
- if ((TInt)iTxDataEnd % iWordSize)
+ if (((TInt)iTxDataEnd == (TInt)iTxData) || // buffer empty
+ (TInt)iTxDataEnd % iWordSize) // or wrong size / word length combination
{
- DBG_ERR(Kern::Printf("Wrong configuration - word size does not match buffer length"));
+ DBG_ERR(if ((TInt)iTxDataEnd == (TInt)iTxData) Kern::Printf("Zero-length buffer used for transfer.."));
+ DBG_ERR(if ((TInt)iTxDataEnd % iWordSize) Kern::Printf("Wrong configuration - word size does not match buffer length"));
+ ExitComplete(KErrArgument, EFalse);
return KErrArgument;
}
@@ -440,6 +446,14 @@
// 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());
+ if (((TInt)iRxDataEnd == (TInt)iRxData) || // buffer empty
+ (TInt)iRxDataEnd % iWordSize) // or wrong size / word length combination
+ {
+ DBG_ERR(if ((TInt)iRxDataEnd == (TInt)iRxData) Kern::Printf("Zero-length buffer used for transfer.."));
+ DBG_ERR(if ((TInt)iRxDataEnd % iWordSize) Kern::Printf("Wrong configuration - word size does not match buffer length"));
+ ExitComplete(KErrArgument, EFalse);
+ return KErrArgument;
+ }
DBGPRINT(Kern::Printf("Rx: Start: %x, End %x, bytes %d", iRxData, iRxDataEnd, aBufPtr->Length()));
--- a/omap3530/omap3530_drivers/spi/omap3530_spi.inl Sat Nov 06 15:14:41 2010 +0000
+++ b/omap3530/omap3530_drivers/spi/omap3530_spi.inl Fri Nov 19 00:43:42 2010 +0000
@@ -44,7 +44,7 @@
__ASSERT_DEBUG(csConf.iAddress, Kern::Fault("omap3530_spi.inl, line: ", __LINE__)); // don't try to use non-existing CS!
// now switch the pin mode back to the SPI
- SCM::SetPadConfig(csConf.iAddress, csConf.iMswLsw, csConf.iFlags | SCM::EInputEnable); // revert to intended mode
+ SCM::SetPadConfig(csConf.iAddress, csConf.iMswLsw, csConf.iFlags);
}
@@ -76,6 +76,22 @@
}
}
+// McSPI3 can have 3 different pin configuration, but only one can be active at the time.
+// for that reason, before switching to different mode -at least SOMI has to be deactivated
+// otherwise the newly activated pin does not work (why??). Changing these pins to the GPIO (mode 4)
+inline void DeactivateSpiPins(TUint aModule, TUint aPinSetId = 0)
+ {
+ __ASSERT_DEBUG(aModule < KMaxSpiChannelsPerModule, Kern::Fault("omap3530_spi.inl, line: ", __LINE__)); // aChannel > module channels
+ __ASSERT_DEBUG(aModule != 2 ? !aPinSetId : ETrue, Kern::Fault("omap3530_spi.inl, line: ", __LINE__)); // only channel 3 supports other pin configurations
+
+ const TSpiPinConfig& pinCnf = ModulePinConfig[aModule + aPinSetId];
+
+ SCM::SetPadConfig(pinCnf.iClk.iAddress, pinCnf.iClk.iMswLsw, SCM::EMode4 | SCM::EInputEnable);
+ SCM::SetPadConfig(pinCnf.iSimo.iAddress, pinCnf.iSimo.iMswLsw, SCM::EMode4 | SCM::EInputEnable);
+ SCM::SetPadConfig(pinCnf.iSomi.iAddress, pinCnf.iSomi.iMswLsw, SCM::EMode4 | SCM::EInputEnable);
+ }
+
+
// helper function - returns appropriate value for the register for a given mode
inline TUint32 SpiClkMode(TSpiClkMode aClkMode)
{
--- a/omap3530/omap3530_drivers/spi/test/d_spi_client_m.cpp Sat Nov 06 15:14:41 2010 +0000
+++ b/omap3530/omap3530_drivers/spi/test/d_spi_client_m.cpp Fri Nov 19 00:43:42 2010 +0000
@@ -208,16 +208,6 @@
r = FullDuplexMultiple();
break;
}
- case RSpiClientTest::EHalfDuplexExtendable:
- {
- r = HalfDuplexExtendable();
- break;
- }
- case RSpiClientTest::EFullDuplexExtendable:
- {
- r = FullDuplexExtendable();
- break;
- }
default:
{
@@ -242,47 +232,47 @@
{
case RSpiClientTest::EAsyncHalfDuplexSingleWrite:
{
- r = KErrNotSupported; // AsyncHalfDuplexSingleWrite(aStatus);
+ r = AsyncHalfDuplexSingleWrite(aStatus);
break;
}
case RSpiClientTest::EAsyncHalfDuplexMultipleWrite:
{
- r = KErrNotSupported; // AsyncHalfDuplexMultipleWrite(aStatus);
+ r = AsyncHalfDuplexMultipleWrite(aStatus);
break;
}
case RSpiClientTest::EAsyncHalfDuplexSingleRead:
{
- r = KErrNotSupported; // AsyncHalfDuplexSingleRead(aStatus);
+ r = AsyncHalfDuplexSingleRead(aStatus);
break;
}
case RSpiClientTest::EAsyncHalfDuplexMultipleRead:
{
- r = KErrNotSupported; // AsyncHalfDuplexMultipleRead(aStatus);
+ r = AsyncHalfDuplexMultipleRead(aStatus);
break;
}
case RSpiClientTest::EAsyncHalfDuplexMultipleWriteRead:
{
- r = KErrNotSupported; // AsyncHalfDuplexMultipleWriteRead(aStatus);
+ r = AsyncHalfDuplexMultipleWriteRead(aStatus);
break;
}
case RSpiClientTest::EAsyncFullDuplexSingle:
{
- r = KErrNotSupported; // AsyncFullDuplexSingle(aStatus);
+ r = AsyncFullDuplexSingle(aStatus);
break;
}
case RSpiClientTest::EAsyncFullDuplexMultiple:
{
- r = KErrNotSupported; // AsyncFullDuplexMultiple(aStatus);
+ r = AsyncFullDuplexMultiple(aStatus);
break;
}
case RSpiClientTest::EAsyncHalfDuplexExtendable:
{
- r = KErrNotSupported; // AsyncHalfDuplexExtendable(aStatus);
+ r = KErrNotSupported; // AsyncHalfDuplexExtendable(aStatus); TODO Not implemented yet..
break;
}
case RSpiClientTest::EAsyncFullDuplexExtendable:
{
- r = KErrNotSupported; // AsyncFullDuplexExtendable(aStatus);
+ r = KErrNotSupported; // AsyncFullDuplexExtendable(aStatus); TODO Not implemented yet..
break;
}
default:
@@ -357,6 +347,7 @@
return r;
}
+
// test half duplex write:
// - one transaction with more write transfers
// - synchronous use - all buffers / transfer objects / transactions - on the stack
@@ -696,9 +687,10 @@
// initially filled with. (i.e. as nothing was driving the SOMI line - rx will count that as 0 sent over the bus)
// see top of this file and IsLoobackAvailable() function description for more details.
TBool checkReceivedMatchesSent = IsLoopbackAvailable();
-
if(!checkReceivedMatchesSent)
+ {
Kern::Printf("!Warning: %s (%d): not using local-loop for duplex test", __FILE__, __LINE__);
+ }
for (int i = 0; i < KBuffLength; i++)
{
@@ -861,13 +853,160 @@
return r;
}
-TInt DSpiClientChannel::HalfDuplexExtendable()
+// test asynchronous transactions.
+// For these transactions - all objects: buffers, transfers and transactions have to be allocated on the heap.
+// At the momment there is no way of extracting pointers to these objects from the transaction
+// pointer by the client (this it TODO and it's currently being addressed as an architectural change).
+// For that reason pointers to these dynamic objects have to be stored by the client, so that
+// he could access these from the request, read the data (for RX request) and de-allocate/re-use them
+// in next transactions.
+// In order to achieve that (until the PIL will support above 'TODO') the templated wrapper class is used
+// to manage all these pointers, i.e.: TTransactionWrapper (see header file)
+
+
+// Callback for Master's asynchronous transactions - called when transaction has been finished
+void DSpiClientChannel::SingleHalfDuplexTransCallback(TIicBusTransaction* aTransaction,
+ TInt aBusId,
+ TInt aResult,
+ TAny* aParam)
+ {
+ LOG_FUNCTION_ENTRY;
+ TTransactionWrapper<1> *twr = (TTransactionWrapper<1>*)aParam;
+
+ if(aResult == KErrNone)
+ {
+ // if this was a read request (AsyncHalfDuplexSingleRead)- read received data:
+ HBuf8* rxBuffer = twr->GetRxBuffer(0);
+ if(rxBuffer)
+ {
+ HBuf8& buffer = *rxBuffer; // a reference to avoid writing: "(*rxBuffer)[0]" below..
+ for(TInt i= 0; i < rxBuffer->Length(); i++)
+ {
+ // check if the data is correct..
+ if(buffer[i] == 0x55)
+ {
+ aResult = KErrCorrupt;
+ break;
+ }
+ }
+ }
+ }
+
+ // complete request.. informing the client on the result.
+ Kern::RequestComplete(twr->iClient, twr->iReqStatus, aResult);
+
+ // now can finally delete the wrapper- this will release all memory it holds
+ delete twr;
+
+ // and delete transaction
+ delete aTransaction;
+ }
+
+
+// Test Asynchronous Transaction
+TInt DSpiClientChannel::AsyncHalfDuplexSingleWrite(TRequestStatus* aStatus)
+ {
+ TInt r = KErrNoMemory;
+
+ // 0. prepare busId - i.e. select module / channel and bus type
+ TUint32 busId = 0;
+ SET_BUS_TYPE(busId, DIicBusChannel::ESpi);
+ SET_CHAN_NUM(busId, 2); // THis is the ModuleNumber, i.e. McSPIx (minus one), e.g. 2 for McSPI3
+ SET_SLAVE_ADDR(busId, 0); // THis is the ChannelNumber (Slave number of the above McSPIx)
+
+ // 1. create header
+ const TConfigSpiV01 KHeader =
+ {
+ ESpiWordWidth_8, //iWordWidth
+ 3000000, //iClkSpeed
+ ESpiPolarityLowRisingEdge, //iClkMode
+ 5000, // iTimeoutPeriod
+ EBigEndian, // iEndianness
+ EMsbFirst, //iBitOrder
+ 0, //iTransactionWaitCycles
+ ESpiCSPinActiveLow //iCsPinActiveMode
+ };
+ // TConfigSpiBufV01 is a TPckgBuf<TConfigSpiV01>
+ TConfigSpiBufV01* spiHeader = new TConfigSpiBufV01(KHeader);
+
+ const TInt KTrasferBufferLength = 64;
+ if(spiHeader)
+ {
+ // 2. create wrapper to store pointers to transfer buffers and objects..
+ TTransactionWrapper<1> *twr = new TTransactionWrapper<1>(iClient, aStatus, spiHeader);
+ if(twr)
+ {
+ // 3. Create the transaction callback object (here we are using this driver's dfcque but it could be any
+ // other one (providing thread synchronization etc..)
+ TIicBusCallback *callback = new TIicBusCallback(SingleHalfDuplexTransCallback, (TAny*)twr, iDfcQ, 5);
+
+ // 4. create buffer for transaction (and store it in a wrapper class)
+ HBuf8* txBuf = HBuf8::New(KTrasferBufferLength);
+ twr->iTxBuffers[0] = txBuf;
+ for(TInt i = 0; i < KTrasferBufferLength; i++)
+ {
+ txBuf->Append(i); // append some data to send..
+ }
+
+ // 5. create transfer (and store it in a wrapper class)
+ TIicBusTransfer* txTransfer = new TIicBusTransfer(TIicBusTransfer::EMasterWrite, 8, txBuf);
+ twr->iTxTransfers[0] = txTransfer;
+
+ // 6. create transaction (no need to store it in the wrapper - this pointer is always passed to the
+ // callback (TODO: and only this could be really used in the callback...it's annoying that it's not yet!)
+ TIicBusTransaction* transaction = new TIicBusTransaction(spiHeader, txTransfer);
+
+ // lazy approach - check result of all allocations here..
+ if(twr && callback && txBuf && txTransfer && transaction)
+ {
+ r = IicBus::QueueTransaction(busId, transaction, callback);
+ }
+ else // ..and cleanup on error..
+ {
+ r = KErrNoMemory;
+ }
+ }
+
+ if(r != KErrNone)
+ {
+ delete twr; // this will clean all that was allocated already..
+ }
+ }
+
+ LOG_FUNCTION_RETURN;
+ return r;
+ }
+
+TInt DSpiClientChannel::AsyncHalfDuplexSingleRead(TRequestStatus* aStatus)
{
return KErrNotSupported; // TODO: not implemented yet..
}
-TInt DSpiClientChannel::FullDuplexExtendable()
+TInt DSpiClientChannel::AsyncHalfDuplexMultipleWrite(TRequestStatus* aStatus)
+ {
+ return KErrNotSupported; // TODO: not implemented yet..
+ }
+
+
+TInt DSpiClientChannel::AsyncHalfDuplexMultipleRead(TRequestStatus* aStatus)
{
return KErrNotSupported; // TODO: not implemented yet..
}
+TInt DSpiClientChannel::AsyncHalfDuplexMultipleWriteRead(TRequestStatus* aStatus)
+ {
+ return KErrNotSupported; // TODO: not implemented yet..
+ }
+
+TInt DSpiClientChannel::AsyncFullDuplexSingle(TRequestStatus* aStatus)
+ {
+ return KErrNotSupported; // TODO: not implemented yet..
+ }
+
+TInt DSpiClientChannel::AsyncFullDuplexMultiple(TRequestStatus* aStatus)
+ {
+ return KErrNotSupported; // TODO: not implemented yet..
+ }
+
+
+
--- a/omap3530/omap3530_drivers/spi/test/d_spi_client_m.h Sat Nov 06 15:14:41 2010 +0000
+++ b/omap3530/omap3530_drivers/spi/test/d_spi_client_m.h Fri Nov 19 00:43:42 2010 +0000
@@ -174,15 +174,87 @@
TInt HalfDuplexMultipleWriteRead();
TInt FullDuplexSingle();
TInt FullDuplexMultiple();
- TInt HalfDuplexExtendable();
- TInt FullDuplexExtendable();
+ // set of example transfers with transactions queued asynchronously
+ // callback for asynchronous transactions..
+ static void SingleHalfDuplexTransCallback(TIicBusTransaction* aTransaction,
+ TInt aBusId,
+ TInt aResult,
+ TAny* aParam);
+ // test functions..
+ TInt AsyncHalfDuplexSingleWrite(TRequestStatus* aStatus);
+ TInt AsyncHalfDuplexSingleRead(TRequestStatus* aStatus);
+ TInt AsyncHalfDuplexMultipleWrite(TRequestStatus* aStatus);
+ TInt AsyncHalfDuplexMultipleRead(TRequestStatus* aStatus);
+ TInt AsyncHalfDuplexMultipleWriteRead(TRequestStatus* aStatus);
+ TInt AsyncFullDuplexSingle(TRequestStatus* aStatus);
+ TInt AsyncFullDuplexMultiple(TRequestStatus* aStatus);
private:
- TRequestStatus iStatus;
DThread* iClient;
};
+// template for wrapper class used in asynchronous transactions in order to manage
+// pointers to buffers and allocated objects (to further access the data/release memory)
+
+template <int Size>
+class TTransactionWrapper
+ {
+public:
+ TTransactionWrapper(DThread* aClient, TRequestStatus* aReqStatus, TConfigSpiBufV01* aHeader) :
+ iHeader(aHeader),
+ iCallback(NULL),
+ iClient(aClient),
+ iReqStatus(aReqStatus)
+ {
+ }
+
+ TTransactionWrapper() : iHeader(NULL), iCallback(NULL), iClient(NULL), iReqStatus(NULL)
+ {
+ for(TUint i = 0; i < Size; ++i)
+ {
+ iTxBuffers[i] = NULL;
+ iRxBuffers[i] = NULL;
+ iTxTransfers[i] = NULL;
+ iRxTransfers[i] = NULL;
+ }
+ }
+
+ inline HBuf8* GetRxBuffer(TInt index)
+ {
+ __ASSERT_DEBUG(index < Size, Kern::Fault("d_spi_client.h, line: %d", __LINE__));
+ return iRxBuffers[index];
+ }
+
+ // destructor - to clean up all the objects..
+ inline ~TTransactionWrapper()
+ {
+ // it is safe to call delete on a 'NULL' pointer
+ delete iHeader;
+ delete iCallback;
+ for(TUint i = 0; i < Size; ++i)
+ {
+ delete iTxBuffers[i];
+ delete iTxTransfers[i];
+ delete iRxBuffers[i];
+ delete iRxTransfers[i];
+ }
+ }
+
+ // store all object used by transaction
+ TConfigSpiBufV01* iHeader;
+ TIicBusCallback *iCallback;
+ HBuf8* iTxBuffers[Size];
+ HBuf8* iRxBuffers[Size];
+ TIicBusTransfer* iTxTransfers[Size];
+ TIicBusTransfer* iRxTransfers[Size];
+
+ // also store client and request information
+ DThread* iClient;
+ TRequestStatus* iReqStatus;
+ };
+
+
// Below is additional stuff for testing with local loopback
// the IsLoopbackAvailable function checks if McSPI3 is configured to use pins from extension header
// (Beagleboard) and if these pins are physically connected (e.g. with jumper)- in order to determine
--- a/omap3530/omap3530_drivers/spi/test/t_spi_client_m.cpp Sat Nov 06 15:14:41 2010 +0000
+++ b/omap3530/omap3530_drivers/spi/test/t_spi_client_m.cpp Fri Nov 19 00:43:42 2010 +0000
@@ -69,10 +69,9 @@
void TestSynchronousOperation()
{
- test.Next(_L("TestSynchronousOperation()"));
+ test.Next(_L("TestSynchronousOperations:"));
test.Next(_L("HalfDuplexSingleWrite()"));
- while(1)
TestError(testLdd.HalfDuplexSingleWrite());
test.Next(_L("HalfDuplexMultipleWrite()"));
@@ -94,20 +93,34 @@
TestError(testLdd.FullDuplexMultiple());
}
+void TestAsynchronousOperation()
+ {
+ test.Next(_L("Test_AsynchronousOperations:"));
+
+ TRequestStatus status;
+
+ test.Next(_L("HalfDuplexSingleWrite()"));
+ testLdd.HalfDuplexSingleWrite(status);
+ User::WaitForRequest(status); // wait for completion..
+ if(status != KErrNone)
+ {
+ test.Printf(_L("Error was %d"), status.Int());
+ test(EFalse);
+ }
+
+ }
TInt E32Main()
{
test.Title();
test.Start(_L("Testing SPI.."));
-
PrepareDriver();
TestSynchronousOperation();
+ TestAsynchronousOperation();
ReleaseDriver();
-
test.End();
-
return KErrNone;
}