diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp index 944503912..63e874a27 100644 --- a/src/posix/platform/hdlc_interface.cpp +++ b/src/posix/platform/hdlc_interface.cpp @@ -247,6 +247,94 @@ exit: return error; } +otError HdlcInterface::WaitForFrame(struct timeval &aTimeout) +{ + otError error = OT_ERROR_NONE; + +#if OPENTHREAD_POSIX_VIRTUAL_TIME + struct Event event; + + platformSimSendSleepEvent(&aTimeout); + platformSimReceiveEvent(&event); + + switch (event.mEvent) + { + case OT_SIM_EVENT_RADIO_SPINEL_WRITE: + Decode(event.mData, event.mDataLength); + break; + + case OT_SIM_EVENT_ALARM_FIRED: + ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT); + break; + + default: + assert(false); + break; + } +#else // OPENTHREAD_POSIX_VIRTUAL_TIME + fd_set read_fds; + fd_set error_fds; + int rval; + + FD_ZERO(&read_fds); + FD_ZERO(&error_fds); + FD_SET(mSockFd, &read_fds); + FD_SET(mSockFd, &error_fds); + + rval = select(mSockFd + 1, &read_fds, NULL, &error_fds, &aTimeout); + + if (rval > 0) + { + if (FD_ISSET(mSockFd, &read_fds)) + { + Read(); + } + else if (FD_ISSET(mSockFd, &error_fds)) + { + DieNowWithMessage("NCP error", OT_EXIT_FAILURE); + } + else + { + DieNow(OT_EXIT_FAILURE); + } + } + else if (rval == 0) + { + ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT); + } + else if (errno != EINTR) + { + DieNowWithMessage("wait response", OT_EXIT_FAILURE); + } +#endif // OPENTHREAD_POSIX_VIRTUAL_TIME + +exit: + return error; +} + +void HdlcInterface::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout) +{ + OT_UNUSED_VARIABLE(aWriteFdSet); + OT_UNUSED_VARIABLE(aTimeout); + + FD_SET(mSockFd, &aReadFdSet); + + if (aMaxFd < mSockFd) + { + aMaxFd = mSockFd; + } +} + +void HdlcInterface::Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet) +{ + OT_UNUSED_VARIABLE(aWriteFdSet); + + if (FD_ISSET(mSockFd, &aReadFdSet)) + { + Read(); + } +} + otError HdlcInterface::WaitForWritable(void) { otError error = OT_ERROR_NONE; diff --git a/src/posix/platform/hdlc_interface.hpp b/src/posix/platform/hdlc_interface.hpp index fe95a81dd..6da08ec93 100644 --- a/src/posix/platform/hdlc_interface.hpp +++ b/src/posix/platform/hdlc_interface.hpp @@ -120,24 +120,6 @@ public: */ void Deinit(void); - /** - * - * This method returns the socket file descriptor associated with the interface. - * - * @returns The associated socket file descriptor, or -1 if interface is not initializes. - * - */ - int GetSocket(void) const { return mSockFd; } - - /** - * This method instructs `HdlcInterface` to read and decode data from radio over the socket. - * - * If a full HDLC frame is decoded while reading data, this method invokes the `HandleReceivedFrame()` (on the - * `aCallback` object from constructor) to pass the received frame to be processed. - * - */ - void Read(void); - /** * This method gets the `RxFrameBuffer`. * @@ -170,6 +152,37 @@ public: */ otError SendFrame(const uint8_t *aFrame, uint16_t aLength); + /** + * This method waits for receiving part or all of spinel frame within specified interval. + * + * @param[in] aTimeout A reference to the timeout. + * + * @retval OT_ERROR_NONE Part or all of spinel frame is received. + * @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout. + * + */ + otError WaitForFrame(struct timeval &aTimeout); + + /** + * This method updates the file descriptor sets with file descriptors used by the radio driver. + * + * @param[inout] aReadFdSet A reference to the read file descriptors. + * @param[inout] aWriteFdSet A reference to the write file descriptors. + * @param[inout] aMaxFd A reference to the max file descriptor. + * @param[inout] aTimeout A reference to the timeout. + * + */ + void UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout); + + /** + * This method performs radio driver processing. + * + * @param[in] aReadFdSet A reference to the read file descriptors. + * @param[in] aWriteFdSet A reference to the write file descriptors. + * + */ + void Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet); + #if OPENTHREAD_POSIX_VIRTUAL_TIME /** * This method process read data (decode the data). @@ -185,6 +198,15 @@ public: #endif private: + /** + * This method instructs `HdlcInterface` to read and decode data from radio over the socket. + * + * If a full HDLC frame is decoded while reading data, this method invokes the `HandleReceivedFrame()` (on the + * `aCallback` object from constructor) to pass the received frame to be processed. + * + */ + void Read(void); + /** * This method waits for the socket file descriptor associated with the HDLC interface to become writable within * `kMaxWaitTime` interval. diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index 93acecbda..9c04c3fdf 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -369,15 +369,6 @@ void platformSimReceiveEvent(struct Event *aEvent); */ void platformSimSendSleepEvent(const struct timeval *aTimeout); -/** - * This function updates the file descriptor sets with file descriptors - * used by radio spinel of virtual time simulation. - * - * @param[out] aTimeout A pointer to the timeout event to be updated. - * - */ -void platformSimRadioSpinelUpdate(struct timeval *atimeout); - /** * This function performs radio spinel processing of virtual time simulation. * diff --git a/src/posix/platform/radio_spinel.cpp b/src/posix/platform/radio_spinel.cpp index c67f05784..23041da15 100644 --- a/src/posix/platform/radio_spinel.cpp +++ b/src/posix/platform/radio_spinel.cpp @@ -681,7 +681,6 @@ void RadioSpinel::RadioReceive(void) ExitNow(); case kStateReceive: - case kStateTransmitPending: case kStateTransmitting: case kStateTransmitDone: break; @@ -703,22 +702,25 @@ exit: return; } +void RadioSpinel::TransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) +{ +#if OPENTHREAD_CONFIG_DIAG_ENABLE + if (otPlatDiagModeGet()) + { + otPlatDiagRadioTransmitDone(mInstance, aFrame, aError); + } + else +#endif + { + otPlatRadioTxDone(mInstance, aFrame, aAckFrame, aError); + } +} + void RadioSpinel::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout) { - int sockFd = mHdlcInterface.GetSocket(); + mHdlcInterface.UpdateFdSet(aReadFdSet, aWriteFdSet, aMaxFd, aTimeout); - FD_SET(sockFd, &aReadFdSet); - - if (aMaxFd < sockFd) - { - aMaxFd = sockFd; - } - - if (mState == kStateTransmitPending) - { - FD_SET(sockFd, &aWriteFdSet); - } - else if (mState == kStateTransmitting) + if (mState == kStateTransmitting) { uint64_t now = platformGetTime(); @@ -754,9 +756,10 @@ void RadioSpinel::Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet) ProcessFrameQueue(); } - if (FD_ISSET(mHdlcInterface.GetSocket(), &aReadFdSet)) + mHdlcInterface.Process(aReadFdSet, aWriteFdSet); + + if (mHdlcInterface.GetRxFrameBuffer().HasSavedFrame()) { - mHdlcInterface.Read(); ProcessFrameQueue(); } @@ -765,30 +768,13 @@ void RadioSpinel::Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet) mState = kStateReceive; mTxRadioEndUs = UINT64_MAX; -#if OPENTHREAD_CONFIG_DIAG_ENABLE - if (otPlatDiagModeGet()) - { - otPlatDiagRadioTransmitDone(mInstance, mTransmitFrame, mTxError); - } - else -#endif - { - otPlatRadioTxDone(mInstance, mTransmitFrame, (mAckRadioFrame.mLength != 0) ? &mAckRadioFrame : NULL, - mTxError); - } + TransmitDone(mTransmitFrame, (mAckRadioFrame.mLength != 0) ? &mAckRadioFrame : NULL, mTxError); } else if (mState == kStateTransmitting && platformGetTime() >= mTxRadioEndUs) { + // Frame has been successfully passed to radio, but no `TransmitDone` event received within TX_WAIT_US. DieNowWithMessage("radio tx timeout", OT_EXIT_FAILURE); } - - if (FD_ISSET(mHdlcInterface.GetSocket(), &aWriteFdSet)) - { - if (mState == kStateTransmitPending) - { - RadioTransmit(); - } - } } otError RadioSpinel::SetPromiscuous(bool aEnable) @@ -1054,68 +1040,12 @@ otError RadioSpinel::WaitResponse(void) do { -#if OPENTHREAD_POSIX_VIRTUAL_TIME - struct Event event; - - platformSimSendSleepEvent(&timeout); - platformSimReceiveEvent(&event); - - switch (event.mEvent) - { - case OT_SIM_EVENT_RADIO_SPINEL_WRITE: - mHdlcInterface.ProcessReadData(event.mData, event.mDataLength); - break; - - case OT_SIM_EVENT_ALARM_FIRED: - FreeTid(mWaitingTid); - mWaitingTid = 0; - ExitNow(mError = OT_ERROR_RESPONSE_TIMEOUT); - break; - - default: - assert(false); - break; - } -#else // OPENTHREAD_POSIX_VIRTUAL_TIME - int sockFd = mHdlcInterface.GetSocket(); - fd_set read_fds; - fd_set error_fds; - int rval; - - FD_ZERO(&read_fds); - FD_ZERO(&error_fds); - FD_SET(sockFd, &read_fds); - FD_SET(sockFd, &error_fds); - - rval = select(sockFd + 1, &read_fds, NULL, &error_fds, &timeout); - - if (rval > 0) - { - if (FD_ISSET(sockFd, &read_fds)) - { - mHdlcInterface.Read(); - } - else if (FD_ISSET(sockFd, &error_fds)) - { - DieNowWithMessage("NCP error", OT_EXIT_FAILURE); - } - else - { - assert(false); - DieNow(OT_EXIT_FAILURE); - } - } - else if (rval == 0) + if (mHdlcInterface.WaitForFrame(timeout) == OT_ERROR_RESPONSE_TIMEOUT) { FreeTid(mWaitingTid); mWaitingTid = 0; ExitNow(mError = OT_ERROR_RESPONSE_TIMEOUT); } - else if (errno != EINTR) - { - DieNowWithMessage("wait response", OT_EXIT_FAILURE); - } -#endif // OPENTHREAD_POSIX_VIRTUAL_TIME now = platformGetTime(); @@ -1154,53 +1084,6 @@ spinel_tid_t RadioSpinel::GetNextTid(void) return tid; } -/** - * This method delivers the radio frame to transceiver. - * - * otPlatRadioTxStarted() is triggered immediately for now, which may be earlier than real started time. - * - */ -void RadioSpinel::RadioTransmit(void) -{ - otError error; - - assert(mTransmitFrame != NULL); - otPlatRadioTxStarted(mInstance, mTransmitFrame); - assert(mState == kStateTransmitPending); - - error = Request(true, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW, - SPINEL_DATATYPE_DATA_WLEN_S // Frame data - SPINEL_DATATYPE_UINT8_S // Channel - SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs - SPINEL_DATATYPE_UINT8_S // MaxFrameRetries - SPINEL_DATATYPE_BOOL_S, // CsmaCaEnabled - mTransmitFrame->mPsdu, mTransmitFrame->mLength, mTransmitFrame->mChannel, - mTransmitFrame->mInfo.mTxInfo.mMaxCsmaBackoffs, mTransmitFrame->mInfo.mTxInfo.mMaxFrameRetries, - mTransmitFrame->mInfo.mTxInfo.mCsmaCaEnabled); - - if (error == OT_ERROR_NONE) - { - mTxRadioEndUs = platformGetTime() + TX_WAIT_US; - mState = kStateTransmitting; - } - else - { - mState = kStateReceive; - -#if OPENTHREAD_CONFIG_DIAG_ENABLE - - if (otPlatDiagModeGet()) - { - otPlatDiagRadioTransmitDone(mInstance, mTransmitFrame, error); - } - else -#endif - { - otPlatRadioTxDone(mInstance, mTransmitFrame, NULL, error); - } - } -} - otError RadioSpinel::SendReset(void) { otError error = OT_ERROR_NONE; @@ -1344,10 +1227,29 @@ otError RadioSpinel::Transmit(otRadioFrame &aFrame) otError error = OT_ERROR_INVALID_STATE; VerifyOrExit(mState == kStateReceive); - mState = kStateTransmitPending; - error = OT_ERROR_NONE; + mTransmitFrame = &aFrame; + // `otPlatRadioTxStarted()` is triggered immediately for now, which may be earlier than real started time. + otPlatRadioTxStarted(mInstance, mTransmitFrame); + + error = Request(true, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW, + SPINEL_DATATYPE_DATA_WLEN_S // Frame data + SPINEL_DATATYPE_UINT8_S // Channel + SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs + SPINEL_DATATYPE_UINT8_S // MaxFrameRetries + SPINEL_DATATYPE_BOOL_S, // CsmaCaEnabled + mTransmitFrame->mPsdu, mTransmitFrame->mLength, mTransmitFrame->mChannel, + mTransmitFrame->mInfo.mTxInfo.mMaxCsmaBackoffs, mTransmitFrame->mInfo.mTxInfo.mMaxFrameRetries, + mTransmitFrame->mInfo.mTxInfo.mCsmaCaEnabled); + + if (error == OT_ERROR_NONE) + { + // Waiting for `TransmitDone` event. + mState = kStateTransmitting; + mTxRadioEndUs = platformGetTime() + TX_WAIT_US; + } + exit: return error; } @@ -1755,47 +1657,27 @@ void ot::PosixApp::RadioSpinel::Process(const Event &aEvent) if (aEvent.mEvent == OT_SIM_EVENT_RADIO_SPINEL_WRITE) { mHdlcInterface.ProcessReadData(aEvent.mData, aEvent.mDataLength); + } + + if (mHdlcInterface.GetRxFrameBuffer().HasSavedFrame()) + { ProcessFrameQueue(); } if (mState == kStateTransmitDone) { - mState = kStateReceive; + mState = kStateReceive; + mTxRadioEndUs = UINT64_MAX; -#if OPENTHREAD_CONFIG_DIAG_ENABLE - if (otPlatDiagModeGet()) - { - otPlatDiagRadioTransmitDone(mInstance, mTransmitFrame, mTxError); - } - else -#endif - { - otPlatRadioTxDone(mInstance, mTransmitFrame, (mAckRadioFrame.mLength != 0) ? &mAckRadioFrame : NULL, - mTxError); - } + TransmitDone(mTransmitFrame, (mAckRadioFrame.mLength != 0) ? &mAckRadioFrame : NULL, mTxError); } - - if (mState == kStateTransmitPending) + else if (mState == kStateTransmitting && platformGetTime() >= mTxRadioEndUs) { - RadioTransmit(); + // Frame has been successfully passed to radio, but no `TransmitDone` event received within TX_WAIT_US. + DieNowWithMessage("radio tx timeout", OT_EXIT_FAILURE); } } -void ot::PosixApp::RadioSpinel::Update(struct timeval &aTimeout) -{ - // Prevent sleep event when transmitting - if (mState == kStateTransmitPending) - { - aTimeout.tv_sec = 0; - aTimeout.tv_usec = 0; - } -} - -void platformSimRadioSpinelUpdate(struct timeval *aTimeout) -{ - sRadioSpinel.Update(*aTimeout); -} - void platformSimRadioSpinelProcess(otInstance *aInstance, const struct Event *aEvent) { sRadioSpinel.Process(*aEvent); diff --git a/src/posix/platform/radio_spinel.hpp b/src/posix/platform/radio_spinel.hpp index 95bd20ad9..320896e7a 100644 --- a/src/posix/platform/radio_spinel.hpp +++ b/src/posix/platform/radio_spinel.hpp @@ -531,12 +531,11 @@ private: enum State { - kStateDisabled, ///< Radio is disabled. - kStateSleep, ///< Radio is sleep. - kStateReceive, ///< Radio is in receive mode. - kStateTransmitPending, ///< Frame transmission requested, waiting to pass frame to radio. - kStateTransmitting, ///< Frame passed to radio for transmission, waiting for done event from radio. - kStateTransmitDone, ///< Radio indicated frame transmission is done. + kStateDisabled, ///< Radio is disabled. + kStateSleep, ///< Radio is sleep. + kStateReceive, ///< Radio is in receive mode. + kStateTransmitting, ///< Frame passed to radio for transmission, waiting for done event from radio. + kStateTransmitDone, ///< Radio indicated frame transmission is done. }; otError CheckSpinelVersion(void); @@ -640,7 +639,8 @@ private: void HandleWaitingResponse(uint32_t aCommand, spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); void RadioReceive(void); - void RadioTransmit(void); + + void TransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError); otInstance *mInstance; diff --git a/src/posix/platform/sim.c b/src/posix/platform/sim.c index f3c8b92b9..5ff05d9d8 100644 --- a/src/posix/platform/sim.c +++ b/src/posix/platform/sim.c @@ -171,14 +171,13 @@ void platformSimUpdateFdSet(fd_set * aReadFdSet, { OT_UNUSED_VARIABLE(aWriteFdSet); OT_UNUSED_VARIABLE(aErrorFdSet); + OT_UNUSED_VARIABLE(aTimeout); FD_SET(sSockFd, aReadFdSet); if (*aMaxFd < sSockFd) { *aMaxFd = sSockFd; } - - platformSimRadioSpinelUpdate(aTimeout); } void platformSimProcess(otInstance * aInstance,