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

/*
 * t1394.c
 *    1394 Target Driver Interface
 *    This file contains all of the 1394 Software Framework routines called
 *    by target drivers
 */

#include <sys/sysmacros.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.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>

static int s1394_allow_detach = 0;

/*
 * Function:    t1394_attach()
 * Input(s):    dip                     The dip given to the target driver
 *                                          in it's attach() routine
 *              version                 The version of the target driver -
 *                                          T1394_VERSION_V1
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   attachinfo              Used to pass info back to target,
 *                                          including bus generation, local
 *                                          node ID, dma attribute, etc.
 *              t1394_hdl               The target "handle" to be used for
 *                                          all subsequent calls into the
 *                                          1394 Software Framework
 *
 * Description: t1394_attach() registers the target (based on its dip) with
 *              the 1394 Software Framework.  It returns the bus_generation,
 *              local_nodeID, iblock_cookie and other useful information to
 *              the target, as well as a handle (t1394_hdl) that will be used
 *              in all subsequent calls into this framework.
 */
/* ARGSUSED */
int
t1394_attach(dev_info_t *dip, int version, uint_t flags,
    t1394_attachinfo_t *attachinfo, t1394_handle_t *t1394_hdl)
{
        s1394_hal_t     *hal;
        s1394_target_t  *target;
        uint_t          dev;
        uint_t          curr;
        uint_t          unit_dir;
        int             hp_node = 0;

        ASSERT(t1394_hdl != NULL);
        ASSERT(attachinfo != NULL);

        *t1394_hdl = NULL;

        if (version != T1394_VERSION_V1) {
                return (DDI_FAILURE);
        }

        hal = s1394_dip_to_hal(ddi_get_parent(dip));
        if (hal == NULL) {
                return (DDI_FAILURE);
        }

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

        hp_node = ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
            "hp-node");

        /* Allocate space for s1394_target_t */
        target = kmem_zalloc(sizeof (s1394_target_t), KM_SLEEP);

        mutex_enter(&hal->topology_tree_mutex);

        target->target_version = version;

        /* Copy in the params */
        target->target_dip = dip;
        target->on_hal     = hal;

        /* Place the target on the appropriate node */
        target->on_node = NULL;

        rw_enter(&target->on_hal->target_list_rwlock, RW_WRITER);
        if (hp_node != 0) {
                s1394_add_target_to_node(target);
                /*
                 * on_node can be NULL if the node got unplugged
                 * while the target driver is in its attach routine.
                 */
                if (target->on_node == NULL) {
                        s1394_remove_target_from_node(target);
                        rw_exit(&target->on_hal->target_list_rwlock);
                        mutex_exit(&hal->topology_tree_mutex);
                        kmem_free(target, sizeof (s1394_target_t));
                        return (DDI_FAILURE);
                }

                target->target_state = S1394_TARG_HP_NODE;
                if (S1394_NODE_BUS_PWR_CONSUMER(target->on_node) == B_TRUE)
                        target->target_state |= S1394_TARG_BUS_PWR_CONSUMER;
        }

        /* Return the current generation */
        attachinfo->localinfo.bus_generation = target->on_hal->generation_count;

        /* Fill in hal node id */
        attachinfo->localinfo.local_nodeID = target->on_hal->node_id;

        /* Give the target driver the iblock_cookie */
        attachinfo->iblock_cookie = target->on_hal->halinfo.hw_interrupt;

        /* Give the target driver the attributes */
        attachinfo->acc_attr    = target->on_hal->halinfo.acc_attr;
        attachinfo->dma_attr    = target->on_hal->halinfo.dma_attr;

        unit_dir = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
            DDI_PROP_DONTPASS, "unit-dir-offset", 0);
        target->unit_dir = unit_dir;

        /* By default, disable all physical AR requests */
        target->physical_arreq_enabled = 0;


        /* Get dev_max_payload & current_max_payload */
        s1394_get_maxpayload(target, &dev, &curr);
        target->dev_max_payload         = dev;
        target->current_max_payload     = curr;

        /* Add into linked list */
        if ((target->on_hal->target_head == NULL) &&
            (target->on_hal->target_tail == NULL)) {
                target->on_hal->target_head = target;
                target->on_hal->target_tail = target;
        } else {
                target->on_hal->target_tail->target_next = target;
                target->target_prev = target->on_hal->target_tail;
                target->on_hal->target_tail = target;
        }
        rw_exit(&target->on_hal->target_list_rwlock);

        /* Fill in services layer private info */
        *t1394_hdl = (t1394_handle_t)target;

        mutex_exit(&hal->topology_tree_mutex);

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_detach()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   DDI_SUCCESS             Target successfully detached
 *              DDI_FAILURE             Target failed to detach
 *
 * Description: t1394_detach() unregisters the target from the 1394 Software
 *              Framework.  t1394_detach() can fail if the target has any
 *              allocated commands that haven't been freed.
 */
