On 二, 2017-01-03 at 10:54 +0100, Jiri Kosina wrote:
> On Fri, 23 Dec 2016, Even Xu wrote:
> 
> [ ... snip ... ]
> > 
> > +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;
> When taking the error path here you'd try to unlock 
> ishtp_cl_misc->cl_mutex before actually acquiring it.
> 

Thanks for your comments, Jiri, I update my patch below:
===============================================================

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...@intel.com>
Reviewed-by: Andriy Shevchenko <andriy.shevche...@intel.com>
Reviewed-by: Srinivas Pandruvada <srinivas.pandruv...@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    | 882 +++++++++++++++++++++
 include/uapi/linux/intel-ishtp-clients.h           |  73 ++
 6 files changed, 980 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..4ca3ab8
--- /dev/null
+++ b/drivers/misc/intel-ish-client/intel-ishtp-clients.c
@@ -0,0 +1,882 @@
+/*
+ * 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)
+               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 (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...@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

Reply via email to