* discovery. No queue is associated with this station.
* @STATION_TYPE_NAN_MGMT: NAN station used for NAN management frames, e.g.,
* SDFs and NAFs.
+ * @STATION_TYPE_NAN_MCAST_DATA: NAN station used for multicast NAN data
+ * frames.
* @STATION_TYPE_MAX: maximum number of FW station types
* @STATION_TYPE_AUX: aux sta. In the FW there is no need for a special type
* for the aux sta, so this type is only for driver - internal use.
STATION_TYPE_NAN_PEER_NDI,
STATION_TYPE_NAN_BCAST,
STATION_TYPE_NAN_MGMT,
+ STATION_TYPE_NAN_MCAST_DATA,
STATION_TYPE_MAX,
STATION_TYPE_AUX = STATION_TYPE_MAX /* this doesn't exist in FW */
}; /* STATION_TYPE_E_VER_1, _VER_2 */
* @mic_prep_pad_delay: MIC prep time padding
* @mic_compute_pad_delay: MIC compute time padding
* @nmi_sta_id: for an NDI peer STA, the NMI peer STA ID it relates to
- * @ndi_local_addr: for an NDI peer STA, the local NDI interface MAC address
+ * @ndi_local_addr: for an NDI peer STA or NAN multicast data station,
+ * the local NDI interface MAC address
* @reserved: Reserved for alignment
*/
struct iwl_sta_cfg_cmd {
iwl_mld_free_internal_sta(mld, &mld_vif->nan.mgmt_sta);
}
+ if (vif->type == NL80211_IFTYPE_NAN_DATA &&
+ mld_vif->nan.mcast_data_sta.sta_id != IWL_INVALID_STA)
+ iwl_mld_free_internal_sta(mld, &mld_vif->nan.mcast_data_sta);
+
CLEANUP_STRUCT(mld_vif);
}
iwl_mld_init_internal_sta(&mld_vif->nan.bcast_sta);
iwl_mld_init_internal_sta(&mld_vif->nan.mgmt_sta);
+ } else if (vif->type == NL80211_IFTYPE_NAN_DATA) {
+ iwl_mld_init_internal_sta(&mld_vif->nan.mcast_data_sta);
}
iwl_mld_init_internal_sta(&mld_vif->aux_sta);
* activities. No queue is associated with it.
* @nan.mgmt_sta: internal station used for NAN management frames, e.g., SDFs
* and NAFs.
+ * @nan.mcast_data_sta: internal station used for multicast NAN Data frames.
*/
struct iwl_mld_vif {
/* Add here fields that need clean up on restart */
bool mac_added;
struct iwl_mld_int_sta bcast_sta;
struct iwl_mld_int_sta mgmt_sta;
+ struct iwl_mld_int_sta mcast_data_sta;
} nan;
struct iwl_mld_emlsr emlsr;
if (ret)
return ret;
- if (vif->type == NL80211_IFTYPE_NAN_DATA)
+ if (vif->type == NL80211_IFTYPE_NAN_DATA) {
+ if (WARN_ON(!mld->nan_device_vif)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (iwl_mld_nan_use_nan_stations(mld)) {
+ struct iwl_mld_vif *mld_vif =
+ iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_int_sta *sta =
+ &mld_vif->nan.mcast_data_sta;
+
+ ret = iwl_mld_add_nan_mcast_data_sta(mld,
+ vif->addr,
+ sta);
+ if (ret)
+ goto err;
+ }
+
return 0;
+ }
/*
* Add the default link, but not if this is an MLD vif as that implies
if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
mld->p2p_device_vif = NULL;
- if (vif->type == NL80211_IFTYPE_NAN)
+ if (vif->type == NL80211_IFTYPE_NAN) {
mld->nan_device_vif = NULL;
- else if (vif->type != NL80211_IFTYPE_NAN_DATA)
+ } else if (vif->type != NL80211_IFTYPE_NAN_DATA) {
iwl_mld_remove_link(mld, &vif->bss_conf);
+ } else if (iwl_mld_nan_use_nan_stations(mld)) {
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_int_sta *sta = &mld_vif->nan.mcast_data_sta;
+
+ if (sta->sta_id != IWL_INVALID_STA)
+ iwl_mld_remove_nan_mcast_data_sta(mld, sta);
+ }
#ifdef CONFIG_IWLWIFI_DEBUGFS
debugfs_remove(iwl_mld_vif_from_mac80211(vif)->dbgfs_slink);
return iwl_mld_send_cmd(mld, &hcmd);
}
-static bool iwl_mld_nan_use_nan_stations(struct iwl_mld *mld)
+bool iwl_mld_nan_use_nan_stations(struct iwl_mld *mld)
{
/*
* If the FW supports version 1 of the NAN config command, it means that
return NULL;
}
+static void iwl_mld_nan_set_mcast_data_links(struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ if (vif->type != NL80211_IFTYPE_NAN_DATA)
+ return;
+
+ /* Note that all errors are handled internally so nothing to do
+ * with the return value (used only to silence compilation warnings)
+ */
+ iwl_mld_update_nan_mcast_data_sta(mld_vif->mld, vif->addr,
+ &mld_vif->nan.mcast_data_sta);
+}
+
void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
struct ieee80211_vif *vif,
u64 changes)
mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI)
iwl_mld_add_modify_sta_cmd(mld, &sta->deflink);
}
+
+ /*
+ * Iterate over all the NAN Data interfaces and update the links
+ * for the internal multicast data station.
+ * In recovery - the station will be added later in
+ * drv_add_interface
+ */
+ if (iwl_mld_nan_use_nan_stations(mld) && !mld->fw_status.in_hw_restart) {
+ struct ieee80211_vif *iter;
+
+ for_each_active_interface(iter, mld->hw)
+ iwl_mld_nan_set_mcast_data_links(iter);
+ }
}
/* delete unused links */
int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif);
+bool iwl_mld_nan_use_nan_stations(struct iwl_mld *mld);
+
#endif /* __iwl_mld_nan_h__ */
len = sizeof(struct iwl_sta_cfg_cmd_v2);
cmd_v2->link_id = cpu_to_le32(__ffs(le32_to_cpu(cmd->link_mask)));
+ } else if (cmd->station_type ==
+ cpu_to_le32(STATION_TYPE_NAN_MCAST_DATA)) {
+ if (WARN_ON(!hweight32(le32_to_cpu(cmd->link_mask))))
+ return -EINVAL;
} else if (WARN_ON(cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) &&
cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) &&
cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_BCAST) &&
return ret;
}
+static u32 iwl_mld_get_nan_link_mask(struct iwl_mld *mld)
+{
+ struct iwl_mld_vif *nan_dev =
+ iwl_mld_vif_from_mac80211(mld->nan_device_vif);
+ struct iwl_mld_nan_link *nan_link;
+ u32 link_mask = 0;
+
+ for_each_mld_nan_valid_link(nan_dev, nan_link)
+ link_mask |= BIT(nan_link->fw_id);
+
+ return link_mask;
+}
+
int iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
struct ieee80211_link_sta *link_sta)
{
if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NMI ||
mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) {
- struct iwl_mld_nan_link *nan_link;
- struct iwl_mld_vif *nan_dev;
-
- is_6ghz = false;
- uora_exists = false;
-
if (WARN_ON(!mld->nan_device_vif))
return -EINVAL;
- nan_dev = iwl_mld_vif_from_mac80211(mld->nan_device_vif);
-
- link_mask = 0;
+ is_6ghz = false;
+ uora_exists = false;
- for_each_mld_nan_valid_link(nan_dev, nan_link)
- link_mask |= BIT(nan_link->fw_id);
+ link_mask = iwl_mld_get_nan_link_mask(mld);
} else {
struct ieee80211_bss_conf *link;
struct iwl_mld_link *mld_link;
}
static int
-iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
+iwl_mld_set_internal_sta_to_fw(struct iwl_mld *mld,
const struct iwl_mld_int_sta *internal_sta,
u32 link_mask, const u8 *addr)
{
cmd.mfp = cpu_to_le32(1);
if (addr) {
- memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
- memcpy(cmd.peer_link_address, addr, ETH_ALEN);
+ if (internal_sta->sta_type == STATION_TYPE_NAN_MCAST_DATA) {
+ ether_addr_copy(cmd.ndi_local_addr, addr);
+ } else {
+ memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
+ memcpy(cmd.peer_link_address, addr, ETH_ALEN);
+ }
}
return iwl_mld_send_sta_cmd(mld, &cmd);
internal_sta->sta_type = sta_type;
- ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, link_mask,
+ ret = iwl_mld_set_internal_sta_to_fw(mld, internal_sta, link_mask,
addr);
if (ret)
goto err;
0, NULL, IWL_MAX_TID_COUNT, true);
}
+int iwl_mld_add_nan_mcast_data_sta(struct iwl_mld *mld,
+ const u8 *ndi_addr,
+ struct iwl_mld_int_sta *sta)
+{
+ u32 link_mask = iwl_mld_get_nan_link_mask(mld);
+
+ /* In case that there are no NAN links, nothing to do */
+ if (!link_mask)
+ return 0;
+
+ return iwl_mld_add_internal_sta(mld, sta,
+ STATION_TYPE_NAN_MCAST_DATA,
+ link_mask, ndi_addr,
+ 0, true);
+}
+
+int iwl_mld_update_nan_mcast_data_sta(struct iwl_mld *mld,
+ const u8 *ndi_addr,
+ struct iwl_mld_int_sta *sta)
+{
+ u32 link_mask;
+
+ if (WARN_ON(!mld->nan_device_vif))
+ return -EINVAL;
+
+ /* If the sta doesn't exist, add it */
+ if (sta->sta_id == IWL_INVALID_STA)
+ return iwl_mld_add_nan_mcast_data_sta(mld, ndi_addr, sta);
+
+ /* The station was already added */
+ if (WARN_ON(sta->sta_type != STATION_TYPE_NAN_MCAST_DATA))
+ return -EINVAL;
+
+ link_mask = iwl_mld_get_nan_link_mask(mld);
+ if (link_mask)
+ return iwl_mld_set_internal_sta_to_fw(mld,
+ sta, link_mask,
+ ndi_addr);
+
+ /* If no links are associated with NAN, remove the station */
+ iwl_mld_remove_nan_mcast_data_sta(mld, sta);
+
+ return 0;
+}
+
void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld,
struct iwl_mld_int_sta *sta)
{
{
iwl_mld_remove_internal_sta(mld, sta, true, IWL_MAX_TID_COUNT);
}
+
+void iwl_mld_remove_nan_mcast_data_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta)
+{
+ iwl_mld_remove_internal_sta(mld, sta, true, 0);
+}
int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld,
struct iwl_mld_int_sta *sta);
+int iwl_mld_add_nan_mcast_data_sta(struct iwl_mld *mld,
+ const u8 *ndi_addr,
+ struct iwl_mld_int_sta *sta);
+
+int iwl_mld_update_nan_mcast_data_sta(struct iwl_mld *mld,
+ const u8 *ndi_addr,
+ struct iwl_mld_int_sta *sta);
+
void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld,
struct iwl_mld_int_sta *sta);
void iwl_mld_remove_nan_mgmt_sta(struct iwl_mld *mld,
struct iwl_mld_int_sta *sta);
+void iwl_mld_remove_nan_mcast_data_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta);
+
#endif /* __iwl_mld_sta_h__ */
iwl_mld_get_basic_rates_and_band(mld, vif, info, &basic_rates, &band);
if (band >= NUM_NL80211_BANDS) {
- WARN_ON(vif->type != NL80211_IFTYPE_NAN);
+ WARN_ON(vif->type != NL80211_IFTYPE_NAN &&
+ vif->type != NL80211_IFTYPE_NAN_DATA);
return IWL_FIRST_OFDM_RATE;
}
case NL80211_IFTYPE_NAN:
WARN_ON(!ieee80211_is_mgmt(fc));
return iwl_mld_nan_get_mgmt_queue(mld, info->control.vif);
+ case NL80211_IFTYPE_NAN_DATA:
+ WARN_ON(!ieee80211_is_data(fc));
+
+ if (!iwl_mld_nan_use_nan_stations(mld))
+ break;
+
+ mld_vif = iwl_mld_vif_from_mac80211(info->control.vif);
+ return mld_vif->nan.mcast_data_sta.queue_id;
default:
WARN_ONCE(1, "Unsupported vif type\n");
break;