linux/drivers/net/wireless/intel/iwlwifi/mvm/binding.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2012-2014, 2020 Intel Corporation
   4 * Copyright (C) 2016 Intel Deutschland GmbH
   5 */
   6#include <net/mac80211.h>
   7#include "fw-api.h"
   8#include "mvm.h"
   9
  10struct iwl_mvm_iface_iterator_data {
  11        struct ieee80211_vif *ignore_vif;
  12        int idx;
  13
  14        struct iwl_mvm_phy_ctxt *phyctxt;
  15
  16        u16 ids[MAX_MACS_IN_BINDING];
  17        u16 colors[MAX_MACS_IN_BINDING];
  18};
  19
  20static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action,
  21                               struct iwl_mvm_iface_iterator_data *data)
  22{
  23        struct iwl_binding_cmd cmd;
  24        struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt;
  25        int i, ret;
  26        u32 status;
  27        int size;
  28
  29        memset(&cmd, 0, sizeof(cmd));
  30
  31        if (fw_has_capa(&mvm->fw->ucode_capa,
  32                        IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT)) {
  33                size = sizeof(cmd);
  34                cmd.lmac_id = cpu_to_le32(iwl_mvm_get_lmac_id(mvm->fw,
  35                                                              phyctxt->channel->band));
  36        } else {
  37                size = IWL_BINDING_CMD_SIZE_V1;
  38        }
  39
  40        cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
  41                                                           phyctxt->color));
  42        cmd.action = cpu_to_le32(action);
  43        cmd.phy = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
  44                                                  phyctxt->color));
  45
  46        for (i = 0; i < MAX_MACS_IN_BINDING; i++)
  47                cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
  48        for (i = 0; i < data->idx; i++)
  49                cmd.macs[i] = cpu_to_le32(FW_CMD_ID_AND_COLOR(data->ids[i],
  50                                                              data->colors[i]));
  51
  52        status = 0;
  53        ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
  54                                          size, &cmd, &status);
  55        if (ret) {
  56                IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n",
  57                        action, ret);
  58                return ret;
  59        }
  60
  61        if (status) {
  62                IWL_ERR(mvm, "Binding command failed: %u\n", status);
  63                ret = -EIO;
  64        }
  65
  66        return ret;
  67}
  68
  69static void iwl_mvm_iface_iterator(void *_data, u8 *mac,
  70                                   struct ieee80211_vif *vif)
  71{
  72        struct iwl_mvm_iface_iterator_data *data = _data;
  73        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
  74
  75        if (vif == data->ignore_vif)
  76                return;
  77
  78        if (mvmvif->phy_ctxt != data->phyctxt)
  79                return;
  80
  81        if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING))
  82                return;
  83
  84        data->ids[data->idx] = mvmvif->id;
  85        data->colors[data->idx] = mvmvif->color;
  86        data->idx++;
  87}
  88
  89static int iwl_mvm_binding_update(struct iwl_mvm *mvm,
  90                                  struct ieee80211_vif *vif,
  91                                  struct iwl_mvm_phy_ctxt *phyctxt,
  92                                  bool add)
  93{
  94        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
  95        struct iwl_mvm_iface_iterator_data data = {
  96                .ignore_vif = vif,
  97                .phyctxt = phyctxt,
  98        };
  99        u32 action = FW_CTXT_ACTION_MODIFY;
 100
 101        lockdep_assert_held(&mvm->mutex);
 102
 103        ieee80211_iterate_active_interfaces_atomic(mvm->hw,
 104                                                   IEEE80211_IFACE_ITER_NORMAL,
 105                                                   iwl_mvm_iface_iterator,
 106                                                   &data);
 107
 108        /*
 109         * If there are no other interfaces yet we
 110         * need to create a new binding.
 111         */
 112        if (data.idx == 0) {
 113                if (add)
 114                        action = FW_CTXT_ACTION_ADD;
 115                else
 116                        action = FW_CTXT_ACTION_REMOVE;
 117        }
 118
 119        if (add) {
 120                if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING))
 121                        return -EINVAL;
 122
 123                data.ids[data.idx] = mvmvif->id;
 124                data.colors[data.idx] = mvmvif->color;
 125                data.idx++;
 126        }
 127
 128        return iwl_mvm_binding_cmd(mvm, action, &data);
 129}
 130
 131int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 132{
 133        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 134
 135        if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
 136                return -EINVAL;
 137
 138        /*
 139         * Update SF - Disable if needed. if this fails, SF might still be on
 140         * while many macs are bound, which is forbidden - so fail the binding.
 141         */
 142        if (iwl_mvm_sf_update(mvm, vif, false))
 143                return -EINVAL;
 144
 145        return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
 146}
 147
 148int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 149{
 150        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 151        int ret;
 152
 153        if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
 154                return -EINVAL;
 155
 156        ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
 157
 158        if (!ret)
 159                if (iwl_mvm_sf_update(mvm, vif, true))
 160                        IWL_ERR(mvm, "Failed to update SF state\n");
 161
 162        return ret;
 163}
 164