Merge tag 'char-misc-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[linux-2.6-block.git] / drivers / misc / mei / amthif.c
index 40ea639fa413a92f0239e1835894e73d36ddc1c1..d2cd53e3fac378b27e0694e04ea8cee0438d784d 100644 (file)
@@ -48,10 +48,7 @@ void mei_amthif_reset_params(struct mei_device *dev)
 {
        /* reset iamthif parameters. */
        dev->iamthif_current_cb = NULL;
-       dev->iamthif_msg_buf_size = 0;
-       dev->iamthif_msg_buf_index = 0;
        dev->iamthif_canceled = false;
-       dev->iamthif_ioctl = false;
        dev->iamthif_state = MEI_IAMTHIF_IDLE;
        dev->iamthif_timer = 0;
        dev->iamthif_stall_timer = 0;
@@ -69,7 +66,6 @@ int mei_amthif_host_init(struct mei_device *dev)
 {
        struct mei_cl *cl = &dev->iamthif_cl;
        struct mei_me_client *me_cl;
-       unsigned char *msg_buf;
        int ret;
 
        dev->iamthif_state = MEI_IAMTHIF_IDLE;
@@ -90,18 +86,6 @@ int mei_amthif_host_init(struct mei_device *dev)
        dev->iamthif_mtu = me_cl->props.max_msg_length;
        dev_dbg(dev->dev, "IAMTHIF_MTU = %d\n", dev->iamthif_mtu);
 
-       kfree(dev->iamthif_msg_buf);
-       dev->iamthif_msg_buf = NULL;
-
-       /* allocate storage for ME message buffer */
-       msg_buf = kcalloc(dev->iamthif_mtu,
-                       sizeof(unsigned char), GFP_KERNEL);
-       if (!msg_buf) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       dev->iamthif_msg_buf = msg_buf;
 
        ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);
        if (ret < 0) {
@@ -194,30 +178,33 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
                dev_dbg(dev->dev, "woke up from sleep\n");
        }
 
+       if (cb->status) {
+               rets = cb->status;
+               dev_dbg(dev->dev, "read operation failed %d\n", rets);
+               goto free;
+       }
 
        dev_dbg(dev->dev, "Got amthif data\n");
        dev->iamthif_timer = 0;
 
-       if (cb) {
-               timeout = cb->read_time +
-                       mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
-               dev_dbg(dev->dev, "amthif timeout = %lud\n",
-                               timeout);
-
-               if  (time_after(jiffies, timeout)) {
-                       dev_dbg(dev->dev, "amthif Time out\n");
-                       /* 15 sec for the message has expired */
-                       list_del(&cb->list);
-                       rets = -ETIME;
-                       goto free;
-               }
+       timeout = cb->read_time +
+               mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
+       dev_dbg(dev->dev, "amthif timeout = %lud\n",
+                       timeout);
+
+       if  (time_after(jiffies, timeout)) {
+               dev_dbg(dev->dev, "amthif Time out\n");
+               /* 15 sec for the message has expired */
+               list_del_init(&cb->list);
+               rets = -ETIME;
+               goto free;
        }
        /* if the whole message will fit remove it from the list */
        if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset))
-               list_del(&cb->list);
+               list_del_init(&cb->list);
        else if (cb->buf_idx > 0 && cb->buf_idx <= *offset) {
                /* end of the message has been reached */
-               list_del(&cb->list);
+               list_del_init(&cb->list);
                rets = 0;
                goto free;
        }
@@ -225,15 +212,15 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
                 * remove message from deletion list
                 */
 
-       dev_dbg(dev->dev, "amthif cb->response_buffer size - %d\n",
-           cb->response_buffer.size);
+       dev_dbg(dev->dev, "amthif cb->buf size - %d\n",
+           cb->buf.size);
        dev_dbg(dev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx);
 
        /* length is being truncated to PAGE_SIZE, however,
         * the buf_idx may point beyond */
        length = min_t(size_t, length, (cb->buf_idx - *offset));
 
