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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * The tvhci driver can be used to exercise the mpxio framework together 31 * with tphci/tclient. 32 */ 33 34 #include <sys/conf.h> 35 #include <sys/file.h> 36 #include <sys/ddi.h> 37 #include <sys/sunddi.h> 38 #include <sys/scsi/scsi.h> 39 #include <sys/scsi/impl/scsi_reset_notify.h> 40 #include <sys/sunmdi.h> 41 #include <sys/mdi_impldefs.h> 42 #include <sys/disp.h> 43 44 /* cb_ops entry points */ 45 static int tvhci_open(dev_t *, int, int, cred_t *); 46 static int tvhci_close(dev_t, int, int, cred_t *); 47 static int tvhci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 48 static int tvhci_attach(dev_info_t *, ddi_attach_cmd_t); 49 static int tvhci_detach(dev_info_t *, ddi_detach_cmd_t); 50 static int tvhci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 51 52 /* bus_ops entry points */ 53 static int tvhci_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 54 void *); 55 static int tvhci_initchild(dev_info_t *, dev_info_t *); 56 static int tvhci_uninitchild(dev_info_t *, dev_info_t *); 57 static int tvhci_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *, 58 dev_info_t **); 59 static int tvhci_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t, 60 void *); 61 static int tvhci_intr_op(dev_info_t *dip, dev_info_t *rdip, 62 ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result); 63 64 /* vhci ops */ 65 static int tvhci_pi_init(dev_info_t *, mdi_pathinfo_t *, int); 66 static int tvhci_pi_uninit(dev_info_t *, mdi_pathinfo_t *, int); 67 static int tvhci_pi_state_change(dev_info_t *, mdi_pathinfo_t *, 68 mdi_pathinfo_state_t, uint32_t, int); 69 static int tvhci_failover(dev_info_t *, dev_info_t *, int); 70 71 static void *tvhci_state; 72 struct tvhci_state { 73 dev_info_t *dip; 74 }; 75 76 static mdi_vhci_ops_t tvhci_opinfo = { 77 MDI_VHCI_OPS_REV, 78 tvhci_pi_init, 79 tvhci_pi_uninit, 80 tvhci_pi_state_change, 81 tvhci_failover 82 }; 83 84 static struct cb_ops tvhci_cb_ops = { 85 tvhci_open, /* open */ 86 tvhci_close, /* close */ 87 nodev, /* strategy */ 88 nodev, /* print */ 89 nodev, /* dump */ 90 nodev, /* read */ 91 nodev, /* write */ 92 tvhci_ioctl, /* ioctl */ 93 nodev, /* devmap */ 94 nodev, /* mmap */ 95 nodev, /* segmap */ 96 nochpoll, /* chpoll */ 97 ddi_prop_op, /* cb_prop_op */ 98 0, /* streamtab */ 99 D_NEW | D_MP, /* cb_flag */ 100 CB_REV, /* rev */ 101 nodev, /* aread */ 102 nodev /* awrite */ 103 }; 104 105 static struct bus_ops tvhci_bus_ops = { 106 BUSO_REV, /* busops_rev */ 107 nullbusmap, /* bus_map */ 108 NULL, /* bus_get_intrspec */ 109 NULL, /* bus_add_interspec */ 110 NULL, /* bus_remove_interspec */ 111 i_ddi_map_fault, /* bus_map_fault */ 112 ddi_no_dma_map, /* bus_dma_map */ 113 ddi_no_dma_allochdl, /* bus_dma_allochdl */ 114 NULL, /* bus_dma_freehdl */ 115 NULL, /* bus_dma_bindhdl */ 116 NULL, /* bus_dma_unbindhdl */ 117 NULL, /* bus_dma_flush */ 118 NULL, /* bus_dma_win */ 119 NULL, /* bus_dma_ctl */ 120 tvhci_ctl, /* bus_ctl */ 121 ddi_bus_prop_op, /* bus_prop_op */ 122 NULL, /* bus_get_eventcookie */ 123 NULL, /* bus_add_eventcall */ 124 NULL, /* bus_remove_event */ 125 NULL, /* bus_post_event */ 126 NULL, /* bus_intr_ctl */ 127 tvhci_bus_config, /* bus_config */ 128 tvhci_bus_unconfig, /* bus_unconfig */ 129 NULL, /* bus_fm_init */ 130 NULL, /* bus_fm_fini */ 131 NULL, /* bus_fm_access_enter */ 132 NULL, /* bus_fm_access_exit */ 133 NULL, /* bus_power */ 134 tvhci_intr_op /* bus_intr_op */ 135 }; 136 137 static struct dev_ops tvhci_ops = { 138 DEVO_REV, 139 0, 140 tvhci_getinfo, 141 nulldev, /* identify */ 142 nulldev, /* probe */ 143 tvhci_attach, /* attach and detach are mandatory */ 144 tvhci_detach, 145 nodev, /* reset */ 146 &tvhci_cb_ops, /* cb_ops */ 147 &tvhci_bus_ops, /* bus_ops */ 148 NULL, /* power */ 149 }; 150 151 extern struct mod_ops mod_driverops; 152 153 static struct modldrv modldrv = { 154 &mod_driverops, 155 "test vhci driver %I%", 156 &tvhci_ops 157 }; 158 159 static struct modlinkage modlinkage = { 160 MODREV_1, 161 &modldrv, 162 NULL 163 }; 164 165 int 166 _init(void) 167 { 168 int rval; 169 170 if ((rval = ddi_soft_state_init(&tvhci_state, 171 sizeof (struct tvhci_state), 2)) != 0) { 172 return (rval); 173 } 174 175 if ((rval = mod_install(&modlinkage)) != 0) { 176 ddi_soft_state_fini(&tvhci_state); 177 } 178 return (rval); 179 } 180 181 182 int 183 _fini(void) 184 { 185 int rval; 186 187 /* 188 * don't start cleaning up until we know that the module remove 189 * has worked -- if this works, then we know that each instance 190 * has successfully been detached 191 */ 192 if ((rval = mod_remove(&modlinkage)) != 0) { 193 return (rval); 194 } 195 196 ddi_soft_state_fini(&tvhci_state); 197 198 return (rval); 199 } 200 201 int 202 _info(struct modinfo *modinfop) 203 { 204 return (mod_info(&modlinkage, modinfop)); 205 } 206 207 /* ARGSUSED */ 208 static int 209 tvhci_open(dev_t *devp, int flag, int otype, cred_t *credp) 210 { 211 struct tvhci_state *vhci; 212 213 if (otype != OTYP_CHR) { 214 return (EINVAL); 215 } 216 217 vhci = ddi_get_soft_state(tvhci_state, getminor(*devp)); 218 if (vhci == NULL) { 219 return (ENXIO); 220 } 221 222 return (0); 223 } 224 225 226 /* ARGSUSED */ 227 static int 228 tvhci_close(dev_t dev, int flag, int otype, cred_t *credp) 229 { 230 struct tvhci_state *vhci; 231 if (otype != OTYP_CHR) { 232 return (EINVAL); 233 } 234 235 vhci = ddi_get_soft_state(tvhci_state, getminor(dev)); 236 if (vhci == NULL) { 237 return (ENXIO); 238 } 239 240 return (0); 241 } 242 243 /* ARGSUSED */ 244 static int 245 tvhci_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 246 cred_t *credp, int *rval) 247 { 248 return (0); 249 } 250 251 /* 252 * attach the module 253 */ 254 static int 255 tvhci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 256 { 257 char *vclass; 258 int instance, vhci_regis = 0; 259 struct tvhci_state *vhci = NULL; 260 dev_info_t *pdip; 261 262 instance = ddi_get_instance(dip); 263 264 switch (cmd) { 265 case DDI_ATTACH: 266 break; 267 268 case DDI_RESUME: 269 case DDI_PM_RESUME: 270 return (0); /* nothing to do */ 271 272 default: 273 return (DDI_FAILURE); 274 } 275 276 /* 277 * Allocate vhci data structure. 278 */ 279 if (ddi_soft_state_zalloc(tvhci_state, instance) != DDI_SUCCESS) { 280 return (DDI_FAILURE); 281 } 282 283 vhci = ddi_get_soft_state(tvhci_state, instance); 284 ASSERT(vhci != NULL); 285 vhci->dip = dip; 286 287 /* parent must be /pshot */ 288 pdip = ddi_get_parent(dip); 289 if (strcmp(ddi_driver_name(pdip), "pshot") != 0 || 290 ddi_get_parent(pdip) != ddi_root_node()) { 291 cmn_err(CE_NOTE, "tvhci must be under /pshot/"); 292 goto attach_fail; 293 } 294 295 /* 296 * XXX add mpxio-disable property. need to remove the check 297 * from the framework 298 */ 299 (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, 300 "mpxio-disable", "no"); 301 302 /* bus_addr is the <vhci_class> */ 303 vclass = ddi_get_name_addr(dip); 304 if (vclass == NULL || vclass[1] == '\0') { 305 cmn_err(CE_NOTE, "tvhci invalid vhci class"); 306 goto attach_fail; 307 } 308 309 /* 310 * Attach this instance with the mpxio framework 311 */ 312 if (mdi_vhci_register(vclass, dip, &tvhci_opinfo, 0) != MDI_SUCCESS) { 313 cmn_err(CE_WARN, "%s mdi_vhci_register failed", 314 ddi_node_name(dip)); 315 goto attach_fail; 316 } 317 vhci_regis++; 318 319 if (ddi_create_minor_node(dip, "devctl", S_IFCHR, 320 instance, DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) { 321 cmn_err(CE_NOTE, "%s ddi_create_minor_node failed", 322 ddi_node_name(dip)); 323 goto attach_fail; 324 } 325 326 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1); 327 ddi_report_dev(dip); 328 return (DDI_SUCCESS); 329 330 attach_fail: 331 if (vhci_regis) 332 (void) mdi_vhci_unregister(dip, 0); 333 334 ddi_soft_state_free(tvhci_state, instance); 335 return (DDI_FAILURE); 336 } 337 338 339 /*ARGSUSED*/ 340 static int 341 tvhci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 342 { 343 int instance = ddi_get_instance(dip); 344 345 switch (cmd) { 346 case DDI_DETACH: 347 break; 348 349 case DDI_SUSPEND: 350 case DDI_PM_SUSPEND: 351 return (0); /* nothing to do */ 352 353 default: 354 return (DDI_FAILURE); 355 } 356 357 if (mdi_vhci_unregister(dip, 0) != MDI_SUCCESS) 358 return (DDI_FAILURE); 359 360 ddi_remove_minor_node(dip, NULL); 361 ddi_soft_state_free(tvhci_state, instance); 362 363 return (DDI_SUCCESS); 364 } 365 366 /* 367 * tvhci_getinfo() 368 * Given the device number, return the devinfo pointer or the 369 * instance number. 370 * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach. 371 */ 372 373 /*ARGSUSED*/ 374 static int 375 tvhci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 376 { 377 struct tvhci_state *vhci; 378 int instance = getminor((dev_t)arg); 379 380 switch (cmd) { 381 case DDI_INFO_DEVT2DEVINFO: 382 vhci = ddi_get_soft_state(tvhci_state, instance); 383 if (vhci != NULL) 384 *result = vhci->dip; 385 else { 386 *result = NULL; 387 return (DDI_FAILURE); 388 } 389 break; 390 391 case DDI_INFO_DEVT2INSTANCE: 392 *result = (void *)(uintptr_t)instance; 393 break; 394 395 default: 396 return (DDI_FAILURE); 397 } 398 399 return (DDI_SUCCESS); 400 } 401 402 /*ARGSUSED*/ 403 static int 404 tvhci_pi_init(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags) 405 { 406 return (MDI_SUCCESS); 407 } 408 409 /*ARGSUSED*/ 410 static int 411 tvhci_pi_uninit(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags) 412 { 413 return (MDI_SUCCESS); 414 } 415 416 /*ARGSUSED*/ 417 static int 418 tvhci_pi_state_change(dev_info_t *vdip, mdi_pathinfo_t *pip, 419 mdi_pathinfo_state_t state, uint32_t ext_state, int flags) 420 { 421 return (MDI_SUCCESS); 422 } 423 424 /*ARGSUSED*/ 425 static int 426 tvhci_failover(dev_info_t *vdip, dev_info_t *cdip, int flags) 427 { 428 return (MDI_SUCCESS); 429 } 430 431 /* 432 * Interrupt stuff. NO OP for pseudo drivers. 433 */ 434 /*ARGSUSED*/ 435 static int 436 tvhci_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op, 437 ddi_intr_handle_impl_t *hdlp, void *result) 438 { 439 return (DDI_FAILURE); 440 } 441 442 /*ARGSUSED*/ 443 static int 444 tvhci_ctl(dev_info_t *dip, dev_info_t *rdip, 445 ddi_ctl_enum_t ctlop, void *arg, void *result) 446 { 447 switch (ctlop) { 448 case DDI_CTLOPS_REPORTDEV: 449 if (rdip == (dev_info_t *)0) 450 return (DDI_FAILURE); 451 cmn_err(CE_CONT, "?tvhci-device: %s%d\n", 452 ddi_get_name(rdip), ddi_get_instance(rdip)); 453 return (DDI_SUCCESS); 454 455 case DDI_CTLOPS_INITCHILD: 456 { 457 dev_info_t *child = (dev_info_t *)arg; 458 return (tvhci_initchild(dip, child)); 459 } 460 461 case DDI_CTLOPS_UNINITCHILD: 462 { 463 dev_info_t *child = (dev_info_t *)arg; 464 return (tvhci_uninitchild(dip, child)); 465 } 466 467 case DDI_CTLOPS_DMAPMAPC: 468 case DDI_CTLOPS_REPORTINT: 469 case DDI_CTLOPS_REGSIZE: 470 case DDI_CTLOPS_NREGS: 471 case DDI_CTLOPS_NINTRS: 472 case DDI_CTLOPS_SIDDEV: 473 case DDI_CTLOPS_SLAVEONLY: 474 case DDI_CTLOPS_AFFINITY: 475 case DDI_CTLOPS_INTR_HILEVEL: 476 case DDI_CTLOPS_XLATE_INTRS: 477 case DDI_CTLOPS_POKE: 478 case DDI_CTLOPS_PEEK: 479 /* 480 * These ops correspond to functions that "shouldn't" be called 481 * by a pseudo driver. So we whine when we're called. 482 */ 483 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n", 484 ddi_get_name(dip), ddi_get_instance(dip), 485 ctlop, ddi_get_name(rdip), ddi_get_instance(rdip)); 486 return (DDI_FAILURE); 487 488 case DDI_CTLOPS_ATTACH: 489 case DDI_CTLOPS_BTOP: 490 case DDI_CTLOPS_BTOPR: 491 case DDI_CTLOPS_DETACH: 492 case DDI_CTLOPS_DVMAPAGESIZE: 493 case DDI_CTLOPS_IOMIN: 494 case DDI_CTLOPS_POWER: 495 case DDI_CTLOPS_PTOB: 496 default: 497 /* 498 * The ops that we pass up (default). We pass up memory 499 * allocation oriented ops that we receive - these may be 500 * associated with pseudo HBA drivers below us with target 501 * drivers below them that use ddi memory allocation 502 * interfaces like scsi_alloc_consistent_buf. 503 */ 504 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 505 } 506 } 507 508 /* set devi_addr to "g<guid>" */ 509 static int 510 tvhci_initchild(dev_info_t *dip, dev_info_t *child) 511 { 512 _NOTE(ARGUNUSED(dip)) 513 char *guid, *addr; 514 515 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 516 MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) { 517 cmn_err(CE_NOTE, "tvhci_initchild - no guid property"); 518 return (DDI_FAILURE); 519 } 520 521 addr = kmem_alloc(MAXNAMELEN, KM_SLEEP); 522 (void) snprintf(addr, MAXNAMELEN, "g%s", guid); 523 ddi_set_name_addr(child, addr); 524 525 kmem_free(addr, MAXNAMELEN); 526 ddi_prop_free(guid); 527 return (DDI_SUCCESS); 528 } 529 530 /*ARGSUSED*/ 531 static int 532 tvhci_uninitchild(dev_info_t *dip, dev_info_t *child) 533 { 534 ddi_set_name_addr(child, NULL); 535 return (DDI_SUCCESS); 536 } 537 538 /* form paddr by cname@<phci_inst>,<guid> */ 539 static char * 540 tvh_get_phci_devname(char *cname, char *guid, 541 dev_info_t *pdip, char *pname, int len) 542 { 543 (void) snprintf(pname, len, "%s@%d,%s", 544 cname, ddi_get_instance(pdip), guid); 545 return (pname); 546 } 547 548 static int 549 tvh_enum_by_phci(dev_info_t *vdip, char *devnm, int flags) 550 { 551 mdi_phci_t *ph; 552 mdi_vhci_t *vh = (mdi_vhci_t *)DEVI(vdip)->devi_mdi_xhci; 553 dev_info_t *pdip, *cdip; 554 char *cname, *caddr, *guid, *pname; 555 int rval = DDI_FAILURE; 556 557 (void) i_ddi_parse_name(devnm, &cname, &caddr, NULL); 558 if (cname == NULL || caddr == NULL || caddr[0] != 'g') 559 return (rval); 560 561 guid = caddr + 1; 562 pname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 563 564 /* mutex_enter(&mdi_mutex); XXX need lock access */ 565 ph = vh->vh_phci_head; 566 while (ph) { 567 pdip = ph->ph_dip; 568 /* mutex_exit(&mdi_mutex); */ 569 (void) tvh_get_phci_devname(cname, guid, pdip, pname, 570 MAXNAMELEN); 571 if (ndi_devi_config_one(pdip, pname, &cdip, flags) 572 == DDI_SUCCESS) { 573 ndi_rele_devi(cdip); 574 rval = DDI_SUCCESS; 575 } 576 /* mutex_enter(&mdi_mutex); */ 577 ph = ph->ph_next; 578 } 579 /* mutex_exit(&mdi_mutex); */ 580 581 *(caddr - 1) = '@'; /* undo damage from i_ddi_parse_name() */ 582 kmem_free(pname, MAXNAMELEN); 583 return (rval); 584 } 585 586 static int 587 tvh_remove_by_phci(dev_info_t *vdip, char *devnm, int flags) 588 { 589 int rval = DDI_SUCCESS; 590 mdi_phci_t *ph; 591 mdi_vhci_t *vh = (mdi_vhci_t *)DEVI(vdip)->devi_mdi_xhci; 592 dev_info_t *pdip; 593 char *cname, *caddr, *guid, *pname; 594 595 (void) i_ddi_parse_name(devnm, &cname, &caddr, NULL); 596 if (cname == NULL || caddr == NULL || caddr[0] != 'g') 597 return (rval); /* devnm can't exist */ 598 599 guid = caddr + 1; 600 pname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 601 602 /* mutex_enter(&mdi_mutex); XXX need lock access */ 603 ph = vh->vh_phci_head; 604 while (ph) { 605 pdip = ph->ph_dip; 606 /* mutex_exit(&mdi_mutex); */ 607 (void) tvh_get_phci_devname(cname, guid, pdip, pname, 608 MAXNAMELEN); 609 rval = ndi_devi_unconfig_one(pdip, pname, NULL, flags); 610 /* mutex_enter(&mdi_mutex); */ 611 if (rval != NDI_SUCCESS) 612 break; 613 ph = ph->ph_next; 614 } 615 /* mutex_exit(&mdi_mutex); */ 616 617 kmem_free(pname, MAXNAMELEN); 618 return (rval); 619 } 620 621 static int 622 tvhci_bus_config(dev_info_t *parent, uint_t flags, 623 ddi_bus_config_op_t op, void *arg, dev_info_t **childp) 624 { 625 char *devnm; 626 dev_info_t *cdip; 627 int circ; 628 629 switch (op) { 630 case BUS_CONFIG_ONE: 631 break; 632 case BUS_CONFIG_ALL: 633 /* XXX call into phci's here? */ 634 case BUS_CONFIG_DRIVER: 635 return (ndi_busop_bus_config(parent, flags, op, arg, childp, 636 0)); 637 default: 638 return (DDI_FAILURE); 639 } 640 641 devnm = (char *)arg; 642 ndi_devi_enter(parent, &circ); 643 cdip = ndi_devi_findchild(parent, devnm); 644 ndi_devi_exit(parent, circ); 645 if (cdip == NULL) { 646 /* call into registered phci's */ 647 (void) tvh_enum_by_phci(parent, devnm, flags); 648 } 649 650 return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0)); 651 } 652 653 static int 654 tvhci_bus_unconfig(dev_info_t *parent, uint_t flags, 655 ddi_bus_config_op_t op, void *arg) 656 { 657 char *devnm, *slashname; 658 int rval, circ; 659 dev_info_t *cdip, *ndip; 660 661 /* 662 * If we are not removing device nodes, pathinfo can be 663 * left as is. So no need to disturb the phci's. 664 */ 665 if ((flags & NDI_DEVI_REMOVE) == 0) { 666 return (ndi_busop_bus_unconfig(parent, flags, op, arg)); 667 } 668 669 switch (op) { 670 case BUS_UNCONFIG_ONE: 671 devnm = (char *)arg; 672 ndi_devi_enter(parent, &circ); 673 if (ndi_devi_findchild(parent, devnm) == NULL) { 674 ndi_devi_exit(parent, circ); 675 return (DDI_SUCCESS); 676 } 677 ndi_devi_exit(parent, circ); 678 return (tvh_remove_by_phci(parent, devnm, flags)); 679 680 case BUS_UNCONFIG_ALL: 681 /* this functionality is for developers only */ 682 rval = DDI_SUCCESS; 683 slashname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 684 devnm = slashname + 1; 685 ndi_devi_enter(parent, &circ); 686 cdip = ddi_get_child(parent); 687 while (cdip != NULL) { 688 ndip = ddi_get_next_sibling(cdip); 689 (void) ddi_deviname(cdip, slashname); 690 ndi_devi_exit(parent, circ); 691 rval = tvh_remove_by_phci(parent, devnm, flags); 692 if (rval != DDI_SUCCESS) { 693 break; 694 } 695 ndi_devi_enter(parent, &circ); 696 cdip = ndip; 697 } 698 ndi_devi_exit(parent, circ); 699 return (rval); 700 701 case BUS_UNCONFIG_DRIVER: 702 /* unconfig driver never comes with NDI_DEVI_REMOVE */ 703 default: 704 return (DDI_FAILURE); 705 } 706 /*NOTREACHED*/ 707 } 708