xref: /linux/drivers/net/wireless/intel/iwlwifi/mvm/binding.c (revision 02680c23d7b3febe45ea3d4f9818c2b2dc89020a)
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