-       if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) {
+       if (copy_to_user(ubuf, cb->buf.data + *offset, length)) {
                dev_dbg(dev->dev, "failed to copy data to userland\n");
                rets = -EFAULT;
        } else {
@@ -252,126 +239,88 @@ out:
 }
 
 /**
- * mei_amthif_send_cmd - send amthif command to the ME
+ * mei_amthif_read_start - queue message for sending read credential
  *
- * @dev: the device structure
- * @cb: mei call back struct
+ * @cl: host client
+ * @file: file pointer of message recipient
  *
  * Return: 0 on success, <0 on failure.
- *
  */
-static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
+static int mei_amthif_read_start(struct mei_cl *cl, struct file *file)
 {
-       struct mei_msg_hdr mei_hdr;
-       struct mei_cl *cl;
-       int ret;
-
-       if (!dev || !cb)
-               return -ENODEV;
+       struct mei_device *dev = cl->dev;
+       struct mei_cl_cb *cb;
+       size_t length = dev->iamthif_mtu;
+       int rets;
 
-       dev_dbg(dev->dev, "write data to amthif client.\n");
+       cb = mei_io_cb_init(cl, MEI_FOP_READ, file);
+       if (!cb) {
+               rets = -ENOMEM;
+               goto err;
+       }
 
-       dev->iamthif_state = MEI_IAMTHIF_WRITING;
-       dev->iamthif_current_cb = cb;
-       dev->iamthif_file_object = cb->file_object;
-       dev->iamthif_canceled = false;
-       dev->iamthif_ioctl = true;
-       dev->iamthif_msg_buf_size = cb->request_buffer.size;
-       memcpy(dev->iamthif_msg_buf, cb->request_buffer.data,
-              cb->request_buffer.size);
-       cl = &dev->iamthif_cl;
+       rets = mei_io_cb_alloc_buf(cb, length);
+       if (rets)
+               goto err;
 
-       ret = mei_cl_flow_ctrl_creds(cl);
-       if (ret < 0)
-               return ret;
+       list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
 
-       if (ret && mei_hbuf_acquire(dev)) {
-               ret = 0;
-               if (cb->request_buffer.size > mei_hbuf_max_len(dev)) {
-                       mei_hdr.length = mei_hbuf_max_len(dev);
-                       mei_hdr.msg_complete = 0;
-               } else {
-                       mei_hdr.length = cb->request_buffer.size;
-                       mei_hdr.msg_complete = 1;
-               }
+       dev->iamthif_state = MEI_IAMTHIF_READING;
+       dev->iamthif_file_object = cb->file_object;
+       dev->iamthif_current_cb = cb;
 
-               mei_hdr.host_addr = cl->host_client_id;
-               mei_hdr.me_addr = cl->me_client_id;
-               mei_hdr.reserved = 0;
-               mei_hdr.internal = 0;
-               dev->iamthif_msg_buf_index += mei_hdr.length;
-               ret = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf);
-               if (ret)
-                       return ret;
-
-               if (mei_hdr.msg_complete) {
-                       if (mei_cl_flow_ctrl_reduce(cl))
-                               return -EIO;
-                       dev->iamthif_flow_control_pending = true;
-                       dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
-                       dev_dbg(dev->dev, "add amthif cb to write waiting list\n");
-                       dev->iamthif_current_cb = cb;
-                       dev->iamthif_file_object = cb->file_object;
-                       list_add_tail(&cb->list, &dev->write_waiting_list.list);
-               } else {
-                       dev_dbg(dev->dev, "message does not complete, so add amthif cb to write list.\n");
-                       list_add_tail(&cb->list, &dev->write_list.list);
-               }
-       } else {
-               list_add_tail(&cb->list, &dev->write_list.list);
-       }
        return 0;
+err:
+       mei_io_cb_free(cb);
+       return rets;
 }
 
 /**
- * mei_amthif_write - write amthif data to amthif client
+ * mei_amthif_send_cmd - send amthif command to the ME
  *
- * @dev: the device structure
+ * @cl: the host client
  * @cb: mei call back struct
  *
  * Return: 0 on success, <0 on failure.
- *
  */
