root/usr/src/uts/common/io/1394/s1394_misc.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * 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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * s1394_misc.c
 *    1394 Services Layer Miscellaneous Routines
 *    This file contains miscellaneous routines used as "helper" functions
 *    by various other files in the Services Layer.
 */

#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/kstat.h>
#include <sys/1394/t1394.h>
#include <sys/1394/s1394.h>
#include <sys/1394/h1394.h>
#include <sys/1394/ieee1394.h>

int s1394_print_guids = 0;              /* patch to print GUIDs */

extern void nx1394_undefine_events(s1394_hal_t *hal);
static void s1394_cleanup_node_cfgrom(s1394_hal_t *hal);

/*
 * s1394_cleanup_for_detach()
 *    is used to do all of the necessary cleanup to handle a detach or a
 *    failure in h1394_attach().  The cleanup_level specifies how far we
 *    got in h1394_attach() before failure.
 */
void
s1394_cleanup_for_detach(s1394_hal_t *hal, uint_t cleanup_level)
{

        switch (cleanup_level) {
        case H1394_CLEANUP_LEVEL7:
                /* remove HAL from the global HAL list */
                mutex_enter(&s1394_statep->hal_list_mutex);
                if ((s1394_statep->hal_head == hal) &&
                    (s1394_statep->hal_tail == hal)) {
                        s1394_statep->hal_head = NULL;
                        s1394_statep->hal_tail = NULL;
                } else {
                        if (hal->hal_prev)
                                hal->hal_prev->hal_next = hal->hal_next;
                        if (hal->hal_next)
                                hal->hal_next->hal_prev = hal->hal_prev;
                        if (s1394_statep->hal_head == hal)
                                s1394_statep->hal_head = hal->hal_next;
                        if (s1394_statep->hal_tail == hal)
                                s1394_statep->hal_tail = hal->hal_prev;
                }
                mutex_exit(&s1394_statep->hal_list_mutex);
                /*
                 * No FCP cleanup needed at this time -- the following call
                 * to s1394_destroy_addr_space() takes care of everything.
                 */
                /* FALLTHROUGH */

        case H1394_CLEANUP_LEVEL6:
                s1394_destroy_addr_space(hal);
                /* FALLTHROUGH */

        case H1394_CLEANUP_LEVEL5:
                s1394_destroy_local_config_rom(hal);
                /* FALLTHROUGH */

        case H1394_CLEANUP_LEVEL4:
                /* Undo all the kstat stuff */
                (void) s1394_kstat_delete(hal);
                /* FALLTHROUGH */

        case H1394_CLEANUP_LEVEL3:
                /* Free up the memory for selfID buffer #1 */
                kmem_free(hal->selfid_buf1, S1394_SELFID_BUF_SIZE);
                /* Free up the memory for selfID buffer #0 */
                kmem_free(hal->selfid_buf0, S1394_SELFID_BUF_SIZE);
                /* Turn off any timers that might be set */
                s1394_destroy_timers(hal);
                /* Destroy the bus_reset thread */
                s1394_destroy_br_thread(hal);
                /* Cleanup the Config ROM buffers in the topology_tree */
                s1394_cleanup_node_cfgrom(hal);
                /* FALLTHROUGH */

        case H1394_CLEANUP_LEVEL2:
                /* Destroy the br_cmplq_cv and br_cmplq_mutex */
                cv_destroy(&hal->br_cmplq_cv);
                mutex_destroy(&hal->br_cmplq_mutex);
                /* Destroy the br_thread_cv and br_thread_mutex */
                cv_destroy(&hal->br_thread_cv);
                mutex_destroy(&hal->br_thread_mutex);
                /* FALLTHROUGH */

        case H1394_CLEANUP_LEVEL1:
                (void) ddi_prop_remove_all(hal->halinfo.dip);
                nx1394_undefine_events(hal);
                /* FALLTHROUGH */

        case H1394_CLEANUP_LEVEL0:
                kmem_cache_destroy(hal->hal_kmem_cachep);
                /* Destroy pending_q_mutex and outstanding_q_mutex */
                mutex_destroy(&hal->pending_q_mutex);
                mutex_destroy(&hal->outstanding_q_mutex);
                /* Destroy target_list_rwlock */
                rw_destroy(&hal->target_list_rwlock);
                /* Destroy bus_mgr_node_mutex and bus_mgr_node_cv */
                cv_destroy(&hal->bus_mgr_node_cv);
                mutex_destroy(&hal->bus_mgr_node_mutex);
                /* Destroy isoch_cec_list_mutex */
                mutex_destroy(&hal->isoch_cec_list_mutex);
                /* Destroy the Cycle Master timer mutex */
                mutex_destroy(&hal->cm_timer_mutex);
                /* Destroy topology_tree_mutex */
                mutex_destroy(&hal->topology_tree_mutex);
                /* Free the hal structure */
                kmem_free(hal, sizeof (s1394_hal_t));
                break;

        default:
                /* Error */
                break;
        }
}

