#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/1394/t1394.h>
#include <sys/1394/s1394.h>
#include <sys/1394/h1394.h>
#include <sys/1394/ieee1394.h>
#include <sys/1394/ieee1212.h>
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;
static int s1394_cfgrom_read_delay_ms = 20;
static int s1394_cfgrom_read_delay_incr = 10;
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;
void
s1394_br_thread(s1394_hal_t *hal)
{
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
hal->bus_mgr_timeout_id = 0;
hal->bus_mgr_query_timeout_id = 0;
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));
s1394_wait_for_events(hal, 0);
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
s1394_bus_mgr_timers_stop(hal, &hal->bus_mgr_query_timeout_id,
&hal->bus_mgr_timeout_id);
s1394_flush_cmplq(hal);
s1394_bus_mgr_timers_start(hal, &hal->bus_mgr_query_timeout_id,
&hal->bus_mgr_timeout_id);
s1394_isoch_rsrc_realloc(hal);
if (s1394_cfgrom_scan_phase1(hal) != DDI_SUCCESS) {
continue;
}
if (s1394_bus_mgr_processing(hal) != DDI_SUCCESS) {
continue;
}
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
if (s1394_cfgrom_scan_phase2(hal) != DDI_SUCCESS) {
continue;
}
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
}
}
static void
s1394_wait_for_events(s1394_hal_t *hal, int firsttime)
{
uint_t event;
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");
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) {
s1394_br_thread_exit(hal);
return;
}
if (firsttime) {
mutex_exit(&hal->br_thread_mutex);
return;
}
mutex_enter(&hal->topology_tree_mutex);
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);
}
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));
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) {
mutex_exit(&hal->topology_tree_mutex);
mutex_exit(&hal->br_cmplq_mutex);
s1394_flush_cmplq(hal);
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) {
(void) s1394_free_cmd(hal, &cmd);
continue;
}
cmdret = (*handle_cmd_fn)(hal, cmd);
ASSERT(cmdret != S1394_HCMD_INVALID);
if (cmdret == S1394_HCMD_LOCK_FAILED) {
ret = DDI_FAILURE;
s1394_flush_cmplq(hal);
break;
} else if (cmdret == S1394_HCMD_NODE_DONE) {
if (--hal->cfgroms_being_read == 0) {
break;
}
} else {
ASSERT(cmdret == S1394_HCMD_NODE_EXPECT_MORE);
done = 0;
}
}
}
return (ret);
}
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));
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;
(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);
}
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));
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();
}
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));
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);
}
}
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));
*status = S1394_NOSTATUS;
ASSERT(node->cfgrom == NULL || (node->cfgrom != NULL &&
CFGROM_GEN_CHANGED(node) == B_TRUE));
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;
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));
return (DDI_SUCCESS);
}
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);
if (options == S1394_FREE_CFGROM_BOTH) {
s1394_node_t *onode = node->old_node;
if (NODE_MATCHED(node) == B_TRUE && onode->cfgrom != NULL)
ASSERT(onode->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) {
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) {
kmem_free(node->cfgrom, IEEE1394_CONFIG_ROM_SZ);
node->cfgrom = NULL;
CLEAR_CFGROM_STATE(node);
}
}
void
s1394_copy_cfgrom(s1394_node_t *to, s1394_node_t *from)
{
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);
if (LINK_ACTIVE(from) == B_FALSE) {
ASSERT(from->cfgrom == NULL);
return;
} else {
s1394_selfid_pkt_t *selfid_pkt = to->selfid_packet;
if (IEEE1394_SELFID_ISLINKON(selfid_pkt))
SET_LINK_ACTIVE(to);
}
}
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;
s1394_unlock_tree(hal);
*status = S1394_NOSTATUS;
if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
*status |= S1394_CMD_ALLOC_FAILED;
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
*status |= S1394_LOCK_FAILED;
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) {
return (DDI_FAILURE);
}
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));
return (DDI_FAILURE);
}
quadlet = CFGROM_BIB_READ(node) ? IEEE1212_NODE_CAP_QUAD : 0;
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);
SETUP_QUAD_READ(node, 1, quadlet, 1);
if (s1394_read_config_quadlet(hal, cmd, status) != DDI_SUCCESS) {
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));
}
return (DDI_FAILURE);
}
hal->cfgroms_being_read++;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
return (DDI_SUCCESS);
}
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);
s1394_unlock_tree(hal);
*status = S1394_NOSTATUS;
if (s1394_alloc_cmd(hal, 0, &cmd) != DDI_SUCCESS) {
*status |= S1394_CMD_ALLOC_FAILED;
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
*status |= S1394_LOCK_FAILED;
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) {
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;
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) {
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));
}
return (DDI_FAILURE);
}
hal->cfgroms_being_read++;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
return (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;
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
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;
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;
ASSERT(NODE_MATCHED(nnode) == B_FALSE || (NODE_MATCHED(nnode) ==
B_TRUE && onode != NULL));
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 {
CLEAR_LINK_ACTIVE(nnode);
if (NODE_MATCHED(nnode) == B_TRUE &&
LINK_ACTIVE(onode) == B_TRUE) {
CLEAR_CFGROM_STATE(nnode);
}
}
} else {
if (node == hal_node_num) {
onode = &hal->old_tree[hal_node_num_old];
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) {
return (DDI_FAILURE);
}
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));
return (ret);
}
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);
cmdret = S1394_HCMD_NODE_EXPECT_MORE;
locked = 1;
freecmd = 1;
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
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);
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);
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 {
SET_CFGROM_GEN_CHANGED(node);
if (onode != NULL)
SET_CFGROM_GEN_CHANGED(onode);
CLEAR_CFGROM_BIB_READ(node);
reread = 1;
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;
goto bail;
}
}
}
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 (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);
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
locked = 0;
} 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 {
const s1394_free_cfgrom_t S =
S1394_FREE_CFGROM_NEW;
if (CFGROM_NEW_ALLOC(node) ==
B_TRUE) {
s1394_free_cfgrom(hal,
node, S);
} else {
CLEAR_CFGROM_STATE(
node);
}
}
done++;
} else {
freecmd = 0;
}
} else {
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++;
}
}
} else {
done = 1;
node->cfgrom_read_fails++;
BUMP_CFGROM_READ_DELAY(node);
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) {
locked = 0;
} 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 {
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 {
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;
}
if (locked == 0)
cmdret = S1394_HCMD_LOCK_FAILED;
else
s1394_unlock_tree(hal);
return (cmdret);
}
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));
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
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));
return (DDI_FAILURE);
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
s1394_update_speed_map_link_speeds(hal);
s1394_unlock_tree(hal);
s1394_physical_arreq_setup_all(hal);
s1394_isoch_rsrc_realloc_notify(hal);
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
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) {
return (DDI_FAILURE);
}
if (hal->disable_requests_bit == 0)
hal->hal_state = S1394_HAL_NORMAL;
else
hal->hal_state = S1394_HAL_DREQ;
s1394_unlock_tree(hal);
s1394_resend_pending_cmds(hal);
if (s1394_process_topology_tree(hal, &wait_for_cbs, &wait_gen)) {
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
return (DDI_FAILURE);
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
s1394_print_node_info(hal);
s1394_unlock_tree(hal);
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
ret = DDI_SUCCESS;
if (wait_for_cbs != 0) {
ret = s1394_wait_for_cfgrom_callbacks(hal, wait_gen,
s1394_br_thread_handle_cmd_phase2);
}
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
return (ret);
}
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;
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) {
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) {
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;
} 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 {
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 {
done = 0;
freecmd = 0;
}
}
} else {
node->cfgrom_read_fails++;
BUMP_CFGROM_READ_DELAY(node);
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;
} 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 {
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 {
done = 0;
freecmd = 0;
}
} else {
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;
}
if (update_devinfo) {
ASSERT(locked);
if (s1394_update_devinfo_tree(hal, node) != DDI_SUCCESS) {
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
locked = 0;
}
}
if (locked == 0)
cmdret = S1394_HCMD_LOCK_FAILED;
else
s1394_unlock_tree(hal);
return (cmdret);
}
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;
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));
return (DDI_FAILURE);
}
s1394_unlock_tree(hal);
ret = DDI_SUCCESS;
if (s1394_xfer_asynch_command(hal, cmd, &err) == DDI_SUCCESS) {
*status |= S1394_CMD_INFLIGHT;
} else {
s1394_cmd_priv_t *s_priv;
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;
}
return (ret);
}
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
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);
#endif
if (cmd->cmd_result == CMD1394_EBUSRESET) {
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
} else {
mutex_enter(&hal->br_cmplq_mutex);
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);
}
}
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)
{
*addr_hi = *addr_lo = *size_hi = *size_lo = 0;
}
static void
s1394_get_quad_info(cmd1394_cmd_t *cmd, uint32_t *node_num, uint32_t *quadlet,
uint32_t *data)
{
uint64_t addr;
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);
}
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;
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) {
cmn_err(CE_NOTE, "!Duplicate GUIDs: %08x%08x",
old_a, old_b);
ret = s1394_offline_node(hal, onode->cur_node);
ASSERT(CFGROM_NEW_ALLOC(nnode) == B_TRUE);
s1394_free_cfgrom(hal, nnode,
S1394_FREE_CFGROM_NEW);
break;
}
if (CFGROM_GEN_CHANGED(nnode) == B_TRUE ||
(CONFIG_ROM_GEN(onode->cfgrom) !=
CONFIG_ROM_GEN(nnode->cfgrom))) {
gen_changed = 1;
} 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 (gen_changed == 1) {
if (s1394_offline_node(hal, onode)) {
ret = DDI_FAILURE;
break;
}
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;
break;
}
if (onode->cfgrom != nnode->cfgrom) {
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);
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;
}
}
return (ret);
}
static int
s1394_match_all_GUIDs(s1394_hal_t *hal)
{
int node;
int ret = DDI_SUCCESS;
s1394_node_t *nnode;
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)) {
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;
}
}
return (ret);
}
boolean_t
s1394_valid_cfgrom(s1394_hal_t *hal, s1394_node_t *node)
{
uint32_t crc_len, crc_value, CRC, CRC_old, quad0;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
ASSERT(node->cfgrom);
if (s1394_enable_crc_validation == 0) {
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) {
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) {
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);
return (B_FALSE);
}
return (B_TRUE);
}
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;
if (s1394_enable_crc_validation == 0) {
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) {
return (B_TRUE);
}
return (B_FALSE);
}
return (B_TRUE);
}
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;
hal = (s1394_hal_t *)arg;
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);
mutex_exit(&hal->topology_tree_mutex);
if (bm_node != -1) {
return;
}
if (IRM_node == hal_node_num) {
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) {
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 {
if (s1394_alloc_cmd(hal, T1394_ALLOC_CMD_NOSLEEP, &cmd) !=
DDI_SUCCESS) {
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;
s_priv = S1394_GET_CMD_PRIV(cmd);
mutex_enter(&hal->topology_tree_mutex);
ret = s1394_setup_asynch_command(hal, NULL, cmd,
S1394_CMD_LOCK, &err);
mutex_exit(&hal->topology_tree_mutex);
if (ret != DDI_SUCCESS) {
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
return;
}
ret = s1394_xfer_asynch_command(hal, cmd, &err);
if (ret != DDI_SUCCESS) {
s1394_remove_q_asynch_cmd(hal, cmd);
s_priv->cmd_in_use = B_FALSE;
mutex_enter(&hal->bus_mgr_node_mutex);
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);
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
}
}
}
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;
s_priv = S1394_GET_CMD_PRIV(cmd);
hal = (s1394_hal_t *)s_priv->sent_on_hal;
mutex_enter(&hal->topology_tree_mutex);
hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
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 {
mutex_enter(&hal->bus_mgr_node_mutex);
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);
}
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
mutex_exit(&hal->topology_tree_mutex);
}
static int
s1394_bus_mgr_processing(s1394_hal_t *hal)
{
int ret;
int IRM_node_num;
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 (hal->halinfo.bus_capabilities & IEEE1394_BIB_IRMC_MASK) {
if (IRM_node_num != -1) {
if (s1394_do_bus_mgr_processing(hal))
ret = DDI_FAILURE;
}
}
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
return (ret);
}
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;
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
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);
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);
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 ((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;
if (!s1394_cycle_master_capable(hal)) {
new_root = hal_node_num;
IRM_flags = IRM_flags | ROOT_HOLDOFF;
new_gap_cnt = hal->optimum_gap_count;
IRM_flags = IRM_flags | GAP_COUNT;
} else {
new_root = (number_of_nodes - 1);
IRM_flags = IRM_flags | ROOT_HOLDOFF;
}
if (hal->gap_count > hal->optimum_gap_count) {
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);
}
return (ret);
}
s1394_unlock_tree(hal);
return (ret);
}
static void
s1394_bus_mgr_timers_stop(s1394_hal_t *hal, timeout_id_t *bus_mgr_query_tid,
timeout_id_t *bus_mgr_tid)
{
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;
}
}
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;
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 (hal->halinfo.bus_capabilities & IEEE1394_BIB_IRMC_MASK) {
if (IRM_node_num == IEEE1394_NODE_NUM(hal_node_num)) {
mutex_exit(&hal->topology_tree_mutex);
*bus_mgr_query_tid = timeout(s1394_become_bus_mgr,
hal, drv_usectohz(IEEE1394_BM_IRM_TIMEOUT));
mutex_enter(&hal->topology_tree_mutex);
}
if (IRM_node_num != -1) {
if ((incumbant == B_TRUE) &&
(hal->abdicate_bus_mgr_bit == 0)) {
mutex_exit(&hal->topology_tree_mutex);
s1394_become_bus_mgr(hal);
mutex_enter(&hal->topology_tree_mutex);
} else {
hal->abdicate_bus_mgr_bit = 0;
mutex_exit(&hal->topology_tree_mutex);
*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);
}
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;
hal = target->on_hal;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
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 {
local_max_blk = 4;
}
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 {
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);
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 {
(*dev_max_payload) = local_max_blk;
(*current_max_payload) = local_max_blk;
}
}
static int
s1394_cycle_master_capable(s1394_hal_t *hal)
{
s1394_node_t *root;
int cycle_master_capable;
uint_t hal_node_num;
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
root = s1394_topology_tree_get_root_node(hal);
if (root == &hal->topology_tree[hal_node_num]) {
return (1);
}
if (LINK_ACTIVE(root) == B_FALSE || root->cfgrom == NULL ||
CFGROM_BIB_READ(root) == 0) {
return (0);
}
cycle_master_capable = root->cfgrom[IEEE1212_NODE_CAP_QUAD] &
IEEE1394_BIB_CMC_MASK;
if (cycle_master_capable) {
return (1);
} else {
return (0);
}
}
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;
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);
}
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) {
return (0);
}
if (s1394_lock_tree(hal) != DDI_SUCCESS) {
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
return (1);
}
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;
s_priv = S1394_GET_CMD_PRIV(cmd);
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;
s_priv->hal_cmd_private.speed = IEEE1394_S100;
s_priv->cmd_in_use = B_TRUE;
s1394_unlock_tree(hal);
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) {
(void) s1394_free_cmd(hal, (cmd1394_cmd_t **)&cmd);
return (0);
} else {
if (IRM_flags & GAP_COUNT) {
return (1);
}
}
}
return (0);
}
static void
s1394_phy_config_callback(cmd1394_cmd_t *cmd)
{
s1394_cmd_priv_t *s_priv;
s1394_hal_t *hal;
uint32_t IRM_flags;
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) {
(void) s1394_free_cmd(hal, &cmd);
} else {
(void) s1394_free_cmd(hal, &cmd);
if (IRM_flags & GAP_COUNT) {
s1394_initiate_hal_reset(hal, NON_CRITICAL);
}
}
}
int
s1394_lock_tree(s1394_hal_t *hal)
{
ASSERT(MUTEX_NOT_HELD(&hal->br_thread_mutex));
ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
mutex_enter(&hal->br_thread_mutex);
ndi_devi_enter(hal->halinfo.dip);
mutex_enter(&hal->topology_tree_mutex);
if ((hal->br_thread_ev_type & BR_THR_GO_AWAY) != 0) {
mutex_exit(&hal->br_thread_mutex);
mutex_exit(&hal->topology_tree_mutex);
ndi_devi_exit(hal->halinfo.dip);
return (DDI_FAILURE);
} else if (hal->br_cfgrom_read_gen != hal->generation_count) {
mutex_exit(&hal->br_thread_mutex);
mutex_exit(&hal->topology_tree_mutex);
ndi_devi_exit(hal->halinfo.dip);
return (DDI_FAILURE);
}
mutex_exit(&hal->br_thread_mutex);
return (DDI_SUCCESS);
}
void
s1394_unlock_tree(s1394_hal_t *hal)
{
ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
mutex_exit(&hal->topology_tree_mutex);
ndi_devi_exit(hal->halinfo.dip);
}
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));
if (((quadlet + 1) >= node->cfgrom_size) ||
(CFGROM_SIZE_IS_CRCSIZE(node) == B_TRUE &&
(quadlet + 1) >= node->cfgrom_valid_size)) {
return (1);
}
if (s1394_turn_off_dir_stack != 0 || CFGROM_DIR_STACK_OFF(node) ==
B_TRUE) {
quadlet++;
*nextquadp = quadlet;
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);
if (node->expected_dir_quad == quadlet) {
if (type != 0 || key != 0) {
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;
quadlet++;
}
} else {
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;
goto donewithcurdir;
}
ptr = &node->cfgrom[node->cur_dir_start];
CFGROM_TYPE_KEY_VALUE(*ptr, type, key, value);
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);
}
*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);
if (type == IEEE1212_LEAF_TYPE || type ==
IEEE1212_DIRECTORY_TYPE) {
top = ++node->dir_stack_top;
if (top == S1394_DIR_STACK_SIZE) {
SET_CFGROM_DIR_STACK_OFF(node);
*nextquadp =
IEEE1212_ROOT_DIR_QUAD;
return (0);
}
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;
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) {
if (node->dir_stack_top >= 0) {
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 {
return (1);
}
}
} else {
quadlet++;
}
}
*nextquadp = quadlet;
return (0);
}