/* ARGSUSED */
int
t1394_detach(t1394_handle_t *t1394_hdl, uint_t flags)
{
        s1394_target_t  *target;
        uint_t          num_cmds;

        ASSERT(t1394_hdl != NULL);

        target = (s1394_target_t *)(*t1394_hdl);

        ASSERT(target->on_hal);

        mutex_enter(&target->on_hal->topology_tree_mutex);
        rw_enter(&target->on_hal->target_list_rwlock, RW_WRITER);

        /* How many cmds has this target allocated? */
        num_cmds = target->target_num_cmds;

        if (num_cmds != 0) {
                rw_exit(&target->on_hal->target_list_rwlock);
                mutex_exit(&target->on_hal->topology_tree_mutex);
                return (DDI_FAILURE);
        }

        /*
         * Remove from linked lists. Topology tree is already locked
         * so that the node won't go away while we are looking at it.
         */
        if ((target->on_hal->target_head == target) &&
            (target->on_hal->target_tail == target)) {
                target->on_hal->target_head = NULL;
                target->on_hal->target_tail = NULL;
        } else {
                if (target->target_prev)
                        target->target_prev->target_next = target->target_next;
                if (target->target_next)
                        target->target_next->target_prev = target->target_prev;
                if (target->on_hal->target_head == target)
                        target->on_hal->target_head = target->target_next;
                if (target->on_hal->target_tail == target)
                        target->on_hal->target_tail = target->target_prev;
        }

        s1394_remove_target_from_node(target);
        rw_exit(&target->on_hal->target_list_rwlock);

        mutex_exit(&target->on_hal->topology_tree_mutex);

        /* Free memory */
        kmem_free(target, sizeof (s1394_target_t));

        *t1394_hdl = NULL;

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_alloc_cmd()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              flags                   The flags parameter is described below
 *
 * Output(s):   cmdp                    Pointer to the newly allocated command
 *
 * Description: t1394_alloc_cmd() allocates a command for use with the
 *              t1394_read(), t1394_write(), or t1394_lock() interfaces
 *              of the 1394 Software Framework.  By default, t1394_alloc_cmd()
 *              may sleep while allocating memory for the command structure.
 *              If this is undesirable, the target may set the
 *              T1394_ALLOC_CMD_NOSLEEP bit in the flags parameter.  Also,
 *              this call may fail because a target driver has already
 *              allocated MAX_NUMBER_ALLOC_CMDS commands.
 */
int
t1394_alloc_cmd(t1394_handle_t t1394_hdl, uint_t flags, cmd1394_cmd_t **cmdp)
{
        s1394_hal_t      *hal;
        s1394_target_t   *target;
        s1394_cmd_priv_t *s_priv;
        uint_t           num_cmds;

        ASSERT(t1394_hdl != NULL);

        target = (s1394_target_t *)t1394_hdl;

        /* Find the HAL this target resides on */
        hal = target->on_hal;

        rw_enter(&hal->target_list_rwlock, RW_WRITER);

        /* How many cmds has this target allocated? */
        num_cmds = target->target_num_cmds;

        if (num_cmds >= MAX_NUMBER_ALLOC_CMDS) {
                rw_exit(&hal->target_list_rwlock);
                /* kstats - cmd alloc failures */
                hal->hal_kstats->cmd_alloc_fail++;
                return (DDI_FAILURE);
        }

        /* Increment the number of cmds this target has allocated? */
        target->target_num_cmds = num_cmds + 1;

        if (s1394_alloc_cmd(hal, flags, cmdp) != DDI_SUCCESS) {
                target->target_num_cmds = num_cmds;     /* Undo increment */
                rw_exit(&hal->target_list_rwlock);
                /* kstats - cmd alloc failures */
                hal->hal_kstats->cmd_alloc_fail++;
                return (DDI_FAILURE);
        }

        rw_exit(&hal->target_list_rwlock);

        /* Get the Services Layer private area */
        s_priv = S1394_GET_CMD_PRIV(*cmdp);

        /* Initialize the command's blocking mutex */
        mutex_init(&s_priv->blocking_mutex, NULL, MUTEX_DRIVER,
            hal->halinfo.hw_interrupt);

        /* Initialize the command's blocking condition variable */
        cv_init(&s_priv->blocking_cv, NULL, CV_DRIVER, NULL);

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_free_cmd()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              flags                   The flags parameter is unused (for now)
 *              cmdp                    Pointer to the command to be freed
 *
 * Output(s):   DDI_SUCCESS             Target successfully freed command
 *              DDI_FAILURE             Target failed to free command
 *
 * Description: t1394_free_cmd() attempts to free a command that has previously
 *              been allocated by the target driver.  It is possible for
 *              t1394_free_cmd() to fail because the command is currently
 *              in-use by the 1394 Software Framework.
 */
/* ARGSUSED */
int
t1394_free_cmd(t1394_handle_t t1394_hdl, uint_t flags, cmd1394_cmd_t **cmdp)
{
        s1394_hal_t      *hal;
        s1394_target_t   *target;
        s1394_cmd_priv_t *s_priv;
        uint_t           num_cmds;

        ASSERT(t1394_hdl != NULL);

        target = (s1394_target_t *)t1394_hdl;

        /* Find the HAL this target resides on */
        hal = target->on_hal;

        rw_enter(&hal->target_list_rwlock, RW_WRITER);

        /* How many cmds has this target allocated? */
        num_cmds = target->target_num_cmds;

        if (num_cmds == 0) {
                rw_exit(&hal->target_list_rwlock);
                ASSERT(num_cmds != 0);
                return (DDI_FAILURE);
        }

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

        /* Decrement the number of cmds this target has allocated */
        target->target_num_cmds--;

        rw_exit(&hal->target_list_rwlock);

        /* Destroy the command's blocking condition variable */
        cv_destroy(&s_priv->blocking_cv);

        /* Destroy the command's blocking mutex */
        mutex_destroy(&s_priv->blocking_mutex);

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

/*
 * Function:    t1394_read()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              cmd                     Pointer to the command to send
 *
 * Output(s):   DDI_SUCCESS             Target successful sent the command
 *              DDI_FAILURE             Target failed to send command
 *
 * Description: t1394_read() attempts to send an asynchronous read request
 *              onto the 1394 bus.
 */
int
t1394_read(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
{
        s1394_hal_t       *to_hal;
        s1394_target_t    *target;
        s1394_cmd_priv_t  *s_priv;
        s1394_hal_state_t state;
        int               ret;
        int               err;

        ASSERT(t1394_hdl != NULL);
        ASSERT(cmd != NULL);

        /* Get the Services Layer private area */
        s_priv = S1394_GET_CMD_PRIV(cmd);

        /* Is this command currently in use? */
        if (s_priv->cmd_in_use == B_TRUE) {
                ASSERT(s_priv->cmd_in_use == B_FALSE);
                return (DDI_FAILURE);
        }

        target = (s1394_target_t *)t1394_hdl;

        /* Set-up the destination of the command */
        to_hal = target->on_hal;

        /* No status (default) */
        cmd->cmd_result = CMD1394_NOSTATUS;

        /* Check for proper command type */
        if ((cmd->cmd_type != CMD1394_ASYNCH_RD_QUAD) &&
            (cmd->cmd_type != CMD1394_ASYNCH_RD_BLOCK)) {
                cmd->cmd_result = CMD1394_EINVALID_COMMAND;
                return (DDI_FAILURE);
        }

        /* Is this a blocking command on interrupt stack? */
        if ((cmd->cmd_options & CMD1394_BLOCKING) &&
            (servicing_interrupt())) {
                cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
                return (DDI_FAILURE);
        }

        mutex_enter(&to_hal->topology_tree_mutex);
        state = to_hal->hal_state;
        if (state != S1394_HAL_NORMAL) {
                ret = s1394_HAL_asynch_error(to_hal, cmd, state);
                if (ret != CMD1394_CMDSUCCESS) {
                        cmd->cmd_result = ret;
                        mutex_exit(&to_hal->topology_tree_mutex);
                        return (DDI_FAILURE);
                }
        }

        ret = s1394_setup_asynch_command(to_hal, target, cmd,
            S1394_CMD_READ, &err);

        /* Command has now been put onto the queue! */
        if (ret != DDI_SUCCESS) {
                /* Copy error code into result */
                cmd->cmd_result = err;
                mutex_exit(&to_hal->topology_tree_mutex);
                return (DDI_FAILURE);
        }

        /*
         * 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(to_hal, cmd);
                /* Are we on the bus reset event stack? */
                if (s1394_on_br_thread(to_hal) == B_TRUE) {
                        /* Blocking commands are not allowed */
                        if (cmd->cmd_options & CMD1394_BLOCKING) {
                                mutex_exit(&to_hal->topology_tree_mutex);
                                s_priv->cmd_in_use = B_FALSE;
                                cmd->cmd_result    = CMD1394_EINVALID_CONTEXT;
                                return (DDI_FAILURE);
                        }
                }

                s1394_pending_q_insert(to_hal, cmd, S1394_PENDING_Q_FRONT);
                mutex_exit(&to_hal->topology_tree_mutex);

                /* Block (if necessary) */
                goto block_on_asynch_cmd;
        }
        mutex_exit(&to_hal->topology_tree_mutex);

        /* Send the command out */
        ret = s1394_xfer_asynch_command(to_hal, cmd, &err);

        if (ret != DDI_SUCCESS) {
                if (err == CMD1394_ESTALE_GENERATION) {
                        /* Remove cmd from outstanding request Q */
                        s1394_remove_q_asynch_cmd(to_hal, cmd);
                        s1394_pending_q_insert(to_hal, cmd,
                            S1394_PENDING_Q_FRONT);

                        /* Block (if necessary) */
                        goto block_on_asynch_cmd;

                } else {
                        /* Remove cmd from outstanding request Q */
                        s1394_remove_q_asynch_cmd(to_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) */
                goto block_on_asynch_cmd;
        }

block_on_asynch_cmd:
        s1394_block_on_asynch_cmd(cmd);

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_write()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              cmd                     Pointer to the command to send
 *
 * Output(s):   DDI_SUCCESS             Target successful sent the command
 *              DDI_FAILURE             Target failed to send command
 *
 * Description: t1394_write() attempts to send an asynchronous write request
 *              onto the 1394 bus.
 */
int
t1394_write(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
{
        s1394_hal_t       *to_hal;
        s1394_target_t    *target;
        s1394_cmd_priv_t  *s_priv;
        s1394_hal_state_t state;
        int               ret;
        int               err;

        ASSERT(t1394_hdl != NULL);
        ASSERT(cmd != NULL);

        /* Get the Services Layer private area */
        s_priv = S1394_GET_CMD_PRIV(cmd);

        /* Is this command currently in use? */
        if (s_priv->cmd_in_use == B_TRUE) {
                ASSERT(s_priv->cmd_in_use == B_FALSE);
                return (DDI_FAILURE);
        }

        target = (s1394_target_t *)t1394_hdl;

        /* Set-up the destination of the command */
        to_hal = target->on_hal;

        /* Is this an FA request? */
        if (s_priv->cmd_ext_type == S1394_CMD_EXT_FA) {
                if (S1394_IS_CMD_FCP(s_priv) &&
                    (s1394_fcp_write_check_cmd(cmd) != DDI_SUCCESS)) {
                        return (DDI_FAILURE);
                }
                s1394_fa_convert_cmd(to_hal, cmd);
        }

        /* No status (default) */
        cmd->cmd_result = CMD1394_NOSTATUS;

        /* Check for proper command type */
        if ((cmd->cmd_type != CMD1394_ASYNCH_WR_QUAD) &&
            (cmd->cmd_type != CMD1394_ASYNCH_WR_BLOCK)) {
                cmd->cmd_result = CMD1394_EINVALID_COMMAND;
                s1394_fa_check_restore_cmd(to_hal, cmd);
                return (DDI_FAILURE);
        }

        /* Is this a blocking command on interrupt stack? */
        if ((cmd->cmd_options & CMD1394_BLOCKING) &&
            (servicing_interrupt())) {
                cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
                s1394_fa_check_restore_cmd(to_hal, cmd);
                return (DDI_FAILURE);
        }

        mutex_enter(&to_hal->topology_tree_mutex);
        state = to_hal->hal_state;
        if (state != S1394_HAL_NORMAL) {
                ret = s1394_HAL_asynch_error(to_hal, cmd, state);
                if (ret != CMD1394_CMDSUCCESS) {
                        cmd->cmd_result = ret;
                        mutex_exit(&to_hal->topology_tree_mutex);
                        s1394_fa_check_restore_cmd(to_hal, cmd);
                        return (DDI_FAILURE);
                }
        }

        ret = s1394_setup_asynch_command(to_hal, target, cmd,
            S1394_CMD_WRITE, &err);

        /* Command has now been put onto the queue! */
        if (ret != DDI_SUCCESS) {
                /* Copy error code into result */
                cmd->cmd_result = err;
                mutex_exit(&to_hal->topology_tree_mutex);
                s1394_fa_check_restore_cmd(to_hal, cmd);
                return (DDI_FAILURE);
        }

        /*
         * 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(to_hal, cmd);
                /* Are we on the bus reset event stack? */
                if (s1394_on_br_thread(to_hal) == B_TRUE) {
                        /* Blocking commands are not allowed */
                        if (cmd->cmd_options & CMD1394_BLOCKING) {
                                mutex_exit(&to_hal->topology_tree_mutex);
                                s_priv->cmd_in_use = B_FALSE;
                                cmd->cmd_result    = CMD1394_EINVALID_CONTEXT;
                                s1394_fa_check_restore_cmd(to_hal, cmd);
                                return (DDI_FAILURE);
                        }
                }

                s1394_pending_q_insert(to_hal, cmd, S1394_PENDING_Q_FRONT);
                mutex_exit(&to_hal->topology_tree_mutex);

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

                return (DDI_SUCCESS);
        }
        mutex_exit(&to_hal->topology_tree_mutex);

        /* Send the command out */
        ret = s1394_xfer_asynch_command(to_hal, cmd, &err);

        if (ret != DDI_SUCCESS) {
                if (err == CMD1394_ESTALE_GENERATION) {
                        /* Remove cmd from outstanding request Q */
                        s1394_remove_q_asynch_cmd(to_hal, cmd);
                        s1394_pending_q_insert(to_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(to_hal, cmd);

                        s_priv->cmd_in_use = B_FALSE;

                        /* Copy error code into result */
                        cmd->cmd_result = err;

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

                return (DDI_SUCCESS);
        }
}

/*
 * Function:    t1394_lock()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              cmd                     Pointer to the command to send
 *
 * Output(s):   DDI_SUCCESS             Target successful sent the command
 *              DDI_FAILURE             Target failed to send command
 *
 * Description: t1394_lock() attempts to send an asynchronous lock request
 *              onto the 1394 bus.
 */
int
t1394_lock(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
{
        s1394_hal_t         *to_hal;
        s1394_target_t      *target;
        s1394_cmd_priv_t    *s_priv;
        s1394_hal_state_t   state;
        cmd1394_lock_type_t lock_type;
        uint_t              num_retries;
        int                 ret;

        ASSERT(t1394_hdl != NULL);
        ASSERT(cmd != NULL);

        /* Get the Services Layer private area */
        s_priv = S1394_GET_CMD_PRIV(cmd);

        /* Is this command currently in use? */
        if (s_priv->cmd_in_use == B_TRUE) {
                ASSERT(s_priv->cmd_in_use == B_FALSE);
                return (DDI_FAILURE);
        }

        target = (s1394_target_t *)t1394_hdl;

        /* Set-up the destination of the command */
        to_hal = target->on_hal;

        mutex_enter(&to_hal->topology_tree_mutex);
        state = to_hal->hal_state;
        if (state != S1394_HAL_NORMAL) {
                ret = s1394_HAL_asynch_error(to_hal, cmd, state);
                if (ret != CMD1394_CMDSUCCESS) {
                        cmd->cmd_result = ret;
                        mutex_exit(&to_hal->topology_tree_mutex);
                        return (DDI_FAILURE);
                }
        }
        mutex_exit(&to_hal->topology_tree_mutex);

        /* Check for proper command type */
        if ((cmd->cmd_type != CMD1394_ASYNCH_LOCK_32) &&
            (cmd->cmd_type != CMD1394_ASYNCH_LOCK_64)) {
                cmd->cmd_result = CMD1394_EINVALID_COMMAND;
                return (DDI_FAILURE);
        }

        /* No status (default) */
        cmd->cmd_result = CMD1394_NOSTATUS;

        /* Is this a blocking command on interrupt stack? */
        if ((cmd->cmd_options & CMD1394_BLOCKING) &&
            (servicing_interrupt())) {
                cmd->cmd_result = CMD1394_EINVALID_CONTEXT;
                return (DDI_FAILURE);
        }

        if (cmd->cmd_type == CMD1394_ASYNCH_LOCK_32) {
                lock_type       = cmd->cmd_u.l32.lock_type;
                num_retries     = cmd->cmd_u.l32.num_retries;
        } else {        /* (cmd->cmd_type == CMD1394_ASYNCH_LOCK_64) */
                lock_type       = cmd->cmd_u.l64.lock_type;
                num_retries     = cmd->cmd_u.l64.num_retries;
        }

        /* Make sure num_retries is reasonable */
        ASSERT(num_retries <= MAX_NUMBER_OF_LOCK_RETRIES);

        switch (lock_type) {
        case CMD1394_LOCK_MASK_SWAP:
        case CMD1394_LOCK_FETCH_ADD:
        case CMD1394_LOCK_LITTLE_ADD:
        case CMD1394_LOCK_BOUNDED_ADD:
        case CMD1394_LOCK_WRAP_ADD:
        case CMD1394_LOCK_COMPARE_SWAP:
                ret = s1394_compare_swap(to_hal, target, cmd);
                break;

        case CMD1394_LOCK_BIT_AND:
        case CMD1394_LOCK_BIT_OR:
        case CMD1394_LOCK_BIT_XOR:
        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:
                ret = s1394_split_lock_req(to_hal, target, cmd);
                break;

        default:
                cmd->cmd_result = CMD1394_EINVALID_COMMAND;
                ret = DDI_FAILURE;
                break;
        }

        return (ret);
}

/*
 * Function:    t1394_alloc_addr()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              addr_allocp             The structure used to specify the type,
 *                                          size, permissions, and callbacks
 *                                          (if any) for the requested block
 *                                          of 1394 address space
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   result                  Used to pass more specific info back
 *                                          to target
 *
 * Description: t1394_alloc_addr() requests that part of the 1394 Address Space
 *              on the local node be set aside for this target driver, and
 *              associated with this address space should be some permissions
 *              and callbacks.  If the request is unable to be fulfilled,
 *              t1394_alloc_addr() will return DDI_FAILURE and result will
 *              indicate the reason.  T1394_EINVALID_PARAM indicates that the
 *              combination of flags given is invalid, and T1394_EALLOC_ADDR
 *              indicates that the requested type of address space is
 *              unavailable.
 */
/* ARGSUSED */
int
t1394_alloc_addr(t1394_handle_t t1394_hdl, t1394_alloc_addr_t *addr_allocp,
    uint_t flags, int *result)
{
        s1394_hal_t     *hal;
        s1394_target_t  *target;
        uint64_t        addr_lo;
        uint64_t        addr_hi;
        int             err;

        ASSERT(t1394_hdl != NULL);
        ASSERT(addr_allocp != NULL);

        target = (s1394_target_t *)t1394_hdl;

        /* Find the HAL this target resides on */
        hal = target->on_hal;

        /* Get the bounds of the request */
        addr_lo = addr_allocp->aa_address;
        addr_hi = addr_lo + addr_allocp->aa_length;

        /* Check combination of flags */
        if ((addr_allocp->aa_enable & T1394_ADDR_RDENBL) &&
            (addr_allocp->aa_evts.recv_read_request == NULL) &&
            (addr_allocp->aa_kmem_bufp == NULL)) {
                if ((addr_allocp->aa_type != T1394_ADDR_FIXED)  ||
                    (addr_lo < hal->physical_addr_lo)           ||
                    (addr_hi > hal->physical_addr_hi)) {

                        /*
                         * Reads are enabled, but target doesn't want to
                         * be notified and hasn't given backing store
                         */
                        *result = T1394_EINVALID_PARAM;

                        /* kstats - addr alloc failures */
                        hal->hal_kstats->addr_alloc_fail++;
                        return (DDI_FAILURE);
                } else {
                        addr_allocp->aa_enable &= ~T1394_ADDR_RDENBL;
                }
        }

        if ((addr_allocp->aa_enable & T1394_ADDR_WRENBL) &&
            (addr_allocp->aa_evts.recv_write_request == NULL) &&
            (addr_allocp->aa_kmem_bufp == NULL)) {
                if ((addr_allocp->aa_type != T1394_ADDR_FIXED)  ||
                    (addr_lo < hal->physical_addr_lo)           ||
                    (addr_hi > hal->physical_addr_hi)) {

                        /*
                         * Writes are enabled, but target doesn't want to
                         * be notified and hasn't given backing store
                         */
                        *result = T1394_EINVALID_PARAM;

                        /* kstats - addr alloc failures */
                        hal->hal_kstats->addr_alloc_fail++;
                        return (DDI_FAILURE);
                } else {
                        addr_allocp->aa_enable &= ~T1394_ADDR_WRENBL;
                }
        }

        if ((addr_allocp->aa_enable & T1394_ADDR_LKENBL) &&
            (addr_allocp->aa_evts.recv_lock_request == NULL) &&
            (addr_allocp->aa_kmem_bufp == NULL)) {
                if ((addr_allocp->aa_type != T1394_ADDR_FIXED)  ||
                    (addr_lo < hal->physical_addr_lo)           ||
                    (addr_hi > hal->physical_addr_hi)) {

                        /*
                         * Locks are enabled, but target doesn't want to
                         * be notified and hasn't given backing store
                         */
                        *result = T1394_EINVALID_PARAM;

                        /* kstats - addr alloc failures */
                        hal->hal_kstats->addr_alloc_fail++;
                        return (DDI_FAILURE);
                } else {
                        addr_allocp->aa_enable &= ~T1394_ADDR_LKENBL;
                }
        }

        /* If not T1394_ADDR_FIXED, then allocate a block */
        if (addr_allocp->aa_type != T1394_ADDR_FIXED) {
                err = s1394_request_addr_blk((s1394_hal_t *)target->on_hal,
                    addr_allocp);
                if (err != DDI_SUCCESS) {
                        *result = T1394_EALLOC_ADDR;
                        /* kstats - addr alloc failures */
                        hal->hal_kstats->addr_alloc_fail++;
                } else {
                        *result = T1394_NOERROR;
                }
                return (err);
        } else {
                err = s1394_claim_addr_blk((s1394_hal_t *)target->on_hal,
                    addr_allocp);
                if (err != DDI_SUCCESS) {
                        *result = T1394_EALLOC_ADDR;
                        /* kstats - addr alloc failures */
                        hal->hal_kstats->addr_alloc_fail++;
                } else {
                        *result = T1394_NOERROR;
                        /* If physical, update the AR request counter */
                        if ((addr_lo >= hal->physical_addr_lo) &&
                            (addr_hi <= hal->physical_addr_hi)) {
                                rw_enter(&hal->target_list_rwlock, RW_WRITER);
                                target->physical_arreq_enabled++;
                                rw_exit(&hal->target_list_rwlock);

                                s1394_physical_arreq_set_one(target);
                        }
                }
                return (err);
        }
}

/*
 * Function:    t1394_free_addr()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              addr_hdl                The address "handle" returned by the
 *                                         the t1394_alloc_addr() routine
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   DDI_SUCCESS             Target successfully freed memory
 *              DDI_FAILURE             Target failed to free the memory block
 *
 * Description: t1394_free_addr() attempts to free up memory that has been
 *              allocated by the target using t1394_alloc_addr().
 */
/* ARGSUSED */
int
t1394_free_addr(t1394_handle_t t1394_hdl, t1394_addr_handle_t *addr_hdl,
    uint_t flags)
{
        s1394_addr_space_blk_t  *curr_blk;
        s1394_hal_t             *hal;
        s1394_target_t          *target;

        ASSERT(t1394_hdl != NULL);
        ASSERT(addr_hdl != NULL);

        target = (s1394_target_t *)t1394_hdl;

        /* Find the HAL this target resides on */
        hal = target->on_hal;

        curr_blk = (s1394_addr_space_blk_t *)(*addr_hdl);

        if (s1394_free_addr_blk(hal, curr_blk) != DDI_SUCCESS) {
                return (DDI_FAILURE);
        }

        /* If physical, update the AR request counter */
        if (curr_blk->addr_type == T1394_ADDR_FIXED) {
                target->physical_arreq_enabled--;
                s1394_physical_arreq_clear_one(target);
        }

        *addr_hdl = NULL;

        /* kstats - number of addr frees */
        hal->hal_kstats->addr_space_free++;

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_recv_request_done()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              resp                    Pointer to the command which the
 *                                          target received in it's callback
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   DDI_SUCCESS             Target successfully returned command
 *                                          to the 1394 Software Framework,
 *                                          and, if necessary, sent response
 *              DDI_FAILURE             Target failed to return the command to
 *                                          the 1394 Software Framework
 *
 * Description: t1394_recv_request_done() takes the command that is given and
 *              determines whether that command requires a response to be
 *              sent on the 1394 bus.  If it is necessary and it's response
 *              code (cmd_result) has been set appropriately, then a response
 *              will be sent.  If no response is necessary (broadcast or
 *              posted write), then the command resources are reclaimed.
 */
/* ARGSUSED */
int
t1394_recv_request_done(t1394_handle_t t1394_hdl, cmd1394_cmd_t *resp,
    uint_t flags)
{
        s1394_hal_t      *hal;
        s1394_cmd_priv_t *s_priv;
        h1394_cmd_priv_t *h_priv;
        mblk_t           *curr_blk;
        size_t           msgb_len;
        size_t           size;
        int              ret;
        boolean_t        response = B_TRUE;
        boolean_t        posted_write = B_FALSE;
        boolean_t        write_cmd = B_FALSE;
        boolean_t        mblk_too_small;

        ASSERT(t1394_hdl != NULL);
        ASSERT(resp != NULL);

        /* Find the HAL this target resides on */
        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

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

        /* Is this an FA request? */
        if (s_priv->cmd_ext_type == S1394_CMD_EXT_FA) {
                s1394_fa_convert_cmd(hal, resp);
        }

        /* Is this a write request? */
        if ((resp->cmd_type == CMD1394_ASYNCH_WR_QUAD) ||
            (resp->cmd_type == CMD1394_ASYNCH_WR_BLOCK)) {
                write_cmd = B_TRUE;
                /* Is this a posted write request? */
                posted_write = s_priv->posted_write;
        }

        /* If broadcast or posted write cmd, don't send response */
        if ((resp->broadcast == 1) ||
            ((write_cmd == B_TRUE) && (posted_write == B_TRUE)))
                response = B_FALSE;

        if (response == B_FALSE) {
                if ((write_cmd == B_TRUE) && (posted_write == B_TRUE)) {
                        /* kstats - Posted Write error */
                        hal->hal_kstats->arreq_posted_write_error++;
                }

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

        ASSERT(response == B_TRUE);

        /* Verify valid response code */
        switch (resp->cmd_result) {
        case IEEE1394_RESP_COMPLETE:
                /* Is the mblk_t too small? */
                if (resp->cmd_type == CMD1394_ASYNCH_RD_BLOCK) {
                        curr_blk = resp->cmd_u.b.data_block;
                        size     = resp->cmd_u.b.blk_length;
                        msgb_len = 0;
                        mblk_too_small = B_TRUE;

                        if (curr_blk == NULL) {
                                /*
                                 * Free the command - Pass it back
                                 * to the HAL
                                 */
                                HAL_CALL(hal).response_complete(
                                    hal->halinfo.hal_private, resp, h_priv);
                                ASSERT(curr_blk != NULL);
                                return (DDI_FAILURE);
                        }

                        while (curr_blk != NULL) {
                                msgb_len +=
                                    (curr_blk->b_wptr - curr_blk->b_rptr);

                                if (msgb_len >= size) {
                                        mblk_too_small = B_FALSE;
                                        break;
                                }
                                curr_blk = curr_blk->b_cont;
                        }

                        if (mblk_too_small == B_TRUE) {
                                /*
                                 * Free the command - Pass it back
                                 * to the HAL
                                 */
                                HAL_CALL(hal).response_complete(
                                    hal->halinfo.hal_private, resp, h_priv);
                                ASSERT(mblk_too_small != B_TRUE);
                                return (DDI_FAILURE);
                        }
                }
                /* FALLTHROUGH */
        case IEEE1394_RESP_CONFLICT_ERROR:
        case IEEE1394_RESP_DATA_ERROR:
        case IEEE1394_RESP_TYPE_ERROR:
        case IEEE1394_RESP_ADDRESS_ERROR:
                ret = s1394_send_response(hal, resp);
                return (ret);

        default:
                return (DDI_FAILURE);
        }
}


/*
 * Function:    t1394_fcp_register_controller()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              evts                    The structure in which the target
 *                                          specifies its callback routines
 *
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   DDI_SUCCESS             Successfully registered.
 *
 *              DDI_FAILURE             Not registered due to failure.
 *
 * Description: Used to register the target within the Framework as an FCP
 *              controller.
 */
/* ARGSUSED */
int
t1394_fcp_register_controller(t1394_handle_t t1394_hdl, t1394_fcp_evts_t *evts,
    uint_t flags)
{
        int             result;

        ASSERT(t1394_hdl != NULL);

        result = s1394_fcp_register_ctl((s1394_target_t *)t1394_hdl, evts);

        return (result);
}

/*
 * Function:    t1394_fcp_unregister_controller()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *
 * Output(s):   DDI_SUCCESS             Successfully unregistered.
 *
 *              DDI_FAILURE             Not unregistered due to failure.
 *
 * Description: Used to unregister the target within the Framework as an FCP
 *              controller.
 */
int
t1394_fcp_unregister_controller(t1394_handle_t t1394_hdl)
{
        int             result;

        ASSERT(t1394_hdl != NULL);

        result = s1394_fcp_unregister_ctl((s1394_target_t *)t1394_hdl);

        return (result);
}

/*
 * Function:    t1394_fcp_register_target()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              evts                    The structure in which the target
 *                                          specifies its callback routines
 *
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   DDI_SUCCESS             Successfully registered.
 *
 *              DDI_FAILURE             Not registered due to failure.
 *
 * Description: Used to register the target within the Framework as an FCP
 *              target.
 */
/* ARGSUSED */
int
t1394_fcp_register_target(t1394_handle_t t1394_hdl, t1394_fcp_evts_t *evts,
    uint_t flags)
{
        int             result;

        ASSERT(t1394_hdl != NULL);

        result = s1394_fcp_register_tgt((s1394_target_t *)t1394_hdl, evts);

        return (result);
}

/*
 * Function:    t1394_fcp_unregister_target()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *
 * Output(s):   DDI_SUCCESS             Successfully unregistered.
 *
 *              DDI_FAILURE             Not unregistered due to failure.
 *
 * Description: Used to unregister the target within the Framework as an FCP
 *              target.
 */
int
t1394_fcp_unregister_target(t1394_handle_t t1394_hdl)
{
        int             result;

        ASSERT(t1394_hdl != NULL);

        result = s1394_fcp_unregister_tgt((s1394_target_t *)t1394_hdl);

        return (result);
}

/*
 * Function:    t1394_cmp_register()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              evts                    The structure in which the target
 *                                          specifies its callback routines
 *
 * Output(s):   DDI_SUCCESS             Successfully registered.
 *
 *              DDI_FAILURE             Not registered due to failure.
 *
 * Description: Used to register the target within the Framework as a CMP
 *              device.
 */
/* ARGSUSED */
int
t1394_cmp_register(t1394_handle_t t1394_hdl, t1394_cmp_evts_t *evts,
    uint_t flags)
{
        int             result;

        ASSERT(t1394_hdl != NULL);

        result = s1394_cmp_register((s1394_target_t *)t1394_hdl, evts);

        return (result);
}

/*
 * Function:    t1394_cmp_unregister()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              evts                    The structure in which the target
 *                                          specifies its callback routines
 *
 * Output(s):   DDI_SUCCESS             Successfully registered.
 *
 *              DDI_FAILURE             Not registered due to failure.
 *
 * Description: Used to unregister the target within the Framework as a CMP
 *              device.
 */
int
t1394_cmp_unregister(t1394_handle_t t1394_hdl)
{
        int             result;

        ASSERT(t1394_hdl != NULL);

        result = s1394_cmp_unregister((s1394_target_t *)t1394_hdl);

        return (result);
}

/*
 * Function:    t1394_cmp_read()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              reg                     Register type.
 *              valp                    Returned register value.
 *
 * Output(s):   DDI_SUCCESS             Successfully registered.
 *
 *              DDI_FAILURE             Not registered due to failure.
 *
 * Description: Used to read a CMP register value.
 */
int
t1394_cmp_read(t1394_handle_t t1394_hdl, t1394_cmp_reg_t reg, uint32_t *valp)
{
        int             result;

        ASSERT(t1394_hdl != NULL);

        result = s1394_cmp_read((s1394_target_t *)t1394_hdl, reg, valp);

        return (result);
}

/*
 * Function:    t1394_cmp_cas()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              reg                     Register type.
 *              arg_val                 Compare argument.
 *              new_val                 New register value.
 *              old_valp                Returned original register value.
 *
 * Output(s):   DDI_SUCCESS             Successfully registered.
 *
 *              DDI_FAILURE             Not registered due to failure.
 *
 * Description: Used to compare-swap a CMP register value.
 */
int
t1394_cmp_cas(t1394_handle_t t1394_hdl, t1394_cmp_reg_t reg, uint32_t arg_val,
    uint32_t new_val, uint32_t *old_valp)
{
        int             result;

        ASSERT(t1394_hdl != NULL);

        result = s1394_cmp_cas((s1394_target_t *)t1394_hdl, reg, arg_val,
            new_val, old_valp);

        return (result);
}

/*
 * Function:    t1394_alloc_isoch_single()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              sii                     The structure used to set up the
 *                                          overall characteristics of the
 *                                          isochronous stream
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   setup_args              Contains the channel number that was
 *                                          allocated
 *              t1394_single_hdl        This in the isoch "handle" used in
 *                                          t1394_free_isoch_single()
 *              result                  Used to pass more specific info back
 *                                          to target
 *
 * Description: t1394_alloc_isoch_single() is used to direct the 1394 Software
 *              Framework to allocate an isochronous channel and bandwidth
 *              from the Isochronous Resource Manager (IRM).  If a bus reset
 *              occurs, the 1394 Software Framework attempts to reallocate the
 *              same resources, calling the rsrc_fail_target() callback if
 *              it is unsuccessful.
 */
/* ARGSUSED */
int
t1394_alloc_isoch_single(t1394_handle_t t1394_hdl,
    t1394_isoch_singleinfo_t *sii, uint_t flags,
    t1394_isoch_single_out_t *output_args,
    t1394_isoch_single_handle_t *t1394_single_hdl, int *result)
{
        s1394_hal_t             *hal;
        s1394_isoch_cec_t       *cec_new;
        t1394_join_isochinfo_t  jii;
        int                     ret;
        int                     err;

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_single_hdl != NULL);
        ASSERT(sii != NULL);

        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Check for invalid channel_mask */
        if (sii->si_channel_mask == 0) {
                return (DDI_FAILURE);
        }

        /* Check for invalid bandwidth */
        if ((sii->si_bandwidth <= IEEE1394_BANDWIDTH_MIN) ||
            (sii->si_bandwidth > IEEE1394_BANDWIDTH_MAX)) {
                return (DDI_FAILURE);
        }

        /* Verify that rsrc_fail_target() callback is non-NULL */
        if (sii->rsrc_fail_target == NULL) {
                return (DDI_FAILURE);
        }

        /*
         * Allocate an Isoch CEC of type S1394_SINGLE
         */

        /* Allocate the Isoch CEC structure */
        cec_new = kmem_zalloc(sizeof (s1394_isoch_cec_t), KM_SLEEP);

        /* Initialize the structure type */
        cec_new->cec_type = S1394_SINGLE;

        /* Create the mutex and "in_callbacks" cv */
        mutex_init(&cec_new->isoch_cec_mutex, NULL, MUTEX_DRIVER,
            hal->halinfo.hw_interrupt);
        cv_init(&cec_new->in_callbacks_cv, NULL, CV_DRIVER,
            hal->halinfo.hw_interrupt);

        /* Initialize the Isoch CEC's member list */
        cec_new->cec_member_list_head = NULL;
        cec_new->cec_member_list_tail = NULL;

        /* Initialize the filters */
        cec_new->filter_min_speed       = sii->si_speed;
        cec_new->filter_max_speed       = sii->si_speed;
        cec_new->filter_current_speed   = cec_new->filter_max_speed;
        cec_new->filter_channel_mask    = sii->si_channel_mask;
        cec_new->bandwidth              = sii->si_bandwidth;
        cec_new->state_transitions      = ISOCH_CEC_FREE | ISOCH_CEC_JOIN |
            ISOCH_CEC_SETUP;

        mutex_enter(&hal->isoch_cec_list_mutex);

        /* Insert Isoch CEC into the HAL's list */
        s1394_isoch_cec_list_insert(hal, cec_new);

        mutex_exit(&hal->isoch_cec_list_mutex);

        /*
         * Join the newly created Isoch CEC
         */
        jii.req_channel_mask    = sii->si_channel_mask;
        jii.req_max_speed       = sii->si_speed;
        jii.jii_options         = T1394_TALKER;
        jii.isoch_cec_evts_arg  = sii->single_evt_arg;

        /* All events are NULL except rsrc_fail_target() */
        jii.isoch_cec_evts.setup_target     = NULL;
        jii.isoch_cec_evts.start_target     = NULL;
        jii.isoch_cec_evts.stop_target      = NULL;
        jii.isoch_cec_evts.stop_target      = NULL;
        jii.isoch_cec_evts.teardown_target  = NULL;
        jii.isoch_cec_evts.rsrc_fail_target = sii->rsrc_fail_target;

        ret = t1394_join_isoch_cec(t1394_hdl,
            (t1394_isoch_cec_handle_t)cec_new, 0, &jii);

        if (ret != DDI_SUCCESS) {
                ret = t1394_free_isoch_cec(t1394_hdl, flags,
                    (t1394_isoch_cec_handle_t *)&cec_new);
                if (ret != DDI_SUCCESS) {
                        /* Unable to free the Isoch CEC */
                        ASSERT(0);
                }

                /* Handle is nulled out before returning */
                *t1394_single_hdl = NULL;

                return (DDI_FAILURE);
        }

        /*
         * Setup the isoch resources, etc.
         */
        ret = t1394_setup_isoch_cec(t1394_hdl,
            (t1394_isoch_cec_handle_t)cec_new, 0, &err);

        if (ret != DDI_SUCCESS) {
                *result = err;

                /* Leave the Isoch CEC */
                ret = t1394_leave_isoch_cec(t1394_hdl,
                    (t1394_isoch_cec_handle_t)cec_new, 0);
                if (ret != DDI_SUCCESS) {
                        /* Unable to leave the Isoch CEC */
                        ASSERT(0);
                }

                /* Free up the Isoch CEC */
                ret = t1394_free_isoch_cec(t1394_hdl, flags,
                    (t1394_isoch_cec_handle_t *)&cec_new);
                if (ret != DDI_SUCCESS) {
                        /* Unable to free the Isoch CEC */
                        ASSERT(0);
                }

                /* Handle is nulled out before returning */
                *t1394_single_hdl = NULL;

                return (DDI_FAILURE);
        }

        /* Return the setup_args - channel num and speed */
        mutex_enter(&cec_new->isoch_cec_mutex);
        output_args->channel_num  = cec_new->realloc_chnl_num;
        mutex_exit(&cec_new->isoch_cec_mutex);

        /* Update the handle */
        *t1394_single_hdl = (t1394_isoch_single_handle_t)cec_new;

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_free_isoch_single()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              t1394_single_hdl        The isoch "handle" return by
 *                                          t1394_alloc_isoch_single()
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   None
 *
 * Description: t1394_free_isoch_single() frees the isochronous resources
 *              and the handle that were allocated during the call to
 *              t1394_alloc_isoch_single().
 */
/* ARGSUSED */
void
t1394_free_isoch_single(t1394_handle_t t1394_hdl,
    t1394_isoch_single_handle_t *t1394_single_hdl, uint_t flags)
{
        s1394_isoch_cec_t *cec_curr;
        int               ret;

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_single_hdl != NULL);

        /* Convert the handle to an Isoch CEC pointer */
        cec_curr = (s1394_isoch_cec_t *)(*t1394_single_hdl);

        /*
         * Teardown the isoch resources, etc.
         */
        ret = t1394_teardown_isoch_cec(t1394_hdl,
            (t1394_isoch_cec_handle_t)cec_curr, 0);
        if (ret != DDI_SUCCESS) {
                /* Unable to teardown the Isoch CEC */
                ASSERT(0);
        }

        /*
         * Leave the Isoch CEC
         */
        ret = t1394_leave_isoch_cec(t1394_hdl,
            (t1394_isoch_cec_handle_t)cec_curr, 0);
        if (ret != DDI_SUCCESS) {
                /* Unable to leave the Isoch CEC */
                ASSERT(0);
        }

        /*
         * Free the Isoch CEC
         */
        ret = t1394_free_isoch_cec(t1394_hdl, flags,
            (t1394_isoch_cec_handle_t *)&cec_curr);
        if (ret != DDI_SUCCESS) {
                /* Unable to free the Isoch CEC */
                ASSERT(0);
        }

        /* Handle is nulled out before returning */
        *t1394_single_hdl = NULL;
}

/*
 * Function:    t1394_alloc_isoch_cec()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              props                   The structure used to set up the
 *                                          overall characteristics of for
 *                                          the Isoch CEC.
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   t1394_isoch_cec_hdl     The Isoch CEC "handle" used in all
 *                                          subsequent isoch_cec() calls
 *
 * Description: t1394_alloc_isoch_cec() allocates and initializes an
 *              isochronous channel event coordinator (Isoch CEC) for use
 *              in managing and coordinating activity for an isoch channel
 */
/* ARGSUSED */
int
t1394_alloc_isoch_cec(t1394_handle_t t1394_hdl, t1394_isoch_cec_props_t *props,
    uint_t flags, t1394_isoch_cec_handle_t *t1394_isoch_cec_hdl)
{
        s1394_hal_t       *hal;
        s1394_isoch_cec_t *cec_new;
        uint64_t          temp;

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_isoch_cec_hdl != NULL);
        ASSERT(props != NULL);

        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Check for invalid channel_mask */
        if (props->cec_channel_mask == 0) {
                return (DDI_FAILURE);
        }

        /* Test conditions specific to T1394_NO_IRM_ALLOC */
        temp = props->cec_channel_mask;
        if (props->cec_options & T1394_NO_IRM_ALLOC) {
                /* If T1394_NO_IRM_ALLOC, then only one bit should be set */
                if (!ISP2(temp)) {
                        return (DDI_FAILURE);
                }

                /* If T1394_NO_IRM_ALLOC, then speeds should be equal */
                if (props->cec_min_speed != props->cec_max_speed) {
                        return (DDI_FAILURE);
                }
        }

        /* Check for invalid bandwidth */
        if ((props->cec_bandwidth <= IEEE1394_BANDWIDTH_MIN) ||
            (props->cec_bandwidth > IEEE1394_BANDWIDTH_MAX)) {
                return (DDI_FAILURE);
        }

        /* Allocate the Isoch CEC structure */
        cec_new = kmem_zalloc(sizeof (s1394_isoch_cec_t), KM_SLEEP);

        /* Initialize the structure type */
        cec_new->cec_type = S1394_PEER_TO_PEER;

        /* Create the mutex and "in_callbacks" cv */
        mutex_init(&cec_new->isoch_cec_mutex, NULL, MUTEX_DRIVER,
            hal->halinfo.hw_interrupt);
        cv_init(&cec_new->in_callbacks_cv, NULL, CV_DRIVER,
            hal->halinfo.hw_interrupt);

        /* Initialize the Isoch CEC's member list */
        cec_new->cec_member_list_head   = NULL;
        cec_new->cec_member_list_tail   = NULL;

        /* Initialize the filters */
        cec_new->filter_min_speed       = props->cec_min_speed;
        cec_new->filter_max_speed       = props->cec_max_speed;
        cec_new->filter_current_speed   = cec_new->filter_max_speed;
        cec_new->filter_channel_mask    = props->cec_channel_mask;
        cec_new->bandwidth              = props->cec_bandwidth;
        cec_new->cec_options            = props->cec_options;
        cec_new->state_transitions      = ISOCH_CEC_FREE | ISOCH_CEC_JOIN |
            ISOCH_CEC_SETUP;

        mutex_enter(&hal->isoch_cec_list_mutex);

        /* Insert Isoch CEC into the HAL's list */
        s1394_isoch_cec_list_insert(hal, cec_new);

        mutex_exit(&hal->isoch_cec_list_mutex);

        /* Update the handle and return */
        *t1394_isoch_cec_hdl = (t1394_isoch_cec_handle_t)cec_new;

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_free_isoch_cec()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              flags                   The flags parameter is unused (for now)
 *              t1394_isoch_cec_hdl     The Isoch CEC "handle" returned by
 *                                          t1394_alloc_isoch_cec()
 *
 * Output(s):   DDI_SUCCESS             Target successfully freed the Isoch CEC
 *              DDI_FAILURE             Target failed to free the Isoch CEC
 *
 * Description: t1394_free_isoch_cec() attempts to free the Isoch CEC
 *              structure.  It will fail (DDI_FAILURE) if there are any
 *              remaining members who have not yet left.
 */
/* ARGSUSED */
int
t1394_free_isoch_cec(t1394_handle_t t1394_hdl, uint_t flags,
    t1394_isoch_cec_handle_t *t1394_isoch_cec_hdl)
{
        s1394_hal_t       *hal;
        s1394_isoch_cec_t *cec_curr;

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_isoch_cec_hdl != NULL);

        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Convert the handle to an Isoch CEC pointer */
        cec_curr = (s1394_isoch_cec_t *)(*t1394_isoch_cec_hdl);

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* Are we in any callbacks? */
        if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Is "free" a legal state transition? */
        if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_FREE) == 0) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }
        mutex_exit(&cec_curr->isoch_cec_mutex);

        mutex_enter(&hal->isoch_cec_list_mutex);

        /* Remove Isoch CEC from HAL's list */
        s1394_isoch_cec_list_remove(hal, cec_curr);

        mutex_exit(&hal->isoch_cec_list_mutex);

        /* Destroy the Isoch CEC's mutex and cv */
        cv_destroy(&cec_curr->in_callbacks_cv);
        mutex_destroy(&cec_curr->isoch_cec_mutex);

        /* Free up the memory for the Isoch CEC struct */
        kmem_free(cec_curr, sizeof (s1394_isoch_cec_t));

        /* Update the handle and return */
        *t1394_isoch_cec_hdl = NULL;

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_join_isoch_cec()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              t1394_isoch_cec_hdl     The Isoch CEC "handle" returned by
 *                                          t1394_alloc_isoch_cec()
 *              flags                   The flags parameter is unused (for now)
 *              join_isoch_info         This structure provides infomation
 *                                          about a target that wishes to join
 *                                          the given Isoch CEC.  It gives
 *                                          max_speed, channel_mask, etc.
 *
 * Output(s):   DDI_SUCCESS             Target successfully joined the
 *                                          Isoch CEC
 *              DDI_FAILURE             Target failed to join the Isoch CEC
 *
 * Description: t1394_join_isoch_cec() determines, based on the information
 *              given in the join_isoch_info structure, if the target may
 *              join the Isoch CEC.  If it is determined that the target may
 *              join, the specified callback routines are stored away for
 *              later use in the coordination tasks.
 */
/* ARGSUSED */
int
t1394_join_isoch_cec(t1394_handle_t t1394_hdl,
    t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags,
    t1394_join_isochinfo_t *join_isoch_info)
{
        s1394_hal_t              *hal;
        s1394_isoch_cec_t        *cec_curr;
        s1394_isoch_cec_member_t *member_new;
        uint64_t                 check_mask;
        uint_t                   curr_max_speed;

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_isoch_cec_hdl != NULL);

        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Convert the handle to an Isoch CEC pointer */
        cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;

        /* Allocate a new Isoch CEC member structure */
        member_new = kmem_zalloc(sizeof (s1394_isoch_cec_member_t), KM_SLEEP);

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* Are we in any callbacks? (Wait for them to finish) */
        while (CEC_IN_ANY_CALLBACKS(cec_curr)) {
                cec_curr->cec_want_wakeup = B_TRUE;
                cv_wait(&cec_curr->in_callbacks_cv,
                    &cec_curr->isoch_cec_mutex);
        }

        /* Is "join" a legal state transition? */
        if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_JOIN) == 0) {
                kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Check the channel mask for consistency */
        check_mask = join_isoch_info->req_channel_mask &
            cec_curr->filter_channel_mask;
        if (check_mask == 0) {
                kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Check for consistent speeds */
        if (join_isoch_info->req_max_speed < cec_curr->filter_min_speed) {
                kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        } else if (join_isoch_info->req_max_speed <
            cec_curr->filter_current_speed) {
                curr_max_speed = join_isoch_info->req_max_speed;
        } else {
                curr_max_speed = cec_curr->filter_current_speed;
        }

        /* Check for no more than one talker */
        if ((join_isoch_info->jii_options & T1394_TALKER) &&
            (cec_curr->cec_member_talker != NULL)) {
                kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Verify that all callbacks are non-NULL (for PEER_TO_PEER) */
        if ((cec_curr->cec_type == S1394_PEER_TO_PEER) &&
            ((join_isoch_info->isoch_cec_evts.setup_target      == NULL) ||
            (join_isoch_info->isoch_cec_evts.start_target       == NULL) ||
            (join_isoch_info->isoch_cec_evts.stop_target        == NULL) ||
            (join_isoch_info->isoch_cec_evts.rsrc_fail_target   == NULL) ||
            (join_isoch_info->isoch_cec_evts.teardown_target    == NULL))) {
                kmem_free(member_new, sizeof (s1394_isoch_cec_member_t));
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Copy the events information into the struct */
        member_new->isoch_cec_evts      = join_isoch_info->isoch_cec_evts;
        member_new->isoch_cec_evts_arg  = join_isoch_info->isoch_cec_evts_arg;
        member_new->cec_mem_options     = join_isoch_info->jii_options;
        member_new->cec_mem_target      = (s1394_target_t *)t1394_hdl;

        /* Insert new member into Isoch CEC's member list */
        s1394_isoch_cec_member_list_insert(hal, cec_curr, member_new);

        /* Update the channel mask filter */
        cec_curr->filter_channel_mask   = check_mask;

        /* Update the speed filter */
        cec_curr->filter_current_speed  = curr_max_speed;

        /* Update the talker pointer (if necessary) */
        if (join_isoch_info->jii_options & T1394_TALKER)
                cec_curr->cec_member_talker = cec_curr->cec_member_list_head;

        /*
         * Now "leave" is a legal state transition
         * and "free" is an illegal state transition
         */
        CEC_SET_LEGAL(cec_curr, ISOCH_CEC_LEAVE);
        CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_FREE);

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_leave_isoch_cec()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              t1394_isoch_cec_hdl     The Isoch CEC "handle" returned by
 *                                          t1394_alloc_isoch_cec()
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   DDI_SUCCESS             Target successfully left the
 *                                          Isoch CEC
 *              DDI_FAILURE             Target failed to leave the Isoch CEC
 *
 * Description: t1394_leave_isoch_cec() is used by a target driver to remove
 *              itself from the Isoch CEC's member list.  It is possible
 *              for this call to fail because the target is not found in
 *              the current member list, or because it is not an appropriate
 *              time for a target to leave.
 */
/* ARGSUSED */
int
t1394_leave_isoch_cec(t1394_handle_t t1394_hdl,
    t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
        s1394_hal_t              *hal;
        s1394_isoch_cec_t        *cec_curr;
        s1394_isoch_cec_member_t *member_curr;
        s1394_isoch_cec_member_t *member_temp;
        boolean_t                found;
        uint64_t                 temp_channel_mask;
        uint_t                   temp_max_speed;

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_isoch_cec_hdl != NULL);

        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Convert the handle to an Isoch CEC pointer */
        cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* Are we in any callbacks? (Wait for them to finish) */
        while (CEC_IN_ANY_CALLBACKS(cec_curr)) {
                cec_curr->cec_want_wakeup = B_TRUE;
                cv_wait(&cec_curr->in_callbacks_cv,
                    &cec_curr->isoch_cec_mutex);
        }

        /* Is "leave" a legal state transition? */
        if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_LEAVE) == 0) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Find the Target on the CEC's member list */
        found = B_FALSE;
        temp_channel_mask = cec_curr->cec_alloc_props.cec_channel_mask;
        temp_max_speed    = cec_curr->cec_alloc_props.cec_max_speed;
        member_curr       = cec_curr->cec_member_list_head;
        while (member_curr != NULL) {
                if (member_curr->cec_mem_target ==
                    (s1394_target_t *)t1394_hdl) {
                        member_temp = member_curr;
                        found       = B_TRUE;
                } else {
                        /* Keep track of channel mask and max speed info */
                        temp_channel_mask &= member_curr->req_channel_mask;
                        if (member_curr->req_max_speed < temp_max_speed)
                                temp_max_speed = member_curr->req_max_speed;
                }
                member_curr = member_curr->cec_mem_next;
        }

        /* Target not found on this Isoch CEC */
        if (found == B_FALSE) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        } else {
                /* This member's departure may change filter constraints */
                cec_curr->filter_current_speed  = temp_max_speed;
                cec_curr->filter_channel_mask   = temp_channel_mask;
        }

        /* Remove member from Isoch CEC's member list */
        s1394_isoch_cec_member_list_remove(hal, cec_curr, member_temp);

        /* If we are removing the talker, then update the pointer */
        if (cec_curr->cec_member_talker == member_temp)
                cec_curr->cec_member_talker = NULL;

        /* Is the Isoch CEC's member list empty? */
        if ((cec_curr->cec_member_list_head == NULL) &&
            (cec_curr->cec_member_list_tail == NULL)) {
                /*
                 * Now "free" _might_ be a legal state transition
                 * if we aren't in setup or start phases and "leave"
                 * is definitely an illegal state transition
                 */
                if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_JOIN) != 0)
                        CEC_SET_LEGAL(cec_curr, ISOCH_CEC_FREE);
                CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_LEAVE);
        }

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);

        /* Free the Isoch CEC member structure */
        kmem_free(member_temp, sizeof (s1394_isoch_cec_member_t));

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_setup_isoch_cec()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              t1394_isoch_cec_hdl     The Isoch CEC "handle" returned by
 *                                          t1394_alloc_isoch_cec()
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   result                  Used to pass more specific info back
 *                                          to target
 *
 * Description: t1394_setup_isoch_cec() directs the 1394 Software Framework
 *              to allocate isochronous resources and invoke the setup_target()
 *              callback for each member of the Isoch CEC.  This call may
 *              fail because bandwidth was unavailable (T1394_ENO_BANDWIDTH),
 *              channels were unavailable (T1394_ENO_CHANNEL), or one of the
 *              member targets returned failure from its setup_target()
 *              callback.
 */
