All of lore.kernel.org
 help / color / mirror / Atom feed
From: Even Xu <even.xu@intel.com>
To: jikos@kernel.org, benjamin.tissoires@redhat.com,
	srinivas.pandruvada@linux.intel.com, arnd@arndb.de,
	gregkh@linuxfoundation.org, andriy.shevchenko@intel.com
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	Even Xu <even.xu@intel.com>
Subject: [PATCH 7/7] misc: intel-ish-client: add intel ishtp clients driver
Date: Fri, 23 Dec 2016 09:22:29 +0800	[thread overview]
Message-ID: <1482456149-4841-7-git-send-email-even.xu@intel.com> (raw)
In-Reply-To: <1482456149-4841-1-git-send-email-even.xu@intel.com>

Intel ISHFW supports many different clients, in
hid/intel-ish-hid/ishtp bus driver, it creates following client devices:
HID client:
	interface of sensor configure and sensor event report.
SMHI client:
	interface of sensor calibration, ISHFW debug, ISHFW performance
	analysis and manufacture support.
Trace client:
	interface of ISHFW debug log output.
Trace configure client:
	interface of ISHFW debug log configuration, such as output port,
	log level, filter.
ISHFW loader client:
	interface of customized ISHFW loader.
HID client has been handle by hid/intel-ish-hid/intel-ishtp-hid client
driver, and rest of the clients export interface using miscellaneous
drivers. This interface is used by user space tools for debugging and
calibration of sensors.

Signed-off-by: Even Xu <even.xu@intel.com>
Reviewed-by: Andriy Shevchenko <andriy.shevchenko@intel.com>
Reviewed-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
 drivers/misc/Kconfig                               |   1 +
 drivers/misc/Makefile                              |   1 +
 drivers/misc/intel-ish-client/Kconfig              |  15 +
 drivers/misc/intel-ish-client/Makefile             |   8 +
 .../misc/intel-ish-client/intel-ishtp-clients.c    | 884 +++++++++++++++++++++
 include/uapi/linux/intel-ishtp-clients.h           |  73 ++
 6 files changed, 982 insertions(+)
 create mode 100644 drivers/misc/intel-ish-client/Kconfig
 create mode 100644 drivers/misc/intel-ish-client/Makefile
 create mode 100644 drivers/misc/intel-ish-client/intel-ishtp-clients.c
 create mode 100644 include/uapi/linux/intel-ishtp-clients.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 64971ba..a89849f 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -778,4 +778,5 @@ source "drivers/misc/mic/Kconfig"
 source "drivers/misc/genwqe/Kconfig"
 source "drivers/misc/echo/Kconfig"
 source "drivers/misc/cxl/Kconfig"
+source "drivers/misc/intel-ish-client/Kconfig"
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 3198336..c54015d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_ECHO)		+= echo/
 obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_PANEL)             += panel.o
