On 10/23/2024 6:29 AM, Kalle Valo wrote: > From: Sriram R <quic_srirrama@xxxxxxxxxxx> > > Add changes to add the link vdevs dynamically whenever a channel is assigned > from mac80211 for a link vdev. During vdev create, update ML address of the > vdev to firmware using the new WMI parameter (WMI_TAG_MLO_VDEV_CREATE_PARAMS). > > During vdev start, notify the firmware that this link vdev is newly added and > also indicate all its known partners so that the firmware can take necessary > actions to internally update the partners on the new link being added. > > Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 > Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 > > Signed-off-by: Sriram R <quic_srirrama@xxxxxxxxxxx> > Co-developed-by: Rameshkumar Sundaram <quic_ramess@xxxxxxxxxxx> > Signed-off-by: Rameshkumar Sundaram <quic_ramess@xxxxxxxxxxx> > Signed-off-by: Kalle Valo <quic_kvalo@xxxxxxxxxxx> > --- > drivers/net/wireless/ath/ath12k/mac.c | 90 ++++++++++++++++++++++++++- > drivers/net/wireless/ath/ath12k/wmi.c | 85 ++++++++++++++++++++++++- > drivers/net/wireless/ath/ath12k/wmi.h | 74 ++++++++++++++++++++++ > 3 files changed, 244 insertions(+), 5 deletions(-) > > diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c > index f45f32f3b5f6..d4aa4540c8e6 100644 > --- a/drivers/net/wireless/ath/ath12k/mac.c > +++ b/drivers/net/wireless/ath/ath12k/mac.c > @@ -648,6 +648,18 @@ struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id) > return NULL; > } > > +static bool ath12k_mac_is_ml_arvif(struct ath12k_link_vif *arvif) > +{ > + struct ath12k_vif *ahvif = arvif->ahvif; > + > + lockdep_assert_wiphy(ahvif->ah->hw->wiphy); should we have helper functions ath12k_<foo>_to_wiphy() to abstract out the underlying linkages? > + > + if (ahvif->vif->valid_links & BIT(arvif->link_id)) > + return true; > + > + return false; > +} > + > static struct ath12k *ath12k_mac_get_ar_by_chan(struct ieee80211_hw *hw, > struct ieee80211_channel *channel) > { > @@ -1512,7 +1524,8 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif) > tx_ahvif = ath12k_vif_to_ahvif(ahvif->vif->mbssid_tx_vif); > tx_arvif = &tx_ahvif->deflink; > beacons = ieee80211_beacon_get_template_ema_list(ath12k_ar_to_hw(tx_arvif->ar), > - tx_ahvif->vif, 0); > + tx_ahvif->vif, > + tx_arvif->link_id); > if (!beacons || !beacons->cnt) { > ath12k_warn(arvif->ar->ab, > "failed to get ema beacon templates from mac80211\n"); > @@ -1577,7 +1590,7 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif) > } > > bcn = ieee80211_beacon_get_template(ath12k_ar_to_hw(tx_arvif->ar), tx_ahvif->vif, > - &offs, 0); > + &offs, tx_arvif->link_id); > if (!bcn) { > ath12k_warn(ab, "failed to get beacon template from mac80211\n"); > return -EPERM; > @@ -1658,7 +1671,7 @@ static void ath12k_control_beaconing(struct ath12k_link_vif *arvif, > > ahvif->aid = 0; > > - ether_addr_copy(arvif->bssid, info->bssid); > + ether_addr_copy(arvif->bssid, info->addr); > > params.vdev_id = arvif->vdev_id; > params.aid = ahvif->aid; > @@ -6671,6 +6684,8 @@ static int ath12k_mac_setup_vdev_create_arg(struct ath12k_link_vif *arvif, > struct ath12k_vif *ahvif = arvif->ahvif; > int ret; > > + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); > + > arg->if_id = arvif->vdev_id; > arg->type = ahvif->vdev_type; > arg->subtype = ahvif->vdev_subtype; > @@ -6702,6 +6717,17 @@ static int ath12k_mac_setup_vdev_create_arg(struct ath12k_link_vif *arvif, > } > > arg->if_stats_id = ath12k_mac_get_vdev_stats_id(arvif); > + > + if (ath12k_mac_is_ml_arvif(arvif)) { > + if (hweight16(ahvif->vif->valid_links) > ATH12K_WMI_MLO_MAX_LINKS) { > + ath12k_warn(ar->ab, "too many MLO links during setting up vdev: %d", > + ahvif->vif->valid_links); > + return -EINVAL; > + } > + > + ether_addr_copy(arg->mld_addr, ahvif->vif->addr); > + } > + > return 0; > } > > @@ -7639,6 +7665,61 @@ ath12k_mac_check_down_grade_phy_mode(struct ath12k *ar, > return down_mode; > } > > +static void > +ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif, > + struct wmi_ml_arg *ml_arg) > +{ > + struct ath12k_vif *ahvif = arvif->ahvif; > + struct wmi_ml_partner_info *partner_info; > + struct ieee80211_bss_conf *link_conf; > + struct ath12k_link_vif *arvif_p; > + unsigned long links; > + u8 link_id; > + > + lockdep_assert_wiphy(ahvif->ah->hw->wiphy); > + > + if (!ath12k_mac_is_ml_arvif(arvif)) > + return; > + > + if (hweight16(ahvif->vif->valid_links) > ATH12K_WMI_MLO_MAX_LINKS) > + return; > + > + rcu_read_lock(); what is this protecting? do all of the statements between here and the for loop really need RCU protection? > + > + ml_arg->enabled = true; > + > + /* Driver always add a new link via VDEV START, FW takes > + * care of internally adding this link to existing > + * link vdevs which are advertised as partners below > + */ > + ml_arg->link_add = true; > + partner_info = ml_arg->partner_info; > + > + links = ahvif->links_map; > + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { > + arvif_p = rcu_dereference(ahvif->link[link_id]); > + > + if (WARN_ON(!arvif_p)) > + continue; > + > + if (arvif == arvif_p) > + continue; > + > + link_conf = rcu_dereference(ahvif->vif->link_conf[arvif_p->link_id]); > + > + if (!link_conf) > + continue; > + > + partner_info->vdev_id = arvif_p->vdev_id; > + partner_info->hw_link_id = arvif_p->ar->pdev->hw_link_id; > + ether_addr_copy(partner_info->addr, link_conf->addr); > + ml_arg->num_partner_links++; > + partner_info++; > + } > + > + rcu_read_unlock(); > +} > + > static int > ath12k_mac_vdev_start_restart(struct ath12k_link_vif *arvif, > struct ieee80211_chanctx_conf *ctx, > @@ -7717,6 +7798,9 @@ ath12k_mac_vdev_start_restart(struct ath12k_link_vif *arvif, > > arg.passive |= !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR); > > + if (!restart) > + ath12k_mac_mlo_get_vdev_args(arvif, &arg.ml); > + > ath12k_dbg(ab, ATH12K_DBG_MAC, > "mac vdev %d start center_freq %d phymode %s punct_bitmap 0x%x\n", > arg.vdev_id, arg.freq, > diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c > index dced2aa9ba1a..e089b58bbea1 100644 > --- a/drivers/net/wireless/ath/ath12k/wmi.c > +++ b/drivers/net/wireless/ath/ath12k/wmi.c > @@ -821,6 +821,8 @@ int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr, > struct wmi_vdev_create_cmd *cmd; > struct sk_buff *skb; > struct ath12k_wmi_vdev_txrx_streams_params *txrx_streams; > + bool is_ml_vdev = is_valid_ether_addr(args->mld_addr); > + struct wmi_vdev_create_mlo_params *ml_params; > struct wmi_tlv *tlv; > int ret, len; > void *ptr; > @@ -830,7 +832,8 @@ int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr, > * both the bands. > */ > len = sizeof(*cmd) + TLV_HDR_SIZE + > - (WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams)); > + (WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams)) + > + (is_ml_vdev ? TLV_HDR_SIZE + sizeof(*ml_params) : 0); > > skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); > if (!skb) > @@ -879,6 +882,21 @@ int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr, > txrx_streams->supported_rx_streams = > cpu_to_le32(args->chains[NL80211_BAND_5GHZ].rx); > > + ptr += WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams); > + > + if (is_ml_vdev) { > + tlv = ptr; > + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, > + sizeof(*ml_params)); > + ptr += TLV_HDR_SIZE; > + ml_params = ptr; > + > + ml_params->tlv_header = > + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_VDEV_CREATE_PARAMS, > + sizeof(*ml_params)); > + ether_addr_copy(ml_params->mld_macaddr.addr, args->mld_addr); > + } > + > ath12k_dbg(ar->ab, ATH12K_DBG_WMI, > "WMI vdev create: id %d type %d subtype %d macaddr %pM pdevid %d\n", > args->if_id, args->type, args->subtype, > @@ -1020,19 +1038,27 @@ static void ath12k_wmi_put_wmi_channel(struct ath12k_wmi_channel_params *chan, > int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg, > bool restart) > { > + struct wmi_vdev_start_mlo_params *ml_params; > + struct wmi_partner_link_info *partner_info; > struct ath12k_wmi_pdev *wmi = ar->wmi; > struct wmi_vdev_start_request_cmd *cmd; > struct sk_buff *skb; > struct ath12k_wmi_channel_params *chan; > struct wmi_tlv *tlv; > void *ptr; > - int ret, len; > + int ret, len, i, ml_arg_size = 0; > > if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) > return -EINVAL; > > len = sizeof(*cmd) + sizeof(*chan) + TLV_HDR_SIZE; > > + if (!restart && arg->ml.enabled) { > + ml_arg_size = TLV_HDR_SIZE + sizeof(*ml_params) + > + TLV_HDR_SIZE + (arg->ml.num_partner_links * > + sizeof(*partner_info)); > + len += ml_arg_size; > + } > skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); > if (!skb) > return -ENOMEM; > @@ -1085,6 +1111,61 @@ int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg, > > ptr += sizeof(*tlv); > > + if (ml_arg_size) { > + tlv = ptr; > + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, > + sizeof(*ml_params)); > + ptr += TLV_HDR_SIZE; > + > + ml_params = ptr; > + > + ml_params->tlv_header = > + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_VDEV_START_PARAMS, > + sizeof(*ml_params)); > + > + ml_params->flags = le32_encode_bits(arg->ml.enabled, > + ATH12K_WMI_FLAG_MLO_ENABLED) | > + le32_encode_bits(arg->ml.assoc_link, > + ATH12K_WMI_FLAG_MLO_ASSOC_LINK) | > + le32_encode_bits(arg->ml.mcast_link, > + ATH12K_WMI_FLAG_MLO_MCAST_VDEV) | > + le32_encode_bits(arg->ml.link_add, > + ATH12K_WMI_FLAG_MLO_LINK_ADD); > + > + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "vdev %d start ml flags 0x%x\n", > + arg->vdev_id, ml_params->flags); > + > + ptr += sizeof(*ml_params); > + > + tlv = ptr; > + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, > + arg->ml.num_partner_links * > + sizeof(*partner_info)); > + ptr += TLV_HDR_SIZE; > + > + partner_info = ptr; > + > + for (i = 0; i < arg->ml.num_partner_links; i++) { > + partner_info->tlv_header = > + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_PARTNER_LINK_PARAMS, > + sizeof(*partner_info)); > + partner_info->vdev_id = > + cpu_to_le32(arg->ml.partner_info[i].vdev_id); > + partner_info->hw_link_id = > + cpu_to_le32(arg->ml.partner_info[i].hw_link_id); > + ether_addr_copy(partner_info->vdev_addr.addr, > + arg->ml.partner_info[i].addr); > + > + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "partner vdev %d hw_link_id %d macaddr%pM\n", > + partner_info->vdev_id, partner_info->hw_link_id, > + partner_info->vdev_addr.addr); > + > + partner_info++; > + } > + > + ptr = partner_info; > + } > + > ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "vdev %s id 0x%x freq 0x%x mode 0x%x\n", > restart ? "restart" : "start", arg->vdev_id, > arg->freq, arg->mode); > diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h > index 6f55dbdf629d..33b9643644c6 100644 > --- a/drivers/net/wireless/ath/ath12k/wmi.h > +++ b/drivers/net/wireless/ath/ath12k/wmi.h > @@ -1929,6 +1929,19 @@ enum wmi_tlv_tag { > WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, > WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, > WMI_TAG_EHT_RATE_SET = 0x3C4, > + WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5, > + WMI_TAG_MLO_TX_SEND_PARAMS, > + WMI_TAG_MLO_PARTNER_LINK_PARAMS, > + WMI_TAG_MLO_PARTNER_LINK_PARAMS_PEER_ASSOC, > + WMI_TAG_MLO_SETUP_CMD = 0x3C9, > + WMI_TAG_MLO_SETUP_COMPLETE_EVENT, > + WMI_TAG_MLO_READY_CMD, > + WMI_TAG_MLO_TEARDOWN_CMD, > + WMI_TAG_MLO_TEARDOWN_COMPLETE, > + WMI_TAG_MLO_PEER_ASSOC_PARAMS = 0x3D0, > + WMI_TAG_MLO_PEER_CREATE_PARAMS = 0x3D5, > + WMI_TAG_MLO_VDEV_START_PARAMS = 0x3D6, > + WMI_TAG_MLO_VDEV_CREATE_PARAMS = 0x3D7, > WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, > WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD = 0x3D9, > WMI_TAG_PDEV_SET_BIOS_INTERFACE_CMD = 0x3FB, > @@ -2740,6 +2753,7 @@ struct ath12k_wmi_vdev_create_arg { > u8 if_stats_id; > u32 mbssid_flags; > u32 mbssid_tx_vdev_id; > + u8 mld_addr[ETH_ALEN]; > }; > > #define ATH12K_MAX_VDEV_STATS_ID 0x30 > @@ -2766,6 +2780,44 @@ struct ath12k_wmi_vdev_txrx_streams_params { > __le32 supported_rx_streams; > } __packed; > > +/* 2 word representation of MAC addr */ > +struct wmi_mac_addr { > + union { > + u8 addr[6]; > + struct { > + u32 word0; > + u32 word1; > + } __packed; > + } __packed; > +} __packed; we already have: /* 2 word representation of MAC addr */ struct ath12k_wmi_mac_addr_params { u8 addr[ETH_ALEN]; u8 padding[2]; } __packed; why aren't we consistently using that? > + > +struct wmi_vdev_create_mlo_params { > + __le32 tlv_header; > + struct wmi_mac_addr mld_macaddr; > +} __packed; > + > +#define ATH12K_WMI_FLAG_MLO_ENABLED BIT(0) > +#define ATH12K_WMI_FLAG_MLO_ASSOC_LINK BIT(1) > +#define ATH12K_WMI_FLAG_MLO_PRIMARY_UMAC BIT(2) > +#define ATH12K_WMI_FLAG_MLO_LOGICAL_LINK_IDX_VALID BIT(3) > +#define ATH12K_WMI_FLAG_MLO_PEER_ID_VALID BIT(4) > +#define ATH12K_WMI_FLAG_MLO_MCAST_VDEV BIT(5) > +#define ATH12K_WMI_FLAG_MLO_EMLSR_SUPPORT BIT(6) > +#define ATH12K_WMI_FLAG_MLO_FORCED_INACTIVE BIT(7) > +#define ATH12K_WMI_FLAG_MLO_LINK_ADD BIT(8) > + > +struct wmi_vdev_start_mlo_params { > + __le32 tlv_header; > + __le32 flags; > +} __packed; > + > +struct wmi_partner_link_info { > + __le32 tlv_header; > + __le32 vdev_id; > + __le32 hw_link_id; > + struct wmi_mac_addr vdev_addr; > +} __packed; > + > struct wmi_vdev_delete_cmd { > __le32 tlv_header; > __le32 vdev_id; > @@ -2909,6 +2961,27 @@ enum wmi_phy_mode { > MODE_MAX = 33, > }; > > +#define ATH12K_WMI_MLO_MAX_LINKS 4 > + > +struct wmi_ml_partner_info { > + u32 vdev_id; > + u32 hw_link_id; > + u8 addr[ETH_ALEN]; > + bool assoc_link; > + bool primary_umac; > + bool logical_link_idx_valid; > + u32 logical_link_idx; > +}; > + > +struct wmi_ml_arg { > + bool enabled; > + bool assoc_link; > + bool mcast_link; > + bool link_add; > + u8 num_partner_links; > + struct wmi_ml_partner_info partner_info[ATH12K_WMI_MLO_MAX_LINKS]; > +}; > + > struct wmi_vdev_start_req_arg { > u32 vdev_id; > u32 freq; > @@ -2946,6 +3019,7 @@ struct wmi_vdev_start_req_arg { > u32 mbssid_flags; > u32 mbssid_tx_vdev_id; > u32 punct_bitmap; > + struct wmi_ml_arg ml; > }; > > struct ath12k_wmi_peer_create_arg {