/* ARGSUSED */
int
t1394_setup_isoch_cec(t1394_handle_t t1394_hdl,
    t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags, int *result)
{
        s1394_hal_t                     *hal;
        s1394_isoch_cec_t               *cec_curr;
        s1394_isoch_cec_member_t        *member_curr;
        t1394_setup_target_args_t       target_args;
        uint64_t                        temp_chnl_mask;
        uint32_t                        old_chnl;
        uint32_t                        try_chnl;
        uint_t                          bw_alloc_units;
        uint_t                          generation;
        int                             chnl_num;
        int                             err;
        int                             ret;
        int                             j;
        int     (*setup_callback)(t1394_isoch_cec_handle_t, opaque_t,
            t1394_setup_target_args_t *);

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_isoch_cec_hdl != NULL);

        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Convert the handle to an Isoch CEC pointer */
        cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* Are we in any callbacks? */
        if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Is "setup" a legal state transition? */
        if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_SETUP) == 0) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* If T1394_NO_IRM_ALLOC is set then don't allocate... do callbacks */
        if (cec_curr->cec_options & T1394_NO_IRM_ALLOC) {
                goto setup_do_callbacks;
        }

        /* Allocate bandwidth and channels */
        for (j = 0; j < S1394_ISOCH_ALLOC_RETRIES; j++) {
                /*
                 * Get the current generation number - don't
                 * need the lock because we are read only here
                 */
                generation = hal->generation_count;

                /* Compute how much bandwidth is needed */
                bw_alloc_units = s1394_compute_bw_alloc_units(hal,
                    cec_curr->bandwidth, cec_curr->filter_current_speed);

                /* Check that the generation has not changed - */
                /* don't need the lock (read only) */
                if (generation != hal->generation_count)
                        continue;

                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);

                /* Try to allocate the bandwidth */
                ret = s1394_bandwidth_alloc(hal, bw_alloc_units, generation,
                    &err);

                /* Lock the Isoch CEC member list */
                mutex_enter(&cec_curr->isoch_cec_mutex);

                /* If there was a bus reset, start over */
                if (ret == DDI_FAILURE) {
                        if (err == CMD1394_EBUSRESET) {
                                continue; /* start over and try again */
                        } else {
                                *result = T1394_ENO_BANDWIDTH;
                                /* Unlock the Isoch CEC member list */
                                mutex_exit(&cec_curr->isoch_cec_mutex);
                                return (DDI_FAILURE);
                        }
                }

                /* Check that the generation has not changed - */
                /* don't need the lock (read only) */
                if (generation != hal->generation_count)
                        continue;

                /*
                 * Allocate a channel
                 *    From IEEE 1394-1995, Section 8.3.2.3.8: "Bits
                 *    allocated in the CHANNELS_AVAILABLE_HI field of
                 *    this register shall start at bit zero (channel
                 *    number zero), and additional channel numbers shall
                 *    be represented in a monotonically increasing sequence
                 *    of bit numbers up to a maximum of bit 31 (channel
                 *    number 31).  Bits allocated in the CHANNELS_AVAILABLE_LO
                 *    field of this register shall start at bit zero
                 *    (channel number 32), and additional channel numbers
                 *    shall be represented in a monotonically increasing
                 *    sequence of bit numbers up to a maximum of bit 31
                 *    (channel number 63).
                 */
                temp_chnl_mask = cec_curr->filter_channel_mask;
                for (chnl_num = 63; chnl_num >= 0; chnl_num--) {
                        if ((temp_chnl_mask & 1) == 1) {
                                try_chnl = (1 << ((63 - chnl_num) % 32));

                                /* Unlock the Isoch CEC member list */
                                mutex_exit(&cec_curr->isoch_cec_mutex);
                                if (chnl_num < 32) {
                                        ret = s1394_channel_alloc(hal,
                                            try_chnl, generation,
                                            S1394_CHANNEL_ALLOC_HI, &old_chnl,
                                            &err);
                                } else {
                                        ret = s1394_channel_alloc(hal,
                                            try_chnl, generation,
                                            S1394_CHANNEL_ALLOC_LO, &old_chnl,
                                            &err);
                                }
                                /* Lock the Isoch CEC member list */
                                mutex_enter(&cec_curr->isoch_cec_mutex);

                                /* Did we get a channel? (or a bus reset) */
                                if ((ret == DDI_SUCCESS) ||
                                    (err == CMD1394_EBUSRESET))
                                        break;
                        }
                        temp_chnl_mask = temp_chnl_mask >> 1;
                }

                /* If we've tried all the possible channels, then fail */
                if (chnl_num == 0) {
                        *result = T1394_ENO_CHANNEL;
                        /*
                         * If we successfully allocate bandwidth, and
                         * then fail getting a channel, we need to
                         * free up the bandwidth
                         */

                        /* Check that the generation has not changed */
                        /* lock not needed here (read only) */
                        if (generation != hal->generation_count)
                                continue;

                        /* Unlock the Isoch CEC member list */
                        mutex_exit(&cec_curr->isoch_cec_mutex);

                        /* Try to free up the bandwidth */
                        ret = s1394_bandwidth_free(hal, bw_alloc_units,
                            generation, &err);

                        /* Lock the Isoch CEC member list */
                        mutex_enter(&cec_curr->isoch_cec_mutex);

                        if (ret == DDI_FAILURE) {
                                if (err == CMD1394_EBUSRESET) {
                                        continue;
                                }
                        }

                        /* Unlock the Isoch CEC member list */
                        mutex_exit(&cec_curr->isoch_cec_mutex);
                        return (DDI_FAILURE);
                }

                /* If we got a channel, we're done (else start over) */
                if (ret == DDI_SUCCESS)
                        break;
                else if (err == CMD1394_EBUSRESET)
                        continue;
        }

        /* Have we gotten too many bus resets? */
        if (j == S1394_ISOCH_ALLOC_RETRIES) {
                *result = T1394_ENO_BANDWIDTH;
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        cec_curr->realloc_valid     = B_TRUE;
        cec_curr->realloc_chnl_num  = chnl_num;
        cec_curr->realloc_bandwidth = cec_curr->bandwidth;
        cec_curr->realloc_speed     = cec_curr->filter_current_speed;

setup_do_callbacks:
        /* Call all of the setup_target() callbacks */
        target_args.channel_num     = chnl_num;
        target_args.channel_speed   = cec_curr->filter_current_speed;

        /* Now we are going into the callbacks */
        cec_curr->in_callbacks      = B_TRUE;

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);

        member_curr = cec_curr->cec_member_list_head;
        *result = 0;
        while (member_curr != NULL) {
                if (member_curr->isoch_cec_evts.setup_target != NULL) {
                        setup_callback =
                            member_curr->isoch_cec_evts.setup_target;
                        ret = setup_callback(t1394_isoch_cec_hdl,
                            member_curr->isoch_cec_evts_arg, &target_args);
                        if (ret != DDI_SUCCESS)
                                *result = T1394_ETARGET;
                }
                member_curr = member_curr->cec_mem_next;
        }

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* We are finished with the callbacks */
        cec_curr->in_callbacks = B_FALSE;
        if (cec_curr->cec_want_wakeup == B_TRUE) {
                cec_curr->cec_want_wakeup = B_FALSE;
                cv_broadcast(&cec_curr->in_callbacks_cv);
        }

        /*
         * Now "start" and "teardown" are legal state transitions
         * and "join", "free", and "setup" are illegal state transitions
         */
        CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
        CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_JOIN | ISOCH_CEC_FREE |
            ISOCH_CEC_SETUP));

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);

        /* Return DDI_FAILURE if any targets failed setup */
        if (*result != 0) {
                return (DDI_FAILURE);
        }

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_start_isoch_cec()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              t1394_isoch_cec_hdl     The Isoch CEC "handle" returned by
 *                                          t1394_alloc_isoch_cec()
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   DDI_SUCCESS             All start_target() callbacks returned
 *                                          successfully
 *              DDI_FAILURE             One or more start_target() callbacks
 *                                          returned failure
 *
 * Description: t1394_start_isoch_cec() directs the 1394 Software Framework
 *              to invoke each of the start_target() callbacks, first for
 *              each listener, then for the talker.
 */
