5255 uts shouldn't open-code ISP2
[illumos-gate.git] / usr / src / uts / common / io / 1394 / s1394_hotplug.c
blob40c2e1902237ad117fe32c9e81c93c56fa198963
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * s1394_hotplug.c
31 * 1394 Services Layer Hotplug Routines
32 * This file contains routines that walk the old and topology
33 * trees, at bus reset time, creating devinfo's for new nodes and offlining
34 * nodes that are removed.
37 #include <sys/conf.h>
38 #include <sys/sysmacros.h>
39 #include <sys/ddi.h>
40 #include <sys/sunddi.h>
41 #include <sys/sunndi.h>
42 #include <sys/modctl.h>
43 #include <sys/sunddi.h>
44 #include <sys/ddi_impldefs.h>
45 #include <sys/types.h>
47 #include <sys/tnf_probe.h>
49 #include <sys/1394/t1394.h>
50 #include <sys/1394/s1394.h>
51 #include <sys/1394/h1394.h>
52 #include <sys/1394/ieee1394.h>
54 static void s1394_send_remove_event(s1394_hal_t *hal, dev_info_t *dip,
55 t1394_localinfo_t *localinfo);
56 static void s1394_send_insert_event(s1394_hal_t *hal, dev_info_t *dip,
57 t1394_localinfo_t *localinfo);
58 static dev_info_t *s1394_create_devinfo(s1394_hal_t *hal, s1394_node_t *node,
59 uint32_t *unit_dir, int nunit);
60 static void s1394_update_unit_dir_location(s1394_hal_t *hal, dev_info_t *tdip,
61 uint_t offset);
64 * s1394_send_remove_event()
65 * Invokes any "remove event" callback registered for dip. Passes
66 * t1394_localinfo_t as impl_data for the callback.
68 static void
69 s1394_send_remove_event(s1394_hal_t *hal, dev_info_t *dip,
70 t1394_localinfo_t *localinfo)
72 char name[128];
73 ddi_eventcookie_t cookie;
75 (void) sprintf(name, "%s%d", ddi_driver_name(dip),
76 ddi_get_instance(dip));
78 TNF_PROBE_1_DEBUG(s1394_send_remove_event_enter,
79 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, device,
80 name);
82 if (ndi_event_retrieve_cookie(hal->hal_ndi_event_hdl, dip,
83 DDI_DEVI_REMOVE_EVENT, &cookie, NDI_EVENT_NOPASS)
84 == NDI_SUCCESS) {
85 (void) ndi_event_run_callbacks(hal->hal_ndi_event_hdl, dip,
86 cookie, localinfo);
88 TNF_PROBE_0_DEBUG(s1394_send_remove_event_exit,
89 S1394_TNF_SL_HOTPLUG_STACK, "");
93 * s1394_send_insert_event()
94 * Invokes any "insert event" callback registered for dip. Passes
95 * t1394_localinfo_t as impl_data for the callback.
97 static void
98 s1394_send_insert_event(s1394_hal_t *hal, dev_info_t *dip,
99 t1394_localinfo_t *localinfo)
101 char name[128];
102 ddi_eventcookie_t cookie;
104 (void) sprintf(name, "%s%d", ddi_driver_name(dip),
105 ddi_get_instance(dip));
107 TNF_PROBE_1_DEBUG(s1394_send_insert_event_enter,
108 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, device,
109 name);
111 if (ndi_event_retrieve_cookie(hal->hal_ndi_event_hdl, dip,
112 DDI_DEVI_INSERT_EVENT, &cookie, NDI_EVENT_NOPASS) ==
113 NDI_SUCCESS)
114 (void) ndi_event_run_callbacks(hal->hal_ndi_event_hdl, dip,
115 cookie, localinfo);
117 TNF_PROBE_0_DEBUG(s1394_send_insert_event_exit,
118 S1394_TNF_SL_HOTPLUG_STACK, "");
122 * s1394_create_devinfo()
123 * This routine creates a devinfo corresponding to the unit_dir passed in.
124 * It adds "hp-node", "reg", "compatible" properties to the devinfo
125 * (formats for "reg" and "compatible" properties are specified by 1275
126 * binding for IEEE1394). If unable to create the devinfo and/or add the
127 * the properties, returns NULL, otherwise, returns the devinfo created.
129 * NOTE: All ndi_* routines are interrupt callable (and thus won't sleep).
130 * So, we don't drop topology_mutex across ndi calls.
132 static dev_info_t *
133 s1394_create_devinfo(s1394_hal_t *hal, s1394_node_t *node, uint32_t *unit_dir,
134 int nunit)
136 dev_info_t *hal_dip;
137 uint32_t *root_dir;
138 dev_info_t *target_dip;
140 int root_dir_len;
141 int result, i, j, spec_id, sw_version;
142 int mod_ven, mod_hw, mod_spec, mod_sw;
143 int node_ven, node_hw, node_spec, node_sw;
145 /*LINTED type is unused*/
146 uint32_t type, key, value;
147 uint32_t unit_spec_id, unit_sw_version;
148 uint32_t node_spec_id, node_sw_version;
149 uint32_t node_vendor_id, node_hw_version;
150 uint32_t module_spec_id, module_sw_version;
151 uint32_t module_vendor_id, module_hw_version;
153 char *fmt = "firewire%06x,%06x";
155 char *buf[5], data[5][24];
156 uint32_t reg[6];
158 ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
160 TNF_PROBE_2_DEBUG(s1394_create_devinfo_enter,
161 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_uint, guid_hi,
162 node->node_guid_hi, tnf_uint, guid_lo, node->node_guid_lo);
164 hal_dip = hal->halinfo.dip;
166 /* Allocate and init a new device node instance. */
167 result = ndi_devi_alloc(hal_dip, "unit", (pnode_t)DEVI_SID_NODEID,
168 &target_dip);
170 if (result != NDI_SUCCESS) {
171 cmn_err(CE_NOTE, "!Unable to create devinfo"
172 " (node's GUID %08x%08x)", node->node_guid_hi,
173 node->node_guid_lo);
174 TNF_PROBE_2(s1394_create_devinfo_fail_alloc,
175 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_uint, guid_hi,
176 node->node_guid_hi, tnf_uint, guid_lo, node->node_guid_lo);
177 TNF_PROBE_0_DEBUG(s1394_create_devinfo_exit,
178 S1394_TNF_SL_HOTPLUG_STACK, "");
179 return (NULL);
182 /* Add "hp-node" property */
183 result = ndi_prop_update_int(DDI_DEV_T_NONE, target_dip, "hp-node", 0);
184 if (result != NDI_SUCCESS) {
185 cmn_err(CE_NOTE, "!Unable to add \"hp-node\" property"
186 " (node's GUID %08x%08x)", node->node_guid_hi,
187 node->node_guid_lo);
188 #if defined(DEBUG)
189 cmn_err(CE_CONT, "!Error code %d", result);
190 #endif
191 TNF_PROBE_3(s1394_create_devinfo_hp_node,
192 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_uint, guid_hi,
193 node->node_guid_hi, tnf_uint, guid_lo, node->node_guid_lo,
194 tnf_int, error, result);
195 ndi_prop_remove_all(target_dip);
196 (void) ndi_devi_free(target_dip);
197 TNF_PROBE_0_DEBUG(s1394_create_devinfo_exit,
198 S1394_TNF_SL_HOTPLUG_STACK, "");
199 return (NULL);
202 spec_id = sw_version = mod_ven = mod_hw = mod_spec = mod_sw =
203 node_ven = node_hw = node_spec = node_sw = 0;
204 unit_sw_version = node_sw_version = node_hw_version =
205 module_sw_version = module_hw_version = 0;
208 root_dir = CFGROM_ROOT_DIR(node->cfgrom);
209 root_dir_len = CFGROM_DIR_LEN(root_dir);
211 for (i = 0; i < root_dir_len; i++) {
213 CFGROM_TYPE_KEY_VALUE(root_dir[i + 1], type, key, value);
214 switch (key) {
216 case IEEE1212_MODULE_VENDOR_ID:
217 module_vendor_id = value;
218 mod_ven++;
219 break;
220 case IEEE1212_MODULE_HW_VERSION:
221 module_hw_version = value;
222 mod_hw++;
223 break;
224 case IEEE1212_MODULE_SPEC_ID:
225 module_spec_id = value;
226 mod_spec++;
227 break;
228 case IEEE1212_MODULE_SW_VERSION:
229 module_sw_version = value;
230 mod_sw++;
231 break;
232 case IEEE1212_NODE_VENDOR_ID:
233 node_vendor_id = value;
234 node_ven++;
235 break;
236 case IEEE1212_NODE_UNIQUE_ID: {
237 uint32_t *node_unique_leaf =
238 &root_dir[i + 1] + value;
239 node_vendor_id = (node_unique_leaf[1] >> 8);
240 node_ven++;
242 break;
243 case IEEE1212_NODE_HW_VERSION:
244 node_hw_version = value;
245 node_hw++;
246 break;
247 case IEEE1212_NODE_SPEC_ID:
248 node_spec_id = value;
249 node_spec++;
250 break;
251 case IEEE1212_NODE_SW_VERSION:
252 node_sw_version = value;
253 node_sw++;
254 break;
257 if (mod_ven && mod_hw && mod_spec && mod_sw && node_ven &&
258 node_hw && node_spec && node_sw) {
259 break;
264 * Search for unit spec and version
266 for (i = 0; i < CFGROM_DIR_LEN(unit_dir); i++) {
268 CFGROM_TYPE_KEY_VALUE(unit_dir[i + 1], type, key, value);
269 if (key == IEEE1212_UNIT_SPEC_ID) {
271 unit_spec_id = value;
272 spec_id++;
273 } else if (key == IEEE1212_UNIT_SW_VERSION) {
275 unit_sw_version = value;
276 sw_version++;
278 if (spec_id && sw_version)
279 break;
283 * Refer to IEEE1212 (pages 90-92) for information regarding various
284 * id's. Module_Vendor_Id is required. Node_Vendor_Id is optional and
285 * if not implemented, its assumed value is Module_Vendor_Id.
286 * Module_Spec_Id is optional and if not implemented, its assumed value
287 * is Module_Vendor_Id. Node_Spec_Id is optional, and if not
288 * implemented, its assumed value is Node_Vendor_Id. Unit_Spec_Id is
289 * optional, and if not implemented, its assumed value is
290 * Node_Vendor_Id.
292 if (node_ven == 0) {
293 node_vendor_id = module_vendor_id;
294 node_ven++;
297 if (node_spec == 0) {
298 node_spec_id = node_vendor_id;
299 node_spec++;
302 if (mod_spec == 0) {
303 module_spec_id = module_vendor_id;
304 mod_spec++;
307 if (spec_id == 0) {
308 unit_spec_id = node_vendor_id;
309 spec_id++;
312 i = 0;
313 if (sw_version != 0) {
314 buf[i] = data[i];
315 (void) sprintf(data[i++], fmt, unit_spec_id, unit_sw_version);
317 if (node_sw != 0) {
318 buf[i] = data[i];
319 (void) sprintf(data[i++], fmt, node_spec_id, node_sw_version);
321 if (node_hw != 0) {
322 buf[i] = data[i];
323 (void) sprintf(data[i++], fmt, node_vendor_id, node_hw_version);
325 if (mod_sw != 0) {
326 buf[i] = data[i];
327 (void) sprintf(data[i++], fmt, module_spec_id,
328 module_sw_version);
330 if (mod_hw != 0) {
331 buf[i] = data[i];
332 (void) sprintf(data[i++], fmt, module_vendor_id,
333 module_hw_version);
336 result = ndi_prop_update_string_array(DDI_DEV_T_NONE, target_dip,
337 "compatible", (char **)&buf, i);
338 if (result != NDI_SUCCESS) {
339 cmn_err(CE_NOTE, "!Unable to add \"compatible\" property"
340 " (node's GUID %08x%08x)", node->node_guid_hi,
341 node->node_guid_lo);
342 #if defined(DEBUG)
343 cmn_err(CE_CONT, "!Error code %d; nelements %d", result, i);
344 for (j = 0; j < i; j++) {
345 cmn_err(CE_CONT, "!buf[%d]: %s", j, buf[j]);
347 #endif
348 ndi_prop_remove_all(target_dip);
349 (void) ndi_devi_free(target_dip);
350 TNF_PROBE_4(s1394_create_devinfo_fail_compat,
351 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_uint, guid_hi,
352 node->node_guid_hi, tnf_uint, guid_lo, node->node_guid_lo,
353 tnf_int, error, result, tnf_int, nelements, i);
354 TNF_PROBE_0_DEBUG(s1394_create_devinfo_exit,
355 S1394_TNF_SL_HOTPLUG_STACK, "");
356 return (NULL);
359 for (j = 0; j < i; j++) {
360 TNF_PROBE_2_DEBUG(s1394_create_devinfo_props,
361 S1394_TNF_SL_HOTPLUG_STACK, "",
362 tnf_int, compat_index, j,
363 tnf_string, compat_prop, buf[j]);
366 /* GUID,ADDR */
367 reg[0] = node->node_guid_hi;
368 reg[1] = node->node_guid_lo;
369 s1394_cfgrom_parse_unit_dir(unit_dir, &reg[2], &reg[3], &reg[4],
370 &reg[5]);
372 reg[3] = nunit;
374 result = ndi_prop_update_int_array(DDI_DEV_T_NONE, target_dip, "reg",
375 (int *)reg, 6);
376 if (result != NDI_SUCCESS) {
377 cmn_err(CE_NOTE, "!Unable to add \"reg\" property");
378 #if defined(DEBUG)
379 cmn_err(CE_CONT, "!Error code %d", result);
380 for (j = 0; j < 6; j++) {
381 cmn_err(CE_CONT, "!reg[%d]: 0x%08x", j, reg[j]);
383 #endif
384 ndi_prop_remove_all(target_dip);
385 (void) ndi_devi_free(target_dip);
386 TNF_PROBE_3(s1394_create_devinfo_fail_reg,
387 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_uint, guid_hi,
388 node->node_guid_hi, tnf_uint, guid_lo, node->node_guid_lo,
389 tnf_int, error, result);
390 TNF_PROBE_0_DEBUG(s1394_create_devinfo_exit,
391 S1394_TNF_SL_HOTPLUG_STACK, "");
392 return (NULL);
395 TNF_PROBE_1_DEBUG(s1394_create_devinfo_exit,
396 S1394_TNF_SL_HOTPLUG_STACK, "",
397 tnf_opaque, target_dip, target_dip);
399 return (target_dip);
403 * s1394_devi_find()
404 * Searches all children of pdip for a match of name@caddr. Builds the
405 * name and address of each child node by looking up the reg property on
406 * the node and compares the built name@addr with the name@addr passed in.
407 * Returns the child dip if a match is found, otherwise, returns NULL.
408 * NOTE:
409 * This routine is decidedly non-ddi. We had to use this one since
410 * ndi_devi_find() can find only nodes that have valid addr field
411 * set and that won't happen unless the node goes through INITCHILD
412 * (at which time nx1394.c calls ddi_set_name_addr()). If, in future,
413 * the ndi_devi_find() provides a way of looking up nodes using criteria
414 * other than addr, we can get rid of this routine.
416 /*ARGSUSED*/
417 dev_info_t *
418 s1394_devi_find(dev_info_t *pdip, char *name, char *caddr)
420 int i, reglen;
421 char addr[32];
422 uint32_t *regptr;
423 dev_info_t *cdip = NULL;
425 ASSERT((name != NULL) && (caddr != NULL));
427 TNF_PROBE_1_DEBUG(s1394_devi_find_enter, S1394_TNF_SL_HOTPLUG_STACK,
428 "", tnf_string, addr, caddr);
431 * for each child of this parent, find name and addr and match with
432 * name and caddr passed in.
434 for (cdip = (dev_info_t *)DEVI(pdip)->devi_child; cdip != NULL;
435 cdip = (dev_info_t *)DEVI(cdip)->devi_sibling) {
437 i = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip,
438 DDI_PROP_DONTPASS, "reg", (int **)&regptr,
439 (uint_t *)&reglen);
441 if (i != DDI_PROP_SUCCESS)
442 continue;
445 * Construct addr from the reg property (addr is of the format
446 * GGGGGGGGGGGGGGGG[,AAAAAAAAAAAA], where GGGGGGGGGGGGGGGG is
447 * the address and AAAAAAAAAAAA is the optional unit address)
449 if (regptr[2] != NULL || regptr[3] != NULL) {
450 (void) sprintf(addr, "%08x%08x,%04x%08x", regptr[0],
451 regptr[1], regptr[2], regptr[3]);
452 } else {
453 (void) sprintf(addr, "%08x%08x", regptr[0], regptr[1]);
455 ddi_prop_free(regptr);
457 if (strcmp(caddr, addr) == 0) {
458 ASSERT(strcmp(ddi_node_name(cdip), name) == 0);
459 break;
463 if (cdip == NULL) {
464 TNF_PROBE_1(s1394_devi_find_no_match,
465 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, addr, caddr);
468 TNF_PROBE_0_DEBUG(s1394_devi_find_exit, S1394_TNF_SL_HOTPLUG_STACK, "");
470 return (cdip);
474 * s1394_update_devinfo_tree()
475 * Parses the config rom for the passed in node and creates/updates devinfo's
476 * for each unit directory found. If the devinfo corresponding to a unit
477 * already exists, any insert event callbacks registered for that devinfo
478 * are called (topology tree is unlocked and relocked around these
479 * callbacks). Returns DDI_SUCCESS if everything went fine and DDI_FAILURE
480 * if unable to reacquire the lock after callbacks (relock fails because of
481 * an intervening bus reset or if the services layer kills the bus reset
482 * thread). The node is marked as parsed before returning.
485 s1394_update_devinfo_tree(s1394_hal_t *hal, s1394_node_t *node)
487 dev_info_t *tdip;
488 int j, units, d, lockfail = 0;
489 s1394_target_t *target, *t;
490 uint32_t hi, lo, size_hi, size_lo, type, key, value;
491 uint32_t *ptr, *root_dir, dir_len;
492 t1394_localinfo_t linfo;
494 uint32_t *unit_dir_ptrs[32];
495 dev_info_t *devinfo_ptrs[32];
496 uint32_t new_devinfo = 0; /* to keep track of new allocations */
498 char caddr[32];
500 ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
502 ASSERT(CFGROM_PARSED(node) == B_FALSE);
503 ASSERT(node->cfgrom != NULL);
505 TNF_PROBE_2_DEBUG(s1394_update_devinfo_tree_enter,
506 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int, node_num,
507 node->node_num, tnf_opaque, cfgrom, node->cfgrom);
509 /* scan through config rom looking for unit dirs */
510 root_dir = CFGROM_ROOT_DIR(node->cfgrom);
512 if (node->cfgrom_valid_size < CFGROM_DIR_LEN(root_dir))
513 dir_len = node->cfgrom_valid_size;
514 else
515 dir_len = CFGROM_DIR_LEN(root_dir);
517 CFGROM_TYPE_KEY_VALUE(root_dir[0], type, key, value);
518 if (s1394_valid_dir(hal, node, key, root_dir) == B_FALSE) {
519 cmn_err(CE_NOTE,
520 "!Bad root directory in config rom (node's GUID %08x%08x)",
521 node->node_guid_hi, node->node_guid_lo);
523 TNF_PROBE_1_DEBUG(s1394_update_devinfo_tree_exit,
524 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_string, msg,
525 "bad directory");
527 SET_CFGROM_PARSED(node);
528 CLEAR_CFGROM_GEN_CHANGED(node); /* if set */
529 CLEAR_CFGROM_NEW_ALLOC(node);
531 return (DDI_SUCCESS);
534 for (units = 0, j = 1; j <= dir_len; j++) {
535 CFGROM_TYPE_KEY_VALUE(root_dir[j], type, key, value);
536 if (key == IEEE1212_UNIT_DIRECTORY && type ==
537 IEEE1212_DIRECTORY_TYPE) {
538 ptr = &root_dir[j] + value;
539 if (s1394_valid_dir(hal, node, key, ptr) == B_TRUE) {
540 unit_dir_ptrs[units++] = ptr;
541 } else {
542 cmn_err(CE_NOTE, "!Bad unit directory in config"
543 " rom (node's GUID %08x%08x)",
544 node->node_guid_hi, node->node_guid_lo);
545 TNF_PROBE_2(s1394_update_devinfo_tree_bad_dir,
546 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_uint,
547 guid_hi, node->node_guid_hi, tnf_uint,
548 guid_lo, node->node_guid_lo);
553 for (d = 0, j = 0; j < units; j++) {
555 s1394_cfgrom_parse_unit_dir(unit_dir_ptrs[j],
556 &hi, &lo, &size_hi, &size_lo);
558 lo = j;
560 if (hi || lo) {
561 (void) sprintf(caddr, "%08x%08x,%04x%08x",
562 node->node_guid_hi, node->node_guid_lo, hi, lo);
563 } else {
564 (void) sprintf(caddr, "%08x%08x",
565 node->node_guid_hi, node->node_guid_lo);
568 tdip = s1394_devi_find(hal->halinfo.dip, "unit", caddr);
569 if (tdip != NULL) {
571 rw_enter(&hal->target_list_rwlock, RW_WRITER);
572 target = s1394_target_from_dip_locked(hal, tdip);
573 if (target != NULL) {
574 target->target_sibling = NULL;
575 target->on_node = node;
576 target->target_state &= ~S1394_TARG_GONE;
577 target->unit_dir = unit_dir_ptrs[j] - root_dir;
579 if ((t = node->target_list) != NULL) {
580 ASSERT(t != target);
581 while (t->target_sibling != NULL) {
582 t = t->target_sibling;
583 ASSERT(t != target);
585 t->target_sibling = target;
586 } else {
587 node->target_list = target;
590 target->target_list = node->target_list;
592 rw_exit(&hal->target_list_rwlock);
594 s1394_update_unit_dir_location(hal, tdip,
595 unit_dir_ptrs[j] - root_dir);
597 } else {
598 /* create devinfo for unit@caddr */
599 tdip = s1394_create_devinfo(hal, node,
600 unit_dir_ptrs[j], j);
601 if (tdip != NULL) {
602 new_devinfo |= (1 << d);
603 s1394_update_unit_dir_location(hal, tdip,
604 unit_dir_ptrs[j] - root_dir);
607 if (tdip != NULL)
608 devinfo_ptrs[d++] = tdip;
611 ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
612 /* Online all valid units */
613 for (j = 0; j < d; j++) {
614 if ((new_devinfo & (1 << j)) == 0) {
615 linfo.bus_generation = hal->generation_count;
616 linfo.local_nodeID = hal->node_id;
618 /* don't need to drop topology_tree_mutex across ndi calls */
619 (void) ndi_devi_online_async(devinfo_ptrs[j], 0);
620 if ((new_devinfo & (1 << j)) == 0) {
622 * send an insert event if this an existing devinfo.
623 * drop and reacquire topology_tree_mutex across
624 * the event calls
626 s1394_unlock_tree(hal);
627 s1394_send_insert_event(hal, devinfo_ptrs[j], &linfo);
628 if (s1394_lock_tree(hal) != DDI_SUCCESS) {
629 TNF_PROBE_4(s1394_update_devinfo_tree_lock_fail,
630 S1394_TNF_SL_HOTPLUG_ERROR, "",
631 tnf_int, node_num, node->node_num,
632 tnf_opaque, cfgrom, node->cfgrom,
633 tnf_int, unit, j,
634 tnf_opaque, devinfo, devinfo_ptrs[j]);
635 lockfail = 1;
636 break;
641 if (lockfail) {
642 TNF_PROBE_0_DEBUG(s1394_update_devinfo_tree_exit,
643 S1394_TNF_SL_HOTPLUG_ERROR, "");
644 return (DDI_FAILURE);
647 SET_CFGROM_PARSED(node);
648 CLEAR_CFGROM_GEN_CHANGED(node); /* if set */
649 CLEAR_CFGROM_NEW_ALLOC(node);
651 TNF_PROBE_0_DEBUG(s1394_update_devinfo_tree_exit,
652 S1394_TNF_SL_HOTPLUG_STACK, "");
654 return (DDI_SUCCESS);
658 * s1394_offline_node()
659 * Offlines a node. This involves marking all targets attached to the
660 * node as gone, invoking any remove event callbacks and calling
661 * ndi_devi_offline to mark the devinfo as OFFLINE (for each unit
662 * directory on the node). The tree is unlocked and relocked around
663 * the callbacks. If unable to relock the tree, DDI_FAILURE, else
664 * returns DDI_SUCCESS.
667 s1394_offline_node(s1394_hal_t *hal, s1394_node_t *node)
669 s1394_target_t *t;
670 dev_info_t *tdip;
671 int j, d, units;
672 uint32_t *unit_dir_ptrs[32];
673 dev_info_t *devinfo_ptrs[32];
674 t1394_localinfo_t linfo;
675 uint_t node_num;
676 uint32_t *ptr, *root_dir, dir_len;
677 uint32_t hi, lo, size_hi, size_lo, type, key, value;
678 char caddr[32];
680 node_num = node->node_num;
682 TNF_PROBE_1_DEBUG(s1394_offline_node_enter, S1394_TNF_SL_HOTPLUG_STACK,
683 "", tnf_uint, node_num, node_num);
685 ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
687 d = 0;
688 rw_enter(&hal->target_list_rwlock, RW_WRITER);
689 t = node->target_list;
690 while (t != NULL) {
691 TNF_PROBE_2(s1394_process_old_tree_mark,
692 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int, node_num, node_num,
693 tnf_opaque, target, t);
694 t->target_state |= S1394_TARG_GONE;
695 t->on_node = NULL;
696 t = t->target_sibling;
698 rw_exit(&hal->target_list_rwlock);
700 /* scan through config rom looking for unit dirs */
701 root_dir = CFGROM_ROOT_DIR(node->cfgrom);
703 if (node->cfgrom_valid_size < CFGROM_DIR_LEN(root_dir))
704 dir_len = node->cfgrom_valid_size;
705 else
706 dir_len = CFGROM_DIR_LEN(root_dir);
708 CFGROM_TYPE_KEY_VALUE(root_dir[0], type, key, value);
710 for (units = 0, j = 1; j <= dir_len; j++) {
711 CFGROM_TYPE_KEY_VALUE(root_dir[j], type, key, value);
712 if (key == IEEE1212_UNIT_DIRECTORY && type ==
713 IEEE1212_DIRECTORY_TYPE) {
714 ptr = &root_dir[j] + value;
715 if (s1394_valid_dir(hal, node, key, ptr) == B_TRUE) {
716 unit_dir_ptrs[units++] = ptr;
721 for (d = 0, j = 0; j < units; j++) {
723 s1394_cfgrom_parse_unit_dir(unit_dir_ptrs[j],
724 &hi, &lo, &size_hi, &size_lo);
726 lo = j;
728 if (hi || lo) {
729 (void) sprintf(caddr, "%08x%08x,%04x%08x",
730 node->node_guid_hi, node->node_guid_lo, hi, lo);
731 } else {
732 (void) sprintf(caddr, "%08x%08x",
733 node->node_guid_hi, node->node_guid_lo);
736 if ((tdip = s1394_devi_find(hal->halinfo.dip, "unit", caddr)) !=
737 NULL)
738 devinfo_ptrs[d++] = tdip;
741 node->old_node = NULL;
743 linfo.bus_generation = hal->generation_count;
744 linfo.local_nodeID = hal->node_id;
746 for (j = 0; j < d; j++) {
747 s1394_unlock_tree(hal);
749 TNF_PROBE_2(s1394_offline_node,
750 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int, node_num, node_num,
751 tnf_opaque, devinfo, devinfo_ptrs[j]);
753 s1394_send_remove_event(hal, devinfo_ptrs[j], &linfo);
754 (void) ndi_devi_offline(devinfo_ptrs[j], NDI_DEVI_REMOVE);
755 if (s1394_lock_tree(hal) != DDI_SUCCESS) {
756 TNF_PROBE_2(s1394_offline_node,
757 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
758 "unlock to relock tree", tnf_uint, node_num,
759 node_num);
760 TNF_PROBE_0_DEBUG(s1394_offline_node_exit,
761 S1394_TNF_SL_HOTPLUG_STACK, "");
762 return (DDI_FAILURE);
766 ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
767 TNF_PROBE_0_DEBUG(s1394_offline_node_exit, S1394_TNF_SL_HOTPLUG_STACK,
768 "");
769 return (DDI_SUCCESS);
773 * s1394_process_topology_tree()
774 * Walks the topology tree, processing each node. If node that has
775 * already been parsed, updates the generation property on all devinfos
776 * for the node. Also, if the node exists in both old & new trees, ASSERTS
777 * that both point to the same config rom. If the node has valid config
778 * rom but hasn't been parsed yet, calls s1394_update_devinfo_tree()
779 * to parse and create devinfos for the node. Kicks off further config
780 * rom reading if only the bus info block for the node is read.
781 * Returns DDI_SUCCESS if everything went fine, else returns DDI_FAILURE
782 * (for eg. unable to reacquire the tree lock etc). wait_for_cbs argument
783 * tells the caller if some completions can be expected. wait_gen tells
784 * the generation the commands were issued at.
787 s1394_process_topology_tree(s1394_hal_t *hal, int *wait_for_cbs,
788 uint_t *wait_gen)
790 int i;
791 uint_t hal_node_num, number_of_nodes;
792 s1394_node_t *node, *onode;
793 s1394_status_t status;
795 ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
797 TNF_PROBE_0_DEBUG(s1394_process_topology_tree_enter,
798 S1394_TNF_SL_HOTPLUG_STACK, "");
800 if (s1394_lock_tree(hal) != DDI_SUCCESS) {
801 TNF_PROBE_0(s1394_process_topology_tree_lock_failed,
802 S1394_TNF_SL_HOTPLUG_ERROR, "");
803 TNF_PROBE_0_DEBUG(s1394_process_topology_tree_exit,
804 S1394_TNF_SL_HOTPLUG_STACK, "");
805 return (DDI_FAILURE);
808 hal_node_num = IEEE1394_NODE_NUM(hal->node_id);
809 hal->cfgroms_being_read = 0;
810 number_of_nodes = hal->number_of_nodes;
811 s1394_unlock_tree(hal);
813 for (i = 0; i < number_of_nodes; i++) {
815 if (i == hal_node_num)
816 continue;
817 if (s1394_lock_tree(hal) != DDI_SUCCESS) {
818 return (DDI_FAILURE);
820 node = &hal->topology_tree[i];
822 TNF_PROBE_4_DEBUG(s1394_process_topology_tree,
823 S1394_TNF_SL_HOTPLUG_STACK, "",
824 tnf_int, node_num, i,
825 tnf_int, parsed, CFGROM_PARSED(node),
826 tnf_int, matched, NODE_MATCHED(node),
827 tnf_int, visited, NODE_VISITED(node));
829 if (LINK_ACTIVE(node) == B_FALSE) {
830 s1394_unlock_tree(hal);
831 continue;
833 if (node->cfgrom == NULL) {
834 s1394_unlock_tree(hal);
835 continue;
838 onode = node->old_node;
840 if (onode != NULL && onode->cfgrom != NULL && node->cfgrom !=
841 NULL) {
843 * onode->cfgrom != node->cfgrom should have been
844 * handled by s1394_match_GUID()!!!
846 if (onode->cfgrom != node->cfgrom)
847 TNF_PROBE_5(s1394_process_topology_tree_err,
848 S1394_TNF_SL_HOTPLUG_ERROR, "",
849 tnf_int, node_num, i, tnf_int, gen_changed,
850 CFGROM_GEN_CHANGED(node), tnf_int, parsed,
851 CFGROM_PARSED(node), tnf_opaque, old_cfgrom,
852 onode->cfgrom, tnf_opaque, new_cfgrom,
853 node->cfgrom);
854 ASSERT(onode->cfgrom == node->cfgrom);
857 if (CFGROM_PARSED(node) == B_FALSE && CFGROM_ALL_READ(node) ==
858 B_TRUE) {
859 ASSERT((node->cfgrom_size <
860 IEEE1394_CONFIG_ROM_QUAD_SZ) ||
861 NODE_MATCHED(node) == B_TRUE);
862 rw_enter(&hal->target_list_rwlock, RW_READER);
863 ASSERT(node->target_list == NULL);
864 rw_exit(&hal->target_list_rwlock);
865 if (s1394_update_devinfo_tree(hal, node) ==
866 DDI_FAILURE) {
867 ASSERT(MUTEX_NOT_HELD(
868 &hal->topology_tree_mutex));
869 TNF_PROBE_1(s1394_process_topology_tree,
870 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string,
871 msg, "failure from update devinfo");
872 TNF_PROBE_0_DEBUG(
873 s1394_process_topology_tree_exit,
874 S1394_TNF_SL_HOTPLUG_STACK, "");
875 return (DDI_FAILURE);
877 } else if (CFGROM_PARSED(node) == B_FALSE && CFGROM_BIB_READ(
878 node) == B_TRUE) {
879 if (s1394_read_rest_of_cfgrom(hal, node, &status) !=
880 DDI_SUCCESS) {
881 TNF_PROBE_1(s1394_process_topology_tree,
882 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string,
883 msg, "failure reading rest of cfgrom");
884 if ((status & S1394_LOCK_FAILED) == 0) {
885 ASSERT(MUTEX_HELD(&hal->
886 topology_tree_mutex));
887 *wait_for_cbs = 0;
888 s1394_unlock_tree(hal);
890 TNF_PROBE_0_DEBUG(
891 s1394_process_topology_tree_exit,
892 S1394_TNF_SL_HOTPLUG_STACK, "");
893 return (DDI_FAILURE);
894 } else {
895 *wait_for_cbs = 1;
896 *wait_gen = hal->br_cfgrom_read_gen;
900 s1394_unlock_tree(hal);
904 * flag the tree as processed; if a single bus reset happens after
905 * this, we will use tree matching.
907 if (s1394_lock_tree(hal) != DDI_SUCCESS) {
908 TNF_PROBE_1(s1394_process_topology_tree,
909 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string,
910 msg, "relock failed while marking tree processed");
911 TNF_PROBE_0_DEBUG(s1394_process_topology_tree_exit,
912 S1394_TNF_SL_HOTPLUG_STACK, "");
913 return (DDI_FAILURE);
915 hal->topology_tree_processed = B_TRUE;
916 s1394_unlock_tree(hal);
918 TNF_PROBE_1_DEBUG(s1394_process_topology_tree_exit,
919 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int, hal_instance,
920 ddi_get_instance(hal->halinfo.dip));
922 return (DDI_SUCCESS);
926 * s1394_process_old_tree()
927 * Walks through the old tree and offlines nodes that are removed. Nodes
928 * with an active link in the old tree but link powered off in the current
929 * generation are also offlined, as well as nodes with invalid config
930 * rom in current generation.
931 * The topology tree is locked/unlocked while walking through all the nodes;
932 * if the locking fails at any stage, stops further walking and returns
933 * DDI_FAILURE. Returns DDI_SUCCESS if everything went fine.
936 s1394_process_old_tree(s1394_hal_t *hal)
938 int i;
939 uint_t hal_node_num_old, old_number_of_nodes;
940 s1394_node_t *onode;
942 TNF_PROBE_0_DEBUG(s1394_process_old_tree_enter,
943 S1394_TNF_SL_HOTPLUG_STACK, "");
946 * NODE_MATCHED(onode) == 0 indicates this node doesn't exist
947 * any more.
949 ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
951 if (s1394_lock_tree(hal) != DDI_SUCCESS) {
952 TNF_PROBE_0(s1394_process_old_tree_lock_failed,
953 S1394_TNF_SL_HOTPLUG_ERROR, "");
954 TNF_PROBE_0_DEBUG(s1394_process_old_tree_exit,
955 S1394_TNF_SL_HOTPLUG_STACK, "");
956 return (DDI_FAILURE);
958 hal_node_num_old = IEEE1394_NODE_NUM(hal->old_node_id);
959 old_number_of_nodes = hal->old_number_of_nodes;
960 s1394_unlock_tree(hal);
962 for (i = 0; i < old_number_of_nodes; i++) {
964 if (i == hal_node_num_old)
965 continue;
966 if (s1394_lock_tree(hal) != DDI_SUCCESS) {
967 TNF_PROBE_2(s1394_process_old_tree,
968 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string, msg,
969 "lock failed while processing node", tnf_uint,
970 node_num, i);
971 TNF_PROBE_0_DEBUG(s1394_process_old_tree_exit,
972 S1394_TNF_SL_HOTPLUG_STACK, "");
973 return (DDI_FAILURE);
976 onode = &hal->old_tree[i];
978 if (onode->cfgrom == NULL) {
979 CLEAR_CFGROM_STATE(onode);
980 s1394_unlock_tree(hal);
981 continue;
984 TNF_PROBE_1_DEBUG(s1394_process_old_tree,
985 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_opaque,
986 cfgrom, onode->cfgrom);
988 TNF_PROBE_5_DEBUG(s1394_process_old_tree,
989 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_int,
990 node_num, i, tnf_int, parsed, CFGROM_PARSED(onode), tnf_int,
991 matched, NODE_MATCHED(onode), tnf_int, visited,
992 NODE_VISITED(onode), tnf_int, generation_changed,
993 CFGROM_GEN_CHANGED(onode));
996 * onode->cur_node == NULL iff we couldn't read cfgrom in the
997 * current generation in non-tree matching case (and thus
998 * match_GUIDs couldn't set cur_node).
1000 if (NODE_MATCHED(onode) == B_FALSE || (onode->cur_node ==
1001 NULL || ((CFGROM_VALID(onode) == B_TRUE &&
1002 CFGROM_VALID(onode->cur_node) == B_FALSE) ||
1003 (LINK_ACTIVE(onode) == B_TRUE && LINK_ACTIVE(onode->
1004 cur_node) == B_FALSE)))) {
1006 if (onode->cur_node != NULL && CFGROM_VALID(onode) ==
1007 B_TRUE && CFGROM_VALID(onode->cur_node) == B_FALSE)
1008 TNF_PROBE_1_DEBUG
1009 (s1394_process_old_tree_invalid_cfgrom,
1010 S1394_TNF_SL_HOTPLUG_STACK, "",
1011 tnf_int, node_num, i);
1012 if (onode->cur_node != NULL && LINK_ACTIVE(onode) ==
1013 B_TRUE && LINK_ACTIVE(onode->cur_node) == B_FALSE)
1014 TNF_PROBE_1_DEBUG
1015 (s1394_process_old_tree_link_off,
1016 S1394_TNF_SL_HOTPLUG_STACK,
1017 "", tnf_int, node_num, i);
1018 if (s1394_offline_node(hal, onode) != DDI_SUCCESS) {
1019 TNF_PROBE_2(s1394_process_old_tree,
1020 S1394_TNF_SL_HOTPLUG_ERROR, "", tnf_string,
1021 msg, "failure from offline node", tnf_uint,
1022 node_num, i);
1023 TNF_PROBE_0_DEBUG(s1394_process_old_tree_exit,
1024 S1394_TNF_SL_HOTPLUG_STACK, "");
1025 return (DDI_FAILURE);
1027 s1394_free_cfgrom(hal, onode, S1394_FREE_CFGROM_OLD);
1030 s1394_unlock_tree(hal);
1033 ASSERT(MUTEX_NOT_HELD(&hal->topology_tree_mutex));
1035 TNF_PROBE_0_DEBUG(s1394_process_old_tree_exit,
1036 S1394_TNF_SL_HOTPLUG_STACK, "");
1038 return (DDI_SUCCESS);
1042 * s1394_update_unit_dir_location()
1043 * Updates the unit-dir-offset property on the devinfo.
1044 * NOTE: ndi_prop_update_int() is interrupt callable (and thus won't block);
1045 * so, the caller doesn't drop topology_tree_mutex when calling this routine.
1047 /*ARGSUSED*/
1048 static void
1049 s1394_update_unit_dir_location(s1394_hal_t *hal, dev_info_t *tdip,
1050 uint_t offset)
1052 ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
1053 ASSERT(tdip != NULL);
1055 TNF_PROBE_1_DEBUG(s1394_update_unit_dir_location_enter,
1056 S1394_TNF_SL_HOTPLUG_STACK, "", tnf_uint, offset, offset);
1057 (void) ndi_prop_update_int(DDI_DEV_T_NONE, tdip, "unit-dir-offset",
1058 offset);
1059 TNF_PROBE_0_DEBUG(s1394_update_unit_dir_location_exit,
1060 S1394_TNF_SL_HOTPLUG_STACK, "");
1064 * s1394_add_target_to_node()
1065 * adds target to the list of targets hanging off the node. Figures out
1066 * the node by searching the topology tree for the GUID corresponding
1067 * to the target. Points on_node field of target structure at the node.
1069 void
1070 s1394_add_target_to_node(s1394_target_t *target)
1072 s1394_target_t *t;
1073 s1394_hal_t *hal;
1074 uint32_t guid_hi;
1075 uint32_t guid_lo;
1076 int i;
1077 char name[MAXNAMELEN];
1078 char *ptr;
1080 TNF_PROBE_0_DEBUG(s1394_add_target_to_node_enter,
1081 S1394_TNF_SL_HOTPLUG_STACK, "");
1083 hal = target->on_hal;
1084 ASSERT(hal != NULL);
1086 /* Topology tree must be locked when it gets here! */
1087 ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
1089 /* target_list_rwlock should be held in write mode */
1090 ASSERT(rw_read_locked(&target->on_hal->target_list_rwlock) == 0);
1092 if ((ptr = ddi_get_name_addr(target->target_dip)) == NULL) {
1093 TNF_PROBE_0_DEBUG(s1394_add_target_to_node_exit_no_name,
1094 S1394_TNF_SL_HOTPLUG_STACK, "");
1095 return;
1098 (void) sprintf(name, ptr);
1099 /* Drop the ,<ADDR> part, if present */
1100 if ((ptr = strchr(name, ',')) != NULL)
1101 *ptr = '\0';
1103 ptr = name;
1104 guid_hi = s1394_stoi(ptr, 8, 16);
1105 guid_lo = s1394_stoi(ptr + 8, 8, 16);
1107 /* Search the HAL's node list for this GUID */
1108 for (i = 0; i < hal->number_of_nodes; i++) {
1109 if (CFGROM_VALID(&hal->topology_tree[i]) == B_TRUE) {
1110 ASSERT(hal->topology_tree[i].cfgrom != NULL);
1112 if ((hal->topology_tree[i].node_guid_hi == guid_hi) &&
1113 (hal->topology_tree[i].node_guid_lo == guid_lo)) {
1114 target->on_node = &hal->topology_tree[i];
1115 if ((t = hal->topology_tree[i].target_list) !=
1116 NULL) {
1117 ASSERT(t != target);
1118 while (t->target_sibling != NULL) {
1119 t = t->target_sibling;
1120 ASSERT(t != target);
1122 t->target_sibling = target;
1123 } else {
1124 hal->topology_tree[i].target_list =
1125 target;
1129 * update target_list in all targets on the
1130 * node
1132 t = hal->topology_tree[i].target_list;
1133 while (t != NULL) {
1134 t->target_list =
1135 hal->topology_tree[i].target_list;
1136 t = t->target_sibling;
1138 break;
1143 TNF_PROBE_0_DEBUG(s1394_add_target_to_node_exit,
1144 S1394_TNF_SL_HOTPLUG_STACK, "");
1148 * s1394_remove_target_from_node()
1149 * Removes target from the corresponding node's target_list.
1151 void
1152 s1394_remove_target_from_node(s1394_target_t *target)
1154 s1394_target_t *t, *t1;
1155 s1394_hal_t *hal;
1157 TNF_PROBE_0_DEBUG(s1394_remove_target_from_node_enter,
1158 S1394_TNF_SL_HOTPLUG_STACK, "");
1160 hal = target->on_hal;
1161 ASSERT(hal != NULL);
1163 /* Topology tree must be locked when it gets here! */
1164 ASSERT(MUTEX_HELD(&hal->topology_tree_mutex));
1166 /* target_list_rwlock should be held in write mode */
1167 ASSERT(rw_read_locked(&target->on_hal->target_list_rwlock) == 0);
1169 if (target->on_node == NULL) {
1170 TNF_PROBE_1_DEBUG(s1394_remove_target_from_node_NULL,
1171 S1394_TNF_SL_HOTPLUG_STACK, "",
1172 tnf_uint, target_state, target->target_state);
1175 t = target->target_list;
1176 t1 = NULL;
1177 while (t != NULL) {
1178 if (t == target) {
1179 if (t1 == NULL) {
1180 target->target_list = t->target_sibling;
1181 } else {
1182 t1->target_sibling = t->target_sibling;
1184 break;
1186 t1 = t;
1187 t = t->target_sibling;
1189 /* Update the target_list pointer in all the targets */
1190 if (target->on_node != NULL)
1191 target->on_node->target_list = target->target_list;
1193 t = t1 = target->target_list;
1194 while (t != NULL) {
1195 t->target_list = t1;
1196 t = t->target_sibling;
1199 target->on_node = NULL;
1200 target->target_sibling = NULL;
1202 TNF_PROBE_0_DEBUG(s1394_remove_target_from_node_exit,
1203 S1394_TNF_SL_HOTPLUG_STACK, "");