]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/misc/mei/client.c
mei: revamp writing slot counting
[karo-tx-linux.git] / drivers / misc / mei / client.c
index 9b809cfc289924912b92a00d826831fbb46433ec..2b0f99955ba6a71babc3d6e2c27df09be661d729 100644 (file)
  * mei_me_cl_by_uuid - locate index of me client
  *
  * @dev: mei device
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
  * returns me client index or -ENOENT if not found
  */
 int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid)
 {
-       int i, res = -ENOENT;
+       int i;
 
        for (i = 0; i < dev->me_clients_num; ++i)
                if (uuid_le_cmp(*uuid,
-                               dev->me_clients[i].props.protocol_name) == 0) {
-                       res = i;
-                       break;
-               }
+                               dev->me_clients[i].props.protocol_name) == 0)
+                       return i;
 
-       return res;
+       return -ENOENT;
 }
 
 
@@ -60,16 +61,12 @@ int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid)
 int mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
 {
        int i;
+
        for (i = 0; i < dev->me_clients_num; i++)
                if (dev->me_clients[i].client_id == client_id)
-                       break;
-       if (WARN_ON(dev->me_clients[i].client_id != client_id))
-               return -ENOENT;
+                       return i;
 
-       if (i == dev->me_clients_num)
-               return -ENOENT;
-
-       return i;
+       return -ENOENT;
 }
 
 
@@ -254,10 +251,9 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev)
 struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
 {
        struct mei_device *dev = cl->dev;
-       struct mei_cl_cb *cb = NULL;
-       struct mei_cl_cb *next = NULL;
+       struct mei_cl_cb *cb;
 
-       list_for_each_entry_safe(cb, next, &dev->read_list.list, list)
+       list_for_each_entry(cb, &dev->read_list.list, list)
                if (mei_cl_cmp_id(cl, cb->cl))
                        return cb;
        return NULL;
@@ -375,6 +371,23 @@ void mei_host_client_init(struct work_struct *work)
        mutex_unlock(&dev->device_lock);
 }
 
+/**
+ * mei_hbuf_acquire: try to acquire host buffer
+ *
+ * @dev: the device structure
+ * returns true if host buffer was acquired
+ */
+bool mei_hbuf_acquire(struct mei_device *dev)
+{
+       if (!dev->hbuf_is_ready) {
+               dev_dbg(&dev->pdev->dev, "hbuf is not ready\n");
+               return false;
+       }
+
+       dev->hbuf_is_ready = false;
+
+       return true;
+}
 
 /**
  * mei_cl_disconnect - disconnect host client from the me one
@@ -406,8 +419,7 @@ int mei_cl_disconnect(struct mei_cl *cl)
                return -ENOMEM;
 
        cb->fop_type = MEI_FOP_CLOSE;
-       if (dev->hbuf_is_ready) {
-               dev->hbuf_is_ready = false;
+       if (mei_hbuf_acquire(dev)) {
                if (mei_hbm_cl_disconnect_req(dev, cl)) {
                        rets = -ENODEV;
                        cl_err(dev, cl, "failed to disconnect.\n");
@@ -461,17 +473,17 @@ free:
 bool mei_cl_is_other_connecting(struct mei_cl *cl)
 {
        struct mei_device *dev;
-       struct mei_cl *pos;
-       struct mei_cl *next;
+       struct mei_cl *ocl; /* the other client */
 
        if (WARN_ON(!cl || !cl->dev))
                return false;
 
        dev = cl->dev;
 
-       list_for_each_entry_safe(pos, next, &dev->file_list, link) {
-               if ((pos->state == MEI_FILE_CONNECTING) &&
-                   (pos != cl) && cl->me_client_id == pos->me_client_id)
+       list_for_each_entry(ocl, &dev->file_list, link) {
+               if (ocl->state == MEI_FILE_CONNECTING &&
+                   ocl != cl &&
+                   cl->me_client_id == ocl->me_client_id)
                        return true;
 
        }
@@ -505,11 +517,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
                goto out;
        }
 
