/* * 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. */ /* * s1394_bus_reset.c * 1394 Services Layer Bus Reset Routines * These routines handle all of the tasks relating to 1394 bus resets */ #include #include #include #include #include #include #include #include #include #include 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; ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); data = (uint32_t *)selfid_buf_addr; if (selfid_size == 0) { /* 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 { /* 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 { /* 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: 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; 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; } } } } } /* * 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 > 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; 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) { kmem_free((void *)config_rom, IEEE1394_CONFIG_ROM_SZ); } } } } tree_size = hal->number_of_nodes * sizeof (s1394_node_t); bzero((void *)hal->topology_tree, tree_size); } /* * 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. */ 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])) { /* Initiate a bus reset */ s1394_initiate_hal_reset(hal, CRITICAL); 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 { /* Initiate a bus reset */ s1394_initiate_hal_reset(hal, CRITICAL); 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) { /* Initiate a bus reset */ s1394_initiate_hal_reset(hal, CRITICAL); 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)) { /* Initiate a bus reset */ s1394_initiate_hal_reset(hal, CRITICAL); 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) { /* Initiate a bus reset */ s1394_initiate_hal_reset(hal, CRITICAL); return (DDI_FAILURE); } /* New topology tree is now valid */ hal->topology_tree_valid = B_TRUE; 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) { if (hal->hal_stack_depth < IEEE1394_MAX_NODES - 1) { hal->hal_stack_depth++; hal->hal_stack[hal->hal_stack_depth] = obj; } } /* * 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) { if (hal->hal_stack_depth > -1) { hal->hal_stack_depth--; return (hal->hal_stack[hal->hal_stack_depth + 1]); } else { 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) { if (((hal->hal_queue_front + 1) % IEEE1394_MAX_NODES) == hal->hal_queue_back) { return; } else { hal->hal_queue[hal->hal_queue_front] = obj; hal->hal_queue_front = (hal->hal_queue_front + 1) % IEEE1394_MAX_NODES; } } /* * 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; if (hal->hal_queue_back == hal->hal_queue_front) { return (NULL); } else { tmp = hal->hal_queue[hal->hal_queue_back]; hal->hal_queue_back = (hal->hal_queue_back + 1) % IEEE1394_MAX_NODES; 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) { if (hal->hal_node_number_list_size >= IEEE1394_MAX_NODES - 1) { return; } hal->hal_node_number_list[hal->hal_node_number_list_size] = node_num; hal->hal_node_number_list_size++; } /* * 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; 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]); } /* * 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; 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]); } /* * 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; 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]); } /* * 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; 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; } /* * 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; 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]); } } } } } } /* * 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; 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); 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; 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 */ if (s1394_ignore_invalid_gap_cnt == 0) { /* Initiate a bus reset */ s1394_initiate_hal_reset(hal, CRITICAL); } return (-1); } } 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; 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); } } /* * 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; 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; } } } /* * 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; 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; } } } /* * 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; 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; } } } } } /* * 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; 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]))) { return (i); } } /* No Isochronous Resource Manager */ 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; 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); } /* * 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; /* 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); } } /* * 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; /* 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); } } /* * 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) { ASSERT(MUTEX_HELD(&hal->topology_tree_mutex)); return (&hal->topology_tree[hal->number_of_nodes - 1]); }