Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10391fb0 authored by Ahmad Masri's avatar Ahmad Masri Committed by Maya Erez
Browse files

wil6210: off channel transmit management frames in AP mode



Currently wil6210 ignores the channel field in the
cfg80211_mgmt_tx_params struct for wil_cfg80211_ops mgmt_tx operation
and sends all management frames on the serving channel.

Add support for off-channel transmission of management frames
(WIPHY_FLAG_OFFCHAN_TX) in AP mode. This is useful in enterprise APs
for sending custom probe request frames.

Change-Id: I0575a2d170c086cec2f60333dbf60f59d788446a
Signed-off-by: default avatarAhmad Masri <amasri@codeaurora.org>
Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
Git-commit: b698e2dfc24cd148ce32f622a20938037ebe06b7
Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git


Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
parent 586460f4
Loading
Loading
Loading
Loading
+40 −7
Original line number Diff line number Diff line
@@ -1354,18 +1354,51 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
	int rc;
	bool tx_status;

	/* Note, currently we do not support the "wait" parameter, user-space
	 * must call remain_on_channel before mgmt_tx or listen on a channel
	 * another way (AP/PCP or connected station)
	 * in addition we need to check if specified "chan" argument is
	 * different from currently "listened" channel and fail if it is.
	wil_dbg_misc(wil, "mgmt_tx: channel %d offchan %d, wait %d\n",
		     params->chan ? params->chan->hw_value : -1,
		     params->offchan,
		     params->wait);

	/* Note, currently we support the "wait" parameter only on AP mode.
	 * In other modes, user-space must call remain_on_channel before
	 * mgmt_tx or listen on a channel other than active one.
	 */

	if (params->chan && params->chan->hw_value == 0) {
		wil_err(wil, "invalid channel\n");
		return -EINVAL;
	}

	if (wdev->iftype != NL80211_IFTYPE_AP) {
		wil_dbg_misc(wil,
			     "send WMI_SW_TX_REQ_CMDID on non-AP interfaces\n");
		rc = wmi_mgmt_tx(vif, buf, len);
	tx_status = (rc == 0);
		goto out;
	}

	if (!params->chan || params->chan->hw_value == vif->channel) {
		wil_dbg_misc(wil,
			     "send WMI_SW_TX_REQ_CMDID for on-channel\n");
		rc = wmi_mgmt_tx(vif, buf, len);
		goto out;
	}

	if (params->offchan == 0) {
		wil_err(wil,
			"invalid channel params: current %d requested %d, off-channel not allowed\n",
			vif->channel, params->chan->hw_value);
		return -EBUSY;
	}

	/* use the wmi_mgmt_tx_ext only on AP mode and off-channel */
	rc = wmi_mgmt_tx_ext(vif, buf, len, params->chan->hw_value,
			     params->wait);

out:
	tx_status = (rc == 0);
	cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
				tx_status, GFP_KERNEL);

	return rc;
}

+3 −0
Original line number Diff line number Diff line
@@ -1134,6 +1134,9 @@ void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
		wiphy->max_sched_scan_plans = WMI_MAX_PLANS_NUM;
	}

	if (test_bit(WMI_FW_CAPABILITY_TX_REQ_EXT, wil->fw_capabilities))
		wiphy->flags |= WIPHY_FLAG_OFFCHAN_TX;

	if (wil->platform_ops.set_features) {
		features = (test_bit(WMI_FW_CAPABILITY_REF_CLOCK_CONTROL,
				     wil->fw_capabilities) &&
+2 −0
Original line number Diff line number Diff line
@@ -1406,6 +1406,8 @@ int wmi_start_sched_scan(struct wil6210_priv *wil,
			 struct cfg80211_sched_scan_request *request);
int wmi_stop_sched_scan(struct wil6210_priv *wil);
int wmi_mgmt_tx(struct wil6210_vif *vif, const u8 *buf, size_t len);
int wmi_mgmt_tx_ext(struct wil6210_vif *vif, const u8 *buf, size_t len,
		    u8 channel, u16 duration_ms);

int wil_wmi2spec_ch(u8 wmi_ch, u8 *spec_ch);
int wil_spec2wmi_ch(u8 spec_ch, u8 *wmi_ch);
+56 −0
Original line number Diff line number Diff line
@@ -467,6 +467,8 @@ static const char *cmdid2name(u16 cmdid)
		return "WMI_CFG_DEF_RX_OFFLOAD_CMD";
	case WMI_LINK_STATS_CMDID:
		return "WMI_LINK_STATS_CMD";
	case WMI_SW_TX_REQ_EXT_CMDID:
		return "WMI_SW_TX_REQ_EXT_CMDID";
	default:
		return "Untracked CMD";
	}
@@ -3330,6 +3332,60 @@ int wmi_mgmt_tx(struct wil6210_vif *vif, const u8 *buf, size_t len)
	return rc;
}

int wmi_mgmt_tx_ext(struct wil6210_vif *vif, const u8 *buf, size_t len,
		    u8 channel, u16 duration_ms)
{
	size_t total;
	struct wil6210_priv *wil = vif_to_wil(vif);
	struct ieee80211_mgmt *mgmt_frame = (void *)buf;
	struct wmi_sw_tx_req_ext_cmd *cmd;
	struct {
		struct wmi_cmd_hdr wmi;
		struct wmi_sw_tx_complete_event evt;
	} __packed evt = {
		.evt = {.status = WMI_FW_STATUS_FAILURE},
	};
	int rc;

	wil_dbg_wmi(wil, "mgmt_tx_ext mid %d channel %d duration %d\n",
		    vif->mid, channel, duration_ms);
	wil_hex_dump_wmi("mgmt_tx_ext frame ", DUMP_PREFIX_OFFSET, 16, 1, buf,
			 len, true);

	if (len < sizeof(struct ieee80211_hdr_3addr)) {
		wil_err(wil, "short frame. len %zu\n", len);
		return -EINVAL;
	}

	total = sizeof(*cmd) + len;
	if (total < len) {
		wil_err(wil, "mgmt_tx_ext invalid len %zu\n", len);
		return -EINVAL;
	}

	cmd = kzalloc(total, GFP_KERNEL);
	if (!cmd)
		return -ENOMEM;

	memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN);
	cmd->len = cpu_to_le16(len);
	memcpy(cmd->payload, buf, len);
	cmd->channel = channel - 1;
	cmd->duration_ms = cpu_to_le16(duration_ms);

	rc = wmi_call(wil, WMI_SW_TX_REQ_EXT_CMDID, vif->mid, cmd, total,
		      WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
	if (!rc && evt.evt.status != WMI_FW_STATUS_SUCCESS) {
		wil_err(wil, "mgmt_tx_ext failed with status %d\n",
			evt.evt.status);
		rc = -EINVAL;
	}

	kfree(cmd);

	return rc;
}

int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id)
{
	int rc;