/*
* 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
* 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"
/*
* 1394 Services Layer Bus Reset Routines
* These routines handle all of the tasks relating to 1394 bus resets
*/
#include <sys/tnf_probe.h>
static int selfid_num_ports(s1394_selfid_pkt_t *s);
/*
* 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)
*/
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
{
uint_t i = 0;
uint_t j = 0;
int valid_pkt_id;
S1394_TNF_SL_BR_STACK, "");
if (selfid_size == 0) {
"SelfID buffer error - zero size");
/* Initiate a bus reset */
/* Set error status */
/* Release HAL lock and return */
goto parse_buffer_done;
}
/* Convert bytes to quadlets */
while (j < selfid_size) {
s = (s1394_selfid_pkt_t *)(&data[j]);
/* Test if packet has valid inverse quadlet */
if (IEEE1394_SELFID_ISVALID(s) &&
hal->selfid_ptrs[i] = s;
/* While this packet contains multiple quadlets */
j += 2;
while (IEEE1394_SELFID_ISMORE(s)) {
((data[j] & IEEE1394_SELFID_PCKT_ID_MASK) >>
s = (s1394_selfid_pkt_t *)(&data[j]);
/* Test if packet has valid inverse quadlet */
if (IEEE1394_SELFID_ISVALID(s) &&
(valid_pkt_id ==
j += 2;
} else {
"error - invalid inverse");
/* Initiate a bus reset */
/* Set error status */
/* Release HAL lock and return */
goto parse_buffer_done;
}
}
i++;
} else {
"SelfID packet error - invalid inverse");
/* Initiate a bus reset */
/* Set error status */
/* Release HAL lock and return */
goto parse_buffer_done;
}
}
hal->number_of_nodes = i;
S1394_TNF_SL_BR_STACK, "");
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
{
int i;
int j;
/* We start at one because the root has no parent to check */
for (i = 1; i < number_of_nodes; i++) {
j = i;
while ((j > 0) && (IEEE1394_SELFID_PHYID(current) >
j = j / 2;
}
}
for (i = number_of_nodes - 1; i > 0; i--) {
j = 0;
while (2 * j + 1 < i) {
if (2 * j + 2 >= i) {
if (IEEE1394_SELFID_PHYID(current) <
hal->selfid_ptrs[j] =
j = 2 * j + 1;
}
break;
}
if (IEEE1394_SELFID_PHYID(current) <
hal->selfid_ptrs[j] =
j = 2 * j + 1;
} else {
break;
}
} else {
if (IEEE1394_SELFID_PHYID(current) <
hal->selfid_ptrs[j] =
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
{
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
{
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)
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
{
int block;
if (port > selfid_num_ports(s)) {
"1394 s1394 error",
"Invalid port number requested for node",
}
if (port > 2) {
/* Calculate which quadlet and bits for this port */
port -= 3;
/* Move to the correct quadlet */
s += block;
}
/* Shift by appropriate number of bits and mask */
}
/*
* 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
{
int i;
S1394_TNF_SL_BR_STACK, "");
/*
* 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.
*/
/* Free any cfgrom alloced and zap the node */
for (i = 0; i < number_of_nodes; i++) {
if (config_rom != NULL) {
kmem_free((void *)config_rom,
} else {
}
}
}
}
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
{
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.
*/
S1394_TNF_SL_BR_STACK, "");
/* Flush the Stack */
/* 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 */
"SelfIDs - Invalid node numbering");
/* Initiate a bus reset */
S1394_TNF_SL_BR_STACK, "");
return (DDI_FAILURE);
}
}
for (i = 0; i < number_of_nodes; i++) {
/* Current node has no parent yet */
/* Current node has no connections yet */
/* Initialize all ports on this node */
for (j = 0; j < IEEE1394_MAX_NUM_PORTS; j++)
/* For each port on the node - highest to lowest */
j >= 0; j--) {
if (found_parent == B_FALSE) {
(char)j;
} else {
"Has multiple parents");
/* Initiate a bus reset */
S1394_TNF_SL_BR_STACK, "");
return (DDI_FAILURE);
}
"invalid - Tree build failed");
/* Initiate a bus reset */
S1394_TNF_SL_BR_STACK, "");
return (DDI_FAILURE);
}
&hal->topology_tree[i];
}
}
/* If current node has no parents or children - Invalid */
"SelfID packet - Has no connections");
/* Initiate a bus reset */
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) {
}
}
/* If the stack is not empty, then something has gone seriously wrong */
"Topology Tree invalid - Tree build failed");
/* Initiate a bus reset */
S1394_TNF_SL_BR_STACK, "");
return (DDI_FAILURE);
}
/* New topology tree is now valid */
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_TNF_SL_BR_STACK, "");
hal->hal_stack_depth++;
} else {
"HAL stack - Overflow");
S1394_TNF_SL_BR_STACK, "");
return;
}
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_TNF_SL_BR_STACK, "");
hal->hal_stack_depth--;
S1394_TNF_SL_BR_STACK, "");
} else {
"HAL stack - Underflow");
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_TNF_SL_BR_STACK, "");
hal->hal_queue_back) {
"HAL Queue - Overflow");
S1394_TNF_SL_BR_STACK, "");
return;
} else {
}
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 *
{
void *tmp;
S1394_TNF_SL_BR_STACK, "");
"HAL Queue - Underflow");
S1394_TNF_SL_BR_STACK, "");
return (NULL);
} else {
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
{
"Node Number List - Overflow");
S1394_TNF_SL_BR_STACK, "");
return;
}
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
{
int i;
S1394_TNF_SL_BR_STACK, "");
for (i = 0; i < number_of_nodes; i++)
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
{
int i;
S1394_TNF_SL_BR_STACK, "");
for (i = 0; i < number_of_nodes; i++)
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
{
int i;
S1394_TNF_SL_BR_STACK, "");
for (i = 0; i < number_of_nodes; i++)
S1394_TNF_SL_BR_STACK, "");
}
/*
* s1394_copy_old_tree()
* switches the pointers for old_tree[] and topology_tree[].
*/
void
{
S1394_TNF_SL_BR_STACK, "");
/* Old tree is now valid and filled also */
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
{
int i;
int port_type;
S1394_TNF_SL_BR_STACK, "");
/* To ensure that the queue is empty */
/* Set up the first matched nodes (which are our own local nodes) */
/* Put the node on the queue */
/* While the queue is not empty, remove a node */
/* Mark both old and new nodes as "visited" */
/* Mark old and new nodes as "matched" */
/* s1394_copy_cfgrom() clears "matched" for some cases... */
/* Move the target list over to the new node and update */
/* the node info. */
s1394_target_t *t;
while (t != NULL) {
t = t->target_sibling;
}
}
/* Is the new port connected? */
if ((port_type == IEEE1394_SELFID_PORT_TO_CHILD) ||
/* Is the old port connected? */
if ((port_type ==
(port_type ==
/* Found a match, check if */
/* we've already visited it */
}
}
}
}
}
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
{
int i;
int start;
int end;
int distance = 0;
int diameter = 0;
int local_diameter = 0;
S1394_TNF_SL_BR_STACK, "");
/* Initialize topology tree */
for (i = 0; i < number_of_nodes; i++) {
}
/* Start at the root node */
/* Flush the stack before we start */
do {
/* Check every previously unchecked port for children */
/* If there is a child push it on the stack */
break;
}
}
/* If we reach here and the stack is empty, we're done */
continue;
}
/* If no children were found, we're at a leaf */
if (found_a_child == B_FALSE) {
/* Pop the child and set the appropriate fields */
}
/* Update maximum distance (diameter), if necessary */
if (local_diameter > diameter)
}
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
{
} else {
" bus - If new devices have recently been added, remove"
" them.");
}
}
/*
* 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
{
int i;
S1394_TNF_SL_BR_STACK, "");
/* Grab the first gap_count in the SelfID packets */
/* Compare it too all the rest */
if (gap_count !=
/* Inconsistent gap counts */
"Inconsistent gap count");
if (s1394_ignore_invalid_gap_cnt == 0) {
/* Initiate a bus reset */
}
S1394_TNF_SL_BR_STACK, "");
return (-1);
}
}
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
{
int i;
int j;
int node_num;
S1394_TNF_SL_BR_STACK, "");
/* Mark all speed = IEEE1394_S100 nodes in the Speed Map */
for (i = 0; i < number_of_nodes; i++) {
for (j = 0; j < number_of_nodes; j++) {
if (j != node_num) {
}
}
}
}
/* Fill in the diagonal */
for (i = 0; i < number_of_nodes; i++) {
}
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
{
int i;
int j;
int k;
int size;
S1394_TNF_SL_BR_STACK, "");
/* Prepare the topology tree */
/* To ensure that the queue is empty */
for (i = 0; i < number_of_nodes; i++) {
/* If the node's speed == min_spd and it hasn't been visited */
min_spd)) {
hal);
/* Add node number to the list */
for (j = 0; j < IEEE1394_MAX_NUM_PORTS; j++) {
if (selfid_speed(
selfid_packet) >= min_spd) {
hal,
}
}
}
}
/* For each pair, mark speed_map as min_spd */
for (j = 0; j < size; j++) {
for (k = 0; k < size; k++) {
if (j != k) {
}
}
}
/* Flush the Node Number List */
hal->hal_node_number_list_size = 0;
}
}
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
{
int i, j;
S1394_TNF_SL_BR_STACK, "");
for (i = 0; i < number_of_nodes; i++) {
for (j = 0; j < number_of_nodes; j++) {
if (i != j)
else
}
}
S1394_TNF_SL_BR_STACK, "");
}
/*
* s1394_speed_map_get()
* queries the speed_map[] for a given pair of nodes.
*/
{
/* If it's not a valid node, then return slowest_node_speed */
/* Send at fastest speed everyone will see */
return (hal->slowest_node_speed);
}
/* else return the correct maximum speed */
}
/*
* 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
{
int i, j;
S1394_TNF_SL_BR_STACK, "");
for (i = 0; i < number_of_nodes; i++) {
/* Skip invalid config ROMs */
/* Skip if Bus_Info_Block generation is 0 */
/* because it isn't a P1394a device */
if ((bus_capabilities & IEEE1394_BIB_GEN_MASK) != 0) {
for (j = 0; j < number_of_nodes; j++) {
/* Update if link_speed is slower */
}
if (link_speed <
}
}
}
}
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
{
int i;
"");
/* Highest numbered node with L=1 and C=1 */
S1394_TNF_SL_BR_STACK, "");
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_TNF_SL_BR_STACK, "");
while (curr_target != NULL) {
(curr_target->physical_arreq_enabled != 0)) {
}
}
/*
* 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.
*/
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_TNF_SL_STACK, "");
/* Find the HAL this target resides on */
(target->physical_arreq_enabled != 0)) {
/*
* 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.
*/
} else {
}
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_TNF_SL_STACK, "");
/* Find the HAL this target resides on */
(target->physical_arreq_enabled == 0)) {
/*
* 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.
*/
} else {
}
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_TNF_SL_STACK, "");
S1394_TNF_SL_STACK, "");
}