/*
 * s1394_hal_shutdown()
 *    is used to shutdown the HAL.  If the HAL indicates that an error
 *    condition (hardware or software) has occurred, it is shutdown. This
 *    routine is also called when HAL informs the services layer of a shutdown
 *    (due an internal shutdown, for eg). disable_hal indicates whether the
 *    caller intends to inform the hal of the (services layer) shutdown or not.
 */
void
s1394_hal_shutdown(s1394_hal_t *hal, boolean_t disable_hal)
{
        ddi_eventcookie_t cookie;
        t1394_localinfo_t localinfo;

        mutex_enter(&hal->topology_tree_mutex);

        if (hal->hal_state == S1394_HAL_SHUTDOWN) {
                mutex_exit(&hal->topology_tree_mutex);
                if (disable_hal == B_TRUE)
                        HAL_CALL(hal).shutdown(hal->halinfo.hal_private);

                return;
        }

        hal->hal_state = S1394_HAL_SHUTDOWN;
        mutex_exit(&hal->topology_tree_mutex);
        /* Disable the HAL */
        if (disable_hal == B_TRUE)
                HAL_CALL(hal).shutdown(hal->halinfo.hal_private);

        /*
         * Send a remove event to all interested parties
         */
        mutex_enter(&hal->topology_tree_mutex);
        localinfo.bus_generation = hal->generation_count;
        localinfo.local_nodeID   = hal->node_id;
        mutex_exit(&hal->topology_tree_mutex);

        if (ndi_event_retrieve_cookie(hal->hal_ndi_event_hdl, NULL,
            DDI_DEVI_REMOVE_EVENT, &cookie, NDI_EVENT_NOPASS) ==
            NDI_SUCCESS)
                (void) ndi_event_run_callbacks(hal->hal_ndi_event_hdl, NULL,
                    cookie, &localinfo);
}

/*
 * s1394_initiate_hal_reset()
 *    sets up the HAL structure to indicate a self-initiated bus reset and
 *    calls the appropriate HAL entry point.  If too many bus resets have
 *    happened, a message is printed out and the call is ignored.
 */
void
s1394_initiate_hal_reset(s1394_hal_t *hal, int reason)
{
        if (hal->num_bus_reset_till_fail > 0) {
                hal->initiated_bus_reset = B_TRUE;
                hal->initiated_br_reason = reason;

                /* Reset the bus */
                (void) HAL_CALL(hal).bus_reset(hal->halinfo.hal_private);
        } else {
                cmn_err(CE_NOTE, "Unable to reenumerate the 1394 bus - If new"
                    " devices have recently been added, remove them.");
        }
}

/*
 * s1394_on_br_thread()
 *    is used to determine if the current thread of execution is the same
 *    as the bus reset thread.  This is useful during bus reset callbacks
 *    to determine whether or not a target may block.
 */
boolean_t
s1394_on_br_thread(s1394_hal_t *hal)
{
        if (hal->br_thread == curthread)
                return (B_TRUE);
        else
                return (B_FALSE);
}

