binding.c 3.9 KB
Newer Older
J
Johannes Berg 已提交
1 2 3 4
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
 * Copyright (C) 2012-2014, 2020 Intel Corporation
 * Copyright (C) 2016 Intel Deutschland GmbH
5
 * Copyright (C) 2022 Intel Corporation
J
Johannes Berg 已提交
6
 */
J
Johannes Berg 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#include <net/mac80211.h>
#include "fw-api.h"
#include "mvm.h"

struct iwl_mvm_iface_iterator_data {
	struct ieee80211_vif *ignore_vif;
	int idx;

	struct iwl_mvm_phy_ctxt *phyctxt;

	u16 ids[MAX_MACS_IN_BINDING];
	u16 colors[MAX_MACS_IN_BINDING];
};

static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action,
			       struct iwl_mvm_iface_iterator_data *data)
{
	struct iwl_binding_cmd cmd;
	struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt;
	int i, ret;
	u32 status;
28 29
	int size;

30 31
	memset(&cmd, 0, sizeof(cmd));

32 33 34
	if (fw_has_capa(&mvm->fw->ucode_capa,
			IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT)) {
		size = sizeof(cmd);
35 36
		cmd.lmac_id = cpu_to_le32(iwl_mvm_get_lmac_id(mvm->fw,
							      phyctxt->channel->band));
37 38 39
	} else {
		size = IWL_BINDING_CMD_SIZE_V1;
	}
J
Johannes Berg 已提交
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

	cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
							   phyctxt->color));
	cmd.action = cpu_to_le32(action);
	cmd.phy = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
						  phyctxt->color));

	for (i = 0; i < MAX_MACS_IN_BINDING; i++)
		cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
	for (i = 0; i < data->idx; i++)
		cmd.macs[i] = cpu_to_le32(FW_CMD_ID_AND_COLOR(data->ids[i],
							      data->colors[i]));

	status = 0;
	ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
55
					  size, &cmd, &status);
J
Johannes Berg 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	if (ret) {
		IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n",
			action, ret);
		return ret;
	}

	if (status) {
		IWL_ERR(mvm, "Binding command failed: %u\n", status);
		ret = -EIO;
	}

	return ret;
}

static void iwl_mvm_iface_iterator(void *_data, u8 *mac,
				   struct ieee80211_vif *vif)
{
	struct iwl_mvm_iface_iterator_data *data = _data;
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);

	if (vif == data->ignore_vif)
		return;

79
	if (mvmvif->deflink.phy_ctxt != data->phyctxt)
J
Johannes Berg 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
		return;

	if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING))
		return;

	data->ids[data->idx] = mvmvif->id;
	data->colors[data->idx] = mvmvif->color;
	data->idx++;
}

static int iwl_mvm_binding_update(struct iwl_mvm *mvm,
				  struct ieee80211_vif *vif,
				  struct iwl_mvm_phy_ctxt *phyctxt,
				  bool add)
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	struct iwl_mvm_iface_iterator_data data = {
		.ignore_vif = vif,
		.phyctxt = phyctxt,
	};
	u32 action = FW_CTXT_ACTION_MODIFY;

	lockdep_assert_held(&mvm->mutex);

	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
						   IEEE80211_IFACE_ITER_NORMAL,
						   iwl_mvm_iface_iterator,
						   &data);

	/*
	 * If there are no other interfaces yet we
	 * need to create a new binding.
	 */
	if (data.idx == 0) {
		if (add)
			action = FW_CTXT_ACTION_ADD;
		else
			action = FW_CTXT_ACTION_REMOVE;
	}

	if (add) {
		if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING))
			return -EINVAL;

		data.ids[data.idx] = mvmvif->id;
		data.colors[data.idx] = mvmvif->color;
		data.idx++;
	}

	return iwl_mvm_binding_cmd(mvm, action, &data);
}

int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);

136
	if (WARN_ON_ONCE(!mvmvif->deflink.phy_ctxt))
J
Johannes Berg 已提交
137 138
		return -EINVAL;

139 140 141 142 143 144 145
	/*
	 * Update SF - Disable if needed. if this fails, SF might still be on
	 * while many macs are bound, which is forbidden - so fail the binding.
	 */
	if (iwl_mvm_sf_update(mvm, vif, false))
		return -EINVAL;

146 147
	return iwl_mvm_binding_update(mvm, vif, mvmvif->deflink.phy_ctxt,
				      true);
J
Johannes Berg 已提交
148 149 150 151 152
}

int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
153
	int ret;
J
Johannes Berg 已提交
154

155
	if (WARN_ON_ONCE(!mvmvif->deflink.phy_ctxt))
J
Johannes Berg 已提交
156 157
		return -EINVAL;

158 159
	ret = iwl_mvm_binding_update(mvm, vif, mvmvif->deflink.phy_ctxt,
				     false);
160 161 162 163 164 165

	if (!ret)
		if (iwl_mvm_sf_update(mvm, vif, true))
			IWL_ERR(mvm, "Failed to update SF state\n");

	return ret;
J
Johannes Berg 已提交
166
}