Skip to content
2 changes: 2 additions & 0 deletions drivers/net/wireless/ath/ath12k/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1515,6 +1515,8 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
}

wiphy_unlock(ah->hw->wiphy);

complete(&ah->peer_ml_id_done);
}

wake_up(&ab->wmi_ab.tx_credits_wq);
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/wireless/ath/ath12k/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@

#define ATH12K_MAX_MLO_PEERS 256
#define ATH12K_MLO_PEER_ID_INVALID 0xFFFF
#define ATH12K_MLO_PEER_ID_PENDING 0xFFFE

#define ATH12K_INVALID_RSSI_FULL -1
#define ATH12K_INVALID_RSSI_EMPTY -128
Expand Down Expand Up @@ -770,6 +771,8 @@ struct ath12k_hw {
enum ath12k_hw_state state;
bool regd_updated;
bool use_6ghz_regd;
bool host_alloc_ml_id;
struct completion peer_ml_id_done;

u8 num_radio;

Expand Down
49 changes: 49 additions & 0 deletions drivers/net/wireless/ath/ath12k/dp_htt.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "core.h"
#include "peer.h"
#include "dp_peer.h"
#include "htc.h"
#include "dp_htt.h"
#include "debugfs_htt_stats.h"
Expand Down Expand Up @@ -573,6 +574,51 @@ static void ath12k_htt_mlo_offset_event_handler(struct ath12k_base *ab,
rcu_read_unlock();
}

static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab,
struct sk_buff *skb)
{
struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data;
struct htt_t2h_mlo_peer_map_event *ev = &resp->mlo_peer_map_ev;
u16 raw_peer_id, peer_id, addr_h16;
u8 peer_addr[ETH_ALEN];
int ret;

if (skb->len < sizeof(*ev)) {
ath12k_warn(ab, "unexpected htt mlo peer map event len %u\n",
skb->len);
return;
}

raw_peer_id = le32_get_bits(ev->info0,
HTT_T2H_MLO_PEER_MAP_INFO0_MLO_PEER_ID);
peer_id = raw_peer_id | ATH12K_PEER_ML_ID_VALID;

addr_h16 = le32_get_bits(ev->info1,
HTT_T2H_MLO_PEER_MAP_INFO1_MAC_ADDR_H16);
ath12k_dp_get_mac_addr(le32_to_cpu(ev->mac_addr_l32), addr_h16,
peer_addr);

ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt mlo peer map peer %pM id %u\n",
peer_addr, peer_id);

/*
* Fix up the dp_peer entry created with ATH12K_MLO_PEER_ID_PENDING
* earlier; on chips with host_alloc_ml_id == false this is the only
* point at which the host learns the firmware-assigned ID. Chips
* that allocate the ID on the host also receive this event but the
* firmware-reported ID matches the host-allocated one, so there is
* nothing to fix up.
*/
if (!ab->hw_params->host_alloc_ml_id) {
ret = ath12k_dp_peer_fixup_peer_id(ab, peer_addr,
peer_id);
if (ret)
ath12k_warn(ab,
"failed to fix up peer id %u for dp peer %pM: %d\n",
peer_id, peer_addr, ret);
}
}

void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
struct sk_buff *skb)
{
Expand Down Expand Up @@ -657,6 +703,9 @@ void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
case HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND:
ath12k_htt_mlo_offset_event_handler(ab, skb);
break;
case HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP:
ath12k_dp_htt_mlo_peer_map_handler(ab, skb);
break;
default:
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "dp_htt event %d not handled\n",
type);
Expand Down
12 changes: 12 additions & 0 deletions drivers/net/wireless/ath/ath12k/dp_htt.h
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,7 @@ enum htt_t2h_msg_type {
HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c,
HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND = 0x24,
HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND = 0x28,
HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP = 0x29,
HTT_T2H_MSG_TYPE_PEER_MAP3 = 0x2b,
HTT_T2H_MSG_TYPE_VDEV_TXRX_STATS_PERIODIC_IND = 0x2c,
};
Expand Down Expand Up @@ -974,11 +975,22 @@ struct htt_t2h_peer_unmap_event {
__le32 info1;
} __packed;

#define HTT_T2H_MLO_PEER_MAP_INFO0_MLO_PEER_ID GENMASK(23, 8)
#define HTT_T2H_MLO_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0)

struct htt_t2h_mlo_peer_map_event {
__le32 info0;
__le32 mac_addr_l32;
__le32 info1;
__le32 reserved[5];
} __packed;

struct htt_resp_msg {
union {
struct htt_t2h_version_conf_msg version_msg;
struct htt_t2h_peer_map_event peer_map_ev;
struct htt_t2h_peer_unmap_event peer_unmap_ev;
struct htt_t2h_mlo_peer_map_event mlo_peer_map_ev;
};
} __packed;

