diff options
author | Marcin Obara <marcin.obara@intel.com> | 2009-02-25 12:29:24 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-04-03 14:54:24 -0700 |
commit | d52b3d9c720a88d9268c92060740ed0cf87795d5 (patch) | |
tree | df9d03930d5c1c7f1d7a569b78734eb65628f26d /drivers/staging/heci/interrupt.c | |
parent | 2418a628ff9d1cfc4fecd9899f915326ff6e7b96 (diff) |
Staging: add heci driver
The Intel Management Engine Interface (aka HECI: Host Embedded
Controller Interface ) enables communication between the host OS and
the Management Engine firmware. MEI is bi-directional, and either the
host or Intel AMT firmware can initiate transactions.
The core hardware architecture of Intel Active Management Technology
(Intel AMT) is resident in firmware. The micro-controller within the
chipset's graphics and memory controller (GMCH) hub houses the
Management Engine (ME) firmware, which implements various services
on behalf of management applications.
Some of the ME subsystems that can be access via MEI driver:
- Intel(R) Quiet System Technology (QST) is implemented as a firmware
subsystem that runs in the ME. Programs that wish to expose the
health monitoring and fan speed control capabilities of Intel(R) QST
will need to use the MEI driver to communicate with the ME sub-system.
- ASF is the "Alert Standard Format" which is an DMTF manageability
standard. It is implemented in the PC's hardware and firmware, and is
managed from a remote console.
Most recent Intel desktop chipsets have one or more of the above ME
services. The MEI driver will make it possible to support the above
features on Linux and provides applications access to the ME and it's
features.
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
Signed-off-by: Marcin Obara <marcin.obara@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/heci/interrupt.c')
-rw-r--r-- | drivers/staging/heci/interrupt.c | 1554 |
1 files changed, 1554 insertions, 0 deletions
diff --git a/drivers/staging/heci/interrupt.c b/drivers/staging/heci/interrupt.c new file mode 100644 index 00000000000..54fcf90807d --- /dev/null +++ b/drivers/staging/heci/interrupt.c @@ -0,0 +1,1554 @@ +/* + * 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/kthread.h> +#include "kcompat.h" + +#include "heci.h" +#include "heci_interface.h" + +/* + * interrupt function prototypes + */ +static void heci_bh_handler(struct work_struct *work); +static int heci_bh_read_handler(struct io_heci_list *complete_list, + struct iamt_heci_device *dev, + __s32 *slots); +static int heci_bh_write_handler(struct io_heci_list *complete_list, + struct iamt_heci_device *dev, + __s32 *slots); +static void heci_bh_read_bus_message(struct iamt_heci_device *dev, + struct heci_msg_hdr *heci_hdr); +static int heci_bh_read_pthi_message(struct io_heci_list *complete_list, + struct iamt_heci_device *dev, + struct heci_msg_hdr *heci_hdr); +static int heci_bh_read_client_message(struct io_heci_list *complete_list, + struct iamt_heci_device *dev, + struct heci_msg_hdr *heci_hdr); +static void heci_client_connect_response(struct iamt_heci_device *dev, + struct hbm_client_connect_response *connect_res); +static void heci_client_disconnect_response(struct iamt_heci_device *dev, + struct hbm_client_connect_response *disconnect_res); +static void heci_client_flow_control_response(struct iamt_heci_device *dev, + struct hbm_flow_control *flow_control); +static void heci_client_disconnect_request(struct iamt_heci_device *dev, + struct hbm_client_disconnect_request *disconnect_req); + + +/** + * heci_isr_interrupt - The ISR of the HECI device + * + * @irq: The irq number + * @dev_id: pointer to the device structure + * + * returns irqreturn_t + */ +irqreturn_t heci_isr_interrupt(int irq, void *dev_id) +{ + int err; + struct iamt_heci_device *dev = (struct iamt_heci_device *) dev_id; + + dev->host_hw_state = read_heci_register(dev, H_CSR); + + if ((dev->host_hw_state & H_IS) != H_IS) + return IRQ_NONE; + + /* disable interrupts */ + heci_csr_disable_interrupts(dev); + + /* + * Our device interrupted, schedule work the heci_bh_handler + * to handle the interrupt processing. This needs to be a + * workqueue item since the handler can sleep. + */ + PREPARE_WORK(&dev->work, heci_bh_handler); + DBG("schedule work the heci_bh_handler.\n"); + err = schedule_work(&dev->work); + if (!err) { + printk(KERN_ERR "heci: schedule the heci_bh_handler" + " failed error=%x\n", err); + } + return IRQ_HANDLED; +} + +/** + * _heci_cmpl - process completed operation. + * + * @file_ext: private data of the file object. + * @priv_cb_pos: callback block. + */ +static void _heci_cmpl(struct heci_file_private *file_ext, + struct heci_cb_private *priv_cb_pos) +{ + if (priv_cb_pos->major_file_operations == HECI_WRITE) { + heci_free_cb_private(priv_cb_pos); + DBG("completing write call back.\n"); + file_ext->writing_state = HECI_WRITE_COMPLETE; + if ((&file_ext->tx_wait) && + waitqueue_active(&file_ext->tx_wait)) + wake_up_interruptible(&file_ext->tx_wait); + + } else if (priv_cb_pos->major_file_operations == HECI_READ + && HECI_READING == file_ext->reading_state) { + DBG("completing read call back information= %lu\n", + priv_cb_pos->information); + file_ext->reading_state = HECI_READ_COMPLETE; + if ((&file_ext->rx_wait) && + waitqueue_active(&file_ext->rx_wait)) + wake_up_interruptible(&file_ext->rx_wait); + + } +} + +/** + * _heci_cmpl_iamthif - process completed iamthif operation. + * + * @dev: Device object for our driver. + * @priv_cb_pos: callback block. + */ +static void _heci_cmpl_iamthif(struct iamt_heci_device *dev, + struct heci_cb_private *priv_cb_pos) +{ + if (dev->iamthif_canceled != 1) { + dev->iamthif_state = HECI_IAMTHIF_READ_COMPLETE; + dev->iamthif_stall_timer = 0; + memcpy(priv_cb_pos->response_buffer.data, + dev->iamthif_msg_buf, + dev->iamthif_msg_buf_index); + list_add_tail(&priv_cb_pos->cb_list, + &dev->pthi_read_complete_list.heci_cb.cb_list); + DBG("pthi read completed.\n"); + } else { + run_next_iamthif_cmd(dev); + } + if (&dev->iamthif_file_ext.wait) { + DBG("completing pthi call back.\n"); + wake_up_interruptible(&dev->iamthif_file_ext.wait); + } +} +/** + * heci_bh_handler - function called after ISR to handle the interrupt + * processing. + * + * @work: pointer to the work structure + * + * NOTE: This function is called by schedule work + */ +static void heci_bh_handler(struct work_struct *work) +{ + struct iamt_heci_device *dev = + container_of(work, struct iamt_heci_device, work); + struct io_heci_list complete_list; + __s32 slots; + int rets; + struct heci_cb_private *cb_pos = NULL, *cb_next = NULL; + struct heci_file_private *file_ext; + int bus_message_received = 0; + struct task_struct *tsk; + + DBG("function called after ISR to handle the interrupt processing.\n"); + /* initialize our complete list */ + spin_lock_bh(&dev->device_lock); + heci_initialize_list(&complete_list, dev); + dev->host_hw_state = read_heci_register(dev, H_CSR); + dev->me_hw_state = read_heci_register(dev, ME_CSR_HA); + + /* check if ME wants a reset */ + if (((dev->me_hw_state & ME_RDY_HRA) == 0) + && (dev->heci_state != HECI_RESETING) + && (dev->heci_state != HECI_INITIALIZING)) { + DBG("FW not ready.\n"); + heci_reset(dev, 1); + spin_unlock_bh(&dev->device_lock); + return; + } + + /* check if we need to start the dev */ + if ((dev->host_hw_state & H_RDY) == 0) { + if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) { + DBG("we need to start the dev.\n"); + dev->host_hw_state |= (H_IE | H_IG | H_RDY); + heci_set_csr_register(dev); + if (dev->heci_state == HECI_INITIALIZING) { + dev->recvd_msg = 1; + spin_unlock_bh(&dev->device_lock); + wake_up_interruptible(&dev->wait_recvd_msg); + return; + + } else { + spin_unlock_bh(&dev->device_lock); + tsk = kthread_run(heci_task_initialize_clients, + dev, "heci_reinit"); + if (IS_ERR(tsk)) { + int rc = PTR_ERR(tsk); + printk(KERN_WARNING "heci: Unable to" + "start the heci thread: %d\n", rc); + } + return; + } + } else { + DBG("enable interrupt FW not ready.\n"); + heci_csr_enable_interrupts(dev); + spin_unlock_bh(&dev->device_lock); + return; + } + } + /* check slots avalable for reading */ + slots = count_full_read_slots(dev); + DBG("slots =%08x extra_write_index =%08x.\n", + slots, dev->extra_write_index); + while ((slots > 0) && (!dev->extra_write_index)) { + DBG("slots =%08x extra_write_index =%08x.\n", slots, + dev->extra_write_index); + DBG("call heci_bh_read_handler.\n"); + rets = heci_bh_read_handler(&complete_list, dev, &slots); + if (rets != 0) + goto end; + } + rets = heci_bh_write_handler(&complete_list, dev, &slots); +end: + DBG("end of bottom half function.\n"); + dev->host_hw_state = read_heci_register(dev, H_CSR); + dev->host_buffer_is_empty = host_buffer_is_empty(dev); + + if ((dev->host_hw_state & H_IS) == H_IS) { + /* acknowledge interrupt and disable interrupts */ + heci_csr_disable_interrupts(dev); + + PREPARE_WORK(&dev->work, heci_bh_handler); + DBG("schedule work the heci_bh_handler.\n"); + rets = schedule_work(&dev->work); + if (!rets) { + printk(KERN_ERR "heci: schedule the heci_bh_handler" + " failed error=%x\n", rets); + } + } else { + heci_csr_enable_interrupts(dev); + } + + if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) { + DBG("received waiting bus message\n"); + bus_message_received = 1; + } + spin_unlock_bh(&dev->device_lock); + if (bus_message_received) { + DBG("wake up dev->wait_recvd_msg\n"); + wake_up_interruptible(&dev->wait_recvd_msg); + bus_message_received = 0; + } + if ((complete_list.status != 0) + || list_empty(&complete_list.heci_cb.cb_list)) + return; + + + list_for_each_entry_safe(cb_pos, cb_next, + &complete_list.heci_cb.cb_list, cb_list) { + file_ext = (struct heci_file_private *)cb_pos->file_private; + list_del(&cb_pos->cb_list); + if (file_ext != NULL) { + if (file_ext != &dev->iamthif_file_ext) { + DBG("completing call back.\n"); + _heci_cmpl(file_ext, cb_pos); + cb_pos = NULL; + } else if (file_ext == &dev->iamthif_file_ext) { + _heci_cmpl_iamthif(dev, cb_pos); + } + } + } +} + + +/** + * heci_bh_read_handler - bottom half read routine after ISR to + * handle the read processing. + * + * @cmpl_list: An instance of our list structure + * @dev: Device object for our driver + * @slots: slots to read. + * + * returns 0 on success, <0 on failure. + */ +static int heci_bh_read_handler(struct io_heci_list *cmpl_list, + struct iamt_heci_device *dev, + __s32 *slots) +{ + struct heci_msg_hdr *heci_hdr; + int ret = 0; + struct heci_file_private *file_pos = NULL; + struct heci_file_private *file_next = NULL; + + if (!dev->rd_msg_hdr) { + dev->rd_msg_hdr = read_heci_register(dev, ME_CB_RW); + DBG("slots=%08x.\n", *slots); + (*slots)--; + DBG("slots=%08x.\n", *slots); + } + heci_hdr = (struct heci_msg_hdr *) &dev->rd_msg_hdr; + DBG("heci_hdr->length =%d\n", heci_hdr->length); + + if ((heci_hdr->reserved) || !(dev->rd_msg_hdr)) { + DBG("corrupted message header.\n"); + ret = -ECORRUPTED_MESSAGE_HEADER; + goto end; + } + + if ((heci_hdr->host_addr) || (heci_hdr->me_addr)) { + list_for_each_entry_safe(file_pos, file_next, + &dev->file_list, link) { + DBG("list_for_each_entry_safe read host" + " client = %d, ME client = %d\n", + file_pos->host_client_id, + file_pos->me_client_id); + if ((file_pos->host_client_id == heci_hdr->host_addr) + && (file_pos->me_client_id == heci_hdr->me_addr)) + break; + } + + if (&file_pos->link == &dev->file_list) { + DBG("corrupted message header\n"); + ret = -ECORRUPTED_MESSAGE_HEADER; + goto end; + } + } + if (((*slots) * sizeof(__u32)) < heci_hdr->length) { + DBG("we can't read the message slots=%08x.\n", *slots); + /* we can't read the message */ + ret = -ERANGE; + goto end; + } + + /* decide where to read the message too */ + if (!heci_hdr->host_addr) { + DBG("call heci_bh_read_bus_message.\n"); + heci_bh_read_bus_message(dev, heci_hdr); + DBG("end heci_bh_read_bus_message.\n"); + } else if ((heci_hdr->host_addr == dev->iamthif_file_ext.host_client_id) + && (HECI_FILE_CONNECTED == dev->iamthif_file_ext.state) + && (dev->iamthif_state == HECI_IAMTHIF_READING)) { + DBG("call heci_bh_read_iamthif_message.\n"); + DBG("heci_hdr->length =%d\n", heci_hdr->length); + ret = heci_bh_read_pthi_message(cmpl_list, dev, heci_hdr); + if (ret != 0) + goto end; + + } else { + DBG("call heci_bh_read_client_message.\n"); + ret = heci_bh_read_client_message(cmpl_list, dev, heci_hdr); + if (ret != 0) + goto end; + + } + + /* reset the number of slots and header */ + *slots = count_full_read_slots(dev); + dev->rd_msg_hdr = 0; + + if (*slots == -ESLOTS_OVERFLOW) { + /* overflow - reset */ + DBG("reseting due to slots overflow.\n"); + /* set the event since message has been read */ + ret = -ERANGE; + goto end; + } +end: + return ret; +} + + +/** + * heci_bh_read_bus_message - bottom half read routine after ISR to + * handle the read bus message cmd processing. + * + * @dev: Device object for our driver + * @heci_hdr: header of bus message + */ +static void heci_bh_read_bus_message(struct iamt_heci_device *dev, + struct heci_msg_hdr *heci_hdr) +{ + struct heci_bus_message *heci_msg; + struct hbm_host_version_response *version_res; + struct hbm_client_connect_response *connect_res; + struct hbm_client_connect_response *disconnect_res; + struct hbm_flow_control *flow_control; + struct hbm_props_response *props_res; + struct hbm_host_enum_response *enum_res; + struct hbm_client_disconnect_request *disconnect_req; + struct hbm_host_stop_request *h_stop_req; + int i; + unsigned char *buffer; + + /* read the message to our buffer */ + buffer = (unsigned char *) dev->rd_msg_buf; + BUG_ON(heci_hdr->length >= sizeof(dev->rd_msg_buf)); + heci_read_slots(dev, buffer, heci_hdr->length); + heci_msg = (struct heci_bus_message *) buffer; + + switch (*(__u8 *) heci_msg) { + case HOST_START_RES_CMD: + version_res = (struct hbm_host_version_response *) heci_msg; + if (version_res->host_version_supported) { + dev->version.major_version = HBM_MAJOR_VERSION; + dev->version.minor_version = HBM_MINOR_VERSION; + } else { + dev->version = version_res->me_max_version; + } + dev->recvd_msg = 1; + DBG("host start response message received.\n"); + break; + + case CLIENT_CONNECT_RES_CMD: + connect_res = + (struct hbm_client_connect_response *) heci_msg; + heci_client_connect_response(dev, connect_res); + DBG("client connect response message received.\n"); + wake_up(&dev->wait_recvd_msg); + break; + + case CLIENT_DISCONNECT_RES_CMD: + disconnect_res = + (struct hbm_client_connect_response *) heci_msg; + heci_client_disconnect_response(dev, disconnect_res); + DBG("client disconnect response message received.\n"); + wake_up(&dev->wait_recvd_msg); + break; + + case HECI_FLOW_CONTROL_CMD: + flow_control = (struct hbm_flow_control *) heci_msg; + heci_client_flow_control_response(dev, flow_control); + DBG("client flow control response message received.\n"); + break; + + case HOST_CLIENT_PROPERTEIS_RES_CMD: + props_res = (struct hbm_props_response *) heci_msg; + if (props_res->status != 0) { + BUG(); + break; + } + for (i = 0; i < dev->num_heci_me_clients; i++) { + if (dev->me_clients[i].client_id == + props_res->address) { + dev->me_clients[i].props = + props_res->client_properties; + break; + } + + } + dev->recvd_msg = 1; + break; + + case HOST_ENUM_RES_CMD: + enum_res = (struct hbm_host_enum_response *) heci_msg; + memcpy(dev->heci_me_clients, enum_res->valid_addresses, 32); + dev->recvd_msg = 1; + break; + + case HOST_STOP_RES_CMD: + dev->heci_state = HECI_DISABLED; + DBG("reseting because of FW stop response.\n"); + heci_reset(dev, 1); + break; + + case CLIENT_DISCONNECT_REQ_CMD: + /* search for client */ + disconnect_req = + (struct hbm_client_disconnect_request *) heci_msg; + heci_client_disconnect_request(dev, disconnect_req); + break; + + case ME_STOP_REQ_CMD: + /* prepare stop request */ + heci_hdr = (struct heci_msg_hdr *) &dev->ext_msg_buf[0]; + heci_hdr->host_addr = 0; + heci_hdr->me_addr = 0; + heci_hdr->length = sizeof(struct hbm_host_stop_request); + heci_hdr->msg_complete = 1; + heci_hdr->reserved = 0; + h_stop_req = + (struct hbm_host_stop_request *) &dev->ext_msg_buf[1]; + memset(h_stop_req, 0, sizeof(struct hbm_host_stop_request)); + h_stop_req->cmd.cmd = HOST_STOP_REQ_CMD; + h_stop_req->reason = DRIVER_STOP_REQUEST; + h_stop_req->reserved[0] = 0; + h_stop_req->reserved[1] = 0; + dev->extra_write_index = 2; + break; + + default: + BUG(); + break; + + } +} + +/** + * heci_bh_read_pthi_message - bottom half read routine after ISR to + * handle the read pthi message data processing. + * + * @complete_list: An instance of our list structure + * @dev: Device object for our driver + * @heci_hdr: header of pthi message + * + * returns 0 on success, <0 on failure. + */ +static int heci_bh_read_pthi_message(struct io_heci_list *complete_list, + struct iamt_heci_device *dev, + struct heci_msg_hdr *heci_hdr) +{ + struct heci_file_private *file_ext; + struct heci_cb_private *priv_cb; + unsigned char *buffer; + + BUG_ON(heci_hdr->me_addr != dev->iamthif_file_ext.me_client_id); + BUG_ON(dev->iamthif_state != HECI_IAMTHIF_READING); + + buffer = (unsigned char *) (dev->iamthif_msg_buf + + dev->iamthif_msg_buf_index); + BUG_ON(sizeof(dev->iamthif_msg_buf) < + (dev->iamthif_msg_buf_index + heci_hdr->length)); + + heci_read_slots(dev, buffer, heci_hdr->length); + + dev->iamthif_msg_buf_index += heci_hdr->length; + + if (!(heci_hdr->msg_complete)) + return 0; + + DBG("pthi_message_buffer_index=%d\n", heci_hdr->length); + DBG("completed pthi read.\n "); + if (!dev->iamthif_current_cb) + return -ENODEV; + + priv_cb = dev->iamthif_current_cb; + dev->iamthif_current_cb = NULL; + + file_ext = (struct heci_file_private *)priv_cb->file_private; + if (!file_ext) + return -ENODEV; + + dev->iamthif_stall_timer = 0; + priv_cb->information = dev->iamthif_msg_buf_index; + priv_cb->read_time = get_seconds(); + if ((dev->iamthif_ioctl) && (file_ext == &dev->iamthif_file_ext)) { + /* found the iamthif cb */ + DBG("complete the pthi read cb.\n "); + if (&dev->iamthif_file_ext) { + DBG("add the pthi read cb to complete.\n "); + list_add_tail(&priv_cb->cb_list, + &complete_list->heci_cb.cb_list); + } + } + return 0; +} + +/** + * _heci_bh_state_ok - check if heci header matches file private data + * + * @file_ext: private data of the file object + * @heci_hdr: header of heci client message + * + * returns !=0 if matches, 0 if no match. + */ +static int _heci_bh_state_ok(struct heci_file_private *file_ext, + struct heci_msg_hdr *heci_hdr) +{ + return ((file_ext->host_client_id == heci_hdr->host_addr) + && (file_ext->me_client_id == heci_hdr->me_addr) + && (file_ext->state == HECI_FILE_CONNECTED) + && (HECI_READ_COMPLETE != file_ext->reading_state)); +} + +/** + * heci_bh_read_client_message - bottom half read routine after ISR to + * handle the read heci client message data processing. + * + * @complete_list: An instance of our list structure + * @dev: Device object for our driver + * @heci_hdr: header of heci client message + * + * returns 0 on success, <0 on failure. + */ +static int heci_bh_read_client_message(struct io_heci_list *complete_list, + struct iamt_heci_device *dev, + struct heci_msg_hdr *heci_hdr) +{ + struct heci_file_private *file_ext; + struct heci_cb_private *priv_cb_pos = NULL, *priv_cb_next = NULL; + unsigned char *buffer = NULL; + + DBG("start client msg\n"); + if (!((dev->read_list.status == 0) && + !list_empty(&dev->read_list.heci_cb.cb_list))) + goto quit; + + list_for_each_entry_safe(priv_cb_pos, priv_cb_next, + &dev->read_list.heci_cb.cb_list, cb_list) { + file_ext = (struct heci_file_private *) + priv_cb_pos->file_private; + if ((file_ext != NULL) && + (_heci_bh_state_ok(file_ext, heci_hdr))) { + spin_lock(&file_ext->read_io_lock); + file_ext->reading_state = HECI_READING; + buffer = (unsigned char *) + (priv_cb_pos->response_buffer.data + + priv_cb_pos->information); + BUG_ON(priv_cb_pos->response_buffer.size < + heci_hdr->length + + priv_cb_pos->information); + + if (priv_cb_pos->response_buffer.size < + heci_hdr->length + + priv_cb_pos->information) { + DBG("message overflow.\n"); + list_del(&priv_cb_pos->cb_list); + spin_unlock(&file_ext->read_io_lock); + return -ENOMEM; + } + if (buffer) { + heci_read_slots(dev, buffer, + heci_hdr->length); + } + priv_cb_pos->information += heci_hdr->length; + if (heci_hdr->msg_complete) { + file_ext->status = 0; + list_del(&priv_cb_pos->cb_list); + spin_unlock(&file_ext->read_io_lock); + DBG("completed read host client = %d," + "ME client = %d, " + "data length = %lu\n", + file_ext->host_client_id, + file_ext->me_client_id, + priv_cb_pos->information); + + *(priv_cb_pos->response_buffer.data + + priv_cb_pos->information) = '\0'; + DBG("priv_cb_pos->res_buffer - %s\n", + priv_cb_pos->response_buffer.data); + list_add_tail(&priv_cb_pos->cb_list, + &complete_list->heci_cb.cb_list); + } else { + spin_unlock(&file_ext->read_io_lock); + } + + break; + } + + } + +quit: + DBG("message read\n"); + if (!buffer) { + heci_read_slots(dev, (unsigned char *) dev->rd_msg_buf, + heci_hdr->length); + DBG("discarding message, header=%08x.\n", + *(__u32 *) dev->rd_msg_buf); + } + + return 0; +} + +/** + * _heci_bh_iamthif_read - prepare to read iamthif data. + * + * @dev: Device object for our driver. + * @slots: free slots. + * + * returns 0, OK; otherwise, error. + */ +static int _heci_bh_iamthif_read(struct iamt_heci_device *dev, __s32 *slots) +{ + + if (((*slots) * sizeof(__u32)) >= (sizeof(struct heci_msg_hdr) + + sizeof(struct hbm_flow_control))) { + *slots -= (sizeof(struct heci_msg_hdr) + + sizeof(struct hbm_flow_control) + 3) / 4; + if (!heci_send_flow_control(dev, &dev->iamthif_file_ext)) { + DBG("iamthif flow control failed\n"); + } else { + DBG("iamthif flow control success\n"); + dev->iamthif_state = HECI_IAMTHIF_READING; + dev->iamthif_flow_control_pending = 0; + dev->iamthif_msg_buf_index = 0; + dev->iamthif_msg_buf_size = 0; + dev->iamthif_stall_timer = IAMTHIF_STALL_TIMER; + dev->host_buffer_is_empty = host_buffer_is_empty(dev); + } + return 0; + } else { + return -ECOMPLETE_MESSAGE; + } +} + +/** + * _heci_bh_close - process close related operation. + * + * @dev: Device object for our driver. + * @slots: free slots. + * @priv_cb_pos: callback block. + * @file_ext: private data of the file object. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int _heci_bh_close(struct iamt_heci_device *dev, __s32 *slots, + struct heci_cb_private *priv_cb_pos, + struct heci_file_private *file_ext, + struct io_heci_list *cmpl_list) +{ + if ((*slots * sizeof(__u32)) >= (sizeof(struct heci_msg_hdr) + + sizeof(struct hbm_client_disconnect_request))) { + *slots -= (sizeof(struct heci_msg_hdr) + + sizeof(struct hbm_client_disconnect_request) + 3) / 4; + + if (!heci_disconnect(dev, file_ext)) { + file_ext->status = 0; + priv_cb_pos->information = 0; + list_move_tail(&priv_cb_pos->cb_list, + &cmpl_list->heci_cb.cb_list); + return -ECOMPLETE_MESSAGE; + } else { + file_ext->state = HECI_FILE_DISCONNECTING; + file_ext->status = 0; + priv_cb_pos->information = 0; + list_move_tail(&priv_cb_pos->cb_list, + &dev->ctrl_rd_list.heci_cb.cb_list); + file_ext->timer_count = HECI_CONNECT_TIMEOUT; + } + } else { + /* return the cancel routine */ + return -ECORRUPTED_MESSAGE_HEADER; + } + + return 0; +} + +/** + * _heci_hb_close - process read related operation. + * + * @dev: Device object for our driver. + * @slots: free slots. + * @priv_cb_pos: callback block. + * @file_ext: private data of the file object. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int _heci_bh_read(struct iamt_heci_device *dev, __s32 *slots, + struct heci_cb_private *priv_cb_pos, + struct heci_file_private *file_ext, + struct io_heci_list *cmpl_list) +{ + if ((*slots * sizeof(__u32)) >= (sizeof(struct heci_msg_hdr) + + sizeof(struct hbm_flow_control))) { + *slots -= (sizeof(struct heci_msg_hdr) + + sizeof(struct hbm_flow_control) + 3) / 4; + if (!heci_send_flow_control(dev, file_ext)) { + file_ext->status = -ENODEV; + priv_cb_pos->information = 0; + list_move_tail(&priv_cb_pos->cb_list, + &cmpl_list->heci_cb.cb_list); + return -ENODEV; + } else { + list_move_tail(&priv_cb_pos->cb_list, + &dev->read_list.heci_cb.cb_list); + } + } else { + /* return the cancel routine */ + list_del(&priv_cb_pos->cb_list); + return -ECORRUPTED_MESSAGE_HEADER; + } + + return 0; +} + + +/** + * _heci_bh_ioctl - process ioctl related operation. + * + * @dev: Device object for our driver. + * @slots: free slots. + * @priv_cb_pos: callback block. + * @file_ext: private data of the file object. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int _heci_bh_ioctl(struct iamt_heci_device *dev, __s32 *slots, + struct heci_cb_private *priv_cb_pos, + struct heci_file_private *file_ext, + struct io_heci_list *cmpl_list) +{ + if ((*slots * sizeof(__u32)) >= (sizeof(struct heci_msg_hdr) + + sizeof(struct hbm_client_connect_request))) { + file_ext->state = HECI_FILE_CONNECTING; + *slots -= (sizeof(struct heci_msg_hdr) + + sizeof(struct hbm_client_connect_request) + 3) / 4; + if (!heci_connect(dev, file_ext)) { + file_ext->status = -ENODEV; + priv_cb_pos->information = 0; + list_del(&priv_cb_pos->cb_list); + return -ENODEV; + } else { + list_move_tail(&priv_cb_pos->cb_list, + &dev->ctrl_rd_list.heci_cb.cb_list); + file_ext->timer_count = HECI_CONNECT_TIMEOUT; + } + } else { + /* return the cancel routine */ + list_del(&priv_cb_pos->cb_list); + return -ECORRUPTED_MESSAGE_HEADER; + } + + return 0; +} + +/** + * _heci_bh_cmpl - process completed and no-iamthif operation. + * + * @dev: Device object for our driver. + * @slots: free slots. + * @priv_cb_pos: callback block. + * @file_ext: private data of the file object. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int _heci_bh_cmpl(struct iamt_heci_device *dev, __s32 *slots, + struct heci_cb_private *priv_cb_pos, + struct heci_file_private *file_ext, + struct io_heci_list *cmpl_list) +{ + struct heci_msg_hdr *heci_hdr; + + if ((*slots * sizeof(__u32)) >= (sizeof(struct heci_msg_hdr) + + (priv_cb_pos->request_buffer.size - + priv_cb_pos->information))) { + heci_hdr = (struct heci_msg_hdr *) &dev->wr_msg_buf[0]; + heci_hdr->host_addr = file_ext->host_client_id; + heci_hdr->me_addr = file_ext->me_client_id; + heci_hdr->length = ((priv_cb_pos->request_buffer.size) - + (priv_cb_pos->information)); + heci_hdr->msg_complete = 1; + heci_hdr->reserved = 0; + DBG("priv_cb_pos->request_buffer.size =%d" + "heci_hdr->msg_complete= %d\n", + priv_cb_pos->request_buffer.size, + heci_hdr->msg_complete); + DBG("priv_cb_pos->information =%lu\n", + priv_cb_pos->information); + DBG("heci_hdr->length =%d\n", + heci_hdr->length); + *slots -= (sizeof(struct heci_msg_hdr) + + heci_hdr->length + 3) / 4; + if (!heci_write_message(dev, heci_hdr, + (unsigned char *) + (priv_cb_pos->request_buffer.data + + priv_cb_pos->information), + heci_hdr->length)) { + file_ext->status = -ENODEV; + list_move_tail(&priv_cb_pos->cb_list, + &cmpl_list->heci_cb.cb_list); + return -ENODEV; + } else { + flow_ctrl_reduce(dev, file_ext); + file_ext->status = 0; + priv_cb_pos->information += heci_hdr->length; + list_move_tail(&priv_cb_pos->cb_list, + &dev->write_waiting_list.heci_cb.cb_list); + } + } else if (*slots == ((dev->host_hw_state & H_CBD) >> 24)) { + /* buffer is still empty */ + heci_hdr = (struct heci_msg_hdr *) &dev->wr_msg_buf[0]; + heci_hdr->host_addr = file_ext->host_client_id; + heci_hdr->me_addr = file_ext->me_client_id; + heci_hdr->length = + (*slots * sizeof(__u32)) - sizeof(struct heci_msg_hdr); + heci_hdr->msg_complete = 0; + heci_hdr->reserved = 0; + + (*slots) -= (sizeof(struct heci_msg_hdr) + + heci_hdr->length + 3) / 4; + if (!heci_write_message(dev, heci_hdr, + (unsigned char *) + (priv_cb_pos->request_buffer.data + + priv_cb_pos->information), + heci_hdr->length)) { + file_ext->status = -ENODEV; + list_move_tail(&priv_cb_pos->cb_list, + &cmpl_list->heci_cb.cb_list); + return -ENODEV; + } else { + priv_cb_pos->information += heci_hdr->length; + DBG("priv_cb_pos->request_buffer.size =%d" + " heci_hdr->msg_complete= %d\n", + priv_cb_pos->request_buffer.size, + heci_hdr->msg_complete); + DBG("priv_cb_pos->information =%lu\n", + priv_cb_pos->information); + DBG("heci_hdr->length =%d\n", heci_hdr->length); + } + return -ECOMPLETE_MESSAGE; + } else { + return -ECORRUPTED_MESSAGE_HEADER; + } + + return 0; +} + +/** + * _heci_bh_cmpl_iamthif - process completed iamthif operation. + * + * @dev: Device object for our driver. + * @slots: free slots. + * @priv_cb_pos: callback block. + * @file_ext: private data of the file object. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int _heci_bh_cmpl_iamthif(struct iamt_heci_device *dev, __s32 *slots, + struct heci_cb_private *priv_cb_pos, + struct heci_file_private *file_ext, + struct io_heci_list *cmpl_list) +{ + struct heci_msg_hdr *heci_hdr; + + if ((*slots * sizeof(__u32)) >= (sizeof(struct heci_msg_hdr) + + dev->iamthif_msg_buf_size - + dev->iamthif_msg_buf_index)) { + heci_hdr = (struct heci_msg_hdr *) &dev->wr_msg_buf[0]; + heci_hdr->host_addr = file_ext->host_client_id; + heci_hdr->me_addr = file_ext->me_client_id; + heci_hdr->length = dev->iamthif_msg_buf_size - + dev->iamthif_msg_buf_index; + heci_hdr->msg_complete = 1; + heci_hdr->reserved = 0; + + *slots -= (sizeof(struct heci_msg_hdr) + + heci_hdr->length + 3) / 4; + + if (!heci_write_message(dev, heci_hdr, + (dev->iamthif_msg_buf + + dev->iamthif_msg_buf_index), + heci_hdr->length)) { + dev->iamthif_state = HECI_IAMTHIF_IDLE; + file_ext->status = -ENODEV; + list_del(&priv_cb_pos->cb_list); + return -ENODEV; + } else { + flow_ctrl_reduce(dev, file_ext); + dev->iamthif_msg_buf_index += heci_hdr->length; + priv_cb_pos->information = dev->iamthif_msg_buf_index; + file_ext->status = 0; + dev->iamthif_state = HECI_IAMTHIF_FLOW_CONTROL; + dev->iamthif_flow_control_pending = 1; + /* save iamthif cb sent to pthi client */ + dev->iamthif_current_cb = priv_cb_pos; + list_move_tail(&priv_cb_pos->cb_list, + &dev->write_waiting_list.heci_cb.cb_list); + + } + } else if (*slots == ((dev->host_hw_state & H_CBD) >> 24)) { + /* buffer is still empty */ + heci_hdr = (struct heci_msg_hdr *) &dev->wr_msg_buf[0]; + heci_hdr->host_addr = file_ext->host_client_id; + heci_hdr->me_addr = file_ext->me_client_id; + heci_hdr->length = + (*slots * sizeof(__u32)) - sizeof(struct heci_msg_hdr); + heci_hdr->msg_complete = 0; + heci_hdr->reserved = 0; + + *slots -= (sizeof(struct heci_msg_hdr) + + heci_hdr->length + 3) / 4; + + if (!heci_write_message(dev, heci_hdr, + (dev->iamthif_msg_buf + + dev->iamthif_msg_buf_index), + heci_hdr->length)) { + file_ext->status = -ENODEV; + list_del(&priv_cb_pos->cb_list); + } else { + dev->iamthif_msg_buf_index += heci_hdr->length; + } + return -ECOMPLETE_MESSAGE; + } else { + return -ECORRUPTED_MESSAGE_HEADER; + } + + return 0; +} + +/** + * heci_bh_write_handler - bottom half write routine after + * ISR to handle the write processing. + * + * @cmpl_list: An instance of our list structure + * @dev: Device object for our driver + * @slots: slots to write. + * + * returns 0 on success, <0 on failure. + */ +static int heci_bh_write_handler(struct io_heci_list *cmpl_list, + struct iamt_heci_device *dev, + __s32 *slots) +{ + + struct heci_file_private *file_ext; + struct heci_cb_private *priv_cb_pos = NULL, *priv_cb_next = NULL; + struct io_heci_list *list; + int ret; + + if (!host_buffer_is_empty(dev)) { + DBG("host buffer is not empty.\n"); + return 0; + } + dev->write_hang = -1; + *slots = count_empty_write_slots(dev); + /* complete all waiting for write CB */ + DBG("complete all waiting for write cb.\n"); + + list = &dev->write_waiting_list; + if ((list->status == 0) + && !list_empty(&list->heci_cb.cb_list)) { + list_for_each_entry_safe(priv_cb_pos, priv_cb_next, + &list->heci_cb.cb_list, cb_list) { + file_ext = (struct heci_file_private *) + priv_cb_pos->file_private; + if (file_ext != NULL) { + file_ext->status = 0; + list_del(&priv_cb_pos->cb_list); + if ((HECI_WRITING == file_ext->writing_state) && + (priv_cb_pos->major_file_operations == + HECI_WRITING) && + (file_ext != &dev->iamthif_file_ext)) { + DBG("HECI WRITE COMPLETE\n"); + file_ext->writing_state = + HECI_WRITE_COMPLETE; + list_add_tail(&priv_cb_pos->cb_list, + &cmpl_list->heci_cb.cb_list); + } + if (file_ext == &dev->iamthif_file_ext) { + DBG("check iamthif flow control.\n"); + if (dev->iamthif_flow_control_pending) { + ret = _heci_bh_iamthif_read(dev, + slots); + if (ret != 0) + return ret; + } + } + } + + } + } + + if ((dev->stop) && (!dev->wd_pending)) { + dev->wd_stoped = 1; + wake_up_interruptible(&dev->wait_stop_wd); + return 0; + } + + if (dev->extra_write_index != 0) { + DBG("extra_write_index =%d.\n", dev->extra_write_index); + heci_write_message(dev, + (struct heci_msg_hdr *) &dev->ext_msg_buf[0], + (unsigned char *) &dev->ext_msg_buf[1], + (dev->extra_write_index - 1) * sizeof(__u32)); + *slots -= dev->extra_write_index; + dev->extra_write_index = 0; + } + if (dev->heci_state == HECI_ENABLED) { + if ((dev->wd_pending) + && flow_ctrl_creds(dev, &dev->wd_file_ext)) { + if (!heci_send_wd(dev)) + DBG("wd send failed.\n"); + else + flow_ctrl_reduce(dev, &dev->wd_file_ext); + + dev->wd_pending = 0; + + if (dev->wd_timeout != 0) { + *slots -= (sizeof(struct heci_msg_hdr) + + HECI_START_WD_DATA_SIZE + 3) / 4; + dev->wd_due_counter = 2; + } else { + *slots -= (sizeof(struct heci_msg_hdr) + + HECI_WD_PARAMS_SIZE + 3) / 4; + dev->wd_due_counter = 0; + } + + } + } + if (dev->stop) + return ~ENODEV; + + /* complete control write list CB */ + if (dev->ctrl_wr_list.status == 0) { + /* complete control write list CB */ + DBG("complete control write list cb.\n"); + list_for_each_entry_safe(priv_cb_pos, priv_cb_next, + &dev->ctrl_wr_list.heci_cb.cb_list, cb_list) { + file_ext = (struct heci_file_private *) + priv_cb_pos->file_private; + if (file_ext == NULL) { + list_del(&priv_cb_pos->cb_list); + return -ENODEV; + } + switch (priv_cb_pos->major_file_operations) { + case HECI_CLOSE: + /* send disconnect message */ + ret = _heci_bh_close(dev, slots, + priv_cb_pos, + file_ext, cmpl_list); + if (ret != 0) + return ret; + + break; + case HECI_READ: + /* send flow control message */ + ret = _heci_bh_read(dev, slots, + priv_cb_pos, + file_ext, cmpl_list); + if (ret != 0) + return ret; + + break; + case HECI_IOCTL: + /* connect message */ + if (!other_client_is_connecting(dev, file_ext)) + continue; + ret = _heci_bh_ioctl(dev, slots, + priv_cb_pos, + file_ext, cmpl_list); + if (ret != 0) + return ret; + + break; + + default: + BUG(); + } + + } + } + /* complete write list CB */ + if ((dev->write_list.status == 0) + && !list_empty(&dev->write_list.heci_cb.cb_list)) { + DBG("complete write list cb.\n"); + list_for_each_entry_safe(priv_cb_pos, priv_cb_next, + &dev->write_list.heci_cb.cb_list, cb_list) { + file_ext = (struct heci_file_private *) + priv_cb_pos->file_private; + + if (file_ext != NULL) { + if (file_ext != &dev->iamthif_file_ext) { + if (!flow_ctrl_creds(dev, file_ext)) { + DBG("No flow control" + " credentials for client" + " %d, not sending.\n", + file_ext->host_client_id); + continue; + } + ret = _heci_bh_cmpl(dev, slots, + priv_cb_pos, + file_ext, + cmpl_list); + if (ret != 0) + return ret; + + } else if (file_ext == &dev->iamthif_file_ext) { + /* IAMTHIF IOCTL */ + DBG("complete pthi write cb.\n"); + if (!flow_ctrl_creds(dev, file_ext)) { + DBG("No flow control" + " credentials for pthi" + " client %d.\n", + file_ext->host_client_id); + continue; + } + ret = _heci_bh_cmpl_iamthif(dev, slots, + priv_cb_pos, + file_ext, + cmpl_list); + if (ret != 0) + return ret; + + } + } + + } + } + return 0; +} + + +/** + * is_treat_specially_client - check if the message belong + * to the file private data. + * + * @file_ext: private data of the file object + * @rs: connect response bus message + * @dev: Device object for our driver + * + * returns 0 on success, <0 on failure. + */ +static int is_treat_specially_client(struct heci_file_private *file_ext, + struct hbm_client_connect_response *rs) +{ + int ret = 0; + + if ((file_ext->host_client_id == rs->host_addr) && + (file_ext->me_client_id == rs->me_addr)) { + if (rs->status == 0) { + DBG("client connect status = 0x%08x.\n", rs->status); + file_ext->state = HECI_FILE_CONNECTED; + file_ext->status = 0; + } else { + DBG("client connect status = 0x%08x.\n", rs->status); + file_ext->state = HECI_FILE_DISCONNECTED; + file_ext->status = -ENODEV; + } + ret = 1; + } + DBG("client state = %d.\n", file_ext->state); + return ret; +} + +/** + * heci_client_connect_response - connect response bh routine + * + * @dev: Device object for our driver + * @rs: connect response bus message + */ +static void heci_client_connect_response(struct iamt_heci_device *dev, + struct hbm_client_connect_response *rs) +{ + + struct heci_file_private *file_ext; + struct heci_cb_private *priv_cb_pos = NULL, *priv_cb_next = NULL; + + /* if WD or iamthif client treat specially */ + + if ((is_treat_specially_client(&(dev->wd_file_ext), rs)) || + (is_treat_specially_client(&(dev->iamthif_file_ext), rs))) + return; + + if (dev->ctrl_rd_list.status == 0 + && !list_empty(&dev->ctrl_rd_list.heci_cb.cb_list)) { + list_for_each_entry_safe(priv_cb_pos, priv_cb_next, + &dev->ctrl_rd_list.heci_cb.cb_list, cb_list) { + file_ext = (struct heci_file_private *) + priv_cb_pos->file_private; + if (file_ext == NULL) { + list_del(&priv_cb_pos->cb_list); + return; + } + if (HECI_IOCTL == priv_cb_pos->major_file_operations) { + if (is_treat_specially_client(file_ext, rs)) { + list_del(&priv_cb_pos->cb_list); + file_ext->status = 0; + file_ext->timer_count = 0; + break; + } + } + } + } +} + +/** + * heci_client_disconnect_response - disconnect response bh routine + * + * @dev: Device object for our driver + * @rs: disconnect response bus message + */ +static void heci_client_disconnect_response(struct iamt_heci_device *dev, + struct hbm_client_connect_response *rs) +{ + struct heci_file_private *file_ext; + struct heci_cb_private *priv_cb_pos = NULL, *priv_cb_next = NULL; + + if (dev->ctrl_rd_list.status == 0 + && !list_empty(&dev->ctrl_rd_list.heci_cb.cb_list)) { + list_for_each_entry_safe(priv_cb_pos, priv_cb_next, + &dev->ctrl_rd_list.heci_cb.cb_list, cb_list) { + file_ext = (struct heci_file_private *) + priv_cb_pos->file_private; + + if (file_ext == NULL) { + list_del(&priv_cb_pos->cb_list); + return; + } + + DBG("list_for_each_entry_safe in ctrl_rd_list.\n"); + if ((file_ext->host_client_id == rs->host_addr) && + (file_ext->me_client_id == rs->me_addr)) { + + list_del(&priv_cb_pos->cb_list); + if (rs->status == 0) { + file_ext->state = + HECI_FILE_DISCONNECTED; + } + + file_ext->status = 0; + file_ext->timer_count = 0; + break; + } + } + } +} + +/** + * same_flow_addr - tell they have same address. + * + * @file: private data of the file object. + * @flow: flow control. + * + * returns !=0, same; 0,not. + */ +static int same_flow_addr(struct heci_file_private *file, + struct hbm_flow_control *flow) +{ + return ((file->host_client_id == flow->host_addr) + && (file->me_client_id == flow->me_addr)); +} + +/** + * add_single_flow_creds - add single buffer credentials. + * + * @file: private data ot the file object. + * @flow: flow control. + */ +static void add_single_flow_creds(struct iamt_heci_device *dev, + struct hbm_flow_control *flow) +{ + struct heci_me_client *client; + int i; + + for (i = 0; i < dev->num_heci_me_clients; i++) { + client = &dev->me_clients[i]; + if ((client != NULL) && + (flow->me_addr == client->client_id)) { + if (client->props.single_recv_buf != 0) { + client->flow_ctrl_creds++; + DBG("recv flow ctrl msg ME %d (single).\n", + flow->me_addr); + DBG("flow control credentials=%d.\n", + client->flow_ctrl_creds); + } else { + BUG(); /* error in flow control */ + } + } + } +} + +/** + * heci_client_flow_control_response - flow control response bh routine + * + * @dev: Device object for our driver + * @flow_control: flow control response bus message + */ +static void heci_client_flow_control_response(struct iamt_heci_device *dev, + struct hbm_flow_control *flow_control) +{ + struct heci_file_private *file_pos = NULL; + struct heci_file_private *file_next = NULL; + + if (flow_control->host_addr == 0) { + /* single receive buffer */ + add_single_flow_creds(dev, flow_control); + } else { + /* normal connection */ + list_for_each_entry_safe(file_pos, file_next, + &dev->file_list, link) { + DBG("list_for_each_entry_safe in file_list\n"); + + DBG("file_ext of host client %d ME client %d.\n", + file_pos->host_client_id, + file_pos->me_client_id); + DBG("flow ctrl msg for host %d ME %d.\n", + flow_control->host_addr, + flow_control->me_addr); + if (same_flow_addr(file_pos, flow_control)) { + DBG("recv ctrl msg for host %d ME %d.\n", + flow_control->host_addr, + flow_control->me_addr); + file_pos->flow_ctrl_creds++; + DBG("flow control credentials=%d.\n", + file_pos->flow_ctrl_creds); + break; + } + } + } +} + +/** + * same_disconn_addr - tell they have same address + * + * @file: private data of the file object. + * @disconn: disconnection request. + * + * returns !=0, same; 0,not. + */ +static int same_disconn_addr(struct heci_file_private *file, + struct hbm_client_disconnect_request *disconn) +{ + return ((file->host_client_id == disconn->host_addr) + && (file->me_client_id == disconn->me_addr)); +} + +/** + * heci_client_disconnect_request - disconnect request bh routine + * + * @dev: Device object for our driver. + * @disconnect_req: disconnect request bus message. + */ +static void heci_client_disconnect_request(struct iamt_heci_device *dev, + struct hbm_client_disconnect_request *disconnect_req) +{ + struct heci_msg_hdr *heci_hdr; + struct hbm_client_connect_response *disconnect_res; + struct heci_file_private *file_pos = NULL; + struct heci_file_private *file_next = NULL; + + list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link) { + if (same_disconn_addr(file_pos, disconnect_req)) { + DBG("disconnect request host client %d ME client %d.\n", + disconnect_req->host_addr, + disconnect_req->me_addr); + file_pos->state = HECI_FILE_DISCONNECTED; + file_pos->timer_count = 0; + if (file_pos == &dev->wd_file_ext) { + dev->wd_due_counter = 0; + dev->wd_pending = 0; + } else if (file_pos == &dev->iamthif_file_ext) + dev->iamthif_timer = 0; + + /* prepare disconnect response */ + heci_hdr = + (struct heci_msg_hdr *) &dev->ext_msg_buf[0]; + heci_hdr->host_addr = 0; + heci_hdr->me_addr = 0; + heci_hdr->length = + sizeof(struct hbm_client_connect_response); + heci_hdr->msg_complete = 1; + heci_hdr->reserved = 0; + + disconnect_res = + (struct hbm_client_connect_response *) + &dev->ext_msg_buf[1]; + disconnect_res->host_addr = file_pos->host_client_id; + disconnect_res->me_addr = file_pos->me_client_id; + *(__u8 *) (&disconnect_res->cmd) = + CLIENT_DISCONNECT_RES_CMD; + disconnect_res->status = 0; + dev->extra_write_index = 2; + break; + } + } +} + +/** + * heci_timer - timer function. + * + * @data: pointer to the device structure + * + * NOTE: This function is called by timer interrupt work + */ +void heci_wd_timer(unsigned long data) +{ + struct iamt_heci_device *dev = (struct iamt_heci_device *) data; + + DBG("send watchdog.\n"); + spin_lock_bh(&dev->device_lock); + if (dev->heci_state != HECI_ENABLED) { + mod_timer(&dev->wd_timer, round_jiffies(jiffies + 2 * HZ)); + spin_unlock_bh(&dev->device_lock); + return; + } + if (dev->wd_file_ext.state != HECI_FILE_CONNECTED) { + mod_timer(&dev->wd_timer, round_jiffies(jiffies + 2 * HZ)); + spin_unlock_bh(&dev->device_lock); + return; + } + /* Watchdog */ + if ((dev->wd_due_counter != 0) && (dev->wd_bypass == 0)) { + if (--dev->wd_due_counter == 0) { + if (dev->host_buffer_is_empty && + flow_ctrl_creds(dev, &dev->wd_file_ext)) { + dev->host_buffer_is_empty = 0; + if (!heci_send_wd(dev)) { + DBG("wd send failed.\n"); + } else { + flow_ctrl_reduce(dev, + &dev->wd_file_ext); + } + + if (dev->wd_timeout != 0) + dev->wd_due_counter = 2; + else + dev->wd_due_counter = 0; + + } else + dev->wd_pending = 1; + + } + } + if (dev->iamthif_stall_timer != 0) { + if (--dev->iamthif_stall_timer == 0) { + DBG("reseting because of hang to PTHI.\n"); + heci_reset(dev, 1); + 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; + spin_unlock_bh(&dev->device_lock); + + if (dev->iamthif_current_cb) + heci_free_cb_private(dev->iamthif_current_cb); + + spin_lock_bh(&dev->device_lock); + dev->iamthif_file_object = NULL; + dev->iamthif_current_cb = NULL; + run_next_iamthif_cmd(dev); + } + } + mod_timer(&dev->wd_timer, round_jiffies(jiffies + 2 * HZ)); + spin_unlock_bh(&dev->device_lock); +} |