mirror of
https://github.com/espressif/openthread.git
synced 2026-08-24 19:29:52 +00:00
[spi-interface] send pending frame from WaitForFrame() (#7431)
This commit updates the `SpiInterface` implementation to send any pending tx SPI frame from `WaitForFrame()` while waiting for a response frame from RCP. This should help address rare RCP response timeout which can potentially happen if RCP is not yet ready to accept a frame in a first SPI transaction from a `SendFrame()` call followed by a call to `WaitForFrame()`. This commit updates the `WaitForFrame()` to reuse `UpdateFdset()` and `Process()` methods which in turn handle the situation that central is being rate-limited by the peripheral.
This commit is contained in:
@@ -88,6 +88,7 @@ SpiInterface::SpiInterface(SpinelInterface::ReceiveFrameCallback aCallback,
|
||||
, mSpiTxPayloadSize(0)
|
||||
, mDidPrintRateLimitLog(false)
|
||||
, mSpiSlaveDataLen(0)
|
||||
, mDidRxFrame(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -579,6 +580,7 @@ otError SpiInterface::PushPullSpi(void)
|
||||
// Upper layer will free the frame buffer.
|
||||
discardRxFrame = false;
|
||||
|
||||
mDidRxFrame = true;
|
||||
mReceiveFrameCallback(mReceiveFrameContext);
|
||||
}
|
||||
}
|
||||
@@ -731,7 +733,14 @@ void SpiInterface::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMa
|
||||
|
||||
void SpiInterface::Process(const RadioProcessContext &aContext)
|
||||
{
|
||||
if (FD_ISSET(mIntGpioValueFd, aContext.mReadFdSet))
|
||||
Process(aContext.mReadFdSet, aContext.mWriteFdSet);
|
||||
}
|
||||
|
||||
void SpiInterface::Process(const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aWriteFdSet);
|
||||
|
||||
if (FD_ISSET(mIntGpioValueFd, aReadFdSet))
|
||||
{
|
||||
struct gpioevent_data event;
|
||||
|
||||
@@ -751,68 +760,48 @@ void SpiInterface::Process(const RadioProcessContext &aContext)
|
||||
|
||||
otError SpiInterface::WaitForFrame(uint64_t aTimeoutUs)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
struct timeval spiTimeout = {kSecPerDay, 0};
|
||||
struct timeval timeout;
|
||||
fd_set readFdSet;
|
||||
int ret;
|
||||
bool isDataReady = false;
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint64_t now = otPlatTimeGet();
|
||||
uint64_t end = now + aTimeoutUs;
|
||||
|
||||
timeout.tv_sec = static_cast<time_t>(aTimeoutUs / US_PER_S);
|
||||
timeout.tv_usec = static_cast<suseconds_t>(aTimeoutUs % US_PER_S);
|
||||
mDidRxFrame = false;
|
||||
|
||||
FD_ZERO(&readFdSet);
|
||||
|
||||
if (mIntGpioValueFd >= 0)
|
||||
while (now < end)
|
||||
{
|
||||
if ((isDataReady = CheckInterrupt()))
|
||||
fd_set readFdSet;
|
||||
fd_set writeFdSet;
|
||||
int maxFds = -1;
|
||||
struct timeval timeout;
|
||||
int ret;
|
||||
|
||||
timeout.tv_sec = static_cast<time_t>((end - now) / US_PER_S);
|
||||
timeout.tv_usec = static_cast<suseconds_t>((end - now) % US_PER_S);
|
||||
|
||||
FD_ZERO(&readFdSet);
|
||||
FD_ZERO(&writeFdSet);
|
||||
|
||||
UpdateFdSet(readFdSet, writeFdSet, maxFds, timeout);
|
||||
|
||||
ret = select(maxFds + 1, &readFdSet, &writeFdSet, nullptr, &timeout);
|
||||
|
||||
if (ret >= 0)
|
||||
{
|
||||
// Interrupt pin is asserted, set the timeout to be 0.
|
||||
spiTimeout.tv_sec = 0;
|
||||
spiTimeout.tv_usec = 0;
|
||||
Process(&readFdSet, &writeFdSet);
|
||||
|
||||
if (mDidRxFrame)
|
||||
{
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (errno != EINTR)
|
||||
{
|
||||
// The interrupt pin was not asserted, so we wait for the interrupt pin to be asserted by adding it to the
|
||||
// read set.
|
||||
FD_SET(mIntGpioValueFd, &readFdSet);
|
||||
DieNow(OT_EXIT_ERROR_ERRNO);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// In this case we don't have an interrupt, so we revert to SPI polling.
|
||||
spiTimeout.tv_sec = 0;
|
||||
spiTimeout.tv_usec = kSpiPollPeriodUs;
|
||||
|
||||
now = otPlatTimeGet();
|
||||
}
|
||||
|
||||
if (timercmp(&spiTimeout, &timeout, <))
|
||||
{
|
||||
timeout = spiTimeout;
|
||||
}
|
||||
|
||||
ret = select(mIntGpioValueFd + 1, &readFdSet, nullptr, nullptr, &timeout);
|
||||
|
||||
if (ret > 0 && FD_ISSET(mIntGpioValueFd, &readFdSet))
|
||||
{
|
||||
struct gpioevent_data event;
|
||||
|
||||
// Read event data to clear interrupt.
|
||||
VerifyOrDie(read(mIntGpioValueFd, &event, sizeof(event)) != -1, OT_EXIT_FAILURE);
|
||||
isDataReady = true;
|
||||
}
|
||||
|
||||
if (isDataReady)
|
||||
{
|
||||
IgnoreError(PushPullSpi());
|
||||
}
|
||||
else if (ret == 0)
|
||||
{
|
||||
ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT);
|
||||
}
|
||||
else if (errno != EINTR)
|
||||
{
|
||||
DieNow(OT_EXIT_ERROR_ERRNO);
|
||||
}
|
||||
error = OT_ERROR_RESPONSE_TIMEOUT;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
|
||||
@@ -173,6 +173,7 @@ private:
|
||||
uint8_t *GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength);
|
||||
otError DoSpiTransfer(uint8_t *aSpiRxFrameBuffer, uint32_t aTransferLength);
|
||||
otError PushPullSpi(void);
|
||||
void Process(const fd_set *aReadFdSet, const fd_set *aWriteFdSet);
|
||||
|
||||
bool CheckInterrupt(void);
|
||||
void LogStats(void);
|
||||
@@ -245,6 +246,8 @@ private:
|
||||
bool mDidPrintRateLimitLog;
|
||||
uint16_t mSpiSlaveDataLen;
|
||||
|
||||
bool mDidRxFrame;
|
||||
|
||||
// Non-copyable, intentionally not implemented.
|
||||
SpiInterface(const SpiInterface &);
|
||||
SpiInterface &operator=(const SpiInterface &);
|
||||
|
||||
Reference in New Issue
Block a user