SPI update: fixed pin and zero-lengh transfer bugs, added asynchronous trasaction example (test) Beagle_BSP_dev
authorLukasz Forynski <lukasz.forynski@gmail.com>
Fri, 19 Nov 2010 00:43:42 +0000
branchBeagle_BSP_dev
changeset 112 fdfa12d9a47a
parent 111 35fb7dda225a
child 116 e7f4b52d2c87
SPI update: fixed pin and zero-lengh transfer bugs, added asynchronous trasaction example (test)
omap3530/beagle_drivers/byd_touch/common/controller.h
omap3530/beagleboard/rom/kernel.iby
omap3530/omap3530_drivers/spi/master.cpp
omap3530/omap3530_drivers/spi/omap3530_spi.inl
omap3530/omap3530_drivers/spi/test/d_spi_client_m.cpp
omap3530/omap3530_drivers/spi/test/d_spi_client_m.h
omap3530/omap3530_drivers/spi/test/t_spi_client_m.cpp
--- 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;
 	}