/* ARGSUSED */
int
t1394_start_isoch_cec(t1394_handle_t t1394_hdl,
    t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
        s1394_isoch_cec_t        *cec_curr;
        s1394_isoch_cec_member_t *member_curr;
        int                      ret;
        boolean_t                err;
        int     (*start_callback)(t1394_isoch_cec_handle_t, opaque_t);

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_isoch_cec_hdl != NULL);

        /* Convert the handle to an Isoch CEC pointer */
        cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* Are we in any callbacks? */
        if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Is "start" a legal state transition? */
        if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_START) == 0) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Now we are going into the callbacks */
        cec_curr->in_callbacks = B_TRUE;

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);

        /*
         * Call all of the start_target() callbacks
         * Start at the tail (listeners first) and
         * go toward the head (talker last)
         */
        member_curr = cec_curr->cec_member_list_tail;
        err = B_FALSE;
        while (member_curr != NULL) {
                if (member_curr->isoch_cec_evts.start_target != NULL) {
                        start_callback =
                            member_curr->isoch_cec_evts.start_target;
                        ret = start_callback(t1394_isoch_cec_hdl,
                            member_curr->isoch_cec_evts_arg);
                if (ret != DDI_SUCCESS)
                        err = B_TRUE;
                }
                member_curr = member_curr->cec_mem_prev;
        }

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* We are finished with the callbacks */
        cec_curr->in_callbacks = B_FALSE;
        if (cec_curr->cec_want_wakeup == B_TRUE) {
                cec_curr->cec_want_wakeup = B_FALSE;
                cv_broadcast(&cec_curr->in_callbacks_cv);
        }

        /*
         * Now "stop" is a legal state transitions
         * and "start" and "teardown" are illegal state transitions
         */
        CEC_SET_LEGAL(cec_curr, ISOCH_CEC_STOP);
        CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);

        /* Return DDI_FAILURE if any targets failed start */
        if (err == B_TRUE) {
                return (DDI_FAILURE);
        }

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_stop_isoch_cec()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              t1394_isoch_cec_hdl     The Isoch CEC "handle" returned by
 *                                          t1394_alloc_isoch_cec()
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   DDI_SUCCESS             Target successfully stopped the
 *                                          Isoch CEC
 *              DDI_FAILURE             Target failed to stop the Isoch CEC
 *
 * Description: t1394_stop_isoch_cec() directs the 1394 Software Framework
 *              to invoke each of the stop_target() callbacks, first for
 *              the talker, then for each listener.
 *              (This call will fail if it is called at an
 *              inappropriate time, i.e. before the t1394_start_isoch_cec()
 *              call, etc.)
 */
