/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* 1394 Services Layer Hotplug Routines
* This file contains routines that walk the old and topology
* trees, at bus reset time, creating devinfo's for new nodes and offlining
* nodes that are removed.
*/
#include <sys/sysmacros.h>
#include <sys/ddi_impldefs.h>
#include <sys/tnf_probe.h>
/*
* s1394_send_remove_event()
* Invokes any "remove event" callback registered for dip. Passes
* t1394_localinfo_t as impl_data for the callback.
*/
static void
{
name);
== NDI_SUCCESS) {
}
}
/*
* s1394_send_insert_event()
* Invokes any "insert event" callback registered for dip. Passes
* t1394_localinfo_t as impl_data for the callback.
*/
static void
{
name);
}
/*
* s1394_create_devinfo()
* This routine creates a devinfo corresponding to the unit_dir passed in.
* It adds "hp-node", "reg", "compatible" properties to the devinfo
* (formats for "reg" and "compatible" properties are specified by 1275
* the properties, returns NULL, otherwise, returns the devinfo created.
*
* NOTE: All ndi_* routines are interrupt callable (and thus won't sleep).
* So, we don't drop topology_mutex across ndi calls.
*/
static dev_info_t *
int nunit)
{
int root_dir_len;
/*LINTED type is unused*/
/* Allocate and init a new device node instance. */
&target_dip);
if (result != NDI_SUCCESS) {
node->node_guid_lo);
return (NULL);
}
/* Add "hp-node" property */
if (result != NDI_SUCCESS) {
node->node_guid_lo);
#if defined(DEBUG)
#endif
(void) ndi_devi_free(target_dip);
return (NULL);
}
for (i = 0; i < root_dir_len; i++) {
switch (key) {
mod_ven++;
break;
mod_hw++;
break;
case IEEE1212_MODULE_SPEC_ID:
mod_spec++;
break;
mod_sw++;
break;
case IEEE1212_NODE_VENDOR_ID:
node_ven++;
break;
case IEEE1212_NODE_UNIQUE_ID: {
node_ven++;
}
break;
case IEEE1212_NODE_HW_VERSION:
node_hw++;
break;
case IEEE1212_NODE_SPEC_ID:
node_spec++;
break;
case IEEE1212_NODE_SW_VERSION:
node_sw++;
break;
}
break;
}
}
/*
* Search for unit spec and version
*/
for (i = 0; i < CFGROM_DIR_LEN(unit_dir); i++) {
if (key == IEEE1212_UNIT_SPEC_ID) {
spec_id++;
} else if (key == IEEE1212_UNIT_SW_VERSION) {
sw_version++;
}
if (spec_id && sw_version)
break;
}
/*
* Refer to IEEE1212 (pages 90-92) for information regarding various
* id's. Module_Vendor_Id is required. Node_Vendor_Id is optional and
* if not implemented, its assumed value is Module_Vendor_Id.
* Module_Spec_Id is optional and if not implemented, its assumed value
* is Module_Vendor_Id. Node_Spec_Id is optional, and if not
* implemented, its assumed value is Node_Vendor_Id. Unit_Spec_Id is
* optional, and if not implemented, its assumed value is
* Node_Vendor_Id.
*/
if (node_ven == 0) {
node_ven++;
}
if (node_spec == 0) {
node_spec++;
}
if (mod_spec == 0) {
mod_spec++;
}
if (spec_id == 0) {
spec_id++;
}
i = 0;
if (sw_version != 0) {
}
if (node_sw != 0) {
}
if (node_hw != 0) {
}
if (mod_sw != 0) {
}
if (mod_hw != 0) {
}
"compatible", (char **)&buf, i);
if (result != NDI_SUCCESS) {
node->node_guid_lo);
#if defined(DEBUG)
for (j = 0; j < i; j++) {
}
#endif
(void) ndi_devi_free(target_dip);
return (NULL);
}
for (j = 0; j < i; j++) {
tnf_int, compat_index, j,
}
/* GUID,ADDR */
®[5]);
(int *)reg, 6);
if (result != NDI_SUCCESS) {
#if defined(DEBUG)
for (j = 0; j < 6; j++) {
}
#endif
(void) ndi_devi_free(target_dip);
return (NULL);
}
return (target_dip);
}
/*
* s1394_devi_find()
* Searches all children of pdip for a match of name@caddr. Builds the
* name and address of each child node by looking up the reg property on
* the node and compares the built name@addr with the name@addr passed in.
* Returns the child dip if a match is found, otherwise, returns NULL.
* NOTE:
* This routine is decidedly non-ddi. We had to use this one since
* ndi_devi_find() can find only nodes that have valid addr field
* set and that won't happen unless the node goes through INITCHILD
* (at which time nx1394.c calls ddi_set_name_addr()). If, in future,
* the ndi_devi_find() provides a way of looking up nodes using criteria
* other than addr, we can get rid of this routine.
*/
/*ARGSUSED*/
{
int i, reglen;
/*
* for each child of this parent, find name and addr and match with
* name and caddr passed in.
*/
if (i != DDI_PROP_SUCCESS)
continue;
/*
* Construct addr from the reg property (addr is of the format
* GGGGGGGGGGGGGGGG[,AAAAAAAAAAAA], where GGGGGGGGGGGGGGGG is
* the address and AAAAAAAAAAAA is the optional unit address)
*/
} else {
}
break;
}
}
}
return (cdip);
}
/*
* s1394_update_devinfo_tree()
* for each unit directory found. If the devinfo corresponding to a unit
* already exists, any insert event callbacks registered for that devinfo
* are called (topology tree is unlocked and relocked around these
* callbacks). Returns DDI_SUCCESS if everything went fine and DDI_FAILURE
* if unable to reacquire the lock after callbacks (relock fails because of
* an intervening bus reset or if the services layer kills the bus reset
* thread). The node is marked as parsed before returning.
*/
int
{
/* scan through config rom looking for unit dirs */
else
"!Bad root directory in config rom (node's GUID %08x%08x)",
"bad directory");
return (DDI_SUCCESS);
}
} else {
" rom (node's GUID %08x%08x)",
}
}
}
for (d = 0, j = 0; j < units; j++) {
lo = j;
} else {
}
while (t->target_sibling != NULL) {
t = t->target_sibling;
}
t->target_sibling = target;
} else {
}
}
unit_dir_ptrs[j] - root_dir);
} else {
/* create devinfo for unit@caddr */
unit_dir_ptrs[j], j);
new_devinfo |= (1 << d);
unit_dir_ptrs[j] - root_dir);
}
}
devinfo_ptrs[d++] = tdip;
}
/* Online all valid units */
for (j = 0; j < d; j++) {
if ((new_devinfo & (1 << j)) == 0) {
}
/* don't need to drop topology_tree_mutex across ndi calls */
(void) ndi_devi_online_async(devinfo_ptrs[j], 0);
if ((new_devinfo & (1 << j)) == 0) {
/*
* send an insert event if this an existing devinfo.
* drop and reacquire topology_tree_mutex across
* the event calls
*/
lockfail = 1;
break;
}
}
}
if (lockfail) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* s1394_offline_node()
* Offlines a node. This involves marking all targets attached to the
* node as gone, invoking any remove event callbacks and calling
* ndi_devi_offline to mark the devinfo as OFFLINE (for each unit
* directory on the node). The tree is unlocked and relocked around
* the callbacks. If unable to relock the tree, DDI_FAILURE, else
* returns DDI_SUCCESS.
*/
int
{
s1394_target_t *t;
int j, d, units;
d = 0;
t = node->target_list;
while (t != NULL) {
tnf_opaque, target, t);
t->target_state |= S1394_TARG_GONE;
t = t->target_sibling;
}
/* scan through config rom looking for unit dirs */
else
}
}
}
for (d = 0, j = 0; j < units; j++) {
lo = j;
} else {
}
NULL)
devinfo_ptrs[d++] = tdip;
}
for (j = 0; j < d; j++) {
node_num);
return (DDI_FAILURE);
}
}
"");
return (DDI_SUCCESS);
}
/*
* s1394_process_topology_tree()
* Walks the topology tree, processing each node. If node that has
* already been parsed, updates the generation property on all devinfos
* for the node. Also, if the node exists in both old & new trees, ASSERTS
* that both point to the same config rom. If the node has valid config
* rom but hasn't been parsed yet, calls s1394_update_devinfo_tree()
* to parse and create devinfos for the node. Kicks off further config
* rom reading if only the bus info block for the node is read.
* Returns DDI_SUCCESS if everything went fine, else returns DDI_FAILURE
* (for eg. unable to reacquire the tree lock etc). wait_for_cbs argument
* tells the caller if some completions can be expected. wait_gen tells
* the generation the commands were issued at.
*/
int
{
int i;
return (DDI_FAILURE);
}
hal->cfgroms_being_read = 0;
for (i = 0; i < number_of_nodes; i++) {
if (i == hal_node_num)
continue;
return (DDI_FAILURE);
}
continue;
}
continue;
}
NULL) {
/*
* onode->cfgrom != node->cfgrom should have been
* handled by s1394_match_GUID()!!!
*/
}
B_TRUE) {
DDI_FAILURE) {
&hal->topology_tree_mutex));
msg, "failure from update devinfo");
return (DDI_FAILURE);
}
DDI_SUCCESS) {
msg, "failure reading rest of cfgrom");
if ((status & S1394_LOCK_FAILED) == 0) {
*wait_for_cbs = 0;
}
return (DDI_FAILURE);
} else {
*wait_for_cbs = 1;
}
}
}
/*
* flag the tree as processed; if a single bus reset happens after
* this, we will use tree matching.
*/
msg, "relock failed while marking tree processed");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* s1394_process_old_tree()
* Walks through the old tree and offlines nodes that are removed. Nodes
* with an active link in the old tree but link powered off in the current
* generation are also offlined, as well as nodes with invalid config
* rom in current generation.
* if the locking fails at any stage, stops further walking and returns
* DDI_FAILURE. Returns DDI_SUCCESS if everything went fine.
*/
int
{
int i;
/*
* NODE_MATCHED(onode) == 0 indicates this node doesn't exist
* any more.
*/
return (DDI_FAILURE);
}
for (i = 0; i < old_number_of_nodes; i++) {
if (i == hal_node_num_old)
continue;
"lock failed while processing node", tnf_uint,
node_num, i);
return (DDI_FAILURE);
}
continue;
}
/*
* onode->cur_node == NULL iff we couldn't read cfgrom in the
* current generation in non-tree matching case (and thus
* match_GUIDs couldn't set cur_node).
*/
node_num, i);
return (DDI_FAILURE);
}
}
}
return (DDI_SUCCESS);
}
/*
* s1394_update_unit_dir_location()
* Updates the unit-dir-offset property on the devinfo.
* NOTE: ndi_prop_update_int() is interrupt callable (and thus won't block);
* so, the caller doesn't drop topology_tree_mutex when calling this routine.
*/
/*ARGSUSED*/
static void
{
offset);
}
/*
* s1394_add_target_to_node()
* adds target to the list of targets hanging off the node. Figures out
* the node by searching the topology tree for the GUID corresponding
* to the target. Points on_node field of target structure at the node.
*/
void
{
s1394_target_t *t;
int i;
char *ptr;
/* Topology tree must be locked when it gets here! */
/* target_list_rwlock should be held in write mode */
return;
}
/* Drop the ,<ADDR> part, if present */
*ptr = '\0';
/* Search the HAL's node list for this GUID */
for (i = 0; i < hal->number_of_nodes; i++) {
NULL) {
while (t->target_sibling != NULL) {
t = t->target_sibling;
}
t->target_sibling = target;
} else {
}
/*
* update target_list in all targets on the
* node
*/
while (t != NULL) {
t->target_list =
t = t->target_sibling;
}
break;
}
}
}
}
/*
* s1394_remove_target_from_node()
* Removes target from the corresponding node's target_list.
*/
void
{
/* Topology tree must be locked when it gets here! */
/* target_list_rwlock should be held in write mode */
}
t = target->target_list;
while (t != NULL) {
if (t == target) {
} else {
}
break;
}
t1 = t;
t = t->target_sibling;
}
/* Update the target_list pointer in all the targets */
while (t != NULL) {
t->target_list = t1;
t = t->target_sibling;
}
}