-int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb)
+static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb)
 {
+       struct mei_device *dev;
        int ret;
 
-       if (!dev || !cb)
+       if (!cl->dev || !cb)
                return -ENODEV;
 
-       ret = mei_io_cb_alloc_resp_buf(cb, dev->iamthif_mtu);
-       if (ret)
+       dev = cl->dev;
+
+       dev->iamthif_state = MEI_IAMTHIF_WRITING;
+       dev->iamthif_current_cb = cb;
+       dev->iamthif_file_object = cb->file_object;
+       dev->iamthif_canceled = false;
+
+       ret = mei_cl_write(cl, cb, false);
+       if (ret < 0)
                return ret;
 
-       cb->fop_type = MEI_FOP_WRITE;
+       if (cb->completed)
+               cb->status = mei_amthif_read_start(cl, cb->file_object);
 
-       if (!list_empty(&dev->amthif_cmd_list.list) ||
-           dev->iamthif_state != MEI_IAMTHIF_IDLE) {
-               dev_dbg(dev->dev,
-                       "amthif state = %d\n", dev->iamthif_state);
-               dev_dbg(dev->dev, "AMTHIF: add cb to the wait list\n");
-               list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
-               return 0;
-       }
-       return mei_amthif_send_cmd(dev, cb);
+       return 0;
 }
+
 /**
  * mei_amthif_run_next_cmd - send next amt command from queue
  *
  * @dev: the device structure
+ *
+ * Return: 0 on success, <0 on failure.
  */
-void mei_amthif_run_next_cmd(struct mei_device *dev)
+int mei_amthif_run_next_cmd(struct mei_device *dev)
 {
+       struct mei_cl *cl = &dev->iamthif_cl;
        struct mei_cl_cb *cb;
-       int ret;
-
-       if (!dev)
-               return;
 
-       dev->iamthif_msg_buf_size = 0;
-       dev->iamthif_msg_buf_index = 0;
        dev->iamthif_canceled = false;
-       dev->iamthif_ioctl = true;
        dev->iamthif_state = MEI_IAMTHIF_IDLE;
        dev->iamthif_timer = 0;
        dev->iamthif_file_object = NULL;
@@ -381,13 +330,48 @@ void mei_amthif_run_next_cmd(struct mei_device *dev)
        cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
                                        typeof(*cb), list);
        if (!cb)
-               return;
-       list_del(&cb->list);
-       ret =  mei_amthif_send_cmd(dev, cb);
-       if (ret)
-               dev_warn(dev->dev, "amthif write failed status = %d\n", ret);
+               return 0;
+
+       list_del_init(&cb->list);
+       return mei_amthif_send_cmd(cl, cb);
 }
 
+/**
+ * mei_amthif_write - write amthif data to amthif client
+ *
+ * @cl: host client
+ * @cb: mei call back struct
+ *
+ * Return: 0 on success, <0 on failure.
+ */
+int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
+{
+
+       struct mei_device *dev;
+
+       if (WARN_ON(!cl || !cl->dev))
+               return -ENODEV;
+
+       if (WARN_ON(!cb))
+               return -EINVAL;
+
+       dev = cl->dev;
+
+       list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
+       return mei_amthif_run_next_cmd(dev);
+}
+
+/**
+ * mei_amthif_poll - the amthif poll function
+ *
+ * @dev: the device structure
+ * @file: pointer to file structure
+ * @wait: pointer to poll_table structure
+ *
+ * Return: poll mask
+ *
+ * Locking: called under "dev->device_lock" lock
+ */
 
 unsigned int mei_amthif_poll(struct mei_device *dev,
                struct file *file, poll_table *wait)
@@ -396,19 +380,12 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
 
        poll_wait(file, &dev->iamthif_cl.wait, wait);
 
-       mutex_lock(&dev->device_lock);
-       if (!mei_cl_is_connected(&dev->iamthif_cl)) {
-
-               mask = POLLERR;
-
-       } else if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
-                  dev->iamthif_file_object == file) {
+       if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
+           dev->iamthif_file_object == file) {
 
-               mask |= (POLLIN | POLLRDNORM);
-               dev_dbg(dev->dev, "run next amthif cb\n");
+               mask |= POLLIN | POLLRDNORM;
                mei_amthif_run_next_cmd(dev);
        }
-       mutex_unlock(&dev->device_lock);
 
        return mask;
 }
