blob: dbb5615dc3f64b22e26e4598fcb6b1833cae3f38 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2024 Intel Corporation
*/
#include "session-protect.h"
#include "fw/api/time-event.h"
#include "fw/api/context.h"
#include "iface.h"
#include <net/mac80211.h>
void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt)
{
struct iwl_session_prot_notif *notif = (void *)pkt->data;
int fw_link_id = le32_to_cpu(notif->mac_link_id);
struct ieee80211_bss_conf *link_conf =
iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
struct ieee80211_vif *vif;
struct iwl_mld_vif *mld_vif;
struct iwl_mld_session_protect *session_protect;
if (WARN_ON(!link_conf))
return;
vif = link_conf->vif;
mld_vif = iwl_mld_vif_from_mac80211(vif);
session_protect = &mld_vif->session_protect;
if (!le32_to_cpu(notif->status)) {
memset(session_protect, 0, sizeof(*session_protect));
} else if (le32_to_cpu(notif->start)) {
/* End_jiffies indicates an active session */
session_protect->session_requested = false;
session_protect->end_jiffies =
TU_TO_EXP_TIME(session_protect->duration);
/* !session_protect->end_jiffies means inactive session */
if (!session_protect->end_jiffies)
session_protect->end_jiffies = 1;
} else {
memset(session_protect, 0, sizeof(*session_protect));
}
}
static int _iwl_mld_schedule_session_protection(struct iwl_mld *mld,
struct ieee80211_vif *vif,
u32 duration, u32 min_duration,
int link_id)
{
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
struct iwl_mld_link *link =
iwl_mld_link_dereference_check(mld_vif, link_id);
struct iwl_mld_session_protect *session_protect =
&mld_vif->session_protect;
struct iwl_session_prot_cmd cmd = {
.id_and_color = cpu_to_le32(link->fw_id),
.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
.conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
.duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
};
int ret;
lockdep_assert_wiphy(mld->wiphy);
WARN(hweight16(vif->active_links) > 1,
"Session protection isn't allowed with more than one active link");
if (session_protect->end_jiffies &&
time_after(session_protect->end_jiffies,
TU_TO_EXP_TIME(min_duration))) {
IWL_DEBUG_TE(mld, "We have ample in the current session: %u\n",
jiffies_to_msecs(session_protect->end_jiffies -
jiffies));
return -EALREADY;
}
IWL_DEBUG_TE(mld, "Add a new session protection, duration %d TU\n",
le32_to_cpu(cmd.duration_tu));
ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP,
SESSION_PROTECTION_CMD), &cmd);
if (ret)
return ret;
/* end_jiffies will be updated when handling session_prot_notif */
session_protect->end_jiffies = 0;
session_protect->duration = duration;
session_protect->session_requested = true;
return 0;
}
void iwl_mld_schedule_session_protection(struct iwl_mld *mld,
struct ieee80211_vif *vif,
u32 duration, u32 min_duration,
int link_id)
{
int ret;
ret = _iwl_mld_schedule_session_protection(mld, vif, duration,
min_duration, link_id);
if (ret && ret != -EALREADY)
IWL_ERR(mld,
"Couldn't send the SESSION_PROTECTION_CMD (%d)\n",
ret);
}
struct iwl_mld_session_start_data {
struct iwl_mld *mld;
struct ieee80211_bss_conf *link_conf;
bool success;
};
static bool iwl_mld_session_start_fn(struct iwl_notif_wait_data *notif_wait,
struct iwl_rx_packet *pkt, void *_data)
{
struct iwl_session_prot_notif *notif = (void *)pkt->data;
unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
struct iwl_mld_session_start_data *data = _data;
struct ieee80211_bss_conf *link_conf;
struct iwl_mld *mld = data->mld;
int fw_link_id;
if (IWL_FW_CHECK(mld, pkt_len < sizeof(*notif),
"short session prot notif (%d)\n",
pkt_len))
return false;
fw_link_id = le32_to_cpu(notif->mac_link_id);
link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
if (link_conf != data->link_conf)
return false;
if (!le32_to_cpu(notif->status))
return true;
if (notif->start) {
data->success = true;
return true;
}
return false;
}
int iwl_mld_start_session_protection(struct iwl_mld *mld,
struct ieee80211_vif *vif,
u32 duration, u32 min_duration,
int link_id, unsigned long timeout)
{
static const u16 start_notif[] = { SESSION_PROTECTION_NOTIF };
struct iwl_notification_wait start_wait;
struct iwl_mld_session_start_data data = {
.mld = mld,
.link_conf = wiphy_dereference(mld->wiphy,
vif->link_conf[link_id]),
};
int ret;
if (WARN_ON(!data.link_conf))
return -EINVAL;
iwl_init_notification_wait(&mld->notif_wait, &start_wait,
start_notif, ARRAY_SIZE(start_notif),
iwl_mld_session_start_fn, &data);
ret = _iwl_mld_schedule_session_protection(mld, vif, duration,
min_duration, link_id);
if (ret) {
iwl_remove_notification(&mld->notif_wait, &start_wait);
return ret == -EALREADY ? 0 : ret;
}
ret = iwl_wait_notification(&mld->notif_wait, &start_wait, timeout);
if (ret)
return ret;
return data.success ? 0 : -EIO;
}
int iwl_mld_cancel_session_protection(struct iwl_mld *mld,
struct ieee80211_vif *vif,
int link_id)
{
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
struct iwl_mld_link *link =
iwl_mld_link_dereference_check(mld_vif, link_id);
struct iwl_mld_session_protect *session_protect =
&mld_vif->session_protect;
struct iwl_session_prot_cmd cmd = {
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
.conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
};
int ret;
lockdep_assert_wiphy(mld->wiphy);
/* If there isn't an active session or a requested one for this
* link do nothing
*/
if (!session_protect->session_requested &&
!session_protect->end_jiffies)
return 0;
if (WARN_ON(!link))
return -EINVAL;
cmd.id_and_color = cpu_to_le32(link->fw_id);
ret = iwl_mld_send_cmd_pdu(mld,
WIDE_ID(MAC_CONF_GROUP,
SESSION_PROTECTION_CMD), &cmd);
if (ret) {
IWL_ERR(mld,
"Couldn't send the SESSION_PROTECTION_CMD\n");
return ret;
}
memset(session_protect, 0, sizeof(*session_protect));
return 0;
}