/* ARGSUSED */
int
t1394_stop_isoch_cec(t1394_handle_t t1394_hdl,
    t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
        s1394_isoch_cec_t        *cec_curr;
        s1394_isoch_cec_member_t *member_curr;
        void    (*stop_callback)(t1394_isoch_cec_handle_t, opaque_t);

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_isoch_cec_hdl != NULL);

        /* Convert the handle to an Isoch CEC pointer */
        cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* Are we in any callbacks? */
        if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Is "stop" a legal state transition? */
        if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_STOP) == 0) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Now we are going into the callbacks */
        cec_curr->in_callbacks = B_TRUE;

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);

        /*
         * Call all of the stop_target() callbacks
         * Start at the head (talker first) and
         * go toward the tail (listeners last)
         */
        member_curr = cec_curr->cec_member_list_head;
        while (member_curr != NULL) {
                if (member_curr->isoch_cec_evts.stop_target != NULL) {
                        stop_callback =
                            member_curr->isoch_cec_evts.stop_target;
                        stop_callback(t1394_isoch_cec_hdl,
                            member_curr->isoch_cec_evts_arg);
                }
                member_curr = member_curr->cec_mem_next;
        }

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* We are finished with the callbacks */
        cec_curr->in_callbacks = B_FALSE;
        if (cec_curr->cec_want_wakeup == B_TRUE) {
                cec_curr->cec_want_wakeup = B_FALSE;
                cv_broadcast(&cec_curr->in_callbacks_cv);
        }

        /*
         * Now "start" and "teardown" are legal state transitions
         * and "stop" is an illegal state transitions
         */
        CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));
        CEC_SET_ILLEGAL(cec_curr, ISOCH_CEC_STOP);

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);

        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_teardown_isoch_cec()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              t1394_isoch_cec_hdl     The Isoch CEC "handle" returned by
 *                                          t1394_alloc_isoch_cec()
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   DDI_SUCCESS             Target successfully tore down the
 *                                          Isoch CEC
 *              DDI_FAILURE             Target failed to tear down the
 *                                          Isoch CEC
 *
 * Description: t1394_teardown_isoch_cec() directs the 1394 Software Framework
 *              to free up any isochronous resources we might be holding and
 *              call all of the teardown_target() callbacks.
 *              (This call will fail if it is called at an
 *              inappropriate time, i.e. before the t1394_start_isoch_cec()
 *              call, before the t1394_stop_isoch_cec, etc.
 */
/* ARGSUSED */
int
t1394_teardown_isoch_cec(t1394_handle_t t1394_hdl,
    t1394_isoch_cec_handle_t t1394_isoch_cec_hdl, uint_t flags)
{
        s1394_hal_t              *hal;
        s1394_isoch_cec_t        *cec_curr;
        s1394_isoch_cec_member_t *member_curr;
        uint32_t                 chnl_mask;
        uint32_t                 old_chnl_mask;
        uint_t                   bw_alloc_units;
        uint_t                   generation;
        int                      ret;
        int                      err;
        void    (*teardown_callback)(t1394_isoch_cec_handle_t, opaque_t);

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_isoch_cec_hdl != NULL);

        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Convert the handle to an Isoch CEC pointer */
        cec_curr = (s1394_isoch_cec_t *)t1394_isoch_cec_hdl;

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* Are we in any callbacks? */
        if (CEC_IN_ANY_CALLBACKS(cec_curr)) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* Is "teardown" a legal state transition? */
        if (CEC_TRANSITION_LEGAL(cec_curr, ISOCH_CEC_TEARDOWN) == 0) {
                /* Unlock the Isoch CEC member list */
                mutex_exit(&cec_curr->isoch_cec_mutex);
                return (DDI_FAILURE);
        }

        /* If T1394_NO_IRM_ALLOC is set then don't free... do callbacks */
        if (cec_curr->cec_options & T1394_NO_IRM_ALLOC) {
                goto teardown_do_callbacks;
        }

        /* If nothing has been allocated or we failed to */
        /* reallocate, then we are done... call the callbacks */
        if ((cec_curr->realloc_valid == B_FALSE) ||
            (cec_curr->realloc_failed == B_TRUE)) {
                goto teardown_do_callbacks;
        }

        /*
         * Get the current generation number - don't need the
         * topology tree mutex here because it is read-only, and
         * there is a race condition with or without it.
         */
        generation = hal->generation_count;

        /* Compute the amount bandwidth to free */
        bw_alloc_units = s1394_compute_bw_alloc_units(hal,
            cec_curr->bandwidth, cec_curr->realloc_speed);

        /* Check that the generation has not changed - */
        /* don't need the lock (read only) */
        if (generation != hal->generation_count)
                goto teardown_do_callbacks;

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);

        /* Try to free up the bandwidth */
        ret = s1394_bandwidth_free(hal, bw_alloc_units, generation, &err);

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        if (ret == DDI_FAILURE) {
                if (err == CMD1394_EBUSRESET) {
                        goto teardown_do_callbacks;
                }
        }

        /* Free the allocated channel */
        chnl_mask = (1 << ((63 - cec_curr->realloc_chnl_num) % 32));

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);
        if (cec_curr->realloc_chnl_num < 32) {
                ret = s1394_channel_free(hal, chnl_mask, generation,
                    S1394_CHANNEL_ALLOC_HI, &old_chnl_mask, &err);
        } else {
                ret = s1394_channel_free(hal, chnl_mask, generation,
                    S1394_CHANNEL_ALLOC_LO, &old_chnl_mask, &err);
        }
        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

