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