diff --git a/src/wifi/model/he/he-frame-exchange-manager.cc b/src/wifi/model/he/he-frame-exchange-manager.cc index c114e478d..59225edcc 100644 --- a/src/wifi/model/he/he-frame-exchange-manager.cc +++ b/src/wifi/model/he/he-frame-exchange-manager.cc @@ -93,6 +93,7 @@ HeFrameExchangeManager::DoDispose (void) m_psduMap.clear (); m_txParams.Clear (); m_muScheduler = 0; + m_multiStaBaEvent.Cancel (); VhtFrameExchangeManager::DoDispose (); } @@ -268,6 +269,9 @@ HeFrameExchangeManager::SendPsduMap (void) WifiTxVector* responseTxVector = nullptr; Ptr mpdu = nullptr; Ptr psdu = nullptr; + WifiTxVector txVector; + + // Compute the type of TX timer to set depending on the acknowledgment method /* * Acknowledgment via a sequence of BlockAckReq and BlockAck frames @@ -409,6 +413,95 @@ HeFrameExchangeManager::SendPsduMap (void) timerType = WifiTxTimer::WAIT_BLOCK_ACKS_IN_TB_PPDU; responseTxVector = &acknowledgment->stationsReplyingWithBlockAck.begin ()->second.blockAckTxVector; } + /* + * Basic Trigger Frame starting an UL MU transmission + */ + else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA) + { + // the PSDU map being sent must contain a (Basic) Trigger Frame + NS_ASSERT (m_psduMap.size () == 1 && m_psduMap.begin ()->first == SU_STA_ID + && (mpdu = *m_psduMap.begin ()->second->begin ())->GetHeader ().IsTrigger ()); + + WifiUlMuMultiStaBa* acknowledgment = static_cast (m_txParams.m_acknowledgment.get ()); + + // record the set of stations solicited by this Trigger Frame + m_staExpectTbPpduFrom.clear (); + + for (const auto& station : acknowledgment->stationsReceivingMultiStaBa) + { + m_staExpectTbPpduFrom.insert (station.first.first); + } + + // Reset stationsReceivingMultiStaBa, which will be filled as soon as + // TB PPDUs are received + acknowledgment->stationsReceivingMultiStaBa.clear (); + acknowledgment->baType.m_bitmapLen.clear (); + + // Add a SIFS and the TB PPDU duration to the acknowledgment time of the + // Trigger Frame, so that its Duration/ID is correctly computed + NS_ASSERT (m_muScheduler != 0); + acknowledgment->acknowledgmentTime += m_mac->GetWifiPhy ()->GetSifs () + + m_muScheduler->GetUlMuInfo ().tbPpduDuration; + + timerType = WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF; + responseTxVector = &acknowledgment->tbPpduTxVector; + } + /* + * BSRP Trigger Frame + */ + else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE + && !m_txParams.m_txVector.IsUlMu () + && m_psduMap.size () == 1 && m_psduMap.begin ()->first == SU_STA_ID + && (mpdu = *m_psduMap.begin ()->second->begin ())->GetHeader ().IsTrigger ()) + { + CtrlTriggerHeader trigger; + mpdu->GetPacket ()->PeekHeader (trigger); + NS_ASSERT (trigger.IsBsrp ()); + NS_ASSERT (m_apMac != 0); + + // record the set of stations solicited by this Trigger Frame + m_staExpectTbPpduFrom.clear (); + + for (const auto& userInfo : trigger) + { + auto staIt = m_apMac->GetStaList ().find (userInfo.GetAid12 ()); + NS_ASSERT (staIt != m_apMac->GetStaList ().end ()); + m_staExpectTbPpduFrom.insert (staIt->second); + } + + // Add a SIFS and the TB PPDU duration to the acknowledgment time of the + // Trigger Frame, so that its Duration/ID is correctly computed + WifiNoAck* acknowledgment = static_cast (m_txParams.m_acknowledgment.get ()); + txVector = trigger.GetHeTbTxVector (trigger.begin ()->GetAid12 ()); + acknowledgment->acknowledgmentTime += m_mac->GetWifiPhy ()->GetSifs () + + HePhy::ConvertLSigLengthToHeTbPpduDuration (trigger.GetUlLength (), + txVector, + m_phy->GetPhyBand ()); + + timerType = WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF; + responseTxVector = &txVector; + } + /* + * TB PPDU solicited by a Basic Trigger Frame + */ + else if (m_txParams.m_txVector.IsUlMu () + && m_txParams.m_acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU) + { + NS_ASSERT (m_psduMap.size () == 1); + timerType = WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU; + NS_ASSERT (m_staMac != 0 && m_staMac->IsAssociated ()); + txVector = m_mac->GetWifiRemoteStationManager ()->GetBlockAckTxVector (m_psduMap.begin ()->second->GetAddr1 (), + m_txParams.m_txVector); + responseTxVector = &txVector; + } + /* + * QoS Null frames solicited by a BSRP Trigger Frame + */ + else if (m_txParams.m_txVector.IsUlMu () + && m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE) + { + // No response is expected, so do nothing. + } else { NS_ABORT_MSG ("Unable to handle the selected acknowledgment method (" @@ -471,6 +564,15 @@ HeFrameExchangeManager::SendPsduMap (void) m_txTimer.Set (timerType, timeout, &HeFrameExchangeManager::BlockAcksInTbPpduTimeout, this, &m_psduMap, &m_staExpectTbPpduFrom, m_staExpectTbPpduFrom.size ()); break; + case WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF: + case WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF: + m_txTimer.Set (timerType, timeout, &HeFrameExchangeManager::TbPpduTimeout, this, + &m_psduMap, &m_staExpectTbPpduFrom, m_staExpectTbPpduFrom.size ()); + break; + case WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU: + m_txTimer.Set (timerType, timeout, &HeFrameExchangeManager::BlockAckAfterTbPpduTimeout, + this, m_psduMap.begin ()->second, m_txParams.m_txVector); + break; default: NS_ABORT_MSG ("Unknown timer type: " << timerType); break; @@ -678,6 +780,28 @@ HeFrameExchangeManager::CalculateAcknowledgmentTime (WifiAcknowledgment* acknowl dlMuAggrTfAcknowledgment->acknowledgmentTime = m_phy->GetSifs () + duration; } + /* + * Basic Trigger Frame starting an UL MU transmission + */ + else if (acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA) + { + WifiUlMuMultiStaBa* ulMuMultiStaBa = static_cast (acknowledgment); + + Time duration = m_phy->CalculateTxDuration (GetBlockAckSize (ulMuMultiStaBa->baType), + ulMuMultiStaBa->multiStaBaTxVector, + m_phy->GetPhyBand ()); + ulMuMultiStaBa->acknowledgmentTime = m_phy->GetSifs () + duration; + } + /* + * TB PPDU solicired by a Basic or BSRP Trigger Frame + */ + else if (acknowledgment->method == WifiAcknowledgment::ACK_AFTER_TB_PPDU) + { + // The station solicited by the Trigger Frame does not have to account + // for the actual acknowledgment time since it is given the PPDU duration + // through the Trigger Frame + acknowledgment->acknowledgmentTime = Seconds (0); + } else { VhtFrameExchangeManager::CalculateAcknowledgmentTime (acknowledgment); @@ -716,6 +840,37 @@ HeFrameExchangeManager::GetTxDuration (uint32_t ppduPayloadSize, Mac48Address re return std::max (psduDuration, txParams.m_txDuration); } +void +HeFrameExchangeManager::TbPpduTimeout (WifiPsduMap* psduMap, + const std::set* staMissedTbPpduFrom, + std::size_t nSolicitedStations) +{ + NS_LOG_FUNCTION (this << psduMap << staMissedTbPpduFrom->size () << nSolicitedStations); + + NS_ASSERT (psduMap != nullptr); + NS_ASSERT (psduMap->size () == 1 && psduMap->begin ()->first == SU_STA_ID + && psduMap->begin ()->second->GetHeader (0).IsTrigger ()); + + // This method is called if some station(s) did not send a TB PPDU + NS_ASSERT (!staMissedTbPpduFrom->empty ()); + NS_ASSERT (m_edca != 0); + + if (staMissedTbPpduFrom->size () == nSolicitedStations) + { + // no station replied, the transmission failed + m_edca->UpdateFailedCw (); + + TransmissionFailed (); + } + else if (!m_multiStaBaEvent.IsRunning ()) + { + m_edca->ResetCw (); + TransmissionSucceeded (); + } + + m_psduMap.clear (); +} + void HeFrameExchangeManager::BlockAcksInTbPpduTimeout (WifiPsduMap* psduMap, const std::set* staMissedBlockAckFrom, @@ -784,6 +939,26 @@ HeFrameExchangeManager::BlockAcksInTbPpduTimeout (WifiPsduMap* psduMap, m_psduMap.clear (); } +void +HeFrameExchangeManager::BlockAckAfterTbPpduTimeout (Ptr psdu, const WifiTxVector& txVector) +{ + NS_LOG_FUNCTION (this << *psdu << txVector); + + bool resetCw; + + // call ReportDataFailed to increase SRC/LRC + m_mac->GetWifiRemoteStationManager ()->ReportDataFailed (*psdu->begin ()); + + MissedBlockAck (psdu, m_txParams.m_txVector, resetCw); + + // This is a PSDU sent in a TB PPDU. An HE STA resumes the EDCA backoff procedure + // without modifying CW or the backoff counter for the associated EDCAF, after + // transmission of an MPDU in a TB PPDU regardless of whether the STA has received + // the corresponding acknowledgment frame in response to the MPDU sent in the TB PPDU + // (Sec. 10.22.2.2 of 11ax Draft 3.0) + m_psduMap.clear (); +} + WifiTxVector HeFrameExchangeManager::GetHeTbTxVector (CtrlTriggerHeader trigger, Mac48Address triggerSender) const { @@ -862,6 +1037,267 @@ HeFrameExchangeManager::SetTargetRssi (CtrlTriggerHeader& trigger) const } } +void +HeFrameExchangeManager::SendMultiStaBlockAck (const WifiTxParameters& txParams) +{ + NS_LOG_FUNCTION (this << &txParams); + + NS_ASSERT (m_apMac != 0); + NS_ASSERT (txParams.m_acknowledgment + && txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA); + WifiUlMuMultiStaBa* acknowledgment = static_cast (txParams.m_acknowledgment.get ()); + + NS_ASSERT (!acknowledgment->stationsReceivingMultiStaBa.empty ()); + + CtrlBAckResponseHeader blockAck; + blockAck.SetType (acknowledgment->baType); + + Mac48Address receiver; + + for (const auto& staInfo : acknowledgment->stationsReceivingMultiStaBa) + { + receiver = staInfo.first.first; + uint8_t tid = staInfo.first.second; + std::size_t index = staInfo.second; + + blockAck.SetAid11 (m_apMac->GetAssociationId (receiver), index); + blockAck.SetTidInfo (tid, index); + + if (tid == 14) + { + // All-ack context + NS_LOG_DEBUG ("Multi-STA Block Ack: Sending All-ack to=" << receiver); + blockAck.SetAckType (true, index); + continue; + } + + if (acknowledgment->baType.m_bitmapLen.at (index) == 0) + { + // Acknowledgment context + NS_LOG_DEBUG ("Multi-STA Block Ack: Sending Ack to=" << receiver); + blockAck.SetAckType (true, index); + } + else + { + // Block acknowledgment context + blockAck.SetAckType (false, index); + + auto addressTidPair = staInfo.first; + auto agreementIt = m_agreements.find (addressTidPair); + NS_ASSERT (agreementIt != m_agreements.end ()); + agreementIt->second.FillBlockAckBitmap (&blockAck, index); + NS_LOG_DEBUG ("Multi-STA Block Ack: Sending Block Ack with seq=" << blockAck.GetStartingSequence (index) + << " to=" << receiver << " tid=" << +tid); + } + } + + WifiMacHeader hdr; + hdr.SetType (WIFI_MAC_CTL_BACKRESP); + hdr.SetAddr1 (acknowledgment->stationsReceivingMultiStaBa.size () == 1 ? receiver : Mac48Address::GetBroadcast ()); + hdr.SetAddr2 (m_self); + hdr.SetDsNotFrom (); + hdr.SetDsNotTo (); + + Ptr packet = Create (); + packet->AddHeader (blockAck); + Ptr psdu = GetWifiPsdu (Create (packet, hdr), + acknowledgment->multiStaBaTxVector); + + // The Duration/ID field in a BlockAck frame transmitted in response to a frame + // carried in HE TB PPDU is set according to the multiple protection settings + // (Sec. 9.2.5.7 of 802.11ax D3.0) + Time txDuration = m_phy->CalculateTxDuration (GetBlockAckSize (acknowledgment->baType), + acknowledgment->multiStaBaTxVector, + m_phy->GetPhyBand ()); + WifiTxParameters params; + // if the TXOP limit is null, GetPsduDurationId returns the acknowledgment time, + // hence we set an method with acknowledgment time equal to zero. + params.m_acknowledgment = std::unique_ptr (new WifiNoAck); + psdu->SetDuration (GetPsduDurationId (txDuration, params)); + + psdu->GetPayload (0)->AddPacketTag (m_muSnrTag); + + ForwardPsduDown (psdu, acknowledgment->multiStaBaTxVector); + + // continue with the TXOP if time remains + m_psduMap.clear (); + m_edca->ResetCw (); + m_muSnrTag.Reset (); + Simulator::Schedule (txDuration, &HeFrameExchangeManager::TransmissionSucceeded, this); +} + +void +HeFrameExchangeManager::ReceiveBasicTrigger (const CtrlTriggerHeader& trigger, const WifiMacHeader& hdr) +{ + NS_LOG_FUNCTION (this << trigger << hdr); + NS_ASSERT (trigger.IsBasic ()); + NS_ASSERT (m_staMac != 0 && m_staMac->IsAssociated ()); + + NS_LOG_DEBUG ("Received a Trigger Frame (basic variant) soliciting a transmission"); + + if (trigger.GetCsRequired () && hdr.GetAddr2 () != m_txopHolder && m_navEnd > Simulator::Now ()) + { + NS_LOG_DEBUG ("Carrier Sensing required and channel busy, do nothing"); + return; + } + + // Starting from the Preferred AC indicated in the Trigger Frame, check if there + // is either a pending BlockAckReq frame or a data frame that can be transmitted + // in the allocated time and is addressed to a station with which a Block Ack + // agreement has been established. + + // create the sequence of TIDs to check + std::vector tids; + uint16_t staId = m_staMac->GetAssociationId (); + AcIndex preferredAc = trigger.FindUserInfoWithAid (staId)->GetPreferredAc (); + auto acIt = wifiAcList.find (preferredAc); + for (uint8_t i = 0; i < 4; i++) + { + NS_ASSERT (acIt != wifiAcList.end ()); + tids.push_back (acIt->second.GetHighTid ()); + tids.push_back (acIt->second.GetLowTid ()); + + acIt++; + if (acIt == wifiAcList.end ()) + { + acIt = wifiAcList.begin (); + } + } + + Ptr mpdu; + Ptr psdu; + WifiTxParameters txParams; + WifiTxVector tbTxVector = GetHeTbTxVector (trigger, hdr.GetAddr2 ()); + Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration (trigger.GetUlLength (), + tbTxVector, + m_phy->GetPhyBand ()); + + for (const auto& tid : tids) + { + Ptr edca = m_mac->GetQosTxop (tid); + + if (!edca->GetBaAgreementEstablished (hdr.GetAddr2 (), tid)) + { + // no Block Ack agreement established for this TID + continue; + } + + txParams.Clear (); + txParams.m_txVector = tbTxVector; + + // first, check if there is a pending BlockAckReq frame + if ((mpdu = edca->GetBaManager ()->GetBar (false, tid, hdr.GetAddr2 ())) != 0 + && TryAddMpdu (mpdu, txParams, ppduDuration)) + { + NS_LOG_DEBUG ("Sending a BAR within a TB PPDU"); + psdu = Create (edca->GetBaManager ()->GetBar (true, tid, hdr.GetAddr2 ()), true); + break; + } + + // otherwise, check if a suitable data frame is available + if ((mpdu = edca->PeekNextMpdu (tid, hdr.GetAddr2 ())) != 0) + { + WifiMacQueueItem::QueueIteratorPair queueIt; + Ptr item = edca->GetNextMpdu (mpdu, txParams, ppduDuration, false, queueIt); + + if (item != 0) + { + // try A-MPDU aggregation + std::vector> mpduList = m_mpduAggregator->GetNextAmpdu (item, txParams, + ppduDuration, + queueIt); + psdu = (mpduList.size () > 1 ? Create (std::move (mpduList)) + : Create (item, true)); + break; + } + } + } + + if (psdu != 0) + { + psdu->SetDuration (hdr.GetDuration () - m_phy->GetSifs () - ppduDuration); + SendPsduMapWithProtection (WifiPsduMap {{staId, psdu}}, txParams); + } + else + { + // send QoS Null frames + SendQosNullFramesInTbPpdu (trigger, hdr); + } +} + +void +HeFrameExchangeManager::SendQosNullFramesInTbPpdu (const CtrlTriggerHeader& trigger, const WifiMacHeader& hdr) +{ + NS_LOG_FUNCTION (this << trigger << hdr); + NS_ASSERT (trigger.IsBasic () || trigger.IsBsrp ()); + NS_ASSERT (m_staMac != 0 && m_staMac->IsAssociated ()); + + NS_LOG_DEBUG ("Requested to send QoS Null frames"); + + if (trigger.GetCsRequired () && hdr.GetAddr2 () != m_txopHolder && m_navEnd > Simulator::Now ()) + { + NS_LOG_DEBUG ("Carrier Sensing required and channel busy, do nothing"); + return; + } + + WifiMacHeader header; + header.SetType (WIFI_MAC_QOSDATA_NULL); + header.SetAddr1 (hdr.GetAddr2 ()); + header.SetAddr2 (m_self); + header.SetAddr3 (hdr.GetAddr2 ()); + header.SetDsTo (); + header.SetDsNotFrom (); + // TR3: Sequence numbers for transmitted QoS (+)Null frames may be set + // to any value. (Table 10-3 of 802.11-2016) + header.SetSequenceNumber (0); + // Set the EOSP bit so that NotifyTxToEdca will add the Queue Size + header.SetQosEosp (); + + WifiTxParameters txParams; + txParams.m_txVector = GetHeTbTxVector (trigger, hdr.GetAddr2 ()); + txParams.m_protection = std::unique_ptr (new WifiNoProtection); + txParams.m_acknowledgment = std::unique_ptr (new WifiNoAck); + + Time ppduDuration = HePhy::ConvertLSigLengthToHeTbPpduDuration (trigger.GetUlLength (), + txParams.m_txVector, + m_phy->GetPhyBand ()); + header.SetDuration (hdr.GetDuration () - m_phy->GetSifs () - ppduDuration); + + Ptr mpdu; + std::vector> mpduList; + uint8_t tid = 0; + header.SetQosTid (tid); + + while (tid < 8 + && IsWithinSizeAndTimeLimits (txParams.GetSizeIfAddMpdu (mpdu = Create (Create (), + header)), + hdr.GetAddr2 (), txParams, ppduDuration)) + { + NS_LOG_DEBUG ("Aggregating a QoS Null frame with tid=" << +tid); + // We could call TryAddMpdu instead of IsWithinSizeAndTimeLimits above in order to + // get the TX parameters updated automatically. However, aggregating the QoS Null + // frames might fail because MPDU aggregation is disabled by default for VO + // and BK. Therefore, we skip the check on max A-MPDU size and only update the + // TX parameters below. + txParams.m_acknowledgment = GetAckManager ()->TryAddMpdu (mpdu, txParams); + txParams.AddMpdu (mpdu); + UpdateTxDuration (mpdu->GetHeader ().GetAddr1 (), txParams); + mpduList.push_back (mpdu); + header.SetQosTid (++tid); + } + + if (mpduList.empty ()) + { + NS_LOG_DEBUG ("Not enough time to send a QoS Null frame"); + return; + } + + Ptr psdu = (mpduList.size () > 1 ? Create (std::move (mpduList)) + : Create (mpduList.front (), true)); + uint16_t staId = m_staMac->GetAssociationId (); + SendPsduMapWithProtection (WifiPsduMap {{staId, psdu}}, txParams); +} + void HeFrameExchangeManager::ReceiveMpdu (Ptr mpdu, RxSignalInfo rxSignalInfo, const WifiTxVector& txVector, bool inAmpdu) @@ -872,6 +1308,96 @@ HeFrameExchangeManager::ReceiveMpdu (Ptr mpdu, RxSignalInfo rx const WifiMacHeader& hdr = mpdu->GetHeader (); + if (txVector.IsUlMu () && m_txTimer.IsRunning () + && m_txTimer.GetReason () == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF) + { + Mac48Address sender = hdr.GetAddr2 (); + NS_ASSERT (m_txParams.m_acknowledgment + && m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA); + WifiUlMuMultiStaBa* acknowledgment = static_cast (m_txParams.m_acknowledgment.get ()); + std::size_t index = acknowledgment->baType.m_bitmapLen.size (); + + if (m_staExpectTbPpduFrom.find (sender) == m_staExpectTbPpduFrom.end ()) + { + NS_LOG_WARN ("Received a TB PPDU from an unexpected station: " << sender); + return; + } + + if (hdr.IsBlockAckReq ()) + { + NS_LOG_DEBUG ("Received a BlockAckReq in a TB PPDU from " << sender); + + CtrlBAckRequestHeader blockAckReq; + mpdu->GetPacket ()->PeekHeader (blockAckReq); + NS_ABORT_MSG_IF (blockAckReq.IsMultiTid (), "Multi-TID BlockAckReq not supported"); + uint8_t tid = blockAckReq.GetTidInfo (); + auto agreementIt = m_agreements.find ({sender, tid}); + NS_ASSERT (agreementIt != m_agreements.end ()); + agreementIt->second.NotifyReceivedBar (blockAckReq.GetStartingSequence ()); + + // Block Acknowledgment context + acknowledgment->stationsReceivingMultiStaBa.emplace (std::make_pair (sender, tid), index); + acknowledgment->baType.m_bitmapLen.push_back (GetBlockAckType (sender, tid).m_bitmapLen.at (0)); + uint16_t staId = txVector.GetHeMuUserInfoMap ().begin ()->first; + m_muSnrTag.Set (staId, rxSignalInfo.snr); + } + else if (hdr.IsQosData () && !inAmpdu && hdr.GetQosAckPolicy () == WifiMacHeader::NORMAL_ACK) + { + NS_LOG_DEBUG ("Received an S-MPDU in a TB PPDU from " << sender << " (" << *mpdu << ")"); + + uint8_t tid = hdr.GetQosTid (); + auto agreementIt = m_agreements.find ({sender, tid}); + NS_ASSERT (agreementIt != m_agreements.end ()); + agreementIt->second.NotifyReceivedMpdu (mpdu); + + // Acknowledgment context of Multi-STA Block Acks + acknowledgment->stationsReceivingMultiStaBa.emplace (std::make_pair (sender, tid), index); + acknowledgment->baType.m_bitmapLen.push_back (0); + uint16_t staId = txVector.GetHeMuUserInfoMap ().begin ()->first; + m_muSnrTag.Set (staId, rxSignalInfo.snr); + } + else if (!(hdr.IsQosData () && !hdr.HasData () && !inAmpdu)) + { + // The other case handled by this function is when we receive a QoS Null frame + // that is not in an A-MPDU. For all other cases, the reception is handled by + // parent classes. In particular, in case of a QoS data frame in A-MPDU, we + // have to wait until the A-MPDU reception is completed, but we let the + // parent classes notify the Block Ack agreement of the reception of this MPDU + VhtFrameExchangeManager::ReceiveMpdu (mpdu, rxSignalInfo, txVector, inAmpdu); + return; + } + + // Schedule the transmission of a Multi-STA BlockAck frame if needed + if (!acknowledgment->stationsReceivingMultiStaBa.empty () && !m_multiStaBaEvent.IsRunning ()) + { + m_multiStaBaEvent = Simulator::Schedule (m_phy->GetSifs (), + &HeFrameExchangeManager::SendMultiStaBlockAck, + this, std::cref (m_txParams)); + } + + // remove the sender from the set of stations that are expected to send a TB PPDU + m_staExpectTbPpduFrom.erase (sender); + + if (m_staExpectTbPpduFrom.empty ()) + { + // we do not expect any other BlockAck frame + m_txTimer.Cancel (); + m_channelAccessManager->NotifyAckTimeoutResetNow (); + + if (!m_multiStaBaEvent.IsRunning ()) + { + // all of the stations that replied with a TB PPDU sent QoS Null frames. + NS_LOG_DEBUG ("Continue the TXOP"); + m_psduMap.clear (); + m_edca->ResetCw (); + TransmissionSucceeded (); + } + } + + // the received TB PPDU has been processed + return; + } + if (hdr.IsCtl ()) { if (hdr.IsAck () && m_txTimer.IsRunning () @@ -927,6 +1453,64 @@ HeFrameExchangeManager::ReceiveMpdu (Ptr mpdu, RxSignalInfo rx TransmissionSucceeded (); } } + else if (hdr.IsBlockAck () && m_txTimer.IsRunning () + && m_txTimer.GetReason () == WifiTxTimer::WAIT_BLOCK_ACK_AFTER_TB_PPDU) + { + CtrlBAckResponseHeader blockAck; + mpdu->GetPacket ()->PeekHeader (blockAck); + + NS_ABORT_MSG_IF (!blockAck.IsMultiSta (), + "A Multi-STA BlockAck is expected after a TB PPDU"); + NS_LOG_DEBUG ("Received a Multi-STA BlockAck from=" << hdr.GetAddr2 ()); + + NS_ASSERT (m_staMac != nullptr && m_staMac->IsAssociated ()); + uint16_t staId = m_staMac->GetAssociationId (); + std::vector indices = blockAck.FindPerAidTidInfoWithAid (staId); + + if (indices.empty ()) + { + NS_LOG_DEBUG ("No Per AID TID Info subfield intended for me"); + return; + } + + MuSnrTag tag; + mpdu->GetPacket ()->PeekPacketTag (tag); + + // notify the Block Ack Manager + for (const auto& index : indices) + { + uint8_t tid = blockAck.GetTidInfo (index); + + if (blockAck.GetAckType (index) && tid < 8) + { + // Acknowledgment context + NS_ABORT_IF (m_psduMap.empty () || m_psduMap.begin ()->first != staId); + GetBaManager (tid)->NotifyGotAck (*m_psduMap.at (staId)->begin ()); + } + else + { + // Block Acknowledgment or All-ack context + if (blockAck.GetAckType (index) && tid == 14) + { + // All-ack context, we need to determine the actual TID(s) of the PSDU + NS_ASSERT (indices.size () == 1); + NS_ABORT_IF (m_psduMap.empty () || m_psduMap.begin ()->first != staId); + std::set tids = m_psduMap.at (staId)->GetTids (); + NS_ABORT_MSG_IF (tids.size () > 1, "Multi-TID A-MPDUs not supported yet"); + tid = *tids.begin (); + } + + GetBaManager (tid)->NotifyGotBlockAck (&blockAck, hdr.GetAddr2 (), {tid}, + rxSignalInfo.snr, tag.Get (staId), + m_txParams.m_txVector, index); + } + } + + // cancel the timer + m_txTimer.Cancel (); + m_channelAccessManager->NotifyAckTimeoutResetNow (); + m_psduMap.clear (); + } else if (hdr.IsTrigger ()) { // Trigger Frames are only processed by STAs @@ -984,6 +1568,16 @@ HeFrameExchangeManager::ReceiveMpdu (Ptr mpdu, RxSignalInfo rx agreementIt->second, hdr.GetDuration (), GetHeTbTxVector (trigger, hdr.GetAddr2 ()), rxSignalInfo.snr); } + else if (trigger.IsBasic ()) + { + Simulator::Schedule (m_phy->GetSifs (), &HeFrameExchangeManager::ReceiveBasicTrigger, + this, trigger, hdr); + } + else if (trigger.IsBsrp ()) + { + Simulator::Schedule (m_phy->GetSifs (), &HeFrameExchangeManager::SendQosNullFramesInTbPpdu, + this, trigger, hdr); + } } else { @@ -1005,6 +1599,118 @@ HeFrameExchangeManager::EndReceiveAmpdu (Ptr psdu, const RxSigna { std::set tids = psdu->GetTids (); + if (txVector.IsUlMu () && m_txTimer.IsRunning () + && m_txTimer.GetReason () == WifiTxTimer::WAIT_TB_PPDU_AFTER_BASIC_TF) + { + Mac48Address sender = psdu->GetAddr2 (); + NS_ASSERT (m_txParams.m_acknowledgment + && m_txParams.m_acknowledgment->method == WifiAcknowledgment::UL_MU_MULTI_STA_BA); + WifiUlMuMultiStaBa* acknowledgment = static_cast (m_txParams.m_acknowledgment.get ()); + std::size_t index = acknowledgment->baType.m_bitmapLen.size (); + + if (m_staExpectTbPpduFrom.find (sender) == m_staExpectTbPpduFrom.end ()) + { + NS_LOG_WARN ("Received a TB PPDU from an unexpected station: " << sender); + return; + } + + NS_LOG_DEBUG ("Received an A-MPDU in a TB PPDU from " << sender << " (" << *psdu << ")"); + + if (std::any_of (tids.begin (), tids.end (), + [&psdu](uint8_t tid) + { return psdu->GetAckPolicyForTid (tid) == WifiMacHeader::NORMAL_ACK; })) + { + if (std::all_of (perMpduStatus.cbegin (), perMpduStatus.cend (), [](bool v) { return v; })) + { + // All-ack context + acknowledgment->stationsReceivingMultiStaBa.emplace (std::make_pair (sender, 14), index); + acknowledgment->baType.m_bitmapLen.push_back (0); + } + else + { + // Block Acknowledgment context + std::size_t i = 0; + for (const auto& tid : tids) + { + acknowledgment->stationsReceivingMultiStaBa.emplace (std::make_pair (sender, tid), index + i++); + acknowledgment->baType.m_bitmapLen.push_back (GetBlockAckType (sender, tid).m_bitmapLen.at (0)); + } + } + uint16_t staId = txVector.GetHeMuUserInfoMap ().begin ()->first; + m_muSnrTag.Set (staId, rxSignalInfo.snr); + } + + // Schedule the transmission of a Multi-STA BlockAck frame if needed + if (!acknowledgment->stationsReceivingMultiStaBa.empty () && !m_multiStaBaEvent.IsRunning ()) + { + m_multiStaBaEvent = Simulator::Schedule (m_phy->GetSifs (), + &HeFrameExchangeManager::SendMultiStaBlockAck, + this, std::cref (m_txParams)); + } + + // remove the sender from the set of stations that are expected to send a TB PPDU + m_staExpectTbPpduFrom.erase (sender); + + if (m_staExpectTbPpduFrom.empty ()) + { + // we do not expect any other BlockAck frame + m_txTimer.Cancel (); + m_channelAccessManager->NotifyAckTimeoutResetNow (); + + if (!m_multiStaBaEvent.IsRunning ()) + { + // all of the stations that replied with a TB PPDU sent QoS Null frames. + NS_LOG_DEBUG ("Continue the TXOP"); + m_psduMap.clear (); + m_edca->ResetCw (); + TransmissionSucceeded (); + } + } + + // the received TB PPDU has been processed + return; + } + + if (txVector.IsUlMu () && m_txTimer.IsRunning () + && m_txTimer.GetReason () == WifiTxTimer::WAIT_QOS_NULL_AFTER_BSRP_TF) + { + Mac48Address sender = psdu->GetAddr2 (); + + if (m_staExpectTbPpduFrom.find (sender) == m_staExpectTbPpduFrom.end ()) + { + NS_LOG_WARN ("Received a TB PPDU from an unexpected station: " << sender); + return; + } + if (std::none_of (psdu->begin (), psdu->end (), [](Ptr mpdu) + { return mpdu->GetHeader ().IsQosData () + && !mpdu->GetHeader ().HasData (); + })) + { + NS_LOG_WARN ("No QoS Null frame in the received PSDU"); + return; + } + + NS_LOG_DEBUG ("Received QoS Null frames in a TB PPDU from " << sender); + + // remove the sender from the set of stations that are expected to send a TB PPDU + m_staExpectTbPpduFrom.erase (sender); + + if (m_staExpectTbPpduFrom.empty ()) + { + // we do not expect any other response + m_txTimer.Cancel (); + m_channelAccessManager->NotifyAckTimeoutResetNow (); + + NS_ASSERT (m_edca != 0); + m_psduMap.clear (); + m_edca->ResetCw (); + TransmissionSucceeded (); + } + + // the received TB PPDU has been processed + return; + } + if (m_triggerFrameInAmpdu) { // the received A-MPDU contains a Trigger Frame. It is now time to handle it. diff --git a/src/wifi/model/he/he-frame-exchange-manager.h b/src/wifi/model/he/he-frame-exchange-manager.h index ffbed49c1..bf74fe2eb 100644 --- a/src/wifi/model/he/he-frame-exchange-manager.h +++ b/src/wifi/model/he/he-frame-exchange-manager.h @@ -22,6 +22,7 @@ #define HE_FRAME_EXCHANGE_MANAGER_H #include "ns3/vht-frame-exchange-manager.h" +#include "mu-snr-tag.h" #include #include @@ -126,6 +127,28 @@ protected: const std::set* staMissedBlockAckFrom, std::size_t nSolicitedStations); + /** + * Take the necessary actions after that some TB PPDUs are missing in + * response to Trigger Frame. This method must not be called if all the + * expected TB PPDUs were received. + * + * \param psduMap a pointer to PSDU map transmitted in a DL MU PPDU + * \param staMissedTbPpduFrom set of stations we missed a TB PPDU from + * \param nSolicitedStations the number of stations solicited to send a TB PPDU + */ + virtual void TbPpduTimeout (WifiPsduMap* psduMap, + const std::set* staMissedTbPpduFrom, + std::size_t nSolicitedStations); + + /** + * Take the necessary actions after that a Block Ack is missing after a + * TB PPDU solicited through a Trigger Frame. + * + * \param psdu the PSDU in the TB PPDU + * \param txVector the TXVECTOR used to transmit the TB PPDU + */ + virtual void BlockAckAfterTbPpduTimeout (Ptr psdu, const WifiTxVector& txVector); + /** * Return a TXVECTOR for the UL frame that the station will send in response to * the given Trigger frame, configured with the BSS color and transmit power @@ -153,6 +176,23 @@ protected: Ptr PrepareMuBar (const WifiTxVector& responseTxVector, std::map recipients) const; + /** + * Send a Multi-STA Block Ack frame after the reception of some TB PPDUs. + * + * \param txParams the TX parameters for the Trigger Frame that solicited the TB PPDUs + */ + void SendMultiStaBlockAck (const WifiTxParameters& txParams); + + /** + * Send QoS Null frames in response to a Basic or BSRP Trigger Frame. The number + * of QoS Null frames that are actually aggregated depends on the available time + * as indicated by the Trigger Frame and is at most 8 (one QoS Null frame per TID). + * + * \param trigger the Basic or BSRP Trigger Frame content + * \param hdr the MAC header of the Basic or BSRP Trigger Frame + */ + void SendQosNullFramesInTbPpdu (const CtrlTriggerHeader& trigger, const WifiMacHeader& hdr); + Ptr m_apMac; //!< MAC pointer (null if not an AP) Ptr m_staMac; //!< MAC pointer (null if not a STA) @@ -162,11 +202,21 @@ private: */ void SendPsduMap (void); + /** + * Take the necessary actions when receiveing a Basic Trigger Frame. + * + * \param trigger the Basic Trigger Frame content + * \param hdr the MAC header of the Basic Trigger Frame + */ + void ReceiveBasicTrigger (const CtrlTriggerHeader& trigger, const WifiMacHeader& hdr); + WifiPsduMap m_psduMap; //!< the A-MPDU being transmitted WifiTxParameters m_txParams; //!< the TX parameters for the current PPDU Ptr m_muScheduler; //!< Multi-user Scheduler (HE APs only) Ptr m_triggerFrame; //!< Trigger Frame being sent std::set m_staExpectTbPpduFrom; //!< set of stations expected to send a TB PPDU + EventId m_multiStaBaEvent; //!< Sending a Multi-STA BlockAck event + MuSnrTag m_muSnrTag; //!< Tag to attach to Multi-STA BlockAck frames bool m_triggerFrameInAmpdu; //!< True if the received A-MPDU contains an MU-BAR }; diff --git a/src/wifi/model/ht/ht-frame-exchange-manager.cc b/src/wifi/model/ht/ht-frame-exchange-manager.cc index e53398035..919775037 100644 --- a/src/wifi/model/ht/ht-frame-exchange-manager.cc +++ b/src/wifi/model/ht/ht-frame-exchange-manager.cc @@ -1263,7 +1263,7 @@ HtFrameExchangeManager::SendBlockAck (const RecipientBlockAckAgreement& agreemen // time, in microseconds between the end of the PPDU carrying the frame that // elicited the response and the end of the PPDU carrying the BlockAck frame. Time baDurationId = durationId - m_phy->GetSifs () - - m_phy->CalculateTxDuration (psdu->GetSize (), blockAckTxVector, m_phy->GetPhyBand ()); + - m_phy->CalculateTxDuration (psdu, blockAckTxVector, m_phy->GetPhyBand ()); // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8 of 802.11-2016) if (baDurationId.IsStrictlyNegative ()) { diff --git a/src/wifi/model/regular-wifi-mac.cc b/src/wifi/model/regular-wifi-mac.cc index a5e0d1e55..303e1a20e 100644 --- a/src/wifi/model/regular-wifi-mac.cc +++ b/src/wifi/model/regular-wifi-mac.cc @@ -1076,8 +1076,9 @@ RegularWifiMac::GetTypeId (void) "A PSDU whose response was not received before the timeout, along with " "an identifier of the type of timeout (see WifiTxTimer::Reason) and the " "TXVECTOR used to transmit the PSDU. This trace source is fired when a " - "BlockAck is missing after an A-MPDU or a BlockAckReq (possibly in the " - "context of the acknowledgment of a DL MU PPDU in SU format).", + "BlockAck is missing after an A-MPDU, a BlockAckReq (possibly in the " + "context of the acknowledgment of a DL MU PPDU in SU format) or a TB PPDU " + "(in the latter case the missing BlockAck is a Multi-STA BlockAck).", MakeTraceSourceAccessor (&RegularWifiMac::m_psduResponseTimeoutCallback), "ns3::RegularWifiMac::PsduResponseTimeoutCallback") .AddTraceSource ("PsduMapResponseTimeout", @@ -1085,8 +1086,9 @@ RegularWifiMac::GetTypeId (void) "along with an identifier of the type of timeout (see WifiTxTimer::Reason), " "the set of MAC addresses of the stations that did not respond and the total " "number of stations that had to respond. This trace source is fired when not " - "all the addressed stations respond to an MU-BAR Trigger frame (either sent as " - "a SU frame or aggregated to PSDUs in the DL MU PPDU.", + "all the addressed stations responded to an MU-BAR Trigger frame (either sent as " + "a SU frame or aggregated to PSDUs in the DL MU PPDU), a Basic Trigger Frame or " + "a BSRP Trigger Frame.", MakeTraceSourceAccessor (&RegularWifiMac::m_psduMapResponseTimeoutCallback), "ns3::RegularWifiMac::PsduMapResponseTimeoutCallback") ; diff --git a/src/wifi/model/wifi-tx-timer.cc b/src/wifi/model/wifi-tx-timer.cc index 8b45dce3b..bd4f77ca0 100644 --- a/src/wifi/model/wifi-tx-timer.cc +++ b/src/wifi/model/wifi-tx-timer.cc @@ -81,6 +81,9 @@ case WAIT_ ## x: \ FOO (BLOCK_ACK); FOO (NORMAL_ACK_AFTER_DL_MU_PPDU); FOO (BLOCK_ACKS_IN_TB_PPDU); + FOO (TB_PPDU_AFTER_BASIC_TF); + FOO (QOS_NULL_AFTER_BSRP_TF); + FOO (BLOCK_ACK_AFTER_TB_PPDU); default: NS_ABORT_MSG ("Unknown reason"); } diff --git a/src/wifi/model/wifi-tx-timer.h b/src/wifi/model/wifi-tx-timer.h index c9d21d8a7..b27b01f46 100644 --- a/src/wifi/model/wifi-tx-timer.h +++ b/src/wifi/model/wifi-tx-timer.h @@ -59,6 +59,9 @@ public: WAIT_BLOCK_ACK, WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU, WAIT_BLOCK_ACKS_IN_TB_PPDU, + WAIT_TB_PPDU_AFTER_BASIC_TF, + WAIT_QOS_NULL_AFTER_BSRP_TF, + WAIT_BLOCK_ACK_AFTER_TB_PPDU, }; /** Default constructor */