teardown_do_callbacks:
        /* From here on reallocation is unnecessary */
        cec_curr->realloc_valid     = B_FALSE;
        cec_curr->realloc_chnl_num  = 0;
        cec_curr->realloc_bandwidth = 0;

        /* Now we are going into the callbacks */
        cec_curr->in_callbacks      = B_TRUE;

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);

        /* Call all of the teardown_target() callbacks */
        member_curr = cec_curr->cec_member_list_head;
        while (member_curr != NULL) {
                if (member_curr->isoch_cec_evts.teardown_target != NULL) {
                        teardown_callback =
                            member_curr->isoch_cec_evts.teardown_target;
                        teardown_callback(t1394_isoch_cec_hdl,
                            member_curr->isoch_cec_evts_arg);
                }
                member_curr = member_curr->cec_mem_next;
        }

        /* Lock the Isoch CEC member list */
        mutex_enter(&cec_curr->isoch_cec_mutex);

        /* We are finished with the callbacks */
        cec_curr->in_callbacks = B_FALSE;
        if (cec_curr->cec_want_wakeup == B_TRUE) {
                cec_curr->cec_want_wakeup = B_FALSE;
                cv_broadcast(&cec_curr->in_callbacks_cv);
        }

        /*
         * Now "join" and "setup" are legal state transitions
         * and "start" and "teardown" are illegal state transitions
         */
        CEC_SET_LEGAL(cec_curr, (ISOCH_CEC_JOIN | ISOCH_CEC_SETUP));
        CEC_SET_ILLEGAL(cec_curr, (ISOCH_CEC_START | ISOCH_CEC_TEARDOWN));

        /* And if the member list is empty, then "free" is legal too */
        if ((cec_curr->cec_member_list_head == NULL) &&
            (cec_curr->cec_member_list_tail == NULL)) {
                CEC_SET_LEGAL(cec_curr, ISOCH_CEC_FREE);
        }

        /* Unlock the Isoch CEC member list */
        mutex_exit(&cec_curr->isoch_cec_mutex);
        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_alloc_isoch_dma()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              idi                     This structure contains information
 *                                          for configuring the data flow for
 *                                          isochronous DMA
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   t1394_idma_hdl          The IDMA "handle" used in all
 *                                          subsequent isoch_dma() calls
 *              result                  Used to pass more specific info back
 *                                          to target
 *
 * Description: t1394_alloc_isoch_dma() allocates and initializes an
 *              isochronous DMA resource for transmitting or receiving
 *              isochronous data.  If it fails, result may hold
 *              T1394_EIDMA_NO_RESRCS, indicating that no isoch DMA resource
 *              are available.
 */
