/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* 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 <sys/ddi_impldefs.h>
#include <sys/instance.h>
#include "heci_data_structures.h"
#include "heci.h"
#include "heci_interface.h"
/*
* interrupt function prototypes
*/
static void heci_bh_handler(void *data);
struct iamt_heci_device *dev,
struct iamt_heci_device *dev,
struct heci_msg_hdr *heci_hdr);
struct iamt_heci_device *dev,
struct heci_msg_hdr *heci_hdr);
struct iamt_heci_device *dev,
struct heci_msg_hdr *heci_hdr);
struct hbm_client_connect_response *connect_res);
struct hbm_client_connect_response *disconnect_res);
struct hbm_flow_control *flow_control);
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
*
* @return irqreturn_t
*/
{
(struct iamt_heci_device *)(void *)arg1;
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
/* disable interrupts */
/*
* Our device interrupted, schedule work the heci_bh_handler
* to handle the interrupt processing. This needs to be a
* taskq
*/
DBG("schedule work the heci_bh_handler.\n");
return (DDI_INTR_CLAIMED);
}
/*
* _heci_cmpl: process completed operation.
*
* @file_ext: private data of the file object.
* @priv_cb_pos: callback block.
*/
struct heci_cb_private *priv_cb_pos)
{
DBG("completing write call back.\n");
DBG("completing read call back information= %lu\n",
}
}
/*
* _heci_cmpl_iamthif: process completed iamthif operation.
*
* @dev: Device object for our driver.
* @priv_cb_pos: callback block.
*/
struct heci_cb_private *priv_cb_pos)
{
dev->iamthif_stall_timer = 0;
DBG("pthi read completed.\n");
} else {
}
DBG("completing pthi call back.\n");
}
}
/*
* 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
{
#ifdef SUNOS
while (heci_bh_process_device(dev))
;
if (heci_bh_process_device(dev)) {
DBG("schedule work the heci_bh_handler.\n");
if (!rets) {
" failed error=%x\n", rets);
}
}
#else
#error "Unknown platform!"
#endif
}
static int
{
int bus_message_received = 0;
DBG("function called after ISR to handle the interrupt processing.\n");
/* initialize our complete list */
/* check if ME wants a reset */
DBG("FW not ready.\n");
return (0);
}
/* check if we need to start the dev */
DBG("we need to start the dev.\n");
return (0);
} else {
if (dev->reinit_tsk &&
"failed for reinit_tsk");
}
return (0);
}
} else {
DBG("enable interrupt FW not ready.\n");
return (0);
}
}
/* check slots avalable for reading */
DBG("slots =%08x extra_write_index =%08x.\n",
DBG("call heci_bh_read_handler.\n");
if (rets != 0)
goto end;
}
end:
DBG("end of bottom half function.\n");
/* acknowledge interrupt and disable interrupts */
DBG("schedule work the heci_bh_handler.\n");
isr_pending = 1;
} else {
}
DBG("received waiting bus message\n");
bus_message_received = 1;
}
if (bus_message_received) {
DBG("wake up dev->wait_recvd_msg\n");
bus_message_received = 0;
}
if ((complete_list.status != 0) ||
return (isr_pending);
}
DBG("completing call back.\n");
}
}
}
return (isr_pending);
}
/*
* 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.
*
* @return 0 on success, <0 on failure.
*/
static int
struct iamt_heci_device *dev,
{
int ret = 0;
if (!dev->rd_msg_hdr) {
(*slots)--;
}
DBG("corrupted message header.\n");
goto end;
}
DBG("list_for_each_entry_safe read host"
" client = %d, ME client = %d\n",
break;
}
DBG("corrupted message header\n");
goto end;
}
}
/* we can't read the message */
goto end;
}
/* decide where to read the message too */
DBG("call heci_bh_read_bus_message.\n");
DBG("end heci_bh_read_bus_message.\n");
DBG("call heci_bh_read_iamthif_message.\n");
if (ret != 0)
goto end;
} else {
DBG("call heci_bh_read_client_message.\n");
if (ret != 0)
goto end;
}
/* reset the number of slots and header */
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 */
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
*/
struct heci_msg_hdr *heci_hdr)
{
int i;
unsigned char *buffer;
/* read the message to our buffer */
case HOST_START_RES_CMD:
if (version_res->host_version_supported) {
} else {
}
DBG("host start response message received.\n");
break;
case CLIENT_CONNECT_RES_CMD:
(struct hbm_client_connect_response *)heci_msg;
DBG("client connect response message received.\n");
break;
(struct hbm_client_connect_response *)heci_msg;
DBG("client disconnect response message received.\n");
break;
case HECI_FLOW_CONTROL_CMD:
DBG("client flow control response message received.\n");
break;
ASSERT(0);
break;
}
for (i = 0; i < dev->num_heci_me_clients; i++) {
break;
}
}
break;
case HOST_ENUM_RES_CMD:
break;
case HOST_STOP_RES_CMD:
DBG("reseting because of FW stop response.\n");
break;
/* search for client */
(struct hbm_client_disconnect_request *)heci_msg;
break;
case ME_STOP_REQ_CMD:
/* prepare stop request */
(void) memset(h_stop_req, 0,
sizeof (struct hbm_host_stop_request));
h_stop_req->reserved[0] = 0;
break;
default:
ASSERT(0);
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
*
* @return 0 on success, <0 on failure.
*/
static int
struct iamt_heci_device *dev,
struct heci_msg_hdr *heci_hdr)
{
unsigned char *buffer;
if (!(heci_hdr->msg_complete))
return (0);
DBG("completed pthi read.\n ");
if (!dev->iamthif_current_cb)
return (-ENODEV);
if (!file_ext)
return (-ENODEV);
dev->iamthif_stall_timer = 0;
/* 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 ");
}
}
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
*
* @return !=0 if matches, 0 if no match.
*/
static int
struct heci_msg_hdr *heci_hdr)
{
}
/*
* 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
*
* @return 0 on success, <0 on failure.
*/
static int
struct iamt_heci_device *dev,
struct heci_msg_hdr *heci_hdr)
{
DBG("start client msg\n");
goto quit;
file_ext = (struct heci_file_private *)
buffer = (unsigned char *)
DBG("message overflow.\n");
return (-ENOMEM);
}
if (buffer) {
}
if (heci_hdr->msg_complete) {
DBG("completed read host client = %d,"
"ME client = %d, "
"data length = %lu\n",
DBG("priv_cb_pos->res_buffer - %s\n",
} else {
}
break;
}
}
quit:
DBG("message read\n");
if (!buffer) {
DBG("discarding message, header=%08x.\n",
}
return (0);
}
/*
* _heci_bh_iamthif_read: prepare to read iamthif data.
*
* @dev: Device object for our driver.
* @slots: free slots.
*
* @return 0, OK; otherwise, error.
*/
static int
{
+ sizeof (struct hbm_flow_control))) {
*slots -= (sizeof (struct heci_msg_hdr) +
DBG("iamthif flow control failed\n");
} else {
DBG("iamthif flow control success\n");
dev->iamthif_msg_buf_index = 0;
dev->iamthif_msg_buf_size = 0;
}
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.
*
* @return 0, OK; otherwise, error.
*/
static int
struct heci_cb_private *priv_cb_pos,
struct heci_file_private *file_ext,
struct io_heci_list *cmpl_list)
{
sizeof (struct hbm_client_disconnect_request))) {
*slots -= (sizeof (struct heci_msg_hdr) +
priv_cb_pos->information = 0;
return (-ECOMPLETE_MESSAGE);
} else {
priv_cb_pos->information = 0;
}
} 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.
*
* @return 0, OK; otherwise, error.
*/
static int
struct heci_cb_private *priv_cb_pos,
struct heci_file_private *file_ext,
struct io_heci_list *cmpl_list)
{
sizeof (struct hbm_flow_control))) {
*slots -= (sizeof (struct heci_msg_hdr) +
priv_cb_pos->information = 0;
return (-ENODEV);
} else {
}
} else {
/* return the cancel routine */
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.
*
* @return 0, OK; otherwise, error.
*/
static int
struct heci_cb_private *priv_cb_pos,
struct heci_file_private *file_ext,
struct io_heci_list *cmpl_list)
{
sizeof (struct hbm_client_connect_request))) {
*slots -= (sizeof (struct heci_msg_hdr) +
priv_cb_pos->information = 0;
return (-ENODEV);
} else {
}
} else {
/* return the cancel routine */
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.
*
* @return 0, OK; otherwise, error.
*/
static int
struct heci_cb_private *priv_cb_pos,
struct heci_file_private *file_ext,
struct io_heci_list *cmpl_list)
{
priv_cb_pos->information))) {
(priv_cb_pos->information));
DBG("priv_cb_pos->request_buffer.size =%d"
"heci_hdr->msg_complete= %d\n",
DBG("priv_cb_pos->information =%lu\n",
DBG("heci_hdr->length =%d\n",
*slots -= (sizeof (struct heci_msg_hdr) +
return (-ENODEV);
} else {
}
/* buffer is still empty */
heci_hdr->msg_complete = 0;
(*slots) -= (sizeof (struct heci_msg_hdr) +
(unsigned char *)
return (-ENODEV);
} else {
DBG("priv_cb_pos->request_buffer.size =%d"
" heci_hdr->msg_complete= %d\n",
DBG("priv_cb_pos->information =%lu\n",
}
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.
*
* @return 0, OK; otherwise, error.
*/
static int
struct heci_cb_private *priv_cb_pos,
struct heci_file_private *file_ext,
struct io_heci_list *cmpl_list)
{
dev->iamthif_msg_buf_index)) {
*slots -= (sizeof (struct heci_msg_hdr) +
(dev->iamthif_msg_buf +
return (-ENODEV);
} else {
/* save iamthif cb sent to pthi client */
}
/* buffer is still empty */
heci_hdr->msg_complete = 0;
*slots -= (sizeof (struct heci_msg_hdr) +
(dev->iamthif_msg_buf +
} else {
}
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.
*
* @return 0 on success, <0 on failure.
*/
static int
struct iamt_heci_device *dev,
{
int ret;
if (!host_buffer_is_empty(dev)) {
DBG("host buffer is not empty.\n");
return (0);
}
/* complete all waiting for write CB */
DBG("complete all waiting for write cb.\n");
file_ext = (struct heci_file_private *)
HECI_WRITE) &&
DBG("HECI WRITE COMPLETE\n");
}
DBG("check iamthif flow control.\n");
if (dev->iamthif_flow_control_pending) {
slots);
if (ret != 0)
return (ret);
}
}
}
}
}
return (0);
}
if (dev->extra_write_index != 0) {
if (!heci_write_message(dev,
DBG("heci_bh_handler: writing msg failed\n");
}
dev->extra_write_index = 0;
}
if (dev->wd_pending &&
if (!heci_send_wd(dev)) {
DBG("wd send failed.\n");
} else
dev->wd_pending = 0;
if (dev->wd_timeout != 0) {
*slots -= (sizeof (struct heci_msg_hdr) +
} else {
*slots -= (sizeof (struct heci_msg_hdr) +
dev->wd_due_counter = 0;
}
}
}
return (~ENODEV);
/* complete control write list CB */
/* complete control write list CB */
DBG("complete control write list cb.\n");
cb_list, struct heci_cb_private) {
file_ext = (struct heci_file_private *)
return (-ENODEV);
}
switch (priv_cb_pos->major_file_operations) {
case HECI_CLOSE:
/* send disconnect message */
if (ret != 0)
return (ret);
break;
case HECI_READ:
/* send flow control message */
if (ret != 0)
return (ret);
break;
case HECI_IOCTL:
/* connect message */
continue;
if (ret != 0)
return (ret);
break;
default:
ASSERT(0);
}
}
}
/* complete write list CB */
DBG("complete write list cb.\n");
cb_list, struct heci_cb_private) {
file_ext = (struct heci_file_private *)
DBG("No flow control"
" credentials for client"
" %d, not sending.\n",
continue;
}
if (ret != 0)
return (ret);
/* IAMTHIF IOCTL */
DBG("complete pthi write cb.\n");
DBG("No flow control"
" credentials for pthi"
" client %d.\n",
continue;
}
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
*
* @return 0 on success, <0 on failure.
*/
static int
struct hbm_client_connect_response *rs)
{
int ret = 0;
} else {
}
ret = 1;
}
return (ret);
}
/*
* heci_client_connect_response - connect response bh routine
*
* @dev: Device object for our driver
* @rs: connect response bus message
*/
static void
struct hbm_client_connect_response *rs)
{
/* if WD or iamthif client treat specially */
return;
cb_list, struct heci_cb_private) {
file_ext = (struct heci_file_private *)
return;
}
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
*/
struct hbm_client_connect_response *rs)
{
cb_list, struct heci_cb_private) {
file_ext = (struct heci_file_private *)
return;
}
DBG("list_for_each_entry_safe in ctrl_rd_list.\n");
}
file_ext->timer_count = 0;
break;
}
}
}
}
/*
* same_flow_addr - tell they have same address.
*
* @file: private data of the file object.
* @flow: flow control.
*
* @return !=0, same; 0,not.
*/
static int
struct hbm_flow_control *flow)
{
}
/*
* add_single_flow_creds - add single buffer credentials.
*
* @file: private data ot the file object.
* @flow: flow control.
*/
static void
struct hbm_flow_control *flow)
{
int i;
for (i = 0; i < dev->num_heci_me_clients; i++) {
DBG("recv flow ctrl msg ME %d (single).\n",
DBG("flow control credentials=%d.\n",
} else {
ASSERT(0); /* 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
struct hbm_flow_control *flow_control)
{
if (flow_control->host_addr == 0) {
/* single receive buffer */
} else {
/* normal connection */
DBG("list_for_each_entry_safe in file_list\n");
DBG("file_ext of host client %d ME client %d.\n",
DBG("flow ctrl msg for host %d ME %d.\n",
DBG("recv ctrl msg for host %d ME %d.\n",
DBG("flow control credentials=%d.\n",
break;
}
}
}
}
/*
* same_disconn_addr - tell they have same address
*
* @file: private data of the file object.
* @disconn: disconnection request.
*
* @return !=0, same; 0,not.
*/
static int
struct hbm_client_disconnect_request *disconn)
{
}
/*
* heci_client_disconnect_request - disconnect request bh routine
*
* @dev: Device object for our driver.
* @disconnect_req: disconnect request bus message.
*/
static void
{
link, struct heci_file_private) {
DBG("disconnect request host client %d ME client %d.\n",
file_pos->timer_count = 0;
dev->wd_due_counter = 0;
dev->wd_pending = 0;
dev->iamthif_timer = 0;
/* prepare disconnect response */
heci_hdr =
sizeof (struct hbm_client_connect_response);
(struct hbm_client_connect_response *)
disconnect_res->status = 0;
break;
}
}
}
/*
* heci_timer - timer function.
*
* @data: pointer to the device structure
*
* NOTE: This function is called by timer interrupt work
*/
void
{
DBG("send watchdog.\n");
return;
}
return;
}
/* Watchdog */
if (--dev->wd_due_counter == 0) {
if (dev->host_buffer_is_empty &&
dev->host_buffer_is_empty = 0;
if (!heci_send_wd(dev)) {
DBG("wd send failed.\n");
} else {
&dev->wd_file_ext);
}
if (dev->wd_timeout != 0)
else
dev->wd_due_counter = 0;
} else
}
}
if (dev->iamthif_stall_timer != 0) {
if (--dev->iamthif_stall_timer == 0) {
DBG("reseting because of hang to PTHI.\n");
dev->iamthif_msg_buf_size = 0;
dev->iamthif_msg_buf_index = 0;
dev->iamthif_canceled = 0;
dev->iamthif_timer = 0;
if (dev->iamthif_current_cb)
}
}
}