/*
 * s1394_destroy_br_thread()
 *    is used in h1394_detach() to signal the bus reset thread to go away.
 */
void
s1394_destroy_br_thread(s1394_hal_t *hal)
{
        /* Send the signal to the reset thread to go away */
        mutex_enter(&hal->br_thread_mutex);
        hal->br_thread_ev_type |= BR_THR_GO_AWAY;
        cv_signal(&hal->br_thread_cv);
        mutex_exit(&hal->br_thread_mutex);

        /* Wakeup the bus_reset thread if waiting for bus_mgr timer */
        mutex_enter(&hal->bus_mgr_node_mutex);
        hal->bus_mgr_node = S1394_INVALID_NODE_NUM;
        cv_signal(&hal->bus_mgr_node_cv);
        mutex_exit(&hal->bus_mgr_node_mutex);

        mutex_enter(&hal->br_cmplq_mutex);
        cv_signal(&hal->br_cmplq_cv);
        mutex_exit(&hal->br_cmplq_mutex);

        /* Wait for the br_thread to be done */
        while (hal->br_thread_ev_type & BR_THR_GO_AWAY)
                delay(drv_usectohz(10));
}

/*
 * s1394_tickle_bus_reset_thread()
 *    is used to wakeup the bus reset thread after the interrupt routine
 *    has completed its bus reset processing.
 */
void
s1394_tickle_bus_reset_thread(s1394_hal_t *hal)
{
        if (hal->topology_tree_processed != B_TRUE) {
                /* Send the signal to the reset thread */
                mutex_enter(&hal->br_thread_mutex);
                hal->br_thread_ev_type |= BR_THR_CFGROM_SCAN;
                cv_signal(&hal->br_thread_cv);
                mutex_exit(&hal->br_thread_mutex);

                /* Signal the msgq wait, too (just in case) */
                mutex_enter(&hal->br_cmplq_mutex);
                cv_signal(&hal->br_cmplq_cv);
                mutex_exit(&hal->br_cmplq_mutex);

                /* Signal the bus_mgr wait, too (just in case) */
                mutex_enter(&hal->bus_mgr_node_mutex);
                cv_signal(&hal->bus_mgr_node_cv);
                mutex_exit(&hal->bus_mgr_node_mutex);
        }
}

/*
 * s1394_block_on_asynch_cmd()
 *    is used by many of the asynch routines to block (if necessary)
 *    while waiting for command completion.
 */
void
s1394_block_on_asynch_cmd(cmd1394_cmd_t *cmd)
{
        s1394_cmd_priv_t  *s_priv;

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

        /* Is this a blocking command? */
        if (cmd->cmd_options & CMD1394_BLOCKING) {
                /* Block until command completes */
                mutex_enter(&s_priv->blocking_mutex);
                while (s_priv->blocking_flag != B_TRUE)
                        cv_wait(&s_priv->blocking_cv, &s_priv->blocking_mutex);
                s_priv->blocking_flag = B_FALSE;
                mutex_exit(&s_priv->blocking_mutex);
        }
}

/*
 * s1394_HAL_asynch_error()
 *    is used by many of the asynch routines to determine what error
 *    code is expected in a given situation (based on HAL state).
 */
/* ARGSUSED */
int
s1394_HAL_asynch_error(s1394_hal_t *hal, cmd1394_cmd_t *cmd,
    s1394_hal_state_t state)
{

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

        switch (state) {
        case S1394_HAL_RESET:
                /* "dreq" bit is set (CSR) */
                if (hal->disable_requests_bit == 1)
                        return (CMD1394_ENO_ATREQ);
                else
                        return (CMD1394_CMDSUCCESS);

        case S1394_HAL_DREQ:
                /* "dreq" bit is set (CSR) */
                return (CMD1394_ENO_ATREQ);

        case S1394_HAL_SHUTDOWN:
                return (CMD1394_EFATAL_ERROR);

        default:
                return (CMD1394_CMDSUCCESS);
        }
}

