/*
 * 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 (c) 1999-2000 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma	ident	"%Z%%M%	%I%	%E% SMI"

/*
 * s1394_csr.c
 *    1394 Services Layer CSR and Config ROM Routines
 *    Contains all of the CSR callback routines for various required
 *    CSR registers.  Also contains routines for their initialization
 *    and destruction, as well as routines to handle the processing
 *    of Config ROM update requests.
 */

#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/tnf_probe.h>

#include <sys/1394/t1394.h>
#include <sys/1394/s1394.h>
#include <sys/1394/h1394.h>
#include <sys/1394/ieee1394.h>
#include <sys/1394/ieee1212.h>

static void s1394_CSR_state_clear(cmd1394_cmd_t *req);

static void s1394_CSR_state_set(cmd1394_cmd_t *req);

static void s1394_CSR_node_ids(cmd1394_cmd_t *req);

static void s1394_CSR_reset_start(cmd1394_cmd_t *req);

static void s1394_CSR_split_timeout(cmd1394_cmd_t *req);

static void s1394_CSR_argument_regs(cmd1394_cmd_t *req);

static void s1394_CSR_test_regs(cmd1394_cmd_t *req);

static void s1394_CSR_interrupt_regs(cmd1394_cmd_t *req);

static void s1394_CSR_clock_regs(cmd1394_cmd_t *req);

static void s1394_CSR_message_regs(cmd1394_cmd_t *req);

static void s1394_CSR_cycle_time(cmd1394_cmd_t *req);

static void s1394_CSR_bus_time(cmd1394_cmd_t *req);

static void s1394_CSR_busy_timeout(cmd1394_cmd_t *req);

static void s1394_CSR_IRM_regs(cmd1394_cmd_t *req);

static void s1394_CSR_topology_map(cmd1394_cmd_t *req);

static void s1394_common_CSR_routine(s1394_hal_t *hal, cmd1394_cmd_t *req);

static int s1394_init_config_rom_structures(s1394_hal_t *hal);

static int s1394_destroy_config_rom_structures(s1394_hal_t *hal);

/*
 * s1394_setup_CSR_space()
 *    setups up the local host's CSR registers and callback routines.
 */
int
s1394_setup_CSR_space(s1394_hal_t *hal)
{
	s1394_addr_space_blk_t	*curr_blk;
	t1394_alloc_addr_t	addr;
	t1394_addr_enable_t	rw_flags;
	int			result;

	TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_enter, S1394_TNF_SL_CSR_STACK,
	    "");

	/*
	 * Although they are not freed up in this routine, if
	 * one of the s1394_claim_addr_blk() routines fails,
	 * all of the previously successful claims will be
	 * freed up in s1394_destroy_addr_space() upon returning
	 * DDI_FAILURE from this routine.
	 */

	rw_flags = T1394_ADDR_RDENBL | T1394_ADDR_WRENBL;

	/*
	 * STATE_CLEAR
	 *    see IEEE 1394-1995, Section 8.3.2.2.1 or
	 *    IEEE 1212-1994, Section 7.4.1
	 */
	addr.aa_address	= IEEE1394_CSR_STATE_CLEAR;
	addr.aa_length	= IEEE1394_QUADLET;
	addr.aa_enable	= rw_flags;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_state_clear;
	addr.aa_evts.recv_write_request	= s1394_CSR_state_clear;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "STATE_CLEAR: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * STATE_SET
	 *    see IEEE 1394-1995, Section 8.3.2.2.2 or
	 *    IEEE 1212-1994, Section 7.4.2
	 */
	addr.aa_address	= IEEE1394_CSR_STATE_SET;
	addr.aa_length	= IEEE1394_QUADLET;
	addr.aa_enable	= T1394_ADDR_WRENBL;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= NULL;
	addr.aa_evts.recv_write_request	= s1394_CSR_state_set;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "STATE_SET: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * NODE_IDS
	 *    see IEEE 1394-1995, Section 8.3.2.2.3 or
	 *    IEEE 1212-1994, Section 7.4.3
	 */
	addr.aa_address	= IEEE1394_CSR_NODE_IDS;
	addr.aa_length	= IEEE1394_QUADLET;
	addr.aa_enable	= rw_flags;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_node_ids;
	addr.aa_evts.recv_write_request = s1394_CSR_node_ids;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "NODE_IDS: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * RESET_START
	 *    see IEEE 1394-1995, Section 8.3.2.2.4 or
	 *    IEEE 1212-1994, Section 7.4.4
	 */
	addr.aa_address	= IEEE1394_CSR_RESET_START;
	addr.aa_length	= IEEE1394_QUADLET;
	addr.aa_enable	= T1394_ADDR_WRENBL;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= NULL;
	addr.aa_evts.recv_write_request	= s1394_CSR_reset_start;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "RESET_START: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * SPLIT_TIMEOUT
	 *    see IEEE 1394-1995, Section 8.3.2.2.6 or
	 *    IEEE 1212-1994, Section 7.4.7
	 */
	addr.aa_address	= IEEE1394_CSR_SPLIT_TIMEOUT_HI;
	addr.aa_length	= IEEE1394_OCTLET;
	addr.aa_enable	= rw_flags;
	addr.aa_type = T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_split_timeout;
	addr.aa_evts.recv_write_request	= s1394_CSR_split_timeout;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "SPLIT_TIMEOUT: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * ARGUMENT_HI and ARGUMENT_LO
	 *    see IEEE 1394-1995, Section 8.3.2.2.7 or
	 *    IEEE 1212-1994, Section 7.4.8
	 */
	addr.aa_address	= IEEE1394_CSR_ARG_HI;
	addr.aa_length	= 2 * (IEEE1394_QUADLET);
	addr.aa_enable	= rw_flags;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_argument_regs;
	addr.aa_evts.recv_write_request	= s1394_CSR_argument_regs;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "ARGUMENT registers: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * TEST_START and TEST_STATUS
	 *    see IEEE 1394-1995, Section 8.3.2.2.7 or
	 *    IEEE 1212-1994, Section 7.4.9 - 7.4.10
	 */
	addr.aa_address	= IEEE1394_CSR_TEST_START;
	addr.aa_length	= 2 * (IEEE1394_QUADLET);
	addr.aa_enable	= rw_flags;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_test_regs;
	addr.aa_evts.recv_write_request	= s1394_CSR_test_regs;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "TEST registers: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * INTERRUPT_TARGET and INTERRUPT_MASK
	 *    see IEEE 1394-1995, Section 8.3.2.2.9 or
	 *    IEEE 1212-1994, Section 7.4.15 - 7.4.16
	 */
	addr.aa_address	= IEEE1394_CSR_INTERRUPT_TARGET;
	addr.aa_length	= 2 * (IEEE1394_QUADLET);
	addr.aa_enable	= rw_flags;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_interrupt_regs;
	addr.aa_evts.recv_write_request	= s1394_CSR_interrupt_regs;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "INTERRUPT registers: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * CLOCK_VALUE, CLOCK_TICK_PERIOD, CLOCK_INFO, etc.
	 *    see IEEE 1394-1995, Section 8.3.2.2.10 or
	 *    IEEE 1212-1994, Section 7.4.17 - 7.4.20
	 */
	addr.aa_address	= IEEE1394_CSR_CLOCK_VALUE;
	addr.aa_length	= IEEE1394_CSR_CLOCK_VALUE_SZ;
	addr.aa_enable	= rw_flags;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_clock_regs;
	addr.aa_evts.recv_write_request	= s1394_CSR_clock_regs;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "CLOCK registers: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * MESSAGE_REQUEST and MESSAGE_RESPONSE
	 *    see IEEE 1394-1995, Section 8.3.2.2.11 or
	 *    IEEE 1212-1994, Section 7.4.21
	 */
	addr.aa_address	= IEEE1394_CSR_MESSAGE_REQUEST;
	addr.aa_length	= IEEE1394_CSR_MESSAGE_REQUEST_SZ;
	addr.aa_enable	= rw_flags;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_message_regs;
	addr.aa_evts.recv_write_request	= s1394_CSR_message_regs;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "MESSAGE registers: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * CYCLE_TIME
	 *    see IEEE 1394-1995, Section 8.3.2.3.1
	 */
	addr.aa_address	= IEEE1394_SCSR_CYCLE_TIME;
	addr.aa_length	= IEEE1394_QUADLET;
	addr.aa_enable	= rw_flags;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_cycle_time;
	addr.aa_evts.recv_write_request	= s1394_CSR_cycle_time;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "CYCLE_TIME: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * BUS_TIME
	 *    see IEEE 1394-1995, Section 8.3.2.3.2
	 */
	addr.aa_address = IEEE1394_SCSR_BUS_TIME;
	addr.aa_length	= IEEE1394_QUADLET;
	addr.aa_enable	= rw_flags;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_bus_time;
	addr.aa_evts.recv_write_request	= s1394_CSR_bus_time;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "BUS_TIME: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * BUSY_TIMEOUT
	 *    see IEEE 1394-1995, Section 8.3.2.3.5
	 */
	addr.aa_address	= IEEE1394_SCSR_BUSY_TIMEOUT;
	addr.aa_length	= IEEE1394_QUADLET;
	addr.aa_enable	= rw_flags;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_busy_timeout;
	addr.aa_evts.recv_write_request	= s1394_CSR_busy_timeout;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "BUSY_TIMEOUT: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * BUS_MANAGER_ID
	 * BANDWIDTH_AVAILABLE
	 * CHANNELS_AVAILABLE
	 *    see IEEE 1394-1995, Section 8.3.2.3.6 - 8.3.2.3.8
	 */
	addr.aa_address	= IEEE1394_SCSR_BUSMGR_ID;
	addr.aa_length	= 3 * (IEEE1394_QUADLET);
	addr.aa_enable	= T1394_ADDR_RDENBL | T1394_ADDR_LKENBL;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_IRM_regs;
	addr.aa_evts.recv_write_request	= NULL;
	addr.aa_evts.recv_lock_request	= s1394_CSR_IRM_regs;
	addr.aa_kmem_bufp = NULL;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "IRM registers: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * Reserved for Configuration ROM
	 *    see IEEE 1394-1995, Section 8.3.2.5.3
	 */
	addr.aa_address	= IEEE1394_CONFIG_ROM_ADDR;
	addr.aa_length	= IEEE1394_CONFIG_ROM_SZ;
	result = s1394_reserve_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "Unable to reserve Config ROM");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * TOPOLOGY_MAP
	 *    see IEEE 1394-1995, Section 8.3.2.4.1
	 */
	hal->CSR_topology_map = kmem_zalloc(IEEE1394_UCSR_TOPOLOGY_MAP_SZ,
	    KM_SLEEP);
	addr.aa_address	= IEEE1394_UCSR_TOPOLOGY_MAP;
	addr.aa_length	= IEEE1394_UCSR_TOPOLOGY_MAP_SZ;
	addr.aa_enable	= T1394_ADDR_RDENBL;
	addr.aa_type	= T1394_ADDR_FIXED;
	addr.aa_evts.recv_read_request	= s1394_CSR_topology_map;
	addr.aa_evts.recv_write_request	= NULL;
	addr.aa_evts.recv_lock_request	= NULL;
	addr.aa_kmem_bufp = (caddr_t)hal->CSR_topology_map;
	addr.aa_arg	  = hal;
	result = s1394_claim_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		kmem_free((void *)hal->CSR_topology_map,
		    IEEE1394_UCSR_TOPOLOGY_MAP_SZ);
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "TOPOLOGY_MAP: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}
	curr_blk = (s1394_addr_space_blk_t *)(addr.aa_hdl);
	/* Set up the block so that we free kmem_bufp at detach */
	curr_blk->free_kmem_bufp = B_TRUE;

	/*
	 * Reserve the SPEED_MAP
	 *    see IEEE 1394-1995, Section 8.3.2.4.1
	 *    (obsoleted in P1394A)
	 */
	addr.aa_address	= IEEE1394_UCSR_SPEED_MAP;
	addr.aa_length	= IEEE1394_UCSR_SPEED_MAP_SZ;
	result = s1394_reserve_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "SPEED_MAP: CSR setup failed");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return (DDI_FAILURE);
	}

	/*
	 * Reserved - Boundary between reserved Serial Bus
	 * dependent registers and other CSR register space.
	 * See IEEE 1394-1995, Table 8-4 for this address.
	 *
	 * This quadlet is reserved as a way of preventing
	 * the inadvertant allocation of a part of CSR space
	 * that will likely be used by future specifications
	 */
	addr.aa_address	= IEEE1394_UCSR_RESERVED_BOUNDARY;
	addr.aa_length	= IEEE1394_QUADLET;
	result = s1394_reserve_addr_blk(hal, &addr);
	if (result != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_setup_CSR_space_error,
		    S1394_TNF_SL_CSR_ERROR, "", tnf_string, msg,
		    "Unable to reserve boundary quadlet");
		TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit,
		    "stacktrace 1394 s1394", "");
		return (DDI_FAILURE);
	}

	TNF_PROBE_0_DEBUG(s1394_setup_CSR_space_exit, S1394_TNF_SL_CSR_STACK,
	    "");
	return (DDI_SUCCESS);
}