+obj-$(CONFIG_INTEL_ISH_CLIENT)	+= intel-ish-client/
 
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_bugs.o
diff --git a/drivers/misc/intel-ish-client/Kconfig b/drivers/misc/intel-ish-client/Kconfig
new file mode 100644
index 0000000..6fa9cc0
--- /dev/null
+++ b/drivers/misc/intel-ish-client/Kconfig
@@ -0,0 +1,15 @@
+menu "Intel ISH Client support"
+	depends on INTEL_ISH_HID
+
+config INTEL_ISH_CLIENT
+	tristate "Intel Integrated Sensor Hub Client"
+	help
+	  The Integrated Sensor Hub (ISH) supports many clients, Intel ISH client
+	  driver supports following clients:
+	  SMHI client: calibration & perfermance & menufacture tool interface
+	  trace configure client: ISHFW trace parameter configure interface
+	  trace tool client: ISHFW trace log output interface
+	  loader client: loading customized ISHFW interface
+
+	  Say Y here if you want to support Intel ISH clients. If unsure, say N.
+endmenu
diff --git a/drivers/misc/intel-ish-client/Makefile b/drivers/misc/intel-ish-client/Makefile
new file mode 100644
index 0000000..29a5461
--- /dev/null
+++ b/drivers/misc/intel-ish-client/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile - Intel ISH client driver
+# Copyright (c) 2014-2016, Intel Corporation.
+#
+#
+obj-$(CONFIG_INTEL_ISH_CLIENT) += intel-ishtp-clients.o
+
+ccflags-y += -I$(srctree)/drivers/hid/intel-ish-hid/ishtp
diff --git a/drivers/misc/intel-ish-client/intel-ishtp-clients.c b/drivers/misc/intel-ish-client/intel-ishtp-clients.c
new file mode 100644
index 0000000..d2b3ffd
--- /dev/null
+++ b/drivers/misc/intel-ish-client/intel-ishtp-clients.c
@@ -0,0 +1,884 @@
+/*
+ * ISHTP clients driver
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/capability.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/intel-ishtp-clients.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+#include <linux/uaccess.h>
+
+#include "ishtp-dev.h"
+#include "client.h"
+
+/*
+ * ISH client misc driver structure
+ */
+struct ishtp_cl_miscdev {
+	struct miscdevice cl_miscdev;
+	struct ishtp_cl_device *cl_device;
+	struct ishtp_cl *cl;
+
+	/* Wait queue for waiting ISHFW event/message */
+	wait_queue_head_t read_wait;
+	/* Read buffer, will point to available ISHFW message */
+	struct ishtp_cl_rb *read_rb;
+
+	/*
+	 * cl member can be freed/changed by ISHFW reset and release() calling,
+	 * so must pay attention to protect cl while try to access it. This
+	 * mutex is used to protect cl member.
+	 */
+	struct mutex cl_mutex;
+
+	struct work_struct reset_work;
+};
+
+/* ISH client GUIDs */
+/* SMHI client UUID: bb579a2e-cc54-4450-b1d0-5e7520dcad25 */
+static const uuid_le ishtp_smhi_guid =
+			UUID_LE(0xbb579a2e, 0xcc54, 0x4450,
+				0xb1, 0xd0, 0x5e, 0x75, 0x20, 0xdc, 0xad, 0x25);
+
+/* Trace log client UUID: c1cc78b9-b693-4e54-9191-5169cb027c25 */
+static const uuid_le ishtp_trace_guid =
+			UUID_LE(0xc1cc78b9, 0xb693, 0x4e54,
+				0x91, 0x91, 0x51, 0x69, 0xcb, 0x02, 0x7c, 0x25);
+
+/* Trace config client UUID: 1f050626-d505-4e94-b189-535d7de19cf2 */
+static const uuid_le ishtp_traceconfig_guid =
+			UUID_LE(0x1f050626, 0xd505, 0x4e94,
+				0xb1, 0x89, 0x53, 0x5d, 0x7d, 0xe1, 0x9c, 0xf2);
+
+/* ISHFW loader client UUID: c804d06a-55bd-4ea7-aded-1e31228c76dc */
+static const uuid_le ishtp_loader_guid =
+			UUID_LE(0xc804d06a, 0x55bd, 0x4ea7,
+				0xad, 0xed, 0x1e, 0x31, 0x22, 0x8c, 0x76, 0xdc);
+
+static int ishtp_cl_open(struct inode *inode, struct file *file)
+{
+	struct miscdevice *misc = file->private_data;
+	struct ishtp_cl *cl;
+	struct ishtp_device *dev;
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+	int ret;
+
+	/* Non-blocking semantics are not supported */
+	if (file->f_flags & O_NONBLOCK)
+		return -EINVAL;
+
+	ishtp_cl_misc = container_of(misc,
+				struct ishtp_cl_miscdev, cl_miscdev);
+	if (!ishtp_cl_misc || !ishtp_cl_misc->cl_device)
+		return -ENODEV;
+
+	dev = ishtp_cl_misc->cl_device->ishtp_dev;
+	if (!dev)
+		return -ENODEV;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	/*
+	 * Every client only supports one opened instance
+	 * at the sametime.
+	 */
+	if (ishtp_cl_misc->cl) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+	cl = ishtp_cl_allocate(dev);
+	if (!cl) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	if (dev->dev_state != ISHTP_DEV_ENABLED) {
+		ret = -ENODEV;
+		goto out_free;
+	}
+
+	ret = ishtp_cl_link(cl, ISHTP_HOST_CLIENT_ID_ANY);
+	if (ret)
+		goto out_free;
+
+	ishtp_cl_misc->cl = cl;
+
+	file->private_data = ishtp_cl_misc;
+
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	return nonseekable_open(inode, file);
+
+out_free:
+	kfree(cl);
+out_unlock:
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+	return ret;
+}
+
+#define WAIT_FOR_SEND_SLICE_MS		100
+#define WAIT_FOR_SEND_COUNT		10
+
+static int ishtp_cl_release(struct inode *inode, struct file *file)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc = file->private_data;
+	struct ishtp_cl *cl;
+	struct ishtp_cl_rb *rb;
+	struct ishtp_device *dev;
+	int try = WAIT_FOR_SEND_COUNT;
+	int ret;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	/* Wake up from waiting if anyone wait on it */
+	wake_up_interruptible(&ishtp_cl_misc->read_wait);
+
+	cl = ishtp_cl_misc->cl;
+	dev = cl->dev;
+
+	/*
+	 * May happen if device sent FW reset or was intentionally
+	 * halted by host SW. The client is then invalid.
+	 */
+	if ((dev->dev_state == ISHTP_DEV_ENABLED) &&
+			(cl->state == ISHTP_CL_CONNECTED)) {
+		/*
+		 * Check and wait 1s for message in tx_list to be sent.
+		 */
+		do {
+			if (!ishtp_cl_tx_empty(cl))
+				msleep_interruptible(WAIT_FOR_SEND_SLICE_MS);
+			else
+				break;
+		} while (--try);
+
+		cl->state = ISHTP_CL_DISCONNECTING;
+		ret = ishtp_cl_disconnect(cl);
+	}
+
+	ishtp_cl_unlink(cl);
+	ishtp_cl_flush_queues(cl);
+	/* Disband and free all Tx and Rx client-level rings */
+	ishtp_cl_free(cl);
+
+	ishtp_cl_misc->cl = NULL;
+
+	rb = ishtp_cl_misc->read_rb;
+	if (rb) {
+		ishtp_cl_io_rb_recycle(rb);
+		ishtp_cl_misc->read_rb = NULL;
+	}
+
+	file->private_data = NULL;
+
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	return ret;
+}
+
+static ssize_t ishtp_cl_read(struct file *file, char __user *ubuf,
+			size_t length, loff_t *offset)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc = file->private_data;
+	struct ishtp_cl *cl;
+	struct ishtp_cl_rb *rb;
+	struct ishtp_device *dev;
+	int ret = 0;
+
+	/* Non-blocking semantics are not supported */
+	if (file->f_flags & O_NONBLOCK)
+		return -EINVAL;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	cl = ishtp_cl_misc->cl;
+
+	/*
+	 * ISHFW reset will cause cl be freed and re-allocated.
+	 * So must make sure cl is re-allocated successfully.
+	 */
+	if (!cl || !cl->dev) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	dev = cl->dev;
+	if (dev->dev_state != ISHTP_DEV_ENABLED) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	if (ishtp_cl_misc->read_rb)
+		goto get_rb;
+
+	rb = ishtp_cl_rx_get_rb(cl);
+	if (rb)
+		goto copy_buffer;
+
+	/*
+	 * Release mutex for other operation can be processed parallelly
+	 * during waiting.
+	 */
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	if (wait_event_interruptible(ishtp_cl_misc->read_wait,
+			ishtp_cl_misc->read_rb != NULL)) {
+		dev_err(&ishtp_cl_misc->cl_device->dev,
+			"Wake up not successful;"
+			"signal pending = %d signal = %08lX\n",
+			signal_pending(current),
+			current->pending.signal.sig[0]);
+		return -ERESTARTSYS;
+	}
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	/*
+	 * waitqueue can be woken up in many cases, so must check
+	 * if dev and cl is still available.
+	 */
+	if (dev->dev_state != ISHTP_DEV_ENABLED) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	cl = ishtp_cl_misc->cl;
+	if (!cl) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	if (cl->state == ISHTP_CL_INITIALIZING ||
+		cl->state == ISHTP_CL_DISCONNECTED ||
+		cl->state == ISHTP_CL_DISCONNECTING) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+get_rb:
+	rb = ishtp_cl_misc->read_rb;
+	if (!rb) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+copy_buffer:
+	/* Now copy the data to user space */
+	if (!length || !ubuf || *offset > rb->buf_idx) {
+		ret = -EMSGSIZE;
+		goto out_unlock;
+	}
+
+	/*
+	 * length is being truncated, however buf_idx may
+	 * point beyond that.
+	 */
+	length = min_t(size_t, length, rb->buf_idx - *offset);
+
+	if (copy_to_user(ubuf, rb->buffer.data + *offset, length)) {
+		ret = -EFAULT;
+		goto out_unlock;
+	}
+
+	*offset += length;
+	if ((unsigned long)*offset < rb->buf_idx)
+		goto out_unlock;
+
+	ishtp_cl_io_rb_recycle(rb);
+	ishtp_cl_misc->read_rb = NULL;
+	*offset = 0;
+
+out_unlock:
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+	return ret < 0 ? ret : length;
+}
+
+static ssize_t ishtp_cl_write(struct file *file, const char __user *ubuf,
+	size_t length, loff_t *offset)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc = file->private_data;
+	struct ishtp_cl *cl;
+	void *write_buf;
+	struct ishtp_device *dev;
+	int ret;
+
+	/* Non-blocking semantics are not supported */
+	if (file->f_flags & O_NONBLOCK) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	cl = ishtp_cl_misc->cl;
+
+	/*
+	 * ISHFW reset will cause cl be freed and re-allocated.
+	 * So must make sure cl is re-allocated successfully.
+	 */
+	if (!cl || !cl->dev) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	dev = cl->dev;
+
+	if (dev->dev_state != ISHTP_DEV_ENABLED) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	if (cl->state != ISHTP_CL_CONNECTED) {
+		dev_err(&ishtp_cl_misc->cl_device->dev,
+			"host client = %d isn't connected to fw client = %d\n",
+			cl->host_client_id, cl->fw_client_id);
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	if (length <= 0 || length > cl->device->fw_client->props.max_msg_length) {
+		ret = -EMSGSIZE;
+		goto out_unlock;
+	}
+
+	write_buf = memdup_user(ubuf, length);
+	if (IS_ERR(write_buf)) {
+		ret = PTR_ERR(write_buf);
+		goto out_unlock;
+	}
+
+	ret = ishtp_cl_send(cl, write_buf, length);
+
+	kfree(write_buf);
+
+out_unlock:
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	return ret < 0 ? ret : length;
+}
+
+static int ishtp_cl_ioctl_connect_client(struct file *file,
+	struct ishtp_connect_client_data *data)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc = file->private_data;
+	struct ishtp_device *dev;
+	struct ishtp_client *client;
+	struct ishtp_cl_device *cl_device;
+	struct ishtp_cl *cl = ishtp_cl_misc->cl;
+	struct ishtp_fw_client *fw_client;
+
+	if (!cl || !cl->dev)
+		return -ENODEV;
+
+	dev = cl->dev;
+
+	if (dev->dev_state != ISHTP_DEV_ENABLED)
+		return -ENODEV;
+
+	if (cl->state != ISHTP_CL_INITIALIZING &&
+			cl->state != ISHTP_CL_DISCONNECTED)
+		return -EBUSY;
+
+	cl_device = ishtp_cl_misc->cl_device;
+
+	if (uuid_le_cmp(data->in_client_uuid,
+			cl_device->fw_client->props.protocol_name) != 0) {
+		dev_err(&ishtp_cl_misc->cl_device->dev,
+			"Required uuid don't match current client uuid\n");
+		return -EFAULT;
+	}
+
+	/* Find the fw client we're trying to connect to */
+	fw_client = ishtp_fw_cl_get_client(dev, &data->in_client_uuid);
+	if (!fw_client) {
+		dev_err(&ishtp_cl_misc->cl_device->dev,
+			"Don't find current client uuid\n");
+		return -ENOENT;
+	}
+
+	cl->fw_client_id = fw_client->client_id;
+	cl->state = ISHTP_CL_CONNECTING;
+
+	/* Prepare the output buffer */
+	client = &data->out_client_properties;
+	client->max_msg_length = fw_client->props.max_msg_length;
+	client->protocol_version = fw_client->props.protocol_version;
+
+	return ishtp_cl_connect(cl);
+}
+
+static long ishtp_cl_ioctl(struct file *file, unsigned int cmd,
+			unsigned long data)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc = file->private_data;
+	struct ishtp_cl *cl;
+	struct ishtp_device *dev;
+	struct ishtp_connect_client_data *connect_data;
+	char fw_stat_buf[20];
+	unsigned int ring_size;
+	int ret = 0;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	cl = ishtp_cl_misc->cl;
+
+	/*
+	 * ISHFW reset will cause cl be freed and re-allocated.
+	 * So must make sure cl is re-allocated successfully.
+	 */
+	if (!cl || !cl->dev) {
+		mutex_unlock(&ishtp_cl_misc->cl_mutex);
+		return -ENODEV;
+	}
+
+	dev = cl->dev;
+
+	switch (cmd) {
+	case IOCTL_ISH_HW_RESET:
+		ret = ish_hw_reset(dev);
+		break;
+
+	case IOCTL_ISHTP_SET_RX_FIFO_SIZE:
+		ring_size = data;
+
+		if (ring_size > CL_MAX_RX_RING_SIZE) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if (cl->state != ISHTP_CL_INITIALIZING) {
+			ret = -EBUSY;
+			break;
+		}
+
+		cl->rx_ring_size = ring_size;
+		break;
+
+	case IOCTL_ISHTP_SET_TX_FIFO_SIZE:
+		ring_size = data;
+
+		if (ring_size > CL_MAX_TX_RING_SIZE) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if (cl->state != ISHTP_CL_INITIALIZING) {
+			ret = -EBUSY;
+			break;
+		}
+
+		cl->tx_ring_size = ring_size;
+		break;
+
+	case IOCTL_ISH_GET_FW_STATUS:
+		if (!data) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		snprintf(fw_stat_buf, sizeof(fw_stat_buf),
+			"%08X\n", dev->ops->get_fw_status(dev));
+
+		if (copy_to_user((char __user *)data, fw_stat_buf,
+				strlen(fw_stat_buf))) {
+			ret = -EFAULT;
+			break;
+		}
+
+		ret = strlen(fw_stat_buf);
+		break;
+
+	case IOCTL_ISHTP_CONNECT_CLIENT:
+		if (dev->dev_state != ISHTP_DEV_ENABLED) {
+			ret = -ENODEV;
+			break;
+		}
+
+		connect_data = memdup_user((char __user *)data,
+					sizeof(struct ishtp_connect_client_data));
+		if (IS_ERR(connect_data)) {
+			ret = PTR_ERR(connect_data);
+			break;
+		}
+
+		ret = ishtp_cl_ioctl_connect_client(file, connect_data);
+		if (ret) {
+			kfree(connect_data);
+			break;
+		}
+
+		/* If all is ok, copying the data back to user. */
+		if (copy_to_user((char __user *)data, connect_data,
+				sizeof(struct ishtp_connect_client_data)))
+			ret = -EFAULT;
+
+		kfree(connect_data);
+
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	return ret;
+}
+
+/*
+ * File operations structure will be used for ishtp client misc device.
+ */
+static const struct file_operations ishtp_cl_fops = {
+	.owner = THIS_MODULE,
+	.read = ishtp_cl_read,
+	.unlocked_ioctl = ishtp_cl_ioctl,
+	.open = ishtp_cl_open,
+	.release = ishtp_cl_release,
+	.write = ishtp_cl_write,
+	.llseek = no_llseek
+};
+
+/**
+ * ishtp_cl_event_cb() - ISHTP client driver event callback
+ * @cl_device:		ISHTP client device instance
+ *
+ * This function gets called on related event recevied from ISHFW.
+ * It will remove event buffer exists on in_process list to related
+ * client device and wait up client driver to process.
+ */
+static void ishtp_cl_event_cb(struct ishtp_cl_device *cl_device)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+	struct ishtp_cl *cl;
+	struct ishtp_cl_rb *rb;
+
+	ishtp_cl_misc = ishtp_get_drvdata(cl_device);
+	if (!ishtp_cl_misc)
+		return;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	/*
+	 * If this waitqueue is active, cl_mutex is locked by read(), it's safe
+	 * to access ishtp_cl_misc and cl.
+	 */
+	if (waitqueue_active(&ishtp_cl_misc->read_wait)) {
+
+		/*
+		 * If already has read_rb, wake up waitqueue directly.
+		 */
+		if (ishtp_cl_misc->read_rb) {
+			mutex_unlock(&ishtp_cl_misc->cl_mutex);
+			wake_up_interruptible(&ishtp_cl_misc->read_wait);
+			return;
+		}
+
+		cl = ishtp_cl_misc->cl;
+
+		rb = ishtp_cl_rx_get_rb(cl);
+		if (rb)
+			ishtp_cl_misc->read_rb = rb;
+
+		wake_up_interruptible(&ishtp_cl_misc->read_wait);
+	}
+
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+}
+
+/**
+ * ishtp_cl_reset_handler() - ISHTP client driver reset work handler
+ * @work:		work struct
+ *
+ * This function gets called on reset workqueue scheduled when ISHFW
+ * reset happen. It will disconnect and remove current ishtp_cl, then
+ * create a new ishtp_cl and re-connect again.
+ */
+static void ishtp_cl_reset_handler(struct work_struct *work)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+	struct ishtp_device *dev;
+	struct ishtp_cl_device *cl_device;
+	struct ishtp_cl *cl;
+	struct ishtp_fw_client *fw_client;
+	int ret = 0;
+
+	ishtp_cl_misc = container_of(work,
+			struct ishtp_cl_miscdev, reset_work);
+
+	dev = ishtp_cl_misc->cl_device->ishtp_dev;
+	if (!dev) {
+		dev_err(&ishtp_cl_misc->cl_device->dev,
+			"This cl_device not link to ishtp_dev\n");
+		return;
+	}
+
+	cl_device = ishtp_cl_misc->cl_device;
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	/* Wake up from waiting if anyone wait on it */
+	wake_up_interruptible(&ishtp_cl_misc->read_wait);
+
+	cl = ishtp_cl_misc->cl;
+	if (cl) {
+		ishtp_cl_flush_queues(cl);
+		ishtp_cl_free(cl);
+
+		cl = NULL;
+
+		cl = ishtp_cl_allocate(dev);
+		if (!cl) {
+			dev_err(&ishtp_cl_misc->cl_device->dev,
+				"Allocate ishtp_cl failed\n");
+			ret = -ENOMEM;
+			goto out_unlock;
+		}
+
+		if (dev->dev_state != ISHTP_DEV_ENABLED) {
+			dev_err(&ishtp_cl_misc->cl_device->dev,
+				"Ishtp dev isn't enabled\n");
+			ret = -ENODEV;
+			goto out_free;
+		}
+
+		ret = ishtp_cl_link(cl, ISHTP_HOST_CLIENT_ID_ANY);
+		if (ret) {
+			dev_err(&ishtp_cl_misc->cl_device->dev,
+				"Can not link to ishtp\n");
+			goto out_free;
+		}
+
+		fw_client = ishtp_fw_cl_get_client(dev,
+				&cl_device->fw_client->props.protocol_name);
+		if (!fw_client) {
+			dev_err(&ishtp_cl_misc->cl_device->dev,
+				"Don't find related fw client\n");
+			ret = -ENOENT;
+			goto out_free;
+		}
+
+		cl->fw_client_id = fw_client->client_id;
+		cl->state = ISHTP_CL_CONNECTING;
+
+		ret = ishtp_cl_connect(cl);
+		if (ret) {
+			dev_err(&ishtp_cl_misc->cl_device->dev,
+				"Connect to fw failed\n");
+			goto out_free;
+		}
+
+		ishtp_cl_misc->cl = cl;
+	}
+
+	/* After reset, must register event callback again */
+	ishtp_register_event_cb(cl_device, ishtp_cl_event_cb);
+
+out_free:
+	if (ret) {
+		ishtp_cl_free(cl);
+		ishtp_cl_misc->cl = NULL;
+
+		dev_err(&ishtp_cl_misc->cl_device->dev, "Reset failed\n");
+	}
+
+out_unlock:
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+}
+
+/**
+ * ishtp_cl_probe() - ISHTP client driver probe
+ * @cl_device:		ISHTP client device instance
+ *
+ * This function gets called on device create on ISHTP bus
+ *
+ * Return: 0 on success, non zero on error
+ */
+static int ishtp_cl_probe(struct ishtp_cl_device *cl_device)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+	int ret;
+
+	if (!cl_device)
+		return -ENODEV;
+
+	ishtp_cl_misc = kzalloc(sizeof(struct ishtp_cl_miscdev),
+				GFP_KERNEL);
+	if (!ishtp_cl_misc)
+		return -ENOMEM;
+
+	if (uuid_le_cmp(ishtp_smhi_guid,
+			cl_device->fw_client->props.protocol_name) == 0) {
+		ishtp_cl_misc->cl_miscdev.name = "ish-smhi";
+	} else if (uuid_le_cmp(ishtp_trace_guid,
+			cl_device->fw_client->props.protocol_name) == 0) {
+		ishtp_cl_misc->cl_miscdev.name = "ish-trace";
+	} else if (uuid_le_cmp(ishtp_traceconfig_guid,
+			cl_device->fw_client->props.protocol_name) == 0) {
+		ishtp_cl_misc->cl_miscdev.name = "ish-tracec";
+	} else if (uuid_le_cmp(ishtp_loader_guid,
+			cl_device->fw_client->props.protocol_name) == 0) {
+		ishtp_cl_misc->cl_miscdev.name = "ish-loader";
+	} else {
+		dev_err(&cl_device->dev, "Not supported client\n");
+		ret = -ENODEV;
+		goto release_mem;
+	}
+
+	ishtp_cl_misc->cl_miscdev.parent = &cl_device->dev;
+	ishtp_cl_misc->cl_miscdev.fops = &ishtp_cl_fops;
+	ishtp_cl_misc->cl_miscdev.minor = MISC_DYNAMIC_MINOR,
+
+	ret = misc_register(&ishtp_cl_misc->cl_miscdev);
+	if (ret) {
+		dev_err(&cl_device->dev, "misc device register failed\n");
+		goto release_mem;
+	}
+
+	ishtp_cl_misc->cl_device = cl_device;
+
+	init_waitqueue_head(&ishtp_cl_misc->read_wait);
+
+	ishtp_set_drvdata(cl_device, ishtp_cl_misc);
+
+	ishtp_get_device(cl_device);
+
+	mutex_init(&ishtp_cl_misc->cl_mutex);
+
+	INIT_WORK(&ishtp_cl_misc->reset_work, ishtp_cl_reset_handler);
+
+	/* Register event callback */
+	ishtp_register_event_cb(cl_device, ishtp_cl_event_cb);
+
+	return 0;
+
+release_mem:
+	kfree(ishtp_cl_misc);
+
+	return ret;
+}
+
+/**
+ * ishtp_cl_remove() - ISHTP client driver remove
+ * @cl_device:		ISHTP client device instance
+ *
+ * This function gets called on device remove on ISHTP bus
+ *
+ * Return: 0
+ */
+static int ishtp_cl_remove(struct ishtp_cl_device *cl_device)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+	struct ishtp_cl *cl;
+
+	ishtp_cl_misc = ishtp_get_drvdata(cl_device);
+	if (!ishtp_cl_misc)
+		return -ENODEV;
+
+	if (!ishtp_cl_misc->cl_miscdev.parent)
+		return -ENODEV;
+
+	/* Wake up from waiting if anyone wait on it */
+	wake_up_interruptible(&ishtp_cl_misc->read_wait);
+
+	mutex_lock(&ishtp_cl_misc->cl_mutex);
+
+	cl = ishtp_cl_misc->cl;
+	if (cl) {
+		cl->state = ISHTP_CL_DISCONNECTING;
+		ishtp_cl_disconnect(cl);
+		ishtp_cl_unlink(cl);
+		ishtp_cl_flush_queues(cl);
+		ishtp_cl_free(cl);
+		ishtp_cl_misc->cl = NULL;
+	}
+
+	mutex_unlock(&ishtp_cl_misc->cl_mutex);
+
+	mutex_destroy(&ishtp_cl_misc->cl_mutex);
+
+	misc_deregister(&ishtp_cl_misc->cl_miscdev);
+
+	ishtp_put_device(cl_device);
+
+	kfree(ishtp_cl_misc);
+
+	return 0;
+}
+
+/**
+ * ishtp_cl_reset() - ISHTP client driver reset
+ * @cl_device:		ISHTP client device instance
+ *
+ * This function gets called on device reset on ISHTP bus.
+ * If client is connected, needs to disconnect client and
+ * reconnect again.
+ *
+ * Return: 0
+ */
+static int ishtp_cl_reset(struct ishtp_cl_device *cl_device)
+{
+	struct ishtp_cl_miscdev *ishtp_cl_misc;
+
+	ishtp_cl_misc = ishtp_get_drvdata(cl_device);
+	if (!ishtp_cl_misc) {
+		dev_err(&cl_device->dev, "Client driver not ready yet\n");
+		return -ENODEV;
+	}
+
+	schedule_work(&ishtp_cl_misc->reset_work);
+
+	return 0;
+}
+
+static struct ishtp_cl_driver ishtp_cl_driver = {
+	.name = "ishtp-client",
+	.probe = ishtp_cl_probe,
+	.remove = ishtp_cl_remove,
+	.reset = ishtp_cl_reset,
+};
+
+static int __init ishtp_client_init(void)
+{
+	/* Register ISHTP client device driver with ISHTP Bus */
+	return ishtp_cl_driver_register(&ishtp_cl_driver);
+}
+
+static void __exit ishtp_client_exit(void)
+{
+	ishtp_cl_driver_unregister(&ishtp_cl_driver);
+}
+
+/* To make sure ISHTP bus driver loaded first */
+late_initcall(ishtp_client_init);
+module_exit(ishtp_client_exit);
+
+MODULE_DESCRIPTION("ISH ISHTP client driver");
+MODULE_AUTHOR("Even Xu <even.xu@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ishtp:*");
diff --git a/include/uapi/linux/intel-ishtp-clients.h b/include/uapi/linux/intel-ishtp-clients.h
new file mode 100644
index 0000000..792500a
--- /dev/null
+++ b/include/uapi/linux/intel-ishtp-clients.h
@@ -0,0 +1,73 @@
+/*
+ * Intel ISHTP Clients Interface Header
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _INTEL_ISHTP_CLIENTS_H
+#define _INTEL_ISHTP_CLIENTS_H
+
+#include <linux/ioctl.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+/*
+ * This IOCTL is used to associate the current file descriptor with a
+ * FW Client (given by UUID). This opens a communication channel
+ * between a host client and a FW client. From this point every read and write
+ * will communicate with the associated FW client.
+ * Only in close() (file_operation release()) the communication between
+ * the clients is disconnected
+ *
+ * The IOCTL argument is a struct with a union that contains
+ * the input parameter and the output parameter for this IOCTL.
+ *
+ * The input parameter is UUID of the FW Client.
+ * The output parameter is the properties of the FW client
+ * (FW protocol version and max message size).
+ *
+ */
+#define IOCTL_ISHTP_CONNECT_CLIENT	_IOWR('H', 0x81,	\
+				struct ishtp_connect_client_data)
+
+/* Configuration: set number of Rx/Tx buffers. Must be used before connection */
+#define IOCTL_ISHTP_SET_RX_FIFO_SIZE	_IOWR('H', 0x82, long)
+#define IOCTL_ISHTP_SET_TX_FIFO_SIZE	_IOWR('H', 0x83, long)
+
+/* Get FW status */
+#define IOCTL_ISH_GET_FW_STATUS	_IO('H', 0x84)
+
+#define IOCTL_ISH_HW_RESET	_IO('H', 0x85)
+
+/*
+ * Intel ISHTP client information struct
+ */
+struct ishtp_client {
+	__u32 max_msg_length;
+	__u8 protocol_version;
+	__u8 reserved[3];
+};
+
+/*
+ * IOCTL Connect client data structure
+ */
+struct ishtp_connect_client_data {
+	union {
+		uuid_le			in_client_uuid;
+		struct ishtp_client 	out_client_properties;
+	};
+};
+
+#endif /* _INTEL_ISHTP_CLIENTS_H */
-- 
2.7.4

  parent reply	other threads:[~2016-12-23  1:23 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-12-23  1:22 [PATCH 1/7] hid: intel-ish-hid: ishtp: add helper function for driver data get/set Even Xu
2016-12-23  1:22 ` [PATCH 2/7] hid: intel-ish-hid: use helper function for private driver data set/get Even Xu
2016-12-23  1:22 ` [PATCH 3/7] hid: intel-ish-hid: ishtp: add helper functions for client buffer operation Even Xu
2016-12-23  1:22 ` [PATCH 4/7] hid: intel-ish-hid: use helper function to access client buffer Even Xu
2016-12-23  1:22 ` [PATCH 5/7] hid: intel-ish-hid: ishtp: add helper function for client search Even Xu
2016-12-23  1:22 ` [PATCH 6/7] hid: intel-ish-hid: use helper function to search client id Even Xu
2016-12-23  1:22 ` Even Xu [this message]
2017-01-03  9:54   ` [PATCH 7/7] misc: intel-ish-client: add intel ishtp clients driver Jiri Kosina
2017-01-04  6:55     ` Xu, Even
2017-01-04  6:55       ` Xu, Even
2017-01-04  9:36       ` Jiri Kosina
2017-01-04  9:36         ` Jiri Kosina
2017-01-04 12:59         ` gregkh
2017-01-04 12:59           ` gregkh
2017-01-04 13:03   ` Greg KH
2017-01-04 17:11     ` Srinivas Pandruvada
2017-01-04 17:18       ` Greg KH
2017-01-04 18:41         ` Srinivas Pandruvada
2017-01-04 19:40           ` Greg KH
2017-01-05  5:38             ` Xu, Even
2017-01-04 13:09   ` Greg KH
2017-01-04 13:13   ` Greg KH

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=1482456149-4841-7-git-send-email-even.xu@intel.com \
    --to=even.xu@intel.com \
    --cc=andriy.shevchenko@intel.com \
    --cc=arnd@arndb.de \
    --cc=benjamin.tissoires@redhat.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=jikos@kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=srinivas.pandruvada@linux.intel.com \
    /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.