/*
 * s1394_mblk_too_small()
 *    is used to determine if the mlbk_t structure(s) given in an asynch
 *    block request are sufficient to hold the amount of data requested.
 */
boolean_t
s1394_mblk_too_small(cmd1394_cmd_t *cmd)
{
        mblk_t    *curr_blk;
        boolean_t flag;
        size_t    msgb_len;
        size_t    size;

        curr_blk = cmd->cmd_u.b.data_block;
        msgb_len = 0;
        flag = B_TRUE;
        size = cmd->cmd_u.b.blk_length;

        while (curr_blk != NULL) {
                if (cmd->cmd_type == CMD1394_ASYNCH_WR_BLOCK) {
                        msgb_len += (curr_blk->b_wptr - curr_blk->b_rptr);
                } else {
                        msgb_len +=
                            (curr_blk->b_datap->db_lim - curr_blk->b_wptr);
                }

                if (msgb_len >= size) {
                        flag = B_FALSE;
                        break;
                }

                curr_blk = curr_blk->b_cont;
        }

        return (flag);
}

/*
 * s1394_address_rollover()
 *    is used to determine if the address given will rollover the 48-bit
 *    address space.
 */
boolean_t
s1394_address_rollover(cmd1394_cmd_t *cmd)
{
        uint64_t addr_before;
        uint64_t addr_after;
        size_t   length;

        switch (cmd->cmd_type) {
        case CMD1394_ASYNCH_RD_QUAD:
        case CMD1394_ASYNCH_WR_QUAD:
        case CMD1394_ASYNCH_LOCK_32:
                length = IEEE1394_QUADLET;
                break;

        case CMD1394_ASYNCH_LOCK_64:
                length = IEEE1394_OCTLET;
                break;

        case CMD1394_ASYNCH_RD_BLOCK:
        case CMD1394_ASYNCH_WR_BLOCK:
                length = cmd->cmd_u.b.blk_length;
                break;
        }

        addr_before = cmd->cmd_addr & IEEE1394_ADDR_OFFSET_MASK;
        addr_after = (addr_before + length) & IEEE1394_ADDR_OFFSET_MASK;

        if (addr_after < addr_before) {
                return (B_TRUE);
        }

        return (B_FALSE);
}

/*
 * s1394_stoi()
 *    returns the integer value of the string of hex/dec/oct numeric characters
 *    beginning at *p. Does no overflow checking.
 */
uint_t
s1394_stoi(char *p, int len, int base)
{
        int     n;
        int     c;

        if (len == 0)
                return (0);

        for (n = 0; len && (c = *p); p++, len--) {
                if (c >= '0' && c <= '9')
                        c = c - '0';
                else if (c >= 'a' && c <= 'f')
                        c = c - 'a' + 10;
                else if (c >= 'A' && c <= 'F')
                        c = c - 'F' + 10;
                n = (n * base) + c;
        }

        return (n);
}

/*
 * s1394_CRC16()
 *    implements ISO/IEC 13213:1994, ANSI/IEEE Std 1212, 1994 - 8.1.5
 */
uint_t
s1394_CRC16(uint_t *d, uint_t crc_length)
{
        uint_t  CRC = 0;
        uint_t  data;
        uint_t  next;
        uint_t  sum;
        int     shift;
        int     i;

        for (i = 0; i < crc_length; i++) {
                data = d[i];

                /* Another check should be made with "shift > 0" in  */
                /* order to support any devices that coded it wrong. */
                for (next = CRC, shift = 28; shift >= 0; shift -= 4) {
                        sum = ((next >> 12) ^ (data >> shift)) & 0xF;
                        next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
                }
                CRC = next & IEEE1394_CRC16_MASK;
        }

        return (CRC);
}

