| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License, Version 1.0 only |
| * (the "License"). You may not use this file except in compliance |
| * with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright (c) 1999-2000 by Sun Microsystems, Inc. |
| * All rights reserved. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| /* |
| * s1394_bus_reset.c |
| * 1394 Services Layer Bus Reset Routines |
| * These routines handle all of the tasks relating to 1394 bus resets |
| */ |
| |
| #include <sys/conf.h> |
| #include <sys/ddi.h> |
| #include <sys/sunddi.h> |
| #include <sys/types.h> |
| #include <sys/kmem.h> |
| #include <sys/tnf_probe.h> |
| |
| #include <sys/1394/t1394.h> |
| #include <sys/1394/s1394.h> |
| #include <sys/1394/h1394.h> |
| #include <sys/1394/ieee1394.h> |
| #include <sys/1394/ieee1212.h> |
| |
| static uint8_t selfid_speed(s1394_selfid_pkt_t *s); |
| |
| static int selfid_num_ports(s1394_selfid_pkt_t *s); |
| |
| static int selfid_port_type(s1394_selfid_pkt_t *s, int port); |
| |
| static void s1394_hal_stack_push(s1394_hal_t *hal, void *o); |
| |
| static void *s1394_hal_stack_pop(s1394_hal_t *hal); |
| |
| static void s1394_hal_queue_insert(s1394_hal_t *hal, void *o); |
| |
| static void *s1394_hal_queue_remove(s1394_hal_t *hal); |
| |
| static void s1394_node_number_list_add(s1394_hal_t *hal, int node_num); |
| |
| static void s1394_speed_map_fill_speed_N(s1394_hal_t *hal, int min_spd); |
| |
| static void s1394_speed_map_initialize(s1394_hal_t *hal); |
| |
| int s1394_ignore_invalid_gap_cnt = 0; /* patch for invalid gap_cnts */ |
| |
| /* |
| * Gap_count look-up table (See IEEE P1394a Table C-2) - Draft 3.0 |
| * (modified from original table IEEE 1394-1995 8.4.6.2) |
| */ |
| static int gap_count[MAX_HOPS + 1] = { |
| 0, 5, 7, 8, 10, 13, 16, 18, 21, |
| 24, 26, 29, 32, 35, 37, 40, 43, |
| 46, 48, 51, 54, 57, 59, 62 |
| }; |
| |
| /* |
| * s1394_parse_selfid_buffer() |
| * takes the SelfID data buffer and parses it, testing whether each packet |
| * is valid (has a correct inverse packet) and setting the pointers in |
| * selfid_ptrs[] to the appropriate offsets within the buffer. |
| */ |
| int |
| s1394_parse_selfid_buffer(s1394_hal_t *hal, void *selfid_buf_addr, |
| uint32_t selfid_size) |
| { |
| s1394_selfid_pkt_t *s; |
| uint32_t *data; |
| uint_t i = 0; |
| uint_t j = 0; |
| boolean_t error = B_FALSE; |
| int valid_pkt_id; |
| |
| TNF_PROBE_0_DEBUG(s1394_parse_selfid_buffer_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| data = (uint32_t *)selfid_buf_addr; |
| |
| if (selfid_size == 0) { |
| TNF_PROBE_1(s1394_parse_selfid_buffer_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "SelfID buffer error - zero size"); |
| |
| /* Initiate a bus reset */ |
| s1394_initiate_hal_reset(hal, CRITICAL); |
| |
| /* Set error status */ |
| error = B_TRUE; |
| |
| /* Release HAL lock and return */ |
| goto parse_buffer_done; |
| } |
| |
| /* Convert bytes to quadlets */ |
| selfid_size = selfid_size >> 2; |
| |
| while (j < selfid_size) { |
| valid_pkt_id = ((data[j] & IEEE1394_SELFID_PCKT_ID_MASK) >> |
| IEEE1394_SELFID_PCKT_ID_SHIFT); |
| |
| s = (s1394_selfid_pkt_t *)(&data[j]); |
| |
| /* Test if packet has valid inverse quadlet */ |
| if (IEEE1394_SELFID_ISVALID(s) && |
| (valid_pkt_id == IEEE1394_SELFID_PCKT_ID_VALID)) { |
| |
| hal->selfid_ptrs[i] = s; |
| |
| /* While this packet contains multiple quadlets */ |
| j += 2; |
| |
| while (IEEE1394_SELFID_ISMORE(s)) { |
| valid_pkt_id = |
| ((data[j] & IEEE1394_SELFID_PCKT_ID_MASK) >> |
| IEEE1394_SELFID_PCKT_ID_SHIFT); |
| |
| s = (s1394_selfid_pkt_t *)(&data[j]); |
| |
| /* Test if packet has valid inverse quadlet */ |
| if (IEEE1394_SELFID_ISVALID(s) && |
| (valid_pkt_id == |
| IEEE1394_SELFID_PCKT_ID_VALID)) { |
| j += 2; |
| } else { |
| TNF_PROBE_1( |
| s1394_parse_selfid_buffer_error, |
| S1394_TNF_SL_BR_ERROR, "", |
| tnf_string, msg, "SelfID packet " |
| "error - invalid inverse"); |
| |
| /* Initiate a bus reset */ |
| s1394_initiate_hal_reset(hal, CRITICAL); |
| |
| /* Set error status */ |
| error = B_TRUE; |
| |
| /* Release HAL lock and return */ |
| goto parse_buffer_done; |
| } |
| } |
| i++; |
| } else { |
| TNF_PROBE_1(s1394_parse_selfid_buffer_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "SelfID packet error - invalid inverse"); |
| |
| /* Initiate a bus reset */ |
| s1394_initiate_hal_reset(hal, CRITICAL); |
| |
| /* Set error status */ |
| error = B_TRUE; |
| |
| /* Release HAL lock and return */ |
| goto parse_buffer_done; |
| } |
| } |
| |
| hal->number_of_nodes = i; |
| |
| parse_buffer_done: |
| TNF_PROBE_0_DEBUG(s1394_parse_selfid_buffer_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| if (error == B_TRUE) |
| return (DDI_FAILURE); |
| else |
| return (DDI_SUCCESS); |
| } |
| |
| /* |
| * s1394_sort_selfids() |
| * takes the selfid_ptrs[] in the HAL struct and sorts them by node number, |
| * using a heapsort. |
| */ |
| void |
| s1394_sort_selfids(s1394_hal_t *hal) |
| { |
| s1394_selfid_pkt_t *current; |
| uint_t number_of_nodes; |
| int i; |
| int j; |
| |
| TNF_PROBE_0_DEBUG(s1394_sort_selfids_enter, S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| number_of_nodes = hal->number_of_nodes; |
| |
| /* We start at one because the root has no parent to check */ |
| for (i = 1; i < number_of_nodes; i++) { |
| current = hal->selfid_ptrs[i]; |
| j = i; |
| while ((j > 0) && (IEEE1394_SELFID_PHYID(current) > |
| IEEE1394_SELFID_PHYID(hal->selfid_ptrs[j / 2]))) { |
| hal->selfid_ptrs[j] = hal->selfid_ptrs[j / 2]; |
| hal->selfid_ptrs[j / 2] = current; |
| j = j / 2; |
| } |
| } |
| |
| for (i = number_of_nodes - 1; i > 0; i--) { |
| current = hal->selfid_ptrs[i]; |
| hal->selfid_ptrs[i] = hal->selfid_ptrs[0]; |
| hal->selfid_ptrs[0] = current; |
| j = 0; |
| while (2 * j + 1 < i) { |
| if (2 * j + 2 >= i) { |
| if (IEEE1394_SELFID_PHYID(current) < |
| IEEE1394_SELFID_PHYID( |
| hal->selfid_ptrs[2 * j + 1])) { |
| hal->selfid_ptrs[j] = |
| hal->selfid_ptrs[2 * j + 1]; |
| hal->selfid_ptrs[2 * j + 1] = current; |
| j = 2 * j + 1; |
| } |
| break; |
| } |
| |
| if (IEEE1394_SELFID_PHYID(hal->selfid_ptrs[2 * j + 1]) > |
| IEEE1394_SELFID_PHYID( |
| hal->selfid_ptrs[2 * j + 2])) { |
| if (IEEE1394_SELFID_PHYID(current) < |
| IEEE1394_SELFID_PHYID( |
| hal->selfid_ptrs[2 * j + 1])) { |
| hal->selfid_ptrs[j] = |
| hal->selfid_ptrs[2 * j + 1]; |
| hal->selfid_ptrs[2 * j + 1] = current; |
| j = 2 * j + 1; |
| } else { |
| break; |
| } |
| } else { |
| if (IEEE1394_SELFID_PHYID(current) < |
| IEEE1394_SELFID_PHYID( |
| hal->selfid_ptrs[2 * j + 2])) { |
| hal->selfid_ptrs[j] = |
| hal->selfid_ptrs[2 * j + 2]; |
| hal->selfid_ptrs[2 * j + 2] = current; |
| j = 2 * j + 2; |
| } else { |
| break; |
| } |
| } |
| } |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_sort_selfids_exit, S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * selfid_speed() |
| * examines the "sp" bits for a given packet (see IEEE 1394-1995 4.3.4.1) |
| * and returns the node's speed capabilities. |
| */ |
| static uint8_t |
| selfid_speed(s1394_selfid_pkt_t *s) |
| { |
| uint32_t sp; |
| |
| sp = ((s->spkt_data & IEEE1394_SELFID_SP_MASK) >> |
| IEEE1394_SELFID_SP_SHIFT); |
| |
| switch (sp) { |
| case IEEE1394_S100: |
| case IEEE1394_S200: |
| case IEEE1394_S400: |
| return (sp); |
| |
| /* |
| * To verify higher speeds we should look at PHY register #3 |
| * on this node. This will need to be done to support P1394b |
| */ |
| default: |
| return (IEEE1394_S400); |
| } |
| } |
| |
| /* |
| * selfid_num_ports() |
| * determines whether a packet is multi-part or single, and from this it |
| * calculates the number of ports which have been specified. |
| * (See IEEE 1394-1995 4.3.4.1) |
| */ |
| static int |
| selfid_num_ports(s1394_selfid_pkt_t *s) |
| { |
| int p = 3; |
| |
| while (IEEE1394_SELFID_ISMORE(s)) { |
| p += 8; |
| s++; |
| } |
| |
| /* Threshold the number of ports at the P1394A defined maximum */ |
| /* (see P1394A Draft 3.0 - Section 8.5.1) */ |
| if (p > IEEE1394_MAX_NUM_PORTS) |
| p = IEEE1394_MAX_NUM_PORTS; |
| |
| return (p); |
| } |
| |
| /* |
| * selfid_port_type() |
| * determines what type of node the specified port connects to. |
| * (See IEEE 1394-1995 4.3.4.1) |
| */ |
| static int |
| selfid_port_type(s1394_selfid_pkt_t *s, int port) |
| { |
| int block; |
| int offset = IEEE1394_SELFID_PORT_OFFSET_FIRST; |
| |
| if (port > selfid_num_ports(s)) { |
| TNF_PROBE_1(selfid_port_type_error, |
| "1394 s1394 error", |
| "Invalid port number requested for node", |
| tnf_uint, node_num, IEEE1394_SELFID_PHYID(s)); |
| } |
| |
| if (port > 2) { |
| /* Calculate which quadlet and bits for this port */ |
| port -= 3; |
| block = (port >> 3) + 1; |
| port = port % 8; |
| /* Move to the correct quadlet */ |
| s += block; |
| offset = IEEE1394_SELFID_PORT_OFFSET_OTHERS; |
| } |
| |
| /* Shift by appropriate number of bits and mask */ |
| return ((s->spkt_data >> (offset - 2 * port)) & 0x00000003); |
| } |
| |
| /* |
| * s1394_init_topology_tree() |
| * frees any config rom's allocated in the topology tree before zapping it. |
| * If it gets a bus reset before the tree is marked processed, there will |
| * be memory allocated for cfgrom's being read. If there is no tree copy, |
| * topology would still be topology tree from the previous generation and |
| * if we bzero'd the tree, we will have a memory leak. To avoid this leak, |
| * walk through the tree and free any config roms in nodes that are NOT |
| * matched. (For matched nodes, we ensure that nodes in old and topology |
| * tree point to the same area of memory.) |
| */ |
| void |
| s1394_init_topology_tree(s1394_hal_t *hal, boolean_t copied, |
| ushort_t number_of_nodes) |
| { |
| s1394_node_t *node; |
| uint32_t *config_rom; |
| uint_t tree_size; |
| int i; |
| |
| TNF_PROBE_0_DEBUG(s1394_init_topology_tree_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| /* |
| * if copied is false, we want to free any cfgrom memory that is |
| * not referenced to in both topology and old trees. However, we |
| * don't use hal->number_of_nodes as the number of nodes to look at. |
| * The reason being we could be seeing the bus reset before the |
| * state is appropriate for a tree copy (which need |
| * toplogy_tree_processed to be true) and some nodes might have |
| * departed in this generation and hal->number_of_nodes reflects |
| * the number of nodes in this generation. Use number_of_nodes that |
| * gets passed into this routine as the actual number of nodes to |
| * look at. |
| */ |
| if (copied == B_FALSE) { |
| /* Free any cfgrom alloced and zap the node */ |
| for (i = 0; i < number_of_nodes; i++) { |
| node = &hal->topology_tree[i]; |
| config_rom = node->cfgrom; |
| if (config_rom != NULL) { |
| if (CFGROM_NEW_ALLOC(node) == B_TRUE) { |
| TNF_PROBE_2_DEBUG( |
| s1394_init_top_tree_free_cfgrom, |
| S1394_TNF_SL_BR_STACK, |
| "cfgrom free", tnf_int, node_num, i, |
| tnf_opaque, cfgrom, config_rom); |
| kmem_free((void *)config_rom, |
| IEEE1394_CONFIG_ROM_SZ); |
| } else { |
| TNF_PROBE_2_DEBUG(s1394_init_top_tree, |
| S1394_TNF_SL_BR_STACK, "", |
| tnf_int, node_num, i, |
| tnf_opaque, cfgrom, config_rom); |
| } |
| } |
| } |
| } |
| |
| tree_size = hal->number_of_nodes * sizeof (s1394_node_t); |
| bzero((void *)hal->topology_tree, tree_size); |
| |
| TNF_PROBE_0_DEBUG(s1394_init_topology_tree_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_topology_tree_build() |
| * takes the selfid_ptrs[] and builds the topology_tree[] by examining |
| * the node numbers (the order in which the nodes responded to SelfID). |
| * It sets the port pointers, leaf label, parent port, and |
| * s1394_selfid_packet_t pointer in each node. |
| */ |
| int |
| s1394_topology_tree_build(s1394_hal_t *hal) |
| { |
| s1394_node_t *tmp; |
| uint32_t number_of_nodes; |
| boolean_t push_to_orphan_stack = B_FALSE; |
| boolean_t found_parent = B_FALSE; |
| boolean_t found_connection = B_FALSE; |
| int i; |
| int j; |
| |
| /* |
| * The method for building the tree is described in IEEE 1394-1995 |
| * (Annex E.3.4). We use an "Orphan" stack to keep track of Child |
| * nodes which have yet to find their Parent node. |
| */ |
| |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_build_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| number_of_nodes = hal->number_of_nodes; |
| |
| /* Flush the Stack */ |
| hal->hal_stack_depth = -1; |
| |
| /* For each node on the bus initialize its topology_tree entry */ |
| for (i = 0; i < number_of_nodes; i++) { |
| /* Make sure that node numbers are correct */ |
| if (i != IEEE1394_SELFID_PHYID(hal->selfid_ptrs[i])) { |
| TNF_PROBE_1(s1394_topology_tree_build_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "SelfIDs - Invalid node numbering"); |
| |
| /* Initiate a bus reset */ |
| s1394_initiate_hal_reset(hal, CRITICAL); |
| |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_build_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (DDI_FAILURE); |
| } |
| |
| hal->topology_tree[i].selfid_packet = hal->selfid_ptrs[i]; |
| hal->topology_tree[i].parent_port = (char)NO_PARENT; |
| hal->topology_tree[i].is_a_leaf = 1; |
| hal->topology_tree[i].node_num = (uchar_t)i; |
| } |
| |
| for (i = 0; i < number_of_nodes; i++) { |
| /* Current node has no parent yet */ |
| found_parent = B_FALSE; |
| |
| /* Current node has no connections yet */ |
| found_connection = B_FALSE; |
| |
| /* Initialize all ports on this node */ |
| for (j = 0; j < IEEE1394_MAX_NUM_PORTS; j++) |
| hal->topology_tree[i].phy_port[j] = NULL; |
| |
| /* For each port on the node - highest to lowest */ |
| for (j = selfid_num_ports(hal->selfid_ptrs[i]) - 1; |
| j >= 0; j--) { |
| if (selfid_port_type(hal->selfid_ptrs[i], j) == |
| IEEE1394_SELFID_PORT_TO_PARENT) { |
| |
| found_connection = B_TRUE; |
| if (found_parent == B_FALSE) { |
| push_to_orphan_stack = B_TRUE; |
| hal->topology_tree[i].parent_port = |
| (char)j; |
| found_parent = B_TRUE; |
| |
| } else { |
| TNF_PROBE_1( |
| s1394_topology_tree_build_error, |
| S1394_TNF_SL_BR_ERROR, "", |
| tnf_string, msg, "SelfID packet - " |
| "Has multiple parents"); |
| |
| /* Initiate a bus reset */ |
| s1394_initiate_hal_reset(hal, CRITICAL); |
| |
| TNF_PROBE_0_DEBUG( |
| s1394_topology_tree_build_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (DDI_FAILURE); |
| } |
| } else if (selfid_port_type(hal->selfid_ptrs[i], j) == |
| IEEE1394_SELFID_PORT_TO_CHILD) { |
| |
| found_connection = B_TRUE; |
| tmp = (s1394_node_t *)s1394_hal_stack_pop(hal); |
| if (tmp == NULL) { |
| TNF_PROBE_1( |
| s1394_topology_tree_build_error, |
| S1394_TNF_SL_BR_ERROR, "", |
| tnf_string, msg, "Topology Tree " |
| "invalid - Tree build failed"); |
| |
| /* Initiate a bus reset */ |
| s1394_initiate_hal_reset(hal, CRITICAL); |
| |
| TNF_PROBE_0_DEBUG( |
| s1394_topology_tree_build_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (DDI_FAILURE); |
| } |
| |
| hal->topology_tree[i].phy_port[j] = tmp; |
| hal->topology_tree[i].is_a_leaf = 0; |
| tmp->phy_port[tmp->parent_port] = |
| &hal->topology_tree[i]; |
| } |
| } |
| |
| /* If current node has no parents or children - Invalid */ |
| if ((found_connection == B_FALSE) && (number_of_nodes > 1)) { |
| TNF_PROBE_1(s1394_topology_tree_build_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "SelfID packet - Has no connections"); |
| |
| /* Initiate a bus reset */ |
| s1394_initiate_hal_reset(hal, CRITICAL); |
| |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_build_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (DDI_FAILURE); |
| } |
| |
| /* Push it on the "Orphan" stack if it has no parent yet */ |
| if (push_to_orphan_stack == B_TRUE) { |
| push_to_orphan_stack = B_FALSE; |
| s1394_hal_stack_push(hal, &hal->topology_tree[i]); |
| } |
| } |
| |
| /* If the stack is not empty, then something has gone seriously wrong */ |
| if (hal->hal_stack_depth != -1) { |
| TNF_PROBE_1(s1394_topology_tree_build_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "Topology Tree invalid - Tree build failed"); |
| |
| /* Initiate a bus reset */ |
| s1394_initiate_hal_reset(hal, CRITICAL); |
| |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_build_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (DDI_FAILURE); |
| } |
| |
| /* New topology tree is now valid */ |
| hal->topology_tree_valid = B_TRUE; |
| |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_build_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (DDI_SUCCESS); |
| } |
| |
| /* |
| * s1394_hal_stack_push() |
| * checks that the stack is not full, and puts the pointer on top of the |
| * HAL's stack if it isn't. This routine is used only by the |
| * h1394_self_ids() interrupt. |
| */ |
| static void |
| s1394_hal_stack_push(s1394_hal_t *hal, void *obj) |
| { |
| TNF_PROBE_0_DEBUG(s1394_hal_stack_push_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| if (hal->hal_stack_depth < IEEE1394_MAX_NODES - 1) { |
| hal->hal_stack_depth++; |
| hal->hal_stack[hal->hal_stack_depth] = obj; |
| } else { |
| TNF_PROBE_1(s1394_hal_stack_push_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "HAL stack - Overflow"); |
| TNF_PROBE_0_DEBUG(s1394_hal_stack_push_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return; |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_hal_stack_push_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_hal_stack_pop() |
| * checks that the stack is not empty, and pops and returns the pointer |
| * from the top of the HAL's stack if it isn't. This routine is used |
| * only by the h1394_self_ids() interrupt. |
| */ |
| static void * |
| s1394_hal_stack_pop(s1394_hal_t *hal) |
| { |
| TNF_PROBE_0_DEBUG(s1394_hal_stack_pop_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| if (hal->hal_stack_depth > -1) { |
| hal->hal_stack_depth--; |
| TNF_PROBE_0_DEBUG(s1394_hal_stack_pop_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (hal->hal_stack[hal->hal_stack_depth + 1]); |
| |
| } else { |
| TNF_PROBE_1(s1394_hal_stack_pop_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "HAL stack - Underflow"); |
| TNF_PROBE_0_DEBUG(s1394_hal_stack_pop_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (NULL); |
| } |
| } |
| |
| /* |
| * s1394_hal_queue_insert() |
| * checks that the queue is not full, and puts the object in the front |
| * of the HAL's queue if it isn't. This routine is used only by the |
| * h1394_self_ids() interrupt. |
| */ |
| static void |
| s1394_hal_queue_insert(s1394_hal_t *hal, void *obj) |
| { |
| TNF_PROBE_0_DEBUG(s1394_hal_queue_insert_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| if (((hal->hal_queue_front + 1) % IEEE1394_MAX_NODES) == |
| hal->hal_queue_back) { |
| TNF_PROBE_1(s1394_hal_queue_insert_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "HAL Queue - Overflow"); |
| TNF_PROBE_0_DEBUG(s1394_hal_queue_insert_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return; |
| |
| } else { |
| hal->hal_queue[hal->hal_queue_front] = obj; |
| hal->hal_queue_front = (hal->hal_queue_front + 1) % |
| IEEE1394_MAX_NODES; |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_hal_queue_insert_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| |
| /* |
| * s1394_hal_queue_remove() |
| * checks that the queue is not empty, and pulls the object off the back |
| * of the HAL's queue (and returns it) if it isn't. This routine is used |
| * only by the h1394_self_ids() interrupt. |
| */ |
| static void * |
| s1394_hal_queue_remove(s1394_hal_t *hal) |
| { |
| void *tmp; |
| |
| TNF_PROBE_0_DEBUG(s1394_hal_queue_remove_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| if (hal->hal_queue_back == hal->hal_queue_front) { |
| TNF_PROBE_1(s1394_hal_queue_remove_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "HAL Queue - Underflow"); |
| TNF_PROBE_0_DEBUG(s1394_hal_queue_remove_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (NULL); |
| |
| } else { |
| tmp = hal->hal_queue[hal->hal_queue_back]; |
| hal->hal_queue_back = (hal->hal_queue_back + 1) % |
| IEEE1394_MAX_NODES; |
| TNF_PROBE_0_DEBUG(s1394_hal_queue_remove_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (tmp); |
| } |
| } |
| |
| |
| /* |
| * s1394_node_number_list_add() |
| * checks that the node_number_list is not full and puts the node number |
| * in the list. The function is used primarily by s1394_speed_map_fill() |
| * to keep track of which connections need to be set in the speed_map[]. |
| * This routine is used only by the h1394_self_ids() interrupt. |
| */ |
| static void |
| s1394_node_number_list_add(s1394_hal_t *hal, int node_num) |
| { |
| TNF_PROBE_1_DEBUG(s1394_node_number_list_add_enter, |
| S1394_TNF_SL_BR_STACK, "", tnf_int, node_num, node_num); |
| |
| if (hal->hal_node_number_list_size >= IEEE1394_MAX_NODES - 1) { |
| TNF_PROBE_1(s1394_node_number_list_add_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "Node Number List - Overflow"); |
| TNF_PROBE_0_DEBUG(s1394_node_number_list_add_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return; |
| } |
| |
| hal->hal_node_number_list[hal->hal_node_number_list_size] = node_num; |
| hal->hal_node_number_list_size++; |
| |
| TNF_PROBE_0_DEBUG(s1394_node_number_list_add_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_topology_tree_mark_all_unvisited() |
| * is used to initialize the topology_tree[] prior to tree traversals. |
| * It resets the "visited" flag for each node in the tree. |
| */ |
| void |
| s1394_topology_tree_mark_all_unvisited(s1394_hal_t *hal) |
| { |
| uint_t number_of_nodes; |
| int i; |
| |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_mark_all_unvisited_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| number_of_nodes = hal->number_of_nodes; |
| for (i = 0; i < number_of_nodes; i++) |
| CLEAR_NODE_VISITED(&hal->topology_tree[i]); |
| |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_mark_all_unvisited_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_old_tree_mark_all_unvisited() |
| * is used to initialize the old_tree[] prior to tree traversals. It |
| * resets the "visited" flag for each node in the tree. |
| */ |
| void |
| s1394_old_tree_mark_all_unvisited(s1394_hal_t *hal) |
| { |
| uint_t number_of_nodes; |
| int i; |
| |
| TNF_PROBE_0_DEBUG(s1394_old_tree_mark_all_unvisited_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| number_of_nodes = hal->old_number_of_nodes; |
| for (i = 0; i < number_of_nodes; i++) |
| CLEAR_NODE_VISITED(&hal->old_tree[i]); |
| |
| TNF_PROBE_0_DEBUG(s1394_old_tree_mark_all_unvisited_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_old_tree_mark_all_unmatched() |
| * is used to initialize the old_tree[] prior to tree traversals. It |
| * resets the "matched" flag for each node in the tree. |
| */ |
| void |
| s1394_old_tree_mark_all_unmatched(s1394_hal_t *hal) |
| { |
| uint_t number_of_nodes; |
| int i; |
| |
| TNF_PROBE_0_DEBUG(s1394_old_tree_mark_all_unmatched_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| number_of_nodes = hal->old_number_of_nodes; |
| |
| for (i = 0; i < number_of_nodes; i++) |
| CLEAR_NODE_MATCHED(&hal->old_tree[i]); |
| |
| TNF_PROBE_0_DEBUG(s1394_old_tree_mark_all_unmatched_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_copy_old_tree() |
| * switches the pointers for old_tree[] and topology_tree[]. |
| */ |
| void |
| s1394_copy_old_tree(s1394_hal_t *hal) |
| { |
| s1394_node_t *temp; |
| |
| TNF_PROBE_0_DEBUG(s1394_copy_old_tree_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| temp = hal->old_tree; |
| hal->old_tree = hal->topology_tree; |
| hal->topology_tree = temp; |
| |
| hal->old_number_of_nodes = hal->number_of_nodes; |
| hal->old_node_id = hal->node_id; |
| hal->old_generation_count = hal->generation_count; |
| |
| /* Old tree is now valid and filled also */ |
| hal->old_tree_valid = B_TRUE; |
| |
| TNF_PROBE_0_DEBUG(s1394_copy_old_tree_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| |
| /* |
| * s1394_match_tree_nodes() |
| * uses the information contained in the SelfID packets of the nodes in |
| * both the old_tree[] and the topology_tree[] to determine which new |
| * nodes correspond to old nodes. Starting with the local node, we |
| * compare both old and new node's ports. Assuming that only one bus |
| * reset has occurred, any node that was connected to another in the old |
| * bus and is still connected to another in the new bus must be connected |
| * (physically) to the same node. Using this information, we can rebuild |
| * and match the old nodes to new ones. Any nodes which aren't matched |
| * are either departing or arriving nodes and must be handled appropriately. |
| */ |
| void |
| s1394_match_tree_nodes(s1394_hal_t *hal) |
| { |
| s1394_node_t *tmp; |
| uint_t hal_node_num; |
| uint_t hal_node_num_old; |
| int i; |
| int port_type; |
| |
| TNF_PROBE_0_DEBUG(s1394_match_tree_nodes_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| /* To ensure that the queue is empty */ |
| hal->hal_queue_front = hal->hal_queue_back = 0; |
| |
| /* Set up the first matched nodes (which are our own local nodes) */ |
| hal_node_num = IEEE1394_NODE_NUM(hal->node_id); |
| hal_node_num_old = IEEE1394_NODE_NUM(hal->old_node_id); |
| hal->topology_tree[hal_node_num].old_node = |
| &hal->old_tree[hal_node_num_old]; |
| hal->old_tree[hal_node_num_old].cur_node = |
| &hal->topology_tree[hal_node_num]; |
| |
| /* Put the node on the queue */ |
| s1394_hal_queue_insert(hal, &hal->topology_tree[hal_node_num]); |
| |
| /* While the queue is not empty, remove a node */ |
| while (hal->hal_queue_front != hal->hal_queue_back) { |
| tmp = (s1394_node_t *)s1394_hal_queue_remove(hal); |
| |
| /* Mark both old and new nodes as "visited" */ |
| SET_NODE_VISITED(tmp); |
| SET_NODE_VISITED(tmp->old_node); |
| tmp->old_node->cur_node = tmp; |
| |
| /* Mark old and new nodes as "matched" */ |
| SET_NODE_MATCHED(tmp); |
| SET_NODE_MATCHED(tmp->old_node); |
| s1394_copy_cfgrom(tmp, tmp->old_node); |
| |
| /* s1394_copy_cfgrom() clears "matched" for some cases... */ |
| if ((tmp->cfgrom != NULL && CONFIG_ROM_GEN(tmp->cfgrom) <= 1) || |
| NODE_MATCHED(tmp) == B_TRUE) { |
| /* Move the target list over to the new node and update */ |
| /* the node info. */ |
| s1394_target_t *t; |
| |
| rw_enter(&hal->target_list_rwlock, RW_WRITER); |
| t = tmp->target_list = tmp->old_node->target_list; |
| while (t != NULL) { |
| t->on_node = tmp; |
| t = t->target_sibling; |
| } |
| rw_exit(&hal->target_list_rwlock); |
| } |
| |
| for (i = 0; i < selfid_num_ports(tmp->selfid_packet); i++) { |
| port_type = selfid_port_type(tmp->selfid_packet, i); |
| |
| /* Is the new port connected? */ |
| if ((port_type == IEEE1394_SELFID_PORT_TO_CHILD) || |
| (port_type == IEEE1394_SELFID_PORT_TO_PARENT)) { |
| port_type = selfid_port_type( |
| tmp->old_node->selfid_packet, i); |
| |
| /* Is the old port connected? */ |
| if ((port_type == |
| IEEE1394_SELFID_PORT_TO_CHILD) || |
| (port_type == |
| IEEE1394_SELFID_PORT_TO_PARENT)) { |
| /* Found a match, check if */ |
| /* we've already visited it */ |
| if (!NODE_VISITED(tmp->phy_port[i])) { |
| tmp->phy_port[i]->old_node = |
| tmp->old_node->phy_port[i]; |
| s1394_hal_queue_insert(hal, |
| tmp->phy_port[i]); |
| } |
| } |
| } |
| } |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_match_tree_nodes_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_topology_tree_calculate_diameter() |
| * does a depth-first tree traversal, tracking at each branch the first |
| * and second deepest paths though that branch's children. The diameter |
| * is given by the maximum of these over all branch nodes |
| */ |
| int |
| s1394_topology_tree_calculate_diameter(s1394_hal_t *hal) |
| { |
| s1394_node_t *current; |
| uint_t number_of_nodes; |
| int i; |
| int start; |
| int end; |
| boolean_t done; |
| boolean_t found_a_child; |
| int distance = 0; |
| int diameter = 0; |
| int local_diameter = 0; |
| |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_calculate_diameter_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| number_of_nodes = hal->number_of_nodes; |
| |
| /* Initialize topology tree */ |
| for (i = 0; i < number_of_nodes; i++) { |
| hal->topology_tree[i].max_1st = 0; |
| hal->topology_tree[i].max_2nd = 0; |
| hal->topology_tree[i].last_port_checked = 0; |
| } |
| |
| /* Start at the root node */ |
| current = s1394_topology_tree_get_root_node(hal); |
| |
| /* Flush the stack before we start */ |
| hal->hal_stack_depth = -1; |
| |
| do { |
| done = B_FALSE; |
| found_a_child = B_FALSE; |
| start = current->last_port_checked; |
| end = selfid_num_ports(current->selfid_packet); |
| |
| /* Check every previously unchecked port for children */ |
| for (i = start; i < end; i++) { |
| current->last_port_checked++; |
| /* If there is a child push it on the stack */ |
| if (selfid_port_type(current->selfid_packet, i) == |
| IEEE1394_SELFID_PORT_TO_CHILD) { |
| found_a_child = B_TRUE; |
| s1394_hal_stack_push(hal, current); |
| current = current->phy_port[i]; |
| break; |
| } |
| } |
| |
| /* If we reach here and the stack is empty, we're done */ |
| if (hal->hal_stack_depth == -1) { |
| done = B_TRUE; |
| continue; |
| } |
| |
| /* If no children were found, we're at a leaf */ |
| if (found_a_child == B_FALSE) { |
| distance = current->max_1st + 1; |
| /* Pop the child and set the appropriate fields */ |
| current = s1394_hal_stack_pop(hal); |
| if (distance > current->max_1st) { |
| current->max_2nd = current->max_1st; |
| current->max_1st = (uchar_t)distance; |
| |
| } else if (distance > current->max_2nd) { |
| current->max_2nd = (uchar_t)distance; |
| } |
| |
| /* Update maximum distance (diameter), if necessary */ |
| local_diameter = current->max_1st + current->max_2nd; |
| if (local_diameter > diameter) |
| diameter = local_diameter; |
| } |
| } while (done == B_FALSE); |
| |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_calculate_diameter_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (diameter); |
| } |
| |
| /* |
| * s1394_gap_count_optimize() |
| * looks in a table to find the appropriate gap_count for a given diameter. |
| * (See above - gap_count[]) |
| */ |
| int |
| s1394_gap_count_optimize(int diameter) |
| { |
| if ((diameter >= 0) && (diameter <= MAX_HOPS)) { |
| return (gap_count[diameter]); |
| } else { |
| cmn_err(CE_NOTE, "Too may point-to-point links on the 1394" |
| " bus - If new devices have recently been added, remove" |
| " them."); |
| return (gap_count[MAX_HOPS]); |
| } |
| } |
| |
| /* |
| * s1394_get_current_gap_count() |
| * looks at all the SelfID packets to determine the current gap_count on |
| * the 1394 bus. If the gap_counts differ from node to node, it initiates |
| * a bus reset and returns -1. |
| */ |
| int |
| s1394_get_current_gap_count(s1394_hal_t *hal) |
| { |
| int i; |
| int gap_count = -1; |
| |
| TNF_PROBE_0_DEBUG(s1394_get_current_gap_count_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| /* Grab the first gap_count in the SelfID packets */ |
| gap_count = IEEE1394_SELFID_GAP_CNT(hal->selfid_ptrs[0]); |
| |
| /* Compare it too all the rest */ |
| for (i = 1; i < hal->number_of_nodes; i++) { |
| if (gap_count != |
| IEEE1394_SELFID_GAP_CNT(hal->selfid_ptrs[i])) { |
| |
| /* Inconsistent gap counts */ |
| TNF_PROBE_1(s1394_get_current_gap_count_error, |
| S1394_TNF_SL_BR_ERROR, "", tnf_string, msg, |
| "Inconsistent gap count"); |
| |
| if (s1394_ignore_invalid_gap_cnt == 0) { |
| /* Initiate a bus reset */ |
| s1394_initiate_hal_reset(hal, CRITICAL); |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_get_current_gap_count_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (-1); |
| } |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_get_current_gap_count_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (gap_count); |
| } |
| |
| /* |
| * s1394_speed_map_fill() |
| * determines, for each pair of nodes, the maximum speed at which those |
| * nodes can communicate. The speed of each node as well as the speed of |
| * any intermediate nodes on a given path must be accounted for, as the |
| * minimum speed on a given edge determines the maximum speed for all |
| * communications across that edge. |
| * In the method we implement below, a current minimum speed is selected. |
| * With this minimum speed in mind, we create subgraphs of the original |
| * bus which contain only edges that connect two nodes whose speeds are |
| * equal to or greater than the current minimum speed. Then, for each of |
| * the subgraphs, we visit every node, keeping a list of the nodes we've |
| * visited. When this list is completed, we can fill in the entries in |
| * the speed map which correspond to a pairs of these nodes. Doing this |
| * for each subgraph and then for each speed we progressively fill in the |
| * parts of the speed map which weren't previously filled in. |
| */ |
| void |
| s1394_speed_map_fill(s1394_hal_t *hal) |
| { |
| uint_t number_of_nodes; |
| int i; |
| int j; |
| int node_num; |
| |
| TNF_PROBE_0_DEBUG(s1394_speed_map_fill_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| number_of_nodes = hal->number_of_nodes; |
| s1394_speed_map_initialize(hal); |
| |
| /* Mark all speed = IEEE1394_S100 nodes in the Speed Map */ |
| for (i = 0; i < number_of_nodes; i++) { |
| if (selfid_speed(hal->topology_tree[i].selfid_packet) == |
| IEEE1394_S100) { |
| hal->slowest_node_speed = IEEE1394_S100; |
| node_num = IEEE1394_SELFID_PHYID( |
| hal->topology_tree[i].selfid_packet); |
| for (j = 0; j < number_of_nodes; j++) { |
| if (j != node_num) { |
| hal->speed_map[node_num][j] = |
| IEEE1394_S100; |
| hal->speed_map[j][node_num] = |
| IEEE1394_S100; |
| } |
| } |
| } |
| } |
| |
| s1394_speed_map_fill_speed_N(hal, IEEE1394_S200); |
| s1394_speed_map_fill_speed_N(hal, IEEE1394_S400); |
| |
| /* Fill in the diagonal */ |
| for (i = 0; i < number_of_nodes; i++) { |
| hal->speed_map[i][i] = |
| selfid_speed(hal->topology_tree[i].selfid_packet); |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_speed_map_fill_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_speed_map_fill_speed_N(), |
| * given a minimum link speed, creates subgraphs of the original bus which |
| * contain only the necessary edges (see speed_map_fill() above). For each |
| * of the subgraphs, it visits and fills in the entries in the speed map |
| * which correspond to a pair of these nodes. |
| */ |
| static void |
| s1394_speed_map_fill_speed_N(s1394_hal_t *hal, int min_spd) |
| { |
| s1394_node_t *tmp; |
| uint_t number_of_nodes; |
| int i; |
| int j; |
| int k; |
| int size; |
| int ix_a, ix_b; |
| |
| TNF_PROBE_0_DEBUG(s1394_speed_map_fill_speed_N_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| number_of_nodes = hal->number_of_nodes; |
| |
| /* Prepare the topology tree */ |
| s1394_topology_tree_mark_all_unvisited(hal); |
| |
| /* To ensure that the queue is empty */ |
| hal->hal_queue_front = hal->hal_queue_back = 0; |
| |
| for (i = 0; i < number_of_nodes; i++) { |
| /* If the node's speed == min_spd and it hasn't been visited */ |
| if (!NODE_VISITED(&hal->topology_tree[i]) && |
| (selfid_speed(hal->topology_tree[i].selfid_packet) == |
| min_spd)) { |
| |
| if (min_spd < hal->slowest_node_speed) |
| hal->slowest_node_speed = (uint8_t)min_spd; |
| |
| SET_NODE_VISITED(&hal->topology_tree[i]); |
| s1394_hal_queue_insert(hal, &hal->topology_tree[i]); |
| |
| while (hal->hal_queue_front != hal->hal_queue_back) { |
| tmp = (s1394_node_t *)s1394_hal_queue_remove( |
| hal); |
| /* Add node number to the list */ |
| s1394_node_number_list_add(hal, |
| IEEE1394_SELFID_PHYID(tmp->selfid_packet)); |
| |
| for (j = 0; j < IEEE1394_MAX_NUM_PORTS; j++) { |
| if ((tmp->phy_port[j] != NULL) && |
| (!NODE_VISITED(tmp->phy_port[j]))) { |
| if (selfid_speed( |
| tmp->phy_port[j]-> |
| selfid_packet) >= min_spd) { |
| SET_NODE_VISITED( |
| tmp->phy_port[j]); |
| s1394_hal_queue_insert( |
| hal, |
| tmp->phy_port[j]); |
| } |
| } |
| } |
| } |
| |
| /* For each pair, mark speed_map as min_spd */ |
| size = hal->hal_node_number_list_size; |
| for (j = 0; j < size; j++) { |
| for (k = 0; k < size; k++) { |
| if (j != k) { |
| ix_a = hal-> |
| hal_node_number_list[j]; |
| ix_b = hal-> |
| hal_node_number_list[k]; |
| hal->speed_map[ix_a][ix_b] = |
| (uint8_t)min_spd; |
| } |
| } |
| } |
| |
| /* Flush the Node Number List */ |
| hal->hal_node_number_list_size = 0; |
| } |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_speed_map_fill_speed_N_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_speed_map_initialize() |
| * fills in the speed_map with IEEE1394_S100's and SPEED_MAP_INVALID's in |
| * the appropriate places. These will be overwritten by |
| * s1394_speed_map_fill(). |
| */ |
| static void |
| s1394_speed_map_initialize(s1394_hal_t *hal) |
| { |
| uint_t number_of_nodes; |
| int i, j; |
| |
| TNF_PROBE_0_DEBUG(s1394_speed_map_initialize_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| number_of_nodes = hal->number_of_nodes; |
| for (i = 0; i < number_of_nodes; i++) { |
| for (j = 0; j < number_of_nodes; j++) { |
| if (i != j) |
| hal->speed_map[i][j] = IEEE1394_S100; |
| else |
| hal->speed_map[i][j] = SPEED_MAP_INVALID; |
| } |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_speed_map_initialize_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_speed_map_get() |
| * queries the speed_map[] for a given pair of nodes. |
| */ |
| uint8_t |
| s1394_speed_map_get(s1394_hal_t *hal, uint_t from_node, uint_t to_node) |
| { |
| /* If it's not a valid node, then return slowest_node_speed */ |
| if (to_node >= hal->number_of_nodes) { |
| /* Send at fastest speed everyone will see */ |
| return (hal->slowest_node_speed); |
| } |
| /* else return the correct maximum speed */ |
| return (hal->speed_map[from_node][to_node]); |
| } |
| |
| /* |
| * s1394_update_speed_map_link_speeds() |
| * takes into account information from Config ROM queries. Any P1394A |
| * device can have a link with a different speed than its PHY. In this |
| * case, the slower speed must be accounted for in order for communication |
| * with the remote node to work. |
| */ |
| void |
| s1394_update_speed_map_link_speeds(s1394_hal_t *hal) |
| { |
| uint32_t bus_capabilities; |
| uint8_t link_speed; |
| uint_t number_of_nodes; |
| int i, j; |
| |
| TNF_PROBE_0_DEBUG(s1394_update_speed_map_link_speeds_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| number_of_nodes = hal->number_of_nodes; |
| |
| for (i = 0; i < number_of_nodes; i++) { |
| |
| /* Skip invalid config ROMs */ |
| if (CFGROM_VALID(&hal->topology_tree[i])) { |
| |
| ASSERT(hal->topology_tree[i].cfgrom); |
| |
| bus_capabilities = hal->topology_tree[i]. |
| cfgrom[IEEE1212_NODE_CAP_QUAD]; |
| |
| /* Skip if Bus_Info_Block generation is 0 */ |
| /* because it isn't a P1394a device */ |
| if ((bus_capabilities & IEEE1394_BIB_GEN_MASK) != 0) { |
| link_speed = (bus_capabilities & |
| IEEE1394_BIB_LNK_SPD_MASK); |
| |
| for (j = 0; j < number_of_nodes; j++) { |
| /* Update if link_speed is slower */ |
| if (hal->speed_map[i][j] > link_speed) { |
| hal->speed_map[i][j] = |
| link_speed; |
| hal->speed_map[j][i] = |
| link_speed; |
| } |
| |
| if (link_speed < |
| hal->slowest_node_speed) |
| hal->slowest_node_speed = |
| link_speed; |
| } |
| } |
| } |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_update_speed_map_link_speeds_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_get_isoch_rsrc_mgr() |
| * looks at the SelfID packets to determine the Isochronous Resource |
| * Manager's node ID. The IRM is the highest numbered node with both |
| * the "L"-bit and the "C"-bit in its SelfID packets turned on. If no |
| * IRM is found on the bus, then -1 is returned. |
| */ |
| int |
| s1394_get_isoch_rsrc_mgr(s1394_hal_t *hal) |
| { |
| int i; |
| |
| TNF_PROBE_0_DEBUG(s1394_get_isoch_rsrc_mgr_enter, S1394_TNF_SL_BR_STACK, |
| ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| for (i = hal->number_of_nodes - 1; i >= 0; i--) { |
| /* Highest numbered node with L=1 and C=1 */ |
| if ((IEEE1394_SELFID_ISLINKON(hal->selfid_ptrs[i])) && |
| (IEEE1394_SELFID_ISCONTENDER(hal->selfid_ptrs[i]))) { |
| |
| TNF_PROBE_0_DEBUG(s1394_get_isoch_rsrc_mgr_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| return (i); |
| } |
| } |
| |
| /* No Isochronous Resource Manager */ |
| TNF_PROBE_0_DEBUG(s1394_get_isoch_rsrc_mgr_exit, S1394_TNF_SL_BR_STACK, |
| ""); |
| return (-1); |
| } |
| |
| /* |
| * s1394_physical_arreq_setup_all() |
| * is used to enable the physical filters for the link. If a target has |
| * registered physical space allocations, then the corresponding node's |
| * bit is set. This is done for all targets on a HAL (usually after bus |
| * reset). |
| */ |
| void |
| s1394_physical_arreq_setup_all(s1394_hal_t *hal) |
| { |
| s1394_target_t *curr_target; |
| uint64_t mask = 0; |
| uint32_t node_num; |
| uint_t generation; |
| |
| TNF_PROBE_0_DEBUG(s1394_physical_arreq_setup_all_enter, |
| S1394_TNF_SL_BR_STACK, ""); |
| |
| ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex)); |
| |
| mutex_enter(&hal->topology_tree_mutex); |
| generation = hal->generation_count; |
| rw_enter(&hal->target_list_rwlock, RW_READER); |
| |
| curr_target = hal->target_head; |
| while (curr_target != NULL) { |
| if ((curr_target->on_node != NULL) && |
| (curr_target->physical_arreq_enabled != 0)) { |
| node_num = curr_target->on_node->node_num; |
| mask = mask | (1 << node_num); |
| } |
| curr_target = curr_target->target_next; |
| } |
| rw_exit(&hal->target_list_rwlock); |
| mutex_exit(&hal->topology_tree_mutex); |
| |
| /* |
| * Since it is cleared to 0 on bus reset, set the bits for all |
| * nodes. This call returns DDI_FAILURE if the generation passed |
| * is invalid or if the HAL is shutdown. In either case, it is |
| * acceptable to simply ignore the result and return. |
| */ |
| (void) HAL_CALL(hal).physical_arreq_enable_set( |
| hal->halinfo.hal_private, mask, generation); |
| |
| TNF_PROBE_0_DEBUG(s1394_physical_arreq_setup_all_exit, |
| S1394_TNF_SL_BR_STACK, ""); |
| } |
| |
| /* |
| * s1394_physical_arreq_set_one() |
| * is used to enable the physical filters for the link. If a target has |
| * registered physical space allocations, then the corresponding node's |
| * bit is set. This is done for one target. |
| */ |
| void |
| s1394_physical_arreq_set_one(s1394_target_t *target) |
| { |
| s1394_hal_t *hal; |
| uint64_t mask = 0; |
| uint32_t node_num; |
| uint_t generation; |
| |
| TNF_PROBE_0_DEBUG(s1394_physical_arreq_set_one_enter, |
| S1394_TNF_SL_STACK, ""); |
| |
| /* Find the HAL this target resides on */ |
| hal = target->on_hal; |
| |
| ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex)); |
| |
| mutex_enter(&hal->topology_tree_mutex); |
| rw_enter(&hal->target_list_rwlock, RW_READER); |
| |
| if ((target->on_node != NULL) && |
| (target->physical_arreq_enabled != 0)) { |
| node_num = target->on_node->node_num; |
| mask = mask | (1 << node_num); |
| |
| generation = hal->generation_count; |
| |
| rw_exit(&hal->target_list_rwlock); |
| mutex_exit(&hal->topology_tree_mutex); |
| |
| /* |
| * Set the bit corresponding to this node. This call |
| * returns DDI_FAILURE if the generation passed |
| * is invalid or if the HAL is shutdown. In either case, |
| * it is acceptable to simply ignore the result and return. |
| */ |
| (void) HAL_CALL(hal).physical_arreq_enable_set( |
| hal->halinfo.hal_private, mask, generation); |
| } else { |
| rw_exit(&hal->target_list_rwlock); |
| mutex_exit(&hal->topology_tree_mutex); |
| } |
| |
| TNF_PROBE_0_DEBUG(s1394_physical_arreq_set_one_exit, |
| S1394_TNF_SL_STACK, ""); |
| } |
| |
| /* |
| * s1394_physical_arreq_clear_one() |
| * is used to disable the physical filters for OpenHCI. If a target frees |
| * up the last of its registered physical space, then the corresponding |
| * node's bit is cleared. This is done for one target. |
| */ |
| void |
| s1394_physical_arreq_clear_one(s1394_target_t *target) |
| { |
| s1394_hal_t *hal; |
| uint64_t mask = 0; |
| uint32_t node_num; |
| uint_t generation; |
| |
| TNF_PROBE_0_DEBUG(s1394_physical_arreq_clear_one_enter, |
| S1394_TNF_SL_STACK, ""); |
| |
| /* Find the HAL this target resides on */ |
| hal = target->on_hal; |
| |
| ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex)); |
| |
| mutex_enter(&hal->topology_tree_mutex); |
| rw_enter(&hal->target_list_rwlock, RW_READER); |
| |
| if ((target->on_node != NULL) && |
| (target->physical_arreq_enabled == 0)) { |
| node_num = target->on_node->node_num; |
| mask = mask | (1 << node_num); |
| |
| generation = hal->generation_count; |
| |
| rw_exit(&hal->target_list_rwlock); |
| mutex_exit(&hal->topology_tree_mutex); |
| |
| /* |
| * Set the bit corresponding to this node. This call |
| * returns DDI_FAILURE if the generation passed |
| * is invalid or if the HAL is shutdown. In either case, |
| * it is acceptable to simply ignore the result and return. |
| */ |
| (void) HAL_CALL(hal).physical_arreq_enable_clr( |
| hal->halinfo.hal_private, mask, generation); |
| } else { |
| rw_exit(&hal->target_list_rwlock); |
| mutex_exit(&hal->topology_tree_mutex); |
| } |
| |
| |
| TNF_PROBE_0_DEBUG(s1394_physical_arreq_clear_one_exit, |
| S1394_TNF_SL_STACK, ""); |
| } |
| |
| /* |
| * s1394_topology_tree_get_root_node() |
| * returns the last entry in topology_tree[] as this must always be the |
| * root node. |
| */ |
| s1394_node_t * |
| s1394_topology_tree_get_root_node(s1394_hal_t *hal) |
| { |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_get_root_node_enter, |
| S1394_TNF_SL_STACK, ""); |
| |
| ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); |
| |
| TNF_PROBE_0_DEBUG(s1394_topology_tree_get_root_node_exit, |
| S1394_TNF_SL_STACK, ""); |
| |
| return (&hal->topology_tree[hal->number_of_nodes - 1]); |
| } |