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
s1394_send_remove_event(s1394_hal_t * hal,dev_info_t * dip,t1394_localinfo_t * localinfo)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
s1394_send_insert_event(s1394_hal_t * hal,dev_info_t * dip,t1394_localinfo_t * localinfo)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 *
s1394_create_devinfo(s1394_hal_t * hal,s1394_node_t * node,uint32_t * unit_dir,int nunit)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, ®[2], ®[3], ®[4],
324 ®[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 *
s1394_devi_find(dev_info_t * pdip,char * name,char * caddr)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 **)®ptr,
380 (uint_t *)®len);
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
s1394_update_devinfo_tree(s1394_hal_t * hal,s1394_node_t * node)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
s1394_offline_node(s1394_hal_t * hal,s1394_node_t * node)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
s1394_process_topology_tree(s1394_hal_t * hal,int * wait_for_cbs,uint_t * wait_gen)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
s1394_process_old_tree(s1394_hal_t * hal)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
s1394_update_unit_dir_location(s1394_hal_t * hal,dev_info_t * tdip,uint_t offset)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
s1394_add_target_to_node(s1394_target_t * target)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
s1394_remove_target_from_node(s1394_target_t * target)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