/*
 * s1394_CSR_state_clear()
 *    handles all requests to the STATE_CLEAR CSR register.  It enforces
 *    that certain bits that can be twiddled only by a given node (IRM or
 *    Bus Manager).
 */
static void
s1394_CSR_state_clear(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;
	uint32_t	data;
	uint_t		offset;
	uint_t		is_from;
	uint_t		should_be_from;
	int		result;

	TNF_PROBE_0_DEBUG(s1394_CSR_state_clear_enter, S1394_TNF_SL_CSR_STACK,
	    "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	/* Register offset */
	offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK;

	/* Verify that request is quadlet aligned */
	if ((offset & 0x3) != 0) {
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
		(void) s1394_send_response(hal, req);
		TNF_PROBE_0_DEBUG(s1394_CSR_state_clear_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return;
	}

	/* Only writes from IRM or Bus Mgr allowed (in some cases) */
	mutex_enter(&hal->topology_tree_mutex);
	is_from = IEEE1394_NODE_NUM(req->nodeID);
	if (hal->bus_mgr_node != -1)
		should_be_from = IEEE1394_NODE_NUM(hal->bus_mgr_node);
	else if (hal->IRM_node != -1)
		should_be_from = IEEE1394_NODE_NUM(hal->IRM_node);
	else
		should_be_from = S1394_INVALID_NODE_NUM;
	mutex_exit(&hal->topology_tree_mutex);

	switch (req->cmd_type) {
	case CMD1394_ASYNCH_RD_QUAD:
		/*
		 * The csr_read() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented. But although the STATE_CLEAR register
		 * is required to be implemented and readable, we will
		 * return IEEE1394_RESP_ADDRESS_ERROR in the response if
		 * we ever see this error.
		 */
		result = HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
		    offset, &data);
		if (result == DDI_SUCCESS) {
			req->cmd_u.q.quadlet_data = data;
			req->cmd_result = IEEE1394_RESP_COMPLETE;
		} else {
			req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
		}
		break;

	case CMD1394_ASYNCH_WR_QUAD:
		data = req->cmd_u.q.quadlet_data;

		/* CMSTR bit - request must be from bus_mgr/IRM */
		if (is_from != should_be_from) {
			data = data & ~IEEE1394_CSR_STATE_CMSTR;
		}

		mutex_enter(&hal->topology_tree_mutex);
		/* DREQ bit - disabling DREQ can come from anyone */
		if (data & IEEE1394_CSR_STATE_DREQ) {
			hal->disable_requests_bit = 0;
			if (hal->hal_state == S1394_HAL_DREQ)
				hal->hal_state = S1394_HAL_NORMAL;
		}

		/* ABDICATE bit */
		if (data & IEEE1394_CSR_STATE_ABDICATE) {
			hal->abdicate_bus_mgr_bit = 0;
		}
		mutex_exit(&hal->topology_tree_mutex);
		/*
		 * The csr_write() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented. But although the STATE_CLEAR register
		 * is required to be implemented and writeable, we will
		 * return IEEE1394_RESP_ADDRESS_ERROR in the response if
		 * we ever see this error.
		 */
		result = HAL_CALL(hal).csr_write(hal->halinfo.hal_private,
		    offset, data);
		if (result == DDI_SUCCESS) {
			req->cmd_result = IEEE1394_RESP_COMPLETE;
		} else {
			req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
		}
		break;

	default:
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
	}

	(void) s1394_send_response(hal, req);
	TNF_PROBE_0_DEBUG(s1394_CSR_state_clear_exit, S1394_TNF_SL_CSR_STACK,
	    "");
}

/*
 * s1394_CSR_state_set()
 *    handles all requests to the STATE_SET CSR register. It enforces that
 *    certain bits that can be twiddled only by a given node (IRM or Bus
 *    Manager).
 */
static void
s1394_CSR_state_set(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;
	uint32_t	data;
	uint_t		offset;
	uint_t		is_from;
	uint_t		should_be_from;
	uint_t		hal_node_num;
	uint_t		hal_number_of_nodes;
	int		result;

	TNF_PROBE_0_DEBUG(s1394_CSR_state_set_enter, S1394_TNF_SL_CSR_STACK,
	    "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	/* Register offset */
	offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK;

	/* Verify that request is quadlet aligned */
	if ((offset & 0x3) != 0) {
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
		(void) s1394_send_response(hal, req);
		TNF_PROBE_0_DEBUG(s1394_CSR_state_set_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return;
	}

	/* Only writes from IRM or Bus Mgr allowed (in some cases) */
	mutex_enter(&hal->topology_tree_mutex);
	is_from = IEEE1394_NODE_NUM(req->nodeID);
	if (hal->bus_mgr_node != -1)
		should_be_from = IEEE1394_NODE_NUM(hal->bus_mgr_node);
	else if (hal->IRM_node != -1)
		should_be_from = IEEE1394_NODE_NUM(hal->IRM_node);
	else
		should_be_from = S1394_INVALID_NODE_NUM;
	hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
	hal_number_of_nodes = hal->number_of_nodes;
	mutex_exit(&hal->topology_tree_mutex);

	switch (req->cmd_type) {
	case CMD1394_ASYNCH_WR_QUAD:
		data = req->cmd_u.q.quadlet_data;

		/* CMSTR bit - request must be from bus_mgr/IRM */
		/*		& must be root to have bit set */
		if ((is_from != should_be_from) ||
		    (hal_node_num != (hal_number_of_nodes - 1))) {
			data = data & ~IEEE1394_CSR_STATE_CMSTR;
		}

		mutex_enter(&hal->topology_tree_mutex);
		/* DREQ bit - only bus_mgr/IRM can set this bit */
		if (is_from != should_be_from) {
			data = data & ~IEEE1394_CSR_STATE_DREQ;

		} else if (data & IEEE1394_CSR_STATE_DREQ) {
			hal->disable_requests_bit = 1;
			if (hal->hal_state == S1394_HAL_NORMAL)
				hal->hal_state = S1394_HAL_DREQ;
		}
		/* ABDICATE bit */
		if (data & IEEE1394_CSR_STATE_ABDICATE) {
			hal->abdicate_bus_mgr_bit = 1;
		}
		mutex_exit(&hal->topology_tree_mutex);
		/*
		 * The csr_write() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented. But although the STATE_SET register
		 * is required to be implemented and writeable, we will
		 * return IEEE1394_RESP_ADDRESS_ERROR in the response if
		 * we ever see this error.
		 */
		result = HAL_CALL(hal).csr_write(hal->halinfo.hal_private,
		    offset, data);
		if (result == DDI_SUCCESS) {
			req->cmd_result = IEEE1394_RESP_COMPLETE;
		} else {
			req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
		}
		break;

	default:
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
	}

	(void) s1394_send_response(hal, req);
	TNF_PROBE_0_DEBUG(s1394_CSR_state_set_exit, S1394_TNF_SL_CSR_STACK,
	    "");
}

/*
 * s1394_CSR_node_ids()
 *    handles all requests to the NODE_IDS CSR register.  It passes all
 *    requests to the common routine - s1394_common_CSR_routine().
 */
static void
s1394_CSR_node_ids(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;

	TNF_PROBE_0_DEBUG(s1394_CSR_node_ids_enter, S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	s1394_common_CSR_routine(hal, req);

	TNF_PROBE_0_DEBUG(s1394_CSR_node_ids_exit, S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_reset_start()
 *    handles all requests to the RESET_START CSR register. Only write
 *    requests are legal, everything else gets a type_error response.
 */
static void
s1394_CSR_reset_start(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;
	uint32_t	data;
	uint_t		offset;

	TNF_PROBE_0_DEBUG(s1394_CSR_reset_start_enter, S1394_TNF_SL_CSR_STACK,
	    "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	/* RESET_START register offset */
	offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK;

	/* Verify that request is quadlet aligned */
	if ((offset & 0x3) != 0) {
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
		(void) s1394_send_response(hal, req);
		TNF_PROBE_0_DEBUG(s1394_CSR_reset_start_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return;
	}

	switch (req->cmd_type) {
	case CMD1394_ASYNCH_WR_QUAD:
		data = req->cmd_u.q.quadlet_data;
		/*
		 * The csr_write() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented. Because we don't do any thing with
		 * the RESET_START register we will ignore failures and
		 * return IEEE1394_RESP_COMPLETE regardless.
		 */
		(void) HAL_CALL(hal).csr_write(hal->halinfo.hal_private,
		    offset, data);
		req->cmd_result = IEEE1394_RESP_COMPLETE;
		break;

	default:
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
	}

	(void) s1394_send_response(hal, req);
	TNF_PROBE_0_DEBUG(s1394_CSR_reset_start_exit, S1394_TNF_SL_CSR_STACK,
	    "");
}

/*
 * s1394_CSR_split_timeout()
 *    handles all requests to the SPLIT_TIMEOUT CSR register.  It passes all
 *    requests to the common routine - s1394_common_CSR_routine().
 */
static void
s1394_CSR_split_timeout(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;

	TNF_PROBE_0_DEBUG(s1394_CSR_split_timeout_enter,
	    S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	s1394_common_CSR_routine(hal, req);

	TNF_PROBE_0_DEBUG(s1394_CSR_split_timeout_exit,
	    S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_argument_regs()
 *    handles all requests to the ARGUMENT CSR registers.  It passes all
 *    requests to the common routine - s1394_common_CSR_routine().
 */
static void
s1394_CSR_argument_regs(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;

	TNF_PROBE_0_DEBUG(s1394_CSR_argument_regs_enter,
	    S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	s1394_common_CSR_routine(hal, req);

	TNF_PROBE_0_DEBUG(s1394_CSR_argument_regs_exit,
	    S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_test_regs()
 *    handles all requests to the TEST CSR registers. It passes all requests
 *    to the common routine - s1394_common_CSR_routine().
 */
static void
s1394_CSR_test_regs(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;
	uint_t		offset;

	TNF_PROBE_0_DEBUG(s1394_CSR_test_regs_enter,
	    S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	/* TEST register offset */
	offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK;

	/* TEST_STATUS is Read-Only */
	if ((offset == (IEEE1394_CSR_TEST_STATUS & IEEE1394_CSR_OFFSET_MASK)) &&
	    (req->cmd_type == CMD1394_ASYNCH_WR_QUAD)) {
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
		(void) s1394_send_response(hal, req);
	} else {
		s1394_common_CSR_routine(hal, req);
	}

	TNF_PROBE_0_DEBUG(s1394_CSR_test_regs_exit,
	    S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_interrupt_regs()
 *    handles all requests to the INTERRUPT CSR registers.  It passes all
 *    requests to the common routine - s1394_common_CSR_routine().
 */
static void
s1394_CSR_interrupt_regs(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;

	TNF_PROBE_0_DEBUG(s1394_CSR_interrupt_regs_enter,
	    S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	s1394_common_CSR_routine(hal, req);

	TNF_PROBE_0_DEBUG(s1394_CSR_interrupt_regs_exit,
	    S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_clock_regs()
 *    handles all requests to the CLOCK CSR registers.  It passes all
 *    requests to the common routine - s1394_common_CSR_routine().
 */
static void
s1394_CSR_clock_regs(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;

	TNF_PROBE_0_DEBUG(s1394_CSR_clock_regs_enter,
	    S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	s1394_common_CSR_routine(hal, req);

	TNF_PROBE_0_DEBUG(s1394_CSR_clock_regs_exit,
	    S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_message_regs()
 *    handles all requests to the MESSAGE CSR registers.  It passes all
 *    requests to the common routine - s1394_common_CSR_routine().
 */
static void
s1394_CSR_message_regs(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;

	TNF_PROBE_0_DEBUG(s1394_CSR_message_regs_enter,
	    S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	s1394_common_CSR_routine(hal, req);

	TNF_PROBE_0_DEBUG(s1394_CSR_message_regs_exit,
	    S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_cycle_time()
 *    handles all requests to the CYCLE_TIME CSR register.
 */
static void
s1394_CSR_cycle_time(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;
	uint32_t	data;
	uint_t		offset;
	int		result;

	TNF_PROBE_0_DEBUG(s1394_CSR_cycle_time_enter,
	    S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	/* CYCLE_TIME register offset */
	offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK;

	/* Verify that request is quadlet aligned */
	if ((offset & 0x3) != 0) {
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
		(void) s1394_send_response(hal, req);
		TNF_PROBE_0_DEBUG(s1394_CSR_cycle_time_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return;
	}

	switch (req->cmd_type) {
	case CMD1394_ASYNCH_RD_QUAD:
		/*
		 * The csr_read() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented. But although the CYCLE_TIME register
		 * is required to be implemented on devices capable of
		 * providing isochronous services (like us), we will
		 * return IEEE1394_RESP_ADDRESS_ERROR in the response
		 * if we ever see this error.
		 */
		result = HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
		    offset, &data);
		if (result == DDI_SUCCESS) {
			req->cmd_u.q.quadlet_data = data;
			req->cmd_result = IEEE1394_RESP_COMPLETE;
		} else {
			req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
		}
		break;

	case CMD1394_ASYNCH_WR_QUAD:
		data = req->cmd_u.q.quadlet_data;
		/*
		 * The csr_write() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented. But although the CYCLE_TIME register
		 * is required to be implemented on devices capable of
		 * providing isochronous services (like us), the effects
		 * of a write are "node-dependent" so we will return
		 * IEEE1394_RESP_ADDRESS_ERROR in the response if we
		 * ever see this error.
		 */
		result = HAL_CALL(hal).csr_write(hal->halinfo.hal_private,
		    offset, data);
		if (result == DDI_SUCCESS) {
			req->cmd_result = IEEE1394_RESP_COMPLETE;
		} else {
			req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
		}
		break;

	default:
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
	}

	(void) s1394_send_response(hal, req);
	TNF_PROBE_0_DEBUG(s1394_CSR_cycle_time_exit,
	    S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_bus_time()
 *    handles all requests to the BUS_TIME CSR register.  It enforces that
 *    only a broadcast write request from the IRM or Bus Manager can change
 *    its value.
 */
static void
s1394_CSR_bus_time(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;
	uint32_t	data;
	uint_t		offset;
	uint_t		is_from;
	uint_t		should_be_from;
	int		result;

	TNF_PROBE_0_DEBUG(s1394_CSR_bus_time_enter, S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	/* BUS_TIME register offset */
	offset = req->cmd_addr & IEEE1394_CSR_OFFSET_MASK;

	/* Verify that request is quadlet aligned */
	if ((offset & 0x3) != 0) {
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
		(void) s1394_send_response(hal, req);
		TNF_PROBE_0_DEBUG(s1394_CSR_bus_time_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return;
	}

	switch (req->cmd_type) {
	case CMD1394_ASYNCH_RD_QUAD:
		/*
		 * The csr_read() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented. But although the BUS_TIME register
		 * is required to be implemented by devices capable of
		 * being cycle master (like us), we will return
		 * IEEE1394_RESP_ADDRESS_ERROR in the response if we
		 * ever see this error.
		 */
		result = HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
		    offset, &data);
		if (result == DDI_SUCCESS) {
			req->cmd_u.q.quadlet_data = data;
			req->cmd_result = IEEE1394_RESP_COMPLETE;
		} else {
			req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
		}
		break;

	case CMD1394_ASYNCH_WR_QUAD:
		/* Only broadcast writes from IRM or Bus Mgr allowed */
		mutex_enter(&hal->topology_tree_mutex);
		is_from = IEEE1394_NODE_NUM(req->nodeID);
		if (hal->bus_mgr_node != -1)
			should_be_from = IEEE1394_NODE_NUM(hal->bus_mgr_node);
		else if (hal->IRM_node != -1)
			should_be_from = IEEE1394_NODE_NUM(hal->IRM_node);
		else
			should_be_from = S1394_INVALID_NODE_NUM;
		mutex_exit(&hal->topology_tree_mutex);

		if ((req->broadcast != 1) || (is_from != should_be_from)) {
			req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
			break;
		}

		data = req->cmd_u.q.quadlet_data;
		/*
		 * The csr_write() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented. But although the BUS_TIME register
		 * is required to be implemented on devices capable of
		 * being cycle master (like us), we will return
		 * IEEE1394_RESP_ADDRESS_ERROR in the response if we
		 * ever see this error.
		 */
		result = HAL_CALL(hal).csr_write(hal->halinfo.hal_private,
		    offset, data);
		if (result == DDI_SUCCESS) {
			req->cmd_result = IEEE1394_RESP_COMPLETE;
		} else {
			req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
		}
		break;

	default:
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
	}

	(void) s1394_send_response(hal, req);
	TNF_PROBE_0_DEBUG(s1394_CSR_bus_time_exit, S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_busy_timeout()
 *    handles all requests to the BUSY_TIMEOUT CSR register.  It passes all
 *    requests to the common routine - s1394_common_CSR_routine().
 */
static void
s1394_CSR_busy_timeout(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;

	TNF_PROBE_0_DEBUG(s1394_CSR_busy_timeout_enter,
	    S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	s1394_common_CSR_routine(hal, req);

	TNF_PROBE_0_DEBUG(s1394_CSR_busy_timeout_exit,
	    S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_IRM_regs()
 *    handles all requests to the IRM registers, including BANDWIDTH_AVAILABLE,
 *    CHANNELS_AVAILABLE, and the BUS_MANAGER_ID.  Only quadlet read and lock
 *    requests are allowed.
 */
static void
s1394_CSR_IRM_regs(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;
	uint32_t	generation;
	uint32_t	data;
	uint32_t	compare;
	uint32_t	swap;
	uint32_t	old;
	uint_t		offset;
	int		result;

	TNF_PROBE_0_DEBUG(s1394_CSR_IRM_regs_enter, S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	/* IRM register offset */
	offset = (req->cmd_addr & IEEE1394_CSR_OFFSET_MASK);

	/* Verify that request is quadlet aligned */
	if ((offset & 0x3) != 0) {
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
		(void) s1394_send_response(hal, req);
		TNF_PROBE_0_DEBUG(s1394_CSR_IRM_regs_exit,
		    S1394_TNF_SL_CSR_STACK, "");
		return;
	}

	switch (req->cmd_type) {
	case CMD1394_ASYNCH_RD_QUAD:
		/*
		 * The csr_read() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented.  In many cases these registers will
		 * have been implemented in HW.  We are not likely to ever
		 * receive this callback.  If we do, though, we will
		 * return IEEE1394_RESP_ADDRESS_ERROR when we get an error
		 * and IEEE1394_RESP_COMPLETE for success.
		 */
		result = HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
		    offset, &data);
		if (result == DDI_SUCCESS) {
			req->cmd_u.q.quadlet_data = data;
			req->cmd_result = IEEE1394_RESP_COMPLETE;
		} else {
			req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
		}
		break;

	case CMD1394_ASYNCH_LOCK_32:
		mutex_enter(&hal->topology_tree_mutex);
		generation = hal->generation_count;
		mutex_exit(&hal->topology_tree_mutex);
		if (req->cmd_u.l32.lock_type == CMD1394_LOCK_COMPARE_SWAP) {
			compare = req->cmd_u.l32.arg_value;
			swap = req->cmd_u.l32.data_value;
			/*
			 * The csr_cswap32() call can return DDI_FAILURE if
			 * the HAL is shutdown, if the register at "offset"
			 * is unimplemented, or if the generation has changed.
			 * In the last case, it shouldn't matter because the
			 * call to s1394_send_response will fail on a bad
			 * generation and the command will be freed.
			 */
			result = HAL_CALL(hal).csr_cswap32(
			    hal->halinfo.hal_private, generation,
			    offset, compare, swap, &old);
			if (result == DDI_SUCCESS) {
				req->cmd_u.l32.old_value = old;
				req->cmd_result = IEEE1394_RESP_COMPLETE;
			} else {
				req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
			}
			break;
		} else {
			req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
		}

		break;

	default:
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
	}

	(void) s1394_send_response(hal, req);
	TNF_PROBE_0_DEBUG(s1394_CSR_IRM_regs_exit, S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_topology_map()
 *    handles all request for the TOPOLOGY_MAP[].  Since it is implemented
 *    with backing store, there isn't much to do besides return success or
 *    failure.
 */
static void
s1394_CSR_topology_map(cmd1394_cmd_t *req)
{
	s1394_hal_t	*hal;

	TNF_PROBE_0_DEBUG(s1394_CSR_topology_map_enter,
	    S1394_TNF_SL_CSR_STACK, "");

	hal = (s1394_hal_t *)req->cmd_callback_arg;

	/* Make sure it's a quadlet read request */
	if (req->cmd_type == CMD1394_ASYNCH_RD_QUAD)
		req->cmd_result = IEEE1394_RESP_COMPLETE;
	else
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;

	(void) s1394_send_response(hal, req);

	TNF_PROBE_0_DEBUG(s1394_CSR_topology_map_exit,
	    S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_CSR_topology_map_update()
 *    is used to update the local host's TOPOLOGY_MAP[] buffer.  It copies in
 *    the SelfID packets, updates the generation and other fields, and
 *    computes the necessary CRC values before returning.
 *    Callers must be holding the topology_tree_mutex.
 */
void
s1394_CSR_topology_map_update(s1394_hal_t *hal)
{
	s1394_selfid_pkt_t *selfid_packet;
	uint32_t	   *tm_ptr;
	uint32_t	   *data_ptr;
	uint32_t	   node_count;
	uint32_t	   self_id_count;
	uint_t		   CRC;
	uint32_t	   length;
	int		   i, j, c;

	TNF_PROBE_0_DEBUG(s1394_CSR_topology_map_update_enter,
	    S1394_TNF_SL_BR_CSR_STACK, "");

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

	tm_ptr = (uint32_t *)hal->CSR_topology_map;
	data_ptr = (uint32_t *)&(tm_ptr[3]);

	c = 0;
	for (i = 0; i < hal->number_of_nodes; i++) {
		j = -1;
		selfid_packet = hal->selfid_ptrs[i];

		do {
			j++;
			data_ptr[c++] = selfid_packet[j].spkt_data;
		}
		while (IEEE1394_SELFID_ISMORE(&selfid_packet[j]));
	}

	/* Update Topology Map Generation */
	tm_ptr[1] = tm_ptr[1] + 1;

	/* Update Node_Count and Self_Id_Count */
	node_count = (i & IEEE1394_TOP_MAP_LEN_MASK);
	self_id_count = (c & IEEE1394_TOP_MAP_LEN_MASK);
	tm_ptr[2] = (node_count << IEEE1394_TOP_MAP_LEN_SHIFT) |
	    (self_id_count);

	/* Calculate CRC-16 */
	length = self_id_count + 2;
	CRC = s1394_CRC16(&(tm_ptr[1]), length);
	tm_ptr[0] = (length << IEEE1394_TOP_MAP_LEN_SHIFT) | CRC;

	TNF_PROBE_0_DEBUG(s1394_CSR_topology_map_update_exit,
	    S1394_TNF_SL_BR_CSR_STACK, "");
}

/*
 * s1394_CSR_topology_map_disable()
 *    is used to disable the local host's TOPOLOGY_MAP[] buffer (during bus
 *    reset processing).  It sets the topology map's length to zero to
 *    indicate that it is invalid.
 */
void
s1394_CSR_topology_map_disable(s1394_hal_t *hal)
{
	uint32_t *tm_ptr;

	TNF_PROBE_0_DEBUG(s1394_CSR_topology_map_disable_enter,
	    S1394_TNF_SL_BR_CSR_STACK, "");

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

	tm_ptr = (uint32_t *)hal->CSR_topology_map;

	/* Set length = 0 */
	tm_ptr[0] = tm_ptr[0] & IEEE1394_TOP_MAP_LEN_MASK;

	TNF_PROBE_0_DEBUG(s1394_CSR_topology_map_disable_exit,
	    S1394_TNF_SL_BR_CSR_STACK, "");
}

/*
 * s1394_common_CSR_routine()
 *    is used to handle most of the CSR register requests.  They are passed
 *    to the appropriate HAL entry point for further processing.  Then they
 *    are filled in with an appropriate response code, and the response is sent.
 */
static void
s1394_common_CSR_routine(s1394_hal_t *hal, cmd1394_cmd_t *req)
{
	uint32_t data;
	uint_t	 offset;
	int	 result;

	TNF_PROBE_0_DEBUG(s1394_common_CSR_routine_enter,
	    S1394_TNF_SL_CSR_STACK, "");

	/* Register offset */
	offset = (req->cmd_addr & IEEE1394_CSR_OFFSET_MASK);

	/* Verify that request is quadlet aligned */
	if ((offset & 0x3) != 0) {
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
		(void) s1394_send_response(hal, req);
	}

	switch (req->cmd_type) {
	case CMD1394_ASYNCH_RD_QUAD:
		/*
		 * The csr_read() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented.  We will return IEEE1394_RESP_ADDRESS_ERROR
		 * in the response if we see this error.
		 */
		result = HAL_CALL(hal).csr_read(hal->halinfo.hal_private,
		    offset, &data);
		if (result == DDI_SUCCESS) {
			req->cmd_u.q.quadlet_data = data;
			req->cmd_result = IEEE1394_RESP_COMPLETE;
		} else {
			req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
		}
		break;

	case CMD1394_ASYNCH_WR_QUAD:
		data = req->cmd_u.q.quadlet_data;
		/*
		 * The csr_read() call can return DDI_FAILURE if the HAL
		 * is shutdown or if the register at "offset" is
		 * unimplemented.  We will return IEEE1394_RESP_ADDRESS_ERROR
		 * in the response if we see this error.
		 */
		result = HAL_CALL(hal).csr_write(hal->halinfo.hal_private,
		    offset, data);
		if (result == DDI_SUCCESS) {
			req->cmd_result = IEEE1394_RESP_COMPLETE;
		} else {
			req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
		}
		break;

	default:
		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
	}

	(void) s1394_send_response(hal, req);
	TNF_PROBE_0_DEBUG(s1394_common_CSR_routine_exit,
	    S1394_TNF_SL_CSR_STACK, "");
}

/*
 * s1394_init_local_config_rom()
 *    is called in the HAL attach routine - h1394_attach() - to setup the
 *    initial Config ROM entries on the local host, including the
 *    bus_info_block and the root and unit directories.
 */
int
s1394_init_local_config_rom(s1394_hal_t *hal)
{
	uint32_t *config_rom;
	uint32_t *node_unique_id_leaf;
	uint32_t *unit_dir;
	uint32_t *text_leaf;
	void	 *n_handle;
	uint64_t guid;
	uint32_t guid_hi, guid_lo;
	uint32_t bus_capabilities;
	uint32_t irmc, g;
	uint32_t module_vendor_id;
	uint32_t node_capabilities;
	uint32_t root_dir_len;
	uint32_t CRC;
	int	 status, i, ret;

	TNF_PROBE_0_DEBUG(s1394_init_local_config_rom_enter,
	    S1394_TNF_SL_CFGROM_STACK, "");

	/* Setup Config ROM mutex */
	mutex_init(&hal->local_config_rom_mutex,
	    NULL, MUTEX_DRIVER, hal->halinfo.hw_interrupt);

	/* Allocate 1K for the Config ROM buffer */
	hal->local_config_rom = (uint32_t *)kmem_zalloc(IEEE1394_CONFIG_ROM_SZ,
	    KM_SLEEP);

	/* Allocate 1K for the temporary buffer */
	hal->temp_config_rom_buf = (uint32_t *)kmem_zalloc(
	    IEEE1394_CONFIG_ROM_SZ, KM_SLEEP);

	config_rom = hal->local_config_rom;

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

	/* Build the config ROM structures */
	ret = s1394_init_config_rom_structures(hal);
	if (ret != DDI_SUCCESS) {
		/* Unlock the Config ROM buffer */
		mutex_exit(&hal->local_config_rom_mutex);
		kmem_free((void *)hal->temp_config_rom_buf,
		    IEEE1394_CONFIG_ROM_SZ);
		kmem_free((void *)hal->local_config_rom,
		    IEEE1394_CONFIG_ROM_SZ);
		mutex_destroy(&hal->local_config_rom_mutex);
		TNF_PROBE_1(s1394_init_local_config_rom_error,
		    S1394_TNF_SL_CFGROM_ERROR, "", tnf_string, msg,
		    "Failed in s1394_init_config_rom_structures()");
		TNF_PROBE_0_DEBUG(s1394_init_local_config_rom_exit,
		    S1394_TNF_SL_CFGROM_STACK, "");
		return (DDI_FAILURE);
	}
	/* Build the Bus_Info_Block - see IEEE 1394-1995, Section 8.3.2.5.4 */
	bus_capabilities = hal->halinfo.bus_capabilities;

	/*
	 * If we are Isoch Resource Manager capable then we are
	 * Bus Manager capable too.
	 */
	irmc = (bus_capabilities & IEEE1394_BIB_IRMC_MASK) >>
	    IEEE1394_BIB_IRMC_SHIFT;
	if (irmc)
		bus_capabilities = bus_capabilities | IEEE1394_BIB_BMC_MASK;

	/*
	 * Set generation to P1394a valid (but changeable)
	 * Even if we have a 1995 PHY, we will still provide
	 * certain P1394A functionality (especially with respect
	 * to Config ROM updates).  So we must publish this
	 * information.
	 */
	g = 2 << IEEE1394_BIB_GEN_SHIFT;
	bus_capabilities = bus_capabilities | g;

	/* Get the GUID */
	guid = hal->halinfo.guid;
	guid_hi = (uint32_t)(guid >> 32);
	guid_lo = (uint32_t)(guid & 0x00000000FFFFFFFF);

	config_rom[1] = 0x31333934;	/* "1394" */
	config_rom[2] = bus_capabilities;
	config_rom[3] = guid_hi;
	config_rom[4] = guid_lo;

	/* The CRC covers only our Bus_Info_Block */
	CRC = s1394_CRC16(&config_rom[1], 4);
	config_rom[0] = (0x04040000) | CRC;

	/* Do byte-swapping if necessary (x86) */
	for (i = 0; i < IEEE1394_BIB_QUAD_SZ; i++)
		config_rom[i] = T1394_DATA32(config_rom[i]);

	/* Build the Root_Directory - see IEEE 1394-1995, Section 8.3.2.5.5 */

	/* MODULE_VENDOR_ID - see IEEE 1394-1995, Section 8.3.2.5.5.1 */
	module_vendor_id = S1394_SUNW_OUI;

	/* NODE_CAPABILITIES - see IEEE 1394-1995, Section 8.3.2.5.5.2 */
	node_capabilities = hal->halinfo.node_capabilities &
	    IEEE1212_NODE_CAPABILITIES_MASK;
	root_dir_len = 2;

	config_rom[6] = (IEEE1212_MODULE_VENDOR_ID <<
	    IEEE1212_KEY_VALUE_SHIFT) | module_vendor_id;
	config_rom[7] = (IEEE1212_NODE_CAPABILITIES <<
	    IEEE1212_KEY_VALUE_SHIFT) | node_capabilities;

	CRC = s1394_CRC16(&config_rom[6], root_dir_len);
	config_rom[IEEE1394_BIB_QUAD_SZ] =
	    (root_dir_len << IEEE1394_CFG_ROM_LEN_SHIFT) | CRC;

	/* Do byte-swapping if necessary (x86) */
	for (i = IEEE1394_BIB_QUAD_SZ; i < 8; i++)
		config_rom[i] = T1394_DATA32(config_rom[i]);

	/* Build the Root Text leaf - see IEEE 1394-1995, Section 8.3.2.5.7 */
	text_leaf = (uint32_t *)kmem_zalloc(S1394_ROOT_TEXT_LEAF_SZ, KM_SLEEP);
	text_leaf[1] = 0x00000000;
	text_leaf[2] = 0x00000000;
	text_leaf[3] = 0x53756e20;	/* "Sun " */
	text_leaf[4] = 0x4d696372;	/* "Micr" */
	text_leaf[5] = 0x6f737973;	/* "osys" */
	text_leaf[6] = 0x74656d73;	/* "tems" */
	text_leaf[7] = 0x2c20496e;	/* ", In" */
	text_leaf[8] = 0x632e0000;	/* "c."   */
	CRC = s1394_CRC16(&text_leaf[1], S1394_ROOT_TEXT_LEAF_QUAD_SZ - 1);
	text_leaf[0] = (0x00080000) | CRC;

	/* Do byte-swapping if necessary (x86) */
	for (i = 0; i < 9; i++)
		text_leaf[i] = T1394_DATA32(text_leaf[i]);

	ret = s1394_add_config_rom_entry(hal, S1394_ROOT_TEXT_KEY, text_leaf,
	    S1394_ROOT_TEXT_LEAF_QUAD_SZ, &n_handle, &status);
	if (ret != DDI_SUCCESS) {
		kmem_free((void *)text_leaf, S1394_ROOT_TEXT_LEAF_SZ);
		/* Destroy the config_rom structures */
		(void) s1394_destroy_config_rom_structures(hal);
		/* Unlock the Config ROM buffer */
		mutex_exit(&hal->local_config_rom_mutex);
		kmem_free((void *)hal->temp_config_rom_buf,
		    IEEE1394_CONFIG_ROM_SZ);
		kmem_free((void *)hal->local_config_rom,
		    IEEE1394_CONFIG_ROM_SZ);
		mutex_destroy(&hal->local_config_rom_mutex);
		TNF_PROBE_1(s1394_init_local_config_rom_error,
		    S1394_TNF_SL_CFGROM_ERROR, "", tnf_string, msg,
		    "Failure in kmem_zalloc");
		TNF_PROBE_0_DEBUG(s1394_init_local_config_rom_exit,
		    S1394_TNF_SL_CFGROM_STACK, "");
		return (DDI_FAILURE);
	}
	kmem_free((void *)text_leaf, S1394_ROOT_TEXT_LEAF_SZ);

	/* Build the Node_Unique_Id leaf - IEEE 1394-1995, Sect. 8.3.2.5.7.1 */
	node_unique_id_leaf = (uint32_t *)kmem_zalloc(S1394_NODE_UNIQUE_ID_SZ,
	    KM_SLEEP);
	node_unique_id_leaf[1] = guid_hi;
	node_unique_id_leaf[2] = guid_lo;
	CRC = s1394_CRC16(&node_unique_id_leaf[1],
	    S1394_NODE_UNIQUE_ID_QUAD_SZ - 1);
	node_unique_id_leaf[0] = (0x00020000) | CRC;

	/* Do byte-swapping if necessary (x86) */
	for (i = 0; i < S1394_NODE_UNIQUE_ID_QUAD_SZ; i++)
		node_unique_id_leaf[i] = T1394_DATA32(node_unique_id_leaf[i]);

	ret = s1394_add_config_rom_entry(hal, S1394_NODE_UNIQUE_ID_KEY,
	    node_unique_id_leaf, S1394_NODE_UNIQUE_ID_QUAD_SZ, &n_handle,
	    &status);
	if (ret != DDI_SUCCESS) {
		kmem_free((void *)node_unique_id_leaf,
		    S1394_NODE_UNIQUE_ID_SZ);
		/* Destroy the config_rom structures */
		(void) s1394_destroy_config_rom_structures(hal);
		/* Unlock the Config ROM buffer */
		mutex_exit(&hal->local_config_rom_mutex);
		kmem_free((void *)hal->temp_config_rom_buf,
		    IEEE1394_CONFIG_ROM_SZ);
		kmem_free((void *)hal->local_config_rom,
		    IEEE1394_CONFIG_ROM_SZ);
		mutex_destroy(&hal->local_config_rom_mutex);
		TNF_PROBE_1(s1394_init_local_config_rom_error,
		    S1394_TNF_SL_CFGROM_ERROR, "", tnf_string, msg,
		    "Failure in kmem_zalloc");
		TNF_PROBE_0_DEBUG(s1394_init_local_config_rom_exit,
		    S1394_TNF_SL_CFGROM_STACK, "");
		return (DDI_FAILURE);
	}
	kmem_free((void *)node_unique_id_leaf, S1394_NODE_UNIQUE_ID_SZ);

	/* Build the Unit_Directory for 1394 Framework */
	unit_dir = (uint32_t *)kmem_zalloc(S1394_UNIT_DIR_SZ, KM_SLEEP);
	unit_dir[1] = 0x12080020;	/* Sun Microsystems */
	unit_dir[2] = 0x13000001;	/* Version 1 */
	unit_dir[3] = 0x81000001;	/* offset to the text leaf */
	CRC = s1394_CRC16(&unit_dir[1], 3);
	unit_dir[0] = (0x00030000) | CRC;

	/* Do byte-swapping if necessary (x86) */
	for (i = 0; i < 4; i++)
		unit_dir[i] = T1394_DATA32(unit_dir[i]);

	/* Build the Unit Directory text leaf */
	unit_dir[5] = 0x00000000;
	unit_dir[6] = 0x00000000;
	unit_dir[7] = 0x536f6c61;	/* "Sola" */
	unit_dir[8] = 0x72697320;	/* "ris " */
	unit_dir[9] = 0x31333934;	/* "1394" */
	unit_dir[10] = 0x20535720;	/* " SW " */
	unit_dir[11] = 0x4672616d;	/* "Fram" */
	unit_dir[12] = 0x65576f72;	/* "ewor" */
	unit_dir[13] = 0x6b000000;	/* "k"    */
	CRC = s1394_CRC16(&unit_dir[5], 9);
	unit_dir[4] = (0x00090000) | CRC;

	/* Do byte-swapping if necessary (x86) */
	for (i = 4; i < S1394_UNIT_DIR_QUAD_SZ; i++)
		unit_dir[i] = T1394_DATA32(unit_dir[i]);

	ret = s1394_add_config_rom_entry(hal, S1394_UNIT_DIR_KEY, unit_dir,
	    S1394_UNIT_DIR_QUAD_SZ, &n_handle, &status);
	if (ret != DDI_SUCCESS) {
		kmem_free((void *)unit_dir, S1394_UNIT_DIR_SZ);
		/* Destroy the config_rom structures */
		(void) s1394_destroy_config_rom_structures(hal);
		/* Unlock the Config ROM buffer */
		mutex_exit(&hal->local_config_rom_mutex);
		kmem_free((void *)hal->temp_config_rom_buf,
		    IEEE1394_CONFIG_ROM_SZ);
		/* Free the 1K for the Config ROM buffer */
		kmem_free((void *)hal->local_config_rom,
		    IEEE1394_CONFIG_ROM_SZ);
		mutex_destroy(&hal->local_config_rom_mutex);
		TNF_PROBE_1(s1394_init_local_config_rom_error,
		    S1394_TNF_SL_CFGROM_ERROR, "", tnf_string, msg,
		    "Failure in kmem_zalloc");
		TNF_PROBE_0_DEBUG(s1394_init_local_config_rom_exit,
		    S1394_TNF_SL_CFGROM_STACK, "");
		return (DDI_FAILURE);
	}
	kmem_free((void *)unit_dir, S1394_UNIT_DIR_SZ);

	hal->config_rom_update_amount = (IEEE1394_CONFIG_ROM_QUAD_SZ -
	    hal->free_space);

	/* Unlock the Config ROM buffer */
	mutex_exit(&hal->local_config_rom_mutex);

	/*
	 * The update_config_rom() call can return DDI_FAILURE if the
	 * HAL is shutdown.
	 */
	(void) HAL_CALL(hal).update_config_rom(hal->halinfo.hal_private,
	    config_rom, IEEE1394_CONFIG_ROM_QUAD_SZ);

	TNF_PROBE_0_DEBUG(s1394_init_local_config_rom_exit,
	    S1394_TNF_SL_CFGROM_STACK, "");
	return (DDI_SUCCESS);
}

/*
 * s1394_destroy_local_config_rom()
 *    is necessary for h1394_detach().  It undoes all the work that
 *    s1394_init_local_config_rom() had setup and more.  By pulling
 *    everything out of the conig rom structures and freeing them and their
 *    associated mutexes, the Config ROM is completely cleaned up.
 */
void
s1394_destroy_local_config_rom(s1394_hal_t *hal)
{
	TNF_PROBE_0_DEBUG(s1394_destroy_local_config_rom_enter,
	    S1394_TNF_SL_CFGROM_STACK, "");

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

	/* Destroy the config_rom structures */
	(void) s1394_destroy_config_rom_structures(hal);

	/* Unlock the Config ROM buffer */
	mutex_exit(&hal->local_config_rom_mutex);

	/* Free the 1K for the temporary buffer */
	kmem_free((void *)hal->temp_config_rom_buf, IEEE1394_CONFIG_ROM_SZ);
	/* Free the 1K for the Config ROM buffer */
	kmem_free((void *)hal->local_config_rom, IEEE1394_CONFIG_ROM_SZ);

	/* Setup Config ROM mutex */
	mutex_destroy(&hal->local_config_rom_mutex);

	TNF_PROBE_0_DEBUG(s1394_destroy_local_config_rom_exit,
	    S1394_TNF_SL_CFGROM_STACK, "");
}

/*
 * s1394_init_config_rom_structures()
 *    initializes the structures that are used to maintain the local Config ROM.
 *    Callers must be holding the local_config_rom_mutex.
 */
static int
s1394_init_config_rom_structures(s1394_hal_t *hal)
{
	s1394_config_rom_t *root_directory;
	s1394_config_rom_t *rest_of_config_rom;

	TNF_PROBE_0_DEBUG(s1394_init_config_rom_structures_enter,
	    S1394_TNF_SL_CFGROM_STACK, "");

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

	root_directory = (s1394_config_rom_t *)kmem_zalloc(
	    sizeof (s1394_config_rom_t), KM_SLEEP);

	root_directory->cfgrom_used = B_TRUE;
	root_directory->cfgrom_addr_lo = IEEE1394_BIB_QUAD_SZ;
	root_directory->cfgrom_addr_hi = IEEE1394_BIB_QUAD_SZ + 2;

	rest_of_config_rom = (s1394_config_rom_t *)kmem_zalloc(
	    sizeof (s1394_config_rom_t), KM_SLEEP);

	rest_of_config_rom->cfgrom_used = B_FALSE;
	rest_of_config_rom->cfgrom_addr_lo = root_directory->cfgrom_addr_hi + 1;
	rest_of_config_rom->cfgrom_addr_hi = IEEE1394_CONFIG_ROM_QUAD_SZ - 1;

	root_directory->cfgrom_next = rest_of_config_rom;
	root_directory->cfgrom_prev = NULL;
	rest_of_config_rom->cfgrom_next = NULL;
	rest_of_config_rom->cfgrom_prev = root_directory;

	hal->root_directory = root_directory;
	hal->free_space = IEEE1394_CONFIG_ROM_QUAD_SZ -
	    (rest_of_config_rom->cfgrom_addr_lo);

	TNF_PROBE_0_DEBUG(s1394_init_config_rom_structures_exit,
	    S1394_TNF_SL_CFGROM_STACK, "");
	return (DDI_SUCCESS);
}

/*
 * s1394_destroy_config_rom_structures()
 *    is used to destroy the structures that maintain the local Config ROM.
 *    Callers must be holding the local_config_rom_mutex.
 */
static int
s1394_destroy_config_rom_structures(s1394_hal_t *hal)
{
	s1394_config_rom_t *curr_blk;
	s1394_config_rom_t *next_blk;

	TNF_PROBE_0_DEBUG(s1394_destroy_config_rom_structures_enter,
	    S1394_TNF_SL_CFGROM_STACK, "");

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

	curr_blk = hal->root_directory;

	while (curr_blk != NULL) {
		next_blk = curr_blk->cfgrom_next;
		kmem_free(curr_blk, sizeof (s1394_config_rom_t));
		curr_blk = next_blk;
	}

	TNF_PROBE_0_DEBUG(s1394_destroy_config_rom_structures_exit,
	    S1394_TNF_SL_CFGROM_STACK, "");
	return (DDI_SUCCESS);
}

/*
 * s1394_add_config_rom_entry()
 *    is used to add a new entry to the local host's config ROM.  By
 *    specifying a key and a buffer, it is possible to update the Root
 *    Directory to point to the new entry (in buffer).  Additionally, all
 *    of the relevant CRCs, lengths, and generations are updated as well.
 *    By returning a Config ROM "handle", we can allow targets to remove
 *    the corresponding entry.
 *    Callers must be holding the local_config_rom_mutex.
 */
int
s1394_add_config_rom_entry(s1394_hal_t *hal, uint8_t key, uint32_t *buffer,
    uint_t size, void **handle, int *status)
{
	s1394_config_rom_t *curr_blk;
	s1394_config_rom_t *new_blk;
	uint32_t	   *config_rom;
	uint32_t	   *temp_buf;
	uint32_t	   CRC;
	uint_t		   tmp_offset;
	uint_t		   tmp_size, temp;
	uint_t		   last_entry_offset;
	int		   i;

	TNF_PROBE_0_DEBUG(s1394_add_config_rom_entry_enter,
	    "stacktrace 1394 s1394", "");

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

	if (size > hal->free_space) {
		/* Out of space */
		*status = CMD1394_ERSRC_CONFLICT;
		TNF_PROBE_0_DEBUG(s1394_add_config_rom_entry_exit,
		    S1394_TNF_SL_CFGROM_STACK, "");
		return (DDI_FAILURE);
	}

	config_rom = hal->local_config_rom;
	temp_buf = hal->temp_config_rom_buf;

	/* Copy the Bus_Info_Block */
	bcopy(&config_rom[0], &temp_buf[0], IEEE1394_BIB_SZ);

	/* Copy and add to the Root_Directory */
	tmp_offset = hal->root_directory->cfgrom_addr_lo;
	tmp_size = (hal->root_directory->cfgrom_addr_hi - tmp_offset) + 1;
	tmp_size = tmp_size + 1;	/* For the new entry */
	bcopy(&config_rom[tmp_offset], &temp_buf[tmp_offset], tmp_size << 2);
	last_entry_offset = hal->root_directory->cfgrom_addr_hi + 1;

	curr_blk = hal->root_directory;
	curr_blk->cfgrom_addr_hi = curr_blk->cfgrom_addr_hi + 1;
	while (curr_blk->cfgrom_next != NULL) {
		if (curr_blk->cfgrom_next->cfgrom_used == B_TRUE) {
			tmp_offset = curr_blk->cfgrom_next->cfgrom_addr_lo;
			tmp_size = (curr_blk->cfgrom_next->cfgrom_addr_hi -
			    tmp_offset) + 1;

			bcopy(&config_rom[tmp_offset],
			    &temp_buf[tmp_offset + 1], tmp_size << 2);
			curr_blk->cfgrom_next->cfgrom_addr_lo++;
			curr_blk->cfgrom_next->cfgrom_addr_hi++;
			last_entry_offset =
			    curr_blk->cfgrom_next->cfgrom_addr_hi;

			tmp_offset = curr_blk->cfgrom_next->root_dir_offset;

			/* Swap... add one... then unswap */
			temp = T1394_DATA32(temp_buf[tmp_offset]);
			temp++;
			temp_buf[tmp_offset] = T1394_DATA32(temp);
		} else {
			curr_blk->cfgrom_next->cfgrom_addr_lo++;
			hal->free_space--;
			break;
		}

		curr_blk = curr_blk->cfgrom_next;
	}

	/* Get the pointer to the "free" space */
	curr_blk = curr_blk->cfgrom_next;

	/* Is it an exact fit? */
	if (hal->free_space == size) {
		curr_blk->cfgrom_used = B_TRUE;

	} else {		/* Must break this piece */
		new_blk = (s1394_config_rom_t *)kmem_zalloc(
		    sizeof (s1394_config_rom_t), KM_SLEEP);
		if (new_blk == NULL) {
			TNF_PROBE_0_DEBUG(s1394_add_config_rom_entry_exit,
			    S1394_TNF_SL_CFGROM_STACK, "");
			return (DDI_FAILURE);
		}

		new_blk->cfgrom_addr_hi = curr_blk->cfgrom_addr_hi;
		new_blk->cfgrom_addr_lo = curr_blk->cfgrom_addr_lo + size;
		curr_blk->cfgrom_addr_hi = new_blk->cfgrom_addr_lo - 1;
		new_blk->cfgrom_next = curr_blk->cfgrom_next;
		curr_blk->cfgrom_next = new_blk;
		new_blk->cfgrom_prev = curr_blk;
		curr_blk->cfgrom_used = B_TRUE;
		last_entry_offset = curr_blk->cfgrom_addr_hi;
	}
	hal->free_space = hal->free_space - size;

	/* Copy in the new entry */
	tmp_offset = curr_blk->cfgrom_addr_lo;
	bcopy(buffer, &temp_buf[tmp_offset], size << 2);

	/* Update root directory */
	tmp_offset = hal->root_directory->cfgrom_addr_hi;
	tmp_size = tmp_offset - hal->root_directory->cfgrom_addr_lo;
	curr_blk->root_dir_offset = tmp_offset;
	tmp_offset = curr_blk->cfgrom_addr_lo - tmp_offset;

	temp_buf[hal->root_directory->cfgrom_addr_hi] =
	    T1394_DATA32((((uint32_t)key) << IEEE1212_KEY_VALUE_SHIFT) |
	    tmp_offset);
	tmp_offset = hal->root_directory->cfgrom_addr_lo;

	/* Do byte-swapping if necessary (x86) */
	for (i = (tmp_offset + 1); i <= hal->root_directory->cfgrom_addr_hi;
	    i++)
		temp_buf[i] = T1394_DATA32(temp_buf[i]);

	CRC = s1394_CRC16(&temp_buf[tmp_offset + 1], tmp_size);
	temp_buf[tmp_offset] = (tmp_size << IEEE1394_CFG_ROM_LEN_SHIFT) | CRC;

	/* Redo byte-swapping if necessary (x86) */
	for (i = tmp_offset; i <= hal->root_directory->cfgrom_addr_hi; i++)
		temp_buf[i] = T1394_DATA32(temp_buf[i]);

	/* Copy it back to config_rom buffer */
	last_entry_offset++;
	bcopy(&temp_buf[0], &config_rom[0], last_entry_offset << 2);

	/* Return a handle to this block */
	*handle = curr_blk;

	*status = T1394_NOERROR;

	TNF_PROBE_0_DEBUG(s1394_add_config_rom_entry_exit,
	    S1394_TNF_SL_CFGROM_STACK, "");
	return (DDI_SUCCESS);
}

/*
 * s1394_remove_config_rom_entry()
 *    is used to remove an entry from the local host's config ROM.  By
 *    specifying the Config ROM "handle" that was given in the allocation,
 *    it is possible to remove the entry.  Subsequently, the Config ROM is
 *    updated again.
 *    Callers must be holding the local_config_rom_mutex.
 */
int
s1394_remove_config_rom_entry(s1394_hal_t *hal, void **handle, int *status)
{
	s1394_config_rom_t *del_blk;
	s1394_config_rom_t *curr_blk;
	s1394_config_rom_t *last_blk;
	s1394_config_rom_t *free_blk;
	uint32_t	   *config_rom;
	uint32_t	   *temp_buf;
	uint32_t	   entry;
	uint_t		   CRC;
	uint_t		   root_offset;
	uint_t		   del_offset;
	uint_t		   tmp_offset;
	uint_t		   tmp_size;
	int		   i;

	TNF_PROBE_0_DEBUG(s1394_remove_config_rom_entry_enter,
	    S1394_TNF_SL_CFGROM_STACK, "");

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

	del_blk = (s1394_config_rom_t *)(*handle);

	config_rom = hal->local_config_rom;
	temp_buf = hal->temp_config_rom_buf;

	/* Copy the Bus_Info_Block */
	bcopy(&config_rom[0], &temp_buf[0], IEEE1394_BIB_SZ);

	root_offset = hal->root_directory->cfgrom_addr_lo;
	del_offset = del_blk->root_dir_offset;

	/* Update Root_Directory entries before the deleted one */
	for (i = root_offset; i < del_offset; i++) {
		entry = T1394_DATA32(config_rom[i]);

		/* If entry is an offset address - update it */
		if (entry & 0x80000000)
			temp_buf[i] = T1394_DATA32(entry - 1);
		else
			temp_buf[i] = T1394_DATA32(entry);
	}

	/* Move all Unit_Directories prior to the deleted one */
	curr_blk = hal->root_directory->cfgrom_next;

	while (curr_blk != del_blk) {
		tmp_offset = curr_blk->cfgrom_addr_lo;
		tmp_size = (curr_blk->cfgrom_addr_hi - tmp_offset) + 1;

		bcopy(&config_rom[tmp_offset], &temp_buf[tmp_offset - 1],
		    tmp_size << 2);
		curr_blk->cfgrom_addr_lo--;
		curr_blk->cfgrom_addr_hi--;
		curr_blk = curr_blk->cfgrom_next;
	}

	/* Move all Unit_Directories after the deleted one */
	curr_blk = del_blk->cfgrom_next;
	last_blk = del_blk->cfgrom_prev;

	del_offset = (del_blk->cfgrom_addr_hi - del_blk->cfgrom_addr_lo) + 1;

	while ((curr_blk != NULL) && (curr_blk->cfgrom_used == B_TRUE)) {
		tmp_offset = curr_blk->cfgrom_addr_lo;
		tmp_size = (curr_blk->cfgrom_addr_hi - tmp_offset) + 1;

		bcopy(&config_rom[tmp_offset],
		    &temp_buf[tmp_offset - (del_offset + 1)], tmp_size << 2);

		root_offset = curr_blk->root_dir_offset;
		temp_buf[root_offset - 1] =
		    config_rom[root_offset] - del_offset;
		curr_blk->root_dir_offset--;
		curr_blk->cfgrom_addr_lo = curr_blk->cfgrom_addr_lo -
		    (del_offset + 1);
		curr_blk->cfgrom_addr_hi = curr_blk->cfgrom_addr_hi -
		    (del_offset + 1);

		last_blk = curr_blk;
		curr_blk = curr_blk->cfgrom_next;
	}

	/* Remove del_blk from the list */
	if (del_blk->cfgrom_prev != NULL)
		del_blk->cfgrom_prev->cfgrom_next = del_blk->cfgrom_next;

	if (del_blk->cfgrom_next != NULL)
		del_blk->cfgrom_next->cfgrom_prev = del_blk->cfgrom_prev;

	del_blk->cfgrom_prev = NULL;
	del_blk->cfgrom_next = NULL;
	kmem_free((void *)del_blk, sizeof (s1394_config_rom_t));

	/* Update and zero out the "free" block */
	if (curr_blk != NULL) {
		curr_blk->cfgrom_addr_lo = curr_blk->cfgrom_addr_lo -
		    (del_offset + 1);

	} else {
		free_blk = (s1394_config_rom_t *)kmem_zalloc(
		    sizeof (s1394_config_rom_t), KM_SLEEP);
		if (free_blk == NULL) {
			TNF_PROBE_0_DEBUG(s1394_remove_config_rom_entry_exit,
			    S1394_TNF_SL_CFGROM_STACK, "");
			return (DDI_FAILURE);
		}

		free_blk->cfgrom_used = B_FALSE;
		free_blk->cfgrom_addr_lo = (IEEE1394_CONFIG_ROM_QUAD_SZ - 1) -
		    (del_offset + 1);
		free_blk->cfgrom_addr_hi = (IEEE1394_CONFIG_ROM_QUAD_SZ - 1);

		free_blk->cfgrom_prev = last_blk;
		free_blk->cfgrom_next = NULL;
		curr_blk = free_blk;
	}
	hal->free_space = hal->free_space + (del_offset + 1);
	tmp_offset = curr_blk->cfgrom_addr_lo;
	tmp_size = (curr_blk->cfgrom_addr_hi - tmp_offset) + 1;
	bzero(&temp_buf[tmp_offset], tmp_size << 2);


	/* Update root directory */
	hal->root_directory->cfgrom_addr_hi--;
	tmp_offset = hal->root_directory->cfgrom_addr_lo;
	tmp_size = hal->root_directory->cfgrom_addr_hi - tmp_offset;

	/* Do byte-swapping if necessary (x86) */
	for (i = (tmp_offset + 1); i <= hal->root_directory->cfgrom_addr_hi;
	    i++)
		temp_buf[i] = T1394_DATA32(temp_buf[i]);

	CRC = s1394_CRC16(&temp_buf[tmp_offset + 1], tmp_size);
	temp_buf[tmp_offset] = (tmp_size << IEEE1394_CFG_ROM_LEN_SHIFT) | CRC;

	/* Do byte-swapping if necessary (x86) */
	for (i = (tmp_offset + 1); i <= hal->root_directory->cfgrom_addr_hi;
	    i++)
		temp_buf[i] = T1394_DATA32(temp_buf[i]);

	/* Copy it back to config_rom buffer */
	tmp_size = IEEE1394_CONFIG_ROM_SZ - (hal->free_space << 2);
	bcopy(&temp_buf[0], &config_rom[0], tmp_size);

	/* Return a handle to this block */
	*handle = NULL;

	*status = T1394_NOERROR;

	TNF_PROBE_0_DEBUG(s1394_remove_config_rom_entry_exit,
	    S1394_TNF_SL_CFGROM_STACK, "");
	return (DDI_SUCCESS);
}

/*
 * s1394_update_config_rom_callback()
 *    is the callback used by t1394_add_cfgrom_entry() and
 *    t1394_rem_cfgrom_entry().  After a target updates the Config ROM, a
 *    timer is set with this as its callback function.  This is to reduce
 *    the number of bus resets that would be necessary if many targets
 *    wished to update the Config ROM simultaneously.
 */
void
s1394_update_config_rom_callback(void *arg)
{
	s1394_hal_t	*hal;
	uint32_t	*config_rom;
	uint32_t	bus_capabilities;
	uint32_t	g;
	uint_t		CRC;
	uint_t		last_entry_offset;
	int		i, ret;

	TNF_PROBE_0_DEBUG(s1394_update_config_rom_callback_enter,
	    S1394_TNF_SL_CFGROM_STACK, "");

	hal = (s1394_hal_t *)arg;

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

	config_rom = hal->local_config_rom;

	/* Update Generation and CRC for Bus_Info_Block */

	/* Do byte-swapping if necessary (x86) */
	for (i = 0; i < IEEE1394_BIB_QUAD_SZ; i++)
		config_rom[i] = T1394_DATA32(config_rom[i]);

	bus_capabilities = config_rom[IEEE1212_NODE_CAP_QUAD];
	g = ((bus_capabilities & IEEE1394_BIB_GEN_MASK) >>
	    IEEE1394_BIB_GEN_SHIFT) + 1;
	if (g > 15)
		g = 2;
	g = g << IEEE1394_BIB_GEN_SHIFT;

	bus_capabilities = (bus_capabilities & (~IEEE1394_BIB_GEN_MASK)) | g;
	config_rom[IEEE1212_NODE_CAP_QUAD] = bus_capabilities;

	CRC = s1394_CRC16(&config_rom[1], IEEE1394_BIB_QUAD_SZ - 1);
	config_rom[0] = (0x04040000) | CRC;

	/* Do byte-swapping if necessary (x86) */
	for (i = 0; i < IEEE1394_BIB_QUAD_SZ; i++)
		config_rom[i] = T1394_DATA32(config_rom[i]);

	/* Make sure we update only what is necessary */
	last_entry_offset = (IEEE1394_CONFIG_ROM_QUAD_SZ - hal->free_space);
	if (last_entry_offset < hal->config_rom_update_amount)
		last_entry_offset = hal->config_rom_update_amount;

	hal->config_rom_update_amount = (IEEE1394_CONFIG_ROM_QUAD_SZ -
	    hal->free_space);

	/* Clear the timer flag */
	hal->config_rom_timer_set = B_FALSE;

	/* Unlock the Config ROM buffer */
	mutex_exit(&hal->local_config_rom_mutex);

	/*
	 * The update_config_rom() call can return DDI_FAILURE if the
	 * HAL is shutdown.
	 */
	(void) HAL_CALL(hal).update_config_rom(hal->halinfo.hal_private,\
	    config_rom, last_entry_offset);

	/* Initiate a bus reset */
	ret = HAL_CALL(hal).bus_reset(hal->halinfo.hal_private);
	if (ret != DDI_SUCCESS) {
		TNF_PROBE_1(s1394_update_config_rom_callback_error,
		    S1394_TNF_SL_ERROR, "", tnf_string, msg,
		    "Error initiating bus reset");
	}

	TNF_PROBE_0_DEBUG(s1394_update_config_rom_callback_exit,
	    S1394_TNF_SL_CFGROM_STACK, "");
}