/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* or http://www.opensolaris.org/os/licensing.
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* s1394_asynch.c
* 1394 Services Layer Asynchronous Communications Routines
* These routines handle all of the tasks relating to asynch commands
*/
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/disp.h>
#include <sys/tnf_probe.h>
#include <sys/1394/t1394.h>
#include <sys/1394/s1394.h>
#include <sys/1394/h1394.h>
#include <sys/1394/ieee1394.h>
#include <sys/1394/ieee1212.h>
static void s1394_handle_lock(cmd1394_cmd_t *cmd);
static cmd1394_cmd_t *s1394_pending_q_remove(s1394_hal_t *hal);
static boolean_t s1394_process_pending_q(s1394_hal_t *hal);
static boolean_t s1394_pending_q_helper(s1394_hal_t *hal, cmd1394_cmd_t *cmd);
static int s1394_process_split_lock(cmd1394_cmd_t *cmd,
cmd1394_cmd_t *target_cmd);
static int s1394_finish_split_lock(cmd1394_cmd_t *cmd,
cmd1394_cmd_t *target_cmd);
/*
* s1394_alloc_cmd()
* is used to allocate a command for a target or for a HAL.
*/
int
s1394_alloc_cmd(s1394_hal_t *hal, uint_t flags, cmd1394_cmd_t **cmdp)
{
s1394_cmd_priv_t *s_priv;
void *hal_overhead;
uint_t cmd_size;
int alloc_sleep;
TNF_PROBE_0_DEBUG(s1394_alloc_cmd_enter, S1394_TNF_SL_STACK, "");
alloc_sleep = (flags & T1394_ALLOC_CMD_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP;
if ((alloc_sleep == KM_SLEEP) &&
(servicing_interrupt())) {
TNF_PROBE_1(s1394_alloc_cmd_error, S1394_TNF_SL_ATREQ_ERROR,
"", tnf_string, msg, "Tried to sleep in intr context");
TNF_PROBE_0_DEBUG(s1394_alloc_cmd_exit,
S1394_TNF_SL_ATREQ_STACK, "");
ASSERT(alloc_sleep != KM_SLEEP); /* fail */
return (DDI_FAILURE);
}
/* either FCP command or response, but not both */
if ((flags &
(T1394_ALLOC_CMD_FCP_COMMAND | T1394_ALLOC_CMD_FCP_RESPONSE)) ==
(T1394_ALLOC_CMD_FCP_COMMAND | T1394_ALLOC_CMD_FCP_RESPONSE)) {
TNF_PROBE_1(s1394_alloc_cmd_error, S1394_TNF_SL_ATREQ_ERROR,
"", tnf_string, msg, "Both FCP cmd and resp flags");
TNF_PROBE_0_DEBUG(s1394_alloc_cmd_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
*cmdp = kmem_cache_alloc(hal->hal_kmem_cachep, alloc_sleep);
if (*cmdp == NULL) {
TNF_PROBE_0_DEBUG(s1394_alloc_cmd_exit,
S1394_TNF_SL_STACK, "");
return (DDI_FAILURE);
}
cmd_size = sizeof (cmd1394_cmd_t) +
sizeof (s1394_cmd_priv_t) + hal->halinfo.hal_overhead;
bzero((void *)*cmdp, cmd_size);
(*cmdp)->cmd_version = T1394_VERSION_V1;
(*cmdp)->cmd_result = CMD1394_NOSTATUS;
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(*cmdp);
/* Set extension type */
if (flags & T1394_ALLOC_CMD_FCP_COMMAND) {
s1394_fa_init_cmd(s_priv, S1394_FA_TYPE_FCP_CTL);
} else if (flags & T1394_ALLOC_CMD_FCP_RESPONSE) {
s1394_fa_init_cmd(s_priv, S1394_FA_TYPE_FCP_TGT);
}
/* Set up the hal_overhead ptr in the hal_cmd_private */
hal_overhead = (uchar_t *)s_priv + sizeof (s1394_cmd_priv_t);
s_priv->hal_cmd_private.hal_overhead = (void *)hal_overhead;
TNF_PROBE_1_DEBUG(s1394_alloc_cmd, S1394_TNF_SL_STACK, "",
tnf_opaque, cmd, *cmdp);
/* kstats - number of cmd allocs */
hal->hal_kstats->cmd_alloc++;
TNF_PROBE_0_DEBUG(s1394_alloc_cmd_exit, S1394_TNF_SL_STACK, "");
return (DDI_SUCCESS);
}
/*
* s1394_free_cmd()
* is used to free a command that had been previously allocated by
* s1394_alloc_cmd().
*/
int
s1394_free_cmd(s1394_hal_t *hal, cmd1394_cmd_t **cmdp)
{
s1394_cmd_priv_t *s_priv;
TNF_PROBE_0_DEBUG(s1394_free_cmd_enter, S1394_TNF_SL_STACK, "");
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(*cmdp);
/* Check that command isn't in use */
if (s_priv->cmd_in_use == B_TRUE) {
TNF_PROBE_1(s1394_free_cmd_error, S1394_TNF_SL_ERROR, "",
tnf_string, msg, "Attempted to free an in-use command");
TNF_PROBE_0_DEBUG(s1394_free_cmd_exit, S1394_TNF_SL_STACK, "");
ASSERT(s_priv->cmd_in_use == B_FALSE);
return (DDI_FAILURE);
}
TNF_PROBE_1_DEBUG(s1394_free_cmd, S1394_TNF_SL_STACK, "",
tnf_opaque, cmd, *cmdp);
/* kstats - number of cmd allocs */
kmem_cache_free(hal->hal_kmem_cachep, *cmdp);
/* Command pointer is set to NULL before returning */
*cmdp = NULL;
/* kstats - number of cmd frees */
hal->hal_kstats->cmd_free++;
TNF_PROBE_0_DEBUG(s1394_free_cmd_exit, S1394_TNF_SL_STACK, "");
return (DDI_SUCCESS);
}
/*
* s1394_xfer_asynch_command()
* is used to send an asynch command down to the HAL. Based upon the type
* of command that is being sent, the appropriate HAL function is called.
* Command failures are handled be returning an error and/or shutting down
* the HAL, depending on the severity of the error.
*/
int
s1394_xfer_asynch_command(s1394_hal_t *hal, cmd1394_cmd_t *cmd, int *err)
{
s1394_cmd_priv_t *s_priv;
h1394_cmd_priv_t *h_priv;
s1394_hal_state_t state;
dev_info_t *dip;
int result_from_hal;
int ret;
TNF_PROBE_0_DEBUG(s1394_xfer_asynch_command_enter,
S1394_TNF_SL_ATREQ_STACK, "");
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
mutex_enter(&hal->topology_tree_mutex);
state = hal->hal_state;
if (((state != S1394_HAL_NORMAL) && (state != S1394_HAL_RESET)) ||
(hal->disable_requests_bit == 1)) {
*err = s1394_HAL_asynch_error(hal, cmd, state);
mutex_exit(&hal->topology_tree_mutex);
TNF_PROBE_0_DEBUG(s1394_xfer_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
mutex_exit(&hal->topology_tree_mutex);
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Get a pointer to the HAL private struct */
h_priv = (h1394_cmd_priv_t *)&s_priv->hal_cmd_private;
/* kstats - number of AT requests sent */
switch (cmd->cmd_type) {
case CMD1394_ASYNCH_RD_QUAD:
hal->hal_kstats->atreq_quad_rd++;
break;
case CMD1394_ASYNCH_RD_BLOCK:
hal->hal_kstats->atreq_blk_rd++;
break;
case CMD1394_ASYNCH_WR_QUAD:
hal->hal_kstats->atreq_quad_wr++;
break;
case CMD1394_ASYNCH_WR_BLOCK:
hal->hal_kstats->atreq_blk_wr++;
hal->hal_kstats->atreq_blk_wr_size += h_priv->mblk.length;
break;
case CMD1394_ASYNCH_LOCK_32:
hal->hal_kstats->atreq_lock32++;
break;
case CMD1394_ASYNCH_LOCK_64:
hal->hal_kstats->atreq_lock64++;
break;
}
switch (s_priv->cmd_priv_xfer_type) {
/* Call the HAL's read entry point */
case S1394_CMD_READ:
ret = HAL_CALL(hal).read(hal->halinfo.hal_private,
(cmd1394_cmd_t *)cmd,
(h1394_cmd_priv_t *)&s_priv->hal_cmd_private,
&result_from_hal);
break;
/* Call the HAL's write entry point */
case S1394_CMD_WRITE:
ret = HAL_CALL(hal).write(hal->halinfo.hal_private,
(cmd1394_cmd_t *)cmd,
(h1394_cmd_priv_t *)&s_priv->hal_cmd_private,
&result_from_hal);
break;
/* Call the HAL's lock entry point */
case S1394_CMD_LOCK:
ret = HAL_CALL(hal).lock(hal->halinfo.hal_private,
(cmd1394_cmd_t *)cmd,
(h1394_cmd_priv_t *)&s_priv->hal_cmd_private,
&result_from_hal);
break;
default:
*err = CMD1394_EUNKNOWN_ERROR;
TNF_PROBE_1(s1394_xfer_asynch_command_error,
S1394_TNF_SL_ATREQ_ERROR, "", tnf_string, msg,
"Invalid command type specified");
TNF_PROBE_0_DEBUG(s1394_xfer_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
TNF_PROBE_0_DEBUG(s1394_xfer_asynch_command_return_from_HAL,
S1394_TNF_SL_ATREQ_STACK, "");
if (ret == DDI_FAILURE) {
switch (result_from_hal) {
case H1394_STATUS_EMPTY_TLABEL:
/* Out of TLABELs - Unable to send AT req */
*err = CMD1394_ENO_ATREQ;
break;
case H1394_STATUS_INVALID_BUSGEN:
/* Out of TLABELs - Unable to send AT req */
*err = CMD1394_ESTALE_GENERATION;
break;
case H1394_STATUS_NOMORE_SPACE:
/* No more space on HAL's HW queue */
*err = CMD1394_ENO_ATREQ;
break;
case H1394_STATUS_INTERNAL_ERROR:
dip = hal->halinfo.dip;
/* An unexpected error in the HAL */
cmn_err(CE_WARN, HALT_ERROR_MESSAGE,
ddi_node_name(dip), ddi_get_instance(dip));
/* Disable the HAL */
s1394_hal_shutdown(hal, B_TRUE);
*err = CMD1394_EFATAL_ERROR;
break;
default:
dip = hal->halinfo.dip;
/* An unexpected error in the HAL */
cmn_err(CE_WARN, HALT_ERROR_MESSAGE,
ddi_node_name(dip), ddi_get_instance(dip));
/* Disable the HAL */
s1394_hal_shutdown(hal, B_TRUE);
*err = CMD1394_EFATAL_ERROR;
break;
}
TNF_PROBE_2_DEBUG(s1394_xfer_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "",
tnf_int, result_from_hal, result_from_hal,
tnf_int, err, *err);
return (DDI_FAILURE);
}
/* No errors, return success */
*err = CMD1394_NOSTATUS;
TNF_PROBE_0_DEBUG(s1394_xfer_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
}
/*
* s1394_setup_asynch_command()
* is used to setup an asynch command to be sent down to the HAL and out
* onto the bus. This function handles setting up the destination address
* (if necessary), speed, max_payload, putting the command onto the
* outstanding Q list, and any other things that must be done prior to
* calling the HAL.
*/
int
s1394_setup_asynch_command(s1394_hal_t *hal, s1394_target_t *target,
cmd1394_cmd_t *cmd, uint32_t xfer_type, int *err)
{
s1394_cmd_priv_t *s_priv;
h1394_cmd_priv_t *h_priv;
uint64_t node;
uint32_t from_node;
uint32_t to_node;
uint32_t bus_capabilities;
uint_t current_max_payload;
uint_t max_rec;
uint_t max_blk;
TNF_PROBE_0_DEBUG(s1394_setup_asynch_command_enter,
S1394_TNF_SL_ATREQ_STACK, "");
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
switch (cmd->cmd_type) {
case CMD1394_ASYNCH_RD_QUAD:
case CMD1394_ASYNCH_WR_QUAD:
case CMD1394_ASYNCH_RD_BLOCK:
case CMD1394_ASYNCH_WR_BLOCK:
case CMD1394_ASYNCH_LOCK_32:
case CMD1394_ASYNCH_LOCK_64:
break;
default:
*err = CMD1394_EINVALID_COMMAND;
TNF_PROBE_1(s1394_setup_asynch_command_error,
S1394_TNF_SL_ATREQ_ERROR, "", tnf_string, msg,
"Invalid command type specified");
TNF_PROBE_0_DEBUG(s1394_setup_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
/* Check for potential address roll-over */
if (s1394_address_rollover(cmd) != B_FALSE) {
*err = CMD1394_EADDRESS_ERROR;
TNF_PROBE_0_DEBUG(s1394_setup_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Set up who sent command on which hal */
s_priv->sent_by_target = (s1394_target_t *)target;
s_priv->sent_on_hal = (s1394_hal_t *)hal;
/* Set up command transfer type */
s_priv->cmd_priv_xfer_type = xfer_type;
if (cmd->cmd_options & CMD1394_OVERRIDE_ADDR) {
/* Compare the current generation from the HAL struct */
/* to the one given by the target */
/* Speed is to be filled in from speed map */
from_node = IEEE1394_NODE_NUM(hal->node_id);
to_node = IEEE1394_ADDR_PHY_ID(cmd->cmd_addr);
if (cmd->bus_generation != hal->generation_count) {
*err = CMD1394_ESTALE_GENERATION;
TNF_PROBE_0_DEBUG(s1394_setup_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
} else {
/* Set the generation */
cmd->bus_generation = hal->generation_count;
/* If not OVERRIDE_ADDR, then target may not be NULL */
ASSERT(target != NULL);
rw_enter(&hal->target_list_rwlock, RW_READER);
if ((target->target_state & S1394_TARG_GONE) != 0 ||
target->on_node == NULL) {
rw_exit(&hal->target_list_rwlock);
*err = CMD1394_EDEVICE_REMOVED;
TNF_PROBE_0_DEBUG(s1394_setup_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
ASSERT((target->target_state & S1394_TARG_GONE) == 0);
node = target->on_node->node_num;
rw_exit(&hal->target_list_rwlock);
/* Mask in the top 16-bits */
cmd->cmd_addr = (cmd->cmd_addr & IEEE1394_ADDR_OFFSET_MASK);
cmd->cmd_addr = (cmd->cmd_addr |
(node << IEEE1394_ADDR_PHY_ID_SHIFT));
cmd->cmd_addr = (cmd->cmd_addr | IEEE1394_ADDR_BUS_ID_MASK);
/* Speed is to be filled in from speed map */
from_node = IEEE1394_NODE_NUM(hal->node_id);
to_node = (uint32_t)node;
}
/* Get a pointer to the HAL private struct */
h_priv = (h1394_cmd_priv_t *)&s_priv->hal_cmd_private;
/* Copy the generation into the HAL's private field */
h_priv->bus_generation = cmd->bus_generation;
/* Fill in the nodeID */
cmd->nodeID = (cmd->cmd_addr & IEEE1394_ADDR_NODE_ID_MASK) >>
IEEE1394_ADDR_NODE_ID_SHIFT;
if (cmd->cmd_options & CMD1394_OVERRIDE_SPEED) {
if (cmd->cmd_speed > IEEE1394_S400) {
*err = CMD1394_EINVALID_COMMAND;
TNF_PROBE_0_DEBUG(s1394_setup_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
} else {
s_priv->hal_cmd_private.speed = (int)cmd->cmd_speed;
}
} else {
/* Speed is to be filled in from speed map */
s_priv->hal_cmd_private.speed = (int)s1394_speed_map_get(hal,
from_node, to_node);
}
/* Is it a block request? */
if ((cmd->cmd_type == CMD1394_ASYNCH_RD_BLOCK) ||
(cmd->cmd_type == CMD1394_ASYNCH_WR_BLOCK)) {
if (cmd->cmd_u.b.data_block == NULL) {
*err = CMD1394_ENULL_MBLK;
TNF_PROBE_0_DEBUG(s1394_setup_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
/* Also need to check for MBLK_TOO_SMALL */
if (s1394_mblk_too_small(cmd) != B_FALSE) {
*err = CMD1394_EMBLK_TOO_SMALL;
TNF_PROBE_0_DEBUG(s1394_setup_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
/* Initialize bytes_transferred to zero */
cmd->cmd_u.b.bytes_transferred = 0;
/* Handle the MAX_PAYLOAD size */
if (cmd->cmd_options & CMD1394_OVERRIDE_ADDR) {
current_max_payload = 512 <<
(s_priv->hal_cmd_private.speed);
if (hal->topology_tree[to_node].cfgrom) {
bus_capabilities =
hal->topology_tree[to_node].cfgrom[
IEEE1212_NODE_CAP_QUAD];
max_rec = (bus_capabilities &
IEEE1394_BIB_MAXREC_MASK) >>
IEEE1394_BIB_MAXREC_SHIFT;
} else {
max_rec = 0;
}
if ((max_rec > 0) && (max_rec < 14)) {
max_blk = 1 << (max_rec + 1);
} else {
/* These are either unspecified or reserved */
max_blk = 4;
}
if (max_blk < current_max_payload)
current_max_payload = max_blk;
} else {
rw_enter(&hal->target_list_rwlock, RW_READER);
current_max_payload = target->current_max_payload;
rw_exit(&hal->target_list_rwlock);
}
if (cmd->cmd_options & CMD1394_OVERRIDE_MAX_PAYLOAD) {
if (current_max_payload > cmd->cmd_u.b.max_payload)
current_max_payload = cmd->cmd_u.b.max_payload;
}
h_priv->mblk.curr_mblk = cmd->cmd_u.b.data_block;
if (cmd->cmd_type == CMD1394_ASYNCH_WR_BLOCK) {
h_priv->mblk.curr_offset =
cmd->cmd_u.b.data_block->b_rptr;
} else {
h_priv->mblk.curr_offset =
cmd->cmd_u.b.data_block->b_wptr;
}
if (cmd->cmd_u.b.blk_length > current_max_payload) {
h_priv->mblk.length = current_max_payload;
s_priv->data_remaining = cmd->cmd_u.b.blk_length;
} else {
h_priv->mblk.length = cmd->cmd_u.b.blk_length;
s_priv->data_remaining = cmd->cmd_u.b.blk_length;
}
}
/* Mark command as being used */
s_priv->cmd_in_use = B_TRUE;
/* Put command on the HAL's outstanding request Q */
s1394_insert_q_asynch_cmd(hal, cmd);
TNF_PROBE_0_DEBUG(s1394_setup_asynch_command_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
}
/*
* s1394_insert_q_asynch_cmd()
* is used to insert a given command structure onto a HAL's outstanding
* asynch queue.
*/
void
s1394_insert_q_asynch_cmd(s1394_hal_t *hal, cmd1394_cmd_t *cmd)
{
s1394_cmd_priv_t *s_priv;
s1394_cmd_priv_t *c_priv;
cmd1394_cmd_t *temp_cmd;
TNF_PROBE_0_DEBUG(s1394_insert_q_asynch_cmd_enter,
S1394_TNF_SL_ATREQ_STACK, "");
mutex_enter(&hal->outstanding_q_mutex);
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Is the outstanding request queue empty? */
if ((hal->outstanding_q_head == NULL) &&
(hal->outstanding_q_tail == NULL)) {
hal->outstanding_q_head = (cmd1394_cmd_t *)cmd;
hal->outstanding_q_tail = (cmd1394_cmd_t *)cmd;
s_priv->cmd_priv_next = (cmd1394_cmd_t *)NULL;
s_priv->cmd_priv_prev = (cmd1394_cmd_t *)NULL;
} else {
s_priv->cmd_priv_next = hal->outstanding_q_head;
s_priv->cmd_priv_prev = (cmd1394_cmd_t *)NULL;
temp_cmd = (cmd1394_cmd_t *)hal->outstanding_q_head;
c_priv = (s1394_cmd_priv_t *)((uchar_t *)temp_cmd +
sizeof (cmd1394_cmd_t));
c_priv->cmd_priv_prev = (cmd1394_cmd_t *)cmd;
hal->outstanding_q_head = (cmd1394_cmd_t *)cmd;
}
mutex_exit(&hal->outstanding_q_mutex);
TNF_PROBE_0_DEBUG(s1394_insert_q_asynch_cmd_exit,
S1394_TNF_SL_ATREQ_STACK, "");
}
/*
* s1394_remove_q_asynch_cmd()
* is used to remove a given command structure from a HAL's outstanding
* asynch queue.
*/
void
s1394_remove_q_asynch_cmd(s1394_hal_t *hal, cmd1394_cmd_t *cmd)
{
s1394_cmd_priv_t *s_priv;
s1394_cmd_priv_t *c_priv;
cmd1394_cmd_t *prev_cmd;
cmd1394_cmd_t *next_cmd;
TNF_PROBE_0_DEBUG(s1394_remove_q_asynch_cmd_enter,
S1394_TNF_SL_ATREQ_STACK, "");
mutex_enter(&hal->outstanding_q_mutex);
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
prev_cmd = (cmd1394_cmd_t *)s_priv->cmd_priv_prev;
next_cmd = (cmd1394_cmd_t *)s_priv->cmd_priv_next;
s_priv->cmd_priv_prev = (cmd1394_cmd_t *)NULL;
s_priv->cmd_priv_next = (cmd1394_cmd_t *)NULL;
if (prev_cmd != NULL) {
c_priv = (s1394_cmd_priv_t *)((uchar_t *)prev_cmd +
sizeof (cmd1394_cmd_t));
c_priv->cmd_priv_next = (cmd1394_cmd_t *)next_cmd;
} else {
if (hal->outstanding_q_head == (cmd1394_cmd_t *)cmd)
hal->outstanding_q_head = (cmd1394_cmd_t *)next_cmd;
}
if (next_cmd != NULL) {
c_priv = (s1394_cmd_priv_t *)((uchar_t *)next_cmd +
sizeof (cmd1394_cmd_t));
c_priv->cmd_priv_prev = (cmd1394_cmd_t *)prev_cmd;
} else {
if (hal->outstanding_q_tail == (cmd1394_cmd_t *)cmd)
hal->outstanding_q_tail = (cmd1394_cmd_t *)prev_cmd;
}
mutex_exit(&hal->outstanding_q_mutex);
TNF_PROBE_0_DEBUG(s1394_remove_q_asynch_cmd_exit,
S1394_TNF_SL_ATREQ_STACK, "");
}
/*
* s1394_atreq_cmd_complete()
* is called by h1394_cmd_is_complete() when an AT request has completed.
* Based upon a command's completion status, s1394_atreq_cmd_complete()
* determines whether to call the target (or unblock), put the command onto
* the pending Q to be sent out later, or to resend the command
* (multi-part command).
*/
void
s1394_atreq_cmd_complete(s1394_hal_t *hal, cmd1394_cmd_t *req, int status)
{
s1394_cmd_priv_t *s_priv;
h1394_cmd_priv_t *h_priv;
dev_info_t *dip;
int ret;
int cmd_result;
int err;
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_enter,
S1394_TNF_SL_ATREQ_STACK, "");
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(req);
/* If not an ack_complete... */
if (status != H1394_CMD_SUCCESS) {
/* kstats - number of failure AT responses */
switch (req->cmd_type) {
case CMD1394_ASYNCH_RD_QUAD:
hal->hal_kstats->atresp_quad_rd_fail++;
break;
case CMD1394_ASYNCH_RD_BLOCK:
hal->hal_kstats->atresp_blk_rd_fail++;
break;
case CMD1394_ASYNCH_WR_QUAD:
hal->hal_kstats->atresp_quad_wr_fail++;
break;
case CMD1394_ASYNCH_WR_BLOCK:
hal->hal_kstats->atresp_blk_wr_fail++;
break;
case CMD1394_ASYNCH_LOCK_32:
hal->hal_kstats->atresp_lock32_fail++;
break;
case CMD1394_ASYNCH_LOCK_64:
hal->hal_kstats->atresp_lock64_fail++;
break;
}
switch (status) {
/* evt_missing_ack */
case H1394_CMD_ETIMEOUT:
cmd_result = CMD1394_ETIMEOUT;
break;
/* evt_flushed */
case H1394_CMD_EBUSRESET:
/* Move request to pending Q if cancel on */
/* reset is not set */
if (req->cmd_options & CMD1394_CANCEL_ON_BUS_RESET) {
cmd_result = CMD1394_EBUSRESET;
break;
}
s1394_remove_q_asynch_cmd(hal, req);
s1394_pending_q_insert(hal, req, S1394_PENDING_Q_REAR);
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return;
/* ack_busy_X */
/* ack_busy_A */
/* ack_busy_B */
case H1394_CMD_EDEVICE_BUSY:
cmd_result = CMD1394_EDEVICE_BUSY;
break;
/* ack_data_error */
case H1394_CMD_EDATA_ERROR:
cmd_result = CMD1394_EDATA_ERROR;
break;
/* ack_type_error */
case H1394_CMD_ETYPE_ERROR:
cmd_result = CMD1394_ETYPE_ERROR;
break;
/* resp_address_error */
/* ack_address_error */
case H1394_CMD_EADDR_ERROR:
cmd_result = CMD1394_EADDRESS_ERROR;
break;
/* resp_conflict_error */
/* ack_conflict_error */
case H1394_CMD_ERSRC_CONFLICT:
cmd_result = CMD1394_ERSRC_CONFLICT;
break;
/* ack_tardy */
case H1394_CMD_EDEVICE_POWERUP:
cmd_result = CMD1394_EDEVICE_BUSY;
break;
/* device errors (bad tcodes, ACKs, etc...) */
case H1394_CMD_EDEVICE_ERROR:
cmd_result = CMD1394_EDEVICE_ERROR;
break;
/* Unknown error type */
case H1394_CMD_EUNKNOWN_ERROR:
cmd_result = CMD1394_EUNKNOWN_ERROR;
break;
/* Unrecognized error */
default:
dip = hal->halinfo.dip;
/* An unexpected error in the HAL */
cmn_err(CE_WARN, HALT_ERROR_MESSAGE,
ddi_node_name(dip), ddi_get_instance(dip));
/* Disable the HAL */
s1394_hal_shutdown(hal, B_TRUE);
TNF_PROBE_2(s1394_atreq_cmd_complete_error,
S1394_TNF_SL_ATREQ_ERROR, "", tnf_string, msg,
"Unrecognized cmd status code",
tnf_int, status, status);
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return;
}
/* Remove command from the HAL's outstanding request Q */
s1394_remove_q_asynch_cmd(hal, req);
s_priv->cmd_in_use = B_FALSE;
req->cmd_result = cmd_result;
/* Is this a blocking command? */
if (req->cmd_options & CMD1394_BLOCKING) {
/* Unblock the waiting command */
mutex_enter(&s_priv->blocking_mutex);
s_priv->blocking_flag = B_TRUE;
cv_signal(&s_priv->blocking_cv);
mutex_exit(&s_priv->blocking_mutex);
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return;
}
/* Call the target's completion_callback() */
if (req->completion_callback != NULL) {
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_do_callback,
S1394_TNF_SL_ATREQ_STACK, "");
req->completion_callback(req);
}
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return;
}
/* Successful unless otherwise modified */
err = CMD1394_CMDSUCCESS;
if ((req->cmd_type == CMD1394_ASYNCH_RD_BLOCK) ||
(req->cmd_type == CMD1394_ASYNCH_WR_BLOCK)) {
/* Get a pointer to the HAL private struct */
h_priv = (h1394_cmd_priv_t *)&s_priv->hal_cmd_private;
/* Update data_remaining */
s_priv->data_remaining -= h_priv->mblk.length;
/* Increment bytes_transferred */
req->cmd_u.b.bytes_transferred += h_priv->mblk.length;
if (req->cmd_type == CMD1394_ASYNCH_RD_BLOCK)
hal->hal_kstats->atreq_blk_rd_size +=
h_priv->mblk.length;
/* Is there still more to send? */
if (s_priv->data_remaining > 0) {
/* Setup the new mblk and offset */
h_priv->mblk.curr_mblk = h_priv->mblk.next_mblk;
h_priv->mblk.curr_offset = h_priv->mblk.next_offset;
/* Update destination address */
if (!(req->cmd_options &
CMD1394_DISABLE_ADDR_INCREMENT)) {
req->cmd_addr += h_priv->mblk.length;
}
/*
* Use the current MAX_PAYLOAD size. This value
* doesn't need to be recalculated because we must
* be in the same generation on the bus, else we
* would have seen a bus reset error.
*/
if (s_priv->data_remaining < h_priv->mblk.length) {
h_priv->mblk.length = s_priv->data_remaining;
}
/* Send command out again */
ret = s1394_xfer_asynch_command(hal, req, &err);
if (ret == DDI_SUCCESS) {
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return;
} else if (err == CMD1394_ESTALE_GENERATION) {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(hal, req);
s1394_pending_q_insert(hal, req,
S1394_PENDING_Q_REAR);
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return;
}
}
}
/* Remove command from the HAL's outstanding request Q */
s1394_remove_q_asynch_cmd(hal, req);
s_priv->cmd_in_use = B_FALSE;
/* Set status */
req->cmd_result = err;
/* Is this a blocking command? */
if (req->cmd_options & CMD1394_BLOCKING) {
/* Unblock the waiting command */
mutex_enter(&s_priv->blocking_mutex);
s_priv->blocking_flag = B_TRUE;
cv_signal(&s_priv->blocking_cv);
mutex_exit(&s_priv->blocking_mutex);
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return;
}
/* Set status and call completion_callback() */
if (req->completion_callback != NULL) {
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_do_callback,
S1394_TNF_SL_ATREQ_STACK, "");
req->completion_callback(req);
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return;
}
TNF_PROBE_0_DEBUG(s1394_atreq_cmd_complete_exit,
S1394_TNF_SL_ATREQ_STACK, "");
}
/*
* s1394_atresp_cmd_complete()
* is similar to s1394_atreq_cmd_complete(). It is also called by
* h1394_cmd_is_complete(), but when an AT response has completed.
* Again, based upon the command's completion status,
* s1394_atresp_cmd_complete() determines whether to call the target or
* to simply cleanup the command and return.
*/
void
s1394_atresp_cmd_complete(s1394_hal_t *hal, cmd1394_cmd_t *resp, int status)
{
s1394_cmd_priv_t *s_priv;
h1394_cmd_priv_t *h_priv;
dev_info_t *dip;
boolean_t valid_addr_blk;
int target_status;
TNF_PROBE_0_DEBUG(s1394_atresp_cmd_complete_enter,
S1394_TNF_SL_ATRESP_STACK, "");
target_status = CMD1394_CMDSUCCESS;
/* If not an ack_complete */
if (status != H1394_CMD_SUCCESS) {
switch (status) {
/* evt_missing_ack */
case H1394_CMD_ETIMEOUT:
target_status = CMD1394_ETIMEOUT;
break;
/* evt_flushed */
case H1394_CMD_EBUSRESET:
target_status = CMD1394_EBUSRESET;
break;
/* ack_busy_X */
/* ack_busy_A */
/* ack_busy_B */
case H1394_CMD_EDEVICE_BUSY:
target_status = CMD1394_EDEVICE_BUSY;
break;
/* ack_data_error */
case H1394_CMD_EDATA_ERROR:
target_status = CMD1394_EDATA_ERROR;
break;
/* ack_type_error */
case H1394_CMD_ETYPE_ERROR:
target_status = CMD1394_ETYPE_ERROR;
break;
/* ack_address_error */
case H1394_CMD_EADDR_ERROR:
target_status = CMD1394_EADDRESS_ERROR;
break;
/* ack_conflict_error */
case H1394_CMD_ERSRC_CONFLICT:
target_status = CMD1394_ERSRC_CONFLICT;
break;
/* ack_tardy */
case H1394_CMD_EDEVICE_POWERUP:
target_status = CMD1394_EDEVICE_BUSY;
break;
/* device errors (bad tcodes, ACKs, etc...) */
case H1394_CMD_EDEVICE_ERROR:
target_status = CMD1394_EDEVICE_ERROR;
break;
/* Unknown error type */
case H1394_CMD_EUNKNOWN_ERROR:
target_status = CMD1394_EUNKNOWN_ERROR;
break;
/* Unrecognized error */
default:
dip = hal->halinfo.dip;
/* An unexpected error in the HAL */
cmn_err(CE_WARN, HALT_ERROR_MESSAGE,
ddi_node_name(dip), ddi_get_instance(dip));
/* Disable the HAL */
s1394_hal_shutdown(hal, B_TRUE);
TNF_PROBE_2(s1394_atresp_cmd_complete_error,
S1394_TNF_SL_ATRESP_ERROR, "", tnf_string, msg,
"Unrecognized cmd status code",
tnf_int, status, status);
TNF_PROBE_0_DEBUG(s1394_atresp_cmd_complete_exit,
S1394_TNF_SL_ATRESP_STACK, "");
return;
}
}
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(resp);
/* Get a pointer to the HAL private struct */
h_priv = (h1394_cmd_priv_t *)&s_priv->hal_cmd_private;
valid_addr_blk = s_priv->arreq_valid_addr;
if (valid_addr_blk == B_TRUE) {
/* Set the command status */
resp->cmd_result = target_status;
switch (s_priv->cmd_priv_xfer_type) {
case S1394_CMD_READ:
case S1394_CMD_WRITE:
case S1394_CMD_LOCK:
if (resp->completion_callback != NULL) {
TNF_PROBE_0_DEBUG(
s1394_atresp_cmd_complete_do_callback,
S1394_TNF_SL_ATRESP_STACK, "");
resp->completion_callback(resp);
}
break;
default:
dip = hal->halinfo.dip;
/* An unexpected error in the HAL */
cmn_err(CE_WARN, HALT_ERROR_MESSAGE,
ddi_node_name(dip), ddi_get_instance(dip));
/* Disable the HAL */
s1394_hal_shutdown(hal, B_TRUE);
TNF_PROBE_1(s1394_atresp_cmd_complete_error,
S1394_TNF_SL_ATRESP_ERROR, "", tnf_string, msg,
"Unrecognized transfer type");
TNF_PROBE_0_DEBUG(s1394_atresp_cmd_complete_exit,
S1394_TNF_SL_ATRESP_STACK, "");
return;
}
}
TNF_PROBE_0_DEBUG(s1394_atresp_cmd_complete_call_hal_cmplt,
S1394_TNF_SL_ATRESP_STACK, "");
/* Free the command - Pass it back to the HAL */
HAL_CALL(hal).response_complete(hal->halinfo.hal_private, resp, h_priv);
TNF_PROBE_1_DEBUG(s1394_atresp_cmd_complete_exit,
S1394_TNF_SL_ATRESP_STACK, "", tnf_int, status, target_status);
}
/*
* s1394_send_response()
* is used to send a response to an AR request. Depending on whether the
* request was a broadcast request, a write to posted write address space,
* or some other request, either a response packet is sent, or the command
* is returned to the HAL. A return value of DDI_SUCCESS means that the
* command has been handled correctly. It was either successfully sent to
* the HAL, or, if it was posted_write of broadcast, it was freed up. A
* return value of DDI_FAILURE indicates either a serious error, in which
* case the HAL is shutdown, or a failure returned by the HAL, in which
* case the command is freed up and notice of the failure is returned.
*/
int
s1394_send_response(s1394_hal_t *hal, cmd1394_cmd_t *resp)
{
s1394_cmd_priv_t *s_priv;
h1394_cmd_priv_t *h_priv;
dev_info_t *dip;
int ret;
int result;
TNF_PROBE_0_DEBUG(s1394_send_response_enter, S1394_TNF_SL_ATRESP_STACK,
"");
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(resp);
/* Get a pointer to the HAL private struct */
h_priv = (h1394_cmd_priv_t *)&s_priv->hal_cmd_private;
/*
* If request was broadcast or a write request to a posted write
* address, don't send a response
*/
if ((resp->broadcast == 1) || ((s_priv->posted_write == B_TRUE) &&
((resp->cmd_result == CMD1394_ASYNCH_WR_QUAD) ||
(resp->cmd_result == CMD1394_ASYNCH_WR_BLOCK)))) {
TNF_PROBE_0_DEBUG(s1394_send_response_call_hal_cmplt,
S1394_TNF_SL_ATRESP_STACK, "");
/* Free the command - Pass it back to the HAL */
HAL_CALL(hal).response_complete(hal->halinfo.hal_private,
resp, h_priv);
TNF_PROBE_0_DEBUG(s1394_send_response_exit,
S1394_TNF_SL_ATRESP_STACK, "");
return (DDI_SUCCESS);
}
TNF_PROBE_0_DEBUG(s1394_send_response_call_hal_resp,
S1394_TNF_SL_ATRESP_STACK, "");
/* kstats - number of failure responses sent */
if (resp->cmd_result != IEEE1394_RESP_COMPLETE) {
switch (resp->cmd_type) {
case CMD1394_ASYNCH_RD_QUAD:
hal->hal_kstats->arresp_quad_rd_fail++;
break;
case CMD1394_ASYNCH_RD_BLOCK:
hal->hal_kstats->arresp_blk_rd_fail++;
break;
case CMD1394_ASYNCH_WR_QUAD:
hal->hal_kstats->arresp_quad_wr_fail++;
break;
case CMD1394_ASYNCH_WR_BLOCK:
hal->hal_kstats->arresp_blk_wr_fail++;
break;
case CMD1394_ASYNCH_LOCK_32:
hal->hal_kstats->arresp_lock32_fail++;
break;
case CMD1394_ASYNCH_LOCK_64:
hal->hal_kstats->arresp_lock64_fail++;
break;
}
} else {
if (resp->cmd_type == CMD1394_ASYNCH_RD_BLOCK)
hal->hal_kstats->arreq_blk_rd_size +=
resp->cmd_u.b.blk_length;
}
if (resp->cmd_type == CMD1394_ASYNCH_RD_BLOCK) {
h_priv->mblk.curr_mblk = resp->cmd_u.b.data_block;
h_priv->mblk.curr_offset = resp->cmd_u.b.data_block->b_rptr;
h_priv->mblk.length = resp->cmd_u.b.blk_length;
}
switch (s_priv->cmd_priv_xfer_type) {
case S1394_CMD_READ:
ret = HAL_CALL(hal).read_response(hal->halinfo.hal_private,
resp, h_priv, &result);
break;
case S1394_CMD_WRITE:
ret = HAL_CALL(hal).write_response(hal->halinfo.hal_private,
resp, h_priv, &result);
break;
case S1394_CMD_LOCK:
ret = HAL_CALL(hal).lock_response(hal->halinfo.hal_private,
resp, h_priv, &result);
break;
default:
dip = hal->halinfo.dip;
/* An unexpected error in the HAL */
cmn_err(CE_WARN, HALT_ERROR_MESSAGE,
ddi_node_name(dip), ddi_get_instance(dip));
/* Disable the HAL */
s1394_hal_shutdown(hal, B_TRUE);
TNF_PROBE_1(s1394_send_response_error,
S1394_TNF_SL_ATRESP_ERROR, "", tnf_string, msg,
"Unrecognized transfer type");
TNF_PROBE_0_DEBUG(s1394_send_response_exit,
S1394_TNF_SL_ATRESP_STACK, "");
return (DDI_FAILURE);
}
/* Unable to send a response */
if (ret != DDI_SUCCESS) {
if (result == H1394_STATUS_INVALID_BUSGEN) {
TNF_PROBE_1(s1394_send_response_error,
S1394_TNF_SL_ATRESP_ERROR, "", tnf_string, msg,
"Invalid generation in response");
} else if (result == H1394_STATUS_NOMORE_SPACE) {
TNF_PROBE_1(s1394_send_response_error,
S1394_TNF_SL_ATRESP_ERROR, "", tnf_string, msg,
"No more space on AT response queue");
} else {
TNF_PROBE_1(s1394_send_response_error,
S1394_TNF_SL_ATRESP_ERROR, "", tnf_string, msg,
"Unknown problem in s1394_send_response");
}
TNF_PROBE_0_DEBUG(s1394_send_response_call_hal_cmplt,
S1394_TNF_SL_ATRESP_STACK, "");
/* Free the command - Pass it back to the HAL */
HAL_CALL(hal).response_complete(hal->halinfo.hal_private,
resp, h_priv);
TNF_PROBE_0_DEBUG(s1394_send_response_exit,
S1394_TNF_SL_ATRESP_STACK, "");
return (DDI_FAILURE);
}
TNF_PROBE_0_DEBUG(s1394_send_response_exit, S1394_TNF_SL_ATRESP_STACK,
"");
return (DDI_SUCCESS);
}
/*
* s1394_compare_swap()
* is used by t1394_lock() to send a lock request. Any of the lock
* requests specified explicitly by the 1394 spec will pass thru here,
* i.e compare-swap, mask-swap, etc.
*/
int
s1394_compare_swap(s1394_hal_t *hal, s1394_target_t *target, cmd1394_cmd_t *cmd)
{
s1394_cmd_priv_t *s_priv;
s1394_hal_state_t state;
int err;
int ret;
TNF_PROBE_0_DEBUG(s1394_compare_swap_enter, S1394_TNF_SL_ATREQ_STACK,
"");
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
/* Lock the topology tree - protect from bus reset */
mutex_enter(&hal->topology_tree_mutex);
ret = s1394_setup_asynch_command(hal, target, cmd, S1394_CMD_LOCK,
&err);
/* Unlock the topology tree */
mutex_exit(&hal->topology_tree_mutex);
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Command has now been put onto the queue! */
if (ret != DDI_SUCCESS) {
/* Copy error code into result */
cmd->cmd_result = err;
TNF_PROBE_0(s1394_compare_swap_error_in_setup_asynch,
S1394_TNF_SL_ATREQ_ERROR, "");
TNF_PROBE_0_DEBUG(s1394_compare_swap_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
mutex_enter(&hal->topology_tree_mutex);
state = hal->hal_state;
/* If this command was sent during a bus reset, */
/* then put it onto the pending Q. */
if (state == S1394_HAL_RESET) {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(hal, cmd);
/* Are we on the bus reset event stack? */
if (s1394_on_br_thread(hal) == B_TRUE) {
/* Blocking commands are not allowed */
if (cmd->cmd_options & CMD1394_BLOCKING) {
mutex_exit(&hal->topology_tree_mutex);
s_priv->cmd_in_use = B_FALSE;
cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
TNF_PROBE_1(s1394_compare_swap_error,
S1394_TNF_SL_ATREQ_ERROR, "", tnf_string,
msg, "CMD1394_BLOCKING in bus reset ctxt");
TNF_PROBE_0_DEBUG(s1394_compare_swap_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
}
s1394_pending_q_insert(hal, cmd, S1394_PENDING_Q_FRONT);
mutex_exit(&hal->topology_tree_mutex);
/* Block (if necessary) */
s1394_block_on_asynch_cmd(cmd);
TNF_PROBE_0_DEBUG(t1394_write_exit, S1394_TNF_SL_ATREQ_STACK,
"");
return (DDI_SUCCESS);
}
mutex_exit(&hal->topology_tree_mutex);
/* Send the command out */
ret = s1394_xfer_asynch_command(hal, cmd, &err);
if (ret != DDI_SUCCESS) {
if (err == CMD1394_ESTALE_GENERATION) {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(hal, cmd);
s1394_pending_q_insert(hal, cmd, S1394_PENDING_Q_FRONT);
/* Block (if necessary) */
s1394_block_on_asynch_cmd(cmd);
TNF_PROBE_0_DEBUG(s1394_compare_swap_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
} else {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(hal, cmd);
s_priv->cmd_in_use = B_FALSE;
/* Copy error code into result */
cmd->cmd_result = err;
TNF_PROBE_0(s1394_compare_swap_error_in_xfer,
S1394_TNF_SL_ATREQ_ERROR, "");
TNF_PROBE_0_DEBUG(s1394_compare_swap_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
} else {
/* Block (if necessary) */
s1394_block_on_asynch_cmd(cmd);
TNF_PROBE_0_DEBUG(s1394_compare_swap_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
}
}
/*
* s1394_split_lock_req()
* is also used by t1394_lock() to send a lock request. The difference
* is that s1394_split_lock_req() is used to send the software supported
* lock types, i.e. bit_and, bit_or, etc. These lock requests require
* more than one transaction, typically compare-swap's.
*/
int
s1394_split_lock_req(s1394_hal_t *hal, s1394_target_t *target,
cmd1394_cmd_t *cmd)
{
s1394_cmd_priv_t *s_priv;
cmd1394_cmd_t *tmp_cmd;
TNF_PROBE_0_DEBUG(s1394_split_lock_req_enter,
S1394_TNF_SL_ATREQ_STACK, "");
/* Allocate a temporary command */
if (s1394_alloc_cmd(hal, T1394_ALLOC_CMD_NOSLEEP, &tmp_cmd) !=
DDI_SUCCESS) {
cmd->cmd_result = CMD1394_EUNKNOWN_ERROR;
TNF_PROBE_0(s1394_split_lock_req_error_alloc_cmd,
S1394_TNF_SL_ATREQ_ERROR, "");
TNF_PROBE_0_DEBUG(s1394_split_lock_req_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(tmp_cmd);
tmp_cmd->completion_callback = s1394_handle_lock;
tmp_cmd->cmd_callback_arg = (opaque_t)cmd;
tmp_cmd->cmd_type = cmd->cmd_type;
tmp_cmd->cmd_addr = cmd->cmd_addr;
tmp_cmd->cmd_options = cmd->cmd_options;
tmp_cmd->bus_generation = cmd->bus_generation;
/* The temporary command can not block */
tmp_cmd->cmd_options = tmp_cmd->cmd_options & ~CMD1394_BLOCKING;
/* Setup compare-swap with data_value == arg_value (read) */
if (tmp_cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) {
tmp_cmd->cmd_u.l32.data_value = 0;
tmp_cmd->cmd_u.l32.arg_value = 0;
tmp_cmd->cmd_u.l32.lock_type = CMD1394_LOCK_COMPARE_SWAP;
s_priv->temp_num_retries = cmd->cmd_u.l32.num_retries;
} else {
tmp_cmd->cmd_u.l64.data_value = 0;
tmp_cmd->cmd_u.l64.arg_value = 0;
tmp_cmd->cmd_u.l64.lock_type = CMD1394_LOCK_COMPARE_SWAP;
s_priv->temp_num_retries = cmd->cmd_u.l64.num_retries;
}
/* Initialize lock_req_step */
s_priv->lock_req_step = 0;
/* Get the Services Layer private area for the target cmd */
s_priv = S1394_GET_CMD_PRIV(cmd);
s_priv->cmd_in_use = B_TRUE;
/* Send the request */
if (s1394_compare_swap(hal, target, tmp_cmd) != DDI_SUCCESS) {
s_priv->cmd_in_use = B_FALSE;
/* Free the temporary command */
if (s1394_free_cmd(hal, &tmp_cmd) != DDI_SUCCESS)
cmd->cmd_result = CMD1394_EUNKNOWN_ERROR;
TNF_PROBE_0_DEBUG(s1394_split_lock_req_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
/* Block (if necessary) */
s1394_block_on_asynch_cmd(cmd);
TNF_PROBE_0_DEBUG(s1394_split_lock_req_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
}
/*
* s1394_handle_lock()
* is the callback for s1394_split_lock_req(). It does all of the real
* work. Based on the specific lock type all necessary manipulation is
* performed and another compare swap is sent out. If the transaction
* is unsuccessful, it is retried.
*/
static void
s1394_handle_lock(cmd1394_cmd_t *cmd)
{
s1394_hal_t *to_hal;
s1394_target_t *target;
s1394_cmd_priv_t *s_priv;
cmd1394_cmd_t *target_cmd;
uint32_t lock_req_step;
int tcmd_result;
int ret;
TNF_PROBE_0_DEBUG(s1394_handle_lock_enter, S1394_TNF_SL_ATREQ_STACK,
"");
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
lock_req_step = s_priv->lock_req_step;
/* Get the target's command */
target_cmd = (cmd1394_cmd_t *)cmd->cmd_callback_arg;
/* Get the destination of the command */
to_hal = s_priv->sent_on_hal;
lock_req_step_0:
/* Is this step 0 completing? */
if (lock_req_step == 0) {
/* Was the request successful? */
if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
/* Do any math, bit ops, or byte-swapping necessary */
ret = s1394_process_split_lock(cmd, target_cmd);
if (ret != DDI_SUCCESS) {
tcmd_result = target_cmd->cmd_result;
goto lock_req_done;
}
s_priv->lock_req_step = 1;
target = s_priv->sent_by_target;
if (s1394_compare_swap(to_hal, target, cmd) !=
DDI_SUCCESS) {
tcmd_result = cmd->cmd_result;
goto lock_req_done;
} else {
TNF_PROBE_0_DEBUG(s1394_handle_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return;
}
} else {
/* Command failed for some reason */
tcmd_result = cmd->cmd_result;
goto lock_req_done;
}
} else { /* lock_req_step == 1 */
/* Was the request successful? */
if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
/* Do whatever's necessary to finish up the lock */
ret = s1394_finish_split_lock(cmd, target_cmd);
if (ret != DDI_SUCCESS) {
lock_req_step = 0;
goto lock_req_step_0;
} else {
tcmd_result = cmd->cmd_result;
goto lock_req_done;
}
} else {
/* Command failed for some reason */
tcmd_result = cmd->cmd_result;
goto lock_req_done;
}
}
lock_req_done:
if (s1394_free_cmd(to_hal, &cmd) != DDI_SUCCESS) {
TNF_PROBE_0(s1394_handle_lock_error_in_freecmd,
S1394_TNF_SL_ATREQ_ERROR, "");
}
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(target_cmd);
s_priv->cmd_in_use = B_FALSE;
target_cmd->cmd_result = tcmd_result;
/* Is this a blocking command? */
if (target_cmd->cmd_options & CMD1394_BLOCKING) {
/* Unblock the waiting command */
mutex_enter(&s_priv->blocking_mutex);
s_priv->blocking_flag = B_TRUE;
cv_signal(&s_priv->blocking_cv);
mutex_exit(&s_priv->blocking_mutex);
TNF_PROBE_0_DEBUG(s1394_handle_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return;
}
/* Call the target's completion_callback() */
if (target_cmd->completion_callback != NULL)
target_cmd->completion_callback(target_cmd);
TNF_PROBE_0_DEBUG(s1394_handle_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
}
/*
* s1394_pending_q_insert()
* is used to insert a given command structure onto a HAL's pending queue
* for later processing (after the bus reset). All commands returned by
* the HAL, are inserted onto the rear of the list (first priority), and
* all other commands (from targets during bus reset) are put onto the front.
*/
void
s1394_pending_q_insert(s1394_hal_t *hal, cmd1394_cmd_t *cmd, uint_t flags)
{
cmd1394_cmd_t *temp_cmd;
s1394_cmd_priv_t *s_priv;
s1394_cmd_priv_t *c_priv;
TNF_PROBE_0_DEBUG(s1394_pending_q_insert_enter,
S1394_TNF_SL_ATREQ_STACK, "");
mutex_enter(&hal->pending_q_mutex);
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Is the outstanding request queue empty? */
if ((hal->pending_q_head == NULL) && (hal->pending_q_tail == NULL)) {
hal->pending_q_head = (cmd1394_cmd_t *)cmd;
hal->pending_q_tail = (cmd1394_cmd_t *)cmd;
s_priv->cmd_priv_next = (cmd1394_cmd_t *)NULL;
s_priv->cmd_priv_prev = (cmd1394_cmd_t *)NULL;
} else if (flags == S1394_PENDING_Q_FRONT) {
s_priv->cmd_priv_next = hal->pending_q_head;
s_priv->cmd_priv_prev = (cmd1394_cmd_t *)NULL;
temp_cmd = (cmd1394_cmd_t *)hal->pending_q_head;
c_priv = (s1394_cmd_priv_t *)((uchar_t *)temp_cmd +
sizeof (cmd1394_cmd_t));
c_priv->cmd_priv_prev = (cmd1394_cmd_t *)cmd;
hal->pending_q_head = (cmd1394_cmd_t *)cmd;
} else {
s_priv->cmd_priv_prev = hal->pending_q_tail;
s_priv->cmd_priv_next = (cmd1394_cmd_t *)NULL;
temp_cmd = (cmd1394_cmd_t *)hal->pending_q_tail;
c_priv = (s1394_cmd_priv_t *)((uchar_t *)temp_cmd +
sizeof (cmd1394_cmd_t));
c_priv->cmd_priv_next = (cmd1394_cmd_t *)cmd;
hal->pending_q_tail = (cmd1394_cmd_t *)cmd;
}
mutex_exit(&hal->pending_q_mutex);
/* kstats - number of pending Q insertions */
hal->hal_kstats->pending_q_insert++;
TNF_PROBE_0_DEBUG(s1394_pending_q_insert_exit,
S1394_TNF_SL_ATREQ_STACK, "");
}
/*
* s1394_pending_q_remove()
* is used to remove a command structure from a HAL's pending queue for
* processing.
*/
static cmd1394_cmd_t *
s1394_pending_q_remove(s1394_hal_t *hal)
{
s1394_cmd_priv_t *s_priv;
s1394_cmd_priv_t *c_priv;
cmd1394_cmd_t *cmd;
cmd1394_cmd_t *prev_cmd;
TNF_PROBE_0_DEBUG(s1394_pending_q_remove_enter,
S1394_TNF_SL_ATREQ_STACK, "");
mutex_enter(&hal->pending_q_mutex);
cmd = (cmd1394_cmd_t *)hal->pending_q_tail;
if (cmd == NULL) {
mutex_exit(&hal->pending_q_mutex);
TNF_PROBE_0_DEBUG(s1394_pending_q_remove_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (NULL);
}
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
prev_cmd = (cmd1394_cmd_t *)s_priv->cmd_priv_prev;
s_priv->cmd_priv_prev = (cmd1394_cmd_t *)NULL;
s_priv->cmd_priv_next = (cmd1394_cmd_t *)NULL;
if (prev_cmd != NULL) {
c_priv = (s1394_cmd_priv_t *)((uchar_t *)prev_cmd +
sizeof (cmd1394_cmd_t));
c_priv->cmd_priv_next = (cmd1394_cmd_t *)NULL;
} else {
hal->pending_q_head = (cmd1394_cmd_t *)NULL;
}
hal->pending_q_tail = (cmd1394_cmd_t *)prev_cmd;
mutex_exit(&hal->pending_q_mutex);
TNF_PROBE_0_DEBUG(s1394_pending_q_remove_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (cmd);
}
/*
* s1394_resend_pending_cmds()
* is called when the pending queue is to be flushed. After most of the
* bus reset processing is completed, the pending commands are sent/resent.
*/
void
s1394_resend_pending_cmds(s1394_hal_t *hal)
{
int done;
TNF_PROBE_0_DEBUG(s1394_resend_pending_cmds_enter,
S1394_TNF_SL_BR_STACK, "");
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
do {
done = s1394_process_pending_q(hal);
} while (done == B_FALSE);
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_0_DEBUG(s1394_resend_pending_cmds_exit,
S1394_TNF_SL_BR_STACK, "");
}
/*
* s1394_process_pending_q()
* is called to send/resend the commands on the pending queue. All command
* handling can be done here, including notifying the target of failed
* commands, etc. If it is necessary to recompute the address, speed,
* or max_payload for a command, that can be done here too. And if there
* is no reason not to continue sending commands from the pending queue,
* then a B_FALSE is returned, else B_TRUE is returned.
*/
static boolean_t
s1394_process_pending_q(s1394_hal_t *hal)
{
s1394_cmd_priv_t *s_priv;
h1394_cmd_priv_t *h_priv;
s1394_target_t *target;
cmd1394_cmd_t *cmd;
uint64_t node;
uint32_t from_node;
uint32_t to_node;
uint_t current_max_payload;
int ret;
TNF_PROBE_0_DEBUG(s1394_process_pending_q_enter,
S1394_TNF_SL_BR_STACK, "");
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
/* Pull a command from the Pending Q */
cmd = s1394_pending_q_remove(hal);
if (cmd == NULL) {
TNF_PROBE_0_DEBUG(s1394_process_pending_q_exit,
S1394_TNF_SL_BR_STACK, "");
return (B_TRUE);
}
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Get a pointer to the HAL private struct */
h_priv = (h1394_cmd_priv_t *)&s_priv->hal_cmd_private;
if ((cmd->cmd_options & CMD1394_OVERRIDE_ADDR) ||
(cmd->cmd_options & CMD1394_CANCEL_ON_BUS_RESET)) {
if (h_priv->bus_generation == hal->generation_count) {
ret = s1394_pending_q_helper(hal, cmd);
return (ret);
} else {
s_priv->cmd_in_use = B_FALSE;
cmd->cmd_result = CMD1394_EBUSRESET;
/* Is this a blocking command? */
if (cmd->cmd_options & CMD1394_BLOCKING) {
/* Unblock the waiting command */
mutex_enter(&s_priv->blocking_mutex);
s_priv->blocking_flag = B_TRUE;
cv_signal(&s_priv->blocking_cv);
mutex_exit(&s_priv->blocking_mutex);
TNF_PROBE_0_DEBUG(s1394_process_pending_q_exit,
S1394_TNF_SL_BR_STACK, "");
return (B_FALSE);
}
/* Call the target's completion_callback() */
if (cmd->completion_callback != NULL) {
cmd->completion_callback(cmd);
}
TNF_PROBE_0_DEBUG(s1394_process_pending_q_exit,
S1394_TNF_SL_BR_STACK, "");
return (B_FALSE);
}
} else {
if (h_priv->bus_generation == hal->generation_count) {
ret = s1394_pending_q_helper(hal, cmd);
return (ret);
} else {
/* Make sure we can get the topology_tree_mutex */
if (s1394_lock_tree(hal) != DDI_SUCCESS)
return (B_TRUE);
/* Set the generation */
cmd->bus_generation = hal->generation_count;
/* Copy the generation into the HAL's private field */
h_priv->bus_generation = cmd->bus_generation;
target = s_priv->sent_by_target;
/* If not OVERRIDE_ADDR, then target may not be NULL */
ASSERT(target != NULL);
rw_enter(&hal->target_list_rwlock, RW_READER);
if (((target->target_state & S1394_TARG_GONE) == 0) &&
(target->on_node != NULL)) {
node = target->on_node->node_num;
rw_exit(&hal->target_list_rwlock);
} else {
rw_exit(&hal->target_list_rwlock);
s_priv->cmd_in_use = B_FALSE;
cmd->cmd_result = CMD1394_EDEVICE_REMOVED;
/* Is this a blocking command? */
if (cmd->cmd_options & CMD1394_BLOCKING) {
s1394_unlock_tree(hal);
/* Unblock the waiting command */
mutex_enter(&s_priv->blocking_mutex);
s_priv->blocking_flag = B_TRUE;
cv_signal(&s_priv->blocking_cv);
mutex_exit(&s_priv->blocking_mutex);
TNF_PROBE_0_DEBUG(
s1394_process_pending_q_exit,
S1394_TNF_SL_BR_STACK,
"");
return (B_FALSE);
}
/* Call the target's completion_callback() */
if (cmd->completion_callback != NULL) {
s1394_unlock_tree(hal);
cmd->completion_callback(cmd);
TNF_PROBE_0_DEBUG(
s1394_process_pending_q_exit,
S1394_TNF_SL_BR_STACK, "");
return (B_FALSE);
} else {
s1394_unlock_tree(hal);
TNF_PROBE_0_DEBUG(
s1394_process_pending_q_exit,
S1394_TNF_SL_BR_STACK, "");
return (B_FALSE);
}
}
/* Mask in the top 16-bits */
cmd->cmd_addr = cmd->cmd_addr &
IEEE1394_ADDR_OFFSET_MASK;
cmd->cmd_addr = cmd->cmd_addr |
(node << IEEE1394_ADDR_PHY_ID_SHIFT);
cmd->cmd_addr = cmd->cmd_addr |
IEEE1394_ADDR_BUS_ID_MASK;
/* Speed is to be filled in from speed map */
from_node = IEEE1394_NODE_NUM(hal->node_id);
to_node = (uint32_t)node;
/* Fill in the nodeID */
cmd->nodeID =
(cmd->cmd_addr & IEEE1394_ADDR_NODE_ID_MASK) >>
IEEE1394_ADDR_NODE_ID_SHIFT;
if (cmd->cmd_options & CMD1394_OVERRIDE_SPEED) {
s_priv->hal_cmd_private.speed =
(int)cmd->cmd_speed;
} else {
/* Speed is to be filled in from speed map */
s_priv->hal_cmd_private.speed =
(int)s1394_speed_map_get(hal, from_node,
to_node);
}
/* Is it a block request? */
if ((cmd->cmd_type == CMD1394_ASYNCH_RD_BLOCK) ||
(cmd->cmd_type == CMD1394_ASYNCH_WR_BLOCK)) {
/* Get a pointer to the HAL private struct */
h_priv = (h1394_cmd_priv_t *)&s_priv->
hal_cmd_private;
/* Handle the MAX_PAYLOAD size */
if (s_priv->sent_by_target != NULL) {
current_max_payload =
s_priv->sent_by_target->
current_max_payload;
} else {
current_max_payload = 4;
}
if (cmd->cmd_options &
CMD1394_OVERRIDE_MAX_PAYLOAD) {
if (current_max_payload >
cmd->cmd_u.b.max_payload)
current_max_payload =
cmd->cmd_u.b.max_payload;
}
if (s_priv->data_remaining <
current_max_payload) {
h_priv->mblk.length =
s_priv->data_remaining;
} else {
h_priv->mblk.length =
current_max_payload;
}
}
s1394_unlock_tree(hal);
ret = s1394_pending_q_helper(hal, cmd);
TNF_PROBE_0_DEBUG(s1394_process_pending_q_exit,
S1394_TNF_SL_BR_STACK, "");
return (ret);
}
}
}
/*
* s1394_pending_q_helper()
* is a "helper" function for s1394_process_pending_q(). It attempts to
* resend commands, handling error conditions whenever necessary.
*/
static boolean_t
s1394_pending_q_helper(s1394_hal_t *hal, cmd1394_cmd_t *cmd)
{
s1394_cmd_priv_t *s_priv;
int err;
int ret;
TNF_PROBE_0_DEBUG(s1394_pending_q_helper_enter,
S1394_TNF_SL_BR_STACK, "");
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Put cmd on outstanding request Q */
s1394_insert_q_asynch_cmd(hal, cmd);
/* Send command out again */
ret = s1394_xfer_asynch_command(hal, cmd, &err);
if (ret != DDI_SUCCESS) {
if (err == CMD1394_ESTALE_GENERATION) {
/* Remove cmd outstanding req Q */
s1394_remove_q_asynch_cmd(hal, cmd);
s1394_pending_q_insert(hal, cmd, S1394_PENDING_Q_FRONT);
TNF_PROBE_0_DEBUG(s1394_pending_q_helper_exit,
S1394_TNF_SL_BR_STACK, "");
return (B_TRUE);
} else {
/* Remove cmd from outstanding request Q */
s1394_remove_q_asynch_cmd(hal, cmd);
s_priv->cmd_in_use = B_FALSE;
cmd->cmd_result = err;
/* Is this a blocking command? */
if (cmd->cmd_options & CMD1394_BLOCKING) {
/* Unblock waiting command */
mutex_enter(&s_priv->blocking_mutex);
s_priv->blocking_flag = B_TRUE;
cv_signal(&s_priv->blocking_cv);
mutex_exit(&s_priv->blocking_mutex);
TNF_PROBE_0_DEBUG(s1394_pending_q_helper_exit,
S1394_TNF_SL_BR_STACK, "");
return (B_FALSE);
}
/* Call target completion_callback() */
if (cmd->completion_callback != NULL) {
cmd->completion_callback(cmd);
TNF_PROBE_0_DEBUG(s1394_pending_q_helper_exit,
S1394_TNF_SL_BR_STACK, "");
return (B_FALSE);
} else {
TNF_PROBE_0_DEBUG(s1394_pending_q_helper_exit,
S1394_TNF_SL_BR_STACK, "");
return (B_FALSE);
}
}
}
TNF_PROBE_0_DEBUG(s1394_pending_q_helper_exit,
S1394_TNF_SL_BR_STACK, "");
return (B_FALSE);
}
/*
* s1394_process_split_lock()
* is a "helper" function for the s1394_handle_lock() callback. Its
* job is to perform whatever manipulation is required for the given
* request.
*/
static int
s1394_process_split_lock(cmd1394_cmd_t *cmd, cmd1394_cmd_t *target_cmd)
{
uint64_t new_value64;
uint64_t data_value64;
uint64_t arg_value64;
uint64_t old_value64;
uint64_t temp_value64;
uint32_t new_value32;
uint32_t data_value32;
uint32_t arg_value32;
uint32_t old_value32;
uint32_t temp_value32;
TNF_PROBE_0_DEBUG(s1394_process_split_lock_enter,
S1394_TNF_SL_ATREQ_STACK, "");
if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) {
old_value32 = cmd->cmd_u.l32.old_value;
data_value32 = target_cmd->cmd_u.l32.data_value;
arg_value32 = target_cmd->cmd_u.l32.arg_value;
/* Lock type specific */
switch (target_cmd->cmd_u.l32.lock_type) {
case CMD1394_LOCK_BIT_AND:
new_value32 = old_value32 & data_value32;
break;
case CMD1394_LOCK_BIT_OR:
new_value32 = old_value32 | data_value32;
break;
case CMD1394_LOCK_BIT_XOR:
new_value32 = old_value32 ^ data_value32;
break;
case CMD1394_LOCK_INCREMENT:
old_value32 = T1394_DATA32(old_value32);
new_value32 = old_value32 + 1;
new_value32 = T1394_DATA32(new_value32);
old_value32 = T1394_DATA32(old_value32);
break;
case CMD1394_LOCK_DECREMENT:
old_value32 = T1394_DATA32(old_value32);
new_value32 = old_value32 - 1;
new_value32 = T1394_DATA32(new_value32);
old_value32 = T1394_DATA32(old_value32);
break;
case CMD1394_LOCK_ADD:
old_value32 = T1394_DATA32(old_value32);
new_value32 = old_value32 + data_value32;
new_value32 = T1394_DATA32(new_value32);
old_value32 = T1394_DATA32(old_value32);
break;
case CMD1394_LOCK_SUBTRACT:
old_value32 = T1394_DATA32(old_value32);
new_value32 = old_value32 - data_value32;
new_value32 = T1394_DATA32(new_value32);
old_value32 = T1394_DATA32(old_value32);
break;
case CMD1394_LOCK_THRESH_ADD:
old_value32 = T1394_DATA32(old_value32);
temp_value32 = (old_value32 + data_value32);
if ((temp_value32 >= old_value32) &&
(temp_value32 <= arg_value32)) {
new_value32 = T1394_DATA32(temp_value32);
old_value32 = T1394_DATA32(old_value32);
} else {
/* Failed threshold add */
target_cmd->cmd_u.l32.old_value =
T1394_DATA32(cmd->cmd_u.l32.old_value);
target_cmd->cmd_result = CMD1394_CMDSUCCESS;
TNF_PROBE_0_DEBUG(
s1394_process_split_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
break;
case CMD1394_LOCK_THRESH_SUBTRACT:
old_value32 = T1394_DATA32(old_value32);
temp_value32 = (old_value32 - data_value32);
if ((old_value32 >= data_value32) &&
(temp_value32 >= arg_value32)) {
new_value32 = T1394_DATA32(temp_value32);
old_value32 = T1394_DATA32(old_value32);
} else {
/* Failed threshold subtract */
target_cmd->cmd_u.l32.old_value =
T1394_DATA32(cmd->cmd_u.l32.old_value);
target_cmd->cmd_result = CMD1394_CMDSUCCESS;
TNF_PROBE_0_DEBUG(
s1394_process_split_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
break;
case CMD1394_LOCK_CLIP_ADD:
old_value32 = T1394_DATA32(old_value32);
temp_value32 = (old_value32 + data_value32);
if ((temp_value32 < old_value32) ||
(temp_value32 > arg_value32))
new_value32 = T1394_DATA32(arg_value32);
else
new_value32 = T1394_DATA32(temp_value32);
old_value32 = T1394_DATA32(old_value32);
break;
case CMD1394_LOCK_CLIP_SUBTRACT:
old_value32 = T1394_DATA32(old_value32);
temp_value32 = (old_value32 - data_value32);
if ((data_value32 > old_value32) ||
(temp_value32 < arg_value32))
new_value32 = T1394_DATA32(arg_value32);
else
new_value32 = T1394_DATA32(temp_value32);
old_value32 = T1394_DATA32(old_value32);
break;
}
/* Send compare-swap lock request */
cmd->cmd_u.l32.arg_value = old_value32;
cmd->cmd_u.l32.data_value = new_value32;
} else {
old_value64 = cmd->cmd_u.l64.old_value;
data_value64 = target_cmd->cmd_u.l64.data_value;
arg_value64 = target_cmd->cmd_u.l64.arg_value;
/* Lock type specific */
switch (target_cmd->cmd_u.l64.lock_type) {
case CMD1394_LOCK_BIT_AND:
new_value64 = old_value64 & data_value64;
break;
case CMD1394_LOCK_BIT_OR:
new_value64 = old_value64 | data_value64;
break;
case CMD1394_LOCK_BIT_XOR:
new_value64 = old_value64 ^ data_value64;
break;
case CMD1394_LOCK_INCREMENT:
old_value64 = T1394_DATA64(old_value64);
new_value64 = old_value64 + 1;
new_value64 = T1394_DATA64(new_value64);
old_value64 = T1394_DATA64(old_value64);
break;
case CMD1394_LOCK_DECREMENT:
old_value64 = T1394_DATA64(old_value64);
new_value64 = old_value64 - 1;
new_value64 = T1394_DATA64(new_value64);
old_value64 = T1394_DATA64(old_value64);
break;
case CMD1394_LOCK_ADD:
old_value64 = T1394_DATA64(old_value64);
new_value64 = old_value64 + data_value64;
new_value64 = T1394_DATA64(new_value64);
old_value64 = T1394_DATA64(old_value64);
break;
case CMD1394_LOCK_SUBTRACT:
old_value64 = T1394_DATA64(old_value64);
new_value64 = old_value64 - data_value64;
new_value64 = T1394_DATA64(new_value64);
old_value64 = T1394_DATA64(old_value64);
break;
case CMD1394_LOCK_THRESH_ADD:
old_value64 = T1394_DATA64(old_value64);
temp_value64 = (old_value64 + data_value64);
if ((temp_value64 >= old_value64) &&
(temp_value64 <= arg_value64)) {
new_value64 = T1394_DATA64(temp_value64);
old_value64 = T1394_DATA64(old_value64);
} else {
/* Failed threshold add */
target_cmd->cmd_u.l64.old_value =
T1394_DATA64(cmd->cmd_u.l64.old_value);
target_cmd->cmd_result = CMD1394_CMDSUCCESS;
TNF_PROBE_0_DEBUG(
s1394_process_split_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
break;
case CMD1394_LOCK_THRESH_SUBTRACT:
old_value64 = T1394_DATA64(old_value64);
temp_value64 = (old_value64 - data_value64);
if ((old_value64 >= data_value64) &&
(temp_value64 >= arg_value64)) {
new_value64 = T1394_DATA64(temp_value64);
old_value64 = T1394_DATA64(old_value64);
} else {
/* Failed threshold subtract */
target_cmd->cmd_u.l64.old_value =
T1394_DATA64(cmd->cmd_u.l64.old_value);
target_cmd->cmd_result = CMD1394_CMDSUCCESS;
TNF_PROBE_0_DEBUG(
s1394_process_split_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
}
break;
case CMD1394_LOCK_CLIP_ADD:
old_value64 = T1394_DATA64(old_value64);
temp_value64 = (old_value64 + data_value64);
if ((temp_value64 < old_value64) ||
(temp_value64 > arg_value64))
new_value64 = T1394_DATA64(arg_value64);
else
new_value64 = T1394_DATA64(temp_value64);
old_value64 = T1394_DATA64(old_value64);
break;
case CMD1394_LOCK_CLIP_SUBTRACT:
old_value64 = T1394_DATA64(old_value64);
temp_value64 = (old_value64 - data_value64);
if ((data_value64 > old_value64) ||
(temp_value64 < arg_value64))
new_value64 = T1394_DATA64(arg_value64);
else
new_value64 = T1394_DATA64(temp_value64);
old_value64 = T1394_DATA64(old_value64);
break;
}
/* Send compare-swap lock request */
cmd->cmd_u.l64.arg_value = old_value64;
cmd->cmd_u.l64.data_value = new_value64;
}
TNF_PROBE_0_DEBUG(s1394_process_split_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
}
/*
* s1394_finish_split_lock()
* is another "helper" function for the s1394_handle_lock() callback.
* Its job is to finish up whatever lock request procesing is necessary.
*/
static int
s1394_finish_split_lock(cmd1394_cmd_t *cmd, cmd1394_cmd_t *target_cmd)
{
s1394_cmd_priv_t *s_priv;
uint64_t tmp_value64;
uint32_t tmp_value32;
TNF_PROBE_0_DEBUG(s1394_finish_split_lock_enter,
S1394_TNF_SL_ATREQ_STACK, "");
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
if (((cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) &&
(cmd->cmd_u.l32.old_value == cmd->cmd_u.l32.arg_value)) ||
((cmd->cmd_type == CMD1394_ASYNCH_LOCK_64) &&
(cmd->cmd_u.l64.old_value == cmd->cmd_u.l64.arg_value))) {
if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) {
switch (cmd->cmd_u.l32.lock_type) {
case CMD1394_LOCK_INCREMENT:
case CMD1394_LOCK_DECREMENT:
case CMD1394_LOCK_ADD:
case CMD1394_LOCK_SUBTRACT:
case CMD1394_LOCK_THRESH_ADD:
case CMD1394_LOCK_THRESH_SUBTRACT:
case CMD1394_LOCK_CLIP_ADD:
case CMD1394_LOCK_CLIP_SUBTRACT:
tmp_value32 = cmd->cmd_u.l32.old_value;
tmp_value32 = T1394_DATA32(tmp_value32);
target_cmd->cmd_u.l32.old_value = tmp_value32;
break;
default:
tmp_value32 = cmd->cmd_u.l32.old_value;
target_cmd->cmd_u.l32.old_value = tmp_value32;
break;
}
} else {
switch (cmd->cmd_u.l64.lock_type) {
case CMD1394_LOCK_INCREMENT:
case CMD1394_LOCK_DECREMENT:
case CMD1394_LOCK_ADD:
case CMD1394_LOCK_SUBTRACT:
case CMD1394_LOCK_THRESH_ADD:
case CMD1394_LOCK_THRESH_SUBTRACT:
case CMD1394_LOCK_CLIP_ADD:
case CMD1394_LOCK_CLIP_SUBTRACT:
tmp_value64 = cmd->cmd_u.l64.old_value;
tmp_value64 = T1394_DATA64(tmp_value64);
target_cmd->cmd_u.l64.old_value = tmp_value64;
break;
default:
tmp_value64 = cmd->cmd_u.l64.old_value;
target_cmd->cmd_u.l64.old_value = tmp_value64;
break;
}
}
/* Set status */
target_cmd->cmd_result = CMD1394_CMDSUCCESS;
TNF_PROBE_0_DEBUG(s1394_finish_split_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
} else {
if (s_priv->temp_num_retries > 0) {
/* Decrement retry count */
s_priv->temp_num_retries--;
/* Reset lock_req_step */
s_priv->lock_req_step = 0;
TNF_PROBE_0_DEBUG(s1394_finish_split_lock_start_over,
S1394_TNF_SL_ATREQ_STACK, "");
/* Resend... start at step 0 again */
TNF_PROBE_0_DEBUG(s1394_finish_split_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_FAILURE);
} else {
/* Failed... RETRIES_EXCEEDED */
target_cmd->cmd_result = CMD1394_ERETRIES_EXCEEDED;
TNF_PROBE_0_DEBUG(s1394_finish_split_lock_exit,
S1394_TNF_SL_ATREQ_STACK, "");
return (DDI_SUCCESS);
}
}
}