NcpSpi: Fix a race situation and simply the tx state model (#1075)

This commit fixes a race issue in tx path of `NcpSpi`: If the SPI
transaction is completed while in middle of `PrepareTxFrame()`task
handler before clearing the `mHandlingSendDone` flag, we can
have a situation where `PrepareTxFrame` task is not posted again and
then the next queued tx frames are not handled immediately.

It also simplifies the tx state model by adding a single `mTxState`
variable to track the current state as an `enum`.
This commit is contained in:
Abtin Keshavarzian
2016-12-14 11:39:43 -08:00
committed by Jonathan Hui
parent 100e568a09
commit 1a220e30d8
2 changed files with 29 additions and 18 deletions
+21 -16
View File
@@ -95,8 +95,7 @@ NcpSpi::NcpSpi(otInstance *aInstance):
memset(mEmptySendFrame, 0, kSpiHeaderLength);
memset(mSendFrame, 0, kSpiHeaderLength);
mSending = false;
mHandlingSendDone = false;
mTxState = kTxStateIdle;
mHandlingRxFrame = false;
mTxFrameBuffer.SetCallbacks(NULL, TxFrameBufferHasData, this);
@@ -181,14 +180,13 @@ NcpSpi::SpiTransactionComplete(
mHandleRxFrameTask.Post();
}
if ( mSending
&& !mHandlingSendDone
if ( (mTxState == kTxStateSending)
&& (tx_data_len > 0)
&& (tx_data_len <= (aTransactionLength - kSpiHeaderLength))
&& (tx_data_len <= tx_accept_len)
) {
// Our transmission was successful.
mHandlingSendDone = true;
mTxState = kTxStateHandlingSendDone;
mPrepareTxFrameTask.Post();
}
}
@@ -201,7 +199,7 @@ NcpSpi::SpiTransactionComplete(
spi_header_set_flag_byte(mEmptySendFrame, SPI_PATTERN_VALUE);
}
if (mSending && !mHandlingSendDone)
if (mTxState == kTxStateSending)
{
aMISOBuf = mSendFrame;
aMISOBufLen = mSendFrameLen;
@@ -230,7 +228,7 @@ NcpSpi::SpiTransactionComplete(
aMISOBufLen,
aMOSIBuf,
aMOSIBufLen,
mSending && !mHandlingSendDone
(mTxState == kTxStateSending)
);
}
@@ -290,7 +288,7 @@ ThreadError NcpSpi::PrepareNextSpiSendFrame(void)
mSendFrameLen = frameLength + kSpiHeaderLength;
mSending = true;
mTxState = kTxStateSending;
errorCode = otPlatSpiSlavePrepareTransaction(
mSendFrame,
@@ -310,7 +308,7 @@ ThreadError NcpSpi::PrepareNextSpiSendFrame(void)
if (errorCode != kThreadError_None)
{
mSending = false;
mTxState = kTxStateIdle;
}
// Remove the frame from tx buffer and inform the base
@@ -329,15 +327,22 @@ void NcpSpi::PrepareTxFrame(void *aContext)
void NcpSpi::PrepareTxFrame(void)
{
if (mHandlingSendDone)
{
mSending = false;
PrepareNextSpiSendFrame();
mHandlingSendDone = false;
}
else if (!mSending)
switch (mTxState)
{
case kTxStateHandlingSendDone:
mTxState = kTxStateIdle;
// Fall-through to next case to prepare the next frame (if any).
case kTxStateIdle:
PrepareNextSpiSendFrame();
break;
case kTxStateSending:
// The next frame in queue (if any) will be prepared when the
// current frame is successfully sent and this task is posted
// again from the `SpiTransactionComplete()` callback.
break;
}
}
+8 -2
View File
@@ -73,6 +73,13 @@ private:
kSpiHeaderLength = 5, // Size of spi header.
};
enum TxState
{
kTxStateIdle, // No frame to send
kTxStateSending, // A frame is ready to be sent
kTxStateHandlingSendDone // The frame was sent successfully, waiting to prepare the next one (if any)
};
uint16_t OutboundFrameSize(void);
static void SpiTransactionComplete(
@@ -102,9 +109,8 @@ private:
ThreadError PrepareNextSpiSendFrame(void);
bool mSending;
TxState mTxState;
bool mHandlingRxFrame;
bool mHandlingSendDone;
Tasklet mHandleRxFrameTask;
Tasklet mPrepareTxFrameTask;