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 10 struct 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 20 static 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 69 static 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 89 static 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 131 int 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 148 int 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