i2o_scsi.c revision 91e1058474884b4b6f1d3665a96ddeb9a30cd489
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* I2O SCSI HBA OSM
*
* I2O Scsi Host Bus Adapter Operating System Module (OSM)
* conforms to I2O and converts SCSI pkt information send by
* the target driver through SCSA to I2O message format and send it
* down to the IOP. This driver utilized the I2O messaging framework,
* i2o_msg. Currently only support on x86.
*
*/
/*
* debugging code
*/
#ifdef DEBUG
#define I2OHBA_DEBUG
#endif
#ifdef I2OHBA_DEBUG
int i2ohbadebugflag = 0;
int ddi_dma_alloc_hdl = 0;
int ddi_dma_alloc_mem = 0;
int ddi_dma_bind_hdl = 0;
int ddi_dma_unbind = 0;
int ddi_dma_free_mem = 0;
int ddi_dma_bind_free = 0;
int ddi_dma_bufalloc = 0;
int ddi_dma_buf_bind = 0;
int ddi_dma_buf_unbind = 0;
int ddi_dma_buf_free_hdl = 0;
#else
#endif
#include "i2o_scsi_var.h"
#include "i2o_scsi_util.h"
#include "i2o_scsi_cmd.h"
/*
* dev_ops functions prototypes
*/
/*
* Function prototypes
*
* SCSA functions exported by means of the transport table
*/
int whom);
/*
* i2ohba's complete function for sending the SCSI pkt.
*/
/*
* Internal Functions
*/
/* tid map to scsi convertion function */
/* capability/prop functions */
int value);
/* dma engine funcitons */
/* timer and recovery functions */
static void i2ohba_i_watch(void *);
struct i2ohba_cmd *cmd);
/* command processing functions */
struct i2ohba_cmd *sp);
/*PRINTFLIKE3*/
/* utility parameter functions */
char flag);
int action);
/*
* mutex for protecting variables shared by all instances of the driver
*/
static kmutex_t i2ohba_log_mutex;
/*
* Local static data
*/
static void *i2ohba_state = NULL;
static int i2ohba_scsi_reset_delay = 3000;
/*
* DMA Attribute for data buffers
*/
static ddi_dma_attr_t i2ohba_dma_attr = {
DMA_ATTR_VERSION, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0xffffffffull, /* dma_attr_addr_hi */
0x00ffffffull, /* dma_attr_count_max */
1, /* dma_attr_align */
1, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffull, /* dma_attr_maxxfer */
0x00ffffffull, /* dma_attr_seg */
I2OHBA_CMD_NSEGS, /* dma_attr_sgllen */
512, /* dma_attr_granular */
0 /* dma_attr_flags */
};
/*
* DMA attributes for SGL buffer
*/
static ddi_dma_attr_t i2ohba_dmasgl_attr = {
DMA_ATTR_VERSION, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0xffffffffull, /* dma_attr_addr_hi */
0x00ffffffull, /* dma_attr_count_max */
1, /* dma_attr_align */
1, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xffffffffull, /* dma_attr_maxxfer */
0x00ffffffull, /* dma_attr_seg */
0x1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
/*
* Hotplug support
* Leaf ops (hotplug controls for target devices)
* XXXLWXXX currently doesn't support any hotpluging
*/
static struct cb_ops i2ohba_cb_ops = {
nodev, /* open */
nodev, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
nodev, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* prop_op */
NULL,
};
/*
* autoconfiguration routines.
*/
static struct dev_ops i2ohba_ops = {
DEVO_REV, /* rev, */
0, /* refcnt */
i2ohba_info, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
i2ohba_attach, /* attach */
i2ohba_detach, /* detach */
nodev, /* reset */
&i2ohba_cb_ops, /* driver operations */
NULL, /* bus ops */
NULL /* power ops */
};
char _depends_on[] = "misc/scsi misc/i2o_msg";
&mod_driverops, /* Type of module */
"I2O SCSI HBA OSM version %I%", /* module name */
&i2ohba_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
(void *)&modldrv,
};
int
_init(void)
{
int ret;
if (ret != 0)
return (ret);
return (ret);
}
}
return (ret);
}
int
{
}
int
_fini(void)
{
int ret;
return (ret);
return (ret);
}
/*
* Given the device number return the devinfo pointer
* from the scsi_device structure.
*/
/*ARGSUSED*/
static int
{
return (DDI_FAILURE);
}
/*
* Attach an instance of an i2o hba OSM module. Allocate data structures,
* initialize the OSM and send commands to IOP to bring I2O on line.
*/
/*ARGSUSED*/
static int
{
char prop_str[32];
int freecount = 0;
int ishdlalloc = 0;
int instance;
switch (cmd) {
case DDI_ATTACH:
/* currently for x86 PCI, we'll use little endian */
break;
/* XXXLWXXX will work on it more */
case DDI_RESUME:
case DDI_PM_RESUME:
default:
"cmd != DDI_ATTACH", instance);
return (DDI_FAILURE);
}
/*
* I2O comes in at intr level 5
*/
if (ddi_intr_hilevel(dip, 0) != 0) {
"interrupt not supported", instance);
return (DDI_FAILURE);
}
/*
* Allocate i2ohba data structure.
*/
" soft state", instance);
return (DDI_FAILURE);
}
instance);
return (DDI_FAILURE);
}
/*
* save the dip info
*/
/*
* register OSM with message layer
*/
!= DDI_SUCCESS) {
"with IOP", instance);
goto cleanup;
}
/*
* get the LCT from iop
*/
"info from IOP", instance);
goto cleanup;
}
/*
* first get the size from IOP, then
* DMA Allocate it.
*/
if (lct_size) {
if (!ishdlalloc) {
goto cleanup;
}
ishdlalloc = 1;
#ifdef I2OHBA_DEBUG
#endif
goto cleanup;
#ifdef I2OHBA_DEBUG
#endif
freecount = 1;
} else {
instance);
goto cleanup;
}
/*
* with the right size buffer, now get the table
*/
"info failed", instance);
goto cleanup;
}
/*
* partial transfer
*/
if (lct_real_size > lct_size) {
#ifdef I2OHBA_DEBUG
#endif
goto retry;
}
/*
* Set up TID->SCSI info map, query all unclaimed SCSI devices
*/
"info map failed", instance);
goto cleanup;
}
/* free dma, may be XXLWXX we can keep it for later LCT update */
freecount = 0;
#ifdef I2OHBA_DEBUG
#endif
ishdlalloc = 0;
#ifdef I2OHBA_DEBUG
#endif
/*
* Allocate a transport structure
*/
/*
* Attach this instance of the hba
*/
0) != DDI_SUCCESS) {
goto cleanup;
}
/*
* find scsi host id property
*/
"scsi-initiator-id", -1);
if (id != scsi_host_id &&
} else {
}
/*
* property: look up the scsi-options property
*/
/*
* property: if target<n>-scsi-options property exists, use it;
* otherwise use the i2o_scsi_options
*/
for (i = 0; i < N_I2OHBA_TARGETS_WIDE; i++) {
if (i2ohba->i2ohba_target_scsi_option[i] !=
"?i2ohba:target%x-scsi-options=0x%x",
i, i2ohba->i2ohba_target_scsi_option[i]);
}
}
"?i2ohba_scsi-reset-delay=%d",
}
/*
* set up watchdog for this i2o
*/
/*
* initialized I2OHBA request mutex and reset mutex
*/
/*
* Initialize the default Target Capabilites and Sync Rates
*/
(void) i2ohba_i_initcap(i2ohba);
/*
* if (i2ohba_i_reset_interface(i2ohba, I2OHBA_FORCE_BUS_RESET)) {
* goto cleanup;
* }
*/
i2ohba->i2ohba_throttle = 0;
return (DDI_SUCCESS);
if (tid2scsi) {
/*
* if tid_to_scsi has succeeded, then at least
* we have the HBA node claimed.
*/
for (i = 0; i < N_I2OHBA_TARGETS_WIDE; i++) {
(void) i2ohba_utilclaim_msg(i2ohba,
}
}
if (freecount) {
}
if (ishdlalloc) {
}
if (i2ohba->i2ohba_timeout_id != 0) {
i2ohba->i2ohba_timeout_id = 0;
}
if (tran) {
}
if (i2ohba->i2ohba_iophdl)
return (DDI_FAILURE);
}
/*
* Function name: i2ohba_i_tid_to_scsi
*
* Return Values: -1 Failed
* 0 Success
*
* Description : Take in the LCT (logical configuration table), and
* parse it through to find the appropriated SCSI target
* devices that belongs to this HBA. Then send a
* I2O_UTIL_PARAM_GET message to the target device to
* to get its parameters, ie SCSI ID and build a
* tid-to-scsi.id map.
*/
/*ARGSUSED*/
static int
{
/*
* translating dev_addr field in the dev_info struct to TID
*/
"i2o-device-id", -1);
/*
*/
if (!tablesize) {
"tablesize");
return (rval);
}
/*
* lct table size in WORDS
*/
sizeof (i2o_lct_entry_t);
/* scan through the table */
/* unclaim devices */
if (userid == 0xFFF) {
int class;
/*
* search for SCSI Peripheral with Parent ID
* as the same as hba's TID and unclaimed devices
*/
(class == I2O_CLASS_SCSI_PERIPHERAL)) {
/* claim it */
I2O_UTIL_CLAIM)) {
"?i2ohba_i_tid_to_scsi: "
"i2ohba_utilclaim_msg failed");
} else {
"?utilclaim:"
"Tid: 0x%x, SCSI_PERIPHERAL", tid);
/* claim succeed */
entries++;
}
}
/*
* find our own bus adapter port and
* claim it as well
*/
(class == I2O_CLASS_BUS_ADAPTER_PORT)) {
I2O_UTIL_CLAIM)) {
"?i2ohba_i_tid_to_scsi: "
"i2ohba_utilclaim_msg failed");
/*
* it is pointless to continue here
* because if we can't claim the bus port
* then we can't do anything except for
* sending utilgetparm and busreset msgs
*/
break;
} else {
"?utilclaim:"
"Tid: 0x%x, BUS_ADAPTER_PORT", tid);
rval = 0;
}
}
} /* userid == 0xFFF */
} /* for loop */
/*
* initialized all utilparam message's mutex's
*/
for (i = 0; i < N_I2OHBA_TARGETS_WIDE; i++) {
MUTEX_DRIVER, NULL);
}
/*
* Now allocated parameter for each qualified peripheral
*/
for (i = 0; i < entries; i++) {
/*
* call i2ohba_utilparamget_msg to get
* parameters
*/
if (rval) {
" i2ohba_utilparamget_msg failed");
}
} /* for loop */
if (i > 0)
rval = 0;
return (rval);
}
/*
* Function name: i2ohba_utilcliam_msg()
*
* Return Values: 0 - success
* -1 - fail to claim the device
*
* Description : send a utilclaim or utilclaim_release msg
* to the device
*
* This function is called from i2ohba_i_tid_to_scsi()
*/
static int
{
int rval = -1;
struct i2ohba_util *sp;
"i2o_msg_alloc failed");
return (rval);
}
/*
* construct a i2o_hba_bus_reset_message
*/
sizeof (i2o_util_claim_message_t) >> 2);
/*
* allocating synchronized status buffer
*/
/*
* initialized a mutex and cond variable to
* send message to IOP, and wait for it to signal back
*/
/*
* process reply message
*/
case I2O_REPLY_STATUS_SUCCESS:
rval = 0;
break;
default:
/*
* Failed the reset for now,
* we can also parse the AdapterStatus
* and retry if needed
*/
break;
}
if (sp)
return (rval);
}
/*
* i2ohba_utilmsg_comp
* passed the status back to the caller to decided
* what to do with the error code.
*/
/*ARGSUSED*/
static void
{
struct i2ohba_util *sp;
switch (type) {
/*
* Bus Reset
*/
case I2O_HBA_BUS_RESET:
if (adptrstatus != I2O_HBA_DSC_BUS_RESET)
else
break;
/*
* Utility Class Function
*/
case I2O_UTIL_PARAMS_GET:
case I2O_UTIL_PARAMS_SET:
case I2O_UTIL_CLAIM:
case I2O_UTIL_CLAIM_RELEASE:
/*
* Adapter Class Function
*/
case I2O_HBA_ADAPTER_RESET:
case I2O_SCSI_DEVICE_RESET:
break;
default:
return;
}
}
/*ARGSUSED*/
static int
{
int i;
switch (cmd) {
case DDI_DETACH:
{
return (DDI_FAILURE);
if (!i2ohba) {
return (DDI_FAILURE);
}
if (i2ohba->i2ohba_iophdl)
/*
* deallocate reset notify callback list
*/
if (i2ohba->i2ohba_timeout_id != 0) {
}
/*
* remove device MT locks
*/
for (i = 0; i < N_I2OHBA_TARGETS_WIDE; i++) {
}
/*
* remove properties created druing attach()
*/
/*
* Delete the DMA limits, transport vectors and remove the
* device links to the scsi_transport layer.
* -- ddi_set_driver_private(dip, NULL)
*/
(void) scsi_hba_detach(dip);
/*
* Free the scsi_transport structure for this device.
*/
return (DDI_SUCCESS);
}
/* XXXLWXXX */
case DDI_SUSPEND:
case DDI_PM_SUSPEND:
default:
return (DDI_FAILURE);
}
}
/*
* Function name : i2ohba_tran_tgt_init
*
* Return Values : DDI_SUCCESS if target supported, DDI_FAILURE otherwise
*
*/
/*ARGSUSED*/
static int
{
}
/*
* Function name : i2ohba_i_initcap
*
* Return Values : NONE
* Description : Initializes the default target capabilites and
* Sync Rates.
*
* Context : Called from the user thread through attach.
*
*/
static void
{
int i, option;
for (i = 0; i < N_I2OHBA_TARGETS_WIDE; i++) {
cap = 0;
synch = 0;
option = 0;
offset = 0;
continue;
/*
* Check for connect/disconnect
*/
} else {
option &= ~SCSI_OPTIONS_DR;
}
/*
* Check for Wide data
*/
cap |= I2OHBA_CAP_WIDE;
} else {
option &= ~SCSI_OPTIONS_WIDE;
}
/*
* Check for synchronization
*/
cap |= I2OHBA_CAP_SYNC;
} else {
option &= ~SCSI_OPTIONS_SYNC;
}
/*
* Check for tag queuing capability
*/
cap |= I2OHBA_CAP_TAG;
} else {
option &= ~SCSI_OPTIONS_TAG;
}
}
}
/*
* Function name : i2ohba_scsi_getcap()
*
* Return Values : current value of capability, if defined
* -1 if capability is not defined
*
* Description : returns current capability value
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
static int
{
int rval = 0;
/*
* We don't allow inquiring about capabilities for other targets
*/
return (-1);
}
switch (scsi_hba_lookup_capstr(cap)) {
case SCSI_CAP_GEOMETRY:
{ /* left this code from adp driver */
uint32_t total_sectors, c, h, s, t;
c = 1024L;
s = 62L;
t = c * s;
h = total_sectors / t;
if (total_sectors % t) {
h ++;
t = c * h;
s = total_sectors / t;
if (total_sectors % t) {
s++;
t = h * s;
c = total_sectors / t;
}
}
if (c == 0) rval = 1;
rval = ((h << 16) | s);
break;
}
case SCSI_CAP_DMA_MAX:
break;
case SCSI_CAP_MSG_OUT:
rval = 1;
break;
case SCSI_CAP_DISCONNECT:
SCSI_OPTIONS_DR) == 0) {
break;
== 0) {
break;
}
rval = 1;
break;
case SCSI_CAP_SYNCHRONOUS:
SCSI_OPTIONS_SYNC) == 0) {
break;
break;
}
rval = 1;
break;
case SCSI_CAP_WIDE_XFER:
SCSI_OPTIONS_WIDE) == 0) {
break;
break;
}
rval = 1;
break;
case SCSI_CAP_TAGGED_QING:
SCSI_OPTIONS_DR) == 0 ||
SCSI_OPTIONS_TAG) == 0) {
break;
break;
}
rval = 1;
break;
case SCSI_CAP_UNTAGGED_QING:
rval = 0;
break;
case SCSI_CAP_PARITY:
rval = 1;
}
break;
case SCSI_CAP_INITIATOR_ID:
break;
case SCSI_CAP_ARQ:
rval = 1;
break;
case SCSI_CAP_LINKED_CMDS:
break;
rval = 1;
break;
default:
rval = -1;
break;
}
return (rval);
}
/*
* Function name : i2ohba_scsi_setcap()
*
* Return Values : 1 - capability exists and can be set to new value
* 0 - capability could not be set to new value
* -1 - no such capability
*
* Description : sets a capability for a target
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
static int
{
int update = 0;
int sync = 0;
int rval = 0;
/*
* We don't allow setting capabilities for other targets
* and the targets that are not in this OSM's control
*/
return (-1);
}
switch (scsi_hba_lookup_capstr(cap)) {
case SCSI_CAP_DMA_MAX:
case SCSI_CAP_MSG_OUT:
case SCSI_CAP_PARITY:
case SCSI_CAP_UNTAGGED_QING:
case SCSI_CAP_LINKED_CMDS:
case SCSI_CAP_GEOMETRY:
/*
* None of these are settable via
* the capability interface.
*/
break;
case SCSI_CAP_SECTOR_SIZE:
if (value) {
rval = 1;
}
break;
case SCSI_CAP_TOTAL_SECTORS:
if (value) {
rval = 1;
}
break;
case SCSI_CAP_ARQ:
if (value) {
} else {
}
rval = 1;
break;
/* disconnect/reconncet either the target supports or doesn't */
case SCSI_CAP_DISCONNECT:
SCSI_OPTIONS_DR) == 0) {
break;
} else {
if (value) {
I2OHBA_CAP_DISCONNECT) == 0) {
I2O_SCSI_ENABLE_DISCONNECT) != 0)
break;
}
} else {
I2OHBA_CAP_DISCONNECT) == 1) {
I2O_SCSI_DISABLE_DISCONNECT) != 0)
break;
}
}
}
rval = 1;
break;
/* these can be set through UtilParamSet message */
case SCSI_CAP_SYNCHRONOUS:
SCSI_OPTIONS_SYNC) == 0) {
break;
} else {
if (value) {
I2OHBA_CAP_SYNC) == 0) {
!= 0)
break;
/* set sync speed to Max */
0xFFFF) != 0)
break;
/* read the set value */
tgt, SYNC_UTILPARAM) != 0)
break;
sync++;
update++;
}
} else {
I2OHBA_CAP_SYNC) == 1) {
!= 0)
break;
update++;
}
}
}
rval = 1;
break;
case SCSI_CAP_TAGGED_QING:
SCSI_OPTIONS_DR) == 0 ||
SCSI_OPTIONS_TAG) == 0) {
break;
} else {
if (value) {
I2OHBA_CAP_TAG) == 0) {
0) != 0)
break;
update++;
}
} else {
I2OHBA_CAP_TAG) == 1) {
(uint16_t)1) != 0)
break;
update++;
}
}
}
rval = 1;
break;
case SCSI_CAP_WIDE_XFER:
SCSI_OPTIONS_WIDE) == 0) {
break;
} else {
if (value) {
I2OHBA_CAP_WIDE) == 0) {
I2O_SCSI_DATA_WIDTH_16) != 0)
break;
update++;
}
} else {
I2OHBA_CAP_WIDE) == 1) {
I2O_SCSI_DATA_WIDTH_8) != 0)
break;
update++;
}
}
}
rval = 1;
break;
case SCSI_CAP_INITIATOR_ID:
if (value < N_I2OHBA_TARGETS_WIDE) {
/*
* set Initiator SCSI ID
*/
value) == 0) {
rval = 1;
}
}
}
break;
default:
rval = -1;
break;
}
/*
* now set flag so latter in i2ohba_i_watch(),
* we can set prop_update
*/
if (sync)
}
return (rval);
}
/*
* Function name : i2ohba_i_updatesync()
*
* Return Values : -1 failed
* 0 success
*
* and TQ.
*
* Context : Can be called from different kernel process threads.
* Can not be called by interrupt thread
*/
static void
{
return;
}
}
/*
* Function name : i2ohba_i_update_props()
*
* wide, and TQ properties
* If offset is 0 then asynchronous mode is assumed and the
* property is removed, if it existed.
*
* Context : Can be called from different kernel process threads.
* Can not be called by interrupt thread
*/
static void
{
static char property[32];
int xfer_speed = 0;
int wide_enabled;
int tq_enabled;
if (wide_enabled) {
/* double xfer speed if wide has been enabled */
} else {
xfer_speed = (int)(synch);
}
}
}
/*
* Update a property's value
*/
static void
{
" can't update %s property to 0x%x",
}
}
/*
* Function name : i2ohba_scsi_init_pkt
*
* Return Values : pointer to scsi_pkt, or NULL
* Description : Called by kernel on behalf of a target driver
* calling scsi_init_pkt(9F).
* Refer to tran_init_pkt(9E) man page
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
static struct scsi_pkt *
{
struct i2ohba_cmd *sp;
/*
* First step of i2ohba_scsi_init_pkt: pkt allocation
*/
arg);
return (NULL);
}
/*
* Initialize the new pkt - we redundantly initialize
* all the fields for illustrative purposes.
*/
sp->cmd_dmacount = 0;
sp->cmd_cookie = 0;
pkt->pkt_statistics = 0;
pkt->pkt_reason = 0;
} else {
}
/*
* Second step of i2ohba_scsi_init_pkt: dma allocation/move
*/
if (new_pkt) {
}
}
} else {
}
}
}
return (pkt);
}
/*
* Function name : i2ohba_i_dma_alloc
*
* Return Values : 0 if successful, -1 if failure
* Description : allocate DMA resources
*
* Context : Can only be called from i2ohba_scsi_init_pkt()
*/
static int
{
int dma_flags;
int i;
} else {
}
if (flags & PKT_CONSISTENT) {
}
if (flags & PKT_DMA_PARTIAL) {
}
!= DDI_SUCCESS) {
switch (i) {
case DDI_DMA_BADATTR:
return (-1);
case DDI_DMA_NORESOURCES:
return (-1);
default:
"?ddi_dma_alloc_handle:"
" 0x%x impossible", i);
/*NOTREACHED*/
}
}
#ifdef I2OHBA_DEBUG
#endif
#ifdef I2OHBA_DEBUG
#endif
sp->cmd_ncookies));
switch (i) {
case DDI_DMA_PARTIAL_MAP:
DDI_FAILURE) {
"?ddi_dma_numwin() failed");
/*NOTREACHED*/
}
DDI_FAILURE) {
"?ddi_dma_getwin() failed");
/*NOTREACHED*/
}
goto get_dma_cookies;
case DDI_DMA_MAPPED:
sp->cmd_dma_len = 0;
sp->cmd_dma_offset = 0;
i = 0;
sp->cmd_dmacount = 0;
for (;;) {
break;
&sp->cmd_dmacookies[i]);
}
sp->cmd_cookie = i;
sp->cmd_cookiecnt = i;
" resid is %lx\n",
return (0);
case DDI_DMA_NORESOURCES:
break;
case DDI_DMA_NOMAPPING:
break;
case DDI_DMA_TOOBIG:
break;
case DDI_DMA_INUSE:
" DDI_DMA_INUSE impossible");
/*NOTREACHED*/
default:
" 0x%x impossible", i);
/*NOTREACHED*/
}
return (-1);
}
/*
* Function name : i2ohba_i_dma_move
*
* Return Values : 0 if successful, -1 if failure
* Description : move DMA resources to next DMA window
*
* Context : Can only be called from i2ohba_scsi_init_pkt()
*/
/*ARGSUSED*/
static int
{
int i;
/*
* If there are no more cookies remaining in this window,
* must move to the next window first.
*/
/*
* 1217340: cmdk reuses pkts incorrectly
*/
return (0);
/*
* At last window, cannot move
*/
return (-1);
return (-1);
sp->cmd_cookie = 0;
} else {
/*
* Still more cookies in this window - get the next one
*/
}
/*
* Get remaining cookies in this window, up to our maximum
*/
i = 0;
for (;;) {
sp->cmd_cookie++;
break;
}
sp->cmd_cookiecnt = i;
return (0);
}
/*
* Function name : i2ohba_scsi_destroy_pkt
*
* Return Values : none
* Description : Called by kernel on behalf of a target driver
* calling scsi_destroy_pkt(9F).
* Refer to tran_destroy_pkt(9E) man page
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
static void
{
/*
* i2o_scsi_dmafree inline to make things faster
*/
/*
* Free the mapping
*/
!= DDI_SUCCESS) {
"?i2ohba_scsi_destroy_pkt: "
"ddi_dma_unbind_handle() for dataseg"
" failed");
/*NOTREACHED*/
}
#ifdef I2OHBA_DEBUG
#endif
#ifdef I2OHBA_DEBUG
#endif
}
}
/*
* Free the pkt
*/
}
/*
* Function name : i2ohba_scsi_dmafree()
*
* Return Values : none
* Description : free dvma resources
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
/*ARGSUSED*/
static void
{
/*
* Free the mapping.
*/
!= DDI_SUCCESS) {
"?i2ohba_scsi_dmafree: "
"ddi_dma_unbind_handle() for dataseg"
" failed");
/*NOTREACHED*/
}
#ifdef I2OHBA_DEBUG
#endif
#ifdef I2OHBA_DEBUG
#endif
}
}
}
/*
* Function name: i2ohba_scsi_sync_pkt()
*
* Return Values: none
* Description : sync dma
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
/*ARGSUSED*/
static void
{
int i;
if (i != DDI_SUCCESS) {
"?i2ohba_scsi_sync_pkt: sync pkt failed");
}
}
}
}
/*
* routine for reset notification setup, to register or cancel.
*/
static int
{
}
/*
* Function name : i2ohba_scsi_start()
*
* Return Values : TRAN_FATAL_ERROR - i2o has been shutdown
* TRAN_BUSY - request queue is full
* TRAN_ACCEPT - pkt has been submitted to i2o
*
* Description : init pkt, start the request
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
static int
{
int rval = TRAN_ACCEPT;
/*
* if we have a shutdown, return packet
*/
if (i2ohba->i2ohba_shutdown) {
return (TRAN_FATAL_ERROR);
}
if (i2ohba->i2ohba_throttle) {
return (TRAN_BUSY);
}
/*
* if the target id is not in our map, don't bother composing
* the message, return fail
*/
return (TRAN_FATAL_ERROR);
} else {
}
i2ohba->i2ohba_counter++;
i2ohba->i2ohba_counter--;
return (TRAN_BUSY);
}
/*
* Set up request in i2ohba_reqhead area so it is ready to
* go once we have the request mutex,
* The reason we don't allocate the msg buffer in scsi_init_pkt
* is that the msg buffer is a resource from the FIFO of the IOP
* we don't want to give that ptr around
*/
" i2o_msg_alloc failed");
pkt->pkt_reason = 0;
i2ohba->i2ohba_counter--;
return (TRAN_BUSY);
}
/*
* Constructed a scsi_scb_execute_message
*/
/* StdMessageFrame */
msgsize = ((sizeof (i2o_scsi_scb_execute_message_t) -
sizeof (i2o_sg_element_t) +
sizeof (i2o_sge_chain_element_t) +
sizeof (i2o_sge_ignore_element_t)) >> 2);
} else {
msgsize = ((sizeof (i2o_scsi_scb_execute_message_t) -
sizeof (i2o_sg_element_t)) >> 2);
}
/*
* To get actual allocation msg size
*/
/* TransactionContext */
/* Set up CDB in the request */
} else {
}
#ifdef I2OHBA_DEBUG
sp->cmd_cdblen,
} else {
}
#endif
/* Tag queuing */
/* DISCONNECT enable by default especially with TAGQ */
else
} else {
}
/*
* All msg are using chain pointer.
* Setup dma transfers data segments.
*/
sizeof (i2o_sge_chain_element_t));
}
/* size of the next SGL chain elements list */
/* flags */
/*
* instead of allocating a chuck of dma
* address here like isp driver, the
* trade of speed vs managing the free
* memory list. I don't want this
* to become kmem_alloc functions.
*/
!= DDI_SUCCESS) {
"cannot allocate SGL's chain buffer handle");
goto cleanup;
}
bound++;
#ifdef I2OHBA_DEBUG
#endif
"cannot allocate SGL's chain buffer memory");
goto cleanup;
}
bound++;
#ifdef I2OHBA_DEBUG
#endif
/*
* we passed in the sgl real length in transaction
* context because we need it to free the dma,
* but we set the chain element header with sglen
*/
/* fill in the SGL chain headers */
sp->cmd_xfercount = 0;
sp->cmd_xfercount +=
}
sp->cmd_cookie));
sp->cmd_xfercount));
/* mark the last chain pointer header as the last one */
cpsgl--;
"cannot bind SGL's bind handle");
goto cleanup;
}
#ifdef I2OHBA_DEBUG
#endif
" cmd_xfercount=%lx\n",
} else {
}
/*
* calculate deadline from pkt_time
* Instead of multiplying by 100 (ie. HZ), we multiply by 128 so
* we can shift and at the same time have a 28% grace period
* we ignore the rare case of pkt_time == 0 and deal with it
* in i2ohba_i_watch()
*/
cur_lbolt = ddi_get_lbolt();
/*
* Start the cmd. If NO_INTR, must wait for cmd reply/completion.
*/
/*
* need a list (or some sort of queue) so that
* when we need to flash the queue, ie: SCSI_BUS_RESET
* we'll have a way to do it (link list)
*/
if (val != DDI_SUCCESS)
} else {
/* poll command */
if (val == DDI_SUCCESS) {
} else {
}
}
return (rval);
if (sp) {
if (bound) {
#ifdef I2OHBA_DEBUG
#endif
if (bound > 1) {
#ifdef I2OHBA_DEBUG
#endif
}
#ifdef I2OHBA_DEBUG
#endif
}
i2ohba->i2ohba_counter--;
pkt->pkt_reason = 0;
}
/* return MFA to IOP */
if (req) {
}
return (TRAN_BUSY);
}
/*
* Function name : i2ohba_i_req_insert()
*
* Return Values : void
*
* Usage : called by i2ohba_scsi_start().
*
* Description : Insert the i2ohba_cmd into the double
* link list in FIFO fashion.
*
* Context : called by SCSA frame work.
*/
static void
{
} else {
}
}
/*
* Function name : i2ohba_i_req_remove()
*
* Return Values : void
*
* Usage : called by i2ohba_callback().
* called by i2ohba_scsi_start() if i2o_msg_send()
* failed.
* Description : remove a i2ohba_cmd from the double link list
*
* Context : called by SCSA frame work.
*/
static void
{
struct i2ohba_cmd *tmp;
else
else
break;
}
}
}
/*
* Function name : i2ohba_callback()
*
* Return Values : None
*
* Context: : called by interrupt thread.
*/
void
{
struct i2ohba_cmd *sp;
if (sp->cmd_dmahandle) {
}
/*
* First filter the reqstatus
* check for any special errors
*/
switch (reqstatus) {
/*
* With paritial transfer, we want to make
* sure it is an USCSI cmd
*/
return;
case I2O_REPLY_STATUS_SUCCESS:
/*
* With successful completetion, only the ReqStatus
* field is set to reflect sucessful completion, the
* DetailedStatusCode is set to zero, the TransferCount
* field indicates the actual amount of data transferred.
* There is no StatusData.
*/
return;
/* XXLWXX Error code handling */
/*
* If message has failed due to aborted by hosts,
* error in execution, due to system command or reconfig.
* failed the request.
*/
default:
#ifdef I2OHBA_DEBUG
"?i2ohba_callback: Reply Failed"));
#endif
break;
}
}
/*
* Function name : i2ohba_i_pkt_complete()
*
* Return Values : none
*
* Description :
* callback into target driver
* argument is a NULL-terminated list of packets
* copy over stuff from response packet
*
* Context : Can be called by interrupt thread.
*/
static void
{
DDI_SUCCESS) {
"ddi_dma_unbind_handle() for sglbuf failed");
/*NOTREACHED*/
}
#ifdef I2OHBA_DEBUG
#endif
#ifdef I2OHBA_DEBUG
#endif
#ifdef I2OHBA_DEBUG
#endif
}
/* this is a workaround for 0x3 and 0x9 LSB is being ORed */
if (detailstat == 0) {
pkt->pkt_statistics = 0;
} else {
/*
* devstatus: SCSI_SUCESS 0, SCSI_CHECK_COND 2,
* SCSI_BUSY 8, SCSI_RES_CONFLICT 18,
* SCSI_CMD_TERM 22, SCSI_TASK_SET_FULL 28,
* SCSI_ACA_ACTIVE 30
*/
if (devstatus == I2O_SCSI_DSC_CHECK_CONDITION) {
pkt->pkt_statistics = 0;
goto arqchk;
}
/* SYM HDM problem, can't recognized USCSI cmd well */
if ((devstatus == I2O_SCSI_DSC_BUSY) &&
pkt->pkt_statistics = 0;
goto done;
}
switch (adptrstatus) {
break;
/* bus reset */
/* Do we need to call notify_callback? */
break;
/* cmd terminated */
break;
/* request aborted */
break;
/* parity error */
break;
/* data overrun */
break;
/* cmd complete with error */
break;
/* cmd aborted by request or aborted by time */
| STAT_ABORTED;
} else {
}
break;
/* cmd timeout */
break;
/* CDB received */
break;
/* device reset msg send */
break;
/* unexpected bus free */
break;
/* cmd failed, transport error */
break;
/* failed command, retry? */
break;
break;
/* what? NO adapter! shutdown now! */
break;
default: /* failed cmd, retry */
break;
}
/*
* was there auto request sense info?
*/
arqchk: if (messagesize >
sizeof (i2o_scsi_success_reply_message_frame_t)) {
/* currently uses the DATA_IN_MESSAGE, only 40 bytes */
if (autocount) {
}
}
}
/*
* if data was xferred and this was an IOPB, we need
* to do a dma sync
*/
}
i2ohba->i2ohba_counter--;
/*
* Call packet completion routine if FLAG_NOINTR is not set.
* If FLAG_NOINTR is set turning on CFLAG_COMPLETED in line
* above will cause busy wait loop in
* i2ohba_i_polled_cmd_start() to exit.
*/
}
}
/*
* Function name : i2ohba_i_handle_arq()
*
* Description : called on an autorequest sense condition, sets up arqstat
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
static void
{
char status;
struct scsi_arq_status *arqstat;
/* clear the pkt_scbp struct */
/*
* I2O does not provide statistics for request sense,
* so use same statistics as the original cmd.
*/
if (aqcount < sizeof (struct scsi_extended_sense)) {
sizeof (struct scsi_extended_sense) - aqcount;
}
(sizeof (struct scsi_extended_sense) <
sizeof (struct scsi_extended_sense) :
/*
* restore status which was wiped out by bzero
*/
return;
}
/*
* Failed cmd auto sense data; can't copy over ARQ data,
*/
}
/*
* Function name : i2ohba_i_polled_cmd_start()
*
* Return Values : TRAN_ACCEPT if transaction was accepted
* TRAN_BUSY if I/O could not be started
* TRAN_ACCEPT if I/O timed out, pkt fields indicate error
*
* Description : Busy waits for I/O to complete or timeout.
* NOTE: This function returns void because the cmd
* has already started in scsi_start() before this
* function is called. So no need to return rval.
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
static void
{
int delay_loops;
/*
* set timeout to SCSI_POLL_TIMEOUT for non-polling
* commands that do not have this field set
*/
}
/*
* busy wait for command to finish
* ie. till CFLAG_COMPLETED is set
*/
if (--delay_loops <= 0) {
RESET_TARGET)) == 0) {
}
i2ohba->i2ohba_counter--;
break;
}
}
}
/*
* Function name : i2ohba_i_watch()
*
* Return Values : none
* Description : I2OHBA deadman timer routine.
*
* Given that the i2ohba's request queue is double link list
* in FIFO fashion, the tail should be pointing at the oldest
* cmd. However, each command has different timeout value set
* by the target driver, if any of them has timeout then we
* will call fatal_error().
*
* A hung i2ohba controller is detected by failure to complete
* cmds within a timeout interval (including grace period for
* i2ohba error recovery). All target error recovery is handled
* directly by the i2ohba.
*
* flushing the double linked list (via i2ohba_i_qflush()).
*
* Tagged queueing gives us other headaches since we cannot know
* exactly when a command starts. For example, a command with a
* 2-hour timeout, will cause a second command with a 60-second timeout
* to be timed-out. We won't worry about it now, but will later
*
* Context : Can be called by timeout thread.
*/
static void
i2ohba_i_watch(void *arg)
{
struct i2ohba_cmd *sp;
if (i2ohba->i2ohba_shutdown) {
return;
}
if ((cur_lbolt = ddi_get_lbolt()) != 0) {
/* report time out */
"?i2ohba_i_watch: "
"Exend cmd timeout on target %d.%d",
/* reset timeout vaule for delay */
/* set DELAY_TIMEOUT indicator */
/* set throttle on */
} else {
"?i2ohba_i_watch: "
"Cmd timeout on target %d.%d",
}
}
}
}
i2ohba->i2ohba_throttle = 0;
}
}
if (i2ohba->i2ohba_need_prop_update) {
int i;
for (i = 0; i < N_I2OHBA_TARGETS_WIDE; i++) {
}
}
}
/*
* Set up next timeout
*/
}
/*
* Function name : i2ohba_i_fatal_error()
*
* Return Values : none
*
* Description :
* Isp fatal error recovery:
* Reset the i2o and flush the active queues and attempt restart.
* This should only happen in case of a firmware bug or hardware
* death. Flushing is from backup queue as I2O cannot be trusted.
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
* i2ohba_request_mutex is held.
*/
static void
{
/*
* hold off starting new requests by grabbing the request mutex
*/
/*
* If shutdown flag is set than no need to do
* fatal error recovery.
*/
if (i2ohba->i2ohba_shutdown) {
return;
}
"Fatal error, resetting interface");
}
/*
* Function name : i2ohba_i_qflush()
*
* Return Values : none
* Description :
* Flush i2ohba queues over range specified
* from start_tgt to end_tgt. Flushing goes from oldest to newest
* to preserve some cmd ordering.
* This is used for i2o crash recovery as normally i2o takes
* care of target or bus problems.
*
* Context : Can be called from different kernel process threads.
* i2ohba_i_fatal_error()
* i2ohba_i_reset_interface()
* Can be called by interrupt thread.
*/
static void
{
struct i2ohba_cmd *sp;
/*
* If flushing active queue, start with current free slot
* ie. oldest request, to preserve some order.
*/
i2ohba->i2ohba_counter--;
}
}
}
}
/*
* Function name : i2ohba_scsi_abort()
*
* Return Values : 0 - abort failed
* 1 - abort succeeded
* Description :
* SCSA interface routine to abort pkt(s) in progress.
* Abort the pkt specified or NULL pkt, abort ALL pkts.
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
static int
{
int rval = 0;
return (rval);
/*
* hold off new requests, we need the req mutex anyway so noone
* can access the queue.
*/
"aborting pkt 0x%p", (void *)pkt);
if (pkt) {
return (0);
}
} else {
return (0);
}
}
return (1);
}
/*
* Function name : i2ohba_scsi_reset()
*
* Return Values : 0 - reset failed
* 1 - reset succeeded
* Description :
* SCSA interface routine to perform scsi resets on either
* a specified target or the bus (default).
*
* Context : Can be called from different kernel process threads.
* Can be called by interrupt thread.
*/
static int
{
int rval = 0;
return (rval);
/*
* hold off new requests, we need the req mutex.
*/
if (level == RESET_TARGET) {
I2O_SCSI_DEVICE_RESET, 0)) {
return (rval);
}
} else {
return (rval);
}
(void) scsi_hba_reset_notify_callback(
/* wait for 3 sec after a reset */
}
return (1);
}
/*
* Function name: i2ohba_i_reset_abort()
*
* Return Values: 0 - success
* -1 - fail reset
*
* Description :
* Reset either the HBA Adpter or Bus Reset
* This function is called from i2ohba_i_reset_interface()
* i2ohba_scsi_abort()
* i2ohba_scsi_reset()
*
* Context : Can be called from different kernel process threads.
* Can not be called by interrupt thread because it
* waits on the reply message.
*/
static int
struct i2ohba_cmd *cmd)
{
int rval = -1;
struct i2ohba_util *sp;
"i2o_msg_alloc failed");
return (rval);
}
/*
* construct a i2o_hba_bus_reset_message
*/
if (action == I2O_SCSI_SCB_ABORT)
sizeof (i2o_scsi_scb_abort_message_t) >> 2);
else
sizeof (i2o_scsi_device_reset_message_t) >> 2);
/*
* allocating synchronized status buffer
*/
if (cmd)
/*
* initialized a mutex and cond variable to
* send message to IOP, and wait for it to signal back
*/
/*
* process reply message
*/
case I2O_REPLY_STATUS_SUCCESS:
rval = 0;
break;
default:
/*
* Failed the reset for now,
* we can also parse the AdapterStatus
* and retry if needed
*/
break;
}
if (sp)
return (rval);
}
/*
* Function name : i2ohba_i_reset_interface()
*
* Return Values : 0 - success
* -1 - hw failure
*
* Description :
* i2ohba; and scsi bus and the scsi adapter. The works!
* This function is called from i2ohba_attach with no mutexes held or
* from i2ohba_i_fatal_error with request mutex held
*
* NOTE: it is up to the caller to flush the response queue
*
* Context : Can be called from different kernel process threads.
* i2ohba_attach() - single thread, no mutex held
* i2ohba_i_fatal_error()
* Can be called by interrupt thread.
*/
static int
{
int i;
int rval = -1;
/*
* Handle reset recovery; reset the bus before we reset the
* adapter chip
*/
/*
* send command Bus Reset
*/
if (action == I2OHBA_FORCE_BUS_RESET) {
I2O_HBA_BUS_RESET, 0)) {
"?i2ohba_i_reset_interface: bus reset failed");
goto cleanup;
}
(void) scsi_hba_reset_notify_callback(
}
/*
* Handle resetting i2o host adapter.
*/
/*
* Reset the i2o host adapter.
*/
I2O_HBA_ADAPTER_RESET, 0)) {
"adapter reset failed");
goto cleanup;
}
/*
* set Initiator SCSI ID using utilparamset
*/
/*
* DEBUGF(1, (CE_CONT, "Initializing SCSI HBA ID"));
*
* i = i2ohba->i2ohba_initiator_id;
* if (i2ohba_utilparamset_msg(i2ohba, 7, i2ohba->i2ohba_tid,
* I2O_HBA_SCSI_CONTROLLER_INFO_GROUP_NO, 0x4, i) != 0) {
* i2ohba_i_log(NULL, CE_WARN, "?i2ohba_i_reset_interface: "
* "resetting initiator id failed");
* goto cleanup;
* }
*
*/
/*
* global per target sync area.
*/
for (i = 0; i < N_I2OHBA_TARGETS_WIDE; i++) {
(void) i2ohba_i_updatesync(i2ohba, i);
}
rval = 0;
if (rval) {
}
return (rval);
}
static void
{
char buf[128];
int i;
/*
* Print out sync scsi info and suppress trailing zero
* period and offset entries.
*/
for (i = 0; i < N_I2OHBA_TARGETS; i++) {
}
for (i = N_I2OHBA_TARGETS; i < N_I2OHBA_TARGETS_WIDE; i++) {
}
}
}
/*
* Function name: i2ohba_utilparamset_msg()
*
* Return Values: 0 - success
* -1 - set prarmeter failed
* Description : common UtilParamSet function.
* However, only one param at a time. Pass in
* the Tid, GroupNumber, and the FieldIdx, and
* value, then compose the Message frame,
* then send it off through i2o_message_send().
*
* Message Format:
*
* Building a param_set message requires:
* Buffer#1 SGL-immediate data (Request)
* 1. i2o_param_operations_list_header_t(1W)
* u16 OperationCount (1)
* u16 Reserved
* 2. i2o_param_operation_specific_template_t(2W)
* u16 Operation (FIELD_SET)
* u16 GroupNumber (group)
* u16 FieldCount 0x1
* u16 FieldIdx (idx)
* u16 value of idx
*
* Buffer#2 SGL-Simple addressing (Reply)
* 1. i2o_param_results_list_header_t(1W)
* u16 ResultCount
* u16 Reserved
* 2. i2o_param_modify_operation_result_t(9W)
* u16 BlockSize
* u8 BlockStatus
* u8 ErrorInfoSize
* ... ErrorInformation
*
*
* Context : Can be called from i2ohba_i_reset_interface()
* Can be called from i2ohba_i_updatecap() or
* i2ohba_scsi_setcap()
*/
static int
{
int rval = -1;
int bound = 0;
/*
* allocate a message frame
*/
!= DDI_SUCCESS) {
"i2o_msg_alloc failed");
return (rval);
}
/*
* To get the actual allocation msg size
*/
if (allocmsgsize < sizeof (i2o_setparam_t)) {
"msg size alloc is smaller than what setparam needed");
goto fail;
}
/*
* Construct a utilparamset message
*/
(sizeof (i2o_setparam_t) >> 2));
/*
* allocating sychronize status buffer
*/
/*
* First message buffer's SGL and operation parameters
*/
/*
* Buf1 SGL header:
* Immediate Data for the operation
*/
/*
* Fill in the modify operation param for in the sgl1 payload
*/
/*
* Buf2 SGL header:
* Simple addressing
* 1. allocate the buffer for reply msg
* 2. setup the SGL header
*/
DDI_SUCCESS) {
"cannot alloc dma handle");
goto fail;
}
bound++;
sizeof (i2o_param_results_list_header_t) + (size_t)
sizeof (i2o_param_modify_operation_result_t),
"cannot allocate param buffer");
goto fail;
}
bound++;
sizeof (i2o_param_results_list_header_t) + (size_t)
sizeof (i2o_param_modify_operation_result_t) + (size_t)
sizeof (i2o_scsi_device_info_scalar_t),
/*
* currently assume simple
* addring with one physical
* continuous address buffer.
*/
"cannot bind buffer");
goto fail;
}
/*
* initialized a mutex and condvariable to
* send message to IOP, and wait for it
* to signal back
*/
/* clear msg pointer */
/*
* process the data
*/
case I2O_REPLY_STATUS_SUCCESS:
/*
* since the reply is successful, will check
* for the opeation reulsts
*/
&result->ResultCount);
if (!count) {
"?i2ohba_utilparamset_msg: No Results");
break;
}
&result->BlockStatus);
&result->ErrorInfoSize);
if ((blockstatus != 0) || (errinfosize != 0)) {
"?i2ohba_utilparamset_msg: Error "
"occured during Modify Operation");
/*
* retrived errorinfo and possibily retry
* XXLWLXX
*/
break;
}
rval = 0;
break;
default:
rval = -1;
break;
}
fail:
if (sp) {
if (bound) {
if (bound > 1)
}
}
/* return MFA to IOP */
if (msgptr) {
msgptr->VersionOffset = 0;
}
return (rval);
}
/*
* Function name: i2ohba_utilparamget_msg()
*
* Return Values: 0 - success
* -1 - get prarmeter failed
*
* Description : common UtilParamGet function.
* However, only one param at a time. Pass in
* the Tid, GroupNumber, and the FieldIdx, and
* value, then compose the Message frame,
* then send it off through i2o_message_send().
*
* Message Format:
*
* building a param_get message requires
* Buffer#1 SGL-immediate data
* 1. i2o_param_operations_list_header_t(1W)
* u16 OperationCount
* u16 Reserved
* 2. i2o_param_operation_speicific_template_t(2W)
* u16 Operation (FIELD_GET)
* u16 GroupNumber
* u16 FieldCount 0xFFFF
* u16 Pad
*
* Buffer#2 SGL-Simple addressing
* Reply Message:
* 1. i2o_param_results_list_header_t(1W)
* u16 ResultCount
* u16 Reserved
* 2. i2o_param_read_operation_result_t(9W)
* u16 BlockSize
* u8 BlockStatus
* u8 ErrorInfoSize
* ... Total 8W for (ALL_PARAM_BLOCK)
* u32 Identifier
* u64 LUN
* ...
* OR Total 9 bytes (ONE_PARAM_BLOCK)
* u8 NegOffset
* u64 NegSynch
*/
static int
{
int rval = -1;
int bound = 0;
/*
* allocate a message frame
*/
!= DDI_SUCCESS) {
"i2o_msg_alloc failed");
return (rval);
}
/*
* NOTE:
* "tidx" is either the entry number for the tid to scsi map
* or tidx is the target id, which needs tgt to id map to
* find the tid.
*/
if (flag == ALL_UTILPARAMS) {
} else {
}
/*
* To get the actual allocation msg
* size (in WORDS)
*/
/*
* Construct a utilparamget message
*/
/* msgptr->VersionOffset = ContextSize32; */
/*
* allocating sychroniz status buffer
*/
/*
* Buf1 SGL header:
* Immediate Data
*/
/*
* Fill in the read operation param for
* in the payload
*/
/*
* End of message sending
*/
/*
* Buf2 SGL header:
* Simple Addressing
* 1. allocating the buffer
* 2. set up the SGL header
*/
DDI_SUCCESS) {
"cannot alloc dma handle");
goto fail;
}
bound++;
if (flag == ALL_UTILPARAMS) {
if (allocmsgsize < sizeof (i2o_getallparam_t))
return (rval);
sizeof (i2o_getallparam_reply_t), &dev_attr,
"?i2ohba_utilparamget_msg: "
"cannot allocate param buffer");
goto fail;
}
bound++;
/* StdMessageFrame */
(sizeof (i2o_getallparam_t) >> 2));
/* i2o_sge_immediate_data_element */
/* i2o_param_operation_all_template */
/* i2o_sge_simple_element */
sizeof (i2o_getallparam_reply_t),
/*
* currently assume simple
* addring with one physical
* continuous address buffer.
*/
"?i2ohba_utilparamget_msg: "
"cannot bind buffer");
goto fail;
}
/*
* initialized the target param_mutex and cv.
* note: given that we don't have target id
* yet, (that is why we are here), we'll just
* use target[0]'s mutex & cv.
*/
} else {
if (allocmsgsize < sizeof (i2o_getsyncparam_t))
return (rval);
sizeof (i2o_getsyncparam_reply_t), &dev_attr,
"?i2ohba_utilparamget_msg: "
"cannot allocate param buffer");
goto fail;
}
bound++;
/* StdMessageFrame */
(sizeof (i2o_getsyncparam_t) >> 2));
/* i2o_sge_immediate_data_element */
/* i2o_param_operation_specific_template */
/* i2o_sge_simple_element */
sizeof (i2o_getsyncparam_reply_t),
/*
* currently assume simple
* addring with one physical
* continuous address buffer.
*/
"?i2ohba_utilparamget_msg: "
"cannot bind buffer");
goto fail;
}
/*
* initialized the target param_mutex and cv
* tidx holds the target id number
*/
}
#ifdef I2OHBA_DEBUG
msgptr->VersionOffset));
&msgptr->MessageSize)));
acc_handle)));
acc_handle)));
#endif
/*
* initialized a mutex and condvariable to
* send message to IOP, and wait for it
* to signal back
*/
/*
* process the data
*/
case I2O_REPLY_STATUS_SUCCESS:
/*
* Success:
* 0) read the reply headers XXLWXX
* 1) copy buffer over to the map
* 2) unbind dma buffer
*/
if (sp->i2ohba_util_buffer) {
if (flag == ALL_UTILPARAMS) {
reply = (i2o_getallparam_reply_t *)
tgtid =
&reply->Identifier);
tgtid));
&reply->QueueDepth);
reply->NegDataWidth));
&reply->NegSyncRate);
(uint32_t)
} else {
&reply->NegSyncRate);
(uint32_t)
}
}
rval = 0;
break;
default:
"Target %d failed", tidx);
break;
}
fail:
if (sp) {
if (bound) {
if (bound > 1)
}
}
/* return MFA to IOP */
if (msgptr) {
msgptr->VersionOffset = 0;
}
return (rval);
}
/*PRINTFLIKE3*/
static void
{
if (i2ohba) {
} else {
dip = 0;
}
} else {
}
}