/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* s1394_dev_disc.c
* 1394 Services Layer Device Discovery Routines
* This file contains the bus reset thread code, bus manager routines and
* various routines that are used to implement remote Config ROM reading.
*
* FUTURE:
* Rescan the bus if invalid nodes are seen.
* Investigate taskq for reading phase2 config rom reads.
* If we are reading the entire bus info blk, we should attempt
* a block read and fallback to quad reads if this fails.
*/
#include <sys/conf.h>
#include <sys/sysmacros.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/sunndi.h>
#include <sys/modctl.h>
#include <sys/ddi_impldefs.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/kstat.h>
#include <sys/varargs.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>
/* hcmd_ret_t */
typedef enum {
S1394_HCMD_INVALID,
S1394_HCMD_NODE_DONE,
S1394_HCMD_NODE_EXPECT_MORE,
S1394_HCMD_LOCK_FAILED
} hcmd_ret_t;
#define QUAD_TO_CFGROM_ADDR(b, n, q, addr) { \
uint64_t bl = (b); \
uint64_t nl = (n); \
addr = ((bl) << IEEE1394_ADDR_BUS_ID_SHIFT) | \
((nl) << IEEE1394_ADDR_PHY_ID_SHIFT); \
addr += IEEE1394_CONFIG_ROM_ADDR + ((q) << 2); \
}
#define CFGROM_READ_PAUSE(d) \
((s1394_cfgrom_read_delay_ms == 0) ? (void) 0 : \
delay(drv_usectohz((d) * 1000)))
#define BUMP_CFGROM_READ_DELAY(n) \
(n)->cfgrom_read_delay += s1394_cfgrom_read_delay_incr
#define CFGROM_GET_READ_DELAY(n, d) \
((d) = (n)->cfgrom_read_delay)
#define SETUP_QUAD_READ(n, reset_fails, quadlet, cnt) \
{ \
int i = (reset_fails); \
if (i != 0) { \
(n)->cfgrom_read_fails = 0; \
(n)->cfgrom_read_delay = (uchar_t)s1394_cfgrom_read_delay_ms; \
} \
(n)->cfgrom_quad_to_read = (quadlet); \
(n)->cfgrom_quad_read_cnt = (cnt); \
}
static void s1394_wait_for_events(s1394_hal_t *hal, int firsttime);
static int s1394_wait_for_cfgrom_callbacks(s1394_hal_t *hal, uint_t wait_gen,
hcmd_ret_t(*handle_cmd_fn)(s1394_hal_t *hal, cmd1394_cmd_t *cmd));
static void s1394_flush_cmplq(s1394_hal_t *hal);
static void s1394_br_thread_exit(s1394_hal_t *hal);
static void s1394_target_bus_reset_notifies(s1394_hal_t *hal,
t1394_localinfo_t *localinfo);
static int s1394_alloc_cfgrom(s1394_hal_t *hal, s1394_node_t *node,
s1394_status_t *status);
static int s1394_cfgrom_scan_phase1(s1394_hal_t *hal);
static hcmd_ret_t s1394_br_thread_handle_cmd_phase1(s1394_hal_t *hal,
cmd1394_cmd_t *cmd);
static int s1394_cfgrom_scan_phase2(s1394_hal_t *hal);
static hcmd_ret_t s1394_br_thread_handle_cmd_phase2(s1394_hal_t *hal,
cmd1394_cmd_t *cmd);
static int s1394_read_config_quadlet(s1394_hal_t *hal, cmd1394_cmd_t *cmd,
s1394_status_t *status);
static void s1394_cfgrom_read_callback(cmd1394_cmd_t *cmd);
static void s1394_get_quad_info(cmd1394_cmd_t *cmd, uint32_t *node_num,
uint32_t *quadlet, uint32_t *data);
static int s1394_match_GUID(s1394_hal_t *hal, s1394_node_t *nnode);
static int s1394_match_all_GUIDs(s1394_hal_t *hal);
static void s1394_become_bus_mgr(void *arg);
static void s1394_become_bus_mgr_callback(cmd1394_cmd_t *cmd);
static int s1394_bus_mgr_processing(s1394_hal_t *hal);
static int s1394_do_bus_mgr_processing(s1394_hal_t *hal);
static void s1394_bus_mgr_timers_stop(s1394_hal_t *hal,
timeout_id_t *bus_mgr_query_tid, timeout_id_t *bus_mgr_tid);
static void s1394_bus_mgr_timers_start(s1394_hal_t *hal,
timeout_id_t *bus_mgr_query_tid, timeout_id_t *bus_mgr_tid);
static int s1394_cycle_master_capable(s1394_hal_t *hal);
static int s1394_do_phy_config_pkt(s1394_hal_t *hal, int new_root,
int new_gap_cnt, uint32_t IRM_flags);
static void s1394_phy_config_callback(cmd1394_cmd_t *cmd);
static int s1394_calc_next_quad(s1394_hal_t *hal, s1394_node_t *node,
uint32_t quadlet, uint32_t *nextquadp);
static int s1394_cfgrom_read_retry_cnt = 3; /* 1 + 3 retries */
static int s1394_cfgrom_read_delay_ms = 20; /* start with 20ms */
static int s1394_cfgrom_read_delay_incr = 10; /* 10ms increments */
static int s1394_enable_crc_validation = 0;
static int s1394_turn_off_dir_stack = 0;
static int s1394_crcsz_is_cfgsz = 0;
static int s1394_enable_rio_pass1_workarounds = 0;
/*
* s1394_br_thread()
* is the bus reset thread. Its sole purpose is to read/reread config roms
* as appropriate and do bus reset time things (bus manager processing,
* isoch resource reallocation etc.).
*/
void
s1394_br_thread(s1394_hal_t *hal)
{
TNF_PROBE_0_DEBUG(s1394_br_thread_enter, S1394_TNF_SL_HOTPLUG_STACK,
"");
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
/* Initialize the Bus Mgr timers */
hal->bus_mgr_timeout_id = 0;
hal->bus_mgr_query_timeout_id = 0;
/* Initialize the cmpletion Q */
mutex_enter(&hal->br_cmplq_mutex);
hal->br_cmplq_head = hal->br_cmplq_tail = NULL;
mutex_exit(&hal->br_cmplq_mutex);
s1394_wait_for_events(hal, 1);
for (;;) {
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_0_DEBUG(s1394_br_thread_wait,
S1394_TNF_SL_HOTPLUG_STACK, "");
s1394_wait_for_events(hal, 0);
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_1_DEBUG(s1394_br_thread_restart,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_int, hal_instance, ddi_get_instance(hal->halinfo.dip));
/* stop bus manager timeouts, if needed */
s1394_bus_mgr_timers_stop(hal, &hal->bus_mgr_query_timeout_id,
&hal->bus_mgr_timeout_id);
s1394_flush_cmplq(hal);
/* start timers for checking bus manager, if needed */
s1394_bus_mgr_timers_start(hal, &hal->bus_mgr_query_timeout_id,
&hal->bus_mgr_timeout_id);
/* Try to reallocate all isoch resources */
s1394_isoch_rsrc_realloc(hal);
if (s1394_cfgrom_scan_phase1(hal) != DDI_SUCCESS) {
TNF_PROBE_0_DEBUG(br_thread_phase1_restart,
S1394_TNF_SL_HOTPLUG_STACK, "");
continue;
}
TNF_PROBE_1_DEBUG(s1394_br_thread_phase1_done,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_int, hal_instance, ddi_get_instance(hal->halinfo.dip));
if (s1394_bus_mgr_processing(hal) != DDI_SUCCESS) {
TNF_PROBE_0_DEBUG(br_thread_bus_mgr_restart,
S1394_TNF_SL_HOTPLUG_STACK, "");
continue;
}
TNF_PROBE_1_DEBUG(s1394_br_thread_bus_mgr_proc_done,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_int, hal_instance, ddi_get_instance(hal->halinfo.dip));
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
if (s1394_cfgrom_scan_phase2(hal) != DDI_SUCCESS) {
TNF_PROBE_0_DEBUG(br_thread_phase2_restart,
S1394_TNF_SL_HOTPLUG_STACK, "");
continue;
}
TNF_PROBE_1_DEBUG(s1394_br_thread_done,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_int, hal_instance, ddi_get_instance(hal->halinfo.dip));
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
}
}
/*
* s1394_wait_for_events()
* blocks waiting for a cv_signal on the bus reset condition variable.
* Used by the bus reset thread for synchronizing with the bus reset/
* self id interrupt callback from the hal. Does CPR initialization
* first time it is called. If services layer sees a valid self id
* buffer, it builds the topology tree and signals the bus reset thread
* to read the config roms as appropriate (indicated by BR_THR_CFGROM_SCAN).
* If the services layer wishes to kill the bus reset thread, it signals
* this by signaling a BR_THR_GO_AWAY event.
*/
static void
s1394_wait_for_events(s1394_hal_t *hal, int firsttime)
{
uint_t event;
TNF_PROBE_0_DEBUG(s1394_wait_for_events_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
ASSERT(MUTEX_NOT_HELD(&hal->br_thread_mutex));
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
if (firsttime)
CALLB_CPR_INIT(&hal->hal_cprinfo, &hal->br_thread_mutex,
callb_generic_cpr, "s1394_br_thread");
/* Check and wait for a BUS RESET */
mutex_enter(&hal->br_thread_mutex);
while ((event = hal->br_thread_ev_type) == 0) {
CALLB_CPR_SAFE_BEGIN(&hal->hal_cprinfo);
cv_wait(&hal->br_thread_cv, &hal->br_thread_mutex);
CALLB_CPR_SAFE_END(&hal->hal_cprinfo, &hal->br_thread_mutex);
}
if (event & BR_THR_GO_AWAY) {
TNF_PROBE_1(s1394_wait_for_events, S1394_TNF_SL_HOTPLUG_STACK,
"", tnf_string, msg, "Go away set");
s1394_br_thread_exit(hal);
TNF_PROBE_0_DEBUG(s1394_wait_for_events_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
/*NOTREACHED*/
return;
}
if (firsttime) {
TNF_PROBE_0_DEBUG(s1394_wait_for_events_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
mutex_exit(&hal->br_thread_mutex);
return;
}
mutex_enter(&hal->topology_tree_mutex);
if (event & BR_THR_CFGROM_SCAN) {
TNF_PROBE_2_DEBUG(s1394_wait_for_events_scan,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_int, br_thread_gen, hal->br_cfgrom_read_gen,
tnf_int, hal_generation, hal->generation_count);
}
hal->br_cfgrom_read_gen = hal->generation_count;
hal->br_thread_ev_type &= ~BR_THR_CFGROM_SCAN;
mutex_exit(&hal->topology_tree_mutex);
mutex_exit(&hal->br_thread_mutex);
TNF_PROBE_0_DEBUG(s1394_wait_for_events_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
}
/*
* s1394_wait_for_cfgrom_callbacks()
* Waits for completed config rom reads. Takes each completion off the
* completion queue and passes it to the "completion handler" function
* that was passed in as an argument. Further processing of the completion
* queue depends on the return status of the completion handler. If there
* is a bus reset while waiting for completions or if the services layer
* signals BR_THR_GO_AWAY, quits waiting for completions and returns
* non-zero. Also returns non-zero if completion handler returns
* S1394_HCMD_LOCK_FAILED. Returns 0 if config roms for all nodes have
* been dealt with.
*/
static int
s1394_wait_for_cfgrom_callbacks(s1394_hal_t *hal, uint_t wait_gen,
hcmd_ret_t(*handle_cmd_fn)(s1394_hal_t *hal, cmd1394_cmd_t *cmd))
{
cmd1394_cmd_t *cmd;
s1394_cmd_priv_t *s_priv;
int ret, done = 0;
hcmd_ret_t cmdret;
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_1_DEBUG(s1394_wait_for_cfgrom_callbacks_enter,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_uint, wait_gen, wait_gen);
ret = DDI_SUCCESS;
while (!done) {
mutex_enter(&hal->br_cmplq_mutex);
mutex_enter(&hal->topology_tree_mutex);
while (wait_gen == hal->generation_count &&
(hal->br_thread_ev_type & BR_THR_GO_AWAY) == 0 &&
hal->br_cmplq_head == NULL) {
mutex_exit(&hal->topology_tree_mutex);
cv_wait(&hal->br_cmplq_cv, &hal->br_cmplq_mutex);
mutex_enter(&hal->topology_tree_mutex);
}
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
if (wait_gen != hal->generation_count ||
(hal->br_thread_ev_type & BR_THR_GO_AWAY) != 0) {
#if !defined(NPROBE) && defined(TNF_DEBUG)
int hal_gen = hal->generation_count;
#endif
mutex_exit(&hal->topology_tree_mutex);
mutex_exit(&hal->br_cmplq_mutex);
s1394_flush_cmplq(hal);
TNF_PROBE_1_DEBUG(s1394_wait_for_cfgrom_callbacks_exit,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int, hal_gen,
hal_gen);
return (DDI_FAILURE);
}
mutex_exit(&hal->topology_tree_mutex);
if ((cmd = hal->br_cmplq_head) != NULL) {
s_priv = S1394_GET_CMD_PRIV(cmd);
hal->br_cmplq_head = s_priv->cmd_priv_next;
}
if (cmd == hal->br_cmplq_tail)
hal->br_cmplq_tail = NULL;
mutex_exit(&hal->br_cmplq_mutex);
if (cmd != NULL) {
if (cmd->bus_generation != wait_gen) {
TNF_PROBE_3(
s1394_wait_for_cfgrom_callbacks,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_string, msg, "command gen != wait_gen",
tnf_uint, cmd_gen, cmd->bus_generation,
tnf_uint, wait_gen, wait_gen);
(void) s1394_free_cmd(hal, &cmd);
continue;
}
cmdret = (*handle_cmd_fn)(hal, cmd);
TNF_PROBE_2_DEBUG(s1394_wait_for_cfgrom_callbacks,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_opaque, cmd, cmd, tnf_int, cmdret, cmdret);
ASSERT(cmdret != S1394_HCMD_INVALID);
if (cmdret == S1394_HCMD_LOCK_FAILED) {
/* flush completion queue */
ret = DDI_FAILURE;
s1394_flush_cmplq(hal);
break;
} else if (cmdret == S1394_HCMD_NODE_DONE) {
if (--hal->cfgroms_being_read == 0) {
/* All done */
break;
}
} else {
ASSERT(cmdret == S1394_HCMD_NODE_EXPECT_MORE);
done = 0;
}
}
}
TNF_PROBE_0_DEBUG(s1394_wait_for_cfgrom_callbacks_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (ret);
}
/*
* s1394_flush_cmplq()
* Frees all cmds on the completion queue.
*/
static void
s1394_flush_cmplq(s1394_hal_t *hal)
{
s1394_cmd_priv_t *s_priv;
cmd1394_cmd_t *cmd, *tcmd;
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_0_DEBUG(s1394_flush_cmplq_enter, S1394_TNF_SL_HOTPLUG_STACK,
"");
cmd = NULL;
do {
mutex_enter(&hal->br_cmplq_mutex);
cmd = hal->br_cmplq_head;
hal->br_cmplq_head = hal->br_cmplq_tail = NULL;
mutex_exit(&hal->br_cmplq_mutex);
while (cmd != NULL) {
s_priv = S1394_GET_CMD_PRIV(cmd);
tcmd = s_priv->cmd_priv_next;
TNF_PROBE_2_DEBUG(s1394_flush_cmplq,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_opaque, cmd,
cmd, tnf_uint, cmd_gen, cmd->bus_generation);
(void) s1394_free_cmd(hal, &cmd);
cmd = tcmd;
}
mutex_enter(&hal->br_cmplq_mutex);
cmd = hal->br_cmplq_head;
mutex_exit(&hal->br_cmplq_mutex);
} while (cmd != NULL);
TNF_PROBE_0_DEBUG(s1394_flush_cmplq_exit, S1394_TNF_SL_HOTPLUG_STACK,
"");
}
/*
* s1394_br_thread_exit()
* Flushes the completion queue and calls thread_exit() (which effectively
* kills the bus reset thread).
*/
static void
s1394_br_thread_exit(s1394_hal_t *hal)
{
ASSERT(MUTEX_HELD(&hal->br_thread_mutex));
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_0(s1394_br_thread_exit_enter, S1394_TNF_SL_HOTPLUG_STACK, "");
s1394_flush_cmplq(hal);
#ifndef __lock_lint
CALLB_CPR_EXIT(&hal->hal_cprinfo);
#endif
hal->br_thread_ev_type &= ~BR_THR_GO_AWAY;
thread_exit();
/*NOTREACHED*/
TNF_PROBE_0(s1394_br_thread_exit_enter, S1394_TNF_SL_HOTPLUG_STACK, "");
}
/*
* s1394_target_bus_reset_notifies()
* tells the ndi event framework to invoke any callbacks registered for
* "bus reset event".
*/
static void
s1394_target_bus_reset_notifies(s1394_hal_t *hal, t1394_localinfo_t *localinfo)
{
ddi_eventcookie_t cookie;
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_2_DEBUG(s1394_target_bus_reset_notifies_enter,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_uint, bus_gen,
localinfo->bus_generation, tnf_uint, node_id,
localinfo->local_nodeID);
if (ndi_event_retrieve_cookie(hal->hal_ndi_event_hdl, NULL,
DDI_DEVI_BUS_RESET_EVENT, &cookie, NDI_EVENT_NOPASS) ==
NDI_SUCCESS) {
(void) ndi_event_run_callbacks(hal->hal_ndi_event_hdl, NULL,
cookie, localinfo);
}
TNF_PROBE_0_DEBUG(s1394_target_bus_reset_notifies_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
}
/*
* s1394_alloc_cfgrom()
* Allocates config rom for the node. Sets CFGROM_NEW_ALLOC bit in the
* node cfgrom state. Drops topology_tree_mutex around the calls to
* kmem_zalloc(). If re-locking fails, returns DDI_FAILURE, else returns
* DDI_SUCCESS.
*/
static int
s1394_alloc_cfgrom(s1394_hal_t *hal, s1394_node_t *node, s1394_status_t *status)
{
uint32_t *cfgrom;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
TNF_PROBE_0_DEBUG(s1394_alloc_cfgrom_enter, S1394_TNF_SL_HOTPLUG_STACK,
"");
*status = S1394_NOSTATUS;
/*
* if cfgrom is non-NULL, this has to be generation changed
* case (where we allocate cfgrom again to reread the cfgrom)
*/
ASSERT(node->cfgrom == NULL || (node->cfgrom != NULL &&
CFGROM_GEN_CHANGED(node) == B_TRUE));
/*
* if node matched, either cfgrom has to be NULL or link should be
* off in the last matched node or config rom generations changed.
*/
ASSERT(NODE_MATCHED(node) == B_FALSE || (NODE_MATCHED(node) == B_TRUE &&
(node->cfgrom == NULL || LINK_ACTIVE(node->old_node) == B_FALSE) ||
CFGROM_GEN_CHANGED(node) == B_TRUE));
s1394_unlock_tree(hal);
cfgrom = (uint32_t *)kmem_zalloc(IEEE1394_CONFIG_ROM_SZ, KM_SLEEP);
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
kmem_free(cfgrom, IEEE1394_CONFIG_ROM_SZ);
*status |= S1394_LOCK_FAILED;
TNF_PROBE_1(s1394_alloc_cfgrom, S1394_TNF_SL_HOTPLUG_ERROR,
"", tnf_string, msg, "cannot relock the tree");
TNF_PROBE_0_DEBUG(s1394_alloc_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
node->cfgrom = cfgrom;
node->cfgrom_size = IEEE1394_CONFIG_ROM_QUAD_SZ;
SET_CFGROM_NEW_ALLOC(node);
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
TNF_PROBE_3(s1394_alloc_cfgrom_exit, S1394_TNF_SL_HOTPLUG_STACK,
"cfgrom alloc", tnf_uint, hal_gen, hal->generation_count, tnf_uint,
node_num, node->node_num, tnf_opaque, cfgrom, cfgrom);
return (DDI_SUCCESS);
}
/*
* s1394_free_cfgrom()
* Marks the config rom invalid and frees up the config based on otpions.
*/
void
s1394_free_cfgrom(s1394_hal_t *hal, s1394_node_t *node,
s1394_free_cfgrom_t options)
{
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
ASSERT(node->cfgrom != NULL);
TNF_PROBE_0_DEBUG(s1394_free_cfgrom_enter, S1394_TNF_SL_HOTPLUG_STACK,
"");
if (options == S1394_FREE_CFGROM_BOTH) {
/*
* free in both old and new trees; will be called with
* new node.
*/
s1394_node_t *onode = node->old_node;
if (NODE_MATCHED(node) == B_TRUE && onode->cfgrom != NULL)
ASSERT(onode->cfgrom == node->cfgrom);
TNF_PROBE_4(s1394_free_cfgrom_both,
S1394_TNF_SL_HOTPLUG_STACK, "cfgrom free", tnf_uint,
hal_gen, hal->generation_count, tnf_int, node_num,
node->node_num, tnf_opaque, old_cfgrom, onode->cfgrom,
tnf_opaque, cfgrom, node->cfgrom);
if (onode != NULL && onode->cfgrom != NULL && onode->cfgrom !=
node->cfgrom)
kmem_free(onode->cfgrom, IEEE1394_CONFIG_ROM_SZ);
kmem_free(node->cfgrom, IEEE1394_CONFIG_ROM_SZ);
onode->cfgrom = NULL;
node->cfgrom = NULL;
CLEAR_CFGROM_STATE(onode);
CLEAR_CFGROM_STATE(node);
} else if (options == S1394_FREE_CFGROM_NEW) {
TNF_PROBE_2(s1394_free_cfgrom_new,
S1394_TNF_SL_HOTPLUG_STACK, "cfgrom free",
tnf_int, node_num, node->node_num,
tnf_opaque, cfgrom, node->cfgrom);
ASSERT(CFGROM_NEW_ALLOC(node) == B_TRUE);
kmem_free(node->cfgrom, IEEE1394_CONFIG_ROM_SZ);
CLEAR_CFGROM_NEW_ALLOC(node);
node->cfgrom = NULL;
CLEAR_CFGROM_STATE(node);
} else if (options == S1394_FREE_CFGROM_OLD) {
/* freeing in old tree */
TNF_PROBE_2_DEBUG(s1394_free_cfgrom_old,
S1394_TNF_SL_HOTPLUG_STACK, "cfgrom free",
tnf_int, node_num, node->node_num,
tnf_opaque, cfgrom, node->cfgrom);
kmem_free(node->cfgrom, IEEE1394_CONFIG_ROM_SZ);
node->cfgrom = NULL;
CLEAR_CFGROM_STATE(node);
}
TNF_PROBE_0_DEBUG(s1394_free_cfgrom_exit, S1394_TNF_SL_HOTPLUG_STACK,
"");
}
/*
* s1394_copy_cfgrom()
* Copies config rom info from "from" node to "to" node. Clears
* CFGROM_NEW_ALLOC bit in cfgrom state in bothe nodes. (CFGROM_NEW_ALLOC
* acts as a reference count. If set, only the node in the current tree
* has a pointer to it; if clear, both the node in the current tree as
* well as the corresponding node in the old tree point to the same memory).
*/
void
s1394_copy_cfgrom(s1394_node_t *to, s1394_node_t *from)
{
TNF_PROBE_3_DEBUG(s1394_copy_cfgrom_enter, S1394_TNF_SL_HOTPLUG_STACK,
"", tnf_int, to_node, to->node_num, tnf_int,
from_node, from->node_num, tnf_opaque, from_cfgrom, from->cfgrom);
ASSERT(to->cfgrom == NULL);
to->cfgrom = from->cfgrom;
to->cfgrom_state = from->cfgrom_state;
to->cfgrom_valid_size = from->cfgrom_valid_size;
to->cfgrom_size = from->cfgrom_size;
to->node_state = from->node_state;
bcopy(from->dir_stack, to->dir_stack,
offsetof(s1394_node_t, cfgrom_quad_to_read) -
offsetof(s1394_node_t, dir_stack));
to->cfgrom_quad_to_read = from->cfgrom_quad_to_read;
CLEAR_CFGROM_NEW_ALLOC(to);
CLEAR_CFGROM_NEW_ALLOC(from);
/*
* old link off, new link on => handled in s1394_cfgrom_scan_phase1
* old link on, new link off => handled in s1394_process_old_tree
*/
if (LINK_ACTIVE(from) == B_FALSE) {
/*
* if last time around, link was off, there wouldn't
* have been config rom allocated.
*/
ASSERT(from->cfgrom == NULL);
TNF_PROBE_0_DEBUG(s1394_copy_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return;
} else {
s1394_selfid_pkt_t *selfid_pkt = to->selfid_packet;
if (IEEE1394_SELFID_ISLINKON(selfid_pkt))
SET_LINK_ACTIVE(to);
}
TNF_PROBE_0_DEBUG(s1394_copy_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
}
/*
* s1394_read_bus_info_blk()
* Attempts to kick off reading IEEE1212_NODE_CAP_QUAD quad or quad 0.
* Increments cfgroms_being_read by 1. Returns DDI_SUCCESS command was
* issued, else sets status to the failure reason and returns DDI_FAILURE.
*/
static int
s1394_read_bus_info_blk(s1394_hal_t *hal, s1394_node_t *node,
s1394_status_t *status)
{
uint32_t quadlet;
cmd1394_cmd_t *cmd;
uchar_t node_num;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
ASSERT(LINK_ACTIVE(node) == B_TRUE);
node_num = node->node_num;
TNF_PROBE_2_DEBUG(s1394_read_bus_info_blk_enter,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_uint, hal_gen,
hal->generation_count, tnf_int, node_num, node_num);
/*
* drop the topology lock around command allocation. Return failure
* if either command allocation fails or cannot reacquire the lock
*/
s1394_unlock_tree(hal);
*status = S1394_NOSTATUS;
if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
TNF_PROBE_1(s1394_read_bus_info_blk, S1394_TNF_SL_HOTPLUG_ERROR,
"", tnf_string, msg, "command allocation failed");
*status |= S1394_CMD_ALLOC_FAILED;
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
*status |= S1394_LOCK_FAILED;
TNF_PROBE_1(s1394_read_bus_info_blk, S1394_TNF_SL_HOTPLUG_ERROR,
"", tnf_string, msg, "unable to relock the tree");
/* free the cmd allocated above */
if (((*status) & S1394_CMD_ALLOC_FAILED) != 0)
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
}
if (((*status) & (S1394_CMD_ALLOC_FAILED | S1394_LOCK_FAILED)) != 0) {
TNF_PROBE_0_DEBUG(s1394_read_bus_info_blk_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
/* allocate cfgrom if needed */
if (node->cfgrom == NULL && s1394_alloc_cfgrom(hal, node, status) !=
DDI_SUCCESS) {
ASSERT(((*status) & S1394_LOCK_FAILED) != 0);
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_1(s1394_read_bus_info_blk, S1394_TNF_SL_HOTPLUG_ERROR,
"", tnf_string, msg, "config rom allocation failed");
TNF_PROBE_0_DEBUG(s1394_read_bus_info_blk_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
/*
* if this is a matched node, read quad 2 (node capabilities) to
* see if the generation count changed.
*/
quadlet = CFGROM_BIB_READ(node) ? IEEE1212_NODE_CAP_QUAD : 0;
/*
* read bus info block at 100Mbit. This will help us with the cases
* where LINK is slower than PHY; s1394 uses PHY speed till speed map
* is updated.
*/
cmd->completion_callback = s1394_cfgrom_read_callback;
cmd->bus_generation = hal->generation_count;
cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
CMD1394_OVERRIDE_ADDR | CMD1394_OVERRIDE_SPEED);
cmd->cmd_speed = IEEE1394_S100;
cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD;
QUAD_TO_CFGROM_ADDR(IEEE1394_LOCAL_BUS, node_num,
quadlet, cmd->cmd_addr);
TNF_PROBE_3_DEBUG(s1394_read_bus_info_blk,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_uint, hal_gen,
hal->generation_count, tnf_int, node_num, node_num, tnf_uint,
quadlet, quadlet);
TNF_PROBE_5_DEBUG(s1394_read_bus_info_blk,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int,
node_num, node_num, tnf_int, parsed, CFGROM_PARSED(node), tnf_int,
matched, NODE_MATCHED(node), tnf_int, visited,
NODE_VISITED(node), tnf_int, generation_changed,
CFGROM_GEN_CHANGED(node));
SETUP_QUAD_READ(node, 1, quadlet, 1);
if (s1394_read_config_quadlet(hal, cmd, status) != DDI_SUCCESS) {
TNF_PROBE_1(s1394_read_bus_info_blk, S1394_TNF_SL_HOTPLUG_ERROR,
"", tnf_string, msg, "Unable to start read");
/* free the command if it wasn't handed over to the HAL */
if (((*status) & S1394_CMD_INFLIGHT) == 0) {
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
}
if (((*status) & S1394_LOCK_FAILED) != 0) {
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
}
TNF_PROBE_0_DEBUG(s1394_read_bus_info_blk_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
hal->cfgroms_being_read++;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
TNF_PROBE_1_DEBUG(s1394_read_bus_info_blk_exit,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int, cfgrom_read_cnt,
hal->cfgroms_being_read);
return (DDI_SUCCESS);
}
/*
* s1394_read_rest_of_cfgrom()
* Attempts to start reading node->cfgrom_quad_to_read quadlet. Increments
* cfgroms_being_read by 1 and returns DDI_SUCCESS if command was issued,
* else sets status to the failure reason and returns DDI_FAILURE.
*/
int
s1394_read_rest_of_cfgrom(s1394_hal_t *hal, s1394_node_t *node,
s1394_status_t *status)
{
cmd1394_cmd_t *cmd;
uchar_t node_num = node->node_num;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
ASSERT(LINK_ACTIVE(node) == B_TRUE);
TNF_PROBE_2_DEBUG(s1394_read_rest_of_cfgrom_enter,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_uint, hal_gen,
hal->generation_count, tnf_int, node_num, node_num);
/*
* drop the topology lock around command allocation. Return failure
* if either command allocation fails or cannot reacquire the lock
*/
s1394_unlock_tree(hal);
*status = S1394_NOSTATUS;
if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
*status |= S1394_CMD_ALLOC_FAILED;
TNF_PROBE_1(s1394_read_rest_of_cfgrom,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"command allocation failed");
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
*status |= S1394_LOCK_FAILED;
/* free if we allocated a cmd above */
if (((*status) & S1394_CMD_ALLOC_FAILED) == 0)
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
TNF_PROBE_1(s1394_read_rest_of_cfgrom,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"unable to relock the tree");
}
if (((*status) & (S1394_CMD_ALLOC_FAILED | S1394_LOCK_FAILED)) != 0) {
TNF_PROBE_0_DEBUG(s1394_read_rest_of_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
cmd->completion_callback = s1394_cfgrom_read_callback;
cmd->bus_generation = hal->generation_count;
cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
CMD1394_OVERRIDE_ADDR);
cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD;
TNF_PROBE_2_DEBUG(s1394_read_rest_of_cfgrom, S1394_TNF_SL_HOTPLUG_STACK,
"", tnf_uint, hal_gen, hal->generation_count, tnf_int, node_num,
node->node_num);
QUAD_TO_CFGROM_ADDR(IEEE1394_LOCAL_BUS, node_num,
node->cfgrom_quad_to_read, cmd->cmd_addr);
SETUP_QUAD_READ(node, 1, node->cfgrom_quad_to_read, 1);
if (s1394_read_config_quadlet(hal, cmd, status) != DDI_SUCCESS) {
TNF_PROBE_1(s1394_read_rest_of_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"unable to start read");
/* free the command if it wasn't handed over to the HAL */
if (((*status) & S1394_CMD_INFLIGHT) == 0) {
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
}
if (((*status) & S1394_LOCK_FAILED) != 0) {
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
}
TNF_PROBE_0_DEBUG(s1394_read_rest_of_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
hal->cfgroms_being_read++;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
TNF_PROBE_1_DEBUG(s1394_read_rest_of_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int, cfgrom_read_cnt,
hal->cfgroms_being_read);
return (DDI_SUCCESS);
}
/*
* s1394_cfgrom_scan_phase1()
* Attempts to read bus info blocks for nodes as needed. Returns DDI_FAILURE
* if bus reset generations changed (as indicated by s1394_lock_tree()
* return status) or if any of the callees return failure, else returns
* DDI_SUCCESS.
*/
static int
s1394_cfgrom_scan_phase1(s1394_hal_t *hal)
{
uint32_t number_of_nodes;
int ret;
int node;
int wait_in_gen;
int wait_for_cbs;
uint_t hal_node_num;
uint_t hal_node_num_old;
s1394_node_t *nnode, *onode;
s1394_selfid_pkt_t *selfid_pkt;
s1394_status_t status;
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase1_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase1_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
wait_for_cbs = 0;
number_of_nodes = hal->number_of_nodes;
hal->cfgroms_being_read = 0;
hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
hal_node_num_old = IEEE1394_NODE_NUM(hal->old_node_id);
s1394_unlock_tree(hal);
ret = DDI_SUCCESS;
/* Send requests for all new node config ROM 0 */
for (node = 0; node < number_of_nodes; node++) {
status = S1394_UNKNOWN;
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
status = S1394_LOCK_FAILED;
break;
}
nnode = &hal->topology_tree[node];
onode = nnode->old_node;
/* if node matched, onode should be non NULL */
ASSERT(NODE_MATCHED(nnode) == B_FALSE || (NODE_MATCHED(nnode) ==
B_TRUE && onode != NULL));
/*
* Read bus info block if it is a brand new node (MATCHED is 0)
* or if matched but link was off in previous generations or
* or if matched but had invalid cfgrom in last generation
* or if matched but config rom generation > 1 (this is to
* check if config rom generation changed between bus resets).
*/
if ((node != hal_node_num) &&
((NODE_MATCHED(nnode) == B_FALSE) ||
(NODE_MATCHED(nnode) == B_TRUE && LINK_ACTIVE(onode) ==
B_FALSE) || (NODE_MATCHED(nnode) == B_TRUE &&
(onode->cfgrom == NULL || CFGROM_VALID(onode) ==
B_FALSE)) || (NODE_MATCHED(nnode) == B_TRUE &&
nnode->cfgrom != NULL && CONFIG_ROM_GEN(nnode->cfgrom) >
1))) {
SET_NODE_VISITED(nnode);
selfid_pkt = nnode->selfid_packet;
if (IEEE1394_SELFID_ISLINKON(selfid_pkt)) {
SET_LINK_ACTIVE(nnode);
status = S1394_UNKNOWN;
if (s1394_read_bus_info_blk(hal, nnode,
&status) != DDI_SUCCESS) {
if ((status & S1394_LOCK_FAILED) != 0)
break;
} else {
wait_for_cbs++;
wait_in_gen = hal->br_cfgrom_read_gen;
}
} else {
/*
* Special case: if link was active last
* time around, this should be treated as
* node going away.
*/
CLEAR_LINK_ACTIVE(nnode);
if (NODE_MATCHED(nnode) == B_TRUE &&
LINK_ACTIVE(onode) == B_TRUE) {
CLEAR_CFGROM_STATE(nnode);
TNF_PROBE_3(s1394_cfgrom_scan_phase1,
S1394_TNF_SL_HOTPLUG_ERROR,
"", tnf_string, msg,
"link lost power", tnf_int, node,
node, tnf_int, onode,
onode->node_num);
}
}
} else {
if (node == hal_node_num) {
onode = &hal->old_tree[hal_node_num_old];
/* Set up the local matched nodes */
if (onode) {
nnode->old_node = onode;
SET_NODE_MATCHED(nnode);
SET_NODE_MATCHED(onode);
s1394_copy_cfgrom(nnode, onode);
}
}
}
s1394_unlock_tree(hal);
}
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
if ((status & S1394_LOCK_FAILED) != 0) {
TNF_PROBE_1(s1394_cfrom_scan_phase1_exit,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_string, msg, "Generations changed");
return (DDI_FAILURE);
}
/*
* If we started any reads, wait for completion callbacks
*/
if (wait_for_cbs != 0) {
ret = s1394_wait_for_cfgrom_callbacks(hal, wait_in_gen,
s1394_br_thread_handle_cmd_phase1);
}
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_0_DEBUG(s1394_cfrom_scan_phase1_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (ret);
}
/*
* s1394_br_thread_handle_cmd_phase1()
* Process the cmd completion for phase 1 config rom reads. If we
* successfully read IEEE1212_NODE_CAP_QUAD quadlet and config rom gen
* did not change, move targets hanging off the old node to the current
* node. If config rom generations change, alloc new config rom and start
* re-reading the new config rom. If all of bus info block is read (as
* required), mark the node as CFGROM_BIB_READ. If config rom read fails
* retry if not too many failures. Topology tree mutex is dropped and
* reacquired in this routine. If reacquiring fails, returns
* S1394_HCMD_LOCK_FAILED. If the entire bus info block is read, returns
* S1394_HCMD_NODE_DONE, else returns S1394_HCMD_NODE_EXPECT_MORE (to
* indicate not done with the node yet).
*
* If we cannot read any of the quadlets in the bus info block, cfgrom
* is marked invalid in this generation (a side effect of calling
* s1394_free_cfgrom()). We free cfgrom in this routine only if the failure
* is not due to bus generations changing.
*/
static hcmd_ret_t
s1394_br_thread_handle_cmd_phase1(s1394_hal_t *hal, cmd1394_cmd_t *cmd)
{
s1394_target_t *t;
s1394_node_t *node, *onode;
uint32_t node_num, quadlet, data;
int freecmd, done, locked;
hcmd_ret_t cmdret;
uchar_t readdelay;
s1394_status_t status;
s1394_get_quad_info(cmd, &node_num, &quadlet, &data);
ASSERT(quadlet == 0 || quadlet < IEEE1394_BIB_QUAD_SZ);
TNF_PROBE_0_DEBUG(s1394_br_thread_handle_cmd_phase1_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
cmdret = S1394_HCMD_NODE_EXPECT_MORE;
locked = 1;
freecmd = 1;
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
TNF_PROBE_1(s1394_br_thread_handle_cmd_phase1,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"unable to lock tree");
locked = 0;
goto bail;
}
node = &hal->topology_tree[node_num];
if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
int reread = 0;
done = 0;
if (quadlet == IEEE1212_NODE_CAP_QUAD &&
CFGROM_BIB_READ(node)) {
int cur_gen = ((data & IEEE1394_BIB_GEN_MASK) >>
IEEE1394_BIB_GEN_SHIFT);
/*
* node->old_node can be NULL if this is a new node &
* we are doing a rescan
*/
onode = node->old_node;
if (CONFIG_ROM_GEN(node->cfgrom) == cur_gen) {
if (CFGROM_PARSED(node) == B_TRUE) {
rw_enter(&hal->target_list_rwlock,
RW_WRITER);
/* Update the target list, if any */
if (onode != NULL &&
(t = onode->target_list) != NULL) {
node->target_list = t;
while (t != NULL) {
t->on_node = node;
t = t->target_sibling;
}
}
rw_exit(&hal->target_list_rwlock);
}
SET_NODE_MATCHED(node);
if (onode)
SET_NODE_MATCHED(onode);
node->cfgrom_quad_to_read =
IEEE1394_BIB_QUAD_SZ;
done++;
} else {
TNF_PROBE_4(s1394_br_thread_handle_cmd_phase1,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string,
msg, "config rom generation changed",
tnf_int, node_num, node_num,
tnf_int, cur_gen, cur_gen, tnf_int, old_gen,
CONFIG_ROM_GEN(node->cfgrom));
SET_CFGROM_GEN_CHANGED(node);
if (onode != NULL)
SET_CFGROM_GEN_CHANGED(onode);
/*
* Reset BIB_READ flag and start reading entire
* config rom.
*/
CLEAR_CFGROM_BIB_READ(node);
reread = 1;
/*
* if generations changed, allocate cfgrom for
* the new generation. s1394_match_GUID() will
* free up the cfgrom from the old generation.
*/
if (s1394_alloc_cfgrom(hal, node, &status) !=
DDI_SUCCESS) {
ASSERT((status & S1394_LOCK_FAILED) !=
0);
ASSERT(MUTEX_NOT_HELD(&hal->
topology_tree_mutex));
locked = 0;
/* we failed to relock the tree */
goto bail;
}
}
}
/*
* we end up here if we don't have bus_info_blk for this
* node or if config rom generation changed.
*/
/*
* Pass1 Rio bug workaround. Due to this bug, if we read
* past quadlet 5 of the config rom, the PCI bus gets wedged.
* Avoid the hang by not reading past quadlet 5.
* We identify a remote Rio by the node vendor id part of
* quad 3 (which is == SUNW == S1394_SUNW_OUI (0x80020)).
*/
if (s1394_enable_rio_pass1_workarounds != 0) {
if ((quadlet == 3) && ((data >> 8) == S1394_SUNW_OUI)) {
node->cfgrom_size = IEEE1394_BIB_QUAD_SZ;
node->cfgrom_valid_size = IEEE1394_BIB_QUAD_SZ;
}
}
if (!done) {
if (reread)
quadlet = 0;
else
node->cfgrom[quadlet++] = data;
/* if we don't have the entire bus_info_blk... */
if (quadlet < IEEE1394_BIB_QUAD_SZ) {
CFGROM_GET_READ_DELAY(node, readdelay);
SETUP_QUAD_READ(node, 1, quadlet, 1);
s1394_unlock_tree(hal);
CFGROM_READ_PAUSE(readdelay);
/* get next quadlet */
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
TNF_PROBE_3(
s1394_br_thread_handle_cmd_phase1,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_string, msg,
"unable to relock tree", tnf_uint,
node_num, node_num, tnf_int,
quad_to_read, quadlet);
locked = 0;
} else if (s1394_read_config_quadlet(hal, cmd,
&status) != DDI_SUCCESS) {
/*
* Failed to get going. If command was
* successfully handed over to the HAL,
* don't free it (it will get freed
* later in the callback).
*/
TNF_PROBE_3(
s1394_br_thread_handle_cmd_phase1,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_string, msg,
"unable to read", tnf_uint,
node_num, node_num, tnf_int,
quad_to_read, quadlet);
if ((status & S1394_CMD_INFLIGHT) !=
0) {
freecmd = 0;
}
if ((status & S1394_LOCK_FAILED) != 0) {
locked = 0;
} else {
if (CFGROM_NEW_ALLOC(node) ==
B_TRUE) {
s1394_free_cfgrom(hal,
node,
S1394_FREE_CFGROM_NEW);
} else {
CLEAR_CFGROM_STATE(
node);
}
}
done++;
} else {
freecmd = 0;
}
} else {
/* got all of bus_info_blk */
SET_CFGROM_BIB_READ(node);
if (node->cfgrom_size == IEEE1394_BIB_QUAD_SZ)
SET_CFGROM_ALL_READ(node);
node->cfgrom_quad_to_read = quadlet;
done++;
TNF_PROBE_3_DEBUG(
s1394_br_thread_handle_cmd_phase1,
S1394_TNF_SL_HOTPLUG_STACK,
"", tnf_string, msg, "read bus info blk",
tnf_int, node_num, node->node_num,
tnf_opaque, cfgrom, node->cfgrom);
}
}
} else {
done = 1;
node->cfgrom_read_fails++;
BUMP_CFGROM_READ_DELAY(node);
/* retry if not too many failures */
if (node->cfgrom_read_fails < s1394_cfgrom_read_retry_cnt) {
CFGROM_GET_READ_DELAY(node, readdelay);
SETUP_QUAD_READ(node, 0, quadlet, 1);
s1394_unlock_tree(hal);
CFGROM_READ_PAUSE(readdelay);
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
TNF_PROBE_3(
s1394_br_thread_handle_cmd_phase1,
S1394_TNF_SL_HOTPLUG_ERROR, "",
tnf_string, msg,
"unable to relock tree", tnf_uint,
node_num, node_num, tnf_int,
quad_to_read, quadlet);
locked = 0;
} else if (s1394_read_config_quadlet(hal, cmd,
&status) != DDI_SUCCESS) {
/*
* Failed to get going. If command was
* successfully handed over to the HAL,
* don't free it (it will get freed
* later in the callback).
*/
TNF_PROBE_3(
s1394_br_thread_handle_cmd_phase1,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string,
msg, "unable to re-read", tnf_uint,
node_num, node_num, tnf_int, quad_to_read,
quadlet);
if ((status & S1394_CMD_INFLIGHT) != 0) {
freecmd = 0;
}
if ((status & S1394_LOCK_FAILED) != 0) {
locked = 0;
} else {
if (CFGROM_NEW_ALLOC(node) == B_TRUE) {
s1394_free_cfgrom(hal, node,
S1394_FREE_CFGROM_NEW);
} else {
CLEAR_CFGROM_STATE(node);
}
}
} else {
done = 0;
freecmd = 0;
}
} else {
TNF_PROBE_4(s1394_br_thread_handle_cmd_phase1,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"retries exceeded", tnf_int, node_num, node_num,
tnf_int, quadlet, quadlet, tnf_opaque, cfgrom,
node->cfgrom);
if (CFGROM_NEW_ALLOC(node) == B_TRUE) {
s1394_free_cfgrom(hal, node,
S1394_FREE_CFGROM_NEW);
} else {
CLEAR_CFGROM_STATE(node);
}
}
}
bail:
if (freecmd) {
(void) s1394_free_cmd(hal, &cmd);
}
if (done) {
cmdret = S1394_HCMD_NODE_DONE;
TNF_PROBE_2_DEBUG(s1394_br_thread_handle_cmd_phase1,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"done with node", tnf_int, node_num, node_num);
}
/* if we are bailing out because locking failed, locked == 0 */
if (locked == 0)
cmdret = S1394_HCMD_LOCK_FAILED;
else
s1394_unlock_tree(hal);
TNF_PROBE_0_DEBUG(s1394_br_thread_handle_cmd_phase1_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (cmdret);
}
/*
* s1394_cfgrom_scan_phase2()
* Handles phase 2 of bus reset processing. Matches GUIDs between old
* and new topology trees to identify which node moved where. Processes
* the old topology tree (involves offlining any nodes that got unplugged
* between the last generation and the current generation). Updates speed
* map, sets up physical AR request filer and does isoch resource
* realloc failure notification and bus reset notifications. Then resends
* any commands that were issued by targets while the reset was being
* processed. Finally, the current topology tree is processed. This involves
* reading config rom past the bus info block for new nodes and parsing
* the config rom, creating a devinfo for each unit directory found in the
* config rom.
* Returns DDI_FAILURE if there was bus reset during any of the function
* calls (as indicated by lock failures) or if any of the routines callees
* return failure, else returns DDI_SUCCESS.
*/
static int
s1394_cfgrom_scan_phase2(s1394_hal_t *hal)
{
int ret;
uint_t wait_gen;
int wait_for_cbs = 0;
t1394_localinfo_t localinfo;
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase2_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase2_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
if (s1394_match_all_GUIDs(hal) == DDI_SUCCESS) {
s1394_unlock_tree(hal);
}
if (s1394_process_old_tree(hal) != DDI_SUCCESS) {
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_1(s1394_cfgrom_scan_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"non-success return from process_old_tree");
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase2_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
TNF_PROBE_1(s1394_cfgrom_scan_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"unable to relock the tree");
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase2_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
s1394_update_speed_map_link_speeds(hal);
s1394_unlock_tree(hal);
/* Setup physical AR request filters */
s1394_physical_arreq_setup_all(hal);
/* Notify targets of isoch resource realloc failures */
s1394_isoch_rsrc_realloc_notify(hal);
/* Notify targets of the end of bus reset processing */
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
TNF_PROBE_1(s1394_cfgrom_scan_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"unable to relock the tree after isoch notify");
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase2_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
localinfo.bus_generation = hal->generation_count;
localinfo.local_nodeID = hal->node_id;
s1394_unlock_tree(hal);
s1394_target_bus_reset_notifies(hal, &localinfo);
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
TNF_PROBE_1(s1394_cfgrom_scan_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"unable to relock the tree after reset notify");
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase2_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
/* Set HAL state to normal */
if (hal->disable_requests_bit == 0)
hal->hal_state = S1394_HAL_NORMAL;
else
hal->hal_state = S1394_HAL_DREQ;
s1394_unlock_tree(hal);
/* Flush the pending Q */
s1394_resend_pending_cmds(hal);
if (s1394_process_topology_tree(hal, &wait_for_cbs, &wait_gen)) {
TNF_PROBE_1(s1394_cfgrom_scan_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"non-success return from process_topology_tree");
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase2_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
return (DDI_FAILURE);
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
TNF_PROBE_1(s1394_cfgrom_scan_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"unable to relock after processing topology tree");
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase2_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
s1394_print_node_info(hal);
s1394_unlock_tree(hal);
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
ret = DDI_SUCCESS;
/*
* If we started any reads, wait for completion callbacks
*/
if (wait_for_cbs != 0) {
ret = s1394_wait_for_cfgrom_callbacks(hal, wait_gen,
s1394_br_thread_handle_cmd_phase2);
TNF_PROBE_2_DEBUG(s1394_cfgrom_scan_phase2,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"returned from waiting for cfgrom callbacks", tnf_int, ret,
ret);
}
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_0_DEBUG(s1394_cfgrom_scan_phase2_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (ret);
}
/*
* s1394_br_thread_handle_cmd_phase2()
* Process the cmd completion for phase 2 config rom reads. If all the
* needed quads are read, validates the config rom; if config rom is
* invalid (crc failures), frees the config rom, else marks the config rom
* valid and calls s1394_update_devinfo_tree() to parse the config rom.
* If need to get more quadlets, attempts to kick off the read and returns
* S1394_HCMD_NODE_EXPECT_MORE if successfully started the read. If a bus
* reset is seen while in this routine, returns S1394_HCMD_LOCK_FAILED. If
* done with the node (with or withoug crc errors), returns
* S1394_HCMD_NODE_DONE, else returns S1394_HCMD_NODE_EXPECT_MORE (to
* indicate not done with the node yet).
*/
static hcmd_ret_t
s1394_br_thread_handle_cmd_phase2(s1394_hal_t *hal, cmd1394_cmd_t *cmd)
{
s1394_node_t *node;
uint32_t node_num, quadlet, data;
int update_devinfo, locked, freecmd, done;
hcmd_ret_t cmdret;
uchar_t readdelay;
s1394_status_t status;
TNF_PROBE_0_DEBUG(s1394_br_thread_handle_cmd_phase2_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
/*
* we end up here if this is a brand new node or if it is a known node
* but the config ROM changed (and triggered a re-read).
*/
s1394_get_quad_info(cmd, &node_num, &quadlet, &data);
ASSERT(quadlet == IEEE1394_BIB_QUAD_SZ || quadlet <
IEEE1394_CONFIG_ROM_QUAD_SZ);
locked = freecmd = done = 1;
cmdret = S1394_HCMD_NODE_EXPECT_MORE;
update_devinfo = 0;
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
TNF_PROBE_3(s1394_br_thread_handle_cmd_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"unable to lock tree", tnf_int, node_num, node_num,
tnf_int, quadlet, quadlet);
locked = 0;
goto bail;
}
node = &hal->topology_tree[node_num];
if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
ASSERT(CFGROM_BIB_READ(node) == B_TRUE);
node->cfgrom[quadlet] = data;
if (s1394_calc_next_quad(hal, node, quadlet, &quadlet) != 0) {
/*
* Done with this node. Mark config rom valid and
* update the devinfo tree for this node.
*/
TNF_PROBE_4_DEBUG(s1394_br_thread_handle_cmd_phase2,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"all read", tnf_int, node_num, node->node_num,
tnf_opaque, cfgrom, node->cfgrom, tnf_int, quadlet,
quadlet);
node->cfgrom_valid_size = quadlet + 1;
if (s1394_valid_cfgrom(hal, node) == B_TRUE) {
SET_CFGROM_ALL_READ(node);
update_devinfo++;
} else {
s1394_free_cfgrom(hal, node,
S1394_FREE_CFGROM_BOTH);
}
} else {
CFGROM_GET_READ_DELAY(node, readdelay);
SETUP_QUAD_READ(node, 1, quadlet, 1);
s1394_unlock_tree(hal);
CFGROM_READ_PAUSE(readdelay);
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
locked = 0;
TNF_PROBE_3(s1394_br_thread_handle_cmd_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string,
msg, "unable to relock the tree",
tnf_int, node_num, node->node_num,
tnf_int, quadlet, quadlet);
} else if (s1394_read_config_quadlet(hal, cmd,
&status) != DDI_SUCCESS) {
/* give up on this guy */
TNF_PROBE_3(s1394_br_thread_handle_cmd_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string,
msg, "cannot start quadlet read", tnf_int,
node_num, node_num, tnf_int, quadlet,
quadlet);
if ((status & S1394_CMD_INFLIGHT) != 0) {
freecmd = 0;
}
if ((status & S1394_LOCK_FAILED) != 0) {
locked = 0;
} else {
node->cfgrom_valid_size = quadlet;
if (s1394_valid_cfgrom(hal, node) ==
B_TRUE) {
SET_CFGROM_ALL_READ(node);
update_devinfo++;
} else {
s1394_free_cfgrom(hal, node,
S1394_FREE_CFGROM_BOTH);
}
}
} else {
/* successfully started next read */
done = 0;
freecmd = 0;
}
}
} else {
node->cfgrom_read_fails++;
BUMP_CFGROM_READ_DELAY(node);
/* retry if not too many failures */
if (node->cfgrom_read_fails < s1394_cfgrom_read_retry_cnt) {
CFGROM_GET_READ_DELAY(node, readdelay);
s1394_unlock_tree(hal);
SETUP_QUAD_READ(node, 0, quadlet, 1);
CFGROM_READ_PAUSE(readdelay);
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
locked = 0;
TNF_PROBE_3(s1394_br_thread_handle_cmd_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string,
msg, "unable to relock for reread",
tnf_int, node_num, node->node_num,
tnf_int, quadlet, quadlet);
} else if (s1394_read_config_quadlet(hal, cmd,
&status) != DDI_SUCCESS) {
if ((status & S1394_CMD_INFLIGHT) != 0) {
freecmd = 0;
}
if ((status & S1394_LOCK_FAILED) != 0) {
locked = 0;
} else {
/* stop further reads */
TNF_PROBE_4(
s1394_br_thread_handle_cmd_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "",
tnf_string, msg, "unable to retry",
tnf_int, node_num, node->node_num,
tnf_int, quadlet, quadlet,
tnf_opaque, cfgrom, node->cfgrom);
node->cfgrom_valid_size = quadlet + 1;
if (s1394_valid_cfgrom(hal, node) ==
B_TRUE) {
SET_CFGROM_ALL_READ(node);
update_devinfo++;
} else {
s1394_free_cfgrom(hal, node,
S1394_FREE_CFGROM_BOTH);
}
}
} else {
/* successfully started next read */
done = 0;
freecmd = 0;
}
} else {
TNF_PROBE_4(s1394_br_thread_handle_cmd_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"retries exceeded", tnf_int, node_num, node_num,
tnf_int, quadlet, quadlet, tnf_opaque, cfgrom,
node->cfgrom);
node->cfgrom_valid_size = quadlet + 1;
if (s1394_valid_cfgrom(hal, node) == B_TRUE) {
SET_CFGROM_ALL_READ(node);
update_devinfo++;
} else {
s1394_free_cfgrom(hal, node,
S1394_FREE_CFGROM_BOTH);
}
}
}
bail:
if (freecmd) {
(void) s1394_free_cmd(hal, &cmd);
}
if (done) {
cmdret = S1394_HCMD_NODE_DONE;
TNF_PROBE_2_DEBUG(s1394_br_thread_handle_cmd_phase2,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"done with a node", tnf_int, node_num, node_num);
}
if (update_devinfo) {
ASSERT(locked);
/*
* s1394_update_devinfo_tree() drops and reacquires the
* topology_tree_mutex. If tree lock fails, it returns
* a DDI_FAILURE. Set locked to 0 so in this case so that
* we will return S1394_HCMD_LOCK_FAILED below
*/
if (s1394_update_devinfo_tree(hal, node) != DDI_SUCCESS) {
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
locked = 0;
TNF_PROBE_2(s1394_br_thread_handle_cmd_phase2,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"update devinfo returned failure", tnf_int,
node_num, node_num);
}
}
/* if we are bailing out because locking failed, locked == 0 */
if (locked == 0)
cmdret = S1394_HCMD_LOCK_FAILED;
else
s1394_unlock_tree(hal);
TNF_PROBE_1_DEBUG(s1394_br_thread_handle_cmd_phase2_exit,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int, cmdret, (int)cmdret);
return (cmdret);
}
/*
* s1394_read_config_quadlet()
* Starts the reads of a config quadlet (deduced cmd_addr). Returns
* DDI_SUCCESS if the read was started with no errors, else DDI_FAILURE
* is returned, with status indicating the reason for the failure(s).
*/
static int
s1394_read_config_quadlet(s1394_hal_t *hal, cmd1394_cmd_t *cmd,
s1394_status_t *status)
{
s1394_node_t *node;
int ret, err, node_num, quadlet;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
node_num = IEEE1394_ADDR_PHY_ID(cmd->cmd_addr);
node = &hal->topology_tree[node_num];
quadlet = node->cfgrom_quad_to_read;
TNF_PROBE_2_DEBUG(s1394_read_config_quadlet_enter,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_uint, node_num, node_num,
tnf_uint, quadlet, quadlet);
/* Calculate the 64-bit address */
QUAD_TO_CFGROM_ADDR(IEEE1394_LOCAL_BUS, node_num, quadlet,
cmd->cmd_addr);
*status = S1394_NOSTATUS;
ret = s1394_setup_asynch_command(hal, NULL, cmd, S1394_CMD_READ, &err);
if (ret != DDI_SUCCESS) {
*status |= S1394_UNKNOWN;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
TNF_PROBE_3(s1394_read_config_quadlet,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"failure from setup asynch command", tnf_uint, node_num,
node_num, tnf_uint, quadlet, quadlet);
TNF_PROBE_0_DEBUG(s1394_read_config_quadlet_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_FAILURE);
}
s1394_unlock_tree(hal);
ret = DDI_SUCCESS;
/* Send the command out */
if (s1394_xfer_asynch_command(hal, cmd, &err) == DDI_SUCCESS) {
/* Callers can expect a callback now */
*status |= S1394_CMD_INFLIGHT;
} else {
s1394_cmd_priv_t *s_priv;
TNF_PROBE_3(s1394_read_config_quadlet,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"failure from xfer asynch command",
tnf_int, quadlet, quadlet, tnf_int, node_num, node_num);
/* Remove from queue */
s1394_remove_q_asynch_cmd(hal, cmd);
s_priv = S1394_GET_CMD_PRIV(cmd);
s_priv->cmd_in_use = B_FALSE;
*status |= S1394_XFER_FAILED;
ret = DDI_FAILURE;
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
*status |= S1394_LOCK_FAILED;
ret = DDI_FAILURE;
TNF_PROBE_1(s1394_read_config_quadlet,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"unable to relock the tree");
}
TNF_PROBE_0_DEBUG(s1394_read_config_quadlet_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (ret);
}
/*
* s1394_cfgrom_read_callback()
* callback routine for config rom reads. Frees the command if it failed
* due to bus reset else appends the command to the completion queue
* and signals the completion queue cv.
*/
static void
s1394_cfgrom_read_callback(cmd1394_cmd_t *cmd)
{
cmd1394_cmd_t *tcmd;
s1394_cmd_priv_t *s_priv;
s1394_hal_t *hal;
#if defined(DEBUG)
uint32_t node_num, quadlet, data;
#endif
TNF_PROBE_0_DEBUG(s1394_cfgrom_read_callback_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
hal = (s1394_hal_t *)s_priv->sent_on_hal;
#if defined(DEBUG)
s1394_get_quad_info(cmd, &node_num, &quadlet, &data);
TNF_PROBE_5_DEBUG(s1394_cfgrom_read_callback,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_uint, gen, cmd->bus_generation,
tnf_int, quadlet, quadlet,
tnf_int, node_num, node_num,
tnf_int, data, data, tnf_int, result, cmd->cmd_result);
#endif
if (cmd->cmd_result == CMD1394_EBUSRESET) {
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
} else {
mutex_enter(&hal->br_cmplq_mutex);
/* Put the command on completion queue */
s_priv->cmd_priv_next = NULL;
if ((tcmd = hal->br_cmplq_tail) != NULL) {
s_priv = S1394_GET_CMD_PRIV(tcmd);
s_priv->cmd_priv_next = cmd;
}
hal->br_cmplq_tail = cmd;
if (hal->br_cmplq_head == NULL)
hal->br_cmplq_head = cmd;
cv_signal(&hal->br_cmplq_cv);
mutex_exit(&hal->br_cmplq_mutex);
}
TNF_PROBE_0_DEBUG(s1394_cfgrom_read_callback_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
}
/*
* s1394_cfgrom_parse_unit_dir()
* Parses the unit directory passed in and returns reg[2...5] of reg
* property (see 1275 binding for reg property defintion). Currently,
* returns 0 for all the values since none of the existing devices implement
* this and future devices, per P1212r, need a binding change.
*/
/* ARGSUSED */
void
s1394_cfgrom_parse_unit_dir(uint32_t *unit_dir, uint32_t *addr_hi,
uint32_t *addr_lo, uint32_t *size_hi, uint32_t *size_lo)
{
TNF_PROBE_0_DEBUG(s1394_cfgrom_parse_unit_dir_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
*addr_hi = *addr_lo = *size_hi = *size_lo = 0;
TNF_PROBE_0_DEBUG(s1394_cfgrom_parse_unit_dir_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
}
/*
* s1394_get_quad_info()
* Helper routine that picks apart the various fields of a 1394 address
*/
static void
s1394_get_quad_info(cmd1394_cmd_t *cmd, uint32_t *node_num, uint32_t *quadlet,
uint32_t *data)
{
uint64_t addr;
TNF_PROBE_0_DEBUG(s1394_get_quad_info_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
addr = cmd->cmd_addr;
*node_num = IEEE1394_ADDR_PHY_ID(addr);
*quadlet = ((addr & IEEE1394_ADDR_OFFSET_MASK) -
IEEE1394_CONFIG_ROM_ADDR);
*quadlet = (*quadlet >> 2);
*data = T1394_DATA32(cmd->cmd_u.q.quadlet_data);
TNF_PROBE_0_DEBUG(s1394_get_quad_info_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
}
/*
* s1394_match_GUID()
* attempts to match nnode (which is in the current topology tree) with
* a node in the old topology tree by comparing GUIDs. If a match is found
* the old_node field of the current node and cur_node field of the old
* are set point to each other. Also, this routine makes both the nodes
* point at the same config rom. If unable to relock the tree, returns
* DDI_FAILURE, else returns DDI_SUCCESS.
*/
static int
s1394_match_GUID(s1394_hal_t *hal, s1394_node_t *nnode)
{
int old_node;
int gen_changed;
uint32_t old_a, old_b;
uint32_t new_a, new_b;
s1394_node_t *onode;
s1394_target_t *t;
int ret = DDI_SUCCESS;
TNF_PROBE_0_DEBUG(s1394_match_GUID_enter, S1394_TNF_SL_HOTPLUG_STACK,
"");
ASSERT(nnode->cfgrom != NULL);
ASSERT(CFGROM_BIB_READ(nnode));
new_a = nnode->node_guid_hi;
new_b = nnode->node_guid_lo;
for (old_node = 0; old_node < hal->old_number_of_nodes; old_node++) {
onode = &hal->old_tree[old_node];
if (onode->cfgrom == NULL || CFGROM_BIB_READ(onode) == B_FALSE)
continue;
old_a = onode->node_guid_hi;
old_b = onode->node_guid_lo;
if ((old_a == new_a) && (old_b == new_b)) {
if (NODE_MATCHED(onode) == B_TRUE) {
TNF_PROBE_4(s1394_match_GUID_duplicate,
S1394_TNF_SL_HOTPLUG_ERROR, "",
tnf_uint, guid_hi, old_a,
tnf_uint, guid_lo, old_b,
tnf_uint, old_node_num, old_node,
tnf_uint, node_num, nnode->node_num);
cmn_err(CE_NOTE, "!Duplicate GUIDs: %08x%08x",
old_a, old_b);
/* offline the new node that last matched */
ret = s1394_offline_node(hal, onode->cur_node);
/* and make the current new node invalid */
ASSERT(CFGROM_NEW_ALLOC(nnode) == B_TRUE);
s1394_free_cfgrom(hal, nnode,
S1394_FREE_CFGROM_NEW);
break;
}
/*
* If there is indeed a cfgrom gen change,
* CFGROM_GEN_CHANGED() will be set iff we are matching
* tree nodes. Otherwise, CONFIG_ROM_GEN(old) !=
* CONFIG_ROM_GEN(new).
*/
if (CFGROM_GEN_CHANGED(nnode) == B_TRUE ||
(CONFIG_ROM_GEN(onode->cfgrom) !=
CONFIG_ROM_GEN(nnode->cfgrom))) {
gen_changed = 1;
TNF_PROBE_4_DEBUG(s1394_match_GUID_gen_change,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_opaque, old_cfgrom, onode->cfgrom,
tnf_int, old_gen,
CONFIG_ROM_GEN(onode->cfgrom), tnf_opaque,
cfgrom, nnode->cfgrom, tnf_int, new_gen,
CONFIG_ROM_GEN(nnode->cfgrom));
} else {
gen_changed = 0;
}
onode->cur_node = nnode;
nnode->old_node = onode;
nnode->node_state = onode->node_state;
SET_NODE_VISITED(onode);
SET_NODE_MATCHED(onode);
SET_NODE_MATCHED(nnode);
/*
* If generations changed, need to offline any targets
* hanging off the old node, prior to freeing up old
* cfgrom. If the generations didn't change, we can
* free up the new config rom and copy all info from
* the old node (this helps in picking up further
* reads from where the last generation left off).
*/
if (gen_changed == 1) {
if (s1394_offline_node(hal, onode)) {
ret = DDI_FAILURE;
break;
}
TNF_PROBE_2(s1394_match_GUID_gen_freecfg,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_opaque, old_cfgrom, onode->cfgrom,
tnf_opaque, new_cfgrom, nnode->cfgrom);
s1394_free_cfgrom(hal, onode,
S1394_FREE_CFGROM_OLD);
CLEAR_CFGROM_PARSED(nnode);
CLEAR_CFGROM_NEW_ALLOC(nnode);
CLEAR_CFGROM_NEW_ALLOC(onode);
onode->cfgrom = nnode->cfgrom;
/* done */
break;
}
/*
* Free up cfgrom memory in the new_node and
* point it at the same config rom as the old one.
*/
if (onode->cfgrom != nnode->cfgrom) {
TNF_PROBE_5_DEBUG(s1394_match_GUID,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_int, node_num, nnode->node_num,
tnf_opaque, cfgrom, nnode->cfgrom,
tnf_int, old_node_num, old_node,
tnf_opaque, old_cfgrom, onode->cfgrom,
tnf_uint, cfgrom_state,
nnode->cfgrom_state);
ASSERT(CFGROM_NEW_ALLOC(nnode) == B_TRUE);
s1394_free_cfgrom(hal, nnode,
S1394_FREE_CFGROM_NEW);
}
nnode->cfgrom = onode->cfgrom;
nnode->cfgrom_state = onode->cfgrom_state;
nnode->cfgrom_valid_size = onode->cfgrom_valid_size;
nnode->cfgrom_size = onode->cfgrom_size;
nnode->cfgrom_quad_to_read = onode->cfgrom_quad_to_read;
bcopy(onode->dir_stack, nnode->dir_stack,
offsetof(s1394_node_t, cfgrom_quad_to_read) -
offsetof(s1394_node_t, dir_stack));
CLEAR_CFGROM_NEW_ALLOC(nnode);
CLEAR_CFGROM_NEW_ALLOC(onode);
if (CFGROM_PARSED(nnode) == B_TRUE) {
rw_enter(&hal->target_list_rwlock, RW_WRITER);
/* Update the target list */
if ((t = onode->target_list) != NULL) {
nnode->target_list = t;
while (t != NULL) {
t->on_node = nnode;
t = t->target_sibling;
}
}
rw_exit(&hal->target_list_rwlock);
}
break;
}
}
TNF_PROBE_0_DEBUG(s1394_match_GUID_exit, S1394_TNF_SL_HOTPLUG_STACK,
"");
return (ret);
}
/*
* s1394_match_all_GUIDs()
* attempt to match each node in the current topology tree with the a
* node in the old topology tree. If unable to relock the tree, returns
* DDI_FAILURE, else returns DDI_SUCCESS.
*/
static int
s1394_match_all_GUIDs(s1394_hal_t *hal)
{
int node;
int ret = DDI_SUCCESS;
s1394_node_t *nnode;
TNF_PROBE_0_DEBUG(s1394_match_all_GUIDs_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
for (node = 0; node < hal->number_of_nodes; node++) {
nnode = &hal->topology_tree[node];
if (LINK_ACTIVE(nnode) == B_FALSE || CFGROM_BIB_READ(nnode) ==
B_FALSE)
continue;
if (NODE_MATCHED(nnode)) {
/*
* Skip if node matched. If config rom generations
* changed, we want to call s1394_match_GUID() even
* if the nodes matched.
*/
int gen_changed;
s1394_node_t *onode = nnode->old_node;
gen_changed = (onode && onode->cfgrom &&
CONFIG_ROM_GEN(onode->cfgrom) != CONFIG_ROM_GEN(
nnode->cfgrom)) ? 1 : 0;
if (CFGROM_GEN_CHANGED(nnode) == 0 && gen_changed == 0)
continue;
}
if (s1394_match_GUID(hal, nnode) == DDI_FAILURE) {
ret = DDI_FAILURE;
}
}
TNF_PROBE_0_DEBUG(s1394_match_all_GUIDs_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (ret);
}
/*
* s1394_valid_cfgrom()
* Performs crc check on the config rom. Returns B_TRUE if config rom has
* good CRC else returns B_FALSE.
*/
/* ARGSUSED */
boolean_t
s1394_valid_cfgrom(s1394_hal_t *hal, s1394_node_t *node)
{
uint32_t crc_len, crc_value, CRC, CRC_old, quad0;
TNF_PROBE_0_DEBUG(s1394_valid_cfgrom_enter, S1394_TNF_SL_HOTPLUG_STACK,
"");
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
ASSERT(node->cfgrom);
if (s1394_enable_crc_validation == 0) {
TNF_PROBE_1_DEBUG(s1394_valid_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"validation turned off");
return (B_TRUE);
}
quad0 = node->cfgrom[0];
crc_len = (quad0 >> IEEE1394_CFG_ROM_CRC_LEN_SHIFT) &
IEEE1394_CFG_ROM_CRC_LEN_MASK;
crc_value = quad0 & IEEE1394_CFG_ROM_CRC_VALUE_MASK;
if (node->cfgrom_valid_size < crc_len + 1) {
TNF_PROBE_4(s1394_valid_cfgrom_not_enough,
S1394_TNF_SL_HOTPLUG_ERROR, "",
tnf_uint, node_guid_hi, node->node_guid_hi,
tnf_uint, node_guid_lo, node->node_guid_lo,
tnf_uint, crc_len, crc_len,
tnf_uint, valid_size, node->cfgrom_valid_size);
TNF_PROBE_0_DEBUG(s1394_valid_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (B_FALSE);
}
CRC = s1394_CRC16(&node->cfgrom[1], crc_len);
if (CRC != crc_value) {
CRC_old = s1394_CRC16_old(&node->cfgrom[1], crc_len);
if (CRC_old == crc_value) {
TNF_PROBE_4_DEBUG(s1394_valid_cfgrom_busted_crc,
S1394_TNF_SL_HOTPLUG_ERROR, "",
tnf_uint, node_guid_hi, node->node_guid_hi,
tnf_uint, node_guid_lo, node->node_guid_lo,
tnf_uint, node_num, node->node_num,
tnf_uint, crc_len, crc_len);
TNF_PROBE_0_DEBUG(s1394_valid_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (B_TRUE);
}
cmn_err(CE_NOTE,
"!Bad CRC in config rom (node's GUID %08x%08x)",
node->node_guid_hi, node->node_guid_lo);
TNF_PROBE_5(s1394_valid_cfgrom_bad_crc,
S1394_TNF_SL_HOTPLUG_ERROR, "",
tnf_uint, node_guid_hi, node->node_guid_hi,
tnf_uint, node_guid_lo, node->node_guid_lo,
tnf_uint, crc_len, crc_len,
tnf_uint, crc, crc_value, tnf_uint, crc_computed, CRC);
TNF_PROBE_0_DEBUG(s1394_valid_cfgrom_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (B_FALSE);
}
TNF_PROBE_3_DEBUG(s1394_valid_cfgrom_exit, S1394_TNF_SL_HOTPLUG_STACK,
"", tnf_uint, node_num, node->node_num, tnf_uint, crc_len, crc_len,
tnf_uint, crc, crc_value);
return (B_TRUE);
}
/*
* s1394_valid_dir()
* Performs crc check on a directory. Returns B_TRUE if dir has good CRC
* else returns B_FALSE.
*/
/*ARGSUSED*/
boolean_t
s1394_valid_dir(s1394_hal_t *hal, s1394_node_t *node,
uint32_t key, uint32_t *dir)
{
uint32_t dir_len, crc_value, CRC, CRC_old, quad0;
TNF_PROBE_0_DEBUG(s1394_valid_dir_enter, S1394_TNF_SL_HOTPLUG_STACK,
"");
/*
* Ideally, we would like to do crc validations for the entire cfgrom
* as well as the individual directories. However, we have seen devices
* that have valid directories but busted cfgrom crc and devices that
* have bad crcs in directories as well as for the entire cfgrom. This
* is sad, but unfortunately, real world!
*/
if (s1394_enable_crc_validation == 0) {
TNF_PROBE_1_DEBUG(s1394_valid_dir_exit,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"validation turned off");
return (B_TRUE);
}
quad0 = dir[0];
dir_len = IEEE1212_DIR_LEN(quad0);
crc_value = IEEE1212_DIR_CRC(quad0);
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
CRC = s1394_CRC16(&dir[1], dir_len);
if (CRC != crc_value) {
CRC_old = s1394_CRC16_old(&dir[1], dir_len);
if (CRC_old == crc_value) {
TNF_PROBE_5_DEBUG(s1394_valid_dir_crc_old,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_uint, node_guid_hi, node->node_guid_hi,
tnf_uint, node_guid_lo, node->node_guid_lo,
tnf_uint, node_num, node->node_num,
tnf_uint, key, key, tnf_uint, dir_len, dir_len);
TNF_PROBE_0_DEBUG(s1394_valid_dir_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (B_TRUE);
}
TNF_PROBE_5(s1394_valid_dir_bad_crc,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_uint, node_guid_hi, node->node_guid_hi,
tnf_uint, node_guid_lo, node->node_guid_lo,
tnf_uint, node_num, node->node_num,
tnf_uint, key, key, tnf_uint, dir_len, dir_len);
TNF_PROBE_0_DEBUG(s1394_valid_dir_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (B_FALSE);
}
TNF_PROBE_4_DEBUG(s1394_valid_dir,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_uint, node_guid_hi, node->node_guid_hi,
tnf_uint, node_guid_lo, node->node_guid_lo,
tnf_uint, node_num, node->node_num, tnf_uint, key, key);
return (B_TRUE);
}
/*
* s1394_become_bus_mgr()
* is a callback from a timeout() setup by the main br_thread. After
* a bus reset, depending on the Bus Manager's incumbancy and the state
* of its abdicate bit, a timer of a certain length is set. After this
* time expires, the local host may attempt to become the Bus Manager.
* This is done by sending a request to the current IRM on the bus. The
* IRM holds the BUS_MANAGER_ID register. Depending on whether or not
* the local host is already the IRM, we will send a request onto the
* 1394 bus or call into the HAL.
*/
static void
s1394_become_bus_mgr(void *arg)
{
s1394_hal_t *hal;
s1394_cmd_priv_t *s_priv;
cmd1394_cmd_t *cmd;
uint64_t Bus_Mgr_ID_addr;
uint32_t hal_node_num;
uint32_t old_value;
uint32_t generation;
uint_t curr_bus_mgr;
uint_t bm_node;
uint_t IRM_node;
int err;
int ret;
TNF_PROBE_0_DEBUG(s1394_become_bus_mgr_enter, S1394_TNF_SL_BR_STACK,
"");
hal = (s1394_hal_t *)arg;
/* Lock the topology tree */
mutex_enter(&hal->topology_tree_mutex);
hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
generation = hal->generation_count;
IRM_node = hal->IRM_node;
mutex_enter(&hal->bus_mgr_node_mutex);
bm_node = hal->bus_mgr_node;
mutex_exit(&hal->bus_mgr_node_mutex);
/* Unlock the topology tree */
mutex_exit(&hal->topology_tree_mutex);
/* Make sure we aren't already the Bus Manager */
if (bm_node != -1) {
TNF_PROBE_0_DEBUG(s1394_become_bus_mgr_exit,
S1394_TNF_SL_BR_STACK, "");
return;
}
/* Send compare-swap to BUS_MANAGER_ID */
/* register on the Isoch Rsrc Mgr */
if (IRM_node == hal_node_num) {
/* Local */
ret = HAL_CALL(hal).csr_cswap32(hal->halinfo.hal_private,
generation, (IEEE1394_SCSR_BUSMGR_ID &
IEEE1394_CSR_OFFSET_MASK), S1394_INVALID_NODE_NUM,
hal_node_num, &old_value);
if (ret != DDI_SUCCESS) {
TNF_PROBE_1(s1394_become_bus_mgr_error,
S1394_TNF_SL_BR_ERROR, "", tnf_string, msg,
"Error in cswap32");
TNF_PROBE_0_DEBUG(s1394_become_bus_mgr_exit,
S1394_TNF_SL_BR_STACK, "");
return;
}
curr_bus_mgr = IEEE1394_NODE_NUM(old_value);
mutex_enter(&hal->bus_mgr_node_mutex);
if ((curr_bus_mgr == S1394_INVALID_NODE_NUM) ||
(curr_bus_mgr == hal_node_num)) {
hal->bus_mgr_node = hal_node_num;
hal->incumbent_bus_mgr = B_TRUE;
} else {
hal->bus_mgr_node = curr_bus_mgr;
hal->incumbent_bus_mgr = B_FALSE;
}
cv_signal(&hal->bus_mgr_node_cv);
mutex_exit(&hal->bus_mgr_node_mutex);
} else {
/* Remote */
if (s1394_alloc_cmd(hal, T1394_ALLOC_CMD_NOSLEEP, &cmd) !=
DDI_SUCCESS) {
TNF_PROBE_1(s1394_become_bus_mgr_error,
S1394_TNF_SL_BR_ERROR, "", tnf_string, msg,
"Error in s1394_alloc_cmd()");
TNF_PROBE_0_DEBUG(s1394_become_bus_mgr_exit,
S1394_TNF_SL_BR_STACK, "");
return;
}
cmd->cmd_options = (CMD1394_CANCEL_ON_BUS_RESET |
CMD1394_OVERRIDE_ADDR);
cmd->cmd_type = CMD1394_ASYNCH_LOCK_32;
cmd->completion_callback = s1394_become_bus_mgr_callback;
Bus_Mgr_ID_addr = (IEEE1394_ADDR_BUS_ID_MASK |
IEEE1394_SCSR_BUSMGR_ID) |
(((uint64_t)hal->IRM_node) << IEEE1394_ADDR_PHY_ID_SHIFT);
cmd->cmd_addr = Bus_Mgr_ID_addr;
cmd->bus_generation = generation;
cmd->cmd_u.l32.arg_value = T1394_DATA32(
S1394_INVALID_NODE_NUM);
cmd->cmd_u.l32.data_value = T1394_DATA32(hal_node_num);
cmd->cmd_u.l32.num_retries = 0;
cmd->cmd_u.l32.lock_type = CMD1394_LOCK_COMPARE_SWAP;
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Lock the topology tree */
mutex_enter(&hal->topology_tree_mutex);
ret = s1394_setup_asynch_command(hal, NULL, cmd,
S1394_CMD_LOCK, &err);
/* Unlock the topology tree */
mutex_exit(&hal->topology_tree_mutex);
/* Command has now been put onto the queue! */
if (ret != DDI_SUCCESS) {
/* Need to free the command */
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
TNF_PROBE_1(s1394_become_bus_mgr_error,
S1394_TNF_SL_BR_ERROR, "", tnf_string, msg,
"Error in s1394_setup_asynch_command()");
TNF_PROBE_0_DEBUG(s1394_become_bus_mgr_exit,
S1394_TNF_SL_BR_STACK, "");
return;
}
/* Send the command out */
ret = s1394_xfer_asynch_command(hal, cmd, &err);
if (ret != DDI_SUCCESS) {
/* Remove cmd outstanding request Q */
s1394_remove_q_asynch_cmd(hal, cmd);
s_priv->cmd_in_use = B_FALSE;
mutex_enter(&hal->bus_mgr_node_mutex);
/* Don't know who the bus_mgr is */
hal->bus_mgr_node = S1394_INVALID_NODE_NUM;
hal->incumbent_bus_mgr = B_FALSE;
cv_signal(&hal->bus_mgr_node_cv);
mutex_exit(&hal->bus_mgr_node_mutex);
/* Need to free the command */
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
}
}
TNF_PROBE_0_DEBUG(s1394_become_bus_mgr_exit, S1394_TNF_SL_BR_STACK,
"");
}
/*
* s1394_become_bus_mgr_callback()
* is the callback used by s1394_become_bus_mgr() when it is necessary
* to send the Bus Manager request to a remote IRM. After the completion
* of the compare-swap request, this routine looks at the "old_value"
* in the request to determine whether or not it has become the Bus
* Manager for the current generation. It sets the bus_mgr_node and
* incumbent_bus_mgr fields to their appropriate values.
*/
static void
s1394_become_bus_mgr_callback(cmd1394_cmd_t *cmd)
{
s1394_cmd_priv_t *s_priv;
s1394_hal_t *hal;
uint32_t hal_node_num;
uint32_t temp;
uint_t curr_bus_mgr;
TNF_PROBE_0_DEBUG(s1394_become_bus_mgr_callback_enter,
S1394_TNF_SL_BR_STACK, "");
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
hal = (s1394_hal_t *)s_priv->sent_on_hal;
/* Lock the topology tree */
mutex_enter(&hal->topology_tree_mutex);
hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
/* Was the command successful? */
if (cmd->cmd_result == CMD1394_CMDSUCCESS) {
temp = T1394_DATA32(cmd->cmd_u.l32.old_value);
curr_bus_mgr = IEEE1394_NODE_NUM(temp);
mutex_enter(&hal->bus_mgr_node_mutex);
if ((curr_bus_mgr == S1394_INVALID_NODE_NUM) ||
(curr_bus_mgr == hal_node_num)) {
hal->bus_mgr_node = hal_node_num;
hal->incumbent_bus_mgr = B_TRUE;
} else {
hal->bus_mgr_node = curr_bus_mgr;
hal->incumbent_bus_mgr = B_FALSE;
}
cv_signal(&hal->bus_mgr_node_cv);
mutex_exit(&hal->bus_mgr_node_mutex);
} else {
TNF_PROBE_2(s1394_become_bus_mgr_callback_error,
S1394_TNF_SL_BR_ERROR, "", tnf_string, msg,
"Error while attempting to become bus manager",
tnf_uint, status, cmd->cmd_result);
mutex_enter(&hal->bus_mgr_node_mutex);
/* Don't know who the bus_mgr is */
hal->bus_mgr_node = S1394_INVALID_NODE_NUM;
hal->incumbent_bus_mgr = B_FALSE;
cv_signal(&hal->bus_mgr_node_cv);
mutex_exit(&hal->bus_mgr_node_mutex);
}
/* Need to free the command */
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
/* Unlock the topology tree */
mutex_exit(&hal->topology_tree_mutex);
TNF_PROBE_0_DEBUG(s1394_become_bus_mgr_callback_exit,
S1394_TNF_SL_BR_STACK, "");
}
/*
* s1394_bus_mgr_processing()
* is called following "phase1" completion of reading Bus_Info_Blocks.
* Its purpose is to determine whether the local node is capable of
* becoming the Bus Manager (has the IRMC bit set) and if so to call
* the s1394_do_bus_mgr_processing() routine.
* NOTE: we overload DDI_FAILURE return value to mean jump back to
* the start of bus reset processing.
*/
static int
s1394_bus_mgr_processing(s1394_hal_t *hal)
{
int ret;
int IRM_node_num;
TNF_PROBE_0_DEBUG(s1394_bus_mgr_processing_enter,
S1394_TNF_SL_BR_STACK, "");
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
IRM_node_num = hal->IRM_node;
s1394_unlock_tree(hal);
ret = DDI_SUCCESS;
/* If we are IRM capable, then do bus_mgr stuff... */
if (hal->halinfo.bus_capabilities & IEEE1394_BIB_IRMC_MASK) {
/* If there is an IRM, then do bus_mgr stuff */
if (IRM_node_num != -1) {
if (s1394_do_bus_mgr_processing(hal))
ret = DDI_FAILURE;
}
}
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_0_DEBUG(s1394_bus_mgr_processing_exit,
S1394_TNF_SL_BR_STACK, "");
return (ret);
}
/*
* s1394_do_bus_mgr_processing()
* is used to perform those operations expected of the Bus Manager.
* After being called, s1394_do_bus_mgr_processing() looks at the value
* in bus_mgr_node and waits if it is -1 (Bus Manager has not been
* chosen yet). Then, if there is more than one node on the 1394 bus,
* and we are either the Bus Manager or (if there is no Bus Manager)
* the IRM, it optimizes the gap_count and/or sets the cycle master's
* root holdoff bit (to ensure that the cycle master is/stays root).
*
* NOTE: we overload DDI_FAILURE return value to mean jump back to
* the start of bus reset processing.
*/
static int
s1394_do_bus_mgr_processing(s1394_hal_t *hal)
{
int ret;
int IRM_flags, hal_bus_mgr_node;
int IRM_node_num;
uint_t hal_node_num, number_of_nodes;
int new_root, new_gap_cnt;
TNF_PROBE_0_DEBUG(s1394_do_bus_mgr_processing_enter,
S1394_TNF_SL_BR_STACK, "");
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
/* Wait for Bus Manager to be determined */
/* or a Bus Reset to happen */
mutex_enter(&hal->bus_mgr_node_mutex);
if (hal->bus_mgr_node == -1)
cv_wait(&hal->bus_mgr_node_cv, &hal->bus_mgr_node_mutex);
/* Check if a BUS RESET has come while we've been waiting */
mutex_enter(&hal->br_thread_mutex);
if (hal->br_thread_ev_type & (BR_THR_CFGROM_SCAN | BR_THR_GO_AWAY)) {
mutex_exit(&hal->br_thread_mutex);
mutex_exit(&hal->bus_mgr_node_mutex);
TNF_PROBE_0_DEBUG(s1394_do_bus_mgr_processing_exit,
S1394_TNF_SL_BR_STACK, "");
return (1);
}
mutex_exit(&hal->br_thread_mutex);
hal_bus_mgr_node = hal->bus_mgr_node;
mutex_exit(&hal->bus_mgr_node_mutex);
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
return (1);
}
hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
IRM_node_num = hal->IRM_node;
number_of_nodes = hal->number_of_nodes;
ret = 0;
/* If we are the bus_mgr or if there is no bus_mgr */
/* the IRM and there is > 1 nodes on the bus */
if ((number_of_nodes > 1) &&
((hal_bus_mgr_node == (int)hal_node_num) ||
((hal_bus_mgr_node == S1394_INVALID_NODE_NUM) &&
(IRM_node_num == (int)hal_node_num)))) {
IRM_flags = 0;
/* Make sure the root node is cycle master capable */
if (!s1394_cycle_master_capable(hal)) {
/* Make the local node root */
new_root = hal_node_num;
IRM_flags = IRM_flags | ROOT_HOLDOFF;
/* If setting root, then optimize gap_count */
new_gap_cnt = hal->optimum_gap_count;
IRM_flags = IRM_flags | GAP_COUNT;
} else {
/* Make sure root's ROOT_HOLDOFF bit is set */
new_root = (number_of_nodes - 1);
IRM_flags = IRM_flags | ROOT_HOLDOFF;
}
if (hal->gap_count > hal->optimum_gap_count) {
/* Set the gap_count to optimum */
new_gap_cnt = hal->optimum_gap_count;
IRM_flags = IRM_flags | GAP_COUNT;
}
s1394_unlock_tree(hal);
if (IRM_flags) {
ret = s1394_do_phy_config_pkt(hal, new_root,
new_gap_cnt, IRM_flags);
}
TNF_PROBE_0_DEBUG(s1394_do_bus_mgr_processing_exit,
S1394_TNF_SL_BR_STACK, "");
return (ret);
}
s1394_unlock_tree(hal);
TNF_PROBE_0_DEBUG(s1394_do_bus_mgr_processing_exit,
S1394_TNF_SL_BR_STACK, "");
return (ret);
}
/*
* s1394_bus_mgr_timers_stop()
* Cancels bus manager timeouts
*/
/*ARGSUSED*/
static void
s1394_bus_mgr_timers_stop(s1394_hal_t *hal, timeout_id_t *bus_mgr_query_tid,
timeout_id_t *bus_mgr_tid)
{
TNF_PROBE_0_DEBUG(s1394_bus_mgr_timers_stop_enter,
S1394_TNF_SL_BR_STACK, "");
/* Cancel the Bus Mgr timeouts (if necessary) */
if (*bus_mgr_tid != 0) {
(void) untimeout(*bus_mgr_tid);
*bus_mgr_tid = 0;
}
if (*bus_mgr_query_tid != 0) {
(void) untimeout(*bus_mgr_query_tid);
*bus_mgr_query_tid = 0;
}
TNF_PROBE_0_DEBUG(s1394_bus_mgr_timers_stop_exit,
S1394_TNF_SL_BR_STACK, "");
}
/*
* s1394_bus_mgr_timers_start()
* Starts bus manager timeouts if the hal is IRM capable.
*/
static void
s1394_bus_mgr_timers_start(s1394_hal_t *hal, timeout_id_t *bus_mgr_query_tid,
timeout_id_t *bus_mgr_tid)
{
boolean_t incumbant;
uint_t hal_node_num;
int IRM_node_num;
TNF_PROBE_0_DEBUG(s1394_bus_mgr_timers_start_enter,
S1394_TNF_SL_BR_STACK, "");
mutex_enter(&hal->topology_tree_mutex);
IRM_node_num = hal->IRM_node;
hal_node_num = hal->node_id;
mutex_enter(&hal->bus_mgr_node_mutex);
incumbant = hal->incumbent_bus_mgr;
mutex_exit(&hal->bus_mgr_node_mutex);
/* If we are IRM capable, then do bus_mgr stuff... */
if (hal->halinfo.bus_capabilities & IEEE1394_BIB_IRMC_MASK) {
/*
* If we are the IRM, then wait 625ms
* before checking BUS_MANAGER_ID register
*/
if (IRM_node_num == IEEE1394_NODE_NUM(hal_node_num)) {
TNF_PROBE_0_DEBUG(s1394_bus_mgr_timers_625ms,
S1394_TNF_SL_BR_STACK, "");
mutex_exit(&hal->topology_tree_mutex);
/* Wait 625ms, then check bus manager */
*bus_mgr_query_tid = timeout(s1394_become_bus_mgr,
hal, drv_usectohz(IEEE1394_BM_IRM_TIMEOUT));
mutex_enter(&hal->topology_tree_mutex);
}
/* If there is an IRM on the bus */
if (IRM_node_num != -1) {
if ((incumbant == B_TRUE) &&
(hal->abdicate_bus_mgr_bit == 0)) {
mutex_exit(&hal->topology_tree_mutex);
/* Try to become bus manager */
s1394_become_bus_mgr(hal);
mutex_enter(&hal->topology_tree_mutex);
} else {
hal->abdicate_bus_mgr_bit = 0;
TNF_PROBE_0_DEBUG(s1394_bus_mgr_timers_125ms,
S1394_TNF_SL_BR_STACK, "");
mutex_exit(&hal->topology_tree_mutex);
/* Wait 125ms, then try to become bus manager */
*bus_mgr_tid = timeout(s1394_become_bus_mgr,
hal, drv_usectohz(
IEEE1394_BM_INCUMBENT_TIMEOUT));
mutex_enter(&hal->topology_tree_mutex);
}
} else {
mutex_enter(&hal->bus_mgr_node_mutex);
hal->incumbent_bus_mgr = B_FALSE;
mutex_exit(&hal->bus_mgr_node_mutex);
}
}
mutex_exit(&hal->topology_tree_mutex);
TNF_PROBE_0_DEBUG(s1394_bus_mgr_timers_start_exit,
S1394_TNF_SL_BR_STACK, "");
}
/*
* s1394_get_maxpayload()
* is used to determine a device's maximum payload size. That is to
* say, the largest packet that can be transmitted or received by the
* the target device given the current topological (speed) constraints
* and the constraints specified in the local host's and remote device's
* Config ROM (max_rec). Caller must hold the topology_tree_mutex and
* the target_list_rwlock as an RW_READER (at least).
*/
/*ARGSUSED*/
void
s1394_get_maxpayload(s1394_target_t *target, uint_t *dev_max_payload,
uint_t *current_max_payload)
{
s1394_hal_t *hal;
uint32_t bus_capabilities;
uint32_t from_node;
uint32_t to_node;
uint_t local_max_rec;
uint_t local_max_blk;
uint_t max_rec;
uint_t max_blk;
uint_t curr_speed;
uint_t speed_max_blk;
uint_t temp;
TNF_PROBE_0_DEBUG(s1394_get_maxpayload_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
/* Find the HAL this target resides on */
hal = target->on_hal;
/* Make sure we're holding the topology_tree_mutex */
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
/* Set dev_max_payload to local (HAL's) size */
bus_capabilities = target->on_hal->halinfo.bus_capabilities;
local_max_rec = (bus_capabilities & IEEE1394_BIB_MAXREC_MASK) >>
IEEE1394_BIB_MAXREC_SHIFT;
if ((local_max_rec > 0) && (local_max_rec < 14)) {
local_max_blk = 1 << (local_max_rec + 1);
} else {
/* These are either unspecified or reserved */
local_max_blk = 4;
}
/* Is this target on a node? */
if ((target->target_state & S1394_TARG_GONE) == 0 &&
(target->on_node != NULL)) {
ASSERT(target->on_node->cfgrom != NULL);
bus_capabilities =
target->on_node->cfgrom[IEEE1212_NODE_CAP_QUAD];
max_rec = (bus_capabilities & IEEE1394_BIB_MAXREC_MASK) >>
IEEE1394_BIB_MAXREC_SHIFT;
if ((max_rec > 0) && (max_rec < 14)) {
max_blk = 1 << (max_rec + 1);
} else {
/* These are either unspecified or reserved */
max_blk = 4;
}
(*dev_max_payload) = max_blk;
from_node = IEEE1394_NODE_NUM(target->on_hal->node_id);
to_node = (target->on_node->node_num);
/* Speed is to be filled in from speed map */
curr_speed = (uint_t)s1394_speed_map_get(target->on_hal,
from_node, to_node);
speed_max_blk = 512 << curr_speed;
temp = (local_max_blk < max_blk) ? local_max_blk : max_blk;
(*current_max_payload) = (temp < speed_max_blk) ? temp :
speed_max_blk;
} else {
/* Set dev_max_payload to local (HAL's) size */
(*dev_max_payload) = local_max_blk;
(*current_max_payload) = local_max_blk;
}
TNF_PROBE_0_DEBUG(s1394_get_maxpayload_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
}
/*
* s1394_cycle_master_capable()
* is used to determine whether or not the current root node on the
* 1394 bus has its CMC-bit set in it Config ROM. If not, then it
* is not capable of being cycle master and a new root node must be
* selected.
*/
static int
s1394_cycle_master_capable(s1394_hal_t *hal)
{
s1394_node_t *root;
int cycle_master_capable;
uint_t hal_node_num;
TNF_PROBE_0_DEBUG(s1394_cycle_master_capable_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
/* Get a pointer to the root node */
root = s1394_topology_tree_get_root_node(hal);
/* Ignore, if we are already root */
if (root == &hal->topology_tree[hal_node_num]) {
TNF_PROBE_2_DEBUG(s1394_cmstr_capable_hal,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int,
node_num, hal_node_num, tnf_int, ret, 1);
return (1);
}
/*
* We want to pick a new root if link is off or we don't have
* valid config rom
*/
if (LINK_ACTIVE(root) == B_FALSE || root->cfgrom == NULL ||
CFGROM_BIB_READ(root) == 0) {
TNF_PROBE_4_DEBUG(s1394_cmstr_capable_not_hal,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int,
root, root->node_num, tnf_int, link_active,
LINK_ACTIVE(root), tnf_opaque, cfgrom, root->cfgrom,
tnf_int, bib, CFGROM_BIB_READ(root));
return (0);
}
/* Check the Cycle Master bit in the Bus Info Block */
cycle_master_capable = root->cfgrom[IEEE1212_NODE_CAP_QUAD] &
IEEE1394_BIB_CMC_MASK;
if (cycle_master_capable) {
TNF_PROBE_1_DEBUG(s1394_cmstr_capable_root,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int,
root, root->node_num);
return (1);
} else {
TNF_PROBE_1(s1394_cmstr_not_capable_root,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int,
root, root->node_num);
return (0);
}
}
/*
* s1394_do_phy_config_pkt()
* is called by s1394_do_bus_mgr_processing() to setup and send out
* a PHY configuration packet onto the 1394 bus. Depending on the
* values in IRM_flags, the gap_count and root_holdoff bits on the
* bus will be affected by this packet.
*
* NOTE: we overload DDI_FAILURE return value to mean jump back to
* the start of bus reset processing.
*/
static int
s1394_do_phy_config_pkt(s1394_hal_t *hal, int new_root, int new_gap_cnt,
uint32_t IRM_flags)
{
cmd1394_cmd_t *cmd;
s1394_cmd_priv_t *s_priv;
h1394_cmd_priv_t *h_priv;
uint32_t pkt_data = 0;
uint32_t gap_cnt = 0;
uint32_t root = 0;
int ret, result;
uint_t flags = 0;
TNF_PROBE_0_DEBUG(s1394_do_phy_config_pkt_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
/* Gap count needs to be optimized */
if (IRM_flags & GAP_COUNT) {
pkt_data = pkt_data | IEEE1394_PHY_CONFIG_T_BIT_MASK;
gap_cnt = ((uint32_t)new_gap_cnt) <<
IEEE1394_PHY_CONFIG_GAP_CNT_SHIFT;
gap_cnt = gap_cnt & IEEE1394_PHY_CONFIG_GAP_CNT_MASK;
pkt_data = pkt_data | gap_cnt;
(void) HAL_CALL(hal).set_gap_count(hal->halinfo.hal_private,
(uint_t)new_gap_cnt);
}
/* Root node needs to be changed */
if (IRM_flags & ROOT_HOLDOFF) {
pkt_data = pkt_data | IEEE1394_PHY_CONFIG_R_BIT_MASK;
root = ((uint32_t)new_root) <<
IEEE1394_PHY_CONFIG_ROOT_HOLD_SHIFT;
root = root & IEEE1394_PHY_CONFIG_ROOT_HOLD_MASK;
pkt_data = pkt_data | root;
(void) HAL_CALL(hal).set_root_holdoff_bit(
hal->halinfo.hal_private);
}
if (IRM_flags) {
if (s1394_alloc_cmd(hal, flags, &cmd) != DDI_SUCCESS) {
TNF_PROBE_1_DEBUG(s1394_do_phy_config_pkt_error,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"Unable to allocate PHY config packet");
TNF_PROBE_0_DEBUG(s1394_do_phy_config_pkt_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (0);
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
/* lock tree failure indicates a bus gen change */
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
TNF_PROBE_0_DEBUG(s1394_do_phy_config_pkt_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (1);
}
/* Setup the callback routine */
cmd->completion_callback = s1394_phy_config_callback;
cmd->cmd_callback_arg = (void *)(uintptr_t)IRM_flags;
cmd->bus_generation = hal->generation_count;
cmd->cmd_options = CMD1394_OVERRIDE_ADDR;
cmd->cmd_type = CMD1394_ASYNCH_WR_QUAD;
cmd->cmd_u.q.quadlet_data = pkt_data;
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
/* Get a pointer to the HAL private struct */
h_priv = (h1394_cmd_priv_t *)&s_priv->hal_cmd_private;
s_priv->sent_by_target = (s1394_target_t *)NULL;
s_priv->sent_on_hal = (s1394_hal_t *)hal;
h_priv->bus_generation = cmd->bus_generation;
/* Speed must be IEEE1394_S100 on PHY config packets */
s_priv->hal_cmd_private.speed = IEEE1394_S100;
/* Mark command as being used */
s_priv->cmd_in_use = B_TRUE;
s1394_unlock_tree(hal);
/* Put command on the HAL's outstanding request Q */
s1394_insert_q_asynch_cmd(hal, cmd);
ret = HAL_CALL(hal).send_phy_configuration_packet(
hal->halinfo.hal_private, (cmd1394_cmd_t *)cmd,
(h1394_cmd_priv_t *)&s_priv->hal_cmd_private, &result);
if (ret != DDI_SUCCESS) {
TNF_PROBE_2_DEBUG(s1394_do_phy_config_pkt_error,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"Unable to send PHY config packet",
tnf_int, result, result);
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
TNF_PROBE_0_DEBUG(s1394_do_phy_config_pkt_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (0);
} else {
/*
* There will be a bus reset only if GAP_COUNT changed
*/
if (IRM_flags & GAP_COUNT) {
return (1);
}
}
}
TNF_PROBE_0_DEBUG(s1394_do_phy_config_pkt_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (0);
}
/*
* s1394_phy_config_callback()
* is the callback called after the PHY configuration packet has been
* sent out onto the 1394 bus. Depending on the values in IRM_flags,
* (specifically if the gap_count has been changed) this routine may
* initiate a bus reset.
*/
static void
s1394_phy_config_callback(cmd1394_cmd_t *cmd)
{
s1394_cmd_priv_t *s_priv;
s1394_hal_t *hal;
uint32_t IRM_flags;
TNF_PROBE_0_DEBUG(s1394_phy_config_callback_enter,
S1394_TNF_SL_HOTPLUG_STACK, "");
/* Get the Services Layer private area */
s_priv = S1394_GET_CMD_PRIV(cmd);
hal = (s1394_hal_t *)s_priv->sent_on_hal;
IRM_flags = (uint32_t)(uintptr_t)cmd->cmd_callback_arg;
if (cmd->cmd_result != CMD1394_CMDSUCCESS) {
TNF_PROBE_2_DEBUG(s1394_do_phy_config_pkt_error,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"Error sending PHY config packet",
tnf_int, result, cmd->cmd_result);
(void) s1394_free_cmd(hal, &cmd);
} else {
(void) s1394_free_cmd(hal, &cmd);
/* Only need a bus reset if we changed GAP_COUNT */
if (IRM_flags & GAP_COUNT) {
s1394_initiate_hal_reset(hal, NON_CRITICAL);
}
}
TNF_PROBE_0_DEBUG(s1394_phy_config_callback_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
}
/*
* s1394_lock_tree()
* Attempts to lock the topology tree. Returns DDI_FAILURE if generations
* changed or if the services layer signals the bus reset thread to go
* away. Otherwise, returns DDI_SUCCESS.
*/
int
s1394_lock_tree(s1394_hal_t *hal)
{
int circular;
ASSERT(MUTEX_NOT_HELD(&hal->br_thread_mutex));
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
TNF_PROBE_0_DEBUG(s1394_lock_tree_enter, S1394_TNF_SL_HOTPLUG_STACK,
"");
mutex_enter(&hal->br_thread_mutex);
ndi_devi_enter(hal->halinfo.dip, &circular);
mutex_enter(&hal->topology_tree_mutex);
if ((hal->br_thread_ev_type & BR_THR_GO_AWAY) != 0) {
TNF_PROBE_2(s1394_lock_tree_go_away,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_int, hal_generation, hal->generation_count,
tnf_int, br_thread_gen, hal->br_cfgrom_read_gen);
TNF_PROBE_0_DEBUG(s1394_lock_tree_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
mutex_exit(&hal->br_thread_mutex);
mutex_exit(&hal->topology_tree_mutex);
ndi_devi_exit(hal->halinfo.dip, circular);
return (DDI_FAILURE);
} else if (hal->br_cfgrom_read_gen != hal->generation_count) {
TNF_PROBE_2(s1394_lock_tree_gen_changed,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_int, hal_generation, hal->generation_count,
tnf_int, br_thread_gen, hal->br_cfgrom_read_gen);
TNF_PROBE_0_DEBUG(s1394_lock_tree_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
mutex_exit(&hal->br_thread_mutex);
mutex_exit(&hal->topology_tree_mutex);
ndi_devi_exit(hal->halinfo.dip, circular);
return (DDI_FAILURE);
}
mutex_exit(&hal->br_thread_mutex);
TNF_PROBE_0_DEBUG(s1394_lock_tree_exit, S1394_TNF_SL_HOTPLUG_STACK, "");
return (DDI_SUCCESS);
}
/*
* s1394_unlock_tree()
* Unlocks the topology tree
*/
void
s1394_unlock_tree(s1394_hal_t *hal)
{
TNF_PROBE_0_DEBUG(s1394_unlock_tree_enter, S1394_TNF_SL_HOTPLUG_STACK,
"");
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
mutex_exit(&hal->topology_tree_mutex);
ndi_devi_exit(hal->halinfo.dip, 0);
TNF_PROBE_0_DEBUG(s1394_unlock_tree_exit, S1394_TNF_SL_HOTPLUG_STACK,
"");
}
/*
* s1394_calc_next_quad()
* figures out the next quadlet to read. This maintains a stack of
* directories in the node. When the first quad of a directory (the
* first directory would be the root directory) is read, it is pushed on
* the this stack. When the directory is all read, it scans the directory
* looking for indirect entries. If any indirect directory entry is found,
* it is pushed on stack and that directory is read. If we are done dealing
* with all entries in the current dir, the directory is popped off the
* stack. If the stack is empty, we are back at the root directory level
* and essentially read the entire directory hierarchy.
* Returns 0 is more quads to read, else returns non-zero.
*/
static int
s1394_calc_next_quad(s1394_hal_t *hal, s1394_node_t *node, uint32_t quadlet,
uint32_t *nextquadp)
{
uint32_t data, type, key, value, *ptr;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
TNF_PROBE_4_DEBUG(s1394_calc_next_quad_enter,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_uint, node_num, node->node_num,
tnf_uint, quadlet, quadlet, tnf_int, cfgrom_size, node->cfgrom_size,
tnf_uint, hal_gen, hal->generation_count);
if (((quadlet + 1) >= node->cfgrom_size) ||
(CFGROM_SIZE_IS_CRCSIZE(node) == B_TRUE && (quadlet + 1) >=
node->cfgrom_valid_size)) {
TNF_PROBE_0_DEBUG(s1394_calc_next_quad_exit,
S1394_TNF_SL_HOTPLUG_STACK, "");
return (1);
}
if (s1394_turn_off_dir_stack != 0 || CFGROM_DIR_STACK_OFF(node) ==
B_TRUE) {
quadlet++;
*nextquadp = quadlet;
TNF_PROBE_3_DEBUG(s1394_calc_next_quad_exit,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
"dir stack turned off", tnf_uint, quadlet, quadlet,
tnf_opaque, cfgrom, node->cfgrom);
return (0);
}
data = node->cfgrom[quadlet];
if (quadlet == IEEE1212_ROOT_DIR_QUAD) {
node->dir_stack_top = -1;
node->expected_dir_quad = quadlet;
node->expected_type = IEEE1212_IMMEDIATE_TYPE;
}
CFGROM_TYPE_KEY_VALUE(data, type, key, value);
/*
* check to make sure we are looking at a dir. If the config rom
* is broken, then revert to normal scanning of the config rom
*/
if (node->expected_dir_quad == quadlet) {
if (type != 0 || key != 0) {
TNF_PROBE_3_DEBUG(s1394_calc_next_quad,
S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
"bad directory turning off stack", tnf_uint,
quadlet, quadlet, tnf_uint, data, data);
SET_CFGROM_DIR_STACK_OFF(node);
quadlet = IEEE1212_ROOT_DIR_QUAD;
} else {
node->cur_dir_start = quadlet;
node->cur_dir_size = IEEE1212_DIR_LEN(data);
node->expected_dir_quad = 0;
/* get the next quad */
quadlet++;
}
} else {
/*
* If we read all quads in cur dir and the cur dir is not
* a leaf, scan for offsets (if the directory's CRC checks
* out OK). If we have a directory or a leaf, we save the
* current location on the stack and start reading that
* directory. So, we will end up with a depth first read of
* the entire config rom. If we are done with the current
* directory, pop it off the stack and continue the scanning
* as appropriate.
*/
if (quadlet == node->cur_dir_start + node->cur_dir_size) {
int i, top;
boolean_t done_with_cur_dir = B_FALSE;
if (node->expected_type == IEEE1212_LEAF_TYPE) {
node->expected_type = IEEE1212_IMMEDIATE_TYPE;
done_with_cur_dir = B_TRUE;
TNF_PROBE_2_DEBUG(s1394_calc_next_quad,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_string, msg, "done with a leaf",
tnf_uint, quadlet, quadlet);
goto donewithcurdir;
}
ptr = &node->cfgrom[node->cur_dir_start];
CFGROM_TYPE_KEY_VALUE(*ptr, type, key, value);
/*
* If CRC for this directory is invalid, turn off
* dir stack and start re-reading from root dir.
* This wastes the work done thus far, but CRC
* errors in directories should be rather rare.
* if s1394_crcsz_is_cfgsz is set, then set
* cfgrom_valid_size to the len specfied as crc len
* in quadlet 0.
*/
if (s1394_valid_dir(hal, node, key, ptr) == B_FALSE) {
SET_CFGROM_DIR_STACK_OFF(node);
if (s1394_crcsz_is_cfgsz != 0) {
SET_CFGROM_SIZE_IS_CRCSIZE(node);
node->cfgrom_valid_size =
((node->cfgrom[0] >>
IEEE1394_CFG_ROM_CRC_LEN_SHIFT) &
IEEE1394_CFG_ROM_CRC_LEN_MASK);
TNF_PROBE_2(s1394_calc_next_quad,
S1394_TNF_SL_HOTPLUG_ERROR, "",
tnf_string, msg, "crc sz is cfg sz",
tnf_uint, size,
node->cfgrom_valid_size);
}
TNF_PROBE_2_DEBUG(s1394_calc_next_quad_exit,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string,
msg, "crc error", tnf_uint, quadlet,
quadlet);
*nextquadp = IEEE1212_ROOT_DIR_QUAD;
return (0);
}
i = node->cur_dir_start + 1;
rescan:
for (done_with_cur_dir = B_FALSE; i <=
node->cur_dir_start + node->cur_dir_size; i++) {
data = node->cfgrom[i];
CFGROM_TYPE_KEY_VALUE(data, type, key, value);
/* read leaf type and directory types only */
if (type == IEEE1212_LEAF_TYPE || type ==
IEEE1212_DIRECTORY_TYPE) {
/*
* push current dir on stack; if the
* stack is overflowing, ie, too many
* directory level nestings, turn off
* dir stack and fall back to serial
* scanning, starting at root dir. This
* wastes all the work we have done
* thus far, but more than 16 levels
* of directories is rather odd...
*/
top = ++node->dir_stack_top;
if (top == S1394_DIR_STACK_SIZE) {
TNF_PROBE_2_DEBUG(
s1394_calc_next_quad_exit,
S1394_TNF_SL_HOTPLUG_STACK,
"", tnf_string, msg,
"dir stack overflow",
tnf_uint, quadlet, quadlet);
SET_CFGROM_DIR_STACK_OFF(node);
*nextquadp =
IEEE1212_ROOT_DIR_QUAD;
return (0);
}
TNF_PROBE_3_DEBUG(
s1394_calc_next_quad,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_string, msg, "push dir stack",
tnf_uint, quadlet, quadlet,
tnf_int, top, top);
node->dir_stack[top].dir_start =
node->cur_dir_start;
node->dir_stack[top].dir_size =
node->cur_dir_size;
node->dir_stack[top].dir_next_quad =
i + 1;
/* and set the next quadlet to read */
quadlet = i + value;
node->expected_dir_quad = quadlet;
node->expected_type = type;
break;
}
}
donewithcurdir:
if ((i > node->cur_dir_start + node->cur_dir_size) ||
done_with_cur_dir == B_TRUE) {
/*
* all done with cur dir; pop it off the stack
*/
if (node->dir_stack_top >= 0) {
TNF_PROBE_3_DEBUG(
s1394_calc_next_quad_exit,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_string, msg, "pop dir stack",
tnf_uint, quadlet, quadlet,
tnf_int, top, node->dir_stack_top);
top = node->dir_stack_top--;
node->cur_dir_start =
node->dir_stack[top].dir_start;
node->cur_dir_size =
node->dir_stack[top].dir_size;
i = node->dir_stack[top].dir_next_quad;
goto rescan;
} else {
/*
* if empty stack, we are at the top
* level; declare done.
*/
TNF_PROBE_1_DEBUG(
s1394_calc_next_quad_exit,
S1394_TNF_SL_HOTPLUG_STACK, "",
tnf_string, msg, "all done");
return (1);
}
}
} else {
/* get the next quadlet */
quadlet++;
}
}
*nextquadp = quadlet;
TNF_PROBE_1_DEBUG(s1394_calc_next_quad_exit, S1394_TNF_SL_HOTPLUG_STACK,
"", tnf_uint, next_quad, quadlet);
return (0);
}