root/usr/src/uts/common/io/1394/s1394_asynch.c
/*
 * 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.
 */

/*
 * 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/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;

        alloc_sleep = (flags & T1394_ALLOC_CMD_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP;

        if ((alloc_sleep == KM_SLEEP) &&
            (servicing_interrupt())) {
                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)) {
                return (DDI_FAILURE);
        }

        *cmdp = kmem_cache_alloc(hal->hal_kmem_cachep, alloc_sleep);
        if (*cmdp == NULL) {
                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;

        /* kstats - number of cmd allocs */
        hal->hal_kstats->cmd_alloc++;

        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;

        /* 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) {
                ASSERT(s_priv->cmd_in_use == B_FALSE);
                return (DDI_FAILURE);
        }

        /* 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++;

        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;

        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);
                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;

                return (DDI_FAILURE);
        }

        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;
                }

                return (DDI_FAILURE);
        }

        /* No errors, return success */
        *err = CMD1394_NOSTATUS;

        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;

        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;
                return (DDI_FAILURE);
        }

        /* Check for potential address roll-over */
        if (s1394_address_rollover(cmd) != B_FALSE) {
                *err = CMD1394_EADDRESS_ERROR;
                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;
                        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;
                        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;
                        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;
                        return (DDI_FAILURE);
                }

                /* Also need to check for MBLK_TOO_SMALL */
                if (s1394_mblk_too_small(cmd) != B_FALSE) {
                        *err = CMD1394_EMBLK_TOO_SMALL;
                        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);

        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;

        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);
}

/*
 * 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;

        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);
}

/*
 * 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;

        /* 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);
                        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);

                        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);

                        return;
                }

                /* Call the target's completion_callback() */
                if (req->completion_callback != NULL) {
                        req->completion_callback(req);
                }

                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) {
                                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);

                                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);

                return;
        }

        /* Set status and call completion_callback() */
        if (req->completion_callback != NULL) {

                req->completion_callback(req);

                return;
        }
}

/*
 * 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;

        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);

                        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) {
                                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);

                        return;
                }
        }

        /* Free the command - Pass it back to the HAL */
        HAL_CALL(hal).response_complete(hal->halinfo.hal_private, resp, h_priv);
}

/*
 * 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;

        /* 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)))) {

                /* Free the command - Pass it back to the HAL */
                HAL_CALL(hal).response_complete(hal->halinfo.hal_private,
                    resp, h_priv);

                return (DDI_SUCCESS);
        }

        /* 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);

                return (DDI_FAILURE);
        }

        /* Unable to send a response */
        if (ret != DDI_SUCCESS) {
                /* Free the command - Pass it back to the HAL */
                HAL_CALL(hal).response_complete(hal->halinfo.hal_private,
                    resp, h_priv);

                return (DDI_FAILURE);
        }

        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;

        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;

                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;

                                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);

                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);

                        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;

                        return (DDI_FAILURE);
                }
        } else {
                /* Block (if necessary) */
                s1394_block_on_asynch_cmd(cmd);

                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;

        /* Allocate a temporary command */
        if (s1394_alloc_cmd(hal, T1394_ALLOC_CMD_NOSLEEP, &tmp_cmd) !=
            DDI_SUCCESS) {
                cmd->cmd_result = CMD1394_EUNKNOWN_ERROR;

                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;

                return (DDI_FAILURE);
        }

        /* Block (if necessary) */
        s1394_block_on_asynch_cmd(cmd);

        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;

        /* 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 {
                                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:
        (void) s1394_free_cmd(to_hal, &cmd);

        /* 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);

                return;
        }

        /* Call the target's completion_callback() */
        if (target_cmd->completion_callback != NULL)
                target_cmd->completion_callback(target_cmd);
}

/*
 * 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;

        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++;
}

/*
 * 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;

        mutex_enter(&hal->pending_q_mutex);

        cmd = (cmd1394_cmd_t *)hal->pending_q_tail;
        if (cmd == NULL) {
                mutex_exit(&hal->pending_q_mutex);
                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);

        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;

        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));
}

/*
 * 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;

        ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));

        /* Pull a command from the Pending Q */
        cmd = s1394_pending_q_remove(hal);

        if (cmd == NULL) {
                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);

                                return (B_FALSE);
                        }

                        /* Call the target's completion_callback() */
                        if (cmd->completion_callback != NULL) {
                                cmd->completion_callback(cmd);
                        }

                        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);

                                        return (B_FALSE);
                                }

                                /* Call the target's completion_callback() */
                                if (cmd->completion_callback != NULL) {
                                        s1394_unlock_tree(hal);
                                        cmd->completion_callback(cmd);
                                        return (B_FALSE);
                                } else {
                                        s1394_unlock_tree(hal);
                                        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);
                        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;

        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);

                        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);

                                return (B_FALSE);
                        }

                        /* Call target completion_callback() */
                        if (cmd->completion_callback != NULL) {
                                cmd->completion_callback(cmd);
                                return (B_FALSE);
                        } else {
                                return (B_FALSE);
                        }
                }
        }

        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;

        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;
                                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;
                                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;
                                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;
                                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;
        }

        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;

        /* 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;
                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;

                        /* Resend... start at step 0 again */
                        return (DDI_FAILURE);
                } else {
                        /* Failed... RETRIES_EXCEEDED */
                        target_cmd->cmd_result = CMD1394_ERETRIES_EXCEEDED;
                        return (DDI_SUCCESS);
                }
        }
}