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