/* ARGSUSED */
int
t1394_alloc_isoch_dma(t1394_handle_t t1394_hdl,
    id1394_isoch_dmainfo_t *idi, uint_t flags,
    t1394_isoch_dma_handle_t *t1394_idma_hdl, int *result)
{
        s1394_hal_t     *hal;
        int             ret;

        ASSERT(t1394_hdl != NULL);
        ASSERT(idi != NULL);
        ASSERT(t1394_idma_hdl != NULL);

        /* Find the HAL this target resides on */
        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Sanity check dma options.  If talk enabled, listen should be off */
        if ((idi->idma_options & ID1394_TALK) &&
            (idi->idma_options != ID1394_TALK)) {
                *result = T1394_EIDMA_CONFLICT;
                return (DDI_FAILURE);
        }

        /* Only one listen mode allowed */
        if ((idi->idma_options & ID1394_LISTEN_PKT_MODE) &&
            (idi->idma_options & ID1394_LISTEN_BUF_MODE)) {
                *result = T1394_EIDMA_CONFLICT;
                return (DDI_FAILURE);
        }

        /* Have HAL alloc a resource and compile ixl */
        ret = HAL_CALL(hal).alloc_isoch_dma(hal->halinfo.hal_private, idi,
            (void **)t1394_idma_hdl, result);

        if (ret != DDI_SUCCESS) {
                if (*result == IXL1394_ENO_DMA_RESRCS) {
                        *result = T1394_EIDMA_NO_RESRCS;
                }
        }

        return (ret);
}

/*
 * Function:    t1394_free_isoch_dma()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              flags                   The flags parameter is unused (for now)
 *              t1394_idma_hdl          The IDMA "handle" returned by
 *                                          t1394_alloc_isoch_dma()
 *
 * Output(s):   None
 *
 * Description: t1394_free_isoch_dma() is used to free all DMA resources
 *              allocated for the isoch stream associated with t1394_idma_hdl.
 */
/* ARGSUSED */
void
t1394_free_isoch_dma(t1394_handle_t t1394_hdl, uint_t flags,
    t1394_isoch_dma_handle_t *t1394_idma_hdl)
{
        s1394_hal_t     *hal;

        ASSERT(t1394_hdl != NULL);
        ASSERT(*t1394_idma_hdl != NULL);

        /* Find the HAL this target resides on */
        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Tell HAL to release local isoch dma resources */
        HAL_CALL(hal).free_isoch_dma(hal->halinfo.hal_private, *t1394_idma_hdl);

        /* Null out isoch handle */
        *t1394_idma_hdl = NULL;
}

/*
 * Function:    t1394_start_isoch_dma()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              t1394_idma_hdl          The IDMA "handle" returned by
 *                                          t1394_alloc_isoch_dma()
 *              idma_ctrlinfo           This structure contains control args
 *                                          used when starting isoch DMA for
 *                                          the allocated resource
 *              flags                   One flag defined - ID1394_START_ON_CYCLE
 *
 * Output(s):   result                  Used to pass more specific info back
 *                                          to target
 *
 * Description: t1394_start_isoch_dma() is used to start DMA for the isoch
 *              stream associated with t1394_idma_hdl.
 */
/* ARGSUSED */
int
t1394_start_isoch_dma(t1394_handle_t t1394_hdl,
    t1394_isoch_dma_handle_t t1394_idma_hdl,
    id1394_isoch_dma_ctrlinfo_t *idma_ctrlinfo, uint_t flags,
    int *result)
{
        s1394_hal_t     *hal;
        int             ret;

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_idma_hdl != NULL);
        ASSERT(idma_ctrlinfo != NULL);

        /* Find the HAL this target resides on */
        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        ret = HAL_CALL(hal).start_isoch_dma(hal->halinfo.hal_private,
            (void *)t1394_idma_hdl, idma_ctrlinfo, flags, result);

        return (ret);
}

/*
 * Function:    t1394_stop_isoch_dma()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              t1394_idma_hdl          The IDMA "handle" returned by
 *                                          t1394_alloc_isoch_dma()
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   None
 *
 * Description: t1394_stop_isoch_dma() is used to stop DMA for the isoch
 *              stream associated with t1394_idma_hdl.
 */
/* ARGSUSED */
void
t1394_stop_isoch_dma(t1394_handle_t t1394_hdl,
    t1394_isoch_dma_handle_t t1394_idma_hdl, uint_t flags)
{
        s1394_hal_t     *hal;
        int             result;

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_idma_hdl != NULL);

        /* Find the HAL this target resides on */
        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        HAL_CALL(hal).stop_isoch_dma(hal->halinfo.hal_private,
            (void *)t1394_idma_hdl, &result);
}

/*
 * Function:    t1394_update_isoch_dma()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              t1394_idma_hdl          The IDMA "handle" returned by
 *                                          t1394_alloc_isoch_dma()
 *              idma_updateinfo         This structure contains ixl command args
 *                                          used when updating args in an
 *                                          existing list of ixl commands with
 *                                          args in a new list of ixl commands.
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   result                  Used to pass more specific info back
 *                                          to target
 *
 * Description: t1394_update_isoch_dma() is used to alter an IXL program that
 *              has already been built (compiled) by t1394_alloc_isoch_dma().
 */
