/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
- * Copyright (C) 2012-2014, 2018-2019, 2021 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2019, 2021-2022 Intel Corporation
  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
  * Copyright (C) 2016-2017 Intel Deutschland GmbH
  */
 #ifndef __iwl_fw_api_mac_cfg_h__
 #define __iwl_fw_api_mac_cfg_h__
 
+#include "mac.h"
+
 /**
  * enum iwl_mac_conf_subcmd_ids - mac configuration command IDs
  */
         * @CANCEL_CHANNEL_SWITCH_CMD: &struct iwl_cancel_channel_switch_cmd
         */
        CANCEL_CHANNEL_SWITCH_CMD = 0x6,
-
+       /**
+        * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd
+        */
+       MAC_CONFIG_CMD = 0x8,
        /**
         * @SESSION_PROTECTION_NOTIF: &struct iwl_mvm_session_prot_notif
         */
        __le16 reserved;
 } __packed; /* MAC_LOW_LATENCY_API_S_VER_1 */
 
+/**
+ * struct iwl_mac_client_data - configuration data for client MAC context
+ *
+ * @is_assoc: 1 for associated state, 0 otherwise
+ * @assoc_id: unique ID assigned by the AP during association
+ * @data_policy: see &enum iwl_mac_data_policy
+ * @ctwin: client traffic window in TU (period after TBTT when GO is present).
+ *     0 indicates that there is no CT window.
+ */
+struct iwl_mac_client_data {
+       __le32 is_assoc;
+       __le32 assoc_id;
+       __le32 data_policy;
+       __le32 ctwin;
+} __packed; /* MAC_CONTEXT_CONFIG_CLIENT_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_go_ibss_data - configuration data for GO and IBSS MAC context
+ *
+ * @beacon_template: beacon template ID
+ */
+struct iwl_mac_go_ibss_data {
+       __le32 beacon_template;
+} __packed; /* MAC_CONTEXT_CONFIG_GO_IBSS_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_p2p_dev_data  - configuration data for P2P device MAC context
+ *
+ * @is_disc_extended: if set to true, P2P Device discoverability is enabled on
+ *     other channels as well. This should be to true only in case that the
+ *     device is discoverable and there is an active GO. Note that setting this
+ *     field when not needed, will increase the number of interrupts and have
+ *     effect on the platform power, as this setting opens the Rx filters on
+ *     all macs.
+ */
+struct iwl_mac_p2p_dev_data {
+       __le32 is_disc_extended;
+} __packed; /* MAC_CONTEXT_CONFIG_P2P_DEV_DATA_API_S_VER_1 */
+
+/**
+ * enum iwl_mac_config_filter_flags - MAC context configuration filter flags
+ *
+ * @MAC_CFG_FILTER_PROMISC: accept all data frames
+ * @MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT: pass all management and
+ *     control frames to the host
+ * @MAC_CFG_FILTER_ACCEPT_GRP: accept multicast frames
+ * @MAC_CFG_FILTER_ACCEPT_BEACON: accept beacon frames
+ * @MAC_CFG_FILTER_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe response
+ * @MAC_CFG_FILTER_ACCEPT_PROBE_REQ: accept probe requests
+ */
+enum iwl_mac_config_filter_flags {
+       MAC_CFG_FILTER_PROMISC                  = BIT(0),
+       MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT  = BIT(1),
+       MAC_CFG_FILTER_ACCEPT_GRP               = BIT(2),
+       MAC_CFG_FILTER_ACCEPT_BEACON            = BIT(3),
+       MAC_CFG_FILTER_ACCEPT_BCAST_PROBE_RESP  = BIT(4),
+       MAC_CFG_FILTER_ACCEPT_PROBE_REQ         = BIT(5),
+}; /* MAC_FILTER_FLAGS_MASK_E_VER_1 */
+
+/**
+ * struct iwl_mac_config_cmd - command structure to configure MAC contexts in
+ *     MLD API
+ * ( MAC_CONTEXT_CONFIG_CMD = 0x8 )
+ *
+ * @id_and_color: ID and color of the MAC
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @mac_type: one of &enum iwl_mac_types
+ * @local_mld_addr: mld address
+ * @reserved_for_local_mld_addr: reserved
+ * @filter_flags: combination of &enum iwl_mac_config_filter_flags
+ * @he_support: does this MAC support HE
+ * @eht_support: does this MAC support EHT. Requires he_support
+ * @nic_not_ack_enabled: mark that the NIC doesn't support receiving
+ *     ACK-enabled AGG, (i.e. both BACK and non-BACK frames in single AGG).
+ *     If the NIC is not ACK_ENABLED it may use the EOF-bit in first non-0
+ *     len delim to determine if AGG or single.
+ * @client: client mac data
+ * @go_ibss: mac data for go or ibss
+ * @p2p_dev: mac data for p2p device
+ */
+struct iwl_mac_config_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       /* MAC_CONTEXT_TYPE_API_E */
+       __le32 mac_type;
+       u8 local_mld_addr[6];
+       __le16 reserved_for_local_mld_addr;
+       __le32 filter_flags;
+       __le32 he_support;
+       __le32 eht_support;
+       __le32 nic_not_ack_enabled;
+       /* MAC_CONTEXT_CONFIG_SPECIFIC_DATA_API_U_VER_1 */
+       union {
+               struct iwl_mac_client_data client;
+               struct iwl_mac_go_ibss_data go_ibss;
+               struct iwl_mac_p2p_dev_data p2p_dev;
+       };
+} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_1 */
+
 #endif /* __iwl_fw_api_mac_cfg_h__ */
 
                *qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN);
 }
 