/*
 * s1394_CRC16_old()
 *    implements a slightly modified version of ISO/IEC 13213:1994,
 *    ANSI/IEEE Std 1212, 1994 - 8.1.5.  In the original IEEE 1212-1994
 *    specification the C code example was incorrect and some devices
 *    were manufactured using this incorrect CRC.  On CRC16 failures
 *    this CRC is tried in case it is a legacy device.
 */
uint_t
s1394_CRC16_old(uint_t *d, uint_t crc_length)
{
        uint_t  CRC = 0;
        uint_t  data;
        uint_t  next;
        uint_t  sum;
        int     shift;
        int     i;

        for (i = 0; i < crc_length; i++) {
                data = d[i];
                for (next = CRC, shift = 28; shift > 0; shift -= 4) {
                        sum = ((next >> 12) ^ (data >> shift)) & 0xF;
                        next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
                }
                CRC = next & IEEE1394_CRC16_MASK;
        }

        return (CRC);
}

/*
 * s1394_ioctl()
 *    implements generic ioctls (eg. devctl support) and any non-HAL ioctls.
 *    Only ioctls required for devctl support are implemented at present.
 */
/* ARGSUSED */
int
s1394_ioctl(s1394_hal_t *hal, int cmd, intptr_t arg, int mode, cred_t *cred_p,
    int *rval_p)
{
        struct devctl_iocdata   *dcp;
        dev_info_t              *self;
        int                     rv = 0;

        self = hal->halinfo.dip;

        /*
         * We can use the generic implementation for these ioctls
         */
        switch (cmd) {
        case DEVCTL_DEVICE_GETSTATE:
        case DEVCTL_DEVICE_ONLINE:
        case DEVCTL_DEVICE_OFFLINE:
        case DEVCTL_DEVICE_REMOVE:
        case DEVCTL_BUS_GETSTATE:
                return (ndi_devctl_ioctl(self, cmd, arg, mode, 0));
        }

        /* Read devctl ioctl data */
        if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) {
                return (EFAULT);
        }

        switch (cmd) {

        case DEVCTL_DEVICE_RESET:
        case DEVCTL_DEVICE_REMOVE:
                rv = ENOTSUP;
                break;

        case DEVCTL_BUS_CONFIGURE:
        case DEVCTL_BUS_UNCONFIGURE:
                rv = ENOTSUP;
                break;

        case DEVCTL_BUS_QUIESCE:
        case DEVCTL_BUS_UNQUIESCE:
                rv = ENOTSUP;   /* Or call up the tree? */
                break;

        case DEVCTL_BUS_RESET:
        case DEVCTL_BUS_RESETALL:
                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);
                }
                break;

        default:
                rv = ENOTTY;
        }

        ndi_dc_freehdl(dcp);

        return (rv);
}

/*
 * s1394_kstat_init()
 *    is used to initialize and the Services Layer's kernel statistics.
 */
int
s1394_kstat_init(s1394_hal_t *hal)
{
        int instance;

        hal->hal_kstats = (s1394_kstat_t *)kmem_zalloc(sizeof (s1394_kstat_t),
            KM_SLEEP);

        instance = ddi_get_instance(hal->halinfo.dip);

        hal->hal_ksp = kstat_create("s1394", instance, "stats", "misc",
            KSTAT_TYPE_RAW, sizeof (s1394_kstat_t), KSTAT_FLAG_VIRTUAL);
        if (hal->hal_ksp != NULL) {
                hal->hal_ksp->ks_private = (void *)hal;
                hal->hal_ksp->ks_update = s1394_kstat_update;
                kstat_install(hal->hal_ksp);

                return (DDI_SUCCESS);
        } else {
                kmem_free((void *)hal->hal_kstats, sizeof (s1394_kstat_t));
                return (DDI_FAILURE);
        }
}

/*
 * s1394_kstat_delete()
 *    is used (in h1394_detach()) to cleanup/free and the Services Layer's
 *    kernel statistics.
 */
int
s1394_kstat_delete(s1394_hal_t *hal)
{
        kstat_delete(hal->hal_ksp);
        kmem_free((void *)hal->hal_kstats, sizeof (s1394_kstat_t));

        return (DDI_SUCCESS);
}