/* ARGSUSED */
int
t1394_update_isoch_dma(t1394_handle_t t1394_hdl,
    t1394_isoch_dma_handle_t t1394_idma_hdl,
    id1394_isoch_dma_updateinfo_t *idma_updateinfo, uint_t flags,
    int *result)
{
        s1394_hal_t     *hal;
        int             ret;

        ASSERT(t1394_hdl != NULL);
        ASSERT(t1394_idma_hdl != NULL);
        ASSERT(idma_updateinfo != NULL);

        /* Find the HAL this target resides on */
        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        ret = HAL_CALL(hal).update_isoch_dma(hal->halinfo.hal_private,
            (void *)t1394_idma_hdl, idma_updateinfo, flags, result);

        return (ret);
}

/*
 * Function:    t1394_initiate_bus_reset()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   None
 *
 * Description: t1394_initiate_bus_reset() determines whether the local
 *              device has a P1394A PHY and will support the arbitrated
 *              short bus reset. If not, it will initiate a normal bus reset.
 */
/* ARGSUSED */
void
t1394_initiate_bus_reset(t1394_handle_t t1394_hdl, uint_t flags)
{
        s1394_hal_t     *hal;

        ASSERT(t1394_hdl != NULL);

        /* Find the HAL this target resides on */
        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Reset the bus */
        if (hal->halinfo.phy == H1394_PHY_1394A) {
                (void) HAL_CALL(hal).short_bus_reset(hal->halinfo.hal_private);
        } else {
                (void) HAL_CALL(hal).bus_reset(hal->halinfo.hal_private);
        }
}

/*
 * Function:    t1394_get_topology_map()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              bus_generation          The current generation
 *              tm_length               The size of the tm_buffer given
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   tm_buffer               Filled in by the 1394 Software Framework
 *                                          with the contents of the local
 *                                          TOPOLOGY_MAP
 *
 * Description: t1394_get_topology_map() returns the 1394 TOPLOGY_MAP.  See
 *              IEEE 1394-1995 Section 8.2.3.4.1 for format information.  This
 *              call can fail if there is a generation mismatch or the
 *              tm_buffer is too small to hold the TOPOLOGY_MAP.
 */
/* ARGSUSED */
int
t1394_get_topology_map(t1394_handle_t t1394_hdl, uint_t bus_generation,
    size_t tm_length, uint_t flags, uint32_t *tm_buffer)
{
        s1394_hal_t     *hal;
        uint32_t        *tm_ptr;
        uint_t          length;

        ASSERT(t1394_hdl != NULL);

        /* Find the HAL this target resides on */
        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        /* Lock the topology tree */
        mutex_enter(&hal->topology_tree_mutex);

        /* Check the bus_generation for the Topology Map */
        if (bus_generation != hal->generation_count) {
                /* Unlock the topology tree */
                mutex_exit(&hal->topology_tree_mutex);
                return (DDI_FAILURE);
        }

        tm_ptr  = (uint32_t *)hal->CSR_topology_map;
        length  = tm_ptr[0] >> 16;
        length  = length * 4;   /* Bytes instead of quadlets   */
        length  = length + 4;   /* don't forget the first quad */

        /* Check that the buffer is big enough */
        if (length > (uint_t)tm_length) {
                /* Unlock the topology tree */
                mutex_exit(&hal->topology_tree_mutex);
                return (DDI_FAILURE);
        }

        /* Do the copy */
        bcopy(tm_ptr, tm_buffer, length);

        /* Unlock the topology tree */
        mutex_exit(&hal->topology_tree_mutex);
        return (DDI_SUCCESS);
}

/*
 * Function:    t1394_CRC16()
 * Input(s):    d                       The data to compute the CRC-16 for
 *              crc_length              The length into the data to compute for
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   CRC                     The CRC-16 computed for the length
 *                                          of data specified
 *
 * Description: t1394_CRC16() implements ISO/IEC 13213:1994, ANSI/IEEE Std
 *              1212, 1994 - 8.1.5.
 */
/* ARGSUSED */
uint_t
t1394_CRC16(uint32_t *d, size_t crc_length, uint_t flags)
{
        /* Implements ISO/IEC 13213:1994,       */
        /* ANSI/IEEE Std 1212, 1994 - 8.1.5     */
        uint_t  ret;

        ret = s1394_CRC16((uint_t *)d, (uint_t)crc_length);

        return (ret);
}

/*
 * Function:    t1394_add_cfgrom_entry()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              cfgrom_entryinfo        This structure holds the cfgrom key,
 *                                          buffer, and size
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   t1394_cfgrom_hdl        The ConfigROM "handle" used in
 *                                          t1394_rem_cfgrom_entry()
 *              result                  Used to pass more specific info back
 *                                          to target
 *
 * Description: t1394_add_cfgrom_entry() adds an entry to the local Config ROM,
 *              updating the directory entries as necessary.  This call could
 *              fail because there is no room for the new entry in Config ROM
 *              (T1394_ECFGROM_FULL), the key is invalid (T1394_EINVALID_PARAM),
 *              or it was called in interrupt context (T1394_EINVALID_CONTEXT).
 */
/* ARGSUSED */
int
t1394_add_cfgrom_entry(t1394_handle_t t1394_hdl,
    t1394_cfgrom_entryinfo_t *cfgrom_entryinfo, uint_t flags,
    t1394_cfgrom_handle_t *t1394_cfgrom_hdl, int *result)
{
        s1394_hal_t     *hal;
        s1394_target_t  *target;
        int             ret;
        uint_t          key;
        uint_t          size;
        uint32_t        *buffer;

        ASSERT(t1394_hdl != NULL);

        target = (s1394_target_t *)t1394_hdl;

        key = cfgrom_entryinfo->ce_key;
        buffer = cfgrom_entryinfo->ce_buffer;
        size = (uint_t)cfgrom_entryinfo->ce_size;

        /* Check for a valid size */
        if (size == 0) {
                *result = T1394_EINVALID_PARAM;
                return (DDI_FAILURE);
        }

        /* Check for a valid key type */
        if (((key << IEEE1212_KEY_VALUE_SHIFT) & IEEE1212_KEY_TYPE_MASK) == 0) {
                *result = T1394_EINVALID_PARAM;
                return (DDI_FAILURE);
        }

        /* Find the HAL this target resides on */
        hal = target->on_hal;

        /* Is this on the interrupt stack? */
        if (servicing_interrupt()) {
                *result = T1394_EINVALID_CONTEXT;
                return (DDI_FAILURE);
        }

        /* Lock the Config ROM buffer */
        mutex_enter(&hal->local_config_rom_mutex);

        ret = s1394_add_config_rom_entry(hal, key, buffer, size,
            (void **)t1394_cfgrom_hdl, result);
        if (ret != DDI_SUCCESS) {
                if (*result == CMD1394_ERSRC_CONFLICT)
                        *result = T1394_ECFGROM_FULL;
                mutex_exit(&hal->local_config_rom_mutex);

                return (ret);
        }

        /* Setup the timeout function */
        if (hal->config_rom_timer_set == B_FALSE) {
                hal->config_rom_timer_set = B_TRUE;
                mutex_exit(&hal->local_config_rom_mutex);
                hal->config_rom_timer =
                    timeout(s1394_update_config_rom_callback, hal,
                    drv_usectohz(CONFIG_ROM_UPDATE_DELAY * 1000));
        } else {
                mutex_exit(&hal->local_config_rom_mutex);
        }

        return (ret);
}

/*
 * Function:    t1394_rem_cfgrom_entry()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              flags                   The flags parameter is unused (for now)
 *              t1394_cfgrom_hdl        The ConfigROM "handle" returned by
 *                                          t1394_add_cfgrom_entry()
 *
 * Output(s):   result                  Used to pass more specific info back
 *                                          to target
 *
 * Description: t1394_rem_cfgrom_entry() is used to remove a previously added
 *              Config ROM entry (indicated by t1394_cfgrom_hdl).
 */
/* ARGSUSED */
int
t1394_rem_cfgrom_entry(t1394_handle_t t1394_hdl, uint_t flags,
    t1394_cfgrom_handle_t *t1394_cfgrom_hdl, int *result)
{
        s1394_hal_t     *hal;
        s1394_target_t  *target;
        int             ret;

        ASSERT(t1394_hdl != NULL);

        target = (s1394_target_t *)t1394_hdl;

        /* Find the HAL this target resides on */
        hal = target->on_hal;

        /* Is this on the interrupt stack? */
        if (servicing_interrupt()) {
                *result = T1394_EINVALID_CONTEXT;
                return (DDI_FAILURE);
        }

        /* Lock the Config ROM buffer */
        mutex_enter(&hal->local_config_rom_mutex);

        ret = s1394_remove_config_rom_entry(hal, (void **)t1394_cfgrom_hdl,
            result);
        if (ret != DDI_SUCCESS) {
                mutex_exit(&hal->local_config_rom_mutex);
                return (ret);
        }

        /* Setup the timeout function */
        if (hal->config_rom_timer_set == B_FALSE) {
                hal->config_rom_timer_set = B_TRUE;
                mutex_exit(&hal->local_config_rom_mutex);
                hal->config_rom_timer =
                    timeout(s1394_update_config_rom_callback, hal,
                    drv_usectohz(CONFIG_ROM_UPDATE_DELAY * 1000));
        } else {
                mutex_exit(&hal->local_config_rom_mutex);
        }

        return (ret);
}

/*
 * Function:    t1394_get_targetinfo()
 * Input(s):    t1394_hdl               The target "handle" returned by
 *                                          t1394_attach()
 *              bus_generation          The current generation
 *              flags                   The flags parameter is unused (for now)
 *
 * Output(s):   targetinfo              Structure containing max_payload,
 *                                          max_speed, and target node ID.
 *
 * Description: t1394_get_targetinfo() is used to retrieve information specific
 *              to a target device.  It will fail if the generation given
 *              does not match the current generation.
 */
/* ARGSUSED */
int
t1394_get_targetinfo(t1394_handle_t t1394_hdl, uint_t bus_generation,
    uint_t flags, t1394_targetinfo_t *targetinfo)
{
        s1394_hal_t     *hal;
        s1394_target_t  *target;
        uint_t          dev;
        uint_t          curr;
        uint_t          from_node;
        uint_t          to_node;

        ASSERT(t1394_hdl != NULL);

        /* Find the HAL this target resides on */
        hal = ((s1394_target_t *)t1394_hdl)->on_hal;

        target = (s1394_target_t *)t1394_hdl;

        /* Lock the topology tree */
        mutex_enter(&hal->topology_tree_mutex);

        /* Check the bus_generation */
        if (bus_generation != hal->generation_count) {
                /* Unlock the topology tree */
                mutex_exit(&hal->topology_tree_mutex);
                return (DDI_FAILURE);
        }

        rw_enter(&hal->target_list_rwlock, RW_READER);
        /*
         * If there is no node, report T1394_INVALID_NODEID for target_nodeID;
         * current_max_speed and current_max_payload are undefined for this
         * case.
         */
        if (((target->target_state & S1394_TARG_GONE) != 0) ||
            (target->on_node == NULL)) {
                targetinfo->target_nodeID = T1394_INVALID_NODEID;
        } else {
                targetinfo->target_nodeID =
                    (target->on_hal->node_id & IEEE1394_BUS_NUM_MASK) |
                    target->on_node->node_num;

                from_node = (target->on_hal->node_id) & IEEE1394_NODE_NUM_MASK;
                to_node = target->on_node->node_num;

                targetinfo->current_max_speed = (uint_t)s1394_speed_map_get(
                    hal, from_node, to_node);

                /* Get current_max_payload */
                s1394_get_maxpayload(target, &dev, &curr);
                targetinfo->current_max_payload = curr;
        }

        rw_exit(&hal->target_list_rwlock);
        /* Unlock the topology tree */
        mutex_exit(&hal->topology_tree_mutex);
        return (DDI_SUCCESS);
}