diff options
Diffstat (limited to 'drivers/staging/heci/io_heci.c')
-rw-r--r-- | drivers/staging/heci/io_heci.c | 847 |
1 files changed, 847 insertions, 0 deletions
diff --git a/drivers/staging/heci/io_heci.c b/drivers/staging/heci/io_heci.c new file mode 100644 index 000000000000..f7544a7bbbe0 --- /dev/null +++ b/drivers/staging/heci/io_heci.c @@ -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); +} + |