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