/*
 * s1394_kstat_update()
 *    is a callback that is called whenever a request to read the kernel
 *    statistics is made.
 */
int
s1394_kstat_update(kstat_t *ksp, int rw)
{
        s1394_hal_t     *hal;

        hal = ksp->ks_private;

        if (rw == KSTAT_WRITE) {
                return (EACCES);
        } else {
                ksp->ks_data = hal->hal_kstats;
        }

        return (0);
}

/*
 * s1394_addr_alloc_kstat()
 *    is used by the kernel statistics to update the count for each type of
 *    address allocation.
 */
void
s1394_addr_alloc_kstat(s1394_hal_t *hal, uint64_t addr)
{
        /* kstats - number of addr allocs */
        if (s1394_is_posted_write(hal, addr) == B_TRUE)
                hal->hal_kstats->addr_posted_alloc++;
        else if (s1394_is_normal_addr(hal, addr) == B_TRUE)
                hal->hal_kstats->addr_normal_alloc++;
        else if (s1394_is_csr_addr(hal, addr) == B_TRUE)
                hal->hal_kstats->addr_csr_alloc++;
        else if (s1394_is_physical_addr(hal, addr) == B_TRUE)
                hal->hal_kstats->addr_phys_alloc++;
}

/*
 * s1394_print_node_info()
 *    is used to print speed map and GUID information on the console.
 */
void
s1394_print_node_info(s1394_hal_t *hal)
{
        int     i, j;
        uint_t  hal_node_num;
        char    str[200], tmp[200];

        /* These are in common/os/logsubr.c */
        extern void log_enter(void);
        extern void log_exit(void);

        if (s1394_print_guids == 0)
                return;

        hal_node_num = IEEE1394_NODE_NUM(hal->node_id);

        log_enter();

        cmn_err(CE_CONT, "Speed Map (%d):\n",
            ddi_get_instance(hal->halinfo.dip));

        (void) strcpy(str, "    |");
        for (i = 0; i < hal->number_of_nodes; i++) {
            (void) sprintf(tmp, " %2d ", i);
            (void) strcat(str, tmp);
        }
        (void) strcat(str, "  |       GUID\n");
        cmn_err(CE_CONT, str);

        (void) strcpy(str, "----|");
        for (i = 0; i < hal->number_of_nodes; i++) {
            (void) sprintf(tmp, "----");
            (void) strcat(str, tmp);
        }
        (void) strcat(str, "--|------------------\n");
        cmn_err(CE_CONT, str);

        for (i = 0; i < hal->number_of_nodes; i++) {

            (void) sprintf(str, " %2d |", i);

            for (j = 0; j < hal->number_of_nodes; j++) {
                (void) sprintf(tmp, " %3d", hal->speed_map[i][j]);
                (void) strcat(str, tmp);
            }

            if (i == hal_node_num) {

                (void) strcat(str, "  | Local OHCI Card\n");

            } else if (CFGROM_BIB_READ(&hal->topology_tree[i])) {

                (void) sprintf(tmp, "  | %08x%08x\n",
                                    hal->topology_tree[i].node_guid_hi,
                                    hal->topology_tree[i].node_guid_lo);
                (void) strcat(str, tmp);

            } else if (hal->topology_tree[i].link_active == 0) {

                (void) strcat(str, "  | Link off\n");

            } else {

                (void) strcat(str, "  | ????????????????\n");
            }
            cmn_err(CE_CONT, str);
        }
        cmn_err(CE_CONT, "\n");

        log_exit();
}

/*
 * s1394_dip_to_hal()
 *    is used to lookup a HAL's structure pointer by its dip.
 */
s1394_hal_t *
s1394_dip_to_hal(dev_info_t *hal_dip)
{
        s1394_hal_t     *current_hal = NULL;

        mutex_enter(&s1394_statep->hal_list_mutex);

        /* Search the HAL list for this dip */
        current_hal = s1394_statep->hal_head;
        while (current_hal != NULL) {
                if (current_hal->halinfo.dip == hal_dip) {
                        break;
                }
                current_hal = current_hal->hal_next;
        }

        mutex_exit(&s1394_statep->hal_list_mutex);

        return (current_hal);
}