-static int iwl_mvm_get_mac_type(struct ieee80211_vif *vif)
+int iwl_mvm_get_mac_type(struct ieee80211_vif *vif)
 {
        u32 mac_type = FW_MAC_TYPE_BSS_STA;
 
                       dtim_offs);
 }
 
-static __le32 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(struct iwl_mvm *mvm,
-                                                          struct ieee80211_vif *vif)
+__le32 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(struct iwl_mvm *mvm,
+                                                   struct ieee80211_vif *vif)
 {
        struct ieee80211_p2p_noa_attr *noa =
                &vif->bss_conf.p2p_noa_attr;
                        IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
 }
 
-static __le32 iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(struct iwl_mvm *mvm,
-                                                     struct ieee80211_vif *vif)
+__le32 iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(struct iwl_mvm *mvm,
+                                              struct ieee80211_vif *vif)
 {
        __le32 twt_policy = cpu_to_le32(0);
 
                data->go_active = true;
 }
 
-static __le32 iwl_mac_ctxt_p2p_dev_has_extended_disc(struct iwl_mvm *mvm,
-                                                    struct ieee80211_vif *vif)
+__le32 iwl_mac_ctxt_p2p_dev_has_extended_disc(struct iwl_mvm *mvm,
+                                             struct ieee80211_vif *vif)
 {
        struct iwl_mvm_go_iterator_data data = {};
 
 /*
  * Fill the filter flags for mac context of type AP or P2P GO.
  */
-static void iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(struct iwl_mvm *mvm,
-                                                    struct iwl_mvm_vif *mvmvif,
-                                                    __le32 *filter_flags,
-                                                    int accept_probe_req_flag,
-                                                    int accept_beacon_flag)
+void iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(struct iwl_mvm *mvm,
+                                             struct iwl_mvm_vif *mvmvif,
+                                             __le32 *filter_flags,
+                                             int accept_probe_req_flag,
+                                             int accept_beacon_flag)
 {
        /*
         * in AP mode, pass probe requests and beacons from other APs
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2022 Intel Corporation
+ */
+#include "mvm.h"
+
+static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
+                                           struct ieee80211_vif *vif,
+                                           struct iwl_mac_config_cmd *cmd,
+                                           u32 action)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       cmd->id_and_color = cpu_to_le32(mvmvif->id);
+       cmd->action = cpu_to_le32(action);
+
+       cmd->mac_type = cpu_to_le32(iwl_mvm_get_mac_type(vif));
+
+       memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN);
+
+       cmd->filter_flags = cpu_to_le32(0);
+       cmd->he_support = cpu_to_le32(0);
+       cmd->eht_support = cpu_to_le32(0);
+
+       cmd->nic_not_ack_enabled =
+               cpu_to_le32(!iwl_mvm_is_nic_ack_enabled(mvm, vif));
+
+       if (iwlwifi_mod_params.disable_11ax)
+               return;
+       cmd->he_support = cpu_to_le32(vif->bss_conf.he_support);
+
+       if (!iwlwifi_mod_params.disable_11be && cmd->he_support)
+               cmd->eht_support = cpu_to_le32(vif->bss_conf.eht_support);
+}
+
+static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
+                                        struct iwl_mac_config_cmd *cmd)
+{
+       int ret = iwl_mvm_send_cmd_pdu(mvm,
+                                      WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),
+                                      0, sizeof(*cmd), cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send MAC_CONFIG_CMD (action:%d): %d\n",
+                       le32_to_cpu(cmd->action), ret);
+       return ret;
+}
+
+static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       u32 action, bool force_assoc_off)
+{
+       struct iwl_mac_config_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_STATION);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       /*
+        * We always want to hear MCAST frames, if we're not authorized yet,
+        * we'll drop them.
+        */
+       cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP);
+
+       if (vif->p2p)
+               cmd.client.ctwin =
+                       iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(mvm, vif);
+
+       if (vif->cfg.assoc && vif->bss_conf.dtim_period &&
+           !force_assoc_off) {
+               struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+               cmd.client.is_assoc = cpu_to_le32(1);
+
+               if (!mvmvif->authorized &&
+                   fw_has_capa(&mvm->fw->ucode_capa,
+                               IWL_UCODE_TLV_CAPA_COEX_HIGH_PRIO))
+                       cmd.client.data_policy |=
+                               cpu_to_le32(COEX_HIGH_PRIORITY_ENABLE);
+
+       } else {
+               cmd.client.is_assoc = cpu_to_le32(0);
+
+               /* Allow beacons to pass through as long as we are not
+                * associated, or we do not have dtim period information.
+                */
+               cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON);
+       }
+
+       cmd.client.assoc_id = cpu_to_le32(vif->cfg.aid);
+
+       if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p)
+               cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);
+
+       if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax)
+               cmd.client.data_policy |=
+                       iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(mvm, vif);
+
+       return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
+                                            struct ieee80211_vif *vif,
+                                            u32 action)
+{
+       struct iwl_mac_config_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
+
+       iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_PROMISC |
+                                      MAC_FILTER_IN_CONTROL_AND_MGMT |
+                                      MAC_CFG_FILTER_ACCEPT_BEACON |
+                                      MAC_CFG_FILTER_ACCEPT_PROBE_REQ |
+                                      MAC_CFG_FILTER_ACCEPT_GRP);
+
+       return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif,
+                                        u32 action)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mac_config_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
+
+       iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON |
+                                      MAC_CFG_FILTER_ACCEPT_PROBE_REQ |
+                                      MAC_CFG_FILTER_ACCEPT_GRP);
+
+       /* TODO: Assumes that the beacon id == mac context id */
+       cmd.go_ibss.beacon_template = cpu_to_le32(mvmvif->id);
+
+       return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
+                                              struct ieee80211_vif *vif,
+                                              u32 action)
+{
+       struct iwl_mac_config_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE);
+
+       iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       cmd.p2p_dev.is_disc_extended =
+               iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif);
+
+       /* Override the filter flags to accept only probe requests */
+       cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ);
+
+       return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         u32 action)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mac_config_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_AP);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif,
+                                                &cmd.filter_flags,
+                                                MAC_CFG_FILTER_ACCEPT_PROBE_REQ,
+                                                MAC_CFG_FILTER_ACCEPT_BEACON);
+
+       /* TODO: Assume that the beacon id == mac context id */
+       cmd.go_ibss.beacon_template = cpu_to_le32(mvmvif->id);
+
+       return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mld_mac_ctx_send(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   u32 action, bool force_assoc_off)
+{
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               return iwl_mvm_mld_mac_ctxt_cmd_sta(mvm, vif, action,
+                                                   force_assoc_off);
+       case NL80211_IFTYPE_AP:
+               return iwl_mvm_mld_mac_ctxt_cmd_ap_go(mvm, vif, action);
+       case NL80211_IFTYPE_MONITOR:
+               return iwl_mvm_mld_mac_ctxt_cmd_listener(mvm, vif, action);
+       case NL80211_IFTYPE_P2P_DEVICE:
+               return iwl_mvm_mld_mac_ctxt_cmd_p2p_device(mvm, vif, action);
+       case NL80211_IFTYPE_ADHOC:
+               return iwl_mvm_mld_mac_ctxt_cmd_ibss(mvm, vif, action);
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN))
+               return -EOPNOTSUPP;
+
+       if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n",
+                     vif->addr, ieee80211_vif_type_p2p(vif)))
+               return -EIO;
+
+       ret = iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD,
+                                      true);
+       if (ret)
+               return ret;
+
+       /* will only do anything at resume from D3 time */
+       iwl_mvm_set_last_nonqos_seq(mvm, vif);
+
+       mvmvif->uploaded = true;
+       return 0;
+}
+
+int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                bool force_assoc_off)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN))
+               return -EOPNOTSUPP;
+
+       if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n",
+                     vif->addr, ieee80211_vif_type_p2p(vif)))
+               return -EIO;
+
+       return iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY,
+                                       force_assoc_off);
+}
+
+int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mac_config_cmd cmd = {
+               .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
+               .id_and_color = cpu_to_le32(mvmvif->id),
+       };
+       int ret;
+
+       if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN))
+               return -EOPNOTSUPP;
+
+       if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n",
+                     vif->addr, ieee80211_vif_type_p2p(vif)))
+               return -EIO;
+
+       ret = iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd);
+       if (ret)
+               return ret;
+
+       mvmvif->uploaded = false;
+
+       return 0;
+}