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 2004 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 /* 31 * generic character driver 32 */ 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/errno.h> 36 #include <sys/uio.h> 37 #include <sys/buf.h> 38 #include <sys/modctl.h> 39 #include <sys/open.h> 40 #include <sys/kmem.h> 41 #include <sys/conf.h> 42 #include <sys/cmn_err.h> 43 #include <sys/stat.h> 44 #include <sys/ddi.h> 45 #include <sys/sunddi.h> 46 #include <sys/sunndi.h> 47 48 49 #define NUMEVENTS 6 50 #define COMPONENTS 2 51 #define COMP_0_MAXPWR 3 52 #define COMP_1_MAXPWR 2 53 #define MINPWR 0 54 static int maxpwr[] = { COMP_0_MAXPWR, COMP_1_MAXPWR }; 55 56 /* 57 * The state for each generic device. 58 * NOTE: We save the node_type in the state structure. The node_type string 59 * (and not a copy) is stashed in a minor node by ddi_create_minor_node(), 60 * so ddi_remove_minor_node() must occur prior to state free. 61 */ 62 typedef struct dstate { 63 uint_t flag; 64 dev_info_t *dip; /* my devinfo handle */ 65 char *node_type; /* stable node_type copy */ 66 ddi_callback_id_t gen_cb_ids[NUMEVENTS]; 67 kmutex_t lock; 68 char *nodename; 69 int level[COMPONENTS]; /* pm level */ 70 int busy[COMPONENTS]; /* busy state */ 71 } dstate_t; 72 73 74 static void *dstates; 75 76 static int gen_debug = 0; 77 78 #ifdef DEBUG 79 #define gen_debug gen_debug_on 80 static int gen_debug_on = 0; 81 #define GEN_DEBUG(args) if (gen_debug) cmn_err args 82 #else 83 #define GEN_DEBUG(args) 84 #endif 85 86 extern void prom_printf(const char *fmt, ...); 87 88 static int gen_open(dev_t *devp, int flag, int otyp, cred_t *cred); 89 static int gen_close(dev_t devp, int flag, int otyp, cred_t *cred); 90 static int gen_read(dev_t dev, struct uio *uiop, cred_t *credp); 91 static int gen_write(dev_t dev, struct uio *uiop, cred_t *credp); 92 static int gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 93 cred_t *credp, int *rvalp); 94 static int gen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 95 static int gen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 96 static void gen_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie, 97 void *arg, void *impl_data); 98 99 static int gen_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 100 void **result); 101 static int gen_create_minor_nodes(dev_info_t *, struct dstate *); 102 static int gen_power(dev_info_t *, int, int); 103 104 static struct cb_ops gen_cb_ops = { 105 gen_open, /* open */ 106 gen_close, /* close */ 107 nodev, /* strategy */ 108 nodev, /* print */ 109 nodev, /* dump */ 110 gen_read, /* read */ 111 gen_write, /* write */ 112 gen_ioctl, /* ioctl */ 113 nodev, /* devmap */ 114 nodev, /* mmap */ 115 nodev, /* segmap */ 116 nochpoll, /* poll */ 117 ddi_prop_op, /* prop_op */ 118 NULL, /* streamtab */ 119 D_NEW | D_MP | D_HOTPLUG, /* flag */ 120 CB_REV, /* cb_rev */ 121 nodev, /* aread */ 122 nodev /* awrite */ 123 }; 124 125 126 static struct dev_ops gen_ops = { 127 DEVO_REV, /* devo_rev */ 128 0, /* refcnt */ 129 gen_info, /* getinfo */ 130 nulldev, /* identify */ 131 nulldev, /* probe */ 132 gen_attach, /* attach */ 133 gen_detach, /* detach */ 134 nodev, /* reset */ 135 &gen_cb_ops, /* driver ops */ 136 (struct bus_ops *)0, /* bus ops */ 137 gen_power /* power */ 138 }; 139 140 /* 141 * INST_TO_MINOR() gives the starting minor number for a given gen_drv driver 142 * instance. A shift left by 6 bits allows for each instance to have upto 143 * 64 (2^6) minor numbers. The maximum minor number allowed by the system 144 * is L_MAXMIN32 (0x3ffff). This effectively limits the gen_drv instance 145 * numbers from 0 to 0xfff for a total of 4096 instances. 146 */ 147 #define INST_TO_MINOR(i) (i << 6) 148 #define MINOR_TO_INST(mn) (mn >> 6) 149 150 static char *mnodetypes[] = { 151 "ddi_nt", 152 "ddi_nt:device_type", 153 "ddi_nt:device_class:bus_class", 154 "ddi_nt2", 155 "ddi_nt2:device_type", 156 "ddi_nt2:device_type:bus_class", 157 }; 158 #define N_NTYPES (sizeof (mnodetypes) / sizeof (char *)) 159 160 static struct modldrv modldrv = { 161 &mod_driverops, 162 "generic test driver %I%", 163 &gen_ops 164 }; 165 166 static struct modlinkage modlinkage = { 167 MODREV_1, &modldrv, NULL 168 }; 169 170 171 /* 172 * flags 173 */ 174 #define OPEN_FLAG 0x001 175 #define PWR_HAS_CHANGED_ON_RESUME_FLAG 0x002 176 #define FAIL_SUSPEND_FLAG 0x004 177 #define PUP_WITH_PWR_HAS_CHANGED_FLAG 0x008 178 #define POWER_FLAG 0x010 179 #define LOWER_POWER_FLAG 0x020 180 #define NO_INVOL_FLAG 0x040 181 #define PM_SUPPORTED_FLAG 0x080 182 183 /* 184 * ioctl commands (non-devctl ioctl commands) 185 */ 186 #define GENDRV_IOCTL ('P' << 8) 187 #define GENDRV_IOFAULT_SIMULATE (GENDRV_IOCTL | 0) 188 #define GENDRV_NDI_EVENT_TEST (GENDRV_IOCTL | 1) 189 190 int 191 _init(void) 192 { 193 int e; 194 195 if ((e = ddi_soft_state_init(&dstates, 196 sizeof (struct dstate), 0)) != 0) { 197 return (e); 198 } 199 200 if ((e = mod_install(&modlinkage)) != 0) { 201 ddi_soft_state_fini(&dstates); 202 } 203 204 return (e); 205 } 206 207 int 208 _fini(void) 209 { 210 int e; 211 212 if ((e = mod_remove(&modlinkage)) != 0) { 213 return (e); 214 } 215 ddi_soft_state_fini(&dstates); 216 return (e); 217 } 218 219 int 220 _info(struct modinfo *modinfop) 221 { 222 return (mod_info(&modlinkage, modinfop)); 223 } 224 225 static int 226 gen_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 227 { 228 int instance = ddi_get_instance(devi); 229 struct dstate *dstatep; 230 int rval; 231 int n_devs; 232 int n_minorcomps; 233 int isclone; 234 ddi_eventcookie_t dev_offline_cookie, dev_reset_cookie; 235 ddi_eventcookie_t bus_reset_cookie, bus_quiesce_cookie; 236 ddi_eventcookie_t bus_unquiesce_cookie, bus_test_post_cookie; 237 int i_init = 0; 238 int level_tmp; 239 240 int i; 241 char *pm_comp[] = { 242 "NAME=leaf0", 243 "0=D0", 244 "1=D1", 245 "2=D2", 246 "3=D3", 247 "NAME=leaf1", 248 "0=off", 249 "1=blank", 250 "2=on"}; 251 char *pm_hw_state = {"needs-suspend-resume"}; 252 253 254 switch (cmd) { 255 case DDI_ATTACH: 256 257 if (ddi_soft_state_zalloc(dstates, instance) != 258 DDI_SUCCESS) { 259 cmn_err(CE_CONT, "%s%d: can't allocate state\n", 260 ddi_get_name(devi), instance); 261 262 return (DDI_FAILURE); 263 } 264 265 dstatep = ddi_get_soft_state(dstates, instance); 266 dstatep->dip = devi; 267 mutex_init(&dstatep->lock, NULL, MUTEX_DRIVER, NULL); 268 269 n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0, 270 "ndevs", 1); 271 272 isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0, 273 "isclone", 0); 274 275 n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0, 276 "ncomps", 1); 277 278 GEN_DEBUG((CE_CONT, 279 "%s%d attaching: n_devs=%d n_minorcomps=%d isclone=%d", 280 ddi_get_name(devi), ddi_get_instance(devi), 281 n_devs, n_minorcomps, isclone)); 282 283 if (isclone) { 284 if (ddi_create_minor_node(devi, "gen", S_IFCHR, 285 INST_TO_MINOR(instance), mnodetypes[0], 286 isclone) != DDI_SUCCESS) { 287 ddi_remove_minor_node(devi, NULL); 288 ddi_soft_state_free(dstates, instance); 289 cmn_err(CE_WARN, "%s%d: can't create minor " 290 "node", ddi_get_name(devi), instance); 291 292 return (DDI_FAILURE); 293 } 294 rval = DDI_SUCCESS; 295 } else { 296 rval = gen_create_minor_nodes(devi, dstatep); 297 if (rval != DDI_SUCCESS) { 298 ddi_prop_remove_all(devi); 299 ddi_remove_minor_node(devi, NULL); 300 ddi_soft_state_free(dstates, instance); 301 cmn_err(CE_WARN, "%s%d: can't create minor " 302 "nodes", ddi_get_name(devi), instance); 303 304 return (DDI_FAILURE); 305 } 306 } 307 308 if (ddi_get_eventcookie(devi, "pshot_dev_offline", 309 &dev_offline_cookie) == DDI_SUCCESS) { 310 (void) ddi_add_event_handler(devi, dev_offline_cookie, 311 gen_event_cb, NULL, &(dstatep->gen_cb_ids[0])); 312 } 313 314 if (ddi_get_eventcookie(devi, "pshot_dev_reset", 315 &dev_reset_cookie) == DDI_SUCCESS) { 316 (void) ddi_add_event_handler(devi, dev_reset_cookie, 317 gen_event_cb, NULL, &(dstatep->gen_cb_ids[1])); 318 } 319 320 if (ddi_get_eventcookie(devi, "pshot_bus_reset", 321 &bus_reset_cookie) == DDI_SUCCESS) { 322 (void) ddi_add_event_handler(devi, bus_reset_cookie, 323 gen_event_cb, NULL, &(dstatep->gen_cb_ids[2])); 324 } 325 326 if (ddi_get_eventcookie(devi, "pshot_bus_quiesce", 327 &bus_quiesce_cookie) == DDI_SUCCESS) { 328 (void) ddi_add_event_handler(devi, bus_quiesce_cookie, 329 gen_event_cb, NULL, &(dstatep->gen_cb_ids[3])); 330 } 331 332 if (ddi_get_eventcookie(devi, "pshot_bus_unquiesce", 333 &bus_unquiesce_cookie) == DDI_SUCCESS) { 334 (void) ddi_add_event_handler(devi, 335 bus_unquiesce_cookie, gen_event_cb, 336 NULL, &(dstatep->gen_cb_ids[4])); 337 } 338 339 if (ddi_get_eventcookie(devi, "pshot_bus_test_post", 340 &bus_test_post_cookie) == DDI_SUCCESS) { 341 (void) ddi_add_event_handler(devi, 342 bus_test_post_cookie, gen_event_cb, 343 NULL, &(dstatep->gen_cb_ids[5])); 344 } 345 346 /* 347 * initialize the devices' pm state 348 */ 349 mutex_enter(&dstatep->lock); 350 dstatep->flag &= ~OPEN_FLAG; 351 dstatep->flag &= ~PWR_HAS_CHANGED_ON_RESUME_FLAG; 352 dstatep->flag &= ~FAIL_SUSPEND_FLAG; 353 dstatep->flag &= ~PUP_WITH_PWR_HAS_CHANGED_FLAG; 354 dstatep->flag |= LOWER_POWER_FLAG; 355 dstatep->flag &= ~NO_INVOL_FLAG; 356 dstatep->flag |= PM_SUPPORTED_FLAG; 357 dstatep->busy[0] = 0; 358 dstatep->busy[1] = 0; 359 dstatep->level[0] = -1; 360 dstatep->level[1] = -1; 361 mutex_exit(&dstatep->lock); 362 363 /* 364 * stash the nodename 365 */ 366 dstatep->nodename = ddi_node_name(devi); 367 368 /* 369 * Check if the no-involuntary-power-cycles property 370 * was created. Set NO_INVOL_FLAG if so. 371 */ 372 if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip, 373 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 374 "no-involuntary-power-cycles") == 1) { 375 GEN_DEBUG((CE_CONT, 376 "%s%d: DDI_ATTACH:\n\tno-involuntary-power-cycles" 377 " property was created", 378 ddi_node_name(devi), ddi_get_instance(devi))); 379 mutex_enter(&dstatep->lock); 380 dstatep->flag |= NO_INVOL_FLAG; 381 mutex_exit(&dstatep->lock); 382 } 383 384 /* 385 * Check if the dependency-property property 386 * was created. 387 */ 388 if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip, 389 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 390 "dependency-property") == 1) { 391 GEN_DEBUG((CE_CONT, 392 "%s%d: DDI_ATTACH:\n\tdependency-property" 393 " property was created", 394 ddi_node_name(devi), ddi_get_instance(devi))); 395 } 396 397 /* 398 * create the pm-components property. two comps: 399 * 4 levels on comp0, 3 on comp 1. 400 * - skip for a "tape" device, clear PM_SUPPORTED_FLAG 401 */ 402 if (strcmp(ddi_node_name(devi), "tape") != 0) { 403 if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, 404 "pm-components", pm_comp, 9) != DDI_PROP_SUCCESS) { 405 cmn_err(CE_WARN, "%s%d: %s\n", 406 ddi_node_name(devi), 407 ddi_get_instance(devi), 408 "unable to create \"pm-components\" " 409 " property."); 410 411 return (DDI_FAILURE); 412 } 413 } else { 414 mutex_enter(&dstatep->lock); 415 dstatep->flag &= ~PM_SUPPORTED_FLAG; 416 mutex_exit(&dstatep->lock); 417 } 418 419 /* 420 * Check if the pm-components property was created 421 */ 422 if (dstatep->flag & PM_SUPPORTED_FLAG) { 423 if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip, 424 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), 425 "pm-components") != 1) { 426 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s", 427 ddi_node_name(devi), 428 ddi_get_instance(devi), 429 "\"pm-components\" property does" 430 " not exist"); 431 432 return (DDI_FAILURE); 433 434 } else { 435 GEN_DEBUG((CE_CONT, "%s%d: DDI_ATTACH:" 436 " created pm-components property", 437 ddi_node_name(devi), 438 ddi_get_instance(devi))); 439 } 440 } 441 442 /* 443 * create the pm-hardware-state property. 444 * needed to get DDI_SUSPEND and DDI_RESUME calls 445 */ 446 if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, 447 "pm-hardware-state", pm_hw_state) != DDI_PROP_SUCCESS) { 448 cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s\n", 449 ddi_node_name(devi), ddi_get_instance(devi), 450 "unable to create \"pm-hardware-state\" " 451 " property."); 452 453 return (DDI_FAILURE); 454 } 455 456 /* 457 * set power levels to max via pm_raise_power(), 458 */ 459 mutex_enter(&dstatep->lock); 460 i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS; 461 mutex_exit(&dstatep->lock); 462 for (i = i_init; i < COMPONENTS; i++) { 463 GEN_DEBUG((CE_CONT, 464 "%s%d: DDI_ATTACH: pm_raise_power comp %d " 465 "to level %d", ddi_node_name(devi), 466 ddi_get_instance(devi), i, maxpwr[i])); 467 if (pm_raise_power(dstatep->dip, i, maxpwr[i]) != 468 DDI_SUCCESS) { 469 cmn_err(CE_WARN, 470 "%s%d: DDI_ATTACH: pm_raise_power failed\n", 471 ddi_node_name(devi), 472 ddi_get_instance(devi)); 473 dstatep->level[i] = -1; 474 475 return (DDI_FAILURE); 476 } 477 } 478 479 if (rval == DDI_SUCCESS) { 480 ddi_report_dev(devi); 481 } 482 return (rval); 483 484 485 case DDI_RESUME: 486 GEN_DEBUG((CE_CONT, "%s%d: DDI_RESUME", ddi_node_name(devi), 487 ddi_get_instance(devi))); 488 489 dstatep = ddi_get_soft_state(dstates, ddi_get_instance(devi)); 490 if (dstatep == NULL) { 491 492 return (DDI_FAILURE); 493 } 494 495 /* 496 * Call pm_power_has_changed() if flag 497 * PWR_HAS_CHANGED_ON_RESUME_FLAG is set, 498 * then clear the flag 499 */ 500 mutex_enter(&dstatep->lock); 501 i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS; 502 mutex_exit(&dstatep->lock); 503 if (dstatep->flag & PWR_HAS_CHANGED_ON_RESUME_FLAG) { 504 for (i = i_init; i < COMPONENTS; i++) { 505 GEN_DEBUG((CE_CONT, 506 "%s%d: DDI_RESUME: pm_power_has_changed " 507 "comp %d to level %d", ddi_node_name(devi), 508 ddi_get_instance(devi), i, maxpwr[i])); 509 mutex_enter(&dstatep->lock); 510 level_tmp = dstatep->level[i]; 511 dstatep->level[i] = maxpwr[i]; 512 if (pm_power_has_changed(dstatep->dip, i, 513 maxpwr[i]) != DDI_SUCCESS) { 514 cmn_err(CE_WARN, 515 "%s%d: DDI_RESUME:\n\t" 516 " pm_power_has_changed" 517 " failed: comp %d to level %d\n", 518 ddi_node_name(devi), 519 ddi_get_instance(devi), 520 i, maxpwr[i]); 521 dstatep->level[i] = level_tmp; 522 } 523 mutex_exit(&dstatep->lock); 524 } 525 } else { 526 /* 527 * Call pm_raise_power() instead 528 */ 529 for (i = i_init; i < COMPONENTS; i++) { 530 GEN_DEBUG((CE_CONT, 531 "%s%d: DDI_RESUME: pm_raise_power" 532 " comp %d to level %d", 533 ddi_node_name(devi), ddi_get_instance(devi), 534 i, maxpwr[i])); 535 if (pm_raise_power(dstatep->dip, i, maxpwr[i]) 536 != DDI_SUCCESS) { 537 cmn_err(CE_WARN, 538 "%s%d: DDI_RESUME:" 539 "\n\tpm_raise_power" 540 "failed: comp %d to level %d\n", 541 ddi_node_name(devi), 542 ddi_get_instance(devi), 543 i, maxpwr[i]); 544 } 545 } 546 } 547 548 return (DDI_SUCCESS); 549 550 default: 551 GEN_DEBUG((CE_WARN, "attach: default")); 552 return (DDI_FAILURE); 553 } 554 } 555 556 static int 557 gen_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 558 { 559 struct dstate *dstatep; 560 int instance; 561 int i; 562 int rv; 563 int rm_power; 564 int level_tmp; 565 566 #ifdef DEBUG 567 int n_devs; 568 int n_minorcomps; 569 int isclone; 570 #endif 571 572 switch (cmd) { 573 case DDI_DETACH: 574 GEN_DEBUG((CE_CONT, "%s%d: DDI_DETACH", ddi_node_name(devi), 575 ddi_get_instance(devi))); 576 577 instance = ddi_get_instance(devi); 578 dstatep = ddi_get_soft_state(dstates, instance); 579 if (dstatep == NULL) { 580 581 return (DDI_FAILURE); 582 } 583 584 #ifdef DEBUG 585 n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0, 586 "ndevs", 1); 587 588 isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0, 589 "isclone", 0); 590 591 n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0, 592 "ncomps", 1); 593 #endif /* DEBUG */ 594 595 /* 596 * power off component 1. 597 */ 598 if (dstatep->flag & PM_SUPPORTED_FLAG) { 599 GEN_DEBUG((CE_CONT, 600 "%s%d: DDI_DETACH: pm_lower_power comp 1 level %d", 601 ddi_node_name(devi), ddi_get_instance(devi), 602 MINPWR)); 603 if (pm_lower_power(dstatep->dip, 1, MINPWR) 604 != DDI_SUCCESS) { 605 cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t" 606 "pm_lower_power failed for comp 1 to" 607 " level %d\n", ddi_node_name(devi), 608 ddi_get_instance(devi), MINPWR); 609 610 return (DDI_FAILURE); 611 } 612 613 /* 614 * check power level. Issue pm_power_has_changed 615 * if not at MINPWR. 616 */ 617 mutex_enter(&dstatep->lock); 618 level_tmp = dstatep->level[1]; 619 dstatep->level[1] = MINPWR; 620 if (dstatep->level[1] != MINPWR) { 621 GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:" 622 " power off via pm_power_has_changed" 623 " instead", ddi_node_name(devi), 624 ddi_get_instance(devi))); 625 if (pm_power_has_changed(dstatep->dip, 626 1, MINPWR) != DDI_SUCCESS) { 627 GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:" 628 " pm_power_has_changed failed for" 629 " comp 1 to level %d", 630 ddi_node_name(devi), 631 ddi_get_instance(devi), 632 MINPWR)); 633 dstatep->level[1] = level_tmp; 634 mutex_exit(&dstatep->lock); 635 636 return (DDI_FAILURE); 637 } 638 } 639 mutex_exit(&dstatep->lock); 640 } 641 642 /* 643 * If the LOWER_POWER_FLAG flag is not set, 644 * don't call pm_lowr_power() for comp 0. 645 * This should be used only for the XXXXX@XX,no_invol 646 * devices that export the 647 * no-involuntary-power-cycles property 648 */ 649 if (!(dstatep->flag & LOWER_POWER_FLAG) && 650 dstatep->flag & PM_SUPPORTED_FLAG) { 651 cmn_err(CE_NOTE, "%s%d: DDI_DETACH:\n\t" 652 " NOT CALLING PM_LOWER_POWER():" 653 " LOWER_POWER_FLAG NOT SET\n", 654 ddi_node_name(devi), ddi_get_instance(devi)); 655 } else if (dstatep->flag & PM_SUPPORTED_FLAG) { 656 GEN_DEBUG((CE_CONT, 657 "%s%d: DDI_DETACH: pm_lower_power comp 0 level %d", 658 ddi_node_name(devi), ddi_get_instance(devi), 659 MINPWR)); 660 if (pm_lower_power(dstatep->dip, 0, MINPWR) 661 != DDI_SUCCESS) { 662 cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t" 663 "pm_lower_power failed for comp 0 to" 664 " level %d\n", ddi_node_name(devi), 665 ddi_get_instance(devi), MINPWR); 666 667 return (DDI_FAILURE); 668 } 669 670 /* 671 * check power level. Issue pm_power_has_changed 672 * if not at MINPWR. 673 */ 674 mutex_enter(&dstatep->lock); 675 level_tmp = dstatep->level[0]; 676 dstatep->level[0] = MINPWR; 677 if (dstatep->level[0] != MINPWR) { 678 GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:" 679 " power off via pm_power_has_changed" 680 " instead", ddi_node_name(devi), 681 ddi_get_instance(devi))); 682 if (pm_power_has_changed(dstatep->dip, 683 0, MINPWR) != DDI_SUCCESS) { 684 GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:" 685 " pm_power_has_changed failed for" 686 " comp 0 to level %d", 687 ddi_node_name(devi), 688 ddi_get_instance(devi), 689 MINPWR)); 690 dstatep->level[0] = level_tmp; 691 mutex_exit(&dstatep->lock); 692 693 return (DDI_FAILURE); 694 } 695 } 696 mutex_exit(&dstatep->lock); 697 } 698 699 GEN_DEBUG((CE_CONT, 700 "%s%d detaching: n_devs=%d n_minorcomps=%d isclone=%d", 701 ddi_node_name(devi), ddi_get_instance(devi), 702 n_devs, n_minorcomps, isclone)); 703 704 for (i = 0; i < NUMEVENTS; i++) { 705 if (dstatep->gen_cb_ids[i]) { 706 (void) ddi_remove_event_handler(dstatep->gen_cb_ids[i]); 707 dstatep->gen_cb_ids[i] = NULL; 708 } 709 } 710 711 ddi_prop_remove_all(devi); 712 ddi_remove_minor_node(devi, NULL); 713 if (dstatep->node_type) 714 kmem_free(dstatep->node_type, 715 strlen(dstatep->node_type) + 1); 716 ddi_soft_state_free(dstates, instance); 717 return (DDI_SUCCESS); 718 719 case DDI_SUSPEND: 720 GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND", 721 ddi_node_name(devi), ddi_get_instance(devi))); 722 723 instance = ddi_get_instance(devi); 724 dstatep = ddi_get_soft_state(dstates, instance); 725 if (dstatep == NULL) { 726 727 return (DDI_FAILURE); 728 } 729 730 /* 731 * fail the suspend if FAIL_SUSPEND_FLAG is set. 732 * clear the FAIL_SUSPEND_FLAG flag 733 */ 734 mutex_enter(&dstatep->lock); 735 if (dstatep->flag & FAIL_SUSPEND_FLAG) { 736 GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:" 737 " FAIL_SUSPEND_FLAG is set," 738 " fail suspend", 739 ddi_node_name(devi), ddi_get_instance(devi))); 740 dstatep->flag &= ~FAIL_SUSPEND_FLAG; 741 rv = DDI_FAILURE; 742 } else { 743 rv = DDI_SUCCESS; 744 } 745 mutex_exit(&dstatep->lock); 746 747 /* 748 * Issue ddi_removing_power() to determine if the suspend 749 * was initiated by either CPR or DR. If CPR, the system 750 * will be powered OFF; if this driver has set the 751 * NO_INVOL_FLAG, then refuse to suspend. If DR, power 752 * will not be removed, thus allow the suspend. 753 */ 754 if (dstatep->flag & NO_INVOL_FLAG && 755 dstatep->flag & PM_SUPPORTED_FLAG) { 756 GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:" 757 " check via ddi_removing_power()", 758 ddi_node_name(devi), ddi_get_instance(devi))); 759 760 rm_power = ddi_removing_power(dstatep->dip); 761 762 if (rm_power < 0) { 763 cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:" 764 " ddi_removing_power() failed\n", 765 ddi_node_name(devi), 766 ddi_get_instance(devi)); 767 } else if (rm_power == 1) { 768 /* 769 * CPR: power will be removed 770 */ 771 GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t" 772 " CPR: POWER WILL BE REMOVED, THEREFORE" 773 " REFUSE TO SUSPEND", ddi_node_name(devi), 774 ddi_get_instance(devi))); 775 rv = DDI_FAILURE; 776 } else if (rm_power == 0) { 777 /* 778 * DR: power will not be removed 779 */ 780 GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t" 781 " DR: POWER WILL NOT BE REMOVED, THEREFORE" 782 " ALLOW THE SUSPEND", ddi_node_name(devi), 783 ddi_get_instance(devi))); 784 rv = DDI_SUCCESS; 785 } 786 } 787 788 /* 789 * power OFF via pm_power_has_changed() 790 */ 791 mutex_enter(&dstatep->lock); 792 if (dstatep->flag & PM_SUPPORTED_FLAG && 793 !(dstatep->flag & NO_INVOL_FLAG)) { 794 level_tmp = dstatep->level[0]; 795 dstatep->level[0] = MINPWR; 796 GEN_DEBUG((CE_CONT, 797 "%s%d: DDI_SUSPEND: pm_power_has_changed comp 0" 798 " level %d", ddi_node_name(devi), 799 ddi_get_instance(devi), MINPWR)); 800 if (pm_power_has_changed(dstatep->dip, 0, MINPWR) 801 != DDI_SUCCESS) { 802 cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:\n\t" 803 "pm_power_has_changed failed for comp 0 to" 804 " level %d\n", ddi_node_name(devi), 805 ddi_get_instance(devi), MINPWR); 806 dstatep->level[0] = level_tmp; 807 mutex_exit(&dstatep->lock); 808 809 return (DDI_FAILURE); 810 } 811 } 812 mutex_exit(&dstatep->lock); 813 814 return (rv); 815 816 default: 817 818 return (DDI_FAILURE); 819 } 820 } 821 822 /* ARGSUSED */ 823 static int 824 gen_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 825 { 826 dev_t dev; 827 int instance; 828 829 if (infocmd != DDI_INFO_DEVT2INSTANCE) 830 return (DDI_FAILURE); 831 832 dev = (dev_t)arg; 833 instance = MINOR_TO_INST(getminor(dev)); 834 *result = (void *)(uintptr_t)instance; 835 return (DDI_SUCCESS); 836 } 837 838 839 /*ARGSUSED*/ 840 static int 841 gen_open(dev_t *devp, int flag, int otyp, cred_t *cred) 842 { 843 minor_t minor; 844 struct dstate *dstatep; 845 846 if (otyp != OTYP_BLK && otyp != OTYP_CHR) 847 return (EINVAL); 848 849 minor = getminor(*devp); 850 if ((dstatep = ddi_get_soft_state(dstates, 851 MINOR_TO_INST(minor))) == NULL) 852 return (ENXIO); 853 854 mutex_enter(&dstatep->lock); 855 dstatep->flag |= OPEN_FLAG; 856 mutex_exit(&dstatep->lock); 857 858 GEN_DEBUG((CE_CONT, 859 "%s%d open", 860 dstatep->nodename, MINOR_TO_INST(minor))); 861 862 return (0); 863 } 864 865 /*ARGSUSED*/ 866 static int 867 gen_close(dev_t dev, int flag, int otyp, cred_t *cred) 868 { 869 struct dstate *dstatep; 870 minor_t minor = getminor(dev); 871 872 if (otyp != OTYP_BLK && otyp != OTYP_CHR) 873 return (EINVAL); 874 875 dstatep = ddi_get_soft_state(dstates, MINOR_TO_INST(minor)); 876 877 if (dstatep == NULL) 878 return (ENXIO); 879 880 mutex_enter(&dstatep->lock); 881 dstatep->flag &= ~OPEN_FLAG; 882 mutex_exit(&dstatep->lock); 883 884 GEN_DEBUG((CE_CONT, 885 "%s%d close", 886 dstatep->nodename, MINOR_TO_INST(minor))); 887 888 return (0); 889 } 890 891 /*ARGSUSED*/ 892 static int 893 gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 894 { 895 struct dstate *dstatep; 896 ddi_eventcookie_t cookie; 897 int instance; 898 int rval = 0; 899 char *nodename; 900 int i; 901 struct devctl_iocdata *dcp; 902 uint_t state; 903 int ret; 904 int level_tmp; 905 906 instance = MINOR_TO_INST(getminor(dev)); 907 dstatep = ddi_get_soft_state(dstates, instance); 908 nodename = dstatep->nodename; 909 910 if (dstatep == NULL) 911 return (ENXIO); 912 913 /* 914 * read devctl ioctl data 915 */ 916 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 917 return (EFAULT); 918 919 switch (cmd) { 920 case GENDRV_IOFAULT_SIMULATE: 921 if (ddi_get_eventcookie(dstatep->dip, DDI_DEVI_FAULT_EVENT, 922 &(cookie)) != NDI_SUCCESS) 923 return (DDI_FAILURE); 924 925 return (ndi_post_event(dstatep->dip, dstatep->dip, cookie, 926 NULL)); 927 928 case GENDRV_NDI_EVENT_TEST: 929 if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_offline", 930 &cookie) == NDI_SUCCESS) { 931 (void) ndi_post_event(dstatep->dip, dstatep->dip, 932 cookie, NULL); 933 } 934 935 if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_reset", 936 &cookie) == NDI_SUCCESS) { 937 (void) ndi_post_event(dstatep->dip, dstatep->dip, 938 cookie, NULL); 939 } 940 941 if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_reset", 942 &cookie) == NDI_SUCCESS) { 943 (void) ndi_post_event(dstatep->dip, dstatep->dip, 944 cookie, NULL); 945 } 946 947 if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_quiesce", 948 &cookie) == NDI_SUCCESS) { 949 (void) ndi_post_event(dstatep->dip, dstatep->dip, 950 cookie, NULL); 951 } 952 953 if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_unquiesce", 954 &cookie) == NDI_SUCCESS) { 955 (void) ndi_post_event(dstatep->dip, dstatep->dip, 956 cookie, NULL); 957 } 958 959 if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_test_post", 960 &cookie) == NDI_SUCCESS) { 961 (void) ndi_post_event(dstatep->dip, dstatep->dip, 962 cookie, NULL); 963 } 964 965 break; 966 967 case DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME: 968 /* 969 * Issue pm_power_has_changed() call on DDI_RESUME 970 */ 971 mutex_enter(&dstatep->lock); 972 dstatep->flag |= PWR_HAS_CHANGED_ON_RESUME_FLAG; 973 mutex_exit(&dstatep->lock); 974 GEN_DEBUG((CE_CONT, "%s%d:" 975 " DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME", nodename, 976 instance)); 977 978 break; 979 980 case DEVCTL_PM_FAIL_SUSPEND: 981 /* 982 * Fail the suspend attempt in DDI_SUSPEND 983 */ 984 mutex_enter(&dstatep->lock); 985 dstatep->flag |= FAIL_SUSPEND_FLAG; 986 mutex_exit(&dstatep->lock); 987 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_FAIL_SUSPEND", 988 nodename, instance)); 989 990 break; 991 992 case DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED: 993 /* 994 * Use pm_power_has_changed() to power up comp 0 when 995 * enforcing the comp 0 vs comp-not 0 dependency: 996 * Power up comp 0 first, if request for comp-not-0 997 * comes in. 998 * Else, default to pm_raise_power(). 999 */ 1000 mutex_enter(&dstatep->lock); 1001 dstatep->flag |= PUP_WITH_PWR_HAS_CHANGED_FLAG; 1002 mutex_exit(&dstatep->lock); 1003 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED", 1004 nodename, instance)); 1005 1006 break; 1007 1008 case DEVCTL_PM_BUSY_COMP: 1009 /* 1010 * mark component 0 busy via a pm_busy_component() call. 1011 * update the busy[] array. 1012 */ 1013 mutex_enter(&dstatep->lock); 1014 ++dstatep->busy[0]; 1015 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP: comp 0:" 1016 " busy=%d", nodename, instance, dstatep->busy[0])); 1017 mutex_exit(&dstatep->lock); 1018 ret = pm_busy_component(dstatep->dip, 0); 1019 ASSERT(ret == DDI_SUCCESS); 1020 1021 break; 1022 1023 case DEVCTL_PM_BUSY_COMP_TEST: 1024 /* 1025 * test busy state on component 0 1026 */ 1027 mutex_enter(&dstatep->lock); 1028 state = dstatep->busy[0]; 1029 if (copyout(&state, dcp->cpyout_buf, 1030 sizeof (uint_t)) != 0) { 1031 cmn_err(CE_WARN, "%s%d:" 1032 " DEVCTL_PM_BUSY_COMP_TEST: copyout failed\n", 1033 nodename, instance); 1034 rval = EINVAL; 1035 } 1036 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP_TEST:" 1037 " comp 0 busy %d", 1038 nodename, instance, state)); 1039 mutex_exit(&dstatep->lock); 1040 1041 break; 1042 1043 case DEVCTL_PM_IDLE_COMP: 1044 /* 1045 * mark component 0 idle via a pm_idle_component() call. 1046 * NOP if dstatep->busy[0] == 0. 1047 */ 1048 mutex_enter(&dstatep->lock); 1049 if (dstatep->busy[0] > 0) { 1050 --dstatep->busy[0]; 1051 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_IDLE_COMP:" 1052 " comp 0: busy=%d", nodename, instance, 1053 dstatep->busy[0])); 1054 mutex_exit(&dstatep->lock); 1055 ret = pm_idle_component(dstatep->dip, 0); 1056 ASSERT(ret == DDI_SUCCESS); 1057 } else { 1058 mutex_exit(&dstatep->lock); 1059 } 1060 1061 break; 1062 1063 case DEVCTL_PM_PROM_PRINTF: 1064 (void) prom_printf("%s%d: PROM_PRINTF FROM GEN_DRV\n", 1065 nodename, instance); 1066 1067 break; 1068 1069 case DEVCTL_PM_RAISE_PWR: 1070 /* 1071 * power up both components to MAXPWR via 1072 * pm_raise_power() calls. this ioctl() cmd 1073 * assumes that the current level is 0 1074 */ 1075 for (i = 0; i < COMPONENTS; i++) { 1076 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_RAISE_PWR:" 1077 " comp %d old 0 new %d", 1078 nodename, instance, i, maxpwr[i])); 1079 if (pm_raise_power(dstatep->dip, 0, maxpwr[i]) 1080 != DDI_SUCCESS) { 1081 rval = EINVAL; 1082 } 1083 } 1084 1085 break; 1086 1087 case DEVCTL_PM_CHANGE_PWR_LOW: 1088 /* 1089 * power off both components via pm_power_has_changed() calls 1090 */ 1091 for (i = (COMPONENTS - 1); i >= 0; --i) { 1092 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_LOW:" 1093 " comp %d new 0", 1094 nodename, instance, i)); 1095 mutex_enter(&dstatep->lock); 1096 level_tmp = dstatep->level[i]; 1097 dstatep->level[i] = 0; 1098 if (pm_power_has_changed(dstatep->dip, i, 0) 1099 != DDI_SUCCESS) { 1100 dstatep->level[i] = level_tmp; 1101 rval = EINVAL; 1102 } 1103 mutex_exit(&dstatep->lock); 1104 } 1105 1106 break; 1107 1108 case DEVCTL_PM_CHANGE_PWR_HIGH: 1109 /* 1110 * power up both components to MAXPWR via 1111 * pm_power_has_changed() calls 1112 */ 1113 for (i = 0; i < COMPONENTS; i++) { 1114 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_HIGH:" 1115 " comp %d new %d", 1116 nodename, instance, i, maxpwr[i])); 1117 mutex_enter(&dstatep->lock); 1118 level_tmp = dstatep->level[i]; 1119 dstatep->level[i] = maxpwr[i]; 1120 if (pm_power_has_changed(dstatep->dip, i, maxpwr[i]) 1121 != DDI_SUCCESS) { 1122 dstatep->level[i] = level_tmp; 1123 rval = EINVAL; 1124 } 1125 mutex_exit(&dstatep->lock); 1126 } 1127 1128 break; 1129 1130 case DEVCTL_PM_POWER: 1131 /* 1132 * test if the gen_drv_power() routine has been called, 1133 * then clear 1134 */ 1135 mutex_enter(&dstatep->lock); 1136 state = (dstatep->flag & POWER_FLAG) ? 1 : 0; 1137 if (copyout(&state, dcp->cpyout_buf, 1138 sizeof (uint_t)) != 0) { 1139 cmn_err(CE_WARN, "%s%d: DEVCTL_PM_POWER:" 1140 " copyout failed\n", nodename, instance); 1141 rval = EINVAL; 1142 } 1143 GEN_DEBUG((CE_CONT, "%s%d: %s POWER_FLAG: %d", 1144 nodename, instance, "DEVCTL_PM_POWER", state)); 1145 dstatep->flag &= ~POWER_FLAG; 1146 mutex_exit(&dstatep->lock); 1147 break; 1148 1149 case DEVCTL_PM_NO_LOWER_POWER: 1150 /* 1151 * issue to not invoke pm_lower_power() on detach 1152 */ 1153 mutex_enter(&dstatep->lock); 1154 dstatep->flag &= ~LOWER_POWER_FLAG; 1155 mutex_exit(&dstatep->lock); 1156 GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_NO_LOWER_POWER", 1157 nodename, instance)); 1158 break; 1159 1160 default: 1161 return (ENOTTY); 1162 } 1163 1164 return (rval); 1165 } 1166 1167 /*ARGSUSED*/ 1168 static int 1169 gen_read(dev_t dev, struct uio *uiop, cred_t *credp) 1170 { 1171 return (0); 1172 } 1173 1174 /*ARGSUSED*/ 1175 static int 1176 gen_write(dev_t dev, struct uio *uiop, cred_t *credp) 1177 { 1178 return (0); 1179 } 1180 1181 /*ARGSUSED0*/ 1182 static int 1183 gen_power(dev_info_t *dip, int cmpt, int level) 1184 { 1185 struct dstate *dstatep; 1186 int instance = ddi_get_instance(dip); 1187 char *nodename = ddi_node_name(dip); 1188 int level_tmp; 1189 1190 GEN_DEBUG((CE_CONT, "%s%d: power: cmpt %d to level %d", 1191 nodename, instance, cmpt, level)); 1192 1193 dstatep = ddi_get_soft_state(dstates, instance); 1194 if (dstatep == NULL) { 1195 1196 return (DDI_FAILURE); 1197 } 1198 1199 /* 1200 * Keep track of the power levels for both components 1201 * in the dstatep->comp[] array. 1202 * Set comp 0 to full level if non-zero comps 1203 * are being set to a higher, non-zero level. 1204 */ 1205 if (cmpt == 0) { 1206 mutex_enter(&dstatep->lock); 1207 dstatep->level[cmpt] = level; 1208 mutex_exit(&dstatep->lock); 1209 } else if (level > dstatep->level[cmpt] && level != 0 && 1210 dstatep->level[0] != COMP_0_MAXPWR) { 1211 /* 1212 * If component 0 is not at COMP_0_MAXPWR, and component 1 1213 * is being powered ON, invoke pm_raise_power() or 1214 * pm_power_has_changed() based on the 1215 * PUP_WITH_PWR_HAS_CHANGED_FLAG flag. 1216 * PUP_WITH_PWR_HAS_CHANGED_FLAG = FALSE by default, invoking 1217 * pm_raise_power(). 1218 */ 1219 if (!(dstatep->flag & PUP_WITH_PWR_HAS_CHANGED_FLAG)) { 1220 /* 1221 * first set comp 0 to level COMP_0_MAXPWR 1222 */ 1223 GEN_DEBUG((CE_CONT, "%s%d: power: " 1224 "pm_raise_power: comp 0 to level %d", 1225 nodename, instance, COMP_0_MAXPWR)); 1226 if (pm_raise_power(dip, 0, COMP_0_MAXPWR) != 1227 DDI_SUCCESS) { 1228 cmn_err(CE_WARN, 1229 "%s%d: power: pm_raise_power() " 1230 "failed: comp 0 to level %d\n", 1231 nodename, instance, COMP_0_MAXPWR); 1232 1233 return (DDI_FAILURE); 1234 1235 } else { 1236 mutex_enter(&dstatep->lock); 1237 dstatep->level[0] = COMP_0_MAXPWR; 1238 /* 1239 * now set the level on the non-zero comp 1240 */ 1241 dstatep->level[cmpt] = level; 1242 mutex_exit(&dstatep->lock); 1243 GEN_DEBUG((CE_CONT, "%s%d: power: " 1244 "comp %d to level %d", 1245 nodename, instance, cmpt, level)); 1246 } 1247 } else { 1248 GEN_DEBUG((CE_CONT, "%s%d: power: " 1249 "pm_power_has_changed: comp 0 to level %d", 1250 nodename, instance, COMP_0_MAXPWR)); 1251 mutex_enter(&dstatep->lock); 1252 level_tmp = dstatep->level[0]; 1253 dstatep->level[0] = COMP_0_MAXPWR; 1254 if (pm_power_has_changed(dip, 0, COMP_0_MAXPWR) != 1255 DDI_SUCCESS) { 1256 cmn_err(CE_WARN, 1257 "%s%d: power: pm_power_has_changed() " 1258 "failed: comp 0 to level %d\n", 1259 nodename, instance, COMP_0_MAXPWR); 1260 dstatep->level[0] = level_tmp; 1261 } else { 1262 /* 1263 * now set the level on the non-zero comp 1264 */ 1265 GEN_DEBUG((CE_CONT, "%s%d: power:" 1266 " pm_power_has_changed: comp %d" 1267 " to level %d", nodename, instance, 1268 cmpt, level)); 1269 dstatep->level[cmpt] = level; 1270 } 1271 mutex_exit(&dstatep->lock); 1272 } 1273 } else { 1274 mutex_enter(&dstatep->lock); 1275 dstatep->level[cmpt] = level; 1276 mutex_exit(&dstatep->lock); 1277 } 1278 1279 return (DDI_SUCCESS); 1280 } 1281 1282 1283 /* 1284 * Create properties of various data types for testing devfs events. 1285 */ 1286 static int 1287 gen_create_properties(dev_info_t *devi) 1288 { 1289 int int_val = 3023; 1290 int int_array[] = { 3, 10, 304, 230, 4}; 1291 int64_t int64_val = 20; 1292 int64_t int64_array[] = { 12, 24, 36, 48}; 1293 char *string_val = "Dev_node_prop"; 1294 char *string_array[] = {"Dev_node_prop:0", 1295 "Dev_node_prop:1", "Dev_node_prop:2", "Dev_node_prop:3"}; 1296 uchar_t byte_array[] = { (uchar_t)0xaa, (uchar_t)0x55, 1297 (uchar_t)0x12, (uchar_t)0xcd }; 1298 char bytes[] = { (char)0x00, (char)0xef, (char)0xff }; 1299 1300 if (ddi_prop_update_int(DDI_DEV_T_NONE, devi, "int", int_val) 1301 != DDI_PROP_SUCCESS) 1302 return (DDI_FAILURE); 1303 1304 if (ddi_prop_update_int_array(DDI_DEV_T_NONE, devi, "int-array", 1305 int_array, sizeof (int_array) / sizeof (int)) != DDI_PROP_SUCCESS) 1306 return (DDI_FAILURE); 1307 1308 if (ddi_prop_update_int64(DDI_DEV_T_NONE, devi, "int64", int64_val) 1309 != DDI_PROP_SUCCESS) 1310 return (DDI_FAILURE); 1311 1312 if (ddi_prop_update_int64_array(DDI_DEV_T_NONE, devi, "int64-array", 1313 int64_array, sizeof (int64_array) / sizeof (int64_t)) 1314 != DDI_PROP_SUCCESS) 1315 return (DDI_FAILURE); 1316 1317 if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "string", string_val) 1318 != DDI_PROP_SUCCESS) 1319 return (DDI_FAILURE); 1320 1321 if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, "string-array", 1322 string_array, sizeof (string_array) / sizeof (char *)) 1323 != DDI_PROP_SUCCESS) 1324 return (DDI_FAILURE); 1325 1326 if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, 1327 "boolean", NULL, 0) != DDI_PROP_SUCCESS) 1328 return (DDI_FAILURE); 1329 1330 if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, devi, "byte-array", 1331 byte_array, sizeof (byte_array)) != DDI_PROP_SUCCESS) 1332 return (DDI_FAILURE); 1333 1334 /* untyped property */ 1335 if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, "untyped", 1336 (caddr_t)bytes, sizeof (bytes)) != DDI_PROP_SUCCESS) 1337 return (DDI_FAILURE); 1338 1339 return (DDI_SUCCESS); 1340 } 1341 1342 static struct driver_minor_data { 1343 char *name; 1344 minor_t minor; 1345 int type; 1346 } disk_minor_data[] = { 1347 {"a", 0, S_IFBLK}, 1348 {"b", 1, S_IFBLK}, 1349 {"c", 2, S_IFBLK}, 1350 {"d", 3, S_IFBLK}, 1351 {"e", 4, S_IFBLK}, 1352 {"f", 5, S_IFBLK}, 1353 {"g", 6, S_IFBLK}, 1354 {"h", 7, S_IFBLK}, 1355 {"a,raw", 0, S_IFCHR}, 1356 {"b,raw", 1, S_IFCHR}, 1357 {"c,raw", 2, S_IFCHR}, 1358 {"d,raw", 3, S_IFCHR}, 1359 {"e,raw", 4, S_IFCHR}, 1360 {"f,raw", 5, S_IFCHR}, 1361 {"g,raw", 6, S_IFCHR}, 1362 {"h,raw", 7, S_IFCHR}, 1363 {0} 1364 }; 1365 1366 1367 static struct driver_serial_minor_data { 1368 char *name; 1369 minor_t minor; 1370 int type; 1371 char *node_type; 1372 } serial_minor_data[] = { 1373 {"0", 0, S_IFCHR, "ddi_serial"}, 1374 {"1", 1, S_IFCHR, "ddi_serial"}, 1375 {"0,cu", 2, S_IFCHR, "ddi_serial:dialout"}, 1376 {"1,cu", 3, S_IFCHR, "ddi_serial:dialout"}, 1377 {0} 1378 }; 1379 1380 1381 static int 1382 gen_create_display(dev_info_t *devi) 1383 { 1384 1385 int instance = ddi_get_instance(devi); 1386 char minor_name[15]; 1387 1388 (void) sprintf(minor_name, "cgtwenty%d", instance); 1389 1390 return (ddi_create_minor_node(devi, minor_name, S_IFCHR, 1391 INST_TO_MINOR(instance), DDI_NT_DISPLAY, NULL)); 1392 } 1393 1394 static int 1395 gen_create_mn_disk_chan(dev_info_t *devi) 1396 { 1397 struct driver_minor_data *dmdp; 1398 int instance = ddi_get_instance(devi); 1399 1400 if (gen_create_properties(devi) != DDI_SUCCESS) 1401 return (DDI_FAILURE); 1402 1403 for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) { 1404 if (ddi_create_minor_node(devi, dmdp->name, dmdp->type, 1405 (INST_TO_MINOR(instance)) | dmdp->minor, 1406 DDI_NT_BLOCK_CHAN, NULL) != DDI_SUCCESS) { 1407 1408 return (DDI_FAILURE); 1409 } 1410 } 1411 return (DDI_SUCCESS); 1412 } 1413 1414 static uint_t 1415 atod(char *s) 1416 { 1417 uint_t val = 0; 1418 uint_t digit; 1419 1420 while (*s) { 1421 if (*s >= '0' && *s <= '9') 1422 digit = *s++ - '0'; 1423 else 1424 break; 1425 val = (val * 10) + digit; 1426 } 1427 return (val); 1428 } 1429 1430 1431 static int 1432 gen_create_mn_disk_wwn(dev_info_t *devi) 1433 { 1434 struct driver_minor_data *dmdp; 1435 int instance = ddi_get_instance(devi); 1436 char *address = ddi_get_name_addr(devi); 1437 int target, lun; 1438 1439 if (address[0] >= '0' && address[0] <= '9' && 1440 strchr(address, ',')) { 1441 target = atod(address); 1442 address = strchr(address, ','); 1443 lun = atod(++address); 1444 } else { /* this hack is for rm_stale_link() testing */ 1445 target = 10; 1446 lun = 5; 1447 } 1448 1449 if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, 1450 "target", (caddr_t)&target, sizeof (int)) 1451 != DDI_PROP_SUCCESS) { 1452 return (DDI_FAILURE); 1453 } 1454 if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, 1455 "lun", (caddr_t)&lun, sizeof (int)) 1456 != DDI_PROP_SUCCESS) { 1457 return (DDI_FAILURE); 1458 } 1459 1460 for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) { 1461 if (ddi_create_minor_node(devi, dmdp->name, dmdp->type, 1462 (INST_TO_MINOR(instance)) | dmdp->minor, 1463 DDI_NT_BLOCK_WWN, NULL) != DDI_SUCCESS) { 1464 1465 return (DDI_FAILURE); 1466 } 1467 } 1468 return (DDI_SUCCESS); 1469 } 1470 1471 static int 1472 gen_create_mn_disk_cdrom(dev_info_t *devi) 1473 { 1474 struct driver_minor_data *dmdp; 1475 int instance = ddi_get_instance(devi); 1476 1477 for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) { 1478 if (ddi_create_minor_node(devi, dmdp->name, dmdp->type, 1479 (INST_TO_MINOR(instance)) | dmdp->minor, 1480 DDI_NT_CD_CHAN, NULL) != DDI_SUCCESS) { 1481 1482 return (DDI_FAILURE); 1483 } 1484 } 1485 return (DDI_SUCCESS); 1486 } 1487 1488 static int 1489 gen_create_mn_disk_fd(dev_info_t *devi) 1490 { 1491 struct driver_minor_data *dmdp; 1492 int instance = ddi_get_instance(devi); 1493 1494 for (dmdp = disk_minor_data; dmdp->name != NULL; dmdp++) { 1495 if (ddi_create_minor_node(devi, dmdp->name, dmdp->type, 1496 (INST_TO_MINOR(instance)) | dmdp->minor, 1497 DDI_NT_BLOCK_CHAN, NULL) != DDI_SUCCESS) { 1498 1499 return (DDI_FAILURE); 1500 } 1501 } 1502 return (DDI_SUCCESS); 1503 } 1504 1505 static int 1506 gen_create_serial(dev_info_t *devi) 1507 { 1508 struct driver_serial_minor_data *dmdp; 1509 int instance = ddi_get_instance(devi); 1510 1511 for (dmdp = serial_minor_data; dmdp->name != NULL; dmdp++) { 1512 if (ddi_create_minor_node(devi, dmdp->name, dmdp->type, 1513 (INST_TO_MINOR(instance)) | dmdp->minor, 1514 dmdp->node_type, NULL) != DDI_SUCCESS) { 1515 1516 return (DDI_FAILURE); 1517 } 1518 } 1519 return (DDI_SUCCESS); 1520 } 1521 1522 static int 1523 gen_create_net(dev_info_t *devi) 1524 { 1525 int instance = ddi_get_instance(devi); 1526 char minorname[32]; 1527 1528 if (gen_create_properties(devi) != DDI_SUCCESS) 1529 return (DDI_FAILURE); 1530 1531 (void) snprintf(minorname, sizeof (minorname), "gen_drv%d", instance); 1532 return (ddi_create_minor_node(devi, minorname, S_IFCHR, 1533 INST_TO_MINOR(instance), DDI_NT_NET, 0)); 1534 } 1535 1536 static int 1537 gen_create_minor_nodes(dev_info_t *devi, struct dstate *dstatep) 1538 { 1539 int rval = DDI_SUCCESS; 1540 char *node_name; 1541 1542 node_name = ddi_node_name(devi); 1543 1544 if (strcmp(node_name, "disk_chan") == 0) { 1545 rval = gen_create_mn_disk_chan(devi); 1546 } else if (strcmp(node_name, "disk_wwn") == 0) { 1547 rval = gen_create_mn_disk_wwn(devi); 1548 } else if (strcmp(node_name, "disk_cdrom") == 0) { 1549 rval = gen_create_mn_disk_cdrom(devi); 1550 } else if (strcmp(node_name, "disk_fd") == 0) { 1551 rval = gen_create_mn_disk_fd(devi); 1552 } else if (strcmp(node_name, "cgtwenty") == 0) { 1553 rval = gen_create_display(devi); 1554 } else if (strcmp(node_name, "genzs") == 0) { 1555 rval = gen_create_serial(devi); 1556 } else if (strcmp(node_name, "net") == 0) { 1557 rval = gen_create_net(devi); 1558 } else { 1559 int instance = ddi_get_instance(devi); 1560 char *node_type; 1561 1562 /* 1563 * Solaris may directly hang the node_type off the minor node 1564 * (without making a copy). Since we free the node_type 1565 * property below we need to make a private copy to pass 1566 * to ddi_create_minor_node to avoid devinfo snapshot panics. 1567 * We store a pointer to our copy in dstate and free it in 1568 * gen_detach after the minor nodes have been deleted by 1569 * ddi_remove_minor_node. 1570 */ 1571 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, 1572 DDI_PROP_DONTPASS, "node-type", &node_type) != 0) { 1573 cmn_err(CE_WARN, "couldn't get node-type\n"); 1574 return (DDI_FAILURE); 1575 } 1576 if (node_type) { 1577 dstatep->node_type = kmem_alloc( 1578 strlen(node_type) + 1, KM_SLEEP); 1579 (void) strcpy(dstatep->node_type, node_type); 1580 } 1581 ddi_prop_free(node_type); 1582 1583 /* the minor name is the same as the node name */ 1584 if (ddi_create_minor_node(devi, node_name, S_IFCHR, 1585 (INST_TO_MINOR(instance)), dstatep->node_type, NULL) != 1586 DDI_SUCCESS) { 1587 if (dstatep->node_type) { 1588 kmem_free(dstatep->node_type, 1589 strlen(dstatep->node_type) + 1); 1590 dstatep->node_type = NULL; 1591 } 1592 return (DDI_FAILURE); 1593 } 1594 return (DDI_SUCCESS); 1595 } 1596 1597 if (rval != DDI_SUCCESS) { 1598 ddi_prop_remove_all(devi); 1599 ddi_remove_minor_node(devi, NULL); 1600 } 1601 1602 return (rval); 1603 } 1604 1605 /*ARGSUSED*/ 1606 static void 1607 gen_event_cb(dev_info_t *dip, ddi_eventcookie_t cookie, void *arg, 1608 void *impl_data) 1609 { 1610 if (gen_debug) 1611 cmn_err(CE_NOTE, "gen_event_cb invoked"); 1612 1613 } 1614