Expand Down
75 changes: 67 additions & 8 deletions drivers/net/wireless/ath/ath12k/dp_peer.c
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,9 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr,
dp_peer->is_mlo = params->is_mlo;

/*
* For MLO client, the host assigns the ML peer ID, so set peer_id in dp_peer
* For MLO client, the ML peer ID, either known or PENDING, needs to be
* initialized here since the following logic depends on it.
*
* For non-MLO client, host gets link peer ID from firmware and will be
* assigned at the time of link peer creation
*/
Expand All @@ -488,13 +490,17 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr,
list_add(&dp_peer->list, &dp_hw->dp_peers_list);

/*
* For MLO client, the peer_id for ath12k_dp_peer is allocated by host
* and that peer_id is known at this point, and hence this ath12k_dp_peer
* can be added to the RCU table using the peer_id.
* For non-MLO client, this addition to RCU table shall be done at the
* time of assignment of ath12k_dp_link_peer to ath12k_dp_peer.
* For an MLO client whose ML peer ID is allocated by the host, the
* peer_id is known here and the dp_peer can be added to the RCU
* table using it. For an MLO client on chips where the firmware
* allocates the ID, peer_id is ATH12K_MLO_PEER_ID_PENDING and the
* RCU table publish is deferred to the
* HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP handler. For a non-MLO client
* the publish happens later, at the time of assignment of
* ath12k_dp_link_peer to ath12k_dp_peer.
*/
if (dp_peer->is_mlo)
if (dp_peer->is_mlo &&
dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], dp_peer);

spin_unlock_bh(&dp_hw->peer_lock);
Expand All @@ -515,7 +521,8 @@ void ath12k_dp_peer_delete(struct ath12k_dp_hw *dp_hw, u8 *addr,
return;
}

if (dp_peer->is_mlo)
if (dp_peer->is_mlo &&
dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);

list_del(&dp_peer->list);
Expand Down Expand Up @@ -688,3 +695,55 @@ void ath12k_dp_link_peer_reset_rx_stats(struct ath12k_dp *dp, const u8 *addr)
if (rx_stats)
memset(rx_stats, 0, sizeof(*rx_stats));
}

int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab,
const u8 *peer_addr, u16 peer_id)
{
struct ath12k_dp_link_peer *link_peer;
struct ath12k_dp_peer *dp_peer = NULL;
struct ath12k_hw_group *ag = ab->ag;
struct ath12k_dp_hw *dp_hw = NULL;
struct ath12k_hw *ah;
int i;

if (peer_id >= (ATH12K_PEER_ML_ID_VALID | ATH12K_MAX_MLO_PEERS))
return -EINVAL;

for (i = 0; i < ag->num_hw; i++) {
ah = ag->ah[i];
if (!ah)
continue;

spin_lock_bh(&ah->dp_hw.peer_lock);
dp_peer = ath12k_dp_peer_find_by_addr(&ah->dp_hw,
(u8 *)peer_addr);
if (dp_peer) {
dp_hw = &ah->dp_hw;
break;
}
spin_unlock_bh(&ah->dp_hw.peer_lock);
}

if (!dp_peer)
return -ENOENT;

/* dp_hw->peer_lock is held */

dp_peer->peer_id = peer_id;
rcu_assign_pointer(dp_hw->dp_peers[peer_id], dp_peer);

for (i = 0; i < ATH12K_NUM_MAX_LINKS; i++) {
link_peer = rcu_dereference_protected(dp_peer->link_peers[i],
lockdep_is_held(&dp_hw->peer_lock));
if (link_peer)
link_peer->ml_id = peer_id;
}

ath12k_sta_to_ahsta(dp_peer->sta)->ml_peer_id = peer_id;

spin_unlock_bh(&dp_hw->peer_lock);

complete(&ah->peer_ml_id_done);

return 0;
}
2 changes: 2 additions & 0 deletions drivers/net/wireless/ath/ath12k/dp_peer.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,6 @@ struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_p
struct ath12k_dp_link_peer *
ath12k_dp_link_peer_find_by_peerid(struct ath12k_pdev_dp *dp_pdev, u16 peer_id);
void ath12k_dp_link_peer_free(struct ath12k_dp_link_peer *peer);
int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab, const u8 *peer_addr,
u16 peer_id);
#endif
2 changes: 2 additions & 0 deletions drivers/net/wireless/ath/ath12k/hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ struct ath12k_hw_params {

/* setup REO queue, frag etc only for primary link peer */
bool dp_primary_link_only:1;

bool host_alloc_ml_id;
};

struct ath12k_hw_ops {
Expand Down
Loading