-       cb->fop_type = MEI_FOP_IOCTL;
-
-       if (dev->hbuf_is_ready && !mei_cl_is_other_connecting(cl)) {
-               dev->hbuf_is_ready = false;
+       cb->fop_type = MEI_FOP_CONNECT;
 
+       /* run hbuf acquire last so we don't have to undo */
+       if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
                if (mei_hbm_cl_connect_req(dev, cl)) {
                        rets = -ENODEV;
                        goto out;
@@ -521,18 +532,19 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
        }
 
        mutex_unlock(&dev->device_lock);
-       rets = wait_event_timeout(dev->wait_recvd_msg,
-                                (cl->state == MEI_FILE_CONNECTED ||
-                                 cl->state == MEI_FILE_DISCONNECTED),
-                                mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
+       wait_event_timeout(dev->wait_recvd_msg,
+                       (cl->state == MEI_FILE_CONNECTED ||
+                        cl->state == MEI_FILE_DISCONNECTED),
+                       mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
        mutex_lock(&dev->device_lock);
 
        if (cl->state != MEI_FILE_CONNECTED) {
-               rets = -EFAULT;
+               /* something went really wrong */
+               if (!cl->status)
+                       cl->status = -EFAULT;
 
                mei_io_list_flush(&dev->ctrl_rd_list, cl);
                mei_io_list_flush(&dev->ctrl_wr_list, cl);
-               goto out;
        }
 
        rets = cl->status;
@@ -554,7 +566,8 @@ out:
 int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
 {
        struct mei_device *dev;
-       int i;
+       struct mei_me_client *me_cl;
+       int id;
 
        if (WARN_ON(!cl || !cl->dev))
                return -EINVAL;
@@ -567,19 +580,19 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
        if (cl->mei_flow_ctrl_creds > 0)
                return 1;
 
-       for (i = 0; i < dev->me_clients_num; i++) {
-               struct mei_me_client  *me_cl = &dev->me_clients[i];
-               if (me_cl->client_id == cl->me_client_id) {
-                       if (me_cl->mei_flow_ctrl_creds) {
-                               if (WARN_ON(me_cl->props.single_recv_buf == 0))
-                                       return -EINVAL;
-                               return 1;
-                       } else {
-                               return 0;
-                       }
-               }
+       id = mei_me_cl_by_id(dev, cl->me_client_id);
+       if (id < 0) {
+               cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
+               return id;
        }
-       return -ENOENT;
+
+       me_cl = &dev->me_clients[id];
+       if (me_cl->mei_flow_ctrl_creds) {
+               if (WARN_ON(me_cl->props.single_recv_buf == 0))
+                       return -EINVAL;
+               return 1;
+       }
+       return 0;
 }
 
 /**
@@ -595,32 +608,31 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
 int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
 {
        struct mei_device *dev;
-       int i;
+       struct mei_me_client *me_cl;
+       int id;
 
        if (WARN_ON(!cl || !cl->dev))
                return -EINVAL;
 
        dev = cl->dev;
 
-       if (!dev->me_clients_num)
-               return -ENOENT;
+       id = mei_me_cl_by_id(dev, cl->me_client_id);
+       if (id < 0) {
+               cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
+               return id;
+       }
 
-       for (i = 0; i < dev->me_clients_num; i++) {
-               struct mei_me_client  *me_cl = &dev->me_clients[i];
-               if (me_cl->client_id == cl->me_client_id) {
-                       if (me_cl->props.single_recv_buf != 0) {
-                               if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
-                                       return -EINVAL;
-                               dev->me_clients[i].mei_flow_ctrl_creds--;
-                       } else {
-                               if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
-                                       return -EINVAL;
-                               cl->mei_flow_ctrl_creds--;
-                       }
-                       return 0;
-               }
+       me_cl = &dev->me_clients[id];
+       if (me_cl->props.single_recv_buf != 0) {
+               if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
+                       return -EINVAL;
+               me_cl->mei_flow_ctrl_creds--;
+       } else {
+               if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
+                       return -EINVAL;
+               cl->mei_flow_ctrl_creds--;
        }
-       return -ENOENT;
+       return 0;
 }
 
 /**
@@ -666,9 +678,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
                goto err;
 
        cb->fop_type = MEI_FOP_READ;
-       cl->read_cb = cb;
-       if (dev->hbuf_is_ready) {
-               dev->hbuf_is_ready = false;
+       if (mei_hbuf_acquire(dev)) {
                if (mei_hbm_cl_flow_control_req(dev, cl)) {
                        cl_err(dev, cl, "flow control send failed\n");
                        rets = -ENODEV;
@@ -678,6 +688,9 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
        } else {
                list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
        }
+
+       cl->read_cb = cb;
+
        return rets;
 err:
        mei_io_cb_free(cb);
@@ -685,27 +698,26 @@ err:
 }
 
 /**
- * mei_cl_irq_write_complete - write a message to device
+ * mei_cl_irq_write - write a message to device
  *     from the interrupt thread context
  *
  * @cl: client
  * @cb: callback block.
- * @slots: free slots.
  * @cmpl_list: complete list.
  *
  * returns 0, OK; otherwise error.
  */
-int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
-                                    s32 *slots, struct mei_cl_cb *cmpl_list)
+int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
+                    struct mei_cl_cb *cmpl_list)
 {
        struct mei_device *dev;
        struct mei_msg_data *buf;
        struct mei_msg_hdr mei_hdr;
        size_t len;
        u32 msg_slots;
+       int slots;
        int rets;
 
-
        if (WARN_ON(!cl || !cl->dev))
                return -ENODEV;
 
@@ -722,6 +734,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
                return 0;
        }
 
+       slots = mei_hbuf_empty_slots(dev);
        len = buf->size - cb->buf_idx;
        msg_slots = mei_data2slots(len);
 
@@ -730,13 +743,13 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
        mei_hdr.reserved = 0;
        mei_hdr.internal = cb->internal;
 
-       if (*slots >= msg_slots) {
+       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);
+       } 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 {
@@ -747,7 +760,6 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
        cl_dbg(dev, cl, "buf: size = %d idx = %lu\n",
                        cb->request_buffer.size, cb->buf_idx);
 
-       *slots -=  msg_slots;
        rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
        if (rets) {
                cl->status = rets;
@@ -800,21 +812,29 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
 
 
        cb->fop_type = MEI_FOP_WRITE;
+       cb->buf_idx = 0;
+       cl->writing_state = MEI_IDLE;
+
+       mei_hdr.host_addr = cl->host_client_id;
+       mei_hdr.me_addr = cl->me_client_id;
+       mei_hdr.reserved = 0;
+       mei_hdr.msg_complete = 0;
+       mei_hdr.internal = cb->internal;
 
        rets = mei_cl_flow_ctrl_creds(cl);
        if (rets < 0)
                goto err;
 
-       /* Host buffer is not ready, we queue the request */
-       if (rets == 0 || !dev->hbuf_is_ready) {
-               cb->buf_idx = 0;
-               /* unseting complete will enqueue the cb for write */
-               mei_hdr.msg_complete = 0;
+       if (rets == 0) {
+               cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
+               rets = buf->size;
+               goto out;
+       }
+       if (!mei_hbuf_acquire(dev)) {
+               cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
                rets = buf->size;
                goto out;
        }
-
-       dev->hbuf_is_ready = false;
 
        /* Check for a maximum length */
        if (buf->size > mei_hbuf_max_len(dev)) {
@@ -825,12 +845,6 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
                mei_hdr.msg_complete = 1;
        }
 
-       mei_hdr.host_addr = cl->host_client_id;
-       mei_hdr.me_addr = cl->me_client_id;
-       mei_hdr.reserved = 0;
-       mei_hdr.internal = cb->internal;
-
-
        rets = mei_write_message(dev, &mei_hdr, buf->data);
        if (rets)
                goto err;
@@ -903,9 +917,9 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
 
 void mei_cl_all_disconnect(struct mei_device *dev)
 {
-       struct mei_cl *cl, *next;
+       struct mei_cl *cl;
 
-       list_for_each_entry_safe(cl, next, &dev->file_list, link) {
+       list_for_each_entry(cl, &dev->file_list, link) {
                cl->state = MEI_FILE_DISCONNECTED;
                cl->mei_flow_ctrl_creds = 0;
                cl->timer_count = 0;
@@ -920,8 +934,8 @@ void mei_cl_all_disconnect(struct mei_device *dev)
  */
 void mei_cl_all_wakeup(struct mei_device *dev)
 {
-       struct mei_cl *cl, *next;
-       list_for_each_entry_safe(cl, next, &dev->file_list, link) {
+       struct mei_cl *cl;
+       list_for_each_entry(cl, &dev->file_list, link) {
                if (waitqueue_active(&cl->rx_wait)) {
                        cl_dbg(dev, cl, "Waking up reading client!\n");
                        wake_up_interruptible(&cl->rx_wait);