@@ -427,71 +404,14 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
 int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
                         struct mei_cl_cb *cmpl_list)
 {
-       struct mei_device *dev = cl->dev;
-       struct mei_msg_hdr mei_hdr;
-       size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index;
-       u32 msg_slots = mei_data2slots(len);
-       int slots;
-       int rets;
-
-       rets = mei_cl_flow_ctrl_creds(cl);
-       if (rets < 0)
-               return rets;
-
-       if (rets == 0) {
-               cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
-               return 0;
-       }
-
-       mei_hdr.host_addr = cl->host_client_id;
-       mei_hdr.me_addr = cl->me_client_id;
-       mei_hdr.reserved = 0;
-       mei_hdr.internal = 0;
-
-       slots = mei_hbuf_empty_slots(dev);
-
-       if (slots >= msg_slots) {
-               mei_hdr.length = len;
-               mei_hdr.msg_complete = 1;
-       /* Split the message only if we can write the whole host buffer */
-       } else if (slots == dev->hbuf_depth) {
-               msg_slots = slots;
-               len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
-               mei_hdr.length = len;
-               mei_hdr.msg_complete = 0;
-       } else {
-               /* wait for next time the host buffer is empty */
-               return 0;
-       }
-
-       dev_dbg(dev->dev, MEI_HDR_FMT,  MEI_HDR_PRM(&mei_hdr));
-
-       rets = mei_write_message(dev, &mei_hdr,
-                       dev->iamthif_msg_buf + dev->iamthif_msg_buf_index);
-       if (rets) {
-               dev->iamthif_state = MEI_IAMTHIF_IDLE;
-               cl->status = rets;
-               list_del(&cb->list);
-               return rets;
-       }
-
-       if (mei_cl_flow_ctrl_reduce(cl))
-               return -EIO;
-
-       dev->iamthif_msg_buf_index += mei_hdr.length;
-       cl->status = 0;
-
-       if (mei_hdr.msg_complete) {
-               dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
-               dev->iamthif_flow_control_pending = true;
-
-               /* save iamthif cb sent to amthif client */
-               cb->buf_idx = dev->iamthif_msg_buf_index;
-               dev->iamthif_current_cb = cb;
+       int ret;
 
-               list_move_tail(&cb->list, &dev->write_waiting_list.list);
-       }
+       ret = mei_cl_irq_write(cl, cb, cmpl_list);
+       if (ret)
+               return ret;
 
+       if (cb->completed)
+               cb->status = mei_amthif_read_start(cl, cb->file_object);
 
        return 0;
 }
@@ -500,83 +420,35 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
  * mei_amthif_irq_read_msg - read routine after ISR to
  *                     handle the read amthif message
  *
- * @dev: the device structure
+ * @cl: mei client
  * @mei_hdr: header of amthif message
- * @complete_list: An instance of our list structure
+ * @cmpl_list: completed callbacks list
  *
- * Return: 0 on success, <0 on failure.
+ * Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status
  */
