xref: /illumos-gate/usr/src/uts/common/io/1394/s1394_hotplug.c (revision dd72704bd9e794056c558153663c739e2012d721)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * s1394_hotplug.c
29  *    1394 Services Layer Hotplug Routines
30  *    This file contains routines that walk the old and topology
31  *    trees, at bus reset time, creating devinfo's for new nodes and offlining
32  *    nodes that are removed.
33  */
34 
35 #include <sys/conf.h>
36 #include <sys/sysmacros.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39 #include <sys/sunndi.h>
40 #include <sys/modctl.h>
41 #include <sys/sunddi.h>
42 #include <sys/ddi_impldefs.h>
43 #include <sys/types.h>
44 #include <sys/1394/t1394.h>
45 #include <sys/1394/s1394.h>
46 #include <sys/1394/h1394.h>
47 #include <sys/1394/ieee1394.h>
48 
49 static void s1394_send_remove_event(s1394_hal_t *hal, dev_info_t *dip,
50     t1394_localinfo_t *localinfo);
51 static void s1394_send_insert_event(s1394_hal_t *hal, dev_info_t *dip,
52     t1394_localinfo_t *localinfo);
53 static dev_info_t *s1394_create_devinfo(s1394_hal_t *hal, s1394_node_t *node,
54     uint32_t *unit_dir, int nunit);
55 static void s1394_update_unit_dir_location(s1394_hal_t *hal, dev_info_t *tdip,
56     uint_t offset);
57 
58 /*
59  * s1394_send_remove_event()
60  *    Invokes any "remove event" callback registered for dip. Passes
61  *    t1394_localinfo_t as impl_data for the callback.
62  */
63 static void
64 s1394_send_remove_event(s1394_hal_t *hal, dev_info_t *dip,
65     t1394_localinfo_t *localinfo)
66 {
67 	char name[128];
68 	ddi_eventcookie_t cookie;
69 
70 	(void) sprintf(name, "%s%d", ddi_driver_name(dip),
71 	    ddi_get_instance(dip));
72 
73 	if (ndi_event_retrieve_cookie(hal->hal_ndi_event_hdl, dip,
74 	    DDI_DEVI_REMOVE_EVENT, &cookie, NDI_EVENT_NOPASS)
75 	    == NDI_SUCCESS) {
76 		(void) ndi_event_run_callbacks(hal->hal_ndi_event_hdl, dip,
77 		    cookie, localinfo);
78 	}
79 }
80 
81 /*
82  * s1394_send_insert_event()
83  *    Invokes any "insert event" callback registered for dip. Passes
84  *    t1394_localinfo_t as impl_data for the callback.
85  */
86 static void
87 s1394_send_insert_event(s1394_hal_t *hal, dev_info_t *dip,
88     t1394_localinfo_t *localinfo)
89 {
90 	char name[128];
91 	ddi_eventcookie_t cookie;
92 
93 	(void) sprintf(name, "%s%d", ddi_driver_name(dip),
94 	    ddi_get_instance(dip));
95 
96 	if (ndi_event_retrieve_cookie(hal->hal_ndi_event_hdl, dip,
97 	    DDI_DEVI_INSERT_EVENT, &cookie, NDI_EVENT_NOPASS) ==
98 	    NDI_SUCCESS)
99 		(void) ndi_event_run_callbacks(hal->hal_ndi_event_hdl, dip,
100 		    cookie, localinfo);
101 }
102 
103 /*
104  * s1394_create_devinfo()
105  *    This routine creates a devinfo corresponding to the unit_dir passed in.
106  *    It adds "hp-node", "reg", "compatible" properties to the devinfo
107  *    (formats for "reg" and "compatible" properties are specified by 1275
108  *    binding for IEEE1394). If unable to create the devinfo and/or add the
109  *    the properties, returns NULL, otherwise, returns the devinfo created.
110  *
111  *    NOTE: All ndi_* routines are interrupt callable (and thus won't sleep).
112  *    So, we don't drop topology_mutex across ndi calls.
113  */
114 static dev_info_t *
115 s1394_create_devinfo(s1394_hal_t *hal, s1394_node_t *node, uint32_t *unit_dir,
116     int nunit)
117 {
118 	dev_info_t *hal_dip;
119 	uint32_t *root_dir;
120 	dev_info_t *target_dip;
121 
122 	int root_dir_len;
123 	int result, i, j, spec_id, sw_version;
124 	int mod_ven, mod_hw, mod_spec, mod_sw;
125 	int node_ven, node_hw, node_spec, node_sw;
126 
127 	/*LINTED type is unused*/
128 	uint32_t type __unused, key, value;
129 	uint32_t unit_spec_id, unit_sw_version;
130 	uint32_t node_spec_id, node_sw_version;
131 	uint32_t node_vendor_id, node_hw_version;
132 	uint32_t module_spec_id, module_sw_version;
133 	uint32_t module_vendor_id, module_hw_version;
134 
135 	char *fmt = "firewire%06x,%06x";
136 
137 	char *buf[5], data[5][24];
138 	uint32_t reg[6];
139 
140 	ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
141 
142 	hal_dip = hal->halinfo.dip;
143 
144 	/* Allocate and init a new device node instance. */
145 	result = ndi_devi_alloc(hal_dip, "unit", (pnode_t)DEVI_SID_NODEID,
146 	    &target_dip);
147 
148 	if (result != NDI_SUCCESS) {
149 		cmn_err(CE_NOTE, "!Unable to create devinfo"
150 		    " (node's GUID %08x%08x)", node->node_guid_hi,
151 		    node->node_guid_lo);
152 		return (NULL);
153 	}
154 
155 	/* Add "hp-node" property */
156 	result = ndi_prop_update_int(DDI_DEV_T_NONE, target_dip, "hp-node", 0);
157 	if (result != NDI_SUCCESS) {
158 		cmn_err(CE_NOTE, "!Unable to add \"hp-node\" property"
159 		    " (node's GUID %08x%08x)", node->node_guid_hi,
160 		    node->node_guid_lo);
161 #if defined(DEBUG)
162 		cmn_err(CE_CONT, "!Error code %d", result);
163 #endif
164 		ndi_prop_remove_all(target_dip);
165 		(void) ndi_devi_free(target_dip);
166 		return (NULL);
167 	}
168 
169 	spec_id = sw_version = mod_ven = mod_hw = mod_spec = mod_sw =
170 	    node_ven = node_hw = node_spec = node_sw = 0;
171 	unit_sw_version = node_sw_version = node_hw_version =
172 	    module_sw_version = module_hw_version = 0;
173 
174 
175 	root_dir = CFGROM_ROOT_DIR(node->cfgrom);
176 	root_dir_len = CFGROM_DIR_LEN(root_dir);
177 
178 	for (i = 0; i < root_dir_len; i++) {
179 
180 		CFGROM_TYPE_KEY_VALUE(root_dir[i + 1], type, key, value);
181 		switch (key) {
182 
183 		case IEEE1212_MODULE_VENDOR_ID:
184 			module_vendor_id = value;
185 			mod_ven++;
186 			break;
187 		case IEEE1212_MODULE_HW_VERSION:
188 			module_hw_version = value;
189 			mod_hw++;
190 			break;
191 		case IEEE1212_MODULE_SPEC_ID:
192 			module_spec_id = value;
193 			mod_spec++;
194 			break;
195 		case IEEE1212_MODULE_SW_VERSION:
196 			module_sw_version = value;
197 			mod_sw++;
198 			break;
199 		case IEEE1212_NODE_VENDOR_ID:
200 			node_vendor_id = value;
201 			node_ven++;
202 			break;
203 		case IEEE1212_NODE_UNIQUE_ID: {
204 				uint32_t *node_unique_leaf =
205 				    &root_dir[i + 1] + value;
206 				node_vendor_id = (node_unique_leaf[1] >> 8);
207 				node_ven++;
208 			}
209 			break;
210 		case IEEE1212_NODE_HW_VERSION:
211 			node_hw_version = value;
212 			node_hw++;
213 			break;
214 		case IEEE1212_NODE_SPEC_ID:
215 			node_spec_id = value;
216 			node_spec++;
217 			break;
218 		case IEEE1212_NODE_SW_VERSION:
219 			node_sw_version = value;
220 			node_sw++;
221 			break;
222 		}
223 
224 		if (mod_ven && mod_hw && mod_spec && mod_sw && node_ven &&
225 		    node_hw && node_spec && node_sw) {
226 			break;
227 		}
228 	}
229 
230 	/*
231 	 * Search for unit spec and version
232 	 */
233 	for (i = 0; i < CFGROM_DIR_LEN(unit_dir); i++) {
234 
235 		CFGROM_TYPE_KEY_VALUE(unit_dir[i + 1], type, key, value);
236 		if (key == IEEE1212_UNIT_SPEC_ID) {
237 
238 			unit_spec_id = value;
239 			spec_id++;
240 		} else if (key == IEEE1212_UNIT_SW_VERSION) {
241 
242 			unit_sw_version = value;
243 			sw_version++;
244 		}
245 		if (spec_id && sw_version)
246 			break;
247 	}
248 
249 	/*
250 	 * Refer to IEEE1212 (pages 90-92) for information regarding various
251 	 * id's. Module_Vendor_Id is required. Node_Vendor_Id is optional and
252 	 * if not implemented, its assumed value is Module_Vendor_Id.
253 	 * Module_Spec_Id is optional and if not implemented, its assumed value
254 	 * is Module_Vendor_Id. Node_Spec_Id is optional, and if not
255 	 * implemented, its assumed value is Node_Vendor_Id. Unit_Spec_Id is
256 	 * optional, and if not implemented, its assumed value is
257 	 * Node_Vendor_Id.
258 	 */
259 	if (node_ven == 0) {
260 		node_vendor_id = module_vendor_id;
261 		node_ven++;
262 	}
263 
264 	if (node_spec == 0) {
265 		node_spec_id = node_vendor_id;
266 		node_spec++;
267 	}
268 
269 	if (mod_spec == 0) {
270 		module_spec_id = module_vendor_id;
271 		mod_spec++;
272 	}
273 
274 	if (spec_id == 0) {
275 		unit_spec_id = node_vendor_id;
276 		spec_id++;
277 	}
278 
279 	i = 0;
280 	if (sw_version != 0) {
281 		buf[i] = data[i];
282 		(void) sprintf(data[i++], fmt, unit_spec_id, unit_sw_version);
283 	}
284 	if (node_sw != 0) {
285 		buf[i] = data[i];
286 		(void) sprintf(data[i++], fmt, node_spec_id, node_sw_version);
287 	}
288 	if (node_hw != 0) {
289 		buf[i] = data[i];
290 		(void) sprintf(data[i++], fmt, node_vendor_id, node_hw_version);
291 	}
292 	if (mod_sw != 0) {
293 		buf[i] = data[i];
294 		(void) sprintf(data[i++], fmt, module_spec_id,
295 		    module_sw_version);
296 	}
297 	if (mod_hw != 0) {
298 		buf[i] = data[i];
299 		(void) sprintf(data[i++], fmt, module_vendor_id,
300 		    module_hw_version);
301 	}
302 
303 	result = ndi_prop_update_string_array(DDI_DEV_T_NONE, target_dip,
304 	    "compatible", (char **)&buf, i);
305 	if (result != NDI_SUCCESS) {
306 		cmn_err(CE_NOTE, "!Unable to add \"compatible\" property"
307 		    " (node's GUID %08x%08x)", node->node_guid_hi,
308 		    node->node_guid_lo);
309 #if defined(DEBUG)
310 		cmn_err(CE_CONT, "!Error code %d; nelements %d", result, i);
311 		for (j = 0; j < i; j++) {
312 			cmn_err(CE_CONT, "!buf[%d]: %s", j, buf[j]);
313 		}
314 #endif
315 		ndi_prop_remove_all(target_dip);
316 		(void) ndi_devi_free(target_dip);
317 		return (NULL);
318 	}
319 
320 	/* GUID,ADDR */
321 	reg[0] = node->node_guid_hi;
322 	reg[1] = node->node_guid_lo;
323 	s1394_cfgrom_parse_unit_dir(unit_dir, &reg[2], &reg[3], &reg[4],
324 	    &reg[5]);
325 
326 	reg[3] = nunit;
327 
328 	result = ndi_prop_update_int_array(DDI_DEV_T_NONE, target_dip, "reg",
329 	    (int *)reg, 6);
330 	if (result != NDI_SUCCESS) {
331 		cmn_err(CE_NOTE, "!Unable to add \"reg\" property");
332 #if defined(DEBUG)
333 		cmn_err(CE_CONT, "!Error code %d", result);
334 		for (j = 0; j < 6; j++) {
335 			cmn_err(CE_CONT, "!reg[%d]: 0x%08x", j, reg[j]);
336 		}
337 #endif
338 		ndi_prop_remove_all(target_dip);
339 		(void) ndi_devi_free(target_dip);
340 		return (NULL);
341 	}
342 
343 	return (target_dip);
344 }
345 
346 /*
347  * s1394_devi_find()
348  *    Searches all children of pdip for a match of name@caddr. Builds the
349  *    name and address of each child node by looking up the reg property on
350  *    the node and compares the built name@addr with the name@addr passed in.
351  *    Returns the child dip if a match is found, otherwise, returns NULL.
352  *    NOTE:
353  *    This routine is decidedly non-ddi. We had to use this one since
354  *    ndi_devi_find() can find only nodes that have valid addr field
355  *    set and that won't happen unless the node goes through INITCHILD
356  *    (at which time nx1394.c calls ddi_set_name_addr()). If, in future,
357  *    the ndi_devi_find() provides a way of looking up nodes using criteria
358  *    other than addr, we can get rid of this routine.
359  */
360 /*ARGSUSED*/
361 dev_info_t *
362 s1394_devi_find(dev_info_t *pdip, char *name, char *caddr)
363 {
364 	int i, reglen;
365 	char addr[32];
366 	uint32_t *regptr;
367 	dev_info_t *cdip = NULL;
368 
369 	ASSERT((name != NULL) && (caddr != NULL));
370 
371 	/*
372 	 * for each child of this parent, find name and addr and match with
373 	 * name and caddr passed in.
374 	 */
375 	for (cdip = (dev_info_t *)DEVI(pdip)->devi_child; cdip != NULL;
376 	    cdip = (dev_info_t *)DEVI(cdip)->devi_sibling) {
377 
378 		i = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip,
379 		    DDI_PROP_DONTPASS, "reg", (int **)&regptr,
380 		    (uint_t *)&reglen);
381 
382 		if (i != DDI_PROP_SUCCESS)
383 			continue;
384 
385 		/*
386 		 * Construct addr from the reg property (addr is of the format
387 		 * GGGGGGGGGGGGGGGG[,AAAAAAAAAAAA], where GGGGGGGGGGGGGGGG is
388 		 * the address and AAAAAAAAAAAA is the optional unit address)
389 		 */
390 		if (regptr[2] != 0 || regptr[3] != 0) {
391 			(void) sprintf(addr, "%08x%08x,%04x%08x", regptr[0],
392 			    regptr[1], regptr[2], regptr[3]);
393 		} else {
394 			(void) sprintf(addr, "%08x%08x", regptr[0], regptr[1]);
395 		}
396 		ddi_prop_free(regptr);
397 
398 		if (strcmp(caddr, addr) == 0) {
399 			ASSERT(strcmp(ddi_node_name(cdip), name) == 0);
400 			break;
401 		}
402 	}
403 
404 	return (cdip);
405 }
406 
407 /*
408  * s1394_update_devinfo_tree()
409  *    Parses the config rom for the passed in node and creates/updates devinfo's
410  *    for each unit directory found. If the devinfo corresponding to a unit
411  *    already exists, any insert event callbacks registered for that devinfo
412  *    are called (topology tree is unlocked and relocked around these
413  *    callbacks). Returns DDI_SUCCESS if everything went fine and DDI_FAILURE
414  *    if unable to reacquire the lock after callbacks (relock fails because of
415  *    an intervening bus reset or if the services layer kills the bus reset
416  *    thread). The node is marked as parsed before returning.
417  */
418 int
419 s1394_update_devinfo_tree(s1394_hal_t *hal, s1394_node_t *node)
420 {
421 	dev_info_t *tdip;
422 	int j, units, d, lockfail = 0;
423 	s1394_target_t *target, *t;
424 	uint32_t hi, lo, size_hi, size_lo, type, key, value;
425 	uint32_t *ptr, *root_dir, dir_len;
426 	t1394_localinfo_t linfo;
427 
428 	uint32_t *unit_dir_ptrs[32];
429 	dev_info_t *devinfo_ptrs[32];
430 	uint32_t new_devinfo = 0;	/* to keep track of new allocations */
431 
432 	char caddr[32];
433 
434 	ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
435 
436 	ASSERT(CFGROM_PARSED(node) == B_FALSE);
437 	ASSERT(node->cfgrom != NULL);
438 
439 	/* scan through config rom looking for unit dirs */
440 	root_dir = CFGROM_ROOT_DIR(node->cfgrom);
441 
442 	if (node->cfgrom_valid_size < CFGROM_DIR_LEN(root_dir))
443 		dir_len = node->cfgrom_valid_size;
444 	else
445 		dir_len = CFGROM_DIR_LEN(root_dir);
446 
447 	CFGROM_TYPE_KEY_VALUE(root_dir[0], type, key, value);
448 	if (s1394_valid_dir(hal, node, key, root_dir) == B_FALSE) {
449 		cmn_err(CE_NOTE,
450 		    "!Bad root directory in config rom (node's GUID %08x%08x)",
451 		    node->node_guid_hi, node->node_guid_lo);
452 
453 		SET_CFGROM_PARSED(node);
454 		CLEAR_CFGROM_GEN_CHANGED(node);	/* if set */
455 		CLEAR_CFGROM_NEW_ALLOC(node);
456 
457 		return (DDI_SUCCESS);
458 	}
459 
460 	for (units = 0, j = 1; j <= dir_len; j++) {
461 		CFGROM_TYPE_KEY_VALUE(root_dir[j], type, key, value);
462 		if (key == IEEE1212_UNIT_DIRECTORY && type ==
463 		    IEEE1212_DIRECTORY_TYPE) {
464 			ptr = &root_dir[j] + value;
465 			if (s1394_valid_dir(hal, node, key, ptr) == B_TRUE) {
466 				unit_dir_ptrs[units++] = ptr;
467 			} else {
468 				cmn_err(CE_NOTE, "!Bad unit directory in config"
469 				    " rom (node's GUID %08x%08x)",
470 				    node->node_guid_hi, node->node_guid_lo);
471 			}
472 		}
473 	}
474 
475 	for (d = 0, j = 0; j < units; j++) {
476 
477 		s1394_cfgrom_parse_unit_dir(unit_dir_ptrs[j],
478 		    &hi, &lo, &size_hi, &size_lo);
479 
480 		lo = j;
481 
482 		if (hi || lo) {
483 			(void) sprintf(caddr, "%08x%08x,%04x%08x",
484 			    node->node_guid_hi, node->node_guid_lo, hi, lo);
485 		} else {
486 			(void) sprintf(caddr, "%08x%08x",
487 			    node->node_guid_hi, node->node_guid_lo);
488 		}
489 
490 		tdip = s1394_devi_find(hal->halinfo.dip, "unit", caddr);
491 		if (tdip != NULL) {
492 
493 			rw_enter(&hal->target_list_rwlock, RW_WRITER);
494 			target = s1394_target_from_dip_locked(hal, tdip);
495 			if (target != NULL) {
496 				target->target_sibling = NULL;
497 				target->on_node = node;
498 				target->target_state &= ~S1394_TARG_GONE;
499 				target->unit_dir = unit_dir_ptrs[j] - root_dir;
500 
501 				if ((t = node->target_list) != NULL) {
502 					ASSERT(t != target);
503 					while (t->target_sibling != NULL) {
504 						t = t->target_sibling;
505 						ASSERT(t != target);
506 					}
507 					t->target_sibling = target;
508 				} else {
509 					node->target_list = target;
510 				}
511 
512 				target->target_list = node->target_list;
513 			}
514 			rw_exit(&hal->target_list_rwlock);
515 
516 			s1394_update_unit_dir_location(hal, tdip,
517 			    unit_dir_ptrs[j] - root_dir);
518 
519 		} else {
520 			/* create devinfo for unit@caddr */
521 			tdip = s1394_create_devinfo(hal, node,
522 			    unit_dir_ptrs[j], j);
523 			if (tdip != NULL) {
524 				new_devinfo |= (1 << d);
525 				s1394_update_unit_dir_location(hal, tdip,
526 				    unit_dir_ptrs[j] - root_dir);
527 			}
528 		}
529 		if (tdip != NULL)
530 			devinfo_ptrs[d++] = tdip;
531 	}
532 
533 	ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
534 	/* Online all valid units */
535 	for (j = 0; j < d; j++) {
536 		if ((new_devinfo & (1 << j)) == 0) {
537 			linfo.bus_generation = hal->generation_count;
538 			linfo.local_nodeID = hal->node_id;
539 		}
540 		/* don't need to drop topology_tree_mutex across ndi calls */
541 		(void) ndi_devi_online_async(devinfo_ptrs[j], 0);
542 		if ((new_devinfo & (1 << j)) == 0) {
543 			/*
544 			 * send an insert event if this an existing devinfo.
545 			 * drop and reacquire topology_tree_mutex across
546 			 * the event calls
547 			 */
548 			s1394_unlock_tree(hal);
549 			s1394_send_insert_event(hal, devinfo_ptrs[j], &linfo);
550 			if (s1394_lock_tree(hal) != DDI_SUCCESS) {
551 				lockfail = 1;
552 				break;
553 			}
554 		}
555 	}
556 
557 	if (lockfail) {
558 		return (DDI_FAILURE);
559 	}
560 
561 	SET_CFGROM_PARSED(node);
562 	CLEAR_CFGROM_GEN_CHANGED(node);	/* if set */
563 	CLEAR_CFGROM_NEW_ALLOC(node);
564 
565 	return (DDI_SUCCESS);
566 }
567 
568 /*
569  * s1394_offline_node()
570  *    Offlines a node. This involves marking all targets attached to the
571  *    node as gone, invoking any remove event callbacks and calling
572  *    ndi_devi_offline to mark the devinfo as OFFLINE (for each unit
573  *    directory on the node). The tree is unlocked and relocked around
574  *    the callbacks. If unable to relock the tree, DDI_FAILURE, else
575  *    returns DDI_SUCCESS.
576  */
577 int
578 s1394_offline_node(s1394_hal_t *hal, s1394_node_t *node)
579 {
580 	s1394_target_t *t;
581 	dev_info_t *tdip;
582 	int j, d, units;
583 	uint32_t *unit_dir_ptrs[32];
584 	dev_info_t *devinfo_ptrs[32];
585 	t1394_localinfo_t linfo;
586 	uint32_t *ptr, *root_dir, dir_len;
587 	uint32_t hi, lo, size_hi, size_lo, type, key, value;
588 	char caddr[32];
589 
590 	ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
591 
592 	d = 0;
593 	rw_enter(&hal->target_list_rwlock, RW_WRITER);
594 	t = node->target_list;
595 	while (t != NULL) {
596 		t->target_state |= S1394_TARG_GONE;
597 		t->on_node = NULL;
598 		t = t->target_sibling;
599 	}
600 	rw_exit(&hal->target_list_rwlock);
601 
602 	/* scan through config rom looking for unit dirs */
603 	root_dir = CFGROM_ROOT_DIR(node->cfgrom);
604 
605 	if (node->cfgrom_valid_size < CFGROM_DIR_LEN(root_dir))
606 		dir_len = node->cfgrom_valid_size;
607 	else
608 		dir_len = CFGROM_DIR_LEN(root_dir);
609 
610 	CFGROM_TYPE_KEY_VALUE(root_dir[0], type, key, value);
611 
612 	for (units = 0, j = 1; j <= dir_len; j++) {
613 		CFGROM_TYPE_KEY_VALUE(root_dir[j], type, key, value);
614 		if (key == IEEE1212_UNIT_DIRECTORY && type ==
615 				IEEE1212_DIRECTORY_TYPE) {
616 			ptr = &root_dir[j] + value;
617 			if (s1394_valid_dir(hal, node, key, ptr) == B_TRUE) {
618 				unit_dir_ptrs[units++] = ptr;
619 			}
620 		}
621 	}
622 
623 	for (d = 0, j = 0; j < units; j++) {
624 
625 		s1394_cfgrom_parse_unit_dir(unit_dir_ptrs[j],
626 				&hi, &lo, &size_hi, &size_lo);
627 
628 		lo = j;
629 
630 		if (hi || lo) {
631 			(void) sprintf(caddr, "%08x%08x,%04x%08x",
632 					node->node_guid_hi, node->node_guid_lo, hi, lo);
633 		} else {
634 			(void) sprintf(caddr, "%08x%08x",
635 					node->node_guid_hi, node->node_guid_lo);
636 		}
637 
638 		if ((tdip = s1394_devi_find(hal->halinfo.dip, "unit", caddr)) !=
639 				NULL)
640 			devinfo_ptrs[d++] = tdip;
641 	}
642 
643 	node->old_node = NULL;
644 
645 	linfo.bus_generation = hal->generation_count;
646 	linfo.local_nodeID = hal->node_id;
647 
648 	for (j = 0; j < d; j++) {
649 		s1394_unlock_tree(hal);
650 
651 		s1394_send_remove_event(hal, devinfo_ptrs[j], &linfo);
652 		(void) ndi_devi_offline(devinfo_ptrs[j], NDI_DEVI_REMOVE);
653 		if (s1394_lock_tree(hal) != DDI_SUCCESS) {
654 			return (DDI_FAILURE);
655 		}
656 	}
657 
658 	ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
659 	return (DDI_SUCCESS);
660 }
661 
662 /*
663  * s1394_process_topology_tree()
664  *    Walks the topology tree, processing each node. If node that has
665  *    already been parsed, updates the generation property on all devinfos
666  *    for the node. Also, if the node exists in both old & new trees, ASSERTS
667  *    that both point to the same config rom. If the node has valid config
668  *    rom but hasn't been parsed yet, calls s1394_update_devinfo_tree()
669  *    to parse and create devinfos for the node. Kicks off further config
670  *    rom reading if only the bus info block for the node is read.
671  *    Returns DDI_SUCCESS if everything went fine, else returns DDI_FAILURE
672  *    (for eg. unable to reacquire the tree lock etc). wait_for_cbs argument
673  *    tells the caller if some completions can be expected. wait_gen tells
674  *    the generation the commands were issued at.
675  */
676 int
677 s1394_process_topology_tree(s1394_hal_t *hal, int *wait_for_cbs,
678     uint_t *wait_gen)
679 {
680 	int i;
681 	uint_t hal_node_num, number_of_nodes;
682 	s1394_node_t *node, *onode;
683 	s1394_status_t status;
684 
685 	ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
686 
687 	if (s1394_lock_tree(hal) != DDI_SUCCESS) {
688 		return (DDI_FAILURE);
689 	}
690 
691 	hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
692 	hal->cfgroms_being_read = 0;
693 	number_of_nodes = hal->number_of_nodes;
694 	s1394_unlock_tree(hal);
695 
696 	for (i = 0; i < number_of_nodes; i++) {
697 
698 		if (i == hal_node_num)
699 			continue;
700 		if (s1394_lock_tree(hal) != DDI_SUCCESS) {
701 			return (DDI_FAILURE);
702 		}
703 		node = &hal->topology_tree[i];
704 
705 		if (LINK_ACTIVE(node) == B_FALSE) {
706 			s1394_unlock_tree(hal);
707 			continue;
708 		}
709 		if (node->cfgrom == NULL) {
710 			s1394_unlock_tree(hal);
711 			continue;
712 		}
713 
714 		onode = node->old_node;
715 
716 		if (onode != NULL && onode->cfgrom != NULL && node->cfgrom !=
717 		    NULL) {
718 			/*
719 			 * onode->cfgrom != node->cfgrom should have been
720 			 * handled by s1394_match_GUID()!!!
721 			 */
722 			ASSERT(onode->cfgrom == node->cfgrom);
723 		}
724 
725 		if (CFGROM_PARSED(node) == B_FALSE && CFGROM_ALL_READ(node) ==
726 		    B_TRUE) {
727 			ASSERT((node->cfgrom_size <
728 			    IEEE1394_CONFIG_ROM_QUAD_SZ) ||
729 			    NODE_MATCHED(node) == B_TRUE);
730 			rw_enter(&hal->target_list_rwlock, RW_READER);
731 			ASSERT(node->target_list == NULL);
732 			rw_exit(&hal->target_list_rwlock);
733 			if (s1394_update_devinfo_tree(hal, node) ==
734 			    DDI_FAILURE) {
735 				ASSERT(MUTEX_NOT_HELD(
736 				    &hal->topology_tree_mutex));
737 				return (DDI_FAILURE);
738 			}
739 		} else if (CFGROM_PARSED(node) == B_FALSE && CFGROM_BIB_READ(
740 		    node) == B_TRUE) {
741 			if (s1394_read_rest_of_cfgrom(hal, node, &status) !=
742 			    DDI_SUCCESS) {
743 				if ((status & S1394_LOCK_FAILED) == 0) {
744 					ASSERT(MUTEX_HELD(&hal->
745 					    topology_tree_mutex));
746 					*wait_for_cbs = 0;
747 					s1394_unlock_tree(hal);
748 				}
749 				return (DDI_FAILURE);
750 			} else {
751 				*wait_for_cbs = 1;
752 				*wait_gen = hal->br_cfgrom_read_gen;
753 			}
754 		}
755 
756 		s1394_unlock_tree(hal);
757 	}
758 
759 	/*
760 	 * flag the tree as processed; if a single bus reset happens after
761 	 * this, we will use tree matching.
762 	 */
763 	if (s1394_lock_tree(hal) != DDI_SUCCESS) {
764 		return (DDI_FAILURE);
765 	}
766 	hal->topology_tree_processed = B_TRUE;
767 	s1394_unlock_tree(hal);
768 
769 	return (DDI_SUCCESS);
770 }
771 
772 /*
773  * s1394_process_old_tree()
774  *    Walks through the old tree and offlines nodes that are removed. Nodes
775  *    with an active link in the old tree but link powered off in the current
776  *    generation are also offlined, as well as nodes with invalid config
777  *    rom in current generation.
778  *    The topology tree is locked/unlocked while walking through all the nodes;
779  *    if the locking fails at any stage, stops further walking and returns
780  *    DDI_FAILURE. Returns DDI_SUCCESS if everything went fine.
781  */
782 int
783 s1394_process_old_tree(s1394_hal_t *hal)
784 {
785 	int i;
786 	uint_t hal_node_num_old, old_number_of_nodes;
787 	s1394_node_t *onode;
788 
789 	/*
790 	 * NODE_MATCHED(onode) == 0 indicates this node doesn't exist
791 	 * any more.
792 	 */
793 	ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
794 
795 	if (s1394_lock_tree(hal) != DDI_SUCCESS) {
796 		return (DDI_FAILURE);
797 	}
798 	hal_node_num_old = IEEE1394_NODE_NUM(hal->old_node_id);
799 	old_number_of_nodes = hal->old_number_of_nodes;
800 	s1394_unlock_tree(hal);
801 
802 	for (i = 0; i < old_number_of_nodes; i++) {
803 
804 		if (i == hal_node_num_old)
805 			continue;
806 		if (s1394_lock_tree(hal) != DDI_SUCCESS) {
807 			return (DDI_FAILURE);
808 		}
809 
810 		onode = &hal->old_tree[i];
811 
812 		if (onode->cfgrom == NULL) {
813 			CLEAR_CFGROM_STATE(onode);
814 			s1394_unlock_tree(hal);
815 			continue;
816 		}
817 
818 		/*
819 		 * onode->cur_node == NULL iff we couldn't read cfgrom in the
820 		 * current generation in non-tree matching case (and thus
821 		 * match_GUIDs couldn't set cur_node).
822 		 */
823 		if (NODE_MATCHED(onode) == B_FALSE || (onode->cur_node ==
824 		    NULL || ((CFGROM_VALID(onode) == B_TRUE &&
825 		    CFGROM_VALID(onode->cur_node) == B_FALSE) ||
826 		    (LINK_ACTIVE(onode) == B_TRUE && LINK_ACTIVE(onode->
827 		    cur_node) == B_FALSE)))) {
828 
829 			if (s1394_offline_node(hal, onode) != DDI_SUCCESS) {
830 				return (DDI_FAILURE);
831 			}
832 			s1394_free_cfgrom(hal, onode, S1394_FREE_CFGROM_OLD);
833 		}
834 
835 		s1394_unlock_tree(hal);
836 	}
837 
838 	ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
839 
840 	return (DDI_SUCCESS);
841 }
842 
843 /*
844  * s1394_update_unit_dir_location()
845  *    Updates the unit-dir-offset property on the devinfo.
846  *    NOTE: ndi_prop_update_int() is interrupt callable (and thus won't block);
847  *    so, the caller doesn't drop topology_tree_mutex when calling this routine.
848  */
849 /*ARGSUSED*/
850 static void
851 s1394_update_unit_dir_location(s1394_hal_t *hal, dev_info_t *tdip,
852     uint_t offset)
853 {
854 	ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
855 	ASSERT(tdip != NULL);
856 
857 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, tdip, "unit-dir-offset",
858 	    offset);
859 }
860 
861 /*
862  * s1394_add_target_to_node()
863  *    adds target to the list of targets hanging off the node. Figures out
864  *    the node by searching the topology tree for the GUID corresponding
865  *    to the target. Points on_node field of target structure at the node.
866  */
867 void
868 s1394_add_target_to_node(s1394_target_t *target)
869 {
870 	s1394_target_t *t;
871 	s1394_hal_t *hal;
872 	uint32_t guid_hi;
873 	uint32_t guid_lo;
874 	int i;
875 	char name[MAXNAMELEN];
876 	char *ptr;
877 
878 	hal = target->on_hal;
879 	ASSERT(hal != NULL);
880 
881 	/* Topology tree must be locked when it gets here! */
882 	ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
883 
884 	/* target_list_rwlock should be held in write mode */
885 	ASSERT(rw_read_locked(&target->on_hal->target_list_rwlock) == 0);
886 
887 	if ((ptr = ddi_get_name_addr(target->target_dip)) == NULL) {
888 		return;
889 	}
890 
891 	(void) sprintf(name, ptr);
892 	/* Drop the ,<ADDR> part, if present */
893 	if ((ptr = strchr(name, ',')) != NULL)
894 		*ptr = '\0';
895 
896 	ptr = name;
897 	guid_hi = s1394_stoi(ptr, 8, 16);
898 	guid_lo = s1394_stoi(ptr + 8, 8, 16);
899 
900 	/* Search the HAL's node list for this GUID */
901 	for (i = 0; i < hal->number_of_nodes; i++) {
902 		if (CFGROM_VALID(&hal->topology_tree[i]) == B_TRUE) {
903 			ASSERT(hal->topology_tree[i].cfgrom != NULL);
904 
905 			if ((hal->topology_tree[i].node_guid_hi == guid_hi) &&
906 			    (hal->topology_tree[i].node_guid_lo == guid_lo)) {
907 				target->on_node = &hal->topology_tree[i];
908 				if ((t = hal->topology_tree[i].target_list) !=
909 				    NULL) {
910 					ASSERT(t != target);
911 					while (t->target_sibling != NULL) {
912 						t = t->target_sibling;
913 						ASSERT(t != target);
914 					}
915 					t->target_sibling = target;
916 				} else {
917 					hal->topology_tree[i].target_list =
918 					    target;
919 				}
920 
921 				/*
922 				 * update target_list in all targets on the
923 				 * node
924 				 */
925 				t = hal->topology_tree[i].target_list;
926 				while (t != NULL) {
927 					t->target_list =
928 					    hal->topology_tree[i].target_list;
929 					t = t->target_sibling;
930 				}
931 				break;
932 			}
933 		}
934 	}
935 }
936 
937 /*
938  * s1394_remove_target_from_node()
939  *    Removes target from the corresponding node's target_list.
940  */
941 void
942 s1394_remove_target_from_node(s1394_target_t *target)
943 {
944 	s1394_target_t *t, *t1;
945 	s1394_hal_t *hal;
946 
947 	hal = target->on_hal;
948 	ASSERT(hal != NULL);
949 
950 	/* Topology tree must be locked when it gets here! */
951 	ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
952 
953 	/* target_list_rwlock should be held in write mode */
954 	ASSERT(rw_read_locked(&target->on_hal->target_list_rwlock) == 0);
955 
956 	t = target->target_list;
957 	t1 = NULL;
958 	while (t != NULL) {
959 		if (t == target) {
960 			if (t1 == NULL) {
961 				target->target_list = t->target_sibling;
962 			} else {
963 				t1->target_sibling = t->target_sibling;
964 			}
965 			break;
966 		}
967 		t1 = t;
968 		t = t->target_sibling;
969 	}
970 	/* Update the target_list pointer in all the targets */
971 	if (target->on_node != NULL)
972 		target->on_node->target_list = target->target_list;
973 
974 	t = t1 = target->target_list;
975 	while (t != NULL) {
976 		t->target_list = t1;
977 		t = t->target_sibling;
978 	}
979 
980 	target->on_node = NULL;
981 	target->target_sibling = NULL;
982 }
983