mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
wifi: mac80211_hwsim: add NAN data path TX/RX support
Implement TX and RX path handling for NAN Data Path (NDP) frames, enabling data communication between NAN peers during scheduled availability windows. TX path: - Select TX channel based on current time slot: use DW channel during Discovery Windows, or FAW channel from local schedule during Further Availability Windows. - Verify peer availability before transmission by checking committed DW schedule or FAW of the peer schedule. RX path: - Extend NAN receive filtering to handle NAN_DATA interface frames. - Accept incoming frames during FAW slots when channel matches local schedule. Signed-off-by: Daniel Gabay <daniel.gabay@intel.com> Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com> Link: https://patch.msgid.link/20260506064301.155252ebc72b.Ic210f6c095c6ff372941bc8c77ee9c8c37d0356c@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
60e3398726
commit
2c8ec5a9b1
|
|
@ -1744,7 +1744,8 @@ static void mac80211_hwsim_tx_iter(void *_data, u8 *addr,
|
|||
struct tx_iter_data *data = _data;
|
||||
int i;
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN) {
|
||||
if (vif->type == NL80211_IFTYPE_NAN ||
|
||||
vif->type == NL80211_IFTYPE_NAN_DATA) {
|
||||
data->receive = mac80211_hwsim_nan_receive(data->hw,
|
||||
data->channel,
|
||||
data->rx_status);
|
||||
|
|
@ -2093,13 +2094,19 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
|
|||
/* re-assign hdr since skb data may have shifted after encryption */
|
||||
hdr = (void *)skb->data;
|
||||
|
||||
if (vif && vif->type == NL80211_IFTYPE_NAN && !data->tmp_chan) {
|
||||
channel = mac80211_hwsim_nan_get_tx_channel(hw);
|
||||
if (vif && !data->tmp_chan &&
|
||||
(vif->type == NL80211_IFTYPE_NAN ||
|
||||
vif->type == NL80211_IFTYPE_NAN_DATA)) {
|
||||
struct cfg80211_chan_def chandef;
|
||||
|
||||
if (WARN_ON(!channel)) {
|
||||
mac80211_hwsim_nan_get_tx_chandef(hw, &chandef);
|
||||
if (WARN_ON(!chandef.chan)) {
|
||||
/* No valid channel in current slot, drop frame */
|
||||
ieee80211_free_txskb(hw, skb);
|
||||
return;
|
||||
}
|
||||
channel = chandef.chan;
|
||||
confbw = chandef.width;
|
||||
} else if (!data->use_chanctx) {
|
||||
channel = data->channel;
|
||||
confbw = data->bw;
|
||||
|
|
@ -2249,7 +2256,8 @@ void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw,
|
|||
};
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (txq->vif->type == NL80211_IFTYPE_NAN &&
|
||||
if ((txq->vif->type == NL80211_IFTYPE_NAN ||
|
||||
txq->vif->type == NL80211_IFTYPE_NAN_DATA) &&
|
||||
!mac80211_hwsim_nan_txq_transmitting(hw, txq))
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ static_assert(DW0_TSF_MASK + 1 == 8192 * 1024);
|
|||
/* Quiet time at the end of each slot where TX is suppressed */
|
||||
#define NAN_CHAN_SWITCH_TIME_US 256
|
||||
|
||||
struct hwsim_nan_sta_iter_ctx {
|
||||
struct ieee80211_hw *hw;
|
||||
bool can_tx;
|
||||
};
|
||||
|
||||
static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data);
|
||||
|
||||
static u64 hwsim_nan_get_timer_tsf(struct mac80211_hwsim_data *data)
|
||||
|
|
@ -77,6 +82,109 @@ static u64 hwsim_nan_get_master_rank(struct mac80211_hwsim_data *data)
|
|||
data->nan.device_vif->addr);
|
||||
}
|
||||
|
||||
static bool mac80211_hwsim_nan_is_dw_slot(struct mac80211_hwsim_data *data,
|
||||
u8 slot)
|
||||
{
|
||||
return slot == SLOT_24GHZ_DW ||
|
||||
(slot == SLOT_5GHZ_DW &&
|
||||
(data->nan.bands & BIT(NL80211_BAND_5GHZ)));
|
||||
}
|
||||
|
||||
static bool
|
||||
hwsim_nan_rx_chandef_compatible(struct mac80211_hwsim_data *data, u8 slot,
|
||||
struct ieee80211_channel *rx_chan, u8 rx_bw)
|
||||
{
|
||||
static const int bw_to_mhz[] = {
|
||||
[RATE_INFO_BW_20] = 20, [RATE_INFO_BW_40] = 40,
|
||||
[RATE_INFO_BW_80] = 80, [RATE_INFO_BW_160] = 160,
|
||||
};
|
||||
struct cfg80211_chan_def sched_chandef;
|
||||
int rx_mhz, sched_mhz;
|
||||
|
||||
scoped_guard(spinlock_bh, &data->nan.state_lock)
|
||||
sched_chandef = data->nan.local_sched[slot];
|
||||
|
||||
if (!sched_chandef.chan ||
|
||||
sched_chandef.chan->center_freq != rx_chan->center_freq)
|
||||
return false;
|
||||
|
||||
if (rx_bw >= ARRAY_SIZE(bw_to_mhz) || !bw_to_mhz[rx_bw])
|
||||
return false;
|
||||
|
||||
rx_mhz = bw_to_mhz[rx_bw];
|
||||
sched_mhz = cfg80211_chandef_get_width(&sched_chandef);
|
||||
|
||||
/* Accept RX at narrower or equal bandwidth */
|
||||
return rx_mhz <= sched_mhz;
|
||||
}
|
||||
|
||||
static bool hwsim_nan_peer_present_in_dw(struct hwsim_sta_priv *sp, u64 tsf)
|
||||
{
|
||||
u8 slot = hwsim_nan_slot_from_tsf(tsf);
|
||||
u8 cdw = 0;
|
||||
u8 dw_index, wake_interval;
|
||||
u16 committed_dw;
|
||||
|
||||
scoped_guard(spinlock_bh, &sp->nan_sched.lock)
|
||||
committed_dw = sp->nan_sched.committed_dw;
|
||||
|
||||
/* If peer doesn't advertise committed DW, assume presence in
|
||||
* all 2.4 GHz DW slots
|
||||
*/
|
||||
if (!committed_dw)
|
||||
return slot == SLOT_24GHZ_DW;
|
||||
|
||||
/* Get DW index (0-15) within the 16-DWST DW0 cycle */
|
||||
dw_index = (tsf / ieee80211_tu_to_usec(DWST_TU)) & 0xf;
|
||||
|
||||
/* Extract CDW for the appropriate band (spec Table 80) */
|
||||
if (slot == SLOT_24GHZ_DW)
|
||||
cdw = committed_dw & 0x7;
|
||||
else if (slot == SLOT_5GHZ_DW)
|
||||
cdw = (committed_dw >> 3) & 0x7;
|
||||
|
||||
if (cdw == 0)
|
||||
return false;
|
||||
|
||||
/* Peer wakes every 2^(cdw-1) DWs: 1, 2, 4, 8, or 16 */
|
||||
wake_interval = 1 << (cdw - 1);
|
||||
|
||||
return (dw_index % wake_interval) == 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
hwsim_nan_peer_present_in_faw(struct hwsim_sta_priv *sp,
|
||||
struct mac80211_hwsim_data *data, u8 slot)
|
||||
{
|
||||
struct cfg80211_chan_def local_chandef;
|
||||
|
||||
scoped_guard(spinlock_bh, &data->nan.state_lock)
|
||||
local_chandef = data->nan.local_sched[slot];
|
||||
|
||||
if (!local_chandef.chan)
|
||||
return false;
|
||||
|
||||
scoped_guard(spinlock_bh, &sp->nan_sched.lock) {
|
||||
for (int i = 0; i < CFG80211_NAN_MAX_PEER_MAPS; i++) {
|
||||
struct cfg80211_chan_def *peer_chandef;
|
||||
|
||||
if (sp->nan_sched.maps[i].map_id ==
|
||||
CFG80211_NAN_INVALID_MAP_ID)
|
||||
continue;
|
||||
|
||||
peer_chandef = &sp->nan_sched.maps[i].chans[slot];
|
||||
if (!peer_chandef->chan)
|
||||
continue;
|
||||
|
||||
if (cfg80211_chandef_compatible(&local_chandef,
|
||||
peer_chandef))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot,
|
||||
bool discontinuity)
|
||||
|
|
@ -880,15 +988,65 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data)
|
||||
static void hwsim_nan_can_sta_transmit(void *_ctx, struct ieee80211_sta *sta)
|
||||
{
|
||||
u32 timeout_ns;
|
||||
struct hwsim_nan_sta_iter_ctx *ctx = _ctx;
|
||||
|
||||
/* Nothing to do if we are not in a DW */
|
||||
if (!mac80211_hwsim_nan_txq_transmitting(data->hw,
|
||||
data->nan.device_vif->txq_mgmt))
|
||||
if (ctx->can_tx)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) {
|
||||
struct ieee80211_txq *txq = sta->txq[i];
|
||||
|
||||
if (!txq)
|
||||
continue;
|
||||
|
||||
if (txq->vif->type != NL80211_IFTYPE_NAN &&
|
||||
txq->vif->type != NL80211_IFTYPE_NAN_DATA)
|
||||
return;
|
||||
|
||||
if (mac80211_hwsim_nan_txq_transmitting(ctx->hw, txq)) {
|
||||
ctx->can_tx = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data)
|
||||
{
|
||||
u64 tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
|
||||
u8 slot = hwsim_nan_slot_from_tsf(tsf);
|
||||
bool is_dw_slot = mac80211_hwsim_nan_is_dw_slot(data, slot);
|
||||
struct hwsim_nan_sta_iter_ctx ctx = {
|
||||
.hw = data->hw,
|
||||
.can_tx = false,
|
||||
};
|
||||
u32 timeout_ns;
|
||||
|
||||
/* Outside DW, require local FAW schedule to proceed */
|
||||
if (!is_dw_slot) {
|
||||
scoped_guard(spinlock_bh, &data->nan.state_lock) {
|
||||
if (!data->nan.local_sched[slot].chan)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
guard(rcu)();
|
||||
|
||||
/* Check if management queue can transmit */
|
||||
if (mac80211_hwsim_nan_txq_transmitting(data->hw,
|
||||
data->nan.device_vif->txq_mgmt))
|
||||
goto resume_txqs_timer;
|
||||
|
||||
/* Check if any STA queue can transmit */
|
||||
ieee80211_iterate_stations_atomic(data->hw,
|
||||
hwsim_nan_can_sta_transmit,
|
||||
&ctx);
|
||||
|
||||
if (!ctx.can_tx)
|
||||
return;
|
||||
|
||||
resume_txqs_timer:
|
||||
/*
|
||||
* Wait a bit and also randomize things so that not everyone is TXing
|
||||
* at the same time. Each slot is 16 TU long, this waits between 100 us
|
||||
|
|
@ -902,6 +1060,26 @@ static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data)
|
|||
HRTIMER_MODE_REL_SOFT);
|
||||
}
|
||||
|
||||
static void hwsim_nan_wake_sta_iter(void *_data, struct ieee80211_sta *sta)
|
||||
{
|
||||
struct ieee80211_hw *hw = _data;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) {
|
||||
struct ieee80211_txq *txq = sta->txq[i];
|
||||
|
||||
if (!txq)
|
||||
continue;
|
||||
|
||||
/* exit early if non-NAN */
|
||||
if (txq->vif->type != NL80211_IFTYPE_NAN &&
|
||||
txq->vif->type != NL80211_IFTYPE_NAN_DATA)
|
||||
return;
|
||||
|
||||
if (mac80211_hwsim_nan_txq_transmitting(hw, txq))
|
||||
ieee80211_hwsim_wake_tx_queue(hw, txq);
|
||||
}
|
||||
}
|
||||
|
||||
enum hrtimer_restart
|
||||
mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer)
|
||||
{
|
||||
|
|
@ -917,6 +1095,11 @@ mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer)
|
|||
ieee80211_hwsim_wake_tx_queue(data->hw,
|
||||
data->nan.device_vif->txq_mgmt);
|
||||
|
||||
/* Wake TX queues for all stations */
|
||||
ieee80211_iterate_stations_atomic(data->hw,
|
||||
hwsim_nan_wake_sta_iter,
|
||||
data->hw);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
|
|
@ -924,6 +1107,9 @@ bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
|
|||
struct ieee80211_txq *txq)
|
||||
{
|
||||
struct mac80211_hwsim_data *data = hw->priv;
|
||||
struct ieee80211_sta *nmi_sta;
|
||||
struct hwsim_sta_priv *sp;
|
||||
bool is_dw_slot;
|
||||
u64 tsf;
|
||||
u8 slot;
|
||||
|
||||
|
|
@ -937,36 +1123,54 @@ bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
|
|||
if (slot != hwsim_nan_slot_from_tsf(tsf + NAN_CHAN_SWITCH_TIME_US))
|
||||
return false;
|
||||
|
||||
/* Check NAN device interface management frame transmission */
|
||||
if (!txq->sta) {
|
||||
/* Only transmit these during one of the DWs */
|
||||
if (slot == SLOT_24GHZ_DW ||
|
||||
(slot == SLOT_5GHZ_DW &&
|
||||
(data->nan.bands & BIT(NL80211_BAND_5GHZ))))
|
||||
return true;
|
||||
is_dw_slot = mac80211_hwsim_nan_is_dw_slot(data, slot);
|
||||
|
||||
return false;
|
||||
/* Non-STA TXQ: allow management frames during DW */
|
||||
if (!txq->sta)
|
||||
return is_dw_slot;
|
||||
|
||||
/* STA TXQ: need peer schedule for availability check */
|
||||
nmi_sta = rcu_dereference(txq->sta->nmi) ?: txq->sta;
|
||||
sp = (void *)nmi_sta->drv_priv;
|
||||
|
||||
/* DW slot: NDI can TX only mgmt but not worth checking,
|
||||
* NMI checks peer's committed DW
|
||||
*/
|
||||
if (is_dw_slot) {
|
||||
if (txq->vif->type == NL80211_IFTYPE_NAN_DATA)
|
||||
return false;
|
||||
return hwsim_nan_peer_present_in_dw(sp, tsf);
|
||||
}
|
||||
|
||||
return true;
|
||||
/* FAW slot: verify local schedule and peer availability */
|
||||
return hwsim_nan_peer_present_in_faw(sp, data, slot);
|
||||
}
|
||||
|
||||
struct ieee80211_channel *
|
||||
mac80211_hwsim_nan_get_tx_channel(struct ieee80211_hw *hw)
|
||||
void mac80211_hwsim_nan_get_tx_chandef(struct ieee80211_hw *hw,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct mac80211_hwsim_data *data = hw->priv;
|
||||
u64 tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
|
||||
u8 slot = hwsim_nan_slot_from_tsf(tsf);
|
||||
|
||||
if (slot == SLOT_24GHZ_DW)
|
||||
return ieee80211_get_channel(hw->wiphy, 2437);
|
||||
/* DW slots are always 20 MHz */
|
||||
if (slot == SLOT_24GHZ_DW) {
|
||||
cfg80211_chandef_create(chandef,
|
||||
ieee80211_get_channel(hw->wiphy, 2437),
|
||||
NL80211_CHAN_NO_HT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot == SLOT_5GHZ_DW &&
|
||||
data->nan.bands & BIT(NL80211_BAND_5GHZ))
|
||||
return ieee80211_get_channel(hw->wiphy, 5745);
|
||||
if (slot == SLOT_5GHZ_DW && data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
|
||||
cfg80211_chandef_create(chandef,
|
||||
ieee80211_get_channel(hw->wiphy, 5745),
|
||||
NL80211_CHAN_NO_HT);
|
||||
return;
|
||||
}
|
||||
|
||||
/* drop frame and warn, NAN_CHAN_SWITCH_TIME_US should avoid races */
|
||||
return NULL;
|
||||
/* FAW slot: copy local schedule for this slot */
|
||||
scoped_guard(spinlock_bh, &data->nan.state_lock)
|
||||
*chandef = data->nan.local_sched[slot];
|
||||
}
|
||||
|
||||
bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,
|
||||
|
|
@ -1006,7 +1210,9 @@ bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,
|
|||
channel->center_freq == 5745)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
/* Accept frames during FAW slots if chandef is compatible */
|
||||
return hwsim_nan_rx_chandef_compatible(data, slot, channel,
|
||||
rx_status->bw);
|
||||
}
|
||||
|
||||
void mac80211_hwsim_nan_local_sched_changed(struct ieee80211_hw *hw,
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ int mac80211_hwsim_nan_peer_sched_changed(struct ieee80211_hw *hw,
|
|||
bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
|
||||
struct ieee80211_txq *txq);
|
||||
|
||||
struct ieee80211_channel *
|
||||
mac80211_hwsim_nan_get_tx_channel(struct ieee80211_hw *hw);
|
||||
void mac80211_hwsim_nan_get_tx_chandef(struct ieee80211_hw *hw,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
|
||||
bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,
|
||||
struct ieee80211_channel *channel,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user