All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jerome Pouiller <Jerome.Pouiller@silabs.com>
To: "devel@driverdev.osuosl.org" <devel@driverdev.osuosl.org>,
	"linux-wireless@vger.kernel.org" <linux-wireless@vger.kernel.org>
Cc: "netdev@vger.kernel.org" <netdev@vger.kernel.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Kalle Valo <kvalo@codeaurora.org>,
	"David S . Miller" <davem@davemloft.net>,
	David Le Goff <David.Legoff@silabs.com>,
	Jerome Pouiller <Jerome.Pouiller@silabs.com>
Subject: [PATCH v3 11/20] staging: wfx: allow to send commands to chip
Date: Thu, 19 Sep 2019 14:25:42 +0000	[thread overview]
Message-ID: <20190919142527.31797-12-Jerome.Pouiller@silabs.com> (raw)
In-Reply-To: <20190919142527.31797-1-Jerome.Pouiller@silabs.com>

From: Jérôme Pouiller <jerome.pouiller@silabs.com>

Chip has multiple input buffers and can handle multiple 802.11 frames
in parallel. However, other HIF command must be sent sequentially.
wsm_send_cmd() handles these requests.

This commit also add send_hif_cmd in debugfs. This file allows to send
arbitrary commands to chip. It can be used for debug and testing.

Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
---
 drivers/staging/wfx/Makefile |   1 +
 drivers/staging/wfx/bh.c     |   5 +-
 drivers/staging/wfx/debug.c  | 130 +++++++++++++++++++++++++++++++++++
 drivers/staging/wfx/debug.h  |   4 ++
 drivers/staging/wfx/hif_rx.c |  45 ++++++++++++
 drivers/staging/wfx/hif_tx.c |  87 +++++++++++++++++++++++
 drivers/staging/wfx/hif_tx.h |  33 +++++++++
 drivers/staging/wfx/main.c   |   1 +
 drivers/staging/wfx/wfx.h    |   4 ++
 9 files changed, 309 insertions(+), 1 deletion(-)
 create mode 100644 drivers/staging/wfx/hif_tx.c
 create mode 100644 drivers/staging/wfx/hif_tx.h

diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile
index 2896a2127c88..e158589468a3 100644
--- a/drivers/staging/wfx/Makefile
+++ b/drivers/staging/wfx/Makefile
@@ -7,6 +7,7 @@ wfx-y := \
 	bh.o \
 	hwio.o \
 	fwio.o \
+	hif_tx.o \
 	hif_rx.o \
 	main.o \
 	sta.o \
diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c
index c40da3f1f25d..c94c9c401a69 100644
--- a/drivers/staging/wfx/bh.c
+++ b/drivers/staging/wfx/bh.c
@@ -190,7 +190,10 @@ static int bh_work_tx(struct wfx_dev *wdev, int max_msg)
 	for (i = 0; i < max_msg; i++) {
 		hif = NULL;
 		if (wdev->hif.tx_buffers_used < wdev->hw_caps.num_inp_ch_bufs) {
-			/* FIXME: get queued data */
+			if (try_wait_for_completion(&wdev->hif_cmd.ready)) {
+				WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error");
+				hif = wdev->hif_cmd.buf_send;
+			}
 		}
 		if (!hif)
 			return i;
diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c
index f28c94d8de89..0a328c96eaa0 100644
--- a/drivers/staging/wfx/debug.c
+++ b/drivers/staging/wfx/debug.c
@@ -7,16 +7,146 @@
  */
 #include <linux/debugfs.h>
 
+#include "debug.h"
 #include "wfx.h"
 
 #define CREATE_TRACE_POINTS
 #include "traces.h"
 
+static const struct trace_print_flags hif_msg_print_map[] = {
+	hif_msg_list,
+};
+
+static const struct trace_print_flags hif_mib_print_map[] = {
+	hif_mib_list,
+};
+
+static const struct trace_print_flags wfx_reg_print_map[] = {
+	wfx_reg_list,
+};
+
+static const char *get_symbol(unsigned long val,
+		const struct trace_print_flags *symbol_array)
+{
+	int i;
+
+	for (i = 0; symbol_array[i].mask != -1; i++) {
+		if (val == symbol_array[i].mask)
+			return symbol_array[i].name;
+	}
+
+	return "unknown";
+}
+
+const char *get_hif_name(unsigned long id)
+{
+	return get_symbol(id, hif_msg_print_map);
+}
+
+const char *get_mib_name(unsigned long id)
+{
+	return get_symbol(id, hif_mib_print_map);
+}
+
+const char *get_reg_name(unsigned long id)
+{
+	return get_symbol(id, wfx_reg_print_map);
+}
+
+struct dbgfs_hif_msg {
+	struct wfx_dev *wdev;
+	struct completion complete;
+	u8 reply[1024];
+	int ret;
+};
+
+static ssize_t wfx_send_hif_msg_write(struct file *file, const char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct dbgfs_hif_msg *context = file->private_data;
+	struct wfx_dev *wdev = context->wdev;
+	struct hif_msg *request;
+
+	if (completion_done(&context->complete)) {
+		dev_dbg(wdev->dev, "read previous result before start a new one\n");
+		return -EBUSY;
+	}
+	if (count < sizeof(struct hif_msg))
+		return -EINVAL;
+
+	// wfx_cmd_send() chekc that reply buffer is wide enough, but do not
+	// return precise length read. User have to know how many bytes should
+	// be read. Filling reply buffer with a memory pattern may help user.
+	memset(context->reply, sizeof(context->reply), 0xFF);
+	request = memdup_user(user_buf, count);
+	if (IS_ERR(request))
+		return PTR_ERR(request);
+	if (request->len != count) {
+		kfree(request);
+		return -EINVAL;
+	}
+	context->ret = wfx_cmd_send(wdev, request, context->reply, sizeof(context->reply), false);
+
+	kfree(request);
+	complete(&context->complete);
+	return count;
+}
+
+static ssize_t wfx_send_hif_msg_read(struct file *file, char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct dbgfs_hif_msg *context = file->private_data;
+	int ret;
+
+	if (count > sizeof(context->reply))
+		return -EINVAL;
+	ret = wait_for_completion_interruptible(&context->complete);
+	if (ret)
+		return ret;
+	if (context->ret < 0)
+		return context->ret;
+	// Be carefull, write() is waiting for a full message while read()
+	// only return a payload
+	ret = copy_to_user(user_buf, context->reply, count);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static int wfx_send_hif_msg_open(struct inode *inode, struct file *file)
+{
+	struct dbgfs_hif_msg *context = kzalloc(sizeof(*context), GFP_KERNEL);
+
+	if (!context)
+		return -ENOMEM;
+	context->wdev = inode->i_private;
+	init_completion(&context->complete);
+	file->private_data = context;
+	return 0;
+}
+
+static int wfx_send_hif_msg_release(struct inode *inode, struct file *file)
+{
+	struct dbgfs_hif_msg *context = file->private_data;
+
+	kfree(context);
+	return 0;
+}
+
+static const struct file_operations wfx_send_hif_msg_fops = {
+	.open = wfx_send_hif_msg_open,
+	.release = wfx_send_hif_msg_release,
+	.write = wfx_send_hif_msg_write,
+	.read = wfx_send_hif_msg_read,
+};
+
 int wfx_debug_init(struct wfx_dev *wdev)
 {
 	struct dentry *d;
 
 	d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir);
+	debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops);
 
 	return 0;
 }
diff --git a/drivers/staging/wfx/debug.h b/drivers/staging/wfx/debug.h
index 8bfba1a9fa20..6f2f84d64c9e 100644
--- a/drivers/staging/wfx/debug.h
+++ b/drivers/staging/wfx/debug.h
@@ -12,4 +12,8 @@ struct wfx_dev;
 
 int wfx_debug_init(struct wfx_dev *wdev);
 
+const char *get_hif_name(unsigned long id);
+const char *get_mib_name(unsigned long id);
+const char *get_reg_name(unsigned long id);
+
 #endif /* WFX_DEBUG_H */
diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c
index 5c207e6d4348..ba8ea4f3c91b 100644
--- a/drivers/staging/wfx/hif_rx.c
+++ b/drivers/staging/wfx/hif_rx.c
@@ -13,6 +13,43 @@
 #include "wfx.h"
 #include "hif_api_cmd.h"
 
