lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 23 Dec 2016 09:22:29 +0800
From:   Even Xu <even.xu@...el.com>
To:     jikos@...nel.org, benjamin.tissoires@...hat.com,
        srinivas.pandruvada@...ux.intel.com, arnd@...db.de,
        gregkh@...uxfoundation.org, andriy.shevchenko@...el.com
Cc:     linux-input@...r.kernel.org, linux-kernel@...r.kernel.org,
        Even Xu <even.xu@...el.com>
Subject: [PATCH 7/7] misc: intel-ish-client: add intel ishtp clients driver

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@...el.com>
Reviewed-by: Andriy Shevchenko <andriy.shevchenko@...el.com>
Reviewed-by: Srinivas Pandruvada <srinivas.pandruvada@...ux.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@...el.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

Powered by blists - more mailing lists