* @STATION_TYPE_NAN_PEER_NDI: NAN data peer station type. A station
* of this type can have any number of links (even none) set in the
* link_mask. (Supported since version 3.)
+ * @STATION_TYPE_NAN_BCAST: NAN station used for synchronization and
+ * 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_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_MCAST,
STATION_TYPE_NAN_PEER_NMI,
STATION_TYPE_NAN_PEER_NDI,
+ STATION_TYPE_NAN_BCAST,
+ STATION_TYPE_NAN_MGMT,
STATION_TYPE_MAX,
STATION_TYPE_AUX = STATION_TYPE_MAX /* this doesn't exist in FW */
}; /* STATION_TYPE_E_VER_1, _VER_2 */
* ( STA_CONFIG_CMD = 0xA )
*
* @sta_id: index of station in uCode's station table
- * @link_mask: bitmap of link FW IDs used with this STA
+ * @link_mask: bitmap of link FW IDs used with this STA. Should be set to 0
+ * for STATION_TYPE_NAN_BCAST and STATION_TYPE_NAN_MGMT as they are not
+ * associated with any link added by the driver.
* @peer_mld_address: the peers mld address
* @reserved_for_peer_mld_address: reserved
* @peer_link_address: the address of the link that is used to communicate
* @discovery_beacon_interval: discovery beacon interval in TUs
* @cluster_id: lower last two bytes of the cluster ID, in case the local
* device starts a cluster
- * @sta_id: station ID of the NAN station
+ * @sta_id: station ID of the NAN station. Used only in version 1, in version 2
+ * it is reserved.
* @hb_channel: channel for 5 GHz if the device supports operation on 5 GHz.
* Valid values are 44 and 149, which correspond to the 5 GHz channel, and
* 0 which means that NAN operation on the 5 GHz band is disabled.
__le32 nan_attr_len;
__le32 nan_vendor_elems_len;
u8 beacon_data[];
-} __packed; /* NAN_CONFIG_CMD_API_S_VER_1 */
+} __packed; /* NAN_CONFIG_CMD_API_S_VER_1, NAN_CONFIG_CMD_API_S_VER_2 */
/**
* struct iwl_nan_schedule_cmd_v1 - NAN schedule command
/* Clean up NAN links */
for (int i = 0; i < ARRAY_SIZE(mld_vif->nan.links); i++)
iwl_mld_cleanup_nan_link(&mld_vif->nan.links[i]);
+
+ if (mld_vif->nan.bcast_sta.sta_id != IWL_INVALID_STA)
+ iwl_mld_free_internal_sta(mld, &mld_vif->nan.bcast_sta);
+ if (mld_vif->nan.mgmt_sta.sta_id != IWL_INVALID_STA)
+ iwl_mld_free_internal_sta(mld, &mld_vif->nan.mgmt_sta);
}
CLEANUP_STRUCT(mld_vif);
memset(&mld_vif->nan.links[i], 0, sizeof(mld_vif->nan.links[i]));
mld_vif->nan.links[i].fw_id = FW_CTXT_ID_INVALID;
}
+
+ iwl_mld_init_internal_sta(&mld_vif->nan.bcast_sta);
+ iwl_mld_init_internal_sta(&mld_vif->nan.mgmt_sta);
}
iwl_mld_init_internal_sta(&mld_vif->aux_sta);
* @nan: NAN parameters
* @nan.links: NAN links for FW (indexed by FW link ID)
* @nan.mac_added: track whether or not the MAC was added to FW
+ * @nan.bcast_sta: internal station used for NAN synchronization and discovery
+ * activities. No queue is associated with it.
+ * @nan.mgmt_sta: internal station used for NAN management frames, e.g., SDFs
+ * and NAFs.
*/
struct iwl_mld_vif {
/* Add here fields that need clean up on restart */
/* use only with wiphy protection */
struct iwl_mld_nan_link links[IWL_FW_MAX_LINKS];
bool mac_added;
+ struct iwl_mld_int_sta bcast_sta;
+ struct iwl_mld_int_sta mgmt_sta;
} nan;
struct iwl_mld_emlsr emlsr;
return iwl_mld_send_cmd(mld, &hcmd);
}
+static 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
+ * it needs to receive the station ID of the auxiliary station in the
+ * NAN configuration command. Otherwise, use the NAN dedicated station
+ * types.
+ */
+ return iwl_fw_lookup_cmd_ver(mld->fw,
+ WIDE_ID(MAC_CONF_GROUP,
+ NAN_CFG_CMD), 1) != 1;
+}
+
+static const struct iwl_mld_int_sta *
+iwl_mld_nan_get_mgmt_sta(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ const struct iwl_mld_int_sta *sta;
+
+ if (iwl_mld_nan_use_nan_stations(mld))
+ sta = &mld_vif->nan.mgmt_sta;
+ else
+ sta = &mld_vif->aux_sta;
+
+ if (WARN_ON(sta->sta_id == IWL_INVALID_STA))
+ return NULL;
+
+ return sta;
+}
+
+int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ const struct iwl_mld_int_sta *sta = iwl_mld_nan_get_mgmt_sta(mld, vif);
+
+ if (!sta)
+ return IWL_MLD_INVALID_QUEUE;
+
+ return sta->queue_id;
+}
+
+static void iwl_mld_nan_flush(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ const struct iwl_mld_int_sta *sta = iwl_mld_nan_get_mgmt_sta(mld, vif);
+
+ if (!sta)
+ return;
+
+ if (WARN_ON(sta->queue_id == IWL_MLD_INVALID_QUEUE))
+ return;
+
+ IWL_DEBUG_INFO(mld, "NAN: flush queues for sta=%u\n",
+ sta->sta_id);
+
+ iwl_mld_flush_link_sta_txqs(mld, sta->sta_id);
+}
+
+static void iwl_mld_nan_remove_stations(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ iwl_mld_nan_flush(mld, vif);
+
+ if (!iwl_mld_nan_use_nan_stations(mld)) {
+ iwl_mld_remove_aux_sta(mld, vif);
+ return;
+ }
+
+ iwl_mld_remove_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
+ iwl_mld_remove_nan_mgmt_sta(mld, &mld_vif->nan.mgmt_sta);
+}
+
+static int iwl_mld_nan_add_stations(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int ret;
+
+ if (!iwl_mld_nan_use_nan_stations(mld))
+ return iwl_mld_add_aux_sta(mld, &mld_vif->aux_sta);
+
+ ret = iwl_mld_add_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_add_nan_mgmt_sta(mld, &mld_vif->nan.mgmt_sta);
+ if (ret)
+ iwl_mld_remove_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
+
+ return ret;
+}
+
static int iwl_mld_nan_config(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct cfg80211_nan_conf *conf,
conf->vendor_elems_len);
}
- cmd.sta_id = mld_vif->aux_sta.sta_id;
+ /* FW needs to know about the station ID only with version 1 of the
+ * NAN configuration command
+ */
+ if (!iwl_mld_nan_use_nan_stations(mld))
+ cmd.sta_id = mld_vif->aux_sta.sta_id;
+
return iwl_mld_nan_send_config_cmd(mld, &cmd, data,
conf->extra_nan_attrs_len +
conf->vendor_elems_len);
struct cfg80211_nan_conf *conf)
{
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
- struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
- struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta;
int ret;
IWL_DEBUG_MAC80211(mld, "NAN: start: bands=0x%x\n", conf->bands);
if (ret)
return ret;
- ret = iwl_mld_add_aux_sta(mld, aux_sta);
+ ret = iwl_mld_nan_add_stations(mld, vif);
if (ret)
goto unblock_emlsr;
ret = iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_ADD);
if (ret) {
IWL_ERR(mld, "Failed to start NAN. ret=%d\n", ret);
- goto remove_aux;
+ goto remove_stas;
}
+
return 0;
-remove_aux:
- iwl_mld_remove_aux_sta(mld, vif);
+remove_stas:
+ iwl_mld_nan_remove_stations(mld, vif);
unblock_emlsr:
iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN);
struct ieee80211_vif *vif)
{
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
- struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
struct iwl_nan_config_cmd cmd = {
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
};
/* assume that higher layer guarantees that no additional frames are
* added before calling this callback
*/
- if (!WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA))
- iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
- iwl_mld_remove_aux_sta(mld, vif);
+ iwl_mld_nan_remove_stations(mld, vif);
/* cancel based on object type being NAN, as the NAN objects do
* not have a unique identifier associated with them
"NAN: DW end without NAN started\n"))
return;
- if (WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA))
- return;
-
- IWL_DEBUG_INFO(mld, "NAN: flush queues for aux sta=%u\n",
- mld_vif->aux_sta.sta_id);
-
- iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
+ iwl_mld_nan_flush(mld, mld->nan_device_vif);
/* TODO: currently the notification specified the band on which the DW
* ended. Need to change that to the actual channel on which the next DW
int iwl_mld_mac802111_nan_peer_sched_changed(struct ieee80211_hw *hw,
struct ieee80211_sta *sta);
+int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif);
+
#endif /* __iwl_mld_nan_h__ */
cmd_v2->link_id = cpu_to_le32(__ffs(le32_to_cpu(cmd->link_mask)));
} 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) &&
+ cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_MGMT) &&
hweight32(le32_to_cpu(cmd->link_mask)) != 1)) {
return -EINVAL;
}
static int
iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
const struct iwl_mld_int_sta *internal_sta,
- u8 fw_link_id,
- const u8 *addr)
+ u32 link_mask, const u8 *addr)
{
struct iwl_sta_cfg_cmd cmd = {};
return iwl_mld_send_aux_sta_cmd(mld, internal_sta);
cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id);
- cmd.link_mask = cpu_to_le32(BIT(fw_link_id));
+ cmd.link_mask = cpu_to_le32(link_mask);
cmd.station_type = cpu_to_le32(internal_sta->sta_type);
/* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP.
static int iwl_mld_add_internal_sta(struct iwl_mld *mld,
struct iwl_mld_int_sta *internal_sta,
enum iwl_fw_sta_type sta_type,
- u8 fw_link_id, const u8 *addr, u8 tid)
+ u32 link_mask, const u8 *addr,
+ u8 tid, bool add_txq)
{
int ret, queue_id;
internal_sta->sta_type = sta_type;
- ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, fw_link_id,
+ ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, link_mask,
addr);
if (ret)
goto err;
+ if (!add_txq)
+ return 0;
+
queue_id = iwl_mld_allocate_internal_txq(mld, internal_sta, tid);
if (queue_id < 0) {
iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id);
return iwl_mld_add_internal_sta(mld, &mld_link->bcast_sta,
STATION_TYPE_BCAST_MGMT,
- mld_link->fw_id, addr,
- IWL_MGMT_TID);
+ BIT(mld_link->fw_id), addr,
+ IWL_MGMT_TID, true);
}
int iwl_mld_add_mcast_sta(struct iwl_mld *mld,
return iwl_mld_add_internal_sta(mld, &mld_link->mcast_sta,
STATION_TYPE_MCAST,
- mld_link->fw_id, mcast_addr, 0);
+ BIT(mld_link->fw_id), mcast_addr,
+ 0, true);
}
int iwl_mld_add_aux_sta(struct iwl_mld *mld,
struct iwl_mld_int_sta *internal_sta)
{
return iwl_mld_add_internal_sta(mld, internal_sta, STATION_TYPE_AUX,
- 0, NULL, IWL_MAX_TID_COUNT);
+ 0, NULL, IWL_MAX_TID_COUNT,
+ true);
}
int iwl_mld_add_mon_sta(struct iwl_mld *mld,
return iwl_mld_add_internal_sta(mld, &mld_link->mon_sta,
STATION_TYPE_BCAST_MGMT,
- mld_link->fw_id, NULL,
- IWL_MAX_TID_COUNT);
+ BIT(mld_link->fw_id), NULL,
+ IWL_MAX_TID_COUNT,
+ true);
}
static void iwl_mld_remove_internal_sta(struct iwl_mld *mld,
struct iwl_mld_int_sta *internal_sta,
bool flush, u8 tid)
{
- if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA ||
- internal_sta->queue_id == IWL_MLD_INVALID_QUEUE))
+ if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA))
return;
- if (flush)
+ if (flush && !WARN_ON_ONCE(internal_sta->queue_id ==
+ IWL_MLD_INVALID_QUEUE))
iwl_mld_flush_link_sta_txqs(mld, internal_sta->sta_id);
- iwl_mld_free_txq(mld, BIT(internal_sta->sta_id),
- tid, internal_sta->queue_id);
+ if (internal_sta->queue_id != IWL_MLD_INVALID_QUEUE)
+ iwl_mld_free_txq(mld, BIT(internal_sta->sta_id),
+ tid, internal_sta->queue_id);
iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id);
return ret;
}
+
+int iwl_mld_add_nan_bcast_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta)
+{
+ const u8 bcast_addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+ return iwl_mld_add_internal_sta(mld, sta, STATION_TYPE_NAN_BCAST,
+ 0, bcast_addr, 0, false);
+}
+
+int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta)
+{
+ return iwl_mld_add_internal_sta(mld, sta, STATION_TYPE_NAN_MGMT,
+ 0, NULL, IWL_MAX_TID_COUNT, true);
+}
+
+void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta)
+{
+ iwl_mld_remove_internal_sta(mld, sta, false, 0);
+}
+
+void iwl_mld_remove_nan_mgmt_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta)
+{
+ iwl_mld_remove_internal_sta(mld, sta, true, IWL_MAX_TID_COUNT);
+}
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
u16 old_links, u16 new_links);
+
+int iwl_mld_add_nan_bcast_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta);
+
+int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld,
+ 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);
+
#endif /* __iwl_mld_sta_h__ */
WARN_ON(!ieee80211_is_mgmt(fc));
return mld_vif->aux_sta.queue_id;
case NL80211_IFTYPE_NAN:
- mld_vif = iwl_mld_vif_from_mac80211(info->control.vif);
-
WARN_ON(!ieee80211_is_mgmt(fc));
-
- return mld_vif->aux_sta.queue_id;
+ return iwl_mld_nan_get_mgmt_queue(mld, info->control.vif);
default:
WARN_ONCE(1, "Unsupported vif type\n");
break;