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

Commit 5d1ad5c4 authored by Yu Wang's avatar Yu Wang
Browse files

cnss2: check wlfw mac before switching to mission mode



To make sure WLAN MAC address has already been well
configured in firmware before switching to mission mode,
send QMI message to WLAN firmware to query the current
MAC status, and wait for 10 seconds at most if it's not
ready.

A new DTS property 'use-nv-mac' is also added:
if 'use-nv-mac' is defined in WLAN dts node, the MAC
checking will be performed; otherwise, it won't.

Change-Id: I1e751d84fb304cb95ea0f989f6939bc7ba5a3ecc
Signed-off-by: default avatarYu Wang <yyuwang@codeaurora.org>
parent b27b7814
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -2126,6 +2126,13 @@ static const struct of_device_id cnss_of_match_table[] = {
};
MODULE_DEVICE_TABLE(of, cnss_of_match_table);

static inline bool
cnss_use_nv_mac(struct cnss_plat_data *plat_priv)
{
	return of_property_read_bool(plat_priv->plat_dev->dev.of_node,
				     "use-nv-mac");
}

static int cnss_probe(struct platform_device *plat_dev)
{
	int ret = 0;
@@ -2158,6 +2165,7 @@ static int cnss_probe(struct platform_device *plat_dev)
	plat_priv->plat_dev = plat_dev;
	plat_priv->device_id = device_id->driver_data;
	plat_priv->bus_type = cnss_get_bus_type(plat_priv->device_id);
	plat_priv->use_nv_mac = cnss_use_nv_mac(plat_priv);
	cnss_set_plat_priv(plat_dev, plat_priv);
	platform_set_drvdata(plat_dev, plat_priv);
	INIT_LIST_HEAD(&plat_priv->vreg_list);
+1 −0
Original line number Diff line number Diff line
@@ -361,6 +361,7 @@ struct cnss_plat_data {
	u64 dynamic_feature;
	void *get_info_cb_ctx;
	int (*get_info_cb)(void *ctx, void *event, int event_len);
	u8 use_nv_mac;
};

#ifdef CONFIG_ARCH_QCOM
+131 −0
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@

#define QMI_WLFW_MAX_RECV_BUF_SIZE	SZ_8K

#define QMI_WLFW_MAC_READY_TIMEOUT_MS	50
#define QMI_WLFW_MAC_READY_MAX_RETRY	200

static char *cnss_qmi_mode_to_str(enum cnss_driver_mode mode)
{
	switch (mode) {
@@ -690,6 +693,131 @@ int cnss_wlfw_m3_dnld_send_sync(struct cnss_plat_data *plat_priv)
	return ret;
}

static int cnss_wlfw_wlan_mac_req_send_sync(struct cnss_plat_data *plat_priv,
					    u8 *mac, u32 mac_len)
{
	struct wlfw_mac_addr_req_msg_v01 *req;
	struct wlfw_mac_addr_resp_msg_v01 *resp;
	struct qmi_txn txn;
	int ret;
	u8 is_query;

	if (!plat_priv)
		return -ENODEV;

	/* NULL mac && zero mac_len means querying the status of MAC in FW */
	if ((mac && mac_len != QMI_WLFW_MAC_ADDR_SIZE_V01) ||
	    (!mac && mac_len != 0))
		return -EINVAL;

	req = kzalloc(sizeof(*req), GFP_KERNEL);
	if (!req)
		return -ENOMEM;

	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
	if (!resp) {
		kfree(req);
		return -ENOMEM;
	}

	ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn,
			   wlfw_mac_addr_resp_msg_v01_ei, resp);
	if (ret < 0) {
		cnss_pr_err("Failed to initialize txn for mac req, err: %d\n",
			    ret);
		ret = -EIO;
		goto out;
	}

	is_query = !mac;
	if (!is_query) {
		/* DO NOT print this for mac query, that might be too many */
		cnss_pr_dbg("Sending WLAN mac req [%pM], state: 0x%lx\n",
			    mac, plat_priv->driver_state);
		memcpy(req->mac_addr, mac, mac_len);

		/* 0 - query status of wlfw MAC; 1 - set wlfw MAC */
		req->mac_addr_valid = 1;
	}

	ret = qmi_send_request(&plat_priv->qmi_wlfw, NULL, &txn,
			       QMI_WLFW_MAC_ADDR_REQ_V01,
			       WLFW_MAC_ADDR_REQ_MSG_V01_MAX_MSG_LEN,
			       wlfw_mac_addr_req_msg_v01_ei, req);
	if (ret < 0) {
		qmi_txn_cancel(&txn);
		cnss_pr_err("Failed to send mac req, err: %d\n", ret);

		ret = -EIO;
		goto out;
	}

	ret = qmi_txn_wait(&txn, QMI_WLFW_TIMEOUT_JF);
	if (ret < 0) {
		cnss_pr_err("Failed to wait for resp of mac req, err: %d\n",
			    ret);

		ret = -EIO;
		goto out;
	}

	if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
		cnss_pr_err("WLAN mac req failed, result: %d, err: %d\n",
			    resp->resp.result);

		ret = -EIO;
		goto out;
	}

	if (resp->resp.error != QMI_ERR_NONE_V01) {
		ret = ((resp->resp.error == QMI_ERR_NETWORK_NOT_READY_V01 &&
			is_query) ? -EAGAIN : -EIO);
		if (ret != -EAGAIN)
			cnss_pr_err("Got error resp for mac req, err: %d\n",
				    resp->resp.error);
		goto out;
	}

	cnss_pr_dbg("WLAN mac req completed\n");

out:
	kfree(req);
	kfree(resp);
	return ret;
}

static void cnss_wait_for_wlfw_mac_ready(struct cnss_plat_data *plat_priv)
{
	int ret, retry = 0;

	if (!plat_priv)
		return;

	cnss_pr_dbg("Checking wlfw mac, state: 0x%lx\n",
		    plat_priv->driver_state);
	do {
		/* query the current status of WLAN MAC */
		ret = cnss_wlfw_wlan_mac_req_send_sync(plat_priv, NULL, 0);
		if (!ret) {
			cnss_pr_dbg("wlfw mac is ready\n");
			break;
		}

		if (ret != -EAGAIN) {
			cnss_pr_err("failed to query wlfw mac, error: %d\n",
				    ret);
			break;
		}

		if (++retry >= QMI_WLFW_MAC_READY_MAX_RETRY) {
			cnss_pr_err("Timeout to wait for wlfw mac ready\n");
			break;
		}

		msleep(QMI_WLFW_MAC_READY_TIMEOUT_MS);
	} while (true);
}

int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv,
				  enum cnss_driver_mode mode)
{
@@ -701,6 +829,9 @@ int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv,
	if (!plat_priv)
		return -ENODEV;

	if (mode == CNSS_MISSION && plat_priv->use_nv_mac)
		cnss_wait_for_wlfw_mac_ready(plat_priv);

	cnss_pr_dbg("Sending mode message, mode: %s(%d), state: 0x%lx\n",
		    cnss_qmi_mode_to_str(mode), mode, plat_priv->driver_state);

+1 −0
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ struct qmi_elem_info {
#define QMI_ERR_INTERNAL_V01			3
#define QMI_ERR_CLIENT_IDS_EXHAUSTED_V01	5
#define QMI_ERR_INVALID_ID_V01			41
#define QMI_ERR_NETWORK_NOT_READY_V01		53
#define QMI_ERR_ENCODING_V01			58
#define QMI_ERR_DISABLED_V01			69
#define QMI_ERR_INCOMPATIBLE_STATE_V01		90