![Greg Kroah-Hartman](/assets/img/avatar_default.png)
It's not needed now that we are now in the main kernel tree. Cc: Anas Nashif <anas.nashif@intel.com> Cc: Marcin Obara <marcin.obara@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
1554 lines
44 KiB
C
1554 lines
44 KiB
C
/*
|
|
* 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 "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);
|
|
}
|