+static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
+{
+	// All confirm messages start with status
+	int status = le32_to_cpu(*((__le32 *) buf));
+	int cmd = hif->id;
+	int len = hif->len - 4; // drop header
+
+	WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error");
+
+	if (!wdev->hif_cmd.buf_send) {
+		dev_warn(wdev->dev, "unexpected confirmation: 0x%.2x\n", cmd);
+		return -EINVAL;
+	}
+
+	if (cmd != wdev->hif_cmd.buf_send->id) {
+		dev_warn(wdev->dev, "chip response mismatch request: 0x%.2x vs 0x%.2x\n",
+			 cmd, wdev->hif_cmd.buf_send->id);
+		return -EINVAL;
+	}
+
+	if (wdev->hif_cmd.buf_recv) {
+		if (wdev->hif_cmd.len_recv >= len)
+			memcpy(wdev->hif_cmd.buf_recv, buf, len);
+		else
+			status = -ENOMEM;
+	}
+	wdev->hif_cmd.ret = status;
+
+	if (!wdev->hif_cmd.async) {
+		complete(&wdev->hif_cmd.done);
+	} else {
+		wdev->hif_cmd.buf_send = NULL;
+		mutex_unlock(&wdev->hif_cmd.lock);
+	}
+	return status;
+}
+
 static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
 {
 	struct hif_ind_startup *body = buf;
@@ -44,6 +81,14 @@ void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb)
 	struct hif_msg *hif = (struct hif_msg *) skb->data;
 	int hif_id = hif->id;
 
+	// Note: mutex_is_lock cause an implicit memory barrier that protect
+	// buf_send
+	if (mutex_is_locked(&wdev->hif_cmd.lock)
+	    && wdev->hif_cmd.buf_send
+	    && wdev->hif_cmd.buf_send->id == hif_id) {
+		hif_generic_confirm(wdev, hif, hif->body);
+		goto free;
+	}
 	for (i = 0; i < ARRAY_SIZE(hif_handlers); i++) {
 		if (hif_handlers[i].msg_id == hif_id) {
 			if (hif_handlers[i].handler)
diff --git a/drivers/staging/wfx/hif_tx.c b/drivers/staging/wfx/hif_tx.c
new file mode 100644
index 000000000000..f81a19089db4
--- /dev/null
+++ b/drivers/staging/wfx/hif_tx.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx
+ * Split Mac (WSM) API.
+ *
+ * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+
+#include "hif_tx.h"
+#include "wfx.h"
+#include "bh.h"
+#include "debug.h"
+
+void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd)
+{
+	init_completion(&hif_cmd->ready);
+	init_completion(&hif_cmd->done);
+	mutex_init(&hif_cmd->lock);
+}
+
+int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, size_t reply_len, bool async)
+{
+	const char *mib_name = "";
+	const char *mib_sep = "";
+	int cmd = request->id;
+	int vif = request->interface;
+	int ret;
+
+	WARN(wdev->hif_cmd.buf_recv && wdev->hif_cmd.async, "API usage error");
+
+	// Do not wait for any reply if chip is frozen
+	if (wdev->chip_frozen)
+		return -ETIMEDOUT;
+
+	mutex_lock(&wdev->hif_cmd.lock);
+	WARN(wdev->hif_cmd.buf_send, "data locking error");
+
+	// Note: call to complete() below has an implicit memory barrier that
+	// hopefully protect buf_send
+	wdev->hif_cmd.buf_send = request;
+	wdev->hif_cmd.buf_recv = reply;
+	wdev->hif_cmd.len_recv = reply_len;
+	wdev->hif_cmd.async = async;
+	complete(&wdev->hif_cmd.ready);
+
+	wfx_bh_request_tx(wdev);
+
+	// NOTE: no timeout is catched async is enabled
+	if (async)
+		return 0;
+
+	ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 1 * HZ);
+	if (!ret) {
+		dev_err(wdev->dev, "chip is abnormally long to answer\n");
+		reinit_completion(&wdev->hif_cmd.ready);
+		ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 3 * HZ);
+	}
+	if (!ret) {
+		dev_err(wdev->dev, "chip did not answer\n");
+		wdev->chip_frozen = 1;
+		reinit_completion(&wdev->hif_cmd.done);
+		ret = -ETIMEDOUT;
+	} else {
+		ret = wdev->hif_cmd.ret;
+	}
+
+	wdev->hif_cmd.buf_send = NULL;
+	mutex_unlock(&wdev->hif_cmd.lock);
+
+	if (ret && (cmd == HIF_REQ_ID_READ_MIB || cmd == HIF_REQ_ID_WRITE_MIB)) {
+		mib_name = get_mib_name(((u16 *) request)[2]);
+		mib_sep = "/";
+	}
+	if (ret < 0)
+		dev_err(wdev->dev,
+			"WSM request %s%s%s (%#.2x) on vif %d returned error %d\n",
+			get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret);
+	if (ret > 0)
+		dev_warn(wdev->dev,
+			 "WSM request %s%s%s (%#.2x) on vif %d returned status %d\n",
+			 get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret);
+
+	return ret;
+}
diff --git a/drivers/staging/wfx/hif_tx.h b/drivers/staging/wfx/hif_tx.h
new file mode 100644
index 000000000000..ccf2b7e5e851
--- /dev/null
+++ b/drivers/staging/wfx/hif_tx.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx
+ * Split Mac (WSM) API.
+ *
+ * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ * Copyright (C) 2010, ST-Ericsson SA
+ */
+#ifndef WFX_HIF_TX_H
+#define WFX_HIF_TX_H
+
+#include "hif_api_cmd.h"
+
+struct wfx_dev;
+struct wfx_vif;
+
+struct wfx_hif_cmd {
+	struct mutex      lock;
+	struct completion ready;
+	struct completion done;
+	bool              async;
+	struct hif_msg    *buf_send;
+	void              *buf_recv;
+	size_t            len_recv;
+	int               ret;
+};
+
+void wfx_init_hif_cmd(struct wfx_hif_cmd *wfx_hif_cmd);
+int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request,
+		 void *reply, size_t reply_len, bool async);
+
+#endif
diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c
index ca0ca873bd7d..8973eeb60eb8 100644
--- a/drivers/staging/wfx/main.c
+++ b/drivers/staging/wfx/main.c
@@ -115,6 +115,7 @@ struct wfx_dev *wfx_init_common(struct device *dev,
 	memcpy(&wdev->pdata, pdata, sizeof(*pdata));
 
 	init_completion(&wdev->firmware_ready);
+	wfx_init_hif_cmd(&wdev->hif_cmd);
 
 	return wdev;
 }
diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h
index a7e571e0da30..bf9de11f8896 100644
--- a/drivers/staging/wfx/wfx.h
+++ b/drivers/staging/wfx/wfx.h
@@ -15,6 +15,7 @@
 
 #include "bh.h"
 #include "main.h"
+#include "hif_tx.h"
 #include "hif_api_general.h"
 
 struct hwbus_ops;
@@ -32,6 +33,9 @@ struct wfx_dev {
 	struct completion	firmware_ready;
 	struct hif_ind_startup	hw_caps;
 	struct wfx_hif		hif;
+	int			chip_frozen;
+
+	struct wfx_hif_cmd	hif_cmd;
 };
 
 struct wfx_vif {
-- 
2.20.1

WARNING: multiple messages have this Message-ID (diff)
From: Jerome Pouiller <Jerome.Pouiller@silabs.com>
To: "devel@driverdev.osuosl.org" <devel@driverdev.osuosl.org>,
	"linux-wireless@vger.kernel.org" <linux-wireless@vger.kernel.org>
Cc: "netdev@vger.kernel.org" <netdev@vger.kernel.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	David Le Goff <David.Legoff@silabs.com>,
	"David S . Miller" <davem@davemloft.net>,
	Kalle Valo <kvalo@codeaurora.org>
Subject: [PATCH v3 11/20] staging: wfx: allow to send commands to chip
Date: Thu, 19 Sep 2019 14:25:42 +0000	[thread overview]
Message-ID: <20190919142527.31797-12-Jerome.Pouiller@silabs.com> (raw)
In-Reply-To: <20190919142527.31797-1-Jerome.Pouiller@silabs.com>

From: Jérôme Pouiller <jerome.pouiller@silabs.com>

Chip has multiple input buffers and can handle multiple 802.11 frames
in parallel. However, other HIF command must be sent sequentially.
wsm_send_cmd() handles these requests.

This commit also add send_hif_cmd in debugfs. This file allows to send
arbitrary commands to chip. It can be used for debug and testing.

Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
---
 drivers/staging/wfx/Makefile |   1 +
 drivers/staging/wfx/bh.c     |   5 +-
 drivers/staging/wfx/debug.c  | 130 +++++++++++++++++++++++++++++++++++
 drivers/staging/wfx/debug.h  |   4 ++
 drivers/staging/wfx/hif_rx.c |  45 ++++++++++++
 drivers/staging/wfx/hif_tx.c |  87 +++++++++++++++++++++++
 drivers/staging/wfx/hif_tx.h |  33 +++++++++
 drivers/staging/wfx/main.c   |   1 +
 drivers/staging/wfx/wfx.h    |   4 ++
 9 files changed, 309 insertions(+), 1 deletion(-)
 create mode 100644 drivers/staging/wfx/hif_tx.c
 create mode 100644 drivers/staging/wfx/hif_tx.h

diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile
index 2896a2127c88..e158589468a3 100644
--- a/drivers/staging/wfx/Makefile
+++ b/drivers/staging/wfx/Makefile
@@ -7,6 +7,7 @@ wfx-y := \
 	bh.o \
 	hwio.o \
 	fwio.o \
+	hif_tx.o \
 	hif_rx.o \
 	main.o \
 	sta.o \
diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c
index c40da3f1f25d..c94c9c401a69 100644
--- a/drivers/staging/wfx/bh.c
+++ b/drivers/staging/wfx/bh.c
@@ -190,7 +190,10 @@ static int bh_work_tx(struct wfx_dev *wdev, int max_msg)
 	for (i = 0; i < max_msg; i++) {
 		hif = NULL;
 		if (wdev->hif.tx_buffers_used < wdev->hw_caps.num_inp_ch_bufs) {
-			/* FIXME: get queued data */
+			if (try_wait_for_completion(&wdev->hif_cmd.ready)) {
+				WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error");
+				hif = wdev->hif_cmd.buf_send;
+			}
 		}
 		if (!hif)
 			return i;
diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c
index f28c94d8de89..0a328c96eaa0 100644
--- a/drivers/staging/wfx/debug.c
+++ b/drivers/staging/wfx/debug.c
@@ -7,16 +7,146 @@
  */
 #include <linux/debugfs.h>
 
+#include "debug.h"
 #include "wfx.h"
 
 #define CREATE_TRACE_POINTS
 #include "traces.h"
 
+static const struct trace_print_flags hif_msg_print_map[] = {
+	hif_msg_list,
+};
+
+static const struct trace_print_flags hif_mib_print_map[] = {
+	hif_mib_list,
+};
+
+static const struct trace_print_flags wfx_reg_print_map[] = {
+	wfx_reg_list,
+};
+
+static const char *get_symbol(unsigned long val,
+		const struct trace_print_flags *symbol_array)
+{
+	int i;
+
+	for (i = 0; symbol_array[i].mask != -1; i++) {
+		if (val == symbol_array[i].mask)
+			return symbol_array[i].name;
+	}
+
+	return "unknown";
+}
+
+const char *get_hif_name(unsigned long id)
+{
+	return get_symbol(id, hif_msg_print_map);
+}
+
+const char *get_mib_name(unsigned long id)
+{
+	return get_symbol(id, hif_mib_print_map);
+}
+
+const char *get_reg_name(unsigned long id)
+{
+	return get_symbol(id, wfx_reg_print_map);
+}
+
+struct dbgfs_hif_msg {
+	struct wfx_dev *wdev;
+	struct completion complete;
+	u8 reply[1024];
+	int ret;
+};
+
+static ssize_t wfx_send_hif_msg_write(struct file *file, const char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct dbgfs_hif_msg *context = file->private_data;
+	struct wfx_dev *wdev = context->wdev;
+	struct hif_msg *request;
+
+	if (completion_done(&context->complete)) {
+		dev_dbg(wdev->dev, "read previous result before start a new one\n");
+		return -EBUSY;
+	}
+	if (count < sizeof(struct hif_msg))
+		return -EINVAL;
+
+	// wfx_cmd_send() chekc that reply buffer is wide enough, but do not
+	// return precise length read. User have to know how many bytes should
+	// be read. Filling reply buffer with a memory pattern may help user.
+	memset(context->reply, sizeof(context->reply), 0xFF);
+	request = memdup_user(user_buf, count);
+	if (IS_ERR(request))
+		return PTR_ERR(request);
+	if (request->len != count) {
+		kfree(request);
+		return -EINVAL;
+	}
+	context->ret = wfx_cmd_send(wdev, request, context->reply, sizeof(context->reply), false);
+
+	kfree(request);
+	complete(&context->complete);
+	return count;
+}
+
+static ssize_t wfx_send_hif_msg_read(struct file *file, char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct dbgfs_hif_msg *context = file->private_data;
+	int ret;
+
+	if (count > sizeof(context->reply))
+		return -EINVAL;
+	ret = wait_for_completion_interruptible(&context->complete);
+	if (ret)
+		return ret;
+	if (context->ret < 0)
+		return context->ret;
+	// Be carefull, write() is waiting for a full message while read()
+	// only return a payload
+	ret = copy_to_user(user_buf, context->reply, count);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static int wfx_send_hif_msg_open(struct inode *inode, struct file *file)
+{
+	struct dbgfs_hif_msg *context = kzalloc(sizeof(*context), GFP_KERNEL);
+
+	if (!context)
+		return -ENOMEM;
+	context->wdev = inode->i_private;
+	init_completion(&context->complete);
+	file->private_data = context;
+	return 0;
+}
+
+static int wfx_send_hif_msg_release(struct inode *inode, struct file *file)
+{
+	struct dbgfs_hif_msg *context = file->private_data;
+
+	kfree(context);
+	return 0;
+}
+
+static const struct file_operations wfx_send_hif_msg_fops = {
+	.open = wfx_send_hif_msg_open,
+	.release = wfx_send_hif_msg_release,
+	.write = wfx_send_hif_msg_write,
+	.read = wfx_send_hif_msg_read,
+};
+
 int wfx_debug_init(struct wfx_dev *wdev)
 {
 	struct dentry *d;
 
 	d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir);
+	debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops);
 
 	return 0;
 }
diff --git a/drivers/staging/wfx/debug.h b/drivers/staging/wfx/debug.h
index 8bfba1a9fa20..6f2f84d64c9e 100644
--- a/drivers/staging/wfx/debug.h
+++ b/drivers/staging/wfx/debug.h
@@ -12,4 +12,8 @@ struct wfx_dev;
 
 int wfx_debug_init(struct wfx_dev *wdev);
 
+const char *get_hif_name(unsigned long id);
+const char *get_mib_name(unsigned long id);
+const char *get_reg_name(unsigned long id);
+
 #endif /* WFX_DEBUG_H */
diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c
index 5c207e6d4348..ba8ea4f3c91b 100644
--- a/drivers/staging/wfx/hif_rx.c
+++ b/drivers/staging/wfx/hif_rx.c
@@ -13,6 +13,43 @@
 #include "wfx.h"
 #include "hif_api_cmd.h"
 
+static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
+{
+	// All confirm messages start with status
+	int status = le32_to_cpu(*((__le32 *) buf));
+	int cmd = hif->id;
+	int len = hif->len - 4; // drop header
+
+	WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error");
+
+	if (!wdev->hif_cmd.buf_send) {
+		dev_warn(wdev->dev, "unexpected confirmation: 0x%.2x\n", cmd);
+		return -EINVAL;
+	}
+
+	if (cmd != wdev->hif_cmd.buf_send->id) {
+		dev_warn(wdev->dev, "chip response mismatch request: 0x%.2x vs 0x%.2x\n",
+			 cmd, wdev->hif_cmd.buf_send->id);
+		return -EINVAL;
+	}
+
+	if (wdev->hif_cmd.buf_recv) {
+		if (wdev->hif_cmd.len_recv >= len)
+			memcpy(wdev->hif_cmd.buf_recv, buf, len);
+		else
+			status = -ENOMEM;
+	}
+	wdev->hif_cmd.ret = status;
+
+	if (!wdev->hif_cmd.async) {
+		complete(&wdev->hif_cmd.done);
+	} else {
+		wdev->hif_cmd.buf_send = NULL;
+		mutex_unlock(&wdev->hif_cmd.lock);
+	}
+	return status;
+}
+
 static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
 {
 	struct hif_ind_startup *body = buf;
@@ -44,6 +81,14 @@ void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb)
 	struct hif_msg *hif = (struct hif_msg *) skb->data;
 	int hif_id = hif->id;
 
+	// Note: mutex_is_lock cause an implicit memory barrier that protect
+	// buf_send
+	if (mutex_is_locked(&wdev->hif_cmd.lock)
+	    && wdev->hif_cmd.buf_send
+	    && wdev->hif_cmd.buf_send->id == hif_id) {
+		hif_generic_confirm(wdev, hif, hif->body);
+		goto free;
+	}
 	for (i = 0; i < ARRAY_SIZE(hif_handlers); i++) {
 		if (hif_handlers[i].msg_id == hif_id) {
 			if (hif_handlers[i].handler)
diff --git a/drivers/staging/wfx/hif_tx.c b/drivers/staging/wfx/hif_tx.c
new file mode 100644
index 000000000000..f81a19089db4
--- /dev/null
+++ b/drivers/staging/wfx/hif_tx.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx
+ * Split Mac (WSM) API.
+ *
+ * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+
+#include "hif_tx.h"
+#include "wfx.h"
+#include "bh.h"
+#include "debug.h"
+
+void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd)
+{
+	init_completion(&hif_cmd->ready);
+	init_completion(&hif_cmd->done);
+	mutex_init(&hif_cmd->lock);
+}
+
+int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, size_t reply_len, bool async)
+{
+	const char *mib_name = "";
+	const char *mib_sep = "";
+	int cmd = request->id;
+	int vif = request->interface;
+	int ret;
+
+	WARN(wdev->hif_cmd.buf_recv && wdev->hif_cmd.async, "API usage error");
+
+	// Do not wait for any reply if chip is frozen
+	if (wdev->chip_frozen)
+		return -ETIMEDOUT;
+
+	mutex_lock(&wdev->hif_cmd.lock);
+	WARN(wdev->hif_cmd.buf_send, "data locking error");
+
+	// Note: call to complete() below has an implicit memory barrier that
+	// hopefully protect buf_send
+	wdev->hif_cmd.buf_send = request;
+	wdev->hif_cmd.buf_recv = reply;
+	wdev->hif_cmd.len_recv = reply_len;
+	wdev->hif_cmd.async = async;
+	complete(&wdev->hif_cmd.ready);
+
+	wfx_bh_request_tx(wdev);
+
+	// NOTE: no timeout is catched async is enabled
+	if (async)
+		return 0;
+
+	ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 1 * HZ);
+	if (!ret) {
+		dev_err(wdev->dev, "chip is abnormally long to answer\n");
+		reinit_completion(&wdev->hif_cmd.ready);
+		ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 3 * HZ);
+	}
+	if (!ret) {
+		dev_err(wdev->dev, "chip did not answer\n");
+		wdev->chip_frozen = 1;
+		reinit_completion(&wdev->hif_cmd.done);
+		ret = -ETIMEDOUT;
+	} else {
+		ret = wdev->hif_cmd.ret;
+	}
+
+	wdev->hif_cmd.buf_send = NULL;
+	mutex_unlock(&wdev->hif_cmd.lock);
+
+	if (ret && (cmd == HIF_REQ_ID_READ_MIB || cmd == HIF_REQ_ID_WRITE_MIB)) {
+		mib_name = get_mib_name(((u16 *) request)[2]);
+		mib_sep = "/";
+	}
+	if (ret < 0)
+		dev_err(wdev->dev,
+			"WSM request %s%s%s (%#.2x) on vif %d returned error %d\n",
+			get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret);
+	if (ret > 0)
+		dev_warn(wdev->dev,
+			 "WSM request %s%s%s (%#.2x) on vif %d returned status %d\n",
+			 get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret);
+
+	return ret;
+}
diff --git a/drivers/staging/wfx/hif_tx.h b/drivers/staging/wfx/hif_tx.h
new file mode 100644
index 000000000000..ccf2b7e5e851
--- /dev/null
+++ b/drivers/staging/wfx/hif_tx.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx
+ * Split Mac (WSM) API.
+ *
+ * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ * Copyright (C) 2010, ST-Ericsson SA
+ */
+#ifndef WFX_HIF_TX_H
+#define WFX_HIF_TX_H
+
+#include "hif_api_cmd.h"
+
+struct wfx_dev;
+struct wfx_vif;
+
+struct wfx_hif_cmd {
+	struct mutex      lock;
+	struct completion ready;
+	struct completion done;
+	bool              async;
+	struct hif_msg    *buf_send;
+	void              *buf_recv;
+	size_t            len_recv;
+	int               ret;
+};
+
+void wfx_init_hif_cmd(struct wfx_hif_cmd *wfx_hif_cmd);
+int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request,
+		 void *reply, size_t reply_len, bool async);
+
+#endif
diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c
index ca0ca873bd7d..8973eeb60eb8 100644
--- a/drivers/staging/wfx/main.c
+++ b/drivers/staging/wfx/main.c
@@ -115,6 +115,7 @@ struct wfx_dev *wfx_init_common(struct device *dev,
 	memcpy(&wdev->pdata, pdata, sizeof(*pdata));
 
 	init_completion(&wdev->firmware_ready);
+	wfx_init_hif_cmd(&wdev->hif_cmd);
 
 	return wdev;
 }
diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h
index a7e571e0da30..bf9de11f8896 100644
--- a/drivers/staging/wfx/wfx.h
+++ b/drivers/staging/wfx/wfx.h
@@ -15,6 +15,7 @@
 
 #include "bh.h"
 #include "main.h"
+#include "hif_tx.h"
 #include "hif_api_general.h"
 
 struct hwbus_ops;
@@ -32,6 +33,9 @@ struct wfx_dev {
 	struct completion	firmware_ready;
 	struct hif_ind_startup	hw_caps;
 	struct wfx_hif		hif;
+	int			chip_frozen;
+
+	struct wfx_hif_cmd	hif_cmd;
 };
 
 struct wfx_vif {
-- 
2.20.1
_______________________________________________
devel mailing list
devel@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

  parent reply	other threads:[~2019-09-19 14:27 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-09-19 14:25 [PATCH v3 00/20] Add support for Silicon Labs WiFi chip WF200 and further Jerome Pouiller
2019-09-19 14:25 ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 01/20] staging: wfx: add infrastructure for new driver Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-10-04  8:23   ` Greg Kroah-Hartman
2019-10-04  8:23     ` Greg Kroah-Hartman
2019-09-19 14:25 ` [PATCH v3 02/20] staging: wfx: add support for I/O access Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 03/20] staging: wfx: add I/O API Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 05/20] staging: wfx: load firmware Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 04/20] staging: wfx: add tracepoints for I/O access Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 06/20] staging: wfx: import HIF API headers Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 07/20] staging: wfx: add IRQ handling Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 08/20] staging: wfx: add tracepoints for HIF Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 09/20] staging: wfx: add support for start-up indication Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 10/20] staging: wfx: instantiate mac80211 data Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` Jerome Pouiller [this message]
2019-09-19 14:25   ` [PATCH v3 11/20] staging: wfx: allow to send commands to chip Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 12/20] staging: wfx: add HIF commands helpers Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 13/20] staging: wfx: introduce "secure link" Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 14/20] staging: wfx: setup initial chip configuration Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 15/20] staging: wfx: add debug files and trace debug events Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 16/20] staging: wfx: allow to send 802.11 frames Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 17/20] staging: wfx: allow to receive " Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 18/20] staging: wfx: allow to scan networks Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 19/20] staging: wfx: implement 802.11 key handling Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:25 ` [PATCH v3 20/20] staging: wfx: implement the rest of mac80211 API Jerome Pouiller
2019-09-19 14:25   ` Jerome Pouiller
2019-09-19 14:41 ` [PATCH v3 00/20] Add support for Silicon Labs WiFi chip WF200 and further Greg Kroah-Hartman
2019-09-19 14:41   ` Greg Kroah-Hartman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190919142527.31797-12-Jerome.Pouiller@silabs.com \
    --to=jerome.pouiller@silabs.com \
    --cc=David.Legoff@silabs.com \
    --cc=davem@davemloft.net \
    --cc=devel@driverdev.osuosl.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=kvalo@codeaurora.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.