-int mei_amthif_irq_read_msg(struct mei_device *dev,
+int mei_amthif_irq_read_msg(struct mei_cl *cl,
                            struct mei_msg_hdr *mei_hdr,
-                           struct mei_cl_cb *complete_list)
+                           struct mei_cl_cb *cmpl_list)
 {
-       struct mei_cl_cb *cb;
-       unsigned char *buffer;
-
-       BUG_ON(mei_hdr->me_addr != dev->iamthif_cl.me_client_id);
-       BUG_ON(dev->iamthif_state != MEI_IAMTHIF_READING);
+       struct mei_device *dev;
+       int ret;
 
-       buffer = dev->iamthif_msg_buf + dev->iamthif_msg_buf_index;
-       BUG_ON(dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length);
+       dev = cl->dev;
 
-       mei_read_slots(dev, buffer, mei_hdr->length);
+       if (dev->iamthif_state != MEI_IAMTHIF_READING)
+               return 0;
 
-       dev->iamthif_msg_buf_index += mei_hdr->length;
+       ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
+       if (ret)
+               return ret;
 
        if (!mei_hdr->msg_complete)
                return 0;
 
-       dev_dbg(dev->dev, "amthif_message_buffer_index =%d\n",
-                       mei_hdr->length);
-
        dev_dbg(dev->dev, "completed amthif read.\n ");
-       if (!dev->iamthif_current_cb)
-               return -ENODEV;
-
-       cb = dev->iamthif_current_cb;
        dev->iamthif_current_cb = NULL;
-
        dev->iamthif_stall_timer = 0;
-       cb->buf_idx = dev->iamthif_msg_buf_index;
-       cb->read_time = jiffies;
-       if (dev->iamthif_ioctl) {
-               /* found the iamthif cb */
-               dev_dbg(dev->dev, "complete the amthif read cb.\n ");
-               dev_dbg(dev->dev, "add the amthif read cb to complete.\n ");
-               list_add_tail(&cb->list, &complete_list->list);
-       }
-       return 0;
-}
-
-/**
- * mei_amthif_irq_read - prepares to read amthif data.
- *
- * @dev: the device structure.
- * @slots: free slots.
- *
- * Return: 0, OK; otherwise, error.
- */
-int mei_amthif_irq_read(struct mei_device *dev, s32 *slots)
-{
-       u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
-
-       if (*slots < msg_slots)
-               return -EMSGSIZE;
-
-       *slots -= msg_slots;
-
-       if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) {
-               dev_dbg(dev->dev, "iamthif flow control failed\n");
-               return -EIO;
-       }
 
-       dev_dbg(dev->dev, "iamthif flow control success\n");
-       dev->iamthif_state = MEI_IAMTHIF_READING;
-       dev->iamthif_flow_control_pending = false;
-       dev->iamthif_msg_buf_index = 0;
-       dev->iamthif_msg_buf_size = 0;
-       dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
-       dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
        return 0;
 }
 
@@ -588,17 +460,30 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots)
  */
 void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
 {
+
+       if (cb->fop_type == MEI_FOP_WRITE) {
+               if (!cb->status) {
+                       dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
+                       mei_io_cb_free(cb);
+                       return;
+               }
+               /*
+                * in case of error enqueue the write cb to complete read list
+                * so it can be propagated to the reader
+                */
+               list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);
+               wake_up_interruptible(&dev->iamthif_cl.wait);
+               return;
+       }
+
        if (dev->iamthif_canceled != 1) {
                dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
                dev->iamthif_stall_timer = 0;
-               memcpy(cb->response_buffer.data,
-                               dev->iamthif_msg_buf,
-                               dev->iamthif_msg_buf_index);
                list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);
                dev_dbg(dev->dev, "amthif read completed\n");
                dev->iamthif_timer = jiffies;
                dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n",
-                               dev->iamthif_timer);
+                       dev->iamthif_timer);
        } else {
                mei_amthif_run_next_cmd(dev);
        }
@@ -623,26 +508,22 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
 static bool mei_clear_list(struct mei_device *dev,
                const struct file *file, struct list_head *mei_cb_list)
 {
-       struct mei_cl_cb *cb_pos = NULL;
-       struct mei_cl_cb *cb_next = NULL;
+       struct mei_cl *cl = &dev->iamthif_cl;
+       struct mei_cl_cb *cb, *next;
        bool removed = false;
 
        /* list all list member */
-       list_for_each_entry_safe(cb_pos, cb_next, mei_cb_list, list) {
+       list_for_each_entry_safe(cbnext, mei_cb_list, list) {
                /* check if list member associated with a file */
-               if (file == cb_pos->file_object) {
-                       /* remove member from the list */
-                       list_del(&cb_pos->list);
+               if (file == cb->file_object) {
                        /* check if cb equal to current iamthif cb */
-                       if (dev->iamthif_current_cb == cb_pos) {
+                       if (dev->iamthif_current_cb == cb) {
                                dev->iamthif_current_cb = NULL;
                                /* send flow control to iamthif client */
-                               mei_hbm_cl_flow_control_req(dev,
-                                                       &dev->iamthif_cl);
+                               mei_hbm_cl_flow_control_req(dev, cl);
                        }
                        /* free all allocated buffers */
-                       mei_io_cb_free(cb_pos);
-                       cb_pos = NULL;
+                       mei_io_cb_free(cb);
                        removed = true;
                }
        }