/*
 * s1394_target_from_dip_locked()
 *    searches target_list on the HAL for target corresponding to tdip;
 *    if found, target is returned, else returns NULL. This routine assumes
 *    target_list_rwlock is locked.
 *    NOTE: the callers may have the list locked in either write mode or read
 *    mode. Currently, there is no ddi-compliant way we can assert on the lock
 *    being held in write mode.
 */
s1394_target_t *
s1394_target_from_dip_locked(s1394_hal_t *hal, dev_info_t *tdip)
{
        s1394_target_t  *temp;

        temp = hal->target_head;
        while (temp != NULL) {
            if (temp->target_dip == tdip) {
                return (temp);
            }
            temp = temp->target_next;
        }

        return (NULL);
}
/*
 * s1394_target_from_dip()
 *    searches target_list on the HAL for target corresponding to tdip;
 *    if found, target is returned locked.
 */
s1394_target_t *
s1394_target_from_dip(s1394_hal_t *hal, dev_info_t *tdip)
{
        s1394_target_t  *target;

        rw_enter(&hal->target_list_rwlock, RW_READER);
        target = s1394_target_from_dip_locked(hal, tdip);
        rw_exit(&hal->target_list_rwlock);

        return (target);
}

/*
 * s1394_destroy_timers()
 *    turns off any outstanding timers in preparation for detach or suspend.
 */
void
s1394_destroy_timers(s1394_hal_t *hal)
{
        /* Destroy both of the Bus Mgr timers */
        (void) untimeout(hal->bus_mgr_timeout_id);
        (void) untimeout(hal->bus_mgr_query_timeout_id);

        /* Destroy the Cycle Master timer */
        (void) untimeout(hal->cm_timer);

        /* Wait for the Config ROM timer (if necessary) */
        while (hal->config_rom_timer_set == B_TRUE) {
                delay(drv_usectohz(10));
        }
}


/*
 * s1394_cleanup_node_cfgrom()
 *    frees up all of the Config ROM in use by nodes in the topology_tree
 */
static void
s1394_cleanup_node_cfgrom(s1394_hal_t *hal)
{
        uint32_t *cfgrom;
        int      i;

        for (i = 0; i < IEEE1394_MAX_NODES; i++) {
                if ((cfgrom = hal->topology_tree[i].cfgrom) != NULL)
                        kmem_free(cfgrom, IEEE1394_CONFIG_ROM_SZ);
        }
}

/*
 * s1394_cycle_too_long_callback()
 *    turns on the cycle master bit of the root node (current Cycle Master)
 */
void
s1394_cycle_too_long_callback(void *arg)
{
        s1394_hal_t     *hal;
        ushort_t        root_node_num;
        ushort_t        hal_node_num;
        uint32_t        data;
        uint_t          offset;

        hal = (s1394_hal_t *)arg;

        /* Clear the cm_timer_cet bit */
        mutex_enter(&hal->topology_tree_mutex);
        mutex_enter(&hal->cm_timer_mutex);
        hal->cm_timer_set = B_FALSE;
        mutex_exit(&hal->cm_timer_mutex);

        /* Get the root node and host node numbers */
        root_node_num = hal->number_of_nodes - 1;
        hal_node_num  = IEEE1394_NODE_NUM(hal->node_id);
        mutex_exit(&hal->topology_tree_mutex);

        /* If we are the root node, set the cycle master bit */
        if (hal_node_num == root_node_num) {
                data    = IEEE1394_CSR_STATE_CMSTR;
                offset  = (IEEE1394_CSR_STATE_SET & IEEE1394_CSR_OFFSET_MASK);
                (void) HAL_CALL(hal).csr_write(hal->halinfo.hal_private,
                    offset, data);
        }
}