/*
* 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.
*
*/
#pragma ident "@(#)heci_main.c 1.7 08/03/07 SMI"
#include <sys/ddi_impldefs.h>
#include <sys/instance.h>
#include "heci_data_structures.h"
#include "heci.h"
#include "heci_interface.h"
#define MINOR_VERSION 0
#define QUICK_FIX_NUMBER 0
#define name(s) #s
/*
* heci driver strings
*/
#ifdef DEBUG
int heci_debug = 0;
#endif
/*
* Local Function Prototypes
*/
static struct heci_cb_private *find_read_list_entry(
struct iamt_heci_device *dev,
struct heci_file_private *file_ext);
struct heci_file_private *fe2);
heci_open, /* open */
heci_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
heci_read, /* read */
heci_write, /* write */
heci_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
heci_poll, /* poll */
ddi_prop_op, /* cb_prop op */
NULL, /* stream tab */
D_MP /* Driver Compatability Flags */
};
DEVO_REV, /* devo_rev */
0, /* refcnt */
heci_getinfo, /* get_dev_info */
nulldev, /* identify */
nulldev, /* probe */
heci_attach, /* attach */
heci_detach, /* detach */
nodev, /* reset */
&heci_cb_ops, /* Driver Ops */
NULL, /* power */
heci_quiesce /* devo_quiesce */
};
/*
* Module linkage information for the kernel
*/
&mod_driverops, /* Type of Module = Driver */
heci_driver_string, /* Driver Identifier string. */
&heci_dev_ops, /* Driver Ops. */
};
};
/*
* Module Initialization functions.
*/
int
_init(void)
{
int stat;
/* Allocate soft state */
return (stat);
}
return (stat);
}
int
{
}
int
_fini(void)
{
int stat;
return (stat);
return (stat);
}
/*
* heci_attach - Driver Attach Routine
*/
static int
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (status != DDI_SUCCESS)
return (DDI_FAILURE);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
DDI_PSEUDO, 0);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (status);
}
/*
* heci_probe - Device Initialization Routine
*/
static int
{
int err;
if (err != DDI_SUCCESS) {
" ddi_get_iblock_cookie() failed\n");
goto end;
}
/* initializes the heci device structure */
goto fini_heci_device;
}
if (err != DDI_SUCCESS) {
goto unmap_memory;
}
if (heci_hw_init(device)) {
goto release_irq;
}
(void) heci_initialize_clients(device);
goto release_hw;
}
if (device->wd_timeout)
DBG("heci driver initialization successful.\n");
return (0);
/* disable interrupts */
end:
return (err);
}
void
{
}
/*
* heci_remove - Device Removal Routine
*
* @pdev: PCI device information struct
*
* heci_remove is called by the PCI subsystem to alert the driver
* that it should release a PCI device.
*/
static int
{
int err;
switch (cmd) {
case DDI_SUSPEND:
if (err)
return (DDI_FAILURE);
else
return (DDI_SUCCESS);
case DDI_DETACH:
break;
default:
return (DDI_FAILURE);
}
dev->wd_timeout) {
dev->wd_timeout = 0;
dev->wd_due_counter = 0;
if (dev->host_buffer_is_empty &&
dev->host_buffer_is_empty = 0;
if (!heci_send_wd(dev)) {
DBG("send stop WD failed\n");
} else
dev->wd_pending = 0;
} else
err = 0;
}
DBG("stop wd failed to complete.\n");
} else {
DBG("stop wd complete.\n");
}
}
(void) heci_disconnect_host_client(dev,
&dev->iamthif_file_ext);
}
(void) heci_disconnect_host_client(dev,
&dev->wd_file_ext);
}
/* remove entry if already in list */
DBG("list del iamthif and wd file list.\n");
/* disable interrupts */
if (dev->reinit_tsk)
}
dev->num_heci_me_clients = 0;
return (DDI_SUCCESS);
}
static int
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
else
break;
case DDI_INFO_DEVT2INSTANCE:
break;
default:
error = DDI_FAILURE;
break;
}
return (error);
}
/*
* heci_clear_list - remove all callbacks associated with file
* from heci_cb_list
*
* @file: file information struct
* @heci_cb_list: callbacks list
*
* heci_clear_list is called to clear resources associated with file
* when application calls close function or Ctrl-C was pressed
*
* @return 1 if callback removed from the list, 0 otherwise
*/
static int
{
int rets = 0;
/* list all list member */
/* check if list member associated with a file */
/* remove member from the list */
/* check if cb equal to current iamthif cb */
/* send flow control to iamthif client */
if (!heci_send_flow_control(dev,
&dev->iamthif_file_ext)) {
DBG("sending flow control failed\n");
}
}
/* free all allocated buffers */
rets = 1;
}
}
return (rets);
}
/*
* heci_clear_lists - remove all callbacks associated with file
*
* @dev: device information struct
* @file: file information struct
*
* heci_clear_lists is called to clear resources associated with file
* when application calls close function or Ctrl-C was pressed
*
* @return 1 if callback removed from the list, 0 otherwise
*/
static int
{
int rets = 0;
/* remove callbacks associated with a file */
rets = 1;
rets = 1;
rets = 1;
rets = 1;
/* check if iamthif_current_cb not NULL */
/* check file and iamthif current cb association */
/* remove cb */
rets = 1;
}
}
return (rets);
}
/*
* heci_open - the open function
*/
static int
{
DBG("heci_open: enter...\n");
/*
* Make sure the open is for the right file type.
*/
return (EINVAL);
return (-ENODEV);
return (-ENOMEM);
return (-ENODEV);
}
return (-ENFILE);
}
dev->open_handle_count++;
DBG("current_host_client_id = %d\n",
DBG("dev->open_handle_count = %lu\n",
}
return (0);
}
/*
* heci_close - the close function
*/
static int
{
int rets = 0;
return (-ENODEV);
DBG("disconnecting client host client = %d, "
"ME client = %d\n",
}
DBG("remove client host client = %d, ME client = %d\n",
if (dev->open_handle_count > 0) {
dev->open_handle_count--;
}
/* free read cb */
/* Remove entry from read list */
}
} else {
if (dev->open_handle_count > 0)
dev->open_handle_count--;
DBG("pthi canceled iamthif state %d\n",
dev->iamthif_state);
DBG("run next pthi iamthif cb\n");
}
}
}
return (rets);
}
static struct heci_cb_private *
struct heci_file_private *file_ext)
{
DBG("remove read_list CB \n");
struct heci_cb_private) {
file_ext_list_temp = (struct heci_file_private *)
if ((file_ext_list_temp != NULL) &&
return (priv_cb_pos);
}
}
return (NULL);
}
/*
* heci_read - the read client message function.
*/
static int
{
int i;
int rets = 0;
return (-ENODEV);
return (-ENODEV);
}
if (!file_ext)
return (-ENODEV);
/* Do not allow to read watchdog client */
for (i = 0; i < dev->num_heci_me_clients; i++) {
if (memcmp(&heci_wd_guid,
sizeof (struct guid)) == 0) {
if (file_ext->me_client_id ==
return (-EBADF);
}
}
} else {
}
goto out;
}
goto copy_buffer;
rets = 0;
goto free;
} else if (
UIO_OFFSET(uio_p) > 0) {
/* Offset needs to be cleaned for contingous reads */
UIO_OFFSET(uio_p) = 0;
rets = 0;
goto out;
}
goto out;
}
goto free;
}
goto out;
}
}
if (!priv_cb) {
return (-ENODEV);
}
return (0);
}
/* now copy the data to user space */
DBG("priv_cb->response_buffer size - %d\n",
goto free;
}
goto free;
}
else
rets = 0;
free:
/* Remove entry from read list */
if (priv_cb_pos != NULL)
file_ext->read_pending = 0;
return (rets);
}
/*
* heci_write - the write function.
*/
static int
{
int rets = 0;
uint8_t i;
DBG("heci_write enter...\n");
return (-ENODEV);
return (-ENODEV);
}
if ((priv_write_cb != NULL) &&
UIO_OFFSET(uio_p) = 0;
}
}
/* free entry used in read */
UIO_OFFSET(uio_p) = 0;
if (priv_write_cb != NULL) {
file_ext->read_pending = 0;
}
file_ext->read_pending == 0)
UIO_OFFSET(uio_p) = 0;
if (!priv_write_cb)
return (-ENOMEM);
return (-ENOMEM);
}
if (err) {
goto fail;
}
if ((length == 4) &&
goto fail;
}
goto fail;
}
for (i = 0; i < dev->num_heci_me_clients; i++) {
break;
}
if ((i == dev->num_heci_me_clients) ||
goto fail;
} else if ((length >
(length == 0)) {
goto fail;
}
priv_write_cb->information = 0;
goto fail;
}
DBG("add PTHI cb to pthi cmd waiting list\n");
rets = 0; /* length; */
} else {
DBG("call pthi write\n");
if (rets != 0) {
DBG("pthi write failed with status = %d\n",
rets);
goto fail;
}
rets = 0; /* length; */
}
return (rets);
}
/* make sure information is zero before we start */
priv_write_cb->information = 0;
DBG("host client = %d, ME client = %d\n",
DBG("host client = %d, is not connected to ME client = %d",
goto unlock;
}
for (i = 0; i < dev->num_heci_me_clients; i++) {
break;
}
if (i == dev->num_heci_me_clients) {
goto unlock;
}
goto unlock;
}
dev->host_buffer_is_empty = 0;
sizeof (uint32_t)) - sizeof (struct heci_msg_hdr))) {
sizeof (uint32_t)) -
sizeof (struct heci_msg_hdr);
heci_hdr.msg_complete = 0;
} else {
}
DBG("call heci_write_message header=%08x.\n",
/* protect heci low level write */
priv_write_cb->information = 0;
return (rets);
}
if (heci_hdr.msg_complete) {
} else {
}
} else {
priv_write_cb->information = 0;
}
return (0);
fail:
return (rets);
}
/*
* heci_ioctl - the IOCTL function
*/
static int
{
int rets = 0;
/* in user space */
return (-ENODEV);
return (-ENODEV);
}
/* first copy from user all data needed */
DBG("first copy from user all data needed filled\n");
return (-EFAULT);
}
#ifdef _LP64
}
#endif
switch (cmd) {
case IOCTL_HECI_GET_VERSION:
DBG(": IOCTL_HECI_GET_VERSION\n");
break;
DBG(": IOCTL_HECI_CONNECT_CLIENT.\n");
break;
case IOCTL_HECI_WD:
DBG(": IOCTL_HECI_WD.\n");
break;
case IOCTL_HECI_BYPASS_WD:
DBG(": IOCTL_HECI_BYPASS_WD.\n");
break;
default:
break;
}
return (rets);
}
/*
* heci_poll - the poll function
*/
static int
{
return (-ENODEV);
return (-ENXIO);
}
DBG("heci_poll: run next pthi cb\n");
} else {
DBG("heci_poll: iamthif no event\n");
*reventsp = 0;
if (!anyyet)
}
} else {
DBG("heci_poll: file_extension poll event\n");
} else {
DBG("heci_poll: file_extension no event\n");
*reventsp = 0;
if (!anyyet)
}
}
return (0);
}
/*
* heci_fe_same_id - tell if file private data have same id
*
* @fe1: private data of 1. file object
* @fe2: private data of 2. file object
*
* @return !=0 - if ids are the same, 0 - if differ.
*/
struct heci_file_private *fe2)
{
}
/*
* Since the ME firmware won't reset itself during OS reboot, it's not enough
* to only disable interrupts in quiesce(), here we do a full hand-shake
* with the firmware.
*/
static int
{
dev->wd_timeout) {
dev->wd_timeout = 0;
dev->wd_due_counter = 0;
if (!heci_send_wd(dev)) {
DBG("send stop WD failed\n");
}
}
/* disable interrupts */
return (DDI_SUCCESS);
}
static int
{
int err = 0;
if (device->reinit_tsk)
/* Stop watchdog if exists */
device->wd_timeout) {
device->wd_timeout = 0;
device->wd_due_counter = 0;
if (device->host_buffer_is_empty &&
device->host_buffer_is_empty = 0;
if (!heci_send_wd(device)) {
DBG("send stop WD failed\n");
}
else
device->wd_pending = 0;
} else {
}
err = 0;
}
DBG("stop wd failed to complete.\n");
} else {
err = 0;
}
}
/* Set new heci state */
heci_reset(device, 0);
}
/* Here interrupts are already disabled by heci_reset() */
return (err);
}
static void
{
/* Start watchdog if stopped in suspend */
if (g_sus_wd_timeout != 0) {
if (device->wd_timeout)
g_sus_wd_timeout = 0;
}
}