Add per client notification request infrastructure
that allows client to enable or disable async
event notification.

Signed-off-by: Tomas Winkler <tomas.wink...@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usys...@intel.com>
---
 drivers/misc/mei/client.c    | 143 +++++++++++++++++++++++++++++++++++++++++++
 drivers/misc/mei/client.h    |   6 ++
 drivers/misc/mei/hbm.c       |   5 +-
 drivers/misc/mei/interrupt.c |   7 +++
 4 files changed, 157 insertions(+), 4 deletions(-)

diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 719a698b333d..72d4e7146411 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -1209,6 +1209,147 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
 }
 
 /**
+ *  mei_cl_notify_fop2req - convert fop to proper request
+ *
+ * @fop: client notification start response command
+ *
+ * Return:  MEI_HBM_NOTIFICATION_START/STOP
+ */
+u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop)
+{
+       if (fop == MEI_FOP_NOTIFY_START)
+               return MEI_HBM_NOTIFICATION_START;
+       else
+               return MEI_HBM_NOTIFICATION_STOP;
+}
+
+/**
+ *  mei_cl_notify_req2fop - convert notification request top file operation 
type
+ *
+ * @req: hbm notification request type
+ *
+ * Return:  MEI_FOP_NOTIFY_START/STOP
+ */
+enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req)
+{
+       if (req == MEI_HBM_NOTIFICATION_START)
+               return MEI_FOP_NOTIFY_START;
+       else
+               return MEI_FOP_NOTIFY_STOP;
+}
+
+/**
+ * mei_cl_irq_notify - send notification request in irq_thread context
+ *
+ * @cl: client
+ * @cb: callback block.
+ * @cmpl_list: complete list.
+ *
+ * Return: 0 on such and error otherwise.
+ */
+int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
+                     struct mei_cl_cb *cmpl_list)
+{
+       struct mei_device *dev = cl->dev;
+       u32 msg_slots;
+       int slots;
+       int ret;
+       bool request;
+
+       msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
+       slots = mei_hbuf_empty_slots(dev);
+
+       if (slots < msg_slots)
+               return -EMSGSIZE;
+
+       request = mei_cl_notify_fop2req(cb->fop_type);
+       ret = mei_hbm_cl_notify_req(dev, cl, request);
+       if (ret) {
+               cl->status = ret;
+               list_move_tail(&cb->list, &cmpl_list->list);
+               return ret;
+       }
+
+       list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
+       return 0;
+}
+
+/**
+ * mei_cl_notify_request - send notification stop/start request
+ *
+ * @cl: host client
+ * @file: associate request with file
+ * @request: 1 for start or 0 for stop
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * Return: 0 on such and error otherwise.
+ */
+int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request)
+{
+       struct mei_device *dev;
+       struct mei_cl_cb *cb;
+       enum mei_cb_file_ops fop_type;
+       int rets;
+
+       if (WARN_ON(!cl || !cl->dev))
+               return -ENODEV;
+
+       dev = cl->dev;
+
+       if (!dev->hbm_f_ev_supported) {
+               cl_dbg(dev, cl, "notifications not supported\n");
+               return -EOPNOTSUPP;
+       }
+
+       rets = pm_runtime_get(dev->dev);
+       if (rets < 0 && rets != -EINPROGRESS) {
+               pm_runtime_put_noidle(dev->dev);
+               cl_err(dev, cl, "rpm: get failed %d\n", rets);
+               return rets;
+       }
+
+       fop_type = mei_cl_notify_req2fop(request);
+       cb = mei_io_cb_init(cl, fop_type, file);
+       if (!cb) {
+               rets = -ENOMEM;
+               goto out;
+       }
+
+       if (mei_hbuf_acquire(dev)) {
+               if (mei_hbm_cl_notify_req(dev, cl, request)) {
+                       rets = -ENODEV;
+                       goto out;
+               }
+               list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
+       } else {
+               list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
+       }
+
+       mutex_unlock(&dev->device_lock);
+       wait_event_timeout(cl->wait, cl->notify_en == request,
+                       mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
+       mutex_lock(&dev->device_lock);
+
+       if (cl->notify_en != request) {
+               mei_io_list_flush(&dev->ctrl_rd_list, cl);
+               mei_io_list_flush(&dev->ctrl_wr_list, cl);
+               if (!cl->status)
+                       cl->status = -EFAULT;
+       }
+
+       rets = cl->status;
+
+out:
+       cl_dbg(dev, cl, "rpm: autosuspend\n");
+       pm_runtime_mark_last_busy(dev->dev);
+       pm_runtime_put_autosuspend(dev->dev);
+
+       mei_io_cb_free(cb);
+       return rets;
+}
+
+/**
  * mei_cl_read_start - the start read client message function.
  *
  * @cl: host client
@@ -1514,6 +1655,8 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb 
*cb)
 
        case MEI_FOP_CONNECT:
        case MEI_FOP_DISCONNECT:
+       case MEI_FOP_NOTIFY_STOP:
+       case MEI_FOP_NOTIFY_START:
                if (waitqueue_active(&cl->wait))
                        wake_up(&cl->wait);
 
diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h
index 8d7f057f1045..506b7d40d427 100644
--- a/drivers/misc/mei/client.h
+++ b/drivers/misc/mei/client.h
@@ -219,6 +219,12 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb 
*cb);
 
 void mei_host_client_init(struct work_struct *work);
 
+u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop);
+enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request);
+int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request);
+int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
+                     struct mei_cl_cb *cmpl_list);
+
 void mei_cl_all_disconnect(struct mei_device *dev);
 void mei_cl_all_wakeup(struct mei_device *dev);
 void mei_cl_all_write_clear(struct mei_device *dev);
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index 95e918c84a6c..12229ff4bc7e 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -446,10 +446,7 @@ static inline enum mei_cb_file_ops 
notify_res_to_fop(struct mei_hbm_cl_cmd *cmd)
        struct hbm_notification_response *rs =
                (struct hbm_notification_response *)cmd;
 
-       if (rs->start == MEI_HBM_NOTIFICATION_START)
-               return MEI_FOP_NOTIFY_START;
-       else
-               return MEI_FOP_NOTIFY_STOP;
+       return mei_cl_notify_req2fop(rs->start);
 }
 
 /**
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index feb2c3c57022..c1439a00d18d 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -403,6 +403,13 @@ int mei_irq_write_handler(struct mei_device *dev, struct 
mei_cl_cb *cmpl_list)
                        if (ret)
                                return ret;
                        break;
+
+               case MEI_FOP_NOTIFY_START:
+               case MEI_FOP_NOTIFY_STOP:
+                       ret = mei_cl_irq_notify(cl, cb, cmpl_list);
+                       if (ret)
+                               return ret;
+                       break;
                default:
                        BUG();
                }
-- 
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to