]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/staging/heci/io_heci.c
Merge branch 'omap-pool'
[linux-2.6-omap-h63xx.git] / drivers / staging / heci / io_heci.c
diff --git a/drivers/staging/heci/io_heci.c b/drivers/staging/heci/io_heci.c
new file mode 100644 (file)
index 0000000..f7544a7
--- /dev/null
@@ -0,0 +1,847 @@
+/*
+ * Part of Intel(R) Manageability Engine Interface Linux driver
+ *
+ * Copyright (c) 2003 - 2008 Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/aio.h>
+#include <linux/pci.h>
+#include <linux/reboot.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/kdev_t.h>
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/delay.h>
+
+#include "heci_data_structures.h"
+#include "heci.h"
+#include "heci_interface.h"
+#include "heci_version.h"
+
+
+/**
+ * heci_ioctl_get_version - the get driver version IOCTL function
+ *
+ * @dev: Device object for our driver
+ * @if_num:  minor number
+ * @*u_msg: pointer to user data struct in user space
+ * @k_msg: data in kernel on the stack
+ * @file_ext: private data of the file object
+ *
+ * returns 0 on success, <0 on failure.
+ */
+int heci_ioctl_get_version(struct iamt_heci_device *dev, int if_num,
+                          struct heci_message_data __user *u_msg,
+                          struct heci_message_data k_msg,
+                          struct heci_file_private *file_ext)
+{
+       int rets = 0;
+       struct heci_driver_version *version;
+       struct heci_message_data res_msg;
+
+       if ((if_num != HECI_MINOR_NUMBER) || (!dev)
+           || (!file_ext))
+               return -ENODEV;
+
+       if (k_msg.size < (sizeof(struct heci_driver_version) - 2)) {
+               DBG("user buffer less than heci_driver_version.\n");
+               return -EMSGSIZE;
+       }
+
+       res_msg.data = kmalloc(sizeof(struct heci_driver_version), GFP_KERNEL);
+       if (!res_msg.data) {
+               DBG("failed allocation response buffer size = %d.\n",
+                   (int) sizeof(struct heci_driver_version));
+               return -ENOMEM;
+       }
+
+       version = (struct heci_driver_version *) res_msg.data;
+       version->major = MAJOR_VERSION;
+       version->minor = MINOR_VERSION;
+       version->hotfix = QUICK_FIX_NUMBER;
+       version->build = VER_BUILD;
+       res_msg.size = sizeof(struct heci_driver_version);
+       if (k_msg.size < sizeof(struct heci_driver_version))
+               res_msg.size -= 2;
+
+       rets = file_ext->status;
+       /* now copy the data to user space */
+       if (copy_to_user(k_msg.data, res_msg.data, res_msg.size)) {
+               rets = -EFAULT;
+               goto end;
+       }
+       if (put_user(res_msg.size, &u_msg->size)) {
+               rets = -EFAULT;
+               goto end;
+       }
+end:
+       kfree(res_msg.data);
+       return rets;
+}
+
+/**
+ * heci_ioctl_connect_client - the connect to fw client IOCTL function
+ *
+ * @dev: Device object for our driver
+ * @if_num:  minor number
+ * @*u_msg: pointer to user data struct in user space
+ * @k_msg: data in kernel on the stack
+ * @file_ext: private data of the file object
+ *
+ * returns 0 on success, <0 on failure.
+ */
+int heci_ioctl_connect_client(struct iamt_heci_device *dev, int if_num,
+                             struct heci_message_data __user *u_msg,
+                             struct heci_message_data k_msg,
+                             struct file *file)
+{
+       int rets = 0;
+       struct heci_message_data req_msg, res_msg;
+       struct heci_cb_private *priv_cb = NULL;
+       struct heci_client *client;
+       struct heci_file_private *file_ext;
+       struct heci_file_private *file_pos = NULL;
+       struct heci_file_private *file_next = NULL;
+       long timeout = 15;      /*15 second */
+       __u8 i;
+       int err = 0;
+
+       if ((if_num != HECI_MINOR_NUMBER) || (!dev) || (!file))
+               return -ENODEV;
+
+       file_ext = file->private_data;
+       if (!file_ext)
+               return -ENODEV;
+
+       if (k_msg.size != sizeof(struct guid)) {
+               DBG("user buffer size is not equal to size of struct "
+                               "guid(16).\n");
+               return -EMSGSIZE;
+       }
+
+       if (!k_msg.data)
+               return -EIO;
+
+       req_msg.data = kmalloc(sizeof(struct guid), GFP_KERNEL);
+       res_msg.data = kmalloc(sizeof(struct heci_client), GFP_KERNEL);
+
+       if (!res_msg.data) {
+               DBG("failed allocation response buffer size = %d.\n",
+                   (int) sizeof(struct heci_client));
+               kfree(req_msg.data);
+               return -ENOMEM;
+       }
+       if (!req_msg.data) {
+               DBG("failed allocation request buffer size = %d.\n",
+                   (int) sizeof(struct guid));
+               kfree(res_msg.data);
+               return -ENOMEM;
+       }
+       req_msg.size = sizeof(struct guid);
+       res_msg.size = sizeof(struct heci_client);
+
+       /* copy the message to kernel space -
+        * use a pointer already copied into kernel space
+        */
+       if (copy_from_user(req_msg.data, k_msg.data, k_msg.size)) {
+               rets = -EFAULT;
+               goto end;
+       }
+       /* buffered ioctl cb */
+       priv_cb = kzalloc(sizeof(struct heci_cb_private), GFP_KERNEL);
+       if (!priv_cb) {
+               rets = -ENOMEM;
+               goto end;
+       }
+       INIT_LIST_HEAD(&priv_cb->cb_list);
+       priv_cb->response_buffer.data = res_msg.data;
+       priv_cb->response_buffer.size = res_msg.size;
+       priv_cb->request_buffer.data = req_msg.data;
+       priv_cb->request_buffer.size = req_msg.size;
+       priv_cb->major_file_operations = HECI_IOCTL;
+       spin_lock_bh(&dev->device_lock);
+       if (dev->heci_state != HECI_ENABLED) {
+               rets = -ENODEV;
+               spin_unlock_bh(&dev->device_lock);
+               goto end;
+       }
+       if ((file_ext->state != HECI_FILE_INITIALIZING) &&
+           (file_ext->state != HECI_FILE_DISCONNECTED)) {
+               rets = -EBUSY;
+               spin_unlock_bh(&dev->device_lock);
+               goto end;
+       }
+
+       /* find ME client we're trying to connect to */
+       for (i = 0; i < dev->num_heci_me_clients; i++) {
+               if (memcmp((struct guid *)req_msg.data,
+                           &dev->me_clients[i].props.protocol_name,
+                           sizeof(struct guid)) == 0) {
+                       if (dev->me_clients[i].props.fixed_address == 0) {
+                               file_ext->me_client_id =
+                                   dev->me_clients[i].client_id;
+                               file_ext->state = HECI_FILE_CONNECTING;
+                       }
+                       break;
+               }
+       }
+       /* if we're connecting to PTHI client so we will use the exist
+        * connection
+        */
+       if (memcmp((struct guid *)req_msg.data, &heci_pthi_guid,
+                               sizeof(struct guid)) == 0) {
+               if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTED) {
+                       rets = -ENODEV;
+                       spin_unlock_bh(&dev->device_lock);
+                       goto end;
+               }
+               dev->heci_host_clients[file_ext->host_client_id / 8] &=
+                       ~(1 << (file_ext->host_client_id % 8));
+               list_for_each_entry_safe(file_pos,
+                   file_next, &dev->file_list, link) {
+                       if (heci_fe_same_id(file_ext, file_pos)) {
+                               DBG("remove file private data node host"
+                                   " client = %d, ME client = %d.\n",
+                                   file_pos->host_client_id,
+                                   file_pos->me_client_id);
+                               list_del(&file_pos->link);
+                       }
+
+               }
+               DBG("free file private data memory.\n");
+               kfree(file_ext);
+               file_ext = NULL;
+               file->private_data = &dev->iamthif_file_ext;
+               client = (struct heci_client *) res_msg.data;
+               client->max_msg_length =
+                       dev->me_clients[i].props.max_msg_length;
+               client->protocol_version =
+                       dev->me_clients[i].props.protocol_version;
+               rets = dev->iamthif_file_ext.status;
+               spin_unlock_bh(&dev->device_lock);
+
+               /* now copy the data to user space */
+               if (copy_to_user(k_msg.data, res_msg.data, res_msg.size)) {
+                       rets = -EFAULT;
+                       goto end;
+               }
+               if (put_user(res_msg.size, &u_msg->size)) {
+                       rets = -EFAULT;
+                       goto end;
+               }
+               goto end;
+       }
+       spin_lock(&file_ext->file_lock);
+       if (file_ext->state != HECI_FILE_CONNECTING) {
+               rets = -ENODEV;
+               spin_unlock(&file_ext->file_lock);
+               spin_unlock_bh(&dev->device_lock);
+               goto end;
+       }
+       spin_unlock(&file_ext->file_lock);
+       /* prepare the output buffer */
+       client = (struct heci_client *) res_msg.data;
+       client->max_msg_length = dev->me_clients[i].props.max_msg_length;
+       client->protocol_version = dev->me_clients[i].props.protocol_version;
+       if (dev->host_buffer_is_empty
+           && !other_client_is_connecting(dev, file_ext)) {
+               dev->host_buffer_is_empty = 0;
+               if (!heci_connect(dev, file_ext)) {
+                       rets = -ENODEV;
+                       spin_unlock_bh(&dev->device_lock);
+                       goto end;
+               } else {
+                       file_ext->timer_count = HECI_CONNECT_TIMEOUT;
+                       priv_cb->file_private = file_ext;
+                       list_add_tail(&priv_cb->cb_list,
+                                     &dev->ctrl_rd_list.heci_cb.
+                                     cb_list);
+               }
+
+
+       } else {
+               priv_cb->file_private = file_ext;
+               DBG("add connect cb to control write list.\n");
+               list_add_tail(&priv_cb->cb_list,
+                             &dev->ctrl_wr_list.heci_cb.cb_list);
+       }
+       spin_unlock_bh(&dev->device_lock);
+       err = wait_event_timeout(dev->wait_recvd_msg,
+                       (HECI_FILE_CONNECTED == file_ext->state
+                        || HECI_FILE_DISCONNECTED == file_ext->state),
+                       timeout * HZ);
+
+       if (HECI_FILE_CONNECTED == file_ext->state) {
+               DBG("successfully connected to FW client.\n");
+               rets = file_ext->status;
+               /* now copy the data to user space */
+               if (copy_to_user(k_msg.data, res_msg.data, res_msg.size)) {
+                       rets = -EFAULT;
+                       goto end;
+               }
+               if (put_user(res_msg.size, &u_msg->size)) {
+                       rets = -EFAULT;
+                       goto end;
+               }
+               goto end;
+       } else {
+               DBG("failed to connect to FW client.file_ext->state = %d.\n",
+                   file_ext->state);
+               if (!err) {
+                       DBG("wait_event_interruptible_timeout failed on client"
+                           " connect message fw response message.\n");
+               }
+               rets = -EFAULT;
+               goto remove_list;
+       }
+
+remove_list:
+       if (priv_cb) {
+               spin_lock_bh(&dev->device_lock);
+               heci_flush_list(&dev->ctrl_rd_list, file_ext);
+               heci_flush_list(&dev->ctrl_wr_list, file_ext);
+               spin_unlock_bh(&dev->device_lock);
+       }
+end:
+       DBG("free connect cb memory.");
+       kfree(req_msg.data);
+       kfree(res_msg.data);
+       kfree(priv_cb);
+       return rets;
+}
+
+/**
+ * heci_ioctl_wd  - the wd IOCTL function
+ *
+ * @dev: Device object for our driver
+ * @if_num:  minor number
+ * @k_msg: data in kernel on the stack
+ * @file_ext: private data of the file object
+ *
+ * returns 0 on success, <0 on failure.
+ */
+int heci_ioctl_wd(struct iamt_heci_device *dev, int if_num,
+                 struct heci_message_data k_msg,
+                 struct heci_file_private *file_ext)
+{
+       int rets = 0;
+       struct heci_message_data req_msg;       /*in kernel on the stack */
+
+       if (if_num != HECI_MINOR_NUMBER)
+               return -ENODEV;
+
+       spin_lock(&file_ext->file_lock);
+       if (k_msg.size != HECI_WATCHDOG_DATA_SIZE) {
+               DBG("user buffer has invalid size.\n");
+               spin_unlock(&file_ext->file_lock);
+               return -EMSGSIZE;
+       }
+       spin_unlock(&file_ext->file_lock);
+
+       req_msg.data = kmalloc(HECI_WATCHDOG_DATA_SIZE, GFP_KERNEL);
+       if (!req_msg.data) {
+               DBG("failed allocation request buffer size = %d.\n",
+                   HECI_WATCHDOG_DATA_SIZE);
+               return -ENOMEM;
+       }
+       req_msg.size = HECI_WATCHDOG_DATA_SIZE;
+
+       /* copy the message to kernel space - use a pointer already
+        * copied into kernel space
+        */
+       if (copy_from_user(req_msg.data, k_msg.data, req_msg.size)) {
+               rets = -EFAULT;
+               goto end;
+       }
+       spin_lock_bh(&dev->device_lock);
+       if (dev->heci_state != HECI_ENABLED) {
+               rets = -ENODEV;
+               spin_unlock_bh(&dev->device_lock);
+               goto end;
+       }
+
+       if (dev->wd_file_ext.state != HECI_FILE_CONNECTED) {
+               rets = -ENODEV;
+               spin_unlock_bh(&dev->device_lock);
+               goto end;
+       }
+       if (!dev->asf_mode) {
+               rets = -EIO;
+               spin_unlock_bh(&dev->device_lock);
+               goto end;
+       }
+
+       memcpy(&dev->wd_data[HECI_WD_PARAMS_SIZE], req_msg.data,
+              HECI_WATCHDOG_DATA_SIZE);
+
+       dev->wd_timeout = (req_msg.data[1] << 8) + req_msg.data[0];
+       dev->wd_pending = 0;
+       dev->wd_due_counter = 1;        /* next timer */
+       if (dev->wd_timeout == 0) {
+               memcpy(dev->wd_data, heci_stop_wd_params,
+                      HECI_WD_PARAMS_SIZE);
+       } else {
+               memcpy(dev->wd_data, heci_start_wd_params,
+                      HECI_WD_PARAMS_SIZE);
+               mod_timer(&dev->wd_timer, jiffies);
+       }
+       spin_unlock_bh(&dev->device_lock);
+end:
+       kfree(req_msg.data);
+       return rets;
+}
+
+
+/**
+ * heci_ioctl_bypass_wd  - the bypass_wd IOCTL function
+ *
+ * @dev: Device object for our driver
+ * @if_num:  minor number
+ * @k_msg: data in kernel on the stack
+ * @file_ext: private data of the file object
+ *
+ * returns 0 on success, <0 on failure.
+ */
+int heci_ioctl_bypass_wd(struct iamt_heci_device *dev, int if_num,
+                 struct heci_message_data k_msg,
+                 struct heci_file_private *file_ext)
+{
+       __u8 flag = 0;
+       int rets = 0;
+
+       if (if_num != HECI_MINOR_NUMBER)
+               return -ENODEV;
+
+       spin_lock(&file_ext->file_lock);
+       if (k_msg.size < 1) {
+               DBG("user buffer less than HECI_WATCHDOG_DATA_SIZE.\n");
+               spin_unlock(&file_ext->file_lock);
+               return -EMSGSIZE;
+       }
+       spin_unlock(&file_ext->file_lock);
+       if (copy_from_user(&flag, k_msg.data, 1)) {
+               rets = -EFAULT;
+               goto end;
+       }
+
+       spin_lock_bh(&dev->device_lock);
+       flag = flag ? (1) : (0);
+       dev->wd_bypass = flag;
+       spin_unlock_bh(&dev->device_lock);
+end:
+       return rets;
+}
+
+/**
+ * find_pthi_read_list_entry - finds a PTHIlist entry for current file
+ *
+ * @dev: Device object for our driver
+ * @file: pointer to file object
+ *
+ * returns   returned a list entry on success, NULL on failure.
+ */
+struct heci_cb_private *find_pthi_read_list_entry(
+               struct iamt_heci_device *dev,
+               struct file *file)
+{
+       struct heci_file_private *file_ext_temp;
+       struct heci_cb_private *priv_cb_pos = NULL;
+       struct heci_cb_private *priv_cb_next = NULL;
+
+       if ((dev->pthi_read_complete_list.status == 0) &&
+           !list_empty(&dev->pthi_read_complete_list.heci_cb.cb_list)) {
+               list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+                   &dev->pthi_read_complete_list.heci_cb.cb_list, cb_list) {
+                       file_ext_temp = (struct heci_file_private *)
+                                       priv_cb_pos->file_private;
+                       if ((file_ext_temp != NULL) &&
+                           (file_ext_temp == &dev->iamthif_file_ext) &&
+                           (priv_cb_pos->file_object == file))
+                               return priv_cb_pos;
+               }
+       }
+       return NULL;
+}
+
+/**
+ * pthi_read - read data from pthi client
+ *
+ * @dev: Device object for our driver
+ * @if_num:  minor number
+ * @file: pointer to file object
+ * @*ubuf: pointer to user data in user space
+ * @length: data length to read
+ * @offset: data read offset
+ *
+ * returns
+ *  returned data length on success,
+ *  zero if no data to read,
+ *  negative on failure.
+ */
+int pthi_read(struct iamt_heci_device *dev, int if_num, struct file *file,
+             char __user *ubuf, size_t length, loff_t *offset)
+{
+       int rets = 0;
+       struct heci_cb_private *priv_cb = NULL;
+       struct heci_file_private *file_ext = file->private_data;
+       __u8 i;
+       unsigned long currtime = get_seconds();
+
+       if ((if_num != HECI_MINOR_NUMBER) || (!dev))
+               return -ENODEV;
+
+       if ((file_ext == NULL) || (file_ext != &dev->iamthif_file_ext))
+               return -ENODEV;
+
+       spin_lock_bh(&dev->device_lock);
+       for (i = 0; i < dev->num_heci_me_clients; i++) {
+               if (dev->me_clients[i].client_id ==
+                   dev->iamthif_file_ext.me_client_id)
+                       break;
+       }
+       BUG_ON(dev->me_clients[i].client_id != file_ext->me_client_id);
+       if ((i == dev->num_heci_me_clients)
+           || (dev->me_clients[i].client_id !=
+               dev->iamthif_file_ext.me_client_id)) {
+               DBG("PTHI client not found.\n");
+               spin_unlock_bh(&dev->device_lock);
+               return -ENODEV;
+       }
+       priv_cb = find_pthi_read_list_entry(dev, file);
+       if (!priv_cb) {
+               spin_unlock_bh(&dev->device_lock);
+               return 0; /* No more data to read */
+       } else {
+               if (priv_cb &&
+                   (currtime - priv_cb->read_time > IAMTHIF_READ_TIMER)) {
+                       /* 15 sec for the message has expired */
+                       list_del(&priv_cb->cb_list);
+                       spin_unlock_bh(&dev->device_lock);
+                       rets = -ETIMEDOUT;
+                       goto free;
+               }
+               /* if the whole message will fit remove it from the list */
+               if ((priv_cb->information >= *offset)  &&
+                   (length >= (priv_cb->information - *offset)))
+                       list_del(&priv_cb->cb_list);
+               else if ((priv_cb->information > 0) &&
+                   (priv_cb->information <= *offset)) {
+                       /* end of the message has been reached */
+                       list_del(&priv_cb->cb_list);
+                       rets = 0;
+                       spin_unlock_bh(&dev->device_lock);
+                       goto free;
+               }
+               /* else means that not full buffer will be read and do not
+                * remove message from deletion list
+                */
+       }
+       DBG("pthi priv_cb->response_buffer size - %d\n",
+           priv_cb->response_buffer.size);
+       DBG("pthi priv_cb->information - %lu\n",
+           priv_cb->information);
+       spin_unlock_bh(&dev->device_lock);
+
+       /* length is being turncated to PAGE_SIZE, however,
+        * the information may be longer */
+       length = length < (priv_cb->information - *offset) ?
+                       length : (priv_cb->information - *offset);
+
+       if (copy_to_user(ubuf,
+                        priv_cb->response_buffer.data + *offset,
+                        length))
+               rets = -EFAULT;
+       else {
+               rets = length;
+               if ((*offset + length) < priv_cb->information) {
+                       *offset += length;
+                       goto out;
+               }
+       }
+free:
+       DBG("free pthi cb memory.\n");
+       *offset = 0;
+       heci_free_cb_private(priv_cb);
+out:
+       return rets;
+}
+
+/**
+ * heci_start_read  - the start read client message function.
+ *
+ * @dev: Device object for our driver
+ * @if_num:  minor number
+ * @file_ext: private data of the file object
+ *
+ * returns 0 on success, <0 on failure.
+ */
+int heci_start_read(struct iamt_heci_device *dev, int if_num,
+                   struct heci_file_private *file_ext)
+{
+       int rets = 0;
+       __u8 i;
+       struct heci_cb_private *priv_cb = NULL;
+
+       if ((if_num != HECI_MINOR_NUMBER) || (!dev) || (!file_ext)) {
+               DBG("received wrong function input param.\n");
+               return -ENODEV;
+       }
+       if (file_ext->state != HECI_FILE_CONNECTED)
+               return -ENODEV;
+
+       spin_lock_bh(&dev->device_lock);
+       if (dev->heci_state != HECI_ENABLED) {
+               spin_unlock_bh(&dev->device_lock);
+               return -ENODEV;
+       }
+       spin_unlock_bh(&dev->device_lock);
+       DBG("check if read is pending.\n");
+       if ((file_ext->read_pending) || (file_ext->read_cb != NULL)) {
+               DBG("read is pending.\n");
+               return -EBUSY;
+       }
+       priv_cb = kzalloc(sizeof(struct heci_cb_private), GFP_KERNEL);
+       if (!priv_cb)
+               return -ENOMEM;
+
+       DBG("allocation call back success\n"
+           "host client = %d, ME client = %d\n",
+           file_ext->host_client_id, file_ext->me_client_id);
+       spin_lock_bh(&dev->device_lock);
+       for (i = 0; i < dev->num_heci_me_clients; i++) {
+               if (dev->me_clients[i].client_id == file_ext->me_client_id)
+                       break;
+
+       }
+
+       BUG_ON(dev->me_clients[i].client_id != file_ext->me_client_id);
+       if (i == dev->num_heci_me_clients) {
+               rets = -ENODEV;
+               goto unlock;
+       }
+
+       priv_cb->response_buffer.size = dev->me_clients[i].props.max_msg_length;
+       spin_unlock_bh(&dev->device_lock);
+       priv_cb->response_buffer.data =
+           kmalloc(priv_cb->response_buffer.size, GFP_KERNEL);
+       if (!priv_cb->response_buffer.data) {
+               rets = -ENOMEM;
+               goto fail;
+       }
+       DBG("allocation call back data success.\n");
+       priv_cb->major_file_operations = HECI_READ;
+       /* make sure information is zero before we start */
+       priv_cb->information = 0;
+       priv_cb->file_private = (void *) file_ext;
+       file_ext->read_cb = priv_cb;
+       spin_lock_bh(&dev->device_lock);
+       if (dev->host_buffer_is_empty) {
+               dev->host_buffer_is_empty = 0;
+               if (!heci_send_flow_control(dev, file_ext)) {
+                       rets = -ENODEV;
+                       goto unlock;
+               } else {
+                       list_add_tail(&priv_cb->cb_list,
+                                     &dev->read_list.heci_cb.cb_list);
+               }
+       } else {
+               list_add_tail(&priv_cb->cb_list,
+                             &dev->ctrl_wr_list.heci_cb.cb_list);
+       }
+       spin_unlock_bh(&dev->device_lock);
+       return rets;
+unlock:
+       spin_unlock_bh(&dev->device_lock);
+fail:
+       heci_free_cb_private(priv_cb);
+       return rets;
+}
+
+/**
+ * pthi_write - write iamthif data to pthi client
+ *
+ * @dev: Device object for our driver
+ * @priv_cb: heci call back struct
+ *
+ * returns 0 on success, <0 on failure.
+ */
+int pthi_write(struct iamt_heci_device *dev,
+              struct heci_cb_private *priv_cb)
+{
+       int rets = 0;
+       struct heci_msg_hdr heci_hdr;
+
+       if ((!dev) || (!priv_cb))
+               return -ENODEV;
+
+       DBG("write data to pthi client.\n");
+
+       dev->iamthif_state = HECI_IAMTHIF_WRITING;
+       dev->iamthif_current_cb = priv_cb;
+       dev->iamthif_file_object = priv_cb->file_object;
+       dev->iamthif_canceled = 0;
+       dev->iamthif_ioctl = 1;
+       dev->iamthif_msg_buf_size = priv_cb->request_buffer.size;
+       memcpy(dev->iamthif_msg_buf, priv_cb->request_buffer.data,
+           priv_cb->request_buffer.size);
+
+       if (flow_ctrl_creds(dev, &dev->iamthif_file_ext) &&
+           dev->host_buffer_is_empty) {
+               dev->host_buffer_is_empty = 0;
+               if (priv_cb->request_buffer.size >
+                   (((dev->host_hw_state & H_CBD) >> 24) *
+                   sizeof(__u32)) - sizeof(struct heci_msg_hdr)) {
+                       heci_hdr.length =
+                           (((dev->host_hw_state & H_CBD) >> 24) *
+                           sizeof(__u32)) - sizeof(struct heci_msg_hdr);
+                       heci_hdr.msg_complete = 0;
+               } else {
+                       heci_hdr.length = priv_cb->request_buffer.size;
+                       heci_hdr.msg_complete = 1;
+               }
+
+               heci_hdr.host_addr = dev->iamthif_file_ext.host_client_id;
+               heci_hdr.me_addr = dev->iamthif_file_ext.me_client_id;
+               heci_hdr.reserved = 0;
+               dev->iamthif_msg_buf_index += heci_hdr.length;
+               if (!heci_write_message(dev, &heci_hdr,
+                                       (unsigned char *)(dev->iamthif_msg_buf),
+                                       heci_hdr.length))
+                       return -ENODEV;
+
+               if (heci_hdr.msg_complete) {
+                       flow_ctrl_reduce(dev, &dev->iamthif_file_ext);
+                       dev->iamthif_flow_control_pending = 1;
+                       dev->iamthif_state = HECI_IAMTHIF_FLOW_CONTROL;
+                       DBG("add pthi cb to write waiting list\n");
+                       dev->iamthif_current_cb = priv_cb;
+                       dev->iamthif_file_object = priv_cb->file_object;
+                       list_add_tail(&priv_cb->cb_list,
+                                     &dev->write_waiting_list.heci_cb.cb_list);
+               } else {
+                       DBG("message does not complete, "
+                                       "so add pthi cb to write list.\n");
+                       list_add_tail(&priv_cb->cb_list,
+                                     &dev->write_list.heci_cb.cb_list);
+               }
+       } else {
+               if (!(dev->host_buffer_is_empty))
+                       DBG("host buffer is not empty");
+
+               DBG("No flow control credentials, "
+                               "so add iamthif cb to write list.\n");
+               list_add_tail(&priv_cb->cb_list,
+                             &dev->write_list.heci_cb.cb_list);
+       }
+       return rets;
+}
+
+/**
+ * iamthif_ioctl_send_msg - send cmd data to pthi client
+ *
+ * @dev: Device object for our driver
+ *
+ * returns 0 on success, <0 on failure.
+ */
+void run_next_iamthif_cmd(struct iamt_heci_device *dev)
+{
+       struct heci_file_private *file_ext_tmp;
+       struct heci_cb_private *priv_cb_pos = NULL;
+       struct heci_cb_private *priv_cb_next = NULL;
+       int status = 0;
+
+       if (!dev)
+               return;
+
+       dev->iamthif_msg_buf_size = 0;
+       dev->iamthif_msg_buf_index = 0;
+       dev->iamthif_canceled = 0;
+       dev->iamthif_ioctl = 1;
+       dev->iamthif_state = HECI_IAMTHIF_IDLE;
+       dev->iamthif_timer = 0;
+       dev->iamthif_file_object = NULL;
+
+       if (dev->pthi_cmd_list.status == 0 &&
+           !list_empty(&dev->pthi_cmd_list.heci_cb.cb_list)) {
+               DBG("complete pthi cmd_list cb.\n");
+
+               list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+                   &dev->pthi_cmd_list.heci_cb.cb_list, cb_list) {
+                       list_del(&priv_cb_pos->cb_list);
+                       file_ext_tmp = (struct heci_file_private *)
+                                       priv_cb_pos->file_private;
+
+                       if ((file_ext_tmp != NULL) &&
+                           (file_ext_tmp == &dev->iamthif_file_ext)) {
+                               status = pthi_write(dev, priv_cb_pos);
+                               if (status != 0) {
+                                       DBG("pthi write failed status = %d\n",
+                                                       status);
+                                       return;
+                               }
+                               break;
+                       }
+               }
+       }
+}
+
+/**
+ * heci_free_cb_private - free heci_cb_private related memory
+ *
+ * @priv_cb: heci callback struct
+ */
+void heci_free_cb_private(struct heci_cb_private *priv_cb)
+{
+       if (priv_cb == NULL)
+               return;
+
+       kfree(priv_cb->request_buffer.data);
+       kfree(priv_cb->response_buffer.data);
+       kfree(priv_cb);
+}
+